Skip to content

Commit 76f8f51

Browse files
authored
Combustion: condensed-phase reactive burn (5-/6-equation, Arrhenius kinetics) + IBM burning surfaces (#1670)
* simulation: fix IBM+chemistry hang by setting consistent species/energy at immersed-boundary points * examples: enable IBM+chemistry in flame-holder now that the hang is fixed * examples: add hybrid-rocket fuel slab and 1D propellant flame (solid-fuel analogs) * simulation: add burning-grain surface fuel injection to IBM (v_blow + inj_species) * simulation: pressure-coupled IBM burn rate (Vieille's law) for internal-ballistics feedback * examples: make flameholder a proper reacting bluff-body (fuel-injecting cylinder, wake flame) * examples: add --tend knob to burning-grain case * simulation: add condensed-phase reactive burn (multi-fluid programmed detonation) * simulation: harden reactive_burn preconditions and IBM burn-rate against negative pressure * test: add reactive_burn regression test (condensed programmed burn) * examples: fix hybrid slab run time and viscosity (flame now anchors) * examples: add 3D spherical burning grain with octant symmetry * example: reactive shock-flame RM roll-up via hardcoded IC 273 * test: golden files for combustion example and reactive-burn tests (generated on master base) * checker: require rburn_pref > 0 and forbid chemistry + Euler bubbles Addresses PR review (Copilot): - reactive_burn divides the pressure drive by rburn_pref; prohibit rburn_pref <= 0 - s_interpolate_image_point selects the bubbles/QBMM branch before chemistry, so the Ys_IP species state is not carried when both are on; forbid chemistry + bubbles_euler/qbmm (Fortran m_checker + Python case_validator) rather than touch the hot IBM path * checker: constrain reactive_burn to model_eqns=2 and burn_rate_exp >= 0 Addresses independent review findings on #1670: - reactive_burn's exact volume-fraction swap and single-pressure read assume the 5-equation pressure-equilibrium model, but num_fluids=2 alone also admits model_eqns=3; prohibit model_eqns /= 2 (m_checker + case_validator.check_reactive_burn + PHYSICS_DOCS) - Vieille's-law v_blow ~ p^n diverges for a negative exponent as surface pressure -> 0; prohibit patch_ib%burn_rate_exp < 0 (case_validator.check_ibm) * feat(reactive-burn): 6-equation model support + Arrhenius temperature kinetics Extends the condensed-phase reactive burn beyond the 5-equation model to the 6-equation (Saurel/Pelanti) multi-fluid model, and adds an optional temperature-driven ignition term. 6-equation support: the reactant->product qv release is carried through the phasic-pressure relaxation. m_pressure_relaxation.fpp threaded the formation energy (alpha_rho_k*qv_k) through s_equilibrate_pressure and s_correct_internal_energies so the phasic pressures and the recovered mixture pressure are qv-consistent with s_compute_pressure -- otherwise a nonzero qv inflated the phasic pressure by rho_k*qv_k/gamma_k. The existing 5-eq burn source then works unchanged on model_eqns=3. For qv=0 the new terms are exactly zero, so all 13 existing 6-eq relaxation goldens are bit-identical (verified CPU/OMP/ACC). Arrhenius kinetics: new rburn_ta (activation temperature [K]); default 0 keeps the pure pressure-driven rate bit-identical. When rburn_ta>0 the rate gains a exp(-rburn_ta/T_r) factor, with T_r=(p+pi_inf)/((gamma-1)*cv*rho) the reactant phasic temperature. Guarded (checker + case_validator) so rburn_ta>0 with cv=0 is rejected instead of silently disabling the factor. Test: adds a model_eqns=3 reactive-burn regression golden (nested off the 5-eq test), and hardens both reactive goldens by giving the uniform case a small advection velocity so the momentum output is well-resolved rather than near-zero roundoff (the old golden compared ~0 momentum at 1e-12 and failed across recompiles/backends). Validated on CPU, OpenMP-offload, and OpenACC. * test(reactive-burn): add Arrhenius (rburn_ta>0) regression golden Nested off the reactive-burn detonation test with rburn_ta=500 and cv=1500, chosen so the exp(-rburn_ta/T_r) factor is O(0.4) at the IC temperature -- exercising the temperature branch (all other reactive goldens use rburn_ta=0). Verified on CPU, OpenMP-offload, and OpenACC. * refactor(reactive-burn): group rburn params into a derived type; address review panel + CI Post-review hardening from a 5-reviewer panel, plus the CI-surfaced flame-example fragility. rburn derived type: the five rburn_* scalars (k, pign, pref, n, ta) become a rburn type instance, matching the sibling chem_params pattern (type in m_derived_types, TYPED_DECLS registration, member _r/_nv, descriptions regex, auto GPU_DECLARE + GPU_UPDATE(device=[rburn]) in m_global_parameters and m_start_up so the type is device-resident). Numerically inert (~1 ULP recompile roundoff); reactive goldens regenerated for the namelist-key change. Verified CPU/OMP/ACC. Completeness guards (review): rburn%k (>0), rburn%pign (set), rburn%pref (>0), rburn%n (>=0) are now validated in both m_checker and case_validator. Each defaults to the dflt_real sentinel, so an unset value silently produced spurious ignition (pign), NaN via drive**n (n), or a backward reaction (k). IB inj_species bound (review): m_checker now requires patch_ib%inj_species <= num_species when chemistry + ib; it indexes Ys_IP(1:num_species) at image points, so an out-of-range value was an out-of-bounds write. Docs (review): reactive_burn requires num_fluids = 2 (checker aborts otherwise), corrected from >= 2 in case.md and the module docstring. CI: skip the 1D_propellant_flame example (golden removed). Its finite-rate flame front drifts past the 1e-3 Example tolerance across compilers (nvhpc 25.11 golden vs 24.3/GNU/Intel/CCE/AMD) -- same non-portability class as the flame examples already skipped. The reactive_burn detonation goldens remain portable (they passed every CI lane). * fix(reactive-burn): broadcast the rburn derived type to non-root ranks The rburn refactor moved the five rburn_* scalars (auto-broadcast) into a derived type, but derived-type members are not auto-broadcast -- they are hand-emitted per type in fortran_gen.py (as for chem_params/lag_params). rburn had no emitter, so on a multi-rank reactive_burn run every non-root rank kept rburn at the dflt_real sentinel: a silent wrong-physics bug the single-rank golden tests cannot catch. Adds _emit_rburn (all members REAL -> mpi_p, under the reactive_burn guard, sim target), mirroring _emit_chem_params. Verified in the regenerated generated_bcast.fpp. * test(reactive-burn): add a 2-rank MPI regression test for the rburn broadcast All other reactive goldens run single-rank, so a broken rburn broadcast is invisible to them. This ppn=2 variant of the detonation case burns rank 1's half of the domain with the broadcast rburn; verified to FAIL when the rburn broadcast emitter is removed and PASS with it, so it locks the fix in CI. * test(ibm): add a golden for the Vieille's-law pressure-coupled burn rate The ibm_burning_grain/ibm_flameholder Example goldens already cover surface injection (v_blow + the inj_species composition swap), but both leave burn_rate_exp at 0, so the pressure-coupled scaling v_blow*(p/burn_rate_pref)^n shipped with no regression coverage. Registers the same example with --burn_exp 0.5 at the Example suite's 25x25/50-step size, so it differs from the existing golden only by the coupling: turning it on moves the solution by ~5e-3 relative (1.3 absolute in energy), well above the 1e-3 tolerance, so a broken multiplier fails the test. Tolerance pinned to 1e-3 to match the sibling Example golden -- compute_tolerance would otherwise pick 1e-10 via the ib branch, which a 50-step IBM + finite-rate-chemistry run cannot hold across compilers. * test: skip the 2D_hybrid_slab example golden (not portable across compilers) The diffusion flame over the fuel slab is positioned by accumulated stiff-kinetics roundoff, so by step 50 the transverse momentum drifts past the 1e-3 Example tolerance: nvhpc lanes pass, Intel and Frontier CCE disagree with the golden by ~2e-3 absolute (up to 44% relative on a small component). Same non-portability class as 1D_propellant_flame and the other flame examples already skipped there, so no single golden passes every lane. The case remains a runnable example; the reactive_burn, shock_flame, ibm_burning_grain and ibm_flameholder goldens are unaffected and pass on all lanes. * fix(ibm): initialize the burning-surface fields on particle-cloud IB patches particle_cloud_ibs is allocated without default initialization, and s_add_cloud_particle set only the geometry/motion members. s_reduce_ib_patch_array then copies the whole struct into patch_ib, overwriting the zeroed defaults from s_assign_default_values_to_user_inputs -- so v_blow, inj_species and burn_rate_exp/pref reached the solver as uninitialized memory. That was latent until this branch added the first read of those members in m_ibm: a garbage v_blow > 0 superimposes a garbage wall-normal velocity on the ghost points, which NaNs the field. It reproduced only where the allocation is not already zero-filled -- Frontier AMD failed 2D_mibm_particle_cloud with 'ICFL is NaN' at step 2 while every NVHPC lane (and local CPU/OMP/ACC) passed. Sets the four fields for the inert particle surfaces; audited that these are exactly the patch_ib members m_ibm reads (airfoil_id/model_id/length_x remain unset but are never read for circle/sphere particles). * docs: record the derived-type broadcast and patch_ib init traps in common-pitfalls Both bit this branch, both fail silently, and neither is visible to local testing: a derived-type param regrouping dropped its MPI broadcast (non-root ranks kept the sentinel, invisible to single-rank goldens), and an uninitialized patch_ib member reached the solver through the whole-struct copy from particle_cloud_ibs (garbage v_blow -> ICFL NaN, reproducing only on Frontier AMD where the allocation is not zero-filled). Documents the required steps and the platform-only-NaN signature.
1 parent 86165fc commit 76f8f51

47 files changed

Lines changed: 3381 additions & 23 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/rules/common-pitfalls.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,23 @@ covered in `docs/documentation/contributing.md`.
5959
Gotcha: ADDING a new file under `toolchain/mfc/params/` needs one reconfigure
6060
(the custom command's DEPENDS list is globbed at configure time). Under `--case-optimization` the baked-in constants are dropped from the
6161
namelist, so changing one needs a *rebuild*, not a case edit.
62+
- Derived-type params (`chem_params`, `lag_params`, `rburn`) are NOT auto-broadcast:
63+
`generated_bcast.fpp` covers namelist *scalars* only. Each type needs a hand-written
64+
`_emit_<name>` in `toolchain/mfc/params/generators/fortran_gen.py` plus its call site in
65+
the `target == "sim"` block, and — if it is read on device — an explicit
66+
`$:GPU_UPDATE(device='[name]')` in BOTH `m_global_parameters.fpp` and `m_start_up.fpp`
67+
(`GPU_DECLARE` alone does not make it device-resident). Regrouping scalars into a derived
68+
type silently drops their broadcast, so every non-root rank keeps the `dflt_real`
69+
sentinel; single-rank goldens cannot see this, so pair such a change with a `ppn=2` test
70+
and confirm it fails without the emitter.
71+
- A `patch_ib` member that any `m_ibm` ghost-point code reads must ALSO be set in
72+
`s_add_cloud_particle` (`src/simulation/m_particle_cloud.fpp`): `particle_cloud_ibs` is
73+
`allocate`d without default initialization, and `s_reduce_ib_patch_array` copies the whole
74+
struct into `patch_ib`, overwriting the defaults from
75+
`s_assign_default_values_to_user_inputs`. Anything left unset reaches the solver as
76+
uninitialized memory, and only where the allocation is not already zero-filled — a
77+
garbage `v_blow` failed Frontier AMD with `ICFL is NaN` while every NVIDIA lane and all
78+
local CPU/GPU runs passed. A platform-only NaN is the signature of this class.
6279
- Shared-state pattern: namelist declarations (`#:include 'generated_decls.fpp'`), the
6380
`eqn_idx`/`sys_size`/`b_size`/`tensor_size` state variables, and the common defaults
6481
core all live in `src/common/m_global_parameters_common.fpp`. Each per-target

docs/documentation/case.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,9 +1129,17 @@ Note: For relativistic flow, the conservative and primitive densities are differ
11291129
| `tau_star` | Real | Threshold stress for continuum damage model |
11301130
| `cont_damage_s` | Real | Power `s` for continuum damage model |
11311131
| `alpha_bar` | Real | Damage factor (rate) for continuum damage model |
1132+
| `reactive_burn` | Logical | Enable condensed-phase reactive burn |
1133+
| `rburn%%k` | Real | Reactive-burn rate coefficient [1/s] |
1134+
| `rburn%%pign` | Real | Reactive-burn ignition pressure threshold [Pa] |
1135+
| `rburn%%pref` | Real | Reactive-burn reference pressure for the drive [Pa] |
1136+
| `rburn%%n` | Real | Reactive-burn pressure-drive exponent |
1137+
| `rburn%%ta` | Real | Reactive-burn activation temperature [K] (0 = off) |
11321138

11331139
- `cont_damage` activates continuum damage model for solid materials. Requires `tau_star`, `cont_damage_s`, and `alpha_bar` to be set (empirically determined) (\cite Cao19).
11341140

1141+
- `reactive_burn` converts a "reactant" fluid into a "product" fluid (`num_fluids = 2`, ``chemistry = 'F'``) via a programmed pressure burn `dlambda/dt = rburn%%k (1 - lambda) ((p - rburn%%pign)/rburn%%pref)^rburn%%n`. The two fluids share the same `gamma`/`pi_inf` and differ only in `qv`, so the conversion releases `qv` through the mixture EOS — a reactive-Euler/ZND detonation model on the diffuse-interface framework. It runs on the 5-equation (`model_eqns = 2`) and 6-equation (`model_eqns = 3`) multi-fluid models. Setting `rburn%%ta > 0` multiplies the rate by an Arrhenius factor `exp(-rburn%%ta/T)`, where `T` is the reactant phasic temperature, giving temperature-driven ignition instead of a pure pressure switch.
1142+
11351143
### 16. Cylindrical Coordinates
11361144

11371145
When ``cyl_coord = 'T'`` is set in 3D the following constraints must be met:

docs/module_categories.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"m_viscous",
2323
"m_hb_function",
2424
"m_surface_tension",
25+
"m_reactive_burn",
2526
"m_bubbles",
2627
"m_bubbles_EE",
2728
"m_bubbles_EL",
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#!/usr/bin/env python3
2+
# 1D premixed H2/O2/Ar deflagration: a self-propagating premixed flame, used
3+
# as a stand-in for a solid-propellant burn front. A hot equilibrium-product
4+
# kernel at the left end ignites a flame that eats into the fresh premix.
5+
import argparse
6+
import json
7+
8+
import cantera as ct
9+
10+
parser = argparse.ArgumentParser(prog="1D_propellant_flame")
11+
parser.add_argument("--mfc", type=json.loads, default="{}", metavar="DICT", help="MFC toolchain state.")
12+
parser.add_argument("--length", type=float, default=0.008, help="Domain length [m].")
13+
parser.add_argument("--m", type=int, default=200, help="Number of grid cells.")
14+
parser.add_argument("--kernel", type=float, default=1e-3, help="Ignition-kernel width at the left end [m].")
15+
parser.add_argument("--ar", type=float, default=3.0, help="Ar dilution (H2:2,O2:1,AR:<ar>); higher = calmer/slower flame.")
16+
parser.add_argument("--tend", type=float, default=1.2e-4, help="Physical end time [s].")
17+
parser.add_argument("--cfl", type=float, default=0.35, help="Target acoustic CFL number.")
18+
parser.add_argument("--frames", type=int, default=10, help="Number of saved output frames (raise for a smooth movie).")
19+
args = parser.parse_args()
20+
21+
ctfile = "h2o2.yaml"
22+
X = f"H2:2,O2:1,AR:{args.ar}"
23+
T0, P0 = 300.0, 101325.0
24+
25+
# Fresh premixed reactants (fill the domain).
26+
fresh = ct.Solution(ctfile)
27+
fresh.TPX = T0, P0, X
28+
29+
# Ignition kernel: constant-pressure (isobaric) equilibrium products of the
30+
# same mixture -> a thermodynamically self-consistent hot patch (T, rho, Y
31+
# all from Cantera) that ignites a flame without an initial pressure jump.
32+
burned = ct.Solution(ctfile)
33+
burned.TPX = T0, P0, X
34+
burned.equilibrate("HP")
35+
36+
L = args.length
37+
Nx = args.m
38+
dx = L / Nx
39+
40+
c_max = max(fresh.sound_speed, burned.sound_speed)
41+
dt = args.cfl * dx / c_max
42+
43+
NT = int(args.tend / dt)
44+
NS = max(1, NT // args.frames)
45+
46+
case = {
47+
"run_time_info": "T",
48+
# Domain
49+
"x_domain%beg": 0.0,
50+
"x_domain%end": L,
51+
"m": Nx,
52+
"n": 0,
53+
"p": 0,
54+
"dt": float(dt),
55+
"t_step_start": 0,
56+
"t_step_stop": NT,
57+
"t_step_save": NS,
58+
"t_step_print": NS,
59+
"parallel_io": "T",
60+
# Algorithm
61+
"model_eqns": 2,
62+
"num_fluids": 1,
63+
"num_patches": 2,
64+
"mpp_lim": "F",
65+
"mixture_err": "F",
66+
"time_stepper": 3,
67+
"weno_order": 5,
68+
"weno_eps": 1e-16,
69+
"weno_avg": "F",
70+
"mapped_weno": "T",
71+
"mp_weno": "T",
72+
"riemann_solver": 2,
73+
"wave_speeds": 2,
74+
"avg_state": 1,
75+
# BCs: left reflective wall, right outflow
76+
"bc_x%beg": -2,
77+
"bc_x%end": -3,
78+
# Flame is diffusion-controlled: viscous + species/thermal diffusion on
79+
"viscous": "T",
80+
"chemistry": "T",
81+
"chem_params%diffusion": "T",
82+
"chem_params%reactions": "T",
83+
"chem_params%transport_model": 2,
84+
"cantera_file": ctfile,
85+
# Output
86+
"format": "silo",
87+
"precision": "double",
88+
"prim_vars_wrt": "T",
89+
"chem_wrt_T": "T",
90+
# Patch 1: fresh premix, whole domain
91+
"patch_icpp(1)%geometry": 1,
92+
"patch_icpp(1)%x_centroid": L / 2,
93+
"patch_icpp(1)%length_x": L,
94+
"patch_icpp(1)%vel(1)": 0.0,
95+
"patch_icpp(1)%pres": fresh.P,
96+
"patch_icpp(1)%alpha(1)": 1.0,
97+
"patch_icpp(1)%alpha_rho(1)": fresh.density,
98+
# Patch 2: hot equilibrium-product ignition kernel, left end
99+
"patch_icpp(2)%geometry": 1,
100+
"patch_icpp(2)%alter_patch(1)": "T",
101+
"patch_icpp(2)%x_centroid": args.kernel / 2,
102+
"patch_icpp(2)%length_x": args.kernel,
103+
"patch_icpp(2)%vel(1)": 0.0,
104+
"patch_icpp(2)%pres": burned.P,
105+
"patch_icpp(2)%alpha(1)": 1.0,
106+
"patch_icpp(2)%alpha_rho(1)": burned.density,
107+
# Fluid EOS (ideal-gas closure is bypassed by chemistry, but gamma/Re must be set)
108+
"fluid_pp(1)%gamma": 1.0 / (1.4 - 1.0),
109+
"fluid_pp(1)%pi_inf": 0.0,
110+
"fluid_pp(1)%Re(1)": 1.0 / fresh.viscosity,
111+
}
112+
113+
# Species mass fractions per patch + OH output (flame marker)
114+
for i in range(fresh.n_species):
115+
case[f"patch_icpp(1)%Y({i + 1})"] = float(fresh.Y[i])
116+
case[f"patch_icpp(2)%Y({i + 1})"] = float(burned.Y[i])
117+
case[f"chem_wrt_Y({fresh.species_index('OH') + 1})"] = "T"
118+
119+
if __name__ == "__main__":
120+
print(json.dumps(case))

examples/1D_reactive_burn/case.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#!/usr/bin/env python3
2+
# 1D condensed-phase detonation via programmed pressure burn (reactive_burn).
3+
# A dense stiffened-gas "reactant" is converted to a "product" fluid that shares
4+
# the same mechanical EOS (gamma, pi_inf) but has a lower reference energy qv --
5+
# so reactant -> product releases (qv_reactant - qv_product) per unit mass through
6+
# the mixture EOS, with no explicit energy source. A high-pressure initiator at the
7+
# left end launches a shock; where the shock raises the pressure above rburn%pign
8+
# the reactant burns, and the energy release sustains a self-propagating detonation
9+
# (von Neumann spike + Taylor rarefaction, near-CJ speed). Multi-fluid model,
10+
# chemistry OFF -- this deliberately bypasses the Cantera num_fluids=1 lock.
11+
import argparse
12+
import json
13+
14+
parser = argparse.ArgumentParser(prog="1D_reactive_burn")
15+
parser.add_argument("--mfc", type=json.loads, default="{}", metavar="DICT", help="MFC toolchain state.")
16+
parser.add_argument("--res", type=float, default=1.0, help="Resolution multiplier: m=3000*res.")
17+
parser.add_argument("--tend", type=float, default=8.0e-6, help="Physical end time [s].")
18+
parser.add_argument("--frames", type=int, default=80, help="Number of saved output frames.")
19+
parser.add_argument("--model", type=int, choices=[2, 3], default=2, help="Multi-fluid model: 2 = 5-equation, 3 = 6-equation.")
20+
args = parser.parse_args()
21+
22+
# 6-eq needs both phases present everywhere (its pressure-relaxation Newton solve is singular
23+
# on a zero-volume phase); seed a trace of product. 5-eq starts pure reactant.
24+
eps = 1.0e-6 if args.model == 3 else 0.0
25+
a1, a2 = 1.0 - eps, eps
26+
27+
# --- Stiffened-gas EOS shared by reactant and product (mechanically identical) ---
28+
Gamma = 3.0 # physical adiabatic exponent
29+
Pi = 6.0e8 # stiffening pressure [Pa]
30+
gamma_p = 1.0 / (Gamma - 1.0) # MFC gamma parameter
31+
pi_inf_p = Gamma * Pi / (Gamma - 1.0) # MFC pi_inf parameter
32+
Q = 4.0e6 # heat of reaction [J/kg] (reactant qv; product qv = 0)
33+
34+
rho0 = 1600.0 # reactant density [kg/m^3]
35+
p0 = 1.0e5 # ambient pressure [Pa]
36+
c0 = (Gamma * (p0 + Pi) / rho0) ** 0.5 # reactant sound speed
37+
38+
# Domain
39+
L = 0.06
40+
m = int(3000 * args.res)
41+
dx = L / m
42+
x_init = 0.004 # initiator occupies the left 4 mm
43+
p_init = 5.0e9 # initiator pressure [Pa] -> launches an igniting shock
44+
45+
# CJ-scale detonation speed sets dt.
46+
D_guess = 8000.0
47+
cfl = 0.3
48+
dt = cfl * dx / (D_guess + c0)
49+
NT = int(args.tend / dt)
50+
NS = max(1, NT // args.frames)
51+
52+
case = {
53+
"run_time_info": "T",
54+
"x_domain%beg": 0.0,
55+
"x_domain%end": L,
56+
"m": m,
57+
"n": 0,
58+
"p": 0,
59+
"dt": float(dt),
60+
"t_step_start": 0,
61+
"t_step_stop": NT,
62+
"t_step_save": NS,
63+
"t_step_print": max(1, NT // 20),
64+
"parallel_io": "T",
65+
# Algorithm
66+
"model_eqns": args.model,
67+
"num_fluids": 2,
68+
"num_patches": 2,
69+
"mpp_lim": "T",
70+
"mixture_err": "T",
71+
"time_stepper": 3,
72+
"weno_order": 5,
73+
"weno_eps": 1e-16,
74+
"mapped_weno": "T",
75+
"mp_weno": "T",
76+
"weno_avg": "F",
77+
"riemann_solver": "hllc",
78+
"wave_speeds": "direct",
79+
"avg_state": "arithmetic",
80+
# Reactant reflects at the initiator wall; open at the far end.
81+
"bc_x%beg": -2,
82+
"bc_x%end": -3,
83+
# Condensed-phase reactive burn
84+
"reactive_burn": "T",
85+
"rburn%k": 5.0e6, # rate coefficient [1/s]
86+
"rburn%pign": 5.0e8, # ignition pressure threshold [Pa]
87+
"rburn%pref": 1.0e9, # reference pressure for the pressure drive [Pa]
88+
"rburn%n": 1.0, # pressure exponent
89+
# Output
90+
"format": "silo",
91+
"precision": "double",
92+
"prim_vars_wrt": "T",
93+
# Patch 1: unreacted reactant fills the domain (fluid 1)
94+
"patch_icpp(1)%geometry": 1,
95+
"patch_icpp(1)%x_centroid": L / 2,
96+
"patch_icpp(1)%length_x": L,
97+
"patch_icpp(1)%vel(1)": 0.0,
98+
"patch_icpp(1)%pres": p0,
99+
"patch_icpp(1)%alpha_rho(1)": rho0 * a1,
100+
"patch_icpp(1)%alpha_rho(2)": rho0 * a2,
101+
"patch_icpp(1)%alpha(1)": a1,
102+
"patch_icpp(1)%alpha(2)": a2,
103+
# Patch 2: high-pressure initiator (reactant), left end
104+
"patch_icpp(2)%geometry": 1,
105+
"patch_icpp(2)%alter_patch(1)": "T",
106+
"patch_icpp(2)%x_centroid": x_init / 2,
107+
"patch_icpp(2)%length_x": x_init,
108+
"patch_icpp(2)%vel(1)": 0.0,
109+
"patch_icpp(2)%pres": p_init,
110+
"patch_icpp(2)%alpha_rho(1)": rho0 * a1,
111+
"patch_icpp(2)%alpha_rho(2)": rho0 * a2,
112+
"patch_icpp(2)%alpha(1)": a1,
113+
"patch_icpp(2)%alpha(2)": a2,
114+
# Fluid EOS: reactant (1) and product (2) share gamma/pi_inf, differ only in qv.
115+
"fluid_pp(1)%gamma": gamma_p,
116+
"fluid_pp(1)%pi_inf": pi_inf_p,
117+
"fluid_pp(1)%qv": Q,
118+
"fluid_pp(2)%gamma": gamma_p,
119+
"fluid_pp(2)%pi_inf": pi_inf_p,
120+
"fluid_pp(2)%qv": 0.0,
121+
}
122+
123+
if __name__ == "__main__":
124+
print(json.dumps(case))

0 commit comments

Comments
 (0)