Source code for thermosteam.properties.combustion

# -*- coding: utf-8 -*-
# BioSTEAM: The Biorefinery Simulation and Techno-Economic Analysis Modules
# Copyright (C) 2020, Yoel Cortes-Pena <yoelcortes@gmail.com>
# 
# A significant portion of this module originates from:
# Chemical Engineering Design Library (ChEDL). Utilities for process modeling.
# Copyright (C) 2020 Caleb Bell <Caleb.Andrew.Bell@gmail.com>
# 
# This module is under a dual license:
# 1. The UIUC open-source license. See 
# github.com/BioSTEAMDevelopmentGroup/biosteam/blob/master/LICENSE.txt
# for license details.
# 
# 2. The MIT open-source license. See
# https://github.com/CalebBell/thermo/blob/master/LICENSE.txt for details.
"""
All data and methods related to chemical combustion.
"""
from .elements import compute_mass_fractions, compute_molecular_weight

__all__ = ('CombustionData',
           'estimate_HHV_modified_Dulong',
           'get_combustion_stoichiometry')

# %% Combustion functions

combustible_elements = ('C', 'H', 'N', 'O', 'S', 'Br', 'I', 'Cl', 'F', 'P')
combustion_products = {'7782-44-7', '124-38-9', '7726-95-6', '7553-56-2',
                       '7647-01-0', '7664-39-3', '7446-09-5', '7727-37-9',
                       '16752-60-6', '7732-18-5', '630-08-0',
                       'Ash', 'H2O', 'CO2', 'CO', 'SO2', 'Br2',
                       'I2', 'HCl', 'HF', 'P4O10', 'O2', 'N2'}

Hf_combustion_products = {
    'H2O': -285825,
    'CO2': -393474,
    'SO2': -296800,
    'Br2': 30880,
    'I2': 62417,
    'HCl': -92173,
    'HF': -272711,
    'P4O10': -3009940,
    'O2': 0,
    'N2': 0,
    "Ash": 0,
}

[docs]def get_combustion_stoichiometry(atoms: dict, MW: float=None) -> dict: r""" Return a dictionary of the combustion stoichiometry of a chemical. Parameters ---------- atoms : dict Dictionary of atoms and their counts. MW : float, optional Molecular weight of chemical. Notes ----- The stoichiometry is given by: .. math:: C_c H_h O_o N_n S_s Br_b I_i Cl_x F_f P_p + kO_2 -> cCO_2 + \frac{b}{2}Br_2 + \frac{i}{2}I + xHCl + fHF + sSO_2 + \frac{n}{2}N_2 + \frac{p}{4}P_4O_{10} +\frac{h + x + f}{2}H_2O k = c + s + \frac{h}{4} + \frac{5P}{4} - \frac{x + f}{4} - \frac{o}{2} """ combustion_atoms = {i:atoms.get(i, 0) for i in combustible_elements} C, H, N, O, S, Br, I, Cl, F, P = combustion_atoms.values() MW = MW or compute_molecular_weight(atoms) Ash = MW - compute_molecular_weight(combustion_atoms) stoichiometry = { 'O2': (Cl + F)/4. + O/2. - (C + S + H/4. + 5*P/4.), 'CO2': C, 'Br2': Br/2., 'I2': I/2., 'HCl': Cl, 'HF': F, 'SO2': S, 'N2': N/2., 'P4O10': P/4., 'H2O': (H - Cl - F)/2., 'Ash': Ash if Ash > 0.01 else 0 } return {i: j for i,j in stoichiometry.items() if j}
def estimate_HHV_from_stoichiometry(stoichiometry, Hf): """Estimate the higher heating value [HHV; in J/mol] given a dictionary of the combustion stoichiometry and the heat of formation of the chemical.""" return sum([stoichiometry[chem] * Hf_combustion_products[chem] for chem in stoichiometry]) - Hf # TODO: Continue adding more methods for estimating HHV
[docs]def estimate_HHV_modified_Dulong(atoms: dict, MW: float=None, check_oxygen_content: bool=False): r""" Return higher heating value [HHV; in J/mol] based on the modified Dulong's equation. Parameters ---------- atoms : dict Dictionary of atoms and their counts. MW : float, optional Molecular weight of chemical. Notes ----- The heat of combustion in J/mol is given by Dulong's equation [1]_: .. math:: Hc (J/mol) = MW \cdot (338C + 1428(H - O/8)+ 95S) This equation is only good for <10 wt. % Oxygen content. Variables C, H, O, and S are atom weight fractions. References ---------- .. [1] Brown et al., Energy Fuels 2010, 24 (6), 3639–3646. """ mass_fractions = compute_mass_fractions(atoms, MW) C = mass_fractions.get('C', 0) H = mass_fractions.get('H', 0) O = mass_fractions.get('O', 0) S = mass_fractions.get('S', 0) if check_oxygen_content: assert O <= 0.105, ( f"Dulong's formula is only valid at " "10 wt. % Oxygen or less ({O:.0%} given)") return - MW * (338*C + 1428*(H - O/8)+ 95*S)
def estimate_LHV(HHV, N_H2O): """Estimate the lower heating value [LHV; in J/mol] of a chemical given the higher heating value [HHV; in J/mol] and the number of water molecules formed per molecule burned.""" return HHV + 44011.496 * N_H2O # %% Combustion reaction
[docs]class CombustionData: """ Create a CombustionData object that contains the stoichiometry coefficients of the reactants and products and the lower and higher heating values of a chemical [LHV, HHV; in J/mol]. Parameters ---------- stoichiometry : dict[str: float] Stoichiometry coefficients of the reactants and products. LHV : float Lower heating value [J/mol]. HHV : float Higher heating value [J/mol]. Hf : float Heat of formation [J/mol]. """ __slots__ = ('stoichiometry', 'HHV', 'LHV', 'Hf', 'MW') def __init__(self, stoichiometry, LHV, HHV, Hf, MW): #: dict[str: float] Stoichiometry coefficients of the reactants and products self.stoichiometry = stoichiometry #: [float] Lower heating value [J/mol] self.LHV = LHV #: [float] Higher heating value [J/mol] self.HHV = HHV #: [float] Heat of formation [J/mol] self.Hf = Hf #: [float] Molecular weight [g/mol] self.MW = MW
[docs] @classmethod def from_chemical_data(cls, atoms, CAS=None, MW=None, Hf=None, method='Stoichiometry'): r''' Return a CombustionData object that contains the stoichiometry coefficients of the reactants and products and the lower and higher heating values [LHV, HHV; in J/mol]. Parameters ---------- atoms : dict Dictionary of atoms and their counts. CAS : str, optional CAS of chemical. MW : float, optional Molecular weight of chemical [g/mol]. Hf : float, optional Heat of formation of given chemical [J/mol]. Required if method is "Stoichiometry". method : "Stoichiometry" or "Dulong" Method to estimate LHV and HHV. Notes ----- Default heats of formation for chemicals are at 298 K, 1 atm. The combustion reaction is based on the following equation: .. math:: C_c H_h O_o N_n S_s Br_b I_i Cl_x F_f P_p + kO_2 -> cCO_2 + \frac{b}{2}Br_2 + \frac{i}{2}I + xHCl + fHF + sSO_2 + \frac{n}{2}N_2 + \frac{p}{4}P_4O_{10} +\frac{h + x + f}{2}H_2O k = c + s + \frac{h}{4} + \frac{5P}{4} - \frac{x + f}{4} - \frac{o}{2} If the method is "Stoichiometry", the HHV is found using through an energy balance on the reaction (i.e. heat of reaction). If the method is "Dulong", Dulong's equation is used [1]_: .. math:: Hc (J/mol) = MW \cdot (338C + 1428(H - O/8)+ 95S) The LHV is calculated as follows: .. math:: LHV = HHV + H_{vap} \cdot H_2O H_{vap} = 44011.496 \frac{J}{mol H_2O} H_2O = \frac{mol H_2O}{mol} Examples -------- Liquid methanol burning: >>> from thermosteam.functors.combustion import CombustionData >>> CombustionData.from_chemical_data({'H': 4, 'C': 1, 'O': 1}, Hf=-239100) CombustionData(stoichiometry={'O2': -1.5, 'CO2': 1, 'H2O': 2.0}, LHV=-6.38e+05, HHV=-7.26e+05, Hf=-2.39e+05) ''' if CAS in combustion_products: if CAS == '7732-18-5': return cls({}, 44011.496, 0, Hf, 18.01528) if atoms and not MW: MW = compute_molecular_weight(atoms) return cls({}, 0, 0, Hf, MW) if not atoms: raise RuntimeError('no atomic formula to estimate combustion stoichiometry') MW = MW or compute_molecular_weight(atoms) stoichiometry = get_combustion_stoichiometry(atoms, MW) if method == 'Stoichiometry': if Hf is None: raise RuntimeError('no heat of formation to estimate ' 'heat of combustion from stoichiometry') HHV = estimate_HHV_from_stoichiometry(stoichiometry, Hf) N_H2O = stoichiometry.get('H2O', 0) elif method == 'Dulong': HHV = estimate_HHV_modified_Dulong(atoms, MW) Hf = HHV - estimate_HHV_from_stoichiometry(stoichiometry, 0) N_H2O = atoms.get('H', 0) / 2 else: raise ValueError(f"invalid method {repr(method)}; method must be either 'Stoichiometry' or 'Dulong'") LHV = estimate_LHV(HHV, N_H2O) return cls(stoichiometry, LHV, HHV, Hf, MW)
def __repr__(self): return f"{type(self).__name__}(stoichiometry={repr(self.stoichiometry)}, LHV={self.LHV:.3g}, HHV={self.HHV:.3g}, Hf={self.Hf:.3g}, MW={self.MW:.3g})"