Cantera 2.6.0
importKinetics.cpp
Go to the documentation of this file.
1/**
2 * @file importKinetics.cpp
3 * Declarations of global routines for the importing
4 * of kinetics data from XML files (see \ref inputfiles).
5 *
6 * This file contains routines which are global routines, that is,
7 * not part of any object. These routine take as input, ctml
8 * pointers to data, and pointers to %Cantera objects. The purpose
9 * of these routines is to initialize the %Cantera objects with data
10 * from the ctml tree structures.
11 */
12
13// This file is part of Cantera. See License.txt in the top-level directory or
14// at https://cantera.org/license.txt for license and copyright information.
15
20#include "cantera/base/ctml.h"
21#include "cantera/base/yaml.h"
22
23#include <cstring>
24
25using namespace std;
26
27namespace Cantera
28{
29
31 std::string default_phase, bool check_for_duplicates)
32{
33 int itot = 0;
34
35 // Search the children of the phase element for the XML element named
36 // reactionArray. If we can't find it, then return signaling having not
37 // found any reactions. Apparently, we allow multiple reactionArray elements
38 // here Each one will be processed sequentially, with the end result being
39 // purely additive.
40 vector<XML_Node*> rarrays = p.getChildren("reactionArray");
41 if (rarrays.empty()) {
42 return false;
43 }
44 for (size_t n = 0; n < rarrays.size(); n++) {
45 // Go get a reference to the current XML element, reactionArray. We will
46 // process this element now.
47 const XML_Node& rxns = *rarrays[n];
48
49 // The reactionArray element has an attribute called, datasrc. The value
50 // of the attribute is the XML element comprising the top of the tree of
51 // reactions for the phase. Find this datasrc element starting with the
52 // root of the current XML node.
53 const XML_Node* rdata = get_XML_Node(rxns["datasrc"], &rxns.root());
54
55 // If the reactionArray element has a child element named "skip", and if
56 // the attribute of skip called "species" has a value of "undeclared",
57 // we will set rxnrule.skipUndeclaredSpecies to 'true'. rxnrule is
58 // passed to the routine that parses each individual reaction so that
59 // the parser will skip all reactions containing an undefined species
60 // without throwing an error.
61 //
62 // Similarly, an attribute named "third_bodies" with the value of
63 // "undeclared" will skip undeclared third body efficiencies (while
64 // retaining the reaction and any other efficiencies).
65 if (rxns.hasChild("skip")) {
66 const XML_Node& sk = rxns.child("skip");
67 if (sk["species"] == "undeclared") {
68 kin.skipUndeclaredSpecies(true);
69 }
70 if (sk["third_bodies"] == "undeclared") {
72 }
73 }
74
75 // Search for child elements called include. We only include a reaction
76 // if it's tagged by one of the include fields. Or, we include all
77 // reactions if there are no include fields.
78 vector<XML_Node*> incl = rxns.getChildren("include");
79 vector<XML_Node*> allrxns = rdata->getChildren("reaction");
80 // if no 'include' directive, then include all reactions
81 if (incl.empty()) {
82 for (size_t i = 0; i < allrxns.size(); i++) {
83 checkElectrochemReaction(p,kin,*allrxns[i]);
84 kin.addReaction(newReaction(*allrxns[i]), false);
85 ++itot;
86 }
87 } else {
88 for (size_t nii = 0; nii < incl.size(); nii++) {
89 const XML_Node& ii = *incl[nii];
90 string imin = ii["min"];
91 string imax = ii["max"];
92
93 string::size_type iwild = string::npos;
94 if (imax == imin) {
95 iwild = imin.find("*");
96 if (iwild != string::npos) {
97 imin = imin.substr(0,iwild);
98 imax = imin;
99 }
100 }
101
102 for (size_t i = 0; i < allrxns.size(); i++) {
103 const XML_Node* r = allrxns[i];
104 string rxid;
105 if (r) {
106 rxid = r->attrib("id");
107 if (iwild != string::npos) {
108 rxid = rxid.substr(0,iwild);
109 }
110
111 // To decide whether the reaction is included or not we
112 // do a lexical min max and operation. This sometimes
113 // has surprising results.
114 if ((rxid >= imin) && (rxid <= imax)) {
115 checkElectrochemReaction(p,kin,*r);
116 kin.addReaction(newReaction(*r), false);
117 ++itot;
118 }
119 }
120 }
121 }
122 }
123 }
124
125 if (check_for_duplicates) {
126 kin.checkDuplicates();
127 }
128
129 kin.resizeReactions();
130
131 return true;
132}
133
134bool importKinetics(const XML_Node& phase, std::vector<ThermoPhase*> th,
135 Kinetics* k)
136{
137 if (k == 0) {
138 return false;
139 }
140
141 // This phase will be the owning phase for the kinetics operator
142 // For interfaces, it is the surface phase between two volumes.
143 // For homogeneous kinetics, it's the current volumetric phase.
144 string owning_phase = phase["id"];
145
146 bool check_for_duplicates = false;
147 if (phase.parent() && phase.parent()->hasChild("validate")) {
148 const XML_Node& d = phase.parent()->child("validate");
149 if (d["reactions"] == "yes") {
150 check_for_duplicates = true;
151 }
152 }
153
154 // If other phases are involved in the reaction mechanism, they must be
155 // listed in a 'phaseArray' child element. Homogeneous mechanisms do not
156 // need to include a phaseArray element.
157 vector<string> phase_ids;
158 if (phase.hasChild("phaseArray")) {
159 const XML_Node& pa = phase.child("phaseArray");
160 getStringArray(pa, phase_ids);
161 }
162 phase_ids.push_back(owning_phase);
163
164 // for each referenced phase, attempt to find its id among those
165 // phases specified.
166 string msg = "";
167 for (size_t n = 0; n < phase_ids.size(); n++) {
168 string phase_id = phase_ids[n];
169 bool phase_ok = false;
170 // loop over the supplied 'ThermoPhase' objects representing
171 // phases, to find an object with the same id.
172 for (size_t m = 0; m < th.size(); m++) {
173 if (th[m]->name() == phase_id) {
174 phase_ok = true;
175 // if no phase with this id has been added to
176 //the kinetics manager yet, then add this one
177 if (k->phaseIndex(phase_id) == npos) {
178 k->addPhase(*th[m]);
179 }
180 }
181 msg += " "+th[m]->name();
182 }
183 if (!phase_ok) {
184 throw CanteraError("importKinetics",
185 "phase "+phase_id+" not found. Supplied phases are:"+msg);
186 }
187 }
188
189 // allocates arrays, etc. Must be called after the phases have been added to
190 // 'kin', so that the number of species in each phase is known.
191 k->init();
192
193 // Install the reactions.
194 return installReactionArrays(phase, *k, owning_phase, check_for_duplicates);
195}
196
197bool buildSolutionFromXML(XML_Node& root, const std::string& id,
198 const std::string& nm, ThermoPhase* th, Kinetics* kin)
199{
200 XML_Node* x = get_XML_NameID(nm, string("#")+id, &root);
201 if (!x) {
202 return false;
203 }
204
205 // Fill in the ThermoPhase object by querying the const XML_Node tree
206 // located at x.
207 importPhase(*x, th);
208
209 // Create a vector of ThermoPhase pointers of length 1 having the current th
210 // ThermoPhase as the entry.
211 std::vector<ThermoPhase*> phases{th};
212
213 // Fill in the kinetics object k, by querying the const XML_Node tree
214 // located by x. The source terms and eventually the source term vector will
215 // be constructed from the list of ThermoPhases in the vector, phases.
216 importKinetics(*x, phases, kin);
217 return true;
218}
219
221{
222 // If other phases are involved in the reaction mechanism, they must be
223 // listed in a 'phaseArray' child element. Homogeneous mechanisms do not
224 // need to include a phaseArray element.
225 vector<string> phase_ids;
226 if (p.hasChild("phaseArray")) {
227 const XML_Node& pa = p.child("phaseArray");
228 getStringArray(pa, phase_ids);
229 }
230 phase_ids.push_back(p["id"]);
231
232 // Get reaction product and reactant information
233 Composition reactants = parseCompString(r.child("reactants").value());
234 Composition products = parseCompString(r.child("products").value());
235
236
237 // If the reaction has undeclared species don't perform electrochemical check
238 for (const auto& sp : reactants) {
239 if (kin.kineticsSpeciesIndex(sp.first) == npos) {
240 return true;
241 }
242 }
243
244 for (const auto& sp : products) {
245 if (kin.kineticsSpeciesIndex(sp.first) == npos) {
246 return true;
247 }
248 }
249
250 // Initialize the electron counter for each phase
251 std::vector<double> e_counter(phase_ids.size(), 0.0);
252
253 // Find the amount of electrons in the products for each phase
254 for (const auto& sp : products) {
255 const ThermoPhase& ph = kin.speciesPhase(sp.first);
256 size_t k = ph.speciesIndex(sp.first);
257 double stoich = sp.second;
258 for (size_t m = 0; m < phase_ids.size(); m++) {
259 if (phase_ids[m] == ph.name()) {
260 e_counter[m] += stoich * ph.charge(k);
261 break;
262 }
263 }
264 }
265
266 // Subtract the amount of electrons in the reactants for each phase
267 for (const auto& sp : reactants) {
268 const ThermoPhase& ph = kin.speciesPhase(sp.first);
269 size_t k = ph.speciesIndex(sp.first);
270 double stoich = sp.second;
271 for (size_t m = 0; m < phase_ids.size(); m++) {
272 if (phase_ids[m] == ph.name()) {
273 e_counter[m] -= stoich * ph.charge(k);
274 break;
275 }
276 }
277 }
278
279 // If the electrons change phases then the reaction is electrochemical
280 bool echemical = false;
281 for(size_t m = 0; m < phase_ids.size(); m++) {
282 if (fabs(e_counter[m]) > 1e-4) {
283 echemical = true;
284 break;
285 }
286 }
287
288 // If the reaction is electrochemical, ensure the reaction is identified as
289 // electrochemical. If not already specified beta is assumed to be 0.5
290 std::string type = toLowerCopy(r["type"]);
291 if (!r.child("rateCoeff").hasChild("electrochem")) {
292 if ((type != "butlervolmer_noactivitycoeffs" &&
293 type != "butlervolmer" &&
294 type != "surfaceaffinity") &&
295 echemical) {
296 XML_Node& f = r.child("rateCoeff").addChild("electrochem","");
297 f.addAttribute("beta",0.5);
298 }
299 }
300 return true;
301}
302
303}
Headers for the factory class that can create known ThermoPhase objects (see Thermodynamic Properties...
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
Public interface for kinetics managers.
Definition: Kinetics.h:114
virtual void resizeReactions()
Finalize Kinetics object and associated objects.
Definition: Kinetics.cpp:48
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 phaseIndex(const std::string &ph) const
Return the phase index of a phase in the list of phases defined within the object.
Definition: Kinetics.h:193
virtual void addPhase(ThermoPhase &thermo)
Add a phase to the kinetics manager object.
Definition: Kinetics.cpp:616
virtual bool addReaction(shared_ptr< Reaction > r, bool resize=true)
Add a single reaction to the mechanism.
Definition: Kinetics.cpp:660
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
virtual void init()
Prepare the class for the addition of reactions, after all phases have been added.
Definition: Kinetics.h:1125
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
virtual std::pair< size_t, size_t > checkDuplicates(bool throw_err=true) const
Check for unmarked duplicate reactions and unmatched marked duplicates.
Definition: Kinetics.cpp:103
std::string name() const
Return the name of the phase.
Definition: Phase.cpp:70
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 speciesIndex(const std::string &name) const
Returns the index of a species named 'name' within the Phase object.
Definition: Phase.cpp:187
Base class for a phase with thermodynamic properties.
Definition: ThermoPhase.h:102
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:103
std::string attrib(const std::string &attr) const
Function returns the value of an attribute.
Definition: xml.cpp:493
bool hasChild(const std::string &ch) const
Tests whether the current node has a child node with a particular name.
Definition: xml.cpp:529
XML_Node & root() const
Return the root of the current XML_Node tree.
Definition: xml.cpp:750
void addAttribute(const std::string &attrib, const std::string &value)
Add or modify an attribute of the current node.
Definition: xml.cpp:467
XML_Node * parent() const
Returns a pointer to the parent node of the current node.
Definition: xml.cpp:518
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
std::string value() const
Return the value of an XML node as a string.
Definition: xml.cpp:442
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.
bool buildSolutionFromXML(XML_Node &root, const std::string &id, const std::string &nm, ThermoPhase *th, Kinetics *kin)
Build a single-phase ThermoPhase object with associated kinetics mechanism.
bool importKinetics(const XML_Node &phase, std::vector< ThermoPhase * > th, Kinetics *kin)
Import a reaction mechanism for a phase or an interface.
bool installReactionArrays(const XML_Node &p, Kinetics &kin, std::string default_phase, bool check_for_duplicates=false)
Install information about reactions into the kinetics object, kin.
void importPhase(XML_Node &phase, ThermoPhase *th)
Import a phase information into an empty ThermoPhase object.
Definitions of global routines for the importing of data from XML files (see Input File Handling).
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
std::map< std::string, double > Composition
Map from string names to doubles.
Definition: ct_defs.h:180
XML_Node * get_XML_Node(const std::string &file_ID, XML_Node *root)
This routine will locate an XML node in either the input XML tree or in another input file specified ...
Definition: global.cpp:235
std::string toLowerCopy(const std::string &input)
Convert to lower case.
unique_ptr< Reaction > newReaction(const std::string &type)
Create a new empty Reaction object.
Definition: Reaction.cpp:1307
XML_Node * get_XML_NameID(const std::string &nameTarget, const std::string &file_ID, XML_Node *root)
This routine will locate an XML node in either the input XML tree or in another input file specified ...
Definition: global.cpp:273
bool checkElectrochemReaction(const XML_Node &p, Kinetics &kin, const XML_Node &r)
Check to ensure that all electrochemical reactions are specified correctly.
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.