Cantera  2.0
ctml.cpp
Go to the documentation of this file.
1 /**
2  * @file ctml.cpp
3  * Definitions for functions to read and write CTML.
4  *
5  */
6 // Copyright 2002 California Institute of Technology
7 
8 #include "cantera/base/ctml.h"
9 
10 //@{
11 #define CTML_VERSION_1_4_1
12 //@}
13 
14 #include "cantera/base/global.h"
16 
17 #include <cctype>
18 #include <cstring>
19 #include <cstdlib>
20 
21 
22 using namespace std;
23 using namespace Cantera;
24 
25 namespace ctml
26 {
27 
28 std::string FP_Format = "%23.15E";
29 std::string INT_Format = "%8d";
30 
31 //====================================================================================================================
32 //! Convert a floating point value from a string to a double
33 /*!
34  * @param val String value input
35  *
36  * @return Returns a double
37  */
38 static doublereal fpValue(std::string val)
39 {
40  return atof(stripws(val).c_str());
41 }
42 //====================================================================================================================
43 // This function adds a child node with the name, "bool", with a value
44 // consisting of a single bool
45 /*
46  * This function will add a child node to the current XML node, with the
47  * name "bool". It will have a title attribute, and the body
48  * of the XML node will be filled out with a single bool
49  *
50  * Example:
51  *
52  * Code snippet:
53  * @verbatim
54  const XML_Node &node;
55  std::string titleString = "doSpecialOp";
56  bool value = true;
57  addBool(node, titleString, value);
58  @endverbatim
59  *
60  * Creates the following the snippet in the XML file:
61  * @verbatim
62  <parentNode>
63  <bool title="doSpecialOp" type="optional">
64  true
65  <\integer>
66  <\parentNode>
67  @endverbatim
68  *
69  * @param node reference to the XML_Node object of the parent XML element
70  * @param titleString String name of the title attribute
71  * @param value Value - single bool
72  *
73  * @todo I don't think this is used. Figure out what is used for writing bools,
74  * and codify that.
75  */
76 void addBool(Cantera::XML_Node& node, const std::string& title, const bool val)
77 {
78  std::string v = (val ? "true" : "false");
79  XML_Node& f = node.addChild("bool", v);
80  f.addAttribute("title", title);
81 }
82 //====================================================================================================================
83 // This function adds a child node with the name, "integer", with a value
84 // consisting of a single integer
85 /*
86  * This function will add a child node to the current XML node, with the
87  * name "integer". It will have a title attribute, and the body
88  * of the XML node will be filled out with a single integer
89  *
90  * Example:
91  *
92  * Code snippet:
93  * @verbatim
94  const XML_Node &node;
95  std::string titleString = "maxIterations";
96  int value = 1000;
97  std::string typeString = "optional";
98  std::string units = "";
99  addInteger(node, titleString, value, typeString, units);
100  @endverbatim
101  *
102  * Creates the following the snippet in the XML file:
103  * @verbatim
104  <parentNode>
105  <integer title="maxIterations" type="optional">
106  100
107  <\integer>
108  <\parentNode>
109  @endverbatim
110  *
111  * @param node reference to the XML_Node object of the parent XML element
112  * @param titleString String name of the title attribute
113  * @param value Value - single integer
114  * @param unitsString String name of the Units attribute. The default is to
115  * have an empty string.
116  * @param typeString String type. This is an optional parameter. The default
117  * is to have an empty string.
118  */
119 void addInteger(Cantera::XML_Node& node, const std::string& title, const int val,
120  const std::string units, const std::string type)
121 {
122 #ifdef CTML_VERSION_1_4
123  XML_Node& f = node.addChild("integer", val);
124  f.addAttribute("title", title);
125 #else
126  XML_Node& f = node.addChild(title, val);
127 #endif
128  f.addAttribute("vtype", "integer");
129  if (type != "") {
130  f.addAttribute("type",type);
131  }
132  if (units != "") {
133  f.addAttribute("units",units);
134  }
135 }
136 //====================================================================================================================
137 // This function adds a child node with the name, "intArray", with a value
138 // consisting of a comma separated list of integers
139 /*
140  * This function will add a child node to the current XML node, with the
141  * name "intArray". It will have a title attribute, and the body
142  * of the XML node will be filled out with a comma separated list of
143  * integers
144  *
145  * Example:
146  *
147  * @verbatim
148  const XML_Node &node;
149  std::string titleString = "additionalCases";
150  int n = 3;
151  int cases[3] = [3, 6, 10];
152  std::string typeString = "optional";
153  std::string units = "";
154  addIntegerArray(node, titleString, n, &cases[0], typeString, units);
155  @endverbatim
156  *
157  * Creates the following the snippet in the XML file:
158  * @verbatim
159  <parentNode>
160  <intArray title="additionalCases" type="optional">
161  3, 6, 10
162  <\intArray>
163  <\parentNode>
164  @endverbatim
165  *
166  *
167  * @param node reference to the XML_Node object of the parent XML element
168  * @param titleString String name of the title attribute
169  * @param n Length of the integer vector.
170  * @param values Pointer to a vector of integers
171  * @param unitsString String name of the Units attribute. This is an optional
172  * parameter. The default is to
173  * have an empty string.
174  * @param typeString String type. This is an optional parameter. The default
175  * is to have an empty string.
176  * @param minval Minimum allowed value of the int. This is an optional
177  * parameter. The default is the
178  * special double, Cantera::Undef, which means to ignore the
179  * entry.
180  * @param maxval Maximum allowed value of the int. This is an optional
181  * parameter. The default is the
182  * special double, Cantera::Undef, which means to ignore the
183  * entry.
184  *
185  * @todo I don't think this is used. Figure out what is used for writing integers,
186  * and codify that. unitsString shouldn't be here, since it's an int.
187  * typeString should be codified as to its usage.
188  */
189 void addIntegerArray(Cantera::XML_Node& node, const std::string& title, const size_t n,
190  const int* const vals, const std::string units, const std::string type,
191  const doublereal minval, const doublereal maxval)
192 {
193  std::string v = "";
194  for (size_t i = 0; i < n; i++) {
195  v += int2str(vals[i],INT_Format);
196  if (i == n-1) {
197  v += "\n";
198  } else if (i > 0 && (i+1) % 3 == 0) {
199  v += ",\n";
200  } else {
201  v += ", ";
202  }
203  }
204 #ifdef CTML_VERSION_1_4
205  XML_Node& f = node.addChild("intArray",v);
206  f.addAttribute("title",title);
207 #else
208  XML_Node& f = node.addChild(title, v);
209 #endif
210  if (type != "") {
211  f.addAttribute("type",type);
212  }
213  f.addAttribute("size", static_cast<double>(n));
214 #ifndef CTML_VERSION_1_4
215  f.addAttribute("vtype", "intArray");
216 #endif
217  if (units != "") {
218  f.addAttribute("units",units);
219  }
220  if (minval != Undef) {
221  f.addAttribute("min",minval);
222  }
223  if (maxval != Undef) {
224  f.addAttribute("max",maxval);
225  }
226 }
227 //====================================================================================================================
228 // This function adds a child node with the name, "float", with a value
229 // consisting of a single floating point number
230 /*
231  * This function will add a child node to the current XML node, with the
232  * name "float". It will have a title attribute, and the body
233  * of the XML node will be filled out with a single float
234  *
235  * Example:
236  *
237  * Code snippet:
238  * @verbatim
239  const XML_Node &node;
240  std::string titleString = "activationEnergy";
241  double value = 50.3;
242  doublereal maxval = 1.0E3;
243  doublereal minval = 0.0;
244  std::string typeString = "optional";
245  std::string unitsString = "kcal/gmol";
246  addFloat(node, titleString, value, unitsString, typeString, minval, maxval);
247  @endverbatim
248  *
249  * Creates the following the snippet in the XML file:
250  * @verbatim
251  <parentNode>
252  <float title="activationEnergy" type="optional" units="kcal/gmol" min="0.0" max="1.0E3">
253  50.3
254  <\float>
255  <\parentNode>
256  @endverbatim
257  *
258  * @param node reference to the XML_Node object of the parent XML element
259  * @param titleString String name of the title attribute
260  * @param value Value - single integer
261  * @param unitsString String name of the Units attribute. The default is to
262  * have an empty string.
263  * @param typeString String type. This is an optional parameter. The default
264  * is to have an empty string.
265  *
266  * @todo minval and maxval should be codified. typeString should be codified
267  * as to its usage.
268  */
269 void addFloat(Cantera::XML_Node& node, const std::string& title,
270  const doublereal val, const std::string units,
271  const std::string type, const doublereal minval,
272  const doublereal maxval)
273 {
274 #ifdef CTML_VERSION_1_4
275  XML_Node& f = node.addChild("float", val, ctml::FP_Format);
276  f.addAttribute("title", title);
277 #else
278  XML_Node& f = node.addChild(title, val, ctml::FP_Format);
279 #endif
280  if (type != "") {
281  f.addAttribute("type",type);
282  }
283  if (units != "") {
284  f.addAttribute("units",units);
285  }
286  f.addAttribute("vtype", "float");
287  if (minval != Undef) {
288  f.addAttribute("min",minval);
289  }
290  if (maxval != Undef) {
291  f.addAttribute("max",maxval);
292  }
293 }
294 //====================================================================================================================
295 // This function adds a child node with the name, "floatArray", with a value
296 // consisting of a comma separated list of floats
297 /*
298  * This function will add a child node to the current XML node, with the
299  * name "floatArray". It will have a title attribute, and the body
300  * of the XML node will be filled out with a comma separated list of
301  * integers
302  *
303  * Example:
304  *
305  * Code snippet:
306  * @verbatim
307  const XML_Node &node;
308  std::string titleString = "additionalTemperatures";
309  int n = 3;
310  int Tcases[3] = [273.15, 298.15, 373.15];
311  std::string typeString = "optional";
312  std::string units = "Kelvin";
313  addFloatArray(node, titleString, n, &cases[0], typeString, units);
314  @endverbatim
315  *
316  * Creates the following the snippet in the XML file:
317  * @verbatim
318  <parentNode>
319  <floatArray title="additionalTemperatures" type="optional" units="Kelvin">
320  273.15, 298.15, 373.15
321  <\floatArray>
322  <\parentNode>
323  @endverbatim
324  *
325  * @param node reference to the XML_Node object of the parent XML element
326  * @param titleString String name of the title attribute
327  * @param n Length of the doubles vector.
328  * @param values Pointer to a vector of doubles
329  * @param unitsString String name of the Units attribute. This is an optional
330  * parameter. The default is to
331  * have an empty string.
332  * @param type String type. This is an optional parameter. The default
333  * is to have an empty string.
334  * @param minval Minimum allowed value of the int. This is an optional
335  * parameter. The default is the
336  * special double, Cantera::Undef, which means to ignore the
337  * entry.
338  * @param maxval Maximum allowed value of the int. This is an optional
339  * parameter. The default is the
340  * special double, Cantera::Undef, which means to ignore the
341  * entry.
342  *
343  * @todo typeString should be codified as to its usage.
344  */
345 void addFloatArray(Cantera::XML_Node& node, const std::string& title, const size_t n,
346  const doublereal* const vals, const std::string units,
347  const std::string type,
348  const doublereal minval, const doublereal maxval)
349 {
350  size_t i;
351  std::string v = "";
352  for (i = 0; i < n; i++) {
353  v += fp2str(vals[i],FP_Format);
354  if (i == n-1) {
355  v += "\n";
356  } else if (i > 0 && (i+1) % 3 == 0) {
357  v += ",\n";
358  } else {
359  v += ", ";
360  }
361  }
362  XML_Node& f = node.addChild("floatArray",v);
363  f.addAttribute("title",title);
364  if (type != "") {
365  f.addAttribute("type",type);
366  }
367  f.addAttribute("size", double(n));
368  if (units != "") {
369  f.addAttribute("units",units);
370  }
371  if (minval != Undef) {
372  f.addAttribute("min",minval);
373  }
374  if (maxval != Undef) {
375  f.addAttribute("max",maxval);
376  }
377 }
378 //====================================================================================================================
379 // This function adds a child node with the name given by the first parameter with a value
380 // consisting of a comma separated list of floats
381 /*
382  * This function will add a child node to the current XML node, with the
383  * name given in the list. It will have a title attribute, and the body
384  * of the XML node will be filled out with a comma separated list of
385  * integers
386  *
387  * Example:
388  *
389  * Code snippet:
390  * @verbatim
391  const XML_Node &node;
392  std::string titleString = "additionalTemperatures";
393  int n = 3;
394  int Tcases[3] = [273.15, 298.15, 373.15];
395  std::string typeString = "optional";
396  std::string units = "Kelvin";
397  addNamedFloatArray(node, titleString, n, &cases[0], typeString, units);
398  @endverbatim
399  *
400  * Creates the following the snippet in the XML file:
401  * @verbatim
402  <parentNode>
403  <additionalTemperatures type="optional" vtype="floatArray" size = "3" units="Kelvin">
404  273.15, 298.15, 373.15
405  <\additionalTemperatures>
406  <\parentNode>
407  @endverbatim
408  *
409  * @param node reference to the XML_Node object of the parent XML element
410  * @param name Name of the XML node
411  * @param n Length of the doubles vector.
412  * @param values Pointer to a vector of doubles
413  * @param unitsString String name of the Units attribute. This is an optional
414  * parameter. The default is to
415  * have an empty string.
416  * @param type String type. This is an optional parameter. The default
417  * is to have an empty string.
418  * @param minval Minimum allowed value of the int. This is an optional
419  * parameter. The default is the
420  * special double, Cantera::Undef, which means to ignore the
421  * entry.
422  * @param maxval Maximum allowed value of the int. This is an optional
423  * parameter. The default is the
424  * special double, Cantera::Undef, which means to ignore the
425  * entry.
426  *
427  * @todo I don't think this is used. Figure out what is used for writing integers,
428  * and codify that. unitsString shouldn't be here, since it's an int.
429  * typeString should be codified as to its usage.
430  */
431 void addNamedFloatArray(Cantera::XML_Node& node, const std::string& name, const int n,
432  const doublereal* const vals, const std::string units,
433  const std::string type, const doublereal minval,
434  const doublereal maxval)
435 {
436  int i;
437  std::string v = "";
438  for (i = 0; i < n; i++) {
439  v += fp2str(vals[i],FP_Format);
440  if (i == n-1) {
441  v += "\n";
442  } else if (i > 0 && (i+1) % 3 == 0) {
443  v += ",\n";
444  } else {
445  v += ", ";
446  }
447  }
448  XML_Node& f = node.addChild(name, v);
449  if (type != "") {
450  f.addAttribute("type",type);
451  }
452  /*
453  * Add vtype, which indicates the type of the value. Here we specify it as a list of floats separated
454  * by commas, with a length given by size attribute.
455  */
456  f.addAttribute("vtype", "floatArray");
457 
458  f.addAttribute("size", n);
459  if (units != "") {
460  f.addAttribute("units", units);
461  }
462  if (minval != Undef) {
463  f.addAttribute("min", minval);
464  }
465  if (maxval != Undef) {
466  f.addAttribute("max", maxval);
467  }
468 }
469 //====================================================================================================================
470 // This function adds a child node with the name string with a string value
471 // to the current node
472 /*
473  * This function will add a child node to the current XML node, with the
474  * name "string". It will have a title attribute, and the body
475  * of the XML node will be filled out with the valueString argument verbatim.
476  *
477  * Example:
478  *
479  * Code snippet:
480  * @verbatim
481  const XML_Node &node;
482  addString(XML_Node& node, std::string titleString, std::string valueString,
483  std::string typeString);
484  @endverbatim
485  *
486  * Creates the following the snippet in the XML file:
487  * @verbatim
488  <string title="titleString" type="typeString">
489  valueString
490  <\string>
491  @endverbatim
492  *
493  * @param node reference to the XML_Node object of the parent XML element
494  * @param valueString Value string to be used in the new XML node.
495  * @param titleString String name of the title attribute
496  * @param typeString String type. This is an optional parameter.
497  */
498 void addString(Cantera::XML_Node& node, const std::string& titleString,
499  const std::string& valueString,
500  const std::string typeString)
501 {
502  XML_Node& f = node.addChild("string", valueString);
503  f.addAttribute("title", titleString);
504  if (typeString != "") {
505  f.addAttribute("type", typeString);
506  }
507 }
508 
509 XML_Node* getByTitle(const Cantera::XML_Node& node, const std::string& title)
510 {
511  XML_Node* s = node.findByAttr("title", title);
512  if (!s) {
513  return 0;
514  }
515  if (s->parent() == &node) {
516  return s;
517  }
518  return 0;
519 }
520 //====================================================================================================================
521 // This function reads a child node with the name string and returns
522 // its xml value as the return string
523 /*
524  * If the child XML_node named "name" doesn't exist, the empty string is returned.
525  *
526  * Code snippet:
527  * @verbatim
528  const XML_Node &parent;
529  string name = "vacency_species";
530  string valueString = getChildValue(parent, name
531  std::string typeString);
532  @endverbatim
533  *
534  * returns valueString = "O(V)"
535  *
536  * from the following the snippet in the XML file:
537  *
538  * @verbatim
539  <vacencySpecies>
540  O(V)
541  <\vancencySpecies>
542  @endverbatim
543  *
544  * @param parent parent reference to the XML_Node object of the parent XML element
545  * @param name Name of the child XML_Node to read the value from.
546  *
547  * @return String value of the child XML_Node
548  */
549 std::string getChildValue(const Cantera::XML_Node& parent, const std::string& nameString)
550 {
551  if (!parent.hasChild(nameString)) {
552  return "";
553  }
554  return parent(nameString);
555 }
556 //====================================================================================================================
557 // This function reads a child node with the name, "string", with a specific
558 // title attribute named "titleString"
559 /*
560  * This function will read a child node to the current XML node, with the
561  * name "string". It must have a title attribute, named titleString, and the body
562  * of the XML node will be read into the valueString output argument.
563  *
564  * Example:
565  *
566  * Code snippet:
567  * @verbatim
568  const XML_Node &node;
569  getString(XML_Node& node, std::string titleString, std::string valueString,
570  std::string typeString);
571  @endverbatim
572  *
573  * Reads the following the snippet in the XML file:
574  * @verbatim
575  <string title="titleString" type="typeString">
576  valueString
577  <\string>
578  @endverbatim
579  *
580  * @param node Reference to the XML_Node object of the parent XML element
581  * @param titleString String name of the title attribute of the child node
582  * @param valueString Value string that is found in the child node. output variable
583  * @param typeString String type. This is an optional output variable. It is filled
584  * with the attribute "type" of the XML entry.
585  */
586 void getString(const Cantera::XML_Node& node, const std::string& titleString, std::string& valueString,
587  std::string& typeString)
588 {
589  valueString = "";
590  typeString = "";
591  XML_Node* s = getByTitle(node, titleString);
592  if (s)
593  if (s->name() == "string") {
594  valueString = (*s).value();
595  typeString = (*s)["type"];
596  return;
597  }
598 }
599 
600 //====================================================================================================================
601 // This function attempts to read a named child node and returns with the contents in the value string.
602 // title attribute named "titleString"
603 /*
604  * This function will read a child node to the current XML node, with the
605  * name "string". It must have a title attribute, named titleString, and the body
606  * of the XML node will be read into the valueString output argument.
607  *
608  * If the child node is not found then the empty string is returned.
609  *
610  * Example:
611  *
612  * Code snippet:
613  * @verbatim
614  const XML_Node &node;
615  std::string valueString;
616  std::string typeString;
617  std::string nameString = "timeIncrement";
618  getString(XML_Node& node, nameString, valueString, valueString, typeString);
619  @endverbatim
620  *
621  * Reads the following the snippet in the XML file:
622  *
623  * * @verbatim
624  <nameString type="typeString">
625  valueString
626  <\nameString>
627  @endverbatim
628  *
629  * or alternatively as a retrofit and special case, it also reads the following case
630  *
631  * @verbatim
632  <string title="nameString" type="typeString">
633  valueString
634  <\string>
635  @endverbatim
636  *
637  * @param node Reference to the XML_Node object of the parent XML element
638  * @param nameString Name of the XML Node input variable
639  * @param valueString Value string that is found in the child node. output variable
640  * @param typeString String type. This is an optional output variable. It is filled
641  * with the attribute "type" of the XML entry. output variable
642  */
643 void getNamedStringValue(const Cantera::XML_Node& node, const std::string& nameString, std::string& valueString,
644  std::string& typeString)
645 {
646  valueString = "";
647  typeString = "";
648  if (node.hasChild(nameString)) {
649  XML_Node& xc = node.child(nameString);
650  valueString = xc.value();
651  typeString = xc["type"];
652  } else {
653  XML_Node* s = getByTitle(node, nameString);
654  if (s) {
655  if (s->name() == "string") {
656  valueString = (*s).value();
657  typeString = (*s)["type"];
658  return;
659  }
660  }
661  }
662 }
663 //====================================================================================================================
664 // Get a vector of integer values from a child element.
665 /*
666  * Returns a std::map containing a keyed values for child XML_Nodes
667  * of the current node with the name, "integer".
668  * In the keyed mapping there will be a list of titles vs. values
669  * for all of the XML nodes.
670  * The integer XML_nodes are expected to be in a particular form created
671  * by the function addInteger(). One value per XML_node is expected.
672  *
673  *
674  * Example:
675  *
676  * Code snippet:
677  * @verbatim
678  const XML_Node &State_XMLNode;
679  std::map<std::string, integer> v;
680  getinteger(State_XMLNode, v);
681  @endverbatim
682  *
683  * reads the corresponding XML file:
684  *
685  * @verbatim
686  <state>
687  <integer title="i1"> 1 <\integer>
688  <integer title="i2"> 2 <\integer>
689  <integer title="i3"> 3 <\integer>
690  <\state>
691  @endverbatim
692  *
693  * Will produce the mapping:
694  *
695  * v["i1"] = 1
696  * v["i2"] = 2
697  * v["i3"] = 3
698  *
699  *
700  * @param node Current XML node to get the values from
701  * @param v Output map of the results.
702  */
704  std::map<std::string, int>& v)
705 {
706  std::vector<XML_Node*> f;
707  node.getChildren("integer",f);
708  int n = static_cast<int>(f.size());
709  integer x;
710  std::string typ, title, vmin, vmax;
711  for (int i = 0; i < n; i++) {
712  const XML_Node& fi = *(f[i]);
713  x = atoi(fi().c_str());
714  title = fi["title"];
715  vmin = fi["min"];
716  vmax = fi["max"];
717  if (vmin != "")
718  if (fi["max"] != "") {
719  v[title] = x;
720  }
721  }
722 }
723 
724 //====================================================================================================================
725 // Get a vector of floating-point values from a child element.
726 /*
727  * Returns a std::map containing a keyed values for child XML_Nodes
728  * of the current node with the name, "float".
729  * In the keyed mapping there will be a list of titles vs. values
730  * for all of the XML nodes.
731  * The float XML_nodes are expected to be in a particular form created
732  * by the function addFloat(). One value per XML_node is expected.
733  *
734  *
735  * Example:
736  *
737  * Code snippet:
738  * @verbatim
739  const XML_Node &State_XMLNode;
740  std::map<std::string,double> v;
741  bool convert = true;
742  getFloats(State_XMLNode, v, convert);
743  @endverbatim
744  *
745  * reads the corresponding XML file:
746  *
747  * @verbatim
748  <state>
749  <float title="a1" units="m3"> 32.4 <\float>
750  <float title="a2" units="cm3"> 1. <\float>
751  <float title="a3"> 100. <\float>
752  <\state>
753  @endverbatim
754  *
755  * Will produce the mapping:
756  *
757  * v["a1"] = 32.4
758  * v["a2"] = 1.0E-6
759  * v["a3"] = 100.
760  *
761  *
762  * @param node Current XML node to get the values from
763  * @param v Output map of the results.
764  * @param convert Turn on conversion to SI units
765  */
766 void getFloats(const Cantera::XML_Node& node, std::map<std::string, double>& v,
767  const bool convert)
768 {
769  std::vector<XML_Node*> f;
770  node.getChildren("float",f);
771  int n = static_cast<int>(f.size());
772  doublereal x, x0, x1, fctr;
773  std::string typ, title, units, vmin, vmax;
774  for (int i = 0; i < n; i++) {
775  const XML_Node& fi = *(f[i]);
776  x = atof(fi().c_str());
777  x0 = Undef;
778  x1 = Undef;
779  typ = fi["type"];
780  title = fi["title"];
781  units = fi["units"];
782  vmin = fi["min"];
783  vmax = fi["max"];
784  if (vmin != "") {
785  x0 = atof(vmin.c_str());
786  if (x < x0 - Tiny) {
787  writelog("\nWarning: value "+fi()+" is below lower limit of "
788  +vmin+".\n");
789  }
790  }
791  if (fi["max"] != "") {
792  x1 = atof(vmax.c_str());
793  if (x > x1 + Tiny) {
794  writelog("\nWarning: value "+fi()+" is above upper limit of "
795  +vmax+".\n");
796  }
797  }
798  fctr = (convert ? toSI(units) : 1.0); // toSI(typ,units);
799  v[title] = fctr*x;
800  }
801 }
802 
803 //====================================================================================================================
804 // Get a floating-point value from a child element.
805 /*
806  * Returns a double value for the child named 'name' of element 'parent'. If
807  * 'type' is supplied and matches a known unit type, unit
808  * conversion to SI will be done if the child element has an attribute
809  * 'units'.
810  *
811  * Note, it's an error for the child element not to exist.
812  *
813  * Example:
814  *
815  * Code snippet:
816  * @verbatim
817  const XML_Node &State_XMLNode;
818  doublereal pres = OneAtm;
819  if (state_XMLNode.hasChild("pressure")) {
820  pres = getFloat(State_XMLNode, "pressure", "toSI");
821  }
822  @endverbatim
823  *
824  * reads the corresponding XML file:
825  * @verbatim
826  <state>
827  <pressure units="Pa"> 101325.0 </pressure>
828  <\state>
829  @endverbatim
830  *
831  * @param parent reference to the XML_Node object of the parent XML element
832  * @param name Name of the XML child element
833  * @param type String type. Currently known types are "toSI" and "actEnergy",
834  * and "" , for no conversion. The default value is ""
835  * which implies that no conversion is allowed.
836  */
837 doublereal getFloat(const Cantera::XML_Node& parent,
838  const std::string& name,
839  const std::string type)
840 {
841  if (!parent.hasChild(name))
842  throw CanteraError("getFloat (called from XML Node \"" +
843  parent.name() + "\"): ",
844  "no child XML element named \"" + name + "\" exists");
845  const XML_Node& node = parent.child(name);
846  return getFloatCurrent(node, type);
847 }
848 
849 //====================================================================================================================
850 // Get a floating-point value from the current XML element
851 /*
852  * Returns a doublereal value from the current element. If
853  * 'type' is supplied and matches a known unit type, unit
854  * conversion to SI will be done if the child element has an attribute 'units'.
855  *
856  * Note, it's an error for the child element not to exist.
857  *
858  * Example:
859  *
860  * Code snippet:
861  * @verbatim
862  const XML_Node &State_XMLNode;
863  doublereal pres = OneAtm;
864  if (state_XMLNode.hasChild("pressure")) {
865  XML_Node *pres_XMLNode = State_XMLNode.getChild("pressure");
866  pres = getFloatCurrent(pres_XMLNode, "toSI");
867  }
868  @endverbatim
869  *
870  * Rreads the corresponding XML file:
871  * @verbatim
872  <state>
873  <pressure units="Pa"> 101325.0 </pressure>
874  <\state>
875  @endverbatim
876  *
877  * @param currXML reference to the current XML_Node object
878  * @param type String type. Currently known types are "toSI" and "actEnergy",
879  * and "" , for no conversion. The default value is "",
880  * which implies that no conversion is allowed.
881  */
882 doublereal getFloatCurrent(const Cantera::XML_Node& node,
883  const std::string type)
884 {
885  doublereal x, x0, x1, fctr = 1.0;
886  string units, vmin, vmax;
887  x = atof(node().c_str());
888  x0 = Undef;
889  x1 = Undef;
890  units = node["units"];
891  vmin = node["min"];
892  vmax = node["max"];
893  if (vmin != "") {
894  x0 = atof(vmin.c_str());
895  if (x < x0 - Tiny) {
896  writelog("\nWarning: value "+node()+" is below lower limit of "
897  +vmin+".\n");
898  }
899  }
900  if (node["max"] != "") {
901  x1 = atof(vmax.c_str());
902  if (x > x1 + Tiny) {
903  writelog("\nWarning: value "+node()+" is above upper limit of "
904  +vmax+".\n");
905  }
906  }
907  // Note, most type's of converters default to toSI() type atm.
908  // This may change and become more specific in the future.
909  if (type == "actEnergy" && units != "") {
910  fctr = actEnergyToSI(units);
911  } else if (type == "toSI" && units != "") {
912  fctr = toSI(units);
913  } else if (type == "temperature" && units != "") {
914  fctr = toSI(units);
915  } else if (type == "density" && units != "") {
916  fctr = toSI(units);
917  } else if (type == "pressure" && units != "") {
918  fctr = toSI(units);
919  } else if (type != "" && units != "") {
920  fctr = toSI(units);
921 #ifdef DEBUG_MODE
922  writelog("\nWarning: conversion toSI() was done on node value " + node.name() +
923  "but wasn't explicity requested. Type was \"" + type + "\"\n");
924 #endif
925  }
926  // Note, below currently produces a lot of output due to transport blocks.
927  // This needs to be addressed.
928 #ifdef DEBUG_MODE_MORE
929  else if (type == "" && units != "") {
930  writelog("\nWarning: XML node " + node.name() +
931  "has a units attribute, \"" + units + "\","
932  "but no conversion was done because the getFloat() command didn't have a type\n");
933  }
934 #endif
935  return fctr*x;
936 }
937 
938 //====================================================================================================================
940  const std::string& name,
941  doublereal& fltRtn,
942  const std::string type)
943 {
944  if (parent.hasChild(name)) {
945  fltRtn= getFloat(parent, name, type);
946  return true;
947  }
948  return false;
949 }
950 //====================================================================================================================
951 // Get an optional floating-point value from a child element.
952 /*
953  * Returns a doublereal value for the child named 'name' of element 'parent'. If
954  * 'type' is supplied and matches a known unit type, unit
955  * conversion to SI will be done if the child element has an attribute
956  * 'units'.
957  *
958  *
959  *
960  * Example:
961  *
962  * Code snippet:
963  * @verbatim
964  const XML_Node &State_XMLNode;
965  doublereal pres = OneAtm;
966  bool exists = getOptionalFloat(State_XMLNode, "pressure", pres, "toSI");
967  @endverbatim
968  *
969  * reads the corresponding XML file:
970  * @verbatim
971  <state>
972  <pressure units="Pa"> 101325.0 </pressure>
973  <\state>
974  @endverbatim
975  *
976  * @param parent reference to the XML_Node object of the parent XML element
977  * @param name Name of the XML child element
978  * @param fltRtn Float Return. It will be overridden if the XML
979  * element exists.
980  * @param type String type. Currently known types are "toSI"
981  * and "actEnergy",
982  * and "" , for no conversion. The default value is "",
983  * which implies that no conversion is allowed.
984  *
985  * @return returns true if the child element named "name" exists
986  */
987 doublereal getFloatDefaultUnits(const Cantera::XML_Node& parent, std::string name,
988  std::string defaultUnits, std::string type)
989 {
990 
991  doublereal fctr = 1.0;
992  if (defaultUnits == "") {
993  throw CanteraError("getFloatDefaultUnits",
994  "need to supply an actual value of defaultUnits");
995  }
996  if (type == "actEnergy") {
997  fctr = actEnergyToSI(defaultUnits);
998  } else if (type == "toSI") {
999  fctr = toSI(defaultUnits);
1000  } else if (defaultUnits == "temperature") {
1001  fctr = toSI(defaultUnits);
1002  } else if (type == "density") {
1003  fctr = toSI(defaultUnits);
1004  } else if (type == "pressure") {
1005  fctr = toSI(defaultUnits);
1006  } else {
1007  throw CanteraError("getFloatDefaultUnits",
1008  "type of units must be supplied and understood");
1009  }
1010  doublereal val = getFloat(parent, name, type);
1011  val /= fctr;
1012  return val;
1013 }
1014 //====================================================================================================================
1015 // Get an optional model name from a named child node.
1016 /*
1017  * Returns the model name attribute for the child named 'nodeName' of element 'parent'.
1018  * Note, it's optional for the child node to exist
1019  *
1020  * Example:
1021  *
1022  * Code snippet:
1023  * @verbatim
1024  std::string modelName = "";
1025  bool exists = getOptionalModel(transportNode, "compositionDependence",
1026  modelName);
1027  @endverbatim
1028  *
1029  * Reads the corresponding XML file:
1030  *
1031  * @verbatim
1032  <transport>
1033  <compositionDependence model="Solvent_Only"/>
1034  </transport>
1035  @endverbatim
1036  *
1037  * On return modelName is set to "Solvent_Only".
1038  *
1039  * @param parent Reference to the XML_Node object of the parent XML element
1040  * @param nodeName Name of the XML child element
1041  * @param modelName On return this contains the contents of the model attribute
1042  *
1043  * @return True if the nodeName XML node exists. False otherwise.
1044  */
1045 bool getOptionalModel(const Cantera::XML_Node& parent, const std::string nodeName,
1046  std::string& modelName)
1047 {
1048  if (parent.hasChild(nodeName)) {
1049  const XML_Node& node = parent.child(nodeName);
1050  modelName = node["model"];
1051  return true;
1052  }
1053  return false;
1054 }
1055 //====================================================================================================================
1056 // Get an integer value from a child element.
1057 /*
1058  * Returns an integer value for the child named 'name' of element 'parent'.
1059  *
1060  * Note, it's an error for the child element not to exist.
1061  *
1062  * Example:
1063  *
1064  * Code snippet:
1065  * @verbatim
1066  const XML_Node &State_XMLNode;
1067  int number = 1;
1068  if (state_XMLNode.hasChild("NumProcs")) {
1069  number = getInteger(State_XMLNode, "numProcs");
1070  }
1071  @endverbatim
1072  *
1073  * reads the corresponding XML file:
1074  * @verbatim
1075  <state>
1076  <numProcs> 10 <\numProcs>
1077  <\state>
1078  @endverbatim
1079  *
1080  * @param parent reference to the XML_Node object of the parent XML element
1081  * @param name Name of the XML child element
1082  */
1083 int getInteger(const Cantera::XML_Node& parent, std::string name)
1084 {
1085  if (!parent.hasChild(name)) {
1086  throw CanteraError("getInteger (called from XML Node \"" +
1087  parent.name() + "\"): ",
1088  "no child XML element named " + name);
1089  }
1090  const XML_Node& node = parent.child(name);
1091  int x, x0, x1;
1092  string units, vmin, vmax;
1093  x = atoi(node().c_str());
1094  x0 = -9999999;
1095  x1 = 9999999;
1096  vmin = node["min"];
1097  vmax = node["max"];
1098  if (vmin != "") {
1099  x0 = atoi(vmin.c_str());
1100  if (x < x0) {
1101  writelog("\nWarning: value "+node()+" is below lower limit of "
1102  +vmin+".\n");
1103  }
1104  }
1105  if (node["max"] != "") {
1106  x1 = atoi(vmax.c_str());
1107  if (x > x1) {
1108  writelog("\nWarning: value "+node()+" is above upper limit of "
1109  +vmax+".\n");
1110  }
1111  }
1112  return x;
1113 }
1114 //====================================================================================================================
1115 // This function reads the current node or a child node of the current node
1116 // with the default name, "floatArray", with a value field
1117 // consisting of a comma separated list of floats
1118 /*
1119  * This function will read either the current XML node or a child node
1120  * to the current XML node, with the
1121  * name "floatArray". It will have a title attribute, and the body
1122  * of the XML node will be filled out with a comma separated list of
1123  * doublereals.
1124  * Get an array of floats from the XML Node. The argument field
1125  * is assumed to consist of an arbitrary number of comma
1126  * separated floats, with an arbitrary amount of white space
1127  * separating each field.
1128  * If the node array has an units attribute field, then
1129  * the units are used to convert the floats, iff convert is true.
1130  *
1131  * Example:
1132  *
1133  * Code snippet:
1134  * @verbatim
1135  const XML_Node &State_XMLNode;
1136  vector_fp v;
1137  bool convert = true;
1138  unitsString = "";
1139  nodeName="floatArray";
1140  getFloatArray(State_XMLNode, v, convert, unitsString, nodeName);
1141  @endverbatim
1142  *
1143  * reads the corresponding XML file:
1144  *
1145  * @verbatim
1146  <state>
1147  <floatArray units="m3"> 32.4, 1, 100. <\floatArray>
1148  <\state>
1149  @endverbatim
1150  *
1151  * Will produce the vector
1152  *
1153  * v[0] = 32.4
1154  * v[1] = 1.0
1155  * v[2] = 100.
1156  *
1157  *
1158  * @param node XML parent node of the floatArray
1159  * @param v Output vector of floats containing the floatArray information.
1160  * @param convert Conversion to SI is carried out if this boolean is
1161  * True. The default is true.
1162  * @param unitsString String name of the type attribute. This is an optional
1163  * parameter. The default is to have an empty string.
1164  * The only string that is recognized is actEnergy.
1165  * Anything else has no effect. This affects what
1166  * units converter is used.
1167  * @param nodeName XML Name of the XML node to read.
1168  * The default value for the node name is floatArray
1169  *
1170  * @return Returns the number of floats read
1171  *
1172  * @note change the v to a std::vector to eliminate a doxygen error. No idea why doxygen needs this.
1173  */
1174 size_t getFloatArray(const Cantera::XML_Node& node, std::vector<doublereal> & v,
1175  const bool convert, const std::string unitsString,
1176  const std::string nodeName)
1177 {
1178  std::string::size_type icom;
1179  string numstr;
1180  doublereal dtmp;
1181  string nn = node.name();
1182  const Cantera::XML_Node* readNode = &node;
1183  if (nn != nodeName) {
1184  vector<Cantera::XML_Node*> ll;
1185  node.getChildren(nodeName, ll);
1186  if (ll.size() == 0) {
1187  throw CanteraError("getFloatArray",
1188  "wrong xml element type/name: was expecting "
1189  + nodeName + "but accessed " + node.name());
1190  } else {
1191  readNode = ll[0];
1192  ll.clear();
1193  readNode->getChildren("floatArray", ll);
1194  if (ll.size() > 0) {
1195  readNode = ll[0];
1196  }
1197  }
1198  }
1199 
1200  v.clear();
1201  doublereal vmin = Undef, vmax = Undef;
1202 
1203  doublereal funit = 1.0;
1204  /*
1205  * Get the attributes field, units, from the XML node
1206  */
1207  std::string units = (*readNode)["units"];
1208  if (units != "" && convert) {
1209  if (unitsString == "actEnergy" && units != "") {
1210  funit = actEnergyToSI(units);
1211  } else if (unitsString != "" && units != "") {
1212  funit = toSI(units);
1213  }
1214  }
1215 
1216  if ((*readNode)["min"] != "") {
1217  vmin = atofCheck((*readNode)["min"].c_str());
1218  }
1219  if ((*readNode)["max"] != "") {
1220  vmax = atofCheck((*readNode)["max"].c_str());
1221  }
1222 
1223  doublereal vv;
1224  std::string val = readNode->value();
1225  while (1 > 0) {
1226  icom = val.find(',');
1227  if (icom != string::npos) {
1228  numstr = val.substr(0,icom);
1229  val = val.substr(icom+1,val.size());
1230  dtmp = atofCheck(numstr.c_str());
1231  v.push_back(dtmp);
1232  } else {
1233  /*
1234  * This little bit of code is to allow for the
1235  * possibility of a comma being the last
1236  * item in the value text. This was allowed in
1237  * previous versions of Cantera, even though it
1238  * would appear to be odd. So, we keep the
1239  * possibilty in for backwards compatibility.
1240  */
1241  if (!val.empty()) {
1242  dtmp = atofCheck(val.c_str());
1243  v.push_back(dtmp);
1244  }
1245  break;
1246  }
1247  vv = v.back();
1248  if (vmin != Undef && vv < vmin - Tiny) {
1249  writelog("\nWarning: value "+fp2str(vv)+
1250  " is below lower limit of " +fp2str(vmin)+".\n");
1251  }
1252  if (vmax != Undef && vv > vmax + Tiny) {
1253  writelog("\nWarning: value "+fp2str(vv)+
1254  " is above upper limit of " +fp2str(vmin)+".\n");
1255  }
1256  }
1257  for (size_t n = 0; n < v.size(); n++) {
1258  v[n] *= funit;
1259  }
1260  return v.size();
1261 }
1262 //====================================================================================================================
1263 size_t getNamedFloatArray(const Cantera::XML_Node& parentNode, const std::string& nodeName, std::vector<doublereal> & v,
1264  const bool convert, const std::string unitsString)
1265 {
1266  std::string::size_type icom;
1267  std::string numstr;
1268  doublereal dtmp;
1269  std::string nn = parentNode.name();
1270  v.clear();
1271  const Cantera::XML_Node* readNode = parentNode.findByName(nodeName);
1272  if (!readNode) {
1273  return 0;
1274  }
1275 
1276  doublereal vmin = Undef;
1277  doublereal vmax = Undef;
1278  doublereal funit = 1.0;
1279  /*
1280  * Get the attributes field, units, from the XML node
1281  */
1282  std::string units = (*readNode)["units"];
1283  if (units != "" && convert) {
1284  if (unitsString == "actEnergy" && units != "") {
1285  funit = actEnergyToSI(units);
1286  } else if (unitsString != "" && units != "") {
1287  funit = toSI(units);
1288  }
1289  }
1290 
1291  if ((*readNode)["min"] != "") {
1292  vmin = atofCheck((*readNode)["min"].c_str());
1293  }
1294  if ((*readNode)["max"] != "") {
1295  vmax = atofCheck((*readNode)["max"].c_str());
1296  }
1297 
1298  size_t expectedSize = 0;
1299  nn = (*readNode)["size"];
1300  expectedSize = atoi(nn.c_str());
1301 
1302  nn = (*readNode)["vtype"];
1303  if (nn != "floatArray") {
1304  throw CanteraError("getNamedFloatArray",
1305  "node named " + nodeName + "didn't have correct vtype");
1306  }
1307 
1308  doublereal vv;
1309  std::string val = readNode->value();
1310  while (1 > 0) {
1311  icom = val.find(',');
1312  if (icom != string::npos) {
1313  numstr = val.substr(0,icom);
1314  val = val.substr(icom+1,val.size());
1315  dtmp = atofCheck(numstr.c_str());
1316  v.push_back(dtmp);
1317  } else {
1318  /*
1319  * This little bit of code is to allow for the
1320  * possibility of a comma being the last
1321  * item in the value text. This was allowed in
1322  * previous versions of Cantera, even though it
1323  * would appear to be odd. So, we keep the
1324  * possibilty in for backwards compatibility.
1325  */
1326  size_t nlen = strlen(val.c_str());
1327  if (nlen > 0) {
1328  dtmp = atofCheck(val.c_str());
1329  v.push_back(dtmp);
1330  }
1331  break;
1332  }
1333  vv = v.back();
1334  if (vmin != Undef && vv < vmin - Tiny) {
1335  writelog("\nWarning: value "+fp2str(vv)+
1336  " is below lower limit of " +fp2str(vmin)+".\n");
1337  }
1338  if (vmax != Undef && vv > vmax + Tiny) {
1339  writelog("\nWarning: value "+fp2str(vv)+
1340  " is above upper limit of " +fp2str(vmin)+".\n");
1341  }
1342  }
1343  size_t nv = v.size();
1344  for (size_t n = 0; n < nv; n++) {
1345  v[n] *= funit;
1346  }
1347  if (nv != expectedSize) {
1348  throw CanteraError("getNamedFloatArray",
1349  "node named " + nodeName + "didn't have correct number of floats"
1350  + int2str(expectedSize) + " vs " + int2str(nv));
1351  }
1352  return nv;
1353 }
1354 //====================================================================================================================
1355 // This routine is used to interpret the value portions of XML
1356 // elements that contain colon separated pairs.
1357 /*
1358  * These are used, for example, in describing the element
1359  * composition of species.
1360  *
1361  * <atomArray> H:4 C:1 <atomArray>
1362  *
1363  * The string is first separated into a string vector according
1364  * to the location of white space. Then each string is again
1365  * separated into two parts according to the location of a
1366  * colon in the string. The first part of the string is
1367  * used as the key, while the second part of the string is
1368  * used as the value, in the return map.
1369  * It is an error to not find a colon in each string pair.
1370  *
1371  * @param node Current node
1372  * @param m Output Map containing the pairs of values found
1373  * in the XML Node
1374  */
1375 void getMap(const Cantera::XML_Node& node, std::map<std::string, std::string>& m)
1376 {
1377  std::vector<std::string> v;
1378  getStringArray(node, v);
1379  std::string key, val;
1380  int n = static_cast<int>(v.size());
1381  string::size_type icolon;
1382  for (int i = 0; i < n; i++) {
1383  icolon = v[i].find(":");
1384  if (icolon == string::npos) {
1385  throw CanteraError("getMap","missing colon in map entry ("
1386  +v[i]+")");
1387  }
1388  key = v[i].substr(0,icolon);
1389  val = v[i].substr(icolon+1, v[i].size());
1390  m[key] = val;
1391  }
1392 }
1393 //====================================================================================================================
1394 // This function interprets the value portion of an XML element
1395 // as a series of "Pairs" separated by white space.
1396 /*
1397  * Each pair consists of nonwhite-space characters.
1398  * The first ":" found in the pair string is used to separate
1399  * the string into two parts. The first part is called the "key"
1400  * The second part is called the "val".
1401  * String vectors of key[i] and val[i] are returned in the
1402  * argument list.
1403  * Warning: No spaces are allowed in each pair. Quotes get included as part
1404  * of the string.
1405  * Example: @verbatim
1406  <xmlNode>
1407  red:112 blue:34
1408  green:banana
1409  </xmlNode>
1410  @endverbatim
1411  *
1412  * Returns:
1413  * key val
1414  * 0: "red" "112"
1415  * 1: "blue" "34"
1416  * 2: "green" "banana"
1417  *
1418  *
1419  * @param node XML Node
1420  * @param key Vector of keys for each entry
1421  * @param val Vector of values for each entry
1422  *
1423  * @return Returns the number of pairs found
1424  */
1425 int getPairs(const Cantera::XML_Node& node, std::vector<std::string>& key,
1426  std::vector<std::string>& val)
1427 {
1428  vector<string> v;
1429  getStringArray(node, v);
1430  int n = static_cast<int>(v.size());
1431  string::size_type icolon;
1432  for (int i = 0; i < n; i++) {
1433  icolon = v[i].find(":");
1434  if (icolon == string::npos) {
1435  throw CanteraError("getPairs","Missing a colon in the Pair entry ("
1436  +v[i]+")");
1437  }
1438  key.push_back(v[i].substr(0,icolon));
1439  val.push_back(v[i].substr(icolon+1, v[i].size()));
1440  //cout << "getPairs: " << key.back() << " " << val.back() << endl;
1441  }
1442  return n;
1443 }
1444 //====================================================================================================================
1445 // This function interprets the value portion of an XML element
1446 // as a series of "Matrix ids and entries" separated by white space.
1447 /*
1448  * Each pair consists of nonwhite-space characters.
1449  * The first two ":" found in the pair string is used to separate
1450  * the string into three parts. The first part is called the first
1451  * key. The second part is the second key. Both parts must match
1452  * an entry in the keyString1 and keyString2, respectively,
1453  * in order to provide a location to
1454  * place the object in the matrix.
1455  * The third part is called the value. It is expected to be
1456  * a double. It is translated into a double and placed into the
1457  * correct location in the matrix.
1458  *
1459  * Warning: No spaces are allowed in each triplet. Quotes are part
1460  * of the string.
1461  * Example
1462  * keyString = red, blue, black, green
1463  * <xmlNode>
1464  * red:green:112
1465  * blue:black:3.3E-23
1466  *
1467  * </xmlNode>
1468  *
1469  * Returns:
1470  * retnValues(0, 3) = 112
1471  * retnValues(1, 2) = 3.3E-23
1472  *
1473  *
1474  * @param node XML Node containing the information for the matrix
1475  * @param keyStringRow Key string for the row
1476  * @param keyStringCol Key string for the column entries
1477  * @param returnValues Return Matrix.
1478  * @param convert If this is true, and if the node has a units
1479  * attribute, then conversion to si units is carried
1480  * out. Default is true.
1481  * @param matrixSymmetric If true entries are made so that the matrix
1482  * is always symmetric. Default is false.
1483  */
1485  const std::vector<std::string>& keyStringRow,
1486  const std::vector<std::string>& keyStringCol,
1487  Cantera::Array2D& retnValues, const bool convert,
1488  const bool matrixSymmetric)
1489 {
1490  size_t szKey1 = keyStringRow.size();
1491  size_t szKey2 = keyStringCol.size();
1492  size_t nrow = retnValues.nRows();
1493  size_t ncol = retnValues.nColumns();
1494  if (szKey1 > nrow) {
1495  throw CanteraError("getMatrixValues",
1496  "size of key1 greater than numrows");
1497  }
1498  if (szKey2 > ncol) {
1499  throw CanteraError("getMatrixValues",
1500  "size of key2 greater than num cols");
1501  }
1502  if (matrixSymmetric) {
1503  if (nrow != ncol) {
1504  throw CanteraError("getMatrixValues",
1505  "nrow != ncol for a symmetric matrix");
1506  }
1507  }
1508 
1509  /*
1510  * Get the attributes field, units, from the XML node
1511  * and determine the conversion factor, funit.
1512  */
1513  doublereal funit = 1.0;
1514  string units = node["units"];
1515  if (units != "" && convert) {
1516  funit = toSI(units);
1517  }
1518 
1519  string key1;
1520  string key2;
1521  string rmm;
1522  string val;
1523  vector<string> v;
1524  getStringArray(node, v);
1525  string::size_type icolon;
1526  for (size_t i = 0; i < v.size(); i++) {
1527  icolon = v[i].find(":");
1528  if (icolon == string::npos) {
1529  throw CanteraError("getMatrixValues","Missing two colons ("
1530  +v[i]+")");
1531  }
1532  key1 = v[i].substr(0,icolon);
1533  rmm = v[i].substr(icolon+1, v[i].size());
1534  icolon = rmm.find(":");
1535  if (icolon == string::npos) {
1536  throw CanteraError("getMatrixValues","Missing one colon ("
1537  +v[i]+")");
1538  }
1539  key2 = rmm.substr(0,icolon);
1540  val = rmm.substr(icolon+1, rmm.size());
1541 
1542  size_t icol = npos;
1543  size_t irow = npos;
1544  for (size_t j = 0; j < szKey1; j++) {
1545  if (key1 == keyStringRow[j]) {
1546  irow = j;
1547  break;
1548  }
1549  }
1550  if (irow == npos) {
1551  throw CanteraError("getMatrixValues","Row not matched by string: "
1552  + key1);
1553  }
1554  for (size_t j = 0; j < szKey2; j++) {
1555  if (key2 == keyStringCol[j]) {
1556  icol = j;
1557  break;
1558  }
1559  }
1560  if (icol == npos) {
1561  throw CanteraError("getMatrixValues","Col not matched by string: "
1562  + key2);
1563  }
1564  double dval = atofCheck(val.c_str());
1565  dval *= funit;
1566  /*
1567  * Finally, insert the value;
1568  */
1569  retnValues(irow, icol) = dval;
1570  if (matrixSymmetric) {
1571  retnValues(icol, irow) = dval;
1572  }
1573  }
1574 }
1575 //====================================================================================================================
1576 // This function interprets the value portion of an XML element
1577 // as a string. It then separates the string up into tokens
1578 // according to the location of white space.
1579 /*
1580  * The separate tokens are returned in the string vector
1581  *
1582  * @param node Node to get the value from
1583  * @param v Output vector containing the string tokens
1584  */
1585 void getStringArray(const Cantera::XML_Node& node, std::vector<std::string>& v)
1586 {
1587  std::string val = node.value();
1588  tokenizeString(val, v);
1589 }
1590 //====================================================================================================================
1591 // This function reads a child node with the default name, "floatArray", with a value
1592 // consisting of a comma separated list of floats
1593 /*
1594  * This function will read a child node to the current XML node, with the
1595  * name "floatArray". It will have a title attribute, and the body
1596  * of the XML node will be filled out with a comma separated list of
1597  * doublereals.
1598  * Get an array of floats from the XML Node. The argument field
1599  * is assumed to consist of an arbitrary number of comma
1600  * separated floats, with an arbitrary amount of white space
1601  * separating each field.
1602  * If the node array has an units attribute field, then
1603  * the units are used to convert the floats, iff convert is true.
1604  * This function is a wrapper around the function getFloatArray().
1605  *
1606  * Example:
1607  *
1608  * Code snippet:
1609  * @verbatim
1610  const XML_Node &State_XMLNode;
1611  vector_fp v;
1612  bool convert = true;
1613  unitsString = "";
1614  nodeName="floatArray";
1615  getFloatArray(State_XMLNode, v, convert, unitsString, nodeName);
1616  @endverbatim
1617  *
1618  * reads the corresponding XML file:
1619  *
1620  * @verbatim
1621  <state>
1622  <floatArray units="m3"> 32.4, 1, 100. <\floatArray>
1623  <\state>
1624  @endverbatim
1625  *
1626  * Will produce the vector
1627  *
1628  * v[0] = 32.4
1629  * v[1] = 1.0
1630  * v[2] = 100.
1631  *
1632  *
1633  * @param node XML parent node of the floatArray
1634  * @param typeString Returns the type attribute of the current node.
1635  * @param xmin Returns the minimum value attribute of the
1636  * current node.
1637  * @param xmax Returns the maximum value attribute of the
1638  * current node.
1639  * @param coeffs Output vector of floats containing the floatArray information.
1640  */
1641 void getFunction(const Cantera::XML_Node& node, std::string& type, doublereal& xmin,
1642  doublereal& xmax, std::vector<doublereal> & coeffs)
1643 {
1644  const XML_Node& c = node.child("floatArray");
1645  coeffs.clear();
1646  getFloatArray(c,coeffs);
1647  xmin = Cantera::Undef;
1648  xmin = Cantera::Undef;
1649  if (node["min"] != "") {
1650  xmin = fpValue(node["min"]);
1651  }
1652  if (node["max"] != "") {
1653  xmax = fpValue(node["max"]);
1654  }
1655  type = node["type"];
1656 }
1657 //====================================================================================================================
1658 }