diff --git a/include/cantera/kinetics/Kinetics.h b/include/cantera/kinetics/Kinetics.h index 8c1014196..4ef547ad8 100644 --- a/include/cantera/kinetics/Kinetics.h +++ b/include/cantera/kinetics/Kinetics.h @@ -806,6 +806,11 @@ public: */ virtual void addReaction(shared_ptr r); + /** + * Return the Reaction object for reaction *i*. + */ + shared_ptr reaction(size_t i); + //! Determine behavior when adding a new reaction that contains species not //! defined in any of the phases associated with this kinetics manager. If //! set to true, the reaction will silently be ignored. If false, (the diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index d083810d9..5e00130cc 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -190,6 +190,51 @@ cdef extern from "cantera/thermo/SurfPhase.h": void getCoverages(double*) except + +cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": + cdef cppclass CxxArrhenius "Cantera::Arrhenius": + CxxArrhenius() + CxxArrhenius(double, double, double) + double updateRC(double, double) + double preExponentialFactor() + double temperatureExponent() + double activationEnergy_R() + + cdef cppclass CxxReaction "Cantera::Reaction": + # Note, this default constructor doesn't actually exist. The declaration + # is required by a Cython bug which should be resolved in Cython 0.22. + CxxReaction() + CxxReaction(int) + + string reactantString() + string productString() + string equation() + void validate() except + + int reaction_type + Composition reactants + Composition products + Composition orders + string id + cbool reversible + cbool duplicate + cbool allow_nonreactant_orders + cbool allow_negative_orders + + cdef cppclass CxxElementaryReaction "Cantera::ElementaryReaction" (CxxReaction): + CxxElementaryReaction() + CxxArrhenius rate + cbool allow_negative_pre_exponential_factor + + cdef cppclass CxxThirdBody "Cantera::ThirdBody": + CxxThirdBody() + CxxThirdBody(double) + double efficiency(string) + Composition efficiencies + double default_efficiency + + cdef cppclass CxxThirdBodyReaction "Cantera::ThirdBodyReaction" (CxxElementaryReaction): + CxxThirdBodyReaction() + CxxThirdBody third_body + cdef extern from "cantera/kinetics/Kinetics.h" namespace "Cantera": cdef cppclass CxxKinetics "Cantera::Kinetics": CxxKinetics() @@ -204,6 +249,7 @@ cdef extern from "cantera/kinetics/Kinetics.h" namespace "Cantera": CxxThermoPhase& thermo(int) + shared_ptr[CxxReaction] reaction(size_t) except + cbool isReversible(int) except + int reactionType(int) except + string reactionString(int) except + @@ -657,6 +703,15 @@ cdef class ThermoPhase(_SolutionBase): cdef class InterfacePhase(ThermoPhase): cdef CxxSurfPhase* surf +cdef class Reaction: + cdef shared_ptr[CxxReaction] _reaction + cdef CxxReaction* reaction + cdef _assign(self, shared_ptr[CxxReaction] other) + +cdef class Arrhenius: + cdef CxxArrhenius* rate + cdef Reaction reaction # parent reaction, to prevent garbage collection + cdef class Kinetics(_SolutionBase): pass @@ -799,3 +854,4 @@ cdef np.ndarray get_transport_1d(Transport tran, transportMethod1d method) cdef np.ndarray get_transport_2d(Transport tran, transportMethod2d method) cdef CxxIdealGasPhase* getIdealGasPhase(ThermoPhase phase) except * cdef wrapSpeciesThermo(shared_ptr[CxxSpeciesThermo] spthermo) +cdef Reaction wrapReaction(shared_ptr[CxxReaction] reaction) diff --git a/interfaces/cython/cantera/_cantera.pyx b/interfaces/cython/cantera/_cantera.pyx index 416f082d3..7879decef 100644 --- a/interfaces/cython/cantera/_cantera.pyx +++ b/interfaces/cython/cantera/_cantera.pyx @@ -16,6 +16,7 @@ include "func1.pyx" include "base.pyx" include "speciesthermo.pyx" include "thermo.pyx" +include "reaction.pyx" include "kinetics.pyx" include "transport.pyx" include "composite.pyx" diff --git a/interfaces/cython/cantera/kinetics.pyx b/interfaces/cython/cantera/kinetics.pyx index 69939039c..e756f4513 100644 --- a/interfaces/cython/cantera/kinetics.pyx +++ b/interfaces/cython/cantera/kinetics.pyx @@ -73,6 +73,9 @@ cdef class Kinetics(_SolutionBase): self._check_phase_index(k) return self.kinetics.kineticsSpeciesIndex(k, phase) + def reaction(self, int i_reaction): + return wrapReaction(self.kinetics.reaction(i_reaction)) + def is_reversible(self, int i_reaction): """True if reaction `i_reaction` is reversible.""" self._check_reaction_index(i_reaction) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx new file mode 100644 index 000000000..e2a9481cc --- /dev/null +++ b/interfaces/cython/cantera/reaction.pyx @@ -0,0 +1,134 @@ +cdef extern from "cantera/kinetics/reaction_defs.h" namespace "Cantera": + cdef int ELEMENTARY_RXN + cdef int THREE_BODY_RXN + cdef int FALLOFF_RXN + cdef int PLOG_RXN + cdef int CHEBYSHEV_RXN + cdef int CHEMACT_RXN + cdef int INTERFACE_RXN + + + +cdef class Reaction: + def __cinit__(self, *args, init=True, **kwargs): + if init: + self._reaction.reset(new CxxReaction(0)) + self.reaction = self._reaction.get() + + cdef _assign(self, shared_ptr[CxxReaction] other): + self._reaction = other + self.reaction = self._reaction.get() + + property reactant_string: + def __get__(self): + return pystr(self.reaction.reactantString()) + + property product_string: + def __get__(self): + return pystr(self.reaction.productString()) + + property equation: + def __get__(self): + return pystr(self.reaction.equation()) + + property reactants: + def __get__(self): + return comp_map_to_dict(self.reaction.reactants) + + property products: + def __get__(self): + return comp_map_to_dict(self.reaction.products) + + property orders: + def __get__(self): + return comp_map_to_dict(self.reaction.orders) + + property ID: + def __get__(self): + return pystr(self.reaction.id) + + property reversible: + def __get__(self): + return self.reaction.reversible + + property duplicate: + def __get__(self): + return self.reaction.duplicate + + property allow_nonreactant_orders: + def __get__(self): + return self.reaction.allow_nonreactant_orders + + property allow_negative_orders: + def __get__(self): + return self.reaction.allow_negative_orders + + +cdef class Arrhenius: + def __cinit__(self, init=True): + if init: + self.rate = new CxxArrhenius() + self.reaction = None + + def __dealloc__(self): + if self.reaction is None: + del self.rate + + property preexponential_factor: + def __get__(self): + return self.rate.preExponentialFactor() + + property temperature_exponent: + def __get__(self): + return self.rate.temperatureExponent() + + property activation_energy: + def __get__(self): + return self.rate.activationEnergy_R() * gas_constant + + +cdef wrapArrhenius(CxxArrhenius* rate, Reaction reaction): + r = Arrhenius(init=False) + r.rate = rate + r.reaction = reaction + return r + + +cdef class ElementaryReaction(Reaction): + property rate: + def __get__(self): + cdef CxxElementaryReaction* r = self.reaction + return wrapArrhenius(&(r.rate), self) + + +cdef class ThirdBodyReaction(ElementaryReaction): + cdef CxxThirdBodyReaction* tbr(self): + return self.reaction + + property efficiencies: + def __get__(self): + return comp_map_to_dict(self.tbr().third_body.efficiencies) + + property default_efficiency: + def __get__(self): + return self.tbr().third_body.default_efficiency + + def efficiency(self, species): + return self.tbr().third_body.efficiency(stringify(species)) + + +cdef Reaction wrapReaction(shared_ptr[CxxReaction] reaction): + """ + Wrap a C++ Reaction object with a Python object of the correct derived type. + """ + cdef int reaction_type = reaction.get().reaction_type + + if reaction_type == ELEMENTARY_RXN: + R = ElementaryReaction(init=False) + elif reaction_type == THREE_BODY_RXN: + R = ThirdBodyReaction(init=False) + else: + R = Reaction(init=False) + + R._assign(reaction) + return R diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index b4f8b41ac..3f1cecef1 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -5,16 +5,6 @@ cdef enum Thermasis: molar_basis = 1 -cdef Composition comp_map(dict X) except *: - cdef Composition m - for species,value in X.items(): - m[stringify(species)] = value - return m - -cdef comp_map_to_dict(Composition m): - return {pystr(species):value for species,value in m.items()} - - cdef class Species: def __cinit__(self, *args, init=True, **kwargs): if init: diff --git a/interfaces/cython/cantera/utils.pyx b/interfaces/cython/cantera/utils.pyx index d808bdc8b..5b8d4ea8f 100644 --- a/interfaces/cython/cantera/utils.pyx +++ b/interfaces/cython/cantera/utils.pyx @@ -34,3 +34,12 @@ __version__ = pystr(get_cantera_version()) def appdelete(): """ Delete all global Cantera C++ objects """ CxxAppdelete() + +cdef Composition comp_map(dict X) except *: + cdef Composition m + for species,value in X.items(): + m[stringify(species)] = value + return m + +cdef comp_map_to_dict(Composition m): + return {pystr(species):value for species,value in m.items()} diff --git a/src/kinetics/Kinetics.cpp b/src/kinetics/Kinetics.cpp index 25a738483..64626f04e 100644 --- a/src/kinetics/Kinetics.cpp +++ b/src/kinetics/Kinetics.cpp @@ -769,6 +769,12 @@ void Kinetics::addReaction(shared_ptr r) m_ropnet.push_back(0.0); } +shared_ptr Kinetics::reaction(size_t i) +{ + checkReactionIndex(i); + return m_reactions[i]; +} + void Kinetics::installGroups(size_t irxn, const vector& r, const vector& p)