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)
274 Arrhenius readArrhenius(
const XML_Node& arrhenius_node)
276 return Arrhenius(
getFloat(arrhenius_node,
"A",
"toSI"),
290 std::vector<std::string> p;
293 size_t np = p.size();
294 for (
size_t n = 0; n < np; n++) {
298 int falloff_type = 0;
299 if (ba::iequals(falloff[
"type"],
"lindemann")) {
300 falloff_type = SIMPLE_FALLOFF;
302 throw CanteraError(
"readFalloff",
"Lindemann parameterization " 303 "takes no parameters, but {} were given", np);
305 }
else if (ba::iequals(falloff[
"type"],
"troe")) {
306 falloff_type = TROE_FALLOFF;
307 if (np != 3 && np != 4) {
308 throw CanteraError(
"readFalloff",
"Troe parameterization takes " 309 "3 or 4 parameters, but {} were given", np);
311 }
else if (ba::iequals(falloff[
"type"],
"sri")) {
312 falloff_type = SRI_FALLOFF;
313 if (np != 3 && np != 5) {
314 throw CanteraError(
"readFalloff",
"SRI parameterization takes " 315 "3 or 5 parameters, but {} were given", np);
318 throw CanteraError(
"readFalloff",
"Unrecognized falloff type: '{}'",
324 void readEfficiencies(ThirdBody& tbody,
const XML_Node& rc_node)
326 if (!rc_node.hasChild(
"efficiencies")) {
327 tbody.default_efficiency = 1.0;
330 const XML_Node& eff_node = rc_node.child(
"efficiencies");
331 tbody.default_efficiency =
fpValue(eff_node[
"default"]);
335 void setupReaction(Reaction& R,
const XML_Node& rxn_node)
342 std::vector<XML_Node*> orders = rxn_node.getChildren(
"order");
343 for (
size_t i = 0; i < orders.size(); i++) {
344 R.orders[orders[i]->attrib(
"species")] = orders[i]->fp_value();
348 R.id = rxn_node.attrib(
"id");
349 R.duplicate = rxn_node.hasAttrib(
"duplicate");
350 const std::string& rev = rxn_node[
"reversible"];
351 R.reversible = (rev ==
"true" || rev ==
"yes");
354 void setupElementaryReaction(ElementaryReaction& R,
const XML_Node& rxn_node)
356 const XML_Node& rc_node = rxn_node.child(
"rateCoeff");
357 if (rc_node.hasChild(
"Arrhenius")) {
358 R.rate = readArrhenius(rc_node.child(
"Arrhenius"));
359 }
else if (rc_node.hasChild(
"Arrhenius_ExchangeCurrentDensity")) {
360 R.rate = readArrhenius(rc_node.child(
"Arrhenius_ExchangeCurrentDensity"));
362 throw CanteraError(
"setupElementaryReaction",
"Couldn't find Arrhenius node");
364 if (rxn_node[
"negative_A"] ==
"yes") {
365 R.allow_negative_pre_exponential_factor =
true;
367 if (rxn_node[
"negative_orders"] ==
"yes") {
368 R.allow_negative_orders =
true;
370 if (rxn_node[
"nonreactant_orders"] ==
"yes") {
371 R.allow_nonreactant_orders =
true;
373 setupReaction(R, rxn_node);
376 void setupThreeBodyReaction(ThreeBodyReaction& R,
const XML_Node& rxn_node)
378 readEfficiencies(R.third_body, rxn_node.child(
"rateCoeff"));
379 setupElementaryReaction(R, rxn_node);
382 void setupFalloffReaction(FalloffReaction& R,
const XML_Node& rxn_node)
384 XML_Node& rc_node = rxn_node.child(
"rateCoeff");
385 std::vector<XML_Node*> rates = rc_node.getChildren(
"Arrhenius");
388 for (
size_t i = 0; i < rates.size(); i++) {
389 XML_Node& node = *rates[i];
390 if (node[
"name"] ==
"") {
391 R.high_rate = readArrhenius(node);
393 }
else if (node[
"name"] ==
"k0") {
394 R.low_rate = readArrhenius(node);
397 throw CanteraError(
"setupFalloffReaction",
"Found an Arrhenius XML " 398 "node with an unexpected type '" + node[
"name"] +
"'");
401 if (nLow != 1 || nHigh != 1) {
402 throw CanteraError(
"setupFalloffReaction",
"Did not find the correct " 403 "number of Arrhenius rate expressions");
406 readEfficiencies(R.third_body, rc_node);
407 setupReaction(R, rxn_node);
410 void setupChemicallyActivatedReaction(ChemicallyActivatedReaction& R,
411 const XML_Node& rxn_node)
413 XML_Node& rc_node = rxn_node.child(
"rateCoeff");
414 std::vector<XML_Node*> rates = rc_node.getChildren(
"Arrhenius");
417 for (
size_t i = 0; i < rates.size(); i++) {
418 XML_Node& node = *rates[i];
419 if (node[
"name"] ==
"kHigh") {
420 R.high_rate = readArrhenius(node);
422 }
else if (node[
"name"] ==
"") {
423 R.low_rate = readArrhenius(node);
426 throw CanteraError(
"setupChemicallyActivatedReaction",
"Found an " 427 "Arrhenius XML node with an unexpected type '" + node[
"name"] +
"'");
430 if (nLow != 1 || nHigh != 1) {
431 throw CanteraError(
"setupChemicallyActivatedReaction",
"Did not find " 432 "the correct number of Arrhenius rate expressions");
435 readEfficiencies(R.third_body, rc_node);
436 setupReaction(R, rxn_node);
439 void setupPlogReaction(PlogReaction& R,
const XML_Node& rxn_node)
441 XML_Node& rc = rxn_node.child(
"rateCoeff");
442 std::multimap<double, Arrhenius> rates;
443 for (
size_t m = 0; m < rc.nChildren(); m++) {
444 const XML_Node& node = rc.child(m);
445 rates.insert({
getFloat(node,
"P",
"toSI"), readArrhenius(node)});
447 R.rate = Plog(rates);
448 setupReaction(R, rxn_node);
461 size_t nP = atoi(coeff_node[
"degreeP"].c_str());
462 size_t nT = atoi(coeff_node[
"degreeT"].c_str());
467 for (
size_t t = 0; t < nT; t++) {
468 for (
size_t p = 0; p < nP; p++) {
469 coeffs(t,p) = coeffs_flat[nP*t + p];
472 R.rate = ChebyshevRate(
getFloat(rc,
"Tmin",
"toSI"),
477 setupReaction(R, rxn_node);
480 void setupInterfaceReaction(InterfaceReaction& R,
const XML_Node& rxn_node)
482 if (ba::iequals(rxn_node[
"type"],
"global")) {
485 XML_Node& arr = rxn_node.child(
"rateCoeff").child(
"Arrhenius");
486 if (ba::iequals(arr[
"type"],
"stick")) {
487 R.is_sticking_coefficient =
true;
488 R.sticking_species = arr[
"species"];
490 if (ba::iequals(arr[
"motz_wise"],
"true")) {
491 R.use_motz_wise_correction =
true;
492 }
else if (ba::iequals(arr[
"motz_wise"],
"false")) {
493 R.use_motz_wise_correction =
false;
496 XML_Node* parent = rxn_node.parent();
497 if (parent && parent->name() ==
"reactionData" 498 && ba::iequals((*parent)[
"motz_wise"],
"true")) {
499 R.use_motz_wise_correction =
true;
503 std::vector<XML_Node*> cov = arr.getChildren(
"coverage");
504 for (
const auto& node : cov) {
505 CoverageDependency& cdep = R.coverage_deps[node->attrib(
"species")];
506 cdep.a =
getFloat(*node,
"a",
"toSI");
510 setupElementaryReaction(R, rxn_node);
513 void setupElectrochemicalReaction(ElectrochemicalReaction& R,
514 const XML_Node& rxn_node)
517 std::string type = ba::to_lower_copy(rxn_node[
"type"]);
518 if (type ==
"butlervolmer") {
520 }
else if (type ==
"butlervolmer_noactivitycoeffs") {
522 }
else if (type ==
"surfaceaffinity") {
524 }
else if (type ==
"global") {
528 XML_Node& rc = rxn_node.
child(
"rateCoeff");
529 std::string rc_type = ba::to_lower_copy(rc[
"type"]);
530 if (rc_type ==
"exchangecurrentdensity") {
531 R.exchange_current_density_formulation =
true;
532 }
else if (rc_type !=
"" && rc_type !=
"arrhenius") {
533 throw CanteraError(
"setupElectrochemicalReaction",
534 "Unknown rate coefficient type: '" + rc_type +
"'");
536 if (rc.hasChild(
"Arrhenius_ExchangeCurrentDensity")) {
537 R.exchange_current_density_formulation =
true;
540 if (rc.hasChild(
"electrochem") && rc.child(
"electrochem").hasAttrib(
"beta")) {
545 setupInterfaceReaction(R, rxn_node);
551 throw CanteraError(
"setupElectrochemicalReaction",
552 "A Butler-Volmer reaction must be reversible");
557 R.allow_nonreactant_orders =
true;
558 for (
const auto& sp : R.reactants) {
559 R.orders[sp.first] += sp.second * (1.0 - R.beta);
561 for (
const auto& sp : R.products) {
562 R.orders[sp.first] += sp.second * R.beta;
567 if (rxn_node.hasChild(
"reactionOrderFormulation")) {
570 R.allow_nonreactant_orders =
true;
571 const XML_Node& rof_node = rxn_node.child(
"reactionOrderFormulation");
572 if (ba::iequals(rof_node[
"model"],
"reactantorders")) {
573 R.orders = initial_orders;
574 }
else if (ba::iequals(rof_node[
"model"],
"zeroorders")) {
575 for (
const auto& sp : R.reactants) {
576 R.orders[sp.first] = 0.0;
578 }
else if (ba::iequals(rof_node[
"model"],
"butlervolmerorders")) {
580 for (
const auto& sp : R.reactants) {
581 double c =
getValue(initial_orders, sp.first, sp.second);
582 R.orders[sp.first] += c * (1.0 - R.beta);
584 for (
const auto& sp : R.products) {
585 double c =
getValue(initial_orders, sp.first, sp.second);
586 R.orders[sp.first] += c * R.beta;
589 throw CanteraError(
"setupElectrochemicalReaction",
"unknown model " 590 "for reactionOrderFormulation XML_Node: '" +
591 rof_node[
"model"] +
"'");
596 if (rxn_node.hasChild(
"orders")) {
598 for (
const auto& order : orders) {
599 R.orders[order.first] = order.second;
606 std::string type = ba::to_lower_copy(rxn_node[
"type"]);
610 if (rxn_node.
child(
"rateCoeff").
hasChild(
"electrochem") && type ==
"edge") {
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...
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...
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.
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) ...
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...
const U & getValue(const std::map< T, U > &m, const T &key)
Const accessor for a value in a std::map.
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.
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.