Skip to content

Commit

Permalink
Update documentation for ExtensibleRate
Browse files Browse the repository at this point in the history
  • Loading branch information
speth authored and ischoegl committed Jan 21, 2023
1 parent 19d55aa commit c336f24
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 44 deletions.
5 changes: 5 additions & 0 deletions doc/sphinx/cython/kinetics.rst
Expand Up @@ -154,6 +154,11 @@ StickingBlowersMaselRate
Auxiliary Reaction Data
-----------------------

ExtensibleRateData
^^^^^^^^^^^^^^^^^^
.. autoclass:: ExtensibleRateData
:no-undoc-members:

ThirdBody
^^^^^^^^^
.. autoclass:: ThirdBody
Expand Down
22 changes: 20 additions & 2 deletions include/cantera/base/ExtensionManager.h
Expand Up @@ -44,22 +44,40 @@ class ExtensionManager
throw NotImplementedError("ExtensionManager::registerRateBuilders");
};

//! Create an object in an external language that wraps the specified ReactionData
//! object
//!
//! @param rateName The name of the reaction rate type, which corresponds to the
//! name used to register the wrapper generator using registerReactionDataLinker
//! @param data The ReactionData object to be wrapped
static void wrapReactionData(const std::string& rateName,
ReactionDataDelegator& data);

static shared_ptr<ExternalHandle> wrapSolution(const std::string& rateName,
//! Create an object in an external language that wraps the specified Solution
//! object.
//!
//! @param wrapperType A name specifying the wrapper type, which corresponds to
//! the name used to register the wrapper generator using registerSolutionLinker
//! @param soln The Solution object to be wrapped
static shared_ptr<ExternalHandle> wrapSolution(const std::string& wrapperType,
shared_ptr<Solution> soln);

//! Register a function that can be used to create wrappers for ReactionData objects
//! in an external language and link them to the corresponding C++ object
static void registerReactionDataLinker(const std::string& rateName,
std::function<void(ReactionDataDelegator&)> link);

static void registerSolutionLinker(const std::string& rateName,
//! Register a function that can be used to create wrappers for Solution objects in
//! an external language and link it to the corresponding C++ objects
static void registerSolutionLinker(const std::string& wrapperName,
std::function<shared_ptr<ExternalHandle>(shared_ptr<Solution>)> link);

protected:
//! Functions for wrapping and linking ReactionData objects
static std::map<std::string,
std::function<void(ReactionDataDelegator&)>> s_ReactionData_linkers;

//! Functions for wrapping and linking Solution objects
static std::map<std::string,
std::function<shared_ptr<ExternalHandle>(shared_ptr<Solution>)>> s_Solution_linkers;

Expand Down
7 changes: 6 additions & 1 deletion include/cantera/extensions/PythonHandle.h
Expand Up @@ -16,6 +16,11 @@ namespace Cantera
class PythonHandle : public ExternalHandle
{
public:
//! Create a handle to hold a Python object
//! @param obj The Python object to be held
//! @param weak `true` if this is a weak reference to the Python object and this
//! handle is not responsible for deleting the Python object, or `false` if this
//! handle "owns" the Python object
PythonHandle(PyObject* obj, bool weak) : m_obj(obj), m_weak(weak) {}

~PythonHandle() {
Expand All @@ -24,7 +29,7 @@ class PythonHandle : public ExternalHandle
}
}

void* get() {
void* get() override {
return m_obj;
}

Expand Down
2 changes: 2 additions & 0 deletions include/cantera/kinetics/Kinetics.h
Expand Up @@ -1233,6 +1233,8 @@ class Kinetics
m_root = root;
}

//! Get the Solution object containing this Kinetics object and associated
//! ThermoPhase objects
shared_ptr<Solution> root() const {
return m_root.lock();
}
Expand Down
2 changes: 2 additions & 0 deletions include/cantera/kinetics/MultiRate.h
Expand Up @@ -129,6 +129,8 @@ class MultiRate final : public MultiRateBase
return R.evalFromStruct(m_shared);
}

//! Access the underlying shared data object. Used for setting up
//! ReactionDataDelegator instances.
DataType& sharedData() {
return m_shared;
}
Expand Down
21 changes: 21 additions & 0 deletions include/cantera/kinetics/ReactionRateDelegator.h
Expand Up @@ -30,28 +30,44 @@ class ReactionDataDelegator : public Delegator, public ReactionData

using ReactionData::update;

//! Set the type of the ReactionData class. This should match the corresponding
//! ReactionRate class's type
void setType(const std::string& name) {
m_rateType = name;
}

//! Get the external language wrapper for this ReactionData object
shared_ptr<ExternalHandle> getWrapper() const {
return m_wrappedData;
}

//! Set the external language wrapper for this ReactionData object
void setWrapper(shared_ptr<ExternalHandle> wrapper) {
m_wrappedData = wrapper;
}

//! Set the type of the Solution wrapper needed for this delegated reaction type.
//! This should correspond to the name registered for the external language with
//! ExtensionManager::registerSolutionLinker().
void setSolutionWrapperType(const std::string& type) {
m_solutionWrapperType = type;
}

protected:
//! The reaction rate type
std::string m_rateType;

//! The name registered for creating Solution wrappers for this delegated reaction
std::string m_solutionWrapperType;

//! An external language's wrapper for the Solution object where this ReactionData
//! object is being used
shared_ptr<ExternalHandle> m_wrappedSolution;

//! An external language's wrapper for this ReactionData object
shared_ptr<ExternalHandle> m_wrappedData;

//! Delegated `update` method taking the Solution wrapper as its argument
std::function<double(void*)> m_update;
};

Expand Down Expand Up @@ -88,8 +104,13 @@ class ReactionRateDelegator : public Delegator, public ReactionRate
}

private:
//! The name of the reaction rate type
std::string m_rateType;

//! Delegated `evalFromStruct` method taking a pointer to the corresponding
//! ReactionData wrapper object
std::function<double(void*)> m_evalFromStruct;

std::function<void(const AnyMap&, const UnitStack&)> m_setParameters;
};

Expand Down
35 changes: 21 additions & 14 deletions interfaces/cython/cantera/delegator.pyx
Expand Up @@ -222,15 +222,18 @@ cdef int assign_delegates(obj, CxxDelegator* delegator) except -1:
Methods that can be delegated are described by the ``delegatable_methods``
dict of ``obj``.
* The keys are the base names of the Python delegate methods,
which may be prefixed with ``before_``, ``after_``, or ``replace_`` in
a specific implementation of a delegated class. For example, for the base
name ``eval``, the delegate class can define one of these methods:
``before_eval``, ``after_eval``, or ``replace_eval``.
* The values are tuples of two elements, where the first element is the name
of the corresponding C++ method, and the second element indicates the
signature of the delegate function, such as ``void(double*)``.
* The keys are the base names of the Python delegate methods. For methods where
delegation is _optional_, the name is prefixed with ``before_``, ``after_``, or
``replace_`` in a specific implementation of a delegated class. For example, for
the base name ``eval``, the delegate class can define one of these methods:
``before_eval``, ``after_eval``, or ``replace_eval``. For methods where delegation
is _required_, no prefix is used.
* The values are tuples of two or three elements, where the first element is the
name of the corresponding C++ method, and the second element indicates the
signature of the delegate function, such as ``void(double*)``. The third element,
if present, indicates that the delegate is required, how it is executed with
respect to the base class method (that is, ``before``, ``after``, or ``replace``).
"""
# Find all delegate methods, and make sure there aren't multiple
# conflicting implementations
Expand Down Expand Up @@ -369,16 +372,20 @@ def extension(*, name, data=None):
import cantera as ct
@ct.extension(name="cool-rate")
class CoolRateData(ct.ExtensibleRateData):
def update(self, soln):
...
@ct.extension(name="cool-rate", data=CoolRateData)
class CoolRate(ct.ExtensibleRate):
def after_set_parameters(self, params, units):
def set_parameters(self, params, units):
...
def replace_eval(self, T):
def eval(self, data):
...
Loading this input file from any Cantera user interface would cause Cantera to load
the ``my_cool_module.py`` module and register the ``CoolRate`` class to handle
reactions whose ``type`` in the YAML file is set to ``cool-rate``.
the ``my_cool_module.py`` module and register the ``CoolRate`` and ``CoolRateData``
classes to handle reactions whose ``type`` in the YAML file is set to ``cool-rate``.
.. versionadded:: 3.0
"""
Expand Down
74 changes: 47 additions & 27 deletions interfaces/cython/cantera/reaction.pyx
Expand Up @@ -713,34 +713,14 @@ cdef class CustomRate(ReactionRate):

cdef class ExtensibleRate(ReactionRate):
"""
A base class for a reaction rate with delegated methods. Classes derived from this
class should be decorated with the `extension` decorator to specify the name
of the rate parameterization and to make these rates constructible through factory
functions and input files.
The following methods of the C++ :ct:`ReactionRate` class can be modified by a
Python class that inherits from this class. For each method, the name below should
be prefixed with ``before_``, ``after_``, or ``replace_``, indicating whether this
method should be called before, after, or instead of the corresponding method from
the base class.
For methods that return a value and have a ``before`` method specified, if that
method returns a value other than ``None`` that value will be returned without
calling the base class method; otherwise, the value from the base class method will
be returned. For methods that return a value and have an ``after`` method specified,
the returned value wil be the sum of the values from the supplied method and the
base class method.
``set_parameters(self, params: dict, units: Units) -> None``
Responsible for setting rate parameters based on the input data. For example,
for reactions created from YAML, ``params`` is the YAML reaction entry converted
to a ``dict``. ``units`` specifies the units of the rate coefficient.
A base class for a user-defined reaction rate parameterization. Classes derived from
this class should be decorated with the `extension` decorator to specify the name
of the rate parameterization and its corresponding data class, and to make these
rates constructible through factory functions and input files.
``eval(self, T: float) -> float``
Responsible for calculating the forward rate constant based on the current state
of the phase. This method must *replace* the base class method, as there is no
base class implementation. Currently, the state information provided is the
temperature, ``T`` [K].
Classes derived from `ExtensibleRate` should implement the `set_parameters` and
`eval` methods, which will be called as delegates from the C++ :ct:`ReactionRate`
class.
**Warning:** The delegatable methods defined here are an experimental part of the
Cantera API and may change without notice.
Expand All @@ -764,6 +744,22 @@ cdef class ExtensibleRate(ReactionRate):
assign_delegates(self, dynamic_cast[CxxDelegatorPtr](self.rate))
# ReactionRate does not define __init__, so it does not need to be called

def set_parameters(self, params: dict, units: Units) -> None:
"""
Responsible for setting rate parameters based on the input data. For example,
for reactions created from YAML, ``params`` is the YAML reaction entry converted
to a ``dict``. ``units`` specifies the units of the rate coefficient.
"""
raise NotImplementedError(f"{self.__class__}.set_parameters")

def eval(self, data: ExtensibleRateData) -> float:
"""
Responsible for calculating the forward rate constant based on the current state
of the phase, stored in an instance of a class derived from
`ExtensibleRateData`.
"""
raise NotImplementedError(f"{self.__class__}.eval")

cdef set_cxx_object(self, CxxReactionRate* rate=NULL):
if rate is NULL:
self.rate = self._rate.get()
Expand All @@ -774,11 +770,35 @@ cdef class ExtensibleRate(ReactionRate):
(<CxxReactionRateDelegator*>self.rate).setType(
stringify(self._reaction_rate_type))


cdef class ExtensibleRateData:
"""
A base class for data used when evaluating instances of `ExtensibleRate`. Classes
derived from `ExtensibleRateData` are used to store common data needed to evaluate
all reactions of a particular type.
Classes derived from `ExtensibleRateData` must implement the `update` method. After
the `update` method has been called, instances of `ExtensibleRateData` are passed as
the argument to `ExtensibleRate.eval`.
.. versionadded:: 3.0
"""
delegatable_methods = {
"update": ("update", "double(void*)", "replace")
}

def update(self, soln):
"""
This method takes a `Solution` object and stores any thermodynamic data (for
example, temperature and pressure) needed to evaluate all reactions of the
corresponding ReactionRate type.
If this state data has changed since the last time `update` was called and the
reaction rates need to be updated, this method should return `True`. Otherwise,
it should return `False`.
"""
raise NotImplementedError(f"{self.__class__}.update")

cdef set_cxx_object(self, CxxReactionDataDelegator* data):
assign_delegates(self, dynamic_cast[CxxDelegatorPtr](data))

Expand Down

0 comments on commit c336f24

Please sign in to comment.