MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_eos_LTE.t
Go to the documentation of this file.
1!=============================================================================
2!> LTE (Saha-table) EoS kernels and finalise for the eos% family.
3!>
4!> Carved out of mod_eos.t (the per-type split). Owns everything LTE: the
5!> per-substep update_eos_LTE (caches Te_/Ne_), the cached-Te/Rfactor getters,
6!> the scalar table kernels (T, y, p2eint, gamma1, p/nH, eint, bisections), the
7!> per-method table finalisers, and eos_finalise_LTE (the LTE arm of the
8!> eos_finalise dispatcher). Depends only on the EoS leaf modules + shared
9!> accessors, never on mod_eos -> no circular use. mod_eos re-exports the public
10!> kernels so existing `use mod_eos` callers are unaffected.
11!=============================================================================
21 use mod_timing
22 use mod_comm_lib, only: mpistop
23
24 implicit none
25 private
26
27 !> update_eos_LTE, get_Te_LTE, get_temperature_from_eint_LTE and
28 !> Rfactor_from_LTE are PRIVATE: all bound to eos% pointers inside
29 !> eos_finalise_LTE, so no external module names them.
30 !> get_temperature_from_eint_fast_LTE stays public -- the seam binds it to
31 !> tc_fl%get_temperature_from_eint (fast TC path).
33 !> Scalar table kernels (re-exported by mod_eos for the hd/mhd/ffhd seams).
34 !> T_and_y_from_nH_eint is internal-only (entropy update path).
38 !> LTE arms of the eos_init / eos_finalise dispatchers
40
41contains
42
43 !> LTE arm of eos_init (before units are known): dispatch on eos_method to
44 !> the chosen method's loader, each owned by its method module (analytic loads
45 !> nothing). The LTE temperature-from-pressure getter is a later pass, so
46 !> unlike FI/PI it is left unset here.
47 subroutine eos_init_lte()
48 select case (eos%method_id)
49 case (eos_analytic); call load_analytic_lte()
50 case (eos_entropy); call load_entropy_lte()
51 case default; call load_state_lte() ! EOS_STATE
52 end select
53 end subroutine eos_init_lte
54
55 !> LTE arm of eos_finalise: wire the LTE runtime pointer targets, then
56 !> dispatch on eos_method to the per-method finaliser, each owned by its
57 !> method module (analytic->mod_eos_LTE_saha, entropy->mod_eos_LTE_entropy,
58 !> tables->mod_eos_LTE_tables).
59 subroutine eos_finalise_lte()
60 eos%update_eos => update_eos_lte
61 ! eos%get_thermal_pressure is set by (m)hd_link_eos
62 eos%get_temperature_from_eint => get_temperature_from_eint_lte
63 eos%get_Te => get_te_lte
64 !> R-factor: physics-independent (cached-Ne lookup), bound here rather
65 !> than in the hd/mhd/ffhd link arms so the target stays private.
66 eos%get_Rfactor => rfactor_from_lte
67 select case (eos%method_id)
70 case default; call finalise_state_lte() ! EOS_STATE
71 end select
72 end subroutine eos_finalise_lte
73
74
75 subroutine update_eos_lte(ixI^L, ixO^L, w, x)
76 use mod_physics
77 !> This routine is called before each RK substep
78 integer, intent(in) :: ixi^l,ixo^l
79 double precision, intent(in) :: x(ixi^s,1:ndim)
80 double precision, intent(inout) :: w(ixi^s,1:nw)
81
82 double precision :: wlocal(ixi^s,1:nw)
83 double precision :: deltaefactor(ixi^s)
84 double precision :: prev_y(ixi^s), new_y(ixi^s)
85 double precision :: pth(ixi^s)
86 double precision :: nh_in(ixi^s), nh(ixi^s), eint_in(ixi^s)
87 double precision :: rfactor_fi
88 double precision :: yy, eint_nh_floor
89 double precision :: time0
90 integer :: ix^d
91
92 timeeos0 = mpi_wtime() !> For monitoring cost of eos module
93
94 wlocal(ixi^s,1:nw)=w(ixi^s,1:nw)
95 call phys_e_to_ei(ixi^l,ixo^l,wlocal,x) !> wlocal now contains internal energy, NOT total energy
96
97 pth(ixo^s) = eos%gamma_minus_1 * wlocal(ixo^s,iw_e) !> pressure from internal energy only - SHOULD ONLY BE USED WHEN IonE IS UNIMPORTANT
98
99 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
100 ! Enforce internal energy floor for EoS lookup only.
101 ! Prevents NaN from dlog10(eint<0) after strong rarefactions where
102 ! kinetic energy can numerically exceed total energy.
103 ! The conserved energy w(iw_e) is NOT modified: the physical fluxes
104 ! based on the small positive pressure will naturally restore the cell.
105 if (eos%method_id == eos_analytic) then
106 ! Analytic: floor to ~100 K equivalent (no tables loaded)
107 ! In code units: eint/nH = T_code / (gamma-1) for neutral gas
108 {do ix^db=ixomin^db,ixomax^db\}
109 eint_nh_floor = nh(ix^d) * eos%inv_gamma_minus_1 * 100.0d0 / unit_temperature
110 wlocal(ix^d,iw_e) = max(wlocal(ix^d,iw_e), eint_nh_floor)
111 {end do\}
112 else
113 nh_in(ixo^s) = dlog10(nh(ixo^s))
114 {do ix^db=ixomin^db,ixomax^db\}
115 !> eos%T%var2_min holds the forward log(eint/nH) lower bound for
116 !> all table methods (entropy sources it from Tfwd in eos_finalise).
117 eint_nh_floor = nh(ix^d) * 10.0d0**eos%T%var2_min
118 if (wlocal(ix^d,iw_e) < eint_nh_floor) then
119 wlocal(ix^d,iw_e) = eint_nh_floor
120 end if
121 {end do\}
122 eint_in(ixo^s) = dlog10(wlocal(ixo^s,iw_e)) - nh_in(ixo^s)
123 end if
124
125 !> Constant FI Rfactor for the fully-ionised fast path.
126 !> Must NOT use eos%get_Rfactor which reads iw_ne (uninitialised at IC).
127 rfactor_fi = eos%n_per_nH_FI / eos%nH2rhoFactor
128
129 {do ix^db=ixomin^db,ixomax^db\}
130 if (wlocal(ix^d,iw_e) / w(ix^d,iw_rho) > eos%eint_rho_FI_threshold) then
131 !> Fully ionised: analytical formulae, no table lookups
132 new_y(ix^d) = eos%neOnH_FI
133 if (eos%ionE) then
134 !> T = (eint - eion*nH) * (gamma-1) / (Rfactor_FI * rho)
135 w(ix^d,iw_te) = eos%gamma_minus_1 &
136 * (wlocal(ix^d,iw_e) - eos%eion_per_nH * nh(ix^d)) &
137 / (rfactor_fi * w(ix^d,iw_rho))
138 else
139 w(ix^d,iw_te) = pth(ix^d) / (nh(ix^d) * (1.0d0 + eos%He_abundance + new_y(ix^d)))
140 end if
141 else
142 !> Ionisation zone
143 if (eos%method_id == eos_analytic) then
144 !> Analytical Saha: solve quadratic for T and y
145 call saha_t_from_nh_eint(nh(ix^d), &
146 wlocal(ix^d,iw_e) / nh(ix^d), &
147 w(ix^d,iw_te), yy)
148 new_y(ix^d) = yy
149 else
150 !> Table lookups: fused T+y for shared index computation
151 if (.not. eos%ionE) then
152 new_y(ix^d) = y_from_nh_eint(nh_in(ix^d),eint_in(ix^d))
153 w(ix^d,iw_te) = pth(ix^d) / (nh(ix^d) * (1.0d0 + eos%He_abundance + new_y(ix^d)))
154 else if (eos%method_id == eos_entropy) then
155 !> Entropy method dispatches T_and_y_from_nH_eint to the
156 !> biquintic-Hermite forward at any eint; no FI fallback
157 !> needed (and eos%T isn't loaded so var2_max is 0 here).
158 call t_and_y_from_nh_eint(nh_in(ix^d), eint_in(ix^d), &
159 w(ix^d,iw_te), yy)
160 new_y(ix^d) = yy
161 else
162 if (eint_in(ix^d) < eos%T%var2_max) then
163 call t_and_y_from_nh_eint(nh_in(ix^d), eint_in(ix^d), &
164 w(ix^d,iw_te), yy)
165 new_y(ix^d) = yy
166 else
167 !> Above-table fallback with ionisation energy correction
168 new_y(ix^d) = eos%neOnH_FI
169 w(ix^d,iw_te) = eos%gamma_minus_1 &
170 * (wlocal(ix^d,iw_e) - eos%eion_per_nH * nh(ix^d)) &
171 / (rfactor_fi * w(ix^d,iw_rho))
172 end if
173 end if
174 end if
175 end if
176 {end do\}
177
178 w(ixo^s,iw_ne) = new_y(ixo^s) * nh(ixo^s)
179
180 timeeos_update=timeeos_update+(mpi_wtime()-timeeos0)
181
182 end subroutine update_eos_lte
183
184 !> Thermodynamic getters shared verbatim by the hd/mhd seams (eos%/phys
185 !> pointer targets). Only routines free of physics-module symbols live here:
186 !> Rfactor_from_LTE uses the cached Ne_ field; get_gamma1_FI returns
187 !> eos%gamma. The FI/LTE variants that read primitive indices (rho_,p_) or
188 !> the physics RR stay in each seam.
189 !> Rfactor from LTE EOS: R = (1 + He + ne/nH) / nH2rhoFactor
190 !> Uses stored Ne_ wextra field from update_eos_LTE.
191 subroutine rfactor_from_lte(w,x,ixI^L,ixO^L,Rfactor)
193 integer, intent(in) :: ixi^l, ixo^l
194 double precision, intent(in) :: w(ixi^s,1:nw)
195 double precision, intent(in) :: x(ixi^s,1:ndim)
196 double precision, intent(out):: rfactor(ixi^s)
197
198 double precision :: y_nh
199 integer :: ix^d
200
201 {do ix^db=ixomin^db,ixomax^db\}
202 y_nh = w(ix^d, iw_ne) * eos%nH2rhoFactor / w(ix^d, iw_rho)
203 rfactor(ix^d) = (1.0d0 + eos%He_abundance + y_nh) / eos%nH2rhoFactor
204 {end do\}
205
206 end subroutine rfactor_from_lte
207
208 !>#######################################################################
209 !> Temperature getters: cached Te accessors and temperature from the
210 !> energy/pressure variable. The from-eint variants are split by eos_type
211 !> (needed by the STS path: dt sees conserved, set_source sees eint -- see
212 !> mod_thermal_conduction).
213 !>#######################################################################
214
215 subroutine get_te_lte(w,x,ixI^L,ixO^L,T)
217 integer, intent(in) :: ixi^l, ixo^l
218 double precision, intent(in) :: w(ixi^s,1:nw)
219 double precision, intent(in) :: x(ixi^s,1:ndim)
220 double precision, intent(out) :: t(ixi^s)
221
222 t(ixo^s) = w(ixo^s,iw_te)
223
224 end subroutine get_te_lte
225
226 subroutine get_temperature_from_eint_lte(w, x, ixI^L, ixO^L, res)
227 !> Assumes input energy is internal energy.
228 !> Includes FI bypass: cells with eint/rho above threshold skip table lookups.
229 use mod_physics
230 integer, intent(in) :: ixi^l,ixo^l
231 double precision, intent(in) :: x(ixi^s,1:ndim)
232 double precision, intent(in) :: w(ixi^s,1:nw)
233 double precision, intent(out) :: res(ixi^s)
234
235 double precision :: nh(ixi^s),nh_in(ixi^s), eint_in(ixi^s)
236 double precision :: rfactor(ixi^s), rfactor_fi, t_loc, y_loc
237 integer :: ix^d
238
239 timeeos0 = mpi_wtime()
240
241 rfactor_fi = eos%n_per_nH_FI / (1.0d0 + 4.0d0*eos%He_abundance)
242
243 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
244 nh_in(ixo^s) = dlog10(nh(ixo^s))
245 eint_in(ixo^s) = dlog10(w(ixo^s,iw_e)) - nh_in(ixo^s)
246
247 {do ix^db=ixomin^db,ixomax^db\}
248 if (w(ix^d,iw_e) / w(ix^d,iw_rho) > eos%eint_rho_FI_threshold) then
249 !> Fully ionised: T = (eint - eion*nH) * (gamma-1) / (Rfactor_FI * rho)
250 res(ix^d) = eos%gamma_minus_1 &
251 * (w(ix^d,iw_e) - eos%eion_per_nH * nh(ix^d)) &
252 / (rfactor_fi * w(ix^d,iw_rho))
253 else if (eos%method_id == eos_analytic) then
254 call saha_t_from_nh_eint(nh(ix^d), &
255 w(ix^d,iw_e) / nh(ix^d), t_loc, y_loc)
256 res(ix^d) = t_loc
257 else
258 res(ix^d) = t_from_nh_eint(nh_in(ix^d),eint_in(ix^d))
259 endif
260 {end do\}
261
262 timeeos_tfromei=timeeos_tfromei+(mpi_wtime()-timeeos0)
263 end subroutine get_temperature_from_eint_lte
264
265 subroutine get_temperature_from_eint_fast_lte(w, x, ixI^L, ixO^L, res)
266 !> Fast TC variant: two-pass regime-aware bypass.
267 !>
268 !> Pass 1 (vectorised): compute FI formula for ALL cells as array ops.
269 !> The compiler vectorises this with SVML (no branches, pure arithmetic).
270 !>
271 !> Pass 2 (scalar): overwrite ionisation-zone cells with
272 !> bilinear table lookup + dexp. Threshold check uses multiply
273 !> (eint <= threshold * rho) to avoid a 14-cycle scalar division.
274 use mod_physics
276 integer, intent(in) :: ixi^l,ixo^l
277 double precision, intent(in) :: x(ixi^s,1:ndim)
278 double precision, intent(in) :: w(ixi^s,1:nw)
279 double precision, intent(out) :: res(ixi^s)
280
281 double precision :: inv_rfactor_fi, eion_rho_inv
282 double precision :: log_nh_val, log_eint_nh_val
283 double precision :: fx, fy, rx, ry, nh_loc, t_loc, y_loc
284 integer :: jx, jy, jx1, jy1, ix^d
285 double precision, parameter :: ln10 = 2.302585092994046d0
286
287 if (eos%type_id /= eos_type_lte) call mpistop("get_temperature_from_eint_fast_LTE called outside its eos_type (LTE)")
288 timeeos0 = mpi_wtime()
289
290 !> Precompute scalar constants (avoid per-cell divisions)
291 inv_rfactor_fi = (1.0d0 + 4.0d0*eos%He_abundance) / eos%n_per_nH_FI
292 eion_rho_inv = eos%eion_per_nH / eos%nH2rhoFactor
293
294 !> Pass 1: FI formula for ALL cells (vectorisable array operations).
295 !> T = (gamma-1) * (eint - eion_per_rho * rho) / (Rfactor_FI * rho)
296 res(ixo^s) = eos%gamma_minus_1 * inv_rfactor_fi &
297 * (w(ixo^s,iw_e) - eion_rho_inv * w(ixo^s,iw_rho)) &
298 / w(ixo^s,iw_rho)
299
300 !> Pass 2: overwrite ionisation-zone cells.
301 {do ix^db=ixomin^db,ixomax^db\}
302 if (w(ix^d,iw_e) <= eos%eint_rho_FI_threshold * w(ix^d,iw_rho)) then
303 if (eos%method_id == eos_analytic) then
304 nh_loc = w(ix^d, iw_rho) / eos%nH2rhoFactor
305 call saha_t_from_nh_eint(nh_loc, &
306 w(ix^d,iw_e) / nh_loc, t_loc, y_loc)
307 res(ix^d) = t_loc
308 else if (eos%method_id == eos_entropy) then
309 if (iw_log_nh > 0) then
310 log_nh_val = block%wextra(ix^d, iw_log_nh)
311 else
312 log_nh_val = dlog10(w(ix^d, iw_rho) / eos%nH2rhoFactor)
313 end if
314 log_eint_nh_val = dlog10(w(ix^d, iw_e)) - log_nh_val
315 res(ix^d) = entropy_t_from_nh_eint(eos%Tfwd, eos%Tfwd_x, &
316 eos%Tfwd_y, eos%Tfwd_xy, log_nh_val, log_eint_nh_val)
317 else
318 if (iw_log_nh > 0) then
319 log_nh_val = block%wextra(ix^d, iw_log_nh)
320 else
321 log_nh_val = dlog10(w(ix^d, iw_rho) / eos%nH2rhoFactor)
322 end if
323 log_eint_nh_val = dlog10(w(ix^d, iw_e)) - log_nh_val
324
325 if (eos%T%is_uniform) then
326 !> Inlined uniform bilinear (hot path: no function call).
327 ry = max(0.0d0, min((log_nh_val - eos%T%var1_min) * eos%T%step_inv_1, &
328 dble(eos%T%dim1-1)))
329 rx = max(0.0d0, min((log_eint_nh_val - eos%T%var2_min) * eos%T%step_inv_2, &
330 dble(eos%T%dim2-1)))
331 jy = int(ry); jx = int(rx)
332 jy1 = min(jy+1, eos%T%dim1-1)
333 jx1 = min(jx+1, eos%T%dim2-1)
334 fy = ry - dble(jy); fx = rx - dble(jx)
335 res(ix^d) = dexp(ln10 * ( &
336 (1.0d0-fy)*((1.0d0-fx)*eos%T%table(jy+1,jx+1) &
337 + fx *eos%T%table(jy+1,jx1+1)) &
338 + fy *((1.0d0-fx)*eos%T%table(jy1+1,jx+1) &
339 + fx *eos%T%table(jy1+1,jx1+1))))
340 else
341 !> Non-uniform: dispatch to the binary-search bilinear.
342 !> Slightly slower per cell (binary search adds ~8 cmps per axis)
343 !> but unavoidable when the grid is non-uniform.
344 res(ix^d) = dexp(ln10 * bilinear_lookup( &
345 log_nh_val, log_eint_nh_val, eos%T))
346 end if
347 end if
348 end if
349 {end do\}
350
351 timeeos_tfromei = timeeos_tfromei + (mpi_wtime()-timeeos0)
353
354 !>#######################################################################
355 !> Scalar EoS kernels: pointwise lookups on (log10 nH, log10 eint/nH) or
356 !> (log10 nH, log10 p/nH) in code units, called by the hd/mhd EoS layer.
357 !> Each dispatches analytic / entropy / table. Ionisation fraction and
358 !> temperature first, then pressure / Gamma_1 / eint.
359 !>#######################################################################
360
361 !> Ionization fraction from (log10 nH, log10 eint/nH) in code units.
362 !> Dispatches: analytic -> Saha quadratic, tables -> PCHIP interpolation.
363 double precision function y_from_nh_eint(nH, eint_nh) result(result_val)
365 double precision, intent(in) :: nh, eint_nh
366 double precision, parameter :: ln10 = 2.302585092994046d0
367 double precision :: t_loc, y_loc, eint_rho
368
369 if (eos%type_id /= eos_type_lte) call mpistop("y_from_nH_eint called outside its eos_type (LTE)")
370 if (eos%method_id == eos_analytic) then
371 ! FI bypass: skip Saha solve for fully ionized cells
372 eint_rho = 10.0d0**eint_nh / eos%nH2rhoFactor
373 if (eint_rho > eos%eint_rho_FI_threshold) then
374 result_val = eos%neOnH_FI
375 return
376 end if
377 call saha_t_from_nh_eint(10.0d0**nh, 10.0d0**eint_nh, t_loc, y_loc)
378 result_val = y_loc
379 else if (eos%method_id == eos_entropy) then
380 ! neOnH stored linearly (not log10); single bicubic Hermite lookup.
381 result_val = entropy_y_from_nh_eint(eos%neOnH, eos%neOnH_x, &
382 eos%neOnH_y, eos%neOnH_xy, nh, eint_nh)
383 else
384 result_val = dexp(ln10 * bicubic_lookup(nh, eint_nh, eos%neOnH))
385 end if
386 end function y_from_nh_eint
387
388 !> Temperature from (log10 nH, log10 eint/nH) in code units.
389 !> Dispatches: analytic -> Saha bisection/Newton, tables -> PCHIP interpolation.
390 double precision function t_from_nh_eint(nH, eint_nh) result(result_val)
392 double precision, intent(in) :: nh, eint_nh
393 double precision, parameter :: ln10 = 2.302585092994046d0
394 double precision :: t_loc, y_loc, eint_rho, rfactor_fi
395
396 if (eos%type_id /= eos_type_lte) call mpistop("T_from_nH_eint called outside its eos_type (LTE)")
397 if (eos%method_id == eos_analytic) then
398 ! FI bypass: skip Saha solve for fully ionized cells
399 eint_rho = 10.0d0**eint_nh / eos%nH2rhoFactor
400 if (eint_rho > eos%eint_rho_FI_threshold) then
401 rfactor_fi = eos%n_per_nH_FI / (1.0d0 + 4.0d0*eos%He_abundance)
402 result_val = eos%gamma_minus_1 &
403 * (10.0d0**eint_nh - eos%eion_per_nH) / rfactor_fi
404 return
405 end if
406 call saha_t_from_nh_eint(10.0d0**nh, 10.0d0**eint_nh, t_loc, y_loc)
407 result_val = t_loc
408 else if (eos%method_id == eos_entropy) then
409 result_val = entropy_t_from_nh_eint(eos%Tfwd, eos%Tfwd_x, &
410 eos%Tfwd_y, eos%Tfwd_xy, nh, eint_nh)
411 else
412 result_val = dexp(ln10 * bicubic_lookup(nh, eint_nh, eos%T))
413 end if
414 end function t_from_nh_eint
415
416 !> Fused T+y lookup from (log10 nH, log10 eint/nH) in code units.
417 !> Computes grid indices once, evaluates both T and y tables.
418 !> Saves one index computation + better cache utilisation vs separate calls.
419 subroutine t_and_y_from_nh_eint(log_nH, log_eint_nH, T_out, y_out)
421 double precision, intent(in) :: log_nh, log_eint_nh
422 double precision, intent(out) :: t_out, y_out
423 double precision, parameter :: ln10 = 2.302585092994046d0
424 double precision :: results(3)
425
426 if (eos%method_id == eos_analytic) then
427 call saha_t_from_nh_eint(10.0d0**log_nh, 10.0d0**log_eint_nh, t_out, y_out)
428 else if (eos%method_id == eos_entropy) then
429 call entropy_t_and_y_from_nh_eint(eos%Tfwd, eos%Tfwd_x, &
430 eos%Tfwd_y, eos%Tfwd_xy, &
431 eos%neOnH, eos%neOnH_x, eos%neOnH_y, eos%neOnH_xy, &
432 log_nh, log_eint_nh, t_out, y_out)
433 else if (allocated(eos%table_eint_il)) then
434 ! Interleaved PCHIP fast-path.
435 ! (T, n_e/n_H, p/n_H), so cache behaviour is preserved.
436 if (eos%T%is_uniform) then
437 call interp_pchip_interleaved(log_nh, log_eint_nh, &
438 eos%table_eint_il, 3, eos%T%dim2, eos%T%dim1, &
439 eos%T%var1_min, eos%T%var1_max, &
440 eos%T%var2_min, eos%T%var2_max, results)
441 else
442 call interp_pchip_interleaved_nu(log_nh, log_eint_nh, &
443 eos%table_eint_il, 3, eos%T%dim2, eos%T%dim1, &
444 eos%T%var1_nodes, eos%T%var2_nodes, &
445 eos%T%guard_1, eos%T%guard_M_1, eos%T%guard_scale_1, &
446 eos%T%guard_2, eos%T%guard_M_2, eos%T%guard_scale_2, &
447 results)
448 end if
449 t_out = dexp(ln10 * results(1))
450 y_out = dexp(ln10 * results(2))
451 else
452 ! No interleaved table built -- separate dispatcher calls
453 t_out = dexp(ln10 * bicubic_lookup(log_nh, log_eint_nh, eos%T))
454 y_out = dexp(ln10 * bicubic_lookup(log_nh, log_eint_nh, eos%neOnH))
455 end if
456 end subroutine t_and_y_from_nh_eint
457
458 !> Pressure-to-eint ratio from (log10 nH, log10 p/nH) in code units.
459 !> Dispatches: analytic -> Saha solve for eint/p, tables -> PCHIP interpolation.
460 double precision function p2eint_from_nh_p(nH, ponH) result(result_val)
462 double precision, intent(in) :: nh, ponh
463 double precision :: nh_code, p_code, t_loc, y_loc, eint_nh_loc, p_rho
464
465 if (eos%type_id /= eos_type_lte) call mpistop("p2eint_from_nH_p called outside its eos_type (LTE)")
466 if (eos%method_id == eos_analytic) then
467 nh_code = 10.0d0**nh
468 p_code = nh_code * 10.0d0**ponh
469 ! FI bypass: skip Saha solve for fully ionized cells
470 p_rho = p_code / (nh_code * eos%nH2rhoFactor)
471 if (p_rho > eos%p_rho_FI_threshold) then
472 result_val = eos%inv_gamma_minus_1 &
473 + eos%eion_per_nH * nh_code / p_code
474 return
475 end if
476 call saha_state_from_nh_p(nh_code, p_code, t_loc, y_loc, eint_nh_loc)
477 result_val = eint_nh_loc * nh_code / p_code
478 else if (eos%method_id == eos_entropy) then
479 ! One bicubic-Hermite lookup on the eintP table.
480 result_val = entropy_eint_from_nh_p(eos%eintP, eos%eintP_x, &
481 eos%eintP_y, eos%eintP_xy, nh, ponh)
482 else
483 result_val = bicubic_lookup(nh, ponh, eos%p2eint)
484 end if
485 end function p2eint_from_nh_p
486
487 !> Gamma_1 from pressure-indexed table: (log10 nH, log10 p/nH) -> Gamma_1.
488 !> For 'entropy' the conversion p -> eint -> Gamma_1 via formula is intended
489 !> to keep Maxwell consistency with the forward at runtime. The p->eint inverse
490 !> is one bisection per cell -- non-trivial cost; this function is in the hot path
491 !> via hd_get_csound2_LTE.
492 double precision function gamma1_from_nh_p(log_nH, log_p_nH) result(g1)
494 double precision, intent(in) :: log_nh, log_p_nh
495 if (eos%type_id /= eos_type_lte) call mpistop("gamma1_from_nH_p called outside its eos_type (LTE)")
496 if (eos%method_id == eos_entropy) then
497 ! Single bicubic Hermite lookup on the pre-built Gamma_1(rho, p) table.
498 ! ZERO runtime iterations; no p->eint intermediate inversion.
499 g1 = entropy_gamma1_from_nh_p(eos%g1p, eos%g1p_x, eos%g1p_y, &
500 eos%g1p_xy, log_nh, log_p_nh)
501 else
502 g1 = bicubic_lookup(log_nh, log_p_nh, eos%gamma1_p)
503 end if
504 end function gamma1_from_nh_p
505
506 !> Merged log10(p/nH) lookup: (log10 nH, log10 eint/nH) -> log10(p/nH)
507 !> Single PCHIP evaluation replacing separate T + neOnH lookups.
508 double precision function log_p_from_nh_eint(log_nH, log_eint_nH) result(lp)
509 double precision, intent(in) :: log_nh, log_eint_nh
510 lp = bicubic_lookup(log_nh, log_eint_nh, eos%log_p)
511 end function log_p_from_nh_eint
512
513 !> p/nH from (log10 nH, log10 eint/nH) in code units.
514 !> Returns (1+He+y)*T directly -- single lookup replaces T + y lookups.
515 double precision function p_nh_from_eint(log_nH, log_eint_nH) result(p_nH)
517 double precision, intent(in) :: log_nh, log_eint_nh
518 double precision, parameter :: ln10 = 2.302585092994046d0
519 double precision :: t_loc, y_loc
520
521 if (eos%type_id /= eos_type_lte) call mpistop("p_nH_from_eint called outside its eos_type (LTE)")
522 if (eos%method_id == eos_analytic) then
523 call saha_t_from_nh_eint(10.0d0**log_nh, 10.0d0**log_eint_nh, t_loc, y_loc)
524 p_nh = (1.0d0 + eos%He_abundance + y_loc) * t_loc
525 else if (eos%method_id == eos_entropy) then
526 p_nh = entropy_p_nh_from_eint(eos%pfwd, eos%pfwd_x, eos%pfwd_y, &
527 eos%pfwd_xy, log_nh, log_eint_nh)
528 else
529 p_nh = dexp(ln10 * bicubic_lookup(log_nh, log_eint_nh, eos%p_over_nH))
530 end if
531 end function p_nh_from_eint
532
533 !> Internal energy per nH from (log10 nH, log10 T) in code units.
534 !> Uses the bisection-built inverse table (H+He, machine precision).
535 !> Fallback: H-only Saha if table not built.
536 double precision function eint_nh_from_t(log_nH, log_T) result(eint_nH)
538 double precision, intent(in) :: log_nh, log_t
539 double precision, parameter :: ln10 = 2.302585092994046d0
540 double precision :: log_e_nh
541
542 if (eos%type_id /= eos_type_lte) call mpistop("eint_nH_from_T called outside its eos_type (LTE)")
543 if (eos%method_id == eos_entropy) then
544 ! Single bicubic Hermite lookup on the pre-built eint(rho, T)
545 ! inverse table. ZERO runtime iterations.
546 log_e_nh = entropy_eint_from_nh_t(eos%eintT, eos%eintT_x, &
547 eos%eintT_y, eos%eintT_xy, log_nh, log_t)
548 eint_nh = 10.0d0**log_e_nh
549 else if (allocated(eos%eint_from_T%table)) then
550 eint_nh = dexp(ln10 * bicubic_lookup(log_nh, log_t, eos%eint_from_T))
551 else
552 eint_nh = saha_eint_from_nh_t(10.0d0**log_nh, 10.0d0**log_t)
553 end if
554 end function eint_nh_from_t
555
556 !>#######################################################################
557 !> Iterative solvers: table-guessed bisection for the WB pressure->eint
558 !> inversion since the method needs high accuracy to not self-seed perturbations.
559 !>#######################################################################
560
561 !> Cached bisection on the log_p table for WB p->eint inversion.
562 !> Precomputes nH-direction indices and table values once, then
563 !> bisects using only cheap PCHIP evaluations with varying tx.
564 !> Expects a narrow initial bracket [lo, hi] (e.g. from p2eint guess).
565 !>
566 !> Adaptive-grid support: when eos%log_p%is_uniform is .false., the
567 !> y- and x-direction index calculations switch to binary search on
568 !> the explicit node arrays. The cached 4x4 stencil mechanism is
569 !> unchanged; only the index-to-cell mapping changes.
570 subroutine log_p_bisect_cached(log_nH, log_p_target, &
571 log_eint_lo, log_eint_hi, max_iter, log_eint_result)
573 double precision, intent(in) :: log_nh, log_p_target
574 double precision, intent(inout) :: log_eint_lo, log_eint_hi
575 integer, intent(in) :: max_iter
576 double precision, intent(out) :: log_eint_result
577
578 integer :: nx, ny, ix, iy, iter, ii
579 integer :: i0, i1, i2, i3, j0, j1, j2, j3
580 double precision :: tx, ty, rx, ry
581 double precision :: xstep_inv, ystep_inv
582 double precision :: vmin_x, vmax_x, vmin_y, vmax_y
583 double precision :: log_eint_mid, log_p_eval
584 logical :: is_unif
585
586 !> Table values: tv(y_row, x_col) for 4 y-rows x 4 x-cols
587 double precision :: tv(4,4)
588 !> Cached ix for detecting grid cell change
589 integer :: ix_cached
590
591 nx = eos%log_p%dim2 ! eint axis (varx)
592 ny = eos%log_p%dim1 ! nH axis (vary)
593 is_unif = eos%log_p%is_uniform
594
595 if (is_unif) then
596 vmin_x = eos%log_p%var2_min
597 vmax_x = eos%log_p%var2_max
598 vmin_y = eos%log_p%var1_min
599 vmax_y = eos%log_p%var1_max
600 xstep_inv = dble(nx-1) / (vmax_x - vmin_x)
601 ystep_inv = dble(ny-1) / (vmax_y - vmin_y)
602 end if
603
604 !> Precompute nH indices (constant across all iterations)
605 if (is_unif) then
606 ry = (log_nh - vmin_y) * ystep_inv
607 ry = max(0.0d0, min(ry, dble(ny-1)))
608 iy = int(ry)
609 ty = ry - dble(iy)
610 else
611 !> Non-uniform: binary search on var1_nodes (length ny = dim1)
612 if (log_nh <= eos%log_p%var1_nodes(1)) then
613 iy = 0; ty = 0.0d0
614 else if (log_nh >= eos%log_p%var1_nodes(ny)) then
615 iy = ny - 2; ty = 1.0d0
616 else
617 ii = find_index_guard(eos%log_p%var1_nodes, ny, log_nh, &
618 eos%log_p%guard_1, eos%log_p%guard_M_1, eos%log_p%guard_scale_1)
619 iy = max(0, min(ii - 2, ny - 2))
620 ty = (log_nh - eos%log_p%var1_nodes(iy+1)) &
621 / (eos%log_p%var1_nodes(iy+2) - eos%log_p%var1_nodes(iy+1))
622 ty = max(0.0d0, min(ty, 1.0d0))
623 end if
624 end if
625
626 !> Clamped y-row indices
627 j0 = max(0, min(ny-1, iy-1))
628 j1 = max(0, min(ny-1, iy ))
629 j2 = max(0, min(ny-1, iy+1))
630 j3 = max(0, min(ny-1, iy+2))
631
632 ix_cached = -1
633
634 do iter = 1, max_iter
635 log_eint_mid = 0.5d0 * (log_eint_lo + log_eint_hi)
636
637 !> Compute eint-direction index
638 if (is_unif) then
639 rx = (log_eint_mid - vmin_x) * xstep_inv
640 rx = max(0.0d0, min(rx, dble(nx-1)))
641 ix = int(rx)
642 tx = rx - dble(ix)
643 else
644 !> Non-uniform: binary search on var2_nodes (length nx = dim2)
645 if (log_eint_mid <= eos%log_p%var2_nodes(1)) then
646 ix = 0; tx = 0.0d0
647 else if (log_eint_mid >= eos%log_p%var2_nodes(nx)) then
648 ix = nx - 2; tx = 1.0d0
649 else
650 ii = find_index_guard(eos%log_p%var2_nodes, nx, log_eint_mid, &
651 eos%log_p%guard_2, eos%log_p%guard_M_2, eos%log_p%guard_scale_2)
652 ix = max(0, min(ii - 2, nx - 2))
653 tx = (log_eint_mid - eos%log_p%var2_nodes(ix+1)) &
654 / (eos%log_p%var2_nodes(ix+2) - eos%log_p%var2_nodes(ix+1))
655 tx = max(0.0d0, min(tx, 1.0d0))
656 end if
657 end if
658
659 !> Load 16 table values only when ix changes
660 if (ix /= ix_cached) then
661 i0 = max(0, min(nx-1, ix-1))
662 i1 = max(0, min(nx-1, ix ))
663 i2 = max(0, min(nx-1, ix+1))
664 i3 = max(0, min(nx-1, ix+2))
665
666 tv(1,1) = eos%log_p%table(j0+1, i0+1)
667 tv(1,2) = eos%log_p%table(j0+1, i1+1)
668 tv(1,3) = eos%log_p%table(j0+1, i2+1)
669 tv(1,4) = eos%log_p%table(j0+1, i3+1)
670 tv(2,1) = eos%log_p%table(j1+1, i0+1)
671 tv(2,2) = eos%log_p%table(j1+1, i1+1)
672 tv(2,3) = eos%log_p%table(j1+1, i2+1)
673 tv(2,4) = eos%log_p%table(j1+1, i3+1)
674 tv(3,1) = eos%log_p%table(j2+1, i0+1)
675 tv(3,2) = eos%log_p%table(j2+1, i1+1)
676 tv(3,3) = eos%log_p%table(j2+1, i2+1)
677 tv(3,4) = eos%log_p%table(j2+1, i3+1)
678 tv(4,1) = eos%log_p%table(j3+1, i0+1)
679 tv(4,2) = eos%log_p%table(j3+1, i1+1)
680 tv(4,3) = eos%log_p%table(j3+1, i2+1)
681 tv(4,4) = eos%log_p%table(j3+1, i3+1)
682
683 ix_cached = ix
684 end if
685
686 !> Evaluate PCHIP: 4 x-rows then 1 y-interp
687 log_p_eval = pchip_2d_from_cache(tv, tx, ty)
688
689 if (log_p_eval < log_p_target) then
690 log_eint_lo = log_eint_mid
691 else
692 log_eint_hi = log_eint_mid
693 end if
694
695 if (dabs(log_eint_hi - log_eint_lo) < 1.0d-14) exit
696 end do
697
698 log_eint_result = 0.5d0 * (log_eint_lo + log_eint_hi)
699
700 contains
701
702 pure double precision function pchip_1d(p0, p1, p2, p3, t) result(v)
703 double precision, intent(in) :: p0, p1, p2, p3, t
704 double precision :: d0, d1, d2, m1, m2, s, a1, a2, lim
705 double precision :: tt, ttt, h00, h10, h01, h11
706
707 d0 = p1 - p0; d1 = p2 - p1; d2 = p3 - p2
708
709 if (d1 == 0.0d0) then
710 m1 = 0.0d0; m2 = 0.0d0
711 else
712 if (d0*d1 <= 0.0d0) then
713 m1 = 0.0d0
714 else
715 m1 = 2.0d0*d0*d1 / (d0 + d1)
716 end if
717 if (d1*d2 <= 0.0d0) then
718 m2 = 0.0d0
719 else
720 m2 = 2.0d0*d1*d2 / (d1 + d2)
721 end if
722 s = sign(1.0d0, d1)
723 a1 = s*m1; a2 = s*m2
724 if (a1 < 0.0d0) a1 = 0.0d0
725 if (a2 < 0.0d0) a2 = 0.0d0
726 lim = 3.0d0*abs(d1)
727 if (a1 > lim) a1 = lim
728 if (a2 > lim) a2 = lim
729 m1 = s*a1; m2 = s*a2
730 end if
731
732 tt = t*t; ttt = tt*t
733 h00 = 2.0d0*ttt - 3.0d0*tt + 1.0d0
734 h10 = ttt - 2.0d0*tt + t
735 h01 = -2.0d0*ttt + 3.0d0*tt
736 h11 = ttt - tt
737 v = h00*p1 + h10*m1 + h01*p2 + h11*m2
738 end function pchip_1d
739
740 pure double precision function pchip_2d_from_cache(c, tx, ty) result(z)
741 double precision, intent(in) :: c(4,4), tx, ty
742 double precision :: g0, g1, g2, g3
743 !> 4 x-direction interpolations (one per y-row)
744 g0 = pchip_1d(c(1,1), c(1,2), c(1,3), c(1,4), tx)
745 g1 = pchip_1d(c(2,1), c(2,2), c(2,3), c(2,4), tx)
746 g2 = pchip_1d(c(3,1), c(3,2), c(3,3), c(3,4), tx)
747 g3 = pchip_1d(c(4,1), c(4,2), c(4,3), c(4,4), tx)
748 !> 1 y-direction interpolation
749 z = pchip_1d(g0, g1, g2, g3, ty)
750 end function pchip_2d_from_cache
751
752 end subroutine log_p_bisect_cached
753
754 !> Given log10(nH) and log10(p), find log10(eint/nH) by table-guessed
755 !> bisection on the forward pressure table. Wraps the bracketing logic
756 !> and log_p_bisect_cached into a single call for use by both HD and MHD
757 !> from_conserved routines.
758 subroutine eint_from_p_bisect(log_nH_val, log_p_val, log_eint_nH_out)
759 double precision, intent(in) :: log_nh_val, log_p_val
760 double precision, intent(out) :: log_eint_nh_out
761
762 double precision :: log_p_target, p2eint_ratio, log_eint_guess
763 double precision :: log_p_at_guess, log_eint_lo, log_eint_hi
764 double precision :: f_bracket, margin
765 integer :: max_iter
766
767 if (eos%type_id /= eos_type_lte) call mpistop("eint_from_p_bisect called outside its eos_type (LTE)")
768 log_p_target = log_p_val - log_nh_val
769
770 ! Initial guess from p2eint table
771 p2eint_ratio = p2eint_from_nh_p(log_nh_val, log_p_target)
772 log_eint_guess = dlog10(p2eint_ratio) + log_p_target
773
774 log_eint_guess = max(log_eint_guess, eos%log_p%var2_min)
775 log_eint_guess = min(log_eint_guess, eos%log_p%var2_max)
776
777 log_p_at_guess = log_p_from_nh_eint(log_nh_val, log_eint_guess)
778
779 ! Establish bracket around the guess
780 margin = 5.0d-4
781 if (log_p_at_guess < log_p_target) then
782 log_eint_lo = log_eint_guess
783 log_eint_hi = min(log_eint_guess + margin, eos%log_p%var2_max)
784 f_bracket = log_p_from_nh_eint(log_nh_val, log_eint_hi) - log_p_target
785 else
786 log_eint_lo = max(log_eint_guess - margin, eos%log_p%var2_min)
787 log_eint_hi = log_eint_guess
788 f_bracket = -(log_p_from_nh_eint(log_nh_val, log_eint_lo) - log_p_target)
789 end if
790
791 if (f_bracket >= 0.0d0) then
792 max_iter = 8
793 else
794 log_eint_lo = eos%log_p%var2_min
795 log_eint_hi = eos%log_p%var2_max
796 max_iter = 20
797 end if
798
799 call log_p_bisect_cached(log_nh_val, log_p_target, &
800 log_eint_lo, log_eint_hi, max_iter, log_eint_nh_out)
801
802 end subroutine eint_from_p_bisect
803
804 !>#######################################################################
805 !> Table-metadata helpers: expose inverse-table grid bounds without
806 !> reaching into EoS table internals.
807 !>#######################################################################
808
809 !> log_nH grid metadata of the (log_nH, log_T) inverse table (eint from T),
810 !> choosing the container by method ('tables'->eint_from_T, 'entropy'->eintT).
811 !> n_nH=0 if no such table (analytic/FI). Lets cooling's build_Y_mod_table
812 !> get its grid without reaching into EoS table internals.
813 subroutine eos_get_eintt_grid(n_nH, lg_nH_min, lg_nH_max)
814 integer, intent(out) :: n_nh
815 double precision, intent(out) :: lg_nh_min, lg_nh_max
816
817 if (eos%type_id /= eos_type_lte) call mpistop("eos_get_eintT_grid called outside its eos_type (LTE)")
818 if (allocated(eos%eint_from_T%table)) then
819 n_nh = eos%eint_from_T%dim1
820 lg_nh_min = eos%eint_from_T%var1_min
821 lg_nh_max = eos%eint_from_T%var1_max
822 else if (allocated(eos%eintT%table)) then
823 n_nh = eos%eintT%dim1
824 lg_nh_min = eos%eintT%var1_min
825 lg_nh_max = eos%eintT%var1_max
826 else
827 n_nh = 0
828 lg_nh_min = 0.0d0
829 lg_nh_max = 0.0d0
830 end if
831 end subroutine eos_get_eintt_grid
832
833end module mod_eos_lte
pure double precision function pchip_1d(p0, p1, p2, p3, t)
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
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).
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)
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.
Analytic H-only Saha EoS (eos_method == 'analytic').
subroutine, public finalise_analytic_lte()
Analytic-method finalise (H-only Saha): no table unit conversion needed; build the Gamma1 table only ...
subroutine, public load_analytic_lte()
Analytic-method load: no binary tables (on-the-fly Saha solve); just announce the method.
'state' method (eos_method='state', legacy 'tables') of the LTE EoS family.
subroutine, public finalise_state_lte()
Table-based method finalise: shift the loaded T/neOnH (and ionE-derived) tables to code units,...
subroutine, public load_state_lte()
state arms of the eos_init / eos_finalise dispatchers (mod_eos_LTE)
Shared LTE lookup-table infrastructure (build/IO; not on the hot path).
LTE (Saha-table) EoS kernels and finalise for the eos% family.
Definition mod_eos_LTE.t:12
double precision function, public p_nh_from_eint(log_nh, log_eint_nh)
p/nH from (log10 nH, log10 eint/nH) in code units. Returns (1+He+y)*T directly – single lookup replac...
double precision function, public p2eint_from_nh_p(nh, ponh)
Pressure-to-eint ratio from (log10 nH, log10 p/nH) in code units. Dispatches: analytic -> Saha solve ...
subroutine, public eos_finalise_lte()
LTE arm of eos_finalise: wire the LTE runtime pointer targets, then dispatch on eos_method to the per...
Definition mod_eos_LTE.t:60
double precision function, public eint_nh_from_t(log_nh, log_t)
Internal energy per nH from (log10 nH, log10 T) in code units. Uses the bisection-built inverse table...
subroutine, public eint_from_p_bisect(log_nh_val, log_p_val, log_eint_nh_out)
Given log10(nH) and log10(p), find log10(eint/nH) by table-guessed bisection on the forward pressure ...
double precision function, public y_from_nh_eint(nh, eint_nh)
Ionization fraction from (log10 nH, log10 eint/nH) in code units. Dispatches: analytic -> Saha quadra...
subroutine, public get_temperature_from_eint_fast_lte(w, x, ixil, ixol, res)
subroutine, public eos_get_eintt_grid(n_nh, lg_nh_min, lg_nh_max)
log_nH grid metadata of the (log_nH, log_T) inverse table (eint from T), choosing the container by me...
double precision function, public gamma1_from_nh_p(log_nh, log_p_nh)
Gamma_1 from pressure-indexed table: (log10 nH, log10 p/nH) -> Gamma_1. For 'entropy' the conversion ...
subroutine, public eos_init_lte()
update_eos_LTE, get_Te_LTE, get_temperature_from_eint_LTE and Rfactor_from_LTE are PRIVATE: all bound...
Definition mod_eos_LTE.t:48
double precision function, public t_from_nh_eint(nh, eint_nh)
Temperature from (log10 nH, log10 eint/nH) in code units. Dispatches: analytic -> Saha bisection/Newt...
Shared EoS accessors (EoS-type-agnostic) for the eos% family.
This module contains definitions of global parameters and variables and some generic functions/subrou...
integer, parameter ndim
Number of spatial dimensions for grid variables.
double precision, dimension(:), allocatable, parameter d
double precision unit_temperature
Physical scaling factor for temperature.
A Fortran 90 module for creating 1D and 2D lookup tables. These tables can be used to efficiently int...
pure integer function, public find_index_bsearch(list, val)
Binary search of sorted list for the smallest ix such that list(ix) >= val. On failure,...
This module defines the procedures of a physics module. It contains function pointers for the various...
Definition mod_physics.t:4
procedure(sub_e_to_ei), pointer phys_e_to_ei
Definition mod_physics.t:66
double precision timeeos0
Definition mod_timing.t:10