Skip to content

Commit 76fc4be

Browse files
committed
[Reactor] Automatically clone Solution objects
1 parent defe59c commit 76fc4be

File tree

9 files changed

+64
-32
lines changed

9 files changed

+64
-32
lines changed

include/cantera/zeroD/ReactorBase.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ class ReactorBase
8484
//! ReactorBase with Solution object.
8585
void setSolution(shared_ptr<Solution> sol);
8686

87+
//! Access the Solution object used to represent the contents of this reactor.
88+
shared_ptr<Solution> solution() { return m_solution; }
89+
8790
//! @name Methods to set up a simulation
8891
//! @{
8992

interfaces/cython/cantera/reactor.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera":
3838
CxxReactorBase() except +translate_exception
3939
string type()
4040
void setSolution(shared_ptr[CxxSolution]) except +translate_exception
41+
shared_ptr[CxxSolution] solution()
4142
void restoreState() except +translate_exception
4243
void syncState() except +translate_exception
4344
double volume()

interfaces/cython/cantera/reactor.pyx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ cdef class ReactorBase:
2828
self._outlets = []
2929
self._walls = []
3030
self._surfaces = []
31-
if isinstance(contents, _SolutionBase):
32-
self._contents = contents
31+
self._contents = _wrap_Solution(self.rbase.solution())
3332

3433
if volume is not None:
3534
self.volume = volume

samples/cxx/kinetics1/kinetics1.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ int kinetics1(int np, void* p)
4545
// create a 2D array to hold the output variables,
4646
// and store the values for the initial state
4747
Array2D states(nsp+4, 1);
48-
saveSoln(0, 0.0, *(sol->thermo()), states);
48+
saveSoln(0, 0.0, *r->solution()->thermo(), states);
4949

5050
// create a container object for reactor to run the simulation
5151
ReactorNet sim(r);
@@ -56,13 +56,13 @@ int kinetics1(int np, void* p)
5656
double tm = i*dt;
5757
sim.advance(tm);
5858
cout << "time = " << tm << " s" << endl;
59-
saveSoln(tm, *(sol->thermo()), states);
59+
saveSoln(tm, *r->solution()->thermo(), states);
6060
}
6161
clock_t t1 = clock(); // save end time
6262

6363

6464
// make a CSV output file
65-
writeCsv("kin1.csv", *sol->thermo(), states);
65+
writeCsv("kin1.csv", *r->solution()->thermo(), states);
6666

6767
// print final temperature and timing data
6868
double tmm = 1.0*(t1 - t0)/CLOCKS_PER_SEC;

samples/cxx/openmp_ignition/openmp_ignition.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,14 @@ void run()
2727
// Containers for Cantera objects to be used in different. Each thread needs
2828
// to have its own set of linked Cantera objects. Multiple threads accessing
2929
// the same objects at the same time will cause errors.
30-
vector<shared_ptr<Solution>> sols;
3130
vector<shared_ptr<IdealGasConstPressureReactor>> reactors;
3231
vector<unique_ptr<ReactorNet>> nets;
3332

3433
// Create and link the Cantera objects for each thread. This step should be
35-
// done in serial
34+
// done in serial. Only one initial Solution object is needed because a copy will
35+
// be created for each new Reactor automatically.
36+
auto sol = newSolution("gri30.yaml", "gri30", "none");
3637
for (int i = 0; i < nThreads; i++) {
37-
auto sol = newSolution("gri30.yaml", "gri30", "none");
38-
sols.emplace_back(sol);
3938
reactors.emplace_back(new IdealGasConstPressureReactor(sol));
4039
nets.emplace_back(new ReactorNet(reactors.back()));
4140
}
@@ -61,7 +60,7 @@ void run()
6160
for (int i = 0; i < nPoints; i++) {
6261
// Get the Cantera objects that were initialized for this thread
6362
size_t j = omp_get_thread_num();
64-
auto gas = sols[j]->thermo();
63+
auto gas = reactors[j]->solution()->thermo();
6564
Reactor& reactor = *reactors[j];
6665
ReactorNet& net = *nets[j];
6766

src/zeroD/Reactor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ namespace Cantera
2525
Reactor::Reactor(shared_ptr<Solution> sol, const string& name)
2626
: ReactorBase(sol, name)
2727
{
28-
m_kin = sol->kinetics().get();
28+
m_kin = m_solution->kinetics().get();
2929
if (m_kin->nReactions() == 0) {
3030
setChemistry(false);
3131
} else {

src/zeroD/ReactorBase.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ ReactorBase::ReactorBase(shared_ptr<Solution> sol, const string& name)
2424
throw CanteraError("ReactorBase::ReactorBase",
2525
"Missing or incomplete Solution object.");
2626
}
27-
m_solution = sol;
27+
m_solution = sol->clone({}, true, false);
2828
m_solution->thermo()->addSpeciesLock();
29-
setThermo(*sol->thermo());
29+
setThermo(*m_solution->thermo());
3030
}
3131

3232
ReactorBase::~ReactorBase()
@@ -63,9 +63,9 @@ void ReactorBase::setSolution(shared_ptr<Solution> sol)
6363
}
6464
m_solution = sol;
6565
m_solution->thermo()->addSpeciesLock();
66-
setThermo(*sol->thermo());
66+
setThermo(*m_solution->thermo());
6767
try {
68-
setKinetics(*sol->kinetics());
68+
setKinetics(*m_solution->kinetics());
6969
} catch (NotImplementedError&) {
7070
// kinetics not used (example: Reservoir)
7171
}

src/zeroD/ReactorSurface.cpp

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,35 @@ namespace Cantera
1616
ReactorSurface::ReactorSurface(shared_ptr<Solution> soln,
1717
const vector<shared_ptr<ReactorBase>>& reactors,
1818
const string& name)
19-
: ReactorSurface(soln, name)
19+
: ReactorBase(name)
2020
{
21-
for (auto& r : reactors) {
22-
r->addSurface(this);
23-
m_reactors.push_back(r.get());
21+
vector<shared_ptr<Solution>> adjacent;
22+
for (auto R : reactors) {
23+
adjacent.push_back(R->solution());
24+
m_reactors.push_back(R.get());
25+
R->addSurface(this);
2426
}
27+
m_solution = soln->clone(adjacent, true, false);
28+
m_solution->thermo()->addSpeciesLock();
29+
setThermo(*m_solution->thermo());
30+
if (!std::dynamic_pointer_cast<SurfPhase>(soln->thermo())) {
31+
throw CanteraError("ReactorSurface::ReactorSurface",
32+
"Solution object must have a SurfPhase object as the thermo manager.");
33+
}
34+
35+
if (!soln->kinetics() ) {
36+
throw CanteraError("ReactorSurface::ReactorSurface",
37+
"Solution object must have kinetics manager.");
38+
} else if (!std::dynamic_pointer_cast<InterfaceKinetics>(soln->kinetics())) {
39+
throw CanteraError("ReactorSurface::ReactorSurface",
40+
"Kinetics manager must be an InterfaceKinetics object.");
41+
}
42+
// todo: move all member variables to use shared pointers after Cantera 3.2
43+
m_kinetics = m_solution->kinetics().get();
44+
m_thermo = m_solution->thermo().get();
45+
m_surf = dynamic_cast<SurfPhase*>(m_thermo);
46+
m_cov.resize(m_surf->nSpecies());
47+
m_surf->getCoverages(m_cov.data());
2548
}
2649

2750
ReactorSurface::ReactorSurface(shared_ptr<Solution> sol, const string& name)

test/python/test_reactor.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ def test_equalize_pressure(self):
259259
self.net.advance(1.0)
260260

261261
assert self.net.time == approx(1.0)
262-
assert self.gas1.P == approx(self.gas2.P)
262+
assert self.r1.thermo.P == approx(self.r2.thermo.P)
263263
assert self.r1.T != approx(self.r2.T)
264264

265265
def test_tolerances(self, rtol_lim=1e-10, atol_lim=1e-20):
@@ -1203,7 +1203,7 @@ def test_nonreacting(self):
12031203
mdot_o = 5.0
12041204
T0 = 900.0
12051205
self.setup_reactor(T0, 10*ct.one_atm, mdot_f, mdot_o)
1206-
self.gas.set_multiplier(0.0)
1206+
self.combustor.thermo.set_multiplier(0.0)
12071207
t,T = self.integrate(100.0)
12081208

12091209
for i in range(len(t)):
@@ -1406,7 +1406,7 @@ def test_with_surface_reactions(self):
14061406
self.net1.rtol = self.net2.rtol = 1e-9
14071407
self.integrate(surf=True)
14081408

1409-
def test_surf_install_deprecated(self):
1409+
def test_surf_install_deprecated(self, allow_deprecated):
14101410
self.create_reactors(add_surf=True, use_surf_install=True)
14111411
self.net1.atol = self.net2.atol = 1e-18
14121412
self.net1.rtol = self.net2.rtol = 1e-9
@@ -1642,9 +1642,10 @@ def update(self, gas):
16421642

16431643
@ct.extension(name="fail-rate", data=FailRateData)
16441644
class FailRate(ct.ExtensibleRate):
1645-
def __init__(self, *args, recoverable, **kwargs):
1645+
def __init__(self, *args, recoverable=None, **kwargs):
16461646
super().__init__(*args, **kwargs)
1647-
self.recoverable = recoverable
1647+
if recoverable is not None:
1648+
self.recoverable = recoverable
16481649
self.count = 0
16491650

16501651
def eval(self, data):
@@ -1654,6 +1655,12 @@ def eval(self, data):
16541655
raise ValueError("spam")
16551656
return 0.0
16561657

1658+
def get_parameters(self, params):
1659+
params["recoverable"] = self.recoverable
1660+
1661+
def set_parameters(self, params, rate_coeff_units):
1662+
self.recoverable = params["recoverable"]
1663+
16571664

16581665
class TestFlowReactor:
16591666
gas_def = """
@@ -1859,7 +1866,7 @@ def test_recoverable_integrator_errors(self):
18591866
sim.step()
18601867

18611868
# At least some "recoverable" errors occurred
1862-
assert fail.rate.count > 0
1869+
assert r.thermo.reaction(gas.n_reactions - 1).rate.count > 0
18631870

18641871
def test_max_steps(self):
18651872
surf, gas = self.import_phases()
@@ -1982,8 +1989,8 @@ def test_reinitialization(self):
19821989
cov1 = rsurf.kinetics.coverages
19831990

19841991
# Reset the reactor to the same initial state
1985-
gas.TPX = 1700, 4000, 'NH3:1.0, SiF4:0.4'
1986-
surf.TP = gas.TP
1992+
r.thermo.TPX = 1700, 4000, 'NH3:1.0, SiF4:0.4'
1993+
surf.TP = 1700, 4000
19871994
r.mass_flow_rate = 0.01
19881995
r.syncState()
19891996

@@ -2354,7 +2361,7 @@ def integrate(r, net):
23542361

23552362
rA, rB, surfX, surfY, net = setup(order)
23562363
for (obj,k) in [(surfY,2), (surfX,2), (rB,18),
2357-
(surfX,0), (rB,2)]:
2364+
(surfY,0), (rB,2)]:
23582365
obj.add_sensitivity_reaction(k)
23592366

23602367
integrate(rB, net)
@@ -2963,7 +2970,7 @@ def after_eval(self,t,LHS,RHS):
29632970
# compare heat added (add_heat) to the equivalent energy contained by the solid
29642971
# and gaseous mass in the reactor
29652972
r1_heat = (mass_lump * cp_lump * (r1.thermo.T - 500) +
2966-
mass_gas * (self.gas.enthalpy_mass - gas_initial_enthalpy))
2973+
mass_gas * (r1.thermo.enthalpy_mass - gas_initial_enthalpy))
29672974
add_heat = Q * time
29682975
assert add_heat == approx(r1_heat, abs=1e-5)
29692976

@@ -3051,9 +3058,9 @@ def replace_update_surface_state(self, y):
30513058
Hweight = ct.Element("H").weight
30523059
total_sites = rsurf.area * surf.site_density
30533060
def masses():
3054-
mass_H = (gas.elemental_mass_fraction("H") * r1.mass +
3061+
mass_H = (r1.thermo.elemental_mass_fraction("H") * r1.mass +
30553062
total_sites * r1.surfaces[0].kinetics["H(s)"].X * Hweight)
3056-
mass_O = gas.elemental_mass_fraction("O") * r1.mass
3063+
mass_O = r1.thermo.elemental_mass_fraction("O") * r1.mass
30573064
return mass_H, mass_O
30583065

30593066
net.step()
@@ -3263,11 +3270,11 @@ def test_jacobian(self):
32633270

32643271
upstream = ct.Reservoir(gas)
32653272
gas.equilibrate("HP")
3266-
gas.set_multiplier(0.0)
32673273
downstream = ct.Reservoir(gas)
32683274
V0 = 1e-3
32693275
mdot = 120
32703276
r = ct.MoleReactor(gas, volume=V0)
3277+
r.thermo.set_multiplier(0.0)
32713278
inlet = ct.MassFlowController(upstream, r, mdot=mdot)
32723279
ct.MassFlowController(r, downstream, mdot=mdot)
32733280
net = ct.ReactorNet([r])

0 commit comments

Comments
 (0)