From 2e3aaadd450e6e1e9da294273d3d2986ce9ccfc5 Mon Sep 17 00:00:00 2001 From: bangshiuh Date: Sat, 4 Dec 2021 15:23:15 -0500 Subject: [PATCH 01/12] [data] add ET_test.yaml --- test/data/ET_test.yaml | 76 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 test/data/ET_test.yaml diff --git a/test/data/ET_test.yaml b/test/data/ET_test.yaml new file mode 100644 index 0000000000..a01b69cd9b --- /dev/null +++ b/test/data/ET_test.yaml @@ -0,0 +1,76 @@ +description: |- + This is a simple data file for testing purpose only! + +units: {length: cm, quantity: mole, activation-energy: K} + +phases: +- name: gas + thermo: ideal-gas + elements: [O, N, E] + species: + - species: [O2, E, O2^-] + skip-undeclared-third-bodies: true + kinetics: gas + reactions: + - reactions: declared-species + transport: ionized-gas + state: + T: 300.0 + P: 1.01325e+05 + +species: +- name: O2 + composition: {O: 2} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 3500.0] + data: + - [3.78245636, -2.99673416e-03, 9.84730201e-06, -9.68129509e-09, 3.24372837e-12, + -1063.94356, 3.65767573] + - [3.28253784, 1.48308754e-03, -7.57966669e-07, 2.09470555e-10, -2.16717794e-14, + -1088.45772, 5.45323129] + transport: + model: gas + geometry: linear + diameter: 3.46 + well-depth: 107.4 + polarizability: 1.131 + rotational-relaxation: 3.8 + note: TPIS89 +- name: E + composition: {E: 1} + thermo: + model: NASA7 + temperature-ranges: [200.0, 1000.0, 6000.0] + data: + - [2.5, 0.0, 0.0, 0.0, 0.0, -745.375, -11.7246902] + - [2.5, 0.0, 0.0, 0.0, 0.0, -745.375, -11.7246902] + transport: + model: gas + geometry: atom + diameter: 2.05 + well-depth: 145.0 + polarizability: 0.667 + note: gas L10/92 +- name: O2^- + composition: {E: 1, O: 2} + thermo: + model: NASA7 + temperature-ranges: [298.15, 1000.0, 6000.0] + data: + - [3.66442522, -9.28741138e-04, 6.45477082e-06, -7.7470338e-09, 2.93332662e-12, + -6870.76983, 4.35140681] + - [3.95666294, 5.98141823e-04, -2.12133905e-07, 3.63267581e-11, -2.24989228e-15, + -7062.87229, 2.27871017] + transport: + model: gas + geometry: linear + diameter: 3.33 + well-depth: 136.5 + polarizability: 1.424 + note: L4/89 + +reactions: +- equation: E + O2 + O2 => O2^- + O2 # Kossyi (1992) Eq.45 + type: two-temperature-plasma + rate-constant: {A: 1.523e+27, b: -1.0, Ea_T: -100, Ea_Te: 700} From daf2766b428563942bfadf62664339e29a23d045 Mon Sep 17 00:00:00 2001 From: bangshiuh Date: Sun, 5 Dec 2021 10:18:22 -0500 Subject: [PATCH 02/12] [thermo, interface, test] add Te --- include/cantera/thermo/Phase.h | 21 +++++++++++++++++++ interfaces/cython/cantera/_cantera.pxd | 2 ++ interfaces/cython/cantera/test/test_thermo.py | 5 +++++ interfaces/cython/cantera/thermo.pyx | 8 +++++++ 4 files changed, 36 insertions(+) diff --git a/include/cantera/thermo/Phase.h b/include/cantera/thermo/Phase.h index 7457fac1f6..7f56972061 100644 --- a/include/cantera/thermo/Phase.h +++ b/include/cantera/thermo/Phase.h @@ -655,6 +655,12 @@ class Phase return m_temp; } + //! Electron Temperature (K) + //! @return The electron temperature of the phase + double electronTemperature() const { + return m_elec_temp; + } + //! Return the thermodynamic pressure (Pa). /*! * This method must be overloaded in derived classes. Within %Cantera, the @@ -718,6 +724,18 @@ class Phase "temperature must be positive. T = {}", temp); } } + + //! Set the internally stored electron temperature of the phase (K). + //! @param etemp Electron temperature in Kelvin + virtual void setElectronTemperature(const double etemp) { + if (etemp > 0) { + m_elec_temp = etemp; + } else { + throw CanteraError("Phase::setElectronTemperature", + "electron temperature must be positive. Te = {}", etemp); + } + } + //! @} //! @name Mean Properties @@ -966,6 +984,9 @@ class Phase doublereal m_temp; //!< Temperature (K). This is an independent variable + //! Electron Temperature (K). + double m_elec_temp; + //! Density (kg m-3). This is an independent variable except in the case //! of incompressible phases, where it has to be changed using the //! assignDensity() method. For compressible substances, the pressure is diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index bbea30cf36..33a12f9adb 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -255,6 +255,7 @@ cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera": # basic thermodynamic properties double temperature() except +translate_exception + double electronTemperature() except +translate_exception double pressure() except +translate_exception double density() except +translate_exception double molarDensity() except +translate_exception @@ -322,6 +323,7 @@ cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera": void setState_VH(double, double) except +translate_exception void setState_TH(double, double) except +translate_exception void setState_SH(double, double) except +translate_exception + void setElectronTemperature(double) except +translate_exception # molar thermodynamic properties: double enthalpy_mole() except +translate_exception diff --git a/interfaces/cython/cantera/test/test_thermo.py b/interfaces/cython/cantera/test/test_thermo.py index a2189bf241..78a45778cc 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -463,9 +463,11 @@ def check_setters(self, T1, rho1, Y1): s1 = self.phase.s u1 = self.phase.u v1 = self.phase.v + self.phase.Te = T1 def check_state(T, rho, Y): self.assertNear(self.phase.T, T) + self.assertNear(self.phase.Te, T) self.assertNear(self.phase.density, rho) self.assertArrayNear(self.phase.Y, Y) @@ -679,6 +681,9 @@ def check_getters(self): self.assertNear(D, self.phase.density) self.assertNear(P, self.phase.P) + Te = self.phase.Te + self.assertNear(Te, self.phase.Te) + def test_getState_mass(self): self.phase.TDY = 350.0, 0.7, 'H2:0.1, H2O2:0.1, AR:0.8' self.check_getters() diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index 9a4e5de2f5..be67f36057 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -1467,6 +1467,14 @@ cdef class ThermoPhase(_SolutionBase): self.thermo.setState_SV(S / self._mass_factor(), V / self._mass_factor()) + property Te: + """Get/Set electron Temperature [K].""" + def __get__(self): + return self.thermo.electronTemperature() + def __set__(self, value): + Te = value if value is not None else self.Te + self.thermo.setElectronTemperature(Te) + # partial molar / non-dimensional properties property partial_molar_enthalpies: """Array of species partial molar enthalpies [J/kmol].""" From 1e4e70630b1118cbab37ec996fc1c1bea92e2ec3 Mon Sep 17 00:00:00 2001 From: bangshiuh Date: Tue, 14 Dec 2021 17:34:00 -0500 Subject: [PATCH 03/12] [kinetics, interface] add ETempData and ETempRate --- include/cantera/kinetics/Arrhenius.h | 91 +++++++++++++++++++++++++ include/cantera/kinetics/ReactionData.h | 28 ++++++++ interfaces/cython/cantera/_cantera.pxd | 9 +++ interfaces/cython/cantera/reaction.pyx | 80 ++++++++++++++++++++++ src/kinetics/Arrhenius.cpp | 65 ++++++++++++++++++ src/kinetics/ReactionData.cpp | 35 ++++++++++ src/kinetics/ReactionRateFactory.cpp | 5 ++ 7 files changed, 313 insertions(+) diff --git a/include/cantera/kinetics/Arrhenius.h b/include/cantera/kinetics/Arrhenius.h index 20200a7e75..ef0a9d7ef5 100644 --- a/include/cantera/kinetics/Arrhenius.h +++ b/include/cantera/kinetics/Arrhenius.h @@ -195,6 +195,97 @@ class ArrheniusRate final : public ArrheniusBase }; +//! Two temperature plasma reaction rate type depends on both +//! gas temperature and electron temperature. +/*! + * The form of the two temperature plasma reaction rate coefficient is similar to an + * Arrhenius reaction rate coefficient. The temperature exponent (b) is applied to + * the electron temperature instead. In addition, the exponential term with + * activation energy for electron is included. + * + * \f[ + * k_f = A T_e^b \exp (-Ea_T/RT) \exp (-Ea_{T_e}/RT_e) + * \f] + * + * @ingroup arrheniusGroup + */ +class TwoTempPlasmaRate final : public ArrheniusBase +{ +public: + TwoTempPlasmaRate(); + + //! Constructor. + /*! + * @param A Pre-exponential factor. The unit system is (kmol, m, s); actual units + * depend on the reaction order and the dimensionality (surface or bulk). + * @param b Temperature exponent (non-dimensional) + * @param Ea Activation energy in energy units [J/kmol] + * @param EE Activation electron energy in energy units [J/kmol] + */ + TwoTempPlasmaRate(double A, double b, double Ea, double EE); + + unique_ptr newMultiRate() const override { + return unique_ptr( + new MultiBulkRate); + } + + //! Constructor based on AnyMap content + TwoTempPlasmaRate(const AnyMap& node, const UnitStack& rate_units={}) + : TwoTempPlasmaRate() + { + setParameters(node, rate_units); + } + + //! Identifier of reaction rate type + virtual const std::string type() const override { + return "two-temperature-plasma"; + } + + virtual void setRateParameters(const AnyValue& rate, + const UnitSystem& units, + const UnitStack& rate_units) override; + + //! Perform object setup based on AnyMap node information + /*! + * @param node AnyMap containing rate information + * @param rate_units Unit definitions specific to rate information + */ + virtual void setParameters(const AnyMap& node, const UnitStack& rate_units) override; + + virtual void getParameters(AnyMap& node) const override; + + double evalFromStruct(const TwoTempPlasmaData& shared_data) { + return m_A * std::exp(m_b * shared_data.logTe - + m_Ea_R * shared_data.recipT - + m_EE_R * shared_data.recipTe); + } + + //! Return the activation energy *Ea* [J/kmol] + double activationEnergy() const { + return m_Ea_R * GasConstant; + } + + //! Return the activation energy divided by the gas constant (i.e. the + //! activation temperature) [K] + double activationEnergy_R() const { + return m_Ea_R; + } + + //! Return the activation energy *Ea* [J/kmol] + double activationElectronEnergy() const { + return m_EE_R * GasConstant; + } + + //! Return the activation energy divided by the gas constant (i.e. the + //! activation temperature) [K] + double activationElectronEnergy_R() const { + return m_EE_R; + } +protected: + double m_EE_R; //!< Activation electron energy (in temperature units) +}; + + //! Blowers Masel reaction rate type depends on the enthalpy of reaction /** * The Blowers Masel approximation is written by Paul Blowers, diff --git a/include/cantera/kinetics/ReactionData.h b/include/cantera/kinetics/ReactionData.h index dd0dbee2c3..c9c6e41795 100644 --- a/include/cantera/kinetics/ReactionData.h +++ b/include/cantera/kinetics/ReactionData.h @@ -82,6 +82,34 @@ struct ArrheniusData : public ReactionData }; +//! Data container holding shared data specific to TwoTempPlasmaRate +/** + * The data container `TwoTempPlasmaData` holds precalculated data common to + * all `TwoTempPlasmaRate` objects. + */ +struct TwoTempPlasmaData : public ReactionData +{ + TwoTempPlasmaData() : elec_temp(1.), logTe(0.), recipTe(1.) {} + + virtual bool update(const ThermoPhase& bulk, const Kinetics& kin) override; + + virtual void update(double T) override; + + virtual void update(double T, double Te) override; + + virtual void updateTe(double Te); + + virtual void invalidateCache() override { + ReactionData::invalidateCache(); + elec_temp = NAN; + } + + double elec_temp; //!< electron temperature + double logTe; //!< logarithm of electron temperature + double recipTe; //!< inverse of electron temperature +}; + + //! Data container holding shared data specific to BlowersMaselRate /** * The data container `BlowersMaselData` holds precalculated data common to diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 33a12f9adb..05b2944463 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -417,6 +417,15 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cbool allowNegativePreExponentialFactor() void setAllowNegativePreExponentialFactor(bool) + cdef cppclass CxxTwoTempPlasmaRate "Cantera::TwoTempPlasmaRate" (CxxReactionRate, CxxArrheniusBase): + CxxTwoTempPlasmaRate() + CxxTwoTempPlasmaRate(CxxAnyMap) except +translate_exception + CxxTwoTempPlasmaRate(double, double, double, double) + double activationEnergy() + double activationElectronEnergy() + cbool allowNegativePreExponentialFactor() + void setAllowNegativePreExponentialFactor(bool) + cdef cppclass CxxBlowersMaselRate "Cantera::BlowersMaselRate" (CxxReactionRate, CxxArrheniusBase): CxxBlowersMaselRate() CxxBlowersMaselRate(CxxAnyMap) except +translate_exception diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 4c850a550c..6344cb772a 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -265,6 +265,86 @@ cdef class BlowersMaselRate(ReactionRate): self.cxx_object().setAllowNegativePreExponentialFactor(allow) +cdef class TwoTempPlasmaRate(ReactionRate): + r""" + A reaction rate coefficient which depends on both gas and electron temperature + with the form similar to the modified Arrhenius form. Specifically, the temperature + exponent (b) is applied to the electron temperature instead. In addition, the + exponential term with activation energy for electron is included. + + .. math:: + + k_f = A T_e^b \exp{-\tfrac{Ea_T}{RT}} \exp{-\tfrac{Ea_{T_e}}{RT_e}} + + where ``A`` is the `pre_exponential_factor`, ``b`` is the `temperature_exponent`, + ``Ea_T`` is the `activation_energy`, and ``Ea_{T_e}`` is the `activation_electron_energy`. + """ + _reaction_rate_type = "two-temperature-plasma" + + def __cinit__(self, A=None, b=None, Ea_T=None, Ea_Te=None, input_data=None, init=True): + + if init: + if isinstance(input_data, dict): + self._rate.reset(new CxxTwoTempPlasmaRate(dict_to_anymap(input_data))) + elif all([arg is not None for arg in [A, b, Ea_T, Ea_Te]]): + self._rate.reset(new CxxTwoTempPlasmaRate(A, b, Ea_T, Ea_Te)) + elif all([arg is None for arg in [A, b, Ea_T, Ea_Te, input_data]]): + self._rate.reset(new CxxTwoTempPlasmaRate(dict_to_anymap({}))) + elif input_data: + raise TypeError("Invalid parameter 'input_data'") + else: + raise TypeError("Invalid parameters 'A', 'b', 'Ea_T' or 'Ea_Te'") + self.set_cxx_object() + + def __call__(self, double temperature, double elec_temp): + """ + Evaluate rate expression based on temperature and enthalpy change of reaction. + """ + return self.rate.eval(temperature, elec_temp) + + cdef CxxTwoTempPlasmaRate* cxx_object(self): + return self.rate + + property pre_exponential_factor: + """ + The pre-exponential factor ``A`` in units of m, kmol, and s raised to + powers depending on the reaction order. + """ + def __get__(self): + return self.cxx_object().preExponentialFactor() + + property temperature_exponent: + """ + The temperature exponent ``b``. + """ + def __get__(self): + return self.cxx_object().temperatureExponent() + + property activation_energy: + """ + The activation energy ``Ea_T`` [J/kmol]. + """ + def __get__(self): + return self.cxx_object().activationEnergy() + + property activation_electron_energy: + """ + The activation electron energy ``Ea_{T_e}`` [J/kmol]. + """ + def __get__(self): + return self.cxx_object().activationElectronEnergy() + + property allow_negative_pre_exponential_factor: + """ + Get/Set whether the rate coefficient is allowed to have a negative + pre-exponential factor. + """ + def __get__(self): + return self.cxx_object().allowNegativePreExponentialFactor() + def __set__(self, cbool allow): + self.cxx_object().setAllowNegativePreExponentialFactor(allow) + + cdef class FalloffRate(ReactionRate): """ Base class for parameterizations used to describe the fall-off in reaction rates diff --git a/src/kinetics/Arrhenius.cpp b/src/kinetics/Arrhenius.cpp index b397df8a22..1d460e7931 100644 --- a/src/kinetics/Arrhenius.cpp +++ b/src/kinetics/Arrhenius.cpp @@ -136,6 +136,71 @@ void ArrheniusRate::getParameters(AnyMap& rateNode) const } } +TwoTempPlasmaRate::TwoTempPlasmaRate() + : m_EE_R(NAN) +{ +} + +TwoTempPlasmaRate::TwoTempPlasmaRate(double A, double b, double Ea, double EE) + : ArrheniusBase(A, b, Ea) + , m_EE_R(EE / GasConstant) +{ + +} + +void TwoTempPlasmaRate::setRateParameters( + const AnyValue& rate, const UnitSystem& units, const UnitStack& rate_units) +{ + if (rate.empty()) { + m_A = NAN; + m_b = NAN; + m_Ea_R = NAN; + m_EE_R = NAN; + m_order = NAN; + m_rate_units = Units(0.); + return; + } + + if (rate.is()) { + ArrheniusBase::setRateParameters(rate, units, rate_units); + auto& rate_map = rate.as(); + m_Ea_R = units.convertActivationEnergy(rate_map["Ea_T"], "K"); + m_EE_R = units.convertActivationEnergy(rate_map["Ea_Te"], "K"); + } else { + setRateUnits(rate_units); + auto& rate_vec = rate.asVector(4); + m_A = units.convert(rate_vec[0], m_rate_units); + m_b = rate_vec[1].asDouble(); + m_Ea_R = units.convertActivationEnergy(rate_vec[2], "K"); + m_EE_R = units.convertActivationEnergy(rate_vec[3], "K"); + } +} + +void TwoTempPlasmaRate::setParameters(const AnyMap& node, const UnitStack& rate_units) +{ + m_negativeA_ok = node.getBool("negative-A", false); + if (!node.hasKey("rate-constant")) { + TwoTempPlasmaRate::setRateParameters(AnyValue(), node.units(), rate_units); + return; + } + TwoTempPlasmaRate::setRateParameters(node["rate-constant"], node.units(), rate_units); +} + +void TwoTempPlasmaRate::getParameters(AnyMap& rateNode) const +{ + if (m_negativeA_ok) { + rateNode["negative-A"] = true; + } + AnyMap node; + ArrheniusBase::getParameters(node); + if (!node.empty()) { + // object is configured + node["Ea_Te"].setQuantity(m_EE_R, "K", true); + rateNode["rate-constant"] = std::move(node); + } + rateNode["type"] = type(); +} + BlowersMaselRate::BlowersMaselRate() : m_w_R(NAN) { diff --git a/src/kinetics/ReactionData.cpp b/src/kinetics/ReactionData.cpp index 3171b8472c..5776a7e503 100644 --- a/src/kinetics/ReactionData.cpp +++ b/src/kinetics/ReactionData.cpp @@ -27,6 +27,41 @@ bool ArrheniusData::update(const ThermoPhase& bulk, const Kinetics& kin) return true; } +bool TwoTempPlasmaData::update(const ThermoPhase& bulk, const Kinetics& kin) +{ + double T = bulk.temperature(); + double Te = bulk.electronTemperature(); + bool changed = false; + if (T != temperature) { + ReactionData::update(T); + changed = true; + } + if (Te != elec_temp) { + updateTe(Te); + changed = true; + } + return changed; +} + +void TwoTempPlasmaData::update(double T) +{ + throw CanteraError("TwoTempPlasmaData::update", + "Missing state information: 'TwoTempPlasmaData' requires electron temperature."); +} + +void TwoTempPlasmaData::update(double T, double Te) +{ + ReactionData::update(T); + updateTe(Te); +} + +void TwoTempPlasmaData::updateTe(double Te) +{ + elec_temp = Te; + logTe = std::log(Te); + recipTe = 1./Te; +} + BlowersMaselData::BlowersMaselData() : ready(false) , density(NAN) diff --git a/src/kinetics/ReactionRateFactory.cpp b/src/kinetics/ReactionRateFactory.cpp index 764d316352..4c1679cd10 100644 --- a/src/kinetics/ReactionRateFactory.cpp +++ b/src/kinetics/ReactionRateFactory.cpp @@ -28,6 +28,11 @@ ReactionRateFactory::ReactionRateFactory() addAlias("Arrhenius", "elementary"); addAlias("Arrhenius", "three-body"); + // TwoTempPlasmaRate evaluator + reg("two-temperature-plasma", [](const AnyMap& node, const UnitStack& rate_units) { + return new TwoTempPlasmaRate(node, rate_units); + }); + // BlowersMaselRate evaluator reg("Blowers-Masel", [](const AnyMap& node, const UnitStack& rate_units) { return new BlowersMaselRate(node, rate_units); From e0149cab10c57b5c1f9883474cc719fd70f10230 Mon Sep 17 00:00:00 2001 From: bangshiuh Date: Sun, 5 Dec 2021 17:46:56 -0500 Subject: [PATCH 04/12] [kinetics, interface] add ETempReaction --- include/cantera/kinetics/Reaction.h | 16 +++++++++ interfaces/cython/cantera/_cantera.pxd | 3 ++ interfaces/cython/cantera/reaction.pyx | 48 ++++++++++++++++++++++++++ src/kinetics/Reaction.cpp | 23 ++++++++++++ src/kinetics/ReactionFactory.cpp | 5 +++ 5 files changed, 95 insertions(+) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index af8b4b76d2..b8dd61060c 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -535,6 +535,22 @@ class ThreeBodyReaction3 : public ElementaryReaction3 }; +//! A reaction for two-temperature-plasma reaction rate +class TwoTempPlasmaReaction: public Reaction +{ +public: + TwoTempPlasmaReaction(); + TwoTempPlasmaReaction(const Composition& reactants, const Composition& products, + const TwoTempPlasmaRate& rate); + + TwoTempPlasmaReaction(const AnyMap& node, const Kinetics& kin); + + virtual std::string type() const { + return "two-temperature-plasma"; + } +}; + + //! A reaction with rate parameters for Blowers-Masel approximation class BlowersMaselReaction: public Reaction { diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 05b2944463..99e8b2794f 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -610,6 +610,9 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": CxxThreeBodyReaction3() shared_ptr[CxxThirdBody] thirdBody() + cdef cppclass CxxTwoTempPlasmaReaction "Cantera::TwoTempPlasmaReaction"(CxxReaction): + CxxTwoTempPlasmaReaction() + cdef cppclass CxxBlowersMaselReaction "Cantera::BlowersMaselReaction"(CxxReaction): CxxBlowersMaselReaction() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 6344cb772a..b00e3204f8 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -2322,6 +2322,54 @@ cdef class BlowersMaselReaction(Reaction): r.setRate(rate_._rate) +cdef class TwoTempPlasmaReaction(Reaction): + """ + A reaction which rate coefficient depends on both gas and electron temperature + + An example for the definition of an `TwoTempPlasmaReaction` object is given as:: + rxn = TwoTempPlasmaReaction( + equation="O2 + E <=> O2-", + rate={"A": 17283, "b": -3.1, "Ea_T": -5820088, "Ea_Te": 10808733}, + kinetics=gas) + The YAML description corresponding to this reaction is:: + equation: O2 + E <=> O2- + type: two-temperature-plasma + rate-constant: {A: 17283, b: -3.1, Ea_T: -700 K, Ea_Te: 1300 K} + """ + _reaction_type = "two-temperature-plasma" + + def __init__(self, equation=None, rate=None, Kinetics kinetics=None, + init=True, **kwargs): + + if init and equation and kinetics: + + rxn_type = self._reaction_type + spec = {"equation": equation, "type": rxn_type} + if isinstance(rate, dict): + spec["rate-constant"] = rate + elif rate is None or isinstance(rate, TwoTempPlasmaRate): + pass + else: + raise TypeError("Invalid rate definition") + + self._reaction = CxxNewReaction(dict_to_anymap(spec), + deref(kinetics.kinetics)) + self.reaction = self._reaction.get() + + if isinstance(rate, TwoTempPlasmaRate): + self.rate = rate + + property rate: + """ Get/Set the `TwoTempPlasmaRate` rate coefficients for this reaction. """ + def __get__(self): + cdef CxxTwoTempPlasmaReaction* r = self.reaction + return TwoTempPlasmaRate.wrap(r.rate()) + def __set__(self, rate): + cdef CxxTwoTempPlasmaReaction* r = self.reaction + cdef TwoTempPlasmaRate rate_ = rate + r.setRate(rate_._rate) + + cdef class InterfaceReaction(ElementaryReaction): """ A reaction occurring on an `Interface` (i.e. a surface or an edge) """ _reaction_type = "interface" diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 8994908403..a4656821b8 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -1091,6 +1091,29 @@ std::string ThreeBodyReaction3::productString() const } } +TwoTempPlasmaReaction::TwoTempPlasmaReaction() +{ + setRate(newReactionRate(type())); +} + +TwoTempPlasmaReaction::TwoTempPlasmaReaction( + const Composition& reactants, const Composition& products, + const TwoTempPlasmaRate& rate) + : Reaction(reactants, products) +{ + m_rate.reset(new TwoTempPlasmaRate(rate)); +} + +TwoTempPlasmaReaction::TwoTempPlasmaReaction(const AnyMap& node, const Kinetics& kin) +{ + if (!node.empty()) { + setParameters(node, kin); + setRate(newReactionRate(node, calculateRateCoeffUnits3(kin))); + } else { + setRate(newReactionRate(type())); + } +} + BlowersMaselReaction::BlowersMaselReaction() { setRate(newReactionRate(type())); diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 1d48a120be..6c810691a7 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -133,6 +133,11 @@ ReactionFactory::ReactionFactory() return R; }); + // register electron-temprature reactions + reg("two-temperature-plasma", [](const AnyMap& node, const Kinetics& kin) { + return new TwoTempPlasmaReaction(node, kin); + }); + // register Blowers Masel reactions reg("Blowers-Masel", [](const AnyMap& node, const Kinetics& kin) { return new BlowersMaselReaction(node, kin); From 415eacaaa984ea409a7345eb6c568ac019604336 Mon Sep 17 00:00:00 2001 From: bangshiuh Date: Sat, 11 Dec 2021 16:38:30 -0500 Subject: [PATCH 05/12] [test] add ETempFromYaml --- test/kinetics/kineticsFromYaml.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index 97de849cf6..b155211181 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -296,6 +296,27 @@ TEST(Reaction, BlowersMaselFromYaml) EXPECT_FALSE(R->allow_negative_orders); } +TEST(Reaction, TwoTempPlasmaFromYaml) +{ + auto sol = newSolution("ET_test.yaml"); + AnyMap rxn = AnyMap::fromYamlString( + "{equation: E + O2 + O2 => O2^- + O2," + " type: two-temperature-plasma," + " rate-constant: [1.523e+27 cm^6/mol^2/s, -1.0, -100 K, 700 K]}"); + + auto R = newReaction(rxn, *(sol->kinetics())); + EXPECT_EQ(R->reactants.at("O2"), 2); + EXPECT_EQ(R->reactants.at("E"), 1); + EXPECT_EQ(R->products.at("O2^-"), 1); + EXPECT_EQ(R->products.at("O2"), 1); + + const auto rate = std::dynamic_pointer_cast(R->rate()); + EXPECT_DOUBLE_EQ(rate->preExponentialFactor(), 1.523e21); + EXPECT_DOUBLE_EQ(rate->temperatureExponent(), -1.0); + EXPECT_DOUBLE_EQ(rate->activationEnergy_R(), -100); + EXPECT_DOUBLE_EQ(rate->activationElectronEnergy_R(), 700); +} + TEST(Kinetics, GasKineticsFromYaml1) { AnyMap infile = AnyMap::fromYamlFile("ideal-gas.yaml"); From 6d81ff2412099d57512d911ba7c1fb6c2b639c0a Mon Sep 17 00:00:00 2001 From: bangshiuh Date: Sat, 11 Dec 2021 22:47:53 -0500 Subject: [PATCH 06/12] [kinetics] make Arrhenius easy to implement and fix ETempRate --- include/cantera/kinetics/Arrhenius.h | 3 +++ src/kinetics/Arrhenius.cpp | 32 ++++++++++++++-------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/include/cantera/kinetics/Arrhenius.h b/include/cantera/kinetics/Arrhenius.h index ef0a9d7ef5..65a6cdd082 100644 --- a/include/cantera/kinetics/Arrhenius.h +++ b/include/cantera/kinetics/Arrhenius.h @@ -121,6 +121,9 @@ class ArrheniusBase : public ReactionRate double m_b; //!< Temperature exponent double m_Ea_R; //!< Activation energy (in temperature units) double m_order; //!< Reaction order + std::string m_A_str = "A"; //!< The string for temperature exponent + std::string m_b_str = "b"; //!< The string for temperature exponent + std::string m_Ea_str = "Ea"; //!< The string for activation energy Units m_rate_units; //!< Reaction rate units }; diff --git a/src/kinetics/Arrhenius.cpp b/src/kinetics/Arrhenius.cpp index 1d460e7931..e0bbbee1a9 100644 --- a/src/kinetics/Arrhenius.cpp +++ b/src/kinetics/Arrhenius.cpp @@ -48,18 +48,18 @@ void ArrheniusBase::setRateParameters( if (m_rate_units.factor() == 0) { // A zero rate units factor is used as a sentinel to detect // stand-alone reaction rate objects - if (rate_map["A"].is()) { + if (rate_map[m_A_str].is()) { throw InputFileError("Arrhenius::setRateParameters", rate_map, "Specification of units is not supported for pre-exponential " "factor when\ncreating a standalone 'ReactionRate' object."); } - m_A = rate_map["A"].asDouble(); + m_A = rate_map[m_A_str].asDouble(); } else { - m_A = units.convert(rate_map["A"], m_rate_units); + m_A = units.convert(rate_map[m_A_str], m_rate_units); } - m_b = rate_map["b"].asDouble(); - if (rate_map.hasKey("Ea")) { - m_Ea_R = units.convertActivationEnergy(rate_map["Ea"], "K"); + m_b = rate_map[m_b_str].asDouble(); + if (rate_map.hasKey(m_Ea_str)) { + m_Ea_R = units.convertActivationEnergy(rate_map[m_Ea_str], "K"); } else { m_Ea_R = NAN; } @@ -82,16 +82,16 @@ void ArrheniusBase::getParameters(AnyMap& node, const Units& rate_units) const // Return empty/unmodified AnyMap return; } else if (rate_units.factor() != 0.0) { - node["A"].setQuantity(m_A, rate_units); + node[m_A_str].setQuantity(m_A, rate_units); } else { - node["A"] = m_A; + node[m_A_str] = m_A; // This can't be converted to a different unit system because the dimensions of // the rate constant were not set. Can occur if the reaction was created outside // the context of a Kinetics object and never added to a Kinetics object. node["__unconvertible__"] = true; } - node["b"] = m_b; - node["Ea"].setQuantity(m_Ea_R, "K", true); + node[m_b_str] = m_b; + node[m_Ea_str].setQuantity(m_Ea_R, "K", true); node.setFlowStyle(); } @@ -137,15 +137,17 @@ void ArrheniusRate::getParameters(AnyMap& rateNode) const } TwoTempPlasmaRate::TwoTempPlasmaRate() - : m_EE_R(NAN) + : ArrheniusBase() + , m_EE_R(NAN) { + m_Ea_str = "Ea_T"; } TwoTempPlasmaRate::TwoTempPlasmaRate(double A, double b, double Ea, double EE) : ArrheniusBase(A, b, Ea) , m_EE_R(EE / GasConstant) { - + m_Ea_str = "Ea_T"; } void TwoTempPlasmaRate::setRateParameters( @@ -164,7 +166,6 @@ void TwoTempPlasmaRate::setRateParameters( if (rate.is()) { ArrheniusBase::setRateParameters(rate, units, rate_units); auto& rate_map = rate.as(); - m_Ea_R = units.convertActivationEnergy(rate_map["Ea_T"], "K"); m_EE_R = units.convertActivationEnergy(rate_map["Ea_Te"], "K"); } else { setRateUnits(rate_units); @@ -204,12 +205,14 @@ void TwoTempPlasmaRate::getParameters(AnyMap& rateNode) const BlowersMaselRate::BlowersMaselRate() : m_w_R(NAN) { + m_Ea_str = "Ea0"; } BlowersMaselRate::BlowersMaselRate(double A, double b, double Ea0, double w) : ArrheniusBase(A, b, Ea0) , m_w_R(w / GasConstant) { + m_Ea_str = "Ea0"; } void BlowersMaselRate::setRateParameters( @@ -228,7 +231,6 @@ void BlowersMaselRate::setRateParameters( if (rate.is()) { ArrheniusBase::setRateParameters(rate, units, rate_units); auto& rate_map = rate.as(); - m_Ea_R = units.convertActivationEnergy(rate_map["Ea0"], "K"); m_w_R = units.convertActivationEnergy(rate_map["w"], "K"); } else { setRateUnits(rate_units); @@ -259,8 +261,6 @@ void BlowersMaselRate::getParameters(AnyMap& rateNode) const ArrheniusBase::getParameters(node); if (!node.empty()) { // object is configured - node.erase("Ea"); - node["Ea0"].setQuantity(m_Ea_R, "K", true); node["w"].setQuantity(m_w_R, "K", true); rateNode["rate-constant"] = std::move(node); } From a32145b294ccd2963258dab9e0404bae628058fb Mon Sep 17 00:00:00 2001 From: bangshiuh Date: Tue, 14 Dec 2021 18:39:53 -0500 Subject: [PATCH 07/12] [test] add ETempRateTest and add TestETemp --- .../cython/cantera/test/test_reaction.py | 61 +++++++++++++++++++ test/data/kineticsfromscratch.yaml | 3 + 2 files changed, 64 insertions(+) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index a7c42f4f94..f570676b26 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -133,6 +133,7 @@ def setUpClass(cls): cls.gas = ct.Solution("kineticsfromscratch.yaml") cls.gas.X = "H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5" cls.gas.TP = 900, 2*ct.one_atm + cls.gas.Te = 2300 def from_parts(self): # create reaction rate object from parts @@ -285,6 +286,43 @@ def test_negative_A(self): self.assertTrue(rate.allow_negative_pre_exponential_factor) +class TestTwoTempPlasmaRate(ReactionRateTests, utilities.CanteraTest): + # test TwoTempPlasma rate expressions + + _cls = ct.TwoTempPlasmaRate + _type = "two-temperature-plasma" + _index = 11 + _input = {"rate-constant": {"A": 17283, "b": -3.1, "Ea_T": -5820000, "Ea_Te": 1081000}} + _yaml = """ + type: two-temperature-plasma + rate-constant: {A: 17283, b: -3.1, Ea_T: -5820 J/mol, Ea_Te: 1081 J/mol} + """ + + @classmethod + def setUpClass(cls): + ReactionRateTests.setUpClass() + cls._parts = cls._input["rate-constant"] + + def eval(self, rate): + # check evaluation as a function of temperature and electron temperature + return rate(self.gas.T, self.gas.Te) + + def test_from_parts(self): + rate = self.from_parts() + self.assertEqual(self._parts["A"], rate.pre_exponential_factor) + self.assertEqual(self._parts["b"], rate.temperature_exponent) + self.assertAlmostEqual(self._parts["Ea_T"], rate.activation_energy) + self.assertAlmostEqual(self._parts["Ea_Te"], rate.activation_electron_energy) + self.check_rate(rate) + + def test_negative_A(self): + # test reaction rate property + rate = self.from_parts() + self.assertFalse(rate.allow_negative_pre_exponential_factor) + rate.allow_negative_pre_exponential_factor = True + self.assertTrue(rate.allow_negative_pre_exponential_factor) + + class FalloffRateTests(ReactionRateTests): # test Falloff rate expressions _n_data = [0] # list of valid falloff coefficient array lengths @@ -886,6 +924,29 @@ def test_efficiencies(self): self.assertEqual(rxn.default_efficiency, 0.) +class TestTwoTempPlasma(ReactionTests, utilities.CanteraTest): + # test two-temperature plasma reaction + + _cls = ct.TwoTempPlasmaReaction + _type = "two-temperature-plasma" + _equation = "O + H <=> O + H" + _rate = {"A": 17283, "b": -3.1, "Ea_T": -5820000, "Ea_Te": 1081000} + _index = 11 + _yaml = """ + equation: O + H <=> O + H + type: two-temperature-plasma + rate-constant: {A: 17283, b: -3.1, Ea_T: -5820 J/mol, Ea_Te: 1081 J/mol} + """ + + @classmethod + def setUpClass(cls): + ReactionTests.setUpClass() + cls._rate_obj = ct.TwoTempPlasmaRate(**cls._rate) + + def eval_rate(self, rate): + return rate(self.gas.T, self.gas.Te) + + class TestBlowersMasel(ReactionTests, utilities.CanteraTest): # test updated version of Blowers-Masel reaction diff --git a/test/data/kineticsfromscratch.yaml b/test/data/kineticsfromscratch.yaml index 2b873616d8..cc9481900b 100644 --- a/test/data/kineticsfromscratch.yaml +++ b/test/data/kineticsfromscratch.yaml @@ -80,3 +80,6 @@ reactions: type: chemically-activated high-P-rate-constant: [5.88E-14, 6.721, -3022.227] low-P-rate-constant: [282320.078, 1.46878, -3270.56495] +- equation: O + H <=> O + H # Reaction 12 + type: two-temperature-plasma + rate-constant: {A: 17283, b: -3.1, Ea_T: -5820 J/mol, Ea_Te: 1081 J/mol} From cdd8aca0726a8b45f00bbb5e4efe1e5407fb07a0 Mon Sep 17 00:00:00 2001 From: bangshiuh Date: Sun, 2 Jan 2022 15:04:12 -0500 Subject: [PATCH 08/12] fix m_elec_temp init value --- src/thermo/Phase.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/thermo/Phase.cpp b/src/thermo/Phase.cpp index c729b3c0a1..d6dc460d77 100644 --- a/src/thermo/Phase.cpp +++ b/src/thermo/Phase.cpp @@ -26,6 +26,7 @@ Phase::Phase() : m_xml(new XML_Node("phase")), m_id(""), m_temp(0.001), + m_elec_temp(0.001), m_dens(0.001), m_mmw(0.0), m_stateNum(-1), From 22336ef21415ae80bb2b43afd9e2aa152f1a0715 Mon Sep 17 00:00:00 2001 From: bangshiuh Date: Wed, 5 Jan 2022 18:48:36 -0500 Subject: [PATCH 09/12] change key from Ea_T and Ea_Te to Ea and EE --- include/cantera/kinetics/Arrhenius.h | 6 ++--- interfaces/cython/cantera/reaction.pyx | 22 +++++++++---------- .../cython/cantera/test/test_reaction.py | 12 +++++----- src/kinetics/Arrhenius.cpp | 6 ++--- test/data/kineticsfromscratch.yaml | 2 +- 5 files changed, 23 insertions(+), 25 deletions(-) diff --git a/include/cantera/kinetics/Arrhenius.h b/include/cantera/kinetics/Arrhenius.h index 65a6cdd082..3da7d7c18d 100644 --- a/include/cantera/kinetics/Arrhenius.h +++ b/include/cantera/kinetics/Arrhenius.h @@ -207,7 +207,7 @@ class ArrheniusRate final : public ArrheniusBase * activation energy for electron is included. * * \f[ - * k_f = A T_e^b \exp (-Ea_T/RT) \exp (-Ea_{T_e}/RT_e) + * k_f = A T_e^b \exp (-E_a/RT) \exp (-E_{a,e}/RT_e) * \f] * * @ingroup arrheniusGroup @@ -274,12 +274,12 @@ class TwoTempPlasmaRate final : public ArrheniusBase return m_Ea_R; } - //! Return the activation energy *Ea* [J/kmol] + //! Return the electron activation energy *Ea* [J/kmol] double activationElectronEnergy() const { return m_EE_R * GasConstant; } - //! Return the activation energy divided by the gas constant (i.e. the + //! Return the electron activation energy divided by the gas constant (i.e. the //! activation temperature) [K] double activationElectronEnergy_R() const { return m_EE_R; diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index b00e3204f8..184a918952 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -274,26 +274,26 @@ cdef class TwoTempPlasmaRate(ReactionRate): .. math:: - k_f = A T_e^b \exp{-\tfrac{Ea_T}{RT}} \exp{-\tfrac{Ea_{T_e}}{RT_e}} + k_f = A T_e^b \exp{-\tfrac{E_a}{RT}} \exp{-\tfrac{E_{a,e}}{RT_e}} where ``A`` is the `pre_exponential_factor`, ``b`` is the `temperature_exponent`, - ``Ea_T`` is the `activation_energy`, and ``Ea_{T_e}`` is the `activation_electron_energy`. + ``E_a`` is the `activation_energy`, and ``E_{a,e}`` is the `activation_electron_energy`. """ _reaction_rate_type = "two-temperature-plasma" - def __cinit__(self, A=None, b=None, Ea_T=None, Ea_Te=None, input_data=None, init=True): + def __cinit__(self, A=None, b=None, Ea=None, EE=None, input_data=None, init=True): if init: if isinstance(input_data, dict): self._rate.reset(new CxxTwoTempPlasmaRate(dict_to_anymap(input_data))) - elif all([arg is not None for arg in [A, b, Ea_T, Ea_Te]]): - self._rate.reset(new CxxTwoTempPlasmaRate(A, b, Ea_T, Ea_Te)) - elif all([arg is None for arg in [A, b, Ea_T, Ea_Te, input_data]]): + elif all([arg is not None for arg in [A, b, Ea, EE]]): + self._rate.reset(new CxxTwoTempPlasmaRate(A, b, Ea, EE)) + elif all([arg is None for arg in [A, b, Ea, EE, input_data]]): self._rate.reset(new CxxTwoTempPlasmaRate(dict_to_anymap({}))) elif input_data: raise TypeError("Invalid parameter 'input_data'") else: - raise TypeError("Invalid parameters 'A', 'b', 'Ea_T' or 'Ea_Te'") + raise TypeError("Invalid parameters 'A', 'b', 'Ea' or 'EE'") self.set_cxx_object() def __call__(self, double temperature, double elec_temp): @@ -322,14 +322,14 @@ cdef class TwoTempPlasmaRate(ReactionRate): property activation_energy: """ - The activation energy ``Ea_T`` [J/kmol]. + The activation energy ``E_a`` [J/kmol]. """ def __get__(self): return self.cxx_object().activationEnergy() property activation_electron_energy: """ - The activation electron energy ``Ea_{T_e}`` [J/kmol]. + The activation electron energy ``E_{a,e}`` [J/kmol]. """ def __get__(self): return self.cxx_object().activationElectronEnergy() @@ -2329,12 +2329,12 @@ cdef class TwoTempPlasmaReaction(Reaction): An example for the definition of an `TwoTempPlasmaReaction` object is given as:: rxn = TwoTempPlasmaReaction( equation="O2 + E <=> O2-", - rate={"A": 17283, "b": -3.1, "Ea_T": -5820088, "Ea_Te": 10808733}, + rate={"A": 17283, "b": -3.1, "Ea": -5820088, "EE": 10808733}, kinetics=gas) The YAML description corresponding to this reaction is:: equation: O2 + E <=> O2- type: two-temperature-plasma - rate-constant: {A: 17283, b: -3.1, Ea_T: -700 K, Ea_Te: 1300 K} + rate-constant: {A: 17283, b: -3.1, Ea: -700 K, EE: 1300 K} """ _reaction_type = "two-temperature-plasma" diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index f570676b26..617e1dee9d 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -292,10 +292,10 @@ class TestTwoTempPlasmaRate(ReactionRateTests, utilities.CanteraTest): _cls = ct.TwoTempPlasmaRate _type = "two-temperature-plasma" _index = 11 - _input = {"rate-constant": {"A": 17283, "b": -3.1, "Ea_T": -5820000, "Ea_Te": 1081000}} + _input = {"rate-constant": {"A": 17283, "b": -3.1, "Ea": -5820000, "EE": 1081000}} _yaml = """ type: two-temperature-plasma - rate-constant: {A: 17283, b: -3.1, Ea_T: -5820 J/mol, Ea_Te: 1081 J/mol} + rate-constant: {A: 17283, b: -3.1, Ea: -5820 J/mol, EE: 1081 J/mol} """ @classmethod @@ -311,8 +311,8 @@ def test_from_parts(self): rate = self.from_parts() self.assertEqual(self._parts["A"], rate.pre_exponential_factor) self.assertEqual(self._parts["b"], rate.temperature_exponent) - self.assertAlmostEqual(self._parts["Ea_T"], rate.activation_energy) - self.assertAlmostEqual(self._parts["Ea_Te"], rate.activation_electron_energy) + self.assertAlmostEqual(self._parts["Ea"], rate.activation_energy) + self.assertAlmostEqual(self._parts["EE"], rate.activation_electron_energy) self.check_rate(rate) def test_negative_A(self): @@ -930,12 +930,12 @@ class TestTwoTempPlasma(ReactionTests, utilities.CanteraTest): _cls = ct.TwoTempPlasmaReaction _type = "two-temperature-plasma" _equation = "O + H <=> O + H" - _rate = {"A": 17283, "b": -3.1, "Ea_T": -5820000, "Ea_Te": 1081000} + _rate = {"A": 17283, "b": -3.1, "Ea": -5820000, "EE": 1081000} _index = 11 _yaml = """ equation: O + H <=> O + H type: two-temperature-plasma - rate-constant: {A: 17283, b: -3.1, Ea_T: -5820 J/mol, Ea_Te: 1081 J/mol} + rate-constant: {A: 17283, b: -3.1, Ea: -5820 J/mol, EE: 1081 J/mol} """ @classmethod diff --git a/src/kinetics/Arrhenius.cpp b/src/kinetics/Arrhenius.cpp index e0bbbee1a9..5cccc37e21 100644 --- a/src/kinetics/Arrhenius.cpp +++ b/src/kinetics/Arrhenius.cpp @@ -140,14 +140,12 @@ TwoTempPlasmaRate::TwoTempPlasmaRate() : ArrheniusBase() , m_EE_R(NAN) { - m_Ea_str = "Ea_T"; } TwoTempPlasmaRate::TwoTempPlasmaRate(double A, double b, double Ea, double EE) : ArrheniusBase(A, b, Ea) , m_EE_R(EE / GasConstant) { - m_Ea_str = "Ea_T"; } void TwoTempPlasmaRate::setRateParameters( @@ -166,7 +164,7 @@ void TwoTempPlasmaRate::setRateParameters( if (rate.is()) { ArrheniusBase::setRateParameters(rate, units, rate_units); auto& rate_map = rate.as(); - m_EE_R = units.convertActivationEnergy(rate_map["Ea_Te"], "K"); + m_EE_R = units.convertActivationEnergy(rate_map["EE"], "K"); } else { setRateUnits(rate_units); auto& rate_vec = rate.asVector(4); @@ -196,7 +194,7 @@ void TwoTempPlasmaRate::getParameters(AnyMap& rateNode) const ArrheniusBase::getParameters(node); if (!node.empty()) { // object is configured - node["Ea_Te"].setQuantity(m_EE_R, "K", true); + node["EE"].setQuantity(m_EE_R, "K", true); rateNode["rate-constant"] = std::move(node); } rateNode["type"] = type(); diff --git a/test/data/kineticsfromscratch.yaml b/test/data/kineticsfromscratch.yaml index cc9481900b..3d9648ea48 100644 --- a/test/data/kineticsfromscratch.yaml +++ b/test/data/kineticsfromscratch.yaml @@ -82,4 +82,4 @@ reactions: low-P-rate-constant: [282320.078, 1.46878, -3270.56495] - equation: O + H <=> O + H # Reaction 12 type: two-temperature-plasma - rate-constant: {A: 17283, b: -3.1, Ea_T: -5820 J/mol, Ea_Te: 1081 J/mol} + rate-constant: {A: 17283, b: -3.1, Ea: -5820 J/mol, EE: 1081 J/mol} From 85f3e910ff8209638f570e9acfbcdcbeafd2efa4 Mon Sep 17 00:00:00 2001 From: bangshiuh Date: Fri, 7 Jan 2022 21:19:19 -0500 Subject: [PATCH 10/12] [interface, test] input change names --- interfaces/cython/cantera/reaction.pyx | 39 ++++++++++++------- .../cython/cantera/test/test_reaction.py | 14 +++---- src/kinetics/Arrhenius.cpp | 6 ++- test/data/kineticsfromscratch.yaml | 2 +- 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 184a918952..435b000129 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -274,26 +274,28 @@ cdef class TwoTempPlasmaRate(ReactionRate): .. math:: - k_f = A T_e^b \exp{-\tfrac{E_a}{RT}} \exp{-\tfrac{E_{a,e}}{RT_e}} + k_f = A T_e^b \exp{-\tfrac{E_{a,g}}{RT}} \exp{-\tfrac{E_{a,e}}{RT_e}} - where ``A`` is the `pre_exponential_factor`, ``b`` is the `temperature_exponent`, - ``E_a`` is the `activation_energy`, and ``E_{a,e}`` is the `activation_electron_energy`. + where :math:`A` is the `pre_exponential_factor`, :math:`b` is the + `temperature_exponent`, :math:`E_{a,g}` is the `activation_energy`, and + :math:`E_{a,e}` is the `activation_electron_energy`. """ _reaction_rate_type = "two-temperature-plasma" - def __cinit__(self, A=None, b=None, Ea=None, EE=None, input_data=None, init=True): - + def __cinit__(self, A=None, b=None, Ea_gas=None, Ea_electron=None, + input_data=None, init=True): if init: if isinstance(input_data, dict): self._rate.reset(new CxxTwoTempPlasmaRate(dict_to_anymap(input_data))) - elif all([arg is not None for arg in [A, b, Ea, EE]]): - self._rate.reset(new CxxTwoTempPlasmaRate(A, b, Ea, EE)) - elif all([arg is None for arg in [A, b, Ea, EE, input_data]]): + elif all([arg is not None for arg in [A, b, Ea_gas, Ea_electron]]): + self._rate.reset(new CxxTwoTempPlasmaRate(A, b, Ea_gas, Ea_electron)) + elif all([arg is None for arg in [A, b, Ea_gas, Ea_electron, input_data]]): self._rate.reset(new CxxTwoTempPlasmaRate(dict_to_anymap({}))) elif input_data: raise TypeError("Invalid parameter 'input_data'") else: - raise TypeError("Invalid parameters 'A', 'b', 'Ea' or 'EE'") + raise TypeError( + "Invalid parameters 'A', 'b', 'Ea_gas' or 'Ea_electron'") self.set_cxx_object() def __call__(self, double temperature, double elec_temp): @@ -322,14 +324,14 @@ cdef class TwoTempPlasmaRate(ReactionRate): property activation_energy: """ - The activation energy ``E_a`` [J/kmol]. + The activation energy :math:`E_{a,g}` [J/kmol]. """ def __get__(self): return self.cxx_object().activationEnergy() property activation_electron_energy: """ - The activation electron energy ``E_{a,e}`` [J/kmol]. + The activation electron energy :math:`E_{a,e}` [J/kmol]. """ def __get__(self): return self.cxx_object().activationElectronEnergy() @@ -2279,11 +2281,14 @@ cdef class BlowersMaselReaction(Reaction): reaction rate. An example for the definition of an `BlowersMaselReaction` object is given as:: + rxn = BlowersMaselReaction( equation="O + H2 <=> H + OH", rate={"A": 38.7, "b": 2.7, "Ea0": 1.0958665856e8, "w": 1.7505856e13}, kinetics=gas) + The YAML description corresponding to this reaction is:: + equation: O + H2 <=> H + OH type: Blowers-Masel rate-constant: {A: 38700 cm^3/mol/s, b: 2.7, Ea0: 2.619184e4 cal/mol, w: 4.184e9 cal/mol} @@ -2327,14 +2332,17 @@ cdef class TwoTempPlasmaReaction(Reaction): A reaction which rate coefficient depends on both gas and electron temperature An example for the definition of an `TwoTempPlasmaReaction` object is given as:: + rxn = TwoTempPlasmaReaction( equation="O2 + E <=> O2-", - rate={"A": 17283, "b": -3.1, "Ea": -5820088, "EE": 10808733}, + rate={"A": 17283, "b": -3.1, "Ea-gas": -5820088, "Ea-electron": 10808733}, kinetics=gas) + The YAML description corresponding to this reaction is:: + equation: O2 + E <=> O2- type: two-temperature-plasma - rate-constant: {A: 17283, b: -3.1, Ea: -700 K, EE: 1300 K} + rate-constant: {A: 17283, b: -3.1, Ea-gas: -700 K, Ea-electron: 1300 K} """ _reaction_type = "two-temperature-plasma" @@ -2346,7 +2354,10 @@ cdef class TwoTempPlasmaReaction(Reaction): rxn_type = self._reaction_type spec = {"equation": equation, "type": rxn_type} if isinstance(rate, dict): - spec["rate-constant"] = rate + replaced_rate = {} + for key, value in rate.items(): + replaced_rate[key.replace("_", "-")] = value + spec["rate-constant"] = replaced_rate elif rate is None or isinstance(rate, TwoTempPlasmaRate): pass else: diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 617e1dee9d..a0208a6842 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -292,16 +292,16 @@ class TestTwoTempPlasmaRate(ReactionRateTests, utilities.CanteraTest): _cls = ct.TwoTempPlasmaRate _type = "two-temperature-plasma" _index = 11 - _input = {"rate-constant": {"A": 17283, "b": -3.1, "Ea": -5820000, "EE": 1081000}} + _input = {"rate-constant": {"A": 17283, "b": -3.1, "Ea-gas": -5820000, "Ea-electron": 1081000}} _yaml = """ type: two-temperature-plasma - rate-constant: {A: 17283, b: -3.1, Ea: -5820 J/mol, EE: 1081 J/mol} + rate-constant: {A: 17283, b: -3.1, Ea-gas: -5820 J/mol, Ea-electron: 1081 J/mol} """ @classmethod def setUpClass(cls): ReactionRateTests.setUpClass() - cls._parts = cls._input["rate-constant"] + cls._parts = {key.replace("-", "_"): value for key, value in cls._input["rate-constant"].items()} def eval(self, rate): # check evaluation as a function of temperature and electron temperature @@ -311,8 +311,8 @@ def test_from_parts(self): rate = self.from_parts() self.assertEqual(self._parts["A"], rate.pre_exponential_factor) self.assertEqual(self._parts["b"], rate.temperature_exponent) - self.assertAlmostEqual(self._parts["Ea"], rate.activation_energy) - self.assertAlmostEqual(self._parts["EE"], rate.activation_electron_energy) + self.assertAlmostEqual(self._parts["Ea_gas"], rate.activation_energy) + self.assertAlmostEqual(self._parts["Ea_electron"], rate.activation_electron_energy) self.check_rate(rate) def test_negative_A(self): @@ -930,12 +930,12 @@ class TestTwoTempPlasma(ReactionTests, utilities.CanteraTest): _cls = ct.TwoTempPlasmaReaction _type = "two-temperature-plasma" _equation = "O + H <=> O + H" - _rate = {"A": 17283, "b": -3.1, "Ea": -5820000, "EE": 1081000} + _rate = {"A": 17283, "b": -3.1, "Ea_gas": -5820000, "Ea_electron": 1081000} _index = 11 _yaml = """ equation: O + H <=> O + H type: two-temperature-plasma - rate-constant: {A: 17283, b: -3.1, Ea: -5820 J/mol, EE: 1081 J/mol} + rate-constant: {A: 17283, b: -3.1, Ea-gas: -5820 J/mol, Ea-electron: 1081 J/mol} """ @classmethod diff --git a/src/kinetics/Arrhenius.cpp b/src/kinetics/Arrhenius.cpp index 5cccc37e21..61e78b0d82 100644 --- a/src/kinetics/Arrhenius.cpp +++ b/src/kinetics/Arrhenius.cpp @@ -140,12 +140,14 @@ TwoTempPlasmaRate::TwoTempPlasmaRate() : ArrheniusBase() , m_EE_R(NAN) { + m_Ea_str = "Ea-gas"; } TwoTempPlasmaRate::TwoTempPlasmaRate(double A, double b, double Ea, double EE) : ArrheniusBase(A, b, Ea) , m_EE_R(EE / GasConstant) { + m_Ea_str = "Ea-gas"; } void TwoTempPlasmaRate::setRateParameters( @@ -164,7 +166,7 @@ void TwoTempPlasmaRate::setRateParameters( if (rate.is()) { ArrheniusBase::setRateParameters(rate, units, rate_units); auto& rate_map = rate.as(); - m_EE_R = units.convertActivationEnergy(rate_map["EE"], "K"); + m_EE_R = units.convertActivationEnergy(rate_map["Ea-electron"], "K"); } else { setRateUnits(rate_units); auto& rate_vec = rate.asVector(4); @@ -194,7 +196,7 @@ void TwoTempPlasmaRate::getParameters(AnyMap& rateNode) const ArrheniusBase::getParameters(node); if (!node.empty()) { // object is configured - node["EE"].setQuantity(m_EE_R, "K", true); + node["Ea-electron"].setQuantity(m_EE_R, "K", true); rateNode["rate-constant"] = std::move(node); } rateNode["type"] = type(); diff --git a/test/data/kineticsfromscratch.yaml b/test/data/kineticsfromscratch.yaml index 3d9648ea48..6c0eea58ed 100644 --- a/test/data/kineticsfromscratch.yaml +++ b/test/data/kineticsfromscratch.yaml @@ -82,4 +82,4 @@ reactions: low-P-rate-constant: [282320.078, 1.46878, -3270.56495] - equation: O + H <=> O + H # Reaction 12 type: two-temperature-plasma - rate-constant: {A: 17283, b: -3.1, Ea: -5820 J/mol, EE: 1081 J/mol} + rate-constant: {A: 17283, b: -3.1, Ea-gas: -5820 J/mol, Ea-electron: 1081 J/mol} From 28f98f91ec98520d2bb1197d5cba16b50e7efb64 Mon Sep 17 00:00:00 2001 From: bangshiuh Date: Fri, 7 Jan 2022 22:03:02 -0500 Subject: [PATCH 11/12] [thermo] rename m_elec_temp to m_electronTemp --- include/cantera/thermo/Phase.h | 6 +++--- src/thermo/Phase.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/cantera/thermo/Phase.h b/include/cantera/thermo/Phase.h index 7f56972061..a0a1d04b0c 100644 --- a/include/cantera/thermo/Phase.h +++ b/include/cantera/thermo/Phase.h @@ -658,7 +658,7 @@ class Phase //! Electron Temperature (K) //! @return The electron temperature of the phase double electronTemperature() const { - return m_elec_temp; + return m_electronTemp; } //! Return the thermodynamic pressure (Pa). @@ -729,7 +729,7 @@ class Phase //! @param etemp Electron temperature in Kelvin virtual void setElectronTemperature(const double etemp) { if (etemp > 0) { - m_elec_temp = etemp; + m_electronTemp = etemp; } else { throw CanteraError("Phase::setElectronTemperature", "electron temperature must be positive. Te = {}", etemp); @@ -985,7 +985,7 @@ class Phase doublereal m_temp; //!< Temperature (K). This is an independent variable //! Electron Temperature (K). - double m_elec_temp; + double m_electronTemp; //! Density (kg m-3). This is an independent variable except in the case //! of incompressible phases, where it has to be changed using the diff --git a/src/thermo/Phase.cpp b/src/thermo/Phase.cpp index d6dc460d77..6b7baa75a5 100644 --- a/src/thermo/Phase.cpp +++ b/src/thermo/Phase.cpp @@ -26,7 +26,7 @@ Phase::Phase() : m_xml(new XML_Node("phase")), m_id(""), m_temp(0.001), - m_elec_temp(0.001), + m_electronTemp(0.001), m_dens(0.001), m_mmw(0.0), m_stateNum(-1), From f6307edce8a76005e616517389932e7ab524dac3 Mon Sep 17 00:00:00 2001 From: bangshiuh Date: Wed, 12 Jan 2022 17:18:49 -0500 Subject: [PATCH 12/12] [kinetics] change TwoTempPlasmaRate expression --- include/cantera/kinetics/Arrhenius.h | 9 +++++-- include/cantera/kinetics/ReactionData.h | 6 ++--- src/kinetics/Arrhenius.cpp | 31 +++++++++++++++++++++---- src/kinetics/ReactionData.cpp | 4 ++-- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/include/cantera/kinetics/Arrhenius.h b/include/cantera/kinetics/Arrhenius.h index 3da7d7c18d..2edf23b9c0 100644 --- a/include/cantera/kinetics/Arrhenius.h +++ b/include/cantera/kinetics/Arrhenius.h @@ -210,6 +210,10 @@ class ArrheniusRate final : public ArrheniusBase * k_f = A T_e^b \exp (-E_a/RT) \exp (-E_{a,e}/RT_e) * \f] * + * Ref.: Kossyi, I. A., Kostinsky, A. Y., Matveyev, A. A., & Silakov, V. P. (1992). + * Kinetic scheme of the non-equilibrium discharge in nitrogen-oxygen mixtures. + * Plasma Sources Science and Technology, 1(3), 207. + * doi: 10.1088/0963-0252/1/3/011 * @ingroup arrheniusGroup */ class TwoTempPlasmaRate final : public ArrheniusBase @@ -259,8 +263,9 @@ class TwoTempPlasmaRate final : public ArrheniusBase double evalFromStruct(const TwoTempPlasmaData& shared_data) { return m_A * std::exp(m_b * shared_data.logTe - - m_Ea_R * shared_data.recipT - - m_EE_R * shared_data.recipTe); + m_Ea_R * shared_data.recipT + + m_EE_R * (shared_data.electronTemp - shared_data.temperature) + * shared_data.recipTe * shared_data.recipT); } //! Return the activation energy *Ea* [J/kmol] diff --git a/include/cantera/kinetics/ReactionData.h b/include/cantera/kinetics/ReactionData.h index c9c6e41795..7f72c04309 100644 --- a/include/cantera/kinetics/ReactionData.h +++ b/include/cantera/kinetics/ReactionData.h @@ -89,7 +89,7 @@ struct ArrheniusData : public ReactionData */ struct TwoTempPlasmaData : public ReactionData { - TwoTempPlasmaData() : elec_temp(1.), logTe(0.), recipTe(1.) {} + TwoTempPlasmaData() : electronTemp(1.), logTe(0.), recipTe(1.) {} virtual bool update(const ThermoPhase& bulk, const Kinetics& kin) override; @@ -101,10 +101,10 @@ struct TwoTempPlasmaData : public ReactionData virtual void invalidateCache() override { ReactionData::invalidateCache(); - elec_temp = NAN; + electronTemp = NAN; } - double elec_temp; //!< electron temperature + double electronTemp; //!< electron temperature double logTe; //!< logarithm of electron temperature double recipTe; //!< inverse of electron temperature }; diff --git a/src/kinetics/Arrhenius.cpp b/src/kinetics/Arrhenius.cpp index 61e78b0d82..de1a314578 100644 --- a/src/kinetics/Arrhenius.cpp +++ b/src/kinetics/Arrhenius.cpp @@ -64,10 +64,14 @@ void ArrheniusBase::setRateParameters( m_Ea_R = NAN; } } else { - auto& rate_vec = rate.asVector(3); + auto& rate_vec = rate.asVector(2, 3); m_A = units.convert(rate_vec[0], m_rate_units); m_b = rate_vec[1].asDouble(); - m_Ea_R = units.convertActivationEnergy(rate_vec[2], "K"); + if (rate_vec.size() == 3) { + m_Ea_R = units.convertActivationEnergy(rate_vec[2], "K"); + } else { + m_Ea_R = NAN; + } } } @@ -166,14 +170,31 @@ void TwoTempPlasmaRate::setRateParameters( if (rate.is()) { ArrheniusBase::setRateParameters(rate, units, rate_units); auto& rate_map = rate.as(); - m_EE_R = units.convertActivationEnergy(rate_map["Ea-electron"], "K"); + // check Ea-gas value. Set to zero in the case of absent. + if (m_Ea_R == NAN) { + m_Ea_R = 0.0; + } + // Get Ea-electron value. If not set it to zero. + if (rate_map.hasKey("Ea-electron")) { + m_EE_R = units.convertActivationEnergy(rate_map["Ea-electron"], "K"); + } else { + m_EE_R = 0.0; + } } else { setRateUnits(rate_units); auto& rate_vec = rate.asVector(4); m_A = units.convert(rate_vec[0], m_rate_units); m_b = rate_vec[1].asDouble(); - m_Ea_R = units.convertActivationEnergy(rate_vec[2], "K"); - m_EE_R = units.convertActivationEnergy(rate_vec[3], "K"); + if (rate_vec.size() == 4) { + m_EE_R = units.convertActivationEnergy(rate_vec[3], "K"); + m_Ea_R = units.convertActivationEnergy(rate_vec[2], "K"); + } else if (rate_vec.size() == 3) { + m_Ea_R = 0.0; + m_Ea_R = units.convertActivationEnergy(rate_vec[2], "K"); + } else { + m_Ea_R = 0.0; + m_EE_R = 0.0; + } } } diff --git a/src/kinetics/ReactionData.cpp b/src/kinetics/ReactionData.cpp index 5776a7e503..48f0925704 100644 --- a/src/kinetics/ReactionData.cpp +++ b/src/kinetics/ReactionData.cpp @@ -36,7 +36,7 @@ bool TwoTempPlasmaData::update(const ThermoPhase& bulk, const Kinetics& kin) ReactionData::update(T); changed = true; } - if (Te != elec_temp) { + if (Te != electronTemp) { updateTe(Te); changed = true; } @@ -57,7 +57,7 @@ void TwoTempPlasmaData::update(double T, double Te) void TwoTempPlasmaData::updateTe(double Te) { - elec_temp = Te; + electronTemp = Te; logTe = std::log(Te); recipTe = 1./Te; }