16#include <boost/math/tools/roots.hpp>
19namespace bmt = boost::math::tools;
34void Reactor::insert(shared_ptr<Solution> sol) {
35 setThermoMgr(*sol->thermo());
36 setKineticsMgr(*sol->kinetics());
42 if (m_kin->nReactions() == 0) {
49void Reactor::getState(
double* y)
53 "Error: reactor is empty.");
55 m_thermo->restoreState(m_state);
58 m_mass = m_thermo->density() * m_vol;
65 y[2] = m_thermo->intEnergy_mass() * m_mass;
68 m_thermo->getMassFractions(y+3);
72 getSurfaceInitialConditions(y + m_nsp + 3);
75void Reactor::getSurfaceInitialConditions(
double* y)
78 for (
auto& S : m_surfaces) {
79 S->getCoverages(y + loc);
80 loc += S->thermo()->nSpecies();
84void Reactor::initialize(doublereal t0)
86 if (!m_thermo || (m_chem && !m_kin)) {
87 throw CanteraError(
"Reactor::initialize",
"Reactor contents not set"
88 " for reactor '" + m_name +
"'.");
90 m_thermo->restoreState(m_state);
91 m_sdot.resize(m_nsp, 0.0);
92 m_wdot.resize(m_nsp, 0.0);
93 updateConnected(
true);
95 for (
size_t n = 0; n < m_wall.size(); n++) {
103 for (
auto& S : m_surfaces) {
104 m_nv_surf += S->thermo()->nSpecies();
105 size_t nt = S->kinetics()->nTotalSpecies();
106 maxnt = std::max(maxnt, nt);
109 m_work.resize(maxnt);
112size_t Reactor::nSensParams()
114 size_t ns = m_sensParams.size();
115 for (
auto& S : m_surfaces) {
116 ns += S->nSensParams();
121void Reactor::syncState()
123 ReactorBase::syncState();
124 m_mass = m_thermo->density() * m_vol;
127void Reactor::updateState(doublereal* y)
134 m_thermo->setMassFractions_NoNorm(y+3);
139 auto u_err = [
this, U](
double T) {
140 m_thermo->setState_TR(T, m_mass / m_vol);
141 return m_thermo->intEnergy_mass() * m_mass - U;
144 double T = m_thermo->temperature();
145 boost::uintmax_t maxiter = 100;
146 std::pair<double, double> TT;
148 TT = bmt::bracket_and_solve_root(
149 u_err, T, 1.2,
true, bmt::eps_tolerance<double>(48), maxiter);
150 }
catch (std::exception&) {
154 TT = bmt::bisect(u_err, m_thermo->minTemp(), m_thermo->maxTemp(),
155 bmt::eps_tolerance<double>(48), maxiter);
156 }
catch (std::exception& err2) {
158 m_thermo->setState_TR(T, m_mass / m_vol);
160 "{}\nat U = {}, rho = {}", err2.what(), U, m_mass / m_vol);
163 if (fabs(TT.first - TT.second) > 1e-7*TT.first) {
164 throw CanteraError(
"Reactor::updateState",
"root finding failed");
166 m_thermo->setState_TR(TT.second, m_mass / m_vol);
168 m_thermo->setDensity(m_mass/m_vol);
171 updateConnected(
true);
172 updateSurfaceState(y + m_nsp + 3);
175void Reactor::updateSurfaceState(
double* y)
178 for (
auto& S : m_surfaces) {
179 S->setCoverages(y+loc);
180 loc += S->thermo()->nSpecies();
184void Reactor::updateConnected(
bool updatePressure) {
186 m_enthalpy = m_thermo->enthalpy_mass();
187 if (updatePressure) {
188 m_pressure = m_thermo->pressure();
190 m_intEnergy = m_thermo->intEnergy_mass();
191 m_thermo->saveState(m_state);
194 double time = (m_net !=
nullptr) ? m_net->time() : 0.0;
195 for (
size_t i = 0; i < m_outlet.size(); i++) {
196 m_outlet[i]->updateMassFlowRate(time);
198 for (
size_t i = 0; i < m_inlet.size(); i++) {
199 m_inlet[i]->updateMassFlowRate(time);
203void Reactor::eval(
double time,
double* LHS,
double* RHS)
205 double& dmdt = RHS[0];
206 double* mdYdt = RHS + 3;
209 m_thermo->restoreState(m_state);
210 const vector_fp& mw = m_thermo->molecularWeights();
211 const doublereal* Y = m_thermo->massFractions();
213 evalSurfaces(LHS + m_nsp + 3, RHS + m_nsp + 3, m_sdot.data());
215 double mdot_surf =
dot(m_sdot.begin(), m_sdot.end(), mw.begin());
222 m_kin->getNetProductionRates(&m_wdot[0]);
225 for (
size_t k = 0; k < m_nsp; k++) {
227 mdYdt[k] = (m_wdot[k] * m_vol + m_sdot[k]) * mw[k];
229 mdYdt[k] -= Y[k] * mdot_surf;
238 RHS[2] = - m_thermo->pressure() * m_vdot + m_Qdot;
244 for (
auto outlet : m_outlet) {
245 double mdot = outlet->massFlowRate();
248 RHS[2] -= mdot * m_enthalpy;
253 for (
auto inlet : m_inlet) {
254 double mdot = inlet->massFlowRate();
256 for (
size_t n = 0; n < m_nsp; n++) {
257 double mdot_spec = inlet->outletSpeciesMassFlowRate(n);
259 mdYdt[n] += (mdot_spec - mdot * Y[n]);
262 RHS[2] += mdot * inlet->enthalpy_mass();
267void Reactor::evalWalls(
double t)
271 for (
size_t i = 0; i < m_wall.size(); i++) {
272 int f = 2 * m_lr[i] - 1;
273 m_vdot -= f * m_wall[i]->vdot(t);
274 m_Qdot += f * m_wall[i]->Q(t);
279void Reactor::evalSurfaces(
double* LHS,
double* RHS,
double* sdot)
281 fill(sdot, sdot + m_nsp, 0.0);
284 for (
auto S : m_surfaces) {
295 for (
size_t k = 1; k < nk; k++) {
297 RHS[loc + k] = m_work[surfloc + k] * rs0 * surf->
size(k);
305 double wallarea = S->area();
306 for (
size_t k = 0; k < m_nsp; k++) {
307 sdot[k] += m_work[bulkloc + k] * wallarea;
312void Reactor::addSensitivityReaction(
size_t rxn)
314 if (!m_chem || rxn >= m_kin->nReactions()) {
316 "Reaction number out of range ({})", rxn);
319 size_t p = network().registerSensitivityParameter(
320 name()+
": "+m_kin->reactionString(rxn), 1.0, 1.0);
321 m_sensParams.emplace_back(
322 SensitivityParameter{rxn, p, 1.0, SensParameterType::reaction});
325void Reactor::addSensitivitySpeciesEnthalpy(
size_t k)
327 if (k >= m_thermo->nSpecies()) {
328 throw CanteraError(
"Reactor::addSensitivitySpeciesEnthalpy",
329 "Species index out of range ({})", k);
332 size_t p = network().registerSensitivityParameter(
333 name() +
": " + m_thermo->speciesName(k) +
" enthalpy",
335 m_sensParams.emplace_back(
336 SensitivityParameter{k, p, m_thermo->Hf298SS(k),
337 SensParameterType::enthalpy});
340size_t Reactor::speciesIndex(
const string& nm)
const
343 size_t k = m_thermo->speciesIndex(nm);
349 size_t offset = m_nsp;
350 for (
auto& S : m_surfaces) {
362size_t Reactor::componentIndex(
const string& nm)
const
364 size_t k = speciesIndex(nm);
367 }
else if (nm ==
"mass") {
369 }
else if (nm ==
"volume") {
371 }
else if (nm ==
"int_energy") {
378std::string Reactor::componentName(
size_t k) {
385 }
else if (k >= 3 && k < neq()) {
387 if (k < m_thermo->nSpecies()) {
388 return m_thermo->speciesName(k);
390 k -= m_thermo->nSpecies();
392 for (
auto& S : m_surfaces) {
394 if (k < th->nSpecies()) {
401 throw CanteraError(
"Reactor::componentName",
"Index is out of bounds.");
404void Reactor::applySensitivity(
double* params)
409 for (
auto& p : m_sensParams) {
410 if (p.type == SensParameterType::reaction) {
411 p.value = m_kin->multiplier(p.local);
412 m_kin->setMultiplier(p.local, p.value*params[p.global]);
413 }
else if (p.type == SensParameterType::enthalpy) {
414 m_thermo->modifyOneHf298SS(p.local, p.value + params[p.global]);
417 for (
auto& S : m_surfaces) {
418 S->setSensitivityParameters(params);
420 m_thermo->invalidateCache();
422 m_kin->invalidateCache();
426void Reactor::resetSensitivity(
double* params)
431 for (
auto& p : m_sensParams) {
432 if (p.type == SensParameterType::reaction) {
433 m_kin->setMultiplier(p.local, p.value);
434 }
else if (p.type == SensParameterType::enthalpy) {
435 m_thermo->resetHf298(p.local);
438 for (
auto& S : m_surfaces) {
439 S->resetSensitivityParameters();
441 m_thermo->invalidateCache();
443 m_kin->invalidateCache();
447void Reactor::setAdvanceLimits(
const double *limits)
451 "Error: reactor is empty.");
453 m_advancelimits.assign(limits, limits + m_nv);
456 if (std::none_of(m_advancelimits.begin(), m_advancelimits.end(),
457 [](
double val){return val>0;})) {
458 m_advancelimits.resize(0);
462bool Reactor::getAdvanceLimits(
double *limits)
464 bool has_limit = hasAdvanceLimits();
466 std::copy(m_advancelimits.begin(), m_advancelimits.end(), limits);
468 std::fill(limits, limits + m_nv, -1.0);
473void Reactor::setAdvanceLimit(
const string& nm,
const double limit)
475 size_t k = componentIndex(nm);
477 throw CanteraError(
"Reactor::setAdvanceLimit",
"No component named '{}'", nm);
482 "Error: reactor is empty.");
487 "Cannot set limit on a reactor that is not "
488 "assigned to a ReactorNet object.");
492 }
else if (k > m_nv) {
494 "Index out of bounds.");
496 m_advancelimits.resize(m_nv, -1.0);
497 m_advancelimits[k] = limit;
500 if (std::none_of(m_advancelimits.begin(), m_advancelimits.end(),
501 [](
double val){return val>0;})) {
502 m_advancelimits.resize(0);
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,...
Header file for base class WallBase.
Base class for exceptions thrown by Cantera classes.
Public interface for kinetics managers.
virtual void getNetProductionRates(doublereal *wdot)
Species net production rates [kmol/m^3/s or kmol/m^2/s].
size_t kineticsSpeciesIndex(size_t k, size_t n) const
The location of species k of phase n in species arrays.
size_t surfacePhaseIndex() const
This returns the integer index of the phase which has ThermoPhase type cSurf.
size_t nSpecies() const
Returns the number of species in the phase.
std::string speciesName(size_t k) const
Name of the species with index k.
size_t speciesIndex(const std::string &name) const
Returns the index of a species named 'name' within the Phase object.
A simple thermodynamic model for a surface phase, assuming an ideal solution model.
virtual double size(size_t k) const
Returns the number of sites occupied by one molecule of species k.
double siteDensity() const
Returns the site density.
Base class for a phase with thermodynamic properties.
Base class for 'walls' (walls, pistons, etc.) connecting reactors.
virtual void initialize()
Called just before the start of integration.
Namespace for the Cantera kernel.
const size_t npos
index returned by functions to indicate "no position"
doublereal dot(InputIter x_begin, InputIter x_end, InputIter2 y_begin)
Function that calculates a templated inner product.
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
const double GasConstant
Universal Gas Constant [J/kmol/K].
Various templated functions that carry out common vector operations (see Templated Utility Functions)...