MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_eos.t
Go to the documentation of this file.
1!> Equation of state for AMRVAC, handled through a single eos_container object.
2!>
3!> Two gas models (eos_type):
4!> FI -- fully ionised ideal gas (constant gamma, mu).
5!> LTE -- local thermodynamic equilibrium with partial ionisation of H (+He),
6!> via one of three interchangeable methods (eos_method):
7!> state -- per-quantity (T,p,ne/nH,eint/p) Saha-equilibrium tables,
8!> PCHIP/bilinear interpolated ('tables' accepted as legacy alias).
9!> entropy -- bicubic-Hermite reconstruction (see mod_eos_LTE_entropy).
10!> analytic -- on-the-fly H-only Saha solve (see saha_* routines).
11!> Tables are generated offline and read as binary; their axes are
12!> (log10 nH, log10 eint/nH) in code units after the unit shift applied
13!> in eos_finalise. ionE optionally folds ionisation energy into eint.
14!>
15!> The code-unit normalisation is kept so an FI atmosphere recovers R = 1;
16!> update_eos_LTE relies on this. update_eos refreshes the cached EoS fields
17!> (Te, ne) once per RK substep, and for boundary cells via update_eos_4_bc;
18!> call eos%update_eos manually before any extra output.
19!>
20!> Jack Jenkins (27.01.2026); legacy EoS type 1: uniform tabulated (by Chris Osborne) H(+He) LTE EoS, in
21!> consultation with Damien Przybylski (MURaM). Routed through legacy usr_Rfactor approach.
22!> Jack Jenkins (05.05.2026); EoS type 2 finished, now separate module that each hd/mhd physics module
23!> from upstream/amrvac3.3 route through.
24!> Jack Jenkins (11.05.2026); Benchmarked curvature-density-hybrid-tabulated EoS tables.
25!> Jack Jenkins (25.05.2026); Single curvature-density-tabulated entropy potential EoS prototyped. Validated
26!> gamma(T) against Ballester et al. 2001 to ~ 1.1% .
27!> Jack Jenkins (16.06.2026); Folded in fld, ffhd, and new partial_ionisation modules to be EoS compliant
28!> and refactored for readability.
29
30module mod_eos
37 use mod_eos_fi
38 use mod_eos_lte
40 use mod_timing
41 use mod_comm_lib, only: mpistop
42
43 implicit none
44 private
45
46 character(len=std_len) :: AMRVAC_DIR
47
48 !> The EoS state object (eos) and the cached-log10(nH) wextra index
49 !> (iw_log_nH) now live in mod_eos_container so every EoS sub-module can
50 !> reach them; re-export here so existing `use mod_eos` callers still see them.
51 public :: eos, iw_log_nh
52
53 !> Lifecycle (called from amrvac.t)
55 !> Type-agnostic helpers, safe in any eos_type. The mode-specific scalar
56 !> kernels (LTE T/y/p/eint/gamma1, saha_*, the PI state/eint/csound2 backend
57 !> and fl-port shims) are deliberately NOT re-exported here: the hd/mhd/ffhd
58 !> seams reach them via their sub-modules (mod_eos_LTE / mod_eos_LTE_saha /
59 !> mod_eos_PI), and each kernel mpistops if called under the wrong
60 !> eos_type/method. So `use mod_eos` no longer exposes mode-specific routines.
62 !> Ideal-gas Gamma_1 (returns eos%gamma); bound to phys_get_gamma1 for FI and
63 !> no-energy PI, valid in any ideal-gas context, so kept on the facade.
64 public :: get_gamma1_fi
65
66contains
67
68 !> Lifecycle: read and validate parameters, allocate, load and finalise
69 !> the chosen backend. Called from amrvac.t.
70 !> Read this module"s parameters from a file
71 subroutine eos_read_params(files)
72 character(len=*), intent(in) :: files(:)
73 integer :: n
74
75 !> Namelist mirror variables: the parfile uses these simple names
76 !> (e.g. eos_type='FI'); they are committed to eos% in one block after
77 !> the read. Derived eos% quantities (gamma_minus_1, method_id, ...) are
78 !> NOT mirrored here -- they are computed from these inputs below.
79 character(len=std_len) :: eos_type, table_location
80 character(len=20) :: eos_method, gamma1_method, p2eint_method, pi_table
81 double precision :: He_abundance, gamma
82 logical :: ionE
83
84 namelist /eos_list/ eos_type, table_location, ione, he_abundance, gamma, &
85 eos_method, gamma1_method, p2eint_method, pi_table
86
87 !> defaults
88 eos_type = 'FI'
89 eos_method = 'state'
90 pi_table = 'chromosphere'
91 gamma1_method = 'exact'
92 p2eint_method = 'table'
93 ione = .false.
94 he_abundance = 0.1d0
95 gamma = 5.0d0/3.0d0
96 call get_environment_variable("AMRVAC_DIR", amrvac_dir)
97 table_location = trim(amrvac_dir)//"/src/tables/eos_tables/uniform256/"
98
99 !> read parfile(s): each successive file overrides the previous
100 do n = 1, size(files)
101 open(unitpar, file=trim(files(n)), status="old")
102 read(unitpar, eos_list, end=111)
103 111 close(unitpar)
104 end do
105
106 !> commit raw inputs to eos% (plain copies, no logic)
107 eos%eos_type = eos_type
108 eos%method = eos_method
109 eos%pi_table = pi_table
110 eos%gamma1_method = gamma1_method
111 eos%p2eint_method = p2eint_method
112 eos%table_location = table_location
113 eos%He_abundance = he_abundance
114 eos%gamma = gamma
115 eos%ionE = ione
116
117 !> derive secondary quantities from the raw inputs
118 eos%gamma_minus_1 = eos%gamma - 1.0d0
119 eos%inv_gamma = 1.0d0 / eos%gamma
120 if (abs(eos%gamma_minus_1) > 1.0d-12) then
121 eos%inv_gamma_minus_1 = 1.0d0 / eos%gamma_minus_1
122 else
123 eos%inv_gamma_minus_1 = 0.0d0
124 end if
125 eos%nH2rhoFactor = 1.0d0 !> FI default; (m)hd_physical_units may reset
126 select case (eos%method)
127 case ('analytic'); eos%method_id = eos_analytic
128 case ('entropy'); eos%method_id = eos_entropy
129 case default; eos%method_id = eos_state
130 end select
131 select case (eos%eos_type)
132 case ('LTE'); eos%type_id = eos_type_lte
133 case ('PI'); eos%type_id = eos_type_pi
134 case default; eos%type_id = eos_type_fi
135 end select
136
137 !> report
138 if (mype == 0) then
139 write(*,*) "EoS type: "//trim(eos%eos_type)
140 if (eos%eos_type == 'LTE') &
141 write(*,*) "EoS table location: "//trim(eos%table_location)
142 end if
143
144 !> validate / coerce (all consistency guards live in one place)
145 call eos_validate_params()
146
147 end subroutine eos_read_params
148
149 !> Validate and coerce the parsed eos_list against the chosen eos_type /
150 !> method. Grouped by concern so the rules are readable in one block (rather
151 !> than interleaved with the field copies in eos_read_params). May coerce
152 !> eos% fields (e.g. FI forces ionE=.false.) and mpistop on illegal combos.
153 subroutine eos_validate_params()
154
155 !> eos_type x ionE
156 if (eos%eos_type == 'FI' .and. eos%ionE) then
157 eos%ionE = .false.
158 if(mype==0) write(*,*) "WARNING: eos_type = 'FI' requires ionE = .false."
159 end if
160
161 !> PI ('partial approximations') table selector
162 if (eos%eos_type == 'PI') then
163 select case (trim(eos%pi_table))
164 case ('chromosphere','flare','prominence')
165 ! valid
166 case default
167 call mpistop("eos_type='PI': pi_table must be "// &
168 "'chromosphere', 'flare', or 'prominence'")
169 end select
170 !> ionisation-energy mode (ionE) is supported only for the T-only
171 !> tables; the prominence table is (T,p)-dependent -> no-energy only.
172 !> This also gates the only PI configuration that cannot model He
173 !> ionisation energy: the chromosphere/flare energy paths now fold
174 !> the He ionisation energy into eint (mod_eos_PI_tables,
175 !> ionization_eps_ion_of_degrees), consistent with the He electron
176 !> count already in the R-factor, so He>0 energy mode is allowed there.
177 if (eos%ionE .and. trim(eos%pi_table) == 'prominence') then
178 call mpistop("eos_type='PI' with ionE=T requires a T-only table "// &
179 "(chromosphere or flare); prominence is no-energy only")
180 end if
181 if(mype==0) write(*,*) "EoS PI table: "//trim(eos%pi_table)// &
182 ", ionE=", eos%ionE
183 end if
184
185 !> eos_method validity and eos_type x method ('tables' kept as a legacy
186 !> alias for 'state' -- both decode to EOS_STATE via the case default).
187 if (eos%method /= 'analytic' .and. eos%method /= 'state' &
188 .and. eos%method /= 'tables' .and. eos%method_id /= eos_entropy) then
189 call mpistop("Unknown eos_method: "//trim(eos%method)// &
190 " (expected 'state', 'analytic', or 'entropy')")
191 end if
192 if (eos%eos_type == 'FI' .and. eos%method_id == eos_analytic) then
193 call mpistop("eos_method='analytic' requires eos_type='LTE'")
194 end if
195
196 !> gamma1_method validity
197 if (eos%gamma1_method /= 'exact' .and. eos%gamma1_method /= 'effective' &
198 .and. eos%gamma1_method /= 'constant') then
199 call mpistop("Unknown gamma1_method: "//trim(eos%gamma1_method)// &
200 " (expected 'exact', 'effective', or 'constant')")
201 end if
202
203 !> analytic-method caveats (H-only Saha)
204 if (eos%method_id == eos_analytic) then
205 if (eos%He_abundance > 0.0d0 .and. mype == 0) write(*,*) &
206 "WARNING: eos_method='analytic' is H-only. He_abundance=", &
207 eos%He_abundance, " used in pressure law but not in Saha ionization."
208 ! effective gamma1 needs T,neOnH tables -> not available analytically
209 if (eos%gamma1_method == 'effective') then
210 if (mype == 0) write(*,*) &
211 "WARNING: gamma1_method='effective' not applicable with analytic EoS."
212 if (mype == 0) write(*,*) &
213 " Falling back to gamma1_method='exact' (analytic 2D table)."
214 eos%gamma1_method = 'exact'
215 end if
216 if (.not. eos%ionE .and. mype == 0) then
217 write(*,*) "WARNING: eos_method='analytic' without ionE"
218 write(*,*) " reduces to ideal gas. Consider eos_type='FI'."
219 end if
220 end if
221
222 !> LTE+ionE effective-gamma1 note
223 if (eos%eos_type == 'LTE' .and. eos%ionE .and. &
224 eos%gamma1_method == 'effective') then
225 if (mype == 0) write(*,*) &
226 "NOTE: gamma1_method='effective' builds gamma_eff = 1+p/eint table."
227 end if
228
229 end subroutine eos_validate_params
230
231 !> Phase 'create' (before units are known): allocate the EoS object, read
232 !> its &eos_list parameters, and load the raw tables for the chosen method.
233 !> The getters wired here must be live before (m)hd_link_eos runs.
234 subroutine eos_init()
235 allocate(eos)
236 call eos_read_params(par_files)
237
238 !> Type-agnostic getters: always live (eos% pointer targets).
239 eos%get_rho => get_rho
240 eos%get_nH => get_nh
241 eos%get_ne_nH => get_ne_nh
242
243 !> Per-type create-phase init (each owns its raw table loads / pointer
244 !> wiring that must be live before (m)hd_link_eos runs), in the matching
245 !> mod_eos_<TYPE> module.
246 select case (eos%eos_type)
247 case ('FI'); call eos_init_fi()
248 case ('LTE'); call eos_init_lte()
249 case ('PI'); call eos_init_pi()
250 case default
251 call mpistop('eos_init: eos_type '//trim(eos%eos_type)//' not recognised')
252 end select
253 end subroutine eos_init
254
255 !> Phase 'commit' (after units are known): finalise the dispatch for the
256 !> loaded physics -- wire the method's runtime pointers and finalise its
257 !> tables. The cross-module wiring that follows (into the ghost-cell update
258 !> and the physics source terms) is done by the caller in amrvac.t, so the
259 !> EoS never reaches back into those modules.
260 !> Finalise the EoS once units are set: dispatch on eos_type to the per-type
261 !> finaliser (each owns its pointer wiring + backend table loading, living in
262 !> mod_eos_FI / mod_eos_LTE / mod_eos_PI), then wire the shared total-energy
263 !> temperature getter. Called from amrvac.t after phys init.
264 subroutine eos_finalise()
265 if (.not. allocated(eos)) &
266 call mpistop('eos_finalise: eos not allocated')
267
268 select case (eos%eos_type)
269 case ('LTE')
270 call eos_finalise_lte()
271 case ('FI')
272 call eos_finalise_fi()
273 case ('PI')
274 call eos_finalise_pi()
275 case default
276 call mpistop('eos_finalise: eos_type '//trim(eos%eos_type)//' not recognised')
277 end select
278
279 eos%get_temperature_from_etot => get_temperature_from_etot
280 end subroutine eos_finalise
281
283 integer :: iigrid, igrid
284
285 ! fill eos space of all grids, inc boundaries
286 do iigrid=1,igridstail_active; igrid=igrids_active(iigrid);
287 call eos%update_eos(ixg^ll,ixg^ll,ps(igrid)%w,ps(igrid)%x)
288 end do
289
290 end subroutine prepare_eos_w_fields
291
292end module mod_eos
293!> 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
integer, parameter, public eos_type_pi
integer, parameter, public eos_type_fi
EoS-type selector, decoded once from eoseos_type into eostype_id. Used by the mode-specific kernels t...
integer, parameter, public eos_state
LTE method selector, decoded once from eosmethod into eosmethod_id so the runtime kernels dispatch on...
integer, parameter, public eos_entropy
type(eos_container), allocatable, public eos
The single EoS state object, allocated in eos_init and shared (read-mostly) across all EoS sub-module...
integer, parameter, public eos_type_lte
integer, public iw_log_nh
wextra index for the cached log10(nH) used during STS substeps (-1 = not allocated)....
FI (fully-ionised, constant-gamma) EoS kernels for the eos% family.
Definition mod_eos_FI.t:10
subroutine, public get_gamma1_fi(w, x, ixil, ixol, gamma1)
Gamma_1 for the fully-ionised ideal gas: constant eosgamma.
Definition mod_eos_FI.t:57
Interpolation kernels for the EoS tables (pure math; no EoS state).
Analytic H-only Saha EoS (eos_method == 'analytic').
Shared LTE lookup-table infrastructure (build/IO; not on the hot path).
LTE (Saha-table) EoS kernels and finalise for the eos% family.
Definition mod_eos_LTE.t:12
PI (partial-ionisation, eos_type='PI') arm of the eos% family.
Definition mod_eos_PI.t:27
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:83
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:74
Shared EoS accessors (EoS-type-agnostic) for the eos% family.
subroutine, public get_ne_nh(ixil, ixol, w, ne, nh)
Return electron and hydrogen number densities in code units. For LTE (iw_ne allocated): ne from Saha ...
double precision function, public eos_get_log_t_floor()
Lower log10(T) bound for the (rho,T) inverse table; FI (no tables) floors at log10(smalldouble)....
Equation of state for AMRVAC, handled through a single eos_container object.
Definition mod_eos.t:30
subroutine, public eos_init()
Phase 'create' (before units are known): allocate the EoS object, read its &eos_list parameters,...
Definition mod_eos.t:235
subroutine, public eos_finalise()
Phase 'commit' (after units are known): finalise the dispatch for the loaded physics – wire the metho...
Definition mod_eos.t:265
subroutine, public prepare_eos_w_fields()
Definition mod_eos.t:283
This module contains definitions of global parameters and variables and some generic functions/subrou...
integer, parameter unitpar
file handle for IO
integer mype
The rank of the current MPI task.
double precision, dimension(:), allocatable, parameter d