From 9f9e9d8263e6a65a871aed98fa52ab882729aa60 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 4 Nov 2019 20:12:32 -0600 Subject: [PATCH 01/67] [Kinetics] create ReactionFactory --- include/cantera/kinetics/Reaction.h | 45 ++++- include/cantera/kinetics/ReactionFactory.h | 84 +++++++++ interfaces/cython/cantera/_cantera.pxd | 4 +- src/kinetics/KineticsFactory.cpp | 1 + src/kinetics/Reaction.cpp | 192 +++++++++------------ src/kinetics/ReactionFactory.cpp | 111 ++++++++++++ src/kinetics/importKinetics.cpp | 1 + test/kinetics/kineticsFromYaml.cpp | 1 + 8 files changed, 323 insertions(+), 116 deletions(-) create mode 100644 include/cantera/kinetics/ReactionFactory.h create mode 100644 src/kinetics/ReactionFactory.cpp diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index e8a300f175..5c359e130a 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -28,6 +28,12 @@ class Reaction const Composition& products); virtual ~Reaction() {} + virtual void setup(const XML_Node& rxn_node) { + } + + virtual void setup(const AnyMap& node, const Kinetics& kin) { + } + //! The reactant side of the chemical equation for this reaction virtual std::string reactantString() const; @@ -88,6 +94,9 @@ class ElementaryReaction : public Reaction const Arrhenius& rate); virtual void validate(); + virtual void setup(const XML_Node& rxn_node); + virtual void setup(const AnyMap& node, const Kinetics& kin); + Arrhenius rate; bool allow_negative_pre_exponential_factor; }; @@ -117,6 +126,10 @@ class ThreeBodyReaction : public ElementaryReaction ThreeBodyReaction(); ThreeBodyReaction(const Composition& reactants, const Composition& products, const Arrhenius& rate, const ThirdBody& tbody); + + virtual void setup(const XML_Node& rxn_node); + virtual void setup(const AnyMap& node, const Kinetics& kin); + virtual std::string reactantString() const; virtual std::string productString() const; @@ -134,6 +147,10 @@ class FalloffReaction : public Reaction FalloffReaction(const Composition& reactants, const Composition& products, const Arrhenius& low_rate, const Arrhenius& high_rate, const ThirdBody& tbody); + + virtual void setup(const XML_Node& rxn_node); + virtual void setup(const AnyMap& node, const Kinetics& kin); + virtual std::string reactantString() const; virtual std::string productString() const; virtual void validate(); @@ -165,6 +182,8 @@ class ChemicallyActivatedReaction : public FalloffReaction ChemicallyActivatedReaction(const Composition& reactants, const Composition& products, const Arrhenius& low_rate, const Arrhenius& high_rate, const ThirdBody& tbody); + + virtual void setup(const XML_Node& rxn_node); }; //! A pressure-dependent reaction parameterized by logarithmically interpolating @@ -175,6 +194,10 @@ class PlogReaction : public Reaction PlogReaction(); PlogReaction(const Composition& reactants, const Composition& products, const Plog& rate); + + virtual void setup(const XML_Node& rxn_node); + virtual void setup(const AnyMap& node, const Kinetics& kin); + virtual void validate(); Plog rate; }; @@ -188,6 +211,9 @@ class ChebyshevReaction : public Reaction ChebyshevReaction(const Composition& reactants, const Composition& products, const ChebyshevRate& rate); + virtual void setup(const XML_Node& rxn_node); + virtual void setup(const AnyMap& node, const Kinetics& kin); + ChebyshevRate rate; }; @@ -214,6 +240,9 @@ class InterfaceReaction : public ElementaryReaction InterfaceReaction(const Composition& reactants, const Composition& products, const Arrhenius& rate, bool isStick=false); + virtual void setup(const XML_Node& rxn_node); + virtual void setup(const AnyMap& node, const Kinetics& kin); + //! Adjustments to the Arrhenius rate expression dependent on surface //! species coverages. Three coverage parameters (a, E, m) are used for each //! species on which the rate depends. See SurfaceArrhenius for details on @@ -249,15 +278,6 @@ class ElectrochemicalReaction : public InterfaceReaction bool exchange_current_density_formulation; }; -//! Create a new Reaction object for the reaction defined in `rxn_node` -//! -//! @deprecated The XML input format is deprecated and will be removed in -//! Cantera 3.0. -shared_ptr newReaction(const XML_Node& rxn_node); - -//! Create a new Reaction object using the specified parameters -unique_ptr newReaction(const AnyMap& rxn_node, const Kinetics& kin); - //! Create Reaction objects for all `` nodes in an XML document. //! //! The `` nodes are assumed to be children of the `` @@ -286,6 +306,13 @@ std::vector > getReactions(const XML_Node& node); //! Kinetics object `kinetics`. std::vector> getReactions(const AnyValue& items, Kinetics& kinetics); + +//! Check whether reaction is an electrochemical reaction +bool isElectrochemicalReaction(Reaction& R, const Kinetics& kin); + +//! Parse reaction equation +void parseReactionEquation(Reaction& R, const AnyValue& equation, + const Kinetics& kin); } #endif diff --git a/include/cantera/kinetics/ReactionFactory.h b/include/cantera/kinetics/ReactionFactory.h new file mode 100644 index 0000000000..6f0d6e0774 --- /dev/null +++ b/include/cantera/kinetics/ReactionFactory.h @@ -0,0 +1,84 @@ +/** + * @file ReactionFactory.h + * Parameterizations for reaction reaction functions. Used by classes + * that implement gas-phase kinetics (GasKinetics, GRI_30_Kinetics) + * (see \ref reactionGroup and class \link Cantera::Reaction Reaction\endlink). + */ + +// This file is part of Cantera. See License.txt in the top-level directory or +// at https://cantera.org/license.txt for license and copyright information. + +#ifndef CT_NEWREACTION_H +#define CT_NEWREACTION_H + +#include "cantera/base/FactoryBase.h" +#include "cantera/kinetics/Reaction.h" + +namespace Cantera +{ + +/** + * Factory class to construct reaction function calculators. + * The reaction factory is accessed through static method factory: + * + * @code + * Reaction* f = ReactionFactory::factory()->newReaction(type, c) + * @endcode + * + * @ingroup reactionGroup + */ +class ReactionFactory : public Factory +{ +public: + /** + * Return a pointer to the factory. On the first call, a new instance is + * created. Since there is no need to instantiate more than one factory, + * on all subsequent calls, a pointer to the existing factory is returned. + */ + static ReactionFactory* factory() { + std::unique_lock lock(reaction_mutex); + if (!s_factory) { + s_factory = new ReactionFactory; + } + return s_factory; + } + + virtual void deleteFactory() { + std::unique_lock lock(reaction_mutex); + delete s_factory; + s_factory = 0; + } + + //! Return a pointer to a new reaction function calculator. + /*! + * @param type Integer flag specifying the type of reaction function. The + * standard types are defined in file reaction_defs.h. A + * factory class derived from ReactionFactory may define other + * types as well. + * @param c input vector of doubles which populates the reaction + * parameterization. + * @returns a pointer to a new Reaction class. + */ + virtual Reaction* newReaction(const XML_Node& rxn_node); + + virtual Reaction* newReaction(const AnyMap& rxn_node, + const Kinetics& kin); +private: + //! Pointer to the single instance of the factory + static ReactionFactory* s_factory; + + //! default constructor, which is defined as private + ReactionFactory(); + + //! Mutex for use when calling the factory + static std::mutex reaction_mutex; +}; + +//! Create a new Reaction object for the reaction defined in `rxn_node` +unique_ptr newReaction(const XML_Node& rxn_node); + +//! Create a new Reaction object using the specified parameters +unique_ptr newReaction(const AnyMap& rxn_node, + const Kinetics& kin); +} +#endif diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 2a0f99b93f..0400a42aea 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -295,9 +295,11 @@ cdef extern from "cantera/thermo/SurfPhase.h": void getCoverages(double*) except +translate_exception -cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": +cdef extern from "cantera/kinetics/ReactionFactory.h" namespace "Cantera": cdef shared_ptr[CxxReaction] CxxNewReaction "newReaction" (XML_Node&) except +translate_exception cdef shared_ptr[CxxReaction] CxxNewReaction "newReaction" (CxxAnyMap&, CxxKinetics&) except +translate_exception + +cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef vector[shared_ptr[CxxReaction]] CxxGetReactions "getReactions" (XML_Node&) except +translate_exception cdef vector[shared_ptr[CxxReaction]] CxxGetReactions "getReactions" (CxxAnyValue&, CxxKinetics&) except +translate_exception diff --git a/src/kinetics/KineticsFactory.cpp b/src/kinetics/KineticsFactory.cpp index e45b887fff..a292bdc224 100644 --- a/src/kinetics/KineticsFactory.cpp +++ b/src/kinetics/KineticsFactory.cpp @@ -10,6 +10,7 @@ #include "cantera/kinetics/InterfaceKinetics.h" #include "cantera/kinetics/EdgeKinetics.h" #include "cantera/kinetics/importKinetics.h" +#include "cantera/kinetics/ReactionFactory.h" #include "cantera/base/xml.h" #include "cantera/base/stringUtils.h" diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index c5f82e8c59..9533593324 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -6,6 +6,7 @@ // at https://cantera.org/license.txt for license and copyright information. #include "cantera/kinetics/Reaction.h" +#include "cantera/kinetics/ReactionFactory.h" #include "cantera/kinetics/FalloffFactory.h" #include "cantera/kinetics/Kinetics.h" #include "cantera/thermo/ThermoPhase.h" @@ -24,6 +25,31 @@ namespace ba = boost::algorithm; namespace Cantera { +// forward declarations +void setupElementaryReaction(ElementaryReaction&, const XML_Node&); +void setupElementaryReaction(ElementaryReaction&, const AnyMap&, + const Kinetics&); +void setupThreeBodyReaction(ThreeBodyReaction&, const XML_Node&); +void setupThreeBodyReaction(ThreeBodyReaction&, const AnyMap&, + const Kinetics&); +void setupFalloffReaction(FalloffReaction&, const XML_Node&); +void setupFalloffReaction(FalloffReaction&, const AnyMap&, + const Kinetics&); +void setupChemicallyActivatedReaction(ChemicallyActivatedReaction&, + const XML_Node&); +void setupPlogReaction(PlogReaction&, const XML_Node&); +void setupPlogReaction(PlogReaction&, const AnyMap&, const Kinetics&); +void setupChebyshevReaction(ChebyshevReaction&, const XML_Node&); +void setupChebyshevReaction(ChebyshevReaction&, const AnyMap&, + const Kinetics&); +void setupInterfaceReaction(InterfaceReaction&, const XML_Node&); +void setupInterfaceReaction(InterfaceReaction&, const AnyMap&, + const Kinetics&); +void setupElectrochemicalReaction(ElectrochemicalReaction&, + const XML_Node&); +void setupElectrochemicalReaction(ElectrochemicalReaction&, + const AnyMap&, const Kinetics&); + Reaction::Reaction(int type) : reaction_type(type) , reversible(true) @@ -131,6 +157,14 @@ void ElementaryReaction::validate() } } +void ElementaryReaction::setup(const XML_Node& rxn_node) { + setupElementaryReaction(*this, rxn_node); +} + +void ElementaryReaction::setup(const AnyMap& node, const Kinetics& kin) { + setupElementaryReaction(*this, node, kin); +} + ThirdBody::ThirdBody(double default_eff) : default_efficiency(default_eff) { @@ -156,6 +190,14 @@ ThreeBodyReaction::ThreeBodyReaction(const Composition& reactants_, reaction_type = THREE_BODY_RXN; } +void ThreeBodyReaction::setup(const XML_Node& rxn_node) { + setupThreeBodyReaction(*this, rxn_node); +} + +void ThreeBodyReaction::setup(const AnyMap& node, const Kinetics& kin) { + setupThreeBodyReaction(*this, node, kin); +} + std::string ThreeBodyReaction::reactantString() const { return ElementaryReaction::reactantString() + " + M"; } @@ -183,6 +225,14 @@ FalloffReaction::FalloffReaction( { } +void FalloffReaction::setup(const XML_Node& rxn_node) { + setupFalloffReaction(*this, rxn_node); +} + +void FalloffReaction::setup(const AnyMap& node, const Kinetics& kin) { + setupFalloffReaction(*this, node, kin); +} + std::string FalloffReaction::reactantString() const { if (third_body.default_efficiency == 0 && third_body.efficiencies.size() == 1) { @@ -232,6 +282,10 @@ ChemicallyActivatedReaction::ChemicallyActivatedReaction( reaction_type = CHEMACT_RXN; } +void ChemicallyActivatedReaction::setup(const XML_Node& rxn_node) { + setupChemicallyActivatedReaction(*this, rxn_node); +} + PlogReaction::PlogReaction() : Reaction(PLOG_RXN) { @@ -244,6 +298,14 @@ PlogReaction::PlogReaction(const Composition& reactants_, { } +void PlogReaction::setup(const XML_Node& rxn_node) { + setupPlogReaction(*this, rxn_node); +} + +void PlogReaction::setup(const AnyMap& node, const Kinetics& kin) { + setupPlogReaction(*this, node, kin); +} + ChebyshevReaction::ChebyshevReaction() : Reaction(CHEBYSHEV_RXN) { @@ -257,6 +319,14 @@ ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, { } +void ChebyshevReaction::setup(const XML_Node& rxn_node) { + setupChebyshevReaction(*this, rxn_node); +} + +void ChebyshevReaction::setup(const AnyMap& node, const Kinetics& kin) { + setupChebyshevReaction(*this, node, kin); +} + InterfaceReaction::InterfaceReaction() : is_sticking_coefficient(false) , use_motz_wise_correction(false) @@ -275,6 +345,14 @@ InterfaceReaction::InterfaceReaction(const Composition& reactants_, reaction_type = INTERFACE_RXN; } +void InterfaceReaction::setup(const XML_Node& rxn_node) { + setupInterfaceReaction(*this, rxn_node); +} + +void InterfaceReaction::setup(const AnyMap& node, const Kinetics& kin) { + setupInterfaceReaction(*this, node, kin); +} + ElectrochemicalReaction::ElectrochemicalReaction() : beta(0.5) , exchange_current_density_formulation(false) @@ -290,6 +368,14 @@ ElectrochemicalReaction::ElectrochemicalReaction(const Composition& reactants_, { } +void ElectrochemicalReaction::setup(const XML_Node& rxn_node) { + setupElectrochemicalReaction(*this, rxn_node); +} + +void ElectrochemicalReaction::setup(const AnyMap& node, const Kinetics& kin) { + setupElectrochemicalReaction(*this, node, kin); +} + Arrhenius readArrhenius(const XML_Node& arrhenius_node) { return Arrhenius(getFloat(arrhenius_node, "A", "toSI"), @@ -935,112 +1021,6 @@ bool isElectrochemicalReaction(Reaction& R, const Kinetics& kin) return false; } -shared_ptr newReaction(const XML_Node& rxn_node) -{ - std::string type = toLowerCopy(rxn_node["type"]); - - // Modify the reaction type for interface reactions which contain - // electrochemical reaction data - if (rxn_node.child("rateCoeff").hasChild("electrochem") - && (type == "edge" || type == "surface")) { - type = "electrochemical"; - } - - // Create a new Reaction object of the appropriate type - if (type == "elementary" || type == "arrhenius" || type == "") { - auto R = make_shared(); - setupElementaryReaction(*R, rxn_node); - return R; - } else if (type == "threebody" || type == "three_body") { - auto R = make_shared(); - setupThreeBodyReaction(*R, rxn_node); - return R; - } else if (type == "falloff") { - auto R = make_shared(); - setupFalloffReaction(*R, rxn_node); - return R; - } else if (type == "chemact" || type == "chemically_activated") { - auto R = make_shared(); - setupChemicallyActivatedReaction(*R, rxn_node); - return R; - } else if (type == "plog" || type == "pdep_arrhenius") { - auto R = make_shared(); - setupPlogReaction(*R, rxn_node); - return R; - } else if (type == "chebyshev") { - auto R = make_shared(); - setupChebyshevReaction(*R, rxn_node); - return R; - } else if (type == "interface" || type == "surface" || type == "edge" || - type == "global") { - auto R = make_shared(); - setupInterfaceReaction(*R, rxn_node); - return R; - } else if (type == "electrochemical" || - type == "butlervolmer_noactivitycoeffs" || - type == "butlervolmer" || - type == "surfaceaffinity") { - auto R = make_shared(); - setupElectrochemicalReaction(*R, rxn_node); - return R; - } else { - throw CanteraError("newReaction", - "Unknown reaction type '" + rxn_node["type"] + "'"); - } -} - -unique_ptr newReaction(const AnyMap& node, const Kinetics& kin) -{ - std::string type = "elementary"; - if (node.hasKey("type")) { - type = node["type"].asString(); - } - - if (kin.thermo(kin.reactionPhaseIndex()).nDim() < 3) { - // See if this is an electrochemical reaction - Reaction testReaction(0); - parseReactionEquation(testReaction, node["equation"], kin); - if (isElectrochemicalReaction(testReaction, kin)) { - unique_ptr R(new ElectrochemicalReaction()); - setupElectrochemicalReaction(*R, node, kin); - return unique_ptr(move(R)); - } else { - unique_ptr R(new InterfaceReaction()); - setupInterfaceReaction(*R, node, kin); - return unique_ptr(move(R)); - } - } - - if (type == "elementary") { - unique_ptr R(new ElementaryReaction()); - setupElementaryReaction(*R, node, kin); - return unique_ptr(move(R)); - } else if (type == "three-body") { - unique_ptr R(new ThreeBodyReaction()); - setupThreeBodyReaction(*R, node, kin); - return unique_ptr(move(R)); - } else if (type == "falloff") { - unique_ptr R(new FalloffReaction()); - setupFalloffReaction(*R, node, kin); - return unique_ptr(move(R)); - } else if (type == "chemically-activated") { - unique_ptr R(new ChemicallyActivatedReaction()); - setupFalloffReaction(*R, node, kin); - return unique_ptr(move(R)); - } else if (type == "pressure-dependent-Arrhenius") { - unique_ptr R(new PlogReaction()); - setupPlogReaction(*R, node, kin); - return unique_ptr(move(R)); - } else if (type == "Chebyshev") { - unique_ptr R(new ChebyshevReaction()); - setupChebyshevReaction(*R, node, kin); - return unique_ptr(move(R)); - } else { - throw InputFileError("newReaction", node["type"], - "Unknown reaction type '{}'", type); - } -} - std::vector > getReactions(const XML_Node& node) { std::vector > all_reactions; diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp new file mode 100644 index 0000000000..61d01290b2 --- /dev/null +++ b/src/kinetics/ReactionFactory.cpp @@ -0,0 +1,111 @@ +/** + * @file ReactionFactory.cpp + */ + +// This file is part of Cantera. See License.txt in the top-level directory or +// at https://cantera.org/license.txt for license and copyright information. + +#include "cantera/kinetics/Reaction.h" +#include "cantera/kinetics/ReactionFactory.h" +#include "cantera/kinetics/Kinetics.h" +#include "cantera/base/ctml.h" +#include "cantera/base/AnyMap.h" + +namespace Cantera +{ + +ReactionFactory* ReactionFactory::s_factory = 0; +std::mutex ReactionFactory::reaction_mutex; + +ReactionFactory::ReactionFactory() +{ + reg("elementary", []() { return new ElementaryReaction(); }); + m_synonyms["arrhenius"] = "elementary"; + m_synonyms[""] = "elementary"; + reg("three-body", []() { return new ThreeBodyReaction(); }); + m_synonyms["threebody"] = "three-body"; + m_synonyms["three_body"] = "three-body"; + reg("falloff", []() { return new FalloffReaction(); }); + reg("chemically-activated", []() { return new ChemicallyActivatedReaction(); }); + m_synonyms["chemact"] = "chemically-activated"; + m_synonyms["chemically_activated"] = "chemically-activated"; + reg("pressure-dependent-arrhenius", []() { return new PlogReaction(); }); + m_synonyms["plog"] = "pressure-dependent-arrhenius"; + m_synonyms["pdep_arrhenius"] = "pressure-dependent-arrhenius"; + reg("chebyshev", []() { return new ChebyshevReaction(); }); + reg("interface", []() { return new InterfaceReaction(); }); + m_synonyms["surface"] = "interface"; + m_synonyms["edge"] = "interface"; + m_synonyms["global"] = "interface"; + reg("electrochemical", []() { return new ElectrochemicalReaction(); }); + m_synonyms["butlervolmer_noactivitycoeffs"] = "electrochemical"; + m_synonyms["butlervolmer"] = "electrochemical"; + m_synonyms["surfaceaffinity"] = "electrochemical"; +} + +Reaction* ReactionFactory::newReaction(const XML_Node& rxn_node) +{ + std::string type = toLowerCopy(rxn_node["type"]); + + // Modify the reaction type for interface reactions which contain + // electrochemical reaction data + if (rxn_node.child("rateCoeff").hasChild("electrochem") + && (type == "edge" || type == "surface")) { + type = "electrochemical"; + } + + Reaction* R; + try { + R = create(type); + } catch (CanteraError& err) { + throw CanteraError("newReaction", + "Unknown reaction type '" + rxn_node["type"] + "'"); + } + R->setup(rxn_node); + return R; +} + +Reaction* ReactionFactory::newReaction(const AnyMap& node, + const Kinetics& kin) +{ + std::string type = "elementary"; + if (node.hasKey("type")) { + type = toLowerCopy(node["type"].asString()); + } + + if (kin.thermo().nDim() < 3) { + // See if this is an electrochemical reaction + Reaction testReaction(0); + parseReactionEquation(testReaction, node["equation"], kin); + if (isElectrochemicalReaction(testReaction, kin)) { + type = "electrochemical"; + } else { + type = "interface"; + } + } + + Reaction* R; + try { + R = create(type); + } catch (CanteraError& err) { + throw InputFileError("newReaction", node["type"], + "Unknown reaction type '{}'", type); + } + R->setup(node, kin); + return R; +} + +unique_ptr newReaction(const XML_Node& rxn_node) +{ + unique_ptr R(ReactionFactory::factory()->newReaction(rxn_node)); + return R; +} + +unique_ptr newReaction(const AnyMap& rxn_node, + const Kinetics& kin) +{ + unique_ptr R(ReactionFactory::factory()->newReaction(rxn_node, kin)); + return R; +} + +} diff --git a/src/kinetics/importKinetics.cpp b/src/kinetics/importKinetics.cpp index 296eb6f35d..3164ead2b1 100644 --- a/src/kinetics/importKinetics.cpp +++ b/src/kinetics/importKinetics.cpp @@ -16,6 +16,7 @@ #include "cantera/kinetics/importKinetics.h" #include "cantera/thermo/ThermoFactory.h" #include "cantera/kinetics/Reaction.h" +#include "cantera/kinetics/ReactionFactory.h" #include "cantera/base/stringUtils.h" #include "cantera/base/ctml.h" #include "cantera/base/yaml.h" diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index 352fb5ff6d..84c5253506 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -4,6 +4,7 @@ #include "cantera/kinetics/GasKinetics.h" #include "cantera/thermo/SurfPhase.h" #include "cantera/kinetics/KineticsFactory.h" +#include "cantera/kinetics/ReactionFactory.h" #include "cantera/thermo/ThermoFactory.h" using namespace Cantera; From aaf3f3164a0ed21285b03d45b3046be38e62ffd8 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 6 Nov 2019 21:38:35 -0600 Subject: [PATCH 02/67] [Kinetics] add Reaction::type method --- include/cantera/kinetics/Reaction.h | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 5c359e130a..7c1769328d 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -28,10 +28,14 @@ class Reaction const Composition& products); virtual ~Reaction() {} + //! set up reaction based on XML node (overloaded) virtual void setup(const XML_Node& rxn_node) { + throw NotImplementedError("Reaction::setup"); } + //! set up reaction based on AnyMap node (overloaded) virtual void setup(const AnyMap& node, const Kinetics& kin) { + throw NotImplementedError("Reaction::setup"); } //! The reactant side of the chemical equation for this reaction @@ -43,6 +47,11 @@ class Reaction //! The chemical equation for this reaction std::string equation() const; + //! The type of reaction + virtual std::string type() const { + return ""; + } + //! Ensure that the rate constant and other parameters for this reaction are //! valid. virtual void validate(); @@ -94,6 +103,9 @@ class ElementaryReaction : public Reaction const Arrhenius& rate); virtual void validate(); + virtual std::string type() const { + return "elementary"; + } virtual void setup(const XML_Node& rxn_node); virtual void setup(const AnyMap& node, const Kinetics& kin); @@ -127,6 +139,9 @@ class ThreeBodyReaction : public ElementaryReaction ThreeBodyReaction(const Composition& reactants, const Composition& products, const Arrhenius& rate, const ThirdBody& tbody); + virtual std::string type() const { + return "three-body"; + } virtual void setup(const XML_Node& rxn_node); virtual void setup(const AnyMap& node, const Kinetics& kin); @@ -148,6 +163,9 @@ class FalloffReaction : public Reaction const Arrhenius& low_rate, const Arrhenius& high_rate, const ThirdBody& tbody); + virtual std::string type() const { + return "falloff"; + } virtual void setup(const XML_Node& rxn_node); virtual void setup(const AnyMap& node, const Kinetics& kin); @@ -183,6 +201,9 @@ class ChemicallyActivatedReaction : public FalloffReaction const Composition& products, const Arrhenius& low_rate, const Arrhenius& high_rate, const ThirdBody& tbody); + virtual std::string type() const { + return "chemically-activated"; + } virtual void setup(const XML_Node& rxn_node); }; @@ -195,6 +216,9 @@ class PlogReaction : public Reaction PlogReaction(const Composition& reactants, const Composition& products, const Plog& rate); + virtual std::string type() const { + return "pressure-dependent-Arrhenius"; + } virtual void setup(const XML_Node& rxn_node); virtual void setup(const AnyMap& node, const Kinetics& kin); @@ -211,6 +235,9 @@ class ChebyshevReaction : public Reaction ChebyshevReaction(const Composition& reactants, const Composition& products, const ChebyshevRate& rate); + virtual std::string type() const { + return "Chebyshev"; + } virtual void setup(const XML_Node& rxn_node); virtual void setup(const AnyMap& node, const Kinetics& kin); @@ -240,6 +267,9 @@ class InterfaceReaction : public ElementaryReaction InterfaceReaction(const Composition& reactants, const Composition& products, const Arrhenius& rate, bool isStick=false); + virtual std::string type() const { + return "interface"; + } virtual void setup(const XML_Node& rxn_node); virtual void setup(const AnyMap& node, const Kinetics& kin); From 4e718f435a205d4aaa0a504fc9a2f8a382c381bd Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 9 Nov 2019 11:13:41 -0600 Subject: [PATCH 03/67] [Kinetics] add setup function wrappers to ReactionFactory --- include/cantera/kinetics/Reaction.h | 23 ---- include/cantera/kinetics/ReactionFactory.h | 41 +++++++ src/kinetics/Reaction.cpp | 84 --------------- src/kinetics/ReactionFactory.cpp | 120 +++++++++++++++++++-- 4 files changed, 153 insertions(+), 115 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 7c1769328d..aaba86193a 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -28,16 +28,6 @@ class Reaction const Composition& products); virtual ~Reaction() {} - //! set up reaction based on XML node (overloaded) - virtual void setup(const XML_Node& rxn_node) { - throw NotImplementedError("Reaction::setup"); - } - - //! set up reaction based on AnyMap node (overloaded) - virtual void setup(const AnyMap& node, const Kinetics& kin) { - throw NotImplementedError("Reaction::setup"); - } - //! The reactant side of the chemical equation for this reaction virtual std::string reactantString() const; @@ -106,8 +96,6 @@ class ElementaryReaction : public Reaction virtual std::string type() const { return "elementary"; } - virtual void setup(const XML_Node& rxn_node); - virtual void setup(const AnyMap& node, const Kinetics& kin); Arrhenius rate; bool allow_negative_pre_exponential_factor; @@ -142,8 +130,6 @@ class ThreeBodyReaction : public ElementaryReaction virtual std::string type() const { return "three-body"; } - virtual void setup(const XML_Node& rxn_node); - virtual void setup(const AnyMap& node, const Kinetics& kin); virtual std::string reactantString() const; virtual std::string productString() const; @@ -166,8 +152,6 @@ class FalloffReaction : public Reaction virtual std::string type() const { return "falloff"; } - virtual void setup(const XML_Node& rxn_node); - virtual void setup(const AnyMap& node, const Kinetics& kin); virtual std::string reactantString() const; virtual std::string productString() const; @@ -204,7 +188,6 @@ class ChemicallyActivatedReaction : public FalloffReaction virtual std::string type() const { return "chemically-activated"; } - virtual void setup(const XML_Node& rxn_node); }; //! A pressure-dependent reaction parameterized by logarithmically interpolating @@ -219,8 +202,6 @@ class PlogReaction : public Reaction virtual std::string type() const { return "pressure-dependent-Arrhenius"; } - virtual void setup(const XML_Node& rxn_node); - virtual void setup(const AnyMap& node, const Kinetics& kin); virtual void validate(); Plog rate; @@ -238,8 +219,6 @@ class ChebyshevReaction : public Reaction virtual std::string type() const { return "Chebyshev"; } - virtual void setup(const XML_Node& rxn_node); - virtual void setup(const AnyMap& node, const Kinetics& kin); ChebyshevRate rate; }; @@ -270,8 +249,6 @@ class InterfaceReaction : public ElementaryReaction virtual std::string type() const { return "interface"; } - virtual void setup(const XML_Node& rxn_node); - virtual void setup(const AnyMap& node, const Kinetics& kin); //! Adjustments to the Arrhenius rate expression dependent on surface //! species coverages. Three coverage parameters (a, E, m) are used for each diff --git a/include/cantera/kinetics/ReactionFactory.h b/include/cantera/kinetics/ReactionFactory.h index 6f0d6e0774..6abec368ef 100644 --- a/include/cantera/kinetics/ReactionFactory.h +++ b/include/cantera/kinetics/ReactionFactory.h @@ -63,6 +63,39 @@ class ReactionFactory : public Factory virtual Reaction* newReaction(const AnyMap& rxn_node, const Kinetics& kin); + + void setup_XML(std::string name, + Reaction* R, const XML_Node& rxn_node) { + try { + m_xml_setup.at(name)(R, rxn_node); + } catch (std::out_of_range&) { + throw CanteraError("ReactionFactory::setup_XML", + "No such type: '{}'", name); + } + } + + //! Register a new object initializer function (from XML node) + void reg_XML(const std::string& name, + std::function f) { + m_xml_setup[name] = f; + } + + void setup_AnyMap(std::string name, + Reaction* R, const AnyMap& node, const Kinetics& kin) { + try { + m_anymap_setup.at(name)(R, node, kin); + } catch (std::out_of_range&) { + throw CanteraError("ReactionFactory::setup_AnyMap", + "No such type: '{}'", name); + } + } + + //! Register a new object initializer function (from XML node) + void reg_AnyMap(const std::string& name, + std::function f) { + m_anymap_setup[name] = f; + } + private: //! Pointer to the single instance of the factory static ReactionFactory* s_factory; @@ -72,6 +105,14 @@ class ReactionFactory : public Factory //! Mutex for use when calling the factory static std::mutex reaction_mutex; + + //! map of XML initializers + std::unordered_map> m_xml_setup; + + //! map of AnyMap initializers + std::unordered_map> m_anymap_setup; }; //! Create a new Reaction object for the reaction defined in `rxn_node` diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 9533593324..987f51b2d7 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -25,30 +25,6 @@ namespace ba = boost::algorithm; namespace Cantera { -// forward declarations -void setupElementaryReaction(ElementaryReaction&, const XML_Node&); -void setupElementaryReaction(ElementaryReaction&, const AnyMap&, - const Kinetics&); -void setupThreeBodyReaction(ThreeBodyReaction&, const XML_Node&); -void setupThreeBodyReaction(ThreeBodyReaction&, const AnyMap&, - const Kinetics&); -void setupFalloffReaction(FalloffReaction&, const XML_Node&); -void setupFalloffReaction(FalloffReaction&, const AnyMap&, - const Kinetics&); -void setupChemicallyActivatedReaction(ChemicallyActivatedReaction&, - const XML_Node&); -void setupPlogReaction(PlogReaction&, const XML_Node&); -void setupPlogReaction(PlogReaction&, const AnyMap&, const Kinetics&); -void setupChebyshevReaction(ChebyshevReaction&, const XML_Node&); -void setupChebyshevReaction(ChebyshevReaction&, const AnyMap&, - const Kinetics&); -void setupInterfaceReaction(InterfaceReaction&, const XML_Node&); -void setupInterfaceReaction(InterfaceReaction&, const AnyMap&, - const Kinetics&); -void setupElectrochemicalReaction(ElectrochemicalReaction&, - const XML_Node&); -void setupElectrochemicalReaction(ElectrochemicalReaction&, - const AnyMap&, const Kinetics&); Reaction::Reaction(int type) : reaction_type(type) @@ -157,14 +133,6 @@ void ElementaryReaction::validate() } } -void ElementaryReaction::setup(const XML_Node& rxn_node) { - setupElementaryReaction(*this, rxn_node); -} - -void ElementaryReaction::setup(const AnyMap& node, const Kinetics& kin) { - setupElementaryReaction(*this, node, kin); -} - ThirdBody::ThirdBody(double default_eff) : default_efficiency(default_eff) { @@ -190,14 +158,6 @@ ThreeBodyReaction::ThreeBodyReaction(const Composition& reactants_, reaction_type = THREE_BODY_RXN; } -void ThreeBodyReaction::setup(const XML_Node& rxn_node) { - setupThreeBodyReaction(*this, rxn_node); -} - -void ThreeBodyReaction::setup(const AnyMap& node, const Kinetics& kin) { - setupThreeBodyReaction(*this, node, kin); -} - std::string ThreeBodyReaction::reactantString() const { return ElementaryReaction::reactantString() + " + M"; } @@ -225,14 +185,6 @@ FalloffReaction::FalloffReaction( { } -void FalloffReaction::setup(const XML_Node& rxn_node) { - setupFalloffReaction(*this, rxn_node); -} - -void FalloffReaction::setup(const AnyMap& node, const Kinetics& kin) { - setupFalloffReaction(*this, node, kin); -} - std::string FalloffReaction::reactantString() const { if (third_body.default_efficiency == 0 && third_body.efficiencies.size() == 1) { @@ -282,10 +234,6 @@ ChemicallyActivatedReaction::ChemicallyActivatedReaction( reaction_type = CHEMACT_RXN; } -void ChemicallyActivatedReaction::setup(const XML_Node& rxn_node) { - setupChemicallyActivatedReaction(*this, rxn_node); -} - PlogReaction::PlogReaction() : Reaction(PLOG_RXN) { @@ -298,14 +246,6 @@ PlogReaction::PlogReaction(const Composition& reactants_, { } -void PlogReaction::setup(const XML_Node& rxn_node) { - setupPlogReaction(*this, rxn_node); -} - -void PlogReaction::setup(const AnyMap& node, const Kinetics& kin) { - setupPlogReaction(*this, node, kin); -} - ChebyshevReaction::ChebyshevReaction() : Reaction(CHEBYSHEV_RXN) { @@ -319,14 +259,6 @@ ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, { } -void ChebyshevReaction::setup(const XML_Node& rxn_node) { - setupChebyshevReaction(*this, rxn_node); -} - -void ChebyshevReaction::setup(const AnyMap& node, const Kinetics& kin) { - setupChebyshevReaction(*this, node, kin); -} - InterfaceReaction::InterfaceReaction() : is_sticking_coefficient(false) , use_motz_wise_correction(false) @@ -345,14 +277,6 @@ InterfaceReaction::InterfaceReaction(const Composition& reactants_, reaction_type = INTERFACE_RXN; } -void InterfaceReaction::setup(const XML_Node& rxn_node) { - setupInterfaceReaction(*this, rxn_node); -} - -void InterfaceReaction::setup(const AnyMap& node, const Kinetics& kin) { - setupInterfaceReaction(*this, node, kin); -} - ElectrochemicalReaction::ElectrochemicalReaction() : beta(0.5) , exchange_current_density_formulation(false) @@ -368,14 +292,6 @@ ElectrochemicalReaction::ElectrochemicalReaction(const Composition& reactants_, { } -void ElectrochemicalReaction::setup(const XML_Node& rxn_node) { - setupElectrochemicalReaction(*this, rxn_node); -} - -void ElectrochemicalReaction::setup(const AnyMap& node, const Kinetics& kin) { - setupElectrochemicalReaction(*this, node, kin); -} - Arrhenius readArrhenius(const XML_Node& arrhenius_node) { return Arrhenius(getFloat(arrhenius_node, "A", "toSI"), diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 61d01290b2..b2b39eb65f 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -19,28 +19,132 @@ std::mutex ReactionFactory::reaction_mutex; ReactionFactory::ReactionFactory() { + // register elementary reactions reg("elementary", []() { return new ElementaryReaction(); }); m_synonyms["arrhenius"] = "elementary"; m_synonyms[""] = "elementary"; + void setupElementaryReaction(ElementaryReaction&, const XML_Node&); + reg_XML("elementary", + [](Reaction* R, const XML_Node& node) { + setupElementaryReaction(*(ElementaryReaction*)R, node); + }); + void setupElementaryReaction(ElementaryReaction&, const AnyMap&, + const Kinetics&); + reg_AnyMap("elementary", + [](Reaction* R, const AnyMap& node, const Kinetics& kin) { + setupElementaryReaction(*(ElementaryReaction*)R, node, kin); + }); + + // register three-body reactions reg("three-body", []() { return new ThreeBodyReaction(); }); m_synonyms["threebody"] = "three-body"; m_synonyms["three_body"] = "three-body"; + void setupThreeBodyReaction(ThreeBodyReaction&, const XML_Node&); + reg_XML("three-body", + [](Reaction* R, const XML_Node& node) { + setupThreeBodyReaction(*(ThreeBodyReaction*)R, node); + }); + void setupThreeBodyReaction(ThreeBodyReaction&, const AnyMap&, + const Kinetics&); + reg_AnyMap("three-body", + [](Reaction* R, const AnyMap& node, const Kinetics& kin) { + setupThreeBodyReaction(*(ThreeBodyReaction*)R, node, kin); + }); + + // register falloff reactions reg("falloff", []() { return new FalloffReaction(); }); + void setupFalloffReaction(FalloffReaction&, const XML_Node&); + reg_XML("falloff", + [](Reaction* R, const XML_Node& node) { + setupFalloffReaction(*(FalloffReaction*)R, node); + }); + void setupFalloffReaction(FalloffReaction&, const AnyMap&, + const Kinetics&); + reg_AnyMap("falloff", + [](Reaction* R, const AnyMap& node, const Kinetics& kin) { + setupFalloffReaction(*(FalloffReaction*)R, node, kin); + }); + + // register falloff reactions reg("chemically-activated", []() { return new ChemicallyActivatedReaction(); }); m_synonyms["chemact"] = "chemically-activated"; m_synonyms["chemically_activated"] = "chemically-activated"; - reg("pressure-dependent-arrhenius", []() { return new PlogReaction(); }); - m_synonyms["plog"] = "pressure-dependent-arrhenius"; - m_synonyms["pdep_arrhenius"] = "pressure-dependent-arrhenius"; - reg("chebyshev", []() { return new ChebyshevReaction(); }); + void setupChemicallyActivatedReaction(ChemicallyActivatedReaction&, + const XML_Node&); + reg_XML("chemically-activated", + [](Reaction* R, const XML_Node& node) { + setupChemicallyActivatedReaction(*(ChemicallyActivatedReaction*)R, node); + }); + reg_AnyMap("chemically-activated", + [](Reaction* R, const AnyMap& node, const Kinetics& kin) { + setupFalloffReaction(*(FalloffReaction*)R, node, kin); + }); + + // register pressure-depdendent-Arrhenius reactions + reg("pressure-dependent-Arrhenius", []() { return new PlogReaction(); }); + m_synonyms["pressure-dependent-arrhenius"] = "pressure-dependent-Arrhenius"; + m_synonyms["plog"] = "pressure-dependent-Arrhenius"; + m_synonyms["pdep_arrhenius"] = "pressure-dependent-Arrhenius"; + void setupPlogReaction(PlogReaction&, const XML_Node&); + reg_XML("pressure-dependent-Arrhenius", + [](Reaction* R, const XML_Node& node) { + setupPlogReaction(*(PlogReaction*)R, node); + }); + void setupPlogReaction(PlogReaction&, const AnyMap&, const Kinetics&); + reg_AnyMap("pressure-dependent-Arrhenius", + [](Reaction* R, const AnyMap& node, const Kinetics& kin) { + setupPlogReaction(*(PlogReaction*)R, node, kin); + }); + + // register Chebyshev reactions + reg("Chebyshev", []() { return new ChebyshevReaction(); }); + m_synonyms["chebyshev"] = "Chebyshev"; + void setupChebyshevReaction(ChebyshevReaction&, const XML_Node&); + reg_XML("Chebyshev", + [](Reaction* R, const XML_Node& node) { + setupChebyshevReaction(*(ChebyshevReaction*)R, node); + }); + void setupChebyshevReaction(ChebyshevReaction&, const AnyMap&, + const Kinetics&); + reg_AnyMap("Chebyshev", + [](Reaction* R, const AnyMap& node, const Kinetics& kin) { + setupChebyshevReaction(*(ChebyshevReaction*)R, node, kin); + }); + + // register interface reactions reg("interface", []() { return new InterfaceReaction(); }); m_synonyms["surface"] = "interface"; m_synonyms["edge"] = "interface"; m_synonyms["global"] = "interface"; + void setupInterfaceReaction(InterfaceReaction&, const XML_Node&); + reg_XML("interface", + [](Reaction* R, const XML_Node& node) { + setupInterfaceReaction(*(InterfaceReaction*)R, node); + }); + void setupInterfaceReaction(InterfaceReaction&, const AnyMap&, + const Kinetics&); + reg_AnyMap("interface", + [](Reaction* R, const AnyMap& node, const Kinetics& kin) { + setupInterfaceReaction(*(InterfaceReaction*)R, node, kin); + }); + + // register electrochemical reactions reg("electrochemical", []() { return new ElectrochemicalReaction(); }); m_synonyms["butlervolmer_noactivitycoeffs"] = "electrochemical"; m_synonyms["butlervolmer"] = "electrochemical"; m_synonyms["surfaceaffinity"] = "electrochemical"; + void setupElectrochemicalReaction(ElectrochemicalReaction&, + const XML_Node&); + reg_XML("electrochemical", + [](Reaction* R, const XML_Node& node) { + setupElectrochemicalReaction(*(ElectrochemicalReaction*)R, node); + }); + void setupElectrochemicalReaction(ElectrochemicalReaction&, + const AnyMap&, const Kinetics&); + reg_AnyMap("electrochemical", + [](Reaction* R, const AnyMap& node, const Kinetics& kin) { + setupElectrochemicalReaction(*(ElectrochemicalReaction*)R, node, kin); + }); } Reaction* ReactionFactory::newReaction(const XML_Node& rxn_node) @@ -61,7 +165,7 @@ Reaction* ReactionFactory::newReaction(const XML_Node& rxn_node) throw CanteraError("newReaction", "Unknown reaction type '" + rxn_node["type"] + "'"); } - R->setup(rxn_node); + setup_XML(R->type(), R, rxn_node); return R; } @@ -70,7 +174,7 @@ Reaction* ReactionFactory::newReaction(const AnyMap& node, { std::string type = "elementary"; if (node.hasKey("type")) { - type = toLowerCopy(node["type"].asString()); + type = node["type"].asString(); } if (kin.thermo().nDim() < 3) { @@ -88,10 +192,10 @@ Reaction* ReactionFactory::newReaction(const AnyMap& node, try { R = create(type); } catch (CanteraError& err) { - throw InputFileError("newReaction", node["type"], + throw InputFileError("ReactionFactory::newReaction", node["type"], "Unknown reaction type '{}'", type); } - R->setup(node, kin); + setup_AnyMap(type, R, node, kin); return R; } From 14115f994ef6f764e254f7d08c5dc6ea2c23e07b Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 8 Nov 2019 16:35:56 -0600 Subject: [PATCH 04/67] [Kinetics] remove magic numbers from Python interface - replace 'wrapReaction' by 'Reaction.wrap' method - eliminate hard-coded object instantiation --- include/cantera/kinetics/ReactionFactory.h | 5 + interfaces/cython/cantera/_cantera.pxd | 7 +- interfaces/cython/cantera/kinetics.pyx | 2 +- interfaces/cython/cantera/reaction.pyx | 120 ++++++++------------- src/kinetics/ReactionFactory.cpp | 12 +++ 5 files changed, 66 insertions(+), 80 deletions(-) diff --git a/include/cantera/kinetics/ReactionFactory.h b/include/cantera/kinetics/ReactionFactory.h index 6abec368ef..7fa9f32df5 100644 --- a/include/cantera/kinetics/ReactionFactory.h +++ b/include/cantera/kinetics/ReactionFactory.h @@ -49,6 +49,8 @@ class ReactionFactory : public Factory s_factory = 0; } + virtual Reaction* newReaction(const std::string& type); + //! Return a pointer to a new reaction function calculator. /*! * @param type Integer flag specifying the type of reaction function. The @@ -115,6 +117,9 @@ class ReactionFactory : public Factory std::function> m_anymap_setup; }; +//! Create a new empty Reaction object +unique_ptr newReaction(const std::string& type); + //! Create a new Reaction object for the reaction defined in `rxn_node` unique_ptr newReaction(const XML_Node& rxn_node); diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 0400a42aea..f8d851e4fa 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -296,6 +296,7 @@ cdef extern from "cantera/thermo/SurfPhase.h": cdef extern from "cantera/kinetics/ReactionFactory.h" namespace "Cantera": + cdef shared_ptr[CxxReaction] CxxNewReaction "Cantera::newReaction" (string) except +translate_exception cdef shared_ptr[CxxReaction] CxxNewReaction "newReaction" (XML_Node&) except +translate_exception cdef shared_ptr[CxxReaction] CxxNewReaction "newReaction" (CxxAnyMap&, CxxKinetics&) except +translate_exception @@ -320,8 +321,8 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": string reactantString() string productString() string equation() + string type() void validate() except +translate_exception - int reaction_type Composition reactants Composition products Composition orders @@ -1008,7 +1009,8 @@ cdef class InterfacePhase(ThermoPhase): cdef class Reaction: cdef shared_ptr[CxxReaction] _reaction cdef CxxReaction* reaction - cdef _assign(self, shared_ptr[CxxReaction] other) + @staticmethod + cdef wrap(shared_ptr[CxxReaction]) cdef class Arrhenius: cdef CxxArrhenius* rate @@ -1186,7 +1188,6 @@ cdef np.ndarray get_transport_1d(Transport tran, transportMethod1d method) cdef np.ndarray get_transport_2d(Transport tran, transportMethod2d method) cdef CxxIdealGasPhase* getIdealGasPhase(ThermoPhase phase) except * cdef wrapSpeciesThermo(shared_ptr[CxxSpeciesThermo] spthermo) -cdef Reaction wrapReaction(shared_ptr[CxxReaction] reaction) cdef extern from "cantera/thermo/Elements.h" namespace "Cantera": double getElementWeight(string ename) except +translate_exception diff --git a/interfaces/cython/cantera/kinetics.pyx b/interfaces/cython/cantera/kinetics.pyx index d8a3f53e07..7c28ac1c9a 100644 --- a/interfaces/cython/cantera/kinetics.pyx +++ b/interfaces/cython/cantera/kinetics.pyx @@ -105,7 +105,7 @@ cdef class Kinetics(_SolutionBase): ``i_reaction``. Changes to this object do not affect the `Kinetics` or `Solution` object until the `modify_reaction` function is called. """ - return wrapReaction(self.kinetics.reaction(i_reaction)) + return Reaction.wrap(self.kinetics.reaction(i_reaction)) def reactions(self): """ diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 0f7bfa6de5..fcb55bc10c 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -1,15 +1,6 @@ # This file is part of Cantera. See License.txt in the top-level directory or # at https://cantera.org/license.txt for license and copyright information. -cdef extern from "cantera/kinetics/reaction_defs.h" namespace "Cantera": - cdef int ELEMENTARY_RXN - cdef int THREE_BODY_RXN - cdef int FALLOFF_RXN - cdef int PLOG_RXN - cdef int CHEBYSHEV_RXN - cdef int CHEMACT_RXN - cdef int INTERFACE_RXN - cdef class Reaction: """ @@ -61,20 +52,45 @@ cdef class Reaction: R = ct.Reaction.fromCti('''reaction('O + H2 <=> H + OH', [(3.87e4, 'cm3/mol/s'), 2.7, (6260, 'cal/mol')])''') """ - reaction_type = 0 + reaction_type = "" def __cinit__(self, reactants='', products='', init=True, **kwargs): + if init: - self._reaction.reset(newReaction(self.reaction_type)) + self._reaction = CxxNewReaction(stringify((self.reaction_type))) self.reaction = self._reaction.get() if reactants: self.reactants = reactants if products: self.products = products - cdef _assign(self, shared_ptr[CxxReaction] other): - self._reaction = other - self.reaction = self._reaction.get() + @staticmethod + def _all_reaction_objects(): + """ + Retrieve all objects derived from Reaction + """ + def all_subclasses(cls): + return set(cls.__subclasses__()).union( + [s for c in cls.__subclasses__() for s in all_subclasses(c)]) + + return {getattr(c, 'reaction_type'): c for c in all_subclasses(Reaction)} + + @staticmethod + cdef wrap(shared_ptr[CxxReaction] reaction): + """ + Wrap a C++ Reaction object with a Python object of the correct derived type. + """ + # identify class + classes = Reaction._all_reaction_objects() + rxn_type = pystr(reaction.get().type()) + cls = classes.get(rxn_type, Reaction) + + # wrap C++ reaction + cdef Reaction R + R = cls(init=False) + R._reaction = reaction + R.reaction = R._reaction.get() + return R @staticmethod def fromCti(text): @@ -87,7 +103,7 @@ cdef class Reaction: """ cxx_reactions = CxxGetReactions(deref(CxxGetXmlFromString(stringify(text)))) assert cxx_reactions.size() == 1, cxx_reactions.size() - return wrapReaction(cxx_reactions[0]) + return Reaction.wrap(cxx_reactions[0]) @staticmethod def fromXml(text): @@ -99,7 +115,7 @@ cdef class Reaction: The XML input format is deprecated and will be removed in Cantera 3.0. """ cxx_reaction = CxxNewReaction(deref(CxxGetXmlFromString(stringify(text)))) - return wrapReaction(cxx_reaction) + return Reaction.wrap(cxx_reaction) @staticmethod def fromYaml(text, Kinetics kinetics): @@ -114,7 +130,7 @@ cdef class Reaction: """ cxx_reaction = CxxNewReaction(AnyMapFromYamlString(stringify(text)), deref(kinetics.kinetics)) - return wrapReaction(cxx_reaction) + return Reaction.wrap(cxx_reaction) @staticmethod def listFromFile(filename, Kinetics kinetics=None, section='reactions'): @@ -145,7 +161,7 @@ cdef class Reaction: deref(kinetics.kinetics)) else: cxx_reactions = CxxGetReactions(deref(CxxGetXmlFile(stringify(filename)))) - return [wrapReaction(r) for r in cxx_reactions] + return [Reaction.wrap(r) for r in cxx_reactions] @staticmethod def listFromXml(text): @@ -160,7 +176,7 @@ cdef class Reaction: The XML input format is deprecated and will be removed in Cantera 3.0. """ cxx_reactions = CxxGetReactions(deref(CxxGetXmlFromString(stringify(text)))) - return [wrapReaction(r) for r in cxx_reactions] + return [Reaction.wrap(r) for r in cxx_reactions] @staticmethod def listFromCti(text): @@ -175,7 +191,7 @@ cdef class Reaction: # Currently identical to listFromXml since get_XML_from_string is able # to distinguish between CTI and XML. cxx_reactions = CxxGetReactions(deref(CxxGetXmlFromString(stringify(text)))) - return [wrapReaction(r) for r in cxx_reactions] + return [Reaction.wrap(r) for r in cxx_reactions] @staticmethod def listFromYaml(text, Kinetics kinetics): @@ -186,7 +202,7 @@ cdef class Reaction: root = AnyMapFromYamlString(stringify(text)) cxx_reactions = CxxGetReactions(root[stringify("items")], deref(kinetics.kinetics)) - return [wrapReaction(r) for r in cxx_reactions] + return [Reaction.wrap(r) for r in cxx_reactions] property reactant_string: """ @@ -380,7 +396,7 @@ cdef class ElementaryReaction(Reaction): A reaction which follows mass-action kinetics with a modified Arrhenius reaction rate. """ - reaction_type = ELEMENTARY_RXN + reaction_type = "elementary" property rate: """ Get/Set the `Arrhenius` rate coefficient for this reaction. """ @@ -409,7 +425,7 @@ cdef class ThreeBodyReaction(ElementaryReaction): A reaction with a non-reacting third body "M" that acts to add or remove energy from the reacting species. """ - reaction_type = THREE_BODY_RXN + reaction_type = "three-body" cdef CxxThreeBodyReaction* tbr(self): return self.reaction @@ -538,7 +554,7 @@ cdef class FalloffReaction(Reaction): A reaction that is first-order in [M] at low pressure, like a third-body reaction, but zeroth-order in [M] as pressure increases. """ - reaction_type = FALLOFF_RXN + reaction_type = "falloff" cdef CxxFalloffReaction* frxn(self): return self.reaction @@ -615,7 +631,7 @@ cdef class ChemicallyActivatedReaction(FalloffReaction): that the forward rate constant is written as being proportional to the low- pressure rate constant. """ - reaction_type = CHEMACT_RXN + reaction_type = "chemically-activated" cdef class PlogReaction(Reaction): @@ -623,7 +639,7 @@ cdef class PlogReaction(Reaction): A pressure-dependent reaction parameterized by logarithmically interpolating between Arrhenius rate expressions at various pressures. """ - reaction_type = PLOG_RXN + reaction_type = "pressure-dependent-Arrhenius" property rates: """ @@ -666,7 +682,7 @@ cdef class ChebyshevReaction(Reaction): A pressure-dependent reaction parameterized by a bivariate Chebyshev polynomial in temperature and pressure. """ - reaction_type = CHEBYSHEV_RXN + reaction_type = "Chebyshev" property Tmin: """ Minimum temperature [K] for the Chebyshev fit """ @@ -743,7 +759,7 @@ cdef class ChebyshevReaction(Reaction): cdef class InterfaceReaction(ElementaryReaction): """ A reaction occurring on an `Interface` (i.e. a surface or an edge) """ - reaction_type = INTERFACE_RXN + reaction_type = "interface" property coverage_deps: """ @@ -809,51 +825,3 @@ cdef class InterfaceReaction(ElementaryReaction): def __set__(self, species): cdef CxxInterfaceReaction* r = self.reaction r.sticking_species = stringify(species) - - -cdef Reaction wrapReaction(shared_ptr[CxxReaction] reaction): - """ - Wrap a C++ Reaction object with a Python object of the correct derived type. - """ - cdef int reaction_type = reaction.get().reaction_type - - if reaction_type == ELEMENTARY_RXN: - R = ElementaryReaction(init=False) - elif reaction_type == THREE_BODY_RXN: - R = ThreeBodyReaction(init=False) - elif reaction_type == FALLOFF_RXN: - R = FalloffReaction(init=False) - elif reaction_type == CHEMACT_RXN: - R = ChemicallyActivatedReaction(init=False) - elif reaction_type == PLOG_RXN: - R = PlogReaction(init=False) - elif reaction_type == CHEBYSHEV_RXN: - R = ChebyshevReaction(init=False) - elif reaction_type == INTERFACE_RXN: - R = InterfaceReaction(init=False) - else: - R = Reaction(init=False) - - R._assign(reaction) - return R - -cdef CxxReaction* newReaction(int reaction_type): - """ - Create a new C++ Reaction object of the specified type - """ - if reaction_type == ELEMENTARY_RXN: - return new CxxElementaryReaction() - elif reaction_type == THREE_BODY_RXN: - return new CxxThreeBodyReaction() - elif reaction_type == FALLOFF_RXN: - return new CxxFalloffReaction() - elif reaction_type == CHEMACT_RXN: - return new CxxChemicallyActivatedReaction() - elif reaction_type == PLOG_RXN: - return new CxxPlogReaction() - elif reaction_type == CHEBYSHEV_RXN: - return new CxxChebyshevReaction() - elif reaction_type == INTERFACE_RXN: - return new CxxInterfaceReaction() - else: - return new CxxReaction(0) diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index b2b39eb65f..c186658394 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -147,6 +147,12 @@ ReactionFactory::ReactionFactory() }); } +Reaction* ReactionFactory::newReaction(const std::string& type) +{ + Reaction* R = create(toLowerCopy(type)); + return R; +} + Reaction* ReactionFactory::newReaction(const XML_Node& rxn_node) { std::string type = toLowerCopy(rxn_node["type"]); @@ -199,6 +205,12 @@ Reaction* ReactionFactory::newReaction(const AnyMap& node, return R; } +unique_ptr newReaction(const std::string& type) +{ + unique_ptr R(ReactionFactory::factory()->newReaction(type)); + return R; +} + unique_ptr newReaction(const XML_Node& rxn_node) { unique_ptr R(ReactionFactory::factory()->newReaction(rxn_node)); From bb26e7ca7287e9e3be2291f80b80b372f42dfe22 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 9 Nov 2019 14:10:00 -0600 Subject: [PATCH 05/67] [Kinetics] simplify newReaction --- include/cantera/kinetics/Reaction.h | 33 +++++++++++ include/cantera/kinetics/ReactionFactory.h | 33 +++++------ src/kinetics/Reaction.cpp | 1 - src/kinetics/ReactionFactory.cpp | 67 +++++----------------- 4 files changed, 59 insertions(+), 75 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index aaba86193a..af44b8fe4d 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -320,6 +320,39 @@ bool isElectrochemicalReaction(Reaction& R, const Kinetics& kin); //! Parse reaction equation void parseReactionEquation(Reaction& R, const AnyValue& equation, const Kinetics& kin); + +// declarations of setup functions +void setupElementaryReaction(ElementaryReaction&, const XML_Node&); +void setupElementaryReaction(ElementaryReaction&, const AnyMap&, + const Kinetics&); + +void setupThreeBodyReaction(ThreeBodyReaction&, const XML_Node&); +void setupThreeBodyReaction(ThreeBodyReaction&, const AnyMap&, + const Kinetics&); + +void setupFalloffReaction(FalloffReaction&, const XML_Node&); +void setupFalloffReaction(FalloffReaction&, const AnyMap&, + const Kinetics&); + +void setupChemicallyActivatedReaction(ChemicallyActivatedReaction&, + const XML_Node&); + +void setupPlogReaction(PlogReaction&, const XML_Node&); +void setupPlogReaction(PlogReaction&, const AnyMap&, const Kinetics&); + +void setupChebyshevReaction(ChebyshevReaction&, const XML_Node&); +void setupChebyshevReaction(ChebyshevReaction&, const AnyMap&, + const Kinetics&); + +void setupInterfaceReaction(InterfaceReaction&, const XML_Node&); +void setupInterfaceReaction(InterfaceReaction&, const AnyMap&, + const Kinetics&); + +void setupElectrochemicalReaction(ElectrochemicalReaction&, + const XML_Node&); +void setupElectrochemicalReaction(ElectrochemicalReaction&, + const AnyMap&, const Kinetics&); + } #endif diff --git a/include/cantera/kinetics/ReactionFactory.h b/include/cantera/kinetics/ReactionFactory.h index 7fa9f32df5..d483795ed3 100644 --- a/include/cantera/kinetics/ReactionFactory.h +++ b/include/cantera/kinetics/ReactionFactory.h @@ -1,7 +1,7 @@ /** * @file ReactionFactory.h - * Parameterizations for reaction reaction functions. Used by classes - * that implement gas-phase kinetics (GasKinetics, GRI_30_Kinetics) + * Factory class for reaction functions. Used by classes + * that implement kinetics * (see \ref reactionGroup and class \link Cantera::Reaction Reaction\endlink). */ @@ -49,23 +49,6 @@ class ReactionFactory : public Factory s_factory = 0; } - virtual Reaction* newReaction(const std::string& type); - - //! Return a pointer to a new reaction function calculator. - /*! - * @param type Integer flag specifying the type of reaction function. The - * standard types are defined in file reaction_defs.h. A - * factory class derived from ReactionFactory may define other - * types as well. - * @param c input vector of doubles which populates the reaction - * parameterization. - * @returns a pointer to a new Reaction class. - */ - virtual Reaction* newReaction(const XML_Node& rxn_node); - - virtual Reaction* newReaction(const AnyMap& rxn_node, - const Kinetics& kin); - void setup_XML(std::string name, Reaction* R, const XML_Node& rxn_node) { try { @@ -118,13 +101,23 @@ class ReactionFactory : public Factory }; //! Create a new empty Reaction object +/*! + * @param type string identifying type of reaction. + */ unique_ptr newReaction(const std::string& type); //! Create a new Reaction object for the reaction defined in `rxn_node` +/*! + * @param rxn_node XML node describing reaction. + */ unique_ptr newReaction(const XML_Node& rxn_node); //! Create a new Reaction object using the specified parameters -unique_ptr newReaction(const AnyMap& rxn_node, +/*! + * @param node AnyMap node describing reaction. + * @param kin kinetics manager + */ +unique_ptr newReaction(const AnyMap& node, const Kinetics& kin); } #endif diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 987f51b2d7..314fc5f913 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -25,7 +25,6 @@ namespace ba = boost::algorithm; namespace Cantera { - Reaction::Reaction(int type) : reaction_type(type) , reversible(true) diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index c186658394..490c19793a 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -1,4 +1,4 @@ -/** + /** * @file ReactionFactory.cpp */ @@ -23,13 +23,10 @@ ReactionFactory::ReactionFactory() reg("elementary", []() { return new ElementaryReaction(); }); m_synonyms["arrhenius"] = "elementary"; m_synonyms[""] = "elementary"; - void setupElementaryReaction(ElementaryReaction&, const XML_Node&); reg_XML("elementary", [](Reaction* R, const XML_Node& node) { setupElementaryReaction(*(ElementaryReaction*)R, node); }); - void setupElementaryReaction(ElementaryReaction&, const AnyMap&, - const Kinetics&); reg_AnyMap("elementary", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { setupElementaryReaction(*(ElementaryReaction*)R, node, kin); @@ -39,13 +36,10 @@ ReactionFactory::ReactionFactory() reg("three-body", []() { return new ThreeBodyReaction(); }); m_synonyms["threebody"] = "three-body"; m_synonyms["three_body"] = "three-body"; - void setupThreeBodyReaction(ThreeBodyReaction&, const XML_Node&); reg_XML("three-body", [](Reaction* R, const XML_Node& node) { setupThreeBodyReaction(*(ThreeBodyReaction*)R, node); }); - void setupThreeBodyReaction(ThreeBodyReaction&, const AnyMap&, - const Kinetics&); reg_AnyMap("three-body", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { setupThreeBodyReaction(*(ThreeBodyReaction*)R, node, kin); @@ -53,13 +47,10 @@ ReactionFactory::ReactionFactory() // register falloff reactions reg("falloff", []() { return new FalloffReaction(); }); - void setupFalloffReaction(FalloffReaction&, const XML_Node&); reg_XML("falloff", [](Reaction* R, const XML_Node& node) { setupFalloffReaction(*(FalloffReaction*)R, node); }); - void setupFalloffReaction(FalloffReaction&, const AnyMap&, - const Kinetics&); reg_AnyMap("falloff", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { setupFalloffReaction(*(FalloffReaction*)R, node, kin); @@ -69,8 +60,6 @@ ReactionFactory::ReactionFactory() reg("chemically-activated", []() { return new ChemicallyActivatedReaction(); }); m_synonyms["chemact"] = "chemically-activated"; m_synonyms["chemically_activated"] = "chemically-activated"; - void setupChemicallyActivatedReaction(ChemicallyActivatedReaction&, - const XML_Node&); reg_XML("chemically-activated", [](Reaction* R, const XML_Node& node) { setupChemicallyActivatedReaction(*(ChemicallyActivatedReaction*)R, node); @@ -82,15 +71,12 @@ ReactionFactory::ReactionFactory() // register pressure-depdendent-Arrhenius reactions reg("pressure-dependent-Arrhenius", []() { return new PlogReaction(); }); - m_synonyms["pressure-dependent-arrhenius"] = "pressure-dependent-Arrhenius"; m_synonyms["plog"] = "pressure-dependent-Arrhenius"; m_synonyms["pdep_arrhenius"] = "pressure-dependent-Arrhenius"; - void setupPlogReaction(PlogReaction&, const XML_Node&); reg_XML("pressure-dependent-Arrhenius", [](Reaction* R, const XML_Node& node) { setupPlogReaction(*(PlogReaction*)R, node); }); - void setupPlogReaction(PlogReaction&, const AnyMap&, const Kinetics&); reg_AnyMap("pressure-dependent-Arrhenius", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { setupPlogReaction(*(PlogReaction*)R, node, kin); @@ -99,13 +85,10 @@ ReactionFactory::ReactionFactory() // register Chebyshev reactions reg("Chebyshev", []() { return new ChebyshevReaction(); }); m_synonyms["chebyshev"] = "Chebyshev"; - void setupChebyshevReaction(ChebyshevReaction&, const XML_Node&); reg_XML("Chebyshev", [](Reaction* R, const XML_Node& node) { setupChebyshevReaction(*(ChebyshevReaction*)R, node); }); - void setupChebyshevReaction(ChebyshevReaction&, const AnyMap&, - const Kinetics&); reg_AnyMap("Chebyshev", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { setupChebyshevReaction(*(ChebyshevReaction*)R, node, kin); @@ -116,13 +99,10 @@ ReactionFactory::ReactionFactory() m_synonyms["surface"] = "interface"; m_synonyms["edge"] = "interface"; m_synonyms["global"] = "interface"; - void setupInterfaceReaction(InterfaceReaction&, const XML_Node&); reg_XML("interface", [](Reaction* R, const XML_Node& node) { setupInterfaceReaction(*(InterfaceReaction*)R, node); }); - void setupInterfaceReaction(InterfaceReaction&, const AnyMap&, - const Kinetics&); reg_AnyMap("interface", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { setupInterfaceReaction(*(InterfaceReaction*)R, node, kin); @@ -133,27 +113,23 @@ ReactionFactory::ReactionFactory() m_synonyms["butlervolmer_noactivitycoeffs"] = "electrochemical"; m_synonyms["butlervolmer"] = "electrochemical"; m_synonyms["surfaceaffinity"] = "electrochemical"; - void setupElectrochemicalReaction(ElectrochemicalReaction&, - const XML_Node&); reg_XML("electrochemical", [](Reaction* R, const XML_Node& node) { setupElectrochemicalReaction(*(ElectrochemicalReaction*)R, node); }); - void setupElectrochemicalReaction(ElectrochemicalReaction&, - const AnyMap&, const Kinetics&); reg_AnyMap("electrochemical", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { setupElectrochemicalReaction(*(ElectrochemicalReaction*)R, node, kin); }); } -Reaction* ReactionFactory::newReaction(const std::string& type) +unique_ptr newReaction(const std::string& type) { - Reaction* R = create(toLowerCopy(type)); + unique_ptr R(ReactionFactory::factory()->create(type)); return R; } -Reaction* ReactionFactory::newReaction(const XML_Node& rxn_node) +unique_ptr newReaction(const XML_Node& rxn_node) { std::string type = toLowerCopy(rxn_node["type"]); @@ -166,17 +142,18 @@ Reaction* ReactionFactory::newReaction(const XML_Node& rxn_node) Reaction* R; try { - R = create(type); + R = ReactionFactory::factory()->create(type); } catch (CanteraError& err) { throw CanteraError("newReaction", "Unknown reaction type '" + rxn_node["type"] + "'"); } - setup_XML(R->type(), R, rxn_node); - return R; + ReactionFactory::factory()->setup_XML(R->type(), R, rxn_node); + + return unique_ptr(R); } -Reaction* ReactionFactory::newReaction(const AnyMap& node, - const Kinetics& kin) +unique_ptr newReaction(const AnyMap& node, + const Kinetics& kin) { std::string type = "elementary"; if (node.hasKey("type")) { @@ -196,32 +173,14 @@ Reaction* ReactionFactory::newReaction(const AnyMap& node, Reaction* R; try { - R = create(type); + R = ReactionFactory::factory()->create(type); } catch (CanteraError& err) { throw InputFileError("ReactionFactory::newReaction", node["type"], "Unknown reaction type '{}'", type); } - setup_AnyMap(type, R, node, kin); - return R; -} - -unique_ptr newReaction(const std::string& type) -{ - unique_ptr R(ReactionFactory::factory()->newReaction(type)); - return R; -} - -unique_ptr newReaction(const XML_Node& rxn_node) -{ - unique_ptr R(ReactionFactory::factory()->newReaction(rxn_node)); - return R; -} + ReactionFactory::factory()->setup_AnyMap(type, R, node, kin); -unique_ptr newReaction(const AnyMap& rxn_node, - const Kinetics& kin) -{ - unique_ptr R(ReactionFactory::factory()->newReaction(rxn_node, kin)); - return R; + return unique_ptr(R); } } From e7958bea4b181dd123cf2246adb6722ef51ad838 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 11 Nov 2019 15:55:14 -0600 Subject: [PATCH 06/67] [Kinetics] make GasKinetics extendable - deprecate magic numbers --- include/cantera/kinetics/GasKinetics.h | 20 ++++++ src/kinetics/GasKinetics.cpp | 99 ++++++++++++++++---------- 2 files changed, 81 insertions(+), 38 deletions(-) diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index 1973323a7c..061fee59b1 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -67,6 +67,18 @@ class GasKinetics : public BulkKinetics //! reactions. virtual void update_rates_C(); + //! register a function that adds a reaction + void reg_addRxn(const std::string& name, + std::function)> f) { + m_addRxn[name] = f; + } + + //! register a function that modifies a reaction + void reg_modRxn(const std::string& name, + std::function)> f) { + m_modRxn[name] = f; + } + protected: //! Reaction index of each falloff reaction std::vector m_fallindx; @@ -117,6 +129,14 @@ class GasKinetics : public BulkKinetics //! Update the equilibrium constants in molar units. void updateKc(); + + //! map functions adding reactions + std::unordered_map)>> m_addRxn; + + //! map functions modifying reactions + std::unordered_map)>> m_modRxn; }; } diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 152e1f0253..5f14130e90 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -19,6 +19,59 @@ GasKinetics::GasKinetics(ThermoPhase* thermo) : m_logStandConc(0.0), m_pres(0.0) { + reg_addRxn("elementary", + [&](shared_ptr R) { + addElementaryReaction(dynamic_cast(*R)); + }); + reg_modRxn("elementary", + [&](size_t i, shared_ptr R) { + modifyElementaryReaction(i, dynamic_cast(*R)); + }); + + reg_addRxn("three-body", + [&](shared_ptr R) { + addThreeBodyReaction(dynamic_cast(*R)); + }); + reg_modRxn("three-body", + [&](size_t i, shared_ptr R) { + modifyThreeBodyReaction(i, dynamic_cast(*R)); + }); + + reg_addRxn("falloff", + [&](shared_ptr R) { + addFalloffReaction(dynamic_cast(*R)); + }); + reg_modRxn("falloff", + [&](size_t i, shared_ptr R) { + modifyFalloffReaction(i, dynamic_cast(*R)); + }); + + reg_addRxn("chemically-activated", + [&](shared_ptr R) { + addFalloffReaction(dynamic_cast(*R)); + }); + reg_modRxn("chemically-activated", + [&](size_t i, shared_ptr R) { + modifyFalloffReaction(i, dynamic_cast(*R)); + }); + + reg_addRxn("pressure-dependent-Arrhenius", + [&](shared_ptr R) { + addPlogReaction(dynamic_cast(*R)); + }); + + reg_modRxn("pressure-dependent-Arrhenius", + [&](size_t i, shared_ptr R) { + modifyPlogReaction(i, dynamic_cast(*R)); + }); + reg_addRxn("Chebyshev", + [&](shared_ptr R) { + addChebyshevReaction(dynamic_cast(*R)); + }); + reg_modRxn("Chebyshev", + [&](size_t i, shared_ptr R) { + modifyChebyshevReaction(i, dynamic_cast(*R)); + }); } void GasKinetics::update_rates_T() @@ -231,26 +284,11 @@ bool GasKinetics::addReaction(shared_ptr r) return false; } - switch (r->reaction_type) { - case ELEMENTARY_RXN: - addElementaryReaction(dynamic_cast(*r)); - break; - case THREE_BODY_RXN: - addThreeBodyReaction(dynamic_cast(*r)); - break; - case FALLOFF_RXN: - case CHEMACT_RXN: - addFalloffReaction(dynamic_cast(*r)); - break; - case PLOG_RXN: - addPlogReaction(dynamic_cast(*r)); - break; - case CHEBYSHEV_RXN: - addChebyshevReaction(dynamic_cast(*r)); - break; - default: + try { + m_addRxn[r->type()](r); + } catch (std::out_of_range&) { throw CanteraError("GasKinetics::addReaction", - "Unknown reaction type specified: {}", r->reaction_type); + "Unknown reaction type specified: '{}'", r->type()); } return true; } @@ -324,26 +362,11 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) // operations common to all reaction types BulkKinetics::modifyReaction(i, rNew); - switch (rNew->reaction_type) { - case ELEMENTARY_RXN: - modifyElementaryReaction(i, dynamic_cast(*rNew)); - break; - case THREE_BODY_RXN: - modifyThreeBodyReaction(i, dynamic_cast(*rNew)); - break; - case FALLOFF_RXN: - case CHEMACT_RXN: - modifyFalloffReaction(i, dynamic_cast(*rNew)); - break; - case PLOG_RXN: - modifyPlogReaction(i, dynamic_cast(*rNew)); - break; - case CHEBYSHEV_RXN: - modifyChebyshevReaction(i, dynamic_cast(*rNew)); - break; - default: + try { + m_modRxn[rNew->type()](i, rNew); + } catch (std::out_of_range&) { throw CanteraError("GasKinetics::modifyReaction", - "Unknown reaction type specified: {}", rNew->reaction_type); + "Unknown reaction type specified: '{}'", rNew->type()); } // invalidate all cached data From d117c94c6b7f0106017084a3b01da99f3251e1df Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Thu, 14 Nov 2019 15:50:21 -0600 Subject: [PATCH 07/67] [Kinetics] buffer available Reaction class constructors in Cython --- interfaces/cython/cantera/reaction.pyx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index fcb55bc10c..62f45ac20f 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -2,6 +2,10 @@ # at https://cantera.org/license.txt for license and copyright information. +# dictionary to store reaction classes +cdef dict _reaction_class_registry = {} + + cdef class Reaction: """ A class which stores data about a reaction and its rate parameterization so @@ -80,10 +84,18 @@ cdef class Reaction: """ Wrap a C++ Reaction object with a Python object of the correct derived type. """ + # ensure all reaction types are registered + if not(_reaction_class_registry): + def all_subclasses(cls): + return set(cls.__subclasses__()).union( + [s for c in cls.__subclasses__() for s in all_subclasses(c)]) + # update global reaction class registry + _reaction_class_registry.update({getattr(c, 'reaction_type'): c + for c in all_subclasses(Reaction)}) + # identify class - classes = Reaction._all_reaction_objects() rxn_type = pystr(reaction.get().type()) - cls = classes.get(rxn_type, Reaction) + cls = _reaction_class_registry.get(rxn_type, Reaction) # wrap C++ reaction cdef Reaction R From d2f6634215393a9c714cab9bb80de401f6b6439e Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 28 Dec 2019 10:56:40 -0600 Subject: [PATCH 08/67] [Kinetics] move isElectrochemicalReaction - relocation to ReactionFactory removes function from interface --- include/cantera/kinetics/Reaction.h | 3 --- src/kinetics/Reaction.cpp | 29 ----------------------------- src/kinetics/ReactionFactory.cpp | 29 +++++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index af44b8fe4d..3a7f0a0847 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -314,9 +314,6 @@ std::vector > getReactions(const XML_Node& node); std::vector> getReactions(const AnyValue& items, Kinetics& kinetics); -//! Check whether reaction is an electrochemical reaction -bool isElectrochemicalReaction(Reaction& R, const Kinetics& kin); - //! Parse reaction equation void parseReactionEquation(Reaction& R, const AnyValue& equation, const Kinetics& kin); diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 314fc5f913..89d32661cd 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -907,35 +907,6 @@ void setupElectrochemicalReaction(ElectrochemicalReaction& R, "exchange-current-density-formulation", false); } -bool isElectrochemicalReaction(Reaction& R, const Kinetics& kin) -{ - vector_fp e_counter(kin.nPhases(), 0.0); - - // Find the number of electrons in the products for each phase - for (const auto& sp : R.products) { - size_t kkin = kin.kineticsSpeciesIndex(sp.first); - size_t i = kin.speciesPhaseIndex(kkin); - size_t kphase = kin.thermo(i).speciesIndex(sp.first); - e_counter[i] += sp.second * kin.thermo(i).charge(kphase); - } - - // Subtract the number of electrons in the reactants for each phase - for (const auto& sp : R.reactants) { - size_t kkin = kin.kineticsSpeciesIndex(sp.first); - size_t i = kin.speciesPhaseIndex(kkin); - size_t kphase = kin.thermo(i).speciesIndex(sp.first); - e_counter[i] -= sp.second * kin.thermo(i).charge(kphase); - } - - // If the electrons change phases then the reaction is electrochemical - for (double delta_e : e_counter) { - if (std::abs(delta_e) > 1e-4) { - return true; - } - } - return false; -} - std::vector > getReactions(const XML_Node& node) { std::vector > all_reactions; diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 490c19793a..0661a07909 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -123,6 +123,35 @@ ReactionFactory::ReactionFactory() }); } +bool isElectrochemicalReaction(Reaction& R, const Kinetics& kin) +{ + vector_fp e_counter(kin.nPhases(), 0.0); + + // Find the number of electrons in the products for each phase + for (const auto& sp : R.products) { + size_t kkin = kin.kineticsSpeciesIndex(sp.first); + size_t i = kin.speciesPhaseIndex(kkin); + size_t kphase = kin.thermo(i).speciesIndex(sp.first); + e_counter[i] += sp.second * kin.thermo(i).charge(kphase); + } + + // Subtract the number of electrons in the reactants for each phase + for (const auto& sp : R.reactants) { + size_t kkin = kin.kineticsSpeciesIndex(sp.first); + size_t i = kin.speciesPhaseIndex(kkin); + size_t kphase = kin.thermo(i).speciesIndex(sp.first); + e_counter[i] -= sp.second * kin.thermo(i).charge(kphase); + } + + // If the electrons change phases then the reaction is electrochemical + for (double delta_e : e_counter) { + if (std::abs(delta_e) > 1e-4) { + return true; + } + } + return false; +} + unique_ptr newReaction(const std::string& type) { unique_ptr R(ReactionFactory::factory()->create(type)); From 2c94acaacefbc3d64b5fa028162d76fef16aeff1 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 27 Jan 2020 17:37:54 -0600 Subject: [PATCH 09/67] [Reaction] replace m_synonyms by addAlias --- src/kinetics/ReactionFactory.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 0661a07909..3fffb7a115 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -21,8 +21,8 @@ ReactionFactory::ReactionFactory() { // register elementary reactions reg("elementary", []() { return new ElementaryReaction(); }); - m_synonyms["arrhenius"] = "elementary"; - m_synonyms[""] = "elementary"; + addAlias("elementary", "arrhenius"); + addAlias("elementary", ""); reg_XML("elementary", [](Reaction* R, const XML_Node& node) { setupElementaryReaction(*(ElementaryReaction*)R, node); @@ -34,8 +34,8 @@ ReactionFactory::ReactionFactory() // register three-body reactions reg("three-body", []() { return new ThreeBodyReaction(); }); - m_synonyms["threebody"] = "three-body"; - m_synonyms["three_body"] = "three-body"; + addAlias("three-body", "threebody"); + addAlias("three-body", "three_body"); reg_XML("three-body", [](Reaction* R, const XML_Node& node) { setupThreeBodyReaction(*(ThreeBodyReaction*)R, node); @@ -58,8 +58,8 @@ ReactionFactory::ReactionFactory() // register falloff reactions reg("chemically-activated", []() { return new ChemicallyActivatedReaction(); }); - m_synonyms["chemact"] = "chemically-activated"; - m_synonyms["chemically_activated"] = "chemically-activated"; + addAlias("chemically-activated", "chemact"); + addAlias("chemically-activated", "chemically_activated"); reg_XML("chemically-activated", [](Reaction* R, const XML_Node& node) { setupChemicallyActivatedReaction(*(ChemicallyActivatedReaction*)R, node); @@ -71,8 +71,8 @@ ReactionFactory::ReactionFactory() // register pressure-depdendent-Arrhenius reactions reg("pressure-dependent-Arrhenius", []() { return new PlogReaction(); }); - m_synonyms["plog"] = "pressure-dependent-Arrhenius"; - m_synonyms["pdep_arrhenius"] = "pressure-dependent-Arrhenius"; + addAlias("pressure-dependent-Arrhenius", "plog"); + addAlias("pressure-dependent-Arrhenius", "pdep_arrhenius"); reg_XML("pressure-dependent-Arrhenius", [](Reaction* R, const XML_Node& node) { setupPlogReaction(*(PlogReaction*)R, node); @@ -84,7 +84,7 @@ ReactionFactory::ReactionFactory() // register Chebyshev reactions reg("Chebyshev", []() { return new ChebyshevReaction(); }); - m_synonyms["chebyshev"] = "Chebyshev"; + addAlias("Chebyshev", "chebyshev"); reg_XML("Chebyshev", [](Reaction* R, const XML_Node& node) { setupChebyshevReaction(*(ChebyshevReaction*)R, node); @@ -96,9 +96,9 @@ ReactionFactory::ReactionFactory() // register interface reactions reg("interface", []() { return new InterfaceReaction(); }); - m_synonyms["surface"] = "interface"; - m_synonyms["edge"] = "interface"; - m_synonyms["global"] = "interface"; + addAlias("interface", "surface"); + addAlias("interface", "edge"); + addAlias("interface", "global"); reg_XML("interface", [](Reaction* R, const XML_Node& node) { setupInterfaceReaction(*(InterfaceReaction*)R, node); @@ -110,9 +110,9 @@ ReactionFactory::ReactionFactory() // register electrochemical reactions reg("electrochemical", []() { return new ElectrochemicalReaction(); }); - m_synonyms["butlervolmer_noactivitycoeffs"] = "electrochemical"; - m_synonyms["butlervolmer"] = "electrochemical"; - m_synonyms["surfaceaffinity"] = "electrochemical"; + addAlias("electrochemical", "butlervolmer_noactivitycoeffs"); + addAlias("electrochemical", "butlervolmer"); + addAlias("electrochemical", "surfaceaffinity"); reg_XML("electrochemical", [](Reaction* R, const XML_Node& node) { setupElectrochemicalReaction(*(ElectrochemicalReaction*)R, node); From 8918c3675f7bc4d24026fc74650774db6db764da Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 21 Feb 2021 12:18:34 -0600 Subject: [PATCH 10/67] [Kinetics] ensure correct setup routine is called for XML node --- include/cantera/kinetics/ReactionFactory.h | 4 ++-- src/kinetics/ReactionFactory.cpp | 17 ++++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/include/cantera/kinetics/ReactionFactory.h b/include/cantera/kinetics/ReactionFactory.h index d483795ed3..1fd7b556b7 100644 --- a/include/cantera/kinetics/ReactionFactory.h +++ b/include/cantera/kinetics/ReactionFactory.h @@ -114,10 +114,10 @@ unique_ptr newReaction(const XML_Node& rxn_node); //! Create a new Reaction object using the specified parameters /*! - * @param node AnyMap node describing reaction. + * @param rxn_node AnyMap node describing reaction. * @param kin kinetics manager */ -unique_ptr newReaction(const AnyMap& node, +unique_ptr newReaction(const AnyMap& rxn_node, const Kinetics& kin); } #endif diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 3fffb7a115..9909862e07 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -176,23 +176,26 @@ unique_ptr newReaction(const XML_Node& rxn_node) throw CanteraError("newReaction", "Unknown reaction type '" + rxn_node["type"] + "'"); } - ReactionFactory::factory()->setup_XML(R->type(), R, rxn_node); + if (type != "electrochemical") { + type = R->type(); + } + ReactionFactory::factory()->setup_XML(type, R, rxn_node); return unique_ptr(R); } -unique_ptr newReaction(const AnyMap& node, +unique_ptr newReaction(const AnyMap& rxn_node, const Kinetics& kin) { std::string type = "elementary"; - if (node.hasKey("type")) { - type = node["type"].asString(); + if (rxn_node.hasKey("type")) { + type = rxn_node["type"].asString(); } if (kin.thermo().nDim() < 3) { // See if this is an electrochemical reaction Reaction testReaction(0); - parseReactionEquation(testReaction, node["equation"], kin); + parseReactionEquation(testReaction, rxn_node["equation"], kin); if (isElectrochemicalReaction(testReaction, kin)) { type = "electrochemical"; } else { @@ -204,10 +207,10 @@ unique_ptr newReaction(const AnyMap& node, try { R = ReactionFactory::factory()->create(type); } catch (CanteraError& err) { - throw InputFileError("ReactionFactory::newReaction", node["type"], + throw InputFileError("ReactionFactory::newReaction", rxn_node["type"], "Unknown reaction type '{}'", type); } - ReactionFactory::factory()->setup_AnyMap(type, R, node, kin); + ReactionFactory::factory()->setup_AnyMap(type, R, rxn_node, kin); return unique_ptr(R); } From 8eedf280d0d1b666a99139ba5c6c89825631a313 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 22 Feb 2021 08:09:54 -0600 Subject: [PATCH 11/67] [Kinetics] add missing header files --- src/kinetics/ReactionFactory.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 9909862e07..4376c97814 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -5,11 +5,13 @@ // This file is part of Cantera. See License.txt in the top-level directory or // at https://cantera.org/license.txt for license and copyright information. +#include "cantera/thermo/ThermoPhase.h" #include "cantera/kinetics/Reaction.h" #include "cantera/kinetics/ReactionFactory.h" #include "cantera/kinetics/Kinetics.h" #include "cantera/base/ctml.h" #include "cantera/base/AnyMap.h" +#include "cantera/base/stringUtils.h" namespace Cantera { From c3123b1545ce8cb6ba9104a0611a9cc622fc2d2d Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 22 Feb 2021 08:11:02 -0600 Subject: [PATCH 12/67] [Scons] add reference to test-help after build --- SConstruct | 1 + 1 file changed, 1 insertion(+) diff --git a/SConstruct b/SConstruct index da7b332a95..25ad07f469 100644 --- a/SConstruct +++ b/SConstruct @@ -1746,6 +1746,7 @@ def postBuildMessage(target, source, env): print("*******************************************************") print("Compilation completed successfully.\n") print("- To run the test suite, type 'scons test'.") + print("- To list available tests, type 'scons test-help'.") if env['googletest'] == 'none': print(" WARNING: You set the 'googletest' to 'none' and all it's tests will be skipped.") if os.name == 'nt': From c35c98398a7c74356ab1865995090a13077d17a5 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 22 Feb 2021 12:21:06 -0600 Subject: [PATCH 13/67] [Kinetics] initial draft for custom reaction rate defined in Python --- include/cantera/kinetics/RxnRates.h | 39 +++++++++++++++++++++++++ interfaces/cython/cantera/_cantera.pxd | 10 +++++++ interfaces/cython/cantera/reaction.pyx | 40 ++++++++++++++++++++++++++ src/kinetics/RxnRates.cpp | 21 ++++++++++++++ 4 files changed, 110 insertions(+) diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index c5e07de478..85eae3644c 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -15,6 +15,7 @@ namespace Cantera { class Array2D; +class Func1; //! Arrhenius reaction rate type depends only on temperature /** @@ -429,6 +430,44 @@ class ChebyshevRate vector_fp dotProd_; //!< dot product of chebCoeffs with the reduced pressure polynomial }; +//! Custom Python reaction rate depending only on temperature +/** + * The rate expression is provided by external Python code taking a single + * argument (temperature) and does not use a formalized parameterization. + * + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ +class CustomPyRxnRate +{ +public: + //! Constructor. + CustomPyRxnRate(); + + // set custom rate + /** + * The Python function takes a single argument (temperature) and does + * not depend on parameters handled in C++. + */ + void setRateFunction(Func1* f); + + //! Update concentration-dependent parts of the rate coefficient. + /*! + * For this class, there are no concentration-dependent parts, so this + * method does nothing. + */ + void update_C(const double* c) {} + + // Update the value the natural logarithm of the rate constant. + double updateLog(double logT, double recipT) const; + + // Update the value the rate constant. + double updateRC(double logT, double recipT) const; + +protected: + Func1* m_ratefunc; +}; + } #endif diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index f8d851e4fa..c68647db3d 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -312,6 +312,11 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": double temperatureExponent() double activationEnergy_R() + cdef cppclass CxxCustomPyRxnRate "Cantera::CustomPyRxnRate": + CxxCustomPyRxnRate() + void setRateFunction(CxxFunc1*) except +translate_exception + double updateRC(double, double) + cdef cppclass CxxReaction "Cantera::Reaction": # Note, this default constructor doesn't actually exist. The declaration # is required by a Cython bug which should be resolved in Cython 0.22. @@ -1016,6 +1021,11 @@ cdef class Arrhenius: cdef CxxArrhenius* rate cdef Reaction reaction # parent reaction, to prevent garbage collection +cdef class CustomRate: + cdef CxxCustomPyRxnRate* rate + cdef Func1 _rate_func + cdef Reaction reaction # parent reaction, to prevent garbage collection + cdef class Falloff: cdef shared_ptr[CxxFalloff] _falloff cdef CxxFalloff* falloff diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 62f45ac20f..16cb2ba72d 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -403,6 +403,46 @@ cdef copyArrhenius(CxxArrhenius* rate): return r +cdef class CustomRate: + r""" + A custom rate coefficient which depends on temperature only. + + Warning: this class is an experimental part of the Cantera API and + may be changed or removed without notice. + """ + def __cinit__(self, init=True): + if init: + self.rate = new CxxCustomPyRxnRate() + self.reaction = None + + def __dealloc__(self): + if self.reaction is None: + del self.rate + + def __repr__(self): + return 'CustomRate()' + + def set_rate_function(self, k): + r""" + Set the function describing a custom reaction rate. + + >>> rr = CustomRate() + >>> rr.set_rate_function(lambda T: 38.7 * T**2.7 * exp(-3150.15/T)) + """ + cdef Func1 g + if isinstance(k, Func1): + g = k + else: + g = Func1(k) + self._rate_func = g + self.rate.setRateFunction(g.func) + + def __call__(self, float T): + cdef double logT = np.log(T) + cdef double recipT = 1/T + return self.rate.updateRC(logT, recipT) + + cdef class ElementaryReaction(Reaction): """ A reaction which follows mass-action kinetics with a modified Arrhenius diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 269378b772..4a19b4cb4e 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -5,6 +5,7 @@ #include "cantera/kinetics/RxnRates.h" #include "cantera/base/Array.h" +#include "cantera/numerics/Func1.h" namespace Cantera { @@ -158,4 +159,24 @@ ChebyshevRate::ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, } } +CustomPyRxnRate::CustomPyRxnRate() : m_ratefunc(0) {} + +void CustomPyRxnRate::setRateFunction(Func1* f) { + m_ratefunc = f; +} + +double CustomPyRxnRate::updateLog(double logT, double recipT) const { + if (m_ratefunc) { + return std::log( m_ratefunc->eval(1./recipT) ); + } + return 1.; +} + +double CustomPyRxnRate::updateRC(double logT, double recipT) const { + if (m_ratefunc) { + return m_ratefunc->eval(1./recipT); + } + return 0.; +} + } From 93ce06a1fa6377d5d6573ec7e6e9579c2e66d030 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 22 Feb 2021 15:31:04 -0600 Subject: [PATCH 14/67] [Kinetics] create rudimentary hooks for CustomPyReaction --- include/cantera/kinetics/Reaction.h | 16 ++++++++++++++++ include/cantera/kinetics/reaction_defs.h | 5 +++++ src/kinetics/Reaction.cpp | 13 +++++++++++++ src/kinetics/ReactionFactory.cpp | 13 +++++++++++++ 4 files changed, 47 insertions(+) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 3a7f0a0847..e9fa7639e8 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -223,6 +223,22 @@ class ChebyshevReaction : public Reaction ChebyshevRate rate; }; +//! A reaction which follows mass-action kinetics with a custom reaction rate +//! defined in Python. +class CustomPyReaction : public Reaction +{ +public: + CustomPyReaction(); + CustomPyReaction(const Composition& reactants, const Composition& products, + const CustomPyRxnRate& rate); + + virtual std::string type() const { + return "custom-Python"; + } + + CustomPyRxnRate rate; +}; + //! Modifications to an InterfaceReaction rate based on a surface species //! coverage. struct CoverageDependency diff --git a/include/cantera/kinetics/reaction_defs.h b/include/cantera/kinetics/reaction_defs.h index 8eaacdfbce..36972fd8de 100644 --- a/include/cantera/kinetics/reaction_defs.h +++ b/include/cantera/kinetics/reaction_defs.h @@ -57,6 +57,11 @@ const int PLOG_RXN = 5; */ const int CHEBYSHEV_RXN = 6; +/** + * A custom Python reaction + */ +const int CUSTOMPY_RXN = 99; + /** * A chemical activation reaction. For these reactions, the rate falls * off as the pressure increases, due to collisional stabilization of diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 89d32661cd..39836f6d50 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -258,6 +258,19 @@ ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, { } +CustomPyReaction::CustomPyReaction() + : Reaction(CUSTOMPY_RXN) +{ +} + +CustomPyReaction::CustomPyReaction(const Composition& reactants_, + const Composition& products_, + const CustomPyRxnRate& rate_) + : Reaction(CUSTOMPY_RXN, reactants_, products_) + , rate(rate_) +{ +} + InterfaceReaction::InterfaceReaction() : is_sticking_coefficient(false) , use_motz_wise_correction(false) diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 4376c97814..6a19f4c7c2 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -96,6 +96,19 @@ ReactionFactory::ReactionFactory() setupChebyshevReaction(*(ChebyshevReaction*)R, node, kin); }); + // register custom Python reactions + reg("custom-Python", []() { return new CustomPyReaction(); }); + reg_XML("custom-Python", + [](Reaction* R, const XML_Node& node) { + throw CanteraError("ReactionFactory", "Custom Python reactions " + "cannot be created from XML nodes'"); + }); + reg_AnyMap("custom-Python", + [](Reaction* R, const AnyMap& node, const Kinetics& kin) { + throw CanteraError("ReactionFactory", "Custom Python reactions " + "are not yet fully implemented'"); + }); + // register interface reactions reg("interface", []() { return new InterfaceReaction(); }); addAlias("interface", "surface"); From 37ce1146b610c3486f64d56ab97fa21b6d7729d8 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 22 Feb 2021 15:51:50 -0600 Subject: [PATCH 15/67] [Kinetics] expose CustomReaction to Python --- interfaces/cython/cantera/_cantera.pxd | 4 ++++ interfaces/cython/cantera/reaction.pyx | 26 ++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index c68647db3d..80cdf8f720 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -399,6 +399,10 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxChebyshevReaction "Cantera::ChebyshevReaction" (CxxReaction): CxxChebyshevRate rate + cdef cppclass CxxCustomPyReaction "Cantera::CustomPyReaction" (CxxReaction): + CxxCustomPyReaction() + CxxCustomPyRxnRate rate + cdef cppclass CxxCoverageDependency "Cantera::CoverageDependency": CxxCoverageDependency(double, double, double) double a diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 16cb2ba72d..6df4f4814a 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -424,7 +424,7 @@ cdef class CustomRate: def set_rate_function(self, k): r""" - Set the function describing a custom reaction rate. + Set the function describing a custom reaction rate. >>> rr = CustomRate() >>> rr.set_rate_function(lambda T: 38.7 * T**2.7 * exp(-3150.15/T)) @@ -436,12 +436,18 @@ cdef class CustomRate: g = Func1(k) self._rate_func = g self.rate.setRateFunction(g.func) - + def __call__(self, float T): cdef double logT = np.log(T) cdef double recipT = 1/T return self.rate.updateRC(logT, recipT) +cdef wrapCustomRate(CxxCustomPyRxnRate* rate, Reaction reaction): + r = CustomRate(init=False) + r.rate = rate + r.reaction = reaction + return r + cdef class ElementaryReaction(Reaction): """ @@ -809,6 +815,22 @@ cdef class ChebyshevReaction(Reaction): return r.rate.updateRC(logT, recipT) +cdef class CustomReaction(Reaction): + """ + A reaction which follows mass-action kinetics with a custom reaction rate. + """ + reaction_type = "custom-Python" + + property rate: + """ Get/Set the `CustomRate` rate coefficient for this reaction. """ + def __get__(self): + cdef CxxCustomPyReaction* r = self.reaction + return wrapCustomRate(&(r.rate), self) + def __set__(self, CustomRate rate): + cdef CxxCustomPyReaction* r = self.reaction + r.rate = deref(rate.rate) + + cdef class InterfaceReaction(ElementaryReaction): """ A reaction occurring on an `Interface` (i.e. a surface or an edge) """ reaction_type = "interface" From 7d8ecbed127f8c429f1858a7385eb24e0b168972 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 22 Feb 2021 22:23:43 -0600 Subject: [PATCH 16/67] [Kinetics] implement Python constructor for CustomReaction --- interfaces/cython/cantera/reaction.pyx | 28 ++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 6df4f4814a..156b00ec2a 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -407,13 +407,20 @@ cdef class CustomRate: r""" A custom rate coefficient which depends on temperature only. + The simplest way to create a `CustomRate` object is to use a lambda function, + for example:: + + rr = CustomRate(lambda T: 38.7 * T**2.7 * exp(-3150.15/T)) + Warning: this class is an experimental part of the Cantera API and may be changed or removed without notice. """ - def __cinit__(self, init=True): + def __cinit__(self, k=None, init=True): if init: self.rate = new CxxCustomPyRxnRate() self.reaction = None + if k: + self.set_rate_function(k) def __dealloc__(self): if self.reaction is None: @@ -818,9 +825,26 @@ cdef class ChebyshevReaction(Reaction): cdef class CustomReaction(Reaction): """ A reaction which follows mass-action kinetics with a custom reaction rate. + + An example for the definition of a `CustomReaction` object is:: + + rxn = CustomReaction(equation='H2 + O <=> H + OH', + rate=lambda T: 38.7 * T**2.7 * exp(-3150.15/T), + kinetics=gas) + + Warning: this class is an experimental part of the Cantera API and + may be changed or removed without notice. """ reaction_type = "custom-Python" + def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): + + if init: + yaml = '{{equation: {}, type: {}}}'.format(equation, self.reaction_type) + self._reaction = CxxNewReaction(AnyMapFromYamlString(stringify(yaml)), + deref(kinetics.kinetics)) + self.rate = CustomRate(rate) + property rate: """ Get/Set the `CustomRate` rate coefficient for this reaction. """ def __get__(self): @@ -841,7 +865,7 @@ cdef class InterfaceReaction(ElementaryReaction): dependent on surface species coverages. The keys of the dict are species names, and the values are tuples specifying the three coverage parameters ``(a, m, E)`` which are the modifiers for the pre-exponential - factor [m, kmol, s units], the temperature exponent [nondimensional], + factor [m, kmol, s units], The temperature exponent [nondimensional], and the activation energy [J/kmol], respectively. """ def __get__(self): From 9126fb5a03a7fe7c0053c777e289744e3b98edcb Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 23 Feb 2021 01:20:34 -0600 Subject: [PATCH 17/67] [Kinetics] implement setup of CustomReaction --- include/cantera/kinetics/Reaction.h | 3 ++ interfaces/cython/cantera/reaction.pyx | 53 +++++++++++++++++++------- src/kinetics/Reaction.cpp | 7 ++++ src/kinetics/ReactionFactory.cpp | 3 +- 4 files changed, 51 insertions(+), 15 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index e9fa7639e8..631afb9bce 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -357,6 +357,9 @@ void setupChebyshevReaction(ChebyshevReaction&, const XML_Node&); void setupChebyshevReaction(ChebyshevReaction&, const AnyMap&, const Kinetics&); +void setupCustomPyReaction(CustomPyReaction&, const AnyMap&, + const Kinetics&); + void setupInterfaceReaction(InterfaceReaction&, const XML_Node&); void setupInterfaceReaction(InterfaceReaction&, const AnyMap&, const Kinetics&); diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 156b00ec2a..5c80a72f63 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -381,7 +381,7 @@ cdef class Arrhenius: return self.rate.activationEnergy_R() * gas_constant def __repr__(self): - return 'Arrhenius(A={:g}, b={:g}, E={:g})'.format( + return ''.format( self.pre_exponential_factor, self.temperature_exponent, self.activation_energy) @@ -419,22 +419,23 @@ cdef class CustomRate: if init: self.rate = new CxxCustomPyRxnRate() self.reaction = None - if k: - self.set_rate_function(k) + self._rate_func = None + if k: + self.set_rate_function(k) def __dealloc__(self): if self.reaction is None: del self.rate def __repr__(self): - return 'CustomRate()' + return '' def set_rate_function(self, k): r""" - Set the function describing a custom reaction rate. + Set the function describing a custom reaction rate:: - >>> rr = CustomRate() - >>> rr.set_rate_function(lambda T: 38.7 * T**2.7 * exp(-3150.15/T)) + rr = CustomRate() + rr.set_rate_function(lambda T: 38.7 * T**2.7 * exp(-3150.15/T)) """ cdef Func1 g if isinstance(k, Func1): @@ -449,6 +450,7 @@ cdef class CustomRate: cdef double recipT = 1/T return self.rate.updateRC(logT, recipT) + cdef wrapCustomRate(CxxCustomPyRxnRate* rate, Reaction reaction): r = CustomRate(init=False) r.rate = rate @@ -460,9 +462,31 @@ cdef class ElementaryReaction(Reaction): """ A reaction which follows mass-action kinetics with a modified Arrhenius reaction rate. + + An example for the definition of a `ElementaryReaction` object is given as:: + + rxn = ElementaryReaction(equation='H2 + O <=> H + OH', + rate={'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07}, + kinetics=gas) """ reaction_type = "elementary" + def __init__(self, equation=None, rate=None, Kinetics kinetics=None, + init=True, **kwargs): + + if init and equation and rate and kinetics: + if isinstance(rate, dict): + coeffs = ['{}: {}'.format(k, v) for k, v in rate.items()] + else: + raise ValueError('Invalid rate definition') + + rate_def = '{{{}}}'.format(', '.join(coeffs)) + yaml = '{{equation: {}, rate-constant: {}, type: {}}}'.format( + equation, rate_def, self.reaction_type) + self._reaction = CxxNewReaction(AnyMapFromYamlString(stringify(yaml)), + deref(kinetics.kinetics)) + self.reaction = self._reaction.get() + property rate: """ Get/Set the `Arrhenius` rate coefficient for this reaction. """ def __get__(self): @@ -826,10 +850,10 @@ cdef class CustomReaction(Reaction): """ A reaction which follows mass-action kinetics with a custom reaction rate. - An example for the definition of a `CustomReaction` object is:: + An example for the definition of a `CustomReaction` object is given as:: rxn = CustomReaction(equation='H2 + O <=> H + OH', - rate=lambda T: 38.7 * T**2.7 * exp(-3150.15/T), + rate=lambda T: 38.7 * T**2.7 * exp(-3150.15428/T), kinetics=gas) Warning: this class is an experimental part of the Cantera API and @@ -837,16 +861,19 @@ cdef class CustomReaction(Reaction): """ reaction_type = "custom-Python" - def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): + def __init__(self, equation=None, rate=None, Kinetics kinetics=None, + init=True, **kwargs): - if init: + if init and equation and kinetics: yaml = '{{equation: {}, type: {}}}'.format(equation, self.reaction_type) self._reaction = CxxNewReaction(AnyMapFromYamlString(stringify(yaml)), deref(kinetics.kinetics)) - self.rate = CustomRate(rate) + self.reaction = self._reaction.get() + rate_obj = CustomRate(rate) + self.rate = rate_obj property rate: - """ Get/Set the `CustomRate` rate coefficient for this reaction. """ + """ Get/Set the `CustomRate` object for this reaction. """ def __get__(self): cdef CxxCustomPyReaction* r = self.reaction return wrapCustomRate(&(r.rate), self) diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 39836f6d50..b3bea58628 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -812,6 +812,13 @@ void setupChebyshevReaction(ChebyshevReaction&R, const AnyMap& node, coeffs); } +void setupCustomPyReaction(CustomPyReaction& R, const AnyMap& node, + const Kinetics& kin) +{ + setupReaction(R, node, kin); + R.rate = CustomPyRxnRate(); +} + void setupInterfaceReaction(InterfaceReaction& R, const XML_Node& rxn_node) { XML_Node& arr = rxn_node.child("rateCoeff").child("Arrhenius"); diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 6a19f4c7c2..85414d0790 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -105,8 +105,7 @@ ReactionFactory::ReactionFactory() }); reg_AnyMap("custom-Python", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - throw CanteraError("ReactionFactory", "Custom Python reactions " - "are not yet fully implemented'"); + setupCustomPyReaction(*(CustomPyReaction*)R, node, kin); }); // register interface reactions From 43261defa75ad5cd5feb881758df0b7f198f0495 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 26 Feb 2021 22:05:27 -0600 Subject: [PATCH 18/67] [Kinetics] introduce boolean to mark reaction validity --- include/cantera/kinetics/Reaction.h | 14 ++++++++++++++ src/kinetics/Reaction.cpp | 8 ++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 631afb9bce..2b94cacba8 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -46,6 +46,16 @@ class Reaction //! valid. virtual void validate(); + //! Get validity flag of reaction + bool valid() const { + return m_valid; + } + + //! Set validity flag of reaction + void set_valid(bool valid) { + m_valid = valid; + } + //! Type of the reaction. The valid types are listed in the file, //! reaction_defs.h, with constants ending in `RXN`. int reaction_type; @@ -80,6 +90,10 @@ class Reaction //! Input data used for specific models AnyMap input; + +protected: + //! Flag indicating whether reaction is set up correctly + bool m_valid; }; diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index b3bea58628..0fcb5292fc 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -31,6 +31,7 @@ Reaction::Reaction(int type) , duplicate(false) , allow_nonreactant_orders(false) , allow_negative_orders(false) + , m_valid(true) { } @@ -43,6 +44,7 @@ Reaction::Reaction(int type, const Composition& reactants_, , duplicate(false) , allow_nonreactant_orders(false) , allow_negative_orders(false) + , m_valid(true) { } @@ -314,7 +316,7 @@ Arrhenius readArrhenius(const XML_Node& arrhenius_node) Units rateCoeffUnits(const Reaction& R, const Kinetics& kin, int pressure_dependence=0) { - if (R.reaction_type == INVALID_RXN) { + if (!R.valid()) { // If a reaction is invalid because of missing species in the Kinetics // object, determining the units of the rate coefficient is impossible. return Units(); @@ -523,6 +525,7 @@ void parseReactionEquation(Reaction& R, const AnyValue& equation, if (kin.kineticsSpeciesIndex(species) == npos && stoich != -1 && species != "M") { R.reaction_type = INVALID_RXN; + R.set_valid(false); } if (reactants) { @@ -555,6 +558,7 @@ void setupReaction(Reaction& R, const AnyMap& node, const Kinetics& kin) R.orders[order.first] = order.second; if (kin.kineticsSpeciesIndex(order.first) == npos) { R.reaction_type = INVALID_RXN; + R.set_valid(false); } } } @@ -942,7 +946,7 @@ std::vector> getReactions(const AnyValue& items, std::vector> all_reactions; for (const auto& node : items.asVector()) { shared_ptr R(newReaction(node, kinetics)); - if (R->reaction_type != INVALID_RXN) { + if (R->valid()) { all_reactions.emplace_back(R); } else if (!kinetics.skipUndeclaredSpecies()) { throw InputFileError("getReactions", node, From d4995275b1505264f385c46920fa5391293dbdf9 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 26 Feb 2021 22:21:57 -0600 Subject: [PATCH 19/67] [Kinetics] replace magic numbers --- src/kinetics/Kinetics.cpp | 6 +++--- src/kinetics/Reaction.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/kinetics/Kinetics.cpp b/src/kinetics/Kinetics.cpp index 42ab44e038..81540e20df 100644 --- a/src/kinetics/Kinetics.cpp +++ b/src/kinetics/Kinetics.cpp @@ -123,8 +123,8 @@ std::pair Kinetics::checkDuplicates(bool throw_err) const continue; // stoichiometries differ (not by a multiple) } else if (c < 0.0 && !R.reversible && !other.reversible) { continue; // irreversible reactions in opposite directions - } else if (R.reaction_type == FALLOFF_RXN || - R.reaction_type == CHEMACT_RXN) { + } else if (R.type() == "falloff" || + R.type() == "chemically-activated") { ThirdBody& tb1 = dynamic_cast(R).third_body; ThirdBody& tb2 = dynamic_cast(other).third_body; bool thirdBodyOk = true; @@ -140,7 +140,7 @@ std::pair Kinetics::checkDuplicates(bool throw_err) const if (thirdBodyOk) { continue; // No overlap in third body efficiencies } - } else if (R.reaction_type == THREE_BODY_RXN) { + } else if (R.type() == "three-body") { ThirdBody& tb1 = dynamic_cast(R).third_body; ThirdBody& tb2 = dynamic_cast(other).third_body; bool thirdBodyOk = true; diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 0fcb5292fc..d71f01a46b 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -320,7 +320,7 @@ Units rateCoeffUnits(const Reaction& R, const Kinetics& kin, // If a reaction is invalid because of missing species in the Kinetics // object, determining the units of the rate coefficient is impossible. return Units(); - } else if (R.reaction_type == INTERFACE_RXN + } else if (R.type() == "interface" && dynamic_cast(R).is_sticking_coefficient) { // Sticking coefficients are dimensionless return Units(); From 4c14bb721ba38b14b7dbf7db7dc04cb2a0083af8 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 26 Feb 2021 22:46:05 -0600 Subject: [PATCH 20/67] [Kinetics] create hooks in GasKinetics --- include/cantera/kinetics/GasKinetics.h | 2 ++ src/kinetics/GasKinetics.cpp | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index 061fee59b1..bd973b9b3b 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -121,11 +121,13 @@ class GasKinetics : public BulkKinetics void addFalloffReaction(FalloffReaction& r); void addPlogReaction(PlogReaction& r); void addChebyshevReaction(ChebyshevReaction& r); + void addCustomPyReaction(CustomPyReaction& r); void modifyThreeBodyReaction(size_t i, ThreeBodyReaction& r); void modifyFalloffReaction(size_t i, FalloffReaction& r); void modifyPlogReaction(size_t i, PlogReaction& r); void modifyChebyshevReaction(size_t i, ChebyshevReaction& r); + void modifyCustomPyReaction(size_t i, CustomPyReaction& r); //! Update the equilibrium constants in molar units. void updateKc(); diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 5f14130e90..5d3afb188b 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -72,6 +72,14 @@ GasKinetics::GasKinetics(ThermoPhase* thermo) : [&](size_t i, shared_ptr R) { modifyChebyshevReaction(i, dynamic_cast(*R)); }); + reg_addRxn("custom-Python", + [&](shared_ptr R) { + addCustomPyReaction(dynamic_cast(*R)); + }); + reg_modRxn("custom-Python", + [&](size_t i, shared_ptr R) { + modifyCustomPyReaction(i, dynamic_cast(*R)); + }); } void GasKinetics::update_rates_T() @@ -357,6 +365,11 @@ void GasKinetics::addChebyshevReaction(ChebyshevReaction& r) m_cheb_rates.install(nReactions()-1, r.rate); } +void GasKinetics::addCustomPyReaction(CustomPyReaction& r) +{ + r.set_valid(false); +} + void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) { // operations common to all reaction types @@ -398,6 +411,11 @@ void GasKinetics::modifyChebyshevReaction(size_t i, ChebyshevReaction& r) m_cheb_rates.replace(i, r.rate); } +void GasKinetics::modifyCustomPyReaction(size_t i, CustomPyReaction& r) +{ + r.set_valid(false); +} + void GasKinetics::init() { BulkKinetics::init(); From 08297931bf19564a2419cb98fb40fdb6dcd6e141 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Thu, 25 Feb 2021 22:32:43 -0600 Subject: [PATCH 21/67] [Kinetics] add initial unit tests --- interfaces/cython/cantera/reaction.pyx | 9 +++- .../cython/cantera/test/test_kinetics.py | 47 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 5c80a72f63..810276ef57 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -416,12 +416,12 @@ cdef class CustomRate: may be changed or removed without notice. """ def __cinit__(self, k=None, init=True): + if init: self.rate = new CxxCustomPyRxnRate() self.reaction = None self._rate_func = None - if k: - self.set_rate_function(k) + self.set_rate_function(k) def __dealloc__(self): if self.reaction is None: @@ -437,6 +437,10 @@ cdef class CustomRate: rr = CustomRate() rr.set_rate_function(lambda T: 38.7 * T**2.7 * exp(-3150.15/T)) """ + if k is None: + self._rate_func = None + return + cdef Func1 g if isinstance(k, Func1): g = k @@ -455,6 +459,7 @@ cdef wrapCustomRate(CxxCustomPyRxnRate* rate, Reaction reaction): r = CustomRate(init=False) r.rate = rate r.reaction = reaction + #r.set_rate_function(k) return r diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 8b08fdff97..3baeb930eb 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -3,6 +3,7 @@ import itertools from os.path import join as pjoin import os +from math import exp import cantera as ct from . import utilities @@ -855,10 +856,56 @@ def test_elementary(self): self.assertNear(gas2.net_rates_of_progress[0], self.gas.net_rates_of_progress[2]) + def test_elementary2(self): + gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', + species=self.species, reactions=[]) + gas2.TPX = self.gas.TPX + + r = ct.ElementaryReaction(equation='H2 + O <=> H + OH', + rate={'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07}, + kinetics=gas2) + + self.assertNear(r.rate(self.gas.T), self.gas.forward_rate_constants[2]) + self.assertEqual(r.reactants, self.gas.reaction(2).reactants) + self.assertEqual(r.products, self.gas.reaction(2).products) + + gas2.add_reaction(r) + self.assertNear(gas2.forward_rate_constants[0], + self.gas.forward_rate_constants[2]) + self.assertNear(gas2.net_rates_of_progress[0], + self.gas.net_rates_of_progress[2]) + def test_arrhenius_rate(self): R = self.gas.reaction(2) self.assertNear(R.rate(self.gas.T), self.gas.forward_rate_constants[2]) + def test_custom(self): + r = ct.CustomReaction({'O':1, 'H2':1}, {'H':1, 'OH':1}) + r.rate = ct.CustomRate(lambda T: 38.7 * T**2.7 * exp(-3150.15428/T)) + + gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', + species=self.species, reactions=[r]) + gas2.TPX = self.gas.TPX + + #self.assertNear(gas2.forward_rate_constants[0], + # self.gas.forward_rate_constants[2]) + #self.assertNear(gas2.net_rates_of_progress[0], + # self.gas.net_rates_of_progress[2]) + + def test_custom2(self): + r = ct.CustomReaction(equation='H2 + O <=> H + OH', + rate=lambda T: 38.7 * T**2.7 * exp(-3150.15428/T), + kinetics=self.gas) + self.assertEqual(r.reactants, {'O':1, 'H2':1}) + self.assertEqual(r.products, {'H':1, 'OH':1}) + #self.assertNear(r.rate(self.gas.T), self.gas.forward_rate_constants[2]) + + def test_custom_rate(self): + # probe O + H2 <=> H + OH + rr = ct.CustomRate(lambda T: 38.7 * T**2.7 * exp(-3150.15428/T)) + R = self.gas.reaction(2) + self.assertNear(R.rate(self.gas.T), rr(self.gas.T)) + def test_negative_A(self): species = ct.Species.listFromFile('gri30.cti') r = ct.ElementaryReaction('NH:1, NO:1', 'N2O:1, H:1') From 1c525f79f8ffa4e841ee2defaa121e3daa82e0a3 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 26 Feb 2021 23:04:07 -0600 Subject: [PATCH 22/67] [Kinetics] simplify naming of CustomPyRate --- include/cantera/kinetics/Reaction.h | 4 ++-- include/cantera/kinetics/RxnRates.h | 4 ++-- interfaces/cython/cantera/_cantera.pxd | 8 ++++---- interfaces/cython/cantera/reaction.pyx | 4 ++-- src/kinetics/Reaction.cpp | 4 ++-- src/kinetics/RxnRates.cpp | 8 ++++---- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 2b94cacba8..b7b72cb431 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -244,13 +244,13 @@ class CustomPyReaction : public Reaction public: CustomPyReaction(); CustomPyReaction(const Composition& reactants, const Composition& products, - const CustomPyRxnRate& rate); + const CustomPyRate& rate); virtual std::string type() const { return "custom-Python"; } - CustomPyRxnRate rate; + CustomPyRate rate; }; //! Modifications to an InterfaceReaction rate based on a surface species diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 85eae3644c..ee9fdc9cee 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -438,11 +438,11 @@ class ChebyshevRate * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. */ -class CustomPyRxnRate +class CustomPyRate { public: //! Constructor. - CustomPyRxnRate(); + CustomPyRate(); // set custom rate /** diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 80cdf8f720..f14e9d74e8 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -312,8 +312,8 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": double temperatureExponent() double activationEnergy_R() - cdef cppclass CxxCustomPyRxnRate "Cantera::CustomPyRxnRate": - CxxCustomPyRxnRate() + cdef cppclass CxxCustomPyRate "Cantera::CustomPyRate": + CxxCustomPyRate() void setRateFunction(CxxFunc1*) except +translate_exception double updateRC(double, double) @@ -401,7 +401,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxCustomPyReaction "Cantera::CustomPyReaction" (CxxReaction): CxxCustomPyReaction() - CxxCustomPyRxnRate rate + CxxCustomPyRate rate cdef cppclass CxxCoverageDependency "Cantera::CoverageDependency": CxxCoverageDependency(double, double, double) @@ -1026,7 +1026,7 @@ cdef class Arrhenius: cdef Reaction reaction # parent reaction, to prevent garbage collection cdef class CustomRate: - cdef CxxCustomPyRxnRate* rate + cdef CxxCustomPyRate* rate cdef Func1 _rate_func cdef Reaction reaction # parent reaction, to prevent garbage collection diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 810276ef57..64e9269a2b 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -418,7 +418,7 @@ cdef class CustomRate: def __cinit__(self, k=None, init=True): if init: - self.rate = new CxxCustomPyRxnRate() + self.rate = new CxxCustomPyRate() self.reaction = None self._rate_func = None self.set_rate_function(k) @@ -455,7 +455,7 @@ cdef class CustomRate: return self.rate.updateRC(logT, recipT) -cdef wrapCustomRate(CxxCustomPyRxnRate* rate, Reaction reaction): +cdef wrapCustomRate(CxxCustomPyRate* rate, Reaction reaction): r = CustomRate(init=False) r.rate = rate r.reaction = reaction diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index d71f01a46b..97ceeb8354 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -267,7 +267,7 @@ CustomPyReaction::CustomPyReaction() CustomPyReaction::CustomPyReaction(const Composition& reactants_, const Composition& products_, - const CustomPyRxnRate& rate_) + const CustomPyRate& rate_) : Reaction(CUSTOMPY_RXN, reactants_, products_) , rate(rate_) { @@ -820,7 +820,7 @@ void setupCustomPyReaction(CustomPyReaction& R, const AnyMap& node, const Kinetics& kin) { setupReaction(R, node, kin); - R.rate = CustomPyRxnRate(); + R.rate = CustomPyRate(); } void setupInterfaceReaction(InterfaceReaction& R, const XML_Node& rxn_node) diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 4a19b4cb4e..63693b6327 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -159,20 +159,20 @@ ChebyshevRate::ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, } } -CustomPyRxnRate::CustomPyRxnRate() : m_ratefunc(0) {} +CustomPyRate::CustomPyRate() : m_ratefunc(0) {} -void CustomPyRxnRate::setRateFunction(Func1* f) { +void CustomPyRate::setRateFunction(Func1* f) { m_ratefunc = f; } -double CustomPyRxnRate::updateLog(double logT, double recipT) const { +double CustomPyRate::updateLog(double logT, double recipT) const { if (m_ratefunc) { return std::log( m_ratefunc->eval(1./recipT) ); } return 1.; } -double CustomPyRxnRate::updateRC(double logT, double recipT) const { +double CustomPyRate::updateRC(double logT, double recipT) const { if (m_ratefunc) { return m_ratefunc->eval(1./recipT); } From e99fd7909f17cb01a03b5e57b5d89e85e1a76430 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 26 Feb 2021 23:26:33 -0600 Subject: [PATCH 23/67] [Kinetics] add default constructor for C++ Reaction --- include/cantera/kinetics/Reaction.h | 1 + include/cantera/kinetics/reaction_defs.h | 1 + interfaces/cython/cantera/_cantera.pxd | 3 --- src/kinetics/Reaction.cpp | 10 ++++++++++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index b7b72cb431..ef5f5ec8c0 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -23,6 +23,7 @@ class XML_Node; class Reaction { public: + explicit Reaction(); explicit Reaction(int type); Reaction(int type, const Composition& reactants, const Composition& products); diff --git a/include/cantera/kinetics/reaction_defs.h b/include/cantera/kinetics/reaction_defs.h index 36972fd8de..2d8a514fcf 100644 --- a/include/cantera/kinetics/reaction_defs.h +++ b/include/cantera/kinetics/reaction_defs.h @@ -16,6 +16,7 @@ namespace Cantera const int INVALID_RXN = 0; const int NONE = 0; +const int UNUSED_MAGIC_NUMBER = -1; /// @name Reaction Types diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index f14e9d74e8..b290842644 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -318,10 +318,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": double updateRC(double, double) cdef cppclass CxxReaction "Cantera::Reaction": - # Note, this default constructor doesn't actually exist. The declaration - # is required by a Cython bug which should be resolved in Cython 0.22. CxxReaction() - CxxReaction(int) string reactantString() string productString() diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 97ceeb8354..3f40a0c506 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -25,6 +25,16 @@ namespace ba = boost::algorithm; namespace Cantera { +Reaction::Reaction() + : reaction_type(UNUSED_MAGIC_NUMBER) + , reversible(true) + , duplicate(false) + , allow_nonreactant_orders(false) + , allow_negative_orders(false) + , m_valid(true) +{ +} + Reaction::Reaction(int type) : reaction_type(type) , reversible(true) From d7e03820f0440212f2c724e3ad05e3b8b3fb5749 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 27 Feb 2021 00:04:45 -0600 Subject: [PATCH 24/67] [Kinetics] mark unused reaction type magic numbers --- include/cantera/kinetics/Kinetics.h | 7 +++++ .../cython/cantera/test/test_kinetics.py | 2 +- src/kinetics/Kinetics.cpp | 3 +++ src/kinetics/Reaction.cpp | 26 +++++++++---------- src/kinetics/ReactionPath.cpp | 8 +++--- test/kinetics/kineticsFromYaml.cpp | 3 ++- 6 files changed, 29 insertions(+), 20 deletions(-) diff --git a/include/cantera/kinetics/Kinetics.h b/include/cantera/kinetics/Kinetics.h index ff2c1ee755..7836701340 100644 --- a/include/cantera/kinetics/Kinetics.h +++ b/include/cantera/kinetics/Kinetics.h @@ -604,6 +604,13 @@ class Kinetics */ virtual int reactionType(size_t i) const; + /** + * String specifying the type of reaction. + * + * @param i reaction index + */ + virtual std::string reactionTypeStr(size_t i) const; + /** * True if reaction i has been declared to be reversible. If isReversible(i) * is false, then the reverse rate of progress for reaction i is always diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 3baeb930eb..1e6d9e4674 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -52,7 +52,7 @@ def test_multiplier(self): def test_reaction_type(self): self.assertNear(self.phase.reaction_type(0), 2) # 3rd body - self.assertNear(self.phase.reaction_type(2), 1) # elementary + #self.assertNear(self.phase.reaction_type(2), 1) # elementary self.assertNear(self.phase.reaction_type(20), 4) # falloff with self.assertRaisesRegex(ValueError, 'out of range'): diff --git a/src/kinetics/Kinetics.cpp b/src/kinetics/Kinetics.cpp index 81540e20df..cb791cec30 100644 --- a/src/kinetics/Kinetics.cpp +++ b/src/kinetics/Kinetics.cpp @@ -373,6 +373,9 @@ int Kinetics::reactionType(size_t i) const { return m_reactions[i]->reaction_type; } +std::string Kinetics::reactionTypeStr(size_t i) const { + return m_reactions[i]->type(); +} std::string Kinetics::reactionString(size_t i) const { diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 3f40a0c506..b42e120dcb 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -121,14 +121,14 @@ std::string Reaction::equation() const ElementaryReaction::ElementaryReaction(const Composition& reactants_, const Composition products_, const Arrhenius& rate_) - : Reaction(ELEMENTARY_RXN, reactants_, products_) + : Reaction(UNUSED_MAGIC_NUMBER, reactants_, products_) , rate(rate_) , allow_negative_pre_exponential_factor(false) { } ElementaryReaction::ElementaryReaction() - : Reaction(ELEMENTARY_RXN) + : Reaction() , allow_negative_pre_exponential_factor(false) { } @@ -233,7 +233,7 @@ void FalloffReaction::validate() { ChemicallyActivatedReaction::ChemicallyActivatedReaction() { - reaction_type = CHEMACT_RXN; + reaction_type = UNUSED_MAGIC_NUMBER; } ChemicallyActivatedReaction::ChemicallyActivatedReaction( @@ -242,43 +242,43 @@ ChemicallyActivatedReaction::ChemicallyActivatedReaction( const ThirdBody& tbody) : FalloffReaction(reactants_, products_, low_rate_, high_rate_, tbody) { - reaction_type = CHEMACT_RXN; + reaction_type = UNUSED_MAGIC_NUMBER; } PlogReaction::PlogReaction() - : Reaction(PLOG_RXN) + : Reaction() { } PlogReaction::PlogReaction(const Composition& reactants_, const Composition& products_, const Plog& rate_) - : Reaction(PLOG_RXN, reactants_, products_) + : Reaction(UNUSED_MAGIC_NUMBER, reactants_, products_) , rate(rate_) { } ChebyshevReaction::ChebyshevReaction() - : Reaction(CHEBYSHEV_RXN) + : Reaction() { } ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, const Composition& products_, const ChebyshevRate& rate_) - : Reaction(CHEBYSHEV_RXN, reactants_, products_) + : Reaction(UNUSED_MAGIC_NUMBER, reactants_, products_) , rate(rate_) { } CustomPyReaction::CustomPyReaction() - : Reaction(CUSTOMPY_RXN) + : Reaction() { } CustomPyReaction::CustomPyReaction(const Composition& reactants_, const Composition& products_, const CustomPyRate& rate_) - : Reaction(CUSTOMPY_RXN, reactants_, products_) + : Reaction(UNUSED_MAGIC_NUMBER, reactants_, products_) , rate(rate_) { } @@ -287,7 +287,7 @@ InterfaceReaction::InterfaceReaction() : is_sticking_coefficient(false) , use_motz_wise_correction(false) { - reaction_type = INTERFACE_RXN; + reaction_type = UNUSED_MAGIC_NUMBER; } InterfaceReaction::InterfaceReaction(const Composition& reactants_, @@ -298,7 +298,7 @@ InterfaceReaction::InterfaceReaction(const Composition& reactants_, , is_sticking_coefficient(isStick) , use_motz_wise_correction(false) { - reaction_type = INTERFACE_RXN; + reaction_type = UNUSED_MAGIC_NUMBER; } ElectrochemicalReaction::ElectrochemicalReaction() @@ -534,7 +534,6 @@ void parseReactionEquation(Reaction& R, const AnyValue& equation, } if (kin.kineticsSpeciesIndex(species) == npos && stoich != -1 && species != "M") { - R.reaction_type = INVALID_RXN; R.set_valid(false); } @@ -567,7 +566,6 @@ void setupReaction(Reaction& R, const AnyMap& node, const Kinetics& kin) for (const auto& order : node["orders"].asMap()) { R.orders[order.first] = order.second; if (kin.kineticsSpeciesIndex(order.first) == npos) { - R.reaction_type = INVALID_RXN; R.set_valid(false); } } diff --git a/src/kinetics/ReactionPath.cpp b/src/kinetics/ReactionPath.cpp index 7be482fdb9..9672e1cc3f 100644 --- a/src/kinetics/ReactionPath.cpp +++ b/src/kinetics/ReactionPath.cpp @@ -680,9 +680,9 @@ string reactionLabel(size_t i, size_t kr, size_t nr, label += " + "+ s.kineticsSpeciesName(slist[j]); } } - if (s.reactionType(i) == THREE_BODY_RXN) { + if (s.reactionTypeStr(i) == "three-body") { label += " + M "; - } else if (s.reactionType(i) == FALLOFF_RXN) { + } else if (s.reactionTypeStr(i) == "falloff") { label += " (+ M)"; } return label; @@ -735,9 +735,9 @@ int ReactionPathBuilder::build(Kinetics& s, const string& element, revlabel += " + "+ s.kineticsSpeciesName(m_prod[i][j]); } } - if (s.reactionType(i) == THREE_BODY_RXN) { + if (s.reactionTypeStr(i) == "three-body") { revlabel += " + M "; - } else if (s.reactionType(i) == FALLOFF_RXN) { + } else if (s.reactionTypeStr(i) == "falloff") { revlabel += " (+ M)"; } diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index 84c5253506..5ce804a7d7 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -20,7 +20,7 @@ TEST(Reaction, ElementaryFromYaml) auto R = newReaction(rxn, *(sol->kinetics())); EXPECT_EQ(R->reactants.at("NO"), 1); EXPECT_EQ(R->products.at("N2"), 1); - EXPECT_EQ(R->reaction_type, ELEMENTARY_RXN); + EXPECT_EQ(R->type(), "elementary"); auto ER = dynamic_cast(*R); EXPECT_DOUBLE_EQ(ER.rate.preExponentialFactor(), -2.7e10); @@ -40,6 +40,7 @@ TEST(Reaction, ThreeBodyFromYaml1) auto R = newReaction(rxn, *(sol->kinetics())); EXPECT_EQ(R->reactants.count("M"), (size_t) 0); + EXPECT_EQ(R->type(), "three-body"); auto TBR = dynamic_cast(*R); EXPECT_DOUBLE_EQ(TBR.rate.preExponentialFactor(), 1.2e11); From fa48c1eb7dc24f06fa2ded358e2bea94a44fd1fe Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 27 Feb 2021 07:38:07 -0600 Subject: [PATCH 25/67] [Kinetics] Replace remaining references to reaction_type --- include/cantera/kinetics/FalloffMgr.h | 34 +++++++++++++++++++++++---- include/cantera/kinetics/Reaction.h | 2 ++ src/kinetics/GasKinetics.cpp | 4 ++-- src/kinetics/Kinetics.cpp | 6 ++--- src/kinetics/Reaction.cpp | 29 ++++++++++++++++------- 5 files changed, 57 insertions(+), 18 deletions(-) diff --git a/include/cantera/kinetics/FalloffMgr.h b/include/cantera/kinetics/FalloffMgr.h index f665cabe89..0add71cc97 100644 --- a/include/cantera/kinetics/FalloffMgr.h +++ b/include/cantera/kinetics/FalloffMgr.h @@ -38,7 +38,31 @@ class FalloffMgr m_offset.push_back(m_worksize); m_worksize += f->workSize(); m_falloff.push_back(f); - m_reactionType.push_back(reactionType); + if (reactionType == FALLOFF_RXN) { + m_isfalloff.push_back(true); + } else { + m_isfalloff.push_back(false); + } + m_indices[rxn] = m_falloff.size()-1; + } + + //! Install a new falloff function calculator. + /* + * @param rxn Index of the falloff reaction. This will be used to + * determine which array entry is modified in method pr_to_falloff. + * @param type Reaction type identifier. + * @param f The falloff function. + */ + void install(size_t rxn, std::string type, shared_ptr f) { + m_rxn.push_back(rxn); + m_offset.push_back(m_worksize); + m_worksize += f->workSize(); + m_falloff.push_back(f); + if (type == "falloff") { + m_isfalloff.push_back(true); + } else { + m_isfalloff.push_back(false); + } m_indices[rxn] = m_falloff.size()-1; } @@ -76,14 +100,14 @@ class FalloffMgr void pr_to_falloff(doublereal* values, const doublereal* work) { for (size_t i = 0; i < m_rxn.size(); i++) { double pr = values[m_rxn[i]]; - if (m_reactionType[i] == FALLOFF_RXN) { + if (m_isfalloff[i]) { // Pr / (1 + Pr) * F values[m_rxn[i]] *= - m_falloff[i]->F(pr, work + m_offset[i]) /(1.0 + pr); + m_falloff[i]->F(pr, work + m_offset[i]) / (1.0 + pr); } else { // 1 / (1 + Pr) * F values[m_rxn[i]] = - m_falloff[i]->F(pr, work + m_offset[i]) /(1.0 + pr); + m_falloff[i]->F(pr, work + m_offset[i]) / (1.0 + pr); } } } @@ -96,7 +120,7 @@ class FalloffMgr size_t m_worksize; //! Distinguish between falloff and chemically activated reactions - vector_int m_reactionType; + std::vector m_isfalloff; //! map of external reaction index to local index std::map m_indices; diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index ef5f5ec8c0..ba034f4dd2 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -25,6 +25,8 @@ class Reaction public: explicit Reaction(); explicit Reaction(int type); + Reaction(const Composition& reactants, + const Composition& products); Reaction(int type, const Composition& reactants, const Composition& products); virtual ~Reaction() {} diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 5d3afb188b..8b427fd83f 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -203,7 +203,7 @@ void GasKinetics::processFalloffReactions() m_falloffn.pr_to_falloff(pr.data(), falloff_work.data()); for (size_t i = 0; i < m_falloff_low_rates.nReactions(); i++) { - if (reactionType(m_fallindx[i]) == FALLOFF_RXN) { + if (reactionTypeStr(m_fallindx[i]) == "falloff") { pr[i] *= m_rfn_high[i]; } else { // CHEMACT_RXN pr[i] *= m_rfn_low[i]; @@ -332,7 +332,7 @@ void GasKinetics::addFalloffReaction(FalloffReaction& r) concm_falloff_values.resize(m_falloff_concm.workSize()); // install the falloff function calculator for this reaction - m_falloffn.install(nfall, r.reaction_type, r.falloff); + m_falloffn.install(nfall, r.type(), r.falloff); falloff_work.resize(m_falloffn.workSize()); } diff --git a/src/kinetics/Kinetics.cpp b/src/kinetics/Kinetics.cpp index cb791cec30..0135bdf39e 100644 --- a/src/kinetics/Kinetics.cpp +++ b/src/kinetics/Kinetics.cpp @@ -115,7 +115,7 @@ std::pair Kinetics::checkDuplicates(bool throw_err) const unmatched_duplicates.erase(i); unmatched_duplicates.erase(m); continue; - } else if (R.reaction_type != other.reaction_type) { + } else if (R.type() != other.type()) { continue; // different reaction types } doublereal c = checkDuplicateStoich(net_stoich[i], net_stoich[m]); @@ -616,10 +616,10 @@ void Kinetics::modifyReaction(size_t i, shared_ptr rNew) { checkReactionIndex(i); shared_ptr& rOld = m_reactions[i]; - if (rNew->reaction_type != rOld->reaction_type) { + if (rNew->type() != rOld->type()) { throw CanteraError("Kinetics::modifyReaction", "Reaction types are different: {} != {}.", - rOld->reaction_type, rNew->reaction_type); + rOld->type(), rNew->type()); } if (rNew->reactants != rOld->reactants) { diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index b42e120dcb..8c06cf816f 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -35,6 +35,19 @@ Reaction::Reaction() { } +Reaction::Reaction(const Composition& reactants_, + const Composition& products_) + : reaction_type(UNUSED_MAGIC_NUMBER) + , reactants(reactants_) + , products(products_) + , reversible(true) + , duplicate(false) + , allow_nonreactant_orders(false) + , allow_negative_orders(false) + , m_valid(true) +{ +} + Reaction::Reaction(int type) : reaction_type(type) , reversible(true) @@ -121,7 +134,7 @@ std::string Reaction::equation() const ElementaryReaction::ElementaryReaction(const Composition& reactants_, const Composition products_, const Arrhenius& rate_) - : Reaction(UNUSED_MAGIC_NUMBER, reactants_, products_) + : Reaction(reactants_, products_) , rate(rate_) , allow_negative_pre_exponential_factor(false) { @@ -156,7 +169,7 @@ double ThirdBody::efficiency(const std::string& k) const ThreeBodyReaction::ThreeBodyReaction() { - reaction_type = THREE_BODY_RXN; + reaction_type = UNUSED_MAGIC_NUMBER; //THREE_BODY_RXN; } ThreeBodyReaction::ThreeBodyReaction(const Composition& reactants_, @@ -166,7 +179,7 @@ ThreeBodyReaction::ThreeBodyReaction(const Composition& reactants_, : ElementaryReaction(reactants_, products_, rate_) , third_body(tbody) { - reaction_type = THREE_BODY_RXN; + reaction_type = UNUSED_MAGIC_NUMBER; //THREE_BODY_RXN; } std::string ThreeBodyReaction::reactantString() const { @@ -178,7 +191,7 @@ std::string ThreeBodyReaction::productString() const { } FalloffReaction::FalloffReaction() - : Reaction(FALLOFF_RXN) + : Reaction() , falloff(new Falloff()) , allow_negative_pre_exponential_factor(false) { @@ -188,7 +201,7 @@ FalloffReaction::FalloffReaction( const Composition& reactants_, const Composition& products_, const Arrhenius& low_rate_, const Arrhenius& high_rate_, const ThirdBody& tbody) - : Reaction(FALLOFF_RXN, reactants_, products_) + : Reaction(reactants_, products_) , low_rate(low_rate_) , high_rate(high_rate_) , third_body(tbody) @@ -252,7 +265,7 @@ PlogReaction::PlogReaction() PlogReaction::PlogReaction(const Composition& reactants_, const Composition& products_, const Plog& rate_) - : Reaction(UNUSED_MAGIC_NUMBER, reactants_, products_) + : Reaction(reactants_, products_) , rate(rate_) { } @@ -265,7 +278,7 @@ ChebyshevReaction::ChebyshevReaction() ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, const Composition& products_, const ChebyshevRate& rate_) - : Reaction(UNUSED_MAGIC_NUMBER, reactants_, products_) + : Reaction(reactants_, products_) , rate(rate_) { } @@ -278,7 +291,7 @@ CustomPyReaction::CustomPyReaction() CustomPyReaction::CustomPyReaction(const Composition& reactants_, const Composition& products_, const CustomPyRate& rate_) - : Reaction(UNUSED_MAGIC_NUMBER, reactants_, products_) + : Reaction(reactants_, products_) , rate(rate_) { } From 7a4fa08c67e377c69782106dcf2bdc36bc89590b Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 27 Feb 2021 07:49:26 -0600 Subject: [PATCH 26/67] [Kinetics] expose reaction_type_str to Python --- interfaces/cython/cantera/_cantera.pxd | 1 + interfaces/cython/cantera/kinetics.pyx | 7 ++++++- interfaces/cython/cantera/test/test_kinetics.py | 6 +++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index b290842644..911ad94712 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -440,6 +440,7 @@ cdef extern from "cantera/kinetics/Kinetics.h" namespace "Cantera": shared_ptr[CxxReaction] reaction(size_t) except +translate_exception cbool isReversible(int) except +translate_exception int reactionType(int) except +translate_exception + string reactionTypeStr(int) except +translate_exception string reactionString(int) except +translate_exception string reactantString(int) except +translate_exception string productString(int) except +translate_exception diff --git a/interfaces/cython/cantera/kinetics.pyx b/interfaces/cython/cantera/kinetics.pyx index 7c28ac1c9a..4e1d909a2e 100644 --- a/interfaces/cython/cantera/kinetics.pyx +++ b/interfaces/cython/cantera/kinetics.pyx @@ -158,10 +158,15 @@ cdef class Kinetics(_SolutionBase): self.kinetics.setMultiplier(i_reaction, value) def reaction_type(self, int i_reaction): - """Type of reaction *i_reaction*.""" + """Type code of reaction *i_reaction*.""" self._check_reaction_index(i_reaction) return self.kinetics.reactionType(i_reaction) + def reaction_type_str(self, int i_reaction): + """Type of reaction *i_reaction*.""" + self._check_reaction_index(i_reaction) + return pystr(self.kinetics.reactionTypeStr(i_reaction)) + def reaction_equation(self, int i_reaction): """The equation for the specified reaction. See also `reaction_equations`.""" self._check_reaction_index(i_reaction) diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 1e6d9e4674..6cb66ebd7e 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -51,9 +51,9 @@ def test_multiplier(self): self.assertArrayNear(0.5 * rev_rates0, rev_rates2) def test_reaction_type(self): - self.assertNear(self.phase.reaction_type(0), 2) # 3rd body - #self.assertNear(self.phase.reaction_type(2), 1) # elementary - self.assertNear(self.phase.reaction_type(20), 4) # falloff + self.assertEqual(self.phase.reaction_type_str(0), "three-body") + self.assertEqual(self.phase.reaction_type_str(2), "elementary") + self.assertEqual(self.phase.reaction_type_str(20), "falloff") with self.assertRaisesRegex(ValueError, 'out of range'): self.phase.reaction_type(33) From 20b762150a1e674a7b2fd0d54ba206c32603b603 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 27 Feb 2021 08:31:14 -0600 Subject: [PATCH 27/67] [Kinetics] deprecate functions referencing magic numbers --- include/cantera/kinetics/Kinetics.h | 2 ++ include/cantera/kinetics/Reaction.h | 5 ++++- interfaces/cython/cantera/kinetics.pyx | 10 +++++++++- interfaces/cython/cantera/test/test_kinetics.py | 4 ++-- src/kinetics/Kinetics.cpp | 5 +++++ src/kinetics/Reaction.cpp | 6 ++++++ src/kinetics/ReactionFactory.cpp | 2 +- 7 files changed, 29 insertions(+), 5 deletions(-) diff --git a/include/cantera/kinetics/Kinetics.h b/include/cantera/kinetics/Kinetics.h index 7836701340..ba7177b321 100644 --- a/include/cantera/kinetics/Kinetics.h +++ b/include/cantera/kinetics/Kinetics.h @@ -601,6 +601,8 @@ class Kinetics * are specific to the particular kinetics manager. * * @param i reaction index + * + * @deprecated To be changed after Cantera 2.6. */ virtual int reactionType(size_t i) const; diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index ba034f4dd2..5c99f73d6d 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -24,9 +24,12 @@ class Reaction { public: explicit Reaction(); - explicit Reaction(int type); Reaction(const Composition& reactants, const Composition& products); + + //! @deprecated To be removed after Cantera 2.6. + explicit Reaction(int type); + //! @deprecated To be removed after Cantera 2.6. Reaction(int type, const Composition& reactants, const Composition& products); virtual ~Reaction() {} diff --git a/interfaces/cython/cantera/kinetics.pyx b/interfaces/cython/cantera/kinetics.pyx index 4e1d909a2e..866b9e5ce5 100644 --- a/interfaces/cython/cantera/kinetics.pyx +++ b/interfaces/cython/cantera/kinetics.pyx @@ -158,7 +158,15 @@ cdef class Kinetics(_SolutionBase): self.kinetics.setMultiplier(i_reaction, value) def reaction_type(self, int i_reaction): - """Type code of reaction *i_reaction*.""" + """ + Type code of reaction *i_reaction*. + + .. deprecated:: 2.6 + """ + warnings.warn("Behavior changes after Cantera 2.6, " + "when function will return a type string. " + "Use method 'reaction_type_str' during " + "transition instead.", DeprecationWarning) self._check_reaction_index(i_reaction) return self.kinetics.reactionType(i_reaction) diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 6cb66ebd7e..d0c8ad7c20 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -56,9 +56,9 @@ def test_reaction_type(self): self.assertEqual(self.phase.reaction_type_str(20), "falloff") with self.assertRaisesRegex(ValueError, 'out of range'): - self.phase.reaction_type(33) + self.phase.reaction_type_str(33) with self.assertRaisesRegex(ValueError, 'out of range'): - self.phase.reaction_type(-2) + self.phase.reaction_type_str(-2) def test_reaction_equations(self): self.assertEqual(self.phase.n_reactions, diff --git a/src/kinetics/Kinetics.cpp b/src/kinetics/Kinetics.cpp index 0135bdf39e..9f883583a5 100644 --- a/src/kinetics/Kinetics.cpp +++ b/src/kinetics/Kinetics.cpp @@ -14,6 +14,7 @@ #include "cantera/thermo/ThermoPhase.h" #include "cantera/base/stringUtils.h" #include "cantera/base/utilities.h" +#include "cantera/base/global.h" #include using namespace std; @@ -370,6 +371,10 @@ double Kinetics::productStoichCoeff(size_t kSpec, size_t irxn) const } int Kinetics::reactionType(size_t i) const { + warn_deprecated("Kinetics::reactionType()", + "To be changed after Cantera 2.6. " + "Return string instead of magic number; use " + "Kinetics::reactionTypeStr during transition."); return m_reactions[i]->reaction_type; } diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 8c06cf816f..e347e6df1d 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -56,6 +56,9 @@ Reaction::Reaction(int type) , allow_negative_orders(false) , m_valid(true) { + warn_deprecated("Reaction::Reaction()", + "To be removed after Cantera 2.6. Use constructor without parameter " + "'type' instead."); } Reaction::Reaction(int type, const Composition& reactants_, @@ -69,6 +72,9 @@ Reaction::Reaction(int type, const Composition& reactants_, , allow_negative_orders(false) , m_valid(true) { + warn_deprecated("Reaction::Reaction()", + "To be removed after Cantera 2.6. Use constructor without parameter " + "'type' instead."); } void Reaction::validate() diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 85414d0790..4eef48ff1e 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -208,7 +208,7 @@ unique_ptr newReaction(const AnyMap& rxn_node, if (kin.thermo().nDim() < 3) { // See if this is an electrochemical reaction - Reaction testReaction(0); + Reaction testReaction; parseReactionEquation(testReaction, rxn_node["equation"], kin); if (isElectrochemicalReaction(testReaction, kin)) { type = "electrochemical"; From 557533666abf558cae22bb4fec4145f95c9ed6d9 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 27 Feb 2021 15:13:31 -0600 Subject: [PATCH 28/67] [Kinetics] finalize deprecation of reaction type magic numbers --- include/cantera/kinetics/FalloffMgr.h | 7 +++++++ include/cantera/kinetics/Reaction.h | 4 ++++ include/cantera/kinetics/reaction_defs.h | 3 ++- src/kinetics/Reaction.cpp | 26 ++++++++++++++++-------- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/include/cantera/kinetics/FalloffMgr.h b/include/cantera/kinetics/FalloffMgr.h index 0add71cc97..bf4c255f11 100644 --- a/include/cantera/kinetics/FalloffMgr.h +++ b/include/cantera/kinetics/FalloffMgr.h @@ -9,6 +9,7 @@ #define CT_FALLOFFMGR_H #include "Falloff.h" +#include "cantera/base/global.h" namespace Cantera { @@ -32,8 +33,14 @@ class FalloffMgr * determine which array entry is modified in method pr_to_falloff. * @param reactionType Either `FALLOFF_RXN` or `CHEMACT_RXN` * @param f The falloff function. + * + * @deprecated To be removed after Cantera 2.6. */ void install(size_t rxn, int reactionType, shared_ptr f) { + warn_deprecated("FalloffMgr::install()", + "To be removed after Cantera 2.6. Specify reaction type using " + "string instead."); + m_rxn.push_back(rxn); m_offset.push_back(m_worksize); m_worksize += f->workSize(); diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 5c99f73d6d..bf9d6c991a 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -64,6 +64,10 @@ class Reaction //! Type of the reaction. The valid types are listed in the file, //! reaction_defs.h, with constants ending in `RXN`. + /*! + * @deprecated To be removed in Cantera 2.6. + * Superseded by Reaction::type(). + */ int reaction_type; //! Reactant species and stoichiometric coefficients diff --git a/include/cantera/kinetics/reaction_defs.h b/include/cantera/kinetics/reaction_defs.h index 2d8a514fcf..7a1aedee97 100644 --- a/include/cantera/kinetics/reaction_defs.h +++ b/include/cantera/kinetics/reaction_defs.h @@ -1,6 +1,8 @@ /** * @file reaction_defs.h * This file defines some constants used to specify reaction types. + * + * @deprecated To be removed after Cantera 2.6. */ // This file is part of Cantera. See License.txt in the top-level directory or @@ -16,7 +18,6 @@ namespace Cantera const int INVALID_RXN = 0; const int NONE = 0; -const int UNUSED_MAGIC_NUMBER = -1; /// @name Reaction Types diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index e347e6df1d..c8fe3ccea7 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -26,7 +26,7 @@ namespace Cantera { Reaction::Reaction() - : reaction_type(UNUSED_MAGIC_NUMBER) + : reaction_type(NONE) , reversible(true) , duplicate(false) , allow_nonreactant_orders(false) @@ -37,7 +37,7 @@ Reaction::Reaction() Reaction::Reaction(const Composition& reactants_, const Composition& products_) - : reaction_type(UNUSED_MAGIC_NUMBER) + : reaction_type(NONE) , reactants(reactants_) , products(products_) , reversible(true) @@ -144,12 +144,14 @@ ElementaryReaction::ElementaryReaction(const Composition& reactants_, , rate(rate_) , allow_negative_pre_exponential_factor(false) { + reaction_type = ELEMENTARY_RXN; } ElementaryReaction::ElementaryReaction() : Reaction() , allow_negative_pre_exponential_factor(false) { + reaction_type = ELEMENTARY_RXN; } void ElementaryReaction::validate() @@ -175,7 +177,7 @@ double ThirdBody::efficiency(const std::string& k) const ThreeBodyReaction::ThreeBodyReaction() { - reaction_type = UNUSED_MAGIC_NUMBER; //THREE_BODY_RXN; + reaction_type = THREE_BODY_RXN; } ThreeBodyReaction::ThreeBodyReaction(const Composition& reactants_, @@ -185,7 +187,7 @@ ThreeBodyReaction::ThreeBodyReaction(const Composition& reactants_, : ElementaryReaction(reactants_, products_, rate_) , third_body(tbody) { - reaction_type = UNUSED_MAGIC_NUMBER; //THREE_BODY_RXN; + reaction_type = THREE_BODY_RXN; } std::string ThreeBodyReaction::reactantString() const { @@ -201,6 +203,7 @@ FalloffReaction::FalloffReaction() , falloff(new Falloff()) , allow_negative_pre_exponential_factor(false) { + reaction_type = FALLOFF_RXN; } FalloffReaction::FalloffReaction( @@ -213,6 +216,7 @@ FalloffReaction::FalloffReaction( , third_body(tbody) , falloff(new Falloff()) { + reaction_type = FALLOFF_RXN; } std::string FalloffReaction::reactantString() const { @@ -252,7 +256,7 @@ void FalloffReaction::validate() { ChemicallyActivatedReaction::ChemicallyActivatedReaction() { - reaction_type = UNUSED_MAGIC_NUMBER; + reaction_type = CHEMACT_RXN; } ChemicallyActivatedReaction::ChemicallyActivatedReaction( @@ -261,12 +265,13 @@ ChemicallyActivatedReaction::ChemicallyActivatedReaction( const ThirdBody& tbody) : FalloffReaction(reactants_, products_, low_rate_, high_rate_, tbody) { - reaction_type = UNUSED_MAGIC_NUMBER; + reaction_type = CHEMACT_RXN; } PlogReaction::PlogReaction() : Reaction() { + reaction_type = PLOG_RXN; } PlogReaction::PlogReaction(const Composition& reactants_, @@ -274,11 +279,13 @@ PlogReaction::PlogReaction(const Composition& reactants_, : Reaction(reactants_, products_) , rate(rate_) { + reaction_type = PLOG_RXN; } ChebyshevReaction::ChebyshevReaction() : Reaction() { + reaction_type = CHEBYSHEV_RXN; } ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, @@ -287,11 +294,13 @@ ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, : Reaction(reactants_, products_) , rate(rate_) { + reaction_type = CHEBYSHEV_RXN; } CustomPyReaction::CustomPyReaction() : Reaction() { + reaction_type = CUSTOMPY_RXN; } CustomPyReaction::CustomPyReaction(const Composition& reactants_, @@ -300,13 +309,14 @@ CustomPyReaction::CustomPyReaction(const Composition& reactants_, : Reaction(reactants_, products_) , rate(rate_) { + reaction_type = CUSTOMPY_RXN; } InterfaceReaction::InterfaceReaction() : is_sticking_coefficient(false) , use_motz_wise_correction(false) { - reaction_type = UNUSED_MAGIC_NUMBER; + reaction_type = INTERFACE_RXN; } InterfaceReaction::InterfaceReaction(const Composition& reactants_, @@ -317,7 +327,7 @@ InterfaceReaction::InterfaceReaction(const Composition& reactants_, , is_sticking_coefficient(isStick) , use_motz_wise_correction(false) { - reaction_type = UNUSED_MAGIC_NUMBER; + reaction_type = INTERFACE_RXN; } ElectrochemicalReaction::ElectrochemicalReaction() From e8bce32c370467492078da37031be03e51bec70d Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 27 Feb 2021 18:53:39 -0600 Subject: [PATCH 29/67] [Kinetics] make C++ pure abstract base class --- include/cantera/kinetics/Reaction.h | 6 ++---- src/kinetics/ReactionFactory.cpp | 5 +++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index bf9d6c991a..ec032a5ce7 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -18,7 +18,7 @@ class Kinetics; class Falloff; class XML_Node; -//! Intermediate class which stores data about a reaction and its rate +//! Abstract base class which stores data about a reaction and its rate //! parameterization so that it can be added to a Kinetics object. class Reaction { @@ -44,9 +44,7 @@ class Reaction std::string equation() const; //! The type of reaction - virtual std::string type() const { - return ""; - } + virtual std::string type() const = 0; // pure virtual function //! Ensure that the rate constant and other parameters for this reaction are //! valid. diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 4eef48ff1e..12c5376c36 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -207,8 +207,9 @@ unique_ptr newReaction(const AnyMap& rxn_node, } if (kin.thermo().nDim() < 3) { - // See if this is an electrochemical reaction - Reaction testReaction; + // See if this is an electrochemical reaction: type of + // receiving reaction object is unimportant in this case + ElementaryReaction testReaction; parseReactionEquation(testReaction, rxn_node["equation"], kin); if (isElectrochemicalReaction(testReaction, kin)) { type = "electrochemical"; From 29101a23a571ed506e1741c89a3605d2ca93cc78 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 28 Feb 2021 08:56:48 -0600 Subject: [PATCH 30/67] [Func1] make CxxFunc1 shareable --- interfaces/cython/cantera/_cantera.pxd | 1 + interfaces/cython/cantera/func1.pyx | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 911ad94712..a2a18e84d7 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -1051,6 +1051,7 @@ cdef class Mixture: cpdef int element_index(self, element) except * cdef class Func1: + cdef shared_ptr[CxxFunc1] _func cdef CxxFunc1* func cdef object callable cdef object exception diff --git a/interfaces/cython/cantera/func1.pyx b/interfaces/cython/cantera/func1.pyx index 802537e3ab..9315576998 100644 --- a/interfaces/cython/cantera/func1.pyx +++ b/interfaces/cython/cantera/func1.pyx @@ -86,10 +86,8 @@ cdef class Func1: cpdef void _set_callback(self, c) except *: self.callable = c - self.func = new CxxFunc1(func_callback, self) - - def __dealloc__(self): - del self.func + self._func.reset(new CxxFunc1(func_callback, self)) + self.func = self._func.get() def __call__(self, t): return self.func.eval(t) From 436f1eeca9e5b810ddf8b95e12bcaa27f9ce1dd1 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 28 Feb 2021 10:12:46 -0600 Subject: [PATCH 31/67] [Kinetics] make CustomRate persistent withing CustomReaction --- include/cantera/kinetics/RxnRates.h | 4 +-- interfaces/cython/cantera/_cantera.pxd | 7 ++-- interfaces/cython/cantera/reaction.pyx | 35 ++++++------------- .../cython/cantera/test/test_kinetics.py | 2 +- src/kinetics/RxnRates.cpp | 2 +- 5 files changed, 19 insertions(+), 31 deletions(-) diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index ee9fdc9cee..2745696f9f 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -449,7 +449,7 @@ class CustomPyRate * The Python function takes a single argument (temperature) and does * not depend on parameters handled in C++. */ - void setRateFunction(Func1* f); + void setRateFunction(shared_ptr f); //! Update concentration-dependent parts of the rate coefficient. /*! @@ -465,7 +465,7 @@ class CustomPyRate double updateRC(double logT, double recipT) const; protected: - Func1* m_ratefunc; + shared_ptr m_ratefunc; }; } diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index a2a18e84d7..77a4bb0145 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -314,7 +314,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxCustomPyRate "Cantera::CustomPyRate": CxxCustomPyRate() - void setRateFunction(CxxFunc1*) except +translate_exception + void setRateFunction(shared_ptr[CxxFunc1]) except +translate_exception double updateRC(double, double) cdef cppclass CxxReaction "Cantera::Reaction": @@ -1019,14 +1019,17 @@ cdef class Reaction: @staticmethod cdef wrap(shared_ptr[CxxReaction]) +cdef class CustomReaction(Reaction): + cdef CustomRate _rate + cdef class Arrhenius: cdef CxxArrhenius* rate cdef Reaction reaction # parent reaction, to prevent garbage collection cdef class CustomRate: + cdef shared_ptr[CxxCustomPyRate] _rate cdef CxxCustomPyRate* rate cdef Func1 _rate_func - cdef Reaction reaction # parent reaction, to prevent garbage collection cdef class Falloff: cdef shared_ptr[CxxFalloff] _falloff diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 64e9269a2b..fa3d094473 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -418,15 +418,10 @@ cdef class CustomRate: def __cinit__(self, k=None, init=True): if init: - self.rate = new CxxCustomPyRate() - self.reaction = None - self._rate_func = None + self._rate.reset(new CxxCustomPyRate()) + self.rate = self._rate.get() self.set_rate_function(k) - def __dealloc__(self): - if self.reaction is None: - del self.rate - def __repr__(self): return '' @@ -441,13 +436,12 @@ cdef class CustomRate: self._rate_func = None return - cdef Func1 g if isinstance(k, Func1): - g = k + self._rate_func = k else: - g = Func1(k) - self._rate_func = g - self.rate.setRateFunction(g.func) + self._rate_func = Func1(k) + + self.rate.setRateFunction(self._rate_func._func) def __call__(self, float T): cdef double logT = np.log(T) @@ -455,14 +449,6 @@ cdef class CustomRate: return self.rate.updateRC(logT, recipT) -cdef wrapCustomRate(CxxCustomPyRate* rate, Reaction reaction): - r = CustomRate(init=False) - r.rate = rate - r.reaction = reaction - #r.set_rate_function(k) - return r - - cdef class ElementaryReaction(Reaction): """ A reaction which follows mass-action kinetics with a modified Arrhenius @@ -874,17 +860,16 @@ cdef class CustomReaction(Reaction): self._reaction = CxxNewReaction(AnyMapFromYamlString(stringify(yaml)), deref(kinetics.kinetics)) self.reaction = self._reaction.get() - rate_obj = CustomRate(rate) - self.rate = rate_obj + self.rate = CustomRate(rate) property rate: """ Get/Set the `CustomRate` object for this reaction. """ def __get__(self): - cdef CxxCustomPyReaction* r = self.reaction - return wrapCustomRate(&(r.rate), self) + return self._rate def __set__(self, CustomRate rate): + self._rate = rate cdef CxxCustomPyReaction* r = self.reaction - r.rate = deref(rate.rate) + r.rate = deref(self._rate.rate) cdef class InterfaceReaction(ElementaryReaction): diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index d0c8ad7c20..95d7b1ad49 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -898,7 +898,7 @@ def test_custom2(self): kinetics=self.gas) self.assertEqual(r.reactants, {'O':1, 'H2':1}) self.assertEqual(r.products, {'H':1, 'OH':1}) - #self.assertNear(r.rate(self.gas.T), self.gas.forward_rate_constants[2]) + self.assertNear(r.rate(self.gas.T), self.gas.forward_rate_constants[2]) def test_custom_rate(self): # probe O + H2 <=> H + OH diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 63693b6327..6ed5f996a3 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -161,7 +161,7 @@ ChebyshevRate::ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, CustomPyRate::CustomPyRate() : m_ratefunc(0) {} -void CustomPyRate::setRateFunction(Func1* f) { +void CustomPyRate::setRateFunction(shared_ptr f) { m_ratefunc = f; } From f71cfb6053c89b169a8c0cc362e292be5445d941 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 28 Feb 2021 12:33:56 -0600 Subject: [PATCH 32/67] [Kinetics] Create C++ abstract base class RxnRate --- include/cantera/kinetics/RxnRates.h | 50 ++++++++++++++++++- interfaces/cython/cantera/_cantera.pxd | 6 ++- interfaces/cython/cantera/reaction.pyx | 18 +++++-- .../cython/cantera/test/test_kinetics.py | 23 ++++++--- src/kinetics/RxnRates.cpp | 4 ++ 5 files changed, 88 insertions(+), 13 deletions(-) diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 2745696f9f..a3ad180ef9 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -430,6 +430,46 @@ class ChebyshevRate vector_fp dotProd_; //!< dot product of chebCoeffs with the reduced pressure polynomial }; + +//! Abstract base class for reaction rate definitions +/** + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ +class RxnRate +{ +public: + //! Constructor + RxnRate() = default; + + //! Identifier of reaction type + virtual std::string type() const = 0; + + double temperature() const { + return m_temperature; + } + + //! Evaluate reaction rate + virtual double eval() const = 0; + + //! Update temperature + void updateTemperature(double T) { + if (T <= 0.) { + throw CanteraError("RxnRate::updateTemperature", + "Temperature has to be positive."); + } + m_temperature = T; + m_logT = std::log(T); + m_recipT = 1./T; + } + +protected: + static double m_temperature; + static double m_logT; + static double m_recipT; +}; + + //! Custom Python reaction rate depending only on temperature /** * The rate expression is provided by external Python code taking a single @@ -438,12 +478,16 @@ class ChebyshevRate * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. */ -class CustomPyRate +class CustomPyRate : public RxnRate { public: //! Constructor. CustomPyRate(); + virtual std::string type() const { + return "custom-Python"; + } + // set custom rate /** * The Python function takes a single argument (temperature) and does @@ -458,6 +502,10 @@ class CustomPyRate */ void update_C(const double* c) {} + virtual double eval() const { + return updateRC(m_logT, m_recipT); + } + // Update the value the natural logarithm of the rate constant. double updateLog(double logT, double recipT) const; diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 77a4bb0145..ebc159d49f 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -314,8 +314,12 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxCustomPyRate "Cantera::CustomPyRate": CxxCustomPyRate() + string type() void setRateFunction(shared_ptr[CxxFunc1]) except +translate_exception - double updateRC(double, double) + double eval() except +translate_exception + double updateRC(double, double) except +translate_exception + double temperature() + void updateTemperature(double) except +translate_exception cdef cppclass CxxReaction "Cantera::Reaction": CxxReaction() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index fa3d094473..438b0174e7 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -423,7 +423,7 @@ cdef class CustomRate: self.set_rate_function(k) def __repr__(self): - return '' + return "<{}>".format(pystr(self.rate.type())) def set_rate_function(self, k): r""" @@ -443,10 +443,18 @@ cdef class CustomRate: self.rate.setRateFunction(self._rate_func._func) - def __call__(self, float T): - cdef double logT = np.log(T) - cdef double recipT = 1/T - return self.rate.updateRC(logT, recipT) + def __call__(self, temperature=None): + if temperature is not None: + self.temperature = temperature + + return self.rate.eval() + + property temperature: + """ Get/Set temperature used for all reaction rate evaluations""" + def __get__(self): + return self.rate.temperature() + def __set__(self, float temperature): + self.rate.updateTemperature(temperature) cdef class ElementaryReaction(Reaction): diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 95d7b1ad49..3fd7fb38b2 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -887,18 +887,29 @@ def test_custom(self): species=self.species, reactions=[r]) gas2.TPX = self.gas.TPX - #self.assertNear(gas2.forward_rate_constants[0], - # self.gas.forward_rate_constants[2]) - #self.assertNear(gas2.net_rates_of_progress[0], - # self.gas.net_rates_of_progress[2]) + self.assertEqual(gas2.n_reactions, 1) + # self.assertNear(gas2.forward_rate_constants[0], + # self.gas.forward_rate_constants[2]) + # self.assertNear(gas2.net_rates_of_progress[0], + # self.gas.net_rates_of_progress[2]) def test_custom2(self): + gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', + species=self.species, reactions=[]) + gas2.TPX = self.gas.TPX + r = ct.CustomReaction(equation='H2 + O <=> H + OH', rate=lambda T: 38.7 * T**2.7 * exp(-3150.15428/T), kinetics=self.gas) - self.assertEqual(r.reactants, {'O':1, 'H2':1}) - self.assertEqual(r.products, {'H':1, 'OH':1}) self.assertNear(r.rate(self.gas.T), self.gas.forward_rate_constants[2]) + self.assertEqual(r.reactants, self.gas.reaction(2).reactants) + self.assertEqual(r.products, self.gas.reaction(2).products) + + gas2.add_reaction(r) + # self.assertNear(gas2.forward_rate_constants[0], + # self.gas.forward_rate_constants[2]) + # self.assertNear(gas2.net_rates_of_progress[0], + # self.gas.net_rates_of_progress[2]) def test_custom_rate(self): # probe O + H2 <=> H + OH diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 6ed5f996a3..dca3dfe35f 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -159,6 +159,10 @@ ChebyshevRate::ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, } } +double RxnRate::m_temperature = 1.; +double RxnRate::m_logT = 0.; +double RxnRate::m_recipT = 1.; + CustomPyRate::CustomPyRate() : m_ratefunc(0) {} void CustomPyRate::setRateFunction(shared_ptr f) { From 18a2ad3f6daf18cf6e8f26333770d163c995cd2b Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 28 Feb 2021 18:38:53 -0600 Subject: [PATCH 33/67] [Kinetics] enable calculation of rate constants for CustomReaction --- include/cantera/kinetics/GasKinetics.h | 3 ++ include/cantera/kinetics/RxnRates.h | 46 +++++++++++-------- interfaces/cython/cantera/_cantera.pxd | 7 +-- interfaces/cython/cantera/reaction.pyx | 7 +++ .../cython/cantera/test/test_kinetics.py | 16 +++---- src/kinetics/GasKinetics.cpp | 30 ++++++++++-- src/kinetics/RxnRates.cpp | 12 ++--- 7 files changed, 77 insertions(+), 44 deletions(-) diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index bd973b9b3b..4b35263ddd 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -101,6 +101,9 @@ class GasKinetics : public BulkKinetics Rate1 m_plog_rates; Rate1 m_cheb_rates; + //! Map of generic reaction rate expressions + std::map> m_rxn_rates; + //! @name Reaction rate data //!@{ doublereal m_logp_ref; diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index a3ad180ef9..927d453b67 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -445,13 +445,11 @@ class RxnRate //! Identifier of reaction type virtual std::string type() const = 0; + //! Get temperature double temperature() const { return m_temperature; } - //! Evaluate reaction rate - virtual double eval() const = 0; - //! Update temperature void updateTemperature(double T) { if (T <= 0.) { @@ -463,10 +461,35 @@ class RxnRate m_recipT = 1./T; } + //! Get pressure + double pressure() const { + return m_pressure; + } + + //! Update pressure + void updatePressure(double P) { + if (P <= 0.) { + throw CanteraError("RxnRate::updatePressure", + "Pressure has to be positive."); + } + m_pressure = P; + } + + //! Evaluate reaction rate + virtual double eval() const = 0; + protected: + //! Temperature used for reaction rate evaluation static double m_temperature; + + //! Logarithm of temperature used for reaction rate evaluation static double m_logT; + + //! Inverse temperature used for reaction rate evaluation static double m_recipT; + + //! Pressure used for reaction rate evaluation + static double m_pressure; }; @@ -495,22 +518,7 @@ class CustomPyRate : public RxnRate */ void setRateFunction(shared_ptr f); - //! Update concentration-dependent parts of the rate coefficient. - /*! - * For this class, there are no concentration-dependent parts, so this - * method does nothing. - */ - void update_C(const double* c) {} - - virtual double eval() const { - return updateRC(m_logT, m_recipT); - } - - // Update the value the natural logarithm of the rate constant. - double updateLog(double logT, double recipT) const; - - // Update the value the rate constant. - double updateRC(double logT, double recipT) const; + virtual double eval() const; protected: shared_ptr m_ratefunc; diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index ebc159d49f..3cb7ff151e 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -315,11 +315,12 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxCustomPyRate "Cantera::CustomPyRate": CxxCustomPyRate() string type() - void setRateFunction(shared_ptr[CxxFunc1]) except +translate_exception - double eval() except +translate_exception - double updateRC(double, double) except +translate_exception double temperature() void updateTemperature(double) except +translate_exception + double pressure() + void updatePressure(double) except +translate_exception + double eval() except +translate_exception + void setRateFunction(shared_ptr[CxxFunc1]) except +translate_exception cdef cppclass CxxReaction "Cantera::Reaction": CxxReaction() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 438b0174e7..4e26c4ef8f 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -456,6 +456,13 @@ cdef class CustomRate: def __set__(self, float temperature): self.rate.updateTemperature(temperature) + property pressure: + """ Get/Set pressure used for all reaction rate evaluations""" + def __get__(self): + return self.rate.pressure() + def __set__(self, float pressure): + self.rate.updatePressure(pressure) + cdef class ElementaryReaction(Reaction): """ diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 3fd7fb38b2..d7a723edff 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -888,10 +888,10 @@ def test_custom(self): gas2.TPX = self.gas.TPX self.assertEqual(gas2.n_reactions, 1) - # self.assertNear(gas2.forward_rate_constants[0], - # self.gas.forward_rate_constants[2]) - # self.assertNear(gas2.net_rates_of_progress[0], - # self.gas.net_rates_of_progress[2]) + self.assertNear(gas2.forward_rate_constants[0], + self.gas.forward_rate_constants[2]) + self.assertNear(gas2.net_rates_of_progress[0], + self.gas.net_rates_of_progress[2]) def test_custom2(self): gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', @@ -906,10 +906,10 @@ def test_custom2(self): self.assertEqual(r.products, self.gas.reaction(2).products) gas2.add_reaction(r) - # self.assertNear(gas2.forward_rate_constants[0], - # self.gas.forward_rate_constants[2]) - # self.assertNear(gas2.net_rates_of_progress[0], - # self.gas.net_rates_of_progress[2]) + self.assertNear(gas2.forward_rate_constants[0], + self.gas.forward_rate_constants[2]) + self.assertNear(gas2.net_rates_of_progress[0], + self.gas.net_rates_of_progress[2]) def test_custom_rate(self): # probe O + H2 <=> H + OH diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 8b427fd83f..63ec1d6c59 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -84,10 +84,10 @@ GasKinetics::GasKinetics(ThermoPhase* thermo) : void GasKinetics::update_rates_T() { - doublereal T = thermo().temperature(); - doublereal P = thermo().pressure(); + double T = thermo().temperature(); + double P = thermo().pressure(); m_logStandConc = log(thermo().standardConcentration()); - doublereal logT = log(T); + double logT = log(T); if (T != m_temp) { if (!m_rfn.empty()) { @@ -106,6 +106,11 @@ void GasKinetics::update_rates_T() } if (T != m_temp || P != m_pres) { + for (auto& rate : m_rxn_rates) { + // generic reaction rates + m_rfn.data()[rate.first] = rate.second->eval(); + } + if (m_plog_rates.nReactions()) { m_plog_rates.update(T, logT, m_rfn.data()); m_ROP_ok = false; @@ -367,7 +372,12 @@ void GasKinetics::addChebyshevReaction(ChebyshevReaction& r) void GasKinetics::addCustomPyReaction(CustomPyReaction& r) { - r.set_valid(false); + shared_ptr rate = std::make_shared(std::move(r.rate)); + rate->updateTemperature(thermo().temperature()); + rate->updatePressure(thermo().pressure()); + m_ROP_ok = false; + + m_rxn_rates[nReactions()-1] = rate; } void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) @@ -413,7 +423,17 @@ void GasKinetics::modifyChebyshevReaction(size_t i, ChebyshevReaction& r) void GasKinetics::modifyCustomPyReaction(size_t i, CustomPyReaction& r) { - r.set_valid(false); + if (m_rxn_rates.find(i) != m_rxn_rates.end()) { + shared_ptr rate = std::make_shared(std::move(r.rate)); + rate->updateTemperature(thermo().temperature()); + rate->updatePressure(thermo().pressure()); + m_ROP_ok = false; + + m_rxn_rates[i] = rate; + } else { + throw CanteraError("GasKinetics::modifyReaction", + "Index {} does not exist.", i); + } } void GasKinetics::init() diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index dca3dfe35f..af33af8397 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -162,6 +162,7 @@ ChebyshevRate::ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, double RxnRate::m_temperature = 1.; double RxnRate::m_logT = 0.; double RxnRate::m_recipT = 1.; +double RxnRate::m_pressure = 1.; CustomPyRate::CustomPyRate() : m_ratefunc(0) {} @@ -169,16 +170,9 @@ void CustomPyRate::setRateFunction(shared_ptr f) { m_ratefunc = f; } -double CustomPyRate::updateLog(double logT, double recipT) const { +double CustomPyRate::eval() const { if (m_ratefunc) { - return std::log( m_ratefunc->eval(1./recipT) ); - } - return 1.; -} - -double CustomPyRate::updateRC(double logT, double recipT) const { - if (m_ratefunc) { - return m_ratefunc->eval(1./recipT); + return m_ratefunc->eval(m_temperature); } return 0.; } From 8d1571beb70527cfe26d6227ed6f30d6b389a35d Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 28 Feb 2021 22:03:19 -0600 Subject: [PATCH 34/67] [Kinetics] generalize addition of RxnRate in GasKinetics --- include/cantera/kinetics/GasKinetics.h | 6 ++-- include/cantera/kinetics/Reaction.h | 17 +++++++--- interfaces/cython/cantera/_cantera.pxd | 2 +- interfaces/cython/cantera/base.pyx | 1 - interfaces/cython/cantera/reaction.pyx | 2 +- .../cython/cantera/test/test_kinetics.py | 10 +++--- src/kinetics/GasKinetics.cpp | 33 ++++++++++--------- src/kinetics/Reaction.cpp | 12 ++----- 8 files changed, 44 insertions(+), 39 deletions(-) diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index 4b35263ddd..adb442d5b4 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -124,13 +124,15 @@ class GasKinetics : public BulkKinetics void addFalloffReaction(FalloffReaction& r); void addPlogReaction(PlogReaction& r); void addChebyshevReaction(ChebyshevReaction& r); - void addCustomPyReaction(CustomPyReaction& r); + + void addRxnRate(shared_ptr rate); void modifyThreeBodyReaction(size_t i, ThreeBodyReaction& r); void modifyFalloffReaction(size_t i, FalloffReaction& r); void modifyPlogReaction(size_t i, PlogReaction& r); void modifyChebyshevReaction(size_t i, ChebyshevReaction& r); - void modifyCustomPyReaction(size_t i, CustomPyReaction& r); + + void modifyRxnRate(size_t i, shared_ptr rate); //! Update the equilibrium constants in molar units. void updateKc(); diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index ec032a5ce7..d3267a919f 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -60,6 +60,16 @@ class Reaction m_valid = valid; } + //! Retrieve reaction rate pointer + shared_ptr rxnRate() const { + return m_rate; + } + + //! Retrieve reaction rate pointer + void setRxnRate(shared_ptr rate) { + m_rate = rate; + } + //! Type of the reaction. The valid types are listed in the file, //! reaction_defs.h, with constants ending in `RXN`. /*! @@ -102,6 +112,9 @@ class Reaction protected: //! Flag indicating whether reaction is set up correctly bool m_valid; + + //! Reaction rate used by generic reactions + shared_ptr m_rate; }; @@ -251,14 +264,10 @@ class CustomPyReaction : public Reaction { public: CustomPyReaction(); - CustomPyReaction(const Composition& reactants, const Composition& products, - const CustomPyRate& rate); virtual std::string type() const { return "custom-Python"; } - - CustomPyRate rate; }; //! Modifications to an InterfaceReaction rate based on a surface species diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 3cb7ff151e..a8664683bd 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -403,7 +403,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxCustomPyReaction "Cantera::CustomPyReaction" (CxxReaction): CxxCustomPyReaction() - CxxCustomPyRate rate + void setRxnRate(shared_ptr[CxxCustomPyRate]) cdef cppclass CxxCoverageDependency "Cantera::CoverageDependency": CxxCoverageDependency(double, double, double) diff --git a/interfaces/cython/cantera/base.pyx b/interfaces/cython/cantera/base.pyx index d147980dce..c92b008a0c 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -223,7 +223,6 @@ cdef class _SolutionBase: for reaction in reactions: self.kinetics.addReaction(reaction._reaction) - def __getitem__(self, selection): copy = self.__class__(origin=self) if isinstance(selection, slice): diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 4e26c4ef8f..4d3818014f 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -884,7 +884,7 @@ cdef class CustomReaction(Reaction): def __set__(self, CustomRate rate): self._rate = rate cdef CxxCustomPyReaction* r = self.reaction - r.rate = deref(self._rate.rate) + r.setRxnRate(self._rate._rate) cdef class InterfaceReaction(ElementaryReaction): diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index d7a723edff..780bfd13a0 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -879,7 +879,7 @@ def test_arrhenius_rate(self): R = self.gas.reaction(2) self.assertNear(R.rate(self.gas.T), self.gas.forward_rate_constants[2]) - def test_custom(self): + def test_custom1(self): r = ct.CustomReaction({'O':1, 'H2':1}, {'H':1, 'OH':1}) r.rate = ct.CustomRate(lambda T: 38.7 * T**2.7 * exp(-3150.15428/T)) @@ -888,10 +888,10 @@ def test_custom(self): gas2.TPX = self.gas.TPX self.assertEqual(gas2.n_reactions, 1) - self.assertNear(gas2.forward_rate_constants[0], - self.gas.forward_rate_constants[2]) - self.assertNear(gas2.net_rates_of_progress[0], - self.gas.net_rates_of_progress[2]) + # self.assertNear(gas2.forward_rate_constants[0], + # self.gas.forward_rate_constants[2]) + # self.assertNear(gas2.net_rates_of_progress[0], + # self.gas.net_rates_of_progress[2]) def test_custom2(self): gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 63ec1d6c59..17288936ed 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -72,14 +72,6 @@ GasKinetics::GasKinetics(ThermoPhase* thermo) : [&](size_t i, shared_ptr R) { modifyChebyshevReaction(i, dynamic_cast(*R)); }); - reg_addRxn("custom-Python", - [&](shared_ptr R) { - addCustomPyReaction(dynamic_cast(*R)); - }); - reg_modRxn("custom-Python", - [&](size_t i, shared_ptr R) { - modifyCustomPyReaction(i, dynamic_cast(*R)); - }); } void GasKinetics::update_rates_T() @@ -298,7 +290,11 @@ bool GasKinetics::addReaction(shared_ptr r) } try { - m_addRxn[r->type()](r); + if (r->rxnRate()) { + addRxnRate(r->rxnRate()); + } else { + m_addRxn[r->type()](r); + } } catch (std::out_of_range&) { throw CanteraError("GasKinetics::addReaction", "Unknown reaction type specified: '{}'", r->type()); @@ -370,9 +366,8 @@ void GasKinetics::addChebyshevReaction(ChebyshevReaction& r) m_cheb_rates.install(nReactions()-1, r.rate); } -void GasKinetics::addCustomPyReaction(CustomPyReaction& r) +void GasKinetics::addRxnRate(shared_ptr rate) { - shared_ptr rate = std::make_shared(std::move(r.rate)); rate->updateTemperature(thermo().temperature()); rate->updatePressure(thermo().pressure()); m_ROP_ok = false; @@ -386,7 +381,11 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) BulkKinetics::modifyReaction(i, rNew); try { - m_modRxn[rNew->type()](i, rNew); + if (rNew->rxnRate()) { + modifyRxnRate(i, rNew->rxnRate()); + } else { + m_modRxn[rNew->type()](i, rNew); + } } catch (std::out_of_range&) { throw CanteraError("GasKinetics::modifyReaction", "Unknown reaction type specified: '{}'", rNew->type()); @@ -421,10 +420,14 @@ void GasKinetics::modifyChebyshevReaction(size_t i, ChebyshevReaction& r) m_cheb_rates.replace(i, r.rate); } -void GasKinetics::modifyCustomPyReaction(size_t i, CustomPyReaction& r) +void GasKinetics::modifyRxnRate(size_t i, shared_ptr rate) { if (m_rxn_rates.find(i) != m_rxn_rates.end()) { - shared_ptr rate = std::make_shared(std::move(r.rate)); + if (m_rxn_rates[i]->type() != rate->type()) { + throw CanteraError("GasKinetics::modifyReaction", + "Attempting to replace '{}' with '{}'.", + m_rxn_rates[i]->type(), rate->type()); + } rate->updateTemperature(thermo().temperature()); rate->updatePressure(thermo().pressure()); m_ROP_ok = false; @@ -432,7 +435,7 @@ void GasKinetics::modifyCustomPyReaction(size_t i, CustomPyReaction& r) m_rxn_rates[i] = rate; } else { throw CanteraError("GasKinetics::modifyReaction", - "Index {} does not exist.", i); + "Index {} does not exist.", i); } } diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index c8fe3ccea7..372b826794 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -303,15 +303,6 @@ CustomPyReaction::CustomPyReaction() reaction_type = CUSTOMPY_RXN; } -CustomPyReaction::CustomPyReaction(const Composition& reactants_, - const Composition& products_, - const CustomPyRate& rate_) - : Reaction(reactants_, products_) - , rate(rate_) -{ - reaction_type = CUSTOMPY_RXN; -} - InterfaceReaction::InterfaceReaction() : is_sticking_coefficient(false) , use_motz_wise_correction(false) @@ -857,7 +848,8 @@ void setupCustomPyReaction(CustomPyReaction& R, const AnyMap& node, const Kinetics& kin) { setupReaction(R, node, kin); - R.rate = CustomPyRate(); + CustomPyRate rate; + R.setRxnRate(std::make_shared(std::move(rate))); } void setupInterfaceReaction(InterfaceReaction& R, const XML_Node& rxn_node) From 71c7150f1ecdcaf208201a60eebfecd67a807346 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 28 Feb 2021 23:13:37 -0600 Subject: [PATCH 35/67] [Kinetics] add alternative ArrheniusRate --- include/cantera/kinetics/RxnRates.h | 31 +++++++++++++++++++++++++++++ src/kinetics/RxnRates.cpp | 2 ++ 2 files changed, 33 insertions(+) diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 927d453b67..6a15bd43b2 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -473,6 +473,8 @@ class RxnRate "Pressure has to be positive."); } m_pressure = P; + m_logP = log(P); + m_log10P = log10(P); } //! Evaluate reaction rate @@ -490,6 +492,12 @@ class RxnRate //! Pressure used for reaction rate evaluation static double m_pressure; + + //! Logarithm of pressure used for reaction rate evaluation + static double m_logP; + + //! 10-base logarithm of pressure used for reaction rate evaluation + static double m_log10P; }; @@ -524,6 +532,29 @@ class CustomPyRate : public RxnRate shared_ptr m_ratefunc; }; + +//! Arrhenius reaction rate type depends only on temperature +/** + * Wrapped Arrhenius rate. + * + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ +class ArrheniusRate : public RxnRate, public Arrhenius +{ +public: + //! Constructor. + ArrheniusRate(); + + virtual std::string type() const { + return "Arrhenius"; + } + + virtual double eval() const { + return updateRC(m_logT, m_recipT); + } +}; + } #endif diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index af33af8397..258881f544 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -163,6 +163,8 @@ double RxnRate::m_temperature = 1.; double RxnRate::m_logT = 0.; double RxnRate::m_recipT = 1.; double RxnRate::m_pressure = 1.; +double RxnRate::m_logP = 0.; +double RxnRate::m_log10P = 0.; CustomPyRate::CustomPyRate() : m_ratefunc(0) {} From d5930d5b081f3da08573b8d040f58e29b888bb51 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 28 Feb 2021 23:16:56 -0600 Subject: [PATCH 36/67] [Kinetics] update state used for RxnRate calculations --- include/cantera/kinetics/FalloffMgr.h | 12 ++---------- include/cantera/kinetics/GasKinetics.h | 2 ++ interfaces/cython/cantera/test/test_kinetics.py | 8 ++++---- src/kinetics/GasKinetics.cpp | 17 +++++++++++++---- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/include/cantera/kinetics/FalloffMgr.h b/include/cantera/kinetics/FalloffMgr.h index bf4c255f11..a89f97ff04 100644 --- a/include/cantera/kinetics/FalloffMgr.h +++ b/include/cantera/kinetics/FalloffMgr.h @@ -45,11 +45,7 @@ class FalloffMgr m_offset.push_back(m_worksize); m_worksize += f->workSize(); m_falloff.push_back(f); - if (reactionType == FALLOFF_RXN) { - m_isfalloff.push_back(true); - } else { - m_isfalloff.push_back(false); - } + m_isfalloff.push_back(reactionType == FALLOFF_RXN); m_indices[rxn] = m_falloff.size()-1; } @@ -65,11 +61,7 @@ class FalloffMgr m_offset.push_back(m_worksize); m_worksize += f->workSize(); m_falloff.push_back(f); - if (type == "falloff") { - m_isfalloff.push_back(true); - } else { - m_isfalloff.push_back(false); - } + m_isfalloff.push_back(type == "falloff"); m_indices[rxn] = m_falloff.size()-1; } diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index adb442d5b4..1ad3d62585 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -113,6 +113,8 @@ class GasKinetics : public BulkKinetics vector_fp m_rfn_high; doublereal m_pres; //!< Last pressure at which rates were evaluated + bool m_update; //!< Boolean flag indicating whether update is needed + vector_fp falloff_work; vector_fp concm_3b_values; vector_fp concm_falloff_values; diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 780bfd13a0..72937a63d7 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -888,10 +888,10 @@ def test_custom1(self): gas2.TPX = self.gas.TPX self.assertEqual(gas2.n_reactions, 1) - # self.assertNear(gas2.forward_rate_constants[0], - # self.gas.forward_rate_constants[2]) - # self.assertNear(gas2.net_rates_of_progress[0], - # self.gas.net_rates_of_progress[2]) + self.assertNear(gas2.forward_rate_constants[0], + self.gas.forward_rate_constants[2]) + self.assertNear(gas2.net_rates_of_progress[0], + self.gas.net_rates_of_progress[2]) def test_custom2(self): gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 17288936ed..9884a0646f 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -17,7 +17,8 @@ GasKinetics::GasKinetics(ThermoPhase* thermo) : m_logp_ref(0.0), m_logc_ref(0.0), m_logStandConc(0.0), - m_pres(0.0) + m_pres(0.0), + m_update(true) { reg_addRxn("elementary", [&](shared_ptr R) { @@ -97,7 +98,13 @@ void GasKinetics::update_rates_T() m_ROP_ok = false; } - if (T != m_temp || P != m_pres) { + if (T != m_temp || P != m_pres || m_update) { + if (!m_rxn_rates.empty()) { + auto rate = m_rxn_rates.begin()->second; + rate->updateTemperature(T); + rate->updatePressure(P); + } + for (auto& rate : m_rxn_rates) { // generic reaction rates m_rfn.data()[rate.first] = rate.second->eval(); @@ -112,6 +119,8 @@ void GasKinetics::update_rates_T() m_cheb_rates.update(T, logT, m_rfn.data()); m_ROP_ok = false; } + + m_update = false; } m_pres = P; m_temp = T; @@ -370,7 +379,7 @@ void GasKinetics::addRxnRate(shared_ptr rate) { rate->updateTemperature(thermo().temperature()); rate->updatePressure(thermo().pressure()); - m_ROP_ok = false; + m_update = true; m_rxn_rates[nReactions()-1] = rate; } @@ -430,7 +439,7 @@ void GasKinetics::modifyRxnRate(size_t i, shared_ptr rate) } rate->updateTemperature(thermo().temperature()); rate->updatePressure(thermo().pressure()); - m_ROP_ok = false; + m_update = true; m_rxn_rates[i] = rate; } else { From 359f7211c5666e3354c5dcff509be2e5587309fb Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 1 Mar 2021 09:38:43 -0600 Subject: [PATCH 37/67] [Func1] prevent invalid callback functions. --- interfaces/cython/cantera/func1.pyx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interfaces/cython/cantera/func1.pyx b/interfaces/cython/cantera/func1.pyx index 9315576998..12ad683674 100644 --- a/interfaces/cython/cantera/func1.pyx +++ b/interfaces/cython/cantera/func1.pyx @@ -63,6 +63,9 @@ cdef class Func1: def __init__(self, c): if hasattr(c, '__call__'): + if type(c).__name__ == 'method': + raise TypeError("Unable to use callback from 'method'.") + # callback function self._set_callback(c) else: From 4c181c0e935c8de50c80f89de6285c1b248f51d5 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 1 Mar 2021 09:38:01 -0600 Subject: [PATCH 38/67] [UnitTest] Create stand-alone file to test reactions --- interfaces/cython/cantera/reaction.pyx | 11 +- .../cython/cantera/test/test_kinetics.py | 58 --------- .../cython/cantera/test/test_reactions.py | 110 ++++++++++++++++++ 3 files changed, 119 insertions(+), 60 deletions(-) create mode 100644 interfaces/cython/cantera/test/test_reactions.py diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 4d3818014f..a710a4a179 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -480,11 +480,14 @@ cdef class ElementaryReaction(Reaction): def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): - if init and equation and rate and kinetics: + if init and equation and kinetics: + if isinstance(rate, dict): coeffs = ['{}: {}'.format(k, v) for k, v in rate.items()] + elif isinstance(rate, Arrhenius) or rate is None: + coeffs = ['{}: 0.'.format(k) for k in ['A', 'b', 'Ea']] else: - raise ValueError('Invalid rate definition') + raise ValueError("Invalid rate definition") rate_def = '{{{}}}'.format(', '.join(coeffs)) yaml = '{{equation: {}, rate-constant: {}, type: {}}}'.format( @@ -493,6 +496,9 @@ cdef class ElementaryReaction(Reaction): deref(kinetics.kinetics)) self.reaction = self._reaction.get() + if isinstance(rate, Arrhenius): + self.rate = rate + property rate: """ Get/Set the `Arrhenius` rate coefficient for this reaction. """ def __get__(self): @@ -871,6 +877,7 @@ cdef class CustomReaction(Reaction): init=True, **kwargs): if init and equation and kinetics: + yaml = '{{equation: {}, type: {}}}'.format(equation, self.reaction_type) self._reaction = CxxNewReaction(AnyMapFromYamlString(stringify(yaml)), deref(kinetics.kinetics)) diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 72937a63d7..7a21bce410 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -3,7 +3,6 @@ import itertools from os.path import join as pjoin import os -from math import exp import cantera as ct from . import utilities @@ -856,67 +855,10 @@ def test_elementary(self): self.assertNear(gas2.net_rates_of_progress[0], self.gas.net_rates_of_progress[2]) - def test_elementary2(self): - gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', - species=self.species, reactions=[]) - gas2.TPX = self.gas.TPX - - r = ct.ElementaryReaction(equation='H2 + O <=> H + OH', - rate={'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07}, - kinetics=gas2) - - self.assertNear(r.rate(self.gas.T), self.gas.forward_rate_constants[2]) - self.assertEqual(r.reactants, self.gas.reaction(2).reactants) - self.assertEqual(r.products, self.gas.reaction(2).products) - - gas2.add_reaction(r) - self.assertNear(gas2.forward_rate_constants[0], - self.gas.forward_rate_constants[2]) - self.assertNear(gas2.net_rates_of_progress[0], - self.gas.net_rates_of_progress[2]) - def test_arrhenius_rate(self): R = self.gas.reaction(2) self.assertNear(R.rate(self.gas.T), self.gas.forward_rate_constants[2]) - def test_custom1(self): - r = ct.CustomReaction({'O':1, 'H2':1}, {'H':1, 'OH':1}) - r.rate = ct.CustomRate(lambda T: 38.7 * T**2.7 * exp(-3150.15428/T)) - - gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', - species=self.species, reactions=[r]) - gas2.TPX = self.gas.TPX - - self.assertEqual(gas2.n_reactions, 1) - self.assertNear(gas2.forward_rate_constants[0], - self.gas.forward_rate_constants[2]) - self.assertNear(gas2.net_rates_of_progress[0], - self.gas.net_rates_of_progress[2]) - - def test_custom2(self): - gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', - species=self.species, reactions=[]) - gas2.TPX = self.gas.TPX - - r = ct.CustomReaction(equation='H2 + O <=> H + OH', - rate=lambda T: 38.7 * T**2.7 * exp(-3150.15428/T), - kinetics=self.gas) - self.assertNear(r.rate(self.gas.T), self.gas.forward_rate_constants[2]) - self.assertEqual(r.reactants, self.gas.reaction(2).reactants) - self.assertEqual(r.products, self.gas.reaction(2).products) - - gas2.add_reaction(r) - self.assertNear(gas2.forward_rate_constants[0], - self.gas.forward_rate_constants[2]) - self.assertNear(gas2.net_rates_of_progress[0], - self.gas.net_rates_of_progress[2]) - - def test_custom_rate(self): - # probe O + H2 <=> H + OH - rr = ct.CustomRate(lambda T: 38.7 * T**2.7 * exp(-3150.15428/T)) - R = self.gas.reaction(2) - self.assertNear(R.rate(self.gas.T), rr(self.gas.T)) - def test_negative_A(self): species = ct.Species.listFromFile('gri30.cti') r = ct.ElementaryReaction('NH:1, NO:1', 'N2O:1, H:1') diff --git a/interfaces/cython/cantera/test/test_reactions.py b/interfaces/cython/cantera/test/test_reactions.py new file mode 100644 index 0000000000..a72799bdb6 --- /dev/null +++ b/interfaces/cython/cantera/test/test_reactions.py @@ -0,0 +1,110 @@ +from math import exp + +import cantera as ct +from . import utilities + + +class TestElementary(utilities.CanteraTest): + + _cls = ct.ElementaryReaction + _equation = 'H2 + O <=> H + OH' + _rate = {'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07} + _rate_obj = ct.Arrhenius(38.7, 2.7, 2.619184e+07) + _index = 2 + _type = "elementary" + + @classmethod + def setUpClass(cls): + utilities.CanteraTest.setUpClass() + cls.gas = ct.Solution('h2o2.xml') + 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.species = ct.Species.listFromFile('h2o2.xml') + + def check_rxn(self, rxn): + ix = self._index + self.assertEqual(rxn.reaction_type, self._type) + self.assertNear(rxn.rate(self.gas.T), self.gas.forward_rate_constants[ix]) + self.assertEqual(rxn.reactants, self.gas.reaction(ix).reactants) + self.assertEqual(rxn.products, self.gas.reaction(ix).products) + + gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', + species=self.species, reactions=[rxn]) + gas2.TPX = self.gas.TPX + self.check_sol(gas2) + + def check_sol(self, gas2): + ix = self._index + self.assertEqual(gas2.reaction_type_str(0), self._type) + self.assertNear(gas2.forward_rate_constants[0], + self.gas.forward_rate_constants[ix]) + self.assertNear(gas2.net_rates_of_progress[0], + self.gas.net_rates_of_progress[ix]) + + def test_rate(self): + self.assertNear(self._rate_obj(self.gas.T), self.gas.forward_rate_constants[self._index]) + + def test_from_parts(self): + orig = self.gas.reaction(self._index) + rxn = self._cls(orig.reactants, orig.products) + rxn.rate = self._rate_obj + self.check_rxn(rxn) + + def test_from_dict(self): + rxn = self._cls(equation=self._equation, rate=self._rate, kinetics=self.gas) + self.check_rxn(rxn) + + def test_from_rate(self): + rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas) + self.check_rxn(rxn) + + def test_add_rxn(self): + gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', + species=self.species, reactions=[]) + gas2.TPX = self.gas.TPX + + rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas) + gas2.add_reaction(rxn) + self.check_sol(gas2) + + def test_no_rate(self): + rxn = self._cls(equation=self._equation, kinetics=self.gas) + self.assertNear(rxn.rate(self.gas.T), 0.) + + gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', + species=self.species, reactions=[rxn]) + gas2.TPX = self.gas.TPX + + self.assertNear(gas2.forward_rate_constants[0], 0.) + self.assertNear(gas2.net_rates_of_progress[0], 0.) + + +class TestCustom(TestElementary): + + # probe O + H2 <=> H + OH + _cls = ct.CustomReaction + _equation = 'H2 + O <=> H + OH' + _rate_obj = ct.CustomRate(lambda T: 38.7 * T**2.7 * exp(-3150.15428/T)) + _index = 2 + _type = "custom-Python" + + def setUp(self): + # need to overwrite rate to ensure correct type ('method' is not compatible with Func1) + self._rate = lambda T: 38.7 * T**2.7 * exp(-3150.15428/T) + + def test_from_func(self): + f = ct.Func1(self._rate) + rxn = ct.CustomReaction(equation=self._equation, rate=f, kinetics=self.gas) + self.check_rxn(rxn) + + def test_rate_func(self): + f = ct.Func1(self._rate) + rate = ct.CustomRate(f) + self.assertNear(rate(self.gas.T), self.gas.forward_rate_constants[self._index]) + + def test_custom(self): + rxn = ct.CustomReaction(equation=self._equation, + rate=lambda T: 38.7 * T**2.7 * exp(-3150.15428/T), + kinetics=self.gas) + self.check_rxn(rxn) + From b1efc54c2dd6c63b38315ae7cfb9032d972c7b7d Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 1 Mar 2021 13:01:23 -0600 Subject: [PATCH 39/67] [Kinetics] create TestReaction for benchmarking purposes --- include/cantera/kinetics/Reaction.h | 29 +++++++++++++++++++++++++++++ src/kinetics/Reaction.cpp | 15 +++++++++++++++ src/kinetics/ReactionFactory.cpp | 12 ++++++++++++ 3 files changed, 56 insertions(+) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index d3267a919f..f64ed0aeb3 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -260,6 +260,10 @@ class ChebyshevReaction : public Reaction //! A reaction which follows mass-action kinetics with a custom reaction rate //! defined in Python. +/** + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ class CustomPyReaction : public Reaction { public: @@ -270,6 +274,28 @@ class CustomPyReaction : public Reaction } }; + +//! A reaction which follows mass-action kinetics with a modified Arrhenius +//! reaction rate. +/** + * Alternative elementary reaction based on RxnRate. + * + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ +class TestReaction : public Reaction +{ +public: + TestReaction(); + + virtual std::string type() const { + return "elementary-new"; + } + + bool allow_negative_pre_exponential_factor; +}; + + //! Modifications to an InterfaceReaction rate based on a surface species //! coverage. struct CoverageDependency @@ -391,6 +417,9 @@ void setupChebyshevReaction(ChebyshevReaction&, const AnyMap&, void setupCustomPyReaction(CustomPyReaction&, const AnyMap&, const Kinetics&); +void setupTestReaction(TestReaction&, const AnyMap&, + const Kinetics&); + void setupInterfaceReaction(InterfaceReaction&, const XML_Node&); void setupInterfaceReaction(InterfaceReaction&, const AnyMap&, const Kinetics&); diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 372b826794..9530785226 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -303,6 +303,12 @@ CustomPyReaction::CustomPyReaction() reaction_type = CUSTOMPY_RXN; } +TestReaction::TestReaction() + : Reaction() + , allow_negative_pre_exponential_factor(false) +{ +} + InterfaceReaction::InterfaceReaction() : is_sticking_coefficient(false) , use_motz_wise_correction(false) @@ -852,6 +858,15 @@ void setupCustomPyReaction(CustomPyReaction& R, const AnyMap& node, R.setRxnRate(std::make_shared(std::move(rate))); } +void setupTestReaction(TestReaction& R, const AnyMap& node, + const Kinetics& kin) +{ + setupReaction(R, node, kin); + R.allow_negative_pre_exponential_factor = node.getBool("negative-A", false); + Arrhenius arr = readArrhenius(R, node["rate-constant"], kin, node.units()); + throw CanteraError("setupTestReaction", "Work-in-progress"); +} + void setupInterfaceReaction(InterfaceReaction& R, const XML_Node& rxn_node) { XML_Node& arr = rxn_node.child("rateCoeff").child("Arrhenius"); diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 12c5376c36..34ac4f6063 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -108,6 +108,18 @@ ReactionFactory::ReactionFactory() setupCustomPyReaction(*(CustomPyReaction*)R, node, kin); }); + // register custom Python reactions + reg("elementary-new", []() { return new TestReaction(); }); + reg_XML("elementary-new", + [](Reaction* R, const XML_Node& node) { + throw CanteraError("ReactionFactory", "Test reactions " + "cannot be created from XML nodes'"); + }); + reg_AnyMap("elementary-new", + [](Reaction* R, const AnyMap& node, const Kinetics& kin) { + setupTestReaction(*(TestReaction*)R, node, kin); + }); + // register interface reactions reg("interface", []() { return new InterfaceReaction(); }); addAlias("interface", "surface"); From fe3e96d9df01add47e366694f859400e48287d41 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 1 Mar 2021 14:53:44 -0600 Subject: [PATCH 40/67] [UnitTest] include new test in __init__ --- interfaces/cython/cantera/test/__init__.py | 15 ++++++++------- .../test/{test_reactions.py => test_reaction.py} | 0 2 files changed, 8 insertions(+), 7 deletions(-) rename interfaces/cython/cantera/test/{test_reactions.py => test_reaction.py} (100%) diff --git a/interfaces/cython/cantera/test/__init__.py b/interfaces/cython/cantera/test/__init__.py index 3eba767bd5..7e8905233c 100644 --- a/interfaces/cython/cantera/test/__init__.py +++ b/interfaces/cython/cantera/test/__init__.py @@ -1,17 +1,18 @@ import os import cantera -from .test_thermo import * -from .test_purefluid import * +from .test_composite import * +from .test_convert import * from .test_equilibrium import * +from .test_func1 import * from .test_kinetics import * -from .test_transport import * from .test_mixture import * -from .test_func1 import * -from .test_reactor import * from .test_onedim import * -from .test_convert import * -from .test_composite import * +from .test_purefluid import * +from .test_reaction import * +from .test_reactor import * +from .test_thermo import * +from .test_transport import * cantera.add_directory(os.path.join(os.path.dirname(__file__), 'data')) cantera.add_directory(os.path.join(os.path.dirname(__file__), '..', 'examples', 'surface_chemistry')) diff --git a/interfaces/cython/cantera/test/test_reactions.py b/interfaces/cython/cantera/test/test_reaction.py similarity index 100% rename from interfaces/cython/cantera/test/test_reactions.py rename to interfaces/cython/cantera/test/test_reaction.py From 7125e11c2d4dc3248bcfef2f6da50e6d0a5b41ed Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 1 Mar 2021 21:26:57 -0600 Subject: [PATCH 41/67] [Kinetics] expose TestReaction to Python interface --- include/cantera/kinetics/Reaction.h | 6 +- include/cantera/kinetics/RxnRates.h | 6 +- interfaces/cython/cantera/_cantera.pxd | 39 ++++-- interfaces/cython/cantera/reaction.pyx | 167 +++++++++++++++++++++---- src/kinetics/Reaction.cpp | 5 +- src/kinetics/RxnRates.cpp | 4 + 6 files changed, 191 insertions(+), 36 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index f64ed0aeb3..9826a14edd 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -60,12 +60,12 @@ class Reaction m_valid = valid; } - //! Retrieve reaction rate pointer - shared_ptr rxnRate() const { + //! Get reaction rate pointer + shared_ptr rxnRate() { return m_rate; } - //! Retrieve reaction rate pointer + //! Set reaction rate pointer void setRxnRate(shared_ptr rate) { m_rate = rate; } diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 6a15bd43b2..d14af5da03 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -516,7 +516,7 @@ class CustomPyRate : public RxnRate CustomPyRate(); virtual std::string type() const { - return "custom-Python"; + return "custom-PythonRate"; } // set custom rate @@ -546,8 +546,10 @@ class ArrheniusRate : public RxnRate, public Arrhenius //! Constructor. ArrheniusRate(); + ArrheniusRate(double A, double b, double E); + virtual std::string type() const { - return "Arrhenius"; + return "ArrheniusRate"; } virtual double eval() const { diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index a8664683bd..4a356df0bf 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -312,16 +312,26 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": double temperatureExponent() double activationEnergy_R() - cdef cppclass CxxCustomPyRate "Cantera::CustomPyRate": - CxxCustomPyRate() + cdef cppclass CxxRxnRate "Cantera::RxnRate": + CxxRxnRate() string type() double temperature() void updateTemperature(double) except +translate_exception double pressure() void updatePressure(double) except +translate_exception double eval() except +translate_exception + + cdef cppclass CxxCustomPyRate "Cantera::CustomPyRate" (CxxRxnRate): + CxxCustomPyRate() void setRateFunction(shared_ptr[CxxFunc1]) except +translate_exception + cdef cppclass CxxArrheniusRate "Cantera::ArrheniusRate" (CxxRxnRate): + CxxArrheniusRate() + CxxArrheniusRate(double, double, double) + double preExponentialFactor() + double temperatureExponent() + double activationEnergy_R() + cdef cppclass CxxReaction "Cantera::Reaction": CxxReaction() @@ -338,6 +348,8 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cbool duplicate cbool allow_nonreactant_orders cbool allow_negative_orders + shared_ptr[CxxRxnRate] rxnRate() + void setRxnRate(shared_ptr[CxxRxnRate]) cdef cppclass CxxElementaryReaction "Cantera::ElementaryReaction" (CxxReaction): CxxElementaryReaction() @@ -403,7 +415,10 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxCustomPyReaction "Cantera::CustomPyReaction" (CxxReaction): CxxCustomPyReaction() - void setRxnRate(shared_ptr[CxxCustomPyRate]) + + cdef cppclass CxxTestReaction "Cantera::TestReaction" (CxxReaction): + CxxTestReaction() + cbool allow_negative_pre_exponential_factor cdef cppclass CxxCoverageDependency "Cantera::CoverageDependency": CxxCoverageDependency(double, double, double) @@ -1018,6 +1033,19 @@ cdef class ThermoPhase(_SolutionBase): cdef class InterfacePhase(ThermoPhase): cdef CxxSurfPhase* surf +cdef class _RxnRate: + cdef shared_ptr[CxxRxnRate] _base + cdef CxxRxnRate* base + +cdef class CustomRate(_RxnRate): + cdef CxxCustomPyRate* rate + cdef Func1 _rate_func + +cdef class ArrheniusRate(_RxnRate): + cdef CxxArrheniusRate* rate + @staticmethod + cdef wrap(shared_ptr[CxxRxnRate]) + cdef class Reaction: cdef shared_ptr[CxxReaction] _reaction cdef CxxReaction* reaction @@ -1031,11 +1059,6 @@ cdef class Arrhenius: cdef CxxArrhenius* rate cdef Reaction reaction # parent reaction, to prevent garbage collection -cdef class CustomRate: - cdef shared_ptr[CxxCustomPyRate] _rate - cdef CxxCustomPyRate* rate - cdef Func1 _rate_func - cdef class Falloff: cdef shared_ptr[CxxFalloff] _falloff cdef CxxFalloff* falloff diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index a710a4a179..7de17b4502 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -403,7 +403,33 @@ cdef copyArrhenius(CxxArrhenius* rate): return r -cdef class CustomRate: +cdef class _RxnRate: + + def __repr__(self): + return "<{}>".format(pystr(self.base.type())) + + def __call__(self, temperature=None): + if temperature is not None: + self.temperature = temperature + + return self.base.eval() + + property temperature: + """ Get/Set temperature used for all reaction rate evaluations""" + def __get__(self): + return self.base.temperature() + def __set__(self, float temperature): + self.base.updateTemperature(temperature) + + property pressure: + """ Get/Set pressure used for all reaction rate evaluations""" + def __get__(self): + return self.base.pressure() + def __set__(self, float pressure): + self.base.updatePressure(pressure) + + +cdef class CustomRate(_RxnRate): r""" A custom rate coefficient which depends on temperature only. @@ -418,13 +444,11 @@ cdef class CustomRate: def __cinit__(self, k=None, init=True): if init: - self._rate.reset(new CxxCustomPyRate()) - self.rate = self._rate.get() + self._base.reset(new CxxCustomPyRate()) + self.base = self._base.get() + self.rate = (self.base) self.set_rate_function(k) - def __repr__(self): - return "<{}>".format(pystr(self.rate.type())) - def set_rate_function(self, k): r""" Set the function describing a custom reaction rate:: @@ -443,25 +467,63 @@ cdef class CustomRate: self.rate.setRateFunction(self._rate_func._func) - def __call__(self, temperature=None): - if temperature is not None: - self.temperature = temperature - return self.rate.eval() +cdef class ArrheniusRate(_RxnRate): + r""" + A reaction rate coefficient which depends on temperature only and follows + the modified Arrhenius form: - property temperature: - """ Get/Set temperature used for all reaction rate evaluations""" + .. math:: + + k_f = A T^b \exp{-\tfrac{E}{RT}} + + where *A* is the `pre_exponential_factor`, *b* is the `temperature_exponent`, + and *E* is the `activation_energy`. + + Warning: this class is an experimental part of the Cantera API and + may be changed or removed without notice. + """ + def __cinit__(self, A=0, b=0, E=0, init=True): + + if init: + self._base.reset(new CxxArrheniusRate(A, b, E / gas_constant)) + self.base = self._base.get() + self.rate = (self.base) + + @staticmethod + cdef wrap(shared_ptr[CxxRxnRate] rate): + """ + Wrap a C++ RxnRate object with a Python object. + """ + # wrap C++ reaction + cdef ArrheniusRate arr + arr = ArrheniusRate(init=False) + arr._base = rate + arr.base = arr._base.get() + arr.rate = (arr.base) + return arr + + 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.rate.temperature() - def __set__(self, float temperature): - self.rate.updateTemperature(temperature) + return self.rate.preExponentialFactor() - property pressure: - """ Get/Set pressure used for all reaction rate evaluations""" + property temperature_exponent: + """ + The temperature exponent *b*. + """ def __get__(self): - return self.rate.pressure() - def __set__(self, float pressure): - self.rate.updatePressure(pressure) + return self.rate.temperatureExponent() + + property activation_energy: + """ + The activation energy *E* [J/kmol]. + """ + def __get__(self): + return self.rate.activationEnergy_R() * gas_constant cdef class ElementaryReaction(Reaction): @@ -487,7 +549,7 @@ cdef class ElementaryReaction(Reaction): elif isinstance(rate, Arrhenius) or rate is None: coeffs = ['{}: 0.'.format(k) for k in ['A', 'b', 'Ea']] else: - raise ValueError("Invalid rate definition") + raise TypeError("Invalid rate definition") rate_def = '{{{}}}'.format(', '.join(coeffs)) yaml = '{{equation: {}, rate-constant: {}, type: {}}}'.format( @@ -891,7 +953,68 @@ cdef class CustomReaction(Reaction): def __set__(self, CustomRate rate): self._rate = rate cdef CxxCustomPyReaction* r = self.reaction - r.setRxnRate(self._rate._rate) + r.setRxnRate(self._rate._base) + + +cdef class TestReaction(Reaction): + """ + A reaction which follows mass-action kinetics with a modified Arrhenius + reaction rate. The class is a re-implementation of `ElementaryReaction` + and serves for testing purposes. + + An example for the definition of a `TestReaction` object is given as:: + + rxn = TestReaction(equation='H2 + O <=> H + OH', + rate={'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07}, + kinetics=gas) + + Warning: this class is an experimental part of the Cantera API and + may be changed or removed without notice. + """ + reaction_type = "elementary-new" + + def __init__(self, equation=None, rate=None, Kinetics kinetics=None, + init=True, **kwargs): + + if init and equation and kinetics: + + if isinstance(rate, dict): + coeffs = ['{}: {}'.format(k, v) for k, v in rate.items()] + elif isinstance(rate, ArrheniusRate) or rate is None: + coeffs = ['{}: 0.'.format(k) for k in ['A', 'b', 'Ea']] + else: + raise TypeError("Invalid rate definition") + + rate_def = '{{{}}}'.format(', '.join(coeffs)) + yaml = '{{equation: {}, rate-constant: {}, type: {}}}'.format( + equation, rate_def, self.reaction_type) + self._reaction = CxxNewReaction(AnyMapFromYamlString(stringify(yaml)), + deref(kinetics.kinetics)) + self.reaction = self._reaction.get() + + if isinstance(rate, ArrheniusRate): + self.rate = rate + + property rate: + """ Get/Set the `Arrhenius` rate coefficient for this reaction. """ + def __get__(self): + cdef CxxTestReaction* r = self.reaction + return ArrheniusRate.wrap(r.rxnRate()) + def __set__(self, ArrheniusRate rate): + cdef CxxTestReaction* r = self.reaction + r.setRxnRate(rate._base) + + property allow_negative_pre_exponential_factor: + """ + Get/Set whether the rate coefficient is allowed to have a negative + pre-exponential factor. + """ + def __get__(self): + cdef CxxElementaryReaction* r = self.reaction + return r.allow_negative_pre_exponential_factor + def __set__(self, allow): + cdef CxxElementaryReaction* r = self.reaction + r.allow_negative_pre_exponential_factor = allow cdef class InterfaceReaction(ElementaryReaction): diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 9530785226..de522b5231 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -864,7 +864,10 @@ void setupTestReaction(TestReaction& R, const AnyMap& node, setupReaction(R, node, kin); R.allow_negative_pre_exponential_factor = node.getBool("negative-A", false); Arrhenius arr = readArrhenius(R, node["rate-constant"], kin, node.units()); - throw CanteraError("setupTestReaction", "Work-in-progress"); + ArrheniusRate rate(arr.preExponentialFactor(), + arr.temperatureExponent(), + arr.activationEnergy_R()); + R.setRxnRate(std::make_shared(std::move(rate))); } void setupInterfaceReaction(InterfaceReaction& R, const XML_Node& rxn_node) diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 258881f544..c78c52dcc4 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -179,4 +179,8 @@ double CustomPyRate::eval() const { return 0.; } +ArrheniusRate::ArrheniusRate(double A, double b, double E) + : Arrhenius(A, b, E) { +} + } From f467be1e994b731589f991a54d8890c1177cf472 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 1 Mar 2021 21:43:07 -0600 Subject: [PATCH 42/67] [UnitTest] add tests for TestReaction --- .../cython/cantera/test/test_reaction.py | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index a72799bdb6..39e430a0f7 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -67,6 +67,10 @@ def test_add_rxn(self): gas2.add_reaction(rxn) self.check_sol(gas2) + def test_wrong_rate(self): + with self.assertRaises(TypeError): + rxn = self._cls(equation=self._equation, rate=[], kinetics=self.gas) + def test_no_rate(self): rxn = self._cls(equation=self._equation, kinetics=self.gas) self.assertNear(rxn.rate(self.gas.T), 0.) @@ -78,6 +82,11 @@ def test_no_rate(self): self.assertNear(gas2.forward_rate_constants[0], 0.) self.assertNear(gas2.net_rates_of_progress[0], 0.) + def test_replace_rate(self): + rxn = self._cls(equation=self._equation, kinetics=self.gas) + rxn.rate = self._rate_obj + self.check_rxn(rxn) + class TestCustom(TestElementary): @@ -91,7 +100,7 @@ class TestCustom(TestElementary): def setUp(self): # need to overwrite rate to ensure correct type ('method' is not compatible with Func1) self._rate = lambda T: 38.7 * T**2.7 * exp(-3150.15428/T) - + def test_from_func(self): f = ct.Func1(self._rate) rxn = ct.CustomReaction(equation=self._equation, rate=f, kinetics=self.gas) @@ -107,4 +116,13 @@ def test_custom(self): rate=lambda T: 38.7 * T**2.7 * exp(-3150.15428/T), kinetics=self.gas) self.check_rxn(rxn) - + + +class TestElementaryNew(TestElementary): + + _cls = ct.TestReaction + _equation = 'H2 + O <=> H + OH' + _rate = {'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07} + _rate_obj = ct.ArrheniusRate(38.7, 2.7, 2.619184e+07) + _index = 2 + _type = "elementary-new" From aa23b52c791e5378f83a45b6c3eca263f7326784 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 1 Mar 2021 22:46:14 -0600 Subject: [PATCH 43/67] [samples] illustrate usage of Python CustomReaction --- .../examples/kinetics/custom_reactions.py | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 interfaces/cython/cantera/examples/kinetics/custom_reactions.py diff --git a/interfaces/cython/cantera/examples/kinetics/custom_reactions.py b/interfaces/cython/cantera/examples/kinetics/custom_reactions.py new file mode 100644 index 0000000000..f6d11c526e --- /dev/null +++ b/interfaces/cython/cantera/examples/kinetics/custom_reactions.py @@ -0,0 +1,88 @@ +""" +An example demonstrating how to use custom reaction objects. + +For benchmark purposes, an ignition test is run to compare simulation times. + +Requires: cantera >= 2.6.0 +""" + +from timeit import default_timer +import numpy as np +from math import exp + +import cantera as ct + +gas0 = ct.Solution('h2o2.yaml') + +species = gas0.species() +reactions = gas0.reactions() + +# construct custom reactions: replace 2nd reaction with equivalent custom reaction +custom_reactions = [r for r in reactions] +custom_reactions[2] = ct.CustomReaction( + equation='H2 + O <=> H + OH', + rate=lambda T: 38.7 * T**2.7 * exp(-3150.15428/T), + kinetics=gas0) + +gas1 = ct.Solution(thermo='ideal-gas', kinetics='gas', + species=species, reactions=custom_reactions) + +# construct test reactions: replace all elementary reactions with alternatives +test_reactions = [] +for r in reactions: + + if r.reaction_type == "elementary": + A = r.rate.pre_exponential_factor + b = r.rate.temperature_exponent + Ea = r.rate.activation_energy + r_new = ct.TestReaction(equation=r.equation, + rate={'A': A, 'b': b, 'Ea': Ea}, + kinetics=gas0) + else: + r_new = r + + test_reactions.append(r_new) + +gas2 = ct.Solution(thermo='ideal-gas', kinetics='gas', + species=species, reactions=test_reactions) + +# construct test case - simulate ignition + +def ignition(gas): + # set up reactor + gas.TP = 900, 5*ct.one_atm + gas.set_equivalence_ratio(0.4, 'H2', 'O2:1.0, AR:4.0') + r = ct.IdealGasReactor(gas) + net = ct.ReactorNet([r]) + net.rtol_sensitivity = 2e-5 + + # time reactor integration + t1 = default_timer() + while net.time < .5: + net.step() + t2 = default_timer() + + return 1000* (t2 - t1) + +# output results + +repeat = 100 +print('Average time of {} simulation runs:'.format(repeat)) + +sim0 = 0 +for i in range(repeat): + sim0 += ignition(gas0) +print('- Original mechanism: ' + '{0:.2f} ms (T_final={1:.2f})'.format(sim0/repeat, gas0.T)) + +sim1 = 0 +for i in range(repeat): + sim1 += ignition(gas1) +print('- One Python reaction: ' + '{0:.2f} ms (T_final={1:.2f})'.format(sim1/repeat, gas1.T)) + +sim2 = 0 +for i in range(repeat): + sim2 += ignition(gas2) +print('- Alternative reactions: ' + '{0:.2f} ms (T_final={1:.2f})'.format(sim2/repeat, gas2.T)) From 139323a7f52aeeb809918b305053d8ff51394cc8 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 2 Mar 2021 17:31:38 -0600 Subject: [PATCH 44/67] [Kinetics] minor simplifications and edits --- include/cantera/kinetics/GasKinetics.h | 1 - include/cantera/kinetics/RxnRates.h | 8 +++---- .../examples/kinetics/custom_reactions.py | 15 ++++++------- src/kinetics/GasKinetics.cpp | 22 +++++-------------- 4 files changed, 17 insertions(+), 29 deletions(-) diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index 1ad3d62585..644460e39e 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -113,7 +113,6 @@ class GasKinetics : public BulkKinetics vector_fp m_rfn_high; doublereal m_pres; //!< Last pressure at which rates were evaluated - bool m_update; //!< Boolean flag indicating whether update is needed vector_fp falloff_work; vector_fp concm_3b_values; diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index d14af5da03..6afe053ee5 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -446,12 +446,12 @@ class RxnRate virtual std::string type() const = 0; //! Get temperature - double temperature() const { + static double temperature() { return m_temperature; } //! Update temperature - void updateTemperature(double T) { + static void updateTemperature(double T) { if (T <= 0.) { throw CanteraError("RxnRate::updateTemperature", "Temperature has to be positive."); @@ -462,12 +462,12 @@ class RxnRate } //! Get pressure - double pressure() const { + static double pressure() { return m_pressure; } //! Update pressure - void updatePressure(double P) { + static void updatePressure(double P) { if (P <= 0.) { throw CanteraError("RxnRate::updatePressure", "Pressure has to be positive."); diff --git a/interfaces/cython/cantera/examples/kinetics/custom_reactions.py b/interfaces/cython/cantera/examples/kinetics/custom_reactions.py index f6d11c526e..bd506e4317 100644 --- a/interfaces/cython/cantera/examples/kinetics/custom_reactions.py +++ b/interfaces/cython/cantera/examples/kinetics/custom_reactions.py @@ -50,19 +50,18 @@ def ignition(gas): # set up reactor - gas.TP = 900, 5*ct.one_atm + gas.TP = 900, 5 * ct.one_atm gas.set_equivalence_ratio(0.4, 'H2', 'O2:1.0, AR:4.0') r = ct.IdealGasReactor(gas) net = ct.ReactorNet([r]) - net.rtol_sensitivity = 2e-5 + net.rtol_sensitivity = 2.e-5 # time reactor integration t1 = default_timer() - while net.time < .5: - net.step() + net.advance(.5) t2 = default_timer() - return 1000* (t2 - t1) + return 1000 * (t2 - t1) # output results @@ -73,16 +72,16 @@ def ignition(gas): for i in range(repeat): sim0 += ignition(gas0) print('- Original mechanism: ' - '{0:.2f} ms (T_final={1:.2f})'.format(sim0/repeat, gas0.T)) + '{0:.2f} ms (T_final={1:.2f})'.format(sim0 / repeat, gas0.T)) sim1 = 0 for i in range(repeat): sim1 += ignition(gas1) print('- One Python reaction: ' - '{0:.2f} ms (T_final={1:.2f})'.format(sim1/repeat, gas1.T)) + '{0:.2f} ms (T_final={1:.2f})'.format(sim1 / repeat, gas1.T)) sim2 = 0 for i in range(repeat): sim2 += ignition(gas2) print('- Alternative reactions: ' - '{0:.2f} ms (T_final={1:.2f})'.format(sim2/repeat, gas2.T)) + '{0:.2f} ms (T_final={1:.2f})'.format(sim2 / repeat, gas2.T)) diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 9884a0646f..25c2862e95 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -17,8 +17,7 @@ GasKinetics::GasKinetics(ThermoPhase* thermo) : m_logp_ref(0.0), m_logc_ref(0.0), m_logStandConc(0.0), - m_pres(0.0), - m_update(true) + m_pres(0.0) { reg_addRxn("elementary", [&](shared_ptr R) { @@ -98,11 +97,10 @@ void GasKinetics::update_rates_T() m_ROP_ok = false; } - if (T != m_temp || P != m_pres || m_update) { + if (T != m_temp || P != m_pres) { if (!m_rxn_rates.empty()) { - auto rate = m_rxn_rates.begin()->second; - rate->updateTemperature(T); - rate->updatePressure(P); + RxnRate::updateTemperature(T); + RxnRate::updatePressure(P); } for (auto& rate : m_rxn_rates) { @@ -119,8 +117,6 @@ void GasKinetics::update_rates_T() m_cheb_rates.update(T, logT, m_rfn.data()); m_ROP_ok = false; } - - m_update = false; } m_pres = P; m_temp = T; @@ -301,6 +297,8 @@ bool GasKinetics::addReaction(shared_ptr r) try { if (r->rxnRate()) { addRxnRate(r->rxnRate()); + RxnRate::updateTemperature(thermo().temperature()); + RxnRate::updatePressure(thermo().pressure()); } else { m_addRxn[r->type()](r); } @@ -377,10 +375,6 @@ void GasKinetics::addChebyshevReaction(ChebyshevReaction& r) void GasKinetics::addRxnRate(shared_ptr rate) { - rate->updateTemperature(thermo().temperature()); - rate->updatePressure(thermo().pressure()); - m_update = true; - m_rxn_rates[nReactions()-1] = rate; } @@ -437,10 +431,6 @@ void GasKinetics::modifyRxnRate(size_t i, shared_ptr rate) "Attempting to replace '{}' with '{}'.", m_rxn_rates[i]->type(), rate->type()); } - rate->updateTemperature(thermo().temperature()); - rate->updatePressure(thermo().pressure()); - m_update = true; - m_rxn_rates[i] = rate; } else { throw CanteraError("GasKinetics::modifyReaction", From 7a4e9f365f5b3060ecfecff8d89e5e16f41a4428 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 6 Mar 2021 07:13:24 -0600 Subject: [PATCH 45/67] [Kinetics] eliminate reg_addRxn and reg_modRxn --- include/cantera/kinetics/GasKinetics.h | 22 ----- src/kinetics/GasKinetics.cpp | 110 ++++++++----------------- 2 files changed, 36 insertions(+), 96 deletions(-) diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index 644460e39e..9cbc0381af 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -67,18 +67,6 @@ class GasKinetics : public BulkKinetics //! reactions. virtual void update_rates_C(); - //! register a function that adds a reaction - void reg_addRxn(const std::string& name, - std::function)> f) { - m_addRxn[name] = f; - } - - //! register a function that modifies a reaction - void reg_modRxn(const std::string& name, - std::function)> f) { - m_modRxn[name] = f; - } - protected: //! Reaction index of each falloff reaction std::vector m_fallindx; @@ -126,8 +114,6 @@ class GasKinetics : public BulkKinetics void addPlogReaction(PlogReaction& r); void addChebyshevReaction(ChebyshevReaction& r); - void addRxnRate(shared_ptr rate); - void modifyThreeBodyReaction(size_t i, ThreeBodyReaction& r); void modifyFalloffReaction(size_t i, FalloffReaction& r); void modifyPlogReaction(size_t i, PlogReaction& r); @@ -137,14 +123,6 @@ class GasKinetics : public BulkKinetics //! Update the equilibrium constants in molar units. void updateKc(); - - //! map functions adding reactions - std::unordered_map)>> m_addRxn; - - //! map functions modifying reactions - std::unordered_map)>> m_modRxn; }; } diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 25c2862e95..9eed64e424 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -19,59 +19,6 @@ GasKinetics::GasKinetics(ThermoPhase* thermo) : m_logStandConc(0.0), m_pres(0.0) { - reg_addRxn("elementary", - [&](shared_ptr R) { - addElementaryReaction(dynamic_cast(*R)); - }); - reg_modRxn("elementary", - [&](size_t i, shared_ptr R) { - modifyElementaryReaction(i, dynamic_cast(*R)); - }); - - reg_addRxn("three-body", - [&](shared_ptr R) { - addThreeBodyReaction(dynamic_cast(*R)); - }); - reg_modRxn("three-body", - [&](size_t i, shared_ptr R) { - modifyThreeBodyReaction(i, dynamic_cast(*R)); - }); - - reg_addRxn("falloff", - [&](shared_ptr R) { - addFalloffReaction(dynamic_cast(*R)); - }); - reg_modRxn("falloff", - [&](size_t i, shared_ptr R) { - modifyFalloffReaction(i, dynamic_cast(*R)); - }); - - reg_addRxn("chemically-activated", - [&](shared_ptr R) { - addFalloffReaction(dynamic_cast(*R)); - }); - reg_modRxn("chemically-activated", - [&](size_t i, shared_ptr R) { - modifyFalloffReaction(i, dynamic_cast(*R)); - }); - - reg_addRxn("pressure-dependent-Arrhenius", - [&](shared_ptr R) { - addPlogReaction(dynamic_cast(*R)); - }); - - reg_modRxn("pressure-dependent-Arrhenius", - [&](size_t i, shared_ptr R) { - modifyPlogReaction(i, dynamic_cast(*R)); - }); - reg_addRxn("Chebyshev", - [&](shared_ptr R) { - addChebyshevReaction(dynamic_cast(*R)); - }); - reg_modRxn("Chebyshev", - [&](size_t i, shared_ptr R) { - modifyChebyshevReaction(i, dynamic_cast(*R)); - }); } void GasKinetics::update_rates_T() @@ -294,15 +241,24 @@ bool GasKinetics::addReaction(shared_ptr r) return false; } - try { - if (r->rxnRate()) { - addRxnRate(r->rxnRate()); - RxnRate::updateTemperature(thermo().temperature()); - RxnRate::updatePressure(thermo().pressure()); - } else { - m_addRxn[r->type()](r); - } - } catch (std::out_of_range&) { + if (r->rxnRate()) { + // new generic reaction type handler + m_rxn_rates[nReactions()-1] = r->rxnRate(); + RxnRate::updateTemperature(thermo().temperature()); + RxnRate::updatePressure(thermo().pressure()); + } else if (r->type() == "elementary") { + addElementaryReaction(dynamic_cast(*r)); + } else if (r->type() == "three-body") { + addThreeBodyReaction(dynamic_cast(*r)); + } else if (r->type() == "falloff") { + addFalloffReaction(dynamic_cast(*r)); + } else if (r->type() == "chemically-activated") { + addFalloffReaction(dynamic_cast(*r)); + } else if (r->type() == "pressure-dependent-Arrhenius") { + addPlogReaction(dynamic_cast(*r)); + } else if (r->type() == "Chebyshev") { + addChebyshevReaction(dynamic_cast(*r)); + } else { throw CanteraError("GasKinetics::addReaction", "Unknown reaction type specified: '{}'", r->type()); } @@ -373,23 +329,29 @@ void GasKinetics::addChebyshevReaction(ChebyshevReaction& r) m_cheb_rates.install(nReactions()-1, r.rate); } -void GasKinetics::addRxnRate(shared_ptr rate) -{ - m_rxn_rates[nReactions()-1] = rate; -} - void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) { // operations common to all reaction types BulkKinetics::modifyReaction(i, rNew); - try { - if (rNew->rxnRate()) { - modifyRxnRate(i, rNew->rxnRate()); - } else { - m_modRxn[rNew->type()](i, rNew); - } - } catch (std::out_of_range&) { + if (rNew->rxnRate()) { + // new generic reaction type handler + modifyRxnRate(i, rNew->rxnRate()); + RxnRate::updateTemperature(thermo().temperature()); + RxnRate::updatePressure(thermo().pressure()); + } else if (rNew->type() == "elementary") { + modifyElementaryReaction(i, dynamic_cast(*rNew)); + } else if (rNew->type() == "three-body") { + modifyThreeBodyReaction(i, dynamic_cast(*rNew)); + } else if (rNew->type() == "falloff") { + modifyFalloffReaction(i, dynamic_cast(*rNew)); + } else if (rNew->type() == "chemically-activated") { + modifyFalloffReaction(i, dynamic_cast(*rNew)); + } else if (rNew->type() == "pressure-dependent-Arrhenius") { + modifyPlogReaction(i, dynamic_cast(*rNew)); + } else if (rNew->type() == "Chebyshev") { + modifyChebyshevReaction(i, dynamic_cast(*rNew)); + } else { throw CanteraError("GasKinetics::modifyReaction", "Unknown reaction type specified: '{}'", rNew->type()); } From 54e7e65003a10b17b3db5988925fb442ce9ce41a Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 6 Mar 2021 07:46:49 -0600 Subject: [PATCH 46/67] [Kinetics] make RxnRate thread-safe --- include/cantera/kinetics/RxnRates.h | 59 ++------------------------ interfaces/cython/cantera/_cantera.pxd | 6 +-- interfaces/cython/cantera/reaction.pyx | 23 ++-------- src/kinetics/GasKinetics.cpp | 12 +----- src/kinetics/RxnRates.cpp | 11 +---- 5 files changed, 13 insertions(+), 98 deletions(-) diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 6afe053ee5..8af68b9c37 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -445,59 +445,8 @@ class RxnRate //! Identifier of reaction type virtual std::string type() const = 0; - //! Get temperature - static double temperature() { - return m_temperature; - } - - //! Update temperature - static void updateTemperature(double T) { - if (T <= 0.) { - throw CanteraError("RxnRate::updateTemperature", - "Temperature has to be positive."); - } - m_temperature = T; - m_logT = std::log(T); - m_recipT = 1./T; - } - - //! Get pressure - static double pressure() { - return m_pressure; - } - - //! Update pressure - static void updatePressure(double P) { - if (P <= 0.) { - throw CanteraError("RxnRate::updatePressure", - "Pressure has to be positive."); - } - m_pressure = P; - m_logP = log(P); - m_log10P = log10(P); - } - //! Evaluate reaction rate - virtual double eval() const = 0; - -protected: - //! Temperature used for reaction rate evaluation - static double m_temperature; - - //! Logarithm of temperature used for reaction rate evaluation - static double m_logT; - - //! Inverse temperature used for reaction rate evaluation - static double m_recipT; - - //! Pressure used for reaction rate evaluation - static double m_pressure; - - //! Logarithm of pressure used for reaction rate evaluation - static double m_logP; - - //! 10-base logarithm of pressure used for reaction rate evaluation - static double m_log10P; + virtual double eval_T(double T, double logT, double recipT) const = 0; }; @@ -526,7 +475,7 @@ class CustomPyRate : public RxnRate */ void setRateFunction(shared_ptr f); - virtual double eval() const; + virtual double eval_T(double T, double logT, double recipT) const; protected: shared_ptr m_ratefunc; @@ -552,8 +501,8 @@ class ArrheniusRate : public RxnRate, public Arrhenius return "ArrheniusRate"; } - virtual double eval() const { - return updateRC(m_logT, m_recipT); + virtual double eval_T(double T, double logT, double recipT) const { + return updateRC(logT, recipT); } }; diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 4a356df0bf..8df7c6039f 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -315,11 +315,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxRxnRate "Cantera::RxnRate": CxxRxnRate() string type() - double temperature() - void updateTemperature(double) except +translate_exception - double pressure() - void updatePressure(double) except +translate_exception - double eval() except +translate_exception + double eval_T(double, double, double) except +translate_exception cdef cppclass CxxCustomPyRate "Cantera::CustomPyRate" (CxxRxnRate): CxxCustomPyRate() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 7de17b4502..09d89747e8 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -408,25 +408,10 @@ cdef class _RxnRate: def __repr__(self): return "<{}>".format(pystr(self.base.type())) - def __call__(self, temperature=None): - if temperature is not None: - self.temperature = temperature - - return self.base.eval() - - property temperature: - """ Get/Set temperature used for all reaction rate evaluations""" - def __get__(self): - return self.base.temperature() - def __set__(self, float temperature): - self.base.updateTemperature(temperature) - - property pressure: - """ Get/Set pressure used for all reaction rate evaluations""" - def __get__(self): - return self.base.pressure() - def __set__(self, float pressure): - self.base.updatePressure(pressure) + def __call__(self, double temperature): + cdef double logT = np.log(temperature) + cdef double recipT = 1/temperature + return self.base.eval_T(temperature, logT, recipT) cdef class CustomRate(_RxnRate): diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 9eed64e424..ee29f69cb1 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -27,6 +27,7 @@ void GasKinetics::update_rates_T() double P = thermo().pressure(); m_logStandConc = log(thermo().standardConcentration()); double logT = log(T); + double recipT = 1./T; if (T != m_temp) { if (!m_rfn.empty()) { @@ -45,14 +46,9 @@ void GasKinetics::update_rates_T() } if (T != m_temp || P != m_pres) { - if (!m_rxn_rates.empty()) { - RxnRate::updateTemperature(T); - RxnRate::updatePressure(P); - } - for (auto& rate : m_rxn_rates) { // generic reaction rates - m_rfn.data()[rate.first] = rate.second->eval(); + m_rfn.data()[rate.first] = rate.second->eval_T(T, logT, recipT); } if (m_plog_rates.nReactions()) { @@ -244,8 +240,6 @@ bool GasKinetics::addReaction(shared_ptr r) if (r->rxnRate()) { // new generic reaction type handler m_rxn_rates[nReactions()-1] = r->rxnRate(); - RxnRate::updateTemperature(thermo().temperature()); - RxnRate::updatePressure(thermo().pressure()); } else if (r->type() == "elementary") { addElementaryReaction(dynamic_cast(*r)); } else if (r->type() == "three-body") { @@ -337,8 +331,6 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) if (rNew->rxnRate()) { // new generic reaction type handler modifyRxnRate(i, rNew->rxnRate()); - RxnRate::updateTemperature(thermo().temperature()); - RxnRate::updatePressure(thermo().pressure()); } else if (rNew->type() == "elementary") { modifyElementaryReaction(i, dynamic_cast(*rNew)); } else if (rNew->type() == "three-body") { diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index c78c52dcc4..76badf824c 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -159,22 +159,15 @@ ChebyshevRate::ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, } } -double RxnRate::m_temperature = 1.; -double RxnRate::m_logT = 0.; -double RxnRate::m_recipT = 1.; -double RxnRate::m_pressure = 1.; -double RxnRate::m_logP = 0.; -double RxnRate::m_log10P = 0.; - CustomPyRate::CustomPyRate() : m_ratefunc(0) {} void CustomPyRate::setRateFunction(shared_ptr f) { m_ratefunc = f; } -double CustomPyRate::eval() const { +double CustomPyRate::eval_T(double T, double logT, double recipT) const { if (m_ratefunc) { - return m_ratefunc->eval(m_temperature); + return m_ratefunc->eval(T); } return 0.; } From 6e512313939001ffa713c6a4cb03b7898c53b2a7 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 6 Mar 2021 09:06:52 -0600 Subject: [PATCH 47/67] [Kinetics] create State data container for RxnRate evaluations --- include/cantera/kinetics/RxnRates.h | 64 ++++++++++++++++++++++++-- interfaces/cython/cantera/_cantera.pxd | 3 +- interfaces/cython/cantera/reaction.pyx | 9 ++-- src/kinetics/GasKinetics.cpp | 7 +-- src/kinetics/RxnRates.cpp | 4 +- 5 files changed, 73 insertions(+), 14 deletions(-) diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 8af68b9c37..1496e88780 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -431,6 +431,47 @@ class ChebyshevRate }; +//! Data container holding state information +/** + * The data container `State` holds information passed to RxnRate objects. Here, a + * pre-calculation of commonly used information (inverse, log, etc.) avoids + * computational overhead. + */ +struct State { + State() + : temperature(1.), logT(0.), recipT(0.) + , pressure(1.), logP(0.), log10P(0.) { + } + + //! Constructor based on temperature *T* + State(double T) : temperature(T), pressure(1.), logP(0.), log10P(0.) { + logT = std::log(T); + recipT = 1./T; + } + + //! Constructor based on temperature *T* and pressure *P* + State(double T, double P) : temperature(T), pressure(P) { + logT = std::log(T); + recipT = 1./T; + logP = std::log(P); + log10P = std::log10(P); + } + + //! Constructor based on temperature *T*, pressure *P* and + //! concentration *conc* + State(double T, double P, const vector_fp& conc) : State(T, P) { + throw CanteraError("State::State", "Not implemented"); + } + double temperature; + double logT; + double recipT; + double pressure; + double logP; + double log10P; + vector_fp conc; +}; + + //! Abstract base class for reaction rate definitions /** * @warning This class is an experimental part of the %Cantera API and @@ -446,7 +487,22 @@ class RxnRate virtual std::string type() const = 0; //! Evaluate reaction rate - virtual double eval_T(double T, double logT, double recipT) const = 0; + virtual double eval(const State& state) const = 0; + + //! Evaluate reaction rate based on temperature + virtual double evalT(double T) const { + return eval(State(T)); + } + + //! Evaluate reaction rate based on temperature and pressure + virtual double evalTP(double T, double P) const { + return eval(State(T, P)); + } + + //! Evaluate reaction rate based on temperature, pressure and concentrations + virtual double evalTPC(double T, double P, const vector_fp& conc) const { + return eval(State(T, P, conc)); + } }; @@ -475,7 +531,7 @@ class CustomPyRate : public RxnRate */ void setRateFunction(shared_ptr f); - virtual double eval_T(double T, double logT, double recipT) const; + virtual double eval(const State& state) const; protected: shared_ptr m_ratefunc; @@ -501,8 +557,8 @@ class ArrheniusRate : public RxnRate, public Arrhenius return "ArrheniusRate"; } - virtual double eval_T(double T, double logT, double recipT) const { - return updateRC(logT, recipT); + virtual double eval(const State& state) const { + return updateRC(state.logT, state.recipT); } }; diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 8df7c6039f..b454c12442 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -315,7 +315,8 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxRxnRate "Cantera::RxnRate": CxxRxnRate() string type() - double eval_T(double, double, double) except +translate_exception + double evalT(double) except +translate_exception + double evalTP(double, double) except +translate_exception cdef cppclass CxxCustomPyRate "Cantera::CustomPyRate" (CxxRxnRate): CxxCustomPyRate() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 09d89747e8..497ca369c8 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -408,10 +408,11 @@ cdef class _RxnRate: def __repr__(self): return "<{}>".format(pystr(self.base.type())) - def __call__(self, double temperature): - cdef double logT = np.log(temperature) - cdef double recipT = 1/temperature - return self.base.eval_T(temperature, logT, recipT) + def __call__(self, double temperature, pressure=None): + if pressure: + return self.base.evalTP(temperature, pressure) + else: + return self.base.evalT(temperature) cdef class CustomRate(_RxnRate): diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index ee29f69cb1..419d226033 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -26,8 +26,8 @@ void GasKinetics::update_rates_T() double T = thermo().temperature(); double P = thermo().pressure(); m_logStandConc = log(thermo().standardConcentration()); - double logT = log(T); - double recipT = 1./T; + State state(T, P); + double logT = state.logT; if (T != m_temp) { if (!m_rfn.empty()) { @@ -46,9 +46,10 @@ void GasKinetics::update_rates_T() } if (T != m_temp || P != m_pres) { + for (auto& rate : m_rxn_rates) { // generic reaction rates - m_rfn.data()[rate.first] = rate.second->eval_T(T, logT, recipT); + m_rfn.data()[rate.first] = rate.second->eval(state); } if (m_plog_rates.nReactions()) { diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 76badf824c..3c0f502d3c 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -165,9 +165,9 @@ void CustomPyRate::setRateFunction(shared_ptr f) { m_ratefunc = f; } -double CustomPyRate::eval_T(double T, double logT, double recipT) const { +double CustomPyRate::eval(const State& state) const { if (m_ratefunc) { - return m_ratefunc->eval(T); + return m_ratefunc->eval(state.temperature); } return 0.; } From 684ca497b4c9963fb225f719a2c1e03b0bef89de Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 7 Mar 2021 09:35:01 -0600 Subject: [PATCH 48/67] [Kinetics] mark derived RxnRate classes as 'final' --- include/cantera/kinetics/RxnRates.h | 20 ++++++++++++++------ src/kinetics/GasKinetics.cpp | 6 ++++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 1496e88780..46fc66d750 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -486,6 +486,12 @@ class RxnRate //! Identifier of reaction type virtual std::string type() const = 0; + //! Index or reaction within kinetics + size_t index() { return m_index; } + + //! Index or reaction within kinetics + void setIndex(size_t index) { m_index = index; } + //! Evaluate reaction rate virtual double eval(const State& state) const = 0; @@ -503,6 +509,8 @@ class RxnRate virtual double evalTPC(double T, double P, const vector_fp& conc) const { return eval(State(T, P, conc)); } +protected: + size_t m_index; //!< reaction index }; @@ -514,13 +522,13 @@ class RxnRate * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. */ -class CustomPyRate : public RxnRate +class CustomPyRate final : public RxnRate { public: //! Constructor. CustomPyRate(); - virtual std::string type() const { + virtual std::string type() const override { return "custom-PythonRate"; } @@ -531,7 +539,7 @@ class CustomPyRate : public RxnRate */ void setRateFunction(shared_ptr f); - virtual double eval(const State& state) const; + virtual double eval(const State& state) const override; protected: shared_ptr m_ratefunc; @@ -545,7 +553,7 @@ class CustomPyRate : public RxnRate * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. */ -class ArrheniusRate : public RxnRate, public Arrhenius +class ArrheniusRate final : public RxnRate, public Arrhenius { public: //! Constructor. @@ -553,11 +561,11 @@ class ArrheniusRate : public RxnRate, public Arrhenius ArrheniusRate(double A, double b, double E); - virtual std::string type() const { + virtual std::string type() const override { return "ArrheniusRate"; } - virtual double eval(const State& state) const { + virtual double eval(const State& state) const override { return updateRC(state.logT, state.recipT); } }; diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 419d226033..04446f3554 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -238,9 +238,11 @@ bool GasKinetics::addReaction(shared_ptr r) return false; } - if (r->rxnRate()) { + shared_ptr rate=r->rxnRate(); + if (rate) { // new generic reaction type handler - m_rxn_rates[nReactions()-1] = r->rxnRate(); + rate->setIndex(nReactions()-1); + m_rxn_rates[nReactions()-1] = rate; } else if (r->type() == "elementary") { addElementaryReaction(dynamic_cast(*r)); } else if (r->type() == "three-body") { From d393bbaf4ce79628dc3b52827ebe03b54f58f060 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 7 Mar 2021 10:00:11 -0600 Subject: [PATCH 49/67] [Kinetics] switch from map to vector of RxnRate pointers --- include/cantera/kinetics/GasKinetics.h | 3 ++- src/kinetics/GasKinetics.cpp | 36 ++++++++++++++++++-------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index 9cbc0381af..c4705cc026 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -90,7 +90,8 @@ class GasKinetics : public BulkKinetics Rate1 m_cheb_rates; //! Map of generic reaction rate expressions - std::map> m_rxn_rates; + std::vector> m_rxn_rates; + std::vector> m_arrhenius_rates; //! @name Reaction rate data //!@{ diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 04446f3554..d6a95768c0 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -47,9 +47,14 @@ void GasKinetics::update_rates_T() if (T != m_temp || P != m_pres) { + for (auto& rate : m_arrhenius_rates) { + // generic reaction rates + m_rfn.data()[rate->index()] = rate->eval(state); + } + for (auto& rate : m_rxn_rates) { // generic reaction rates - m_rfn.data()[rate.first] = rate.second->eval(state); + m_rfn.data()[rate->index()] = rate->eval(state); } if (m_plog_rates.nReactions()) { @@ -242,7 +247,7 @@ bool GasKinetics::addReaction(shared_ptr r) if (rate) { // new generic reaction type handler rate->setIndex(nReactions()-1); - m_rxn_rates[nReactions()-1] = rate; + m_rxn_rates.push_back(rate); } else if (r->type() == "elementary") { addElementaryReaction(dynamic_cast(*r)); } else if (r->type() == "three-body") { @@ -380,17 +385,26 @@ void GasKinetics::modifyChebyshevReaction(size_t i, ChebyshevReaction& r) m_cheb_rates.replace(i, r.rate); } -void GasKinetics::modifyRxnRate(size_t i, shared_ptr rate) +void GasKinetics::modifyRxnRate(size_t i, shared_ptr newRate) { - if (m_rxn_rates.find(i) != m_rxn_rates.end()) { - if (m_rxn_rates[i]->type() != rate->type()) { - throw CanteraError("GasKinetics::modifyReaction", - "Attempting to replace '{}' with '{}'.", - m_rxn_rates[i]->type(), rate->type()); + bool found = false; + size_t j = 0; + + while ((!found) && (j < m_rxn_rates.size())) { + if (m_rxn_rates[j]->index() == i) { + if (newRate->type() != m_rxn_rates[j]->type()) { + throw CanteraError("GasKinetics::modifyRxnRate", + "Attempting to replace '{}' with '{}'.", + m_rxn_rates[j]->type(), newRate->type()); + } + m_rxn_rates[j] = newRate; + found = true; } - m_rxn_rates[i] = rate; - } else { - throw CanteraError("GasKinetics::modifyReaction", + j++; + } + + if (!found) { + throw CanteraError("GasKinetics::modifyRxnRate", "Index {} does not exist.", i); } } From 7188f8191ca1d37df684875773c12e32f39b3e0c Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 7 Mar 2021 10:30:14 -0600 Subject: [PATCH 50/67] [Kinetics] store specialized reaction rate types separately --- include/cantera/kinetics/GasKinetics.h | 8 +++- src/kinetics/GasKinetics.cpp | 57 +++++++++++++++++--------- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index c4705cc026..ca4f1b1b74 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -89,10 +89,15 @@ class GasKinetics : public BulkKinetics Rate1 m_plog_rates; Rate1 m_cheb_rates; - //! Map of generic reaction rate expressions + //! Vector of generic reaction rates std::vector> m_rxn_rates; + + //! Vector of specialized arrhenius reaction rates std::vector> m_arrhenius_rates; + std::map m_rxn_indices; + std::map m_arrhenius_indices; + //! @name Reaction rate data //!@{ doublereal m_logp_ref; @@ -121,6 +126,7 @@ class GasKinetics : public BulkKinetics void modifyChebyshevReaction(size_t i, ChebyshevReaction& r); void modifyRxnRate(size_t i, shared_ptr rate); + void modifyArrheniusRate(size_t i, shared_ptr rate); //! Update the equilibrium constants in molar units. void updateKc(); diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index d6a95768c0..e858c732aa 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -245,9 +245,16 @@ bool GasKinetics::addReaction(shared_ptr r) shared_ptr rate=r->rxnRate(); if (rate) { + size_t nRxn = nReactions() - 1; // new generic reaction type handler - rate->setIndex(nReactions()-1); - m_rxn_rates.push_back(rate); + rate->setIndex(nRxn); + if (rate->type() == "ArrheniusRate") { + m_arrhenius_indices[nRxn] = m_arrhenius_rates.size(); + m_arrhenius_rates.push_back(std::dynamic_pointer_cast(rate)); + } else { + m_rxn_indices[nRxn] = m_rxn_rates.size(); + m_rxn_rates.push_back(rate); + } } else if (r->type() == "elementary") { addElementaryReaction(dynamic_cast(*r)); } else if (r->type() == "three-body") { @@ -336,9 +343,14 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) // operations common to all reaction types BulkKinetics::modifyReaction(i, rNew); - if (rNew->rxnRate()) { + shared_ptr rate=rNew->rxnRate(); + if (rate) { // new generic reaction type handler - modifyRxnRate(i, rNew->rxnRate()); + if (rate->type() == "ArrheniusRate") { + modifyArrheniusRate(i, std::dynamic_pointer_cast(rate)); + } else { + modifyRxnRate(i, rate); + } } else if (rNew->type() == "elementary") { modifyElementaryReaction(i, dynamic_cast(*rNew)); } else if (rNew->type() == "three-body") { @@ -387,24 +399,31 @@ void GasKinetics::modifyChebyshevReaction(size_t i, ChebyshevReaction& r) void GasKinetics::modifyRxnRate(size_t i, shared_ptr newRate) { - bool found = false; - size_t j = 0; - - while ((!found) && (j < m_rxn_rates.size())) { - if (m_rxn_rates[j]->index() == i) { - if (newRate->type() != m_rxn_rates[j]->type()) { - throw CanteraError("GasKinetics::modifyRxnRate", - "Attempting to replace '{}' with '{}'.", - m_rxn_rates[j]->type(), newRate->type()); - } - m_rxn_rates[j] = newRate; - found = true; + if (m_rxn_indices.find(i) != m_rxn_indices.end()) { + + size_t j = m_rxn_indices[i]; + if (newRate->type() != m_rxn_rates[j]->type()) { + throw CanteraError("GasKinetics::modifyRxnRate", + "Attempting to replace '{}' with '{}'.", + m_rxn_rates[j]->type(), newRate->type()); } - j++; + newRate->setIndex(m_rxn_rates[j]->index()); + m_rxn_rates[j] = newRate; + } else { + throw CanteraError("GasKinetics::modifyRxnRate", + "Index {} does not exist.", i); } +} - if (!found) { - throw CanteraError("GasKinetics::modifyRxnRate", +void GasKinetics::modifyArrheniusRate(size_t i, + shared_ptr newRate) +{ + if (m_arrhenius_indices.find(i) != m_arrhenius_indices.end()) { + size_t j = m_arrhenius_indices[i]; + newRate->setIndex(m_arrhenius_rates[j]->index()); + m_arrhenius_rates[j] = newRate; + } else { + throw CanteraError("GasKinetics::modifyArrheniusRate", "Index {} does not exist.", i); } } From 77bda949e7e75d117c4034c0ab5c34477945e2eb Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 6 Mar 2021 11:01:18 -0600 Subject: [PATCH 51/67] [examples] switch custom_reactions.py to gri30.yaml --- .../examples/kinetics/custom_reactions.py | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/interfaces/cython/cantera/examples/kinetics/custom_reactions.py b/interfaces/cython/cantera/examples/kinetics/custom_reactions.py index bd506e4317..267d746569 100644 --- a/interfaces/cython/cantera/examples/kinetics/custom_reactions.py +++ b/interfaces/cython/cantera/examples/kinetics/custom_reactions.py @@ -12,7 +12,9 @@ import cantera as ct -gas0 = ct.Solution('h2o2.yaml') +mech = 'gri30.yaml' +fuel = 'CH4' +gas0 = ct.Solution(mech) species = gas0.species() reactions = gas0.reactions() @@ -50,8 +52,8 @@ def ignition(gas): # set up reactor - gas.TP = 900, 5 * ct.one_atm - gas.set_equivalence_ratio(0.4, 'H2', 'O2:1.0, AR:4.0') + gas.TP = 1000., 5 * ct.one_atm + gas.set_equivalence_ratio(0.8, fuel, 'O2:1.0, N2:3.773') r = ct.IdealGasReactor(gas) net = ct.ReactorNet([r]) net.rtol_sensitivity = 2.e-5 @@ -66,22 +68,28 @@ def ignition(gas): # output results repeat = 100 -print('Average time of {} simulation runs:'.format(repeat)) +print("Average time of {} simulation runs for '{}' " + "({})".format(repeat, mech, fuel)) sim0 = 0 for i in range(repeat): sim0 += ignition(gas0) +sim0 /= repeat print('- Original mechanism: ' - '{0:.2f} ms (T_final={1:.2f})'.format(sim0 / repeat, gas0.T)) + '{0:.2f} ms (T_final={1:.2f})'.format(sim0, gas0.T)) sim1 = 0 for i in range(repeat): sim1 += ignition(gas1) +sim1 /= repeat print('- One Python reaction: ' - '{0:.2f} ms (T_final={1:.2f})'.format(sim1 / repeat, gas1.T)) + '{0:.2f} ms (T_final={1:.2f}) ... ' + '{2:+.2f}%'.format(sim1, gas1.T, 100 * sim1 / sim0 - 100)) sim2 = 0 for i in range(repeat): sim2 += ignition(gas2) +sim2 /= repeat print('- Alternative reactions: ' - '{0:.2f} ms (T_final={1:.2f})'.format(sim2 / repeat, gas2.T)) + '{0:.2f} ms (T_final={1:.2f}) ... ' + '{2:+.2f}%'.format(sim2, gas2.T, 100 * sim2 / sim0 - 100)) From ac8fc76e25641f8494888d4e5e9d0f36fbd959c1 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 7 Mar 2021 11:22:40 -0600 Subject: [PATCH 52/67] [Kinetics] store specialized RxnRate objects without pointers --- include/cantera/kinetics/GasKinetics.h | 2 +- src/kinetics/GasKinetics.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index ca4f1b1b74..88b158157e 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -93,7 +93,7 @@ class GasKinetics : public BulkKinetics std::vector> m_rxn_rates; //! Vector of specialized arrhenius reaction rates - std::vector> m_arrhenius_rates; + std::vector m_arrhenius_rates; std::map m_rxn_indices; std::map m_arrhenius_indices; diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index e858c732aa..ccde3170d3 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -49,7 +49,7 @@ void GasKinetics::update_rates_T() for (auto& rate : m_arrhenius_rates) { // generic reaction rates - m_rfn.data()[rate->index()] = rate->eval(state); + m_rfn.data()[rate.index()] = rate.eval(state); } for (auto& rate : m_rxn_rates) { @@ -250,7 +250,7 @@ bool GasKinetics::addReaction(shared_ptr r) rate->setIndex(nRxn); if (rate->type() == "ArrheniusRate") { m_arrhenius_indices[nRxn] = m_arrhenius_rates.size(); - m_arrhenius_rates.push_back(std::dynamic_pointer_cast(rate)); + m_arrhenius_rates.push_back(*std::dynamic_pointer_cast(rate)); } else { m_rxn_indices[nRxn] = m_rxn_rates.size(); m_rxn_rates.push_back(rate); @@ -420,8 +420,8 @@ void GasKinetics::modifyArrheniusRate(size_t i, { if (m_arrhenius_indices.find(i) != m_arrhenius_indices.end()) { size_t j = m_arrhenius_indices[i]; - newRate->setIndex(m_arrhenius_rates[j]->index()); - m_arrhenius_rates[j] = newRate; + newRate->setIndex(m_arrhenius_rates[j].index()); + m_arrhenius_rates[j] = *newRate; } else { throw CanteraError("GasKinetics::modifyArrheniusRate", "Index {} does not exist.", i); From 8c0722636b59995685e2e9347e52e926753f8474 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 7 Mar 2021 14:17:37 -0600 Subject: [PATCH 53/67] [kinetics] add signatures for analytical derivatives to RxnRates --- include/cantera/kinetics/RxnRates.h | 49 +++++++++++++++++++++++--- interfaces/cython/cantera/_cantera.pxd | 6 ++-- interfaces/cython/cantera/reaction.pyx | 10 ++++-- 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 46fc66d750..bf0fe41aa9 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -460,8 +460,9 @@ struct State { //! Constructor based on temperature *T*, pressure *P* and //! concentration *conc* State(double T, double P, const vector_fp& conc) : State(T, P) { - throw CanteraError("State::State", "Not implemented"); + throw CanteraError("State::State", "Not implemented (yet)"); } + double temperature; double logT; double recipT; @@ -496,19 +497,50 @@ class RxnRate virtual double eval(const State& state) const = 0; //! Evaluate reaction rate based on temperature - virtual double evalT(double T) const { + double eval(double T) const { return eval(State(T)); } //! Evaluate reaction rate based on temperature and pressure - virtual double evalTP(double T, double P) const { + double eval(double T, double P) const { return eval(State(T, P)); } //! Evaluate reaction rate based on temperature, pressure and concentrations - virtual double evalTPC(double T, double P, const vector_fp& conc) const { + double eval(double T, double P, const vector_fp& conc) const { return eval(State(T, P, conc)); } + + // [...] other overrides are not created (yet) + + //! Evaluate reaction rate derivative (with respect to temperature) + virtual double ddT(const State& state) const { + throw CanteraError("RxnRate::ddT", + "Not (yet) implemented by derived RxnRate object."); + } + + //! Evaluate reaction rate based on temperature + double ddT(double T) const { + return ddT(State(T)); + } + + //! Evaluate reaction rate based on temperature and pressure + double ddT(double T, double P) const { + return ddT(State(T, P)); + } + + // [...] other overrides are not created (yet) + + //! Evaluate reaction rate derivative (with respect to pressure) + virtual double ddP(const State& state) const { + throw CanteraError("RxnRate::ddP", + "Not (yet) implemented by derived RxnRate object."); + } + + // [...] other signatures are not created (yet) + + // signatures of a ddC method tbd + protected: size_t m_index; //!< reaction index }; @@ -568,6 +600,15 @@ class ArrheniusRate final : public RxnRate, public Arrhenius virtual double eval(const State& state) const override { return updateRC(state.logT, state.recipT); } + + virtual double ddT(const State& state) const override { + return updateRC(state.logT, state.recipT) * + (m_b + m_E * state.recipT) * state.recipT; + } + + virtual double ddP(const State& state) const override { + return 0.; + } }; } diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index b454c12442..2e4c0dbc39 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -315,8 +315,10 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxRxnRate "Cantera::RxnRate": CxxRxnRate() string type() - double evalT(double) except +translate_exception - double evalTP(double, double) except +translate_exception + double eval(double) except +translate_exception + double eval(double, double) except +translate_exception + double ddT(double) except +translate_exception + double ddT(double, double) except +translate_exception cdef cppclass CxxCustomPyRate "Cantera::CustomPyRate" (CxxRxnRate): CxxCustomPyRate() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 497ca369c8..f5f7c424fe 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -410,9 +410,15 @@ cdef class _RxnRate: def __call__(self, double temperature, pressure=None): if pressure: - return self.base.evalTP(temperature, pressure) + return self.base.eval(temperature, pressure) else: - return self.base.evalT(temperature) + return self.base.eval(temperature) + + def ddT(self, double temperature, pressure=None): + if pressure: + return self.base.ddT(temperature, pressure) + else: + return self.base.ddT(temperature) cdef class CustomRate(_RxnRate): From 7622fda2832d8758772ae99fb412391ffe641b52 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 13 Mar 2021 08:22:54 -0600 Subject: [PATCH 54/67] [Kinetics] address 'simple' review comments for new reaction framework * streamline names: - RxnRate -> ReactionRate - CustomPyRate -> CustomFunc1Rate - CustomPyReaction -> CustomFunc1Reaction * label some (potentially temporary) methods as @internal * minor fixes in Python interface --- include/cantera/kinetics/GasKinetics.h | 6 ++- include/cantera/kinetics/Kinetics.h | 1 + include/cantera/kinetics/Reaction.h | 31 +++++++++------ include/cantera/kinetics/RxnRates.h | 30 +++++++------- interfaces/cython/cantera/_cantera.pxd | 32 +++++++-------- interfaces/cython/cantera/reaction.pyx | 39 +++++++------------ .../cython/cantera/test/test_reaction.py | 2 +- src/kinetics/GasKinetics.cpp | 12 +++--- src/kinetics/KineticsFactory.cpp | 1 - src/kinetics/Reaction.cpp | 16 ++++---- src/kinetics/ReactionFactory.cpp | 17 ++++---- src/kinetics/RxnRates.cpp | 6 +-- 12 files changed, 95 insertions(+), 98 deletions(-) diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index 88b158157e..8d12fc14da 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -90,7 +90,7 @@ class GasKinetics : public BulkKinetics Rate1 m_cheb_rates; //! Vector of generic reaction rates - std::vector> m_rxn_rates; + std::vector> m_rxn_rates; //! Vector of specialized arrhenius reaction rates std::vector m_arrhenius_rates; @@ -125,7 +125,9 @@ class GasKinetics : public BulkKinetics void modifyPlogReaction(size_t i, PlogReaction& r); void modifyChebyshevReaction(size_t i, ChebyshevReaction& r); - void modifyRxnRate(size_t i, shared_ptr rate); + //! @internal May be changed without notice in future versions + void modifyReactionRate(size_t i, shared_ptr rate); + //! @internal May be changed without notice in future versions void modifyArrheniusRate(size_t i, shared_ptr rate); //! Update the equilibrium constants in molar units. diff --git a/include/cantera/kinetics/Kinetics.h b/include/cantera/kinetics/Kinetics.h index ba7177b321..f448243f93 100644 --- a/include/cantera/kinetics/Kinetics.h +++ b/include/cantera/kinetics/Kinetics.h @@ -13,6 +13,7 @@ #include "StoichManager.h" #include "cantera/base/ValueCache.h" +#include "cantera/kinetics/ReactionFactory.h" namespace Cantera { diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 9826a14edd..a0e65afb81 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -23,7 +23,7 @@ class XML_Node; class Reaction { public: - explicit Reaction(); + Reaction(); Reaction(const Composition& reactants, const Composition& products); @@ -56,17 +56,17 @@ class Reaction } //! Set validity flag of reaction - void set_valid(bool valid) { + void setValid(bool valid) { m_valid = valid; } //! Get reaction rate pointer - shared_ptr rxnRate() { + shared_ptr reactionRate() { return m_rate; } //! Set reaction rate pointer - void setRxnRate(shared_ptr rate) { + void setReactionRate(shared_ptr rate) { m_rate = rate; } @@ -114,7 +114,7 @@ class Reaction bool m_valid; //! Reaction rate used by generic reactions - shared_ptr m_rate; + shared_ptr m_rate; }; @@ -264,13 +264,13 @@ class ChebyshevReaction : public Reaction * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. */ -class CustomPyReaction : public Reaction +class CustomFunc1Reaction : public Reaction { public: - CustomPyReaction(); + CustomFunc1Reaction(); virtual std::string type() const { - return "custom-Python"; + return "custom-rate-function"; } }; @@ -278,7 +278,7 @@ class CustomPyReaction : public Reaction //! A reaction which follows mass-action kinetics with a modified Arrhenius //! reaction rate. /** - * Alternative elementary reaction based on RxnRate. + * Alternative elementary reaction based on ReactionRate. * * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. @@ -393,14 +393,17 @@ void parseReactionEquation(Reaction& R, const AnyValue& equation, // declarations of setup functions void setupElementaryReaction(ElementaryReaction&, const XML_Node&); +//! @internal May be changed without notice in future versions void setupElementaryReaction(ElementaryReaction&, const AnyMap&, const Kinetics&); void setupThreeBodyReaction(ThreeBodyReaction&, const XML_Node&); +//! @internal May be changed without notice in future versions void setupThreeBodyReaction(ThreeBodyReaction&, const AnyMap&, const Kinetics&); void setupFalloffReaction(FalloffReaction&, const XML_Node&); +//! @internal May be changed without notice in future versions void setupFalloffReaction(FalloffReaction&, const AnyMap&, const Kinetics&); @@ -408,24 +411,30 @@ void setupChemicallyActivatedReaction(ChemicallyActivatedReaction&, const XML_Node&); void setupPlogReaction(PlogReaction&, const XML_Node&); +//! @internal May be changed without notice in future versions void setupPlogReaction(PlogReaction&, const AnyMap&, const Kinetics&); void setupChebyshevReaction(ChebyshevReaction&, const XML_Node&); +//! @internal May be changed without notice in future versions void setupChebyshevReaction(ChebyshevReaction&, const AnyMap&, const Kinetics&); -void setupCustomPyReaction(CustomPyReaction&, const AnyMap&, - const Kinetics&); +//! @internal May be changed without notice in future versions +void setupCustomFunc1Reaction(CustomFunc1Reaction&, const AnyMap&, + const Kinetics&); +//! @internal May be changed without notice in future versions void setupTestReaction(TestReaction&, const AnyMap&, const Kinetics&); void setupInterfaceReaction(InterfaceReaction&, const XML_Node&); +//! @internal May be changed without notice in future versions void setupInterfaceReaction(InterfaceReaction&, const AnyMap&, const Kinetics&); void setupElectrochemicalReaction(ElectrochemicalReaction&, const XML_Node&); +//! @internal May be changed without notice in future versions void setupElectrochemicalReaction(ElectrochemicalReaction&, const AnyMap&, const Kinetics&); diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index bf0fe41aa9..4d60d12a36 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -433,7 +433,7 @@ class ChebyshevRate //! Data container holding state information /** - * The data container `State` holds information passed to RxnRate objects. Here, a + * The data container `State` holds information passed to ReactionRate objects. Here, a * pre-calculation of commonly used information (inverse, log, etc.) avoids * computational overhead. */ @@ -478,11 +478,11 @@ struct State { * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. */ -class RxnRate +class ReactionRate { public: //! Constructor - RxnRate() = default; + ReactionRate() = default; //! Identifier of reaction type virtual std::string type() const = 0; @@ -515,8 +515,8 @@ class RxnRate //! Evaluate reaction rate derivative (with respect to temperature) virtual double ddT(const State& state) const { - throw CanteraError("RxnRate::ddT", - "Not (yet) implemented by derived RxnRate object."); + throw CanteraError("ReactionRate::ddT", + "Not (yet) implemented by derived ReactionRate object."); } //! Evaluate reaction rate based on temperature @@ -533,8 +533,8 @@ class RxnRate //! Evaluate reaction rate derivative (with respect to pressure) virtual double ddP(const State& state) const { - throw CanteraError("RxnRate::ddP", - "Not (yet) implemented by derived RxnRate object."); + throw CanteraError("ReactionRate::ddP", + "Not (yet) implemented by derived ReactionRate object."); } // [...] other signatures are not created (yet) @@ -546,28 +546,28 @@ class RxnRate }; -//! Custom Python reaction rate depending only on temperature +//! Custom reaction rate depending only on temperature /** - * The rate expression is provided by external Python code taking a single + * The rate expression is provided by a Func1 object taking a single * argument (temperature) and does not use a formalized parameterization. * * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. */ -class CustomPyRate final : public RxnRate +class CustomFunc1Rate final : public ReactionRate { public: //! Constructor. - CustomPyRate(); + CustomFunc1Rate(); virtual std::string type() const override { - return "custom-PythonRate"; + return "custom-function"; } // set custom rate /** - * The Python function takes a single argument (temperature) and does - * not depend on parameters handled in C++. + * The call to the Func1 object takes a single argument (temperature) and + * does not depend on parameters handled in C++. */ void setRateFunction(shared_ptr f); @@ -585,7 +585,7 @@ class CustomPyRate final : public RxnRate * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. */ -class ArrheniusRate final : public RxnRate, public Arrhenius +class ArrheniusRate final : public ReactionRate, public Arrhenius { public: //! Constructor. diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 2e4c0dbc39..3f9a3e1acb 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -312,19 +312,19 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": double temperatureExponent() double activationEnergy_R() - cdef cppclass CxxRxnRate "Cantera::RxnRate": - CxxRxnRate() + cdef cppclass CxxReactionRate "Cantera::ReactionRate": + CxxReactionRate() string type() double eval(double) except +translate_exception double eval(double, double) except +translate_exception double ddT(double) except +translate_exception double ddT(double, double) except +translate_exception - cdef cppclass CxxCustomPyRate "Cantera::CustomPyRate" (CxxRxnRate): - CxxCustomPyRate() + cdef cppclass CxxCustomFunc1Rate "Cantera::CustomFunc1Rate" (CxxReactionRate): + CxxCustomFunc1Rate() void setRateFunction(shared_ptr[CxxFunc1]) except +translate_exception - cdef cppclass CxxArrheniusRate "Cantera::ArrheniusRate" (CxxRxnRate): + cdef cppclass CxxArrheniusRate "Cantera::ArrheniusRate" (CxxReactionRate): CxxArrheniusRate() CxxArrheniusRate(double, double, double) double preExponentialFactor() @@ -347,8 +347,8 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cbool duplicate cbool allow_nonreactant_orders cbool allow_negative_orders - shared_ptr[CxxRxnRate] rxnRate() - void setRxnRate(shared_ptr[CxxRxnRate]) + shared_ptr[CxxReactionRate] reactionRate() + void setReactionRate(shared_ptr[CxxReactionRate]) cdef cppclass CxxElementaryReaction "Cantera::ElementaryReaction" (CxxReaction): CxxElementaryReaction() @@ -412,8 +412,8 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxChebyshevReaction "Cantera::ChebyshevReaction" (CxxReaction): CxxChebyshevRate rate - cdef cppclass CxxCustomPyReaction "Cantera::CustomPyReaction" (CxxReaction): - CxxCustomPyReaction() + cdef cppclass CxxCustomFunc1Reaction "Cantera::CustomFunc1Reaction" (CxxReaction): + CxxCustomFunc1Reaction() cdef cppclass CxxTestReaction "Cantera::TestReaction" (CxxReaction): CxxTestReaction() @@ -1032,18 +1032,18 @@ cdef class ThermoPhase(_SolutionBase): cdef class InterfacePhase(ThermoPhase): cdef CxxSurfPhase* surf -cdef class _RxnRate: - cdef shared_ptr[CxxRxnRate] _base - cdef CxxRxnRate* base +cdef class _ReactionRate: + cdef shared_ptr[CxxReactionRate] _base + cdef CxxReactionRate* base -cdef class CustomRate(_RxnRate): - cdef CxxCustomPyRate* rate +cdef class CustomRate(_ReactionRate): + cdef CxxCustomFunc1Rate* rate cdef Func1 _rate_func -cdef class ArrheniusRate(_RxnRate): +cdef class ArrheniusRate(_ReactionRate): cdef CxxArrheniusRate* rate @staticmethod - cdef wrap(shared_ptr[CxxRxnRate]) + cdef wrap(shared_ptr[CxxReactionRate]) cdef class Reaction: cdef shared_ptr[CxxReaction] _reaction diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index f5f7c424fe..154f88e5af 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -68,17 +68,6 @@ cdef class Reaction: if products: self.products = products - @staticmethod - def _all_reaction_objects(): - """ - Retrieve all objects derived from Reaction - """ - def all_subclasses(cls): - return set(cls.__subclasses__()).union( - [s for c in cls.__subclasses__() for s in all_subclasses(c)]) - - return {getattr(c, 'reaction_type'): c for c in all_subclasses(Reaction)} - @staticmethod cdef wrap(shared_ptr[CxxReaction] reaction): """ @@ -381,7 +370,7 @@ cdef class Arrhenius: return self.rate.activationEnergy_R() * gas_constant def __repr__(self): - return ''.format( + return 'Arrhenius(A={:g}, b={:g}, E={:g})'.format( self.pre_exponential_factor, self.temperature_exponent, self.activation_energy) @@ -403,10 +392,10 @@ cdef copyArrhenius(CxxArrhenius* rate): return r -cdef class _RxnRate: +cdef class _ReactionRate: def __repr__(self): - return "<{}>".format(pystr(self.base.type())) + return "<{} at {:0x}>".format(pystr(self.base.type()), id(self)) def __call__(self, double temperature, pressure=None): if pressure: @@ -421,7 +410,7 @@ cdef class _RxnRate: return self.base.ddT(temperature) -cdef class CustomRate(_RxnRate): +cdef class CustomRate(_ReactionRate): r""" A custom rate coefficient which depends on temperature only. @@ -436,9 +425,9 @@ cdef class CustomRate(_RxnRate): def __cinit__(self, k=None, init=True): if init: - self._base.reset(new CxxCustomPyRate()) + self._base.reset(new CxxCustomFunc1Rate()) self.base = self._base.get() - self.rate = (self.base) + self.rate = (self.base) self.set_rate_function(k) def set_rate_function(self, k): @@ -460,7 +449,7 @@ cdef class CustomRate(_RxnRate): self.rate.setRateFunction(self._rate_func._func) -cdef class ArrheniusRate(_RxnRate): +cdef class ArrheniusRate(_ReactionRate): r""" A reaction rate coefficient which depends on temperature only and follows the modified Arrhenius form: @@ -483,9 +472,9 @@ cdef class ArrheniusRate(_RxnRate): self.rate = (self.base) @staticmethod - cdef wrap(shared_ptr[CxxRxnRate] rate): + cdef wrap(shared_ptr[CxxReactionRate] rate): """ - Wrap a C++ RxnRate object with a Python object. + Wrap a C++ ReactionRate object with a Python object. """ # wrap C++ reaction cdef ArrheniusRate arr @@ -925,7 +914,7 @@ cdef class CustomReaction(Reaction): Warning: this class is an experimental part of the Cantera API and may be changed or removed without notice. """ - reaction_type = "custom-Python" + reaction_type = "custom-rate-function" def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): @@ -944,8 +933,8 @@ cdef class CustomReaction(Reaction): return self._rate def __set__(self, CustomRate rate): self._rate = rate - cdef CxxCustomPyReaction* r = self.reaction - r.setRxnRate(self._rate._base) + cdef CxxCustomFunc1Reaction* r = self.reaction + r.setReactionRate(self._rate._base) cdef class TestReaction(Reaction): @@ -991,10 +980,10 @@ cdef class TestReaction(Reaction): """ Get/Set the `Arrhenius` rate coefficient for this reaction. """ def __get__(self): cdef CxxTestReaction* r = self.reaction - return ArrheniusRate.wrap(r.rxnRate()) + return ArrheniusRate.wrap(r.reactionRate()) def __set__(self, ArrheniusRate rate): cdef CxxTestReaction* r = self.reaction - r.setRxnRate(rate._base) + r.setReactionRate(rate._base) property allow_negative_pre_exponential_factor: """ diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 39e430a0f7..adbeff2600 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -95,7 +95,7 @@ class TestCustom(TestElementary): _equation = 'H2 + O <=> H + OH' _rate_obj = ct.CustomRate(lambda T: 38.7 * T**2.7 * exp(-3150.15428/T)) _index = 2 - _type = "custom-Python" + _type = "custom-rate-function" def setUp(self): # need to overwrite rate to ensure correct type ('method' is not compatible with Func1) diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index ccde3170d3..717c30891a 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -243,7 +243,7 @@ bool GasKinetics::addReaction(shared_ptr r) return false; } - shared_ptr rate=r->rxnRate(); + shared_ptr rate=r->reactionRate(); if (rate) { size_t nRxn = nReactions() - 1; // new generic reaction type handler @@ -343,13 +343,13 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) // operations common to all reaction types BulkKinetics::modifyReaction(i, rNew); - shared_ptr rate=rNew->rxnRate(); + shared_ptr rate=rNew->reactionRate(); if (rate) { // new generic reaction type handler if (rate->type() == "ArrheniusRate") { modifyArrheniusRate(i, std::dynamic_pointer_cast(rate)); } else { - modifyRxnRate(i, rate); + modifyReactionRate(i, rate); } } else if (rNew->type() == "elementary") { modifyElementaryReaction(i, dynamic_cast(*rNew)); @@ -397,20 +397,20 @@ void GasKinetics::modifyChebyshevReaction(size_t i, ChebyshevReaction& r) m_cheb_rates.replace(i, r.rate); } -void GasKinetics::modifyRxnRate(size_t i, shared_ptr newRate) +void GasKinetics::modifyReactionRate(size_t i, shared_ptr newRate) { if (m_rxn_indices.find(i) != m_rxn_indices.end()) { size_t j = m_rxn_indices[i]; if (newRate->type() != m_rxn_rates[j]->type()) { - throw CanteraError("GasKinetics::modifyRxnRate", + throw CanteraError("GasKinetics::modifyReactionRate", "Attempting to replace '{}' with '{}'.", m_rxn_rates[j]->type(), newRate->type()); } newRate->setIndex(m_rxn_rates[j]->index()); m_rxn_rates[j] = newRate; } else { - throw CanteraError("GasKinetics::modifyRxnRate", + throw CanteraError("GasKinetics::modifyReactionRate", "Index {} does not exist.", i); } } diff --git a/src/kinetics/KineticsFactory.cpp b/src/kinetics/KineticsFactory.cpp index a292bdc224..e45b887fff 100644 --- a/src/kinetics/KineticsFactory.cpp +++ b/src/kinetics/KineticsFactory.cpp @@ -10,7 +10,6 @@ #include "cantera/kinetics/InterfaceKinetics.h" #include "cantera/kinetics/EdgeKinetics.h" #include "cantera/kinetics/importKinetics.h" -#include "cantera/kinetics/ReactionFactory.h" #include "cantera/base/xml.h" #include "cantera/base/stringUtils.h" diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index de522b5231..863a745eb9 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -297,7 +297,7 @@ ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, reaction_type = CHEBYSHEV_RXN; } -CustomPyReaction::CustomPyReaction() +CustomFunc1Reaction::CustomFunc1Reaction() : Reaction() { reaction_type = CUSTOMPY_RXN; @@ -560,7 +560,7 @@ void parseReactionEquation(Reaction& R, const AnyValue& equation, } if (kin.kineticsSpeciesIndex(species) == npos && stoich != -1 && species != "M") { - R.set_valid(false); + R.setValid(false); } if (reactants) { @@ -592,7 +592,7 @@ void setupReaction(Reaction& R, const AnyMap& node, const Kinetics& kin) for (const auto& order : node["orders"].asMap()) { R.orders[order.first] = order.second; if (kin.kineticsSpeciesIndex(order.first) == npos) { - R.set_valid(false); + R.setValid(false); } } } @@ -850,12 +850,12 @@ void setupChebyshevReaction(ChebyshevReaction&R, const AnyMap& node, coeffs); } -void setupCustomPyReaction(CustomPyReaction& R, const AnyMap& node, - const Kinetics& kin) +void setupCustomFunc1Reaction(CustomFunc1Reaction& R, const AnyMap& node, + const Kinetics& kin) { setupReaction(R, node, kin); - CustomPyRate rate; - R.setRxnRate(std::make_shared(std::move(rate))); + CustomFunc1Rate rate; + R.setReactionRate(std::make_shared(std::move(rate))); } void setupTestReaction(TestReaction& R, const AnyMap& node, @@ -867,7 +867,7 @@ void setupTestReaction(TestReaction& R, const AnyMap& node, ArrheniusRate rate(arr.preExponentialFactor(), arr.temperatureExponent(), arr.activationEnergy_R()); - R.setRxnRate(std::make_shared(std::move(rate))); + R.setReactionRate(std::make_shared(std::move(rate))); } void setupInterfaceReaction(InterfaceReaction& R, const XML_Node& rxn_node) diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 34ac4f6063..fb0b6736a7 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -96,16 +96,16 @@ ReactionFactory::ReactionFactory() setupChebyshevReaction(*(ChebyshevReaction*)R, node, kin); }); - // register custom Python reactions - reg("custom-Python", []() { return new CustomPyReaction(); }); - reg_XML("custom-Python", + // register custom reactions specified by Func1 objects + reg("custom-rate-function", []() { return new CustomFunc1Reaction(); }); + reg_XML("custom-rate-function", [](Reaction* R, const XML_Node& node) { - throw CanteraError("ReactionFactory", "Custom Python reactions " - "cannot be created from XML nodes'"); + throw CanteraError("ReactionFactory", "Custom reactions based " + "on 'Func1' objects cannot be created from XML nodes'"); }); - reg_AnyMap("custom-Python", + reg_AnyMap("custom-rate-function", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - setupCustomPyReaction(*(CustomPyReaction*)R, node, kin); + setupCustomFunc1Reaction(*(CustomFunc1Reaction*)R, node, kin); }); // register custom Python reactions @@ -136,9 +136,6 @@ ReactionFactory::ReactionFactory() // register electrochemical reactions reg("electrochemical", []() { return new ElectrochemicalReaction(); }); - addAlias("electrochemical", "butlervolmer_noactivitycoeffs"); - addAlias("electrochemical", "butlervolmer"); - addAlias("electrochemical", "surfaceaffinity"); reg_XML("electrochemical", [](Reaction* R, const XML_Node& node) { setupElectrochemicalReaction(*(ElectrochemicalReaction*)R, node); diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 3c0f502d3c..a7dcb285a2 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -159,13 +159,13 @@ ChebyshevRate::ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, } } -CustomPyRate::CustomPyRate() : m_ratefunc(0) {} +CustomFunc1Rate::CustomFunc1Rate() : m_ratefunc(0) {} -void CustomPyRate::setRateFunction(shared_ptr f) { +void CustomFunc1Rate::setRateFunction(shared_ptr f) { m_ratefunc = f; } -double CustomPyRate::eval(const State& state) const { +double CustomFunc1Rate::eval(const State& state) const { if (m_ratefunc) { return m_ratefunc->eval(state.temperature); } From 4d086a6a27494b97551d4a2914aad05564ec8e77 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 13 Mar 2021 13:26:50 -0600 Subject: [PATCH 55/67] [Kinetics] introduce reaction-rate specific shared data types --- include/cantera/kinetics/GasKinetics.h | 10 +- include/cantera/kinetics/Reaction.h | 8 +- include/cantera/kinetics/ReactionRate.h | 252 ++++++++++++++++++++++++ include/cantera/kinetics/RxnRates.h | 182 ----------------- interfaces/cython/cantera/_cantera.pxd | 18 +- interfaces/cython/cantera/reaction.pyx | 4 +- src/kinetics/GasKinetics.cpp | 40 +++- src/kinetics/ReactionRate.cpp | 41 ++++ src/kinetics/RxnRates.cpp | 18 -- 9 files changed, 346 insertions(+), 227 deletions(-) create mode 100644 include/cantera/kinetics/ReactionRate.h create mode 100644 src/kinetics/ReactionRate.cpp diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index 8d12fc14da..033d6f2568 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -90,13 +90,17 @@ class GasKinetics : public BulkKinetics Rate1 m_cheb_rates; //! Vector of generic reaction rates - std::vector> m_rxn_rates; + std::vector> m_rxn_rates; //! Vector of specialized arrhenius reaction rates std::vector m_arrhenius_rates; + //! Vector of specialized custom reaction rates + std::vector m_func1_rates; + std::map m_rxn_indices; std::map m_arrhenius_indices; + std::map m_func1_indices; //! @name Reaction rate data //!@{ @@ -126,9 +130,11 @@ class GasKinetics : public BulkKinetics void modifyChebyshevReaction(size_t i, ChebyshevReaction& r); //! @internal May be changed without notice in future versions - void modifyReactionRate(size_t i, shared_ptr rate); + void modifyReactionRate(size_t i, shared_ptr rate); //! @internal May be changed without notice in future versions void modifyArrheniusRate(size_t i, shared_ptr rate); + //! @internal May be changed without notice in future versions + void modifyCustomFunc1Rate(size_t i, shared_ptr rate); //! Update the equilibrium constants in molar units. void updateKc(); diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index a0e65afb81..9a0c52f665 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -9,7 +9,7 @@ #define CT_REACTION_H #include "cantera/base/AnyMap.h" -#include "cantera/kinetics/RxnRates.h" +#include "cantera/kinetics/ReactionRate.h" namespace Cantera { @@ -61,12 +61,12 @@ class Reaction } //! Get reaction rate pointer - shared_ptr reactionRate() { + shared_ptr reactionRate() { return m_rate; } //! Set reaction rate pointer - void setReactionRate(shared_ptr rate) { + void setReactionRate(shared_ptr rate) { m_rate = rate; } @@ -114,7 +114,7 @@ class Reaction bool m_valid; //! Reaction rate used by generic reactions - shared_ptr m_rate; + shared_ptr m_rate; }; diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h new file mode 100644 index 0000000000..71d9ea4b81 --- /dev/null +++ b/include/cantera/kinetics/ReactionRate.h @@ -0,0 +1,252 @@ +/** + * @file ReactionRate.h + */ + +// This file is part of Cantera. See License.txt in the top-level directory or +// at https://cantera.org/license.txt for license and copyright information. + +#ifndef CT_REACTIONRATE_H +#define CT_REACTIONRATE_H + +#include "cantera/kinetics/RxnRates.h" +#include "cantera/base/ctexceptions.h" + +namespace Cantera +{ + +class Func1; +class ThermoPhase; + + +//! Abstract base class for reaction rate definitions +/** + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ +class ReactionRateBase +{ +public: + virtual ~ReactionRateBase() {} + + //! Identifier of reaction type + virtual std::string type() const = 0; + + //! Evaluate reaction rate based on temperature + virtual double eval(double T) const = 0; + + //! Evaluate reaction rate based on temperature and pressure + virtual double eval(double T, double P) const = 0; + + //! Evaluate reaction rate derivative based on temperature + virtual double ddT(double T) const = 0; + + //! Evaluate reaction rate derivative based on temperature and pressure + virtual double ddT(double T, double P) const = 0; + + //! Update information specific to reaction + virtual bool uses_update() const { return false; } + + //! Index or reaction within kinetics + size_t index() { return m_index; } + + //! Index or reaction within kinetics + void setIndex(size_t index) { m_index = index; } + +protected: + size_t m_index; //!< reaction index +}; + + +//! Class template for reaction rate definitions with specialized DataType +/** + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ +template +class ReactionRate : public ReactionRateBase +{ +public: + //! Constructor + ReactionRate() = default; + + //! Update information specific to reaction + virtual void update(const DataType& shared_data) { } + + //! Evaluate reaction rate + virtual double eval(const DataType& shared_data) const = 0; + + virtual double eval(double T) const override { + return eval(DataType(T)); + } + + virtual double eval(double T, double P) const override { + return eval(DataType(T, P)); + } + + //! Evaluate reaction rate based on temperature, pressure and concentrations + double eval(double T, double P, const vector_fp& conc) const { + return eval(DataType(T, P, conc)); + } + + // [...] other overrides are not created (yet) + + //! Evaluate reaction rate derivative (with respect to temperature) + virtual double ddT(const DataType& shared_data) const { + throw CanteraError("ReactionRate::ddT", + "Not (yet) implemented by derived ReactionRate object."); + } + + virtual double ddT(double T) const override { + return ddT(DataType(T)); + } + + virtual double ddT(double T, double P) const override { + return ddT(DataType(T, P)); + } + + // [...] other overrides are not created (yet) + + //! Evaluate reaction rate derivative (with respect to pressure) + virtual double ddP(const DataType& shared_data) const { + throw CanteraError("ReactionRate::ddP", + "Not (yet) implemented by derived ReactionRate object."); + } + + // [...] other signatures are not created (yet) + + // signatures of a ddC method tbd +}; + + +//! Data container holding shared data specific to `ArrheniusRate` +/** + * The data container `ArrheniusData` holds precalculated data common to + * all `ArrheniusRate` objects. + * + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ +struct ArrheniusData { + ArrheniusData() + : m_temperature(1.), m_logT(0.), m_recipT(1.) { + } + + //! Constructor based on temperature *T* + ArrheniusData(double T) : m_temperature(T) { + m_logT = std::log(T); + m_recipT = 1./T; + } + + //! Constructor based on temperature *T* + ArrheniusData(double T, double P) : ArrheniusData(T) {} + + //! Constructor accessing reacting phase definitions + ArrheniusData(const ThermoPhase& bulk_phase) { + update(bulk_phase); + } + + //! Update based on bulk phase definitions + void update(const ThermoPhase& bulk_phase); + + double m_temperature; + double m_logT; + double m_recipT; +}; + + +//! Arrhenius reaction rate type depends only on temperature +/** + * Wrapped Arrhenius rate. + * + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ +class ArrheniusRate final : public ReactionRate, public Arrhenius +{ +public: + //! Constructor. + ArrheniusRate(); + + ArrheniusRate(double A, double b, double E); + + virtual std::string type() const override { + return "ArrheniusRate"; + } + + virtual double eval(const ArrheniusData& shared_data) const override { + return updateRC(shared_data.m_logT, shared_data.m_recipT); + } + + virtual double ddT(const ArrheniusData& shared_data) const override { + return updateRC(shared_data.m_logT, shared_data.m_recipT) * + (m_b + m_E * shared_data.m_recipT) * shared_data.m_recipT; + } + + virtual double ddP(const ArrheniusData& shared_data) const override { + return 0.; + } +}; + + +//! Data container holding shared data specific to `CustomFunc1Rate` +/** + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ +struct CustomFunc1Data { + CustomFunc1Data() + : m_temperature(1.) { + } + + //! Constructor based on temperature *T* + CustomFunc1Data(double T) : m_temperature(T) {} + + //! Constructor based on temperature *T* + CustomFunc1Data(double T, double P) : CustomFunc1Data(T) {} + + //! Constructor accessing reacting phase definitions + CustomFunc1Data(const ThermoPhase& bulk_phase) { + update(bulk_phase); + } + + //! Update based on reacting phase definitions + void update(const ThermoPhase& bulk_phase); + + double m_temperature; +}; + + +//! Custom reaction rate depending only on temperature +/** + * The rate expression is provided by a Func1 object taking a single + * argument (temperature) and does not use a formalized parameterization. + * + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ +class CustomFunc1Rate final : public ReactionRate +{ +public: + //! Constructor. + CustomFunc1Rate(); + + virtual std::string type() const override { + return "custom-function"; + } + + // set custom rate + /** + * The call to the Func1 object takes a single argument (temperature) and + * does not depend on parameters handled in C++. + */ + void setRateFunction(shared_ptr f); + + virtual double eval(const CustomFunc1Data& shared_data) const override; + +protected: + shared_ptr m_ratefunc; +}; + +} + +#endif diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 4d60d12a36..c5e07de478 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -15,7 +15,6 @@ namespace Cantera { class Array2D; -class Func1; //! Arrhenius reaction rate type depends only on temperature /** @@ -430,187 +429,6 @@ class ChebyshevRate vector_fp dotProd_; //!< dot product of chebCoeffs with the reduced pressure polynomial }; - -//! Data container holding state information -/** - * The data container `State` holds information passed to ReactionRate objects. Here, a - * pre-calculation of commonly used information (inverse, log, etc.) avoids - * computational overhead. - */ -struct State { - State() - : temperature(1.), logT(0.), recipT(0.) - , pressure(1.), logP(0.), log10P(0.) { - } - - //! Constructor based on temperature *T* - State(double T) : temperature(T), pressure(1.), logP(0.), log10P(0.) { - logT = std::log(T); - recipT = 1./T; - } - - //! Constructor based on temperature *T* and pressure *P* - State(double T, double P) : temperature(T), pressure(P) { - logT = std::log(T); - recipT = 1./T; - logP = std::log(P); - log10P = std::log10(P); - } - - //! Constructor based on temperature *T*, pressure *P* and - //! concentration *conc* - State(double T, double P, const vector_fp& conc) : State(T, P) { - throw CanteraError("State::State", "Not implemented (yet)"); - } - - double temperature; - double logT; - double recipT; - double pressure; - double logP; - double log10P; - vector_fp conc; -}; - - -//! Abstract base class for reaction rate definitions -/** - * @warning This class is an experimental part of the %Cantera API and - * may be changed or removed without notice. - */ -class ReactionRate -{ -public: - //! Constructor - ReactionRate() = default; - - //! Identifier of reaction type - virtual std::string type() const = 0; - - //! Index or reaction within kinetics - size_t index() { return m_index; } - - //! Index or reaction within kinetics - void setIndex(size_t index) { m_index = index; } - - //! Evaluate reaction rate - virtual double eval(const State& state) const = 0; - - //! Evaluate reaction rate based on temperature - double eval(double T) const { - return eval(State(T)); - } - - //! Evaluate reaction rate based on temperature and pressure - double eval(double T, double P) const { - return eval(State(T, P)); - } - - //! Evaluate reaction rate based on temperature, pressure and concentrations - double eval(double T, double P, const vector_fp& conc) const { - return eval(State(T, P, conc)); - } - - // [...] other overrides are not created (yet) - - //! Evaluate reaction rate derivative (with respect to temperature) - virtual double ddT(const State& state) const { - throw CanteraError("ReactionRate::ddT", - "Not (yet) implemented by derived ReactionRate object."); - } - - //! Evaluate reaction rate based on temperature - double ddT(double T) const { - return ddT(State(T)); - } - - //! Evaluate reaction rate based on temperature and pressure - double ddT(double T, double P) const { - return ddT(State(T, P)); - } - - // [...] other overrides are not created (yet) - - //! Evaluate reaction rate derivative (with respect to pressure) - virtual double ddP(const State& state) const { - throw CanteraError("ReactionRate::ddP", - "Not (yet) implemented by derived ReactionRate object."); - } - - // [...] other signatures are not created (yet) - - // signatures of a ddC method tbd - -protected: - size_t m_index; //!< reaction index -}; - - -//! Custom reaction rate depending only on temperature -/** - * The rate expression is provided by a Func1 object taking a single - * argument (temperature) and does not use a formalized parameterization. - * - * @warning This class is an experimental part of the %Cantera API and - * may be changed or removed without notice. - */ -class CustomFunc1Rate final : public ReactionRate -{ -public: - //! Constructor. - CustomFunc1Rate(); - - virtual std::string type() const override { - return "custom-function"; - } - - // set custom rate - /** - * The call to the Func1 object takes a single argument (temperature) and - * does not depend on parameters handled in C++. - */ - void setRateFunction(shared_ptr f); - - virtual double eval(const State& state) const override; - -protected: - shared_ptr m_ratefunc; -}; - - -//! Arrhenius reaction rate type depends only on temperature -/** - * Wrapped Arrhenius rate. - * - * @warning This class is an experimental part of the %Cantera API and - * may be changed or removed without notice. - */ -class ArrheniusRate final : public ReactionRate, public Arrhenius -{ -public: - //! Constructor. - ArrheniusRate(); - - ArrheniusRate(double A, double b, double E); - - virtual std::string type() const override { - return "ArrheniusRate"; - } - - virtual double eval(const State& state) const override { - return updateRC(state.logT, state.recipT); - } - - virtual double ddT(const State& state) const override { - return updateRC(state.logT, state.recipT) * - (m_b + m_E * state.recipT) * state.recipT; - } - - virtual double ddP(const State& state) const override { - return 0.; - } -}; - } #endif diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 3f9a3e1acb..731ffad703 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -312,19 +312,19 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": double temperatureExponent() double activationEnergy_R() - cdef cppclass CxxReactionRate "Cantera::ReactionRate": - CxxReactionRate() + cdef cppclass CxxReactionRateBase "Cantera::ReactionRateBase": + CxxReactionRateBase() string type() double eval(double) except +translate_exception double eval(double, double) except +translate_exception double ddT(double) except +translate_exception double ddT(double, double) except +translate_exception - cdef cppclass CxxCustomFunc1Rate "Cantera::CustomFunc1Rate" (CxxReactionRate): + cdef cppclass CxxCustomFunc1Rate "Cantera::CustomFunc1Rate" (CxxReactionRateBase): CxxCustomFunc1Rate() void setRateFunction(shared_ptr[CxxFunc1]) except +translate_exception - cdef cppclass CxxArrheniusRate "Cantera::ArrheniusRate" (CxxReactionRate): + cdef cppclass CxxArrheniusRate "Cantera::ArrheniusRate" (CxxReactionRateBase): CxxArrheniusRate() CxxArrheniusRate(double, double, double) double preExponentialFactor() @@ -347,8 +347,8 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cbool duplicate cbool allow_nonreactant_orders cbool allow_negative_orders - shared_ptr[CxxReactionRate] reactionRate() - void setReactionRate(shared_ptr[CxxReactionRate]) + shared_ptr[CxxReactionRateBase] reactionRate() + void setReactionRate(shared_ptr[CxxReactionRateBase]) cdef cppclass CxxElementaryReaction "Cantera::ElementaryReaction" (CxxReaction): CxxElementaryReaction() @@ -1033,8 +1033,8 @@ cdef class InterfacePhase(ThermoPhase): cdef CxxSurfPhase* surf cdef class _ReactionRate: - cdef shared_ptr[CxxReactionRate] _base - cdef CxxReactionRate* base + cdef shared_ptr[CxxReactionRateBase] _base + cdef CxxReactionRateBase* base cdef class CustomRate(_ReactionRate): cdef CxxCustomFunc1Rate* rate @@ -1043,7 +1043,7 @@ cdef class CustomRate(_ReactionRate): cdef class ArrheniusRate(_ReactionRate): cdef CxxArrheniusRate* rate @staticmethod - cdef wrap(shared_ptr[CxxReactionRate]) + cdef wrap(shared_ptr[CxxReactionRateBase]) cdef class Reaction: cdef shared_ptr[CxxReaction] _reaction diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 154f88e5af..636e15fc44 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -472,9 +472,9 @@ cdef class ArrheniusRate(_ReactionRate): self.rate = (self.base) @staticmethod - cdef wrap(shared_ptr[CxxReactionRate] rate): + cdef wrap(shared_ptr[CxxReactionRateBase] rate): """ - Wrap a C++ ReactionRate object with a Python object. + Wrap a C++ ReactionRateBase object with a Python object. """ # wrap C++ reaction cdef ArrheniusRate arr diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 717c30891a..e55d7256e9 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -26,8 +26,9 @@ void GasKinetics::update_rates_T() double T = thermo().temperature(); double P = thermo().pressure(); m_logStandConc = log(thermo().standardConcentration()); - State state(T, P); - double logT = state.logT; + ArrheniusData arrhenius_shared(T); + CustomFunc1Data func1_shared(T); + double logT = arrhenius_shared.m_logT; if (T != m_temp) { if (!m_rfn.empty()) { @@ -49,12 +50,12 @@ void GasKinetics::update_rates_T() for (auto& rate : m_arrhenius_rates) { // generic reaction rates - m_rfn.data()[rate.index()] = rate.eval(state); + m_rfn.data()[rate.index()] = rate.eval(arrhenius_shared); } - for (auto& rate : m_rxn_rates) { + for (auto& rate : m_func1_rates) { // generic reaction rates - m_rfn.data()[rate->index()] = rate->eval(state); + m_rfn.data()[rate.index()] = rate.eval(func1_shared); } if (m_plog_rates.nReactions()) { @@ -243,7 +244,7 @@ bool GasKinetics::addReaction(shared_ptr r) return false; } - shared_ptr rate=r->reactionRate(); + shared_ptr rate=r->reactionRate(); if (rate) { size_t nRxn = nReactions() - 1; // new generic reaction type handler @@ -251,9 +252,13 @@ bool GasKinetics::addReaction(shared_ptr r) if (rate->type() == "ArrheniusRate") { m_arrhenius_indices[nRxn] = m_arrhenius_rates.size(); m_arrhenius_rates.push_back(*std::dynamic_pointer_cast(rate)); + } else if (rate->type() == "custom-function") { + m_func1_indices[nRxn] = m_func1_rates.size(); + m_func1_rates.push_back(*std::dynamic_pointer_cast(rate)); } else { - m_rxn_indices[nRxn] = m_rxn_rates.size(); - m_rxn_rates.push_back(rate); + throw CanteraError("GasKinetics::addReaction", "No longer used."); + //m_rxn_indices[nRxn] = m_rxn_rates.size(); + //m_rxn_rates.push_back(rate); } } else if (r->type() == "elementary") { addElementaryReaction(dynamic_cast(*r)); @@ -343,11 +348,13 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) // operations common to all reaction types BulkKinetics::modifyReaction(i, rNew); - shared_ptr rate=rNew->reactionRate(); + shared_ptr rate=rNew->reactionRate(); if (rate) { // new generic reaction type handler if (rate->type() == "ArrheniusRate") { modifyArrheniusRate(i, std::dynamic_pointer_cast(rate)); + } else if (rate->type() == "custom-function") { + modifyCustomFunc1Rate(i, std::dynamic_pointer_cast(rate)); } else { modifyReactionRate(i, rate); } @@ -397,7 +404,7 @@ void GasKinetics::modifyChebyshevReaction(size_t i, ChebyshevReaction& r) m_cheb_rates.replace(i, r.rate); } -void GasKinetics::modifyReactionRate(size_t i, shared_ptr newRate) +void GasKinetics::modifyReactionRate(size_t i, shared_ptr newRate) { if (m_rxn_indices.find(i) != m_rxn_indices.end()) { @@ -428,6 +435,19 @@ void GasKinetics::modifyArrheniusRate(size_t i, } } +void GasKinetics::modifyCustomFunc1Rate(size_t i, + shared_ptr newRate) +{ + if (m_func1_indices.find(i) != m_func1_indices.end()) { + size_t j = m_func1_indices[i]; + newRate->setIndex(m_func1_rates[j].index()); + m_func1_rates[j] = *newRate; + } else { + throw CanteraError("GasKinetics::modifyCustomFunc1Rate", + "Index {} does not exist.", i); + } +} + void GasKinetics::init() { BulkKinetics::init(); diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp new file mode 100644 index 0000000000..69ae9bdffe --- /dev/null +++ b/src/kinetics/ReactionRate.cpp @@ -0,0 +1,41 @@ +//! @file RxnRates.cpp + +// This file is part of Cantera. See License.txt in the top-level directory or +// at https://cantera.org/license.txt for license and copyright information. + +#include "cantera/kinetics/ReactionRate.h" +#include "cantera/numerics/Func1.h" +#include "cantera/thermo/ThermoPhase.h" + +namespace Cantera +{ + +void CustomFunc1Data::update(const ThermoPhase& bulk_phase) { + m_temperature = bulk_phase.temperature(); +} + +CustomFunc1Rate::CustomFunc1Rate() : m_ratefunc(0) {} + +void CustomFunc1Rate::setRateFunction(shared_ptr f) { + m_ratefunc = f; +} + +double CustomFunc1Rate::eval(const CustomFunc1Data& shared_data) const { + if (m_ratefunc) { + return m_ratefunc->eval(shared_data.m_temperature); + } + return 0.; +} + + +void ArrheniusData::update(const ThermoPhase& bulk_phase) { + m_temperature = bulk_phase.temperature(); + m_logT = std::log(m_temperature); + m_recipT = 1./m_temperature; +} + +ArrheniusRate::ArrheniusRate(double A, double b, double E) + : Arrhenius(A, b, E) { +} + +} diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index a7dcb285a2..269378b772 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -5,7 +5,6 @@ #include "cantera/kinetics/RxnRates.h" #include "cantera/base/Array.h" -#include "cantera/numerics/Func1.h" namespace Cantera { @@ -159,21 +158,4 @@ ChebyshevRate::ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, } } -CustomFunc1Rate::CustomFunc1Rate() : m_ratefunc(0) {} - -void CustomFunc1Rate::setRateFunction(shared_ptr f) { - m_ratefunc = f; -} - -double CustomFunc1Rate::eval(const State& state) const { - if (m_ratefunc) { - return m_ratefunc->eval(state.temperature); - } - return 0.; -} - -ArrheniusRate::ArrheniusRate(double A, double b, double E) - : Arrhenius(A, b, E) { -} - } From efbe7bcbcbb44ee9d99815fb212ac2ab6db074b0 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 13 Mar 2021 23:34:23 -0600 Subject: [PATCH 56/67] [Kinetics] create ArrheniusRate from AnyMap --- include/cantera/kinetics/ReactionRate.h | 8 +++++++- include/cantera/kinetics/RxnRates.h | 7 +++++++ src/kinetics/Reaction.cpp | 24 ++++++------------------ src/kinetics/ReactionRate.cpp | 5 ++++- src/kinetics/RxnRates.cpp | 23 +++++++++++++++++++++++ 5 files changed, 47 insertions(+), 20 deletions(-) diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 71d9ea4b81..5a5bd53452 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -16,6 +16,7 @@ namespace Cantera class Func1; class ThermoPhase; +class AnyMap; //! Abstract base class for reaction rate definitions @@ -169,6 +170,8 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius ArrheniusRate(double A, double b, double E); + ArrheniusRate(const AnyMap& node, const Units& rc_units); + virtual std::string type() const override { return "ArrheniusRate"; } @@ -230,11 +233,14 @@ class CustomFunc1Rate final : public ReactionRate //! Constructor. CustomFunc1Rate(); + // Does nothing, as there is no formalized parameterization. + CustomFunc1Rate(const AnyMap& rate, const Units& rc_units) {} + virtual std::string type() const override { return "custom-function"; } - // set custom rate + //! Set custom rate /** * The call to the Func1 object takes a single argument (temperature) and * does not depend on parameters handled in C++. diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index c5e07de478..1e0fbfcdae 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -15,6 +15,9 @@ namespace Cantera { class Array2D; +class AnyValue; +class UnitSystem; +class Units; //! Arrhenius reaction rate type depends only on temperature /** @@ -38,6 +41,10 @@ class Arrhenius /// @param E Activation energy in temperature units. Kelvin. Arrhenius(doublereal A, doublereal b, doublereal E); + //! Run object setup based on AnyMap node information + void setup(const AnyValue& rate, + const UnitSystem& units, const Units& rc_units); + //! Update concentration-dependent parts of the rate coefficient. /*! * For this class, there are no concentration-dependent parts, so this diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 863a745eb9..d72f96d25a 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -391,20 +391,10 @@ Arrhenius readArrhenius(const Reaction& R, const AnyValue& rate, const Kinetics& kin, const UnitSystem& units, int pressure_dependence=0) { - double A, b, Ta; Units rc_units = rateCoeffUnits(R, kin, pressure_dependence); - if (rate.is()) { - auto& rate_map = rate.as(); - A = units.convert(rate_map["A"], rc_units); - b = rate_map["b"].asDouble(); - Ta = units.convertActivationEnergy(rate_map["Ea"], "K"); - } else { - auto& rate_vec = rate.asVector(3); - A = units.convert(rate_vec[0], rc_units); - b = rate_vec[1].asDouble(); - Ta = units.convertActivationEnergy(rate_vec[2], "K"); - } - return Arrhenius(A, b, Ta); + Arrhenius arr; + arr.setup(rate, units, rc_units); + return arr; } //! Parse falloff parameters, given a rateCoeff node @@ -862,12 +852,10 @@ void setupTestReaction(TestReaction& R, const AnyMap& node, const Kinetics& kin) { setupReaction(R, node, kin); - R.allow_negative_pre_exponential_factor = node.getBool("negative-A", false); - Arrhenius arr = readArrhenius(R, node["rate-constant"], kin, node.units()); - ArrheniusRate rate(arr.preExponentialFactor(), - arr.temperatureExponent(), - arr.activationEnergy_R()); + Units rc_units = rateCoeffUnits(R, kin); + ArrheniusRate rate(node, rc_units); R.setReactionRate(std::make_shared(std::move(rate))); + R.allow_negative_pre_exponential_factor = node.getBool("negative-A", false); } void setupInterfaceReaction(InterfaceReaction& R, const XML_Node& rxn_node) diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 69ae9bdffe..fd24c981f5 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -27,7 +27,6 @@ double CustomFunc1Rate::eval(const CustomFunc1Data& shared_data) const { return 0.; } - void ArrheniusData::update(const ThermoPhase& bulk_phase) { m_temperature = bulk_phase.temperature(); m_logT = std::log(m_temperature); @@ -38,4 +37,8 @@ ArrheniusRate::ArrheniusRate(double A, double b, double E) : Arrhenius(A, b, E) { } +ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rc_units) { + setup(node["rate-constant"], node.units(), rc_units); +} + } diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 269378b772..959ad86a1d 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -5,6 +5,7 @@ #include "cantera/kinetics/RxnRates.h" #include "cantera/base/Array.h" +#include "cantera/base/AnyMap.h" namespace Cantera { @@ -28,6 +29,28 @@ Arrhenius::Arrhenius(doublereal A, doublereal b, doublereal E) } } +void Arrhenius::setup(const AnyValue& rate, + const UnitSystem& units, const Units& rc_units) +{ + if (rate.is()) { + auto& rate_map = rate.as(); + m_A = units.convert(rate_map["A"], rc_units); + m_b = rate_map["b"].asDouble(); + m_E = units.convertActivationEnergy(rate_map["Ea"], "K"); + } else { + auto& rate_vec = rate.asVector(3); + m_A = units.convert(rate_vec[0], rc_units); + m_b = rate_vec[1].asDouble(); + m_E = units.convertActivationEnergy(rate_vec[2], "K"); + } + + if (m_A <= 0.0) { + m_logA = -1.0E300; + } else { + m_logA = std::log(m_A); + } +} + SurfaceArrhenius::SurfaceArrhenius() : m_b(0.0) , m_E(0.0) From 7362930a1f06ec04e2ceeba3b2129d4dcc8502f6 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 14 Mar 2021 09:23:04 -0500 Subject: [PATCH 57/67] [Kinetics] add XML related 'todo' comments to ReactionFactory After removal of XML support, some service functions can be removed to simplify the code structure. --- include/cantera/kinetics/ReactionFactory.h | 23 +++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/include/cantera/kinetics/ReactionFactory.h b/include/cantera/kinetics/ReactionFactory.h index 1fd7b556b7..6cf0ff7e09 100644 --- a/include/cantera/kinetics/ReactionFactory.h +++ b/include/cantera/kinetics/ReactionFactory.h @@ -26,6 +26,11 @@ namespace Cantera * @endcode * * @ingroup reactionGroup + * + * @todo Once support for XML is phased out, a change of the base class type + * to `Factory` will ensure that + * `reg` and `create` methods provided by the base factory class can be + * used. Thus, separate `setup_AnyMap` and other methods can be avoided. */ class ReactionFactory : public Factory { @@ -49,6 +54,11 @@ class ReactionFactory : public Factory s_factory = 0; } + //! Set up reaction based on XML node information + /** + * Kept separate from base `create` method as the factory has to handle both + * XML and `AnyMap` input until support for the former is removed. + */ void setup_XML(std::string name, Reaction* R, const XML_Node& rxn_node) { try { @@ -60,11 +70,19 @@ class ReactionFactory : public Factory } //! Register a new object initializer function (from XML node) + /** + * Kept separate from base `reg` method as the factory has to handle both + * XML and `AnyMap` input until support for the former is removed. + */ void reg_XML(const std::string& name, std::function f) { m_xml_setup[name] = f; } + //! Set up reaction based on AnyMap node information + /** + * @todo call setup directly from constructor after removal of XML support. + */ void setup_AnyMap(std::string name, Reaction* R, const AnyMap& node, const Kinetics& kin) { try { @@ -75,7 +93,10 @@ class ReactionFactory : public Factory } } - //! Register a new object initializer function (from XML node) + //! Register a new object initializer function (from AnyMap node) + /** + * @todo can be handled by `reg` after removal of XML support. + */ void reg_AnyMap(const std::string& name, std::function f) { m_anymap_setup[name] = f; From 0454bf40f1be68093908c7c86ee7b69e4ad9e50c Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 14 Mar 2021 11:37:38 -0500 Subject: [PATCH 58/67] [Kinetics] implement Reaction::setup method The member 'setup' method avoids having to recreate separate stand-alone setup methods. --- include/cantera/kinetics/Reaction.h | 24 ++++++---- include/cantera/kinetics/ReactionRate.h | 2 +- src/kinetics/Reaction.cpp | 63 +++++++++++++++++-------- src/kinetics/ReactionFactory.cpp | 4 +- 4 files changed, 63 insertions(+), 30 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 9a0c52f665..ae0cbb0bec 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -60,6 +60,9 @@ class Reaction m_valid = valid; } + //! Set up reaction based on AnyMap node + virtual void setup(const AnyMap& node, const Kinetics& kin); + //! Get reaction rate pointer shared_ptr reactionRate() { return m_rate; @@ -269,6 +272,8 @@ class CustomFunc1Reaction : public Reaction public: CustomFunc1Reaction(); + virtual void setup(const AnyMap& node, const Kinetics& kin); + virtual std::string type() const { return "custom-rate-function"; } @@ -292,6 +297,8 @@ class TestReaction : public Reaction return "elementary-new"; } + virtual void setup(const AnyMap& node, const Kinetics& kin); + bool allow_negative_pre_exponential_factor; }; @@ -391,6 +398,15 @@ std::vector> getReactions(const AnyValue& items, void parseReactionEquation(Reaction& R, const AnyValue& equation, const Kinetics& kin); +//! The units of the rate constant. These are determined by the units of the +//! standard concentration of the reactant species' phases of the phase +//! where the reaction occurs. +//! +//! @todo Rate units will become available as `rate_units` after serialization +//! is implemented. +Units rateCoeffUnits(const Reaction& R, const Kinetics& kin, + int pressure_dependence=0); + // declarations of setup functions void setupElementaryReaction(ElementaryReaction&, const XML_Node&); //! @internal May be changed without notice in future versions @@ -419,14 +435,6 @@ void setupChebyshevReaction(ChebyshevReaction&, const XML_Node&); void setupChebyshevReaction(ChebyshevReaction&, const AnyMap&, const Kinetics&); -//! @internal May be changed without notice in future versions -void setupCustomFunc1Reaction(CustomFunc1Reaction&, const AnyMap&, - const Kinetics&); - -//! @internal May be changed without notice in future versions -void setupTestReaction(TestReaction&, const AnyMap&, - const Kinetics&); - void setupInterfaceReaction(InterfaceReaction&, const XML_Node&); //! @internal May be changed without notice in future versions void setupInterfaceReaction(InterfaceReaction&, const AnyMap&, diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 5a5bd53452..bbbe4bdef5 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -54,7 +54,7 @@ class ReactionRateBase void setIndex(size_t index) { m_index = index; } protected: - size_t m_index; //!< reaction index + size_t m_index = npos; //!< reaction index }; diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index d72f96d25a..5904e51e86 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -98,6 +98,29 @@ void Reaction::validate() } } +void Reaction::setup(const AnyMap& node, const Kinetics& kin) +{ + parseReactionEquation(*this, node["equation"], kin); + // Non-stoichiometric reaction orders + std::map orders; + if (node.hasKey("orders")) { + for (const auto& order : node["orders"].asMap()) { + orders[order.first] = order.second; + if (kin.kineticsSpeciesIndex(order.first) == npos) { + setValid(false); + } + } + } + + // Flags + id = node.getString("id", ""); + duplicate = node.getBool("duplicate", false); + allow_negative_orders = node.getBool("negative-orders", false); + allow_nonreactant_orders = node.getBool("nonreactant-orders", false); + + input = node; +} + std::string Reaction::reactantString() const { std::ostringstream result; @@ -303,12 +326,32 @@ CustomFunc1Reaction::CustomFunc1Reaction() reaction_type = CUSTOMPY_RXN; } +void CustomFunc1Reaction::setup(const AnyMap& node, const Kinetics& kin) +{ + Reaction::setup(node, kin); + Units rc_units; // @todo Not needed once `rate_units` is implemented. + setReactionRate( + std::shared_ptr(new CustomFunc1Rate(node, rc_units))); +} + TestReaction::TestReaction() : Reaction() , allow_negative_pre_exponential_factor(false) { } +void TestReaction::setup(const AnyMap& node, const Kinetics& kin) +{ + Reaction::setup(node, kin); + + // @todo Rate units will become available as `rate_units` after + // serialization is fully implemented. + Units rc_units = rateCoeffUnits(*this, kin); + setReactionRate( + std::shared_ptr(new ArrheniusRate(node, rc_units))); + allow_negative_pre_exponential_factor = node.getBool("negative-A", false); +} + InterfaceReaction::InterfaceReaction() : is_sticking_coefficient(false) , use_motz_wise_correction(false) @@ -350,7 +393,7 @@ Arrhenius readArrhenius(const XML_Node& arrhenius_node) } Units rateCoeffUnits(const Reaction& R, const Kinetics& kin, - int pressure_dependence=0) + int pressure_dependence) { if (!R.valid()) { // If a reaction is invalid because of missing species in the Kinetics @@ -840,24 +883,6 @@ void setupChebyshevReaction(ChebyshevReaction&R, const AnyMap& node, coeffs); } -void setupCustomFunc1Reaction(CustomFunc1Reaction& R, const AnyMap& node, - const Kinetics& kin) -{ - setupReaction(R, node, kin); - CustomFunc1Rate rate; - R.setReactionRate(std::make_shared(std::move(rate))); -} - -void setupTestReaction(TestReaction& R, const AnyMap& node, - const Kinetics& kin) -{ - setupReaction(R, node, kin); - Units rc_units = rateCoeffUnits(R, kin); - ArrheniusRate rate(node, rc_units); - R.setReactionRate(std::make_shared(std::move(rate))); - R.allow_negative_pre_exponential_factor = node.getBool("negative-A", false); -} - void setupInterfaceReaction(InterfaceReaction& R, const XML_Node& rxn_node) { XML_Node& arr = rxn_node.child("rateCoeff").child("Arrhenius"); diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index fb0b6736a7..e8b7da4313 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -105,7 +105,7 @@ ReactionFactory::ReactionFactory() }); reg_AnyMap("custom-rate-function", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - setupCustomFunc1Reaction(*(CustomFunc1Reaction*)R, node, kin); + R->setup(node, kin); }); // register custom Python reactions @@ -117,7 +117,7 @@ ReactionFactory::ReactionFactory() }); reg_AnyMap("elementary-new", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - setupTestReaction(*(TestReaction*)R, node, kin); + R->setup(node, kin); }); // register interface reactions From aa4d0723b9526b31f7696abad780792cb9225585 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 14 Mar 2021 16:12:56 -0500 Subject: [PATCH 59/67] [Kinetics] add MultiRateBase evaluator class --- include/cantera/kinetics/BulkKinetics.h | 5 ++ include/cantera/kinetics/MultiRate.h | 115 ++++++++++++++++++++++++ include/cantera/kinetics/ReactionRate.h | 100 +++++++++++---------- src/kinetics/BulkKinetics.cpp | 22 +++++ src/kinetics/GasKinetics.cpp | 3 +- src/kinetics/ReactionRate.cpp | 8 +- 6 files changed, 200 insertions(+), 53 deletions(-) create mode 100644 include/cantera/kinetics/MultiRate.h diff --git a/include/cantera/kinetics/BulkKinetics.h b/include/cantera/kinetics/BulkKinetics.h index 52672639c0..25087f7884 100644 --- a/include/cantera/kinetics/BulkKinetics.h +++ b/include/cantera/kinetics/BulkKinetics.h @@ -11,6 +11,7 @@ #include "Kinetics.h" #include "RateCoeffMgr.h" +#include "cantera/kinetics/MultiRate.h" namespace Cantera { @@ -46,6 +47,10 @@ class BulkKinetics : public Kinetics virtual void addElementaryReaction(ElementaryReaction& r); virtual void modifyElementaryReaction(size_t i, ElementaryReaction& rNew); + //! Vector of rate handlers + std::vector> m_bulk_rates; + std::map m_bulk_types; //!< Mapping of rate handlers + Rate1 m_rates; std::vector m_revindex; //!< Indices of reversible reactions std::vector m_irrev; //!< Indices of irreversible reactions diff --git a/include/cantera/kinetics/MultiRate.h b/include/cantera/kinetics/MultiRate.h new file mode 100644 index 0000000000..7f88408ac8 --- /dev/null +++ b/include/cantera/kinetics/MultiRate.h @@ -0,0 +1,115 @@ +/** + * @file MultiRate.h + * + * @warning This file is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ + +// This file is part of Cantera. See License.txt in the top-level directory or +// at https://cantera.org/license.txt for license and copyright information. + +#ifndef CT_MULTIRATE_H +#define CT_MULTIRATE_H + +#include "cantera/kinetics/ReactionRate.h" + +namespace Cantera +{ + +//! An abstract base class for evaluating all reactions of a particular type. +/** + * Because this class has no template parameters, the `Kinetics` object + * can store all of these rate coefficient evaluators as a + * `vector>`. + * + * @todo At the moment, implemented methods are specific to `BulkKinetics`, + * which can be updated using information of a single `ThermoPhase`. + * `InterfaceKinetics` will require access to an entire `Kinetics` object + * or the underlying `vector` vector (e.g. `m_thermo`). + * + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ +class MultiRateBase +{ +public: + virtual ~MultiRateBase() {} + + //! Add reaction rate object to the evaluator + virtual void add(const size_t rxn_index, + const shared_ptr rate) = 0; + + //! Modify reaction rate object handled by the evaluator + virtual bool modify(const size_t rxn_index, + const shared_ptr rate) = 0; + + //! Evaluate all rate constants handled by the evaluator + virtual void getRateConstants(const ThermoPhase& bulk_phase, + double* kf) const = 0; + + //! Update data common to reaction rates of a specific type + virtual void update(const ThermoPhase& bulk) = 0; +}; + + +//! A class template handling all reaction rates specific to `BulkKinetics`. +/** + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ +template +class MultiBulkRates : public MultiRateBase +{ +public: + virtual void add(const size_t rxn_index, + const shared_ptr rate) override { + m_indices[rxn_index] = m_rates.size(); + m_rates.push_back(*std::dynamic_pointer_cast(rate)); + m_rxn.push_back(rxn_index); + } + + virtual bool modify(const size_t rxn_index, + const shared_ptr rate) override { + if (rate->type() != RateType::staticType()) { + throw CanteraError("MultiBulkRate::modify", + "Invalid operation: cannot replace rate object of type '{}' " + "with a new rate of type '{}'.", RateType::staticType(), rate->type()); + } + if (m_indices.find(rxn_index) != m_indices.end()) { + size_t j = m_indices[rxn_index]; + m_rates[j] = *std::dynamic_pointer_cast(rate); + return true; + } + return false; + } + + virtual void getRateConstants(const ThermoPhase& bulk, + double* kf) const override { + for (size_t i = 0; i < m_rates.size(); i++) { + kf[m_rxn[i]] = m_rates[i].eval(m_shared); + } + } + + virtual void update(const ThermoPhase& bulk) override { + // update common data once for each reaction type + m_shared.update(bulk); + if (RateType::uses_update()) { + // update reaction-specific data for each reaction. This loop + // is efficient as all function calls are de-virtualized, and + // all of the rate objects are contiguous in memory + for (auto& rate : m_rates) { + rate.update(m_shared, bulk); + } + } + } + +protected: + std::vector m_rates; //! Reaction rate objects + std::vector m_rxn; //! Index within overall rate vector + std::map m_indices; //! Mapping of indices + DataType m_shared; +}; + +} + +#endif diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index bbbe4bdef5..5089c366b0 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -1,5 +1,8 @@ /** - * @file ReactionRate.h + * @file ReactionRate.h + * + * @warning This file is an experimental part of the %Cantera API and + * may be changed or removed without notice. */ // This file is part of Cantera. See License.txt in the top-level directory or @@ -21,6 +24,15 @@ class AnyMap; //! Abstract base class for reaction rate definitions /** + * Because this class has no template parameters, derived objects can be + * accessed via `shared_ptr`. For performance reasons + * it is essential that derived classes use the keyword `final` to + * de-virtualize `virtual` methods. + * + * Methods defined for the abstract base class are not aware of specialized + * data handlers defined by the template class `ReactionRate` + * and thus can be exposed to the API. + * * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. */ @@ -38,14 +50,17 @@ class ReactionRateBase //! Evaluate reaction rate based on temperature and pressure virtual double eval(double T, double P) const = 0; + //! Evaluate reaction rate based on bulk phase + virtual double eval(const ThermoPhase& bulk) const = 0; + //! Evaluate reaction rate derivative based on temperature virtual double ddT(double T) const = 0; //! Evaluate reaction rate derivative based on temperature and pressure virtual double ddT(double T, double P) const = 0; - //! Update information specific to reaction - virtual bool uses_update() const { return false; } + //! Evaluate reaction rate derivative based on bulk phase + virtual double ddT(const ThermoPhase& bulk) const = 0; //! Index or reaction within kinetics size_t index() { return m_index; } @@ -60,6 +75,9 @@ class ReactionRateBase //! Class template for reaction rate definitions with specialized DataType /** + * This class template ensures that derived objects are aware of specialized + * data types, which are passed by `MultiRateBase` evaluators. + * * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. */ @@ -71,7 +89,7 @@ class ReactionRate : public ReactionRateBase ReactionRate() = default; //! Update information specific to reaction - virtual void update(const DataType& shared_data) { } + virtual void update(const DataType& shared_data, const ThermoPhase& bulk) {} //! Evaluate reaction rate virtual double eval(const DataType& shared_data) const = 0; @@ -84,17 +102,14 @@ class ReactionRate : public ReactionRateBase return eval(DataType(T, P)); } - //! Evaluate reaction rate based on temperature, pressure and concentrations - double eval(double T, double P, const vector_fp& conc) const { - return eval(DataType(T, P, conc)); + virtual double eval(const ThermoPhase& bulk) const override { + return eval(DataType(bulk)); } - // [...] other overrides are not created (yet) - - //! Evaluate reaction rate derivative (with respect to temperature) + //! Evaluate derivative of reaction rate with respect to temperature virtual double ddT(const DataType& shared_data) const { throw CanteraError("ReactionRate::ddT", - "Not (yet) implemented by derived ReactionRate object."); + "Not implemented by derived ReactionRate object."); } virtual double ddT(double T) const override { @@ -105,17 +120,12 @@ class ReactionRate : public ReactionRateBase return ddT(DataType(T, P)); } - // [...] other overrides are not created (yet) - - //! Evaluate reaction rate derivative (with respect to pressure) - virtual double ddP(const DataType& shared_data) const { - throw CanteraError("ReactionRate::ddP", - "Not (yet) implemented by derived ReactionRate object."); + virtual double ddT(const ThermoPhase& bulk) const { + return ddT(DataType(bulk)); } - // [...] other signatures are not created (yet) - - // signatures of a ddC method tbd + // [...] signatures for derivative with respect to pressure and concentration + // are not created (yet) }; @@ -128,9 +138,7 @@ class ReactionRate : public ReactionRateBase * may be changed or removed without notice. */ struct ArrheniusData { - ArrheniusData() - : m_temperature(1.), m_logT(0.), m_recipT(1.) { - } + ArrheniusData() : m_temperature(1.), m_logT(0.), m_recipT(1.) {} //! Constructor based on temperature *T* ArrheniusData(double T) : m_temperature(T) { @@ -141,13 +149,11 @@ struct ArrheniusData { //! Constructor based on temperature *T* ArrheniusData(double T, double P) : ArrheniusData(T) {} - //! Constructor accessing reacting phase definitions - ArrheniusData(const ThermoPhase& bulk_phase) { - update(bulk_phase); - } + //! Constructor accessing bulk phase definitions + ArrheniusData(const ThermoPhase& bulk) { update(bulk); } //! Update based on bulk phase definitions - void update(const ThermoPhase& bulk_phase); + void update(const ThermoPhase& bulk); double m_temperature; double m_logT; @@ -172,9 +178,13 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius ArrheniusRate(const AnyMap& node, const Units& rc_units); - virtual std::string type() const override { - return "ArrheniusRate"; - } + //! @internal access to reaction type is used by `MultiRateBase` evaluator + static std::string staticType() { return "ArrheniusRate"; } + + virtual std::string type() const override { return staticType(); } + + //! Update information specific to reaction + static bool uses_update() { return false; } virtual double eval(const ArrheniusData& shared_data) const override { return updateRC(shared_data.m_logT, shared_data.m_recipT); @@ -184,10 +194,6 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius return updateRC(shared_data.m_logT, shared_data.m_recipT) * (m_b + m_E * shared_data.m_recipT) * shared_data.m_recipT; } - - virtual double ddP(const ArrheniusData& shared_data) const override { - return 0.; - } }; @@ -197,9 +203,7 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius * may be changed or removed without notice. */ struct CustomFunc1Data { - CustomFunc1Data() - : m_temperature(1.) { - } + CustomFunc1Data() : m_temperature(1.) {} //! Constructor based on temperature *T* CustomFunc1Data(double T) : m_temperature(T) {} @@ -207,13 +211,11 @@ struct CustomFunc1Data { //! Constructor based on temperature *T* CustomFunc1Data(double T, double P) : CustomFunc1Data(T) {} - //! Constructor accessing reacting phase definitions - CustomFunc1Data(const ThermoPhase& bulk_phase) { - update(bulk_phase); - } + //! Constructor accessing bulk phase definitions + CustomFunc1Data(const ThermoPhase& bulk) { update(bulk); } //! Update based on reacting phase definitions - void update(const ThermoPhase& bulk_phase); + void update(const ThermoPhase& bulk); double m_temperature; }; @@ -221,7 +223,7 @@ struct CustomFunc1Data { //! Custom reaction rate depending only on temperature /** - * The rate expression is provided by a Func1 object taking a single + * The rate expression is provided by a `Func1` object taking a single * argument (temperature) and does not use a formalized parameterization. * * @warning This class is an experimental part of the %Cantera API and @@ -236,9 +238,13 @@ class CustomFunc1Rate final : public ReactionRate // Does nothing, as there is no formalized parameterization. CustomFunc1Rate(const AnyMap& rate, const Units& rc_units) {} - virtual std::string type() const override { - return "custom-function"; - } + //! @internal access to reaction type is used by `MultiRateBase` evaluator + static std::string staticType() { return "custom-function"; } + + virtual std::string type() const override { return staticType(); } + + //! Update information specific to reaction + static bool uses_update() { return false; } //! Set custom rate /** diff --git a/src/kinetics/BulkKinetics.cpp b/src/kinetics/BulkKinetics.cpp index 7791ebe772..85c7e4ccaf 100644 --- a/src/kinetics/BulkKinetics.cpp +++ b/src/kinetics/BulkKinetics.cpp @@ -120,6 +120,28 @@ bool BulkKinetics::addReaction(shared_ptr r) } else { m_irrev.push_back(nReactions()-1); } + + shared_ptr rate=r->reactionRate(); + if (rate) { + + // If neccessary, add new MultiBulkRates evaluator + if (m_bulk_types.find(rate->type()) != m_bulk_types.end()) { + m_bulk_types[rate->type()] = m_bulk_rates.size(); + + if (rate->type() == "ArrheniusRate") { + m_bulk_rates.push_back(std::unique_ptr( + new MultiBulkRates)); + } else if (rate->type() == "custom-function") { + m_bulk_rates.push_back(std::unique_ptr( + new MultiBulkRates)); + } + } + + // Add reaction rate to evaluator + //size_t index = m_bulk_types[rate->type()]; + //m_bulk_rates[index].add(nReactions() - 1, rate) + } + return true; } diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index e55d7256e9..85ce633ff6 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -247,6 +247,7 @@ bool GasKinetics::addReaction(shared_ptr r) shared_ptr rate=r->reactionRate(); if (rate) { size_t nRxn = nReactions() - 1; + // new generic reaction type handler rate->setIndex(nRxn); if (rate->type() == "ArrheniusRate") { @@ -257,8 +258,6 @@ bool GasKinetics::addReaction(shared_ptr r) m_func1_rates.push_back(*std::dynamic_pointer_cast(rate)); } else { throw CanteraError("GasKinetics::addReaction", "No longer used."); - //m_rxn_indices[nRxn] = m_rxn_rates.size(); - //m_rxn_rates.push_back(rate); } } else if (r->type() == "elementary") { addElementaryReaction(dynamic_cast(*r)); diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index fd24c981f5..7e6af7f0f7 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -10,8 +10,8 @@ namespace Cantera { -void CustomFunc1Data::update(const ThermoPhase& bulk_phase) { - m_temperature = bulk_phase.temperature(); +void CustomFunc1Data::update(const ThermoPhase& bulk) { + m_temperature = bulk.temperature(); } CustomFunc1Rate::CustomFunc1Rate() : m_ratefunc(0) {} @@ -27,8 +27,8 @@ double CustomFunc1Rate::eval(const CustomFunc1Data& shared_data) const { return 0.; } -void ArrheniusData::update(const ThermoPhase& bulk_phase) { - m_temperature = bulk_phase.temperature(); +void ArrheniusData::update(const ThermoPhase& bulk) { + m_temperature = bulk.temperature(); m_logT = std::log(m_temperature); m_recipT = 1./m_temperature; } From 1b15047a7eaa249bfb6ba647673bbc67f345055f Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 14 Mar 2021 17:07:37 -0500 Subject: [PATCH 60/67] [Kinetics] switch to MultiRateBase framework --- include/cantera/kinetics/BulkKinetics.h | 2 + include/cantera/kinetics/GasKinetics.h | 20 ------ include/cantera/kinetics/MultiRate.h | 14 ++-- include/cantera/kinetics/ReactionRate.h | 9 --- src/kinetics/BulkKinetics.cpp | 26 +++++-- src/kinetics/GasKinetics.cpp | 95 ++++--------------------- 6 files changed, 46 insertions(+), 120 deletions(-) diff --git a/include/cantera/kinetics/BulkKinetics.h b/include/cantera/kinetics/BulkKinetics.h index 25087f7884..a778e199e1 100644 --- a/include/cantera/kinetics/BulkKinetics.h +++ b/include/cantera/kinetics/BulkKinetics.h @@ -38,6 +38,8 @@ class BulkKinetics : public Kinetics bool doIrreversible = false); virtual bool addReaction(shared_ptr r); + virtual void modifyReaction(size_t i, shared_ptr rNew); + virtual void resizeSpecies(); virtual void setMultiplier(size_t i, double f); diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index 033d6f2568..01aada38ed 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -89,19 +89,6 @@ class GasKinetics : public BulkKinetics Rate1 m_plog_rates; Rate1 m_cheb_rates; - //! Vector of generic reaction rates - std::vector> m_rxn_rates; - - //! Vector of specialized arrhenius reaction rates - std::vector m_arrhenius_rates; - - //! Vector of specialized custom reaction rates - std::vector m_func1_rates; - - std::map m_rxn_indices; - std::map m_arrhenius_indices; - std::map m_func1_indices; - //! @name Reaction rate data //!@{ doublereal m_logp_ref; @@ -129,13 +116,6 @@ class GasKinetics : public BulkKinetics void modifyPlogReaction(size_t i, PlogReaction& r); void modifyChebyshevReaction(size_t i, ChebyshevReaction& r); - //! @internal May be changed without notice in future versions - void modifyReactionRate(size_t i, shared_ptr rate); - //! @internal May be changed without notice in future versions - void modifyArrheniusRate(size_t i, shared_ptr rate); - //! @internal May be changed without notice in future versions - void modifyCustomFunc1Rate(size_t i, shared_ptr rate); - //! Update the equilibrium constants in molar units. void updateKc(); }; diff --git a/include/cantera/kinetics/MultiRate.h b/include/cantera/kinetics/MultiRate.h index 7f88408ac8..25bb1d96a6 100644 --- a/include/cantera/kinetics/MultiRate.h +++ b/include/cantera/kinetics/MultiRate.h @@ -39,9 +39,9 @@ class MultiRateBase virtual void add(const size_t rxn_index, const shared_ptr rate) = 0; - //! Modify reaction rate object handled by the evaluator - virtual bool modify(const size_t rxn_index, - const shared_ptr rate) = 0; + //! Replace reaction rate object handled by the evaluator + virtual bool replace(const size_t rxn_index, + const shared_ptr rate) = 0; //! Evaluate all rate constants handled by the evaluator virtual void getRateConstants(const ThermoPhase& bulk_phase, @@ -58,7 +58,7 @@ class MultiRateBase * may be changed or removed without notice. */ template -class MultiBulkRates : public MultiRateBase +class MultiBulkRates final : public MultiRateBase { public: virtual void add(const size_t rxn_index, @@ -68,10 +68,10 @@ class MultiBulkRates : public MultiRateBase m_rxn.push_back(rxn_index); } - virtual bool modify(const size_t rxn_index, - const shared_ptr rate) override { + virtual bool replace(const size_t rxn_index, + const shared_ptr rate) override { if (rate->type() != RateType::staticType()) { - throw CanteraError("MultiBulkRate::modify", + throw CanteraError("MultiBulkRate::replace", "Invalid operation: cannot replace rate object of type '{}' " "with a new rate of type '{}'.", RateType::staticType(), rate->type()); } diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 5089c366b0..b789cca2c4 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -61,15 +61,6 @@ class ReactionRateBase //! Evaluate reaction rate derivative based on bulk phase virtual double ddT(const ThermoPhase& bulk) const = 0; - - //! Index or reaction within kinetics - size_t index() { return m_index; } - - //! Index or reaction within kinetics - void setIndex(size_t index) { m_index = index; } - -protected: - size_t m_index = npos; //!< reaction index }; diff --git a/src/kinetics/BulkKinetics.cpp b/src/kinetics/BulkKinetics.cpp index 85c7e4ccaf..60961eec14 100644 --- a/src/kinetics/BulkKinetics.cpp +++ b/src/kinetics/BulkKinetics.cpp @@ -123,9 +123,8 @@ bool BulkKinetics::addReaction(shared_ptr r) shared_ptr rate=r->reactionRate(); if (rate) { - // If neccessary, add new MultiBulkRates evaluator - if (m_bulk_types.find(rate->type()) != m_bulk_types.end()) { + if (m_bulk_types.find(rate->type()) == m_bulk_types.end()) { m_bulk_types[rate->type()] = m_bulk_rates.size(); if (rate->type() == "ArrheniusRate") { @@ -138,8 +137,8 @@ bool BulkKinetics::addReaction(shared_ptr r) } // Add reaction rate to evaluator - //size_t index = m_bulk_types[rate->type()]; - //m_bulk_rates[index].add(nReactions() - 1, rate) + size_t index = m_bulk_types[rate->type()]; + m_bulk_rates[index]->add(nReactions() - 1, rate); } return true; @@ -150,6 +149,25 @@ void BulkKinetics::addElementaryReaction(ElementaryReaction& r) m_rates.install(nReactions()-1, r.rate); } +void BulkKinetics::modifyReaction(size_t i, shared_ptr rNew) +{ + // operations common to all reaction types + Kinetics::modifyReaction(i, rNew); + + shared_ptr rate=rNew->reactionRate(); + if (rate) { + // Ensure that MultiBulkRates evaluator is available + if (m_bulk_types.find(rate->type()) != m_bulk_types.end()) { + throw CanteraError("BulkKinetics::modifyReaction", + "Evaluator not available for type '{}'.", rate->type()); + } + + // Replace reaction rate to evaluator + size_t index = m_bulk_types[rate->type()]; + m_bulk_rates[index]->replace(i, rate); + } +} + void BulkKinetics::modifyElementaryReaction(size_t i, ElementaryReaction& rNew) { m_rates.replace(i, rNew.rate); diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 85ce633ff6..56c73a4e19 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -48,14 +48,10 @@ void GasKinetics::update_rates_T() if (T != m_temp || P != m_pres) { - for (auto& rate : m_arrhenius_rates) { - // generic reaction rates - m_rfn.data()[rate.index()] = rate.eval(arrhenius_shared); - } - - for (auto& rate : m_func1_rates) { - // generic reaction rates - m_rfn.data()[rate.index()] = rate.eval(func1_shared); + // loop over MultiBulkRates evaluators + for (auto& rates : m_bulk_rates) { + rates->update(thermo()); + rates->getRateConstants(thermo(), m_rfn.data()); } if (m_plog_rates.nReactions()) { @@ -242,24 +238,12 @@ bool GasKinetics::addReaction(shared_ptr r) bool added = BulkKinetics::addReaction(r); if (!added) { return false; + } else if (r->reactionRate()) { + // Rate object already added in BulkKinetics::addReaction + return true; } - shared_ptr rate=r->reactionRate(); - if (rate) { - size_t nRxn = nReactions() - 1; - - // new generic reaction type handler - rate->setIndex(nRxn); - if (rate->type() == "ArrheniusRate") { - m_arrhenius_indices[nRxn] = m_arrhenius_rates.size(); - m_arrhenius_rates.push_back(*std::dynamic_pointer_cast(rate)); - } else if (rate->type() == "custom-function") { - m_func1_indices[nRxn] = m_func1_rates.size(); - m_func1_rates.push_back(*std::dynamic_pointer_cast(rate)); - } else { - throw CanteraError("GasKinetics::addReaction", "No longer used."); - } - } else if (r->type() == "elementary") { + if (r->type() == "elementary") { addElementaryReaction(dynamic_cast(*r)); } else if (r->type() == "three-body") { addThreeBodyReaction(dynamic_cast(*r)); @@ -344,20 +328,15 @@ void GasKinetics::addChebyshevReaction(ChebyshevReaction& r) void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) { - // operations common to all reaction types + // operations common to all bulk reaction types BulkKinetics::modifyReaction(i, rNew); - shared_ptr rate=rNew->reactionRate(); - if (rate) { - // new generic reaction type handler - if (rate->type() == "ArrheniusRate") { - modifyArrheniusRate(i, std::dynamic_pointer_cast(rate)); - } else if (rate->type() == "custom-function") { - modifyCustomFunc1Rate(i, std::dynamic_pointer_cast(rate)); - } else { - modifyReactionRate(i, rate); - } - } else if (rNew->type() == "elementary") { + if (rNew->reactionRate()) { + // Rate object already modified in BulkKinetics::modifyReaction + return; + } + + if (rNew->type() == "elementary") { modifyElementaryReaction(i, dynamic_cast(*rNew)); } else if (rNew->type() == "three-body") { modifyThreeBodyReaction(i, dynamic_cast(*rNew)); @@ -403,50 +382,6 @@ void GasKinetics::modifyChebyshevReaction(size_t i, ChebyshevReaction& r) m_cheb_rates.replace(i, r.rate); } -void GasKinetics::modifyReactionRate(size_t i, shared_ptr newRate) -{ - if (m_rxn_indices.find(i) != m_rxn_indices.end()) { - - size_t j = m_rxn_indices[i]; - if (newRate->type() != m_rxn_rates[j]->type()) { - throw CanteraError("GasKinetics::modifyReactionRate", - "Attempting to replace '{}' with '{}'.", - m_rxn_rates[j]->type(), newRate->type()); - } - newRate->setIndex(m_rxn_rates[j]->index()); - m_rxn_rates[j] = newRate; - } else { - throw CanteraError("GasKinetics::modifyReactionRate", - "Index {} does not exist.", i); - } -} - -void GasKinetics::modifyArrheniusRate(size_t i, - shared_ptr newRate) -{ - if (m_arrhenius_indices.find(i) != m_arrhenius_indices.end()) { - size_t j = m_arrhenius_indices[i]; - newRate->setIndex(m_arrhenius_rates[j].index()); - m_arrhenius_rates[j] = *newRate; - } else { - throw CanteraError("GasKinetics::modifyArrheniusRate", - "Index {} does not exist.", i); - } -} - -void GasKinetics::modifyCustomFunc1Rate(size_t i, - shared_ptr newRate) -{ - if (m_func1_indices.find(i) != m_func1_indices.end()) { - size_t j = m_func1_indices[i]; - newRate->setIndex(m_func1_rates[j].index()); - m_func1_rates[j] = *newRate; - } else { - throw CanteraError("GasKinetics::modifyCustomFunc1Rate", - "Index {} does not exist.", i); - } -} - void GasKinetics::init() { BulkKinetics::init(); From d046a0f51b5170ac60827a1ae80310dbc05ad346 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 14 Mar 2021 20:45:57 -0500 Subject: [PATCH 61/67] [Kinetics] minor updates --- include/cantera/kinetics/GasKinetics.h | 1 - src/kinetics/GasKinetics.cpp | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index 01aada38ed..1973323a7c 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -98,7 +98,6 @@ class GasKinetics : public BulkKinetics vector_fp m_rfn_high; doublereal m_pres; //!< Last pressure at which rates were evaluated - vector_fp falloff_work; vector_fp concm_3b_values; vector_fp concm_falloff_values; diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 56c73a4e19..323739c408 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -26,9 +26,7 @@ void GasKinetics::update_rates_T() double T = thermo().temperature(); double P = thermo().pressure(); m_logStandConc = log(thermo().standardConcentration()); - ArrheniusData arrhenius_shared(T); - CustomFunc1Data func1_shared(T); - double logT = arrhenius_shared.m_logT; + double logT = log(T); if (T != m_temp) { if (!m_rfn.empty()) { From b3194f6276b54efc8414c96704ea9649b464bcf2 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 15 Mar 2021 08:04:02 -0500 Subject: [PATCH 62/67] [Kinetics] add intermediate class Reaction2 as base for new framework The intermediate class prevents naming clashes between derived classes (e.g. 'rate' members). --- include/cantera/kinetics/Reaction.h | 36 ++++++++++++++++++-------- interfaces/cython/cantera/_cantera.pxd | 11 +++++--- interfaces/cython/cantera/reaction.pyx | 6 ++--- src/kinetics/BulkKinetics.cpp | 10 ++++--- src/kinetics/GasKinetics.cpp | 4 +-- src/kinetics/Reaction.cpp | 8 +++--- 6 files changed, 47 insertions(+), 28 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index ae0cbb0bec..6f17f37464 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -63,15 +63,8 @@ class Reaction //! Set up reaction based on AnyMap node virtual void setup(const AnyMap& node, const Kinetics& kin); - //! Get reaction rate pointer - shared_ptr reactionRate() { - return m_rate; - } - - //! Set reaction rate pointer - void setReactionRate(shared_ptr rate) { - m_rate = rate; - } + //! Flag indicating whether new framework is used + virtual bool newFramework() { return false; } //! Type of the reaction. The valid types are listed in the file, //! reaction_defs.h, with constants ending in `RXN`. @@ -115,7 +108,28 @@ class Reaction protected: //! Flag indicating whether reaction is set up correctly bool m_valid; +}; + +//! An intermediate class used to avoid naming conflicts of 'rate' member +//! variables and getters (see `ElementaryReaction`, `PlogReaction` and +//! `ChebyshevReaction`). +class Reaction2 : public Reaction +{ +public: + virtual bool newFramework() { return true; } + + //! Get reaction rate pointer + shared_ptr rate() { + return m_rate; + } + + //! Set reaction rate pointer + void setRate(shared_ptr rate) { + m_rate = rate; + } + +protected: //! Reaction rate used by generic reactions shared_ptr m_rate; }; @@ -267,7 +281,7 @@ class ChebyshevReaction : public Reaction * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. */ -class CustomFunc1Reaction : public Reaction +class CustomFunc1Reaction : public Reaction2 { public: CustomFunc1Reaction(); @@ -288,7 +302,7 @@ class CustomFunc1Reaction : public Reaction * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. */ -class TestReaction : public Reaction +class TestReaction : public Reaction2 { public: TestReaction(); diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 731ffad703..4b267fa4bc 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -347,8 +347,11 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cbool duplicate cbool allow_nonreactant_orders cbool allow_negative_orders - shared_ptr[CxxReactionRateBase] reactionRate() - void setReactionRate(shared_ptr[CxxReactionRateBase]) + + cdef cppclass CxxReaction2 "Cantera::Reaction2" (CxxReaction): + CxxReaction2() + shared_ptr[CxxReactionRateBase] rate() + void setRate(shared_ptr[CxxReactionRateBase]) cdef cppclass CxxElementaryReaction "Cantera::ElementaryReaction" (CxxReaction): CxxElementaryReaction() @@ -412,10 +415,10 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxChebyshevReaction "Cantera::ChebyshevReaction" (CxxReaction): CxxChebyshevRate rate - cdef cppclass CxxCustomFunc1Reaction "Cantera::CustomFunc1Reaction" (CxxReaction): + cdef cppclass CxxCustomFunc1Reaction "Cantera::CustomFunc1Reaction" (CxxReaction2): CxxCustomFunc1Reaction() - cdef cppclass CxxTestReaction "Cantera::TestReaction" (CxxReaction): + cdef cppclass CxxTestReaction "Cantera::TestReaction" (CxxReaction2): CxxTestReaction() cbool allow_negative_pre_exponential_factor diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 636e15fc44..5cd3d96c23 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -934,7 +934,7 @@ cdef class CustomReaction(Reaction): def __set__(self, CustomRate rate): self._rate = rate cdef CxxCustomFunc1Reaction* r = self.reaction - r.setReactionRate(self._rate._base) + r.setRate(self._rate._base) cdef class TestReaction(Reaction): @@ -980,10 +980,10 @@ cdef class TestReaction(Reaction): """ Get/Set the `Arrhenius` rate coefficient for this reaction. """ def __get__(self): cdef CxxTestReaction* r = self.reaction - return ArrheniusRate.wrap(r.reactionRate()) + return ArrheniusRate.wrap(r.rate()) def __set__(self, ArrheniusRate rate): cdef CxxTestReaction* r = self.reaction - r.setReactionRate(rate._base) + r.setRate(rate._base) property allow_negative_pre_exponential_factor: """ diff --git a/src/kinetics/BulkKinetics.cpp b/src/kinetics/BulkKinetics.cpp index 60961eec14..89a1daf8f6 100644 --- a/src/kinetics/BulkKinetics.cpp +++ b/src/kinetics/BulkKinetics.cpp @@ -121,8 +121,9 @@ bool BulkKinetics::addReaction(shared_ptr r) m_irrev.push_back(nReactions()-1); } - shared_ptr rate=r->reactionRate(); - if (rate) { + if (r->newFramework()) { + shared_ptr rate; + rate = std::dynamic_pointer_cast(r)->rate(); // If neccessary, add new MultiBulkRates evaluator if (m_bulk_types.find(rate->type()) == m_bulk_types.end()) { m_bulk_types[rate->type()] = m_bulk_rates.size(); @@ -154,8 +155,9 @@ void BulkKinetics::modifyReaction(size_t i, shared_ptr rNew) // operations common to all reaction types Kinetics::modifyReaction(i, rNew); - shared_ptr rate=rNew->reactionRate(); - if (rate) { + if (rNew->newFramework()) { + shared_ptr rate; + rate = std::dynamic_pointer_cast(rNew)->rate(); // Ensure that MultiBulkRates evaluator is available if (m_bulk_types.find(rate->type()) != m_bulk_types.end()) { throw CanteraError("BulkKinetics::modifyReaction", diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 323739c408..fd08b42751 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -236,7 +236,7 @@ bool GasKinetics::addReaction(shared_ptr r) bool added = BulkKinetics::addReaction(r); if (!added) { return false; - } else if (r->reactionRate()) { + } else if (r->newFramework()) { // Rate object already added in BulkKinetics::addReaction return true; } @@ -329,7 +329,7 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) // operations common to all bulk reaction types BulkKinetics::modifyReaction(i, rNew); - if (rNew->reactionRate()) { + if (rNew->newFramework()) { // Rate object already modified in BulkKinetics::modifyReaction return; } diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 5904e51e86..bd97d7e866 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -321,7 +321,7 @@ ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, } CustomFunc1Reaction::CustomFunc1Reaction() - : Reaction() + : Reaction2() { reaction_type = CUSTOMPY_RXN; } @@ -330,12 +330,12 @@ void CustomFunc1Reaction::setup(const AnyMap& node, const Kinetics& kin) { Reaction::setup(node, kin); Units rc_units; // @todo Not needed once `rate_units` is implemented. - setReactionRate( + setRate( std::shared_ptr(new CustomFunc1Rate(node, rc_units))); } TestReaction::TestReaction() - : Reaction() + : Reaction2() , allow_negative_pre_exponential_factor(false) { } @@ -347,7 +347,7 @@ void TestReaction::setup(const AnyMap& node, const Kinetics& kin) // @todo Rate units will become available as `rate_units` after // serialization is fully implemented. Units rc_units = rateCoeffUnits(*this, kin); - setReactionRate( + setRate( std::shared_ptr(new ArrheniusRate(node, rc_units))); allow_negative_pre_exponential_factor = node.getBool("negative-A", false); } From a69c25f8846eac679294432c0ad9c18293fa04a3 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Thu, 18 Mar 2021 15:45:56 -0500 Subject: [PATCH 63/67] [Func1] roll back blocking method callbacks --- interfaces/cython/cantera/func1.pyx | 3 --- 1 file changed, 3 deletions(-) diff --git a/interfaces/cython/cantera/func1.pyx b/interfaces/cython/cantera/func1.pyx index 12ad683674..9315576998 100644 --- a/interfaces/cython/cantera/func1.pyx +++ b/interfaces/cython/cantera/func1.pyx @@ -63,9 +63,6 @@ cdef class Func1: def __init__(self, c): if hasattr(c, '__call__'): - if type(c).__name__ == 'method': - raise TypeError("Unable to use callback from 'method'.") - # callback function self._set_callback(c) else: From 8f785051aa9fcf9a49af78e3c61a5901c314d251 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Thu, 18 Mar 2021 22:12:38 -0500 Subject: [PATCH 64/67] [Kinetics] implement review comments --- include/cantera/kinetics/MultiRate.h | 21 ++++++++++++------- include/cantera/kinetics/Reaction.h | 11 +++------- include/cantera/kinetics/ReactionRate.h | 15 +++---------- interfaces/cython/cantera/reaction.pyx | 2 +- .../cython/cantera/test/test_reaction.py | 12 +++++++++++ src/kinetics/BulkKinetics.cpp | 8 +++---- src/kinetics/GasKinetics.cpp | 4 ++-- src/kinetics/Reaction.cpp | 6 +++--- src/kinetics/ReactionFactory.cpp | 5 ++--- src/kinetics/ReactionRate.cpp | 5 +++-- 10 files changed, 46 insertions(+), 43 deletions(-) diff --git a/include/cantera/kinetics/MultiRate.h b/include/cantera/kinetics/MultiRate.h index 25bb1d96a6..fbd8549e82 100644 --- a/include/cantera/kinetics/MultiRate.h +++ b/include/cantera/kinetics/MultiRate.h @@ -37,11 +37,11 @@ class MultiRateBase //! Add reaction rate object to the evaluator virtual void add(const size_t rxn_index, - const shared_ptr rate) = 0; + ReactionRateBase& rate) = 0; //! Replace reaction rate object handled by the evaluator virtual bool replace(const size_t rxn_index, - const shared_ptr rate) = 0; + ReactionRateBase& rate) = 0; //! Evaluate all rate constants handled by the evaluator virtual void getRateConstants(const ThermoPhase& bulk_phase, @@ -62,22 +62,27 @@ class MultiBulkRates final : public MultiRateBase { public: virtual void add(const size_t rxn_index, - const shared_ptr rate) override { + ReactionRateBase& rate) override { m_indices[rxn_index] = m_rates.size(); - m_rates.push_back(*std::dynamic_pointer_cast(rate)); + m_rates.push_back(dynamic_cast(rate)); m_rxn.push_back(rxn_index); } virtual bool replace(const size_t rxn_index, - const shared_ptr rate) override { - if (rate->type() != RateType::staticType()) { + ReactionRateBase& rate) override { + if (!m_rates.size()) { + throw CanteraError("MultiBulkRate::replace", + "Invalid operation: cannot replace rate object " + "in empty rate handler."); + } else if (typeid(rate) != typeid(RateType)) { throw CanteraError("MultiBulkRate::replace", "Invalid operation: cannot replace rate object of type '{}' " - "with a new rate of type '{}'.", RateType::staticType(), rate->type()); + "with a new rate of type '{}'.", + m_rates[0].type(), rate.type()); } if (m_indices.find(rxn_index) != m_indices.end()) { size_t j = m_indices[rxn_index]; - m_rates[j] = *std::dynamic_pointer_cast(rate); + m_rates[j] = dynamic_cast(rate); return true; } return false; diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 6f17f37464..9ee2b381e9 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -60,12 +60,6 @@ class Reaction m_valid = valid; } - //! Set up reaction based on AnyMap node - virtual void setup(const AnyMap& node, const Kinetics& kin); - - //! Flag indicating whether new framework is used - virtual bool newFramework() { return false; } - //! Type of the reaction. The valid types are listed in the file, //! reaction_defs.h, with constants ending in `RXN`. /*! @@ -117,8 +111,6 @@ class Reaction class Reaction2 : public Reaction { public: - virtual bool newFramework() { return true; } - //! Get reaction rate pointer shared_ptr rate() { return m_rate; @@ -129,6 +121,9 @@ class Reaction2 : public Reaction m_rate = rate; } + //! Set up reaction based on AnyMap node + virtual void setup(const AnyMap& node, const Kinetics& kin); + protected: //! Reaction rate used by generic reactions shared_ptr m_rate; diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index b789cca2c4..43fe8a3a8a 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -67,7 +67,7 @@ class ReactionRateBase //! Class template for reaction rate definitions with specialized DataType /** * This class template ensures that derived objects are aware of specialized - * data types, which are passed by `MultiRateBase` evaluators. + * data types, which are passed by MultiRateBase evaluators. * * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. @@ -76,9 +76,6 @@ template class ReactionRate : public ReactionRateBase { public: - //! Constructor - ReactionRate() = default; - //! Update information specific to reaction virtual void update(const DataType& shared_data, const ThermoPhase& bulk) {} @@ -169,10 +166,7 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius ArrheniusRate(const AnyMap& node, const Units& rc_units); - //! @internal access to reaction type is used by `MultiRateBase` evaluator - static std::string staticType() { return "ArrheniusRate"; } - - virtual std::string type() const override { return staticType(); } + virtual std::string type() const override { return "ArrheniusRate"; } //! Update information specific to reaction static bool uses_update() { return false; } @@ -229,10 +223,7 @@ class CustomFunc1Rate final : public ReactionRate // Does nothing, as there is no formalized parameterization. CustomFunc1Rate(const AnyMap& rate, const Units& rc_units) {} - //! @internal access to reaction type is used by `MultiRateBase` evaluator - static std::string staticType() { return "custom-function"; } - - virtual std::string type() const override { return staticType(); } + virtual std::string type() const override { return "custom-function"; } //! Update information specific to reaction static bool uses_update() { return false; } diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 5cd3d96c23..a7aba65163 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -1008,7 +1008,7 @@ cdef class InterfaceReaction(ElementaryReaction): dependent on surface species coverages. The keys of the dict are species names, and the values are tuples specifying the three coverage parameters ``(a, m, E)`` which are the modifiers for the pre-exponential - factor [m, kmol, s units], The temperature exponent [nondimensional], + factor [m, kmol, s units], the temperature exponent [nondimensional], and the activation energy [J/kmol], respectively. """ def __get__(self): diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index adbeff2600..741c5ee7e4 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -101,6 +101,18 @@ def setUp(self): # need to overwrite rate to ensure correct type ('method' is not compatible with Func1) self._rate = lambda T: 38.7 * T**2.7 * exp(-3150.15428/T) + def test_no_rate(self): + rxn = self._cls(equation=self._equation, kinetics=self.gas) + with self.assertRaisesRegex(ct.CanteraError, "Custom rate function is not initialized."): + rxn.rate(self.gas.T) + + gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', + species=self.species, reactions=[rxn]) + gas2.TPX = self.gas.TPX + + with self.assertRaisesRegex(ct.CanteraError, "Custom rate function is not initialized."): + gas2.forward_rate_constants + def test_from_func(self): f = ct.Func1(self._rate) rxn = ct.CustomReaction(equation=self._equation, rate=f, kinetics=self.gas) diff --git a/src/kinetics/BulkKinetics.cpp b/src/kinetics/BulkKinetics.cpp index 89a1daf8f6..f85dcba818 100644 --- a/src/kinetics/BulkKinetics.cpp +++ b/src/kinetics/BulkKinetics.cpp @@ -121,7 +121,7 @@ bool BulkKinetics::addReaction(shared_ptr r) m_irrev.push_back(nReactions()-1); } - if (r->newFramework()) { + if (std::dynamic_pointer_cast(r) != nullptr) { shared_ptr rate; rate = std::dynamic_pointer_cast(r)->rate(); // If neccessary, add new MultiBulkRates evaluator @@ -139,7 +139,7 @@ bool BulkKinetics::addReaction(shared_ptr r) // Add reaction rate to evaluator size_t index = m_bulk_types[rate->type()]; - m_bulk_rates[index]->add(nReactions() - 1, rate); + m_bulk_rates[index]->add(nReactions() - 1, *rate); } return true; @@ -155,7 +155,7 @@ void BulkKinetics::modifyReaction(size_t i, shared_ptr rNew) // operations common to all reaction types Kinetics::modifyReaction(i, rNew); - if (rNew->newFramework()) { + if (std::dynamic_pointer_cast(rNew) != nullptr) { shared_ptr rate; rate = std::dynamic_pointer_cast(rNew)->rate(); // Ensure that MultiBulkRates evaluator is available @@ -166,7 +166,7 @@ void BulkKinetics::modifyReaction(size_t i, shared_ptr rNew) // Replace reaction rate to evaluator size_t index = m_bulk_types[rate->type()]; - m_bulk_rates[index]->replace(i, rate); + m_bulk_rates[index]->replace(i, *rate); } } diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index fd08b42751..ef008ab112 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -236,7 +236,7 @@ bool GasKinetics::addReaction(shared_ptr r) bool added = BulkKinetics::addReaction(r); if (!added) { return false; - } else if (r->newFramework()) { + } else if (std::dynamic_pointer_cast(r) != nullptr) { // Rate object already added in BulkKinetics::addReaction return true; } @@ -329,7 +329,7 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) // operations common to all bulk reaction types BulkKinetics::modifyReaction(i, rNew); - if (rNew->newFramework()) { + if (std::dynamic_pointer_cast(rNew) != nullptr) { // Rate object already modified in BulkKinetics::modifyReaction return; } diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index bd97d7e866..7447250399 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -98,7 +98,7 @@ void Reaction::validate() } } -void Reaction::setup(const AnyMap& node, const Kinetics& kin) +void Reaction2::setup(const AnyMap& node, const Kinetics& kin) { parseReactionEquation(*this, node["equation"], kin); // Non-stoichiometric reaction orders @@ -328,7 +328,7 @@ CustomFunc1Reaction::CustomFunc1Reaction() void CustomFunc1Reaction::setup(const AnyMap& node, const Kinetics& kin) { - Reaction::setup(node, kin); + Reaction2::setup(node, kin); Units rc_units; // @todo Not needed once `rate_units` is implemented. setRate( std::shared_ptr(new CustomFunc1Rate(node, rc_units))); @@ -342,7 +342,7 @@ TestReaction::TestReaction() void TestReaction::setup(const AnyMap& node, const Kinetics& kin) { - Reaction::setup(node, kin); + Reaction2::setup(node, kin); // @todo Rate units will become available as `rate_units` after // serialization is fully implemented. diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index e8b7da4313..50861187f5 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -105,7 +105,7 @@ ReactionFactory::ReactionFactory() }); reg_AnyMap("custom-rate-function", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - R->setup(node, kin); + ((Reaction2*)R)->setup(node, kin); }); // register custom Python reactions @@ -117,14 +117,13 @@ ReactionFactory::ReactionFactory() }); reg_AnyMap("elementary-new", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - R->setup(node, kin); + (dynamic_cast(R))->setup(node, kin); }); // register interface reactions reg("interface", []() { return new InterfaceReaction(); }); addAlias("interface", "surface"); addAlias("interface", "edge"); - addAlias("interface", "global"); reg_XML("interface", [](Reaction* R, const XML_Node& node) { setupInterfaceReaction(*(InterfaceReaction*)R, node); diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 7e6af7f0f7..196e4c31de 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -1,4 +1,4 @@ -//! @file RxnRates.cpp +//! @file ReactionRate.cpp // This file is part of Cantera. See License.txt in the top-level directory or // at https://cantera.org/license.txt for license and copyright information. @@ -24,7 +24,8 @@ double CustomFunc1Rate::eval(const CustomFunc1Data& shared_data) const { if (m_ratefunc) { return m_ratefunc->eval(shared_data.m_temperature); } - return 0.; + throw CanteraError("CustomFunc1Rate::eval", + "Custom rate function is not initialized."); } void ArrheniusData::update(const ThermoPhase& bulk) { From 5fb18133a2e0223fad67cfd94aef4b9be16b7829 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 19 Mar 2021 00:48:14 -0500 Subject: [PATCH 65/67] [Kinetics] finalize ReactionRate and corresponding data types --- include/cantera/kinetics/MultiRate.h | 9 ++- include/cantera/kinetics/ReactionData.h | 82 ++++++++++++++++++++++ include/cantera/kinetics/ReactionRate.h | 90 +++++++------------------ src/kinetics/ReactionData.cpp | 20 ++++++ src/kinetics/ReactionRate.cpp | 24 ++----- 5 files changed, 143 insertions(+), 82 deletions(-) create mode 100644 include/cantera/kinetics/ReactionData.h create mode 100644 src/kinetics/ReactionData.cpp diff --git a/include/cantera/kinetics/MultiRate.h b/include/cantera/kinetics/MultiRate.h index fbd8549e82..e2fad2002d 100644 --- a/include/cantera/kinetics/MultiRate.h +++ b/include/cantera/kinetics/MultiRate.h @@ -36,18 +36,25 @@ class MultiRateBase virtual ~MultiRateBase() {} //! Add reaction rate object to the evaluator + //! @param rxn_index index of reaction + //! @param rate reaction rate object virtual void add(const size_t rxn_index, ReactionRateBase& rate) = 0; //! Replace reaction rate object handled by the evaluator + //! @param rxn_index index of reaction + //! @param rate reaction rate object virtual bool replace(const size_t rxn_index, ReactionRateBase& rate) = 0; //! Evaluate all rate constants handled by the evaluator - virtual void getRateConstants(const ThermoPhase& bulk_phase, + //! @param bulk object representing bulk phase + //! @param kf array of rate constants + virtual void getRateConstants(const ThermoPhase& bulk, double* kf) const = 0; //! Update data common to reaction rates of a specific type + //! @param bulk object representing bulk phase virtual void update(const ThermoPhase& bulk) = 0; }; diff --git a/include/cantera/kinetics/ReactionData.h b/include/cantera/kinetics/ReactionData.h new file mode 100644 index 0000000000..89da9c51e3 --- /dev/null +++ b/include/cantera/kinetics/ReactionData.h @@ -0,0 +1,82 @@ +/** + * @file ReactionData.h + * + * @warning This file is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ + +// This file is part of Cantera. See License.txt in the top-level directory or +// at https://cantera.org/license.txt for license and copyright information. + +#ifndef CT_REACTIONDATA_H +#define CT_REACTIONDATA_H + +namespace Cantera +{ + +class ThermoPhase; + + +//! Data container holding shared data specific to ArrheniusRate +/** + * The data container `ArrheniusData` holds precalculated data common to + * all `ArrheniusRate` objects. + * + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ +struct ArrheniusData +{ + ArrheniusData() : m_temperature(1.), m_logT(0.), m_recipT(1.) {} + + //! Constructor based on temperature *T* + ArrheniusData(double T) { update(T); } + + //! Constructor based on temperature *T* and pressure *P* + ArrheniusData(double T, double P) { update(T); }; + + //! Constructor accessing *bulk* phase definitions + ArrheniusData(const ThermoPhase& bulk) { update(bulk); } + + void update(double T) { + m_temperature = T; + m_logT = std::log(T); + m_recipT = 1./T; + } + + void update(const ThermoPhase& bulk); + + double m_temperature; //!< temperature + double m_logT; //!< logarithm of temperature + double m_recipT; //!< inverse of temperature +}; + + +//! Data container holding shared data specific to CustomFunc1Rate +/** + * @warning This class is an experimental part of the %Cantera API and + * may be changed or removed without notice. + */ +struct CustomFunc1Data +{ + CustomFunc1Data() : m_temperature(1.) {} + + //! Constructor based on temperature *T* + CustomFunc1Data(double T) { update(T); } + + //! Constructor based on temperature *T* and pressure *P* + CustomFunc1Data(double T, double P) { update(T); }; + + //! Constructor accessing *bulk* phase definitions + CustomFunc1Data(const ThermoPhase& bulk) { update(bulk); } + + void update(double T) { m_temperature = T; } + + void update(const ThermoPhase& bulk); + + double m_temperature; //!< temperature +}; + +} + +#endif diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 43fe8a3a8a..927986f45b 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -12,13 +12,13 @@ #define CT_REACTIONRATE_H #include "cantera/kinetics/RxnRates.h" +#include "cantera/kinetics/ReactionData.h" #include "cantera/base/ctexceptions.h" namespace Cantera { class Func1; -class ThermoPhase; class AnyMap; @@ -45,21 +45,29 @@ class ReactionRateBase virtual std::string type() const = 0; //! Evaluate reaction rate based on temperature + //! @param T temperature [K] virtual double eval(double T) const = 0; //! Evaluate reaction rate based on temperature and pressure + //! @param T temperature [K] + //! @param P pressure [Pa] virtual double eval(double T, double P) const = 0; //! Evaluate reaction rate based on bulk phase + //! @param bulk object representing bulk phase virtual double eval(const ThermoPhase& bulk) const = 0; //! Evaluate reaction rate derivative based on temperature + //! @param T temperature [K] virtual double ddT(double T) const = 0; //! Evaluate reaction rate derivative based on temperature and pressure + //! @param T temperature [K] + //! @param P pressure [Pa] virtual double ddT(double T, double P) const = 0; //! Evaluate reaction rate derivative based on bulk phase + //! @param bulk object representing bulk phase virtual double ddT(const ThermoPhase& bulk) const = 0; }; @@ -76,10 +84,19 @@ template class ReactionRate : public ReactionRateBase { public: + ReactionRate() = default; + + ReactionRate(const AnyMap& node, const Units& rc_units) { + throw CanteraError("ReactionRate::ReactionRate", + "Constructor needs to be implemented by derived classes."); + } + //! Update information specific to reaction + //! @param shared_data data shared by all reactions of a given type virtual void update(const DataType& shared_data, const ThermoPhase& bulk) {} //! Evaluate reaction rate + //! @param shared_data data shared by all reactions of a given type virtual double eval(const DataType& shared_data) const = 0; virtual double eval(double T) const override { @@ -95,6 +112,7 @@ class ReactionRate : public ReactionRateBase } //! Evaluate derivative of reaction rate with respect to temperature + //! @param shared_data data shared by all reactions of a given type virtual double ddT(const DataType& shared_data) const { throw CanteraError("ReactionRate::ddT", "Not implemented by derived ReactionRate object."); @@ -108,44 +126,9 @@ class ReactionRate : public ReactionRateBase return ddT(DataType(T, P)); } - virtual double ddT(const ThermoPhase& bulk) const { + virtual double ddT(const ThermoPhase& bulk) const override { return ddT(DataType(bulk)); } - - // [...] signatures for derivative with respect to pressure and concentration - // are not created (yet) -}; - - -//! Data container holding shared data specific to `ArrheniusRate` -/** - * The data container `ArrheniusData` holds precalculated data common to - * all `ArrheniusRate` objects. - * - * @warning This class is an experimental part of the %Cantera API and - * may be changed or removed without notice. - */ -struct ArrheniusData { - ArrheniusData() : m_temperature(1.), m_logT(0.), m_recipT(1.) {} - - //! Constructor based on temperature *T* - ArrheniusData(double T) : m_temperature(T) { - m_logT = std::log(T); - m_recipT = 1./T; - } - - //! Constructor based on temperature *T* - ArrheniusData(double T, double P) : ArrheniusData(T) {} - - //! Constructor accessing bulk phase definitions - ArrheniusData(const ThermoPhase& bulk) { update(bulk); } - - //! Update based on bulk phase definitions - void update(const ThermoPhase& bulk); - - double m_temperature; - double m_logT; - double m_recipT; }; @@ -159,11 +142,13 @@ struct ArrheniusData { class ArrheniusRate final : public ReactionRate, public Arrhenius { public: - //! Constructor. ArrheniusRate(); ArrheniusRate(double A, double b, double E); + //! Constructor + //! @param node AnyMap object containing reaction rate specification + //! @param rc_units Description of units used for rate parameters ArrheniusRate(const AnyMap& node, const Units& rc_units); virtual std::string type() const override { return "ArrheniusRate"; } @@ -182,30 +167,6 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius }; -//! Data container holding shared data specific to `CustomFunc1Rate` -/** - * @warning This class is an experimental part of the %Cantera API and - * may be changed or removed without notice. - */ -struct CustomFunc1Data { - CustomFunc1Data() : m_temperature(1.) {} - - //! Constructor based on temperature *T* - CustomFunc1Data(double T) : m_temperature(T) {} - - //! Constructor based on temperature *T* - CustomFunc1Data(double T, double P) : CustomFunc1Data(T) {} - - //! Constructor accessing bulk phase definitions - CustomFunc1Data(const ThermoPhase& bulk) { update(bulk); } - - //! Update based on reacting phase definitions - void update(const ThermoPhase& bulk); - - double m_temperature; -}; - - //! Custom reaction rate depending only on temperature /** * The rate expression is provided by a `Func1` object taking a single @@ -217,10 +178,11 @@ struct CustomFunc1Data { class CustomFunc1Rate final : public ReactionRate { public: - //! Constructor. CustomFunc1Rate(); - // Does nothing, as there is no formalized parameterization. + //! Constructor does nothing, as there is no formalized parameterization + //! @param node AnyMap object containing reaction rate specification + //! @param rc_units Description of units used for rate parameters CustomFunc1Rate(const AnyMap& rate, const Units& rc_units) {} virtual std::string type() const override { return "custom-function"; } diff --git a/src/kinetics/ReactionData.cpp b/src/kinetics/ReactionData.cpp new file mode 100644 index 0000000000..07041e33a2 --- /dev/null +++ b/src/kinetics/ReactionData.cpp @@ -0,0 +1,20 @@ +//! @file ReactionData.cpp + +// This file is part of Cantera. See License.txt in the top-level directory or +// at https://cantera.org/license.txt for license and copyright information. + +#include "cantera/kinetics/ReactionData.h" +#include "cantera/thermo/ThermoPhase.h" + +namespace Cantera +{ + +void ArrheniusData::update(const ThermoPhase& bulk) { + update(bulk.temperature()); +} + +void CustomFunc1Data::update(const ThermoPhase& bulk) { + m_temperature = bulk.temperature(); +} + +} diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 196e4c31de..38e897600d 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -5,13 +5,17 @@ #include "cantera/kinetics/ReactionRate.h" #include "cantera/numerics/Func1.h" -#include "cantera/thermo/ThermoPhase.h" +#include "cantera/base/AnyMap.h" namespace Cantera { -void CustomFunc1Data::update(const ThermoPhase& bulk) { - m_temperature = bulk.temperature(); +ArrheniusRate::ArrheniusRate(double A, double b, double E) + : Arrhenius(A, b, E) { +} + +ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rc_units) { + setup(node["rate-constant"], node.units(), rc_units); } CustomFunc1Rate::CustomFunc1Rate() : m_ratefunc(0) {} @@ -28,18 +32,4 @@ double CustomFunc1Rate::eval(const CustomFunc1Data& shared_data) const { "Custom rate function is not initialized."); } -void ArrheniusData::update(const ThermoPhase& bulk) { - m_temperature = bulk.temperature(); - m_logT = std::log(m_temperature); - m_recipT = 1./m_temperature; -} - -ArrheniusRate::ArrheniusRate(double A, double b, double E) - : Arrhenius(A, b, E) { -} - -ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rc_units) { - setup(node["rate-constant"], node.units(), rc_units); -} - } From 5369f51e767968d43e75943fdea7b5194b8a6cdb Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 19 Mar 2021 07:14:17 -0500 Subject: [PATCH 66/67] [Kinetics] simplify Reaction.wrap --- interfaces/cython/cantera/reaction.pyx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index a7aba65163..ee2370c784 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -75,12 +75,13 @@ cdef class Reaction: """ # ensure all reaction types are registered if not(_reaction_class_registry): - def all_subclasses(cls): - return set(cls.__subclasses__()).union( - [s for c in cls.__subclasses__() for s in all_subclasses(c)]) + def register_subclasses(cls): + for c in cls.__subclasses__(): + _reaction_class_registry[getattr(c, 'reaction_type')] = c + register_subclasses(c) + # update global reaction class registry - _reaction_class_registry.update({getattr(c, 'reaction_type'): c - for c in all_subclasses(Reaction)}) + register_subclasses(Reaction) # identify class rxn_type = pystr(reaction.get().type()) From b770450dc000e696cc6091c821a4ddf763f3f1e1 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 19 Mar 2021 07:43:10 -0500 Subject: [PATCH 67/67] [Kinetics] rename Reaction2::setup to Reaction2::setParameters --- include/cantera/kinetics/Reaction.h | 6 +-- include/cantera/kinetics/ReactionRate.h | 13 ++--- include/cantera/kinetics/RxnRates.h | 4 +- src/kinetics/Reaction.cpp | 66 ++++++++++++------------- src/kinetics/ReactionFactory.cpp | 4 +- src/kinetics/ReactionRate.cpp | 4 +- src/kinetics/RxnRates.cpp | 8 +-- 7 files changed, 50 insertions(+), 55 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 9ee2b381e9..f19b3cf69d 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -122,7 +122,7 @@ class Reaction2 : public Reaction } //! Set up reaction based on AnyMap node - virtual void setup(const AnyMap& node, const Kinetics& kin); + virtual void setParameters(const AnyMap& node, const Kinetics& kin); protected: //! Reaction rate used by generic reactions @@ -281,7 +281,7 @@ class CustomFunc1Reaction : public Reaction2 public: CustomFunc1Reaction(); - virtual void setup(const AnyMap& node, const Kinetics& kin); + virtual void setParameters(const AnyMap& node, const Kinetics& kin); virtual std::string type() const { return "custom-rate-function"; @@ -306,7 +306,7 @@ class TestReaction : public Reaction2 return "elementary-new"; } - virtual void setup(const AnyMap& node, const Kinetics& kin); + virtual void setParameters(const AnyMap& node, const Kinetics& kin); bool allow_negative_pre_exponential_factor; }; diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 927986f45b..52635465b8 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -86,11 +86,6 @@ class ReactionRate : public ReactionRateBase public: ReactionRate() = default; - ReactionRate(const AnyMap& node, const Units& rc_units) { - throw CanteraError("ReactionRate::ReactionRate", - "Constructor needs to be implemented by derived classes."); - } - //! Update information specific to reaction //! @param shared_data data shared by all reactions of a given type virtual void update(const DataType& shared_data, const ThermoPhase& bulk) {} @@ -148,8 +143,8 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius //! Constructor //! @param node AnyMap object containing reaction rate specification - //! @param rc_units Description of units used for rate parameters - ArrheniusRate(const AnyMap& node, const Units& rc_units); + //! @param rate_units Description of units used for rate parameters + ArrheniusRate(const AnyMap& node, const Units& rate_units); virtual std::string type() const override { return "ArrheniusRate"; } @@ -182,8 +177,8 @@ class CustomFunc1Rate final : public ReactionRate //! Constructor does nothing, as there is no formalized parameterization //! @param node AnyMap object containing reaction rate specification - //! @param rc_units Description of units used for rate parameters - CustomFunc1Rate(const AnyMap& rate, const Units& rc_units) {} + //! @param rate_units Description of units used for rate parameters + CustomFunc1Rate(const AnyMap& rate, const Units& rate_units) {} virtual std::string type() const override { return "custom-function"; } diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 1e0fbfcdae..7205eb6226 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -42,8 +42,8 @@ class Arrhenius Arrhenius(doublereal A, doublereal b, doublereal E); //! Run object setup based on AnyMap node information - void setup(const AnyValue& rate, - const UnitSystem& units, const Units& rc_units); + void setParameters(const AnyValue& rate, + const UnitSystem& units, const Units& rate_units); //! Update concentration-dependent parts of the rate coefficient. /*! diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 7447250399..23b5047ba7 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -98,29 +98,6 @@ void Reaction::validate() } } -void Reaction2::setup(const AnyMap& node, const Kinetics& kin) -{ - parseReactionEquation(*this, node["equation"], kin); - // Non-stoichiometric reaction orders - std::map orders; - if (node.hasKey("orders")) { - for (const auto& order : node["orders"].asMap()) { - orders[order.first] = order.second; - if (kin.kineticsSpeciesIndex(order.first) == npos) { - setValid(false); - } - } - } - - // Flags - id = node.getString("id", ""); - duplicate = node.getBool("duplicate", false); - allow_negative_orders = node.getBool("negative-orders", false); - allow_nonreactant_orders = node.getBool("nonreactant-orders", false); - - input = node; -} - std::string Reaction::reactantString() const { std::ostringstream result; @@ -320,18 +297,41 @@ ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, reaction_type = CHEBYSHEV_RXN; } +void Reaction2::setParameters(const AnyMap& node, const Kinetics& kin) +{ + parseReactionEquation(*this, node["equation"], kin); + // Non-stoichiometric reaction orders + std::map orders; + if (node.hasKey("orders")) { + for (const auto& order : node["orders"].asMap()) { + orders[order.first] = order.second; + if (kin.kineticsSpeciesIndex(order.first) == npos) { + setValid(false); + } + } + } + + // Flags + id = node.getString("id", ""); + duplicate = node.getBool("duplicate", false); + allow_negative_orders = node.getBool("negative-orders", false); + allow_nonreactant_orders = node.getBool("nonreactant-orders", false); + + input = node; +} + CustomFunc1Reaction::CustomFunc1Reaction() : Reaction2() { reaction_type = CUSTOMPY_RXN; } -void CustomFunc1Reaction::setup(const AnyMap& node, const Kinetics& kin) +void CustomFunc1Reaction::setParameters(const AnyMap& node, const Kinetics& kin) { - Reaction2::setup(node, kin); - Units rc_units; // @todo Not needed once `rate_units` is implemented. + Reaction2::setParameters(node, kin); + Units rate_units; // @todo Not needed once `rate_units` is implemented. setRate( - std::shared_ptr(new CustomFunc1Rate(node, rc_units))); + std::shared_ptr(new CustomFunc1Rate(node, rate_units))); } TestReaction::TestReaction() @@ -340,15 +340,15 @@ TestReaction::TestReaction() { } -void TestReaction::setup(const AnyMap& node, const Kinetics& kin) +void TestReaction::setParameters(const AnyMap& node, const Kinetics& kin) { - Reaction2::setup(node, kin); + Reaction2::setParameters(node, kin); // @todo Rate units will become available as `rate_units` after // serialization is fully implemented. - Units rc_units = rateCoeffUnits(*this, kin); + Units rate_units = rateCoeffUnits(*this, kin); setRate( - std::shared_ptr(new ArrheniusRate(node, rc_units))); + std::shared_ptr(new ArrheniusRate(node, rate_units))); allow_negative_pre_exponential_factor = node.getBool("negative-A", false); } @@ -434,9 +434,9 @@ Arrhenius readArrhenius(const Reaction& R, const AnyValue& rate, const Kinetics& kin, const UnitSystem& units, int pressure_dependence=0) { - Units rc_units = rateCoeffUnits(R, kin, pressure_dependence); + Units rate_units = rateCoeffUnits(R, kin, pressure_dependence); Arrhenius arr; - arr.setup(rate, units, rc_units); + arr.setParameters(rate, units, rate_units); return arr; } diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 50861187f5..fa741741f6 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -105,7 +105,7 @@ ReactionFactory::ReactionFactory() }); reg_AnyMap("custom-rate-function", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - ((Reaction2*)R)->setup(node, kin); + ((Reaction2*)R)->setParameters(node, kin); }); // register custom Python reactions @@ -117,7 +117,7 @@ ReactionFactory::ReactionFactory() }); reg_AnyMap("elementary-new", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - (dynamic_cast(R))->setup(node, kin); + (dynamic_cast(R))->setParameters(node, kin); }); // register interface reactions diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 38e897600d..29485704c9 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -14,8 +14,8 @@ ArrheniusRate::ArrheniusRate(double A, double b, double E) : Arrhenius(A, b, E) { } -ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rc_units) { - setup(node["rate-constant"], node.units(), rc_units); +ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rate_units) { + setParameters(node["rate-constant"], node.units(), rate_units); } CustomFunc1Rate::CustomFunc1Rate() : m_ratefunc(0) {} diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 959ad86a1d..0b72288158 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -29,17 +29,17 @@ Arrhenius::Arrhenius(doublereal A, doublereal b, doublereal E) } } -void Arrhenius::setup(const AnyValue& rate, - const UnitSystem& units, const Units& rc_units) +void Arrhenius::setParameters(const AnyValue& rate, + const UnitSystem& units, const Units& rate_units) { if (rate.is()) { auto& rate_map = rate.as(); - m_A = units.convert(rate_map["A"], rc_units); + m_A = units.convert(rate_map["A"], rate_units); m_b = rate_map["b"].asDouble(); m_E = units.convertActivationEnergy(rate_map["Ea"], "K"); } else { auto& rate_vec = rate.asVector(3); - m_A = units.convert(rate_vec[0], rc_units); + m_A = units.convert(rate_vec[0], rate_units); m_b = rate_vec[1].asDouble(); m_E = units.convertActivationEnergy(rate_vec[2], "K"); }