Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customizable data objects for ExtensibleRate #1417

Merged
merged 15 commits into from
Jan 21, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jobs:
sudo apt update
sudo apt install libboost-dev gfortran libopenmpi-dev libpython3-dev \
libblas-dev liblapack-dev libhdf5-dev
gcc --version
- name: Upgrade pip
run: python3 -m pip install -U pip setuptools wheel
- name: Install Python dependencies
Expand Down Expand Up @@ -189,6 +190,7 @@ jobs:
run: |
sudo apt update
sudo apt install libboost-dev gfortran liblapack-dev libblas-dev libsundials-dev libhdf5-dev
gcc --version
- name: Upgrade pip
run: python3 -m pip install -U pip setuptools wheel
- name: Install Python dependencies
Expand Down
5 changes: 5 additions & 0 deletions doc/sphinx/cython/kinetics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ StickingBlowersMaselRate
Auxiliary Reaction Data
-----------------------

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

ThirdBody
^^^^^^^^^
.. autoclass:: ThirdBody
Expand Down
31 changes: 23 additions & 8 deletions include/cantera/base/Delegator.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,16 @@ namespace Cantera
class Delegator
{
public:
//! Get the name of the user-defined class in the extension language
std::string delegatorName() const {
return m_delegatorName;
}

//! Set the name of the user-defined class in the extension language
void setDelegatorName(const std::string& delegatorName) {
m_delegatorName = delegatorName;
}

//! Set delegates for member functions with the signature `void()`.
void setDelegate(const std::string& name, const std::function<void()>& func,
const std::string& when)
Expand Down Expand Up @@ -215,7 +225,7 @@ class Delegator
throw NotImplementedError("Delegator::setDelegate",
"for function named '{}' with signature 'double(void*)'.", name);
}
*m_funcs_d_vp[name] = makeDelegate(func, when, m_base_d_vp[name]);
*m_funcs_d_vp[name] = makeDelegate(name, func, when, m_base_d_vp[name]);
}

//! Set delegates for member functions with the signature `string(size_t)`
Expand All @@ -228,7 +238,7 @@ class Delegator
"for function named '{}' with signature "
"'string(size_t)'.", name);
}
*m_funcs_s_sz[name] = makeDelegate(func, when, m_base_s_sz[name]);
*m_funcs_s_sz[name] = makeDelegate(name, func, when, m_base_s_sz[name]);
}

//! Set delegates for member functions with the signature `size_t(string)`
Expand All @@ -238,10 +248,10 @@ class Delegator
{
if (!m_funcs_sz_csr.count(name)) {
throw NotImplementedError("Delegator::setDelegate",
"for function named '{}' with signature "
"for function '{}' with signature "
"'size_t(const string&)'.", name);
}
*m_funcs_sz_csr[name] = makeDelegate(func, when, m_base_sz_csr[name]);
*m_funcs_sz_csr[name] = makeDelegate(name, func, when, m_base_sz_csr[name]);
}

void holdExternalHandle(const shared_ptr<ExternalHandle>& handle) {
Expand Down Expand Up @@ -382,6 +392,7 @@ class Delegator
//! Create a delegate for a function with a return value
template <typename ReturnType, class ... Args>
std::function<ReturnType(Args ...)> makeDelegate(
const std::string& name,
const std::function<int(ReturnType&, Args ...)>& func,
const std::string& when,
const std::function<ReturnType(Args ...)>& base)
Expand Down Expand Up @@ -413,20 +424,21 @@ class Delegator
}
};
} else if (when == "replace") {
return [base, func](Args ... args) {
return [base, name, func, this](Args ... args) {
ReturnType ret;
int has_ret = func(ret, args ...);
if (!has_ret) {
throw CanteraError("Lambda generated by Delegator::makeDelegate",
"Delegate for function of type '{}'\ndid not return a value",
demangle(typeid(base)));
"Method '{}' of class '{}' did not return a value of type '{}'.",
name, delegatorName(), demangle(typeid(ret)));
}
return ret;
};
} else {
throw CanteraError("Delegator::makeDelegate",
"For function named '{}':\n"
"'when' must be one of 'before', 'after', or 'replace';"
" not '{}", when);
" not '{}'", name, when);
}
}

Expand Down Expand Up @@ -486,6 +498,9 @@ class Delegator

//! Cleanup functions to be called from the destructor
std::list<shared_ptr<ExternalHandle>> m_handles;

//! Name of the class in the extension language
std::string m_delegatorName;
};

}
Expand Down
63 changes: 56 additions & 7 deletions include/cantera/base/ExtensionManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,29 @@
// at https://cantera.org/license.txt for license and copyright information.

#include "cantera/base/ctexceptions.h"
#include <functional>

namespace Cantera
{

class ReactionDataDelegator;
class Solution;

//! A base class for managing the lifetime of an external object, such as a Python
//! object used by a Delegator
class ExternalHandle
{
public:
ExternalHandle() {}
ExternalHandle(const ExternalHandle&) = delete;
virtual ~ExternalHandle() = default;

//! Get the underlying external object
virtual void* get() {
throw NotImplementedError("ExternalHandle::get");
}
};

//! Base class for managing user-defined Cantera extensions written in other languages
//!
//! @since New in Cantera 3.0
Expand All @@ -24,14 +43,44 @@ class ExtensionManager
virtual void registerRateBuilders(const std::string& extensionName) {
throw NotImplementedError("ExtensionManager::registerRateBuilders");
};
};

//! A base class for managing the lifetime of an external object, such as a Python
//! object used by a Delegator
class ExternalHandle
{
public:
virtual ~ExternalHandle() = default;
//! 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);

//! 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);

//! 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
16 changes: 15 additions & 1 deletion include/cantera/base/Solution.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ namespace Cantera
class ThermoPhase;
class Kinetics;
class Transport;
class ExternalHandle;

//! A container class holding managers for all pieces defining a phase
class Solution
class Solution : public std::enable_shared_from_this<Solution>
{
protected:
Solution();
Expand Down Expand Up @@ -98,6 +99,15 @@ class Solution
//! Overwrite source (only required if object is not created using newSolution)
void setSource(const std::string& source);

//! Store a handle to a wrapper for this Solution object from an external
//! language interface (for example, a Python Solution object)
void holdExternalHandle(const std::string& name, shared_ptr<ExternalHandle> handle);

//! Get the handle for a wrapper for this Solution object from an external
//! language interface.
//! Returns a null pointer if the requested handle does not exist.
shared_ptr<ExternalHandle> getExternalHandle(const std::string& name) const;

protected:
shared_ptr<ThermoPhase> m_thermo; //!< ThermoPhase manager
shared_ptr<Kinetics> m_kinetics; //!< Kinetics manager
Expand All @@ -110,6 +120,10 @@ class Solution
std::map<std::string, shared_ptr<Solution>> m_adjacentByName;

AnyMap m_header; //!< Additional input fields; usually from a YAML input file

//! Wrappers for this Kinetics object in extension languages, for evaluation
//! of user-defined reaction rates
std::map<std::string, shared_ptr<ExternalHandle>> m_externalHandles;
};

//! Create and initialize a new Solution from an input file
Expand Down
5 changes: 5 additions & 0 deletions include/cantera/extensions/PythonExtensionManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
namespace Cantera
{


//! Class for managing user-defined Cantera extensions written in Python
//!
//! Handles Python initialization if the main application is not the Python interpreter.
Expand All @@ -32,6 +33,10 @@ class PythonExtensionManager : public ExtensionManager
static void registerPythonRateBuilder(const std::string& moduleName,
const std::string& className, const std::string& rateName);

//! Function called from Cython to register an ExtensibleRateData implementation
static void registerPythonRateDataBuilder(const std::string& moduleName,
const std::string& className, const std::string& rateName);

private:
static bool s_imported;
};
Expand Down
43 changes: 43 additions & 0 deletions include/cantera/extensions/PythonHandle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//! @file PythonHandle.h

#ifndef CT_PYTHONHANDLE_H
#define CT_PYTHONHANDLE_H

// This file is part of Cantera. See License.txt in the top-level directory or
// at https://cantera.org/license.txt for license and copyright information.

#include "Python.h"
#include "cantera/base/ExtensionManager.h"

namespace Cantera
{

//! Class that holds an owned or weak (borrowed) reference to a Python object
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() {
if (!m_weak) {
Py_XDECREF(m_obj);
}
}

void* get() override {
return m_obj;
}

private:
PyObject* m_obj;
bool m_weak;
};

}

#endif
6 changes: 6 additions & 0 deletions include/cantera/kinetics/Kinetics.h
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,12 @@ 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();
}

//! Calculate the reaction enthalpy of a reaction which
//! has not necessarily been added into the Kinetics object
virtual double reactionEnthalpy(const Composition& reactants,
Expand Down
6 changes: 6 additions & 0 deletions include/cantera/kinetics/MultiRate.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ 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;
}

protected:
//! Helper function to process updates for rate types that implement the
//! `updateFromStruct` method.
Expand Down
Loading