16#include "cantera/numerics/eigen_dense.h"
18#include <boost/math/tools/roots.hpp>
21namespace bmt = boost::math::tools;
26Reactor::Reactor(shared_ptr<Solution> sol,
const string& name)
31Reactor::Reactor(shared_ptr<Solution> sol,
bool clone,
const string& name)
32 : ReactorBase(sol, clone, name)
34 m_kin = m_solution->kinetics().get();
35 setChemistryEnabled(m_kin->nReactions() > 0);
37 m_sdot.resize(m_nsp, 0.0);
38 m_wdot.resize(m_nsp, 0.0);
44 m_kin->setDerivativeSettings(settings);
45 bool force = settings.
empty();
46 if (force || settings.
hasKey(
"skip-flow-devices")) {
49 if (force || settings.
hasKey(
"skip-walls")) {
52 if (force || settings.
hasKey(
"skip-connector-composition-dependence")) {
54 settings.
getBool(
"skip-connector-composition-dependence",
false);
56 if (force || settings.
hasKey(
"skip-connector-pressure-composition-dependence")) {
58 settings.
getBool(
"skip-connector-pressure-composition-dependence",
false);
72 y[2] = m_thermo->intEnergy_mass() *
m_mass;
75 m_thermo->getMassFractions(y.subspan(3,
m_nsp));
80 if (!m_thermo || (m_chem && !
m_kin)) {
81 throw CanteraError(
"Reactor::initialize",
"Reactor contents not set"
82 " for reactor '" +
m_name +
"'.");
86 for (
size_t n = 0; n < m_wall.size(); n++) {
99 m_thermo->setMassFractions_NoNorm(y.subspan(3,
m_nsp));
104 auto u_err = [
this, U](
double T) {
106 return m_thermo->intEnergy_mass() *
m_mass - U;
109 double T = m_thermo->temperature();
110 boost::uintmax_t maxiter = 100;
111 pair<double, double> TT;
113 TT = bmt::bracket_and_solve_root(
114 u_err, T, 1.2,
true, bmt::eps_tolerance<double>(48), maxiter);
115 }
catch (std::exception&) {
119 TT = bmt::bisect(u_err, m_thermo->minTemp(), m_thermo->maxTemp(),
120 bmt::eps_tolerance<double>(48), maxiter);
121 }
catch (std::exception& err2) {
125 "{}\nat U = {}, rho = {}", err2.what(), U,
m_mass /
m_vol);
128 if (fabs(TT.first - TT.second) > 1e-7*TT.first) {
129 throw CanteraError(
"Reactor::updateState",
"root finding failed");
141 double& dmdt = RHS[0];
142 auto mdYdt = RHS.subspan(3);
146 auto mw = m_thermo->molecularWeights();
147 auto Y = m_thermo->massFractions();
160 for (
size_t k = 0; k <
m_nsp; k++) {
164 mdYdt[k] -= Y[k] * mdot_surf;
174 RHS[2] += m_thermo->intrinsicHeating() *
m_vol;
180 for (
auto outlet : m_outlet) {
181 double mdot =
outlet->massFlowRate();
189 for (
auto inlet : m_inlet) {
190 double mdot =
inlet->massFlowRate();
192 for (
size_t n = 0; n <
m_nsp; n++) {
193 double mdot_spec =
inlet->outletSpeciesMassFlowRate(n);
195 mdYdt[n] += (mdot_spec - mdot * Y[n]);
198 RHS[2] += mdot *
inlet->enthalpy_mass();
205 eval(time, LHS, RHS);
214 for (
size_t i = 0; i < m_wall.size(); i++) {
215 int f = 2 *
m_lr[i] - 1;
216 m_vdot -= f * m_wall[i]->expansionRate();
217 m_Qdot += f * m_wall[i]->heatRate();
224 throw CanteraError(
"Reactor::initializeSteady",
"Steady state solver cannot"
225 " be used with {0} when energy equation is disabled."
226 "\nConsider using IdealGas{0} instead.\n"
227 "See https://github.com/Cantera/enhancements/issues/234",
type());
235 vector<Eigen::Triplet<double>> trips;
236 Eigen::ArrayXd yCurrent(
m_nv);
238 double time = (
m_net !=
nullptr) ?
m_net->time() : 0.0;
240 Eigen::ArrayXd yPerturbed = yCurrent;
241 Eigen::ArrayXd lhsPerturbed(
m_nv), lhsCurrent(
m_nv);
242 Eigen::ArrayXd rhsPerturbed(
m_nv), rhsCurrent(
m_nv);
248 double rel_perturb = std::sqrt(std::numeric_limits<double>::epsilon());
249 double atol = (
m_net !=
nullptr) ?
m_net->atol() : 1e-15;
251 for (
size_t j = 0; j <
m_nv; j++) {
252 yPerturbed = yCurrent;
253 double delta_y = std::max(std::abs(yCurrent[j]), 1000 * atol) * rel_perturb;
254 yPerturbed[j] += delta_y;
262 for (
size_t i = 0; i <
m_nv; i++) {
263 double ydotPerturbed = rhsPerturbed[i] / lhsPerturbed[i];
264 double ydotCurrent = rhsCurrent[i] / lhsCurrent[i];
265 if (ydotCurrent != ydotPerturbed) {
266 trips.emplace_back(
static_cast<int>(i),
static_cast<int>(j),
267 (ydotPerturbed - ydotCurrent) / delta_y);
273 Eigen::SparseMatrix<double> jac(
m_nv,
m_nv);
274 jac.setFromTriplets(trips.begin(), trips.end());
280 if (!m_chem || rxn >=
m_kin->nReactions()) {
282 "Reaction number out of range ({})", rxn);
285 size_t p =
network().registerSensitivityParameter(
286 name()+
": "+
m_kin->reaction(rxn)->equation(), 1.0, 1.0);
287 m_sensParams.emplace_back(
293 if (k >= m_thermo->nSpecies()) {
294 throw CanteraError(
"Reactor::addSensitivitySpeciesEnthalpy",
295 "Species index out of range ({})", k);
298 size_t p =
network().registerSensitivityParameter(
299 name() +
": " + m_thermo->speciesName(k) +
" enthalpy",
301 m_sensParams.emplace_back(
303 SensParameterType::enthalpy});
309 for (
auto& S : m_surfaces) {
310 const auto& sdot = S->surfaceProductionRates();
311 size_t offset = S->kinetics()->kineticsSpeciesIndex(m_thermo->speciesName(0));
312 for (
size_t k = 0; k <
m_nsp; k++) {
323 if (nm ==
"volume") {
326 if (nm ==
"int_energy") {
330 return m_thermo->speciesIndex(nm) + 3;
333 "Component '{}' not found", nm);
344 }
else if (k >= 3 && k <
neq()) {
345 return m_thermo->speciesName(k - 3);
357 }
else if (k >= 3 && k <
m_nv) {
360 throw CanteraError(
"Reactor::upperBound",
"Index {} is out of bounds.", k);
371 }
else if (k >= 3 && k <
m_nv) {
374 throw CanteraError(
"Reactor::lowerBound",
"Index {} is out of bounds.", k);
379 for (
size_t k = 3; k <
m_nv; k++) {
380 y[k] = std::max(y[k], 0.0);
386 if (params.empty()) {
389 for (
auto& p : m_sensParams) {
390 if (p.type == SensParameterType::reaction) {
391 p.value =
m_kin->multiplier(p.local);
392 m_kin->setMultiplier(p.local, p.value*params[p.global]);
393 }
else if (p.type == SensParameterType::enthalpy) {
394 m_thermo->modifyOneHf298SS(p.local, p.value + params[p.global]);
397 m_thermo->invalidateCache();
399 m_kin->invalidateCache();
405 if (params.empty()) {
408 for (
auto& p : m_sensParams) {
409 if (p.type == SensParameterType::reaction) {
410 m_kin->setMultiplier(p.local, p.value);
411 }
else if (p.type == SensParameterType::enthalpy) {
412 m_thermo->resetHf298(p.local);
415 m_thermo->invalidateCache();
417 m_kin->invalidateCache();
427 [](
double val){return val>0;})) {
438 std::fill(limits.begin(), limits.end(), -1.0);
451 [](
double val){return val>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.
A map of string keys to values whose type can vary at runtime.
bool hasKey(const string &key) const
Returns true if the map contains an item named key.
bool empty() const
Return boolean indicating whether AnyMap is empty.
bool getBool(const string &key, bool default_) const
If key exists, return it as a bool, otherwise return default_.
Base class for exceptions thrown by Cantera classes.
An array index is out of range.
FlowDevice & outlet(size_t n=0)
Return a reference to the n-th outlet FlowDevice connected to this reactor.
ReactorNet * m_net
The ReactorNet that this reactor is part of.
size_t neq()
Number of equations (state variables) for this reactor.
size_t m_nv
Number of state variables for this reactor.
FlowDevice & inlet(size_t n=0)
Return a reference to the n-th inlet FlowDevice connected to this reactor.
vector< int > m_lr
Vector of length nWalls(), indicating whether this reactor is on the left (0) or right (1) of each wa...
double m_vol
Current volume of the reactor [m^3].
double m_mass
Current mass of the reactor [kg].
size_t m_nsp
Number of homogeneous species in the mixture.
string m_name
Reactor name.
ReactorNet & network()
The ReactorNet that this reactor belongs to.
size_t offset() const
Get the starting offset for this reactor's state variables within the global state vector of the Reac...
double m_enthalpy
Current specific enthalpy of the reactor [J/kg].
string name() const
Return the name of this reactor.
virtual void updateConnected(bool updatePressure)
Update state information needed by connected reactors, flow devices, and walls.
Class Reactor is a general-purpose class for stirred reactors.
void evalWalls(double t) override
Evaluate terms related to Walls.
double upperBound(size_t k) const override
Get the upper bound on the k-th component of the local state vector.
bool m_jac_skip_connector_pressure_composition_dependence
Omit species terms in pressure derivatives, which can add fill-in.
void resetBadValues(span< double > y) override
Reset physically or mathematically problematic values, such as negative species concentrations.
bool getAdvanceLimits(span< double > limits) const
Retrieve absolute step size limits during advance.
void eval(double t, span< double > LHS, span< double > RHS) override
Evaluate the reactor governing equations.
bool m_jac_skip_connector_composition_dependence
Omit flow composition derivatives, which can add dense connector blocks.
Kinetics * m_kin
Pointer to the homogeneous Kinetics object that handles the reactions.
void evalSteady(double t, span< double > LHS, span< double > RHS) override
Evaluate the governing equations with modifications for the steady-state solver.
vector< double > m_wdot
Species net molar production rates.
Eigen::SparseMatrix< double > finiteDifferenceJacobian()
Calculate the reactor-specific Jacobian using a finite difference method.
bool energyEnabled() const override
Returns true if solution of the energy equation is enabled.
string type() const override
String indicating the reactor model implemented.
bool m_jac_skip_flow_devices
Omit flow-device terms from the sparse Jacobian.
double m_Qdot
net heat transfer into the reactor, through walls [W]
size_t componentIndex(const string &nm) const override
Return the index in the solution vector for this reactor of the component named nm.
vector< double > m_advancelimits
Advance step limit.
bool m_jac_skip_walls
Omit wall terms from the sparse Jacobian.
void resetSensitivity(span< const double > params) override
Reset the reaction rate multipliers.
void applySensitivity(span< const double > params) override
Set reaction rate multipliers based on the sensitivity variables in params.
void updateSurfaceProductionRates()
Update m_sdot to reflect current production rates of bulk phase species due to reactions on adjacent ...
void addSensitivitySpeciesEnthalpy(size_t k) override
Add a sensitivity parameter associated with the enthalpy formation of species k.
void setAdvanceLimit(const string &nm, const double limit)
Set individual step size limit for component name nm.
vector< size_t > initializeSteady() override
Initialize the reactor before solving a steady-state problem.
void addSensitivityReaction(size_t rxn) override
Add a sensitivity parameter associated with the reaction number rxn.
double lowerBound(size_t k) const override
Get the lower bound on the k-th component of the local state vector.
vector< double > m_sdot
Total production rate of bulk phase species on surfaces [kmol/s].
string componentName(size_t k) override
Return the name of the solution component with index i.
bool hasAdvanceLimits() const
Check whether Reactor object uses advance limits.
double m_vdot
net rate of volume change from moving walls [m^3/s]
void initialize(double t0=0.0) override
Initialize the reactor.
double m_initialVolume
Initial volume [m³]; used for steady-state calculations.
void updateState(span< const double > y) override
Set the state of the reactor to correspond to the state vector y.
void setAdvanceLimits(span< const double > limits)
Set absolute step size limits during advance.
void setDerivativeSettings(AnyMap &settings) override
Control terms included when calculating sparse Jacobian approximations.
void getState(span< double > y) override
Get the current state of the reactor.
Base class for 'walls' (walls, pistons, etc.) connecting reactors.
virtual void initialize()
Called just before the start of integration.
double dot(InputIter x_begin, InputIter x_end, InputIter2 y_begin)
Function that calculates a templated inner product.
const double GasConstant
Universal Gas Constant [J/kmol/K].
Namespace for the Cantera kernel.
const double Tiny
Small number to compare differences of mole fractions against.
span< double > asSpan(Eigen::DenseBase< Derived > &v)
Convenience wrapper for accessing Eigen vector/array/map data as a span.
const double BigNumber
largest number to compare to inf.
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...