MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_eos_LTE_tables.t
Go to the documentation of this file.
1!=============================================================================
2!> Shared LTE lookup-table infrastructure (build/IO; not on the hot path).
3!>
4!> The table-file machinery used by ALL LTE methods (state, entropy, analytic):
5!> binary-file readers (read_eos_from_file / load_tables_LTE / try_load_tables_LTE),
6!> per-container preparation (ensure_axis_nodes, code-unit axis shifts, O(1) guard
7!> arrays via eos_build_guards, eos_validate_table) and the fully-ionised bypass
8!> constants. NOT a method: 'tables' here means the literal lookup tables, not the
9!> 'state' method (which lives in mod_eos_LTE_state and consumes this infra).
10!> Everything here runs once, during eos_init_LTE / eos_finalise_LTE.
11!=============================================================================
16 use mod_comm_lib, only: mpistop
17 implicit none
18 private
19
20 !> Shared table-file I/O + per-container prep, consumed by mod_eos_LTE_state
21 !> (state method) and mod_eos_LTE_entropy (entropy method).
25
26 character(len=std_len), parameter :: eos_table_prefix = "LTEeos_"
27
28contains
29
30 !> Ensure tc%var1_nodes / tc%var2_nodes are allocated and populated so
31 !> that build_*_table routines and other code can reference axis positions
32 !> uniformly (no is_uniform branching). For uniform tables we generate
33 !> evenly-spaced node arrays from var{1,2}_min/max.
34 !>
35 !> Lookup performance is unaffected: bicubic_lookup / bilinear_lookup
36 !> still branch on tc%is_uniform -- uniform tables go through the affine
37 !> fast path; only adaptive tables consult var{1,2}_nodes.
38 subroutine ensure_axis_nodes(tc)
39 type(eos_table_container), intent(inout) :: tc
40 integer :: k
41 double precision :: dx
42 if (.not. allocated(tc%table)) return
43 if (.not. allocated(tc%var1_nodes)) then
44 allocate(tc%var1_nodes(tc%dim1))
45 dx = (tc%var1_max - tc%var1_min) / dble(tc%dim1 - 1)
46 do k = 1, tc%dim1
47 tc%var1_nodes(k) = tc%var1_min + (k - 1) * dx
48 end do
49 end if
50 if (.not. allocated(tc%var2_nodes)) then
51 allocate(tc%var2_nodes(tc%dim2))
52 dx = (tc%var2_max - tc%var2_min) / dble(tc%dim2 - 1)
53 do k = 1, tc%dim2
54 tc%var2_nodes(k) = tc%var2_min + (k - 1) * dx
55 end do
56 end if
57 end subroutine ensure_axis_nodes
58
59 !> Shift a table's (log_nH, log_eint/nH) axes -- and adaptive node arrays
60 !> if present -- from CGS storage to code units. Used by the entropy
61 !> method so eos_finalise can apply the shift AFTER unit_* are populated.
62 subroutine shift_axis_to_code(tc)
63 !> CGS -> code shift assuming axis-1 = log(nH) and axis-2 = log(eint/nH)
64 !> (or log(p/nH); both share the same erg/particle unit conversion).
65 type(eos_table_container), intent(inout) :: tc
66 tc%var1_min = tc%var1_min - dlog10(unit_numberdensity)
67 tc%var1_max = tc%var1_max - dlog10(unit_numberdensity)
68 tc%var2_min = tc%var2_min - dlog10(unit_pressure/unit_numberdensity)
69 tc%var2_max = tc%var2_max - dlog10(unit_pressure/unit_numberdensity)
70 if (.not. tc%is_uniform) then
71 tc%var1_nodes = tc%var1_nodes - dlog10(unit_numberdensity)
72 tc%var2_nodes = tc%var2_nodes - dlog10(unit_pressure/unit_numberdensity)
73 end if
74 end subroutine shift_axis_to_code
75
76 subroutine shift_axis_to_code_t(tc)
77 !> CGS -> code shift for tables whose axis-2 = log(T) (K).
78 !> Axis-1 = log(nH) shift as in the standard case.
79 type(eos_table_container), intent(inout) :: tc
80 tc%var1_min = tc%var1_min - dlog10(unit_numberdensity)
81 tc%var1_max = tc%var1_max - dlog10(unit_numberdensity)
82 tc%var2_min = tc%var2_min - dlog10(unit_temperature)
83 tc%var2_max = tc%var2_max - dlog10(unit_temperature)
84 if (.not. tc%is_uniform) then
85 tc%var1_nodes = tc%var1_nodes - dlog10(unit_numberdensity)
86 tc%var2_nodes = tc%var2_nodes - dlog10(unit_temperature)
87 end if
88 end subroutine shift_axis_to_code_t
89
90 !> Build guard (bucket) arrays so that adaptive index lookup is O(1).
91 !> No-op for uniform tables. Safe to call repeatedly.
92 !> Idea here is to map nonuniform table nodes to a uniform search grid
93 subroutine eos_build_guards(tc)
94 type(eos_table_container), intent(inout) :: tc
95 if (tc%is_uniform) return
96 if (.not. allocated(tc%var1_nodes)) return
97 if (.not. allocated(tc%var2_nodes)) return
98 call build_guard_for_axis(tc%var1_nodes, tc%dim1, &
99 tc%guard_1, tc%guard_M_1, tc%guard_scale_1)
100 call build_guard_for_axis(tc%var2_nodes, tc%dim2, &
101 tc%guard_2, tc%guard_M_2, tc%guard_scale_2)
102 end subroutine eos_build_guards
103
104 subroutine build_guard_for_axis(nodes, n, guard, M_out, scale_out)
105 double precision, intent(in) :: nodes(:)
106 integer, intent(in) :: n
107 integer, allocatable, intent(inout) :: guard(:)
108 integer, intent(out) :: m_out
109 double precision, intent(out) :: scale_out
110
111 integer, parameter :: safety = 4
112 integer :: k, ii, m
113 double precision :: xmin, xmax, span, min_gap, bk
114
115 xmin = nodes(1); xmax = nodes(n)
116 span = xmax - xmin
117 if (span <= 0.0d0 .or. n < 2) then
118 m_out = 0; scale_out = 0.0d0
119 if (allocated(guard)) deallocate(guard)
120 return
121 end if
122 min_gap = huge(1.0d0)
123 do ii = 1, n-1
124 if (nodes(ii+1) - nodes(ii) < min_gap) min_gap = nodes(ii+1) - nodes(ii)
125 end do
126 if (min_gap <= 0.0d0) then
127 !> Degenerate node array -- fall back to bsearch by leaving M=0
128 m_out = 0; scale_out = 0.0d0
129 if (allocated(guard)) deallocate(guard)
130 return
131 end if
132 m = max(2*n, safety * int(ceiling(span / min_gap)))
133 if (allocated(guard)) deallocate(guard)
134 allocate(guard(m))
135 scale_out = dble(m) / span
136 m_out = m
137
138 ii = 1
139 do k = 1, m
140 bk = xmin + dble(k-1) * span / dble(m)
141 do while (ii < n .and. nodes(ii) < bk)
142 ii = ii + 1
143 end do
144 guard(k) = ii
145 end do
146 end subroutine build_guard_for_axis
147
148 !> Defensive consistency check for one table after init. Aborts with a
149 !> diagnostic message if any silent-failure invariant is violated.
150 subroutine eos_validate_table(tc, name)
151 type(eos_table_container), intent(in) :: tc
152 character(len=*), intent(in) :: name
153 integer :: i
154
155 if (.not. allocated(tc%table)) then
156 if (mype == 0) write(*,'(A,A)') " EOS validate FAIL: table not allocated for ", trim(name)
157 call mpistop("eos_validate_table: missing table data")
158 end if
159 if (size(tc%table, 1) /= tc%dim1 .or. size(tc%table, 2) /= tc%dim2) then
160 if (mype == 0) write(*,'(A,A)') " EOS validate FAIL: dim mismatch for ", trim(name)
161 call mpistop("eos_validate_table: dim mismatch")
162 end if
163 !> Value finiteness: a NaN (e.g. from dlog10 of a non-positive stored
164 !> value, or a truncated/corrupt file read) otherwise passes the
165 !> structural checks and only surfaces as a runtime FPE deep in a lookup.
166 !> The x /= x idiom is NaN-true without needing the ieee_arithmetic module.
167 if (any(tc%table /= tc%table)) then
168 if (mype == 0) write(*,'(A,A)') " EOS validate FAIL: NaN in table ", trim(name)
169 call mpistop("eos_validate_table: NaN in table data")
170 end if
171 if (.not. tc%is_uniform) then
172 if (.not. allocated(tc%var1_nodes) .or. .not. allocated(tc%var2_nodes)) then
173 if (mype == 0) write(*,'(A,A)') &
174 " EOS validate FAIL: adaptive table missing node arrays for ", trim(name)
175 call mpistop("eos_validate_table: adaptive table without nodes")
176 end if
177 if (size(tc%var1_nodes) /= tc%dim1 .or. size(tc%var2_nodes) /= tc%dim2) then
178 if (mype == 0) write(*,'(A,A)') &
179 " EOS validate FAIL: node array length mismatch for ", trim(name)
180 call mpistop("eos_validate_table: node length mismatch")
181 end if
182 do i = 1, tc%dim1 - 1
183 if (tc%var1_nodes(i+1) <= tc%var1_nodes(i)) then
184 if (mype == 0) write(*,'(A,A,A,I0)') &
185 " EOS validate FAIL: var1_nodes not strictly monotonic for ", &
186 trim(name), " at index ", i
187 call mpistop("eos_validate_table: non-monotonic nodes")
188 end if
189 end do
190 do i = 1, tc%dim2 - 1
191 if (tc%var2_nodes(i+1) <= tc%var2_nodes(i)) then
192 if (mype == 0) write(*,'(A,A,A,I0)') &
193 " EOS validate FAIL: var2_nodes not strictly monotonic for ", &
194 trim(name), " at index ", i
195 call mpistop("eos_validate_table: non-monotonic nodes")
196 end if
197 end do
198 if (.not. allocated(tc%guard_1) .or. .not. allocated(tc%guard_2) .or. &
199 tc%guard_M_1 <= 0 .or. tc%guard_M_2 <= 0 .or. &
200 tc%guard_scale_1 <= 0.0d0 .or. tc%guard_scale_2 <= 0.0d0) then
201 if (mype == 0) write(*,'(A,A,A)') &
202 " EOS validate WARN: adaptive table without guard for ", trim(name), &
203 " - lookup will fall back to bsearch"
204 end if
205 end if
206 if (mype == 0) write(*,'(A,A)') " EOS validate OK: ", trim(name)
207 end subroutine eos_validate_table
208
210 !> Precompute constants for bypassing table lookups when gas is fully ionised.
211 !> Physics: above T ~ 50 kK, H is >99.99% ionised and He is >90% ionised.
212 !> The EoS reduces to ideal gas with constant mu, Gamma_1 = 5/3, ne/nH = 1+2*A_He.
213 !> We check eint/rho > threshold (1 division + 1 comparison) to skip all table lookups.
214 double precision :: chi_h, chi_hei, chi_heii
215 double precision :: t_thresh, eint_nh_thresh, p_nh_thresh
216
217 !> Ionisation energies in CGS (erg)
218 chi_h = 13.6d0 * 1.602d-12
219 chi_hei = 24.6d0 * 1.602d-12
220 chi_heii = 54.4d0 * 1.602d-12
221
222 !> Fully-ionised particle counts and electron fraction
223 eos%n_per_nH_FI = 2.0d0 + 3.0d0 * eos%He_abundance
224 eos%neOnH_FI = 1.0d0 + 2.0d0 * eos%He_abundance
225
226 !> Total ionisation energy per hydrogen atom (CGS), converted to code units
227 !> eion_per_nH [code] = eion_per_nH [erg] / (unit_pressure / unit_numberdensity)
228 eos%eion_per_nH = (chi_h + eos%He_abundance * (chi_hei + chi_heii)) &
230 !> No-energy mode (.not. ionE): the ionisation energy is NOT folded into
231 !> eint, so it must not appear anywhere in the thermodynamics. Zeroing it
232 !> here collapses both the FI threshold below and every FI-zone temperature
233 !> path (update_eos_LTE, get_temperature_from_eint[_fast]_LTE, the analytic
234 !> FI bypasses) to their correct no-energy form from one place -- the
235 !> from-eint getters subtract eion unconditionally, so this is what keeps
236 !> them consistent with update_eos_LTE's ionE-gated branch.
237 if (.not. eos%ionE) eos%eion_per_nH = 0.0d0
238
239 !> Bypass threshold: eint/rho value corresponding to T = 200,000 K fully ionised.
240 !> Raised from 50 kK to 200 kK so that blended LTE tables (which converge to
241 !> FI by 200 kK) match the bypass formula exactly at the threshold -- no
242 !> discontinuity in ne/nH, T, Gamma_1, or p when cells cross the boundary.
243 !> eint/nH = 1.5 * n_per_nH * kB * T + eion_per_nH (all in code units)
244 !> eint/rho = eint/nH / nH2rhoFactor
245 t_thresh = 200000.0d0 / unit_temperature
246 eint_nh_thresh = 1.5d0 * eos%n_per_nH_FI * t_thresh + eos%eion_per_nH
247 eos%eint_rho_FI_threshold = eint_nh_thresh / eos%nH2rhoFactor
248
249 !> Pressure-based threshold for primitive-variable checks:
250 !> For FI gas, T = p / (rho * Rfactor_FI), so p/rho > Rfactor_FI * T_thresh
251 !> where Rfactor_FI = n_per_nH_FI / (1 + 4*A_He) in code units
252 eos%p_rho_FI_threshold = eos%n_per_nH_FI &
253 / (1.0d0 + 4.0d0*eos%He_abundance) * t_thresh
254
255 if (mype == 0) then
256 write(*,'(a,es12.4)') ' FI bypass: eion_per_nH (code) = ', eos%eion_per_nH
257 write(*,'(a,es12.4)') ' FI bypass: eint/rho threshold = ', eos%eint_rho_FI_threshold
258 write(*,'(a,f8.4)') ' FI bypass: n_per_nH_FI = ', eos%n_per_nH_FI
259 write(*,'(a,f8.4)') ' FI bypass: neOnH_FI = ', eos%neOnH_FI
260 end if
261
262 end subroutine precompute_fi_bypass_constants
263
264 !> Read one named table file into its eos% container. The filename encodes
265 !> the composition (H or HHe) and whether ionisation energy is included.
266 subroutine load_tables_lte(fieldname)
267 character(len=*), intent(in) :: fieldname
268 character(len=std_len) :: subname, tablename, filename, filepath
269 subname = "H" !> Always consider at least hydrogen
270
271 if (eos%He_abundance > 0.0d0) then
272 write(subname,"(A,A)") trim(subname), "He"
273 endif
274
275 if (eos%ionE) then !> Consider the ionisation energy?
276 write(subname,"(A,A)") trim(subname), "_IonE"
277 else
278 write(subname,"(A,A)") trim(subname), "_NoIonE"
279 endif
280
281 write(tablename, "(A,A,A)") trim(eos_table_prefix), fieldname, "_"
282 write(filename,"(A,A,A)") trim(tablename), trim(subname), '.bin'
283
284 if (mype==0) then
285 print*, "Reading EoS tables from: ", trim(filename)
286 endif
287
288 write(filepath,"(A,A)") trim(eos%table_location), trim(filename)
289
290 select case (fieldname)
291 case("T")
292 eos%T%filename = filename
293 call read_eos_from_file(trim(filepath), eos%T, .true.)
294 case("neOnH")
295 eos%neOnH%filename = filename
296 ! Legacy tables (method='tables'/'analytic') store log10(neOnH); the
297 ! entropy method's tables store raw (linear) neOnH because they go
298 ! through bicubic Hermite (which interpolates the value directly,
299 ! no log10 transform needed and would break stored derivatives).
300 call read_eos_from_file(trim(filepath), eos%neOnH, &
301 eos%method_id /= eos_entropy)
302 case("p2eint")
303 eos%p2eint%filename = filename
304 call read_eos_from_file(trim(filepath), eos%p2eint, .false.)
305 case("gamma1")
306 eos%gamma1%filename = filename
307 call read_eos_from_file(trim(filepath), eos%gamma1, .false.)
308 case("eint_from_T")
309 eos%eint_from_T%filename = filename
310 call read_eos_from_file(trim(filepath), eos%eint_from_T, .false.)
311 !> Entropy-method tables. Unit shifts MUST happen in eos_finalise (where
312 !> unit_* are set), not here.
313 case("neOnH_x")
314 eos%neOnH_x%filename = filename
315 call read_eos_from_file(trim(filepath), eos%neOnH_x, .false.)
316 case("neOnH_y")
317 eos%neOnH_y%filename = filename
318 call read_eos_from_file(trim(filepath), eos%neOnH_y, .false.)
319 case("neOnH_xy")
320 eos%neOnH_xy%filename = filename
321 call read_eos_from_file(trim(filepath), eos%neOnH_xy, .false.)
322 case("eintP")
323 eos%eintP%filename = filename
324 call read_eos_from_file(trim(filepath), eos%eintP, .false.)
325 case("eintP_x")
326 eos%eintP_x%filename = filename
327 call read_eos_from_file(trim(filepath), eos%eintP_x, .false.)
328 case("eintP_y")
329 eos%eintP_y%filename = filename
330 call read_eos_from_file(trim(filepath), eos%eintP_y, .false.)
331 case("eintP_xy")
332 eos%eintP_xy%filename = filename
333 call read_eos_from_file(trim(filepath), eos%eintP_xy, .false.)
334 case("g1p")
335 eos%g1p%filename = filename
336 call read_eos_from_file(trim(filepath), eos%g1p, .false.)
337 case("g1p_x")
338 eos%g1p_x%filename = filename
339 call read_eos_from_file(trim(filepath), eos%g1p_x, .false.)
340 case("g1p_y")
341 eos%g1p_y%filename = filename
342 call read_eos_from_file(trim(filepath), eos%g1p_y, .false.)
343 case("g1p_xy")
344 eos%g1p_xy%filename = filename
345 call read_eos_from_file(trim(filepath), eos%g1p_xy, .false.)
346 case("eintT")
347 eos%eintT%filename = filename
348 call read_eos_from_file(trim(filepath), eos%eintT, .false.)
349 case("eintT_x")
350 eos%eintT_x%filename = filename
351 call read_eos_from_file(trim(filepath), eos%eintT_x, .false.)
352 case("eintT_y")
353 eos%eintT_y%filename = filename
354 call read_eos_from_file(trim(filepath), eos%eintT_y, .false.)
355 case("eintT_xy")
356 eos%eintT_xy%filename = filename
357 call read_eos_from_file(trim(filepath), eos%eintT_xy, .false.)
358 ! 'yT' tables dropped -- zero runtime references.
359 case("Tfwd")
360 eos%Tfwd%filename = filename
361 call read_eos_from_file(trim(filepath), eos%Tfwd, .false.)
362 case("Tfwd_x")
363 eos%Tfwd_x%filename = filename
364 call read_eos_from_file(trim(filepath), eos%Tfwd_x, .false.)
365 case("Tfwd_y")
366 eos%Tfwd_y%filename = filename
367 call read_eos_from_file(trim(filepath), eos%Tfwd_y, .false.)
368 case("Tfwd_xy")
369 eos%Tfwd_xy%filename = filename
370 call read_eos_from_file(trim(filepath), eos%Tfwd_xy, .false.)
371 case("pfwd")
372 eos%pfwd%filename = filename
373 call read_eos_from_file(trim(filepath), eos%pfwd, .false.)
374 case("pfwd_x")
375 eos%pfwd_x%filename = filename
376 call read_eos_from_file(trim(filepath), eos%pfwd_x, .false.)
377 case("pfwd_y")
378 eos%pfwd_y%filename = filename
379 call read_eos_from_file(trim(filepath), eos%pfwd_y, .false.)
380 case("pfwd_xy")
381 eos%pfwd_xy%filename = filename
382 call read_eos_from_file(trim(filepath), eos%pfwd_xy, .false.)
383 case default
384 call mpistop("eos table name "//trim(fieldname)//" not recognised in load_tables_LTE")
385 end select
386
387 end subroutine load_tables_lte
388
389 !> Attempt to load pre-computed table from binary file.
390 !> If the file does not exist, silently skip and the table will be built at runtime.
391 subroutine try_load_tables_lte(fieldname)
392 character(len=*), intent(in) :: fieldname
393 character(len=std_len) :: subname, tablename, filename, filepath
394 logical :: file_exists
395
396 subname = "H"
397 if (eos%He_abundance > 0.0d0) then
398 write(subname,"(A,A)") trim(subname), "He"
399 endif
400 if (eos%ionE) then
401 write(subname,"(A,A)") trim(subname), "_IonE"
402 else
403 write(subname,"(A,A)") trim(subname), "_NoIonE"
404 endif
405 write(tablename, "(A,A,A)") trim(eos_table_prefix), fieldname, "_"
406 write(filename,"(A,A,A)") trim(tablename), trim(subname), '.bin'
407 write(filepath,"(A,A)") trim(eos%table_location), trim(filename)
408
409 inquire(file=trim(filepath), exist=file_exists)
410 if (file_exists) then
411 call load_tables_lte(fieldname)
412 else
413 if (mype == 0) write(*,*) &
414 trim(fieldname)//' table not found, will build at runtime: '//trim(filename)
415 endif
416 end subroutine try_load_tables_lte
417
418 !> Read an EoS table from a binary file written by generate_lte_tables.py.
419 !>
420 !> File format (uniform -- legacy):
421 !> [2 x int32] : (dim1, dim2) = (dimy, dimx) in Fortran column-major
422 !> [4 x float64] : (var1_min, var1_max, var2_min, var2_max)
423 !> [dim1*dim2 x float64] : table values
424 !>
425 !> File format (adaptive -- extension): same prefix, then optional trailer
426 !> [1 x int32] : trailer_flag (= 1)
427 !> [dim1 x float64] : x_nodes (axis-1 node positions, log10 space)
428 !> [dim2 x float64] : y_nodes (axis-2 node positions, log10 space)
429 !>
430 !> The trailer is detected via end-of-file on the trailer_flag read:
431 !> uniform tables (no trailer) leave is_uniform = .true. and produce
432 !> ios /= 0 on the read; adaptive tables set is_uniform = .false. and
433 !> populate x_nodes, y_nodes.
434 subroutine read_eos_from_file(filename, tc, logtable)
435 character(len=*), intent(in) :: filename
436 type(eos_table_container), intent(inout) :: tc
437 logical, intent(in) :: logtable
438
439 integer :: ios, trailer_flag, lun
440
441 !> The mandatory header/payload reads are iostat-guarded so a missing,
442 !> truncated, or wrong-format file fails with a clear mpistop rather than
443 !> an opaque "Fortran runtime error: end of file". (Only the OPTIONAL
444 !> adaptive trailer below is allowed to hit EOF cleanly.)
445 open(newunit=lun, file=filename, access='stream', form='unformatted', &
446 action='read', iostat=ios)
447 if (ios /= 0) call mpistop( &
448 "read_eos_from_file: cannot open EoS table "//trim(filename))
449
450 !> First read the header information, size from python file, and allocate
451 read(lun, iostat=ios) tc%dim1, tc%dim2
452 if (ios /= 0) call mpistop( &
453 "read_eos_from_file: cannot read dims from "//trim(filename))
454
455 !> Then read the header information, bounds from python file
456 read(lun, iostat=ios) tc%var1_min, tc%var1_max, tc%var2_min, tc%var2_max
457 if (ios /= 0) call mpistop( &
458 "read_eos_from_file: cannot read axis bounds from "//trim(filename))
459
460 if (allocated(tc%table)) deallocate(tc%table)
461 allocate(tc%table(tc%dim1, tc%dim2))
462
463 !> Then read the 2D cube from the python file
464 read(lun, iostat=ios) tc%table
465 if (ios /= 0) call mpistop( &
466 "read_eos_from_file: cannot read table payload from "//trim(filename))
467
468 if (logtable) then
469 tc%table = dlog10(tc%table) !> Store tables in log10 space for interpolation
470 endif
471
472 !> Optional adaptive-grid trailer. Uniform (legacy) tables hit EOF here
473 !> and the iostat-guarded read keeps is_uniform = .true.
474 tc%is_uniform = .true.
475 if (allocated(tc%var1_nodes)) deallocate(tc%var1_nodes)
476 if (allocated(tc%var2_nodes)) deallocate(tc%var2_nodes)
477
478 read(lun, iostat=ios) trailer_flag
479 if (ios == 0) then
480 if (trailer_flag == 1) then
481 tc%is_uniform = .false.
482 allocate(tc%var1_nodes(tc%dim1))
483 allocate(tc%var2_nodes(tc%dim2))
484 read(lun) tc%var1_nodes
485 read(lun) tc%var2_nodes
486 if (mype == 0) then
487 print*, trim(filename), " (adaptive grid trailer detected)"
488 endif
489 else
490 if (mype == 0) then
491 print*, "WARNING: unrecognised trailer flag ", trailer_flag, &
492 " in ", trim(filename), " - treating as uniform"
493 endif
494 endif
495 end if
496
497 close(lun)
498
499 end subroutine read_eos_from_file
500
501end module mod_eos_lte_tables
502!> 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.
integer, parameter, public eos_entropy
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).
Shared LTE lookup-table infrastructure (build/IO; not on the hot path).
subroutine, public try_load_tables_lte(fieldname)
Attempt to load pre-computed table from binary file. If the file does not exist, silently skip and th...
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