From d9192076a56a9a553950eb4f431bb13d728a7b13 Mon Sep 17 00:00:00 2001 From: Fahad Nabid <55717325+fahnab666@users.noreply.github.com> Date: Mon, 20 Jul 2026 23:41:29 -0400 Subject: [PATCH] Add per-fluid eos selector with stable EOS enumeration Introduce fluid_pp(:)%eos, a stable five-value enumeration (stiffened_gas, ideal_gas_mixture, mie_gruneisen, jwl, table) replacing implicit backend selection. Only stiffened_gas (default) and ideal_gas_mixture (chemistry, Pyrometheus) are backed by an adapter; the remaining values are reserved and rejected explicitly. The default resolves from the compile-time chemistry flag so all existing goldens stay bit-identical, and no runtime dispatch enters the hot per-cell path. Case files accept readable names; the integer representation stays internal. Checkers (Fortran and Python) reject unsupported values, ideal_gas_mixture without a chemistry build, and intra-cell EOS mixing (every fluid must share one family). The five enum constants are hand-written in m_constants.fpp; the constant generator now skips compound registry keys so the per-fluid CONSTRAINTS entries drive only readable-name resolution and validation. --- docs/documentation/case.md | 2 ++ src/common/m_checker_common.fpp | 18 +++++++++++ src/common/m_constants.fpp | 9 ++++++ src/common/m_derived_types.fpp | 1 + src/common/m_global_parameters_common.fpp | 2 +- src/post_process/m_global_parameters.fpp | 1 + src/pre_process/m_global_parameters.fpp | 1 + src/simulation/m_global_parameters.fpp | 1 + toolchain/mfc/case_validator.py | 32 +++++++++++++++++++ toolchain/mfc/params/definitions.py | 13 ++++++++ toolchain/mfc/params/descriptions.py | 1 + .../mfc/params/generators/fortran_gen.py | 4 +++ 12 files changed, 84 insertions(+), 1 deletion(-) diff --git a/docs/documentation/case.md b/docs/documentation/case.md index a0292e94ef..51d1d29eb9 100644 --- a/docs/documentation/case.md +++ b/docs/documentation/case.md @@ -450,6 +450,8 @@ The parameters define material's property of compressible fluids that are used i - `fluid_pp(i)%%gamma` and `fluid_pp(i)%%pi_inf` define \f$\Gamma\f$ and \f$\Pi\f$ as parameters of $i$-th fluid that are used in stiffened gas equation of state. +- `fluid_pp(i)%%eos` selects the equation of state of the $i$-th fluid. Only `stiffened_gas` (the default) and `ideal_gas_mixture` (requires a chemistry build, backed by Pyrometheus) are currently supported; the enumeration reserves `mie_gruneisen`, `jwl`, and `table` for future backends. Every fluid in a run must use the same family. + - `fluid_pp(i)%%Re(1)` and `fluid_pp(i)%%Re(2)` define the shear and volume viscosities of $i$-th fluid, respectively. When these parameters are undefined, fluids are treated as inviscid. diff --git a/src/common/m_checker_common.fpp b/src/common/m_checker_common.fpp index dfdcff7678..df48166b78 100644 --- a/src/common/m_checker_common.fpp +++ b/src/common/m_checker_common.fpp @@ -25,12 +25,30 @@ contains #ifndef MFC_SIMULATION call s_check_total_cells #endif + call s_check_eos #:if USING_AMD call s_check_amd #:endif end subroutine s_check_inputs_common + !> Restrict the per-fluid EOS selector to the currently supported adapters. Only stiffened_gas (non-chemistry) and + !! ideal_gas_mixture (chemistry) are backed by a thermodynamics backend, and a single run uses one family for every fluid, so + !! the unimplemented values and any intra-cell EOS mixing are both rejected here. + impure subroutine s_check_eos + + integer :: i + + do i = 1, num_fluids + @:PROHIBIT(chemistry .and. fluid_pp(i)%eos /= eos_ideal_gas_mixture, & + & "fluid_pp(:)%eos must be 'ideal_gas_mixture' for every fluid when chemistry is enabled") + @:PROHIBIT(.not. chemistry .and. fluid_pp(i)%eos /= eos_stiffened_gas, & + & "fluid_pp(:)%eos selector is not supported; only 'stiffened_gas' is available " & + & // "(or 'ideal_gas_mixture' with a chemistry build)") + end do + + end subroutine s_check_eos + #ifndef MFC_SIMULATION !> Verify that the total number of grid cells meets the minimum required by the number of dimensions and MPI ranks. impure subroutine s_check_total_cells diff --git a/src/common/m_constants.fpp b/src/common/m_constants.fpp index 4857d93249..24249dca7a 100644 --- a/src/common/m_constants.fpp +++ b/src/common/m_constants.fpp @@ -114,6 +114,15 @@ module m_constants integer, parameter :: BC_NO_SLIP_WALL = -16 integer, parameter :: BC_DIRICHLET = -17 + ! Equation-of-state selector (fluid_pp(:)%eos). Values must match _EOS_NAMES in + ! toolchain/mfc/params/definitions.py. Only stiffened_gas and ideal_gas_mixture + ! are currently backed by a thermodynamics adapter. + integer, parameter :: eos_stiffened_gas = 1 + integer, parameter :: eos_ideal_gas_mixture = 2 + integer, parameter :: eos_mie_gruneisen = 3 + integer, parameter :: eos_jwl = 4 + integer, parameter :: eos_table = 5 + ! Synthetic turbulence array size limits integer, parameter :: num_synth_shells_max = 50 !< Max energy shells for synthetic turbulence integer, parameter :: num_turb_sources_max = 10 !< Max Gaussian forcing zones for synthetic turbulence diff --git a/src/common/m_derived_types.fpp b/src/common/m_derived_types.fpp index 45b0394679..e39cf2a2b9 100644 --- a/src/common/m_derived_types.fpp +++ b/src/common/m_derived_types.fpp @@ -379,6 +379,7 @@ module m_derived_types !> Derived type annexing the physical parameters (PP) of the fluids. These include the specific heat ratio function and liquid !! stiffness function. type physical_parameters + integer :: eos !< Equation of state selector (eos_* in m_constants) real(wp) :: gamma !< Sp. heat ratio real(wp) :: pi_inf !< Liquid stiffness real(wp), dimension(2) :: Re !< Reynolds number diff --git a/src/common/m_global_parameters_common.fpp b/src/common/m_global_parameters_common.fpp index 13e84c8b79..443956bce3 100644 --- a/src/common/m_global_parameters_common.fpp +++ b/src/common/m_global_parameters_common.fpp @@ -16,7 +16,7 @@ module m_global_parameters_common use m_derived_types use m_thermochem, only: num_species use m_constants, only: model_eqns_gamma_law, model_eqns_5eq, model_eqns_6eq, model_eqns_4eq, recon_type_weno, & - & recon_type_muscl, name_len, dflt_int, dflt_real + & recon_type_muscl, name_len, dflt_int, dflt_real, eos_stiffened_gas, eos_ideal_gas_mixture implicit none diff --git a/src/post_process/m_global_parameters.fpp b/src/post_process/m_global_parameters.fpp index 7e1c804700..90846750d3 100644 --- a/src/post_process/m_global_parameters.fpp +++ b/src/post_process/m_global_parameters.fpp @@ -211,6 +211,7 @@ contains fluid_pp(i)%cv = 0._wp fluid_pp(i)%qv = 0._wp fluid_pp(i)%qvp = 0._wp + fluid_pp(i)%eos = merge(eos_ideal_gas_mixture, eos_stiffened_gas, chemistry) fluid_pp(i)%G = dflt_real fluid_pp(i)%non_newtonian = .false. fluid_pp(i)%K = dflt_real diff --git a/src/pre_process/m_global_parameters.fpp b/src/pre_process/m_global_parameters.fpp index 1e0fcc22f0..1357740ee8 100644 --- a/src/pre_process/m_global_parameters.fpp +++ b/src/pre_process/m_global_parameters.fpp @@ -392,6 +392,7 @@ contains fluid_pp(i)%cv = 0._wp fluid_pp(i)%qv = 0._wp fluid_pp(i)%qvp = 0._wp + fluid_pp(i)%eos = merge(eos_ideal_gas_mixture, eos_stiffened_gas, chemistry) fluid_pp(i)%G = 0._wp fluid_pp(i)%non_newtonian = .false. fluid_pp(i)%K = dflt_real diff --git a/src/simulation/m_global_parameters.fpp b/src/simulation/m_global_parameters.fpp index f52d55a87b..604071f424 100644 --- a/src/simulation/m_global_parameters.fpp +++ b/src/simulation/m_global_parameters.fpp @@ -430,6 +430,7 @@ contains fluid_pp(i)%cv = 0._wp fluid_pp(i)%qv = 0._wp fluid_pp(i)%qvp = 0._wp + fluid_pp(i)%eos = merge(eos_ideal_gas_mixture, eos_stiffened_gas, chemistry) fluid_pp(i)%Re(:) = dflt_real fluid_pp(i)%G = 0._wp fluid_pp(i)%non_newtonian = .false. diff --git a/toolchain/mfc/case_validator.py b/toolchain/mfc/case_validator.py index 1099b51328..b2d1b72d82 100644 --- a/toolchain/mfc/case_validator.py +++ b/toolchain/mfc/case_validator.py @@ -42,6 +42,16 @@ "explanation": ("MFC uses the transformed stiffened gas parameter. A common mistake is entering the physical gamma (e.g., 1.4 for air) instead of the transformed value 1/(gamma-1) = 2.5."), "references": ["Wilfong26", "Allaire02"], }, + "check_eos": { + "title": "Equation of State Selection", + "category": "Thermodynamic Constraints", + "explanation": ( + "The per-fluid eos selector exposes only the backends with a thermodynamics adapter: " + "'stiffened_gas' (default) and 'ideal_gas_mixture' (chemistry, Pyrometheus). A single run " + "uses one family for every fluid; unimplemented values and intra-cell EOS mixing are rejected." + ), + "references": ["Wilfong26"], + }, "check_patch_physics": { "title": "Patch Initial Condition Constraints", "category": "Thermodynamic Constraints", @@ -730,6 +740,27 @@ def check_stiffened_eos(self): self.prohibit(gamma is not None, f"model_eqns = 1 does not support fluid_pp({i})%gamma") self.prohibit(pi_inf is not None, f"model_eqns = 1 does not support fluid_pp({i})%pi_inf") + def check_eos(self): + """Restricts the per-fluid EOS selector to the currently supported adapters""" + num_fluids = self.get("num_fluids") + chemistry = self.get("chemistry", "F") == "T" + + if num_fluids is None: + return + + eos_stiffened_gas, eos_ideal_gas_mixture = 1, 2 + + for i in range(1, num_fluids + 1): + eos = self.get(f"fluid_pp({i})%eos") + if eos is None: + continue + self.prohibit( + eos not in (eos_stiffened_gas, eos_ideal_gas_mixture), + f"fluid_pp({i})%eos selects an equation of state that is not yet implemented; " "only 'stiffened_gas' and 'ideal_gas_mixture' are available", + ) + self.prohibit(chemistry and eos != eos_ideal_gas_mixture, f"fluid_pp({i})%eos must be 'ideal_gas_mixture' when chemistry is enabled") + self.prohibit(not chemistry and eos == eos_ideal_gas_mixture, f"fluid_pp({i})%eos = 'ideal_gas_mixture' requires a chemistry build") + def check_surface_tension(self): """Checks constraints on surface tension""" surface_tension = self.get("surface_tension", "F") == "T" @@ -2277,6 +2308,7 @@ def validate_common(self): self.check_phase_change() self.check_ibm() self.check_stiffened_eos() + self.check_eos() self.check_eos_parameter_sanity() self.check_surface_tension() self.check_mhd() diff --git a/toolchain/mfc/params/definitions.py b/toolchain/mfc/params/definitions.py index b2514f0ce7..ea6b2eb0d4 100644 --- a/toolchain/mfc/params/definitions.py +++ b/toolchain/mfc/params/definitions.py @@ -113,6 +113,7 @@ def _fc(name: str, default: int) -> int: "cv": "Specific heat at constant volume", "qv": "Heat of formation", "qvp": "Heat of formation derivative", + "eos": "Equation of state selector", }, } @@ -386,6 +387,17 @@ def get_value_label(param_name: str, value: int) -> str: "p": {"min": 0}, } +# Per-fluid equation-of-state selector. The enumeration is stable (its integer +# values must match the eos_* constants hand-written in src/common/m_constants.fpp); +# only stiffened_gas and ideal_gas_mixture are currently backed by a thermodynamics +# adapter, so the remaining values are rejected explicitly in the checkers. The +# constants are NOT auto-generated from here: generate_constants_fpp skips compound +# keys, so these entries only drive readable-name resolution and validation. +_EOS_VALUE_LABELS = {1: "stiffened-gas", 2: "ideal-gas mixture", 3: "Mie-Grueneisen", 4: "JWL", 5: "tabulated"} +_EOS_NAMES = {"stiffened_gas": 1, "ideal_gas_mixture": 2, "mie_gruneisen": 3, "jwl": 4, "table": 5} +for _f in range(1, NF + 1): + CONSTRAINTS[f"fluid_pp({_f})%eos"] = {"choices": [1, 2, 3, 4, 5], "value_labels": _EOS_VALUE_LABELS, "names": _EOS_NAMES} + # Parameter dependencies (requires, recommends) DEPENDENCIES = { "bubbles_euler": { @@ -876,6 +888,7 @@ def _load(): px = f"fluid_pp({f})%" for a, sym in [("gamma", r"\f$\gamma_k\f$"), ("pi_inf", r"\f$\pi_{\infty,k}\f$"), ("cv", r"\f$c_{v,k}\f$"), ("qv", r"\f$q_{v,k}\f$"), ("qvp", r"\f$q'_{v,k}\f$")]: _r(f"{px}{a}", REAL, math=sym) + _r(f"{px}eos", INT, math=r"\f$\mathrm{EOS}_k\f$") _r(f"{px}G", REAL, {"elasticity"}, math=r"\f$G_k\f$") _r(f"{px}Re(1)", REAL, {"viscosity"}, math=r"\f$\mathrm{Re}_k\f$ (shear)") _r(f"{px}Re(2)", REAL, {"viscosity"}, math=r"\f$\mathrm{Re}_k\f$ (bulk)") diff --git a/toolchain/mfc/params/descriptions.py b/toolchain/mfc/params/descriptions.py index addcdf5432..09ee23a347 100644 --- a/toolchain/mfc/params/descriptions.py +++ b/toolchain/mfc/params/descriptions.py @@ -338,6 +338,7 @@ (r"fluid_pp\((\d+)\)%cv", "Specific heat at constant volume for fluid {0}"), (r"fluid_pp\((\d+)\)%qv", "Heat of formation for fluid {0}"), (r"fluid_pp\((\d+)\)%qvp", "Heat of formation prime for fluid {0}"), + (r"fluid_pp\((\d+)\)%eos", "Equation of state selector for fluid {0}"), (r"fluid_pp\((\d+)\)%Re\((\d+)\)", "Reynolds number component {1} for fluid {0}"), (r"fluid_pp\((\d+)\)%non_newtonian", "Enable Herschel-Bulkley non-Newtonian viscosity for fluid {0}"), (r"fluid_pp\((\d+)\)%K", "HB consistency index for fluid {0}"), diff --git a/toolchain/mfc/params/generators/fortran_gen.py b/toolchain/mfc/params/generators/fortran_gen.py index dc218107e5..505d208a45 100644 --- a/toolchain/mfc/params/generators/fortran_gen.py +++ b/toolchain/mfc/params/generators/fortran_gen.py @@ -153,6 +153,10 @@ def generate_constants_fpp() -> str: lines = [_HEADER.rstrip()] for param in sorted(CONSTRAINTS): + # Compound keys (e.g. fluid_pp(1)%eos) cannot form valid Fortran identifiers; + # their enum constants are hand-written in m_constants.fpp instead. + if "%" in param or "(" in param: + continue names = CONSTRAINTS[param].get("names") if not names: continue