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