218 lines
7.8 KiB
Python
218 lines
7.8 KiB
Python
from functools import reduce
|
|
import argparse
|
|
import logging
|
|
|
|
import cantera as ct
|
|
|
|
logger = logging.getLogger()
|
|
stream_handler = logging.StreamHandler()
|
|
logger.addHandler(stream_handler)
|
|
|
|
parser = argparse.ArgumentParser(description='Calculate Thermodynamic States of the Coal fired Boiler')
|
|
parser.add_argument('--hhv', action='store_true', help='Higher heating value is used')
|
|
parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
|
|
args = parser.parse_args()
|
|
|
|
is_HHV = vars(args)['hhv']
|
|
is_verbose = vars(args)['verbose']
|
|
|
|
if is_verbose:
|
|
logger.setLevel(logging.INFO)
|
|
else:
|
|
logger.setLevel(logging.WARNING)
|
|
|
|
stdT = 298.15 # Temperature at standard state, K
|
|
|
|
"""#############################################################################
|
|
Input Parameter Section
|
|
#############################################################################"""
|
|
|
|
# Air stream Temperatures and Mass flow rates
|
|
t = list(map(float, ''' 348.15 315.65 600.79 308.54 318.03 306.11 339.45 '''.split()))
|
|
m = list(map(float, ''' 114.4 6.94 362.92 7.25 7.25 7.25 7.25 '''.split()))
|
|
|
|
coalT = 348.15 # K
|
|
coalMfr = 56.8813 # kg/s
|
|
|
|
# Ultimate Analysis - percentage by weight
|
|
coal_ua = {
|
|
"C": 81.41,
|
|
"H2": 5.47,
|
|
"O2": 10.83,
|
|
"N2": 1.72,
|
|
"S": 0.57,
|
|
}
|
|
|
|
# Proximate Analysis - fraction by weight
|
|
moist = 0.1551
|
|
vm = 0.3047
|
|
fc = 0.4347
|
|
ash = 0.1055
|
|
|
|
vm_fc = 1. - moist - ash
|
|
fuelMfr = coalMfr
|
|
|
|
# add mass of moisture and ash
|
|
coal_ua['N2'] += 100 * ash / vm_fc
|
|
coal_ua["H2O"] = 100 * moist / vm_fc
|
|
|
|
# Boiler Performance Data
|
|
# - Heat Transfer Rates
|
|
heatXferPipe = 677656911.111 # J/s
|
|
heatXferWall = 592070866.667 # J/s
|
|
|
|
# Loading thermo data of species to consider
|
|
gri_species = {S.name: S for S in ct.Species.listFromFile('gri30.cti')}
|
|
nasa_species = {S.name: S for S in ct.Species.listFromFile('nasa_gas.cti')}
|
|
nasa_species.update(gri_species)
|
|
system_species = [nasa_species[S] for S in (str.split(' C S H2 O2 N2 CO CO2 H2O SO2 '))]
|
|
|
|
|
|
"""#############################################################################
|
|
Calculate averaged air property
|
|
#############################################################################"""
|
|
|
|
gases = [ct.Solution(thermo='IdealGas', species=system_species) for i in range(7)]
|
|
for i, gas in enumerate(gases):
|
|
gas.TPY = t[i], ct.one_atm, "O2: 0.233, N2: 0.767"
|
|
|
|
airs = [
|
|
ct.Quantity(gas, constant='HP', mass=m[i])
|
|
for i, gas in enumerate(gases)
|
|
]
|
|
|
|
for i, air in enumerate(airs):
|
|
logger.info("air {}, T = {}, mass flow rate = {}".format(i+1, air.T, air.mass))
|
|
|
|
airmix = reduce(lambda a, b: a+b, airs)
|
|
print("Total Air flow rate = ", airmix.mass)
|
|
|
|
|
|
"""#############################################################################
|
|
Dummy gaseous coal object containing elementary composition
|
|
mass of ash contents is added to N2
|
|
#############################################################################"""
|
|
|
|
coal = ct.Solution(thermo='IdealGas', species=system_species)
|
|
coal.TPY = coalT, ct.one_atm, coal_ua
|
|
|
|
|
|
"""#############################################################################
|
|
Coal + v O2 = (Y_C/W_C) CO2 + (Y_H/W_H/2) H2O + (Y_S/W_S) SO2 + (Y_N/W_N) N2
|
|
Heating value = Sum(Product Enthalpy of Formation) - Sum(Reactant Enthalpy of Formation)
|
|
#############################################################################"""
|
|
|
|
# coal heating value = 5761 Kcal/kg * 4.184 kJ/kcal
|
|
coalHV = - 5761 * 4.184 # J/kg, with Moisture and Ash
|
|
coalHVdaf = - 5761 * 4.184 / vm_fc # kJ/kg, Dry and Ash Free, Negative since exothermic
|
|
print("HHV" if is_HHV else "LHV", "of Coal , kJ/kg = ", coalHV)
|
|
print("HHV" if is_HHV else "LHV", "of Coal(daf), kJ/kg = ", coalHVdaf)
|
|
|
|
# sum product of 1kg coal enthalpy of formation - kJ/kg
|
|
def hf_product_coefs (product_name, element_name):
|
|
""" returns kJ/kg """
|
|
# J/kmol / kg/kmol / 1000
|
|
if product_name == "H2O" and is_HHV:
|
|
return (-285820 * 1000.
|
|
/ ct.Element(element_name).weight
|
|
/ nasa_species[product_name].composition[element_name]
|
|
/ 1000. )
|
|
else:
|
|
return (nasa_species[product_name].thermo.h(stdT)
|
|
/ ct.Element(element_name).weight
|
|
/ nasa_species[product_name].composition[element_name]
|
|
/ 1000. )
|
|
|
|
'''
|
|
H(stdT)/W values from NASA polynomial
|
|
- 32762.281048240053
|
|
- 119952.68929829611
|
|
- 9258.666766366705
|
|
|
|
H_f/W values from google search data
|
|
- 32762.45348
|
|
- 141887.60121
|
|
- 8919.38250
|
|
|
|
Discrepency in Enthalpy of Formation for H2O is due to phase difference
|
|
value above is for vapor and otherwise is for liquid water
|
|
'''
|
|
|
|
logger.info("hf(CO2) / W(C) = {}".format(hf_product_coefs("CO2", "C")))
|
|
logger.info("hf(H2O) / W(H) = {}".format(hf_product_coefs("H2O", "H")))
|
|
logger.info("hf(SO2) / W(S) = {}".format(hf_product_coefs("SO2", "S")))
|
|
|
|
sum_product_hf = (
|
|
hf_product_coefs("CO2", "C") * coal.elemental_mass_fraction('C')
|
|
+ hf_product_coefs("H2O", "H") * coal.elemental_mass_fraction('H')
|
|
+ hf_product_coefs("SO2", "S") * coal.elemental_mass_fraction('S'))
|
|
|
|
sum_coal_hf = - coalHV + sum_product_hf
|
|
|
|
logger.info("Sum(Hf_product), kJ/kg = {}".format(sum_product_hf))
|
|
logger.info("Sum(Hf_reactant), kJ/kg = {}".format(sum_coal_hf))
|
|
|
|
|
|
"""#############################################################################
|
|
Coal Enthalpy at 348.15 K = \Delta H_f + (H(348.15) - H(298.15))
|
|
- Use graphite's Cp to calculate H difference (H(348.15) - H(298.15))
|
|
#############################################################################"""
|
|
|
|
gr = ct.Solution('graphite.cti')
|
|
gr.TP = coalT, ct.one_atm
|
|
coal_preheat_enthalpy = gr.enthalpy_mass / 1000. # kJ/kg
|
|
coal_enthalpy = sum_coal_hf + coal_preheat_enthalpy
|
|
|
|
logger.info("Coal preheat H , kJ/kg = ", coal_preheat_enthalpy)
|
|
logger.info("Coal enthalpy , kJ/kg = ", coal_enthalpy)
|
|
logger.info("Dummy Coal H , kJ/kg = ", coal.enthalpy_mass/1000.)
|
|
|
|
|
|
"""#############################################################################
|
|
Can't Set coal object's H to coal_enthalpy since it is composed of gaseous
|
|
C S H2 O2 N2 and can't have H=coal_enthalpy with T >= 0 K.
|
|
Therefore only difference between real coal enthalpy and dummy gas coal is
|
|
caculated and it will be added later.
|
|
#############################################################################"""
|
|
|
|
enthalpy_added_after_mixing = (coal_enthalpy*1000 - coal.enthalpy_mass) * fuelMfr # J
|
|
logger.info("enthalpy to add later = ", enthalpy_added_after_mixing)
|
|
|
|
|
|
################################################################################
|
|
## Adiabatic Flame Temperature
|
|
## - also adjust enthalpy difference btw dummy gas fuel and real solid fuel
|
|
################################################################################
|
|
|
|
reactant = airmix + ct.Quantity(coal, constant='HP', mass=fuelMfr)
|
|
print("Total Mass flow rate = ", reactant.mass)
|
|
|
|
|
|
################################################################################
|
|
## Adiabatic Flame Temperature
|
|
## - also adjust enthalpy difference btw dummy gas fuel and real solid fuel
|
|
################################################################################
|
|
|
|
reactant.equilibrate("HP")
|
|
reactant.HP = reactant.enthalpy/reactant.mass + enthalpy_added_after_mixing/reactant.mass, ct.one_atm
|
|
reactant.equilibrate("HP")
|
|
|
|
print("")
|
|
print("Chemical Equilibrium")
|
|
reactant.phase()
|
|
|
|
|
|
################################################################################
|
|
## Heat Transfer to pipes and walls
|
|
################################################################################
|
|
|
|
reactant.HP = reactant.enthalpy/reactant.mass - (heatXferWall+heatXferPipe)/reactant.mass, ct.one_atm
|
|
print("After Heat Transfer")
|
|
reactant.phase()
|
|
|
|
note_ref='''\
|
|
Reference for Estimation of Coal Enthalpy of Formation
|
|
|
|
Eqn (14) and (15) of
|
|
Sciazko, M. (2013). Rank-dependent formation enthalpy of coal. Fuel, 114, 2-9. doi:10.1016/j.fuel.2012.06.099
|
|
'''
|