MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_hd_eos.t
Go to the documentation of this file.
1!=============================================================================
2!> HD <-> EoS seam: binds the eos% authority into hydrodynamics.
3!>
4!> hd_link_eos wires the eos%/phys_ procedure pointers (conversions, pthermal,
5!> csound2/gamma1, get_Rfactor, temperature, update_temperature) per eos_type
6!> (FI / LTE / PI, with PI energy- and prominence-mode overrides).
7!> bind_eos_to_source wires the thermal-conduction / radiative-cooling / FLD
8!> fluid-port callbacks from eos%.
9!>
10!> Holds the HD block routines that wrap the shared thermodynamics with HD's
11!> mechanical-energy bookkeeping -- total-energy ("origin") formulation,
12!> dust-aware: FI/LTE/PI conversions, p_to_e, pthermal, csound2/gamma1, and the
13!> PI energy + prominence R-factor / temperature kernels.
14!=============================================================================
17 use mod_physics
18 use mod_eos
19 !> Mode-specific kernels come from their sub-modules (the facade no longer
20 !> re-exports them); each mpistops if called under the wrong eos_type/method.
21 use mod_eos_lte
24 use mod_eos_pi
26 use mod_hd_phys
27 use mod_timing
29
30 use mod_comm_lib, only: mpistop
31
32 implicit none
33 private
34
35 procedure(sub_convert), pointer, public :: hd_to_primitive => null()
36 procedure(sub_convert), pointer, public :: hd_to_conserved => null()
37
39
40contains
41
42 !> Link the appropriate EOS conversion routines based on the selected EoS type
43 subroutine hd_link_eos()
45
46 ! PI (partial approximations) takes the FI ideal-gas conversions as
47 ! its BASE (e_int=p/(gamma-1); variable R via get_Rfactor). Energy
48 ! mode (ionE) overrides them with the PI-energy variants at the end
49 ! of this routine; FI conversions stay pristine.
50 if (eos%eos_type == 'FI' .or. eos%eos_type == 'PI') then
51 eos%to_conserved => hd_to_conserved_origin
52 eos%to_primitive => hd_to_primitive_origin
53 else if (eos%eos_type == 'LTE') then
54 eos%to_conserved => hd_to_conserved_lte
55 eos%to_primitive => hd_to_primitive_lte
56 else
57 call mpistop('Error: Unknown HD EOS type: ' // trim(eos%eos_type))
58 end if
59
60 phys_to_primitive => eos%to_primitive
61 phys_to_conserved => eos%to_conserved
62 phys_get_rho => eos%get_rho
64 eos%get_thermal_pressure => hd_get_pthermal
65 phys_bind_eos_to_source => bind_eos_to_source
66
67 eos%p_to_e => p_to_e !> suitable for both FI and LTE
68
69 ! Link sound speed and gamma1 computation
70 if (eos%eos_type == 'LTE' .and. eos%ionE) then
71 eos%get_csound2 => hd_get_csound2_lte
72 phys_get_gamma1 => hd_get_gamma1_lte
73 ! EoS-aware prolongation: interpolate in (rho, v, T) space
74 phys_to_prolong => hd_to_prolong_lte
75 phys_from_prolong => hd_from_prolong_lte
76 else
77 eos%get_csound2 => hd_get_csound2_fi
78 phys_get_gamma1 => get_gamma1_fi
79 end if
80
81 ! Rfactor: only the FI case is physics-dependent (the usr_Rfactor user
82 ! hook). LTE and PI bind pure eos% routines in eos_finalise_{LTE,PI},
83 ! which run after this and before bind_eos_to_source (so they win and
84 ! their targets stay private). Note: this makes usr_Rfactor apply only to
85 ! FI for HD -- consistent with MHD, where the EoS owns R for LTE/PI.
86 if(associated(usr_rfactor)) then
87 eos%get_Rfactor=>usr_rfactor
88 else
89 eos%get_Rfactor=>rfactor_from_constant_ionization
90 end if
91
92 !> PI energy-mode overrides (eos_type='PI', ionE=.true.)
93 ! Energy mode differs from no-energy PI only in the eint<->p relation,
94 ! so override exactly the routines that touch it (conversions, p_to_e,
95 ! csound2/gamma1, Te update), all via the portable backend. pthermal
96 ! is unchanged: hd_get_pthermal already carries a PI-energy branch.
97 if (eos%eos_type == 'PI' .and. eos%ionE) then
98 if (.not. hd_energy) &
99 call mpistop('PI energy EoS requires hd_energy=.true.')
100 eos%to_conserved => hd_to_conserved_pi
101 eos%to_primitive => hd_to_primitive_pi
102 phys_to_primitive => eos%to_primitive
103 phys_to_conserved => eos%to_conserved
104 eos%p_to_e => p_to_e_pi
105 !> eos%get_csound2 => get_csound2_PI set in eos_finalise_PI (private target)
107 ! Te_ from eint each substep (uses HD's Te_ index: PI registers
108 ! Te via var_set_auxvar, so the generic iw_te is unset).
109 end if
110 hd_to_primitive => eos%to_primitive
111 hd_to_conserved => eos%to_conserved
112
113 end subroutine hd_link_eos
114
115 subroutine bind_eos_to_source() !> this is called in eos_finalise through mod_physics procedure linking
116 if (allocated(tc_fl)) then
117 tc_fl%get_temperature_from_conserved => eos%get_temperature_from_etot
118 !> Use fast bilinear T lookup for TC STS substeps (density fixed,
119 !> TC flux dominated by corona where T(eint) is smooth)
120 if (eos%eos_type == 'LTE' .and. eos%ionE) then
121 tc_fl%get_temperature_from_eint => get_temperature_from_eint_fast_lte
122 else
123 tc_fl%get_temperature_from_eint => eos%get_temperature_from_eint
124 end if
125 tc_fl%get_rho => eos%get_rho
126 tc_fl%get_ne_nH => eos%get_ne_nH
127 tc_fl%get_var_Rfactor => eos%get_Rfactor
128 tc_fl%inv_gamma_minus_1 = eos%inv_gamma_minus_1
129 tc_fl%nH2rhoFactor = eos%nH2rhoFactor
130 tc_fl%log_T_floor = eos_get_log_t_floor()
131 tc_fl%eint_from_T => eint_nh_from_t
132 end if
133
134 if (allocated(rc_fl)) then
135 rc_fl%get_rho => eos%get_rho
136 rc_fl%get_pthermal => eos%get_thermal_pressure
137 rc_fl%get_var_Rfactor => eos%get_Rfactor
138 rc_fl%get_Te => eos%get_Te
139 rc_fl%get_ne_nH => eos%get_ne_nH
140 rc_fl%ionE = eos%ionE
141 rc_fl%method = eos%method
142 rc_fl%inv_gamma_minus_1 = eos%inv_gamma_minus_1
143 rc_fl%nH2rhoFactor = eos%nH2rhoFactor
144 rc_fl%eion_per_nH = eos%eion_per_nH
145 rc_fl%eint_from_T => eint_nh_from_t
146 rc_fl%p2eint => p2eint_from_nh_p
147 rc_fl%T_from_eint => t_from_nh_eint
148 rc_fl%y_from_eint => y_from_nh_eint
149 !> Build the variable-c_V Townsend Y_mod table now that all
150 !> EoS tables (eint_from_T, T, neOnH) are in code units.
151 !> build_Y_mod_table checks coolmethod=='exact' and .not.isPPL
152 !> internally and early-returns otherwise. LTE only:
153 !> eos_get_eintT_grid reads the LTE eintT grid; PI runs classical
154 !> Townsend (Y_mod-for-PI is a later refinement).
155 if (eos%ionE .and. eos%eos_type == 'LTE') then
156 call eos_get_eintt_grid(rc_fl%Y_mod_n_nH, &
157 rc_fl%Y_mod_lg_nH_min, rc_fl%Y_mod_lg_nH_max)
159 end if
160 end if
161
162 !> PI energy mode: repoint cooling/conduction eint<->T callbacks at
163 !> the ionisation backend (the LTE-table versions above assume LTE
164 !> tables PI does not load). Classical Townsend; Y_mod not built (above).
165 if (eos%eos_type == 'PI' .and. eos%ionE) then
166 if (allocated(tc_fl)) tc_fl%eint_from_T => eint_from_t_pi
167 if (allocated(rc_fl)) then
168 rc_fl%eint_from_T => eint_from_t_pi
169 rc_fl%p2eint => p2eint_pi
170 rc_fl%T_from_eint => t_from_eint_pi
171 rc_fl%y_from_eint => y_from_eint_pi
172 end if
173 end if
174
175 if (allocated(fld_fl)) then
176 !> Radiation (FLD) fluid: gas-EoS callbacks. get_temperature_from_pressure
177 !> is the FI T=p/(R*rho) routine (the LTE variant is a later pass).
178 fld_fl%gamma = eos%gamma
179 fld_fl%get_tgas => eos%get_temperature_from_pressure
180 fld_fl%get_Rfactor => eos%get_Rfactor
181 end if
182 end subroutine bind_eos_to_source
183
184 !> Transform primitive variables into conservative ones
185 subroutine hd_to_conserved_origin(ixI^L, ixO^L, w, x)
187 use mod_dust, only: dust_to_conserved
188 integer, intent(in) :: ixi^l, ixo^l
189 double precision, intent(inout) :: w(ixi^s, nw)
190 double precision, intent(in) :: x(ixi^s, 1:ndim)
191
192 integer :: ix^d
193
194 timeeos0 = mpi_wtime() !> For monitoring cost of eos module
195
196 {do ix^db=ixomin^db,ixomax^db\}
197 if (hd_energy) then
198 ! Calculate total energy from pressure and kinetic energy
199 w(ix^d,e_)=w(ix^d,p_)*eos%inv_gamma_minus_1+&
200 half*(^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)
201 end if
202 ! Convert velocity to momentum
203 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)\
204 {end do\}
205
206 if (hd_dust) then
207 call dust_to_conserved(ixi^l, ixo^l, w, x)
208 end if
209
211
212 end subroutine hd_to_conserved_origin
213
214 !> Transform conservative variables into primitive ones
215 subroutine hd_to_primitive_origin(ixI^L, ixO^L, w, x)
217 use mod_dust, only: dust_to_primitive
218 integer, intent(in) :: ixi^l, ixo^l
219 double precision, intent(inout) :: w(ixi^s, nw)
220 double precision, intent(in) :: x(ixi^s, 1:ndim)
221
222 double precision :: inv_rho
223 integer :: ix^d
224
225 timeeos0 = mpi_wtime() !> For monitoring cost of eos module
226
227 if (fix_small_values) then
228 call hd_handle_small_values(.false., w, x, ixi^l, ixo^l, 'hd_to_primitive')
229 end if
230
231 {do ix^db=ixomin^db,ixomax^db\}
232 inv_rho = 1.d0/w(ix^d,rho_)
233 ! Convert momentum to velocity
234 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
235 ! Calculate pressure = (gamma-1) * (e-ek)
236 if(hd_energy) then
237 ! Compute pressure
238 w(ix^d,p_)=(eos%gamma_minus_1)*(w(ix^d,e_)&
239 -half*w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+))
240 end if
241 {end do\}
242
243 ! Convert dust momentum to dust velocity
244 if (hd_dust) then
245 call dust_to_primitive(ixi^l, ixo^l, w, x)
246 end if
247
249
250 end subroutine hd_to_primitive_origin
251
252 !> LTE primitive -> conserved conversion.
253 !>
254 !> On entry: rho_ = density, m_ = velocity, p_ = pressure.
255 !> On exit: rho_ = density (unchanged), m_ = momentum, e_ = total energy.
256 subroutine hd_to_conserved_lte(ixI^L, ixO^L, w, x)
258 use mod_dust, only: dust_to_conserved
259 integer, intent(in) :: ixi^l, ixo^l
260 double precision, intent(inout) :: w(ixi^s, nw)
261 double precision, intent(in) :: x(ixi^s, 1:ndim)
262
263 timeeos0 = mpi_wtime()
264
265 call p_to_e(ixi^l, ixo^l, w, x)
266
267 ! Convert velocity to momentum
268 ^c&w(ixo^s,m^c_)=w(ixo^s,rho_)*w(ixo^s,m^c_)\
269
270 if (hd_dust) then
271 call dust_to_conserved(ixi^l, ixo^l, w, x)
272 end if
273
275
276 end subroutine hd_to_conserved_lte
277
278 subroutine p_to_e(ixI^L, ixO^L, w, x)
279 !> Convert pressure to total energy: E = eint(rho, p) + KE.
280 !>
281 !> On entry: w(rho_) = density, w(m_) = velocity, w(p_) = pressure.
282 !> On exit: w(rho_) unchanged, w(m_) unchanged, w(e_) = total energy.
283 !>
284 !> Four paths for ionE (LTE with ionisation):
285 !> 1. FI bypass (p/rho > threshold): analytic eint = p/(gamma-1) + eion*nH
286 !> 2. Analytical Saha (eos_method='analytic'): direct Saha solve for T,y from p
287 !> 3. WB mode (hd_well_balanced): cached bisection on forward tables
288 !> 4. Standard: p2eint inverse table lookup (fast, ~0.01% round-trip error)
290 integer, intent(in) :: ixi^l, ixo^l
291 double precision, intent(inout) :: w(ixi^s, nw)
292 double precision, intent(in) :: x(ixi^s, 1:ndim)
293
294 integer :: ix^d
295 double precision :: p_to_eint, p_over_rho
296 double precision :: nh(ixi^s), nh_in(ixi^s), p_in(ixi^s)
297 double precision :: log_eint_mid, eint_total
298 double precision :: t_solve, y_solve, eint_nh_solve
299
300 if (eos%ionE) then
301 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
302 nh_in(ixo^s) = dlog10(nh(ixo^s))
303 if (eos%p2eint_method /= 'bisect') then
304 p_in(ixo^s) = dlog10(w(ixo^s,p_)) - nh_in(ixo^s)
305 end if
306 endif
307
308 p_to_eint = eos%inv_gamma_minus_1
309 {do ix^db=ixomin^db,ixomax^db\}
310 if (hd_energy) then
311 if (eos%ionE) then
312 p_over_rho = w(ix^d,p_) / w(ix^d,rho_)
313 if (p_over_rho > eos%p_rho_FI_threshold) then
314 !> FI bypass: exact inverse of to_primitive
315 p_to_eint = eos%inv_gamma_minus_1 &
316 + eos%eion_per_nH * nh(ix^d) / w(ix^d,p_)
317 w(ix^d,e_) = w(ix^d,p_)*p_to_eint + &
318 half*(^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)
319 else if (eos%method == 'analytic') then
320 !> Analytical Saha: bisect for T from p, return eint directly
321 call saha_state_from_nh_p(nh(ix^d), w(ix^d,p_), &
322 t_solve, y_solve, eint_nh_solve)
323 w(ix^d,e_) = eint_nh_solve * nh(ix^d) + &
324 half*(^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)
325 else if (eos%p2eint_method == 'bisect') then
326 !> Bisection on forward p table: finds eint such that
327 !> p(nH, eint) = p_target exactly. More accurate than
328 !> the p2eint table in the ionisation zone.
329 !> Entropy mode uses the pfwd/eintP pair: eos%log_p (used by
330 !> eint_from_p_bisect) is not loaded there.
331 if (eos%method == 'entropy') then
333 eos%pfwd, eos%pfwd_x, eos%pfwd_y, eos%pfwd_xy, &
334 eos%eintP, eos%eintP_x, eos%eintP_y, eos%eintP_xy, &
335 nh_in(ix^d), dlog10(w(ix^d,p_)) - nh_in(ix^d), &
336 log_eint_mid)
337 eint_total = nh(ix^d) * 10.0d0**log_eint_mid
338 else
339 call eint_from_p_bisect(nh_in(ix^d), &
340 dlog10(w(ix^d,p_)), log_eint_mid)
341 eint_total = nh(ix^d) * 10.0d0**log_eint_mid
342 eint_total = max(eint_total, &
343 nh(ix^d) * 10.0d0**eos%T%var2_min)
344 end if
345 w(ix^d,e_) = eint_total + &
346 half*(^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)
347 else
348 !> Standard table lookup (fast, default)
349 p_to_eint = p2eint_from_nh_p(nh_in(ix^d), p_in(ix^d))
350 w(ix^d,e_) = w(ix^d,p_)*p_to_eint + &
351 half*(^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)
352 end if
353 else
354 w(ix^d,e_) = w(ix^d,p_)*p_to_eint + &
355 half*(^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)
356 end if
357 end if
358 {end do\}
359
360 end subroutine p_to_e
361
362 !> LTE conserved -> primitive conversion.
363 !>
364 !> On entry: rho_ = density, m_ = momentum, e_ = total energy.
365 !> On exit: rho_ = density (unchanged), m_ = velocity, p_ = pressure.
366 !>
367 !> Pressure is computed energy-consistently from actual eint via EoS
368 !> table lookups (T and ne/nH). Cannot use stored Ne_/Te_ because
369 !> they may be stale after AMR prolongation/coarsening.
370 subroutine hd_to_primitive_lte(ixI^L, ixO^L, w, x)
372 use mod_dust, only: dust_to_primitive
373 integer, intent(in) :: ixi^l, ixo^l
374 double precision, intent(inout) :: w(ixi^s, nw)
375 double precision, intent(in) :: x(ixi^s, 1:ndim)
376
377 double precision :: inv_rho
378 double precision :: nh(ixi^s)
379 double precision :: log_nh(ixi^s)
380 double precision :: eint_val, eint_in, t_loc, y_loc
381 integer :: ix^d
382
383 timeeos0 = mpi_wtime()
384
385 if (fix_small_values) then
386 call hd_handle_small_values(.false., w, x, ixi^l, ixo^l, 'hd_to_primitive_LTE')
387 end if
388
389 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
390
391 ! Cache log10(nH) for all cells (used by table lookups for IonE)
392 if (eos%ionE) then
393 log_nh(ixo^s) = dlog10(nh(ixo^s))
394 end if
395
396 {do ix^db=ixomin^db,ixomax^db\}
397 inv_rho = 1.d0/w(ix^d,rho_)
398 ! Convert momentum to velocity
399 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
400 ! Calculate pressure
401 if(hd_energy) then
402 if (eos%ionE) then
403 ! Energy-consistent pressure from actual eint via EoS tables.
404 ! Cannot use stored Ne_/Te_ because they may be stale after
405 ! AMR prolongation/coarsening (nonlinear EoS breaks averaging).
406 eint_val = w(ix^d,e_) - half*w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+)
407 ! Floor eint to prevent unphysical values
408 if (eos%method /= 'analytic') then
409 eint_val = max(eint_val, nh(ix^d) * 10.0d0**eos%T%var2_min)
410 end if
411 eint_val = max(eint_val, smalldouble)
412 if (eint_val * inv_rho > eos%eint_rho_FI_threshold) then
413 ! FI bypass: p = (gamma-1)*(eint - eion*nH)
414 w(ix^d,p_) = eos%gamma_minus_1 &
415 * (eint_val - eos%eion_per_nH * nh(ix^d))
416 else
417 ! Ionisation zone: single p/nH lookup
418 eint_in = dlog10(eint_val) - log_nh(ix^d)
419 w(ix^d,p_) = nh(ix^d) &
420 * p_nh_from_eint(log_nh(ix^d), eint_in)
421 end if
422 else
423 w(ix^d,p_)=(eos%gamma_minus_1)*(w(ix^d,e_)&
424 -half*w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+))
425 end if
426 end if
427 {end do\}
428
429 ! Convert dust momentum to dust velocity
430 if (hd_dust) then
431 call dust_to_primitive(ixi^l, ixo^l, w, x)
432 end if
433
435
436 end subroutine hd_to_primitive_lte
437
438 !> Calculate thermal pressure within ixO^L.
439 !> For energy runs delegates to eos%get_thermal_pressure; for no-energy
440 !> (isothermal) uses the adiabatic relation p = adiab * rho^gamma.
441 subroutine hd_get_pthermal(w, x, ixI^L, ixO^L, pth)
443 use mod_physics, only: phys_get_ei
446
447 integer, intent(in) :: ixi^l, ixo^l
448 double precision, intent(in) :: w(ixi^s, 1:nw)
449 double precision, intent(in) :: x(ixi^s, 1:ndim)
450 double precision, intent(out):: pth(ixi^s)
451 integer :: iw, ix^d
452 double precision :: nh(ixi^s), ei(ixi^s), tpi, rpi
453
454 if (hd_energy) then
455 if (eos%eos_type == 'LTE') then
456 ! LTE: p = nH * (1 + He + ne/nH) * T from stored state
457 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
458 pth(ixo^s) = nh(ixo^s) * (1.0d0 + eos%He_abundance &
459 + (w(ixo^s,iw_ne) / nh(ixo^s))) * w(ixo^s,iw_te)
460 else if (eos%eos_type == 'PI' .and. eos%ionE) then
461 ! PI energy: invert eint (= e - KE, via phys_get_ei) -> p with
462 ! the ionisation backend (eint carries the ionisation energy).
463 ei(ixo^s) = phys_get_ei(w, ixi^l, ixo^l)
464 {do ix^db=ixomin^db,ixomax^db\}
465 call state_from_eint_pi(w(ix^d,rho_), ei(ix^d), &
466 tpi, pth(ix^d), rpi)
467 {end do\}
468 else
469 ! FI: p = (gamma-1) * (e - KE)
470 pth(ixo^s) = eos%gamma_minus_1 * phys_get_ei(w, ixi^l, ixo^l)
471 end if
472 else
473 if (.not. associated(usr_set_pthermal)) then
474 pth(ixo^s) = hd_adiab * w(ixo^s, rho_)**eos%gamma
475 else
476 call usr_set_pthermal(w,x,ixi^l,ixo^l,pth)
477 end if
478 end if
479
480 if (fix_small_values) then
481 {do ix^db= ixo^lim^db\}
482 if(pth(ix^d)<small_pressure) then
483 pth(ix^d)=small_pressure
484 endif
485 {enddo^d&\}
486 else if (check_small_values) then
487 {do ix^db= ixo^lim^db\}
488 if(pth(ix^d)<small_pressure) then
489 write(*,*) "Error: small value of gas pressure",pth(ix^d),&
490 " encountered when call hd_get_pthermal"
491 write(*,*) "Iteration: ", it, " Time: ", global_time
492 write(*,*) "Location: ", x(ix^d,:)
493 write(*,*) "Cell number: ", ix^d
494 do iw=1,nw
495 write(*,*) trim(cons_wnames(iw)),": ",w(ix^d,iw)
496 end do
497 if(trace_small_values) write(*,*) dsqrt(pth(ix^d)-bigdouble)
498 write(*,*) "Saving status at the previous time step"
499 crash=.true.
500 end if
501 {enddo^d&\}
502 end if
503
504 end subroutine hd_get_pthermal
505
506 !> Sound speed squared for FI (fully ionized / constant gamma) EoS.
507 !> Expects w in primitive form: w(p_) = pressure, w(rho_) = density.
508 subroutine hd_get_csound2_fi(w, x, ixI^L, ixO^L, cs2)
510 integer, intent(in) :: ixi^l, ixo^l
511 double precision, intent(in) :: w(ixi^s, nw)
512 double precision, intent(in) :: x(ixi^s, 1:ndim)
513 double precision, intent(out) :: cs2(ixi^s)
514
515 timeeos0 = mpi_wtime()
516
517 cs2(ixo^s) = eos%gamma * w(ixo^s, p_) / w(ixo^s, rho_)
518
519 timeeos_csound = timeeos_csound + (mpi_wtime()-timeeos0)
520
521 end subroutine hd_get_csound2_fi
522
523 !> Sound speed squared for LTE+IonE EoS.
524 !> Delegates Gamma_1 computation to hd_get_gamma1_LTE, then cs2 = Gamma_1 * p/rho.
525 subroutine hd_get_csound2_lte(w, x, ixI^L, ixO^L, cs2)
527 integer, intent(in) :: ixi^l, ixo^l
528 double precision, intent(in) :: w(ixi^s, nw)
529 double precision, intent(in) :: x(ixi^s, 1:ndim)
530 double precision, intent(out) :: cs2(ixi^s)
531 integer :: ix^d
532
533 timeeos0 = mpi_wtime()
534
535 call hd_get_gamma1_lte(w, x, ixi^l, ixo^l, cs2)
536 {do ix^db=ixomin^db,ixomax^db\}
537 cs2(ix^d) = cs2(ix^d) * w(ix^d, p_) / w(ix^d, rho_)
538 {end do\}
539
540 timeeos_csound = timeeos_csound + (mpi_wtime()-timeeos0)
541
542 end subroutine hd_get_csound2_lte
543
544 !> PI energy-mode routines (eos_type='PI', ionE=.true.)
545 !> Mirror the HD FI/LTE family; the eint<->p relation is delegated to the
546 !> portable scalar backend (mod_eos_PI). Same eq_state_units /
547 !> RR=1 normalisation as FI. Origin (total-energy) only, as in HD. eint
548 !> carries the ionisation-energy term, so p=(gamma-1)*eint no longer holds.
549
550 !> Primitive pressure -> total energy via backend (m_ is velocity here).
551 subroutine p_to_e_pi(ixI^L, ixO^L, w, x)
553 integer, intent(in) :: ixi^l, ixo^l
554 double precision, intent(inout) :: w(ixi^s, nw)
555 double precision, intent(in) :: x(ixi^s, 1:ndim)
556 double precision :: eint
557 integer :: ix^d
558
559 {do ix^db=ixomin^db,ixomax^db\}
560 if (hd_energy) then
561 call eint_from_rho_p_pi(w(ix^d,rho_), w(ix^d,p_), eint)
562 w(ix^d,e_) = eint + half*(^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)
563 end if
564 {end do\}
565 end subroutine p_to_e_pi
566
567 !> Primitive -> conserved (PI energy)
568 subroutine hd_to_conserved_pi(ixI^L, ixO^L, w, x)
570 use mod_dust, only: dust_to_conserved
571 integer, intent(in) :: ixi^l, ixo^l
572 double precision, intent(inout) :: w(ixi^s, nw)
573 double precision, intent(in) :: x(ixi^s, 1:ndim)
574
575 timeeos0 = mpi_wtime()
576 call p_to_e_pi(ixi^l, ixo^l, w, x)
577 ! Convert velocity to momentum
578 ^c&w(ixo^s,m^c_)=w(ixo^s,rho_)*w(ixo^s,m^c_)\
579 if (hd_dust) call dust_to_conserved(ixi^l, ixo^l, w, x)
581 end subroutine hd_to_conserved_pi
582
583 !> Conserved -> primitive (PI energy): KE removed -> eint, backend -> p.
584 subroutine hd_to_primitive_pi(ixI^L, ixO^L, w, x)
586 use mod_dust, only: dust_to_primitive
587 integer, intent(in) :: ixi^l, ixo^l
588 double precision, intent(inout) :: w(ixi^s, nw)
589 double precision, intent(in) :: x(ixi^s, 1:ndim)
590 double precision :: inv_rho, eint_val, t, rfac
591 integer :: ix^d
592
593 timeeos0 = mpi_wtime()
594
595 if (fix_small_values) then
596 call hd_handle_small_values(.false., w, x, ixi^l, ixo^l, &
597 'hd_to_primitive_PI')
598 end if
599
600 {do ix^db=ixomin^db,ixomax^db\}
601 inv_rho = 1.d0/w(ix^d,rho_)
602 ! Convert momentum to velocity
603 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
604 if (hd_energy) then
605 eint_val = w(ix^d,e_) &
606 - half*w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+)
607 eint_val = max(eint_val, smalldouble)
608 call state_from_eint_pi(w(ix^d,rho_), eint_val, &
609 t, w(ix^d,p_), rfac)
610 end if
611 {end do\}
612
613 if (hd_dust) call dust_to_primitive(ixi^l, ixo^l, w, x)
615 end subroutine hd_to_primitive_pi
616
617 !> Adiabatic sound speed squared from primitive (rho, p).
618
619 !> Effective Gamma1 = cs2 * rho / p.
620
621 !> PI energy mode: refresh Te_ from gas internal energy via the backend
622 !> eint->T inversion. Uses HD's Te_ index (PI registers Te via
623 !> var_set_auxvar, so the generic iw_te is unset). phys_get_ei supplies
624 !> eint (KE removed).
625
626 !> PI no-energy R-factor from the stored Te_ (HD's index). Mirrors MHD's
627 !> Rfactor_from_temperature_ionization. The generic mod_eos version uses
628 !> the global iw_te, which PI leaves unset (var_set_auxvar) -- so HD needs
629 !> its own Te_-addressed routine, exactly as MHD does.
630
631 !> PI no-energy Te_ update (pth/(rho*R) with lagged iz_H from wCT(Te_)).
632 !> HD's Te_-addressed mirror of MHD's mhd_update_temperature.
633
634 !> Prominence (T,p) R-factor: recompute from (rho, pth) via the module's
635 !> prominence inversion (no-energy only -> pth=(gamma-1)*eint, R-indep).
636
637 !> Prominence (T,p) Te_ update from (rho, pth).
638
639 !> Return effective adiabatic index for LTE+IonE EoS.
640 !> Dispatches on gamma1_method and eos%method.
641 subroutine hd_get_gamma1_lte(w, x, ixI^L, ixO^L, gamma1)
643 integer, intent(in) :: ixi^l, ixo^l
644 double precision, intent(in) :: w(ixi^s, nw)
645 double precision, intent(in) :: x(ixi^s, 1:ndim)
646 double precision, intent(out) :: gamma1(ixi^s)
647
648 double precision :: nh_val, p_over_rho
649 integer :: ix^d
650
651 if (eos%gamma1_method == 'constant') then
652 gamma1(ixo^s) = eos%gamma
653 return
654 end if
655
656 {do ix^db=ixomin^db,ixomax^db\}
657 p_over_rho = w(ix^d, p_) / w(ix^d, rho_)
658 if (p_over_rho > eos%p_rho_FI_threshold) then
659 gamma1(ix^d) = eos%gamma
660 else
661 nh_val = w(ix^d, rho_) / eos%nH2rhoFactor
662 if (eos%method == 'analytic') then
663 if (iw_te > 0 .and. w(ix^d,iw_te) > 0.0d0) then
664 gamma1(ix^d) = saha_gamma1_from_nh_t(nh_val, w(ix^d,iw_te))
665 else
666 gamma1(ix^d) = eos%gamma
667 end if
668 else
669 gamma1(ix^d) = gamma1_from_nh_p(dlog10(nh_val), &
670 dlog10(w(ix^d, p_) / nh_val))
671 end if
672 end if
673 {end do\}
674
675 end subroutine hd_get_gamma1_lte
676
677 !> Rfactor = p/(rho*T) for constant ionisation degree (FI/PI no-energy).
678 !> Stays in the seam: RR is the physics module's gas-constant factor, not
679 !> visible to mod_eos. Rfactor_from_LTE lives in mod_eos (no RR).
680 subroutine rfactor_from_constant_ionization(w,x,ixI^L,ixO^L,Rfactor)
682 integer, intent(in) :: ixi^l, ixo^l
683 double precision, intent(in) :: w(ixi^s,1:nw)
684 double precision, intent(in) :: x(ixi^s,1:ndim)
685 double precision, intent(out):: rfactor(ixi^s)
686
687 rfactor(ixo^s)=rr
688
689 end subroutine rfactor_from_constant_ionization
690
691 !> Convert conserved (rho, rho*v, E) to prolong form (rho, v, T).
692 !> T is stored in the e_ slot. Interpolation in this space avoids
693 !> Jensen's inequality across the ionisation plateau.
694 subroutine hd_to_prolong_lte(ixI^L, ixO^L, w, x)
695 integer, intent(in) :: ixi^l, ixo^l
696 double precision, intent(inout) :: w(ixi^s, nw)
697 double precision, intent(in) :: x(ixi^s, 1:ndim)
698
699 double precision :: inv_rho, eint_val, nh_val, log_nh, t_loc, y_loc
700 integer :: ix^d
701
702 {do ix^db=ixomin^db,ixomax^db\}
703 inv_rho = 1.d0 / w(ix^d, rho_)
704 nh_val = w(ix^d, rho_) / eos%nH2rhoFactor
705 log_nh = dlog10(nh_val)
706
707 ! Convert momentum to velocity
708 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
709
710 ! Compute eint = E - 0.5*rho*v^2
711 eint_val = w(ix^d, e_) &
712 - half * w(ix^d, rho_) * (^c&w(ix^d,m^c_)**2+)
713 ! Floor eint to prevent unphysical values
714 if (eos%method /= 'analytic') then
715 eint_val = max(eint_val, nh_val * 10.0d0**eos%T%var2_min)
716 end if
717 eint_val = max(eint_val, smalldouble)
718
719 ! Convert eint to T via EoS
720 if (eint_val * inv_rho > eos%eint_rho_FI_threshold) then
721 ! FI bypass: T = (gamma-1)*(eint - eion*nH) / (nH * n_per_nH_FI)
722 w(ix^d, e_) = eos%gamma_minus_1 &
723 * (eint_val - eos%eion_per_nH * nh_val) &
724 / (nh_val * eos%n_per_nH_FI)
725 else if (eos%method == 'analytic') then
726 ! Analytical Saha: solve for T from eint
727 call saha_t_from_nh_eint(nh_val, &
728 eint_val / nh_val, t_loc, y_loc)
729 w(ix^d, e_) = t_loc
730 else
731 ! Ionisation zone: T from table
732 w(ix^d, e_) = t_from_nh_eint(log_nh, &
733 dlog10(eint_val) - log_nh)
734 end if
735 {end do\}
736
737 end subroutine hd_to_prolong_lte
738
739 !> Convert prolong form (rho, v, T) back to conserved (rho, rho*v, E).
740 !> T is read from the e_ slot. Uses eint_nH_from_T for back-conversion.
741 subroutine hd_from_prolong_lte(ixI^L, ixO^L, w, x)
742 integer, intent(in) :: ixi^l, ixo^l
743 double precision, intent(inout) :: w(ixi^s, nw)
744 double precision, intent(in) :: x(ixi^s, 1:ndim)
745
746 double precision :: t_val, eint_val, t_fi, nh_val, log_nh, log_t_min
747 integer :: ix^d
748
749 t_fi = (eos%eint_rho_FI_threshold &
750 * eos%nH2rhoFactor - eos%eion_per_nH) &
751 * eos%gamma_minus_1 / eos%n_per_nH_FI
752
753 ! Floor for log_T when calling the (rho, T) inverse table.
754 ! Legacy 'tables' method populates eos%eint_from_T; entropy method
755 ! populates eos%eintT. Picking the wrong container leaves var2_min = 0
756 ! (uninitialised), which floors T at 10^0 = 1 code unit (= 10^6 K) —
757 ! that clobbers any cold cell going through AMR prolongation.
758 if (eos%method == 'entropy') then
759 log_t_min = eos%eintT%var2_min
760 else
761 log_t_min = eos%eint_from_T%var2_min
762 end if
763
764 {do ix^db=ixomin^db,ixomax^db\}
765 t_val = w(ix^d, e_) ! T stored in e_ slot
766 nh_val = w(ix^d, rho_) / eos%nH2rhoFactor
767 log_nh = dlog10(nh_val)
768
769 if (t_val > t_fi) then
770 ! FI: eint = nH*(n_per_nH*T/(gamma-1) + eion)
771 eint_val = nh_val &
772 * (eos%n_per_nH_FI * t_val * eos%inv_gamma_minus_1 &
773 + eos%eion_per_nH)
774 else if (eos%method == 'analytic') then
775 ! Analytical Saha: eint from T directly
776 eint_val = saha_eint_from_nh_t(nh_val, t_val) * nh_val
777 else
778 ! Ionisation zone: eint/nH from T table
779 eint_val = eint_nh_from_t(log_nh, &
780 dlog10(max(t_val, 10.0d0**log_t_min))) &
781 * nh_val
782 end if
783
784 ! E = eint + 0.5*rho*v^2
785 w(ix^d, e_) = eint_val &
786 + half * w(ix^d, rho_) * (^c&w(ix^d,m^c_)**2+)
787 ! Convert velocity to momentum
788 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)\
789 {end do\}
790
791 end subroutine hd_from_prolong_lte
792
793end module mod_hd_eos
794!> Needs a line after to pass the preprocesor
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
Module for including dust species, which interact with the gas through a drag force.
Definition mod_dust.t:3
subroutine, public dust_to_primitive(ixil, ixol, w, x)
Definition mod_dust.t:229
subroutine, public dust_to_conserved(ixil, ixol, w, x)
Definition mod_dust.t:209
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...
Entropy-method LTE EoS: every query is ONE bicubic Hermite evaluation.
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,...
Analytic H-only Saha EoS (eos_method == 'analytic').
double precision function, public saha_gamma1_from_nh_t(nh_code, t_code)
Look up Gamma1 from the analytical 2D table (nH, T axes in code units). For use when eosmethod == 'an...
subroutine, public saha_t_from_nh_eint(nh_code, eint_nh_code, t_out, y_out)
Temperature inversion: given (nH, eint/nH) in CODE UNITS, find T in CODE UNITS, by bisection (guarant...
subroutine, public saha_state_from_nh_p(nh_code, p_code, t_out, y_out, eint_nh_out)
Given (nH, p) in CODE UNITS, find T and y by solving p = nH*(1+y(T))*T_code. Uses bisection on T....
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 ...
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 ...
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...
PI (partial-ionisation, eos_type='PI') arm of the eos% family.
Definition mod_eos_PI.t:27
double precision function, dimension(log_nh, log_p_nh), public p2eint_pi(log_nh, log_p_nh)
eint/p factor from pressure per H: maps p -> eint = p * (this).
Definition mod_eos_PI.t:336
double precision function, dimension(log_nh, log_t), public eint_from_t_pi(log_nh, log_t)
Internal energy per H from temperature: eint/nH(T).
Definition mod_eos_PI.t:329
double precision function, dimension(log_nh, log_eint_nh), public y_from_eint_pi(log_nh, log_eint_nh)
Electron-to-hydrogen ratio ne/nH from internal energy per H. ne/nH = iz_H + A_He*iz_He*(1+iz_He) (mat...
Definition mod_eos_PI.t:354
subroutine, public get_gamma1_pi(w, x, ixil, ixol, gamma1)
Effective Gamma1 = cs2 * rho / p for the same primitive state.
Definition mod_eos_PI.t:146
subroutine, public eint_from_rho_p_pi(rho, p, eint)
Primitive pressure -> GAS internal energy (prim -> conserved direction): invert (rho,...
Definition mod_eos_PI.t:299
double precision function, dimension(log_nh, log_eint_nh), public t_from_eint_pi(log_nh, log_eint_nh)
Temperature from internal energy per H.
Definition mod_eos_PI.t:345
subroutine, public state_from_eint_pi(rho, eint, t, p, rfactor, iz_h, iz_he)
Conserved gas internal energy -> temperature, thermal pressure, R. Inverse of p_eint_from_rho_T_PI....
Definition mod_eos_PI.t:274
Equation of state for AMRVAC, handled through a single eos_container object.
Definition mod_eos.t:30
subroutine, public eos_finalise()
Phase 'commit' (after units are known): finalise the dispatch for the loaded physics – wire the metho...
Definition mod_eos.t:265
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
logical fix_small_values
fix small values with average or replace methods
HD <-> EoS seam: binds the eos% authority into hydrodynamics.
Definition mod_hd_eos.t:15
subroutine, public hd_get_pthermal(w, x, ixil, ixol, pth)
Calculate thermal pressure within ixO^L. For energy runs delegates to eosget_thermal_pressure; for no...
Definition mod_hd_eos.t:442
procedure(sub_convert), pointer, public hd_to_conserved
Definition mod_hd_eos.t:36
subroutine, public hd_link_eos()
Link the appropriate EOS conversion routines based on the selected EoS type.
Definition mod_hd_eos.t:44
procedure(sub_convert), pointer, public hd_to_primitive
Definition mod_hd_eos.t:35
Hydrodynamics physics module.
Definition mod_hd_phys.t:2
integer, public, protected m
Definition mod_hd_phys.t:70
logical, public, protected hd_energy
Whether an energy equation is used.
Definition mod_hd_phys.t:14
logical, public, protected hd_dust
Whether dust is added.
Definition mod_hd_phys.t:32
integer, public, protected e_
Index of the energy density (-1 if not present)
Definition mod_hd_phys.t:76
double precision, public, protected rr
type(tc_fluid), allocatable, public tc_fl
Definition mod_hd_phys.t:24
integer, public, protected c
Indices of the momentum density for the form of better vectorization.
Definition mod_hd_phys.t:70
double precision, public hd_adiab
gamma is set in &eos_list and accessed via eosgamma
integer, public, protected rho_
Whether plasma is partially ionized.
Definition mod_hd_phys.t:64
subroutine, public hd_handle_small_values(primitive, w, x, ixil, ixol, subname)
type(fld_fluid), allocatable, public fld_fl
Radiation fluid object (gas-EoS callbacks for FLD), wired in hd_link_eos.
Definition mod_hd_phys.t:41
integer, public, protected c_
Definition mod_hd_phys.t:70
type(rc_fluid), allocatable, public rc_fl
Definition mod_hd_phys.t:29
integer, public, protected p_
Index of the gas pressure (-1 if not present) should equal e_.
Definition mod_hd_phys.t:79
This module defines the procedures of a physics module. It contains function pointers for the various...
Definition mod_physics.t:4
procedure(sub_check_params), pointer phys_bind_eos_to_source
Definition mod_physics.t:53
procedure(sub_convert), pointer phys_to_primitive
Definition mod_physics.t:52
procedure(sub_get_pthermal), pointer phys_get_gamma1
Definition mod_physics.t:78
procedure(sub_get_pthermal), pointer phys_get_pthermal
Definition mod_physics.t:77
procedure(sub_get_ei), pointer phys_get_ei
Definition mod_physics.t:68
procedure(sub_convert), pointer phys_to_prolong
Definition mod_physics.t:55
procedure(sub_convert), pointer phys_to_conserved
Definition mod_physics.t:51
procedure(sub_convert), pointer phys_from_prolong
Definition mod_physics.t:56
procedure(sub_get_rho), pointer phys_get_rho
Definition mod_physics.t:70
module radiative cooling – add optically thin radiative cooling
subroutine build_y_mod_table(fl)
===================================================================
Module for handling problematic values in simulations, such as negative pressures.
logical, public trace_small_values
trace small values in the source file using traceback flag of compiler
double precision timeeos0
Definition mod_timing.t:10
double precision timeeos_conv
Definition mod_timing.t:13
double precision timeeos_csound
Definition mod_timing.t:12
Module with all the methods that users can customize in AMRVAC.
procedure(rfactor), pointer usr_rfactor
procedure(hd_pthermal), pointer usr_set_pthermal