MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_eos_LTE_entropy.t
Go to the documentation of this file.
1!=============================================================================
2!> Entropy-method LTE EoS: every query is ONE bicubic Hermite evaluation.
3!>
4!> Each quantity is stored as four tables -- the value plus its three
5!> derivatives (d/dx, d/dy, d2/dxdy) at every node -- which fully determine
6!> the bicubic Hermite polynomial in each cell: exact at nodes, O(h^4) inside,
7!> no closure. No Newton, bisection, or iteration on the hot path; the
8!> iterative work is done offline against the analytic Saha solver (see
9!> entropy128/generate_entropy_tables.py).
10!>
11!> Forward tables, axes (log10 nH, log10 eint/nH):
12!> Tfwd -> T pfwd -> p neOnH -> ne/nH
13!> Inverse tables:
14!> eintP (log nH, log p/nH) -> log10(eint/nH)
15!> eintT (log nH, log T) -> log10(eint/nH)
16!> g1p (log nH, log p/nH) -> Gamma_1
17!>
18!> Gamma_1 is tabulated directly (g1p) rather than recovered from second
19!> derivatives, so the lookup stays smooth; values are Maxwell-consistent with
20!> (p, T) by construction.
21!=============================================================================
24 use mod_comm_lib, only: mpistop
30 implicit none
31 private
32
33 double precision, parameter :: LN10 = 2.302585092994046d0
34
36
37 !> Forward (log nH, log eint/nH) -> thermodynamic state
42 !> Inverse (log nH, log p/nH) -> eint/p ratio, Gamma_1
46 !> Inverse (log nH, log T) -> log10(eint/nH)
48
49contains
50
51 !> Entropy-method load: six quantities, each a value table plus its three
52 !> derivative tables, on the forward (nH, eint/nH), inverse-p (nH, p/nH) and
53 !> inverse-T (nH, T) grids. Every runtime query is one bicubic-Hermite
54 !> evaluation (see generate_entropy_tables.py).
55 subroutine load_entropy_lte()
56 integer :: iq, id
57 character(len=8), parameter :: quantity(6) = &
58 [character(8):: 'neOnH', 'Tfwd', 'pfwd', 'eintP', 'g1p', 'eintT']
59 character(len=3), parameter :: deriv(4) = &
60 [character(3):: '', '_x', '_y', '_xy']
61
62 if (mype == 0) write(*,*) &
63 'EoS method: entropy (bicubic Hermite, 4-derivative corners, no closure)'
64 do iq = 1, size(quantity)
65 do id = 1, size(deriv)
66 call load_tables_lte(trim(quantity(iq))//trim(deriv(id)))
67 end do
68 end do
69 end subroutine load_entropy_lte
70
71 !> Entropy-method finalise: shift each loaded table's axes from CGS to code
72 !> units and run the standard prepare. No aux builds -- every runtime quantity
73 !> is a single Hermite lookup. neOnH/Tfwd/pfwd/eintP/g1p are on (log nH,
74 !> log eint/nH) (axis2_is_T=.false.); eintT is on (log nH, log T)
75 !> (axis2_is_T=.true.). Each quantity ships value + three derivative tables.
77 call entropy_shift_prepare(eos%neOnH, .false.)
78 call entropy_shift_prepare(eos%neOnH_x, .false.)
79 call entropy_shift_prepare(eos%neOnH_y, .false.)
80 call entropy_shift_prepare(eos%neOnH_xy, .false.)
81 call entropy_shift_prepare(eos%Tfwd, .false.)
82 call entropy_shift_prepare(eos%Tfwd_x, .false.)
83 call entropy_shift_prepare(eos%Tfwd_y, .false.)
84 call entropy_shift_prepare(eos%Tfwd_xy, .false.)
85 call entropy_shift_prepare(eos%pfwd, .false.)
86 call entropy_shift_prepare(eos%pfwd_x, .false.)
87 call entropy_shift_prepare(eos%pfwd_y, .false.)
88 call entropy_shift_prepare(eos%pfwd_xy, .false.)
89 call entropy_shift_prepare(eos%eintP, .false.)
90 call entropy_shift_prepare(eos%eintP_x, .false.)
91 call entropy_shift_prepare(eos%eintP_y, .false.)
92 call entropy_shift_prepare(eos%eintP_xy, .false.)
93 call entropy_shift_prepare(eos%g1p, .false.)
94 call entropy_shift_prepare(eos%g1p_x, .false.)
95 call entropy_shift_prepare(eos%g1p_y, .false.)
96 call entropy_shift_prepare(eos%g1p_xy, .false.)
97 call entropy_shift_prepare(eos%eintT, .true.)
98 call entropy_shift_prepare(eos%eintT_x, .true.)
99 call entropy_shift_prepare(eos%eintT_y, .true.)
100 call entropy_shift_prepare(eos%eintT_xy, .true.)
102 !> The entropy method never loads eos%T, but several callers read
103 !> eos%T%var{1,2}_{min,max} (the cold-cell eint floor, FI-fallback
104 !> checks). Source them from eos%Tfwd, on the same forward grid.
105 eos%T%var2_min = eos%Tfwd%var2_min
106 eos%T%var2_max = eos%Tfwd%var2_max
107 eos%T%var1_min = eos%Tfwd%var1_min
108 eos%T%var1_max = eos%Tfwd%var1_max
109 if (mype == 0) write(*,'(A)') &
110 ' EoS entropy: forward + inverse tables loaded; no aux builds.'
111 end subroutine finalise_entropy_lte
112
113 !> Index location: uniform and non-uniform variants.
114 pure subroutine locate_idx_uniform(n, vmin, vmax, val, ix, t, h)
115 integer, intent(in) :: n
116 double precision, intent(in) :: vmin, vmax, val
117 integer, intent(out) :: ix
118 double precision, intent(out) :: t, h
119 h = (vmax - vmin) / dble(n - 1)
120 if (val <= vmin) then
121 ix = 1; t = 0.0d0; return
122 end if
123 if (val >= vmax) then
124 ix = n - 1; t = 1.0d0; return
125 end if
126 ix = 1 + int((val - vmin) / h)
127 if (ix < 1) ix = 1
128 if (ix > n - 1) ix = n - 1
129 t = (val - (vmin + (ix - 1) * h)) / h
130 if (t < 0.0d0) t = 0.0d0
131 if (t > 1.0d0) t = 1.0d0
132 end subroutine locate_idx_uniform
133
134 pure subroutine locate_idx_nodes(n, nodes, val, ix, t, h)
135 integer, intent(in) :: n
136 double precision, intent(in) :: nodes(n), val
137 integer, intent(out) :: ix
138 double precision, intent(out) :: t, h
139 integer :: lo, mid, hi
140 if (val <= nodes(1)) then
141 ix = 1; t = 0.0d0; h = nodes(2) - nodes(1); return
142 end if
143 if (val >= nodes(n)) then
144 ix = n - 1; t = 1.0d0; h = nodes(n) - nodes(n - 1); return
145 end if
146 ! Binary search for cell containing val.
147 lo = 1; hi = n
148 do
149 if (hi - lo <= 1) exit
150 mid = (lo + hi) / 2
151 if (nodes(mid) <= val) then
152 lo = mid
153 else
154 hi = mid
155 end if
156 end do
157 ix = lo
158 h = nodes(ix + 1) - nodes(ix)
159 t = (val - nodes(ix)) / h
160 if (t < 0.0d0) t = 0.0d0
161 if (t > 1.0d0) t = 1.0d0
162 end subroutine locate_idx_nodes
163
164 pure subroutine locate_idx_axis1(tab, val, ix, t, h)
165 type(eos_table_container), intent(in) :: tab
166 double precision, intent(in) :: val
167 integer, intent(out) :: ix
168 double precision, intent(out) :: t, h
169 if (tab%is_uniform) then
170 call locate_idx_uniform(tab%dim1, tab%var1_min, tab%var1_max, val, ix, t, h)
171 else
172 call locate_idx_nodes(tab%dim1, tab%var1_nodes, val, ix, t, h)
173 end if
174 end subroutine locate_idx_axis1
175
176 pure subroutine locate_idx_axis2(tab, val, ix, t, h)
177 type(eos_table_container), intent(in) :: tab
178 double precision, intent(in) :: val
179 integer, intent(out) :: ix
180 double precision, intent(out) :: t, h
181 if (tab%is_uniform) then
182 call locate_idx_uniform(tab%dim2, tab%var2_min, tab%var2_max, val, ix, t, h)
183 else
184 call locate_idx_nodes(tab%dim2, tab%var2_nodes, val, ix, t, h)
185 end if
186 end subroutine locate_idx_axis2
187
188 !> 1D cubic Hermite basis on t in [0, 1] (value weights).
189 !> H0 = 2t^3 - 3t^2 + 1 value at t=0 H2 = -2t^3 + 3t^2 value at t=1
190 !> H1 = t^3 - 2t^2 + t slope at t=0 H3 = t^3 - t^2 slope at t=1
191 pure subroutine cubic_basis(t, H0, H1, H2, H3)
192 double precision, intent(in) :: t
193 double precision, intent(out) :: h0, h1, h2, h3
194 double precision :: t2, t3
195 t2 = t * t
196 t3 = t2 * t
197 h0 = 2.0d0*t3 - 3.0d0*t2 + 1.0d0
198 h1 = t3 - 2.0d0*t2 + t
199 h2 = -2.0d0*t3 + 3.0d0*t2
200 h3 = t3 - t2
201 end subroutine cubic_basis
202
203 !> Bicubic Hermite evaluator: value only.
204 !
205 !> 4 stored derivatives per corner (f, fx, fy, fxy) x 4 corners = 16
206 !> conditions; fully determines the 16-coefficient degree-(3,3)
207 !> polynomial. The stored fx, fy, fxy are physical-axis derivatives;
208 !> we multiply by the local cell widths to convert to unit-interval
209 !> basis coordinates.
210 pure subroutine bicubic_hermite_eval(f_t, fx_t, fy_t, fxy_t, x, y, val)
211 type(eos_table_container), intent(in) :: f_t, fx_t, fy_t, fxy_t
212 double precision, intent(in) :: x, y
213 double precision, intent(out) :: val
214 integer :: ix, iy
215 double precision :: tx, ty, dx, dy
216 double precision :: h0x, h1x, h2x, h3x
217 double precision :: h0y, h1y, h2y, h3y
218 double precision :: f00, f10, f01, f11
219 double precision :: fx00, fx10, fx01, fx11
220 double precision :: fy00, fy10, fy01, fy11
221 double precision :: fxy00, fxy10, fxy01, fxy11
222
223 call locate_idx_axis1(f_t, x, ix, tx, dx)
224 call locate_idx_axis2(f_t, y, iy, ty, dy)
225
226 call cubic_basis(tx, h0x, h1x, h2x, h3x)
227 call cubic_basis(ty, h0y, h1y, h2y, h3y)
228
229 f00 = f_t%table(ix, iy ); f10 = f_t%table(ix+1, iy )
230 f01 = f_t%table(ix, iy+1); f11 = f_t%table(ix+1, iy+1)
231 fx00 = fx_t%table(ix, iy ) * dx
232 fx10 = fx_t%table(ix+1, iy ) * dx
233 fx01 = fx_t%table(ix, iy+1) * dx
234 fx11 = fx_t%table(ix+1, iy+1) * dx
235 fy00 = fy_t%table(ix, iy ) * dy
236 fy10 = fy_t%table(ix+1, iy ) * dy
237 fy01 = fy_t%table(ix, iy+1) * dy
238 fy11 = fy_t%table(ix+1, iy+1) * dy
239 fxy00 = fxy_t%table(ix, iy ) * dx * dy
240 fxy10 = fxy_t%table(ix+1, iy ) * dx * dy
241 fxy01 = fxy_t%table(ix, iy+1) * dx * dy
242 fxy11 = fxy_t%table(ix+1, iy+1) * dx * dy
243
244 val = h0x*h0y*f00 + h2x*h0y*f10 + h0x*h2y*f01 + h2x*h2y*f11 &
245 + h1x*h0y*fx00 + h3x*h0y*fx10 + h1x*h2y*fx01 + h3x*h2y*fx11 &
246 + h0x*h1y*fy00 + h2x*h1y*fy10 + h0x*h3y*fy01 + h2x*h3y*fy11 &
247 + h1x*h1y*fxy00 + h3x*h1y*fxy10 + h1x*h3y*fxy01 + h3x*h3y*fxy11
248 end subroutine bicubic_hermite_eval
249
250 !> Public wrappers -- code-unit out.
251 double precision function entropy_t_from_nh_eint(Tfwd, Tfwd_x, Tfwd_y, Tfwd_xy, &
252 log_nH_code, log_e_nh_code) &
253 result(t_code)
254 type(eos_table_container), intent(in) :: tfwd, tfwd_x, tfwd_y, tfwd_xy
255 double precision, intent(in) :: log_nh_code, log_e_nh_code
256 double precision :: t_cgs
257 call bicubic_hermite_eval(tfwd, tfwd_x, tfwd_y, tfwd_xy, &
258 log_nh_code, log_e_nh_code, t_cgs)
259 t_code = t_cgs / unit_temperature
260 end function entropy_t_from_nh_eint
261
262 pure double precision function entropy_p_nh_from_eint(pfwd, pfwd_x, pfwd_y, pfwd_xy, &
263 log_nH_code, log_e_nh_code) &
264 result(p_nh_code)
265 type(eos_table_container), intent(in) :: pfwd, pfwd_x, pfwd_y, pfwd_xy
266 double precision, intent(in) :: log_nh_code, log_e_nh_code
267 double precision :: p_cgs, nh_cgs
268 call bicubic_hermite_eval(pfwd, pfwd_x, pfwd_y, pfwd_xy, &
269 log_nh_code, log_e_nh_code, p_cgs)
270 nh_cgs = 10.0d0**(log_nh_code + dlog10(unit_numberdensity))
271 p_nh_code = (p_cgs / nh_cgs) * unit_numberdensity / unit_pressure
272 end function entropy_p_nh_from_eint
273
274 !> Forward: ne/nH from (log nH, log eint/nH). Single bicubic Hermite
275 !> eval of the neOnH table.
276 double precision function entropy_y_from_nh_eint(neOnH, neOnH_x, neOnH_y, &
277 neOnH_xy, log_nH_code, &
278 log_e_nh_code) result(y)
279 type(eos_table_container), intent(in) :: neonh, neonh_x, neonh_y, neonh_xy
280 double precision, intent(in) :: log_nh_code, log_e_nh_code
281 call bicubic_hermite_eval(neonh, neonh_x, neonh_y, neonh_xy, &
282 log_nh_code, log_e_nh_code, y)
283 end function entropy_y_from_nh_eint
284
285 !> Forward: T and y from (log nH, log eint/nH) -- combined for the
286 !> update_eos_LTE hot path that needs both at once.
287 subroutine entropy_t_and_y_from_nh_eint(Tfwd, Tfwd_x, Tfwd_y, Tfwd_xy, &
288 neOnH, neOnH_x, neOnH_y, neOnH_xy, &
289 log_nH_code, log_e_nh_code, &
290 T_code, y_out)
291 !> Fused (T, y) lookup: shares ONE cell-location call between the two
292 !> bicubic Hermite evaluations. Tfwd and neOnH live on the same
293 !> adaptive (lr, le) grid (same axis nodes), so the (ix, iy, tx, ty,
294 !> dx, dy) coordinates and the cubic-basis values are identical for
295 !> both. We compute them once and re-use, saving the locate work
296 !> (binary search on adaptive axes) and the basis evaluations.
297 type(eos_table_container), intent(in) :: tfwd, tfwd_x, tfwd_y, tfwd_xy
298 type(eos_table_container), intent(in) :: neonh, neonh_x, neonh_y, neonh_xy
299 double precision, intent(in) :: log_nh_code, log_e_nh_code
300 double precision, intent(out) :: t_code, y_out
301 double precision :: t_cgs
302 integer :: ix, iy
303 double precision :: tx, ty, dx, dy
304 double precision :: h0x, h1x, h2x, h3x
305 double precision :: h0y, h1y, h2y, h3y
306
307 call locate_idx_axis1(tfwd, log_nh_code, ix, tx, dx)
308 call locate_idx_axis2(tfwd, log_e_nh_code, iy, ty, dy)
309 call cubic_basis(tx, h0x, h1x, h2x, h3x)
310 call cubic_basis(ty, h0y, h1y, h2y, h3y)
311
312 t_cgs = contract_value(tfwd, tfwd_x, tfwd_y, tfwd_xy, ix, iy, &
313 dx, dy, h0x, h1x, h2x, h3x, h0y, h1y, h2y, h3y)
314 y_out = contract_value(neonh, neonh_x, neonh_y, neonh_xy, ix, iy, &
315 dx, dy, h0x, h1x, h2x, h3x, h0y, h1y, h2y, h3y)
316 t_code = t_cgs / unit_temperature
317 end subroutine entropy_t_and_y_from_nh_eint
318
319 !> Bicubic Hermite contraction given pre-computed cell coordinates and
320 !> basis values. Lets multiple quantities at the SAME query point share
321 !> the cell-location and basis-evaluation work. Returns value only.
322 pure double precision function contract_value(f_t, fx_t, fy_t, fxy_t, &
323 ix, iy, dx, dy, &
324 H0x, H1x, H2x, H3x, &
325 H0y, H1y, H2y, H3y) result(val)
326 !> Shared-locate bicubic-Hermite contraction for fused multi-quantity
327 !> lookups at the same query point.
328 type(eos_table_container), intent(in) :: f_t, fx_t, fy_t, fxy_t
329 integer, intent(in) :: ix, iy
330 double precision, intent(in) :: dx, dy
331 double precision, intent(in) :: h0x, h1x, h2x, h3x
332 double precision, intent(in) :: h0y, h1y, h2y, h3y
333 double precision :: f00, f10, f01, f11
334 double precision :: fx00, fx10, fx01, fx11
335 double precision :: fy00, fy10, fy01, fy11
336 double precision :: fxy00, fxy10, fxy01, fxy11
337 f00 = f_t%table(ix, iy ); f10 = f_t%table(ix+1, iy )
338 f01 = f_t%table(ix, iy+1); f11 = f_t%table(ix+1, iy+1)
339 fx00 = fx_t%table(ix, iy ) * dx
340 fx10 = fx_t%table(ix+1, iy ) * dx
341 fx01 = fx_t%table(ix, iy+1) * dx
342 fx11 = fx_t%table(ix+1, iy+1) * dx
343 fy00 = fy_t%table(ix, iy ) * dy
344 fy10 = fy_t%table(ix+1, iy ) * dy
345 fy01 = fy_t%table(ix, iy+1) * dy
346 fy11 = fy_t%table(ix+1, iy+1) * dy
347 fxy00 = fxy_t%table(ix, iy ) * dx * dy
348 fxy10 = fxy_t%table(ix+1, iy ) * dx * dy
349 fxy01 = fxy_t%table(ix, iy+1) * dx * dy
350 fxy11 = fxy_t%table(ix+1, iy+1) * dx * dy
351 val = h0x*h0y*f00 + h2x*h0y*f10 + h0x*h2y*f01 + h2x*h2y*f11 &
352 + h1x*h0y*fx00 + h3x*h0y*fx10 + h1x*h2y*fx01 + h3x*h2y*fx11 &
353 + h0x*h1y*fy00 + h2x*h1y*fy10 + h0x*h3y*fy01 + h2x*h3y*fy11 &
354 + h1x*h1y*fxy00 + h3x*h1y*fxy10 + h1x*h3y*fxy01 + h3x*h3y*fxy11
355 end function contract_value
356
357 !> Inverse: log10(eint/nH) from (log nH, log p/nH). Single bicubic
358 !> Hermite eval of the eintP table. Replaces p2eint bisection.
359 !
360 !> Returns the eint/p ratio for drop-in compatibility with the
361 !> existing p2eint_from_nH_p call sites which use it as
362 !> eint = p * (returned ratio).
363 double precision function entropy_eint_from_nh_p(eintP, eintP_x, eintP_y, eintP_xy, &
364 log_nH_code, log_p_nH_code) result(ratio)
365 !> Returns eint/p in dimensionless code units. The stored table value is
366 !> log10(eint/nH) in CGS; we convert to code (axis-2 shift = the same
367 !> log10(unit_pressure/unit_numberdensity) as for log(p/nH) and log(eint/nH))
368 !> before forming the ratio with log_p_nH_code, otherwise the units mix.
369 type(eos_table_container), intent(in) :: eintp, eintp_x, eintp_y, eintp_xy
370 double precision, intent(in) :: log_nh_code, log_p_nh_code
371 double precision :: log_e_nh_cgs, log_e_nh_code
372 call bicubic_hermite_eval(eintp, eintp_x, eintp_y, eintp_xy, &
373 log_nh_code, log_p_nh_code, log_e_nh_cgs)
374 log_e_nh_code = log_e_nh_cgs - dlog10(unit_pressure / unit_numberdensity)
375 ratio = 10.0d0**(log_e_nh_code - log_p_nh_code)
376 end function entropy_eint_from_nh_p
377
378 !> Bisection inverse for the entropy table set: find log10(eint/nH) [code] such that
379 !> p(nH, eint) = p_target exactly, using the forward pfwd table. The eintP table only
380 !> supplies the initial guess. This is the entropy-mode counterpart of
381 !> eint_from_p_bisect (mod_eos_LTE), which is bound to eos%log_p - a table that is not
382 !> loaded when eos_method='entropy' (its use there segfaulted on an unallocated array).
383 subroutine entropy_eint_from_p_bisect(pfwd, pfwd_x, pfwd_y, pfwd_xy, &
384 eintP, eintP_x, eintP_y, eintP_xy, &
385 log_nH_code, log_p_nH_code, log_eint_nH_code)
386 type(eos_table_container), intent(in) :: pfwd, pfwd_x, pfwd_y, pfwd_xy
387 type(eos_table_container), intent(in) :: eintp, eintp_x, eintp_y, eintp_xy
388 double precision, intent(in) :: log_nh_code, log_p_nh_code
389 double precision, intent(out) :: log_eint_nh_code
390 double precision :: ratio, guess, lo, hi, mid, f_lo, f_hi, f_mid
391 double precision :: lim_lo, lim_hi
392 integer :: iter
393
394 !> eint-axis limits of the forward table (uniform vs adaptive grid)
395 if (pfwd%is_uniform) then
396 lim_lo = pfwd%var2_min
397 lim_hi = pfwd%var2_max
398 else
399 lim_lo = pfwd%var2_nodes(1)
400 lim_hi = pfwd%var2_nodes(pfwd%dim2)
401 end if
402
403 !> initial guess from the inverse table
404 ratio = entropy_eint_from_nh_p(eintp, eintp_x, eintp_y, eintp_xy, &
405 log_nh_code, log_p_nh_code)
406 guess = dlog10(max(ratio, 1.0d-300)) + log_p_nh_code
407 guess = max(lim_lo, min(lim_hi, guess))
408
409 !> bracket, expanding until the target is straddled
410 lo = max(lim_lo, guess - 2.0d-2)
411 hi = min(lim_hi, guess + 2.0d-2)
412 f_lo = dlog10(max(entropy_p_nh_from_eint(pfwd, pfwd_x, pfwd_y, pfwd_xy, &
413 log_nh_code, lo), 1.0d-300)) - log_p_nh_code
414 f_hi = dlog10(max(entropy_p_nh_from_eint(pfwd, pfwd_x, pfwd_y, pfwd_xy, &
415 log_nh_code, hi), 1.0d-300)) - log_p_nh_code
416 do iter = 1, 12
417 if (f_lo*f_hi <= 0.0d0) exit
418 lo = max(lim_lo, lo - 1.0d-1)
419 hi = min(lim_hi, hi + 1.0d-1)
420 f_lo = dlog10(max(entropy_p_nh_from_eint(pfwd, pfwd_x, pfwd_y, pfwd_xy, &
421 log_nh_code, lo), 1.0d-300)) - log_p_nh_code
422 f_hi = dlog10(max(entropy_p_nh_from_eint(pfwd, pfwd_x, pfwd_y, pfwd_xy, &
423 log_nh_code, hi), 1.0d-300)) - log_p_nh_code
424 if (lo <= lim_lo .and. hi >= lim_hi) exit
425 end do
426
427 !> no bracket (target outside the table range) -> keep the table guess
428 if (f_lo*f_hi > 0.0d0) then
429 log_eint_nh_code = guess
430 return
431 end if
432
433 do iter = 1, 40
434 mid = 0.5d0*(lo + hi)
435 f_mid = dlog10(max(entropy_p_nh_from_eint(pfwd, pfwd_x, pfwd_y, pfwd_xy, &
436 log_nh_code, mid), 1.0d-300)) - log_p_nh_code
437 if (f_mid*f_lo <= 0.0d0) then
438 hi = mid; f_hi = f_mid
439 else
440 lo = mid; f_lo = f_mid
441 end if
442 if (hi - lo < 1.0d-12) exit
443 end do
444 log_eint_nh_code = 0.5d0*(lo + hi)
445 end subroutine entropy_eint_from_p_bisect
446
447 !> Inverse: Gamma_1 from (log nH, log p/nH). Bicubic Hermite of g1p.
448 double precision function entropy_gamma1_from_nh_p(g1p, g1p_x, g1p_y, g1p_xy, &
449 log_nH_code, log_p_nH_code) &
450 result(g1)
451 type(eos_table_container), intent(in) :: g1p, g1p_x, g1p_y, g1p_xy
452 double precision, intent(in) :: log_nh_code, log_p_nh_code
453 call bicubic_hermite_eval(g1p, g1p_x, g1p_y, g1p_xy, &
454 log_nh_code, log_p_nh_code, g1)
455 end function entropy_gamma1_from_nh_p
456
457 !> Inverse: log10(eint/nH) from (log nH, log T). Single bicubic
458 !> Hermite eval of the eintT table.
459 double precision function entropy_eint_from_nh_t(eintT, eintT_x, eintT_y, eintT_xy, &
460 log_nH_code, log_T_code) &
461 result(log_e_nh_code)
462 !> Stored table value is log10(eint/nH) in CGS; convert to code units
463 !> before returning so the caller can use it as a code-unit log directly.
464 type(eos_table_container), intent(in) :: eintt, eintt_x, eintt_y, eintt_xy
465 double precision, intent(in) :: log_nh_code, log_t_code
466 double precision :: log_e_nh_cgs
467 call bicubic_hermite_eval(eintt, eintt_x, eintt_y, eintt_xy, &
468 log_nh_code, log_t_code, log_e_nh_cgs)
469 log_e_nh_code = log_e_nh_cgs - dlog10(unit_pressure / unit_numberdensity)
470 end function entropy_eint_from_nh_t
471
472
473 !> Per-container code-unit shift + prepare for loaded entropy tables
474 !> (moved from mod_eos_LTE_tables; uses the shared infra there).
475 subroutine entropy_table_prepare(tc)
476 !> Bundle ensure_axis_nodes + precompute_step_inv + build_guards + validate,
477 !> the identical four-step setup each loaded entropy table container needs.
478 type(eos_table_container), intent(inout) :: tc
479 call ensure_axis_nodes(tc)
480 call precompute_step_inv(tc)
481 call eos_build_guards(tc)
482 call eos_validate_table(tc, trim(tc%filename))
483 end subroutine entropy_table_prepare
484
485
486 subroutine entropy_shift_prepare(tc, axis2_is_T)
487 type(eos_table_container), intent(inout) :: tc
488 logical, intent(in) :: axis2_is_t
489 if (axis2_is_t) then
490 call shift_axis_to_code_t(tc)
491 else
492 call shift_axis_to_code(tc)
493 end if
494 call entropy_table_prepare(tc)
495 end subroutine entropy_shift_prepare
496
497end module mod_eos_lte_entropy
498!> Needs a line after to pass the preprocessor
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.
double precision function, public entropy_eint_from_nh_t(eintt, eintt_x, eintt_y, eintt_xy, log_nh_code, log_t_code)
Inverse: log10(eint/nH) from (log nH, log T). Single bicubic Hermite eval of the eintT table.
pure double precision function, public entropy_p_nh_from_eint(pfwd, pfwd_x, pfwd_y, pfwd_xy, log_nh_code, log_e_nh_code)
subroutine, public entropy_eint_from_p_bisect(pfwd, pfwd_x, pfwd_y, pfwd_xy, eintp, eintp_x, eintp_y, eintp_xy, log_nh_code, log_p_nh_code, log_eint_nh_code)
Bisection inverse for the entropy table set: find log10(eint/nH) [code] such that p(nH,...
double precision function, public entropy_t_from_nh_eint(tfwd, tfwd_x, tfwd_y, tfwd_xy, log_nh_code, log_e_nh_code)
Public wrappers – code-unit out.
subroutine, public finalise_entropy_lte()
Entropy-method finalise: shift each loaded table's axes from CGS to code units and run the standard p...
double precision function, public entropy_gamma1_from_nh_p(g1p, g1p_x, g1p_y, g1p_xy, log_nh_code, log_p_nh_code)
Inverse: Gamma_1 from (log nH, log p/nH). Bicubic Hermite of g1p.
subroutine, public entropy_t_and_y_from_nh_eint(tfwd, tfwd_x, tfwd_y, tfwd_xy, neonh, neonh_x, neonh_y, neonh_xy, log_nh_code, log_e_nh_code, t_code, y_out)
Forward: T and y from (log nH, log eint/nH) – combined for the update_eos_LTE hot path that needs bot...
double precision function, public entropy_y_from_nh_eint(neonh, neonh_x, neonh_y, neonh_xy, log_nh_code, log_e_nh_code)
Forward: ne/nH from (log nH, log eint/nH). Single bicubic Hermite eval of the neOnH table.
double precision function, public entropy_eint_from_nh_p(eintp, eintp_x, eintp_y, eintp_xy, log_nh_code, log_p_nh_code)
Inverse: log10(eint/nH) from (log nH, log p/nH). Single bicubic Hermite eval of the eintP table....
subroutine, public load_entropy_lte()
Forward (log nH, log eint/nH) -> thermodynamic state.
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 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....
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 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