From 0cb057ad67049800ae0b84821e9e63e7b1b733fc Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 19 Mar 2021 10:37:12 -0500 Subject: [PATCH 01/84] [Kinetics] Remove 'experimental' warnings for new classes --- include/cantera/kinetics/MultiRate.h | 10 ---------- include/cantera/kinetics/ReactionData.h | 10 ---------- include/cantera/kinetics/ReactionRate.h | 12 ------------ interfaces/cython/cantera/reaction.pyx | 6 ------ 4 files changed, 38 deletions(-) diff --git a/include/cantera/kinetics/MultiRate.h b/include/cantera/kinetics/MultiRate.h index e2fad2002d1..97742f0894c 100644 --- a/include/cantera/kinetics/MultiRate.h +++ b/include/cantera/kinetics/MultiRate.h @@ -1,8 +1,5 @@ /** * @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 @@ -26,9 +23,6 @@ namespace Cantera * 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 { @@ -60,10 +54,6 @@ class MultiRateBase //! 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 final : public MultiRateBase { diff --git a/include/cantera/kinetics/ReactionData.h b/include/cantera/kinetics/ReactionData.h index f5ae68317f4..0d561e73a6e 100644 --- a/include/cantera/kinetics/ReactionData.h +++ b/include/cantera/kinetics/ReactionData.h @@ -1,8 +1,5 @@ /** * @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 @@ -23,9 +20,6 @@ class ThermoPhase; /** * 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 { @@ -55,10 +49,6 @@ struct ArrheniusData //! 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.) {} diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 52635465b84..15c3ffbebd4 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -32,9 +32,6 @@ class AnyMap; * 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. */ class ReactionRateBase { @@ -76,9 +73,6 @@ class ReactionRateBase /** * 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. */ template class ReactionRate : public ReactionRateBase @@ -130,9 +124,6 @@ class ReactionRate : public ReactionRateBase //! 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 { @@ -166,9 +157,6 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius /** * 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 { diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 7c31a16319c..77b382b752f 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -447,9 +447,6 @@ cdef class CustomRate(_ReactionRate): 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, k=None, init=True): @@ -489,9 +486,6 @@ cdef class ArrheniusRate(_ReactionRate): 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): From 12543ad5effb4217b2fb854121924670ddf3b4b3 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 19 Mar 2021 15:39:23 -0500 Subject: [PATCH 02/84] [Kinetics] Create ElementaryReaction2 class --- include/cantera/kinetics/Reaction.h | 30 +++++++++++++++++++++- include/cantera/kinetics/ReactionFactory.h | 9 +++++++ include/cantera/kinetics/reaction_defs.h | 5 ---- src/kinetics/GasKinetics.cpp | 2 +- src/kinetics/Reaction.cpp | 30 +++++++++++++++++++++- src/kinetics/ReactionFactory.cpp | 22 ++++++++++------ 6 files changed, 82 insertions(+), 16 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 6922d4930b9..1f4dda3b169 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -157,6 +157,9 @@ class Reaction class Reaction2 : public Reaction { public: + Reaction2() : Reaction() {} + Reaction2(const Composition& reactants, const Composition& products); + //! Get reaction rate pointer shared_ptr rate() { return m_rate; @@ -188,7 +191,7 @@ class ElementaryReaction : public Reaction virtual void getParameters(AnyMap& reactionNode) const; virtual std::string type() const { - return "elementary"; + return "elementary-old"; } Arrhenius rate; @@ -341,6 +344,31 @@ class ChebyshevReaction : public Reaction ChebyshevRate rate; }; +//! A reaction which follows mass-action kinetics with a modified Arrhenius +//! reaction rate. +/** + * 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. + */ +class ElementaryReaction2 : public Reaction2 +{ +public: + ElementaryReaction2(); + ElementaryReaction2(const Composition& reactants, const Composition& products, + const ArrheniusRate& rate); + + virtual std::string type() const { + return "elementary"; + } + + virtual void setParameters(const AnyMap& node, const Kinetics& kin); + + bool allow_negative_pre_exponential_factor; +}; + + //! A reaction which follows mass-action kinetics with a custom reaction rate //! defined in Python. /** diff --git a/include/cantera/kinetics/ReactionFactory.h b/include/cantera/kinetics/ReactionFactory.h index 6cf0ff7e09c..fb3427835e6 100644 --- a/include/cantera/kinetics/ReactionFactory.h +++ b/include/cantera/kinetics/ReactionFactory.h @@ -79,6 +79,15 @@ class ReactionFactory : public Factory m_xml_setup[name] = f; } + //! Check whether `name` is registered + /** + * Detect whether '-old' suffix is required for valid XML constructor; used + * until XML support is removed. + */ + bool missing_XML(const std::string& name) { + return m_xml_setup.find(name) == m_xml_setup.end(); + } + //! Set up reaction based on AnyMap node information /** * @todo call setup directly from constructor after removal of XML support. diff --git a/include/cantera/kinetics/reaction_defs.h b/include/cantera/kinetics/reaction_defs.h index 7e3050b6427..ed9697ad297 100644 --- a/include/cantera/kinetics/reaction_defs.h +++ b/include/cantera/kinetics/reaction_defs.h @@ -59,11 +59,6 @@ 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/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index b89505d2b5b..701bf2d1eb2 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -247,7 +247,7 @@ bool GasKinetics::addReaction(shared_ptr r) return true; } - if (r->type() == "elementary") { + if (r->type() == "elementary-old") { addElementaryReaction(dynamic_cast(*r)); } else if (r->type() == "three-body") { addThreeBodyReaction(dynamic_cast(*r)); diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 7e783631e2c..0efb616ff01 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -637,6 +637,11 @@ ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, reaction_type = CHEBYSHEV_RXN; } +Reaction2::Reaction2(const Composition& reactants, const Composition& products) + : Reaction(reactants, products) +{ +} + void Reaction2::setParameters(const AnyMap& node, const Kinetics& kin) { parseReactionEquation(*this, node["equation"], kin); @@ -661,10 +666,33 @@ void Reaction2::setParameters(const AnyMap& node, const Kinetics& kin) input = node; } +ElementaryReaction2::ElementaryReaction2() + : Reaction2() + , allow_negative_pre_exponential_factor(false) +{ +} + +ElementaryReaction2::ElementaryReaction2(const Composition& reactants, + const Composition& products, + const ArrheniusRate& rate) + : Reaction2(reactants, products) + , allow_negative_pre_exponential_factor(false) +{ + setRate(std::make_shared(rate)); +} + +void ElementaryReaction2::setParameters(const AnyMap& node, const Kinetics& kin) +{ + Reaction2::setParameters(node, kin); + + setRate( + std::shared_ptr(new ArrheniusRate(node, rate_units))); + allow_negative_pre_exponential_factor = node.getBool("negative-A", false); +} + CustomFunc1Reaction::CustomFunc1Reaction() : Reaction2() { - reaction_type = CUSTOMPY_RXN; } void CustomFunc1Reaction::setParameters(const AnyMap& node, const Kinetics& kin) diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index b0b7834ac69..b9f20e51488 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -22,14 +22,19 @@ std::mutex ReactionFactory::reaction_mutex; ReactionFactory::ReactionFactory() { // register elementary reactions - reg("elementary", []() { return new ElementaryReaction(); }); + reg("elementary", []() { return new ElementaryReaction2(); }); addAlias("elementary", "arrhenius"); addAlias("elementary", ""); - reg_XML("elementary", + reg_AnyMap("elementary", + [](Reaction* R, const AnyMap& node, const Kinetics& kin) { + (dynamic_cast(R))->setParameters(node, kin); + }); + reg("elementary-old", []() { return new ElementaryReaction(); }); + reg_XML("elementary-old", [](Reaction* R, const XML_Node& node) { setupElementaryReaction(*(ElementaryReaction*)R, node); }); - reg_AnyMap("elementary", + reg_AnyMap("elementary-old", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { setupElementaryReaction(*(ElementaryReaction*)R, node, kin); }); @@ -98,11 +103,6 @@ ReactionFactory::ReactionFactory() // 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 reactions based " - "on 'Func1' objects cannot be created from XML nodes'"); - }); reg_AnyMap("custom-rate-function", [](Reaction* R, const AnyMap& node, const Kinetics& kin) { ((Reaction2*)R)->setParameters(node, kin); @@ -244,6 +244,11 @@ unique_ptr newReaction(const XML_Node& rxn_node) } Reaction* R; + + if (ReactionFactory::factory()->missing_XML(type)) { + type += "-old"; + } + try { R = ReactionFactory::factory()->create(type); } catch (CanteraError& err) { @@ -263,6 +268,7 @@ unique_ptr newReaction(const XML_Node& rxn_node) if (type != "electrochemical") { type = R->type(); } + ReactionFactory::factory()->setup_XML(type, R, rxn_node); return unique_ptr(R); From dc609bcd5007a3f8572cbe50ddafe287dc4645e9 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 19 Mar 2021 22:53:07 -0500 Subject: [PATCH 03/84] [Kinetics] Implement Reaction constructors for AnyMap --- include/cantera/kinetics/Reaction.h | 6 ++++++ src/kinetics/Reaction.cpp | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 1f4dda3b169..3ddf116c3bc 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -359,6 +359,8 @@ class ElementaryReaction2 : public Reaction2 ElementaryReaction2(const Composition& reactants, const Composition& products, const ArrheniusRate& rate); + ElementaryReaction2(const AnyMap& node, const Kinetics& kin); + virtual std::string type() const { return "elementary"; } @@ -380,6 +382,8 @@ class CustomFunc1Reaction : public Reaction2 public: CustomFunc1Reaction(); + CustomFunc1Reaction(const AnyMap& node, const Kinetics& kin); + virtual void setParameters(const AnyMap& node, const Kinetics& kin); virtual std::string type() const { @@ -401,6 +405,8 @@ class TestReaction : public Reaction2 public: TestReaction(); + TestReaction(const AnyMap& node, const Kinetics& kin); + virtual std::string type() const { return "elementary-new"; } diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 0efb616ff01..deccce0c457 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -681,6 +681,12 @@ ElementaryReaction2::ElementaryReaction2(const Composition& reactants, setRate(std::make_shared(rate)); } +ElementaryReaction2::ElementaryReaction2(const AnyMap& node, const Kinetics& kin) + : ElementaryReaction2() +{ + setParameters(node, kin); +} + void ElementaryReaction2::setParameters(const AnyMap& node, const Kinetics& kin) { Reaction2::setParameters(node, kin); @@ -695,6 +701,13 @@ CustomFunc1Reaction::CustomFunc1Reaction() { } +CustomFunc1Reaction::CustomFunc1Reaction(const AnyMap& node, + const Kinetics& kin) + : CustomFunc1Reaction() +{ + setParameters(node, kin); +} + void CustomFunc1Reaction::setParameters(const AnyMap& node, const Kinetics& kin) { Reaction2::setParameters(node, kin); @@ -708,6 +721,12 @@ TestReaction::TestReaction() { } +TestReaction::TestReaction(const AnyMap& node, const Kinetics& kin) + : TestReaction() +{ + setParameters(node, kin); +} + void TestReaction::setParameters(const AnyMap& node, const Kinetics& kin) { Reaction2::setParameters(node, kin); From 542820671fd3510aa3faa3dd513cced05055037b Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 19 Mar 2021 22:53:55 -0500 Subject: [PATCH 04/84] [Kinetics] Separate ReactionFactory for AnyMap and XML --- include/cantera/kinetics/ReactionFactory.h | 90 +++----- src/kinetics/ReactionFactory.cpp | 257 +++++++++++---------- 2 files changed, 167 insertions(+), 180 deletions(-) diff --git a/include/cantera/kinetics/ReactionFactory.h b/include/cantera/kinetics/ReactionFactory.h index fb3427835e6..45a956fae6a 100644 --- a/include/cantera/kinetics/ReactionFactory.h +++ b/include/cantera/kinetics/ReactionFactory.h @@ -26,13 +26,8 @@ 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 +class ReactionFactory : public Factory { public: /** @@ -54,82 +49,51 @@ 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 { - m_xml_setup.at(name)(R, rxn_node); - } catch (std::out_of_range&) { - throw CanteraError("ReactionFactory::setup_XML", - "No such type: '{}'", name); - } - } +private: + //! Pointer to the single instance of the factory + static ReactionFactory* s_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; - } + //! default constructor, which is defined as private + ReactionFactory(); - //! Check whether `name` is registered - /** - * Detect whether '-old' suffix is required for valid XML constructor; used - * until XML support is removed. - */ - bool missing_XML(const std::string& name) { - return m_xml_setup.find(name) == m_xml_setup.end(); - } + //! Mutex for use when calling the factory + static std::mutex reaction_mutex; +}; - //! Set up reaction based on AnyMap node information +class ReactionFactoryXML : public Factory +{ +public: /** - * @todo call setup directly from constructor after removal of XML support. + * 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. */ - 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); + static ReactionFactoryXML* factory() { + std::unique_lock lock(reaction_mutex); + if (!s_factory) { + s_factory = new ReactionFactoryXML; } + return s_factory; } - //! 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; + virtual void deleteFactory() { + std::unique_lock lock(reaction_mutex); + delete s_factory; + s_factory = 0; } private: //! Pointer to the single instance of the factory - static ReactionFactory* s_factory; + static ReactionFactoryXML* s_factory; //! default constructor, which is defined as private - ReactionFactory(); + ReactionFactoryXML(); //! 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 empty Reaction object /*! * @param type string identifying type of reaction. diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index b9f20e51488..b07b74081bd 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -22,141 +22,173 @@ std::mutex ReactionFactory::reaction_mutex; ReactionFactory::ReactionFactory() { // register elementary reactions - reg("elementary", []() { return new ElementaryReaction2(); }); + reg("elementary", [](const AnyMap& node, const Kinetics& kin) { + return new ElementaryReaction2(node, kin); + }); addAlias("elementary", "arrhenius"); addAlias("elementary", ""); - reg_AnyMap("elementary", - [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - (dynamic_cast(R))->setParameters(node, kin); - }); - reg("elementary-old", []() { return new ElementaryReaction(); }); - reg_XML("elementary-old", - [](Reaction* R, const XML_Node& node) { - setupElementaryReaction(*(ElementaryReaction*)R, node); - }); - reg_AnyMap("elementary-old", - [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - setupElementaryReaction(*(ElementaryReaction*)R, node, kin); - }); + + reg("elementary-old", [](const AnyMap& node, const Kinetics& kin) { + Reaction* R = new ElementaryReaction(); + setupElementaryReaction(*(ElementaryReaction*)R, node, kin); + return R; + }); // register three-body reactions - reg("three-body", []() { return new ThreeBodyReaction(); }); + reg("three-body", [](const AnyMap& node, const Kinetics& kin) { + Reaction* R = new ThreeBodyReaction(); + setupThreeBodyReaction(*(ThreeBodyReaction*)R, node, kin); + return R; + }); addAlias("three-body", "threebody"); addAlias("three-body", "three_body"); - reg_XML("three-body", - [](Reaction* R, const XML_Node& node) { - setupThreeBodyReaction(*(ThreeBodyReaction*)R, node); - }); - 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(); }); - reg_XML("falloff", - [](Reaction* R, const XML_Node& node) { - setupFalloffReaction(*(FalloffReaction*)R, node); - }); - reg_AnyMap("falloff", - [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - setupFalloffReaction(*(FalloffReaction*)R, node, kin); - }); + reg("falloff", [](const AnyMap& node, const Kinetics& kin) { + Reaction* R = new FalloffReaction(); + setupFalloffReaction(*(FalloffReaction*)R, node, kin); + return R; + }); // register falloff reactions - reg("chemically-activated", []() { return new ChemicallyActivatedReaction(); }); + reg("chemically-activated", [](const AnyMap& node, const Kinetics& kin) { + Reaction* R = new ChemicallyActivatedReaction(); + setupFalloffReaction(*(FalloffReaction*)R, node, kin); + return R; + }); addAlias("chemically-activated", "chemact"); addAlias("chemically-activated", "chemically_activated"); - 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(); }); + reg("pressure-dependent-Arrhenius", [](const AnyMap& node, const Kinetics& kin) { + Reaction* R = new PlogReaction(); + setupPlogReaction(*(PlogReaction*)R, node, kin); + return R; + }); 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); - }); - 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(); }); + reg("Chebyshev", [](const AnyMap& node, const Kinetics& kin) { + Reaction* R = new ChebyshevReaction(); + setupChebyshevReaction(*(ChebyshevReaction*)R, node, kin); + return R; + }); addAlias("Chebyshev", "chebyshev"); - reg_XML("Chebyshev", - [](Reaction* R, const XML_Node& node) { - setupChebyshevReaction(*(ChebyshevReaction*)R, node); - }); - reg_AnyMap("Chebyshev", - [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - setupChebyshevReaction(*(ChebyshevReaction*)R, node, kin); - }); // register custom reactions specified by Func1 objects - reg("custom-rate-function", []() { return new CustomFunc1Reaction(); }); - reg_AnyMap("custom-rate-function", - [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - ((Reaction2*)R)->setParameters(node, kin); - }); + reg("custom-rate-function", [](const AnyMap& node, const Kinetics& kin) { + return new CustomFunc1Reaction(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) { - (dynamic_cast(R))->setParameters(node, kin); - }); + reg("elementary-new", [](const AnyMap& node, const Kinetics& kin) { + return new TestReaction(node, kin); + }); // register interface reactions - reg("interface", []() { return new InterfaceReaction(); }); + reg("interface", [](const AnyMap& node, const Kinetics& kin) { + Reaction* R = new InterfaceReaction(); + setupInterfaceReaction(*(InterfaceReaction*)R, node, kin); + return R; + }); addAlias("interface", "surface"); addAlias("interface", "edge"); - reg_XML("interface", - [](Reaction* R, const XML_Node& node) { - setupInterfaceReaction(*(InterfaceReaction*)R, node); - }); - reg_AnyMap("interface", - [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - setupInterfaceReaction(*(InterfaceReaction*)R, node, kin); - }); // register electrochemical reactions - reg("electrochemical", []() { return new ElectrochemicalReaction(); }); - reg_XML("electrochemical", - [](Reaction* R, const XML_Node& node) { - setupElectrochemicalReaction(*(ElectrochemicalReaction*)R, node); - }); - reg_AnyMap("electrochemical", - [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - setupElectrochemicalReaction(*(ElectrochemicalReaction*)R, node, kin); - }); + reg("electrochemical", [](const AnyMap& node, const Kinetics& kin) { + Reaction* R = new ElectrochemicalReaction(); + setupElectrochemicalReaction(*(ElectrochemicalReaction*)R, node, kin); + return R; + }); // register Blowers Masel reactions - reg("Blowers-Masel", []() { return new BlowersMaselReaction(); }); - reg_AnyMap("Blowers-Masel", - [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - setupBlowersMaselReaction(*(BlowersMaselReaction*)R, node, kin); - }); + reg("Blowers-Masel", [](const AnyMap& node, const Kinetics& kin) { + Reaction* R = new BlowersMaselReaction(); + setupBlowersMaselReaction(*(BlowersMaselReaction*)R, node, kin); + return R; + }); // register surface Blowers Masel reactions - reg("surface-Blowers-Masel", []() { return new BlowersMaselInterfaceReaction(); }); - reg_AnyMap("surface-Blowers-Masel", - [](Reaction* R, const AnyMap& node, const Kinetics& kin) { - setupBlowersMaselInterfaceReaction(*(BlowersMaselInterfaceReaction*)R, node, kin); - }); + reg("surface-Blowers-Masel", [](const AnyMap& node, const Kinetics& kin) { + Reaction* R = new BlowersMaselInterfaceReaction(); + setupBlowersMaselInterfaceReaction(*(BlowersMaselInterfaceReaction*)R, node, kin); + return R; + }); +} + +ReactionFactoryXML* ReactionFactoryXML::s_factory = 0; +std::mutex ReactionFactoryXML::reaction_mutex; + +ReactionFactoryXML::ReactionFactoryXML() +{ + // register elementary reactions + reg("elementary-old", [](const XML_Node& node) { + Reaction* R = new ElementaryReaction(); + setupElementaryReaction(*(ElementaryReaction*)R, node); + return R; + }); + addAlias("elementary-old", "elementary"); + addAlias("elementary-old", "arrhenius"); + addAlias("elementary-old", ""); + + // register three-body reactions + reg("three-body", [](const XML_Node& node) { + Reaction* R = new ThreeBodyReaction(); + setupThreeBodyReaction(*(ThreeBodyReaction*)R, node); + return R; + }); + addAlias("three-body", "threebody"); + addAlias("three-body", "three_body"); + + // register falloff reactions + reg("falloff", [](const XML_Node& node) { + Reaction* R = new FalloffReaction(); + setupFalloffReaction(*(FalloffReaction*)R, node); + return R; + }); + + // register falloff reactions + reg("chemically-activated", [](const XML_Node& node) { + Reaction* R = new ChemicallyActivatedReaction(); + setupChemicallyActivatedReaction(*(ChemicallyActivatedReaction*)R, node); + return R; + }); + addAlias("chemically-activated", "chemact"); + addAlias("chemically-activated", "chemically_activated"); + + // register pressure-depdendent-Arrhenius reactions + reg("pressure-dependent-Arrhenius", [](const XML_Node& node) { + Reaction* R = new PlogReaction(); + setupPlogReaction(*(PlogReaction*)R, node); + return R; + }); + addAlias("pressure-dependent-Arrhenius", "plog"); + addAlias("pressure-dependent-Arrhenius", "pdep_arrhenius"); + + // register Chebyshev reactions + reg("Chebyshev", [](const XML_Node& node) { + Reaction* R = new ChebyshevReaction(); + setupChebyshevReaction(*(ChebyshevReaction*)R, node); + return R; + }); + addAlias("Chebyshev", "chebyshev"); + + // register interface reactions + reg("interface", [](const XML_Node& node) { + Reaction* R = new InterfaceReaction(); + setupInterfaceReaction(*(InterfaceReaction*)R, node); + return R; + }); + addAlias("interface", "surface"); + addAlias("interface", "edge"); + + // register electrochemical reactions + reg("electrochemical", [](const XML_Node& node) { + Reaction* R = new ElectrochemicalReaction(); + setupElectrochemicalReaction(*(ElectrochemicalReaction*)R, node); + return R; + }); } bool isThreeBody(const Reaction& R) @@ -228,7 +260,9 @@ bool isElectrochemicalReaction(Reaction& R, const Kinetics& kin) unique_ptr newReaction(const std::string& type) { - unique_ptr R(ReactionFactory::factory()->create(type)); + AnyMap rxn_node; + Kinetics kin; + unique_ptr R(ReactionFactory::factory()->create(type, rxn_node, kin)); return R; } @@ -244,13 +278,8 @@ unique_ptr newReaction(const XML_Node& rxn_node) } Reaction* R; - - if (ReactionFactory::factory()->missing_XML(type)) { - type += "-old"; - } - try { - R = ReactionFactory::factory()->create(type); + R = ReactionFactoryXML::factory()->create(type, rxn_node); } catch (CanteraError& err) { throw CanteraError("newReaction", "Unknown reaction type '" + rxn_node["type"] + "'"); @@ -262,20 +291,15 @@ unique_ptr newReaction(const XML_Node& rxn_node) setupReaction(testReaction, rxn_node); if (isThreeBody(testReaction)) { type = "three-body"; - R = ReactionFactory::factory()->create(type); } } if (type != "electrochemical") { type = R->type(); } - - ReactionFactory::factory()->setup_XML(type, R, rxn_node); - return unique_ptr(R); } -unique_ptr newReaction(const AnyMap& rxn_node, - const Kinetics& kin) +unique_ptr newReaction(const AnyMap& rxn_node, const Kinetics& kin) { std::string type = "elementary"; if (rxn_node.hasKey("type")) { @@ -308,12 +332,11 @@ unique_ptr newReaction(const AnyMap& rxn_node, Reaction* R; try { - R = ReactionFactory::factory()->create(type); + R = ReactionFactory::factory()->create(type, rxn_node, kin); } catch (CanteraError& err) { throw InputFileError("ReactionFactory::newReaction", rxn_node["type"], "Unknown reaction type '{}'", type); } - ReactionFactory::factory()->setup_AnyMap(type, R, rxn_node, kin); return unique_ptr(R); } From aeb47a1526c7bed6c89910c6fb3d4c00e5ac9732 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 20 Mar 2021 01:55:03 -0500 Subject: [PATCH 05/84] [Kinetics] Return boolean from Reaction2::setParameters --- include/cantera/kinetics/Reaction.h | 8 ++--- include/cantera/kinetics/ReactionRate.h | 13 ++++++-- src/kinetics/BulkKinetics.cpp | 2 ++ src/kinetics/Reaction.cpp | 42 ++++++++++++++----------- src/kinetics/ReactionRate.cpp | 14 ++++++++- 5 files changed, 53 insertions(+), 26 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 3ddf116c3bc..f94a382f02a 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -171,7 +171,7 @@ class Reaction2 : public Reaction } //! Set up reaction based on AnyMap node - virtual void setParameters(const AnyMap& node, const Kinetics& kin); + virtual bool setParameters(const AnyMap& node, const Kinetics& kin); protected: //! Reaction rate used by generic reactions @@ -365,7 +365,7 @@ class ElementaryReaction2 : public Reaction2 return "elementary"; } - virtual void setParameters(const AnyMap& node, const Kinetics& kin); + virtual bool setParameters(const AnyMap& node, const Kinetics& kin); bool allow_negative_pre_exponential_factor; }; @@ -384,7 +384,7 @@ class CustomFunc1Reaction : public Reaction2 CustomFunc1Reaction(const AnyMap& node, const Kinetics& kin); - virtual void setParameters(const AnyMap& node, const Kinetics& kin); + virtual bool setParameters(const AnyMap& node, const Kinetics& kin); virtual std::string type() const { return "custom-rate-function"; @@ -411,7 +411,7 @@ class TestReaction : public Reaction2 return "elementary-new"; } - virtual void setParameters(const AnyMap& node, const Kinetics& kin); + virtual bool 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 15c3ffbebd4..485a9041564 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -38,6 +38,14 @@ class ReactionRateBase public: virtual ~ReactionRateBase() {} + //! Set parameters + //! @param node AnyMap object containing reaction rate specification + //! @param rate_units Description of units used for rate parameters + virtual bool setParameters(const AnyMap& node, const Units& rate_units) { + throw CanteraError("ReactionRate::setParameters", + "Not implemented by derived ReactionRate object."); + } + //! Identifier of reaction type virtual std::string type() const = 0; @@ -132,11 +140,10 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius ArrheniusRate(double A, double b, double E); - //! Constructor - //! @param node AnyMap object containing reaction rate specification - //! @param rate_units Description of units used for rate parameters ArrheniusRate(const AnyMap& node, const Units& rate_units); + virtual bool setParameters(const AnyMap& node, const Units& rate_units) override; + virtual std::string type() const override { return "ArrheniusRate"; } //! Update information specific to reaction diff --git a/src/kinetics/BulkKinetics.cpp b/src/kinetics/BulkKinetics.cpp index 270545709b9..0fa5aecd26a 100644 --- a/src/kinetics/BulkKinetics.cpp +++ b/src/kinetics/BulkKinetics.cpp @@ -1,3 +1,4 @@ + // 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. @@ -103,6 +104,7 @@ bool BulkKinetics::addReaction(shared_ptr r) { bool added = Kinetics::addReaction(r); if (!added) { + // undeclared species, etc. return false; } double dn = 0.0; diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index deccce0c457..e57453d7e3f 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -642,8 +642,13 @@ Reaction2::Reaction2(const Composition& reactants, const Composition& products) { } -void Reaction2::setParameters(const AnyMap& node, const Kinetics& kin) +bool Reaction2::setParameters(const AnyMap& node, const Kinetics& kin) { + if (!node.hasKey("equation")) { + // empty node: used by newReaction() factory loader + return false; + } + parseReactionEquation(*this, node["equation"], kin); // Non-stoichiometric reaction orders std::map orders; @@ -664,12 +669,14 @@ void Reaction2::setParameters(const AnyMap& node, const Kinetics& kin) calculateRateCoeffUnits(kin); input = node; + return true; } ElementaryReaction2::ElementaryReaction2() : Reaction2() , allow_negative_pre_exponential_factor(false) { + m_rate = std::shared_ptr(new ArrheniusRate); } ElementaryReaction2::ElementaryReaction2(const Composition& reactants, @@ -678,7 +685,7 @@ ElementaryReaction2::ElementaryReaction2(const Composition& reactants, : Reaction2(reactants, products) , allow_negative_pre_exponential_factor(false) { - setRate(std::make_shared(rate)); + m_rate = std::make_shared(rate); } ElementaryReaction2::ElementaryReaction2(const AnyMap& node, const Kinetics& kin) @@ -687,38 +694,38 @@ ElementaryReaction2::ElementaryReaction2(const AnyMap& node, const Kinetics& kin setParameters(node, kin); } -void ElementaryReaction2::setParameters(const AnyMap& node, const Kinetics& kin) +bool ElementaryReaction2::setParameters(const AnyMap& node, const Kinetics& kin) { - Reaction2::setParameters(node, kin); - - setRate( - std::shared_ptr(new ArrheniusRate(node, rate_units))); + bool ok = Reaction2::setParameters(node, kin); + setRate(std::shared_ptr(new ArrheniusRate(node, rate_units))); allow_negative_pre_exponential_factor = node.getBool("negative-A", false); + return ok; } CustomFunc1Reaction::CustomFunc1Reaction() : Reaction2() { + m_rate = std::shared_ptr(new CustomFunc1Rate); } -CustomFunc1Reaction::CustomFunc1Reaction(const AnyMap& node, - const Kinetics& kin) +CustomFunc1Reaction::CustomFunc1Reaction(const AnyMap& node, const Kinetics& kin) : CustomFunc1Reaction() { setParameters(node, kin); } -void CustomFunc1Reaction::setParameters(const AnyMap& node, const Kinetics& kin) +bool CustomFunc1Reaction::setParameters(const AnyMap& node, const Kinetics& kin) { - Reaction2::setParameters(node, kin); - setRate( - std::shared_ptr(new CustomFunc1Rate(node, rate_units))); + bool ok = Reaction2::setParameters(node, kin); + setRate(std::shared_ptr(new CustomFunc1Rate(node, rate_units))); + return ok; } TestReaction::TestReaction() : Reaction2() , allow_negative_pre_exponential_factor(false) { + m_rate = std::shared_ptr(new ArrheniusRate); } TestReaction::TestReaction(const AnyMap& node, const Kinetics& kin) @@ -727,13 +734,12 @@ TestReaction::TestReaction(const AnyMap& node, const Kinetics& kin) setParameters(node, kin); } -void TestReaction::setParameters(const AnyMap& node, const Kinetics& kin) +bool TestReaction::setParameters(const AnyMap& node, const Kinetics& kin) { - Reaction2::setParameters(node, kin); - - setRate( - std::shared_ptr(new ArrheniusRate(node, rate_units))); + bool ok = Reaction2::setParameters(node, kin); + setRate(std::shared_ptr(new ArrheniusRate(node, rate_units))); allow_negative_pre_exponential_factor = node.getBool("negative-A", false); + return ok; } void ChebyshevReaction::getParameters(AnyMap& reactionNode) const diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 29485704c97..dc5387e9917 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -10,12 +10,24 @@ namespace Cantera { +ArrheniusRate::ArrheniusRate() + : Arrhenius() { +} + ArrheniusRate::ArrheniusRate(double A, double b, double E) : Arrhenius(A, b, E) { } ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rate_units) { - setParameters(node["rate-constant"], node.units(), rate_units); + setParameters(node, rate_units); +} + +bool ArrheniusRate::setParameters(const AnyMap& node, const Units& rate_units) { + if (!node.hasKey("rate-constant")) { + return false; + } + Arrhenius::setParameters(node["rate-constant"], node.units(), rate_units); + return true; } CustomFunc1Rate::CustomFunc1Rate() : m_ratefunc(0) {} From d8e56de673a32ad659313b8e2327d57a4e955696 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 20 Mar 2021 02:20:17 -0500 Subject: [PATCH 06/84] [Kinetics] Ensure integration works --- include/cantera/kinetics/Reaction.h | 4 +- src/kinetics/GasKinetics.cpp | 2 +- src/kinetics/Reaction.cpp | 25 ++++++++++--- src/kinetics/ReactionFactory.cpp | 57 ++++++++++++++++++----------- 4 files changed, 57 insertions(+), 31 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index f94a382f02a..b955d3653ab 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -367,6 +367,8 @@ class ElementaryReaction2 : public Reaction2 virtual bool setParameters(const AnyMap& node, const Kinetics& kin); + virtual void validate(); + bool allow_negative_pre_exponential_factor; }; @@ -412,8 +414,6 @@ class TestReaction : public Reaction2 } virtual bool setParameters(const AnyMap& node, const Kinetics& kin); - - bool allow_negative_pre_exponential_factor; }; diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 701bf2d1eb2..25fb1623eb0 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -339,7 +339,7 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) return; } - if (rNew->type() == "elementary") { + if (rNew->type() == "elementary-old") { modifyElementaryReaction(i, dynamic_cast(*rNew)); } else if (rNew->type() == "three-body") { modifyThreeBodyReaction(i, dynamic_cast(*rNew)); diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index e57453d7e3f..32c29cb0e4b 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -696,10 +696,23 @@ ElementaryReaction2::ElementaryReaction2(const AnyMap& node, const Kinetics& kin bool ElementaryReaction2::setParameters(const AnyMap& node, const Kinetics& kin) { - bool ok = Reaction2::setParameters(node, kin); + if (!Reaction2::setParameters(node, kin)) { + return false; + } setRate(std::shared_ptr(new ArrheniusRate(node, rate_units))); allow_negative_pre_exponential_factor = node.getBool("negative-A", false); - return ok; + return true; +} + +void ElementaryReaction2::validate() +{ + Reaction::validate(); + if (!allow_negative_pre_exponential_factor && + std::dynamic_pointer_cast(m_rate)->preExponentialFactor() < 0) { + throw CanteraError("ElementaryReaction::validate", + "Undeclared negative pre-exponential factor found in reaction '" + + equation() + "'"); + } } CustomFunc1Reaction::CustomFunc1Reaction() @@ -723,7 +736,6 @@ bool CustomFunc1Reaction::setParameters(const AnyMap& node, const Kinetics& kin) TestReaction::TestReaction() : Reaction2() - , allow_negative_pre_exponential_factor(false) { m_rate = std::shared_ptr(new ArrheniusRate); } @@ -736,10 +748,11 @@ TestReaction::TestReaction(const AnyMap& node, const Kinetics& kin) bool TestReaction::setParameters(const AnyMap& node, const Kinetics& kin) { - bool ok = Reaction2::setParameters(node, kin); + if (!Reaction2::setParameters(node, kin)) { + return false; + } setRate(std::shared_ptr(new ArrheniusRate(node, rate_units))); - allow_negative_pre_exponential_factor = node.getBool("negative-A", false); - return ok; + return true; } void ChebyshevReaction::getParameters(AnyMap& reactionNode) const diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index b07b74081bd..7368032e705 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -30,14 +30,18 @@ ReactionFactory::ReactionFactory() reg("elementary-old", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ElementaryReaction(); - setupElementaryReaction(*(ElementaryReaction*)R, node, kin); + if (node.hasKey("equation")) { + setupElementaryReaction(*(ElementaryReaction*)R, node, kin); + } return R; }); // register three-body reactions reg("three-body", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ThreeBodyReaction(); - setupThreeBodyReaction(*(ThreeBodyReaction*)R, node, kin); + if (node.hasKey("equation")) { + setupThreeBodyReaction(*(ThreeBodyReaction*)R, node, kin); + } return R; }); addAlias("three-body", "threebody"); @@ -46,14 +50,18 @@ ReactionFactory::ReactionFactory() // register falloff reactions reg("falloff", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new FalloffReaction(); - setupFalloffReaction(*(FalloffReaction*)R, node, kin); + if (node.hasKey("equation")) { + setupFalloffReaction(*(FalloffReaction*)R, node, kin); + } return R; }); // register falloff reactions reg("chemically-activated", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ChemicallyActivatedReaction(); - setupFalloffReaction(*(FalloffReaction*)R, node, kin); + if (node.hasKey("equation")) { + setupFalloffReaction(*(FalloffReaction*)R, node, kin); + } return R; }); addAlias("chemically-activated", "chemact"); @@ -62,7 +70,9 @@ ReactionFactory::ReactionFactory() // register pressure-depdendent-Arrhenius reactions reg("pressure-dependent-Arrhenius", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new PlogReaction(); - setupPlogReaction(*(PlogReaction*)R, node, kin); + if (node.hasKey("equation")) { + setupPlogReaction(*(PlogReaction*)R, node, kin); + } return R; }); addAlias("pressure-dependent-Arrhenius", "plog"); @@ -71,7 +81,9 @@ ReactionFactory::ReactionFactory() // register Chebyshev reactions reg("Chebyshev", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ChebyshevReaction(); - setupChebyshevReaction(*(ChebyshevReaction*)R, node, kin); + if (node.hasKey("equation")) { + setupChebyshevReaction(*(ChebyshevReaction*)R, node, kin); + } return R; }); addAlias("Chebyshev", "chebyshev"); @@ -89,7 +101,9 @@ ReactionFactory::ReactionFactory() // register interface reactions reg("interface", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new InterfaceReaction(); - setupInterfaceReaction(*(InterfaceReaction*)R, node, kin); + if (node.hasKey("equation")) { + setupInterfaceReaction(*(InterfaceReaction*)R, node, kin); + } return R; }); addAlias("interface", "surface"); @@ -98,21 +112,27 @@ ReactionFactory::ReactionFactory() // register electrochemical reactions reg("electrochemical", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ElectrochemicalReaction(); - setupElectrochemicalReaction(*(ElectrochemicalReaction*)R, node, kin); + if (node.hasKey("equation")) { + setupElectrochemicalReaction(*(ElectrochemicalReaction*)R, node, kin); + } return R; }); // register Blowers Masel reactions reg("Blowers-Masel", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new BlowersMaselReaction(); - setupBlowersMaselReaction(*(BlowersMaselReaction*)R, node, kin); + if (node.hasKey("equation")) { + setupBlowersMaselReaction(*(BlowersMaselReaction*)R, node, kin); + } return R; }); // register surface Blowers Masel reactions reg("surface-Blowers-Masel", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new BlowersMaselInterfaceReaction(); - setupBlowersMaselInterfaceReaction(*(BlowersMaselInterfaceReaction*)R, node, kin); + if (node.hasKey("equation")) { + setupBlowersMaselInterfaceReaction(*(BlowersMaselInterfaceReaction*)R, node, kin); + } return R; }); } @@ -277,10 +297,7 @@ unique_ptr newReaction(const XML_Node& rxn_node) type = "electrochemical"; } - Reaction* R; - try { - R = ReactionFactoryXML::factory()->create(type, rxn_node); - } catch (CanteraError& err) { + if (!(ReactionFactoryXML::factory()->exists(type))) { throw CanteraError("newReaction", "Unknown reaction type '" + rxn_node["type"] + "'"); } @@ -293,9 +310,7 @@ unique_ptr newReaction(const XML_Node& rxn_node) type = "three-body"; } } - if (type != "electrochemical") { - type = R->type(); - } + Reaction* R = ReactionFactoryXML::factory()->create(type, rxn_node); return unique_ptr(R); } @@ -330,14 +345,12 @@ unique_ptr newReaction(const AnyMap& rxn_node, const Kinetics& kin) type = "surface-Blowers-Masel"; } - Reaction* R; - try { - R = ReactionFactory::factory()->create(type, rxn_node, kin); - } catch (CanteraError& err) { + if (!(ReactionFactory::factory()->exists(type))) { throw InputFileError("ReactionFactory::newReaction", rxn_node["type"], "Unknown reaction type '{}'", type); } - + Reaction* R; + R = ReactionFactory::factory()->create(type, rxn_node, kin); return unique_ptr(R); } From 54589fda2f83c0d3075d4194390071636a324454 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 19 Mar 2021 16:24:29 -0500 Subject: [PATCH 07/84] [CI] Create test/kinetics/kineticsFromScratch2.cpp --- test/kinetics/kineticsFromScratch2.cpp | 517 +++++++++++++++++++++++++ test/kinetics/kineticsFromYaml.cpp | 30 +- 2 files changed, 544 insertions(+), 3 deletions(-) create mode 100644 test/kinetics/kineticsFromScratch2.cpp diff --git a/test/kinetics/kineticsFromScratch2.cpp b/test/kinetics/kineticsFromScratch2.cpp new file mode 100644 index 00000000000..22589a7dea2 --- /dev/null +++ b/test/kinetics/kineticsFromScratch2.cpp @@ -0,0 +1,517 @@ +#include "gtest/gtest.h" +#include "cantera/thermo/ThermoFactory.h" +#include "cantera/thermo/SurfPhase.h" +#include "cantera/thermo/Species.h" +#include "cantera/kinetics/KineticsFactory.h" +#include "cantera/kinetics/InterfaceKinetics.h" +#include "cantera/kinetics/FalloffFactory.h" +#include "cantera/base/Array.h" +#include "cantera/base/stringUtils.h" + +using namespace Cantera; + +class KineticsFromScratch2 : public testing::Test +{ +public: + KineticsFromScratch2() + { + std::string yaml_file = "../data/kineticsfromscratch.yaml"; + std::string phase_name = "ohmech"; + + p = std::shared_ptr(newPhase(yaml_file, phase_name)); + kin = std::shared_ptr(newKineticsMgr("GasKinetics")); + kin->addPhase(*p.get()); + + p_ref = std::shared_ptr(newPhase(yaml_file, phase_name)); + std::vector th; + th.push_back(p_ref.get()); + kin_ref = shared_ptr(newKinetics(th, yaml_file, phase_name)); + } + + std::shared_ptr p; + std::shared_ptr p_ref; + std::shared_ptr kin; + std::shared_ptr kin_ref; + + //! iRef is the index of the corresponding reaction in the reference mech + void check_rates(int iRef) { + ASSERT_EQ((size_t) 1, kin->nReactions()); + + std::string X = "O:0.02 H2:0.2 O2:0.5 H:0.03 OH:0.05 H2O:0.1 HO2:0.01"; + p->setState_TPX(1200, 5*OneAtm, X); + p_ref->setState_TPX(1200, 5*OneAtm, X); + + vector_fp k(1), k_ref(kin_ref->nReactions()); + + kin->getFwdRateConstants(&k[0]); + kin_ref->getFwdRateConstants(&k_ref[0]); + EXPECT_DOUBLE_EQ(k_ref[iRef], k[0]); + + kin->getRevRateConstants(&k[0]); + kin_ref->getRevRateConstants(&k_ref[0]); + EXPECT_DOUBLE_EQ(k_ref[iRef], k[0]); + } +}; + +TEST_F(KineticsFromScratch2, add_elementary_reaction) +{ + // reaction 0: + // reaction('O + H2 <=> H + OH', [3.870000e+01, 2.7, 6260.0]) + Composition reac = parseCompString("O:1 H2:1"); + Composition prod = parseCompString("H:1 OH:1"); + ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); + auto R = make_shared(reac, prod, rate); + + kin->addReaction(R); + check_rates(0); +} + +/* +TEST_F(KineticsFromScratch2, add_three_body_reaction) +{ + // reaction 1: + // three_body_reaction('2 O + M <=> O2 + M', [1.200000e+11, -1.0, 0.0], + // efficiencies='AR:0.83 H2:2.4 H2O:15.4') + Composition reac = parseCompString("O:2"); + Composition prod = parseCompString("O2:1"); + Arrhenius rate(1.2e11, -1.0, 0.0); + ThirdBody tbody; + tbody.efficiencies = parseCompString("AR:0.83 H2:2.4 H2O:15.4"); + auto R = make_shared(reac, prod, rate, tbody); + + kin->addReaction(R); + check_rates(1); +} + +TEST_F(KineticsFromScratch2, undefined_third_body) +{ + Composition reac = parseCompString("O:2"); + Composition prod = parseCompString("O2:1"); + Arrhenius rate(1.2e11, -1.0, 0.0); + ThirdBody tbody; + tbody.efficiencies = parseCompString("H2:0.1 CO2:0.83"); + auto R = make_shared(reac, prod, rate, tbody); + + ASSERT_THROW(kin->addReaction(R), CanteraError); +} + +TEST_F(KineticsFromScratch2, skip_undefined_third_body) +{ + Composition reac = parseCompString("O:2"); + Composition prod = parseCompString("O2:1"); + Arrhenius rate(1.2e11, -1.0, 0.0); + ThirdBody tbody; + tbody.efficiencies = parseCompString("H2:0.1 CO2:0.83"); + auto R = make_shared(reac, prod, rate, tbody); + + kin->skipUndeclaredThirdBodies(true); + kin->addReaction(R); + ASSERT_EQ((size_t) 1, kin->nReactions()); +} + + +TEST_F(KineticsFromScratch2, add_falloff_reaction) +{ + // reaction 2: + // falloff_reaction('2 OH (+ M) <=> H2O2 (+ M)', + // kf=[7.400000e+10, -0.37, 0.0], + // kf0=[2.300000e+12, -0.9, -1700.0], + // efficiencies='AR:0.7 H2:2.0 H2O:6.0', + // falloff=Troe(A=0.7346, T3=94.0, T1=1756.0, T2=5182.0)) + Composition reac = parseCompString("OH:2"); + Composition prod = parseCompString("H2O2:1"); + Arrhenius high_rate(7.4e10, -0.37, 0.0); + Arrhenius low_rate(2.3e12, -0.9, -1700.0 / GasConst_cal_mol_K); + vector_fp falloff_params { 0.7346, 94.0, 1756.0, 5182.0 }; + ThirdBody tbody; + tbody.efficiencies = parseCompString("AR:0.7 H2:2.0 H2O:6.0"); + auto R = make_shared(reac, prod, low_rate, high_rate, tbody); + R->falloff = newFalloff("Troe", falloff_params); + kin->addReaction(R); + check_rates(2); +} + +TEST_F(KineticsFromScratch2, add_plog_reaction) +{ + // reaction 3: + // pdep_arrhenius('H2 + O2 <=> 2 OH', + // [(0.01, 'atm'), 1.212400e+16, -0.5779, 10872.7], + // [(1.0, 'atm'), 4.910800e+31, -4.8507, 24772.8], + // [(10.0, 'atm'), 1.286600e+47, -9.0246, 39796.5], + // [(100.0, 'atm'), 5.963200e+56, -11.529, 52599.6]) + Composition reac = parseCompString("H2:1, O2:1"); + Composition prod = parseCompString("OH:2"); + std::multimap rates { + { 0.01*101325, Arrhenius(1.212400e+16, -0.5779, 10872.7 / GasConst_cal_mol_K) }, + { 1.0*101325, Arrhenius(4.910800e+31, -4.8507, 24772.8 / GasConst_cal_mol_K) }, + { 10.0*101325, Arrhenius(1.286600e+47, -9.0246, 39796.5 / GasConst_cal_mol_K) }, + { 100.0*101325, Arrhenius(5.963200e+56, -11.529, 52599.6 / GasConst_cal_mol_K) } + }; + + auto R = make_shared(reac, prod, Plog(rates)); + kin->addReaction(R); + check_rates(3); +} + +TEST_F(KineticsFromScratch2, plog_invalid_rate) +{ + Composition reac = parseCompString("H2:1, O2:1"); + Composition prod = parseCompString("OH:2"); + std::multimap rates { + { 0.01*101325, Arrhenius(1.2124e+16, -0.5779, 10872.7 / GasConst_cal_mol_K) }, + { 10.0*101325, Arrhenius(1e15, -1, 10000 / GasConst_cal_mol_K) }, + { 10.0*101325, Arrhenius(-2e20, -2.0, 20000 / GasConst_cal_mol_K) }, + { 100.0*101325, Arrhenius(5.9632e+56, -11.529, 52599.6 / GasConst_cal_mol_K) } + }; + + auto R = make_shared(reac, prod, Plog(rates)); + ASSERT_THROW(kin->addReaction(R), CanteraError); +} + +TEST_F(KineticsFromScratch2, add_chebyshev_reaction) +{ + // reaction 4: + // chebyshev_reaction( + // 'HO2 <=> OH + O', + // Tmin=290.0, Tmax=3000.0, + // Pmin=(0.0098692326671601278, 'atm'), Pmax=(98.692326671601279, 'atm'), + // coeffs=[[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], + // [ 1.9764e+00, 1.0037e+00, 7.2865e-03, -3.0432e-02], + // [ 3.1770e-01, 2.6889e-01, 9.4806e-02, -7.6385e-03]]) + Composition reac = parseCompString("HO2:1"); + Composition prod = parseCompString("OH:1 O:1"); + Array2D coeffs(3, 4); + coeffs(0,0) = 8.2883e+00; + coeffs(0,1) = -1.1397e+00; + coeffs(0,2) = -1.2059e-01; + coeffs(0,3) = 1.6034e-02; + coeffs(1,0) = 1.9764e+00; + coeffs(1,1) = 1.0037e+00; + coeffs(1,2) = 7.2865e-03; + coeffs(1,3) = -3.0432e-02; + coeffs(2,0) = 3.1770e-01; + coeffs(2,1) = 2.6889e-01; + coeffs(2,2) = 9.4806e-02; + coeffs(2,3) = -7.6385e-03; + ChebyshevRate rate(290, 3000, 1000.0, 10000000.0, coeffs); + + auto R = make_shared(reac, prod, rate); + kin->addReaction(R); + check_rates(4); +} +*/ + +TEST_F(KineticsFromScratch2, undeclared_species) +{ + Composition reac = parseCompString("CO:1 OH:1"); + Composition prod = parseCompString("CO2:1 H:1"); + ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); + auto R = make_shared(reac, prod, rate); + + ASSERT_THROW(kin->addReaction(R), CanteraError); + ASSERT_EQ((size_t) 0, kin->nReactions()); +} + +TEST_F(KineticsFromScratch2, skip_undeclared_species) +{ + Composition reac = parseCompString("CO:1 OH:1"); + Composition prod = parseCompString("CO2:1 H:1"); + ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); + auto R = make_shared(reac, prod, rate); + + kin->skipUndeclaredSpecies(true); + kin->addReaction(R); + ASSERT_EQ((size_t) 0, kin->nReactions()); +} + +TEST_F(KineticsFromScratch2, negative_A_error) +{ + Composition reac = parseCompString("O:1 H2:1"); + Composition prod = parseCompString("H:1 OH:1"); + ArrheniusRate rate(-3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); + auto R = make_shared(reac, prod, rate); + + ASSERT_THROW(kin->addReaction(R), CanteraError); + ASSERT_EQ((size_t) 0, kin->nReactions()); +} + +TEST_F(KineticsFromScratch2, allow_negative_A) +{ + Composition reac = parseCompString("O:1 H2:1"); + Composition prod = parseCompString("H:1 OH:1"); + ArrheniusRate rate(-3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); + auto R = make_shared(reac, prod, rate); + R->allow_negative_pre_exponential_factor = true; + + kin->addReaction(R); + ASSERT_EQ((size_t) 1, kin->nReactions()); +} + +TEST_F(KineticsFromScratch2, invalid_reversible_with_orders) +{ + Composition reac = parseCompString("O:1 H2:1"); + Composition prod = parseCompString("H:1 OH:1"); + ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); + auto R = make_shared(reac, prod, rate); + R->orders["H2"] = 0.5; + + ASSERT_THROW(kin->addReaction(R), CanteraError); + ASSERT_EQ((size_t) 0, kin->nReactions()); +} + +TEST_F(KineticsFromScratch2, negative_order_override) +{ + Composition reac = parseCompString("O:1 H2:1"); + Composition prod = parseCompString("H:1 OH:1"); + ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); + auto R = make_shared(reac, prod, rate); + R->reversible = false; + R->allow_negative_orders = true; + R->orders["H2"] = - 0.5; + + kin->addReaction(R); + ASSERT_EQ((size_t) 1, kin->nReactions()); +} + +TEST_F(KineticsFromScratch2, invalid_negative_orders) +{ + Composition reac = parseCompString("O:1 H2:1"); + Composition prod = parseCompString("H:1 OH:1"); + ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); + auto R = make_shared(reac, prod, rate); + R->reversible = false; + R->orders["H2"] = - 0.5; + + ASSERT_THROW(kin->addReaction(R), CanteraError); + ASSERT_EQ((size_t) 0, kin->nReactions()); +} + +TEST_F(KineticsFromScratch2, nonreactant_order_override) +{ + Composition reac = parseCompString("O:1 H2:1"); + Composition prod = parseCompString("H:1 OH:1"); + ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); + auto R = make_shared(reac, prod, rate); + R->reversible = false; + R->allow_nonreactant_orders = true; + R->orders["OH"] = 0.5; + + kin->addReaction(R); + ASSERT_EQ((size_t) 1, kin->nReactions()); +} + +TEST_F(KineticsFromScratch2, invalid_nonreactant_order) +{ + Composition reac = parseCompString("O:1 H2:1"); + Composition prod = parseCompString("H:1 OH:1"); + ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); + auto R = make_shared(reac, prod, rate); + R->reversible = false; + R->orders["OH"] = 0.5; + + ASSERT_THROW(kin->addReaction(R), CanteraError); + ASSERT_EQ((size_t) 0, kin->nReactions()); +} + +/* +class InterfaceKineticsFromScratch2 : public testing::Test +{ +public: + InterfaceKineticsFromScratch2() + : gas("../data/sofc-test.xml", "gas") + , gas_ref("../data/sofc-test.xml", "gas") + , surf("../data/sofc-test.xml", "metal_surface") + , surf_ref("../data/sofc-test.xml", "metal_surface") + { + std::vector th = { &surf_ref, &gas_ref }; + importKinetics(surf_ref.xml(), th, &kin_ref); + kin->addPhase(surf); + kin->addPhase(gas); + } + + IdealGasPhase gas; + IdealGasPhase gas_ref; + SurfPhase surf; + SurfPhase surf_ref; + InterfaceKinetics kin; + InterfaceKinetics kin_ref; + + //! iRef is the index of the corresponding reaction in the reference mech + void check_rates(int iRef) { + ASSERT_EQ((size_t) 1, kin->nReactions()); + + std::string X = "H2:0.2 O2:0.5 H2O:0.1 N2:0.2"; + std::string Xs = "H(m):0.1 O(m):0.2 OH(m):0.3 (m):0.4"; + gas.setState_TPX(1200, 5*OneAtm, X); + gas_ref.setState_TPX(1200, 5*OneAtm, X); + surf.setState_TP(1200, 5*OneAtm); + surf_ref.setState_TP(1200, 5*OneAtm); + surf.setCoveragesByName(Xs); + surf_ref.setCoveragesByName(Xs); + + vector_fp k(1), k_ref(kin_ref->nReactions()); + + kin->getFwdRateConstants(&k[0]); + kin_ref->getFwdRateConstants(&k_ref[0]); + EXPECT_DOUBLE_EQ(k_ref[iRef], k[0]); + + kin->getRevRateConstants(&k[0]); + kin_ref->getRevRateConstants(&k_ref[0]); + EXPECT_DOUBLE_EQ(k_ref[iRef], k[0]); + } +}; + +TEST_F(InterfaceKineticsFromScratch2, add_surface_reaction) +{ + // Reaction 3 on the metal surface + // surface_reaction( "H(m) + O(m) <=> OH(m) + (m)", + // [5.00000E+22, 0, 100.0], id = 'metal-rxn4') + Composition reac = parseCompString("H(m):1 O(m):1"); + Composition prod = parseCompString("OH(m):1 (m):1"); + Arrhenius rate(5e21, 0, 100.0e6 / GasConstant); // kJ/mol -> J/kmol + + auto R = make_shared(reac, prod, rate); + kin->addReaction(R); + check_rates(3); +} + +TEST_F(InterfaceKineticsFromScratch2, add_sticking_reaction) +{ + // Reaction 0 on the metal surface + // surface_reaction( "H2 + (m) + (m) <=> H(m) + H(m)", + // stick(0.1, 0, 0), id = 'metal-rxn1') + Composition reac = parseCompString("H2:1 (m):2"); + Composition prod = parseCompString("H(m):2"); + Arrhenius rate(0.1, 0, 0.0); + + auto R = make_shared(reac, prod, rate, true); + kin->addReaction(R); + check_rates(0); +} + +TEST_F(InterfaceKineticsFromScratch2, unbalanced_sites) +{ + Composition reac = parseCompString("H(m):1 O(m):1"); + Composition prod = parseCompString("OH(m):1"); + Arrhenius rate(5e21, 0, 100.0e6 / GasConstant); + + auto R = make_shared(reac, prod, rate); + ASSERT_THROW(kin->addReaction(R), CanteraError); +} + +class KineticsAddSpecies2 : public testing::Test +{ +public: + KineticsAddSpecies2() + : p_ref("../data/kineticsfromscratch.cti") + { + std::vector th; + th.push_back(&p_ref); + importKinetics(p_ref->xml(), th, &kin_ref); + kin->addPhase(p); + + std::vector> S = getSpecies(*get_XML_File("h2o2.cti")); + for (auto sp : S) { + species[sp->name] = sp; + } + reactions = getReactions(*get_XML_File("../data/kineticsfromscratch.cti")); + } + + IdealGasPhase p; + IdealGasPhase p_ref; + GasKinetics kin; + GasKinetics kin_ref; + std::vector> reactions; + std::map> species; + + void check_rates(size_t N, const std::string& X) { + for (size_t i = 0; i < kin_ref->nReactions(); i++) { + if (i >= N) { + kin_ref->setMultiplier(i, 0); + } else { + kin_ref->setMultiplier(i, 1); + } + } + p->setState_TPX(1200, 5*OneAtm, X); + p_ref->setState_TPX(1200, 5*OneAtm, X); + + vector_fp k(kin->nReactions()), k_ref(kin_ref->nReactions()); + vector_fp w(kin->nTotalSpecies()), w_ref(kin_ref->nTotalSpecies()); + + kin->getFwdRateConstants(k.data()); + kin_ref->getFwdRateConstants(k_ref.data()); + for (size_t i = 0; i < kin->nReactions(); i++) { + EXPECT_DOUBLE_EQ(k_ref[i], k[i]) << "i = " << i << "; N = " << N; + } + + kin->getFwdRatesOfProgress(k.data()); + kin_ref->getFwdRatesOfProgress(k_ref.data()); + for (size_t i = 0; i < kin->nReactions(); i++) { + EXPECT_DOUBLE_EQ(k_ref[i], k[i]) << "i = " << i << "; N = " << N; + } + + kin->getRevRateConstants(k.data()); + kin_ref->getRevRateConstants(k_ref.data()); + for (size_t i = 0; i < kin->nReactions(); i++) { + EXPECT_DOUBLE_EQ(k_ref[i], k[i]) << "i = " << i << "; N = " << N; + } + + kin->getRevRatesOfProgress(k.data()); + kin_ref->getRevRatesOfProgress(k_ref.data()); + for (size_t i = 0; i < kin->nReactions(); i++) { + EXPECT_DOUBLE_EQ(k_ref[i], k[i]) << "i = " << i << "; N = " << N; + } + + kin->getCreationRates(w.data()); + kin_ref->getCreationRates(w_ref.data()); + for (size_t i = 0; i < kin->nTotalSpecies(); i++) { + size_t iref = p_ref->speciesIndex(p->speciesName(i)); + EXPECT_DOUBLE_EQ(w_ref[iref], w[i]) << "sp = " << p->speciesName(i) << "; N = " << N; + } + } +}; + +TEST_F(KineticsAddSpecies2, add_species_sequential) +{ + ASSERT_EQ((size_t) 0, kin->nReactions()); + + for (auto s : {"AR", "O", "H2", "H", "OH"}) { + p->addSpecies(species[s]); + } + kin->addReaction(reactions[0]); + ASSERT_EQ(5, (int) kin->nTotalSpecies()); + check_rates(1, "O:0.001, H2:0.1, H:0.005, OH:0.02, AR:0.88"); + + p->addSpecies(species["O2"]); + p->addSpecies(species["H2O"]); + kin->addReaction(reactions[1]); + ASSERT_EQ(7, (int) kin->nTotalSpecies()); + ASSERT_EQ(2, (int) kin->nReactions()); + check_rates(2, "O:0.001, H2:0.1, H:0.005, OH:0.02, O2:0.5, AR:0.38"); + + p->addSpecies(species["H2O2"]); + kin->addReaction(reactions[2]); + kin->addReaction(reactions[3]); + check_rates(4, "O:0.001, H2:0.1, H:0.005, OH:0.02, O2:0.5, AR:0.38"); // no change + check_rates(4, "O:0.001, H2:0.1, H:0.005, OH:0.02, O2:0.5, AR:0.35, H2O2:0.03"); + + p->addSpecies(species["HO2"]); + kin->addReaction(reactions[4]); + check_rates(5, "O:0.01, H2:0.1, H:0.02, OH:0.03, O2:0.4, AR:0.3, H2O2:0.03, HO2:0.01"); +} + +TEST_F(KineticsAddSpecies2, add_species_err_first) +{ + for (auto s : {"AR", "O", "H2", "H"}) { + p->addSpecies(species[s]); + } + ASSERT_THROW(kin->addReaction(reactions[0]), CanteraError); + ASSERT_EQ((size_t) 0, kin->nReactions()); + + p->addSpecies(species["OH"]); + kin->addReaction(reactions[0]); + ASSERT_EQ(5, (int) kin->nTotalSpecies()); + ASSERT_EQ((size_t) 1, kin->nReactions()); + check_rates(1, "O:0.001, H2:0.1, H:0.005, OH:0.02, AR:0.88"); +} +*/ diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index 261909768da..66f3624eb61 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -10,7 +10,7 @@ using namespace Cantera; -TEST(Reaction, ElementaryFromYaml) +TEST(Reaction, ElementaryFromYaml1) { auto sol = newSolution("gri30.yaml", "", "None"); AnyMap rxn = AnyMap::fromYamlString( @@ -23,6 +23,29 @@ TEST(Reaction, ElementaryFromYaml) EXPECT_EQ(R->products.at("N2"), 1); EXPECT_EQ(R->type(), "elementary"); + auto ER = dynamic_cast(*R); + EXPECT_TRUE(ER.allow_negative_pre_exponential_factor); + EXPECT_FALSE(ER.allow_negative_orders); + + const auto& rate = std::dynamic_pointer_cast(ER.rate()); + EXPECT_DOUBLE_EQ(rate->preExponentialFactor(), -2.7e10); + EXPECT_DOUBLE_EQ(rate->activationEnergy_R(), 355 / GasConst_cal_mol_K); +} + +TEST(Reaction, ElementaryFromYaml2) +{ + auto sol = newSolution("gri30.yaml"); + AnyMap rxn = AnyMap::fromYamlString( + "{equation: N + NO <=> N2 + O," + " type: elementary-old," + " rate-constant: [-2.70000E+13 cm^3/mol/s, 0, 355 cal/mol]," + " negative-A: true}"); + + auto R = newReaction(rxn, *(sol->kinetics())); + EXPECT_EQ(R->reactants.at("NO"), 1); + EXPECT_EQ(R->products.at("N2"), 1); + EXPECT_EQ(R->type(), "elementary-old"); + auto ER = dynamic_cast(*R); EXPECT_DOUBLE_EQ(ER.rate.preExponentialFactor(), -2.7e10); EXPECT_DOUBLE_EQ(ER.rate.activationEnergy_R(), 355 / GasConst_cal_mol_K); @@ -218,8 +241,9 @@ TEST(Kinetics, GasKineticsFromYaml1) EXPECT_EQ(R->reactants.at("NO"), 1); EXPECT_EQ(R->products.at("N2"), 1); EXPECT_EQ(R->id, "NOx-R1"); - const auto& ER = std::dynamic_pointer_cast(R); - EXPECT_DOUBLE_EQ(ER->rate.preExponentialFactor(), 2.7e10); + const auto& ER = std::dynamic_pointer_cast(R); + const auto& rate = std::dynamic_pointer_cast(ER->rate()); + EXPECT_DOUBLE_EQ(rate->preExponentialFactor(), 2.7e10); } TEST(Kinetics, GasKineticsFromYaml2) From 1798508e6713ce0e05fd68915a3581edadfd1586 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 20 Mar 2021 11:59:41 -0500 Subject: [PATCH 08/84] [Kinetics] Expose ElementaryReaction2 to Python --- interfaces/cython/cantera/_cantera.pxd | 5 +- interfaces/cython/cantera/reaction.pyx | 75 +++++++++++++++++++++----- 2 files changed, 66 insertions(+), 14 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 028dd7318a0..49d1e3e0d1d 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -459,12 +459,15 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxChebyshevReaction "Cantera::ChebyshevReaction" (CxxReaction): CxxChebyshevRate rate + cdef cppclass CxxElementaryReaction2 "Cantera::ElementaryReaction2" (CxxReaction2): + CxxElementaryReaction2() + cbool allow_negative_pre_exponential_factor + cdef cppclass CxxCustomFunc1Reaction "Cantera::CustomFunc1Reaction" (CxxReaction2): CxxCustomFunc1Reaction() cdef cppclass CxxTestReaction "Cantera::TestReaction" (CxxReaction2): CxxTestReaction() - cbool allow_negative_pre_exponential_factor cdef cppclass CxxBlowersMasel "Cantera::BlowersMasel": CxxBlowersMasel() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 77b382b752f..219cf323320 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -541,7 +541,7 @@ cdef class ElementaryReaction(Reaction): rate={'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07}, kinetics=gas) """ - reaction_type = "elementary" + reaction_type = "elementary-old" def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): @@ -1024,6 +1024,67 @@ cdef class BlowersMaselReaction(Reaction): cdef CxxBlowersMaselReaction* r = self.reaction r.allow_negative_pre_exponential_factor = allow +cdef class ElementaryReaction2(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 = ElementaryReaction2(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" + + 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 CxxElementaryReaction2* r = self.reaction + return ArrheniusRate.wrap(r.rate()) + def __set__(self, ArrheniusRate rate): + cdef CxxElementaryReaction2* r = self.reaction + r.setRate(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 CxxElementaryReaction2* r = self.reaction + return r.allow_negative_pre_exponential_factor + def __set__(self, allow): + cdef CxxElementaryReaction2* r = self.reaction + r.allow_negative_pre_exponential_factor = allow + + cdef class CustomReaction(Reaction): """ A reaction which follows mass-action kinetics with a custom reaction rate. @@ -1108,18 +1169,6 @@ cdef class TestReaction(Reaction): cdef CxxTestReaction* r = self.reaction r.setRate(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): """ A reaction occurring on an `Interface` (i.e. a surface or an edge) """ From 1d532c812ed451120bffc96ed868e59ff41e584e Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 20 Mar 2021 12:00:35 -0500 Subject: [PATCH 09/84] [CI] Update Python tests --- interfaces/cython/cantera/test/test_kinetics.py | 2 +- interfaces/cython/cantera/test/test_reaction.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index a5e035ec8c1..1be097dc8b7 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -49,7 +49,7 @@ def test_multiplier(self): def test_reaction_type(self): self.assertEqual(self.phase.reaction_type_str(0), "three-body") - self.assertEqual(self.phase.reaction_type_str(2), "elementary") + self.assertIn(self.phase.reaction_type_str(2), ["elementary", "elementary-old"]) self.assertEqual(self.phase.reaction_type_str(21), "falloff") with self.assertRaisesRegex(ValueError, 'out of range'): diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index f2d66c6b36c..66794454dae 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -116,7 +116,7 @@ class TestElementary(utilities.CanteraTest): _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" + _type = "elementary-old" @classmethod def setUpClass(cls): @@ -193,6 +193,16 @@ def test_replace_rate(self): self.check_rxn(rxn) +class TestElementary2(TestElementary): + + _cls = ct.ElementaryReaction2 + _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" + + class TestCustom(TestElementary): # probe O + H2 <=> H + OH From 45b90672044dec33858cbfd8eaeb739d1fc5fbc9 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 20 Mar 2021 12:13:13 -0500 Subject: [PATCH 10/84] [CI] Accommodate new names in conversion tests --- interfaces/cython/cantera/test/test_convert.py | 10 ++++++++-- test/kinetics/kineticsFromYaml.cpp | 13 ++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/interfaces/cython/cantera/test/test_convert.py b/interfaces/cython/cantera/test/test_convert.py index 7ae4c871019..5521cde9a89 100644 --- a/interfaces/cython/cantera/test/test_convert.py +++ b/interfaces/cython/cantera/test/test_convert.py @@ -604,7 +604,10 @@ def checkConversion(self, basename, cls=ct.Solution, ctiphases=(), self.assertEqual(C.composition, Y.composition) for C, Y in zip(ctiPhase.reactions(), yamlPhase.reactions()): - self.assertEqual(C.__class__, Y.__class__) + if Y.__class__.__name__.endswith('2'): + self.assertEqual(C.__class__.__name__, Y.__class__.__name__[:-1]) + else: + self.assertEqual(C.__class__, Y.__class__) self.assertEqual(C.reactants, Y.reactants) self.assertEqual(C.products, Y.products) self.assertEqual(C.duplicate, Y.duplicate) @@ -851,7 +854,10 @@ def checkConversion(self, basename, cls=ct.Solution, ctmlphases=(), self.assertEqual(C.composition, Y.composition) for C, Y in zip(ctmlPhase.reactions(), yamlPhase.reactions()): - self.assertEqual(C.__class__, Y.__class__) + if Y.__class__.__name__.endswith('2'): + self.assertEqual(C.__class__.__name__, Y.__class__.__name__[:-1]) + else: + self.assertEqual(C.__class__, Y.__class__) self.assertEqual(C.reactants, Y.reactants) self.assertEqual(C.products, Y.products) self.assertEqual(C.duplicate, Y.duplicate) diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index 66f3624eb61..da3fbe7d0d3 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -421,11 +421,14 @@ class ReactionToYaml : public testing::Test iNew = kin->nReactions() - 1; } - void compareReactions() { + void compareReactions(bool old=false) { auto kin = soln->kinetics(); EXPECT_EQ(kin->reactionString(iOld), kin->reactionString(iNew)); - EXPECT_EQ(kin->reactionTypeStr(iOld), kin->reactionTypeStr(iNew)); - EXPECT_EQ(kin->isReversible(iOld), kin->isReversible(iNew)); + if (old) { + EXPECT_EQ(kin->reactionTypeStr(iOld), kin->reactionTypeStr(iNew) + "-old"); + } else { + EXPECT_EQ(kin->isReversible(iOld), kin->isReversible(iNew)); + } vector_fp kf(kin->nReactions()), kr(kin->nReactions()); vector_fp ropf(kin->nReactions()), ropr(kin->nReactions()); @@ -451,8 +454,8 @@ TEST_F(ReactionToYaml, elementary) soln = newSolution("h2o2.yaml", "", "None"); soln->thermo()->setState_TPY(1000, 2e5, "H2:1.0, O2:0.5, O:1e-8, OH:3e-8"); duplicateReaction(2); - EXPECT_TRUE(std::dynamic_pointer_cast(duplicate)); - compareReactions(); + EXPECT_TRUE(std::dynamic_pointer_cast(duplicate)); + compareReactions(true); } TEST_F(ReactionToYaml, threeBody) From ec98014b838d03c5a34c9c7eebf77ed29f933374 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 20 Mar 2021 13:31:04 -0500 Subject: [PATCH 11/84] [Kinetics] Rename Reaction2 to Reaction3 to reflect Cantera version --- include/cantera/kinetics/Reaction.h | 25 +++---- interfaces/cython/cantera/_cantera.pxd | 12 ++-- interfaces/cython/cantera/reaction.pyx | 43 ++++++------ .../cython/cantera/test/test_convert.py | 4 +- .../cython/cantera/test/test_reaction.py | 4 +- src/kinetics/BulkKinetics.cpp | 8 +-- src/kinetics/GasKinetics.cpp | 4 +- src/kinetics/Reaction.cpp | 30 ++++---- src/kinetics/ReactionFactory.cpp | 2 +- ...mScratch2.cpp => kineticsFromScratch3.cpp} | 68 +++++++++---------- test/kinetics/kineticsFromYaml.cpp | 4 +- 11 files changed, 101 insertions(+), 103 deletions(-) rename test/kinetics/{kineticsFromScratch2.cpp => kineticsFromScratch3.cpp} (89%) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index b955d3653ab..557a164c93a 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -154,11 +154,11 @@ class Reaction //! An intermediate class used to avoid naming conflicts of 'rate' member //! variables and getters (see `ElementaryReaction`, `PlogReaction` and //! `ChebyshevReaction`). -class Reaction2 : public Reaction +class Reaction3 : public Reaction { public: - Reaction2() : Reaction() {} - Reaction2(const Composition& reactants, const Composition& products); + Reaction3() : Reaction() {} + Reaction3(const Composition& reactants, const Composition& products); //! Get reaction rate pointer shared_ptr rate() { @@ -348,18 +348,15 @@ class ChebyshevReaction : public Reaction //! reaction rate. /** * 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. */ -class ElementaryReaction2 : public Reaction2 +class ElementaryReaction3 : public Reaction3 { public: - ElementaryReaction2(); - ElementaryReaction2(const Composition& reactants, const Composition& products, + ElementaryReaction3(); + ElementaryReaction3(const Composition& reactants, const Composition& products, const ArrheniusRate& rate); - ElementaryReaction2(const AnyMap& node, const Kinetics& kin); + ElementaryReaction3(const AnyMap& node, const Kinetics& kin); virtual std::string type() const { return "elementary"; @@ -375,11 +372,7 @@ class ElementaryReaction2 : public Reaction2 //! 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 CustomFunc1Reaction : public Reaction2 +class CustomFunc1Reaction : public Reaction3 { public: CustomFunc1Reaction(); @@ -402,7 +395,7 @@ class CustomFunc1Reaction : public Reaction2 * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. */ -class TestReaction : public Reaction2 +class TestReaction : public Reaction3 { public: TestReaction(); diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 49d1e3e0d1d..8910668a359 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -392,8 +392,8 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cbool allow_nonreactant_orders cbool allow_negative_orders - cdef cppclass CxxReaction2 "Cantera::Reaction2" (CxxReaction): - CxxReaction2() + cdef cppclass CxxReaction3 "Cantera::Reaction3" (CxxReaction): + CxxReaction3() shared_ptr[CxxReactionRateBase] rate() void setRate(shared_ptr[CxxReactionRateBase]) @@ -459,14 +459,14 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxChebyshevReaction "Cantera::ChebyshevReaction" (CxxReaction): CxxChebyshevRate rate - cdef cppclass CxxElementaryReaction2 "Cantera::ElementaryReaction2" (CxxReaction2): - CxxElementaryReaction2() + cdef cppclass CxxElementaryReaction3 "Cantera::ElementaryReaction3" (CxxReaction3): + CxxElementaryReaction3() cbool allow_negative_pre_exponential_factor - cdef cppclass CxxCustomFunc1Reaction "Cantera::CustomFunc1Reaction" (CxxReaction2): + cdef cppclass CxxCustomFunc1Reaction "Cantera::CustomFunc1Reaction" (CxxReaction3): CxxCustomFunc1Reaction() - cdef cppclass CxxTestReaction "Cantera::TestReaction" (CxxReaction2): + cdef cppclass CxxTestReaction "Cantera::TestReaction" (CxxReaction3): CxxTestReaction() cdef cppclass CxxBlowersMasel "Cantera::BlowersMasel": diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 219cf323320..a6f4eb4dc41 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -540,6 +540,12 @@ cdef class ElementaryReaction(Reaction): rxn = ElementaryReaction(equation='H2 + O <=> H + OH', rate={'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07}, kinetics=gas) + + .. deprecated:: 2.6 + + This class is superseded by `ElementaryReaction3` and only used by XML. + The implementation of this reaction type will change after Cantera 2.6; + refer to `ElementaryReaction3` for new behavior. """ reaction_type = "elementary-old" @@ -1024,20 +1030,21 @@ cdef class BlowersMaselReaction(Reaction): cdef CxxBlowersMaselReaction* r = self.reaction r.allow_negative_pre_exponential_factor = allow -cdef class ElementaryReaction2(Reaction): +cdef class ElementaryReaction3(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. + reaction rate. - An example for the definition of a `TestReaction` object is given as:: + An example for the definition of an `ElementaryReaction3` object is given + as:: - rxn = ElementaryReaction2(equation='H2 + O <=> H + OH', - rate={'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07}, - kinetics=gas) + rxn = ElementaryReaction3( + 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. + This class is a replacement for `ElementaryReaction` and cannot be + instantiated from XML. It is the default for import from YAML. """ reaction_type = "elementary" @@ -1066,10 +1073,10 @@ cdef class ElementaryReaction2(Reaction): property rate: """ Get/Set the `Arrhenius` rate coefficient for this reaction. """ def __get__(self): - cdef CxxElementaryReaction2* r = self.reaction + cdef CxxElementaryReaction3* r = self.reaction return ArrheniusRate.wrap(r.rate()) def __set__(self, ArrheniusRate rate): - cdef CxxElementaryReaction2* r = self.reaction + cdef CxxElementaryReaction3* r = self.reaction r.setRate(rate._base) property allow_negative_pre_exponential_factor: @@ -1078,10 +1085,10 @@ cdef class ElementaryReaction2(Reaction): pre-exponential factor. """ def __get__(self): - cdef CxxElementaryReaction2* r = self.reaction + cdef CxxElementaryReaction3* r = self.reaction return r.allow_negative_pre_exponential_factor def __set__(self, allow): - cdef CxxElementaryReaction2* r = self.reaction + cdef CxxElementaryReaction3* r = self.reaction r.allow_negative_pre_exponential_factor = allow @@ -1091,12 +1098,10 @@ cdef class CustomReaction(Reaction): 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.15428/T), - kinetics=gas) - - Warning: this class is an experimental part of the Cantera API and - may be changed or removed without notice. + rxn = CustomReaction( + equation='H2 + O <=> H + OH', + rate=lambda T: 38.7 * T**2.7 * exp(-3150.15428/T), + kinetics=gas) """ reaction_type = "custom-rate-function" diff --git a/interfaces/cython/cantera/test/test_convert.py b/interfaces/cython/cantera/test/test_convert.py index 5521cde9a89..87a6ef84af8 100644 --- a/interfaces/cython/cantera/test/test_convert.py +++ b/interfaces/cython/cantera/test/test_convert.py @@ -604,7 +604,7 @@ def checkConversion(self, basename, cls=ct.Solution, ctiphases=(), self.assertEqual(C.composition, Y.composition) for C, Y in zip(ctiPhase.reactions(), yamlPhase.reactions()): - if Y.__class__.__name__.endswith('2'): + if Y.__class__.__name__.endswith('3'): self.assertEqual(C.__class__.__name__, Y.__class__.__name__[:-1]) else: self.assertEqual(C.__class__, Y.__class__) @@ -854,7 +854,7 @@ def checkConversion(self, basename, cls=ct.Solution, ctmlphases=(), self.assertEqual(C.composition, Y.composition) for C, Y in zip(ctmlPhase.reactions(), yamlPhase.reactions()): - if Y.__class__.__name__.endswith('2'): + if Y.__class__.__name__.endswith('3'): self.assertEqual(C.__class__.__name__, Y.__class__.__name__[:-1]) else: self.assertEqual(C.__class__, Y.__class__) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 66794454dae..c21a3962670 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -193,9 +193,9 @@ def test_replace_rate(self): self.check_rxn(rxn) -class TestElementary2(TestElementary): +class TestElementary3(TestElementary): - _cls = ct.ElementaryReaction2 + _cls = ct.ElementaryReaction3 _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) diff --git a/src/kinetics/BulkKinetics.cpp b/src/kinetics/BulkKinetics.cpp index 0fa5aecd26a..4fab774b22f 100644 --- a/src/kinetics/BulkKinetics.cpp +++ b/src/kinetics/BulkKinetics.cpp @@ -123,9 +123,9 @@ bool BulkKinetics::addReaction(shared_ptr r) m_irrev.push_back(nReactions()-1); } - if (std::dynamic_pointer_cast(r) != nullptr) { + if (std::dynamic_pointer_cast(r) != nullptr) { shared_ptr rate; - rate = std::dynamic_pointer_cast(r)->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(); @@ -157,9 +157,9 @@ void BulkKinetics::modifyReaction(size_t i, shared_ptr rNew) // operations common to all reaction types Kinetics::modifyReaction(i, rNew); - if (std::dynamic_pointer_cast(rNew) != nullptr) { + if (std::dynamic_pointer_cast(rNew) != nullptr) { shared_ptr rate; - rate = std::dynamic_pointer_cast(rNew)->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 25fb1623eb0..17d20d328b7 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -242,7 +242,7 @@ bool GasKinetics::addReaction(shared_ptr r) bool added = BulkKinetics::addReaction(r); if (!added) { return false; - } else if (std::dynamic_pointer_cast(r) != nullptr) { + } else if (std::dynamic_pointer_cast(r) != nullptr) { // Rate object already added in BulkKinetics::addReaction return true; } @@ -334,7 +334,7 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) // operations common to all bulk reaction types BulkKinetics::modifyReaction(i, rNew); - if (std::dynamic_pointer_cast(rNew) != nullptr) { + 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 32c29cb0e4b..a712c6cad1d 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -637,12 +637,12 @@ ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, reaction_type = CHEBYSHEV_RXN; } -Reaction2::Reaction2(const Composition& reactants, const Composition& products) +Reaction3::Reaction3(const Composition& reactants, const Composition& products) : Reaction(reactants, products) { } -bool Reaction2::setParameters(const AnyMap& node, const Kinetics& kin) +bool Reaction3::setParameters(const AnyMap& node, const Kinetics& kin) { if (!node.hasKey("equation")) { // empty node: used by newReaction() factory loader @@ -672,31 +672,31 @@ bool Reaction2::setParameters(const AnyMap& node, const Kinetics& kin) return true; } -ElementaryReaction2::ElementaryReaction2() - : Reaction2() +ElementaryReaction3::ElementaryReaction3() + : Reaction3() , allow_negative_pre_exponential_factor(false) { m_rate = std::shared_ptr(new ArrheniusRate); } -ElementaryReaction2::ElementaryReaction2(const Composition& reactants, +ElementaryReaction3::ElementaryReaction3(const Composition& reactants, const Composition& products, const ArrheniusRate& rate) - : Reaction2(reactants, products) + : Reaction3(reactants, products) , allow_negative_pre_exponential_factor(false) { m_rate = std::make_shared(rate); } -ElementaryReaction2::ElementaryReaction2(const AnyMap& node, const Kinetics& kin) - : ElementaryReaction2() +ElementaryReaction3::ElementaryReaction3(const AnyMap& node, const Kinetics& kin) + : ElementaryReaction3() { setParameters(node, kin); } -bool ElementaryReaction2::setParameters(const AnyMap& node, const Kinetics& kin) +bool ElementaryReaction3::setParameters(const AnyMap& node, const Kinetics& kin) { - if (!Reaction2::setParameters(node, kin)) { + if (!Reaction3::setParameters(node, kin)) { return false; } setRate(std::shared_ptr(new ArrheniusRate(node, rate_units))); @@ -704,7 +704,7 @@ bool ElementaryReaction2::setParameters(const AnyMap& node, const Kinetics& kin) return true; } -void ElementaryReaction2::validate() +void ElementaryReaction3::validate() { Reaction::validate(); if (!allow_negative_pre_exponential_factor && @@ -716,7 +716,7 @@ void ElementaryReaction2::validate() } CustomFunc1Reaction::CustomFunc1Reaction() - : Reaction2() + : Reaction3() { m_rate = std::shared_ptr(new CustomFunc1Rate); } @@ -729,13 +729,13 @@ CustomFunc1Reaction::CustomFunc1Reaction(const AnyMap& node, const Kinetics& kin bool CustomFunc1Reaction::setParameters(const AnyMap& node, const Kinetics& kin) { - bool ok = Reaction2::setParameters(node, kin); + bool ok = Reaction3::setParameters(node, kin); setRate(std::shared_ptr(new CustomFunc1Rate(node, rate_units))); return ok; } TestReaction::TestReaction() - : Reaction2() + : Reaction3() { m_rate = std::shared_ptr(new ArrheniusRate); } @@ -748,7 +748,7 @@ TestReaction::TestReaction(const AnyMap& node, const Kinetics& kin) bool TestReaction::setParameters(const AnyMap& node, const Kinetics& kin) { - if (!Reaction2::setParameters(node, kin)) { + if (!Reaction3::setParameters(node, kin)) { return false; } setRate(std::shared_ptr(new ArrheniusRate(node, rate_units))); diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 7368032e705..15d8d65f351 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -23,7 +23,7 @@ ReactionFactory::ReactionFactory() { // register elementary reactions reg("elementary", [](const AnyMap& node, const Kinetics& kin) { - return new ElementaryReaction2(node, kin); + return new ElementaryReaction3(node, kin); }); addAlias("elementary", "arrhenius"); addAlias("elementary", ""); diff --git a/test/kinetics/kineticsFromScratch2.cpp b/test/kinetics/kineticsFromScratch3.cpp similarity index 89% rename from test/kinetics/kineticsFromScratch2.cpp rename to test/kinetics/kineticsFromScratch3.cpp index 22589a7dea2..c79f567b344 100644 --- a/test/kinetics/kineticsFromScratch2.cpp +++ b/test/kinetics/kineticsFromScratch3.cpp @@ -10,10 +10,10 @@ using namespace Cantera; -class KineticsFromScratch2 : public testing::Test +class KineticsFromScratch3 : public testing::Test { public: - KineticsFromScratch2() + KineticsFromScratch3() { std::string yaml_file = "../data/kineticsfromscratch.yaml"; std::string phase_name = "ohmech"; @@ -53,21 +53,21 @@ class KineticsFromScratch2 : public testing::Test } }; -TEST_F(KineticsFromScratch2, add_elementary_reaction) +TEST_F(KineticsFromScratch3, add_elementary_reaction) { // reaction 0: // reaction('O + H2 <=> H + OH', [3.870000e+01, 2.7, 6260.0]) Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); kin->addReaction(R); check_rates(0); } /* -TEST_F(KineticsFromScratch2, add_three_body_reaction) +TEST_F(KineticsFromScratch3, add_three_body_reaction) { // reaction 1: // three_body_reaction('2 O + M <=> O2 + M', [1.200000e+11, -1.0, 0.0], @@ -83,7 +83,7 @@ TEST_F(KineticsFromScratch2, add_three_body_reaction) check_rates(1); } -TEST_F(KineticsFromScratch2, undefined_third_body) +TEST_F(KineticsFromScratch3, undefined_third_body) { Composition reac = parseCompString("O:2"); Composition prod = parseCompString("O2:1"); @@ -95,7 +95,7 @@ TEST_F(KineticsFromScratch2, undefined_third_body) ASSERT_THROW(kin->addReaction(R), CanteraError); } -TEST_F(KineticsFromScratch2, skip_undefined_third_body) +TEST_F(KineticsFromScratch3, skip_undefined_third_body) { Composition reac = parseCompString("O:2"); Composition prod = parseCompString("O2:1"); @@ -110,7 +110,7 @@ TEST_F(KineticsFromScratch2, skip_undefined_third_body) } -TEST_F(KineticsFromScratch2, add_falloff_reaction) +TEST_F(KineticsFromScratch3, add_falloff_reaction) { // reaction 2: // falloff_reaction('2 OH (+ M) <=> H2O2 (+ M)', @@ -131,7 +131,7 @@ TEST_F(KineticsFromScratch2, add_falloff_reaction) check_rates(2); } -TEST_F(KineticsFromScratch2, add_plog_reaction) +TEST_F(KineticsFromScratch3, add_plog_reaction) { // reaction 3: // pdep_arrhenius('H2 + O2 <=> 2 OH', @@ -153,7 +153,7 @@ TEST_F(KineticsFromScratch2, add_plog_reaction) check_rates(3); } -TEST_F(KineticsFromScratch2, plog_invalid_rate) +TEST_F(KineticsFromScratch3, plog_invalid_rate) { Composition reac = parseCompString("H2:1, O2:1"); Composition prod = parseCompString("OH:2"); @@ -168,7 +168,7 @@ TEST_F(KineticsFromScratch2, plog_invalid_rate) ASSERT_THROW(kin->addReaction(R), CanteraError); } -TEST_F(KineticsFromScratch2, add_chebyshev_reaction) +TEST_F(KineticsFromScratch3, add_chebyshev_reaction) { // reaction 4: // chebyshev_reaction( @@ -201,70 +201,70 @@ TEST_F(KineticsFromScratch2, add_chebyshev_reaction) } */ -TEST_F(KineticsFromScratch2, undeclared_species) +TEST_F(KineticsFromScratch3, undeclared_species) { Composition reac = parseCompString("CO:1 OH:1"); Composition prod = parseCompString("CO2:1 H:1"); ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); ASSERT_THROW(kin->addReaction(R), CanteraError); ASSERT_EQ((size_t) 0, kin->nReactions()); } -TEST_F(KineticsFromScratch2, skip_undeclared_species) +TEST_F(KineticsFromScratch3, skip_undeclared_species) { Composition reac = parseCompString("CO:1 OH:1"); Composition prod = parseCompString("CO2:1 H:1"); ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); kin->skipUndeclaredSpecies(true); kin->addReaction(R); ASSERT_EQ((size_t) 0, kin->nReactions()); } -TEST_F(KineticsFromScratch2, negative_A_error) +TEST_F(KineticsFromScratch3, negative_A_error) { Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); ArrheniusRate rate(-3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); ASSERT_THROW(kin->addReaction(R), CanteraError); ASSERT_EQ((size_t) 0, kin->nReactions()); } -TEST_F(KineticsFromScratch2, allow_negative_A) +TEST_F(KineticsFromScratch3, allow_negative_A) { Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); ArrheniusRate rate(-3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); R->allow_negative_pre_exponential_factor = true; kin->addReaction(R); ASSERT_EQ((size_t) 1, kin->nReactions()); } -TEST_F(KineticsFromScratch2, invalid_reversible_with_orders) +TEST_F(KineticsFromScratch3, invalid_reversible_with_orders) { Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); R->orders["H2"] = 0.5; ASSERT_THROW(kin->addReaction(R), CanteraError); ASSERT_EQ((size_t) 0, kin->nReactions()); } -TEST_F(KineticsFromScratch2, negative_order_override) +TEST_F(KineticsFromScratch3, negative_order_override) { Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); R->reversible = false; R->allow_negative_orders = true; R->orders["H2"] = - 0.5; @@ -273,12 +273,12 @@ TEST_F(KineticsFromScratch2, negative_order_override) ASSERT_EQ((size_t) 1, kin->nReactions()); } -TEST_F(KineticsFromScratch2, invalid_negative_orders) +TEST_F(KineticsFromScratch3, invalid_negative_orders) { Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); R->reversible = false; R->orders["H2"] = - 0.5; @@ -286,12 +286,12 @@ TEST_F(KineticsFromScratch2, invalid_negative_orders) ASSERT_EQ((size_t) 0, kin->nReactions()); } -TEST_F(KineticsFromScratch2, nonreactant_order_override) +TEST_F(KineticsFromScratch3, nonreactant_order_override) { Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); R->reversible = false; R->allow_nonreactant_orders = true; R->orders["OH"] = 0.5; @@ -300,12 +300,12 @@ TEST_F(KineticsFromScratch2, nonreactant_order_override) ASSERT_EQ((size_t) 1, kin->nReactions()); } -TEST_F(KineticsFromScratch2, invalid_nonreactant_order) +TEST_F(KineticsFromScratch3, invalid_nonreactant_order) { Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); R->reversible = false; R->orders["OH"] = 0.5; @@ -314,10 +314,10 @@ TEST_F(KineticsFromScratch2, invalid_nonreactant_order) } /* -class InterfaceKineticsFromScratch2 : public testing::Test +class InterfaceKineticsFromScratch3 : public testing::Test { public: - InterfaceKineticsFromScratch2() + InterfaceKineticsFromScratch3() : gas("../data/sofc-test.xml", "gas") , gas_ref("../data/sofc-test.xml", "gas") , surf("../data/sofc-test.xml", "metal_surface") @@ -361,7 +361,7 @@ class InterfaceKineticsFromScratch2 : public testing::Test } }; -TEST_F(InterfaceKineticsFromScratch2, add_surface_reaction) +TEST_F(InterfaceKineticsFromScratch3, add_surface_reaction) { // Reaction 3 on the metal surface // surface_reaction( "H(m) + O(m) <=> OH(m) + (m)", @@ -375,7 +375,7 @@ TEST_F(InterfaceKineticsFromScratch2, add_surface_reaction) check_rates(3); } -TEST_F(InterfaceKineticsFromScratch2, add_sticking_reaction) +TEST_F(InterfaceKineticsFromScratch3, add_sticking_reaction) { // Reaction 0 on the metal surface // surface_reaction( "H2 + (m) + (m) <=> H(m) + H(m)", @@ -389,7 +389,7 @@ TEST_F(InterfaceKineticsFromScratch2, add_sticking_reaction) check_rates(0); } -TEST_F(InterfaceKineticsFromScratch2, unbalanced_sites) +TEST_F(InterfaceKineticsFromScratch3, unbalanced_sites) { Composition reac = parseCompString("H(m):1 O(m):1"); Composition prod = parseCompString("OH(m):1"); diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index da3fbe7d0d3..14d405874fc 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -23,7 +23,7 @@ TEST(Reaction, ElementaryFromYaml1) EXPECT_EQ(R->products.at("N2"), 1); EXPECT_EQ(R->type(), "elementary"); - auto ER = dynamic_cast(*R); + auto ER = dynamic_cast(*R); EXPECT_TRUE(ER.allow_negative_pre_exponential_factor); EXPECT_FALSE(ER.allow_negative_orders); @@ -241,7 +241,7 @@ TEST(Kinetics, GasKineticsFromYaml1) EXPECT_EQ(R->reactants.at("NO"), 1); EXPECT_EQ(R->products.at("N2"), 1); EXPECT_EQ(R->id, "NOx-R1"); - const auto& ER = std::dynamic_pointer_cast(R); + const auto& ER = std::dynamic_pointer_cast(R); const auto& rate = std::dynamic_pointer_cast(ER->rate()); EXPECT_DOUBLE_EQ(rate->preExponentialFactor(), 2.7e10); } From fea207eea40d9a717874552b0bc7a4bf9ae77d96 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 20 Mar 2021 22:21:54 -0500 Subject: [PATCH 12/84] [Kinetics] Fix shadowed member variable --- src/kinetics/Reaction.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index a712c6cad1d..9470f30a72f 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -651,7 +651,6 @@ bool Reaction3::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; @@ -1234,7 +1233,7 @@ void setupReaction(Reaction& R, const AnyMap& node, const Kinetics& kin) { parseReactionEquation(R, node["equation"], kin); // Non-stoichiometric reaction orders - std::map orders; + //std::map orders; if (node.hasKey("orders")) { for (const auto& order : node["orders"].asMap()) { R.orders[order.first] = order.second; From 6a2157e56a8d51f5352c9d01d24ae58425919d46 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 25 Apr 2021 08:16:21 -0500 Subject: [PATCH 13/84] [Kinetics] Add setParameters to ElementaryReaction3 --- include/cantera/kinetics/Reaction.h | 1 + include/cantera/kinetics/ReactionRate.h | 10 ++++++++++ src/kinetics/Reaction.cpp | 11 +++++++++++ src/kinetics/ReactionRate.cpp | 5 +++++ 4 files changed, 27 insertions(+) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 557a164c93a..6f0f8135773 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -363,6 +363,7 @@ class ElementaryReaction3 : public Reaction3 } virtual bool setParameters(const AnyMap& node, const Kinetics& kin); + virtual void getParameters(AnyMap& reactionNode) const; virtual void validate(); diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 485a9041564..18e9b2b245b 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -46,6 +46,14 @@ class ReactionRateBase "Not implemented by derived ReactionRate object."); } + //! Get parameters + //! Store the parameters of a ReactionRate needed to reconstruct an identical + //! object. Does not include user-defined fields available in the #input map. + virtual void getParameters(AnyMap& rateNode, const Units& rate_units) const { + throw CanteraError("ReactionRate::getParameters", + "Not implemented by derived ReactionRate object."); + } + //! Identifier of reaction type virtual std::string type() const = 0; @@ -143,6 +151,8 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius ArrheniusRate(const AnyMap& node, const Units& rate_units); virtual bool setParameters(const AnyMap& node, const Units& rate_units) override; + virtual void getParameters(AnyMap& rateNode, + const Units& rate_units) const override; virtual std::string type() const override { return "ArrheniusRate"; } diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 9470f30a72f..00d37f69ec9 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -703,6 +703,17 @@ bool ElementaryReaction3::setParameters(const AnyMap& node, const Kinetics& kin) return true; } +void ElementaryReaction3::getParameters(AnyMap& reactionNode) const +{ + Reaction::getParameters(reactionNode); + if (allow_negative_pre_exponential_factor) { + reactionNode["negative-A"] = true; + } + AnyMap rateNode; + m_rate->getParameters(rateNode, rate_units); + reactionNode["rate-constant"] = std::move(rateNode); +} + void ElementaryReaction3::validate() { Reaction::validate(); diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index dc5387e9917..7058987e734 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -30,6 +30,11 @@ bool ArrheniusRate::setParameters(const AnyMap& node, const Units& rate_units) { return true; } +void ArrheniusRate::getParameters(AnyMap& rateNode, + const Units& rate_units) const { + Arrhenius::getParameters(rateNode, rate_units); +} + CustomFunc1Rate::CustomFunc1Rate() : m_ratefunc(0) {} void CustomFunc1Rate::setRateFunction(shared_ptr f) { From cedceabe0a9491468e9851ff343d7c2bb30d1137 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 21 Mar 2021 06:22:57 -0500 Subject: [PATCH 14/84] [Kinetics] Create ThreeBodyReaction3 --- include/cantera/kinetics/Reaction.h | 34 ++++++++++++++++-- src/kinetics/Reaction.cpp | 55 ++++++++++++++++++++++++++--- src/kinetics/ReactionFactory.cpp | 5 +++ 3 files changed, 87 insertions(+), 7 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 6f0f8135773..6a85ca9cc46 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -204,6 +204,11 @@ class ThirdBody public: explicit ThirdBody(double default_efficiency=1.0); + ThirdBody(const AnyMap& node); + + //! Set third-body efficiencies from AnyMap *node* + bool setEfficiencies(const AnyMap& node); + //! Get the third-body efficiency for species *k* double efficiency(const std::string& k) const; @@ -346,9 +351,6 @@ class ChebyshevReaction : public Reaction //! A reaction which follows mass-action kinetics with a modified Arrhenius //! reaction rate. -/** - * Alternative elementary reaction based on ReactionRate. - */ class ElementaryReaction3 : public Reaction3 { public: @@ -371,6 +373,32 @@ class ElementaryReaction3 : public Reaction3 }; +//! A reaction with a non-reacting third body "M" that acts to add or remove +//! energy from the reacting species +class ThreeBodyReaction3 : public ElementaryReaction3 +{ +public: + ThreeBodyReaction3(); + ThreeBodyReaction3(const Composition& reactants, const Composition& products, + const ArrheniusRate& rate, const ThirdBody& tbody); + + ThreeBodyReaction3(const AnyMap& node, const Kinetics& kin); + + virtual std::string type() const { + return "three-body-new"; + } + + virtual bool setParameters(const AnyMap& node, const Kinetics& kin); + + virtual std::string reactantString() const; + virtual std::string productString() const; + + //! Relative efficiencies of third-body species in enhancing the reaction + //! rate. + ThirdBody third_body; +}; + + //! A reaction which follows mass-action kinetics with a custom reaction rate //! defined in Python. class CustomFunc1Reaction : public Reaction3 diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 00d37f69ec9..380b3d66883 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -387,6 +387,20 @@ ThirdBody::ThirdBody(double default_eff) { } +ThirdBody::ThirdBody(const AnyMap& node) +{ + setEfficiencies(node); +} + +bool ThirdBody::setEfficiencies(const AnyMap& node) +{ + default_efficiency = node.getDouble("default-efficiency", 1.0); + if (node.hasKey("efficiencies")) { + efficiencies = node["efficiencies"].asMap(); + } + return true; +} + double ThirdBody::efficiency(const std::string& k) const { return getValue(efficiencies, k, default_efficiency); @@ -725,6 +739,42 @@ void ElementaryReaction3::validate() } } +ThreeBodyReaction3::ThreeBodyReaction3() + : ElementaryReaction3() +{ +} + +ThreeBodyReaction3::ThreeBodyReaction3(const Composition& reactants, + const Composition& products, + const ArrheniusRate& rate, + const ThirdBody& tbody) + : ElementaryReaction3(reactants, products, rate) + , third_body(tbody) +{ +} + +ThreeBodyReaction3::ThreeBodyReaction3(const AnyMap& node, const Kinetics& kin) + : ThreeBodyReaction3() +{ + setParameters(node, kin); +} + +bool ThreeBodyReaction3::setParameters(const AnyMap& node, const Kinetics& kin) +{ + if (!ElementaryReaction3::setParameters(node, kin)) { + return false; + } + return third_body.setEfficiencies(node); +} + +std::string ThreeBodyReaction3::reactantString() const { + return ElementaryReaction3::reactantString() + " + M"; +} + +std::string ThreeBodyReaction3::productString() const { + return ElementaryReaction3::productString() + " + M"; +} + CustomFunc1Reaction::CustomFunc1Reaction() : Reaction3() { @@ -1084,10 +1134,7 @@ void readEfficiencies(ThirdBody& tbody, const XML_Node& rc_node) void readEfficiencies(ThirdBody& tbody, const AnyMap& node) { - tbody.default_efficiency = node.getDouble("default-efficiency", 1.0); - if (node.hasKey("efficiencies")) { - tbody.efficiencies = node["efficiencies"].asMap(); - } + tbody.setEfficiencies(node); } BlowersMasel readBlowersMasel(const Reaction& R, const AnyValue& rate, diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 15d8d65f351..e1e801873a4 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -36,6 +36,11 @@ ReactionFactory::ReactionFactory() return R; }); + // register three-body reactions + reg("three-body-new", [](const AnyMap& node, const Kinetics& kin) { + return new ThreeBodyReaction3(node, kin); + }); + // register three-body reactions reg("three-body", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ThreeBodyReaction(); From 79bd243af17375ad297c3368dc78e7b336424481 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 21 Mar 2021 07:10:35 -0500 Subject: [PATCH 15/84] [Kinetics] Expose ThreeBodyReaction3 to Python --- interfaces/cython/cantera/_cantera.pxd | 4 + interfaces/cython/cantera/reaction.pyx | 117 +++++++++++++++++++++---- 2 files changed, 105 insertions(+), 16 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 8910668a359..a743a0f7b0e 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -463,6 +463,10 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": CxxElementaryReaction3() cbool allow_negative_pre_exponential_factor + cdef cppclass CxxThreeBodyReaction3 "Cantera::ThreeBodyReaction3" (CxxElementaryReaction3): + CxxThreeBodyReaction3() + CxxThirdBody third_body + cdef cppclass CxxCustomFunc1Reaction "Cantera::CustomFunc1Reaction" (CxxReaction3): CxxCustomFunc1Reaction() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index a6f4eb4dc41..9843432c027 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -543,8 +543,8 @@ cdef class ElementaryReaction(Reaction): .. deprecated:: 2.6 - This class is superseded by `ElementaryReaction3` and only used by XML. - The implementation of this reaction type will change after Cantera 2.6; + This class is superseded by `ElementaryReaction3` and only used by XML. + The implementation of this reaction type will change after Cantera 2.6; refer to `ElementaryReaction3` for new behavior. """ reaction_type = "elementary-old" @@ -1035,7 +1035,7 @@ cdef class ElementaryReaction3(Reaction): A reaction which follows mass-action kinetics with a modified Arrhenius reaction rate. - An example for the definition of an `ElementaryReaction3` object is given + An example for the definition of an `ElementaryReaction3` object is given as:: rxn = ElementaryReaction3( @@ -1048,21 +1048,26 @@ cdef class ElementaryReaction3(Reaction): """ reaction_type = "elementary" - def __init__(self, equation=None, rate=None, Kinetics kinetics=None, - init=True, **kwargs): + cdef CxxElementaryReaction3* er(self): + return self.reaction + + def __init__(self, equation=None, rate=None, + Kinetics kinetics=None, init=True, **kwargs): if init and equation and kinetics: + # todo: simplify after creation of AnyMap from dict is implemented 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) + rate_def = ', rate-constant: {}'.format(rate_def) + + yaml = '{{equation: {}, type: {}{}}}'.format( + equation, self.reaction_type, rate_def) self._reaction = CxxNewReaction(AnyMapFromYamlString(stringify(yaml)), deref(kinetics.kinetics)) self.reaction = self._reaction.get() @@ -1073,11 +1078,9 @@ cdef class ElementaryReaction3(Reaction): property rate: """ Get/Set the `Arrhenius` rate coefficient for this reaction. """ def __get__(self): - cdef CxxElementaryReaction3* r = self.reaction - return ArrheniusRate.wrap(r.rate()) + return ArrheniusRate.wrap(self.er().rate()) def __set__(self, ArrheniusRate rate): - cdef CxxElementaryReaction3* r = self.reaction - r.setRate(rate._base) + self.er().setRate(rate._base) property allow_negative_pre_exponential_factor: """ @@ -1085,11 +1088,93 @@ cdef class ElementaryReaction3(Reaction): pre-exponential factor. """ def __get__(self): - cdef CxxElementaryReaction3* r = self.reaction - return r.allow_negative_pre_exponential_factor + return self.er().allow_negative_pre_exponential_factor def __set__(self, allow): - cdef CxxElementaryReaction3* r = self.reaction - r.allow_negative_pre_exponential_factor = allow + self.er().allow_negative_pre_exponential_factor = allow + + +cdef class ThreeBodyReaction3(ElementaryReaction3): + """ + A reaction with a non-reacting third body "M" that acts to add or remove + energy from the reacting species. + + An example for the definition of an `ThreeBodyReaction3` object is given + as:: + + rxn = ThreeBodyReaction3( + equation: '2 O + M <=> O2 + M', + type: three-body, + rate: {A: 1.2e+17, b: -1.0, Ea: 0.0}, + efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83}, + kinetics=gas) + + This class is a replacement for `ThreeBodyReaction` and cannot be + instantiated from XML. It is the default for import from YAML. + """ + reaction_type = "three-body-new" + + cdef CxxThreeBodyReaction3* tbr(self): + return self.reaction + + def __init__(self, equation=None, rate=None, efficiencies=None, + Kinetics kinetics=None, init=True, **kwargs): + + if init and equation and kinetics: + + # todo: simplify after creation of AnyMap from dict is implemented + 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)) + rate_def = ', rate-constant: {}'.format(rate_def) + + # todo: simplify after creation of AnyMap from dict is implemented + if isinstance(efficiencies, dict): + effs = ['{}: {}'.format(k, v) for k, v in efficiencies.items()] + eff_def = '{{{}}}'.format(', '.join(effs)) + eff_def = ', efficiencies: {}'.format(eff_def) + elif efficiencies is None: + eff_def = '' + + yaml = '{{equation: {}, type: {}{}{}}}'.format( + equation, self.reaction_type, rate_def, eff_def) + self._reaction = CxxNewReaction(AnyMapFromYamlString(stringify(yaml)), + deref(kinetics.kinetics)) + self.reaction = self._reaction.get() + + if isinstance(rate, ArrheniusRate): + self.rate = rate + + property efficiencies: + """ + Get/Set a `dict` defining non-default third-body efficiencies for this + reaction, where the keys are the species names and the values are the + efficiencies. + """ + def __get__(self): + return comp_map_to_dict(self.tbr().third_body.efficiencies) + def __set__(self, eff): + self.tbr().third_body.efficiencies = comp_map(eff) + + property default_efficiency: + """ + Get/Set the default third-body efficiency for this reaction, used for + species used for species not in `efficiencies`. + """ + def __get__(self): + return self.tbr().third_body.default_efficiency + def __set__(self, default_eff): + self.tbr().third_body.default_efficiency = default_eff + + def efficiency(self, species): + """ + Get the efficiency of the third body named *species* considering both + the default efficiency and species-specific efficiencies. + """ + return self.tbr().third_body.efficiency(stringify(species)) cdef class CustomReaction(Reaction): From c302278e1265677d03b15c61a1835b7a6bb0cf62 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 25 Apr 2021 08:33:23 -0500 Subject: [PATCH 16/84] [Kinetics] Add getParameters to ThreeBodyReaction3 --- include/cantera/kinetics/Reaction.h | 4 +++ src/kinetics/Reaction.cpp | 48 +++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 6a85ca9cc46..f17c1641907 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -388,7 +388,9 @@ class ThreeBodyReaction3 : public ElementaryReaction3 return "three-body-new"; } + virtual void calculateRateCoeffUnits(const Kinetics& kin); virtual bool setParameters(const AnyMap& node, const Kinetics& kin); + virtual void getParameters(AnyMap& reactionNode) const; virtual std::string reactantString() const; virtual std::string productString() const; @@ -396,6 +398,8 @@ class ThreeBodyReaction3 : public ElementaryReaction3 //! Relative efficiencies of third-body species in enhancing the reaction //! rate. ThirdBody third_body; + + bool specified_collision_partner = false; //!< Input specifies collision partner }; diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 380b3d66883..5e7f2fd6a6f 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -759,6 +759,27 @@ ThreeBodyReaction3::ThreeBodyReaction3(const AnyMap& node, const Kinetics& kin) setParameters(node, kin); } +void ThreeBodyReaction3::calculateRateCoeffUnits(const Kinetics& kin) +{ + ElementaryReaction3::calculateRateCoeffUnits(kin); + bool specified_collision_partner_ = false; + for (const auto& reac : reactants) { + // While this reaction was already identified as a three-body reaction in a + // pre-processing step, this method is often called before a three-body + // reaction is fully instantiated. For the determination of the correct units, + // it is necessary to check whether the reaction uses a generic 'M' or an + // explicitly specified collision partner that may not have been deleted yet. + if (reac.first != "M" && products.count(reac.first)) { + // detected specified third-body collision partner + specified_collision_partner_ = true; + } + } + if (!specified_collision_partner_) { + const ThermoPhase& rxn_phase = kin.thermo(kin.reactionPhaseIndex()); + rate_units *= rxn_phase.standardConcentrationUnits().pow(-1); + } +} + bool ThreeBodyReaction3::setParameters(const AnyMap& node, const Kinetics& kin) { if (!ElementaryReaction3::setParameters(node, kin)) { @@ -767,12 +788,35 @@ bool ThreeBodyReaction3::setParameters(const AnyMap& node, const Kinetics& kin) return third_body.setEfficiencies(node); } +void ThreeBodyReaction3::getParameters(AnyMap& reactionNode) const +{ + ElementaryReaction3::getParameters(reactionNode); + if (!specified_collision_partner) { + reactionNode["type"] = "three-body"; + reactionNode["efficiencies"] = m_third_body->efficiencies; + reactionNode["efficiencies"].setFlowStyle(); + if (m_third_body->default_efficiency != 1.0) { + reactionNode["default-efficiency"] = m_third_body->default_efficiency; + } + } +} + std::string ThreeBodyReaction3::reactantString() const { - return ElementaryReaction3::reactantString() + " + M"; + if (specified_collision_partner) { + return ElementaryReaction3::reactantString() + " + " + + m_third_body->efficiencies.begin()->first; + } else { + return ElementaryReaction3::reactantString() + " + M"; + } } std::string ThreeBodyReaction3::productString() const { - return ElementaryReaction3::productString() + " + M"; + if (specified_collision_partner) { + return ElementaryReaction3::productString() + " + " + + m_third_body->efficiencies.begin()->first; + } else { + return ElementaryReaction3::productString() + " + M"; + } } CustomFunc1Reaction::CustomFunc1Reaction() From 5beeae10b206d0c08e83db5e87255d960bffd0af Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 21 Mar 2021 15:20:15 -0500 Subject: [PATCH 17/84] [Kinetics] Implement ThirdBody handling for ThreeBodyReaction3 --- include/cantera/kinetics/BulkKinetics.h | 7 ++++++ include/cantera/kinetics/GasKinetics.h | 1 - include/cantera/kinetics/Reaction.h | 10 +++++++++ src/kinetics/BulkKinetics.cpp | 29 ++++++++++++++++++++++--- src/kinetics/GasKinetics.cpp | 19 ++++++++++++++++ src/kinetics/Reaction.cpp | 7 ++++-- 6 files changed, 67 insertions(+), 6 deletions(-) diff --git a/include/cantera/kinetics/BulkKinetics.h b/include/cantera/kinetics/BulkKinetics.h index 471f3bb5109..13cf8cf8f2a 100644 --- a/include/cantera/kinetics/BulkKinetics.h +++ b/include/cantera/kinetics/BulkKinetics.h @@ -11,6 +11,7 @@ #include "Kinetics.h" #include "RateCoeffMgr.h" +#include "ThirdBodyCalc.h" #include "cantera/kinetics/MultiRate.h" namespace Cantera @@ -45,6 +46,12 @@ class BulkKinetics : public Kinetics virtual void setMultiplier(size_t i, double f); virtual void invalidateCache(); + void addThirdBody(shared_ptr r); + + ThirdBodyCalc m_multi_concm; //!< used with MultiRate evaluator + vector_fp concm_multi_values; + std::vector m_multi_indices; //!< reaction indices + protected: virtual void addElementaryReaction(ElementaryReaction& r); virtual void modifyElementaryReaction(size_t i, ElementaryReaction& rNew); diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index 7f351a161a4..ddf148ff099 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -10,7 +10,6 @@ #define CT_GASKINETICS_H #include "BulkKinetics.h" -#include "ThirdBodyCalc.h" #include "FalloffMgr.h" #include "Reaction.h" diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index f17c1641907..c3a62690cc3 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -19,6 +19,7 @@ namespace Cantera class Kinetics; class Falloff; class XML_Node; +class ThirdBody; //! Abstract base class which stores data about a reaction and its rate //! parameterization so that it can be added to a Kinetics object. @@ -173,9 +174,18 @@ class Reaction3 : public Reaction //! Set up reaction based on AnyMap node virtual bool setParameters(const AnyMap& node, const Kinetics& kin); + //! Get pointer to third-body + shared_ptr thirdBody() { + return m_third_body; + } + protected: //! Reaction rate used by generic reactions shared_ptr m_rate; + + //! Relative efficiencies of third-body species in enhancing the reaction + //! rate (if applicable) + shared_ptr m_third_body; }; diff --git a/src/kinetics/BulkKinetics.cpp b/src/kinetics/BulkKinetics.cpp index 4fab774b22f..05e4a74fb22 100644 --- a/src/kinetics/BulkKinetics.cpp +++ b/src/kinetics/BulkKinetics.cpp @@ -1,4 +1,3 @@ - // 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. @@ -124,8 +123,8 @@ bool BulkKinetics::addReaction(shared_ptr r) } if (std::dynamic_pointer_cast(r) != nullptr) { - shared_ptr rate; - rate = std::dynamic_pointer_cast(r)->rate(); + shared_ptr r3 = std::dynamic_pointer_cast(r); + shared_ptr rate = r3->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(); @@ -142,11 +141,35 @@ 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); + + // Add reaction to third-body evaluator + if (r3->thirdBody() != nullptr) { + addThirdBody(r3); + } } return true; } +void BulkKinetics::addThirdBody(shared_ptr r) +{ + std::map efficiencies; + for (const auto& eff : r->thirdBody()->efficiencies) { + size_t k = kineticsSpeciesIndex(eff.first); + if (k != npos) { + efficiencies[k] = eff.second; + } else if (!m_skipUndeclaredThirdBodies) { + throw CanteraError("BulkKinetics::addThirdBody", "Found " + "third-body efficiency for undefined species '" + eff.first + + "' while adding reaction '" + r->equation() + "'"); + } + } + m_multi_concm.install(nReactions() - 1, efficiencies, + r->thirdBody()->default_efficiency); + concm_multi_values.resize(m_multi_concm.workSize()); + m_multi_indices.push_back(nReactions() - 1); +} + void BulkKinetics::addElementaryReaction(ElementaryReaction& r) { m_rates.install(nReactions()-1, r.rate); diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 17d20d328b7..3162dfefd9a 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -87,6 +87,11 @@ void GasKinetics::update_rates_C() m_falloff_concm.update(m_phys_conc, ctot, concm_falloff_values.data()); } + // Third-body objects interacting with MultiRate evaluator + if (!concm_multi_values.empty()) { + m_multi_concm.update(m_phys_conc, ctot, concm_multi_values.data()); + } + // P-log reactions if (m_plog_rates.nReactions()) { double logP = log(thermo().pressure()); @@ -184,6 +189,13 @@ void GasKinetics::updateROP() processFalloffReactions(); } + if (!concm_multi_values.empty()) { + // multiply 3rd body concentrations + for (size_t i = 0; i < m_multi_indices.size(); i++) { + m_ropf[m_multi_indices[i]] *= concm_multi_values[i]; + } + } + for (size_t i = 0; i < nReactions(); i++) { // Scale the forward rate coefficient by the perturbation factor m_ropf[i] *= m_perturb[i]; @@ -230,6 +242,13 @@ void GasKinetics::getFwdRateConstants(doublereal* kfwd) processFalloffReactions(); } + if (!concm_multi_values.empty()) { + // multiply 3rd body concentrations + for (size_t i = 0; i < m_multi_indices.size(); i++) { + m_ropf[m_multi_indices[i]] *= concm_multi_values[i]; + } + } + for (size_t i = 0; i < nReactions(); i++) { // multiply by perturbation factor kfwd[i] = m_ropf[i] * m_perturb[i]; diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 5e7f2fd6a6f..bdc9d1f3ab3 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -742,6 +742,7 @@ void ElementaryReaction3::validate() ThreeBodyReaction3::ThreeBodyReaction3() : ElementaryReaction3() { + m_third_body = std::shared_ptr(new ThirdBody); } ThreeBodyReaction3::ThreeBodyReaction3(const Composition& reactants, @@ -749,8 +750,8 @@ ThreeBodyReaction3::ThreeBodyReaction3(const Composition& reactants, const ArrheniusRate& rate, const ThirdBody& tbody) : ElementaryReaction3(reactants, products, rate) - , third_body(tbody) { + m_third_body = std::make_shared(tbody); } ThreeBodyReaction3::ThreeBodyReaction3(const AnyMap& node, const Kinetics& kin) @@ -785,7 +786,9 @@ bool ThreeBodyReaction3::setParameters(const AnyMap& node, const Kinetics& kin) if (!ElementaryReaction3::setParameters(node, kin)) { return false; } - return third_body.setEfficiencies(node); + reactants.erase("M"); + products.erase("M"); + return m_third_body->setEfficiencies(node); } void ThreeBodyReaction3::getParameters(AnyMap& reactionNode) const From 9190de79d2d136adbb2425f0cc52bdfc05c7df31 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 21 Mar 2021 15:21:16 -0500 Subject: [PATCH 18/84] [Kinetics] Update cython interface --- interfaces/cython/cantera/_cantera.pxd | 2 +- interfaces/cython/cantera/reaction.pyx | 58 ++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index a743a0f7b0e..14c34848f09 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -465,7 +465,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxThreeBodyReaction3 "Cantera::ThreeBodyReaction3" (CxxElementaryReaction3): CxxThreeBodyReaction3() - CxxThirdBody third_body + shared_ptr[CxxThirdBody] thirdBody() cdef cppclass CxxCustomFunc1Reaction "Cantera::CustomFunc1Reaction" (CxxReaction3): CxxCustomFunc1Reaction() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 9843432c027..5b040b2fe35 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -600,6 +600,38 @@ cdef class ThreeBodyReaction(ElementaryReaction): """ reaction_type = "three-body" + def __init__(self, equation=None, rate=None, efficiencies=None, + Kinetics kinetics=None, init=True, **kwargs): + + if init and equation and kinetics: + + # todo: simplify after creation of AnyMap from dict is implemented + 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 TypeError("Invalid rate definition") + rate_def = '{{{}}}'.format(', '.join(coeffs)) + rate_def = ', rate-constant: {}'.format(rate_def) + + # todo: simplify after creation of AnyMap from dict is implemented + if isinstance(efficiencies, dict): + effs = ['{}: {}'.format(k, v) for k, v in efficiencies.items()] + eff_def = '{{{}}}'.format(', '.join(effs)) + eff_def = ', efficiencies: {}'.format(eff_def) + elif efficiencies is None: + eff_def = '' + + yaml = '{{equation: {}, type: {}{}{}}}'.format( + equation, self.reaction_type, rate_def, eff_def) + self._reaction = CxxNewReaction(AnyMapFromYamlString(stringify(yaml)), + deref(kinetics.kinetics)) + self.reaction = self._reaction.get() + + if isinstance(rate, Arrhenius): + self.rate = rate + cdef CxxThreeBodyReaction* tbr(self): return self.reaction @@ -1102,10 +1134,10 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): as:: rxn = ThreeBodyReaction3( - equation: '2 O + M <=> O2 + M', - type: three-body, - rate: {A: 1.2e+17, b: -1.0, Ea: 0.0}, - efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83}, + equation='2 O + M <=> O2 + M', + type='three-body', + rate={'A': 1.2e+17, 'b': -1.0, 'Ea': 0.0}, + efficiencies={'H2': 2.4, 'H2O': 15.4, 'AR': 0.83}, kinetics=gas) This class is a replacement for `ThreeBodyReaction` and cannot be @@ -1116,6 +1148,9 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): cdef CxxThreeBodyReaction3* tbr(self): return self.reaction + cdef CxxThirdBody* thirdbody(self): + return (self.tbr().thirdBody().get()) + def __init__(self, equation=None, rate=None, efficiencies=None, Kinetics kinetics=None, init=True, **kwargs): @@ -1155,9 +1190,11 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): efficiencies. """ def __get__(self): - return comp_map_to_dict(self.tbr().third_body.efficiencies) + #CxxThirdBody* thirdbody = self.tbr().third_body().get() + return comp_map_to_dict(self.thirdbody().efficiencies) def __set__(self, eff): - self.tbr().third_body.efficiencies = comp_map(eff) + #CxxThirdBody* thirdbody = self.tbr().third_body().get() + self.thirdbody().efficiencies = comp_map(eff) property default_efficiency: """ @@ -1165,16 +1202,19 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): species used for species not in `efficiencies`. """ def __get__(self): - return self.tbr().third_body.default_efficiency + #CxxThirdBody* thirdbody = self.tbr().third_body().get() + return self.thirdbody().default_efficiency def __set__(self, default_eff): - self.tbr().third_body.default_efficiency = default_eff + #CxxThirdBody* thirdbody = self.tbr().third_body().get() + self.thirdbody().default_efficiency = default_eff def efficiency(self, species): """ Get the efficiency of the third body named *species* considering both the default efficiency and species-specific efficiencies. """ - return self.tbr().third_body.efficiency(stringify(species)) + #CxxThirdBody* thirdbody = self.tbr().third_body().get() + return self.thirdbody().efficiency(stringify(species)) cdef class CustomReaction(Reaction): From f9ce6f041f756c8d22704396c7fcb1d8a5534c0b Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 21 Mar 2021 15:22:12 -0500 Subject: [PATCH 19/84] [CI] Add unit tests for ThreeBodyReaction3 --- .../cython/cantera/test/test_reaction.py | 52 +++++++++++++++---- test/kinetics/kineticsFromScratch3.cpp | 14 ++--- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index c21a3962670..5df5480764c 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -115,6 +115,7 @@ class TestElementary(utilities.CanteraTest): _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) + _kwargs = {} _index = 2 _type = "elementary-old" @@ -129,7 +130,6 @@ def setUpClass(cls): 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) @@ -156,11 +156,11 @@ def test_from_parts(self): self.check_rxn(rxn) def test_from_dict(self): - rxn = self._cls(equation=self._equation, rate=self._rate, kinetics=self.gas) + rxn = self._cls(equation=self._equation, rate=self._rate, kinetics=self.gas, **self._kwargs) self.check_rxn(rxn) def test_from_rate(self): - rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas) + rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) self.check_rxn(rxn) def test_add_rxn(self): @@ -168,16 +168,16 @@ def test_add_rxn(self): species=self.species, reactions=[]) gas2.TPX = self.gas.TPX - rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas) + rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) 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) + rxn = self._cls(equation=self._equation, rate=[], kinetics=self.gas, **self._kwargs) def test_no_rate(self): - rxn = self._cls(equation=self._equation, kinetics=self.gas) + rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) self.assertNear(rxn.rate(self.gas.T), 0.) gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', @@ -188,7 +188,7 @@ def test_no_rate(self): self.assertNear(gas2.net_rates_of_progress[0], 0.) def test_replace_rate(self): - rxn = self._cls(equation=self._equation, kinetics=self.gas) + rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) rxn.rate = self._rate_obj self.check_rxn(rxn) @@ -196,10 +196,7 @@ def test_replace_rate(self): class TestElementary3(TestElementary): _cls = ct.ElementaryReaction3 - _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" @@ -253,3 +250,38 @@ class TestElementaryNew(TestElementary): _rate_obj = ct.ArrheniusRate(38.7, 2.7, 2.619184e+07) _index = 2 _type = "elementary-new" + + +class TestThreeBody(TestElementary): + + _cls = ct.ThreeBodyReaction + _equation = '2 O + M <=> O2 + M' + _rate = {'A': 1.2e11, 'b': -1.0, 'Ea': 0.0} + _rate_obj = ct.Arrhenius(1.2e11, -1., 0.) + _kwargs = {'efficiencies': {'H2': 2.4, 'H2O': 15.4, 'AR': 0.83}} + _index = 0 + _type = "three-body" + + def test_from_parts(self): + orig = self.gas.reaction(self._index) + rxn = self._cls(orig.reactants, orig.products) + rxn.rate = self._rate_obj + rxn.efficiencies = self._kwargs['efficiencies'] + self.check_rxn(rxn) + + def test_rate(self): + # rate constant contains third-body concentration + pass + + def test_efficiencies(self): + rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) + + self.assertEqual(rxn.efficiencies, self._kwargs['efficiencies']) + + +class TestThreeBody3(TestThreeBody): + + _cls = ct.ThreeBodyReaction3 + _rate_obj = ct.ArrheniusRate(1.2e11, -1., 0.) + _type = "three-body-new" + diff --git a/test/kinetics/kineticsFromScratch3.cpp b/test/kinetics/kineticsFromScratch3.cpp index c79f567b344..85e3932db05 100644 --- a/test/kinetics/kineticsFromScratch3.cpp +++ b/test/kinetics/kineticsFromScratch3.cpp @@ -66,7 +66,6 @@ TEST_F(KineticsFromScratch3, add_elementary_reaction) check_rates(0); } -/* TEST_F(KineticsFromScratch3, add_three_body_reaction) { // reaction 1: @@ -74,10 +73,10 @@ TEST_F(KineticsFromScratch3, add_three_body_reaction) // efficiencies='AR:0.83 H2:2.4 H2O:15.4') Composition reac = parseCompString("O:2"); Composition prod = parseCompString("O2:1"); - Arrhenius rate(1.2e11, -1.0, 0.0); + ArrheniusRate rate(1.2e11, -1.0, 0.0); ThirdBody tbody; tbody.efficiencies = parseCompString("AR:0.83 H2:2.4 H2O:15.4"); - auto R = make_shared(reac, prod, rate, tbody); + auto R = make_shared(reac, prod, rate, tbody); kin->addReaction(R); check_rates(1); @@ -87,10 +86,10 @@ TEST_F(KineticsFromScratch3, undefined_third_body) { Composition reac = parseCompString("O:2"); Composition prod = parseCompString("O2:1"); - Arrhenius rate(1.2e11, -1.0, 0.0); + ArrheniusRate rate(1.2e11, -1.0, 0.0); ThirdBody tbody; tbody.efficiencies = parseCompString("H2:0.1 CO2:0.83"); - auto R = make_shared(reac, prod, rate, tbody); + auto R = make_shared(reac, prod, rate, tbody); ASSERT_THROW(kin->addReaction(R), CanteraError); } @@ -99,10 +98,10 @@ TEST_F(KineticsFromScratch3, skip_undefined_third_body) { Composition reac = parseCompString("O:2"); Composition prod = parseCompString("O2:1"); - Arrhenius rate(1.2e11, -1.0, 0.0); + ArrheniusRate rate(1.2e11, -1.0, 0.0); ThirdBody tbody; tbody.efficiencies = parseCompString("H2:0.1 CO2:0.83"); - auto R = make_shared(reac, prod, rate, tbody); + auto R = make_shared(reac, prod, rate, tbody); kin->skipUndeclaredThirdBodies(true); kin->addReaction(R); @@ -110,6 +109,7 @@ TEST_F(KineticsFromScratch3, skip_undefined_third_body) } +/* TEST_F(KineticsFromScratch3, add_falloff_reaction) { // reaction 2: From dbaae563f8842722a43424dcb4b059d6e5c8b96e Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 21 Mar 2021 16:28:11 -0500 Subject: [PATCH 20/84] [Kinetics] Switch ThreeBodyReaction to ThreeBodyReaction3 --- include/cantera/kinetics/Reaction.h | 9 ++-- interfaces/cython/cantera/reaction.pyx | 9 +--- .../cython/cantera/test/test_kinetics.py | 4 +- .../cython/cantera/test/test_reaction.py | 5 +- src/kinetics/GasKinetics.cpp | 4 +- src/kinetics/Kinetics.cpp | 16 ++++++ src/kinetics/Reaction.cpp | 50 +++++++++++++++++++ src/kinetics/ReactionFactory.cpp | 15 +++--- test/kinetics/kineticsFromYaml.cpp | 32 +++++++++--- 9 files changed, 111 insertions(+), 33 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index c3a62690cc3..7412163783d 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -240,7 +240,7 @@ class ThreeBodyReaction : public ElementaryReaction const Arrhenius& rate, const ThirdBody& tbody); virtual std::string type() const { - return "three-body"; + return "three-body-old"; } virtual std::string reactantString() const; @@ -395,9 +395,10 @@ class ThreeBodyReaction3 : public ElementaryReaction3 ThreeBodyReaction3(const AnyMap& node, const Kinetics& kin); virtual std::string type() const { - return "three-body-new"; + return "three-body"; } + bool detectEfficiencies(); virtual void calculateRateCoeffUnits(const Kinetics& kin); virtual bool setParameters(const AnyMap& node, const Kinetics& kin); virtual void getParameters(AnyMap& reactionNode) const; @@ -405,10 +406,6 @@ class ThreeBodyReaction3 : public ElementaryReaction3 virtual std::string reactantString() const; virtual std::string productString() const; - //! Relative efficiencies of third-body species in enhancing the reaction - //! rate. - ThirdBody third_body; - bool specified_collision_partner = false; //!< Input specifies collision partner }; diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 5b040b2fe35..8f93cd7e801 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -598,7 +598,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" + reaction_type = "three-body-old" def __init__(self, equation=None, rate=None, efficiencies=None, Kinetics kinetics=None, init=True, **kwargs): @@ -1143,7 +1143,7 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): This class is a replacement for `ThreeBodyReaction` and cannot be instantiated from XML. It is the default for import from YAML. """ - reaction_type = "three-body-new" + reaction_type = "three-body" cdef CxxThreeBodyReaction3* tbr(self): return self.reaction @@ -1190,10 +1190,8 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): efficiencies. """ def __get__(self): - #CxxThirdBody* thirdbody = self.tbr().third_body().get() return comp_map_to_dict(self.thirdbody().efficiencies) def __set__(self, eff): - #CxxThirdBody* thirdbody = self.tbr().third_body().get() self.thirdbody().efficiencies = comp_map(eff) property default_efficiency: @@ -1202,10 +1200,8 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): species used for species not in `efficiencies`. """ def __get__(self): - #CxxThirdBody* thirdbody = self.tbr().third_body().get() return self.thirdbody().default_efficiency def __set__(self, default_eff): - #CxxThirdBody* thirdbody = self.tbr().third_body().get() self.thirdbody().default_efficiency = default_eff def efficiency(self, species): @@ -1213,7 +1209,6 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): Get the efficiency of the third body named *species* considering both the default efficiency and species-specific efficiencies. """ - #CxxThirdBody* thirdbody = self.tbr().third_body().get() return self.thirdbody().efficiency(stringify(species)) diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 1be097dc8b7..28af85b719f 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -48,7 +48,7 @@ def test_multiplier(self): self.assertArrayNear(0.5 * rev_rates0, rev_rates2) def test_reaction_type(self): - self.assertEqual(self.phase.reaction_type_str(0), "three-body") + self.assertIn(self.phase.reaction_type_str(0), ["three-body", "three-body-old"]) self.assertIn(self.phase.reaction_type_str(2), ["elementary", "elementary-old"]) self.assertEqual(self.phase.reaction_type_str(21), "falloff") @@ -905,7 +905,7 @@ def test_fromYaml(self): " efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83}}", self.gas) - self.assertTrue(isinstance(r, ct.ThreeBodyReaction)) + self.assertTrue(isinstance(r, ct.ThreeBodyReaction3)) self.assertEqual(r.reactants['O'], 2) self.assertEqual(r.products['O2'], 1) self.assertEqual(r.efficiencies['H2O'], 15.4) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 5df5480764c..422f205677d 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -260,7 +260,7 @@ class TestThreeBody(TestElementary): _rate_obj = ct.Arrhenius(1.2e11, -1., 0.) _kwargs = {'efficiencies': {'H2': 2.4, 'H2O': 15.4, 'AR': 0.83}} _index = 0 - _type = "three-body" + _type = "three-body-old" def test_from_parts(self): orig = self.gas.reaction(self._index) @@ -283,5 +283,4 @@ class TestThreeBody3(TestThreeBody): _cls = ct.ThreeBodyReaction3 _rate_obj = ct.ArrheniusRate(1.2e11, -1., 0.) - _type = "three-body-new" - + _type = "three-body" diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 3162dfefd9a..fd90414700c 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -268,7 +268,7 @@ bool GasKinetics::addReaction(shared_ptr r) if (r->type() == "elementary-old") { addElementaryReaction(dynamic_cast(*r)); - } else if (r->type() == "three-body") { + } else if (r->type() == "three-body-old") { addThreeBodyReaction(dynamic_cast(*r)); } else if (r->type() == "falloff") { addFalloffReaction(dynamic_cast(*r)); @@ -360,7 +360,7 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) if (rNew->type() == "elementary-old") { modifyElementaryReaction(i, dynamic_cast(*rNew)); - } else if (rNew->type() == "three-body") { + } else if (rNew->type() == "three-body-old") { modifyThreeBodyReaction(i, dynamic_cast(*rNew)); } else if (rNew->type() == "falloff") { modifyFalloffReaction(i, dynamic_cast(*rNew)); diff --git a/src/kinetics/Kinetics.cpp b/src/kinetics/Kinetics.cpp index 4874d7d70ec..03447b252a9 100644 --- a/src/kinetics/Kinetics.cpp +++ b/src/kinetics/Kinetics.cpp @@ -144,6 +144,22 @@ std::pair Kinetics::checkDuplicates(bool throw_err) const continue; // No overlap in third body efficiencies } } else if (R.type() == "three-body") { + ThirdBody& tb1 = *(dynamic_cast(R).thirdBody()); + ThirdBody& tb2 = *(dynamic_cast(other).thirdBody()); + bool thirdBodyOk = true; + for (size_t k = 0; k < nTotalSpecies(); k++) { + string s = kineticsSpeciesName(k); + if (tb1.efficiency(s) * tb2.efficiency(s) != 0.0) { + // non-zero third body efficiencies for species `s` in + // both reactions + thirdBodyOk = false; + break; + } + } + if (thirdBodyOk) { + continue; // No overlap in third body efficiencies + } + } else if (R.type() == "three-body-old") { 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 bdc9d1f3ab3..f720949045e 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -760,6 +760,46 @@ ThreeBodyReaction3::ThreeBodyReaction3(const AnyMap& node, const Kinetics& kin) setParameters(node, kin); } +bool ThreeBodyReaction3::detectEfficiencies() +{ + for (const auto& reac : reactants) { + // detect explicitly specified collision partner + if (products.count(reac.first)) { + m_third_body->efficiencies[reac.first] = 1.; + } + } + + if (m_third_body->efficiencies.size() == 0) { + return false; + } else if (m_third_body->efficiencies.size() > 1) { + throw CanteraError("ThreeBodyReaction3::detectEfficiencies", + "Found more than one explicitly specified collision partner\n" + "in reaction '{}'.", equation()); + } + + m_third_body->default_efficiency = 0.; + specified_collision_partner = true; + auto sp = m_third_body->efficiencies.begin(); + + // adjust reactant coefficients + auto reac = reactants.find(sp->first); + if (trunc(reac->second) != 1) { + reac->second -= 1.; + } else { + reactants.erase(reac); + } + + // adjust product coefficients + auto prod = products.find(sp->first); + if (trunc(prod->second) != 1) { + prod->second -= 1.; + } else { + products.erase(prod); + } + + return true; +} + void ThreeBodyReaction3::calculateRateCoeffUnits(const Kinetics& kin) { ElementaryReaction3::calculateRateCoeffUnits(kin); @@ -786,6 +826,16 @@ bool ThreeBodyReaction3::setParameters(const AnyMap& node, const Kinetics& kin) if (!ElementaryReaction3::setParameters(node, kin)) { return false; } + + if (reactants.count("M") != 1 || products.count("M") != 1) { + if (!detectEfficiencies()) { + throw InputFileError("ThreeBodyReaction3::setParameters", node["equation"], + "Reaction equation '{}' does not contain third body 'M'", + node["equation"].asString()); + } + return true; + } + reactants.erase("M"); products.erase("M"); return m_third_body->setEfficiencies(node); diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index e1e801873a4..dd629d01afd 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -37,20 +37,20 @@ ReactionFactory::ReactionFactory() }); // register three-body reactions - reg("three-body-new", [](const AnyMap& node, const Kinetics& kin) { + reg("three-body", [](const AnyMap& node, const Kinetics& kin) { return new ThreeBodyReaction3(node, kin); }); + addAlias("three-body", "threebody"); + addAlias("three-body", "three_body"); // register three-body reactions - reg("three-body", [](const AnyMap& node, const Kinetics& kin) { + reg("three-body-old", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ThreeBodyReaction(); if (node.hasKey("equation")) { setupThreeBodyReaction(*(ThreeBodyReaction*)R, node, kin); } return R; }); - addAlias("three-body", "threebody"); - addAlias("three-body", "three_body"); // register falloff reactions reg("falloff", [](const AnyMap& node, const Kinetics& kin) { @@ -158,13 +158,14 @@ ReactionFactoryXML::ReactionFactoryXML() addAlias("elementary-old", ""); // register three-body reactions - reg("three-body", [](const XML_Node& node) { + reg("three-body-old", [](const XML_Node& node) { Reaction* R = new ThreeBodyReaction(); setupThreeBodyReaction(*(ThreeBodyReaction*)R, node); return R; }); - addAlias("three-body", "threebody"); - addAlias("three-body", "three_body"); + addAlias("three-body-old", "three-body"); + addAlias("three-body-old", "threebody"); + addAlias("three-body-old", "three_body"); // register falloff reactions reg("falloff", [](const XML_Node& node) { diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index 14d405874fc..2eb0890c28b 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -66,10 +66,11 @@ TEST(Reaction, ThreeBodyFromYaml1) 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); - EXPECT_DOUBLE_EQ(TBR.third_body.efficiencies["H2O"], 5.0); - EXPECT_DOUBLE_EQ(TBR.third_body.default_efficiency, 1.0); + auto TBR = dynamic_cast(*R); + const auto& rate = std::dynamic_pointer_cast(TBR.rate()); + EXPECT_DOUBLE_EQ(rate->preExponentialFactor(), 1.2e11); + EXPECT_DOUBLE_EQ(TBR.thirdBody()->efficiencies["H2O"], 5.0); + EXPECT_DOUBLE_EQ(TBR.thirdBody()->default_efficiency, 1.0); } TEST(Reaction, ThreeBodyFromYaml2) @@ -83,6 +84,25 @@ TEST(Reaction, ThreeBodyFromYaml2) EXPECT_THROW(newReaction(rxn, *(sol->kinetics())), CanteraError); } +TEST(Reaction, ThreeBodyFromYaml3) +{ + auto sol = newSolution("gri30.yaml"); + AnyMap rxn = AnyMap::fromYamlString( + "{equation: 2 O + M = O2 + M," + " type: three-body-old," + " rate-constant: [1.20000E+17 cm^6/mol^2/s, -1, 0]," + " efficiencies: {AR: 0.83, H2O: 5}}"); + + auto R = newReaction(rxn, *(sol->kinetics())); + EXPECT_EQ(R->reactants.count("M"), (size_t) 0); + EXPECT_EQ(R->type(), "three-body-old"); + + auto TBR = dynamic_cast(*R); + EXPECT_DOUBLE_EQ(TBR.rate.preExponentialFactor(), 1.2e11); + EXPECT_DOUBLE_EQ(TBR.third_body.efficiencies["H2O"], 5.0); + EXPECT_DOUBLE_EQ(TBR.third_body.default_efficiency, 1.0); +} + TEST(Reaction, FalloffFromYaml1) { auto sol = newSolution("gri30.yaml", "", "None"); @@ -463,8 +483,8 @@ TEST_F(ReactionToYaml, threeBody) soln = newSolution("h2o2.yaml", "", "None"); soln->thermo()->setState_TPY(1000, 2e5, "H2:1.0, O2:0.5, O:1e-8, OH:3e-8, H:2e-7"); duplicateReaction(1); - EXPECT_TRUE(std::dynamic_pointer_cast(duplicate)); - compareReactions(); + EXPECT_TRUE(std::dynamic_pointer_cast(duplicate)); + compareReactions(true); } TEST_F(ReactionToYaml, TroeFalloff) From d750f95d3809f53f58eccf9c67fb5296852d12a8 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 21 Mar 2021 20:36:50 -0500 Subject: [PATCH 21/84] [Kinetics] Simplify Python constructors --- interfaces/cython/cantera/reaction.pyx | 112 ++++++++++++------------- 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 8f93cd7e801..73502ddb336 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -119,8 +119,8 @@ cdef class Reaction: cxx_reaction = CxxNewReaction(deref(CxxGetXmlFromString(stringify(text)))) return Reaction.wrap(cxx_reaction) - @staticmethod - def fromYaml(text, Kinetics kinetics): + @classmethod + def fromYaml(cls, text, Kinetics kinetics): """ Create a `Reaction` object from its YAML string representation. @@ -130,6 +130,11 @@ cdef class Reaction: A `Kinetics` object whose associated phase(s) contain the species involved in the reaction. """ + if cls.reaction_type != "": + raise TypeError( + "Class method 'fromYaml' was invoked from '{}' but should " + "be called from base class 'Reaction'".format(cls.__name__)) + cxx_reaction = CxxNewReaction(AnyMapFromYamlString(stringify(text)), deref(kinetics.kinetics)) return Reaction.wrap(cxx_reaction) @@ -554,17 +559,15 @@ cdef class ElementaryReaction(Reaction): if init and equation and kinetics: + spec = {'equation': equation, 'type': self.reaction_type} if isinstance(rate, dict): - coeffs = ['{}: {}'.format(k, v) for k, v in rate.items()] + spec['rate-constant'] = rate elif isinstance(rate, Arrhenius) or rate is None: - coeffs = ['{}: 0.'.format(k) for k in ['A', 'b', 'Ea']] + spec['rate-constant'] = dict.fromkeys(['A', 'b', 'Ea'], 0.) 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)), + self._reaction = CxxNewReaction(dict_to_anymap(spec), deref(kinetics.kinetics)) self.reaction = self._reaction.get() @@ -597,6 +600,12 @@ cdef class ThreeBodyReaction(ElementaryReaction): """ A reaction with a non-reacting third body "M" that acts to add or remove energy from the reacting species. + + .. deprecated:: 2.6 + + This class is superseded by `ThreeBodyReaction3` and only used by XML. + The implementation of this reaction type will change after Cantera 2.6; + refer to `ThreeBodyReaction3` for new behavior. """ reaction_type = "three-body-old" @@ -605,33 +614,24 @@ cdef class ThreeBodyReaction(ElementaryReaction): if init and equation and kinetics: - # todo: simplify after creation of AnyMap from dict is implemented + spec = {'equation': equation, 'type': self.reaction_type} if isinstance(rate, dict): - coeffs = ['{}: {}'.format(k, v) for k, v in rate.items()] + spec['rate-constant'] = rate elif isinstance(rate, Arrhenius) or rate is None: - coeffs = ['{}: 0.'.format(k) for k in ['A', 'b', 'Ea']] + spec['rate-constant'] = dict.fromkeys(['A', 'b', 'Ea'], 0.) else: raise TypeError("Invalid rate definition") - rate_def = '{{{}}}'.format(', '.join(coeffs)) - rate_def = ', rate-constant: {}'.format(rate_def) - # todo: simplify after creation of AnyMap from dict is implemented if isinstance(efficiencies, dict): - effs = ['{}: {}'.format(k, v) for k, v in efficiencies.items()] - eff_def = '{{{}}}'.format(', '.join(effs)) - eff_def = ', efficiencies: {}'.format(eff_def) - elif efficiencies is None: - eff_def = '' - - yaml = '{{equation: {}, type: {}{}{}}}'.format( - equation, self.reaction_type, rate_def, eff_def) - self._reaction = CxxNewReaction(AnyMapFromYamlString(stringify(yaml)), + spec['efficiencies'] = efficiencies + + self._reaction = CxxNewReaction(dict_to_anymap(spec), deref(kinetics.kinetics)) self.reaction = self._reaction.get() if isinstance(rate, Arrhenius): self.rate = rate - + cdef CxxThreeBodyReaction* tbr(self): return self.reaction @@ -1071,10 +1071,15 @@ cdef class ElementaryReaction3(Reaction): as:: rxn = ElementaryReaction3( - equation='H2 + O <=> H + OH', + equation='O + H2 <=> H + OH', rate={'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07}, kinetics=gas) + The YAML description corresponding to this reaction is:: + + equation: O + H2 <=> H + OH + rate-constant: {A: 3.87e+04 cm^3/mol/s, b: 2.7, Ea: 6260.0 cal/mol} + This class is a replacement for `ElementaryReaction` and cannot be instantiated from XML. It is the default for import from YAML. """ @@ -1088,19 +1093,15 @@ cdef class ElementaryReaction3(Reaction): if init and equation and kinetics: - # todo: simplify after creation of AnyMap from dict is implemented + spec = {'equation': equation, 'type': self.reaction_type} if isinstance(rate, dict): - coeffs = ['{}: {}'.format(k, v) for k, v in rate.items()] + spec['rate-constant'] = rate elif isinstance(rate, ArrheniusRate) or rate is None: - coeffs = ['{}: 0.'.format(k) for k in ['A', 'b', 'Ea']] + spec['rate-constant'] = dict.fromkeys(['A', 'b', 'Ea'], 0.) else: raise TypeError("Invalid rate definition") - rate_def = '{{{}}}'.format(', '.join(coeffs)) - rate_def = ', rate-constant: {}'.format(rate_def) - yaml = '{{equation: {}, type: {}{}}}'.format( - equation, self.reaction_type, rate_def) - self._reaction = CxxNewReaction(AnyMapFromYamlString(stringify(yaml)), + self._reaction = CxxNewReaction(dict_to_anymap(spec), deref(kinetics.kinetics)) self.reaction = self._reaction.get() @@ -1140,6 +1141,13 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): efficiencies={'H2': 2.4, 'H2O': 15.4, 'AR': 0.83}, kinetics=gas) + The YAML description corresponding to this reaction is:: + + equation: 2 O + M <=> O2 + M # Reaction 1 + type: three-body + rate-constant: {A: 1.2e+17 cm^6/mol^2/s, b: -1.0, Ea: 0.0 cal/mol} + efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} + This class is a replacement for `ThreeBodyReaction` and cannot be instantiated from XML. It is the default for import from YAML. """ @@ -1150,33 +1158,24 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): cdef CxxThirdBody* thirdbody(self): return (self.tbr().thirdBody().get()) - + def __init__(self, equation=None, rate=None, efficiencies=None, Kinetics kinetics=None, init=True, **kwargs): if init and equation and kinetics: - # todo: simplify after creation of AnyMap from dict is implemented + spec = {'equation': equation, 'type': self.reaction_type} if isinstance(rate, dict): - coeffs = ['{}: {}'.format(k, v) for k, v in rate.items()] + spec['rate-constant'] = rate elif isinstance(rate, ArrheniusRate) or rate is None: - coeffs = ['{}: 0.'.format(k) for k in ['A', 'b', 'Ea']] + spec['rate-constant'] = dict.fromkeys(['A', 'b', 'Ea'], 0.) else: raise TypeError("Invalid rate definition") - rate_def = '{{{}}}'.format(', '.join(coeffs)) - rate_def = ', rate-constant: {}'.format(rate_def) - # todo: simplify after creation of AnyMap from dict is implemented if isinstance(efficiencies, dict): - effs = ['{}: {}'.format(k, v) for k, v in efficiencies.items()] - eff_def = '{{{}}}'.format(', '.join(effs)) - eff_def = ', efficiencies: {}'.format(eff_def) - elif efficiencies is None: - eff_def = '' - - yaml = '{{equation: {}, type: {}{}{}}}'.format( - equation, self.reaction_type, rate_def, eff_def) - self._reaction = CxxNewReaction(AnyMapFromYamlString(stringify(yaml)), + spec['efficiencies'] = efficiencies + + self._reaction = CxxNewReaction(dict_to_anymap(spec), deref(kinetics.kinetics)) self.reaction = self._reaction.get() @@ -1230,8 +1229,9 @@ cdef class CustomReaction(Reaction): if init and equation and kinetics: - yaml = '{{equation: {}, type: {}}}'.format(equation, self.reaction_type) - self._reaction = CxxNewReaction(AnyMapFromYamlString(stringify(yaml)), + spec = {'equation': equation, 'type': self.reaction_type} + + self._reaction = CxxNewReaction(dict_to_anymap(spec), deref(kinetics.kinetics)) self.reaction = self._reaction.get() self.rate = CustomRate(rate) @@ -1268,17 +1268,15 @@ cdef class TestReaction(Reaction): if init and equation and kinetics: + spec = {'equation': equation, 'type': self.reaction_type} if isinstance(rate, dict): - coeffs = ['{}: {}'.format(k, v) for k, v in rate.items()] + spec['rate-constant'] = rate elif isinstance(rate, ArrheniusRate) or rate is None: - coeffs = ['{}: 0.'.format(k) for k in ['A', 'b', 'Ea']] + spec['rate-constant'] = dict.fromkeys(['A', 'b', 'Ea'], 0.) 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)), + self._reaction = CxxNewReaction(dict_to_anymap(spec), deref(kinetics.kinetics)) self.reaction = self._reaction.get() From 62e55b984995f7d2fce525d1be92d4f96507a779 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 21 Mar 2021 21:39:39 -0500 Subject: [PATCH 22/84] [CI] Add unit tests for conversion from YAML --- .../cython/cantera/test/test_reaction.py | 54 +++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 422f205677d..0775bc88b23 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -118,6 +118,11 @@ class TestElementary(utilities.CanteraTest): _kwargs = {} _index = 2 _type = "elementary-old" + _yaml = """ + equation: O + H2 <=> H + OH + type: elementary-old + rate-constant: {A: 3.87e+04 cm^3/mol/s, b: 2.7, Ea: 6260.0 cal/mol} + """ @classmethod def setUpClass(cls): @@ -159,6 +164,13 @@ def test_from_dict(self): rxn = self._cls(equation=self._equation, rate=self._rate, kinetics=self.gas, **self._kwargs) self.check_rxn(rxn) + def test_from_yaml(self): + if self._yaml is None: + return + + rxn = ct.Reaction.fromYaml(self._yaml, 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._kwargs) self.check_rxn(rxn) @@ -198,7 +210,10 @@ class TestElementary3(TestElementary): _cls = ct.ElementaryReaction3 _rate_obj = ct.ArrheniusRate(38.7, 2.7, 2.619184e+07) _type = "elementary" - + _yaml = """ + equation: O + H2 <=> H + OH + rate-constant: {A: 3.87e+04 cm^3/mol/s, b: 2.7, Ea: 6260.0 cal/mol} + """ class TestCustom(TestElementary): @@ -208,6 +223,7 @@ class TestCustom(TestElementary): _rate_obj = ct.CustomRate(lambda T: 38.7 * T**2.7 * exp(-3150.15428/T)) _index = 2 _type = "custom-rate-function" + _yaml = None def setUp(self): # need to overwrite rate to ensure correct type ('method' is not compatible with Func1) @@ -250,6 +266,11 @@ class TestElementaryNew(TestElementary): _rate_obj = ct.ArrheniusRate(38.7, 2.7, 2.619184e+07) _index = 2 _type = "elementary-new" + _yaml = """ + equation: O + H2 <=> H + OH + type: elementary-new + rate-constant: {A: 3.87e+04 cm^3/mol/s, b: 2.7, Ea: 6260.0 cal/mol} + """ class TestThreeBody(TestElementary): @@ -261,6 +282,12 @@ class TestThreeBody(TestElementary): _kwargs = {'efficiencies': {'H2': 2.4, 'H2O': 15.4, 'AR': 0.83}} _index = 0 _type = "three-body-old" + _yaml = """ + equation: 2 O + M <=> O2 + M + type: three-body-old + rate-constant: {A: 1.2e+17 cm^6/mol^2/s, b: -1.0, Ea: 0.0 cal/mol} + efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} + """ def test_from_parts(self): orig = self.gas.reaction(self._index) @@ -268,19 +295,38 @@ def test_from_parts(self): rxn.rate = self._rate_obj rxn.efficiencies = self._kwargs['efficiencies'] self.check_rxn(rxn) - + def test_rate(self): # rate constant contains third-body concentration pass - + def test_efficiencies(self): rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) self.assertEqual(rxn.efficiencies, self._kwargs['efficiencies']) - + class TestThreeBody3(TestThreeBody): _cls = ct.ThreeBodyReaction3 _rate_obj = ct.ArrheniusRate(1.2e11, -1., 0.) _type = "three-body" + _yaml = """ + equation: 2 O + M <=> O2 + M + type: three-body + rate-constant: {A: 1.2e+17 cm^6/mol^2/s, b: -1.0, Ea: 0.0 cal/mol} + efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} + """ + + +#class TestChebyshev(TestElementary): +# +# equation: R5 + H (+ M) <=> P5A + P5B (+M) +# type: Chebyshev +# temperature-range: [300.0, 2000.0] +# pressure-range: [9.86e-03 atm, 98.6 atm] +# data: +# - [8.2883, -1.1397, -0.12059, 0.016034] +# - [1.9764, 1.0037, 7.2865e-03] +# - [0.3177, 0.26889, 0.094806, -7.6385e-03] +# - [-0.031285, -0.039412, 0.044375, 0.014458] From 480084311251f19616f55c698a797d20d8f08e49 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 21 Mar 2021 22:27:15 -0500 Subject: [PATCH 23/84] [Kinetics] Prepare for unified handling of third-body concentrations --- include/cantera/kinetics/BulkKinetics.h | 15 +++++++------ include/cantera/kinetics/MultiRate.h | 6 +++--- include/cantera/kinetics/ReactionRate.h | 28 +++++++++++++++---------- src/kinetics/BulkKinetics.cpp | 2 ++ src/kinetics/GasKinetics.cpp | 22 +++++++++---------- src/kinetics/ReactionRate.cpp | 3 ++- 6 files changed, 44 insertions(+), 32 deletions(-) diff --git a/include/cantera/kinetics/BulkKinetics.h b/include/cantera/kinetics/BulkKinetics.h index 13cf8cf8f2a..7984f5a33b4 100644 --- a/include/cantera/kinetics/BulkKinetics.h +++ b/include/cantera/kinetics/BulkKinetics.h @@ -48,10 +48,6 @@ class BulkKinetics : public Kinetics void addThirdBody(shared_ptr r); - ThirdBodyCalc m_multi_concm; //!< used with MultiRate evaluator - vector_fp concm_multi_values; - std::vector m_multi_indices; //!< reaction indices - protected: virtual void addElementaryReaction(ElementaryReaction& r); virtual void modifyElementaryReaction(size_t i, ElementaryReaction& rNew); @@ -69,12 +65,19 @@ class BulkKinetics : public Kinetics //! valued stoichiometries. vector_fp m_dn; - //! Activity concentrations, as calculated by - //! ThermoPhase::getActivityConcentrations + ThirdBodyCalc m_multi_concm; //!< used with MultiRate evaluator + vector_fp concm_multi_values; + std::vector m_multi_indices; //!< reaction indices + + //! Third body concentrations + vector_fp m_concm; + + //! Activity concentrations, as calculated by ThermoPhase::getActivityConcentrations vector_fp m_act_conc; //! Physical concentrations, as calculated by ThermoPhase::getConcentrations vector_fp m_phys_conc; + vector_fp m_grt; bool m_ROP_ok; diff --git a/include/cantera/kinetics/MultiRate.h b/include/cantera/kinetics/MultiRate.h index 97742f0894c..3010dace6ae 100644 --- a/include/cantera/kinetics/MultiRate.h +++ b/include/cantera/kinetics/MultiRate.h @@ -45,7 +45,7 @@ class MultiRateBase //! @param bulk object representing bulk phase //! @param kf array of rate constants virtual void getRateConstants(const ThermoPhase& bulk, - double* kf) const = 0; + double* kf, double* concm) const = 0; //! Update data common to reaction rates of a specific type //! @param bulk object representing bulk phase @@ -86,9 +86,9 @@ class MultiBulkRates final : public MultiRateBase } virtual void getRateConstants(const ThermoPhase& bulk, - double* kf) const override { + double* kf, double* concm) const override { for (size_t i = 0; i < m_rates.size(); i++) { - kf[m_rxn[i]] = m_rates[i].eval(m_shared); + kf[m_rxn[i]] = m_rates[i].eval(m_shared, concm[m_rxn[i]]); } } diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 18e9b2b245b..818b016a7fe 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -68,7 +68,8 @@ class ReactionRateBase //! Evaluate reaction rate based on bulk phase //! @param bulk object representing bulk phase - virtual double eval(const ThermoPhase& bulk) const = 0; + //! @param concm third-body concentration (if applicable) + virtual double eval(const ThermoPhase& bulk, double concm=0.) const = 0; //! Evaluate reaction rate derivative based on temperature //! @param T temperature [K] @@ -81,7 +82,8 @@ class ReactionRateBase //! Evaluate reaction rate derivative based on bulk phase //! @param bulk object representing bulk phase - virtual double ddT(const ThermoPhase& bulk) const = 0; + //! @param concm third-body concentration (if applicable) + virtual double ddT(const ThermoPhase& bulk, double concm=0.) const = 0; }; @@ -102,7 +104,8 @@ class ReactionRate : public ReactionRateBase //! Evaluate reaction rate //! @param shared_data data shared by all reactions of a given type - virtual double eval(const DataType& shared_data) const = 0; + //! @param concm third-body concentration (if applicable) + virtual double eval(const DataType& shared_data, double concm=0.) const = 0; virtual double eval(double T) const override { return eval(DataType(T)); @@ -112,13 +115,13 @@ class ReactionRate : public ReactionRateBase return eval(DataType(T, P)); } - virtual double eval(const ThermoPhase& bulk) const override { - return eval(DataType(bulk)); + virtual double eval(const ThermoPhase& bulk, double concm=0.) const override { + return eval(DataType(bulk), concm); } //! 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 { + virtual double ddT(const DataType& shared_data, double concm=0.) const { throw CanteraError("ReactionRate::ddT", "Not implemented by derived ReactionRate object."); } @@ -131,8 +134,8 @@ class ReactionRate : public ReactionRateBase return ddT(DataType(T, P)); } - virtual double ddT(const ThermoPhase& bulk) const override { - return ddT(DataType(bulk)); + virtual double ddT(const ThermoPhase& bulk, double concm=0.) const override { + return ddT(DataType(bulk), concm); } }; @@ -159,11 +162,13 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius //! Update information specific to reaction static bool uses_update() { return false; } - virtual double eval(const ArrheniusData& shared_data) const override { + virtual double eval(const ArrheniusData& shared_data, + double concm=0.) const override { return updateRC(shared_data.m_logT, shared_data.m_recipT); } - virtual double ddT(const ArrheniusData& shared_data) const override { + virtual double ddT(const ArrheniusData& shared_data, + double concm=0.) const override { return updateRC(shared_data.m_logT, shared_data.m_recipT) * (m_b + m_E * shared_data.m_recipT) * shared_data.m_recipT; } @@ -197,7 +202,8 @@ class CustomFunc1Rate final : public ReactionRate */ void setRateFunction(shared_ptr f); - virtual double eval(const CustomFunc1Data& shared_data) const override; + virtual double eval(const CustomFunc1Data& shared_data, + double concm=0.) const override; protected: shared_ptr m_ratefunc; diff --git a/src/kinetics/BulkKinetics.cpp b/src/kinetics/BulkKinetics.cpp index 05e4a74fb22..1969bf3bc46 100644 --- a/src/kinetics/BulkKinetics.cpp +++ b/src/kinetics/BulkKinetics.cpp @@ -148,6 +148,8 @@ bool BulkKinetics::addReaction(shared_ptr r) } } + m_concm.push_back(0.0); + return true; } diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index fd90414700c..28ae648bd81 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -54,7 +54,7 @@ void GasKinetics::update_rates_T() // loop over MultiBulkRates evaluators for (auto& rates : m_bulk_rates) { rates->update(thermo()); - rates->getRateConstants(thermo(), m_rfn.data()); + rates->getRateConstants(thermo(), m_rfn.data(), m_concm.data()); } if (m_plog_rates.nReactions()) { @@ -89,7 +89,11 @@ void GasKinetics::update_rates_C() // Third-body objects interacting with MultiRate evaluator if (!concm_multi_values.empty()) { + // using pre-existing third-body handlers requires copying; m_multi_concm.update(m_phys_conc, ctot, concm_multi_values.data()); + for (size_t i = 0; i < m_multi_indices.size(); i++) { + m_concm[m_multi_indices[i]] = concm_multi_values[i]; + } } // P-log reactions @@ -189,11 +193,9 @@ void GasKinetics::updateROP() processFalloffReactions(); } - if (!concm_multi_values.empty()) { - // multiply 3rd body concentrations - for (size_t i = 0; i < m_multi_indices.size(); i++) { - m_ropf[m_multi_indices[i]] *= concm_multi_values[i]; - } + // reactions involving third body + for (auto & index : m_multi_indices) { + m_ropf[index] *= m_concm[index]; } for (size_t i = 0; i < nReactions(); i++) { @@ -242,11 +244,9 @@ void GasKinetics::getFwdRateConstants(doublereal* kfwd) processFalloffReactions(); } - if (!concm_multi_values.empty()) { - // multiply 3rd body concentrations - for (size_t i = 0; i < m_multi_indices.size(); i++) { - m_ropf[m_multi_indices[i]] *= concm_multi_values[i]; - } + // reactions involving third body + for (auto & index : m_multi_indices) { + m_ropf[index] *= m_concm[index]; } for (size_t i = 0; i < nReactions(); i++) { diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 7058987e734..73c5ced3d3b 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -41,7 +41,8 @@ void CustomFunc1Rate::setRateFunction(shared_ptr f) { m_ratefunc = f; } -double CustomFunc1Rate::eval(const CustomFunc1Data& shared_data) const { +double CustomFunc1Rate::eval(const CustomFunc1Data& shared_data, + double concm) const { if (m_ratefunc) { return m_ratefunc->eval(shared_data.m_temperature); } From 504a02af4d5ad0f2c476b1533ffd98c4f1a2a193 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 22 Mar 2021 06:51:58 -0500 Subject: [PATCH 24/84] [CI] Switch test_reaction.py to kineticsfromscratch.yaml --- .../cython/cantera/test/test_reaction.py | 133 +++++++++++------- 1 file changed, 84 insertions(+), 49 deletions(-) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 0775bc88b23..047523cf1b4 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -109,28 +109,24 @@ def test_user_override(self): self.assertEqual(rxn.reaction_type, "elementary") -class TestElementary(utilities.CanteraTest): +class TestReaction(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) + _cls = None + _equation = None + _rate = None + _rate_obj = None _kwargs = {} - _index = 2 - _type = "elementary-old" - _yaml = """ - equation: O + H2 <=> H + OH - type: elementary-old - rate-constant: {A: 3.87e+04 cm^3/mol/s, b: 2.7, Ea: 6260.0 cal/mol} - """ + _index = None + _type = None + _yaml = None @classmethod def setUpClass(cls): utilities.CanteraTest.setUpClass() - cls.gas = ct.Solution('h2o2.yaml', transport_model=None) + cls.gas = ct.Solution('kineticsfromscratch.yaml', transport_model=None) 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.yaml') + cls.species = cls.gas.species() def check_rxn(self, rxn): ix = self._index @@ -152,30 +148,39 @@ def check_sol(self, gas2): self.gas.net_rates_of_progress[ix]) def test_rate(self): + if self._rate_obj is None: + return self.assertNear(self._rate_obj(self.gas.T), self.gas.forward_rate_constants[self._index]) def test_from_parts(self): + if self._cls is None: + return 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): + if self._cls is None: + return rxn = self._cls(equation=self._equation, rate=self._rate, kinetics=self.gas, **self._kwargs) self.check_rxn(rxn) def test_from_yaml(self): if self._yaml is None: return - rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) self.check_rxn(rxn) - + def test_from_rate(self): + if self._cls is None or self._rate_obj is None: + return rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) self.check_rxn(rxn) def test_add_rxn(self): + if self._cls is None: + return gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', species=self.species, reactions=[]) gas2.TPX = self.gas.TPX @@ -185,10 +190,14 @@ def test_add_rxn(self): self.check_sol(gas2) def test_wrong_rate(self): + if self._cls is None: + return with self.assertRaises(TypeError): rxn = self._cls(equation=self._equation, rate=[], kinetics=self.gas, **self._kwargs) def test_no_rate(self): + if self._cls is None: + return rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) self.assertNear(rxn.rate(self.gas.T), 0.) @@ -200,11 +209,28 @@ def test_no_rate(self): self.assertNear(gas2.net_rates_of_progress[0], 0.) def test_replace_rate(self): + if self._cls is None: + return rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) rxn.rate = self._rate_obj self.check_rxn(rxn) +class TestElementary(TestReaction): + + _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) + _kwargs = {} + _index = 0 + _type = "elementary-old" + _yaml = """ + equation: O + H2 <=> H + OH + type: elementary-old + rate-constant: {A: 38.7, b: 2.7, Ea: 6260.0 cal/mol} + """ + class TestElementary3(TestElementary): _cls = ct.ElementaryReaction3 @@ -212,16 +238,16 @@ class TestElementary3(TestElementary): _type = "elementary" _yaml = """ equation: O + H2 <=> H + OH - rate-constant: {A: 3.87e+04 cm^3/mol/s, b: 2.7, Ea: 6260.0 cal/mol} + rate-constant: {A: 38.7, b: 2.7, Ea: 6260.0 cal/mol} """ -class TestCustom(TestElementary): +class TestCustom(TestReaction): # 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 + _index = 0 _type = "custom-rate-function" _yaml = None @@ -258,34 +284,19 @@ def test_custom(self): 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" - _yaml = """ - equation: O + H2 <=> H + OH - type: elementary-new - rate-constant: {A: 3.87e+04 cm^3/mol/s, b: 2.7, Ea: 6260.0 cal/mol} - """ - - -class TestThreeBody(TestElementary): +class TestThreeBody(TestReaction): _cls = ct.ThreeBodyReaction _equation = '2 O + M <=> O2 + M' _rate = {'A': 1.2e11, 'b': -1.0, 'Ea': 0.0} _rate_obj = ct.Arrhenius(1.2e11, -1., 0.) _kwargs = {'efficiencies': {'H2': 2.4, 'H2O': 15.4, 'AR': 0.83}} - _index = 0 + _index = 1 _type = "three-body-old" _yaml = """ equation: 2 O + M <=> O2 + M type: three-body-old - rate-constant: {A: 1.2e+17 cm^6/mol^2/s, b: -1.0, Ea: 0.0 cal/mol} + rate-constant: {A: 1.2e+11, b: -1.0, Ea: 0.0 cal/mol} efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} """ @@ -314,19 +325,43 @@ class TestThreeBody3(TestThreeBody): _yaml = """ equation: 2 O + M <=> O2 + M type: three-body - rate-constant: {A: 1.2e+17 cm^6/mol^2/s, b: -1.0, Ea: 0.0 cal/mol} + rate-constant: {A: 1.2e+11, b: -1.0, Ea: 0.0 cal/mol} efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} """ -#class TestChebyshev(TestElementary): -# -# equation: R5 + H (+ M) <=> P5A + P5B (+M) -# type: Chebyshev -# temperature-range: [300.0, 2000.0] -# pressure-range: [9.86e-03 atm, 98.6 atm] -# data: -# - [8.2883, -1.1397, -0.12059, 0.016034] -# - [1.9764, 1.0037, 7.2865e-03] -# - [0.3177, 0.26889, 0.094806, -7.6385e-03] -# - [-0.031285, -0.039412, 0.044375, 0.014458] +class TestPlog(TestReaction): + + _equation = "H2 + O2 <=> 2 OH" + _type = "pressure-dependent-Arrhenius" + _index = 3 + _yaml = """ + equation: H2 + O2 <=> 2 OH # Reaction 4 + type: pressure-dependent-Arrhenius + rate-constants: + - {P: 0.01 atm, A: 1.2124e+16, b: -0.5779, Ea: 1.08727e+04 cal/mol} + - {P: 1.0 atm, A: 4.9108e+31, b: -4.8507, Ea: 2.47728e+04 cal/mol} + - {P: 10.0 atm, A: 1.2866e+47, b: -9.0246, Ea: 3.97965e+04 cal/mol} + - {P: 100.0 atm, A: 5.9632e+56, b: -11.529, Ea: 5.25996e+04 cal/mol} + """ + + +class TestChebyshev(TestReaction): + + _equation = "HO2 <=> OH + O" + _rate = {'Tmin': 290., 'Tmax': 3000., 'Pmin': 1000., 'Pmax': 10000000.0, + 'data': [[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], + [ 1.9764e+00, 1.0037e+00, 7.2865e-03, -3.0432e-02], + [ 3.1770e-01, 2.6889e-01, 9.4806e-02, -7.6385e-03]]} + _type = "Chebyshev" + _index = 4 + _yaml = """ + equation: HO2 <=> OH + O # Reaction 5 + type: Chebyshev + temperature-range: [290.0, 3000.0] + pressure-range: [9.869232667160128e-03 atm, 98.69232667160128 atm] + data: + - [8.2883, -1.1397, -0.12059, 0.016034] + - [1.9764, 1.0037, 7.2865e-03, -0.030432] + - [0.3177, 0.26889, 0.094806, -7.6385e-03] + """ From cab1687321fa280edd42ba12d217f7b854b50737 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 22 Mar 2021 07:29:41 -0500 Subject: [PATCH 25/84] [Kinetics] Add __init__ to PlogReaction and ChebyshevReaction --- interfaces/cython/cantera/reaction.pyx | 34 +++++++++++++++++++ .../cython/cantera/test/test_reaction.py | 16 ++++++--- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 73502ddb336..008dce84eb5 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -846,6 +846,23 @@ cdef class PlogReaction(Reaction): """ reaction_type = "pressure-dependent-Arrhenius" + def __init__(self, equation=None, rate=None, Kinetics kinetics=None, + init=True, **kwargs): + + if init and equation and kinetics: + + spec = {'equation': equation, 'type': self.reaction_type} + if isinstance(rate, list): + spec['rate-constants'] = [] + for r in rate: + spec['rate-constants'].append({'P': r['P'], **r['rate-constant']}) + else: + raise TypeError("Invalid rate definition") + + self._reaction = CxxNewReaction(dict_to_anymap(spec), + deref(kinetics.kinetics)) + self.reaction = self._reaction.get() + property rates: """ Get/Set the rate coefficients for this reaction, which are given as a @@ -889,6 +906,23 @@ cdef class ChebyshevReaction(Reaction): """ reaction_type = "Chebyshev" + def __init__(self, equation=None, rate=None, Kinetics kinetics=None, + init=True, **kwargs): + + if init and equation and kinetics: + + spec = {'equation': equation, 'type': self.reaction_type} + if isinstance(rate, dict): + spec['temperature-range'] = [rate['Tmin'], rate['Tmax']] + spec['pressure-range'] = [rate['Pmin'], rate['Pmax']] + spec['data'] = rate['data'] + else: + raise TypeError("Invalid rate definition") + + self._reaction = CxxNewReaction(dict_to_anymap(spec), + deref(kinetics.kinetics)) + self.reaction = self._reaction.get() + property Tmin: """ Minimum temperature [K] for the Chebyshev fit """ def __get__(self): diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 047523cf1b4..5bd634f6181 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -153,7 +153,7 @@ def test_rate(self): self.assertNear(self._rate_obj(self.gas.T), self.gas.forward_rate_constants[self._index]) def test_from_parts(self): - if self._cls is None: + if self._cls is None or not hasattr(self._cls, 'rate'): return orig = self.gas.reaction(self._index) rxn = self._cls(orig.reactants, orig.products) @@ -179,7 +179,7 @@ def test_from_rate(self): self.check_rxn(rxn) def test_add_rxn(self): - if self._cls is None: + if self._cls is None or self._rate_obj is None: return gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', species=self.species, reactions=[]) @@ -193,10 +193,10 @@ def test_wrong_rate(self): if self._cls is None: return with self.assertRaises(TypeError): - rxn = self._cls(equation=self._equation, rate=[], kinetics=self.gas, **self._kwargs) + rxn = self._cls(equation=self._equation, rate=(), kinetics=self.gas, **self._kwargs) def test_no_rate(self): - if self._cls is None: + if self._cls is None or not hasattr(self._cls, 'rate'): return rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) self.assertNear(rxn.rate(self.gas.T), 0.) @@ -209,7 +209,7 @@ def test_no_rate(self): self.assertNear(gas2.net_rates_of_progress[0], 0.) def test_replace_rate(self): - if self._cls is None: + if self._cls is None or self._rate_obj is None: return rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) rxn.rate = self._rate_obj @@ -332,7 +332,12 @@ class TestThreeBody3(TestThreeBody): class TestPlog(TestReaction): + _cls = ct.PlogReaction _equation = "H2 + O2 <=> 2 OH" + _rate = [{'P': 1013.25, 'rate-constant': {'A': 1.2124e+16, 'b': -0.5779, 'Ea': 45491376.8}}, + {'P': 101325., 'rate-constant': {'A': 4.9108e+31, 'b': -4.8507, 'Ea': 103649395.2}}, + {'P': 1013250., 'rate-constant': {'A': 1.2866e+47, 'b': -9.0246, 'Ea': 166508556.0}}, + {'P': 10132500., 'rate-constant': {'A': 5.9632e+56, 'b': -11.529, 'Ea': 220076726.4}}] _type = "pressure-dependent-Arrhenius" _index = 3 _yaml = """ @@ -348,6 +353,7 @@ class TestPlog(TestReaction): class TestChebyshev(TestReaction): + _cls = ct.ChebyshevReaction _equation = "HO2 <=> OH + O" _rate = {'Tmin': 290., 'Tmax': 3000., 'Pmin': 1000., 'Pmax': 10000000.0, 'data': [[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], From 88b34b04375af0f5e649d9694aa0cb798c5a2266 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 22 Mar 2021 08:07:45 -0500 Subject: [PATCH 26/84] [Kinetics] Switch ArrheniusRate to customary activation energy units --- include/cantera/kinetics/ReactionRate.h | 15 +++++++++++++++ interfaces/cython/cantera/_cantera.pxd | 2 +- interfaces/cython/cantera/reaction.pyx | 4 ++-- src/kinetics/ReactionRate.cpp | 2 +- test/kinetics/kineticsFromScratch3.cpp | 2 +- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 818b016a7fe..cd58334e4cf 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -147,10 +147,20 @@ class ReactionRate : public ReactionRateBase class ArrheniusRate final : public ReactionRate, public Arrhenius { public: + //! Default constructor. ArrheniusRate(); + /// Constructor. + /// @param A pre-exponential. The unit system is + /// (kmol, m, s). The actual units depend on the reaction + /// order and the dimensionality (surface or bulk). + /// @param b Temperature exponent. Non-dimensional. + /// @param E Activation energy. J/kmol. ArrheniusRate(double A, double b, double E); + //! AnyMap constructor. + /// @param node AnyMap containing rate information + /// @param rate_units unit definitions used for rate information ArrheniusRate(const AnyMap& node, const Units& rate_units); virtual bool setParameters(const AnyMap& node, const Units& rate_units) override; @@ -172,6 +182,11 @@ 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; } + + //! Return the activation energy [J/kmol] + double activationEnergy() const { + return m_E * GasConstant; + } }; diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 14c34848f09..7364dd4bb6b 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -370,7 +370,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": CxxArrheniusRate(double, double, double) double preExponentialFactor() double temperatureExponent() - double activationEnergy_R() + double activationEnergy() cdef cppclass CxxReaction "Cantera::Reaction": CxxReaction() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 008dce84eb5..0e3e896f8ac 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -495,7 +495,7 @@ cdef class ArrheniusRate(_ReactionRate): 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.reset(new CxxArrheniusRate(A, b, E)) self.base = self._base.get() self.rate = (self.base) @@ -532,7 +532,7 @@ cdef class ArrheniusRate(_ReactionRate): The activation energy *E* [J/kmol]. """ def __get__(self): - return self.rate.activationEnergy_R() * gas_constant + return self.rate.activationEnergy() cdef class ElementaryReaction(Reaction): diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 73c5ced3d3b..776839718e1 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -15,7 +15,7 @@ ArrheniusRate::ArrheniusRate() } ArrheniusRate::ArrheniusRate(double A, double b, double E) - : Arrhenius(A, b, E) { + : Arrhenius(A, b, E / GasConstant) { } ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rate_units) { diff --git a/test/kinetics/kineticsFromScratch3.cpp b/test/kinetics/kineticsFromScratch3.cpp index 85e3932db05..354de7c791b 100644 --- a/test/kinetics/kineticsFromScratch3.cpp +++ b/test/kinetics/kineticsFromScratch3.cpp @@ -59,7 +59,7 @@ TEST_F(KineticsFromScratch3, add_elementary_reaction) // reaction('O + H2 <=> H + OH', [3.870000e+01, 2.7, 6260.0]) Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); - ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); + ArrheniusRate rate(3.87e1, 2.7, 2.619184e+07); auto R = make_shared(reac, prod, rate); kin->addReaction(R); From 0255e6ddcb402abd12bb05645993c962aa48cf34 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 22 Mar 2021 08:40:38 -0500 Subject: [PATCH 27/84] [samples] Add XML benchmark to custom_reaction.py --- .../examples/kinetics/custom_reactions.py | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/interfaces/cython/cantera/examples/kinetics/custom_reactions.py b/interfaces/cython/cantera/examples/kinetics/custom_reactions.py index 267d746569d..3d39a3dcb9b 100644 --- a/interfaces/cython/cantera/examples/kinetics/custom_reactions.py +++ b/interfaces/cython/cantera/examples/kinetics/custom_reactions.py @@ -29,24 +29,9 @@ 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: +# old framework - use xml input - 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) +gas2 = ct.Solution(mech.replace('.yaml', '.xml')) # construct test case - simulate ignition @@ -75,7 +60,7 @@ def ignition(gas): for i in range(repeat): sim0 += ignition(gas0) sim0 /= repeat -print('- Original mechanism: ' +print('- New framework (YAML): ' '{0:.2f} ms (T_final={1:.2f})'.format(sim0, gas0.T)) sim1 = 0 @@ -90,6 +75,6 @@ def ignition(gas): for i in range(repeat): sim2 += ignition(gas2) sim2 /= repeat -print('- Alternative reactions: ' +print('- Old framework (XML): ' '{0:.2f} ms (T_final={1:.2f}) ... ' '{2:+.2f}%'.format(sim2, gas2.T, 100 * sim2 / sim0 - 100)) From b2ced6dc2152d49ea402a71d8f618563c6587e16 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 22 Mar 2021 22:35:58 -0500 Subject: [PATCH 28/84] [Kinetics] Implement PlogRate --- include/cantera/kinetics/MultiRate.h | 2 +- include/cantera/kinetics/ReactionData.h | 39 +++++++++++++++ include/cantera/kinetics/ReactionRate.h | 64 +++++++++++++++++++++++-- include/cantera/kinetics/RxnRates.h | 13 +++++ src/kinetics/Reaction.cpp | 11 ++--- src/kinetics/ReactionData.cpp | 15 ++++++ src/kinetics/ReactionRate.cpp | 25 ++++++++++ src/kinetics/RxnRates.cpp | 34 +++++++++++++ 8 files changed, 191 insertions(+), 12 deletions(-) diff --git a/include/cantera/kinetics/MultiRate.h b/include/cantera/kinetics/MultiRate.h index 3010dace6ae..e9b75a07493 100644 --- a/include/cantera/kinetics/MultiRate.h +++ b/include/cantera/kinetics/MultiRate.h @@ -88,7 +88,7 @@ class MultiBulkRates final : public MultiRateBase virtual void getRateConstants(const ThermoPhase& bulk, double* kf, double* concm) const override { for (size_t i = 0; i < m_rates.size(); i++) { - kf[m_rxn[i]] = m_rates[i].eval(m_shared, concm[m_rxn[i]]); + kf[m_rxn[i]] = m_rates[i].eval(m_shared, concm[m_rxn[i]]); } } diff --git a/include/cantera/kinetics/ReactionData.h b/include/cantera/kinetics/ReactionData.h index 0d561e73a6e..fe706d2c02d 100644 --- a/include/cantera/kinetics/ReactionData.h +++ b/include/cantera/kinetics/ReactionData.h @@ -48,6 +48,45 @@ struct ArrheniusData }; +//! Data container holding shared data specific to PlogRate +/** + * The data container `PlogData` holds precalculated data common to + * all `PlogRate` objects. + */ +struct PlogData +{ + PlogData() : m_temperature(1.), m_logT(0.), m_recipT(1.), m_logP(0.) {} + + //! Constructor based on temperature *T* and pressure *P* + PlogData(double T); + + //! Constructor based on temperature *T* and pressure *P* + PlogData(double T, double P) { update(T, P); }; + + //! Constructor accessing *bulk* phase definitions + PlogData(const ThermoPhase& bulk) { update(bulk); } + + void update(double T); + + void update(double T, double P) { + m_temperature = T; + m_logT = std::log(T); + m_recipT = 1./T; + m_logP = std::log(P); + } + + void update(const ThermoPhase& bulk); + + //! Pointer to logP (required by Plog::update_C) + const double* logP() const { return &m_logP; } + + double m_temperature; //!< temperature + double m_logT; //!< logarithm of temperature + double m_recipT; //!< inverse of temperature + double m_logP; //!< logarithm of pressure +}; + + //! Data container holding shared data specific to CustomFunc1Rate struct CustomFunc1Data { diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index cd58334e4cf..f407d51fcff 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -140,9 +140,15 @@ class ReactionRate : public ReactionRateBase }; -//! Arrhenius reaction rate type depends only on temperature +//! The ArrheniusRate reaction rate type depends only on temperature /** - * Wrapped Arrhenius rate. + * A reaction rate coefficient of the following form. + * + * \f[ + * k_f = A T^b \exp (-E/RT) + * \f] + * + * Note: ArrheniusRate acts as a wrapper for the Arrhenius class. */ class ArrheniusRate final : public ReactionRate, public Arrhenius { @@ -158,7 +164,7 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius /// @param E Activation energy. J/kmol. ArrheniusRate(double A, double b, double E); - //! AnyMap constructor. + //! Constructor using AnyMap content /// @param node AnyMap containing rate information /// @param rate_units unit definitions used for rate information ArrheniusRate(const AnyMap& node, const Units& rate_units); @@ -190,6 +196,58 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius }; +//! Pressure-dependent reaction rate expressed by logarithmically interpolating +//! between Arrhenius rate expressions at various pressures. +/*! + * Given two rate expressions at two specific pressures: + * + * * \f$ P_1: k_1(T) = A_1 T^{b_1} e^{-E_1 / RT} \f$ + * * \f$ P_2: k_2(T) = A_2 T^{b_2} e^{-E_2 / RT} \f$ + * + * The rate at an intermediate pressure \f$ P_1 < P < P_2 \f$ is computed as + * \f[ + * \log k(T,P) = \log k_1(T) + \bigl(\log k_2(T) - \log k_1(T)\bigr) + * \frac{\log P - \log P_1}{\log P_2 - \log P_1} + * \f] + * Multiple rate expressions may be given at the same pressure, in which case + * the rate used in the interpolation formula is the sum of all the rates given + * at that pressure. For pressures outside the given range, the rate expression + * at the nearest pressure is used. + */ +class PlogRate final : public ReactionRate, public Plog +{ +public: + PlogRate(); + + //! Constructor from Arrhenius rate expressions at a set of pressures + explicit PlogRate(const std::multimap& rates); + + //! Constructor using AnyMap content + /// @param node AnyMap containing rate information + /// @param rate_units unit definitions used for rate information + PlogRate(const AnyMap& node, const Units& rate_units); + + virtual bool setParameters(const AnyMap& node, const Units& rate_units) override; + virtual void getParameters(AnyMap& rateNode, + const Units& rate_units) const override; + + //! Update information specific to reaction + static bool uses_update() { return true; } + + virtual void update(const PlogData& shared_data, + const ThermoPhase& bulk) override { + update_C(shared_data.logP()); + } + + virtual std::string type() const override { return "PlogRate"; } + + virtual double eval(const PlogData& shared_data, + double concm=0.) const override { + return updateRC(shared_data.m_logT, shared_data.m_recipT); + } +}; + + //! Custom reaction rate depending only on temperature /** * The rate expression is provided by a `Func1` object taking a single diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 5f526ebf220..b278b740a90 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -43,6 +43,10 @@ class Arrhenius /// @param E Activation energy in temperature units. Kelvin. Arrhenius(doublereal A, doublereal b, doublereal E); + //! Constructor based on AnyMap content + Arrhenius(const AnyValue& rate, + const UnitSystem& units, const Units& rate_units); + //! Run object setup based on AnyMap node information void setParameters(const AnyValue& rate, const UnitSystem& units, const Units& rate_units); @@ -324,6 +328,15 @@ class Plog //! Constructor from Arrhenius rate expressions at a set of pressures explicit Plog(const std::multimap& rates); + //! Run object setup based on AnyMap node information + void setParameters(const AnyValue& node, + const UnitSystem& units, const Units& rate_units); + + void getParameters(AnyMap& rateNode, const Units& rate_units) const; + + //! Set up Plog object + void setup(const std::multimap& rates); + //! Update concentration-dependent parts of the rate coefficient. //! @param c natural log of the pressure in Pa void update_C(const doublereal* c) { diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index f720949045e..5a117cec722 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -626,14 +626,9 @@ void PlogReaction::getParameters(AnyMap& reactionNode) const { Reaction::getParameters(reactionNode); reactionNode["type"] = "pressure-dependent-Arrhenius"; - std::vector rateList; - for (const auto& r : rate.rates()) { - AnyMap rateNode; - rateNode["P"].setQuantity(r.first, "Pa"); - r.second.getParameters(rateNode, rate_units); - rateList.push_back(std::move(rateNode)); - } - reactionNode["rate-constants"] = std::move(rateList); + AnyMap rateNode; + rate.getParameters(rateNode, rate_units); + reactionNode.update(rateNode); } ChebyshevReaction::ChebyshevReaction() diff --git a/src/kinetics/ReactionData.cpp b/src/kinetics/ReactionData.cpp index 07041e33a21..413f97396cd 100644 --- a/src/kinetics/ReactionData.cpp +++ b/src/kinetics/ReactionData.cpp @@ -5,6 +5,7 @@ #include "cantera/kinetics/ReactionData.h" #include "cantera/thermo/ThermoPhase.h" +#include "cantera/base/ctexceptions.h" namespace Cantera { @@ -13,6 +14,20 @@ void ArrheniusData::update(const ThermoPhase& bulk) { update(bulk.temperature()); } +PlogData::PlogData(double T) { + throw CanteraError("PlogData::PlogData", + "Missing state information: reaction type requires pressure."); +} + +void PlogData::update(double T) { + throw CanteraError("PlogData::update", + "Missing state information: reaction type requires pressure."); +} + +void PlogData::update(const ThermoPhase& bulk) { + update(bulk.temperature(), bulk.pressure()); +} + void CustomFunc1Data::update(const ThermoPhase& bulk) { m_temperature = bulk.temperature(); } diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 776839718e1..95213e511df 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -35,6 +35,31 @@ void ArrheniusRate::getParameters(AnyMap& rateNode, Arrhenius::getParameters(rateNode, rate_units); } +PlogRate::PlogRate() + : Plog() { +} + +PlogRate::PlogRate(const std::multimap& rates) + : Plog(rates) { +} + +PlogRate::PlogRate(const AnyMap& node, const Units& rate_units) { + setParameters(node, rate_units); +} + +bool PlogRate::setParameters(const AnyMap& node, const Units& rate_units) { + if (!node.hasKey("rate-constant")) { + return false; + } + Plog::setParameters(node["rate-constant"], node.units(), rate_units); + return true; +} + +void PlogRate::getParameters(AnyMap& rateNode, + const Units& rate_units) const { + Plog::getParameters(rateNode, rate_units); +} + CustomFunc1Rate::CustomFunc1Rate() : m_ratefunc(0) {} void CustomFunc1Rate::setRateFunction(shared_ptr f) { diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 5571aca3042..9f110ee88be 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -29,6 +29,12 @@ Arrhenius::Arrhenius(doublereal A, doublereal b, doublereal E) } } +Arrhenius::Arrhenius(const AnyValue& rate, + const UnitSystem& units, const Units& rate_units) +{ + setParameters(rate, units, rate_units); +} + void Arrhenius::setParameters(const AnyValue& rate, const UnitSystem& units, const Units& rate_units) { @@ -145,6 +151,34 @@ Plog::Plog(const std::multimap& rates) , logP1_(1000) , logP2_(-1000) , rDeltaP_(-1.0) +{ + setup(rates); +} + +void Plog::setParameters(const AnyValue& node, + const UnitSystem& units, const Units& rate_units) +{ + std::multimap rates; + for (const auto& rate : node["rate-constants"].asVector()) { + rates.insert({rate.convert("P", "Pa"), + Arrhenius(AnyValue(rate), units, rate_units)}); + } + setup(rates); +} + +void Plog::getParameters(AnyMap& rateNode, const Units& rate_units) const +{ + std::vector rateList; + for (const auto& r : rates()) { + AnyMap rateNode_; + rateNode_["P"].setQuantity(r.first, "Pa"); + r.second.getParameters(rateNode_, rate_units); + rateList.push_back(std::move(rateNode_)); + } + rateNode["rate-constants"] = std::move(rateList); +} + +void Plog::setup(const std::multimap& rates) { size_t j = 0; rates_.reserve(rates.size()); From f5a31e2eac1aa202b59e72af3e3175be9758ae71 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 22 Mar 2021 22:57:00 -0500 Subject: [PATCH 29/84] [Kinetics] Implement PlogReaction3 --- include/cantera/kinetics/Reaction.h | 22 +++++++++++++ include/cantera/kinetics/ReactionRate.h | 1 - include/cantera/kinetics/RxnRates.h | 3 +- src/kinetics/BulkKinetics.cpp | 6 ++++ src/kinetics/Reaction.cpp | 44 +++++++++++++++++++++++++ src/kinetics/ReactionFactory.cpp | 3 ++ src/kinetics/ReactionRate.cpp | 5 +-- src/kinetics/RxnRates.cpp | 10 +++--- test/kinetics/kineticsFromScratch3.cpp | 6 ++-- 9 files changed, 89 insertions(+), 11 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 7412163783d..6dc3976cc9c 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -410,6 +410,28 @@ class ThreeBodyReaction3 : public ElementaryReaction3 }; +//! A pressure-dependent reaction parameterized by logarithmically interpolating +//! between Arrhenius rate expressions at various pressures. +class PlogReaction3 : public Reaction3 +{ +public: + PlogReaction3(); + PlogReaction3(const Composition& reactants, const Composition& products, + const PlogRate& rate); + + PlogReaction3(const AnyMap& node, const Kinetics& kin); + + virtual std::string type() const { + return "pressure-dependent-Arrhenius-new"; + } + + virtual bool setParameters(const AnyMap& node, const Kinetics& kin); + virtual void getParameters(AnyMap& reactionNode) const; + + virtual void validate(); +}; + + //! A reaction which follows mass-action kinetics with a custom reaction rate //! defined in Python. class CustomFunc1Reaction : public Reaction3 diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index f407d51fcff..83a5f954d5c 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -19,7 +19,6 @@ namespace Cantera { class Func1; -class AnyMap; //! Abstract base class for reaction rate definitions diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index b278b740a90..d717c18e0f4 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -16,6 +16,7 @@ namespace Cantera class Array2D; class AnyValue; +class AnyMap; class UnitSystem; class Units; class AnyMap; @@ -329,7 +330,7 @@ class Plog explicit Plog(const std::multimap& rates); //! Run object setup based on AnyMap node information - void setParameters(const AnyValue& node, + void setParameters(const std::vector& rates, const UnitSystem& units, const Units& rate_units); void getParameters(AnyMap& rateNode, const Units& rate_units) const; diff --git a/src/kinetics/BulkKinetics.cpp b/src/kinetics/BulkKinetics.cpp index 1969bf3bc46..b6c98d1db20 100644 --- a/src/kinetics/BulkKinetics.cpp +++ b/src/kinetics/BulkKinetics.cpp @@ -132,9 +132,15 @@ bool BulkKinetics::addReaction(shared_ptr r) if (rate->type() == "ArrheniusRate") { m_bulk_rates.push_back(std::unique_ptr( new MultiBulkRates)); + } else if (rate->type() == "PlogRate") { + 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)); + } else { + throw CanteraError("BulkKinetics::addReaction", "Adding " + "reaction type '" + rate->type() + "' is not implemented"); } } diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 5a117cec722..acadc883b1f 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -867,6 +867,50 @@ std::string ThreeBodyReaction3::productString() const { } } +PlogReaction3::PlogReaction3() + : Reaction3() +{ + m_rate = std::shared_ptr(new PlogRate); +} + +PlogReaction3::PlogReaction3(const Composition& reactants, + const Composition& products, const PlogRate& rate) + : Reaction3(reactants, products) +{ + m_rate = std::make_shared(rate); +} + +PlogReaction3::PlogReaction3(const AnyMap& node, const Kinetics& kin) + : PlogReaction3() +{ + setParameters(node, kin); +} + +void PlogReaction3::getParameters(AnyMap& reactionNode) const +{ + Reaction::getParameters(reactionNode); + reactionNode["type"] = "pressure-dependent-Arrhenius"; + AnyMap rateNode; + m_rate->getParameters(rateNode, rate_units); + reactionNode.update(rateNode); +} + +bool PlogReaction3::setParameters(const AnyMap& node, const Kinetics& kin) +{ + if (!Reaction3::setParameters(node, kin)) { + return false; + } + + setRate(std::shared_ptr(new PlogRate(node, rate_units))); + return true; +} + +void PlogReaction3::validate() +{ + Reaction3::validate(); + std::dynamic_pointer_cast(m_rate)->validate(equation()); +} + CustomFunc1Reaction::CustomFunc1Reaction() : Reaction3() { diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index dd629d01afd..3331c16faeb 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -73,6 +73,9 @@ ReactionFactory::ReactionFactory() addAlias("chemically-activated", "chemically_activated"); // register pressure-depdendent-Arrhenius reactions + reg("pressure-dependent-Arrhenius-new", [](const AnyMap& node, const Kinetics& kin) { + return new PlogReaction3(node, kin); + }); reg("pressure-dependent-Arrhenius", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new PlogReaction(); if (node.hasKey("equation")) { diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 95213e511df..1a30ca24fbd 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -48,10 +48,11 @@ PlogRate::PlogRate(const AnyMap& node, const Units& rate_units) { } bool PlogRate::setParameters(const AnyMap& node, const Units& rate_units) { - if (!node.hasKey("rate-constant")) { + if (!node.hasKey("rate-constants")) { return false; } - Plog::setParameters(node["rate-constant"], node.units(), rate_units); + Plog::setParameters(node.at("rate-constants").asVector(), + node.units(), rate_units); return true; } diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 9f110ee88be..f1f78932703 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -155,15 +155,15 @@ Plog::Plog(const std::multimap& rates) setup(rates); } -void Plog::setParameters(const AnyValue& node, +void Plog::setParameters(const std::vector& rates, const UnitSystem& units, const Units& rate_units) { - std::multimap rates; - for (const auto& rate : node["rate-constants"].asVector()) { - rates.insert({rate.convert("P", "Pa"), + std::multimap multi_rates; + for (const auto& rate : rates) { + multi_rates.insert({rate.convert("P", "Pa"), Arrhenius(AnyValue(rate), units, rate_units)}); } - setup(rates); + setup(multi_rates); } void Plog::getParameters(AnyMap& rateNode, const Units& rate_units) const diff --git a/test/kinetics/kineticsFromScratch3.cpp b/test/kinetics/kineticsFromScratch3.cpp index 354de7c791b..c69ac0ec998 100644 --- a/test/kinetics/kineticsFromScratch3.cpp +++ b/test/kinetics/kineticsFromScratch3.cpp @@ -130,6 +130,7 @@ TEST_F(KineticsFromScratch3, add_falloff_reaction) kin->addReaction(R); check_rates(2); } +*/ TEST_F(KineticsFromScratch3, add_plog_reaction) { @@ -148,7 +149,7 @@ TEST_F(KineticsFromScratch3, add_plog_reaction) { 100.0*101325, Arrhenius(5.963200e+56, -11.529, 52599.6 / GasConst_cal_mol_K) } }; - auto R = make_shared(reac, prod, Plog(rates)); + auto R = make_shared(reac, prod, PlogRate(rates)); kin->addReaction(R); check_rates(3); } @@ -164,10 +165,11 @@ TEST_F(KineticsFromScratch3, plog_invalid_rate) { 100.0*101325, Arrhenius(5.9632e+56, -11.529, 52599.6 / GasConst_cal_mol_K) } }; - auto R = make_shared(reac, prod, Plog(rates)); + auto R = make_shared(reac, prod, PlogRate(rates)); ASSERT_THROW(kin->addReaction(R), CanteraError); } +/* TEST_F(KineticsFromScratch3, add_chebyshev_reaction) { // reaction 4: From 7df04c34a1e6c534505a12e9df1c8ca75d51fc69 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 23 Mar 2021 13:37:58 -0500 Subject: [PATCH 30/84] [Kinetics] Streamline MultiRate update mechanism --- include/cantera/kinetics/MultiRate.h | 8 ++--- include/cantera/kinetics/ReactionRate.h | 41 +++++++++++++++++++++---- src/kinetics/GasKinetics.cpp | 2 +- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/include/cantera/kinetics/MultiRate.h b/include/cantera/kinetics/MultiRate.h index e9b75a07493..83bba1b1ae5 100644 --- a/include/cantera/kinetics/MultiRate.h +++ b/include/cantera/kinetics/MultiRate.h @@ -49,7 +49,7 @@ class MultiRateBase //! Update data common to reaction rates of a specific type //! @param bulk object representing bulk phase - virtual void update(const ThermoPhase& bulk) = 0; + virtual void update(const ThermoPhase& bulk, double* concm) = 0; }; @@ -92,15 +92,15 @@ class MultiBulkRates final : public MultiRateBase } } - virtual void update(const ThermoPhase& bulk) override { + virtual void update(const ThermoPhase& bulk, double* concm) 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); + for (size_t i = 0; i < m_rates.size(); i++) { + m_rates[i].update(m_shared, concm[m_rxn[i]]); } } } diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 83a5f954d5c..280b4d4894e 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -56,6 +56,21 @@ class ReactionRateBase //! Identifier of reaction type virtual std::string type() const = 0; + //! Update reaction rate data based on temperature + //! @param T temperature [K] + virtual void update(double T) = 0; + + //! Update reaction rate data based on temperature and pressure + //! @param T temperature [K] + //! @param P pressure [Pa] + //! @param concm third-body concentration (if applicable) + virtual void update(double T, double P, double concm=0.) = 0; + + //! Update reaction rate data based on bulk phase + //! @param bulk object representing bulk phase + //! @param concm third-body concentration (if applicable) + virtual void update(const ThermoPhase& bulk, double concm=0.) = 0; + //! Evaluate reaction rate based on temperature //! @param T temperature [K] virtual double eval(double T) const = 0; @@ -63,7 +78,8 @@ class ReactionRateBase //! 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; + //! @param concm third-body concentration (if applicable) + virtual double eval(double T, double P, double concm=0.) const = 0; //! Evaluate reaction rate based on bulk phase //! @param bulk object representing bulk phase @@ -99,7 +115,21 @@ class ReactionRate : public ReactionRateBase //! 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) {} + virtual void update(const DataType& shared_data, + double concm=0.) {} + + virtual void update(double T) override { + update(DataType(T)); + } + + virtual void update(double T, double P, double concm=0.) override { + update(DataType(T, P), concm); + } + + virtual void update(const ThermoPhase& bulk, double concm=0.) override { + update(DataType(bulk), concm); + } + //! Evaluate reaction rate //! @param shared_data data shared by all reactions of a given type @@ -110,8 +140,8 @@ class ReactionRate : public ReactionRateBase return eval(DataType(T)); } - virtual double eval(double T, double P) const override { - return eval(DataType(T, P)); + virtual double eval(double T, double P, double concm=0.) const override { + return eval(DataType(T, P), concm); } virtual double eval(const ThermoPhase& bulk, double concm=0.) const override { @@ -233,8 +263,7 @@ class PlogRate final : public ReactionRate, public Plog //! Update information specific to reaction static bool uses_update() { return true; } - virtual void update(const PlogData& shared_data, - const ThermoPhase& bulk) override { + virtual void update(const PlogData& shared_data, double concm=0.) override { update_C(shared_data.logP()); } diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 28ae648bd81..ad4fa549ae1 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -53,7 +53,7 @@ void GasKinetics::update_rates_T() // loop over MultiBulkRates evaluators for (auto& rates : m_bulk_rates) { - rates->update(thermo()); + rates->update(thermo(), m_concm.data()); rates->getRateConstants(thermo(), m_rfn.data(), m_concm.data()); } From 08ba915ea623aa7c05e0d82b9164c86ec8a6d86d Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 22 Mar 2021 23:26:18 -0500 Subject: [PATCH 31/84] [Kinetics] Expose PlogRate and PlogReaction3 to Python --- interfaces/cython/cantera/_cantera.pxd | 15 +++ interfaces/cython/cantera/reaction.pyx | 103 ++++++++++++++++++ .../cython/cantera/test/test_reaction.py | 33 +++++- 3 files changed, 149 insertions(+), 2 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 7364dd4bb6b..9eeb49198ab 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -356,6 +356,8 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxReactionRateBase "Cantera::ReactionRateBase": CxxReactionRateBase() string type() + void update(double) except +translate_exception + void update(double, double) except +translate_exception double eval(double) except +translate_exception double eval(double, double) except +translate_exception double ddT(double) except +translate_exception @@ -372,6 +374,11 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": double temperatureExponent() double activationEnergy() + cdef cppclass CxxPlogRate "Cantera::PlogRate" (CxxReactionRateBase): + CxxPlogRate() + CxxPlogRate(multimap[double, CxxArrhenius]) + vector[pair[double, CxxArrhenius]] rates() + cdef cppclass CxxReaction "Cantera::Reaction": CxxReaction() @@ -467,6 +474,9 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": CxxThreeBodyReaction3() shared_ptr[CxxThirdBody] thirdBody() + cdef cppclass CxxPlogReaction3 "Cantera::PlogReaction3" (CxxReaction3): + CxxPlogReaction3() + cdef cppclass CxxCustomFunc1Reaction "Cantera::CustomFunc1Reaction" (CxxReaction3): CxxCustomFunc1Reaction() @@ -1133,6 +1143,11 @@ cdef class ArrheniusRate(_ReactionRate): @staticmethod cdef wrap(shared_ptr[CxxReactionRateBase]) +cdef class PlogRate(_ReactionRate): + cdef CxxPlogRate* rate + @staticmethod + cdef wrap(shared_ptr[CxxReactionRateBase]) + cdef class Reaction: cdef shared_ptr[CxxReaction] _reaction cdef CxxReaction* reaction diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 0e3e896f8ac..50c1c95c68d 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -433,14 +433,18 @@ cdef class _ReactionRate: def __call__(self, double temperature, pressure=None): if pressure: + self.base.update(temperature, pressure) return self.base.eval(temperature, pressure) else: + self.base.update(temperature) return self.base.eval(temperature) def ddT(self, double temperature, pressure=None): if pressure: + self.base.update(temperature, pressure) return self.base.ddT(temperature, pressure) else: + self.base.update(temperature) return self.base.ddT(temperature) @@ -535,6 +539,54 @@ cdef class ArrheniusRate(_ReactionRate): return self.rate.activationEnergy() +cdef class PlogRate(_ReactionRate): + r""" + """ + def __cinit__(self, rates=None, init=True): + + if rates and init: + self.rates = rates + + @staticmethod + cdef wrap(shared_ptr[CxxReactionRateBase] rate): + """ + Wrap a C++ ReactionRateBase object with a Python object. + """ + # wrap C++ reaction + cdef PlogRate arr + arr = PlogRate(init=False) + arr._base = rate + arr.base = arr._base.get() + arr.rate = (arr.base) + return arr + + property rates: + """ + Get/Set the rate coefficients for this reaction, which are given as a + list of (pressure, `Arrhenius`) tuples. + """ + def __get__(self): + rates = [] + cdef vector[pair[double, CxxArrhenius]] cxxrates = self.rate.rates() + cdef pair[double, CxxArrhenius] p_rate + for p_rate in cxxrates: + rates.append((p_rate.first, copyArrhenius(&p_rate.second))) + return rates + + def __set__(self, rates): + cdef multimap[double, CxxArrhenius] ratemap + cdef Arrhenius rate + cdef pair[double, CxxArrhenius] item + for p, rate in rates: + item.first = p + item.second = deref(rate.rate) + ratemap.insert(item) + + self._base.reset(new CxxPlogRate(ratemap)) + self.base = self._base.get() + self.rate = (self.base) + + cdef class ElementaryReaction(Reaction): """ A reaction which follows mass-action kinetics with a modified Arrhenius @@ -1245,6 +1297,57 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): return self.thirdbody().efficiency(stringify(species)) +cdef class PlogReaction3(Reaction): + """ + A pressure-dependent reaction parameterized by logarithmically interpolating + between Arrhenius rate expressions at various pressures. + """ + reaction_type = "pressure-dependent-Arrhenius-new" + + cdef CxxPlogReaction3* pr(self): + return self.reaction + + def __init__(self, equation=None, rate=None, Kinetics kinetics=None, + init=True, **kwargs): + + if init and equation and kinetics: + + spec = {'equation': equation, 'type': self.reaction_type} + if isinstance(rate, list): + spec['rate-constants'] = [] + for r in rate: + spec['rate-constants'].append({'P': r['P'], **r['rate-constant']}) + elif isinstance(rate, PlogRate) or rate is None: + spec['rate-constants'] = [ + {"P": 1, **dict.fromkeys(['A', 'b', 'Ea'], 0.)}, + {"P": 1e7, **dict.fromkeys(['A', 'b', 'Ea'], 0.)}] + else: + raise TypeError("Invalid rate definition") + + self._reaction = CxxNewReaction(dict_to_anymap(spec), + deref(kinetics.kinetics)) + self.reaction = self._reaction.get() + + if isinstance(rate, PlogRate): + self.rate = rate + + property rate: + """ Get/Set the `Plog` rate coefficients for this reaction. """ + def __get__(self): + return PlogRate.wrap(self.pr().rate()) + def __set__(self, PlogRate rate): + self.pr().setRate(rate._base) + + def __call__(self, float T, float P): + cdef CxxPlogReaction* r = self.reaction + cdef double logT = np.log(T) + cdef double recipT = 1/T + cdef double logP = np.log(P) + + r.rate.update_C(&logP) + return r.rate.updateRC(logT, recipT) + + cdef class CustomReaction(Reaction): """ A reaction which follows mass-action kinetics with a custom reaction rate. diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 5bd634f6181..c2ce8c7c048 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -150,7 +150,11 @@ def check_sol(self, gas2): def test_rate(self): if self._rate_obj is None: return - self.assertNear(self._rate_obj(self.gas.T), self.gas.forward_rate_constants[self._index]) + if type(self._rate_obj).__name__.endswith('Rate'): + self.assertNear(self._rate_obj(self.gas.T, self.gas.P), + self.gas.forward_rate_constants[self._index]) + else: + self.assertNear(self._rate_obj(self.gas.T), self.gas.forward_rate_constants[self._index]) def test_from_parts(self): if self._cls is None or not hasattr(self._cls, 'rate'): @@ -199,7 +203,10 @@ def test_no_rate(self): if self._cls is None or not hasattr(self._cls, 'rate'): return rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) - self.assertNear(rxn.rate(self.gas.T), 0.) + if type(self._rate_obj).__name__.endswith('Rate'): + self.assertNear(rxn.rate(self.gas.T, self.gas.P), 0.) + else: + self.assertNear(rxn.rate(self.gas.T), 0.) gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', species=self.species, reactions=[rxn]) @@ -351,6 +358,28 @@ class TestPlog(TestReaction): """ +class TestPlog3(TestPlog): + + _cls = ct.PlogReaction3 + _rate_obj = ct.PlogRate([(1013.25, ct.Arrhenius(1.2124e+16, -0.5779, 45491376.8)), + (101325., ct.Arrhenius(4.9108e+31, -4.8507, 103649395.2)), + (1013250., ct.Arrhenius(1.2866e+47, -9.0246, 166508556.0)), + (10132500., ct.Arrhenius(5.9632e+56, -11.529, 220076726.4))]) + _type = "pressure-dependent-Arrhenius-new" + _yaml = """ + equation: H2 + O2 <=> 2 OH # Reaction 4 + type: pressure-dependent-Arrhenius-new + rate-constants: + - {P: 0.01 atm, A: 1.2124e+16, b: -0.5779, Ea: 1.08727e+04 cal/mol} + - {P: 1.0 atm, A: 4.9108e+31, b: -4.8507, Ea: 2.47728e+04 cal/mol} + - {P: 10.0 atm, A: 1.2866e+47, b: -9.0246, Ea: 3.97965e+04 cal/mol} + - {P: 100.0 atm, A: 5.9632e+56, b: -11.529, Ea: 5.25996e+04 cal/mol} + """ + + def test_from_yaml(self): + pass + + class TestChebyshev(TestReaction): _cls = ct.ChebyshevReaction From 012d9de3d834a7a89a155b6f44f229a28bd965c0 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 23 Mar 2021 17:48:31 -0500 Subject: [PATCH 32/84] [Kinetics] Make PlogReaction3 default for YAML import --- include/cantera/kinetics/Reaction.h | 4 ++-- include/cantera/kinetics/RxnRates.h | 2 +- interfaces/cython/cantera/reaction.pyx | 4 ++-- .../cython/cantera/test/test_reaction.py | 11 ++++------ src/kinetics/GasKinetics.cpp | 4 ++-- src/kinetics/ReactionFactory.cpp | 22 +++++++++++-------- src/kinetics/RxnRates.cpp | 10 ++++++--- test/kinetics/kineticsFromYaml.cpp | 8 +++---- 8 files changed, 35 insertions(+), 30 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 6dc3976cc9c..7a1f950eff6 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -333,7 +333,7 @@ class PlogReaction : public Reaction const Plog& rate); virtual std::string type() const { - return "pressure-dependent-Arrhenius"; + return "pressure-dependent-Arrhenius-old"; } virtual void validate(); @@ -422,7 +422,7 @@ class PlogReaction3 : public Reaction3 PlogReaction3(const AnyMap& node, const Kinetics& kin); virtual std::string type() const { - return "pressure-dependent-Arrhenius-new"; + return "pressure-dependent-Arrhenius"; } virtual bool setParameters(const AnyMap& node, const Kinetics& kin); diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index d717c18e0f4..66eced14056 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -324,7 +324,7 @@ class Plog { public: //! Default constructor. - Plog() {} + Plog(); //! Constructor from Arrhenius rate expressions at a set of pressures explicit Plog(const std::multimap& rates); diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 50c1c95c68d..041e17cf6a5 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -896,7 +896,7 @@ cdef class PlogReaction(Reaction): A pressure-dependent reaction parameterized by logarithmically interpolating between Arrhenius rate expressions at various pressures. """ - reaction_type = "pressure-dependent-Arrhenius" + reaction_type = "pressure-dependent-Arrhenius-old" def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): @@ -1302,7 +1302,7 @@ cdef class PlogReaction3(Reaction): A pressure-dependent reaction parameterized by logarithmically interpolating between Arrhenius rate expressions at various pressures. """ - reaction_type = "pressure-dependent-Arrhenius-new" + reaction_type = "pressure-dependent-Arrhenius" cdef CxxPlogReaction3* pr(self): return self.reaction diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index c2ce8c7c048..a6b6064ca33 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -345,11 +345,11 @@ class TestPlog(TestReaction): {'P': 101325., 'rate-constant': {'A': 4.9108e+31, 'b': -4.8507, 'Ea': 103649395.2}}, {'P': 1013250., 'rate-constant': {'A': 1.2866e+47, 'b': -9.0246, 'Ea': 166508556.0}}, {'P': 10132500., 'rate-constant': {'A': 5.9632e+56, 'b': -11.529, 'Ea': 220076726.4}}] - _type = "pressure-dependent-Arrhenius" + _type = "pressure-dependent-Arrhenius-old" _index = 3 _yaml = """ equation: H2 + O2 <=> 2 OH # Reaction 4 - type: pressure-dependent-Arrhenius + type: pressure-dependent-Arrhenius-old rate-constants: - {P: 0.01 atm, A: 1.2124e+16, b: -0.5779, Ea: 1.08727e+04 cal/mol} - {P: 1.0 atm, A: 4.9108e+31, b: -4.8507, Ea: 2.47728e+04 cal/mol} @@ -365,10 +365,10 @@ class TestPlog3(TestPlog): (101325., ct.Arrhenius(4.9108e+31, -4.8507, 103649395.2)), (1013250., ct.Arrhenius(1.2866e+47, -9.0246, 166508556.0)), (10132500., ct.Arrhenius(5.9632e+56, -11.529, 220076726.4))]) - _type = "pressure-dependent-Arrhenius-new" + _type = "pressure-dependent-Arrhenius" _yaml = """ equation: H2 + O2 <=> 2 OH # Reaction 4 - type: pressure-dependent-Arrhenius-new + type: pressure-dependent-Arrhenius rate-constants: - {P: 0.01 atm, A: 1.2124e+16, b: -0.5779, Ea: 1.08727e+04 cal/mol} - {P: 1.0 atm, A: 4.9108e+31, b: -4.8507, Ea: 2.47728e+04 cal/mol} @@ -376,9 +376,6 @@ class TestPlog3(TestPlog): - {P: 100.0 atm, A: 5.9632e+56, b: -11.529, Ea: 5.25996e+04 cal/mol} """ - def test_from_yaml(self): - pass - class TestChebyshev(TestReaction): diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index ad4fa549ae1..f92605cfcb7 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -274,7 +274,7 @@ bool GasKinetics::addReaction(shared_ptr r) addFalloffReaction(dynamic_cast(*r)); } else if (r->type() == "chemically-activated") { addFalloffReaction(dynamic_cast(*r)); - } else if (r->type() == "pressure-dependent-Arrhenius") { + } else if (r->type() == "pressure-dependent-Arrhenius-old") { addPlogReaction(dynamic_cast(*r)); } else if (r->type() == "Chebyshev") { addChebyshevReaction(dynamic_cast(*r)); @@ -366,7 +366,7 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) modifyFalloffReaction(i, dynamic_cast(*rNew)); } else if (rNew->type() == "chemically-activated") { modifyFalloffReaction(i, dynamic_cast(*rNew)); - } else if (rNew->type() == "pressure-dependent-Arrhenius") { + } else if (rNew->type() == "pressure-dependent-Arrhenius-old") { modifyPlogReaction(i, dynamic_cast(*rNew)); } else if (rNew->type() == "Chebyshev") { modifyChebyshevReaction(i, dynamic_cast(*rNew)); diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 3331c16faeb..e15bb9c9961 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -28,6 +28,7 @@ ReactionFactory::ReactionFactory() addAlias("elementary", "arrhenius"); addAlias("elementary", ""); + // register elementary reactions (old framework) reg("elementary-old", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ElementaryReaction(); if (node.hasKey("equation")) { @@ -43,7 +44,7 @@ ReactionFactory::ReactionFactory() addAlias("three-body", "threebody"); addAlias("three-body", "three_body"); - // register three-body reactions + // register three-body reactions (old framework) reg("three-body-old", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ThreeBodyReaction(); if (node.hasKey("equation")) { @@ -72,19 +73,21 @@ ReactionFactory::ReactionFactory() addAlias("chemically-activated", "chemact"); addAlias("chemically-activated", "chemically_activated"); - // register pressure-depdendent-Arrhenius reactions - reg("pressure-dependent-Arrhenius-new", [](const AnyMap& node, const Kinetics& kin) { + // register pressure-dependent-Arrhenius reactions + reg("pressure-dependent-Arrhenius", [](const AnyMap& node, const Kinetics& kin) { return new PlogReaction3(node, kin); }); - reg("pressure-dependent-Arrhenius", [](const AnyMap& node, const Kinetics& kin) { + addAlias("pressure-dependent-Arrhenius", "plog"); + addAlias("pressure-dependent-Arrhenius", "pdep_arrhenius"); + + // register pressure-dependent-Arrhenius reactions (old framework) + reg("pressure-dependent-Arrhenius-old", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new PlogReaction(); if (node.hasKey("equation")) { setupPlogReaction(*(PlogReaction*)R, node, kin); } return R; }); - addAlias("pressure-dependent-Arrhenius", "plog"); - addAlias("pressure-dependent-Arrhenius", "pdep_arrhenius"); // register Chebyshev reactions reg("Chebyshev", [](const AnyMap& node, const Kinetics& kin) { @@ -187,13 +190,14 @@ ReactionFactoryXML::ReactionFactoryXML() addAlias("chemically-activated", "chemically_activated"); // register pressure-depdendent-Arrhenius reactions - reg("pressure-dependent-Arrhenius", [](const XML_Node& node) { + reg("pressure-dependent-Arrhenius-old", [](const XML_Node& node) { Reaction* R = new PlogReaction(); setupPlogReaction(*(PlogReaction*)R, node); return R; }); - addAlias("pressure-dependent-Arrhenius", "plog"); - addAlias("pressure-dependent-Arrhenius", "pdep_arrhenius"); + addAlias("pressure-dependent-Arrhenius-old", "pressure-dependent-Arrhenius"); + addAlias("pressure-dependent-Arrhenius-old", "plog"); + addAlias("pressure-dependent-Arrhenius-old", "pdep_arrhenius"); // register Chebyshev reactions reg("Chebyshev", [](const XML_Node& node) { diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index f1f78932703..977f3268b03 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -146,11 +146,16 @@ void SurfaceArrhenius::addCoverageDependence(size_t k, doublereal a, } } -Plog::Plog(const std::multimap& rates) +Plog::Plog() : logP_(-1000) , logP1_(1000) , logP2_(-1000) , rDeltaP_(-1.0) +{ +} + +Plog::Plog(const std::multimap& rates) + : Plog() { setup(rates); } @@ -212,7 +217,7 @@ void Plog::validate(const std::string& equation) for (size_t i=0; i < 6; i++) { double k = 0; for (size_t p = ilow1_; p < ilow2_; p++) { - k += rates_[p].updateRC(log(T[i]), 1.0/T[i]); + k += rates_.at(p).updateRC(log(T[i]), 1.0/T[i]); } if (k < 0) { format_to(err_reactions, @@ -243,7 +248,6 @@ std::vector > Plog::rates() const return R; } - ChebyshevRate::ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, const Array2D& coeffs) : Tmin_(Tmin) diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index 2eb0890c28b..a6b5e985835 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -177,8 +177,8 @@ TEST(Reaction, PlogFromYaml) "- {P: 1.01325 MPa, A: 1.680000e+16, b: -0.6, Ea: 14754.0}"); auto R = newReaction(rxn, *(sol->kinetics())); - auto PR = dynamic_cast(*R); - const auto& rates = PR.rate.rates(); + auto PR = dynamic_cast(*R); + const auto& rates = dynamic_cast(*PR.rate()).rates(); EXPECT_EQ(rates.size(), (size_t) 4); EXPECT_NEAR(rates[0].first, 0.039474 * OneAtm, 1e-6); EXPECT_NEAR(rates[2].first, OneAtm, 1e-6); @@ -521,10 +521,10 @@ TEST_F(ReactionToYaml, pdepArrhenius) soln = newSolution("pdep-test.yaml"); soln->thermo()->setState_TPY(1000, 2e5, "R2:1, H:0.1, P2A:2, P2B:0.3"); duplicateReaction(1); - EXPECT_TRUE(std::dynamic_pointer_cast(duplicate)); + EXPECT_TRUE(std::dynamic_pointer_cast(duplicate)); compareReactions(); soln->thermo()->setState_TPY(1100, 1e3, "R2:1, H:0.2, P2A:2, P2B:0.3"); - compareReactions(); + compareReactions(true); } TEST_F(ReactionToYaml, Chebyshev) From 358b454f1bbe3e355a9b176b983eb75a951f32d1 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 23 Mar 2021 17:49:22 -0500 Subject: [PATCH 33/84] [Kinetics] Introduce ReactionRate::validate --- include/cantera/kinetics/ReactionRate.h | 13 ++++++++++++- src/kinetics/Reaction.cpp | 3 ++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 280b4d4894e..85b997db4cc 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -99,6 +99,9 @@ class ReactionRateBase //! @param bulk object representing bulk phase //! @param concm third-body concentration (if applicable) virtual double ddT(const ThermoPhase& bulk, double concm=0.) const = 0; + + //! Validate the reaction rate expression + virtual void validate(const std::string& equation) = 0; }; @@ -130,7 +133,6 @@ class ReactionRate : public ReactionRateBase update(DataType(bulk), concm); } - //! Evaluate reaction rate //! @param shared_data data shared by all reactions of a given type //! @param concm third-body concentration (if applicable) @@ -222,6 +224,8 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius double activationEnergy() const { return m_E * GasConstant; } + + virtual void validate(const std::string& equation) {} }; @@ -273,6 +277,11 @@ class PlogRate final : public ReactionRate, public Plog double concm=0.) const override { return updateRC(shared_data.m_logT, shared_data.m_recipT); } + + virtual void validate(const std::string& equation) { + // changing to const so non-virtual base version can be used + Plog::validate(equation); + } }; @@ -306,6 +315,8 @@ class CustomFunc1Rate final : public ReactionRate virtual double eval(const CustomFunc1Data& shared_data, double concm=0.) const override; + virtual void validate(const std::string& equation) {} + protected: shared_ptr m_ratefunc; }; diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index acadc883b1f..4b836640a27 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -908,7 +908,8 @@ bool PlogReaction3::setParameters(const AnyMap& node, const Kinetics& kin) void PlogReaction3::validate() { Reaction3::validate(); - std::dynamic_pointer_cast(m_rate)->validate(equation()); + //std::dynamic_pointer_cast(m_rate)->validate(equation()); + m_rate->validate(equation()); } CustomFunc1Reaction::CustomFunc1Reaction() From 28bbee2b148eeb9013e0abc65cd03f0aff0d9c0d Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 23 Mar 2021 18:36:35 -0500 Subject: [PATCH 34/84] [Kinetics] Simplify validation checks --- include/cantera/kinetics/Reaction.h | 8 ++----- include/cantera/kinetics/ReactionRate.h | 4 +++- interfaces/cython/cantera/_cantera.pxd | 2 +- interfaces/cython/cantera/reaction.pyx | 7 ++++-- src/kinetics/Reaction.cpp | 31 +++++++------------------ src/kinetics/ReactionRate.cpp | 17 ++++++++++++-- 6 files changed, 34 insertions(+), 35 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 7a1f950eff6..baf7c9ce694 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -179,6 +179,8 @@ class Reaction3 : public Reaction return m_third_body; } + virtual void validate(); + protected: //! Reaction rate used by generic reactions shared_ptr m_rate; @@ -376,10 +378,6 @@ class ElementaryReaction3 : public Reaction3 virtual bool setParameters(const AnyMap& node, const Kinetics& kin); virtual void getParameters(AnyMap& reactionNode) const; - - virtual void validate(); - - bool allow_negative_pre_exponential_factor; }; @@ -427,8 +425,6 @@ class PlogReaction3 : public Reaction3 virtual bool setParameters(const AnyMap& node, const Kinetics& kin); virtual void getParameters(AnyMap& reactionNode) const; - - virtual void validate(); }; diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 85b997db4cc..112d60bbc45 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -225,7 +225,9 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius return m_E * GasConstant; } - virtual void validate(const std::string& equation) {} + virtual void validate(const std::string& equation); + + bool allow_negative_pre_exponential_factor; }; diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 9eeb49198ab..2bf37c9f8c8 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -373,6 +373,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": double preExponentialFactor() double temperatureExponent() double activationEnergy() + cbool allow_negative_pre_exponential_factor cdef cppclass CxxPlogRate "Cantera::PlogRate" (CxxReactionRateBase): CxxPlogRate() @@ -468,7 +469,6 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxElementaryReaction3 "Cantera::ElementaryReaction3" (CxxReaction3): CxxElementaryReaction3() - cbool allow_negative_pre_exponential_factor cdef cppclass CxxThreeBodyReaction3 "Cantera::ThreeBodyReaction3" (CxxElementaryReaction3): CxxThreeBodyReaction3() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 041e17cf6a5..2b872c89e99 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -1174,6 +1174,9 @@ cdef class ElementaryReaction3(Reaction): cdef CxxElementaryReaction3* er(self): return self.reaction + cdef CxxArrheniusRate* arr(self): + return (self.er().rate().get()) + def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): @@ -1207,9 +1210,9 @@ cdef class ElementaryReaction3(Reaction): pre-exponential factor. """ def __get__(self): - return self.er().allow_negative_pre_exponential_factor + return self.arr().allow_negative_pre_exponential_factor def __set__(self, allow): - self.er().allow_negative_pre_exponential_factor = allow + self.arr().allow_negative_pre_exponential_factor = allow cdef class ThreeBodyReaction3(ElementaryReaction3): diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 4b836640a27..f7ccca148cb 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -680,9 +680,14 @@ bool Reaction3::setParameters(const AnyMap& node, const Kinetics& kin) return true; } +void Reaction3::validate() +{ + Reaction::validate(); + m_rate->validate(equation()); +} + ElementaryReaction3::ElementaryReaction3() : Reaction3() - , allow_negative_pre_exponential_factor(false) { m_rate = std::shared_ptr(new ArrheniusRate); } @@ -691,7 +696,6 @@ ElementaryReaction3::ElementaryReaction3(const Composition& reactants, const Composition& products, const ArrheniusRate& rate) : Reaction3(reactants, products) - , allow_negative_pre_exponential_factor(false) { m_rate = std::make_shared(rate); } @@ -708,14 +712,14 @@ bool ElementaryReaction3::setParameters(const AnyMap& node, const Kinetics& kin) return false; } setRate(std::shared_ptr(new ArrheniusRate(node, rate_units))); - allow_negative_pre_exponential_factor = node.getBool("negative-A", false); return true; } void ElementaryReaction3::getParameters(AnyMap& reactionNode) const { Reaction::getParameters(reactionNode); - if (allow_negative_pre_exponential_factor) { + ArrheniusRate& rate = dynamic_cast(*m_rate); + if (rate.allow_negative_pre_exponential_factor) { reactionNode["negative-A"] = true; } AnyMap rateNode; @@ -723,17 +727,6 @@ void ElementaryReaction3::getParameters(AnyMap& reactionNode) const reactionNode["rate-constant"] = std::move(rateNode); } -void ElementaryReaction3::validate() -{ - Reaction::validate(); - if (!allow_negative_pre_exponential_factor && - std::dynamic_pointer_cast(m_rate)->preExponentialFactor() < 0) { - throw CanteraError("ElementaryReaction::validate", - "Undeclared negative pre-exponential factor found in reaction '" - + equation() + "'"); - } -} - ThreeBodyReaction3::ThreeBodyReaction3() : ElementaryReaction3() { @@ -900,18 +893,10 @@ bool PlogReaction3::setParameters(const AnyMap& node, const Kinetics& kin) if (!Reaction3::setParameters(node, kin)) { return false; } - setRate(std::shared_ptr(new PlogRate(node, rate_units))); return true; } -void PlogReaction3::validate() -{ - Reaction3::validate(); - //std::dynamic_pointer_cast(m_rate)->validate(equation()); - m_rate->validate(equation()); -} - CustomFunc1Reaction::CustomFunc1Reaction() : Reaction3() { diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 1a30ca24fbd..a8cfb237301 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -11,11 +11,15 @@ namespace Cantera { ArrheniusRate::ArrheniusRate() - : Arrhenius() { + : Arrhenius() + , allow_negative_pre_exponential_factor(false) +{ } ArrheniusRate::ArrheniusRate(double A, double b, double E) - : Arrhenius(A, b, E / GasConstant) { + : Arrhenius(A, b, E / GasConstant) + , allow_negative_pre_exponential_factor(false) +{ } ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rate_units) { @@ -23,6 +27,7 @@ ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rate_units) { } bool ArrheniusRate::setParameters(const AnyMap& node, const Units& rate_units) { + allow_negative_pre_exponential_factor = node.getBool("negative-A", false); if (!node.hasKey("rate-constant")) { return false; } @@ -35,6 +40,14 @@ void ArrheniusRate::getParameters(AnyMap& rateNode, Arrhenius::getParameters(rateNode, rate_units); } +void ArrheniusRate::validate(const std::string& equation) { + if (!allow_negative_pre_exponential_factor && preExponentialFactor() < 0) { + throw CanteraError("ArrheniusRate::validate", + "Undeclared negative pre-exponential factor found in reaction '" + + equation + "'"); + } +} + PlogRate::PlogRate() : Plog() { } From 220c1fc49e9489cd2d88de6e893a3a17f12bbc93 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 23 Mar 2021 20:17:56 -0500 Subject: [PATCH 35/84] [Kinetics] Simplify setParameters --- include/cantera/kinetics/Reaction.h | 95 +++++++++++++------------ include/cantera/kinetics/ReactionRate.h | 16 +++-- src/kinetics/Reaction.cpp | 41 +++-------- src/kinetics/ReactionRate.cpp | 3 +- 4 files changed, 69 insertions(+), 86 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index baf7c9ce694..1469a717180 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -152,45 +152,6 @@ class Reaction }; -//! An intermediate class used to avoid naming conflicts of 'rate' member -//! variables and getters (see `ElementaryReaction`, `PlogReaction` and -//! `ChebyshevReaction`). -class Reaction3 : public Reaction -{ -public: - Reaction3() : Reaction() {} - Reaction3(const Composition& reactants, const Composition& products); - - //! Get reaction rate pointer - shared_ptr rate() { - return m_rate; - } - - //! Set reaction rate pointer - void setRate(shared_ptr rate) { - m_rate = rate; - } - - //! Set up reaction based on AnyMap node - virtual bool setParameters(const AnyMap& node, const Kinetics& kin); - - //! Get pointer to third-body - shared_ptr thirdBody() { - return m_third_body; - } - - virtual void validate(); - -protected: - //! Reaction rate used by generic reactions - shared_ptr m_rate; - - //! Relative efficiencies of third-body species in enhancing the reaction - //! rate (if applicable) - shared_ptr m_third_body; -}; - - //! A reaction which follows mass-action kinetics with a modified Arrhenius //! reaction rate. class ElementaryReaction : public Reaction @@ -210,6 +171,7 @@ class ElementaryReaction : public Reaction bool allow_negative_pre_exponential_factor; }; + //! A class for managing third-body efficiencies, including default values class ThirdBody { @@ -232,6 +194,7 @@ class ThirdBody double default_efficiency; }; + //! A reaction with a non-reacting third body "M" that acts to add or remove //! energy from the reacting species class ThreeBodyReaction : public ElementaryReaction @@ -261,6 +224,7 @@ class ThreeBodyReaction : public ElementaryReaction undeclaredThirdBodies(const Kinetics& kin) const; }; + //! A reaction that is first-order in [M] at low pressure, like a third-body //! reaction, but zeroth-order in [M] as pressure increases. class FalloffReaction : public Reaction @@ -305,6 +269,7 @@ class FalloffReaction : public Reaction const Kinetics& kin) const; }; + //! A reaction where the rate decreases as pressure increases due to collisional //! stabilization of a reaction intermediate. Like a FalloffReaction, except //! that the forward rate constant is written as being proportional to the low- @@ -325,6 +290,7 @@ class ChemicallyActivatedReaction : public FalloffReaction virtual void getParameters(AnyMap& reactionNode) const; }; + //! A pressure-dependent reaction parameterized by logarithmically interpolating //! between Arrhenius rate expressions at various pressures. class PlogReaction : public Reaction @@ -344,6 +310,7 @@ class PlogReaction : public Reaction Plog rate; }; + //! A pressure-dependent reaction parameterized by a bi-variate Chebyshev //! polynomial in temperature and pressure class ChebyshevReaction : public Reaction @@ -361,6 +328,46 @@ class ChebyshevReaction : public Reaction ChebyshevRate rate; }; + +//! An intermediate class used to avoid naming conflicts of 'rate' member +//! variables and getters (see `ElementaryReaction`, `PlogReaction` and +//! `ChebyshevReaction`). +class Reaction3 : public Reaction +{ +public: + Reaction3() : Reaction() {} + Reaction3(const Composition& reactants, const Composition& products); + + //! Get reaction rate pointer + shared_ptr rate() { + return m_rate; + } + + //! Set reaction rate pointer + void setRate(shared_ptr rate) { + m_rate = rate; + } + + //! Set up reaction based on AnyMap *node* + virtual bool setParameters(const AnyMap& node, const Kinetics& kin); + + //! Get pointer to third-body + shared_ptr thirdBody() { + return m_third_body; + } + + virtual void validate(); + +protected: + //! Reaction rate used by generic reactions + shared_ptr m_rate; + + //! Relative efficiencies of third-body species in enhancing the reaction + //! rate (if applicable) + shared_ptr m_third_body; +}; + + //! A reaction which follows mass-action kinetics with a modified Arrhenius //! reaction rate. class ElementaryReaction3 : public Reaction3 @@ -375,8 +382,6 @@ class ElementaryReaction3 : public Reaction3 virtual std::string type() const { return "elementary"; } - - virtual bool setParameters(const AnyMap& node, const Kinetics& kin); virtual void getParameters(AnyMap& reactionNode) const; }; @@ -422,8 +427,6 @@ class PlogReaction3 : public Reaction3 virtual std::string type() const { return "pressure-dependent-Arrhenius"; } - - virtual bool setParameters(const AnyMap& node, const Kinetics& kin); virtual void getParameters(AnyMap& reactionNode) const; }; @@ -434,11 +437,11 @@ class CustomFunc1Reaction : public Reaction3 { public: CustomFunc1Reaction(); + CustomFunc1Reaction(const Composition& reactants, const Composition& products, + const CustomFunc1Rate& rate); CustomFunc1Reaction(const AnyMap& node, const Kinetics& kin); - virtual bool setParameters(const AnyMap& node, const Kinetics& kin); - virtual std::string type() const { return "custom-rate-function"; } @@ -463,8 +466,6 @@ class TestReaction : public Reaction3 virtual std::string type() const { return "elementary-new"; } - - virtual bool setParameters(const AnyMap& node, const Kinetics& kin); }; diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 112d60bbc45..66172d39e7d 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -40,10 +40,7 @@ class ReactionRateBase //! Set parameters //! @param node AnyMap object containing reaction rate specification //! @param rate_units Description of units used for rate parameters - virtual bool setParameters(const AnyMap& node, const Units& rate_units) { - throw CanteraError("ReactionRate::setParameters", - "Not implemented by derived ReactionRate object."); - } + virtual bool setParameters(const AnyMap& node, const Units& rate_units) = 0; //! Get parameters //! Store the parameters of a ReactionRate needed to reconstruct an identical @@ -262,7 +259,10 @@ class PlogRate final : public ReactionRate, public Plog /// @param rate_units unit definitions used for rate information PlogRate(const AnyMap& node, const Units& rate_units); - virtual bool setParameters(const AnyMap& node, const Units& rate_units) override; + virtual std::string type() const override { return "PlogRate"; } + + virtual bool setParameters(const AnyMap& node, + const Units& rate_units) override; virtual void getParameters(AnyMap& rateNode, const Units& rate_units) const override; @@ -273,8 +273,6 @@ class PlogRate final : public ReactionRate, public Plog update_C(shared_data.logP()); } - virtual std::string type() const override { return "PlogRate"; } - virtual double eval(const PlogData& shared_data, double concm=0.) const override { return updateRC(shared_data.m_logT, shared_data.m_recipT); @@ -304,6 +302,10 @@ class CustomFunc1Rate final : public ReactionRate virtual std::string type() const override { return "custom-function"; } + virtual bool setParameters(const AnyMap& node, const Units& rate_units) { + return true; + } + //! Update information specific to reaction static bool uses_update() { return false; } diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index f7ccca148cb..689a7ee463d 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -704,15 +704,7 @@ ElementaryReaction3::ElementaryReaction3(const AnyMap& node, const Kinetics& kin : ElementaryReaction3() { setParameters(node, kin); -} - -bool ElementaryReaction3::setParameters(const AnyMap& node, const Kinetics& kin) -{ - if (!Reaction3::setParameters(node, kin)) { - return false; - } setRate(std::shared_ptr(new ArrheniusRate(node, rate_units))); - return true; } void ElementaryReaction3::getParameters(AnyMap& reactionNode) const @@ -746,6 +738,7 @@ ThreeBodyReaction3::ThreeBodyReaction3(const AnyMap& node, const Kinetics& kin) : ThreeBodyReaction3() { setParameters(node, kin); + setRate(std::shared_ptr(new ArrheniusRate(node, rate_units))); } bool ThreeBodyReaction3::detectEfficiencies() @@ -877,6 +870,7 @@ PlogReaction3::PlogReaction3(const AnyMap& node, const Kinetics& kin) : PlogReaction3() { setParameters(node, kin); + setRate(std::shared_ptr(new PlogRate(node, rate_units))); } void PlogReaction3::getParameters(AnyMap& reactionNode) const @@ -888,32 +882,25 @@ void PlogReaction3::getParameters(AnyMap& reactionNode) const reactionNode.update(rateNode); } -bool PlogReaction3::setParameters(const AnyMap& node, const Kinetics& kin) -{ - if (!Reaction3::setParameters(node, kin)) { - return false; - } - setRate(std::shared_ptr(new PlogRate(node, rate_units))); - return true; -} - CustomFunc1Reaction::CustomFunc1Reaction() : Reaction3() { m_rate = std::shared_ptr(new CustomFunc1Rate); } -CustomFunc1Reaction::CustomFunc1Reaction(const AnyMap& node, const Kinetics& kin) - : CustomFunc1Reaction() +CustomFunc1Reaction::CustomFunc1Reaction(const Composition& reactants, + const Composition& products, + const CustomFunc1Rate& rate) + : Reaction3(reactants, products) { - setParameters(node, kin); + m_rate = std::make_shared(rate); } -bool CustomFunc1Reaction::setParameters(const AnyMap& node, const Kinetics& kin) +CustomFunc1Reaction::CustomFunc1Reaction(const AnyMap& node, const Kinetics& kin) + : CustomFunc1Reaction() { - bool ok = Reaction3::setParameters(node, kin); + setParameters(node, kin); setRate(std::shared_ptr(new CustomFunc1Rate(node, rate_units))); - return ok; } TestReaction::TestReaction() @@ -926,15 +913,7 @@ TestReaction::TestReaction(const AnyMap& node, const Kinetics& kin) : TestReaction() { setParameters(node, kin); -} - -bool TestReaction::setParameters(const AnyMap& node, const Kinetics& kin) -{ - if (!Reaction3::setParameters(node, kin)) { - return false; - } setRate(std::shared_ptr(new ArrheniusRate(node, rate_units))); - return true; } void ChebyshevReaction::getParameters(AnyMap& reactionNode) const diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index a8cfb237301..d5fd359e86f 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -56,7 +56,8 @@ PlogRate::PlogRate(const std::multimap& rates) : Plog(rates) { } -PlogRate::PlogRate(const AnyMap& node, const Units& rate_units) { +PlogRate::PlogRate(const AnyMap& node, const Units& rate_units) + : Plog() { setParameters(node, rate_units); } From 5f94dcd0a775b9ac543ea022c74c2f7ef4053784 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 23 Mar 2021 22:37:16 -0500 Subject: [PATCH 36/84] [CI] Update unit tests --- .../cython/cantera/test/test_kinetics.py | 3 +- test/kinetics/kineticsFromScratch3.cpp | 59 +++++++++++-------- test/kinetics/kineticsFromYaml.cpp | 24 +++++++- 3 files changed, 59 insertions(+), 27 deletions(-) diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 28af85b719f..a21399848ef 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -404,7 +404,8 @@ def test_modify_thermo(self): self.assertAlmostEqual(w2[i] / w1[i], 1.0) def test_pdep_err(self): - err_msg = ("Invalid rate coefficient for reaction 'CH2CHOO <=> CH3O + CO'", + err_msg = ("CanteraError thrown by Plog::validate:", + "Invalid rate coefficient for reaction 'CH2CHOO <=> CH3O + CO'", "at P = 32019, T = 500.0", "at P = 32019, T = 1000.0", "at P = 1.0132e+05, T = 500.0", diff --git a/test/kinetics/kineticsFromScratch3.cpp b/test/kinetics/kineticsFromScratch3.cpp index c69ac0ec998..0dc39336443 100644 --- a/test/kinetics/kineticsFromScratch3.cpp +++ b/test/kinetics/kineticsFromScratch3.cpp @@ -1,4 +1,5 @@ #include "gtest/gtest.h" +#include "cantera/base/Solution.h" #include "cantera/thermo/ThermoFactory.h" #include "cantera/thermo/SurfPhase.h" #include "cantera/thermo/Species.h" @@ -18,19 +19,19 @@ class KineticsFromScratch3 : public testing::Test std::string yaml_file = "../data/kineticsfromscratch.yaml"; std::string phase_name = "ohmech"; - p = std::shared_ptr(newPhase(yaml_file, phase_name)); + p = std::unique_ptr(newPhase(yaml_file, phase_name)); kin = std::shared_ptr(newKineticsMgr("GasKinetics")); kin->addPhase(*p.get()); - p_ref = std::shared_ptr(newPhase(yaml_file, phase_name)); - std::vector th; - th.push_back(p_ref.get()); - kin_ref = shared_ptr(newKinetics(th, yaml_file, phase_name)); + sol = newSolution(yaml_file, phase_name); + p_ref = sol->thermo(); + kin_ref = sol->kinetics(); } - std::shared_ptr p; - std::shared_ptr p_ref; + std::unique_ptr p; std::shared_ptr kin; + std::shared_ptr sol; + std::shared_ptr p_ref; std::shared_ptr kin_ref; //! iRef is the index of the corresponding reaction in the reference mech @@ -243,7 +244,8 @@ TEST_F(KineticsFromScratch3, allow_negative_A) Composition prod = parseCompString("H:1 OH:1"); ArrheniusRate rate(-3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); auto R = make_shared(reac, prod, rate); - R->allow_negative_pre_exponential_factor = true; + auto rr = std::dynamic_pointer_cast(R->rate()); + rr->allow_negative_pre_exponential_factor = true; kin->addReaction(R); ASSERT_EQ((size_t) 1, kin->nReactions()); @@ -400,29 +402,39 @@ TEST_F(InterfaceKineticsFromScratch3, unbalanced_sites) auto R = make_shared(reac, prod, rate); ASSERT_THROW(kin->addReaction(R), CanteraError); } +*/ -class KineticsAddSpecies2 : public testing::Test +class KineticsAddSpecies3 : public testing::Test { public: - KineticsAddSpecies2() - : p_ref("../data/kineticsfromscratch.cti") + KineticsAddSpecies3() { - std::vector th; - th.push_back(&p_ref); - importKinetics(p_ref->xml(), th, &kin_ref); - kin->addPhase(p); + std::string yaml_file = "../data/kineticsfromscratch.yaml"; + std::string phase_name = "ohmech"; + + p = std::shared_ptr(newThermoPhase("ideal-gas")); + kin = std::shared_ptr(newKineticsMgr("GasKinetics")); + kin->addPhase(*p.get()); - std::vector> S = getSpecies(*get_XML_File("h2o2.cti")); + AnyMap h2o2 = AnyMap::fromYamlFile("h2o2.yaml"); + std::vector> S = getSpecies(h2o2["species"]); for (auto sp : S) { species[sp->name] = sp; } - reactions = getReactions(*get_XML_File("../data/kineticsfromscratch.cti")); + + sol = newSolution(yaml_file, phase_name); + p_ref = sol->thermo(); + kin_ref = sol->kinetics(); + + AnyMap root = AnyMap::fromYamlFile(yaml_file); + reactions = getReactions(root["reactions"], *kin_ref); } - IdealGasPhase p; - IdealGasPhase p_ref; - GasKinetics kin; - GasKinetics kin_ref; + std::shared_ptr sol; + std::shared_ptr p; + std::shared_ptr kin; + std::shared_ptr p_ref; + std::shared_ptr kin_ref; std::vector> reactions; std::map> species; @@ -473,7 +485,7 @@ class KineticsAddSpecies2 : public testing::Test } }; -TEST_F(KineticsAddSpecies2, add_species_sequential) +TEST_F(KineticsAddSpecies3, add_species_sequential) { ASSERT_EQ((size_t) 0, kin->nReactions()); @@ -502,7 +514,7 @@ TEST_F(KineticsAddSpecies2, add_species_sequential) check_rates(5, "O:0.01, H2:0.1, H:0.02, OH:0.03, O2:0.4, AR:0.3, H2O2:0.03, HO2:0.01"); } -TEST_F(KineticsAddSpecies2, add_species_err_first) +TEST_F(KineticsAddSpecies3, add_species_err_first) { for (auto s : {"AR", "O", "H2", "H"}) { p->addSpecies(species[s]); @@ -516,4 +528,3 @@ TEST_F(KineticsAddSpecies2, add_species_err_first) ASSERT_EQ((size_t) 1, kin->nReactions()); check_rates(1, "O:0.001, H2:0.1, H:0.005, OH:0.02, AR:0.88"); } -*/ diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index a6b5e985835..cb626755f08 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -10,6 +10,25 @@ using namespace Cantera; +TEST(ReactionRate, ModifyArrheniusRate) +{ + auto sol = newSolution("gri30.yaml"); + AnyMap rxn = AnyMap::fromYamlString( + "{equation: N + NO <=> N2 + O," + " rate-constant: [-2.70000E+13 cm^3/mol/s, 0, 355 cal/mol]," + " negative-A: true}"); + + auto R = newReaction(rxn, *(sol->kinetics())); + auto ER = dynamic_cast(*R); + + auto rr0 = std::dynamic_pointer_cast(ER.rate()); + EXPECT_TRUE(rr0->allow_negative_pre_exponential_factor); + rr0->allow_negative_pre_exponential_factor = false; + + auto rr1 = std::dynamic_pointer_cast(ER.rate()); + EXPECT_FALSE(rr1->allow_negative_pre_exponential_factor); +} + TEST(Reaction, ElementaryFromYaml1) { auto sol = newSolution("gri30.yaml", "", "None"); @@ -24,7 +43,8 @@ TEST(Reaction, ElementaryFromYaml1) EXPECT_EQ(R->type(), "elementary"); auto ER = dynamic_cast(*R); - EXPECT_TRUE(ER.allow_negative_pre_exponential_factor); + auto rr = std::dynamic_pointer_cast(ER.rate()); + EXPECT_TRUE(rr->allow_negative_pre_exponential_factor); EXPECT_FALSE(ER.allow_negative_orders); const auto& rate = std::dynamic_pointer_cast(ER.rate()); @@ -178,7 +198,7 @@ TEST(Reaction, PlogFromYaml) auto R = newReaction(rxn, *(sol->kinetics())); auto PR = dynamic_cast(*R); - const auto& rates = dynamic_cast(*PR.rate()).rates(); + const auto& rates = std::dynamic_pointer_cast(PR.rate())->rates(); EXPECT_EQ(rates.size(), (size_t) 4); EXPECT_NEAR(rates[0].first, 0.039474 * OneAtm, 1e-6); EXPECT_NEAR(rates[2].first, OneAtm, 1e-6); From 394697da10f776671eabffe5b322b9fabfc5c588 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 24 Mar 2021 08:14:12 -0500 Subject: [PATCH 37/84] [Kinetics] Implement ChebyshevRate3 wrapper for ChebyshevRate --- include/cantera/kinetics/ReactionData.h | 37 ++++++++++++ include/cantera/kinetics/ReactionRate.h | 75 ++++++++++++++++++++++++- include/cantera/kinetics/RxnRates.h | 8 +++ src/kinetics/ReactionData.cpp | 14 +++++ src/kinetics/ReactionRate.cpp | 32 +++++++++++ src/kinetics/RxnRates.cpp | 36 ++++++++++++ 6 files changed, 201 insertions(+), 1 deletion(-) diff --git a/include/cantera/kinetics/ReactionData.h b/include/cantera/kinetics/ReactionData.h index fe706d2c02d..e013a273191 100644 --- a/include/cantera/kinetics/ReactionData.h +++ b/include/cantera/kinetics/ReactionData.h @@ -87,6 +87,43 @@ struct PlogData }; +//! Data container holding shared data specific to ChebyshevRate +/** + * The data container `ChebyshevData` holds precalculated data common to + * all `ChebyshevRate3` objects. + */ +struct ChebyshevData +{ + ChebyshevData() : m_temperature(1.), m_recipT(1.), m_log10P(0.) {} + + //! Constructor based on temperature *T* and pressure *P* + ChebyshevData(double T); + + //! Constructor based on temperature *T* and pressure *P* + ChebyshevData(double T, double P) { update(T, P); }; + + //! Constructor accessing *bulk* phase definitions + ChebyshevData(const ThermoPhase& bulk) { update(bulk); } + + void update(double T); + + void update(double T, double P) { + m_temperature = T; + m_recipT = 1./T; + m_log10P = std::log10(P); + } + + void update(const ThermoPhase& bulk); + + //! Pointer to logP (required by Chebyshev::update_C) + const double* log10P() const { return &m_log10P; } + + double m_temperature; //!< temperature + double m_recipT; //!< inverse of temperature + double m_log10P; //!< base 10 logarithm of pressure +}; + + //! Data container holding shared data specific to CustomFunc1Rate struct CustomFunc1Data { diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 66172d39e7d..4c1e764455c 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -279,12 +279,85 @@ class PlogRate final : public ReactionRate, public Plog } virtual void validate(const std::string& equation) { - // changing to const so non-virtual base version can be used Plog::validate(equation); } }; +//! Pressure-dependent rate expression where the rate coefficient is expressed +//! as a bivariate Chebyshev polynomial in temperature and pressure. +/*! + * The rate constant can be written as: + * \f[ + * \log k(T,P) = \sum_{t=1}^{N_T} \sum_{p=1}^{N_P} \alpha_{tp} + * \phi_t(\tilde{T}) \phi_p(\tilde{P}) + * \f] + * where \f$\alpha_{tp}\f$ are the constants defining the rate, \f$\phi_n(x)\f$ + * is the Chebyshev polynomial of the first kind of degree *n* evaluated at + * *x*, and + * \f[ + * \tilde{T} \equiv \frac{2T^{-1} - T_\mathrm{min}^{-1} - T_\mathrm{max}^{-1}} + * {T_\mathrm{max}^{-1} - T_\mathrm{min}^{-1}} + * \f] + * \f[ + * \tilde{P} \equiv \frac{2 \log P - \log P_\mathrm{min} - \log P_\mathrm{max}} + * {\log P_\mathrm{max} - \log P_\mathrm{min}} + * \f] + * are reduced temperature and reduced pressures which map the ranges + * \f$ (T_\mathrm{min}, T_\mathrm{max}) \f$ and + * \f$ (P_\mathrm{min}, P_\mathrm{max}) \f$ to (-1, 1). + * + * A Chebyshev rate expression is specified in terms of the coefficient matrix + * \f$ \alpha \f$ and the temperature and pressure ranges. Note that the + * Chebyshev polynomials are not defined outside the interval (-1,1), and + * therefore extrapolation of rates outside the range of temperatures and + * pressures for which they are defined is strongly discouraged. + */ +class ChebyshevRate3 final : public ReactionRate, public ChebyshevRate +{ +public: + //! Default constructor. + ChebyshevRate3(); + + //! Constructor directly from coefficient array + /* + * @param Tmin Minimum temperature [K] + * @param Tmax Maximum temperature [K] + * @param Pmin Minimum pressure [Pa] + * @param Pmax Maximum pressure [Pa] + * @param coeffs Coefficient array dimensioned `nT` by `nP` where `nT` and + * `nP` are the number of temperatures and pressures used in the fit, + * respectively. + */ + ChebyshevRate3(double Tmin, double Tmax, double Pmin, double Pmax, + const Array2D& coeffs); + + //! Constructor using AnyMap content + /// @param node AnyMap containing rate information + /// @param rate_units unit definitions used for rate information + ChebyshevRate3(const AnyMap& node, const Units& rate_units); + + virtual std::string type() const override { return "ChebyshevRate"; } + + virtual bool setParameters(const AnyMap& node, + const Units& rate_units) override; + + //! Update information specific to reaction + static bool uses_update() { return true; } + + virtual void update(const ChebyshevData& shared_data, double concm=0.) override { + update_C(shared_data.log10P()); + } + + virtual double eval(const ChebyshevData& shared_data, + double concm=0.) const override { + return updateRC(0., shared_data.m_recipT); + } + + virtual void validate(const std::string& equation); +}; + + //! Custom reaction rate depending only on temperature /** * The rate expression is provided by a `Func1` object taking a single diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 66eced14056..47ae421d2ef 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -472,6 +472,14 @@ class ChebyshevRate ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, const Array2D& coeffs); + //! Run object setup based on AnyMap node information + void setParameters(const AnyMap& node, + const UnitSystem& units, const Units& rate_units); + + //! Set up Chebyshev object + void setup(double Tmin, double Tmax, double Pmin, double Pmax, + const Array2D& coeffs); + //! Update concentration-dependent parts of the rate coefficient. //! @param c base-10 logarithm of the pressure in Pa void update_C(const doublereal* c) { diff --git a/src/kinetics/ReactionData.cpp b/src/kinetics/ReactionData.cpp index 413f97396cd..50fde1bd0cb 100644 --- a/src/kinetics/ReactionData.cpp +++ b/src/kinetics/ReactionData.cpp @@ -28,6 +28,20 @@ void PlogData::update(const ThermoPhase& bulk) { update(bulk.temperature(), bulk.pressure()); } +ChebyshevData::ChebyshevData(double T) { + throw CanteraError("ChebyshevData::ChebyshevData", + "Missing state information: reaction type requires pressure."); +} + +void ChebyshevData::update(double T) { + throw CanteraError("ChebyshevData::update", + "Missing state information: reaction type requires pressure."); +} + +void ChebyshevData::update(const ThermoPhase& bulk) { + update(bulk.temperature(), bulk.pressure()); +} + void CustomFunc1Data::update(const ThermoPhase& bulk) { m_temperature = bulk.temperature(); } diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index d5fd359e86f..8e37bb73f02 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -75,6 +75,38 @@ void PlogRate::getParameters(AnyMap& rateNode, Plog::getParameters(rateNode, rate_units); } +ChebyshevRate3::ChebyshevRate3() + : ChebyshevRate() { +} + +ChebyshevRate3::ChebyshevRate3(double Tmin, double Tmax, double Pmin, double Pmax, + const Array2D& coeffs) + : ChebyshevRate(Tmin, Tmax, Pmin, Pmax, coeffs) { +} + +ChebyshevRate3::ChebyshevRate3(const AnyMap& node, const Units& rate_units) + : ChebyshevRate() { + setParameters(node, rate_units); +} + +bool ChebyshevRate3::setParameters(const AnyMap& node, const Units& rate_units) { + if (!node.hasKey("rate-constants")) { + return false; + } + ChebyshevRate::setParameters(node.at("rate-constants").asVector(), + node.units(), rate_units); + return true; +} + +void ChebyshevRate3::getParameters(AnyMap& rateNode, + const Units& rate_units) const { + throw CanteraError("ChebyshevRate3::getParameters", + "@todo"); +} + +void ChebyshevRate3::validate(const std::string& equation) { +} + CustomFunc1Rate::CustomFunc1Rate() : m_ratefunc(0) {} void CustomFunc1Rate::setRateFunction(shared_ptr f) { diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 977f3268b03..59f18b8f02e 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -258,6 +258,42 @@ ChebyshevRate::ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, , nT_(coeffs.nRows()) , chebCoeffs_(coeffs.nColumns() * coeffs.nRows(), 0.0) , dotProd_(coeffs.nRows()) +{ + setup(Tmin, Tmax, Pmin, Pmax, coeffs); +} + +void ChebyshevRate::setParameters(const AnyMap& node, + const UnitSystem& units, const Units& rate_units) +{ + const auto& T_range = node["temperature-range"].asVector(2); + const auto& P_range = node["pressure-range"].asVector(2); + auto& vcoeffs = node["data"].asVector(); + Array2D coeffs(vcoeffs.size(), vcoeffs[0].size()); + for (size_t i = 0; i < coeffs.nRows(); i++) { + if (vcoeffs[i].size() != vcoeffs[0].size()) { + throw InputFileError("ChebyshevRate::setParameters", node["data"], + "Inconsistent number of coefficients in row {} of matrix", i + 1); + } + for (size_t j = 0; j < coeffs.nColumns(); j++) { + coeffs(i, j) = vcoeffs[i][j]; + } + } + coeffs(0, 0) += std::log10(units.convert(1.0, rate_units)); + + Tmin_ = units.convert(T_range[0], "K"); + Tmax_ = units.convert(T_range[1], "K"); + Pmin_ = units.convert(P_range[0], "Pa"); + Pmax_ = units.convert(P_range[1], "Pa"); + nP_ = coeffs.nColumns(); + nT_ = coeffs.nRows(); + chebCoeffs_.resize(nP_ * nT_); + dotProd_.resize(nT_); + + setup(Tmin_, Tmax_, Pmin_, Pmax_, coeffs); +} + +void ChebyshevRate::setup(double Tmin, double Tmax, double Pmin, double Pmax, + const Array2D& coeffs) { double logPmin = std::log10(Pmin); double logPmax = std::log10(Pmax); From 4c5d1a860a4603b4b702f160851c0035ff495826 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 24 Mar 2021 09:25:20 -0500 Subject: [PATCH 38/84] [Kinetics] Implement ChebyshevReaction3 --- include/cantera/kinetics/Reaction.h | 19 +++++++++ src/kinetics/BulkKinetics.cpp | 3 ++ src/kinetics/Reaction.cpp | 53 ++++++++++++++++++++++++++ src/kinetics/ReactionFactory.cpp | 3 ++ src/kinetics/ReactionRate.cpp | 5 +-- test/kinetics/kineticsFromScratch3.cpp | 6 +-- 6 files changed, 82 insertions(+), 7 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 1469a717180..8773afe2f75 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -431,6 +431,25 @@ class PlogReaction3 : public Reaction3 }; +//! A pressure-dependent reaction parameterized by a bi-variate Chebyshev +//! polynomial in temperature and pressure +class ChebyshevReaction3 : public Reaction3 +{ +public: + ChebyshevReaction3(); + ChebyshevReaction3(const Composition& reactants, const Composition& products, + const ChebyshevRate3& rate); + + ChebyshevReaction3(const AnyMap& node, const Kinetics& kin); + + virtual std::string type() const { + return "Chebyshev-new"; + } + + virtual bool setParameters(const AnyMap& node, const Kinetics& kin); +}; + + //! A reaction which follows mass-action kinetics with a custom reaction rate //! defined in Python. class CustomFunc1Reaction : public Reaction3 diff --git a/src/kinetics/BulkKinetics.cpp b/src/kinetics/BulkKinetics.cpp index b6c98d1db20..ff199a727d0 100644 --- a/src/kinetics/BulkKinetics.cpp +++ b/src/kinetics/BulkKinetics.cpp @@ -135,6 +135,9 @@ bool BulkKinetics::addReaction(shared_ptr r) } else if (rate->type() == "PlogRate") { m_bulk_rates.push_back(std::unique_ptr( new MultiBulkRates)); + } else if (rate->type() == "ChebyshevRate") { + 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)); diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 689a7ee463d..f10d29f64d1 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -882,6 +882,59 @@ void PlogReaction3::getParameters(AnyMap& reactionNode) const reactionNode.update(rateNode); } +ChebyshevReaction3::ChebyshevReaction3() + : Reaction3() +{ + m_rate = std::shared_ptr(new ChebyshevRate3); +} + +ChebyshevReaction3::ChebyshevReaction3(const Composition& reactants, + const Composition& products, + const ChebyshevRate3& rate) + : Reaction3(reactants, products) +{ + m_rate = std::make_shared(rate); +} + +ChebyshevReaction3::ChebyshevReaction3(const AnyMap& node, const Kinetics& kin) + : ChebyshevReaction3() +{ + setParameters(node, kin); +} + +bool ChebyshevReaction3::setParameters(const AnyMap& node, const Kinetics& kin) +{ + if (!node.hasKey("equation")) { + // empty node: used by newReaction() factory loader + return false; + } + + parseReactionEquation(*this, node["equation"], kin); + // Non-stoichiometric reaction 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); + } + } + } + + reactants.erase("(+M)"); // remove optional third body notation + products.erase("(+M)"); + + // 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; + + setRate(std::shared_ptr(new ChebyshevRate3(node, rate_units))); + return true +} + CustomFunc1Reaction::CustomFunc1Reaction() : Reaction3() { diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index e15bb9c9961..225595b7ce9 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -90,6 +90,9 @@ ReactionFactory::ReactionFactory() }); // register Chebyshev reactions + reg("Chebyshev-new", [](const AnyMap& node, const Kinetics& kin) { + return new ChebyshevReaction3(node, kin); + }); reg("Chebyshev", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ChebyshevReaction(); if (node.hasKey("equation")) { diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 8e37bb73f02..0aec27e9e3b 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -90,11 +90,10 @@ ChebyshevRate3::ChebyshevRate3(const AnyMap& node, const Units& rate_units) } bool ChebyshevRate3::setParameters(const AnyMap& node, const Units& rate_units) { - if (!node.hasKey("rate-constants")) { + if (!node.hasKey("data")) { return false; } - ChebyshevRate::setParameters(node.at("rate-constants").asVector(), - node.units(), rate_units); + ChebyshevRate::setParameters(node, node.units(), rate_units); return true; } diff --git a/test/kinetics/kineticsFromScratch3.cpp b/test/kinetics/kineticsFromScratch3.cpp index 0dc39336443..0e9f71c0acb 100644 --- a/test/kinetics/kineticsFromScratch3.cpp +++ b/test/kinetics/kineticsFromScratch3.cpp @@ -170,7 +170,6 @@ TEST_F(KineticsFromScratch3, plog_invalid_rate) ASSERT_THROW(kin->addReaction(R), CanteraError); } -/* TEST_F(KineticsFromScratch3, add_chebyshev_reaction) { // reaction 4: @@ -196,13 +195,12 @@ TEST_F(KineticsFromScratch3, add_chebyshev_reaction) coeffs(2,1) = 2.6889e-01; coeffs(2,2) = 9.4806e-02; coeffs(2,3) = -7.6385e-03; - ChebyshevRate rate(290, 3000, 1000.0, 10000000.0, coeffs); + ChebyshevRate3 rate(290, 3000, 1000.0, 10000000.0, coeffs); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); kin->addReaction(R); check_rates(4); } -*/ TEST_F(KineticsFromScratch3, undeclared_species) { From 985a52dcac671d1baee4843b5dafc70a3b2cf59c Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 24 Mar 2021 10:52:42 -0500 Subject: [PATCH 39/84] [Kinetics] Expose ChebyshevReaction3 to Python --- interfaces/cython/cantera/_cantera.pxd | 21 +++ interfaces/cython/cantera/reaction.pyx | 137 ++++++++++++++++++ .../cython/cantera/test/test_reaction.py | 24 ++- src/kinetics/Reaction.cpp | 1 + src/kinetics/ReactionRate.cpp | 10 +- 5 files changed, 188 insertions(+), 5 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 2bf37c9f8c8..25796c18c43 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -380,6 +380,19 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": CxxPlogRate(multimap[double, CxxArrhenius]) vector[pair[double, CxxArrhenius]] rates() + cdef cppclass CxxChebyshevRate3 "Cantera::ChebyshevRate3" (CxxReactionRateBase): + CxxChebyshevRate3() + CxxChebyshevRate3(double, double, double, double, CxxArray2D) + double Tmin() + double Tmax() + double Pmin() + double Pmax() + size_t nPressure() + size_t nTemperature() + vector[double]& coeffs() + void update_C(double*) + double updateRC(double, double) + cdef cppclass CxxReaction "Cantera::Reaction": CxxReaction() @@ -477,6 +490,9 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxPlogReaction3 "Cantera::PlogReaction3" (CxxReaction3): CxxPlogReaction3() + cdef cppclass CxxChebyshevReaction3 "Cantera::ChebyshevReaction3" (CxxReaction3): + CxxChebyshevReaction3() + cdef cppclass CxxCustomFunc1Reaction "Cantera::CustomFunc1Reaction" (CxxReaction3): CxxCustomFunc1Reaction() @@ -1148,6 +1164,11 @@ cdef class PlogRate(_ReactionRate): @staticmethod cdef wrap(shared_ptr[CxxReactionRateBase]) +cdef class ChebyshevRate(_ReactionRate): + cdef CxxChebyshevRate3* rate + @staticmethod + cdef wrap(shared_ptr[CxxReactionRateBase]) + cdef class Reaction: cdef shared_ptr[CxxReaction] _reaction cdef CxxReaction* reaction diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 2b872c89e99..54d0bdb5ee4 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -587,6 +587,84 @@ cdef class PlogRate(_ReactionRate): self.rate = (self.base) +cdef class ChebyshevRate(_ReactionRate): + r""" + """ + def __cinit__(self, Tmin=None, Tmax=None, Pmin=None, Pmax=None, data=None, init=True): + + if Tmin and Tmax and Pmin and Pmax and data and init: + self._setup(Tmin, Tmax, Pmin, Pmax, data) + + def _setup(self, Tmin, Tmax, Pmin, Pmax, coeffs): + """ + Simultaneously set values for `Tmin`, `Tmax`, `Pmin`, `Pmax`, and + `coeffs`. + """ + cdef CxxArray2D data + data.resize(len(coeffs), len(coeffs[0])) + cdef double value + cdef int i + cdef int j + for i,row in enumerate(coeffs): + for j,value in enumerate(row): + CxxArray2D_set(data, i, j, value) + + self._base.reset(new CxxChebyshevRate3(Tmin, Tmax, Pmin, Pmax, data)) + self.base = self._base.get() + self.rate = (self.base) + + @staticmethod + cdef wrap(shared_ptr[CxxReactionRateBase] rate): + """ + Wrap a C++ ReactionRateBase object with a Python object. + """ + # wrap C++ reaction + cdef ChebyshevRate arr + arr = ChebyshevRate(init=False) + arr._base = rate + arr.base = arr._base.get() + arr.rate = (arr.base) + return arr + + property Tmin: + """ Minimum temperature [K] for the Chebyshev fit """ + def __get__(self): + return self.rate.Tmin() + + property Tmax: + """ Maximum temperature [K] for the Chebyshev fit """ + def __get__(self): + return self.rate.Tmax() + + property Pmin: + """ Minimum pressure [Pa] for the Chebyshev fit """ + def __get__(self): + return self.rate.Pmin() + + property Pmax: + """ Maximum pressure [Pa] for the Chebyshev fit """ + def __get__(self): + return self.rate.Pmax() + + property nPressure: + """ Number of pressures over which the Chebyshev fit is computed """ + def __get__(self): + return self.rate.nPressure() + + property nTemperature: + """ Number of temperatures over which the Chebyshev fit is computed """ + def __get__(self): + return self.rate.nTemperature() + + property coeffs: + """ + 2D array of Chebyshev coefficients of size `(nTemperature, nPressure)`. + """ + def __get__(self): + c = np.fromiter(self.rate.coeffs(), np.double) + return c.reshape((self.rate.nTemperature(), self.rate.nPressure())) + + cdef class ElementaryReaction(Reaction): """ A reaction which follows mass-action kinetics with a modified Arrhenius @@ -1351,6 +1429,65 @@ cdef class PlogReaction3(Reaction): return r.rate.updateRC(logT, recipT) +cdef class ChebyshevReaction3(Reaction): + """ + A pressure-dependent reaction parameterized by a bivariate Chebyshev + polynomial in temperature and pressure. + """ + reaction_type = "Chebyshev-new" + + cdef CxxChebyshevReaction3* cr(self): + return self.reaction + + cdef CxxChebyshevRate3* crr(self): + return (self.cr().rate().get()) + + def __init__(self, equation=None, rate=None, Kinetics kinetics=None, + init=True, **kwargs): + + if init and equation and kinetics: + + spec = {'equation': equation, 'type': self.reaction_type} + if isinstance(rate, dict): + spec['temperature-range'] = [rate['Tmin'], rate['Tmax']] + spec['pressure-range'] = [rate['Pmin'], rate['Pmax']] + spec['data'] = rate['data'] + elif isinstance(rate, ChebyshevRate) or rate is None: + pass + else: + raise TypeError("Invalid rate definition") + + self._reaction = CxxNewReaction(dict_to_anymap(spec), + deref(kinetics.kinetics)) + self.reaction = self._reaction.get() + + if isinstance(rate, ChebyshevRate): + self.rate = rate + + property rate: + """ Get/Set the `Chebyshev` rate coefficients for this reaction. """ + def __get__(self): + return ChebyshevRate.wrap(self.cr().rate()) + def __set__(self, ChebyshevRate rate): + self.cr().setRate(rate._base) + + def set_parameters(self, Tmin, Tmax, Pmin, Pmax, coeffs): + """ + Simultaneously set values for `Tmin`, `Tmax`, `Pmin`, `Pmax`, and + `coeffs`. + """ + self.rate = self.ChebyshevRate(Tmin, Tmax, Pmin, Pmax, coeffs) + + def __call__(self, float T, float P): + cdef CxxChebyshevReaction3* r = self.reaction + cdef double logT = np.log(T) + cdef double recipT = 1/T + cdef double logP = np.log10(P) + + self.crr().update_C(&logP) + return self.crr().updateRC(logT, recipT) + + cdef class CustomReaction(Reaction): """ A reaction which follows mass-action kinetics with a custom reaction rate. diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index a6b6064ca33..c44509aed8c 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -150,7 +150,7 @@ def check_sol(self, gas2): def test_rate(self): if self._rate_obj is None: return - if type(self._rate_obj).__name__.endswith('Rate'): + if 'Rate' in type(self._rate_obj).__name__: self.assertNear(self._rate_obj(self.gas.T, self.gas.P), self.gas.forward_rate_constants[self._index]) else: @@ -203,7 +203,7 @@ def test_no_rate(self): if self._cls is None or not hasattr(self._cls, 'rate'): return rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) - if type(self._rate_obj).__name__.endswith('Rate'): + if 'Rate' in type(self._rate_obj).__name__: self.assertNear(rxn.rate(self.gas.T, self.gas.P), 0.) else: self.assertNear(rxn.rate(self.gas.T), 0.) @@ -397,3 +397,23 @@ class TestChebyshev(TestReaction): - [1.9764, 1.0037, 7.2865e-03, -0.030432] - [0.3177, 0.26889, 0.094806, -7.6385e-03] """ + + +class TestChebyshev3(TestChebyshev): + + _cls = ct.ChebyshevReaction3 + _rate_obj = ct.ChebyshevRate(Tmin=290., Tmax=3000., Pmin=1000., Pmax=10000000.0, + data=[[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], + [ 1.9764e+00, 1.0037e+00, 7.2865e-03, -3.0432e-02], + [ 3.1770e-01, 2.6889e-01, 9.4806e-02, -7.6385e-03]]) + _type = "Chebyshev-new" + _yaml = """ + equation: HO2 <=> OH + O # Reaction 5 + type: Chebyshev-new + temperature-range: [290.0, 3000.0] + pressure-range: [9.869232667160128e-03 atm, 98.69232667160128 atm] + data: + - [8.2883, -1.1397, -0.12059, 0.016034] + - [1.9764, 1.0037, 7.2865e-03, -0.030432] + - [0.3177, 0.26889, 0.094806, -7.6385e-03] + """ diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index f10d29f64d1..a63ec85045a 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -929,6 +929,7 @@ bool ChebyshevReaction3::setParameters(const AnyMap& node, const Kinetics& kin) allow_negative_orders = node.getBool("negative-orders", false); allow_nonreactant_orders = node.getBool("nonreactant-orders", false); + calculateRateCoeffUnits(kin); input = node; setRate(std::shared_ptr(new ChebyshevRate3(node, rate_units))); diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 0aec27e9e3b..afa47260a5d 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -91,7 +91,12 @@ ChebyshevRate3::ChebyshevRate3(const AnyMap& node, const Units& rate_units) bool ChebyshevRate3::setParameters(const AnyMap& node, const Units& rate_units) { if (!node.hasKey("data")) { - return false; + // ensure that Chebyshev has defined state and produces zero reaction rate + AnyMap rate = AnyMap::fromYamlString( + "temperature-range: [290, 3000]\n" + "pressure-range: [1.e-7, 1.e7]\n" + "data: [[-16.]]\n"); + Chebyshev::setParameters(rate, node.units(), rate_units); return false; } ChebyshevRate::setParameters(node, node.units(), rate_units); return true; @@ -99,8 +104,7 @@ bool ChebyshevRate3::setParameters(const AnyMap& node, const Units& rate_units) void ChebyshevRate3::getParameters(AnyMap& rateNode, const Units& rate_units) const { - throw CanteraError("ChebyshevRate3::getParameters", - "@todo"); + Chebyshev::getParameters(rateNode, rate_units); } void ChebyshevRate3::validate(const std::string& equation) { From 275144c5045229d8c317738b5afd1a6e368b4a0e Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 24 Mar 2021 11:09:50 -0500 Subject: [PATCH 40/84] [Kinetics] Make ChebyshevReaction3 default for YAML import --- include/cantera/kinetics/Reaction.h | 4 ++-- interfaces/cython/cantera/reaction.pyx | 4 ++-- .../cython/cantera/test/test_reaction.py | 8 ++++---- src/kinetics/GasKinetics.cpp | 4 ++-- src/kinetics/ReactionFactory.cpp | 10 +++++----- test/kinetics/kineticsFromYaml.cpp | 19 ++++++++++--------- 6 files changed, 25 insertions(+), 24 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 8773afe2f75..77fb2af78f1 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -322,7 +322,7 @@ class ChebyshevReaction : public Reaction virtual void getParameters(AnyMap& reactionNode) const; virtual std::string type() const { - return "Chebyshev"; + return "Chebyshev-old"; } ChebyshevRate rate; @@ -443,7 +443,7 @@ class ChebyshevReaction3 : public Reaction3 ChebyshevReaction3(const AnyMap& node, const Kinetics& kin); virtual std::string type() const { - return "Chebyshev-new"; + return "Chebyshev"; } virtual bool setParameters(const AnyMap& node, const Kinetics& kin); diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 54d0bdb5ee4..201846ef278 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -1034,7 +1034,7 @@ cdef class ChebyshevReaction(Reaction): A pressure-dependent reaction parameterized by a bivariate Chebyshev polynomial in temperature and pressure. """ - reaction_type = "Chebyshev" + reaction_type = "Chebyshev-old" def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): @@ -1434,7 +1434,7 @@ cdef class ChebyshevReaction3(Reaction): A pressure-dependent reaction parameterized by a bivariate Chebyshev polynomial in temperature and pressure. """ - reaction_type = "Chebyshev-new" + reaction_type = "Chebyshev" cdef CxxChebyshevReaction3* cr(self): return self.reaction diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index c44509aed8c..8e3d3845f7b 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -385,11 +385,11 @@ class TestChebyshev(TestReaction): 'data': [[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], [ 1.9764e+00, 1.0037e+00, 7.2865e-03, -3.0432e-02], [ 3.1770e-01, 2.6889e-01, 9.4806e-02, -7.6385e-03]]} - _type = "Chebyshev" + _type = "Chebyshev-old" _index = 4 _yaml = """ equation: HO2 <=> OH + O # Reaction 5 - type: Chebyshev + type: Chebyshev-old temperature-range: [290.0, 3000.0] pressure-range: [9.869232667160128e-03 atm, 98.69232667160128 atm] data: @@ -406,10 +406,10 @@ class TestChebyshev3(TestChebyshev): data=[[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], [ 1.9764e+00, 1.0037e+00, 7.2865e-03, -3.0432e-02], [ 3.1770e-01, 2.6889e-01, 9.4806e-02, -7.6385e-03]]) - _type = "Chebyshev-new" + _type = "Chebyshev" _yaml = """ equation: HO2 <=> OH + O # Reaction 5 - type: Chebyshev-new + type: Chebyshev temperature-range: [290.0, 3000.0] pressure-range: [9.869232667160128e-03 atm, 98.69232667160128 atm] data: diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index f92605cfcb7..a17bc38b494 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -276,7 +276,7 @@ bool GasKinetics::addReaction(shared_ptr r) addFalloffReaction(dynamic_cast(*r)); } else if (r->type() == "pressure-dependent-Arrhenius-old") { addPlogReaction(dynamic_cast(*r)); - } else if (r->type() == "Chebyshev") { + } else if (r->type() == "Chebyshev-old") { addChebyshevReaction(dynamic_cast(*r)); } else if (r->type() == "Blowers-Masel") { addBlowersMaselReaction(dynamic_cast(*r)); @@ -368,7 +368,7 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) modifyFalloffReaction(i, dynamic_cast(*rNew)); } else if (rNew->type() == "pressure-dependent-Arrhenius-old") { modifyPlogReaction(i, dynamic_cast(*rNew)); - } else if (rNew->type() == "Chebyshev") { + } else if (rNew->type() == "Chebyshev-old") { modifyChebyshevReaction(i, dynamic_cast(*rNew)); } else if (rNew->type() == "Blowers-Masel") { modifyBlowersMaselReaction(i, dynamic_cast(*rNew)); diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 225595b7ce9..88733574304 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -90,17 +90,17 @@ ReactionFactory::ReactionFactory() }); // register Chebyshev reactions - reg("Chebyshev-new", [](const AnyMap& node, const Kinetics& kin) { + reg("Chebyshev", [](const AnyMap& node, const Kinetics& kin) { return new ChebyshevReaction3(node, kin); }); - reg("Chebyshev", [](const AnyMap& node, const Kinetics& kin) { + addAlias("Chebyshev", "chebyshev"); + reg("Chebyshev-old", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ChebyshevReaction(); if (node.hasKey("equation")) { setupChebyshevReaction(*(ChebyshevReaction*)R, node, kin); } return R; }); - addAlias("Chebyshev", "chebyshev"); // register custom reactions specified by Func1 objects reg("custom-rate-function", [](const AnyMap& node, const Kinetics& kin) { @@ -203,12 +203,12 @@ ReactionFactoryXML::ReactionFactoryXML() addAlias("pressure-dependent-Arrhenius-old", "pdep_arrhenius"); // register Chebyshev reactions - reg("Chebyshev", [](const XML_Node& node) { + reg("Chebyshev-old", [](const XML_Node& node) { Reaction* R = new ChebyshevReaction(); setupChebyshevReaction(*(ChebyshevReaction*)R, node); return R; }); - addAlias("Chebyshev", "chebyshev"); + addAlias("Chebyshev-old", "chebyshev"); // register interface reactions reg("interface", [](const XML_Node& node) { diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index cb626755f08..c7f1677c0ed 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -225,15 +225,16 @@ TEST(Reaction, ChebyshevFromYaml) auto R = newReaction(rxn, *(sol->kinetics())); EXPECT_EQ(R->reactants.size(), (size_t) 1); - auto CR = dynamic_cast(*R); + auto CR = dynamic_cast(*R); + const auto& rate = std::dynamic_pointer_cast(CR.rate()); double logP = std::log10(2e6); double T = 1800; - CR.rate.update_C(&logP); - EXPECT_EQ(CR.rate.nTemperature(), (size_t) 6); - EXPECT_EQ(CR.rate.nPressure(), (size_t) 4); - EXPECT_DOUBLE_EQ(CR.rate.Tmax(), 3000); - EXPECT_DOUBLE_EQ(CR.rate.Pmin(), 1000); - EXPECT_NEAR(CR.rate.updateRC(std::log(T), 1.0/T), 130512.2773948636, 1e-9); + rate->update_C(&logP); + EXPECT_EQ(rate->nTemperature(), (size_t) 6); + EXPECT_EQ(rate->nPressure(), (size_t) 4); + EXPECT_DOUBLE_EQ(rate->Tmax(), 3000); + EXPECT_DOUBLE_EQ(rate->Pmin(), 1000); + EXPECT_NEAR(rate->updateRC(std::log(T), 1.0/T), 130512.2773948636, 1e-9); } TEST(Reaction, BlowersMaselFromYaml) @@ -552,8 +553,8 @@ TEST_F(ReactionToYaml, Chebyshev) soln = newSolution("pdep-test.yaml"); soln->thermo()->setState_TPY(1000, 2e5, "R6:1, P6A:2, P6B:0.3"); duplicateReaction(5); - EXPECT_TRUE(std::dynamic_pointer_cast(duplicate)); - compareReactions(); + EXPECT_TRUE(std::dynamic_pointer_cast(duplicate)); + compareReactions(true); } TEST_F(ReactionToYaml, surface) From fde3b41a9dcac9a400245c4e077cbd369cb26d4e Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 26 Apr 2021 14:05:06 -0500 Subject: [PATCH 41/84] [Kinetics] Implement getParameters for ChebyshevReaction3 --- include/cantera/kinetics/Reaction.h | 1 + include/cantera/kinetics/ReactionRate.h | 2 ++ include/cantera/kinetics/RxnRates.h | 1 + src/kinetics/Reaction.cpp | 39 ++++++++----------------- src/kinetics/RxnRates.cpp | 32 +++++++++++++++++++- 5 files changed, 47 insertions(+), 28 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 77fb2af78f1..e90f179529a 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -447,6 +447,7 @@ class ChebyshevReaction3 : public Reaction3 } virtual bool setParameters(const AnyMap& node, const Kinetics& kin); + virtual void getParameters(AnyMap& reactionNode) const; }; diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 4c1e764455c..a90a9ea1082 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -341,6 +341,8 @@ class ChebyshevRate3 final : public ReactionRate, public Chebyshe virtual bool setParameters(const AnyMap& node, const Units& rate_units) override; + virtual void getParameters(AnyMap& rateNode, + const Units& rate_units) const override; //! Update information specific to reaction static bool uses_update() { return true; } diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 47ae421d2ef..70e3ce7e8a8 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -475,6 +475,7 @@ class ChebyshevRate //! Run object setup based on AnyMap node information void setParameters(const AnyMap& node, const UnitSystem& units, const Units& rate_units); + void getParameters(AnyMap& rateNode, const Units& rate_units) const; //! Set up Chebyshev object void setup(double Tmin, double Tmax, double Pmin, double Pmax, diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index a63ec85045a..693f8810193 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -933,7 +933,15 @@ bool ChebyshevReaction3::setParameters(const AnyMap& node, const Kinetics& kin) input = node; setRate(std::shared_ptr(new ChebyshevRate3(node, rate_units))); - return true + return true; +} + +void ChebyshevReaction3::getParameters(AnyMap& reactionNode) const +{ + Reaction::getParameters(reactionNode); + AnyMap rateNode; + m_rate->getParameters(rateNode, rate_units); + reactionNode.update(rateNode); } CustomFunc1Reaction::CustomFunc1Reaction() @@ -974,32 +982,9 @@ void ChebyshevReaction::getParameters(AnyMap& reactionNode) const { Reaction::getParameters(reactionNode); reactionNode["type"] = "Chebyshev"; - reactionNode["temperature-range"].setQuantity({rate.Tmin(), rate.Tmax()}, "K"); - reactionNode["pressure-range"].setQuantity({rate.Pmin(), rate.Pmax()}, "Pa"); - const auto& coeffs1d = rate.coeffs(); - size_t nT = rate.nTemperature(); - size_t nP = rate.nPressure(); - std::vector coeffs2d(nT, vector_fp(nP)); - for (size_t i = 0; i < nT; i++) { - for (size_t j = 0; j < nP; j++) { - coeffs2d[i][j] = coeffs1d[nP*i + j]; - } - } - // Unit conversions must take place later, after the destination unit system - // is known. A lambda function is used here to override the default behavior - Units rate_units2 = rate_units; - auto converter = [rate_units2](AnyValue& coeffs, const UnitSystem& units) { - if (rate_units2.factor() != 0.0) { - coeffs.asVector()[0][0] += std::log10(units.convertFrom(1.0, rate_units2)); - } else if (units.getDelta(UnitSystem()).size()) { - throw CanteraError("ChebyshevReaction::getParameters lambda", - "Cannot convert rate constant with unknown dimensions to a " - "non-default unit system"); - } - }; - AnyValue coeffs; - coeffs = std::move(coeffs2d); - reactionNode["data"].setQuantity(coeffs, converter); + AnyMap rateNode; + rate.getParameters(rateNode, rate_units); + reactionNode.update(rateNode); } InterfaceReaction::InterfaceReaction() diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 59f18b8f02e..0284d628362 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -278,7 +278,7 @@ void ChebyshevRate::setParameters(const AnyMap& node, coeffs(i, j) = vcoeffs[i][j]; } } - coeffs(0, 0) += std::log10(units.convert(1.0, rate_units)); + coeffs(0, 0) += std::log10(units.convertTo(1.0, rate_units)); Tmin_ = units.convert(T_range[0], "K"); Tmax_ = units.convert(T_range[1], "K"); @@ -312,6 +312,36 @@ void ChebyshevRate::setup(double Tmin, double Tmax, double Pmin, double Pmax, } } +void ChebyshevRate::getParameters(AnyMap& rateNode, const Units& rate_units) const +{ + rateNode["temperature-range"].setQuantity({Tmin(), Tmax()}, "K"); + rateNode["pressure-range"].setQuantity({Pmin(), Pmax()}, "Pa"); + const auto& coeffs1d = coeffs(); + size_t nT = nTemperature(); + size_t nP = nPressure(); + std::vector coeffs2d(nT, vector_fp(nP)); + for (size_t i = 0; i < nT; i++) { + for (size_t j = 0; j < nP; j++) { + coeffs2d[i][j] = coeffs1d[nP*i + j]; + } + } + // Unit conversions must take place later, after the destination unit system + // is known. A lambda function is used here to override the default behavior + Units rate_units2 = rate_units; + auto converter = [rate_units2](AnyValue& coeffs, const UnitSystem& units) { + if (rate_units2.factor() != 0.0) { + coeffs.asVector()[0][0] += std::log10(units.convertFrom(1.0, rate_units2)); + } else if (units.getDelta(UnitSystem()).size()) { + throw CanteraError("ChebyshevReaction::getParameters lambda", + "Cannot convert rate constant with unknown dimensions to a " + "non-default unit system"); + } + }; + AnyValue coeffs; + coeffs = std::move(coeffs2d); + rateNode["data"].setQuantity(coeffs, converter); +} + BMSurfaceArrhenius::BMSurfaceArrhenius() : m_b(0.0) , m_A(0.0) From fbcec15dfdc3bfab06f4811ec36500298556cb6c Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 24 Mar 2021 13:28:03 -0500 Subject: [PATCH 42/84] [Kinetics] Deprecate ChebyshevRate in favor of Chebyshev --- include/cantera/kinetics/GasKinetics.h | 2 +- include/cantera/kinetics/Reaction.h | 25 +------------ include/cantera/kinetics/ReactionRate.h | 2 +- include/cantera/kinetics/RxnRates.h | 40 +++++++++++++++++--- interfaces/cython/cantera/_cantera.pxd | 9 ++--- interfaces/cython/cantera/reaction.pyx | 49 +------------------------ src/kinetics/Kinetics.cpp | 2 +- src/kinetics/Reaction.cpp | 38 ++++++------------- src/kinetics/ReactionFactory.cpp | 5 --- src/kinetics/ReactionRate.cpp | 8 ++-- src/kinetics/RxnRates.cpp | 32 ++++++++++++---- test/kinetics/kineticsFromScratch.cpp | 2 +- test/kinetics/kineticsFromYaml.cpp | 2 +- 13 files changed, 85 insertions(+), 131 deletions(-) diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index ddf148ff099..4d19a3809a6 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -86,7 +86,7 @@ class GasKinetics : public BulkKinetics ThirdBodyCalc m_falloff_concm; Rate1 m_plog_rates; - Rate1 m_cheb_rates; + Rate1 m_cheb_rates; Rate1 m_blowersmasel_rates; //! @name Reaction rate data diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index e90f179529a..d60d3282fe7 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -318,14 +318,14 @@ class ChebyshevReaction : public Reaction public: ChebyshevReaction(); ChebyshevReaction(const Composition& reactants, const Composition& products, - const ChebyshevRate& rate); + const Chebyshev& rate); virtual void getParameters(AnyMap& reactionNode) const; virtual std::string type() const { return "Chebyshev-old"; } - ChebyshevRate rate; + Chebyshev rate; }; @@ -468,27 +468,6 @@ class CustomFunc1Reaction : public Reaction3 }; -//! A reaction which follows mass-action kinetics with a modified Arrhenius -//! reaction rate. -/** - * 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. - */ -class TestReaction : public Reaction3 -{ -public: - TestReaction(); - - TestReaction(const AnyMap& node, const Kinetics& kin); - - virtual std::string type() const { - return "elementary-new"; - } -}; - - //! Modifications to an InterfaceReaction rate based on a surface species //! coverage. struct CoverageDependency diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index a90a9ea1082..3b137da9964 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -313,7 +313,7 @@ class PlogRate final : public ReactionRate, public Plog * therefore extrapolation of rates outside the range of temperatures and * pressures for which they are defined is strongly discouraged. */ -class ChebyshevRate3 final : public ReactionRate, public ChebyshevRate +class ChebyshevRate3 final : public ReactionRate, public Chebyshev { public: //! Default constructor. diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 70e3ce7e8a8..e87c7a09865 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -453,11 +453,11 @@ class Plog * therefore extrapolation of rates outside the range of temperatures and * pressures for which they are defined is strongly discouraged. */ -class ChebyshevRate +class Chebyshev { public: //! Default constructor. - ChebyshevRate() {} + Chebyshev() {} //! Constructor directly from coefficient array /* @@ -469,8 +469,8 @@ class ChebyshevRate * `nP` are the number of temperatures and pressures used in the fit, * respectively. */ - ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, - const Array2D& coeffs); + Chebyshev(double Tmin, double Tmax, double Pmin, double Pmax, + const Array2D& coeffs); //! Run object setup based on AnyMap node information void setParameters(const AnyMap& node, @@ -483,7 +483,7 @@ class ChebyshevRate //! Update concentration-dependent parts of the rate coefficient. //! @param c base-10 logarithm of the pressure in Pa - void update_C(const doublereal* c) { + void update_C(const double* c) { double Pr = (2 * c[0] + PrNum_) * PrDen_; double Cnm1 = Pr; double Cn = 1; @@ -506,7 +506,7 @@ class ChebyshevRate * * This function returns the actual value of the rate constant. */ - doublereal updateRC(doublereal logT, doublereal recipT) const { + double updateRC(double logT, double recipT) const { double Tr = (2 * recipT + TrNum_) * TrDen_; double Cnm1 = Tr; double Cn = 1; @@ -710,6 +710,34 @@ class BMSurfaceArrhenius }; + +//! Pressure-dependent rate expression where the rate coefficient is expressed +//! as a bivariate Chebyshev polynomial in temperature and pressure. +/*! + * @deprecated Renamed to Chebyshev. Behavior will change after Cantera 2.6. + * For future behavior, refer to ChebyshevRate3. + */ +class ChebyshevRate : public Chebyshev +{ +public: + //! Default constructor. + ChebyshevRate(); + + //! Constructor directly from coefficient array + /* + * @param Tmin Minimum temperature [K] + * @param Tmax Maximum temperature [K] + * @param Pmin Minimum pressure [Pa] + * @param Pmax Maximum pressure [Pa] + * @param coeffs Coefficient array dimensioned `nT` by `nP` where `nT` and + * `nP` are the number of temperatures and pressures used in the fit, + * respectively. + */ + ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, + const Array2D& coeffs); + +}; + } #endif diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 25796c18c43..ae5cbcd585b 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -465,8 +465,8 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxPlogReaction "Cantera::PlogReaction" (CxxReaction): CxxPlog rate - cdef cppclass CxxChebyshevRate "Cantera::ChebyshevRate": - CxxChebyshevRate(double, double, double, double, CxxArray2D) + cdef cppclass CxxChebyshev "Cantera::Chebyshev": + CxxChebyshev(double, double, double, double, CxxArray2D) double Tmin() double Tmax() double Pmin() @@ -478,7 +478,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": double updateRC(double, double) cdef cppclass CxxChebyshevReaction "Cantera::ChebyshevReaction" (CxxReaction): - CxxChebyshevRate rate + CxxChebyshev rate cdef cppclass CxxElementaryReaction3 "Cantera::ElementaryReaction3" (CxxReaction3): CxxElementaryReaction3() @@ -496,9 +496,6 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxCustomFunc1Reaction "Cantera::CustomFunc1Reaction" (CxxReaction3): CxxCustomFunc1Reaction() - cdef cppclass CxxTestReaction "Cantera::TestReaction" (CxxReaction3): - CxxTestReaction() - cdef cppclass CxxBlowersMasel "Cantera::BlowersMasel": CxxBlowersMasel() CxxBlowersMasel(double, double, double, double) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 201846ef278..063dab7ec80 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -1114,7 +1114,7 @@ cdef class ChebyshevReaction(Reaction): for j,value in enumerate(row): CxxArray2D_set(data, i, j, value) - r.rate = CxxChebyshevRate(Tmin, Tmax, Pmin, Pmax, data) + r.rate = CxxChebyshev(Tmin, Tmax, Pmin, Pmax, data) def __call__(self, float T, float P): cdef CxxChebyshevReaction* r = self.reaction @@ -1523,53 +1523,6 @@ cdef class CustomReaction(Reaction): r.setRate(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: - - spec = {'equation': equation, 'type': self.reaction_type} - if isinstance(rate, dict): - spec['rate-constant'] = rate - elif isinstance(rate, ArrheniusRate) or rate is None: - spec['rate-constant'] = dict.fromkeys(['A', 'b', 'Ea'], 0.) - else: - raise TypeError("Invalid rate definition") - - self._reaction = CxxNewReaction(dict_to_anymap(spec), - 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.rate()) - def __set__(self, ArrheniusRate rate): - cdef CxxTestReaction* r = self.reaction - r.setRate(rate._base) - - cdef class InterfaceReaction(ElementaryReaction): """ A reaction occurring on an `Interface` (i.e. a surface or an edge) """ reaction_type = "interface" diff --git a/src/kinetics/Kinetics.cpp b/src/kinetics/Kinetics.cpp index 03447b252a9..863f7a37b6e 100644 --- a/src/kinetics/Kinetics.cpp +++ b/src/kinetics/Kinetics.cpp @@ -354,7 +354,7 @@ double Kinetics::productStoichCoeff(size_t kSpec, size_t irxn) const } int Kinetics::reactionType(size_t i) const { - warn_deprecated("Kinetics::reactionType()", + warn_deprecated("Kinetics::reactionType", "To be changed after Cantera 2.6. " "Return string instead of magic number; use " "Kinetics::reactionTypeStr during transition."); diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 693f8810193..6f64b320941 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -639,7 +639,7 @@ ChebyshevReaction::ChebyshevReaction() ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, const Composition& products_, - const ChebyshevRate& rate_) + const Chebyshev& rate_) : Reaction(reactants_, products_) , rate(rate_) { @@ -900,6 +900,7 @@ ChebyshevReaction3::ChebyshevReaction3(const AnyMap& node, const Kinetics& kin) : ChebyshevReaction3() { setParameters(node, kin); + setRate(std::shared_ptr(new ChebyshevRate3(node, rate_units))); } bool ChebyshevReaction3::setParameters(const AnyMap& node, const Kinetics& kin) @@ -931,8 +932,6 @@ bool ChebyshevReaction3::setParameters(const AnyMap& node, const Kinetics& kin) calculateRateCoeffUnits(kin); input = node; - - setRate(std::shared_ptr(new ChebyshevRate3(node, rate_units))); return true; } @@ -965,19 +964,6 @@ CustomFunc1Reaction::CustomFunc1Reaction(const AnyMap& node, const Kinetics& kin setRate(std::shared_ptr(new CustomFunc1Rate(node, rate_units))); } -TestReaction::TestReaction() - : Reaction3() -{ - m_rate = std::shared_ptr(new ArrheniusRate); -} - -TestReaction::TestReaction(const AnyMap& node, const Kinetics& kin) - : TestReaction() -{ - setParameters(node, kin); - setRate(std::shared_ptr(new ArrheniusRate(node, rate_units))); -} - void ChebyshevReaction::getParameters(AnyMap& reactionNode) const { Reaction::getParameters(reactionNode); @@ -1657,11 +1643,11 @@ void setupChebyshevReaction(ChebyshevReaction& R, const XML_Node& rxn_node) coeffs(t,p) = coeffs_flat[nP*t + p]; } } - R.rate = ChebyshevRate(getFloat(rc, "Tmin", "toSI"), - getFloat(rc, "Tmax", "toSI"), - getFloat(rc, "Pmin", "toSI"), - getFloat(rc, "Pmax", "toSI"), - coeffs); + R.rate = Chebyshev(getFloat(rc, "Tmin", "toSI"), + getFloat(rc, "Tmax", "toSI"), + getFloat(rc, "Pmin", "toSI"), + getFloat(rc, "Pmax", "toSI"), + coeffs); setupReaction(R, rxn_node); } @@ -1686,11 +1672,11 @@ void setupChebyshevReaction(ChebyshevReaction&R, const AnyMap& node, } const UnitSystem& units = node.units(); coeffs(0, 0) += std::log10(units.convertTo(1.0, R.rate_units)); - R.rate = ChebyshevRate(units.convert(T_range[0], "K"), - units.convert(T_range[1], "K"), - units.convert(P_range[0], "Pa"), - units.convert(P_range[1], "Pa"), - coeffs); + R.rate = Chebyshev(units.convert(T_range[0], "K"), + units.convert(T_range[1], "K"), + units.convert(P_range[0], "Pa"), + units.convert(P_range[1], "Pa"), + coeffs); } void setupInterfaceReaction(InterfaceReaction& R, const XML_Node& rxn_node) diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 88733574304..27be9ec0e28 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -107,11 +107,6 @@ ReactionFactory::ReactionFactory() return new CustomFunc1Reaction(node, kin); }); - // register custom Python reactions - reg("elementary-new", [](const AnyMap& node, const Kinetics& kin) { - return new TestReaction(node, kin); - }); - // register interface reactions reg("interface", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new InterfaceReaction(); diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index afa47260a5d..65beb061358 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -76,16 +76,16 @@ void PlogRate::getParameters(AnyMap& rateNode, } ChebyshevRate3::ChebyshevRate3() - : ChebyshevRate() { + : Chebyshev() { } ChebyshevRate3::ChebyshevRate3(double Tmin, double Tmax, double Pmin, double Pmax, const Array2D& coeffs) - : ChebyshevRate(Tmin, Tmax, Pmin, Pmax, coeffs) { + : Chebyshev(Tmin, Tmax, Pmin, Pmax, coeffs) { } ChebyshevRate3::ChebyshevRate3(const AnyMap& node, const Units& rate_units) - : ChebyshevRate() { + : Chebyshev() { setParameters(node, rate_units); } @@ -98,7 +98,7 @@ bool ChebyshevRate3::setParameters(const AnyMap& node, const Units& rate_units) "data: [[-16.]]\n"); Chebyshev::setParameters(rate, node.units(), rate_units); return false; } - ChebyshevRate::setParameters(node, node.units(), rate_units); + Chebyshev::setParameters(node, node.units(), rate_units); return true; } diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 0284d628362..d7f84f37916 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -6,6 +6,7 @@ #include "cantera/kinetics/RxnRates.h" #include "cantera/base/Array.h" #include "cantera/base/AnyMap.h" +#include "cantera/base/global.h" namespace Cantera { @@ -248,8 +249,8 @@ std::vector > Plog::rates() const return R; } -ChebyshevRate::ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, - const Array2D& coeffs) +Chebyshev::Chebyshev(double Tmin, double Tmax, double Pmin, double Pmax, + const Array2D& coeffs) : Tmin_(Tmin) , Tmax_(Tmax) , Pmin_(Pmin) @@ -262,8 +263,8 @@ ChebyshevRate::ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, setup(Tmin, Tmax, Pmin, Pmax, coeffs); } -void ChebyshevRate::setParameters(const AnyMap& node, - const UnitSystem& units, const Units& rate_units) +void Chebyshev::setParameters(const AnyMap& node, + const UnitSystem& units, const Units& rate_units) { const auto& T_range = node["temperature-range"].asVector(2); const auto& P_range = node["pressure-range"].asVector(2); @@ -271,7 +272,7 @@ void ChebyshevRate::setParameters(const AnyMap& node, Array2D coeffs(vcoeffs.size(), vcoeffs[0].size()); for (size_t i = 0; i < coeffs.nRows(); i++) { if (vcoeffs[i].size() != vcoeffs[0].size()) { - throw InputFileError("ChebyshevRate::setParameters", node["data"], + throw InputFileError("Chebyshev::setParameters", node["data"], "Inconsistent number of coefficients in row {} of matrix", i + 1); } for (size_t j = 0; j < coeffs.nColumns(); j++) { @@ -292,8 +293,8 @@ void ChebyshevRate::setParameters(const AnyMap& node, setup(Tmin_, Tmax_, Pmin_, Pmax_, coeffs); } -void ChebyshevRate::setup(double Tmin, double Tmax, double Pmin, double Pmax, - const Array2D& coeffs) +void Chebyshev::setup(double Tmin, double Tmax, double Pmin, double Pmax, + const Array2D& coeffs) { double logPmin = std::log10(Pmin); double logPmax = std::log10(Pmax); @@ -312,7 +313,7 @@ void ChebyshevRate::setup(double Tmin, double Tmax, double Pmin, double Pmax, } } -void ChebyshevRate::getParameters(AnyMap& rateNode, const Units& rate_units) const +void Chebyshev::getParameters(AnyMap& rateNode, const Units& rate_units) const { rateNode["temperature-range"].setQuantity({Tmin(), Tmax()}, "K"); rateNode["pressure-range"].setQuantity({Pmin(), Pmax()}, "Pa"); @@ -376,4 +377,19 @@ void BMSurfaceArrhenius::addCoverageDependence(size_t k, double a, } } +ChebyshevRate::ChebyshevRate() + : Chebyshev() +{ + warn_deprecated("ChebyshevRate::ChebyshevRate", + "Renamed to Chebyshev. Behavior will change after Cantera 2.6. " + "For future behavior, refer to ChebyshevRate3"); +} + +ChebyshevRate::ChebyshevRate(double Tmin, double Tmax, double Pmin, double Pmax, + const Array2D& coeffs) + : Chebyshev(Tmin, Tmax, Pmin, Pmax, coeffs) +{ + warn_deprecated("ChebyshevRate::ChebyshevRate", + "Renamed to Chebyshev. Behavior will change after Cantera 2.6. " + "For future behavior, refer to ChebyshevRate3");} } diff --git a/test/kinetics/kineticsFromScratch.cpp b/test/kinetics/kineticsFromScratch.cpp index 42845753a3d..e972d4ba204 100644 --- a/test/kinetics/kineticsFromScratch.cpp +++ b/test/kinetics/kineticsFromScratch.cpp @@ -189,7 +189,7 @@ TEST_F(KineticsFromScratch, add_chebyshev_reaction) coeffs(2,1) = 2.6889e-01; coeffs(2,2) = 9.4806e-02; coeffs(2,3) = -7.6385e-03; - ChebyshevRate rate(290, 3000, 1000.0, 10000000.0, coeffs); + Chebyshev rate(290, 3000, 1000.0, 10000000.0, coeffs); auto R = make_shared(reac, prod, rate); kin.addReaction(R); diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index c7f1677c0ed..d896cf472dd 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -606,7 +606,7 @@ TEST_F(ReactionToYaml, unconvertible2) Array2D coeffs(2, 2, 1.0); ChebyshevReaction R({{"H2", 1}, {"OH", 1}}, {{"H2O", 1}, {"H", 1}}, - ChebyshevRate(273, 3000, 1e2, 1e7, coeffs)); + Chebyshev(273, 3000, 1e2, 1e7, coeffs)); UnitSystem U{"g", "cm", "mol"}; AnyMap params = R.parameters(); params.setUnits(U); From de57371207506c4f23a09c5a35dcf8f15689c6f0 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 24 Mar 2021 23:10:40 -0500 Subject: [PATCH 43/84] [Kinetics] Construct ArrheniusRate from Arrhenius --- include/cantera/kinetics/ReactionRate.h | 33 +++++++++++++++---------- src/kinetics/ReactionRate.cpp | 8 ++++++ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 3b137da9964..c34e211f06a 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -184,22 +184,29 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius //! Default constructor. ArrheniusRate(); - /// Constructor. - /// @param A pre-exponential. The unit system is - /// (kmol, m, s). The actual units depend on the reaction - /// order and the dimensionality (surface or bulk). - /// @param b Temperature exponent. Non-dimensional. - /// @param E Activation energy. J/kmol. + //! Constructor. + //! @param A pre-exponential. The unit system is + //! (kmol, m, s). The actual units depend on the reaction + //! order and the dimensionality (surface or bulk). + //! @param b Temperature exponent. Non-dimensional. + //! @param E Activation energy. J/kmol. ArrheniusRate(double A, double b, double E); //! Constructor using AnyMap content - /// @param node AnyMap containing rate information - /// @param rate_units unit definitions used for rate information + //! @param node AnyMap containing rate information + //! @param rate_units unit definitions used for rate information ArrheniusRate(const AnyMap& node, const Units& rate_units); - virtual bool setParameters(const AnyMap& node, const Units& rate_units) override; + //! Constructor based on Arrhenius object + //! @param arr Arrhenius object + //! @param allow_negative_A allow negative pre-exponential factor + //! (optional, default is false) + ArrheniusRate(const Arrhenius& arr, bool allow_negative_A=false); + virtual void getParameters(AnyMap& rateNode, const Units& rate_units) const override; + virtual bool setParameters(const AnyMap& node, + const Units& rate_units) override; virtual std::string type() const override { return "ArrheniusRate"; } @@ -255,8 +262,8 @@ class PlogRate final : public ReactionRate, public Plog explicit PlogRate(const std::multimap& rates); //! Constructor using AnyMap content - /// @param node AnyMap containing rate information - /// @param rate_units unit definitions used for rate information + //! @param node AnyMap containing rate information + //! @param rate_units unit definitions used for rate information PlogRate(const AnyMap& node, const Units& rate_units); virtual std::string type() const override { return "PlogRate"; } @@ -333,8 +340,8 @@ class ChebyshevRate3 final : public ReactionRate, public Chebyshe const Array2D& coeffs); //! Constructor using AnyMap content - /// @param node AnyMap containing rate information - /// @param rate_units unit definitions used for rate information + //! @param node AnyMap containing rate information + //! @param rate_units unit definitions used for rate information ChebyshevRate3(const AnyMap& node, const Units& rate_units); virtual std::string type() const override { return "ChebyshevRate"; } diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 65beb061358..b6c2e221c1e 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -22,6 +22,14 @@ ArrheniusRate::ArrheniusRate(double A, double b, double E) { } +ArrheniusRate::ArrheniusRate(const Arrhenius& arr, bool allow_negative_A) + : Arrhenius(arr.preExponentialFactor(), + arr.temperatureExponent(), + arr.activationEnergy_R()) + , allow_negative_pre_exponential_factor(allow_negative_A) +{ +} + ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rate_units) { setParameters(node, rate_units); } From 81a7938767071be7cbd31865ddd02637f9229aab Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 24 Mar 2021 23:29:06 -0500 Subject: [PATCH 44/84] [Kinetics] Remove unused and mark deprecated Python methods --- interfaces/cython/cantera/_cantera.pxd | 2 - interfaces/cython/cantera/reaction.pyx | 76 ++++++++++++-------------- 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index ae5cbcd585b..58695d48ba5 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -390,8 +390,6 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": size_t nPressure() size_t nTemperature() vector[double]& coeffs() - void update_C(double*) - double updateRC(double, double) cdef cppclass CxxReaction "Cantera::Reaction": CxxReaction() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 063dab7ec80..762fe86aba9 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -538,9 +538,21 @@ cdef class ArrheniusRate(_ReactionRate): def __get__(self): return self.rate.activationEnergy() + property allow_negative_pre_exponential_factor: + """ + Get/Set whether the rate coefficient is allowed to have a negative + pre-exponential factor. + """ + def __get__(self): + return self.rate.allow_negative_pre_exponential_factor + def __set__(self, allow): + self.rate.allow_negative_pre_exponential_factor = allow + cdef class PlogRate(_ReactionRate): r""" + A pressure-dependent reaction rate parameterized by logarithmically + interpolating between Arrhenius rate expressions at various pressures. """ def __cinit__(self, rates=None, init=True): @@ -589,8 +601,11 @@ cdef class PlogRate(_ReactionRate): cdef class ChebyshevRate(_ReactionRate): r""" + A pressure-dependent reaction rate parameterized by a bivariate Chebyshev + polynomial in temperature and pressure. """ - def __cinit__(self, Tmin=None, Tmax=None, Pmin=None, Pmax=None, data=None, init=True): + def __cinit__(self, Tmin=None, Tmax=None, Pmin=None, Pmax=None, data=None, + init=True): if Tmin and Tmax and Pmin and Pmax and data and init: self._setup(Tmin, Tmax, Pmin, Pmax, data) @@ -973,6 +988,12 @@ cdef class PlogReaction(Reaction): """ A pressure-dependent reaction parameterized by logarithmically interpolating between Arrhenius rate expressions at various pressures. + + .. deprecated:: 2.6 + + This class is superseded by `PlogReaction3` and only used by XML. + The implementation of this reaction type will change after Cantera 2.6; + refer to `PlogReaction3` for new behavior. """ reaction_type = "pressure-dependent-Arrhenius-old" @@ -1033,6 +1054,12 @@ cdef class ChebyshevReaction(Reaction): """ A pressure-dependent reaction parameterized by a bivariate Chebyshev polynomial in temperature and pressure. + + .. deprecated:: 2.6 + + This class is superseded by `ChebyshevReaction3` and only used by XML. + The implementation of this reaction type will change after Cantera 2.6; + refer to `ChebyshevReaction3` for new behavior. """ reaction_type = "Chebyshev-old" @@ -1252,9 +1279,6 @@ cdef class ElementaryReaction3(Reaction): cdef CxxElementaryReaction3* er(self): return self.reaction - cdef CxxArrheniusRate* arr(self): - return (self.er().rate().get()) - def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): @@ -1282,16 +1306,6 @@ cdef class ElementaryReaction3(Reaction): def __set__(self, ArrheniusRate rate): self.er().setRate(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): - return self.arr().allow_negative_pre_exponential_factor - def __set__(self, allow): - self.arr().allow_negative_pre_exponential_factor = allow - cdef class ThreeBodyReaction3(ElementaryReaction3): """ @@ -1382,6 +1396,9 @@ cdef class PlogReaction3(Reaction): """ A pressure-dependent reaction parameterized by logarithmically interpolating between Arrhenius rate expressions at various pressures. + + This class is a replacement for `PlogReaction` and cannot be + instantiated from XML. It is the default for import from YAML. """ reaction_type = "pressure-dependent-Arrhenius" @@ -1419,29 +1436,20 @@ cdef class PlogReaction3(Reaction): def __set__(self, PlogRate rate): self.pr().setRate(rate._base) - def __call__(self, float T, float P): - cdef CxxPlogReaction* r = self.reaction - cdef double logT = np.log(T) - cdef double recipT = 1/T - cdef double logP = np.log(P) - - r.rate.update_C(&logP) - return r.rate.updateRC(logT, recipT) - cdef class ChebyshevReaction3(Reaction): """ A pressure-dependent reaction parameterized by a bivariate Chebyshev polynomial in temperature and pressure. + + This class is a replacement for `ChebyshevReaction` and cannot be + instantiated from XML. It is the default for import from YAML. """ reaction_type = "Chebyshev" cdef CxxChebyshevReaction3* cr(self): return self.reaction - cdef CxxChebyshevRate3* crr(self): - return (self.cr().rate().get()) - def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): @@ -1471,22 +1479,6 @@ cdef class ChebyshevReaction3(Reaction): def __set__(self, ChebyshevRate rate): self.cr().setRate(rate._base) - def set_parameters(self, Tmin, Tmax, Pmin, Pmax, coeffs): - """ - Simultaneously set values for `Tmin`, `Tmax`, `Pmin`, `Pmax`, and - `coeffs`. - """ - self.rate = self.ChebyshevRate(Tmin, Tmax, Pmin, Pmax, coeffs) - - def __call__(self, float T, float P): - cdef CxxChebyshevReaction3* r = self.reaction - cdef double logT = np.log(T) - cdef double recipT = 1/T - cdef double logP = np.log10(P) - - self.crr().update_C(&logP) - return self.crr().updateRC(logT, recipT) - cdef class CustomReaction(Reaction): """ From 4e98794b2cf8e43140fd9f0deac9159a919b812b Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Thu, 25 Mar 2021 00:17:27 -0500 Subject: [PATCH 45/84] [Kinetics] Update documentation for Reaction3 classes --- interfaces/cython/cantera/reaction.pyx | 42 ++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 762fe86aba9..7b04488ee70 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -607,7 +607,7 @@ cdef class ChebyshevRate(_ReactionRate): def __cinit__(self, Tmin=None, Tmax=None, Pmin=None, Pmax=None, data=None, init=True): - if Tmin and Tmax and Pmin and Pmax and data and init: + if Tmin and Tmax and Pmin and Pmax and data is not None and init: self._setup(Tmin, Tmax, Pmin, Pmax, data) def _setup(self, Tmin, Tmax, Pmin, Pmax, coeffs): @@ -1324,7 +1324,7 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): The YAML description corresponding to this reaction is:: - equation: 2 O + M <=> O2 + M # Reaction 1 + equation: 2 O + M <=> O2 + M type: three-body rate-constant: {A: 1.2e+17 cm^6/mol^2/s, b: -1.0, Ea: 0.0 cal/mol} efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} @@ -1397,6 +1397,25 @@ cdef class PlogReaction3(Reaction): A pressure-dependent reaction parameterized by logarithmically interpolating between Arrhenius rate expressions at various pressures. + An example for the definition of an `PlogReaction3` object is given + as:: + + rxn = PlogReaction3( + [{'P': 1013.25, 'rate-constant': {'A': 1.2124e+16, 'b': -0.5779, 'Ea': 45491376.8}}, + {'P': 101325., 'rate-constant': {'A': 4.9108e+31, 'b': -4.8507, 'Ea': 103649395.2}}, + {'P': 1013250., 'rate-constant': {'A': 1.2866e+47, 'b': -9.0246, 'Ea': 166508556.0}}, + {'P': 10132500., 'rate-constant': {'A': 5.9632e+56, 'b': -11.529, 'Ea': 220076726.4}}]) + + The YAML description corresponding to this reaction is:: + + equation: H2 + O2 <=> 2 OH + type: pressure-dependent-Arrhenius + rate-constants: + - {P: 0.01 atm, A: 1.2124e+16, b: -0.5779, Ea: 1.08727e+04 cal/mol} + - {P: 1.0 atm, A: 4.9108e+31, b: -4.8507, Ea: 2.47728e+04 cal/mol} + - {P: 10.0 atm, A: 1.2866e+47, b: -9.0246, Ea: 3.97965e+04 cal/mol} + - {P: 100.0 atm, A: 5.9632e+56, b: -11.529, Ea: 5.25996e+04 cal/mol} + This class is a replacement for `PlogReaction` and cannot be instantiated from XML. It is the default for import from YAML. """ @@ -1442,6 +1461,25 @@ cdef class ChebyshevReaction3(Reaction): A pressure-dependent reaction parameterized by a bivariate Chebyshev polynomial in temperature and pressure. + An example for the definition of an `PlogReaction3` object is given + as:: + + rxm = ChebyshevRate(Tmin=290., Tmax=3000., Pmin=1000., Pmax=10000000.0, + data=[[8.2883, -1.1397, -0.12059, 0.016034], + [1.9764, 1.0037, 7.2865e-03, -0.030432], + [0.3177, 0.26889, 0.094806, -7.6385e-03]]) + + The YAML description corresponding to this reaction is:: + + equation: HO2 <=> OH + O # Reaction 5 + type: Chebyshev + temperature-range: [290.0, 3000.0] + pressure-range: [9.869232667160128e-03 atm, 98.69232667160128 atm] + data: + - [8.2883, -1.1397, -0.12059, 0.016034] + - [1.9764, 1.0037, 7.2865e-03, -0.030432] + - [0.3177, 0.26889, 0.094806, -7.6385e-03] + This class is a replacement for `ChebyshevReaction` and cannot be instantiated from XML. It is the default for import from YAML. """ From e169f87b21ae5d7bf37e20e3360e52dcd70aade3 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Thu, 25 Mar 2021 00:18:04 -0500 Subject: [PATCH 46/84] [CI] Add unit tests for Python ReactionRate objects --- .../cython/cantera/test/test_reaction.py | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 8e3d3845f7b..c55d8e6fb8b 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -2,6 +2,7 @@ from pathlib import Path import cantera as ct +import numpy as np from . import utilities @@ -109,6 +110,107 @@ def test_user_override(self): self.assertEqual(rxn.reaction_type, "elementary") +class TestReactionRate(utilities.CanteraTest): + + _cls = None + _type = None + _uses_pressure = False + _index = None + + @classmethod + def setUpClass(cls): + utilities.CanteraTest.setUpClass() + cls.gas = ct.Solution('kineticsfromscratch.yaml') + cls.gas.X = 'H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5' + cls.gas.TP = 900, 2*ct.one_atm + + def test_type(self): + if self._index is None: + return + self.assertIn(self._type, '{}'.format(self.rate)) + + def test_rate_T(self): + if self._index is None: + return + if self._uses_pressure: + with self.assertRaisesRegex(ct.CanteraError, "reaction type requires pressure"): + self.assertNear(self.rate(self.gas.T), + self.gas.forward_rate_constants[self._index]) + else: + self.assertNear(self.rate(self.gas.T), + self.gas.forward_rate_constants[self._index]) + + def test_rate_TP(self): + if self._index is None: + return + self.assertNear(self.rate(self.gas.T, self.gas.P), + self.gas.forward_rate_constants[self._index]) + + +class TestArrheniusRate(TestReactionRate): + + _type = "Arrhenius" + _uses_pressure = False + _index = 0 + + def setUp(self): + self.A = self.gas.reaction(self._index).rate.pre_exponential_factor + self.b = self.gas.reaction(self._index).rate.temperature_exponent + self.Ea = self.gas.reaction(self._index).rate.activation_energy + self.rate = ct.ArrheniusRate(self.A, self.b, self.Ea) + + def test_parameters(self): + self.assertEqual(self.A, self.rate.pre_exponential_factor) + self.assertEqual(self.b, self.rate.temperature_exponent) + self.assertEqual(self.Ea, self.rate.activation_energy) + + def test_allow_negative_pre_exponential_factor1(self): + self.assertFalse(self.rate.allow_negative_pre_exponential_factor) + self.rate.allow_negative_pre_exponential_factor = True + self.assertTrue(self.rate.allow_negative_pre_exponential_factor) + + def test_allow_negative_pre_exponential_factor2(self): + # modify value in memory + self.assertFalse(self.gas.reaction(self._index).rate.allow_negative_pre_exponential_factor) + self.gas.reaction(self._index).rate.allow_negative_pre_exponential_factor = True + self.assertTrue(self.gas.reaction(self._index).rate.allow_negative_pre_exponential_factor) + + +class TestPlogRate(TestReactionRate): + + _type = "Plog" + _uses_pressure = True + _index = 3 + + def setUp(self): + self.rate = ct.PlogRate([(1013.25, ct.Arrhenius(1.2124e+16, -0.5779, 45491376.8)), + (101325., ct.Arrhenius(4.9108e+31, -4.8507, 103649395.2)), + (1013250., ct.Arrhenius(1.2866e+47, -9.0246, 166508556.0)), + (10132500., ct.Arrhenius(5.9632e+56, -11.529, 220076726.4))]) + + +class TestChebyshevRate(TestReactionRate): + + _type = "Chebyshev" + _uses_pressure = True + _index = 4 + + def setUp(self): + self.Tmin = self.gas.reaction(self._index).rate.Tmin + self.Tmax = self.gas.reaction(self._index).rate.Tmax + self.Pmin = self.gas.reaction(self._index).rate.Pmin + self.Pmax = self.gas.reaction(self._index).rate.Pmax + self.coeffs = self.gas.reaction(self._index).rate.coeffs + self.rate = ct.ChebyshevRate(self.Tmin, self.Tmax, self.Pmin, self.Pmax, self.coeffs) + + def test_parameters(self): + self.assertEqual(self.Tmin, self.rate.Tmin) + self.assertEqual(self.Tmax, self.rate.Tmax) + self.assertEqual(self.Pmin, self.rate.Pmin) + self.assertEqual(self.Pmax, self.rate.Pmax) + self.assertTrue(np.all(self.coeffs == self.rate.coeffs)) + + class TestReaction(utilities.CanteraTest): _cls = None From 3a0f32f6d6b2a1832c9fd0dda2cee368fffd59ad Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 26 Apr 2021 23:32:05 -0500 Subject: [PATCH 47/84] [Kinetics] Organize new code sections --- include/cantera/kinetics/Reaction.h | 243 +++--- interfaces/cython/cantera/_cantera.pxd | 58 +- interfaces/cython/cantera/reaction.pyx | 799 +++++++++--------- .../cython/cantera/test/test_reaction.py | 86 +- src/kinetics/Reaction.cpp | 358 ++++---- 5 files changed, 778 insertions(+), 766 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index d60d3282fe7..4e0a519611a 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -329,6 +329,130 @@ class ChebyshevReaction : public Reaction }; +//! Modifications to an InterfaceReaction rate based on a surface species +//! coverage. +struct CoverageDependency +{ + //! Constructor + //! @param a_ coefficient for exponential dependence on coverage [dimensionless] + //! @param E_ modification to the activation energy [K] + //! @param m_ exponent for power law dependence on coverage [dimensionless] + CoverageDependency(double a_, double E_, double m_) : a(a_), E(E_), m(m_) {} + CoverageDependency() {} + double a; //!< coefficient for exponential dependence on coverage [dimensionless] + double E; //!< modification to the activation energy [K] + double m; //!< exponent for power law dependence on coverage [dimensionless] +}; + + +//! A reaction occurring on an interface (i.e. a SurfPhase or an EdgePhase) +class InterfaceReaction : public ElementaryReaction +{ +public: + InterfaceReaction(); + InterfaceReaction(const Composition& reactants, const Composition& products, + const Arrhenius& rate, bool isStick=false); + virtual void calculateRateCoeffUnits(const Kinetics& kin); + virtual void getParameters(AnyMap& reactionNode) const; + + virtual std::string type() const { + return "interface"; + } + + //! 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 + //! the parameterization. + std::map coverage_deps; + + //! Set to true if `rate` is a parameterization of the sticking coefficient + //! rather than the forward rate constant + bool is_sticking_coefficient; + + //! Set to true if `rate` is a sticking coefficient which should be + //! translated into a rate coefficient using the correction factor developed + //! by Motz & Wise for reactions with high (near-unity) sticking + //! coefficients. Defaults to 'false'. + bool use_motz_wise_correction; + + //! For reactions with multiple non-surface species, the sticking species + //! needs to be explicitly identified. + std::string sticking_species; +}; + + +//! An interface reaction which involves charged species +class ElectrochemicalReaction : public InterfaceReaction +{ +public: + ElectrochemicalReaction(); + ElectrochemicalReaction(const Composition& reactants, + const Composition& products, const Arrhenius& rate); + virtual void getParameters(AnyMap& reactionNode) const; + + //! Forward value of the apparent Electrochemical transfer coefficient + doublereal beta; + + bool exchange_current_density_formulation; +}; + + +//! A reaction with rate parameters for Blowers-Masel approximation +class BlowersMaselReaction: public Reaction +{ +public: + BlowersMaselReaction(); + BlowersMaselReaction(const Composition& reactants, + const Composition& products, const BlowersMasel& rate); + virtual void getParameters(AnyMap& reactionNode) const; + virtual void validate(); + + virtual std::string type() const { + return "Blowers-Masel"; + } + + BlowersMasel rate; + + bool allow_negative_pre_exponential_factor; +}; + + +//! A reaction occurring on an interface (i.e. a SurfPhase or an EdgePhase) +//! with the rate calculated with Blowers-Masel approximation. +class BlowersMaselInterfaceReaction : public BlowersMaselReaction +{ +public: + BlowersMaselInterfaceReaction(); + BlowersMaselInterfaceReaction(const Composition& reactants, const Composition& products, + const BlowersMasel& rate, bool isStick=false); + virtual void getParameters(AnyMap& reactionNode) const; + virtual void calculateRateCoeffUnits(const Kinetics& kin); + + virtual std::string type() const { + return "surface-Blowers-Masel"; + } + //! 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 + //! the parameterization. + std::map coverage_deps; + + //! Set to true if `rate` is a parameterization of the sticking coefficient + //! rather than the forward rate constant + bool is_sticking_coefficient; + + //! Set to true if `rate` is a sticking coefficient which should be + //! translated into a rate coefficient using the correction factor developed + //! by Motz & Wise for reactions with high (near-unity) sticking + //! coefficients. Defaults to 'false'. + bool use_motz_wise_correction; + + //! For reactions with multiple non-surface species, the sticking species + //! needs to be explicitly identified. + std::string sticking_species; +}; + + //! An intermediate class used to avoid naming conflicts of 'rate' member //! variables and getters (see `ElementaryReaction`, `PlogReaction` and //! `ChebyshevReaction`). @@ -468,125 +592,6 @@ class CustomFunc1Reaction : public Reaction3 }; -//! Modifications to an InterfaceReaction rate based on a surface species -//! coverage. -struct CoverageDependency -{ - //! Constructor - //! @param a_ coefficient for exponential dependence on coverage [dimensionless] - //! @param E_ modification to the activation energy [K] - //! @param m_ exponent for power law dependence on coverage [dimensionless] - CoverageDependency(double a_, double E_, double m_) : a(a_), E(E_), m(m_) {} - CoverageDependency() {} - double a; //!< coefficient for exponential dependence on coverage [dimensionless] - double E; //!< modification to the activation energy [K] - double m; //!< exponent for power law dependence on coverage [dimensionless] -}; - -//! A reaction occurring on an interface (i.e. a SurfPhase or an EdgePhase) -class InterfaceReaction : public ElementaryReaction -{ -public: - InterfaceReaction(); - InterfaceReaction(const Composition& reactants, const Composition& products, - const Arrhenius& rate, bool isStick=false); - virtual void calculateRateCoeffUnits(const Kinetics& kin); - virtual void getParameters(AnyMap& reactionNode) const; - - virtual std::string type() const { - return "interface"; - } - - //! 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 - //! the parameterization. - std::map coverage_deps; - - //! Set to true if `rate` is a parameterization of the sticking coefficient - //! rather than the forward rate constant - bool is_sticking_coefficient; - - //! Set to true if `rate` is a sticking coefficient which should be - //! translated into a rate coefficient using the correction factor developed - //! by Motz & Wise for reactions with high (near-unity) sticking - //! coefficients. Defaults to 'false'. - bool use_motz_wise_correction; - - //! For reactions with multiple non-surface species, the sticking species - //! needs to be explicitly identified. - std::string sticking_species; -}; - -//! An interface reaction which involves charged species -class ElectrochemicalReaction : public InterfaceReaction -{ -public: - ElectrochemicalReaction(); - ElectrochemicalReaction(const Composition& reactants, - const Composition& products, const Arrhenius& rate); - virtual void getParameters(AnyMap& reactionNode) const; - - //! Forward value of the apparent Electrochemical transfer coefficient - doublereal beta; - - bool exchange_current_density_formulation; -}; - -//! A reaction with rate parameters for Blowers-Masel approximation -class BlowersMaselReaction: public Reaction -{ -public: - BlowersMaselReaction(); - BlowersMaselReaction(const Composition& reactants, - const Composition& products, const BlowersMasel& rate); - virtual void getParameters(AnyMap& reactionNode) const; - virtual void validate(); - - virtual std::string type() const { - return "Blowers-Masel"; - } - - BlowersMasel rate; - - bool allow_negative_pre_exponential_factor; -}; - -//! A reaction occurring on an interface (i.e. a SurfPhase or an EdgePhase) -//! with the rate calculated with Blowers-Masel approximation. -class BlowersMaselInterfaceReaction : public BlowersMaselReaction -{ -public: - BlowersMaselInterfaceReaction(); - BlowersMaselInterfaceReaction(const Composition& reactants, const Composition& products, - const BlowersMasel& rate, bool isStick=false); - virtual void getParameters(AnyMap& reactionNode) const; - virtual void calculateRateCoeffUnits(const Kinetics& kin); - - virtual std::string type() const { - return "surface-Blowers-Masel"; - } - //! 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 - //! the parameterization. - std::map coverage_deps; - - //! Set to true if `rate` is a parameterization of the sticking coefficient - //! rather than the forward rate constant - bool is_sticking_coefficient; - - //! Set to true if `rate` is a sticking coefficient which should be - //! translated into a rate coefficient using the correction factor developed - //! by Motz & Wise for reactions with high (near-unity) sticking - //! coefficients. Defaults to 'false'. - bool use_motz_wise_correction; - - //! For reactions with multiple non-surface species, the sticking species - //! needs to be explicitly identified. - std::string sticking_species; -}; - //! Create Reaction objects for all `` nodes in an XML document. //! //! The `` nodes are assumed to be children of the `` diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 58695d48ba5..df572a0169d 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -363,10 +363,6 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": double ddT(double) except +translate_exception double ddT(double, double) except +translate_exception - cdef cppclass CxxCustomFunc1Rate "Cantera::CustomFunc1Rate" (CxxReactionRateBase): - CxxCustomFunc1Rate() - void setRateFunction(shared_ptr[CxxFunc1]) except +translate_exception - cdef cppclass CxxArrheniusRate "Cantera::ArrheniusRate" (CxxReactionRateBase): CxxArrheniusRate() CxxArrheniusRate(double, double, double) @@ -391,6 +387,10 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": size_t nTemperature() vector[double]& coeffs() + cdef cppclass CxxCustomFunc1Rate "Cantera::CustomFunc1Rate" (CxxReactionRateBase): + CxxCustomFunc1Rate() + void setRateFunction(shared_ptr[CxxFunc1]) except +translate_exception + cdef cppclass CxxReaction "Cantera::Reaction": CxxReaction() @@ -411,11 +411,6 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cbool allow_nonreactant_orders cbool allow_negative_orders - cdef cppclass CxxReaction3 "Cantera::Reaction3" (CxxReaction): - CxxReaction3() - shared_ptr[CxxReactionRateBase] rate() - void setRate(shared_ptr[CxxReactionRateBase]) - cdef cppclass CxxElementaryReaction "Cantera::ElementaryReaction" (CxxReaction): CxxElementaryReaction() CxxArrhenius rate @@ -478,22 +473,6 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxChebyshevReaction "Cantera::ChebyshevReaction" (CxxReaction): CxxChebyshev rate - cdef cppclass CxxElementaryReaction3 "Cantera::ElementaryReaction3" (CxxReaction3): - CxxElementaryReaction3() - - cdef cppclass CxxThreeBodyReaction3 "Cantera::ThreeBodyReaction3" (CxxElementaryReaction3): - CxxThreeBodyReaction3() - shared_ptr[CxxThirdBody] thirdBody() - - cdef cppclass CxxPlogReaction3 "Cantera::PlogReaction3" (CxxReaction3): - CxxPlogReaction3() - - cdef cppclass CxxChebyshevReaction3 "Cantera::ChebyshevReaction3" (CxxReaction3): - CxxChebyshevReaction3() - - cdef cppclass CxxCustomFunc1Reaction "Cantera::CustomFunc1Reaction" (CxxReaction3): - CxxCustomFunc1Reaction() - cdef cppclass CxxBlowersMasel "Cantera::BlowersMasel": CxxBlowersMasel() CxxBlowersMasel(double, double, double, double) @@ -527,6 +506,27 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cbool use_motz_wise_correction string sticking_species + cdef cppclass CxxReaction3 "Cantera::Reaction3" (CxxReaction): + CxxReaction3() + shared_ptr[CxxReactionRateBase] rate() + void setRate(shared_ptr[CxxReactionRateBase]) + + cdef cppclass CxxElementaryReaction3 "Cantera::ElementaryReaction3" (CxxReaction3): + CxxElementaryReaction3() + + cdef cppclass CxxThreeBodyReaction3 "Cantera::ThreeBodyReaction3" (CxxElementaryReaction3): + CxxThreeBodyReaction3() + shared_ptr[CxxThirdBody] thirdBody() + + cdef cppclass CxxPlogReaction3 "Cantera::PlogReaction3" (CxxReaction3): + CxxPlogReaction3() + + cdef cppclass CxxChebyshevReaction3 "Cantera::ChebyshevReaction3" (CxxReaction3): + CxxChebyshevReaction3() + + cdef cppclass CxxCustomFunc1Reaction "Cantera::CustomFunc1Reaction" (CxxReaction3): + CxxCustomFunc1Reaction() + cdef extern from "cantera/kinetics/FalloffFactory.h" namespace "Cantera": cdef shared_ptr[CxxFalloff] CxxNewFalloff "Cantera::newFalloff" (string, vector[double]) except +translate_exception @@ -1145,10 +1145,6 @@ cdef class _ReactionRate: cdef shared_ptr[CxxReactionRateBase] _base cdef CxxReactionRateBase* base -cdef class CustomRate(_ReactionRate): - cdef CxxCustomFunc1Rate* rate - cdef Func1 _rate_func - cdef class ArrheniusRate(_ReactionRate): cdef CxxArrheniusRate* rate @staticmethod @@ -1164,6 +1160,10 @@ cdef class ChebyshevRate(_ReactionRate): @staticmethod cdef wrap(shared_ptr[CxxReactionRateBase]) +cdef class CustomRate(_ReactionRate): + cdef CxxCustomFunc1Rate* rate + cdef Func1 _rate_func + cdef class Reaction: cdef shared_ptr[CxxReaction] _reaction cdef CxxReaction* reaction diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 7b04488ee70..422eba1b252 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -6,6 +6,260 @@ cdef dict _reaction_class_registry = {} +cdef class _ReactionRate: + + def __repr__(self): + return "<{} at {:0x}>".format(pystr(self.base.type()), id(self)) + + def __call__(self, double temperature, pressure=None): + if pressure: + self.base.update(temperature, pressure) + return self.base.eval(temperature, pressure) + else: + self.base.update(temperature) + return self.base.eval(temperature) + + def ddT(self, double temperature, pressure=None): + if pressure: + self.base.update(temperature, pressure) + return self.base.ddT(temperature, pressure) + else: + self.base.update(temperature) + return self.base.ddT(temperature) + + +cdef class ArrheniusRate(_ReactionRate): + r""" + A reaction rate coefficient which depends on temperature only and follows + the modified Arrhenius form: + + .. 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`. + """ + def __cinit__(self, A=0, b=0, E=0, init=True): + + if init: + self._base.reset(new CxxArrheniusRate(A, b, E)) + self.base = self._base.get() + self.rate = (self.base) + + @staticmethod + cdef wrap(shared_ptr[CxxReactionRateBase] rate): + """ + Wrap a C++ ReactionRateBase 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.preExponentialFactor() + + property temperature_exponent: + """ + The temperature exponent *b*. + """ + def __get__(self): + return self.rate.temperatureExponent() + + property activation_energy: + """ + The activation energy *E* [J/kmol]. + """ + def __get__(self): + return self.rate.activationEnergy() + + property allow_negative_pre_exponential_factor: + """ + Get/Set whether the rate coefficient is allowed to have a negative + pre-exponential factor. + """ + def __get__(self): + return self.rate.allow_negative_pre_exponential_factor + def __set__(self, allow): + self.rate.allow_negative_pre_exponential_factor = allow + + +cdef class PlogRate(_ReactionRate): + r""" + A pressure-dependent reaction rate parameterized by logarithmically + interpolating between Arrhenius rate expressions at various pressures. + """ + def __cinit__(self, rates=None, init=True): + + if rates and init: + self.rates = rates + + @staticmethod + cdef wrap(shared_ptr[CxxReactionRateBase] rate): + """ + Wrap a C++ ReactionRateBase object with a Python object. + """ + # wrap C++ reaction + cdef PlogRate arr + arr = PlogRate(init=False) + arr._base = rate + arr.base = arr._base.get() + arr.rate = (arr.base) + return arr + + property rates: + """ + Get/Set the rate coefficients for this reaction, which are given as a + list of (pressure, `Arrhenius`) tuples. + """ + def __get__(self): + rates = [] + cdef vector[pair[double, CxxArrhenius]] cxxrates = self.rate.rates() + cdef pair[double, CxxArrhenius] p_rate + for p_rate in cxxrates: + rates.append((p_rate.first, copyArrhenius(&p_rate.second))) + return rates + + def __set__(self, rates): + cdef multimap[double, CxxArrhenius] ratemap + cdef Arrhenius rate + cdef pair[double, CxxArrhenius] item + for p, rate in rates: + item.first = p + item.second = deref(rate.rate) + ratemap.insert(item) + + self._base.reset(new CxxPlogRate(ratemap)) + self.base = self._base.get() + self.rate = (self.base) + + +cdef class ChebyshevRate(_ReactionRate): + r""" + A pressure-dependent reaction rate parameterized by a bivariate Chebyshev + polynomial in temperature and pressure. + """ + def __cinit__(self, Tmin=None, Tmax=None, Pmin=None, Pmax=None, data=None, + init=True): + + if Tmin and Tmax and Pmin and Pmax and data is not None and init: + self._setup(Tmin, Tmax, Pmin, Pmax, data) + + def _setup(self, Tmin, Tmax, Pmin, Pmax, coeffs): + """ + Simultaneously set values for `Tmin`, `Tmax`, `Pmin`, `Pmax`, and + `coeffs`. + """ + cdef CxxArray2D data + data.resize(len(coeffs), len(coeffs[0])) + cdef double value + cdef int i + cdef int j + for i,row in enumerate(coeffs): + for j,value in enumerate(row): + CxxArray2D_set(data, i, j, value) + + self._base.reset(new CxxChebyshevRate3(Tmin, Tmax, Pmin, Pmax, data)) + self.base = self._base.get() + self.rate = (self.base) + + @staticmethod + cdef wrap(shared_ptr[CxxReactionRateBase] rate): + """ + Wrap a C++ ReactionRateBase object with a Python object. + """ + # wrap C++ reaction + cdef ChebyshevRate arr + arr = ChebyshevRate(init=False) + arr._base = rate + arr.base = arr._base.get() + arr.rate = (arr.base) + return arr + + property Tmin: + """ Minimum temperature [K] for the Chebyshev fit """ + def __get__(self): + return self.rate.Tmin() + + property Tmax: + """ Maximum temperature [K] for the Chebyshev fit """ + def __get__(self): + return self.rate.Tmax() + + property Pmin: + """ Minimum pressure [Pa] for the Chebyshev fit """ + def __get__(self): + return self.rate.Pmin() + + property Pmax: + """ Maximum pressure [Pa] for the Chebyshev fit """ + def __get__(self): + return self.rate.Pmax() + + property nPressure: + """ Number of pressures over which the Chebyshev fit is computed """ + def __get__(self): + return self.rate.nPressure() + + property nTemperature: + """ Number of temperatures over which the Chebyshev fit is computed """ + def __get__(self): + return self.rate.nTemperature() + + property coeffs: + """ + 2D array of Chebyshev coefficients of size `(nTemperature, nPressure)`. + """ + def __get__(self): + c = np.fromiter(self.rate.coeffs(), np.double) + return c.reshape((self.rate.nTemperature(), self.rate.nPressure())) + + +cdef class CustomRate(_ReactionRate): + 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)) + """ + def __cinit__(self, k=None, init=True): + + if init: + self._base.reset(new CxxCustomFunc1Rate()) + self.base = self._base.get() + self.rate = (self.base) + self.set_rate_function(k) + + 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)) + """ + if k is None: + self._rate_func = None + return + + if isinstance(k, Func1): + self._rate_func = k + else: + self._rate_func = Func1(k) + + self.rate.setRateFunction(self._rate_func._func) + + cdef class Reaction: """ A class which stores data about a reaction and its rate parameterization so @@ -426,260 +680,6 @@ cdef copyArrhenius(CxxArrhenius* rate): return r -cdef class _ReactionRate: - - def __repr__(self): - return "<{} at {:0x}>".format(pystr(self.base.type()), id(self)) - - def __call__(self, double temperature, pressure=None): - if pressure: - self.base.update(temperature, pressure) - return self.base.eval(temperature, pressure) - else: - self.base.update(temperature) - return self.base.eval(temperature) - - def ddT(self, double temperature, pressure=None): - if pressure: - self.base.update(temperature, pressure) - return self.base.ddT(temperature, pressure) - else: - self.base.update(temperature) - return self.base.ddT(temperature) - - -cdef class CustomRate(_ReactionRate): - 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)) - """ - def __cinit__(self, k=None, init=True): - - if init: - self._base.reset(new CxxCustomFunc1Rate()) - self.base = self._base.get() - self.rate = (self.base) - self.set_rate_function(k) - - 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)) - """ - if k is None: - self._rate_func = None - return - - if isinstance(k, Func1): - self._rate_func = k - else: - self._rate_func = Func1(k) - - self.rate.setRateFunction(self._rate_func._func) - - -cdef class ArrheniusRate(_ReactionRate): - r""" - A reaction rate coefficient which depends on temperature only and follows - the modified Arrhenius form: - - .. 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`. - """ - def __cinit__(self, A=0, b=0, E=0, init=True): - - if init: - self._base.reset(new CxxArrheniusRate(A, b, E)) - self.base = self._base.get() - self.rate = (self.base) - - @staticmethod - cdef wrap(shared_ptr[CxxReactionRateBase] rate): - """ - Wrap a C++ ReactionRateBase 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.preExponentialFactor() - - property temperature_exponent: - """ - The temperature exponent *b*. - """ - def __get__(self): - return self.rate.temperatureExponent() - - property activation_energy: - """ - The activation energy *E* [J/kmol]. - """ - def __get__(self): - return self.rate.activationEnergy() - - property allow_negative_pre_exponential_factor: - """ - Get/Set whether the rate coefficient is allowed to have a negative - pre-exponential factor. - """ - def __get__(self): - return self.rate.allow_negative_pre_exponential_factor - def __set__(self, allow): - self.rate.allow_negative_pre_exponential_factor = allow - - -cdef class PlogRate(_ReactionRate): - r""" - A pressure-dependent reaction rate parameterized by logarithmically - interpolating between Arrhenius rate expressions at various pressures. - """ - def __cinit__(self, rates=None, init=True): - - if rates and init: - self.rates = rates - - @staticmethod - cdef wrap(shared_ptr[CxxReactionRateBase] rate): - """ - Wrap a C++ ReactionRateBase object with a Python object. - """ - # wrap C++ reaction - cdef PlogRate arr - arr = PlogRate(init=False) - arr._base = rate - arr.base = arr._base.get() - arr.rate = (arr.base) - return arr - - property rates: - """ - Get/Set the rate coefficients for this reaction, which are given as a - list of (pressure, `Arrhenius`) tuples. - """ - def __get__(self): - rates = [] - cdef vector[pair[double, CxxArrhenius]] cxxrates = self.rate.rates() - cdef pair[double, CxxArrhenius] p_rate - for p_rate in cxxrates: - rates.append((p_rate.first, copyArrhenius(&p_rate.second))) - return rates - - def __set__(self, rates): - cdef multimap[double, CxxArrhenius] ratemap - cdef Arrhenius rate - cdef pair[double, CxxArrhenius] item - for p, rate in rates: - item.first = p - item.second = deref(rate.rate) - ratemap.insert(item) - - self._base.reset(new CxxPlogRate(ratemap)) - self.base = self._base.get() - self.rate = (self.base) - - -cdef class ChebyshevRate(_ReactionRate): - r""" - A pressure-dependent reaction rate parameterized by a bivariate Chebyshev - polynomial in temperature and pressure. - """ - def __cinit__(self, Tmin=None, Tmax=None, Pmin=None, Pmax=None, data=None, - init=True): - - if Tmin and Tmax and Pmin and Pmax and data is not None and init: - self._setup(Tmin, Tmax, Pmin, Pmax, data) - - def _setup(self, Tmin, Tmax, Pmin, Pmax, coeffs): - """ - Simultaneously set values for `Tmin`, `Tmax`, `Pmin`, `Pmax`, and - `coeffs`. - """ - cdef CxxArray2D data - data.resize(len(coeffs), len(coeffs[0])) - cdef double value - cdef int i - cdef int j - for i,row in enumerate(coeffs): - for j,value in enumerate(row): - CxxArray2D_set(data, i, j, value) - - self._base.reset(new CxxChebyshevRate3(Tmin, Tmax, Pmin, Pmax, data)) - self.base = self._base.get() - self.rate = (self.base) - - @staticmethod - cdef wrap(shared_ptr[CxxReactionRateBase] rate): - """ - Wrap a C++ ReactionRateBase object with a Python object. - """ - # wrap C++ reaction - cdef ChebyshevRate arr - arr = ChebyshevRate(init=False) - arr._base = rate - arr.base = arr._base.get() - arr.rate = (arr.base) - return arr - - property Tmin: - """ Minimum temperature [K] for the Chebyshev fit """ - def __get__(self): - return self.rate.Tmin() - - property Tmax: - """ Maximum temperature [K] for the Chebyshev fit """ - def __get__(self): - return self.rate.Tmax() - - property Pmin: - """ Minimum pressure [Pa] for the Chebyshev fit """ - def __get__(self): - return self.rate.Pmin() - - property Pmax: - """ Maximum pressure [Pa] for the Chebyshev fit """ - def __get__(self): - return self.rate.Pmax() - - property nPressure: - """ Number of pressures over which the Chebyshev fit is computed """ - def __get__(self): - return self.rate.nPressure() - - property nTemperature: - """ Number of temperatures over which the Chebyshev fit is computed """ - def __get__(self): - return self.rate.nTemperature() - - property coeffs: - """ - 2D array of Chebyshev coefficients of size `(nTemperature, nPressure)`. - """ - def __get__(self): - c = np.fromiter(self.rate.coeffs(), np.double) - return c.reshape((self.rate.nTemperature(), self.rate.nPressure())) - - cdef class ElementaryReaction(Reaction): """ A reaction which follows mass-action kinetics with a modified Arrhenius @@ -1152,6 +1152,7 @@ cdef class ChebyshevReaction(Reaction): r.rate.update_C(&logP) return r.rate.updateRC(logT, recipT) + cdef class BlowersMasel: """ A reaction rate coefficient which depends on temperature and enthalpy change @@ -1219,12 +1220,14 @@ cdef class BlowersMasel: cdef double recipT = 1/T return self.rate.updateRC(logT, recipT, dH) + cdef wrapBlowersMasel(CxxBlowersMasel* rate, Reaction reaction): r = BlowersMasel(init=False) r.rate = rate r.reaction = reaction return r + cdef class BlowersMaselReaction(Reaction): """ A reaction which follows mass-action kinetics with Blowers Masel @@ -1253,7 +1256,151 @@ cdef class BlowersMaselReaction(Reaction): cdef CxxBlowersMaselReaction* r = self.reaction r.allow_negative_pre_exponential_factor = allow -cdef class ElementaryReaction3(Reaction): + +cdef class InterfaceReaction(ElementaryReaction): + """ A reaction occurring on an `Interface` (i.e. a surface or an edge) """ + reaction_type = "interface" + + property coverage_deps: + """ + Get/Set a dict containing adjustments to the Arrhenius rate expression + 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], + and the activation energy [J/kmol], respectively. + """ + def __get__(self): + cdef CxxInterfaceReaction* r = self.reaction + deps = {} + cdef pair[string,CxxCoverageDependency] item + for item in r.coverage_deps: + deps[pystr(item.first)] = (item.second.a, item.second.m, + item.second.E * gas_constant) + return deps + def __set__(self, deps): + cdef CxxInterfaceReaction* r = self.reaction + r.coverage_deps.clear() + cdef str species + for species, D in deps.items(): + r.coverage_deps[stringify(species)] = CxxCoverageDependency( + D[0], D[2] / gas_constant, D[1]) + + property is_sticking_coefficient: + """ + Get/Set a boolean indicating if the rate coefficient for this reaction + is expressed as a sticking coefficient rather than the forward rate + constant. + """ + def __get__(self): + cdef CxxInterfaceReaction* r = self.reaction + return r.is_sticking_coefficient + def __set__(self, stick): + cdef CxxInterfaceReaction* r = self.reaction + r.is_sticking_coefficient = stick + + property use_motz_wise_correction: + """ + Get/Set a boolean indicating whether to use the correction factor + developed by Motz & Wise for reactions with high (near-unity) sticking + coefficients when converting the sticking coefficient to a rate + coefficient. + """ + def __get__(self): + cdef CxxInterfaceReaction* r = self.reaction + return r.use_motz_wise_correction + def __set__(self, mw): + cdef CxxInterfaceReaction* r = self.reaction + r.use_motz_wise_correction = mw + + property sticking_species: + """ + The name of the sticking species. Needed only for reactions with + multiple non-surface reactant species, where the sticking species is + ambiguous. + """ + def __get__(self): + cdef CxxInterfaceReaction* r = self.reaction + return pystr(r.sticking_species) + def __set__(self, species): + cdef CxxInterfaceReaction* r = self.reaction + r.sticking_species = stringify(species) + + +cdef class BlowersMaselInterfaceReaction(BlowersMaselReaction): + """ + A reaction occurring on an `Interface` (i.e. a surface or an edge) + with the rate parameterization of `BlowersMasel`. + """ + reaction_type = "surface-Blowers-Masel" + + property coverage_deps: + """ + Get/Set a dict containing adjustments to the Arrhenius rate expression + 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], + and the activation energy [J/kmol], respectively. + """ + def __get__(self): + cdef CxxBlowersMaselInterfaceReaction* r = self.reaction + deps = {} + cdef pair[string,CxxCoverageDependency] item + for item in r.coverage_deps: + deps[pystr(item.first)] = (item.second.a, item.second.m, + item.second.E * gas_constant) + return deps + def __set__(self, deps): + cdef CxxBlowersMaselInterfaceReaction* r = self.reaction + r.coverage_deps.clear() + cdef str species + for species, D in deps.items(): + r.coverage_deps[stringify(species)] = CxxCoverageDependency( + D[0], D[2] / gas_constant, D[1]) + + property is_sticking_coefficient: + """ + Get/Set a boolean indicating if the rate coefficient for this reaction + is expressed as a sticking coefficient rather than the forward rate + constant. + """ + def __get__(self): + cdef CxxBlowersMaselInterfaceReaction* r = self.reaction + return r.is_sticking_coefficient + def __set__(self, stick): + cdef CxxBlowersMaselInterfaceReaction* r = self.reaction + r.is_sticking_coefficient = stick + + property use_motz_wise_correction: + """ + Get/Set a boolean indicating whether to use the correction factor + developed by Motz & Wise for reactions with high (near-unity) sticking + coefficients when converting the sticking coefficient to a rate + coefficient. + """ + def __get__(self): + cdef CxxBlowersMaselInterfaceReaction* r = self.reaction + return r.use_motz_wise_correction + def __set__(self, mw): + cdef CxxBlowersMaselInterfaceReaction* r = self.reaction + r.use_motz_wise_correction = mw + + property sticking_species: + """ + The name of the sticking species. Needed only for reactions with + multiple non-surface reactant species, where the sticking species is + ambiguous. + """ + def __get__(self): + cdef CxxBlowersMaselInterfaceReaction* r = self.reaction + return pystr(r.sticking_species) + def __set__(self, species): + cdef CxxBlowersMaselInterfaceReaction* r = self.reaction + r.sticking_species = stringify(species) + + +cdef class ElementaryReaction3(Reaction): """ A reaction which follows mass-action kinetics with a modified Arrhenius reaction rate. @@ -1551,145 +1698,3 @@ cdef class CustomReaction(Reaction): self._rate = rate cdef CxxCustomFunc1Reaction* r = self.reaction r.setRate(self._rate._base) - - -cdef class InterfaceReaction(ElementaryReaction): - """ A reaction occurring on an `Interface` (i.e. a surface or an edge) """ - reaction_type = "interface" - - property coverage_deps: - """ - Get/Set a dict containing adjustments to the Arrhenius rate expression - 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], - and the activation energy [J/kmol], respectively. - """ - def __get__(self): - cdef CxxInterfaceReaction* r = self.reaction - deps = {} - cdef pair[string,CxxCoverageDependency] item - for item in r.coverage_deps: - deps[pystr(item.first)] = (item.second.a, item.second.m, - item.second.E * gas_constant) - return deps - def __set__(self, deps): - cdef CxxInterfaceReaction* r = self.reaction - r.coverage_deps.clear() - cdef str species - for species, D in deps.items(): - r.coverage_deps[stringify(species)] = CxxCoverageDependency( - D[0], D[2] / gas_constant, D[1]) - - property is_sticking_coefficient: - """ - Get/Set a boolean indicating if the rate coefficient for this reaction - is expressed as a sticking coefficient rather than the forward rate - constant. - """ - def __get__(self): - cdef CxxInterfaceReaction* r = self.reaction - return r.is_sticking_coefficient - def __set__(self, stick): - cdef CxxInterfaceReaction* r = self.reaction - r.is_sticking_coefficient = stick - - property use_motz_wise_correction: - """ - Get/Set a boolean indicating whether to use the correction factor - developed by Motz & Wise for reactions with high (near-unity) sticking - coefficients when converting the sticking coefficient to a rate - coefficient. - """ - def __get__(self): - cdef CxxInterfaceReaction* r = self.reaction - return r.use_motz_wise_correction - def __set__(self, mw): - cdef CxxInterfaceReaction* r = self.reaction - r.use_motz_wise_correction = mw - - property sticking_species: - """ - The name of the sticking species. Needed only for reactions with - multiple non-surface reactant species, where the sticking species is - ambiguous. - """ - def __get__(self): - cdef CxxInterfaceReaction* r = self.reaction - return pystr(r.sticking_species) - def __set__(self, species): - cdef CxxInterfaceReaction* r = self.reaction - r.sticking_species = stringify(species) - -cdef class BlowersMaselInterfaceReaction(BlowersMaselReaction): - """ - A reaction occurring on an `Interface` (i.e. a surface or an edge) - with the rate parameterization of `BlowersMasel`. - """ - reaction_type = "surface-Blowers-Masel" - - property coverage_deps: - """ - Get/Set a dict containing adjustments to the Arrhenius rate expression - 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], - and the activation energy [J/kmol], respectively. - """ - def __get__(self): - cdef CxxBlowersMaselInterfaceReaction* r = self.reaction - deps = {} - cdef pair[string,CxxCoverageDependency] item - for item in r.coverage_deps: - deps[pystr(item.first)] = (item.second.a, item.second.m, - item.second.E * gas_constant) - return deps - def __set__(self, deps): - cdef CxxBlowersMaselInterfaceReaction* r = self.reaction - r.coverage_deps.clear() - cdef str species - for species, D in deps.items(): - r.coverage_deps[stringify(species)] = CxxCoverageDependency( - D[0], D[2] / gas_constant, D[1]) - - property is_sticking_coefficient: - """ - Get/Set a boolean indicating if the rate coefficient for this reaction - is expressed as a sticking coefficient rather than the forward rate - constant. - """ - def __get__(self): - cdef CxxBlowersMaselInterfaceReaction* r = self.reaction - return r.is_sticking_coefficient - def __set__(self, stick): - cdef CxxBlowersMaselInterfaceReaction* r = self.reaction - r.is_sticking_coefficient = stick - - property use_motz_wise_correction: - """ - Get/Set a boolean indicating whether to use the correction factor - developed by Motz & Wise for reactions with high (near-unity) sticking - coefficients when converting the sticking coefficient to a rate - coefficient. - """ - def __get__(self): - cdef CxxBlowersMaselInterfaceReaction* r = self.reaction - return r.use_motz_wise_correction - def __set__(self, mw): - cdef CxxBlowersMaselInterfaceReaction* r = self.reaction - r.use_motz_wise_correction = mw - - property sticking_species: - """ - The name of the sticking species. Needed only for reactions with - multiple non-surface reactant species, where the sticking species is - ambiguous. - """ - def __get__(self): - cdef CxxBlowersMaselInterfaceReaction* r = self.reaction - return pystr(r.sticking_species) - def __set__(self, species): - cdef CxxBlowersMaselInterfaceReaction* r = self.reaction - r.sticking_species = stringify(species) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index c55d8e6fb8b..fa10afa9d93 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -340,6 +340,7 @@ class TestElementary(TestReaction): rate-constant: {A: 38.7, b: 2.7, Ea: 6260.0 cal/mol} """ + class TestElementary3(TestElementary): _cls = ct.ElementaryReaction3 @@ -350,48 +351,6 @@ class TestElementary3(TestElementary): rate-constant: {A: 38.7, b: 2.7, Ea: 6260.0 cal/mol} """ -class TestCustom(TestReaction): - - # 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 = 0 - _type = "custom-rate-function" - _yaml = None - - 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) - 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) - class TestThreeBody(TestReaction): @@ -519,3 +478,46 @@ class TestChebyshev3(TestChebyshev): - [1.9764, 1.0037, 7.2865e-03, -0.030432] - [0.3177, 0.26889, 0.094806, -7.6385e-03] """ + + +class TestCustom(TestReaction): + + # 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 = 0 + _type = "custom-rate-function" + _yaml = None + + 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) + 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) diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 6f64b320941..66f9f6b2468 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -646,6 +646,185 @@ ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, reaction_type = CHEBYSHEV_RXN; } +void ChebyshevReaction::getParameters(AnyMap& reactionNode) const +{ + Reaction::getParameters(reactionNode); + reactionNode["type"] = "Chebyshev"; + AnyMap rateNode; + rate.getParameters(rateNode, rate_units); + reactionNode.update(rateNode); +} + +InterfaceReaction::InterfaceReaction() + : is_sticking_coefficient(false) + , use_motz_wise_correction(false) +{ + reaction_type = INTERFACE_RXN; +} + +InterfaceReaction::InterfaceReaction(const Composition& reactants_, + const Composition& products_, + const Arrhenius& rate_, + bool isStick) + : ElementaryReaction(reactants_, products_, rate_) + , is_sticking_coefficient(isStick) + , use_motz_wise_correction(false) +{ + reaction_type = INTERFACE_RXN; +} + +void InterfaceReaction::calculateRateCoeffUnits(const Kinetics& kin) +{ + ElementaryReaction::calculateRateCoeffUnits(kin); + if (is_sticking_coefficient || input.hasKey("sticking-coefficient")) { + rate_units = Units(1.0); // sticking coefficients are dimensionless + } +} + +void InterfaceReaction::getParameters(AnyMap& reactionNode) const +{ + ElementaryReaction::getParameters(reactionNode); + if (is_sticking_coefficient) { + reactionNode["sticking-coefficient"] = std::move(reactionNode["rate-constant"]); + reactionNode.erase("rate-constant"); + } + if (use_motz_wise_correction) { + reactionNode["Motz-Wise"] = true; + } + if (!sticking_species.empty()) { + reactionNode["sticking-species"] = sticking_species; + } + if (!coverage_deps.empty()) { + AnyMap deps; + for (const auto& d : coverage_deps) { + AnyMap dep; + dep["a"] = d.second.a; + dep["m"] = d.second.m; + dep["E"].setQuantity(d.second.E, "K", true); + deps[d.first] = std::move(dep); + } + reactionNode["coverage-dependencies"] = std::move(deps); + } +} + +ElectrochemicalReaction::ElectrochemicalReaction() + : beta(0.5) + , exchange_current_density_formulation(false) +{ +} + +ElectrochemicalReaction::ElectrochemicalReaction(const Composition& reactants_, + const Composition& products_, + const Arrhenius& rate_) + : InterfaceReaction(reactants_, products_, rate_) + , beta(0.5) + , exchange_current_density_formulation(false) +{ +} + +void ElectrochemicalReaction::getParameters(AnyMap& reactionNode) const +{ + InterfaceReaction::getParameters(reactionNode); + if (beta != 0.5) { + reactionNode["beta"] = beta; + } + if (exchange_current_density_formulation) { + reactionNode["exchange-current-density-formulation"] = true; + } +} + +BlowersMaselReaction::BlowersMaselReaction() + : allow_negative_pre_exponential_factor(false) +{ + reaction_type = BLOWERSMASEL_RXN; +} + +void BlowersMaselReaction::validate() +{ + Reaction::validate(); + if (!allow_negative_pre_exponential_factor && + rate.preExponentialFactor() < 0) { + throw CanteraError("BlowersMaselReaction::validate", + "Undeclared negative pre-exponential factor found in reaction '" + + equation() + "'"); + } +} + +BlowersMaselReaction::BlowersMaselReaction(const Composition& reactants_, + const Composition& products_, + const BlowersMasel& rate_) + : Reaction(reactants_, products_) + , rate(rate_) + , allow_negative_pre_exponential_factor(false) +{ + reaction_type = BLOWERSMASEL_RXN; +} + +void BlowersMaselReaction::getParameters(AnyMap& reactionNode) const +{ + Reaction::getParameters(reactionNode); + reactionNode["type"] = "Blowers-Masel"; + if (allow_negative_pre_exponential_factor) { + reactionNode["negative-A"] = true; + } + AnyMap rateNode; + rate.getParameters(rateNode, rate_units); + reactionNode["rate-constant"] = std::move(rateNode); +} + +BlowersMaselInterfaceReaction::BlowersMaselInterfaceReaction() + : is_sticking_coefficient(false) + , use_motz_wise_correction(false) +{ + reaction_type = BMINTERFACE_RXN; +} + +BlowersMaselInterfaceReaction::BlowersMaselInterfaceReaction(const Composition& reactants_, + const Composition& products_, + const BlowersMasel& rate_, + bool isStick) + : BlowersMaselReaction(reactants_, products_, rate_) + , is_sticking_coefficient(isStick) + , use_motz_wise_correction(false) +{ + reaction_type = BMINTERFACE_RXN; +} + +void BlowersMaselInterfaceReaction::calculateRateCoeffUnits(const Kinetics& kin) +{ + BlowersMaselReaction::calculateRateCoeffUnits(kin); + if (is_sticking_coefficient || input.hasKey("sticking-coefficient")) { + rate_units = Units(1.0); // sticking coefficients are dimensionless + } +} + +void BlowersMaselInterfaceReaction::getParameters(AnyMap& reactionNode) const +{ + BlowersMaselReaction::getParameters(reactionNode); + reactionNode["type"] = "Blowers-Masel"; + if (is_sticking_coefficient) { + reactionNode["sticking-coefficient"] = std::move(reactionNode["rate-constant"]); + reactionNode.erase("rate-constant"); + } + if (use_motz_wise_correction) { + reactionNode["Motz-Wise"] = true; + } + if (!sticking_species.empty()) { + reactionNode["sticking-species"] = sticking_species; + } + if (!coverage_deps.empty()) { + AnyMap deps; + for (const auto& d : coverage_deps) { + AnyMap dep; + dep["a"] = d.second.a; + dep["m"] = d.second.m; + dep["E"].setQuantity(d.second.E, "K", true); + deps[d.first] = std::move(dep); + } + reactionNode["coverage-dependencies"] = std::move(deps); + } +} + Reaction3::Reaction3(const Composition& reactants, const Composition& products) : Reaction(reactants, products) { @@ -964,185 +1143,6 @@ CustomFunc1Reaction::CustomFunc1Reaction(const AnyMap& node, const Kinetics& kin setRate(std::shared_ptr(new CustomFunc1Rate(node, rate_units))); } -void ChebyshevReaction::getParameters(AnyMap& reactionNode) const -{ - Reaction::getParameters(reactionNode); - reactionNode["type"] = "Chebyshev"; - AnyMap rateNode; - rate.getParameters(rateNode, rate_units); - reactionNode.update(rateNode); -} - -InterfaceReaction::InterfaceReaction() - : is_sticking_coefficient(false) - , use_motz_wise_correction(false) -{ - reaction_type = INTERFACE_RXN; -} - -InterfaceReaction::InterfaceReaction(const Composition& reactants_, - const Composition& products_, - const Arrhenius& rate_, - bool isStick) - : ElementaryReaction(reactants_, products_, rate_) - , is_sticking_coefficient(isStick) - , use_motz_wise_correction(false) -{ - reaction_type = INTERFACE_RXN; -} - -void InterfaceReaction::calculateRateCoeffUnits(const Kinetics& kin) -{ - ElementaryReaction::calculateRateCoeffUnits(kin); - if (is_sticking_coefficient || input.hasKey("sticking-coefficient")) { - rate_units = Units(1.0); // sticking coefficients are dimensionless - } -} - -void InterfaceReaction::getParameters(AnyMap& reactionNode) const -{ - ElementaryReaction::getParameters(reactionNode); - if (is_sticking_coefficient) { - reactionNode["sticking-coefficient"] = std::move(reactionNode["rate-constant"]); - reactionNode.erase("rate-constant"); - } - if (use_motz_wise_correction) { - reactionNode["Motz-Wise"] = true; - } - if (!sticking_species.empty()) { - reactionNode["sticking-species"] = sticking_species; - } - if (!coverage_deps.empty()) { - AnyMap deps; - for (const auto& d : coverage_deps) { - AnyMap dep; - dep["a"] = d.second.a; - dep["m"] = d.second.m; - dep["E"].setQuantity(d.second.E, "K", true); - deps[d.first] = std::move(dep); - } - reactionNode["coverage-dependencies"] = std::move(deps); - } -} - -ElectrochemicalReaction::ElectrochemicalReaction() - : beta(0.5) - , exchange_current_density_formulation(false) -{ -} - -ElectrochemicalReaction::ElectrochemicalReaction(const Composition& reactants_, - const Composition& products_, - const Arrhenius& rate_) - : InterfaceReaction(reactants_, products_, rate_) - , beta(0.5) - , exchange_current_density_formulation(false) -{ -} - -void ElectrochemicalReaction::getParameters(AnyMap& reactionNode) const -{ - InterfaceReaction::getParameters(reactionNode); - if (beta != 0.5) { - reactionNode["beta"] = beta; - } - if (exchange_current_density_formulation) { - reactionNode["exchange-current-density-formulation"] = true; - } -} - -BlowersMaselReaction::BlowersMaselReaction() - : allow_negative_pre_exponential_factor(false) -{ - reaction_type = BLOWERSMASEL_RXN; -} - -void BlowersMaselReaction::validate() -{ - Reaction::validate(); - if (!allow_negative_pre_exponential_factor && - rate.preExponentialFactor() < 0) { - throw InputFileError("BlowersMaselReaction::validate", input, - "Undeclared negative pre-exponential factor found in reaction '" - + equation() + "'"); - } -} - -BlowersMaselReaction::BlowersMaselReaction(const Composition& reactants_, - const Composition& products_, - const BlowersMasel& rate_) - : Reaction(reactants_, products_) - , rate(rate_) - , allow_negative_pre_exponential_factor(false) -{ - reaction_type = BLOWERSMASEL_RXN; -} - -void BlowersMaselReaction::getParameters(AnyMap& reactionNode) const -{ - Reaction::getParameters(reactionNode); - reactionNode["type"] = "Blowers-Masel"; - if (allow_negative_pre_exponential_factor) { - reactionNode["negative-A"] = true; - } - AnyMap rateNode; - rate.getParameters(rateNode, rate_units); - reactionNode["rate-constant"] = std::move(rateNode); -} - -BlowersMaselInterfaceReaction::BlowersMaselInterfaceReaction() - : is_sticking_coefficient(false) - , use_motz_wise_correction(false) -{ - reaction_type = BMINTERFACE_RXN; -} - -BlowersMaselInterfaceReaction::BlowersMaselInterfaceReaction(const Composition& reactants_, - const Composition& products_, - const BlowersMasel& rate_, - bool isStick) - : BlowersMaselReaction(reactants_, products_, rate_) - , is_sticking_coefficient(isStick) - , use_motz_wise_correction(false) -{ - reaction_type = BMINTERFACE_RXN; -} - -void BlowersMaselInterfaceReaction::calculateRateCoeffUnits(const Kinetics& kin) -{ - BlowersMaselReaction::calculateRateCoeffUnits(kin); - if (is_sticking_coefficient || input.hasKey("sticking-coefficient")) { - rate_units = Units(1.0); // sticking coefficients are dimensionless - } -} - -void BlowersMaselInterfaceReaction::getParameters(AnyMap& reactionNode) const -{ - BlowersMaselReaction::getParameters(reactionNode); - reactionNode["type"] = "Blowers-Masel"; - if (is_sticking_coefficient) { - reactionNode["sticking-coefficient"] = std::move(reactionNode["rate-constant"]); - reactionNode.erase("rate-constant"); - } - if (use_motz_wise_correction) { - reactionNode["Motz-Wise"] = true; - } - if (!sticking_species.empty()) { - reactionNode["sticking-species"] = sticking_species; - } - if (!coverage_deps.empty()) { - AnyMap deps; - for (const auto& d : coverage_deps) { - AnyMap dep; - dep["a"] = d.second.a; - dep["m"] = d.second.m; - dep["E"].setQuantity(d.second.E, "K", true); - deps[d.first] = std::move(dep); - } - reactionNode["coverage-dependencies"] = std::move(deps); - } -} - Arrhenius readArrhenius(const XML_Node& arrhenius_node) { return Arrhenius(getFloat(arrhenius_node, "A", "toSI"), From 357ad68ecc5db8b890d6f5e23a9b9f52b592dc55 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 27 Apr 2021 06:10:12 -0500 Subject: [PATCH 48/84] [Kinetics] Streamline setParameters --- include/cantera/kinetics/Reaction.h | 6 +++--- include/cantera/kinetics/ReactionRate.h | 15 +++++---------- include/cantera/kinetics/RxnRates.h | 2 +- interfaces/cython/cantera/reaction.pyx | 8 +++----- src/kinetics/Reaction.cpp | 23 +++++++++++------------ src/kinetics/ReactionRate.cpp | 23 ++++++++++++++--------- 6 files changed, 37 insertions(+), 40 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 4e0a519611a..00e0d650227 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -473,7 +473,7 @@ class Reaction3 : public Reaction } //! Set up reaction based on AnyMap *node* - virtual bool setParameters(const AnyMap& node, const Kinetics& kin); + virtual void setParameters(const AnyMap& node, const Kinetics& kin); //! Get pointer to third-body shared_ptr thirdBody() { @@ -527,7 +527,7 @@ class ThreeBodyReaction3 : public ElementaryReaction3 bool detectEfficiencies(); virtual void calculateRateCoeffUnits(const Kinetics& kin); - virtual bool setParameters(const AnyMap& node, const Kinetics& kin); + virtual void setParameters(const AnyMap& node, const Kinetics& kin); virtual void getParameters(AnyMap& reactionNode) const; virtual std::string reactantString() const; @@ -570,7 +570,7 @@ class ChebyshevReaction3 : public Reaction3 return "Chebyshev"; } - virtual bool setParameters(const AnyMap& node, const Kinetics& kin); + virtual void setParameters(const AnyMap& node, const Kinetics& kin); virtual void getParameters(AnyMap& reactionNode) const; }; diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index c34e211f06a..0cd16bd9dd9 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -40,7 +40,7 @@ class ReactionRateBase //! Set parameters //! @param node AnyMap object containing reaction rate specification //! @param rate_units Description of units used for rate parameters - virtual bool setParameters(const AnyMap& node, const Units& rate_units) = 0; + virtual void setParameters(const AnyMap& node, const Units& rate_units) = 0; //! Get parameters //! Store the parameters of a ReactionRate needed to reconstruct an identical @@ -203,10 +203,9 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius //! (optional, default is false) ArrheniusRate(const Arrhenius& arr, bool allow_negative_A=false); + virtual void setParameters(const AnyMap& node, const Units& rate_units) override; virtual void getParameters(AnyMap& rateNode, const Units& rate_units) const override; - virtual bool setParameters(const AnyMap& node, - const Units& rate_units) override; virtual std::string type() const override { return "ArrheniusRate"; } @@ -268,8 +267,7 @@ class PlogRate final : public ReactionRate, public Plog virtual std::string type() const override { return "PlogRate"; } - virtual bool setParameters(const AnyMap& node, - const Units& rate_units) override; + virtual void setParameters(const AnyMap& node, const Units& rate_units) override; virtual void getParameters(AnyMap& rateNode, const Units& rate_units) const override; @@ -346,8 +344,7 @@ class ChebyshevRate3 final : public ReactionRate, public Chebyshe virtual std::string type() const override { return "ChebyshevRate"; } - virtual bool setParameters(const AnyMap& node, - const Units& rate_units) override; + virtual void setParameters(const AnyMap& node, const Units& rate_units) override; virtual void getParameters(AnyMap& rateNode, const Units& rate_units) const override; @@ -384,9 +381,7 @@ class CustomFunc1Rate final : public ReactionRate virtual std::string type() const override { return "custom-function"; } - virtual bool setParameters(const AnyMap& node, const Units& rate_units) { - return true; - } + virtual void setParameters(const AnyMap& node, const Units& rate_units) override {} //! Update information specific to reaction static bool uses_update() { return false; } diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index e87c7a09865..ba17f3aaf8d 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -457,7 +457,7 @@ class Chebyshev { public: //! Default constructor. - Chebyshev() {} + Chebyshev() : nP_(0), nT_(0) {} //! Constructor directly from coefficient array /* diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 422eba1b252..543551c2750 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -1435,7 +1435,7 @@ cdef class ElementaryReaction3(Reaction): if isinstance(rate, dict): spec['rate-constant'] = rate elif isinstance(rate, ArrheniusRate) or rate is None: - spec['rate-constant'] = dict.fromkeys(['A', 'b', 'Ea'], 0.) + pass else: raise TypeError("Invalid rate definition") @@ -1496,7 +1496,7 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): if isinstance(rate, dict): spec['rate-constant'] = rate elif isinstance(rate, ArrheniusRate) or rate is None: - spec['rate-constant'] = dict.fromkeys(['A', 'b', 'Ea'], 0.) + pass else: raise TypeError("Invalid rate definition") @@ -1582,9 +1582,7 @@ cdef class PlogReaction3(Reaction): for r in rate: spec['rate-constants'].append({'P': r['P'], **r['rate-constant']}) elif isinstance(rate, PlogRate) or rate is None: - spec['rate-constants'] = [ - {"P": 1, **dict.fromkeys(['A', 'b', 'Ea'], 0.)}, - {"P": 1e7, **dict.fromkeys(['A', 'b', 'Ea'], 0.)}] + pass else: raise TypeError("Invalid rate definition") diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 66f9f6b2468..66ce3127050 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -830,11 +830,11 @@ Reaction3::Reaction3(const Composition& reactants, const Composition& products) { } -bool Reaction3::setParameters(const AnyMap& node, const Kinetics& kin) +void Reaction3::setParameters(const AnyMap& node, const Kinetics& kin) { if (!node.hasKey("equation")) { // empty node: used by newReaction() factory loader - return false; + return; } parseReactionEquation(*this, node["equation"], kin); @@ -856,7 +856,6 @@ bool Reaction3::setParameters(const AnyMap& node, const Kinetics& kin) calculateRateCoeffUnits(kin); input = node; - return true; } void Reaction3::validate() @@ -981,24 +980,25 @@ void ThreeBodyReaction3::calculateRateCoeffUnits(const Kinetics& kin) } } -bool ThreeBodyReaction3::setParameters(const AnyMap& node, const Kinetics& kin) +void ThreeBodyReaction3::setParameters(const AnyMap& node, const Kinetics& kin) { - if (!ElementaryReaction3::setParameters(node, kin)) { - return false; + if (!node.hasKey("equation")) { + // empty node: used by newReaction() factory loader + return; } - + ElementaryReaction3::setParameters(node, kin); if (reactants.count("M") != 1 || products.count("M") != 1) { if (!detectEfficiencies()) { throw InputFileError("ThreeBodyReaction3::setParameters", node["equation"], "Reaction equation '{}' does not contain third body 'M'", node["equation"].asString()); } - return true; + return; } reactants.erase("M"); products.erase("M"); - return m_third_body->setEfficiencies(node); + m_third_body->setEfficiencies(node); } void ThreeBodyReaction3::getParameters(AnyMap& reactionNode) const @@ -1082,11 +1082,11 @@ ChebyshevReaction3::ChebyshevReaction3(const AnyMap& node, const Kinetics& kin) setRate(std::shared_ptr(new ChebyshevRate3(node, rate_units))); } -bool ChebyshevReaction3::setParameters(const AnyMap& node, const Kinetics& kin) +void ChebyshevReaction3::setParameters(const AnyMap& node, const Kinetics& kin) { if (!node.hasKey("equation")) { // empty node: used by newReaction() factory loader - return false; + return; } parseReactionEquation(*this, node["equation"], kin); @@ -1111,7 +1111,6 @@ bool ChebyshevReaction3::setParameters(const AnyMap& node, const Kinetics& kin) calculateRateCoeffUnits(kin); input = node; - return true; } void ChebyshevReaction3::getParameters(AnyMap& reactionNode) const diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index b6c2e221c1e..9d3aa6d66ea 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -34,13 +34,12 @@ ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rate_units) { setParameters(node, rate_units); } -bool ArrheniusRate::setParameters(const AnyMap& node, const Units& rate_units) { +void ArrheniusRate::setParameters(const AnyMap& node, const Units& rate_units) { allow_negative_pre_exponential_factor = node.getBool("negative-A", false); if (!node.hasKey("rate-constant")) { - return false; + return; } Arrhenius::setParameters(node["rate-constant"], node.units(), rate_units); - return true; } void ArrheniusRate::getParameters(AnyMap& rateNode, @@ -69,13 +68,19 @@ PlogRate::PlogRate(const AnyMap& node, const Units& rate_units) setParameters(node, rate_units); } -bool PlogRate::setParameters(const AnyMap& node, const Units& rate_units) { +void PlogRate::setParameters(const AnyMap& node, const Units& rate_units) { if (!node.hasKey("rate-constants")) { - return false; + // ensure that Plog has defined state and produces zero reaction rate + AnyMap rate = AnyMap::fromYamlString( + "rate-constants:\n" + "- {P: 1e-7, A: 0., b: 0., Ea: 0.}\n" + "- {P: 1e7, A: 0., b: 0., Ea: 0.}"); + Plog::setParameters(rate.at("rate-constants").asVector(), + node.units(), rate_units); + return; } Plog::setParameters(node.at("rate-constants").asVector(), node.units(), rate_units); - return true; } void PlogRate::getParameters(AnyMap& rateNode, @@ -97,17 +102,17 @@ ChebyshevRate3::ChebyshevRate3(const AnyMap& node, const Units& rate_units) setParameters(node, rate_units); } -bool ChebyshevRate3::setParameters(const AnyMap& node, const Units& rate_units) { +void ChebyshevRate3::setParameters(const AnyMap& node, const Units& rate_units) { if (!node.hasKey("data")) { // ensure that Chebyshev has defined state and produces zero reaction rate AnyMap rate = AnyMap::fromYamlString( "temperature-range: [290, 3000]\n" "pressure-range: [1.e-7, 1.e7]\n" "data: [[-16.]]\n"); - Chebyshev::setParameters(rate, node.units(), rate_units); return false; + Chebyshev::setParameters(rate, node.units(), rate_units); + return; } Chebyshev::setParameters(node, node.units(), rate_units); - return true; } void ChebyshevRate3::getParameters(AnyMap& rateNode, From 97a31c5c20c9eaedc5d06f8b0041750fc9b46503 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 27 Apr 2021 06:30:39 -0500 Subject: [PATCH 49/84] [Kinetics] Update docstrings --- include/cantera/kinetics/GasKinetics.h | 9 ++++++ include/cantera/kinetics/Reaction.h | 6 ++-- include/cantera/kinetics/ReactionData.h | 12 +++++++- include/cantera/kinetics/ReactionRate.h | 8 ++--- include/cantera/kinetics/RxnRates.h | 15 +++++++-- interfaces/cython/cantera/reaction.pyx | 41 +++++++++++++++++-------- 6 files changed, 67 insertions(+), 24 deletions(-) diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index 4d19a3809a6..3386b887177 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -106,15 +106,24 @@ class GasKinetics : public BulkKinetics void processFalloffReactions(); + // functions marked as deprecated below are only used for XML import and + // transitional reaction types marked as '-old' + + //! @deprecated Cantera 2.6 (replaced by MultiRate approach) void addThreeBodyReaction(ThreeBodyReaction& r); void addFalloffReaction(FalloffReaction& r); + //! @deprecated Cantera 2.6 (replaced by MultiRate approach) void addPlogReaction(PlogReaction& r); + //! @deprecated Cantera 2.6 (replaced by MultiRate approach) void addChebyshevReaction(ChebyshevReaction& r); void addBlowersMaselReaction(BlowersMaselReaction& r); + //! @deprecated Cantera 2.6 (replaced by MultiRate approach) void modifyThreeBodyReaction(size_t i, ThreeBodyReaction& r); void modifyFalloffReaction(size_t i, FalloffReaction& r); + //! @deprecated Cantera 2.6 (replaced by MultiRate approach) void modifyPlogReaction(size_t i, PlogReaction& r); + //! @deprecated Cantera 2.6 (replaced by MultiRate approach) void modifyChebyshevReaction(size_t i, ChebyshevReaction& r); void modifyBlowersMaselReaction(size_t i, BlowersMaselReaction& r); diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 00e0d650227..ea691bc1a24 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -634,7 +634,7 @@ void setupElementaryReaction(ElementaryReaction&, const AnyMap&, const Kinetics&); void setupThreeBodyReaction(ThreeBodyReaction&, const XML_Node&); -//! @internal May be changed without notice in future versions +//! @deprecated Cantera 2.6 (replaced by setParameters) void setupThreeBodyReaction(ThreeBodyReaction&, const AnyMap&, const Kinetics&); @@ -647,11 +647,11 @@ void setupChemicallyActivatedReaction(ChemicallyActivatedReaction&, const XML_Node&); void setupPlogReaction(PlogReaction&, const XML_Node&); -//! @internal May be changed without notice in future versions +//! @deprecated Cantera 2.6 (replaced by setParameters) void setupPlogReaction(PlogReaction&, const AnyMap&, const Kinetics&); void setupChebyshevReaction(ChebyshevReaction&, const XML_Node&); -//! @internal May be changed without notice in future versions +//! @deprecated Cantera 2.6 (replaced by setParameters) void setupChebyshevReaction(ChebyshevReaction&, const AnyMap&, const Kinetics&); diff --git a/include/cantera/kinetics/ReactionData.h b/include/cantera/kinetics/ReactionData.h index e013a273191..6a2deed9555 100644 --- a/include/cantera/kinetics/ReactionData.h +++ b/include/cantera/kinetics/ReactionData.h @@ -34,12 +34,14 @@ struct ArrheniusData //! Constructor accessing *bulk* phase definitions ArrheniusData(const ThermoPhase& bulk) { update(bulk); } + //! Update data container based on temperature *T* void update(double T) { m_temperature = T; m_logT = std::log(T); m_recipT = 1./T; } + //! Update data container based on *bulk* phase state void update(const ThermoPhase& bulk); double m_temperature; //!< temperature @@ -66,8 +68,10 @@ struct PlogData //! Constructor accessing *bulk* phase definitions PlogData(const ThermoPhase& bulk) { update(bulk); } + //! Update data container based on temperature *T* (raises exception) void update(double T); + //! Update data container based on temperature *T* and *P* void update(double T, double P) { m_temperature = T; m_logT = std::log(T); @@ -75,6 +79,7 @@ struct PlogData m_logP = std::log(P); } + //! Update data container based on *bulk* phase state void update(const ThermoPhase& bulk); //! Pointer to logP (required by Plog::update_C) @@ -105,14 +110,17 @@ struct ChebyshevData //! Constructor accessing *bulk* phase definitions ChebyshevData(const ThermoPhase& bulk) { update(bulk); } + //! Update data container based on temperature *T* (raises exception) void update(double T); + //! Update data container based on temperature *T* and *P* void update(double T, double P) { m_temperature = T; m_recipT = 1./T; m_log10P = std::log10(P); - } + } + //! Update data container based on *bulk* phase state void update(const ThermoPhase& bulk); //! Pointer to logP (required by Chebyshev::update_C) @@ -138,8 +146,10 @@ struct CustomFunc1Data //! Constructor accessing *bulk* phase definitions CustomFunc1Data(const ThermoPhase& bulk) { update(bulk); } + //! Update data container based on temperature *T* void update(double T) { m_temperature = T; } + //! Update data container based on *bulk* phase state void update(const ThermoPhase& bulk); double m_temperature; //!< temperature diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 0cd16bd9dd9..668b5975c95 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -228,7 +228,7 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius return m_E * GasConstant; } - virtual void validate(const std::string& equation); + virtual void validate(const std::string& equation) override; bool allow_negative_pre_exponential_factor; }; @@ -283,7 +283,7 @@ class PlogRate final : public ReactionRate, public Plog return updateRC(shared_data.m_logT, shared_data.m_recipT); } - virtual void validate(const std::string& equation) { + virtual void validate(const std::string& equation) override { Plog::validate(equation); } }; @@ -360,7 +360,7 @@ class ChebyshevRate3 final : public ReactionRate, public Chebyshe return updateRC(0., shared_data.m_recipT); } - virtual void validate(const std::string& equation); + virtual void validate(const std::string& equation) override; }; @@ -396,7 +396,7 @@ class CustomFunc1Rate final : public ReactionRate virtual double eval(const CustomFunc1Data& shared_data, double concm=0.) const override; - virtual void validate(const std::string& equation) {} + virtual void validate(const std::string& equation) override {} protected: shared_ptr m_ratefunc; diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index ba17f3aaf8d..1270a1548cd 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -48,7 +48,10 @@ class Arrhenius Arrhenius(const AnyValue& rate, const UnitSystem& units, const Units& rate_units); - //! Run object setup based on AnyMap node information + //! Perform object setup based on AnyMap node information + //! @param node AnyMap containing rate information + //! @param units unit system + //! @param rate_units unit definitions specific to rate information void setParameters(const AnyValue& rate, const UnitSystem& units, const Units& rate_units); @@ -329,7 +332,10 @@ class Plog //! Constructor from Arrhenius rate expressions at a set of pressures explicit Plog(const std::multimap& rates); - //! Run object setup based on AnyMap node information + //! Perform object setup based on AnyMap node information + //! @param rates vector of AnyMap containing rate information + //! @param units unit system + //! @param rate_units unit definitions specific to rate information void setParameters(const std::vector& rates, const UnitSystem& units, const Units& rate_units); @@ -472,7 +478,10 @@ class Chebyshev Chebyshev(double Tmin, double Tmax, double Pmin, double Pmax, const Array2D& coeffs); - //! Run object setup based on AnyMap node information + //! Perform object setup based on AnyMap node information + //! @param node AnyMap containing rate information + //! @param units unit system + //! @param rate_units unit definitions specific to rate information void setParameters(const AnyMap& node, const UnitSystem& units, const Units& rate_units); void getParameters(AnyMap& rateNode, const Units& rate_units) const; diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 543551c2750..47d1a973f76 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -7,11 +7,23 @@ cdef dict _reaction_class_registry = {} cdef class _ReactionRate: + """ + Base class for ReactionRate objects. + ReactionRate objects are used to calculate reaction rates and are associated + with a Reaction object. In order to improve computational speed, Kinetics + objects create internal copies that are linked to the original Reaction + definition. + """ def __repr__(self): return "<{} at {:0x}>".format(pystr(self.base.type()), id(self)) def __call__(self, double temperature, pressure=None): + """ + Evaluate rate expression based on temperature and pressure. For rate + expressions that are dependent of pressure, an omission of pressure + will raise an exception. + """ if pressure: self.base.update(temperature, pressure) return self.base.eval(temperature, pressure) @@ -20,6 +32,9 @@ cdef class _ReactionRate: return self.base.eval(temperature) def ddT(self, double temperature, pressure=None): + """ + Evaluate derivative of rate expression with respect to temperature. + """ if pressure: self.base.update(temperature, pressure) return self.base.ddT(temperature, pressure) @@ -1431,9 +1446,9 @@ cdef class ElementaryReaction3(Reaction): if init and equation and kinetics: - spec = {'equation': equation, 'type': self.reaction_type} + spec = {"equation": equation, "type": self.reaction_type} if isinstance(rate, dict): - spec['rate-constant'] = rate + spec["rate-constant"] = rate elif isinstance(rate, ArrheniusRate) or rate is None: pass else: @@ -1492,16 +1507,16 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): if init and equation and kinetics: - spec = {'equation': equation, 'type': self.reaction_type} + spec = {"equation": equation, "type": self.reaction_type} if isinstance(rate, dict): - spec['rate-constant'] = rate + spec["rate-constant"] = rate elif isinstance(rate, ArrheniusRate) or rate is None: pass else: raise TypeError("Invalid rate definition") if isinstance(efficiencies, dict): - spec['efficiencies'] = efficiencies + spec["efficiencies"] = efficiencies self._reaction = CxxNewReaction(dict_to_anymap(spec), deref(kinetics.kinetics)) @@ -1576,11 +1591,11 @@ cdef class PlogReaction3(Reaction): if init and equation and kinetics: - spec = {'equation': equation, 'type': self.reaction_type} + spec = {"equation": equation, "type": self.reaction_type} if isinstance(rate, list): - spec['rate-constants'] = [] + spec["rate-constants"] = [] for r in rate: - spec['rate-constants'].append({'P': r['P'], **r['rate-constant']}) + spec["rate-constants"].append({"P": r["P"], **r["rate-constant"]}) elif isinstance(rate, PlogRate) or rate is None: pass else: @@ -1638,11 +1653,11 @@ cdef class ChebyshevReaction3(Reaction): if init and equation and kinetics: - spec = {'equation': equation, 'type': self.reaction_type} + spec = {"equation": equation, "type": self.reaction_type} if isinstance(rate, dict): - spec['temperature-range'] = [rate['Tmin'], rate['Tmax']] - spec['pressure-range'] = [rate['Pmin'], rate['Pmax']] - spec['data'] = rate['data'] + spec["temperature-range"] = [rate["Tmin"], rate["Tmax"]] + spec["pressure-range"] = [rate["Pmin"], rate["Pmax"]] + spec["data"] = rate["data"] elif isinstance(rate, ChebyshevRate) or rate is None: pass else: @@ -1681,7 +1696,7 @@ cdef class CustomReaction(Reaction): if init and equation and kinetics: - spec = {'equation': equation, 'type': self.reaction_type} + spec = {"equation": equation, "type": self.reaction_type} self._reaction = CxxNewReaction(dict_to_anymap(spec), deref(kinetics.kinetics)) From c6473f47e4279120d2461b035264fbc4f7693d37 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 27 Apr 2021 07:39:15 -0500 Subject: [PATCH 50/84] [Kinetics] Tweak performance of MultiRate Access of rates and indices via vector of pairs is more efficient. --- include/cantera/kinetics/MultiRate.h | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/include/cantera/kinetics/MultiRate.h b/include/cantera/kinetics/MultiRate.h index 83bba1b1ae5..0ac10f768a0 100644 --- a/include/cantera/kinetics/MultiRate.h +++ b/include/cantera/kinetics/MultiRate.h @@ -60,14 +60,13 @@ class MultiBulkRates final : public MultiRateBase public: virtual void add(const size_t rxn_index, ReactionRateBase& rate) override { - m_indices[rxn_index] = m_rates.size(); - m_rates.push_back(dynamic_cast(rate)); - m_rxn.push_back(rxn_index); + m_indices[rxn_index] = m_rxn_rates.size(); + m_rxn_rates.emplace_back(rxn_index, static_cast(rate)); } virtual bool replace(const size_t rxn_index, ReactionRateBase& rate) override { - if (!m_rates.size()) { + if (!m_rxn_rates.size()) { throw CanteraError("MultiBulkRate::replace", "Invalid operation: cannot replace rate object " "in empty rate handler."); @@ -75,11 +74,11 @@ class MultiBulkRates final : public MultiRateBase throw CanteraError("MultiBulkRate::replace", "Invalid operation: cannot replace rate object of type '{}' " "with a new rate of type '{}'.", - m_rates[0].type(), rate.type()); + m_rxn_rates.at(0).second.type(), rate.type()); } if (m_indices.find(rxn_index) != m_indices.end()) { size_t j = m_indices[rxn_index]; - m_rates[j] = dynamic_cast(rate); + m_rxn_rates.at(j).second = static_cast(rate); return true; } return false; @@ -87,8 +86,8 @@ class MultiBulkRates final : public MultiRateBase virtual void getRateConstants(const ThermoPhase& bulk, double* kf, double* concm) const override { - for (size_t i = 0; i < m_rates.size(); i++) { - kf[m_rxn[i]] = m_rates[i].eval(m_shared, concm[m_rxn[i]]); + for (const auto& rxn : m_rxn_rates) { + kf[rxn.first] = rxn.second.eval(m_shared, concm[rxn.first]); } } @@ -99,15 +98,15 @@ class MultiBulkRates final : public MultiRateBase // 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 (size_t i = 0; i < m_rates.size(); i++) { - m_rates[i].update(m_shared, concm[m_rxn[i]]); + for (auto& rxn : m_rxn_rates) { + rxn.second.update(m_shared, concm[rxn.first]); } } } protected: - std::vector m_rates; //! Reaction rate objects - std::vector m_rxn; //! Index within overall rate vector + //! Vector of pairs of reaction rates indices and reaction rates + std::vector> m_rxn_rates; std::map m_indices; //! Mapping of indices DataType m_shared; }; From ddaf2a39eae73ca7c28ceacb54be48dd5ecba97b Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 27 Apr 2021 10:36:25 -0500 Subject: [PATCH 51/84] [Kinetics] Complete serialization of ReactionRate --- include/cantera/kinetics/ReactionRate.h | 22 ++++++++++++++++++++-- interfaces/cython/cantera/_cantera.pxd | 1 + interfaces/cython/cantera/reaction.pyx | 7 +++++++ src/kinetics/Reaction.cpp | 17 ++++------------- src/kinetics/ReactionRate.cpp | 25 ++++++++++++++++++++++++- 5 files changed, 56 insertions(+), 16 deletions(-) diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 668b5975c95..c789441d34f 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -11,6 +11,7 @@ #ifndef CT_REACTIONRATE_H #define CT_REACTIONRATE_H +#include "cantera/base/Units.h" #include "cantera/kinetics/RxnRates.h" #include "cantera/kinetics/ReactionData.h" #include "cantera/base/ctexceptions.h" @@ -20,7 +21,6 @@ namespace Cantera class Func1; - //! Abstract base class for reaction rate definitions /** * Because this class has no template parameters, derived objects can be @@ -35,6 +35,7 @@ class Func1; class ReactionRateBase { public: + ReactionRateBase() : units(0.) {} virtual ~ReactionRateBase() {} //! Set parameters @@ -99,6 +100,21 @@ class ReactionRateBase //! Validate the reaction rate expression virtual void validate(const std::string& equation) = 0; + + //! Return the parameters such that an identical Reaction could be reconstructed + //! using the newReaction() function. Behavior specific to derived classes is + //! handled by the getParameters() method. + //! @param rate_units units used for rate parameters + AnyMap parameters(const Units& rate_units) const; + + //! Return parameters using original unit system + AnyMap parameters() const; + +protected: + //! 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. + Units units; }; @@ -381,7 +397,9 @@ class CustomFunc1Rate final : public ReactionRate virtual std::string type() const override { return "custom-function"; } - virtual void setParameters(const AnyMap& node, const Units& rate_units) override {} + virtual void setParameters(const AnyMap& node, const Units& rate_units) override { + units = rate_units; + } //! Update information specific to reaction static bool uses_update() { return false; } diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index df572a0169d..307ff1e5420 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -362,6 +362,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": double eval(double, double) except +translate_exception double ddT(double) except +translate_exception double ddT(double, double) except +translate_exception + CxxAnyMap parameters() except +translate_exception cdef cppclass CxxArrheniusRate "Cantera::ArrheniusRate" (CxxReactionRateBase): CxxArrheniusRate() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 47d1a973f76..f50b27230cd 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -42,6 +42,13 @@ cdef class _ReactionRate: self.base.update(temperature) return self.base.ddT(temperature) + property input_data: + """ + Get input data for this reaction rate with its current parameter values. + """ + def __get__(self): + return anymap_to_dict(self.base.parameters()) + cdef class ArrheniusRate(_ReactionRate): r""" diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 66ce3127050..43307df2f6d 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -888,13 +888,7 @@ ElementaryReaction3::ElementaryReaction3(const AnyMap& node, const Kinetics& kin void ElementaryReaction3::getParameters(AnyMap& reactionNode) const { Reaction::getParameters(reactionNode); - ArrheniusRate& rate = dynamic_cast(*m_rate); - if (rate.allow_negative_pre_exponential_factor) { - reactionNode["negative-A"] = true; - } - AnyMap rateNode; - m_rate->getParameters(rateNode, rate_units); - reactionNode["rate-constant"] = std::move(rateNode); + reactionNode.update(m_rate->parameters(rate_units)); } ThreeBodyReaction3::ThreeBodyReaction3() @@ -1056,9 +1050,7 @@ void PlogReaction3::getParameters(AnyMap& reactionNode) const { Reaction::getParameters(reactionNode); reactionNode["type"] = "pressure-dependent-Arrhenius"; - AnyMap rateNode; - m_rate->getParameters(rateNode, rate_units); - reactionNode.update(rateNode); + reactionNode.update(m_rate->parameters(rate_units)); } ChebyshevReaction3::ChebyshevReaction3() @@ -1116,9 +1108,8 @@ void ChebyshevReaction3::setParameters(const AnyMap& node, const Kinetics& kin) void ChebyshevReaction3::getParameters(AnyMap& reactionNode) const { Reaction::getParameters(reactionNode); - AnyMap rateNode; - m_rate->getParameters(rateNode, rate_units); - reactionNode.update(rateNode); + reactionNode["type"] = "Chebyshev"; + reactionNode.update(m_rate->parameters(rate_units)); } CustomFunc1Reaction::CustomFunc1Reaction() diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 9d3aa6d66ea..4eea0afd225 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -6,10 +6,25 @@ #include "cantera/kinetics/ReactionRate.h" #include "cantera/numerics/Func1.h" #include "cantera/base/AnyMap.h" +#include "cantera/base/Units.h" namespace Cantera { +AnyMap ReactionRateBase::parameters(const Units& rate_units) const +{ + AnyMap out; + getParameters(out, rate_units); + return out; +} + +AnyMap ReactionRateBase::parameters() const +{ + AnyMap out; + getParameters(out, units); + return out; +} + ArrheniusRate::ArrheniusRate() : Arrhenius() , allow_negative_pre_exponential_factor(false) @@ -35,6 +50,7 @@ ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rate_units) { } void ArrheniusRate::setParameters(const AnyMap& node, const Units& rate_units) { + units = rate_units; allow_negative_pre_exponential_factor = node.getBool("negative-A", false); if (!node.hasKey("rate-constant")) { return; @@ -44,7 +60,12 @@ void ArrheniusRate::setParameters(const AnyMap& node, const Units& rate_units) { void ArrheniusRate::getParameters(AnyMap& rateNode, const Units& rate_units) const { - Arrhenius::getParameters(rateNode, rate_units); + if (allow_negative_pre_exponential_factor) { + rateNode["negative-A"] = true; + } + AnyMap node; + Arrhenius::getParameters(node, rate_units); + rateNode["rate-constant"] = std::move(node); } void ArrheniusRate::validate(const std::string& equation) { @@ -69,6 +90,7 @@ PlogRate::PlogRate(const AnyMap& node, const Units& rate_units) } void PlogRate::setParameters(const AnyMap& node, const Units& rate_units) { + units = rate_units; if (!node.hasKey("rate-constants")) { // ensure that Plog has defined state and produces zero reaction rate AnyMap rate = AnyMap::fromYamlString( @@ -103,6 +125,7 @@ ChebyshevRate3::ChebyshevRate3(const AnyMap& node, const Units& rate_units) } void ChebyshevRate3::setParameters(const AnyMap& node, const Units& rate_units) { + units = rate_units; if (!node.hasKey("data")) { // ensure that Chebyshev has defined state and produces zero reaction rate AnyMap rate = AnyMap::fromYamlString( From c920ccd9caec9f22daeeb73aa924c35d4f3b9faa Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 27 Apr 2021 11:56:04 -0500 Subject: [PATCH 52/84] [Kinetics] Create ReactionRate objects from Python input_data --- include/cantera/kinetics/ReactionRate.h | 12 +++++++++++ interfaces/cython/cantera/_cantera.pxd | 3 +++ interfaces/cython/cantera/reaction.pyx | 28 +++++++++++++++++-------- src/kinetics/ReactionRate.cpp | 23 +++++++++++++++----- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index c789441d34f..d3106ee5c96 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -213,6 +213,10 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius //! @param rate_units unit definitions used for rate information ArrheniusRate(const AnyMap& node, const Units& rate_units); + //! Constructor using AnyMap content + //! @param node AnyMap containing rate information + ArrheniusRate(const AnyMap& node); + //! Constructor based on Arrhenius object //! @param arr Arrhenius object //! @param allow_negative_A allow negative pre-exponential factor @@ -281,6 +285,10 @@ class PlogRate final : public ReactionRate, public Plog //! @param rate_units unit definitions used for rate information PlogRate(const AnyMap& node, const Units& rate_units); + //! Constructor using AnyMap content + //! @param node AnyMap containing rate information + PlogRate(const AnyMap& node); + virtual std::string type() const override { return "PlogRate"; } virtual void setParameters(const AnyMap& node, const Units& rate_units) override; @@ -358,6 +366,10 @@ class ChebyshevRate3 final : public ReactionRate, public Chebyshe //! @param rate_units unit definitions used for rate information ChebyshevRate3(const AnyMap& node, const Units& rate_units); + //! Constructor using AnyMap content + //! @param node AnyMap containing rate information + ChebyshevRate3(const AnyMap& node); + virtual std::string type() const override { return "ChebyshevRate"; } virtual void setParameters(const AnyMap& node, const Units& rate_units) override; diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 307ff1e5420..934feaa9fb5 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -366,6 +366,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxArrheniusRate "Cantera::ArrheniusRate" (CxxReactionRateBase): CxxArrheniusRate() + CxxArrheniusRate(CxxAnyMap) except +translate_exception CxxArrheniusRate(double, double, double) double preExponentialFactor() double temperatureExponent() @@ -374,11 +375,13 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cdef cppclass CxxPlogRate "Cantera::PlogRate" (CxxReactionRateBase): CxxPlogRate() + CxxPlogRate(CxxAnyMap) except +translate_exception CxxPlogRate(multimap[double, CxxArrhenius]) vector[pair[double, CxxArrhenius]] rates() cdef cppclass CxxChebyshevRate3 "Cantera::ChebyshevRate3" (CxxReactionRateBase): CxxChebyshevRate3() + CxxChebyshevRate3(CxxAnyMap) except +translate_exception CxxChebyshevRate3(double, double, double, double, CxxArray2D) double Tmin() double Tmax() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index f50b27230cd..6aeea8c3eff 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -9,10 +9,9 @@ cdef dict _reaction_class_registry = {} cdef class _ReactionRate: """ Base class for ReactionRate objects. + ReactionRate objects are used to calculate reaction rates and are associated - with a Reaction object. In order to improve computational speed, Kinetics - objects create internal copies that are linked to the original Reaction - definition. + with a Reaction object. """ def __repr__(self): @@ -62,10 +61,13 @@ cdef class ArrheniusRate(_ReactionRate): where *A* is the `pre_exponential_factor`, *b* is the `temperature_exponent`, and *E* is the `activation_energy`. """ - def __cinit__(self, A=0, b=0, E=0, init=True): + def __cinit__(self, A=0, b=0, E=0, input_data=None, init=True): if init: - self._base.reset(new CxxArrheniusRate(A, b, E)) + if isinstance(input_data, dict): + self._base.reset(new CxxArrheniusRate(dict_to_anymap(input_data))) + else: + self._base.reset(new CxxArrheniusRate(A, b, E)) self.base = self._base.get() self.rate = (self.base) @@ -120,9 +122,13 @@ cdef class PlogRate(_ReactionRate): A pressure-dependent reaction rate parameterized by logarithmically interpolating between Arrhenius rate expressions at various pressures. """ - def __cinit__(self, rates=None, init=True): + def __cinit__(self, rates=None, input_data=None, init=True): - if rates and init: + if isinstance(input_data, dict) and init: + self._base.reset(new CxxPlogRate(dict_to_anymap(input_data))) + self.base = self._base.get() + self.rate = (self.base) + elif rates and init: self.rates = rates @staticmethod @@ -171,9 +177,13 @@ cdef class ChebyshevRate(_ReactionRate): polynomial in temperature and pressure. """ def __cinit__(self, Tmin=None, Tmax=None, Pmin=None, Pmax=None, data=None, - init=True): + input_data=None, init=True): - if Tmin and Tmax and Pmin and Pmax and data is not None and init: + if isinstance(input_data, dict) and init: + self._base.reset(new CxxChebyshevRate3(dict_to_anymap(input_data))) + self.base = self._base.get() + self.rate = (self.base) + elif Tmin and Tmax and Pmin and Pmax and data is not None and init: self._setup(Tmin, Tmax, Pmin, Pmax, data) def _setup(self, Tmin, Tmax, Pmin, Pmax, coeffs): diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 4eea0afd225..7524c2e91ca 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -6,7 +6,6 @@ #include "cantera/kinetics/ReactionRate.h" #include "cantera/numerics/Func1.h" #include "cantera/base/AnyMap.h" -#include "cantera/base/Units.h" namespace Cantera { @@ -37,6 +36,14 @@ ArrheniusRate::ArrheniusRate(double A, double b, double E) { } +ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rate_units) { + setParameters(node, rate_units); +} + +ArrheniusRate::ArrheniusRate(const AnyMap& node) { + setParameters(node, Units(1.)); +} + ArrheniusRate::ArrheniusRate(const Arrhenius& arr, bool allow_negative_A) : Arrhenius(arr.preExponentialFactor(), arr.temperatureExponent(), @@ -45,10 +52,6 @@ ArrheniusRate::ArrheniusRate(const Arrhenius& arr, bool allow_negative_A) { } -ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rate_units) { - setParameters(node, rate_units); -} - void ArrheniusRate::setParameters(const AnyMap& node, const Units& rate_units) { units = rate_units; allow_negative_pre_exponential_factor = node.getBool("negative-A", false); @@ -89,6 +92,11 @@ PlogRate::PlogRate(const AnyMap& node, const Units& rate_units) setParameters(node, rate_units); } +PlogRate::PlogRate(const AnyMap& node) + : Plog() { + setParameters(node, Units(1.)); +} + void PlogRate::setParameters(const AnyMap& node, const Units& rate_units) { units = rate_units; if (!node.hasKey("rate-constants")) { @@ -124,6 +132,11 @@ ChebyshevRate3::ChebyshevRate3(const AnyMap& node, const Units& rate_units) setParameters(node, rate_units); } +ChebyshevRate3::ChebyshevRate3(const AnyMap& node) + : Chebyshev() { + setParameters(node, Units(1.)); +} + void ChebyshevRate3::setParameters(const AnyMap& node, const Units& rate_units) { units = rate_units; if (!node.hasKey("data")) { From a26f834c1a54a2fff592f683051e00f0b4f9bc61 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 27 Apr 2021 13:38:52 -0500 Subject: [PATCH 53/84] [CI] Add checks for serialization of new ReactionRate objects --- .../cython/cantera/test/test_reaction.py | 149 +++++++++++++----- test/data/kineticsfromscratch.yaml | 2 + 2 files changed, 113 insertions(+), 38 deletions(-) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index fa10afa9d93..2d71d90016c 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -116,18 +116,20 @@ class TestReactionRate(utilities.CanteraTest): _type = None _uses_pressure = False _index = None + _input = None + _cls = None @classmethod def setUpClass(cls): utilities.CanteraTest.setUpClass() - cls.gas = ct.Solution('kineticsfromscratch.yaml') - cls.gas.X = 'H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5' + cls.gas = ct.Solution("kineticsfromscratch.yaml") + cls.gas.X = "H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5" cls.gas.TP = 900, 2*ct.one_atm def test_type(self): if self._index is None: return - self.assertIn(self._type, '{}'.format(self.rate)) + self.assertIn(self._type, "{}".format(self.rate)) def test_rate_T(self): if self._index is None: @@ -146,12 +148,30 @@ def test_rate_TP(self): self.assertNear(self.rate(self.gas.T, self.gas.P), self.gas.forward_rate_constants[self._index]) + def test_input(self): + if self._input is None or self._cls is None: + return + rate = self._cls(input_data=self._input) + self.assertIn(self._type, "{}".format(rate)) + self.assertNear(rate(self.gas.T, self.gas.P), + self.rate(self.gas.T, self.gas.P)) + + def test_roundtrip(self): + if self._index is None: + return + input_data = self.rate.input_data + rate = self._cls(input_data=input_data) + self.assertNear(rate(self.gas.T, self.gas.P), + self.rate(self.gas.T, self.gas.P)) + class TestArrheniusRate(TestReactionRate): _type = "Arrhenius" _uses_pressure = False _index = 0 + _input = {"rate-constant": {"A": 38.7, "b": 2.7, "Ea": 26191840.0}} + _cls = ct.ArrheniusRate def setUp(self): self.A = self.gas.reaction(self._index).rate.pre_exponential_factor @@ -181,6 +201,12 @@ class TestPlogRate(TestReactionRate): _type = "Plog" _uses_pressure = True _index = 3 + _input = {"rate-constants": [ + {"P": 1013.25, "A": 1.2124e+16, "b": -0.5779, "Ea": 45491376.8}, + {"P": 101325., "A": 4.9108e+31, "b": -4.8507, "Ea": 103649395.2}, + {"P": 1013250., "A": 1.2866e+47, "b": -9.0246, "Ea": 166508556.0}, + {"P": 10132500., "A": 5.9632e+56, "b": -11.529, "Ea": 220076726.4}]} + _cls = ct.PlogRate def setUp(self): self.rate = ct.PlogRate([(1013.25, ct.Arrhenius(1.2124e+16, -0.5779, 45491376.8)), @@ -194,6 +220,12 @@ class TestChebyshevRate(TestReactionRate): _type = "Chebyshev" _uses_pressure = True _index = 4 + _input = {"data": [[8.2883, -1.1397, -0.12059, 0.016034], + [1.9764, 1.0037, 0.0072865, -0.030432], + [0.3177, 0.26889, 0.094806, -0.0076385]], + "pressure-range": [1000.0, 10000000.0], + "temperature-range": [290.0, 3000.0]} + _cls = ct.ChebyshevRate def setUp(self): self.Tmin = self.gas.reaction(self._index).rate.Tmin @@ -221,6 +253,7 @@ class TestReaction(utilities.CanteraTest): _index = None _type = None _yaml = None + _input = None @classmethod def setUpClass(cls): @@ -236,12 +269,12 @@ def check_rxn(self, rxn): 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', + gas2 = ct.Solution(thermo="IdealGas", kinetics="GasKinetics", species=self.species, reactions=[rxn]) gas2.TPX = self.gas.TPX - self.check_sol(gas2) + self.check_solution(gas2) - def check_sol(self, gas2): + def check_solution(self, gas2): ix = self._index self.assertEqual(gas2.reaction_type_str(0), self._type) self.assertNear(gas2.forward_rate_constants[0], @@ -252,14 +285,14 @@ def check_sol(self, gas2): def test_rate(self): if self._rate_obj is None: return - if 'Rate' in type(self._rate_obj).__name__: + if "Rate" in type(self._rate_obj).__name__: self.assertNear(self._rate_obj(self.gas.T, self.gas.P), - self.gas.forward_rate_constants[self._index]) + self.gas.forward_rate_constants[self._index]) else: self.assertNear(self._rate_obj(self.gas.T), self.gas.forward_rate_constants[self._index]) def test_from_parts(self): - if self._cls is None or not hasattr(self._cls, 'rate'): + if self._cls is None or not hasattr(self._cls, "rate"): return orig = self.gas.reaction(self._index) rxn = self._cls(orig.reactants, orig.products) @@ -287,13 +320,13 @@ def test_from_rate(self): def test_add_rxn(self): if self._cls is None or self._rate_obj is None: return - gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', + 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, **self._kwargs) gas2.add_reaction(rxn) - self.check_sol(gas2) + self.check_solution(gas2) def test_wrong_rate(self): if self._cls is None: @@ -302,15 +335,15 @@ def test_wrong_rate(self): rxn = self._cls(equation=self._equation, rate=(), kinetics=self.gas, **self._kwargs) def test_no_rate(self): - if self._cls is None or not hasattr(self._cls, 'rate'): + if self._cls is None or not hasattr(self._cls, "rate"): return rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) - if 'Rate' in type(self._rate_obj).__name__: + if "Rate" in type(self._rate_obj).__name__: self.assertNear(rxn.rate(self.gas.T, self.gas.P), 0.) else: self.assertNear(rxn.rate(self.gas.T), 0.) - gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', + gas2 = ct.Solution(thermo="IdealGas", kinetics="GasKinetics", species=self.species, reactions=[rxn]) gas2.TPX = self.gas.TPX @@ -324,12 +357,21 @@ def test_replace_rate(self): rxn.rate = self._rate_obj self.check_rxn(rxn) + def test_roundtrip(self): + if self._cls is None or self._type.endswith("-old"): + return + rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) + input_data = rxn.rate.input_data + rate_obj = rxn.rate.__class__(input_data=input_data) + rxn2 = self._cls(equation=self._equation, rate=rate_obj, kinetics=self.gas, **self._kwargs) + self.check_rxn(rxn2) + class TestElementary(TestReaction): _cls = ct.ElementaryReaction - _equation = 'H2 + O <=> H + OH' - _rate = {'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07} + _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) _kwargs = {} _index = 0 @@ -338,7 +380,7 @@ class TestElementary(TestReaction): equation: O + H2 <=> H + OH type: elementary-old rate-constant: {A: 38.7, b: 2.7, Ea: 6260.0 cal/mol} - """ + """ class TestElementary3(TestElementary): @@ -349,16 +391,16 @@ class TestElementary3(TestElementary): _yaml = """ equation: O + H2 <=> H + OH rate-constant: {A: 38.7, b: 2.7, Ea: 6260.0 cal/mol} - """ + """ class TestThreeBody(TestReaction): _cls = ct.ThreeBodyReaction - _equation = '2 O + M <=> O2 + M' - _rate = {'A': 1.2e11, 'b': -1.0, 'Ea': 0.0} + _equation = "2 O + M <=> O2 + M" + _rate = {"A": 1.2e11, "b": -1.0, "Ea": 0.0} _rate_obj = ct.Arrhenius(1.2e11, -1., 0.) - _kwargs = {'efficiencies': {'H2': 2.4, 'H2O': 15.4, 'AR': 0.83}} + _kwargs = {"efficiencies": {"H2": 2.4, "H2O": 15.4, "AR": 0.83}} _index = 1 _type = "three-body-old" _yaml = """ @@ -366,13 +408,13 @@ class TestThreeBody(TestReaction): type: three-body-old rate-constant: {A: 1.2e+11, b: -1.0, Ea: 0.0 cal/mol} efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} - """ + """ def test_from_parts(self): orig = self.gas.reaction(self._index) rxn = self._cls(orig.reactants, orig.products) rxn.rate = self._rate_obj - rxn.efficiencies = self._kwargs['efficiencies'] + rxn.efficiencies = self._kwargs["efficiencies"] self.check_rxn(rxn) def test_rate(self): @@ -382,7 +424,7 @@ def test_rate(self): def test_efficiencies(self): rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) - self.assertEqual(rxn.efficiencies, self._kwargs['efficiencies']) + self.assertEqual(rxn.efficiencies, self._kwargs["efficiencies"]) class TestThreeBody3(TestThreeBody): @@ -395,17 +437,44 @@ class TestThreeBody3(TestThreeBody): type: three-body rate-constant: {A: 1.2e+11, b: -1.0, Ea: 0.0 cal/mol} efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} - """ + """ + + +class TestImplicitThreeBody3(TestThreeBody): + + _cls = ct.ThreeBodyReaction3 + _equation = "H + 2 O2 <=> HO2 + O2" + _rate = {"A": 2.08e+19, "b": -1.24, "Ea": 0.0} + _rate_obj = ct.ArrheniusRate(2.08e+19, -1.24, 0.) + _index = 5 + _type = "three-body" + _yaml = """ + equation: H + 2 O2 <=> HO2 + O2 + rate-constant: {A: 2.08e+19, b: -1.24, Ea: 0.0} + """ + + def test_efficiencies(self): + rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas) + self.assertEqual(rxn.efficiencies, {"O2": 1.}) + self.assertEqual(rxn.default_efficiency, 0.) + + def test_from_parts(self): + orig = self.gas.reaction(self._index) + rxn = self._cls(orig.reactants, orig.products) + rxn.rate = self._rate_obj + rxn.efficiencies = {"O2": 1.} + rxn.default_efficiency = 0 + self.check_rxn(rxn) class TestPlog(TestReaction): _cls = ct.PlogReaction _equation = "H2 + O2 <=> 2 OH" - _rate = [{'P': 1013.25, 'rate-constant': {'A': 1.2124e+16, 'b': -0.5779, 'Ea': 45491376.8}}, - {'P': 101325., 'rate-constant': {'A': 4.9108e+31, 'b': -4.8507, 'Ea': 103649395.2}}, - {'P': 1013250., 'rate-constant': {'A': 1.2866e+47, 'b': -9.0246, 'Ea': 166508556.0}}, - {'P': 10132500., 'rate-constant': {'A': 5.9632e+56, 'b': -11.529, 'Ea': 220076726.4}}] + _rate = [{"P": 1013.25, "rate-constant": {"A": 1.2124e+16, "b": -0.5779, "Ea": 45491376.8}}, + {"P": 101325., "rate-constant": {"A": 4.9108e+31, "b": -4.8507, "Ea": 103649395.2}}, + {"P": 1013250., "rate-constant": {"A": 1.2866e+47, "b": -9.0246, "Ea": 166508556.0}}, + {"P": 10132500., "rate-constant": {"A": 5.9632e+56, "b": -11.529, "Ea": 220076726.4}}] _type = "pressure-dependent-Arrhenius-old" _index = 3 _yaml = """ @@ -416,7 +485,7 @@ class TestPlog(TestReaction): - {P: 1.0 atm, A: 4.9108e+31, b: -4.8507, Ea: 2.47728e+04 cal/mol} - {P: 10.0 atm, A: 1.2866e+47, b: -9.0246, Ea: 3.97965e+04 cal/mol} - {P: 100.0 atm, A: 5.9632e+56, b: -11.529, Ea: 5.25996e+04 cal/mol} - """ + """ class TestPlog3(TestPlog): @@ -435,15 +504,15 @@ class TestPlog3(TestPlog): - {P: 1.0 atm, A: 4.9108e+31, b: -4.8507, Ea: 2.47728e+04 cal/mol} - {P: 10.0 atm, A: 1.2866e+47, b: -9.0246, Ea: 3.97965e+04 cal/mol} - {P: 100.0 atm, A: 5.9632e+56, b: -11.529, Ea: 5.25996e+04 cal/mol} - """ + """ class TestChebyshev(TestReaction): _cls = ct.ChebyshevReaction _equation = "HO2 <=> OH + O" - _rate = {'Tmin': 290., 'Tmax': 3000., 'Pmin': 1000., 'Pmax': 10000000.0, - 'data': [[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], + _rate = {"Tmin": 290., "Tmax": 3000., "Pmin": 1000., "Pmax": 10000000.0, + "data": [[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], [ 1.9764e+00, 1.0037e+00, 7.2865e-03, -3.0432e-02], [ 3.1770e-01, 2.6889e-01, 9.4806e-02, -7.6385e-03]]} _type = "Chebyshev-old" @@ -457,7 +526,7 @@ class TestChebyshev(TestReaction): - [8.2883, -1.1397, -0.12059, 0.016034] - [1.9764, 1.0037, 7.2865e-03, -0.030432] - [0.3177, 0.26889, 0.094806, -7.6385e-03] - """ + """ class TestChebyshev3(TestChebyshev): @@ -477,21 +546,21 @@ class TestChebyshev3(TestChebyshev): - [8.2883, -1.1397, -0.12059, 0.016034] - [1.9764, 1.0037, 7.2865e-03, -0.030432] - [0.3177, 0.26889, 0.094806, -7.6385e-03] - """ + """ class TestCustom(TestReaction): # probe O + H2 <=> H + OH _cls = ct.CustomReaction - _equation = 'H2 + O <=> H + OH' + _equation = "H2 + O <=> H + OH" _rate_obj = ct.CustomRate(lambda T: 38.7 * T**2.7 * exp(-3150.15428/T)) _index = 0 _type = "custom-rate-function" _yaml = None def setUp(self): - # need to overwrite rate to ensure correct type ('method' is not compatible with Func1) + # 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): @@ -499,13 +568,16 @@ def test_no_rate(self): with self.assertRaisesRegex(ct.CanteraError, "Custom rate function is not initialized."): rxn.rate(self.gas.T) - gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', + 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_roundtrip(self): + pass + def test_from_func(self): f = ct.Func1(self._rate) rxn = ct.CustomReaction(equation=self._equation, rate=f, kinetics=self.gas) @@ -521,3 +593,4 @@ def test_custom(self): rate=lambda T: 38.7 * T**2.7 * exp(-3150.15428/T), kinetics=self.gas) self.check_rxn(rxn) + diff --git a/test/data/kineticsfromscratch.yaml b/test/data/kineticsfromscratch.yaml index 92cced86289..f59c2c31559 100644 --- a/test/data/kineticsfromscratch.yaml +++ b/test/data/kineticsfromscratch.yaml @@ -45,3 +45,5 @@ reactions: - [8.2883, -1.1397, -0.12059, 0.016034] - [1.9764, 1.0037, 7.2865e-03, -0.030432] - [0.3177, 0.26889, 0.094806, -7.6385e-03] +- equation: H + 2 O2 <=> HO2 + O2 # Reaction 6 + rate-constant: {A: 2.08e+19, b: -1.24, Ea: 0.0} From fef74079ac75c4d0ce3857f1ab0cf2e43485b335 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 27 Apr 2021 11:08:41 -0500 Subject: [PATCH 54/84] [CI] Fix kineticsFromScratch3 --- test/kinetics/kineticsFromScratch3.cpp | 237 ++++++++++++------------- 1 file changed, 113 insertions(+), 124 deletions(-) diff --git a/test/kinetics/kineticsFromScratch3.cpp b/test/kinetics/kineticsFromScratch3.cpp index 0e9f71c0acb..2ec0842d69f 100644 --- a/test/kinetics/kineticsFromScratch3.cpp +++ b/test/kinetics/kineticsFromScratch3.cpp @@ -1,9 +1,9 @@ #include "gtest/gtest.h" -#include "cantera/base/Solution.h" -#include "cantera/thermo/ThermoFactory.h" +#include "cantera/kinetics/KineticsFactory.h" +#include "cantera/thermo/IdealGasPhase.h" #include "cantera/thermo/SurfPhase.h" #include "cantera/thermo/Species.h" -#include "cantera/kinetics/KineticsFactory.h" +#include "cantera/kinetics/GasKinetics.h" #include "cantera/kinetics/InterfaceKinetics.h" #include "cantera/kinetics/FalloffFactory.h" #include "cantera/base/Array.h" @@ -15,40 +15,36 @@ class KineticsFromScratch3 : public testing::Test { public: KineticsFromScratch3() + : p("../data/kineticsfromscratch.yaml") + , p_ref("../data/kineticsfromscratch.yaml") { - std::string yaml_file = "../data/kineticsfromscratch.yaml"; - std::string phase_name = "ohmech"; - - p = std::unique_ptr(newPhase(yaml_file, phase_name)); - kin = std::shared_ptr(newKineticsMgr("GasKinetics")); - kin->addPhase(*p.get()); - - sol = newSolution(yaml_file, phase_name); - p_ref = sol->thermo(); - kin_ref = sol->kinetics(); + std::vector th; + th.push_back(&p_ref); + kin_ref = newKinetics(th, "../data/kineticsfromscratch.yaml", "ohmech"); + kin.addPhase(p); + kin.init(); } - std::unique_ptr p; - std::shared_ptr kin; - std::shared_ptr sol; - std::shared_ptr p_ref; - std::shared_ptr kin_ref; + IdealGasPhase p; + IdealGasPhase p_ref; + GasKinetics kin; + unique_ptr kin_ref; //! iRef is the index of the corresponding reaction in the reference mech void check_rates(int iRef) { - ASSERT_EQ((size_t) 1, kin->nReactions()); + ASSERT_EQ((size_t) 1, kin.nReactions()); std::string X = "O:0.02 H2:0.2 O2:0.5 H:0.03 OH:0.05 H2O:0.1 HO2:0.01"; - p->setState_TPX(1200, 5*OneAtm, X); - p_ref->setState_TPX(1200, 5*OneAtm, X); + p.setState_TPX(1200, 5*OneAtm, X); + p_ref.setState_TPX(1200, 5*OneAtm, X); vector_fp k(1), k_ref(kin_ref->nReactions()); - kin->getFwdRateConstants(&k[0]); + kin.getFwdRateConstants(&k[0]); kin_ref->getFwdRateConstants(&k_ref[0]); EXPECT_DOUBLE_EQ(k_ref[iRef], k[0]); - kin->getRevRateConstants(&k[0]); + kin.getRevRateConstants(&k[0]); kin_ref->getRevRateConstants(&k_ref[0]); EXPECT_DOUBLE_EQ(k_ref[iRef], k[0]); } @@ -63,7 +59,7 @@ TEST_F(KineticsFromScratch3, add_elementary_reaction) ArrheniusRate rate(3.87e1, 2.7, 2.619184e+07); auto R = make_shared(reac, prod, rate); - kin->addReaction(R); + kin.addReaction(R); check_rates(0); } @@ -79,7 +75,7 @@ TEST_F(KineticsFromScratch3, add_three_body_reaction) tbody.efficiencies = parseCompString("AR:0.83 H2:2.4 H2O:15.4"); auto R = make_shared(reac, prod, rate, tbody); - kin->addReaction(R); + kin.addReaction(R); check_rates(1); } @@ -92,7 +88,7 @@ TEST_F(KineticsFromScratch3, undefined_third_body) tbody.efficiencies = parseCompString("H2:0.1 CO2:0.83"); auto R = make_shared(reac, prod, rate, tbody); - ASSERT_THROW(kin->addReaction(R), CanteraError); + ASSERT_THROW(kin.addReaction(R), CanteraError); } TEST_F(KineticsFromScratch3, skip_undefined_third_body) @@ -104,12 +100,11 @@ TEST_F(KineticsFromScratch3, skip_undefined_third_body) tbody.efficiencies = parseCompString("H2:0.1 CO2:0.83"); auto R = make_shared(reac, prod, rate, tbody); - kin->skipUndeclaredThirdBodies(true); - kin->addReaction(R); - ASSERT_EQ((size_t) 1, kin->nReactions()); + kin.skipUndeclaredThirdBodies(true); + kin.addReaction(R); + ASSERT_EQ((size_t) 1, kin.nReactions()); } - /* TEST_F(KineticsFromScratch3, add_falloff_reaction) { @@ -128,7 +123,7 @@ TEST_F(KineticsFromScratch3, add_falloff_reaction) tbody.efficiencies = parseCompString("AR:0.7 H2:2.0 H2O:6.0"); auto R = make_shared(reac, prod, low_rate, high_rate, tbody); R->falloff = newFalloff("Troe", falloff_params); - kin->addReaction(R); + kin.addReaction(R); check_rates(2); } */ @@ -151,7 +146,7 @@ TEST_F(KineticsFromScratch3, add_plog_reaction) }; auto R = make_shared(reac, prod, PlogRate(rates)); - kin->addReaction(R); + kin.addReaction(R); check_rates(3); } @@ -167,7 +162,7 @@ TEST_F(KineticsFromScratch3, plog_invalid_rate) }; auto R = make_shared(reac, prod, PlogRate(rates)); - ASSERT_THROW(kin->addReaction(R), CanteraError); + ASSERT_THROW(kin.addReaction(R), CanteraError); } TEST_F(KineticsFromScratch3, add_chebyshev_reaction) @@ -198,7 +193,7 @@ TEST_F(KineticsFromScratch3, add_chebyshev_reaction) ChebyshevRate3 rate(290, 3000, 1000.0, 10000000.0, coeffs); auto R = make_shared(reac, prod, rate); - kin->addReaction(R); + kin.addReaction(R); check_rates(4); } @@ -209,8 +204,8 @@ TEST_F(KineticsFromScratch3, undeclared_species) ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); auto R = make_shared(reac, prod, rate); - ASSERT_THROW(kin->addReaction(R), CanteraError); - ASSERT_EQ((size_t) 0, kin->nReactions()); + ASSERT_THROW(kin.addReaction(R), CanteraError); + ASSERT_EQ((size_t) 0, kin.nReactions()); } TEST_F(KineticsFromScratch3, skip_undeclared_species) @@ -220,9 +215,9 @@ TEST_F(KineticsFromScratch3, skip_undeclared_species) ArrheniusRate rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); auto R = make_shared(reac, prod, rate); - kin->skipUndeclaredSpecies(true); - kin->addReaction(R); - ASSERT_EQ((size_t) 0, kin->nReactions()); + kin.skipUndeclaredSpecies(true); + kin.addReaction(R); + ASSERT_EQ((size_t) 0, kin.nReactions()); } TEST_F(KineticsFromScratch3, negative_A_error) @@ -232,8 +227,8 @@ TEST_F(KineticsFromScratch3, negative_A_error) ArrheniusRate rate(-3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); auto R = make_shared(reac, prod, rate); - ASSERT_THROW(kin->addReaction(R), CanteraError); - ASSERT_EQ((size_t) 0, kin->nReactions()); + ASSERT_THROW(kin.addReaction(R), CanteraError); + ASSERT_EQ((size_t) 0, kin.nReactions()); } TEST_F(KineticsFromScratch3, allow_negative_A) @@ -245,8 +240,8 @@ TEST_F(KineticsFromScratch3, allow_negative_A) auto rr = std::dynamic_pointer_cast(R->rate()); rr->allow_negative_pre_exponential_factor = true; - kin->addReaction(R); - ASSERT_EQ((size_t) 1, kin->nReactions()); + kin.addReaction(R); + ASSERT_EQ((size_t) 1, kin.nReactions()); } TEST_F(KineticsFromScratch3, invalid_reversible_with_orders) @@ -257,8 +252,8 @@ TEST_F(KineticsFromScratch3, invalid_reversible_with_orders) auto R = make_shared(reac, prod, rate); R->orders["H2"] = 0.5; - ASSERT_THROW(kin->addReaction(R), CanteraError); - ASSERT_EQ((size_t) 0, kin->nReactions()); + ASSERT_THROW(kin.addReaction(R), CanteraError); + ASSERT_EQ((size_t) 0, kin.nReactions()); } TEST_F(KineticsFromScratch3, negative_order_override) @@ -271,8 +266,8 @@ TEST_F(KineticsFromScratch3, negative_order_override) R->allow_negative_orders = true; R->orders["H2"] = - 0.5; - kin->addReaction(R); - ASSERT_EQ((size_t) 1, kin->nReactions()); + kin.addReaction(R); + ASSERT_EQ((size_t) 1, kin.nReactions()); } TEST_F(KineticsFromScratch3, invalid_negative_orders) @@ -284,8 +279,8 @@ TEST_F(KineticsFromScratch3, invalid_negative_orders) R->reversible = false; R->orders["H2"] = - 0.5; - ASSERT_THROW(kin->addReaction(R), CanteraError); - ASSERT_EQ((size_t) 0, kin->nReactions()); + ASSERT_THROW(kin.addReaction(R), CanteraError); + ASSERT_EQ((size_t) 0, kin.nReactions()); } TEST_F(KineticsFromScratch3, nonreactant_order_override) @@ -298,8 +293,8 @@ TEST_F(KineticsFromScratch3, nonreactant_order_override) R->allow_nonreactant_orders = true; R->orders["OH"] = 0.5; - kin->addReaction(R); - ASSERT_EQ((size_t) 1, kin->nReactions()); + kin.addReaction(R); + ASSERT_EQ((size_t) 1, kin.nReactions()); } TEST_F(KineticsFromScratch3, invalid_nonreactant_order) @@ -311,8 +306,8 @@ TEST_F(KineticsFromScratch3, invalid_nonreactant_order) R->reversible = false; R->orders["OH"] = 0.5; - ASSERT_THROW(kin->addReaction(R), CanteraError); - ASSERT_EQ((size_t) 0, kin->nReactions()); + ASSERT_THROW(kin.addReaction(R), CanteraError); + ASSERT_EQ((size_t) 0, kin.nReactions()); } /* @@ -320,15 +315,15 @@ class InterfaceKineticsFromScratch3 : public testing::Test { public: InterfaceKineticsFromScratch3() - : gas("../data/sofc-test.xml", "gas") - , gas_ref("../data/sofc-test.xml", "gas") - , surf("../data/sofc-test.xml", "metal_surface") - , surf_ref("../data/sofc-test.xml", "metal_surface") + : gas("sofc.yaml", "gas") + , gas_ref("sofc.yaml", "gas") + , surf("sofc.yaml", "metal_surface") + , surf_ref("sofc.yaml", "metal_surface") { std::vector th = { &surf_ref, &gas_ref }; - importKinetics(surf_ref.xml(), th, &kin_ref); - kin->addPhase(surf); - kin->addPhase(gas); + kin_ref = newKinetics(th, "sofc.yaml", "metal_surface"); + kin.addPhase(surf); + kin.addPhase(gas); } IdealGasPhase gas; @@ -336,11 +331,11 @@ class InterfaceKineticsFromScratch3 : public testing::Test SurfPhase surf; SurfPhase surf_ref; InterfaceKinetics kin; - InterfaceKinetics kin_ref; + unique_ptr kin_ref; //! iRef is the index of the corresponding reaction in the reference mech void check_rates(int iRef) { - ASSERT_EQ((size_t) 1, kin->nReactions()); + ASSERT_EQ((size_t) 1, kin.nReactions()); std::string X = "H2:0.2 O2:0.5 H2O:0.1 N2:0.2"; std::string Xs = "H(m):0.1 O(m):0.2 OH(m):0.3 (m):0.4"; @@ -353,11 +348,11 @@ class InterfaceKineticsFromScratch3 : public testing::Test vector_fp k(1), k_ref(kin_ref->nReactions()); - kin->getFwdRateConstants(&k[0]); + kin.getFwdRateConstants(&k[0]); kin_ref->getFwdRateConstants(&k_ref[0]); EXPECT_DOUBLE_EQ(k_ref[iRef], k[0]); - kin->getRevRateConstants(&k[0]); + kin.getRevRateConstants(&k[0]); kin_ref->getRevRateConstants(&k_ref[0]); EXPECT_DOUBLE_EQ(k_ref[iRef], k[0]); } @@ -373,7 +368,7 @@ TEST_F(InterfaceKineticsFromScratch3, add_surface_reaction) Arrhenius rate(5e21, 0, 100.0e6 / GasConstant); // kJ/mol -> J/kmol auto R = make_shared(reac, prod, rate); - kin->addReaction(R); + kin.addReaction(R); check_rates(3); } @@ -387,7 +382,7 @@ TEST_F(InterfaceKineticsFromScratch3, add_sticking_reaction) Arrhenius rate(0.1, 0, 0.0); auto R = make_shared(reac, prod, rate, true); - kin->addReaction(R); + kin.addReaction(R); check_rates(0); } @@ -398,7 +393,7 @@ TEST_F(InterfaceKineticsFromScratch3, unbalanced_sites) Arrhenius rate(5e21, 0, 100.0e6 / GasConstant); auto R = make_shared(reac, prod, rate); - ASSERT_THROW(kin->addReaction(R), CanteraError); + ASSERT_THROW(kin.addReaction(R), CanteraError); } */ @@ -406,33 +401,27 @@ class KineticsAddSpecies3 : public testing::Test { public: KineticsAddSpecies3() + : p_ref("../data/kineticsfromscratch.yaml") { - std::string yaml_file = "../data/kineticsfromscratch.yaml"; - std::string phase_name = "ohmech"; - - p = std::shared_ptr(newThermoPhase("ideal-gas")); - kin = std::shared_ptr(newKineticsMgr("GasKinetics")); - kin->addPhase(*p.get()); + std::vector th; + th.push_back(&p_ref); + kin_ref = newKinetics(th, "../data/kineticsfromscratch.yaml", "ohmech"); + kin.addPhase(p); - AnyMap h2o2 = AnyMap::fromYamlFile("h2o2.yaml"); - std::vector> S = getSpecies(h2o2["species"]); + std::vector> S = getSpecies( + AnyMap::fromYamlFile("h2o2.yaml")["species"]); for (auto sp : S) { species[sp->name] = sp; } - - sol = newSolution(yaml_file, phase_name); - p_ref = sol->thermo(); - kin_ref = sol->kinetics(); - - AnyMap root = AnyMap::fromYamlFile(yaml_file); - reactions = getReactions(root["reactions"], *kin_ref); + reactions = getReactions( + AnyMap::fromYamlFile("../data/kineticsfromscratch.yaml")["reactions"], + *kin_ref); } - std::shared_ptr sol; - std::shared_ptr p; - std::shared_ptr kin; - std::shared_ptr p_ref; - std::shared_ptr kin_ref; + IdealGasPhase p; + IdealGasPhase p_ref; + GasKinetics kin; + unique_ptr kin_ref; std::vector> reactions; std::map> species; @@ -444,85 +433,85 @@ class KineticsAddSpecies3 : public testing::Test kin_ref->setMultiplier(i, 1); } } - p->setState_TPX(1200, 5*OneAtm, X); - p_ref->setState_TPX(1200, 5*OneAtm, X); + p.setState_TPX(1200, 5*OneAtm, X); + p_ref.setState_TPX(1200, 5*OneAtm, X); - vector_fp k(kin->nReactions()), k_ref(kin_ref->nReactions()); - vector_fp w(kin->nTotalSpecies()), w_ref(kin_ref->nTotalSpecies()); + vector_fp k(kin.nReactions()), k_ref(kin_ref->nReactions()); + vector_fp w(kin.nTotalSpecies()), w_ref(kin_ref->nTotalSpecies()); - kin->getFwdRateConstants(k.data()); + kin.getFwdRateConstants(k.data()); kin_ref->getFwdRateConstants(k_ref.data()); - for (size_t i = 0; i < kin->nReactions(); i++) { + for (size_t i = 0; i < kin.nReactions(); i++) { EXPECT_DOUBLE_EQ(k_ref[i], k[i]) << "i = " << i << "; N = " << N; } - kin->getFwdRatesOfProgress(k.data()); + kin.getFwdRatesOfProgress(k.data()); kin_ref->getFwdRatesOfProgress(k_ref.data()); - for (size_t i = 0; i < kin->nReactions(); i++) { + for (size_t i = 0; i < kin.nReactions(); i++) { EXPECT_DOUBLE_EQ(k_ref[i], k[i]) << "i = " << i << "; N = " << N; } - kin->getRevRateConstants(k.data()); + kin.getRevRateConstants(k.data()); kin_ref->getRevRateConstants(k_ref.data()); - for (size_t i = 0; i < kin->nReactions(); i++) { + for (size_t i = 0; i < kin.nReactions(); i++) { EXPECT_DOUBLE_EQ(k_ref[i], k[i]) << "i = " << i << "; N = " << N; } - kin->getRevRatesOfProgress(k.data()); + kin.getRevRatesOfProgress(k.data()); kin_ref->getRevRatesOfProgress(k_ref.data()); - for (size_t i = 0; i < kin->nReactions(); i++) { + for (size_t i = 0; i < kin.nReactions(); i++) { EXPECT_DOUBLE_EQ(k_ref[i], k[i]) << "i = " << i << "; N = " << N; } - kin->getCreationRates(w.data()); + kin.getCreationRates(w.data()); kin_ref->getCreationRates(w_ref.data()); - for (size_t i = 0; i < kin->nTotalSpecies(); i++) { - size_t iref = p_ref->speciesIndex(p->speciesName(i)); - EXPECT_DOUBLE_EQ(w_ref[iref], w[i]) << "sp = " << p->speciesName(i) << "; N = " << N; + for (size_t i = 0; i < kin.nTotalSpecies(); i++) { + size_t iref = p_ref.speciesIndex(p.speciesName(i)); + EXPECT_DOUBLE_EQ(w_ref[iref], w[i]) << "sp = " << p.speciesName(i) << "; N = " << N; } } }; TEST_F(KineticsAddSpecies3, add_species_sequential) { - ASSERT_EQ((size_t) 0, kin->nReactions()); + ASSERT_EQ((size_t) 0, kin.nReactions()); for (auto s : {"AR", "O", "H2", "H", "OH"}) { - p->addSpecies(species[s]); + p.addSpecies(species[s]); } - kin->addReaction(reactions[0]); - ASSERT_EQ(5, (int) kin->nTotalSpecies()); + kin.addReaction(reactions[0]); + ASSERT_EQ(5, (int) kin.nTotalSpecies()); check_rates(1, "O:0.001, H2:0.1, H:0.005, OH:0.02, AR:0.88"); - p->addSpecies(species["O2"]); - p->addSpecies(species["H2O"]); - kin->addReaction(reactions[1]); - ASSERT_EQ(7, (int) kin->nTotalSpecies()); - ASSERT_EQ(2, (int) kin->nReactions()); + p.addSpecies(species["O2"]); + p.addSpecies(species["H2O"]); + kin.addReaction(reactions[1]); + ASSERT_EQ(7, (int) kin.nTotalSpecies()); + ASSERT_EQ(2, (int) kin.nReactions()); check_rates(2, "O:0.001, H2:0.1, H:0.005, OH:0.02, O2:0.5, AR:0.38"); - p->addSpecies(species["H2O2"]); - kin->addReaction(reactions[2]); - kin->addReaction(reactions[3]); + p.addSpecies(species["H2O2"]); + kin.addReaction(reactions[2]); + kin.addReaction(reactions[3]); check_rates(4, "O:0.001, H2:0.1, H:0.005, OH:0.02, O2:0.5, AR:0.38"); // no change check_rates(4, "O:0.001, H2:0.1, H:0.005, OH:0.02, O2:0.5, AR:0.35, H2O2:0.03"); - p->addSpecies(species["HO2"]); - kin->addReaction(reactions[4]); + p.addSpecies(species["HO2"]); + kin.addReaction(reactions[4]); check_rates(5, "O:0.01, H2:0.1, H:0.02, OH:0.03, O2:0.4, AR:0.3, H2O2:0.03, HO2:0.01"); } TEST_F(KineticsAddSpecies3, add_species_err_first) { for (auto s : {"AR", "O", "H2", "H"}) { - p->addSpecies(species[s]); + p.addSpecies(species[s]); } - ASSERT_THROW(kin->addReaction(reactions[0]), CanteraError); - ASSERT_EQ((size_t) 0, kin->nReactions()); + ASSERT_THROW(kin.addReaction(reactions[0]), CanteraError); + ASSERT_EQ((size_t) 0, kin.nReactions()); - p->addSpecies(species["OH"]); - kin->addReaction(reactions[0]); - ASSERT_EQ(5, (int) kin->nTotalSpecies()); - ASSERT_EQ((size_t) 1, kin->nReactions()); + p.addSpecies(species["OH"]); + kin.addReaction(reactions[0]); + ASSERT_EQ(5, (int) kin.nTotalSpecies()); + ASSERT_EQ((size_t) 1, kin.nReactions()); check_rates(1, "O:0.001, H2:0.1, H:0.005, OH:0.02, AR:0.88"); } From 26ee443993450841173ac37f6b9f7f442b259349 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 8 May 2021 08:31:15 -0500 Subject: [PATCH 55/84] [Kinetics] Revert to dynamic_cast in MultiRate.h --- include/cantera/kinetics/MultiRate.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/cantera/kinetics/MultiRate.h b/include/cantera/kinetics/MultiRate.h index 0ac10f768a0..2eac026ddfb 100644 --- a/include/cantera/kinetics/MultiRate.h +++ b/include/cantera/kinetics/MultiRate.h @@ -61,7 +61,7 @@ class MultiBulkRates final : public MultiRateBase virtual void add(const size_t rxn_index, ReactionRateBase& rate) override { m_indices[rxn_index] = m_rxn_rates.size(); - m_rxn_rates.emplace_back(rxn_index, static_cast(rate)); + m_rxn_rates.emplace_back(rxn_index, dynamic_cast(rate)); } virtual bool replace(const size_t rxn_index, @@ -78,7 +78,7 @@ class MultiBulkRates final : public MultiRateBase } if (m_indices.find(rxn_index) != m_indices.end()) { size_t j = m_indices[rxn_index]; - m_rxn_rates.at(j).second = static_cast(rate); + m_rxn_rates.at(j).second = dynamic_cast(rate); return true; } return false; From e9ce2aeddbad4d08461fc418b5b35c126aabe8fa Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 8 May 2021 10:03:05 -0500 Subject: [PATCH 56/84] [Kinetics] Add transitional deprecated Reaction3 properties --- interfaces/cython/cantera/_cantera.pxd | 5 +- interfaces/cython/cantera/reaction.pyx | 194 +++++++++++++++++++++++-- 2 files changed, 185 insertions(+), 14 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 934feaa9fb5..aa616a42c27 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -1174,7 +1174,10 @@ cdef class Reaction: @staticmethod cdef wrap(shared_ptr[CxxReaction]) -cdef class CustomReaction(Reaction): +cdef class Reaction3(Reaction): + pass + +cdef class CustomReaction(Reaction3): cdef CustomRate _rate cdef class Arrhenius: diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 6aeea8c3eff..fb5aade975e 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -1432,13 +1432,21 @@ cdef class BlowersMaselInterfaceReaction(BlowersMaselReaction): r.sticking_species = stringify(species) -cdef class ElementaryReaction3(Reaction): +cdef class Reaction3(Reaction): + """ Convenience class holding methods common to the Reaction3 framework """ + + def _deprecation_warning(self, attr, what="property"): + return ("\n{} '{}' to be removed after Cantera 2.6.\nThis {} is moved to " + "the {} object accessed via the 'rate' property." + ).format(what.capitalize(), attr, what, type(self.rate).__name__) + + +cdef class ElementaryReaction3(Reaction3): """ A reaction which follows mass-action kinetics with a modified Arrhenius reaction rate. - An example for the definition of an `ElementaryReaction3` object is given - as:: + An example for the definition of an `ElementaryReaction3` object is given as:: rxn = ElementaryReaction3( equation='O + H2 <=> H + OH', @@ -1485,14 +1493,33 @@ cdef class ElementaryReaction3(Reaction): def __set__(self, ArrheniusRate rate): self.er().setRate(rate._base) + property allow_negative_pre_exponential_factor: + """ + Get/Set whether the rate coefficient is allowed to have a negative + pre-exponential factor. + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ArrheniusRate.allow_negative_pre_exponential_factor`. + """ + def __get__(self): + attr = "allow_negative_pre_exponential_factor" + warnings.warn( + self._deprecation_warning(attr), DeprecationWarning) + return self.rate.allow_negative_pre_exponential_factor + def __set__(self, allow): + attr = "allow_negative_pre_exponential_factor" + warnings.warn( + self._deprecation_warning(attr), DeprecationWarning) + self.rate.allow_negative_pre_exponential_factor = allow + cdef class ThreeBodyReaction3(ElementaryReaction3): """ A reaction with a non-reacting third body "M" that acts to add or remove energy from the reacting species. - An example for the definition of an `ThreeBodyReaction3` object is given - as:: + An example for the definition of an `ThreeBodyReaction3` object is given as:: rxn = ThreeBodyReaction3( equation='2 O + M <=> O2 + M', @@ -1571,13 +1598,12 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): return self.thirdbody().efficiency(stringify(species)) -cdef class PlogReaction3(Reaction): +cdef class PlogReaction3(Reaction3): """ A pressure-dependent reaction parameterized by logarithmically interpolating between Arrhenius rate expressions at various pressures. - An example for the definition of an `PlogReaction3` object is given - as:: + An example for the definition of an `PlogReaction3` object is given as:: rxn = PlogReaction3( [{'P': 1013.25, 'rate-constant': {'A': 1.2124e+16, 'b': -0.5779, 'Ea': 45491376.8}}, @@ -1632,14 +1658,42 @@ cdef class PlogReaction3(Reaction): def __set__(self, PlogRate rate): self.pr().setRate(rate._base) + property rates: + """ + Get/Set the rate coefficients for this reaction, which are given as a + list of (pressure, `Arrhenius`) tuples. + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `PlogRate.rates`. + """ + def __get__(self): + warnings.warn( + self._deprecation_warning("rates"), DeprecationWarning) + return self.rate.rates -cdef class ChebyshevReaction3(Reaction): + def __set__(self, rates): + warnings.warn( + self._deprecation_warning("rates"), DeprecationWarning) + self.rate.rates = rates + + def __call__(self, float T, float P): + """ + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replacable by call to `rate` property. + """ + warnings.warn( + self._deprecation_warning("__call__", "method"), DeprecationWarning) + return self.rate(T, P) + + +cdef class ChebyshevReaction3(Reaction3): """ A pressure-dependent reaction parameterized by a bivariate Chebyshev polynomial in temperature and pressure. - An example for the definition of an `PlogReaction3` object is given - as:: + An example for the definition of an `PlogReaction3` object is given as:: rxm = ChebyshevRate(Tmin=290., Tmax=3000., Pmin=1000., Pmax=10000000.0, data=[[8.2883, -1.1397, -0.12059, 0.016034], @@ -1688,14 +1742,128 @@ cdef class ChebyshevReaction3(Reaction): self.rate = rate property rate: - """ Get/Set the `Chebyshev` rate coefficients for this reaction. """ + """ Get/Set the `ChebyshevRate` rate coefficients for this reaction. """ def __get__(self): return ChebyshevRate.wrap(self.cr().rate()) def __set__(self, ChebyshevRate rate): self.cr().setRate(rate._base) + property Tmin: + """ + Minimum temperature [K] for the Chebyshev fit + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ChebyshevRate.Tmin`. + """ + def __get__(self): + warnings.warn( + self._deprecation_warning("Tmin"), DeprecationWarning) + return self.rate.Tmin + + property Tmax: + """ + Maximum temperature [K] for the Chebyshev fit + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ChebyshevRate.Tmax`. + """ + def __get__(self): + warnings.warn( + self._deprecation_warning("Tmax"), DeprecationWarning) + return self.rate.Tmax + + property Pmin: + """ + Minimum pressure [Pa] for the Chebyshev fit + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ChebyshevRate.Pmin`. + """ + def __get__(self): + warnings.warn( + self._deprecation_warning("Pmin"), DeprecationWarning) + return self.rate.Pmin + + property Pmax: + """ Maximum pressure [Pa] for the Chebyshev fit + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ChebyshevRate.Pmax`. + """ + def __get__(self): + warnings.warn( + self._deprecation_warning("Pmax"), DeprecationWarning) + return self.rate.Pmax + + property nPressure: + """ + Number of pressures over which the Chebyshev fit is computed + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ChebyshevRate.nPressure`. + """ + def __get__(self): + warnings.warn( + self._deprecation_warning("nPressure"), DeprecationWarning) + return self.rate.nPressure + + property nTemperature: + """ + Number of temperatures over which the Chebyshev fit is computed + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ChebyshevRate.nTemperature`. + """ + def __get__(self): + warnings.warn( + self._deprecation_warning("nTemperature"), DeprecationWarning) + return self.rate.nTemperature + + property coeffs: + """ + 2D array of Chebyshev coefficients of size `(nTemperature, nPressure)`. + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ChebyshevRate.coeffs`. + """ + def __get__(self): + warnings.warn( + self._deprecation_warning("coeffs"), DeprecationWarning) + return self.rate.coeffs + + def set_parameters(self, Tmin, Tmax, Pmin, Pmax, coeffs): + """ + Simultaneously set values for `Tmin`, `Tmax`, `Pmin`, `Pmax`, and + `coeffs`. + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by `ChebyshevRate` construcor. + """ + warnings.warn("Method 'set_parameters' to be removed after Cantera 2.6. " + "Method is replacable by assigning a new 'ChebyshevRate' object to the " + "rate property.", DeprecationWarning) + self.rate = ChebyshevRate(Tmin, Tmax, Pmin, Pmax, coeffs) + + def __call__(self, float T, float P): + """ + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replacable by call to `rate` property. + """ + warnings.warn( + self._deprecation_warning("__call__", "method"), DeprecationWarning) + return self.rate(T, P) + -cdef class CustomReaction(Reaction): +cdef class CustomReaction(Reaction3): """ A reaction which follows mass-action kinetics with a custom reaction rate. From febce88434b8b71c4132be68d2e6e721a1dfae9c Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 9 May 2021 10:36:12 -0500 Subject: [PATCH 57/84] [Kinetics] Update Plog3 instantiation --- interfaces/cython/cantera/reaction.pyx | 42 ++++++++++++++------------ 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index fb5aade975e..1dc5b07eccf 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -124,12 +124,18 @@ cdef class PlogRate(_ReactionRate): """ def __cinit__(self, rates=None, input_data=None, init=True): - if isinstance(input_data, dict) and init: - self._base.reset(new CxxPlogRate(dict_to_anymap(input_data))) + if init and isinstance(rates, list): + self.rates = rates + + elif init: + if isinstance(input_data, dict): + self._base.reset(new CxxPlogRate(dict_to_anymap(input_data))) + elif rates is None: + self._base.reset(new CxxPlogRate(dict_to_anymap({}))) + else: + raise TypeError("Invalid type for parameter 'rates'") self.base = self._base.get() self.rate = (self.base) - elif rates and init: - self.rates = rates @staticmethod cdef wrap(shared_ptr[CxxReactionRateBase] rate): @@ -1035,10 +1041,8 @@ cdef class PlogReaction(Reaction): if init and equation and kinetics: spec = {'equation': equation, 'type': self.reaction_type} - if isinstance(rate, list): - spec['rate-constants'] = [] - for r in rate: - spec['rate-constants'].append({'P': r['P'], **r['rate-constant']}) + if isinstance(rate, dict): + spec.update(rate) else: raise TypeError("Invalid rate definition") @@ -1064,7 +1068,7 @@ cdef class PlogReaction(Reaction): cdef multimap[double,CxxArrhenius] ratemap cdef Arrhenius rate cdef pair[double,CxxArrhenius] item - for p,rate in rates: + for p, rate in rates: item.first = p item.second = deref(rate.rate) ratemap.insert(item) @@ -1605,11 +1609,11 @@ cdef class PlogReaction3(Reaction3): An example for the definition of an `PlogReaction3` object is given as:: - rxn = PlogReaction3( - [{'P': 1013.25, 'rate-constant': {'A': 1.2124e+16, 'b': -0.5779, 'Ea': 45491376.8}}, - {'P': 101325., 'rate-constant': {'A': 4.9108e+31, 'b': -4.8507, 'Ea': 103649395.2}}, - {'P': 1013250., 'rate-constant': {'A': 1.2866e+47, 'b': -9.0246, 'Ea': 166508556.0}}, - {'P': 10132500., 'rate-constant': {'A': 5.9632e+56, 'b': -11.529, 'Ea': 220076726.4}}]) + rxn = PlogReaction3(equation='H2 + O2 <=> 2 OH', rate={"rate-constants": + [{'P': 1013.25, 'A': 1.2124e+16, 'b': -0.5779, 'Ea': 45491376.8}, + {'P': 101325., 'A': 4.9108e+31, 'b': -4.8507, 'Ea': 103649395.2}, + {'P': 1013250., 'A': 1.2866e+47, 'b': -9.0246, 'Ea': 166508556.0}, + {'P': 10132500., 'A': 5.9632e+56, 'b': -11.529, 'Ea': 220076726.4}]}) The YAML description corresponding to this reaction is:: @@ -1635,10 +1639,8 @@ cdef class PlogReaction3(Reaction3): if init and equation and kinetics: spec = {"equation": equation, "type": self.reaction_type} - if isinstance(rate, list): - spec["rate-constants"] = [] - for r in rate: - spec["rate-constants"].append({"P": r["P"], **r["rate-constant"]}) + if isinstance(rate, dict): + spec.update(rate) elif isinstance(rate, PlogRate) or rate is None: pass else: @@ -1675,7 +1677,9 @@ cdef class PlogReaction3(Reaction3): def __set__(self, rates): warnings.warn( self._deprecation_warning("rates"), DeprecationWarning) - self.rate.rates = rates + rate_ = self.rate + rate_.rates = rates + self.rate = rate_ def __call__(self, float T, float P): """ From 5925fff9c3c4ddf4034b767593eec1a181f1841b Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 10 May 2021 01:51:48 -0500 Subject: [PATCH 58/84] [Kinetics] Fix logic for modifyReaction exception --- interfaces/cython/cantera/reaction.pyx | 3 +++ src/kinetics/BulkKinetics.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 1dc5b07eccf..9975851478f 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -1675,6 +1675,9 @@ cdef class PlogReaction3(Reaction3): return self.rate.rates def __set__(self, rates): + warnings.warn("Property 'rates' to be removed after Cantera 2.6. " + "Setter is replacable by assigning a new 'PlogRate' object created " + "from rates to the rate property.", DeprecationWarning) warnings.warn( self._deprecation_warning("rates"), DeprecationWarning) rate_ = self.rate diff --git a/src/kinetics/BulkKinetics.cpp b/src/kinetics/BulkKinetics.cpp index ff199a727d0..f2aa2a85446 100644 --- a/src/kinetics/BulkKinetics.cpp +++ b/src/kinetics/BulkKinetics.cpp @@ -195,7 +195,7 @@ void BulkKinetics::modifyReaction(size_t i, shared_ptr rNew) 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()) { + if (m_bulk_types.find(rate->type()) == m_bulk_types.end()) { throw CanteraError("BulkKinetics::modifyReaction", "Evaluator not available for type '{}'.", rate->type()); } From 845721459a8c1a0a23d3c8ceac49638d9238c7d2 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 8 May 2021 16:08:44 -0500 Subject: [PATCH 59/84] [Tests] Add tests for deprecated reaction properties --- .../cython/cantera/test/test_reaction.py | 220 ++++++++++++++++-- 1 file changed, 204 insertions(+), 16 deletions(-) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 2d71d90016c..dc8b3033421 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -7,6 +7,7 @@ class TestImplicitThirdBody(utilities.CanteraTest): + # tests for three-body reactions with specified collision partner @classmethod def setUpClass(cls): @@ -14,6 +15,7 @@ def setUpClass(cls): cls.gas = ct.Solution("gri30.yaml") def test_implicit_three_body(self): + # check equivalency of auto-detected and explicit specification yaml1 = """ equation: H + 2 O2 <=> HO2 + O2 rate-constant: {A: 2.08e+19, b: -1.24, Ea: 0.0} @@ -73,6 +75,7 @@ def test_duplicate(self): Path(fname).unlink() def test_short_serialization(self): + # check that serialized output is compact yaml = """ equation: H + O2 + H2O <=> HO2 + H2O rate-constant: {A: 1.126e+19, b: -0.76, Ea: 0.0} @@ -85,14 +88,16 @@ def test_short_serialization(self): self.assertNotIn("efficiencies", input_data) def test_non_integer_stoich(self): + # check that non-integer coefficients prevent automatic conversion yaml = """ - equation: H + 1.5 O2 <=> HO2 + O2 + equation: 2 H + 1.5 O2 <=> H2O + O2 rate-constant: {A: 2.08e+19, b: -1.24, Ea: 0.0} """ rxn = ct.Reaction.fromYaml(yaml, self.gas) self.assertEqual(rxn.reaction_type, "elementary") def test_not_three_body(self): + # check that insufficient reactants prevent automatic conversion yaml = """ equation: HCNO + H <=> H + HNCO # Reaction 270 rate-constant: {A: 2.1e+15, b: -0.69, Ea: 2850.0} @@ -101,6 +106,7 @@ def test_not_three_body(self): self.assertEqual(rxn.reaction_type, "elementary") def test_user_override(self): + # check that type specification prevents automatic conversion yaml = """ equation: H + 2 O2 <=> HO2 + O2 rate-constant: {A: 2.08e+19, b: -1.24, Ea: 0.0} @@ -111,6 +117,7 @@ def test_user_override(self): class TestReactionRate(utilities.CanteraTest): + # test suite for reaction rate expressions _cls = None _type = None @@ -127,11 +134,13 @@ def setUpClass(cls): cls.gas.TP = 900, 2*ct.one_atm def test_type(self): + # check reaction type if self._index is None: return self.assertIn(self._type, "{}".format(self.rate)) def test_rate_T(self): + # check evaluation as a function of temperature only if self._index is None: return if self._uses_pressure: @@ -143,12 +152,14 @@ def test_rate_T(self): self.gas.forward_rate_constants[self._index]) def test_rate_TP(self): + # check evaluation as a function of temperature and pressure if self._index is None: return self.assertNear(self.rate(self.gas.T, self.gas.P), self.gas.forward_rate_constants[self._index]) def test_input(self): + # check instantiation based on input_data if self._input is None or self._cls is None: return rate = self._cls(input_data=self._input) @@ -157,6 +168,7 @@ def test_input(self): self.rate(self.gas.T, self.gas.P)) def test_roundtrip(self): + # check round-trip instantiation via input_data if self._index is None: return input_data = self.rate.input_data @@ -166,6 +178,7 @@ def test_roundtrip(self): class TestArrheniusRate(TestReactionRate): + # test Arrhenius rate expressions _type = "Arrhenius" _uses_pressure = False @@ -180,23 +193,20 @@ def setUp(self): self.rate = ct.ArrheniusRate(self.A, self.b, self.Ea) def test_parameters(self): + # test reaction rate parameters self.assertEqual(self.A, self.rate.pre_exponential_factor) self.assertEqual(self.b, self.rate.temperature_exponent) self.assertEqual(self.Ea, self.rate.activation_energy) - def test_allow_negative_pre_exponential_factor1(self): + def test_allow_negative_pre_exponential_factor(self): + # test reaction rate property self.assertFalse(self.rate.allow_negative_pre_exponential_factor) self.rate.allow_negative_pre_exponential_factor = True self.assertTrue(self.rate.allow_negative_pre_exponential_factor) - def test_allow_negative_pre_exponential_factor2(self): - # modify value in memory - self.assertFalse(self.gas.reaction(self._index).rate.allow_negative_pre_exponential_factor) - self.gas.reaction(self._index).rate.allow_negative_pre_exponential_factor = True - self.assertTrue(self.gas.reaction(self._index).rate.allow_negative_pre_exponential_factor) - class TestPlogRate(TestReactionRate): + # test Plog rate expressions _type = "Plog" _uses_pressure = True @@ -214,8 +224,47 @@ def setUp(self): (1013250., ct.Arrhenius(1.2866e+47, -9.0246, 166508556.0)), (10132500., ct.Arrhenius(5.9632e+56, -11.529, 220076726.4))]) + def test_get_rates(self): + # test getter for property rates + rates = self.rate.rates + self.assertIsInstance(rates, list) + + other = self._input["rate-constants"] + self.assertEqual(len(rates), len(other)) + for index, item in enumerate(rates): + P, rate = item + self.assertNear(P, other[index]["P"]) + self.assertNear(rate.pre_exponential_factor, other[index]["A"]) + self.assertNear(rate.temperature_exponent, other[index]["b"]) + self.assertNear(rate.activation_energy, other[index]["Ea"]) + + def test_set_rates(self): + # test setter for property rates + other = [ + {"P": 100., "A": 1.2124e+16, "b": -1., "Ea": 45491376.8}, + {"P": 10000., "A": 4.9108e+31, "b": -2., "Ea": 103649395.2}, + {"P": 1000000., "A": 1.2866e+47, "b": -3., "Ea": 166508556.0}] + rate = ct.PlogRate([(o["P"], ct.Arrhenius(o["A"], o["b"], o["Ea"])) + for o in other]) + rates = rate.rates + self.assertEqual(len(rates), len(other)) + + self.assertEqual(len(rates), len(other)) + for index, item in enumerate(rates): + P, rate = item + self.assertNear(P, other[index]["P"]) + self.assertNear(rate.pre_exponential_factor, other[index]["A"]) + self.assertNear(rate.temperature_exponent, other[index]["b"]) + self.assertNear(rate.activation_energy, other[index]["Ea"]) + + def test_no_rates(self): + # test instantiation of empty rate + rate = ct.PlogRate() + self.assertIsInstance(rate.rates, list) + class TestChebyshevRate(TestReactionRate): + # test Chebyshev rate expressions _type = "Chebyshev" _uses_pressure = True @@ -236,6 +285,7 @@ def setUp(self): self.rate = ct.ChebyshevRate(self.Tmin, self.Tmax, self.Pmin, self.Pmax, self.coeffs) def test_parameters(self): + # test getters for rate properties self.assertEqual(self.Tmin, self.rate.Tmin) self.assertEqual(self.Tmax, self.rate.Tmax) self.assertEqual(self.Pmin, self.rate.Pmin) @@ -244,6 +294,7 @@ def test_parameters(self): class TestReaction(utilities.CanteraTest): + # test suite for reaction expressions _cls = None _equation = None @@ -254,6 +305,9 @@ class TestReaction(utilities.CanteraTest): _type = None _yaml = None _input = None + _deprecated_getters = {} + _deprecated_setters = {} + _deprecated_callers = {} @classmethod def setUpClass(cls): @@ -264,6 +318,7 @@ def setUpClass(cls): cls.species = cls.gas.species() def check_rxn(self, rxn): + # helper function that checks reaction configuration ix = self._index self.assertEqual(rxn.reaction_type, self._type) self.assertEqual(rxn.reactants, self.gas.reaction(ix).reactants) @@ -275,6 +330,7 @@ def check_rxn(self, rxn): self.check_solution(gas2) def check_solution(self, gas2): + # helper function that checks evaluation of reaction rates ix = self._index self.assertEqual(gas2.reaction_type_str(0), self._type) self.assertNear(gas2.forward_rate_constants[0], @@ -283,6 +339,7 @@ def check_solution(self, gas2): self.gas.net_rates_of_progress[ix]) def test_rate(self): + # check consistency of reaction rate and forward rate constant if self._rate_obj is None: return if "Rate" in type(self._rate_obj).__name__: @@ -292,6 +349,7 @@ def test_rate(self): self.assertNear(self._rate_obj(self.gas.T), self.gas.forward_rate_constants[self._index]) def test_from_parts(self): + # check instantiation from parts (reactants, products, rate expression) if self._cls is None or not hasattr(self._cls, "rate"): return orig = self.gas.reaction(self._index) @@ -300,24 +358,28 @@ def test_from_parts(self): self.check_rxn(rxn) def test_from_dict(self): + # check instantiation from keywords / rate defined by dictionary if self._cls is None: return rxn = self._cls(equation=self._equation, rate=self._rate, kinetics=self.gas, **self._kwargs) self.check_rxn(rxn) def test_from_yaml(self): + # check instantiation from yaml string if self._yaml is None: return rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) self.check_rxn(rxn) def test_from_rate(self): + # check instantiation from keywords / rate provided as object if self._cls is None or self._rate_obj is None: return rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) self.check_rxn(rxn) def test_add_rxn(self): + # check adding new reaction to solution if self._cls is None or self._rate_obj is None: return gas2 = ct.Solution(thermo="IdealGas", kinetics="GasKinetics", @@ -328,13 +390,15 @@ def test_add_rxn(self): gas2.add_reaction(rxn) self.check_solution(gas2) - def test_wrong_rate(self): + def test_raises_invalid_rate(self): + # check exception for instantiation from keywords / invalid rate if self._cls is None: return with self.assertRaises(TypeError): rxn = self._cls(equation=self._equation, rate=(), kinetics=self.gas, **self._kwargs) def test_no_rate(self): + # check behavior for instantiation from keywords / no rate if self._cls is None or not hasattr(self._cls, "rate"): return rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) @@ -351,6 +415,7 @@ def test_no_rate(self): self.assertNear(gas2.net_rates_of_progress[0], 0.) def test_replace_rate(self): + # check replacing reaction rate expression if self._cls is None or self._rate_obj is None: return rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) @@ -358,6 +423,7 @@ def test_replace_rate(self): self.check_rxn(rxn) def test_roundtrip(self): + # check round-trip instantiation via input_data if self._cls is None or self._type.endswith("-old"): return rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) @@ -366,8 +432,60 @@ def test_roundtrip(self): rxn2 = self._cls(equation=self._equation, rate=rate_obj, kinetics=self.gas, **self._kwargs) self.check_rxn(rxn2) + def check_equal(self, one, two): + # helper function for deprecation tests + self.assertEqual(type(one), type(two)) + if isinstance(one, (list, tuple, np.ndarray)): + self.assertArrayNear(one, two) + else: + self.assertEqual(one, two) + + def test_deprecated_getters(self): + # check property getters deprecated in new framework + if self._yaml is None: + return + + rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) + for attr, default in self._deprecated_getters.items(): + if self._type.endswith("-old"): + self.check_equal(getattr(rxn, attr), default) + else: + with self.assertWarnsRegex(DeprecationWarning, "property is moved"): + self.check_equal(getattr(rxn, attr), default) + + def test_deprecated_setters(self): + # check property setters deprecated in new framework + if self._yaml is None: + return + + rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) + for attr, new in self._deprecated_setters.items(): + if self._type.endswith("-old"): + setattr(rxn, attr, new) + self.check_equal(getattr(rxn, attr), new) + else: + with self.assertWarnsRegex(DeprecationWarning, "property is moved"): + setattr(rxn, attr, new) + with self.assertWarnsRegex(DeprecationWarning, "property is moved"): + self.check_equal(getattr(rxn, attr), new) + + def test_deprecated_callers (self): + # check methods deprecated in new framework + if self._yaml is None: + return + + rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) + for state, value in self._deprecated_callers.items(): + T, P = state + if self._type.endswith("-old"): + self.check_equal(rxn(T, P), value) + else: + with self.assertWarnsRegex(DeprecationWarning, "method is moved"): + self.check_equal(rxn(T, P), value) + class TestElementary(TestReaction): + # test legacy version of elementary reaction _cls = ct.ElementaryReaction _equation = "H2 + O <=> H + OH" @@ -381,9 +499,12 @@ class TestElementary(TestReaction): type: elementary-old rate-constant: {A: 38.7, b: 2.7, Ea: 6260.0 cal/mol} """ + _deprecated_getters = {"allow_negative_pre_exponential_factor": False} + _deprecated_setters = {"allow_negative_pre_exponential_factor": True} class TestElementary3(TestElementary): + # test updated version of elementary reaction _cls = ct.ElementaryReaction3 _rate_obj = ct.ArrheniusRate(38.7, 2.7, 2.619184e+07) @@ -394,7 +515,8 @@ class TestElementary3(TestElementary): """ -class TestThreeBody(TestReaction): +class TestThreeBody(TestElementary): + # test legacy version of three-body reaction _cls = ct.ThreeBodyReaction _equation = "2 O + M <=> O2 + M" @@ -411,6 +533,7 @@ class TestThreeBody(TestReaction): """ def test_from_parts(self): + # overload default reaction creation from parts orig = self.gas.reaction(self._index) rxn = self._cls(orig.reactants, orig.products) rxn.rate = self._rate_obj @@ -422,12 +545,14 @@ def test_rate(self): pass def test_efficiencies(self): + # check efficiencies rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) self.assertEqual(rxn.efficiencies, self._kwargs["efficiencies"]) class TestThreeBody3(TestThreeBody): + # test updated version of three-body reaction _cls = ct.ThreeBodyReaction3 _rate_obj = ct.ArrheniusRate(1.2e11, -1., 0.) @@ -441,6 +566,7 @@ class TestThreeBody3(TestThreeBody): class TestImplicitThreeBody3(TestThreeBody): + # test three-body reactions with explicit collision parther _cls = ct.ThreeBodyReaction3 _equation = "H + 2 O2 <=> HO2 + O2" @@ -454,11 +580,13 @@ class TestImplicitThreeBody3(TestThreeBody): """ def test_efficiencies(self): + # overload of default tester rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas) self.assertEqual(rxn.efficiencies, {"O2": 1.}) self.assertEqual(rxn.default_efficiency, 0.) def test_from_parts(self): + # overload of default tester orig = self.gas.reaction(self._index) rxn = self._cls(orig.reactants, orig.products) rxn.rate = self._rate_obj @@ -468,13 +596,15 @@ def test_from_parts(self): class TestPlog(TestReaction): + # test legacy version of Plog reaction _cls = ct.PlogReaction _equation = "H2 + O2 <=> 2 OH" - _rate = [{"P": 1013.25, "rate-constant": {"A": 1.2124e+16, "b": -0.5779, "Ea": 45491376.8}}, - {"P": 101325., "rate-constant": {"A": 4.9108e+31, "b": -4.8507, "Ea": 103649395.2}}, - {"P": 1013250., "rate-constant": {"A": 1.2866e+47, "b": -9.0246, "Ea": 166508556.0}}, - {"P": 10132500., "rate-constant": {"A": 5.9632e+56, "b": -11.529, "Ea": 220076726.4}}] + _rate = {"rate-constants": [ + {"P": 1013.25, "A": 1.2124e+16, "b": -0.5779, "Ea": 45491376.8}, + {"P": 101325., "A": 4.9108e+31, "b": -4.8507, "Ea": 103649395.2}, + {"P": 1013250., "A": 1.2866e+47, "b": -9.0246, "Ea": 166508556.0}, + {"P": 10132500., "A": 5.9632e+56, "b": -11.529, "Ea": 220076726.4}]} _type = "pressure-dependent-Arrhenius-old" _index = 3 _yaml = """ @@ -486,9 +616,50 @@ class TestPlog(TestReaction): - {P: 10.0 atm, A: 1.2866e+47, b: -9.0246, Ea: 3.97965e+04 cal/mol} - {P: 100.0 atm, A: 5.9632e+56, b: -11.529, Ea: 5.25996e+04 cal/mol} """ + _deprecated_callers = {(1000., ct.one_atm): 530968934612.9017} + + def check_rates(self, rates, other): + # helper function used by deprecation tests + self.assertEqual(len(rates), len(other)) + for index, item in enumerate(rates): + P, rate = item + self.assertNear(P, other[index]["P"]) + self.assertNear(rate.pre_exponential_factor, other[index]["A"]) + self.assertNear(rate.temperature_exponent, other[index]["b"]) + self.assertNear(rate.activation_energy, other[index]["Ea"]) + + def test_deprecated_getters(self): + # overload default tester for deprecated property getters + rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) + if self._type.endswith("-old"): + self.check_rates(rxn.rates, self._rate["rate-constants"]) + else: + with self.assertWarnsRegex(DeprecationWarning, "property is moved"): + self.check_rates(rxn.rates, self._rate["rate-constants"]) + + def test_deprecated_setters(self): + # overload default tester for deprecated property setters + _rate = [ + {"P": 100., "A": 4.9108e+31, "b": -4.8507, "Ea": 103649395.2}, + {"P": 10000., "A": 1.2866e+47, "b": -9.0246, "Ea": 166508556.0}, + {"P": 1000000., "A": 5.9632e+56, "b": -11.529, "Ea": 220076726.4}] + rate = ct.PlogRate([(o["P"], ct.Arrhenius(o["A"], o["b"], o["Ea"])) + for o in _rate]) + rates = rate.rates + + rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) + if self._type.endswith("-old"): + rxn.rates = rates + self.check_rates(rxn.rates, _rate) + else: + with self.assertWarnsRegex(DeprecationWarning, "property is moved"): + rxn.rates = rates + with self.assertWarnsRegex(DeprecationWarning, "property is moved"): + self.check_rates(rxn.rates, _rate) class TestPlog3(TestPlog): + # test updated version of Plog reaction _cls = ct.PlogReaction3 _rate_obj = ct.PlogRate([(1013.25, ct.Arrhenius(1.2124e+16, -0.5779, 45491376.8)), @@ -508,6 +679,7 @@ class TestPlog3(TestPlog): class TestChebyshev(TestReaction): + # test legacy version of Cheyshev reaction _cls = ct.ChebyshevReaction _equation = "HO2 <=> OH + O" @@ -527,9 +699,19 @@ class TestChebyshev(TestReaction): - [1.9764, 1.0037, 7.2865e-03, -0.030432] - [0.3177, 0.26889, 0.094806, -7.6385e-03] """ + _deprecated_getters = {"nPressure": 4, "nTemperature": 3} + _deprecated_callers = {(1000., ct.one_atm): 2858762454.1119065} + + @classmethod + def setUpClass(obj): + TestReaction.setUpClass() + obj._deprecated_getters.update({"coeffs": np.array(obj._rate["data"])}) + obj._deprecated_getters.update( + {k: v for k, v in obj._rate.items() if k != "data"}) class TestChebyshev3(TestChebyshev): + # test updated version of Cheyshev reaction _cls = ct.ChebyshevReaction3 _rate_obj = ct.ChebyshevRate(Tmin=290., Tmax=3000., Pmin=1000., Pmax=10000000.0, @@ -550,6 +732,7 @@ class TestChebyshev3(TestChebyshev): class TestCustom(TestReaction): + # test Custom reaction # probe O + H2 <=> H + OH _cls = ct.CustomReaction @@ -564,6 +747,7 @@ def setUp(self): self._rate = lambda T: 38.7 * T**2.7 * exp(-3150.15428/T) def test_no_rate(self): + # overload default tester for missing rate definition 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) @@ -576,19 +760,23 @@ def test_no_rate(self): gas2.forward_rate_constants def test_roundtrip(self): + # overload default tester for round trip pass - def test_from_func(self): + def test_from_func1(self): + # check instantiation from keywords / rate provided as func1 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): + # check result of rate expression 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): + def test_custom_lambda(self): + # check instantiation from keywords / rate provided as lambda function rxn = ct.CustomReaction(equation=self._equation, rate=lambda T: 38.7 * T**2.7 * exp(-3150.15428/T), kinetics=self.gas) From 52cf2cba771a4c65fea169be3d5af7f09c6bac91 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Thu, 13 May 2021 06:00:24 -0500 Subject: [PATCH 60/84] [Kinetics] Save input node within ReactionRate.h --- include/cantera/kinetics/ReactionRate.h | 5 +- src/kinetics/Reaction.cpp | 6 +- src/kinetics/ReactionRate.cpp | 73 ++++++++++++++++--------- 3 files changed, 55 insertions(+), 29 deletions(-) diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index d3106ee5c96..81935919638 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -41,7 +41,7 @@ class ReactionRateBase //! Set parameters //! @param node AnyMap object containing reaction rate specification //! @param rate_units Description of units used for rate parameters - virtual void setParameters(const AnyMap& node, const Units& rate_units) = 0; + virtual void setParameters(const AnyMap& node, const Units& rate_units); //! Get parameters //! Store the parameters of a ReactionRate needed to reconstruct an identical @@ -111,6 +111,9 @@ class ReactionRateBase AnyMap parameters() const; protected: + //! Input data used for specific models + AnyMap input; + //! 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. diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 43307df2f6d..0e8747ad217 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -1008,7 +1008,8 @@ void ThreeBodyReaction3::getParameters(AnyMap& reactionNode) const } } -std::string ThreeBodyReaction3::reactantString() const { +std::string ThreeBodyReaction3::reactantString() const +{ if (specified_collision_partner) { return ElementaryReaction3::reactantString() + " + " + m_third_body->efficiencies.begin()->first; @@ -1017,7 +1018,8 @@ std::string ThreeBodyReaction3::reactantString() const { } } -std::string ThreeBodyReaction3::productString() const { +std::string ThreeBodyReaction3::productString() const +{ if (specified_collision_partner) { return ElementaryReaction3::productString() + " + " + m_third_body->efficiencies.begin()->first; diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 7524c2e91ca..2f29398c2ae 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -10,6 +10,12 @@ namespace Cantera { +void ReactionRateBase::setParameters(const AnyMap& node, const Units& rate_units) +{ + units = rate_units; + input = node; +} + AnyMap ReactionRateBase::parameters(const Units& rate_units) const { AnyMap out; @@ -36,11 +42,13 @@ ArrheniusRate::ArrheniusRate(double A, double b, double E) { } -ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rate_units) { +ArrheniusRate::ArrheniusRate(const AnyMap& node, const Units& rate_units) +{ setParameters(node, rate_units); } -ArrheniusRate::ArrheniusRate(const AnyMap& node) { +ArrheniusRate::ArrheniusRate(const AnyMap& node) +{ setParameters(node, Units(1.)); } @@ -52,8 +60,9 @@ ArrheniusRate::ArrheniusRate(const Arrhenius& arr, bool allow_negative_A) { } -void ArrheniusRate::setParameters(const AnyMap& node, const Units& rate_units) { - units = rate_units; +void ArrheniusRate::setParameters(const AnyMap& node, const Units& rate_units) +{ + ReactionRateBase::setParameters(node, rate_units); allow_negative_pre_exponential_factor = node.getBool("negative-A", false); if (!node.hasKey("rate-constant")) { return; @@ -62,7 +71,8 @@ void ArrheniusRate::setParameters(const AnyMap& node, const Units& rate_units) { } void ArrheniusRate::getParameters(AnyMap& rateNode, - const Units& rate_units) const { + const Units& rate_units) const +{ if (allow_negative_pre_exponential_factor) { rateNode["negative-A"] = true; } @@ -71,7 +81,8 @@ void ArrheniusRate::getParameters(AnyMap& rateNode, rateNode["rate-constant"] = std::move(node); } -void ArrheniusRate::validate(const std::string& equation) { +void ArrheniusRate::validate(const std::string& equation) +{ if (!allow_negative_pre_exponential_factor && preExponentialFactor() < 0) { throw CanteraError("ArrheniusRate::validate", "Undeclared negative pre-exponential factor found in reaction '" @@ -79,26 +90,28 @@ void ArrheniusRate::validate(const std::string& equation) { } } -PlogRate::PlogRate() - : Plog() { +PlogRate::PlogRate() : Plog() +{ } PlogRate::PlogRate(const std::multimap& rates) - : Plog(rates) { + : Plog(rates) +{ } -PlogRate::PlogRate(const AnyMap& node, const Units& rate_units) - : Plog() { +PlogRate::PlogRate(const AnyMap& node, const Units& rate_units) : Plog() +{ setParameters(node, rate_units); } -PlogRate::PlogRate(const AnyMap& node) - : Plog() { +PlogRate::PlogRate(const AnyMap& node) : Plog() +{ setParameters(node, Units(1.)); } -void PlogRate::setParameters(const AnyMap& node, const Units& rate_units) { - units = rate_units; +void PlogRate::setParameters(const AnyMap& node, const Units& rate_units) +{ + ReactionRateBase::setParameters(node, rate_units); if (!node.hasKey("rate-constants")) { // ensure that Plog has defined state and produces zero reaction rate AnyMap rate = AnyMap::fromYamlString( @@ -113,8 +126,8 @@ void PlogRate::setParameters(const AnyMap& node, const Units& rate_units) { node.units(), rate_units); } -void PlogRate::getParameters(AnyMap& rateNode, - const Units& rate_units) const { +void PlogRate::getParameters(AnyMap& rateNode, const Units& rate_units) const +{ Plog::getParameters(rateNode, rate_units); } @@ -124,21 +137,25 @@ ChebyshevRate3::ChebyshevRate3() ChebyshevRate3::ChebyshevRate3(double Tmin, double Tmax, double Pmin, double Pmax, const Array2D& coeffs) - : Chebyshev(Tmin, Tmax, Pmin, Pmax, coeffs) { + : Chebyshev(Tmin, Tmax, Pmin, Pmax, coeffs) +{ } ChebyshevRate3::ChebyshevRate3(const AnyMap& node, const Units& rate_units) - : Chebyshev() { + : Chebyshev() +{ setParameters(node, rate_units); } ChebyshevRate3::ChebyshevRate3(const AnyMap& node) - : Chebyshev() { + : Chebyshev() +{ setParameters(node, Units(1.)); } -void ChebyshevRate3::setParameters(const AnyMap& node, const Units& rate_units) { - units = rate_units; +void ChebyshevRate3::setParameters(const AnyMap& node, const Units& rate_units) +{ + ReactionRateBase::setParameters(node, rate_units); if (!node.hasKey("data")) { // ensure that Chebyshev has defined state and produces zero reaction rate AnyMap rate = AnyMap::fromYamlString( @@ -152,21 +169,25 @@ void ChebyshevRate3::setParameters(const AnyMap& node, const Units& rate_units) } void ChebyshevRate3::getParameters(AnyMap& rateNode, - const Units& rate_units) const { + const Units& rate_units) const +{ Chebyshev::getParameters(rateNode, rate_units); } -void ChebyshevRate3::validate(const std::string& equation) { +void ChebyshevRate3::validate(const std::string& equation) +{ } CustomFunc1Rate::CustomFunc1Rate() : m_ratefunc(0) {} -void CustomFunc1Rate::setRateFunction(shared_ptr f) { +void CustomFunc1Rate::setRateFunction(shared_ptr f) +{ m_ratefunc = f; } double CustomFunc1Rate::eval(const CustomFunc1Data& shared_data, - double concm) const { + double concm) const +{ if (m_ratefunc) { return m_ratefunc->eval(shared_data.m_temperature); } From 7a0e8d5a70e2a98400ce72ae2730782c202343fa Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Thu, 13 May 2021 06:15:46 -0500 Subject: [PATCH 61/84] [Kinetics] Add Reaction3::undeclaredThirdBodies --- include/cantera/kinetics/Reaction.h | 3 +++ src/kinetics/Reaction.cpp | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index ea691bc1a24..45c80e5b183 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -483,6 +483,9 @@ class Reaction3 : public Reaction virtual void validate(); protected: + virtual std::pair, bool> + undeclaredThirdBodies(const Kinetics& kin) const; + //! Reaction rate used by generic reactions shared_ptr m_rate; diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 0e8747ad217..43707d6fc41 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -858,6 +858,19 @@ void Reaction3::setParameters(const AnyMap& node, const Kinetics& kin) input = node; } +std::pair, bool> Reaction3::undeclaredThirdBodies( + const Kinetics& kin) const +{ + std::vector undeclared; + if (m_third_body) { + updateUndeclared(undeclared, m_third_body->efficiencies, kin); + bool specified_collision_partner = dynamic_cast( + this)->specified_collision_partner; + return std::make_pair(undeclared, specified_collision_partner); + } + return std::make_pair(undeclared, false); +} + void Reaction3::validate() { Reaction::validate(); From 1413fc5d6a24428d971157fb404cb491f465ee3a Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Thu, 13 May 2021 10:50:37 -0500 Subject: [PATCH 62/84] [Kinetics] Non-configured reaction rates return SNAN --- interfaces/cython/cantera/reaction.pyx | 31 ++++++++--- src/kinetics/ReactionRate.cpp | 21 ++------ src/kinetics/RxnRates.cpp | 73 +++++++++++++++++--------- 3 files changed, 77 insertions(+), 48 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 9975851478f..eda1374da3d 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -61,13 +61,19 @@ cdef class ArrheniusRate(_ReactionRate): where *A* is the `pre_exponential_factor`, *b* is the `temperature_exponent`, and *E* is the `activation_energy`. """ - def __cinit__(self, A=0, b=0, E=0, input_data=None, init=True): + def __cinit__(self, A=None, b=None, E=None, input_data=None, init=True): if init: if isinstance(input_data, dict): self._base.reset(new CxxArrheniusRate(dict_to_anymap(input_data))) - else: + elif all([arg is not None for arg in [A, b, E]]): self._base.reset(new CxxArrheniusRate(A, b, E)) + elif all([arg is None for arg in [A, b, E, input_data]]): + self._base.reset(new CxxArrheniusRate(dict_to_anymap({}))) + elif input_data: + raise TypeError("Invalid parameter 'input_data'") + else: + raise TypeError("Invalid parameters 'A', 'b' or 'E'") self.base = self._base.get() self.rate = (self.base) @@ -132,8 +138,10 @@ cdef class PlogRate(_ReactionRate): self._base.reset(new CxxPlogRate(dict_to_anymap(input_data))) elif rates is None: self._base.reset(new CxxPlogRate(dict_to_anymap({}))) + elif input_data: + raise TypeError("Invalid parameter 'input_data'") else: - raise TypeError("Invalid type for parameter 'rates'") + raise TypeError("Invalid parameter 'rates'") self.base = self._base.get() self.rate = (self.base) @@ -185,12 +193,21 @@ cdef class ChebyshevRate(_ReactionRate): def __cinit__(self, Tmin=None, Tmax=None, Pmin=None, Pmax=None, data=None, input_data=None, init=True): - if isinstance(input_data, dict) and init: - self._base.reset(new CxxChebyshevRate3(dict_to_anymap(input_data))) + if init: + if isinstance(input_data, dict): + self._base.reset(new CxxChebyshevRate3(dict_to_anymap(input_data))) + elif all([arg is not None for arg in [Tmin, Tmax, Pmin, Pmax, data]]): + self._setup(Tmin, Tmax, Pmin, Pmax, data) + return + elif all([arg is None + for arg in [Tmin, Tmax, Pmin, Pmax, data, input_data]]): + self._base.reset(new CxxChebyshevRate3(dict_to_anymap({}))) + elif input_data: + raise TypeError("Invalid parameter 'input_data'") + else: + raise TypeError("Invalid parameters") self.base = self._base.get() self.rate = (self.base) - elif Tmin and Tmax and Pmin and Pmax and data is not None and init: - self._setup(Tmin, Tmax, Pmin, Pmax, data) def _setup(self, Tmin, Tmax, Pmin, Pmax, coeffs): """ diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 2f29398c2ae..f39b5d1a825 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -31,7 +31,7 @@ AnyMap ReactionRateBase::parameters() const } ArrheniusRate::ArrheniusRate() - : Arrhenius() + : Arrhenius(SNAN, SNAN, SNAN) , allow_negative_pre_exponential_factor(false) { } @@ -65,6 +65,7 @@ void ArrheniusRate::setParameters(const AnyMap& node, const Units& rate_units) ReactionRateBase::setParameters(node, rate_units); allow_negative_pre_exponential_factor = node.getBool("negative-A", false); if (!node.hasKey("rate-constant")) { + Arrhenius::setParameters(AnyValue(), node.units(), rate_units); return; } Arrhenius::setParameters(node["rate-constant"], node.units(), rate_units); @@ -113,13 +114,7 @@ void PlogRate::setParameters(const AnyMap& node, const Units& rate_units) { ReactionRateBase::setParameters(node, rate_units); if (!node.hasKey("rate-constants")) { - // ensure that Plog has defined state and produces zero reaction rate - AnyMap rate = AnyMap::fromYamlString( - "rate-constants:\n" - "- {P: 1e-7, A: 0., b: 0., Ea: 0.}\n" - "- {P: 1e7, A: 0., b: 0., Ea: 0.}"); - Plog::setParameters(rate.at("rate-constants").asVector(), - node.units(), rate_units); + Plog::setParameters(std::vector (), node.units(), rate_units); return; } Plog::setParameters(node.at("rate-constants").asVector(), @@ -157,12 +152,7 @@ void ChebyshevRate3::setParameters(const AnyMap& node, const Units& rate_units) { ReactionRateBase::setParameters(node, rate_units); if (!node.hasKey("data")) { - // ensure that Chebyshev has defined state and produces zero reaction rate - AnyMap rate = AnyMap::fromYamlString( - "temperature-range: [290, 3000]\n" - "pressure-range: [1.e-7, 1.e7]\n" - "data: [[-16.]]\n"); - Chebyshev::setParameters(rate, node.units(), rate_units); + Chebyshev::setParameters(AnyMap(), node.units(), rate_units); return; } Chebyshev::setParameters(node, node.units(), rate_units); @@ -191,8 +181,7 @@ double CustomFunc1Rate::eval(const CustomFunc1Data& shared_data, if (m_ratefunc) { return m_ratefunc->eval(shared_data.m_temperature); } - throw CanteraError("CustomFunc1Rate::eval", - "Custom rate function is not initialized."); + return SNAN; } } diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index d7f84f37916..fca7c225719 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -39,7 +39,11 @@ Arrhenius::Arrhenius(const AnyValue& rate, void Arrhenius::setParameters(const AnyValue& rate, const UnitSystem& units, const Units& rate_units) { - if (rate.is()) { + if (rate.empty()) { + m_A = SNAN; + m_b = SNAN; + m_E = SNAN; + } else if (rate.is()) { auto& rate_map = rate.as(); m_A = units.convert(rate_map["A"], rate_units); m_b = rate_map["b"].asDouble(); @@ -51,7 +55,7 @@ void Arrhenius::setParameters(const AnyValue& rate, m_E = units.convertActivationEnergy(rate_vec[2], "K"); } - if (m_A <= 0.0) { + if (m_A <= 0.0) { m_logA = -1.0E300; } else { m_logA = std::log(m_A); @@ -165,9 +169,15 @@ void Plog::setParameters(const std::vector& rates, const UnitSystem& units, const Units& rate_units) { std::multimap multi_rates; - for (const auto& rate : rates) { - multi_rates.insert({rate.convert("P", "Pa"), - Arrhenius(AnyValue(rate), units, rate_units)}); + if (rates.size()) { + for (const auto& rate : rates) { + multi_rates.insert({rate.convert("P", "Pa"), + Arrhenius(AnyValue(rate), units, rate_units)}); + } + } else { + // ensure that reaction rate can be evaluated (but returns SNAN) + multi_rates.insert({1.e-7, Arrhenius(SNAN, SNAN, SNAN)}); + multi_rates.insert({1.e14, Arrhenius(SNAN, SNAN, SNAN)}); } setup(multi_rates); } @@ -266,30 +276,43 @@ Chebyshev::Chebyshev(double Tmin, double Tmax, double Pmin, double Pmax, void Chebyshev::setParameters(const AnyMap& node, const UnitSystem& units, const Units& rate_units) { - const auto& T_range = node["temperature-range"].asVector(2); - const auto& P_range = node["pressure-range"].asVector(2); - auto& vcoeffs = node["data"].asVector(); - Array2D coeffs(vcoeffs.size(), vcoeffs[0].size()); - for (size_t i = 0; i < coeffs.nRows(); i++) { - if (vcoeffs[i].size() != vcoeffs[0].size()) { - throw InputFileError("Chebyshev::setParameters", node["data"], - "Inconsistent number of coefficients in row {} of matrix", i + 1); - } - for (size_t j = 0; j < coeffs.nColumns(); j++) { - coeffs(i, j) = vcoeffs[i][j]; + Array2D coeffs; + if (!node.empty()) { + const auto& T_range = node["temperature-range"].asVector(2); + const auto& P_range = node["pressure-range"].asVector(2); + auto& vcoeffs = node["data"].asVector(); + coeffs = Array2D(vcoeffs.size(), vcoeffs[0].size()); + for (size_t i = 0; i < coeffs.nRows(); i++) { + if (vcoeffs[i].size() != vcoeffs[0].size()) { + throw InputFileError("Chebyshev::setParameters", node["data"], + "Inconsistent number of coefficients in row {} of matrix", i + 1); + } + for (size_t j = 0; j < coeffs.nColumns(); j++) { + coeffs(i, j) = vcoeffs[i][j]; + } } + coeffs(0, 0) += std::log10(units.convertTo(1.0, rate_units)); + + Tmin_ = units.convert(T_range[0], "K"); + Tmax_ = units.convert(T_range[1], "K"); + Pmin_ = units.convert(P_range[0], "Pa"); + Pmax_ = units.convert(P_range[1], "Pa"); + nP_ = coeffs.nColumns(); + nT_ = coeffs.nRows(); + } else { + // ensure that reaction rate can be evaluated (but returns SNAN) + coeffs = Array2D(1, 1); + coeffs(0, 0) = SNAN; + Tmin_ = 290.; + Tmax_ = 3000.; + Pmin_ = 1.e-7; + Pmax_ = 1.e14; + nP_ = 1; + nT_ = 1; } - coeffs(0, 0) += std::log10(units.convertTo(1.0, rate_units)); - - Tmin_ = units.convert(T_range[0], "K"); - Tmax_ = units.convert(T_range[1], "K"); - Pmin_ = units.convert(P_range[0], "Pa"); - Pmax_ = units.convert(P_range[1], "Pa"); - nP_ = coeffs.nColumns(); - nT_ = coeffs.nRows(); + chebCoeffs_.resize(nP_ * nT_); dotProd_.resize(nT_); - setup(Tmin_, Tmax_, Pmin_, Pmax_, coeffs); } From 9cf96d713dfe2a793e51adf759372300a286d481 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Thu, 13 May 2021 12:03:50 -0500 Subject: [PATCH 63/84] [Kinetics] Update serialization of unconfigured reaction rates --- src/kinetics/ReactionRate.cpp | 5 ++++- src/kinetics/RxnRates.cpp | 23 ++++++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index f39b5d1a825..c05946f2e1a 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -79,7 +79,10 @@ void ArrheniusRate::getParameters(AnyMap& rateNode, } AnyMap node; Arrhenius::getParameters(node, rate_units); - rateNode["rate-constant"] = std::move(node); + if (!node.empty()) { + // Arrhenius object is configured + rateNode["rate-constant"] = std::move(node); + } } void ArrheniusRate::validate(const std::string& equation) diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index fca7c225719..0e054c53995 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -64,10 +64,15 @@ void Arrhenius::setParameters(const AnyValue& rate, void Arrhenius::getParameters(AnyMap& rateNode, const Units& rate_units) const { - if (rate_units.factor() != 0.0) { - rateNode["A"].setQuantity(preExponentialFactor(), rate_units); + double A = preExponentialFactor(); + if (A != A) { + // Evaluates true if A is not a number (unconfigured object) + // Return empty/unmodified AnyMap + return; + } else if (rate_units.factor() != 0.0) { + rateNode["A"].setQuantity(A, rate_units); } else { - rateNode["A"] = preExponentialFactor(); + rateNode["A"] = A; // This can't be converted to a different unit system because the dimensions of // the rate constant were not set. Can occur if the reaction was created outside // the context of a Kinetics object and never added to a Kinetics object. @@ -185,6 +190,12 @@ void Plog::setParameters(const std::vector& rates, void Plog::getParameters(AnyMap& rateNode, const Units& rate_units) const { std::vector rateList; + double A = rates_[1].preExponentialFactor(); + if (A != A) { + // Evaluates true if A is not a number (unconfigured object) + // Return empty/unmodified AnyMap + return; + } for (const auto& r : rates()) { AnyMap rateNode_; rateNode_["P"].setQuantity(r.first, "Pa"); @@ -338,6 +349,12 @@ void Chebyshev::setup(double Tmin, double Tmax, double Pmin, double Pmax, void Chebyshev::getParameters(AnyMap& rateNode, const Units& rate_units) const { + double A = chebCoeffs_[0]; + if (A != A) { + // Evaluates true if A is not a number (unconfigured object) + // Return empty/unmodified AnyMap + return; + } rateNode["temperature-range"].setQuantity({Tmin(), Tmax()}, "K"); rateNode["pressure-range"].setQuantity({Pmin(), Pmax()}, "Pa"); const auto& coeffs1d = coeffs(); From 45e4e485fe0805cf327cc715bd0a5281ce8accc2 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 10 May 2021 00:44:13 -0500 Subject: [PATCH 64/84] [Tests] Remove deprecation warnings and update unit tests * A rebase switching CTI to YAML input caused some broken unit tests * Check that rates that are not set up return NaN * Add test for serialization of unconfigured reaction rate objects --- .../cython/cantera/test/test_kinetics.py | 17 ++++++------ .../cython/cantera/test/test_reaction.py | 27 +++++++++---------- test/kinetics/kineticsFromYaml.cpp | 16 +++++------ 3 files changed, 28 insertions(+), 32 deletions(-) diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index a21399848ef..5c9357f2bc4 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -1085,7 +1085,7 @@ def test_plog_rate(self): gas1 = ct.Solution('pdep-test.yaml') gas1.TP = 800, 2*ct.one_atm for i in range(4): - self.assertNear(gas1.reaction(i)(gas1.T, gas1.P), + self.assertNear(gas1.reaction(i).rate(gas1.T, gas1.P), gas1.forward_rate_constants[i]) def test_chebyshev(self): @@ -1158,7 +1158,7 @@ def test_chebyshev_rate(self): gas1 = ct.Solution('pdep-test.yaml') gas1.TP = 800, 2*ct.one_atm for i in range(4,6): - self.assertNear(gas1.reaction(i)(gas1.T, gas1.P), + self.assertNear(gas1.reaction(i).rate(gas1.T, gas1.P), gas1.forward_rate_constants[i]) def test_chebyshev_bad_shape_cti(self): @@ -1322,7 +1322,7 @@ def test_BlowersMaselinterface(self): def test_modify_invalid(self): # different reaction type tbr = self.gas.reaction(0) - R2 = ct.ElementaryReaction(tbr.reactants, tbr.products) + R2 = ct.ElementaryReaction3(tbr.reactants, tbr.products) R2.rate = tbr.rate with self.assertRaisesRegex(ct.CanteraError, 'types are different'): self.gas.modify_reaction(0, R2) @@ -1350,7 +1350,7 @@ def test_modify_elementary(self): A2 = 1.5 * A1 b2 = b1 + 0.1 Ta2 = Ta1 * 1.2 - R.rate = ct.Arrhenius(A2, b2, Ta2 * ct.gas_constant) + R.rate = ct.ArrheniusRate(A2, b2, Ta2 * ct.gas_constant) gas.modify_reaction(2, R) self.assertNear(A2*T**b2*np.exp(-Ta2/T), gas.forward_rate_constants[2]) @@ -1365,7 +1365,7 @@ def test_modify_third_body(self): A2 = 1.7 * A1 b2 = b1 - 0.1 - R.rate = ct.Arrhenius(A2, b2, 0.0) + R.rate = ct.ArrheniusRate(A2, b2, 0.0) gas.modify_reaction(5, R) kf2 = gas.forward_rate_constants[5] self.assertNear((A2*T**b2) / (A1*T**b1), kf2/kf1) @@ -1394,13 +1394,13 @@ def test_modify_plog(self): r0 = gas.reaction(0) r1 = gas.reaction(1) - r0.rates = r1.rates + r0.rate = ct.PlogRate(r1.rate.rates) gas.modify_reaction(0, r0) kf = gas.forward_rate_constants self.assertNear(kf[0], kf[1]) # Removing the high-pressure rates should have no effect at low P... - r1.rates = r1.rates[:-4] + r1.rate = ct.PlogRate(rates=r1.rate.rates[:-4]) gas.modify_reaction(1, r1) self.assertNear(kf[1], gas.forward_rate_constants[1]) @@ -1415,7 +1415,8 @@ def test_modify_chebyshev(self): r1 = gas.reaction(4) r2 = gas.reaction(5) - r1.set_parameters(r2.Tmin, r2.Tmax, r2.Pmin, r2.Pmax, r2.coeffs) + r1.rate = ct.ChebyshevRate(r2.rate.Tmin, r2.rate.Tmax, + r2.rate.Pmin, r2.rate.Pmax, r2.rate.coeffs) # rates should be different before calling 'modify_reaction' kf = gas.forward_rate_constants diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index dc8b3033421..0b2b829dc09 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -167,6 +167,16 @@ def test_input(self): self.assertNear(rate(self.gas.T, self.gas.P), self.rate(self.gas.T, self.gas.P)) + def test_unconfigured(self): + # check behavior of unconfigured rate object + if self._input is None or self._cls is None: + return + rate = self._cls(input_data={}) + self.assertTrue(np.isnan(rate(self.gas.T, self.gas.P))) + input_data = rate.input_data + self.assertIsInstance(input_data, dict) + self.assertEqual(input_data, {}) + def test_roundtrip(self): # check round-trip instantiation via input_data if self._index is None: @@ -403,8 +413,10 @@ def test_no_rate(self): return rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) if "Rate" in type(self._rate_obj).__name__: - self.assertNear(rxn.rate(self.gas.T, self.gas.P), 0.) + # rate expressions from new framework end in "Rate" + self.assertTrue(np.isnan(rxn.rate(self.gas.T, self.gas.P))) else: + # legacy framework self.assertNear(rxn.rate(self.gas.T), 0.) gas2 = ct.Solution(thermo="IdealGas", kinetics="GasKinetics", @@ -746,19 +758,6 @@ 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): - # overload default tester for missing rate definition - 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_roundtrip(self): # overload default tester for round trip pass diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index d896cf472dd..75eb6e6c6ba 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -462,14 +462,10 @@ class ReactionToYaml : public testing::Test iNew = kin->nReactions() - 1; } - void compareReactions(bool old=false) { + void compareReactions() { auto kin = soln->kinetics(); EXPECT_EQ(kin->reactionString(iOld), kin->reactionString(iNew)); - if (old) { - EXPECT_EQ(kin->reactionTypeStr(iOld), kin->reactionTypeStr(iNew) + "-old"); - } else { - EXPECT_EQ(kin->isReversible(iOld), kin->isReversible(iNew)); - } + EXPECT_EQ(kin->isReversible(iOld), kin->isReversible(iNew)); vector_fp kf(kin->nReactions()), kr(kin->nReactions()); vector_fp ropf(kin->nReactions()), ropr(kin->nReactions()); @@ -496,7 +492,7 @@ TEST_F(ReactionToYaml, elementary) soln->thermo()->setState_TPY(1000, 2e5, "H2:1.0, O2:0.5, O:1e-8, OH:3e-8"); duplicateReaction(2); EXPECT_TRUE(std::dynamic_pointer_cast(duplicate)); - compareReactions(true); + compareReactions(); } TEST_F(ReactionToYaml, threeBody) @@ -505,7 +501,7 @@ TEST_F(ReactionToYaml, threeBody) soln->thermo()->setState_TPY(1000, 2e5, "H2:1.0, O2:0.5, O:1e-8, OH:3e-8, H:2e-7"); duplicateReaction(1); EXPECT_TRUE(std::dynamic_pointer_cast(duplicate)); - compareReactions(true); + compareReactions(); } TEST_F(ReactionToYaml, TroeFalloff) @@ -545,7 +541,7 @@ TEST_F(ReactionToYaml, pdepArrhenius) EXPECT_TRUE(std::dynamic_pointer_cast(duplicate)); compareReactions(); soln->thermo()->setState_TPY(1100, 1e3, "R2:1, H:0.2, P2A:2, P2B:0.3"); - compareReactions(true); + compareReactions(); } TEST_F(ReactionToYaml, Chebyshev) @@ -554,7 +550,7 @@ TEST_F(ReactionToYaml, Chebyshev) soln->thermo()->setState_TPY(1000, 2e5, "R6:1, P6A:2, P6B:0.3"); duplicateReaction(5); EXPECT_TRUE(std::dynamic_pointer_cast(duplicate)); - compareReactions(true); + compareReactions(); } TEST_F(ReactionToYaml, surface) From e04fa1862c20c3ce7c5d7b27a9396073db3b5c0b Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Thu, 13 May 2021 12:39:15 -0500 Subject: [PATCH 65/84] Update definition for NaN --- src/kinetics/ReactionRate.cpp | 4 ++-- src/kinetics/RxnRates.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index c05946f2e1a..0fd443bd027 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -31,7 +31,7 @@ AnyMap ReactionRateBase::parameters() const } ArrheniusRate::ArrheniusRate() - : Arrhenius(SNAN, SNAN, SNAN) + : Arrhenius(NAN, NAN, NAN) , allow_negative_pre_exponential_factor(false) { } @@ -184,7 +184,7 @@ double CustomFunc1Rate::eval(const CustomFunc1Data& shared_data, if (m_ratefunc) { return m_ratefunc->eval(shared_data.m_temperature); } - return SNAN; + return NAN; } } diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 0e054c53995..c36b2181276 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -40,9 +40,9 @@ void Arrhenius::setParameters(const AnyValue& rate, const UnitSystem& units, const Units& rate_units) { if (rate.empty()) { - m_A = SNAN; - m_b = SNAN; - m_E = SNAN; + m_A = NAN; + m_b = NAN; + m_E = NAN; } else if (rate.is()) { auto& rate_map = rate.as(); m_A = units.convert(rate_map["A"], rate_units); @@ -180,9 +180,9 @@ void Plog::setParameters(const std::vector& rates, Arrhenius(AnyValue(rate), units, rate_units)}); } } else { - // ensure that reaction rate can be evaluated (but returns SNAN) - multi_rates.insert({1.e-7, Arrhenius(SNAN, SNAN, SNAN)}); - multi_rates.insert({1.e14, Arrhenius(SNAN, SNAN, SNAN)}); + // ensure that reaction rate can be evaluated (but returns NaN) + multi_rates.insert({1.e-7, Arrhenius(NAN, NAN, NAN)}); + multi_rates.insert({1.e14, Arrhenius(NAN, NAN, NAN)}); } setup(multi_rates); } @@ -311,9 +311,9 @@ void Chebyshev::setParameters(const AnyMap& node, nP_ = coeffs.nColumns(); nT_ = coeffs.nRows(); } else { - // ensure that reaction rate can be evaluated (but returns SNAN) + // ensure that reaction rate can be evaluated (but returns NaN) coeffs = Array2D(1, 1); - coeffs(0, 0) = SNAN; + coeffs(0, 0) = NAN; Tmin_ = 290.; Tmax_ = 3000.; Pmin_ = 1.e-7; From 5e2b6ab714da0d5d5ad2994057a8f0f89ea3de5f Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 17 May 2021 02:23:08 -0500 Subject: [PATCH 66/84] [Kinetics] Remove non-void ReactionData constructors & simplify code Also: * Remove unnecessary calls to default constructors * Update check for not-a-number * Make ReactionRate::getParameters protected * Simplify Reaction::getParameters and various constructors * Update includes to fix docs build --- include/cantera/kinetics/BulkKinetics.h | 2 +- include/cantera/kinetics/ReactionData.h | 42 +++------------ include/cantera/kinetics/ReactionRate.h | 68 ++++++++++++++++--------- src/kinetics/Reaction.cpp | 45 +++++++--------- src/kinetics/ReactionData.cpp | 10 ---- src/kinetics/ReactionFactory.cpp | 20 ++++---- src/kinetics/ReactionRate.cpp | 15 +----- src/kinetics/RxnRates.cpp | 9 ++-- 8 files changed, 84 insertions(+), 127 deletions(-) diff --git a/include/cantera/kinetics/BulkKinetics.h b/include/cantera/kinetics/BulkKinetics.h index 7984f5a33b4..311cbfbc250 100644 --- a/include/cantera/kinetics/BulkKinetics.h +++ b/include/cantera/kinetics/BulkKinetics.h @@ -66,7 +66,7 @@ class BulkKinetics : public Kinetics vector_fp m_dn; ThirdBodyCalc m_multi_concm; //!< used with MultiRate evaluator - vector_fp concm_multi_values; + vector_fp concm_multi_values; //!< concentrations of third-body collision partners std::vector m_multi_indices; //!< reaction indices //! Third body concentrations diff --git a/include/cantera/kinetics/ReactionData.h b/include/cantera/kinetics/ReactionData.h index 6a2deed9555..1a347868b57 100644 --- a/include/cantera/kinetics/ReactionData.h +++ b/include/cantera/kinetics/ReactionData.h @@ -25,15 +25,6 @@ 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); } - //! Update data container based on temperature *T* void update(double T) { m_temperature = T; @@ -41,6 +32,9 @@ struct ArrheniusData m_recipT = 1./T; } + //! Update data container based on temperature *T* and pressure *P* + void update(double T, double P) { update(T); }; + //! Update data container based on *bulk* phase state void update(const ThermoPhase& bulk); @@ -59,15 +53,6 @@ struct PlogData { PlogData() : m_temperature(1.), m_logT(0.), m_recipT(1.), m_logP(0.) {} - //! Constructor based on temperature *T* and pressure *P* - PlogData(double T); - - //! Constructor based on temperature *T* and pressure *P* - PlogData(double T, double P) { update(T, P); }; - - //! Constructor accessing *bulk* phase definitions - PlogData(const ThermoPhase& bulk) { update(bulk); } - //! Update data container based on temperature *T* (raises exception) void update(double T); @@ -101,15 +86,6 @@ struct ChebyshevData { ChebyshevData() : m_temperature(1.), m_recipT(1.), m_log10P(0.) {} - //! Constructor based on temperature *T* and pressure *P* - ChebyshevData(double T); - - //! Constructor based on temperature *T* and pressure *P* - ChebyshevData(double T, double P) { update(T, P); }; - - //! Constructor accessing *bulk* phase definitions - ChebyshevData(const ThermoPhase& bulk) { update(bulk); } - //! Update data container based on temperature *T* (raises exception) void update(double T); @@ -137,18 +113,12 @@ 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); } - //! Update data container based on temperature *T* void update(double T) { m_temperature = T; } + //! Update data container based on temperature *T* and pressure *P* + void update(double T, double P) { update(T); }; + //! Update data container based on *bulk* phase state void update(const ThermoPhase& bulk); diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 81935919638..e9eff54a0a7 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -11,6 +11,7 @@ #ifndef CT_REACTIONRATE_H #define CT_REACTIONRATE_H +#include "cantera/base/AnyMap.h" #include "cantera/base/Units.h" #include "cantera/kinetics/RxnRates.h" #include "cantera/kinetics/ReactionData.h" @@ -38,19 +39,7 @@ class ReactionRateBase ReactionRateBase() : units(0.) {} virtual ~ReactionRateBase() {} - //! Set parameters - //! @param node AnyMap object containing reaction rate specification - //! @param rate_units Description of units used for rate parameters - virtual void setParameters(const AnyMap& node, const Units& rate_units); - - //! Get parameters - //! Store the parameters of a ReactionRate needed to reconstruct an identical - //! object. Does not include user-defined fields available in the #input map. - virtual void getParameters(AnyMap& rateNode, const Units& rate_units) const { - throw CanteraError("ReactionRate::getParameters", - "Not implemented by derived ReactionRate object."); - } - +public: //! Identifier of reaction type virtual std::string type() const = 0; @@ -110,7 +99,20 @@ class ReactionRateBase //! Return parameters using original unit system AnyMap parameters() const; + //! Set parameters + //! @param node AnyMap object containing reaction rate specification + //! @param rate_units Description of units used for rate parameters + virtual void setParameters(const AnyMap& node, const Units& rate_units); + protected: + //! Get parameters + //! Store the parameters of a ReactionRate needed to reconstruct an identical + //! object. Does not include user-defined fields available in the #input map. + virtual void getParameters(AnyMap& rateNode, const Units& rate_units) const { + throw CanteraError("ReactionRate::getParameters", + "Not implemented by derived ReactionRate object."); + } + //! Input data used for specific models AnyMap input; @@ -138,15 +140,21 @@ class ReactionRate : public ReactionRateBase double concm=0.) {} virtual void update(double T) override { - update(DataType(T)); + DataType data; + data.update(T); + update(data); } virtual void update(double T, double P, double concm=0.) override { - update(DataType(T, P), concm); + DataType data; + data.update(T, P); + update(data, concm); } virtual void update(const ThermoPhase& bulk, double concm=0.) override { - update(DataType(bulk), concm); + DataType data; + data.update(bulk); + update(data); } //! Evaluate reaction rate @@ -155,15 +163,21 @@ class ReactionRate : public ReactionRateBase virtual double eval(const DataType& shared_data, double concm=0.) const = 0; virtual double eval(double T) const override { - return eval(DataType(T)); + DataType data; + data.update(T); + return eval(data); } virtual double eval(double T, double P, double concm=0.) const override { - return eval(DataType(T, P), concm); + DataType data; + data.update(T, P); + return eval(data, concm); } virtual double eval(const ThermoPhase& bulk, double concm=0.) const override { - return eval(DataType(bulk), concm); + DataType data; + data.update(bulk); + return eval(data, concm); } //! Evaluate derivative of reaction rate with respect to temperature @@ -174,15 +188,21 @@ class ReactionRate : public ReactionRateBase } virtual double ddT(double T) const override { - return ddT(DataType(T)); + DataType data; + data.update(T); + return ddT(data); } virtual double ddT(double T, double P) const override { - return ddT(DataType(T, P)); + DataType data; + data.update(T, P); + return ddT(data); } virtual double ddT(const ThermoPhase& bulk, double concm=0.) const override { - return ddT(DataType(bulk), concm); + DataType data; + data.update(bulk); + return ddT(data, concm); } }; @@ -278,7 +298,7 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius class PlogRate final : public ReactionRate, public Plog { public: - PlogRate(); + PlogRate() {} //! Constructor from Arrhenius rate expressions at a set of pressures explicit PlogRate(const std::multimap& rates); @@ -349,7 +369,7 @@ class ChebyshevRate3 final : public ReactionRate, public Chebyshe { public: //! Default constructor. - ChebyshevRate3(); + ChebyshevRate3() {} //! Constructor directly from coefficient array /* diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 43707d6fc41..e4b0eded8cc 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -626,9 +626,7 @@ void PlogReaction::getParameters(AnyMap& reactionNode) const { Reaction::getParameters(reactionNode); reactionNode["type"] = "pressure-dependent-Arrhenius"; - AnyMap rateNode; - rate.getParameters(rateNode, rate_units); - reactionNode.update(rateNode); + rate.getParameters(reactionNode, rate_units); } ChebyshevReaction::ChebyshevReaction() @@ -650,9 +648,7 @@ void ChebyshevReaction::getParameters(AnyMap& reactionNode) const { Reaction::getParameters(reactionNode); reactionNode["type"] = "Chebyshev"; - AnyMap rateNode; - rate.getParameters(rateNode, rate_units); - reactionNode.update(rateNode); + rate.getParameters(reactionNode, rate_units); } InterfaceReaction::InterfaceReaction() @@ -832,7 +828,7 @@ Reaction3::Reaction3(const Composition& reactants, const Composition& products) void Reaction3::setParameters(const AnyMap& node, const Kinetics& kin) { - if (!node.hasKey("equation")) { + if (node.empty()) { // empty node: used by newReaction() factory loader return; } @@ -878,9 +874,8 @@ void Reaction3::validate() } ElementaryReaction3::ElementaryReaction3() - : Reaction3() { - m_rate = std::shared_ptr(new ArrheniusRate); + m_rate.reset(new ArrheniusRate); } ElementaryReaction3::ElementaryReaction3(const Composition& reactants, @@ -888,14 +883,14 @@ ElementaryReaction3::ElementaryReaction3(const Composition& reactants, const ArrheniusRate& rate) : Reaction3(reactants, products) { - m_rate = std::make_shared(rate); + m_rate.reset(new ArrheniusRate(rate)); } ElementaryReaction3::ElementaryReaction3(const AnyMap& node, const Kinetics& kin) : ElementaryReaction3() { setParameters(node, kin); - setRate(std::shared_ptr(new ArrheniusRate(node, rate_units))); + setRate(std::make_shared(node, rate_units)); } void ElementaryReaction3::getParameters(AnyMap& reactionNode) const @@ -923,7 +918,7 @@ ThreeBodyReaction3::ThreeBodyReaction3(const AnyMap& node, const Kinetics& kin) : ThreeBodyReaction3() { setParameters(node, kin); - setRate(std::shared_ptr(new ArrheniusRate(node, rate_units))); + setRate(std::make_shared(node, rate_units)); } bool ThreeBodyReaction3::detectEfficiencies() @@ -989,7 +984,7 @@ void ThreeBodyReaction3::calculateRateCoeffUnits(const Kinetics& kin) void ThreeBodyReaction3::setParameters(const AnyMap& node, const Kinetics& kin) { - if (!node.hasKey("equation")) { + if (node.empty()) { // empty node: used by newReaction() factory loader return; } @@ -1042,23 +1037,22 @@ std::string ThreeBodyReaction3::productString() const } PlogReaction3::PlogReaction3() - : Reaction3() { - m_rate = std::shared_ptr(new PlogRate); + m_rate.reset(new PlogRate); } PlogReaction3::PlogReaction3(const Composition& reactants, const Composition& products, const PlogRate& rate) : Reaction3(reactants, products) { - m_rate = std::make_shared(rate); + m_rate.reset(new PlogRate(rate)); } PlogReaction3::PlogReaction3(const AnyMap& node, const Kinetics& kin) : PlogReaction3() { setParameters(node, kin); - setRate(std::shared_ptr(new PlogRate(node, rate_units))); + setRate(std::make_shared(node, rate_units)); } void PlogReaction3::getParameters(AnyMap& reactionNode) const @@ -1069,9 +1063,8 @@ void PlogReaction3::getParameters(AnyMap& reactionNode) const } ChebyshevReaction3::ChebyshevReaction3() - : Reaction3() { - m_rate = std::shared_ptr(new ChebyshevRate3); + m_rate.reset(new ChebyshevRate3); } ChebyshevReaction3::ChebyshevReaction3(const Composition& reactants, @@ -1079,19 +1072,19 @@ ChebyshevReaction3::ChebyshevReaction3(const Composition& reactants, const ChebyshevRate3& rate) : Reaction3(reactants, products) { - m_rate = std::make_shared(rate); + m_rate.reset(new ChebyshevRate3(rate)); } ChebyshevReaction3::ChebyshevReaction3(const AnyMap& node, const Kinetics& kin) : ChebyshevReaction3() { setParameters(node, kin); - setRate(std::shared_ptr(new ChebyshevRate3(node, rate_units))); + setRate(std::make_shared(node, rate_units)); } void ChebyshevReaction3::setParameters(const AnyMap& node, const Kinetics& kin) { - if (!node.hasKey("equation")) { + if (node.empty()) { // empty node: used by newReaction() factory loader return; } @@ -1128,9 +1121,8 @@ void ChebyshevReaction3::getParameters(AnyMap& reactionNode) const } CustomFunc1Reaction::CustomFunc1Reaction() - : Reaction3() { - m_rate = std::shared_ptr(new CustomFunc1Rate); + m_rate.reset(new CustomFunc1Rate); } CustomFunc1Reaction::CustomFunc1Reaction(const Composition& reactants, @@ -1138,14 +1130,14 @@ CustomFunc1Reaction::CustomFunc1Reaction(const Composition& reactants, const CustomFunc1Rate& rate) : Reaction3(reactants, products) { - m_rate = std::make_shared(rate); + m_rate.reset(new CustomFunc1Rate(rate)); } CustomFunc1Reaction::CustomFunc1Reaction(const AnyMap& node, const Kinetics& kin) : CustomFunc1Reaction() { setParameters(node, kin); - setRate(std::shared_ptr(new CustomFunc1Rate(node, rate_units))); + setRate(std::make_shared(node, rate_units)); } Arrhenius readArrhenius(const XML_Node& arrhenius_node) @@ -1422,7 +1414,6 @@ void setupReaction(Reaction& R, const AnyMap& node, const Kinetics& kin) { parseReactionEquation(R, node["equation"], kin); // Non-stoichiometric reaction orders - //std::map orders; if (node.hasKey("orders")) { for (const auto& order : node["orders"].asMap()) { R.orders[order.first] = order.second; diff --git a/src/kinetics/ReactionData.cpp b/src/kinetics/ReactionData.cpp index 50fde1bd0cb..cadee35e0e1 100644 --- a/src/kinetics/ReactionData.cpp +++ b/src/kinetics/ReactionData.cpp @@ -14,11 +14,6 @@ void ArrheniusData::update(const ThermoPhase& bulk) { update(bulk.temperature()); } -PlogData::PlogData(double T) { - throw CanteraError("PlogData::PlogData", - "Missing state information: reaction type requires pressure."); -} - void PlogData::update(double T) { throw CanteraError("PlogData::update", "Missing state information: reaction type requires pressure."); @@ -28,11 +23,6 @@ void PlogData::update(const ThermoPhase& bulk) { update(bulk.temperature(), bulk.pressure()); } -ChebyshevData::ChebyshevData(double T) { - throw CanteraError("ChebyshevData::ChebyshevData", - "Missing state information: reaction type requires pressure."); -} - void ChebyshevData::update(double T) { throw CanteraError("ChebyshevData::update", "Missing state information: reaction type requires pressure."); diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 27be9ec0e28..b741d87abbe 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -31,7 +31,7 @@ ReactionFactory::ReactionFactory() // register elementary reactions (old framework) reg("elementary-old", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ElementaryReaction(); - if (node.hasKey("equation")) { + if (!node.empty()) { setupElementaryReaction(*(ElementaryReaction*)R, node, kin); } return R; @@ -47,7 +47,7 @@ ReactionFactory::ReactionFactory() // register three-body reactions (old framework) reg("three-body-old", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ThreeBodyReaction(); - if (node.hasKey("equation")) { + if (!node.empty()) { setupThreeBodyReaction(*(ThreeBodyReaction*)R, node, kin); } return R; @@ -56,7 +56,7 @@ ReactionFactory::ReactionFactory() // register falloff reactions reg("falloff", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new FalloffReaction(); - if (node.hasKey("equation")) { + if (!node.empty()) { setupFalloffReaction(*(FalloffReaction*)R, node, kin); } return R; @@ -65,7 +65,7 @@ ReactionFactory::ReactionFactory() // register falloff reactions reg("chemically-activated", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ChemicallyActivatedReaction(); - if (node.hasKey("equation")) { + if (!node.empty()) { setupFalloffReaction(*(FalloffReaction*)R, node, kin); } return R; @@ -83,7 +83,7 @@ ReactionFactory::ReactionFactory() // register pressure-dependent-Arrhenius reactions (old framework) reg("pressure-dependent-Arrhenius-old", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new PlogReaction(); - if (node.hasKey("equation")) { + if (!node.empty()) { setupPlogReaction(*(PlogReaction*)R, node, kin); } return R; @@ -96,7 +96,7 @@ ReactionFactory::ReactionFactory() addAlias("Chebyshev", "chebyshev"); reg("Chebyshev-old", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ChebyshevReaction(); - if (node.hasKey("equation")) { + if (!node.empty()) { setupChebyshevReaction(*(ChebyshevReaction*)R, node, kin); } return R; @@ -110,7 +110,7 @@ ReactionFactory::ReactionFactory() // register interface reactions reg("interface", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new InterfaceReaction(); - if (node.hasKey("equation")) { + if (!node.empty()) { setupInterfaceReaction(*(InterfaceReaction*)R, node, kin); } return R; @@ -121,7 +121,7 @@ ReactionFactory::ReactionFactory() // register electrochemical reactions reg("electrochemical", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ElectrochemicalReaction(); - if (node.hasKey("equation")) { + if (!node.empty()) { setupElectrochemicalReaction(*(ElectrochemicalReaction*)R, node, kin); } return R; @@ -130,7 +130,7 @@ ReactionFactory::ReactionFactory() // register Blowers Masel reactions reg("Blowers-Masel", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new BlowersMaselReaction(); - if (node.hasKey("equation")) { + if (!node.empty()) { setupBlowersMaselReaction(*(BlowersMaselReaction*)R, node, kin); } return R; @@ -139,7 +139,7 @@ ReactionFactory::ReactionFactory() // register surface Blowers Masel reactions reg("surface-Blowers-Masel", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new BlowersMaselInterfaceReaction(); - if (node.hasKey("equation")) { + if (!node.empty()) { setupBlowersMaselInterfaceReaction(*(BlowersMaselInterfaceReaction*)R, node, kin); } return R; diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 0fd443bd027..24054059ca8 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -5,7 +5,6 @@ #include "cantera/kinetics/ReactionRate.h" #include "cantera/numerics/Func1.h" -#include "cantera/base/AnyMap.h" namespace Cantera { @@ -94,21 +93,17 @@ void ArrheniusRate::validate(const std::string& equation) } } -PlogRate::PlogRate() : Plog() -{ -} - PlogRate::PlogRate(const std::multimap& rates) : Plog(rates) { } -PlogRate::PlogRate(const AnyMap& node, const Units& rate_units) : Plog() +PlogRate::PlogRate(const AnyMap& node, const Units& rate_units) { setParameters(node, rate_units); } -PlogRate::PlogRate(const AnyMap& node) : Plog() +PlogRate::PlogRate(const AnyMap& node) { setParameters(node, Units(1.)); } @@ -129,10 +124,6 @@ void PlogRate::getParameters(AnyMap& rateNode, const Units& rate_units) const Plog::getParameters(rateNode, rate_units); } -ChebyshevRate3::ChebyshevRate3() - : Chebyshev() { -} - ChebyshevRate3::ChebyshevRate3(double Tmin, double Tmax, double Pmin, double Pmax, const Array2D& coeffs) : Chebyshev(Tmin, Tmax, Pmin, Pmax, coeffs) @@ -140,13 +131,11 @@ ChebyshevRate3::ChebyshevRate3(double Tmin, double Tmax, double Pmin, double Pma } ChebyshevRate3::ChebyshevRate3(const AnyMap& node, const Units& rate_units) - : Chebyshev() { setParameters(node, rate_units); } ChebyshevRate3::ChebyshevRate3(const AnyMap& node) - : Chebyshev() { setParameters(node, Units(1.)); } diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index c36b2181276..1d4008e32f2 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -65,8 +65,7 @@ void Arrhenius::setParameters(const AnyValue& rate, void Arrhenius::getParameters(AnyMap& rateNode, const Units& rate_units) const { double A = preExponentialFactor(); - if (A != A) { - // Evaluates true if A is not a number (unconfigured object) + if (std::isnan(A)) { // Return empty/unmodified AnyMap return; } else if (rate_units.factor() != 0.0) { @@ -191,8 +190,7 @@ void Plog::getParameters(AnyMap& rateNode, const Units& rate_units) const { std::vector rateList; double A = rates_[1].preExponentialFactor(); - if (A != A) { - // Evaluates true if A is not a number (unconfigured object) + if (std::isnan(A)) { // Return empty/unmodified AnyMap return; } @@ -350,8 +348,7 @@ void Chebyshev::setup(double Tmin, double Tmax, double Pmin, double Pmax, void Chebyshev::getParameters(AnyMap& rateNode, const Units& rate_units) const { double A = chebCoeffs_[0]; - if (A != A) { - // Evaluates true if A is not a number (unconfigured object) + if (std::isnan(A)) { // Return empty/unmodified AnyMap return; } From 65fd6d1b3d5602bec6bebf87a0c4c4291138955f Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 17 May 2021 02:54:20 -0500 Subject: [PATCH 67/84] [Kinetics] Fix Python docstrings --- interfaces/cython/cantera/reaction.pyx | 39 +++++++++++-------- .../cython/cantera/test/test_reaction.py | 7 ++-- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index eda1374da3d..7ff3a299f7d 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -1626,11 +1626,14 @@ cdef class PlogReaction3(Reaction3): An example for the definition of an `PlogReaction3` object is given as:: - rxn = PlogReaction3(equation='H2 + O2 <=> 2 OH', rate={"rate-constants": - [{'P': 1013.25, 'A': 1.2124e+16, 'b': -0.5779, 'Ea': 45491376.8}, - {'P': 101325., 'A': 4.9108e+31, 'b': -4.8507, 'Ea': 103649395.2}, - {'P': 1013250., 'A': 1.2866e+47, 'b': -9.0246, 'Ea': 166508556.0}, - {'P': 10132500., 'A': 5.9632e+56, 'b': -11.529, 'Ea': 220076726.4}]}) + rxn = PlogReaction3( + equation='H2 + O2 <=> 2 OH', + rate={"rate-constants": + [{'P': 1013.25, 'A': 1.2124e+16, 'b': -0.5779, 'Ea': 45491376.8}, + {'P': 101325., 'A': 4.9108e+31, 'b': -4.8507, 'Ea': 103649395.2}, + {'P': 1013250., 'A': 1.2866e+47, 'b': -9.0246, 'Ea': 166508556.0}, + {'P': 10132500., 'A': 5.9632e+56, 'b': -11.529, 'Ea': 220076726.4}]}, + kinetics=gas) The YAML description corresponding to this reaction is:: @@ -1693,7 +1696,7 @@ cdef class PlogReaction3(Reaction3): def __set__(self, rates): warnings.warn("Property 'rates' to be removed after Cantera 2.6. " - "Setter is replacable by assigning a new 'PlogRate' object created " + "Setter is replaceable by assigning a new 'PlogRate' object created " "from rates to the rate property.", DeprecationWarning) warnings.warn( self._deprecation_warning("rates"), DeprecationWarning) @@ -1705,7 +1708,7 @@ cdef class PlogReaction3(Reaction3): """ .. deprecated:: 2.6 To be deprecated with version 2.6, and removed thereafter. - Replacable by call to `rate` property. + Replaceable by call to `rate` property. """ warnings.warn( self._deprecation_warning("__call__", "method"), DeprecationWarning) @@ -1719,17 +1722,21 @@ cdef class ChebyshevReaction3(Reaction3): An example for the definition of an `PlogReaction3` object is given as:: - rxm = ChebyshevRate(Tmin=290., Tmax=3000., Pmin=1000., Pmax=10000000.0, - data=[[8.2883, -1.1397, -0.12059, 0.016034], - [1.9764, 1.0037, 7.2865e-03, -0.030432], - [0.3177, 0.26889, 0.094806, -7.6385e-03]]) + rxn = ChebyshevReaction3( + equation="HO2 <=> OH + O", + rate={"Tmin": 290.0, "Tmax": 3000.0, + "Pmin": 1e3, "Pmax": 1e8, + "data": [[8.2883, -1.1397, -0.12059, 0.016034], + [1.9764, 1.0037, 7.2865e-03, -0.030432], + [0.3177, 0.26889, 0.094806, -7.6385e-03]]}, + kinetics=gas) The YAML description corresponding to this reaction is:: - equation: HO2 <=> OH + O # Reaction 5 + equation: HO2 <=> OH + O type: Chebyshev temperature-range: [290.0, 3000.0] - pressure-range: [9.869232667160128e-03 atm, 98.69232667160128 atm] + pressure-range: [1.e-03 bar, 10. bar] data: - [8.2883, -1.1397, -0.12059, 0.016034] - [1.9764, 1.0037, 7.2865e-03, -0.030432] @@ -1869,10 +1876,10 @@ cdef class ChebyshevReaction3(Reaction3): .. deprecated:: 2.6 To be deprecated with version 2.6, and removed thereafter. - Replaced by `ChebyshevRate` construcor. + Replaced by `ChebyshevRate` constructor. """ warnings.warn("Method 'set_parameters' to be removed after Cantera 2.6. " - "Method is replacable by assigning a new 'ChebyshevRate' object to the " + "Method is replaceable by assigning a new 'ChebyshevRate' object to the " "rate property.", DeprecationWarning) self.rate = ChebyshevRate(Tmin, Tmax, Pmin, Pmax, coeffs) @@ -1880,7 +1887,7 @@ cdef class ChebyshevReaction3(Reaction3): """ .. deprecated:: 2.6 To be deprecated with version 2.6, and removed thereafter. - Replacable by call to `rate` property. + Replaceable by call to `rate` property. """ warnings.warn( self._deprecation_warning("__call__", "method"), DeprecationWarning) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 0b2b829dc09..56dff17ca76 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -259,7 +259,6 @@ def test_set_rates(self): rates = rate.rates self.assertEqual(len(rates), len(other)) - self.assertEqual(len(rates), len(other)) for index, item in enumerate(rates): P, rate = item self.assertNear(P, other[index]["P"]) @@ -481,7 +480,7 @@ def test_deprecated_setters(self): with self.assertWarnsRegex(DeprecationWarning, "property is moved"): self.check_equal(getattr(rxn, attr), new) - def test_deprecated_callers (self): + def test_deprecated_callers(self): # check methods deprecated in new framework if self._yaml is None: return @@ -691,7 +690,7 @@ class TestPlog3(TestPlog): class TestChebyshev(TestReaction): - # test legacy version of Cheyshev reaction + # test legacy version of Chebyshev reaction _cls = ct.ChebyshevReaction _equation = "HO2 <=> OH + O" @@ -723,7 +722,7 @@ def setUpClass(obj): class TestChebyshev3(TestChebyshev): - # test updated version of Cheyshev reaction + # test updated version of Chebyshev reaction _cls = ct.ChebyshevReaction3 _rate_obj = ct.ChebyshevRate(Tmin=290., Tmax=3000., Pmin=1000., Pmax=10000000.0, From 1ee6add74aa31f49fdf0f5ee1f2fe3e70bb112ce Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 17 May 2021 04:24:53 -0500 Subject: [PATCH 68/84] [Tests] Simplify test_reaction.py --- .../cython/cantera/test/test_reaction.py | 129 ++++++++---------- 1 file changed, 59 insertions(+), 70 deletions(-) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 56dff17ca76..d41d5b521dd 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -116,15 +116,14 @@ def test_user_override(self): self.assertEqual(rxn.reaction_type, "elementary") -class TestReactionRate(utilities.CanteraTest): +class ReactionRateTests: # test suite for reaction rate expressions - _cls = None - _type = None - _uses_pressure = False - _index = None - _input = None - _cls = None + _cls = None # reaction rate object to be tested + _type = None # name of reaction rate + _uses_pressure = False # rate evaluation requires pressure + _index = None # index of reaction in "kineticsfromscratch.yaml" + _input = None # input parameters (dict corresponding to YAML) @classmethod def setUpClass(cls): @@ -135,14 +134,10 @@ def setUpClass(cls): def test_type(self): # check reaction type - if self._index is None: - return self.assertIn(self._type, "{}".format(self.rate)) def test_rate_T(self): # check evaluation as a function of temperature only - if self._index is None: - return if self._uses_pressure: with self.assertRaisesRegex(ct.CanteraError, "reaction type requires pressure"): self.assertNear(self.rate(self.gas.T), @@ -153,15 +148,11 @@ def test_rate_T(self): def test_rate_TP(self): # check evaluation as a function of temperature and pressure - if self._index is None: - return self.assertNear(self.rate(self.gas.T, self.gas.P), self.gas.forward_rate_constants[self._index]) def test_input(self): # check instantiation based on input_data - if self._input is None or self._cls is None: - return rate = self._cls(input_data=self._input) self.assertIn(self._type, "{}".format(rate)) self.assertNear(rate(self.gas.T, self.gas.P), @@ -169,8 +160,6 @@ def test_input(self): def test_unconfigured(self): # check behavior of unconfigured rate object - if self._input is None or self._cls is None: - return rate = self._cls(input_data={}) self.assertTrue(np.isnan(rate(self.gas.T, self.gas.P))) input_data = rate.input_data @@ -187,14 +176,14 @@ def test_roundtrip(self): self.rate(self.gas.T, self.gas.P)) -class TestArrheniusRate(TestReactionRate): +class TestArrheniusRate(ReactionRateTests, utilities.CanteraTest): # test Arrhenius rate expressions + _cls = ct.ArrheniusRate _type = "Arrhenius" _uses_pressure = False _index = 0 _input = {"rate-constant": {"A": 38.7, "b": 2.7, "Ea": 26191840.0}} - _cls = ct.ArrheniusRate def setUp(self): self.A = self.gas.reaction(self._index).rate.pre_exponential_factor @@ -215,9 +204,10 @@ def test_allow_negative_pre_exponential_factor(self): self.assertTrue(self.rate.allow_negative_pre_exponential_factor) -class TestPlogRate(TestReactionRate): +class TestPlogRate(ReactionRateTests, utilities.CanteraTest): # test Plog rate expressions + _cls = ct.PlogRate _type = "Plog" _uses_pressure = True _index = 3 @@ -226,7 +216,6 @@ class TestPlogRate(TestReactionRate): {"P": 101325., "A": 4.9108e+31, "b": -4.8507, "Ea": 103649395.2}, {"P": 1013250., "A": 1.2866e+47, "b": -9.0246, "Ea": 166508556.0}, {"P": 10132500., "A": 5.9632e+56, "b": -11.529, "Ea": 220076726.4}]} - _cls = ct.PlogRate def setUp(self): self.rate = ct.PlogRate([(1013.25, ct.Arrhenius(1.2124e+16, -0.5779, 45491376.8)), @@ -272,9 +261,10 @@ def test_no_rates(self): self.assertIsInstance(rate.rates, list) -class TestChebyshevRate(TestReactionRate): +class TestChebyshevRate(ReactionRateTests, utilities.CanteraTest): # test Chebyshev rate expressions + _cls = ct.ChebyshevRate _type = "Chebyshev" _uses_pressure = True _index = 4 @@ -283,7 +273,6 @@ class TestChebyshevRate(TestReactionRate): [0.3177, 0.26889, 0.094806, -0.0076385]], "pressure-range": [1000.0, 10000000.0], "temperature-range": [290.0, 3000.0]} - _cls = ct.ChebyshevRate def setUp(self): self.Tmin = self.gas.reaction(self._index).rate.Tmin @@ -302,21 +291,22 @@ def test_parameters(self): self.assertTrue(np.all(self.coeffs == self.rate.coeffs)) -class TestReaction(utilities.CanteraTest): +class ReactionTests: # test suite for reaction expressions - _cls = None - _equation = None - _rate = None - _rate_obj = None - _kwargs = {} - _index = None - _type = None - _yaml = None - _input = None - _deprecated_getters = {} - _deprecated_setters = {} - _deprecated_callers = {} + _cls = None # reaction object to be tested + _type = None # name of reaction rate + _legacy = True # object uses legacy framework + _equation = None # reaction equation string + _rate = None # parameters for reaction rate object constructor + _rate_obj = None # reaction rate object + _kwargs = {} # additional parameters required by contructor + _index = None # index of reaction in "kineticsfromscratch.yaml" + _yaml = None # YAML parameterization + _input = None # input parameters (dict corresponding to YAML) + _deprecated_getters = {} # test legacy getters (old framework) + _deprecated_setters = {} # test legacy setters (old framework) + _deprecated_callers = {} # test legacy callers (old framework) @classmethod def setUpClass(cls): @@ -351,15 +341,15 @@ def test_rate(self): # check consistency of reaction rate and forward rate constant if self._rate_obj is None: return - if "Rate" in type(self._rate_obj).__name__: + if self._legacy: + self.assertNear(self._rate_obj(self.gas.T), self.gas.forward_rate_constants[self._index]) + else: self.assertNear(self._rate_obj(self.gas.T, self.gas.P), self.gas.forward_rate_constants[self._index]) - else: - self.assertNear(self._rate_obj(self.gas.T), self.gas.forward_rate_constants[self._index]) def test_from_parts(self): # check instantiation from parts (reactants, products, rate expression) - if self._cls is None or not hasattr(self._cls, "rate"): + if not hasattr(self._cls, "rate"): return orig = self.gas.reaction(self._index) rxn = self._cls(orig.reactants, orig.products) @@ -368,8 +358,6 @@ def test_from_parts(self): def test_from_dict(self): # check instantiation from keywords / rate defined by dictionary - if self._cls is None: - return rxn = self._cls(equation=self._equation, rate=self._rate, kinetics=self.gas, **self._kwargs) self.check_rxn(rxn) @@ -382,14 +370,14 @@ def test_from_yaml(self): def test_from_rate(self): # check instantiation from keywords / rate provided as object - if self._cls is None or self._rate_obj is None: + if self._rate_obj is None: return rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) self.check_rxn(rxn) def test_add_rxn(self): # check adding new reaction to solution - if self._cls is None or self._rate_obj is None: + if self._rate_obj is None: return gas2 = ct.Solution(thermo="IdealGas", kinetics="GasKinetics", species=self.species, reactions=[]) @@ -401,22 +389,18 @@ def test_add_rxn(self): def test_raises_invalid_rate(self): # check exception for instantiation from keywords / invalid rate - if self._cls is None: - return with self.assertRaises(TypeError): rxn = self._cls(equation=self._equation, rate=(), kinetics=self.gas, **self._kwargs) def test_no_rate(self): # check behavior for instantiation from keywords / no rate - if self._cls is None or not hasattr(self._cls, "rate"): + if not hasattr(self._cls, "rate"): return rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) - if "Rate" in type(self._rate_obj).__name__: - # rate expressions from new framework end in "Rate" - self.assertTrue(np.isnan(rxn.rate(self.gas.T, self.gas.P))) - else: - # legacy framework + if self._legacy: self.assertNear(rxn.rate(self.gas.T), 0.) + else: + self.assertTrue(np.isnan(rxn.rate(self.gas.T, self.gas.P))) gas2 = ct.Solution(thermo="IdealGas", kinetics="GasKinetics", species=self.species, reactions=[rxn]) @@ -427,7 +411,7 @@ def test_no_rate(self): def test_replace_rate(self): # check replacing reaction rate expression - if self._cls is None or self._rate_obj is None: + if self._rate_obj is None: return rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) rxn.rate = self._rate_obj @@ -435,7 +419,7 @@ def test_replace_rate(self): def test_roundtrip(self): # check round-trip instantiation via input_data - if self._cls is None or self._type.endswith("-old"): + if self._legacy: return rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) input_data = rxn.rate.input_data @@ -495,16 +479,16 @@ def test_deprecated_callers(self): self.check_equal(rxn(T, P), value) -class TestElementary(TestReaction): +class TestElementary(ReactionTests, utilities.CanteraTest): # test legacy version of elementary reaction _cls = ct.ElementaryReaction + _type = "elementary-old" _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) _kwargs = {} _index = 0 - _type = "elementary-old" _yaml = """ equation: O + H2 <=> H + OH type: elementary-old @@ -518,8 +502,9 @@ class TestElementary3(TestElementary): # test updated version of elementary reaction _cls = ct.ElementaryReaction3 - _rate_obj = ct.ArrheniusRate(38.7, 2.7, 2.619184e+07) _type = "elementary" + _legacy = False + _rate_obj = ct.ArrheniusRate(38.7, 2.7, 2.619184e+07) _yaml = """ equation: O + H2 <=> H + OH rate-constant: {A: 38.7, b: 2.7, Ea: 6260.0 cal/mol} @@ -530,12 +515,12 @@ class TestThreeBody(TestElementary): # test legacy version of three-body reaction _cls = ct.ThreeBodyReaction + _type = "three-body-old" _equation = "2 O + M <=> O2 + M" _rate = {"A": 1.2e11, "b": -1.0, "Ea": 0.0} _rate_obj = ct.Arrhenius(1.2e11, -1., 0.) _kwargs = {"efficiencies": {"H2": 2.4, "H2O": 15.4, "AR": 0.83}} _index = 1 - _type = "three-body-old" _yaml = """ equation: 2 O + M <=> O2 + M type: three-body-old @@ -566,8 +551,9 @@ class TestThreeBody3(TestThreeBody): # test updated version of three-body reaction _cls = ct.ThreeBodyReaction3 - _rate_obj = ct.ArrheniusRate(1.2e11, -1., 0.) _type = "three-body" + _legacy = False + _rate_obj = ct.ArrheniusRate(1.2e11, -1., 0.) _yaml = """ equation: 2 O + M <=> O2 + M type: three-body @@ -580,11 +566,12 @@ class TestImplicitThreeBody3(TestThreeBody): # test three-body reactions with explicit collision parther _cls = ct.ThreeBodyReaction3 + _type = "three-body" + _legacy = False _equation = "H + 2 O2 <=> HO2 + O2" _rate = {"A": 2.08e+19, "b": -1.24, "Ea": 0.0} _rate_obj = ct.ArrheniusRate(2.08e+19, -1.24, 0.) _index = 5 - _type = "three-body" _yaml = """ equation: H + 2 O2 <=> HO2 + O2 rate-constant: {A: 2.08e+19, b: -1.24, Ea: 0.0} @@ -606,17 +593,17 @@ def test_from_parts(self): self.check_rxn(rxn) -class TestPlog(TestReaction): +class TestPlog(ReactionTests, utilities.CanteraTest): # test legacy version of Plog reaction _cls = ct.PlogReaction + _type = "pressure-dependent-Arrhenius-old" _equation = "H2 + O2 <=> 2 OH" _rate = {"rate-constants": [ {"P": 1013.25, "A": 1.2124e+16, "b": -0.5779, "Ea": 45491376.8}, {"P": 101325., "A": 4.9108e+31, "b": -4.8507, "Ea": 103649395.2}, {"P": 1013250., "A": 1.2866e+47, "b": -9.0246, "Ea": 166508556.0}, {"P": 10132500., "A": 5.9632e+56, "b": -11.529, "Ea": 220076726.4}]} - _type = "pressure-dependent-Arrhenius-old" _index = 3 _yaml = """ equation: H2 + O2 <=> 2 OH # Reaction 4 @@ -673,11 +660,12 @@ class TestPlog3(TestPlog): # test updated version of Plog reaction _cls = ct.PlogReaction3 + _type = "pressure-dependent-Arrhenius" + _legacy = False _rate_obj = ct.PlogRate([(1013.25, ct.Arrhenius(1.2124e+16, -0.5779, 45491376.8)), (101325., ct.Arrhenius(4.9108e+31, -4.8507, 103649395.2)), (1013250., ct.Arrhenius(1.2866e+47, -9.0246, 166508556.0)), (10132500., ct.Arrhenius(5.9632e+56, -11.529, 220076726.4))]) - _type = "pressure-dependent-Arrhenius" _yaml = """ equation: H2 + O2 <=> 2 OH # Reaction 4 type: pressure-dependent-Arrhenius @@ -689,16 +677,16 @@ class TestPlog3(TestPlog): """ -class TestChebyshev(TestReaction): +class TestChebyshev(ReactionTests, utilities.CanteraTest): # test legacy version of Chebyshev reaction _cls = ct.ChebyshevReaction + _type = "Chebyshev-old" _equation = "HO2 <=> OH + O" _rate = {"Tmin": 290., "Tmax": 3000., "Pmin": 1000., "Pmax": 10000000.0, "data": [[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], [ 1.9764e+00, 1.0037e+00, 7.2865e-03, -3.0432e-02], [ 3.1770e-01, 2.6889e-01, 9.4806e-02, -7.6385e-03]]} - _type = "Chebyshev-old" _index = 4 _yaml = """ equation: HO2 <=> OH + O # Reaction 5 @@ -715,7 +703,7 @@ class TestChebyshev(TestReaction): @classmethod def setUpClass(obj): - TestReaction.setUpClass() + ReactionTests.setUpClass() obj._deprecated_getters.update({"coeffs": np.array(obj._rate["data"])}) obj._deprecated_getters.update( {k: v for k, v in obj._rate.items() if k != "data"}) @@ -725,11 +713,12 @@ class TestChebyshev3(TestChebyshev): # test updated version of Chebyshev reaction _cls = ct.ChebyshevReaction3 + _type = "Chebyshev" + _legacy = False _rate_obj = ct.ChebyshevRate(Tmin=290., Tmax=3000., Pmin=1000., Pmax=10000000.0, data=[[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], [ 1.9764e+00, 1.0037e+00, 7.2865e-03, -3.0432e-02], [ 3.1770e-01, 2.6889e-01, 9.4806e-02, -7.6385e-03]]) - _type = "Chebyshev" _yaml = """ equation: HO2 <=> OH + O # Reaction 5 type: Chebyshev @@ -742,15 +731,16 @@ class TestChebyshev3(TestChebyshev): """ -class TestCustom(TestReaction): +class TestCustom(ReactionTests, utilities.CanteraTest): # test Custom reaction # probe O + H2 <=> H + OH _cls = ct.CustomReaction + _type = "custom-rate-function" + _legacy = False _equation = "H2 + O <=> H + OH" _rate_obj = ct.CustomRate(lambda T: 38.7 * T**2.7 * exp(-3150.15428/T)) _index = 0 - _type = "custom-rate-function" _yaml = None def setUp(self): @@ -779,4 +769,3 @@ def test_custom_lambda(self): rate=lambda T: 38.7 * T**2.7 * exp(-3150.15428/T), kinetics=self.gas) self.check_rxn(rxn) - From 18c616d0f2c3aea38cebf0b65481c472056eb461 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 17 May 2021 07:03:11 -0500 Subject: [PATCH 69/84] [Kinetics] Use suffix -legacy rather than -old for reaction type names --- include/cantera/kinetics/Reaction.h | 8 ++--- interfaces/cython/cantera/reaction.pyx | 8 ++--- .../cython/cantera/test/test_kinetics.py | 4 +-- .../cython/cantera/test/test_reaction.py | 26 +++++++------- src/kinetics/GasKinetics.cpp | 16 ++++----- src/kinetics/Kinetics.cpp | 2 +- src/kinetics/ReactionFactory.cpp | 36 +++++++++---------- test/kinetics/kineticsFromYaml.cpp | 8 ++--- 8 files changed, 54 insertions(+), 54 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 45c80e5b183..4a1422bfe3d 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -164,7 +164,7 @@ class ElementaryReaction : public Reaction virtual void getParameters(AnyMap& reactionNode) const; virtual std::string type() const { - return "elementary-old"; + return "elementary-legacy"; } Arrhenius rate; @@ -205,7 +205,7 @@ class ThreeBodyReaction : public ElementaryReaction const Arrhenius& rate, const ThirdBody& tbody); virtual std::string type() const { - return "three-body-old"; + return "three-body-legacy"; } virtual std::string reactantString() const; @@ -301,7 +301,7 @@ class PlogReaction : public Reaction const Plog& rate); virtual std::string type() const { - return "pressure-dependent-Arrhenius-old"; + return "pressure-dependent-Arrhenius-legacy"; } virtual void validate(); @@ -322,7 +322,7 @@ class ChebyshevReaction : public Reaction virtual void getParameters(AnyMap& reactionNode) const; virtual std::string type() const { - return "Chebyshev-old"; + return "Chebyshev-legacy"; } Chebyshev rate; diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 7ff3a299f7d..fb32730430e 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -752,7 +752,7 @@ cdef class ElementaryReaction(Reaction): The implementation of this reaction type will change after Cantera 2.6; refer to `ElementaryReaction3` for new behavior. """ - reaction_type = "elementary-old" + reaction_type = "elementary-legacy" def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): @@ -807,7 +807,7 @@ cdef class ThreeBodyReaction(ElementaryReaction): The implementation of this reaction type will change after Cantera 2.6; refer to `ThreeBodyReaction3` for new behavior. """ - reaction_type = "three-body-old" + reaction_type = "three-body-legacy" def __init__(self, equation=None, rate=None, efficiencies=None, Kinetics kinetics=None, init=True, **kwargs): @@ -1050,7 +1050,7 @@ cdef class PlogReaction(Reaction): The implementation of this reaction type will change after Cantera 2.6; refer to `PlogReaction3` for new behavior. """ - reaction_type = "pressure-dependent-Arrhenius-old" + reaction_type = "pressure-dependent-Arrhenius-legacy" def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): @@ -1114,7 +1114,7 @@ cdef class ChebyshevReaction(Reaction): The implementation of this reaction type will change after Cantera 2.6; refer to `ChebyshevReaction3` for new behavior. """ - reaction_type = "Chebyshev-old" + reaction_type = "Chebyshev-legacy" def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 5c9357f2bc4..88574dc5141 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -48,8 +48,8 @@ def test_multiplier(self): self.assertArrayNear(0.5 * rev_rates0, rev_rates2) def test_reaction_type(self): - self.assertIn(self.phase.reaction_type_str(0), ["three-body", "three-body-old"]) - self.assertIn(self.phase.reaction_type_str(2), ["elementary", "elementary-old"]) + self.assertIn(self.phase.reaction_type_str(0), ["three-body", "three-body-legacy"]) + self.assertIn(self.phase.reaction_type_str(2), ["elementary", "elementary-legacy"]) self.assertEqual(self.phase.reaction_type_str(21), "falloff") with self.assertRaisesRegex(ValueError, 'out of range'): diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index d41d5b521dd..53c068d78e7 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -442,7 +442,7 @@ def test_deprecated_getters(self): rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) for attr, default in self._deprecated_getters.items(): - if self._type.endswith("-old"): + if self._legacy: self.check_equal(getattr(rxn, attr), default) else: with self.assertWarnsRegex(DeprecationWarning, "property is moved"): @@ -455,7 +455,7 @@ def test_deprecated_setters(self): rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) for attr, new in self._deprecated_setters.items(): - if self._type.endswith("-old"): + if self._legacy: setattr(rxn, attr, new) self.check_equal(getattr(rxn, attr), new) else: @@ -472,7 +472,7 @@ def test_deprecated_callers(self): rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) for state, value in self._deprecated_callers.items(): T, P = state - if self._type.endswith("-old"): + if self._legacy: self.check_equal(rxn(T, P), value) else: with self.assertWarnsRegex(DeprecationWarning, "method is moved"): @@ -483,7 +483,7 @@ class TestElementary(ReactionTests, utilities.CanteraTest): # test legacy version of elementary reaction _cls = ct.ElementaryReaction - _type = "elementary-old" + _type = "elementary-legacy" _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) @@ -491,7 +491,7 @@ class TestElementary(ReactionTests, utilities.CanteraTest): _index = 0 _yaml = """ equation: O + H2 <=> H + OH - type: elementary-old + type: elementary-legacy rate-constant: {A: 38.7, b: 2.7, Ea: 6260.0 cal/mol} """ _deprecated_getters = {"allow_negative_pre_exponential_factor": False} @@ -515,7 +515,7 @@ class TestThreeBody(TestElementary): # test legacy version of three-body reaction _cls = ct.ThreeBodyReaction - _type = "three-body-old" + _type = "three-body-legacy" _equation = "2 O + M <=> O2 + M" _rate = {"A": 1.2e11, "b": -1.0, "Ea": 0.0} _rate_obj = ct.Arrhenius(1.2e11, -1., 0.) @@ -523,7 +523,7 @@ class TestThreeBody(TestElementary): _index = 1 _yaml = """ equation: 2 O + M <=> O2 + M - type: three-body-old + type: three-body-legacy rate-constant: {A: 1.2e+11, b: -1.0, Ea: 0.0 cal/mol} efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} """ @@ -597,7 +597,7 @@ class TestPlog(ReactionTests, utilities.CanteraTest): # test legacy version of Plog reaction _cls = ct.PlogReaction - _type = "pressure-dependent-Arrhenius-old" + _type = "pressure-dependent-Arrhenius-legacy" _equation = "H2 + O2 <=> 2 OH" _rate = {"rate-constants": [ {"P": 1013.25, "A": 1.2124e+16, "b": -0.5779, "Ea": 45491376.8}, @@ -607,7 +607,7 @@ class TestPlog(ReactionTests, utilities.CanteraTest): _index = 3 _yaml = """ equation: H2 + O2 <=> 2 OH # Reaction 4 - type: pressure-dependent-Arrhenius-old + type: pressure-dependent-Arrhenius-legacy rate-constants: - {P: 0.01 atm, A: 1.2124e+16, b: -0.5779, Ea: 1.08727e+04 cal/mol} - {P: 1.0 atm, A: 4.9108e+31, b: -4.8507, Ea: 2.47728e+04 cal/mol} @@ -629,7 +629,7 @@ def check_rates(self, rates, other): def test_deprecated_getters(self): # overload default tester for deprecated property getters rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) - if self._type.endswith("-old"): + if self._legacy: self.check_rates(rxn.rates, self._rate["rate-constants"]) else: with self.assertWarnsRegex(DeprecationWarning, "property is moved"): @@ -646,7 +646,7 @@ def test_deprecated_setters(self): rates = rate.rates rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) - if self._type.endswith("-old"): + if self._legacy: rxn.rates = rates self.check_rates(rxn.rates, _rate) else: @@ -681,7 +681,7 @@ class TestChebyshev(ReactionTests, utilities.CanteraTest): # test legacy version of Chebyshev reaction _cls = ct.ChebyshevReaction - _type = "Chebyshev-old" + _type = "Chebyshev-legacy" _equation = "HO2 <=> OH + O" _rate = {"Tmin": 290., "Tmax": 3000., "Pmin": 1000., "Pmax": 10000000.0, "data": [[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], @@ -690,7 +690,7 @@ class TestChebyshev(ReactionTests, utilities.CanteraTest): _index = 4 _yaml = """ equation: HO2 <=> OH + O # Reaction 5 - type: Chebyshev-old + type: Chebyshev-legacy temperature-range: [290.0, 3000.0] pressure-range: [9.869232667160128e-03 atm, 98.69232667160128 atm] data: diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index a17bc38b494..0e7eeb90576 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -266,17 +266,17 @@ bool GasKinetics::addReaction(shared_ptr r) return true; } - if (r->type() == "elementary-old") { + if (r->type() == "elementary-legacy") { addElementaryReaction(dynamic_cast(*r)); - } else if (r->type() == "three-body-old") { + } else if (r->type() == "three-body-legacy") { 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-old") { + } else if (r->type() == "pressure-dependent-Arrhenius-legacy") { addPlogReaction(dynamic_cast(*r)); - } else if (r->type() == "Chebyshev-old") { + } else if (r->type() == "Chebyshev-legacy") { addChebyshevReaction(dynamic_cast(*r)); } else if (r->type() == "Blowers-Masel") { addBlowersMaselReaction(dynamic_cast(*r)); @@ -358,17 +358,17 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) return; } - if (rNew->type() == "elementary-old") { + if (rNew->type() == "elementary-legacy") { modifyElementaryReaction(i, dynamic_cast(*rNew)); - } else if (rNew->type() == "three-body-old") { + } else if (rNew->type() == "three-body-legacy") { 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-old") { + } else if (rNew->type() == "pressure-dependent-Arrhenius-legacy") { modifyPlogReaction(i, dynamic_cast(*rNew)); - } else if (rNew->type() == "Chebyshev-old") { + } else if (rNew->type() == "Chebyshev-legacy") { modifyChebyshevReaction(i, dynamic_cast(*rNew)); } else if (rNew->type() == "Blowers-Masel") { modifyBlowersMaselReaction(i, dynamic_cast(*rNew)); diff --git a/src/kinetics/Kinetics.cpp b/src/kinetics/Kinetics.cpp index 863f7a37b6e..622773be6b0 100644 --- a/src/kinetics/Kinetics.cpp +++ b/src/kinetics/Kinetics.cpp @@ -159,7 +159,7 @@ std::pair Kinetics::checkDuplicates(bool throw_err) const if (thirdBodyOk) { continue; // No overlap in third body efficiencies } - } else if (R.type() == "three-body-old") { + } else if (R.type() == "three-body-legacy") { ThirdBody& tb1 = dynamic_cast(R).third_body; ThirdBody& tb2 = dynamic_cast(other).third_body; bool thirdBodyOk = true; diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index b741d87abbe..25e2a72e8b0 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -29,7 +29,7 @@ ReactionFactory::ReactionFactory() addAlias("elementary", ""); // register elementary reactions (old framework) - reg("elementary-old", [](const AnyMap& node, const Kinetics& kin) { + reg("elementary-legacy", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ElementaryReaction(); if (!node.empty()) { setupElementaryReaction(*(ElementaryReaction*)R, node, kin); @@ -45,7 +45,7 @@ ReactionFactory::ReactionFactory() addAlias("three-body", "three_body"); // register three-body reactions (old framework) - reg("three-body-old", [](const AnyMap& node, const Kinetics& kin) { + reg("three-body-legacy", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ThreeBodyReaction(); if (!node.empty()) { setupThreeBodyReaction(*(ThreeBodyReaction*)R, node, kin); @@ -81,7 +81,7 @@ ReactionFactory::ReactionFactory() addAlias("pressure-dependent-Arrhenius", "pdep_arrhenius"); // register pressure-dependent-Arrhenius reactions (old framework) - reg("pressure-dependent-Arrhenius-old", [](const AnyMap& node, const Kinetics& kin) { + reg("pressure-dependent-Arrhenius-legacy", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new PlogReaction(); if (!node.empty()) { setupPlogReaction(*(PlogReaction*)R, node, kin); @@ -94,7 +94,7 @@ ReactionFactory::ReactionFactory() return new ChebyshevReaction3(node, kin); }); addAlias("Chebyshev", "chebyshev"); - reg("Chebyshev-old", [](const AnyMap& node, const Kinetics& kin) { + reg("Chebyshev-legacy", [](const AnyMap& node, const Kinetics& kin) { Reaction* R = new ChebyshevReaction(); if (!node.empty()) { setupChebyshevReaction(*(ChebyshevReaction*)R, node, kin); @@ -152,24 +152,24 @@ std::mutex ReactionFactoryXML::reaction_mutex; ReactionFactoryXML::ReactionFactoryXML() { // register elementary reactions - reg("elementary-old", [](const XML_Node& node) { + reg("elementary-legacy", [](const XML_Node& node) { Reaction* R = new ElementaryReaction(); setupElementaryReaction(*(ElementaryReaction*)R, node); return R; }); - addAlias("elementary-old", "elementary"); - addAlias("elementary-old", "arrhenius"); - addAlias("elementary-old", ""); + addAlias("elementary-legacy", "elementary"); + addAlias("elementary-legacy", "arrhenius"); + addAlias("elementary-legacy", ""); // register three-body reactions - reg("three-body-old", [](const XML_Node& node) { + reg("three-body-legacy", [](const XML_Node& node) { Reaction* R = new ThreeBodyReaction(); setupThreeBodyReaction(*(ThreeBodyReaction*)R, node); return R; }); - addAlias("three-body-old", "three-body"); - addAlias("three-body-old", "threebody"); - addAlias("three-body-old", "three_body"); + addAlias("three-body-legacy", "three-body"); + addAlias("three-body-legacy", "threebody"); + addAlias("three-body-legacy", "three_body"); // register falloff reactions reg("falloff", [](const XML_Node& node) { @@ -188,22 +188,22 @@ ReactionFactoryXML::ReactionFactoryXML() addAlias("chemically-activated", "chemically_activated"); // register pressure-depdendent-Arrhenius reactions - reg("pressure-dependent-Arrhenius-old", [](const XML_Node& node) { + reg("pressure-dependent-Arrhenius-legacy", [](const XML_Node& node) { Reaction* R = new PlogReaction(); setupPlogReaction(*(PlogReaction*)R, node); return R; }); - addAlias("pressure-dependent-Arrhenius-old", "pressure-dependent-Arrhenius"); - addAlias("pressure-dependent-Arrhenius-old", "plog"); - addAlias("pressure-dependent-Arrhenius-old", "pdep_arrhenius"); + addAlias("pressure-dependent-Arrhenius-legacy", "pressure-dependent-Arrhenius"); + addAlias("pressure-dependent-Arrhenius-legacy", "plog"); + addAlias("pressure-dependent-Arrhenius-legacy", "pdep_arrhenius"); // register Chebyshev reactions - reg("Chebyshev-old", [](const XML_Node& node) { + reg("Chebyshev-legacy", [](const XML_Node& node) { Reaction* R = new ChebyshevReaction(); setupChebyshevReaction(*(ChebyshevReaction*)R, node); return R; }); - addAlias("Chebyshev-old", "chebyshev"); + addAlias("Chebyshev-legacy", "chebyshev"); // register interface reactions reg("interface", [](const XML_Node& node) { diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index 75eb6e6c6ba..ae68a4d621a 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -57,14 +57,14 @@ TEST(Reaction, ElementaryFromYaml2) auto sol = newSolution("gri30.yaml"); AnyMap rxn = AnyMap::fromYamlString( "{equation: N + NO <=> N2 + O," - " type: elementary-old," + " type: elementary-legacy," " rate-constant: [-2.70000E+13 cm^3/mol/s, 0, 355 cal/mol]," " negative-A: true}"); auto R = newReaction(rxn, *(sol->kinetics())); EXPECT_EQ(R->reactants.at("NO"), 1); EXPECT_EQ(R->products.at("N2"), 1); - EXPECT_EQ(R->type(), "elementary-old"); + EXPECT_EQ(R->type(), "elementary-legacy"); auto ER = dynamic_cast(*R); EXPECT_DOUBLE_EQ(ER.rate.preExponentialFactor(), -2.7e10); @@ -109,13 +109,13 @@ TEST(Reaction, ThreeBodyFromYaml3) auto sol = newSolution("gri30.yaml"); AnyMap rxn = AnyMap::fromYamlString( "{equation: 2 O + M = O2 + M," - " type: three-body-old," + " type: three-body-legacy," " rate-constant: [1.20000E+17 cm^6/mol^2/s, -1, 0]," " efficiencies: {AR: 0.83, H2O: 5}}"); auto R = newReaction(rxn, *(sol->kinetics())); EXPECT_EQ(R->reactants.count("M"), (size_t) 0); - EXPECT_EQ(R->type(), "three-body-old"); + EXPECT_EQ(R->type(), "three-body-legacy"); auto TBR = dynamic_cast(*R); EXPECT_DOUBLE_EQ(TBR.rate.preExponentialFactor(), 1.2e11); From 57d604a3adaf60a7b3d5cc0f5d3b10ea7b1ca388 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 18 May 2021 05:07:28 -0500 Subject: [PATCH 70/84] [Kinetics] Implement *Reaction2 in conjunction with C++ preprocessor --- include/cantera/kinetics/BulkKinetics.h | 6 +- include/cantera/kinetics/GasKinetics.h | 12 ++-- include/cantera/kinetics/Reaction.h | 67 ++++++++++-------- interfaces/cython/cantera/_cantera.pxd | 14 ++-- interfaces/cython/cantera/reaction.pyx | 36 +++++----- src/kinetics/BulkKinetics.cpp | 4 +- src/kinetics/GasKinetics.cpp | 28 ++++---- src/kinetics/Kinetics.cpp | 4 +- src/kinetics/Reaction.cpp | 92 ++++++++++++------------- src/kinetics/ReactionFactory.cpp | 38 +++++----- src/kinetics/RxnRates.cpp | 2 +- test/kinetics/kineticsFromScratch.cpp | 32 ++++----- test/kinetics/kineticsFromYaml.cpp | 16 ++--- 13 files changed, 182 insertions(+), 169 deletions(-) diff --git a/include/cantera/kinetics/BulkKinetics.h b/include/cantera/kinetics/BulkKinetics.h index 311cbfbc250..f2f73cc3294 100644 --- a/include/cantera/kinetics/BulkKinetics.h +++ b/include/cantera/kinetics/BulkKinetics.h @@ -17,7 +17,7 @@ namespace Cantera { -class ElementaryReaction; +class ElementaryReaction2; //! Partial specialization of Kinetics for chemistry in a single bulk phase class BulkKinetics : public Kinetics @@ -49,8 +49,8 @@ class BulkKinetics : public Kinetics void addThirdBody(shared_ptr r); protected: - virtual void addElementaryReaction(ElementaryReaction& r); - virtual void modifyElementaryReaction(size_t i, ElementaryReaction& rNew); + virtual void addElementaryReaction(ElementaryReaction2& r); + virtual void modifyElementaryReaction(size_t i, ElementaryReaction2& rNew); //! Vector of rate handlers std::vector> m_bulk_rates; diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index 3386b887177..a877e0abb83 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -110,21 +110,21 @@ class GasKinetics : public BulkKinetics // transitional reaction types marked as '-old' //! @deprecated Cantera 2.6 (replaced by MultiRate approach) - void addThreeBodyReaction(ThreeBodyReaction& r); + void addThreeBodyReaction(ThreeBodyReaction2& r); void addFalloffReaction(FalloffReaction& r); //! @deprecated Cantera 2.6 (replaced by MultiRate approach) - void addPlogReaction(PlogReaction& r); + void addPlogReaction(PlogReaction2& r); //! @deprecated Cantera 2.6 (replaced by MultiRate approach) - void addChebyshevReaction(ChebyshevReaction& r); + void addChebyshevReaction(ChebyshevReaction2& r); void addBlowersMaselReaction(BlowersMaselReaction& r); //! @deprecated Cantera 2.6 (replaced by MultiRate approach) - void modifyThreeBodyReaction(size_t i, ThreeBodyReaction& r); + void modifyThreeBodyReaction(size_t i, ThreeBodyReaction2& r); void modifyFalloffReaction(size_t i, FalloffReaction& r); //! @deprecated Cantera 2.6 (replaced by MultiRate approach) - void modifyPlogReaction(size_t i, PlogReaction& r); + void modifyPlogReaction(size_t i, PlogReaction2& r); //! @deprecated Cantera 2.6 (replaced by MultiRate approach) - void modifyChebyshevReaction(size_t i, ChebyshevReaction& r); + void modifyChebyshevReaction(size_t i, ChebyshevReaction2& r); void modifyBlowersMaselReaction(size_t i, BlowersMaselReaction& r); //! Update the equilibrium constants in molar units. diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 4a1422bfe3d..1212beb9bbf 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -154,12 +154,12 @@ class Reaction //! A reaction which follows mass-action kinetics with a modified Arrhenius //! reaction rate. -class ElementaryReaction : public Reaction +class ElementaryReaction2 : public Reaction { public: - ElementaryReaction(); - ElementaryReaction(const Composition& reactants, const Composition products, - const Arrhenius& rate); + ElementaryReaction2(); + ElementaryReaction2(const Composition& reactants, const Composition products, + const Arrhenius& rate); virtual void validate(); virtual void getParameters(AnyMap& reactionNode) const; @@ -197,12 +197,12 @@ class ThirdBody //! A reaction with a non-reacting third body "M" that acts to add or remove //! energy from the reacting species -class ThreeBodyReaction : public ElementaryReaction +class ThreeBodyReaction2 : public ElementaryReaction2 { public: - ThreeBodyReaction(); - ThreeBodyReaction(const Composition& reactants, const Composition& products, - const Arrhenius& rate, const ThirdBody& tbody); + ThreeBodyReaction2(); + ThreeBodyReaction2(const Composition& reactants, const Composition& products, + const Arrhenius& rate, const ThirdBody& tbody); virtual std::string type() const { return "three-body-legacy"; @@ -293,12 +293,12 @@ class ChemicallyActivatedReaction : public FalloffReaction //! A pressure-dependent reaction parameterized by logarithmically interpolating //! between Arrhenius rate expressions at various pressures. -class PlogReaction : public Reaction +class PlogReaction2 : public Reaction { public: - PlogReaction(); - PlogReaction(const Composition& reactants, const Composition& products, - const Plog& rate); + PlogReaction2(); + PlogReaction2(const Composition& reactants, const Composition& products, + const Plog& rate); virtual std::string type() const { return "pressure-dependent-Arrhenius-legacy"; @@ -313,12 +313,12 @@ class PlogReaction : public Reaction //! A pressure-dependent reaction parameterized by a bi-variate Chebyshev //! polynomial in temperature and pressure -class ChebyshevReaction : public Reaction +class ChebyshevReaction2 : public Reaction { public: - ChebyshevReaction(); - ChebyshevReaction(const Composition& reactants, const Composition& products, - const Chebyshev& rate); + ChebyshevReaction2(); + ChebyshevReaction2(const Composition& reactants, const Composition& products, + const Chebyshev& rate); virtual void getParameters(AnyMap& reactionNode) const; virtual std::string type() const { @@ -346,7 +346,7 @@ struct CoverageDependency //! A reaction occurring on an interface (i.e. a SurfPhase or an EdgePhase) -class InterfaceReaction : public ElementaryReaction +class InterfaceReaction : public ElementaryReaction2 { public: InterfaceReaction(); @@ -454,8 +454,8 @@ class BlowersMaselInterfaceReaction : public BlowersMaselReaction //! An intermediate class used to avoid naming conflicts of 'rate' member -//! variables and getters (see `ElementaryReaction`, `PlogReaction` and -//! `ChebyshevReaction`). +//! variables and getters (see `ElementaryReaction2`, `PlogReaction2` and +//! `ChebyshevReaction2`). class Reaction3 : public Reaction { public: @@ -595,6 +595,19 @@ class CustomFunc1Reaction : public Reaction3 }; +#ifdef CT_NO_LEGACY_REACTIONS_26 +typedef ElementaryReaction3 ElementaryReaction; +typedef ThreeBodyReaction3 ThreeBodyReaction; +typedef PlogReaction3 PlogReaction; +typedef ChebyshevReaction3 ChebyshevReaction; +#else +typedef ElementaryReaction2 ElementaryReaction; +typedef ThreeBodyReaction2 ThreeBodyReaction; +typedef PlogReaction2 PlogReaction; +typedef ChebyshevReaction2 ChebyshevReaction; +#endif + + //! Create Reaction objects for all `` nodes in an XML document. //! //! The `` nodes are assumed to be children of the `` @@ -631,14 +644,14 @@ void parseReactionEquation(Reaction& R, const AnyValue& equation, // declarations of setup functions void setupReaction(Reaction& R, const XML_Node& rxn_node); -void setupElementaryReaction(ElementaryReaction&, const XML_Node&); +void setupElementaryReaction(ElementaryReaction2&, const XML_Node&); //! @internal May be changed without notice in future versions -void setupElementaryReaction(ElementaryReaction&, const AnyMap&, +void setupElementaryReaction(ElementaryReaction2&, const AnyMap&, const Kinetics&); -void setupThreeBodyReaction(ThreeBodyReaction&, const XML_Node&); +void setupThreeBodyReaction(ThreeBodyReaction2&, const XML_Node&); //! @deprecated Cantera 2.6 (replaced by setParameters) -void setupThreeBodyReaction(ThreeBodyReaction&, const AnyMap&, +void setupThreeBodyReaction(ThreeBodyReaction2&, const AnyMap&, const Kinetics&); void setupFalloffReaction(FalloffReaction&, const XML_Node&); @@ -649,13 +662,13 @@ void setupFalloffReaction(FalloffReaction&, const AnyMap&, void setupChemicallyActivatedReaction(ChemicallyActivatedReaction&, const XML_Node&); -void setupPlogReaction(PlogReaction&, const XML_Node&); +void setupPlogReaction(PlogReaction2&, const XML_Node&); //! @deprecated Cantera 2.6 (replaced by setParameters) -void setupPlogReaction(PlogReaction&, const AnyMap&, const Kinetics&); +void setupPlogReaction(PlogReaction2&, const AnyMap&, const Kinetics&); -void setupChebyshevReaction(ChebyshevReaction&, const XML_Node&); +void setupChebyshevReaction(ChebyshevReaction2&, const XML_Node&); //! @deprecated Cantera 2.6 (replaced by setParameters) -void setupChebyshevReaction(ChebyshevReaction&, const AnyMap&, +void setupChebyshevReaction(ChebyshevReaction2&, const AnyMap&, const Kinetics&); void setupInterfaceReaction(InterfaceReaction&, const XML_Node&); diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index aa616a42c27..98ea6645af3 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -415,8 +415,8 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cbool allow_nonreactant_orders cbool allow_negative_orders - cdef cppclass CxxElementaryReaction "Cantera::ElementaryReaction" (CxxReaction): - CxxElementaryReaction() + cdef cppclass CxxElementaryReaction2 "Cantera::ElementaryReaction2" (CxxReaction): + CxxElementaryReaction2() CxxArrhenius rate cbool allow_negative_pre_exponential_factor @@ -427,8 +427,8 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": Composition efficiencies double default_efficiency - cdef cppclass CxxThreeBodyReaction "Cantera::ThreeBodyReaction" (CxxElementaryReaction): - CxxThreeBodyReaction() + cdef cppclass CxxThreeBodyReaction2 "Cantera::ThreeBodyReaction2" (CxxElementaryReaction2): + CxxThreeBodyReaction2() CxxThirdBody third_body cdef cppclass CxxFalloff "Cantera::Falloff": @@ -459,7 +459,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": void update_C(double*) double updateRC(double, double) - cdef cppclass CxxPlogReaction "Cantera::PlogReaction" (CxxReaction): + cdef cppclass CxxPlogReaction2 "Cantera::PlogReaction2" (CxxReaction): CxxPlog rate cdef cppclass CxxChebyshev "Cantera::Chebyshev": @@ -474,7 +474,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": void update_C(double*) double updateRC(double, double) - cdef cppclass CxxChebyshevReaction "Cantera::ChebyshevReaction" (CxxReaction): + cdef cppclass CxxChebyshevReaction2 "Cantera::ChebyshevReaction2" (CxxReaction): CxxChebyshev rate cdef cppclass CxxBlowersMasel "Cantera::BlowersMasel": @@ -498,7 +498,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": double E double m - cdef cppclass CxxInterfaceReaction "Cantera::InterfaceReaction" (CxxElementaryReaction): + cdef cppclass CxxInterfaceReaction "Cantera::InterfaceReaction" (CxxElementaryReaction2): stdmap[string, CxxCoverageDependency] coverage_deps cbool is_sticking_coefficient cbool use_motz_wise_correction diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index fb32730430e..1b4e6c4e717 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -777,10 +777,10 @@ cdef class ElementaryReaction(Reaction): property rate: """ Get/Set the `Arrhenius` rate coefficient for this reaction. """ def __get__(self): - cdef CxxElementaryReaction* r = self.reaction + cdef CxxElementaryReaction2* r = self.reaction return wrapArrhenius(&(r.rate), self) def __set__(self, Arrhenius rate): - cdef CxxElementaryReaction* r = self.reaction + cdef CxxElementaryReaction2* r = self.reaction r.rate = deref(rate.rate) property allow_negative_pre_exponential_factor: @@ -789,10 +789,10 @@ cdef class ElementaryReaction(Reaction): pre-exponential factor. """ def __get__(self): - cdef CxxElementaryReaction* r = self.reaction + cdef CxxElementaryReaction2* r = self.reaction return r.allow_negative_pre_exponential_factor def __set__(self, allow): - cdef CxxElementaryReaction* r = self.reaction + cdef CxxElementaryReaction2* r = self.reaction r.allow_negative_pre_exponential_factor = allow @@ -832,8 +832,8 @@ cdef class ThreeBodyReaction(ElementaryReaction): if isinstance(rate, Arrhenius): self.rate = rate - cdef CxxThreeBodyReaction* tbr(self): - return self.reaction + cdef CxxThreeBodyReaction2* tbr(self): + return self.reaction property efficiencies: """ @@ -1073,7 +1073,7 @@ cdef class PlogReaction(Reaction): list of (pressure, `Arrhenius`) tuples. """ def __get__(self): - cdef CxxPlogReaction* r = self.reaction + cdef CxxPlogReaction2* r = self.reaction rates = [] cdef vector[pair[double,CxxArrhenius]] cxxrates = r.rate.rates() cdef pair[double,CxxArrhenius] p_rate @@ -1090,11 +1090,11 @@ cdef class PlogReaction(Reaction): item.second = deref(rate.rate) ratemap.insert(item) - cdef CxxPlogReaction* r = self.reaction + cdef CxxPlogReaction2* r = self.reaction r.rate = CxxPlog(ratemap) def __call__(self, float T, float P): - cdef CxxPlogReaction* r = self.reaction + cdef CxxPlogReaction2* r = self.reaction cdef double logT = np.log(T) cdef double recipT = 1/T cdef double logP = np.log(P) @@ -1136,37 +1136,37 @@ cdef class ChebyshevReaction(Reaction): property Tmin: """ Minimum temperature [K] for the Chebyshev fit """ def __get__(self): - cdef CxxChebyshevReaction* r = self.reaction + cdef CxxChebyshevReaction2* r = self.reaction return r.rate.Tmin() property Tmax: """ Maximum temperature [K] for the Chebyshev fit """ def __get__(self): - cdef CxxChebyshevReaction* r = self.reaction + cdef CxxChebyshevReaction2* r = self.reaction return r.rate.Tmax() property Pmin: """ Minimum pressure [Pa] for the Chebyshev fit """ def __get__(self): - cdef CxxChebyshevReaction* r = self.reaction + cdef CxxChebyshevReaction2* r = self.reaction return r.rate.Pmin() property Pmax: """ Maximum pressure [Pa] for the Chebyshev fit """ def __get__(self): - cdef CxxChebyshevReaction* r = self.reaction + cdef CxxChebyshevReaction2* r = self.reaction return r.rate.Pmax() property nPressure: """ Number of pressures over which the Chebyshev fit is computed """ def __get__(self): - cdef CxxChebyshevReaction* r = self.reaction + cdef CxxChebyshevReaction2* r = self.reaction return r.rate.nPressure() property nTemperature: """ Number of temperatures over which the Chebyshev fit is computed """ def __get__(self): - cdef CxxChebyshevReaction* r = self.reaction + cdef CxxChebyshevReaction2* r = self.reaction return r.rate.nTemperature() property coeffs: @@ -1174,7 +1174,7 @@ cdef class ChebyshevReaction(Reaction): 2D array of Chebyshev coefficients of size `(nTemperature, nPressure)`. """ def __get__(self): - cdef CxxChebyshevReaction* r = self.reaction + cdef CxxChebyshevReaction2* r = self.reaction c = np.fromiter(r.rate.coeffs(), np.double) return c.reshape((r.rate.nTemperature(), r.rate.nPressure())) @@ -1183,7 +1183,7 @@ cdef class ChebyshevReaction(Reaction): Simultaneously set values for `Tmin`, `Tmax`, `Pmin`, `Pmax`, and `coeffs`. """ - cdef CxxChebyshevReaction* r = self.reaction + cdef CxxChebyshevReaction2* r = self.reaction cdef CxxArray2D data data.resize(len(coeffs), len(coeffs[0])) @@ -1197,7 +1197,7 @@ cdef class ChebyshevReaction(Reaction): r.rate = CxxChebyshev(Tmin, Tmax, Pmin, Pmax, data) def __call__(self, float T, float P): - cdef CxxChebyshevReaction* r = self.reaction + cdef CxxChebyshevReaction2* r = self.reaction cdef double logT = np.log(T) cdef double recipT = 1/T cdef double logP = np.log10(P) diff --git a/src/kinetics/BulkKinetics.cpp b/src/kinetics/BulkKinetics.cpp index f2aa2a85446..69616748721 100644 --- a/src/kinetics/BulkKinetics.cpp +++ b/src/kinetics/BulkKinetics.cpp @@ -181,7 +181,7 @@ void BulkKinetics::addThirdBody(shared_ptr r) m_multi_indices.push_back(nReactions() - 1); } -void BulkKinetics::addElementaryReaction(ElementaryReaction& r) +void BulkKinetics::addElementaryReaction(ElementaryReaction2& r) { m_rates.install(nReactions()-1, r.rate); } @@ -206,7 +206,7 @@ void BulkKinetics::modifyReaction(size_t i, shared_ptr rNew) } } -void BulkKinetics::modifyElementaryReaction(size_t i, ElementaryReaction& rNew) +void BulkKinetics::modifyElementaryReaction(size_t i, ElementaryReaction2& rNew) { m_rates.replace(i, rNew.rate); } diff --git a/src/kinetics/GasKinetics.cpp b/src/kinetics/GasKinetics.cpp index 0e7eeb90576..6942a0cdd58 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -267,17 +267,17 @@ bool GasKinetics::addReaction(shared_ptr r) } if (r->type() == "elementary-legacy") { - addElementaryReaction(dynamic_cast(*r)); + addElementaryReaction(dynamic_cast(*r)); } else if (r->type() == "three-body-legacy") { - addThreeBodyReaction(dynamic_cast(*r)); + 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-legacy") { - addPlogReaction(dynamic_cast(*r)); + addPlogReaction(dynamic_cast(*r)); } else if (r->type() == "Chebyshev-legacy") { - addChebyshevReaction(dynamic_cast(*r)); + addChebyshevReaction(dynamic_cast(*r)); } else if (r->type() == "Blowers-Masel") { addBlowersMaselReaction(dynamic_cast(*r)); } else { @@ -318,7 +318,7 @@ void GasKinetics::addFalloffReaction(FalloffReaction& r) falloff_work.resize(m_falloffn.workSize()); } -void GasKinetics::addThreeBodyReaction(ThreeBodyReaction& r) +void GasKinetics::addThreeBodyReaction(ThreeBodyReaction2& r) { m_rates.install(nReactions()-1, r.rate); map efficiencies; @@ -333,12 +333,12 @@ void GasKinetics::addThreeBodyReaction(ThreeBodyReaction& r) concm_3b_values.resize(m_3b_concm.workSize()); } -void GasKinetics::addPlogReaction(PlogReaction& r) +void GasKinetics::addPlogReaction(PlogReaction2& r) { m_plog_rates.install(nReactions()-1, r.rate); } -void GasKinetics::addChebyshevReaction(ChebyshevReaction& r) +void GasKinetics::addChebyshevReaction(ChebyshevReaction2& r) { m_cheb_rates.install(nReactions()-1, r.rate); } @@ -359,17 +359,17 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) } if (rNew->type() == "elementary-legacy") { - modifyElementaryReaction(i, dynamic_cast(*rNew)); + modifyElementaryReaction(i, dynamic_cast(*rNew)); } else if (rNew->type() == "three-body-legacy") { - modifyThreeBodyReaction(i, dynamic_cast(*rNew)); + 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-legacy") { - modifyPlogReaction(i, dynamic_cast(*rNew)); + modifyPlogReaction(i, dynamic_cast(*rNew)); } else if (rNew->type() == "Chebyshev-legacy") { - modifyChebyshevReaction(i, dynamic_cast(*rNew)); + modifyChebyshevReaction(i, dynamic_cast(*rNew)); } else if (rNew->type() == "Blowers-Masel") { modifyBlowersMaselReaction(i, dynamic_cast(*rNew)); } else { @@ -383,7 +383,7 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) m_pres += 0.1234; } -void GasKinetics::modifyThreeBodyReaction(size_t i, ThreeBodyReaction& r) +void GasKinetics::modifyThreeBodyReaction(size_t i, ThreeBodyReaction2& r) { m_rates.replace(i, r.rate); } @@ -396,12 +396,12 @@ void GasKinetics::modifyFalloffReaction(size_t i, FalloffReaction& r) m_falloffn.replace(iFall, r.falloff); } -void GasKinetics::modifyPlogReaction(size_t i, PlogReaction& r) +void GasKinetics::modifyPlogReaction(size_t i, PlogReaction2& r) { m_plog_rates.replace(i, r.rate); } -void GasKinetics::modifyChebyshevReaction(size_t i, ChebyshevReaction& r) +void GasKinetics::modifyChebyshevReaction(size_t i, ChebyshevReaction2& r) { m_cheb_rates.replace(i, r.rate); } diff --git a/src/kinetics/Kinetics.cpp b/src/kinetics/Kinetics.cpp index 622773be6b0..92b13370e2f 100644 --- a/src/kinetics/Kinetics.cpp +++ b/src/kinetics/Kinetics.cpp @@ -160,8 +160,8 @@ std::pair Kinetics::checkDuplicates(bool throw_err) const continue; // No overlap in third body efficiencies } } else if (R.type() == "three-body-legacy") { - ThirdBody& tb1 = dynamic_cast(R).third_body; - ThirdBody& tb2 = dynamic_cast(other).third_body; + ThirdBody& tb1 = dynamic_cast(R).third_body; + ThirdBody& tb2 = dynamic_cast(other).third_body; bool thirdBodyOk = true; for (size_t k = 0; k < nTotalSpecies(); k++) { string s = kineticsSpeciesName(k); diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index e4b0eded8cc..79be1f68cd9 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -343,9 +343,9 @@ bool Reaction::checkSpecies(const Kinetics& kin) const return true; } -ElementaryReaction::ElementaryReaction(const Composition& reactants_, - const Composition products_, - const Arrhenius& rate_) +ElementaryReaction2::ElementaryReaction2(const Composition& reactants_, + const Composition products_, + const Arrhenius& rate_) : Reaction(reactants_, products_) , rate(rate_) , allow_negative_pre_exponential_factor(false) @@ -353,25 +353,25 @@ ElementaryReaction::ElementaryReaction(const Composition& reactants_, reaction_type = ELEMENTARY_RXN; } -ElementaryReaction::ElementaryReaction() +ElementaryReaction2::ElementaryReaction2() : Reaction() , allow_negative_pre_exponential_factor(false) { reaction_type = ELEMENTARY_RXN; } -void ElementaryReaction::validate() +void ElementaryReaction2::validate() { Reaction::validate(); if (!allow_negative_pre_exponential_factor && rate.preExponentialFactor() < 0) { - throw InputFileError("ElementaryReaction::validate", input, + throw InputFileError("ElementaryReaction2::validate", input, "Undeclared negative pre-exponential factor found in reaction '" + equation() + "'"); } } -void ElementaryReaction::getParameters(AnyMap& reactionNode) const +void ElementaryReaction2::getParameters(AnyMap& reactionNode) const { Reaction::getParameters(reactionNode); if (allow_negative_pre_exponential_factor) { @@ -406,44 +406,44 @@ double ThirdBody::efficiency(const std::string& k) const return getValue(efficiencies, k, default_efficiency); } -ThreeBodyReaction::ThreeBodyReaction() +ThreeBodyReaction2::ThreeBodyReaction2() { reaction_type = THREE_BODY_RXN; } -ThreeBodyReaction::ThreeBodyReaction(const Composition& reactants_, - const Composition& products_, - const Arrhenius& rate_, - const ThirdBody& tbody) - : ElementaryReaction(reactants_, products_, rate_) +ThreeBodyReaction2::ThreeBodyReaction2(const Composition& reactants_, + const Composition& products_, + const Arrhenius& rate_, + const ThirdBody& tbody) + : ElementaryReaction2(reactants_, products_, rate_) , third_body(tbody) { reaction_type = THREE_BODY_RXN; } -std::string ThreeBodyReaction::reactantString() const +std::string ThreeBodyReaction2::reactantString() const { if (specified_collision_partner) { - return ElementaryReaction::reactantString() + " + " + return ElementaryReaction2::reactantString() + " + " + third_body.efficiencies.begin()->first; } else { - return ElementaryReaction::reactantString() + " + M"; + return ElementaryReaction2::reactantString() + " + M"; } } -std::string ThreeBodyReaction::productString() const +std::string ThreeBodyReaction2::productString() const { if (specified_collision_partner) { - return ElementaryReaction::productString() + " + " + return ElementaryReaction2::productString() + " + " + third_body.efficiencies.begin()->first; } else { - return ElementaryReaction::productString() + " + M"; + return ElementaryReaction2::productString() + " + M"; } } -void ThreeBodyReaction::calculateRateCoeffUnits(const Kinetics& kin) +void ThreeBodyReaction2::calculateRateCoeffUnits(const Kinetics& kin) { - ElementaryReaction::calculateRateCoeffUnits(kin); + ElementaryReaction2::calculateRateCoeffUnits(kin); bool specified_collision_partner_ = false; for (const auto& reac : reactants) { // While this reaction was already identified as a three-body reaction in a @@ -462,9 +462,9 @@ void ThreeBodyReaction::calculateRateCoeffUnits(const Kinetics& kin) } } -void ThreeBodyReaction::getParameters(AnyMap& reactionNode) const +void ThreeBodyReaction2::getParameters(AnyMap& reactionNode) const { - ElementaryReaction::getParameters(reactionNode); + ElementaryReaction2::getParameters(reactionNode); if (!specified_collision_partner) { reactionNode["type"] = "three-body"; reactionNode["efficiencies"] = third_body.efficiencies; @@ -475,7 +475,7 @@ void ThreeBodyReaction::getParameters(AnyMap& reactionNode) const } } -std::pair, bool> ThreeBodyReaction::undeclaredThirdBodies( +std::pair, bool> ThreeBodyReaction2::undeclaredThirdBodies( const Kinetics& kin) const { std::vector undeclared; @@ -608,43 +608,43 @@ void ChemicallyActivatedReaction::getParameters(AnyMap& reactionNode) const reactionNode["type"] = "chemically-activated"; } -PlogReaction::PlogReaction() +PlogReaction2::PlogReaction2() : Reaction() { reaction_type = PLOG_RXN; } -PlogReaction::PlogReaction(const Composition& reactants_, - const Composition& products_, const Plog& rate_) +PlogReaction2::PlogReaction2(const Composition& reactants_, + const Composition& products_, const Plog& rate_) : Reaction(reactants_, products_) , rate(rate_) { reaction_type = PLOG_RXN; } -void PlogReaction::getParameters(AnyMap& reactionNode) const +void PlogReaction2::getParameters(AnyMap& reactionNode) const { Reaction::getParameters(reactionNode); reactionNode["type"] = "pressure-dependent-Arrhenius"; rate.getParameters(reactionNode, rate_units); } -ChebyshevReaction::ChebyshevReaction() +ChebyshevReaction2::ChebyshevReaction2() : Reaction() { reaction_type = CHEBYSHEV_RXN; } -ChebyshevReaction::ChebyshevReaction(const Composition& reactants_, - const Composition& products_, - const Chebyshev& rate_) +ChebyshevReaction2::ChebyshevReaction2(const Composition& reactants_, + const Composition& products_, + const Chebyshev& rate_) : Reaction(reactants_, products_) , rate(rate_) { reaction_type = CHEBYSHEV_RXN; } -void ChebyshevReaction::getParameters(AnyMap& reactionNode) const +void ChebyshevReaction2::getParameters(AnyMap& reactionNode) const { Reaction::getParameters(reactionNode); reactionNode["type"] = "Chebyshev"; @@ -662,7 +662,7 @@ InterfaceReaction::InterfaceReaction(const Composition& reactants_, const Composition& products_, const Arrhenius& rate_, bool isStick) - : ElementaryReaction(reactants_, products_, rate_) + : ElementaryReaction2(reactants_, products_, rate_) , is_sticking_coefficient(isStick) , use_motz_wise_correction(false) { @@ -671,7 +671,7 @@ InterfaceReaction::InterfaceReaction(const Composition& reactants_, void InterfaceReaction::calculateRateCoeffUnits(const Kinetics& kin) { - ElementaryReaction::calculateRateCoeffUnits(kin); + ElementaryReaction2::calculateRateCoeffUnits(kin); if (is_sticking_coefficient || input.hasKey("sticking-coefficient")) { rate_units = Units(1.0); // sticking coefficients are dimensionless } @@ -679,7 +679,7 @@ void InterfaceReaction::calculateRateCoeffUnits(const Kinetics& kin) void InterfaceReaction::getParameters(AnyMap& reactionNode) const { - ElementaryReaction::getParameters(reactionNode); + ElementaryReaction2::getParameters(reactionNode); if (is_sticking_coefficient) { reactionNode["sticking-coefficient"] = std::move(reactionNode["rate-constant"]); reactionNode.erase("rate-constant"); @@ -1286,7 +1286,7 @@ BlowersMasel readBlowersMasel(const Reaction& R, const AnyValue& rate, return BlowersMasel(A, b, Ta0, w); } -bool detectEfficiencies(ThreeBodyReaction& R) +bool detectEfficiencies(ThreeBodyReaction2& R) { for (const auto& reac : R.reactants) { // detect explicitly specified collision partner @@ -1433,7 +1433,7 @@ void setupReaction(Reaction& R, const AnyMap& node, const Kinetics& kin) R.calculateRateCoeffUnits(kin); } -void setupElementaryReaction(ElementaryReaction& R, const XML_Node& rxn_node) +void setupElementaryReaction(ElementaryReaction2& R, const XML_Node& rxn_node) { const XML_Node& rc_node = rxn_node.child("rateCoeff"); if (rc_node.hasChild("Arrhenius")) { @@ -1455,7 +1455,7 @@ void setupElementaryReaction(ElementaryReaction& R, const XML_Node& rxn_node) setupReaction(R, rxn_node); } -void setupElementaryReaction(ElementaryReaction& R, const AnyMap& node, +void setupElementaryReaction(ElementaryReaction2& R, const AnyMap& node, const Kinetics& kin) { setupReaction(R, node, kin); @@ -1463,7 +1463,7 @@ void setupElementaryReaction(ElementaryReaction& R, const AnyMap& node, R.rate = readArrhenius(R, node["rate-constant"], kin, node.units()); } -void setupThreeBodyReaction(ThreeBodyReaction& R, const XML_Node& rxn_node) +void setupThreeBodyReaction(ThreeBodyReaction2& R, const XML_Node& rxn_node) { readEfficiencies(R.third_body, rxn_node.child("rateCoeff")); setupElementaryReaction(R, rxn_node); @@ -1472,7 +1472,7 @@ void setupThreeBodyReaction(ThreeBodyReaction& R, const XML_Node& rxn_node) } } -void setupThreeBodyReaction(ThreeBodyReaction& R, const AnyMap& node, +void setupThreeBodyReaction(ThreeBodyReaction2& R, const AnyMap& node, const Kinetics& kin) { setupElementaryReaction(R, node, kin); @@ -1595,7 +1595,7 @@ void setupChemicallyActivatedReaction(ChemicallyActivatedReaction& R, setupReaction(R, rxn_node); } -void setupPlogReaction(PlogReaction& R, const XML_Node& rxn_node) +void setupPlogReaction(PlogReaction2& R, const XML_Node& rxn_node) { XML_Node& rc = rxn_node.child("rateCoeff"); std::multimap rates; @@ -1607,7 +1607,7 @@ void setupPlogReaction(PlogReaction& R, const XML_Node& rxn_node) setupReaction(R, rxn_node); } -void setupPlogReaction(PlogReaction& R, const AnyMap& node, const Kinetics& kin) +void setupPlogReaction(PlogReaction2& R, const AnyMap& node, const Kinetics& kin) { setupReaction(R, node, kin); std::multimap rates; @@ -1618,13 +1618,13 @@ void setupPlogReaction(PlogReaction& R, const AnyMap& node, const Kinetics& kin) R.rate = Plog(rates); } -void PlogReaction::validate() +void PlogReaction2::validate() { Reaction::validate(); rate.validate(equation()); } -void setupChebyshevReaction(ChebyshevReaction& R, const XML_Node& rxn_node) +void setupChebyshevReaction(ChebyshevReaction2& R, const XML_Node& rxn_node) { XML_Node& rc = rxn_node.child("rateCoeff"); const XML_Node& coeff_node = rc.child("floatArray"); @@ -1647,7 +1647,7 @@ void setupChebyshevReaction(ChebyshevReaction& R, const XML_Node& rxn_node) setupReaction(R, rxn_node); } -void setupChebyshevReaction(ChebyshevReaction&R, const AnyMap& node, +void setupChebyshevReaction(ChebyshevReaction2&R, const AnyMap& node, const Kinetics& kin) { setupReaction(R, node, kin); diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 25e2a72e8b0..1a50fc9ddaf 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -30,9 +30,9 @@ ReactionFactory::ReactionFactory() // register elementary reactions (old framework) reg("elementary-legacy", [](const AnyMap& node, const Kinetics& kin) { - Reaction* R = new ElementaryReaction(); + Reaction* R = new ElementaryReaction2(); if (!node.empty()) { - setupElementaryReaction(*(ElementaryReaction*)R, node, kin); + setupElementaryReaction(*(ElementaryReaction2*)R, node, kin); } return R; }); @@ -46,9 +46,9 @@ ReactionFactory::ReactionFactory() // register three-body reactions (old framework) reg("three-body-legacy", [](const AnyMap& node, const Kinetics& kin) { - Reaction* R = new ThreeBodyReaction(); + Reaction* R = new ThreeBodyReaction2(); if (!node.empty()) { - setupThreeBodyReaction(*(ThreeBodyReaction*)R, node, kin); + setupThreeBodyReaction(*(ThreeBodyReaction2*)R, node, kin); } return R; }); @@ -82,9 +82,9 @@ ReactionFactory::ReactionFactory() // register pressure-dependent-Arrhenius reactions (old framework) reg("pressure-dependent-Arrhenius-legacy", [](const AnyMap& node, const Kinetics& kin) { - Reaction* R = new PlogReaction(); + Reaction* R = new PlogReaction2(); if (!node.empty()) { - setupPlogReaction(*(PlogReaction*)R, node, kin); + setupPlogReaction(*(PlogReaction2*)R, node, kin); } return R; }); @@ -95,9 +95,9 @@ ReactionFactory::ReactionFactory() }); addAlias("Chebyshev", "chebyshev"); reg("Chebyshev-legacy", [](const AnyMap& node, const Kinetics& kin) { - Reaction* R = new ChebyshevReaction(); + Reaction* R = new ChebyshevReaction2(); if (!node.empty()) { - setupChebyshevReaction(*(ChebyshevReaction*)R, node, kin); + setupChebyshevReaction(*(ChebyshevReaction2*)R, node, kin); } return R; }); @@ -153,8 +153,8 @@ ReactionFactoryXML::ReactionFactoryXML() { // register elementary reactions reg("elementary-legacy", [](const XML_Node& node) { - Reaction* R = new ElementaryReaction(); - setupElementaryReaction(*(ElementaryReaction*)R, node); + Reaction* R = new ElementaryReaction2(); + setupElementaryReaction(*(ElementaryReaction2*)R, node); return R; }); addAlias("elementary-legacy", "elementary"); @@ -163,8 +163,8 @@ ReactionFactoryXML::ReactionFactoryXML() // register three-body reactions reg("three-body-legacy", [](const XML_Node& node) { - Reaction* R = new ThreeBodyReaction(); - setupThreeBodyReaction(*(ThreeBodyReaction*)R, node); + Reaction* R = new ThreeBodyReaction2(); + setupThreeBodyReaction(*(ThreeBodyReaction2*)R, node); return R; }); addAlias("three-body-legacy", "three-body"); @@ -189,8 +189,8 @@ ReactionFactoryXML::ReactionFactoryXML() // register pressure-depdendent-Arrhenius reactions reg("pressure-dependent-Arrhenius-legacy", [](const XML_Node& node) { - Reaction* R = new PlogReaction(); - setupPlogReaction(*(PlogReaction*)R, node); + Reaction* R = new PlogReaction2(); + setupPlogReaction(*(PlogReaction2*)R, node); return R; }); addAlias("pressure-dependent-Arrhenius-legacy", "pressure-dependent-Arrhenius"); @@ -199,8 +199,8 @@ ReactionFactoryXML::ReactionFactoryXML() // register Chebyshev reactions reg("Chebyshev-legacy", [](const XML_Node& node) { - Reaction* R = new ChebyshevReaction(); - setupChebyshevReaction(*(ChebyshevReaction*)R, node); + Reaction* R = new ChebyshevReaction2(); + setupChebyshevReaction(*(ChebyshevReaction2*)R, node); return R; }); addAlias("Chebyshev-legacy", "chebyshev"); @@ -315,7 +315,7 @@ unique_ptr newReaction(const XML_Node& rxn_node) if (type.empty()) { // Reaction type is not specified // See if this is a three-body reaction with a specified collision partner - ElementaryReaction testReaction; + ElementaryReaction2 testReaction; setupReaction(testReaction, rxn_node); if (isThreeBody(testReaction)) { type = "three-body"; @@ -333,7 +333,7 @@ unique_ptr newReaction(const AnyMap& rxn_node, const Kinetics& kin) } else if (kin.thermo().nDim() == 3) { // Reaction type is not specified // See if this is a three-body reaction with a specified collision partner - ElementaryReaction testReaction; + ElementaryReaction2 testReaction; parseReactionEquation(testReaction, rxn_node["equation"], kin); if (isThreeBody(testReaction)) { type = "three-body"; @@ -343,7 +343,7 @@ unique_ptr newReaction(const AnyMap& rxn_node, const Kinetics& kin) if (kin.thermo().nDim() < 3 && type == "elementary") { // See if this is an electrochemical reaction: type of // receiving reaction object is unimportant in this case - ElementaryReaction testReaction; + ElementaryReaction2 testReaction; parseReactionEquation(testReaction, rxn_node["equation"], kin); if (isElectrochemicalReaction(testReaction, kin)) { type = "electrochemical"; diff --git a/src/kinetics/RxnRates.cpp b/src/kinetics/RxnRates.cpp index 1d4008e32f2..892821dc8ec 100644 --- a/src/kinetics/RxnRates.cpp +++ b/src/kinetics/RxnRates.cpp @@ -370,7 +370,7 @@ void Chebyshev::getParameters(AnyMap& rateNode, const Units& rate_units) const if (rate_units2.factor() != 0.0) { coeffs.asVector()[0][0] += std::log10(units.convertFrom(1.0, rate_units2)); } else if (units.getDelta(UnitSystem()).size()) { - throw CanteraError("ChebyshevReaction::getParameters lambda", + throw CanteraError("Chebyshev::getParameters lambda", "Cannot convert rate constant with unknown dimensions to a " "non-default unit system"); } diff --git a/test/kinetics/kineticsFromScratch.cpp b/test/kinetics/kineticsFromScratch.cpp index e972d4ba204..291af13dda9 100644 --- a/test/kinetics/kineticsFromScratch.cpp +++ b/test/kinetics/kineticsFromScratch.cpp @@ -57,7 +57,7 @@ TEST_F(KineticsFromScratch, add_elementary_reaction) Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); Arrhenius rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); kin.addReaction(R); check_rates(0); @@ -73,7 +73,7 @@ TEST_F(KineticsFromScratch, add_three_body_reaction) Arrhenius rate(1.2e11, -1.0, 0.0); ThirdBody tbody; tbody.efficiencies = parseCompString("AR:0.83 H2:2.4 H2O:15.4"); - auto R = make_shared(reac, prod, rate, tbody); + auto R = make_shared(reac, prod, rate, tbody); kin.addReaction(R); check_rates(1); @@ -86,7 +86,7 @@ TEST_F(KineticsFromScratch, undefined_third_body) Arrhenius rate(1.2e11, -1.0, 0.0); ThirdBody tbody; tbody.efficiencies = parseCompString("H2:0.1 CO2:0.83"); - auto R = make_shared(reac, prod, rate, tbody); + auto R = make_shared(reac, prod, rate, tbody); ASSERT_THROW(kin.addReaction(R), CanteraError); } @@ -98,7 +98,7 @@ TEST_F(KineticsFromScratch, skip_undefined_third_body) Arrhenius rate(1.2e11, -1.0, 0.0); ThirdBody tbody; tbody.efficiencies = parseCompString("H2:0.1 CO2:0.83"); - auto R = make_shared(reac, prod, rate, tbody); + auto R = make_shared(reac, prod, rate, tbody); kin.skipUndeclaredThirdBodies(true); kin.addReaction(R); @@ -144,7 +144,7 @@ TEST_F(KineticsFromScratch, add_plog_reaction) { 100.0*101325, Arrhenius(5.963200e+56, -11.529, 52599.6 / GasConst_cal_mol_K) } }; - auto R = make_shared(reac, prod, Plog(rates)); + auto R = make_shared(reac, prod, Plog(rates)); kin.addReaction(R); check_rates(3); } @@ -160,7 +160,7 @@ TEST_F(KineticsFromScratch, plog_invalid_rate) { 100.0*101325, Arrhenius(5.9632e+56, -11.529, 52599.6 / GasConst_cal_mol_K) } }; - auto R = make_shared(reac, prod, Plog(rates)); + auto R = make_shared(reac, prod, Plog(rates)); ASSERT_THROW(kin.addReaction(R), CanteraError); } @@ -191,7 +191,7 @@ TEST_F(KineticsFromScratch, add_chebyshev_reaction) coeffs(2,3) = -7.6385e-03; Chebyshev rate(290, 3000, 1000.0, 10000000.0, coeffs); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); kin.addReaction(R); check_rates(4); } @@ -201,7 +201,7 @@ TEST_F(KineticsFromScratch, undeclared_species) Composition reac = parseCompString("CO:1 OH:1"); Composition prod = parseCompString("CO2:1 H:1"); Arrhenius rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); ASSERT_THROW(kin.addReaction(R), CanteraError); ASSERT_EQ((size_t) 0, kin.nReactions()); @@ -212,7 +212,7 @@ TEST_F(KineticsFromScratch, skip_undeclared_species) Composition reac = parseCompString("CO:1 OH:1"); Composition prod = parseCompString("CO2:1 H:1"); Arrhenius rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); kin.skipUndeclaredSpecies(true); kin.addReaction(R); @@ -224,7 +224,7 @@ TEST_F(KineticsFromScratch, negative_A_error) Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); Arrhenius rate(-3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); ASSERT_THROW(kin.addReaction(R), CanteraError); ASSERT_EQ((size_t) 0, kin.nReactions()); @@ -235,7 +235,7 @@ TEST_F(KineticsFromScratch, allow_negative_A) Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); Arrhenius rate(-3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); R->allow_negative_pre_exponential_factor = true; kin.addReaction(R); @@ -247,7 +247,7 @@ TEST_F(KineticsFromScratch, invalid_reversible_with_orders) Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); Arrhenius rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); R->orders["H2"] = 0.5; ASSERT_THROW(kin.addReaction(R), CanteraError); @@ -259,7 +259,7 @@ TEST_F(KineticsFromScratch, negative_order_override) Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); Arrhenius rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); R->reversible = false; R->allow_negative_orders = true; R->orders["H2"] = - 0.5; @@ -273,7 +273,7 @@ TEST_F(KineticsFromScratch, invalid_negative_orders) Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); Arrhenius rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); R->reversible = false; R->orders["H2"] = - 0.5; @@ -286,7 +286,7 @@ TEST_F(KineticsFromScratch, nonreactant_order_override) Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); Arrhenius rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); R->reversible = false; R->allow_nonreactant_orders = true; R->orders["OH"] = 0.5; @@ -300,7 +300,7 @@ TEST_F(KineticsFromScratch, invalid_nonreactant_order) Composition reac = parseCompString("O:1 H2:1"); Composition prod = parseCompString("H:1 OH:1"); Arrhenius rate(3.87e1, 2.7, 6260.0 / GasConst_cal_mol_K); - auto R = make_shared(reac, prod, rate); + auto R = make_shared(reac, prod, rate); R->reversible = false; R->orders["OH"] = 0.5; diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index ae68a4d621a..22a0eb6bee2 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -66,7 +66,7 @@ TEST(Reaction, ElementaryFromYaml2) EXPECT_EQ(R->products.at("N2"), 1); EXPECT_EQ(R->type(), "elementary-legacy"); - auto ER = dynamic_cast(*R); + auto ER = dynamic_cast(*R); EXPECT_DOUBLE_EQ(ER.rate.preExponentialFactor(), -2.7e10); EXPECT_DOUBLE_EQ(ER.rate.activationEnergy_R(), 355 / GasConst_cal_mol_K); EXPECT_TRUE(ER.allow_negative_pre_exponential_factor); @@ -117,7 +117,7 @@ TEST(Reaction, ThreeBodyFromYaml3) EXPECT_EQ(R->reactants.count("M"), (size_t) 0); EXPECT_EQ(R->type(), "three-body-legacy"); - auto TBR = dynamic_cast(*R); + auto TBR = dynamic_cast(*R); EXPECT_DOUBLE_EQ(TBR.rate.preExponentialFactor(), 1.2e11); EXPECT_DOUBLE_EQ(TBR.third_body.efficiencies["H2O"], 5.0); EXPECT_DOUBLE_EQ(TBR.third_body.default_efficiency, 1.0); @@ -588,9 +588,9 @@ TEST_F(ReactionToYaml, electrochemical) TEST_F(ReactionToYaml, unconvertible1) { - ElementaryReaction R({{"H2", 1}, {"OH", 1}}, - {{"H2O", 1}, {"H", 1}}, - Arrhenius(1e5, -1.0, 12.5)); + ElementaryReaction2 R({{"H2", 1}, {"OH", 1}}, + {{"H2O", 1}, {"H", 1}}, + Arrhenius(1e5, -1.0, 12.5)); AnyMap params = R.parameters(); UnitSystem U{"g", "cm", "mol"}; params.setUnits(U); @@ -600,9 +600,9 @@ TEST_F(ReactionToYaml, unconvertible1) TEST_F(ReactionToYaml, unconvertible2) { Array2D coeffs(2, 2, 1.0); - ChebyshevReaction R({{"H2", 1}, {"OH", 1}}, - {{"H2O", 1}, {"H", 1}}, - Chebyshev(273, 3000, 1e2, 1e7, coeffs)); + ChebyshevReaction2 R({{"H2", 1}, {"OH", 1}}, + {{"H2O", 1}, {"H", 1}}, + Chebyshev(273, 3000, 1e2, 1e7, coeffs)); UnitSystem U{"g", "cm", "mol"}; AnyMap params = R.parameters(); params.setUnits(U); From 3773f6a4eabfe7e23b8b7981200fc1ae4a765600 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 18 May 2021 07:26:31 -0500 Subject: [PATCH 71/84] [Kinetics] Use numbered *Reaction2 labels for old Python framework --- .../examples/kinetics/blowers_masel.py | 2 +- interfaces/cython/cantera/reaction.pyx | 78 +++++------- .../cython/cantera/test/test_convert.py | 8 +- .../cython/cantera/test/test_kinetics.py | 46 +++---- .../cython/cantera/test/test_reaction.py | 113 +++++++++--------- 5 files changed, 119 insertions(+), 128 deletions(-) diff --git a/interfaces/cython/cantera/examples/kinetics/blowers_masel.py b/interfaces/cython/cantera/examples/kinetics/blowers_masel.py index 90db4174c69..de7fff54d94 100644 --- a/interfaces/cython/cantera/examples/kinetics/blowers_masel.py +++ b/interfaces/cython/cantera/examples/kinetics/blowers_masel.py @@ -24,7 +24,7 @@ #Create an elementary reaction O+H2<=>H+OH r1 = ct.ElementaryReaction({'O':1, 'H2':1}, {'H':1, 'OH':1}) -r1.rate = ct.Arrhenius(3.87e1, 2.7, 6260*1000*4.184) +r1.rate = ct.ArrheniusRate(3.87e1, 2.7, 6260*1000*4.184) #Create a Blowers-Masel reaction O+H2<=>H+OH r2 = ct.BlowersMaselReaction({'O':1, 'H2':1}, {'H':1, 'OH':1}) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 1b4e6c4e717..2b1a455addf 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -735,22 +735,22 @@ cdef copyArrhenius(CxxArrhenius* rate): return r -cdef class ElementaryReaction(Reaction): +cdef class ElementaryReaction2(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:: + An example for the definition of a `ElementaryReaction2` object is given as:: - rxn = ElementaryReaction(equation='H2 + O <=> H + OH', - rate={'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07}, - kinetics=gas) + rxn = ElementaryReaction2(equation='H2 + O <=> H + OH', + rate={'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07}, + kinetics=gas) .. deprecated:: 2.6 - This class is superseded by `ElementaryReaction3` and only used by XML. - The implementation of this reaction type will change after Cantera 2.6; - refer to `ElementaryReaction3` for new behavior. + This class is replaced by `ElementaryReaction` and only used by CTI/XML. The + implementation uses the legacy framework for reaction rate evaluations used + by Cantera 2.5.1 and earlier. Refer to `ElementaryReaction` for new behavior. """ reaction_type = "elementary-legacy" @@ -796,16 +796,16 @@ cdef class ElementaryReaction(Reaction): r.allow_negative_pre_exponential_factor = allow -cdef class ThreeBodyReaction(ElementaryReaction): +cdef class ThreeBodyReaction2(ElementaryReaction2): """ A reaction with a non-reacting third body "M" that acts to add or remove energy from the reacting species. .. deprecated:: 2.6 - This class is superseded by `ThreeBodyReaction3` and only used by XML. - The implementation of this reaction type will change after Cantera 2.6; - refer to `ThreeBodyReaction3` for new behavior. + This class is replaced by `ThreeBodyReaction` and only used by CTI/XML. The + implementation uses the legacy framework for reaction rate evaluations used + by Cantera 2.5.1 and earlier. Refer to `ThreeBodyReaction` for new behavior. """ reaction_type = "three-body-legacy" @@ -1039,16 +1039,16 @@ cdef class ChemicallyActivatedReaction(FalloffReaction): reaction_type = "chemically-activated" -cdef class PlogReaction(Reaction): +cdef class PlogReaction2(Reaction): """ A pressure-dependent reaction parameterized by logarithmically interpolating between Arrhenius rate expressions at various pressures. .. deprecated:: 2.6 - This class is superseded by `PlogReaction3` and only used by XML. - The implementation of this reaction type will change after Cantera 2.6; - refer to `PlogReaction3` for new behavior. + This class is replaced by `PlogReaction` and only used by CTI/XML. The + implementation uses the legacy framework for reaction rate evaluations used + by Cantera 2.5.1 and earlier. Refer to `PlogReaction` for new behavior. """ reaction_type = "pressure-dependent-Arrhenius-legacy" @@ -1103,16 +1103,16 @@ cdef class PlogReaction(Reaction): return r.rate.updateRC(logT, recipT) -cdef class ChebyshevReaction(Reaction): +cdef class ChebyshevReaction2(Reaction): """ A pressure-dependent reaction parameterized by a bivariate Chebyshev polynomial in temperature and pressure. .. deprecated:: 2.6 - This class is superseded by `ChebyshevReaction3` and only used by XML. - The implementation of this reaction type will change after Cantera 2.6; - refer to `ChebyshevReaction3` for new behavior. + This class is replaced by `ChebyshevReaction` and only used by CTI/XML. The + implementation uses the legacy framework for reaction rate evaluations used + by Cantera 2.5.1 and earlier. Refer to `ChebyshevReaction` for new behavior. """ reaction_type = "Chebyshev-legacy" @@ -1310,7 +1310,7 @@ cdef class BlowersMaselReaction(Reaction): r.allow_negative_pre_exponential_factor = allow -cdef class InterfaceReaction(ElementaryReaction): +cdef class InterfaceReaction(ElementaryReaction2): """ A reaction occurring on an `Interface` (i.e. a surface or an edge) """ reaction_type = "interface" @@ -1462,14 +1462,14 @@ cdef class Reaction3(Reaction): ).format(what.capitalize(), attr, what, type(self.rate).__name__) -cdef class ElementaryReaction3(Reaction3): +cdef class ElementaryReaction(Reaction3): """ A reaction which follows mass-action kinetics with a modified Arrhenius reaction rate. - An example for the definition of an `ElementaryReaction3` object is given as:: + An example for the definition of an `ElementaryReaction` object is given as:: - rxn = ElementaryReaction3( + rxn = ElementaryReaction( equation='O + H2 <=> H + OH', rate={'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07}, kinetics=gas) @@ -1478,9 +1478,6 @@ cdef class ElementaryReaction3(Reaction3): equation: O + H2 <=> H + OH rate-constant: {A: 3.87e+04 cm^3/mol/s, b: 2.7, Ea: 6260.0 cal/mol} - - This class is a replacement for `ElementaryReaction` and cannot be - instantiated from XML. It is the default for import from YAML. """ reaction_type = "elementary" @@ -1535,14 +1532,14 @@ cdef class ElementaryReaction3(Reaction3): self.rate.allow_negative_pre_exponential_factor = allow -cdef class ThreeBodyReaction3(ElementaryReaction3): +cdef class ThreeBodyReaction(ElementaryReaction): """ A reaction with a non-reacting third body "M" that acts to add or remove energy from the reacting species. - An example for the definition of an `ThreeBodyReaction3` object is given as:: + An example for the definition of an `ThreeBodyReaction` object is given as:: - rxn = ThreeBodyReaction3( + rxn = ThreeBodyReaction( equation='2 O + M <=> O2 + M', type='three-body', rate={'A': 1.2e+17, 'b': -1.0, 'Ea': 0.0}, @@ -1555,9 +1552,6 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): type: three-body rate-constant: {A: 1.2e+17 cm^6/mol^2/s, b: -1.0, Ea: 0.0 cal/mol} efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} - - This class is a replacement for `ThreeBodyReaction` and cannot be - instantiated from XML. It is the default for import from YAML. """ reaction_type = "three-body" @@ -1619,14 +1613,14 @@ cdef class ThreeBodyReaction3(ElementaryReaction3): return self.thirdbody().efficiency(stringify(species)) -cdef class PlogReaction3(Reaction3): +cdef class PlogReaction(Reaction3): """ A pressure-dependent reaction parameterized by logarithmically interpolating between Arrhenius rate expressions at various pressures. - An example for the definition of an `PlogReaction3` object is given as:: + An example for the definition of an `PlogReaction` object is given as:: - rxn = PlogReaction3( + rxn = PlogReaction( equation='H2 + O2 <=> 2 OH', rate={"rate-constants": [{'P': 1013.25, 'A': 1.2124e+16, 'b': -0.5779, 'Ea': 45491376.8}, @@ -1644,9 +1638,6 @@ cdef class PlogReaction3(Reaction3): - {P: 1.0 atm, A: 4.9108e+31, b: -4.8507, Ea: 2.47728e+04 cal/mol} - {P: 10.0 atm, A: 1.2866e+47, b: -9.0246, Ea: 3.97965e+04 cal/mol} - {P: 100.0 atm, A: 5.9632e+56, b: -11.529, Ea: 5.25996e+04 cal/mol} - - This class is a replacement for `PlogReaction` and cannot be - instantiated from XML. It is the default for import from YAML. """ reaction_type = "pressure-dependent-Arrhenius" @@ -1715,14 +1706,14 @@ cdef class PlogReaction3(Reaction3): return self.rate(T, P) -cdef class ChebyshevReaction3(Reaction3): +cdef class ChebyshevReaction(Reaction3): """ A pressure-dependent reaction parameterized by a bivariate Chebyshev polynomial in temperature and pressure. - An example for the definition of an `PlogReaction3` object is given as:: + An example for the definition of an `PlogReaction` object is given as:: - rxn = ChebyshevReaction3( + rxn = ChebyshevReaction( equation="HO2 <=> OH + O", rate={"Tmin": 290.0, "Tmax": 3000.0, "Pmin": 1e3, "Pmax": 1e8, @@ -1741,9 +1732,6 @@ cdef class ChebyshevReaction3(Reaction3): - [8.2883, -1.1397, -0.12059, 0.016034] - [1.9764, 1.0037, 7.2865e-03, -0.030432] - [0.3177, 0.26889, 0.094806, -7.6385e-03] - - This class is a replacement for `ChebyshevReaction` and cannot be - instantiated from XML. It is the default for import from YAML. """ reaction_type = "Chebyshev" diff --git a/interfaces/cython/cantera/test/test_convert.py b/interfaces/cython/cantera/test/test_convert.py index 87a6ef84af8..9a93d5531ef 100644 --- a/interfaces/cython/cantera/test/test_convert.py +++ b/interfaces/cython/cantera/test/test_convert.py @@ -604,8 +604,8 @@ def checkConversion(self, basename, cls=ct.Solution, ctiphases=(), self.assertEqual(C.composition, Y.composition) for C, Y in zip(ctiPhase.reactions(), yamlPhase.reactions()): - if Y.__class__.__name__.endswith('3'): - self.assertEqual(C.__class__.__name__, Y.__class__.__name__[:-1]) + if C.__class__.__name__.endswith('2'): + self.assertEqual(C.__class__.__name__[:-1], Y.__class__.__name__) else: self.assertEqual(C.__class__, Y.__class__) self.assertEqual(C.reactants, Y.reactants) @@ -854,8 +854,8 @@ def checkConversion(self, basename, cls=ct.Solution, ctmlphases=(), self.assertEqual(C.composition, Y.composition) for C, Y in zip(ctmlPhase.reactions(), yamlPhase.reactions()): - if Y.__class__.__name__.endswith('3'): - self.assertEqual(C.__class__.__name__, Y.__class__.__name__[:-1]) + if C.__class__.__name__.endswith('2'): + self.assertEqual(C.__class__.__name__[:-1], Y.__class__.__name__) else: self.assertEqual(C.__class__, Y.__class__) self.assertEqual(C.reactants, Y.reactants) diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 88574dc5141..ae5d8edd377 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -877,7 +877,7 @@ def test_fromCti(self): r = ct.Reaction.fromCti('''three_body_reaction('2 O + M <=> O2 + M', [1.200000e+11, -1.0, 0.0], efficiencies='AR:0.83 H2:2.4 H2O:15.4')''') - self.assertTrue(isinstance(r, ct.ThreeBodyReaction)) + self.assertTrue(isinstance(r, ct.ThreeBodyReaction2)) self.assertEqual(r.reactants['O'], 2) self.assertEqual(r.products['O2'], 1) self.assertEqual(r.efficiencies['H2O'], 15.4) @@ -892,7 +892,7 @@ def test_fromXml(self): rxn_node = root.find('.//reaction[@id="0001"]') r = ct.Reaction.fromXml(ET.tostring(rxn_node)) - self.assertTrue(isinstance(r, ct.ThreeBodyReaction)) + self.assertTrue(isinstance(r, ct.ThreeBodyReaction2)) self.assertEqual(r.reactants['O'], 2) self.assertEqual(r.products['O2'], 1) self.assertEqual(r.efficiencies['H2O'], 15.4) @@ -906,7 +906,7 @@ def test_fromYaml(self): " efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83}}", self.gas) - self.assertTrue(isinstance(r, ct.ThreeBodyReaction3)) + self.assertTrue(isinstance(r, ct.ThreeBodyReaction)) self.assertEqual(r.reactants['O'], 2) self.assertEqual(r.products['O2'], 1) self.assertEqual(r.efficiencies['H2O'], 15.4) @@ -959,7 +959,7 @@ def test_input_data_from_file(self): def test_input_data_from_scratch(self): r = ct.ElementaryReaction({'O':1, 'H2':1}, {'H':1, 'OH':1}) - r.rate = ct.Arrhenius(3.87e1, 2.7, 2.6e7) + r.rate = ct.ArrheniusRate(3.87e1, 2.7, 2.6e7) data = r.input_data self.assertNear(data['rate-constant']['A'], 3.87e1) self.assertNear(data['rate-constant']['b'], 2.7) @@ -970,7 +970,7 @@ def test_input_data_from_scratch(self): def test_elementary(self): r = ct.ElementaryReaction({'O':1, 'H2':1}, {'H':1, 'OH':1}) - r.rate = ct.Arrhenius(3.87e1, 2.7, 6260*1000*4.184) + r.rate = ct.ArrheniusRate(3.87e1, 2.7, 6260*1000*4.184) gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', species=self.species, reactions=[r]) @@ -988,15 +988,15 @@ def test_arrhenius_rate(self): def test_negative_A(self): species = ct.Species.listFromFile('gri30.cti') r = ct.ElementaryReaction('NH:1, NO:1', 'N2O:1, H:1') - r.rate = ct.Arrhenius(-2.16e13, -0.23, 0) + r.rate = ct.ArrheniusRate(-2.16e13, -0.23, 0) - self.assertFalse(r.allow_negative_pre_exponential_factor) + self.assertFalse(r.rate.allow_negative_pre_exponential_factor) with self.assertRaisesRegex(ct.CanteraError, 'negative pre-exponential'): gas = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', species=species, reactions=[r]) - r.allow_negative_pre_exponential_factor = True + r.rate.allow_negative_pre_exponential_factor = True gas = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', species=species, reactions=[r]) @@ -1026,7 +1026,7 @@ def test_threebody(self): r = ct.ThreeBodyReaction() r.reactants = {'O':1, 'H':1} r.products = {'OH':1} - r.rate = ct.Arrhenius(5e11, -1.0, 0.0) + r.rate = ct.ArrheniusRate(5e11, -1.0, 0.0) r.efficiencies = {'AR':0.7, 'H2':2.0, 'H2O':6.0} gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', @@ -1062,7 +1062,7 @@ def test_plog(self): r = ct.PlogReaction() r.reactants = {'R1A':1, 'R1B':1} r.products = {'P1':1, 'H':1} - r.rates = [ + r.rate.rates = [ (0.01*ct.one_atm, ct.Arrhenius(1.2124e13, -0.5779, 10872.7*4184)), (1.0*ct.one_atm, ct.Arrhenius(4.9108e28, -4.8507, 24772.8*4184)), (10.0*ct.one_atm, ct.Arrhenius(1.2866e44, -9.0246, 39796.5*4184)), @@ -1095,11 +1095,11 @@ def test_chebyshev(self): r = ct.ChebyshevReaction() r.reactants = 'R5:1, H:1' r.products = 'P5A:1, P5B:1' - r.set_parameters(Tmin=300.0, Tmax=2000.0, Pmin=1000, Pmax=10000000, - coeffs=[[ 5.28830e+00, -1.13970e+00, -1.20590e-01, 1.60340e-02], - [ 1.97640e+00, 1.00370e+00, 7.28650e-03, -3.04320e-02], - [ 3.17700e-01, 2.68890e-01, 9.48060e-02, -7.63850e-03], - [-3.12850e-02, -3.94120e-02, 4.43750e-02, 1.44580e-02]]) + r.rate = ct.ChebyshevRate(Tmin=300.0, Tmax=2000.0, Pmin=1000, Pmax=10000000, + data=[[ 5.28830e+00, -1.13970e+00, -1.20590e-01, 1.60340e-02], + [ 1.97640e+00, 1.00370e+00, 7.28650e-03, -3.04320e-02], + [ 3.17700e-01, 2.68890e-01, 9.48060e-02, -7.63850e-03], + [-3.12850e-02, -3.94120e-02, 4.43750e-02, 1.44580e-02]]) gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', species=species, reactions=[r]) @@ -1118,11 +1118,11 @@ def test_chebyshev_single_P(self): r = ct.ChebyshevReaction() r.reactants = 'R5:1, H:1' r.products = 'P5A:1, P5B:1' - r.set_parameters(Tmin=300.0, Tmax=2000.0, Pmin=1000, Pmax=10000000, - coeffs=[[ 5.28830e+00], - [ 1.97640e+00], - [ 3.17700e-01], - [-3.12850e-02]]) + r.rate = ct.ChebyshevRate(Tmin=300.0, Tmax=2000.0, Pmin=1000, Pmax=10000000, + data=[[ 5.28830e+00], + [ 1.97640e+00], + [ 3.17700e-01], + [-3.12850e-02]]) gas = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', species=species, reactions=[r]) @@ -1140,8 +1140,8 @@ def test_chebyshev_single_T(self): r = ct.ChebyshevReaction() r.reactants = 'R5:1, H:1' r.products = 'P5A:1, P5B:1' - r.set_parameters(Tmin=300.0, Tmax=2000.0, Pmin=1000, Pmax=10000000, - coeffs=[[ 5.28830e+00, -1.13970e+00, -1.20590e-01, 1.60340e-02]]) + r.rate = ct.ChebyshevRate(Tmin=300.0, Tmax=2000.0, Pmin=1000, Pmax=10000000, + data=[[ 5.28830e+00, -1.13970e+00, -1.20590e-01, 1.60340e-02]]) gas = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', species=species, reactions=[r]) @@ -1322,7 +1322,7 @@ def test_BlowersMaselinterface(self): def test_modify_invalid(self): # different reaction type tbr = self.gas.reaction(0) - R2 = ct.ElementaryReaction3(tbr.reactants, tbr.products) + R2 = ct.ElementaryReaction(tbr.reactants, tbr.products) R2.rate = tbr.rate with self.assertRaisesRegex(ct.CanteraError, 'types are different'): self.gas.modify_reaction(0, R2) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 53c068d78e7..5bdf3e8aba2 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -296,7 +296,7 @@ class ReactionTests: _cls = None # reaction object to be tested _type = None # name of reaction rate - _legacy = True # object uses legacy framework + _legacy = False # object uses legacy framework _equation = None # reaction equation string _rate = None # parameters for reaction rate object constructor _rate_obj = None # reaction rate object @@ -480,50 +480,51 @@ def test_deprecated_callers(self): class TestElementary(ReactionTests, utilities.CanteraTest): - # test legacy version of elementary reaction + # test updated version of elementary reaction _cls = ct.ElementaryReaction - _type = "elementary-legacy" + _type = "elementary" _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) + _rate_obj = ct.ArrheniusRate(38.7, 2.7, 2.619184e+07) _kwargs = {} _index = 0 _yaml = """ equation: O + H2 <=> H + OH - type: elementary-legacy rate-constant: {A: 38.7, b: 2.7, Ea: 6260.0 cal/mol} """ + _deprecated_getters = {"allow_negative_pre_exponential_factor": False} _deprecated_setters = {"allow_negative_pre_exponential_factor": True} -class TestElementary3(TestElementary): - # test updated version of elementary reaction +class TestElementary2(TestElementary): + # test legacy version of elementary reaction - _cls = ct.ElementaryReaction3 - _type = "elementary" - _legacy = False - _rate_obj = ct.ArrheniusRate(38.7, 2.7, 2.619184e+07) + _cls = ct.ElementaryReaction2 + _type = "elementary-legacy" + _legacy = True + _rate_obj = ct.Arrhenius(38.7, 2.7, 2.619184e+07) _yaml = """ equation: O + H2 <=> H + OH + type: elementary-legacy rate-constant: {A: 38.7, b: 2.7, Ea: 6260.0 cal/mol} """ class TestThreeBody(TestElementary): - # test legacy version of three-body reaction + # test updated version of three-body reaction _cls = ct.ThreeBodyReaction - _type = "three-body-legacy" + _type = "three-body" _equation = "2 O + M <=> O2 + M" _rate = {"A": 1.2e11, "b": -1.0, "Ea": 0.0} - _rate_obj = ct.Arrhenius(1.2e11, -1., 0.) + _rate_obj = ct.ArrheniusRate(1.2e11, -1., 0.) _kwargs = {"efficiencies": {"H2": 2.4, "H2O": 15.4, "AR": 0.83}} _index = 1 _yaml = """ equation: 2 O + M <=> O2 + M - type: three-body-legacy + type: three-body rate-constant: {A: 1.2e+11, b: -1.0, Ea: 0.0 cal/mol} efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} """ @@ -547,27 +548,26 @@ def test_efficiencies(self): self.assertEqual(rxn.efficiencies, self._kwargs["efficiencies"]) -class TestThreeBody3(TestThreeBody): - # test updated version of three-body reaction +class TestThreeBody2(TestThreeBody): + # test legacy version of three-body reaction - _cls = ct.ThreeBodyReaction3 - _type = "three-body" - _legacy = False - _rate_obj = ct.ArrheniusRate(1.2e11, -1., 0.) + _cls = ct.ThreeBodyReaction2 + _type = "three-body-legacy" + _legacy = True + _rate_obj = ct.Arrhenius(1.2e11, -1., 0.) _yaml = """ equation: 2 O + M <=> O2 + M - type: three-body + type: three-body-legacy rate-constant: {A: 1.2e+11, b: -1.0, Ea: 0.0 cal/mol} efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} """ -class TestImplicitThreeBody3(TestThreeBody): +class TestImplicitThreeBody(TestThreeBody): # test three-body reactions with explicit collision parther - _cls = ct.ThreeBodyReaction3 + _cls = ct.ThreeBodyReaction _type = "three-body" - _legacy = False _equation = "H + 2 O2 <=> HO2 + O2" _rate = {"A": 2.08e+19, "b": -1.24, "Ea": 0.0} _rate_obj = ct.ArrheniusRate(2.08e+19, -1.24, 0.) @@ -594,20 +594,25 @@ def test_from_parts(self): class TestPlog(ReactionTests, utilities.CanteraTest): - # test legacy version of Plog reaction + # test updated version of Plog reaction _cls = ct.PlogReaction - _type = "pressure-dependent-Arrhenius-legacy" + _type = "pressure-dependent-Arrhenius" + _legacy = False _equation = "H2 + O2 <=> 2 OH" _rate = {"rate-constants": [ {"P": 1013.25, "A": 1.2124e+16, "b": -0.5779, "Ea": 45491376.8}, {"P": 101325., "A": 4.9108e+31, "b": -4.8507, "Ea": 103649395.2}, {"P": 1013250., "A": 1.2866e+47, "b": -9.0246, "Ea": 166508556.0}, {"P": 10132500., "A": 5.9632e+56, "b": -11.529, "Ea": 220076726.4}]} + _rate_obj = ct.PlogRate([(1013.25, ct.Arrhenius(1.2124e+16, -0.5779, 45491376.8)), + (101325., ct.Arrhenius(4.9108e+31, -4.8507, 103649395.2)), + (1013250., ct.Arrhenius(1.2866e+47, -9.0246, 166508556.0)), + (10132500., ct.Arrhenius(5.9632e+56, -11.529, 220076726.4))]) _index = 3 _yaml = """ - equation: H2 + O2 <=> 2 OH # Reaction 4 - type: pressure-dependent-Arrhenius-legacy + equation: H2 + O2 <=> 2 OH + type: pressure-dependent-Arrhenius rate-constants: - {P: 0.01 atm, A: 1.2124e+16, b: -0.5779, Ea: 1.08727e+04 cal/mol} - {P: 1.0 atm, A: 4.9108e+31, b: -4.8507, Ea: 2.47728e+04 cal/mol} @@ -656,19 +661,16 @@ def test_deprecated_setters(self): self.check_rates(rxn.rates, _rate) -class TestPlog3(TestPlog): - # test updated version of Plog reaction +class TestPlog2(TestPlog): + # test legacy version of Plog reaction - _cls = ct.PlogReaction3 - _type = "pressure-dependent-Arrhenius" - _legacy = False - _rate_obj = ct.PlogRate([(1013.25, ct.Arrhenius(1.2124e+16, -0.5779, 45491376.8)), - (101325., ct.Arrhenius(4.9108e+31, -4.8507, 103649395.2)), - (1013250., ct.Arrhenius(1.2866e+47, -9.0246, 166508556.0)), - (10132500., ct.Arrhenius(5.9632e+56, -11.529, 220076726.4))]) + _cls = ct.PlogReaction2 + _type = "pressure-dependent-Arrhenius-legacy" + _legacy = True + _rate_obj = None _yaml = """ - equation: H2 + O2 <=> 2 OH # Reaction 4 - type: pressure-dependent-Arrhenius + equation: H2 + O2 <=> 2 OH + type: pressure-dependent-Arrhenius-legacy rate-constants: - {P: 0.01 atm, A: 1.2124e+16, b: -0.5779, Ea: 1.08727e+04 cal/mol} - {P: 1.0 atm, A: 4.9108e+31, b: -4.8507, Ea: 2.47728e+04 cal/mol} @@ -678,19 +680,23 @@ class TestPlog3(TestPlog): class TestChebyshev(ReactionTests, utilities.CanteraTest): - # test legacy version of Chebyshev reaction + # test updated version of Chebyshev reaction _cls = ct.ChebyshevReaction - _type = "Chebyshev-legacy" + _type = "Chebyshev" _equation = "HO2 <=> OH + O" _rate = {"Tmin": 290., "Tmax": 3000., "Pmin": 1000., "Pmax": 10000000.0, "data": [[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], [ 1.9764e+00, 1.0037e+00, 7.2865e-03, -3.0432e-02], [ 3.1770e-01, 2.6889e-01, 9.4806e-02, -7.6385e-03]]} + _rate_obj = ct.ChebyshevRate(Tmin=290., Tmax=3000., Pmin=1000., Pmax=10000000.0, + data=[[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], + [ 1.9764e+00, 1.0037e+00, 7.2865e-03, -3.0432e-02], + [ 3.1770e-01, 2.6889e-01, 9.4806e-02, -7.6385e-03]]) _index = 4 _yaml = """ - equation: HO2 <=> OH + O # Reaction 5 - type: Chebyshev-legacy + equation: HO2 <=> OH + O + type: Chebyshev temperature-range: [290.0, 3000.0] pressure-range: [9.869232667160128e-03 atm, 98.69232667160128 atm] data: @@ -709,19 +715,16 @@ def setUpClass(obj): {k: v for k, v in obj._rate.items() if k != "data"}) -class TestChebyshev3(TestChebyshev): - # test updated version of Chebyshev reaction +class TestChebyshev2(TestChebyshev): + # test legacy version of Chebyshev reaction - _cls = ct.ChebyshevReaction3 - _type = "Chebyshev" - _legacy = False - _rate_obj = ct.ChebyshevRate(Tmin=290., Tmax=3000., Pmin=1000., Pmax=10000000.0, - data=[[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], - [ 1.9764e+00, 1.0037e+00, 7.2865e-03, -3.0432e-02], - [ 3.1770e-01, 2.6889e-01, 9.4806e-02, -7.6385e-03]]) + _cls = ct.ChebyshevReaction2 + _type = "Chebyshev-legacy" + _legacy = True + _rate_obj = None _yaml = """ - equation: HO2 <=> OH + O # Reaction 5 - type: Chebyshev + equation: HO2 <=> OH + O + type: Chebyshev-legacy temperature-range: [290.0, 3000.0] pressure-range: [9.869232667160128e-03 atm, 98.69232667160128 atm] data: From 18c906ec5bec6b122d829ca1c100b7750112011b Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 18 May 2021 08:23:36 -0500 Subject: [PATCH 72/84] [Kinetics] Allow setting of ElementaryReaction.rate using legacy Arrhenius --- interfaces/cython/cantera/reaction.pyx | 22 ++++++++++++++----- .../cython/cantera/test/test_reaction.py | 12 +++++++++- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 2b1a455addf..4d486cf5121 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -1501,15 +1501,27 @@ cdef class ElementaryReaction(Reaction3): deref(kinetics.kinetics)) self.reaction = self._reaction.get() - if isinstance(rate, ArrheniusRate): + if isinstance(rate, (ArrheniusRate, Arrhenius)): self.rate = rate property rate: - """ Get/Set the `Arrhenius` rate coefficient for this reaction. """ + """ Get/Set the `ArrheniusRate` rate coefficient for this reaction. """ def __get__(self): return ArrheniusRate.wrap(self.er().rate()) - def __set__(self, ArrheniusRate rate): - self.er().setRate(rate._base) + def __set__(self, rate): + cdef ArrheniusRate rate3 + if isinstance(rate, ArrheniusRate): + rate3 = rate + elif isinstance(rate, Arrhenius): + warnings.warn("Setting the rate using an 'Arrhenius' object is " + "deprecated and will be removed after Cantera 2.6. The argument " + "type is replaceable by 'ArrheniusRate'.", DeprecationWarning) + rate3 = ArrheniusRate(rate.pre_exponential_factor, + rate.temperature_exponent, + rate.activation_energy) + else: + raise TypeError("Invalid rate definition") + self.er().setRate(rate3._base) property allow_negative_pre_exponential_factor: """ @@ -1665,7 +1677,7 @@ cdef class PlogReaction(Reaction3): self.rate = rate property rate: - """ Get/Set the `Plog` rate coefficients for this reaction. """ + """ Get/Set the `PlogRate` rate coefficients for this reaction. """ def __get__(self): return PlogRate.wrap(self.pr().rate()) def __set__(self, PlogRate rate): diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 5bdf3e8aba2..ae2ef20cf88 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -493,10 +493,20 @@ class TestElementary(ReactionTests, utilities.CanteraTest): equation: O + H2 <=> H + OH rate-constant: {A: 38.7, b: 2.7, Ea: 6260.0 cal/mol} """ - _deprecated_getters = {"allow_negative_pre_exponential_factor": False} _deprecated_setters = {"allow_negative_pre_exponential_factor": True} + def test_arrhenius(self): + # test assigning Arrhenius rate + rate = ct.Arrhenius(self._rate["A"], self._rate["b"], self._rate["Ea"]) + rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) + if self._legacy: + rxn.rate = rate + else: + with self.assertWarnsRegex(DeprecationWarning, "'Arrhenius' object is deprecated"): + rxn.rate = rate + self.check_rxn(rxn) + class TestElementary2(TestElementary): # test legacy version of elementary reaction From 73a5661249f820f2442fb44835509f355a5c56f2 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 26 May 2021 06:53:49 -0500 Subject: [PATCH 73/84] [Kinetics] Clarify Reaction3 docstring --- include/cantera/kinetics/Reaction.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index 1212beb9bbf..fe3d3c6370f 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -453,9 +453,10 @@ class BlowersMaselInterfaceReaction : public BlowersMaselReaction }; -//! An intermediate class used to avoid naming conflicts of 'rate' member -//! variables and getters (see `ElementaryReaction2`, `PlogReaction2` and -//! `ChebyshevReaction2`). +//! An intermediate class collecting new features of the updated reaction classes. +//! It also avoids ambiguous uses of 'rate' member variables and getters (see +//! `ElementaryReaction2`, `PlogReaction2` and `ChebyshevReaction2`). The class will +//! be merged with Reaction after deprecation of the CTI/XML framework. class Reaction3 : public Reaction { public: From 26ea9036e9e4e9df618360ecff7eb562916db1f8 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Mon, 31 May 2021 08:06:14 -0500 Subject: [PATCH 74/84] [Kinetics] Prepare ReactionData for holding species information --- include/cantera/kinetics/MultiRate.h | 21 ++++++++++++++---- include/cantera/kinetics/ReactionData.h | 29 +++++++++++++++++++------ src/kinetics/BulkKinetics.cpp | 4 ++++ src/kinetics/ReactionData.cpp | 18 ++++++++++----- 4 files changed, 55 insertions(+), 17 deletions(-) diff --git a/include/cantera/kinetics/MultiRate.h b/include/cantera/kinetics/MultiRate.h index 2eac026ddfb..61d0045f843 100644 --- a/include/cantera/kinetics/MultiRate.h +++ b/include/cantera/kinetics/MultiRate.h @@ -41,6 +41,10 @@ class MultiRateBase virtual bool replace(const size_t rxn_index, ReactionRateBase& rate) = 0; + //! Update number of species + //! @param n_species number of species + virtual void resizeSpecies(size_t n_species) = 0; + //! Evaluate all rate constants handled by the evaluator //! @param bulk object representing bulk phase //! @param kf array of rate constants @@ -59,13 +63,15 @@ class MultiBulkRates final : public MultiRateBase { public: virtual void add(const size_t rxn_index, - ReactionRateBase& rate) override { + ReactionRateBase& rate) override + { m_indices[rxn_index] = m_rxn_rates.size(); m_rxn_rates.emplace_back(rxn_index, dynamic_cast(rate)); } virtual bool replace(const size_t rxn_index, - ReactionRateBase& rate) override { + ReactionRateBase& rate) override + { if (!m_rxn_rates.size()) { throw CanteraError("MultiBulkRate::replace", "Invalid operation: cannot replace rate object " @@ -84,14 +90,21 @@ class MultiBulkRates final : public MultiRateBase return false; } + virtual void resizeSpecies(size_t n_species) + { + m_shared.resizeSpecies(n_species); + } + virtual void getRateConstants(const ThermoPhase& bulk, - double* kf, double* concm) const override { + double* kf, double* concm) const override + { for (const auto& rxn : m_rxn_rates) { kf[rxn.first] = rxn.second.eval(m_shared, concm[rxn.first]); } } - virtual void update(const ThermoPhase& bulk, double* concm) override { + virtual void update(const ThermoPhase& bulk, double* concm) override + { // update common data once for each reaction type m_shared.update(bulk); if (RateType::uses_update()) { diff --git a/include/cantera/kinetics/ReactionData.h b/include/cantera/kinetics/ReactionData.h index 1a347868b57..4cb1d617600 100644 --- a/include/cantera/kinetics/ReactionData.h +++ b/include/cantera/kinetics/ReactionData.h @@ -26,18 +26,22 @@ struct ArrheniusData ArrheniusData() : m_temperature(1.), m_logT(0.), m_recipT(1.) {} //! Update data container based on temperature *T* - void update(double T) { + void update(double T) + { m_temperature = T; m_logT = std::log(T); m_recipT = 1./T; } //! Update data container based on temperature *T* and pressure *P* - void update(double T, double P) { update(T); }; + void update(double T, double P) { update(T); } //! Update data container based on *bulk* phase state void update(const ThermoPhase& bulk); + //! Update number of species; unused + void resizeSpecies(size_t n_species) {} + double m_temperature; //!< temperature double m_logT; //!< logarithm of temperature double m_recipT; //!< inverse of temperature @@ -57,7 +61,8 @@ struct PlogData void update(double T); //! Update data container based on temperature *T* and *P* - void update(double T, double P) { + void update(double T, double P) + { m_temperature = T; m_logT = std::log(T); m_recipT = 1./T; @@ -67,12 +72,15 @@ struct PlogData //! Update data container based on *bulk* phase state void update(const ThermoPhase& bulk); + //! Update number of species; unused + void resizeSpecies(size_t n_species) {} + //! Pointer to logP (required by Plog::update_C) const double* logP() const { return &m_logP; } double m_temperature; //!< temperature double m_logT; //!< logarithm of temperature - double m_recipT; //!< inverse of temperature + double m_recipT; //!< inverse of temperature double m_logP; //!< logarithm of pressure }; @@ -90,7 +98,8 @@ struct ChebyshevData void update(double T); //! Update data container based on temperature *T* and *P* - void update(double T, double P) { + void update(double T, double P) + { m_temperature = T; m_recipT = 1./T; m_log10P = std::log10(P); @@ -99,11 +108,14 @@ struct ChebyshevData //! Update data container based on *bulk* phase state void update(const ThermoPhase& bulk); + //! Update number of species; unused + void resizeSpecies(size_t n_species) {} + //! Pointer to logP (required by Chebyshev::update_C) const double* log10P() const { return &m_log10P; } double m_temperature; //!< temperature - double m_recipT; //!< inverse of temperature + double m_recipT; //!< inverse of temperature double m_log10P; //!< base 10 logarithm of pressure }; @@ -117,11 +129,14 @@ struct CustomFunc1Data void update(double T) { m_temperature = T; } //! Update data container based on temperature *T* and pressure *P* - void update(double T, double P) { update(T); }; + void update(double T, double P) { update(T); } //! Update data container based on *bulk* phase state void update(const ThermoPhase& bulk); + //! Update number of species; unused + void resizeSpecies(size_t n_species) {} + double m_temperature; //!< temperature }; diff --git a/src/kinetics/BulkKinetics.cpp b/src/kinetics/BulkKinetics.cpp index 69616748721..7d6c12acd52 100644 --- a/src/kinetics/BulkKinetics.cpp +++ b/src/kinetics/BulkKinetics.cpp @@ -145,6 +145,7 @@ bool BulkKinetics::addReaction(shared_ptr r) throw CanteraError("BulkKinetics::addReaction", "Adding " "reaction type '" + rate->type() + "' is not implemented"); } + m_bulk_rates.back()->resizeSpecies(m_kk); } // Add reaction rate to evaluator @@ -217,6 +218,9 @@ void BulkKinetics::resizeSpecies() m_act_conc.resize(m_kk); m_phys_conc.resize(m_kk); m_grt.resize(m_kk); + for (auto& rates : m_bulk_rates) { + rates->resizeSpecies(m_kk); + } } void BulkKinetics::setMultiplier(size_t i, double f) { diff --git a/src/kinetics/ReactionData.cpp b/src/kinetics/ReactionData.cpp index cadee35e0e1..9da9832f91c 100644 --- a/src/kinetics/ReactionData.cpp +++ b/src/kinetics/ReactionData.cpp @@ -10,29 +10,35 @@ namespace Cantera { -void ArrheniusData::update(const ThermoPhase& bulk) { +void ArrheniusData::update(const ThermoPhase& bulk) +{ update(bulk.temperature()); } -void PlogData::update(double T) { +void PlogData::update(double T) +{ throw CanteraError("PlogData::update", "Missing state information: reaction type requires pressure."); } -void PlogData::update(const ThermoPhase& bulk) { +void PlogData::update(const ThermoPhase& bulk) +{ update(bulk.temperature(), bulk.pressure()); } -void ChebyshevData::update(double T) { +void ChebyshevData::update(double T) +{ throw CanteraError("ChebyshevData::update", "Missing state information: reaction type requires pressure."); } -void ChebyshevData::update(const ThermoPhase& bulk) { +void ChebyshevData::update(const ThermoPhase& bulk) +{ update(bulk.temperature(), bulk.pressure()); } -void CustomFunc1Data::update(const ThermoPhase& bulk) { +void CustomFunc1Data::update(const ThermoPhase& bulk) +{ m_temperature = bulk.temperature(); } From bd4f00d9a36c072308390ce3c94ac66c4f5fcdbf Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 2 Jun 2021 06:59:43 -0500 Subject: [PATCH 75/84] [Kinetics] Always reference underlying C++ type in Python reaction type --- interfaces/cython/cantera/reaction.pyx | 72 +++++++++++++++----------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 4d486cf5121..287d058cc0a 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -365,12 +365,12 @@ cdef class Reaction: R = ct.Reaction.fromCti('''reaction('O + H2 <=> H + OH', [(3.87e4, 'cm3/mol/s'), 2.7, (6260, 'cal/mol')])''') """ - reaction_type = "" + _reaction_type = "" def __cinit__(self, reactants='', products='', init=True, **kwargs): if init: - self._reaction = CxxNewReaction(stringify((self.reaction_type))) + self._reaction = CxxNewReaction(stringify((self._reaction_type))) self.reaction = self._reaction.get() if reactants: self.reactants = reactants @@ -386,7 +386,7 @@ cdef class Reaction: if not(_reaction_class_registry): def register_subclasses(cls): for c in cls.__subclasses__(): - _reaction_class_registry[getattr(c, 'reaction_type')] = c + _reaction_class_registry[getattr(c, '_reaction_type')] = c register_subclasses(c) # update global reaction class registry @@ -439,7 +439,7 @@ cdef class Reaction: A `Kinetics` object whose associated phase(s) contain the species involved in the reaction. """ - if cls.reaction_type != "": + if cls._reaction_type != "": raise TypeError( "Class method 'fromYaml' was invoked from '{}' but should " "be called from base class 'Reaction'".format(cls.__name__)) @@ -594,6 +594,14 @@ cdef class Reaction: def __set__(self, ID): self.reaction.id = stringify(ID) + property reaction_type: + """ + Get/Set the identification string for the reaction, which can be used in + filtering operations. + """ + def __get__(self): + return pystr(self.reaction.type()) + property reversible: """ Get/Set a flag which is `True` if this reaction is reversible or `False` @@ -665,6 +673,11 @@ cdef class Reaction: def __str__(self): return self.equation + def _deprecation_warning(self, attr, what="property"): + return ("\n{} '{}' to be removed after Cantera 2.6.\nThis {} is moved to " + "the {} object accessed via the 'rate' property." + ).format(what.capitalize(), attr, what, type(self.rate).__name__) + cdef class Arrhenius: r""" @@ -752,14 +765,14 @@ cdef class ElementaryReaction2(Reaction): implementation uses the legacy framework for reaction rate evaluations used by Cantera 2.5.1 and earlier. Refer to `ElementaryReaction` for new behavior. """ - reaction_type = "elementary-legacy" + _reaction_type = "elementary-legacy" def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): if init and equation and kinetics: - spec = {'equation': equation, 'type': self.reaction_type} + spec = {'equation': equation, 'type': self._reaction_type} if isinstance(rate, dict): spec['rate-constant'] = rate elif isinstance(rate, Arrhenius) or rate is None: @@ -807,14 +820,14 @@ cdef class ThreeBodyReaction2(ElementaryReaction2): implementation uses the legacy framework for reaction rate evaluations used by Cantera 2.5.1 and earlier. Refer to `ThreeBodyReaction` for new behavior. """ - reaction_type = "three-body-legacy" + _reaction_type = "three-body-legacy" def __init__(self, equation=None, rate=None, efficiencies=None, Kinetics kinetics=None, init=True, **kwargs): if init and equation and kinetics: - spec = {'equation': equation, 'type': self.reaction_type} + spec = {'equation': equation, 'type': self._reaction_type} if isinstance(rate, dict): spec['rate-constant'] = rate elif isinstance(rate, Arrhenius) or rate is None: @@ -959,7 +972,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" + _reaction_type = "falloff" cdef CxxFalloffReaction* frxn(self): return self.reaction @@ -1036,7 +1049,7 @@ cdef class ChemicallyActivatedReaction(FalloffReaction): that the forward rate constant is written as being proportional to the low- pressure rate constant. """ - reaction_type = "chemically-activated" + _reaction_type = "chemically-activated" cdef class PlogReaction2(Reaction): @@ -1050,14 +1063,14 @@ cdef class PlogReaction2(Reaction): implementation uses the legacy framework for reaction rate evaluations used by Cantera 2.5.1 and earlier. Refer to `PlogReaction` for new behavior. """ - reaction_type = "pressure-dependent-Arrhenius-legacy" + _reaction_type = "pressure-dependent-Arrhenius-legacy" def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): if init and equation and kinetics: - spec = {'equation': equation, 'type': self.reaction_type} + spec = {'equation': equation, 'type': self._reaction_type} if isinstance(rate, dict): spec.update(rate) else: @@ -1114,14 +1127,14 @@ cdef class ChebyshevReaction2(Reaction): implementation uses the legacy framework for reaction rate evaluations used by Cantera 2.5.1 and earlier. Refer to `ChebyshevReaction` for new behavior. """ - reaction_type = "Chebyshev-legacy" + _reaction_type = "Chebyshev-legacy" def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): if init and equation and kinetics: - spec = {'equation': equation, 'type': self.reaction_type} + spec = {'equation': equation, 'type': self._reaction_type} if isinstance(rate, dict): spec['temperature-range'] = [rate['Tmin'], rate['Tmax']] spec['pressure-range'] = [rate['Pmin'], rate['Pmax']] @@ -1286,7 +1299,7 @@ cdef class BlowersMaselReaction(Reaction): A reaction which follows mass-action kinetics with Blowers Masel reaction rate. """ - reaction_type = "Blowers-Masel" + _reaction_type = "Blowers-Masel" property rate: """ Get/Set the `Arrhenius` rate coefficient for this reaction. """ @@ -1312,7 +1325,7 @@ cdef class BlowersMaselReaction(Reaction): cdef class InterfaceReaction(ElementaryReaction2): """ A reaction occurring on an `Interface` (i.e. a surface or an edge) """ - reaction_type = "interface" + _reaction_type = "interface" property coverage_deps: """ @@ -1385,7 +1398,7 @@ cdef class BlowersMaselInterfaceReaction(BlowersMaselReaction): A reaction occurring on an `Interface` (i.e. a surface or an edge) with the rate parameterization of `BlowersMasel`. """ - reaction_type = "surface-Blowers-Masel" + _reaction_type = "surface-Blowers-Masel" property coverage_deps: """ @@ -1456,10 +1469,7 @@ cdef class BlowersMaselInterfaceReaction(BlowersMaselReaction): cdef class Reaction3(Reaction): """ Convenience class holding methods common to the Reaction3 framework """ - def _deprecation_warning(self, attr, what="property"): - return ("\n{} '{}' to be removed after Cantera 2.6.\nThis {} is moved to " - "the {} object accessed via the 'rate' property." - ).format(what.capitalize(), attr, what, type(self.rate).__name__) + pass cdef class ElementaryReaction(Reaction3): @@ -1479,7 +1489,7 @@ cdef class ElementaryReaction(Reaction3): equation: O + H2 <=> H + OH rate-constant: {A: 3.87e+04 cm^3/mol/s, b: 2.7, Ea: 6260.0 cal/mol} """ - reaction_type = "elementary" + _reaction_type = "elementary" cdef CxxElementaryReaction3* er(self): return self.reaction @@ -1489,7 +1499,7 @@ cdef class ElementaryReaction(Reaction3): if init and equation and kinetics: - spec = {"equation": equation, "type": self.reaction_type} + spec = {"equation": equation, "type": self._reaction_type} if isinstance(rate, dict): spec["rate-constant"] = rate elif isinstance(rate, ArrheniusRate) or rate is None: @@ -1565,7 +1575,7 @@ cdef class ThreeBodyReaction(ElementaryReaction): rate-constant: {A: 1.2e+17 cm^6/mol^2/s, b: -1.0, Ea: 0.0 cal/mol} efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} """ - reaction_type = "three-body" + _reaction_type = "three-body" cdef CxxThreeBodyReaction3* tbr(self): return self.reaction @@ -1578,7 +1588,7 @@ cdef class ThreeBodyReaction(ElementaryReaction): if init and equation and kinetics: - spec = {"equation": equation, "type": self.reaction_type} + spec = {"equation": equation, "type": self._reaction_type} if isinstance(rate, dict): spec["rate-constant"] = rate elif isinstance(rate, ArrheniusRate) or rate is None: @@ -1651,7 +1661,7 @@ cdef class PlogReaction(Reaction3): - {P: 10.0 atm, A: 1.2866e+47, b: -9.0246, Ea: 3.97965e+04 cal/mol} - {P: 100.0 atm, A: 5.9632e+56, b: -11.529, Ea: 5.25996e+04 cal/mol} """ - reaction_type = "pressure-dependent-Arrhenius" + _reaction_type = "pressure-dependent-Arrhenius" cdef CxxPlogReaction3* pr(self): return self.reaction @@ -1661,7 +1671,7 @@ cdef class PlogReaction(Reaction3): if init and equation and kinetics: - spec = {"equation": equation, "type": self.reaction_type} + spec = {"equation": equation, "type": self._reaction_type} if isinstance(rate, dict): spec.update(rate) elif isinstance(rate, PlogRate) or rate is None: @@ -1745,7 +1755,7 @@ cdef class ChebyshevReaction(Reaction3): - [1.9764, 1.0037, 7.2865e-03, -0.030432] - [0.3177, 0.26889, 0.094806, -7.6385e-03] """ - reaction_type = "Chebyshev" + _reaction_type = "Chebyshev" cdef CxxChebyshevReaction3* cr(self): return self.reaction @@ -1755,7 +1765,7 @@ cdef class ChebyshevReaction(Reaction3): if init and equation and kinetics: - spec = {"equation": equation, "type": self.reaction_type} + spec = {"equation": equation, "type": self._reaction_type} if isinstance(rate, dict): spec["temperature-range"] = [rate["Tmin"], rate["Tmax"]] spec["pressure-range"] = [rate["Pmin"], rate["Pmax"]] @@ -1905,14 +1915,14 @@ cdef class CustomReaction(Reaction3): rate=lambda T: 38.7 * T**2.7 * exp(-3150.15428/T), kinetics=gas) """ - reaction_type = "custom-rate-function" + _reaction_type = "custom-rate-function" def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): if init and equation and kinetics: - spec = {"equation": equation, "type": self.reaction_type} + spec = {"equation": equation, "type": self._reaction_type} self._reaction = CxxNewReaction(dict_to_anymap(spec), deref(kinetics.kinetics)) From c539b0b7fde3abc9d6572382d43c7b76f0fb8360 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 2 Jun 2021 08:11:49 -0500 Subject: [PATCH 76/84] [Kinetics] Introduce Python Reaction.uses_legacy flag --- interfaces/cython/cantera/reaction.pyx | 44 ++++++++++++++----- .../cython/cantera/test/test_reaction.py | 1 + 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 287d058cc0a..6eafee6425f 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -366,11 +366,15 @@ cdef class Reaction: [(3.87e4, 'cm3/mol/s'), 2.7, (6260, 'cal/mol')])''') """ _reaction_type = "" + _has_legacy = False def __cinit__(self, reactants='', products='', init=True, **kwargs): if init: - self._reaction = CxxNewReaction(stringify((self._reaction_type))) + rxn_type = self._reaction_type + if self._has_legacy: + rxn_type += "-legacy" + self._reaction = CxxNewReaction(stringify((rxn_type))) self.reaction = self._reaction.get() if reactants: self.reactants = reactants @@ -386,7 +390,10 @@ cdef class Reaction: if not(_reaction_class_registry): def register_subclasses(cls): for c in cls.__subclasses__(): - _reaction_class_registry[getattr(c, '_reaction_type')] = c + rxn_type = getattr(c, "_reaction_type") + if getattr(c, "_has_legacy", False): + rxn_type += "-legacy" + _reaction_class_registry[rxn_type] = c register_subclasses(c) # update global reaction class registry @@ -596,8 +603,7 @@ cdef class Reaction: property reaction_type: """ - Get/Set the identification string for the reaction, which can be used in - filtering operations. + Retrieve the native type name of the reaction. """ def __get__(self): return pystr(self.reaction.type()) @@ -678,6 +684,11 @@ cdef class Reaction: "the {} object accessed via the 'rate' property." ).format(what.capitalize(), attr, what, type(self.rate).__name__) + property uses_legacy: + """Indicate whether reaction uses a legacy implementation""" + def __get__(self): + return pystr(self.reaction.type()).endswith("-legacy") + cdef class Arrhenius: r""" @@ -765,14 +776,16 @@ cdef class ElementaryReaction2(Reaction): implementation uses the legacy framework for reaction rate evaluations used by Cantera 2.5.1 and earlier. Refer to `ElementaryReaction` for new behavior. """ - _reaction_type = "elementary-legacy" + _reaction_type = "elementary" + _has_legacy = True def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): if init and equation and kinetics: - spec = {'equation': equation, 'type': self._reaction_type} + rxn_type = self._reaction_type + "-legacy" + spec = {"equation": equation, "type": rxn_type} if isinstance(rate, dict): spec['rate-constant'] = rate elif isinstance(rate, Arrhenius) or rate is None: @@ -820,14 +833,16 @@ cdef class ThreeBodyReaction2(ElementaryReaction2): implementation uses the legacy framework for reaction rate evaluations used by Cantera 2.5.1 and earlier. Refer to `ThreeBodyReaction` for new behavior. """ - _reaction_type = "three-body-legacy" + _reaction_type = "three-body" + _has_legacy = True def __init__(self, equation=None, rate=None, efficiencies=None, Kinetics kinetics=None, init=True, **kwargs): if init and equation and kinetics: - spec = {'equation': equation, 'type': self._reaction_type} + rxn_type = self._reaction_type + "-legacy" + spec = {"equation": equation, "type": rxn_type} if isinstance(rate, dict): spec['rate-constant'] = rate elif isinstance(rate, Arrhenius) or rate is None: @@ -1063,14 +1078,16 @@ cdef class PlogReaction2(Reaction): implementation uses the legacy framework for reaction rate evaluations used by Cantera 2.5.1 and earlier. Refer to `PlogReaction` for new behavior. """ - _reaction_type = "pressure-dependent-Arrhenius-legacy" + _reaction_type = "pressure-dependent-Arrhenius" + _has_legacy = True def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): if init and equation and kinetics: - spec = {'equation': equation, 'type': self._reaction_type} + rxn_type = self._reaction_type + "-legacy" + spec = {"equation": equation, "type": rxn_type} if isinstance(rate, dict): spec.update(rate) else: @@ -1127,14 +1144,16 @@ cdef class ChebyshevReaction2(Reaction): implementation uses the legacy framework for reaction rate evaluations used by Cantera 2.5.1 and earlier. Refer to `ChebyshevReaction` for new behavior. """ - _reaction_type = "Chebyshev-legacy" + _reaction_type = "Chebyshev" + _has_legacy = True def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, **kwargs): if init and equation and kinetics: - spec = {'equation': equation, 'type': self._reaction_type} + rxn_type = self._reaction_type + "-legacy" + spec = {"equation": equation, "type": rxn_type} if isinstance(rate, dict): spec['temperature-range'] = [rate['Tmin'], rate['Tmax']] spec['pressure-range'] = [rate['Pmin'], rate['Pmax']] @@ -1326,6 +1345,7 @@ cdef class BlowersMaselReaction(Reaction): cdef class InterfaceReaction(ElementaryReaction2): """ A reaction occurring on an `Interface` (i.e. a surface or an edge) """ _reaction_type = "interface" + _has_legacy = False property coverage_deps: """ diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index ae2ef20cf88..f93387aae1a 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -322,6 +322,7 @@ def check_rxn(self, rxn): self.assertEqual(rxn.reaction_type, self._type) self.assertEqual(rxn.reactants, self.gas.reaction(ix).reactants) self.assertEqual(rxn.products, self.gas.reaction(ix).products) + self.assertEqual(rxn.uses_legacy, self._type.endswith("-legacy")) gas2 = ct.Solution(thermo="IdealGas", kinetics="GasKinetics", species=self.species, reactions=[rxn]) From 18f807df1565ae71871478f7fc3e4d70d02bc2a3 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 2 Jun 2021 09:38:20 -0500 Subject: [PATCH 77/84] [Kinetics] Make Python PlogReaction agnostic of framework --- interfaces/cython/cantera/reaction.pyx | 203 +++++++++--------- .../cython/cantera/test/test_reaction.py | 41 ++-- 2 files changed, 125 insertions(+), 119 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 6eafee6425f..05b502a5b10 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -367,12 +367,13 @@ cdef class Reaction: """ _reaction_type = "" _has_legacy = False + _dual = False # indicate whether legacy implementations are separate or merged - def __cinit__(self, reactants='', products='', init=True, **kwargs): + def __cinit__(self, reactants='', products='', init=True, legacy=False, **kwargs): if init: rxn_type = self._reaction_type - if self._has_legacy: + if (not self._dual and self._has_legacy) or (self._dual and legacy): rxn_type += "-legacy" self._reaction = CxxNewReaction(stringify((rxn_type))) self.reaction = self._reaction.get() @@ -391,6 +392,9 @@ cdef class Reaction: def register_subclasses(cls): for c in cls.__subclasses__(): rxn_type = getattr(c, "_reaction_type") + if getattr(c, "_dual"): + # registry needs to contain both updated and "-legacy" variants + _reaction_class_registry[rxn_type] = c if getattr(c, "_has_legacy", False): rxn_type += "-legacy" _reaction_class_registry[rxn_type] = c @@ -1067,29 +1071,55 @@ cdef class ChemicallyActivatedReaction(FalloffReaction): _reaction_type = "chemically-activated" -cdef class PlogReaction2(Reaction): +cdef class PlogReaction(Reaction): """ A pressure-dependent reaction parameterized by logarithmically interpolating between Arrhenius rate expressions at various pressures. - .. deprecated:: 2.6 + An example for the definition of an `PlogReaction` object is given as:: - This class is replaced by `PlogReaction` and only used by CTI/XML. The - implementation uses the legacy framework for reaction rate evaluations used - by Cantera 2.5.1 and earlier. Refer to `PlogReaction` for new behavior. + rxn = PlogReaction( + equation="H2 + O2 <=> 2 OH", + rate={"rate-constants": + [{"P": 1013.25, "A": 1.2124e+16, "b": -0.5779, "Ea": 45491376.8}, + {"P": 101325., "A": 4.9108e+31, "b": -4.8507, "Ea": 103649395.2}, + {"P": 1013250., "A": 1.2866e+47, "b": -9.0246, "Ea": 166508556.0}, + {"P": 10132500., "A": 5.9632e+56, "b": -11.529, "Ea": 220076726.4}]}, + kinetics=gas) + + The YAML description corresponding to this reaction is:: + + equation: H2 + O2 <=> 2 OH + type: pressure-dependent-Arrhenius + rate-constants: + - {P: 0.01 atm, A: 1.2124e+16, b: -0.5779, Ea: 1.08727e+04 cal/mol} + - {P: 1.0 atm, A: 4.9108e+31, b: -4.8507, Ea: 2.47728e+04 cal/mol} + - {P: 10.0 atm, A: 1.2866e+47, b: -9.0246, Ea: 3.97965e+04 cal/mol} + - {P: 100.0 atm, A: 5.9632e+56, b: -11.529, Ea: 5.25996e+04 cal/mol}or. """ _reaction_type = "pressure-dependent-Arrhenius" _has_legacy = True + _dual = True + + cdef CxxPlogReaction3* pr(self): + if not self.uses_legacy: + return self.reaction + raise AttributeError("Rate object accessor not defined for legacy implementation") def __init__(self, equation=None, rate=None, Kinetics kinetics=None, - init=True, **kwargs): + init=True, legacy=False, **kwargs): if init and equation and kinetics: - rxn_type = self._reaction_type + "-legacy" + if legacy: + rxn_type = self._reaction_type + "-legacy" + else: + rxn_type = self._reaction_type spec = {"equation": equation, "type": rxn_type} if isinstance(rate, dict): spec.update(rate) + elif not legacy and (isinstance(rate, PlogRate) or rate is None): + pass else: raise TypeError("Invalid rate definition") @@ -1097,7 +1127,21 @@ cdef class PlogReaction2(Reaction): deref(kinetics.kinetics)) self.reaction = self._reaction.get() - property rates: + if not legacy and isinstance(rate, PlogRate): + self.rate = rate + + property rate: + """ Get/Set the `PlogRate` rate coefficients for this reaction. """ + def __get__(self): + if self.uses_legacy: + raise AttributeError("Legacy implementation does not use rate property.") + return PlogRate.wrap(self.pr().rate()) + def __set__(self, PlogRate rate): + if self.uses_legacy: + raise AttributeError("Legacy implementation does not use rate property.") + self.pr().setRate(rate._base) + + property _legacy_rates: """ Get/Set the rate coefficients for this reaction, which are given as a list of (pressure, `Arrhenius`) tuples. @@ -1123,7 +1167,38 @@ cdef class PlogReaction2(Reaction): cdef CxxPlogReaction2* r = self.reaction r.rate = CxxPlog(ratemap) - def __call__(self, float T, float P): + property rates: + """ + Get/Set the rate coefficients for this reaction, which are given as a + list of (pressure, `Arrhenius`) tuples. + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `PlogRate.rates`. + """ + def __get__(self): + if self.uses_legacy: + return self._legacy_rates + + warnings.warn( + self._deprecation_warning("rates"), DeprecationWarning) + return self.rate.rates + + def __set__(self, rates): + if self.uses_legacy: + self._legacy_rates = rates + return + + warnings.warn("Property 'rates' to be removed after Cantera 2.6. " + "Setter is replaceable by assigning a new 'PlogRate' object created " + "from rates to the rate property.", DeprecationWarning) + warnings.warn( + self._deprecation_warning("rates"), DeprecationWarning) + rate_ = self.rate + rate_.rates = rates + self.rate = rate_ + + def _legacy_call(self, float T, float P): cdef CxxPlogReaction2* r = self.reaction cdef double logT = np.log(T) cdef double recipT = 1/T @@ -1132,6 +1207,19 @@ cdef class PlogReaction2(Reaction): r.rate.update_C(&logP) return r.rate.updateRC(logT, recipT) + def __call__(self, float T, float P): + """ + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaceable by call to `rate` property. + """ + if self.uses_legacy: + return self._legacy_call(T, P) + + warnings.warn( + self._deprecation_warning("__call__", "method"), DeprecationWarning) + return self.rate(T, P) + cdef class ChebyshevReaction2(Reaction): """ @@ -1655,99 +1743,6 @@ cdef class ThreeBodyReaction(ElementaryReaction): return self.thirdbody().efficiency(stringify(species)) -cdef class PlogReaction(Reaction3): - """ - A pressure-dependent reaction parameterized by logarithmically interpolating - between Arrhenius rate expressions at various pressures. - - An example for the definition of an `PlogReaction` object is given as:: - - rxn = PlogReaction( - equation='H2 + O2 <=> 2 OH', - rate={"rate-constants": - [{'P': 1013.25, 'A': 1.2124e+16, 'b': -0.5779, 'Ea': 45491376.8}, - {'P': 101325., 'A': 4.9108e+31, 'b': -4.8507, 'Ea': 103649395.2}, - {'P': 1013250., 'A': 1.2866e+47, 'b': -9.0246, 'Ea': 166508556.0}, - {'P': 10132500., 'A': 5.9632e+56, 'b': -11.529, 'Ea': 220076726.4}]}, - kinetics=gas) - - The YAML description corresponding to this reaction is:: - - equation: H2 + O2 <=> 2 OH - type: pressure-dependent-Arrhenius - rate-constants: - - {P: 0.01 atm, A: 1.2124e+16, b: -0.5779, Ea: 1.08727e+04 cal/mol} - - {P: 1.0 atm, A: 4.9108e+31, b: -4.8507, Ea: 2.47728e+04 cal/mol} - - {P: 10.0 atm, A: 1.2866e+47, b: -9.0246, Ea: 3.97965e+04 cal/mol} - - {P: 100.0 atm, A: 5.9632e+56, b: -11.529, Ea: 5.25996e+04 cal/mol} - """ - _reaction_type = "pressure-dependent-Arrhenius" - - cdef CxxPlogReaction3* pr(self): - return self.reaction - - def __init__(self, equation=None, rate=None, Kinetics kinetics=None, - init=True, **kwargs): - - if init and equation and kinetics: - - spec = {"equation": equation, "type": self._reaction_type} - if isinstance(rate, dict): - spec.update(rate) - elif isinstance(rate, PlogRate) or rate is None: - pass - else: - raise TypeError("Invalid rate definition") - - self._reaction = CxxNewReaction(dict_to_anymap(spec), - deref(kinetics.kinetics)) - self.reaction = self._reaction.get() - - if isinstance(rate, PlogRate): - self.rate = rate - - property rate: - """ Get/Set the `PlogRate` rate coefficients for this reaction. """ - def __get__(self): - return PlogRate.wrap(self.pr().rate()) - def __set__(self, PlogRate rate): - self.pr().setRate(rate._base) - - property rates: - """ - Get/Set the rate coefficients for this reaction, which are given as a - list of (pressure, `Arrhenius`) tuples. - - .. deprecated:: 2.6 - To be deprecated with version 2.6, and removed thereafter. - Replaced by property `PlogRate.rates`. - """ - def __get__(self): - warnings.warn( - self._deprecation_warning("rates"), DeprecationWarning) - return self.rate.rates - - def __set__(self, rates): - warnings.warn("Property 'rates' to be removed after Cantera 2.6. " - "Setter is replaceable by assigning a new 'PlogRate' object created " - "from rates to the rate property.", DeprecationWarning) - warnings.warn( - self._deprecation_warning("rates"), DeprecationWarning) - rate_ = self.rate - rate_.rates = rates - self.rate = rate_ - - def __call__(self, float T, float P): - """ - .. deprecated:: 2.6 - To be deprecated with version 2.6, and removed thereafter. - Replaceable by call to `rate` property. - """ - warnings.warn( - self._deprecation_warning("__call__", "method"), DeprecationWarning) - return self.rate(T, P) - - cdef class ChebyshevReaction(Reaction3): """ A pressure-dependent reaction parameterized by a bivariate Chebyshev diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index f93387aae1a..f20aeb172dd 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -350,16 +350,17 @@ def test_rate(self): def test_from_parts(self): # check instantiation from parts (reactants, products, rate expression) - if not hasattr(self._cls, "rate"): + if not self._rate_obj: return orig = self.gas.reaction(self._index) - rxn = self._cls(orig.reactants, orig.products) + rxn = self._cls(orig.reactants, orig.products, legacy=self._legacy) rxn.rate = self._rate_obj self.check_rxn(rxn) def test_from_dict(self): # check instantiation from keywords / rate defined by dictionary - rxn = self._cls(equation=self._equation, rate=self._rate, kinetics=self.gas, **self._kwargs) + rxn = self._cls(equation=self._equation, rate=self._rate, kinetics=self.gas, + legacy=self._legacy, **self._kwargs) self.check_rxn(rxn) def test_from_yaml(self): @@ -373,7 +374,8 @@ def test_from_rate(self): # check instantiation from keywords / rate provided as object if self._rate_obj is None: return - rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) + rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, + legacy=self._legacy, **self._kwargs) self.check_rxn(rxn) def test_add_rxn(self): @@ -384,20 +386,23 @@ def test_add_rxn(self): species=self.species, reactions=[]) gas2.TPX = self.gas.TPX - rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) + rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, + legacy=self._legacy, **self._kwargs) gas2.add_reaction(rxn) self.check_solution(gas2) def test_raises_invalid_rate(self): # check exception for instantiation from keywords / invalid rate with self.assertRaises(TypeError): - rxn = self._cls(equation=self._equation, rate=(), kinetics=self.gas, **self._kwargs) + rxn = self._cls(equation=self._equation, rate=(), kinetics=self.gas, + legacy=self._legacy, **self._kwargs) def test_no_rate(self): # check behavior for instantiation from keywords / no rate - if not hasattr(self._cls, "rate"): + if self._rate_obj is None: return - rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) + rxn = self._cls(equation=self._equation, kinetics=self.gas, + legacy=self._legacy, **self._kwargs) if self._legacy: self.assertNear(rxn.rate(self.gas.T), 0.) else: @@ -414,7 +419,8 @@ def test_replace_rate(self): # check replacing reaction rate expression if self._rate_obj is None: return - rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) + rxn = self._cls(equation=self._equation, kinetics=self.gas, + legacy=self._legacy, **self._kwargs) rxn.rate = self._rate_obj self.check_rxn(rxn) @@ -422,10 +428,12 @@ def test_roundtrip(self): # check round-trip instantiation via input_data if self._legacy: return - rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) + rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, + legacy=self._legacy, **self._kwargs) input_data = rxn.rate.input_data rate_obj = rxn.rate.__class__(input_data=input_data) - rxn2 = self._cls(equation=self._equation, rate=rate_obj, kinetics=self.gas, **self._kwargs) + rxn2 = self._cls(equation=self._equation, rate=rate_obj, kinetics=self.gas, + legacy=self._legacy, **self._kwargs) self.check_rxn(rxn2) def check_equal(self, one, two): @@ -500,7 +508,8 @@ class TestElementary(ReactionTests, utilities.CanteraTest): def test_arrhenius(self): # test assigning Arrhenius rate rate = ct.Arrhenius(self._rate["A"], self._rate["b"], self._rate["Ea"]) - rxn = self._cls(equation=self._equation, kinetics=self.gas, **self._kwargs) + rxn = self._cls(equation=self._equation, kinetics=self.gas, + legacy=self._legacy, **self._kwargs) if self._legacy: rxn.rate = rate else: @@ -554,7 +563,8 @@ def test_rate(self): def test_efficiencies(self): # check efficiencies - rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, **self._kwargs) + rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, + legacy=self._legacy, **self._kwargs) self.assertEqual(rxn.efficiencies, self._kwargs["efficiencies"]) @@ -590,7 +600,8 @@ class TestImplicitThreeBody(TestThreeBody): def test_efficiencies(self): # overload of default tester - rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas) + rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, + legacy=self._legacy) self.assertEqual(rxn.efficiencies, {"O2": 1.}) self.assertEqual(rxn.default_efficiency, 0.) @@ -675,7 +686,7 @@ def test_deprecated_setters(self): class TestPlog2(TestPlog): # test legacy version of Plog reaction - _cls = ct.PlogReaction2 + _cls = ct.PlogReaction _type = "pressure-dependent-Arrhenius-legacy" _legacy = True _rate_obj = None From 1b9faf96e3cadd2bdfdca203a7ad0d965515cf70 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 2 Jun 2021 09:59:08 -0500 Subject: [PATCH 78/84] [Kinetics] Make Python ChebyshevReaction agnostic of framework --- interfaces/cython/cantera/reaction.pyx | 403 +++++++++--------- .../cython/cantera/test/test_reaction.py | 2 +- 2 files changed, 202 insertions(+), 203 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 05b502a5b10..1ebb3b76705 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -260,19 +260,19 @@ cdef class ChebyshevRate(_ReactionRate): def __get__(self): return self.rate.Pmax() - property nPressure: + property n_pressure: """ Number of pressures over which the Chebyshev fit is computed """ def __get__(self): return self.rate.nPressure() - property nTemperature: + property n_temperature: """ Number of temperatures over which the Chebyshev fit is computed """ def __get__(self): return self.rate.nTemperature() property coeffs: """ - 2D array of Chebyshev coefficients of size `(nTemperature, nPressure)`. + 2D array of Chebyshev coefficients of size `(n_temperature, n_pressure)`. """ def __get__(self): c = np.fromiter(self.rate.coeffs(), np.double) @@ -1102,9 +1102,9 @@ cdef class PlogReaction(Reaction): _dual = True cdef CxxPlogReaction3* pr(self): - if not self.uses_legacy: - return self.reaction - raise AttributeError("Rate object accessor not defined for legacy implementation") + if self.uses_legacy: + raise AttributeError("Rate object accessor not defined for legacy implementation") + return self.reaction def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, legacy=False, **kwargs): @@ -1221,31 +1221,58 @@ cdef class PlogReaction(Reaction): return self.rate(T, P) -cdef class ChebyshevReaction2(Reaction): +cdef class ChebyshevReaction(Reaction): """ A pressure-dependent reaction parameterized by a bivariate Chebyshev polynomial in temperature and pressure. - .. deprecated:: 2.6 + An example for the definition of an `PlogReaction` object is given as:: - This class is replaced by `ChebyshevReaction` and only used by CTI/XML. The - implementation uses the legacy framework for reaction rate evaluations used - by Cantera 2.5.1 and earlier. Refer to `ChebyshevReaction` for new behavior. + rxn = ChebyshevReaction( + equation="HO2 <=> OH + O", + rate={"Tmin": 290.0, "Tmax": 3000.0, + "Pmin": 1e3, "Pmax": 1e8, + "data": [[8.2883, -1.1397, -0.12059, 0.016034], + [1.9764, 1.0037, 7.2865e-03, -0.030432], + [0.3177, 0.26889, 0.094806, -7.6385e-03]]}, + kinetics=gas) + + The YAML description corresponding to this reaction is:: + + equation: HO2 <=> OH + O + type: Chebyshev + temperature-range: [290.0, 3000.0] + pressure-range: [1.e-03 bar, 10. bar] + data: + - [8.2883, -1.1397, -0.12059, 0.016034] + - [1.9764, 1.0037, 7.2865e-03, -0.030432] + - [0.3177, 0.26889, 0.094806, -7.6385e-03] """ _reaction_type = "Chebyshev" _has_legacy = True + _dual = True + + cdef CxxChebyshevReaction3* cr(self): + if self.uses_legacy: + raise AttributeError("Rate object accessor not defined for legacy implementation") + return self.reaction def __init__(self, equation=None, rate=None, Kinetics kinetics=None, - init=True, **kwargs): + init=True, legacy=False, **kwargs): if init and equation and kinetics: - rxn_type = self._reaction_type + "-legacy" + if legacy: + rxn_type = self._reaction_type + "-legacy" + else: + rxn_type = self._reaction_type spec = {"equation": equation, "type": rxn_type} if isinstance(rate, dict): - spec['temperature-range'] = [rate['Tmin'], rate['Tmax']] - spec['pressure-range'] = [rate['Pmin'], rate['Pmax']] - spec['data'] = rate['data'] + spec["temperature-range"] = [rate["Tmin"], rate["Tmax"]] + spec["pressure-range"] = [rate["Pmin"], rate["Pmax"]] + spec["data"] = rate["data"] + elif not legacy and (isinstance(rate, ChebyshevRate) or rate is None): + pass else: raise TypeError("Invalid rate definition") @@ -1253,52 +1280,172 @@ cdef class ChebyshevReaction2(Reaction): deref(kinetics.kinetics)) self.reaction = self._reaction.get() - property Tmin: + if not legacy and isinstance(rate, ChebyshevRate): + self.rate = rate + + property rate: + """ Get/Set the `ChebyshevRate` rate coefficients for this reaction. """ + def __get__(self): + if self.uses_legacy: + raise AttributeError("Legacy implementation does not use rate property.") + return ChebyshevRate.wrap(self.cr().rate()) + def __set__(self, ChebyshevRate rate): + if self.uses_legacy: + raise AttributeError("Legacy implementation does not use rate property.") + self.cr().setRate(rate._base) + + property _legacy_Tmin: """ Minimum temperature [K] for the Chebyshev fit """ def __get__(self): cdef CxxChebyshevReaction2* r = self.reaction return r.rate.Tmin() - property Tmax: + property Tmin: + """ + Minimum temperature [K] for the Chebyshev fit + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ChebyshevRate.Tmin`. + """ + def __get__(self): + if self.uses_legacy: + return self._legacy_Tmin + warnings.warn( + self._deprecation_warning("Tmin"), DeprecationWarning) + return self.rate.Tmin + + property _legacy_Tmax: """ Maximum temperature [K] for the Chebyshev fit """ def __get__(self): cdef CxxChebyshevReaction2* r = self.reaction return r.rate.Tmax() - property Pmin: + property Tmax: + """ + Maximum temperature [K] for the Chebyshev fit + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ChebyshevRate.Tmax`. + """ + def __get__(self): + if self.uses_legacy: + return self._legacy_Tmax + warnings.warn( + self._deprecation_warning("Tmax"), DeprecationWarning) + return self.rate.Tmax + + + property _legacy_Pmin: """ Minimum pressure [Pa] for the Chebyshev fit """ def __get__(self): cdef CxxChebyshevReaction2* r = self.reaction return r.rate.Pmin() - property Pmax: + property Pmin: + """ + Minimum pressure [Pa] for the Chebyshev fit + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ChebyshevRate.Pmin`. + """ + def __get__(self): + if self.uses_legacy: + return self._legacy_Pmin + warnings.warn( + self._deprecation_warning("Pmin"), DeprecationWarning) + return self.rate.Pmin + + + property _legacy_Pmax: """ Maximum pressure [Pa] for the Chebyshev fit """ def __get__(self): cdef CxxChebyshevReaction2* r = self.reaction return r.rate.Pmax() - property nPressure: + property Pmax: + """ Maximum pressure [Pa] for the Chebyshev fit + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ChebyshevRate.Pmax`. + """ + def __get__(self): + if self.uses_legacy: + return self._legacy_Pmax + warnings.warn( + self._deprecation_warning("Pmax"), DeprecationWarning) + return self.rate.Pmax + + property _legacy_nPressure: """ Number of pressures over which the Chebyshev fit is computed """ def __get__(self): cdef CxxChebyshevReaction2* r = self.reaction return r.rate.nPressure() - property nTemperature: + property nPressure: + """ + Number of pressures over which the Chebyshev fit is computed + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ChebyshevRate.nPressure`. + """ + def __get__(self): + if self.uses_legacy: + return self._legacy_nPressure + warnings.warn( + self._deprecation_warning("nPressure"), DeprecationWarning) + return self.rate.n_pressure + + property _legacy_nTemperature: """ Number of temperatures over which the Chebyshev fit is computed """ def __get__(self): cdef CxxChebyshevReaction2* r = self.reaction return r.rate.nTemperature() - property coeffs: + property nTemperature: """ - 2D array of Chebyshev coefficients of size `(nTemperature, nPressure)`. + Number of temperatures over which the Chebyshev fit is computed + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ChebyshevRate.nTemperature`. + """ + def __get__(self): + if self.uses_legacy: + return self._legacy_nTemperature + warnings.warn( + self._deprecation_warning("nTemperature"), DeprecationWarning) + return self.rate.n_temperature + + property _legacy_coeffs: + """ + 2D array of Chebyshev coefficients of size `(n_temperature, n_pressure)`. """ def __get__(self): cdef CxxChebyshevReaction2* r = self.reaction c = np.fromiter(r.rate.coeffs(), np.double) return c.reshape((r.rate.nTemperature(), r.rate.nPressure())) - def set_parameters(self, Tmin, Tmax, Pmin, Pmax, coeffs): + property coeffs: + """ + 2D array of Chebyshev coefficients of size `(n_temperature, n_pressure)`. + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ChebyshevRate.coeffs`. + """ + def __get__(self): + if self.uses_legacy: + return self._legacy_coeffs + warnings.warn( + self._deprecation_warning("coeffs"), DeprecationWarning) + return self.rate.coeffs + + def _legacy_set_parameters(self, Tmin, Tmax, Pmin, Pmax, coeffs): """ Simultaneously set values for `Tmin`, `Tmax`, `Pmin`, `Pmax`, and `coeffs`. @@ -1316,7 +1463,23 @@ cdef class ChebyshevReaction2(Reaction): r.rate = CxxChebyshev(Tmin, Tmax, Pmin, Pmax, data) - def __call__(self, float T, float P): + def set_parameters(self, Tmin, Tmax, Pmin, Pmax, coeffs): + """ + Simultaneously set values for `Tmin`, `Tmax`, `Pmin`, `Pmax`, and + `coeffs`. + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by `ChebyshevRate` constructor. + """ + if self.uses_legacy: + return self._legacy_set_parameters(Tmin, Tmax, Pmin, Pmax, coeffs) + warnings.warn("Method 'set_parameters' to be removed after Cantera 2.6. " + "Method is replaceable by assigning a new 'ChebyshevRate' object to the " + "rate property.", DeprecationWarning) + self.rate = ChebyshevRate(Tmin, Tmax, Pmin, Pmax, coeffs) + + def _legacy_call(self, float T, float P): cdef CxxChebyshevReaction2* r = self.reaction cdef double logT = np.log(T) cdef double recipT = 1/T @@ -1325,6 +1488,18 @@ cdef class ChebyshevReaction2(Reaction): r.rate.update_C(&logP) return r.rate.updateRC(logT, recipT) + def __call__(self, float T, float P): + """ + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaceable by call to `rate` property. + """ + if self.uses_legacy: + return self._legacy_call(T, P) + warnings.warn( + self._deprecation_warning("__call__", "method"), DeprecationWarning) + return self.rate(T, P) + cdef class BlowersMasel: """ @@ -1743,182 +1918,6 @@ cdef class ThreeBodyReaction(ElementaryReaction): return self.thirdbody().efficiency(stringify(species)) -cdef class ChebyshevReaction(Reaction3): - """ - A pressure-dependent reaction parameterized by a bivariate Chebyshev - polynomial in temperature and pressure. - - An example for the definition of an `PlogReaction` object is given as:: - - rxn = ChebyshevReaction( - equation="HO2 <=> OH + O", - rate={"Tmin": 290.0, "Tmax": 3000.0, - "Pmin": 1e3, "Pmax": 1e8, - "data": [[8.2883, -1.1397, -0.12059, 0.016034], - [1.9764, 1.0037, 7.2865e-03, -0.030432], - [0.3177, 0.26889, 0.094806, -7.6385e-03]]}, - kinetics=gas) - - The YAML description corresponding to this reaction is:: - - equation: HO2 <=> OH + O - type: Chebyshev - temperature-range: [290.0, 3000.0] - pressure-range: [1.e-03 bar, 10. bar] - data: - - [8.2883, -1.1397, -0.12059, 0.016034] - - [1.9764, 1.0037, 7.2865e-03, -0.030432] - - [0.3177, 0.26889, 0.094806, -7.6385e-03] - """ - _reaction_type = "Chebyshev" - - cdef CxxChebyshevReaction3* cr(self): - return self.reaction - - def __init__(self, equation=None, rate=None, Kinetics kinetics=None, - init=True, **kwargs): - - if init and equation and kinetics: - - spec = {"equation": equation, "type": self._reaction_type} - if isinstance(rate, dict): - spec["temperature-range"] = [rate["Tmin"], rate["Tmax"]] - spec["pressure-range"] = [rate["Pmin"], rate["Pmax"]] - spec["data"] = rate["data"] - elif isinstance(rate, ChebyshevRate) or rate is None: - pass - else: - raise TypeError("Invalid rate definition") - - self._reaction = CxxNewReaction(dict_to_anymap(spec), - deref(kinetics.kinetics)) - self.reaction = self._reaction.get() - - if isinstance(rate, ChebyshevRate): - self.rate = rate - - property rate: - """ Get/Set the `ChebyshevRate` rate coefficients for this reaction. """ - def __get__(self): - return ChebyshevRate.wrap(self.cr().rate()) - def __set__(self, ChebyshevRate rate): - self.cr().setRate(rate._base) - - property Tmin: - """ - Minimum temperature [K] for the Chebyshev fit - - .. deprecated:: 2.6 - To be deprecated with version 2.6, and removed thereafter. - Replaced by property `ChebyshevRate.Tmin`. - """ - def __get__(self): - warnings.warn( - self._deprecation_warning("Tmin"), DeprecationWarning) - return self.rate.Tmin - - property Tmax: - """ - Maximum temperature [K] for the Chebyshev fit - - .. deprecated:: 2.6 - To be deprecated with version 2.6, and removed thereafter. - Replaced by property `ChebyshevRate.Tmax`. - """ - def __get__(self): - warnings.warn( - self._deprecation_warning("Tmax"), DeprecationWarning) - return self.rate.Tmax - - property Pmin: - """ - Minimum pressure [Pa] for the Chebyshev fit - - .. deprecated:: 2.6 - To be deprecated with version 2.6, and removed thereafter. - Replaced by property `ChebyshevRate.Pmin`. - """ - def __get__(self): - warnings.warn( - self._deprecation_warning("Pmin"), DeprecationWarning) - return self.rate.Pmin - - property Pmax: - """ Maximum pressure [Pa] for the Chebyshev fit - - .. deprecated:: 2.6 - To be deprecated with version 2.6, and removed thereafter. - Replaced by property `ChebyshevRate.Pmax`. - """ - def __get__(self): - warnings.warn( - self._deprecation_warning("Pmax"), DeprecationWarning) - return self.rate.Pmax - - property nPressure: - """ - Number of pressures over which the Chebyshev fit is computed - - .. deprecated:: 2.6 - To be deprecated with version 2.6, and removed thereafter. - Replaced by property `ChebyshevRate.nPressure`. - """ - def __get__(self): - warnings.warn( - self._deprecation_warning("nPressure"), DeprecationWarning) - return self.rate.nPressure - - property nTemperature: - """ - Number of temperatures over which the Chebyshev fit is computed - - .. deprecated:: 2.6 - To be deprecated with version 2.6, and removed thereafter. - Replaced by property `ChebyshevRate.nTemperature`. - """ - def __get__(self): - warnings.warn( - self._deprecation_warning("nTemperature"), DeprecationWarning) - return self.rate.nTemperature - - property coeffs: - """ - 2D array of Chebyshev coefficients of size `(nTemperature, nPressure)`. - - .. deprecated:: 2.6 - To be deprecated with version 2.6, and removed thereafter. - Replaced by property `ChebyshevRate.coeffs`. - """ - def __get__(self): - warnings.warn( - self._deprecation_warning("coeffs"), DeprecationWarning) - return self.rate.coeffs - - def set_parameters(self, Tmin, Tmax, Pmin, Pmax, coeffs): - """ - Simultaneously set values for `Tmin`, `Tmax`, `Pmin`, `Pmax`, and - `coeffs`. - - .. deprecated:: 2.6 - To be deprecated with version 2.6, and removed thereafter. - Replaced by `ChebyshevRate` constructor. - """ - warnings.warn("Method 'set_parameters' to be removed after Cantera 2.6. " - "Method is replaceable by assigning a new 'ChebyshevRate' object to the " - "rate property.", DeprecationWarning) - self.rate = ChebyshevRate(Tmin, Tmax, Pmin, Pmax, coeffs) - - def __call__(self, float T, float P): - """ - .. deprecated:: 2.6 - To be deprecated with version 2.6, and removed thereafter. - Replaceable by call to `rate` property. - """ - warnings.warn( - self._deprecation_warning("__call__", "method"), DeprecationWarning) - return self.rate(T, P) - - cdef class CustomReaction(Reaction3): """ A reaction which follows mass-action kinetics with a custom reaction rate. diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index f20aeb172dd..83b1b5b6dd5 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -740,7 +740,7 @@ def setUpClass(obj): class TestChebyshev2(TestChebyshev): # test legacy version of Chebyshev reaction - _cls = ct.ChebyshevReaction2 + _cls = ct.ChebyshevReaction _type = "Chebyshev-legacy" _legacy = True _rate_obj = None From 2af12d08f850dd6beb4da5b85dd984d35c5a6ae2 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 2 Jun 2021 10:55:59 -0500 Subject: [PATCH 79/84] [Kinetics] Make remaining Python reactions agnostic of framework --- interfaces/cython/cantera/_cantera.pxd | 5 +- interfaces/cython/cantera/reaction.pyx | 388 ++++++++---------- .../cython/cantera/test/test_convert.py | 10 +- .../cython/cantera/test/test_kinetics.py | 4 +- .../cython/cantera/test/test_reaction.py | 10 +- 5 files changed, 180 insertions(+), 237 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 98ea6645af3..4ffb035bca9 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -1174,10 +1174,7 @@ cdef class Reaction: @staticmethod cdef wrap(shared_ptr[CxxReaction]) -cdef class Reaction3(Reaction): - pass - -cdef class CustomReaction(Reaction3): +cdef class CustomReaction(Reaction): cdef CustomRate _rate cdef class Arrhenius: diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 1ebb3b76705..a6ab6d3b748 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -367,13 +367,13 @@ cdef class Reaction: """ _reaction_type = "" _has_legacy = False - _dual = False # indicate whether legacy implementations are separate or merged + _hybrid = False # indicate whether legacy implementations are separate or merged - def __cinit__(self, reactants='', products='', init=True, legacy=False, **kwargs): + def __cinit__(self, reactants="", products="", init=True, legacy=False, **kwargs): if init: rxn_type = self._reaction_type - if (not self._dual and self._has_legacy) or (self._dual and legacy): + if (not self._hybrid and self._has_legacy) or (self._hybrid and legacy): rxn_type += "-legacy" self._reaction = CxxNewReaction(stringify((rxn_type))) self.reaction = self._reaction.get() @@ -392,7 +392,7 @@ cdef class Reaction: def register_subclasses(cls): for c in cls.__subclasses__(): rxn_type = getattr(c, "_reaction_type") - if getattr(c, "_dual"): + if getattr(c, "_hybrid"): # registry needs to contain both updated and "-legacy" variants _reaction_class_registry[rxn_type] = c if getattr(c, "_has_legacy", False): @@ -678,7 +678,7 @@ cdef class Reaction: self.reaction.input.clear() def __repr__(self): - return '<{}: {}>'.format(self.__class__.__name__, self.equation) + return f"<{self.__class__.__name__}: {self.equation}>" def __str__(self): return self.equation @@ -763,37 +763,50 @@ cdef copyArrhenius(CxxArrhenius* rate): return r -cdef class ElementaryReaction2(Reaction): +cdef class ElementaryReaction(Reaction): """ A reaction which follows mass-action kinetics with a modified Arrhenius reaction rate. - An example for the definition of a `ElementaryReaction2` object is given as:: + An example for the definition of an `ElementaryReaction` object is given as:: - rxn = ElementaryReaction2(equation='H2 + O <=> H + OH', - rate={'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07}, - kinetics=gas) + rxn = ElementaryReaction( + equation="O + H2 <=> H + OH", + rate={"A": 38.7, "b": 2.7, "Ea": 2.619184e+07}, + kinetics=gas) - .. deprecated:: 2.6 + The YAML description corresponding to this reaction is:: - This class is replaced by `ElementaryReaction` and only used by CTI/XML. The - implementation uses the legacy framework for reaction rate evaluations used - by Cantera 2.5.1 and earlier. Refer to `ElementaryReaction` for new behavior. + equation: O + H2 <=> H + OH + rate-constant: {A: 3.87e+04 cm^3/mol/s, b: 2.7, Ea: 6260.0 cal/mol} """ _reaction_type = "elementary" _has_legacy = True + _hybrid = True + + cdef CxxElementaryReaction3* er(self): + if self.uses_legacy: + raise AttributeError("Rate object accessor not defined for legacy implementation") + return self.reaction def __init__(self, equation=None, rate=None, Kinetics kinetics=None, - init=True, **kwargs): + init=True, legacy=False, **kwargs): if init and equation and kinetics: - rxn_type = self._reaction_type + "-legacy" + if legacy: + rxn_type = self._reaction_type + "-legacy" + else: + rxn_type = self._reaction_type spec = {"equation": equation, "type": rxn_type} if isinstance(rate, dict): - spec['rate-constant'] = rate - elif isinstance(rate, Arrhenius) or rate is None: - spec['rate-constant'] = dict.fromkeys(['A', 'b', 'Ea'], 0.) + spec["rate-constant"] = rate + elif legacy and (isinstance(rate, Arrhenius) or rate is None): + spec["rate-constant"] = dict.fromkeys(["A", "b", "Ea"], 0.) + elif rate is None: + pass + elif not legacy and isinstance(rate, (Arrhenius, ArrheniusRate)): + pass else: raise TypeError("Invalid rate definition") @@ -801,10 +814,12 @@ cdef class ElementaryReaction2(Reaction): deref(kinetics.kinetics)) self.reaction = self._reaction.get() - if isinstance(rate, Arrhenius): + if legacy and isinstance(rate, Arrhenius): + self.rate = rate + elif not legacy and isinstance(rate, (ArrheniusRate, Arrhenius)): self.rate = rate - property rate: + property _legacy_rate: """ Get/Set the `Arrhenius` rate coefficient for this reaction. """ def __get__(self): cdef CxxElementaryReaction2* r = self.reaction @@ -813,7 +828,33 @@ cdef class ElementaryReaction2(Reaction): cdef CxxElementaryReaction2* r = self.reaction r.rate = deref(rate.rate) - property allow_negative_pre_exponential_factor: + property rate: + """ Get/Set the `ArrheniusRate` rate coefficient for this reaction. """ + def __get__(self): + if self.uses_legacy: + return self._legacy_rate + + return ArrheniusRate.wrap(self.er().rate()) + def __set__(self, rate): + if self.uses_legacy: + self._legacy_rate = rate + return + + cdef ArrheniusRate rate3 + if isinstance(rate, ArrheniusRate): + rate3 = rate + elif isinstance(rate, Arrhenius): + warnings.warn("Setting the rate using an 'Arrhenius' object is " + "deprecated and will be removed after Cantera 2.6. The argument " + "type is replaceable by 'ArrheniusRate'.", DeprecationWarning) + rate3 = ArrheniusRate(rate.pre_exponential_factor, + rate.temperature_exponent, + rate.activation_energy) + else: + raise TypeError("Invalid rate definition") + self.er().setRate(rate3._base) + + property _legacy_allow_negative_pre_exponential_factor: """ Get/Set whether the rate coefficient is allowed to have a negative pre-exponential factor. @@ -825,47 +866,106 @@ cdef class ElementaryReaction2(Reaction): cdef CxxElementaryReaction2* r = self.reaction r.allow_negative_pre_exponential_factor = allow + property allow_negative_pre_exponential_factor: + """ + Get/Set whether the rate coefficient is allowed to have a negative + pre-exponential factor. + + .. deprecated:: 2.6 + To be deprecated with version 2.6, and removed thereafter. + Replaced by property `ArrheniusRate.allow_negative_pre_exponential_factor`. + """ + def __get__(self): + if self.uses_legacy: + return self._legacy_allow_negative_pre_exponential_factor + + attr = "allow_negative_pre_exponential_factor" + warnings.warn( + self._deprecation_warning(attr), DeprecationWarning) + return self.rate.allow_negative_pre_exponential_factor + def __set__(self, allow): + if self.uses_legacy: + self._legacy_allow_negative_pre_exponential_factor = allow + return + + attr = "allow_negative_pre_exponential_factor" + warnings.warn( + self._deprecation_warning(attr), DeprecationWarning) + self.rate.allow_negative_pre_exponential_factor = allow + -cdef class ThreeBodyReaction2(ElementaryReaction2): +cdef class ThreeBodyReaction(ElementaryReaction): """ A reaction with a non-reacting third body "M" that acts to add or remove energy from the reacting species. - .. deprecated:: 2.6 + An example for the definition of an `ThreeBodyReaction` object is given as:: + + rxn = ThreeBodyReaction( + equation="2 O + M <=> O2 + M", + type="three-body", + rate={"A": 1.2e+17, "b": -1.0, "Ea": 0.0}, + efficiencies={"H2": 2.4, "H2O": 15.4, "AR": 0.83}, + kinetics=gas) + + The YAML description corresponding to this reaction is:: - This class is replaced by `ThreeBodyReaction` and only used by CTI/XML. The - implementation uses the legacy framework for reaction rate evaluations used - by Cantera 2.5.1 and earlier. Refer to `ThreeBodyReaction` for new behavior. + equation: 2 O + M <=> O2 + M + type: three-body + rate-constant: {A: 1.2e+17 cm^6/mol^2/s, b: -1.0, Ea: 0.0 cal/mol} + efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} """ _reaction_type = "three-body" _has_legacy = True + _hybrid = True + + cdef CxxThreeBodyReaction3* tbr(self): + if self.uses_legacy: + raise AttributeError("Incorrect accessor for updated implementation") + return self.reaction + + cdef CxxThreeBodyReaction2* tbr2(self): + if not self.uses_legacy: + raise AttributeError("Incorrect accessor for legacy implementation") + return self.reaction + + cdef CxxThirdBody* thirdbody(self): + if self.uses_legacy: + raise AttributeError("Accessor not defined for legacy implementation") + return (self.tbr().thirdBody().get()) def __init__(self, equation=None, rate=None, efficiencies=None, - Kinetics kinetics=None, init=True, **kwargs): + Kinetics kinetics=None, legacy=False, init=True, **kwargs): if init and equation and kinetics: - rxn_type = self._reaction_type + "-legacy" + if legacy: + rxn_type = self._reaction_type + "-legacy" + else: + rxn_type = self._reaction_type spec = {"equation": equation, "type": rxn_type} if isinstance(rate, dict): - spec['rate-constant'] = rate - elif isinstance(rate, Arrhenius) or rate is None: - spec['rate-constant'] = dict.fromkeys(['A', 'b', 'Ea'], 0.) + spec["rate-constant"] = rate + elif legacy and (isinstance(rate, Arrhenius) or rate is None): + spec["rate-constant"] = dict.fromkeys(["A", "b", "Ea"], 0.) + elif rate is None: + pass + elif not legacy and isinstance(rate, (Arrhenius, ArrheniusRate)): + pass else: raise TypeError("Invalid rate definition") if isinstance(efficiencies, dict): - spec['efficiencies'] = efficiencies + spec["efficiencies"] = efficiencies self._reaction = CxxNewReaction(dict_to_anymap(spec), deref(kinetics.kinetics)) self.reaction = self._reaction.get() - if isinstance(rate, Arrhenius): + if legacy and isinstance(rate, Arrhenius): + self.rate = rate + elif not legacy and isinstance(rate, (Arrhenius, ArrheniusRate)): self.rate = rate - - cdef CxxThreeBodyReaction2* tbr(self): - return self.reaction property efficiencies: """ @@ -874,9 +974,14 @@ cdef class ThreeBodyReaction2(ElementaryReaction2): efficiencies. """ def __get__(self): - return comp_map_to_dict(self.tbr().third_body.efficiencies) + if self.uses_legacy: + return comp_map_to_dict(self.tbr2().third_body.efficiencies) + return comp_map_to_dict(self.thirdbody().efficiencies) def __set__(self, eff): - self.tbr().third_body.efficiencies = comp_map(eff) + if self.uses_legacy: + self.tbr2().third_body.efficiencies = comp_map(eff) + return + self.thirdbody().efficiencies = comp_map(eff) property default_efficiency: """ @@ -884,16 +989,23 @@ cdef class ThreeBodyReaction2(ElementaryReaction2): species used for species not in `efficiencies`. """ def __get__(self): - return self.tbr().third_body.default_efficiency + if self.uses_legacy: + return self.tbr2().third_body.default_efficiency + return self.thirdbody().default_efficiency def __set__(self, default_eff): - self.tbr().third_body.default_efficiency = default_eff + if self.uses_legacy: + self.tbr2().third_body.default_efficiency = default_eff + return + self.thirdbody().default_efficiency = default_eff def efficiency(self, species): """ Get the efficiency of the third body named *species* considering both the default efficiency and species-specific efficiencies. """ - return self.tbr().third_body.efficiency(stringify(species)) + if self.uses_legacy: + return self.tbr2().third_body.efficiency(stringify(species)) + return self.thirdbody().efficiency(stringify(species)) cdef class Falloff: @@ -1099,11 +1211,11 @@ cdef class PlogReaction(Reaction): """ _reaction_type = "pressure-dependent-Arrhenius" _has_legacy = True - _dual = True + _hybrid = True cdef CxxPlogReaction3* pr(self): if self.uses_legacy: - raise AttributeError("Rate object accessor not defined for legacy implementation") + raise AttributeError("Accessor not defined for legacy implementation") return self.reaction def __init__(self, equation=None, rate=None, Kinetics kinetics=None, @@ -1250,11 +1362,11 @@ cdef class ChebyshevReaction(Reaction): """ _reaction_type = "Chebyshev" _has_legacy = True - _dual = True + _hybrid = True cdef CxxChebyshevReaction3* cr(self): if self.uses_legacy: - raise AttributeError("Rate object accessor not defined for legacy implementation") + raise AttributeError("Accessor not defined for legacy implementation") return self.reaction def __init__(self, equation=None, rate=None, Kinetics kinetics=None, @@ -1605,10 +1717,16 @@ cdef class BlowersMaselReaction(Reaction): r.allow_negative_pre_exponential_factor = allow -cdef class InterfaceReaction(ElementaryReaction2): +cdef class InterfaceReaction(ElementaryReaction): """ A reaction occurring on an `Interface` (i.e. a surface or an edge) """ _reaction_type = "interface" _has_legacy = False + _hybrid = False + + property uses_legacy: + # legacy framework is implicitly used + def __get__(self): + return True property coverage_deps: """ @@ -1749,185 +1867,19 @@ cdef class BlowersMaselInterfaceReaction(BlowersMaselReaction): r.sticking_species = stringify(species) -cdef class Reaction3(Reaction): - """ Convenience class holding methods common to the Reaction3 framework """ - - pass - - -cdef class ElementaryReaction(Reaction3): - """ - A reaction which follows mass-action kinetics with a modified Arrhenius - reaction rate. - - An example for the definition of an `ElementaryReaction` object is given as:: - - rxn = ElementaryReaction( - equation='O + H2 <=> H + OH', - rate={'A': 38.7, 'b': 2.7, 'Ea': 2.619184e+07}, - kinetics=gas) - - The YAML description corresponding to this reaction is:: - - equation: O + H2 <=> H + OH - rate-constant: {A: 3.87e+04 cm^3/mol/s, b: 2.7, Ea: 6260.0 cal/mol} - """ - _reaction_type = "elementary" - - cdef CxxElementaryReaction3* er(self): - return self.reaction - - def __init__(self, equation=None, rate=None, - Kinetics kinetics=None, init=True, **kwargs): - - if init and equation and kinetics: - - spec = {"equation": equation, "type": self._reaction_type} - if isinstance(rate, dict): - spec["rate-constant"] = rate - elif isinstance(rate, ArrheniusRate) or rate is None: - pass - else: - raise TypeError("Invalid rate definition") - - self._reaction = CxxNewReaction(dict_to_anymap(spec), - deref(kinetics.kinetics)) - self.reaction = self._reaction.get() - - if isinstance(rate, (ArrheniusRate, Arrhenius)): - self.rate = rate - - property rate: - """ Get/Set the `ArrheniusRate` rate coefficient for this reaction. """ - def __get__(self): - return ArrheniusRate.wrap(self.er().rate()) - def __set__(self, rate): - cdef ArrheniusRate rate3 - if isinstance(rate, ArrheniusRate): - rate3 = rate - elif isinstance(rate, Arrhenius): - warnings.warn("Setting the rate using an 'Arrhenius' object is " - "deprecated and will be removed after Cantera 2.6. The argument " - "type is replaceable by 'ArrheniusRate'.", DeprecationWarning) - rate3 = ArrheniusRate(rate.pre_exponential_factor, - rate.temperature_exponent, - rate.activation_energy) - else: - raise TypeError("Invalid rate definition") - self.er().setRate(rate3._base) - - property allow_negative_pre_exponential_factor: - """ - Get/Set whether the rate coefficient is allowed to have a negative - pre-exponential factor. - - .. deprecated:: 2.6 - To be deprecated with version 2.6, and removed thereafter. - Replaced by property `ArrheniusRate.allow_negative_pre_exponential_factor`. - """ - def __get__(self): - attr = "allow_negative_pre_exponential_factor" - warnings.warn( - self._deprecation_warning(attr), DeprecationWarning) - return self.rate.allow_negative_pre_exponential_factor - def __set__(self, allow): - attr = "allow_negative_pre_exponential_factor" - warnings.warn( - self._deprecation_warning(attr), DeprecationWarning) - self.rate.allow_negative_pre_exponential_factor = allow - - -cdef class ThreeBodyReaction(ElementaryReaction): - """ - A reaction with a non-reacting third body "M" that acts to add or remove - energy from the reacting species. - - An example for the definition of an `ThreeBodyReaction` object is given as:: - - rxn = ThreeBodyReaction( - equation='2 O + M <=> O2 + M', - type='three-body', - rate={'A': 1.2e+17, 'b': -1.0, 'Ea': 0.0}, - efficiencies={'H2': 2.4, 'H2O': 15.4, 'AR': 0.83}, - kinetics=gas) - - The YAML description corresponding to this reaction is:: - - equation: 2 O + M <=> O2 + M - type: three-body - rate-constant: {A: 1.2e+17 cm^6/mol^2/s, b: -1.0, Ea: 0.0 cal/mol} - efficiencies: {H2: 2.4, H2O: 15.4, AR: 0.83} - """ - _reaction_type = "three-body" - - cdef CxxThreeBodyReaction3* tbr(self): - return self.reaction - - cdef CxxThirdBody* thirdbody(self): - return (self.tbr().thirdBody().get()) - - def __init__(self, equation=None, rate=None, efficiencies=None, - Kinetics kinetics=None, init=True, **kwargs): - - if init and equation and kinetics: - - spec = {"equation": equation, "type": self._reaction_type} - if isinstance(rate, dict): - spec["rate-constant"] = rate - elif isinstance(rate, ArrheniusRate) or rate is None: - pass - else: - raise TypeError("Invalid rate definition") - - if isinstance(efficiencies, dict): - spec["efficiencies"] = efficiencies - - self._reaction = CxxNewReaction(dict_to_anymap(spec), - deref(kinetics.kinetics)) - self.reaction = self._reaction.get() - - if isinstance(rate, ArrheniusRate): - self.rate = rate - - property efficiencies: - """ - Get/Set a `dict` defining non-default third-body efficiencies for this - reaction, where the keys are the species names and the values are the - efficiencies. - """ - def __get__(self): - return comp_map_to_dict(self.thirdbody().efficiencies) - def __set__(self, eff): - self.thirdbody().efficiencies = comp_map(eff) - - property default_efficiency: - """ - Get/Set the default third-body efficiency for this reaction, used for - species used for species not in `efficiencies`. - """ - def __get__(self): - return self.thirdbody().default_efficiency - def __set__(self, default_eff): - self.thirdbody().default_efficiency = default_eff - - def efficiency(self, species): - """ - Get the efficiency of the third body named *species* considering both - the default efficiency and species-specific efficiencies. - """ - return self.thirdbody().efficiency(stringify(species)) - - -cdef class CustomReaction(Reaction3): +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 given as:: rxn = CustomReaction( - equation='H2 + O <=> H + OH', + equation="H2 + O <=> H + OH", 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 + may be changed or removed without notice. """ _reaction_type = "custom-rate-function" diff --git a/interfaces/cython/cantera/test/test_convert.py b/interfaces/cython/cantera/test/test_convert.py index 9a93d5531ef..7ae4c871019 100644 --- a/interfaces/cython/cantera/test/test_convert.py +++ b/interfaces/cython/cantera/test/test_convert.py @@ -604,10 +604,7 @@ def checkConversion(self, basename, cls=ct.Solution, ctiphases=(), self.assertEqual(C.composition, Y.composition) for C, Y in zip(ctiPhase.reactions(), yamlPhase.reactions()): - if C.__class__.__name__.endswith('2'): - self.assertEqual(C.__class__.__name__[:-1], Y.__class__.__name__) - else: - self.assertEqual(C.__class__, Y.__class__) + self.assertEqual(C.__class__, Y.__class__) self.assertEqual(C.reactants, Y.reactants) self.assertEqual(C.products, Y.products) self.assertEqual(C.duplicate, Y.duplicate) @@ -854,10 +851,7 @@ def checkConversion(self, basename, cls=ct.Solution, ctmlphases=(), self.assertEqual(C.composition, Y.composition) for C, Y in zip(ctmlPhase.reactions(), yamlPhase.reactions()): - if C.__class__.__name__.endswith('2'): - self.assertEqual(C.__class__.__name__[:-1], Y.__class__.__name__) - else: - self.assertEqual(C.__class__, Y.__class__) + self.assertEqual(C.__class__, Y.__class__) self.assertEqual(C.reactants, Y.reactants) self.assertEqual(C.products, Y.products) self.assertEqual(C.duplicate, Y.duplicate) diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index ae5d8edd377..db2becc7634 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -877,7 +877,7 @@ def test_fromCti(self): r = ct.Reaction.fromCti('''three_body_reaction('2 O + M <=> O2 + M', [1.200000e+11, -1.0, 0.0], efficiencies='AR:0.83 H2:2.4 H2O:15.4')''') - self.assertTrue(isinstance(r, ct.ThreeBodyReaction2)) + self.assertTrue(isinstance(r, ct.ThreeBodyReaction)) self.assertEqual(r.reactants['O'], 2) self.assertEqual(r.products['O2'], 1) self.assertEqual(r.efficiencies['H2O'], 15.4) @@ -892,7 +892,7 @@ def test_fromXml(self): rxn_node = root.find('.//reaction[@id="0001"]') r = ct.Reaction.fromXml(ET.tostring(rxn_node)) - self.assertTrue(isinstance(r, ct.ThreeBodyReaction2)) + self.assertTrue(isinstance(r, ct.ThreeBodyReaction)) self.assertEqual(r.reactants['O'], 2) self.assertEqual(r.products['O2'], 1) self.assertEqual(r.efficiencies['H2O'], 15.4) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 83b1b5b6dd5..8a4e045d3a9 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -311,8 +311,8 @@ class ReactionTests: @classmethod def setUpClass(cls): utilities.CanteraTest.setUpClass() - cls.gas = ct.Solution('kineticsfromscratch.yaml', transport_model=None) - cls.gas.X = 'H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5' + cls.gas = ct.Solution("kineticsfromscratch.yaml", transport_model=None) + 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 = cls.gas.species() @@ -521,7 +521,7 @@ def test_arrhenius(self): class TestElementary2(TestElementary): # test legacy version of elementary reaction - _cls = ct.ElementaryReaction2 + _cls = ct.ElementaryReaction _type = "elementary-legacy" _legacy = True _rate_obj = ct.Arrhenius(38.7, 2.7, 2.619184e+07) @@ -552,7 +552,7 @@ class TestThreeBody(TestElementary): def test_from_parts(self): # overload default reaction creation from parts orig = self.gas.reaction(self._index) - rxn = self._cls(orig.reactants, orig.products) + rxn = self._cls(orig.reactants, orig.products, legacy=self._legacy) rxn.rate = self._rate_obj rxn.efficiencies = self._kwargs["efficiencies"] self.check_rxn(rxn) @@ -572,7 +572,7 @@ def test_efficiencies(self): class TestThreeBody2(TestThreeBody): # test legacy version of three-body reaction - _cls = ct.ThreeBodyReaction2 + _cls = ct.ThreeBodyReaction _type = "three-body-legacy" _legacy = True _rate_obj = ct.Arrhenius(1.2e11, -1., 0.) From 9d2db3aad975acdcc74bdc37efaa0f82b3f0f8a3 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 2 Jun 2021 14:12:58 -0500 Subject: [PATCH 80/84] [Kinetics] Simplify handling of legacy methods in Python reactions Also re-insert warnings about experimental custom reactions. --- include/cantera/kinetics/Reaction.h | 4 + include/cantera/kinetics/ReactionRate.h | 3 + interfaces/cython/cantera/reaction.pyx | 263 +++++++----------- .../cython/cantera/test/test_reaction.py | 2 +- 4 files changed, 104 insertions(+), 168 deletions(-) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index fe3d3c6370f..e5c0090d73b 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -581,6 +581,10 @@ class ChebyshevReaction3 : public Reaction3 //! 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 CustomFunc1Reaction : public Reaction3 { public: diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index e9eff54a0a7..50d8190f6cb 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -419,6 +419,9 @@ class ChebyshevRate3 final : public ReactionRate, public Chebyshe /** * 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 { diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index a6ab6d3b748..158d18927eb 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -15,7 +15,7 @@ cdef class _ReactionRate: """ def __repr__(self): - return "<{} at {:0x}>".format(pystr(self.base.type()), id(self)) + return f"<{pystr(self.base.type())} at {id(self):0x}>" def __call__(self, double temperature, pressure=None): """ @@ -331,9 +331,9 @@ cdef class Reaction: will produce a list of the 325 reactions which make up the GRI 3.0 mechanism:: - R = ct.Reaction.listFromFile('gri30.yaml', gas) - R = ct.Reaction.listFromCti(open('path/to/gri30.cti').read()) - R = ct.Reaction.listFromXml(open('path/to/gri30.xml').read()) + R = ct.Reaction.listFromFile("gri30.yaml", gas) + R = ct.Reaction.listFromCti(open("path/to/gri30.cti").read()) + R = ct.Reaction.listFromXml(open("path/to/gri30.xml").read()) where `gas` is a `Solution` object with the appropriate thermodynamic model, which is the `ideal-gas` model in this case. @@ -786,18 +786,22 @@ cdef class ElementaryReaction(Reaction): cdef CxxElementaryReaction3* er(self): if self.uses_legacy: - raise AttributeError("Rate object accessor not defined for legacy implementation") + raise AttributeError("Incorrect accessor for updated implementation") return self.reaction + cdef CxxElementaryReaction2* er2(self): + if not self.uses_legacy: + raise AttributeError("Incorrect accessor for legacy implementation") + return self.reaction + def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, legacy=False, **kwargs): if init and equation and kinetics: + rxn_type = self._reaction_type if legacy: - rxn_type = self._reaction_type + "-legacy" - else: - rxn_type = self._reaction_type + rxn_type += "-legacy" spec = {"equation": equation, "type": rxn_type} if isinstance(rate, dict): spec["rate-constant"] = rate @@ -819,25 +823,20 @@ cdef class ElementaryReaction(Reaction): elif not legacy and isinstance(rate, (ArrheniusRate, Arrhenius)): self.rate = rate - property _legacy_rate: - """ Get/Set the `Arrhenius` rate coefficient for this reaction. """ - def __get__(self): - cdef CxxElementaryReaction2* r = self.reaction - return wrapArrhenius(&(r.rate), self) - def __set__(self, Arrhenius rate): - cdef CxxElementaryReaction2* r = self.reaction - r.rate = deref(rate.rate) + cdef _legacy_set_rate(self, Arrhenius rate): + cdef CxxElementaryReaction2* r = self.er2() + r.rate = deref(rate.rate) property rate: """ Get/Set the `ArrheniusRate` rate coefficient for this reaction. """ def __get__(self): if self.uses_legacy: - return self._legacy_rate + return wrapArrhenius(&(self.er2().rate), self) return ArrheniusRate.wrap(self.er().rate()) def __set__(self, rate): if self.uses_legacy: - self._legacy_rate = rate + self._legacy_set_rate(rate) return cdef ArrheniusRate rate3 @@ -854,18 +853,6 @@ cdef class ElementaryReaction(Reaction): raise TypeError("Invalid rate definition") self.er().setRate(rate3._base) - property _legacy_allow_negative_pre_exponential_factor: - """ - Get/Set whether the rate coefficient is allowed to have a negative - pre-exponential factor. - """ - def __get__(self): - cdef CxxElementaryReaction2* r = self.reaction - return r.allow_negative_pre_exponential_factor - def __set__(self, allow): - cdef CxxElementaryReaction2* r = self.reaction - r.allow_negative_pre_exponential_factor = allow - property allow_negative_pre_exponential_factor: """ Get/Set whether the rate coefficient is allowed to have a negative @@ -877,20 +864,18 @@ cdef class ElementaryReaction(Reaction): """ def __get__(self): if self.uses_legacy: - return self._legacy_allow_negative_pre_exponential_factor + return self.er2().allow_negative_pre_exponential_factor attr = "allow_negative_pre_exponential_factor" - warnings.warn( - self._deprecation_warning(attr), DeprecationWarning) + warnings.warn(self._deprecation_warning(attr), DeprecationWarning) return self.rate.allow_negative_pre_exponential_factor def __set__(self, allow): if self.uses_legacy: - self._legacy_allow_negative_pre_exponential_factor = allow + self.er2().allow_negative_pre_exponential_factor = allow return attr = "allow_negative_pre_exponential_factor" - warnings.warn( - self._deprecation_warning(attr), DeprecationWarning) + warnings.warn(self._deprecation_warning(attr), DeprecationWarning) self.rate.allow_negative_pre_exponential_factor = allow @@ -931,7 +916,7 @@ cdef class ThreeBodyReaction(ElementaryReaction): cdef CxxThirdBody* thirdbody(self): if self.uses_legacy: - raise AttributeError("Accessor not defined for legacy implementation") + return &(self.tbr2().third_body) return (self.tbr().thirdBody().get()) def __init__(self, equation=None, rate=None, efficiencies=None, @@ -939,10 +924,9 @@ cdef class ThreeBodyReaction(ElementaryReaction): if init and equation and kinetics: + rxn_type = self._reaction_type if legacy: - rxn_type = self._reaction_type + "-legacy" - else: - rxn_type = self._reaction_type + rxn_type += "-legacy" spec = {"equation": equation, "type": rxn_type} if isinstance(rate, dict): spec["rate-constant"] = rate @@ -974,13 +958,8 @@ cdef class ThreeBodyReaction(ElementaryReaction): efficiencies. """ def __get__(self): - if self.uses_legacy: - return comp_map_to_dict(self.tbr2().third_body.efficiencies) return comp_map_to_dict(self.thirdbody().efficiencies) def __set__(self, eff): - if self.uses_legacy: - self.tbr2().third_body.efficiencies = comp_map(eff) - return self.thirdbody().efficiencies = comp_map(eff) property default_efficiency: @@ -989,13 +968,8 @@ cdef class ThreeBodyReaction(ElementaryReaction): species used for species not in `efficiencies`. """ def __get__(self): - if self.uses_legacy: - return self.tbr2().third_body.default_efficiency return self.thirdbody().default_efficiency def __set__(self, default_eff): - if self.uses_legacy: - self.tbr2().third_body.default_efficiency = default_eff - return self.thirdbody().default_efficiency = default_eff def efficiency(self, species): @@ -1003,8 +977,6 @@ cdef class ThreeBodyReaction(ElementaryReaction): Get the efficiency of the third body named *species* considering both the default efficiency and species-specific efficiencies. """ - if self.uses_legacy: - return self.tbr2().third_body.efficiency(stringify(species)) return self.thirdbody().efficiency(stringify(species)) @@ -1215,18 +1187,22 @@ cdef class PlogReaction(Reaction): cdef CxxPlogReaction3* pr(self): if self.uses_legacy: - raise AttributeError("Accessor not defined for legacy implementation") + raise AttributeError("Incorrect accessor for updated implementation") return self.reaction + cdef CxxPlogReaction2* cp2(self): + if not self.uses_legacy: + raise AttributeError("Incorrect accessor for legacy implementation") + return self.reaction + def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, legacy=False, **kwargs): if init and equation and kinetics: + rxn_type = self._reaction_type if legacy: - rxn_type = self._reaction_type + "-legacy" - else: - rxn_type = self._reaction_type + rxn_type += "-legacy" spec = {"equation": equation, "type": rxn_type} if isinstance(rate, dict): spec.update(rate) @@ -1253,31 +1229,26 @@ cdef class PlogReaction(Reaction): raise AttributeError("Legacy implementation does not use rate property.") self.pr().setRate(rate._base) - property _legacy_rates: - """ - Get/Set the rate coefficients for this reaction, which are given as a - list of (pressure, `Arrhenius`) tuples. - """ - def __get__(self): - cdef CxxPlogReaction2* r = self.reaction - rates = [] - cdef vector[pair[double,CxxArrhenius]] cxxrates = r.rate.rates() - cdef pair[double,CxxArrhenius] p_rate - for p_rate in cxxrates: - rates.append((p_rate.first,copyArrhenius(&p_rate.second))) - return rates - - def __set__(self, rates): - cdef multimap[double,CxxArrhenius] ratemap - cdef Arrhenius rate - cdef pair[double,CxxArrhenius] item - for p, rate in rates: - item.first = p - item.second = deref(rate.rate) - ratemap.insert(item) - - cdef CxxPlogReaction2* r = self.reaction - r.rate = CxxPlog(ratemap) + cdef list _legacy_get_rates(self): + cdef CxxPlogReaction2* r = self.cp2() + cdef vector[pair[double,CxxArrhenius]] cxxrates = r.rate.rates() + cdef pair[double,CxxArrhenius] p_rate + rates = [] + for p_rate in cxxrates: + rates.append((p_rate.first,copyArrhenius(&p_rate.second))) + return rates + + cdef _legacy_set_rates(self, list rates): + cdef multimap[double,CxxArrhenius] ratemap + cdef Arrhenius rate + cdef pair[double,CxxArrhenius] item + for p, rate in rates: + item.first = p + item.second = deref(rate.rate) + ratemap.insert(item) + + cdef CxxPlogReaction2* r = self.cp2() + r.rate = CxxPlog(ratemap) property rates: """ @@ -1290,28 +1261,25 @@ cdef class PlogReaction(Reaction): """ def __get__(self): if self.uses_legacy: - return self._legacy_rates + return self._legacy_get_rates() - warnings.warn( - self._deprecation_warning("rates"), DeprecationWarning) + warnings.warn(self._deprecation_warning("rates"), DeprecationWarning) return self.rate.rates def __set__(self, rates): if self.uses_legacy: - self._legacy_rates = rates + self._legacy_set_rates(rates) return warnings.warn("Property 'rates' to be removed after Cantera 2.6. " "Setter is replaceable by assigning a new 'PlogRate' object created " "from rates to the rate property.", DeprecationWarning) - warnings.warn( - self._deprecation_warning("rates"), DeprecationWarning) rate_ = self.rate rate_.rates = rates self.rate = rate_ - def _legacy_call(self, float T, float P): - cdef CxxPlogReaction2* r = self.reaction + cdef _legacy_call(self, float T, float P): + cdef CxxPlogReaction2* r = self.cp2() cdef double logT = np.log(T) cdef double recipT = 1/T cdef double logP = np.log(P) @@ -1366,18 +1334,22 @@ cdef class ChebyshevReaction(Reaction): cdef CxxChebyshevReaction3* cr(self): if self.uses_legacy: - raise AttributeError("Accessor not defined for legacy implementation") + raise AttributeError("Incorrect accessor for updated implementation") return self.reaction + cdef CxxChebyshevReaction2* cr2(self): + if not self.uses_legacy: + raise AttributeError("Incorrect accessor for legacy implementation") + return self.reaction + def __init__(self, equation=None, rate=None, Kinetics kinetics=None, init=True, legacy=False, **kwargs): if init and equation and kinetics: + rxn_type = self._reaction_type if legacy: - rxn_type = self._reaction_type + "-legacy" - else: - rxn_type = self._reaction_type + rxn_type += "-legacy" spec = {"equation": equation, "type": rxn_type} if isinstance(rate, dict): spec["temperature-range"] = [rate["Tmin"], rate["Tmax"]] @@ -1406,12 +1378,6 @@ cdef class ChebyshevReaction(Reaction): raise AttributeError("Legacy implementation does not use rate property.") self.cr().setRate(rate._base) - property _legacy_Tmin: - """ Minimum temperature [K] for the Chebyshev fit """ - def __get__(self): - cdef CxxChebyshevReaction2* r = self.reaction - return r.rate.Tmin() - property Tmin: """ Minimum temperature [K] for the Chebyshev fit @@ -1422,16 +1388,10 @@ cdef class ChebyshevReaction(Reaction): """ def __get__(self): if self.uses_legacy: - return self._legacy_Tmin - warnings.warn( - self._deprecation_warning("Tmin"), DeprecationWarning) - return self.rate.Tmin + return self.cr2().rate.Tmin() - property _legacy_Tmax: - """ Maximum temperature [K] for the Chebyshev fit """ - def __get__(self): - cdef CxxChebyshevReaction2* r = self.reaction - return r.rate.Tmax() + warnings.warn(self._deprecation_warning("Tmin"), DeprecationWarning) + return self.rate.Tmin property Tmax: """ @@ -1443,17 +1403,10 @@ cdef class ChebyshevReaction(Reaction): """ def __get__(self): if self.uses_legacy: - return self._legacy_Tmax - warnings.warn( - self._deprecation_warning("Tmax"), DeprecationWarning) - return self.rate.Tmax - + return self.cr2().rate.Tmax() - property _legacy_Pmin: - """ Minimum pressure [Pa] for the Chebyshev fit """ - def __get__(self): - cdef CxxChebyshevReaction2* r = self.reaction - return r.rate.Pmin() + warnings.warn(self._deprecation_warning("Tmax"), DeprecationWarning) + return self.rate.Tmax property Pmin: """ @@ -1465,17 +1418,10 @@ cdef class ChebyshevReaction(Reaction): """ def __get__(self): if self.uses_legacy: - return self._legacy_Pmin - warnings.warn( - self._deprecation_warning("Pmin"), DeprecationWarning) - return self.rate.Pmin - + return self.cr2().rate.Pmin() - property _legacy_Pmax: - """ Maximum pressure [Pa] for the Chebyshev fit """ - def __get__(self): - cdef CxxChebyshevReaction2* r = self.reaction - return r.rate.Pmax() + warnings.warn(self._deprecation_warning("Pmin"), DeprecationWarning) + return self.rate.Pmin property Pmax: """ Maximum pressure [Pa] for the Chebyshev fit @@ -1486,16 +1432,10 @@ cdef class ChebyshevReaction(Reaction): """ def __get__(self): if self.uses_legacy: - return self._legacy_Pmax - warnings.warn( - self._deprecation_warning("Pmax"), DeprecationWarning) - return self.rate.Pmax + return self.cr2().rate.Pmax() - property _legacy_nPressure: - """ Number of pressures over which the Chebyshev fit is computed """ - def __get__(self): - cdef CxxChebyshevReaction2* r = self.reaction - return r.rate.nPressure() + warnings.warn(self._deprecation_warning("Pmax"), DeprecationWarning) + return self.rate.Pmax property nPressure: """ @@ -1507,16 +1447,10 @@ cdef class ChebyshevReaction(Reaction): """ def __get__(self): if self.uses_legacy: - return self._legacy_nPressure - warnings.warn( - self._deprecation_warning("nPressure"), DeprecationWarning) - return self.rate.n_pressure + return self.cr2().rate.nPressure() - property _legacy_nTemperature: - """ Number of temperatures over which the Chebyshev fit is computed """ - def __get__(self): - cdef CxxChebyshevReaction2* r = self.reaction - return r.rate.nTemperature() + warnings.warn(self._deprecation_warning("nPressure"), DeprecationWarning) + return self.rate.n_pressure property nTemperature: """ @@ -1528,19 +1462,16 @@ cdef class ChebyshevReaction(Reaction): """ def __get__(self): if self.uses_legacy: - return self._legacy_nTemperature + return self.cr2().rate.nTemperature() + warnings.warn( self._deprecation_warning("nTemperature"), DeprecationWarning) return self.rate.n_temperature - property _legacy_coeffs: - """ - 2D array of Chebyshev coefficients of size `(n_temperature, n_pressure)`. - """ - def __get__(self): - cdef CxxChebyshevReaction2* r = self.reaction - c = np.fromiter(r.rate.coeffs(), np.double) - return c.reshape((r.rate.nTemperature(), r.rate.nPressure())) + cdef _legacy_get_coeffs(self): + cdef CxxChebyshevReaction2* r = self.cr2() + c = np.fromiter(r.rate.coeffs(), np.double) + return c.reshape((r.rate.nTemperature(), r.rate.nPressure())) property coeffs: """ @@ -1552,17 +1483,13 @@ cdef class ChebyshevReaction(Reaction): """ def __get__(self): if self.uses_legacy: - return self._legacy_coeffs - warnings.warn( - self._deprecation_warning("coeffs"), DeprecationWarning) + return self._legacy_get_coeffs() + + warnings.warn(self._deprecation_warning("coeffs"), DeprecationWarning) return self.rate.coeffs - def _legacy_set_parameters(self, Tmin, Tmax, Pmin, Pmax, coeffs): - """ - Simultaneously set values for `Tmin`, `Tmax`, `Pmin`, `Pmax`, and - `coeffs`. - """ - cdef CxxChebyshevReaction2* r = self.reaction + cdef _legacy_set_parameters(self, Tmin, Tmax, Pmin, Pmax, coeffs): + cdef CxxChebyshevReaction2* r = self.cr2() cdef CxxArray2D data data.resize(len(coeffs), len(coeffs[0])) @@ -1570,7 +1497,7 @@ cdef class ChebyshevReaction(Reaction): cdef int i cdef int j for i,row in enumerate(coeffs): - for j,value in enumerate(row): + for j, value in enumerate(row): CxxArray2D_set(data, i, j, value) r.rate = CxxChebyshev(Tmin, Tmax, Pmin, Pmax, data) @@ -1586,13 +1513,14 @@ cdef class ChebyshevReaction(Reaction): """ if self.uses_legacy: return self._legacy_set_parameters(Tmin, Tmax, Pmin, Pmax, coeffs) + warnings.warn("Method 'set_parameters' to be removed after Cantera 2.6. " "Method is replaceable by assigning a new 'ChebyshevRate' object to the " "rate property.", DeprecationWarning) self.rate = ChebyshevRate(Tmin, Tmax, Pmin, Pmax, coeffs) - def _legacy_call(self, float T, float P): - cdef CxxChebyshevReaction2* r = self.reaction + cdef _legacy_call(self, float T, float P): + cdef CxxChebyshevReaction2* r = self.cr2() cdef double logT = np.log(T) cdef double recipT = 1/T cdef double logP = np.log10(P) @@ -1608,6 +1536,7 @@ cdef class ChebyshevReaction(Reaction): """ if self.uses_legacy: return self._legacy_call(T, P) + warnings.warn( self._deprecation_warning("__call__", "method"), DeprecationWarning) return self.rate(T, P) diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 8a4e045d3a9..9988ca53155 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -677,7 +677,7 @@ def test_deprecated_setters(self): rxn.rates = rates self.check_rates(rxn.rates, _rate) else: - with self.assertWarnsRegex(DeprecationWarning, "property is moved"): + with self.assertWarnsRegex(DeprecationWarning, "Setter is replaceable"): rxn.rates = rates with self.assertWarnsRegex(DeprecationWarning, "property is moved"): self.check_rates(rxn.rates, _rate) From bd9d20bf5f10d4bc5196bd4d1136ad386a0e4774 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 6 Jun 2021 12:04:33 -0500 Subject: [PATCH 81/84] [Kinetics] Streamline code based on review comments --- include/cantera/kinetics/GasKinetics.h | 14 +-- include/cantera/kinetics/MultiRate.h | 4 +- include/cantera/kinetics/Reaction.h | 2 +- include/cantera/kinetics/ReactionData.h | 6 -- include/cantera/kinetics/ReactionRate.h | 12 +-- include/cantera/kinetics/RxnRates.h | 3 + src/kinetics/Reaction.cpp | 7 +- src/kinetics/ReactionFactory.cpp | 43 +++++---- src/kinetics/ReactionRate.cpp | 8 ++ test/kinetics/kineticsFromScratch3.cpp | 110 ------------------------ test/kinetics/kineticsFromYaml.cpp | 12 ++- 11 files changed, 57 insertions(+), 164 deletions(-) diff --git a/include/cantera/kinetics/GasKinetics.h b/include/cantera/kinetics/GasKinetics.h index a877e0abb83..2ba5b768626 100644 --- a/include/cantera/kinetics/GasKinetics.h +++ b/include/cantera/kinetics/GasKinetics.h @@ -107,23 +107,23 @@ class GasKinetics : public BulkKinetics void processFalloffReactions(); // functions marked as deprecated below are only used for XML import and - // transitional reaction types marked as '-old' + // transitional reaction types are marked as '-legacy' - //! @deprecated Cantera 2.6 (replaced by MultiRate approach) + //! @deprecated To be removed after Cantera 2.6 (replaced by MultiRate approach) void addThreeBodyReaction(ThreeBodyReaction2& r); void addFalloffReaction(FalloffReaction& r); - //! @deprecated Cantera 2.6 (replaced by MultiRate approach) + //! @deprecated To be removed after Cantera 2.6 (replaced by MultiRate approach) void addPlogReaction(PlogReaction2& r); - //! @deprecated Cantera 2.6 (replaced by MultiRate approach) + //! @deprecated To be removed after Cantera 2.6 (replaced by MultiRate approach) void addChebyshevReaction(ChebyshevReaction2& r); void addBlowersMaselReaction(BlowersMaselReaction& r); - //! @deprecated Cantera 2.6 (replaced by MultiRate approach) + //! @deprecated To be removed after Cantera 2.6 (replaced by MultiRate approach) void modifyThreeBodyReaction(size_t i, ThreeBodyReaction2& r); void modifyFalloffReaction(size_t i, FalloffReaction& r); - //! @deprecated Cantera 2.6 (replaced by MultiRate approach) + //! @deprecated To be removed after Cantera 2.6 (replaced by MultiRate approach) void modifyPlogReaction(size_t i, PlogReaction2& r); - //! @deprecated Cantera 2.6 (replaced by MultiRate approach) + //! @deprecated To be removed after Cantera 2.6 (replaced by MultiRate approach) void modifyChebyshevReaction(size_t i, ChebyshevReaction2& r); void modifyBlowersMaselReaction(size_t i, BlowersMaselReaction& r); diff --git a/include/cantera/kinetics/MultiRate.h b/include/cantera/kinetics/MultiRate.h index 61d0045f843..0bdb00c5962 100644 --- a/include/cantera/kinetics/MultiRate.h +++ b/include/cantera/kinetics/MultiRate.h @@ -53,6 +53,8 @@ class MultiRateBase //! Update data common to reaction rates of a specific type //! @param bulk object representing bulk phase + //! @param concm effective third-body concentrations + //! @TODO enable more generic handling of non-trivial concentration dependencies virtual void update(const ThermoPhase& bulk, double* concm) = 0; }; @@ -107,7 +109,7 @@ class MultiBulkRates final : public MultiRateBase { // update common data once for each reaction type m_shared.update(bulk); - if (RateType::uses_update()) { + if (RateType::usesUpdate()) { // 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 diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index e5c0090d73b..d2f38b1b801 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -181,7 +181,7 @@ class ThirdBody ThirdBody(const AnyMap& node); //! Set third-body efficiencies from AnyMap *node* - bool setEfficiencies(const AnyMap& node); + void setEfficiencies(const AnyMap& node); //! Get the third-body efficiency for species *k* double efficiency(const std::string& k) const; diff --git a/include/cantera/kinetics/ReactionData.h b/include/cantera/kinetics/ReactionData.h index 4cb1d617600..b7117c8ddd3 100644 --- a/include/cantera/kinetics/ReactionData.h +++ b/include/cantera/kinetics/ReactionData.h @@ -75,9 +75,6 @@ struct PlogData //! Update number of species; unused void resizeSpecies(size_t n_species) {} - //! Pointer to logP (required by Plog::update_C) - const double* logP() const { return &m_logP; } - double m_temperature; //!< temperature double m_logT; //!< logarithm of temperature double m_recipT; //!< inverse of temperature @@ -111,9 +108,6 @@ struct ChebyshevData //! Update number of species; unused void resizeSpecies(size_t n_species) {} - //! Pointer to logP (required by Chebyshev::update_C) - const double* log10P() const { return &m_log10P; } - double m_temperature; //!< temperature double m_recipT; //!< inverse of temperature double m_log10P; //!< base 10 logarithm of pressure diff --git a/include/cantera/kinetics/ReactionRate.h b/include/cantera/kinetics/ReactionRate.h index 50d8190f6cb..53448d84cd8 100644 --- a/include/cantera/kinetics/ReactionRate.h +++ b/include/cantera/kinetics/ReactionRate.h @@ -253,7 +253,7 @@ class ArrheniusRate final : public ReactionRate, public Arrhenius virtual std::string type() const override { return "ArrheniusRate"; } //! Update information specific to reaction - static bool uses_update() { return false; } + static bool usesUpdate() { return false; } virtual double eval(const ArrheniusData& shared_data, double concm=0.) const override { @@ -319,10 +319,10 @@ class PlogRate final : public ReactionRate, public Plog const Units& rate_units) const override; //! Update information specific to reaction - static bool uses_update() { return true; } + static bool usesUpdate() { return true; } virtual void update(const PlogData& shared_data, double concm=0.) override { - update_C(shared_data.logP()); + update_C(&shared_data.m_logP); } virtual double eval(const PlogData& shared_data, @@ -400,10 +400,10 @@ class ChebyshevRate3 final : public ReactionRate, public Chebyshe const Units& rate_units) const override; //! Update information specific to reaction - static bool uses_update() { return true; } + static bool usesUpdate() { return true; } virtual void update(const ChebyshevData& shared_data, double concm=0.) override { - update_C(shared_data.log10P()); + update_C(&shared_data.m_log10P); } virtual double eval(const ChebyshevData& shared_data, @@ -440,7 +440,7 @@ class CustomFunc1Rate final : public ReactionRate } //! Update information specific to reaction - static bool uses_update() { return false; } + static bool usesUpdate() { return false; } //! Set custom rate /** diff --git a/include/cantera/kinetics/RxnRates.h b/include/cantera/kinetics/RxnRates.h index 1270a1548cd..2edc6dbe7a2 100644 --- a/include/cantera/kinetics/RxnRates.h +++ b/include/cantera/kinetics/RxnRates.h @@ -1,5 +1,8 @@ /** * @file RxnRates.h + * + * @TODO at least part of the content of this file should be transferred + * to ReactionRate.h once the old XML interface is removed after Cantera 2.6 */ // This file is part of Cantera. See License.txt in the top-level directory or diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 79be1f68cd9..a21ee60db78 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -392,13 +392,12 @@ ThirdBody::ThirdBody(const AnyMap& node) setEfficiencies(node); } -bool ThirdBody::setEfficiencies(const AnyMap& node) +void ThirdBody::setEfficiencies(const AnyMap& node) { default_efficiency = node.getDouble("default-efficiency", 1.0); if (node.hasKey("efficiencies")) { efficiencies = node["efficiencies"].asMap(); } - return true; } double ThirdBody::efficiency(const std::string& k) const @@ -740,7 +739,7 @@ void BlowersMaselReaction::validate() Reaction::validate(); if (!allow_negative_pre_exponential_factor && rate.preExponentialFactor() < 0) { - throw CanteraError("BlowersMaselReaction::validate", + throw InputFileError("BlowersMaselReaction::validate", input, "Undeclared negative pre-exponential factor found in reaction '" + equation() + "'"); } @@ -902,7 +901,7 @@ void ElementaryReaction3::getParameters(AnyMap& reactionNode) const ThreeBodyReaction3::ThreeBodyReaction3() : ElementaryReaction3() { - m_third_body = std::shared_ptr(new ThirdBody); + m_third_body.reset(new ThirdBody); } ThreeBodyReaction3::ThreeBodyReaction3(const Composition& reactants, diff --git a/src/kinetics/ReactionFactory.cpp b/src/kinetics/ReactionFactory.cpp index 1a50fc9ddaf..bdffc49332a 100644 --- a/src/kinetics/ReactionFactory.cpp +++ b/src/kinetics/ReactionFactory.cpp @@ -30,9 +30,9 @@ ReactionFactory::ReactionFactory() // register elementary reactions (old framework) reg("elementary-legacy", [](const AnyMap& node, const Kinetics& kin) { - Reaction* R = new ElementaryReaction2(); + ElementaryReaction* R = new ElementaryReaction2(); if (!node.empty()) { - setupElementaryReaction(*(ElementaryReaction2*)R, node, kin); + setupElementaryReaction(*R, node, kin); } return R; }); @@ -46,27 +46,27 @@ ReactionFactory::ReactionFactory() // register three-body reactions (old framework) reg("three-body-legacy", [](const AnyMap& node, const Kinetics& kin) { - Reaction* R = new ThreeBodyReaction2(); + ThreeBodyReaction* R = new ThreeBodyReaction2(); if (!node.empty()) { - setupThreeBodyReaction(*(ThreeBodyReaction2*)R, node, kin); + setupThreeBodyReaction(*R, node, kin); } return R; }); // register falloff reactions reg("falloff", [](const AnyMap& node, const Kinetics& kin) { - Reaction* R = new FalloffReaction(); + FalloffReaction* R = new FalloffReaction(); if (!node.empty()) { - setupFalloffReaction(*(FalloffReaction*)R, node, kin); + setupFalloffReaction(*R, node, kin); } return R; }); // register falloff reactions reg("chemically-activated", [](const AnyMap& node, const Kinetics& kin) { - Reaction* R = new ChemicallyActivatedReaction(); + FalloffReaction* R = new ChemicallyActivatedReaction(); if (!node.empty()) { - setupFalloffReaction(*(FalloffReaction*)R, node, kin); + setupFalloffReaction(*R, node, kin); } return R; }); @@ -82,9 +82,9 @@ ReactionFactory::ReactionFactory() // register pressure-dependent-Arrhenius reactions (old framework) reg("pressure-dependent-Arrhenius-legacy", [](const AnyMap& node, const Kinetics& kin) { - Reaction* R = new PlogReaction2(); + PlogReaction* R = new PlogReaction2(); if (!node.empty()) { - setupPlogReaction(*(PlogReaction2*)R, node, kin); + setupPlogReaction(*R, node, kin); } return R; }); @@ -95,9 +95,9 @@ ReactionFactory::ReactionFactory() }); addAlias("Chebyshev", "chebyshev"); reg("Chebyshev-legacy", [](const AnyMap& node, const Kinetics& kin) { - Reaction* R = new ChebyshevReaction2(); + ChebyshevReaction* R = new ChebyshevReaction2(); if (!node.empty()) { - setupChebyshevReaction(*(ChebyshevReaction2*)R, node, kin); + setupChebyshevReaction(*R, node, kin); } return R; }); @@ -109,9 +109,9 @@ ReactionFactory::ReactionFactory() // register interface reactions reg("interface", [](const AnyMap& node, const Kinetics& kin) { - Reaction* R = new InterfaceReaction(); + InterfaceReaction* R = new InterfaceReaction(); if (!node.empty()) { - setupInterfaceReaction(*(InterfaceReaction*)R, node, kin); + setupInterfaceReaction(*R, node, kin); } return R; }); @@ -120,27 +120,27 @@ ReactionFactory::ReactionFactory() // register electrochemical reactions reg("electrochemical", [](const AnyMap& node, const Kinetics& kin) { - Reaction* R = new ElectrochemicalReaction(); + ElectrochemicalReaction* R = new ElectrochemicalReaction(); if (!node.empty()) { - setupElectrochemicalReaction(*(ElectrochemicalReaction*)R, node, kin); + setupElectrochemicalReaction(*R, node, kin); } return R; }); // register Blowers Masel reactions reg("Blowers-Masel", [](const AnyMap& node, const Kinetics& kin) { - Reaction* R = new BlowersMaselReaction(); + BlowersMaselReaction* R = new BlowersMaselReaction(); if (!node.empty()) { - setupBlowersMaselReaction(*(BlowersMaselReaction*)R, node, kin); + setupBlowersMaselReaction(*R, node, kin); } return R; }); // register surface Blowers Masel reactions reg("surface-Blowers-Masel", [](const AnyMap& node, const Kinetics& kin) { - Reaction* R = new BlowersMaselInterfaceReaction(); + BlowersMaselInterfaceReaction* R = new BlowersMaselInterfaceReaction(); if (!node.empty()) { - setupBlowersMaselInterfaceReaction(*(BlowersMaselInterfaceReaction*)R, node, kin); + setupBlowersMaselInterfaceReaction(*R, node, kin); } return R; }); @@ -360,8 +360,7 @@ unique_ptr newReaction(const AnyMap& rxn_node, const Kinetics& kin) throw InputFileError("ReactionFactory::newReaction", rxn_node["type"], "Unknown reaction type '{}'", type); } - Reaction* R; - R = ReactionFactory::factory()->create(type, rxn_node, kin); + Reaction* R = ReactionFactory::factory()->create(type, rxn_node, kin); return unique_ptr(R); } diff --git a/src/kinetics/ReactionRate.cpp b/src/kinetics/ReactionRate.cpp index 24054059ca8..695d210a84e 100644 --- a/src/kinetics/ReactionRate.cpp +++ b/src/kinetics/ReactionRate.cpp @@ -110,6 +110,8 @@ PlogRate::PlogRate(const AnyMap& node) void PlogRate::setParameters(const AnyMap& node, const Units& rate_units) { + // @TODO implementation of Plog::setParameters should be transferred here + // when the Plog class is removed from RxnRates.h after Cantera 2.6 ReactionRateBase::setParameters(node, rate_units); if (!node.hasKey("rate-constants")) { Plog::setParameters(std::vector (), node.units(), rate_units); @@ -121,6 +123,8 @@ void PlogRate::setParameters(const AnyMap& node, const Units& rate_units) void PlogRate::getParameters(AnyMap& rateNode, const Units& rate_units) const { + // @TODO implementation of Plog::getParameters should be transferred here + // when the Plog class is removed from RxnRates.h after Cantera 2.6 Plog::getParameters(rateNode, rate_units); } @@ -147,12 +151,16 @@ void ChebyshevRate3::setParameters(const AnyMap& node, const Units& rate_units) Chebyshev::setParameters(AnyMap(), node.units(), rate_units); return; } + // @TODO implementation of Chebyshev::setParameters should be transferred here + // when the Chebyshev class is removed from RxnRates.h after Cantera 2.6 Chebyshev::setParameters(node, node.units(), rate_units); } void ChebyshevRate3::getParameters(AnyMap& rateNode, const Units& rate_units) const { + // @TODO implementation of Chebyshev::getParameters should be transferred here + // when the Chebyshev class is removed from RxnRates.h after Cantera 2.6 Chebyshev::getParameters(rateNode, rate_units); } diff --git a/test/kinetics/kineticsFromScratch3.cpp b/test/kinetics/kineticsFromScratch3.cpp index 2ec0842d69f..603a2d24143 100644 --- a/test/kinetics/kineticsFromScratch3.cpp +++ b/test/kinetics/kineticsFromScratch3.cpp @@ -105,29 +105,6 @@ TEST_F(KineticsFromScratch3, skip_undefined_third_body) ASSERT_EQ((size_t) 1, kin.nReactions()); } -/* -TEST_F(KineticsFromScratch3, add_falloff_reaction) -{ - // reaction 2: - // falloff_reaction('2 OH (+ M) <=> H2O2 (+ M)', - // kf=[7.400000e+10, -0.37, 0.0], - // kf0=[2.300000e+12, -0.9, -1700.0], - // efficiencies='AR:0.7 H2:2.0 H2O:6.0', - // falloff=Troe(A=0.7346, T3=94.0, T1=1756.0, T2=5182.0)) - Composition reac = parseCompString("OH:2"); - Composition prod = parseCompString("H2O2:1"); - Arrhenius high_rate(7.4e10, -0.37, 0.0); - Arrhenius low_rate(2.3e12, -0.9, -1700.0 / GasConst_cal_mol_K); - vector_fp falloff_params { 0.7346, 94.0, 1756.0, 5182.0 }; - ThirdBody tbody; - tbody.efficiencies = parseCompString("AR:0.7 H2:2.0 H2O:6.0"); - auto R = make_shared(reac, prod, low_rate, high_rate, tbody); - R->falloff = newFalloff("Troe", falloff_params); - kin.addReaction(R); - check_rates(2); -} -*/ - TEST_F(KineticsFromScratch3, add_plog_reaction) { // reaction 3: @@ -310,93 +287,6 @@ TEST_F(KineticsFromScratch3, invalid_nonreactant_order) ASSERT_EQ((size_t) 0, kin.nReactions()); } -/* -class InterfaceKineticsFromScratch3 : public testing::Test -{ -public: - InterfaceKineticsFromScratch3() - : gas("sofc.yaml", "gas") - , gas_ref("sofc.yaml", "gas") - , surf("sofc.yaml", "metal_surface") - , surf_ref("sofc.yaml", "metal_surface") - { - std::vector th = { &surf_ref, &gas_ref }; - kin_ref = newKinetics(th, "sofc.yaml", "metal_surface"); - kin.addPhase(surf); - kin.addPhase(gas); - } - - IdealGasPhase gas; - IdealGasPhase gas_ref; - SurfPhase surf; - SurfPhase surf_ref; - InterfaceKinetics kin; - unique_ptr kin_ref; - - //! iRef is the index of the corresponding reaction in the reference mech - void check_rates(int iRef) { - ASSERT_EQ((size_t) 1, kin.nReactions()); - - std::string X = "H2:0.2 O2:0.5 H2O:0.1 N2:0.2"; - std::string Xs = "H(m):0.1 O(m):0.2 OH(m):0.3 (m):0.4"; - gas.setState_TPX(1200, 5*OneAtm, X); - gas_ref.setState_TPX(1200, 5*OneAtm, X); - surf.setState_TP(1200, 5*OneAtm); - surf_ref.setState_TP(1200, 5*OneAtm); - surf.setCoveragesByName(Xs); - surf_ref.setCoveragesByName(Xs); - - vector_fp k(1), k_ref(kin_ref->nReactions()); - - kin.getFwdRateConstants(&k[0]); - kin_ref->getFwdRateConstants(&k_ref[0]); - EXPECT_DOUBLE_EQ(k_ref[iRef], k[0]); - - kin.getRevRateConstants(&k[0]); - kin_ref->getRevRateConstants(&k_ref[0]); - EXPECT_DOUBLE_EQ(k_ref[iRef], k[0]); - } -}; - -TEST_F(InterfaceKineticsFromScratch3, add_surface_reaction) -{ - // Reaction 3 on the metal surface - // surface_reaction( "H(m) + O(m) <=> OH(m) + (m)", - // [5.00000E+22, 0, 100.0], id = 'metal-rxn4') - Composition reac = parseCompString("H(m):1 O(m):1"); - Composition prod = parseCompString("OH(m):1 (m):1"); - Arrhenius rate(5e21, 0, 100.0e6 / GasConstant); // kJ/mol -> J/kmol - - auto R = make_shared(reac, prod, rate); - kin.addReaction(R); - check_rates(3); -} - -TEST_F(InterfaceKineticsFromScratch3, add_sticking_reaction) -{ - // Reaction 0 on the metal surface - // surface_reaction( "H2 + (m) + (m) <=> H(m) + H(m)", - // stick(0.1, 0, 0), id = 'metal-rxn1') - Composition reac = parseCompString("H2:1 (m):2"); - Composition prod = parseCompString("H(m):2"); - Arrhenius rate(0.1, 0, 0.0); - - auto R = make_shared(reac, prod, rate, true); - kin.addReaction(R); - check_rates(0); -} - -TEST_F(InterfaceKineticsFromScratch3, unbalanced_sites) -{ - Composition reac = parseCompString("H(m):1 O(m):1"); - Composition prod = parseCompString("OH(m):1"); - Arrhenius rate(5e21, 0, 100.0e6 / GasConstant); - - auto R = make_shared(reac, prod, rate); - ASSERT_THROW(kin.addReaction(R), CanteraError); -} -*/ - class KineticsAddSpecies3 : public testing::Test { public: diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index 22a0eb6bee2..31b5f02ea18 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -12,7 +12,7 @@ using namespace Cantera; TEST(ReactionRate, ModifyArrheniusRate) { - auto sol = newSolution("gri30.yaml"); + auto sol = newSolution("gri30.yaml", "", "None"); AnyMap rxn = AnyMap::fromYamlString( "{equation: N + NO <=> N2 + O," " rate-constant: [-2.70000E+13 cm^3/mol/s, 0, 355 cal/mol]," @@ -21,12 +21,10 @@ TEST(ReactionRate, ModifyArrheniusRate) auto R = newReaction(rxn, *(sol->kinetics())); auto ER = dynamic_cast(*R); - auto rr0 = std::dynamic_pointer_cast(ER.rate()); - EXPECT_TRUE(rr0->allow_negative_pre_exponential_factor); - rr0->allow_negative_pre_exponential_factor = false; - - auto rr1 = std::dynamic_pointer_cast(ER.rate()); - EXPECT_FALSE(rr1->allow_negative_pre_exponential_factor); + auto rr = std::dynamic_pointer_cast(ER.rate()); + EXPECT_TRUE(rr->allow_negative_pre_exponential_factor); + rr->allow_negative_pre_exponential_factor = false; + EXPECT_FALSE(rr->allow_negative_pre_exponential_factor); } TEST(Reaction, ElementaryFromYaml1) From 85fa6005f59a555494c0fc16a220526b253c414b Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 6 Jun 2021 13:31:40 -0500 Subject: [PATCH 82/84] [Kinetics] Remove transitional Reaction3 object --- include/cantera/kinetics/BulkKinetics.h | 2 +- include/cantera/kinetics/Reaction.h | 82 ++++++-------- interfaces/cython/cantera/_cantera.pxd | 1 + interfaces/cython/cantera/reaction.pyx | 2 +- src/kinetics/BulkKinetics.cpp | 16 ++- src/kinetics/GasKinetics.cpp | 8 +- src/kinetics/Reaction.cpp | 136 +++++++++--------------- test/kinetics/kineticsFromYaml.cpp | 30 +++--- 8 files changed, 108 insertions(+), 169 deletions(-) diff --git a/include/cantera/kinetics/BulkKinetics.h b/include/cantera/kinetics/BulkKinetics.h index f2f73cc3294..aae4a96f313 100644 --- a/include/cantera/kinetics/BulkKinetics.h +++ b/include/cantera/kinetics/BulkKinetics.h @@ -46,7 +46,7 @@ class BulkKinetics : public Kinetics virtual void setMultiplier(size_t i, double f); virtual void invalidateCache(); - void addThirdBody(shared_ptr r); + void addThirdBody(shared_ptr r); protected: virtual void addElementaryReaction(ElementaryReaction2& r); diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index d2f38b1b801..84f6511c0b1 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -66,6 +66,9 @@ class Reaction //! contained in the #input attribute. AnyMap parameters(bool withInput=true) const; + //! Set up reaction based on AnyMap *node* + virtual void setParameters(const AnyMap& node, const Kinetics& kin); + //! Get validity flag of reaction bool valid() const { return m_valid; @@ -133,6 +136,26 @@ class Reaction //! where the reaction occurs. Units rate_units; + //! Get reaction rate pointer + shared_ptr rate() { + return m_rate; + } + + //! Set reaction rate pointer + void setRate(shared_ptr rate) { + m_rate = rate; + } + + //! Get pointer to third-body + shared_ptr thirdBody() { + return m_third_body; + } + + //! Indicate whether object uses legacy framework + bool usesLegacy() const { + return !m_rate; + } + protected: //! Store the parameters of a Reaction needed to reconstruct an identical //! object using the newReaction(AnyMap&, Kinetics&) function. Does not @@ -149,6 +172,13 @@ class Reaction //! @param kin Kinetics object virtual std::pair, bool> undeclaredThirdBodies(const Kinetics& kin) const; + + //! Reaction rate used by generic reactions + shared_ptr m_rate; + + //! Relative efficiencies of third-body species in enhancing the reaction + //! rate (if applicable) + shared_ptr m_third_body; }; @@ -453,52 +483,9 @@ class BlowersMaselInterfaceReaction : public BlowersMaselReaction }; -//! An intermediate class collecting new features of the updated reaction classes. -//! It also avoids ambiguous uses of 'rate' member variables and getters (see -//! `ElementaryReaction2`, `PlogReaction2` and `ChebyshevReaction2`). The class will -//! be merged with Reaction after deprecation of the CTI/XML framework. -class Reaction3 : public Reaction -{ -public: - Reaction3() : Reaction() {} - Reaction3(const Composition& reactants, const Composition& products); - - //! Get reaction rate pointer - shared_ptr rate() { - return m_rate; - } - - //! Set reaction rate pointer - void setRate(shared_ptr rate) { - m_rate = rate; - } - - //! Set up reaction based on AnyMap *node* - virtual void setParameters(const AnyMap& node, const Kinetics& kin); - - //! Get pointer to third-body - shared_ptr thirdBody() { - return m_third_body; - } - - virtual void validate(); - -protected: - virtual std::pair, bool> - undeclaredThirdBodies(const Kinetics& kin) const; - - //! Reaction rate used by generic reactions - shared_ptr m_rate; - - //! Relative efficiencies of third-body species in enhancing the reaction - //! rate (if applicable) - shared_ptr m_third_body; -}; - - //! A reaction which follows mass-action kinetics with a modified Arrhenius //! reaction rate. -class ElementaryReaction3 : public Reaction3 +class ElementaryReaction3 : public Reaction { public: ElementaryReaction3(); @@ -543,7 +530,7 @@ class ThreeBodyReaction3 : public ElementaryReaction3 //! A pressure-dependent reaction parameterized by logarithmically interpolating //! between Arrhenius rate expressions at various pressures. -class PlogReaction3 : public Reaction3 +class PlogReaction3 : public Reaction { public: PlogReaction3(); @@ -561,7 +548,7 @@ class PlogReaction3 : public Reaction3 //! A pressure-dependent reaction parameterized by a bi-variate Chebyshev //! polynomial in temperature and pressure -class ChebyshevReaction3 : public Reaction3 +class ChebyshevReaction3 : public Reaction { public: ChebyshevReaction3(); @@ -574,7 +561,6 @@ class ChebyshevReaction3 : public Reaction3 return "Chebyshev"; } - virtual void setParameters(const AnyMap& node, const Kinetics& kin); virtual void getParameters(AnyMap& reactionNode) const; }; @@ -585,7 +571,7 @@ class ChebyshevReaction3 : public Reaction3 * @warning This class is an experimental part of the %Cantera API and * may be changed or removed without notice. */ -class CustomFunc1Reaction : public Reaction3 +class CustomFunc1Reaction : public Reaction { public: CustomFunc1Reaction(); diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 4ffb035bca9..4e45b527195 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -414,6 +414,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": cbool duplicate cbool allow_nonreactant_orders cbool allow_negative_orders + cbool usesLegacy() cdef cppclass CxxElementaryReaction2 "Cantera::ElementaryReaction2" (CxxReaction): CxxElementaryReaction2() diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 158d18927eb..ff2013bdcb7 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -691,7 +691,7 @@ cdef class Reaction: property uses_legacy: """Indicate whether reaction uses a legacy implementation""" def __get__(self): - return pystr(self.reaction.type()).endswith("-legacy") + return self.reaction.usesLegacy() cdef class Arrhenius: diff --git a/src/kinetics/BulkKinetics.cpp b/src/kinetics/BulkKinetics.cpp index 7d6c12acd52..36914c73f84 100644 --- a/src/kinetics/BulkKinetics.cpp +++ b/src/kinetics/BulkKinetics.cpp @@ -122,9 +122,8 @@ bool BulkKinetics::addReaction(shared_ptr r) m_irrev.push_back(nReactions()-1); } - if (std::dynamic_pointer_cast(r) != nullptr) { - shared_ptr r3 = std::dynamic_pointer_cast(r); - shared_ptr rate = r3->rate(); + if (!(r->usesLegacy())) { + shared_ptr rate = 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(); @@ -153,8 +152,8 @@ bool BulkKinetics::addReaction(shared_ptr r) m_bulk_rates[index]->add(nReactions() - 1, *rate); // Add reaction to third-body evaluator - if (r3->thirdBody() != nullptr) { - addThirdBody(r3); + if (r->thirdBody() != nullptr) { + addThirdBody(r); } } @@ -163,7 +162,7 @@ bool BulkKinetics::addReaction(shared_ptr r) return true; } -void BulkKinetics::addThirdBody(shared_ptr r) +void BulkKinetics::addThirdBody(shared_ptr r) { std::map efficiencies; for (const auto& eff : r->thirdBody()->efficiencies) { @@ -192,9 +191,8 @@ void BulkKinetics::modifyReaction(size_t i, shared_ptr rNew) // operations common to all reaction types Kinetics::modifyReaction(i, rNew); - if (std::dynamic_pointer_cast(rNew) != nullptr) { - shared_ptr rate; - rate = std::dynamic_pointer_cast(rNew)->rate(); + if (!(rNew->usesLegacy())) { + shared_ptr rate = 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 6942a0cdd58..8812c093a95 100644 --- a/src/kinetics/GasKinetics.cpp +++ b/src/kinetics/GasKinetics.cpp @@ -194,7 +194,7 @@ void GasKinetics::updateROP() } // reactions involving third body - for (auto & index : m_multi_indices) { + for (auto& index : m_multi_indices) { m_ropf[index] *= m_concm[index]; } @@ -245,7 +245,7 @@ void GasKinetics::getFwdRateConstants(doublereal* kfwd) } // reactions involving third body - for (auto & index : m_multi_indices) { + for (auto& index : m_multi_indices) { m_ropf[index] *= m_concm[index]; } @@ -261,7 +261,7 @@ bool GasKinetics::addReaction(shared_ptr r) bool added = BulkKinetics::addReaction(r); if (!added) { return false; - } else if (std::dynamic_pointer_cast(r) != nullptr) { + } else if (!(r->usesLegacy())) { // Rate object already added in BulkKinetics::addReaction return true; } @@ -353,7 +353,7 @@ void GasKinetics::modifyReaction(size_t i, shared_ptr rNew) // operations common to all bulk reaction types BulkKinetics::modifyReaction(i, rNew); - if (std::dynamic_pointer_cast(rNew) != nullptr) { + if (!(rNew->usesLegacy())) { // Rate object already modified in BulkKinetics::modifyReaction return; } diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index a21ee60db78..21eee166913 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -111,6 +111,11 @@ void Reaction::validate() throw InputFileError("Reaction::validate", input, "Reaction orders may only be given for irreversible reactions"); } + + // Call validation of reaction rate evaluator + if (!usesLegacy()) { + m_rate->validate(equation()); + } } AnyMap Reaction::parameters(bool withInput) const @@ -153,6 +158,38 @@ void Reaction::getParameters(AnyMap& reactionNode) const } } +void Reaction::setParameters(const AnyMap& node, const Kinetics& kin) +{ + if (node.empty()) { + // empty node: used by newReaction() factory loader + return; + } + + parseReactionEquation(*this, node["equation"], kin); + // Non-stoichiometric reaction 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); + } + } + } + + // remove optional third body notation (used by ChebyshevReaction) + reactants.erase("(+M)"); + products.erase("(+M)"); + + // 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); + + calculateRateCoeffUnits(kin); + input = node; +} + std::string Reaction::reactantString() const { std::ostringstream result; @@ -236,6 +273,12 @@ std::pair, bool> Reaction::undeclaredThirdBodies( const Kinetics& kin) const { std::vector undeclared; + if (m_third_body) { + updateUndeclared(undeclared, m_third_body->efficiencies, kin); + bool specified_collision_partner = dynamic_cast( + this)->specified_collision_partner; + return std::make_pair(undeclared, specified_collision_partner); + } return std::make_pair(undeclared, false); } @@ -820,58 +863,6 @@ void BlowersMaselInterfaceReaction::getParameters(AnyMap& reactionNode) const } } -Reaction3::Reaction3(const Composition& reactants, const Composition& products) - : Reaction(reactants, products) -{ -} - -void Reaction3::setParameters(const AnyMap& node, const Kinetics& kin) -{ - if (node.empty()) { - // empty node: used by newReaction() factory loader - return; - } - - parseReactionEquation(*this, node["equation"], kin); - // Non-stoichiometric reaction 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); - - calculateRateCoeffUnits(kin); - input = node; -} - -std::pair, bool> Reaction3::undeclaredThirdBodies( - const Kinetics& kin) const -{ - std::vector undeclared; - if (m_third_body) { - updateUndeclared(undeclared, m_third_body->efficiencies, kin); - bool specified_collision_partner = dynamic_cast( - this)->specified_collision_partner; - return std::make_pair(undeclared, specified_collision_partner); - } - return std::make_pair(undeclared, false); -} - -void Reaction3::validate() -{ - Reaction::validate(); - m_rate->validate(equation()); -} - ElementaryReaction3::ElementaryReaction3() { m_rate.reset(new ArrheniusRate); @@ -880,7 +871,7 @@ ElementaryReaction3::ElementaryReaction3() ElementaryReaction3::ElementaryReaction3(const Composition& reactants, const Composition& products, const ArrheniusRate& rate) - : Reaction3(reactants, products) + : Reaction(reactants, products) { m_rate.reset(new ArrheniusRate(rate)); } @@ -987,7 +978,7 @@ void ThreeBodyReaction3::setParameters(const AnyMap& node, const Kinetics& kin) // empty node: used by newReaction() factory loader return; } - ElementaryReaction3::setParameters(node, kin); + Reaction::setParameters(node, kin); if (reactants.count("M") != 1 || products.count("M") != 1) { if (!detectEfficiencies()) { throw InputFileError("ThreeBodyReaction3::setParameters", node["equation"], @@ -1042,7 +1033,7 @@ PlogReaction3::PlogReaction3() PlogReaction3::PlogReaction3(const Composition& reactants, const Composition& products, const PlogRate& rate) - : Reaction3(reactants, products) + : Reaction(reactants, products) { m_rate.reset(new PlogRate(rate)); } @@ -1069,7 +1060,7 @@ ChebyshevReaction3::ChebyshevReaction3() ChebyshevReaction3::ChebyshevReaction3(const Composition& reactants, const Composition& products, const ChebyshevRate3& rate) - : Reaction3(reactants, products) + : Reaction(reactants, products) { m_rate.reset(new ChebyshevRate3(rate)); } @@ -1081,37 +1072,6 @@ ChebyshevReaction3::ChebyshevReaction3(const AnyMap& node, const Kinetics& kin) setRate(std::make_shared(node, rate_units)); } -void ChebyshevReaction3::setParameters(const AnyMap& node, const Kinetics& kin) -{ - if (node.empty()) { - // empty node: used by newReaction() factory loader - return; - } - - parseReactionEquation(*this, node["equation"], kin); - // Non-stoichiometric reaction 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); - } - } - } - - reactants.erase("(+M)"); // remove optional third body notation - products.erase("(+M)"); - - // 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); - - calculateRateCoeffUnits(kin); - input = node; -} - void ChebyshevReaction3::getParameters(AnyMap& reactionNode) const { Reaction::getParameters(reactionNode); @@ -1127,7 +1087,7 @@ CustomFunc1Reaction::CustomFunc1Reaction() CustomFunc1Reaction::CustomFunc1Reaction(const Composition& reactants, const Composition& products, const CustomFunc1Rate& rate) - : Reaction3(reactants, products) + : Reaction(reactants, products) { m_rate.reset(new CustomFunc1Rate(rate)); } diff --git a/test/kinetics/kineticsFromYaml.cpp b/test/kinetics/kineticsFromYaml.cpp index 31b5f02ea18..93fb086c53c 100644 --- a/test/kinetics/kineticsFromYaml.cpp +++ b/test/kinetics/kineticsFromYaml.cpp @@ -27,7 +27,7 @@ TEST(ReactionRate, ModifyArrheniusRate) EXPECT_FALSE(rr->allow_negative_pre_exponential_factor); } -TEST(Reaction, ElementaryFromYaml1) +TEST(Reaction, ElementaryFromYaml3) { auto sol = newSolution("gri30.yaml", "", "None"); AnyMap rxn = AnyMap::fromYamlString( @@ -39,13 +39,10 @@ TEST(Reaction, ElementaryFromYaml1) EXPECT_EQ(R->reactants.at("NO"), 1); EXPECT_EQ(R->products.at("N2"), 1); EXPECT_EQ(R->type(), "elementary"); + EXPECT_FALSE(R->allow_negative_orders); - auto ER = dynamic_cast(*R); - auto rr = std::dynamic_pointer_cast(ER.rate()); - EXPECT_TRUE(rr->allow_negative_pre_exponential_factor); - EXPECT_FALSE(ER.allow_negative_orders); - - const auto& rate = std::dynamic_pointer_cast(ER.rate()); + const auto& rate = std::dynamic_pointer_cast(R->rate()); + EXPECT_TRUE(rate->allow_negative_pre_exponential_factor); EXPECT_DOUBLE_EQ(rate->preExponentialFactor(), -2.7e10); EXPECT_DOUBLE_EQ(rate->activationEnergy_R(), 355 / GasConst_cal_mol_K); } @@ -71,7 +68,7 @@ TEST(Reaction, ElementaryFromYaml2) EXPECT_FALSE(ER.allow_negative_orders); } -TEST(Reaction, ThreeBodyFromYaml1) +TEST(Reaction, ThreeBodyFromYaml3) { auto sol = newSolution("gri30.yaml", "", "None"); AnyMap rxn = AnyMap::fromYamlString( @@ -83,15 +80,14 @@ TEST(Reaction, ThreeBodyFromYaml1) auto R = newReaction(rxn, *(sol->kinetics())); EXPECT_EQ(R->reactants.count("M"), (size_t) 0); EXPECT_EQ(R->type(), "three-body"); + EXPECT_DOUBLE_EQ(R->thirdBody()->efficiencies["H2O"], 5.0); + EXPECT_DOUBLE_EQ(R->thirdBody()->default_efficiency, 1.0); - auto TBR = dynamic_cast(*R); - const auto& rate = std::dynamic_pointer_cast(TBR.rate()); + const auto& rate = std::dynamic_pointer_cast(R->rate()); EXPECT_DOUBLE_EQ(rate->preExponentialFactor(), 1.2e11); - EXPECT_DOUBLE_EQ(TBR.thirdBody()->efficiencies["H2O"], 5.0); - EXPECT_DOUBLE_EQ(TBR.thirdBody()->default_efficiency, 1.0); } -TEST(Reaction, ThreeBodyFromYaml2) +TEST(Reaction, ThreeBodyFromYamlMissingM) { auto sol = newSolution("gri30.yaml", "", "None"); AnyMap rxn = AnyMap::fromYamlString( @@ -102,7 +98,7 @@ TEST(Reaction, ThreeBodyFromYaml2) EXPECT_THROW(newReaction(rxn, *(sol->kinetics())), CanteraError); } -TEST(Reaction, ThreeBodyFromYaml3) +TEST(Reaction, ThreeBodyFromYaml2) { auto sol = newSolution("gri30.yaml"); AnyMap rxn = AnyMap::fromYamlString( @@ -195,8 +191,7 @@ TEST(Reaction, PlogFromYaml) "- {P: 1.01325 MPa, A: 1.680000e+16, b: -0.6, Ea: 14754.0}"); auto R = newReaction(rxn, *(sol->kinetics())); - auto PR = dynamic_cast(*R); - const auto& rates = std::dynamic_pointer_cast(PR.rate())->rates(); + const auto& rates = std::dynamic_pointer_cast(R->rate())->rates(); EXPECT_EQ(rates.size(), (size_t) 4); EXPECT_NEAR(rates[0].first, 0.039474 * OneAtm, 1e-6); EXPECT_NEAR(rates[2].first, OneAtm, 1e-6); @@ -223,8 +218,7 @@ TEST(Reaction, ChebyshevFromYaml) auto R = newReaction(rxn, *(sol->kinetics())); EXPECT_EQ(R->reactants.size(), (size_t) 1); - auto CR = dynamic_cast(*R); - const auto& rate = std::dynamic_pointer_cast(CR.rate()); + const auto rate = std::dynamic_pointer_cast(R->rate()); double logP = std::log10(2e6); double T = 1800; rate->update_C(&logP); From bed79829f2aa5b3a0c9eefc0bb42d7c535de0904 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 6 Jun 2021 15:27:04 -0500 Subject: [PATCH 83/84] [Kinetics] Make rate constructor keyword consistent --- interfaces/cython/cantera/reaction.pyx | 37 ++++++---- .../cython/cantera/test/test_reaction.py | 71 +++++++++---------- 2 files changed, 57 insertions(+), 51 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index ff2013bdcb7..fd4e8115328 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -59,21 +59,21 @@ cdef class ArrheniusRate(_ReactionRate): 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`. + and *Ea* is the `activation_energy`. """ - def __cinit__(self, A=None, b=None, E=None, input_data=None, init=True): + def __cinit__(self, A=None, b=None, Ea=None, input_data=None, init=True): if init: if isinstance(input_data, dict): self._base.reset(new CxxArrheniusRate(dict_to_anymap(input_data))) - elif all([arg is not None for arg in [A, b, E]]): - self._base.reset(new CxxArrheniusRate(A, b, E)) - elif all([arg is None for arg in [A, b, E, input_data]]): + elif all([arg is not None for arg in [A, b, Ea]]): + self._base.reset(new CxxArrheniusRate(A, b, Ea)) + elif all([arg is None for arg in [A, b, Ea, input_data]]): self._base.reset(new CxxArrheniusRate(dict_to_anymap({}))) elif input_data: raise TypeError("Invalid parameter 'input_data'") else: - raise TypeError("Invalid parameters 'A', 'b' or 'E'") + raise TypeError("Invalid parameters 'A', 'b' or 'Ea'") self.base = self._base.get() self.rate = (self.base) @@ -1164,11 +1164,10 @@ cdef class PlogReaction(Reaction): rxn = PlogReaction( equation="H2 + O2 <=> 2 OH", - rate={"rate-constants": - [{"P": 1013.25, "A": 1.2124e+16, "b": -0.5779, "Ea": 45491376.8}, - {"P": 101325., "A": 4.9108e+31, "b": -4.8507, "Ea": 103649395.2}, - {"P": 1013250., "A": 1.2866e+47, "b": -9.0246, "Ea": 166508556.0}, - {"P": 10132500., "A": 5.9632e+56, "b": -11.529, "Ea": 220076726.4}]}, + rate=[(1013.25, Arrhenius(1.2124e+16, -0.5779, 45491376.8)), + (101325., Arrhenius(4.9108e+31, -4.8507, 103649395.2)), + (1013250., Arrhenius(1.2866e+47, -9.0246, 166508556.0)), + (10132500., Arrhenius(5.9632e+56, -11.529, 220076726.4))], kinetics=gas) The YAML description corresponding to this reaction is:: @@ -1204,9 +1203,17 @@ cdef class PlogReaction(Reaction): if legacy: rxn_type += "-legacy" spec = {"equation": equation, "type": rxn_type} - if isinstance(rate, dict): - spec.update(rate) - elif not legacy and (isinstance(rate, PlogRate) or rate is None): + if legacy and isinstance(rate, list): + rates = [] + for r in rate: + rates.append({ + "P": r[0], + "A": r[1].pre_exponential_factor, + "b": r[1].temperature_exponent, + "Ea": r[1].activation_energy, + }) + spec.update({'rate-constants': rates}) + elif not legacy and (isinstance(rate, (PlogRate, list)) or rate is None): pass else: raise TypeError("Invalid rate definition") @@ -1217,6 +1224,8 @@ cdef class PlogReaction(Reaction): if not legacy and isinstance(rate, PlogRate): self.rate = rate + elif not legacy and isinstance(rate, list): + self.rate = PlogRate(rate) property rate: """ Get/Set the `PlogRate` rate coefficients for this reaction. """ diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 9988ca53155..160ac05df00 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -495,8 +495,6 @@ class TestElementary(ReactionTests, utilities.CanteraTest): _type = "elementary" _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) - _kwargs = {} _index = 0 _yaml = """ equation: O + H2 <=> H + OH @@ -505,6 +503,15 @@ class TestElementary(ReactionTests, utilities.CanteraTest): _deprecated_getters = {"allow_negative_pre_exponential_factor": False} _deprecated_setters = {"allow_negative_pre_exponential_factor": True} + @classmethod + def setUpClass(cls): + ReactionTests.setUpClass() + if cls._legacy: + args = list(cls._rate.values()) + cls._rate_obj = ct.Arrhenius(*args) + else: + cls._rate_obj = ct.ArrheniusRate(**cls._rate) + def test_arrhenius(self): # test assigning Arrhenius rate rate = ct.Arrhenius(self._rate["A"], self._rate["b"], self._rate["Ea"]) @@ -524,7 +531,6 @@ class TestElementary2(TestElementary): _cls = ct.ElementaryReaction _type = "elementary-legacy" _legacy = True - _rate_obj = ct.Arrhenius(38.7, 2.7, 2.619184e+07) _yaml = """ equation: O + H2 <=> H + OH type: elementary-legacy @@ -539,7 +545,6 @@ class TestThreeBody(TestElementary): _type = "three-body" _equation = "2 O + M <=> O2 + M" _rate = {"A": 1.2e11, "b": -1.0, "Ea": 0.0} - _rate_obj = ct.ArrheniusRate(1.2e11, -1., 0.) _kwargs = {"efficiencies": {"H2": 2.4, "H2O": 15.4, "AR": 0.83}} _index = 1 _yaml = """ @@ -575,7 +580,6 @@ class TestThreeBody2(TestThreeBody): _cls = ct.ThreeBodyReaction _type = "three-body-legacy" _legacy = True - _rate_obj = ct.Arrhenius(1.2e11, -1., 0.) _yaml = """ equation: 2 O + M <=> O2 + M type: three-body-legacy @@ -591,7 +595,6 @@ class TestImplicitThreeBody(TestThreeBody): _type = "three-body" _equation = "H + 2 O2 <=> HO2 + O2" _rate = {"A": 2.08e+19, "b": -1.24, "Ea": 0.0} - _rate_obj = ct.ArrheniusRate(2.08e+19, -1.24, 0.) _index = 5 _yaml = """ equation: H + 2 O2 <=> HO2 + O2 @@ -622,15 +625,10 @@ class TestPlog(ReactionTests, utilities.CanteraTest): _type = "pressure-dependent-Arrhenius" _legacy = False _equation = "H2 + O2 <=> 2 OH" - _rate = {"rate-constants": [ - {"P": 1013.25, "A": 1.2124e+16, "b": -0.5779, "Ea": 45491376.8}, - {"P": 101325., "A": 4.9108e+31, "b": -4.8507, "Ea": 103649395.2}, - {"P": 1013250., "A": 1.2866e+47, "b": -9.0246, "Ea": 166508556.0}, - {"P": 10132500., "A": 5.9632e+56, "b": -11.529, "Ea": 220076726.4}]} - _rate_obj = ct.PlogRate([(1013.25, ct.Arrhenius(1.2124e+16, -0.5779, 45491376.8)), - (101325., ct.Arrhenius(4.9108e+31, -4.8507, 103649395.2)), - (1013250., ct.Arrhenius(1.2866e+47, -9.0246, 166508556.0)), - (10132500., ct.Arrhenius(5.9632e+56, -11.529, 220076726.4))]) + _rate = [(1013.25, ct.Arrhenius(1.2124e+16, -0.5779, 45491376.8)), + (101325., ct.Arrhenius(4.9108e+31, -4.8507, 103649395.2)), + (1013250., ct.Arrhenius(1.2866e+47, -9.0246, 166508556.0)), + (10132500., ct.Arrhenius(5.9632e+56, -11.529, 220076726.4))] _index = 3 _yaml = """ equation: H2 + O2 <=> 2 OH @@ -643,44 +641,45 @@ class TestPlog(ReactionTests, utilities.CanteraTest): """ _deprecated_callers = {(1000., ct.one_atm): 530968934612.9017} + @classmethod + def setUpClass(cls): + ReactionTests.setUpClass() + if not cls._legacy: + cls._rate_obj = ct.PlogRate(cls._rate) + def check_rates(self, rates, other): # helper function used by deprecation tests self.assertEqual(len(rates), len(other)) for index, item in enumerate(rates): P, rate = item - self.assertNear(P, other[index]["P"]) - self.assertNear(rate.pre_exponential_factor, other[index]["A"]) - self.assertNear(rate.temperature_exponent, other[index]["b"]) - self.assertNear(rate.activation_energy, other[index]["Ea"]) + self.assertNear(P, other[index][0]) + self.assertNear(rate.pre_exponential_factor, other[index][1].pre_exponential_factor) + self.assertNear(rate.temperature_exponent, other[index][1].temperature_exponent) + self.assertNear(rate.activation_energy, other[index][1].activation_energy) def test_deprecated_getters(self): # overload default tester for deprecated property getters rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) if self._legacy: - self.check_rates(rxn.rates, self._rate["rate-constants"]) + self.check_rates(rxn.rates, self._rate) else: with self.assertWarnsRegex(DeprecationWarning, "property is moved"): - self.check_rates(rxn.rates, self._rate["rate-constants"]) + self.check_rates(rxn.rates, self._rate) def test_deprecated_setters(self): # overload default tester for deprecated property setters - _rate = [ - {"P": 100., "A": 4.9108e+31, "b": -4.8507, "Ea": 103649395.2}, - {"P": 10000., "A": 1.2866e+47, "b": -9.0246, "Ea": 166508556.0}, - {"P": 1000000., "A": 5.9632e+56, "b": -11.529, "Ea": 220076726.4}] - rate = ct.PlogRate([(o["P"], ct.Arrhenius(o["A"], o["b"], o["Ea"])) - for o in _rate]) + rate = ct.PlogRate(self._rate) rates = rate.rates rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) if self._legacy: rxn.rates = rates - self.check_rates(rxn.rates, _rate) + self.check_rates(rxn.rates, self._rate) else: with self.assertWarnsRegex(DeprecationWarning, "Setter is replaceable"): rxn.rates = rates with self.assertWarnsRegex(DeprecationWarning, "property is moved"): - self.check_rates(rxn.rates, _rate) + self.check_rates(rxn.rates, self._rate) class TestPlog2(TestPlog): @@ -711,10 +710,6 @@ class TestChebyshev(ReactionTests, utilities.CanteraTest): "data": [[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], [ 1.9764e+00, 1.0037e+00, 7.2865e-03, -3.0432e-02], [ 3.1770e-01, 2.6889e-01, 9.4806e-02, -7.6385e-03]]} - _rate_obj = ct.ChebyshevRate(Tmin=290., Tmax=3000., Pmin=1000., Pmax=10000000.0, - data=[[ 8.2883e+00, -1.1397e+00, -1.2059e-01, 1.6034e-02], - [ 1.9764e+00, 1.0037e+00, 7.2865e-03, -3.0432e-02], - [ 3.1770e-01, 2.6889e-01, 9.4806e-02, -7.6385e-03]]) _index = 4 _yaml = """ equation: HO2 <=> OH + O @@ -730,11 +725,13 @@ class TestChebyshev(ReactionTests, utilities.CanteraTest): _deprecated_callers = {(1000., ct.one_atm): 2858762454.1119065} @classmethod - def setUpClass(obj): + def setUpClass(cls): ReactionTests.setUpClass() - obj._deprecated_getters.update({"coeffs": np.array(obj._rate["data"])}) - obj._deprecated_getters.update( - {k: v for k, v in obj._rate.items() if k != "data"}) + if not cls._legacy: + cls._rate_obj = ct.ChebyshevRate(**cls._rate) + cls._deprecated_getters.update({"coeffs": np.array(cls._rate["data"])}) + cls._deprecated_getters.update( + {k: v for k, v in cls._rate.items() if k != "data"}) class TestChebyshev2(TestChebyshev): From 952d11360ee5331e0d7dc698a403005a9937e0ce Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 6 Jun 2021 15:59:13 -0500 Subject: [PATCH 84/84] [Kinetics] Introduce Reaction.from_dict in Python This commit squashes several approaches for the creation of a Reaction from a dictionary that corresponds to a YAML definition. --- interfaces/cython/cantera/reaction.pyx | 56 +++++++++++++++++-- .../cython/cantera/test/test_reaction.py | 27 ++++++--- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index fd4e8115328..cd025c8587e 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -440,12 +440,53 @@ cdef class Reaction: return Reaction.wrap(cxx_reaction) @classmethod - def fromYaml(cls, text, Kinetics kinetics): + def from_dict(cls, data, Kinetics kinetics=None): + """ + Create a `Reaction` object from a dictionary corresponding to its YAML + representation. + + An example for the creation of a Reaction from a dictionary is:: + + rxn = Reaction.from_dict( + {"equation": "O + H2 <=> H + OH", + "rate-constant": {"A": 38.7, "b": 2.7, "Ea": 26191840.0}}, + kinetics=gas) + + In the example, *gas* is a Kinetics (or Solution) object. + + :param data: + A dictionary corresponding to the YAML representation. + :param kinetics: + A `Kinetics` object whose associated phase(s) contain the species + involved in the reaction. + """ + if cls._reaction_type != "": + raise TypeError( + "Class method 'from_dict' was invoked from '{}' but should " + "be called from base class 'Reaction'".format(cls.__name__)) + if kinetics is None: + raise ValueError("A Kinetics object is required.") + + cdef CxxAnyMap any_map = dict_to_anymap(data) + cxx_reaction = CxxNewReaction(any_map, deref(kinetics.kinetics)) + return Reaction.wrap(cxx_reaction) + + @classmethod + def fromYaml(cls, text, Kinetics kinetics=None): """ Create a `Reaction` object from its YAML string representation. + An example for the creation of a Reaction from a YAML string is:: + + rxn = Reaction.fromYaml(''' + equation: O + H2 <=> H + OH + rate-constant: {A: 38.7, b: 2.7, Ea: 6260.0 cal/mol} + ''', kinetics=gas) + + In the example, *gas* is a Kinetics (or Solution) object. + :param text: - The YAML reaction string + The YAML reaction string. :param kinetics: A `Kinetics` object whose associated phase(s) contain the species involved in the reaction. @@ -454,9 +495,12 @@ cdef class Reaction: raise TypeError( "Class method 'fromYaml' was invoked from '{}' but should " "be called from base class 'Reaction'".format(cls.__name__)) + if kinetics is None: + raise ValueError("A Kinetics object is required.") - cxx_reaction = CxxNewReaction(AnyMapFromYamlString(stringify(text)), - deref(kinetics.kinetics)) + cdef CxxAnyMap any_map + any_map = AnyMapFromYamlString(stringify(text)) + cxx_reaction = CxxNewReaction(any_map, deref(kinetics.kinetics)) return Reaction.wrap(cxx_reaction) @staticmethod @@ -1160,7 +1204,7 @@ cdef class PlogReaction(Reaction): A pressure-dependent reaction parameterized by logarithmically interpolating between Arrhenius rate expressions at various pressures. - An example for the definition of an `PlogReaction` object is given as:: + An example for the definition of a `PlogReaction` object is given as:: rxn = PlogReaction( equation="H2 + O2 <=> 2 OH", @@ -1315,7 +1359,7 @@ cdef class ChebyshevReaction(Reaction): A pressure-dependent reaction parameterized by a bivariate Chebyshev polynomial in temperature and pressure. - An example for the definition of an `PlogReaction` object is given as:: + An example for the definition of a `ChebyshevReaction` object is given as:: rxn = ChebyshevReaction( equation="HO2 <=> OH + O", diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 160ac05df00..df21c31dfbf 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -316,23 +316,26 @@ def setUpClass(cls): cls.gas.TP = 900, 2*ct.one_atm cls.species = cls.gas.species() - def check_rxn(self, rxn): + def check_rxn(self, rxn, check_legacy=True): # helper function that checks reaction configuration ix = self._index - self.assertEqual(rxn.reaction_type, self._type) self.assertEqual(rxn.reactants, self.gas.reaction(ix).reactants) self.assertEqual(rxn.products, self.gas.reaction(ix).products) - self.assertEqual(rxn.uses_legacy, self._type.endswith("-legacy")) + if check_legacy: + self.assertEqual(rxn.reaction_type, self._type) + self.assertEqual(rxn.uses_legacy, self._type.endswith("-legacy")) + self.assertEqual(rxn.uses_legacy, self._legacy) gas2 = ct.Solution(thermo="IdealGas", kinetics="GasKinetics", species=self.species, reactions=[rxn]) gas2.TPX = self.gas.TPX - self.check_solution(gas2) + self.check_solution(gas2, check_legacy) - def check_solution(self, gas2): + def check_solution(self, gas2, check_legacy=True): # helper function that checks evaluation of reaction rates ix = self._index - self.assertEqual(gas2.reaction_type_str(0), self._type) + if check_legacy: + 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], @@ -357,7 +360,7 @@ def test_from_parts(self): rxn.rate = self._rate_obj self.check_rxn(rxn) - def test_from_dict(self): + def test_from_dict1(self): # check instantiation from keywords / rate defined by dictionary rxn = self._cls(equation=self._equation, rate=self._rate, kinetics=self.gas, legacy=self._legacy, **self._kwargs) @@ -370,6 +373,16 @@ def test_from_yaml(self): rxn = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) self.check_rxn(rxn) + def test_from_dict2(self): + # check instantiation from a yaml dictionary + if self._yaml is None: + return + rxn1 = ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) + input_data = rxn1.input_data + rxn2 = ct.Reaction.from_dict(input_data, kinetics=self.gas) + # cannot compare types as input_data does not recreate legacy objects + self.check_rxn(rxn2, check_legacy=False) + def test_from_rate(self): # check instantiation from keywords / rate provided as object if self._rate_obj is None: