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
209 !> Precompute constants for bypassing table lookups when gas is fully ionised.
210 !> Physics: above T ~ 50 kK, H is >99.99% ionised and He is >90% ionised.
211 !> The EoS reduces to ideal gas with constant mu, Gamma_1 = 5/3, ne/nH = 1+2*A_He.
212 !> We check eint/rho > threshold (1 division + 1 comparison) to skip all table lookups.
213 !> With chi_H_only (the analytic route, erg) helium stays neutral and only hydrogen ionises
214 subroutine precompute_fi_bypass_constants(chi_H_only)
215 double precision, intent(in), optional :: chi_h_only
216 double precision :: chi_h, chi_hei, chi_heii
217 double precision :: t_thresh, eint_nh_thresh, p_nh_thresh
218
219 !> Ionisation energies in CGS (erg)
220 chi_h = 13.6d0 * 1.602d-12
221 chi_hei = 24.6d0 * 1.602d-12
222 chi_heii = 54.4d0 * 1.602d-12
223
224 !> fully ionised particle count, electron fraction and ionisation energy per hydrogen
225 !> atom, eion_per_nH [code] = eion_per_nH [erg] * unit_numberdensity / unit_pressure
226 if (present(chi_h_only)) then
227 eos%n_per_nH_FI = 2.0d0 + eos%He_abundance
228 eos%neOnH_FI = 1.0d0
229 eos%eion_per_nH = chi_h_only * unit_numberdensity / unit_pressure
230 else
231 eos%n_per_nH_FI = 2.0d0 + 3.0d0 * eos%He_abundance
232 eos%neOnH_FI = 1.0d0 + 2.0d0 * eos%He_abundance
233 eos%eion_per_nH = (chi_h + eos%He_abundance * (chi_hei + chi_heii)) &
235 end if
236 !> No-energy mode (.not. ionE): the ionisation energy is NOT folded into
237 !> eint, so it must not appear anywhere in the thermodynamics. Zeroing it
238 !> here collapses both the FI threshold below and every FI-zone temperature
239 !> path (update_eos_LTE, get_temperature_from_eint[_fast]_LTE, the analytic
240 !> FI bypasses) to their correct no-energy form from one place -- the
241 !> from-eint getters subtract eion unconditionally, so this is what keeps
242 !> them consistent with update_eos_LTE's ionE-gated branch.
243 if (.not. eos%ionE) eos%eion_per_nH = 0.0d0
244
245 !> Bypass threshold: eint/rho value corresponding to T = 200,000 K fully ionised.
246 !> Raised from 50 kK to 200 kK so that blended LTE tables (which converge to
247 !> FI by 200 kK) match the bypass formula exactly at the threshold -- no
248 !> discontinuity in ne/nH, T, Gamma_1, or p when cells cross the boundary.
249 !> eint/nH = 1.5 * n_per_nH * kB * T + eion_per_nH (all in code units)
250 !> eint/rho = eint/nH / nH2rhoFactor
251 t_thresh = 200000.0d0 / unit_temperature
252 eint_nh_thresh = 1.5d0 * eos%n_per_nH_FI * t_thresh + eos%eion_per_nH
253 eos%eint_rho_FI_threshold = eint_nh_thresh / eos%nH2rhoFactor
254
255 !> Pressure-based threshold for primitive-variable checks:
256 !> For FI gas, T = p / (rho * Rfactor_FI), so p/rho > Rfactor_FI * T_thresh
257 !> where Rfactor_FI = n_per_nH_FI / (1 + 4*A_He) in code units
258 eos%p_rho_FI_threshold = eos%n_per_nH_FI &
259 / (1.0d0 + 4.0d0*eos%He_abundance) * t_thresh
260
261 if (mype == 0) then
262 write(*,'(a,es12.4)') ' FI bypass: eion_per_nH (code) = ', eos%eion_per_nH
263 write(*,'(a,es12.4)') ' FI bypass: eint/rho threshold = ', eos%eint_rho_FI_threshold
264 write(*,'(a,f8.4)') ' FI bypass: n_per_nH_FI = ', eos%n_per_nH_FI
265 write(*,'(a,f8.4)') ' FI bypass: neOnH_FI = ', eos%neOnH_FI
266 end if
267
268 end subroutine precompute_fi_bypass_constants
269
270 !> Read one named table file into its eos% container. The filename encodes
271 !> the composition (H or HHe) and whether ionisation energy is included.
272 subroutine load_tables_lte(fieldname)
273 character(len=*), intent(in) :: fieldname
274 character(len=std_len) :: subname, tablename, filename, filepath
275 subname = "H" !> Always consider at least hydrogen
276
277 if (eos%He_abundance > 0.0d0) then
278 write(subname,"(A,A)") trim(subname), "He"
279 endif
280
281 if (eos%ionE) then !> Consider the ionisation energy?
282 write(subname,"(A,A)") trim(subname), "_IonE"
283 else
284 write(subname,"(A,A)") trim(subname), "_NoIonE"
285 endif
286
287 write(tablename, "(A,A,A)") trim(eos_table_prefix), fieldname, "_"
288 write(filename,"(A,A,A)") trim(tablename), trim(subname), '.bin'
289
290 if (mype==0) then
291 print*, "Reading EoS tables from: ", trim(filename)
292 endif
293
294 write(filepath,"(A,A)") trim(eos%table_location), trim(filename)
295
296 select case (fieldname)
297 case("T")
298 eos%T%filename = filename
299 call read_eos_from_file(trim(filepath), eos%T, .true.)
300 case("neOnH")
301 eos%neOnH%filename = filename
302 ! Legacy tables (method='tables'/'analytic') store log10(neOnH); the
303 ! entropy method's tables store raw (linear) neOnH because they go
304 ! through bicubic Hermite (which interpolates the value directly,
305 ! no log10 transform needed and would break stored derivatives).
306 call read_eos_from_file(trim(filepath), eos%neOnH, &
307 eos%method_id /= eos_entropy)
308 case("p2eint")
309 eos%p2eint%filename = filename
310 call read_eos_from_file(trim(filepath), eos%p2eint, .false.)
311 case("gamma1")
312 eos%gamma1%filename = filename
313 call read_eos_from_file(trim(filepath), eos%gamma1, .false.)
314 case("eint_from_T")
315 eos%eint_from_T%filename = filename
316 call read_eos_from_file(trim(filepath), eos%eint_from_T, .false.)
317 !> Entropy-method tables. Unit shifts MUST happen in eos_finalise (where
318 !> unit_* are set), not here.
319 case("neOnH_x")
320 eos%neOnH_x%filename = filename
321 call read_eos_from_file(trim(filepath), eos%neOnH_x, .false.)
322 case("neOnH_y")
323 eos%neOnH_y%filename = filename
324 call read_eos_from_file(trim(filepath), eos%neOnH_y, .false.)
325 case("neOnH_xy")
326 eos%neOnH_xy%filename = filename
327 call read_eos_from_file(trim(filepath), eos%neOnH_xy, .false.)
328 case("eintP")
329 eos%eintP%filename = filename
330 call read_eos_from_file(trim(filepath), eos%eintP, .false.)
331 case("eintP_x")
332 eos%eintP_x%filename = filename
333 call read_eos_from_file(trim(filepath), eos%eintP_x, .false.)
334 case("eintP_y")
335 eos%eintP_y%filename = filename
336 call read_eos_from_file(trim(filepath), eos%eintP_y, .false.)
337 case("eintP_xy")
338 eos%eintP_xy%filename = filename
339 call read_eos_from_file(trim(filepath), eos%eintP_xy, .false.)
340 case("g1p")
341 eos%g1p%filename = filename
342 call read_eos_from_file(trim(filepath), eos%g1p, .false.)
343 case("g1p_x")
344 eos%g1p_x%filename = filename
345 call read_eos_from_file(trim(filepath), eos%g1p_x, .false.)
346 case("g1p_y")
347 eos%g1p_y%filename = filename
348 call read_eos_from_file(trim(filepath), eos%g1p_y, .false.)
349 case("g1p_xy")
350 eos%g1p_xy%filename = filename
351 call read_eos_from_file(trim(filepath), eos%g1p_xy, .false.)
352 case("eintT")
353 eos%eintT%filename = filename
354 call read_eos_from_file(trim(filepath), eos%eintT, .false.)
355 case("eintT_x")
356 eos%eintT_x%filename = filename
357 call read_eos_from_file(trim(filepath), eos%eintT_x, .false.)
358 case("eintT_y")
359 eos%eintT_y%filename = filename
360 call read_eos_from_file(trim(filepath), eos%eintT_y, .false.)
361 case("eintT_xy")
362 eos%eintT_xy%filename = filename
363 call read_eos_from_file(trim(filepath), eos%eintT_xy, .false.)
364 ! 'yT' tables dropped -- zero runtime references.
365 case("Tfwd")
366 eos%Tfwd%filename = filename
367 call read_eos_from_file(trim(filepath), eos%Tfwd, .false.)
368 case("Tfwd_x")
369 eos%Tfwd_x%filename = filename
370 call read_eos_from_file(trim(filepath), eos%Tfwd_x, .false.)
371 case("Tfwd_y")
372 eos%Tfwd_y%filename = filename
373 call read_eos_from_file(trim(filepath), eos%Tfwd_y, .false.)
374 case("Tfwd_xy")
375 eos%Tfwd_xy%filename = filename
376 call read_eos_from_file(trim(filepath), eos%Tfwd_xy, .false.)
377 case("pfwd")
378 eos%pfwd%filename = filename
379 call read_eos_from_file(trim(filepath), eos%pfwd, .false.)
380 case("pfwd_x")
381 eos%pfwd_x%filename = filename
382 call read_eos_from_file(trim(filepath), eos%pfwd_x, .false.)
383 case("pfwd_y")
384 eos%pfwd_y%filename = filename
385 call read_eos_from_file(trim(filepath), eos%pfwd_y, .false.)
386 case("pfwd_xy")
387 eos%pfwd_xy%filename = filename
388 call read_eos_from_file(trim(filepath), eos%pfwd_xy, .false.)
389 case default
390 call mpistop("eos table name "//trim(fieldname)//" not recognised in load_tables_LTE")
391 end select
392
393 end subroutine load_tables_lte
394
395 !> Attempt to load pre-computed table from binary file.
396 !> If the file does not exist, silently skip and the table will be built at runtime.
397 subroutine try_load_tables_lte(fieldname)
398 character(len=*), intent(in) :: fieldname
399 character(len=std_len) :: subname, tablename, filename, filepath
400 logical :: file_exists
401
402 subname = "H"
403 if (eos%He_abundance > 0.0d0) then
404 write(subname,"(A,A)") trim(subname), "He"
405 endif
406 if (eos%ionE) then
407 write(subname,"(A,A)") trim(subname), "_IonE"
408 else
409 write(subname,"(A,A)") trim(subname), "_NoIonE"
410 endif
411 write(tablename, "(A,A,A)") trim(eos_table_prefix), fieldname, "_"
412 write(filename,"(A,A,A)") trim(tablename), trim(subname), '.bin'
413 write(filepath,"(A,A)") trim(eos%table_location), trim(filename)
414
415 inquire(file=trim(filepath), exist=file_exists)
416 if (file_exists) then
417 call load_tables_lte(fieldname)
418 else
419 if (mype == 0) write(*,*) &
420 trim(fieldname)//' table not found, will build at runtime: '//trim(filename)
421 endif
422 end subroutine try_load_tables_lte
423
424 !> Read an EoS table from a binary file written by generate_state_tables.py.
425 !>
426 !> File format (uniform -- legacy):
427 !> [2 x int32] : (dim1, dim2) = (dimy, dimx) in Fortran column-major
428 !> [4 x float64] : (var1_min, var1_max, var2_min, var2_max)
429 !> [dim1*dim2 x float64] : table values
430 !>
431 !> File format (adaptive -- extension): same prefix, then optional trailer
432 !> [1 x int32] : trailer_flag (= 1)
433 !> [dim1 x float64] : x_nodes (axis-1 node positions, log10 space)
434 !> [dim2 x float64] : y_nodes (axis-2 node positions, log10 space)
435 !>
436 !> The trailer is detected via end-of-file on the trailer_flag read:
437 !> uniform tables (no trailer) leave is_uniform = .true. and produce
438 !> ios /= 0 on the read; adaptive tables set is_uniform = .false. and
439 !> populate x_nodes, y_nodes.
440 subroutine read_eos_from_file(filename, tc, logtable)
441 character(len=*), intent(in) :: filename
442 type(eos_table_container), intent(inout) :: tc
443 logical, intent(in) :: logtable
444
445 integer :: ios, trailer_flag, lun
446
447 !> The mandatory header/payload reads are iostat-guarded so a missing,
448 !> truncated, or wrong-format file fails with a clear mpistop rather than
449 !> an opaque "Fortran runtime error: end of file". (Only the OPTIONAL
450 !> adaptive trailer below is allowed to hit EOF cleanly.)
451 open(newunit=lun, file=filename, access='stream', form='unformatted', &
452 action='read', iostat=ios)
453 if (ios /= 0) call mpistop( &
454 "read_eos_from_file: cannot open EoS table "//trim(filename))
455
456 !> First read the header information, size from python file, and allocate
457 read(lun, iostat=ios) tc%dim1, tc%dim2
458 if (ios /= 0) call mpistop( &
459 "read_eos_from_file: cannot read dims from "//trim(filename))
460
461 !> Then read the header information, bounds from python file
462 read(lun, iostat=ios) tc%var1_min, tc%var1_max, tc%var2_min, tc%var2_max
463 if (ios /= 0) call mpistop( &
464 "read_eos_from_file: cannot read axis bounds from "//trim(filename))
465
466 if (allocated(tc%table)) deallocate(tc%table)
467 allocate(tc%table(tc%dim1, tc%dim2))
468
469 !> Then read the 2D cube from the python file
470 read(lun, iostat=ios) tc%table
471 if (ios /= 0) call mpistop( &
472 "read_eos_from_file: cannot read table payload from "//trim(filename))
473
474 if (logtable) then
475 tc%table = dlog10(tc%table) !> Store tables in log10 space for interpolation
476 endif
477
478 !> Optional adaptive-grid trailer. Uniform (legacy) tables hit EOF here
479 !> and the iostat-guarded read keeps is_uniform = .true.
480 tc%is_uniform = .true.
481 if (allocated(tc%var1_nodes)) deallocate(tc%var1_nodes)
482 if (allocated(tc%var2_nodes)) deallocate(tc%var2_nodes)
483
484 read(lun, iostat=ios) trailer_flag
485 if (ios == 0) then
486 if (trailer_flag == 1) then
487 tc%is_uniform = .false.
488 allocate(tc%var1_nodes(tc%dim1))
489 allocate(tc%var2_nodes(tc%dim2))
490 read(lun) tc%var1_nodes
491 read(lun) tc%var2_nodes
492 if (mype == 0) then
493 print*, trim(filename), " (adaptive grid trailer detected)"
494 endif
495 else
496 if (mype == 0) then
497 print*, "WARNING: unrecognised trailer flag ", trailer_flag, &
498 " in ", trim(filename), " - treating as uniform"
499 endif
500 endif
501 end if
502
503 close(lun)
504
505 end subroutine read_eos_from_file
506
507end module mod_eos_lte_tables
508!> 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 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