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
29
30 use mod_comm_lib, only: mpistop
31
32 implicit none
33 private
34
35 !> Thermal pressure pointer: set by mhd_link_eos based on energy formulation.
36 !> Internal to mod_mhd_eos — external callers use eos%get_thermal_pressure.
37 procedure(sub_get_pthermal), pointer, public :: mhd_get_pthermal => null()
38
39 !> Temperature pointer: set by mhd_link_eos based on EoS type and energy formulation.
40 procedure(sub_get_pthermal), pointer, public :: mhd_get_temperature => null()
41 !> use habitual name of converting to primitive
42 procedure(sub_convert), pointer, public :: mhd_to_primitive => null()
43 !> use habitual name of converting to conserved
44 procedure(sub_convert), pointer, public :: mhd_to_conserved => null()
45
46 public :: mhd_link_eos
47contains
48
49 !> Link the appropriate EOS conversion routines based on the selected EoS type
50 subroutine mhd_link_eos()
52
53 !> Primitive <-> Conserved routing
54 ! PI (partial approximations) takes the FI conversions as its BASE: in
55 ! no-energy mode this is exact (e_int = p/(gamma-1); the variable R-factor
56 ! enters only via eos%get_Rfactor, below). In energy mode (eos%ionE) the
57 ! PI override block at the end of this routine repoints to_conserved/
58 ! to_primitive (and p_to_e/pthermal/csound) at the PI-energy variants, so
59 ! the FI assignment here is just a harmless base that gets replaced.
60 if (eos%eos_type == 'FI' .or. eos%eos_type == 'PI') then
61 ! PI takes its electron count from a variable R factor evaluated on
62 ! the rho slot, which under splitting holds only the perturbation.
63 if (eos%eos_type == 'PI' .and. has_equi_rho_and_p) &
64 call mpistop('PI EoS not supported with equilibrium splitting')
65 if(mhd_hydrodynamic_e) then
66 if(eos%ionE) call mpistop('PI energy EoS not supported with mhd_hydrodynamic_e')
67 eos%to_primitive => mhd_to_primitive_hde
68 eos%to_conserved => mhd_to_conserved_hde
69 phys_get_pthermal => mhd_get_pthermal_hde
70 mhd_get_pthermal => mhd_get_pthermal_hde
71 else if(mhd_semirelativistic) then
72 if(mhd_energy) then
73 ! the eint <-> p step inside dispatches on the EoS
74 eos%to_primitive => mhd_to_primitive_semirelati
75 eos%to_conserved => mhd_to_conserved_semirelati
76 if(mhd_internal_e .and. eos%ionE) then
77 phys_get_pthermal => mhd_get_pthermal_inte_pi
78 else if(mhd_internal_e) then
79 phys_get_pthermal => mhd_get_pthermal_inte
80 else
81 phys_get_pthermal => mhd_get_pthermal_semirelati
82 end if
84 else
85 eos%to_primitive => mhd_to_primitive_semirelati_noe
86 eos%to_conserved => mhd_to_conserved_semirelati_noe
87 phys_get_pthermal => mhd_get_pthermal_noe
88 mhd_get_pthermal => mhd_get_pthermal_noe
89 end if
90 else
91 if(has_equi_rho_and_p) then
92 eos%to_primitive => mhd_to_primitive_split_rho
93 eos%to_conserved => mhd_to_conserved_split_rho
94 else if(mhd_internal_e) then
95 if(eos%ionE) then
96 eos%to_conserved => mhd_to_conserved_inte_pi
97 eos%to_primitive => mhd_to_primitive_inte_pi
98 eos%p_to_e => mhd_p_to_eint_pi
99 phys_get_pthermal => mhd_get_pthermal_inte_pi
100 mhd_get_pthermal => mhd_get_pthermal_inte_pi
101 else
102 eos%to_primitive => mhd_to_primitive_inte
103 eos%to_conserved => mhd_to_conserved_inte
104 phys_get_pthermal => mhd_get_pthermal_inte
105 mhd_get_pthermal => mhd_get_pthermal_inte
106 end if
107 else if(mhd_energy) then
108 if(eos%ionE) then
109 eos%to_conserved => mhd_to_conserved_origin_pi
110 eos%to_primitive => mhd_to_primitive_origin_pi
111 eos%p_to_e => mhd_p_to_e_pi
112 phys_get_pthermal => mhd_get_pthermal_origin_pi
113 mhd_get_pthermal => mhd_get_pthermal_origin_pi
114 else
115 eos%to_primitive => mhd_to_primitive_origin
116 eos%to_conserved => mhd_to_conserved_origin
117 phys_get_pthermal => mhd_get_pthermal_origin
118 mhd_get_pthermal => mhd_get_pthermal_origin
119 end if
120 else
121 eos%to_primitive => mhd_to_primitive_origin_noe
122 eos%to_conserved => mhd_to_conserved_origin_noe
123 phys_get_pthermal => mhd_get_pthermal_noe
124 mhd_get_pthermal => mhd_get_pthermal_noe
125 end if
126 end if
127 else if (eos%eos_type == 'LTE') then
128 ! Guard unsupported combinations
129 if (mhd_hydrodynamic_e) &
130 call mpistop('LTE EoS not supported with mhd_hydrodynamic_e')
131 if (has_equi_rho_and_p) &
132 call mpistop('LTE EoS not supported with equilibrium splitting')
133 if (.not. mhd_energy) &
134 call mpistop('LTE EoS requires mhd_energy=.true.')
135 ! Route LTE conversion based on energy formulation
136 if (mhd_semirelativistic) then
137 eos%to_primitive => mhd_to_primitive_semirelati
138 eos%to_conserved => mhd_to_conserved_semirelati
139 else if (mhd_internal_e) then
140 eos%to_primitive => mhd_to_primitive_inte_lte
141 eos%to_conserved => mhd_to_conserved_inte_lte
142 else
143 eos%to_primitive => mhd_to_primitive_origin_lte
144 eos%to_conserved => mhd_to_conserved_origin_lte
145 end if
146 else
147 call mpistop('Error: Unknown MHD EOS type: ' // trim(eos%eos_type))
148 end if
149
150 phys_to_primitive => eos%to_primitive
151 phys_to_conserved => eos%to_conserved
152 phys_bind_eos_to_source => bind_eos_to_source
153
154 !> p_to_e (pressure -> total energy for origin, or eint for inte)
155 if (mhd_internal_e) then
156 eos%p_to_e => mhd_p_to_eint
157 else
158 eos%p_to_e => mhd_p_to_e
159 end if
160
161 !> Sound speed and Gamma1
162 if (eos%eos_type == 'LTE' .and. eos%ionE) then
163 eos%get_csound2 => mhd_get_csound2_lte
164 phys_get_gamma1 => mhd_get_gamma1_lte
165 else if (eos%eos_type == 'PI' .and. eos%ionE) then
166 eos%get_csound2 => mhd_get_csound2_fi
168 else
169 eos%get_csound2 => mhd_get_csound2_fi
170 phys_get_gamma1 => get_gamma1_fi
171 end if
172
173 !> Prolongation
174 if (eos%eos_type == 'LTE' .and. eos%ionE) then
175 phys_to_prolong => mhd_to_prolong_lte
176 phys_from_prolong => mhd_from_prolong_lte
177 end if
178
179 !> Rfactor: only the FI case is physics-dependent (the usr_Rfactor user
180 !> hook). The LTE and PI bindings are pure eos% routines, so they are set
181 !> in eos_finalise_{LTE,PI} (which run after this and before
182 !> bind_eos_to_source, so they win) -- keeping those targets private.
183 if(associated(usr_rfactor)) then
184 eos%get_Rfactor => usr_rfactor
185 else
186 eos%get_Rfactor => rfactor_from_constant_ionization
187 end if
188
189 ! For LTE, override with version that delegates to eos%get_thermal_pressure
190 if (eos%eos_type == 'LTE') then
191 phys_get_pthermal => mhd_get_pthermal_lte
192 mhd_get_pthermal => mhd_get_pthermal_lte
193 end if
194 ! Single entry point: all callers use eos%get_thermal_pressure
195 eos%get_thermal_pressure => mhd_get_pthermal
196
197 !> Temperature routing
198 if(eos%eos_type == 'PI' .or. eos%eos_type == 'LTE') then
199 mhd_get_temperature => mhd_get_temperature_from_te
200 else
201 if(mhd_internal_e) then
202 if(has_equi_rho_and_p) then
203 mhd_get_temperature => mhd_get_temperature_from_eint_with_equi
204 else
205 mhd_get_temperature => mhd_get_temperature_from_eint
206 end if
207 else
208 if(has_equi_rho_and_p) then
209 mhd_get_temperature => mhd_get_temperature_from_etot_with_equi
210 else
211 mhd_get_temperature => mhd_get_temperature_from_etot
212 end if
213 end if
214 end if
215
216 !> Internal energy extraction
217 if(mhd_internal_e) then
218 phys_get_ei => mhd_get_ei_inte
219 else if(mhd_energy) then
220 if(mhd_semirelativistic) then
221 phys_get_ei => mhd_get_ei_semirelati
222 else
223 phys_get_ei => mhd_get_ei_origin
224 end if
225 end if
226
227 ! phys_e_to_ei / phys_ei_to_e assigned in mhd_phys_init (mod_mhd_phys.t)
228 mhd_to_primitive => eos%to_primitive
229 mhd_to_conserved => eos%to_conserved
230 phys_to_primitive => eos%to_primitive
231 phys_to_conserved => eos%to_conserved
232 eos%get_thermal_pressure => mhd_get_pthermal
233
234 end subroutine mhd_link_eos
235
236 !> Called by eos_finalise via phys_bind_eos_to_source to link
237 !> TC and RC modules to EoS-aware function pointers.
238 subroutine bind_eos_to_source()
239
240 ! Override eos%get_temperature_from_{etot,eint} per (eos_type, internal_e, equi).
241 ! Runs AFTER eos_finalise's unconditional generic-helper assignment.
242 ! Two semantically distinct hooks:
243 ! - from_etot: caller's w(:,e_) is total energy; subtract KE+ME before T.
244 ! - from_eint: caller's w(:,e_) is gas internal energy; compute T directly.
245 ! In mhd_internal_e mode w(:,e_) is already eint, so the etot hook is bound
246 ! to the same routine as the eint hook (no subtraction)
247 if (eos%eos_type == 'LTE' .or. (eos%eos_type == 'PI' .and. eos%ionE)) then
248 ! eint carries the ionisation energy, so T comes from the EoS's own
249 ! eint->T inversion (LTE tables, or the PI energy-mode backend)
250 if (mhd_internal_e) then
251 ! w(:,e_) is eint; both hooks do the EoS lookup directly
252 eos%get_temperature_from_etot => eos%get_temperature_from_eint
253 else
254 ! Total-energy: subtract KE+ME (via phys_e_to_ei), then the EoS eint->T lookup
255 eos%get_temperature_from_etot => mhd_get_temperature_from_etot_lte
256 end if
257 else ! FI, or PI without ionisation energy
258 if (mhd_internal_e) then
259 ! w(:,e_) is eint; both hooks share the eint variant.
260 if (has_equi_rho_and_p) then
261 eos%get_temperature_from_etot => mhd_get_temperature_from_eint_with_equi
262 eos%get_temperature_from_eint => mhd_get_temperature_from_eint_with_equi
263 else
264 eos%get_temperature_from_etot => mhd_get_temperature_from_eint
265 eos%get_temperature_from_eint => mhd_get_temperature_from_eint
266 end if
267 else
268 ! Total-energy: distinct etot/eint paths.
269 if (has_equi_rho_and_p) then
270 eos%get_temperature_from_etot => mhd_get_temperature_from_etot_with_equi
271 eos%get_temperature_from_eint => mhd_get_temperature_from_eint_with_equi
272 else
273 eos%get_temperature_from_etot => mhd_get_temperature_from_etot
274 eos%get_temperature_from_eint => mhd_get_temperature_from_eint
275 end if
276 end if
277 end if
278
279 if (allocated(tc_fl)) then
280 call eos_bind_tc(tc_fl, .not. associated(eos%get_Rfactor, rfactor_from_constant_ionization))
281 ! Equilibrium-specific pointers
283 tc_fl%subtract_equi = .true.
284 tc_fl%get_temperature_equi => mhd_get_temperature_equi
285 tc_fl%get_rho_equi => mhd_get_rho_equi
286 else
287 tc_fl%subtract_equi = .false.
288 end if
289 end if
290
291 if (allocated(rc_fl)) then
292 rc_fl%get_rho => eos%get_rho
293 rc_fl%get_pthermal => eos%get_thermal_pressure
294 rc_fl%get_var_Rfactor => eos%get_Rfactor
295 rc_fl%get_Te => eos%get_Te
296 rc_fl%get_ne_nH => eos%get_ne_nH
297 nullify(rc_fl%get_rho2_factor)
298 if(mhd_uawsom) rc_fl%get_rho2_factor => mhd_uawsom_rho2_factor
299 rc_fl%ionE = eos%ionE
300 rc_fl%method_analytic = (eos%method_id == eos_analytic)
301 rc_fl%inv_gamma_minus_1 = eos%inv_gamma_minus_1
302 rc_fl%nH2rhoFactor = eos%nH2rhoFactor
303 rc_fl%eion_per_nH = eos%eion_per_nH
304 rc_fl%eint_from_T => eint_nh_from_t
305 rc_fl%log_T_floor = eos_get_log_t_floor()
306 rc_fl%p2eint => p2eint_from_nh_p
307 rc_fl%T_from_eint => t_from_nh_eint
308 rc_fl%y_from_eint => y_from_nh_eint
310 ! Equilibrium-specific pointers
312 rc_fl%subtract_equi = .true.
313 rc_fl%get_rho_equi => mhd_get_rho_equi
314 rc_fl%get_pthermal_equi => mhd_get_pe_equi
315 rc_fl%get_ne_nH_equi => mhd_get_ne_nh_equi
316 rc_fl%get_temperature_equi => mhd_get_temperature_equi
317 else
318 rc_fl%subtract_equi = .false.
319 end if
320 !> Build the variable-c_V Townsend Y_mod table now that all
321 !> EoS tables (eint_from_T, T, neOnH) are in code units.
322 !> build_Y_mod_table checks coolmethod=='exact' and .not.isPPL
323 !> internally and early-returns otherwise. Feed it the inverse-table nH
324 !> grid via the port so it never touches eos% directly. LTE only:
325 !> eos_get_eintT_grid reads the LTE eintT grid, and PI (T-only) cooling
326 !> runs the classical Townsend path (Y_mod-for-PI is a later refinement).
327 if (eos%ionE .and. eos%eos_type == 'LTE') then
328 call eos_get_eintt_grid(rc_fl%Y_mod_n_nH, &
329 rc_fl%Y_mod_lg_nH_min, rc_fl%Y_mod_lg_nH_max)
331 end if
332 end if
333
334 !> PI energy mode: the eint<->T scalar callbacks wired above are the
335 !> LTE-table lookups, which PI does not load. Repoint cooling/conduction
336 !> at the ionisation backend (rho=nH=1 per H). Classical Townsend; Y_mod
337 !> is not built for PI (guard above), so the classical branch is used.
338 if (eos%eos_type == 'PI' .and. eos%ionE) then
339 if (allocated(tc_fl)) tc_fl%eint_from_T => eint_from_t_pi
340 if (allocated(rc_fl)) then
341 rc_fl%eint_from_T => eint_from_t_pi
342 rc_fl%p2eint => p2eint_pi
343 rc_fl%T_from_eint => t_from_eint_pi
344 rc_fl%y_from_eint => y_from_eint_pi
345 end if
346 end if
347
348 if (allocated(te_fl_mhd)) then
349 te_fl_mhd%get_rho => eos%get_rho
350 te_fl_mhd%get_pthermal => eos%get_thermal_pressure
351 te_fl_mhd%get_var_Rfactor => eos%get_Rfactor
352 te_fl_mhd%get_ne_nH => eos%get_ne_nH
353 end if
354
355 if (allocated(fld_fl)) then
356 !> Radiation (FLD) fluid: gas-EoS callbacks. get_temperature_from_pressure
357 !> is the FI T=p/(R*rho) routine (the LTE variant is a later pass).
358 fld_fl%gamma = eos%gamma
359 fld_fl%get_tgas => eos%get_temperature_from_pressure
360 fld_fl%get_Rfactor => eos%get_Rfactor
361 end if
362
363 end subroutine bind_eos_to_source
364
365 !> FI conversion routines (signatures updated for convert_condition interface)
366
367 !> Transform primitive variables into conservative ones (origin, total energy)
368 subroutine mhd_to_conserved_origin(ixI^L,ixO^L,w,x)
370 integer, intent(in) :: ixi^l, ixo^l
371 double precision, intent(inout) :: w(ixi^s, nw)
372 double precision, intent(in) :: x(ixi^s, 1:ndim)
373
374 integer :: ix^d
375
376 {do ix^db=ixomin^db,ixomax^db\}
377 ! Calculate total energy from pressure, kinetic and magnetic energy
378 w(ix^d,e_)=w(ix^d,p_)*eos%inv_gamma_minus_1&
379 +half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
380 +(^c&w(ix^d,b^c_)**2+))
381 if(mhd_uawsom) w(ix^d,e_)=w(ix^d,e_)+&
382 w(ix^d,waplus_)+w(ix^d,waminus_)+w(ix^d,wkplus_)+w(ix^d,wkminus_)
383 ! Convert velocity to momentum
384 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)\
385 {end do\}
386
387 end subroutine mhd_to_conserved_origin
388
389 !> Transform primitive variables into conservative ones (no energy)
390 subroutine mhd_to_conserved_origin_noe(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 ! Convert velocity to momentum
400 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)\
401 {end do\}
402
403 end subroutine mhd_to_conserved_origin_noe
404
405 !> Transform primitive variables into conservative ones (hydrodynamic energy)
406 subroutine mhd_to_conserved_hde(ixI^L,ixO^L,w,x)
408 integer, intent(in) :: ixi^l, ixo^l
409 double precision, intent(inout) :: w(ixi^s, nw)
410 double precision, intent(in) :: x(ixi^s, 1:ndim)
411
412 integer :: ix^d
413
414 {do ix^db=ixomin^db,ixomax^db\}
415 ! Calculate total energy from pressure, kinetic and magnetic energy
416 w(ix^d,e_)=w(ix^d,p_)*eos%inv_gamma_minus_1&
417 +half*(^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)
418 ! Convert velocity to momentum
419 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)\
420 {end do\}
421
422 end subroutine mhd_to_conserved_hde
423
424 !> Transform primitive variables into conservative ones (internal energy)
425 subroutine mhd_to_conserved_inte(ixI^L,ixO^L,w,x)
427 integer, intent(in) :: ixi^l, ixo^l
428 double precision, intent(inout) :: w(ixi^s, nw)
429 double precision, intent(in) :: x(ixi^s, 1:ndim)
430
431 integer :: ix^d
432
433 {do ix^db=ixomin^db,ixomax^db\}
434 ! Calculate internal energy from pressure
435 w(ix^d,e_)=w(ix^d,p_)*eos%inv_gamma_minus_1
436 ! Convert velocity to momentum
437 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)\
438 {end do\}
439
440 end subroutine mhd_to_conserved_inte
441
442 !> Transform primitive variables into conservative ones (split rho)
443 subroutine mhd_to_conserved_split_rho(ixI^L,ixO^L,w,x)
445 integer, intent(in) :: ixi^l, ixo^l
446 double precision, intent(inout) :: w(ixi^s, nw)
447 double precision, intent(in) :: x(ixi^s, 1:ndim)
448
449 double precision :: rho
450 integer :: ix^d
451
452 {do ix^db=ixomin^db,ixomax^db\}
453 rho=w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i)
454 ! Calculate total energy from pressure, kinetic and magnetic energy
455 w(ix^d,e_)=w(ix^d,p_)*eos%inv_gamma_minus_1&
456 +half*((^c&w(ix^d,m^c_)**2+)*rho&
457 +(^c&w(ix^d,b^c_)**2+))
458 ! Convert velocity to momentum
459 ^c&w(ix^d,m^c_)=rho*w(ix^d,m^c_)\
460 {end do\}
461
462 end subroutine mhd_to_conserved_split_rho
463
464 !> Transform primitive variables into conservative ones (semirelativistic, any EoS)
465 subroutine mhd_to_conserved_semirelati(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 :: efd(1:ndir), s(1:ndir), eint(ixo^s)
472 double precision :: btot(1:ndir)
473 integer :: ix^d
474
475 {do ix^db=ixomin^db,ixomax^db\}
476 ! this loop can not be vectorized
477 eint(ix^d)=mhd_semirelati_eint(w(ix^d,p_),w(ix^d,rho_))
478 {end do\}
479 {do ix^db=ixomin^db,ixomax^db\}
480 ! fully vectorized loop, E, S, e_, m^C_ all in one loop for speed
481 ^c&btot(^c)=w(ix^d,b^c_)\
482 if(b0field) then
483 ^c&btot(^c)=btot(^c)+block%B0(ix^d,^c,b0i)\
484 end if
485 {^ifthreec
486 efd(1)=btot(2)*w(ix^d,m3_)-btot(3)*w(ix^d,m2_)
487 efd(2)=btot(3)*w(ix^d,m1_)-btot(1)*w(ix^d,m3_)
488 efd(3)=btot(1)*w(ix^d,m2_)-btot(2)*w(ix^d,m1_)
489 s(1)=efd(2)*btot(3)-efd(3)*btot(2)
490 s(2)=efd(3)*btot(1)-efd(1)*btot(3)
491 s(3)=efd(1)*btot(2)-efd(2)*btot(1)
492 }
493 {^iftwoc
494 efd(1)=zero
495 ! switch 3 with 2 to add 3 when ^C from 1 to 2
496 efd(2)=btot(1)*w(ix^d,m2_)-btot(2)*w(ix^d,m1_)
497 s(1)=-efd(2)*btot(2)
498 s(2)=efd(2)*btot(1)
499 }
500 {^ifonec
501 efd(1)=zero
502 s(1)=zero
503 }
504 if(mhd_internal_e) then
505 w(ix^d,e_)=eint(ix^d)
506 else
507 ! equation (9), the magnetic energy from the stored slot (eq 92 under splitting)
508 w(ix^d,e_)=eint(ix^d)+half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
509 +(^c&w(ix^d,b^c_)**2+)&
510 +(^c&efd(^c)**2+)*eos%inv_squared_c)
511 end if
512 ! Convert velocity to momentum, equation (9)
513 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)+s(^c)*eos%inv_squared_c\
514 {end do\}
515
516 end subroutine mhd_to_conserved_semirelati
517
518 subroutine mhd_to_conserved_semirelati_noe(ixI^L,ixO^L,w,x)
520 integer, intent(in) :: ixi^l, ixo^l
521 double precision, intent(inout) :: w(ixi^s, nw)
522 double precision, intent(in) :: x(ixi^s, 1:ndim)
523
524 double precision :: efd(1:ndir), s(1:ndir)
525 double precision :: btot(1:ndir)
526 integer :: ix^d
527
528 {do ix^db=ixomin^db,ixomax^db\}
529 ! fully vectorized loop, E, S, e_, m^C_ all in one loop for speed
530 ^c&btot(^c)=w(ix^d,b^c_)\
531 if(b0field) then
532 ^c&btot(^c)=btot(^c)+block%B0(ix^d,^c,b0i)\
533 end if
534 {^ifthreec
535 efd(1)=btot(2)*w(ix^d,m3_)-btot(3)*w(ix^d,m2_)
536 efd(2)=btot(3)*w(ix^d,m1_)-btot(1)*w(ix^d,m3_)
537 efd(3)=btot(1)*w(ix^d,m2_)-btot(2)*w(ix^d,m1_)
538 s(1)=efd(2)*btot(3)-efd(3)*btot(2)
539 s(2)=efd(3)*btot(1)-efd(1)*btot(3)
540 s(3)=efd(1)*btot(2)-efd(2)*btot(1)
541 }
542 {^iftwoc
543 efd(1)=zero
544 ! switch 3 with 2 to add 3 when ^C from 1 to 2
545 efd(2)=btot(1)*w(ix^d,m2_)-btot(2)*w(ix^d,m1_)
546 s(1)=-efd(2)*btot(2)
547 s(2)=efd(2)*btot(1)
548 }
549 {^ifonec
550 efd(1)=zero
551 s(1)=zero
552 }
553 ! Convert velocity to momentum, equation (9)
554 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)+s(^c)*eos%inv_squared_c\
555 {end do\}
556
557 end subroutine mhd_to_conserved_semirelati_noe
558
559 !> Transform conservative variables into primitive ones (origin)
560 subroutine mhd_to_primitive_origin(ixI^L,ixO^L,w,x)
562 integer, intent(in) :: ixi^l, ixo^l
563 double precision, intent(inout) :: w(ixi^s, nw)
564 double precision, intent(in) :: x(ixi^s, 1:ndim)
565
566 double precision :: inv_rho
567 integer :: ix^d
568
569 if (fix_small_values) then
570 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_origin')
571 end if
572
573 {do ix^db=ixomin^db,ixomax^db\}
574 inv_rho = 1.d0/w(ix^d,rho_)
575 ! Convert momentum to velocity
576 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
577 ! Calculate pressure = (gamma-1) * (e-ek-eb)
578 w(ix^d,p_)=eos%gamma_minus_1*(w(ix^d,e_)&
579 -half*(w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+)&
580 +(^c&w(ix^d,b^c_)**2+))&
581 -mhd_uawsom_wave_energy_cell(w(ix^d,:)))
582 {end do\}
583
584 end subroutine mhd_to_primitive_origin
585
586 !> Transform conservative variables into primitive ones (no energy)
587 subroutine mhd_to_primitive_origin_noe(ixI^L,ixO^L,w,x)
589 integer, intent(in) :: ixi^l, ixo^l
590 double precision, intent(inout) :: w(ixi^s, nw)
591 double precision, intent(in) :: x(ixi^s, 1:ndim)
592
593 double precision :: inv_rho
594 integer :: ix^d
595
596 if (fix_small_values) then
597 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_origin_noe')
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 {end do\}
605
606 end subroutine mhd_to_primitive_origin_noe
607
608 !> Transform conservative variables into primitive ones (hde)
609 subroutine mhd_to_primitive_hde(ixI^L,ixO^L,w,x)
611 integer, intent(in) :: ixi^l, ixo^l
612 double precision, intent(inout) :: w(ixi^s, nw)
613 double precision, intent(in) :: x(ixi^s, 1:ndim)
614
615 double precision :: inv_rho
616 integer :: ix^d
617
618 if (fix_small_values) then
619 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_hde')
620 end if
621
622 {do ix^db=ixomin^db,ixomax^db\}
623 inv_rho = 1.d0/w(ix^d,rho_)
624 ! Convert momentum to velocity
625 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
626 ! Calculate pressure = (gamma-1) * (e-ek)
627 w(ix^d,p_)=eos%gamma_minus_1*(w(ix^d,e_)-half*w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+))
628 {end do\}
629
630 end subroutine mhd_to_primitive_hde
631
632 !> Transform conservative variables into primitive ones (internal energy)
633 subroutine mhd_to_primitive_inte(ixI^L,ixO^L,w,x)
635 integer, intent(in) :: ixi^l, ixo^l
636 double precision, intent(inout) :: w(ixi^s, nw)
637 double precision, intent(in) :: x(ixi^s, 1:ndim)
638
639 double precision :: inv_rho
640 integer :: ix^d
641
642 if (fix_small_values) then
643 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_inte')
644 end if
645
646 {do ix^db=ixomin^db,ixomax^db\}
647 ! Calculate pressure = (gamma-1) * e_internal
648 w(ix^d,p_)=w(ix^d,e_)*eos%gamma_minus_1
649 ! Convert momentum to velocity
650 inv_rho = 1.d0/w(ix^d,rho_)
651 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
652 {end do\}
653
654 end subroutine mhd_to_primitive_inte
655
656 !> Transform conservative variables into primitive ones (split rho)
657 subroutine mhd_to_primitive_split_rho(ixI^L,ixO^L,w,x)
659 integer, intent(in) :: ixi^l, ixo^l
660 double precision, intent(inout) :: w(ixi^s, nw)
661 double precision, intent(in) :: x(ixi^s, 1:ndim)
662
663 double precision :: inv_rho
664 integer :: ix^d
665
666 if (fix_small_values) then
667 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_split_rho')
668 end if
669
670 {do ix^db=ixomin^db,ixomax^db\}
671 inv_rho=1.d0/(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
672 ! Convert momentum to velocity
673 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
674 ! Calculate pressure = (gamma-1) * (e-ek-eb)
675 w(ix^d,p_)=eos%gamma_minus_1*(w(ix^d,e_)&
676 -half*((w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))*&
677 (^c&w(ix^d,m^c_)**2+)+(^c&w(ix^d,b^c_)**2+)))
678 {end do\}
679
680 end subroutine mhd_to_primitive_split_rho
681
682 !> Transform conservative variables into primitive ones (semirelativistic, any EoS)
683 subroutine mhd_to_primitive_semirelati(ixI^L,ixO^L,w,x)
685 integer, intent(in) :: ixi^l, ixo^l
686 double precision, intent(inout) :: w(ixi^s, nw)
687 double precision, intent(in) :: x(ixi^s, 1:ndim)
688
689 double precision :: btot(1:ndir), efd(1:ndir)
690 double precision :: b2, tmp, inv_rho, rhoc2
691 integer :: ix^d
692
693 if (fix_small_values) then
694 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_semirelati')
695 end if
696
697 {do ix^db=ixomin^db,ixomax^db\}
698 ^c&btot(^c)=w(ix^d,b^c_)\
699 if(b0field) then
700 ^c&btot(^c)=btot(^c)+block%B0(ix^d,^c,b0i)\
701 end if
702 ! Keppens's formula to convert momentum to velocity
703 b2=(^c&btot(^c)**2+)
704 tmp=(^c&btot(^c)*w(ix^d,m^c_)+)
705 rhoc2=w(ix^d,rho_)*mhd_squared_c
706 inv_rho=1.d0/(w(ix^d,rho_)*(rhoc2+b2))
707 ^c&w(ix^d,m^c_)=(rhoc2*w(ix^d,m^c_)+btot(^c)*tmp)*inv_rho\
708 if(.not.mhd_internal_e) then
709 ! E=Bxv
710 {^ifthreec
711 efd(1)=btot(2)*w(ix^d,m3_)-btot(3)*w(ix^d,m2_)
712 efd(2)=btot(3)*w(ix^d,m1_)-btot(1)*w(ix^d,m3_)
713 efd(3)=btot(1)*w(ix^d,m2_)-btot(2)*w(ix^d,m1_)
714 }
715 {^iftwoc
716 efd(1)=zero
717 efd(2)=btot(1)*w(ix^d,m2_)-btot(2)*w(ix^d,m1_)
718 }
719 {^ifonec
720 efd(1)=zero
721 }
722 w(ix^d,e_)=w(ix^d,e_)-half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
723 +(^c&w(ix^d,b^c_)**2+)&
724 +(^c&efd(^c)**2+)*eos%inv_squared_c)
725 end if
726 {end do\}
727 {do ix^db=ixomin^db,ixomax^db\}
728 ! loop can not be vectorized
729 w(ix^d,p_)=mhd_semirelati_p(w(ix^d,e_),w(ix^d,rho_))
730 {end do\}
731
732 end subroutine mhd_to_primitive_semirelati
733
734 !> Transform conservative variables into primitive ones (semirelativistic noe)
735 subroutine mhd_to_primitive_semirelati_noe(ixI^L,ixO^L,w,x)
737 integer, intent(in) :: ixi^l, ixo^l
738 double precision, intent(inout) :: w(ixi^s, nw)
739 double precision, intent(in) :: x(ixi^s, 1:ndim)
740
741 double precision :: btot(1:ndir)
742 double precision :: b2, rhoc2, tmp, inv_rho
743 integer :: ix^d
744
745 if (fix_small_values) then
746 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, 'mhd_to_primitive_semirelati')
747 end if
748
749 {do ix^db=ixomin^db,ixomax^db\}
750 ^c&btot(^c)=w(ix^d,b^c_)\
751 if(b0field) then
752 ^c&btot(^c)=btot(^c)+block%B0(ix^d,^c,b0i)\
753 end if
754 ! Keppens's formula to convert momentum to velocity
755 b2=(^c&btot(^c)**2+)
756 tmp=(^c&btot(^c)*w(ix^d,m^c_)+)
757 rhoc2=w(ix^d,rho_)*mhd_squared_c
758 inv_rho=1.d0/(w(ix^d,rho_)*(rhoc2+b2))
759 ^c&w(ix^d,m^c_)=(rhoc2*w(ix^d,m^c_)+btot(^c)*tmp)*inv_rho\
760 {end do\}
761
762 end subroutine mhd_to_primitive_semirelati_noe
763
764 !> LTE conversion routines
765
766 !> LTE: Primitive (rho, v, p, B) -> Conserved (rho, rho*v, E_total, B)
767 !> E_total = eint + 0.5*rho*v^2 + 0.5*B^2
768 subroutine mhd_to_conserved_origin_lte(ixI^L,ixO^L,w,x)
770 integer, intent(in) :: ixi^l, ixo^l
771 double precision, intent(inout) :: w(ixi^s, nw)
772 double precision, intent(in) :: x(ixi^s, 1:ndim)
773
774 ! Convert p -> E_total (eint + eK + eB) via EoS table
775 call mhd_p_to_e(ixi^l, ixo^l, w, x)
776 ! Convert velocity to momentum
777 ^c&w(ixo^s,m^c_)=w(ixo^s,rho_)*w(ixo^s,m^c_)\
778
779 end subroutine mhd_to_conserved_origin_lte
780
781 !> LTE: Primitive (rho, v, p, B) -> Conserved (rho, rho*v, eint, B)
782 !> Internal energy formulation: e_ stores eint only
783 subroutine mhd_to_conserved_inte_lte(ixI^L,ixO^L,w,x)
785 integer, intent(in) :: ixi^l, ixo^l
786 double precision, intent(inout) :: w(ixi^s, nw)
787 double precision, intent(in) :: x(ixi^s, 1:ndim)
788
789 ! Convert p -> eint via EoS table (no mechanical energy)
790 call mhd_p_to_eint(ixi^l, ixo^l, w, x)
791 ! Convert velocity to momentum
792 ^c&w(ixo^s,m^c_)=w(ixo^s,rho_)*w(ixo^s,m^c_)\
793
794 end subroutine mhd_to_conserved_inte_lte
795
796 !> Convert pressure to total energy (eint + eK + eB) for origin formulation
797 subroutine mhd_p_to_e(ixI^L,ixO^L,w,x)
799 integer, intent(in) :: ixi^l, ixo^l
800 double precision, intent(inout) :: w(ixi^s, nw)
801 double precision, intent(in) :: x(ixi^s, 1:ndim)
802
803 integer :: ix^d
804
805 call mhd_p_to_eint(ixi^l,ixo^l,w,x)
806 {do ix^db=ixomin^db,ixomax^db\}
807 w(ix^d,e_) = w(ix^d,e_) + half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_) + (^c&w(ix^d,b^c_)**2+))
808 if(mhd_uawsom) w(ix^d,e_)=w(ix^d,e_)+&
809 w(ix^d,waplus_)+w(ix^d,waminus_)+w(ix^d,wkplus_)+w(ix^d,wkminus_)
810 {end do\}
811
812 end subroutine mhd_p_to_e
813
814 !> Convert pressure to internal energy only (for internal_e formulation)
815 subroutine mhd_p_to_eint(ixI^L,ixO^L,w,x)
817 integer, intent(in) :: ixi^l, ixo^l
818 double precision, intent(inout) :: w(ixi^s, nw)
819 double precision, intent(in) :: x(ixi^s, 1:ndim)
820
821 integer :: ix^d
822 double precision :: nh(ixi^s), log_nh(ixi^s)
823
824 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
825 log_nh(ixo^s) = 0.0d0
826 if (eos%ionE) log_nh(ixo^s) = dlog10(nh(ixo^s))
827
828 {do ix^db=ixomin^db,ixomax^db\}
829 w(ix^d,e_) = lte_eint_from_p(w(ix^d,p_), nh(ix^d), log_nh(ix^d), w(ix^d,rho_))
830 {end do\}
831
832 end subroutine mhd_p_to_eint
833
834 !> LTE: Conserved (rho, rho*v, E_total, B) -> Primitive (rho, v, p, B)
835 subroutine mhd_to_primitive_origin_lte(ixI^L,ixO^L,w,x)
837 integer, intent(in) :: ixi^l, ixo^l
838 double precision, intent(inout) :: w(ixi^s, nw)
839 double precision, intent(in) :: x(ixi^s, 1:ndim)
840
841 double precision :: inv_rho, eint_val
842 double precision :: nh(ixi^s), log_nh(ixi^s)
843 integer :: ix^d
844
845 if (fix_small_values) then
846 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, &
847 'mhd_to_primitive_origin_LTE')
848 end if
849
850 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
851 log_nh(ixo^s) = 0.0d0
852 if (eos%ionE) log_nh(ixo^s) = dlog10(nh(ixo^s))
853
854 {do ix^db=ixomin^db,ixomax^db\}
855 inv_rho = 1.d0/w(ix^d,rho_)
856 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
857 eint_val = w(ix^d,e_) &
858 - half*(w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+) &
859 + (^c&w(ix^d,b^c_)**2+))
860 w(ix^d,p_) = lte_p_from_eint(eint_val, nh(ix^d), log_nh(ix^d), inv_rho)
861 {end do\}
862
863 end subroutine mhd_to_primitive_origin_lte
864
865 !> LTE: Conserved (rho, rho*v, eint, B) -> Primitive (rho, v, p, B)
866 !> Internal energy formulation: eint = w(e_) directly.
867 subroutine mhd_to_primitive_inte_lte(ixI^L,ixO^L,w,x)
869 integer, intent(in) :: ixi^l, ixo^l
870 double precision, intent(inout) :: w(ixi^s, nw)
871 double precision, intent(in) :: x(ixi^s, 1:ndim)
872
873 double precision :: inv_rho
874 double precision :: nh(ixi^s), log_nh(ixi^s)
875 integer :: ix^d
876
877 if (fix_small_values) then
878 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, &
879 'mhd_to_primitive_inte_LTE')
880 end if
881
882 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
883 log_nh(ixo^s) = 0.0d0
884 if (eos%ionE) log_nh(ixo^s) = dlog10(nh(ixo^s))
885
886 {do ix^db=ixomin^db,ixomax^db\}
887 inv_rho = 1.d0/w(ix^d,rho_)
888 w(ix^d,p_) = lte_p_from_eint(w(ix^d,e_), nh(ix^d), log_nh(ix^d), inv_rho)
889 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
890 {end do\}
891
892 end subroutine mhd_to_primitive_inte_lte
893
894 !> LTE prolongation routines
895
896 !> Convert conserved (rho, rho*v, E_total, B) to prolongation form (rho, v, T, B).
897 !> Temperature is stored in the p_ slot for AMR interpolation.
898 !> T is smoothest through the ionisation zone (conduction-dominated, linear gradient).
899 subroutine mhd_to_prolong_lte(ixI^L,ixO^L,w,x)
901 integer, intent(in) :: ixi^l, ixo^l
902 double precision, intent(inout) :: w(ixi^s, nw)
903 double precision, intent(in) :: x(ixi^s, 1:ndim)
904
905 double precision :: inv_rho, eint_val, t_loc, y_loc
906 double precision :: nh(ixi^s), log_nh(ixi^s)
907 double precision :: vel(ixo^s,1:ndir), emech(ixo^s)
908 integer :: ix^d
909
910 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
911 log_nh(ixo^s) = dlog10(nh(ixo^s))
912 if (mhd_semirelativistic) call mhd_semirelati_vel_emech(w,ixi^l,ixo^l,vel,emech)
913
914 {do ix^db=ixomin^db,ixomax^db\}
915 inv_rho = 1.d0/w(ix^d,rho_)
916 if (mhd_semirelativistic) then
917 ! momentum and energy carry the electric-field terms
918 ^c&w(ix^d,m^c_)=vel(ix^d,^c)\
919 eint_val = w(ix^d,e_) - emech(ix^d)
920 else
921 ! Convert momentum to velocity
922 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
923 if (mhd_internal_e) then
924 eint_val = w(ix^d,e_)
925 else
926 ! Compute eint = E - 0.5*rho*v^2 - 0.5*B^2
927 eint_val = w(ix^d,e_) &
928 - half*(w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+) &
929 + (^c&w(ix^d,b^c_)**2+))
930 end if
931 end if
932 eint_val = max(eint_val, nh(ix^d)*eos%eint_nH_min, smalldouble)
933
934 if (eint_val * inv_rho > eos%eint_rho_FI_threshold) then
935 ! FI: T = (gamma-1)*(eint - eion*nH) / (nH * n_per_nH_FI)
936 w(ix^d,p_) = eos%gamma_minus_1 &
937 * (eint_val - eos%eion_per_nH * nh(ix^d)) &
938 / (nh(ix^d) * eos%n_per_nH_FI)
939 else if (eos%method_id == eos_analytic) then
940 ! Analytical Saha: solve for T from eint
941 call saha_t_from_nh_eint(nh(ix^d), &
942 eint_val / nh(ix^d), t_loc, y_loc)
943 w(ix^d,p_) = t_loc
944 else
945 ! Ionisation zone: T from table
946 w(ix^d,p_) = t_from_nh_eint( &
947 log_nh(ix^d), &
948 dlog10(eint_val) - log_nh(ix^d))
949 end if
950 {end do\}
951
952 end subroutine mhd_to_prolong_lte
953
954 !> Convert prolongation form (rho, v, T, B) to conserved (rho, rho*v, E_total, B).
955 !> T is read from the p_ slot. Uses eint_nH_from_T table for back-conversion.
956 subroutine mhd_from_prolong_lte(ixI^L,ixO^L,w,x)
958 integer, intent(in) :: ixi^l, ixo^l
959 double precision, intent(inout) :: w(ixi^s, nw)
960 double precision, intent(in) :: x(ixi^s, 1:ndim)
961
962 double precision :: t_val, eint_val(ixo^s), t_fi, log_t_min
963 double precision :: nh(ixi^s), btot(1:ndir)
964 double precision :: efd(1:ndir), s(1:ndir)
965 integer :: ix^d
966
967 ! Temperature above which gas is fully ionised
968 t_fi = (eos%eint_rho_FI_threshold &
969 * eos%nH2rhoFactor - eos%eion_per_nH) &
970 * eos%gamma_minus_1 / eos%n_per_nH_FI
971
972 ! Floor for log_T into the (rho, T) inverse table; entropy method uses
973 ! eos%eintT, legacy 'tables' method uses eos%eint_from_T. Picking the
974 ! wrong container leaves var2_min = 0 → floors T at 10^6 K.
975 if (eos%method_id == eos_entropy) then
976 log_t_min = eos%eintT%var2_min
977 else
978 log_t_min = eos%eint_from_T%var2_min
979 end if
980
981 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
982
983 {do ix^db=ixomin^db,ixomax^db\}
984 ! loop can not be vectorized
985 t_val = w(ix^d,p_) ! T stored in p_ slot
986 if (t_val > t_fi) then
987 ! FI: eint = nH*(n_per_nH*T/(gamma-1) + eion)
988 eint_val(ix^d) = nh(ix^d) &
989 * (eos%n_per_nH_FI * t_val * eos%inv_gamma_minus_1 &
990 + eos%eion_per_nH)
991 else if (eos%method_id == eos_analytic) then
992 ! Analytical Saha: eint from T directly
993 eint_val(ix^d) = saha_eint_from_nh_t(nh(ix^d), t_val) * nh(ix^d)
994 else
995 ! Ionisation zone: eint/nH from T table
996 eint_val(ix^d) = eint_nh_from_t( &
997 dlog10(nh(ix^d)), &
998 dlog10(max(t_val, 10.0d0**log_t_min))) &
999 * nh(ix^d)
1000 end if
1001 {end do\}
1002
1003 if (mhd_semirelativistic) then
1004 {do ix^db=ixomin^db,ixomax^db\}
1005 ! fully vectorized loop, E, S, e_, m^C_ all in one loop for speed
1006 ^c&btot(^c)=w(ix^d,b^c_)\
1007 if(b0field) then
1008 ^c&btot(^c)=btot(^c)+block%B0(ix^d,^c,b0i)\
1009 end if
1010 {^ifthreec
1011 efd(1)=btot(2)*w(ix^d,m3_)-btot(3)*w(ix^d,m2_)
1012 efd(2)=btot(3)*w(ix^d,m1_)-btot(1)*w(ix^d,m3_)
1013 efd(3)=btot(1)*w(ix^d,m2_)-btot(2)*w(ix^d,m1_)
1014 s(1)=efd(2)*btot(3)-efd(3)*btot(2)
1015 s(2)=efd(3)*btot(1)-efd(1)*btot(3)
1016 s(3)=efd(1)*btot(2)-efd(2)*btot(1)
1017 }
1018 {^iftwoc
1019 efd(1)=zero
1020 ! switch 3 with 2 to add 3 when ^C from 1 to 2
1021 efd(2)=btot(1)*w(ix^d,m2_)-btot(2)*w(ix^d,m1_)
1022 s(1)=-efd(2)*btot(2)
1023 s(2)=efd(2)*btot(1)
1024 }
1025 {^ifonec
1026 efd(1)=zero
1027 s(1)=zero
1028 }
1029 if (mhd_internal_e) then
1030 w(ix^d,e_) = eint_val(ix^d)
1031 else
1032 ! equation (9), the magnetic energy from the stored slot
1033 w(ix^d,e_) = eint_val(ix^d) &
1034 + half*(w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+) &
1035 + (^c&w(ix^d,b^c_)**2+) &
1036 + (^c&efd(^c)**2+)*eos%inv_squared_c)
1037 end if
1038 ! Convert velocity to momentum
1039 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)+s(^c)*eos%inv_squared_c\
1040 {end do\}
1041 else
1042 {do ix^db=ixomin^db,ixomax^db\}
1043 if (mhd_internal_e) then
1044 w(ix^d,e_) = eint_val(ix^d)
1045 else
1046 ! E = eint + 0.5*rho*v^2 + 0.5*B^2
1047 w(ix^d,e_) = eint_val(ix^d) &
1048 + half*(w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+) &
1049 + (^c&w(ix^d,b^c_)**2+))
1050 end if
1051 ! Convert velocity to momentum
1052 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)\
1053 {end do\}
1054 end if
1055
1056 end subroutine mhd_from_prolong_lte
1057
1058 !> Sound speed routines
1059
1060 !> Acoustic sound speed squared for FI (constant gamma) EoS.
1061 !> Expects w in primitive form: w(p_) = pressure, w(rho_) = density.
1062 subroutine mhd_get_csound2_fi(w, x, ixI^L, ixO^L, cs2)
1064 integer, intent(in) :: ixi^l, ixo^l
1065 double precision, intent(in) :: w(ixi^s, nw)
1066 double precision, intent(in) :: x(ixi^s, 1:ndim)
1067 double precision, intent(out) :: cs2(ixi^s)
1068
1069 double precision :: rho, ploc
1070 integer :: ix^d
1071
1072 {do ix^db=ixomin^db,ixomax^db \}
1073 if(has_equi_rho_and_p) then
1074 rho=(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0))
1075 ploc=(w(ix^d,p_)+block%equi_vars(ix^d,equi_pe0_,0))
1076 ! sound speed**2
1077 cs2(ix^d)=eos%gamma*ploc/rho
1078 else
1079 ! sound speed**2
1080 cs2(ix^d)=eos%gamma*w(ix^d,p_)/w(ix^d,rho_)
1081 end if
1082 {end do\}
1083
1084 end subroutine mhd_get_csound2_fi
1085
1086 !> Acoustic sound speed squared for LTE+IonE EoS using Gamma_1 table.
1087 !> Expects w in primitive form. Uses FI bypass for hot cells.
1088 subroutine mhd_get_csound2_lte(w, x, ixI^L, ixO^L, cs2)
1090 integer, intent(in) :: ixi^l, ixo^l
1091 double precision, intent(in) :: w(ixi^s, nw)
1092 double precision, intent(in) :: x(ixi^s, 1:ndim)
1093 double precision, intent(out) :: cs2(ixi^s)
1094
1095 integer :: ix^d
1096
1097 if (eos%gamma1_method == 'constant') then
1098 cs2(ixo^s) = eos%gamma * w(ixo^s, p_) / w(ixo^s, rho_)
1099 else
1100 {do ix^db=ixomin^db,ixomax^db\}
1101 cs2(ix^d) = lte_gamma1_from_p(w(ix^d,p_), w(ix^d,rho_)) * (w(ix^d,p_) / w(ix^d,rho_))
1102 {end do\}
1103 end if
1104
1105 end subroutine mhd_get_csound2_lte
1106
1107 subroutine mhd_get_gamma1_lte(w, x, ixI^L, ixO^L, gamma1)
1109 integer, intent(in) :: ixi^l, ixo^l
1110 double precision, intent(in) :: w(ixi^s, nw)
1111 double precision, intent(in) :: x(ixi^s, 1:ndim)
1112 double precision, intent(out) :: gamma1(ixi^s)
1113
1114 integer :: ix^d
1115
1116 if (eos%gamma1_method == 'constant') then
1117 gamma1(ixo^s) = eos%gamma
1118 return
1119 end if
1120
1121 {do ix^db=ixomin^db,ixomax^db\}
1122 gamma1(ix^d) = lte_gamma1_from_p(w(ix^d,p_), w(ix^d,rho_))
1123 {end do\}
1124
1125 end subroutine mhd_get_gamma1_lte
1126
1127 !> Rfactor routines (matching HD: EoS functions live in EoS module)
1128
1129 !> Rfactor = p/(rho*T) for constant ionisation degree (FI/PI no-energy).
1130 !> Stays in the seam: RR is the physics module's gas-constant factor, not
1131 !> visible to mod_eos. Rfactor_from_LTE lives in mod_eos (no RR).
1132 subroutine rfactor_from_constant_ionization(w,x,ixI^L,ixO^L,Rfactor)
1134 integer, intent(in) :: ixi^l, ixo^l
1135 double precision, intent(in) :: w(ixi^s,1:nw)
1136 double precision, intent(in) :: x(ixi^s,1:ndim)
1137 double precision, intent(out):: rfactor(ixi^s)
1138
1139 rfactor(ixo^s)=rr
1140
1141 end subroutine rfactor_from_constant_ionization
1142
1143 !> Rfactor from partial ionisation temperature lookup (Leenaarts et al. 2012)
1144
1145 !> Thermal pressure routines (moved from mod_mhd_phys.t)
1146
1147 !> Calculate isothermal thermal pressure
1148 subroutine mhd_get_pthermal_noe(w,x,ixI^L,ixO^L,pth)
1151
1152 integer, intent(in) :: ixi^l, ixo^l
1153 double precision, intent(in) :: w(ixi^s,nw)
1154 double precision, intent(in) :: x(ixi^s,1:ndim)
1155 double precision, intent(out):: pth(ixi^s)
1156
1157 double precision :: adiabs(ixi^s), gammas(ixi^s)
1158
1159 if(associated(usr_set_adiab)) then
1160 call usr_set_adiab(w,x,ixi^l,ixo^l,adiabs)
1161 else
1162 adiabs=mhd_adiab
1163 end if
1164 if(associated(usr_set_gamma)) then
1165 call usr_set_gamma(w,x,ixi^l,ixo^l,gammas)
1166 else
1167 gammas=eos%gamma
1168 end if
1169
1170 if(has_equi_rho_and_p) then
1171 pth(ixo^s)=adiabs(ixo^s)*(w(ixo^s,rho_)+block%equi_vars(ixo^s,equi_rho0_,0))**gammas(ixo^s)
1172 else
1173 pth(ixo^s)=adiabs(ixo^s)*w(ixo^s,rho_)**gammas(ixo^s)
1174 end if
1175
1176 end subroutine mhd_get_pthermal_noe
1177
1178 !> Calculate thermal pressure from internal energy
1179 subroutine mhd_get_pthermal_inte(w,x,ixI^L,ixO^L,pth)
1182
1183 integer, intent(in) :: ixi^l, ixo^l
1184 double precision, intent(in) :: w(ixi^s,nw)
1185 double precision, intent(in) :: x(ixi^s,1:ndim)
1186 double precision, intent(out):: pth(ixi^s)
1187
1188 integer :: iw, ix^d
1189
1190 {do ix^db= ixomin^db,ixomax^db\}
1191 if(has_equi_rho_and_p) then
1192 pth(ix^d)=eos%gamma_minus_1*w(ix^d,e_)+block%equi_vars(ix^d,equi_pe0_,0)
1193 else
1194 pth(ix^d)=eos%gamma_minus_1*w(ix^d,e_)
1195 end if
1196 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1197 {end do\}
1198
1199 if(check_small_values.and..not.fix_small_values) then
1200 {do ix^db= ixomin^db,ixomax^db\}
1201 if(pth(ix^d)<small_pressure) then
1202 write(*,*) "Error: small value of gas pressure",pth(ix^d),&
1203 " encountered when call mhd_get_pthermal_inte"
1204 write(*,*) "Iteration: ", it, " Time: ", global_time
1205 write(*,*) "Location: ", x(ix^d,:)
1206 write(*,*) "Cell number: ", ix^d
1207 do iw=1,nw
1208 write(*,*) trim(cons_wnames(iw)),": ",w(ix^d,iw)
1209 end do
1210 if(trace_small_values) write(*,*) sqrt(pth(ix^d)-bigdouble)
1211 write(*,*) "Saving status at the previous time step"
1212 crash=.true.
1213 end if
1214 {end do\}
1215 end if
1216
1217 end subroutine mhd_get_pthermal_inte
1218
1219 !> Calculate thermal pressure=(gamma-1)*(e-0.5*m**2/rho-b**2/2) within ixO^L
1220 subroutine mhd_get_pthermal_origin(w,x,ixI^L,ixO^L,pth)
1223
1224 integer, intent(in) :: ixi^l, ixo^l
1225 double precision, intent(in) :: w(ixi^s,nw)
1226 double precision, intent(in) :: x(ixi^s,1:ndim)
1227 double precision, intent(out):: pth(ixi^s)
1228
1229 integer :: iw, ix^d
1230
1231 {do ix^db=ixomin^db,ixomax^db\}
1232 if(has_equi_rho_and_p) then
1233 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))&
1234 +(^c&w(ix^d,b^c_)**2+)))+block%equi_vars(ix^d,equi_pe0_,0)
1235 else
1236 pth(ix^d)=eos%gamma_minus_1*(w(ix^d,e_)-half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)&
1237 +(^c&w(ix^d,b^c_)**2+))-mhd_uawsom_wave_energy_cell(w(ix^d,:)))
1238 end if
1239 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1240 {end do\}
1241
1242 if(check_small_values.and..not.fix_small_values) then
1243 {do ix^db=ixomin^db,ixomax^db\}
1244 if(pth(ix^d)<small_pressure) then
1245 write(*,*) "Error: small value of gas pressure",pth(ix^d),&
1246 " encountered when call mhd_get_pthermal"
1247 write(*,*) "Iteration: ", it, " Time: ", global_time
1248 write(*,*) "Location: ", x(ix^d,:)
1249 write(*,*) "Cell number: ", ix^d
1250 do iw=1,nw
1251 write(*,*) trim(cons_wnames(iw)),": ",w(ix^d,iw)
1252 end do
1253 if(trace_small_values) write(*,*) sqrt(pth(ix^d)-bigdouble)
1254 write(*,*) "Saving status at the previous time step"
1255 crash=.true.
1256 end if
1257 {end do\}
1258 end if
1259
1260 end subroutine mhd_get_pthermal_origin
1261
1262 !> Calculate thermal pressure for LTE EoS (delegates to eos%get_thermal_pressure)
1263 subroutine mhd_get_pthermal_lte(w,x,ixI^L,ixO^L,pth)
1266
1267 integer, intent(in) :: ixi^l, ixo^l
1268 double precision, intent(in) :: w(ixi^s,nw)
1269 double precision, intent(in) :: x(ixi^s,1:ndim)
1270 double precision, intent(out):: pth(ixi^s)
1271 double precision :: nh(ixi^s)
1272
1273 integer :: iw, ix^d
1274
1275 ! LTE: p = nH * (1 + He + ne/nH) * T from stored state variables
1276 call eos%get_nH(w, x, ixi^l, ixo^l, nh)
1277 pth(ixo^s) = nh(ixo^s) * (1.0d0 + eos%He_abundance &
1278 + (w(ixo^s,ne_) / nh(ixo^s))) * w(ixo^s,te_)
1279
1280 if(fix_small_values) then
1281 {do ix^db=ixomin^db,ixomax^db\}
1282 if(pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1283 {end do\}
1284 else if(check_small_values) then
1285 {do ix^db=ixomin^db,ixomax^db\}
1286 if(pth(ix^d)<small_pressure) then
1287 write(*,*) "Error: small value of gas pressure",pth(ix^d),&
1288 " encountered when call mhd_get_pthermal_LTE"
1289 write(*,*) "Iteration: ", it, " Time: ", global_time
1290 write(*,*) "Location: ", x(ix^d,:)
1291 write(*,*) "Cell number: ", ix^d
1292 do iw=1,nw
1293 write(*,*) trim(cons_wnames(iw)),": ",w(ix^d,iw)
1294 end do
1295 if(trace_small_values) write(*,*) sqrt(pth(ix^d)-bigdouble)
1296 write(*,*) "Saving status at the previous time step"
1297 crash=.true.
1298 end if
1299 {end do\}
1300 end if
1301
1302 end subroutine mhd_get_pthermal_lte
1303
1304 !> Calculate thermal pressure for semirelativistic MHD (any EoS)
1305 subroutine mhd_get_pthermal_semirelati(w,x,ixI^L,ixO^L,pth)
1308
1309 integer, intent(in) :: ixi^l, ixo^l
1310 double precision, intent(in) :: w(ixi^s,nw)
1311 double precision, intent(in) :: x(ixi^s,1:ndim)
1312 double precision, intent(out):: pth(ixi^s)
1313
1314 double precision :: vel(ixo^s,1:ndir), emech(ixo^s)
1315 integer :: iw, ix^d
1316
1317 if(mhd_internal_e) then
1318 {do ix^db=ixomin^db,ixomax^db\}
1319 pth(ix^d)=mhd_semirelati_p(w(ix^d,e_),w(ix^d,rho_))
1320 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1321 {end do\}
1322 else
1323 call mhd_semirelati_vel_emech(w,ixi^l,ixo^l,vel,emech)
1324 {do ix^db=ixomin^db,ixomax^db\}
1325 pth(ix^d)=mhd_semirelati_p(w(ix^d,e_)-emech(ix^d),w(ix^d,rho_))
1326 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1327 {end do\}
1328 end if
1329
1330 if(check_small_values.and..not.fix_small_values) then
1331 {do ix^db=ixomin^db,ixomax^db\}
1332 if(pth(ix^d)<small_pressure) then
1333 write(*,*) "Error: small value of gas pressure",pth(ix^d),&
1334 " encountered when call mhd_get_pthermal_semirelati"
1335 write(*,*) "Iteration: ", it, " Time: ", global_time
1336 write(*,*) "Location: ", x(ix^d,:)
1337 write(*,*) "Cell number: ", ix^d
1338 do iw=1,nw
1339 write(*,*) trim(cons_wnames(iw)),": ",w(ix^d,iw)
1340 end do
1341 if(trace_small_values) write(*,*) sqrt(pth(ix^d)-bigdouble)
1342 write(*,*) "Saving status at the previous time step"
1343 crash=.true.
1344 end if
1345 {end do\}
1346 end if
1347
1348 end subroutine mhd_get_pthermal_semirelati
1349
1350 !> Calculate thermal pressure=(gamma-1)*(e-0.5*m**2/rho) within ixO^L
1351 subroutine mhd_get_pthermal_hde(w,x,ixI^L,ixO^L,pth)
1354
1355 integer, intent(in) :: ixi^l, ixo^l
1356 double precision, intent(in) :: w(ixi^s,nw)
1357 double precision, intent(in) :: x(ixi^s,1:ndim)
1358 double precision, intent(out):: pth(ixi^s)
1359
1360 integer :: iw, ix^d
1361
1362 {do ix^db= ixomin^db,ixomax^db\}
1363 pth(ix^d)=eos%gamma_minus_1*(w(ix^d,e_)-half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)))
1364 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1365 {end do\}
1366 if(check_small_values.and..not.fix_small_values) then
1367 {do ix^db= ixomin^db,ixomax^db\}
1368 if(pth(ix^d)<small_pressure) then
1369 write(*,*) "Error: small value of gas pressure",pth(ix^d),&
1370 " encountered when call mhd_get_pthermal_hde"
1371 write(*,*) "Iteration: ", it, " Time: ", global_time
1372 write(*,*) "Location: ", x(ix^d,:)
1373 write(*,*) "Cell number: ", ix^d
1374 do iw=1,nw
1375 write(*,*) trim(cons_wnames(iw)),": ",w(ix^d,iw)
1376 end do
1377 if(trace_small_values) write(*,*) sqrt(pth(ix^d)-bigdouble)
1378 write(*,*) "Saving status at the previous time step"
1379 crash=.true.
1380 end if
1381 {end do\}
1382 end if
1383
1384 end subroutine mhd_get_pthermal_hde
1385
1386 !> Temperature routines (moved from mod_mhd_phys.t)
1387
1388 !> Copy temperature from stored Te variable
1389 subroutine mhd_get_temperature_from_te(w, x, ixI^L, ixO^L, res)
1391 integer, intent(in) :: ixi^l, ixo^l
1392 double precision, intent(in) :: w(ixi^s, 1:nw)
1393 double precision, intent(in) :: x(ixi^s, 1:ndim)
1394 double precision, intent(out):: res(ixi^s)
1395 res(ixo^s) = w(ixo^s, te_)
1396 end subroutine mhd_get_temperature_from_te
1397
1398 !> Calculate temperature=p/rho when in e_ the internal energy is stored
1399 subroutine mhd_get_temperature_from_eint(w, x, ixI^L, ixO^L, res)
1401 integer, intent(in) :: ixi^l, ixo^l
1402 double precision, intent(in) :: w(ixi^s, 1:nw)
1403 double precision, intent(in) :: x(ixi^s, 1:ndim)
1404 double precision, intent(out):: res(ixi^s)
1405
1406 double precision :: r(ixi^s)
1407
1408 call eos%get_Rfactor(w,x,ixi^l,ixo^l,r)
1409 res(ixo^s) = eos%gamma_minus_1 * w(ixo^s, e_)/(w(ixo^s,rho_)*r(ixo^s))
1410 end subroutine mhd_get_temperature_from_eint
1411
1412 !> Calculate temperature=p/rho when in e_ the total energy is stored
1413 subroutine mhd_get_temperature_from_etot(w, x, ixI^L, ixO^L, res)
1415 integer, intent(in) :: ixi^l, ixo^l
1416 double precision, intent(in) :: w(ixi^s, 1:nw)
1417 double precision, intent(in) :: x(ixi^s, 1:ndim)
1418 double precision, intent(out):: res(ixi^s)
1419
1420 double precision :: r(ixi^s)
1421
1422 call eos%get_Rfactor(w,x,ixi^l,ixo^l,r)
1423 call mhd_get_pthermal(w,x,ixi^l,ixo^l,res)
1424 res(ixo^s)=res(ixo^s)/(r(ixo^s)*w(ixo^s,rho_))
1425
1426 end subroutine mhd_get_temperature_from_etot
1427
1428 subroutine mhd_get_temperature_from_etot_with_equi(w, x, ixI^L, ixO^L, res)
1430 integer, intent(in) :: ixi^l, ixo^l
1431 double precision, intent(in) :: w(ixi^s, 1:nw)
1432 double precision, intent(in) :: x(ixi^s, 1:ndim)
1433 double precision, intent(out):: res(ixi^s)
1434
1435 double precision :: r(ixi^s)
1436
1437 call eos%get_Rfactor(w,x,ixi^l,ixo^l,r)
1438 call mhd_get_pthermal(w,x,ixi^l,ixo^l,res)
1439 res(ixo^s)=res(ixo^s)/(r(ixo^s)*(w(ixo^s,rho_)+block%equi_vars(ixo^s,equi_rho0_,b0i)))
1440
1441 end subroutine mhd_get_temperature_from_etot_with_equi
1442
1443 subroutine mhd_get_temperature_from_eint_with_equi(w, x, ixI^L, ixO^L, res)
1445 integer, intent(in) :: ixi^l, ixo^l
1446 double precision, intent(in) :: w(ixi^s, 1:nw)
1447 double precision, intent(in) :: x(ixi^s, 1:ndim)
1448 double precision, intent(out):: res(ixi^s)
1449
1450 double precision :: r(ixi^s)
1451
1452 call eos%get_Rfactor(w,x,ixi^l,ixo^l,r)
1453 res(ixo^s) = (eos%gamma_minus_1 * w(ixo^s, e_) + block%equi_vars(ixo^s,equi_pe0_,b0i)) /&
1454 ((w(ixo^s,rho_) +block%equi_vars(ixo^s,equi_rho0_,b0i))*r(ixo^s))
1455
1456 end subroutine mhd_get_temperature_from_eint_with_equi
1457
1458 !> T from total energy for LTE, and for PI with ionisation energy. KE+ME are subtracted through
1459 !> the MHD-aware phys_e_to_ei, and T comes from the EoS eint->T lookup through
1460 !> eos%get_temperature_from_eint. Always fresh; does not use the iw_te cache. With
1461 !> mhd_internal_e w(:,e_) is already eint, and eos%get_temperature_from_eint is bound instead
1462 subroutine mhd_get_temperature_from_etot_lte(w, x, ixI^L, ixO^L, res)
1464 integer, intent(in) :: ixi^l, ixo^l
1465 double precision, intent(in) :: w(ixi^s, 1:nw)
1466 double precision, intent(in) :: x(ixi^s, 1:ndim)
1467 double precision, intent(out):: res(ixi^s)
1468
1469 double precision :: wlocal(ixi^s, 1:nw)
1470
1471 wlocal(ixi^s, 1:nw) = w(ixi^s, 1:nw)
1472 ! Subtract KE+ME from w(:,e_) via the MHD-aware dispatcher
1473 call phys_e_to_ei(ixi^l, ixo^l, wlocal, x)
1474 ! Now wlocal(:,e_) is the gas internal energy; LTE table lookup
1475 call eos%get_temperature_from_eint(wlocal, x, ixi^l, ixo^l, res)
1476
1477 end subroutine mhd_get_temperature_from_etot_lte
1478
1479 subroutine mhd_get_temperature_equi(w,x, ixI^L, ixO^L, res)
1481 integer, intent(in) :: ixi^l, ixo^l
1482 double precision, intent(in) :: w(ixi^s, 1:nw)
1483 double precision, intent(in) :: x(ixi^s, 1:ndim)
1484 double precision, intent(out):: res(ixi^s)
1485
1486 double precision :: r(ixi^s)
1487
1488 call eos%get_Rfactor(w,x,ixi^l,ixo^l,r)
1489 res(ixo^s)= block%equi_vars(ixo^s,equi_pe0_,b0i)/(block%equi_vars(ixo^s,equi_rho0_,b0i)*r(ixo^s))
1490
1491 end subroutine mhd_get_temperature_equi
1492
1493 !> Equilibrium extraction routines (moved from mod_mhd_phys.t)
1494
1495 subroutine mhd_get_rho_equi(w, x, ixI^L, ixO^L, res)
1497 integer, intent(in) :: ixi^l, ixo^l
1498 double precision, intent(in) :: w(ixi^s, 1:nw)
1499 double precision, intent(in) :: x(ixi^s, 1:ndim)
1500 double precision, intent(out):: res(ixi^s)
1501 res(ixo^s) = block%equi_vars(ixo^s,equi_rho0_,b0i)
1502 end subroutine mhd_get_rho_equi
1503
1504 subroutine mhd_get_pe_equi(w,x, ixI^L, ixO^L, res)
1506 integer, intent(in) :: ixi^l, ixo^l
1507 double precision, intent(in) :: w(ixi^s, 1:nw)
1508 double precision, intent(in) :: x(ixi^s, 1:ndim)
1509 double precision, intent(out):: res(ixi^s)
1510 res(ixo^s) = block%equi_vars(ixo^s,equi_pe0_,b0i)
1511 end subroutine mhd_get_pe_equi
1512
1513 !> Electron and hydrogen number densities of the background alone, for the
1514 !> equilibrium rate that subtract_equi removes. Splitting is restricted to
1515 !> the FI EoS, so the electron count is the fully ionised one.
1516 subroutine mhd_get_ne_nh_equi(ixI^L, ixO^L, w, x, ne, nH)
1518 integer, intent(in) :: ixi^l, ixo^l
1519 double precision, intent(in) :: w(ixi^s, nw)
1520 double precision, intent(in) :: x(ixi^s, 1:ndim)
1521 double precision, intent(out):: ne(ixi^s), nh(ixi^s)
1522 nh(ixo^s) = block%equi_vars(ixo^s,equi_rho0_,b0i) / eos%nH2rhoFactor
1523 ne(ixo^s) = nh(ixo^s) * eos%neOnH_FI
1524 end subroutine mhd_get_ne_nh_equi
1525
1526 !> Internal energy extraction + small value handling (moved from mod_mhd_phys.t)
1527
1528 !> Get internal energy from conserved state (origin formulation)
1529 function mhd_get_ei_origin(w, ixI^L, ixO^L) result(ei)
1531 integer, intent(in) :: ixi^l, ixo^l
1532 double precision, intent(in) :: w(ixi^s, nw)
1533 double precision :: ei(ixo^s)
1534
1535 ! ei = E_total - e_kinetic - e_magnetic
1536 ei(ixo^s) = w(ixo^s,e_) - half*((^c&w(ixo^s,m^c_)**2+)/w(ixo^s,rho_) &
1537 + (^c&w(ixo^s,b^c_)**2+))
1538 end function mhd_get_ei_origin
1539
1540 !> Get internal energy from conserved state (semirelativistic formulation)
1541 function mhd_get_ei_semirelati(w, ixI^L, ixO^L) result(ei)
1543 integer, intent(in) :: ixi^l, ixo^l
1544 double precision, intent(in) :: w(ixi^s, nw)
1545 double precision :: ei(ixo^s)
1546
1547 double precision :: vel(ixo^s,1:ndir), emech(ixo^s)
1548
1549 call mhd_semirelati_vel_emech(w,ixi^l,ixo^l,vel,emech)
1550 ei(ixo^s)=w(ixo^s,e_)-emech(ixo^s)
1551 end function mhd_get_ei_semirelati
1552
1553 !> Get internal energy from conserved state (internal energy formulation)
1554 function mhd_get_ei_inte(w, ixI^L, ixO^L) result(ei)
1556 integer, intent(in) :: ixi^l, ixo^l
1557 double precision, intent(in) :: w(ixi^s, nw)
1558 double precision :: ei(ixo^s)
1559
1560 ! e_ already stores internal energy
1561 ei(ixo^s) = w(ixo^s,e_)
1562 end function mhd_get_ei_inte
1563
1564 ! mhd_handle_small_ei stays in mod_mhd_phys.t (callers are there)
1565
1566 !> Update temperature Te_ from pressure using partial ionization
1567
1568 !> PI energy-mode routines (eos_type='PI', ionE=.true.)
1569 !> Structural mirror of the FI conversion/pthermal/csound family, sharing
1570 !> FI's eq_state_units / RR=1 normalisation; the only difference is that the
1571 !> eint<->p relation is delegated to the portable scalar backend
1572 !> (mod_eos_pi), so eint carries the ionisation-energy term.
1573 !> Origin: w(e_) is total energy; inte: w(e_) is gas internal energy.
1574
1575 !> Primitive pressure -> total energy (origin). m^C_ is still velocity here
1576 !> (momentum conversion follows in to_conserved), so KE = half*rho*v^2.
1577 subroutine mhd_p_to_e_pi(ixI^L,ixO^L,w,x)
1579 integer, intent(in) :: ixi^l, ixo^l
1580 double precision, intent(inout) :: w(ixi^s, nw)
1581 double precision, intent(in) :: x(ixi^s, 1:ndim)
1582
1583 double precision :: eint
1584 integer :: ix^d
1585
1586 {do ix^db=ixomin^db,ixomax^db\}
1587 call eint_from_rho_p_pi(w(ix^d,rho_), w(ix^d,p_), eint)
1588 w(ix^d,e_)=eint &
1589 +half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
1590 +(^c&w(ix^d,b^c_)**2+))
1591 {end do\}
1592
1593 end subroutine mhd_p_to_e_pi
1594
1595 !> Primitive pressure -> gas internal energy (inte formulation)
1596 subroutine mhd_p_to_eint_pi(ixI^L,ixO^L,w,x)
1598 integer, intent(in) :: ixi^l, ixo^l
1599 double precision, intent(inout) :: w(ixi^s, nw)
1600 double precision, intent(in) :: x(ixi^s, 1:ndim)
1601
1602 double precision :: eint
1603 integer :: ix^d
1604
1605 {do ix^db=ixomin^db,ixomax^db\}
1606 call eint_from_rho_p_pi(w(ix^d,rho_), w(ix^d,p_), eint)
1607 w(ix^d,e_)=eint
1608 {end do\}
1609
1610 end subroutine mhd_p_to_eint_pi
1611
1612 !> Primitive -> conserved (origin, total energy)
1613 subroutine mhd_to_conserved_origin_pi(ixI^L,ixO^L,w,x)
1615 integer, intent(in) :: ixi^l, ixo^l
1616 double precision, intent(inout) :: w(ixi^s, nw)
1617 double precision, intent(in) :: x(ixi^s, 1:ndim)
1618
1619 call mhd_p_to_e_pi(ixi^l, ixo^l, w, x)
1620 ^c&w(ixo^s,m^c_)=w(ixo^s,rho_)*w(ixo^s,m^c_)\
1621
1622 end subroutine mhd_to_conserved_origin_pi
1623
1624 !> Primitive -> conserved (internal energy)
1625 subroutine mhd_to_conserved_inte_pi(ixI^L,ixO^L,w,x)
1627 integer, intent(in) :: ixi^l, ixo^l
1628 double precision, intent(inout) :: w(ixi^s, nw)
1629 double precision, intent(in) :: x(ixi^s, 1:ndim)
1630
1631 call mhd_p_to_eint_pi(ixi^l, ixo^l, w, x)
1632 ^c&w(ixo^s,m^c_)=w(ixo^s,rho_)*w(ixo^s,m^c_)\
1633
1634 end subroutine mhd_to_conserved_inte_pi
1635
1636 !> Conserved -> primitive (origin). Subtract KE+ME -> eint, invert -> p.
1637 subroutine mhd_to_primitive_origin_pi(ixI^L,ixO^L,w,x)
1639 integer, intent(in) :: ixi^l, ixo^l
1640 double precision, intent(inout) :: w(ixi^s, nw)
1641 double precision, intent(in) :: x(ixi^s, 1:ndim)
1642
1643 double precision :: inv_rho, eint_val, t, rfac
1644 integer :: ix^d
1645
1646 if (fix_small_values) then
1647 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, &
1648 'mhd_to_primitive_origin_PI')
1649 end if
1650
1651 {do ix^db=ixomin^db,ixomax^db\}
1652 inv_rho = 1.d0/w(ix^d,rho_)
1653 ! Convert momentum to velocity
1654 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
1655 ! eint = E - 0.5*rho*v^2 - 0.5*B^2
1656 eint_val = w(ix^d,e_) &
1657 - half*(w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+) &
1658 + (^c&w(ix^d,b^c_)**2+))
1659 eint_val = max(eint_val, smalldouble)
1660 call state_from_eint_pi(w(ix^d,rho_), eint_val, t, w(ix^d,p_), rfac)
1661 {end do\}
1662
1663 end subroutine mhd_to_primitive_origin_pi
1664
1665 !> Conserved -> primitive (internal energy). e_ already holds eint.
1666 subroutine mhd_to_primitive_inte_pi(ixI^L,ixO^L,w,x)
1668 integer, intent(in) :: ixi^l, ixo^l
1669 double precision, intent(inout) :: w(ixi^s, nw)
1670 double precision, intent(in) :: x(ixi^s, 1:ndim)
1671
1672 double precision :: inv_rho, eint_val, t, rfac
1673 integer :: ix^d
1674
1675 if (fix_small_values) then
1676 call phys_handle_small_values(.false., w, x, ixi^l, ixo^l, &
1677 'mhd_to_primitive_inte_PI')
1678 end if
1679
1680 {do ix^db=ixomin^db,ixomax^db\}
1681 eint_val = max(w(ix^d,e_), smalldouble)
1682 call state_from_eint_pi(w(ix^d,rho_), eint_val, t, w(ix^d,p_), rfac)
1683 ! Convert momentum to velocity
1684 inv_rho = 1.d0/w(ix^d,rho_)
1685 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
1686 {end do\}
1687
1688 end subroutine mhd_to_primitive_inte_pi
1689
1690 !> Thermal pressure (origin): subtract KE+ME, invert eint -> p.
1691 subroutine mhd_get_pthermal_origin_pi(w,x,ixI^L,ixO^L,pth)
1693 integer, intent(in) :: ixi^l, ixo^l
1694 double precision, intent(in) :: w(ixi^s,nw)
1695 double precision, intent(in) :: x(ixi^s,1:ndim)
1696 double precision, intent(out):: pth(ixi^s)
1697
1698 double precision :: eint_val, t, rfac
1699 integer :: ix^d
1700
1701 {do ix^db=ixomin^db,ixomax^db\}
1702 eint_val = w(ix^d,e_) &
1703 - half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_) &
1704 + (^c&w(ix^d,b^c_)**2+))
1705 eint_val = max(eint_val, smalldouble)
1706 call state_from_eint_pi(w(ix^d,rho_), eint_val, t, pth(ix^d), rfac)
1707 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1708 {end do\}
1709
1710 end subroutine mhd_get_pthermal_origin_pi
1711
1712 !> Thermal pressure (inte): e_ holds eint, invert eint -> p.
1713 subroutine mhd_get_pthermal_inte_pi(w,x,ixI^L,ixO^L,pth)
1715 integer, intent(in) :: ixi^l, ixo^l
1716 double precision, intent(in) :: w(ixi^s,nw)
1717 double precision, intent(in) :: x(ixi^s,1:ndim)
1718 double precision, intent(out):: pth(ixi^s)
1719
1720 double precision :: eint_val, t, rfac
1721 integer :: ix^d
1722
1723 {do ix^db=ixomin^db,ixomax^db\}
1724 eint_val = max(w(ix^d,e_), smalldouble)
1725 call state_from_eint_pi(w(ix^d,rho_), eint_val, t, pth(ix^d), rfac)
1726 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1727 {end do\}
1728
1729 end subroutine mhd_get_pthermal_inte_pi
1730
1731 !> Adiabatic sound speed squared from primitive (rho, p).
1732
1733 !> Effective Gamma1 = cs2 * rho / p (for the same primitive state).
1734
1735 !> PI energy mode: refresh Te_ from gas internal energy via the backend
1736 !> eint->T inversion (direct, so no lagged wCT -- unlike no-energy
1737 !> mhd_update_temperature which lags iz_H via wCT(Te_)). Uses MHD's Te_ index
1738 !> (PI registers Te via var_set_auxvar, so the generic iw_te is unset).
1739
1740 !> PI prominence (T,p) routines (eos_type='PI', pi_table='prominence').
1741 !> Prominence ionisation depends on (T,p), not T alone, so the chromosphere
1742 !> T-only seam does not apply. Following the collaborator's design, R and T
1743 !> are recomputed from (rho, pth) via ionization_get_state, which dispatches
1744 !> the prominence (pressure-bisection) inversion internally. No-energy only
1745 !> (guarded in eos_validate_params), so pth = (gamma-1)*eint is R-independent
1746 !> and obtained from phys_get_ei (conserved w; KE/ME removed).
1747
1748 !> R-factor for the prominence (T,p) table: recompute from (rho, pth).
1749
1750 !> Refresh stored Te_ for the prominence table from (rho, pth).
1751
1752end module mod_mhd_eos
1753!> 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.
integer, parameter, public eos_analytic
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 hydrogen Saha EoS with neutral helium (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)
Ratio eint/p from (log10 nH, log10 p/nH).
double precision function, public eint_nh_from_t(log_nh, log_t)
eint/nH from (log10 nH, log10 T), from the inverse table when one is built and the Saha solve otherwi...
double precision function, public y_from_nh_eint(nh, eint_nh)
Scalar EoS kernels: one cell on (log10 nH, log10 eint/nH) or (log10 nH, log10 p/nH),...
subroutine, public eos_get_eintt_grid(n_nh, lg_nh_min, lg_nh_max)
####################################################################### Table-metadata helpers: expos...
double precision function, public t_from_nh_eint(nh, eint_nh)
Temperature from (log10 nH, log10 eint/nH).
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:322
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:315
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:340
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:143
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:331
Equation of state for AMRVAC, handled through a single eos_container object.
Definition mod_eos.t:30
subroutine, public eos_bind_tc(fl, sat_r_variable)
The EoS hooks of the conduction fluid fl. sat_R_variable is false when R is the constant 1,...
Definition mod_eos.t:420
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 b0field
split magnetic field as background B0 field
double precision unit_temperature
Physical scaling factor for temperature.
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:51
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:40
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:37
procedure(sub_convert), pointer, public mhd_to_conserved
use habitual name of converting to conserved
Definition mod_mhd_eos.t:44
procedure(sub_convert), pointer, public mhd_to_primitive
use habitual name of converting to primitive
Definition mod_mhd_eos.t:42
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
double precision, parameter rc_t_ymod_max_k
Temperature in K above which c_V is constant, the upper end of the Y_mod path.
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