batch_reactor_ignition_delay_NTC.ipynb (Source)

Data files used in this example: seiser.yaml

Ignition delay computation

In this example, we will illustrate how to setup and use a constant volume batch reactor. This reactor will then be used to compute the ignition delay of a gas at any temperature and pressure.

The reactor (system) is simply an insulated box.

In [1]:
import pandas as pd
import numpy as np

import time

import cantera as ct

print(f"Runnning Cantera version: {ct.__version__}")
Runnning Cantera version: 3.0.0

Import modules and set plotting defaults

In [2]:
%matplotlib inline
%config InlineBackend.figure_formats = ["svg"]
import matplotlib.pyplot as plt

plt.rcParams["axes.labelsize"] = 18
plt.rcParams["xtick.labelsize"] = 12
plt.rcParams["ytick.labelsize"] = 12
plt.rcParams["figure.autolayout"] = True
plt.rcParams["figure.dpi"] = 120

plt.style.use("ggplot")

Define the gas

In this example we will choose n-heptane as the gas. For a representative kinetic model, we use the 160 species mechanism by Seier et al. 2000, Proc. Comb. Inst.

In [3]:
gas = ct.Solution("../data/seiser.yaml")
C:\Users\Niclas\AppData\Local\Temp\ipykernel_2336\3597652223.py:1: UserWarning: NasaPoly2::validate: 
For species c7h15o-1, discontinuity in h/RT detected at Tmid = 1391
	Value computed using low-temperature polynomial:  21.83428060093267
	Value computed using high-temperature polynomial: 21.76699500201913

  gas = ct.Solution("../data/seiser.yaml")

Define reactor conditions : temperature, pressure, fuel, stoichiometry

In [4]:
# Define the reactor temperature and pressure
reactor_temperature = 1000  # Kelvin
reactor_pressure = 101325  # Pascals

gas.TP = reactor_temperature, reactor_pressure

# Define the fuel, oxidizer and set the stoichiometry
gas.set_equivalence_ratio(phi=1.0, fuel="nc7h16", oxidizer={"o2": 1.0, "n2": 3.76})

# Create a batch reactor object and add it to a reactor network
# In this example, the batch reactor will be the only reactor
# in the network
r = ct.IdealGasReactor(contents=gas, name="Batch Reactor")
reactor_network = ct.ReactorNet([r])

# use the above list to create a DataFrame
time_history = ct.SolutionArray(gas, extra="t")

Define useful functions

In [5]:
def ignition_delay(states, species):
    """
    This function computes the ignition delay from the occurence of the
    peak in species' concentration.
    """
    i_ign = states(species).Y.argmax()
    return states.t[i_ign]
In [6]:
reference_species = "oh"

# Tic
t0 = time.time()

# This is a starting estimate. If you do not get an ignition within this time, increase it
estimated_ignition_delay_time = 0.1
t = 0

counter = 1
while t < estimated_ignition_delay_time:
    t = reactor_network.step()
    if not counter % 10:
        # We will save only every 10th value. Otherwise, this takes too long
        # Note that the species concentrations are mass fractions
        time_history.append(r.thermo.state, t=t)
    counter += 1

# We will use the 'oh' species to compute the ignition delay
tau = ignition_delay(time_history, reference_species)

# Toc
t1 = time.time()

print(f"Computed Ignition Delay: {tau:.3e} seconds. Took {t1-t0:3.2f}s to compute")

# If you want to save all the data - molefractions, temperature, pressure, etc
# uncomment the next line
# time_history.to_csv("time_history.csv")
Computed Ignition Delay: 3.248e-02 seconds. Took 0.88s to compute

Plot the result

Figure illustrating the definition of ignition delay

In [7]:
plt.figure()
plt.plot(time_history.t, time_history(reference_species).Y, "-o")
plt.xlabel("Time (s)")
plt.ylabel("$Y_{OH}$")

plt.xlim([0, 0.05])
plt.arrow(
    0,
    0.008,
    tau,
    0,
    width=0.0001,
    head_width=0.0005,
    head_length=0.001,
    length_includes_head=True,
    color="r",
    shape="full",
)
plt.annotate(
    r"$Ignition Delay: \tau_{ign}$", xy=(0, 0), xytext=(0.01, 0.0082), fontsize=16
);
2023-11-06T00:19:20.976638image/svg+xmlMatplotlib v3.8.1, https://matplotlib.org/

Illustration : NTC behavior

A common benchmark for a reaction mechanism is its ability to reproduce the Negative Temperature Coefficient behavior. Intuitively, as the temperature of an explosive mixture increases, it should ignite faster. But, under certain conditions, we observe the opposite. This is referred to as NTC behavior. Reproducing experimentally observed NTC behavior is thus an important test for any mechanism. We will do this now by computing and visualizing the ignition delay for a wide range of temperatures

Define the temperatures for which we will run the simulations

In [8]:
# Make a list of all the temperatures we would like to run simulations at
T = np.hstack((np.arange(1800, 900, -100), np.arange(975, 475, -25)))

estimated_ignition_delay_times = np.ones_like(T, dtype=float)

# Make time adjustments for the highest and lowest temperatures. This we do empirically
estimated_ignition_delay_times[:6] = 6 * [0.1]
estimated_ignition_delay_times[-4:-2] = 10
estimated_ignition_delay_times[-2:] = 100

# Now create a SolutionArray out of these
ignition_delays = ct.SolutionArray(
    gas, shape=T.shape, extra={"tau": estimated_ignition_delay_times}
)
ignition_delays.set_equivalence_ratio(
    1.0, fuel="nc7h16", oxidizer={"o2": 1.0, "n2": 3.76}
)
ignition_delays.TP = T, reactor_pressure

Now, what we will do is simply run the code above the plots for each temperature.

In [9]:
for i, state in enumerate(ignition_delays):
    # Setup the gas and reactor
    gas.TPX = state.TPX
    r = ct.IdealGasReactor(contents=gas, name="Batch Reactor")
    reactor_network = ct.ReactorNet([r])

    reference_species_history = []
    time_history = []

    t0 = time.time()

    t = 0
    while t < estimated_ignition_delay_times[i]:
        t = reactor_network.step()
        time_history.append(t)
        reference_species_history.append(gas[reference_species].X[0])

    i_ign = np.array(reference_species_history).argmax()
    tau = time_history[i_ign]
    t1 = time.time()

    print(
        f"Computed Ignition Delay: {tau:.3e} seconds for T={state.T}K. Took {t1 - t0:3.2f}s to compute"
    )

    ignition_delays[i].tau = tau
Computed Ignition Delay: 2.943e-05 seconds for T=3048.1371968673066K. Took 0.62s to compute
Computed Ignition Delay: 3.085e-05 seconds for T=3023.4796537279385K. Took 0.67s to compute
Computed Ignition Delay: 4.082e-05 seconds for T=2998.3778591617374K. Took 0.63s to compute
Computed Ignition Delay: 7.172e-05 seconds for T=2972.8347419918496K. Took 0.60s to compute
Computed Ignition Delay: 1.646e-04 seconds for T=2946.858633180261K. Took 0.62s to compute
Computed Ignition Delay: 4.748e-04 seconds for T=2920.4702945235736K. Took 0.71s to compute
Computed Ignition Delay: 1.632e-03 seconds for T=2893.7088208401537K. Took 0.67s to compute
Computed Ignition Delay: 6.443e-03 seconds for T=2866.624960561075K. Took 0.74s to compute
Computed Ignition Delay: 3.248e-02 seconds for T=2839.2914558923862K. Took 0.78s to compute
Computed Ignition Delay: 5.040e-02 seconds for T=2832.430978134923K. Took 0.85s to compute
Computed Ignition Delay: 7.909e-02 seconds for T=2825.566934469486K. Took 0.84s to compute
Computed Ignition Delay: 1.252e-01 seconds for T=2818.7047474963733K. Took 0.85s to compute
Computed Ignition Delay: 1.983e-01 seconds for T=2811.849413329189K. Took 0.86s to compute
Computed Ignition Delay: 3.057e-01 seconds for T=2805.0055341133316K. Took 0.93s to compute
Computed Ignition Delay: 4.266e-01 seconds for T=2798.177351132977K. Took 0.95s to compute
Computed Ignition Delay: 4.726e-01 seconds for T=2791.3687801293227K. Took 0.95s to compute
Computed Ignition Delay: 3.795e-01 seconds for T=2784.5834564797206K. Took 0.99s to compute
Computed Ignition Delay: 2.443e-01 seconds for T=2777.8247752274706K. Took 1.06s to compute
Computed Ignition Delay: 1.462e-01 seconds for T=2771.0959422413275K. Took 1.10s to compute
Computed Ignition Delay: 9.154e-02 seconds for T=2764.4000290839094K. Took 1.13s to compute
Computed Ignition Delay: 6.427e-02 seconds for T=2757.7400327239848K. Took 1.17s to compute
Computed Ignition Delay: 5.791e-02 seconds for T=2751.118942923986K. Took 1.14s to compute
Computed Ignition Delay: 7.723e-02 seconds for T=2744.5398203117347K. Took 1.19s to compute
Computed Ignition Delay: 1.503e-01 seconds for T=2738.005879744561K. Took 1.16s to compute
Computed Ignition Delay: 3.754e-01 seconds for T=2731.520590755388K. Took 1.38s to compute
Computed Ignition Delay: 1.106e+00 seconds for T=2725.087789024315K. Took 1.34s to compute
Computed Ignition Delay: 3.746e+00 seconds for T=2718.7118087665854K. Took 1.36s to compute
Computed Ignition Delay: 1.474e+01 seconds for T=2712.3976334047347K. Took 1.47s to compute
Computed Ignition Delay: 6.945e+01 seconds for T=2706.1510725024746K. Took 1.37s to compute

Figure: ignition delay ($\tau$) vs. the inverse of temperature ($\frac{1000}{T}$).

In [10]:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.semilogy(1000 / ignition_delays.T, ignition_delays.tau, "o-")
ax.set_ylabel("Ignition Delay (s)")
ax.set_xlabel(r"$\frac{1000}{T (K)}$", fontsize=18)

# Add a second axis on top to plot the temperature for better readability
ax2 = ax.twiny()
ticks = ax.get_xticks()
ax2.set_xticks(ticks)
ax2.set_xticklabels((1000 / ticks).round(1))
ax2.set_xlim(ax.get_xlim())
ax2.set_xlabel("Temperature: $T(K)$");
2023-11-06T00:19:50.170894image/svg+xmlMatplotlib v3.8.1, https://matplotlib.org/
In [ ]: