mirror of
https://github.com/Cantera/cantera.git
synced 2025-02-25 18:55:29 -06:00
Add callbacks for synchronizing with C++ Solution members
Objects that need to hold pointers to the members of a C++ Solution, like the Python Solution object or StFlow, can register a callback with the Solution that will be called if any of the thermo/kinetics/transport objects change. Fixes #1409
This commit is contained in:
parent
6392724bd0
commit
c54e429472
@ -108,6 +108,22 @@ public:
|
||||
//! Returns a null pointer if the requested handle does not exist.
|
||||
shared_ptr<ExternalHandle> getExternalHandle(const std::string& name) const;
|
||||
|
||||
//! Register a function to be called if any of the Solution's thermo, kinetics,
|
||||
//! or transport objects is replaced.
|
||||
//! @param id A unique ID corresponding to the object affected by the callback.
|
||||
//! Typically, this is a pointer to an object that also holds a reference to the
|
||||
//! Solution object.
|
||||
//! @param callback The callback function to be called after any component of the
|
||||
//! Solution is replaced.
|
||||
//! When the callback becomes invalid (for example, the corresponding object is
|
||||
//! being deleted, the removeChangedCallback() method must be invoked.
|
||||
//! @since New in Cantera 3.0
|
||||
void registerChangedCallback(void* id, const function<void()>& callback);
|
||||
|
||||
//! Remove the callback function associated with the specified object.
|
||||
//! @since New in Cantera 3.0
|
||||
void removeChangedCallback(void* id);
|
||||
|
||||
protected:
|
||||
shared_ptr<ThermoPhase> m_thermo; //!< ThermoPhase manager
|
||||
shared_ptr<Kinetics> m_kinetics; //!< Kinetics manager
|
||||
@ -124,6 +140,10 @@ protected:
|
||||
//! Wrappers for this Kinetics object in extension languages, for evaluation
|
||||
//! of user-defined reaction rates
|
||||
std::map<std::string, shared_ptr<ExternalHandle>> m_externalHandles;
|
||||
|
||||
//! Callback functions that are invoked when the therm, kinetics, or transport
|
||||
//! members of the Solution are replaced.
|
||||
map<void*, function<void()>> m_changeCallbacks;
|
||||
};
|
||||
|
||||
//! Create and initialize a new Solution from an input file
|
||||
|
@ -63,6 +63,8 @@ public:
|
||||
//! @param points initial number of grid points
|
||||
StFlow(shared_ptr<Solution> sol, const std::string& id="", size_t points=1);
|
||||
|
||||
~StFlow();
|
||||
|
||||
//! @name Problem Specification
|
||||
//! @{
|
||||
|
||||
@ -440,11 +442,6 @@ protected:
|
||||
Kinetics* m_kin;
|
||||
Transport* m_trans;
|
||||
|
||||
// Smart pointer preventing garbage collection when the transport model of an
|
||||
// associated Solution object changes: the transport model of the StFlow object
|
||||
// will remain unaffected by an external change.
|
||||
shared_ptr<Transport> m_trans_shared;
|
||||
|
||||
// boundary emissivities for the radiation calculations
|
||||
doublereal m_epsilon_left;
|
||||
doublereal m_epsilon_right;
|
||||
|
@ -84,3 +84,4 @@ cdef extern from "cantera/base/ExtensionManagerFactory.h" namespace "Cantera":
|
||||
ctypedef CxxDelegator* CxxDelegatorPtr
|
||||
|
||||
cdef int assign_delegates(object, CxxDelegator*) except -1
|
||||
cdef void callback_v(PyFuncInfo& funcInfo)
|
||||
|
@ -59,6 +59,8 @@ cdef extern from "cantera/base/Solution.h" namespace "Cantera":
|
||||
size_t nAdjacent()
|
||||
shared_ptr[CxxSolution] adjacent(size_t)
|
||||
void holdExternalHandle(string&, shared_ptr[CxxExternalHandle])
|
||||
void registerChangedCallback(void*, function[void()])
|
||||
void removeChangedCallback(void*)
|
||||
|
||||
cdef shared_ptr[CxxSolution] CxxNewSolution "Cantera::Solution::create" ()
|
||||
cdef shared_ptr[CxxSolution] newSolution (
|
||||
@ -86,4 +88,5 @@ cdef class _SolutionBase:
|
||||
cdef np.ndarray _selected_species
|
||||
cdef object parent
|
||||
cdef object _adjacent
|
||||
cdef object _soln_changed_callback
|
||||
cdef public object _references
|
||||
|
@ -13,6 +13,7 @@ from .kinetics cimport *
|
||||
from .transport cimport *
|
||||
from .reaction cimport *
|
||||
from ._utils cimport *
|
||||
from .delegator cimport pyOverride, callback_v
|
||||
from .yamlwriter cimport YamlWriter
|
||||
|
||||
ctypedef CxxSurfPhase* CxxSurfPhasePtr
|
||||
@ -110,6 +111,10 @@ cdef class _SolutionBase:
|
||||
if name is not None:
|
||||
self.name = name
|
||||
|
||||
def __dealloc__(self):
|
||||
if self.base != NULL:
|
||||
self.base.removeChangedCallback(<PyObject*>self)
|
||||
|
||||
property name:
|
||||
"""
|
||||
The name assigned to this object. The default value corresponds
|
||||
@ -429,11 +434,22 @@ cdef _assign_Solution(_SolutionBase soln, shared_ptr[CxxSolution] cxx_soln,
|
||||
if not weak:
|
||||
# When the main application isn't Python, we should only hold a weak reference
|
||||
# here, since the C++ Solution object owns this Python Solution.
|
||||
if soln._base.get() != NULL:
|
||||
soln._base.get().removeChangedCallback(<PyObject*>(soln))
|
||||
soln._base = cxx_soln
|
||||
soln.base = cxx_soln.get()
|
||||
soln.thermo = soln.base.thermo().get()
|
||||
soln.kinetics = soln.base.kinetics().get()
|
||||
soln.transport = soln.base.transport().get()
|
||||
|
||||
def assign_pointers():
|
||||
soln.thermo = soln.base.thermo().get()
|
||||
soln.kinetics = soln.base.kinetics().get()
|
||||
soln.transport = soln.base.transport().get()
|
||||
assign_pointers()
|
||||
|
||||
soln.base.registerChangedCallback(<PyObject*>soln,
|
||||
pyOverride(<PyObject*>assign_pointers, callback_v))
|
||||
# PyOverride only holds a weak reference to the function, so this also needs to be
|
||||
# stored on the Python Solution object to have the right lifetime
|
||||
soln._soln_changed_callback = assign_pointers
|
||||
|
||||
cdef shared_ptr[CxxSolution] adj_soln
|
||||
if reset_adjacent:
|
||||
|
@ -195,7 +195,6 @@ cdef class Transport(_SolutionBase):
|
||||
|
||||
def __set__(self, model):
|
||||
self.base.setTransport(newTransport(self.thermo, stringify(model)))
|
||||
self.transport = self.base.transport().get()
|
||||
|
||||
property CK_mode:
|
||||
"""Boolean to indicate if the chemkin interpretation is used."""
|
||||
|
@ -42,6 +42,9 @@ void Solution::setName(const std::string& name) {
|
||||
|
||||
void Solution::setThermo(shared_ptr<ThermoPhase> thermo) {
|
||||
m_thermo = thermo;
|
||||
for (const auto& [id, callback] : m_changeCallbacks) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
void Solution::setKinetics(shared_ptr<Kinetics> kinetics) {
|
||||
@ -49,10 +52,16 @@ void Solution::setKinetics(shared_ptr<Kinetics> kinetics) {
|
||||
if (m_kinetics) {
|
||||
m_kinetics->setRoot(shared_from_this());
|
||||
}
|
||||
for (const auto& [id, callback] : m_changeCallbacks) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
void Solution::setTransport(shared_ptr<Transport> transport) {
|
||||
m_transport = transport;
|
||||
for (const auto& [id, callback] : m_changeCallbacks) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
void Solution::setTransportModel(const std::string& model) {
|
||||
@ -152,10 +161,20 @@ shared_ptr<ExternalHandle> Solution::getExternalHandle(const std::string& name)
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<Solution> newSolution(const std::string& infile,
|
||||
const std::string& name,
|
||||
const std::string& transport,
|
||||
const std::vector<shared_ptr<Solution>>& adjacent)
|
||||
void Solution::registerChangedCallback(void *id, const function<void()>& callback)
|
||||
{
|
||||
m_changeCallbacks[id] = callback;
|
||||
}
|
||||
|
||||
void Solution::removeChangedCallback(void* id)
|
||||
{
|
||||
m_changeCallbacks.erase(id);
|
||||
}
|
||||
|
||||
shared_ptr<Solution> newSolution(const std::string &infile,
|
||||
const std::string &name,
|
||||
const std::string &transport,
|
||||
const std::vector<shared_ptr<Solution>> &adjacent)
|
||||
{
|
||||
// get file extension
|
||||
size_t dot = infile.find_last_of(".");
|
||||
|
@ -64,8 +64,7 @@ IonFlow::IonFlow(shared_ptr<Solution> sol, const std::string& id, size_t points)
|
||||
m_solution = sol;
|
||||
m_id = id;
|
||||
m_kin = m_solution->kinetics().get();
|
||||
m_trans_shared = m_solution->transport();
|
||||
m_trans = m_trans_shared.get();
|
||||
m_trans = m_solution->transport().get();
|
||||
if (m_trans->transportModel() == "None") {
|
||||
// @deprecated
|
||||
warn_deprecated("IonFlow",
|
||||
@ -74,6 +73,10 @@ IonFlow::IonFlow(shared_ptr<Solution> sol, const std::string& id, size_t points)
|
||||
"is deprecated and\nwill be removed after Cantera 3.0.");
|
||||
setTransportModel("Ion");
|
||||
}
|
||||
m_solution->registerChangedCallback(this, [this]() {
|
||||
setKinetics(*m_solution->kinetics());
|
||||
setTransport(*m_solution->transport());
|
||||
});
|
||||
}
|
||||
|
||||
void IonFlow::resize(size_t components, size_t points){
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "cantera/oneD/StFlow.h"
|
||||
#include "cantera/oneD/refine.h"
|
||||
#include "cantera/transport/Transport.h"
|
||||
#include "cantera/transport/TransportFactory.h"
|
||||
#include "cantera/numerics/funcs.h"
|
||||
#include "cantera/base/global.h"
|
||||
|
||||
@ -113,8 +114,7 @@ StFlow::StFlow(shared_ptr<Solution> sol, const std::string& id, size_t points)
|
||||
m_solution = sol;
|
||||
m_id = id;
|
||||
m_kin = m_solution->kinetics().get();
|
||||
m_trans_shared = m_solution->transport();
|
||||
m_trans = m_trans_shared.get();
|
||||
m_trans = m_solution->transport().get();
|
||||
if (m_trans->transportModel() == "None") {
|
||||
// @deprecated
|
||||
warn_deprecated("StFlow",
|
||||
@ -123,6 +123,17 @@ StFlow::StFlow(shared_ptr<Solution> sol, const std::string& id, size_t points)
|
||||
"is deprecated and\nwill be removed after Cantera 3.0.");
|
||||
setTransportModel("Mix");
|
||||
}
|
||||
m_solution->registerChangedCallback(this, [this]() {
|
||||
setKinetics(*m_solution->kinetics());
|
||||
setTransport(*m_solution->transport());
|
||||
});
|
||||
}
|
||||
|
||||
StFlow::~StFlow()
|
||||
{
|
||||
if (m_solution) {
|
||||
m_solution->removeChangedCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
void StFlow::setThermo(IdealGasPhase& th) {
|
||||
@ -187,9 +198,6 @@ void StFlow::setTransportModel(const std::string& trans)
|
||||
"from a Solution manager: set Transport object directly instead.");
|
||||
}
|
||||
m_solution->setTransportModel(trans);
|
||||
m_trans_shared = m_solution->transport();
|
||||
m_trans = m_trans_shared.get();
|
||||
setTransport(*m_trans);
|
||||
}
|
||||
|
||||
std::string StFlow::transportModel() const {
|
||||
|
@ -128,6 +128,22 @@ class TestOnedim(utilities.CanteraTest):
|
||||
self.assertEqual(rtol_ss, set((5e-3, 3e-4, 7e-7)))
|
||||
self.assertEqual(rtol_ts, set((6e-3, 4e-4, 2e-7)))
|
||||
|
||||
def test_switch_transport(self):
|
||||
gas = ct.Solution('h2o2.yaml')
|
||||
gas.set_equivalence_ratio(0.9, 'H2', 'O2:0.21, N2:0.79')
|
||||
flame = ct.FreeFlame(gas, width=0.1)
|
||||
flame.set_initial_guess()
|
||||
|
||||
assert gas.transport_model == flame.transport_model == 'Mix'
|
||||
|
||||
flame.transport_model = 'UnityLewis'
|
||||
assert gas.transport_model == flame.transport_model == 'UnityLewis'
|
||||
Dkm_flame = flame.mix_diff_coeffs
|
||||
assert all(Dkm_flame[1,:] == Dkm_flame[2,:])
|
||||
|
||||
gas.transport_model = 'Multi'
|
||||
assert flame.transport_model == 'Multi'
|
||||
|
||||
|
||||
class TestFreeFlame(utilities.CanteraTest):
|
||||
tol_ss = [1.0e-5, 1.0e-14] # [rtol atol] for steady-state problem
|
||||
|
Loading…
Reference in New Issue
Block a user