This commit is contained in:
Ingmar Schoegl 2025-02-16 11:59:25 -05:00 committed by GitHub
commit 74e10f9989
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 790 additions and 672 deletions

View File

@ -19,12 +19,12 @@ extern "C" {
CANTERA_CAPI int reactor_new(const char* type, int n, const char* name);
CANTERA_CAPI int reactor_del(int i);
CANTERA_CAPI int reactor_type(int i, int len, char* nbuf);
CANTERA_CAPI int reactor_name(int i, int len, char* nbuf);
CANTERA_CAPI int reactor_setName(int i, const char* name);
CANTERA_CAPI int reactor_setInitialVolume(int i, double v);
CANTERA_CAPI int reactor_setChemistry(int i, int cflag);
CANTERA_CAPI int reactor_setEnergy(int i, int eflag);
CANTERA_CAPI int reactor_setSolution(int i, int n);
CANTERA_CAPI double reactor_mass(int i);
CANTERA_CAPI double reactor_volume(int i);
CANTERA_CAPI double reactor_density(int i);
@ -51,11 +51,12 @@ extern "C" {
CANTERA_CAPI double reactornet_atol(int i);
CANTERA_CAPI double reactornet_sensitivity(int i, const char* v, int p, int r);
CANTERA_CAPI int flowdev_new(const char* type, const char* name);
CANTERA_CAPI int flowdev_del(int i);
CANTERA_CAPI int flowdev_name(int i, int len, char* nbuf);
CANTERA_CAPI int flowdev_setName(int i, const char* name);
CANTERA_CAPI int flowdev_install(int i, int n, int m);
CANTERA_CAPI int connector_new(const char* type, int r0, int r1, const char* name);
CANTERA_CAPI int connector_del(int i);
CANTERA_CAPI int connector_type(int i, int len, char* nbuf);
CANTERA_CAPI int connector_name(int i, int len, char* nbuf);
CANTERA_CAPI int connector_setName(int i, const char* name);
CANTERA_CAPI int flowdev_setPrimary(int i, int n);
CANTERA_CAPI double flowdev_massFlowRate(int i);
CANTERA_CAPI int flowdev_setMassFlowCoeff(int i, double v);
@ -64,11 +65,6 @@ extern "C" {
CANTERA_CAPI int flowdev_setPressureFunction(int i, int n);
CANTERA_CAPI int flowdev_setTimeFunction(int i, int n);
CANTERA_CAPI int wall_new(const char* type, const char* name);
CANTERA_CAPI int wall_del(int i);
CANTERA_CAPI int wall_name(int i, int len, char* nbuf);
CANTERA_CAPI int wall_setName(int i, const char* name);
CANTERA_CAPI int wall_install(int i, int n, int m);
CANTERA_CAPI double wall_expansionRate(int i);
CANTERA_CAPI double wall_heatRate(int i);
CANTERA_CAPI double wall_area(int i);
@ -79,7 +75,6 @@ extern "C" {
CANTERA_CAPI int wall_setExpansionRateCoeff(int i, double k);
CANTERA_CAPI int wall_setVelocity(int i, int n);
CANTERA_CAPI int wall_setEmissivity(int i, double epsilon);
CANTERA_CAPI int wall_ready(int i);
CANTERA_CAPI int reactorsurface_new(const char* name);
CANTERA_CAPI int reactorsurface_del(int i);

View File

@ -0,0 +1,98 @@
//! @file ConnectorFactory.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.
#ifndef CONNECTOR_FACTORY_H
#define CONNECTOR_FACTORY_H
#include "cantera/base/FactoryBase.h"
#include "cantera/zeroD/ConnectorNode.h"
namespace Cantera
{
class FlowDevice;
class WallBase;
//! Factory class to create ConnectorNode objects.
//!
//! This class is mainly used via the newConnectorNode() function, for example:
//!
//! ```cpp
//! shared_ptr<ConnectorNode> valve = newConnectorNode("Valve", r0, r1, "my_valve");
//! ```
//!
//! where `r0` and `r1` are reactor objects.
class ConnectorFactory :
public Factory<ConnectorNode,
shared_ptr<ReactorBase>, shared_ptr<ReactorBase>, const string&>
{
public:
static ConnectorFactory* factory();
void deleteFactory() override;
private:
static ConnectorFactory* s_factory;
static std::mutex connector_mutex;
ConnectorFactory();
};
//! @defgroup connectorGroup Connectors
//! %ConnectorNode objects connect zero-dimensional reactors.
//! ConnectorNode objects should be instantiated via the newConnectorNode() function,
//! for example:
//!
//! ```cpp
//! shared_ptr<ConnectorNode> valve = newConnectorNode("Valve", r0, r1, "my_valve");
//! ```
//!
//! where `r0` and `r1` are reactor objects.
//!
//! @since New in %Cantera 3.2.
//!
//! @ingroup zerodGroup
//! @{
//! Create a ConnectorNode object of the specified type
//! @param model String specifying reactor type.
//! @param r0 First reactor.
//! @param r1 Second reactor.
//! @param name Name of the connector.
//! @since New in %Cantera 3.2.
shared_ptr<ConnectorNode> newConnectorNode(const string& model,
shared_ptr<ReactorBase> r0,
shared_ptr<ReactorBase> r1,
const string& name="(none)");
//! Create a FlowDevice object of the specified type
//! @since Starting in %Cantera 3.1, this method returns a `shared_ptr<FlowDevice>`
//! @deprecated To be removed after %Cantera 3.2. Use version that provides reactors
//! as parameter instead.
shared_ptr<FlowDevice> newFlowDevice(const string& model, const string& name="(none)");
//! Create a FlowDevice object of the specified type.
//! @copydetails newConnectorNode
shared_ptr<FlowDevice> newFlowDevice(const string& model,
shared_ptr<ReactorBase> r0,
shared_ptr<ReactorBase> r1,
const string& name="(none)");
//! Create a WallBase object of the specified type
//! @since Starting in %Cantera 3.1, this method returns a `shared_ptr<WallBase>`
//! @deprecated To be removed after %Cantera 3.2. Use version that provides reactors
//! as parameter instead.
shared_ptr<WallBase> newWall(const string& model, const string& name="(none)");
//! Create a WallBase object of the specified type.
//! @copydetails newConnectorNode
shared_ptr<WallBase> newWall(const string& model,
shared_ptr<ReactorBase> r0,
shared_ptr<ReactorBase> r1,
const string& name="(none)");
//! @}
}
#endif

View File

@ -0,0 +1,74 @@
//! @file ConnectorNode.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.
#ifndef CT_CONNECTOR_H
#define CT_CONNECTOR_H
#include "cantera/base/ct_defs.h"
#include "cantera/base/global.h"
namespace Cantera
{
class ReactorBase;
/**
* Base class for walls and flow devices connecting reactors.
* In a reactor network, walls and flow devices (e.g., valves, pressure regulators)
* represent nodes in a directed bipartite graph - a graph whose vertices can be
* divided into two disjoint sets such that no two vertices within the same set are
* adjacent - with reactors forming the second set of nodes.
*
* @since New in %Cantera 3.2.
*
* @ingroup connectorGroup
*/
class ConnectorNode
{
public:
//! Transitional constructor.
//! @todo Implement deprecation warning.
ConnectorNode(const string& name="(none)") : m_name(name) {}
//! Instantiate a ConnectorNode object with associated ReactorBase objects.
//! @param r0 First reactor.
//! @param r1 Second reactor.
//! @param name Name of the connector.
ConnectorNode(shared_ptr<ReactorBase> r0, shared_ptr<ReactorBase> r1,
const string& name="(none)") : m_nodes({r0, r1}), m_name(name) {}
virtual ~ConnectorNode() = default;
ConnectorNode(const ConnectorNode&) = delete;
ConnectorNode& operator=(const ConnectorNode&) = delete;
//! String indicating the connector implemented. Usually
//! corresponds to the name of the derived class.
virtual string type() const {
return "ConnectorNode";
}
//! Retrieve connector name.
string name() const {
return m_name;
}
//! Set connector name.
void setName(const string& name) {
m_name = name;
}
//! Set the default name of a connector. Returns `false` if it was previously set.
void setDefaultName(map<string, int>& counts);
protected:
//! Pair of reactors forming end points of the connector.
pair<shared_ptr<ReactorBase>, shared_ptr<ReactorBase>> m_nodes;
string m_name; //!< ConnectorNode name.
bool m_defaultNameSet = false; //!< `true` if default name has been previously set.
};
}
#endif

View File

@ -9,6 +9,7 @@
#include "cantera/base/ct_defs.h"
#include "cantera/base/global.h"
#include "cantera/base/ctexceptions.h"
#include "ConnectorNode.h"
namespace Cantera
{
@ -18,36 +19,19 @@ class ReactorBase;
/**
* Base class for 'flow devices' (valves, pressure regulators, etc.)
* connecting reactors.
* @ingroup flowDeviceGroup
* @ingroup connectorGroup
*/
class FlowDevice
class FlowDevice : public ConnectorNode
{
public:
FlowDevice(const string& name="(none)") : m_name(name) {}
FlowDevice(shared_ptr<ReactorBase> r0, shared_ptr<ReactorBase> r1,
const string& name="(none)");
using ConnectorNode::ConnectorNode; // inherit constructors
virtual ~FlowDevice() = default;
FlowDevice(const FlowDevice&) = delete;
FlowDevice& operator=(const FlowDevice&) = delete;
//! String indicating the flow device implemented. Usually
//! corresponds to the name of the derived class.
virtual string type() const {
string type() const override {
return "FlowDevice";
}
//! Retrieve flow device name.
string name() const {
return m_name;
}
//! Set flow device name.
void setName(const string& name) {
m_name = name;
}
//! Set the default name of a flow device. Returns `false` if it was previously set.
bool setDefaultName(map<string, int>& counts);
//! Mass flow rate (kg/s).
double massFlowRate() {
if (m_mdot == Undef) {
@ -73,6 +57,8 @@ public:
/*!
* @param in Upstream reactor.
* @param out Downstream reactor.
* @deprecated To be removed after %Cantera 3.2. Reactors should be provided to
* constructor instead.
*/
bool install(ReactorBase& in, ReactorBase& out);
@ -133,9 +119,6 @@ public:
}
protected:
string m_name; //!< Flow device name.
bool m_defaultNameSet = false; //!< `true` if default name has been previously set.
double m_mdot = Undef;
//! Function set by setPressureFunction; used by updateMassFlowRate

View File

@ -6,48 +6,9 @@
#ifndef FLOWDEVICE_FACTORY_H
#define FLOWDEVICE_FACTORY_H
#include "cantera/base/FactoryBase.h"
#include "cantera/zeroD/FlowDevice.h"
#pragma message("warning: FlowDeviceFactory.h is deprecated and will be removed " \
"after Cantera 3.2. Use ConnectorFactory.h instead.")
namespace Cantera
{
//! Factory class to create FlowDevice objects.
//!
//! This class is mainly used via the newFlowDevice() function, for example:
//!
//! ```cpp
//! shared_ptr<FlowDevice> mfc = newFlowDevice("MassFlowController");
//! ```
class FlowDeviceFactory : public Factory<FlowDevice, const string&>
{
public:
static FlowDeviceFactory* factory();
void deleteFactory() override;
private:
static FlowDeviceFactory* s_factory;
static std::mutex flowDevice_mutex;
FlowDeviceFactory();
};
//! @defgroup flowDeviceGroup Flow Devices
//! Flow device objects connect zero-dimensional reactors.
//! FlowDevice objects should be instantiated via the newFlowDevice function, for
//! example:
//!
//! ```cpp
//! shared_ptr<FlowDevice> mfc = newFlowDevice("MassFlowController", "my_mfc");
//! ```
//! @ingroup zerodGroup
//! @{
//! Create a FlowDevice object of the specified type
//! @since Starting in %Cantera 3.1, this method returns a `shared_ptr<FlowDevice>`
shared_ptr<FlowDevice> newFlowDevice(const string& model, const string& name="(none)");
//! @}
}
#include "ConnectorFactory.h"
#endif

View File

@ -80,6 +80,8 @@ public:
//! Set the Solution specifying the ReactorBase content.
//! @param sol Solution object to be set.
//! @since New in %Cantera 3.1.
//! @deprecated To be removed after %Cantera 3.2. Superseded by instantiation of
//! ReactorBase with Solution object.
void setSolution(shared_ptr<Solution> sol);
//! @name Methods to set up a simulation

View File

@ -16,7 +16,6 @@ class SurfPhase;
//! A surface where reactions can occur that is in contact with the bulk fluid of a
//! Reactor.
//! @ingroup wallGroup
class ReactorSurface
{
public:

View File

@ -8,6 +8,7 @@
#include "cantera/base/ctexceptions.h"
#include "cantera/zeroD/ReactorBase.h"
#include "ConnectorNode.h"
namespace Cantera
{
@ -16,36 +17,19 @@ class Func1;
/**
* Base class for 'walls' (walls, pistons, etc.) connecting reactors.
* @ingroup wallGroup
* @ingroup connectorGroup
*/
class WallBase
class WallBase : public ConnectorNode
{
public:
WallBase(const string& name="(none)") : m_name(name) {}
WallBase(shared_ptr<ReactorBase> r0, shared_ptr<ReactorBase> r1,
const string& name="(none)");
using ConnectorNode::ConnectorNode; // inherit constructors
virtual ~WallBase() {}
WallBase(const WallBase&) = delete;
WallBase& operator=(const WallBase&) = delete;
//! String indicating the wall model implemented. Usually
//! corresponds to the name of the derived class.
virtual string type() const {
string type() const override {
return "WallBase";
}
//! Retrieve wall name.
string name() const {
return m_name;
}
//! Set wall name.
void setName(const string& name) {
m_name = name;
}
//! Set the default name of a wall. Returns `false` if it was previously set.
bool setDefaultName(map<string, int>& counts);
//! Rate of volume change (m^3/s) for the adjacent reactors at current reactor
//! network time.
/*!
@ -76,6 +60,8 @@ public:
virtual void setArea(double a);
//! Install the wall between two reactors or reservoirs
//! @deprecated To be removed after %Cantera 3.2. Reactors should be provided to
//! constructor instead.
bool install(ReactorBase& leftReactor, ReactorBase& rightReactor);
//! Called just before the start of integration
@ -105,9 +91,6 @@ public:
}
protected:
string m_name; //!< Wall name.
bool m_defaultNameSet = false; //!< `true` if default name has been previously set.
ReactorBase* m_left = nullptr;
ReactorBase* m_right = nullptr;
@ -121,7 +104,7 @@ protected:
/*!
* Walls can move (changing the volume of the adjacent reactors) and allow heat
* transfer between reactors.
* @ingroup wallGroup
* @ingroup connectorGroup
*/
class Wall : public WallBase
{

View File

@ -6,49 +6,9 @@
#ifndef WALL_FACTORY_H
#define WALL_FACTORY_H
#include "cantera/base/FactoryBase.h"
#include "cantera/zeroD/Wall.h"
#pragma message("warning: WallFactory.h is deprecated and will be removed " \
"after Cantera 3.2. Use ConnectorFactory.h instead.")
namespace Cantera
{
//! Factory class to create WallBase objects
//!
//! This class is mainly used via the newWall() function, for example:
//!
//! ```cpp
//! shared_ptr<WallBase> piston = newWall("Wall");
//! ```
class WallFactory : public Factory<WallBase, const string&>
{
public:
static WallFactory* factory();
void deleteFactory() override;
private:
static WallFactory* s_factory;
static std::mutex wall_mutex;
WallFactory();
};
//! @defgroup wallGroup Walls
//! Zero-dimensional objects adjacent to reactors.
//! Wall objects should be instantiated via the newWall function, for
//! example:
//!
//! ```cpp
//! shared_ptr<WallBase> piston = newWall("Wall", "my_piston");
//! ```
//! @ingroup zerodGroup
//! @{
//! Create a WallBase object of the specified type
//! @since Starting in %Cantera 3.1, this method returns a `shared_ptr<WallBase>`
shared_ptr<WallBase> newWall(const string& model, const string& name="(none)");
//! @}
}
#include "ConnectorFactory.h"
#endif

View File

@ -15,7 +15,7 @@ namespace Cantera
/**
* A class for mass flow controllers. The mass flow rate is constant or
* specified as a function of time.
* @ingroup flowDeviceGroup
* @ingroup connectorGroup
*/
class MassFlowController : public FlowDevice
{
@ -60,7 +60,7 @@ public:
* A class for flow controllers where the flow rate is equal to the flow rate
* of a primary mass flow controller plus a correction proportional to the
* pressure difference between the inlet and outlet.
* @ingroup flowDeviceGroup
* @ingroup connectorGroup
*/
class PressureController : public FlowDevice
{
@ -118,7 +118,7 @@ protected:
* The default behavior is a linearly proportional to the pressure difference.
* Note that real valves do not have this behavior, so this class does not
* model real, physical valves.
* @ingroup flowDeviceGroup
* @ingroup connectorGroup
*/
class Valve : public FlowDevice
{

View File

@ -33,8 +33,7 @@
// factories
#include "cantera/zeroD/ReactorFactory.h"
#include "cantera/zeroD/FlowDeviceFactory.h"
#include "cantera/zeroD/WallFactory.h"
#include "cantera/zeroD/ConnectorFactory.h"
// func1
#include "cantera/numerics/Func1.h"

View File

@ -32,12 +32,11 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera":
# factories
cdef shared_ptr[CxxReactorBase] newReactor(string) except +translate_exception
cdef shared_ptr[CxxReactorBase] newReactor(string, shared_ptr[CxxSolution], string) except +translate_exception
cdef shared_ptr[CxxFlowDevice] newFlowDevice(string, string) except +translate_exception
cdef shared_ptr[CxxWallBase] newWall(string, string) except +translate_exception
cdef shared_ptr[CxxConnectorNode] newConnectorNode(string, shared_ptr[CxxReactorBase], shared_ptr[CxxReactorBase], string) except +translate_exception
# reactors
cdef cppclass CxxReactorBase "Cantera::ReactorBase":
CxxReactorBase()
CxxReactorBase() except +translate_exception
string type()
void setSolution(shared_ptr[CxxSolution]) except +translate_exception
void restoreState() except +translate_exception
@ -48,7 +47,7 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera":
void setInitialVolume(double)
cdef cppclass CxxReactor "Cantera::Reactor" (CxxReactorBase):
CxxReactor()
CxxReactor() except +translate_exception
void setChemistry(cbool)
cbool chemistryEnabled()
void setEnergy(int)
@ -59,7 +58,7 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera":
void getState(double*) except +translate_exception
CxxSparseMatrix jacobian() except +translate_exception
CxxSparseMatrix finiteDifferenceJacobian() except +translate_exception
void addSurface(CxxReactorSurface*)
void addSurface(CxxReactorSurface*) except +translate_exception
void setAdvanceLimit(string&, double) except +translate_exception
void addSensitivityReaction(size_t) except +translate_exception
void addSensitivitySpeciesEnthalpy(size_t) except +translate_exception
@ -82,40 +81,6 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera":
int inletSurfaceMaxErrorFailures()
void setInletSurfaceMaxErrorFailures(int) except +translate_exception
# walls
cdef cppclass CxxWallBase "Cantera::WallBase":
CxxWallBase()
string type()
string name()
void setName(string) except +translate_exception
cbool install(CxxReactorBase&, CxxReactorBase&)
double area()
void setArea(double)
void setKinetics(CxxKinetics*, CxxKinetics*)
void setCoverages(int, double*)
void setCoverages(int, Composition&) except +translate_exception
void syncCoverages(int)
double expansionRate() except +translate_exception
double vdot(double) except +translate_exception
double heatRate() except +translate_exception
double Q(double) except +translate_exception
void addSensitivityReaction(int, size_t) except +translate_exception
size_t nSensParams(int)
cdef cppclass CxxWall "Cantera::Wall" (CxxWallBase):
CxxWall()
void setExpansionRateCoeff(double)
double getExpansionRateCoeff()
void setHeatTransferCoeff(double)
double getHeatTransferCoeff()
void setEmissivity(double) except +translate_exception
double getEmissivity()
double velocity()
void setVelocity(CxxFunc1*)
double heatFlux()
void setHeatFlux(CxxFunc1*)
# reactor surface
cdef cppclass CxxReactorSurface "Cantera::ReactorSurface":
@ -132,25 +97,59 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera":
void addSensitivityReaction(size_t) except +translate_exception
size_t nSensParams()
# flow devices
# connectors
cdef cppclass CxxFlowDevice "Cantera::FlowDevice":
CxxFlowDevice()
cdef cppclass CxxConnectorNode "Cantera::ConnectorNode":
CxxConnectorNode() except +translate_exception
string type()
string name()
void setName(string) except +translate_exception
# walls
cdef cppclass CxxWallBase "Cantera::WallBase" (CxxConnectorNode ):
CxxWallBase() except +translate_exception
double area()
void setArea(double)
void setKinetics(CxxKinetics*, CxxKinetics*)
void setCoverages(int, double*)
void setCoverages(int, Composition&) except +translate_exception
void syncCoverages(int)
double expansionRate() except +translate_exception
double vdot(double) except +translate_exception
double heatRate() except +translate_exception
double Q(double) except +translate_exception
void addSensitivityReaction(int, size_t) except +translate_exception
size_t nSensParams(int)
cdef cppclass CxxWall "Cantera::Wall" (CxxWallBase):
CxxWall() except +translate_exception
void setExpansionRateCoeff(double)
double getExpansionRateCoeff()
void setHeatTransferCoeff(double)
double getHeatTransferCoeff()
void setEmissivity(double) except +translate_exception
double getEmissivity()
double velocity()
void setVelocity(CxxFunc1*)
double heatFlux()
void setHeatFlux(CxxFunc1*)
# flow devices
cdef cppclass CxxFlowDevice "Cantera::FlowDevice" (CxxConnectorNode):
CxxFlowDevice() except +translate_exception
double massFlowRate() except +translate_exception
double massFlowRate(double) except +translate_exception
cbool install(CxxReactorBase&, CxxReactorBase&) except +translate_exception
double evalPressureFunction() except +translate_exception
void setPressureFunction(CxxFunc1*) except +translate_exception
double evalTimeFunction() except +translate_exception
void setTimeFunction(CxxFunc1*) except +translate_exception
cdef cppclass CxxMassFlowController "Cantera::MassFlowController" (CxxFlowDevice):
CxxMassFlowController()
void setMassFlowRate(double)
void setMassFlowCoeff(double)
CxxMassFlowController() except +translate_exception
void setMassFlowRate(double) except +translate_exception
void setMassFlowCoeff(double) except +translate_exception
double getMassFlowCoeff()
cdef cppclass CxxValve "Cantera::Valve" (CxxFlowDevice):
@ -225,7 +224,7 @@ ctypedef CxxReactorAccessor* CxxReactorAccessorPtr
cdef class ReactorBase:
cdef shared_ptr[CxxReactorBase] _reactor
cdef CxxReactorBase* rbase
cdef object _thermo
cdef object _contents
cdef list _inlets
cdef list _outlets
cdef list _walls
@ -241,7 +240,6 @@ cdef class ReactorBase:
cdef class Reactor(ReactorBase):
cdef CxxReactor* reactor
cdef object _kinetics
cdef public str group_name
"""
Optional name of a grouping of reactors that will be drawn as a cluster in the
@ -289,14 +287,9 @@ cdef class ReactorSurface:
.. versionadded:: 3.1
"""
cdef class WallBase:
cdef shared_ptr[CxxWallBase] _wall
cdef CxxWallBase* wall
cdef object _velocity_func
cdef object _heat_flux_func
cdef ReactorBase _left_reactor
cdef ReactorBase _right_reactor
cdef str name
cdef class ConnectorNode:
cdef shared_ptr[CxxConnectorNode] _node
cdef CxxConnectorNode* node
cdef public dict edge_attr
"""
A dictionary containing draw attributes for the representation of the `WallBase` as
@ -306,25 +299,24 @@ cdef class WallBase:
.. versionadded:: 3.1
"""
cdef class WallBase(ConnectorNode):
# cdef shared_ptr[CxxWallBase] _wall
cdef CxxWallBase* wall
cdef object _velocity_func
cdef object _heat_flux_func
cdef ReactorBase _left_reactor
cdef ReactorBase _right_reactor
cdef class Wall(WallBase):
pass
cdef class FlowDevice:
cdef shared_ptr[CxxFlowDevice] _dev
cdef class FlowDevice(ConnectorNode):
# cdef shared_ptr[CxxFlowDevice] _dev
cdef CxxFlowDevice* dev
cdef Func1 _rate_func
cdef Func1 _time_func
cdef str name
cdef ReactorBase _upstream
cdef ReactorBase _downstream
cdef public dict edge_attr
"""
A dictionary containing draw attributes for the representation of the `FlowDevice`
as a graphviz edge.See https://graphviz.org/docs/edges/ for a list of all usable
attributes.
.. versionadded:: 3.1
"""
cdef class MassFlowController(FlowDevice):
pass

View File

@ -28,7 +28,7 @@ cdef class ReactorBase:
self._walls = []
self._surfaces = []
if isinstance(contents, _SolutionBase):
self.insert(contents) # leave insert for the time being
self._contents = contents
if volume is not None:
self.volume = volume
@ -39,9 +39,14 @@ cdef class ReactorBase:
"""
Set ``solution`` to be the object used to compute thermodynamic
properties and kinetic rates for this reactor.
.. deprecated:: 3.2
After Cantera 3.2, a change of reactor contents after instantiation
will be disabled and this method will be removed.
"""
self._thermo = solution
self.rbase.setSolution(solution._base)
self.rbase.setSolution(solution._base) # raises warning in C++ core
self._contents = solution
property type:
"""The type of the reactor."""
@ -64,10 +69,12 @@ cdef class ReactorBase:
self.rbase.syncState()
property thermo:
"""The `ThermoPhase` object representing the reactor's contents."""
"""
The `ThermoPhase` object representing the reactor's contents.
"""
def __get__(self):
self.rbase.restoreState()
return self._thermo
return self._contents
property volume:
"""The volume [m^3] of the reactor."""
@ -239,14 +246,6 @@ cdef class Reactor(ReactorBase):
self.group_name = group_name
def insert(self, _SolutionBase solution):
"""
Set ``solution`` to be the object used to compute thermodynamic
properties and kinetic rates for this reactor.
"""
ReactorBase.insert(self, solution)
self._kinetics = solution
property kinetics:
"""
The `Kinetics` object used for calculating kinetic rates in
@ -254,7 +253,7 @@ cdef class Reactor(ReactorBase):
"""
def __get__(self):
self.rbase.restoreState()
return self._kinetics
return self._contents
property chemistry_enabled:
"""
@ -939,14 +938,52 @@ cdef class ReactorSurface:
self.surface.addSensitivityReaction(m)
cdef class WallBase:
cdef class ConnectorNode:
"""
Common base class for walls and flow devices.
"""
node_type = "none"
def __cinit__(self, ReactorBase left=None, ReactorBase right=None, *,
ReactorBase upstream=None, ReactorBase downstream=None,
name="(none)", **kwargs):
# ensure that both naming conventions (Wall and FlowDevice) are covered
cdef ReactorBase r0 = left or upstream
cdef ReactorBase r1 = right or downstream
if isinstance(r0, ReactorBase) and isinstance(r1, ReactorBase):
self._node = newConnectorNode(stringify(self.node_type),
r0._reactor, r1._reactor, stringify(name))
self.node = self._node.get()
return
raise TypeError(f"Invalid reactor types: {r0} and {r1}.")
@property
def type(self):
"""The type of the connector."""
return pystr(self.node.type())
@property
def name(self):
"""The name of the connector."""
return pystr(self.node.name())
@name.setter
def name(self, name):
self.node.setName(stringify(name))
def __reduce__(self):
raise NotImplementedError('Reactor object is not picklable')
def __copy__(self):
raise NotImplementedError('Reactor object is not copyable')
cdef class WallBase(ConnectorNode):
"""
Common base class for walls.
"""
wall_type = "none"
def __cinit__(self, *args, name="(none)", **kwargs):
self._wall = newWall(stringify(self.wall_type), stringify(name))
self.wall = self._wall.get()
def __cinit__(self, *args, **kwargs):
self.wall = <CxxWall*>(self.node)
def __init__(self, left, right, *, name="(none)", A=None, K=None, U=None,
Q=None, velocity=None, edge_attr=None):
@ -985,8 +1022,6 @@ cdef class WallBase:
self._velocity_func = None
self._heat_flux_func = None
self._install(left, right)
if A is not None:
self.area = A
if K is not None:
@ -999,30 +1034,12 @@ cdef class WallBase:
self.velocity = velocity
self.edge_attr = edge_attr or {}
def _install(self, ReactorBase left, ReactorBase right):
"""
Install this Wall between two `Reactor` objects or between a
`Reactor` and a `Reservoir`.
"""
left._add_wall(self)
right._add_wall(self)
self.wall.install(deref(left.rbase), deref(right.rbase))
# Keep references to prevent premature garbage collection
self._left_reactor = left
self._right_reactor = right
property type:
"""The type of the wall."""
def __get__(self):
return pystr(self.wall.type())
property name:
"""The name of the wall."""
def __get__(self):
return pystr(self.wall.name())
def __set__(self, name):
self.wall.setName(stringify(name))
property area:
""" The wall area [m^2]. """
def __get__(self):
@ -1138,7 +1155,7 @@ cdef class Wall(WallBase):
:math:`q_0(t)` is a specified function of time. The heat flux is positive
when heat flows from the reactor on the left to the reactor on the right.
"""
wall_type = "Wall"
node_type = "Wall"
property expansion_rate_coeff:
"""
@ -1207,7 +1224,7 @@ cdef class Wall(WallBase):
(<CxxWall*>self.wall).setHeatFlux(f.func)
cdef class FlowDevice:
cdef class FlowDevice(ConnectorNode):
"""
Base class for devices that allow flow between reactors.
@ -1218,37 +1235,15 @@ cdef class FlowDevice:
across a FlowDevice, and the pressure difference equals the difference in
pressure between the upstream and downstream reactors.
"""
flowdevice_type = "none"
def __cinit__(self, *args, name="(none)", **kwargs):
self._dev = newFlowDevice(stringify(self.flowdevice_type), stringify(name))
self.dev = self._dev.get()
def __cinit__(self, *args, **kwargs):
self.dev = <CxxFlowDevice*>(self.node)
def __init__(self, upstream, downstream, *, name="(none)", edge_attr=None):
assert self.dev != NULL
self._rate_func = None
self.edge_attr = edge_attr or {}
self._install(upstream, downstream)
property type:
"""The type of the flow device."""
def __get__(self):
return pystr(self.dev.type())
property name:
"""The name of the flow device."""
def __get__(self):
return pystr(self.dev.name())
def __set__(self, name):
self.dev.setName(stringify(name))
def _install(self, ReactorBase upstream, ReactorBase downstream):
"""
Install the device between the ``upstream`` (source) and ``downstream``
(destination) reactors or reservoirs.
"""
upstream._add_outlet(self)
downstream._add_inlet(self)
self.dev.install(deref(upstream.rbase), deref(downstream.rbase))
# Keep references to prevent premature garbage collection
self._upstream = upstream
self._downstream = downstream
@ -1387,10 +1382,10 @@ cdef class MassFlowController(FlowDevice):
that this capability should be used with caution, since no account is
taken of the work required to do this.
"""
flowdevice_type = "MassFlowController"
node_type = "MassFlowController"
def __init__(self, upstream, downstream, *, name="(none)", mdot=1., **kwargs):
super().__init__(upstream, downstream, name=name, **kwargs)
def __init__(self, upstream, downstream, *, name="(none)", mdot=1., edge_attr=None):
super().__init__(upstream, downstream, name=name, edge_attr=edge_attr)
self.mass_flow_rate = mdot
property mass_flow_coeff:
@ -1461,10 +1456,10 @@ cdef class Valve(FlowDevice):
value, very small pressure differences will result in flow between the
reactors that counteracts the pressure difference.
"""
flowdevice_type = "Valve"
node_type = "Valve"
def __init__(self, upstream, downstream, *, name="(none)", K=1., **kwargs):
super().__init__(upstream, downstream, name=name, **kwargs)
def __init__(self, upstream, downstream, *, name="(none)", K=1., edge_attr=None):
super().__init__(upstream, downstream, name=name, edge_attr=edge_attr)
if isinstance(K, _numbers.Real):
self.valve_coeff = K
else:
@ -1504,10 +1499,11 @@ cdef class PressureController(FlowDevice):
where :math:`f` is the arbitrary function of a single argument.
"""
flowdevice_type = "PressureController"
node_type = "PressureController"
def __init__(self, upstream, downstream, *, name="(none)", primary=None, K=1.):
super().__init__(upstream, downstream, name=name)
def __init__(self, upstream, downstream, *,
name="(none)", primary=None, K=1., edge_attr=None):
super().__init__(upstream, downstream, name=name, edge_attr=edge_attr)
if primary is not None:
self.primary = primary
if isinstance(K, _numbers.Real):

View File

@ -1,4 +1,4 @@
classdef Interface < handle & ThermoPhase & Kinetics
classdef Interface < Solution
% Interface Class ::
%
% >> s = Interface(src, name, p1, p2)
@ -13,11 +13,6 @@ classdef Interface < handle & ThermoPhase & Kinetics
% :return:
% Instance of class :mat:class:`Interface`.
properties (SetAccess = immutable)
solnID % ID of the interface.
interfaceName % Name of the interface.
end
properties (SetAccess = public)
% Surface coverages of the species on an interface.
@ -50,11 +45,8 @@ classdef Interface < handle & ThermoPhase & Kinetics
ID = ctFunc('soln_newInterface', src, name, na, adj);
% Inherit methods and properties from ThermoPhase and Kinetics
s@ThermoPhase(ID);
s@Kinetics(ID);
s.solnID = ID;
s.interfaceName = name;
% Inherit methods and properties from Solution
s@Solution(ID);
s.nAdjacent = ctFunc('soln_nAdjacent', ID);
s.adjacentNames = {};
for i = 1:s.nAdjacent
@ -62,13 +54,6 @@ classdef Interface < handle & ThermoPhase & Kinetics
end
end
%% Interface Class Destructor
function delete(s)
% Delete :mat:class:`Interface` object.
ctFunc('soln_del', s.solnID);
end
%% Interface Get Methods
function adj = adjacent(s, name)

View File

@ -0,0 +1,84 @@
classdef Connector < handle
% Connector Class ::
%
% >> c = Connector(typ, r1, r2, name)
%
% Base class for walls and flow devices.
%
% See also: :mat:class:`FlowDevice`, :mat:class:`Wall`
%
% :param typ:
% Type of connector.
% :param r1:
% Reactor one.
% :param r2:
% Reactor two.
% :param name:
% Connector name (optional; default is ``(none)``).
% :return:
% Instance of class :mat:class:`Connector`.
properties (SetAccess = immutable)
id % ID of Connector object.
end
properties (SetAccess = public)
type % Name of connector.
name % Name of connector.
end
methods
%% Connector Class Constructor
function c = Connector(typ, r1, r2, name)
% Create a :mat:class:`Connector` object.
ctIsLoaded;
if nargin < 3
error('please specify type and reactors');
end
if nargin < 4
name = '(none)';
end
if ~isa(r1, 'Reactor') || ~isa(r1, 'Reactor')
error(['Connectors can only be installed between', ...
'reactors or reservoirs']);
end
c.id = ctFunc('connector_new', typ, r1.id, r2.id, name);
end
%% Connector Class Destructor
function delete(c)
% Delete the :mat:class:`Connector` object.
ctFunc('connector_del', c.id);
end
%% Connector Get Methods
function typ = get.type(c)
typ = ctString('connector_type', c.id);
end
function name = get.name(c)
name = ctString('connector_name', c.id);
end
%% Connector Set Methods
function set.name(c, name)
ctFunc('connector_setName', c.id, name);
end
end
end

View File

@ -1,4 +1,4 @@
classdef FlowDevice < handle
classdef FlowDevice < Connector
% FlowDevice Class ::
%
% >> x = FlowDevice(typ, name)
@ -25,21 +25,16 @@ classdef FlowDevice < handle
properties (SetAccess = immutable)
type % Type of flow device.
id % ID of FlowDevice object.
end
properties (SetAccess = public)
name % Name of flow device.
% Upstream object of type :mat:class:`Reactor` or :mat:class:`Reservoir`.
upstream
% Downstream object of type :mat:class:`Reactor` or :mat:class:`Reservoir`.
downstream
end
properties (SetAccess = public)
% The mass flow rate through the :mat:class:`FlowDevice` at the current time.
%
% The setter method can either take a double value or a function represented by
@ -60,79 +55,28 @@ classdef FlowDevice < handle
methods
%% FlowDevice Class Constructor
function x = FlowDevice(typ, name)
function x = FlowDevice(typ, upstream, downstream, name)
% Create a :mat:class:`FlowDevice` object.
ctIsLoaded;
if nargin == 0
error('please specify the type of flow device to be created');
end
if nargin < 2
if nargin < 4
name = '(none)';
end
x.type = typ;
x.id = ctFunc('flowdev_new', typ, name);
x.upstream = -1;
x.downstream = -1;
end
%% FlowDevice Class Destructor
function delete(f)
% Delete the :mat:class:`FlowDevice` object.
ctFunc('flowdev_del', f.id);
end
%% Utility Methods
function install(f, upstream, downstream)
% Install a flow device between reactors or reservoirs. ::
%
% >> f.install(upstream, downstream)
%
% :param f:
% Instance of class :mat:class:`FlowDevice` to install.
% :param upstream:
% Upstream :mat:class:`Reactor` or :mat:class:`Reservoir`.
% :param downstream:
% Downstream :mat:class:`Reactor` or :mat:class:`Reservoir`.
% :return:
% Instance of class :mat:class:`FlowDevice`.
if nargin == 3
if ~isa(upstream, 'Reactor') || ~isa(downstream, 'Reactor')
error(['Flow devices can only be installed between', ...
'reactors or reservoirs']);
end
i = upstream.id;
j = downstream.id;
ctFunc('flowdev_install', f.id, i, j);
else error('install requires 3 arguments');
end
x@Connector(typ, upstream, downstream, name)
x.upstream = upstream;
x.downstream = downstream;
end
%% FlowDevice Get Methods
function name = get.name(f)
name = ctString('flowdev_name', f.id);
end
function mdot = get.massFlowRate(f)
mdot = ctFunc('flowdev_massFlowRate2', f.id);
end
%% FlowDevice Set Methods
function set.name(f, name)
ctFunc('flowdev_setName', f.id, name);
end
function set.massFlowRate(f, mdot)
if strcmp(f.type, 'MassFlowController')

View File

@ -31,8 +31,7 @@ classdef MassFlowController < FlowDevice
name = '(none)';
end
m@FlowDevice('MassFlowController', name);
m.install(upstream, downstream)
m@FlowDevice('MassFlowController', upstream, downstream, name);
end
end

View File

@ -2,13 +2,14 @@ classdef Reactor < handle
properties (SetAccess = immutable)
type % Type of Reactor.
id % ID of Reactor.
end
properties (SetAccess = public)
type % Reactor type.
name % Name of reactor.
contents
@ -131,8 +132,8 @@ classdef Reactor < handle
error('Reactor contents must be an object of type "Solution"');
end
r.type = char(typ);
r.id = ctFunc('reactor_new', typ, content.solnID, name);
r.contents = content;
end
%% Reactor Class Destructor
@ -160,6 +161,10 @@ classdef Reactor < handle
%% Reactor Get Methods
function typ = get.type(r)
typ = ctString('reactor_type', r.id);
end
function name = get.name(r)
name = ctString('reactor_name', r.id);
end

View File

@ -40,8 +40,7 @@ classdef Valve < FlowDevice
name = '(none)';
end
v@FlowDevice('Valve', name);
v.install(upstream, downstream)
v@FlowDevice('Valve', upstream, downstream, name);
end
end

View File

@ -1,4 +1,4 @@
classdef Wall < handle
classdef Wall < Connector
% Wall Class ::
%
% >> x = Wall(l, r, name)
@ -47,15 +47,6 @@ classdef Wall < handle
properties (SetAccess = immutable)
id
type
end
properties (SetAccess = protected)
name % Name of wall.
left % Reactor on the left.
right % Reactor on the right.
@ -93,49 +84,26 @@ classdef Wall < handle
function w = Wall(l, r, name)
% Create a :mat:class:`Wall` object.
ctIsLoaded;
% At the moment, only one wall type is implemented
typ = 'Wall';
if nargin < 3
name = '(none)';
end
w.type = char(typ);
w.id = ctFunc('wall_new', w.type, name);
% Install the wall between left and right reactors
w@Connector('Wall', l, r, name)
w.left = l;
w.right = r;
ctFunc('wall_install', w.id, l.id, r.id);
% Set default values.
w.area = 1.0;
w.expansionRateCoeff = 0.0;
w.heatTransferCoeff = 0.0;
% Check whether the wall is ready.
ok = ctFunc('wall_ready', w.id);
if ~ok
error('The wall object is not ready.');
end
end
%% Wall Class Destructor
function delete(w)
% Clear the :mat:class:`Wall` object.
ctFunc('wall_del', w.id);
end
%% ReactorNet get methods
function name = get.name(w)
name = ctString('wall_name', w.id);
end
function a = get.area(w)
a = ctFunc('wall_area', w.id);
end
@ -150,10 +118,6 @@ classdef Wall < handle
%% ReactorNet set methods
function set.name(w, name)
ctFunc('wall_setName', w.id, name);
end
function set.area(w, a)
ctFunc('wall_setArea', w.id, a);
end

View File

@ -24,6 +24,7 @@ class_crosswalk:
trans: Transport
wall: Wall
func: Func1
connector: Connector
# Provides information on instance methods that return instances
# of other C# classes.
@ -42,6 +43,8 @@ class_accessors:
# Derived: Base
derived_handles:
SurfaceHandle: ThermoPhaseHandle
WallHandle: ConnectorHandle
FlowDeviceHandle: ConnectorHandle
# Provides info for scaffolding higher-level idiomatic C# classes
# At this stage, we can scaffold simple properties that follow the

View File

@ -73,8 +73,7 @@ function periodic_cstr
sccm = 1.25;
vdot = sccm * 1.0e-6/60.0 * ((OneAtm / gas.P) * (gas.T / 273.15)); % m^3/s
mdot = gas.D * vdot; % kg/s
mfc = MassFlowController;
mfc.install(upstream, cstr);
mfc = MassFlowController(upstream, cstr);
mfc.massFlowRate = mdot;
% now create a downstream reservoir to exhaust into.
@ -83,8 +82,7 @@ function periodic_cstr
% connect the reactor to the downstream reservoir with a valve, and
% set the coefficient sufficiently large to keep the reactor pressure
% close to the downstream pressure of 60 Torr.
v = Valve;
v.install(cstr, downstream);
v = Valve(cstr, downstream);
v.valveCoeff = 1.0e-9;
% create the network

View File

@ -131,7 +131,7 @@ class SolidProperties:
def effectiveConductivitySiC(Ts): # for silicon carbide
return (1 - 0.84) * 1857.0 * Ts**(-0.5332)
# YZA: Thermal conductivity of zirconiaalumina composites, N.P. Bansal, D. Zhu,
# YZA: Thermal conductivity of zirconiaalumina composites, N.P. Bansal, D. Zhu,
# Ceramics International, 31(7), pp 911-916 (2015)
def effectiveConductivityYZA(Ts): # for yittria-stabilized zirconia alumina
return 0.3
@ -409,3 +409,4 @@ plt.legend()
plt.xlabel("x (m)")
plt.ylabel("T (K)")
plt.savefig("T.png")
plt.show()

View File

@ -162,6 +162,7 @@ plt.figure()
plt.semilogx(time_history.t, time_history("CO").X, "-o")
plt.xlabel("Time (s)")
plt.ylabel("Mole Fraction : $X_{CO}$")
plt.show()
# %%
# Illustration : Modeling experimental data
@ -288,6 +289,7 @@ plt.ylabel(r"Mole Fractions")
plt.xlim([650, 1100])
plt.legend(loc=1)
plt.show()
# %%
# References

View File

@ -19,8 +19,7 @@ using namespace Cantera;
typedef Cabinet<ReactorBase> ReactorCabinet;
typedef Cabinet<ReactorNet> NetworkCabinet;
typedef Cabinet<FlowDevice> FlowDeviceCabinet;
typedef Cabinet<WallBase> WallCabinet;
typedef Cabinet<ConnectorNode> ConnectorCabinet;
typedef Cabinet<Func1> FuncCabinet;
typedef Cabinet<ThermoPhase> ThermoCabinet;
typedef Cabinet<Kinetics> KineticsCabinet;
@ -29,8 +28,7 @@ typedef Cabinet<ReactorSurface> ReactorSurfaceCabinet;
template<> ReactorCabinet* ReactorCabinet::s_storage = 0;
template<> NetworkCabinet* NetworkCabinet::s_storage = 0;
template<> FlowDeviceCabinet* FlowDeviceCabinet::s_storage = 0;
template<> WallCabinet* WallCabinet::s_storage = 0;
template<> ConnectorCabinet* ConnectorCabinet::s_storage = 0;
template<> ReactorSurfaceCabinet* ReactorSurfaceCabinet::s_storage = 0;
template<> FuncCabinet* FuncCabinet::s_storage; // defined in ctfunc.cpp
template<> ThermoCabinet* ThermoCabinet::s_storage; // defined in ct.cpp
@ -44,7 +42,8 @@ extern "C" {
int reactor_new(const char* type, int n, const char* name)
{
try {
return ReactorCabinet::add(newReactor(type, SolutionCabinet::at(n), name));
return ReactorCabinet::add(
newReactor(type, SolutionCabinet::at(n), name));
} catch (...) {
return handleAllExceptions(-1, ERR);
}
@ -60,6 +59,16 @@ extern "C" {
}
}
int reactor_type(int i, int len, char* nbuf)
{
try {
return static_cast<int>(
copyString(ReactorCabinet::at(i)->type(), nbuf, len));
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
int reactor_name(int i, int len, char* nbuf)
{
try {
@ -90,16 +99,6 @@ extern "C" {
}
}
int reactor_setSolution(int i, int n)
{
try {
ReactorCabinet::as<Reactor>(i)->setSolution(SolutionCabinet::at(n));
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
double reactor_mass(int i)
{
try {
@ -348,67 +347,67 @@ extern "C" {
}
}
// flow devices
// connectors
int flowdev_new(const char* type, const char* name)
int connector_new(const char* type, int n, int m, const char* name)
{
try {
return FlowDeviceCabinet::add(newFlowDevice(type, name));
return ConnectorCabinet::add(
newConnectorNode(type,
ReactorCabinet::at(n), ReactorCabinet::at(m), name));
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
int flowdev_del(int i)
int connector_del(int i)
{
try {
FlowDeviceCabinet::del(i);
ConnectorCabinet::del(i);
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
int flowdev_name(int i, int len, char* nbuf)
int connector_type(int i, int len, char* nbuf)
{
try {
return static_cast<int>(
copyString(FlowDeviceCabinet::at(i)->name(), nbuf, len));
copyString(ConnectorCabinet::at(i)->type(), nbuf, len));
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
int flowdev_setName(int i, const char* name)
int connector_name(int i, int len, char* nbuf)
{
try {
FlowDeviceCabinet::at(i)->setName(name);
return static_cast<int>(
copyString(ConnectorCabinet::at(i)->name(), nbuf, len));
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
int connector_setName(int i, const char* name)
{
try {
ConnectorCabinet::at(i)->setName(name);
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
int flowdev_install(int i, int n, int m)
{
try {
bool ok = FlowDeviceCabinet::at(i)->install(*ReactorCabinet::at(n),
*ReactorCabinet::at(m));
if (!ok) {
throw CanteraError("flowdev_install",
"Could not install flow device.");
}
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
// flow devices
int flowdev_setPrimary(int i, int n)
{
try {
FlowDeviceCabinet::as<PressureController>(i)->setPrimary(
FlowDeviceCabinet::at(n).get());
ConnectorCabinet::as<PressureController>(i)->setPrimary(
ConnectorCabinet::as<FlowDevice>(n).get());
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
@ -418,7 +417,7 @@ extern "C" {
double flowdev_massFlowRate(int i)
{
try {
return FlowDeviceCabinet::at(i)->massFlowRate();
return ConnectorCabinet::as<FlowDevice>(i)->massFlowRate();
} catch (...) {
return handleAllExceptions(DERR, DERR);
}
@ -427,7 +426,7 @@ extern "C" {
int flowdev_setMassFlowCoeff(int i, double v)
{
try {
FlowDeviceCabinet::as<MassFlowController>(i)->setMassFlowCoeff(v);
ConnectorCabinet::as<MassFlowController>(i)->setMassFlowCoeff(v);
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
@ -437,7 +436,7 @@ extern "C" {
int flowdev_setValveCoeff(int i, double v)
{
try {
FlowDeviceCabinet::as<Valve>(i)->setValveCoeff(v);
ConnectorCabinet::as<Valve>(i)->setValveCoeff(v);
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
@ -447,7 +446,7 @@ extern "C" {
int flowdev_setPressureCoeff(int i, double v)
{
try {
FlowDeviceCabinet::as<PressureController>(i)->setPressureCoeff(v);
ConnectorCabinet::as<PressureController>(i)->setPressureCoeff(v);
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
@ -457,7 +456,8 @@ extern "C" {
int flowdev_setPressureFunction(int i, int n)
{
try {
FlowDeviceCabinet::at(i)->setPressureFunction(FuncCabinet::at(n).get());
ConnectorCabinet::as<FlowDevice>(i)->setPressureFunction(
FuncCabinet::at(n).get());
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
@ -467,7 +467,8 @@ extern "C" {
int flowdev_setTimeFunction(int i, int n)
{
try {
FlowDeviceCabinet::at(i)->setTimeFunction(FuncCabinet::at(n).get());
ConnectorCabinet::as<FlowDevice>(i)->setTimeFunction(
FuncCabinet::at(n).get());
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
@ -476,60 +477,10 @@ extern "C" {
///////////// Walls ///////////////////////
int wall_new(const char* type, const char* name)
{
try {
return WallCabinet::add(newWall(type, name));
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
int wall_del(int i)
{
try {
WallCabinet::del(i);
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
int wall_name(int i, int len, char* nbuf)
{
try {
return static_cast<int>(
copyString(WallCabinet::at(i)->name(), nbuf, len));
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
int wall_setName(int i, const char* name)
{
try {
WallCabinet::at(i)->setName(name);
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
int wall_install(int i, int n, int m)
{
try {
WallCabinet::at(i)->install(*ReactorCabinet::at(n),
*ReactorCabinet::at(m));
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
double wall_expansionRate(int i)
{
try {
return WallCabinet::at(i)->expansionRate();
return ConnectorCabinet::as<Wall>(i)->expansionRate();
} catch (...) {
return handleAllExceptions(DERR, DERR);
}
@ -538,7 +489,7 @@ extern "C" {
double wall_heatRate(int i)
{
try {
return WallCabinet::at(i)->heatRate();
return ConnectorCabinet::as<Wall>(i)->heatRate();
} catch (...) {
return handleAllExceptions(DERR, DERR);
}
@ -547,7 +498,7 @@ extern "C" {
double wall_area(int i)
{
try {
return WallCabinet::at(i)->area();
return ConnectorCabinet::as<Wall>(i)->area();
} catch (...) {
return handleAllExceptions(DERR, DERR);
}
@ -556,7 +507,7 @@ extern "C" {
int wall_setArea(int i, double v)
{
try {
WallCabinet::at(i)->setArea(v);
ConnectorCabinet::as<Wall>(i)->setArea(v);
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
@ -566,7 +517,7 @@ extern "C" {
int wall_setThermalResistance(int i, double rth)
{
try {
WallCabinet::as<Wall>(i)->setThermalResistance(rth);
ConnectorCabinet::as<Wall>(i)->setThermalResistance(rth);
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
@ -576,7 +527,7 @@ extern "C" {
int wall_setHeatTransferCoeff(int i, double u)
{
try {
WallCabinet::as<Wall>(i)->setHeatTransferCoeff(u);
ConnectorCabinet::as<Wall>(i)->setHeatTransferCoeff(u);
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
@ -586,7 +537,7 @@ extern "C" {
int wall_setHeatFlux(int i, int n)
{
try {
WallCabinet::as<Wall>(i)->setHeatFlux(FuncCabinet::at(n).get());
ConnectorCabinet::as<Wall>(i)->setHeatFlux(FuncCabinet::at(n).get());
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
@ -596,7 +547,7 @@ extern "C" {
int wall_setExpansionRateCoeff(int i, double k)
{
try {
WallCabinet::as<Wall>(i)->setExpansionRateCoeff(k);
ConnectorCabinet::as<Wall>(i)->setExpansionRateCoeff(k);
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
@ -606,7 +557,7 @@ extern "C" {
int wall_setVelocity(int i, int n)
{
try {
WallCabinet::as<Wall>(i)->setVelocity(FuncCabinet::at(n).get());
ConnectorCabinet::as<Wall>(i)->setVelocity(FuncCabinet::at(n).get());
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
@ -616,22 +567,13 @@ extern "C" {
int wall_setEmissivity(int i, double epsilon)
{
try {
WallCabinet::as<Wall>(i)->setEmissivity(epsilon);
ConnectorCabinet::as<Wall>(i)->setEmissivity(epsilon);
return 0;
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
int wall_ready(int i)
{
try {
return int(WallCabinet::at(i)->ready());
} catch (...) {
return handleAllExceptions(-1, ERR);
}
}
// ReactorSurface
int reactorsurface_new(const char* name)
@ -727,8 +669,7 @@ extern "C" {
try {
ReactorCabinet::clear();
NetworkCabinet::clear();
FlowDeviceCabinet::clear();
WallCabinet::clear();
ConnectorCabinet::clear();
ReactorSurfaceCabinet::clear();
return 0;
} catch (...) {

View File

@ -0,0 +1,95 @@
//! @file ConnectorFactory.cpp
// 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 "cantera/zeroD/ConnectorFactory.h"
#include "cantera/zeroD/FlowDevice.h"
#include "cantera/zeroD/flowControllers.h"
#include "cantera/zeroD/Wall.h"
namespace Cantera
{
ConnectorFactory* ConnectorFactory::s_factory = 0;
std::mutex ConnectorFactory::connector_mutex;
ConnectorFactory::ConnectorFactory()
{
reg("MassFlowController",
[](shared_ptr<ReactorBase> r0, shared_ptr<ReactorBase> r1, const string& name)
{ return new MassFlowController(r0, r1, name); });
reg("PressureController",
[](shared_ptr<ReactorBase> r0, shared_ptr<ReactorBase> r1, const string& name)
{ return new PressureController(r0, r1, name); });
reg("Valve",
[](shared_ptr<ReactorBase> r0, shared_ptr<ReactorBase> r1, const string& name)
{ return new Valve(r0, r1, name); });
reg("Wall",
[](shared_ptr<ReactorBase> r0, shared_ptr<ReactorBase> r1, const string& name)
{ return new Wall(r0, r1, name); });
}
ConnectorFactory* ConnectorFactory::factory() {
std::unique_lock<std::mutex> lock(connector_mutex);
if (!s_factory) {
s_factory = new ConnectorFactory;
}
return s_factory;
}
void ConnectorFactory::deleteFactory() {
std::unique_lock<std::mutex> lock(connector_mutex);
delete s_factory;
s_factory = 0;
}
shared_ptr<ConnectorNode> newConnectorNode(
const string& model,
shared_ptr<ReactorBase> r0, shared_ptr<ReactorBase> r1, const string& name)
{
return shared_ptr<ConnectorNode>(
ConnectorFactory::factory()->create(model, r0, r1, name));
}
shared_ptr<FlowDevice> newFlowDevice(
const string& model,
shared_ptr<ReactorBase> r0, shared_ptr<ReactorBase> r1, const string& name)
{
auto dev = std::dynamic_pointer_cast<FlowDevice>(
newConnectorNode(model, r0, r1, name));
if (!dev) {
throw CanteraError("newFlowDevice",
"Detected incompatible ConnectorNode type '{}'", model);
}
return dev;
}
shared_ptr<FlowDevice> newFlowDevice(const string& model, const string& name)
{
warn_deprecated("newFlowDevice",
"After Cantera 3.1, Reactors must be provided as parameters.");
return newFlowDevice(model, nullptr, nullptr, name);
}
shared_ptr<WallBase> newWall(
const string& model,
shared_ptr<ReactorBase> r0, shared_ptr<ReactorBase> r1, const string& name)
{
auto wall = std::dynamic_pointer_cast<WallBase>(
newConnectorNode(model, r0, r1, name));
if (!wall) {
throw CanteraError("newWall",
"Detected incompatible ConnectorNode type '{}'", model);
}
return wall;
}
shared_ptr<WallBase> newWall(const string& model, const string& name)
{
warn_deprecated("newWall",
"After Cantera 3.1, Reactors must be provided as parameters.");
return newWall(model, nullptr, nullptr, name);
}
}

View File

@ -0,0 +1,25 @@
//! @file ConnectorNode.cpp
// 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 "cantera/zeroD/ConnectorNode.h"
#include "cantera/zeroD/ReactorBase.h"
namespace Cantera
{
void ConnectorNode::setDefaultName(map<string, int>& counts)
{
if (m_defaultNameSet) {
return;
}
m_defaultNameSet = true;
string typ(type());
if (m_name == "(none)" || m_name == "") {
m_name = fmt::format("{}_{}", type(), counts[type()]);
}
counts[type()]++;
}
}

View File

@ -11,22 +11,45 @@
namespace Cantera
{
bool FlowDevice::setDefaultName(map<string, int>& counts)
FlowDevice::FlowDevice(shared_ptr<ReactorBase> r0, shared_ptr<ReactorBase> r1,
const string& name) : ConnectorNode(r0, r1, name)
{
if (m_defaultNameSet) {
return false;
if (!m_nodes.first || !m_nodes.second) {
warn_deprecated("FlowDevice::FlowDevice",
"After Cantera 3.1, Reactors must be provided to a FlowDevice "
"constructor.");
return;
}
m_defaultNameSet = true;
string typ(type());
if (m_name == "(none)" || m_name == "") {
m_name = fmt::format("{}_{}", type(), counts[type()]);
m_in = r0.get();
m_out = r1.get();
m_in->addOutlet(*this);
m_out->addInlet(*this);
// construct adapters between inlet and outlet species
const ThermoPhase& mixin = m_in->contents();
const ThermoPhase& mixout = m_out->contents();
m_nspin = mixin.nSpecies();
m_nspout = mixout.nSpecies();
string nm;
size_t ki, ko;
for (ki = 0; ki < m_nspin; ki++) {
nm = mixin.speciesName(ki);
ko = mixout.speciesIndex(nm);
m_in2out.push_back(ko);
}
for (ko = 0; ko < m_nspout; ko++) {
nm = mixout.speciesName(ko);
ki = mixin.speciesIndex(nm);
m_out2in.push_back(ki);
}
counts[type()]++;
return true;
}
bool FlowDevice::install(ReactorBase& in, ReactorBase& out)
{
warn_deprecated("FlowDevice::install",
"To be removed after Cantera 3.1. Reactors should be provided to constructor "
"instead.");
if (m_in || m_out) {
throw CanteraError("FlowDevice::install", "Already installed");
}

View File

@ -1,47 +0,0 @@
//! @file FlowDeviceFactory.cpp
// 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 "cantera/zeroD/FlowDeviceFactory.h"
#include "cantera/zeroD/flowControllers.h"
namespace Cantera
{
FlowDeviceFactory* FlowDeviceFactory::s_factory = 0;
std::mutex FlowDeviceFactory::flowDevice_mutex;
FlowDeviceFactory::FlowDeviceFactory()
{
reg("MassFlowController", [](const string& name) {
return new MassFlowController(name);
});
reg("PressureController", [](const string& name) {
return new PressureController(name);
});
reg("Valve", [](const string& name) {
return new Valve(name);
});
}
FlowDeviceFactory* FlowDeviceFactory::factory() {
std::unique_lock<std::mutex> lock(flowDevice_mutex);
if (!s_factory) {
s_factory = new FlowDeviceFactory;
}
return s_factory;
}
void FlowDeviceFactory::deleteFactory() {
std::unique_lock<std::mutex> lock(flowDevice_mutex);
delete s_factory;
s_factory = 0;
}
shared_ptr<FlowDevice> newFlowDevice(const string& model, const string& name)
{
return shared_ptr<FlowDevice>(FlowDeviceFactory::factory()->create(model, name));
}
}

View File

@ -23,14 +23,8 @@ namespace Cantera
{
Reactor::Reactor(shared_ptr<Solution> sol, const string& name)
: ReactorBase(name)
: ReactorBase(sol, name)
{
if (!sol || !(sol->thermo())) {
throw CanteraError("Reactor::Reactor",
"Reactor contents must be provided as constructor arguments");
}
setSolution(sol);
setThermo(*sol->thermo());
setKinetics(*sol->kinetics());
}

View File

@ -24,7 +24,14 @@ ReactorBase::ReactorBase(shared_ptr<Solution> sol, const string& name)
throw CanteraError("ReactorBase::ReactorBase",
"Missing or incomplete Solution object.");
}
setSolution(sol);
m_solution = sol;
setThermo(*sol->thermo());
try {
setKinetics(*sol->kinetics());
} catch (NotImplementedError&) {
// kinetics not used (example: Reservoir)
}
m_solution->thermo()->addSpeciesLock();
}
ReactorBase::~ReactorBase()
@ -47,7 +54,11 @@ bool ReactorBase::setDefaultName(map<string, int>& counts)
return true;
}
void ReactorBase::setSolution(shared_ptr<Solution> sol) {
void ReactorBase::setSolution(shared_ptr<Solution> sol)
{
warn_deprecated("ReactorBase::setSolution",
"After Cantera 3.2, a change of reactor contents after instantiation "
"will be disabled.");
if (!sol || !(sol->thermo())) {
throw CanteraError("ReactorBase::setSolution",
"Missing or incomplete Solution object.");

View File

@ -10,21 +10,26 @@
namespace Cantera
{
bool WallBase::setDefaultName(map<string, int>& counts)
WallBase::WallBase(shared_ptr<ReactorBase> r0, shared_ptr<ReactorBase> r1,
const string& name) : ConnectorNode(r0, r1, name)
{
if (m_defaultNameSet) {
return false;
if (!m_nodes.first || !m_nodes.second) {
warn_deprecated("FlowDevice::FlowDevice",
"After Cantera 3.2, Reactors must be provided to a FlowDevice "
"constructor.");
return;
}
m_defaultNameSet = true;
if (m_name == "(none)" || m_name == "") {
m_name = fmt::format("{}_{}", type(), counts[type()]);
}
counts[type()]++;
return true;
m_left = r0.get();
m_right = r1.get();
m_left->addWall(*this, 0);
m_right->addWall(*this, 1);
}
bool WallBase::install(ReactorBase& rleft, ReactorBase& rright)
{
warn_deprecated("WallBase::install",
"To be removed after Cantera 3.2. Reactors should be provided to constructor "
"instead.");
// check if wall is already installed
if (m_left || m_right) {
return false;

View File

@ -1,39 +0,0 @@
//! @file WallFactory.cpp
// 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 "cantera/zeroD/WallFactory.h"
#include "cantera/zeroD/Wall.h"
namespace Cantera
{
WallFactory* WallFactory::s_factory = 0;
std::mutex WallFactory::wall_mutex;
WallFactory::WallFactory()
{
reg("Wall", [](const string& name) { return new Wall(name); });
}
WallFactory* WallFactory::factory() {
std::unique_lock<std::mutex> lock(wall_mutex);
if (!s_factory) {
s_factory = new WallFactory;
}
return s_factory;
}
void WallFactory::deleteFactory() {
std::unique_lock<std::mutex> lock(wall_mutex);
delete s_factory;
s_factory = 0;
}
shared_ptr<WallBase> newWall(const string& model, const string& name)
{
return shared_ptr<WallBase>(WallFactory::factory()->create(model, name));
}
}

View File

@ -728,13 +728,6 @@ class TestReactor:
assert self.r1.name.startswith(f"{self.r1.type}_") # default name
assert res.name.startswith(f"{res.type}_") # default name
def test_valve_errors(self):
self.make_reactors()
v = ct.Valve(self.r1, self.r2)
with pytest.raises(ct.CanteraError, match='Already installed'):
# inlet and outlet cannot be reassigned
v._install(self.r2, self.r1)
def test_pressure_controller1(self):
self.make_reactors(n_reactors=1)
g = ct.Solution('h2o2.yaml', transport_model=None)

View File

@ -19,16 +19,16 @@ TEST(zerodim, simple)
auto sol = newSolution("gri30.yaml", "gri30", "none");
sol->thermo()->setState_TPX(T, P, X);
auto cppReactor = newReactor("IdealGasReactor", sol, "simple");
ASSERT_EQ(cppReactor->name(), "simple");
cppReactor->initialize();
auto reactor = newReactor("IdealGasReactor", sol, "simple");
ASSERT_EQ(reactor->name(), "simple");
reactor->initialize();
ReactorNet network;
network.addReactor(dynamic_cast<IdealGasReactor&>(*cppReactor));
network.addReactor(dynamic_cast<IdealGasReactor&>(*reactor));
network.initialize();
double t = 0.0;
while (t < 0.1) {
ASSERT_GE(cppReactor->temperature(), T);
ASSERT_GE(reactor->temperature(), T);
t = network.time() + 5e-3;
network.advance(t);
}
@ -56,6 +56,97 @@ TEST(zerodim, test_guards)
EXPECT_THROW(Valve().updateMassFlowRate(0.), CanteraError);
}
TEST(zerodim, flowdevice)
{
auto gas = newSolution("gri30.yaml", "gri30", "none");
auto node0 = newReactor("IdealGasReactor", gas, "upstream");
auto node1 = newReactor("IdealGasReactor", gas, "downstream");
auto valve = newFlowDevice("Valve", node0, node1, "valve");
ASSERT_EQ(valve->name(), "valve");
ASSERT_EQ(valve->in().name(), "upstream");
ASSERT_EQ(valve->out().name(), "downstream");
ASSERT_EQ(node0->nInlets(), 0);
ASSERT_EQ(node0->nOutlets(), 1);
ASSERT_EQ(node1->nInlets(), 1);
ASSERT_EQ(node1->nOutlets(), 0);
}
TEST(zerodim, wall)
{
auto gas = newSolution("gri30.yaml", "gri30", "none");
auto node0 = newReactor("IdealGasReactor", gas, "left");
auto node1 = newReactor("IdealGasReactor", gas, "right");
auto wall = newWall("Wall", node0, node1, "wall");
ASSERT_EQ(wall->name(), "wall");
ASSERT_EQ(wall->left().name(), "left");
ASSERT_EQ(wall->right().name(), "right");
ASSERT_EQ(node0->nWalls(), 1);
ASSERT_EQ(node1->nWalls(), 1);
}
TEST(zerodim, mole_reactor)
{
// simplified version of continuous_reactor.py
auto gas = newSolution("h2o2.yaml", "ohmech", "none");
auto tank = make_shared<Reservoir>(gas, "fuel-air-tank");
auto exhaust = make_shared<Reservoir>(gas, "exhaust");
auto stirred = make_shared<IdealGasMoleReactor>(gas, "stirred-reactor");
stirred->setEnergy(0);
stirred->setInitialVolume(30.5 * 1e-6);
auto mfc = make_shared<MassFlowController>(tank, stirred, "mass-flow-controller");
double residenceTime = 2.;
double mass = stirred->mass();
mfc->setMassFlowRate(mass/residenceTime);
auto preg = make_shared<PressureController>(stirred, exhaust, "pressure-regulator");
preg->setPrimary(mfc.get());
preg->setPressureCoeff(1e-3);
auto net = ReactorNet();
net.addReactor(*stirred);
net.initialize();
}
TEST(zerodim, mole_reactor_2)
{
// simplified version of continuous_reactor.py
auto gas = newSolution("h2o2.yaml", "ohmech", "none");
auto tank = std::dynamic_pointer_cast<Reservoir>(
newReactor("Reservoir", gas, "fuel-air-tank"));
auto exhaust = std::dynamic_pointer_cast<Reservoir>(
newReactor("Reservoir", gas, "exhaust"));
auto stirred = std::dynamic_pointer_cast<IdealGasMoleReactor>(
newReactor("IdealGasMoleReactor", gas, "stirred-reactor"));
stirred->setEnergy(0);
stirred->setInitialVolume(30.5 * 1e-6);
auto mfc = std::dynamic_pointer_cast<MassFlowController>(
newConnectorNode("MassFlowController", tank, stirred, "mass-flow-controller"));
double residenceTime = 2.;
double mass = stirred->mass();
mfc->setMassFlowRate(mass/residenceTime);
auto preg = std::dynamic_pointer_cast<PressureController>(
newConnectorNode("PressureController", stirred, exhaust, "pressure-regulator"));
preg->setPrimary(mfc.get());
preg->setPressureCoeff(1e-3);
auto net = ReactorNet();
net.addReactor(*stirred);
net.initialize();
}
// This test ensures that prior reactor initialization of a reactor does
// not affect later integration within a network. This example was
// adapted from test_reactor.py::test_equilibrium_HP.