Allow Solution to hold references to external wrappers

This commit is contained in:
Ray Speth 2022-12-10 13:10:13 -05:00 committed by Ingmar Schoegl
parent 760b0fb4b0
commit 2e65ab1ed5
8 changed files with 92 additions and 21 deletions

View File

@ -31,6 +31,8 @@ public:
class ExternalHandle
{
public:
ExternalHandle() {}
ExternalHandle(const ExternalHandle&) = delete;
virtual ~ExternalHandle() = default;
};

View File

@ -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();
@ -98,6 +99,15 @@ public:
//! 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
@ -110,6 +120,10 @@ protected:
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

View File

@ -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.

View File

@ -0,0 +1,34 @@
//! @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:
PythonHandle(PyObject* obj, bool weak) : m_obj(obj), m_weak(weak) {}
~PythonHandle() {
if (!m_weak) {
Py_XDECREF(m_obj);
}
}
private:
PyObject* m_obj;
bool m_weak;
};
}
#endif

View File

@ -29,6 +29,14 @@ cdef extern from "cantera/base/Interface.h" namespace "Cantera":
string, string, vector[string]) except +translate_exception
cdef extern from "cantera/extensions/PythonHandle.h" namespace "Cantera":
cdef cppclass CxxExternalHandle "Cantera::ExternalHandle":
pass
cdef cppclass CxxPythonHandle "Cantera::PythonHandle" (CxxExternalHandle):
CxxPythonHandle(PyObject*, cbool)
cdef extern from "cantera/base/Solution.h" namespace "Cantera":
cdef cppclass CxxKinetics "Cantera::Kinetics"
cdef cppclass CxxTransport "Cantera::Transport"
@ -50,6 +58,7 @@ cdef extern from "cantera/base/Solution.h" namespace "Cantera":
CxxAnyMap parameters(cbool) except +translate_exception
size_t nAdjacent()
shared_ptr[CxxSolution] adjacent(size_t)
void holdExternalHandle(string&, shared_ptr[CxxExternalHandle])
cdef shared_ptr[CxxSolution] CxxNewSolution "Cantera::Solution::create" ()
cdef shared_ptr[CxxSolution] newSolution (

View File

@ -88,17 +88,20 @@ cdef class _SolutionBase:
# Transport model: "" is a sentinel value to use the default model
transport = kwargs.get("transport_model", "")
# Parse YAML input
if infile or yaml:
# Parse YAML input
self._init_yaml(infile, name, adjacent, yaml, transport)
self._selected_species = np.ndarray(0, dtype=np.uint64)
return
else:
# Assign base and set managers
_assign_Solution(self, CxxNewSolution(), True)
self._init_parts(thermo, species, kinetics, transport, adjacent, reactions)
# Assign base and set managers
_assign_Solution(self, CxxNewSolution(), True)
self._init_parts(thermo, species, kinetics, transport, adjacent, reactions)
self._selected_species = np.ndarray(0, dtype=np.uint64)
cdef shared_ptr[CxxExternalHandle] handle
handle.reset(new CxxPythonHandle(<PyObject*>self, True))
self.base.holdExternalHandle(stringify("python-Solution"), handle)
def __init__(self, *args, **kwargs):
if isinstance(self, Transport) and kwargs.get("init", True):
assert self.transport is not NULL

View File

@ -8,6 +8,7 @@
#include "cantera/base/Solution.h"
#include "cantera/base/Interface.h"
#include "cantera/base/ExtensionManager.h"
#include "cantera/thermo/ThermoPhase.h"
#include "cantera/thermo/ThermoFactory.h"
#include "cantera/kinetics/Kinetics.h"
@ -45,6 +46,9 @@ void Solution::setThermo(shared_ptr<ThermoPhase> thermo) {
void Solution::setKinetics(shared_ptr<Kinetics> kinetics) {
m_kinetics = kinetics;
if (m_kinetics) {
m_kinetics->setRoot(shared_from_this());
}
}
void Solution::setTransport(shared_ptr<Transport> transport) {
@ -133,6 +137,21 @@ void Solution::setSource(const std::string& source) {
m_header.setMetadata("filename", filename);
}
void Solution::holdExternalHandle(const std::string& name,
shared_ptr<ExternalHandle> handle)
{
m_externalHandles[name] = handle;
}
shared_ptr<ExternalHandle> Solution::getExternalHandle(const std::string& name) const
{
if (m_externalHandles.count(name)) {
return m_externalHandles.at(name);
} else {
return shared_ptr<ExternalHandle>();
}
}
shared_ptr<Solution> newSolution(const std::string& infile,
const std::string& name,
const std::string& transport,

View File

@ -4,6 +4,7 @@
// at https://cantera.org/license.txt for license and copyright information.
#include "cantera/extensions/PythonExtensionManager.h"
#include "cantera/extensions/PythonHandle.h"
#include "cantera/kinetics/ReactionRateFactory.h"
#include "cantera/kinetics/ReactionRateDelegator.h"
@ -21,19 +22,6 @@ using namespace std;
namespace {
class PythonHandle : public Cantera::ExternalHandle
{
public:
explicit PythonHandle(PyObject* obj) : m_obj(obj) {}
~PythonHandle() {
Py_XDECREF(m_obj);
}
private:
PyObject* m_obj;
};
std::string getPythonExceptionInfo()
{
@ -80,6 +68,7 @@ void checkPythonError(bool condition, const std::string& message) {
namespace Cantera
{
bool PythonExtensionManager::s_imported = false;
PythonExtensionManager::PythonExtensionManager()
@ -201,7 +190,7 @@ void PythonExtensionManager::registerPythonRateBuilder(
delegator->setParameters(params, units);
// The delegator is responsible for eventually deleting the Python object
delegator->holdExternalHandle(make_shared<PythonHandle>(extRate));
delegator->holdExternalHandle(make_shared<PythonHandle>(extRate, false));
return delegator.release();
};
ReactionRateFactory::factory()->reg(rateName, builder);