1 /**
2 * @file ctml.cpp
3 * Definitions for functions to read and write CTML.
4 */
5
6 // This file is part of Cantera. See License.txt in the top-level directory or
7 // at https://cantera.org/license.txt for license and copyright information.
8
9 #include "cantera/base/ctml.h"
10 #include "cantera/base/stringUtils.h"
11 #include "cantera/base/Array.h"
12 #include "cantera/base/ctexceptions.h"
13 #include "cantera/base/global.h"
14
15 using namespace std;
16
17 namespace Cantera
18 {
19 std::string FP_Format = "%23.15E";
20
addFloat(XML_Node & node,const std::string & title,const doublereal val,const std::string & units,const std::string & type,const doublereal minval,const doublereal maxval)21 void addFloat(XML_Node& node, const std::string& title,
22 const doublereal val, const std::string& units,
23 const std::string& type, const doublereal minval,
24 const doublereal maxval)
25 {
26 XML_Node& f = node.addChild(title, val, FP_Format);
27 if (type != "") {
28 f.addAttribute("type",type);
29 }
30 if (units != "") {
31 f.addAttribute("units",units);
32 }
33 f.addAttribute("vtype", "float");
34 if (minval != Undef) {
35 f.addAttribute("min",minval);
36 }
37 if (maxval != Undef) {
38 f.addAttribute("max",maxval);
39 }
40 }
41
addFloatArray(XML_Node & node,const std::string & title,const size_t n,const doublereal * const vals,const std::string & units,const std::string & type,const doublereal minval,const doublereal maxval)42 void addFloatArray(XML_Node& node, const std::string& title, const size_t n,
43 const doublereal* const vals, const std::string& units,
44 const std::string& type,
45 const doublereal minval, const doublereal maxval)
46 {
47 std::string v = "";
48 for (size_t i = 0; i < n; i++) {
49 v += fmt::sprintf(FP_Format, vals[i]);
50 if (i == n-1) {
51 v += "\n";
52 } else if (i > 0 && (i+1) % 3 == 0) {
53 v += ",\n";
54 } else {
55 v += ", ";
56 }
57 }
58 XML_Node& f = node.addChild("floatArray",v);
59 f.addAttribute("title",title);
60 if (type != "") {
61 f.addAttribute("type",type);
62 }
63 f.addAttribute("size", double(n));
64 if (units != "") {
65 f.addAttribute("units",units);
66 }
67 if (minval != Undef) {
68 f.addAttribute("min",minval);
69 }
70 if (maxval != Undef) {
71 f.addAttribute("max",maxval);
72 }
73 }
74
addNamedFloatArray(XML_Node & node,const std::string & name,const size_t n,const doublereal * const vals,const std::string units,const std::string type,const doublereal minval,const doublereal maxval)75 void addNamedFloatArray(XML_Node& node, const std::string& name, const size_t n,
76 const doublereal* const vals, const std::string units,
77 const std::string type, const doublereal minval,
78 const doublereal maxval)
79 {
80 std::string v = "";
81 for (size_t i = 0; i < n; i++) {
82 v += fmt::sprintf(FP_Format, vals[i]);
83 if (i == n-1) {
84 v += "\n";
85 } else if (i > 0 && (i+1) % 3 == 0) {
86 v += ",\n";
87 } else {
88 v += ", ";
89 }
90 }
91 XML_Node& f = node.addChild(name, v);
92 if (type != "") {
93 f.addAttribute("type",type);
94 }
95
96 // Add vtype, which indicates the type of the value. Here we specify it as a
97 // list of floats separated by commas, with a length given by size
98 // attribute.
99 f.addAttribute("vtype", "floatArray");
100
101 f.addAttribute("size", n);
102 if (units != "") {
103 f.addAttribute("units", units);
104 }
105 if (minval != Undef) {
106 f.addAttribute("min", minval);
107 }
108 if (maxval != Undef) {
109 f.addAttribute("max", maxval);
110 }
111 }
112
addString(XML_Node & node,const std::string & titleString,const std::string & valueString,const std::string & typeString)113 void addString(XML_Node& node, const std::string& titleString,
114 const std::string& valueString,
115 const std::string& typeString)
116 {
117 XML_Node& f = node.addChild("string", valueString);
118 f.addAttribute("title", titleString);
119 if (typeString != "") {
120 f.addAttribute("type", typeString);
121 }
122 }
123
getByTitle(const XML_Node & node,const std::string & title)124 XML_Node* getByTitle(const XML_Node& node, const std::string& title)
125 {
126 XML_Node* s = node.findByAttr("title", title);
127 if (s && s->parent() == &node) {
128 return s;
129 }
130 return 0;
131 }
132
getChildValue(const XML_Node & parent,const std::string & nameString)133 std::string getChildValue(const XML_Node& parent, const std::string& nameString)
134 {
135 if (!parent.hasChild(nameString)) {
136 return "";
137 }
138 return parent(nameString);
139 }
140
getString(const XML_Node & node,const std::string & titleString,std::string & valueString,std::string & typeString)141 void getString(const XML_Node& node, const std::string& titleString, std::string& valueString,
142 std::string& typeString)
143 {
144 XML_Node* s = getByTitle(node, titleString);
145 if (s && s->name() == "string") {
146 valueString = s->value();
147 typeString = s->attrib("type");
148 } else {
149 valueString = "";
150 typeString = "";
151 }
152 }
153
getIntegers(const XML_Node & node,std::map<std::string,int> & v)154 void getIntegers(const XML_Node& node,
155 std::map<std::string, int>& v)
156 {
157 std::vector<XML_Node*> f = node.getChildren("integer");
158 for (size_t i = 0; i < f.size(); i++) {
159 const XML_Node& fi = *f[i];
160 if (fi["min"] != "" && fi["max"] != "") {
161 v[fi["title"]] = fi.int_value();
162 }
163 }
164 }
165
getFloat(const XML_Node & parent,const std::string & name,const std::string & type)166 doublereal getFloat(const XML_Node& parent,
167 const std::string& name,
168 const std::string& type)
169 {
170 if (!parent.hasChild(name)) {
171 throw CanteraError("getFloat (called from XML Node \"" +
172 parent.name() + "\"): ",
173 "no child XML element named \"" + name + "\" exists");
174 }
175 const XML_Node& node = parent.child(name);
176 return getFloatCurrent(node, type);
177 }
178
getFloatCurrent(const XML_Node & node,const std::string & type)179 doublereal getFloatCurrent(const XML_Node& node, const std::string& type)
180 {
181 doublereal fctr = 1.0;
182 doublereal x = node.fp_value();
183 const string& units = node["units"];
184 const string& vmin = node["min"];
185 const string& vmax = node["max"];
186 if (vmin != "" && x < fpValue(vmin) - Tiny) {
187 writelog("\nWarning: value "+node.value()+" is below lower limit of "
188 +vmin+".\n");
189 }
190 if (node["max"] != "" && x > fpValue(vmax) + Tiny) {
191 writelog("\nWarning: value "+node.value()+" is above upper limit of "
192 +vmax+".\n");
193 }
194 // Note, most types of converters default to toSI() type atm.
195 // This may change and become more specific in the future.
196 if (type == "actEnergy" && units != "") {
197 fctr = actEnergyToSI(units);
198 } else if (type == "toSI" && units != "") {
199 fctr = toSI(units);
200 } else if (type == "temperature" && units != "") {
201 fctr = toSI(units);
202 } else if (type == "density" && units != "") {
203 fctr = toSI(units);
204 } else if (type == "pressure" && units != "") {
205 fctr = toSI(units);
206 } else if (type != "" && units != "") {
207 fctr = toSI(units);
208 writelog("\nWarning: conversion toSI() was done on node value " + node.name() +
209 "but wasn't explicitly requested. Type was \"" + type + "\"\n");
210 }
211 return fctr*x;
212 }
213
getOptionalFloat(const XML_Node & parent,const std::string & name,doublereal & fltRtn,const std::string & type)214 bool getOptionalFloat(const XML_Node& parent,
215 const std::string& name,
216 doublereal& fltRtn,
217 const std::string& type)
218 {
219 if (parent.hasChild(name)) {
220 fltRtn = getFloat(parent, name, type);
221 return true;
222 }
223 return false;
224 }
225
getOptionalModel(const XML_Node & parent,const std::string & nodeName,std::string & modelName)226 bool getOptionalModel(const XML_Node& parent, const std::string& nodeName,
227 std::string& modelName)
228 {
229 if (parent.hasChild(nodeName)) {
230 modelName = parent.child(nodeName)["model"];
231 return true;
232 }
233 return false;
234 }
235
getInteger(const XML_Node & parent,const std::string & name)236 int getInteger(const XML_Node& parent, const std::string& name)
237 {
238 if (!parent.hasChild(name)) {
239 throw CanteraError("getInteger (called from XML Node \"" +
240 parent.name() + "\"): ",
241 "no child XML element named " + name);
242 }
243 const XML_Node& node = parent.child(name);
244 int x = node.int_value();
245 const string& vmin = node["min"];
246 const string& vmax = node["max"];
247 if (vmin != "" && x < intValue(vmin)) {
248 writelog("\nWarning: value "+node.value()+" is below lower limit of "
249 +vmin+".\n");
250 }
251 if (node["max"] != "" && x > intValue(vmax)) {
252 writelog("\nWarning: value "+node.value()+" is above upper limit of "
253 +vmax+".\n");
254 }
255 return x;
256 }
257
getFloatArray(const XML_Node & node,vector_fp & v,const bool convert,const std::string & unitsString,const std::string & nodeName)258 size_t getFloatArray(const XML_Node& node, vector_fp & v,
259 const bool convert, const std::string& unitsString,
260 const std::string& nodeName)
261 {
262 const XML_Node* readNode = &node;
263 if (node.name() != nodeName) {
264 vector<XML_Node*> ll = node.getChildren(nodeName);
265 if (ll.size() == 0) {
266 throw CanteraError("getFloatArray",
267 "wrong XML element type/name: was expecting "
268 + nodeName + "but accessed " + node.name());
269 } else {
270 readNode = ll[0];
271 ll = readNode->getChildren("floatArray");
272 if (ll.size() > 0) {
273 readNode = ll[0];
274 }
275 }
276 }
277
278 v.clear();
279 doublereal vmin = Undef, vmax = Undef;
280 doublereal funit = 1.0;
281
282 // Get the attributes field, units, from the XML node
283 std::string units = readNode->attrib("units");
284 if (units != "" && convert) {
285 if (unitsString == "actEnergy" && units != "") {
286 funit = actEnergyToSI(units);
287 } else if (unitsString != "" && units != "") {
288 funit = toSI(units);
289 }
290 }
291
292 if (readNode->attrib("min") != "") {
293 vmin = fpValueCheck(readNode->attrib("min"));
294 }
295 if (readNode->attrib("max") != "") {
296 vmax = fpValueCheck(readNode->attrib("max"));
297 }
298
299 std::string val = readNode->value();
300 while (true) {
301 size_t icom = val.find(',');
302 if (icom != string::npos) {
303 string numstr = val.substr(0,icom);
304 val = val.substr(icom+1,val.size());
305 v.push_back(fpValueCheck(numstr));
306 } else {
307 // This little bit of code is to allow for the possibility of a
308 // comma being the last item in the value text. This was allowed in
309 // previous versions of Cantera, even though it would appear to be
310 // odd. So, we keep the possibility in for backwards compatibility.
311 if (!val.empty()) {
312 v.push_back(fpValueCheck(val));
313 }
314 break;
315 }
316 doublereal vv = v.back();
317 if (vmin != Undef && vv < vmin - Tiny) {
318 writelog("\nWarning: value {} is below lower limit of {}.\n",
319 vv, vmin);
320 }
321 if (vmax != Undef && vv > vmax + Tiny) {
322 writelog("\nWarning: value {} is above upper limit of {}.\n",
323 vv, vmax);
324 }
325 }
326 for (size_t n = 0; n < v.size(); n++) {
327 v[n] *= funit;
328 }
329 return v.size();
330 }
331
getMap(const XML_Node & node,std::map<std::string,std::string> & m)332 void getMap(const XML_Node& node, std::map<std::string, std::string>& m)
333 {
334 std::vector<std::string> v;
335 getStringArray(node, v);
336 for (size_t i = 0; i < v.size(); i++) {
337 size_t icolon = v[i].find(":");
338 if (icolon == string::npos) {
339 throw CanteraError("getMap","missing colon in map entry ("
340 +v[i]+")");
341 }
342 m[v[i].substr(0,icolon)] = v[i].substr(icolon+1, v[i].size());
343 }
344 }
345
getPairs(const XML_Node & node,std::vector<std::string> & key,std::vector<std::string> & val)346 int getPairs(const XML_Node& node, std::vector<std::string>& key,
347 std::vector<std::string>& val)
348 {
349 vector<string> v;
350 getStringArray(node, v);
351 int n = static_cast<int>(v.size());
352 for (int i = 0; i < n; i++) {
353 size_t icolon = v[i].find(":");
354 if (icolon == string::npos) {
355 throw CanteraError("getPairs","Missing a colon in the Pair entry ("
356 +v[i]+")");
357 }
358 key.push_back(v[i].substr(0,icolon));
359 val.push_back(v[i].substr(icolon+1, v[i].size()));
360 }
361 return n;
362 }
363
getMatrixValues(const XML_Node & node,const std::vector<std::string> & keyStringRow,const std::vector<std::string> & keyStringCol,Array2D & retnValues,const bool convert,const bool matrixSymmetric)364 void getMatrixValues(const XML_Node& node,
365 const std::vector<std::string>& keyStringRow,
366 const std::vector<std::string>& keyStringCol,
367 Array2D& retnValues, const bool convert,
368 const bool matrixSymmetric)
369 {
370 if (keyStringRow.size() > retnValues.nRows()) {
371 throw CanteraError("getMatrixValues",
372 "size of key1 greater than numrows");
373 } else if (keyStringCol.size() > retnValues.nColumns()) {
374 throw CanteraError("getMatrixValues",
375 "size of key2 greater than num cols");
376 } else if (matrixSymmetric && retnValues.nRows() != retnValues.nColumns()) {
377 throw CanteraError("getMatrixValues",
378 "nrow != ncol for a symmetric matrix");
379 }
380
381 // Get the attributes field, units, from the XML node and determine the
382 // conversion factor, funit.
383 doublereal funit = 1.0;
384 if (convert && node["units"] != "") {
385 funit = toSI(node["units"]);
386 }
387
388 vector<string> v;
389 getStringArray(node, v);
390 for (size_t i = 0; i < v.size(); i++) {
391 size_t icolon = v[i].find(":");
392 if (icolon == string::npos) {
393 throw CanteraError("getMatrixValues","Missing two colons ("
394 +v[i]+")");
395 }
396 string key1 = v[i].substr(0,icolon);
397 string rmm = v[i].substr(icolon+1, v[i].size());
398
399 icolon = rmm.find(":");
400 if (icolon == string::npos) {
401 throw CanteraError("getMatrixValues","Missing one colon ("
402 +v[i]+")");
403 }
404
405 size_t irow = find(keyStringRow.begin(), keyStringRow.end(), key1)
406 - keyStringRow.begin();
407 if (irow == keyStringRow.size()) {
408 throw CanteraError("getMatrixValues","Row not matched by string: "
409 + key1);
410 }
411
412 string key2 = rmm.substr(0,icolon);
413 size_t icol = find(keyStringCol.begin(), keyStringCol.end(), key2)
414 - keyStringCol.begin();
415 if (icol == keyStringCol.size()) {
416 throw CanteraError("getMatrixValues","Col not matched by string: "
417 + key2);
418 }
419 double dval = fpValueCheck(rmm.substr(icolon+1, rmm.size())) * funit;
420
421 // Finally, insert the value;
422 retnValues(irow, icol) = dval;
423 if (matrixSymmetric) {
424 retnValues(icol, irow) = dval;
425 }
426 }
427 }
428
getStringArray(const XML_Node & node,std::vector<std::string> & v)429 void getStringArray(const XML_Node& node, std::vector<std::string>& v)
430 {
431 tokenizeString(node.value(), v);
432 }
433
434 }
435