Modeling combustion
IdealGasThermo
allows us to perform simple combustion calculations. The following functions are useful utilites to do so.
Calculating stoichiometric fuel-oxidizer ratio
For an arbitrary fuel of type $\genfuel$ we can write a general equation representing the combustion of 1 mole of fuel,
\[\genfuel + n_{\mathrm{O}_2} \mathrm{O}_2 \longrightarrow n_{\mathrm{CO}_2} \mathrm{CO}_2 + n_{\mathrm{H}_2\mathrm{O}} \mathrm{H}_2\mathrm{O} + n_{\mathrm{N}_2} \mathrm{N}_2 \]
where the number of moles of the different species is given by balancing the reaction:
\[\begin{aligned} n_{\rm{CO_2}} &= x_{\mathrm{C}} \tag{1}\\ n_{\rm{H_2O}} &= \frac{x_{\mathrm{H}}}{2}\\ n_{\rm{N_2}} &= \frac{x_{\mathrm{N}}}{2}\\ n_{\rm{O_2}} &= x_{\mathrm{C}} + \frac{x_{\mathrm{H}}}{4} - \frac{x_{\mathrm{O}}}{2}. \end{aligned}\]
The molar fuel-oxygen ratio $f$ is then $\displaystyle{\frac{1}{n_{\rm{O_2}}}}$. If the oxidzier is not pure oxygen then the stoichiometric molar fuel-oxidizer ratio is given by $\displaystyle{f_{\text{stoich.}}=\frac{X_{\rm{O_2}}}{n_{\rm{O_2}} }}$, where $X_{\rm{O_2}}$ is the mole fraction of oxygen in the oxidizer (e.g., $X_{\rm{O_2}} \approx 0.21$ in dry air).
This reaction can also be written as
\[\genfuel \longrightarrow n_{\mathrm{CO}_2} \mathrm{CO}_2 + n_{\mathrm{H}_2\mathrm{O}} \mathrm{H}_2\mathrm{O} + n_{\mathrm{N}_2} \mathrm{N}_2 - n_{\mathrm{O}_2} \mathrm{O}_2 \]
which can be read as "the complete combustion of 1 mole of fuel $(\genfuel)$ consumes $n_{\mathrm{O}_2}$ moles of $\mathrm{O}_2$ and produces $n_{\mathrm{CO}_2}$ moles of $\mathrm{CO}_2$, $n_{\mathrm{H}_2\mathrm{O}}$ moles of $\mathrm{H}_2\mathrm{O}$, and $n_{\mathrm{N}_2}$ moles of $\mathrm{N}_2$".
IdealGasThermo.fuelbreakdown
— Functionfuelbreakdown(fuel::String)
Returns the number of C, H, O, and N atoms that the fuel is composed of.
Examples
julia> IdealGasThermo.fuelbreakdown("CH4")' #transpose is simply to save space in the docs
1×4 adjoint(::Vector{Float64}) with eltype Float64:
1.0 4.0 0.0 0.0
julia> IdealGasThermo.fuelbreakdown("C12H23.5")'
1×4 adjoint(::Vector{Float64}) with eltype Float64:
12.0 23.5 0.0 0.0
julia> IdealGasThermo.fuelbreakdown("CH3COOH")'
1×4 adjoint(::Vector{Float64}) with eltype Float64:
2.0 4.0 2.0 0.0
julia> IdealGasThermo.fuelbreakdown("CH3CH2OH")'
1×4 adjoint(::Vector{Float64}) with eltype Float64:
2.0 6.0 1.0 0.0
IdealGasThermo.stoich_molar_fuel_oxy_ratio
— Functionstoich_molar_fuel_oxy_ratio(fuel::AbstractString)
Calculates the molar fuel-oxygen ratio for stoichiometric combustion.
Examples
julia> using IdealGasThermo
julia> IdealGasThermo.stoich_molar_fuel_oxy_ratio("CH4")
0.5
julia> IdealGasThermo.stoich_molar_fuel_oxy_ratio("C12H23")
0.056338028169014086
IdealGasThermo.stoich_molar_FOR
— Functionstoich_molar_FOR(fuel::AbstractSpecies, oxidizer::AbstractSpecies)
Calculates the molar fuel-oxidizer ratio for stoichiometeric combustion for and arbitrary fuel and oxidizer.
Examples
julia> CH4 = species_in_spdict("CH4");
julia> IdealGasThermo.stoich_molar_FOR(CH4)
0.104738
IdealGasThermo.stoich_FOR
— Functionstoich_FOR(fuel::AbstractSpecies, oxidizer::AbstractSpecies=DryAir)
Calculates the mass fuel-oxidizer ratio for stoichiometeric combustion for and arbitrary fuel and oxidizer.
Examples
julia> IdealGasThermo.stoich_FOR(CH4)
0.05800961333050494
IdealGasThermo.reaction_change_fraction
— Functionreaction_change_fraction(fuel::String)
Returns the mass fraction change due to complete combustion
Assume fuel of type CᵢHⱼOₖNₗ , then
CᵢHⱼOₖNₗ + n(O2) * O2 ---> n(CO2)*CO2 + n(H2O)*H2O + n(N2)*N2
⟹CᵢHⱼOₖNₗ ---> n(CO2)*CO2 + n(H2O)*H2O + n(N2)*N2 - n(O2)*O2
Examples
julia> reaction_change_fraction("CH4")
Dict{String, Float64} with 4 entries:
"O2" => -3.98926
"H2O" => 2.24595
"CO2" => 2.74331
"N2" => 0.0
IdealGasThermo.reaction_change_molar_fraction
— Functionreaction_change_molar_fraction(fuel::AbstractString)
Returns the mole fraction change due to complete combustion of one mole of the specified fuel
Assume fuel of type CᵢHⱼOₖNₗ , then
CᵢHⱼOₖNₗ + n(O2) * O2 ---> n(CO2)*CO2 + n(H2O)*H2O + n(N2)*N2
⟹CᵢHⱼOₖNₗ ---> n(CO2)*CO2 + n(H2O)*H2O + n(N2)*N2 - n(O2)*O2
Examples
julia> IdealGasThermo.reaction_change_molar_fraction("CH4")
4-element Vector{Float64}:
1.0
0.0
2.0
-2.0
Vitiated gas composition
If we consider lean combustion (i.e., more oxygen present than required to completely react with the fuel) we can write the above equation for some molar fuel-oxygen (or more generally oxidizer) ratio $(f \leq f_{\mathrm{stoich.}})$ as follows
\[ \begin{aligned} f \times \genfuel+ 1\times \mathrm{O}_2 &\longrightarrow f n_{\mathrm{CO}_2} \mathrm{CO}_2 &+& f n_{\mathrm{H}_2\mathrm{O}} \mathrm{H}_2\mathrm{O} &+& f n_{\mathrm{N}_2} \mathrm{N}_2 &+&\left(1 - fn_{\mathrm{O}_2}\right) \mathrm{O}_2 \\ f \times \genfuel + 1\times \mathrm{O}_2 &\longrightarrow f x_{\mathrm{C}} \mathrm{CO}_2 &+& f \frac{x_{\mathrm{H}}}{2} \mathrm{H}_2\mathrm{O} &+& f \frac{x_{\mathrm{N}}}{2}\mathrm{N}_2 &+&\left(1 - \frac{f}{f_{\mathrm{stoich.}}}\right)\mathrm{O}_2. \end{aligned}\]
where $\fst = 1/n_{\mathrm{O}_2}$ for oxy-combustion. More generally for some oxidzer that has $X_{\rm{O_2}}$ moles of oxygen per mole of oxidizer,
\[\begin{aligned} f \genfuel + \mathrm{Oxidizer} \longrightarrow f x_{\mathrm{C}} \mathrm{CO}_2 + f \frac{x_{\mathrm{H}}}{2} \mathrm{H}_2\mathrm{O} + f \frac{x_{\mathrm{N}}}{2}\mathrm{N}_2 &+\mathrm{Oxidizer}\\ & - \left(\frac{f X_{\mathrm{O}_2}}{\fst}\right)\mathrm{O}_2. \end{aligned}\]
IdealGasThermo.vitiated_mixture
— Functionvitiated_mixture(fuel::AbstractSpecies, oxidizer::AbstractSpecies,
FAR::Float64, ηburn::Float64=1.0)
Calculates the composition of a burnt gas mixture. Defaults to stoichiometric conditions if FAR is not specified. vitiated_mixture
returns the number of moles of each species present in the burnt gas mixture after combustion at the specified FAR. Note the sum of result in general will not sum to 1.
Examples
julia> CH4 = species_in_spdict("CH4");
julia> Air = IdealGasThermo.DryAir;
julia> IdealGasThermo.vitiated_mixture(CH4, Air, 0.04)
Dict{Any, Any} with 6 entries:
"O2" => 0.0650337
"CH4" => 0.0
"Ar" => 0.009365
"H2O" => 0.144442
"CO2" => 0.0725401
"N2" => 0.78084
julia> IdealGasThermo.vitiated_mixture(CH4, Air)
Dict{Any, Any} with 6 entries:
"O2" => 0.0
"CH4" => 0.0
"Ar" => 0.009365
"H2O" => 0.209476
"CO2" => 0.105057
"N2" => 0.78084
See here for some explanation of the background.
vitiated_mixture(fuel::AbstractString, oxidizer::AbstractString,
FAR::Float64, ηburn::Float64=1.0)
Convenience function that finds fuel and oxidizer from thermo database
IdealGasThermo.vitiated_species
— Functionvitiated_species(fuel::AbstractSpecies, oxidizer::AbstractSpecies,
FAR::Float64, ηburn::Float64=1.0, name::AbstractString="vitiated species")
Returns a composite_species
that represents the burnt gas mixture at the specified FAR. If no FAR is provided stoichiometeric conditions are assumed.
Examples
julia> IdealGasThermo.vitiated_species(CH4, Air, 0.05, name = "CH4-Air-0.05")
Composite Species: "CH4-Air-0.05"
MW = 27.89510190126262 g/mol
with composition:
Species Xᵢ
O2 0.02653
Ar 0.00859
H2O 0.16560
CO2 0.08309
N2 0.71619
------------------
Σ 1.00000
IdealGasThermo.fixed_fuel_vitiated_species
— Functionfixed_fuel_vitiated_species(fuel, oxidizer, ηburn::Float64=1.0)
Returns a function burntgas(FAR::Float64)
that is specific to the fuel and oxidizer combination provided. This gives a highly performant function that can simply be called at any given FAR for that specific fuel+oxidizer combo.
This is ~4x faster than doing burntgas(FAR) = vitiated_species("CH4", "Air", FAR)
Examples
julia> burntgas = IdealGasThermo.fixed_fuel_vitiated_species(CH4, Air)
(::IdealGasThermo.var"#burntgas#52"{species, composite_species, Vector{Float64}, Vector{Float64}, Float64}) (generic function with 1 method)
julia> burntgas(0.05)
Composite Species: "burntgas(CH4 + Dry Air; 0.05)"
MW = 27.89510190126262 g/mol
with composition:
Species Xᵢ
O2 0.02653
Ar 0.00859
H2O 0.16560
CO2 0.08309
N2 0.71619
------------------
Σ 1.00000
julia> gas1 = Gas1D(burntgas(0.02)) #Returns a Gas1D intialized with the burnt gas properties
Gas1D(burntgas(CH4 + Dry Air; 0.02); MW = 28.51473501878705 g/mol)
at T = 298.15 K; P = 101.325 kPa