winnse_module.f90
Go to the documentation of this file.
1 !> @file winnse_module.f90
2 !!
3 !! The error file code for this file is ***W45***.
4 !!
5 !! @brief Module \ref winnse_module with subroutines needed for calculating
6 !! nuclear statistical equilibrium
7 !!
8 !! @author Christian Winteler
9 !! @date 25.03.10
10 !!
11 !! \b Edited:
12 !! - OK: 07.11.16
13 !! - MR: 11.01.21 - implemented error_msg_class
14 !! - MR: 15.01.21 - Made it a module
15 !! - MR: 14.06.23 - Inplemented Powell's hybrid method
16 
17 !> @brief Module to calculate NSE
18 !!
19 !! The winnse_module calculates NSE composition (with or without screening corrections).
20 !! For this it uses various inputs as, e.g., the partition functions,
21 !! binding energies, and the spin of the ground state of each nucleus.
22 !! The necessary data is stored in \ref global_class::isotope.
23 !!
24 !! The NSE composition favors different nuclei, dependent on the conditions.
25 !! A rough overview is given by:
26 !! @image html nse_com.png "Dominant nuclear species when assuming NSE for different conditions." width=600
27 !!
28 !! @see [Hix & Thielemann 1999](https://ui.adsabs.harvard.edu/abs/1999JCoAM.109..321H/abstract),
29 !! [Winteler 2013](https://edoc.unibas.ch/29895/),
30 !! [Lippuner & Roberts 2017](https://ui.adsabs.harvard.edu/abs/2017ApJS..233...18L/abstract),
31 !! [Smith et al. 2023](https://ui.adsabs.harvard.edu/abs/2022arXiv221009965S/abstract)
32 !!
33 #include "macros.h"
35  implicit none
36 
37  real(r_kind),dimension(:),allocatable,public :: ynse !< NSE abundances
38  real(r_kind),dimension(:),allocatable,public :: pf !< Partition function for a given temperature
39  real(r_kind),dimension(:),allocatable,private :: gg !< \f$ G(Z,A)=(2J_0+1) \f$ where \f$ J_0 \f$ is the spin of the ground state
40  real(r_kind),dimension(:),allocatable,private :: be !< Binding energy, calculated using mass excesses
41  real(r_kind),dimension(:),allocatable,private :: scrn !< Screening correction (details see \ref nse_screen)
42  real(r_kind),dimension(:),allocatable,public :: cnse !< NSE coefficients (details see \ref cnsecalc)
43  real(r_kind),public :: ye_ext !< Electron fraction to pass to external function
44 
45  integer,dimension(:),allocatable,public :: aa !< Mass number
46  integer,dimension(:),allocatable,public :: zz !< Proton number
47  integer,dimension(:),allocatable,public :: nn !< Neutron number
48 
49  integer,private :: zmax !< Highest proton number occuring in network
50 
51  !
52  ! Public and private fields and methods of the module.
53  !
54  public:: &
56  private:: &
58 
59 contains
60 
61 !>
62 !! @brief Allocates and initialises various arrays needed for the nse calculation
63 !!
64 !! This subroutine also writes the content of \ref global_class::isotope
65 !! into shorter variables, such as \ref aa, \ref nn, and \ref gg. In
66 !! addition it calculates the binding energy
67 !! \f[ Be(i) = Z(i) \cdot (Z_{me} + e_{m}) + N(i) \cdot N_{me} - M_{exc}(i) \f]
68 !! with the mass excess of protons (Z), neutrons (N), and the isotope (i), \f$ Z_{me} \f$,
69 !! \f$ N_{me} \f$, and \f$ M_{exc} \f$. The mass of the electrons (e) is given by \f$ e_{m} \f$.
70 !! Furthermore it calculates \f$ 1 + 2 \cdot J \f$ with J being the spin
71 !! of the ground state.
72 !!
73 !! \b Edited: 12.01.14
74 subroutine nse_init()
75  use global_class, only: isotope, net_size
76 
77  implicit none
78  !
79  integer :: i
80  real(r_kind),parameter :: nem = 8.071323d0 !neutron excess mass [MeV]
81  real(r_kind),parameter :: pem = 7.288969d0 !hydrogen excess mass (including the electron) [MeV]
82 
83 !-----allocate arrays of dimension net_size
85  allocate(aa(net_size),zz(net_size),nn(net_size))
86 
87 !-----initialise arrays
88  do i=1,net_size
89  aa(i) = isotope(i)%mass
90  nn(i) = isotope(i)%n_nr
91  zz(i) = isotope(i)%p_nr
92  gg(i) = 1.d0 + 2.d0*isotope(i)%spin
93  be(i) = zz(i)*pem + nn(i)*nem - isotope(i)%mass_exc
94  end do
95  be(1) = 0.d0
96  be(2) = 0.d0
97 
98 !-----find maximum value in zz
99  zmax = maxval(zz)
100  allocate(scrn(zmax))
101 
102  return
103 
104 end subroutine nse_init
105 
106 
107 !>
108 !! @brief Calculate NSE composition with an initial guess.
109 !!
110 !! The subroutine tries to calculate nse with an initial guess and if it does
111 !! not converge, it descends from a high temperature. This routine is used
112 !! if the parameter \ref parameter_class::nse_calc_every is not equal to zero.
113 !!
114 !! @author Moritz Reichert
115 subroutine winnse_guess(t9, rho, ye, yn_guess, yp_guess, ysol)
117  use global_class, only: net_size
118  implicit none
119 
120  real(r_kind),intent(in) :: t9 !< desired temperature in GK
121  real(r_kind),intent(in) :: rho !< density in g/cm^3
122  real(r_kind),intent(in) :: ye !< electron fraction
123  real(r_kind),intent(in) :: yn_guess !< neutron abundance
124  real(r_kind),intent(in) :: yp_guess !< proton abundance
125  real(r_kind),dimension(net_size),intent(inout) :: ysol !< [out] nse abundances
126 
127  real(r_kind) :: yn,yp
128  integer :: kit,i
129 ! integer,parameter :: kmax=25
130 
131  info_entry("winnse_guess")
132 
133 
134  ynse = ysol
135  yn = yn_guess
136  yp = yp_guess
137 
138 
139  do
140  call winnse_calc(t9, rho, ye, yn, yp, nse_max_it, kit)
141 
142  if ((kit .ge. 1) .and. (kit .le. nse_max_it)) then
143  !-convergence successful
144  ! Terminate
145  exit
146  else
147  ! If no convergence is achieved, try to descend
148  call winnse_descend(nse_descend_t9start, t9, rho, ye, yn, yp, ysol)
149  ! If the process is still alive, terminate.
150  exit
151  end if
152 
153  end do
154 
155  do i=1,net_size
156  if(ynse(i).lt.1.d-25) ynse(i) = 0.d0
157  end do
158 
159  ysol = ynse
160 
161  info_exit("winnse_guess")
162 end subroutine winnse_guess
163 
164 
165 !>
166 !! @brief This routine descends from an initially high temperature, at which
167 !! the NSE abudances can be accurately predicted, to the desired
168 !! temperature
169 !!
170 !! At high temperatures, the solution is more stable and the solution of NSE
171 !! at a high temperature can be used as an initial guess of a lower temperature.
172 !! This increases the stability of the calculation and it can iterate to the
173 !! input temperature "t9fnl". The temperature step is adaptive in this process,
174 !! however, if it becomes too small (\f$ 10^{-20} \f$ GK) an error is raised.
175 !!
176 subroutine winnse_descend(t9strt, t9fnl, rho, ye, yni, ypi, ysol)
179  use screening_module,only: iscreen
180  use global_class, only: net_size
181  implicit none
182 
183  real(r_kind),intent(in) :: t9strt !< high initial temperature in GK
184  real(r_kind),intent(in) :: t9fnl !< desired temperature in GK
185  real(r_kind),intent(in) :: rho !< density in g/cm^3
186  real(r_kind),intent(in) :: ye !< electron fraction
187  real(r_kind),intent(in) :: yni !< neutron abundance
188  real(r_kind),intent(in) :: ypi !< proton abundance
189  real(r_kind),dimension(net_size),intent(inout) :: ysol !< [out] nse abundances
190 
191  real(r_kind) :: t9,delt9,t9hi
192  real(r_kind) :: yn,yn0,yp0,yp
193  integer :: kit,i
194 
195  info_entry("winnse_descend")
196 
197  t9hi = t9strt
198  t9 = t9strt
199 
200  if(t9.eq.1.d2) then
201  delt9 = sign(10.d0,(t9fnl-t9strt))
202  else
203  delt9 = 5.d-1*(t9fnl-t9hi)
204  end if
205 
206  ynse = ysol
207  yn0 = yni
208  yp0 = ypi
209  yn = yni
210  yp = ypi
211 
212 
213  do
214 
215  call winnse_calc(t9, rho, ye, yn, yp, nse_max_it, kit)
216 
217  if ((kit .ge. 1) .and. (kit .le. nse_max_it)) then
218  !-convergence successful
219  if(kit.lt.4) delt9 = 2.d0*delt9
220  if(t9.eq.t9fnl) exit
221 
222 !-----convergence not successful step back and retry with delt9 halved
223  else
224  if((t9.eq.t9fnl).and.(delt9.eq.0.d0)) then
225  call raise_exception("Reached maximum number of iterations ("//&
226  trim(adjustl(int_to_str(kit)))//") when calculating NSE.",&
227  "winnse_descend",450003)
228  end if
229  t9 = t9-delt9
230  delt9 = 5.d-1*delt9
231  end if
232 
233 !-----make sure delt9 is not bigger than t9-t9fnl
234  delt9 = sign(min(2.d1,abs(delt9)),delt9)
235  delt9 = sign(min(abs(t9-t9fnl),abs(delt9)),delt9)
236 
237  if (dabs(delt9).lt.nse_delt_t9min)then
238  call raise_exception("Temperature difference was too small when calculating"//new_line("A")//&
239  "NSE composition (T9= "//trim(adjustl(num_to_str(dabs(t9))))//", delt9="//trim(adjustl(num_to_str(dabs(delt9))))//")."//new_line("A")//&
240  'Try to set "nse_descend_t9start", "nse_nr_tol", or "nse_max_it" to a higher value.',&
241  "winnse_descend",450004)
242  end if
243 !-----descend in temperature
244  t9 = t9+delt9
245  if(t9.le.t9fnl) t9 = t9fnl !TODO: this can be wrong
246 
247  end do
248 
249  do i=1,net_size
250  if(ynse(i).lt.1.d-25) ynse(i) = 0.d0
251  end do
252 
253  ysol = ynse
254 
255  info_exit("winnse_descend")
256 
257 end subroutine winnse_descend
258 
259 
260 
261 !> Solves the nse-equations.
262 !!
263 !! This subroutine solves the NSE equations. It hereby
264 !! choses between two different solvers as defined by the
265 !! parameter \ref parameter_class::nse_solver that is defined as
266 !! in the following table:
267 !! | nse_solver | Solver |
268 !! |:----------:|:----------------------:|
269 !! | 0 | Newton-Raphson |
270 !! | 1 | Powell's hybrid method |
271 !!.
272 !! @author M. Reichert
273 !! @date 14.06.23
274 subroutine winnse_calc(t9, rho, ye, yn, yp, imax, kit)
275  use screening_module,only: iscreen
277  use parameter_class, only: nse_solver
278  implicit none
279  real(r_kind),intent(in) :: t9 !< temperature in GK
280  real(r_kind),intent(in) :: rho !< density in g/cm^3
281  real(r_kind),intent(in) :: ye !< electron fraction
282  real(r_kind),intent(inout) :: yn !< neutron abundance
283  real(r_kind),intent(inout) :: yp !< proton abundance
284  integer,intent(in) :: imax !< max. number of iterations
285  integer,intent(out) :: kit !< actual number of iterations
286 
287  info_entry("winnse_calc")
288 
289  ! Calculate screening coefficients needed for wincnse_calc
290  if (iscreen) call nse_screen(t9, rho, ye)
291 
292  ! Calculate nse coefficients cnse
293  call wincnse_calc(t9, rho)
294 
295  ! Choose the NSE solver
296  if (nse_solver .eq. 0) then
297  call winnse_calc_nr(t9, rho, ye, yn, yp, imax, kit)
298  elseif (nse_solver .eq. 1) then
299  call winnse_calc_hybrid_powell(t9, rho, ye, yn, yp, imax, kit)
300  else
301  call raise_exception("Unknown NSE solver ("//int_to_str(nse_solver)//").", &
302  "winnse_calc",450006)
303  end if
304 
305  info_exit("winnse_calc")
306 end subroutine winnse_calc
307 
308 
309 
310 
311 
312 !> Solves the nse-equations using the Powell hybrid method.
313 !!
314 !! This subroutine solves the NSE equations. It's two dimensional as the unknowns
315 !! are the neutron abundances Yn and proton abundances Yp. The underlying
316 !! constraints are mass conservation \f$ \sum X_i = \sum Y_i \cdot A_i = 1 \f$
317 !! and charge conservation \f$ \sum Y_i \cdot Z_i = y_e \f$.
318 !! The subroutine makes use of the minpack routine fsolve.
319 !!
320 !! @author M. Reichert
321 !! @date 14.06.23
322 subroutine winnse_calc_hybrid_powell(t9, rho, ye, yn, yp, imax, kit)
323  use parameter_class, only: nse_nr_tol
324  implicit none
325  ! Declare the pass
326  real(r_kind),intent(in) :: t9 !< temperature in GK
327  real(r_kind),intent(in) :: rho !< density in g/cm^3
328  real(r_kind),intent(in) :: ye !< electron fraction
329  real(r_kind),intent(inout) :: yn !< neutron abundance
330  real(r_kind),intent(inout) :: yp !< proton abundance
331  integer,intent(in) :: imax !< max. number of iterations
332  integer,intent(out) :: kit !< actual number of iterations
333  !
334  integer :: info !< Info variable for fsolve
335  real(r_kind) :: tol !< Tolerance for convergence (see nse_nr_tol parameter)
336  real(r_kind), dimension(2) :: x !< Array for the solution
337  real(r_kind), dimension(2) :: fvec !< Array for the function values
338  external mass_and_charge_conservation !< External function for the system of equations
339 
340 
341  ! Set tolerance for convergence
342  tol = nse_nr_tol
343 
344  ! Set initial guess
345  x(1) = yn
346  x(2) = yp
347 
348  ! Update external Ye
349  ye_ext = ye
350 
351  ! Use Powell's hybrid method to solve the system of equations
352  call fsolve ( mass_and_charge_conservation, 2, x, fvec, tol, info )
353 
354  ! Check if the solution is valid
355  ! info = 1 means that the solution converged
356  if (info .ne. 1) then
357  kit = imax+10
358  else
359  ! Check if the function is really zero
360  if (maxval(abs(fvec)) .gt. tol) then
361  kit = imax+10
362  else
363  kit = 1
364  yn = x(1)
365  yp = x(2)
366  end if
367  end if
368 
369  return
370 
371  end subroutine winnse_calc_hybrid_powell
372 
373 
374 
375 !>
376 !! @brief Solves the nse-equations using a 2-dimensional newton-raphson scheme
377 !!
378 !! This subroutine solves the NSE equations. It's two dimensional as the unknowns
379 !! are the neutron abundances Yn and proton abundances Yp. The underlying
380 !! constraints are mass conservation \f$ \sum X_i = \sum Y_i \cdot A_i = 1 \f$
381 !! and charge conservation \f$ \sum Y_i \cdot Z_i = y_e \f$.
382 !! Furthermore it uses the so called saha equation:
383 !! \f[
384 !! Y(N,Z) = G_{N,Z}(\rho N_A)^{N+Z-1}\frac{\left( N +Z \right)^{3/2}}{2^{N+Z}}\left(\frac{2\pi \hbar}
385 !! {m_\mathrm{u} k_{\mathrm{B}}T}\right)^{\frac{3}{2}(N+Z-1)} \mathrm{exp}(B_{N,Z}/
386 !! k_B T)Y^{N}_\mathrm{n} Y^Z_\mathrm{p},
387 !!\f]
388 !!
389 !! @warning Here, the abundance of neutrons and protons are used explicitely.
390 !! A simulation without neutrons and protons specified in the sunet
391 !! that tries to calculate NSE will result in an error!
392 !!
393 !! @see wincnse_calc
394 !!
395 !! \b Edited:
396 !! - 12.01.14
397 !! - 15.01.21
398 !! - 23.01.21 MR, Fixed overflows in this routine
399 !! .
400 subroutine winnse_calc_nr(t9, rho, ye, yn, yp, imax, kit)
402  use parameter_class, only: nse_nr_tol
403  use screening_module,only: iscreen
404  use global_class, only: net_size
405  implicit none
406 
407  real(r_kind),intent(in) :: t9 !< temperature in GK
408  real(r_kind),intent(in) :: rho !< density in g/cm^3
409  real(r_kind),intent(in) :: ye !< electron fraction
410  real(r_kind),intent(inout) :: yn !< neutron abundance
411  real(r_kind),intent(inout) :: yp !< proton abundance
412  integer,intent(in) :: imax !< max. number of iterations
413  integer,intent(out) :: kit !< actual number of iterations
414  !
415  real(r_kind) :: yn0,ynl,ynr,delyn,testyn
416  real(r_kind) :: yp0,ypl,ypr,delyp,testyp
417  real(r_kind) :: f,dfdn,dfdp
418  real(r_kind) :: g,dgdn,dgdp
419  real(r_kind) :: det,detr
420  real(r_kind) :: tol,testk
421  real(r_kind) :: atst,ztst,ytst,abar,zbar
422  real(r_kind) :: infty !< Infinity to check for overflows
423  integer :: i,j
424 
425  infty = huge(infty)
426 
427 !-----set tolerance for convergence
428  tol = nse_nr_tol
429 
430 !-----set maximum number of iteration steps
431 ! imax = 10
432 
433 !-----save initial values
434  yn0 = yn
435  yp0 = yp
436 
437 
438 !-----calculate screening coefficients needed for wincnse_calc
439  if (iscreen) call nse_screen(t9, rho, ye)
440 
441 !-----calculate nse coefficients cnse
442  call wincnse_calc(t9, rho)
443 
444 !-----iteratively solve the coupled system of equations:
445 ! f...expression for charge conservation
446 ! g...expression for mass conservation
447 
448  kit = 0
449 
450  do i=1,imax
451  ynl = dlog(yn)
452  ypl = dlog(yp)
453 !-----calculate new abundances using the nse equation
454  !! Take care of possible overflows
455  ynse = cnse + nn*ynl + zz*ypl
456 
457  !< Ensure that ynse does not get to large
458  if (maxval(ynse) .ge. dlog(infty)-5) then
459  kit = imax+4
460  exit
461  end if
462 
463  ynse = dexp(ynse)
464 
465  ynr = 1.d0/yn
466  ypr = 1.d0/yp
467 
468 !-----calculate electron and mass conservation
469  f = sum(zz*ynse) - ye
470  g = sum(aa*ynse) -1.d0
471 
472 ! print *, 'nn, zz, aa = ', nn, zz, aa
473 !-----calculate the entries in the jacobian, df/dYp,df/dYn,dg/dYp,dg/dYn
474  dfdp = sum(ynse*zz*zz)*ypr
475  dfdn = sum(ynse*zz*nn)*ynr
476  dgdp = sum(ynse*aa*zz)*ypr
477  dgdn = sum(ynse*aa*nn)*ynr
478 
479 !-----calculate determinant of the jacobian
480 
481  ! The following large construct is just to ensure that there are
482  ! no floating over/underflows
483  ! The determinant is just given by
484  ! det = dfdp*dgdn - dfdn*dgdp
485  if ((dfdp .gt. 0) .and. (dgdn .gt. 0) .and.&
486  (dfdn .gt. 0) .and. (dgdp .gt. 0)) then
487 
488  ! Check for overflows and correct them
489  if ((dlog(dfdp)+dlog(dgdn) .ge. dlog(infty)) .or. &
490  (dlog(dfdn)+dlog(dgdp) .ge. dlog(infty))) then
491  ! Express it as dfdn*dfdp*(dgdn/dfdn - dgdp/dfdp)
492  det = dlog(dfdn)+dlog(dfdp)+dlog(dgdn/dfdn - dgdp/dfdp)
493  ! Check if there is still an overflow, if yes, this iteration has failed
494  if (det .ge. dlog(infty)) then
495  kit = imax+1
496  exit
497  else
498  det = dexp(det)
499  end if
500  else
501  det = dfdp*dgdn - dfdn*dgdp
502  end if
503  elseif (((dfdp .gt. infty/10.) .and. (dgdn .gt. infty/10.)) .or.&
504  ((dfdn .gt. infty/10.) .and. (dgdp .gt. infty/10.))) then
505  kit = imax+1
506  exit
507  else
508  det = dfdp*dgdn - dfdn*dgdp
509  end if
510 
511 
512 
513 !-----solve the matrix equation for the changes in yp and yn
514  if(det .ne. 0) then
515  detr = 1.d0/det
516  delyp = (dgdn*f - dfdn*g)*detr
517  delyn = (dfdp*g - dgdp*f)*detr
518  else
519  call raise_exception('Did not converge when trying to calculate NSE. '//new_line("A")//&
520  'Found zero determinant.',&
521  "winnse_calc",450005)
522  end if
523 
524 !-----update the abundances, check if positive
525  testyp = yp - delyp
526  testyn = yn - delyn
527 
528  if ((testyp.gt.0.d0).and.(testyn.gt.0.d0)) then
529  yp = testyp
530  yn = testyn
531  else
532  kit = imax+4
533  exit
534  end if
535 
536 !-----check for convergence
537  testyp = delyp/yp
538  testyn = delyn/yn
539  testk = sqrt(testyp**2 + testyn**2 + f**2 + g**2)
540  kit = i
541  if (testk .lt. tol) exit
542 
543  end do
544 
545 !-----if nse converges, update abundances
546  if(kit.lt.imax) then
547  ypl = dlog(yp)
548  ynl = dlog(yn)
549  ynse = dexp(cnse +ypl*zz +ynl*nn)
550 
551 !-----calculate some composition properties, atst, ztst, ytst are the
552 ! total mass, electron fraction and abundances, abar and zbar are
553 ! the average baryon and proton number
554  atst = sum(aa*ynse)
555  ztst = sum(zz*ynse)
556  ytst = sum(ynse)
557  abar = atst/(ytst)
558  zbar = ztst/(ytst)
559  else
560 !-----set yn and yp to initial values and return
561  kit = imax+1
562  yn = yn0
563  yp = yp0
564  end if
565 
566  return
567 
568 end subroutine winnse_calc_nr
569 
570 
571 !>
572 !! @brief Calls screening subroutine and adds subsequent proton capture screening
573 !! corrections in scrn(1:zmax).
574 !!
575 !! This subroutine iterates over all possible amount of protons of a nucleus
576 !! (up to \ref zmax). It uses the subroutine \ref screen to calculate the
577 !! screening corrections. After it has been called, the array \ref scrn contains
578 !! summed screening corrections for proton captures from z=1 to z=i for scrn(i)
579 !! on symmetric nuclei (\f$ A = 2 \cdot Z \f$).
580 !!
581 !! \b Edited:
582 !! - 12.01.14
583 !! .
584 subroutine nse_screen(t9, rho, ye)
585  use screening_module, only: screening
586  implicit none
587  real(r_kind),intent(in) :: t9 !< temperature in GK
588  real(r_kind),intent(in) :: rho !< density in g/cm^3
589  real(r_kind),intent(in) :: ye !< electron fraction
590  real(r_kind) :: hp, ht, hh
591  integer :: i,z1,z2,a1,a2
592 
593  scrn(1) = 0.d0
594 
595  z1 = 1
596  a1 = 1
597  do i=2,zmax
598  z2 = i-1
599  a2 = 2*z2
600  if(z2.eq.1) a2=1
601  call screening (t9,rho,z1,z2,a1,a2,ye,0,hh,ht,hp)
602 !----- add subsequent screening factors
603  scrn(i) = scrn(i-1) + hh
604  end do
605  return
606 end subroutine nse_screen
607 
608 
609 !>
610 !! Calculates the nse coefficients C, as given in
611 !! [Hix,Thielemann '99](https://ui.adsabs.harvard.edu/abs/1999JCoAM.109..321H/abstract), Eq.25
612 !!
613 !! This subroutine calculates:
614 !! \f[ \frac{G(^A Z)}{2^A} \left( \frac{ \rho N_A }{\theta} \right)^{A-1} A^{\frac{3}{2}}
615 !! \exp{ \left( \frac{B(^A Z)}{k_B T} \right)} \f]
616 !! ,where \f$ G \f$ are the partition functions, A the mass number, \f$ \rho \f$
617 !! the density, \f$ N_A \f$ avogadros number, B the binding energy, \f$ k_B \f$
618 !! the boltzman constant, and T the temperature. Theta is a helper variable and given
619 !! by:
620 !! \f[ \theta = \left( \frac{m_u k_B T}{2\pi \hbar^2} \right) \f]
621 !! The result is stored in the array \ref cnse.
622 !!
623 !! \b Edited:
624 !! - 12.01.14
625 !! .
626 subroutine wincnse_calc(t9, rho)
627  use parameter_class, only: unit
628  use screening_module,only: iscreen
629  use nucstuff_class, only: inter_partf
630  use global_class, only: net_size
631  implicit none
632 
633  real(r_kind),intent(in) :: t9 !< temperature in GK
634  real(r_kind),intent(in) :: rho !< density in g/cm^3
635  !
636  real(r_kind) :: bkt, bktr
637  real(r_kind) :: ronath !< rho*Na/theta
638  real(r_kind) :: ln2
639 
640 !-----interpolate partition functions for current temperature
641  call inter_partf (t9, pf)
642  pf = dlog(pf)
643 
644 !-----calculate boltzmann factor 1/kT in MeV^-1
645  bkt = 1.d9*t9*unit%k_mev
646  bktr= 1.d0/bkt
647 
648 !-----calculate log[(rho*Na/theta)] see Hix '99 eq(26) for theta
649  ronath = dlog(unit%n_a*rho)+1.5d0*dlog((2.d0*unit%pi*unit%hbar_mev**2)/ &
650  (bkt*unit%hix))
651 
652  ln2 = dlog(2.d0)
653 
654 !-----calculate the coefficients
655  cnse(1) = 0.d0
656  cnse(2) = 0.d0
657 
658  ! MR: Screening was thrown out previously. Why? Implemented it again...
659  if (iscreen) then
660  cnse(3:net_size) = dlog(gg(3:net_size)*aa(3:net_size)*dsqrt(dble(aa(3:net_size)))) &
661  + pf(3:net_size) - ln2*aa(3:net_size) + (dble(aa(3:net_size))-1.d0)*ronath &
662  + be(3:net_size)*bktr + scrn(zz(3:net_size))
663  else
664  cnse(3:net_size) = dlog(gg(3:net_size)*aa(3:net_size)*dsqrt(dble(aa(3:net_size)))) &
665  + pf(3:net_size) - ln2*aa(3:net_size) + (dble(aa(3:net_size))-1.d0)*ronath &
666  + be(3:net_size)*bktr
667  end if
668 
669 
670 
671 end subroutine wincnse_calc
672 
673 
674 end module winnse_module
675 
676 
677 
678 !> Determines the mass and charge conservation for the NSE calculation
679 !!
680 !! This subroutine calculates the mass and charge conservation for the NSE
681 !! calculation. It is used in the Powell hybrid method to solve the system of
682 !! equations.
683 !! The first entry of fvec will contain
684 !! \f[ \sum_i Z_i Y_i - y_e \f]
685 !! and the second entry will contain
686 !! \f[ \sum_i A_i Y_i - 1 \f]
687 !! where \f$ Z_i \f$ is the proton number, \f$ A_i \f$ the mass number, \f$ Y_i \f$
688 !! the abundance of the isotope i, and \f$ y_e \f$ the electron fraction.
689 !!
690 !! @author M. Reichert
691 !! @date 14.06.23
692 subroutine mass_and_charge_conservation(n,x,fvec)
693  use winnse_module, only: cnse, nn, zz, aa, ynse, ye_ext
694  implicit none
695  integer :: n !< Number of equations and variables (2)
696  real (r_kind), dimension(n) :: fvec !< Vector that contains function evaluations
697  real (r_kind), dimension(n) :: x !< Vector of variables
698 
699  real (r_kind) :: ynl !< Logarithm of the neutron abundance
700  real (r_kind) :: ypl !< Logarithm of the proton abundance
701 
702 
703  ! Define some helpers
704  ynl = dlog(x(1))
705  ypl = dlog(x(2))
706  ! Calculate new abundances using the nse equation
707  ! Take care of possible overflows
708  ynse = cnse + nn*ynl + zz*ypl
709 
710  !< Ensure that ynse does not get to large
711  if (maxval(ynse) .ge. dlog(huge(ynl))-5) then
712  fvec(1) = huge(ynl)
713  fvec(2) = huge(ynl)
714  else
715  ! Calculate the actual abundances
716  ynse = dexp(ynse)
717 
718  ! Calculate electron and mass conservation
719  fvec(1) = sum(zz*ynse) -ye_ext
720  fvec(2) = sum(aa*ynse) -1.d0
721  end if
722 
723 
724 end subroutine mass_and_charge_conservation
winnse_module::winnse_calc_nr
subroutine, private winnse_calc_nr(t9, rho, ye, yn, yp, imax, kit)
Solves the nse-equations using a 2-dimensional newton-raphson scheme.
Definition: winnse_module.f90:401
winnse_module::pf
real(r_kind), dimension(:), allocatable, public pf
Partition function for a given temperature.
Definition: winnse_module.f90:38
winnse_module::aa
integer, dimension(:), allocatable, public aa
Mass number.
Definition: winnse_module.f90:45
winnse_module
Module to calculate NSE.
Definition: winnse_module.f90:34
screening_module
Module to calculate electron screening.
Definition: screening_module.f90:17
parameter_class::nse_descend_t9start
real(r_kind) nse_descend_t9start
high initial temperature in GK for winnse_descend subroutine
Definition: parameter_class.f90:125
winnse_module::winnse_guess
subroutine, public winnse_guess(t9, rho, ye, yn_guess, yp_guess, ysol)
Calculate NSE composition with an initial guess.
Definition: winnse_module.f90:116
winnse_module::scrn
real(r_kind), dimension(:), allocatable, private scrn
Screening correction (details see nse_screen)
Definition: winnse_module.f90:41
winnse_module::be
real(r_kind), dimension(:), allocatable, private be
Binding energy, calculated using mass excesses.
Definition: winnse_module.f90:40
winnse_module::winnse_calc
subroutine, private winnse_calc(t9, rho, ye, yn, yp, imax, kit)
Solves the nse-equations.
Definition: winnse_module.f90:275
parameter_class::nse_solver
integer nse_solver
Solver for calculating NSE. 0: Newton-Raphson, 1: Powell's hybrid method.
Definition: parameter_class.f90:118
error_msg_class
Error handling routines.
Definition: error_msg_class.f90:16
winnse_module::wincnse_calc
subroutine, private wincnse_calc(t9, rho)
Calculates the nse coefficients C, as given in Hix,Thielemann '99, Eq.25.
Definition: winnse_module.f90:627
error_msg_class::int_to_str
character(:) function, allocatable, public int_to_str(num)
Converts a given integer to a string.
Definition: error_msg_class.f90:224
winnse_module::zz
integer, dimension(:), allocatable, public zz
Proton number.
Definition: winnse_module.f90:46
error_msg_class::raise_exception
subroutine, public raise_exception(msg, sub, error_code)
Raise a exception with a given error message.
Definition: error_msg_class.f90:245
winnse_module::nse_init
subroutine, public nse_init()
Allocates and initialises various arrays needed for the nse calculation.
Definition: winnse_module.f90:75
global_class::isotope
type(isotope_type), dimension(:), allocatable, public isotope
all nuclides used in the network
Definition: global_class.f90:34
nucstuff_class
Module with helper functions such as rate tabulation, partition functions, and theoretical weak rates...
Definition: nucstuff_class.f90:11
screening
Definition: screening.py:1
winnse_module::ynse
real(r_kind), dimension(:), allocatable, public ynse
NSE abundances.
Definition: winnse_module.f90:37
winnse_module::zmax
integer, private zmax
Highest proton number occuring in network.
Definition: winnse_module.f90:49
winnse_module::winnse_descend
subroutine, public winnse_descend(t9strt, t9fnl, rho, ye, yni, ypi, ysol)
This routine descends from an initially high temperature, at which the NSE abudances can be accuratel...
Definition: winnse_module.f90:177
global_class::net_size
integer, public net_size
total number of isotopes (network size)
Definition: global_class.f90:93
parameter_class::unit
type(unit_type), public unit
constants and unit conversion factors
Definition: parameter_class.f90:54
error_msg_class::num_to_str
character(:) function, allocatable, public num_to_str(num)
Converts a given real to a string with format "(1pE10.2)".
Definition: error_msg_class.f90:205
winnse_module::cnse
real(r_kind), dimension(:), allocatable, public cnse
NSE coefficients (details see cnsecalc)
Definition: winnse_module.f90:42
winnse_module::nse_screen
subroutine, private nse_screen(t9, rho, ye)
Calls screening subroutine and adds subsequent proton capture screening corrections in scrn(1:zmax).
Definition: winnse_module.f90:585
r_kind
#define r_kind
Definition: macros.h:46
fsolve
subroutine fsolve(fcn, n, x, fvec, tol, info)
Definition: fsolve.f90:504
winnse_module::ye_ext
real(r_kind), public ye_ext
Electron fraction to pass to external function.
Definition: winnse_module.f90:43
winnse_module::winnse_calc_hybrid_powell
subroutine, private winnse_calc_hybrid_powell(t9, rho, ye, yn, yp, imax, kit)
Solves the nse-equations using the Powell hybrid method.
Definition: winnse_module.f90:323
winnse_module::gg
real(r_kind), dimension(:), allocatable, private gg
where is the spin of the ground state
Definition: winnse_module.f90:39
winnse_module::nn
integer, dimension(:), allocatable, public nn
Neutron number.
Definition: winnse_module.f90:47
nucstuff_class::inter_partf
subroutine, public inter_partf(temp, interpol)
Calculates partition function.
Definition: nucstuff_class.f90:132
global_class
Contains types and objects shared between multiple modules.
Definition: global_class.f90:19
mass_and_charge_conservation
subroutine mass_and_charge_conservation(n, x, fvec)
Determines the mass and charge conservation for the NSE calculation.
Definition: winnse_module.f90:693
parameter_class::nse_delt_t9min
real(r_kind) nse_delt_t9min
Minimum temperature [GK] when descending to desired temperature in NSE.
Definition: parameter_class.f90:115
parameter_class::nse_nr_tol
real(r_kind) nse_nr_tol
Tolerance for the NR loop in the NSE calculation.
Definition: parameter_class.f90:116
screening_module::iscreen
logical, public iscreen
Flag whether screening is enabled or not.
Definition: screening_module.f90:24
parameter_class::nse_max_it
integer nse_max_it
Maximum amount of NSE iterations.
Definition: parameter_class.f90:117
parameter_class
Contains all runtime parameters as well as phys and math constants.
Definition: parameter_class.f90:24