Cantera  2.5.1
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, i.e.,
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 
25 using namespace std;
26 
27 namespace 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") {
71  kin.skipUndeclaredThirdBodies(true);
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]));
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));
117  ++itot;
118  }
119  }
120  }
121  }
122  }
123  }
124 
125  if (check_for_duplicates) {
126  kin.checkDuplicates();
127  }
128 
129  return true;
130 }
131 
132 bool importKinetics(const XML_Node& phase, std::vector<ThermoPhase*> th,
133  Kinetics* k)
134 {
135  if (k == 0) {
136  return false;
137  }
138 
139  // This phase will be the owning phase for the kinetics operator
140  // For interfaces, it is the surface phase between two volumes.
141  // For homogeneous kinetics, it's the current volumetric phase.
142  string owning_phase = phase["id"];
143 
144  bool check_for_duplicates = false;
145  if (phase.parent() && phase.parent()->hasChild("validate")) {
146  const XML_Node& d = phase.parent()->child("validate");
147  if (d["reactions"] == "yes") {
148  check_for_duplicates = true;
149  }
150  }
151 
152  // If other phases are involved in the reaction mechanism, they must be
153  // listed in a 'phaseArray' child element. Homogeneous mechanisms do not
154  // need to include a phaseArray element.
155  vector<string> phase_ids;
156  if (phase.hasChild("phaseArray")) {
157  const XML_Node& pa = phase.child("phaseArray");
158  getStringArray(pa, phase_ids);
159  }
160  phase_ids.push_back(owning_phase);
161 
162  // for each referenced phase, attempt to find its id among those
163  // phases specified.
164  string msg = "";
165  for (size_t n = 0; n < phase_ids.size(); n++) {
166  string phase_id = phase_ids[n];
167  bool phase_ok = false;
168  // loop over the supplied 'ThermoPhase' objects representing
169  // phases, to find an object with the same id.
170  for (size_t m = 0; m < th.size(); m++) {
171  if (th[m]->name() == phase_id) {
172  phase_ok = true;
173  // if no phase with this id has been added to
174  //the kinetics manager yet, then add this one
175  if (k->phaseIndex(phase_id) == npos) {
176  k->addPhase(*th[m]);
177  }
178  }
179  msg += " "+th[m]->name();
180  }
181  if (!phase_ok) {
182  throw CanteraError("importKinetics",
183  "phase "+phase_id+" not found. Supplied phases are:"+msg);
184  }
185  }
186 
187  // allocates arrays, etc. Must be called after the phases have been added to
188  // 'kin', so that the number of species in each phase is known.
189  k->init();
190 
191  // Install the reactions.
192  return installReactionArrays(phase, *k, owning_phase, check_for_duplicates);
193 }
194 
195 bool buildSolutionFromXML(XML_Node& root, const std::string& id,
196  const std::string& nm, ThermoPhase* th, Kinetics* kin)
197 {
198  XML_Node* x = get_XML_NameID(nm, string("#")+id, &root);
199  if (!x) {
200  return false;
201  }
202 
203  // Fill in the ThermoPhase object by querying the const XML_Node tree
204  // located at x.
205  importPhase(*x, th);
206 
207  // Create a vector of ThermoPhase pointers of length 1 having the current th
208  // ThermoPhase as the entry.
209  std::vector<ThermoPhase*> phases{th};
210 
211  // Fill in the kinetics object k, by querying the const XML_Node tree
212  // located by x. The source terms and eventually the source term vector will
213  // be constructed from the list of ThermoPhases in the vector, phases.
214  importKinetics(*x, phases, kin);
215  return true;
216 }
217 
218 bool checkElectrochemReaction(const XML_Node& p, Kinetics& kin, const XML_Node& r)
219 {
220  // If other phases are involved in the reaction mechanism, they must be
221  // listed in a 'phaseArray' child element. Homogeneous mechanisms do not
222  // need to include a phaseArray element.
223  vector<string> phase_ids;
224  if (p.hasChild("phaseArray")) {
225  const XML_Node& pa = p.child("phaseArray");
226  getStringArray(pa, phase_ids);
227  }
228  phase_ids.push_back(p["id"]);
229 
230  // Get reaction product and reactant information
231  Composition reactants = parseCompString(r.child("reactants").value());
232  Composition products = parseCompString(r.child("products").value());
233 
234 
235  // If the reaction has undeclared species don't perform electrochemical check
236  for (const auto& sp : reactants) {
237  if (kin.kineticsSpeciesIndex(sp.first) == npos) {
238  return true;
239  }
240  }
241 
242  for (const auto& sp : products) {
243  if (kin.kineticsSpeciesIndex(sp.first) == npos) {
244  return true;
245  }
246  }
247 
248  // Initialize the electron counter for each phase
249  std::vector<double> e_counter(phase_ids.size(), 0.0);
250 
251  // Find the amount of electrons in the products for each phase
252  for (const auto& sp : products) {
253  const ThermoPhase& ph = kin.speciesPhase(sp.first);
254  size_t k = ph.speciesIndex(sp.first);
255  double stoich = sp.second;
256  for (size_t m = 0; m < phase_ids.size(); m++) {
257  if (phase_ids[m] == ph.name()) {
258  e_counter[m] += stoich * ph.charge(k);
259  break;
260  }
261  }
262  }
263 
264  // Subtract the amount of electrons in the reactants for each phase
265  for (const auto& sp : reactants) {
266  const ThermoPhase& ph = kin.speciesPhase(sp.first);
267  size_t k = ph.speciesIndex(sp.first);
268  double stoich = sp.second;
269  for (size_t m = 0; m < phase_ids.size(); m++) {
270  if (phase_ids[m] == ph.name()) {
271  e_counter[m] -= stoich * ph.charge(k);
272  break;
273  }
274  }
275  }
276 
277  // If the electrons change phases then the reaction is electrochemical
278  bool echemical = false;
279  for(size_t m = 0; m < phase_ids.size(); m++) {
280  if (fabs(e_counter[m]) > 1e-4) {
281  echemical = true;
282  break;
283  }
284  }
285 
286  // If the reaction is electrochemical, ensure the reaction is identified as
287  // electrochemical. If not already specified beta is assumed to be 0.5
288  std::string type = toLowerCopy(r["type"]);
289  if (!r.child("rateCoeff").hasChild("electrochem")) {
290  if ((type != "butlervolmer_noactivitycoeffs" &&
291  type != "butlervolmer" &&
292  type != "surfaceaffinity") &&
293  echemical) {
294  XML_Node& f = r.child("rateCoeff").addChild("electrochem","");
295  f.addAttribute("beta",0.5);
296  }
297  }
298  return true;
299 }
300 
301 }
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:111
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:189
virtual bool addReaction(shared_ptr< Reaction > r)
Add a single reaction to the mechanism.
Definition: Kinetics.cpp:476
void skipUndeclaredSpecies(bool skip)
Determine behavior when adding a new reaction that contains species not defined in any of the phases ...
Definition: Kinetics.h:740
virtual void init()
Prepare the class for the addition of reactions, after all phases have been added.
Definition: Kinetics.h:700
void skipUndeclaredThirdBodies(bool skip)
Determine behavior when adding a new reaction that contains third-body efficiencies for species not d...
Definition: Kinetics.h:752
thermo_t & 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:326
size_t kineticsSpeciesIndex(size_t k, size_t n) const
The location of species k of phase n in species arrays.
Definition: Kinetics.h:261
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:78
virtual void addPhase(thermo_t &thermo)
Add a phase to the kinetics manager object.
Definition: Kinetics.cpp:445
std::string name() const
Return the name of the phase.
Definition: Phase.cpp:84
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:643
size_t speciesIndex(const std::string &name) const
Returns the index of a species named 'name' within the Phase object.
Definition: Phase.cpp:201
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:104
std::string attrib(const std::string &attr) const
Function returns the value of an attribute.
Definition: xml.cpp:492
bool hasChild(const std::string &ch) const
Tests whether the current node has a child node with a particular name.
Definition: xml.cpp:528
XML_Node & root() const
Return the root of the current XML_Node tree.
Definition: xml.cpp:749
void addAttribute(const std::string &attrib, const std::string &value)
Add or modify an attribute of the current node.
Definition: xml.cpp:466
XML_Node * parent() const
Returns a pointer to the parent node of the current node.
Definition: xml.cpp:517
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:711
std::string value() const
Return the value of an XML node as a string.
Definition: xml.cpp:441
XML_Node & child(const size_t n) const
Return a changeable reference to the n'th child of the current node.
Definition: xml.cpp:546
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.
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:194
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:232
bool importKinetics(const XML_Node &phase, std::vector< ThermoPhase * > th, Kinetics *k)
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)
Install information about reactions into the kinetics object, kin.
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:188
std::map< std::string, double > Composition
Map from string names to doubles.
Definition: ct_defs.h:176
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.cpp:264
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:427
shared_ptr< Reaction > newReaction(const XML_Node &rxn_node)
Create a new Reaction object for the reaction defined in rxn_node
Definition: Reaction.cpp:1010
std::string toLowerCopy(const std::string &input)
Convert to lower case.
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)
Parse a composition string into a map consisting of individual key:composition pairs.
Definition: stringUtils.cpp:60
Contains declarations for string manipulation functions within Cantera.