diff --git a/include/cantera/zeroD/FlowDevice.h b/include/cantera/zeroD/FlowDevice.h index 999bb9c77e0..ec9cf419d81 100644 --- a/include/cantera/zeroD/FlowDevice.h +++ b/include/cantera/zeroD/FlowDevice.h @@ -53,6 +53,9 @@ class FlowDevice return m_type; } + //! Generate self-documenting YAML string. + virtual std::string toYAML() const; + //! Mass flow rate (kg/s). double massFlowRate(double time = -999.0) { if (time != -999.0) { @@ -89,7 +92,7 @@ class FlowDevice } //! Return a const reference to the downstream reactor. - const ReactorBase& out() const { + ReactorBase& out() const { return *m_out; } diff --git a/include/cantera/zeroD/ReactorBase.h b/include/cantera/zeroD/ReactorBase.h index 8a8497fdf95..21ccad5a728 100644 --- a/include/cantera/zeroD/ReactorBase.h +++ b/include/cantera/zeroD/ReactorBase.h @@ -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"); diff --git a/include/cantera/zeroD/ReactorNet.h b/include/cantera/zeroD/ReactorNet.h index 4393f45c559..aa1260586bb 100644 --- a/include/cantera/zeroD/ReactorNet.h +++ b/include/cantera/zeroD/ReactorNet.h @@ -31,6 +31,7 @@ class ReactorNet : public FuncEval //! @name Methods to set up a simulation. //@{ + //! Set initial time. Default = 0.0 s. Restarts integration from this time //! using the current mixture state as the initial condition. void setInitialTime(double time); @@ -97,6 +98,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 bfaaebf2426..d79b71ec110 100644 --- a/include/cantera/zeroD/ReactorSurface.h +++ b/include/cantera/zeroD/ReactorSurface.h @@ -22,6 +22,9 @@ class ReactorSurface ReactorSurface(const ReactorSurface&) = delete; ReactorSurface& operator=(const ReactorSurface&) = delete; + //! Generate self-documenting YAML string. + virtual std::string toYAML() const; + //! Returns the surface area [m^2] double area() const; diff --git a/include/cantera/zeroD/Wall.h b/include/cantera/zeroD/Wall.h index a7619c9bbbb..f3386abc5dd 100644 --- a/include/cantera/zeroD/Wall.h +++ b/include/cantera/zeroD/Wall.h @@ -40,6 +40,9 @@ class WallBase return "WallBase"; } + //! 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 @@ -72,9 +75,9 @@ class WallBase * @deprecated To be removed after Cantera 2.5. */ double getArea() const { - warn_deprecated("WallBase::getArea()", + warn_deprecated("WallBase::getArea()", "To be removed after Cantera 2.5. " - "Replace with WallBase::area()."); + "Replace with WallBase::area()."); return m_area; } @@ -95,7 +98,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; } @@ -216,7 +219,7 @@ class Wall : public WallBase //! Heat flux function Func1* m_qf; }; - + } #endif diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 26eff9b62e4..5a1ca4ed232 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -505,6 +505,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 @@ -541,6 +542,7 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera": cdef cppclass CxxWallBase "Cantera::WallBase": CxxWallBase() string type() + string toYAML() cbool install(CxxReactorBase&, CxxReactorBase&) double area() void setArea(double) @@ -569,6 +571,7 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera": cdef cppclass CxxReactorSurface "Cantera::ReactorSurface": CxxReactorSurface() + string toYAML() double area() void setArea(double) void setKinetics(CxxKinetics*) @@ -583,6 +586,7 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera": cdef cppclass CxxFlowDevice "Cantera::FlowDevice": CxxFlowDevice() string typeStr() + string toYAML() double massFlowRate(double) except +translate_exception cbool install(CxxReactorBase&, CxxReactorBase&) except +translate_exception void setPressureFunction(CxxFunc1*) except +translate_exception @@ -609,6 +613,7 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera": cdef cppclass CxxReactorNet "Cantera::ReactorNet": CxxReactorNet() void addReactor(CxxReactor&) + string toYAML() double advance(double, cbool) except +translate_exception double step() except +translate_exception void initialize() except +translate_exception diff --git a/interfaces/cython/cantera/reactor.pyx b/interfaces/cython/cantera/reactor.pyx index c3555828196..a0be40af997 100644 --- a/interfaces/cython/cantera/reactor.pyx +++ b/interfaces/cython/cantera/reactor.pyx @@ -63,6 +63,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 @@ -414,6 +418,10 @@ cdef class ReactorSurface: if A is not None: self.area = A + 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) @@ -542,6 +550,10 @@ cdef class WallBase: def __get__(self): return pystr(self.wall.type()) + 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): @@ -695,6 +707,10 @@ cdef class FlowDevice: def __get__(self): return pystr(self.dev.typeStr()) + 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* @@ -761,11 +777,11 @@ cdef class MassFlowController(FlowDevice): .. math:: \dot m = \max(\dot m_0*g(t), 0.), - where :math:`\dot m_0` is a constant value and :math:`g(t)` is a function of + where :math:`\dot m_0` is a constant value and :math:`g(t)` is a function of time. Both :math:`\dot m_0` and :math:`g(t)` can be set individually by - the property `mass_flow_coeff` and the method `set_time_function`, - respectively. The method `set_mass_flow_rate` combines the former - into a single function. Note that if :math:`\dot m_0*g(t) < 0`, the mass flow + the property `mass_flow_coeff` and the method `set_time_function`, + respectively. The method `set_mass_flow_rate` combines the former + into a single function. Note that if :math:`\dot m_0*g(t) < 0`, the mass flow rate will be set to zero, since reversal of the flow direction is not allowed. Unlike a real mass flow controller, a MassFlowController object will @@ -805,7 +821,7 @@ cdef class MassFlowController(FlowDevice): Set the mass flow rate [kg/s] through this controller to be either a constant or an arbitrary function of time. See `Func1`. - Note that depending on the argument type, this method either changes + Note that depending on the argument type, this method either changes the property `mass_flow_coeff` or calls the `set_time_function` method. >>> mfc.set_mass_flow_rate(0.3) @@ -936,7 +952,7 @@ cdef class PressureController(FlowDevice): .. math:: \dot m = \dot m_{\rm master} + K_v*f(P_1 - P_2) - where :math:`f` is the arbitrary function of a single argument. + where :math:`f` is the arbitrary function of a single argument. """ flowdevice_type = "PressureController" @@ -1007,6 +1023,10 @@ cdef class ReactorNet: self._reactors.append(r) self.net.addReactor(deref(r.reactor)) + def to_yaml(self): + """Return a YAML representation of the ReactorNet setup.""" + return pystr(self.net.toYAML()) + 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/src/zeroD/FlowDevice.cpp b/src/zeroD/FlowDevice.cpp index fa428c953e0..6ee6839f00e 100644 --- a/src/zeroD/FlowDevice.cpp +++ b/src/zeroD/FlowDevice.cpp @@ -6,6 +6,7 @@ #include "cantera/zeroD/FlowDevice.h" #include "cantera/zeroD/ReactorBase.h" #include "cantera/numerics/Func1.h" +#include "cantera/base/yaml.h" namespace Cantera { @@ -46,6 +47,25 @@ bool FlowDevice::install(ReactorBase& in, ReactorBase& out) return true; } +std::string FlowDevice::toYAML() const +{ + YAML::Emitter yml; + std::stringstream out; + + // object is not aware of its unique identifier + yml << YAML::BeginMap; + yml << YAML::Key << "type"; + yml << YAML::Value << typeStr(); + yml << YAML::Key << "in"; + yml << YAML::Value << m_in->name(); + yml << YAML::Key << "out"; + yml << YAML::Value << m_out->name(); + yml << YAML::EndMap; + + out << yml.c_str(); + return out.str(); +} + void FlowDevice::setPressureFunction(Func1* f) { m_pfunc = f; diff --git a/src/zeroD/ReactorBase.cpp b/src/zeroD/ReactorBase.cpp index 566250e457d..2db8a704c0e 100644 --- a/src/zeroD/ReactorBase.cpp +++ b/src/zeroD/ReactorBase.cpp @@ -7,6 +7,7 @@ #include "cantera/zeroD/FlowDevice.h" #include "cantera/zeroD/ReactorNet.h" #include "cantera/zeroD/ReactorSurface.h" +#include "cantera/base/yaml.h" using namespace std; namespace Cantera @@ -44,6 +45,25 @@ 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 << "thermo"; + yml << YAML::Value << m_thermo->name(); + 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 256c1f9ebb1..9b2c4bac2ad 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,161 @@ ReactorNet::ReactorNet() : m_integ->setProblemType(DENSE + NOJAC); } +std::string ReactorNet::toYAML() const +{ + YAML::Emitter yml; + yml.SetIndent(1); + std::stringstream out; + + // components; using generic pointers + std::map reactors; + std::map phases; + std::map walls; + std::map devices; + std::map surfaces; + + // construct complete maps + for (auto r=m_reactors.begin(); r!=m_reactors.end(); r++) { + + ReactorBase* rb = *r; + + // insert reactor (emplace ensures that it is unique) + reactors.emplace(rb->name(), rb); + + // walls + for (size_t i=0; inWalls(); i++) { + WallBase& wall = rb->wall(i); + ReactorBase& rl = wall.left(); + reactors.emplace(rl.name(), &rl); + ReactorBase& rr = wall.right(); + reactors.emplace(rr.name(), &rr); + + // workaround: need to uniquely identify wall + bool old = false; + for (const auto& w : walls) { + old |= (&wall == w.second); + } + if (!old) { + std::stringstream name; + name << wall.type() << "#" << walls.size() + 1; + walls.emplace(name.str(), &wall); + } + } + + // inlets + for (size_t i=0; inInlets(); i++) { + FlowDevice& inlet = rb->inlet(i); + ReactorBase& in = inlet.in(); + reactors.emplace(in.name(), &in); + + // workaround: need to uniquely identify inlet + bool old = false; + for (const auto& d : devices) { + old |= (&inlet == d.second); + } + if (!old) { + std::stringstream name; + name << inlet.typeStr() << "#" << devices.size() + 1; + devices.emplace(name.str(), &inlet); + } + } + + // outlets + for (size_t i=0; inOutlets(); i++) { + FlowDevice& outlet = rb->outlet(i); + ReactorBase& out = outlet.out(); + reactors.emplace(out.name(), &out); + + // workaround: need to uniquely identify outlet + bool old = false; + for (const auto& d : devices) { + old |= (&outlet == d.second); + } + if (!old) { + std::stringstream name; + name << outlet.typeStr() << "#" << devices.size() + 1; + devices.emplace(name.str(), &outlet); + } + } + } + + // collect thermo information + for (const auto& r : reactors) { + // workaround: need to uniquely identify inlet + thermo_t& contents = r.second->contents(); + bool old = false; + for (const auto& p : phases) { + old |= (&contents == p.second); + } + if (!old) { + std::stringstream name; + phases.emplace(contents.name(), &contents); + } + } + + // header + yml << YAML::BeginMap; + yml << YAML::Key << "ReactorNet"; + yml << YAML::BeginMap; + + // emit list of thermo phases + yml << YAML::Key << "t_thermo"; + yml << YAML::Value << YAML::BeginSeq; + for (const auto& p : phases) { + yml << YAML::BeginMap; + yml << YAML::Key << p.first; + yml << YAML::BeginMap; + yml << YAML::Key << "type"; + yml << YAML::Value << p.second->type(); + yml << YAML::EndMap; + yml << YAML::EndMap; + } + yml << YAML::EndSeq; + + // emit list of reactors + yml << YAML::Key << "ReactorBase"; + yml << YAML::Value << YAML::BeginSeq; + for (const auto& r : reactors) { + yml << YAML::Load(r.second->toYAML()); + } + yml << YAML::EndSeq; + + // emit list of walls + if (walls.size()) { + yml << YAML::Key << "WallBase"; + yml << YAML::Value << YAML::BeginSeq; + for (const auto& w : walls) { + yml << YAML::BeginMap; + yml << YAML::Key << w.first; + yml << YAML::Load(w.second->toYAML()); + yml << YAML::EndMap; + } + yml << YAML::EndSeq; + } + + // emit list of flow devices + if (devices.size()) { + yml << YAML::Key << "FlowDevice"; + yml << YAML::Value << YAML::BeginSeq; + for (const auto& d : devices) { + yml << YAML::BeginMap; + yml << YAML::Key << d.first; + yml << YAML::Load(d.second->toYAML()); + yml << YAML::EndMap; + } + yml << YAML::EndSeq; + } + + // missing: + // ReactorSurface + + // 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 ce880847c63..e2ea7043a6a 100644 --- a/src/zeroD/ReactorSurface.cpp +++ b/src/zeroD/ReactorSurface.cpp @@ -7,6 +7,7 @@ #include "cantera/zeroD/ReactorNet.h" #include "cantera/thermo/SurfPhase.h" #include "cantera/kinetics/Kinetics.h" +#include "cantera/base/yaml.h" namespace Cantera { @@ -19,6 +20,11 @@ ReactorSurface::ReactorSurface() { } +std::string ReactorSurface::toYAML() const +{ + throw NotImplementedError("ReactorSurface::toYAML"); +} + double ReactorSurface::area() const { return m_area; diff --git a/src/zeroD/Wall.cpp b/src/zeroD/Wall.cpp index b43b793bd32..f09ba5aac2a 100644 --- a/src/zeroD/Wall.cpp +++ b/src/zeroD/Wall.cpp @@ -7,12 +7,13 @@ #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) {} - + bool WallBase::install(ReactorBase& rleft, ReactorBase& rright) { // check if wall is already installed @@ -28,6 +29,25 @@ bool WallBase::install(ReactorBase& rleft, ReactorBase& rright) return true; } +std::string WallBase::toYAML() const +{ + YAML::Emitter yml; + std::stringstream out; + + // object is not aware of its unique identifier + yml << YAML::BeginMap; + yml << YAML::Key << "type"; + yml << YAML::Value << type(); + yml << YAML::Key << "left"; + yml << YAML::Value << m_left->name(); + yml << YAML::Key << "right"; + yml << YAML::Value << m_right->name(); + yml << YAML::EndMap; + + out << yml.c_str(); + return out.str(); +} + void WallBase::setArea(double a) { m_area = a; m_surf[0].setArea(a); @@ -39,7 +59,7 @@ Wall::Wall() : WallBase(), m_k(0.0), m_rrth(0.0), m_emiss(0.0), m_vf(0), m_qf(0) double Wall::vdot(double t) { double rate = m_k * m_area * (m_left->pressure() - m_right->pressure()); - + if (m_vf) { rate += m_area * m_vf->eval(t); } @@ -55,11 +75,11 @@ double Wall::Q(double t) double tr = m_right->temperature(); q1 += m_emiss * m_area * StefanBoltz * (tl*tl*tl*tl - tr*tr*tr*tr); } - + if (m_qf) { q1 += m_area * m_qf->eval(t); } return q1; } - + }