From cd7b90ff97b90e0be926457a08c920b1d4fa210b Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Sat, 27 Aug 2022 17:13:39 -0400 Subject: [PATCH] Register ExtensibleRate types with ReactionRateFactory --- interfaces/cython/cantera/_cantera.pyx | 1 + src/extensions/PythonExtensionManager.cpp | 35 +++++++++++++++++++++-- src/extensions/pythonExtensions.pyx | 26 +++++++++++++++-- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pyx b/interfaces/cython/cantera/_cantera.pyx index 943080c4ef..4503566d6c 100644 --- a/interfaces/cython/cantera/_cantera.pyx +++ b/interfaces/cython/cantera/_cantera.pyx @@ -7,6 +7,7 @@ import sys import importlib import importlib.abc +import importlib.util # Chooses the right init function # See https://stackoverflow.com/a/52714500 diff --git a/src/extensions/PythonExtensionManager.cpp b/src/extensions/PythonExtensionManager.cpp index 5547922dcf..fba5fc9fb1 100644 --- a/src/extensions/PythonExtensionManager.cpp +++ b/src/extensions/PythonExtensionManager.cpp @@ -5,9 +5,15 @@ #include "cantera/extensions/PythonExtensionManager.h" +#include "cantera/kinetics/ReactionRateFactory.h" #include "cantera/kinetics/ReactionRateDelegator.h" #include "pythonExtensions.h" // generated by Cython +#include + +namespace ba = boost::algorithm; +using namespace std; + namespace Cantera { @@ -49,12 +55,35 @@ PythonExtensionManager::PythonExtensionManager() Py_DECREF(pyModule); } -void PythonExtensionManager::registerRateBuilders(const std::string& extensionName) +void PythonExtensionManager::registerRateBuilders(const string& extensionName) { char* c_rateTypes = ct_getPythonExtensibleRateTypes(extensionName); - std::string rateTypes(c_rateTypes); + string rateTypes(c_rateTypes); free(c_rateTypes); - writelog("Module returned types: '{}'\n", rateTypes); + + // Each line in rateTypes is a (class name, rate name) pair, separated by a tab + vector lines; + ba::split(lines, rateTypes, boost::is_any_of("\n")); + for (auto& line : lines) { + vector tokens; + ba::split(tokens, line, boost::is_any_of("\t")); + if (tokens.size() != 2) { + CanteraError("PythonExtensionManager::registerRateBuilders", + "Got unparsable input from ct_getPythonExtensibleRateTypes:" + "\n'''{}\n'''", rateTypes); + } + string rateName = tokens[0]; + + // Create a function that constructs and links a C++ ReactionRateDelegator + // object and a Python ExtensibleRate object of a particular type, and register + // this as the builder for reactions of this type + auto builder = [rateName, extensionName](const AnyMap& params, const UnitStack& units) { + auto delegator = make_unique(); + ct_addPythonExtensibleRate(delegator.get(), extensionName, rateName); + return delegator.release(); + }; + ReactionRateFactory::factory()->reg(tokens[1], builder); + } } }; diff --git a/src/extensions/pythonExtensions.pyx b/src/extensions/pythonExtensions.pyx index 52ae8bc9fa..ad5c37c619 100644 --- a/src/extensions/pythonExtensions.pyx +++ b/src/extensions/pythonExtensions.pyx @@ -7,14 +7,22 @@ from libcpp.string cimport string from libc.stdlib cimport malloc from libc.string cimport strcpy +from cpython.ref cimport Py_INCREF import importlib import inspect import cantera as ct -from cantera.reaction cimport ExtensibleRate +from cantera.reaction cimport ExtensibleRate, CxxReactionRate +from cantera.delegator cimport CxxDelegator, assign_delegates -cdef public char* ct_getPythonExtensibleRateTypes(const string& module_name): + +cdef extern from "cantera/kinetics/ReactionRateDelegator.h" namespace "Cantera": + cdef cppclass CxxReactionRateDelegator "Cantera::ReactionRateDelegator" (CxxDelegator, CxxReactionRate): + CxxReactionRateDelegator() + + +cdef public char* ct_getPythonExtensibleRateTypes(const string& module_name) except NULL: """ Load the named module and find classes derived from ExtensibleRate. @@ -23,10 +31,22 @@ cdef public char* ct_getPythonExtensibleRateTypes(const string& module_name): """ mod = importlib.import_module(module_name.decode()) names = "\n".join( - f"{name} {cls._reaction_rate_type}" + f"{name}\t{cls._reaction_rate_type}" for name, cls in inspect.getmembers(mod) if inspect.isclass(cls) and issubclass(cls, ct.ExtensibleRate)) tmp = bytes(names.encode()) cdef char* c_string = malloc((len(tmp) + 1) * sizeof(char)) strcpy(c_string, tmp) return c_string + + +cdef public int ct_addPythonExtensibleRate(CxxReactionRateDelegator* delegator, + const string& module_name, + const string& class_name) except -1: + + mod = importlib.import_module(module_name.decode()) + cdef ExtensibleRate rate = getattr(mod, class_name.decode())(init=False) + Py_INCREF(rate) + rate.set_cxx_object(delegator) + assign_delegates(rate, delegator) + return 0