13 #include <boost/math/tools/roots.hpp>
16 namespace bmt = boost::math::tools;
33 if (m_kin->nReactions() == 0) {
40 void Reactor::getState(
double* y)
44 "Error: reactor is empty.");
46 m_thermo->restoreState(m_state);
49 m_mass = m_thermo->density() * m_vol;
56 y[2] = m_thermo->intEnergy_mass() * m_mass;
59 m_thermo->getMassFractions(y+3);
63 getSurfaceInitialConditions(y + m_nsp + 3);
66 void Reactor::getSurfaceInitialConditions(
double* y)
69 for (
auto& S : m_surfaces) {
70 S->getCoverages(y + loc);
71 loc += S->thermo()->nSpecies();
75 void Reactor::initialize(doublereal t0)
77 if (!m_thermo || (m_chem && !m_kin)) {
78 throw CanteraError(
"Reactor::initialize",
"Reactor contents not set"
79 " for reactor '" + m_name +
"'.");
81 m_thermo->restoreState(m_state);
82 m_sdot.resize(m_nsp, 0.0);
83 m_wdot.resize(m_nsp, 0.0);
84 updateConnected(
true);
86 for (
size_t n = 0; n < m_wall.size(); n++) {
93 for (
auto& S : m_surfaces) {
94 m_nv += S->thermo()->nSpecies();
95 size_t nt = S->kinetics()->nTotalSpecies();
96 maxnt = std::max(maxnt, nt);
97 if (m_chem && &m_kin->thermo(0) != &S->kinetics()->thermo(0)) {
99 "First phase of all kinetics managers must be the gas.");
102 m_work.resize(maxnt);
105 size_t Reactor::nSensParams()
107 size_t ns = m_sensParams.size();
108 for (
auto& S : m_surfaces) {
109 ns += S->nSensParams();
114 void Reactor::syncState()
116 ReactorBase::syncState();
117 m_mass = m_thermo->density() * m_vol;
120 void Reactor::updateState(doublereal* y)
127 m_thermo->setMassFractions_NoNorm(y+3);
132 auto u_err = [
this, U](
double T) {
133 m_thermo->setState_TR(T, m_mass / m_vol);
134 return m_thermo->intEnergy_mass() * m_mass - U;
137 double T = m_thermo->temperature();
138 boost::uintmax_t maxiter = 100;
139 std::pair<double, double> TT;
141 TT = bmt::bracket_and_solve_root(
142 u_err, T, 1.2,
true, bmt::eps_tolerance<double>(48), maxiter);
143 }
catch (std::exception&) {
147 TT = bmt::bisect(u_err, m_thermo->minTemp(), m_thermo->maxTemp(),
148 bmt::eps_tolerance<double>(48), maxiter);
149 }
catch (std::exception& err2) {
151 m_thermo->setState_TR(T, m_mass / m_vol);
153 "{}\nat U = {}, rho = {}", err2.what(), U, m_mass / m_vol);
156 if (fabs(TT.first - TT.second) > 1e-7*TT.first) {
157 throw CanteraError(
"Reactor::updateState",
"root finding failed");
159 m_thermo->setState_TR(TT.second, m_mass / m_vol);
161 m_thermo->setDensity(m_mass/m_vol);
164 updateSurfaceState(y + m_nsp + 3);
165 updateConnected(
true);
168 void Reactor::updateSurfaceState(
double* y)
171 for (
auto& S : m_surfaces) {
172 S->setCoverages(y+loc);
173 loc += S->thermo()->nSpecies();
177 void Reactor::updateConnected(
bool updatePressure) {
179 m_enthalpy = m_thermo->enthalpy_mass();
180 if (updatePressure) {
181 m_pressure = m_thermo->pressure();
183 m_intEnergy = m_thermo->intEnergy_mass();
184 m_thermo->saveState(m_state);
187 double time = m_net->time();
188 for (
size_t i = 0; i < m_outlet.size(); i++) {
189 m_outlet[i]->updateMassFlowRate(time);
191 for (
size_t i = 0; i < m_inlet.size(); i++) {
192 m_inlet[i]->updateMassFlowRate(time);
196 void Reactor::evalEqs(doublereal time, doublereal* y,
197 doublereal* ydot, doublereal* params)
200 double* dYdt = ydot + 3;
203 applySensitivity(params);
204 m_thermo->restoreState(m_state);
205 double mdot_surf = evalSurfaces(time, ydot + m_nsp + 3);
211 const vector_fp& mw = m_thermo->molecularWeights();
212 const doublereal* Y = m_thermo->massFractions();
215 m_kin->getNetProductionRates(&m_wdot[0]);
218 for (
size_t k = 0; k < m_nsp; k++) {
220 dYdt[k] = (m_wdot[k] * m_vol + m_sdot[k]) * mw[k] / m_mass;
222 dYdt[k] -= Y[k] * mdot_surf / m_mass;
230 ydot[2] = - m_thermo->pressure() * m_vdot - m_Q;
236 for (
auto outlet : m_outlet) {
237 double mdot = outlet->massFlowRate();
240 ydot[2] -= mdot * m_enthalpy;
245 for (
auto inlet : m_inlet) {
246 double mdot = inlet->massFlowRate();
248 for (
size_t n = 0; n < m_nsp; n++) {
249 double mdot_spec = inlet->outletSpeciesMassFlowRate(n);
251 dYdt[n] += (mdot_spec - mdot * Y[n]) / m_mass;
254 ydot[2] += mdot * inlet->enthalpy_mass();
259 resetSensitivity(params);
262 void Reactor::evalWalls(
double t)
266 for (
size_t i = 0; i < m_wall.size(); i++) {
267 int lr = 1 - 2*m_lr[i];
268 m_vdot += lr*m_wall[i]->vdot(t);
269 m_Q += lr*m_wall[i]->Q(t);
273 double Reactor::evalSurfaces(
double t,
double* ydot)
275 const vector_fp& mw = m_thermo->molecularWeights();
276 fill(m_sdot.begin(), m_sdot.end(), 0.0);
278 double mdot_surf = 0.0;
280 for (
auto S : m_surfaces) {
292 for (
size_t k = 1; k < nk; k++) {
293 ydot[loc + k] = m_work[surfloc+k]*rs0*surf->
size(k);
294 sum -= ydot[loc + k];
300 double wallarea = S->area();
301 for (
size_t k = 0; k < m_nsp; k++) {
302 m_sdot[k] += m_work[bulkloc + k] * wallarea;
303 mdot_surf += m_sdot[k] * mw[k];
309 void Reactor::addSensitivityReaction(
size_t rxn)
311 if (!m_chem || rxn >= m_kin->nReactions()) {
313 "Reaction number out of range ({})", rxn);
316 size_t p = network().registerSensitivityParameter(
317 name()+
": "+m_kin->reactionString(rxn), 1.0, 1.0);
318 m_sensParams.emplace_back(
319 SensitivityParameter{rxn, p, 1.0, SensParameterType::reaction});
322 void Reactor::addSensitivitySpeciesEnthalpy(
size_t k)
324 if (k >= m_thermo->nSpecies()) {
325 throw CanteraError(
"Reactor::addSensitivitySpeciesEnthalpy",
326 "Species index out of range ({})", k);
329 size_t p = network().registerSensitivityParameter(
330 name() +
": " + m_thermo->speciesName(k) +
" enthalpy",
332 m_sensParams.emplace_back(
333 SensitivityParameter{k, p, m_thermo->Hf298SS(k),
334 SensParameterType::enthalpy});
337 size_t Reactor::speciesIndex(
const string& nm)
const
340 size_t k = m_thermo->speciesIndex(nm);
346 size_t offset = m_nsp;
347 for (
auto& S : m_surfaces) {
359 size_t Reactor::componentIndex(
const string& nm)
const
361 size_t k = speciesIndex(nm);
364 }
else if (nm ==
"mass") {
366 }
else if (nm ==
"volume") {
368 }
else if (nm ==
"int_energy") {
375 std::string Reactor::componentName(
size_t k) {
382 }
else if (k >= 3 && k < neq()) {
384 if (k < m_thermo->nSpecies()) {
385 return m_thermo->speciesName(k);
387 k -= m_thermo->nSpecies();
389 for (
auto& S : m_surfaces) {
391 if (k < th->nSpecies()) {
398 throw CanteraError(
"Reactor::componentName",
"Index is out of bounds.");
401 void Reactor::applySensitivity(
double* params)
406 for (
auto& p : m_sensParams) {
407 if (p.type == SensParameterType::reaction) {
408 p.value = m_kin->multiplier(p.local);
409 m_kin->setMultiplier(p.local, p.value*params[p.global]);
410 }
else if (p.type == SensParameterType::enthalpy) {
411 m_thermo->modifyOneHf298SS(p.local, p.value + params[p.global]);
414 for (
auto& S : m_surfaces) {
415 S->setSensitivityParameters(params);
417 m_thermo->invalidateCache();
419 m_kin->invalidateCache();
423 void Reactor::resetSensitivity(
double* params)
428 for (
auto& p : m_sensParams) {
429 if (p.type == SensParameterType::reaction) {
430 m_kin->setMultiplier(p.local, p.value);
431 }
else if (p.type == SensParameterType::enthalpy) {
432 m_thermo->resetHf298(p.local);
435 for (
auto& S : m_surfaces) {
436 S->resetSensitivityParameters();
438 m_thermo->invalidateCache();
440 m_kin->invalidateCache();
444 void Reactor::setAdvanceLimits(
const double *limits)
448 "Error: reactor is empty.");
450 m_advancelimits.assign(limits, limits + m_nv);
453 if (std::none_of(m_advancelimits.begin(), m_advancelimits.end(),
454 [](
double val){return val>0;})) {
455 m_advancelimits.resize(0);
459 bool Reactor::getAdvanceLimits(
double *limits)
461 bool has_limit = hasAdvanceLimits();
463 std::copy(m_advancelimits.begin(), m_advancelimits.end(), limits);
465 std::fill(limits, limits + m_nv, -1.0);
470 void Reactor::setAdvanceLimit(
const string& nm,
const double limit)
472 size_t k = componentIndex(nm);
476 "Error: reactor is empty.");
481 "Cannot set limit on a reactor that is not "
482 "assigned to a ReactorNet object.");
486 }
else if (k > m_nv) {
488 "Index out of bounds.");
490 m_advancelimits.resize(m_nv, -1.0);
491 m_advancelimits[k] = limit;
494 if (std::none_of(m_advancelimits.begin(), m_advancelimits.end(),
495 [](
double val){return val>0;})) {
496 m_advancelimits.resize(0);
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.
virtual void setTemperature(const doublereal temp)
Set the internally stored temperature of the phase (K).
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.
doublereal siteDensity()
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.
const size_t npos
index returned by functions to indicate "no position"
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].
Namespace for the Cantera kernel.