27 double precision,
parameter :: saha_pf = 2.4146830395719654d15
28 double precision,
parameter :: saha_chi_kB = 157763.42386247337d0
29 double precision,
parameter :: saha_kB_cgs = 1.380649
d-16
30 double precision,
parameter :: saha_chi_H_cgs = 2.178710282685096
d-11
35 logical :: saha_nonconv_warned = .false.
43 write(*,*)
'EoS method: analytic (hydrogen Saha, helium neutral)'
44 write(*,*)
' gamma1_method: ', trim(
eos%gamma1_method)
52 if (
eos%gamma1_method ==
'exact')
then
53 call build_gamma1_analytic_table()
60 double precision function saha_y_cgs(nH_cgs, T_cgs)
result(y)
61 double precision,
intent(in) :: nh_cgs, t_cgs
62 double precision :: x_saha, disc
64 x_saha = saha_pf * t_cgs * dsqrt(t_cgs) * dexp(-saha_chi_kb / t_cgs)
65 disc = dsqrt(x_saha * x_saha + 4.0d0 * nh_cgs * x_saha)
69 y = 2.0d0 * x_saha / (x_saha + disc)
71 end function saha_y_cgs
74 double precision function saha_y_from_nh_t(nH_code, T_code)
result(y)
75 double precision,
intent(in) :: nh_code, t_code
79 end function saha_y_from_nh_t
82 double precision function saha_eint_cgs(y, T_cgs)
result(e)
83 double precision,
intent(in) :: y, t_cgs
84 e = 1.5d0 * (1.0d0 +
eos%He_abundance + y) * saha_kb_cgs * t_cgs
85 if (
eos%ionE) e = e + y * saha_chi_h_cgs
86 end function saha_eint_cgs
90 double precision,
intent(in) :: nh_code, t_code
93 eint_nh_code = saha_eint_cgs(saha_y_from_nh_t(nh_code, t_code), t_code *
unit_temperature) &
101 double precision,
intent(in) :: nh_code, p_code
102 double precision,
intent(out) :: t_out, y_out
103 double precision,
intent(out),
optional :: eint_nh_out
105 double precision :: nh_cgs, ut
106 double precision :: t_lo, t_hi, t_mid, y_mid, p_eval
108 integer,
parameter :: max_iter = 30
109 double precision,
parameter :: tol = 1.0
d-8
116 t_hi = p_code / (nh_code * (1.0d0 +
eos%He_abundance))
117 t_lo = p_code / (nh_code * (2.0d0 +
eos%He_abundance))
118 t_lo = max(t_lo, 100.0d0 / ut)
120 do iter = 1, max_iter
121 t_mid = 0.5d0 * (t_lo + t_hi)
122 y_mid = saha_y_cgs(nh_cgs, t_mid * ut)
123 p_eval = nh_code * (1.0d0 +
eos%He_abundance + y_mid) * t_mid
125 if (p_eval < p_code)
then
131 if (dabs(t_hi - t_lo) < tol * t_mid)
exit
134 if (iter > max_iter .and. .not. saha_nonconv_warned .and.
mype == 0)
then
135 saha_nonconv_warned = .true.
136 write(*,*)
"WARNING: saha_state_from_nH_p bisection reached max_iter ", &
137 "without converging; using last iterate. Not reported again."
143 if (
present(eint_nh_out)) &
150 double precision,
intent(in) :: nh_code, eint_nh_code
151 double precision,
intent(out) :: t_out, y_out
154 call saha_t_bisection(nh_code, eint_nh_code, t_out, y_out)
159 subroutine saha_t_bisection(nH_code, eint_nH_code, T_out, y_out)
160 double precision,
intent(in) :: nh_code, eint_nh_code
161 double precision,
intent(out) :: t_out, y_out
163 double precision :: nh_cgs, eint_nh_cgs, t_lo, t_hi, t_mid
164 double precision :: y_mid, cv_ion, cv_neu
166 integer,
parameter :: max_iter = 30
167 double precision,
parameter :: tol = 1.0
d-8
173 cv_ion = 1.5d0 * (2.0d0 +
eos%He_abundance) * saha_kb_cgs
174 cv_neu = 1.5d0 * (1.0d0 +
eos%He_abundance) * saha_kb_cgs
176 t_lo = (eint_nh_cgs - saha_chi_h_cgs) / cv_ion
178 t_lo = eint_nh_cgs / cv_ion
180 t_lo = max(t_lo, 100.0d0)
181 t_hi = max(eint_nh_cgs / cv_neu, t_lo + 1.0d0)
186 if (saha_y_cgs(nh_cgs, t_lo) > 1.0d0 - 1.0
d-10)
then
194 do iter = 1, max_iter
195 t_mid = 0.5d0 * (t_lo + t_hi)
196 y_mid = saha_y_cgs(nh_cgs, t_mid)
197 if (saha_eint_cgs(y_mid, t_mid) < eint_nh_cgs)
then
203 if (dabs(t_hi - t_lo) < tol * t_mid)
exit
206 if (iter > max_iter .and. .not. saha_nonconv_warned .and.
mype == 0)
then
207 saha_nonconv_warned = .true.
208 write(*,*)
"WARNING: saha_T_bisection reached max_iter without ", &
209 "converging; using last iterate. Not reported again."
215 end subroutine saha_t_bisection
220 subroutine build_gamma1_analytic_table()
221 integer,
parameter ::
ng = 256
223 double precision :: log_nh_min, log_nh_max, log_pn_min, log_pn_max, t_min, t_max
224 double precision :: dx1, dx2, nh_code, p_code, t_code, y, chi, dlny, npart, cv
225 double precision :: chi_t, chi_rho, g1_val, g1_min, g1_max
233 log_pn_min = dlog10((1.0d0 +
eos%He_abundance) * t_min)
234 log_pn_max = dlog10((2.0d0 +
eos%He_abundance) * t_max)
236 eos%gamma1_p%dim1 =
ng
237 eos%gamma1_p%dim2 =
ng
238 eos%gamma1_p%var1_min = log_nh_min
239 eos%gamma1_p%var1_max = log_nh_max
240 eos%gamma1_p%var2_min = log_pn_min
241 eos%gamma1_p%var2_max = log_pn_max
242 eos%gamma1_p%filename =
'computed_gamma1_analytic'
243 allocate(
eos%gamma1_p%table(
ng,
ng))
245 dx1 = (log_nh_max - log_nh_min) / dble(
ng - 1)
246 dx2 = (log_pn_max - log_pn_min) / dble(
ng - 1)
251 nh_code = 10.0d0**(log_nh_min + (i - 1) * dx1)
252 p_code = nh_code * 10.0d0**(log_pn_min + (j - 1) * dx2)
256 dlny = (1.0d0 - y) / (2.0d0 - y) * (1.5d0 + chi)
257 npart = 1.0d0 +
eos%He_abundance + y
258 cv = 1.5d0 * npart + 1.5d0 * y * dlny
259 if (
eos%ionE) cv = cv + y * dlny * chi
260 chi_t = 1.0d0 + y * dlny / npart
261 chi_rho = 1.0d0 - y * (1.0d0 - y) / ((2.0d0 - y) * npart)
262 g1_val = chi_rho + chi_t**2 * npart / cv
263 eos%gamma1_p%table(i, j) = g1_val
264 g1_min = min(g1_min, g1_val)
265 g1_max = max(g1_max, g1_val)
271 write(*,
'(A,F8.4,A,F8.4)') &
272 ' Gamma1 analytic table built (nH x p/nH): min = ', g1_min,
', max = ', g1_max
275 end subroutine build_gamma1_analytic_table
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
EoS state container – the single thermodynamic authority for AMRVAC.
integer, parameter, public eos_analytic
type(eos_container), allocatable, public eos
The single EoS state object, allocated in eos_init and shared (read-mostly) across all EoS sub-module...
Interpolation kernels for the EoS tables (pure math; no EoS state).
subroutine, public precompute_step_inv(tc)
Analytic hydrogen Saha EoS with neutral helium (eos_method == 'analytic').
subroutine, public finalise_analytic_lte()
Analytic-method finalise: no table unit conversion needed; build the Gamma1 table only for 'exact' mo...
subroutine, public saha_t_from_nh_eint(nh_code, eint_nh_code, t_out, y_out)
Temperature and ionisation fraction from (nH, eint/nH) in code units, by bisection.
subroutine, public load_analytic_lte()
Analytic-method load: no binary tables (on-the-fly Saha solve); just announce the method.
double precision function, public saha_eint_from_nh_t(nh_code, t_code)
eint/nH from Saha at (nH, T), in code units
subroutine, public saha_state_from_nh_p(nh_code, p_code, t_out, y_out, eint_nh_out)
Given (nH, p) in code units, T, y and optionally eint/nH: bisection on p = nH*(1+He+y(T))*T,...
Shared LTE lookup-table infrastructure (build/IO; not on the hot path).
subroutine, public precompute_fi_bypass_constants(chi_h_only)
Precompute constants for bypassing table lookups when gas is fully ionised. Physics: above T ~ 50 kK,...
This module contains definitions of global parameters and variables and some generic functions/subrou...
double precision unit_numberdensity
Physical scaling factor for number density.
double precision unit_pressure
Physical scaling factor for pressure.
integer, dimension(:), allocatable ng
number of grid blocks in domain per dimension, in array over levels
integer mype
The rank of the current MPI task.
double precision, dimension(:), allocatable, parameter d
double precision unit_temperature
Physical scaling factor for temperature.