State-dependent equations of state: Mie-Gruneisen (linear and cubic Hugoniot), JWL and Vinet, with temperature, per-phase evaluation on the 5- and 6-equation models - #1811
Conversation
Any Mie-Gruneisen EOS p = p_ref + rho Gamma_G (e - e_ref) is MFC's rho e = Gamma p + Pi with Gamma = 1/Gamma_G and Pi = rho e_ref - p_ref/Gamma_G, so the existing operators need only the coefficients and dPi/drho. s_eos_coefficients is the single dispatch: the Mie-Gruneisen case supplies a linear-Hugoniot reference curve (u_s = c0 + s u_p, linear release) and one shared conversion produces Gamma, Pi and dPi/drho; a second family is one more case. Stiffened and ideal gas return the constants resolved at init, and no caller invokes the routine yet, so every existing answer is bit-identical - 27-case gate. Parameters mg_rho0, mg_c0, mg_s, mg_G0 follow the flat per-fluid style; the validator requires all four under mie_gruneisen, forbids them otherwise, and forbids qv there because e_ref carries the formation energy. Thirty pytest checks pin the maths against finite differences and a numerically integrated isentrope.
A Mie-Gruneisen fluid has neither gamma, pi_inf nor qv, and an ideal gas has no pi_inf: the validator now refuses them, and the suite's base fluid and every example drop the dead values. The ideal-gas half duplicates MFlowCode#1808 and drops out once it merges.
The mixture loop calls s_eos_coefficients per phase when any fluid's EOS is state dependent; the stiffened-gas arithmetic is untouched (27/27 bit-identical on the gate). The sound speed takes the partial densities as an optional argument and mixes frozen per-phase moduli, with the derivative term applied at that one site. Callers that cannot supply them are refused by the validator: 5-equation, HLL/HLLC/LF only, no bubbles, hypoelasticity, IBM, IGR, relativity, MHD, chemistry, acoustic source, probes, post-process c, characteristic BCs or Wood's law. Validation: symmetric-impact shock speed and plateau density match the Hugoniot to 1.4e-3, and a small pulse travels at the analytic sound speed to 1.9e-4 (the missing derivative term would give 24%).
gamma = gammas(1) and friends were assigned and never read; lit_gamma was assigned in both output routines and read in neither.
…eck passes from worktrees (cherry picked from commit 5ad5c85)
…ristic BCs, probes and post-process c
Isentropic release of a JWL fluid lands on the closed-form isentrope to 7.5e-5 at N = 200 and 9e-7 at N = 800; goldens for JWL beside an ideal gas, Mie-Gruneisen under Wood's law, and Mie-Gruneisen with characteristic walls. The acoustic pulse now comes from a rectangle patch and is tracked by its centroid: an analytic IC is compiled in and every distinct one costs the suite a full rebuild.
One reference-curve routine with a Gruneisen closure Gamma_G = Gamma_0 + a mu, one coefficient conversion, one sound-speed formula and one RK4 stepper for the phasic isentrope and the reference temperature. Adds the temperature T = T_ref + (e - e_ref)/c_v and its T_wrt output, the cubic Hugoniot, the Vinet cold curve, the 6-equation model on the phasic isentrope, and helpers that fetch their own coefficients so no feature reads gammas(i) directly: hypoelasticity, IBM, acoustic sources and the reactive burn open up. The validator refuses initial states outside a state-dependent EOS and keeps qv as the shared formation-energy zero.
Goldens for Vinet, the cubic Hugoniot, MG with hypoelasticity, with IBM, and a MG reactant burning to JWL products; the isentropic release now serves JWL and Vinet and the Hugoniot recovery a quadratic fit. The convergence harness runs a private copy of each case in a temporary directory without rebuilding, so specs that share a case file can run concurrently.
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Pull request overview
Completes integration of state-dependent equation-of-state (EOS) families across the solver and toolchain, including new per-phase coefficient paths, temperature output, and tooling/test updates to support the extended EOS selector set.
Changes:
- Add/select new EOS families (Mie–Grüneisen, JWL, Vinet) in parameters, validation, and toolchain mirror logic.
- Route simulation/post-process paths through per-phase coefficients (sound speed, internal energy, bulk modulus, isentropes, relaxation, burn temperature).
- Improve test tooling robustness (coverage-map git isolation) and update examples/docs for ideal-gas stiffness ownership and new
T_wrtoutput.
Reviewed changes
Copilot reviewed 181 out of 184 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| toolchain/mfc/test_case_validator.py | Adds selector/constraint tests for new state-dependent EOS families and T_wrt requirements |
| toolchain/mfc/test/test_coverage_unit.py | Updates unit tests to reuse git-env scrubbing helper from coverage module |
| toolchain/mfc/test/coverage.py | Ensures git subprocess calls scrub inherited GIT_* env to avoid hook-induced repo corruption |
| toolchain/mfc/test/case.py | Removes ideal-gas pi_inf override from test case defaults |
| toolchain/mfc/params_tests/test_eos_selector.py | Updates ideal-gas stiffness ownership expectations and error messaging assertions |
| toolchain/mfc/params/descriptions.py | Documents new T_wrt output parameter |
| toolchain/mfc/params/definitions.py | Registers T_wrt, expands EOS enum to include mie_gruneisen/jwl/vinet, and registers new EOS parameters |
| toolchain/mfc/eos.py | Adds Python mirror implementations for reference curves, coefficients, sound speed, RK4, and temperature |
| .github/scripts/check_coverage_map_health.py | Switches git calls to shared helper for consistent env scrubbing |
| docs/documentation/case.md | Documents T_wrt in case-file output options |
| src/common/m_constants.fpp | Adds EOS constants and RK4/ODE identifiers for state-dependent EOS support |
| src/common/m_derived_types.fpp | Extends physical_parameters with MG/JWL/Vinet curve and temperature reference fields |
| src/common/m_global_parameters_common.fpp | Adds per-fluid EOS selector and state-dependent curve parameter arrays and GPU declarations |
| src/pre_process/m_global_parameters.fpp | Initializes new state-dependent EOS parameter defaults in pre-process |
| src/simulation/m_global_parameters.fpp | Initializes new state-dependent EOS parameter defaults in simulation |
| src/post_process/m_global_parameters.fpp | Initializes new state-dependent EOS parameter defaults and introduces T_wrt default in post-process |
| src/simulation/m_sim_helpers.fpp | Extends s_compute_cell_state to output alpha_rho alongside alpha |
| src/simulation/m_time_steppers.fpp | Plumbs alpha_rho through CFL computation and sound-speed evaluation |
| src/simulation/m_start_up.fpp | Switches internal energy initialization to per-phase EOS lookup (f_phase_internal_energy(..., i)) |
| src/simulation/m_riemann_solver_hllc.fpp | Uses per-phase isentropes and per-phase internal energy; plumbs alpha_rho into sound speed paths |
| src/simulation/m_riemann_solver_hll.fpp | Plumbs alpha_rho into mixture sound-speed evaluation |
| src/simulation/m_riemann_solver_lf.fpp | Plumbs alpha_rho into mixture sound-speed evaluation |
| src/simulation/m_riemann_solver_hypo_hlld.fpp | Replaces stiffened-gas bulk-modulus usage with per-phase bulk modulus |
| src/simulation/m_rhs.fpp | Replaces stiffened-gas bulk modulus calls with per-phase bulk modulus in hypoelastic coupling |
| src/simulation/m_ibm.fpp | Switches IBM internal-energy recovery to per-phase EOS lookup |
| src/simulation/m_hypoelastic.fpp | Uses per-phase bulk modulus for hypoelastic K computation |
| src/simulation/m_pressure_relaxation.fpp | Extends relaxation Newton to use phasic isentrope density inversion for state-dependent EOS |
| src/simulation/m_reactive_burn.fpp | Uses per-phase temperature (f_phase_temperature) for Arrhenius branch |
| src/simulation/m_data_output.fpp | Threads alpha_rho through stability criteria and sound speed computations |
| src/simulation/m_cbc.fpp | Threads alpha_rho into sound-speed evaluation and mixture-coefficients time-derivative call |
| src/simulation/m_acoustic_src.fpp | Inlines frozen mixing of per-phase bulk moduli when any state-dependent EOS is present |
| src/post_process/m_start_up.fpp | Threads alpha_rho into sound speed post-processing and adds T_wrt field outputs |
| src/post_process/m_derived_variables.fpp | Removes dead s_derive_sound_speed code path from derived variables |
| src/post_process/m_data_output.fpp | Uses per-phase internal energy and threads alpha_rho into sound speed computation |
| tests/ED75D01D/golden-metadata.txt | Adds new golden metadata for updated EOS behavior |
| tests/A6846AD4/golden-metadata.txt | Adds new golden metadata for updated EOS behavior |
| tests/A421E318/golden-metadata.txt | Adds new golden metadata for updated EOS behavior |
| tests/7853BD45/golden-metadata.txt | Adds new golden metadata for updated EOS behavior |
| tests/6AE3FB4E/golden-metadata.txt | Adds new golden metadata for updated EOS behavior |
| tests/61AF4509/golden-metadata.txt | Adds new golden metadata for updated EOS behavior |
| tests/6191C8D0/golden-metadata.txt | Adds new golden metadata for updated EOS behavior |
| tests/590E4427/golden-metadata.txt | Adds new golden metadata for updated EOS behavior |
| tests/4A0CDF9C/golden-metadata.txt | Adds new golden metadata for updated EOS behavior |
| tests/471270BB/golden-metadata.txt | Adds new golden metadata for updated EOS behavior |
| tests/120043F6/golden-metadata.txt | Adds new golden metadata for updated EOS behavior |
| tests/5AC2F65D/golden-metadata.txt | Adds new golden metadata for updated EOS behavior |
| examples/1D_mg_impact/case.py | Adds MG symmetric impact validation case generator |
| examples/1D_mg_acoustic/case.py | Adds MG acoustic pulse validation case generator |
| examples/1D_isentropic_release/case.py | Adds isentropic release validation case generator (JWL/Vinet) |
| examples/3D_turb_mixing/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_sphbubcollapse/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_rotating_sphere/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_recovering_sphere/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_rayleigh_taylor_muscl/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_rayleigh_taylor/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_performance_test/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_patch_spherical_harmonic/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_moving_lag_particles/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_mibm_sphere_head_on_collision/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_mibm_periodic_collision/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_lagrange_shbubcollapse/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_lagrange_bubblescreen/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_icpp_stl_cube/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_ibm_stl_test/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_ibm_stl_pyramid/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_ibm_stl_ellipsoid/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_ibm_bowshock/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_brio_wu/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_advection_convergence/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_TaylorGreenVortex_analytical/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_TaylorGreenVortex/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_IGR_jet_1fluid/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_IGR_jet/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/3D_IGR_TaylorGreenVortex_nvidia/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_IGR_TaylorGreenVortex/case.py | Removes ideal-gas pi_inf setting |
| examples/3D_IGR_33jet/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_zero_circ_vortex_analytical/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_zero_circ_vortex/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_viscous_shock_tube/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_tumbling_rectangle/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_triple_point/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_synthetic_turbulence/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_shockdroplet_muscl/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_shockdroplet/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_shockbubble/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_shock_cloud_rmhd/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_riemann_test_muscl/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_riemann_test/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_richtmyer_meshkov/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_rayleigh_taylor/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_poiseuille_thickening_nn/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_poiseuille_nn/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_patch_modal_shape_exp/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_patch_modal_shape/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_orszag_tang_hyper_cleaning/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_orszag_tang/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_moving_lag_bubs/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_mibm_shock_cylinder/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_mibm_particle_cloud/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_mibm_cylinder_in_cross_flow/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_mhd_rotor/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_mhd_magnetic_vortex/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_lid_driven_cavity_nn/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_lid_driven_cavity/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_laplace_pressure_jump/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_lagrange_rising_bubble/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_lagrange_in_crossflow/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_lagrange_bubblescreen/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_kelvin_helmholtz/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_jet/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_isentropicvortex_analytical/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_isentropicvortex/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_icpp_stl_circle/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_ibm_viscous_drag_over_cylinder/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_ibm_stl_wedge/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_ibm_stl_test/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_ibm_stl_MFCCharacter/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_ibm_poiseuille_nn/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_ibm_multiphase/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_ibm_ellipse/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_ibm_cfl_dt/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_ibm_airfoil/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_ibm/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_hypo_shear_contact/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_herschel_bulkley_poiseuille_nn/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_hardcoded_ic/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_forward_facing_step/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_bingham_poiseuille_nn/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_backward_facing_step/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_axisym_shockbubble/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_advection_muscl/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_advection_convergence/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_advection/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_acoustic_pulse_analytical/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_acoustic_pulse/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_acoustic_broadband/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_TaylorGreenVortex/case.py | Removes ideal-gas pi_inf setting |
| examples/2D_IGR_triple_point/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_IGR_2fluid/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/2D_GreshoVortex/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_vacuum_restart/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_vacuum/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_titarevtorro_analytical/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_titarevtorro/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_sodshocktube_muscl/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_sodshocktube/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_sod_convergence/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_shuosher_wenoz5/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_shuosher_wenom5/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_shuosher_wenojs5/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_shuosher_teno7/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_shuosher_teno5/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_shuosher_old/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_shuosher_analytical/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_mhd_smooth_alfven_wave/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_laxshocktube/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_kapilashocktube/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_hypo_2materials/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_euler_convergence/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_dai_woodward_hlld/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_dai_woodward/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_convergence/case.py | Removes ideal-gas pi_inf setting (both fluids) |
| examples/1D_brio_wu_rmhd/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_brio_wu_hlld/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_brio_wu/case.py | Removes ideal-gas pi_inf setting |
| examples/1D_advection_convergence/case.py | Removes ideal-gas pi_inf setting (both fluids) |
Suppressed comments (3)
toolchain/mfc/test_case_validator.py:1
- The
unittest.main()call now appears before newly-added test classes, so running this file directly won’t discover/execute the new tests. Move theif __name__ == "__main__": unittest.main()block to the end of the file (after all test class definitions).
src/simulation/m_pressure_relaxation.fpp:1 - This skips the stiffened-gas pressure lower bound for all fluids whenever any fluid is state-dependent. In mixed-EOS runs (some stiffened gas, some state-dependent), stiffened-gas phases can end up with invalid initial pressures and break the Saurel isentrope inversion. Apply the bound per fluid (e.g., skip only when that fluid’s EOS is state-dependent, using
f_is_state_dependent(eoss(i))) rather than gating on the globalany_state_dependent_eos.
toolchain/mfc/test_case_validator.py:1 - The docstring refers to
fluid_pp(i)%eos = 'mie_gruneisen', but the tests setfluid_pp(1)%eosusing the integer selector (3). To keep the tests self-describing, either adjust the docstring to mention the numeric selector (and/or the mapping), or use the string form consistently in the new test cases.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #1811 +/- ##
==========================================
+ Coverage 62.26% 62.70% +0.44%
==========================================
Files 84 84
Lines 21558 21847 +289
Branches 3188 3206 +18
==========================================
+ Hits 13423 13700 +277
- Misses 5937 5938 +1
- Partials 2198 2209 +11 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…NaN-safe harness, coverage for the temperature, IBM and cubic paths; EOS helpers as subroutines
Nothing marked an outage any more once the PyPI classifier went, so the two remaining check call sites could only ever act on a stale marker -- which is exactly what happened: a marker written before that fix landed kept failing the CCE cpu lane afterwards. The design decision behind removing it rather than repairing it: the breaker converts a local failure into a global one, and every recorded instance of it firing was a false positive caused by something else (a syscheck install timeout, a compile error, and #1813's retry deleting build/venv so attempt 2 could not reach PyPI). It reddened #1805, #1807 and #1811 for twenty minutes at a time. Meanwhile the cost it was protecting against is small: Frontier fetches dependencies on the login node, before any allocation is committed. Removes ci-outage.sh, both check call sites, the exit-78 relay through monitor_slurm_job.sh and run_monitored_slurm_job.sh, and the tests that pinned all of it. The node-fault path (77) is untouched and still covered -- 28 tests across preflight, monitor and requeue still pass. Net -319 lines.
…a device-array element passed by reference
…routine called with an array element
Lines of Code
|
|
@sbryngelson Can help with any part of this code? I would love to. |
Supersedes #1805, #1809 and #1810 (closed): the state-dependent equation-of-state family of #1638 in one PR. The diff against master is the complete change.
The mapping. Any Mie-Grüneisen equation of state
p = p_ref(ρ) + ρΓ_G(e − e_ref(ρ))is MFC's formρe = Γp + ΠwithΓ = 1/Γ_GandΠ = ρe_ref − p_ref/Γ_G, so the centralized operators of #1762 need only those two coefficients plusΠ′ = dΠ/dρandΓ′for the sound speed,c² = [((Γ+1)p + Π)/ρ − Π′ − pΓ′]/Γ. Stiffened gas is the degenerate member (p_ref = −γπ_∞,e_ref = 0,Γ_G = γ − 1) and, with ideal gas, keeps the constants resolved at init, bit for bit.Structure.
s_reference_curveis the only place a reference curve lives: each family is onecasereturningp_ref, e_refand their density derivatives, and the closureΓ_G = Γ₀ + aμis one line after theselect.s_eos_coefficientsconverts any curve to(Γ, Π, Π′, Γ′);s_phase_coefficientsis the only place that chooses per-cell evaluation over the init-time constants;f_c2_from_coefficientsis the only sound-speed formula;s_rk4is the only integrator, serving the phasic isentrope and the reference temperature.s_phase_internal_energyands_phase_bulk_modulusfetch their own coefficients, so no feature readsgammas(i)directly any more (the fenced QBMM excepted). The same maths lives once more intoolchain/mfc/eos.py, shared by the validator, the unit tests and the validation runners.Families and parameters (
fluid_pp(i)%eos, each family owning its parameters and the validator refusing the rest):mie_gruneisen: Hugoniotu_s = c₀ + s u_p + s₂u_p² + s₃u_p³(mg_rho0, mg_c0, mg_s, optionalmg_s2, mg_s3), linear on release; the cubic fit solvesu_p(μ)by Newton with implicit-differentiation derivatives, the linear fit stays closed form. A cubic fit bounds the compression it can describe; the mirror refuses a state past that bound and so does the validator.jwl:p_ref = A e^{−R₁V} + B e^{−R₂V}, an isentrope reference (jwl_a, jwl_b, jwl_r1, jwl_r2, jwl_omega, jwl_rho0).vinet: the cold curvep_c = 3K₀(1 − x)/x² exp(η(1 − x)),x = (ρ₀/ρ)^{1/3}(vinet_k0, vinet_k0p, vinet_rho0, vinet_gruneisen).Γ_G(ρ)viamg_gruneisen_a/vinet_gruneisen_a; reference temperaturemg_t0/jwl_t0/vinet_t0.gammanorpi_inf;qvstays the shared formation-energy zero (an MG reactant burning to JWL products). An ideal gas has nopi_inf.Where it is evaluated. Per phase at the phase's own density in the mixture coefficients and their time derivative, the frozen mixture sound speed (volume-weighted mean of phasic moduli, Wood's law as an N-fluid loop), the characteristic-BC coefficient derivative, probes and post-process
c; hypoelasticity, immersed boundaries, acoustic sources and the reactive burn throughs_phase_bulk_modulus/s_phase_internal_energy/s_phase_temperature. The 6-equation model: the HLLC star pressure and the relaxation Newton use the phasic isentrope (closed form for stiffened gas, RK4 otherwise; density at pressure by Newton on the same integrator), with the star coefficients read at the star densityξ_K αρ. TemperatureT = T_ref(ρ) + (e − e_ref)/c_v,T_reffrom the Maxwell relationdT/dV = (e_ref′ + p_ref)/c_v − Γ_G T/V(closed form for an isentrope reference), written byT_wrtand used by the burn's Arrhenius rate. Still refused: bubbles, IGR (its flux carries no phasic volume fractions), relativity, MHD, chemistry (species EOS), phase change,wave_speeds = 2(the PVRS estimate is stiffened-gas only), the two hard-coded ICs that readfluid_pp(1)%gamma. The validator also refuses a patch that starts a state-dependent fluid outside its equation of state (ρe ≤ 0orc² ≤ 0, or a singular density) and a temperature withoutc_v > 0and*_t0 > 0.Behaviour change worth knowing. With any state-dependent fluid present, the 5-equation mixture sound speed is the frozen volume-weighted mean of phasic moduli rather than
K(Γ_mix, Π_mix)/ρ; the two agree for stiffened gas alone. A case without a state-dependent fluid is unaffected, bit for bit.Verification.
model_eqns = 315/15, Reactive Burn 5/5, each including its new state-dependent golden.μ = 0.1), MG under Wood's law, MG with characteristic walls and a probe, MG with hypoelasticity, MG on the 6-equation IBM (1 and 2 fluids), MG reactant → JWL products under the Arrhenius rate.s₂ = 0.3, s₃ = 0.05) over four closing speeds (bound 5e-3); an acoustic pulse at the analyticcwitha = 0.5, 5.0e-4 (bound 1e-3); isentropic release on the closed-form isentrope through the initial state, JWL 7.5e-5 at N = 200 → 9e-7 at N = 800 (bound 1e-3), Vinet fan 1e-5 with a uniform 3.7e-3 → 8.6e-4 star-plateau offset (the scheme's star state, no sonic point; bound 5e-3).c²against a numerical isentrope witha ≠ 0, RK4 against the closed-form JWL isentrope (1e-8 over ±25%), the Maxwell relation for the Hugoniot temperature, Vinet pinned toK₀andK₀′, the cubic fit refusing states past its maximum compression. 144 unit tests; all 758 test definitions validate.Review round. Four independent reviews of the diff (correctness, silent failures, test coverage, Cray OpenACC) led to: star-state coefficients at the star density, star temporaries private on the loop that writes them, the hypoelastic HLLD solver passing
alpha_rhoto the sound speed, the relaxation floor gated per fluid, the 6-equationprim → consconversion using the phase helper (pre_process wrote stiffened-gas constants for a state-dependent fluid before), a vanishing phase never evaluating its curve atρ = 0, a defined reference state for every fluid, a harness that fails on a NaN error instead of dropping it inmax(), and the validator rules above.Cray OpenACC. The first CI runs failed 52 tests with NaNs on Frontier CCE OpenACC while every other backend was bit-identical. Probes inside MFC showed every helper returning the host value when called with scalars, single-lane and over 100k lanes, and an in-situ check showed the real kernel's output never landing (device 0.0, host 1.4, same fields). A standalone bisection (#1815) pinned the trigger: a device routine that contains an
acc loop seq(the Newton and RK4 loops this PR adds) called with an array element as an actual argument, on CCE 19 through 21.0.2 at-O2; either ingredient alone is fine, and the parent PR had no loops. Every helper call now passes scalars and receives into scalars; the rule is in.claude/rules/common-pitfalls.md.Also fixed in the convergence harness: each run executes a private copy of its case file in a temporary directory; specs sharing one case file used to clobber each other's
pre_processoutput when run concurrently.