<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cantera on Harry's Personal Site</title><link>https://harryzhou2000.github.io/hugo-harry/tags/cantera/</link><description>Recent content in Cantera on Harry's Personal Site</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Thu, 27 Aug 2026 17:00:00 +0800</lastBuildDate><atom:link href="https://harryzhou2000.github.io/hugo-harry/tags/cantera/index.xml" rel="self" type="application/rss+xml"/><item><title>DNDSR Reaction Experiments</title><link>https://harryzhou2000.github.io/hugo-harry/p/dndsr-reaction-experiments/</link><pubDate>Thu, 27 Aug 2026 17:00:00 +0800</pubDate><guid>https://harryzhou2000.github.io/hugo-harry/p/dndsr-reaction-experiments/</guid><description>&lt;img src="https://harryzhou2000.github.io/hugo-harry/p/dndsr-reaction-experiments/cover.png" alt="Featured image of post DNDSR Reaction Experiments" />&lt;p>This note summarizes the current reactive-flow implementation in DNDSR and the practical pitfalls we hit while validating it against 1-D premixed-flame and detonation problems. The discussion is anchored to actual commits in the DNDSR repository.&lt;/p>
&lt;h2 id="1-reactive-flow-implementation-overview">&lt;a href="#1-reactive-flow-implementation-overview" class="header-anchor">&lt;/a>1. Reactive-flow implementation overview
&lt;/h2>&lt;p>DNDSR&amp;rsquo;s reactive solver couples the compressible Euler/Navier-Stokes equations with Cantera thermodynamics, kinetics, and mixture-averaged transport. The feature landed in the large dev/harry integration (&lt;a class="link" href="https://github.com/harryzhou2000/DNDSR/commit/1f20f525a95d3d2b006193b6bfddd82efdb44ee0" target="_blank" rel="noopener"
>1f20f525&lt;/a>). Key pieces include:&lt;/p>
&lt;ul>
&lt;li>Multi-species transport with an $N_s - 1$ independent-species formulation.&lt;/li>
&lt;li>A PIMPL ChemicalSource wrapper around Cantera (src/Euler/Chemistry/ChemicalSource.{hpp,cpp}) so that the rest of the Euler module is Cantera-free at compile time.&lt;/li>
&lt;li>Reactive Roe/HLLC/HLLEP Riemann solvers with species-aware positivity preservation.&lt;/li>
&lt;li>Optional Strang splitting (sourceStrangSplitting) versus a fully coupled (non-Strang) pseudo-time source.&lt;/li>
&lt;/ul>
&lt;p>The conservative state for the extended Euler model is&lt;/p>
$$
U = \begin{bmatrix}
\rho \\
\rho u \\
\rho v \\
\rho w \\
\rho E \\
\rho Y_1 \\
\vdots \\
\rho Y_{N_s-1}
\end{bmatrix},
$$&lt;p>where the last species $Y_{N_s}$ (usually N2) is algebraically dependent:&lt;/p>
$$
\rho Y_{N_s} = \rho - \sum_{k=1}^{N_s-1} \rho Y_k .
$$&lt;h2 id="2-energy-bookkeeping-canteras-internal-energy-vs-dndsrs-sensible-representation">&lt;a href="#2-energy-bookkeeping-canteras-internal-energy-vs-dndsrs-sensible-representation" class="header-anchor">&lt;/a>2. Energy bookkeeping: Cantera&amp;rsquo;s internal energy vs. DNDSR&amp;rsquo;s sensible representation
&lt;/h2>&lt;h3 id="21-the-convention-gap">&lt;a href="#21-the-convention-gap" class="header-anchor">&lt;/a>2.1 The convention gap
&lt;/h3>&lt;p>Cantera reports an absolute specific internal energy $u_k^{abs}(T)$ for each species. In a reacting simulation the zero point of that energy is irrelevant for the dynamics, but it can be huge and of mixed sign. To keep the total-energy conservative variable well-conditioned and to guarantee a positive sensible internal energy, DNDSR subtracts a base internal energy evaluated at a reference temperature $T_{base}$:&lt;/p>
$$
e_{base,k} = u_k^{abs}(T_{base}) .
$$&lt;p>The sensible specific internal energy is then&lt;/p>
$$
e_{sensible} = \sum_k Y_k \bigl(u_k^{abs}(T) - e_{base,k}\bigr) .
$$&lt;p>In conservative form the total energy density is split as&lt;/p>
$$
\rho E = \underbrace{\rho e_{sensible} + \frac{1}{2}\rho |\mathbf{u}|^2}_{\text{sensible part}} + \underbrace{\rho \sum_k Y_k e_{base,k}}_{\rho e_{base}} .
$$&lt;p>The volumetric base energy is&lt;/p>
$$
\rho E_{base} = \rho \sum_k Y_k e_{base,k} .
$$&lt;p>Because $e_{base,k}$ is constant, $\rho E_{base}$ depends only on the species field, and the sensible part carries the temperature.&lt;/p>
&lt;h3 id="22-temperature-positivity-through-sensible-energy">&lt;a href="#22-temperature-positivity-through-sensible-energy" class="header-anchor">&lt;/a>2.2 Temperature positivity through sensible energy
&lt;/h3>&lt;p>DNDSR does &lt;strong>not&lt;/strong> enforce $T &amp;gt; 0$ directly. Instead, every positivity-preserving check enforces&lt;/p>
$$
\rho e_{\text{sensible}} > 0.
$$&lt;p>Because&lt;/p>
$$
e_{\text{sensible}}(T,Y)
= \sum_k Y_k \bigl(u_k^{abs}(T) - e_{\text{base},k}\bigr)
= e_{\text{tot}}(T,Y) - e_{\text{tot}}(T_{\text{base}},Y),
$$&lt;p>and the mixture heat capacity at constant volume is positive, $e_{\text{sensible}}(T,Y)$ is strictly increasing with $T$. Therefore&lt;/p>
$$
\rho e_{\text{sensible}} > 0
\;\Longleftrightarrow\;
T > T_{\text{base}}.
$$&lt;p>That is why &lt;code>AssertMeanValuePP&lt;/code>, &lt;code>EvaluateCellRHSAlpha&lt;/code>, and &lt;code>EvaluateURecBeta&lt;/code> all operate on the sensible energy rather than on temperature: requiring $T &amp;gt; T_{\text{base}}$ is the same physical requirement as $T$ staying above the lowest thermodynamically tabulated temperature. A cell with $T \le T_{\text{base}}$ would have non-positive sensible energy and would be rejected by the limiter.&lt;/p>
&lt;h3 id="23-equation-of-state-and-equivalent-gamma">&lt;a href="#23-equation-of-state-and-equivalent-gamma" class="header-anchor">&lt;/a>2.3 Equation of state and equivalent gamma
&lt;/h3>&lt;p>The ideal-gas closure is written in terms of the sensible internal energy:&lt;/p>
$$
p = (\gamma_{eq} - 1) \rho e_{sensible} .
$$&lt;p>For a reactive mixture, DNDSR defines an equivalent $\gamma_{eq}$ so that this relation holds with the exact Cantera pressure $p = \rho R_{mix} T$:&lt;/p>
$$
\gamma_{eq} = 1 + \frac{p}{\rho e_{sensible}} = 1 + \frac{R_{mix} T}{e_{sensible}} .
$$&lt;p>This is implemented in PhysicsProperties::gammaEq (src/Euler/Physics/PhysicsProperties.hpp). The acoustic decomposition in the Roe solver and the primitive-to-conservative conversions in Gas.hpp all accept an optional $\rho E_{base}$ argument.&lt;/p>
&lt;h3 id="24-temperature-floor-and-dynamic-base-temperature">&lt;a href="#24-temperature-floor-and-dynamic-base-temperature" class="header-anchor">&lt;/a>2.4 Temperature floor and dynamic base temperature
&lt;/h3>&lt;p>Early code hard-coded temperature floors at 200 K or 300 K. These magic numbers caused subtle mismatches with the base-energy bookkeeping. They were replaced by ChemicalSource::baseTemperature() (&lt;a class="link" href="https://github.com/harryzhou2000/DNDSR/commit/f78b03338acb948df266fde1a225decb1ca29f92" target="_blank" rel="noopener"
>f78b0333&lt;/a>), so the thermodynamic floor is exactly the temperature at which the base energy is evaluated.&lt;/p>
&lt;h2 id="3-positivity-preserving-of-species">&lt;a href="#3-positivity-preserving-of-species" class="header-anchor">&lt;/a>3. Positivity-preserving of species
&lt;/h2>&lt;h3 id="31-independentdependent-species-and-the-simplex">&lt;a href="#31-independentdependent-species-and-the-simplex" class="header-anchor">&lt;/a>3.1 Independent/dependent species and the simplex
&lt;/h3>&lt;p>DNDSR transports only $N_s - 1$ species. The dependent species is recovered by mass conservation. For the mean state to be physically admissible we need&lt;/p>
$$
\rho Y_k \ge 0, \qquad \sum_{k=1}^{N_s-1} \rho Y_k \le \rho .
$$&lt;p>These checks are enforced in AssertMeanValuePP (src/Euler/EulerEvaluator.hxx). If either condition fails, the cell mean is flagged as non-physical.&lt;/p>
&lt;h3 id="32-repairing-mass-fractions-for-cantera">&lt;a href="#32-repairing-mass-fractions-for-cantera" class="header-anchor">&lt;/a>3.2 Repairing mass fractions for Cantera
&lt;/h3>&lt;p>Reconstructed quadrature states and boundary values may lie slightly outside the species simplex. Before calling Cantera, DNDSR repairs them with Chemistry::RepairMassFractions (src/Euler/Chemistry/ChemicalSource.hpp):&lt;/p>
&lt;ol>
&lt;li>Clamp each transported $\rho Y_k$ to $[0, \rho]$ and divide by $\rho$ to get a trial $Y_k$.&lt;/li>
&lt;li>Set the dependent species as $Y_{N_s} = \max{0, 1 - \sum_{k=1}^{N_s-1} Y_k }$.&lt;/li>
&lt;li>Renormalize the whole vector so that $\sum_k Y_k = 1$.&lt;/li>
&lt;/ol>
&lt;p>Crucially, the repair only touches the temporary composition passed to Cantera. The transported conservative variables $\rho Y_k$ are left untouched; otherwise the solver would quietly inject/consume mass during reconstruction.&lt;/p>
&lt;h3 id="33-reconstruction-pp-vs-mean-state-pp">&lt;a href="#33-reconstruction-pp-vs-mean-state-pp" class="header-anchor">&lt;/a>3.3 Reconstruction PP vs. mean-state PP
&lt;/h3>&lt;p>There are two different positivity-preserving constraints and they are not the same:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Mean-state PP&lt;/strong> operates on the cell-average $U_i$. It asserts $\rho Y_k \ge 0$ and $\sum \rho Y_k \le \rho$, and checks that the sensible energy is above the base energy.&lt;/li>
&lt;li>&lt;strong>Reconstruction PP&lt;/strong> operates on the reconstructed quadrature values $U_{iG}$. In EvaluateURecBeta it checks that the species block is non-negative and that the row-wise sum satisfies $\sum_k \rho Y_{k,G} \le \rho_G$ before accepting the reconstruction.&lt;/li>
&lt;/ul>
&lt;p>The reconstruction check was made species-aware in &lt;a class="link" href="https://github.com/harryzhou2000/DNDSR/commit/ec3eb0ba83926bed04a220a00d7281e2cfd363f7" target="_blank" rel="noopener"
>ec3eb0ba&lt;/a>.&lt;/p>
&lt;h2 id="4-other-pitfalls-state-mismatching-source-jacobian-and-rhoe-flux">&lt;a href="#4-other-pitfalls-state-mismatching-source-jacobian-and-rhoe-flux" class="header-anchor">&lt;/a>4. Other pitfalls: state mismatching, source Jacobian, and rhoE flux
&lt;/h2>&lt;h3 id="41-clipped-vs-raw-base-energy--the-central-mismatch">&lt;a href="#41-clipped-vs-raw-base-energy--the-central-mismatch" class="header-anchor">&lt;/a>4.1 Clipped vs. raw base energy — the central mismatch
&lt;/h3>&lt;p>PhysicsProperties provides two base-energy evaluations:&lt;/p>
&lt;ul>
&lt;li>mixtureBaseInternalRhoE(U) — calls massFractionsVector(U), which repairs negative/overshoot species before computing $\rho E_{base}$.&lt;/li>
&lt;li>mixtureBaseInternalRhoERaw(U) — uses the raw $\rho Y_k$ values directly.&lt;/li>
&lt;/ul>
&lt;p>The Raw version is linear in $U$, so it is attractive for reconstruction algebra. However, gammaEq and the Riemann solver use the clipped version. This created a mismatch: the PP limiter could declare a reconstructed state valid using Raw, but gammaEq would compute a non-positive sensible energy because the clipped composition increased $\rho E_{base}$. The resulting crash was &amp;ldquo;$e_{sensible} \le 0$&amp;rdquo; inside gammaEq.&lt;/p>
&lt;p>&lt;a class="link" href="https://github.com/harryzhou2000/DNDSR/commit/ec3eb0ba83926bed04a220a00d7281e2cfd363f7" target="_blank" rel="noopener"
>ec3eb0ba&lt;/a> fixed this by switching the PP-sensitive calls in CompressInc, CompressRecPart, EvaluateCellRHSAlpha, AssertMeanValuePP, AddFixedIncrement, and EvaluateURecBeta to the clipped mixtureBaseInternalRhoE. EvaluateURecBeta also gained a post-$\theta_P$ guard that recomputes the sensible energy with the clipped base energy and forces a full fallback if it is still invalid.&lt;/p>
&lt;h3 id="42-barth-slope-limiter-and-energy-decay">&lt;a href="#42-barth-slope-limiter-and-energy-decay" class="header-anchor">&lt;/a>4.2 Barth slope limiter and energy decay
&lt;/h3>&lt;p>Species columns also participate in the gradient limiter. &lt;a class="link" href="https://github.com/harryzhou2000/DNDSR/commit/fc4cd0e56eaa3ea6babc59f6c7af4ff060254a1e" target="_blank" rel="noopener"
>fc4cd0e5&lt;/a> corrected a bug in LimiterUGrad where the maximum neighboring state was not accumulated correctly, and added an iterative decay loop that reduces the gradient until both sensible-energy and species positivity are satisfied. The limiter now uses the clipped base energy consistently with gammaEq.&lt;/p>
&lt;h3 id="43-source-jacobian-term">&lt;a href="#43-source-jacobian-term" class="header-anchor">&lt;/a>4.3 Source Jacobian term
&lt;/h3>&lt;p>In SourceTermContributor.hpp, the ChemicalContributor evaluates the chemical source in Mode 0 (residual) but explicitly asserts false in Mode 1 (diagonal source Jacobian):&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;ChemicalContributor: diagonal-Jacobian mode not implemented.&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>For implicit time stepping the reactive source is therefore treated as explicitly or embedded inside the ODE integrator (Strang) rather than being linearized into the Newton/Krylov Jacobian. This is a current limitation for very stiff chemistry.&lt;/p>
&lt;h3 id="44-mixture-induced-rho-e-flux-and-gradient-correction">&lt;a href="#44-mixture-induced-rho-e-flux-and-gradient-correction" class="header-anchor">&lt;/a>4.4 Mixture-induced $\rho E$ flux and gradient correction
&lt;/h3>&lt;p>Because $\rho E_{base}$ varies with composition, the pressure gradient derived from the EOS must be corrected for $\nabla(\rho E_{base})$. Gas::GradientCons2Prim_IdealGas does this:&lt;/p>
$$
\nabla p = (\gamma_{eq} - 1) \Bigl( \nabla(\rho E) - \frac{1}{2}\nabla(\rho |\mathbf{u}|^2) - \nabla(\rho E_{base}) \Bigr),
$$&lt;p>with&lt;/p>
$$
\nabla(\rho E_{base}) = \sum_{k=1}^{N_s-1} (e_{base,k} - e_{base,N_s}) \nabla(\rho Y_k) + e_{base,N_s} \nabla \rho .
$$&lt;p>The inviscid flux itself already carries the total energy through the mass flux, but the gradient/viscous paths need this explicit correction or the pressure and temperature fields become inconsistent where species gradients are strong.&lt;/p>
&lt;h3 id="45-initialization-and-restart-repairs">&lt;a href="#45-initialization-and-restart-repairs" class="header-anchor">&lt;/a>4.5 Initialization and restart repairs
&lt;/h3>&lt;p>&lt;a class="link" href="https://github.com/harryzhou2000/DNDSR/commit/8e2bc4466b43cacee911117bccd8bf0ea2539569" target="_blank" rel="noopener"
>8e2bc446&lt;/a> made initialization and restart loading apply the same simplex repair that is used during time stepping. Without this, interpolated/extrapolated initial fields could enter the first RHS evaluation with negative species masses and crash immediately. A focused MPI regression test (test_EulerEvaluatorReactive.cpp) now covers this at 1, 2, 4, and 8 ranks.&lt;/p>
&lt;h2 id="5-1-d-premixed-h2air-flame-test">&lt;a href="#5-1-d-premixed-h2air-flame-test" class="header-anchor">&lt;/a>5. 1-D premixed H2/air flame test
&lt;/h2>&lt;p>The flame case uses the stoichiometric H2/air mechanism h2o2.yaml with mixture-averaged transport. Cantera gives a reference laminar flame speed $S_u = 2.2540$ m/s and a burned temperature of 2360.02 K.&lt;/p>
&lt;p>Configuration highlights:&lt;/p>
&lt;ul>
&lt;li>Mesh: Uniform_01_400.cgns, 400 cells, 2 cm domain, $\Delta x = 0.05$ mm.&lt;/li>
&lt;li>BCIn on both ends: burned products on the left, unburned reactants on the right.&lt;/li>
&lt;li>Initial: tanh profile centered at $x = 5$ mm, width 1 mm.&lt;/li>
&lt;li>Time integrator: ESDIRK2, CFL 10, 200 pseudo-time steps per physical step.&lt;/li>
&lt;li>Limiter: PP reconstruction limiter enabled from step 0.&lt;/li>
&lt;li>MPI: 8 ranks.&lt;/li>
&lt;/ul>
&lt;h3 id="physical-setup">&lt;a href="#physical-setup" class="header-anchor">&lt;/a>Physical setup
&lt;/h3>&lt;p>The flame run is driven by &lt;a class="link" href="https://github.com/harryzhou2000/DNDSR/blob/91630810/cases/eulerEX/config_1d_premixed_stoichiometric.json" target="_blank" rel="noopener"
>&lt;code>config_1d_premixed_stoichiometric.json&lt;/code>&lt;/a> in the DNDSR repository.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Mechanism:&lt;/strong> &lt;code>h2o2.yaml&lt;/code> with Cantera mixture-averaged viscosity, conductivity, and species diffusivity.&lt;/li>
&lt;li>&lt;strong>Reactants (right side):&lt;/strong> stoichiometric H2/air, $T_u = 300$ K, $p = 101325$ Pa, $Y_{H_2}=0.028$, $Y_{O_2}=0.222$, $Y_{N_2}=0.75$ (derived by closure).&lt;/li>
&lt;li>&lt;strong>Products (left side):&lt;/strong> Cantera free-flame outlet, $T_b = 2358.89$ K, $p = 101325$ Pa.&lt;/li>
&lt;li>&lt;strong>Domain:&lt;/strong> 2 cm, &lt;code>Uniform_01_400.cgns&lt;/code> with &lt;code>meshScale = 0.02&lt;/code>, giving $\Delta x = 0.05$ mm.&lt;/li>
&lt;li>&lt;strong>Boundaries:&lt;/strong> BCIn on both ends, burned products on the left and unburned reactants on the right.&lt;/li>
&lt;li>&lt;strong>Initial field:&lt;/strong> tanh profile centered at $x = 5$ mm with width 1 mm.&lt;/li>
&lt;li>&lt;strong>Time marching:&lt;/strong> ESDIRK2, CFL 10, 200 internal pseudo-time steps per physical step (&lt;code>sourceStrangSplitting = 1&lt;/code>).&lt;/li>
&lt;/ul>
&lt;p>The front is tracked by the midpoint-temperature crossing ($T_{mid} \approx 1330$ K), and the flame speed is recovered as&lt;/p>
$$
S_u = \dot{x}_{front} - u_{unburned},
$$&lt;p>because the BCIn boundaries do not represent a free flame.&lt;/p>
&lt;p>&lt;img src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/front_marker_time.png"
loading="lazy"
alt="Front marker positions for the six flame runs"
>&lt;/p>
&lt;h3 id="results">&lt;a href="#results" class="header-anchor">&lt;/a>Results
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Method&lt;/th>
&lt;th>$\Delta t$&lt;/th>
&lt;th>$S_u$&lt;/th>
&lt;th>vs. Cantera&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Non-Strang&lt;/td>
&lt;td>1e-3&lt;/td>
&lt;td>2.46 m/s&lt;/td>
&lt;td>1.09×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Non-Strang&lt;/td>
&lt;td>2e-3&lt;/td>
&lt;td>2.41 m/s&lt;/td>
&lt;td>1.07×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Non-Strang R1&lt;/td>
&lt;td>2e-3&lt;/td>
&lt;td>2.54 m/s&lt;/td>
&lt;td>1.13×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Strang&lt;/td>
&lt;td>1e-3&lt;/td>
&lt;td>2.66 m/s&lt;/td>
&lt;td>1.18×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Strang&lt;/td>
&lt;td>2e-3&lt;/td>
&lt;td>2.97 m/s&lt;/td>
&lt;td>1.32×&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Strang R1&lt;/td>
&lt;td>2e-3&lt;/td>
&lt;td>3.06 m/s&lt;/td>
&lt;td>1.36×&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Observations:&lt;/p>
&lt;ul>
&lt;li>The fully coupled (non-Strang) scheme agrees with Cantera to within 7–13 % on this coarse mesh.&lt;/li>
&lt;li>Strang splitting systematically over-predicts $S_u$, and the error grows with $\Delta t$, consistent with an $O(\Delta t^2)$ splitting error.&lt;/li>
&lt;li>Repeat runs are consistent, but the 2 cm domain is too short; the fit window is restricted to $t_{code} &amp;lt; 0.6$ to avoid boundary influence.&lt;/li>
&lt;/ul>
&lt;h2 id="6-1-d-h2o2-detonation-test">&lt;a href="#6-1-d-h2o2-detonation-test" class="header-anchor">&lt;/a>6. 1-D H2/O2 detonation test
&lt;/h2>&lt;p>The detonation case uses the same h2o2.yaml mechanism. A Cantera CJ analysis gives $U_{CJ} = 2836.4$ m/s with an induction length of about 50 $\mu$m.&lt;/p>
&lt;p>Configuration highlights:&lt;/p>
&lt;ul>
&lt;li>Mesh: Uniform_01_5000.cgns, 5000 cells, 5 cm domain, $\Delta x = 10$ $\mu$m (5 cells across the induction zone).&lt;/li>
&lt;li>Left wall: BCWallInvis; right inflow: BCIn with unburned H2/O2 at rest.&lt;/li>
&lt;li>Initial spark: $T = 3500$ K, $p = 20$ bar in $x &amp;lt; 1$ mm.&lt;/li>
&lt;li>MPI: 16 ranks.&lt;/li>
&lt;/ul>
&lt;h3 id="physical-setup-1">&lt;a href="#physical-setup-1" class="header-anchor">&lt;/a>Physical setup
&lt;/h3>&lt;p>The detonation run is driven by &lt;a class="link" href="https://github.com/harryzhou2000/DNDSR/blob/91630810/cases/eulerEX/config_1d_detonation.json" target="_blank" rel="noopener"
>&lt;code>config_1d_detonation.json&lt;/code>&lt;/a> in the DNDSR repository.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Mechanism:&lt;/strong> &lt;code>h2o2.yaml&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Reactants:&lt;/strong> stoichiometric H2/O2, $T = 300$ K, $p = 101325$ Pa, $Y_{H_2}=0.1111$, $Y_{O_2}=0.8889$ (no N2 filler).&lt;/li>
&lt;li>&lt;strong>Spark (left of $x = 1$ mm):&lt;/strong> $T = 3500$ K, $p = 20$ bar, CJ-equilibrium composition ($Y_{H_2O}=0.487$, $Y_{OH}=0.229$, $Y_{O_2}=0.137$, $Y_O=0.103$, $Y_{H_2}=0.031$, $Y_H=0.013$).&lt;/li>
&lt;li>&lt;strong>Domain:&lt;/strong> 5 cm, &lt;code>Uniform_01_5000.cgns&lt;/code> with &lt;code>meshScale = 0.05&lt;/code>, giving $\Delta x = 10$ $\mu$m (about 5 cells across the induction zone).&lt;/li>
&lt;li>&lt;strong>Boundaries:&lt;/strong> left BCWallInvis (reflecting, adiabatic), right BCIn with unburned H2/O2 at rest.&lt;/li>
&lt;li>&lt;strong>Time marching:&lt;/strong> non-Strang coupled (&lt;code>sourceStrangSplitting = 0&lt;/code>), $\Delta t = 4 \times 10^{-6}$, CFL 10, 100 internal pseudo-time steps.&lt;/li>
&lt;/ul>
&lt;p>&lt;img src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/detonation_speed_final.png"
loading="lazy"
alt="Shock-front position vs. time for the four detonation runs"
>&lt;/p>
&lt;h3 id="results-1">&lt;a href="#results-1" class="header-anchor">&lt;/a>Results
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Method&lt;/th>
&lt;th>$\Delta t$&lt;/th>
&lt;th>Simulated $U$&lt;/th>
&lt;th>Error vs. CJ&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Coupled&lt;/td>
&lt;td>1e-6&lt;/td>
&lt;td>2842.5 m/s&lt;/td>
&lt;td>+0.21 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Strang&lt;/td>
&lt;td>1e-6&lt;/td>
&lt;td>2842.5 m/s&lt;/td>
&lt;td>+0.21 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Coupled&lt;/td>
&lt;td>4e-6&lt;/td>
&lt;td>2908.8 m/s&lt;/td>
&lt;td>+2.55 %&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Strang&lt;/td>
&lt;td>4e-6&lt;/td>
&lt;td>2842.5 m/s&lt;/td>
&lt;td>+0.21 %&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Observations:&lt;/p>
&lt;ul>
&lt;li>At the fine time step both coupled and Strang reproduce the CJ speed to better than 0.25 %.&lt;/li>
&lt;li>At $\Delta t = 4\times10^{-6}$ the coupled scheme drifts +2.55 %, while Strang stays at +0.21 %. For this problem the Strang splitting error is smaller than the coupled temporal truncation error at large steps.&lt;/li>
&lt;li>Strang is also cheaper (2h47m vs. 3h05m wall time at $\Delta t = 4\times10^{-6}$).&lt;/li>
&lt;li>The ZND structure is clearly visible: shock front → induction zone → reaction zone → expansion products.&lt;/li>
&lt;/ul>
&lt;h2 id="7-2-d-h2o2-detonation-test">&lt;a href="#7-2-d-h2o2-detonation-test" class="header-anchor">&lt;/a>7. 2-D H2/O2 detonation test
&lt;/h2>&lt;p>To exercise the reactive solver in two dimensions we initialize a ZND detonation profile and propagate it in a shock-attached frame. The same base configuration, &lt;a class="link" href="https://github.com/harryzhou2000/DNDSR/blob/91630810/cases/eulerEX/config_2d_detonation_largeS1.json" target="_blank" rel="noopener"
>&lt;code>config_2d_detonation_largeS1.json&lt;/code>&lt;/a>, is run at two geometric scales: 1.0× and 0.3×.&lt;/p>
&lt;h3 id="physical-setup-2">&lt;a href="#physical-setup-2" class="header-anchor">&lt;/a>Physical setup
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Mechanism:&lt;/strong> &lt;code>h2o2.yaml&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Unburned state:&lt;/strong> $T = 300$ K, $p = 6667$ Pa, $Y_{H_2}=0.01277$, $Y_{O_2}=0.10136$, $Y_{N_2}=0.88587$ (dilute stoichiometric H2/O2, H2:O2:Ar = 2:1:7).&lt;/li>
&lt;li>&lt;strong>Initial condition:&lt;/strong> a 1-D CJ/ZND profile mapped into the 2-D domain; a small transverse-velocity perturbation is added to seed instability. The shock-attached inflow velocity is $D \approx 1616.6$ m/s.&lt;/li>
&lt;li>&lt;strong>Boundaries:&lt;/strong> periodic in $y$; BCIn on the left (post-shock products) and on the right (unburned inflow), both in the shock-attached frame.&lt;/li>
&lt;li>&lt;strong>Time marching:&lt;/strong> non-Strang coupled (&lt;code>sourceStrangSplitting = 0&lt;/code>) with Roe-M9 (&lt;code>Roe_M9&lt;/code>), CFL ramping 1 to 10 and the PP reconstruction limiter enabled. Base runs use 40 internal pseudo-time steps ($\Delta t = 4 \times 10^{-5}$ at 1.0× and $\Delta t = 2 \times 10^{-5}$ at 0.3×); the O4 restart runs use &lt;code>nInternalRecStep = 4&lt;/code> and 20 internal pseudo-time steps.&lt;/li>
&lt;li>&lt;strong>Output:&lt;/strong> VTK-HDF cell snapshots every 20 physical steps; HDF5 restart files every 100 physical steps.&lt;/li>
&lt;/ul>
&lt;p>The two runs use the same two-zone mesh file &lt;code>Uniform_5x2-10x2_1000.cgns&lt;/code>: a coarse 10×2 inflow block on the left ($x \in [-1.0, 0]$ at 1.0× scale) and the major (fine) 5×2 detonation block on the right ($x \in [0, 0.5]$ at 1.0× scale), so the joined physical domain is 15×2 (width×height) before scaling (1.5 m × 0.2 m at 1.0×; 0.45 m × 0.06 m at 0.3×). The videos render the right-hand 5×2 detonation block; $x$ in the frames is relative to the left boundary of that block.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Scale&lt;/th>
&lt;th style="text-align: right">Mesh scale&lt;/th>
&lt;th style="text-align: right">Right-block $x$ range&lt;/th>
&lt;th style="text-align: right">$y$ range&lt;/th>
&lt;th style="text-align: right">Frame $x_{\text{shock}}$&lt;/th>
&lt;th>Purpose&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>1.0×&lt;/td>
&lt;td style="text-align: right">0.10&lt;/td>
&lt;td style="text-align: right">0.0 – 0.5 m&lt;/td>
&lt;td style="text-align: right">0 – 0.2 m&lt;/td>
&lt;td style="text-align: right">0.4 m&lt;/td>
&lt;td>Larger domain, longer transverse-wavelength development&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.3×&lt;/td>
&lt;td style="text-align: right">0.03&lt;/td>
&lt;td style="text-align: right">0.0 – 0.15 m&lt;/td>
&lt;td style="text-align: right">0 – 0.06 m&lt;/td>
&lt;td style="text-align: right">0.12 m&lt;/td>
&lt;td>Smaller, cheaper domain used for stability screening&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="znd-reference-profile">&lt;a href="#znd-reference-profile" class="header-anchor">&lt;/a>ZND reference profile
&lt;/h3>&lt;p>The one-dimensional CJ/ZND profile used to initialize the two-dimensional runs is computed with the DNDSR &lt;code>cj-detonation&lt;/code> skill (SDToolbox, Cantera &lt;code>h2o2.yaml&lt;/code>) for the diluted 2:1:7 mixture at $T_1 = 300$ K, $p_1 = 6667$ Pa. The minimum wave speed solution gives $U_{CJ} = 1616.6$ m/s with an induction length of 1.51 mm, and the profile below is plotted in the shock-fixed frame.&lt;/p>
&lt;p>&lt;img src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/znd_dilute_profile.png"
loading="lazy"
alt="ZND profile for the diluted 2:1:7 mixture at 300 K and 6667 Pa"
>&lt;/p>
&lt;h3 id="10-scale-results">&lt;a href="#10-scale-results" class="header-anchor">&lt;/a>1.0× scale results
&lt;/h3>&lt;p>The base coupled run is RM9EFIX (&lt;code>Roe_M9&lt;/code> with &lt;code>nInternalRecStep = 1&lt;/code>); the restart group (&lt;code>O4-restartFromO2&lt;/code>) advances with four reconstruction stages per internal step (&lt;code>nInternalRecStep = 4&lt;/code>).&lt;/p>
&lt;p>&lt;strong>Base run — pressure&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-1x/out-T0-coupled-RM9EFIX_P.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>Base run — density&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-1x/out-T0-coupled-RM9EFIX_R.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>Base run — temperature&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-1x/out-T0-coupled-RM9EFIX_T.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>Base run — H2O mass fraction&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-1x/out-T0-coupled-RM9EFIX_Y_H2O.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>O4-restartFromO2 — pressure&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-1x/out-T0-coupled-RM9EFIX-O4-restartFromO2_P.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>O4-restartFromO2 — density&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-1x/out-T0-coupled-RM9EFIX-O4-restartFromO2_R.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>O4-restartFromO2 — temperature&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-1x/out-T0-coupled-RM9EFIX-O4-restartFromO2_T.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>O4-restartFromO2 — H2O mass fraction&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-1x/out-T0-coupled-RM9EFIX-O4-restartFromO2_Y_H2O.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;h3 id="03-scale-results">&lt;a href="#03-scale-results" class="header-anchor">&lt;/a>0.3× scale results
&lt;/h3>&lt;p>The 0.3× case uses the same numerical settings on the scaled domain; the restart group (&lt;code>O4-restart4000&lt;/code>) continues from step 4000 with four reconstruction stages per internal step.&lt;/p>
&lt;p>&lt;strong>Base run — pressure&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-03x/out-T0-coupled-RM9EFIX_P.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>Base run — density&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-03x/out-T0-coupled-RM9EFIX_R.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>Base run — temperature&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-03x/out-T0-coupled-RM9EFIX_T.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>Base run — H2O mass fraction&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-03x/out-T0-coupled-RM9EFIX_Y_H2O.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>O4-restart4000 — pressure&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-03x/out-T0-coupled-RM9EFIX-O4-restart4000_P.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>O4-restart4000 — density&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-03x/out-T0-coupled-RM9EFIX-O4-restart4000_R.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>O4-restart4000 — temperature&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-03x/out-T0-coupled-RM9EFIX-O4-restart4000_T.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>O4-restart4000 — H2O mass fraction&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-03x/out-T0-coupled-RM9EFIX-O4-restart4000_Y_H2O.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>The same O4 restart is also run with Strang splitting (sourceStrangSplitting = 1) instead of the coupled scheme.&lt;/p>
&lt;p>&lt;strong>Strang O4-restart4000 — pressure&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-03x/out-T0-strang-RM9EFIX-O4-restart4000_P.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>Strang O4-restart4000 — density&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-03x/out-T0-strang-RM9EFIX-O4-restart4000_R.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>Strang O4-restart4000 — temperature&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-03x/out-T0-strang-RM9EFIX-O4-restart4000_T.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;p>&lt;strong>Strang O4-restart4000 — H2O mass fraction&lt;/strong>
&lt;video controls width="100%">&lt;source src="https://raw.githubusercontent.com/harryzhou2000/resources-0/main/2026/dndsr-reaction-experiments/videos/2d-detonation-03x/out-T0-strang-RM9EFIX-O4-restart4000_Y_H2O.mp4" type="video/mp4">&lt;/video>&lt;/p>
&lt;h2 id="8-takeaways">&lt;a href="#8-takeaways" class="header-anchor">&lt;/a>8. Takeaways
&lt;/h2>&lt;ol>
&lt;li>The sensible-energy split with a species-dependent $\rho E_{base}$ is essential for positivity-preserving reactive flow, but it introduces a clipped vs. raw bookkeeping mismatch that must be tracked consistently through the limiter, the Riemann solver, and the source term.&lt;/li>
&lt;li>Species positivity has two stages: mean-state simplex assertions and reconstruction quadrature checks. Repairing mass fractions should only touch temporary buffers passed to Cantera.&lt;/li>
&lt;li>The reactive source Jacobian is not yet assembled for implicit linear solvers; stiff chemistry still relies on the ODE/Strang path.&lt;/li>
&lt;li>On the tested 1-D problems the implementation is accurate enough for engineering validation (flame speed within 10 %, detonation speed within 0.25 %), but the choice between coupled and Strang is problem-dependent rather than uniformly superior.&lt;/li>
&lt;/ol></description></item></channel></rss>