MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_eos_container.t
Go to the documentation of this file.
1!=============================================================================
2!> EoS state container -- the single thermodynamic authority for AMRVAC.
3!>
4!> Defines the eos_container derived type: its scalar fields (gamma, He_abundance,
5!> eos_type, method_id, ionE, the unit-normalisation factors) and its
6!> procedure-pointer slots -- to_conserved/to_primitive, p_to_e,
7!> get_thermal_pressure, get_csound2, get_Rfactor, get_temperature_from_{eint,
8!> etot,pressure}, get_Te, get_ne_nH, get_rho/get_nH, update_eos -- which ARE the
9!> EoS public API the physics modules call. Also defines eos_table_container (one
10!> lookup table + its axes/guards) and the global `eos` instance plus the
11!> cached-log10(nH) wextra index iw_log_nH.
12!>
13!> Pure declarations -- no logic. The routines bound to these pointers live in
14!> the EoS sub-modules (mod_eos*) and the physics seams (mod_(m)hd_eos).
15!=============================================================================
18 implicit none
19 private
20
22 !> slot interfaces, for routines that hold or wrap an EoS slot
24
25 !> LTE method selector, decoded once from eos%method into eos%method_id so
26 !> the runtime kernels dispatch on an integer rather than a string compare.
27 integer, parameter, public :: eos_state = 1, eos_analytic = 2, eos_entropy = 3
28
29 !> EoS-type selector, decoded once from eos%eos_type into eos%type_id. Used by
30 !> the mode-specific kernels to mpistop if called under the wrong eos_type
31 !> (e.g. an LTE table kernel invoked while running FI or PI).
32 integer, parameter, public :: eos_type_fi = 1, eos_type_lte = 2, eos_type_pi = 3
33
34 abstract interface
35 subroutine convert_condition(ixI^L, ixO^L, w, x)
37 integer, intent(in) :: ixI^L, ixO^L
38 double precision, intent(inout) :: w(ixI^S, nw)
39 double precision, intent(in) :: x(ixI^S, 1:ndim)
40 end subroutine convert_condition
41
42 subroutine update_eos_spaces(ixI^L, ixO^L, w, x)
44 integer, intent(in) :: ixI^L, ixO^L
45 double precision, intent(inout) :: w(ixI^S, nw)
46 double precision, intent(in) :: x(ixI^S, 1:ndim)
47 end subroutine update_eos_spaces
48
49 subroutine get_eos_variable(ixI^L, ixO^L, w, x, res)
51 integer, intent(in) :: ixI^L, ixO^L
52 double precision, intent(in) :: w(ixI^S, nw)
53 double precision, intent(in) :: x(ixI^S, 1:ndim)
54 double precision, intent(out) :: res(ixI^S)
55 end subroutine get_eos_variable
56
57 subroutine get_eos_variable_alt(w, x, ixI^L, ixO^L, res)
59 integer, intent(in) :: ixI^L, ixO^L
60 double precision, intent(in) :: w(ixI^S, nw)
61 double precision, intent(in) :: x(ixI^S, 1:ndim)
62 double precision, intent(out) :: res(ixI^S)
63 end subroutine get_eos_variable_alt
64
65 subroutine get_ne_nh_iface(ixI^L, ixO^L, w, x, ne, nH)
67 integer, intent(in) :: ixI^L, ixO^L
68 double precision, intent(in) :: w(ixI^S, nw)
69 double precision, intent(in) :: x(ixI^S, 1:ndim)
70 double precision, intent(out) :: ne(ixI^S), nH(ixI^S)
71 end subroutine get_ne_nh_iface
72 end interface
73
75
76 character(len=std_len) :: filename
77 double precision, allocatable :: table(:,:)
78 integer :: dim1, dim2
79 double precision :: var1_min, var1_max, var2_min, var2_max
80 double precision :: step_inv_1, step_inv_2 !> precomputed (dim-1)/(max-min) for each axis
81
82 !> Curvature-adaptive (non-uniform) grid support.
83 !> When .true. (default), the grid is uniform in log10 space and
84 !> the existing affine index calculation applies. When .false.,
85 !> the table file carries an explicit pair of axis node arrays
86 !> read from the binary trailer; index lookup uses binary search
87 !> and the local fractional coordinate becomes
88 !> t1 = (var1 - var1_nodes(i)) / (var1_nodes(i+1) - var1_nodes(i))
89 !> The PCHIP/bicubic kernels themselves are unchanged.
90 !>
91 !> Naming follows the existing var1/var2 convention:
92 !> var1_nodes has length dim1, var2_nodes has length dim2.
93 logical :: is_uniform = .true.
94 double precision, allocatable :: var1_nodes(:), var2_nodes(:)
95
96 !> Guard (bucket) array for O(1) adaptive index lookup. Built when
97 !> is_uniform = .false. via eos_build_guards. For each axis:
98 !> guard_M : number of buckets covering [v(1), v(N)]
99 !> guard_scale: M / (v(N) - v(1))
100 !> guard(:) : 1-based array, size M; guard(k) is the smallest
101 !> 1-based node index ii such that v(ii) >= left
102 !> edge of bucket k (i.e., v(1) + (k-1)*span/M).
103 !> Lookup:
104 !> k = 1 + min(M-1, max(0, floor((val - v(1)) * scale)))
105 !> ii = guard(k); while (ii < N .and. v(ii) < val) ii = ii + 1
106 !> M is sized at build time so the safety while-loop iterates at
107 !> most once for typical curvature-equidistributed grids.
108 integer :: guard_m_1 = 0, guard_m_2 = 0
109 double precision :: guard_scale_1 = 0.0d0, guard_scale_2 = 0.0d0
110 integer, allocatable :: guard_1(:), guard_2(:)
111
112 end type eos_table_container
113
115
116 character(len=std_len) :: eos_type
117 character(len=20) :: method = 'state' !> 'state', 'analytic' or 'entropy' ('tables' = legacy alias for 'state')
118 integer :: method_id = eos_state !> integer form of method (set at init)
119 integer :: type_id = eos_type_fi !> integer form of eos_type (set at init)
120 character(len=20) :: gamma1_method = 'exact' !> 'exact' or 'effective'
121 character(len=20) :: p2eint_method = 'table' !> 'table' (fast) or 'bisect' (accurate)
122 logical :: p2eint_bisect = .false. !> p2eint_method == 'bisect'
123 character(len=20) :: pi_table = 'chromosphere' !> PI backend table: 'chromosphere'|'flare'|'prominence'
124 logical :: ione
125 character(len=std_len) :: table_location
126 double precision :: he_abundance
127 double precision :: gamma
128 double precision :: gamma_minus_1
129 double precision :: inv_gamma
130 double precision :: inv_gamma_minus_1
131 double precision :: inv_squared_c0
132 double precision :: inv_squared_c
133 double precision :: nh2rhofactor
134
135 !> Fully-ionised regime bypass constants (precomputed in eos_finalise)
136 double precision :: eion_per_nh !> Total ionisation energy per nH [code units]
137 double precision :: eint_rho_fi_threshold !> eint/rho above which gas is fully ionised [code units]
138 double precision :: eint_nh_min = 0.0d0 !> floor of eint/nH of the LTE paths [code units]
139 double precision :: p_rho_fi_threshold !> p/rho above which gas is fully ionised [code units]
140 double precision :: n_per_nh_fi !> Total particles per nH when FI = 2 + 3*A_He
141 double precision :: neonh_fi !> ne/nH when fully ionised = 1 + 2*A_He
142
143 !>Leaving a comment here to remind me that it's a bad idea to have anything outside
144 !> of the w() array if one wants to /ensure/ the fields are consistent
145 !> given OMP directives and load balancing
146
147 ! Expected components of the EoS
148 type(eos_table_container) :: neonh
150 type(eos_table_container) :: p2eint
151 type(eos_table_container) :: gamma1 !> First adiabatic index Gamma_1(nH, eint/nH)
152 type(eos_table_container) :: gamma1_p !> First adiabatic index Gamma_1(nH, p/nH) for fast csound2
153 type(eos_table_container) :: eint_from_t !> Inverse T table: eint/nH(nH, T)
154 type(eos_table_container) :: log_p !> Merged log10(p/nH)(nH, eint/nH) for WB bisection
155 type(eos_table_container) :: p_over_nh !> (1+He+y)*T for direct p lookup in to_primitive
156
157 !> Entropy-method tables (eos%method == 'entropy'). Each quantity Q is
158 !> stored as four containers -- Q, Q_x, Q_y, Q_xy (value plus the two
159 !> first derivatives and the cross derivative) -- fully determining the
160 !> bicubic Hermite polynomial in each cell (mod_eos_lte_entropy). Five
161 !> quantities:
162 !> Forward (log_nH, log_eint/nH): Tfwd, pfwd, neOnH.
163 !> Inverse (log_nH, log_p/nH): eintP, g1p (= Gamma_1).
164 !> Inverse (log_nH, log_T): eintT.
165 !> The inverse tables are bisected against the analytic Saha solver at
166 !> build time and frozen to disk, so every runtime query is one lookup.
167 !
168 !> Forward (lr, le): ionisation fraction. The neOnH value container
169 !> is the existing neOnH field above; add three derivative tables.
170 type(eos_table_container) :: neonh_x !> d(ne/nH)/dx
171 type(eos_table_container) :: neonh_y !> d(ne/nH)/dy
172 type(eos_table_container) :: neonh_xy !> d2(ne/nH)/(dx dy)
173 !
174 !> Forward (lr, le): direct T and p tables. We do NOT derive T from
175 !> ds/dy of the s polynomial at runtime; the bicubic Hermite gives
176 !> O(h^3) error in derivatives vs O(h^4) in values. Storing T and p
177 !> as their own bicubic Hermite tables (built from the same Saha
178 !> call as s) keeps O(h^4) accuracy and preserves Maxwell consistency
179 !> at every node (T, p, s, gamma1 all from one Saha state).
180 type(eos_table_container) :: tfwd !> T [K] (CGS-stored) at (lr, le)
181 type(eos_table_container) :: tfwd_x !> dT/dx
182 type(eos_table_container) :: tfwd_y !> dT/dy
183 type(eos_table_container) :: tfwd_xy !> d2T/(dx dy)
184 type(eos_table_container) :: pfwd !> p [erg/cm^3] (CGS-stored) at (lr, le)
185 type(eos_table_container) :: pfwd_x !> dp/dx
186 type(eos_table_container) :: pfwd_y !> dp/dy
187 type(eos_table_container) :: pfwd_xy !> d2p/(dx dy)
188 !
189 !> Inverse (lr, lp): eint from (rho, p), replaces p2eint bisection
190 !> at runtime. Stored as log10(eint/nH), CGS axis units before shift.
191 type(eos_table_container) :: eintp !> log10(eint/nH) [CGS log10]
192 type(eos_table_container) :: eintp_x !> d(eintP)/dx x = log10(nH)
193 type(eos_table_container) :: eintp_y !> d(eintP)/dy y = log10(p/nH)
194 type(eos_table_container) :: eintp_xy !> d2(eintP)/(dx dy)
195 !
196 !> Inverse (lr, lp): gamma_1 at (rho, p) for csound2-from-pressure paths
197 type(eos_table_container) :: g1p !> Gamma_1(rho, p) dimensionless
198 type(eos_table_container) :: g1p_x !> dGamma_1/dx
199 type(eos_table_container) :: g1p_y !> dGamma_1/dy y = log10(p/nH)
200 type(eos_table_container) :: g1p_xy !> d2Gamma_1/(dx dy)
201 !
202 !> Inverse (lr, lT): eint from (rho, T). Used by IC, HSE BC, cooling.
203 type(eos_table_container) :: eintt !> log10(eint/nH) [CGS log10]
204 type(eos_table_container) :: eintt_x !> d(eintT)/dx x = log10(nH)
205 type(eos_table_container) :: eintt_y !> d(eintT)/dy y = log10(T)
206 type(eos_table_container) :: eintt_xy !> d2(eintT)/(dx dy)
207 !> Interleaved Group A table: T, neOnH, p_over_nH at same (nH, eint/nH) grid
208 double precision, allocatable :: table_eint_il(:,:,:) !> (3, dim1, dim2)
209 integer :: il_nq = 3 !> number of interleaved quantities
210
211 procedure(convert_condition), pointer, nopass :: to_conserved => null()
212 procedure(convert_condition), pointer, nopass :: to_primitive => null()
213 procedure(convert_condition), pointer, nopass :: p_to_e => null()
214 procedure(update_eos_spaces), pointer, nopass :: update_eos => null()
215 procedure(get_eos_variable_alt), pointer, nopass :: get_thermal_pressure => null()
216 procedure(get_eos_variable_alt), pointer, nopass :: get_temperature_from_eint => null()
217 procedure(get_eos_variable_alt), pointer, nopass :: get_temperature_from_etot => null()
218 procedure(get_eos_variable_alt), pointer, nopass :: get_temperature_from_pressure => null()
219 procedure(get_eos_variable_alt), pointer, nopass :: get_rfactor => null()
220 procedure(get_eos_variable_alt), pointer, nopass :: get_rho => null()
221 procedure(get_eos_variable_alt), pointer, nopass :: get_nh => null()
222 procedure(get_ne_nh_iface), pointer, nopass :: get_ne_nh => null()
223 procedure(get_eos_variable_alt), pointer, nopass :: get_te => null()
224 procedure(get_eos_variable_alt), pointer, nopass :: get_csound2 => null()
225
226 contains
227
228 !> Internal setter for gamma: updates gamma + the three derived cached
229 !> values (gamma_minus_1, inv_gamma, inv_gamma_minus_1) atomically.
230 !> PRIVATE on purpose: gamma is set ONLY via the parfile (&eos_list gamma=),
231 !> read once at eos_init, so the derived constants can never go stale.
232 !> User code must not set gamma; assigning eos%gamma directly is caught by
233 !> the consistency check in (m)hd_check_params.
234 procedure, private :: set_gamma => eos_set_gamma
235
236 end type eos_container
237
238 !> The single EoS state object, allocated in eos_init and shared (read-mostly)
239 !> across all EoS sub-modules and the physics layer.
240 type(eos_container), allocatable, public :: eos
241
242 !> wextra index for the cached log10(nH) used during STS substeps
243 !> (-1 = not allocated). Set by the physics module that owns the wextra slot.
244 integer, public :: iw_log_nh = -1
245
246contains
247
248 subroutine eos_set_gamma(this, gamma_new)
249 class(eos_container), intent(inout) :: this
250 double precision, intent(in) :: gamma_new
251
252 this%gamma = gamma_new
253 this%gamma_minus_1 = this%gamma - 1.0d0
254 this%inv_gamma = 1.0d0 / this%gamma
255 if (abs(this%gamma_minus_1) > 1.0d-12) then
256 this%inv_gamma_minus_1 = 1.0d0 / this%gamma_minus_1
257 else
258 this%inv_gamma_minus_1 = 0.0d0
259 end if
260 end subroutine eos_set_gamma
261
262end module mod_eos_container
263!> Needs a line after to pass the preprocesor
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
slot interfaces, for routines that hold or wrap an EoS slot
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)....
This module contains definitions of global parameters and variables and some generic functions/subrou...