diff --git a/include/cantera/zeroD/FlowDevice.h b/include/cantera/zeroD/FlowDevice.h index 46902db7706..ff9e7e3d9c7 100644 --- a/include/cantera/zeroD/FlowDevice.h +++ b/include/cantera/zeroD/FlowDevice.h @@ -30,7 +30,7 @@ const int Valve_Type = 3; class FlowDevice { public: - FlowDevice(); + FlowDevice(const std::string& name = "(none)"); virtual ~FlowDevice() {} FlowDevice(const FlowDevice&) = delete; @@ -54,6 +54,19 @@ class FlowDevice return m_type; } + //! Return the name of this flow device + std::string name() const { + return m_name; + } + + //! Set the name of this flow device + void setName(const std::string& name) { + m_name = name; + } + + //! Generate self-documenting YAML string. + virtual std::string toYAML() const; + //! Mass flow rate (kg/s). //! @deprecated The 'time' argument will be removed after Cantera 2.5. //! Evaluating the mass flow rate at times other than the current time @@ -101,7 +114,7 @@ class FlowDevice } //! Return a const reference to the downstream reactor. - const ReactorBase& out() const { + ReactorBase& out() const { return *m_out; } @@ -166,6 +179,8 @@ class FlowDevice int m_type; //!< @deprecated To be removed after Cantera 2.5. + std::string m_name; //! flow device name + private: size_t m_nspin, m_nspout; ReactorBase* m_in; diff --git a/include/cantera/zeroD/Reactor.h b/include/cantera/zeroD/Reactor.h index 41528862b28..93c75c5f0f0 100644 --- a/include/cantera/zeroD/Reactor.h +++ b/include/cantera/zeroD/Reactor.h @@ -205,9 +205,6 @@ class Reactor : public ReactorBase //! Get initial conditions for SurfPhase objects attached to this reactor virtual void getSurfaceInitialConditions(double* y); - //! Pointer to the homogeneous Kinetics object that handles the reactions - Kinetics* m_kin; - doublereal m_vdot; //!< net rate of volume change from moving walls [m^3/s] doublereal m_Q; //!< net heat transfer through walls [W] doublereal m_mass; //!< total mass diff --git a/include/cantera/zeroD/ReactorBase.h b/include/cantera/zeroD/ReactorBase.h index 9ac35a5398e..b80d29bf59b 100644 --- a/include/cantera/zeroD/ReactorBase.h +++ b/include/cantera/zeroD/ReactorBase.h @@ -89,7 +89,7 @@ class ReactorBase //! Specify the mixture contained in the reactor. Note that a pointer to //! this substance is stored, and as the integration proceeds, the state of //! the substance is modified. - virtual void setThermoMgr(thermo_t& thermo); + virtual void setThermoMgr(ThermoPhase& thermo); //! Specify chemical kinetics governing the reactor. virtual void setKineticsMgr(Kinetics& kin) { @@ -101,6 +101,9 @@ class ReactorBase throw NotImplementedError("ReactorBase::setChemistry"); } + //! Generate self-documenting YAML string. + virtual std::string toYAML() const; + //! Set the energy equation on or off. virtual void setEnergy(int eflag = 1) { throw NotImplementedError("ReactorBase::setEnergy"); @@ -136,6 +139,11 @@ class ReactorBase return m_wall.size(); } + //! Return the number of ReactorSurface objects connected to this reactor. + size_t nSurfaces() { + return m_surfaces.size(); + } + //! Insert a Wall between this reactor and another reactor. /*! * `lr` = 0 if this reactor is to the left of the wall and `lr` = 1 if @@ -177,7 +185,7 @@ class ReactorBase virtual void syncState(); //! return a reference to the contents. - thermo_t& contents() { + ThermoPhase& contents() { if (!m_thermo) { throw CanteraError("ReactorBase::contents", "Reactor contents not defined."); @@ -185,7 +193,7 @@ class ReactorBase return *m_thermo; } - const thermo_t& contents() const { + const ThermoPhase& contents() const { if (!m_thermo) { throw CanteraError("ReactorBase::contents", "Reactor contents not defined."); @@ -261,7 +269,11 @@ class ReactorBase //! Number of homogeneous species in the mixture size_t m_nsp; - thermo_t* m_thermo; + ThermoPhase* m_thermo; + + //! Pointer to the homogeneous Kinetics object that handles the reactions + Kinetics* m_kin; + doublereal m_vol; doublereal m_enthalpy; doublereal m_intEnergy; diff --git a/include/cantera/zeroD/ReactorNet.h b/include/cantera/zeroD/ReactorNet.h index af656e07311..739357a9452 100644 --- a/include/cantera/zeroD/ReactorNet.h +++ b/include/cantera/zeroD/ReactorNet.h @@ -102,6 +102,9 @@ class ReactorNet : public FuncEval //@} + //! Generate self-documenting YAML string. + std::string toYAML() const; + //! Add the reactor *r* to this reactor network. void addReactor(Reactor& r); diff --git a/include/cantera/zeroD/ReactorSurface.h b/include/cantera/zeroD/ReactorSurface.h index e3a4d3d2994..b3b903817ad 100644 --- a/include/cantera/zeroD/ReactorSurface.h +++ b/include/cantera/zeroD/ReactorSurface.h @@ -17,11 +17,30 @@ class SurfPhase; class ReactorSurface { public: - ReactorSurface(); + ReactorSurface(const std::string& name = "(none)"); virtual ~ReactorSurface() {} ReactorSurface(const ReactorSurface&) = delete; ReactorSurface& operator=(const ReactorSurface&) = delete; + //! String indicating the wall model implemented. Usually + //! corresponds to the name of the derived class. + virtual std::string type() const { + return "ReactorSurface"; + } + + //! Return the name of this surface + std::string name() const { + return m_name; + } + + //! Set the name of this surface + void setName(const std::string& name) { + m_name = name; + } + + //! Generate self-documenting YAML string. + virtual std::string toYAML() const; + //! Returns the surface area [m^2] double area() const; @@ -90,6 +109,8 @@ class ReactorSurface ReactorBase* m_reactor; vector_fp m_cov; std::vector m_params; + + std::string m_name; //! reactor surface name }; } diff --git a/include/cantera/zeroD/Wall.h b/include/cantera/zeroD/Wall.h index ff7fe0a4690..2be60b12d8f 100644 --- a/include/cantera/zeroD/Wall.h +++ b/include/cantera/zeroD/Wall.h @@ -28,7 +28,7 @@ const int WallType = 1; class WallBase { public: - WallBase(); + WallBase(const std::string& name = "(none)"); virtual ~WallBase() {} WallBase(const WallBase&) = delete; @@ -40,6 +40,19 @@ class WallBase return "WallBase"; } + //! Return the name of this wall + std::string name() const { + return m_name; + } + + //! Set the name of this wall + void setName(const std::string& name) { + m_name = name; + } + + //! Generate self-documenting YAML string. + virtual std::string toYAML() const; + //! Rate of volume change (m^3/s) for the adjacent reactors. /*! * This method is called by Reactor::evalWalls(). Base class method @@ -95,7 +108,7 @@ class WallBase } //! Return a reference to the Reactor or Reservoir to the right of the wall. - const ReactorBase& right() { + ReactorBase& right() const { return *m_right; } @@ -106,6 +119,8 @@ class WallBase std::vector m_surf; double m_area; + + std::string m_name; //! wall name }; //! Represents a wall between between two ReactorBase objects. diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 26ff56dcd3c..439101e1045 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -546,6 +546,7 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera": cdef cppclass CxxReactorBase "Cantera::ReactorBase": CxxReactorBase() string typeStr() + string toYAML() void setThermoMgr(CxxThermoPhase&) except +translate_exception void restoreState() except +translate_exception void syncState() except +translate_exception @@ -582,6 +583,9 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera": cdef cppclass CxxWallBase "Cantera::WallBase": CxxWallBase() string type() + string name() + void setName(string) + string toYAML() cbool install(CxxReactorBase&, CxxReactorBase&) double area() void setArea(double) @@ -610,6 +614,10 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera": cdef cppclass CxxReactorSurface "Cantera::ReactorSurface": CxxReactorSurface() + string type() + string name() + void setName(string) + string toYAML() double area() void setArea(double) void setKinetics(CxxKinetics*) @@ -624,6 +632,9 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera": cdef cppclass CxxFlowDevice "Cantera::FlowDevice": CxxFlowDevice() string typeStr() + string name() + void setName(string) + string toYAML() double massFlowRate() except +translate_exception double massFlowRate(double) except +translate_exception cbool install(CxxReactorBase&, CxxReactorBase&) except +translate_exception @@ -652,6 +663,7 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera": cdef cppclass CxxReactorNet "Cantera::ReactorNet": CxxReactorNet() void addReactor(CxxReactor&) + string toYAML() except +translate_exception double advance(double, cbool) except +translate_exception double step() except +translate_exception void initialize() except +translate_exception @@ -1088,7 +1100,6 @@ cdef class WallBase: cdef object _heat_flux_func cdef ReactorBase _left_reactor cdef ReactorBase _right_reactor - cdef str name cdef class Wall(WallBase): pass @@ -1097,7 +1108,6 @@ cdef class FlowDevice: cdef CxxFlowDevice* dev cdef Func1 _rate_func cdef Func1 _time_func - cdef str name cdef ReactorBase _upstream cdef ReactorBase _downstream diff --git a/interfaces/cython/cantera/examples/reactors/ic_engine.py b/interfaces/cython/cantera/examples/reactors/ic_engine.py index c1260cfd0ee..f02d19b4312 100644 --- a/interfaces/cython/cantera/examples/reactors/ic_engine.py +++ b/interfaces/cython/cantera/examples/reactors/ic_engine.py @@ -97,15 +97,15 @@ def piston_speed(t): # define initial state and set up reactor gas.TPX = T_inlet, p_inlet, comp_inlet -cyl = ct.IdealGasReactor(gas) +cyl = ct.IdealGasReactor(gas, name='Cylinder') cyl.volume = V_oT # define inlet state gas.TPX = T_inlet, p_inlet, comp_inlet -inlet = ct.Reservoir(gas) +inlet = ct.Reservoir(gas, name='Inlet') # inlet valve -inlet_valve = ct.Valve(inlet, cyl) +inlet_valve = ct.Valve(inlet, cyl, name='InletValve') inlet_delta = np.mod(inlet_close - inlet_open, 4 * np.pi) inlet_valve.valve_coeff = inlet_valve_coeff inlet_valve.set_time_function( @@ -113,10 +113,10 @@ def piston_speed(t): # define injector state (gaseous!) gas.TPX = T_injector, p_injector, comp_injector -injector = ct.Reservoir(gas) +injector = ct.Reservoir(gas, name='Fuel') # injector is modeled as a mass flow controller -injector_mfc = ct.MassFlowController(injector, cyl) +injector_mfc = ct.MassFlowController(injector, cyl, name='Injector') injector_delta = np.mod(injector_close - injector_open, 4 * np.pi) injector_t_open = (injector_close - injector_open) / 2. / np.pi / f injector_mfc.mass_flow_coeff = injector_mass / injector_t_open @@ -125,10 +125,10 @@ def piston_speed(t): # define outlet pressure (temperature and composition don't matter) gas.TPX = T_ambient, p_outlet, comp_ambient -outlet = ct.Reservoir(gas) +outlet = ct.Reservoir(gas, name='Outlet') # outlet valve -outlet_valve = ct.Valve(cyl, outlet) +outlet_valve = ct.Valve(cyl, outlet, name='OutletValve') outlet_delta = np.mod(outlet_close - outlet_open, 4 * np.pi) outlet_valve.valve_coeff = outlet_valve_coeff outlet_valve.set_time_function( @@ -136,10 +136,10 @@ def piston_speed(t): # define ambient pressure (temperature and composition don't matter) gas.TPX = T_ambient, p_ambient, comp_ambient -ambient_air = ct.Reservoir(gas) +ambient_air = ct.Reservoir(ct.Solution('air.cti'), name='Ambient') # piston is modeled as a moving wall -piston = ct.Wall(ambient_air, cyl) +piston = ct.Wall(ambient_air, cyl, name='Piston') piston.area = A_piston piston.set_velocity(piston_speed) diff --git a/interfaces/cython/cantera/reactor.pyx b/interfaces/cython/cantera/reactor.pyx index 2bde1067d72..80a36949080 100644 --- a/interfaces/cython/cantera/reactor.pyx +++ b/interfaces/cython/cantera/reactor.pyx @@ -7,6 +7,18 @@ import numbers as _numbers _reactor_counts = _defaultdict(int) + +def _unique_name(name, class_type): + """Generate a unique name.""" + + if name is not None: + return name + else: + _reactor_counts[class_type] += 1 + n = _reactor_counts[class_type] + return '{0}_{1}'.format(class_type, n) + + # Need a pure-python class to store weakrefs to class _WeakrefProxy: pass @@ -28,12 +40,7 @@ cdef class ReactorBase: if isinstance(contents, ThermoPhase): self.insert(contents) - if name is not None: - self.name = name - else: - _reactor_counts[self.reactor_type] += 1 - n = _reactor_counts[self.reactor_type] - self.name = '{0}_{1}'.format(self.reactor_type, n) + self.name = _unique_name(name, self.type) if volume is not None: self.volume = volume @@ -63,6 +70,10 @@ cdef class ReactorBase: def __set__(self, name): self.rbase.setName(stringify(name)) + def to_yaml(self): + """Return a YAML representation of the Reactor setup.""" + return pystr(self.rbase.toYAML()) + def syncState(self): """ Set the state of the Reactor to match that of the associated @@ -406,7 +417,8 @@ cdef class ReactorSurface: def __dealloc__(self): del self.surface - def __init__(self, kin=None, Reactor r=None, *, A=None): + def __init__(self, kin=None, Reactor r=None, *, name=None, A=None): + if kin is not None: self.kinetics = kin if r is not None: @@ -414,6 +426,25 @@ cdef class ReactorSurface: if A is not None: self.area = A + self.name = _unique_name(name, self.type) + + property type: + """The type of the reactor.""" + def __get__(self): + return pystr(self.surface.type()) + + property name: + """The name of the reactor.""" + def __get__(self): + return pystr(self.surface.name()) + + def __set__(self, name): + self.surface.setName(stringify(name)) + + def to_yaml(self): + """Return a YAML representation of the ReactorSurface setup.""" + return pystr(self.surface.toYAML()) + def install(self, Reactor r): r.reactor.addSurface(self.surface) @@ -507,12 +538,8 @@ cdef class WallBase: self._heat_flux_func = None self._install(left, right) - if name is not None: - self.name = name - else: - _reactor_counts['Wall'] += 1 - n = _reactor_counts['Wall'] - self.name = 'Wall_{0}'.format(n) + + self.name = _unique_name(name, self.type) if A is not None: self.area = A @@ -542,6 +569,18 @@ cdef class WallBase: def __get__(self): return pystr(self.wall.type()) + property name: + """The name of the reactor.""" + def __get__(self): + return pystr(self.wall.name()) + + def __set__(self, name): + self.wall.setName(stringify(name)) + + def to_yaml(self): + """Return a YAML representation of the WallBase setup.""" + return pystr(self.wall.toYAML()) + property left: """ The left surface of this wall. """ def __get__(self): @@ -678,12 +717,7 @@ cdef class FlowDevice: assert self.dev != NULL self._rate_func = None - if name is not None: - self.name = name - else: - _reactor_counts[self.__class__.__name__] += 1 - n = _reactor_counts[self.__class__.__name__] - self.name = '{0}_{1}'.format(self.__class__.__name__, n) + self.name = _unique_name(name, self.type) self._install(upstream, downstream) @@ -695,6 +729,18 @@ cdef class FlowDevice: def __get__(self): return pystr(self.dev.typeStr()) + property name: + """The name of the reactor.""" + def __get__(self): + return pystr(self.dev.name()) + + def __set__(self, name): + self.dev.setName(stringify(name)) + + def to_yaml(self): + """Return a YAML representation of the FlowDevice setup.""" + return pystr(self.dev.toYAML()) + def _install(self, ReactorBase upstream, ReactorBase downstream): """ Install the device between the *upstream* (source) and *downstream* @@ -1045,6 +1091,20 @@ cdef class ReactorNet: self._reactors.append(r) self.net.addReactor(deref(r.reactor)) + def to_yaml(self): + """ + Return a YAML representation of the ReactorNet structure. To + print the structure to the terminal, simply call the ReactorNet object. + The following two statements are equivalent for the sim object:: + + >>> sim() + >>> print(sim.to_yaml()) + """ + return pystr(self.net.toYAML()) + + def __call__(self): + print(self.to_yaml()) + def advance(self, double t, pybool apply_limit=True): """ Advance the state of the reactor network in time from the current time diff --git a/interfaces/cython/cantera/test/test_reactor.py b/interfaces/cython/cantera/test/test_reactor.py index 7747c1ddab9..496347a70e2 100644 --- a/interfaces/cython/cantera/test/test_reactor.py +++ b/interfaces/cython/cantera/test/test_reactor.py @@ -5,6 +5,10 @@ import numpy as np from .utilities import unittest +try: + from ruamel.yaml import YAML +except: + from ruamel_yaml import YAML import cantera as ct from . import utilities @@ -396,6 +400,43 @@ def v(t): self.assertNear(self.r1.volume, V1 + 1.0 * A, 1e-7) self.assertNear(self.r2.volume, V2 - 1.0 * A, 1e-7) + def test_yaml(self, **kwargs): + self.make_reactors() + self.add_wall() + reservoir = ct.Reservoir(self.gas1) + mfc = ct.MassFlowController(reservoir, self.r1) + + self.assertTrue(isinstance(self.net.to_yaml(), str)) + self.assertTrue(isinstance(self.r1.to_yaml(), str)) + self.assertTrue(isinstance(self.r2.to_yaml(), str)) + self.assertTrue(isinstance(self.w.to_yaml(), str)) + self.assertTrue(isinstance(reservoir.to_yaml(), str)) + self.assertTrue(isinstance(mfc.to_yaml(), str)) + + yaml = YAML() + yml = yaml.load(self.net.to_yaml()) + self.assertTrue('reactor-network' in yml) + net = yml['reactor-network'] + + self.assertTrue('reactors' in net) + reactors = [tuple(n)[0] for n in net['reactors']] + self.assertTrue(self.r1.name in reactors) + self.assertTrue(self.r2.name in reactors) + self.assertTrue(reservoir.name in reactors) + + self.assertTrue('walls' in net) + walls = [tuple(n)[0] for n in net['walls']] + self.assertTrue(self.w.name in walls) + + self.assertTrue('flow-devices' in net) + devices = [tuple(n)[0] for n in net['flow-devices']] + self.assertTrue(mfc.name in devices) + + self.r2.name = self.r1.name + with self.assertRaisesRegex(ct.CanteraError, 'names are not unique'): + # reactor names need to be unique + self.net.to_yaml() + def test_disable_energy(self): self.make_reactors(T1=500) self.r1.energy_enabled = False diff --git a/src/zeroD/FlowDevice.cpp b/src/zeroD/FlowDevice.cpp index 11ed774d19e..ce534cedee9 100644 --- a/src/zeroD/FlowDevice.cpp +++ b/src/zeroD/FlowDevice.cpp @@ -6,14 +6,20 @@ #include "cantera/zeroD/FlowDevice.h" #include "cantera/zeroD/ReactorBase.h" #include "cantera/numerics/Func1.h" +#include "cantera/base/yaml.h" namespace Cantera { -FlowDevice::FlowDevice() : m_mdot(Undef), m_pfunc(0), m_tfunc(0), - m_coeff(1.0), m_type(0), - m_nspin(0), m_nspout(0), - m_in(0), m_out(0) {} +FlowDevice::FlowDevice(const std::string& name) : + m_mdot(Undef), + m_pfunc(0), m_tfunc(0), + m_coeff(1.0), m_type(0), + m_nspin(0), m_nspout(0), + m_in(0), m_out(0) +{ + m_name = name; +} bool FlowDevice::install(ReactorBase& in, ReactorBase& out) { @@ -46,6 +52,24 @@ bool FlowDevice::install(ReactorBase& in, ReactorBase& out) return true; } +std::string FlowDevice::toYAML() const +{ + YAML::Emitter yml; + std::stringstream out; + + yml << YAML::BeginMap; + yml << YAML::Key << name(); + yml << YAML::BeginMap; + yml << YAML::Key << "type" << YAML::Value << typeStr(); + yml << YAML::Key << "reactors" << YAML::Flow; + yml << YAML::BeginSeq << m_in->name() << m_out->name() << YAML::EndSeq; + yml << YAML::EndMap; + yml << YAML::EndMap; + + out << yml.c_str(); + return out.str(); +} + void FlowDevice::setPressureFunction(Func1* f) { m_pfunc = f; diff --git a/src/zeroD/Reactor.cpp b/src/zeroD/Reactor.cpp index da3f985312b..36af26216ff 100644 --- a/src/zeroD/Reactor.cpp +++ b/src/zeroD/Reactor.cpp @@ -18,7 +18,6 @@ namespace bmt = boost::math::tools; namespace Cantera { Reactor::Reactor() : - m_kin(0), m_vdot(0.0), m_Q(0.0), m_mass(0.0), diff --git a/src/zeroD/ReactorBase.cpp b/src/zeroD/ReactorBase.cpp index edcb7e1532c..1df46a38c9b 100644 --- a/src/zeroD/ReactorBase.cpp +++ b/src/zeroD/ReactorBase.cpp @@ -1,3 +1,4 @@ + //! @file ReactorBase.cpp // This file is part of Cantera. See License.txt in the top-level directory or @@ -7,13 +8,17 @@ #include "cantera/zeroD/FlowDevice.h" #include "cantera/zeroD/ReactorNet.h" #include "cantera/zeroD/ReactorSurface.h" +#include "cantera/thermo/SurfPhase.h" +#include "cantera/base/yaml.h" using namespace std; namespace Cantera { -ReactorBase::ReactorBase(const string& name) : m_nsp(0), +ReactorBase::ReactorBase(const string& name) : + m_nsp(0), m_thermo(0), + m_kin(0), m_vol(1.0), m_enthalpy(0.0), m_intEnergy(0.0), @@ -23,7 +28,7 @@ ReactorBase::ReactorBase(const string& name) : m_nsp(0), m_name = name; } -void ReactorBase::setThermoMgr(thermo_t& thermo) +void ReactorBase::setThermoMgr(ThermoPhase& thermo) { m_thermo = &thermo; m_nsp = m_thermo->nSpecies(); @@ -44,6 +49,30 @@ void ReactorBase::syncState() } } +std::string ReactorBase::toYAML() const +{ + YAML::Emitter yml; + std::stringstream out; + + yml << YAML::BeginMap; + yml << YAML::Key << name(); + yml << YAML::BeginMap; + yml << YAML::Key << "type"; + yml << YAML::Value << typeStr(); + yml << YAML::Key << "phases" << YAML::Flow; + yml << YAML::BeginSeq; + yml << m_thermo->name(); + for (const auto& s : m_surfaces) { + yml << s->thermo()->name(); + } + yml << YAML::EndSeq; + yml << YAML::EndMap; + yml << YAML::EndMap; + + out << yml.c_str(); + return out.str(); +} + void ReactorBase::addInlet(FlowDevice& inlet) { m_inlet.push_back(&inlet); diff --git a/src/zeroD/ReactorNet.cpp b/src/zeroD/ReactorNet.cpp index e51627c7121..9d811c18b2a 100644 --- a/src/zeroD/ReactorNet.cpp +++ b/src/zeroD/ReactorNet.cpp @@ -6,6 +6,7 @@ #include "cantera/zeroD/ReactorNet.h" #include "cantera/zeroD/FlowDevice.h" #include "cantera/zeroD/Wall.h" +#include "cantera/base/yaml.h" #include @@ -30,6 +31,149 @@ ReactorNet::ReactorNet() : m_integ->setProblemType(DENSE + NOJAC); } +std::string uniqueName(void const *ptr) { + // use pointer address as unique name + std::ostringstream address; + address << ptr; + return address.str(); +} + +std::string ReactorNet::toYAML() const +{ + YAML::Emitter yml; + std::stringstream out; + std::map names; + + // components: maps use generic pointers + std::map reactors; + std::map walls; + std::map devices; + // std::map surfaces; + + // construct complete maps + for (auto r=m_reactors.begin(); r!=m_reactors.end(); r++) { + + // insert reactor (emplace ensures that it remains unique) + ReactorBase* rb = *r; + reactors.emplace(uniqueName((void const *)rb), rb); + + // walls + for (size_t i=0; inWalls(); i++) { + + // wall has reactor to left and right + WallBase& wall = rb->wall(i); + walls.emplace(uniqueName((void const *)(&wall)), &wall); + + ReactorBase& rl = wall.left(); + reactors.emplace(uniqueName((void const *)(&rl)), &rl); + + ReactorBase& rr = wall.right(); + reactors.emplace(uniqueName((void const *)(&rr)), &rr); + } + + // inlets + for (size_t i=0; inInlets(); i++) { + + // flow devices at inlet have inlets + FlowDevice& inlet = rb->inlet(i); + devices.emplace(uniqueName((void const *)(&inlet)), &inlet); + + ReactorBase& in = inlet.in(); + reactors.emplace(uniqueName((void const *)(&in)), &in); + } + + // outlets + for (size_t i=0; inOutlets(); i++) { + + // flow devices at outlet have outlets + FlowDevice& outlet = rb->outlet(i); + devices.emplace(uniqueName((void const *)(&outlet)), &outlet); + + ReactorBase& out = outlet.out(); + reactors.emplace(uniqueName((void const *)(&out)), &out); + } + + // // surfaces + // for (size_t i=0; inSurfaces(); i++) { + + // ReactorSurface* surface = rb->surface(i); + // surfaces.emplace(uniqueName((void const *)surface), surface); + // } + } + + // header + yml << YAML::BeginMap; + yml << YAML::Key << "reactor-network"; + yml << YAML::BeginMap; + + // emit list of reactors + yml << YAML::Key << "reactors"; + yml << YAML::Value << YAML::BeginSeq; + for (const auto& r : reactors) { + names.emplace(r.second->name(), r.first); + yml << YAML::Load(r.second->toYAML()); + } + yml << YAML::EndSeq; + if (names.size()!=reactors.size()) { + // this should raise a warning, but an applicable warning system is not in place + throw CanteraError("ReactorNet::toYAML", "ReactorBase names are not unique."); + } + + // emit list of walls + names.clear(); + if (walls.size()) { + yml << YAML::Key << "walls"; + yml << YAML::Value << YAML::BeginSeq; + for (const auto& w : walls) { + names.emplace(w.second->name(), w.first); + yml << YAML::Load(w.second->toYAML()); + } + yml << YAML::EndSeq; + } + if (names.size()!=walls.size()) { + // this should raise a warning, but an applicable warning system is not in place + throw CanteraError("ReactorNet::toYAML", "Wall names are not unique."); + } + + // emit list of flow devices + names.clear(); + if (devices.size()) { + yml << YAML::Key << "flow-devices"; + yml << YAML::Value << YAML::BeginSeq; + for (const auto& d : devices) { + names.emplace(d.second->name(), d.first); + yml << YAML::Load(d.second->toYAML()); + } + yml << YAML::EndSeq; + } + if (names.size()!=devices.size()) { + // this should raise a warning, but an applicable warning system is not in place + throw CanteraError("ReactorNet::toYAML", "FlowDevice names are not unique."); + } + + // // emit list of reactor surfaces + // names.clear(); + // if (surfaces.size()) { + // yml << YAML::Key << "ReactorSurface"; + // yml << YAML::Value << YAML::BeginSeq; + // for (const auto& s : surfaces) { + // names.emplace(s.second->name(), s.first); + // yml << YAML::Load(s.second->toYAML()); + // } + // yml << YAML::EndSeq; + // } + // if (names.size()!=surfaces.size()) { + // // this should raise a warning, but an applicable warning system is not in place + // throw CanteraError("ReactorNet::toYAML", "ReactorSurface names are not unique."); + // } + + // close out + yml << YAML::EndMap; + yml << YAML::EndMap; + out << yml.c_str(); + return out.str(); +} + void ReactorNet::setInitialTime(double time) { m_time = time; diff --git a/src/zeroD/ReactorSurface.cpp b/src/zeroD/ReactorSurface.cpp index 88a446ecaf7..5122d30886c 100644 --- a/src/zeroD/ReactorSurface.cpp +++ b/src/zeroD/ReactorSurface.cpp @@ -7,16 +7,38 @@ #include "cantera/zeroD/ReactorNet.h" #include "cantera/thermo/SurfPhase.h" #include "cantera/kinetics/Kinetics.h" +#include "cantera/base/yaml.h" namespace Cantera { -ReactorSurface::ReactorSurface() - : m_area(1.0) - , m_thermo(nullptr) - , m_kinetics(nullptr) - , m_reactor(nullptr) +ReactorSurface::ReactorSurface(const std::string& name) : + m_area(1.0), + m_thermo(nullptr), + m_kinetics(nullptr), + m_reactor(nullptr) { + m_name = name; +} + +std::string ReactorSurface::toYAML() const +{ + YAML::Emitter yml; + std::stringstream out; + + yml << YAML::BeginMap; + yml << YAML::Key << name(); + yml << YAML::BeginMap; + yml << YAML::Key << "type" << YAML::Value << type(); + yml << YAML::Key << "reactor" << YAML::Value << m_reactor->name(); + yml << YAML::Key << "phase" << YAML::Value << m_thermo->name(); + yml << YAML::Key << "thermo.type" << YAML::Value << m_thermo->type(); + yml << YAML::Key << "kinetics.type" << YAML::Value << m_kinetics->kineticsType(); + yml << YAML::EndMap; + yml << YAML::EndMap; + + out << yml.c_str(); + return out.str(); } double ReactorSurface::area() const diff --git a/src/zeroD/Wall.cpp b/src/zeroD/Wall.cpp index 6132186cd84..667142c14b4 100644 --- a/src/zeroD/Wall.cpp +++ b/src/zeroD/Wall.cpp @@ -7,11 +7,16 @@ #include "cantera/numerics/Func1.h" #include "cantera/zeroD/Wall.h" #include "cantera/thermo/SurfPhase.h" +#include "cantera/base/yaml.h" namespace Cantera { -WallBase::WallBase() : m_left(0), m_right(0), m_surf(2), m_area(1.0) {} +WallBase::WallBase(const std::string& name) : + m_left(0), m_right(0), m_surf(2), m_area(1.0) +{ + m_name = name; +} bool WallBase::install(ReactorBase& rleft, ReactorBase& rright) { @@ -28,6 +33,24 @@ bool WallBase::install(ReactorBase& rleft, ReactorBase& rright) return true; } +std::string WallBase::toYAML() const +{ + YAML::Emitter yml; + std::stringstream out; + + yml << YAML::BeginMap; + yml << YAML::Key << name(); + yml << YAML::BeginMap; + yml << YAML::Key << "type" << YAML::Value << type(); + yml << YAML::Key << "reactors" << YAML::Flow; + yml << YAML::BeginSeq << m_left->name() << m_right->name() << YAML::EndSeq; + yml << YAML::EndMap; + yml << YAML::EndMap; + + out << yml.c_str(); + return out.str(); +} + void WallBase::setArea(double a) { m_area = a; m_surf[0].setArea(a);