33 double precision,
parameter :: LN10 = 2.302585092994046d0
37 double precision :: ent_shift_pe = 0.0d0, ent_inv_up = 1.0d0
41 double precision,
allocatable :: il_neOnH(:,:,:), il_Tfwd(:,:,:), il_pfwd(:,:,:)
42 double precision,
allocatable :: il_eintP(:,:,:), il_g1p(:,:,:), il_eintT(:,:,:)
66 character(len=8),
parameter :: quantity(6) = &
67 [character(8)::
'neOnH',
'Tfwd',
'pfwd',
'eintP',
'g1p',
'eintT']
68 character(len=3),
parameter :: deriv(4) = &
69 [character(3)::
'',
'_x',
'_y',
'_xy']
71 if (
mype == 0)
write(*,*) &
72 'EoS method: entropy (bicubic Hermite, 4-derivative corners, no closure)'
73 do iq = 1,
size(quantity)
74 do id = 1,
size(deriv)
86 call entropy_shift_prepare(
eos%neOnH, .false.)
87 call entropy_shift_prepare(
eos%neOnH_x, .false.)
88 call entropy_shift_prepare(
eos%neOnH_y, .false.)
89 call entropy_shift_prepare(
eos%neOnH_xy, .false.)
90 call entropy_shift_prepare(
eos%Tfwd, .false.)
91 call entropy_shift_prepare(
eos%Tfwd_x, .false.)
92 call entropy_shift_prepare(
eos%Tfwd_y, .false.)
93 call entropy_shift_prepare(
eos%Tfwd_xy, .false.)
94 call entropy_shift_prepare(
eos%pfwd, .false.)
95 call entropy_shift_prepare(
eos%pfwd_x, .false.)
96 call entropy_shift_prepare(
eos%pfwd_y, .false.)
97 call entropy_shift_prepare(
eos%pfwd_xy, .false.)
98 call entropy_shift_prepare(
eos%eintP, .false.)
99 call entropy_shift_prepare(
eos%eintP_x, .false.)
100 call entropy_shift_prepare(
eos%eintP_y, .false.)
101 call entropy_shift_prepare(
eos%eintP_xy, .false.)
102 call entropy_shift_prepare(
eos%g1p, .false.)
103 call entropy_shift_prepare(
eos%g1p_x, .false.)
104 call entropy_shift_prepare(
eos%g1p_y, .false.)
105 call entropy_shift_prepare(
eos%g1p_xy, .false.)
106 call entropy_shift_prepare(
eos%eintT, .true.)
107 call entropy_shift_prepare(
eos%eintT_x, .true.)
108 call entropy_shift_prepare(
eos%eintT_y, .true.)
109 call entropy_shift_prepare(
eos%eintT_xy, .true.)
113 call interleave(
eos%neOnH,
eos%neOnH_x,
eos%neOnH_y,
eos%neOnH_xy, il_neonh)
114 call interleave(
eos%Tfwd,
eos%Tfwd_x,
eos%Tfwd_y,
eos%Tfwd_xy, il_tfwd)
115 call interleave(
eos%pfwd,
eos%pfwd_x,
eos%pfwd_y,
eos%pfwd_xy, il_pfwd)
116 call interleave(
eos%eintP,
eos%eintP_x,
eos%eintP_y,
eos%eintP_xy, il_eintp)
117 call interleave(
eos%g1p,
eos%g1p_x,
eos%g1p_y,
eos%g1p_xy, il_g1p)
118 call interleave(
eos%eintT,
eos%eintT_x,
eos%eintT_y,
eos%eintT_xy, il_eintt)
122 eos%T%var2_min =
eos%Tfwd%var2_min
123 eos%eint_nH_min = 10.0d0**
eos%T%var2_min
124 eos%T%var2_max =
eos%Tfwd%var2_max
125 eos%T%var1_min =
eos%Tfwd%var1_min
126 eos%T%var1_max =
eos%Tfwd%var1_max
127 if (
mype == 0)
write(*,
'(A)') &
128 ' EoS entropy: forward + inverse tables loaded; no aux builds.'
132 pure subroutine locate_idx_uniform(n, vmin, vmax, val, ix, t, h)
133 integer,
intent(in) :: n
134 double precision,
intent(in) :: vmin, vmax, val
135 integer,
intent(out) :: ix
136 double precision,
intent(out) :: t, h
137 h = (vmax - vmin) / dble(n - 1)
138 if (val <= vmin)
then
139 ix = 1; t = 0.0d0;
return
141 if (val >= vmax)
then
142 ix = n - 1; t = 1.0d0;
return
144 ix = 1 + int((val - vmin) / h)
146 if (ix > n - 1) ix = n - 1
147 t = (val - (vmin + (ix - 1) * h)) / h
148 if (t < 0.0d0) t = 0.0d0
149 if (t > 1.0d0) t = 1.0d0
150 end subroutine locate_idx_uniform
152 pure subroutine locate_idx_nodes(n, nodes, val, ix, t, h)
153 integer,
intent(in) :: n
154 double precision,
intent(in) :: nodes(n), val
155 integer,
intent(out) :: ix
156 double precision,
intent(out) :: t, h
157 integer :: lo, mid, hi
158 if (val <= nodes(1))
then
159 ix = 1; t = 0.0d0; h = nodes(2) - nodes(1);
return
161 if (val >= nodes(n))
then
162 ix = n - 1; t = 1.0d0; h = nodes(n) - nodes(n - 1);
return
167 if (hi - lo <= 1)
exit
169 if (nodes(mid) <= val)
then
176 h = nodes(ix + 1) - nodes(ix)
177 t = (val - nodes(ix)) / h
178 if (t < 0.0d0) t = 0.0d0
179 if (t > 1.0d0) t = 1.0d0
180 end subroutine locate_idx_nodes
186 pure subroutine locate_idx_guard(n, nodes, guard, M, scale, val, ix, t, h)
187 integer,
intent(in) :: n, m
188 double precision,
intent(in) :: nodes(n), scale, val
189 integer,
intent(in) :: guard(m)
190 integer,
intent(out) :: ix
191 double precision,
intent(out) :: t, h
193 if (val <= nodes(1))
then
194 ix = 1; t = 0.0d0; h = nodes(2) - nodes(1);
return
196 if (val >= nodes(n))
then
197 ix = n - 1; t = 1.0d0; h = nodes(n) - nodes(n - 1);
return
199 k = 1 + min(m-1, max(0, int((val - nodes(1)) * scale)))
201 do while (j < n .and. nodes(j) < val)
204 do while (j > 1 .and. nodes(j-1) >= val)
207 if (nodes(j) > val) j = j - 1
208 ix = min(max(j, 1), n - 1)
209 h = nodes(ix + 1) - nodes(ix)
210 t = (val - nodes(ix)) / h
211 if (t < 0.0d0) t = 0.0d0
212 if (t > 1.0d0) t = 1.0d0
213 end subroutine locate_idx_guard
215 pure subroutine locate_idx_axis1(tab, val, ix, t, h)
217 double precision,
intent(in) :: val
218 integer,
intent(out) :: ix
219 double precision,
intent(out) :: t, h
220 if (tab%is_uniform)
then
221 call locate_idx_uniform(tab%dim1, tab%var1_min, tab%var1_max, val, ix, t, h)
222 else if (tab%guard_M_1 > 0)
then
223 call locate_idx_guard(tab%dim1, tab%var1_nodes, tab%guard_1, tab%guard_M_1, &
224 tab%guard_scale_1, val, ix, t, h)
226 call locate_idx_nodes(tab%dim1, tab%var1_nodes, val, ix, t, h)
228 end subroutine locate_idx_axis1
230 pure subroutine locate_idx_axis2(tab, val, ix, t, h)
232 double precision,
intent(in) :: val
233 integer,
intent(out) :: ix
234 double precision,
intent(out) :: t, h
235 if (tab%is_uniform)
then
236 call locate_idx_uniform(tab%dim2, tab%var2_min, tab%var2_max, val, ix, t, h)
237 else if (tab%guard_M_2 > 0)
then
238 call locate_idx_guard(tab%dim2, tab%var2_nodes, tab%guard_2, tab%guard_M_2, &
239 tab%guard_scale_2, val, ix, t, h)
241 call locate_idx_nodes(tab%dim2, tab%var2_nodes, val, ix, t, h)
243 end subroutine locate_idx_axis2
248 pure subroutine cubic_basis(t, H0, H1, H2, H3)
249 double precision,
intent(in) :: t
250 double precision,
intent(out) :: h0, h1, h2, h3
251 double precision :: t2, t3
254 h0 = 2.0d0*t3 - 3.0d0*t2 + 1.0d0
255 h1 = t3 - 2.0d0*t2 + t
256 h2 = -2.0d0*t3 + 3.0d0*t2
258 end subroutine cubic_basis
265 pure double precision function contract_il(c, ix, iy, dx, dy, &
266 H0x, H1x, H2x, H3x, &
267 H0y, H1y, H2y, H3y)
result(val)
268 double precision,
intent(in) :: c(:,:,:)
269 integer,
intent(in) :: ix, iy
270 double precision,
intent(in) ::
dx, dy
271 double precision,
intent(in) :: h0x, h1x, h2x, h3x
272 double precision,
intent(in) :: h0y, h1y, h2y, h3y
273 double precision :: f00, f10, f01, f11
274 double precision :: fx00, fx10, fx01, fx11
275 double precision :: fy00, fy10, fy01, fy11
276 double precision :: fxy00, fxy10, fxy01, fxy11
277 f00 = c(1,ix, iy ); f10 = c(1,ix+1,iy )
278 f01 = c(1,ix, iy+1); f11 = c(1,ix+1,iy+1)
279 fx00 = c(2,ix, iy ) *
dx
280 fx10 = c(2,ix+1,iy ) *
dx
281 fx01 = c(2,ix, iy+1) *
dx
282 fx11 = c(2,ix+1,iy+1) *
dx
283 fy00 = c(3,ix, iy ) * dy
284 fy10 = c(3,ix+1,iy ) * dy
285 fy01 = c(3,ix, iy+1) * dy
286 fy11 = c(3,ix+1,iy+1) * dy
287 fxy00 = c(4,ix, iy ) *
dx * dy
288 fxy10 = c(4,ix+1,iy ) *
dx * dy
289 fxy01 = c(4,ix, iy+1) *
dx * dy
290 fxy11 = c(4,ix+1,iy+1) *
dx * dy
291 val = h0x*h0y*f00 + h2x*h0y*f10 + h0x*h2y*f01 + h2x*h2y*f11 &
292 + h1x*h0y*fx00 + h3x*h0y*fx10 + h1x*h2y*fx01 + h3x*h2y*fx11 &
293 + h0x*h1y*fy00 + h2x*h1y*fy10 + h0x*h3y*fy01 + h2x*h3y*fy11 &
294 + h1x*h1y*fxy00 + h3x*h1y*fxy10 + h1x*h3y*fxy01 + h3x*h3y*fxy11
295 end function contract_il
298 pure double precision function hermite_eval(ax, c, x, y)
result(val)
300 double precision,
intent(in) :: c(:,:,:)
301 double precision,
intent(in) :: x, y
303 double precision :: tx, ty,
dx, dy
304 double precision :: h0x, h1x, h2x, h3x
305 double precision :: h0y, h1y, h2y, h3y
306 call locate_idx_axis1(ax, x, ix, tx,
dx)
307 call locate_idx_axis2(ax, y, iy, ty, dy)
308 call cubic_basis(tx, h0x, h1x, h2x, h3x)
309 call cubic_basis(ty, h0y, h1y, h2y, h3y)
310 val = contract_il(c, ix, iy,
dx, dy, h0x, h1x, h2x, h3x, h0y, h1y, h2y, h3y)
311 end function hermite_eval
315 subroutine interleave(f_t, fx_t, fy_t, fxy_t, c)
317 double precision,
allocatable,
intent(out) :: c(:,:,:)
318 allocate(c(4,
size(f_t%table,1),
size(f_t%table,2)))
320 c(2,:,:) = fx_t%table
321 c(3,:,:) = fy_t%table
322 c(4,:,:) = fxy_t%table
323 end subroutine interleave
329 double precision,
intent(in) :: log_nh_code, log_e_nh_code
334 double precision,
intent(in) :: log_nh_code, log_e_nh_code
335 p_nh_code = hermite_eval(
eos%pfwd, il_pfwd, log_nh_code, log_e_nh_code) &
336 * ent_inv_up / 10.0d0**log_nh_code
341 double precision,
intent(in) :: log_nh_code, log_e_nh_code
342 p_code = hermite_eval(
eos%pfwd, il_pfwd, log_nh_code, log_e_nh_code) * ent_inv_up
346 double precision,
intent(in) :: log_nh_code, log_e_nh_code
347 y = hermite_eval(
eos%neOnH, il_neonh, log_nh_code, log_e_nh_code)
353 double precision,
intent(in) :: log_nh_code, log_e_nh_code
354 double precision,
intent(out) :: t_code, y_out
356 double precision :: tx, ty,
dx, dy
357 double precision :: h0x, h1x, h2x, h3x
358 double precision :: h0y, h1y, h2y, h3y
359 call locate_idx_axis1(
eos%Tfwd, log_nh_code, ix, tx,
dx)
360 call locate_idx_axis2(
eos%Tfwd, log_e_nh_code, iy, ty, dy)
361 call cubic_basis(tx, h0x, h1x, h2x, h3x)
362 call cubic_basis(ty, h0y, h1y, h2y, h3y)
363 t_code = contract_il(il_tfwd, ix, iy,
dx, dy, h0x, h1x, h2x, h3x, h0y, h1y, h2y, h3y) &
365 y_out = contract_il(il_neonh, ix, iy,
dx, dy, h0x, h1x, h2x, h3x, h0y, h1y, h2y, h3y)
371 double precision,
intent(in) :: log_nh_code, log_p_nh_code
372 double precision :: log_e_nh_code
373 log_e_nh_code = hermite_eval(
eos%eintP, il_eintp, log_nh_code, log_p_nh_code) - ent_shift_pe
374 ratio = 10.0d0**(log_e_nh_code - log_p_nh_code)
380 double precision,
intent(in) :: log_nh_code, log_p_nh_code
381 double precision,
intent(out) :: log_eint_nh_code
382 double precision :: ratio, guess, lo, hi, mid, f_lo, f_hi, f_mid
383 double precision :: lim_lo, lim_hi, log_p_code
387 if (
eos%pfwd%is_uniform)
then
388 lim_lo =
eos%pfwd%var2_min
389 lim_hi =
eos%pfwd%var2_max
391 lim_lo =
eos%pfwd%var2_nodes(1)
392 lim_hi =
eos%pfwd%var2_nodes(
eos%pfwd%dim2)
396 guess = dlog10(max(ratio, 1.0
d-300)) + log_p_nh_code
397 guess = max(lim_lo, min(lim_hi, guess))
400 log_p_code = log_nh_code + log_p_nh_code
401 lo = max(lim_lo, guess - 2.0
d-2)
402 hi = min(lim_hi, guess + 2.0
d-2)
406 if (f_lo*f_hi <= 0.0d0)
exit
407 lo = max(lim_lo, lo - 1.0
d-1)
408 hi = min(lim_hi, hi + 1.0
d-1)
411 if (lo <= lim_lo .and. hi >= lim_hi)
exit
415 if (f_lo*f_hi > 0.0d0)
then
416 log_eint_nh_code = guess
421 mid = 0.5d0*(lo + hi)
423 if (f_mid*f_lo <= 0.0d0)
then
424 hi = mid; f_hi = f_mid
426 lo = mid; f_lo = f_mid
428 if (hi - lo < 1.0
d-12)
exit
430 log_eint_nh_code = 0.5d0*(lo + hi)
435 double precision function resid(x)
436 double precision,
intent(in) :: x
443 double precision,
intent(in) :: log_nh_code, log_p_nh_code
444 g1 = hermite_eval(
eos%g1p, il_g1p, log_nh_code, log_p_nh_code)
449 double precision,
intent(in) :: log_nh_code, log_t_code
450 log_e_nh_code = hermite_eval(
eos%eintT, il_eintt, log_nh_code, log_t_code) - ent_shift_pe
456 subroutine entropy_table_prepare(tc)
464 end subroutine entropy_table_prepare
467 subroutine entropy_shift_prepare(tc, axis2_is_T)
469 logical,
intent(in) :: axis2_is_t
475 call entropy_table_prepare(tc)
476 end subroutine entropy_shift_prepare
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
EoS state container – the single thermodynamic authority for AMRVAC.
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)
Entropy-method LTE EoS: every query is ONE bicubic Hermite evaluation.
pure double precision function, public entropy_p_from_eint(log_nh_code, log_e_nh_code)
Pressure in code units from (log nH, log eint/nH); the pfwd table holds p itself.
double precision function, public entropy_t_from_nh_eint(log_nh_code, log_e_nh_code)
Public lookups, code units in and out. Forward tables are on (log nH, log eint/nH),...
double precision function, public entropy_y_from_nh_eint(log_nh_code, log_e_nh_code)
double precision function, public entropy_eint_from_nh_t(log_nh_code, log_t_code)
log10(eint/nH) in code units from (log nH, log T); the table holds it in CGS
double precision function, public entropy_eint_from_nh_p(log_nh_code, log_p_nh_code)
eint/p in code units from (log nH, log p/nH). The table holds log10(eint/nH) in CGS
subroutine, public finalise_entropy_lte()
Entropy-method finalise: shift each loaded table's axes from CGS to code units and run the standard p...
pure double precision function, public entropy_p_nh_from_eint(log_nh_code, log_e_nh_code)
double precision function, public entropy_gamma1_from_nh_p(log_nh_code, log_p_nh_code)
subroutine, public entropy_t_and_y_from_nh_eint(log_nh_code, log_e_nh_code, t_code, y_out)
T and ne/nH at one point: Tfwd and neOnH share the forward grid, so the cell location and the basis a...
subroutine, public load_entropy_lte()
Forward (log nH, log eint/nH) -> thermodynamic state.
subroutine, public entropy_eint_from_p_bisect(log_nh_code, log_p_nh_code, log_eint_nh_code)
Bisection inverse: log10(eint/nH) [code] such that the forward pressure matches p exactly; the eintP ...
Shared LTE lookup-table infrastructure (build/IO; not on the hot path).
subroutine, public eos_validate_table(tc, name)
Defensive consistency check for one table after init. Aborts with a diagnostic message if any silent-...
subroutine, public load_tables_lte(fieldname)
Read one named table file into its eos% container. The filename encodes the composition (H or HHe) an...
subroutine, public shift_axis_to_code(tc)
Shift a table's (log_nH, log_eint/nH) axes – and adaptive node arrays if present – from CGS storage t...
subroutine, public shift_axis_to_code_t(tc)
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,...
subroutine, public ensure_axis_nodes(tc)
Ensure tcvar1_nodes / tcvar2_nodes are allocated and populated so that build_*_table routines and oth...
subroutine, public eos_build_guards(tc)
Build guard (bucket) arrays so that adaptive index lookup is O(1). No-op for uniform tables....
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 mype
The rank of the current MPI task.
double precision, dimension(:), allocatable, parameter d
double precision unit_temperature
Physical scaling factor for temperature.
double precision, dimension(:,:), allocatable dx
spatial steps for all dimensions at all levels