Modeling flammability limits in hydrogen-air mixtures using Mathematica
This post demonstrates how to use Mathematica to calculate and visualize the 3 flammability limits that occur in hydrogen-air mixtures. The implementation calculates reaction durations across various pressure and temperature conditions to identify these critical boundaries.
1 Equations of hydrogen combustion
The values of constants and kinetic equations are taken from the author of the KINET
program A. V. Abramenkov (Lomonosov MSU, Moscow).
H2 + O2 = OH + OH, k = 1.48 e + 10, 161.080
OH + OH = H2 + O2, k = 4.6 e + 08, 85.600
OH + H2 = H2O + H, k = 2 e + 10, 21.760
H2O + H = OH + H2, k = 8.5 e + 10, 86.400
H + O2 = OH + O, k = 2.5 e + 11, 71.760
OH + O = H + O2, k = 2.55 e + 10, 1.570
O + H2 = OH + H, k = 5.66 e + 09, 36.610, 1.00
OH + H = O + H2, k = 2.46 e + 09, 29.290, 1.00
O + H2O = OH + OH, k = 7 e + 10, 77.610
OH + OH = O + H2O, k = 6.5 e + 09, 4.810
H + H + M = H2 + M, k = 6.5 e + 08
H2 + M = H + H + M, k = 6.71 e + 12, 428.860, -1.00
O + O + M = O2 + M, k = 6.5 e + 08
O2 + M = O + O + M, k = 2.35 e + 13, 500.820, -1.00
H + OH + M = H2O + M, k = 1.69 e + 12, , -2.00
H2O + M = H + OH + M, k = 2.5 e + 13, 442.670
OH + OH + M = H2O2 + M, k = 8.75 e + 08, -21.610
H2O2 + M = OH + OH + M, k = 1.2 e + 14, 191.210
OH + O + M = HO2 + M, k = 4.75 e + 10
HO2 + M = OH + O + M, k = 1 e + 12, 280.330
H + O2 + M = HO2 + M, k = 2.7 e + 09, -6.020
HO2 + M = H + O2 + M, k = 2.25 e + 12, 194.140
HO2 + H2 = H2O2 + H, k = 7.25 e + 08, 82.840
H2O2 + H = HO2 + H2, k = 1.55 e + 09, 16.740
HO2 + H2 = H2O + OH, k = 1.15 e + 08, 99.580
H2O + OH = HO2 + H2, k = 2.76 e + 07, 232.630, 0.50
HO2 + H2O = H2O2 + OH, k = 2.5 e + 10, 135.980
H2O2 + OH = HO2 + H2O, k = 1.15 e + 10, 7.410
HO2 + HO2 = H2O2 + O2, k = 3 e + 09
H2O2 + O2 = HO2 + HO2, k = 2.16 e + 09, 172.380, 0.50
H + HO2 = OH + OH, k = 2 e + 11, 7.470
OH + OH = H + HO2, k = 1.15 e + 10, 168.620
H + HO2 = H2O + O, k = 1.4 e + 10, 8.740
H2O + O = H + HO2, k = 5.5 e + 09, 242.670
H + HO2 = H2 + O2, k = 4 e + 10, 2.510
H2 + O2 = H + HO2, k = 5.5 e + 10, 241.840
O + HO2 = OH + O2, k = 3.5 e + 10
OH + O2 = O + HO2, k = 3.5 e + 10, 236.400
H + H2O2 = H2O + OH, k = 1 e + 12, 44.770
H2O + OH = H + H2O2, k = 1.15 e + 11, 333.460
O + H2O2 = OH + HO2, k = 2.5 e + 10, 4.600
OH + HO2 = O + H2O2, k = 3.8 e + 10, 50.210
H2 + O2 = H2O + O, k = 5.5 e + 10, 228.030
H2O + O = H2 + O2, k = 5.5 e + 10, 228.030
H2 + O2 + M = H2O2 + M, k = 3 e + 07, 83.260
H2O2 + M = H2 + O2 + M, k = 2.75 e + 10, 209.200
OH + M = O + H + M, k = 2.5 e + 13, 432.210
O + H + M = OH + M, k = 2.55 e + 10
HO2 + OH = H2O + O2, k = 2 e + 10, 1.260
H2O + O2 = HO2 + OH, k = 1.9 e + 11, 307.520, 0.50
H2 + O + M = H2O + M, k = 2.75 e + 08
H2O + M = H2 + O + M, k = 2.75 e + 14, 456.060
O + H2O + M = H2O2 + M, k = 7.5 e + 07, 50.210
H2O2 + M = O + H2O + M, k = 1.05 e + 12, 190.790
O + H2O2 = H2O + O2, k = 1.3 e + 08, 112.970
H2O + O2 = O + H2O2, k = 1.25 e + 09, 445.600
H2 + H2O2 = H2O + H2O, k = 1.3 e + 10, 87.860
H2O + H2O = H2 + H2O2, k = 3.5 e + 09, 460.240
H + HO2 + M = H2O2 + M, k = 1.9 e + 08, 5.230 H2O2 + M = H + HO2 + M, k = 1.5 e + 12, 380.740
Save the above data in a file named H2Burn.kin
in the same directory as this notebook.
2 Extract data from file
We start by importing the reaction data from the file. The data is in a text format, so we use Import
to read it:
= Import["H2Burn.kin","Text"]; str
Now we parse the reaction data. Each line contains a reaction equation and kinetic parameters:
= Replace[
lines StringSplit[StringSplit[str,"\n"],RegularExpression[",\\s+"]],
{{eq_,k_}:>{eq,k,0,0},{eq_,A_,Ea_}:>{eq,A,Ea,0}},
1
];
Next, we extract the different components from our parsed data:
{reactionStrings, preExponential, activations, empirical} = Transpose@lines;
The pre-exponential factors need to be converted to numerical values:
= Interpreter["Number"]@*StringDelete[RegularExpression["\\s+"]]/@StringTrim[preExponential,{"k = "}]/.Null->0; preExponential
Similarly, we process the activation energies and empirical coefficients:
= 1000ToExpression/@activations/.Null->0;
activations = ToExpression/@empirical; empirical
We transform the reaction strings into rule-based format for easier processing:
= Rule@@@(StringSplit[#," + "]&/@StringSplit[reactionStrings," = "])/.Null->0; rules
Finally, we extract all unique reagents from the reactions:
= DeleteDuplicates@Flatten[List@@@rules]; reagents
3 Transform data to system of equations
Now we’ll create a function that builds differential equations for each reagent. This function identifies reactions involving a specific reagent and computes its rate of change:
[reag_] := Module[{mask, factors, act, pre, emp, consts},
makeEq= Map[Not@*FreeQ[reag], rules];
mask = Cases[
factors Pick[rules, mask],
HoldPattern[r_->p_] :> Times@@(c[#][t]&/@r)(Count[reag]@p-Count[reag]@r)
];
= Pick[activations, mask];
act = Pick[preExponential, mask];
pre = Pick[empirical, mask];
emp = Replace[
consts Transpose[{pre, act, emp}],
{a_, e_, n_} :> a (T/298)^n Exp[-( e/(8.31 T))],
1
];
c[reag]'[t] == factors . consts
]
The rate constants follow the Arrhenius equation with temperature dependence.
Next, we build a complete system of differential equations for all reagents:
[init_, temp_] := Module[{eqs, inits, unconditioned, sys},
makeSys= makeEq/@reagents;
eqs = Complement[reagents, First/@init];
unconditioned = init/.HoldPattern[r_->conc_]:>c[r][0]==conc;
inits = c[#][0]==0&/@unconditioned;
unconditioned
= Join[eqs, inits, unconditioned]/.T->temp;
sys = Select[sys, FreeQ[#,c["M"]'[t]==0.]&&FreeQ[#,c["M"][0]]&];
sys = sys/.c["M"][t]->c["H2"][t]+c["N2"][t]+c["O2"][t];
sys = sys/.c["N2"][_]->cN2;
sys = sys/.True->Nothing;
sys
sys]
Prepare the list of variables for which we’ll solve the system:
= DeleteDuplicates@DeleteCases[Map[c[#][t]&, reagents], c["M"][t]]; vars
Now we define the solver function that will integrate our system of ODEs:
[cH2_, cO2_, cN2_, T_, tmax_] :=
sol[{end = 1, sol1},
Quiet@Module= First @ NDSolve[
sol1 [{"H2" -> cH2, "O2" -> cO2, "N2" -> cN2}, T],
makeSys,
vars{t, 0, tmax},
Method -> {
"EventLocator",
"Event" -> {c["H2"][t] <= cH2 / 2 || c["O2"][t] <= cO2 / 2},
"EventAction" :> Throw[end = t, "StopIntegration"]
}
];
{sol1, end}
]
4 Define measurable metrics
Now we define functions to characterize the flammability limits. First, a function to calculate reaction duration at a given pressure and temperature:
ClearAll[duration];
[p_, T_] := Module[{
duration= {6.9797, 1.4657, 5.5140}10^-3,
coeffs
press},
= coeffs{p, p, p};
press [Sequence@@press, T, 1]
Last@sol]
Next, we define a function to calculate data points across a range of pressures and temperatures:
ClearAll[calculate];
[{logPmin_, logPmax_}, {Tmin_, Tmax_}, grid_:10] :=
calculateFlatten[#, 1]& @ ParallelTable[
{T, p, -Log10 @ duration[p, T]},
{p, 10^Subdivide[logPmin, logPmax, grid]},
{T, Subdivide[Tmin, Tmax, grid]}
];
5 Start parallel calculations and plot results
Now we run our calculation across a range of pressures (10^-3 to 10^3 atm) and temperatures (600 to 1000 K):
= calculate[{-3, 3}, {600, 1000}, 10]; data
Finally, we visualize the results as a contour plot showing the three distinct flammability limits:
ListContourPlot[
,
dataClippingStyle -> Automatic,
ScalingFunctions -> {None, "Log10"},
Epilog -> {
Text[Style["1st limit", 20, White], Scaled[{.7, .3}]],
Text[Style["2nd limit", 20, White], Scaled[{.7, .5}], Automatic, {1, 0.4}],
Text[Style["3rd limit", 20, White], Scaled[{.5, .7}], Automatic, {1, -0.2}]
},
FrameLabel -> {"T, K", "p, atm"},
-> Automatic,
PlotLegends ColorFunction -> "TemperatureMap",
PlotLabel -> "-lg [duration]"
]
The resulting plot shows three distinct regions corresponding to the three flammability limits of hydrogen-air mixtures. These limits represent boundaries where combustion becomes sustainable or unsustainable based on mixture composition, pressure, and temperature conditions.