Adiabatic flame temperatures

Flame temperatures depend on * stoichiometry * products * fuel * oxidizer * enthalpy

These properties are affected by * heat losses (radiation) * differential diffusion * flame strain, mixing fields * residence time * soot formation

For convenience here * use products of complete combustion, * at constant pressure (1 atm), * with reactants at 298.15 K (25 C) * adiabatic

Two kinds of energy: internal, and sensible * Internal is energy stored in bonds * Sensible is thermal energy * Combustion reactants have more energy stored in chemical bonds (high internal energy) than combustion products (low internal energy). * The difference is sensible energy that results in high flame temperatures. * That is, internal energy is released as sensible energy

Enthalpy, energy, C$_p$

C$_p$

Heat of reaction

Heating values: LHV, HHV

T$_{ad}$ for CH$_4$

$$CH_4 + 2O_2 + 7.52N_2 \rightarrow CO_2 + 2H_2O + 7.52 N_2$$

$H_R(298.15\, K) = H_P(T_{ad})$ * This is one equation in one unknown. * The reactant and product compositions are known. The reactant temperature is known, so the reactant enthalpy is known. * Solve $T_{ad}$.

$$H_R = H_P(T_{ad})$$

$$H_P(T_{ad}) = 1\mbox{ mole}\cdot h_{CO2}(T_{ad}) + 2\cdot h_{H2O}(T_{ad}) + 7.52\cdot h_{N2}(T_{ad})$$

Set some data and make a thermo class for convenience

import yaml
import numpy as np
from scipy.optimize import fsolve

########################################################################
# Thermo data from Cantera's gri30.cti file

thermoData = '''
Tlow:  200
Tmid:  1000
Thigh: 3500

CH4:
    a_lo: [  5.149876130E+00,  -1.367097880E-02, 4.918005990E-05,  -4.847430260E-08,   1.666939560E-11,-1.024664760E+04,  -4.641303760E+00]
    a_hi: [  7.485149500E-02,   1.339094670E-02, -5.732858090E-06,   1.222925350E-09,  -1.018152300E-13,-9.468344590E+03,   1.843731800E+01]
O2:
    a_lo: [  3.782456360E+00,  -2.996734160E-03, 9.847302010E-06,  -9.681295090E-09,   3.243728370E-12,-1.063943560E+03,   3.657675730E+00]
    a_hi: [  3.282537840E+00,   1.483087540E-03, -7.579666690E-07,   2.094705550E-10,  -2.167177940E-14,-1.088457720E+03,   5.453231290E+00]
N2:
    a_lo: [  3.298677000E+00,   1.408240400E-03, -3.963222000E-06,   5.641515000E-09,  -2.444854000E-12,-1.020899900E+03,   3.950372000E+00]
    a_hi: [  2.926640000E+00,   1.487976800E-03, -5.684760000E-07,   1.009703800E-10,  -6.753351000E-15,-9.227977000E+02,   5.980528000E+00]
CO2:
    a_lo: [  2.356773520E+00,   8.984596770E-03, -7.123562690E-06,   2.459190220E-09,  -1.436995480E-13,-4.837196970E+04,   9.901052220E+00]
    a_hi: [  3.857460290E+00,   4.414370260E-03, -2.214814040E-06,   5.234901880E-10,  -4.720841640E-14,-4.875916600E+04,   2.271638060E+00]
H2O:
    a_lo: [  4.198640560E+00,  -2.036434100E-03, 6.520402110E-06,  -5.487970620E-09,   1.771978170E-12,-3.029372670E+04,  -8.490322080E-01]
    a_hi: [  3.033992490E+00,   2.176918040E-03, -1.640725180E-07,  -9.704198700E-11,   1.682009920E-14,-3.000429710E+04,   4.966770100E+00]
'''

########################################################################

class thermo():

    def __init__(self, species) :
        """
        species: input, string name of species in thermoData.yaml
        M: input (species molecular weight, kg/kmol)
        Call as: h2o = thermo("H2O"), then get enthalpy as h2o.h_mole(298.15), etc.
        """

        s = self

        #tData = open("thermoData.yaml"); tData = yaml.load(tData)
        #tData = yaml.load(thermoData, 'r')
        tData = yaml.safe_load(thermoData)   
        

        s.a_lo = tData[species]["a_lo"]
        s.a_hi = tData[species]["a_hi"]

        s.T_lo = tData["Tlow"]
        s.T_mid = tData["Tmid"]
        s.T_hi = tData["Thigh"]

    #--------------------------------------------------------

    def h_mole(self,T) :
        """
        T: input, (K)
        return enthalpy in units of J/kmol
        """
        s = self
        
        if T<=s.T_mid and T>=s.T_lo :
            a = s.a_lo
        elif T>s.T_mid and T<=s.T_hi :
            a = s.a_hi
        else :
            raise ValueError("ERROR: temperature is out of range")

        hRT = a[0] + a[1]/2.0*T + a[2]/3.0*T*T + a[3]/4.0*T**3.0 + a[4]/5.0*T**4.0 + a[5]/T

        return hRT * 8314.46 * T 

Calculate T$_{ad}$

ch4 = thermo("CH4")
o2  = thermo("O2")
n2  = thermo("N2")
co2 = thermo("CO2")
h2o = thermo("H2O")

Tr = 298.15
Hr = ch4.h_mole(Tr)  + 2*o2.h_mole(Tr)   + 7.52*n2.h_mole(Tr)

def F(Tad):
    Hp = co2.h_mole(Tad) + 2*h2o.h_mole(Tad) + 7.52*n2.h_mole(Tad)
    return Hr - Hp

Tad_guess=2000.0
Tad = fsolve(F, Tad_guess)[0]

print("Adiabatic flame temperature =", Tad, "K")
Adiabatic flame temperature = 2325.598129753966 K

Do the same thing using Cantera instead

import cantera as ct
gas = ct.Solution("gri30.yaml")
P = 101325   # Pa

#----------- Stoichiometric reactants
Tr = 298.15
gas.TPX = Tr, P, "CH4:1, O2:2, N2:7.52"
hR = gas.enthalpy_mass

#----------- Stoichiometric products
gas.HPX = hR, P, "CO2:1, H2O:2, N2:7.52"       # note, enthalpy is reactant enthalpy
yst = gas.Y
print("Tad =", gas.T, "K")
Tad = 2325.5981297600447 K

Compute T$_{ad}$ over all $\xi$ using Cantera

import matplotlib.pyplot as plt
%matplotlib inline

#########################################################

def Tad_all_mixf(oxidizer, fuel, stoicReac, stoicProd, plotLabel):
    '''
    Plot Tad versus mixture fraction
    oxidizer,  input, dictionary of species names and moles
    fuel,      input, dictionary of species names and moles
    stoicReac, input, dictionary of species names and moles
    stoicProd, input, dictionary of species names and moles
    plotLabel, input, string for the plot label
    '''
    
    species = ["CH4", "O2", "N2", "CO2", "H2O"]

    #--------- Get the state at ξ = 0
    gas.TPX = 298.15, P, oxidizer
    y0 = gas.Y
    h0 = gas.enthalpy_mass 
    
    #--------- Get the state at ξ = 1
    gas.TPX = 298.15, P, fuel
    y1 = gas.Y
    h1 = gas.enthalpy_mass
    
    #----------- Stoichiometric products
    gas.X = stoicProd
    yst = gas.Y
    
    #--------- Get Tad at all ξ
    
    ξ_st = gas.molecular_weights[gas.species_index("CH4")] * stoicReac["CH4"]/ (          
           gas.molecular_weights[gas.species_index("CH4")] * stoicReac["CH4"] + 
           gas.molecular_weights[gas.species_index("O2")]  * stoicReac["O2"] +
           gas.molecular_weights[gas.species_index("N2")]  * stoicReac["N2"])
    
    n = 1000
    ξ = np.linspace(0.0,1,n)
    T = np.zeros(n)
    
    for i in range(n):
        if ξ[i] <= ξ_st:
            y = y0 + (yst-y0)*ξ[i]/ξ_st
        else:
            y = yst + (y1-yst)*(ξ[i]-ξ_st)/(1-ξ_st)
        h = h0*(1-ξ[i]) + h1*ξ[i]
        gas.HPY = h, P, y
        T[i] = gas.T
        
    #--------- Plot results
        
    plt.rc('font', size=14)
    plt.plot(ξ,T, label=plotLabel)
    plt.xlabel('ξ')
    plt.ylabel('T (K)')
    plt.xlim([0,1])
    plt.ylim([298.15,5500])
    plt.legend(frameon=False);
    
#########################################################

#---- Do combustion in air

oxidizer  = {"O2":1, "N2":3.76}
fuel      = {"CH4":1}
stoicReac = {"CH4":1, "O2":2, "N2":7.52}
stoicProd = {"CO2":1, "H2O":2, "N2":7.52}

Tad_all_mixf(oxidizer, fuel, stoicReac, stoicProd, 'burn in air')

#---- Do combustion in o2

oxidizer  = {"O2":1, "N2":0}
fuel      = {"CH4":1}
stoicReac = {"CH4":1, "O2":2, "N2":0}
stoicProd = {"CO2":1, "H2O":2, "N2":0}

Tad_all_mixf(oxidizer, fuel, stoicReac, stoicProd, 'burn in O2')