Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add get/set equivalenceRatio/mixtureFraction functions #851

Merged
merged 6 commits into from
Jul 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions include/cantera/thermo/Phase.h
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,24 @@ class Phase
m_root = root;
}

//! Converts a compositionMap to a vector with entries for each species
//! Species that are not specified are set to zero in the vector
/*!
* @param[in] comp compositionMap containing the mixture composition
* @return vector with length m_kk
*/
vector_fp getCompositionFromMap(const compositionMap& comp) const;

//! Converts a mixture composition from mole fractions to mass fractions
//! @param[in] Y mixture composition in mass fractions (length m_kk)
//! @param[out] X mixture composition in mole fractions (length m_kk)
void massFractionsToMoleFractions(const double* Y, double* X) const;

//! Converts a mixture composition from mass fractions to mole fractions
//! @param[in] X mixture composition in mole fractions (length m_kk)
//! @param[out] Y mixture composition in mass fractions (length m_kk)
void moleFractionsToMassFractions(const double* X, double* Y) const;

protected:
//! Ensure that phase is compressible.
//! An error is raised if the state is incompressible
Expand Down
206 changes: 206 additions & 0 deletions include/cantera/thermo/ThermoPhase.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ const int cSS_CONVENTION_VPSS = 1;
const int cSS_CONVENTION_SLAVE = 2;
//@}

//! Differentiate between mole fractions and mass fractions for input mixture composition
enum class ThermoBasis
{
mass,
molar
};
//@}

//! Base class for a phase with thermodynamic properties.
/*!
* Class ThermoPhase is the base class for the family of classes that represent
Expand Down Expand Up @@ -1172,7 +1180,190 @@ class ThermoPhase : public Phase

//@}

//! @name Set Mixture Composition by Mixture Fraction
//! @{

//! Set the mixture composition according to the
//! mixture fraction = kg fuel / (kg oxidizer + kg fuel)
/*!
* Fuel and oxidizer compositions are given either as
* mole fractions or mass fractions (specified by `basis`)
* and do not need to be normalized. Pressure and temperature are
* kept constant. Elements C, S, H and O are considered for the oxidation.
*
* @param mixFrac mixture fraction (between 0 and 1)
* @param fuelComp composition of the fuel
* @param oxComp composition of the oxidizer
* @param basis either ThermoPhase::molar or ThermoPhase::mass.
* Fuel and oxidizer composition are interpreted
* as mole or mass fractions (default: molar)
*/
void setMixtureFraction(double mixFrac, const double* fuelComp, const double* oxComp,
ThermoBasis basis = ThermoBasis::molar);
//! @copydoc ThermoPhase::setMixtureFraction
void setMixtureFraction(double mixFrac, const std::string& fuelComp, const std::string& oxComp,
ThermoBasis basis = ThermoBasis::molar);
//! @copydoc ThermoPhase::setMixtureFraction
void setMixtureFraction(double mixFrac, const compositionMap& fuelComp, const compositionMap& oxComp,
ThermoBasis basis = ThermoBasis::molar);
//@}

//! @name Compute Mixture Fraction
//! @{

//! Compute the mixture fraction = kg fuel / (kg oxidizer + kg fuel) for
//! the current mixture given fuel and oxidizer compositions.
/*!
* Fuel and oxidizer compositions are given either as
* mole fractions or mass fractions (specified by `basis`)
* and do not need to be normalized.
* The mixture fraction \f$ Z \f$ can be computed from a single element
* \f[ Z_m = \frac{Z_{\mathrm{mass},m}-Z_{\mathrm{mass},m,\mathrm{ox}}}
* {Z_{\mathrm{mass},\mathrm{fuel}}-Z_{\mathrm{mass},m,\mathrm{ox}}} \f] where
* \f$ Z_{\mathrm{mass},m} \f$ is the elemental mass fraction of element m
* in the mixture, and \f$ Z_{\mathrm{mass},m,\mathrm{ox}} \f$ and
* \f$ Z_{\mathrm{mass},m,\mathrm{fuel}} \f$ are the elemental mass fractions
* of the oxidizer and fuel, or from the Bilger mixture fraction,
* which considers the elements C, S, H and O (R. W. Bilger, "Turbulent jet
* diffusion flames," Prog. Energy Combust. Sci., 109-131 (1979))
* \f[ Z_{\mathrm{Bilger}} = \frac{\beta-\beta_{\mathrm{ox}}}
* {\beta_{\mathrm{fuel}}-\beta_{\mathrm{ox}}} \f]
* with \f$ \beta = 2\frac{Z_C}{M_C}+2\frac{Z_S}{M_S}+\frac{1}{2}\frac{Z_H}{M_H}
* -\frac{Z_O}{M_O} \f$
* and \f$ M_m \f$ the atomic weight of element \f$ m \f$.
*
* @param fuelComp composition of the fuel
* @param oxComp composition of the oxidizer
* @param basis either ThermoPhase::mole or ThermoPhase::mass.
* Fuel and oxidizer composition are interpreted
* as mole or mass fractions (default: molar)
* @param element either "Bilger" to compute the mixture fraction
* in terms of the Bilger mixture fraction, or
* an element name, to compute the mixture fraction
* bsaed on a single element (default: "Bilger")
* @returns mixture fraction (kg fuel / kg mixture)
*/
double mixtureFraction(const double* fuelComp, const double* oxComp,
ThermoBasis basis = ThermoBasis::molar, const std::string& element = "Bilger") const;
//! @copydoc ThermoPhase::mixtureFraction
double mixtureFraction(const std::string& fuelComp, const std::string& oxComp,
ThermoBasis basis = ThermoBasis::molar, const std::string& element = "Bilger") const;
//! @copydoc ThermoPhase::mixtureFraction
double mixtureFraction(const compositionMap& fuelComp, const compositionMap& oxComp,
ThermoBasis basis = ThermoBasis::molar, const std::string& element = "Bilger") const;
//@}

//! @name Set Mixture Composition by Equivalence Ratio
//! @{

//! Set the mixture composition according to the equivalence ratio.
/*!
* Fuel and oxidizer compositions are given either as
* mole fractions or mass fractions (specified by `basis`)
* and do not need to be normalized. Pressure and temperature are
* kept constant. Elements C, S, H and O are considered for the oxidation.
*
* @param phi equivalence ratio
* @param fuelComp composition of the fuel
* @param oxComp composition of the oxidizer
* @param basis either ThermoPhase::mole or ThermoPhase::mass.
* Fuel and oxidizer composition are interpreted
* as mole or mass fractions (default: molar)
*/
void setEquivalenceRatio(double phi, const double* fuelComp, const double* oxComp, ThermoBasis basis = ThermoBasis::molar);
//! @copydoc ThermoPhase::setEquivalenceRatio
void setEquivalenceRatio(double phi, const std::string& fuelComp, const std::string& oxComp, ThermoBasis basis = ThermoBasis::molar);
//! @copydoc ThermoPhase::setEquivalenceRatio
void setEquivalenceRatio(double phi, const compositionMap& fuelComp, const compositionMap& oxComp, ThermoBasis basis = ThermoBasis::molar);
//@}

//! @name Compute Equivalence Ratio
//! @{

//! Compute the equivalence ratio for the current mixture
//! given the compositions of fuel and oxidizer
/*!
* The equivalence ratio \f$ \phi \f$ is computed from
* \f[ \phi = \frac{Z}{1-Z}\frac{1-Z_{\mathrm{st}}}{Z_{\mathrm{st}}} \f]
* where \f$ Z \f$ is the Bilger mixture fraction of the mixture
* given the specified fuel and oxidizer compositions
* \f$ Z_{\mathrm{st}} \f$ is the mixture fraction at stoichiometric
* conditions. Fuel and oxidizer compositions are given either as
speth marked this conversation as resolved.
Show resolved Hide resolved
* mole fractions or mass fractions (specified by `basis`)
* and do not need to be normalized.
* Elements C, S, H and O are considered for the oxidation.
* If fuel and oxidizer composition are unknown or not specified,
* use the version that takes no arguments.
*
* @param fuelComp composition of the fuel
* @param oxComp composition of the oxidizer
* @param basis either ThermoPhase::mole or ThermoPhase::mass.
* Fuel and oxidizer composition are interpreted
* as mole or mass fractions (default: molar)
* @returns equivalence ratio
* @see mixtureFraction for the definition of the Bilger mixture fraction
* @see equivalenceRatio() for the computation of \f$ \phi \f$ without arguments
*/
double equivalenceRatio(const double* fuelComp, const double* oxComp, ThermoBasis basis = ThermoBasis::molar) const;
//! @copydoc ThermoPhase::equivalenceRatio
double equivalenceRatio(const std::string& fuelComp, const std::string& oxComp, ThermoBasis basis = ThermoBasis::molar) const;
//! @copydoc ThermoPhase::equivalenceRatio
double equivalenceRatio(const compositionMap& fuelComp, const compositionMap& oxComp, ThermoBasis basis = ThermoBasis::molar) const;
//@}

//! Compute the equivalence ratio for the current mixture
//! from available oxygen and required oxygen
/*!
* Computes the equivalence ratio \f$ \phi \f$ from
* \f[ \phi = \frac{Z_{\mathrm{mole},C} + Z_{\mathrm{mole},S} + \frac{1}{4}Z_{\mathrm{mole},H}}
* {\frac{1}{2}Z_{\mathrm{mole},O}} \f]
* where \f$ Z_{\mathrm{mole},m} \f$ is the elemental mole fraction
* of element \f$ m \f$. In this special case, the equivalence ratio
* is independent of a fuel or oxidizer composition because it only
* considers the locally available oxygen compared to the required oxygen
* for complete oxidation. It is the same as assuming that the oxidizer
* only contains O (and inert elements) and the fuel contains only
* H, C and S (and inert elements). If either of these conditions is
* not met, use the version of this functions which takes the fuel and
* oxidizer compositions as input
*
* @returns equivalence ratio
* @see equivalenceRatio compute the equivalence ratio from specific
* fuel and oxidizer compositions
*/
double equivalenceRatio() const;

//! @name Compute Stoichiometric Air to Fuel Ratio
//! @{

//! Compute the stoichiometric air to fuel ratio (kg oxidizer / kg fuel)
//! given fuel and oxidizer compositions.
/*!
* Fuel and oxidizer compositions are given either as
* mole fractions or mass fractions (specified by `basis`)
* and do not need to be normalized.
* Elements C, S, H and O are considered for the oxidation.
speth marked this conversation as resolved.
Show resolved Hide resolved
* Note that the stoichiometric air to fuel ratio \f$ \mathit{AFR}_{\mathrm{st}} \f$
* does not depend on the current mixture composition. The current
* air to fuel ratio can be computed from \f$ \mathit{AFR} = \mathit{AFR}_{\mathrm{st}}/\phi \f$
* where \f$ \phi \f$ is the equivalence ratio of the current mixture
*
* @param fuelComp composition of the fuel
* @param oxComp composition of the oxidizer
* @param basis either ThermoPhase::mole or ThermoPhase::mass.
* Fuel and oxidizer composition are interpreted
* as mole or mass fractions (default: molar)
* @returns Stoichiometric Air to Fuel Ratio (kg oxidizer / kg fuel)
*/
double stoichAirFuelRatio(const double* fuelComp, const double* oxComp, ThermoBasis basis = ThermoBasis::molar) const;
//! @copydoc ThermoPhase::stoichAirFuelRatio
double stoichAirFuelRatio(const std::string& fuelComp, const std::string& oxComp, ThermoBasis basis = ThermoBasis::molar) const;
//! @copydoc ThermoPhase::stoichAirFuelRatio
double stoichAirFuelRatio(const compositionMap& fuelComp, const compositionMap& oxComp, ThermoBasis basis = ThermoBasis::molar) const;
//@}

private:

//! Carry out work in HP and UV calculations.
/*!
* @param h Specific enthalpy or internal energy (J/kg)
Expand Down Expand Up @@ -1200,6 +1391,21 @@ class ThermoPhase : public Phase
//! Sets the temperature and (if set_p is true) the pressure.
void setState_conditional_TP(doublereal t, doublereal p, bool set_p);

//! Helper function for computing the amount of oxygen required for complete oxidation.
/*!
* @param y array of (possibly non-normalized) mass fractions (length m_kk)
* @returns amount of required oxygen in kmol O / kg mixture
*/
double o2Required(const double* y) const;

//! Helper function for computing the amount of oxygen
//! available in the current mixture.
/*!
* @param y array of (possibly non-normalized) mass fractions (length m_kk)
* @returns amount of O in kmol O / kg mixture
*/
double o2Present(const double* y) const;

public:
/**
* @name Chemical Equilibrium
Expand Down
11 changes: 11 additions & 0 deletions interfaces/cython/cantera/_cantera.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ cdef extern from "cantera/base/Solution.h" namespace "Cantera":
cdef shared_ptr[CxxSolution] CxxNewSolution "Cantera::Solution::create" ()


cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera":
ctypedef enum ThermoBasis:
mass "Cantera::ThermoBasis::mass",
molar "Cantera::ThermoBasis::molar"

cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera":
cdef cppclass CxxThermoPhase "Cantera::ThermoPhase":
CxxThermoPhase()
Expand Down Expand Up @@ -268,6 +273,12 @@ cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera":
void setState_Psat(double P, double x) except +translate_exception
void setState_TPQ(double T, double P, double Q) except +translate_exception

void setMixtureFraction(double mixFrac, const double* fuelComp, const double* oxComp, ThermoBasis basis) except +translate_exception
double mixtureFraction(const double* fuelComp, const double* oxComp, ThermoBasis basis, string element) except +translate_exception
void setEquivalenceRatio(double phi, const double* fuelComp, const double* oxComp, ThermoBasis basis) except +translate_exception
double equivalenceRatio(const double* fuelComp, const double* oxComp, ThermoBasis basis) except +translate_exception
double equivalenceRatio() except +translate_exception
double stoichAirFuelRatio(const double* fuelComp, const double* oxComp, ThermoBasis basis) except +translate_exception

cdef extern from "cantera/thermo/IdealGasPhase.h":
cdef cppclass CxxIdealGasPhase "Cantera::IdealGasPhase"
Expand Down
53 changes: 53 additions & 0 deletions interfaces/cython/cantera/examples/thermo/equivalenceRatio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""
This example demonstrates how to set a mixture according to equivalence ratio
and mixture fraction.

Requires: cantera >= 2.5.0
"""

import cantera as ct

gas = ct.Solution('gri30.yaml')

# fuel and oxidizer compositions
fuel = "CH4"
oxidizer = "O2:0.21,N2:0.79"

gas.TP = 300, ct.one_atm

# set the mixture composition according to the stoichiometric mixture
# (equivalence ratio = 1)
gas.set_equivalence_ratio(1, fuel, oxidizer)

# This function can be used to compute the equivalence ratio for any mixture.
# An optional argument "basis" indicates if fuel and oxidizer compositions are
# provided in terms of mass or mole fractions. Default is mole fractions.
# If fuel and oxidizer are given in mass fractions, use basis='mass'
phi = gas.equivalence_ratio(fuel, oxidizer)
print("phi = {:1.3f}".format(phi))

# The equivalence ratio can also be computed from the elemental composition
# assuming that there is no oxygen in the fuel and no C,H and S elements
# in the oxidizer so that the composition of fuel and oxidizer can be omitted
phi = gas.equivalence_ratio()

# In this example, the result is the same as above
print("phi = {:1.3f}".format(phi))

# the mixture fraction Z can be computed as follows:
Z = gas.mixture_fraction(fuel, oxidizer)
print("Z = {:1.3f}".format(Z))

# The mixture fraction is kg fuel / (kg fuel + kg oxidizer). Since the fuel in
# this example is pure methane and the oxidizer is air, the mixture fraction
# is the same as the mass fraction of methane in the mixture
print("mass fraction of CH4 = {:1.3f}".format(gas["CH4"].Y[0]))

# Mixture fraction and equivalence ratio are invariant to the reaction progress.
# For example, they stay constant if the mixture composition changes to the burnt
# state
gas.equilibrate('HP')
phi_burnt = gas.equivalence_ratio(fuel, oxidizer)
Z_burnt = gas.mixture_fraction(fuel, oxidizer)
print("phi(burnt) = {:1.3f}".format(phi_burnt))
print("Z(burnt) = {:1.3f}".format(Z))
28 changes: 24 additions & 4 deletions interfaces/cython/cantera/onedim.py
Original file line number Diff line number Diff line change
Expand Up @@ -1361,7 +1361,8 @@ def strain_rate(self, definition, fuel=None, oxidizer='O2', stoich=None):

def mixture_fraction(self, m):
r"""
Compute the mixture fraction based on element *m*
Compute the mixture fraction based on element *m* or from the
Bilger mixture fraction (m="Bilger")

The mixture fraction is computed from the elemental mass fraction of
element *m*, normalized by its values on the fuel and oxidizer
Expand All @@ -1372,15 +1373,34 @@ def mixture_fraction(self, m):
{Z_{\mathrm{mass},m}(z_\mathrm{fuel}) -
Z_{\mathrm{mass},m}(z_\mathrm{oxidizer})}

or from the Bilger mixture fraction:

.. math:: Z = \frac{\beta-\beta_{\mathrm{oxidizer}}}
{\beta_{\mathrm{fuel}}-\beta_{\mathrm{oxidizer}}}

with

.. math:: \beta = 2\frac{Z_C}{M_C}+2\frac{Z_S}{M_S}
+\frac{1}{2}\frac{Z_H}{M_H}-\frac{Z_O}{M_O}

:param m:
The element based on which the mixture fraction is computed,
may be specified by name or by index
may be specified by name or by index, or "Bilger" for the
Bilger mixture fraction, which considers the elements C,
H, S, and O

>>> f.mixture_fraction('H')
>>> f.mixture_fraction('Bilger')
"""
emf = self.elemental_mass_fraction(m)
return (emf - emf[-1]) / (emf[0] - emf[-1])

Yf = [self.solution(k, 0) for k in self.gas.species_names]
Yo = [self.solution(k, self.flame.n_points-1) for k in self.gas.species_names]

vals = np.empty(self.flame.n_points)
for i in range(self.flame.n_points):
self.set_gas_state(i)
vals[i] = self.gas.mixture_fraction(Yf, Yo, 'mass', m)
return vals

class ImpingingJet(FlameBase):
"""An axisymmetric flow impinging on a surface at normal incidence."""
Expand Down
6 changes: 5 additions & 1 deletion interfaces/cython/cantera/test/test_onedim.py
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,11 @@ def test_mixture_fraction(self):
self.assertNear(Z[-1], 0.0)
self.assertTrue(all(Z >= 0))
self.assertTrue(all(Z <= 1.0))

Z = self.sim.mixture_fraction('Bilger')
self.assertNear(Z[0], 1.0)
self.assertNear(Z[-1], 0.0)
self.assertTrue(all(Z >= 0))
self.assertTrue(all(Z <= 1.0))

class TestCounterflowPremixedFlame(utilities.CanteraTest):
# Note: to re-create the reference file:
Expand Down
Loading