00001 /** 00002 * @file ctml.cpp 00003 * Definitions for functions to read and write CTML. 00004 * 00005 */ 00006 00007 /* 00008 * $Revision: 374 $ 00009 * $Date: 2010-01-12 16:27:32 -0500 (Tue, 12 Jan 2010) $ 00010 */ 00011 00012 // Copyright 2002 California Institute of Technology 00013 00014 00015 // turn off warnings under Windows 00016 #ifdef WIN32 00017 #pragma warning(disable:4786) 00018 #pragma warning(disable:4503) 00019 #endif 00020 00021 #include "ctml.h" 00022 00023 //@{ 00024 #define CTML_VERSION_1_4_1 00025 //@} 00026 00027 #include "global.h" 00028 #include "stringUtils.h" 00029 00030 #include <cctype> 00031 #include <cstring> 00032 #include <cstdlib> 00033 00034 00035 using namespace std; 00036 using namespace Cantera; 00037 00038 namespace ctml { 00039 00040 static doublereal fpValue(std::string val) { 00041 return atof(stripws(val).c_str()); 00042 } 00043 00044 // This function adds a child node with the name, "bool", with a value 00045 // consisting of a single bool 00046 /* 00047 * This function will add a child node to the current XML node, with the 00048 * name "bool". It will have a title attribute, and the body 00049 * of the XML node will be filled out with a single bool 00050 * 00051 * Example: 00052 * 00053 * Code snipet: 00054 * @verbatum 00055 const XML_Node &node; 00056 std::string titleString = "doSpecialOp"; 00057 bool value = true; 00058 addBool(node, titleString, value); 00059 @endverbatum 00060 * 00061 * Creates the following the snippet in the XML file: 00062 * @verbatum 00063 <parentNode> 00064 <bool title="doSpecialOp" type="optional"> 00065 true 00066 <\integer> 00067 <\parentNode> 00068 @endverbatum 00069 * 00070 * @param node reference to the XML_Node object of the parent XML element 00071 * @param titleString String name of the title attribute 00072 * @param value Value - single bool 00073 * 00074 * @todo I don't think this is used. Figure out what is used for writing bools, 00075 * and codify that. 00076 */ 00077 void addBool(Cantera::XML_Node& node, const std::string &title, const bool val) { 00078 std::string v = (val ? "true" : "false"); 00079 XML_Node& f = node.addChild("bool", v); 00080 f.addAttribute("title", title); 00081 } 00082 00083 // This function adds a child node with the name, "integer", with a value 00084 // consisting of a single integer 00085 /* 00086 * This function will add a child node to the current XML node, with the 00087 * name "integer". It will have a title attribute, and the body 00088 * of the XML node will be filled out with a single integer 00089 * 00090 * Example: 00091 * 00092 * Code snipet: 00093 * @verbatum 00094 const XML_Node &node; 00095 std::string titleString = "maxIterations"; 00096 int value = 1000; 00097 std::string typeString = "optional"; 00098 std::string units = ""; 00099 addInteger(node, titleString, value, typeString, units); 00100 @endverbatum 00101 * 00102 * Creates the following the snippet in the XML file: 00103 * @verbatum 00104 <parentNode> 00105 <integer title="maxIterations" type="optional"> 00106 100 00107 <\integer> 00108 <\parentNode> 00109 @endverbatum 00110 * 00111 * @param node reference to the XML_Node object of the parent XML element 00112 * @param titleString String name of the title attribute 00113 * @param value Value - single integer 00114 * @param unitsString String name of the Units attribute. The default is to 00115 * have an empty string. 00116 * @param typeString String type. This is an optional parameter. The default 00117 * is to have an empty string. 00118 */ 00119 void addInteger(Cantera::XML_Node& node, const std::string &title, const int val, 00120 const std::string units, const std::string type) { 00121 XML_Node& f = node.addChild("integer",val); 00122 f.addAttribute("title",title); 00123 if (type != "") f.addAttribute("type",type); 00124 if (units != "") f.addAttribute("units",units); 00125 } 00126 00127 // This function adds a child node with the name, "intArray", with a value 00128 // consisting of a comma separated list of integers 00129 /* 00130 * This function will add a child node to the current XML node, with the 00131 * name "intArray". It will have a title attribute, and the body 00132 * of the XML node will be filled out with a comma separated list of 00133 * integers 00134 * 00135 * Example: 00136 * 00137 * @verbatum 00138 const XML_Node &node; 00139 std::string titleString = "additionalCases"; 00140 int n = 3; 00141 int cases[3] = [3, 6, 10]; 00142 std::string typeString = "optional"; 00143 std::string units = ""; 00144 addIntegerArray(node, titleString, n, &cases[0], typeString, units); 00145 @endverbatum 00146 * 00147 * Creates the following the snippet in the XML file: 00148 * @verbatum 00149 <parentNode> 00150 <intArray title="additionalCases" type="optional"> 00151 3, 6, 10 00152 <\intArray> 00153 <\parentNode> 00154 @endverbatum 00155 * 00156 * 00157 * @param node reference to the XML_Node object of the parent XML element 00158 * @param titleString String name of the title attribute 00159 * @param n Length of the integer vector. 00160 * @param values Pointer to a vector of integers 00161 * @param unitsString String name of the Units attribute. This is an optional 00162 * parameter. The default is to 00163 * have an empty string. 00164 * @param typeString String type. This is an optional parameter. The default 00165 * is to have an empty string. 00166 * @param minval Minimum allowed value of the int. This is an optional 00167 * parameter. The default is the 00168 * special double, Cantera::Undef, which means to ignore the 00169 * entry. 00170 * @param maxval Maximum allowed value of the int. This is an optional 00171 * parameter. The default is the 00172 * special double, Cantera::Undef, which means to ignore the 00173 * entry. 00174 * 00175 * @todo I don't think this is used. Figure out what is used for writing integers, 00176 * and codify that. unitsString shouldn't be here, since it's an int. 00177 * typeString should be codified as to its usage. 00178 */ 00179 void addIntegerArray(Cantera::XML_Node& node, const std::string &title, const int n, 00180 const int* const vals, const std::string units, const std::string type, 00181 const doublereal minval, const doublereal maxval) { 00182 std::string fmt = "%8d"; 00183 int i; 00184 std::string v = ""; 00185 for (i = 0; i < n; i++) { 00186 v += int2str(vals[i],fmt); 00187 if (i == n-1) v += "\n"; 00188 else if (i > 0 && (i+1) % 3 == 0) v += ",\n"; 00189 else v += ", "; 00190 } 00191 XML_Node& f = node.addChild("intArray",v); 00192 f.addAttribute("title",title); 00193 if (type != "") f.addAttribute("type",type); 00194 f.addAttribute("size",n); 00195 if (units != "") f.addAttribute("units",units); 00196 if (minval != Undef) f.addAttribute("min",minval); 00197 if (maxval != Undef) f.addAttribute("max",maxval); 00198 } 00199 00200 // This function adds a child node with the name, "float", with a value 00201 // consisting of a single floating point number 00202 /* 00203 * This function will add a child node to the current XML node, with the 00204 * name "float". It will have a title attribute, and the body 00205 * of the XML node will be filled out with a single float 00206 * 00207 * Example: 00208 * 00209 * Code snipet: 00210 * @verbatum 00211 const XML_Node &node; 00212 std::string titleString = "activationEnergy"; 00213 double value = 50.3; 00214 doublereal maxval = 1.0E3; 00215 doublereal minval = 0.0; 00216 std::string typeString = "optional"; 00217 std::string unitsString = "kcal/gmol"; 00218 addFloat(node, titleString, value, unitsString, typeString, minval, maxval); 00219 @endverbatum 00220 * 00221 * Creates the following the snippet in the XML file: 00222 * @verbatum 00223 <parentNode> 00224 <float title="activationEnergy" type="optional" units="kcal/gmol" min="0.0" max="1.0E3"> 00225 50.3 00226 <\float> 00227 <\parentNode> 00228 @endverbatum 00229 * 00230 * @param node reference to the XML_Node object of the parent XML element 00231 * @param titleString String name of the title attribute 00232 * @param value Value - single integer 00233 * @param unitsString String name of the Units attribute. The default is to 00234 * have an empty string. 00235 * @param typeString String type. This is an optional parameter. The default 00236 * is to have an empty string. 00237 * 00238 * @todo I don't think this is used. Figure out what is used for writing floats, 00239 * and codify that. minval and maxval should be codified. 00240 * typeString should be codified as to its usage. 00241 */ 00242 void addFloat(Cantera::XML_Node& node, const std::string &title, 00243 const doublereal val, const std::string units, 00244 const std::string type, const doublereal minval, 00245 const doublereal maxval) { 00246 string fmt = "%17.9E"; 00247 #ifdef CTML_VERSION_1_4 00248 XML_Node& f = node.addChild("float",val,fmt); 00249 f.addAttribute("title",title); 00250 #else 00251 XML_Node& f = node.addChild(title,val,fmt); 00252 #endif 00253 if (type != "") f.addAttribute("type",type); 00254 if (units != "") f.addAttribute("units",units); 00255 if (minval != Undef) f.addAttribute("min",minval); 00256 if (maxval != Undef) f.addAttribute("max",maxval); 00257 } 00258 00259 // This function adds a child node with the name, "floatArray", with a value 00260 // consisting of a comma separated list of floats 00261 /* 00262 * This function will add a child node to the current XML node, with the 00263 * name "floatArray". It will have a title attribute, and the body 00264 * of the XML node will be filled out with a comma separated list of 00265 * integers 00266 * 00267 * Example: 00268 * 00269 * Code snipet: 00270 * @verbatum 00271 const XML_Node &node; 00272 std::string titleString = "additionalTemperatures"; 00273 int n = 3; 00274 int Tcases[3] = [273.15, 298.15, 373.15]; 00275 std::string typeString = "optional"; 00276 std::string units = "Kelvin"; 00277 addFloatArray(node, titleString, n, &cases[0], typeString, units); 00278 @endverbatum 00279 * 00280 * Creates the following the snippet in the XML file: 00281 * @verbatum 00282 <parentNode> 00283 <floatArray title="additionalTemperatures" type="optional" units="Kelvin"> 00284 273.15, 298.15, 373.15 00285 <\floatArray> 00286 <\parentNode> 00287 @endverbatum 00288 * 00289 * @param node reference to the XML_Node object of the parent XML element 00290 * @param titleString String name of the title attribute 00291 * @param n Length of the doubles vector. 00292 * @param values Pointer to a vector of doubles 00293 * @param unitsString String name of the Units attribute. This is an optional 00294 * parameter. The default is to 00295 * have an empty string. 00296 * @param typeString String type. This is an optional parameter. The default 00297 * is to have an empty string. 00298 * @param minval Minimum allowed value of the int. This is an optional 00299 * parameter. The default is the 00300 * special double, Cantera::Undef, which means to ignore the 00301 * entry. 00302 * @param maxval Maximum allowed value of the int. This is an optional 00303 * parameter. The default is the 00304 * special double, Cantera::Undef, which means to ignore the 00305 * entry. 00306 * 00307 * @todo I don't think this is used. Figure out what is used for writing integers, 00308 * and codify that. unitsString shouldn't be here, since it's an int. 00309 * typeString should be codified as to its usage. 00310 */ 00311 void addFloatArray(Cantera::XML_Node& node, const std::string &title, const int n, 00312 const doublereal* const vals, const std::string units, 00313 const std::string type, 00314 const doublereal minval, const doublereal maxval) { 00315 std::string fmt = "%17.9E"; 00316 int i; 00317 std::string v = ""; 00318 for (i = 0; i < n; i++) { 00319 v += fp2str(vals[i],fmt); 00320 if (i == n-1) v += "\n"; 00321 else if (i > 0 && (i+1) % 3 == 0) v += ",\n"; 00322 else v += ", "; 00323 } 00324 XML_Node& f = node.addChild("floatArray",v); 00325 f.addAttribute("title",title); 00326 if (type != "") f.addAttribute("type",type); 00327 f.addAttribute("size",n); 00328 if (units != "") f.addAttribute("units",units); 00329 if (minval != Undef) f.addAttribute("min",minval); 00330 if (maxval != Undef) f.addAttribute("max",maxval); 00331 } 00332 00333 // This function adds a child node with the name string with a string value 00334 // to the current node 00335 /* 00336 * This function will add a child node to the current XML node, with the 00337 * name "string". It will have a title attribute, and the body 00338 * of the XML node will be filled out with the valueString argument verbatim. 00339 * 00340 * Example: 00341 * 00342 * Code snipet: 00343 * @verbatum 00344 const XML_Node &node; 00345 addString(XML_Node& node, std::string titleString, std::string valueString, 00346 std::string typeString); 00347 @endverbatum 00348 * 00349 * Creates the following the snippet in the XML file: 00350 * @verbatum 00351 <string title="titleString" type="typeString"> 00352 valueString 00353 <\string> 00354 @endverbatum 00355 * 00356 * @param node reference to the XML_Node object of the parent XML element 00357 * @param valueString Value string to be used in the new XML node. 00358 * @param titleString String name of the title attribute 00359 * @param typeString String type. This is an optional parameter. 00360 */ 00361 void addString(Cantera::XML_Node& node, const std::string &titleString, 00362 const std::string &valueString, 00363 const std::string typeString) { 00364 XML_Node& f = node.addChild("string", valueString); 00365 f.addAttribute("title", titleString); 00366 if (typeString != "") f.addAttribute("type", typeString); 00367 } 00368 00369 XML_Node* getByTitle(const Cantera::XML_Node& node, const std::string &title) { 00370 XML_Node* s = node.findByAttr("title", title); 00371 if (!s) return 0; 00372 if (s->parent() == &node) { 00373 return s; 00374 } 00375 return 0; 00376 } 00377 00378 // This function reads a child node with the name string and returns 00379 // its xml value as the return string 00380 /* 00381 * If the child XML_node named "name" doesn't exist, the empty string is returned. 00382 * 00383 * Code snipet: 00384 * @verbatum 00385 const XML_Node &parent; 00386 string name = "vacency_species"; 00387 string valueString = getChildValue(parent, name 00388 std::string typeString); 00389 @endverbatum 00390 * 00391 * returns valueString = "O(V)" 00392 * 00393 * from the following the snippet in the XML file: 00394 * 00395 * @verbatum 00396 <vacencySpecies> 00397 O(V) 00398 <\vancencySpecies> 00399 @endverbatum 00400 * 00401 * @param parent parent reference to the XML_Node object of the parent XML element 00402 * @param name Name of the childe XML_Node to read the value from. 00403 * 00404 * @return String value of the child XML_Node 00405 */ 00406 std::string getChildValue(const Cantera::XML_Node& parent, const std::string &nameString) { 00407 if (!parent.hasChild(nameString)) return ""; 00408 return parent(nameString); 00409 } 00410 00411 // This function reads a child node with the name, "string", with a specific 00412 // title attribute named "titleString" 00413 /* 00414 * This function will read a child node to the current XML node, with the 00415 * name "string". It must have a title attribute, named titleString, and the body 00416 * of the XML node will be read into the valueString output argument. 00417 * 00418 * Example: 00419 * 00420 * Code snipet: 00421 * @verbatum 00422 const XML_Node &node; 00423 getString(XML_Node& node, std::string titleString, std::string valueString, 00424 std::string typeString); 00425 @endverbatum 00426 * 00427 * Reads the following the snippet in the XML file: 00428 * @verbatum 00429 <string title="titleString" type="typeString"> 00430 valueString 00431 <\string> 00432 @endverbatum 00433 * 00434 * @param node reference to the XML_Node object of the parent XML element 00435 * @param titleString String name of the title attribute of the child node 00436 * @param valueString Value string that is found in the child node. output variable 00437 * @param typeString String type. This is an optional output variable 00438 */ 00439 void getString(const Cantera::XML_Node& node, const std::string &titleString, std::string& valueString, 00440 std::string& typeString) { 00441 valueString = ""; 00442 typeString = ""; 00443 XML_Node* s = getByTitle(node, titleString); 00444 if (s) 00445 if (s->name() == "string") { 00446 valueString = (*s).value(); 00447 typeString = (*s)["type"]; 00448 return; 00449 } 00450 } 00451 00452 00453 // Get a vector of integer values from a child element. 00454 /* 00455 * Returns a std::map containing a keyed values for child XML_Nodes 00456 * of the current node with the name, "integer". 00457 * In the keyed mapping there will be a list of titles vs. values 00458 * for all of the XML nodes. 00459 * The integer XML_nodes are expected to be in a particular form created 00460 * by the function addInteger(). One value per XML_node is expected. 00461 * 00462 * 00463 * Example: 00464 * 00465 * Code snipet: 00466 * @verbatum 00467 const XML_Node &State_XMLNode; 00468 std::map<std::string, integer> v; 00469 getinteger(State_XMLNode, v); 00470 @endverbatum 00471 * 00472 * reads the corresponding XML file: 00473 * 00474 * @verbatum 00475 <state> 00476 <integer title="i1"> 1 <\integer> 00477 <integer title="i2"> 2 <\integer> 00478 <integer title="i3"> 3 <\integer> 00479 <\state> 00480 @endverbatum 00481 * 00482 * Will produce the mapping: 00483 * 00484 * v["i1"] = 1 00485 * v["i2"] = 2 00486 * v["i3"] = 3 00487 * 00488 * 00489 * @param node Current XML node to get the values from 00490 * @param v Output map of the results. 00491 */ 00492 void getIntegers(const Cantera::XML_Node& node, 00493 std::map<std::string, int>& v) { 00494 std::vector<XML_Node*> f; 00495 node.getChildren("integer",f); 00496 int n = static_cast<int>(f.size()); 00497 integer x, x0, x1; 00498 std::string typ, title, vmin, vmax; 00499 for (int i = 0; i < n; i++) { 00500 const XML_Node& fi = *(f[i]); 00501 x = atoi(fi().c_str()); 00502 title = fi["title"]; 00503 vmin = fi["min"]; 00504 vmax = fi["max"]; 00505 if (vmin != "") 00506 x0 = atoi(vmin.c_str()); 00507 if (fi["max"] != "") 00508 x1 = atoi(vmax.c_str()); 00509 v[title] = x; 00510 } 00511 } 00512 00513 00514 // Get a vector of floating-point values from a child element. 00515 /* 00516 * Returns a std::map containing a keyed values for child XML_Nodes 00517 * of the current node with the name, "float". 00518 * In the keyed mapping there will be a list of titles vs. values 00519 * for all of the XML nodes. 00520 * The float XML_nodes are expected to be in a particular form created 00521 * by the function addFloat(). One value per XML_node is expected. 00522 * 00523 * 00524 * Example: 00525 * 00526 * Code snipet: 00527 * @verbatum 00528 const XML_Node &State_XMLNode; 00529 std::map<std::string,double> v; 00530 bool convert = true; 00531 getFloats(State_XMLNode, v, convert); 00532 @endverbatum 00533 * 00534 * reads the corresponding XML file: 00535 * 00536 * @verbatum 00537 <state> 00538 <float title="a1" units="m3"> 32.4 <\float> 00539 <float title="a2" units="cm3"> 1. <\float> 00540 <float title="a3"> 100. <\float> 00541 <\state> 00542 @endverbatum 00543 * 00544 * Will produce the mapping: 00545 * 00546 * v["a1"] = 32.4 00547 * v["a2"] = 1.0E-6 00548 * v["a3"] = 100. 00549 * 00550 * 00551 * @param node Current XML node to get the values from 00552 * @param v Output map of the results. 00553 * @param convert Turn on conversion to SI units 00554 */ 00555 void getFloats(const Cantera::XML_Node& node, std::map<std::string, double>& v, 00556 const bool convert) { 00557 std::vector<XML_Node*> f; 00558 node.getChildren("float",f); 00559 int n = static_cast<int>(f.size()); 00560 doublereal x, x0, x1, fctr; 00561 std::string typ, title, units, vmin, vmax; 00562 for (int i = 0; i < n; i++) { 00563 const XML_Node& fi = *(f[i]); 00564 x = atof(fi().c_str()); 00565 x0 = Undef; 00566 x1 = Undef; 00567 typ = fi["type"]; 00568 title = fi["title"]; 00569 units = fi["units"]; 00570 vmin = fi["min"]; 00571 vmax = fi["max"]; 00572 if (vmin != "") { 00573 x0 = atof(vmin.c_str()); 00574 if (x < x0 - Tiny) { 00575 writelog("\nWarning: value "+fi()+" is below lower limit of " 00576 +vmin+".\n"); 00577 } 00578 } 00579 if (fi["max"] != "") { 00580 x1 = atof(vmax.c_str()); 00581 if (x > x1 + Tiny) { 00582 writelog("\nWarning: value "+fi()+" is above upper limit of " 00583 +vmax+".\n"); 00584 } 00585 } 00586 fctr = (convert ? toSI(units) : 1.0); // toSI(typ,units); 00587 v[title] = fctr*x; 00588 } 00589 } 00590 00591 00592 // Get a floating-point value from a child element. 00593 /* 00594 * Returns a double value for the child named 'name' of element 'parent'. If 00595 * 'type' is supplied and matches a known unit type, unit 00596 * conversion to SI will be done if the child element has an attribute 00597 * 'units'. 00598 * 00599 * Note, it's an error for the child element not to exist. 00600 * 00601 * Example: 00602 * 00603 * Code snipet: 00604 * @verbatum 00605 const XML_Node &State_XMLNode; 00606 doublereal pres = OneAtm; 00607 if (state_XMLNode.hasChild("pressure")) { 00608 pres = getFloat(State_XMLNode, "pressure", "toSI"); 00609 } 00610 @endverbatum 00611 * 00612 * reads the corresponding XML file: 00613 * @verbatum 00614 <state> 00615 <pressure units="Pa"> 101325.0 </pressure> 00616 <\state> 00617 @endverbatum 00618 * 00619 * @param parent reference to the XML_Node object of the parent XML element 00620 * @param name Name of the XML child element 00621 * @param type String type. Currently known types are "toSI" and "actEnergy", 00622 * and "" , for no conversion. The default value is "" 00623 * which implies that no conversion is allowed. 00624 */ 00625 doublereal getFloat(const Cantera::XML_Node& parent, 00626 const std::string &name, 00627 const std::string type) { 00628 if (!parent.hasChild(name)) 00629 throw CanteraError("getFloat (called from XML Node \"" + 00630 parent.name() + "\"): ", 00631 "no child XML element named \"" + name + "\" exists"); 00632 const XML_Node& node = parent.child(name); 00633 return getFloatCurrent(node, type); 00634 } 00635 00636 doublereal getFloatCurrent(const Cantera::XML_Node& node, 00637 const std::string type) { 00638 doublereal x, x0, x1, fctr = 1.0; 00639 string units, vmin, vmax; 00640 x = atof(node().c_str()); 00641 x0 = Undef; 00642 x1 = Undef; 00643 units = node["units"]; 00644 vmin = node["min"]; 00645 vmax = node["max"]; 00646 if (vmin != "") { 00647 x0 = atof(vmin.c_str()); 00648 if (x < x0 - Tiny) { 00649 writelog("\nWarning: value "+node()+" is below lower limit of " 00650 +vmin+".\n"); 00651 } 00652 } 00653 if (node["max"] != "") { 00654 x1 = atof(vmax.c_str()); 00655 if (x > x1 + Tiny) { 00656 writelog("\nWarning: value "+node()+" is above upper limit of " 00657 +vmax+".\n"); 00658 } 00659 } 00660 // Note, most type's of converters default to toSI() type atm. 00661 // This may change and become more specific in the future. 00662 if (type == "actEnergy" && units != "") { 00663 fctr = actEnergyToSI(units); 00664 } else if (type == "toSI" && units != "") { 00665 fctr = toSI(units); 00666 } else if (type == "temperature" && units != "") { 00667 fctr = toSI(units); 00668 } else if (type == "density" && units != "") { 00669 fctr = toSI(units); 00670 } else if (type == "pressure" && units != "") { 00671 fctr = toSI(units); 00672 } else if (type != "" && units != "") { 00673 fctr = toSI(units); 00674 #ifdef DEBUG_MODE 00675 writelog("\nWarning: conversion toSI() was done on node value " + node.name() + 00676 "but wasn't explicity requested. Type was \"" + type + "\"\n"); 00677 #endif 00678 } 00679 // Note, below currently produces a lot of output due to transport blocks. 00680 // This needs to be addressed. 00681 #ifdef DEBUG_MODE_MORE 00682 else if (type == "" && units != "") { 00683 writelog("\nWarning: XML node " + node.name() + 00684 "has a units attribute, \"" + units + "\"," 00685 "but no conversion was done because the getFloat() command didn't have a type\n"); 00686 } 00687 #endif 00688 return fctr*x; 00689 } 00690 00691 00692 bool getOptionalFloat(const Cantera::XML_Node& parent, 00693 const std::string &name, 00694 doublereal &fltRtn, 00695 const std::string type) { 00696 if (parent.hasChild(name)) { 00697 fltRtn= getFloat(parent, name, type); 00698 return true; 00699 } 00700 return false; 00701 } 00702 00703 // Get an optional floating-point value from a child element. 00704 /* 00705 * Returns a doublereal value for the child named 'name' of element 'parent'. If 00706 * 'type' is supplied and matches a known unit type, unit 00707 * conversion to SI will be done if the child element has an attribute 00708 * 'units'. 00709 * 00710 * 00711 * 00712 * Example: 00713 * 00714 * Code snipet: 00715 * @verbatim 00716 const XML_Node &State_XMLNode; 00717 doublereal pres = OneAtm; 00718 bool exists = getOptionalFloat(State_XMLNode, "pressure", pres, "toSI"); 00719 @endverbatim 00720 * 00721 * reads the corresponding XML file: 00722 * @verbatim 00723 <state> 00724 <pressure units="Pa"> 101325.0 </pressure> 00725 <\state> 00726 @endverbatim 00727 * 00728 * @param parent reference to the XML_Node object of the parent XML element 00729 * @param name Name of the XML child element 00730 * @param fltRtn Float Return. It will be overridden if the XML 00731 * element exists. 00732 * @param type String type. Currently known types are "toSI" 00733 * and "actEnergy", 00734 * and "" , for no conversion. The default value is "", 00735 * which implies that no conversion is allowed. 00736 * 00737 * @return returns true if the child element named "name" exists 00738 */ 00739 doublereal getFloatDefaultUnits(const Cantera::XML_Node& parent, std::string name, 00740 std::string defaultUnits, std::string type) { 00741 00742 doublereal fctr = 1.0; 00743 if (defaultUnits == "") { 00744 throw CanteraError("getFloatDefaultUnits", 00745 "need to supply an actual value of defaultUnits"); 00746 } 00747 if (type == "actEnergy") { 00748 fctr = actEnergyToSI(defaultUnits); 00749 } else if (type == "toSI") { 00750 fctr = toSI(defaultUnits); 00751 } else if (defaultUnits == "temperature") { 00752 fctr = toSI(defaultUnits); 00753 } else if (type == "density") { 00754 fctr = toSI(defaultUnits); 00755 } else if (type == "pressure") { 00756 fctr = toSI(defaultUnits); 00757 } else { 00758 throw CanteraError("getFloatDefaultUnits", 00759 "type of units must be supplied and understood"); 00760 } 00761 doublereal val = getFloat(parent, name, type); 00762 val /= fctr; 00763 return val; 00764 } 00765 00766 bool getOptionalModel(const Cantera::XML_Node& parent, const std::string nodeName, 00767 std::string &modelName) { 00768 if (parent.hasChild(nodeName)) { 00769 const XML_Node& node = parent.child(nodeName); 00770 modelName = node["model"]; 00771 return true; 00772 } 00773 return false; 00774 } 00775 00776 // Get an integer value from a child element. 00777 /* 00778 * Returns an integer value for the child named 'name' of element 'parent'. 00779 * 00780 * Note, it's an error for the child element not to exist. 00781 * 00782 * Example: 00783 * 00784 * Code snipet: 00785 * @verbatum 00786 const XML_Node &State_XMLNode; 00787 int number = 1; 00788 if (state_XMLNode.hasChild("NumProcs")) { 00789 number = getInteger(State_XMLNode, "numProcs"); 00790 } 00791 @endverbatum 00792 * 00793 * reads the corresponding XML file: 00794 * @verbatum 00795 <state> 00796 <numProcs> 10 <numProcs/> 00797 <\state> 00798 @endverbatum 00799 * 00800 * @param parent reference to the XML_Node object of the parent XML element 00801 * @param name Name of the XML child element 00802 */ 00803 int getInteger(const Cantera::XML_Node& parent, std::string name) { 00804 if (!parent.hasChild(name)) { 00805 throw CanteraError("getInteger (called from XML Node \"" + 00806 parent.name() + "\"): ", 00807 "no child XML element named " + name); 00808 } 00809 const XML_Node& node = parent.child(name); 00810 int x, x0, x1; 00811 string units, vmin, vmax; 00812 x = atoi(node().c_str()); 00813 x0 = -9999999; 00814 x1 = 9999999; 00815 vmin = node["min"]; 00816 vmax = node["max"]; 00817 if (vmin != "") { 00818 x0 = atoi(vmin.c_str()); 00819 if (x < x0) { 00820 writelog("\nWarning: value "+node()+" is below lower limit of " 00821 +vmin+".\n"); 00822 } 00823 } 00824 if (node["max"] != "") { 00825 x1 = atoi(vmax.c_str()); 00826 if (x > x1) { 00827 writelog("\nWarning: value "+node()+" is above upper limit of " 00828 +vmax+".\n"); 00829 } 00830 } 00831 return x; 00832 } 00833 00834 // This function reads the current node or a child node of the current node 00835 // with the default name, "floatArray", with a value field 00836 // consisting of a comma separated list of floats 00837 /* 00838 * This function will read either the current XML node or a child node 00839 * to the current XML node, with the 00840 * name "floatArray". It will have a title attribute, and the body 00841 * of the XML node will be filled out with a comma separated list of 00842 * doublereals. 00843 * Get an array of floats from the XML Node. The argument field 00844 * is assumed to consist of an arbitrary number of comma 00845 * separated floats, with an arbitrary amount of white space 00846 * separating each field. 00847 * If the node array has an units attribute field, then 00848 * the units are used to convert the floats, iff convert is true. 00849 * 00850 * Example: 00851 * 00852 * Code snipet: 00853 * @verbatum 00854 const XML_Node &State_XMLNode; 00855 vector_fp v; 00856 bool convert = true; 00857 unitsString = ""; 00858 nodeName="floatArray"; 00859 getFloatArray(State_XMLNode, v, convert, unitsString, nodeName); 00860 @endverbatum 00861 * 00862 * reads the corresponding XML file: 00863 * 00864 * @verbatum 00865 <state> 00866 <floatArray units="m3"> 32.4, 1, 100. <\floatArray> 00867 <\state> 00868 @endverbatum 00869 * 00870 * Will produce the vector 00871 * 00872 * v[0] = 32.4 00873 * v[1] = 1.0 00874 * v[2] = 100. 00875 * 00876 * 00877 * @param node XML parent node of the floatArray 00878 * @param v Output vector of floats containing the floatArray information. 00879 * @param convert Conversion to SI is carried out if this boolean is 00880 * True. The default is true. 00881 * @param unitsString String name of the type attribute. This is an optional 00882 * parameter. The default is to have an empty string. 00883 * The only string that is recognized is actEnergy. 00884 * Anything else has no effect. This affects what 00885 * units converter is used. 00886 * @param nodeName XML Name of the XML node to read. 00887 * The default value for the node name is floatArray 00888 * 00889 * @return Returns the number of floats read 00890 */ 00891 int getFloatArray(const Cantera::XML_Node& node, Cantera::vector_fp& v, 00892 const bool convert, const std::string unitsString, 00893 const std::string nodeName) { 00894 string::size_type icom; 00895 string numstr; 00896 doublereal dtmp; 00897 string nn = node.name(); 00898 const Cantera::XML_Node *readNode = &node; 00899 if (nn != nodeName) { 00900 vector<Cantera::XML_Node *> ll; 00901 node.getChildren(nodeName, ll); 00902 if (ll.size() == 0) { 00903 throw CanteraError("getFloatArray", 00904 "wrong xml element type/name: was expecting " 00905 + nodeName + "but accessed " + node.name()); 00906 } else { 00907 readNode = ll[0]; 00908 } 00909 } 00910 00911 v.clear(); 00912 doublereal vmin = Undef, vmax = Undef; 00913 00914 doublereal funit = 1.0; 00915 /* 00916 * Get the attributes field, units, from the XML node 00917 */ 00918 std::string units = (*readNode)["units"]; 00919 if (units != "" && convert) { 00920 if (unitsString == "actEnergy" && units != "") { 00921 funit = actEnergyToSI(units); 00922 } else if (unitsString != "" && units != "") { 00923 funit = toSI(units); 00924 } 00925 } 00926 00927 if ((*readNode)["min"] != "") 00928 vmin = atofCheck((*readNode)["min"].c_str()); 00929 if ((*readNode)["max"] != "") 00930 vmax = atofCheck((*readNode)["max"].c_str()); 00931 00932 doublereal vv; 00933 std::string val = readNode->value(); 00934 while (1 > 0) { 00935 icom = val.find(','); 00936 if (icom != string::npos) { 00937 numstr = val.substr(0,icom); 00938 val = val.substr(icom+1,val.size()); 00939 dtmp = atofCheck(numstr.c_str()); 00940 v.push_back(dtmp); 00941 } 00942 else { 00943 /* 00944 * This little bit of code is to allow for the 00945 * possibility of a comma being the last 00946 * item in the value text. This was allowed in 00947 * previous versions of Cantera, even though it 00948 * would appear to be odd. So, we keep the 00949 * possibilty in for backwards compatibility. 00950 */ 00951 int nlen = strlen(val.c_str()); 00952 if (nlen > 0) { 00953 dtmp = atofCheck(val.c_str()); 00954 v.push_back(dtmp); 00955 } 00956 break; 00957 } 00958 vv = v.back(); 00959 if (vmin != Undef && vv < vmin - Tiny) { 00960 writelog("\nWarning: value "+fp2str(vv)+ 00961 " is below lower limit of " +fp2str(vmin)+".\n"); 00962 } 00963 if (vmax != Undef && vv > vmax + Tiny) { 00964 writelog("\nWarning: value "+fp2str(vv)+ 00965 " is above upper limit of " +fp2str(vmin)+".\n"); 00966 } 00967 } 00968 int nv = v.size(); 00969 for (int n = 0; n < nv; n++) { 00970 v[n] *= funit; 00971 } 00972 return v.size(); 00973 } 00974 00975 // This routine is used to interpret the value portions of XML 00976 // elements that contain colon separated pairs. 00977 /* 00978 * These are used, for example, in describing the element 00979 * composition of species. 00980 * 00981 * <atomArray> H:4 C:1 <atomArray> 00982 * 00983 * The string is first separated into a string vector according 00984 * to the location of white space. Then each string is again 00985 * separated into two parts according to the location of a 00986 * colon in the string. The first part of the string is 00987 * used as the key, while the second part of the string is 00988 * used as the value, in the return map. 00989 * It is an error to not find a colon in each string pair. 00990 * 00991 * @param node Current node 00992 * @param m Output Map containing the pairs of values found 00993 * in the XML Node 00994 */ 00995 void getMap(const Cantera::XML_Node& node, std::map<std::string, std::string>& m) { 00996 std::vector<std::string> v; 00997 getStringArray(node, v); 00998 std::string key, val; 00999 int n = static_cast<int>(v.size()); 01000 string::size_type icolon; 01001 for (int i = 0; i < n; i++) { 01002 icolon = v[i].find(":"); 01003 if (icolon == string::npos) { 01004 throw CanteraError("getMap","missing colon in map entry (" 01005 +v[i]+")"); 01006 } 01007 key = v[i].substr(0,icolon); 01008 val = v[i].substr(icolon+1, v[i].size()); 01009 m[key] = val; 01010 } 01011 } 01012 01013 // This function interprets the value portion of an XML element 01014 // as a series of "Pairs" separated by white space. 01015 /* 01016 * Each pair consists of nonwhite-space characters. 01017 * The first ":" found in the pair string is used to separate 01018 * the string into two parts. The first part is called the "key" 01019 * The second part is called the "val". 01020 * String vectors of key[i] and val[i] are returned in the 01021 * argument list. 01022 * Warning: No spaces are allowed in each pair. Quotes get included as part 01023 * of the string. 01024 * Example: @verbatum 01025 <xmlNode> 01026 red:112 blue:34 01027 green:banana 01028 </xmlNode> 01029 @endverbatum 01030 * 01031 * Returns: 01032 * key val 01033 * 0: "red" "112" 01034 * 1: "blue" "34" 01035 * 2: "green" "banana" 01036 * 01037 * 01038 * @param node XML Node 01039 * @param key Vector of keys for each entry 01040 * @param val Vector of values for each entry 01041 * 01042 * @return Returns the number of pairs found 01043 */ 01044 int getPairs(const Cantera::XML_Node& node, std::vector<std::string>& key, 01045 std::vector<std::string>& val) { 01046 vector<string> v; 01047 getStringArray(node, v); 01048 int n = static_cast<int>(v.size()); 01049 string::size_type icolon; 01050 for (int i = 0; i < n; i++) { 01051 icolon = v[i].find(":"); 01052 if (icolon == string::npos) { 01053 throw CanteraError("getPairs","Missing a colon in the Pair entry (" 01054 +v[i]+")"); 01055 } 01056 key.push_back(v[i].substr(0,icolon)); 01057 val.push_back(v[i].substr(icolon+1, v[i].size())); 01058 //cout << "getPairs: " << key.back() << " " << val.back() << endl; 01059 } 01060 return n; 01061 } 01062 01063 // This function interprets the value portion of an XML element 01064 // as a series of "Matrix ids and entries" separated by white space. 01065 /* 01066 * Each pair consists of nonwhite-space characters. 01067 * The first two ":" found in the pair string is used to separate 01068 * the string into three parts. The first part is called the first 01069 * key. The second part is the second key. Both parts must match 01070 * an entry in the keyString1 and keyString2, respectively, 01071 * in order to provide a location to 01072 * place the object in the matrix. 01073 * The third part is called the value. It is expected to be 01074 * a double. It is translated into a double and placed into the 01075 * correct location in the matrix. 01076 * 01077 * Warning: No spaces are allowed in each triplet. Quotes are part 01078 * of the string. 01079 * Example 01080 * keyString = red, blue, black, green 01081 * <xmlNode> 01082 * red:green:112 01083 * blue:black:3.3E-23 01084 * 01085 * </xmlNode> 01086 * 01087 * Returns: 01088 * retnValues(0, 3) = 112 01089 * retnValues(1, 2) = 3.3E-23 01090 * 01091 * 01092 * @param node XML Node containing the information for the matrix 01093 * @param keyStringRow Key string for the row 01094 * @param keyStringCol Key string for the column entries 01095 * @param returnValues Return Matrix. 01096 * @param convert If this is true, and if the node has a units 01097 * attribute, then conversion to si units is carried 01098 * out. Default is true. 01099 * @param matrixSymmetric If true entries are made so that the matrix 01100 * is always symmetric. Default is false. 01101 */ 01102 void getMatrixValues(const Cantera::XML_Node& node, 01103 const std::vector<std::string>& keyStringRow, 01104 const std::vector<std::string>& keyStringCol, 01105 Cantera::Array2D &retnValues, const bool convert, 01106 const bool matrixSymmetric) { 01107 int szKey1 = keyStringRow.size(); 01108 int szKey2 = keyStringCol.size(); 01109 int nrow = retnValues.nRows(); 01110 int ncol = retnValues.nColumns(); 01111 if (szKey1 > nrow) { 01112 throw CanteraError("getMatrixValues", 01113 "size of key1 greater than numrows"); 01114 } 01115 if (szKey2 > ncol) { 01116 throw CanteraError("getMatrixValues", 01117 "size of key2 greater than num cols"); 01118 } 01119 if (matrixSymmetric) { 01120 if (nrow != ncol) { 01121 throw CanteraError("getMatrixValues", 01122 "nrow != ncol for a symmetric matrix"); 01123 } 01124 } 01125 01126 /* 01127 * Get the attributes field, units, from the XML node 01128 * and determine the conversion factor, funit. 01129 */ 01130 doublereal funit = 1.0; 01131 string units = node["units"]; 01132 if (units != "" && convert) { 01133 funit = toSI(units); 01134 } 01135 01136 string key1; 01137 string key2; 01138 string rmm; 01139 string val; 01140 vector<string> v; 01141 getStringArray(node, v); 01142 int icol, irow; 01143 int n = static_cast<int>(v.size()); 01144 string::size_type icolon; 01145 for (int i = 0; i < n; i++) { 01146 icolon = v[i].find(":"); 01147 if (icolon == string::npos) { 01148 throw CanteraError("getMatrixValues","Missing two colons (" 01149 +v[i]+")"); 01150 } 01151 key1 = v[i].substr(0,icolon); 01152 rmm = v[i].substr(icolon+1, v[i].size()); 01153 icolon = rmm.find(":"); 01154 if (icolon == string::npos) { 01155 throw CanteraError("getMatrixValues","Missing one colon (" 01156 +v[i]+")"); 01157 } 01158 key2 = rmm.substr(0,icolon); 01159 val = rmm.substr(icolon+1, rmm.size()); 01160 icol = -1; 01161 irow = -1; 01162 for (int j = 0; j < szKey1; j++) { 01163 if (key1 == keyStringRow[j]) { 01164 irow = j; 01165 break; 01166 } 01167 } 01168 if (irow == -1) { 01169 throw CanteraError("getMatrixValues","Row not matched by string: " 01170 + key1); 01171 } 01172 for (int j = 0; j < szKey2; j++) { 01173 if (key2 == keyStringCol[j]) { 01174 icol = j; 01175 break; 01176 } 01177 } 01178 if (icol == -1) { 01179 throw CanteraError("getMatrixValues","Col not matched by string: " 01180 + key2); 01181 } 01182 double dval = atofCheck(val.c_str()); 01183 dval *= funit; 01184 /* 01185 * Finally, insert the value; 01186 */ 01187 retnValues(irow, icol) = dval; 01188 if (matrixSymmetric) { 01189 retnValues(icol, irow) = dval; 01190 } 01191 } 01192 } 01193 01194 // This function interprets the value portion of an XML element 01195 // as a string. It then separates the string up into tokens 01196 // according to the location of white space. 01197 /* 01198 * The separate tokens are returned in the string vector 01199 * 01200 * @param node Node to get the value from 01201 * @param v Output vector containing the string tokens 01202 */ 01203 void getStringArray(const Cantera::XML_Node& node, std::vector<std::string>& v) { 01204 std::string val = node.value(); 01205 tokenizeString(val, v); 01206 } 01207 01208 // This function reads a child node with the default name, "floatArray", with a value 01209 // consisting of a comma separated list of floats 01210 /* 01211 * This function will read a child node to the current XML node, with the 01212 * name "floatArray". It will have a title attribute, and the body 01213 * of the XML node will be filled out with a comma separated list of 01214 * doublereals. 01215 * Get an array of floats from the XML Node. The argument field 01216 * is assumed to consist of an arbitrary number of comma 01217 * separated floats, with an arbitrary amount of white space 01218 * separating each field. 01219 * If the node array has an units attribute field, then 01220 * the units are used to convert the floats, iff convert is true. 01221 * 01222 * Example: 01223 * 01224 * Code snipet: 01225 * @verbatum 01226 const XML_Node &State_XMLNode; 01227 vector_fp v; 01228 bool convert = true; 01229 unitsString = ""; 01230 nodeName="floatArray"; 01231 getFloatArray(State_XMLNode, v, convert, unitsString, nodeName); 01232 @endverbatum 01233 * 01234 * reads the corresponding XML file: 01235 * 01236 * @verbatum 01237 <state> 01238 <floatArray units="m3"> 32.4, 1, 100. <\floatArray> 01239 <\state> 01240 @endverbatum 01241 * 01242 * Will produce the vector 01243 * 01244 * v[0] = 32.4 01245 * v[1] = 1.0 01246 * v[2] = 100. 01247 * 01248 * 01249 * @param node XML parent node of the floatArray 01250 * @param v Output vector of floats containing the floatArray information. 01251 * @param convert Conversion to SI is carried out if this boolean is 01252 * True. The default is true. 01253 * @param typeString String name of the type attribute. This is an optional 01254 * parameter. The default is to have an empty string. 01255 * The only string that is recognized is actEnergy. 01256 * Anything else has no effect. This affects what 01257 * units converter is used. 01258 * @param nodeName XML Name of the XML node to read. 01259 * The default value for the node name is floatArray 01260 */ 01261 void getFunction(const Cantera::XML_Node& node, std::string& type, doublereal& xmin, 01262 doublereal& xmax, Cantera::vector_fp& coeffs) { 01263 const XML_Node& c = node.child("floatArray"); 01264 coeffs.clear(); 01265 getFloatArray(c,coeffs); 01266 xmin = Cantera::Undef; 01267 xmin = Cantera::Undef; 01268 if (node["min"] != "") xmin = fpValue(node["min"]); 01269 if (node["max"] != "") xmax = fpValue(node["max"]); 01270 type = node["type"]; 01271 } 01272 }