Skip to content

Commit

Permalink
[Kinetics] make MultiRate and ReactionRate aware of each other
Browse files Browse the repository at this point in the history
  • Loading branch information
ischoegl committed Mar 29, 2021
1 parent 66df9cc commit 155f467
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 76 deletions.
62 changes: 44 additions & 18 deletions include/cantera/kinetics/MultiRate.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ class MultiRateBase
//! @param rxn_index index of reaction
//! @param rate reaction rate object
virtual void add(const size_t rxn_index,
ReactionRateBase& rate) = 0;
shared_ptr<ReactionRateBase> rate) = 0;

//! Replace reaction rate object handled by the evaluator
//! @param rxn_index index of reaction
//! @param rate reaction rate object
virtual bool replace(const size_t rxn_index,
ReactionRateBase& rate) = 0;
shared_ptr<ReactionRateBase> rate) = 0;

//! Evaluate all rate constants handled by the evaluator
//! @param bulk object representing bulk phase
Expand All @@ -58,38 +58,61 @@ template <class RateType, class DataType>
class MultiBulkRates final : public MultiRateBase
{
public:
virtual ~MultiBulkRates() {
for (size_t i = 0; i < m_bases.size(); i++) {
m_bases[i]->releaseEvaluator();
}
}

//! Called by destructor of ReactionRate object
void releaseBase(size_t rxn_index) {
size_t j = m_indices[rxn_index];
m_bases[j] = nullptr;
}

virtual void add(const size_t rxn_index,
ReactionRateBase& rate) override {
if (typeid(rate) != typeid(RateType)) {
shared_ptr<ReactionRateBase> rate) override {
if (typeid(*rate) != typeid(RateType)) {
throw CanteraError("MultiBulkRate::add",
"Wrong type: cannot add rate object of type '{}'",
rate->type());
} else if (rate->linked()) {
// ensure there are no dangling objects (unlikely)
throw CanteraError("MultiBulkRate::add",
"Wrong type: cannot add rate object of type '{}' ",
rate.type());
"Reaction rate is already linked to a reaction rate "
"evaluator. Re-linked reaction rates are not allowed");
}
size_t j = m_rates.size();
m_indices[rxn_index] = j;
m_rates.push_back(static_cast<RateType&>(rate));
m_rates.push_back(static_cast<RateType&>(*rate));
m_rxn.push_back(rxn_index);
// reassign pointer to newly created copy
//rate = std::make_shared<RateType>(m_rates[j]);
// keep link to original object
m_bases.push_back(rate);
}

virtual bool replace(const size_t rxn_index,
ReactionRateBase& rate) override {
shared_ptr<ReactionRateBase> rate) override {
if (!m_rates.size()) {
throw CanteraError("MultiBulkRate::replace",
"Invalid operation: cannot replace rate object "
"in empty rate handler.");
} else if (typeid(rate) != typeid(RateType)) {
} else if (typeid(*rate) != typeid(RateType)) {
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_rates[0].type(), rate->type());
} else if (rate->linked()) {
// ensure there are no dangling objects (unlikely)
throw CanteraError("MultiBulkRate::replace",
"Reaction rate is already linked to a reaction rate "
"evaluator. Re-linked reaction rates are not allowed");
}
if (m_indices.find(rxn_index) != m_indices.end()) {
size_t j = m_indices[rxn_index];
m_rates[j] = static_cast<RateType&>(rate);
// reassign pointer to rate object
//rate = std::make_shared<RateType>(m_rates[j]);
m_rates[j] = static_cast<RateType&>(*rate);
// release evaluator from previously used rate object and update
m_bases[j]->releaseEvaluator();
m_bases[j] = rate;
return true;
}
return false;
Expand All @@ -116,9 +139,12 @@ class MultiBulkRates final : public MultiRateBase
}

protected:
std::vector<RateType> m_rates; //! Reaction rate objects
std::vector<size_t> m_rxn; //! Index within overall rate vector
std::map<size_t, size_t> m_indices; //! Mapping of indices
//! Raw pointers to reaction rate objects managed by Reaction object
std::vector<shared_ptr<ReactionRateBase>> m_bases;

std::vector<RateType> m_rates; //!< Reaction rate objects
std::vector<size_t> m_rxn; //!< Index within overall rate vector
std::map<size_t, size_t> m_indices; //!< Mapping of indices
DataType m_shared;
};

Expand Down
16 changes: 2 additions & 14 deletions include/cantera/kinetics/Reaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ class Kinetics;
class Falloff;
class XML_Node;
class ThirdBody;
class MultiRateBase;

//! Abstract base class which stores data about a reaction and its rate
//! parameterization so that it can be added to a Kinetics object.
Expand Down Expand Up @@ -331,13 +330,8 @@ class Reaction3 : public Reaction
//! Set reaction rate pointer
void setRate(shared_ptr<ReactionRateBase> rate);

//! Link reaction to kinetics object
void linkEvaluator(size_t index, const shared_ptr<MultiRateBase> evaluator);

//! Indicate whether reaction is linked to a rate evaluator
bool linked() {
return bool(m_evaluator);
}
//! Indicate whether associated reaction rate is linked to a rate evaluator
bool linked();

//! Return index of reaction within the Kinetics object owning the rate
//! evaluator. Raises an exception if the reaction is not linked.
Expand All @@ -360,12 +354,6 @@ class Reaction3 : public Reaction
//! Relative efficiencies of third-body species in enhancing the reaction
//! rate (if applicable)
shared_ptr<ThirdBody> m_third_body;

//! Evaluator handling the reaction rate
shared_ptr<MultiRateBase> m_evaluator;

//! Index of reaction within kinetics object (if applicable)
size_t m_index;
};


Expand Down
28 changes: 26 additions & 2 deletions include/cantera/kinetics/ReactionRate.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace Cantera
{

class Func1;
class MultiRateBase;


//! Abstract base class for reaction rate definitions
Expand All @@ -34,6 +35,9 @@ class Func1;
*/
class ReactionRateBase
{
protected:
ReactionRateBase() : m_index(npos) {}

public:
virtual ~ReactionRateBase() {}

Expand Down Expand Up @@ -91,6 +95,26 @@ class ReactionRateBase

//! Validate the reaction rate expression
virtual void validate(const std::string& equation) = 0;

//! Indicate whether reaction is linked to a rate evaluator
bool linked() { return bool(m_evaluator); }

//! Link reaction to MultiRateBase evaluator
void linkEvaluator(size_t index, const shared_ptr<MultiRateBase> evaluator);

//! Link reaction to MultiRateBase evaluator
void releaseEvaluator();

//! Return index of reaction within the Kinetics object owning the rate
//! evaluator. Raises an exception if the reaction is not linked.
size_t index();

protected:
//! Evaluator handling the reaction rate
std::shared_ptr<MultiRateBase> m_evaluator;

//! Index of reaction within kinetics object (if applicable)
size_t m_index;
};


Expand All @@ -103,7 +127,7 @@ template <class DataType>
class ReactionRate : public ReactionRateBase
{
public:
ReactionRate() = default;
ReactionRate() : ReactionRateBase() {}

//! Update information specific to reaction
//! @param shared_data data shared by all reactions of a given type
Expand Down Expand Up @@ -382,7 +406,7 @@ class CustomFunc1Rate final : public ReactionRate<CustomFunc1Data>
//! Constructor does nothing, as there is no formalized parameterization
//! @param node AnyMap object containing reaction rate specification
//! @param rate_units Description of units used for rate parameters
CustomFunc1Rate(const AnyMap& rate, const Units& rate_units) {}
CustomFunc1Rate(const AnyMap& rate, const Units& rate_units);

virtual std::string type() const override { return "custom-function"; }

Expand Down
2 changes: 2 additions & 0 deletions interfaces/cython/cantera/_cantera.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,8 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera":
cdef cppclass CxxReactionRateBase "Cantera::ReactionRateBase":
CxxReactionRateBase()
string type()
cbool linked()
void releaseEvaluator()
void update(double) except +translate_exception
void update(double, double) except +translate_exception
double eval(double) except +translate_exception
Expand Down
7 changes: 7 additions & 0 deletions interfaces/cython/cantera/reaction.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ cdef class _ReactionRate:
def __repr__(self):
return "<{} at {:0x}>".format(pystr(self.base.type()), id(self))

property _linked:
def __get__(self):
return self.base.linked()

def _release(self):
self.base.releaseEvaluator()

def __call__(self, double temperature, pressure=None):
if pressure:
self.base.update(temperature, pressure)
Expand Down
19 changes: 18 additions & 1 deletion interfaces/cython/cantera/test/test_reaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class TestReactionRate(utilities.CanteraTest):
_type = None
_uses_pressure = False
_index = None
rate = None

@classmethod
def setUpClass(cls):
Expand Down Expand Up @@ -41,6 +42,11 @@ def test_rate_TP(self):
self.assertNear(self.rate(self.gas.T, self.gas.P),
self.gas.forward_rate_constants[self._index])

def test_linked(self):
if self.rate is None:
return
self.assertFalse(self.rate._linked)


class TestArrheniusRate(TestReactionRate):

Expand Down Expand Up @@ -145,6 +151,11 @@ def setUpClass(cls):
cls.gas.TP = 900, 2*ct.one_atm
cls.species = cls.gas.species()

def setUp(self):
if self._rate_obj is None or not self._new:
return
self._rate_obj._release()

def check_rxn(self, rxn):
ix = self._index
self.assertEqual(rxn.reaction_type, self._type)
Expand All @@ -154,6 +165,7 @@ def check_rxn(self, rxn):
self.assertFalse(rxn._linked)
with self.assertRaises(ct.CanteraError):
rxn.index
self.assertFalse(rxn.rate._linked)

gas2 = ct.Solution(thermo='IdealGas', kinetics='GasKinetics',
species=self.species, reactions=[rxn])
Expand All @@ -168,6 +180,7 @@ def check_sol(self, gas2, index=None):
rxn = self.gas.reaction(index)
self.assertTrue(rxn._linked)
self.assertEqual(rxn.index, index)
self.assertTrue(rxn.rate._linked)

self.assertEqual(gas2.reaction_type_str(index), self._type)
self.assertNear(gas2.forward_rate_constants[index],
Expand All @@ -180,9 +193,11 @@ def test_rate(self):
return
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])
if self._new:
self.assertFalse(self._rate_obj._linked)

def test_from_parts(self):
if self._cls is None or not hasattr(self._cls, 'rate'):
Expand Down Expand Up @@ -256,6 +271,7 @@ def test_replace_reaction(self):
rxn = self._cls(equation=self._equation, rate=self._rate, kinetics=self.gas, **self._kwargs)
if self._new:
self.assertFalse(rxn._linked)
self.assertFalse(rxn.rate._linked)

gas2 = ct.Solution('kineticsfromscratch.yaml')
gas2.TPX = self.gas.TPX
Expand Down Expand Up @@ -307,6 +323,7 @@ class TestCustom(TestReaction):

def setUp(self):
# need to overwrite rate to ensure correct type ('method' is not compatible with Func1)
super().setUp()
self._rate = lambda T: 38.7 * T**2.7 * exp(-3150.15428/T)

def test_no_rate(self):
Expand Down
9 changes: 5 additions & 4 deletions src/kinetics/BulkKinetics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,9 @@ bool BulkKinetics::addReaction(shared_ptr<Reaction> r)

// Add reaction rate to evaluator
size_t index = m_bulk_types[rate->type()];
m_bulk_rates[index]->add(nReactions() - 1, *rate);
r3->linkEvaluator(nReactions() - 1, m_bulk_rates[index]);
size_t i = nReactions() - 1;
m_bulk_rates[index]->add(i, rate);
rate->linkEvaluator(i, m_bulk_rates[index]);

// Add reaction to third-body evaluator
if (r3->thirdBody() != nullptr) {
Expand Down Expand Up @@ -203,8 +204,8 @@ void BulkKinetics::modifyReaction(size_t i, shared_ptr<Reaction> rNew)

// Replace reaction rate to evaluator
size_t index = m_bulk_types[rate->type()];
m_bulk_rates[index]->replace(i, *rate);
r3->linkEvaluator(i, m_bulk_rates[index]);
m_bulk_rates[index]->replace(i, rate);
rate->linkEvaluator(i, m_bulk_rates[index]);
}
}

Expand Down
28 changes: 14 additions & 14 deletions src/kinetics/Reaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include "cantera/kinetics/ReactionFactory.h"
#include "cantera/kinetics/FalloffFactory.h"
#include "cantera/kinetics/Kinetics.h"
#include "cantera/kinetics/MultiRate.h"
#include "cantera/thermo/ThermoPhase.h"
#include "cantera/base/ctml.h"
#include "cantera/base/Array.h"
Expand Down Expand Up @@ -346,38 +345,39 @@ ElectrochemicalReaction::ElectrochemicalReaction(const Composition& reactants_,
}

Reaction3::Reaction3()
: Reaction()
, m_index(npos)
: Reaction()
{
}

Reaction3::Reaction3(const Composition& reactants, const Composition& products)
: Reaction(reactants, products)
, m_index(npos)
{
}

void Reaction3::setRate(shared_ptr<ReactionRateBase> rate)
{
m_rate = rate;
if (m_evaluator) {
m_evaluator->replace(m_index, *rate);
if (m_rate) {
m_rate->releaseEvaluator();
}
m_rate = rate;
}

void Reaction3::linkEvaluator(size_t index, shared_ptr<MultiRateBase> evaluator)
bool Reaction3::linked()
{
m_index = index;
m_evaluator = evaluator;
if (m_rate) {
return m_rate->linked();
}
throw CanteraError("Reaction3::linked", "Not applicable: no "
"associated reaction rate object.");
}

size_t Reaction3::index()
{
if (m_evaluator) {
return m_index;
if (m_rate) {
return m_rate->index();
}
throw CanteraError("Reaction3::index", "Not applicable, as reaction is not "
"linked to Kinetics object with associated rate evaluator");
throw CanteraError("Reaction3::index", "Not applicable: no "
"associated reaction rate object.");
}

bool Reaction3::setParameters(const AnyMap& node, const Kinetics& kin)
Expand Down
Loading

0 comments on commit 155f467

Please sign in to comment.