Skip to content

Commit e24805d

Browse files
committed
[Reactor] finalize and add unit tests
1 parent 4fe2aa6 commit e24805d

File tree

5 files changed

+90
-27
lines changed

5 files changed

+90
-27
lines changed

include/cantera/zeroD/ReactorNet.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ class ReactorNet : public FuncEval
3131
//! @name Methods to set up a simulation.
3232
//@{
3333

34-
3534
//! Set initial time. Default = 0.0 s. Restarts integration from this time
3635
//! using the current mixture state as the initial condition.
3736
void setInitialTime(double time);

interfaces/cython/cantera/_cantera.pxd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera":
620620
cdef cppclass CxxReactorNet "Cantera::ReactorNet":
621621
CxxReactorNet()
622622
void addReactor(CxxReactor&)
623-
string toYAML()
623+
string toYAML() except +translate_exception
624624
double advance(double, cbool) except +translate_exception
625625
double step() except +translate_exception
626626
void initialize() except +translate_exception

interfaces/cython/cantera/reactor.pyx

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ import numbers as _numbers
77

88
_reactor_counts = _defaultdict(int)
99

10+
11+
def _unique_name(name, class_type):
12+
"""Generate a unique name."""
13+
14+
if name is not None:
15+
return name
16+
else:
17+
_reactor_counts[class_type] += 1
18+
n = _reactor_counts[class_type]
19+
return '{0}_{1}'.format(class_type, n)
20+
21+
1022
# Need a pure-python class to store weakrefs to
1123
class _WeakrefProxy:
1224
pass
@@ -28,12 +40,7 @@ cdef class ReactorBase:
2840
if isinstance(contents, ThermoPhase):
2941
self.insert(contents)
3042

31-
if name is not None:
32-
self.name = name
33-
else:
34-
_reactor_counts[self.reactor_type] += 1
35-
n = _reactor_counts[self.reactor_type]
36-
self.name = '{0}_{1}'.format(self.reactor_type, n)
43+
self.name = _unique_name(name, self.type)
3744

3845
if volume is not None:
3946
self.volume = volume
@@ -419,12 +426,7 @@ cdef class ReactorSurface:
419426
if A is not None:
420427
self.area = A
421428

422-
if name is not None:
423-
self.name = name
424-
else:
425-
_reactor_counts[self.__class__.__name__] += 1
426-
n = _reactor_counts[self.__class__.__name__]
427-
self.name = '{0}_{1}'.format(self.__class__.__name__, n)
429+
self.name = _unique_name(name, self.type)
428430

429431
property type:
430432
"""The type of the reactor."""
@@ -537,12 +539,7 @@ cdef class WallBase:
537539

538540
self._install(left, right)
539541

540-
if name is not None:
541-
self.name = name
542-
else:
543-
_reactor_counts[self.__class__.__name__] += 1
544-
n = _reactor_counts[self.__class__.__name__]
545-
self.name = '{0}_{1}'.format(self.__class__.__name__, n)
542+
self.name = _unique_name(name, self.type)
546543

547544
if A is not None:
548545
self.area = A
@@ -720,12 +717,7 @@ cdef class FlowDevice:
720717
assert self.dev != NULL
721718
self._rate_func = None
722719

723-
if name is not None:
724-
self.name = name
725-
else:
726-
_reactor_counts[self.__class__.__name__] += 1
727-
n = _reactor_counts[self.__class__.__name__]
728-
self.name = '{0}_{1}'.format(self.__class__.__name__, n)
720+
self.name = _unique_name(name, self.type)
729721

730722
self._install(upstream, downstream)
731723

@@ -1062,9 +1054,19 @@ cdef class ReactorNet:
10621054
self.net.addReactor(deref(r.reactor))
10631055

10641056
def to_yaml(self):
1065-
"""Return a YAML representation of the ReactorNet setup."""
1057+
"""
1058+
Return a YAML representation of the ReactorNet structure. To
1059+
print the structure to the terminal, simply call the ReactorNet object.
1060+
The following two statements are equivalent for the sim object::
1061+
1062+
>>> sim()
1063+
>>> print(sim.to_yaml())
1064+
"""
10661065
return pystr(self.net.toYAML())
10671066

1067+
def __call__(self):
1068+
print(self.to_yaml())
1069+
10681070
def advance(self, double t, pybool apply_limit=True):
10691071
"""
10701072
Advance the state of the reactor network in time from the current time

interfaces/cython/cantera/test/test_reactor.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import numpy as np
77
from .utilities import unittest
8+
from ruamel.yaml import YAML
89

910
import cantera as ct
1011
from . import utilities
@@ -391,6 +392,43 @@ def v(t):
391392
self.assertNear(self.r1.volume, V1 + 1.0 * A, 1e-7)
392393
self.assertNear(self.r2.volume, V2 - 1.0 * A, 1e-7)
393394

395+
def test_yaml(self, **kwargs):
396+
self.make_reactors()
397+
self.add_wall()
398+
reservoir = ct.Reservoir(self.gas1)
399+
mfc = ct.MassFlowController(reservoir, self.r1)
400+
401+
self.assertTrue(isinstance(self.net.to_yaml(), str))
402+
self.assertTrue(isinstance(self.r1.to_yaml(), str))
403+
self.assertTrue(isinstance(self.r2.to_yaml(), str))
404+
self.assertTrue(isinstance(self.w.to_yaml(), str))
405+
self.assertTrue(isinstance(reservoir.to_yaml(), str))
406+
self.assertTrue(isinstance(mfc.to_yaml(), str))
407+
408+
yaml = YAML()
409+
yml = yaml.load(self.net.to_yaml())
410+
self.assertTrue('ReactorNet' in yml)
411+
net = yml['ReactorNet']
412+
413+
self.assertTrue('ReactorBase' in net)
414+
reactors = [tuple(n)[0] for n in net['ReactorBase']]
415+
self.assertTrue(self.r1.name in reactors)
416+
self.assertTrue(self.r2.name in reactors)
417+
self.assertTrue(reservoir.name in reactors)
418+
419+
self.assertTrue('WallBase' in net)
420+
walls = [tuple(n)[0] for n in net['WallBase']]
421+
self.assertTrue(self.w.name in walls)
422+
423+
self.assertTrue('FlowDevice' in net)
424+
devices = [tuple(n)[0] for n in net['FlowDevice']]
425+
self.assertTrue(mfc.name in devices)
426+
427+
self.r2.name = self.r1.name
428+
with self.assertRaisesRegex(ct.CanteraError, 'names are not unique'):
429+
# reactor names need to be unique
430+
self.net.to_yaml()
431+
394432
def test_disable_energy(self):
395433
self.make_reactors(T1=500)
396434
self.r1.energy_enabled = False

src/zeroD/ReactorNet.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ std::string ReactorNet::toYAML() const
4242
{
4343
YAML::Emitter yml;
4444
std::stringstream out;
45+
std::map<std::string, std::string> names;
4546

4647
// components: maps use generic pointers
4748
std::map<std::string, ReactorBase* > reactors;
@@ -109,39 +110,62 @@ std::string ReactorNet::toYAML() const
109110
yml << YAML::Key << "ReactorBase";
110111
yml << YAML::Value << YAML::BeginSeq;
111112
for (const auto& r : reactors) {
113+
names.emplace(r.second->name(), r.first);
112114
yml << YAML::Load(r.second->toYAML());
113115
}
114116
yml << YAML::EndSeq;
117+
if (names.size()!=reactors.size()) {
118+
// this should raise a warning, but an applicable warning system is not in place
119+
throw CanteraError("ReactorNet::toYAML", "ReactorBase names are not unique.");
120+
}
115121

116122
// emit list of walls
123+
names.clear();
117124
if (walls.size()) {
118125
yml << YAML::Key << "WallBase";
119126
yml << YAML::Value << YAML::BeginSeq;
120127
for (const auto& w : walls) {
128+
names.emplace(w.second->name(), w.first);
121129
yml << YAML::Load(w.second->toYAML());
122130
}
123131
yml << YAML::EndSeq;
124132
}
133+
if (names.size()!=walls.size()) {
134+
// this should raise a warning, but an applicable warning system is not in place
135+
throw CanteraError("ReactorNet::toYAML", "Wall names are not unique.");
136+
}
125137

126138
// emit list of flow devices
139+
names.clear();
127140
if (devices.size()) {
128141
yml << YAML::Key << "FlowDevice";
129142
yml << YAML::Value << YAML::BeginSeq;
130143
for (const auto& d : devices) {
144+
names.emplace(d.second->name(), d.first);
131145
yml << YAML::Load(d.second->toYAML());
132146
}
133147
yml << YAML::EndSeq;
134148
}
149+
if (names.size()!=devices.size()) {
150+
// this should raise a warning, but an applicable warning system is not in place
151+
throw CanteraError("ReactorNet::toYAML", "FlowDevice names are not unique.");
152+
}
135153

136154
// emit list of reactor surfaces
155+
names.clear();
137156
if (surfaces.size()) {
138157
yml << YAML::Key << "ReactorSurface";
139158
yml << YAML::Value << YAML::BeginSeq;
140159
for (const auto& s : surfaces) {
160+
names.emplace(s.second->name(), s.first);
141161
yml << YAML::Load(s.second->toYAML());
142162
}
143163
yml << YAML::EndSeq;
144164
}
165+
if (names.size()!=surfaces.size()) {
166+
// this should raise a warning, but an applicable warning system is not in place
167+
throw CanteraError("ReactorNet::toYAML", "ReactorSurface names are not unique.");
168+
}
145169

146170
// close out
147171
yml << YAML::EndMap;

0 commit comments

Comments
 (0)