MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_eos_pi.t
Go to the documentation of this file.
1!=============================================================================
2!> PI (partial-ionisation, eos_type='PI') arm of the eos% family.
3!>
4!> The adapter between the ionisation backend (mod_eos_pi_tables) and the eos%
5!> authority. PI rides the FI fully-ionised ideal-gas closure (RR=1
6!> normalisation, p=(gamma-1) eint when ionE=F) and differs only through the
7!> variable mean-mass that enters via eos%get_Rfactor and -- in energy mode --
8!> the ionisation energy folded into eint. mod_eos_pi_tables has no mod_eos
9!> dependency, so eos% -> ionisation is acyclic.
10!>
11!> Structure mirrors mod_eos_fi / mod_eos_lte:
12!> A. init / finalise -- wire the eos% pointer targets
13!> B. generic block getters -- the eos%-interface routines bound for PI
14!> (csound2, gamma1, Rfactor, Te update);
15!> module-agnostic via iw_rho/iw_e/iw_te,
16!> so the hd/mhd/ffhd seams share ONE copy
17!> instead of three. The seam keeps only
18!> the B/KE-specific conversions.
19!> C. scalar combined-solve backend -- one ionisation solve yields T,p,R(,iz)
20!> together; called per-cell by the seam
21!> conversions and by section D.
22!> D. fl-port scalar shims -- f(log_nH,log_x) callbacks for the
23!> cooling / conduction port objects.
24!> mod_eos re-exports the public names so the seams reach them through the single
25!> `use mod_eos` facade, exactly as for the FI/LTE kernels.
26!=============================================================================
33 use mod_comm_lib, only: mpistop
34 implicit none
35 private
36
37 !> Lifecycle (PI arms of the eos_init / eos_finalise dispatchers). The eos%
38 !> block getters (update_eos_PI, get_Te_PI, get_csound2_PI, get_Rfactor_*_PI)
39 !> are PRIVATE: they are bound to eos% pointers inside eos_finalise_PI, so no
40 !> external module needs to name them.
42
43 !> get_gamma1_PI is bound to the phys_get_gamma1 pointer in the seam (the
44 !> phys layer owns that pointer), so it must stay public.
45 public :: get_gamma1_pi
46
47 !> Scalar combined-solve backend, called per-cell by the seam conversions
48 !> (rho = nH = 1 per H under the FI normalisation; eint is the GAS internal
49 !> energy, carrying the ionisation energy in energy mode).
50 public :: state_from_eint_pi !> rho,eint -> T, p, R (energy mode)
51 public :: p_eint_from_rho_t_pi !> rho,T -> p, eint, R
52 public :: eint_from_rho_p_pi !> rho,p -> eint (prim -> cons)
53 public :: csound2_prim_pi !> rho,p -> csound2
54
55 !> fl-port scalar callbacks (signature f(log_nH, log_x) -> scalar) for the
56 !> cooling / conduction port objects. PI's T-only ionisation is
57 !> nH-independent, so log_nH is ignored and quantities are evaluated per H.
58 public :: eint_from_t_pi !> (log_nH, log_T) -> eint/nH
59 public :: p2eint_pi !> (log_nH, log_p_nH) -> eint/p factor
60 public :: t_from_eint_pi !> (log_nH, log_eint_nH)-> T
61 public :: y_from_eint_pi !> (log_nH, log_eint_nH)-> ne/nH
62
63contains
64
65 !=========================================================================
66 ! A. Lifecycle
67 !=========================================================================
68
69 !> PI arm of eos_init (before units are known): PI shares the FI
70 !> temperature-from-pressure getter -- T=p/(R*rho) routed through
71 !> eos%get_Rfactor, which the seam points at the ionisation R-factor.
72 subroutine eos_init_pi()
73 eos%get_temperature_from_pressure => get_temperature_from_pressure_fi
74 end subroutine eos_init_pi
75
76 !> PI arm of eos_finalise (after unit_* are set): ride FI's ideal-gas getter
77 !> set and RR=1 normalisation (the variable mean-mass enters only via
78 !> eos%get_Rfactor, wired by the seam link arm), then initialise the
79 !> ionisation backend. In energy mode eint carries the ionisation energy, so
80 !> swap in the backend eint->T inversion.
81 subroutine eos_finalise_pi()
83 double precision :: rfactor_norm
84
85 eos%update_eos => update_eos_pi !> direct Te cache refresh each substep
86 eos%get_temperature_from_eint => get_temperature_from_eint_fi
87 eos%get_Te => get_te_pi !> read the cached Te (refreshed above)
88
89 !> R-factor: dispatch on pi_table. Physics-independent, so bound here
90 !> (was in the hd/mhd/ffhd link arms) -> these targets stay private.
91 if (eos%pi_table == 'prominence') then
92 eos%get_Rfactor => get_rfactor_prominence_pi
93 else
94 eos%get_Rfactor => get_rfactor_tonly_pi
95 end if
96 !> Fully-ionised particle counts (2 + 3*A_He per H; ne/nH = 1 + 2*A_He)
97 eos%n_per_nH_FI = 2.0d0 + 3.0d0 * eos%He_abundance
98 eos%neOnH_FI = 1.0d0 + 2.0d0 * eos%He_abundance
99
100 !> Initialise the ionisation backend. Under the FI normalisation the
101 !> (a,b) factors are absorbed into unit_*, RR=1, and b=(2+3 A_He) is the
102 !> full-ionisation reference in unit_pressure; the module's
103 !> R = (1+iz_H+A_He(...))/Rfactor_norm must reduce to 1 at full ionisation,
104 !> so Rfactor_norm = 2+3 A_He (the LTE a=b=1 value 1+4 A_He would be wrong).
105 rfactor_norm = 2.0d0 + 3.0d0 * eos%He_abundance
106 call ionization_degree_init(eos%He_abundance, rfactor_norm, &
107 trim(eos%pi_table), include_energy=eos%ionE)
108 if (mype == 0) write(*,*) "[eos PI] ionisation backend initialised: table=", &
109 trim(eos%pi_table), " ionE=", eos%ionE, " Rfactor_norm=", rfactor_norm
110
111 !> Energy mode: eint carries the ionisation energy, so swap in the
112 !> backend eint->T inversion and the energy-mode csound2 (both
113 !> physics-independent; get_csound2_PI was in the link arms).
114 if (eos%ionE) then
115 eos%get_temperature_from_eint => get_temperature_from_eint_pi
116 eos%get_csound2 => get_csound2_pi
117 end if
118 end subroutine eos_finalise_pi
119
120 !=========================================================================
121 ! B. Generic block getters (eos%-interface shape).
122 ! Module-agnostic: the primitive pressure is w(iw_e) (e_ aliases p_ in
123 ! primitive state) and the cached temperature is w(iw_te), published by
124 ! the phys module. One copy serves hd, mhd and ffhd.
125 !=========================================================================
126
127 !> Adiabatic sound speed squared from primitive (rho, p). PI energy mode.
128 subroutine get_csound2_pi(w, x, ixI^L, ixO^L, cs2)
129 integer, intent(in) :: ixi^l, ixo^l
130 double precision, intent(in) :: w(ixi^s, nw)
131 double precision, intent(in) :: x(ixi^s, 1:ndim)
132 double precision, intent(out):: cs2(ixi^s)
133
134 integer :: ix^d
135
136 {do ix^db=ixomin^db,ixomax^db\}
137 call csound2_prim_pi(w(ix^d,iw_rho), w(ix^d,iw_e), cs2(ix^d))
138 {end do\}
139 end subroutine get_csound2_pi
140
141 !> Effective Gamma1 = cs2 * rho / p for the same primitive state.
142 subroutine get_gamma1_pi(w, x, ixI^L, ixO^L, gamma1)
143 integer, intent(in) :: ixi^l, ixo^l
144 double precision, intent(in) :: w(ixi^s, nw)
145 double precision, intent(in) :: x(ixi^s, 1:ndim)
146 double precision, intent(out):: gamma1(ixi^s)
147
148 double precision :: cs2
149 integer :: ix^d
150
151 {do ix^db=ixomin^db,ixomax^db\}
152 call csound2_prim_pi(w(ix^d,iw_rho), w(ix^d,iw_e), cs2)
153 gamma1(ix^d) = cs2 * w(ix^d,iw_rho) / w(ix^d,iw_e)
154 {end do\}
155 end subroutine get_gamma1_pi
156
157 !> No-energy R-factor (chromosphere/flare T-only tables) from the stored Te_.
158 !> Denominator (2+3 He) = b (full-ion reference) absorbed into unit_pressure
159 !> under the FI normalisation, RR=1 -> R->1 at full ionisation.
160 subroutine get_rfactor_tonly_pi(w, x, ixI^L, ixO^L, Rfactor)
161 integer, intent(in) :: ixi^l, ixo^l
162 double precision, intent(in) :: w(ixi^s,1:nw)
163 double precision, intent(in) :: x(ixi^s,1:ndim)
164 double precision, intent(out):: rfactor(ixi^s)
165 double precision :: iz_h(ixo^s), iz_he(ixo^s)
166
167 call ionization_degree_from_temperature(ixi^l,ixo^l,w(ixi^s,iw_te),iz_h,iz_he)
168 rfactor(ixo^s) = (1.d0 + iz_h(ixo^s) + eos%He_abundance &
169 * (1.d0 + iz_he(ixo^s)*(1.d0+iz_he(ixo^s)))) / (2.d0 + 3.d0*eos%He_abundance)
170 end subroutine get_rfactor_tonly_pi
171
172 !> Prominence (T,p) R-factor: recompute from (rho, pth) via the backend's
173 !> prominence inversion (no-energy only -> pth=(gamma-1)*eint, R-independent).
174 subroutine get_rfactor_prominence_pi(w, x, ixI^L, ixO^L, Rfactor)
175 use mod_physics, only: phys_get_ei
176 integer, intent(in) :: ixi^l, ixo^l
177 double precision, intent(in) :: w(ixi^s,1:nw)
178 double precision, intent(in) :: x(ixi^s,1:ndim)
179 double precision, intent(out):: rfactor(ixi^s)
180 double precision :: ei(ixi^s), pth(ixi^s), rho(ixi^s), t(ixi^s)
181
182 ei(ixo^s) = phys_get_ei(w, ixi^l, ixo^l)
183 pth(ixo^s) = eos%gamma_minus_1 * ei(ixo^s)
184 rho(ixo^s) = w(ixo^s,iw_rho)
185 call ionization_get_state(ixi^l, ixo^l, rho, pth, t, rfactor)
186 end subroutine get_rfactor_prominence_pi
187
188 !> Direct (no-lag) temperature refresh: recompute the cached w(iw_te) from the
189 !> CURRENT conserved state, mirroring update_eos_LTE. Energy mode inverts the
190 !> gas internal energy (incl. ionisation energy) by Newton; no-energy modes
191 !> form pth=(gamma-1)*eint and invert (rho,pth)->T via the backend
192 !> (y-bisection for chromosphere/flare, p-bisection for prominence). Shared by
193 !> update_eos_PI (eos%update_eos) and the gated source/STS call sites, refresh Te
194 subroutine update_eos_pi(ixI^L, ixO^L, w, x)
195 use mod_physics, only: phys_get_ei
196 integer, intent(in) :: ixi^l, ixo^l
197 double precision, intent(in) :: x(ixi^s,1:ndim)
198 double precision, intent(inout) :: w(ixi^s,1:nw)
199 double precision :: ei(ixi^s), pth(ixi^s), rho(ixi^s), t(ixi^s), rf(ixi^s)
200 double precision :: tsc, psc, rsc
201 integer :: ix^d
202
203 ei(ixo^s) = phys_get_ei(w, ixi^l, ixo^l)
204 if (eos%ionE) then
205 !> energy mode (chromosphere/flare): eint carries the ionisation
206 !> energy, invert eint->T by Newton per cell.
207 {do ix^db=ixomin^db,ixomax^db\}
208 call state_from_eint_pi(w(ix^d,iw_rho), ei(ix^d), tsc, psc, rsc)
209 w(ix^d,iw_te) = tsc
210 {end do\}
211 else
212 !> no-energy: pth=(gamma-1)*eint; ionization_get_state inverts
213 !> (rho,pth)->T (T-only y-bisection or prominence p-bisection).
214 pth(ixo^s) = eos%gamma_minus_1 * ei(ixo^s)
215 rho(ixo^s) = w(ixo^s,iw_rho)
216 call ionization_get_state(ixi^l, ixo^l, rho, pth, t, rf)
217 w(ixo^s,iw_te) = t(ixo^s)
218 end if
219 end subroutine update_eos_pi
220
221 !> eos%get_Te arm: read the cached temperature (refreshed by update_eos_PI /
222 !> the gated source/STS sites), mirroring get_Te_LTE.
223 subroutine get_te_pi(w, x, ixI^L, ixO^L, T)
224 integer, intent(in) :: ixi^l, ixo^l
225 double precision, intent(in) :: w(ixi^s,1:nw)
226 double precision, intent(in) :: x(ixi^s,1:ndim)
227 double precision, intent(out) :: t(ixi^s)
228 t(ixo^s) = w(ixo^s,iw_te)
229 end subroutine get_te_pi
230
231 !> PI energy mode: temperature from GAS internal energy via the backend
232 !> eint->T inversion (eint carries the ionisation-energy term, so the FI
233 !> p=(gamma-1)*eint relation does not hold). w(iw_e) is assumed to be eint
234 !> already (the etot hook subtracts KE/ME via phys_e_to_ei before calling).
235 subroutine get_temperature_from_eint_pi(w, x, ixI^L, ixO^L, res)
236 integer, intent(in) :: ixi^l, ixo^l
237 double precision, intent(in) :: x(ixi^s,1:ndim)
238 double precision, intent(in) :: w(ixi^s,1:nw)
239 double precision, intent(out):: res(ixi^s)
240
241 double precision :: pth, rfac
242 integer :: ix^d
243
244 {do ix^db=ixomin^db,ixomax^db\}
245 call state_from_eint_pi(w(ix^d,iw_rho), w(ix^d,iw_e), &
246 res(ix^d), pth, rfac)
247 {end do\}
248 end subroutine get_temperature_from_eint_pi
249
250 !=========================================================================
251 ! C. Scalar combined-solve backend (eos%inv_gamma_minus_1 throughout,
252 ! matching the FI ideal-gas split; the ionisation-energy term is folded
253 ! in by the ionisation module when ionE=.true.).
254 !=========================================================================
255
256 !> Conserved gas internal energy -> temperature, thermal pressure, R.
257 !> Inverse of p_eint_from_rho_T_PI. eint is the GAS internal energy
258 !> (mechanical energy already removed by the caller).
259 subroutine state_from_eint_pi(rho, eint, T, p, Rfactor, iz_H, iz_He)
260 double precision, intent(in) :: rho, eint
261 double precision, intent(out) :: t, p, rfactor
262 double precision, intent(out), optional :: iz_h, iz_he
263
264 if (eos%type_id /= eos_type_pi) call mpistop( &
265 "state_from_eint_PI called outside eos_type='PI'")
266 call ionization_get_state_from_eint(rho, eint, eos%inv_gamma_minus_1, &
267 t, p, rfactor, iz_h, iz_he)
268 end subroutine state_from_eint_pi
269
270 !> Temperature -> thermal pressure and GAS internal energy (incl. ion energy)
271 subroutine p_eint_from_rho_t_pi(rho, T, p, eint, Rfactor, iz_H, iz_He)
272 double precision, intent(in) :: rho, t
273 double precision, intent(out) :: p, eint, rfactor
274 double precision, intent(out), optional :: iz_h, iz_he
275
276 if (eos%type_id /= eos_type_pi) call mpistop( &
277 "p_eint_from_rho_T_PI called outside eos_type='PI'")
278 call ionization_get_p_eint_from_rho_t(rho, t, eos%inv_gamma_minus_1, &
279 p, eint, rfactor, iz_h, iz_he)
280 end subroutine p_eint_from_rho_t_pi
281
282 !> Primitive pressure -> GAS internal energy (prim -> conserved direction):
283 !> invert (rho,p)->T then map T->eint, so the forward/inverse pair stay consistent.
284 subroutine eint_from_rho_p_pi(rho, p, eint)
285 double precision, intent(in) :: rho, p
286 double precision, intent(out) :: eint
287 double precision :: t, pcheck, rfactor
288
289 if (eos%type_id /= eos_type_pi) call mpistop( &
290 "eint_from_rho_p_PI called outside eos_type='PI'")
291 call ionization_get_state_scalar(rho, p, t, rfactor)
292 call ionization_get_p_eint_from_rho_t(rho, t, eos%inv_gamma_minus_1, &
293 pcheck, eint, rfactor)
294 end subroutine eint_from_rho_p_pi
295
296 !> Adiabatic sound speed squared from primitive (rho, p).
297 subroutine csound2_prim_pi(rho, p, csound2)
298 double precision, intent(in) :: rho, p
299 double precision, intent(out) :: csound2
300 double precision :: t, rfactor
301
302 if (eos%type_id /= eos_type_pi) call mpistop( &
303 "csound2_prim_PI called outside eos_type='PI'")
304 call ionization_get_state_scalar(rho, p, t, rfactor)
305 call ionization_get_csound2_t(t, eos%inv_gamma_minus_1, csound2)
306 end subroutine csound2_prim_pi
307
308 !=========================================================================
309 ! D. fl-port scalar callbacks (rho = nH = 1 per H). Thin shims over the
310 ! section-C backend matching the cooling/conduction port signatures.
311 !=========================================================================
312
313 !> Internal energy per H from temperature: eint/nH(T).
314 double precision function eint_from_t_pi(log_nH, log_T) result(eint)
315 double precision, intent(in) :: log_nh, log_t
316 double precision :: p, rfactor
317 call p_eint_from_rho_t_pi(1.d0, 10.d0**log_t, p, eint, rfactor)
318 end function eint_from_t_pi
319
320 !> eint/p factor from pressure per H: maps p -> eint = p * (this).
321 double precision function p2eint_pi(log_nH, log_p_nH) result(factor)
322 double precision, intent(in) :: log_nh, log_p_nh
323 double precision :: p, eint
324 p = 10.d0**log_p_nh
325 call eint_from_rho_p_pi(1.d0, p, eint)
326 factor = eint / p
327 end function p2eint_pi
328
329 !> Temperature from internal energy per H.
330 double precision function t_from_eint_pi(log_nH, log_eint_nH) result(T)
331 double precision, intent(in) :: log_nh, log_eint_nh
332 double precision :: p, rfactor
333 call state_from_eint_pi(1.d0, 10.d0**log_eint_nh, t, p, rfactor)
334 end function t_from_eint_pi
335
336 !> Electron-to-hydrogen ratio ne/nH from internal energy per H.
337 !> ne/nH = iz_H + A_He*iz_He*(1+iz_He) (matches the R-factor numerator's
338 !> electron count; second He ionisation assumed equal to the first).
339 double precision function y_from_eint_pi(log_nH, log_eint_nH) result(y)
340 double precision, intent(in) :: log_nh, log_eint_nh
341 double precision :: t, p, rfactor, iz_h, iz_he
342 call state_from_eint_pi(1.d0, 10.d0**log_eint_nh, t, p, rfactor, iz_h, iz_he)
343 y = iz_h + eos%He_abundance*iz_he*(1.d0+iz_he)
344 end function y_from_eint_pi
345
346end module mod_eos_pi
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
EoS state container – the single thermodynamic authority for AMRVAC.
integer, parameter, public eos_type_pi
type(eos_container), allocatable, public eos
The single EoS state object, allocated in eos_init and shared (read-mostly) across all EoS sub-module...
FI (fully-ionised, constant-gamma) EoS kernels for the eos% family.
Definition mod_eos_fi.t:10
subroutine, public get_temperature_from_pressure_fi(w, x, ixil, ixol, res)
FI temperature from primitive pressure: T = p / (R * rho). w is PRIMITIVE here, so iw_e holds the the...
Definition mod_eos_fi.t:112
subroutine, public get_temperature_from_eint_fi(w, x, ixil, ixol, res)
Temperature from internal energy, fully-ionised ideal gas: T = pth/(rho*R).
Definition mod_eos_fi.t:87
PI (partial-ionisation) ionisation-degree backend for the eos% family.
subroutine, public ionization_degree_init(he_abundance, rfactor_norm, table_name, include_energy)
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
subroutine, public eos_finalise_pi()
PI arm of eos_finalise (after unit_* are set): ride FI's ideal-gas getter set and RR=1 normalisation ...
Definition mod_eos_pi.t:82
subroutine, public csound2_prim_pi(rho, p, csound2)
Adiabatic sound speed squared from primitive (rho, p).
Definition mod_eos_pi.t:298
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 eos_init_pi()
Lifecycle (PI arms of the eos_init / eos_finalise dispatchers). The eos% block getters (update_eos_PI...
Definition mod_eos_pi.t:73
subroutine, public p_eint_from_rho_t_pi(rho, t, p, eint, rfactor, iz_h, iz_he)
Temperature -> thermal pressure and GAS internal energy (incl. ion energy).
Definition mod_eos_pi.t:272
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
subroutine, public eint_from_rho_p_pi(rho, p, eint)
Primitive pressure -> GAS internal energy (prim -> conserved direction): invert (rho,...
Definition mod_eos_pi.t:285
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
subroutine, public state_from_eint_pi(rho, eint, t, p, rfactor, iz_h, iz_he)
Conserved gas internal energy -> temperature, thermal pressure, R. Inverse of p_eint_from_rho_T_PI....
Definition mod_eos_pi.t:260
This module contains definitions of global parameters and variables and some generic functions/subrou...
integer mype
The rank of the current MPI task.
This module defines the procedures of a physics module. It contains function pointers for the various...
Definition mod_physics.t:4
procedure(sub_get_ei), pointer phys_get_ei
Definition mod_physics.t:68