Cantera  4.0.0a1
Loading...
Searching...
No Matches
MoleReactor.cpp
Go to the documentation of this file.
1//! @file MoleReactor.cpp A zero-dimensional reactor with a moles as the
2//! state
3
4// This file is part of Cantera. See License.txt in the top-level directory or
5// at https://cantera.org/license.txt for license and copyright information.
6
13#include <boost/math/tools/roots.hpp>
14#include <numeric>
15
16using namespace std;
17namespace bmt = boost::math::tools;
18
19namespace Cantera
20{
21
22MoleReactor::MoleReactor(shared_ptr<Solution> sol, const string& name)
23 : MoleReactor(sol, true, name)
24{
25}
26
27MoleReactor::MoleReactor(shared_ptr<Solution> sol, bool clone, const string& name)
28 : Reactor(sol, clone, name)
29{
30 m_nv = 2 + m_nsp; // internal energy, volume, and moles of each species
31}
32
33void MoleReactor::getMoles(span<double> y)
34{
35 // Use inverse molecular weights to convert to moles
36 auto Y = m_thermo->massFractions();
37 auto imw = m_thermo->inverseMolecularWeights();
38 for (size_t i = 0; i < m_nsp; i++) {
39 y[i] = m_mass * imw[i] * Y[i];
40 }
41}
42
43void MoleReactor::setMassFromMoles(span<const double> y)
44{
45 auto mw = m_thermo->molecularWeights();
46 // calculate mass from moles
47 m_mass = 0;
48 for (size_t i = 0; i < m_nsp; i++) {
49 m_mass += y[i] * mw[i];
50 }
51}
52
53void MoleReactor::getState(span<double> y)
54{
55 // set the first component to the internal energy
56 m_mass = m_thermo->density() * m_vol;
57 y[0] = m_thermo->intEnergy_mass() * m_mass;
58 // set the second component to the total volume
59 y[1] = m_vol;
60 // set components y+2 ... y+K+2 to the moles of each species
61 getMoles(y.subspan(m_sidx));
62}
63
64void MoleReactor::updateDefaultTolerances(span<double> atol, double baseAtol)
65{
66 vector<double> y(neq());
67 getState(y);
68 size_t sidx = speciesOffset();
69 double totalMoles = std::accumulate(y.begin() + sidx,
70 y.begin() + sidx + m_nsp, 0.0);
71 if (totalMoles <= 0.0) {
72 return;
73 }
74 std::fill(atol.begin() + sidx, atol.begin() + sidx + m_nsp,
75 baseAtol * totalMoles);
76}
77
78void MoleReactor::updateState(span<const double> y)
79{
80 // The components of y are [0] total internal energy, [1] the total volume, and
81 // [2...K+3] are the moles of each species, and [K+3...] are the moles
82 // of surface species on each wall.
83 setMassFromMoles(y.subspan(m_sidx));
84 m_vol = y[1];
85 m_thermo->setMolesNoTruncate(y.subspan(m_sidx, m_nsp));
86 if (m_energy) {
87 double U = y[0];
88 // Residual function: error in internal energy as a function of T
89 auto u_err = [this, U](double T) {
90 m_thermo->setState_TD(T, m_mass / m_vol);
91 return m_thermo->intEnergy_mass() * m_mass - U;
92 };
93 double T = m_thermo->temperature();
94 boost::uintmax_t maxiter = 100;
95 pair<double, double> TT;
96 try {
97 TT = bmt::bracket_and_solve_root(
98 u_err, T, 1.2, true, bmt::eps_tolerance<double>(48), maxiter);
99 } catch (std::exception&) {
100 // Try full-range bisection if bracketing fails (for example, near
101 // temperature limits for the phase's equation of state)
102 try {
103 TT = bmt::bisect(u_err, m_thermo->minTemp(), m_thermo->maxTemp(),
104 bmt::eps_tolerance<double>(48), maxiter);
105 } catch (std::exception& err2) {
106 // Set m_thermo back to a reasonable state if root finding fails
107 m_thermo->setState_TD(T, m_mass / m_vol);
108 throw CanteraError("MoleReactor::updateState",
109 "{}\nat U = {}, rho = {}", err2.what(), U, m_mass / m_vol);
110 }
111 }
112 if (fabs(TT.first - TT.second) > 1e-7*TT.first) {
113 throw CanteraError("MoleReactor::updateState", "root finding failed");
114 }
115 m_thermo->setState_TD(TT.second, m_mass / m_vol);
116 } else {
117 m_thermo->setDensity(m_mass / m_vol);
118 }
119 updateConnected(true);
120}
121
122void MoleReactor::eval(double time, span<double> LHS, span<double> RHS)
123{
124 auto dndt = RHS.subspan(m_sidx); // moles per time
125
126 evalWalls(time);
127 updateSurfaceProductionRates();
128 // inverse molecular weights for conversion
129 auto imw = m_thermo->inverseMolecularWeights();
130 // volume equation
131 RHS[1] = m_vdot;
132
133 if (m_chem) {
134 m_kin->getNetProductionRates(m_wdot); // "omega dot"
135 }
136
137 // Energy equation.
138 // @f[
139 // \dot U = -P\dot V + A \dot q + \dot m_{in} h_{in} - \dot m_{out} h.
140 // @f]
141 if (m_energy) {
142 RHS[0] = - m_thermo->pressure() * m_vdot + m_Qdot;
143 RHS[0] += m_thermo->intrinsicHeating() * m_vol;
144 } else {
145 RHS[0] = 0.0;
146 }
147
148 for (size_t k = 0; k < m_nsp; k++) {
149 // production in gas phase and from surfaces
150 dndt[k] = m_wdot[k] * m_vol + m_sdot[k];
151 }
152
153 // add terms for outlets
154 for (auto outlet : m_outlet) {
155 // flow of species into system and dilution by other species
156 for (size_t n = 0; n < m_nsp; n++) {
157 dndt[n] -= outlet->outletSpeciesMassFlowRate(n) * imw[n];
158 }
159 // energy update based on mass flow
160 double mdot = outlet->massFlowRate();
161 if (m_energy) {
162 RHS[0] -= mdot * m_enthalpy;
163 }
164 }
165
166 // add terms for inlets
167 for (auto inlet : m_inlet) {
168 double mdot = inlet->massFlowRate();
169 for (size_t n = 0; n < m_nsp; n++) {
170 // flow of species into system and dilution by other species
171 dndt[n] += inlet->outletSpeciesMassFlowRate(n) * imw[n];
172 }
173 if (m_energy) {
174 RHS[0] += mdot * inlet->enthalpy_mass();
175 }
176 }
177}
178
179
180size_t MoleReactor::componentIndex(const string& nm) const
181{
182 if (nm == "int_energy") {
183 return 0;
184 }
185 if (nm == "volume") {
186 return 1;
187 }
188 try {
189 return m_thermo->speciesIndex(nm) + m_sidx;
190 } catch (const CanteraError&) {
191 throw CanteraError("MoleReactor::componentIndex",
192 "Component '{}' not found", nm);
193 }
194}
195
196string MoleReactor::componentName(size_t k) {
197 if (k == 0) {
198 return "int_energy";
199 } else if (k == 1) {
200 return "volume";
201 } else if (k >= m_sidx && k < neq()) {
202 return m_thermo->speciesName(k - m_sidx);
203 }
204 throw IndexError("MoleReactor::componentName", "component", k, m_nv);
205}
206
207double MoleReactor::upperBound(size_t k) const {
208 // Component is either int_energy, volume, or moles of a bulk or surface species
209 return BigNumber;
210}
211
212double MoleReactor::lowerBound(size_t k) const {
213 if (k == 0) {
214 return -BigNumber; // int_energy
215 } else if (k == 1) {
216 return 0; // volume
217 } else if (k >= 2 && k < m_nv) {
218 return -Tiny; // moles of bulk or surface species
219 } else {
220 throw CanteraError("MoleReactor::lowerBound", "Index {} is out of bounds.", k);
221 }
222}
223
224void MoleReactor::resetBadValues(span<double> y) {
225 for (size_t k = m_sidx; k < m_nv; k++) {
226 y[k] = std::max(y[k], 0.0);
227 }
228}
229
230}
Base class for kinetics managers and also contains the kineticsmgr module documentation (see Kinetics...
Header file for class ReactorSurface.
Header for a simple thermodynamics model of a surface phase derived from ThermoPhase,...
Base class for exceptions thrown by Cantera classes.
An array index is out of range.
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
const double Tiny
Small number to compare differences of mole fractions against.
Definition ct_defs.h:176
const double BigNumber
largest number to compare to inf.
Definition ct_defs.h:163
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...