from __future__ import division
from __future__ import absolute_import
from copy import deepcopy
from scipy import optimize
from .burcat import Mixture
SUPPORTED = ['C', 'H', 'N', 'O']
# TODO: Check that adiabatic flame temperature is ok
[docs]def balance(fuel, am, phi):
"""
Function that balances the combustion equation given any simple
fuel element
:fuel:
Formula of a Burcat's database fuel as a string. The fuel can
only contain C, H, O and N. Other atoms will be ignored.
:phi:
Equivalence ratio for air.
"""
reactants = {'fuel': am, 'N2': 0, 'O2': 0}
products = {'N2': 0, 'CO2': 0, 'H2O': 0, 'O2': 0}
atoms = {'C': 0, 'H': 0, 'N': 0, 'O': 0}
lamb = 1 / phi
for element in fuel.elements:
if element[0] in SUPPORTED:
atoms[element[0]] = element[1]
k = lamb * (atoms['C'] + atoms['H'] / 4 + atoms['O'] / 2)
reactants['O2'] = am * k
reactants['N2'] = am * 3.76 * k
products['N2'] = am * (atoms['N'] + 3.76 * k)
products['CO2'] = am * (atoms['C'])
products['H2O'] = am * (atoms['H'] / 2)
products['O2'] = am * ((lamb - 1) * (k / lamb))
return (reactants, products)
[docs]def balance_mix(fuels, phi):
"""
function that balances the combustion equation given a mix of
fuels.
:fuels:
type Mixture. Simple fuels formed only by C, H, O and N and the
amount of each one
:phi:
Equivalence ratio for air.
"""
reactants = {'N2': 0, 'O2': 0}
products = {'N2': 0, 'CO2': 0, 'H2O': 0, 'O2': 0}
for fuel in fuels:
(dreac, dprod) = balance(fuel[0], fuel[1], phi)
reactants['O2'] += dreac['O2']
reactants['N2'] += dreac['N2']
products['N2'] += dprod['N2']
products['CO2'] += dprod['CO2']
products['H2O'] += dprod['H2O']
products['O2'] += dprod['O2']
return (reactants, products)
[docs]class SimpleCombustor(object):
"""
This class models a simple combustor that uses fuel as a reductor
and air as a single oxidizer. The combustion is complete, no CO
nor radicals are formed.
It only supports fuels with C, N, H and O. If you put a more
complicated fuel it will ignore the rest of atoms to balance the
reaction.
"""
def __init__(self, fuel, phi, db):
"""
:fuel:
The formula of the fuel
:phi:
Equivalence ratio
:db:
Element database. It is passed as argument because it has
to be allocated only once.
>>> from .burcat import Elementdb
>>> db = Elementdb()
>>> methane = db.getelementdata("CH4 RRHO")
>>> combustor = SimpleCombustor(methane,1,db)
>>> print(round(combustor.products.cp, 6))
1112.222587
>>> print(round(combustor.heat_of_comb(298.15), 2))
50027136.34
>>> print(round(combustor.adiabatic_flame_temp(298.15)[0], 6))
2128.632842
"""
# REACTANTS
self.reactants = Mixture()
self.products = Mixture()
oxygen = db.getelementdata("O2 REF ELEMENT")
nitrogen = db.getelementdata("N2 REF ELEMENT")
carbondiox = db.getelementdata("CO2")
water = db.getelementdata("H2O")
(rdict, pdict) = balance(fuel, 1, phi)
self.reactants.add(fuel, rdict['fuel'])
self.reactants.add(oxygen, rdict['O2'])
self.reactants.add(nitrogen, rdict['N2'])
self.products.add(nitrogen, pdict['N2'])
self.products.add(carbondiox, pdict['CO2'])
self.products.add(water, pdict['H2O'])
self.products.add(oxygen, pdict['O2'])
[docs] def heat_of_comb(self, T):
"""
Calculates the heat of combustion per kg of fuel. Checked ok
"""
hreac = float(0)
for reac in self.reactants:
hreac += reac[0].ho(T) * reac[1]
hprod = float(0)
for prod in self.products:
hprod += prod[0].ho(T) * prod[1]
return float(hreac - hprod) / self.reactants[0][0].mm
@property
def lower_heating_value(self):
return self.heat_of_comb(423.15)
[docs] def adiabatic_flame_temp(self, T):
"""
This is the adiabatic flame temp for the given mixtures of
reactants and products. If you want the true adiabatic flame
temperature remember to set the equivalence ratio to 1.
Otherwise you will always get lower temperatures.
"""
dh = self.heat_of_comb(T) * self.reactants[0][0].mm
f = \
lambda Tg: self.products[0][1] * self.products[0][0].cpo(Tg) +\
self.products[1][1] * self.products[1][0].cpo(Tg) +\
self.products[2][1] * self.products[2][0].cpo(Tg) +\
self.products[3][1] * self.products[3][0].cpo(Tg) - dh / (Tg - T)
return optimize.fsolve(f, 1000)
[docs]class Combustor(object):
"""
Combustor that is able to characterize the combustion of a mixture
of fuels
"""
def __init__(self, fuels, phi, db):
self.fuels = deepcopy(fuels)
self.reactants = fuels
self.products = Mixture()
oxygen = db.getelementdata("O2 REF ELEMENT")
nitrogen = db.getelementdata("N2 REF ELEMENT")
carbondiox = db.getelementdata("CO2")
water = db.getelementdata("H2O")
(dreac, dprod) = balance_mix(fuels, phi)
self.reactants.add(oxygen, dreac['O2'])
self.reactants.add(nitrogen, dreac['N2'])
self.products.add(nitrogen, dprod['N2'])
self.products.add(carbondiox, dprod['CO2'])
self.products.add(water, dprod['H2O'])
self.products.add(oxygen, dprod['O2'])
[docs] def heat_of_comb(self, T):
"""
Calculates the heat of combustion per kg of fuel. Checked ok
"""
hreac = float(0)
for reac in self.reactants:
hreac += reac[0].ho(T) * reac[1]
hprod = float(0)
for prod in self.products:
hprod += prod[0].ho(T) * prod[1]
return float(hreac - hprod) / self.fuels.mm
@property
def lower_heating_value(self):
return self.heat_of_comb(423.15)
[docs] def adiabatic_flame_temp(self, T):
"""
This is the adiabatic flame temp for the given mixtures of
reactants and products. If you want the true adiabatic flame
temperature remember to set the equivalence ratio to 1.
Otherwise you will always get lower temperatures.
"""
dh = self.heat_of_comb(T) * self.fuels.mm
f = \
lambda Tg: self.products[0][1] * self.products[0][0].cpo(Tg) +\
self.products[1][1] * self.products[1][0].cpo(Tg) +\
self.products[2][1] * self.products[2][0].cpo(Tg) +\
self.products[3][1] * self.products[3][0].cpo(Tg) - dh / (Tg - T)
return optimize.fsolve(f, 1000)