From 172bfa7a90aef086c3739de0920ca90371e167af Mon Sep 17 00:00:00 2001 From: Fahad Nabid <55717325+fahnab666@users.noreply.github.com> Date: Fri, 31 Jul 2026 10:34:18 -0400 Subject: [PATCH 1/6] Add per-fluid EOS selector with a stable EOS enumeration Introduce fluid_pp(:)%eos, a stable five-value enumeration (stiffened_gas, ideal_gas_mixture, mie_gruneisen, jwl, table) in place of 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 every existing case is unchanged and all goldens stay bit-identical, and no runtime dispatch enters the hot per-cell path. Case files accept readable names while the integer representation stays internal. The Fortran and Python checkers 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 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 | 17 ++++++++++ src/common/m_constants.fpp | 7 ++++ 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 | 9 ++++++ toolchain/mfc/params/descriptions.py | 1 + .../mfc/params/generators/fortran_gen.py | 3 ++ 12 files changed, 76 insertions(+), 1 deletion(-) diff --git a/docs/documentation/case.md b/docs/documentation/case.md index ec82b5bbd8..f17e759405 100644 --- a/docs/documentation/case.md +++ b/docs/documentation/case.md @@ -463,6 +463,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 f7b7b42d74..53055abbaa 100644 --- a/src/common/m_checker_common.fpp +++ b/src/common/m_checker_common.fpp @@ -26,12 +26,29 @@ contains integer(kind=8), intent(in) :: n_global if (check_total_cells) call s_check_total_cells(n_global) + call s_check_eos #:if USING_AMD call s_check_amd #:endif end subroutine s_check_inputs_common + !> Reject unimplemented EOS selectors and intra-cell mixing; only stiffened_gas (non-chemistry) and ideal_gas_mixture + !! (chemistry) have a backend, and every fluid in a run must share one family. + 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 + !> 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(n_global) diff --git a/src/common/m_constants.fpp b/src/common/m_constants.fpp index 4857d93249..ae38c5e01c 100644 --- a/src/common/m_constants.fpp +++ b/src/common/m_constants.fpp @@ -114,6 +114,13 @@ module m_constants integer, parameter :: BC_NO_SLIP_WALL = -16 integer, parameter :: BC_DIRICHLET = -17 + ! fluid_pp(:)%eos selector; values must match _EOS_NAMES in toolchain/mfc/params/definitions.py + 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 872c04b390..1ebb138c99 100644 --- a/src/common/m_derived_types.fpp +++ b/src/common/m_derived_types.fpp @@ -382,6 +382,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 76184fe13c..996cb7c848 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 1423c62feb..233ef73b83 100644 --- a/src/post_process/m_global_parameters.fpp +++ b/src/post_process/m_global_parameters.fpp @@ -207,6 +207,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 6a76fc073a..8996c387d8 100644 --- a/src/pre_process/m_global_parameters.fpp +++ b/src/pre_process/m_global_parameters.fpp @@ -391,6 +391,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 7108ad17dc..f002b6ac5d 100644 --- a/src/simulation/m_global_parameters.fpp +++ b/src/simulation/m_global_parameters.fpp @@ -427,6 +427,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 03d6421b5d..9e4bf8edc9 100644 --- a/toolchain/mfc/case_validator.py +++ b/toolchain/mfc/case_validator.py @@ -41,6 +41,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", @@ -747,6 +757,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" @@ -2309,6 +2340,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 fd32e8c3ae..4c2361bb4d 100644 --- a/toolchain/mfc/params/definitions.py +++ b/toolchain/mfc/params/definitions.py @@ -112,6 +112,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,13 @@ def get_value_label(param_name: str, value: int) -> str: "p": {"min": 0}, } +# Values must match the hand-written eos_* constants in src/common/m_constants.fpp; +# generate_constants_fpp skips compound keys, so these entries only drive 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": { @@ -887,6 +895,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, {"hypoelasticity"}, 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 bd09bb4f65..917d49caa9 100644 --- a/toolchain/mfc/params/descriptions.py +++ b/toolchain/mfc/params/descriptions.py @@ -345,6 +345,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 8209334e2b..f775bda887 100644 --- a/toolchain/mfc/params/generators/fortran_gen.py +++ b/toolchain/mfc/params/generators/fortran_gen.py @@ -258,6 +258,9 @@ def generate_constants_fpp() -> str: lines = [_HEADER.rstrip()] for param in sorted(CONSTRAINTS): + # Compound keys (e.g. fluid_pp(1)%eos) are not valid Fortran identifiers; hand-written in m_constants.fpp + if "%" in param or "(" in param: + continue names = CONSTRAINTS[param].get("names") if not names: continue From 7ee3cd3d47ab98dbe4d7e8b04eab95a45812b2de Mon Sep 17 00:00:00 2001 From: Fahad Nabid <55717325+fahnab666@users.noreply.github.com> Date: Fri, 31 Jul 2026 10:41:42 -0400 Subject: [PATCH 2/6] Document the non-chemistry ideal-gas route in the eos selector note State plainly that a plain ideal gas without chemistry is stiffened_gas with pi_inf = 0, and that ideal_gas_mixture is the Pyrometheus mixture backend valid only in a chemistry build. This keeps users from reaching for ideal_gas_mixture on a non-chemistry build, where it is rejected. --- docs/documentation/case.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/documentation/case.md b/docs/documentation/case.md index f17e759405..5259d1091a 100644 --- a/docs/documentation/case.md +++ b/docs/documentation/case.md @@ -463,7 +463,7 @@ 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)%%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. For a non-chemistry ideal gas, use `stiffened_gas` with `pi_inf = 0`; `ideal_gas_mixture` is the Pyrometheus mixture backend and is only valid in a chemistry build. - `fluid_pp(i)%%Re(1)` and `fluid_pp(i)%%Re(2)` define the shear and volume viscosities of $i$-th fluid, respectively. From ff17a03953d32eac2d3db499c580d4cfce777469 Mon Sep 17 00:00:00 2001 From: Fahad Nabid <55717325+fahnab666@users.noreply.github.com> Date: Sat, 1 Aug 2026 22:43:20 -0400 Subject: [PATCH 3/6] Address review: document the eos enum manual exception, derive rather than hand-sync the eos integer values, and cover the bubbles_euler slot Records the fluid_pp(:)%eos hand-written constants as a "still manual" case in common-pitfalls.md and notes it in the m_constants.fpp comment, since generate_constants_fpp silently skips compound registry keys. check_eos in case_validator.py now reads its two supported values from CONSTRAINTS["fluid_pp(1)%eos"]["names"], the same idiom already used for recon_type, instead of a third hand-synced copy of 1 and 2. check_eos (Python) and s_check_eos (Fortran) both now cover the extra fluid property slot that bubbles_euler uses, matching the existing check_stiffened_eos pattern; previously fluid_pp(num_fluids+1)%eos went unvalidated when bubbles_euler was set. Adds eos to the physical_parameters member list comment in definitions.py, the registry/type drift guard from #1553. --- .claude/rules/common-pitfalls.md | 5 ++++- src/common/m_checker_common.fpp | 2 +- src/common/m_constants.fpp | 9 ++++++--- toolchain/mfc/case_validator.py | 9 +++++++-- toolchain/mfc/params/definitions.py | 2 +- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/.claude/rules/common-pitfalls.md b/.claude/rules/common-pitfalls.md index 71bf6e452f..a8e4e5961b 100644 --- a/.claude/rules/common-pitfalls.md +++ b/.claude/rules/common-pitfalls.md @@ -48,7 +48,10 @@ covered in `docs/documentation/contributing.md`. `CASE_OPT_EXTRA_LINES` literal in `toolchain/mfc/params/generators/fortran_gen.py` (covers `num_dims`, `num_vels`, `weno_polyn`, `muscl_polyn`, `weno_num_stencils`, `wenojs`); multi-variable declaration lines (`bc_x/y/z`, `x/y/z_domain`, `x/y/z_output`, post's - `G`); and the MPI broadcast residue in `m_mpi_proxy` (computed variables that are not + `G`); enum constants for compound registry keys (e.g. `fluid_pp(:)%eos`), hand-written in + `m_constants.fpp` because `{param}_{name}` is not a valid Fortran identifier for a + `fluid_pp(i)%...` key — `generate_constants_fpp` skips any key containing `%` or `(`; and the + MPI broadcast residue in `m_mpi_proxy` (computed variables that are not namelist-bound: `m_glb`/`n_glb`/`p_glb`, `cfl_dt`, `bc_io`, and complex struct-member array loops — these cannot be auto-generated and stay hand-listed). Everything else — scalar declarations, plain arrays (`FORTRAN_ARRAY_DIMS` table in `definitions.py`), derived-type namelist declarations including `GPU_DECLARE` diff --git a/src/common/m_checker_common.fpp b/src/common/m_checker_common.fpp index 53055abbaa..d1641e6ee4 100644 --- a/src/common/m_checker_common.fpp +++ b/src/common/m_checker_common.fpp @@ -39,7 +39,7 @@ contains integer :: i - do i = 1, num_fluids + do i = 1, num_fluids + merge(1, 0, bubbles_euler) @: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, & diff --git a/src/common/m_constants.fpp b/src/common/m_constants.fpp index ae38c5e01c..46bdc282de 100644 --- a/src/common/m_constants.fpp +++ b/src/common/m_constants.fpp @@ -114,7 +114,9 @@ module m_constants integer, parameter :: BC_NO_SLIP_WALL = -16 integer, parameter :: BC_DIRICHLET = -17 - ! fluid_pp(:)%eos selector; values must match _EOS_NAMES in toolchain/mfc/params/definitions.py + ! fluid_pp(:)%eos selector; values must match _EOS_NAMES in toolchain/mfc/params/definitions.py. + ! Hand-written, not part of the auto-generated block below: fluid_pp(1)%eos is a compound + ! registry key, and generate_constants_fpp skips those (see common-pitfalls.md). integer, parameter :: eos_stiffened_gas = 1 integer, parameter :: eos_ideal_gas_mixture = 2 integer, parameter :: eos_mie_gruneisen = 3 @@ -125,7 +127,8 @@ module m_constants 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 - ! Named values for enumerated case parameters (e.g. riemann_solver_hllc). - ! AUTO-GENERATED from "names" in toolchain/mfc/params/definitions.py. + ! Named values for enumerated case parameters (e.g. riemann_solver_hllc). AUTO-GENERATED + ! from "names" in toolchain/mfc/params/definitions.py; compound registry keys are skipped + ! (their constants are hand-written above instead, e.g. the eos_* block). #:include 'generated_constants.fpp' end module m_constants diff --git a/toolchain/mfc/case_validator.py b/toolchain/mfc/case_validator.py index 9e4bf8edc9..4ab9b9a6ec 100644 --- a/toolchain/mfc/case_validator.py +++ b/toolchain/mfc/case_validator.py @@ -761,13 +761,18 @@ 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" + bubbles_euler = self.get("bubbles_euler", "F") == "T" if num_fluids is None: return - eos_stiffened_gas, eos_ideal_gas_mixture = 1, 2 + eos_names = CONSTRAINTS["fluid_pp(1)%eos"]["names"] + eos_stiffened_gas, eos_ideal_gas_mixture = eos_names["stiffened_gas"], eos_names["ideal_gas_mixture"] - for i in range(1, num_fluids + 1): + # Allow one extra fluid property slot when using bubbles_euler + bub_fac = 1 if (bubbles_euler) else 0 + + for i in range(1, num_fluids + 1 + bub_fac): eos = self.get(f"fluid_pp({i})%eos") if eos is None: continue diff --git a/toolchain/mfc/params/definitions.py b/toolchain/mfc/params/definitions.py index 4c2361bb4d..1167311398 100644 --- a/toolchain/mfc/params/definitions.py +++ b/toolchain/mfc/params/definitions.py @@ -888,7 +888,7 @@ def _load(): _r(f"{px}sph_har_coeff({ll},{mm})", REAL) # fluid_pp (10 fluids) - # Members present in physical_parameters: gamma, pi_inf, Re, cv, qv, qvp, G. + # Members present in physical_parameters: gamma, pi_inf, Re, cv, qv, qvp, eos, G. # mul0/ss/pv/gamma_v/M_v/mu_v/k_v/cp_v/D_v were removed from the Fortran type # by upstream #1085/#1093 — they must NOT be registered (namelist read would crash). for f in range(1, NF + 1): From 0e8230f9b780b48b6274427bd1cb278eb5d77e7a Mon Sep 17 00:00:00 2001 From: Fahad Nabid <55717325+fahnab666@users.noreply.github.com> Date: Sat, 1 Aug 2026 23:30:12 -0400 Subject: [PATCH 4/6] Place eos_* constants above the auto-gen include and flag the silent skip --- .claude/rules/common-pitfalls.md | 8 +++++--- src/common/m_constants.fpp | 22 +++++++++++----------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/.claude/rules/common-pitfalls.md b/.claude/rules/common-pitfalls.md index a8e4e5961b..292e3e5ee1 100644 --- a/.claude/rules/common-pitfalls.md +++ b/.claude/rules/common-pitfalls.md @@ -48,9 +48,11 @@ covered in `docs/documentation/contributing.md`. `CASE_OPT_EXTRA_LINES` literal in `toolchain/mfc/params/generators/fortran_gen.py` (covers `num_dims`, `num_vels`, `weno_polyn`, `muscl_polyn`, `weno_num_stencils`, `wenojs`); multi-variable declaration lines (`bc_x/y/z`, `x/y/z_domain`, `x/y/z_output`, post's - `G`); enum constants for compound registry keys (e.g. `fluid_pp(:)%eos`), hand-written in - `m_constants.fpp` because `{param}_{name}` is not a valid Fortran identifier for a - `fluid_pp(i)%...` key — `generate_constants_fpp` skips any key containing `%` or `(`; and the + `G`); enum constants for compound registry keys (a `CONSTRAINTS` key with a `names` dict whose + key contains `%` or `(`, e.g. `fluid_pp(:)%eos`): `generate_constants_fpp` **silently** skips + these (the `{param}_{name}` form, `fluid_pp(1)%eos_stiffened_gas`, is not a valid Fortran + identifier), so their constants must be hand-written in `m_constants.fpp` (as `eos_*` is) or + they simply never exist; and the MPI broadcast residue in `m_mpi_proxy` (computed variables that are not namelist-bound: `m_glb`/`n_glb`/`p_glb`, `cfl_dt`, `bc_io`, and complex struct-member array loops — these cannot be auto-generated and stay hand-listed). Everything else — scalar declarations, plain arrays (`FORTRAN_ARRAY_DIMS` diff --git a/src/common/m_constants.fpp b/src/common/m_constants.fpp index 46bdc282de..d248e310d8 100644 --- a/src/common/m_constants.fpp +++ b/src/common/m_constants.fpp @@ -114,21 +114,21 @@ module m_constants integer, parameter :: BC_NO_SLIP_WALL = -16 integer, parameter :: BC_DIRICHLET = -17 - ! fluid_pp(:)%eos selector; values must match _EOS_NAMES in toolchain/mfc/params/definitions.py. - ! Hand-written, not part of the auto-generated block below: fluid_pp(1)%eos is a compound - ! registry key, and generate_constants_fpp skips those (see common-pitfalls.md). + ! 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 + + ! Named values for enumerated case parameters (e.g. riemann_solver_hllc) are AUTO-GENERATED + ! from "names" in toolchain/mfc/params/definitions.py by the include below. Exception: + ! generate_constants_fpp silently skips any registry key containing "%" or "(", because the + ! generated {param}_{name} form (e.g. fluid_pp(1)%eos_stiffened_gas) is not a valid Fortran + ! identifier. Enum constants for such compound keys are therefore hand-written here, directly + ! above the include, and must stay in sync with their "names" dict (see common-pitfalls.md). + ! Current case: the eos_* selector, whose values must match _EOS_NAMES in definitions.py. 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 - - ! Named values for enumerated case parameters (e.g. riemann_solver_hllc). AUTO-GENERATED - ! from "names" in toolchain/mfc/params/definitions.py; compound registry keys are skipped - ! (their constants are hand-written above instead, e.g. the eos_* block). #:include 'generated_constants.fpp' end module m_constants From 1a7222f6546c02abd6951912ba6f1602e51495ab Mon Sep 17 00:00:00 2001 From: Fahad Nabid <55717325+fahnab666@users.noreply.github.com> Date: Sun, 2 Aug 2026 00:09:43 -0400 Subject: [PATCH 5/6] Tighten the eos auto-gen note and complete the physical_parameters drift-guard list --- src/common/m_constants.fpp | 10 +++------- toolchain/mfc/params/definitions.py | 3 ++- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/common/m_constants.fpp b/src/common/m_constants.fpp index d248e310d8..927107ace4 100644 --- a/src/common/m_constants.fpp +++ b/src/common/m_constants.fpp @@ -118,13 +118,9 @@ module m_constants 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 - ! Named values for enumerated case parameters (e.g. riemann_solver_hllc) are AUTO-GENERATED - ! from "names" in toolchain/mfc/params/definitions.py by the include below. Exception: - ! generate_constants_fpp silently skips any registry key containing "%" or "(", because the - ! generated {param}_{name} form (e.g. fluid_pp(1)%eos_stiffened_gas) is not a valid Fortran - ! identifier. Enum constants for such compound keys are therefore hand-written here, directly - ! above the include, and must stay in sync with their "names" dict (see common-pitfalls.md). - ! Current case: the eos_* selector, whose values must match _EOS_NAMES in definitions.py. + ! Enum values are auto-generated from "names" in definitions.py by the include below, except + ! compound keys ("%" or "("), which generate_constants_fpp silently skips. So eos_* is + ! hand-written here and must match _EOS_NAMES in definitions.py (see common-pitfalls.md). integer, parameter :: eos_stiffened_gas = 1 integer, parameter :: eos_ideal_gas_mixture = 2 integer, parameter :: eos_mie_gruneisen = 3 diff --git a/toolchain/mfc/params/definitions.py b/toolchain/mfc/params/definitions.py index 1167311398..7be9cf05bc 100644 --- a/toolchain/mfc/params/definitions.py +++ b/toolchain/mfc/params/definitions.py @@ -888,7 +888,8 @@ def _load(): _r(f"{px}sph_har_coeff({ll},{mm})", REAL) # fluid_pp (10 fluids) - # Members present in physical_parameters: gamma, pi_inf, Re, cv, qv, qvp, eos, G. + # Members present in physical_parameters: gamma, pi_inf, Re, cv, qv, qvp, eos, G, + # non_newtonian, K, nn, tau0, hb_m, mu_min, mu_max, mu_bulk. # mul0/ss/pv/gamma_v/M_v/mu_v/k_v/cp_v/D_v were removed from the Fortran type # by upstream #1085/#1093 — they must NOT be registered (namelist read would crash). for f in range(1, NF + 1): From d401bba469f4707b9515370bba082f4c4d528c6e Mon Sep 17 00:00:00 2001 From: Fahad Nabid <55717325+fahnab666@users.noreply.github.com> Date: Mon, 3 Aug 2026 17:30:09 -0400 Subject: [PATCH 6/6] Clamp s_check_eos fluid loop to num_fluids_max to avoid out-of-bounds access --- src/common/m_checker_common.fpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/m_checker_common.fpp b/src/common/m_checker_common.fpp index d1641e6ee4..76f2387761 100644 --- a/src/common/m_checker_common.fpp +++ b/src/common/m_checker_common.fpp @@ -39,7 +39,7 @@ contains integer :: i - do i = 1, num_fluids + merge(1, 0, bubbles_euler) + do i = 1, min(num_fluids + merge(1, 0, bubbles_euler), num_fluids_max) @: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, &