cantera-combustion

star 29

Use this Skill for combustion simulations with Cantera: mechanism loading, freely propagating flames, ignition delay, reactors, and sensitivity analysis.

xjtulyc By xjtulyc schedule Updated 3/18/2026

name: cantera-combustion description: > Use this Skill for combustion simulations with Cantera: mechanism loading, freely propagating flames, ignition delay, reactors, and sensitivity analysis. tags:

  • engineering
  • combustion
  • cantera
  • chemical-kinetics
  • thermodynamics version: "1.0.0" authors:
  • name: Rosetta Skills Contributors github: "@xjtulyc" license: "MIT" platforms:
  • claude-code
  • codex
  • gemini-cli
  • cursor dependencies: python:
    • cantera>=3.0
    • numpy>=1.24
    • matplotlib>=3.7
    • pandas>=2.0
    • scipy>=1.11 last_updated: "2026-03-17" status: "stable"

Cantera Combustion Simulation

One-line summary: Simulate combustion processes with Cantera: load reaction mechanisms, compute adiabatic flame temperatures, ignition delays, laminar flame speeds, and reactor sensitivity analysis.


When to Use This Skill

  • When computing adiabatic flame temperature for fuel-air mixtures
  • When calculating laminar burning velocity of a fuel
  • When simulating ignition delay times for engine/safety applications
  • When running perfectly stirred reactor (PSR) simulations
  • When performing sensitivity analysis of reaction mechanisms
  • When comparing GRI-3.0, YAML, and custom mechanisms

Trigger keywords: Cantera, combustion, flame speed, ignition delay, adiabatic flame temperature, reaction mechanism, GRI-3.0, PSR, chemical kinetics, equivalence ratio


Background & Key Concepts

Equivalence Ratio

$$ \phi = \frac{(F/A)\text{actual}}{(F/A)\text{stoichiometric}} $$

$\phi < 1$: lean mixture; $\phi = 1$: stoichiometric; $\phi > 1$: rich mixture.

Adiabatic Flame Temperature

Maximum temperature for complete combustion at constant enthalpy:

$$ h_\text{reactants}(T_0) = h_\text{products}(T_\text{ad}) $$

Laminar Burning Velocity

The speed at which an unburnt gas mixture is consumed by a propagating flame. Key parameter for:

  • Engine knock modeling
  • Fire safety assessment
  • Turbulent combustion modeling (closure)

Reaction Mechanism

A set of elementary reactions with rate coefficients $k = A T^n e^{-E_a/RT}$ (modified Arrhenius).


Environment Setup

Install Dependencies

pip install cantera>=3.0 numpy>=1.24 matplotlib>=3.7 pandas>=2.0 scipy>=1.11

Verify Installation

import cantera as ct
print(f"Cantera version: {ct.__version__}")

# Test: load GRI-3.0 methane mechanism
gas = ct.Solution("gri30.yaml")
print(f"GRI-3.0: {gas.n_species} species, {gas.n_reactions} reactions")
# Expected: 53 species, 325 reactions

Core Workflow

Step 1: Adiabatic Flame Temperature

import cantera as ct
import numpy as np
import matplotlib.pyplot as plt

def adiabatic_flame_temperature(fuel, oxidizer="air", phi_range=None, mechanism="gri30.yaml",
                                T_initial=300.0, P=101325.0):
    """
    Compute adiabatic flame temperature over a range of equivalence ratios.

    Parameters
    ----------
    fuel : str
        Fuel species name (e.g., "CH4", "H2", "C2H5OH")
    oxidizer : str
        "air" or specific composition string
    phi_range : array-like
        Equivalence ratios to sweep
    mechanism : str
        Cantera mechanism file (.yaml or .cti)

    Returns
    -------
    pd.DataFrame with phi, T_adiabatic, and selected species
    """
    if phi_range is None:
        phi_range = np.arange(0.5, 2.01, 0.05)

    gas = ct.Solution(mechanism)
    results = []

    for phi in phi_range:
        try:
            gas.set_equivalence_ratio(phi, fuel, oxidizer)
            gas.TP = T_initial, P

            # Equilibrate at constant enthalpy and pressure (adiabatic combustion)
            gas.equilibrate("HP")

            results.append({
                "phi": phi,
                "T_adiabatic_K": gas.T,
                "X_CO2": gas["CO2"].X[0],
                "X_CO":  gas["CO"].X[0],
                "X_H2O": gas["H2O"].X[0],
                "X_O2":  gas["O2"].X[0],
            })
        except ct.CanteraError as e:
            print(f"  phi={phi:.2f}: {e}")

    import pandas as pd
    df = pd.DataFrame(results)
    print(f"\nAFT for {fuel}/{oxidizer}:")
    print(f"  Max T_ad = {df['T_adiabatic_K'].max():.0f} K at φ = {df.loc[df['T_adiabatic_K'].idxmax(), 'phi']:.2f}")
    return df

# Compare methane and hydrogen
fig, axes = plt.subplots(2, 1, figsize=(10, 8))

for fuel, color, label in [("CH4", "b", "Methane (GRI-3.0)"),
                             ("H2",  "r", "Hydrogen (GRI-3.0)")]:
    aft_df = adiabatic_flame_temperature(fuel, phi_range=np.arange(0.5, 2.01, 0.05))

    axes[0].plot(aft_df["phi"], aft_df["T_adiabatic_K"], f"{color}-o",
                 ms=4, label=label)
    axes[1].plot(aft_df["phi"], aft_df["X_CO2"] * 100, f"{color}--", label=f"CO₂ ({label[:4]})")
    axes[1].plot(aft_df["phi"], aft_df["X_CO"] * 100, f"{color}:", label=f"CO ({label[:4]})")

axes[0].axvline(1.0, color='k', linestyle='--', linewidth=0.8, label="φ=1 (stoich)")
axes[0].set_xlabel("Equivalence ratio φ"); axes[0].set_ylabel("T_adiabatic (K)")
axes[0].set_title("Adiabatic Flame Temperature"); axes[0].legend()

axes[1].set_xlabel("Equivalence ratio φ"); axes[1].set_ylabel("Mole fraction (%)")
axes[1].set_title("Product Species Mole Fractions"); axes[1].legend(fontsize=8)

plt.tight_layout()
plt.savefig("adiabatic_flame_temperature.png", dpi=150)
plt.show()

Step 2: Ignition Delay Time

import cantera as ct
import numpy as np
import matplotlib.pyplot as plt

def compute_ignition_delay(fuel, phi=1.0, T_range=None, P=20*101325,
                            mechanism="gri30.yaml"):
    """
    Compute ignition delay time via constant-volume homogeneous reactor.

    Parameters
    ----------
    T_range : array-like
        Initial temperatures (K) to sweep (Arrhenius plot)

    Returns
    -------
    dict: T_K → tau_ign_ms
    """
    if T_range is None:
        T_range = np.arange(1000, 1800, 50)

    gas = ct.Solution(mechanism)
    results = []

    for T0 in T_range:
        gas.set_equivalence_ratio(phi, fuel, "air")
        gas.TP = T0, P
        reactor = ct.IdealGasReactor(gas)
        net = ct.ReactorNet([reactor])

        # Integrate until OH peak (ignition criterion)
        t_end = 0.01  # 10 ms max
        dt = 1e-6
        t, OH_prev = 0.0, gas["OH"].X[0]
        t_ign = None

        while t < t_end:
            t = net.advance(t + dt)
            OH_cur = reactor.thermo["OH"].X[0]
            if OH_cur > 10 * OH_prev and OH_cur > 1e-4:
                t_ign = t
                break
            OH_prev = max(OH_cur, OH_prev)

        tau = (t_ign * 1000) if t_ign else np.nan
        results.append({"T_K": T0, "tau_ign_ms": tau})

    import pandas as pd
    return pd.DataFrame(results).dropna()

# Methane ignition delay
tau_df = compute_ignition_delay("CH4", phi=1.0,
                                 T_range=np.arange(1000, 1700, 50),
                                 P=20*101325)

fig, ax = plt.subplots(figsize=(8, 5))
ax.semilogy(1000 / tau_df["T_K"], tau_df["tau_ign_ms"], 'bs-', ms=6)
ax.set_xlabel("1000/T (K⁻¹)")
ax.set_ylabel("Ignition delay τ (ms)")
ax.set_title("Methane Ignition Delay Time (φ=1.0, P=20 atm)")
ax.grid(True, which="both", alpha=0.3)
plt.tight_layout()
plt.savefig("ignition_delay.png", dpi=150)
plt.show()

print("Ignition delays:")
print(tau_df.to_string(index=False))

Step 3: Laminar Flame Speed

import cantera as ct
import numpy as np
import matplotlib.pyplot as plt

def laminar_flame_speed(fuel, phi_range=None, T0=300, P=101325, mechanism="gri30.yaml"):
    """
    Compute laminar burning velocity using 1D freely propagating flame.

    Returns
    -------
    pd.DataFrame: phi, Su_cm_s (laminar flame speed in cm/s)
    """
    if phi_range is None:
        phi_range = np.arange(0.7, 1.41, 0.1)

    results = []
    for phi in phi_range:
        gas = ct.Solution(mechanism)
        gas.set_equivalence_ratio(phi, fuel, "air")
        gas.TP = T0, P

        flame = ct.FreeFlame(gas, width=0.03)  # 3 cm domain
        flame.set_refine_criteria(ratio=3, slope=0.06, curve=0.12)
        flame.set_max_jac_age(50, 50)
        flame.solve(loglevel=0, auto=True)

        Su = flame.velocity[0] * 100  # m/s → cm/s
        results.append({"phi": phi, "Su_cm_s": Su})
        print(f"  φ={phi:.1f}: Su={Su:.1f} cm/s")

    import pandas as pd
    return pd.DataFrame(results)

print("Computing methane laminar flame speeds...")
flame_df = laminar_flame_speed("CH4", phi_range=[0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3])

fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(flame_df["phi"], flame_df["Su_cm_s"], 'ro-', ms=8, linewidth=2)
ax.set_xlabel("Equivalence ratio φ")
ax.set_ylabel("Laminar burning velocity Su (cm/s)")
ax.set_title("Methane/Air Laminar Flame Speed")
ax.set_ylim(0)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("laminar_flame_speed.png", dpi=150)
plt.show()

peak_phi = flame_df.loc[flame_df["Su_cm_s"].idxmax(), "phi"]
peak_Su  = flame_df["Su_cm_s"].max()
print(f"\nPeak Su = {peak_Su:.1f} cm/s at φ = {peak_phi:.1f}")

Advanced Usage

Perfectly Stirred Reactor (PSR) and Sensitivity Analysis

import cantera as ct
import numpy as np
import matplotlib.pyplot as plt

def psr_residence_time_sweep(fuel, phi=1.0, T0=800, P=101325,
                              tau_range=None, mechanism="gri30.yaml"):
    """
    Sweep residence times in a PSR to find extinction.
    """
    if tau_range is None:
        tau_range = np.logspace(-4, 0, 30)  # 0.1 ms to 1 s

    gas = ct.Solution(mechanism)
    results = []

    for tau in tau_range:
        gas.set_equivalence_ratio(phi, fuel, "air")
        gas.TP = T0, P

        reactor = ct.IdealGasReactor(gas)
        upstream = ct.Reservoir(gas)
        exhaust = ct.Reservoir(gas)

        intake_mfc = ct.MassFlowController(upstream, reactor)
        intake_mfc.mass_flow_rate = reactor.mass / tau

        ct.PressureController(reactor, exhaust, master=intake_mfc)
        net = ct.ReactorNet([reactor])

        try:
            net.advance_to_steady_state()
            results.append({"tau_s": tau, "T_K": reactor.T, "X_CO2": reactor.thermo["CO2"].X[0]})
        except Exception:
            break

    import pandas as pd
    df = pd.DataFrame(results)
    extinction_tau = df[df["T_K"] < T0 + 100]["tau_s"].min() if not df.empty else None
    print(f"PSR extinction residence time: {extinction_tau:.4f} s" if extinction_tau else "No extinction")
    return df

psr_df = psr_residence_time_sweep("CH4")

fig, ax = plt.subplots(figsize=(8, 5))
ax.semilogx(psr_df["tau_s"] * 1000, psr_df["T_K"], 'b-o', ms=5)
ax.set_xlabel("Residence time τ (ms)")
ax.set_ylabel("Reactor temperature T (K)")
ax.set_title("PSR S-curve: CH4/Air, φ=1.0")
ax.grid(True, which="both", alpha=0.3)
plt.tight_layout()
plt.savefig("psr_s_curve.png", dpi=150)
plt.show()

Troubleshooting

Error: cantera._cantera.CanteraError: mechanism file not found

Fix:

import cantera as ct
# List available built-in mechanisms
import os
print([f for f in os.listdir(ct.get_data_path()) if f.endswith(".yaml")])
# Common: gri30.yaml, h2o2.yaml, air.yaml, nDodecane_Reitz.yaml

Issue: Flame solver doesn't converge

Fix:

# Use wider domain and coarser initial grid
flame = ct.FreeFlame(gas, width=0.05)
flame.set_refine_criteria(ratio=5, slope=0.2, curve=0.3)
flame.solve(loglevel=0, auto=True)

Version Compatibility

Package Tested versions Known issues
cantera 3.0, 3.0.1 YAML mechanism format preferred over CTI

External Resources

Official Documentation

Key Papers

  • Goodwin, D.G. et al. (2023). Cantera: An object-oriented software toolkit for chemical kinetics, thermodynamics, and transport processes. Zenodo.

Examples

Example 1: NOx Formation vs. Equivalence Ratio

# =============================================
# NOx equilibrium concentration vs. phi
# =============================================
import cantera as ct, numpy as np, matplotlib.pyplot as plt

gas = ct.Solution("gri30.yaml")
phi_arr = np.linspace(0.6, 1.4, 30)
NOx_ppm = []

for phi in phi_arr:
    gas.set_equivalence_ratio(phi, "CH4", "air")
    gas.TP = 300, 101325
    gas.equilibrate("HP")
    NO  = gas["NO"].X[0]
    NO2 = gas["NO2"].X[0]
    NOx_ppm.append((NO + NO2) * 1e6)

fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(phi_arr, NOx_ppm, 'r-o', ms=5)
ax.set_xlabel("Equivalence ratio φ")
ax.set_ylabel("NOx (ppm)")
ax.set_title("Equilibrium NOx vs. Equivalence Ratio (CH4/air)")
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("nox_vs_phi.png", dpi=150)
plt.show()
print(f"Peak NOx: {max(NOx_ppm):.0f} ppm at φ = {phi_arr[np.argmax(NOx_ppm)]:.2f}")

Interpreting these results: NOx peaks near stoichiometric conditions due to the high adiabatic flame temperature. Lean or rich operation reduces NOx — the basis of lean-premix combustors.


Last updated: 2026-03-17 | Maintainer: @xjtulyc Issues: GitHub Issues

Install via CLI
npx skills add https://github.com/xjtulyc/awesome-rosetta-skills --skill cantera-combustion
Repository Details
star Stars 29
call_split Forks 6
navigation Branch main
article Path SKILL.md
More from Creator