17 Reaction::Reaction(
int type)
21 , allow_nonreactant_orders(false)
22 , allow_negative_orders(false)
26 Reaction::Reaction(
int type,
const Composition& reactants_,
29 , reactants(reactants_)
33 , allow_nonreactant_orders(false)
34 , allow_negative_orders(false)
38 void Reaction::validate()
40 if (!allow_nonreactant_orders) {
41 for (
const auto& order : orders) {
42 if (reactants.find(order.first) == reactants.end()) {
43 throw CanteraError(
"Reaction::validate",
"Reaction order " 44 "specified for non-reactant species '" + order.first +
"'");
49 if (!allow_negative_orders) {
50 for (
const auto& order : orders) {
51 if (order.second < 0.0) {
52 throw CanteraError(
"Reaction::validate",
"Negative reaction " 53 "order specified for species '" + order.first +
"'");
59 std::string Reaction::reactantString()
const 61 std::ostringstream result;
62 for (
auto iter = reactants.begin(); iter != reactants.end(); ++iter) {
63 if (iter != reactants.begin()) {
66 if (iter->second != 1.0) {
67 result << iter->second <<
" ";
69 result << iter->first;
74 std::string Reaction::productString()
const 76 std::ostringstream result;
77 for (
auto iter = products.begin(); iter != products.end(); ++iter) {
78 if (iter != products.begin()) {
81 if (iter->second != 1.0) {
82 result << iter->second <<
" ";
84 result << iter->first;
89 std::string Reaction::equation()
const 92 return reactantString() +
" <=> " + productString();
94 return reactantString() +
" => " + productString();
98 ElementaryReaction::ElementaryReaction(
const Composition& reactants_,
103 , allow_negative_pre_exponential_factor(false)
107 ElementaryReaction::ElementaryReaction()
109 , allow_negative_pre_exponential_factor(false)
116 if (!allow_negative_pre_exponential_factor &&
119 "Undeclared negative pre-exponential factor found in reaction '" 124 ThirdBody::ThirdBody(
double default_eff)
125 : default_efficiency(default_eff)
129 ThreeBodyReaction::ThreeBodyReaction()
134 ThreeBodyReaction::ThreeBodyReaction(
const Composition& reactants_,
136 const Arrhenius& rate_,
137 const ThirdBody& tbody)
138 : ElementaryReaction(reactants_, products_, rate_)
152 FalloffReaction::FalloffReaction()
158 FalloffReaction::FalloffReaction(
160 const Arrhenius& low_rate_,
const Arrhenius& high_rate_,
161 const ThirdBody& tbody)
163 , low_rate(low_rate_)
164 , high_rate(high_rate_)
166 , falloff(new Falloff())
194 throw CanteraError(
"FalloffReaction::validate",
"Negative " 195 "pre-exponential factor found for reaction '" +
equation() +
"'");
199 ChemicallyActivatedReaction::ChemicallyActivatedReaction()
204 ChemicallyActivatedReaction::ChemicallyActivatedReaction(
206 const Arrhenius& low_rate_,
const Arrhenius& high_rate_,
207 const ThirdBody& tbody)
208 : FalloffReaction(reactants_, products_, low_rate_, high_rate_, tbody)
213 PlogReaction::PlogReaction()
218 PlogReaction::PlogReaction(
const Composition& reactants_,
220 : Reaction(
PLOG_RXN, reactants_, products_)
225 ChebyshevReaction::ChebyshevReaction()
230 ChebyshevReaction::ChebyshevReaction(
const Composition& reactants_,
232 const ChebyshevRate& rate_)
238 InterfaceReaction::InterfaceReaction()
239 : is_sticking_coefficient(false)
240 , use_motz_wise_correction(false)
245 InterfaceReaction::InterfaceReaction(
const Composition& reactants_,
247 const Arrhenius& rate_,
249 : ElementaryReaction(reactants_, products_, rate_)
250 , is_sticking_coefficient(isStick)
251 , use_motz_wise_correction(false)
256 ElectrochemicalReaction::ElectrochemicalReaction()
257 : film_resistivity(0.0)
259 , exchange_current_density_formulation(false)
263 ElectrochemicalReaction::ElectrochemicalReaction(
const Composition& reactants_,
265 const Arrhenius& rate_)
266 : InterfaceReaction(reactants_, products_, rate_)
267 , film_resistivity(0.0)
269 , exchange_current_density_formulation(false)
273 Arrhenius readArrhenius(
const XML_Node& arrhenius_node)
275 return Arrhenius(
getFloat(arrhenius_node,
"A",
"toSI"),
289 std::vector<std::string> p;
292 size_t np = p.size();
293 for (
size_t n = 0; n < np; n++) {
297 int falloff_type = 0;
299 falloff_type = SIMPLE_FALLOFF;
301 throw CanteraError(
"readFalloff",
"Lindemann parameterization " 302 "takes no parameters, but {} were given", np);
305 falloff_type = TROE_FALLOFF;
306 if (np != 3 && np != 4) {
307 throw CanteraError(
"readFalloff",
"Troe parameterization takes " 308 "3 or 4 parameters, but {} were given", np);
311 falloff_type = SRI_FALLOFF;
312 if (np != 3 && np != 5) {
313 throw CanteraError(
"readFalloff",
"SRI parameterization takes " 314 "3 or 5 parameters, but {} were given", np);
317 throw CanteraError(
"readFalloff",
"Unrecognized falloff type: '{}'",
323 void readEfficiencies(ThirdBody& tbody,
const XML_Node& rc_node)
325 if (!rc_node.hasChild(
"efficiencies")) {
326 tbody.default_efficiency = 1.0;
329 const XML_Node& eff_node = rc_node.child(
"efficiencies");
330 tbody.default_efficiency =
fpValue(eff_node[
"default"]);
334 void setupReaction(Reaction& R,
const XML_Node& rxn_node)
341 std::vector<XML_Node*> orders = rxn_node.getChildren(
"order");
342 for (
size_t i = 0; i < orders.size(); i++) {
343 R.orders[orders[i]->attrib(
"species")] = orders[i]->fp_value();
347 R.id = rxn_node.attrib(
"id");
348 R.duplicate = rxn_node.hasAttrib(
"duplicate");
349 const std::string& rev = rxn_node[
"reversible"];
350 R.reversible = (rev ==
"true" || rev ==
"yes");
353 void setupElementaryReaction(ElementaryReaction& R,
const XML_Node& rxn_node)
355 const XML_Node& rc_node = rxn_node.child(
"rateCoeff");
356 if (rc_node.hasChild(
"Arrhenius")) {
357 R.rate = readArrhenius(rc_node.child(
"Arrhenius"));
358 }
else if (rc_node.hasChild(
"Arrhenius_ExchangeCurrentDensity")) {
359 R.rate = readArrhenius(rc_node.child(
"Arrhenius_ExchangeCurrentDensity"));
361 throw CanteraError(
"setupElementaryReaction",
"Couldn't find Arrhenius node");
363 if (rxn_node[
"negative_A"] ==
"yes") {
364 R.allow_negative_pre_exponential_factor =
true;
366 if (rxn_node[
"negative_orders"] ==
"yes") {
367 R.allow_negative_orders =
true;
369 if (rxn_node[
"nonreactant_orders"] ==
"yes") {
370 R.allow_nonreactant_orders =
true;
372 setupReaction(R, rxn_node);
375 void setupThreeBodyReaction(ThreeBodyReaction& R,
const XML_Node& rxn_node)
377 readEfficiencies(R.third_body, rxn_node.child(
"rateCoeff"));
378 setupElementaryReaction(R, rxn_node);
381 void setupFalloffReaction(FalloffReaction& R,
const XML_Node& rxn_node)
383 XML_Node& rc_node = rxn_node.child(
"rateCoeff");
384 std::vector<XML_Node*> rates = rc_node.getChildren(
"Arrhenius");
387 for (
size_t i = 0; i < rates.size(); i++) {
388 XML_Node& node = *rates[i];
389 if (node[
"name"] ==
"") {
390 R.high_rate = readArrhenius(node);
392 }
else if (node[
"name"] ==
"k0") {
393 R.low_rate = readArrhenius(node);
396 throw CanteraError(
"setupFalloffReaction",
"Found an Arrhenius XML " 397 "node with an unexpected type '" + node[
"name"] +
"'");
400 if (nLow != 1 || nHigh != 1) {
401 throw CanteraError(
"setupFalloffReaction",
"Did not find the correct " 402 "number of Arrhenius rate expressions");
405 readEfficiencies(R.third_body, rc_node);
406 setupReaction(R, rxn_node);
409 void setupChemicallyActivatedReaction(ChemicallyActivatedReaction& R,
410 const XML_Node& rxn_node)
412 XML_Node& rc_node = rxn_node.child(
"rateCoeff");
413 std::vector<XML_Node*> rates = rc_node.getChildren(
"Arrhenius");
416 for (
size_t i = 0; i < rates.size(); i++) {
417 XML_Node& node = *rates[i];
418 if (node[
"name"] ==
"kHigh") {
419 R.high_rate = readArrhenius(node);
421 }
else if (node[
"name"] ==
"") {
422 R.low_rate = readArrhenius(node);
425 throw CanteraError(
"setupChemicallyActivatedReaction",
"Found an " 426 "Arrhenius XML node with an unexpected type '" + node[
"name"] +
"'");
429 if (nLow != 1 || nHigh != 1) {
430 throw CanteraError(
"setupChemicallyActivatedReaction",
"Did not find " 431 "the correct number of Arrhenius rate expressions");
434 readEfficiencies(R.third_body, rc_node);
435 setupReaction(R, rxn_node);
438 void setupPlogReaction(PlogReaction& R,
const XML_Node& rxn_node)
440 XML_Node& rc = rxn_node.child(
"rateCoeff");
441 std::multimap<double, Arrhenius> rates;
442 for (
size_t m = 0; m < rc.nChildren(); m++) {
443 const XML_Node& node = rc.child(m);
444 rates.insert({
getFloat(node,
"P",
"toSI"), readArrhenius(node)});
446 R.rate = Plog(rates);
447 setupReaction(R, rxn_node);
460 size_t nP = atoi(coeff_node[
"degreeP"].c_str());
461 size_t nT = atoi(coeff_node[
"degreeT"].c_str());
466 for (
size_t t = 0; t < nT; t++) {
467 for (
size_t p = 0; p < nP; p++) {
468 coeffs(t,p) = coeffs_flat[nP*t + p];
471 R.rate = ChebyshevRate(
getFloat(rc,
"Tmin",
"toSI"),
476 setupReaction(R, rxn_node);
479 void setupInterfaceReaction(InterfaceReaction& R,
const XML_Node& rxn_node)
484 XML_Node& arr = rxn_node.child(
"rateCoeff").child(
"Arrhenius");
486 R.is_sticking_coefficient =
true;
487 R.sticking_species = arr[
"species"];
490 R.use_motz_wise_correction =
true;
492 R.use_motz_wise_correction =
false;
495 XML_Node* parent = rxn_node.parent();
496 if (parent && parent->name() ==
"reactionData" 498 R.use_motz_wise_correction =
true;
502 std::vector<XML_Node*> cov = arr.getChildren(
"coverage");
503 for (
const auto& node : cov) {
504 CoverageDependency& cdep = R.coverage_deps[node->attrib(
"species")];
505 cdep.a =
getFloat(*node,
"a",
"toSI");
509 setupElementaryReaction(R, rxn_node);
512 void setupElectrochemicalReaction(ElectrochemicalReaction& R,
513 const XML_Node& rxn_node)
517 if (type ==
"butlervolmer") {
519 }
else if (type ==
"butlervolmer_noactivitycoeffs") {
521 }
else if (type ==
"surfaceaffinity") {
523 }
else if (type ==
"global") {
527 XML_Node& rc = rxn_node.
child(
"rateCoeff");
529 if (rc_type ==
"exchangecurrentdensity") {
530 R.exchange_current_density_formulation =
true;
531 }
else if (rc_type !=
"" && rc_type !=
"arrhenius") {
532 throw CanteraError(
"setupElectrochemicalReaction",
533 "Unknown rate coefficient type: '" + rc_type +
"'");
535 if (rc.hasChild(
"Arrhenius_ExchangeCurrentDensity")) {
536 R.exchange_current_density_formulation =
true;
539 if (rc.hasChild(
"electrochem") && rc.child(
"electrochem").hasAttrib(
"beta")) {
544 setupInterfaceReaction(R, rxn_node);
550 throw CanteraError(
"setupElectrochemicalReaction",
551 "A Butler-Volmer reaction must be reversible");
556 R.allow_nonreactant_orders =
true;
557 for (
const auto& sp : R.reactants) {
558 R.orders[sp.first] += sp.second * (1.0 - R.beta);
560 for (
const auto& sp : R.products) {
561 R.orders[sp.first] += sp.second * R.beta;
566 if (rxn_node.hasChild(
"reactionOrderFormulation")) {
569 R.allow_nonreactant_orders =
true;
570 const XML_Node& rof_node = rxn_node.child(
"reactionOrderFormulation");
572 R.orders = initial_orders;
574 for (
const auto& sp : R.reactants) {
575 R.orders[sp.first] = 0.0;
579 for (
const auto& sp : R.reactants) {
580 double c =
getValue(initial_orders, sp.first, sp.second);
581 R.orders[sp.first] += c * (1.0 - R.beta);
583 for (
const auto& sp : R.products) {
584 double c =
getValue(initial_orders, sp.first, sp.second);
585 R.orders[sp.first] += c * R.beta;
588 throw CanteraError(
"setupElectrochemicalReaction",
"unknown model " 589 "for reactionOrderFormulation XML_Node: '" +
590 rof_node[
"model"] +
"'");
595 if (rxn_node.hasChild(
"orders")) {
597 for (
const auto& order : orders) {
598 R.orders[order.first] = order.second;
610 && (type ==
"edge" || type ==
"surface")) {
611 type =
"electrochemical";
615 if (type ==
"elementary" || type ==
"arrhenius" || type ==
"") {
616 auto R = make_shared<ElementaryReaction>();
617 setupElementaryReaction(*R, rxn_node);
619 }
else if (type ==
"threebody" || type ==
"three_body") {
620 auto R = make_shared<ThreeBodyReaction>();
621 setupThreeBodyReaction(*R, rxn_node);
623 }
else if (type ==
"falloff") {
624 auto R = make_shared<FalloffReaction>();
625 setupFalloffReaction(*R, rxn_node);
627 }
else if (type ==
"chemact" || type ==
"chemically_activated") {
628 auto R = make_shared<ChemicallyActivatedReaction>();
629 setupChemicallyActivatedReaction(*R, rxn_node);
631 }
else if (type ==
"plog" || type ==
"pdep_arrhenius") {
632 auto R = make_shared<PlogReaction>();
633 setupPlogReaction(*R, rxn_node);
635 }
else if (type ==
"chebyshev") {
636 auto R = make_shared<ChebyshevReaction>();
637 setupChebyshevReaction(*R, rxn_node);
639 }
else if (type ==
"interface" || type ==
"surface" || type ==
"edge" ||
641 auto R = make_shared<InterfaceReaction>();
642 setupInterfaceReaction(*R, rxn_node);
644 }
else if (type ==
"electrochemical" ||
645 type ==
"butlervolmer_noactivitycoeffs" ||
646 type ==
"butlervolmer" ||
647 type ==
"surfaceaffinity") {
648 auto R = make_shared<ElectrochemicalReaction>();
649 setupElectrochemicalReaction(*R, rxn_node);
653 "Unknown reaction type '" + rxn_node[
"type"] +
"'");
659 std::vector<shared_ptr<Reaction> > all_reactions;
660 for (
const auto& rxnnode : node.
child(
"reactionData").
getChildren(
"reaction")) {
663 return all_reactions;
virtual std::string reactantString() const
The reactant side of the chemical equation for this reaction.
doublereal fpValue(const std::string &val)
Translate a string into one doublereal value.
std::vector< XML_Node * > getChildren(const std::string &name) const
Get a vector of pointers to XML_Node containing all of the children of the current node which match t...
const int PLOG_RXN
A pressure-dependent rate expression consisting of several Arrhenius rate expressions evaluated at di...
void readFalloff(FalloffReaction &R, const XML_Node &rc_node)
Parse falloff parameters, given a rateCoeff node.
virtual std::string productString() const
The product side of the chemical equation for this reaction.
int reaction_type
Type of the reaction.
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data...
const int INTERFACE_RXN
A reaction occurring on an interface, e.g a surface or edge.
A pressure-dependent reaction parameterized by a bi-variate Chebyshev polynomial in temperature and p...
shared_ptr< Falloff > newFalloff(int type, const vector_fp &c)
Return a pointer to a new falloff function calculator.
size_t getFloatArray(const XML_Node &node, vector_fp &v, const bool convert, const std::string &unitsString, const std::string &nodeName)
This function reads the current node or a child node of the current node with the default name...
virtual void validate()
Ensure that the rate constant and other parameters for this reaction are valid.
virtual void validate()
Ensure that the rate constant and other parameters for this reaction are valid.
const int CHEBYSHEV_RXN
A general gas-phase pressure-dependent reaction where k(T,P) is defined in terms of a bivariate Cheby...
const int SURFACEAFFINITY_RXN
This is a surface reaction that is formulated using the affinity representation, common in the geoche...
Class XML_Node is a tree-based representation of the contents of an XML file.
Parameterizations for reaction falloff functions.
const int CHEMACT_RXN
A chemical activation reaction.
virtual std::string reactantString() const
The reactant side of the chemical equation for this reaction.
virtual std::string productString() const
The product side of the chemical equation for this reaction.
A class for 2D arrays stored in column-major (Fortran-compatible) form.
Header file for class Cantera::Array2D.
const int FALLOFF_RXN
The general form for a gas-phase association or dissociation reaction, with a pressure-dependent rate...
virtual void validate()
Ensure that the rate constant and other parameters for this reaction are valid.
shared_ptr< Falloff > falloff
Falloff function which determines how low_rate and high_rate are combined to determine the rate const...
std::map< std::string, doublereal > Composition
Map from string names to doubles.
A reaction that is first-order in [M] at low pressure, like a third-body reaction, but zeroth-order in [M] as pressure increases.
double preExponentialFactor() const
Return the pre-exponential factor A (in m, kmol, s to powers depending on the reaction order) ...
bool caseInsensitiveEquals(const std::string &input, const std::string &test)
Case insensitive equality predicate.
const U & getValue(const std::map< T, U > &m, const T &key, const U &default_val)
Const accessor for a value in a std::map.
Base class for exceptions thrown by Cantera classes.
shared_ptr< Reaction > newReaction(const XML_Node &rxn_node)
Create a new Reaction object for the reaction defined in rxn_node
void validate(const std::string &equation)
Check to make sure that the rate expression is finite over a range of temperatures at each interpolat...
Intermediate class which stores data about a reaction and its rate parameterization so that it can be...
Base class for falloff function calculators.
const int THREE_BODY_RXN
A gas-phase reaction that requires a third-body collision partner.
Arrhenius reaction rate type depends only on temperature.
bool hasChild(const std::string &ch) const
Tests whether the current node has a child node with a particular name.
XML_Node & child(const size_t n) const
Return a changeable reference to the n'th child of the current node.
const int GLOBAL_RXN
A global reaction.
compositionMap parseCompString(const std::string &ss, const std::vector< std::string > &names)
Parse a composition string into a map consisting of individual key:composition pairs.
virtual std::string productString() const
The product side of the chemical equation for this reaction.
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
Composition efficiencies
Map of species to third body efficiency.
virtual void validate()
Ensure that the rate constant and other parameters for this reaction are valid.
virtual std::string reactantString() const
The reactant side of the chemical equation for this reaction.
void getStringArray(const XML_Node &node, std::vector< std::string > &v)
This function interprets the value portion of an XML element as a string.
const doublereal GasConstant
Universal Gas Constant. [J/kmol/K].
const int ELEMENTARY_RXN
A reaction with a rate coefficient that depends only on temperature and voltage that also obeys mass-...
doublereal getFloat(const XML_Node &parent, const std::string &name, const std::string &type)
Get a floating-point value from a child element.
Arrhenius high_rate
The rate constant in the high-pressure limit.
Arrhenius low_rate
The rate constant in the low-pressure limit.
const int BUTLERVOLMER_NOACTIVITYCOEFFS_RXN
This is a surface reaction that is formulated using the Butler-Volmer formulation and using concentra...
const int BUTLERVOLMER_RXN
This is a surface reaction that is formulated using the Butler-Volmer formulation.
std::vector< shared_ptr< Reaction > > getReactions(const XML_Node &node)
Create Reaction objects for all <reaction> nodes in an XML document.
std::string toLowerCopy(const std::string &input)
Convert to lower case.
Namespace for the Cantera kernel.
doublereal fpValueCheck(const std::string &val)
Translate a string into one doublereal value, with error checking.
double default_efficiency
The default third body efficiency for species not listed in efficiencies.
ThirdBody third_body
Relative efficiencies of third-body species in enhancing the reaction rate.
std::string equation() const
The chemical equation for this reaction.
bool getOptionalFloat(const XML_Node &parent, const std::string &name, doublereal &fltRtn, const std::string &type)
Get an optional floating-point value from a child element.