Repository navigation
Add JWL equation of state and detonation modeling for the five-equation model #1638
Description
Summary
The goal is Jones-Wilkins-Lee (JWL) detonation products in the five-equation Allaire model (model_eqns = 2), selectable per fluid (fluid_pp(i)%eos) and coexisting with an ideal-gas or stiffened-gas ambient, so MFC can simulate explosive products expanding into air or water.
A monolithic implementation was closed in favor of an incremental equation-of-state architecture. This issue tracks that incremental path: a central thermodynamics interface first, then explicit EOS selection, then a generic Mie-Gruneisen backend, and only then JWL as a particular Mie-Gruneisen reference curve with user-supplied parameters.
The two goals of this path are:
- MFC gets multi-EOS support in a quick and easy way that is ensured correct and extensible.
- MFC carries no lingering export-control risk, because no calibrated explosive catalog ever enters the repository.
Current PR
- Add a per-fluid equation-of-state selector #1700 adds
fluid_pp(i)%eos, a stable enumeration for a fluid's equation of state, in place of implicit backend selection. Only the two already-supported backends are accepted (stiffened_gasdefault,ideal_gas_mixturefor chemistry builds via Pyrometheus);mie_gruneisen,jwl, andtableare reserved in the enum and rejected until their backends exist. The default resolves from the compile-timechemistryflag, so existing cases do not change and the golden suite stays bit-identical.
Plan
The right route is incremental EOS support first, while keeping thermodynamics, reaction kinetics, and material data separate. Pyrometheus remains the gas-chemistry backend; it is not forced to represent every phenomenological explosive burn law.
MFC currently has stiffened-gas thermodynamics for ordinary fluids and a Pyrometheus-generated ideal-gas-mixture path when chemistry = T. What it lacks is an explicit, coherent EOS/backend abstraction.
PR 1: Establish the thermodynamics interface. No new physics, no user-facing EOS choice. Centralize the operations currently scattered through conversion routines, Riemann solvers, post-processing, and coupling code: pressure(state), internal_energy(rho, pressure, composition), temperature(state), sound_speed(state), thermodynamic_derivatives(state), validate_state(state). Implement two adapters, stiffened_gas_thermo and pyrometheus_thermo, the latter wrapping the already-generated m_thermochem and selected automatically when chemistry is enabled. Results stay bit-identical. The goal is to prove all solver paths obtain thermodynamics through one interface.
PR 2: Introduce explicit EOS selection. Add a stable enumeration rather than scattered booleans: EOS_STIFFENED_GAS, EOS_IDEAL_GAS_MIXTURE, EOS_MIE_GRUNEISEN, EOS_JWL, EOS_TABLE. Initially expose only the already-supported choices (for example fluid_pp(1)%eos: "stiffened_gas"); when chemistry = T the reacting gas uses ideal_gas_mixture backed by Pyrometheus. Preserve stiffened gas as the default, reject unsupported combinations explicitly, do not promise arbitrary combinations of EOS families within one cell, and keep the integer representation internal while preferring readable names in case files.
PR 3: Add generic Mie-Gruneisen support. A cleaner second non-chemistry EOS than jumping straight to an explosives-branded path. Provide reference pressure p_ref(rho), reference energy e_ref(rho), the Gruneisen coefficient, pressure/energy inversion, analytic sound speed and derivatives, and manufactured tests. JWL then becomes a particular Mie-Gruneisen reference curve rather than an unrelated special path.
PR 4: Add nonreactive JWL. Add only the JWL reference curve, its derivatives and inversion, user-supplied parameters, and generic products-expansion and shock-tube cases. No named explosive catalog, no reaction source. The repository holds the equation family, not a library of military-relevant materials.
PR 5: Create the external material-file mechanism. Follow the existing Cantera-file model: a material file naming an eos family and its parameters plus a provenance block (citation, release status). Search locations mirror Cantera mechanisms (explicit path, case directory, configured public material directory). Analytic EOS parameters stay runtime values; tabular EOS data stays external; only a custom symbolic EOS would need generated source.
PR 6: Separate chemistry from reaction integration. Formalize three concepts: thermodynamic backend, reaction mechanism, and reaction integrator. For current Pyrometheus chemistry these are the ideal-gas mixture, a mechanism generated from a Cantera file, and an explicit or alpha-QSS integrator. The interface is conceptually reaction_rates(state, mechanism_parameters), advance_reactions(state, dt), reaction_timestep_constraint(state). This lets integration improve without changing EOS selection and lets an EOS be added without implicitly adding reactions.
PR 7: Strengthen the Pyrometheus path. Before coupling it to new EOS families, make it a first-class supported backend: mechanism identity in build hashes, generated-code provenance recorded, mechanism validation before compilation, elemental and mass-conservation diagnostics, constant-volume and constant-pressure reactor tests, CPU/GPU agreement tests, and explicit reporting of the selected thermodynamic and kinetic backends. Keep generated m_thermochem.f90 in the staging/build area, not the source tree.
PR 8: Add generic progress-variable reactions. Distinct from Pyrometheus elementary chemistry. Introduce a small generic mechanism for condensed materials: progress lambda, a supplied rate law d(lambda)/dt, reactant EOS, product EOS, and energy release. Start with a published generic rate law or manufactured source. Test the zero-rate limit, homogeneous-reactor convergence, energy conservation, positivity and bounds 0 <= lambda <= 1, and equilibrium/end-state recovery.
PR 9: Couple reactants and products. Only after the individual EOS implementations work: reactant EOS, product JWL EOS, reaction progress, consistent mixture energy, pressure equilibrium, reaction energy offset, and sound speed/derivatives for intermediate states. This is where the hard thermodynamics lives; it should not be hidden inside "add a reaction source."
PR 10: Add specific reaction families individually. Each family in a separate PR: generic programmed burn, pressure-dependent progress law, ignition-and-growth-style model, and gas-phase afterburn coupled to Pyrometheus where scientifically appropriate. Each PR initially uses synthetic or clearly unrestricted parameters; real material calibration remains external.
Recommended first three PRs
- Central thermodynamics interface, no changed answers.
- Explicit EOS selector plus existing stiffened-gas and Pyrometheus adapters.
- Generic Mie-Gruneisen, followed by JWL as a reference-curve specialization.
That gives MFC a durable foundation. External material files and generic progress-variable reactions can then be added without building explosive-specific assumptions into the solver core.
Where Pyrometheus fits, and where it does not
Pyrometheus is well suited to gas-phase species chemistry, Cantera mechanisms, thermochemical properties, afterburn involving conventional chemical kinetics, and generated portable CPU/GPU reaction kernels. It is not the natural sole representation for program burn, ignition-and-growth progress laws, pressure-triggered empirical condensed-phase reactions, or a reactant-to-JWL-products mixture closure. Those should use the same reaction-integration infrastructure with a different mechanism backend; encoding them all as Cantera chemistry would blur important physics.
Target capability
- JWL pressure law for pure products:
- A composition-weighted mixture closure between JWL products and the ambient, with a closed-form pressure-energy inverse and analytic sound speed, degenerating exactly to the pure laws at the mixture endpoints.
- Optional reaction sources, off by default: kinematic program burn, products-air afterburn, and JWL++ (Souers 2000) reactive burn.
- Coupling with immersed boundaries and Euler-Lagrange bubbles.
- Validation: exact Riemann star states, resolved ZND structure against the analytic Hugoniot/Rayleigh/CJ construction, Sedov-Taylor blast scaling, far-field TNT overpressure against Kinney-Graham.
References
- Arienti, M., Morano, E., Shepherd, J.E., Shock and detonation modeling with the Mie-Gruneisen equation of state, GALCIT Report FM99-8 (2004).
- Souers, P.C. et al., JWL++: A Simple Reactive Flow Code Package for Detonation, Propellants, Explosives, Pyrotechnics 25 (2000).
- Garno, J.M. et al., Phys. Rev. Fluids 5, 123201 (2020), reactant energy offset for resolved ZND structure.
Activity
sbryngelson commentedon Aug 26, 2026
Two constraints that surfaced while centralising the EOS expressions (#1762), both of which bound what a JWL backend can cover more than any interface question does.
1. The pressure relaxation needs a closed-form invertible isentrope, which JWL lacks.
s_equilibrate_pressure(m_pressure_relaxation.fpp) already runs a per-cell Newton solve inside a[seq]device routine — so iterative equilibration on device is an existing, already-paid cost, not a new one. But the iteration needsrho(p)anddrho/dpper phase in closed form:That is the stiffened-gas isentrope (Saurel et al., JFM 2009). JWL's pressure inversion
p(rho, e)is closed-form and ports directly; its isentrope is not analytically invertible. A JWL phase in the six-equation model therefore needs either a nested sub-iteration inside that Newton loop or a reformulated relaxation. Numerical-methods work, not refactoring.2. Phase change needs a thermodynamically complete EOS; JWL is mechanical.
m_phase_changecomputes entropy, temperature and per-phase enthalpy (sk,hk,rhok,ek), and is the only consumer ofcvsandqvps. Stiffened gas has closed forms for all of them. JWL as normally written isp(rho, e)with no temperature or entropy unless a caloric extension is supplied. This is a physics limit rather than an implementation gap — the likely answer is that JWL with phase change is refused at validation.Neither blocks a JWL backend for the mechanical relations (energy, pressure, sound speed), which are all closed-form and now go through single operators after #1762. They bound where it can be used.
Also relevant: a per-fluid
eos_modeldispatch is viable on GPU because the branch is wavefront-uniform — every lane sits at the same fluid index — unlike branching on cell contents. But it must not be implemented with a derived type in the operator signature; see #1714, which cost 20% on gfx90a that way.sbryngelson commentedon Aug 28, 2026
Working through what the generic Mie-Gruneisen backend actually requires of the existing operators,
with numerical verification. Short version: the mixture rule needs no change, and the sound speed
needs exactly one extra term.
Mie-Gruneisen maps exactly onto the coefficients MFC already carries
Any Mie-Gruneisen EOS is affine in
pat fixed density, so it can be written in MFC's stored formwith density-dependent coefficients:
For JWL in standard form,
this gives
Gamma = 1/w(constant) andPi(rho) = -g(rho)/w, wheregcollects the twoexponential terms. Consistency check: an ideal gas has
w = gamma - 1, soGamma = 1/(gamma - 1),which is MFC's stored form for
fluid_pp(i)%gamma.The five-equation mixture rule survives mixed EOS families unchanged
s_compute_mixture_coefficientscomputesGamma_mix = sum(alpha_k Gamma_k)andPi_mix = sum(alpha_k Pi_k). That is just pressure equilibrium plus additivity of internal energy,solved for
p. What it requires is that each phase's(rho e)_kbe affine in p - not that thephases share an EOS family. Since Mie-Gruneisen is affine in
p, a JWL phase mixes with a stiffenedor ideal phase with no change to the mixture rule at all. JWL products expanding into air or water in
model_eqns = 2is therefore sound, which is what this issue assumes.The sound speed needs one extra term, and
c = sqrt(K/rho)still holdsFrom
c^2 = (dp/drho)_e + (p/rho^2)(dp/de)_rhowithrho e = Gamma(rho) p + Pi(rho):Pi' = Gamma' = 0, so this reduces to((Gamma+1)p + Pi)/(rho*Gamma),which is exactly today's
f_bulk_modulus/rho. Existing cases stay bit-identical by construction.Gamma' = 0, so the single new term is- rho*Pi'.So
f_bulk_modulusgeneralizes by one term that is identically zero for the current backends, andevery consumer of it - every sound speed in MFC is
K/rho, differing only in how phases are mixed -is untouched.
Mixture sound speed
Wood's law follows from differentiating volume additivity
1/rho = sum(Y_k/rho_k)at frozen massfractions with
drho_k = dp/c_k^2, giving1/(rho c^2) = sum(alpha_k/K_k)with no reference to theEOS form. It therefore inherits correctness from the per-phase
K_kabove, and thealt_soundspeedpath works for mixed EOS as soon as
f_bulk_moduluscarries the new term.Worth flagging: the default (non-
alt_soundspeed) path instead usesGamma_mix/Pi_mixdirectly - the frozen mixture speed. With a density-dependent
Pithose two differ, so mixed-EOScases should use the Wood path, or the default path needs its own treatment.
Verification
All of the above checked numerically against finite differences of the defining relations:
c^2formula vs(dp/drho)_e + (p/rho^2)(dp/de)_rho, 12 states spanning compression andexpansion: agreement to ~1e-10 relative.
Gamma(rho)andPi(rho)varying, to exercise theGamma'term:agreement to ~1e-10, including a state with
c^2 < 0, so the formula is not accidentally correctonly in the physically stable region.
Kon a genuine stiffened-water + JWL mixture, overtwo decades of pressure with
alpha_1from 0.29 to 0.60: agreement to ~1e-9.Implication for the incremental path
A backend supplies
Gamma(rho),Pi(rho)and their density derivatives. Nothing else in the solverlearns that a second EOS exists: no per-operator dispatch, no widened signatures in the hot loops,
and - importantly given #1714 - no derived type anywhere near a
[seq]device routine.sbryngelson commentedon Aug 29, 2026
#1762 lands PRs 1 and 2 of the plan above, and supersedes #1700 and #1714. A note on what the next
step actually needs, since the shape of PR 3 changed once the operators were written.
What is in place
Every EOS expression now has one definition, in
src/common/m_variables_conversion.fpp:Copies collapsed: mixture rule 9, bulk modulus 7, pressure inversion 5, elastic strain energy 4,
per-phase internal energy 3, isentrope parameters 3.
fluid_pp(i)%eosis live withstiffened_gasandideal_gas, and ideal gas is a real second backend rather than a relabelling -it does no arithmetic on
pi_infat all. A Python/Fortran enum-agreement test guards the twotables, because compound registry keys cannot be auto-generated.
PR 3 is smaller than it looks
MFC's stored form is already Mie-Gruneisen. Writing MFC's$\Gamma$ and $\Pi$ ,
gammaaspi_infasthe solver only ever needs
and any Mie-Gruneisen EOS$p = p_\text{ref}(\rho) + \Gamma_G(\rho)\rho,(e - e_\text{ref}(\rho))$
maps onto it exactly with
For JWL specifically$\Gamma = 1/\omega$ is constant and $\Pi(\rho) = -g(\rho)/\omega$ , where $g$ is
the density-only part of the JWL pressure. Checked numerically against the JWL law across a range
of densities and energies; the identity is exact.
So the generalisation is not a new state representation, it is letting$\Gamma$ and $\Pi$ depend on
$\rho$ . Two consequences:
The sound speed needs two extra terms. The general form is
which collapses to the existing
$\Gamma' = \Pi' = 0$ , so stiffened gas and ideal gas stay bit-identical. Verified against a$(\partial p/\partial\rho)_s$ for JWL to a relative error of 3e-11, and against a
((gamma + 1)*pres + pi_inf)/(gamma*rho)whenevernumerical
synthetic EOS with both derivatives nonzero.
Mixtures still work. Wood's law is a harmonic mean of bulk moduli, and$K$ that knows about $\Pi'$ and $\Gamma'$ . Checked on a
f_bulk_modulusisalready the single definition of that quantity, so a JWL-in-air or JWL-in-water mixture needs no new
mixture rule - only a per-fluid
stiffened-gas + JWL mixture.
Concrete shape of the patch
eos_mie_gruneiseninm_constants.fppand_EOS_NAMESinparams/definitions.py(both, orthe enum test fails).
fluid_pp, plus acase_validatorrule rejecting aMie-Gruneisen fluid where
model_eqnsor a feature cannot support it.f_mie_gruneisen_coefficients(rho, i, gamma, pi_inf, dgamma, dpi_inf)returning the fourquantities the sound speed needs.
s_compute_speed_of_soundgains the two derivative terms behind a branch that is dead for theconstant-coefficient backends.
The one structural change:
gammas(i)/pi_infs(i)are currently filled once at start-up fromfluid_pp, because a per-cell branch there costs registers in the Riemann kernels. Astate-dependent EOS has to be evaluated per cell instead, so the coefficient formation moves into
s_compute_mixture_coefficientsfor those fluids only. That is the part worth benchmarking early -it is in the hot path, and #1714 was abandoned for a 20% regression from exactly this kind of
change.
Two constraints worth knowing before writing device code
Both cost a CI round trip on #1762 and neither is visible from a local build:
dimension(num_fluids)) passed to adevice routine from inside another
GPU_ROUTINE(parallelism='[seq]'); the same call is finefrom a parallel-loop body. It reports
ftn-7066 ... Global in accelerator routine without declareat whatever line it gave up on, and the line walks forward as triggers are removed, so the
reported location is not the cause. Only non-case-optimized lanes fail.
fort2on a caller that passes aparameterarray fromm_thermocheminto a declare-target routine. Read those directly in the kernel instead.Both are now in
.claude/rules/common-pitfalls.md.sbryngelson commentedon Aug 30, 2026
One more site for the Mie-Gruneisen backend, found while auditing #1762 for inline stiffened-gas
arithmetic and deliberately left alone there.
f_phase_internal_energy(pres, alpha, alpha_rho, gamma, pi_inf, qv)givese_k = alpha_k (Gamma_k p + Pi_k) + alpha_rho_k qv_k. It has no inverse in the module, and the one placethat needs it inverts it by hand -
src/simulation/m_pressure_relaxation.fpp:139:which is exactly
p_k = ((e_k - alpha_rho_k qv_k)/alpha_k - Pi_k)/Gamma_k.Left as-is in #1762 because it is a single call site, and an
f_phase_pressurethere would be anabstraction for one caller. A second EOS creates the second caller: the phasic inversion is EOS-specific,
so this line has to change for JWL, and at that point the forward/inverse pair earns its place beside
f_phase_internal_energy.Related: the mixture-level inversion is already centralized as
f_pressure; this is the per-phase one.The isentrope inversion that pressure relaxation also needs is tracked separately in #1785.
sbryngelson commentedon Sep 2, 2026
PR 3 of the plan is split in two. #1805 lands the backend: Mie-Gruneisen as one dispatch over a reference curve, mapped onto MFC's
rho e = Gamma p + Piform so the operators centralized in #1762 are reused unchanged, with stiffened gas as the degenerate member and no solver wiring yet - every existing answer bit-identical. The follow-up wires it into the mixture coefficients and sound speed (frozen per-phase mixing under the five-equation model, Wood's law generalized to N fluids) and adds Hugoniot-recovery and isentropic-release validation cases via theconvergenceharness. JWL then becomes one morecasesupplying its reference curve.