diff --git a/include/cantera/zeroD/ConstPressureReactor.h b/include/cantera/zeroD/ConstPressureReactor.h index 445344434..4c5c896b3 100644 --- a/include/cantera/zeroD/ConstPressureReactor.h +++ b/include/cantera/zeroD/ConstPressureReactor.h @@ -43,6 +43,7 @@ public: //! the name of a homogeneous phase species, or the name of a surface //! species. virtual size_t componentIndex(const std::string& nm) const; + std::string componentName(size_t k); }; } diff --git a/include/cantera/zeroD/IdealGasConstPressureReactor.h b/include/cantera/zeroD/IdealGasConstPressureReactor.h index 76f0accac..bdca2b36c 100644 --- a/include/cantera/zeroD/IdealGasConstPressureReactor.h +++ b/include/cantera/zeroD/IdealGasConstPressureReactor.h @@ -44,6 +44,7 @@ public: //! "temperature", the name of a homogeneous phase species, or the name of a //! surface species. virtual size_t componentIndex(const std::string& nm) const; + std::string componentName(size_t k); protected: vector_fp m_hk; //!< Species molar enthalpies diff --git a/include/cantera/zeroD/IdealGasReactor.h b/include/cantera/zeroD/IdealGasReactor.h index ca1f0f747..babd7518c 100644 --- a/include/cantera/zeroD/IdealGasReactor.h +++ b/include/cantera/zeroD/IdealGasReactor.h @@ -43,6 +43,7 @@ public: //! "volume", "temperature", the name of a homogeneous phase species, or the //! name of a surface species. virtual size_t componentIndex(const std::string& nm) const; + std::string componentName(size_t k); protected: vector_fp m_uk; //!< Species molar internal energies diff --git a/include/cantera/zeroD/Reactor.h b/include/cantera/zeroD/Reactor.h index cc2a43708..1c8e4f7d5 100644 --- a/include/cantera/zeroD/Reactor.h +++ b/include/cantera/zeroD/Reactor.h @@ -80,6 +80,9 @@ public: //! Number of equations (state variables) for this reactor virtual size_t neq() { + if (!m_nv) { + initialize(); + } return m_nv; } @@ -136,6 +139,10 @@ public: //! surface species. virtual size_t componentIndex(const std::string& nm) const; + //! Return the name of the solution component with index *i*. + //! @see componentIndex() + virtual std::string componentName(size_t k); + protected: //! Set reaction rate multipliers based on the sensitivity variables in //! *params*. diff --git a/include/cantera/zeroD/ReactorNet.h b/include/cantera/zeroD/ReactorNet.h index 7dcb1bfab..e88a9a3c2 100644 --- a/include/cantera/zeroD/ReactorNet.h +++ b/include/cantera/zeroD/ReactorNet.h @@ -174,6 +174,11 @@ public: //! reactor network. size_t globalComponentIndex(const std::string& component, size_t reactor=0); + //! Return the name of the i-th component of the global state vector. The + //! name returned includes both the name of the reactor and the specific + //! component, e.g. `'reactor1: CH4'`. + std::string componentName(size_t i) const; + //! Used by Reactor and Wall objects to register the addition of //! sensitivity parameters so that the ReactorNet can keep track of the //! order in which sensitivity parameters are added. diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index ae2528fb9..7e04cae5f 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -487,6 +487,7 @@ cdef extern from "cantera/zeroD/Reactor.h": void setEnergy(int) cbool energyEnabled() size_t componentIndex(string&) + string componentName(size_t) except + size_t neq() void getState(double*) @@ -566,6 +567,7 @@ cdef extern from "cantera/zeroD/ReactorNet.h": void setVerbose(cbool) size_t neq() void getState(double*) + string componentName(size_t) except + void setSensitivityTolerances(double, double) double rtolSensitivity() diff --git a/interfaces/cython/cantera/reactor.pyx b/interfaces/cython/cantera/reactor.pyx index e02ad9e99..9e66284ff 100644 --- a/interfaces/cython/cantera/reactor.pyx +++ b/interfaces/cython/cantera/reactor.pyx @@ -233,9 +233,9 @@ cdef class Reactor(ReactorBase): """ Returns the index of the component named *name* in the system. This determines the (relative) index of the component in the vector of - sensitivity coefficients. *name* is either a species name or the name - of a reactor state variable, e.g. 'U', 'T', depending on the reactor's - equations. + sensitivity coefficients. *name* is either a species name or the name of + a reactor state variable, e.g. 'int_energy', 'temperature', depending on + the reactor's equations. """ k = self.reactor.componentIndex(stringify(name)) @@ -243,6 +243,14 @@ cdef class Reactor(ReactorBase): raise IndexError('No such component: {!r}'.format(name)) return k + def component_name(self, int i): + """ + Returns the name of the component with index *i* within the array of + variables returned by `get_state`. This is the inverse of + `component_index`. + """ + return pystr(self.reactor.componentName(i)) + property n_vars: """ The number of state variables in the reactor. @@ -276,8 +284,9 @@ cdef class Reactor(ReactorBase): - 1 - enthalpy or temperature - 2+ - mass fractions of the species - You can use the function `component_index` to determine the location - of a specific component + You can use the function `component_index` to determine the location of + a specific component from its name, or `component_name` to determine the + name from the index. """ if not self.n_vars: raise Exception('Reactor empty or network not initialized.') @@ -908,6 +917,14 @@ cdef class ReactorNet: def __set__(self, pybool v): self.net.setVerbose(v) + def component_name(self, int i): + """ + Return the name of the i-th component of the global state vector. The + name returned includes both the name of the reactor and the specific + component, e.g. `'reactor1: CH4'`. + """ + return pystr(self.net.componentName(i)) + def sensitivity(self, component, int p, int r=0): """ Returns the sensitivity of the solution variable *component* in diff --git a/interfaces/cython/cantera/test/test_reactor.py b/interfaces/cython/cantera/test/test_reactor.py index a34c298bc..1e4187bbc 100644 --- a/interfaces/cython/cantera/test/test_reactor.py +++ b/interfaces/cython/cantera/test/test_reactor.py @@ -83,6 +83,16 @@ class TestReactor(utilities.CanteraTest): for i, name in enumerate(self.gas1.species_names): self.assertEqual(i + N0, self.r1.component_index(name)) + def test_component_names(self): + self.make_reactors(n_reactors=2) + N = self.net.n_vars // 2 + for i in range(N): + self.assertEqual(self.r1.component_index(self.r1.component_name(i)), i) + self.assertEqual(self.net.component_name(i), + '{}: {}'.format(self.r1.name, self.r1.component_name(i))) + self.assertEqual(self.net.component_name(N+i), + '{}: {}'.format(self.r2.name, self.r2.component_name(i))) + def test_disjoint(self): T1, P1 = 300, 101325 T2, P2 = 500, 300000 @@ -757,6 +767,13 @@ class TestConstPressureReactor(utilities.CanteraTest): for i, name in enumerate(iface.species_names): self.assertEqual(i + N1, r.component_index(name)) + def test_component_names(self): + self.create_reactors(add_surf=True) + for i in range(self.net1.n_vars): + self.assertEqual(self.r1.component_index(self.r1.component_name(i)), i) + self.assertEqual(self.net1.component_name(i), + '{}: {}'.format(self.r1.name, self.r1.component_name(i))) + def integrate(self, surf=False): for t in np.arange(0.5, 50, 1.0): self.net1.advance(t) diff --git a/src/zeroD/ConstPressureReactor.cpp b/src/zeroD/ConstPressureReactor.cpp index 171a1467c..9f16a037e 100644 --- a/src/zeroD/ConstPressureReactor.cpp +++ b/src/zeroD/ConstPressureReactor.cpp @@ -4,6 +4,7 @@ #include "cantera/zeroD/ConstPressureReactor.h" #include "cantera/zeroD/FlowDevice.h" +#include "cantera/zeroD/Wall.h" using namespace std; @@ -149,4 +150,33 @@ size_t ConstPressureReactor::componentIndex(const string& nm) const } } +std::string ConstPressureReactor::componentName(size_t k) { + if (k == 0) { + return "mass"; + } else if (k == 1) { + return "enthalpy"; + } else if (k >= 2 && k < neq()) { + k -= 2; + if (k < m_thermo->nSpecies()) { + return m_thermo->speciesName(k); + } else { + k -= m_thermo->nSpecies(); + } + for (size_t m = 0; m < m_wall.size(); m++) { + Wall& w = *m_wall[m]; + if (w.kinetics(m_lr[m])) { + size_t kp = w.kinetics(m_lr[m])->reactionPhaseIndex(); + ThermoPhase& th = w.kinetics(m_lr[m])->thermo(kp); + if (k < th.nSpecies()) { + return th.speciesName(k); + } else { + k -= th.nSpecies(); + } + } + } + } + throw CanteraError("ConstPressureReactor::componentName", + "Index is out of bounds."); +} + } diff --git a/src/zeroD/IdealGasConstPressureReactor.cpp b/src/zeroD/IdealGasConstPressureReactor.cpp index 6df0b9dd9..480ceeaa2 100644 --- a/src/zeroD/IdealGasConstPressureReactor.cpp +++ b/src/zeroD/IdealGasConstPressureReactor.cpp @@ -161,4 +161,12 @@ size_t IdealGasConstPressureReactor::componentIndex(const string& nm) const } } +std::string IdealGasConstPressureReactor::componentName(size_t k) { + if (k == 1) { + return "temperature"; + } else { + return ConstPressureReactor::componentName(k); + } +} + } diff --git a/src/zeroD/IdealGasReactor.cpp b/src/zeroD/IdealGasReactor.cpp index 4f7064bb8..5c2b9fa3b 100644 --- a/src/zeroD/IdealGasReactor.cpp +++ b/src/zeroD/IdealGasReactor.cpp @@ -176,4 +176,13 @@ size_t IdealGasReactor::componentIndex(const string& nm) const } } +std::string IdealGasReactor::componentName(size_t k) { + if (k == 2) { + return "temperature"; + } else { + return Reactor::componentName(k); + } +} + + } diff --git a/src/zeroD/Reactor.cpp b/src/zeroD/Reactor.cpp index 49db701a6..3917a96eb 100644 --- a/src/zeroD/Reactor.cpp +++ b/src/zeroD/Reactor.cpp @@ -398,6 +398,36 @@ size_t Reactor::componentIndex(const string& nm) const } } +std::string Reactor::componentName(size_t k) { + if (k == 0) { + return "mass"; + } else if (k == 1) { + return "volume"; + } else if (k == 2) { + return "int_energy"; + } else if (k >= 3 && k < neq()) { + k -= 3; + if (k < m_thermo->nSpecies()) { + return m_thermo->speciesName(k); + } else { + k -= m_thermo->nSpecies(); + } + for (size_t m = 0; m < m_wall.size(); m++) { + Wall& w = *m_wall[m]; + if (w.kinetics(m_lr[m])) { + size_t kp = w.kinetics(m_lr[m])->reactionPhaseIndex(); + ThermoPhase& th = w.kinetics(m_lr[m])->thermo(kp); + if (k < th.nSpecies()) { + return th.speciesName(k); + } else { + k -= th.nSpecies(); + } + } + } + } + throw CanteraError("Reactor::componentName", "Index is out of bounds."); +} + void Reactor::applySensitivity(double* params) { if (!params) { diff --git a/src/zeroD/ReactorNet.cpp b/src/zeroD/ReactorNet.cpp index adc790f66..f7c78bfe9 100644 --- a/src/zeroD/ReactorNet.cpp +++ b/src/zeroD/ReactorNet.cpp @@ -242,6 +242,18 @@ size_t ReactorNet::globalComponentIndex(const string& component, size_t reactor) return m_start[reactor] + m_reactors[reactor]->componentIndex(component); } +std::string ReactorNet::componentName(size_t i) const +{ + for (auto r : m_reactors) { + if (i < r->neq()) { + return r->name() + ": " + r->componentName(i); + } else { + i -= r->neq(); + } + } + throw CanteraError("ReactorNet::componentName", "Index out of bounds"); +} + size_t ReactorNet::registerSensitivityParameter( const std::string& name, double value, double scale) {