Cantera 2.6.0
Reaction.cpp
Go to the documentation of this file.
1/**
2 * @file Reaction.cpp
3 */
4
5// This file is part of Cantera. See License.txt in the top-level directory or
6// at https://cantera.org/license.txt for license and copyright information.
7
9#include "ReactionFactory.h"
15#include "cantera/base/ctml.h"
16#include "cantera/base/Array.h"
17#include "cantera/base/AnyMap.h"
20#include <boost/algorithm/string/predicate.hpp>
21#include <sstream>
22#include <set>
23
24#include <boost/algorithm/string.hpp>
25
26namespace ba = boost::algorithm;
27
28namespace Cantera
29{
30
31Reaction::Reaction()
32 : reaction_type(NONE)
33 , reversible(true)
34 , duplicate(false)
35 , allow_nonreactant_orders(false)
36 , allow_negative_orders(false)
37 , rate_units(0.0)
38 , m_valid(true)
39{
40}
41
42Reaction::Reaction(const Composition& reactants_,
43 const Composition& products_,
44 shared_ptr<ReactionRate> rate_)
45 : reaction_type(NONE)
46 , reactants(reactants_)
47 , products(products_)
48 , reversible(true)
49 , duplicate(false)
50 , allow_nonreactant_orders(false)
51 , allow_negative_orders(false)
52 , rate_units(0.0)
53 , m_valid(true)
54 , m_rate(rate_)
55{
56}
57
58Reaction::Reaction(const AnyMap& node, const Kinetics& kin)
59 : Reaction()
60{
61 setParameters(node, kin);
62 if (kin.nPhases()) {
63 size_t nDim = kin.thermo(kin.reactionPhaseIndex()).nDim();
64 if (nDim == 3) {
66 } else {
67 AnyMap rateNode = node;
68 if (!rateNode.hasKey("type")) {
69 // Reaction type is not specified
70 rateNode["type"] = "Arrhenius";
71 }
72 std::string type = rateNode["type"].asString();
73 if (rateNode.hasKey("rate-constant")) {
74 if (!boost::algorithm::starts_with(type, "interface-")) {
75 rateNode["type"] = "interface-" + type;
76 }
77 } else if (node.hasKey("sticking-coefficient")) {
78 if (!boost::algorithm::starts_with(type, "sticking-")) {
79 rateNode["type"] = "sticking-" + type;
80 }
81 } else {
82 throw InputFileError("Reaction::Reaction", input,
83 "Unable to infer interface reaction type.");
84 }
86 }
87 } else {
88 // @deprecated This route is only used for legacy reaction types.
90 }
91}
92
93Reaction::Reaction(int type)
94 : reaction_type(type)
95 , reversible(true)
96 , duplicate(false)
97 , allow_nonreactant_orders(false)
98 , allow_negative_orders(false)
99 , rate_units(0.0)
100 , m_valid(true)
101{
102 warn_deprecated("Reaction::Reaction()",
103 "To be removed after Cantera 2.6. Use constructor without parameter "
104 "'type' instead.");
105}
106
107Reaction::Reaction(int type, const Composition& reactants_,
108 const Composition& products_)
109 : reaction_type(type)
110 , reactants(reactants_)
111 , products(products_)
112 , reversible(true)
113 , duplicate(false)
114 , allow_nonreactant_orders(false)
115 , allow_negative_orders(false)
116 , rate_units(0.0)
117 , m_valid(true)
118{
119 warn_deprecated("Reaction::Reaction()",
120 "To be removed after Cantera 2.6. Use constructor without parameter "
121 "'type' instead.");
122}
123
125{
127 for (const auto& order : orders) {
128 if (reactants.find(order.first) == reactants.end()) {
129 throw InputFileError("Reaction::validate", input,
130 "Reaction order specified for non-reactant species '{}'",
131 order.first);
132 }
133 }
134 }
135
137 for (const auto& order : orders) {
138 if (order.second < 0.0) {
139 throw InputFileError("Reaction::validate", input,
140 "Negative reaction order specified for species '{}'",
141 order.first);
142 }
143 }
144 }
145
146 // If reaction orders are specified, then this reaction does not follow
147 // mass-action kinetics, and is not an elementary reaction. So check that it
148 // is not reversible, since computing the reverse rate from thermochemistry
149 // only works for elementary reactions.
150 if (reversible && !orders.empty()) {
151 throw InputFileError("Reaction::validate", input,
152 "Reaction orders may only be given for irreversible reactions");
153 }
154
155 // Call validation of reaction rate evaluator
156 if (!usesLegacy()) {
157 m_rate->check(equation(), input);
158 m_rate->validate(equation());
159 }
160}
161
162AnyMap Reaction::parameters(bool withInput) const
163{
164 AnyMap out;
165 getParameters(out);
166 if (withInput) {
167 out.update(input);
168 }
169
170 static bool reg = AnyMap::addOrderingRules("Reaction",
171 {{"head", "type"},
172 {"head", "equation"},
173 {"tail", "duplicate"},
174 {"tail", "orders"},
175 {"tail", "negative-orders"},
176 {"tail", "nonreactant-orders"}
177 });
178 if (reg) {
179 out["__type__"] = "Reaction";
180 }
181 return out;
182}
183
184void Reaction::getParameters(AnyMap& reactionNode) const
185{
186 reactionNode["equation"] = equation();
187
188 if (duplicate) {
189 reactionNode["duplicate"] = true;
190 }
191 if (orders.size()) {
192 reactionNode["orders"] = orders;
193 }
195 reactionNode["negative-orders"] = true;
196 }
198 reactionNode["nonreactant-orders"] = true;
199 }
200
201 if (m_rate) {
202 reactionNode.update(m_rate->parameters());
203
204 // strip information not needed for reconstruction
205 if (reactionNode.hasKey("type")) {
206 std::string type = reactionNode["type"].asString();
207 if (boost::algorithm::starts_with(type, "Arrhenius")) {
208 reactionNode.erase("type");
209 } else if (boost::algorithm::starts_with(type, "Blowers-Masel")) {
210 reactionNode["type"] = "Blowers-Masel";
211 }
212 }
213 }
214}
215
216void Reaction::setParameters(const AnyMap& node, const Kinetics& kin)
217{
218 if (node.empty()) {
219 // empty node: used by newReaction() factory loader
220 return;
221 }
222
223 input = node;
224 input.copyMetadata(node);
225 setEquation(node["equation"].asString(), &kin);
226 // Non-stoichiometric reaction orders
227 if (node.hasKey("orders")) {
228 for (const auto& order : node["orders"].asMap<double>()) {
229 orders[order.first] = order.second;
230 if (kin.kineticsSpeciesIndex(order.first) == npos) {
231 setValid(false);
232 }
233 }
234 }
235
236 // Flags
237 id = node.getString("id", "");
238 duplicate = node.getBool("duplicate", false);
239 allow_negative_orders = node.getBool("negative-orders", false);
240 allow_nonreactant_orders = node.getBool("nonreactant-orders", false);
241}
242
243void Reaction::setRate(shared_ptr<ReactionRate> rate)
244{
245 if (!rate) {
246 // null pointer
247 m_rate.reset();
248 } else {
249 m_rate = rate;
250 }
251
252 if (reactants.count("(+M)") && std::dynamic_pointer_cast<ChebyshevRate>(m_rate)) {
253 warn_deprecated("Chebyshev reaction equation", input, "Specifying '(+M)' "
254 "in the reaction equation for Chebyshev reactions is deprecated.");
255 // remove optional third body notation
256 reactants.erase("(+M)");
257 products.erase("(+M)");
258 }
259
260 if (reactants.count("M") && std::dynamic_pointer_cast<PlogRate>(m_rate)) {
261 throw InputFileError("Reaction::setRate", input, "Found superfluous 'M' in "
262 "pressure-dependent-Arrhenius reaction.");
263 }
264}
265
266std::string Reaction::reactantString() const
267{
268 std::ostringstream result;
269 for (auto iter = reactants.begin(); iter != reactants.end(); ++iter) {
270 if (iter != reactants.begin()) {
271 result << " + ";
272 }
273 if (iter->second != 1.0) {
274 result << iter->second << " ";
275 }
276 result << iter->first;
277 }
278 return result.str();
279}
280
281std::string Reaction::productString() const
282{
283 std::ostringstream result;
284 for (auto iter = products.begin(); iter != products.end(); ++iter) {
285 if (iter != products.begin()) {
286 result << " + ";
287 }
288 if (iter->second != 1.0) {
289 result << iter->second << " ";
290 }
291 result << iter->first;
292 }
293 return result.str();
294}
295
296std::string Reaction::equation() const
297{
298 if (reversible) {
299 return reactantString() + " <=> " + productString();
300 } else {
301 return reactantString() + " => " + productString();
302 }
303}
304
305void Reaction::setEquation(const std::string& equation, const Kinetics* kin)
306{
308}
309
310std::string Reaction::type() const
311{
312 return "reaction";
313}
314
316{
317 if (!valid()) {
318 // If a reaction is invalid because of missing species in the Kinetics
319 // object, determining the units of the rate coefficient is impossible.
320 return;
321 }
322
323 // Determine the units of the rate coefficient
324 Units rxn_phase_units = kin.thermo(kin.reactionPhaseIndex()).standardConcentrationUnits();
325 rate_units = rxn_phase_units;
326 rate_units *= Units(1.0, 0, 0, -1);
327 for (const auto& order : orders) {
328 const auto& phase = kin.speciesPhase(order.first);
329 rate_units *= phase.standardConcentrationUnits().pow(-order.second);
330 }
331 for (const auto& stoich : reactants) {
332 // Order for each reactant is the reactant stoichiometric coefficient,
333 // unless already overridden by user-specified orders
334 if (stoich.first == "M" || ba::starts_with(stoich.first, "(+")) {
335 // calculateRateCoeffUnits may be called before these pseudo-species
336 // have been stripped from the reactants
337 continue;
338 } else if (orders.find(stoich.first) == orders.end()) {
339 const auto& phase = kin.speciesPhase(stoich.first);
340 rate_units *= phase.standardConcentrationUnits().pow(-stoich.second);
341 }
342 }
343}
344
346{
347 if (!valid()) {
348 // If a reaction is invalid because of missing species in the Kinetics
349 // object, determining the units of the rate coefficient is impossible.
350 return UnitStack({});
351 }
352
353 // Determine the units of the rate coefficient
354 const auto& rxn_phase = kin.thermo(kin.reactionPhaseIndex());
355 UnitStack rate_units(rxn_phase.standardConcentrationUnits());
356
357 // Set output units to standardConcentrationUnits per second
358 rate_units.join(1.);
359 rate_units.update(Units(1.0, 0, 0, -1), 1.);
360
361 for (const auto& order : orders) {
362 const auto& phase = kin.speciesPhase(order.first);
363 // Account for specified reaction orders
364 rate_units.update(phase.standardConcentrationUnits(), -order.second);
365 }
366 for (const auto& stoich : reactants) {
367 // Order for each reactant is the reactant stoichiometric coefficient,
368 // unless already overridden by user-specified orders
369 if (stoich.first == "M" || ba::starts_with(stoich.first, "(+")) {
370 // calculateRateCoeffUnits may be called before these pseudo-species
371 // have been stripped from the reactants
372 continue;
373 } else if (orders.find(stoich.first) == orders.end()) {
374 const auto& phase = kin.speciesPhase(stoich.first);
375 // Account for each reactant species
376 rate_units.update(phase.standardConcentrationUnits(), -stoich.second);
377 }
378 }
379
380 if (m_third_body) {
381 // Account for third-body collision partner as the last entry
382 rate_units.join(-1);
383 }
384
385 return rate_units;
386}
387
388void updateUndeclared(std::vector<std::string>& undeclared,
389 const Composition& comp, const Kinetics& kin)
390{
391 for (const auto& sp: comp) {
392 if (kin.kineticsSpeciesIndex(sp.first) == npos) {
393 undeclared.emplace_back(sp.first);
394 }
395 }
396}
397
398std::pair<std::vector<std::string>, bool> Reaction::undeclaredThirdBodies(
399 const Kinetics& kin) const
400{
401 std::vector<std::string> undeclared;
402 if (m_third_body) {
403 updateUndeclared(undeclared, m_third_body->efficiencies, kin);
404 return std::make_pair(undeclared, m_third_body->specified_collision_partner);
405 }
406 return std::make_pair(undeclared, false);
407}
408
409void Reaction::checkBalance(const Kinetics& kin) const
410{
411 Composition balr, balp;
412
413 // iterate over products and reactants
414 for (const auto& sp : products) {
415 const ThermoPhase& ph = kin.speciesPhase(sp.first);
416 size_t k = ph.speciesIndex(sp.first);
417 double stoich = sp.second;
418 for (size_t m = 0; m < ph.nElements(); m++) {
419 balr[ph.elementName(m)] = 0.0; // so that balr contains all species
420 balp[ph.elementName(m)] += stoich * ph.nAtoms(k, m);
421 }
422 }
423 for (const auto& sp : reactants) {
424 const ThermoPhase& ph = kin.speciesPhase(sp.first);
425 size_t k = ph.speciesIndex(sp.first);
426 double stoich = sp.second;
427 for (size_t m = 0; m < ph.nElements(); m++) {
428 balr[ph.elementName(m)] += stoich * ph.nAtoms(k, m);
429 }
430 }
431
432 std::string msg;
433 bool ok = true;
434 for (const auto& el : balr) {
435 const std::string& elem = el.first;
436 double elemsum = balr[elem] + balp[elem];
437 double elemdiff = fabs(balp[elem] - balr[elem]);
438 if (elemsum > 0.0 && elemdiff / elemsum > 1e-4) {
439 ok = false;
440 msg += fmt::format(" {} {} {}\n",
441 elem, balr[elem], balp[elem]);
442 }
443 }
444 if (!ok) {
445 throw InputFileError("Reaction::checkBalance", input,
446 "The following reaction is unbalanced: {}\n"
447 " Element Reactants Products\n{}",
448 equation(), msg);
449 }
450
451 if (kin.thermo(kin.reactionPhaseIndex()).nDim() == 3) {
452 return;
453 }
454
455 // Check that the number of surface sites is balanced
456 double reac_sites = 0.0;
457 double prod_sites = 0.0;
458 auto& surf = dynamic_cast<const SurfPhase&>(kin.thermo(kin.surfacePhaseIndex()));
459 for (const auto& reactant : reactants) {
460 size_t k = surf.speciesIndex(reactant.first);
461 if (k != npos) {
462 reac_sites += reactant.second * surf.size(k);
463 }
464 }
465 for (const auto& product : products) {
466 size_t k = surf.speciesIndex(product.first);
467 if (k != npos) {
468 prod_sites += product.second * surf.size(k);
469 }
470 }
471 if (fabs(reac_sites - prod_sites) > 1e-5 * (reac_sites + prod_sites)) {
472 throw InputFileError("Reaction::checkBalance", input,
473 "Number of surface sites not balanced in reaction {}.\n"
474 "Reactant sites: {}\nProduct sites: {}",
475 equation(), reac_sites, prod_sites);
476 }
477}
478
479bool Reaction::checkSpecies(const Kinetics& kin) const
480{
481 // Check for undeclared species
482 std::vector<std::string> undeclared;
483 updateUndeclared(undeclared, reactants, kin);
484 updateUndeclared(undeclared, products, kin);
485 if (!undeclared.empty()) {
486 if (kin.skipUndeclaredSpecies()) {
487 return false;
488 } else {
489 throw InputFileError("Reaction::checkSpecies", input, "Reaction '{}'\n"
490 "contains undeclared species: '{}'",
491 equation(), boost::algorithm::join(undeclared, "', '"));
492 }
493 }
494
495 undeclared.clear();
496 updateUndeclared(undeclared, orders, kin);
497 if (!undeclared.empty()) {
498 if (kin.skipUndeclaredSpecies()) {
499 return false;
500 } else {
501 if (input.hasKey("orders")) {
502 throw InputFileError("Reaction::checkSpecies", input["orders"],
503 "Reaction '{}'\n"
504 "defines reaction orders for undeclared species: '{}'",
505 equation(), boost::algorithm::join(undeclared, "', '"));
506 }
507 // Error for empty input AnyMap (that is, XML)
508 throw InputFileError("Reaction::checkSpecies", input, "Reaction '{}'\n"
509 "defines reaction orders for undeclared species: '{}'",
510 equation(), boost::algorithm::join(undeclared, "', '"));
511 }
512 }
513
514 // Use helper function while there is no uniform handling of third bodies
515 auto third = undeclaredThirdBodies(kin);
516 undeclared = third.first;
517 bool specified_collision_partner_ = third.second;
518 if (!undeclared.empty()) {
519 if (!kin.skipUndeclaredThirdBodies()) {
520 if (input.hasKey("efficiencies")) {
521 throw InputFileError("Reaction::checkSpecies", input["efficiencies"],
522 "Reaction '{}'\n"
523 "defines third-body efficiencies for undeclared species: '{}'",
524 equation(), boost::algorithm::join(undeclared, "', '"));
525 }
526 // Error for specified ThirdBody or empty input AnyMap
527 throw InputFileError("Reaction::checkSpecies", input, "Reaction '{}'\n"
528 "is a three-body reaction with undeclared species: '{}'",
529 equation(), boost::algorithm::join(undeclared, "', '"));
530 } else if (kin.skipUndeclaredSpecies() && specified_collision_partner_) {
531 return false;
532 }
533 }
534
535 checkBalance(kin);
536
537 return true;
538}
539
541{
542 // Check electrochemistry
543 vector_fp e_counter(kin.nPhases(), 0.0);
544
545 // Find the number of electrons in the products for each phase
546 for (const auto& sp : products) {
547 size_t kkin = kin.kineticsSpeciesIndex(sp.first);
548 size_t i = kin.speciesPhaseIndex(kkin);
549 size_t kphase = kin.thermo(i).speciesIndex(sp.first);
550 e_counter[i] += sp.second * kin.thermo(i).charge(kphase);
551 }
552
553 // Subtract the number of electrons in the reactants for each phase
554 for (const auto& sp : reactants) {
555 size_t kkin = kin.kineticsSpeciesIndex(sp.first);
556 size_t i = kin.speciesPhaseIndex(kkin);
557 size_t kphase = kin.thermo(i).speciesIndex(sp.first);
558 e_counter[i] -= sp.second * kin.thermo(i).charge(kphase);
559 }
560
561 // If the electrons change phases then the reaction is electrochemical
562 for (double delta_e : e_counter) {
563 if (std::abs(delta_e) > 1e-4) {
564 return true;
565 }
566 }
567
568 return false;
569}
570
571ElementaryReaction2::ElementaryReaction2(const Composition& reactants_,
572 const Composition products_,
573 const Arrhenius2& rate_)
574 : Reaction(reactants_, products_)
575 , rate(rate_)
576 , allow_negative_pre_exponential_factor(false)
577{
579}
580
581ElementaryReaction2::ElementaryReaction2()
582 : Reaction()
583 , allow_negative_pre_exponential_factor(false)
584{
586}
587
589{
591 if (!allow_negative_pre_exponential_factor &&
592 rate.preExponentialFactor() < 0) {
593 throw InputFileError("ElementaryReaction2::validate", input,
594 "Undeclared negative pre-exponential factor found in reaction '"
595 + equation() + "'");
596 }
597}
598
600{
601 Reaction::getParameters(reactionNode);
602 if (allow_negative_pre_exponential_factor) {
603 reactionNode["negative-A"] = true;
604 }
605 AnyMap rateNode;
606 rate.getParameters(rateNode, rate_units);
607 reactionNode["rate-constant"] = std::move(rateNode);
608}
609
610ThirdBody::ThirdBody(double default_eff)
611 : default_efficiency(default_eff)
612 , specified_collision_partner(false)
613 , mass_action(true)
614{
615}
616
617ThirdBody::ThirdBody(const AnyMap& node)
618 : specified_collision_partner(false)
619 , mass_action(true)
620{
621 setEfficiencies(node);
622}
623
625{
626 default_efficiency = node.getDouble("default-efficiency", 1.0);
627 if (node.hasKey("efficiencies")) {
628 efficiencies = node["efficiencies"].asMap<double>();
629 }
630}
631
632double ThirdBody::efficiency(const std::string& k) const
633{
635}
636
637ThreeBodyReaction2::ThreeBodyReaction2()
638{
640}
641
642ThreeBodyReaction2::ThreeBodyReaction2(const Composition& reactants_,
643 const Composition& products_,
644 const Arrhenius2& rate_,
645 const ThirdBody& tbody)
646 : ElementaryReaction2(reactants_, products_, rate_)
647 , third_body(tbody)
648{
649 reaction_type = THREE_BODY_RXN;
650}
651
653{
656 + third_body.efficiencies.begin()->first;
657 } else {
658 return ElementaryReaction2::reactantString() + " + M";
659 }
660}
661
663{
666 + third_body.efficiencies.begin()->first;
667 } else {
668 return ElementaryReaction2::productString() + " + M";
669 }
670}
671
673{
675 bool specified_collision_partner_ = false;
676 for (const auto& reac : reactants) {
677 // While this reaction was already identified as a three-body reaction in a
678 // pre-processing step, this method is often called before a three-body
679 // reaction is fully instantiated. For the determination of the correct units,
680 // it is necessary to check whether the reaction uses a generic 'M' or an
681 // explicitly specified collision partner that may not have been deleted yet.
682 if (reac.first != "M" && products.count(reac.first)) {
683 // detected specified third-body collision partner
684 specified_collision_partner_ = true;
685 }
686 }
687 if (!specified_collision_partner_) {
688 const ThermoPhase& rxn_phase = kin.thermo(kin.reactionPhaseIndex());
689 rate_units *= rxn_phase.standardConcentrationUnits().pow(-1);
690 }
691}
692
694{
697 reactionNode["type"] = "three-body";
698 reactionNode["efficiencies"] = third_body.efficiencies;
699 reactionNode["efficiencies"].setFlowStyle();
700 if (third_body.default_efficiency != 1.0) {
701 reactionNode["default-efficiency"] = third_body.default_efficiency;
702 }
703 }
704}
705
706std::pair<std::vector<std::string>, bool> ThreeBodyReaction2::undeclaredThirdBodies(
707 const Kinetics& kin) const
708{
709 std::vector<std::string> undeclared;
710 updateUndeclared(undeclared, third_body.efficiencies, kin);
711 return std::make_pair(undeclared, third_body.specified_collision_partner);
712}
713
714FalloffReaction2::FalloffReaction2()
715 : Reaction()
716 , falloff(new Lindemann())
717 , allow_negative_pre_exponential_factor(false)
718 , low_rate_units(0.0)
719{
721}
722
723FalloffReaction2::FalloffReaction2(
724 const Composition& reactants_, const Composition& products_,
725 const Arrhenius2& low_rate_, const Arrhenius2& high_rate_,
726 const ThirdBody& tbody)
727 : Reaction(reactants_, products_)
728 , low_rate(low_rate_)
729 , high_rate(high_rate_)
730 , third_body(tbody)
731 , falloff(new Lindemann())
732 , allow_negative_pre_exponential_factor(false)
733 , low_rate_units(0.0)
734{
736}
737
739{
741 third_body.efficiencies.size() == 1) {
742 return Reaction::reactantString() + " (+" +
743 third_body.efficiencies.begin()->first + ")";
744 } else {
745 return Reaction::reactantString() + " (+M)";
746 }
747}
748
750{
752 third_body.efficiencies.size() == 1) {
753 return Reaction::productString() + " (+" +
754 third_body.efficiencies.begin()->first + ")";
755 } else {
756 return Reaction::productString() + " (+M)";
757 }
758}
759
761{
763 if (!allow_negative_pre_exponential_factor &&
766 throw InputFileError("FalloffReaction2::validate", input, "Negative "
767 "pre-exponential factor found for reaction '" + equation() + "'");
768 }
770 throw InputFileError("FalloffReaction2::validate", input, "High and "
771 "low rate pre-exponential factors must have the same sign."
772 "Reaction: '{}'", equation());
773 }
774}
775
777{
779 const ThermoPhase& rxn_phase = kin.thermo(kin.reactionPhaseIndex());
782}
783
785{
786 Reaction::getParameters(reactionNode);
787 reactionNode["type"] = "falloff-legacy";
788 AnyMap lowRateNode;
790 reactionNode["low-P-rate-constant"] = std::move(lowRateNode);
791 AnyMap highRateNode;
792 high_rate.getParameters(highRateNode, rate_units);
793 reactionNode["high-P-rate-constant"] = std::move(highRateNode);
794 falloff->getParameters(reactionNode);
795
796 reactionNode["efficiencies"] = third_body.efficiencies;
797 reactionNode["efficiencies"].setFlowStyle();
798 if (third_body.default_efficiency != 1.0) {
799 reactionNode["default-efficiency"] = third_body.default_efficiency;
800 }
801}
802
803std::pair<std::vector<std::string>, bool> FalloffReaction2::undeclaredThirdBodies(
804 const Kinetics& kin) const
805{
806 std::vector<std::string> undeclared;
807 updateUndeclared(undeclared, third_body.efficiencies, kin);
808 return std::make_pair(undeclared, false);
809}
810
811ChemicallyActivatedReaction2::ChemicallyActivatedReaction2()
812{
814}
815
816ChemicallyActivatedReaction2::ChemicallyActivatedReaction2(
817 const Composition& reactants_, const Composition& products_,
818 const Arrhenius2& low_rate_, const Arrhenius2& high_rate_,
819 const ThirdBody& tbody)
820 : FalloffReaction2(reactants_, products_, low_rate_, high_rate_, tbody)
821{
823}
824
826{
827 Reaction::calculateRateCoeffUnits(kin); // Skip FalloffReaction2
828 const ThermoPhase& rxn_phase = kin.thermo(kin.reactionPhaseIndex());
831}
832
834{
836 reactionNode["type"] = "chemically-activated";
837}
838
839PlogReaction2::PlogReaction2()
840 : Reaction()
841{
843}
844
845PlogReaction2::PlogReaction2(const Composition& reactants_,
846 const Composition& products_, const Plog& rate_)
847 : Reaction(reactants_, products_)
848 , rate(rate_)
849{
851}
852
853void PlogReaction2::getParameters(AnyMap& reactionNode) const
854{
855 Reaction::getParameters(reactionNode);
856 reactionNode["type"] = "pressure-dependent-Arrhenius";
857 rate.getParameters(reactionNode, rate_units);
858}
859
860ChebyshevReaction2::ChebyshevReaction2()
861 : Reaction()
862{
864}
865
866ChebyshevReaction2::ChebyshevReaction2(const Composition& reactants_,
867 const Composition& products_,
868 const ChebyshevRate& rate_)
869 : Reaction(reactants_, products_)
870 , rate(rate_)
871{
873}
874
876{
877 Reaction::getParameters(reactionNode);
878 reactionNode["type"] = "Chebyshev";
879 rate.getParameters(reactionNode, rate_units);
880}
881
882InterfaceReaction2::InterfaceReaction2()
883 : is_sticking_coefficient(false)
884 , use_motz_wise_correction(false)
885{
887}
888
889InterfaceReaction2::InterfaceReaction2(const Composition& reactants_,
890 const Composition& products_,
891 const Arrhenius2& rate_,
892 bool isStick)
893 : ElementaryReaction2(reactants_, products_, rate_)
894 , is_sticking_coefficient(isStick)
895 , use_motz_wise_correction(false)
896{
898}
899
901{
903 if (is_sticking_coefficient || input.hasKey("sticking-coefficient")) {
904 rate_units = Units(1.0); // sticking coefficients are dimensionless
905 }
906}
907
909{
912 reactionNode["sticking-coefficient"] = std::move(reactionNode["rate-constant"]);
913 reactionNode.erase("rate-constant");
914 }
916 reactionNode["Motz-Wise"] = true;
917 }
918 if (!sticking_species.empty()) {
919 reactionNode["sticking-species"] = sticking_species;
920 }
921 if (!coverage_deps.empty()) {
922 AnyMap deps;
923 for (const auto& d : coverage_deps) {
924 AnyMap dep;
925 dep["a"] = d.second.a;
926 dep["m"] = d.second.m;
927 dep["E"].setQuantity(d.second.E, "K", true);
928 deps[d.first] = std::move(dep);
929 }
930 reactionNode["coverage-dependencies"] = std::move(deps);
931 }
932}
933
935{
937 fmt::memory_buffer err_reactions;
938 double T[] = {200.0, 500.0, 1000.0, 2000.0, 5000.0, 10000.0};
939 for (size_t i=0; i < 6; i++) {
940 double k = rate.updateRC(log(T[i]), 1/T[i]);
941 if (k > 1) {
942 fmt_append(err_reactions,
943 "\n Sticking coefficient is greater than 1 for reaction '{}'\n"
944 " at T = {:.1f}\n",
945 equation(), T[i]);
946 }
947 }
948 if (err_reactions.size()) {
949 warn_user("InterfaceReaction2::validate", to_string(err_reactions));
950 }
951 }
952}
953
954ElectrochemicalReaction2::ElectrochemicalReaction2()
955 : beta(0.5)
956 , exchange_current_density_formulation(false)
957{
958}
959
960ElectrochemicalReaction2::ElectrochemicalReaction2(const Composition& reactants_,
961 const Composition& products_,
962 const Arrhenius2& rate_)
963 : InterfaceReaction2(reactants_, products_, rate_)
964 , beta(0.5)
965 , exchange_current_density_formulation(false)
966{
967}
968
970{
972 if (beta != 0.5) {
973 reactionNode["beta"] = beta;
974 }
975 if (exchange_current_density_formulation) {
976 reactionNode["exchange-current-density-formulation"] = true;
977 }
978}
979
980ThreeBodyReaction3::ThreeBodyReaction3()
981{
982 m_third_body.reset(new ThirdBody);
984}
985
986ThreeBodyReaction3::ThreeBodyReaction3(const Composition& reactants,
987 const Composition& products,
988 const ArrheniusRate& rate,
989 const ThirdBody& tbody)
990 : Reaction(reactants, products, make_shared<ArrheniusRate>(rate))
991{
992 m_third_body = std::make_shared<ThirdBody>(tbody);
993}
994
995ThreeBodyReaction3::ThreeBodyReaction3(const AnyMap& node, const Kinetics& kin)
996{
997 m_third_body.reset(new ThirdBody);
998 if (!node.empty()) {
999 setParameters(node, kin);
1001 } else {
1003 }
1004}
1005
1006bool ThreeBodyReaction3::detectEfficiencies()
1007{
1008 for (const auto& reac : reactants) {
1009 // detect explicitly specified collision partner
1010 if (products.count(reac.first)) {
1011 m_third_body->efficiencies[reac.first] = 1.;
1012 }
1013 }
1014
1015 if (m_third_body->efficiencies.size() == 0) {
1016 return false;
1017 } else if (m_third_body->efficiencies.size() > 1) {
1018 throw CanteraError("ThreeBodyReaction3::detectEfficiencies",
1019 "Found more than one explicitly specified collision partner\n"
1020 "in reaction '{}'.", equation());
1021 }
1022
1023 m_third_body->default_efficiency = 0.;
1024 m_third_body->specified_collision_partner = true;
1025 auto sp = m_third_body->efficiencies.begin();
1026
1027 // adjust reactant coefficients
1028 auto reac = reactants.find(sp->first);
1029 if (trunc(reac->second) != 1) {
1030 reac->second -= 1.;
1031 } else {
1032 reactants.erase(reac);
1033 }
1034
1035 // adjust product coefficients
1036 auto prod = products.find(sp->first);
1037 if (trunc(prod->second) != 1) {
1038 prod->second -= 1.;
1039 } else {
1040 products.erase(prod);
1041 }
1042
1043 return true;
1044}
1045
1046void ThreeBodyReaction3::setEquation(const std::string& equation, const Kinetics* kin)
1047{
1049 if (reactants.count("M") != 1 || products.count("M") != 1) {
1050 if (!detectEfficiencies()) {
1051 throw InputFileError("ThreeBodyReaction3::setParameters", input,
1052 "Reaction equation '{}' does not contain third body 'M'",
1053 equation);
1054 }
1055 return;
1056 }
1057
1058 reactants.erase("M");
1059 products.erase("M");
1060}
1061
1063{
1064 if (node.empty()) {
1065 // empty node: used by newReaction() factory loader
1066 return;
1067 }
1068 Reaction::setParameters(node, kin);
1069 if (!m_third_body->specified_collision_partner) {
1070 m_third_body->setEfficiencies(node);
1071 }
1072}
1073
1075{
1076 Reaction::getParameters(reactionNode);
1077 if (!m_third_body->specified_collision_partner) {
1078 reactionNode["type"] = "three-body";
1079 reactionNode["efficiencies"] = m_third_body->efficiencies;
1080 reactionNode["efficiencies"].setFlowStyle();
1081 if (m_third_body->default_efficiency != 1.0) {
1082 reactionNode["default-efficiency"] = m_third_body->default_efficiency;
1083 }
1084 }
1085}
1086
1088{
1089 if (m_third_body->specified_collision_partner) {
1090 return Reaction::reactantString() + " + "
1091 + m_third_body->efficiencies.begin()->first;
1092 } else {
1093 return Reaction::reactantString() + " + M";
1094 }
1095}
1096
1098{
1099 if (m_third_body->specified_collision_partner) {
1100 return Reaction::productString() + " + "
1101 + m_third_body->efficiencies.begin()->first;
1102 } else {
1103 return Reaction::productString() + " + M";
1104 }
1105}
1106
1107FalloffReaction3::FalloffReaction3()
1108 : Reaction()
1109{
1110 m_third_body.reset(new ThirdBody);
1111 m_third_body->mass_action = false;
1113}
1114
1115FalloffReaction3::FalloffReaction3(const Composition& reactants,
1116 const Composition& products,
1117 const ReactionRate& rate,
1118 const ThirdBody& tbody)
1119 : Reaction(reactants, products)
1120{
1121 m_third_body = std::make_shared<ThirdBody>(tbody);
1122 m_third_body->mass_action = false;
1123 AnyMap node = rate.parameters();
1124 node.applyUnits();
1125 std::string rate_type = node["type"].asString();
1126 if (rate_type != "falloff" && rate_type != "chemically-activated") {
1127 // use node information to determine whether rate is a falloff rate
1128 throw CanteraError("FalloffReaction3::FalloffReaction3",
1129 "Incompatible types: '{}' is not a falloff rate object.", rate.type());
1130 }
1131 setRate(newReactionRate(node));
1132}
1133
1134FalloffReaction3::FalloffReaction3(const AnyMap& node, const Kinetics& kin)
1135{
1136 m_third_body.reset(new ThirdBody);
1137 m_third_body->mass_action = false;
1138 if (!node.empty()) {
1139 setParameters(node, kin);
1141 } else {
1143 }
1144}
1145
1146std::string FalloffReaction3::type() const
1147{
1148 if (m_rate &&
1149 std::dynamic_pointer_cast<FalloffRate>(m_rate)->chemicallyActivated())
1150 {
1151 return "chemically-activated";
1152 }
1153 return "falloff";
1154}
1155
1157{
1158 if (m_third_body->specified_collision_partner) {
1159 return Reaction::reactantString() + " (+" +
1160 m_third_body->efficiencies.begin()->first + ")";
1161 } else {
1162 return Reaction::reactantString() + " (+M)";
1163 }
1164}
1165
1167{
1168 if (m_third_body->specified_collision_partner) {
1169 return Reaction::productString() + " (+" +
1170 m_third_body->efficiencies.begin()->first + ")";
1171 } else {
1172 return Reaction::productString() + " (+M)";
1173 }
1174}
1175
1177{
1178 if (node.empty()) {
1179 // empty node: used by newReaction() factory loader
1180 return;
1181 }
1182 Reaction::setParameters(node, kin);
1183
1184 if (!m_third_body->specified_collision_partner) {
1185 m_third_body->setEfficiencies(node);
1186 }
1187}
1188
1189void FalloffReaction3::setEquation(const std::string& equation, const Kinetics* kin)
1190{
1192
1193 // Detect falloff third body based on partial setup;
1194 // parseReactionEquation (called via Reaction::setEquation) sets the
1195 // stoichiometric coefficient of the falloff species to -1.
1196 std::string third_body_str;
1197 std::string third_body;
1198 for (auto& reactant : reactants) {
1199 if (reactant.second == -1 && ba::starts_with(reactant.first, "(+")) {
1200 third_body_str = reactant.first;
1201 third_body = third_body_str.substr(2, third_body_str.size() - 3);
1202 break;
1203 }
1204 }
1205
1206 // Equation must contain a third body, and it must appear on both sides
1207 if (third_body_str == "") {
1208 throw InputFileError("FalloffReaction3::setParameters", input,
1209 "Reactants for reaction '{}' do not contain a pressure-dependent "
1210 "third body", equation);
1211 }
1212 if (products.count(third_body_str) == 0) {
1213 throw InputFileError("FalloffReaction3::setParameters", input,
1214 "Unable to match third body '{}' in reactants and products of "
1215 "reaction '{}'", third_body, equation);
1216 }
1217
1218 // Remove the dummy species
1219 reactants.erase(third_body_str);
1220 products.erase(third_body_str);
1221
1222 if (third_body == "M") {
1223 m_third_body->specified_collision_partner = false;
1224 } else {
1225 // Specific species is listed as the third body
1226 m_third_body->default_efficiency = 0;
1227 m_third_body->efficiencies.emplace(third_body, 1.0);
1228 m_third_body->specified_collision_partner = true;
1229 }
1230}
1231
1233{
1234 Reaction::getParameters(reactionNode);
1235 if (m_third_body->specified_collision_partner) {
1236 // pass
1237 } else if (m_third_body->efficiencies.size()) {
1238 reactionNode["efficiencies"] = m_third_body->efficiencies;
1239 reactionNode["efficiencies"].setFlowStyle();
1240 if (m_third_body->default_efficiency != 1.0) {
1241 reactionNode["default-efficiency"] = m_third_body->default_efficiency;
1242 }
1243 }
1244}
1245
1246CustomFunc1Reaction::CustomFunc1Reaction()
1247{
1249}
1250
1251CustomFunc1Reaction::CustomFunc1Reaction(const Composition& reactants,
1252 const Composition& products,
1253 const CustomFunc1Rate& rate)
1254 : Reaction(reactants, products)
1255{
1256 m_rate.reset(new CustomFunc1Rate(rate));
1257}
1258
1259CustomFunc1Reaction::CustomFunc1Reaction(const AnyMap& node, const Kinetics& kin)
1260{
1261 if (!node.empty()) {
1262 setParameters(node, kin);
1264 } else {
1266 }
1267}
1268
1269bool isThreeBody(const Reaction& R)
1270{
1271 // detect explicitly specified collision partner
1272 size_t found = 0;
1273 for (const auto& reac : R.reactants) {
1274 auto prod = R.products.find(reac.first);
1275 if (prod != R.products.end() &&
1276 trunc(reac.second) == reac.second && trunc(prod->second) == prod->second) {
1277 // candidate species with integer stoichiometric coefficients on both sides
1278 found += 1;
1279 }
1280 }
1281 if (found != 1) {
1282 return false;
1283 }
1284
1285 // ensure that all reactants have integer stoichiometric coefficients
1286 size_t nreac = 0;
1287 for (const auto& reac : R.reactants) {
1288 if (trunc(reac.second) != reac.second) {
1289 return false;
1290 }
1291 nreac += static_cast<size_t>(reac.second);
1292 }
1293
1294 // ensure that all products have integer stoichiometric coefficients
1295 size_t nprod = 0;
1296 for (const auto& prod : R.products) {
1297 if (trunc(prod.second) != prod.second) {
1298 return false;
1299 }
1300 nprod += static_cast<size_t>(prod.second);
1301 }
1302
1303 // either reactant or product side involves exactly three species
1304 return (nreac == 3) || (nprod == 3);
1305}
1306
1307unique_ptr<Reaction> newReaction(const std::string& type)
1308{
1309 AnyMap rxn_node;
1310 Kinetics kin;
1311 unique_ptr<Reaction> R(ReactionFactory::factory()->create(type, rxn_node, kin));
1312 return R;
1313}
1314
1315unique_ptr<Reaction> newReaction(const XML_Node& rxn_node)
1316{
1317 std::string type = toLowerCopy(rxn_node["type"]);
1318
1319 // Modify the reaction type for interface reactions which contain
1320 // electrochemical reaction data
1321 if (rxn_node.child("rateCoeff").hasChild("electrochem")
1322 && (type == "edge" || type == "surface")) {
1323 type = "electrochemical";
1324 }
1325
1326 if (!(ReactionFactoryXML::factory()->exists(type))) {
1327 throw CanteraError("newReaction",
1328 "Unknown reaction type '" + rxn_node["type"] + "'");
1329 }
1330 if (type.empty()) {
1331 // Reaction type is not specified
1332 // See if this is a three-body reaction with a specified collision partner
1333 ElementaryReaction2 testReaction;
1334 setupReaction(testReaction, rxn_node);
1335 if (isThreeBody(testReaction)) {
1336 type = "three-body";
1337 }
1338 }
1339 Reaction* R = ReactionFactoryXML::factory()->create(type, rxn_node);
1340 return unique_ptr<Reaction>(R);
1341}
1342
1343unique_ptr<Reaction> newReaction(const AnyMap& rxn_node, const Kinetics& kin)
1344{
1345 std::string type = "elementary";
1346 size_t nDim = kin.thermo(kin.reactionPhaseIndex()).nDim();
1347 if (rxn_node.hasKey("type")) {
1348 type = rxn_node["type"].asString();
1349 } else if (nDim == 3) {
1350 // Reaction type is not specified
1351 // See if this is a three-body reaction with a specified collision partner
1352 ElementaryReaction2 testReaction;
1353 parseReactionEquation(testReaction, rxn_node["equation"].asString(),
1354 rxn_node, &kin);
1355 if (isThreeBody(testReaction)) {
1356 type = "three-body";
1357 }
1358 }
1359
1360 if (!(ReactionFactory::factory()->exists(type))) {
1361 throw InputFileError("ReactionFactory::newReaction", rxn_node["type"],
1362 "Unknown reaction type '{}'", type);
1363 }
1364 Reaction* R = ReactionFactory::factory()->create(type, rxn_node, kin);
1365 return unique_ptr<Reaction>(R);
1366}
1367
1368Arrhenius2 readArrhenius(const XML_Node& arrhenius_node)
1369{
1370 return Arrhenius2(getFloat(arrhenius_node, "A", "toSI"),
1371 getFloat(arrhenius_node, "b"),
1372 getFloat(arrhenius_node, "E", "actEnergy") / GasConstant);
1373}
1374
1375Arrhenius2 readArrhenius(const Reaction& R, const AnyValue& rate,
1376 const Kinetics& kin, const UnitSystem& units,
1377 int pressure_dependence=0)
1378{
1379 double A, b, Ta;
1380 Units rc_units = R.rate_units;
1381 if (pressure_dependence) {
1382 Units rxn_phase_units = kin.thermo(kin.reactionPhaseIndex()).standardConcentrationUnits();
1383 rc_units *= rxn_phase_units.pow(-pressure_dependence);
1384 }
1385 if (rate.is<AnyMap>()) {
1386 auto& rate_map = rate.as<AnyMap>();
1387 A = units.convert(rate_map["A"], rc_units);
1388 b = rate_map["b"].asDouble();
1389 Ta = units.convertActivationEnergy(rate_map["Ea"], "K");
1390 } else {
1391 auto& rate_vec = rate.asVector<AnyValue>(3);
1392 A = units.convert(rate_vec[0], rc_units);
1393 b = rate_vec[1].asDouble();
1394 Ta = units.convertActivationEnergy(rate_vec[2], "K");
1395 }
1396 return Arrhenius2(A, b, Ta);
1397}
1398
1399//! Parse falloff parameters, given a rateCoeff node
1400/*!
1401 * @verbatim
1402 <falloff type="Troe"> 0.5 73.2 5000. 9999. </falloff>
1403 @endverbatim
1404*/
1405void readFalloff(FalloffReaction2& R, const XML_Node& rc_node)
1406{
1407 XML_Node& falloff = rc_node.child("falloff");
1408 std::vector<std::string> p;
1409 vector_fp falloff_parameters;
1410 getStringArray(falloff, p);
1411 size_t np = p.size();
1412 for (size_t n = 0; n < np; n++) {
1413 falloff_parameters.push_back(fpValueCheck(p[n]));
1414 }
1415
1416 if (caseInsensitiveEquals(falloff["type"], "lindemann")) {
1417 if (np != 0) {
1418 throw CanteraError("readFalloff", "Lindemann parameterization "
1419 "takes no parameters, but {} were given", np);
1420 }
1421 R.falloff = newFalloff("Lindemann", falloff_parameters);
1422 } else if (caseInsensitiveEquals(falloff["type"], "troe")) {
1423 if (np != 3 && np != 4) {
1424 throw CanteraError("readFalloff", "Troe parameterization takes "
1425 "3 or 4 parameters, but {} were given", np);
1426 }
1427 R.falloff = newFalloff("Troe", falloff_parameters);
1428 } else if (caseInsensitiveEquals(falloff["type"], "sri")) {
1429 if (np != 3 && np != 5) {
1430 throw CanteraError("readFalloff", "SRI parameterization takes "
1431 "3 or 5 parameters, but {} were given", np);
1432 }
1433 R.falloff = newFalloff("SRI", falloff_parameters);
1434 } else if (caseInsensitiveEquals(falloff["type"], "tsang")) {
1435 if (np != 2) {
1436 throw CanteraError("readFalloff", "Tsang parameterization takes "
1437 "2 parameters, but {} were given", np);
1438 }
1439 R.falloff = newFalloff("Tsang", falloff_parameters);
1440 } else {
1441 throw CanteraError("readFalloff", "Unrecognized falloff type: '{}'",
1442 falloff["type"]);
1443 }
1444}
1445
1446void readFalloff(FalloffReaction2& R, const AnyMap& node)
1447{
1448 if (node.hasKey("Troe")) {
1449 auto& f = node["Troe"].as<AnyMap>();
1450 vector_fp params{
1451 f["A"].asDouble(),
1452 f["T3"].asDouble(),
1453 f["T1"].asDouble()
1454 };
1455 if (f.hasKey("T2")) {
1456 params.push_back(f["T2"].asDouble());
1457 }
1458 R.falloff = newFalloff("Troe", params);
1459 } else if (node.hasKey("SRI")) {
1460 auto& f = node["SRI"].as<AnyMap>();
1461 vector_fp params{
1462 f["A"].asDouble(),
1463 f["B"].asDouble(),
1464 f["C"].asDouble()
1465 };
1466 if (f.hasKey("D")) {
1467 params.push_back(f["D"].asDouble());
1468 }
1469 if (f.hasKey("E")) {
1470 params.push_back(f["E"].asDouble());
1471 }
1472 R.falloff = newFalloff("SRI", params);
1473 } else if (node.hasKey("Tsang")) {
1474 auto& f = node["Tsang"].as<AnyMap>();
1475 vector_fp params{
1476 f["A"].asDouble(),
1477 f["B"].asDouble()
1478 };
1479 R.falloff = newFalloff("Tsang", params);
1480 } else {
1481 R.falloff = newFalloff("Lindemann", {});
1482 }
1483}
1484
1485void readEfficiencies(ThirdBody& tbody, const XML_Node& rc_node)
1486{
1487 if (!rc_node.hasChild("efficiencies")) {
1488 tbody.default_efficiency = 1.0;
1489 return;
1490 }
1491 const XML_Node& eff_node = rc_node.child("efficiencies");
1492 tbody.default_efficiency = fpValue(eff_node["default"]);
1493 tbody.efficiencies = parseCompString(eff_node.value());
1494}
1495
1496void readEfficiencies(ThirdBody& tbody, const AnyMap& node)
1497{
1498 tbody.setEfficiencies(node);
1499}
1500
1501bool detectEfficiencies(ThreeBodyReaction2& R)
1502{
1503 for (const auto& reac : R.reactants) {
1504 // detect explicitly specified collision partner
1505 if (R.products.count(reac.first)) {
1506 R.third_body.efficiencies[reac.first] = 1.;
1507 }
1508 }
1509
1510 if (R.third_body.efficiencies.size() == 0) {
1511 return false;
1512 } else if (R.third_body.efficiencies.size() > 1) {
1513 throw CanteraError("detectEfficiencies",
1514 "Found more than one explicitly specified collision partner\n"
1515 "in reaction '{}'.", R.equation());
1516 }
1517
1518 R.third_body.default_efficiency = 0.;
1519 R.third_body.specified_collision_partner = true;
1520 auto sp = R.third_body.efficiencies.begin();
1521
1522 // adjust reactant coefficients
1523 auto reac = R.reactants.find(sp->first);
1524 if (trunc(reac->second) != 1) {
1525 reac->second -= 1.;
1526 } else {
1527 R.reactants.erase(reac);
1528 }
1529
1530 // adjust product coefficients
1531 auto prod = R.products.find(sp->first);
1532 if (trunc(prod->second) != 1) {
1533 prod->second -= 1.;
1534 } else {
1535 R.products.erase(prod);
1536 }
1537
1538 return true;
1539}
1540
1541void setupReaction(Reaction& R, const XML_Node& rxn_node)
1542{
1543 // Reactant and product stoichiometries
1544 R.reactants = parseCompString(rxn_node.child("reactants").value());
1545 R.products = parseCompString(rxn_node.child("products").value());
1546
1547 // Non-stoichiometric reaction orders
1548 std::vector<XML_Node*> orders = rxn_node.getChildren("order");
1549 for (size_t i = 0; i < orders.size(); i++) {
1550 R.orders[orders[i]->attrib("species")] = orders[i]->fp_value();
1551 }
1552
1553 // Flags
1554 R.id = rxn_node.attrib("id");
1555 R.duplicate = rxn_node.hasAttrib("duplicate");
1556 const std::string& rev = rxn_node["reversible"];
1557 R.reversible = (rev == "true" || rev == "yes");
1558}
1559
1560void parseReactionEquation(Reaction& R, const std::string& equation,
1561 const AnyBase& reactionNode, const Kinetics* kin)
1562{
1563 // Parse the reaction equation to determine participating species and
1564 // stoichiometric coefficients
1565 std::vector<std::string> tokens;
1566 tokenizeString(equation, tokens);
1567 tokens.push_back("+"); // makes parsing last species not a special case
1568
1569 size_t last_used = npos; // index of last-used token
1570 bool reactants = true;
1571 for (size_t i = 1; i < tokens.size(); i++) {
1572 if (tokens[i] == "+" || ba::starts_with(tokens[i], "(+") ||
1573 tokens[i] == "<=>" || tokens[i] == "=" || tokens[i] == "=>") {
1574 std::string species = tokens[i-1];
1575
1576 double stoich;
1577 if (last_used != npos && tokens[last_used] == "(+") {
1578 // Falloff third body with space, such as "(+ M)"
1579 species = "(+" + species;
1580 stoich = -1;
1581 } else if (last_used == i-1 && ba::starts_with(species, "(+")
1582 && ba::ends_with(species, ")")) {
1583 // Falloff 3rd body written without space, such as "(+M)"
1584 stoich = -1;
1585 } else if (last_used == i-2) { // Species with no stoich. coefficient
1586 stoich = 1.0;
1587 } else if (last_used == i-3) { // Stoich. coefficient and species
1588 try {
1589 stoich = fpValueCheck(tokens[i-2]);
1590 } catch (CanteraError& err) {
1591 throw InputFileError("parseReactionEquation", reactionNode,
1592 err.getMessage());
1593 }
1594 } else {
1595 throw InputFileError("parseReactionEquation", reactionNode,
1596 "Error parsing reaction string '{}'.\n"
1597 "Current token: '{}'\nlast_used: '{}'",
1598 equation, tokens[i],
1599 (last_used == npos) ? "n/a" : tokens[last_used]);
1600 }
1601 if (!kin || (kin->kineticsSpeciesIndex(species) == npos
1602 && stoich != -1 && species != "M"))
1603 {
1604 R.setValid(false);
1605 }
1606
1607 if (reactants) {
1608 R.reactants[species] += stoich;
1609 } else {
1610 R.products[species] += stoich;
1611 }
1612
1613 last_used = i;
1614 }
1615
1616 // Tokens after this point are part of the products string
1617 if (tokens[i] == "<=>" || tokens[i] == "=") {
1618 R.reversible = true;
1619 reactants = false;
1620 } else if (tokens[i] == "=>") {
1621 R.reversible = false;
1622 reactants = false;
1623 }
1624 }
1625}
1626
1627void setupReaction(Reaction& R, const AnyMap& node, const Kinetics& kin)
1628{
1629 parseReactionEquation(R, node["equation"].asString(), node, &kin);
1630 // Non-stoichiometric reaction orders
1631 if (node.hasKey("orders")) {
1632 for (const auto& order : node["orders"].asMap<double>()) {
1633 R.orders[order.first] = order.second;
1634 if (kin.kineticsSpeciesIndex(order.first) == npos) {
1635 R.setValid(false);
1636 }
1637 }
1638 }
1639
1640 //Flags
1641 R.id = node.getString("id", "");
1642 R.duplicate = node.getBool("duplicate", false);
1643 R.allow_negative_orders = node.getBool("negative-orders", false);
1644 R.allow_nonreactant_orders = node.getBool("nonreactant-orders", false);
1645
1646 R.input = node;
1647 R.calculateRateCoeffUnits(kin);
1648}
1649
1650void setupElementaryReaction(ElementaryReaction2& R, const XML_Node& rxn_node)
1651{
1652 const XML_Node& rc_node = rxn_node.child("rateCoeff");
1653 if (rc_node.hasChild("Arrhenius")) {
1654 R.rate = readArrhenius(rc_node.child("Arrhenius"));
1655 } else if (rc_node.hasChild("Arrhenius_ExchangeCurrentDensity")) {
1656 R.rate = readArrhenius(rc_node.child("Arrhenius_ExchangeCurrentDensity"));
1657 } else {
1658 throw CanteraError("setupElementaryReaction", "Couldn't find Arrhenius node");
1659 }
1660 if (rxn_node["negative_A"] == "yes") {
1661 R.allow_negative_pre_exponential_factor = true;
1662 }
1663 if (rxn_node["negative_orders"] == "yes") {
1664 R.allow_negative_orders = true;
1665 }
1666 if (rxn_node["nonreactant_orders"] == "yes") {
1667 R.allow_nonreactant_orders = true;
1668 }
1669 setupReaction(R, rxn_node);
1670}
1671
1672void setupElementaryReaction(ElementaryReaction2& R, const AnyMap& node,
1673 const Kinetics& kin)
1674{
1675 setupReaction(R, node, kin);
1676 R.allow_negative_pre_exponential_factor = node.getBool("negative-A", false);
1677 R.rate = readArrhenius(R, node["rate-constant"], kin, node.units());
1678}
1679
1680void setupThreeBodyReaction(ThreeBodyReaction2& R, const XML_Node& rxn_node)
1681{
1682 readEfficiencies(R.third_body, rxn_node.child("rateCoeff"));
1683 setupElementaryReaction(R, rxn_node);
1684 if (R.third_body.efficiencies.size() == 0) {
1685 detectEfficiencies(R);
1686 }
1687}
1688
1689void setupThreeBodyReaction(ThreeBodyReaction2& R, const AnyMap& node,
1690 const Kinetics& kin)
1691{
1692 setupElementaryReaction(R, node, kin);
1693 if (R.reactants.count("M") != 1 || R.products.count("M") != 1) {
1694 if (!detectEfficiencies(R)) {
1695 throw InputFileError("setupThreeBodyReaction", node["equation"],
1696 "Reaction equation '{}' does not contain third body 'M'",
1697 node["equation"].asString());
1698 }
1699 } else {
1700 R.reactants.erase("M");
1701 R.products.erase("M");
1702 readEfficiencies(R.third_body, node);
1703 }
1704}
1705
1706void setupFalloffReaction(FalloffReaction2& R, const XML_Node& rxn_node)
1707{
1708 XML_Node& rc_node = rxn_node.child("rateCoeff");
1709 std::vector<XML_Node*> rates = rc_node.getChildren("Arrhenius");
1710 int nLow = 0;
1711 int nHigh = 0;
1712 for (size_t i = 0; i < rates.size(); i++) {
1713 XML_Node& node = *rates[i];
1714 if (node["name"] == "") {
1715 R.high_rate = readArrhenius(node);
1716 nHigh++;
1717 } else if (node["name"] == "k0") {
1718 R.low_rate = readArrhenius(node);
1719 nLow++;
1720 } else {
1721 throw CanteraError("setupFalloffReaction", "Found an Arrhenius XML "
1722 "node with an unexpected type '" + node["name"] + "'");
1723 }
1724 }
1725 if (nLow != 1 || nHigh != 1) {
1726 throw CanteraError("setupFalloffReaction", "Did not find the correct "
1727 "number of Arrhenius rate expressions");
1728 }
1729 if (rxn_node["negative_A"] == "yes") {
1730 R.allow_negative_pre_exponential_factor = true;
1731 }
1732 readFalloff(R, rc_node);
1733 readEfficiencies(R.third_body, rc_node);
1734 setupReaction(R, rxn_node);
1735}
1736
1737void setupFalloffReaction(FalloffReaction2& R, const AnyMap& node,
1738 const Kinetics& kin)
1739{
1740 setupReaction(R, node, kin);
1741 // setupReaction sets the stoichiometric coefficient for the falloff third
1742 // body to -1.
1743 std::string third_body;
1744 for (auto& reactant : R.reactants) {
1745 if (reactant.second == -1 && ba::starts_with(reactant.first, "(+")) {
1746 third_body = reactant.first;
1747 break;
1748 }
1749 }
1750
1751 // Equation must contain a third body, and it must appear on both sides
1752 if (third_body == "") {
1753 throw InputFileError("setupFalloffReaction", node["equation"],
1754 "Reactants for reaction '{}' do not contain a pressure-dependent "
1755 "third body", node["equation"].asString());
1756 } else if (R.products.count(third_body) == 0) {
1757 throw InputFileError("setupFalloffReaction", node["equation"],
1758 "Unable to match third body '{}' in reactants and products of "
1759 "reaction '{}'", third_body, node["equation"].asString());
1760 }
1761
1762 // Remove the dummy species
1763 R.reactants.erase(third_body);
1764 R.products.erase(third_body);
1765
1766 R.allow_negative_pre_exponential_factor = node.getBool("negative-A", false);
1767 if (third_body == "(+M)") {
1768 readEfficiencies(R.third_body, node);
1769 } else {
1770 // Specific species is listed as the third body
1772 R.third_body.efficiencies[third_body.substr(2, third_body.size() - 3)] = 1.0;
1773 }
1774
1775 R.low_rate = readArrhenius(R, node["low-P-rate-constant"], kin,
1776 node.units(), 1);
1777 R.high_rate = readArrhenius(R, node["high-P-rate-constant"], kin,
1778 node.units());
1779
1780 readFalloff(R, node);
1781}
1782
1784 const XML_Node& rxn_node)
1785{
1786 XML_Node& rc_node = rxn_node.child("rateCoeff");
1787 std::vector<XML_Node*> rates = rc_node.getChildren("Arrhenius");
1788 int nLow = 0;
1789 int nHigh = 0;
1790 for (size_t i = 0; i < rates.size(); i++) {
1791 XML_Node& node = *rates[i];
1792 if (node["name"] == "kHigh") {
1793 R.high_rate = readArrhenius(node);
1794 nHigh++;
1795 } else if (node["name"] == "") {
1796 R.low_rate = readArrhenius(node);
1797 nLow++;
1798 } else {
1799 throw CanteraError("setupChemicallyActivatedReaction", "Found an "
1800 "Arrhenius XML node with an unexpected type '" + node["name"] + "'");
1801 }
1802 }
1803 if (nLow != 1 || nHigh != 1) {
1804 throw CanteraError("setupChemicallyActivatedReaction", "Did not find "
1805 "the correct number of Arrhenius rate expressions");
1806 }
1807 readFalloff(R, rc_node);
1808 readEfficiencies(R.third_body, rc_node);
1809 setupReaction(R, rxn_node);
1810}
1811
1812void setupPlogReaction(PlogReaction2& R, const XML_Node& rxn_node)
1813{
1814 XML_Node& rc = rxn_node.child("rateCoeff");
1815 std::multimap<double, Arrhenius2> rates;
1816 for (size_t m = 0; m < rc.nChildren(); m++) {
1817 const XML_Node& node = rc.child(m);
1818 rates.insert({getFloat(node, "P", "toSI"), readArrhenius(node)});
1819 }
1820 R.rate = Plog(rates);
1821 setupReaction(R, rxn_node);
1822}
1823
1824void setupPlogReaction(PlogReaction2& R, const AnyMap& node, const Kinetics& kin)
1825{
1826 setupReaction(R, node, kin);
1827 std::multimap<double, Arrhenius2> rates;
1828 for (const auto& rate : node.at("rate-constants").asVector<AnyMap>()) {
1829 rates.insert({rate.convert("P", "Pa"),
1830 readArrhenius(R, AnyValue(rate), kin, node.units())});
1831 }
1832 R.rate = Plog(rates);
1833}
1834
1836{
1838 rate.validate(equation());
1839}
1840
1841void setupChebyshevReaction(ChebyshevReaction2& R, const XML_Node& rxn_node)
1842{
1843 XML_Node& rc = rxn_node.child("rateCoeff");
1844 const XML_Node& coeff_node = rc.child("floatArray");
1845 size_t nP = atoi(coeff_node["degreeP"].c_str());
1846 size_t nT = atoi(coeff_node["degreeT"].c_str());
1847
1848 vector_fp coeffs_flat;
1849 getFloatArray(rc, coeffs_flat, false);
1850 Array2D coeffs(nT, nP);
1851 for (size_t t = 0; t < nT; t++) {
1852 for (size_t p = 0; p < nP; p++) {
1853 coeffs(t,p) = coeffs_flat[nP*t + p];
1854 }
1855 }
1856 R.rate = ChebyshevRate(getFloat(rc, "Tmin", "toSI"),
1857 getFloat(rc, "Tmax", "toSI"),
1858 getFloat(rc, "Pmin", "toSI"),
1859 getFloat(rc, "Pmax", "toSI"),
1860 coeffs);
1861 setupReaction(R, rxn_node);
1862}
1863
1864void setupChebyshevReaction(ChebyshevReaction2&R, const AnyMap& node,
1865 const Kinetics& kin)
1866{
1867 setupReaction(R, node, kin);
1868 R.reactants.erase("(+M)"); // remove optional third body notation
1869 R.products.erase("(+M)");
1870 const auto& T_range = node["temperature-range"].asVector<AnyValue>(2);
1871 const auto& P_range = node["pressure-range"].asVector<AnyValue>(2);
1872 auto& vcoeffs = node["data"].asVector<vector_fp>();
1873 Array2D coeffs(vcoeffs.size(), vcoeffs[0].size());
1874 for (size_t i = 0; i < coeffs.nRows(); i++) {
1875 if (vcoeffs[i].size() != vcoeffs[0].size()) {
1876 throw InputFileError("setupChebyshevReaction", node["data"],
1877 "Inconsistent number of coefficients in row {} of matrix", i + 1);
1878 }
1879 for (size_t j = 0; j < coeffs.nColumns(); j++) {
1880 coeffs(i, j) = vcoeffs[i][j];
1881 }
1882 }
1883 const UnitSystem& units = node.units();
1884 coeffs(0, 0) += std::log10(units.convertTo(1.0, R.rate_units));
1885 R.rate = ChebyshevRate(units.convert(T_range[0], "K"),
1886 units.convert(T_range[1], "K"),
1887 units.convert(P_range[0], "Pa"),
1888 units.convert(P_range[1], "Pa"),
1889 coeffs);
1890}
1891
1892void setupInterfaceReaction(InterfaceReaction2& R, const XML_Node& rxn_node)
1893{
1894 XML_Node& arr = rxn_node.child("rateCoeff").child("Arrhenius");
1895 if (caseInsensitiveEquals(arr["type"], "stick")) {
1896 R.is_sticking_coefficient = true;
1897 R.sticking_species = arr["species"];
1898
1899 if (caseInsensitiveEquals(arr["motz_wise"], "true")) {
1900 R.use_motz_wise_correction = true;
1901 } else if (caseInsensitiveEquals(arr["motz_wise"], "false")) {
1902 R.use_motz_wise_correction = false;
1903 } else {
1904 // Default value for all reactions
1905 XML_Node* parent = rxn_node.parent();
1906 if (parent && parent->name() == "reactionData"
1907 && caseInsensitiveEquals((*parent)["motz_wise"], "true")) {
1908 R.use_motz_wise_correction = true;
1909 }
1910 }
1911 }
1912 std::vector<XML_Node*> cov = arr.getChildren("coverage");
1913 for (const auto& node : cov) {
1914 CoverageDependency& cdep = R.coverage_deps[node->attrib("species")];
1915 cdep.a = getFloat(*node, "a", "toSI");
1916 cdep.m = getFloat(*node, "m");
1917 cdep.E = getFloat(*node, "e", "actEnergy") / GasConstant;
1918 }
1919 setupElementaryReaction(R, rxn_node);
1920}
1921
1922void setupInterfaceReaction(InterfaceReaction2& R, const AnyMap& node,
1923 const Kinetics& kin)
1924{
1925 setupReaction(R, node, kin);
1926 R.allow_negative_pre_exponential_factor = node.getBool("negative-A", false);
1927
1928 if (node.hasKey("rate-constant")) {
1929 R.rate = readArrhenius(R, node["rate-constant"], kin, node.units());
1930 } else if (node.hasKey("sticking-coefficient")) {
1931 R.is_sticking_coefficient = true;
1932 R.rate = readArrhenius(R, node["sticking-coefficient"], kin, node.units());
1933 R.use_motz_wise_correction = node.getBool("Motz-Wise",
1934 kin.thermo().input().getBool("Motz-Wise", false));
1935 R.sticking_species = node.getString("sticking-species", "");
1936 } else {
1937 throw InputFileError("setupInterfaceReaction", node,
1938 "Reaction must include either a 'rate-constant' or"
1939 " 'sticking-coefficient' node.");
1940 }
1941
1942 if (node.hasKey("coverage-dependencies")) {
1943 for (const auto& item : node["coverage-dependencies"].as<AnyMap>()) {
1944 double a, E, m;
1945 if (item.second.is<AnyMap>()) {
1946 auto& cov_map = item.second.as<AnyMap>();
1947 a = cov_map["a"].asDouble();
1948 m = cov_map["m"].asDouble();
1949 E = node.units().convertActivationEnergy(cov_map["E"], "K");
1950 } else {
1951 auto& cov_vec = item.second.asVector<AnyValue>(3);
1952 a = cov_vec[0].asDouble();
1953 m = cov_vec[1].asDouble();
1954 E = node.units().convertActivationEnergy(cov_vec[2], "K");
1955 }
1956 R.coverage_deps[item.first] = CoverageDependency(a, E, m);
1957 }
1958 }
1959}
1960
1961void setupElectrochemicalReaction(ElectrochemicalReaction2& R,
1962 const XML_Node& rxn_node)
1963{
1964 XML_Node& rc = rxn_node.child("rateCoeff");
1965 std::string rc_type = toLowerCopy(rc["type"]);
1966 if (rc_type == "exchangecurrentdensity") {
1967 R.exchange_current_density_formulation = true;
1968 } else if (rc_type != "" && rc_type != "arrhenius") {
1969 throw CanteraError("setupElectrochemicalReaction",
1970 "Unknown rate coefficient type: '" + rc_type + "'");
1971 }
1972 if (rc.hasChild("Arrhenius_ExchangeCurrentDensity")) {
1973 R.exchange_current_density_formulation = true;
1974 }
1975
1976 if (rc.hasChild("electrochem") && rc.child("electrochem").hasAttrib("beta")) {
1977 R.beta = fpValueCheck(rc.child("electrochem")["beta"]);
1978 }
1979
1980 setupInterfaceReaction(R, rxn_node);
1981
1982 // Override orders based on the <orders> node
1983 if (rxn_node.hasChild("orders")) {
1984 Composition orders = parseCompString(rxn_node.child("orders").value());
1985 for (const auto& order : orders) {
1986 R.orders[order.first] = order.second;
1987 }
1988 }
1989}
1990
1991void setupElectrochemicalReaction(ElectrochemicalReaction2& R,
1992 const AnyMap& node, const Kinetics& kin)
1993{
1994 setupInterfaceReaction(R, node, kin);
1995 R.beta = node.getDouble("beta", 0.5);
1996 R.exchange_current_density_formulation = node.getBool(
1997 "exchange-current-density-formulation", false);
1998}
1999
2000std::vector<shared_ptr<Reaction> > getReactions(const XML_Node& node)
2001{
2002 std::vector<shared_ptr<Reaction> > all_reactions;
2003 for (const auto& rxnnode : node.child("reactionData").getChildren("reaction")) {
2004 all_reactions.push_back(newReaction(*rxnnode));
2005 }
2006 return all_reactions;
2007}
2008
2009std::vector<shared_ptr<Reaction>> getReactions(const AnyValue& items,
2010 Kinetics& kinetics)
2011{
2012 std::vector<shared_ptr<Reaction>> all_reactions;
2013 for (const auto& node : items.asVector<AnyMap>()) {
2014 shared_ptr<Reaction> R(newReaction(node, kinetics));
2015 R->validate();
2016 R->validate(kinetics);
2017 if (R->valid() && R->checkSpecies(kinetics)) {
2018 all_reactions.emplace_back(R);
2019 }
2020 }
2021 return all_reactions;
2022}
2023
2024}
Header file for class Cantera::Array2D.
Parameterizations for reaction falloff functions.
Base class for kinetics managers and also contains the kineticsmgr module documentation (see Kinetics...
Factory class for reaction functions.
Factory class for reaction rate objects.
Header for a simple thermodynamics model of a surface phase derived from ThermoPhase,...
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
Base class defining common data possessed by both AnyMap and AnyValue objects.
Definition: AnyMap.h:33
A map of string keys to values whose type can vary at runtime.
Definition: AnyMap.h:399
const AnyValue & at(const std::string &key) const
Get the value of the item stored in key.
Definition: AnyMap.cpp:1391
void copyMetadata(const AnyMap &other)
Copy metadata including input line/column from an existing AnyMap.
Definition: AnyMap.cpp:1465
const UnitSystem & units() const
Return the default units that should be used to convert stored values.
Definition: AnyMap.h:598
bool empty() const
Return boolean indicating whether AnyMap is empty.
Definition: AnyMap.cpp:1401
const std::string & getString(const std::string &key, const std::string &default_) const
If key exists, return it as a string, otherwise return default_.
Definition: AnyMap.cpp:1502
bool getBool(const std::string &key, bool default_) const
If key exists, return it as a bool, otherwise return default_.
Definition: AnyMap.cpp:1487
void erase(const std::string &key)
Erase the value held by key.
Definition: AnyMap.cpp:1411
double getDouble(const std::string &key, double default_) const
If key exists, return it as a double, otherwise return default_.
Definition: AnyMap.cpp:1492
static bool addOrderingRules(const std::string &objectType, const std::vector< std::vector< std::string > > &specs)
Add global rules for setting the order of elements when outputting AnyMap objects to YAML.
Definition: AnyMap.cpp:1702
void setFlowStyle(bool flow=true)
Use "flow" style when outputting this AnyMap to YAML.
Definition: AnyMap.cpp:1698
bool hasKey(const std::string &key) const
Returns true if the map contains an item named key.
Definition: AnyMap.cpp:1406
void update(const AnyMap &other, bool keepExisting=true)
Add items from other to this AnyMap.
Definition: AnyMap.cpp:1421
A wrapper for a variable whose type is determined at runtime.
Definition: AnyMap.h:84
const std::vector< T > & asVector(size_t nMin=npos, size_t nMax=npos) const
Return the held value, if it is a vector of type T.
Definition: AnyMap.inl.h:72
double & asDouble()
Return the held value as a double, if it is a double or a long int.
Definition: AnyMap.cpp:801
A class for 2D arrays stored in column-major (Fortran-compatible) form.
Definition: Array.h:30
Arrhenius reaction rate type depends only on temperature.
Definition: RxnRates.h:42
doublereal updateRC(doublereal logT, doublereal recipT) const
Update the value the rate constant.
Definition: RxnRates.h:94
void getParameters(AnyMap &node, const Units &rate_units) const
Return parameters - two-parameter version.
Definition: RxnRates.cpp:50
virtual double preExponentialFactor() const
Return the pre-exponential factor A (in m, kmol, s to powers depending on the reaction order)
Definition: Arrhenius.h:108
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
virtual std::string getMessage() const
Method overridden by derived classes to format the error message.
Pressure-dependent rate expression where the rate coefficient is expressed as a bivariate Chebyshev p...
Definition: ChebyshevRate.h:90
A pressure-dependent reaction parameterized by a bi-variate Chebyshev polynomial in temperature and p...
Definition: Reaction.h:386
virtual void getParameters(AnyMap &reactionNode) const
Store the parameters of a Reaction needed to reconstruct an identical object using the newReaction(An...
Definition: Reaction.cpp:875
A reaction where the rate decreases as pressure increases due to collisional stabilization of a react...
Definition: Reaction.h:346
virtual void calculateRateCoeffUnits(const Kinetics &kin)
Calculate the units of the rate constant.
Definition: Reaction.cpp:825
virtual void getParameters(AnyMap &reactionNode) const
Store the parameters of a Reaction needed to reconstruct an identical object using the newReaction(An...
Definition: Reaction.cpp:833
virtual std::string type() const
The type of reaction.
Definition: Reaction.h:538
An interface reaction which involves charged species.
Definition: Reaction.h:458
doublereal beta
Forward value of the apparent Electrochemical transfer coefficient.
Definition: Reaction.h:466
virtual void getParameters(AnyMap &reactionNode) const
Store the parameters of a Reaction needed to reconstruct an identical object using the newReaction(An...
Definition: Reaction.cpp:969
A reaction which follows mass-action kinetics with a modified Arrhenius reaction rate.
Definition: Reaction.h:217
virtual void validate()
Ensure that the rate constant and other parameters for this reaction are valid.
Definition: Reaction.cpp:588
virtual void getParameters(AnyMap &reactionNode) const
Store the parameters of a Reaction needed to reconstruct an identical object using the newReaction(An...
Definition: Reaction.cpp:599
T * create(const std::string &name, Args... args)
Create an object using the object construction function corresponding to "name" and the provided cons...
Definition: FactoryBase.h:76
A reaction that is first-order in [M] at low pressure, like a third-body reaction,...
Definition: Reaction.h:297
virtual std::string reactantString() const
The reactant side of the chemical equation for this reaction.
Definition: Reaction.cpp:738
virtual void calculateRateCoeffUnits(const Kinetics &kin)
Calculate the units of the rate constant.
Definition: Reaction.cpp:776
shared_ptr< FalloffRate > falloff
Falloff function which determines how low_rate and high_rate are combined to determine the rate const...
Definition: Reaction.h:327
virtual void validate()
Ensure that the rate constant and other parameters for this reaction are valid.
Definition: Reaction.cpp:760
virtual std::string productString() const
The product side of the chemical equation for this reaction.
Definition: Reaction.cpp:749
Units low_rate_units
The units of the low-pressure rate constant.
Definition: Reaction.h:333
Arrhenius2 high_rate
The rate constant in the high-pressure limit.
Definition: Reaction.h:320
ThirdBody third_body
Relative efficiencies of third-body species in enhancing the reaction rate.
Definition: Reaction.h:323
virtual void getParameters(AnyMap &reactionNode) const
Store the parameters of a Reaction needed to reconstruct an identical object using the newReaction(An...
Definition: Reaction.cpp:784
Arrhenius2 low_rate
The rate constant in the low-pressure limit.
Definition: Reaction.h:317
virtual std::pair< std::vector< std::string >, bool > undeclaredThirdBodies(const Kinetics &kin) const
Definition: Reaction.cpp:803
virtual void setParameters(const AnyMap &node, const Kinetics &kin)
Set up reaction based on AnyMap node
Definition: Reaction.cpp:1176
virtual std::string reactantString() const
The reactant side of the chemical equation for this reaction.
Definition: Reaction.cpp:1156
virtual std::string productString() const
The product side of the chemical equation for this reaction.
Definition: Reaction.cpp:1166
virtual std::string type() const
The type of reaction.
Definition: Reaction.cpp:1146
virtual void getParameters(AnyMap &reactionNode) const
Store the parameters of a Reaction needed to reconstruct an identical object using the newReaction(An...
Definition: Reaction.cpp:1232
virtual void setEquation(const std::string &equation, const Kinetics *kin)
Set the reactants and products based on the reaction equation.
Definition: Reaction.cpp:1189
Error thrown for problems processing information contained in an AnyMap or AnyValue.
Definition: AnyMap.h:702
A reaction occurring on an interface (for example, a SurfPhase or an EdgePhase)
Definition: Reaction.h:419
bool is_sticking_coefficient
Set to true if rate is a parameterization of the sticking coefficient rather than the forward rate co...
Definition: Reaction.h:442
std::map< std::string, CoverageDependency > coverage_deps
Adjustments to the Arrhenius rate expression dependent on surface species coverages.
Definition: Reaction.h:438
virtual void calculateRateCoeffUnits(const Kinetics &kin)
Calculate the units of the rate constant.
Definition: Reaction.cpp:900
virtual void validate()
Ensure that the rate constant and other parameters for this reaction are valid.
Definition: Reaction.cpp:124
std::string sticking_species
For reactions with multiple non-surface species, the sticking species needs to be explicitly identifi...
Definition: Reaction.h:452
bool use_motz_wise_correction
Set to true if rate is a sticking coefficient which should be translated into a rate coefficient usin...
Definition: Reaction.h:448
virtual void getParameters(AnyMap &reactionNode) const
Store the parameters of a Reaction needed to reconstruct an identical object using the newReaction(An...
Definition: Reaction.cpp:908
Public interface for kinetics managers.
Definition: Kinetics.h:114
size_t reactionPhaseIndex() const
Phase where the reactions occur.
Definition: Kinetics.h:218
ThermoPhase & thermo(size_t n=0)
This method returns a reference to the nth ThermoPhase object defined in this kinetics mechanism.
Definition: Kinetics.h:231
ThermoPhase & speciesPhase(const std::string &nm)
This function looks up the name of a species and returns a reference to the ThermoPhase object of the...
Definition: Kinetics.cpp:352
size_t nPhases() const
The number of phases participating in the reaction mechanism.
Definition: Kinetics.h:171
void skipUndeclaredSpecies(bool skip)
Determine behavior when adding a new reaction that contains species not defined in any of the phases ...
Definition: Kinetics.h:1171
void skipUndeclaredThirdBodies(bool skip)
Determine behavior when adding a new reaction that contains third-body efficiencies for species not d...
Definition: Kinetics.h:1183
size_t kineticsSpeciesIndex(size_t k, size_t n) const
The location of species k of phase n in species arrays.
Definition: Kinetics.h:265
size_t surfacePhaseIndex() const
This returns the integer index of the phase which has ThermoPhase type cSurf.
Definition: Kinetics.h:206
size_t speciesPhaseIndex(size_t k) const
This function takes as an argument the kineticsSpecies index (that is, the list index in the list of ...
Definition: Kinetics.cpp:373
The Lindemann falloff parameterization.
Definition: Falloff.h:276
doublereal charge(size_t k) const
Dimensionless electrical charge of a single molecule of species k The charge is normalized by the the...
Definition: Phase.h:630
size_t nDim() const
Returns the number of spatial dimensions (1, 2, or 3)
Definition: Phase.h:638
doublereal nAtoms(size_t k, size_t m) const
Number of atoms of element m in species k.
Definition: Phase.cpp:154
size_t nElements() const
Number of elements.
Definition: Phase.cpp:81
size_t speciesIndex(const std::string &name) const
Returns the index of a species named 'name' within the Phase object.
Definition: Phase.cpp:187
std::string elementName(size_t m) const
Name of the element with index m.
Definition: Phase.cpp:100
Pressure-dependent reaction rate expressed by logarithmically interpolating between Arrhenius rate ex...
Definition: PlogRate.h:78
A pressure-dependent reaction parameterized by logarithmically interpolating between Arrhenius rate e...
Definition: Reaction.h:365
virtual void validate()
Ensure that the rate constant and other parameters for this reaction are valid.
Definition: Reaction.cpp:1835
virtual void getParameters(AnyMap &reactionNode) const
Store the parameters of a Reaction needed to reconstruct an identical object using the newReaction(An...
Definition: Reaction.cpp:853
static ReactionFactory * factory()
Return a pointer to the factory.
Abstract base class which stores data about a reaction and its rate parameterization so that it can b...
Definition: Reaction.h:33
virtual void setParameters(const AnyMap &node, const Kinetics &kin)
Set up reaction based on AnyMap node
Definition: Reaction.cpp:216
virtual std::string reactantString() const
The reactant side of the chemical equation for this reaction.
Definition: Reaction.cpp:266
void checkBalance(const Kinetics &kin) const
Check that the specified reaction is balanced (same number of atoms for each element in the reactants...
Definition: Reaction.cpp:409
Units rate_units
The units of the rate constant.
Definition: Reaction.h:168
bool valid() const
Get validity flag of reaction.
Definition: Reaction.h:100
virtual void calculateRateCoeffUnits(const Kinetics &kin)
Calculate the units of the rate constant.
Definition: Reaction.cpp:315
shared_ptr< ReactionRate > rate()
Get reaction rate pointer.
Definition: Reaction.h:171
Composition orders
Forward reaction order with respect to specific species.
Definition: Reaction.h:143
virtual void validate()
Ensure that the rate constant and other parameters for this reaction are valid.
Definition: Reaction.cpp:124
bool checkSpecies(const Kinetics &kin) const
Verify that all species involved in the reaction are defined in the Kinetics object.
Definition: Reaction.cpp:479
void setRate(shared_ptr< ReactionRate > rate)
Set reaction rate pointer.
Definition: Reaction.cpp:243
shared_ptr< ThirdBody > m_third_body
Relative efficiencies of third-body species in enhancing the reaction rate (if applicable)
Definition: Reaction.h:210
AnyMap parameters(bool withInput=true) const
Return the parameters such that an identical Reaction could be reconstructed using the newReaction() ...
Definition: Reaction.cpp:162
virtual void setEquation(const std::string &equation, const Kinetics *kin=0)
Set the reactants and products based on the reaction equation.
Definition: Reaction.cpp:305
virtual std::string productString() const
The product side of the chemical equation for this reaction.
Definition: Reaction.cpp:281
bool reversible
True if the current reaction is reversible. False otherwise.
Definition: Reaction.h:150
bool allow_nonreactant_orders
True if reaction orders can be specified for non-reactant species.
Definition: Reaction.h:157
virtual std::string type() const
The type of reaction.
Definition: Reaction.cpp:310
shared_ptr< ReactionRate > m_rate
Reaction rate used by generic reactions.
Definition: Reaction.h:206
bool usesLegacy() const
Indicate whether object uses legacy framework.
Definition: Reaction.h:184
bool usesElectrochemistry(const Kinetics &kin) const
Check whether reaction uses electrochemistry.
Definition: Reaction.cpp:540
virtual void getParameters(AnyMap &reactionNode) const
Store the parameters of a Reaction needed to reconstruct an identical object using the newReaction(An...
Definition: Reaction.cpp:184
bool allow_negative_orders
True if negative reaction orders are allowed. Default is false.
Definition: Reaction.h:160
Composition products
Product species and stoichiometric coefficients.
Definition: Reaction.h:138
Composition reactants
Reactant species and stoichiometric coefficients.
Definition: Reaction.h:135
AnyMap input
Input data used for specific models.
Definition: Reaction.h:163
bool duplicate
True if the current reaction is marked as duplicate.
Definition: Reaction.h:153
UnitStack calculateRateCoeffUnits3(const Kinetics &kin)
Calculate the units of the rate constant.
Definition: Reaction.cpp:345
int reaction_type
Type of the reaction.
Definition: Reaction.h:132
std::string equation() const
The chemical equation for this reaction.
Definition: Reaction.cpp:296
virtual std::pair< std::vector< std::string >, bool > undeclaredThirdBodies(const Kinetics &kin) const
Definition: Reaction.cpp:398
void setValid(bool valid)
Set validity flag of reaction.
Definition: Reaction.h:105
A simple thermodynamic model for a surface phase, assuming an ideal solution model.
Definition: SurfPhase.h:125
Base class for a phase with thermodynamic properties.
Definition: ThermoPhase.h:102
virtual Units standardConcentrationUnits() const
Returns the units of the "standard concentration" for this phase.
Definition: ThermoPhase.cpp:66
const AnyMap & input() const
Access input data associated with the phase description.
A class for managing third-body efficiencies, including default values.
Definition: Reaction.h:238
bool specified_collision_partner
Input explicitly specifies collision partner.
Definition: Reaction.h:258
void setEfficiencies(const AnyMap &node)
Set third-body efficiencies from AnyMap node
Definition: Reaction.cpp:624
double default_efficiency
The default third body efficiency for species not listed in efficiencies.
Definition: Reaction.h:255
double efficiency(const std::string &k) const
Get the third-body efficiency for species k
Definition: Reaction.cpp:632
Composition efficiencies
Map of species to third body efficiency.
Definition: Reaction.h:251
A reaction with a non-reacting third body "M" that acts to add or remove energy from the reacting spe...
Definition: Reaction.h:269
virtual std::string reactantString() const
The reactant side of the chemical equation for this reaction.
Definition: Reaction.cpp:652
virtual void calculateRateCoeffUnits(const Kinetics &kin)
Calculate the units of the rate constant.
Definition: Reaction.cpp:672
virtual std::string productString() const
The product side of the chemical equation for this reaction.
Definition: Reaction.cpp:662
ThirdBody third_body
Relative efficiencies of third-body species in enhancing the reaction rate.
Definition: Reaction.h:286
virtual void getParameters(AnyMap &reactionNode) const
Store the parameters of a Reaction needed to reconstruct an identical object using the newReaction(An...
Definition: Reaction.cpp:693
virtual std::pair< std::vector< std::string >, bool > undeclaredThirdBodies(const Kinetics &kin) const
Definition: Reaction.cpp:706
virtual void setParameters(const AnyMap &node, const Kinetics &kin)
Set up reaction based on AnyMap node
Definition: Reaction.cpp:1062
virtual std::string reactantString() const
The reactant side of the chemical equation for this reaction.
Definition: Reaction.cpp:1087
virtual void setEquation(const std::string &equation, const Kinetics *kin=0)
Set the reactants and products based on the reaction equation.
Definition: Reaction.cpp:1046
virtual std::string productString() const
The product side of the chemical equation for this reaction.
Definition: Reaction.cpp:1097
virtual std::string type() const
The type of reaction.
Definition: Reaction.h:483
virtual void getParameters(AnyMap &reactionNode) const
Store the parameters of a Reaction needed to reconstruct an identical object using the newReaction(An...
Definition: Reaction.cpp:1074
Unit conversion utility.
Definition: Units.h:161
double convert(double value, const std::string &src, const std::string &dest) const
Convert value from the units of src to the units of dest.
Definition: Units.cpp:558
double convertActivationEnergy(double value, const std::string &src, const std::string &dest) const
Convert value from the units of src to the units of dest, allowing for the different dimensions that ...
Definition: Units.cpp:658
double convertTo(double value, const std::string &dest) const
Convert value to the specified dest units from the appropriate units for this unit system (defined by...
Definition: Units.cpp:575
A representation of the units associated with a dimensional quantity.
Definition: Units.h:30
Units pow(double exponent) const
Raise these Units to a power, changing both the conversion factor and the dimensions of these Units.
Definition: Units.cpp:234
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:103
bool hasChild(const std::string &ch) const
Tests whether the current node has a child node with a particular name.
Definition: xml.cpp:529
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...
Definition: xml.cpp:712
XML_Node & child(const size_t n) const
Return a changeable reference to the n'th child of the current node.
Definition: xml.cpp:547
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data.
Namespace for the Cantera kernel.
Definition: AnyMap.h:29
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:192
void getStringArray(const XML_Node &node, std::vector< std::string > &v)
This function interprets the value portion of an XML element as a string.
Definition: ctml.cpp:429
const int CHEBYSHEV_RXN
A general gas-phase pressure-dependent reaction where k(T,P) is defined in terms of a bivariate Cheby...
Definition: reaction_defs.h:60
void parseReactionEquation(Reaction &R, const std::string &equation, const AnyBase &reactionNode, const Kinetics *kin)
Parse reaction equation.
Definition: Reaction.cpp:1560
std::vector< shared_ptr< Reaction > > getReactions(const XML_Node &node)
Create Reaction objects for all <reaction> nodes in an XML document.
Definition: Reaction.cpp:2000
doublereal getFloat(const XML_Node &parent, const std::string &name, const std::string &type="")
Get a floating-point value from a child element.
Definition: ctml.cpp:166
const int PLOG_RXN
A pressure-dependent rate expression consisting of several Arrhenius rate expressions evaluated at di...
Definition: reaction_defs.h:54
doublereal fpValue(const std::string &val)
Translate a string into one doublereal value.
bool caseInsensitiveEquals(const std::string &input, const std::string &test)
Case insensitive equality predicate.
std::map< std::string, double > Composition
Map from string names to doubles.
Definition: ct_defs.h:180
shared_ptr< Falloff > newFalloff(const std::string &type, const vector_fp &c)
Return a pointer to a new falloff function calculator.
void warn_deprecated(const std::string &source, const AnyBase &node, const std::string &message)
A deprecation warning for syntax in an input file.
Definition: AnyMap.cpp:1901
const int THREE_BODY_RXN
A gas-phase reaction that requires a third-body collision partner.
Definition: reaction_defs.h:40
std::string toLowerCopy(const std::string &input)
Convert to lower case.
shared_ptr< ReactionRate > newReactionRate(const std::string &type)
Create a new empty ReactionRate object.
void readFalloff(FalloffReaction2 &R, const XML_Node &rc_node)
Parse falloff parameters, given a rateCoeff node.
Definition: Reaction.cpp:1405
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
Definition: ct_defs.h:184
const int ELEMENTARY_RXN
A reaction with a rate coefficient that depends only on temperature and voltage that also obeys mass-...
Definition: reaction_defs.h:34
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition: ct_defs.h:113
const int INTERFACE_RXN
A reaction occurring on an interface, e.g a surface or edge.
Definition: reaction_defs.h:79
void setupChemicallyActivatedReaction(ChemicallyActivatedReaction2 &, const XML_Node &)
Definition: Reaction.cpp:1783
size_t getFloatArray(const XML_Node &node, vector_fp &v, const bool convert=true, const std::string &unitsString="", const std::string &nodeName="floatArray")
This function reads the current node or a child node of the current node with the default name,...
Definition: ctml.cpp:258
void warn_user(const std::string &method, const std::string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Definition: global.h:245
const int CHEMACT_RXN
A chemical activation reaction.
Definition: reaction_defs.h:68
unique_ptr< Reaction > newReaction(const std::string &type)
Create a new empty Reaction object.
Definition: Reaction.cpp:1307
doublereal fpValueCheck(const std::string &val)
Translate a string into one doublereal value, with error checking.
const int FALLOFF_RXN
The general form for a gas-phase association or dissociation reaction, with a pressure-dependent rate...
Definition: reaction_defs.h:46
void tokenizeString(const std::string &oval, std::vector< std::string > &v)
This function separates a string up into tokens according to the location of white space.
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.
Definition: utilities.h:184
compositionMap parseCompString(const std::string &ss, const std::vector< std::string > &names=std::vector< std::string >())
Parse a composition string into a map consisting of individual key:composition pairs.
Definition: stringUtils.cpp:59
Contains declarations for string manipulation functions within Cantera.
Modifications to an InterfaceReaction2 rate based on a surface species coverage.
Definition: Reaction.h:404
Unit aggregation utility.
Definition: Units.h:99
Various templated functions that carry out common vector operations (see Templated Utility Functions)...