28 double precision,
parameter :: saha_pf = 2.4146830395719654d15
29 double precision,
parameter :: saha_chi_kB = 157763.42386247337d0
30 double precision,
parameter :: saha_kB_cgs = 1.380649
d-16
31 double precision,
parameter :: saha_chi_H_cgs = 2.178710282685096
d-11
36 logical :: saha_nonconv_warned = .false.
44 write(*,*)
'EoS method: analytic (H-only Saha)'
45 write(*,*)
' gamma1_method: ', trim(
eos%gamma1_method)
53 if (
eos%gamma1_method ==
'exact')
then
54 call build_gamma1_analytic_table()
61 double precision function saha_y_cgs(nH_cgs, T_cgs)
result(y)
62 double precision,
intent(in) :: nh_cgs, t_cgs
63 double precision :: x_saha, disc
65 x_saha = saha_pf * t_cgs * dsqrt(t_cgs) * dexp(-saha_chi_kb / t_cgs)
66 disc = dsqrt(x_saha * x_saha + 4.0d0 * nh_cgs * x_saha)
70 y = 2.0d0 * x_saha / (x_saha + disc)
72 end function saha_y_cgs
75 double precision function saha_y_from_nh_t(nH_code, T_code)
result(y)
76 double precision,
intent(in) :: nh_code, t_code
80 end function saha_y_from_nh_t
85 double precision,
intent(in) :: nh_code, t_code
86 double precision :: y, t_cgs, eint_nh_cgs
89 y = saha_y_from_nh_t(nh_code, t_code)
92 eint_nh_cgs = 1.5d0 * (1.0d0 + y) * saha_kb_cgs * t_cgs
94 eint_nh_cgs = eint_nh_cgs + y * saha_chi_h_cgs
107 double precision,
intent(in) :: nh_code, p_code
108 double precision,
intent(out) :: t_out, y_out
109 double precision,
intent(out),
optional :: eint_nh_out
111 double precision :: nh_cgs, ut, eint_nh_cgs
112 double precision :: t_lo, t_hi, t_mid, y_mid, p_eval
114 integer,
parameter :: max_iter = 30
115 double precision,
parameter :: tol = 1.0
d-8
123 t_hi = p_code / nh_code
124 t_lo = p_code / (2.0d0 * nh_code)
125 t_lo = max(t_lo, 100.0d0 / ut)
127 do iter = 1, max_iter
128 t_mid = 0.5d0 * (t_lo + t_hi)
129 y_mid = saha_y_cgs(nh_cgs, t_mid * ut)
130 p_eval = nh_code * (1.0d0 + y_mid) * t_mid
132 if (p_eval < p_code)
then
138 if (dabs(t_hi - t_lo) < tol * t_mid)
exit
141 if (iter > max_iter .and. .not. saha_nonconv_warned .and.
mype == 0)
then
142 saha_nonconv_warned = .true.
143 write(*,*)
"WARNING: saha_state_from_nH_p bisection reached max_iter ", &
144 "without converging; using last iterate. Not reported again."
151 if (
present(eint_nh_out))
then
152 eint_nh_cgs = 1.5d0 * (1.0d0 + y_mid) * saha_kb_cgs * (t_mid * ut)
153 if (
eos%ionE) eint_nh_cgs = eint_nh_cgs + y_mid * saha_chi_h_cgs
162 double precision,
intent(in) :: nh_code, eint_nh_code
163 double precision,
intent(out) :: t_out, y_out
166 call saha_t_bisection(nh_code, eint_nh_code, t_out, y_out)
174 subroutine saha_t_bisection(nH_code, eint_nH_code, T_out, y_out)
175 double precision,
intent(in) :: nh_code, eint_nh_code
176 double precision,
intent(out) :: t_out, y_out
178 double precision :: nh_cgs, eint_nh_cgs, t_lo, t_hi, t_mid
179 double precision :: eint_mid, y_mid, y_lo
181 integer,
parameter :: max_iter = 30
182 double precision,
parameter :: tol = 1.0
d-8
191 t_lo = max((eint_nh_cgs - saha_chi_h_cgs) / (3.0d0 * saha_kb_cgs), 100.0d0)
193 t_hi = eint_nh_cgs / (1.5d0 * saha_kb_cgs)
195 t_lo = eint_nh_cgs / (3.0d0 * saha_kb_cgs)
196 t_hi = eint_nh_cgs / (1.5d0 * saha_kb_cgs)
198 t_lo = max(t_lo, 100.0d0)
199 t_hi = max(t_hi, t_lo + 1.0d0)
209 y_lo = saha_y_cgs(nh_cgs, t_lo)
210 if (y_lo > 0.999d0)
then
212 t_out = max((eint_nh_cgs - saha_chi_h_cgs) &
220 do iter = 1, max_iter
221 t_mid = 0.5d0 * (t_lo + t_hi)
222 y_mid = saha_y_cgs(nh_cgs, t_mid)
224 eint_mid = 1.5d0 * (1.0d0 + y_mid) * saha_kb_cgs * t_mid
225 if (
eos%ionE) eint_mid = eint_mid + y_mid * saha_chi_h_cgs
227 if (eint_mid < eint_nh_cgs)
then
233 if (dabs(t_hi - t_lo) < tol * t_mid)
exit
236 if (iter > max_iter .and. .not. saha_nonconv_warned .and.
mype == 0)
then
237 saha_nonconv_warned = .true.
238 write(*,*)
"WARNING: saha_T_bisection reached max_iter without ", &
239 "converging; using last iterate. Not reported again."
245 end subroutine saha_t_bisection
252 subroutine build_gamma1_analytic_table()
253 integer,
parameter ::
ng = 256
255 double precision :: log_nh_min, log_nh_max, log_t_min, log_t_max
256 double precision :: dx1, dx2, log_nh, log_t
257 double precision :: nh_code, t_code, y, yp, ym
258 double precision :: cv, g1_val, g1_min, g1_max
259 double precision :: t_cgs, dt_cgs
260 double precision :: eint_p, eint_m
269 eos%gamma1_p%dim1 =
ng
270 eos%gamma1_p%dim2 =
ng
271 eos%gamma1_p%var1_min = log_nh_min
272 eos%gamma1_p%var1_max = log_nh_max
273 eos%gamma1_p%var2_min = log_t_min
274 eos%gamma1_p%var2_max = log_t_max
275 eos%gamma1_p%filename =
'computed_gamma1_analytic'
277 allocate(
eos%gamma1_p%table(
ng,
ng))
279 dx1 = (log_nh_max - log_nh_min) / dble(
ng - 1)
280 dx2 = (log_t_max - log_t_min) / dble(
ng - 1)
286 log_t = log_t_min + (j - 1) * dx2
287 t_code = 10.0d0**log_t
290 dt_cgs = 1.0
d-3 * t_cgs
293 log_nh = log_nh_min + (i - 1) * dx1
294 nh_code = 10.0d0**log_nh
296 y = saha_y_from_nh_t(nh_code, t_code)
302 eint_p = 1.5d0 * (1.0d0 + yp) * saha_kb_cgs * (t_cgs + dt_cgs)
303 eint_m = 1.5d0 * (1.0d0 + ym) * saha_kb_cgs * (t_cgs - dt_cgs)
305 eint_p = eint_p + yp * saha_chi_h_cgs
306 eint_m = eint_m + ym * saha_chi_h_cgs
309 cv = (eint_p - eint_m) / (2.0d0 * dt_cgs)
313 g1_val = 1.0d0 + (1.0d0 + y) * saha_kb_cgs / cv
318 eos%gamma1_p%table(i, j) = g1_val
319 g1_min = min(g1_min, g1_val)
320 g1_max = max(g1_max, g1_val)
327 write(*,
'(A,F8.4,A,F8.4)') &
328 ' Gamma1 analytic table built (nH x T): min = ', g1_min,
', max = ', g1_max
331 end subroutine build_gamma1_analytic_table
336 double precision,
intent(in) :: nh_code, t_code
337 double precision :: log_nh, log_t
340 log_nh = dlog10(nh_code)
341 log_t = dlog10(t_code)
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).
pure double precision function, public bicubic_lookup(var1, var2, tc)
Dispatch wrapper: bicubic lookup that branches on the table's is_uniform flag. Hides the uniform-vs-a...
subroutine, public precompute_step_inv(tc)
Analytic H-only Saha EoS (eos_method == 'analytic').
double precision function, public saha_gamma1_from_nh_t(nh_code, t_code)
Look up Gamma1 from the analytical 2D table (nH, T axes in code units). For use when eosmethod == 'an...
subroutine, public finalise_analytic_lte()
Analytic-method finalise (H-only Saha): no table unit conversion needed; build the Gamma1 table only ...
subroutine, public saha_t_from_nh_eint(nh_code, eint_nh_code, t_out, y_out)
Temperature inversion: given (nH, eint/nH) in CODE UNITS, find T in CODE UNITS, by bisection (guarant...
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)
Internal energy per nH from analytical Saha, in CODE UNITS. eint_nH = 1.5*(1+y)*kB*T [+ y*chi_H] in C...
subroutine, public saha_state_from_nh_p(nh_code, p_code, t_out, y_out, eint_nh_out)
Given (nH, p) in CODE UNITS, find T and y by solving p = nH*(1+y(T))*T_code. Uses bisection on T....
Shared LTE lookup-table infrastructure (build/IO; not on the hot path).
subroutine, public precompute_fi_bypass_constants()
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.