def setup_reaction_parameters(self): r"""Setup parameters for reaction (gas name aliases and LHV).""" self.fuel_list = [] fuels = [ 'methane', 'ethane', 'propane', 'butane', 'hydrogen', 'nDodecane' ] for f in fuels: self.fuel_list += [ x for x in self.nw_fluids if x in [a.replace(' ', '') for a in CP.get_aliases(f)] ] if len(self.fuel_list) == 0: msg = ('Your network\'s fluids do not contain any fuels, that are ' 'available for the component ' + self.label + ' of type ' + self.component() + '. Available fuels are: ' + ', '.join(fuels) + '.') logging.error(msg) raise TESPyComponentError(msg) else: msg = ('The fuels for component ' + self.label + ' of type ' + self.component() + ' are: ' + ', '.join(self.fuel_list) + '.') logging.debug(msg) for fluid in ['o2', 'co2', 'h2o', 'n2']: try: setattr(self, fluid, [ x for x in self.nw_fluids if x in [ a.replace(' ', '') for a in CP.get_aliases(fluid.upper()) ] ][0]) except IndexError: msg = ('The component ' + self.label + ' (class ' + self.__class__.__name__ + ') requires that the fluid ' '[fluid] is in the network\'s list of fluids.') aliases = ', '.join(CP.get_aliases(fluid.upper())) msg = msg.replace( '[fluid]', fluid.upper() + ' (aliases: ' + aliases + ')') logging.error(msg) raise TESPyComponentError(msg) self.fuels = {} for f in self.fuel_list: self.fuels[f] = {} structure = fluid_structure(f) for el in ['C', 'H', 'O']: if el in structure: self.fuels[f][el] = structure[el] else: self.fuels[f][el] = 0 self.fuels[f]['LHV'] = self.calc_lhv(f)
def calc_lhv(self): r""" Calculate the lower heating value of the combustion chambers fuel. Returns ------- val : float Lower heating value of the combustion chambers fuel. .. math:: LHV = \sum_{fuels} \left(-\frac{\sum_i {\Delta H_f^0}_i - \sum_j {\Delta H_f^0}_j } {M_{fuel}} \cdot x_{fuel} \right)\\ \forall i \in \text{reation products},\\ \forall j \in \text{reation educts},\\ \forall fuel \in \text{fuels},\\ \Delta H_f^0: \text{molar formation enthalpy},\\ x_{fuel}: \text{mass fraction of fuel in fuel mixture} """ hf = {} hf['hydrogen'] = 0 hf['methane'] = -74.85 hf['ethane'] = -84.68 hf['propane'] = -103.8 hf['butane'] = -124.51 hf['O2'] = 0 hf['CO2'] = -393.5 # water (gaseous) hf['H2O'] = -241.8 lhv = 0 for f, x in self.fuel.val.items(): molar_masses[f] = CP.PropsSI('M', f) fl = set(list(hf.keys())).intersection( set([a.replace(' ', '') for a in CP.get_aliases(f)])) if len(fl) == 0: continue if list(fl)[0] in self.fuels(): structure = fluid_structure(f) n = {} for el in ['C', 'H', 'O']: if el in structure.keys(): n[el] = structure[el] else: n[el] = 0 lhv += (-(n['H'] / 2 * hf['H2O'] + n['C'] * hf['CO2'] - ((n['C'] + n['H'] / 4) * hf['O2'] + hf[list(fl)[0]])) / molar_masses[f] * 1000) * x return lhv
def stoich_flue_gas(self, nw): r""" Calculate the fluid composition of the stoichiometric flue gas. - uses one mole of fuel as reference quantity and :math:`\lambda=1` for stoichiometric flue gas calculation (no oxygen in flue gas) - calculate molar quantities of (reactive) fuel components to determine water and carbondioxide mass fraction in flue gas - calculate required molar quantity for oxygen and required fresh air mass - calculate residual mass fractions for non reactive components of fresh air in the flue gas - calculate flue gas fluid composition - generate custom fluid porperties Reactive components in fuel .. math:: m_{fuel} = \frac{1}{M_{fuel}}\\ m_{CO_2} = \sum_{i} \frac{x_{i} \cdot m_{fuel} \cdot num_{C,i} \cdot M_{CO_{2}}}{M_{i}}\\ m_{H_{2}O} = \sum_{i} \frac{x_{i} \cdot m_{fuel} \cdot num_{H,i} \cdot M_{H_{2}O}}{2 \cdot M_{i}}\\ \forall i \in \text{fuels in fuel vector},\\ num = \text{number of atoms in molecule} Other components of fuel vector .. math:: m_{fg,j} = x_{j} \cdot m_{fuel}\\ \forall j \in \text{non fuels in fuel vecotr, e.g. } CO_2,\\ m_{fg,j} = \text{mass of fluid component j in flue gas} Non-reactive components in air .. math:: n_{O_2} = \left( \frac{m_{CO_2}}{M_{CO_2}} + \frac{m_{H_{2}O}} {0,5 \cdot M_{H_{2}O}} \right) \cdot \lambda,\\ n_{O_2} = \text{mol of oxygen required}\\ m_{air} = \frac{n_{O_2} \cdot M_{O_2}}{x_{O_{2}, air}},\\ m_{air} = \text{required total air mass}\\ m_{fg,j} = x_{j, air} \cdot m_{air}\\ m_{fg, O_2} = 0,\\ m_{fg,j} = \text{mass of fluid component j in flue gas} Flue gas composition .. math:: x_{fg,j} = \frac{m_{fg, j}}{m_{air} + m_{fuel}} Parameters ---------- nw : tespy.networks.network.Network TESPy network to generate stoichiometric flue gas for. """ lamb = 1 n_fuel = 1 m_fuel = 1 / molar_mass_flow(self.fuel.val) * n_fuel m_fuel_fg = m_fuel m_co2 = 0 m_h2o = 0 molar_masses[self.h2o] = CP.PropsSI('M', self.h2o) molar_masses[self.co2] = CP.PropsSI('M', self.co2) molar_masses[self.o2] = CP.PropsSI('M', self.o2) self.fg = {} self.fg[self.co2] = 0 self.fg[self.h2o] = 0 for f, x in self.fuel.val.items(): fl = set(list(self.fuels())).intersection( set([a.replace(' ', '') for a in CP.get_aliases(f)])) if len(fl) == 0: if f in self.fg.keys(): self.fg[f] += x * m_fuel else: self.fg[f] = x * m_fuel else: n_fluid = x * m_fuel / molar_masses[f] m_fuel_fg -= n_fluid * molar_masses[f] structure = fluid_structure(f) n = {} for el in ['C', 'H', 'O']: if el in structure.keys(): n[el] = structure[el] else: n[el] = 0 m_co2 += n_fluid * n['C'] * molar_masses[self.co2] m_h2o += n_fluid * n['H'] / 2 * molar_masses[self.h2o] self.fg[self.co2] += m_co2 self.fg[self.h2o] += m_h2o n_o2 = (m_co2 / molar_masses[self.co2] + 0.5 * m_h2o / molar_masses[self.h2o]) * lamb m_air = n_o2 * molar_masses[self.o2] / self.air.val[self.o2] self.air_min = m_air / m_fuel for f, x in self.air.val.items(): if f != self.o2: if f in self.fg.keys(): self.fg[f] += m_air * x else: self.fg[f] = m_air * x m_fg = m_fuel + m_air for f in self.fg.keys(): self.fg[f] /= m_fg if not self.path.is_set: self.path.val = None TESPyFluid( self.fuel_alias.val, self.fuel.val, [1000, nw.p_range_SI[1]], path=self.path.val) TESPyFluid( self.fuel_alias.val + '_fg', self.fg, [1000, nw.p_range_SI[1]], path=self.path.val) msg = ( 'Generated lookup table for ' + self.fuel_alias.val + ' and for ' 'stoichiometric flue gas at component ' + self.label + '.') logging.debug(msg) if self.air_alias.val not in ['Air', 'air']: TESPyFluid( self.air_alias.val, self.air.val, [1000, nw.p_range_SI[1]], path=self.path.val) msg = ('Generated lookup table for ' + self.air_alias.val + ' at stoichiometric combustion chamber ' + self.label + '.') else: msg = ('Using CoolProp air at stoichiometric combustion chamber ' + self.label + '.') logging.debug(msg)