MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_mhd_eos.t
Go to the documentation of this file.
1!=============================================================================
2!> MHD <-> EoS seam: binds the eos% authority into magnetohydrodynamics.
3!>
4!> mhd_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) and per energy
7!> formulation (origin / internal_e / hydrodynamic_e / semirelativistic).
8!> bind_eos_to_source wires the thermal-conduction / radiative-cooling / thermal-
9!> emission / FLD fluid-port callbacks from eos%.
10!>
11!> Holds the MHD block routines that wrap the shared thermodynamics with MHD's
12!> mechanical-energy bookkeeping (kinetic + MAGNETIC energy): the FI/LTE/PI
13!> conversions, p_to_e, the pthermal variants, csound2/gamma1, and the PI energy
14!> + prominence R-factor / temperature kernels.
15!=============================================================================
18 use mod_physics
19 use mod_eos
20 !> The mode-specific scalar kernels are reached from their sub-modules, not the
21 !> mod_eos facade (which no longer re-exports them). Each kernel mpistops if
22 !> called under the wrong eos_type/method.
23 use mod_eos_lte
25 use mod_eos_pi
27 use mod_mhd_phys
28 use mod_timing
30
31 use mod_comm_lib, only: mpistop
32
33 implicit none
34 private
35
36 !> Thermal pressure pointer: set by mhd_link_eos based on energy formulation.
37 !> Internal to mod_mhd_eos — external callers use eos%get_thermal_pressure.
38 procedure(sub_get_pthermal), pointer, public :: mhd_get_pthermal => null()
39
40 !> Temperature pointer: set by mhd_link_eos based on EoS type and energy formulation.
41 procedure(sub_get_pthermal), pointer, public :: mhd_get_temperature => null()
42 !> use habitual name of converting to primitive
43 procedure(sub_convert), pointer, public :: mhd_to_primitive => null()
44 !> use habitual name of converting to conserved
45 procedure(sub_convert), pointer, public :: mhd_to_conserved => null()
46
47 public :: mhd_link_eos
48contains
49
50 !> Link the appropriate EOS conversion routines based on the selected EoS type
51 subroutine mhd_link_eos()
53
54 !> Primitive <-> Conserved routing
55 ! PI (partial approximations) takes the FI conversions as its BASE: in
56 ! no-energy mode this is exact (e_int = p/(gamma-1); the variable R-factor
57 ! enters only via eos%get_Rfactor, below). In energy mode (eos%ionE) the
58 ! PI override block at the end of this routine repoints to_conserved/
59 ! to_primitive (and p_to_e/pthermal/csound) at the PI-energy variants, so
60 ! the FI assignment here is just a harmless base that gets replaced.
61 if (eos%eos_type == 'FI' .or. eos%eos_type == 'PI') then
62 ! PI takes its electron count from a variable R factor evaluated on
63 ! the rho slot, which under splitting holds only the perturbation.
64 if (eos%eos_type == 'PI' .and. has_equi_rho_and_p) &
65 call mpistop('PI EoS not supported with equilibrium splitting')
66 if(mhd_hydrodynamic_e) then
67 if(eos%ionE) call mpistop('PI energy EoS not supported with mhd_hydrodynamic_e')
68 eos%to_primitive => mhd_to_primitive_hde
69 eos%to_conserved => mhd_to_conserved_hde
70 phys_get_pthermal => mhd_get_pthermal_hde
71 mhd_get_pthermal => mhd_get_pthermal_hde
72 else if(mhd_semirelativistic) then
73 if(mhd_energy) then
74 if(eos%ionE) then
75 eos%to_primitive => mhd_to_primitive_semirelati_pi
76 eos%to_conserved => mhd_to_conserved_semirelati_pi
77 if (mhd_internal_e) then
78 eos%p_to_e => mhd_p_to_eint_pi
79 phys_get_pthermal => mhd_get_pthermal_inte_pi
80 mhd_get_pthermal => mhd_get_pthermal_inte_pi
81 else
82 eos%p_to_e => mhd_p_to_e_pi
83 phys_get_pthermal => mhd_get_pthermal_semirelati_pi
84 mhd_get_pthermal => mhd_get_pthermal_semirelati_pi
85 end if
86 else
87 eos%to_primitive => mhd_to_primitive_semirelati
88 eos%to_conserved => mhd_to_conserved_semirelati
89 if (mhd_internal_e) then
90 phys_get_pthermal => mhd_get_pthermal_inte
91 mhd_get_pthermal => mhd_get_pthermal_inte
92 else
93 phys_get_pthermal => mhd_get_pthermal_semirelati
94 mhd_get_pthermal => mhd_get_pthermal_semirelati
95 end if
96 end if
97 else
98 eos%to_primitive => mhd_to_primitive_semirelati_noe
99 eos%to_conserved => mhd_to_conserved_semirelati_noe
100 phys_get_pthermal => mhd_get_pthermal_noe
101 mhd_get_pthermal => mhd_get_pthermal_noe
102 end if
103 else
104 if(has_equi_rho_and_p) then
105 eos%to_primitive => mhd_to_primitive_split_rho
106 eos%to_conserved => mhd_to_conserved_split_rho
107 else if(mhd_internal_e) then
108 if(eos%ionE) then
109 eos%to_conserved => mhd_to_conserved_inte_pi
110 eos%to_primitive => mhd_to_primitive_inte_pi
111 eos%p_to_e => mhd_p_to_eint_pi
112 phys_get_pthermal => mhd_get_pthermal_inte_pi
113 mhd_get_pthermal => mhd_get_pthermal_inte_pi
114 else
115 eos%to_primitive => mhd_to_primitive_inte
116 eos%to_conserved => mhd_to_conserved_inte
117 phys_get_pthermal => mhd_get_pthermal_inte
118 mhd_get_pthermal => mhd_get_pthermal_inte
119 end if
120 else if(mhd_energy) then
121 if(eos%ionE) then
122 eos%to_conserved => mhd_to_conserved_origin_pi
123 eos%to_primitive => mhd_to_primitive_origin_pi
124 eos%p_to_e => mhd_p_to_e_pi
125 phys_get_pthermal => mhd_get_pthermal_origin_pi
126 mhd_get_pthermal => mhd_get_pthermal_origin_pi
127 else
128 eos%to_primitive => mhd_to_primitive_origin
129 eos%to_conserved => mhd_to_conserved_origin
130 phys_get_pthermal => mhd_get_pthermal_origin
131 mhd_get_pthermal => mhd_get_pthermal_origin
132 end if
133 else
134 eos%to_primitive => mhd_to_primitive_origin_noe
135 eos%to_conserved => mhd_to_conserved_origin_noe
136 phys_get_pthermal => mhd_get_pthermal_noe
137 mhd_get_pthermal => mhd_get_pthermal_noe
138 end if
139 end if
140 else if (eos%eos_type == 'LTE') then
141 ! Guard unsupported combinations
142 if (mhd_hydrodynamic_e) &
143 call mpistop('LTE EoS not supported with mhd_hydrodynamic_e')
145 call mpistop('LTE EoS not supported with mhd_semirelativistic')
146 if (has_equi_rho_and_p) &
147 call mpistop('LTE EoS not supported with equilibrium splitting')
148 if (.not. mhd_energy) &
149 call mpistop('LTE EoS requires mhd_energy=.true.')
150 ! Route LTE conversion based on energy formulation
151 if (mhd_internal_e) then
152 eos%to_primitive => mhd_to_primitive_inte_lte
153 eos%to_conserved => mhd_to_conserved_inte_lte
154 else
155 eos%to_primitive => mhd_to_primitive_origin_lte
156 eos%to_conserved => mhd_to_conserved_origin_lte
157 end if
158 else
159 call mpistop('Error: Unknown MHD EOS type: ' // trim(eos%eos_type))
160 end if
161
162 phys_to_primitive => eos%to_primitive
163 phys_to_conserved => eos%to_conserved
164 phys_bind_eos_to_source => bind_eos_to_source
165
166 !> p_to_e (pressure -> total energy for origin, or eint for inte)
167 if (mhd_internal_e) then
168 eos%p_to_e => mhd_p_to_eint
169 else
170 eos%p_to_e => mhd_p_to_e
171 end if
172
173 !> Sound speed and Gamma1
174 if (eos%eos_type == 'LTE' .and. eos%ionE) then
175 eos%get_csound2 => mhd_get_csound2_lte
176 phys_get_gamma1 => mhd_get_gamma1_lte
177 else if (eos%eos_type == 'PI' .and. eos%ionE) then
178 eos%get_csound2 => mhd_get_csound2_fi
180 else
181 eos%get_csound2 => mhd_get_csound2_fi
182 phys_get_gamma1 => get_gamma1_fi
183 end if
184
185 !> Prolongation
186 if (eos%eos_type == 'LTE' .and. eos%ionE) then
187 phys_to_prolong => mhd_to_prolong_lte
188 phys_from_prolong => mhd_from_prolong_lte
189 end if
190
191 !> Rfactor: only the FI case is physics-dependent (the usr_Rfactor user
192 !> hook). The LTE and PI bindings are pure eos% routines, so they are set
193 !> in eos_finalise_{LTE,PI} (which run after this and before
194 !> bind_eos_to_source, so they win) -- keeping those targets private.
195 if(associated(usr_rfactor)) then
196 eos%get_Rfactor => usr_rfactor
197 else
198 eos%get_Rfactor => rfactor_from_constant_ionization
199 end if
200
201 ! For LTE, override with version that delegates to eos%get_thermal_pressure
202 if (eos%eos_type == 'LTE') then
203 phys_get_pthermal => mhd_get_pthermal_lte
204 mhd_get_pthermal => mhd_get_pthermal_lte
205 end if
206 ! Single entry point: all callers use eos%get_thermal_pressure
207 eos%get_thermal_pressure => mhd_get_pthermal
208
209 !> Temperature routing
210 if(eos%eos_type == 'PI' .or. eos%eos_type == 'LTE') then
211 mhd_get_temperature => mhd_get_temperature_from_te
212 else
213 if(mhd_internal_e) then
214 if(has_equi_rho_and_p) then
215 mhd_get_temperature => mhd_get_temperature_from_eint_with_equi
216 else
217 mhd_get_temperature => mhd_get_temperature_from_eint
218 end if
219 else
220 if(has_equi_rho_and_p) then
221 mhd_get_temperature => mhd_get_temperature_from_etot_with_equi
222 else
223 mhd_get_temperature => mhd_get_temperature_from_etot
224 end if
225 end if
226 end if
227
228 !> Internal energy extraction
229 if(mhd_internal_e) then
230 phys_get_ei => mhd_get_ei_inte
231 else if(mhd_energy) then
232 if(mhd_semirelativistic) then
233 phys_get_ei => mhd_get_ei_semirelati
234 else
235 phys_get_ei => mhd_get_ei_origin
236 end if
237 end if
238
239 ! phys_e_to_ei / phys_ei_to_e assigned in mhd_phys_init (mod_mhd_phys.t)
240 mhd_to_primitive => eos%to_primitive
241 mhd_to_conserved => eos%to_conserved
242 phys_to_primitive => eos%to_primitive
243 phys_to_conserved => eos%to_conserved
244 eos%get_thermal_pressure => mhd_get_pthermal
245
246 end subroutine mhd_link_eos
247
248 !> Called by eos_finalise via phys_bind_eos_to_source to link
249 !> TC and RC modules to EoS-aware function pointers.
250 subroutine bind_eos_to_source()
251
252 ! Override eos%get_temperature_from_{etot,eint} per (eos_type, internal_e, equi).
253 ! Runs AFTER eos_finalise's unconditional generic-helper assignment.
254 ! Two semantically distinct hooks:
255 ! - from_etot: caller's w(:,e_) is total energy; subtract KE+ME before T.
256 ! - from_eint: caller's w(:,e_) is gas internal energy; compute T directly.
257 ! In mhd_internal_e mode w(:,e_) IS already eint, so the etot hook is bound
258 ! to the same routine as the eint hook (no subtraction). Required because
259 ! eos_finalise's generic helper calls phys_e_to_ei, which is left unbound
260 ! by mhd_phys_init when mhd_internal_e=.true.
261 if (eos%eos_type == 'LTE') then
262 if (mhd_internal_e) then
263 ! w(:,e_) is eint; both hooks do the LTE table lookup directly.
264 eos%get_temperature_from_etot => eos%get_temperature_from_eint
265 else
266 ! Total-energy: subtract KE+ME (via phys_e_to_ei) then LTE table lookup.
267 eos%get_temperature_from_etot => mhd_get_temperature_from_etot_lte
268 end if
269 else ! FI
270 if (mhd_internal_e) then
271 ! w(:,e_) is eint; both hooks share the eint variant.
272 if (has_equi_rho_and_p) then
273 eos%get_temperature_from_etot => mhd_get_temperature_from_eint_with_equi
274 eos%get_temperature_from_eint => mhd_get_temperature_from_eint_with_equi
275 else
276 eos%get_temperature_from_etot => mhd_get_temperature_from_eint
277 eos%get_temperature_from_eint => mhd_get_temperature_from_eint
278 end if
279 else
280 ! Total-energy: distinct etot/eint paths.
281 if (has_equi_rho_and_p) then
282 eos%get_temperature_from_etot => mhd_get_temperature_from_etot_with_equi
283 eos%get_temperature_from_eint => mhd_get_temperature_from_eint_with_equi
284 else
285 eos%get_temperature_from_etot => mhd_get_temperature_from_etot
286 eos%get_temperature_from_eint => mhd_get_temperature_from_eint
287 end if
288 end if
289 end if
290
291 if (allocated(tc_fl)) then
292 tc_fl%get_temperature_from_conserved => eos%get_temperature_from_etot
293 if (eos%eos_type == 'LTE' .and. eos%ionE) then
294 tc_fl%get_temperature_from_eint => get_temperature_from_eint_fast_lte
295 else
296 tc_fl%get_temperature_from_eint => eos%get_temperature_from_eint
297 end if
298 tc_fl%get_rho => eos%get_rho
299 tc_fl%get_ne_nH => eos%get_ne_nH
300 tc_fl%get_var_Rfactor => eos%get_Rfactor
301 tc_fl%inv_gamma_minus_1 = eos%inv_gamma_minus_1
302 tc_fl%nH2rhoFactor = eos%nH2rhoFactor
303 tc_fl%log_T_floor = eos_get_log_t_floor()
304 tc_fl%eint_from_T => eint_nh_from_t
305 ! Equilibrium-specific pointers
307 tc_fl%subtract_equi = .true.
308 tc_fl%get_temperature_equi => mhd_get_temperature_equi
309 tc_fl%get_rho_equi => mhd_get_rho_equi
310 else
311 tc_fl%subtract_equi = .false.
312 end if
313 end if
314
315 if (allocated(rc_fl)) then
316 rc_fl%get_rho => eos%get_rho
317 rc_fl%get_pthermal => eos%get_thermal_pressure
318 rc_fl%get_var_Rfactor => eos%get_Rfactor
319 rc_fl%get_Te => eos%get_Te
320 rc_fl%get_ne_nH => eos%get_ne_nH
321 nullify(rc_fl%get_rho2_factor)
322 if(mhd_uawsom) rc_fl%get_rho2_factor => mhd_uawsom_rho2_factor
323 rc_fl%ionE = eos%ionE
324 rc_fl%method = eos%method
325 rc_fl%inv_gamma_minus_1 = eos%inv_gamma_minus_1
326 rc_fl%nH2rhoFactor = eos%nH2rhoFactor
327 rc_fl%eion_per_nH = eos%eion_per_nH
328 rc_fl%eint_from_T => eint_nh_from_t
329 rc_fl%p2eint => p2eint_from_nh_p
330 rc_fl%T_from_eint => t_from_nh_eint
331 rc_fl%y_from_eint => y_from_nh_eint
332 ! Equilibrium-specific pointers
334 rc_fl%subtract_equi = .true.
335 rc_fl%get_rho_equi => mhd_get_rho_equi
336 rc_fl%get_pthermal_equi => mhd_get_pe_equi
337 rc_fl%get_ne_nH_equi => mhd_get_ne_nh_equi
338 rc_fl%get_temperature_equi => mhd_get_temperature_equi
339 else
340 rc_fl%subtract_equi = .false.
341 end if
342 !> Build the variable-c_V Townsend Y_mod table now that all
343 !> EoS tables (eint_from_T, T, neOnH) are in code units.
344 !> build_Y_mod_table checks coolmethod=='exact' and .not.isPPL
345 !> internally and early-returns otherwise. Feed it the inverse-table nH
346 !> grid via the port so it never touches eos% directly. LTE only:
347 !> eos_get_eintT_grid reads the LTE eintT grid, and PI (T-only) cooling
348 !> runs the classical Townsend path (Y_mod-for-PI is a later refinement).
349 if (eos%ionE .and. eos%eos_type == 'LTE') then
350 call eos_get_eintt_grid(rc_fl%Y_mod_n_nH, &
351 rc_fl%Y_mod_lg_nH_min, rc_fl%Y_mod_lg_nH_max)
353 end if
354 end if
355
356 !> PI energy mode: the eint<->T scalar callbacks wired above are the
357 !> LTE-table lookups, which PI does not load. Repoint cooling/conduction
358 !> at the ionisation backend (rho=nH=1 per H). Classical Townsend; Y_mod
359 !> is not built for PI (guard above), so the classical branch is used.
360 if (eos%eos_type == 'PI' .and. eos%ionE) then
361 if (allocated(tc_fl)) tc_fl%eint_from_T => eint_from_t_pi
362 if (allocated(rc_fl)) then
363 rc_fl%eint_from_T => eint_from_t_pi
364 rc_fl%p2eint => p2eint_pi
365 rc_fl%T_from_eint => t_from_eint_pi
366 rc_fl%y_from_eint => y_from_eint_pi
367 end if
368 end if
369
370 if (allocated(te_fl_mhd)) then
371 te_fl_mhd%get_rho => eos%get_rho
372 te_fl_mhd%get_pthermal => eos%get_thermal_pressure
373 te_fl_mhd%get_var_Rfactor => eos%get_Rfactor
374 te_fl_mhd%get_ne_nH => eos%get_ne_nH
375 end if
376
377 if (allocated(fld_fl)) then
378 !> Radiation (FLD) fluid: gas-EoS callbacks. get_temperature_from_pressure
379 !> is the FI T=p/(R*rho) routine (the LTE variant is a later pass).
380 fld_fl%gamma = eos%gamma
381 fld_fl%get_tgas => eos%get_temperature_from_pressure
382 fld_fl%get_Rfactor => eos%get_Rfactor
383 end if
384
385 end subroutine bind_eos_to_source
386
387 !> FI conversion routines (signatures updated for convert_condition interface)
388
389 !> Transform primitive variables into conservative ones (origin, total energy)
390 subroutine mhd_to_conserved_origin(ixI^L,ixO^L,w,x)
392 integer, intent(in) :: ixi^l, ixo^l
393 double precision, intent(inout) :: w(ixi^s, nw)
394 double precision, intent(in) :: x(ixi^s, 1:ndim)
395
396 integer :: ix^d
397
398 {do ix^db=ixomin^db,ixomax^db\}
399 ! Calculate total energy from pressure, kinetic and magnetic energy
400 w(ix^d,e_)=w(ix^d,p_)*eos%inv_gamma_minus_1&
401 +half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
402 +(^c&w(ix^d,b^c_)**2+))
403 if(mhd_uawsom) w(ix^d,e_)=w(ix^d,e_)+&
404 w(ix^d,waplus_)+w(ix^d,waminus_)+w(ix^d,wkplus_)+w(ix^d,wkminus_)
405 ! Convert velocity to momentum
406 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)\
407 {end do\}
408
409 end subroutine mhd_to_conserved_origin
410
411 !> Transform primitive variables into conservative ones (no energy)
412 subroutine mhd_to_conserved_origin_noe(ixI^L,ixO^L,w,x)
414 integer, intent(in) :: ixi^l, ixo^l
415 double precision, intent(inout) :: w(ixi^s, nw)
416 double precision, intent(in) :: x(ixi^s, 1:ndim)
417
418 integer :: ix^d
419
420 {do ix^db=ixomin^db,ixomax^db\}
421 ! Convert velocity to momentum
422 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)\
423 {end do\}
424
425 end subroutine mhd_to_conserved_origin_noe
426
427 !> Transform primitive variables into conservative ones (hydrodynamic energy)
428 subroutine mhd_to_conserved_hde(ixI^L,ixO^L,w,x)
430 integer, intent(in) :: ixi^l, ixo^l
431 double precision, intent(inout) :: w(ixi^s, nw)
432 double precision, intent(in) :: x(ixi^s, 1:ndim)
433
434 integer :: ix^d
435
436 {do ix^db=ixomin^db,ixomax^db\}
437 ! Calculate total energy from pressure, kinetic and magnetic energy
438 w(ix^d,e_)=w(ix^d,p_)*eos%inv_gamma_minus_1&
439 +half*(^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)
440 ! Convert velocity to momentum
441 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)\
442 {end do\}
443
444 end subroutine mhd_to_conserved_hde
445
446 !> Transform primitive variables into conservative ones (internal energy)
447 subroutine mhd_to_conserved_inte(ixI^L,ixO^L,w,x)
449 integer, intent(in) :: ixi^l, ixo^l
450 double precision, intent(inout) :: w(ixi^s, nw)
451 double precision, intent(in) :: x(ixi^s, 1:ndim)
452
453 integer :: ix^d
454
455 {do ix^db=ixomin^db,ixomax^db\}
456 ! Calculate internal energy from pressure
457 w(ix^d,e_)=w(ix^d,p_)*eos%inv_gamma_minus_1
458 ! Convert velocity to momentum
459 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)\
460 {end do\}
461
462 end subroutine mhd_to_conserved_inte
463
464 !> Transform primitive variables into conservative ones (split rho)
465 subroutine mhd_to_conserved_split_rho(ixI^L,ixO^L,w,x)
467 integer, intent(in) :: ixi^l, ixo^l
468 double precision, intent(inout) :: w(ixi^s, nw)
469 double precision, intent(in) :: x(ixi^s, 1:ndim)
470
471 double precision :: rho
472 integer :: ix^d
473
474 {do ix^db=ixomin^db,ixomax^db\}
475 rho=w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i)
476 ! Calculate total energy from pressure, kinetic and magnetic energy
477 w(ix^d,e_)=w(ix^d,p_)*eos%inv_gamma_minus_1&
478 +half*((^c&w(ix^d,m^c_)**2+)*rho&
479 +(^c&w(ix^d,b^c_)**2+))
480 ! Convert velocity to momentum
481 ^c&w(ix^d,m^c_)=rho*w(ix^d,m^c_)\
482 {end do\}
483
484 end subroutine mhd_to_conserved_split_rho
485
486 !> Transform primitive variables into conservative ones (semirelativistic)
487 subroutine mhd_to_conserved_semirelati(ixI^L,ixO^L,w,x)
489 integer, intent(in) :: ixi^l, ixo^l
490 double precision, intent(inout) :: w(ixi^s, nw)
491 double precision, intent(in) :: x(ixi^s, 1:ndim)
492
493 double precision :: e(ixo^s,1:ndir), s(ixo^s,1:ndir)
494 integer :: ix^d
495
496 {do ix^db=ixomin^db,ixomax^db\}
497 {^ifthreec
498 e(ix^d,1)=w(ix^d,b2_)*w(ix^d,m3_)-w(ix^d,b3_)*w(ix^d,m2_)
499 e(ix^d,2)=w(ix^d,b3_)*w(ix^d,m1_)-w(ix^d,b1_)*w(ix^d,m3_)
500 e(ix^d,3)=w(ix^d,b1_)*w(ix^d,m2_)-w(ix^d,b2_)*w(ix^d,m1_)
501 s(ix^d,1)=e(ix^d,2)*w(ix^d,b3_)-e(ix^d,3)*w(ix^d,b2_)
502 s(ix^d,2)=e(ix^d,3)*w(ix^d,b1_)-e(ix^d,1)*w(ix^d,b3_)
503 s(ix^d,3)=e(ix^d,1)*w(ix^d,b2_)-e(ix^d,2)*w(ix^d,b1_)
504 }
505 {^iftwoc
506 e(ix^d,1)=zero
507 ! switch 3 with 2 to add 3 when ^C from 1 to 2
508 e(ix^d,2)=w(ix^d,b1_)*w(ix^d,m2_)-w(ix^d,b2_)*w(ix^d,m1_)
509 s(ix^d,1)=-e(ix^d,2)*w(ix^d,b2_)
510 s(ix^d,2)=e(ix^d,2)*w(ix^d,b1_)
511 }
512 {^ifonec
513 e(ix^d,1)=zero
514 s(ix^d,1)=zero
515 }
516 if(mhd_internal_e) then
517 ! internal energy
518 w(ix^d,e_)=w(ix^d,p_)*eos%inv_gamma_minus_1
519 else
520 ! equation (9)
521 ! Calculate total energy from internal, kinetic and magnetic energy
522 w(ix^d,e_)=w(ix^d,p_)*eos%inv_gamma_minus_1&
523 +half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
524 +(^c&w(ix^d,b^c_)**2+)&
525 +(^c&e(ix^d,^c)**2+)*eos%inv_squared_c)
526 end if
527
528 ! Convert velocity to momentum, equation (9)
529 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)+s(ix^d,^c)*eos%inv_squared_c\
530
531 {end do\}
532
533 end subroutine mhd_to_conserved_semirelati
534
535 !> Transform primitive variables into conservative ones (semirelativistic)
536 subroutine mhd_to_conserved_semirelati_pi(ixI^L,ixO^L,w,x)
538 integer, intent(in) :: ixi^l, ixo^l
539 double precision, intent(inout) :: w(ixi^s, nw)
540 double precision, intent(in) :: x(ixi^s, 1:ndim)
541
542 double precision :: e(ixo^s,1:ndir), s(ixo^s,1:ndir), eint
543 integer :: ix^d
544
545 {do ix^db=ixomin^db,ixomax^db\}
546 {^ifthreec
547 e(ix^d,1)=w(ix^d,b2_)*w(ix^d,m3_)-w(ix^d,b3_)*w(ix^d,m2_)
548 e(ix^d,2)=w(ix^d,b3_)*w(ix^d,m1_)-w(ix^d,b1_)*w(ix^d,m3_)
549 e(ix^d,3)=w(ix^d,b1_)*w(ix^d,m2_)-w(ix^d,b2_)*w(ix^d,m1_)
550 s(ix^d,1)=e(ix^d,2)*w(ix^d,b3_)-e(ix^d,3)*w(ix^d,b2_)
551 s(ix^d,2)=e(ix^d,3)*w(ix^d,b1_)-e(ix^d,1)*w(ix^d,b3_)
552 s(ix^d,3)=e(ix^d,1)*w(ix^d,b2_)-e(ix^d,2)*w(ix^d,b1_)
553 }
554 {^iftwoc
555 e(ix^d,1)=zero
556 ! switch 3 with 2 to add 3 when ^C from 1 to 2
557 e(ix^d,2)=w(ix^d,b1_)*w(ix^d,m2_)-w(ix^d,b2_)*w(ix^d,m1_)
558 s(ix^d,1)=-e(ix^d,2)*w(ix^d,b2_)
559 s(ix^d,2)=e(ix^d,2)*w(ix^d,b1_)
560 }
561 {^ifonec
562 e(ix^d,1)=zero
563 s(ix^d,1)=zero
564 }
565 if(mhd_internal_e) then
566 ! internal energy
567 call eint_from_rho_p_pi(w(ix^d,rho_), w(ix^d,p_), w(ix^d,e_))
568 else
569 ! equation (9)
570 ! Calculate total energy from internal, kinetic and magnetic energy
571 call eint_from_rho_p_pi(w(ix^d,rho_), w(ix^d,p_), eint)
572 w(ix^d,e_)=eint&
573 +half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
574 +(^c&w(ix^d,b^c_)**2+)&
575 +(^c&e(ix^d,^c)**2+)*eos%inv_squared_c)
576 end if
577
578 ! Convert velocity to momentum, equation (9)
579 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)+s(ix^d,^c)*eos%inv_squared_c\
580
581 {end do\}
582
583 end subroutine mhd_to_conserved_semirelati_pi
584
585 subroutine mhd_to_conserved_semirelati_noe(ixI^L,ixO^L,w,x)
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
591 double precision :: e(ixo^s,1:ndir), s(ixo^s,1:ndir)
592 integer :: ix^d
593
594 {do ix^db=ixomin^db,ixomax^db\}
595 {^ifthreec
596 e(ix^d,1)=w(ix^d,b2_)*w(ix^d,m3_)-w(ix^d,b3_)*w(ix^d,m2_)
597 e(ix^d,2)=w(ix^d,b3_)*w(ix^d,m1_)-w(ix^d,b1_)*w(ix^d,m3_)
598 e(ix^d,3)=w(ix^d,b1_)*w(ix^d,m2_)-w(ix^d,b2_)*w(ix^d,m1_)
599 s(ix^d,1)=e(ix^d,2)*w(ix^d,b3_)-e(ix^d,3)*w(ix^d,b2_)
600 s(ix^d,2)=e(ix^d,3)*w(ix^d,b1_)-e(ix^d,1)*w(ix^d,b3_)
601 s(ix^d,3)=e(ix^d,1)*w(ix^d,b2_)-e(ix^d,2)*w(ix^d,b1_)
602 }
603 {^iftwoc
604 e(ix^d,1)=zero
605 ! switch 3 with 2 to add 3 when ^C from 1 to 2
606 e(ix^d,2)=w(ix^d,b1_)*w(ix^d,m2_)-w(ix^d,b2_)*w(ix^d,m1_)
607 s(ix^d,1)=-e(ix^d,2)*w(ix^d,b2_)
608 s(ix^d,2)=e(ix^d,2)*w(ix^d,b1_)
609 }
610 {^ifonec
611 s(ix^d,1)=zero
612 }
613 ! Convert velocity to momentum, equation (9)
614 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)+s(ix^d,^c)*eos%inv_squared_c\
615
616 {end do\}
617
618 end subroutine mhd_to_conserved_semirelati_noe
619
620 !> Transform conservative variables into primitive ones (origin)
621 subroutine mhd_to_primitive_origin(ixI^L,ixO^L,w,x)
623 integer, intent(in) :: ixi^l, ixo^l
624 double precision, intent(inout) :: w(ixi^s, nw)
625 double precision, intent(in) :: x(ixi^s, 1:ndim)
626
627 double precision :: inv_rho
628 integer :: ix^d
629
630 if (fix_small_values) then
631 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_origin')
632 end if
633
634 {do ix^db=ixomin^db,ixomax^db\}
635 inv_rho = 1.d0/w(ix^d,rho_)
636 ! Convert momentum to velocity
637 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
638 ! Calculate pressure = (gamma-1) * (e-ek-eb)
639 w(ix^d,p_)=eos%gamma_minus_1*(w(ix^d,e_)&
640 -half*(w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+)&
641 +(^c&w(ix^d,b^c_)**2+))&
642 -mhd_uawsom_wave_energy_cell(w(ix^d,:)))
643 {end do\}
644
645 end subroutine mhd_to_primitive_origin
646
647 !> Transform conservative variables into primitive ones (no energy)
648 subroutine mhd_to_primitive_origin_noe(ixI^L,ixO^L,w,x)
650 integer, intent(in) :: ixi^l, ixo^l
651 double precision, intent(inout) :: w(ixi^s, nw)
652 double precision, intent(in) :: x(ixi^s, 1:ndim)
653
654 double precision :: inv_rho
655 integer :: ix^d
656
657 if (fix_small_values) then
658 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_origin_noe')
659 end if
660
661 {do ix^db=ixomin^db,ixomax^db\}
662 inv_rho = 1.d0/w(ix^d,rho_)
663 ! Convert momentum to velocity
664 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
665 {end do\}
666
667 end subroutine mhd_to_primitive_origin_noe
668
669 !> Transform conservative variables into primitive ones (hde)
670 subroutine mhd_to_primitive_hde(ixI^L,ixO^L,w,x)
672 integer, intent(in) :: ixi^l, ixo^l
673 double precision, intent(inout) :: w(ixi^s, nw)
674 double precision, intent(in) :: x(ixi^s, 1:ndim)
675
676 double precision :: inv_rho
677 integer :: ix^d
678
679 if (fix_small_values) then
680 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_hde')
681 end if
682
683 {do ix^db=ixomin^db,ixomax^db\}
684 inv_rho = 1.d0/w(ix^d,rho_)
685 ! Convert momentum to velocity
686 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
687 ! Calculate pressure = (gamma-1) * (e-ek)
688 w(ix^d,p_)=eos%gamma_minus_1*(w(ix^d,e_)-half*w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+))
689 {end do\}
690
691 end subroutine mhd_to_primitive_hde
692
693 !> Transform conservative variables into primitive ones (internal energy)
694 subroutine mhd_to_primitive_inte(ixI^L,ixO^L,w,x)
696 integer, intent(in) :: ixi^l, ixo^l
697 double precision, intent(inout) :: w(ixi^s, nw)
698 double precision, intent(in) :: x(ixi^s, 1:ndim)
699
700 double precision :: inv_rho
701 integer :: ix^d
702
703 if (fix_small_values) then
704 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_inte')
705 end if
706
707 {do ix^db=ixomin^db,ixomax^db\}
708 ! Calculate pressure = (gamma-1) * e_internal
709 w(ix^d,p_)=w(ix^d,e_)*eos%gamma_minus_1
710 ! Convert momentum to velocity
711 inv_rho = 1.d0/w(ix^d,rho_)
712 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
713 {end do\}
714
715 end subroutine mhd_to_primitive_inte
716
717 !> Transform conservative variables into primitive ones (split rho)
718 subroutine mhd_to_primitive_split_rho(ixI^L,ixO^L,w,x)
720 integer, intent(in) :: ixi^l, ixo^l
721 double precision, intent(inout) :: w(ixi^s, nw)
722 double precision, intent(in) :: x(ixi^s, 1:ndim)
723
724 double precision :: inv_rho
725 integer :: ix^d
726
727 if (fix_small_values) then
728 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_split_rho')
729 end if
730
731 {do ix^db=ixomin^db,ixomax^db\}
732 inv_rho=1.d0/(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
733 ! Convert momentum to velocity
734 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
735 ! Calculate pressure = (gamma-1) * (e-ek-eb)
736 w(ix^d,p_)=eos%gamma_minus_1*(w(ix^d,e_)&
737 -half*((w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))*&
738 (^c&w(ix^d,m^c_)**2+)+(^c&w(ix^d,b^c_)**2+)))
739 {end do\}
740
741 end subroutine mhd_to_primitive_split_rho
742
743 !> Transform conservative variables into primitive ones (semirelativistic)
744 subroutine mhd_to_primitive_semirelati(ixI^L,ixO^L,w,x)
746 integer, intent(in) :: ixi^l, ixo^l
747 double precision, intent(inout) :: w(ixi^s, nw)
748 double precision, intent(in) :: x(ixi^s, 1:ndim)
749
750 double precision :: b(ixo^s,1:ndir), tmp, b2, gamma2, inv_rho
751 integer :: ix^d
752
753 if (fix_small_values) then
754 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_semirelati')
755 end if
756
757 {do ix^db=ixomin^db,ixomax^db\}
758 b2=(^c&w(ix^d,b^c_)**2+)
759 if(b2>smalldouble) then
760 tmp=1.d0/sqrt(b2)
761 else
762 tmp=0.d0
763 end if
764 ^c&b(ix^d,^c)=w(ix^d,b^c_)*tmp\
765 tmp=(^c&b(ix^d,^c)*w(ix^d,m^c_)+)
766
767 inv_rho=1.d0/w(ix^d,rho_)
768 ! Va^2/c^2
769 b2=b2*inv_rho*eos%inv_squared_c
770 ! equation (15)
771 gamma2=1.d0/(1.d0+b2)
772 ! Convert momentum to velocity
773 ^c&w(ix^d,m^c_)=gamma2*(w(ix^d,m^c_)+b2*b(ix^d,^c)*tmp)*inv_rho\
774
775 if(mhd_internal_e) then
776 ! internal energy to pressure
777 w(ix^d,p_)=eos%gamma_minus_1*w(ix^d,e_)
778 else
779 ! E=Bxv
780 {^ifthreec
781 b(ix^d,1)=w(ix^d,b2_)*w(ix^d,m3_)-w(ix^d,b3_)*w(ix^d,m2_)
782 b(ix^d,2)=w(ix^d,b3_)*w(ix^d,m1_)-w(ix^d,b1_)*w(ix^d,m3_)
783 b(ix^d,3)=w(ix^d,b1_)*w(ix^d,m2_)-w(ix^d,b2_)*w(ix^d,m1_)
784 }
785 {^iftwoc
786 b(ix^d,1)=zero
787 b(ix^d,2)=w(ix^d,b1_)*w(ix^d,m2_)-w(ix^d,b2_)*w(ix^d,m1_)
788 }
789 {^ifonec
790 b(ix^d,1)=zero
791 }
792 ! Calculate pressure = (gamma-1) * (e-eK-eB-eE)
793 w(ix^d,p_)=eos%gamma_minus_1*(w(ix^d,e_)&
794 -half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
795 +(^c&w(ix^d,b^c_)**2+)&
796 +(^c&b(ix^d,^c)**2+)*eos%inv_squared_c))
797 end if
798 {end do\}
799
800 end subroutine mhd_to_primitive_semirelati
801
802 !> Transform conservative variables into primitive ones (semirelativistic)
803 subroutine mhd_to_primitive_semirelati_pi(ixI^L,ixO^L,w,x)
805 integer, intent(in) :: ixi^l, ixo^l
806 double precision, intent(inout) :: w(ixi^s, nw)
807 double precision, intent(in) :: x(ixi^s, 1:ndim)
808
809 double precision :: b(ixo^s,1:ndir), tmp, b2, gamma2, inv_rho, eint, t, rfac
810 integer :: ix^d
811
812 if (fix_small_values) then
813 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_semirelati')
814 end if
815
816 {do ix^db=ixomin^db,ixomax^db\}
817 b2=(^c&w(ix^d,b^c_)**2+)
818 if(b2>smalldouble) then
819 tmp=1.d0/sqrt(b2)
820 else
821 tmp=0.d0
822 end if
823 ^c&b(ix^d,^c)=w(ix^d,b^c_)*tmp\
824 tmp=(^c&b(ix^d,^c)*w(ix^d,m^c_)+)
825
826 inv_rho=1.d0/w(ix^d,rho_)
827 ! Va^2/c^2
828 b2=b2*inv_rho*eos%inv_squared_c
829 ! equation (15)
830 gamma2=1.d0/(1.d0+b2)
831 ! Convert momentum to velocity
832 ^c&w(ix^d,m^c_)=gamma2*(w(ix^d,m^c_)+b2*b(ix^d,^c)*tmp)*inv_rho\
833
834 if(mhd_internal_e) then
835 ! internal energy to pressure
836 call state_from_eint_pi(w(ix^d,rho_), w(ix^d,e_), t, w(ix^d,p_), rfac)
837 else
838 ! E=Bxv
839 {^ifthreec
840 b(ix^d,1)=w(ix^d,b2_)*w(ix^d,m3_)-w(ix^d,b3_)*w(ix^d,m2_)
841 b(ix^d,2)=w(ix^d,b3_)*w(ix^d,m1_)-w(ix^d,b1_)*w(ix^d,m3_)
842 b(ix^d,3)=w(ix^d,b1_)*w(ix^d,m2_)-w(ix^d,b2_)*w(ix^d,m1_)
843 }
844 {^iftwoc
845 b(ix^d,1)=zero
846 b(ix^d,2)=w(ix^d,b1_)*w(ix^d,m2_)-w(ix^d,b2_)*w(ix^d,m1_)
847 }
848 {^ifonec
849 b(ix^d,1)=zero
850 }
851 ! internal energy= e-eK-eB-eE
852 eint=w(ix^d,e_)&
853 -half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
854 +(^c&w(ix^d,b^c_)**2+)&
855 +(^c&b(ix^d,^c)**2+)*eos%inv_squared_c)
856 call state_from_eint_pi(w(ix^d,rho_), eint, t, w(ix^d,p_), rfac)
857 end if
858 {end do\}
859
860 end subroutine mhd_to_primitive_semirelati_pi
861
862 !> Transform conservative variables into primitive ones (semirelativistic noe)
863 subroutine mhd_to_primitive_semirelati_noe(ixI^L,ixO^L,w,x)
865 integer, intent(in) :: ixi^l, ixo^l
866 double precision, intent(inout) :: w(ixi^s, nw)
867 double precision, intent(in) :: x(ixi^s, 1:ndim)
868
869 double precision :: b(ixo^s,1:ndir),tmp,b2,gamma2,inv_rho
870 integer :: ix^d, idir
871
872 if (fix_small_values) then
873 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_semirelati_noe')
874 end if
875
876 {do ix^db=ixomin^db,ixomax^db\}
877 b2=(^c&w(ix^d,b^c_)**2+)
878 if(b2>smalldouble) then
879 tmp=1.d0/sqrt(b2)
880 else
881 tmp=0.d0
882 end if
883 ^c&b(ix^d,^c)=w(ix^d,b^c_)*tmp\
884 tmp=(^c&b(ix^d,^c)*w(ix^d,m^c_)+)
885
886 inv_rho=1.d0/w(ix^d,rho_)
887 ! Va^2/c^2
888 b2=b2*inv_rho*eos%inv_squared_c
889 ! equation (15)
890 gamma2=1.d0/(1.d0+b2)
891 ! Convert momentum to velocity
892 ^c&w(ix^d,m^c_)=gamma2*(w(ix^d,m^c_)+b2*b(ix^d,^c)*tmp)*inv_rho\
893 {end do\}
894
895 end subroutine mhd_to_primitive_semirelati_noe
896
897 !> LTE conversion routines
898
899 !> LTE: Primitive (rho, v, p, B) -> Conserved (rho, rho*v, E_total, B)
900 !> E_total = eint + 0.5*rho*v^2 + 0.5*B^2
901 subroutine mhd_to_conserved_origin_lte(ixI^L,ixO^L,w,x)
903 integer, intent(in) :: ixi^l, ixo^l
904 double precision, intent(inout) :: w(ixi^s, nw)
905 double precision, intent(in) :: x(ixi^s, 1:ndim)
906
907 timeeos0 = mpi_wtime()
908
909 ! Convert p -> E_total (eint + eK + eB) via EoS table
910 call mhd_p_to_e(ixi^l, ixo^l, w, x)
911 ! Convert velocity to momentum
912 ^c&w(ixo^s,m^c_)=w(ixo^s,rho_)*w(ixo^s,m^c_)\
913
914 timeeos_conv=timeeos_conv+(mpi_wtime()-timeeos0)
915
916 end subroutine mhd_to_conserved_origin_lte
917
918 !> LTE: Primitive (rho, v, p, B) -> Conserved (rho, rho*v, eint, B)
919 !> Internal energy formulation: e_ stores eint only
920 subroutine mhd_to_conserved_inte_lte(ixI^L,ixO^L,w,x)
922 integer, intent(in) :: ixi^l, ixo^l
923 double precision, intent(inout) :: w(ixi^s, nw)
924 double precision, intent(in) :: x(ixi^s, 1:ndim)
925
926 timeeos0 = mpi_wtime()
927
928 ! Convert p -> eint via EoS table (no mechanical energy)
929 call mhd_p_to_eint(ixi^l, ixo^l, w, x)
930 ! Convert velocity to momentum
931 ^c&w(ixo^s,m^c_)=w(ixo^s,rho_)*w(ixo^s,m^c_)\
932
933 timeeos_conv=timeeos_conv+(mpi_wtime()-timeeos0)
934
935 end subroutine mhd_to_conserved_inte_lte
936
937 !> Convert pressure to total energy (eint + eK + eB) for origin formulation.
938 !> Uses p2eint table for LTE ionE, with FI bypass for hot cells.
939 subroutine mhd_p_to_e(ixI^L,ixO^L,w,x)
941 integer, intent(in) :: ixi^l, ixo^l
942 double precision, intent(inout) :: w(ixi^s, nw)
943 double precision, intent(in) :: x(ixi^s, 1:ndim)
944
945 integer :: ix^d
946 double precision :: p_to_eint, p_over_rho
947 double precision :: nh(ixi^s), nh_in(ixi^s), p_in(ixi^s)
948 double precision :: t_solve, y_solve, eint_nh_solve
949 double precision :: log_eint_mid, eint_total
950
951 if (eos%ionE) then
952 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
953 nh_in(ixo^s) = dlog10(nh(ixo^s))
954 if (eos%p2eint_method /= 'bisect') then
955 p_in(ixo^s) = dlog10(w(ixo^s,p_)) - nh_in(ixo^s)
956 end if
957 endif
958
959 p_to_eint = eos%inv_gamma_minus_1
960 {do ix^db=ixomin^db,ixomax^db\}
961 if (eos%ionE) then
962 p_over_rho = w(ix^d,p_) / w(ix^d,rho_)
963 if (p_over_rho > eos%p_rho_FI_threshold) then
964 p_to_eint = eos%inv_gamma_minus_1 &
965 + eos%eion_per_nH * nh(ix^d) / w(ix^d,p_)
966 w(ix^d,e_)=w(ix^d,p_)*p_to_eint&
967 +half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
968 +(^c&w(ix^d,b^c_)**2+))
969 else if (eos%method == 'analytic') then
970 call saha_state_from_nh_p(nh(ix^d), w(ix^d,p_), &
971 t_solve, y_solve, eint_nh_solve)
972 w(ix^d,e_) = eint_nh_solve * nh(ix^d) &
973 +half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
974 +(^c&w(ix^d,b^c_)**2+))
975 else if (eos%p2eint_method == 'bisect') then
976 call eint_from_p_bisect(nh_in(ix^d), &
977 dlog10(w(ix^d,p_)), log_eint_mid)
978 eint_total = nh(ix^d) * 10.0d0**log_eint_mid
979 eint_total = max(eint_total, &
980 nh(ix^d) * 10.0d0**eos%T%var2_min)
981 w(ix^d,e_) = eint_total + &
982 half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
983 +(^c&w(ix^d,b^c_)**2+))
984 else
985 p_to_eint = p2eint_from_nh_p(nh_in(ix^d), p_in(ix^d))
986 w(ix^d,e_)=w(ix^d,p_)*p_to_eint&
987 +half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
988 +(^c&w(ix^d,b^c_)**2+))
989 end if
990 else
991 w(ix^d,e_)=w(ix^d,p_)*p_to_eint&
992 +half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
993 +(^c&w(ix^d,b^c_)**2+))
994 end if
995 if(mhd_uawsom) w(ix^d,e_)=w(ix^d,e_)+&
996 w(ix^d,waplus_)+w(ix^d,waminus_)+w(ix^d,wkplus_)+w(ix^d,wkminus_)
997 {end do\}
998
999 end subroutine mhd_p_to_e
1000
1001 !> Convert pressure to internal energy only (for internal_e formulation).
1002 !> Uses p2eint table for LTE ionE, with FI bypass for hot cells.
1003 subroutine mhd_p_to_eint(ixI^L,ixO^L,w,x)
1005 integer, intent(in) :: ixi^l, ixo^l
1006 double precision, intent(inout) :: w(ixi^s, nw)
1007 double precision, intent(in) :: x(ixi^s, 1:ndim)
1008
1009 integer :: ix^d
1010 double precision :: p_to_eint, p_over_rho
1011 double precision :: nh(ixi^s), nh_in(ixi^s), p_in(ixi^s)
1012 double precision :: t_solve, y_solve, eint_nh_solve
1013 double precision :: log_eint_mid, eint_total
1014
1015 if (eos%ionE) then
1016 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
1017 nh_in(ixo^s) = dlog10(nh(ixo^s))
1018 if (eos%p2eint_method /= 'bisect') then
1019 p_in(ixo^s) = dlog10(w(ixo^s,p_)) - nh_in(ixo^s)
1020 end if
1021 endif
1022
1023 p_to_eint = eos%inv_gamma_minus_1
1024 {do ix^db=ixomin^db,ixomax^db\}
1025 if (eos%ionE) then
1026 p_over_rho = w(ix^d,p_) / w(ix^d,rho_)
1027 if (p_over_rho > eos%p_rho_FI_threshold) then
1028 p_to_eint = eos%inv_gamma_minus_1 &
1029 + eos%eion_per_nH * nh(ix^d) / w(ix^d,p_)
1030 w(ix^d,e_) = w(ix^d,p_) * p_to_eint
1031 else if (eos%method == 'analytic') then
1032 call saha_state_from_nh_p(nh(ix^d), w(ix^d,p_), &
1033 t_solve, y_solve, eint_nh_solve)
1034 w(ix^d,e_) = eint_nh_solve * nh(ix^d)
1035 else if (eos%p2eint_method == 'bisect') then
1036 call eint_from_p_bisect(nh_in(ix^d), &
1037 dlog10(w(ix^d,p_)), log_eint_mid)
1038 eint_total = nh(ix^d) * 10.0d0**log_eint_mid
1039 eint_total = max(eint_total, &
1040 nh(ix^d) * 10.0d0**eos%T%var2_min)
1041 w(ix^d,e_) = eint_total
1042 else
1043 p_to_eint = p2eint_from_nh_p(nh_in(ix^d), p_in(ix^d))
1044 w(ix^d,e_) = w(ix^d,p_) * p_to_eint
1045 end if
1046 else
1047 w(ix^d,e_) = w(ix^d,p_) * p_to_eint
1048 end if
1049 {end do\}
1050
1051 end subroutine mhd_p_to_eint
1052
1053 !> LTE: Conserved (rho, rho*v, E_total, B) -> Primitive (rho, v, p, B)
1054 !> Uses T and neOnH tables with FI bypass for hot cells.
1055 subroutine mhd_to_primitive_origin_lte(ixI^L,ixO^L,w,x)
1057 integer, intent(in) :: ixi^l, ixo^l
1058 double precision, intent(inout) :: w(ixi^s, nw)
1059 double precision, intent(in) :: x(ixi^s, 1:ndim)
1060
1061 double precision :: inv_rho, eint_val, eint_in
1062 double precision :: nh(ixi^s), log_nh(ixi^s)
1063 integer :: ix^d
1064
1065 timeeos0 = mpi_wtime()
1066
1067 if (fix_small_values) then
1068 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, &
1069 'mhd_to_primitive_origin_LTE')
1070 end if
1071
1072 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
1073 if (eos%ionE) then
1074 log_nh(ixo^s) = dlog10(nh(ixo^s))
1075 end if
1076
1077 {do ix^db=ixomin^db,ixomax^db\}
1078 inv_rho = 1.d0/w(ix^d,rho_)
1079 ! Convert momentum to velocity
1080 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
1081 ! Extract internal energy: eint = E - 0.5*rho*v^2 - 0.5*B^2
1082 eint_val = w(ix^d,e_) &
1083 - half*(w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+) &
1084 + (^c&w(ix^d,b^c_)**2+))
1085 ! Floor eint to prevent unphysical values
1086 if (eos%method /= 'analytic') then
1087 eint_val = max(eint_val, nh(ix^d) * 10.0d0**eos%T%var2_min)
1088 end if
1089 eint_val = max(eint_val, smalldouble)
1090
1091 if (eos%ionE) then
1092 if (eint_val * inv_rho > eos%eint_rho_FI_threshold) then
1093 ! FI bypass: p = (gamma-1) * (eint - eion*nH)
1094 w(ix^d,p_) = eos%gamma_minus_1 &
1095 * (eint_val - eos%eion_per_nH * nh(ix^d))
1096 else
1097 ! Ionisation zone: single p/nH lookup (replaces separate T + y)
1098 eint_in = dlog10(eint_val) - log_nh(ix^d)
1099 w(ix^d,p_) = nh(ix^d) * p_nh_from_eint(log_nh(ix^d), eint_in)
1100 end if
1101 else
1102 ! FI without ionE tables
1103 w(ix^d,p_) = eos%gamma_minus_1 * eint_val
1104 end if
1105 {end do\}
1106
1107 timeeos_conv=timeeos_conv+(mpi_wtime()-timeeos0)
1108
1109 end subroutine mhd_to_primitive_origin_lte
1110
1111 !> LTE: Conserved (rho, rho*v, eint, B) -> Primitive (rho, v, p, B)
1112 !> Internal energy formulation: eint = w(e_) directly.
1113 subroutine mhd_to_primitive_inte_lte(ixI^L,ixO^L,w,x)
1115 integer, intent(in) :: ixi^l, ixo^l
1116 double precision, intent(inout) :: w(ixi^s, nw)
1117 double precision, intent(in) :: x(ixi^s, 1:ndim)
1118
1119 double precision :: inv_rho, eint_val, eint_in, t_loc, y_loc
1120 double precision :: nh(ixi^s), log_nh(ixi^s)
1121 integer :: ix^d
1122
1123 timeeos0 = mpi_wtime()
1124
1125 if (fix_small_values) then
1126 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, &
1127 'mhd_to_primitive_inte_LTE')
1128 end if
1129
1130 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
1131 if (eos%ionE) then
1132 log_nh(ixo^s) = dlog10(nh(ixo^s))
1133 end if
1134
1135 {do ix^db=ixomin^db,ixomax^db\}
1136 ! eint = w(e_) directly for internal energy formulation
1137 eint_val = w(ix^d,e_)
1138 eint_val = max(eint_val, nh(ix^d) * 10.0d0**eos%T%var2_min)
1139
1140 if (eos%ionE) then
1141 if (eint_val / w(ix^d,rho_) > eos%eint_rho_FI_threshold) then
1142 w(ix^d,p_) = eos%gamma_minus_1 &
1143 * (eint_val - eos%eion_per_nH * nh(ix^d))
1144 else
1145 eint_in = dlog10(eint_val) - log_nh(ix^d)
1146 w(ix^d,p_) = nh(ix^d) * p_nh_from_eint(log_nh(ix^d), eint_in)
1147 end if
1148 else
1149 w(ix^d,p_) = eos%gamma_minus_1 * eint_val
1150 end if
1151
1152 ! Convert momentum to velocity
1153 inv_rho = 1.d0/w(ix^d,rho_)
1154 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
1155 {end do\}
1156
1157 timeeos_conv=timeeos_conv+(mpi_wtime()-timeeos0)
1158
1159 end subroutine mhd_to_primitive_inte_lte
1160
1161 !> LTE prolongation routines
1162
1163 !> Convert conserved (rho, rho*v, E_total, B) to prolongation form (rho, v, T, B).
1164 !> Temperature is stored in the p_ slot for AMR interpolation.
1165 !> T is smoothest through the ionisation zone (conduction-dominated, linear gradient).
1166 subroutine mhd_to_prolong_lte(ixI^L,ixO^L,w,x)
1168 integer, intent(in) :: ixi^l, ixo^l
1169 double precision, intent(inout) :: w(ixi^s, nw)
1170 double precision, intent(in) :: x(ixi^s, 1:ndim)
1171
1172 double precision :: inv_rho, eint_val, t_loc, y_loc
1173 double precision :: nh(ixi^s), log_nh(ixi^s)
1174 integer :: ix^d
1175
1176 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
1177 log_nh(ixo^s) = dlog10(nh(ixo^s))
1178
1179 {do ix^db=ixomin^db,ixomax^db\}
1180 inv_rho = 1.d0/w(ix^d,rho_)
1181 ! Convert momentum to velocity
1182 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
1183 if (mhd_internal_e) then
1184 eint_val = w(ix^d,e_)
1185 else
1186 ! Compute eint = E - 0.5*rho*v^2 - 0.5*B^2
1187 eint_val = w(ix^d,e_) &
1188 - half*(w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+) &
1189 + (^c&w(ix^d,b^c_)**2+))
1190 end if
1191 ! Floor eint to prevent unphysical values
1192 if (eos%method /= 'analytic') then
1193 eint_val = max(eint_val, nh(ix^d) * 10.0d0**eos%T%var2_min)
1194 end if
1195 eint_val = max(eint_val, smalldouble)
1196
1197 if (eint_val * inv_rho > eos%eint_rho_FI_threshold) then
1198 ! FI: T = (gamma-1)*(eint - eion*nH) / (nH * n_per_nH_FI)
1199 w(ix^d,p_) = eos%gamma_minus_1 &
1200 * (eint_val - eos%eion_per_nH * nh(ix^d)) &
1201 / (nh(ix^d) * eos%n_per_nH_FI)
1202 else if (eos%method == 'analytic') then
1203 ! Analytical Saha: solve for T from eint
1204 call saha_t_from_nh_eint(nh(ix^d), &
1205 eint_val / nh(ix^d), t_loc, y_loc)
1206 w(ix^d,p_) = t_loc
1207 else
1208 ! Ionisation zone: T from table
1209 w(ix^d,p_) = t_from_nh_eint( &
1210 log_nh(ix^d), &
1211 dlog10(eint_val) - log_nh(ix^d))
1212 end if
1213 {end do\}
1214
1215 end subroutine mhd_to_prolong_lte
1216
1217 !> Convert prolongation form (rho, v, T, B) to conserved (rho, rho*v, E_total, B).
1218 !> T is read from the p_ slot. Uses eint_nH_from_T table for back-conversion.
1219 subroutine mhd_from_prolong_lte(ixI^L,ixO^L,w,x)
1221 integer, intent(in) :: ixi^l, ixo^l
1222 double precision, intent(inout) :: w(ixi^s, nw)
1223 double precision, intent(in) :: x(ixi^s, 1:ndim)
1224
1225 double precision :: t_val, eint_val, t_fi, log_t_min
1226 double precision :: nh(ixi^s), log_nh(ixi^s)
1227 integer :: ix^d
1228
1229 ! Temperature above which gas is fully ionised
1230 t_fi = (eos%eint_rho_FI_threshold &
1231 * eos%nH2rhoFactor - eos%eion_per_nH) &
1232 * eos%gamma_minus_1 / eos%n_per_nH_FI
1233
1234 ! Floor for log_T into the (rho, T) inverse table; entropy method uses
1235 ! eos%eintT, legacy 'tables' method uses eos%eint_from_T. Picking the
1236 ! wrong container leaves var2_min = 0 → floors T at 10^6 K.
1237 if (eos%method == 'entropy') then
1238 log_t_min = eos%eintT%var2_min
1239 else
1240 log_t_min = eos%eint_from_T%var2_min
1241 end if
1242
1243 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
1244 log_nh(ixo^s) = dlog10(nh(ixo^s))
1245
1246 {do ix^db=ixomin^db,ixomax^db\}
1247 t_val = w(ix^d,p_) ! T stored in p_ slot
1248 if (t_val > t_fi) then
1249 ! FI: eint = nH*(n_per_nH*T/(gamma-1) + eion)
1250 eint_val = nh(ix^d) &
1251 * (eos%n_per_nH_FI * t_val * eos%inv_gamma_minus_1 &
1252 + eos%eion_per_nH)
1253 else if (eos%method == 'analytic') then
1254 ! Analytical Saha: eint from T directly
1255 eint_val = saha_eint_from_nh_t(nh(ix^d), t_val) * nh(ix^d)
1256 else
1257 ! Ionisation zone: eint/nH from T table
1258 eint_val = eint_nh_from_t( &
1259 log_nh(ix^d), &
1260 dlog10(max(t_val, 10.0d0**log_t_min))) &
1261 * nh(ix^d)
1262 end if
1263 if (mhd_internal_e) then
1264 w(ix^d,e_) = eint_val
1265 else
1266 ! E = eint + 0.5*rho*v^2 + 0.5*B^2
1267 w(ix^d,e_) = eint_val &
1268 + half*(w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+) &
1269 + (^c&w(ix^d,b^c_)**2+))
1270 end if
1271 ! Convert velocity to momentum
1272 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)\
1273 {end do\}
1274
1275 end subroutine mhd_from_prolong_lte
1276
1277 !> Sound speed routines
1278
1279 !> Acoustic sound speed squared for FI (constant gamma) EoS.
1280 !> Expects w in primitive form: w(p_) = pressure, w(rho_) = density.
1281 subroutine mhd_get_csound2_fi(w, x, ixI^L, ixO^L, cs2)
1283 integer, intent(in) :: ixi^l, ixo^l
1284 double precision, intent(in) :: w(ixi^s, nw)
1285 double precision, intent(in) :: x(ixi^s, 1:ndim)
1286 double precision, intent(out) :: cs2(ixi^s)
1287
1288 double precision :: rho(ixi^s), pth(ixi^s)
1289
1290 timeeos0 = mpi_wtime()
1291
1292 !> Both slots hold perturbations under equilibrium splitting, so the
1293 !> background is restored before the ratio is formed.
1294 call eos%get_rho(w, x, ixi^l, ixo^l, rho)
1295 pth(ixo^s) = w(ixo^s, p_)
1296 if (iw_equi_p > 0) pth(ixo^s) = pth(ixo^s) &
1297 + block%equi_vars(ixo^s, iw_equi_p, b0i)
1298 cs2(ixo^s) = eos%gamma * pth(ixo^s) / rho(ixo^s)
1299
1300 timeeos_csound = timeeos_csound + (mpi_wtime()-timeeos0)
1301
1302 end subroutine mhd_get_csound2_fi
1303
1304 !> Acoustic sound speed squared for LTE+IonE EoS using Gamma_1 table.
1305 !> Expects w in primitive form. Uses FI bypass for hot cells.
1306 subroutine mhd_get_csound2_lte(w, x, ixI^L, ixO^L, cs2)
1308 integer, intent(in) :: ixi^l, ixo^l
1309 double precision, intent(in) :: w(ixi^s, nw)
1310 double precision, intent(in) :: x(ixi^s, 1:ndim)
1311 double precision, intent(out) :: cs2(ixi^s)
1312
1313 double precision :: nh_val, log_nh, log_p_nh, g1, p_over_rho
1314 integer :: ix^d
1315
1316 timeeos0 = mpi_wtime()
1317
1318 if (eos%gamma1_method == 'constant') then
1319 cs2(ixo^s) = eos%gamma * w(ixo^s, p_) / w(ixo^s, rho_)
1320 else
1321 {do ix^db=ixomin^db,ixomax^db\}
1322 p_over_rho = w(ix^d, p_) / w(ix^d, rho_)
1323 if (p_over_rho > eos%p_rho_FI_threshold) then
1324 cs2(ix^d) = eos%gamma * p_over_rho
1325 else
1326 nh_val = w(ix^d, rho_) / eos%nH2rhoFactor
1327 if (eos%method == 'analytic') then
1328 if (iw_te > 0 .and. w(ix^d,iw_te) > 0.0d0) then
1329 g1 = saha_gamma1_from_nh_t(nh_val, w(ix^d,iw_te))
1330 else
1331 g1 = eos%gamma
1332 end if
1333 else
1334 log_nh = dlog10(nh_val)
1335 log_p_nh = dlog10(w(ix^d, p_) / nh_val)
1336 g1 = gamma1_from_nh_p(log_nh, log_p_nh)
1337 end if
1338 cs2(ix^d) = g1 * p_over_rho
1339 end if
1340 {end do\}
1341 end if
1342
1343 timeeos_csound = timeeos_csound + (mpi_wtime()-timeeos0)
1344
1345 end subroutine mhd_get_csound2_lte
1346
1347 subroutine mhd_get_gamma1_lte(w, x, ixI^L, ixO^L, gamma1)
1349 integer, intent(in) :: ixi^l, ixo^l
1350 double precision, intent(in) :: w(ixi^s, nw)
1351 double precision, intent(in) :: x(ixi^s, 1:ndim)
1352 double precision, intent(out) :: gamma1(ixi^s)
1353
1354 double precision :: nh_val, p_over_rho
1355 integer :: ix^d
1356
1357 if (eos%gamma1_method == 'constant') then
1358 gamma1(ixo^s) = eos%gamma
1359 return
1360 end if
1361
1362 {do ix^db=ixomin^db,ixomax^db\}
1363 p_over_rho = w(ix^d, p_) / w(ix^d, rho_)
1364 if (p_over_rho > eos%p_rho_FI_threshold) then
1365 gamma1(ix^d) = eos%gamma
1366 else
1367 nh_val = w(ix^d, rho_) / eos%nH2rhoFactor
1368 if (eos%method == 'analytic') then
1369 if (iw_te > 0 .and. w(ix^d,iw_te) > 0.0d0) then
1370 gamma1(ix^d) = saha_gamma1_from_nh_t(nh_val, w(ix^d,iw_te))
1371 else
1372 gamma1(ix^d) = eos%gamma
1373 end if
1374 else
1375 gamma1(ix^d) = gamma1_from_nh_p(dlog10(nh_val), &
1376 dlog10(w(ix^d, p_) / nh_val))
1377 end if
1378 end if
1379 {end do\}
1380
1381 end subroutine mhd_get_gamma1_lte
1382
1383 !> Rfactor routines (matching HD: EoS functions live in EoS module)
1384
1385 !> Rfactor = p/(rho*T) for constant ionisation degree (FI/PI no-energy).
1386 !> Stays in the seam: RR is the physics module's gas-constant factor, not
1387 !> visible to mod_eos. Rfactor_from_LTE lives in mod_eos (no RR).
1388 subroutine rfactor_from_constant_ionization(w,x,ixI^L,ixO^L,Rfactor)
1390 integer, intent(in) :: ixi^l, ixo^l
1391 double precision, intent(in) :: w(ixi^s,1:nw)
1392 double precision, intent(in) :: x(ixi^s,1:ndim)
1393 double precision, intent(out):: rfactor(ixi^s)
1394
1395 rfactor(ixo^s)=rr
1396
1397 end subroutine rfactor_from_constant_ionization
1398
1399 !> Rfactor from partial ionisation temperature lookup (Leenaarts et al. 2012)
1400
1401 !> Thermal pressure routines (moved from mod_mhd_phys.t)
1402
1403 !> Calculate isothermal thermal pressure
1404 subroutine mhd_get_pthermal_noe(w,x,ixI^L,ixO^L,pth)
1407
1408 integer, intent(in) :: ixi^l, ixo^l
1409 double precision, intent(in) :: w(ixi^s,nw)
1410 double precision, intent(in) :: x(ixi^s,1:ndim)
1411 double precision, intent(out):: pth(ixi^s)
1412
1413 double precision :: adiabs(ixi^s), gammas(ixi^s)
1414
1415 if(associated(usr_set_adiab)) then
1416 call usr_set_adiab(w,x,ixi^l,ixo^l,adiabs)
1417 else
1418 adiabs=mhd_adiab
1419 end if
1420 if(associated(usr_set_gamma)) then
1421 call usr_set_gamma(w,x,ixi^l,ixo^l,gammas)
1422 else
1423 gammas=eos%gamma
1424 end if
1425
1426 if(has_equi_rho_and_p) then
1427 pth(ixo^s)=adiabs(ixo^s)*(w(ixo^s,rho_)+block%equi_vars(ixo^s,equi_rho0_,0))**gammas(ixo^s)
1428 else
1429 pth(ixo^s)=adiabs(ixo^s)*w(ixo^s,rho_)**gammas(ixo^s)
1430 end if
1431
1432 end subroutine mhd_get_pthermal_noe
1433
1434 !> Calculate thermal pressure from internal energy
1435 subroutine mhd_get_pthermal_inte(w,x,ixI^L,ixO^L,pth)
1438
1439 integer, intent(in) :: ixi^l, ixo^l
1440 double precision, intent(in) :: w(ixi^s,nw)
1441 double precision, intent(in) :: x(ixi^s,1:ndim)
1442 double precision, intent(out):: pth(ixi^s)
1443
1444 integer :: iw, ix^d
1445
1446 {do ix^db= ixomin^db,ixomax^db\}
1447 if(has_equi_rho_and_p) then
1448 pth(ix^d)=eos%gamma_minus_1*w(ix^d,e_)+block%equi_vars(ix^d,equi_pe0_,0)
1449 else
1450 pth(ix^d)=eos%gamma_minus_1*w(ix^d,e_)
1451 end if
1452 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1453 {end do\}
1454
1455 if(check_small_values.and..not.fix_small_values) then
1456 {do ix^db= ixomin^db,ixomax^db\}
1457 if(pth(ix^d)<small_pressure) then
1458 write(*,*) "Error: small value of gas pressure",pth(ix^d),&
1459 " encountered when call mhd_get_pthermal_inte"
1460 write(*,*) "Iteration: ", it, " Time: ", global_time
1461 write(*,*) "Location: ", x(ix^d,:)
1462 write(*,*) "Cell number: ", ix^d
1463 do iw=1,nw
1464 write(*,*) trim(cons_wnames(iw)),": ",w(ix^d,iw)
1465 end do
1466 if(trace_small_values) write(*,*) sqrt(pth(ix^d)-bigdouble)
1467 write(*,*) "Saving status at the previous time step"
1468 crash=.true.
1469 end if
1470 {end do\}
1471 end if
1472
1473 end subroutine mhd_get_pthermal_inte
1474
1475 !> Calculate thermal pressure=(gamma-1)*(e-0.5*m**2/rho-b**2/2) within ixO^L
1476 subroutine mhd_get_pthermal_origin(w,x,ixI^L,ixO^L,pth)
1479
1480 integer, intent(in) :: ixi^l, ixo^l
1481 double precision, intent(in) :: w(ixi^s,nw)
1482 double precision, intent(in) :: x(ixi^s,1:ndim)
1483 double precision, intent(out):: pth(ixi^s)
1484
1485 integer :: iw, ix^d
1486
1487 {do ix^db=ixomin^db,ixomax^db\}
1488 if(has_equi_rho_and_p) then
1489 pth(ix^d)=eos%gamma_minus_1*(w(ix^d,e_)-half*((^c&w(ix^d,m^c_)**2+)/(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0))&
1490 +(^c&w(ix^d,b^c_)**2+)))+block%equi_vars(ix^d,equi_pe0_,0)
1491 else
1492 pth(ix^d)=eos%gamma_minus_1*(w(ix^d,e_)-half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)&
1493 +(^c&w(ix^d,b^c_)**2+))-mhd_uawsom_wave_energy_cell(w(ix^d,:)))
1494 end if
1495 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1496 {end do\}
1497
1498 if(check_small_values.and..not.fix_small_values) then
1499 {do ix^db=ixomin^db,ixomax^db\}
1500 if(pth(ix^d)<small_pressure) then
1501 write(*,*) "Error: small value of gas pressure",pth(ix^d),&
1502 " encountered when call mhd_get_pthermal"
1503 write(*,*) "Iteration: ", it, " Time: ", global_time
1504 write(*,*) "Location: ", x(ix^d,:)
1505 write(*,*) "Cell number: ", ix^d
1506 do iw=1,nw
1507 write(*,*) trim(cons_wnames(iw)),": ",w(ix^d,iw)
1508 end do
1509 if(trace_small_values) write(*,*) sqrt(pth(ix^d)-bigdouble)
1510 write(*,*) "Saving status at the previous time step"
1511 crash=.true.
1512 end if
1513 {end do\}
1514 end if
1515
1516 end subroutine mhd_get_pthermal_origin
1517
1518 !> Calculate thermal pressure for LTE EoS (delegates to eos%get_thermal_pressure)
1519 subroutine mhd_get_pthermal_lte(w,x,ixI^L,ixO^L,pth)
1522
1523 integer, intent(in) :: ixi^l, ixo^l
1524 double precision, intent(in) :: w(ixi^s,nw)
1525 double precision, intent(in) :: x(ixi^s,1:ndim)
1526 double precision, intent(out):: pth(ixi^s)
1527 double precision :: nh(ixi^s)
1528
1529 integer :: iw, ix^d
1530
1531 ! LTE: p = nH * (1 + He + ne/nH) * T from stored state variables
1532 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
1533 pth(ixo^s) = nh(ixo^s) * (1.0d0 + eos%He_abundance &
1534 + (w(ixo^s,ne_) / nh(ixo^s))) * w(ixo^s,te_)
1535
1536 if(fix_small_values) then
1537 {do ix^db=ixomin^db,ixomax^db\}
1538 if(pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1539 {end do\}
1540 else if(check_small_values) then
1541 {do ix^db=ixomin^db,ixomax^db\}
1542 if(pth(ix^d)<small_pressure) then
1543 write(*,*) "Error: small value of gas pressure",pth(ix^d),&
1544 " encountered when call mhd_get_pthermal_LTE"
1545 write(*,*) "Iteration: ", it, " Time: ", global_time
1546 write(*,*) "Location: ", x(ix^d,:)
1547 write(*,*) "Cell number: ", ix^d
1548 do iw=1,nw
1549 write(*,*) trim(cons_wnames(iw)),": ",w(ix^d,iw)
1550 end do
1551 if(trace_small_values) write(*,*) sqrt(pth(ix^d)-bigdouble)
1552 write(*,*) "Saving status at the previous time step"
1553 crash=.true.
1554 end if
1555 {end do\}
1556 end if
1557
1558 end subroutine mhd_get_pthermal_lte
1559
1560 !> Calculate thermal pressure for semirelativistic MHD
1561 subroutine mhd_get_pthermal_semirelati(w,x,ixI^L,ixO^L,pth)
1564
1565 integer, intent(in) :: ixi^l, ixo^l
1566 double precision, intent(in) :: w(ixi^s,nw)
1567 double precision, intent(in) :: x(ixi^s,1:ndim)
1568 double precision, intent(out):: pth(ixi^s)
1569
1570 double precision :: b(ixo^s,1:ndir), v(ndir), tmp, b2, gamma2, inv_rho
1571 integer :: iw, ix^d
1572
1573 {do ix^db=ixomin^db,ixomax^db\}
1574 b2=(^c&w(ix^d,b^c_)**2+)
1575 if(b2>smalldouble) then
1576 tmp=1.d0/sqrt(b2)
1577 else
1578 tmp=0.d0
1579 end if
1580 ^c&b(ix^d,^c)=w(ix^d,b^c_)*tmp\
1581 tmp=(^c&b(ix^d,^c)*w(ix^d,m^c_)+)
1582
1583 inv_rho=1.d0/w(ix^d,rho_)
1584 ! Va^2/c^2
1585 b2=b2*inv_rho*eos%inv_squared_c
1586 ! equation (15)
1587 gamma2=1.d0/(1.d0+b2)
1588 ! Convert momentum to velocity
1589 ^c&v(^c)=gamma2*(w(ix^d,m^c_)+b2*b(ix^d,^c)*tmp)*inv_rho\
1590
1591 ! E=Bxv
1592 {^ifthreec
1593 b(ix^d,1)=w(ix^d,b2_)*v(3)-w(ix^d,b3_)*v(2)
1594 b(ix^d,2)=w(ix^d,b3_)*v(1)-w(ix^d,b1_)*v(3)
1595 b(ix^d,3)=w(ix^d,b1_)*v(2)-w(ix^d,b2_)*v(1)
1596 }
1597 {^iftwoc
1598 b(ix^d,1)=zero
1599 b(ix^d,2)=w(ix^d,b1_)*v(2)-w(ix^d,b2_)*v(1)
1600 }
1601 {^ifonec
1602 b(ix^d,1)=zero
1603 }
1604 ! Calculate pressure = (gamma-1) * (e-eK-eB-eE)
1605 pth(ix^d)=eos%gamma_minus_1*(w(ix^d,e_)&
1606 -half*((^c&v(^c)**2+)*w(ix^d,rho_)&
1607 +(^c&w(ix^d,b^c_)**2+)&
1608 +(^c&b(ix^d,^c)**2+)*eos%inv_squared_c))
1609 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1610 {end do\}
1611
1612 if(check_small_values.and..not.fix_small_values) then
1613 {do ix^db=ixomin^db,ixomax^db\}
1614 if(pth(ix^d)<small_pressure) then
1615 write(*,*) "Error: small value of gas pressure",pth(ix^d),&
1616 " encountered when call mhd_get_pthermal_semirelati"
1617 write(*,*) "Iteration: ", it, " Time: ", global_time
1618 write(*,*) "Location: ", x(ix^d,:)
1619 write(*,*) "Cell number: ", ix^d
1620 do iw=1,nw
1621 write(*,*) trim(cons_wnames(iw)),": ",w(ix^d,iw)
1622 end do
1623 if(trace_small_values) write(*,*) sqrt(pth(ix^d)-bigdouble)
1624 write(*,*) "Saving status at the previous time step"
1625 crash=.true.
1626 end if
1627 {end do\}
1628 end if
1629
1630 end subroutine mhd_get_pthermal_semirelati
1631
1632 !> Calculate thermal pressure for semirelativistic MHD
1633 subroutine mhd_get_pthermal_semirelati_pi(w,x,ixI^L,ixO^L,pth)
1636
1637 integer, intent(in) :: ixi^l, ixo^l
1638 double precision, intent(in) :: w(ixi^s,nw)
1639 double precision, intent(in) :: x(ixi^s,1:ndim)
1640 double precision, intent(out):: pth(ixi^s)
1641
1642 double precision :: b(ixo^s,1:ndir), v(ndir), tmp, b2, gamma2, inv_rho, eint, t, rfac
1643 integer :: iw, ix^d
1644
1645 {do ix^db=ixomin^db,ixomax^db\}
1646 b2=(^c&w(ix^d,b^c_)**2+)
1647 if(b2>smalldouble) then
1648 tmp=1.d0/sqrt(b2)
1649 else
1650 tmp=0.d0
1651 end if
1652 ^c&b(ix^d,^c)=w(ix^d,b^c_)*tmp\
1653 tmp=(^c&b(ix^d,^c)*w(ix^d,m^c_)+)
1654
1655 inv_rho=1.d0/w(ix^d,rho_)
1656 ! Va^2/c^2
1657 b2=b2*inv_rho*eos%inv_squared_c
1658 ! equation (15)
1659 gamma2=1.d0/(1.d0+b2)
1660 ! Convert momentum to velocity
1661 ^c&v(^c)=gamma2*(w(ix^d,m^c_)+b2*b(ix^d,^c)*tmp)*inv_rho\
1662
1663 ! E=Bxv
1664 {^ifthreec
1665 b(ix^d,1)=w(ix^d,b2_)*v(3)-w(ix^d,b3_)*v(2)
1666 b(ix^d,2)=w(ix^d,b3_)*v(1)-w(ix^d,b1_)*v(3)
1667 b(ix^d,3)=w(ix^d,b1_)*v(2)-w(ix^d,b2_)*v(1)
1668 }
1669 {^iftwoc
1670 b(ix^d,1)=zero
1671 b(ix^d,2)=w(ix^d,b1_)*v(2)-w(ix^d,b2_)*v(1)
1672 }
1673 {^ifonec
1674 b(ix^d,1)=zero
1675 }
1676 ! internal energy= e-eK-eB-eE
1677 eint=w(ix^d,e_)&
1678 -half*((^c&v(^c)**2+)*w(ix^d,rho_)&
1679 +(^c&w(ix^d,b^c_)**2+)&
1680 +(^c&b(ix^d,^c)**2+)*eos%inv_squared_c)
1681 call state_from_eint_pi(w(ix^d,rho_), eint, t, pth(ix^d), rfac)
1682 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1683 {end do\}
1684
1685 if(check_small_values.and..not.fix_small_values) then
1686 {do ix^db=ixomin^db,ixomax^db\}
1687 if(pth(ix^d)<small_pressure) then
1688 write(*,*) "Error: small value of gas pressure",pth(ix^d),&
1689 " encountered when call mhd_get_pthermal_semirelati"
1690 write(*,*) "Iteration: ", it, " Time: ", global_time
1691 write(*,*) "Location: ", x(ix^d,:)
1692 write(*,*) "Cell number: ", ix^d
1693 do iw=1,nw
1694 write(*,*) trim(cons_wnames(iw)),": ",w(ix^d,iw)
1695 end do
1696 if(trace_small_values) write(*,*) sqrt(pth(ix^d)-bigdouble)
1697 write(*,*) "Saving status at the previous time step"
1698 crash=.true.
1699 end if
1700 {end do\}
1701 end if
1702
1703 end subroutine mhd_get_pthermal_semirelati_pi
1704
1705 !> Calculate thermal pressure=(gamma-1)*(e-0.5*m**2/rho) within ixO^L
1706 subroutine mhd_get_pthermal_hde(w,x,ixI^L,ixO^L,pth)
1709
1710 integer, intent(in) :: ixi^l, ixo^l
1711 double precision, intent(in) :: w(ixi^s,nw)
1712 double precision, intent(in) :: x(ixi^s,1:ndim)
1713 double precision, intent(out):: pth(ixi^s)
1714
1715 integer :: iw, ix^d
1716
1717 {do ix^db= ixomin^db,ixomax^db\}
1718 pth(ix^d)=eos%gamma_minus_1*(w(ix^d,e_)-half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)))
1719 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1720 {end do\}
1721 if(check_small_values.and..not.fix_small_values) then
1722 {do ix^db= ixomin^db,ixomax^db\}
1723 if(pth(ix^d)<small_pressure) then
1724 write(*,*) "Error: small value of gas pressure",pth(ix^d),&
1725 " encountered when call mhd_get_pthermal_hde"
1726 write(*,*) "Iteration: ", it, " Time: ", global_time
1727 write(*,*) "Location: ", x(ix^d,:)
1728 write(*,*) "Cell number: ", ix^d
1729 do iw=1,nw
1730 write(*,*) trim(cons_wnames(iw)),": ",w(ix^d,iw)
1731 end do
1732 if(trace_small_values) write(*,*) sqrt(pth(ix^d)-bigdouble)
1733 write(*,*) "Saving status at the previous time step"
1734 crash=.true.
1735 end if
1736 {end do\}
1737 end if
1738
1739 end subroutine mhd_get_pthermal_hde
1740
1741 !> Temperature routines (moved from mod_mhd_phys.t)
1742
1743 !> Copy temperature from stored Te variable
1744 subroutine mhd_get_temperature_from_te(w, x, ixI^L, ixO^L, res)
1746 integer, intent(in) :: ixi^l, ixo^l
1747 double precision, intent(in) :: w(ixi^s, 1:nw)
1748 double precision, intent(in) :: x(ixi^s, 1:ndim)
1749 double precision, intent(out):: res(ixi^s)
1750 res(ixo^s) = w(ixo^s, te_)
1751 end subroutine mhd_get_temperature_from_te
1752
1753 !> Calculate temperature=p/rho when in e_ the internal energy is stored
1754 subroutine mhd_get_temperature_from_eint(w, x, ixI^L, ixO^L, res)
1756 integer, intent(in) :: ixi^l, ixo^l
1757 double precision, intent(in) :: w(ixi^s, 1:nw)
1758 double precision, intent(in) :: x(ixi^s, 1:ndim)
1759 double precision, intent(out):: res(ixi^s)
1760
1761 double precision :: r(ixi^s)
1762
1763 call eos%get_Rfactor(w,x,ixi^l,ixo^l,r)
1764 res(ixo^s) = eos%gamma_minus_1 * w(ixo^s, e_)/(w(ixo^s,rho_)*r(ixo^s))
1765 end subroutine mhd_get_temperature_from_eint
1766
1767 !> Calculate temperature=p/rho when in e_ the total energy is stored
1768 subroutine mhd_get_temperature_from_etot(w, x, ixI^L, ixO^L, res)
1770 integer, intent(in) :: ixi^l, ixo^l
1771 double precision, intent(in) :: w(ixi^s, 1:nw)
1772 double precision, intent(in) :: x(ixi^s, 1:ndim)
1773 double precision, intent(out):: res(ixi^s)
1774
1775 double precision :: r(ixi^s)
1776
1777 call eos%get_Rfactor(w,x,ixi^l,ixo^l,r)
1778 call mhd_get_pthermal(w,x,ixi^l,ixo^l,res)
1779 res(ixo^s)=res(ixo^s)/(r(ixo^s)*w(ixo^s,rho_))
1780
1781 end subroutine mhd_get_temperature_from_etot
1782
1783 subroutine mhd_get_temperature_from_etot_with_equi(w, x, ixI^L, ixO^L, res)
1785 integer, intent(in) :: ixi^l, ixo^l
1786 double precision, intent(in) :: w(ixi^s, 1:nw)
1787 double precision, intent(in) :: x(ixi^s, 1:ndim)
1788 double precision, intent(out):: res(ixi^s)
1789
1790 double precision :: r(ixi^s)
1791
1792 call eos%get_Rfactor(w,x,ixi^l,ixo^l,r)
1793 call mhd_get_pthermal(w,x,ixi^l,ixo^l,res)
1794 res(ixo^s)=res(ixo^s)/(r(ixo^s)*(w(ixo^s,rho_)+block%equi_vars(ixo^s,equi_rho0_,b0i)))
1795
1796 end subroutine mhd_get_temperature_from_etot_with_equi
1797
1798 subroutine mhd_get_temperature_from_eint_with_equi(w, x, ixI^L, ixO^L, res)
1800 integer, intent(in) :: ixi^l, ixo^l
1801 double precision, intent(in) :: w(ixi^s, 1:nw)
1802 double precision, intent(in) :: x(ixi^s, 1:ndim)
1803 double precision, intent(out):: res(ixi^s)
1804
1805 double precision :: r(ixi^s)
1806
1807 call eos%get_Rfactor(w,x,ixi^l,ixo^l,r)
1808 res(ixo^s) = (eos%gamma_minus_1 * w(ixo^s, e_) + block%equi_vars(ixo^s,equi_pe0_,b0i)) /&
1809 ((w(ixo^s,rho_) +block%equi_vars(ixo^s,equi_rho0_,b0i))*r(ixo^s))
1810
1811 end subroutine mhd_get_temperature_from_eint_with_equi
1812
1813 !> LTE+MHD: T from total energy. Subtract KE+ME (via the MHD-aware
1814 !> phys_e_to_ei dispatcher) to get eint, then look up T in the LTE table
1815 !> through eos%get_temperature_from_eint (bound to get_temperature_from_eint_LTE
1816 !> by eos_finalise). Always fresh; does not use the iw_te cache.
1817 !> Should NOT be bound when mhd_internal_e=.true. (w(:,e_) is already eint
1818 !> and phys_e_to_ei is unbound) - use eos%get_temperature_from_eint directly.
1819 subroutine mhd_get_temperature_from_etot_lte(w, x, ixI^L, ixO^L, res)
1821 integer, intent(in) :: ixi^l, ixo^l
1822 double precision, intent(in) :: w(ixi^s, 1:nw)
1823 double precision, intent(in) :: x(ixi^s, 1:ndim)
1824 double precision, intent(out):: res(ixi^s)
1825
1826 double precision :: wlocal(ixi^s, 1:nw)
1827
1828 wlocal(ixi^s, 1:nw) = w(ixi^s, 1:nw)
1829 ! Subtract KE+ME from w(:,e_) via the MHD-aware dispatcher
1830 call phys_e_to_ei(ixi^l, ixo^l, wlocal, x)
1831 ! Now wlocal(:,e_) is the gas internal energy; LTE table lookup
1832 call eos%get_temperature_from_eint(wlocal, x, ixi^l, ixo^l, res)
1833
1834 end subroutine mhd_get_temperature_from_etot_lte
1835
1836 subroutine mhd_get_temperature_equi(w,x, ixI^L, ixO^L, res)
1838 integer, intent(in) :: ixi^l, ixo^l
1839 double precision, intent(in) :: w(ixi^s, 1:nw)
1840 double precision, intent(in) :: x(ixi^s, 1:ndim)
1841 double precision, intent(out):: res(ixi^s)
1842
1843 double precision :: r(ixi^s)
1844
1845 call eos%get_Rfactor(w,x,ixi^l,ixo^l,r)
1846 res(ixo^s)= block%equi_vars(ixo^s,equi_pe0_,b0i)/(block%equi_vars(ixo^s,equi_rho0_,b0i)*r(ixo^s))
1847
1848 end subroutine mhd_get_temperature_equi
1849
1850 !> Equilibrium extraction routines (moved from mod_mhd_phys.t)
1851
1852 subroutine mhd_get_rho_equi(w, x, ixI^L, ixO^L, res)
1854 integer, intent(in) :: ixi^l, ixo^l
1855 double precision, intent(in) :: w(ixi^s, 1:nw)
1856 double precision, intent(in) :: x(ixi^s, 1:ndim)
1857 double precision, intent(out):: res(ixi^s)
1858 res(ixo^s) = block%equi_vars(ixo^s,equi_rho0_,b0i)
1859 end subroutine mhd_get_rho_equi
1860
1861 subroutine mhd_get_pe_equi(w,x, ixI^L, ixO^L, res)
1863 integer, intent(in) :: ixi^l, ixo^l
1864 double precision, intent(in) :: w(ixi^s, 1:nw)
1865 double precision, intent(in) :: x(ixi^s, 1:ndim)
1866 double precision, intent(out):: res(ixi^s)
1867 res(ixo^s) = block%equi_vars(ixo^s,equi_pe0_,b0i)
1868 end subroutine mhd_get_pe_equi
1869
1870 !> Electron and hydrogen number densities of the background alone, for the
1871 !> equilibrium rate that subtract_equi removes. Splitting is restricted to
1872 !> the FI EoS, so the electron count is the fully ionised one.
1873 subroutine mhd_get_ne_nh_equi(ixI^L, ixO^L, w, x, ne, nH)
1875 integer, intent(in) :: ixi^l, ixo^l
1876 double precision, intent(in) :: w(ixi^s, nw)
1877 double precision, intent(in) :: x(ixi^s, 1:ndim)
1878 double precision, intent(out):: ne(ixi^s), nh(ixi^s)
1879 nh(ixo^s) = block%equi_vars(ixo^s,equi_rho0_,b0i) / eos%nH2rhoFactor
1880 ne(ixo^s) = nh(ixo^s) * eos%neOnH_FI
1881 end subroutine mhd_get_ne_nh_equi
1882
1883 !> Internal energy extraction + small value handling (moved from mod_mhd_phys.t)
1884
1885 !> Get internal energy from conserved state (origin formulation)
1886 function mhd_get_ei_origin(w, ixI^L, ixO^L) result(ei)
1888 integer, intent(in) :: ixi^l, ixo^l
1889 double precision, intent(in) :: w(ixi^s, nw)
1890 double precision :: ei(ixo^s)
1891
1892 ! ei = E_total - e_kinetic - e_magnetic
1893 ei(ixo^s) = w(ixo^s,e_) - half*((^c&w(ixo^s,m^c_)**2+)/w(ixo^s,rho_) &
1894 + (^c&w(ixo^s,b^c_)**2+))
1895 end function mhd_get_ei_origin
1896
1897 !> Get internal energy from conserved state (semirelativistic formulation)
1898 function mhd_get_ei_semirelati(w, ixI^L, ixO^L) result(ei)
1900 integer, intent(in) :: ixi^l, ixo^l
1901 double precision, intent(in) :: w(ixi^s, nw)
1902 double precision :: ei(ixo^s)
1903
1904 double precision :: b(ixo^s,1:ndir), v(ndir), tmp, b2, gamma2, inv_rho
1905 integer :: ix^d
1906
1907 {do ix^db=ixomin^db,ixomax^db\}
1908 b2=(^c&w(ix^d,b^c_)**2+)
1909 if(b2>smalldouble) then
1910 tmp=1.d0/sqrt(b2)
1911 else
1912 tmp=0.d0
1913 end if
1914 ^c&b(ix^d,^c)=w(ix^d,b^c_)*tmp\
1915 tmp=(^c&b(ix^d,^c)*w(ix^d,m^c_)+)
1916
1917 inv_rho=1.d0/w(ix^d,rho_)
1918 ! Va^2/c^2
1919 b2=b2*inv_rho*eos%inv_squared_c
1920 ! equation (15)
1921 gamma2=1.d0/(1.d0+b2)
1922 ! Convert momentum to velocity
1923 ^c&v(^c)=gamma2*(w(ix^d,m^c_)+b2*b(ix^d,^c)*tmp)*inv_rho\
1924
1925 ! E=Bxv
1926 {^ifthreec
1927 b(ix^d,1)=w(ix^d,b2_)*v(3)-w(ix^d,b3_)*v(2)
1928 b(ix^d,2)=w(ix^d,b3_)*v(1)-w(ix^d,b1_)*v(3)
1929 b(ix^d,3)=w(ix^d,b1_)*v(2)-w(ix^d,b2_)*v(1)
1930 }
1931 {^iftwoc
1932 b(ix^d,1)=zero
1933 b(ix^d,2)=w(ix^d,b1_)*v(2)-w(ix^d,b2_)*v(1)
1934 }
1935 {^ifonec
1936 b(ix^d,1)=zero
1937 }
1938 ! internal energy= e-eK-eB-eE
1939 ei(ix^d)=w(ix^d,e_)&
1940 -half*((^c&v(^c)**2+)*w(ix^d,rho_)&
1941 +(^c&w(ix^d,b^c_)**2+)&
1942 +(^c&b(ix^d,^c)**2+)*eos%inv_squared_c)
1943 {end do\}
1944 end function mhd_get_ei_semirelati
1945
1946 !> Get internal energy from conserved state (internal energy formulation)
1947 function mhd_get_ei_inte(w, ixI^L, ixO^L) result(ei)
1949 integer, intent(in) :: ixi^l, ixo^l
1950 double precision, intent(in) :: w(ixi^s, nw)
1951 double precision :: ei(ixo^s)
1952
1953 ! e_ already stores internal energy
1954 ei(ixo^s) = w(ixo^s,e_)
1955 end function mhd_get_ei_inte
1956
1957 ! mhd_handle_small_ei stays in mod_mhd_phys.t (callers are there)
1958
1959 !> Update temperature Te_ from pressure using partial ionization
1960
1961 !> PI energy-mode routines (eos_type='PI', ionE=.true.)
1962 !> Structural mirror of the FI conversion/pthermal/csound family, sharing
1963 !> FI's eq_state_units / RR=1 normalisation; the only difference is that the
1964 !> eint<->p relation is delegated to the portable scalar backend
1965 !> (mod_eos_pi), so eint carries the ionisation-energy term.
1966 !> Origin: w(e_) is total energy; inte: w(e_) is gas internal energy.
1967
1968 !> Primitive pressure -> total energy (origin). m^C_ is still velocity here
1969 !> (momentum conversion follows in to_conserved), so KE = half*rho*v^2.
1970 subroutine mhd_p_to_e_pi(ixI^L,ixO^L,w,x)
1972 integer, intent(in) :: ixi^l, ixo^l
1973 double precision, intent(inout) :: w(ixi^s, nw)
1974 double precision, intent(in) :: x(ixi^s, 1:ndim)
1975
1976 double precision :: eint
1977 integer :: ix^d
1978
1979 {do ix^db=ixomin^db,ixomax^db\}
1980 call eint_from_rho_p_pi(w(ix^d,rho_), w(ix^d,p_), eint)
1981 w(ix^d,e_)=eint &
1982 +half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
1983 +(^c&w(ix^d,b^c_)**2+))
1984 {end do\}
1985
1986 end subroutine mhd_p_to_e_pi
1987
1988 !> Primitive pressure -> gas internal energy (inte formulation)
1989 subroutine mhd_p_to_eint_pi(ixI^L,ixO^L,w,x)
1991 integer, intent(in) :: ixi^l, ixo^l
1992 double precision, intent(inout) :: w(ixi^s, nw)
1993 double precision, intent(in) :: x(ixi^s, 1:ndim)
1994
1995 double precision :: eint
1996 integer :: ix^d
1997
1998 {do ix^db=ixomin^db,ixomax^db\}
1999 call eint_from_rho_p_pi(w(ix^d,rho_), w(ix^d,p_), eint)
2000 w(ix^d,e_)=eint
2001 {end do\}
2002
2003 end subroutine mhd_p_to_eint_pi
2004
2005 !> Primitive -> conserved (origin, total energy)
2006 subroutine mhd_to_conserved_origin_pi(ixI^L,ixO^L,w,x)
2008 integer, intent(in) :: ixi^l, ixo^l
2009 double precision, intent(inout) :: w(ixi^s, nw)
2010 double precision, intent(in) :: x(ixi^s, 1:ndim)
2011
2012 call mhd_p_to_e_pi(ixi^l, ixo^l, w, x)
2013 ^c&w(ixo^s,m^c_)=w(ixo^s,rho_)*w(ixo^s,m^c_)\
2014
2015 end subroutine mhd_to_conserved_origin_pi
2016
2017 !> Primitive -> conserved (internal energy)
2018 subroutine mhd_to_conserved_inte_pi(ixI^L,ixO^L,w,x)
2020 integer, intent(in) :: ixi^l, ixo^l
2021 double precision, intent(inout) :: w(ixi^s, nw)
2022 double precision, intent(in) :: x(ixi^s, 1:ndim)
2023
2024 call mhd_p_to_eint_pi(ixi^l, ixo^l, w, x)
2025 ^c&w(ixo^s,m^c_)=w(ixo^s,rho_)*w(ixo^s,m^c_)\
2026
2027 end subroutine mhd_to_conserved_inte_pi
2028
2029 !> Conserved -> primitive (origin). Subtract KE+ME -> eint, invert -> p.
2030 subroutine mhd_to_primitive_origin_pi(ixI^L,ixO^L,w,x)
2032 integer, intent(in) :: ixi^l, ixo^l
2033 double precision, intent(inout) :: w(ixi^s, nw)
2034 double precision, intent(in) :: x(ixi^s, 1:ndim)
2035
2036 double precision :: inv_rho, eint_val, t, rfac
2037 integer :: ix^d
2038
2039 if (fix_small_values) then
2040 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, &
2041 'mhd_to_primitive_origin_PI')
2042 end if
2043
2044 {do ix^db=ixomin^db,ixomax^db\}
2045 inv_rho = 1.d0/w(ix^d,rho_)
2046 ! Convert momentum to velocity
2047 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
2048 ! eint = E - 0.5*rho*v^2 - 0.5*B^2
2049 eint_val = w(ix^d,e_) &
2050 - half*(w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+) &
2051 + (^c&w(ix^d,b^c_)**2+))
2052 eint_val = max(eint_val, smalldouble)
2053 call state_from_eint_pi(w(ix^d,rho_), eint_val, t, w(ix^d,p_), rfac)
2054 {end do\}
2055
2056 end subroutine mhd_to_primitive_origin_pi
2057
2058 !> Conserved -> primitive (internal energy). e_ already holds eint.
2059 subroutine mhd_to_primitive_inte_pi(ixI^L,ixO^L,w,x)
2061 integer, intent(in) :: ixi^l, ixo^l
2062 double precision, intent(inout) :: w(ixi^s, nw)
2063 double precision, intent(in) :: x(ixi^s, 1:ndim)
2064
2065 double precision :: inv_rho, eint_val, t, rfac
2066 integer :: ix^d
2067
2068 if (fix_small_values) then
2069 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, &
2070 'mhd_to_primitive_inte_PI')
2071 end if
2072
2073 {do ix^db=ixomin^db,ixomax^db\}
2074 eint_val = max(w(ix^d,e_), smalldouble)
2075 call state_from_eint_pi(w(ix^d,rho_), eint_val, t, w(ix^d,p_), rfac)
2076 ! Convert momentum to velocity
2077 inv_rho = 1.d0/w(ix^d,rho_)
2078 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
2079 {end do\}
2080
2081 end subroutine mhd_to_primitive_inte_pi
2082
2083 !> Thermal pressure (origin): subtract KE+ME, invert eint -> p.
2084 subroutine mhd_get_pthermal_origin_pi(w,x,ixI^L,ixO^L,pth)
2086 integer, intent(in) :: ixi^l, ixo^l
2087 double precision, intent(in) :: w(ixi^s,nw)
2088 double precision, intent(in) :: x(ixi^s,1:ndim)
2089 double precision, intent(out):: pth(ixi^s)
2090
2091 double precision :: eint_val, t, rfac
2092 integer :: ix^d
2093
2094 {do ix^db=ixomin^db,ixomax^db\}
2095 eint_val = w(ix^d,e_) &
2096 - half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_) &
2097 + (^c&w(ix^d,b^c_)**2+))
2098 eint_val = max(eint_val, smalldouble)
2099 call state_from_eint_pi(w(ix^d,rho_), eint_val, t, pth(ix^d), rfac)
2100 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
2101 {end do\}
2102
2103 end subroutine mhd_get_pthermal_origin_pi
2104
2105 !> Thermal pressure (inte): e_ holds eint, invert eint -> p.
2106 subroutine mhd_get_pthermal_inte_pi(w,x,ixI^L,ixO^L,pth)
2108 integer, intent(in) :: ixi^l, ixo^l
2109 double precision, intent(in) :: w(ixi^s,nw)
2110 double precision, intent(in) :: x(ixi^s,1:ndim)
2111 double precision, intent(out):: pth(ixi^s)
2112
2113 double precision :: eint_val, t, rfac
2114 integer :: ix^d
2115
2116 {do ix^db=ixomin^db,ixomax^db\}
2117 eint_val = max(w(ix^d,e_), smalldouble)
2118 call state_from_eint_pi(w(ix^d,rho_), eint_val, t, pth(ix^d), rfac)
2119 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
2120 {end do\}
2121
2122 end subroutine mhd_get_pthermal_inte_pi
2123
2124 !> Adiabatic sound speed squared from primitive (rho, p).
2125
2126 !> Effective Gamma1 = cs2 * rho / p (for the same primitive state).
2127
2128 !> PI energy mode: refresh Te_ from gas internal energy via the backend
2129 !> eint->T inversion (direct, so no lagged wCT -- unlike no-energy
2130 !> mhd_update_temperature which lags iz_H via wCT(Te_)). Uses MHD's Te_ index
2131 !> (PI registers Te via var_set_auxvar, so the generic iw_te is unset).
2132
2133 !> PI prominence (T,p) routines (eos_type='PI', pi_table='prominence').
2134 !> Prominence ionisation depends on (T,p), not T alone, so the chromosphere
2135 !> T-only seam does not apply. Following the collaborator's design, R and T
2136 !> are recomputed from (rho, pth) via ionization_get_state, which dispatches
2137 !> the prominence (pressure-bisection) inversion internally. No-energy only
2138 !> (guarded in eos_validate_params), so pth = (gamma-1)*eint is R-independent
2139 !> and obtained from phys_get_ei (conserved w; KE/ME removed).
2140
2141 !> R-factor for the prominence (T,p) table: recompute from (rho, pth).
2142
2143 !> Refresh stored Te_ for the prominence table from (rho, pth).
2144
2145end module mod_mhd_eos
2146!> Needs a line after to pass the preprocesor
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
EoS state container – the single thermodynamic authority for AMRVAC.
type(eos_container), allocatable, public eos
The single EoS state object, allocated in eos_init and shared (read-mostly) across all EoS sub-module...
Analytic H-only Saha EoS (eos_method == 'analytic').
LTE (Saha-table) EoS kernels and finalise for the eos% family.
Definition mod_eos_lte.t:12
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...
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 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
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
Equation of state for AMRVAC, handled through a single eos_container object.
Definition mod_eos.t:30
This module contains definitions of global parameters and variables and some generic functions/subrou...
type(state), pointer block
Block pointer for using one block and its previous state.
integer, parameter ndim
Number of spatial dimensions for grid variables.
integer b0i
background magnetic field location indicator
integer ndir
Number of spatial dimensions (components) for vector variables.
double precision, dimension(:), allocatable, parameter d
logical fix_small_values
fix small values with average or replace methods
MHD <-> EoS seam: binds the eos% authority into magnetohydrodynamics.
Definition mod_mhd_eos.t:16
subroutine, public mhd_link_eos()
Link the appropriate EOS conversion routines based on the selected EoS type.
Definition mod_mhd_eos.t:52
procedure(sub_get_pthermal), pointer, public mhd_get_temperature
Temperature pointer: set by mhd_link_eos based on EoS type and energy formulation.
Definition mod_mhd_eos.t:41
procedure(sub_get_pthermal), pointer, public mhd_get_pthermal
Thermal pressure pointer: set by mhd_link_eos based on energy formulation. Internal to mod_mhd_eos — ...
Definition mod_mhd_eos.t:38
procedure(sub_convert), pointer, public mhd_to_conserved
use habitual name of converting to conserved
Definition mod_mhd_eos.t:45
procedure(sub_convert), pointer, public mhd_to_primitive
use habitual name of converting to primitive
Definition mod_mhd_eos.t:43
Magneto-hydrodynamics module.
Definition mod_mhd_phys.t:2
integer, public, protected c_
logical, public, protected mhd_internal_e
Whether internal energy is solved instead of total energy.
integer, public, protected wkminus_
type(tc_fluid), allocatable, public tc_fl
type of fluid for thermal conduction
logical, public, protected mhd_semirelativistic
Whether semirelativistic MHD equations (Gombosi 2002 JCP) are solved.
integer, public, protected m
type(te_fluid), allocatable, public te_fl_mhd
type of fluid for thermal emission synthesis
logical, public has_equi_rho_and_p
whether split off equilibrium density and pressure
integer, public, protected waminus_
logical, public, protected mhd_energy
Whether an energy equation is used.
type(fld_fluid), allocatable, public fld_fl
Radiation fluid object (gas-EoS callbacks for FLD), wired in mhd_link_eos.
integer, public, protected waplus_
Conserved wave-energy indices. The plus variables propagate against B.
subroutine, public mhd_uawsom_rho2_factor(ixil, ixol, w, x, factor)
integer, public, protected p_
Index of the gas pressure (-1 if not present) should equal e_.
integer, public, protected c
Indices of the momentum density for the form of better vectorization.
integer, public, protected b
logical, public, protected mhd_hydrodynamic_e
Whether hydrodynamic energy is solved instead of total energy.
type(rc_fluid), allocatable, public rc_fl
type of fluid for radiative cooling
integer, public, protected wkplus_
integer, public, protected rho_
Index of the density (in the w array)
integer, public, protected e_
Index of the energy density (-1 if not present)
logical, public, protected mhd_uawsom
Enable the Uniturbulence and Alfven Wave Solar Model extension.
logical, public mhd_equi_thermal
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
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
Module with all the methods that users can customize in AMRVAC.
procedure(rfactor), pointer usr_rfactor
procedure(set_adiab), pointer usr_set_adiab
procedure(set_adiab), pointer usr_set_gamma