525 lines
17 KiB
C++
525 lines
17 KiB
C++
/**
|
|
* @file ctml.cpp
|
|
* Definitions for functions to read and write CTML.
|
|
*/
|
|
// Copyright 2002 California Institute of Technology
|
|
|
|
#include "cantera/base/ctml.h"
|
|
|
|
#define CTML_VERSION_1_4_1
|
|
|
|
#include "cantera/base/global.h"
|
|
#include "cantera/base/stringUtils.h"
|
|
|
|
using namespace std;
|
|
using namespace Cantera;
|
|
|
|
namespace ctml
|
|
{
|
|
std::string FP_Format = "%23.15E";
|
|
|
|
void addInteger(Cantera::XML_Node& node, const std::string& title, const int val,
|
|
const std::string& units, const std::string& type)
|
|
{
|
|
#ifdef CTML_VERSION_1_4
|
|
XML_Node& f = node.addChild("integer", val);
|
|
f.addAttribute("title", title);
|
|
#else
|
|
XML_Node& f = node.addChild(title, val);
|
|
#endif
|
|
f.addAttribute("vtype", "integer");
|
|
if (type != "") {
|
|
f.addAttribute("type",type);
|
|
}
|
|
if (units != "") {
|
|
f.addAttribute("units",units);
|
|
}
|
|
}
|
|
|
|
void addFloat(Cantera::XML_Node& node, const std::string& title,
|
|
const doublereal val, const std::string& units,
|
|
const std::string& type, const doublereal minval,
|
|
const doublereal maxval)
|
|
{
|
|
#ifdef CTML_VERSION_1_4
|
|
XML_Node& f = node.addChild("float", val, ctml::FP_Format);
|
|
f.addAttribute("title", title);
|
|
#else
|
|
XML_Node& f = node.addChild(title, val, ctml::FP_Format);
|
|
#endif
|
|
if (type != "") {
|
|
f.addAttribute("type",type);
|
|
}
|
|
if (units != "") {
|
|
f.addAttribute("units",units);
|
|
}
|
|
f.addAttribute("vtype", "float");
|
|
if (minval != Undef) {
|
|
f.addAttribute("min",minval);
|
|
}
|
|
if (maxval != Undef) {
|
|
f.addAttribute("max",maxval);
|
|
}
|
|
}
|
|
|
|
void addFloatArray(Cantera::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)
|
|
{
|
|
std::string v = "";
|
|
for (size_t i = 0; i < n; i++) {
|
|
v += fp2str(vals[i],FP_Format);
|
|
if (i == n-1) {
|
|
v += "\n";
|
|
} else if (i > 0 && (i+1) % 3 == 0) {
|
|
v += ",\n";
|
|
} else {
|
|
v += ", ";
|
|
}
|
|
}
|
|
XML_Node& f = node.addChild("floatArray",v);
|
|
f.addAttribute("title",title);
|
|
if (type != "") {
|
|
f.addAttribute("type",type);
|
|
}
|
|
f.addAttribute("size", double(n));
|
|
if (units != "") {
|
|
f.addAttribute("units",units);
|
|
}
|
|
if (minval != Undef) {
|
|
f.addAttribute("min",minval);
|
|
}
|
|
if (maxval != Undef) {
|
|
f.addAttribute("max",maxval);
|
|
}
|
|
}
|
|
|
|
void addNamedFloatArray(Cantera::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)
|
|
{
|
|
std::string v = "";
|
|
for (size_t i = 0; i < n; i++) {
|
|
v += fp2str(vals[i],FP_Format);
|
|
if (i == n-1) {
|
|
v += "\n";
|
|
} else if (i > 0 && (i+1) % 3 == 0) {
|
|
v += ",\n";
|
|
} else {
|
|
v += ", ";
|
|
}
|
|
}
|
|
XML_Node& f = node.addChild(name, v);
|
|
if (type != "") {
|
|
f.addAttribute("type",type);
|
|
}
|
|
/*
|
|
* Add vtype, which indicates the type of the value. Here we specify it as a list of floats separated
|
|
* by commas, with a length given by size attribute.
|
|
*/
|
|
f.addAttribute("vtype", "floatArray");
|
|
|
|
f.addAttribute("size", n);
|
|
if (units != "") {
|
|
f.addAttribute("units", units);
|
|
}
|
|
if (minval != Undef) {
|
|
f.addAttribute("min", minval);
|
|
}
|
|
if (maxval != Undef) {
|
|
f.addAttribute("max", maxval);
|
|
}
|
|
}
|
|
|
|
void addString(Cantera::XML_Node& node, const std::string& titleString,
|
|
const std::string& valueString,
|
|
const std::string& typeString)
|
|
{
|
|
XML_Node& f = node.addChild("string", valueString);
|
|
f.addAttribute("title", titleString);
|
|
if (typeString != "") {
|
|
f.addAttribute("type", typeString);
|
|
}
|
|
}
|
|
|
|
XML_Node* getByTitle(const Cantera::XML_Node& node, const std::string& title)
|
|
{
|
|
XML_Node* s = node.findByAttr("title", title);
|
|
if (s && s->parent() == &node) {
|
|
return s;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
std::string getChildValue(const Cantera::XML_Node& parent, const std::string& nameString)
|
|
{
|
|
if (!parent.hasChild(nameString)) {
|
|
return "";
|
|
}
|
|
return parent(nameString);
|
|
}
|
|
|
|
void getString(const Cantera::XML_Node& node, const std::string& titleString, std::string& valueString,
|
|
std::string& typeString)
|
|
{
|
|
XML_Node* s = getByTitle(node, titleString);
|
|
if (s && s->name() == "string") {
|
|
valueString = s->value();
|
|
typeString = (*s)["type"];
|
|
} else {
|
|
valueString = "";
|
|
typeString = "";
|
|
}
|
|
}
|
|
|
|
void getNamedStringValue(const Cantera::XML_Node& node, const std::string& nameString, std::string& valueString,
|
|
std::string& typeString)
|
|
{
|
|
valueString = "";
|
|
typeString = "";
|
|
if (node.hasChild(nameString)) {
|
|
XML_Node& xc = node.child(nameString);
|
|
valueString = xc.value();
|
|
typeString = xc["type"];
|
|
} else {
|
|
XML_Node* s = getByTitle(node, nameString);
|
|
if (s && s->name() == "string") {
|
|
valueString = (*s).value();
|
|
typeString = (*s)["type"];
|
|
}
|
|
}
|
|
}
|
|
|
|
void getIntegers(const Cantera::XML_Node& node,
|
|
std::map<std::string, int>& v)
|
|
{
|
|
std::vector<XML_Node*> f;
|
|
node.getChildren("integer",f);
|
|
for (size_t i = 0; i < f.size(); i++) {
|
|
const XML_Node& fi = *f[i];
|
|
if (fi["min"] != "" && fi["max"] != "") {
|
|
v[fi["title"]] = intValue(fi());
|
|
}
|
|
}
|
|
}
|
|
|
|
doublereal getFloat(const Cantera::XML_Node& parent,
|
|
const std::string& name,
|
|
const std::string& type)
|
|
{
|
|
if (!parent.hasChild(name))
|
|
throw CanteraError("getFloat (called from XML Node \"" +
|
|
parent.name() + "\"): ",
|
|
"no child XML element named \"" + name + "\" exists");
|
|
const XML_Node& node = parent.child(name);
|
|
return getFloatCurrent(node, type);
|
|
}
|
|
|
|
doublereal getFloatCurrent(const Cantera::XML_Node& node,
|
|
const std::string& type)
|
|
{
|
|
doublereal fctr = 1.0;
|
|
doublereal x = fpValue(node());
|
|
const string& units = node["units"];
|
|
const string& vmin = node["min"];
|
|
const string& vmax = node["max"];
|
|
if (vmin != "" && x < fpValue(vmin) - Tiny) {
|
|
writelog("\nWarning: value "+node()+" is below lower limit of "
|
|
+vmin+".\n");
|
|
}
|
|
if (node["max"] != "" && x > fpValue(vmax) + Tiny) {
|
|
writelog("\nWarning: value "+node()+" is above upper limit of "
|
|
+vmax+".\n");
|
|
}
|
|
// Note, most types of converters default to toSI() type atm.
|
|
// This may change and become more specific in the future.
|
|
if (type == "actEnergy" && units != "") {
|
|
fctr = actEnergyToSI(units);
|
|
} else if (type == "toSI" && units != "") {
|
|
fctr = toSI(units);
|
|
} else if (type == "temperature" && units != "") {
|
|
fctr = toSI(units);
|
|
} else if (type == "density" && units != "") {
|
|
fctr = toSI(units);
|
|
} else if (type == "pressure" && units != "") {
|
|
fctr = toSI(units);
|
|
} else if (type != "" && units != "") {
|
|
fctr = toSI(units);
|
|
#ifdef DEBUG_MODE
|
|
writelog("\nWarning: conversion toSI() was done on node value " + node.name() +
|
|
"but wasn't explicitly requested. Type was \"" + type + "\"\n");
|
|
#endif
|
|
}
|
|
// Note, below currently produces a lot of output due to transport blocks.
|
|
// This needs to be addressed.
|
|
#ifdef DEBUG_MODE_MORE
|
|
else if (type == "" && units != "") {
|
|
writelog("\nWarning: XML node " + node.name() +
|
|
"has a units attribute, \"" + units + "\","
|
|
"but no conversion was done because the getFloat() command didn't have a type\n");
|
|
}
|
|
#endif
|
|
return fctr*x;
|
|
}
|
|
|
|
bool getOptionalFloat(const Cantera::XML_Node& parent,
|
|
const std::string& name,
|
|
doublereal& fltRtn,
|
|
const std::string& type)
|
|
{
|
|
if (parent.hasChild(name)) {
|
|
fltRtn = getFloat(parent, name, type);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
doublereal getFloatDefaultUnits(const Cantera::XML_Node& parent,
|
|
const std::string& name,
|
|
const std::string& defaultUnits,
|
|
const std::string& type)
|
|
{
|
|
doublereal fctr = 1.0;
|
|
if (defaultUnits == "") {
|
|
throw CanteraError("getFloatDefaultUnits",
|
|
"need to supply an actual value of defaultUnits");
|
|
}
|
|
if (type == "actEnergy") {
|
|
fctr = actEnergyToSI(defaultUnits);
|
|
} else if (type == "toSI") {
|
|
fctr = toSI(defaultUnits);
|
|
} else if (defaultUnits == "temperature") {
|
|
fctr = toSI(defaultUnits);
|
|
} else if (type == "density") {
|
|
fctr = toSI(defaultUnits);
|
|
} else if (type == "pressure") {
|
|
fctr = toSI(defaultUnits);
|
|
} else {
|
|
throw CanteraError("getFloatDefaultUnits",
|
|
"type of units must be supplied and understood");
|
|
}
|
|
return getFloat(parent, name, type) / fctr;
|
|
}
|
|
|
|
bool getOptionalModel(const Cantera::XML_Node& parent, const std::string& nodeName,
|
|
std::string& modelName)
|
|
{
|
|
if (parent.hasChild(nodeName)) {
|
|
modelName = parent.child(nodeName)["model"];
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int getInteger(const Cantera::XML_Node& parent, const std::string& name)
|
|
{
|
|
if (!parent.hasChild(name)) {
|
|
throw CanteraError("getInteger (called from XML Node \"" +
|
|
parent.name() + "\"): ",
|
|
"no child XML element named " + name);
|
|
}
|
|
const XML_Node& node = parent.child(name);
|
|
int x = intValue(node());
|
|
const string& vmin = node["min"];
|
|
const string& vmax = node["max"];
|
|
if (vmin != "" && x < intValue(vmin)) {
|
|
writelog("\nWarning: value "+node()+" is below lower limit of "
|
|
+vmin+".\n");
|
|
}
|
|
if (node["max"] != "" && x > intValue(vmax)) {
|
|
writelog("\nWarning: value "+node()+" is above upper limit of "
|
|
+vmax+".\n");
|
|
}
|
|
return x;
|
|
}
|
|
|
|
size_t getFloatArray(const Cantera::XML_Node& node, std::vector<doublereal> & v,
|
|
const bool convert, const std::string& unitsString,
|
|
const std::string& nodeName)
|
|
{
|
|
const Cantera::XML_Node* readNode = &node;
|
|
if (node.name() != nodeName) {
|
|
vector<Cantera::XML_Node*> ll;
|
|
node.getChildren(nodeName, ll);
|
|
if (ll.size() == 0) {
|
|
throw CanteraError("getFloatArray",
|
|
"wrong xml element type/name: was expecting "
|
|
+ nodeName + "but accessed " + node.name());
|
|
} else {
|
|
readNode = ll[0];
|
|
ll.clear();
|
|
readNode->getChildren("floatArray", ll);
|
|
if (ll.size() > 0) {
|
|
readNode = ll[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
v.clear();
|
|
doublereal vmin = Undef, vmax = Undef;
|
|
|
|
doublereal funit = 1.0;
|
|
/*
|
|
* Get the attributes field, units, from the XML node
|
|
*/
|
|
std::string units = readNode->attrib("units");
|
|
if (units != "" && convert) {
|
|
if (unitsString == "actEnergy" && units != "") {
|
|
funit = actEnergyToSI(units);
|
|
} else if (unitsString != "" && units != "") {
|
|
funit = toSI(units);
|
|
}
|
|
}
|
|
|
|
if (readNode->attrib("min") != "") {
|
|
vmin = fpValueCheck(readNode->attrib("min"));
|
|
}
|
|
if (readNode->attrib("max") != "") {
|
|
vmax = fpValueCheck(readNode->attrib("max"));
|
|
}
|
|
|
|
std::string val = readNode->value();
|
|
while (true) {
|
|
size_t icom = val.find(',');
|
|
if (icom != string::npos) {
|
|
string numstr = val.substr(0,icom);
|
|
val = val.substr(icom+1,val.size());
|
|
v.push_back(fpValueCheck(numstr));
|
|
} else {
|
|
/*
|
|
* This little bit of code is to allow for the
|
|
* possibility of a comma being the last
|
|
* item in the value text. This was allowed in
|
|
* previous versions of Cantera, even though it
|
|
* would appear to be odd. So, we keep the
|
|
* possibility in for backwards compatibility.
|
|
*/
|
|
if (!val.empty()) {
|
|
v.push_back(fpValueCheck(val));
|
|
}
|
|
break;
|
|
}
|
|
doublereal vv = v.back();
|
|
if (vmin != Undef && vv < vmin - Tiny) {
|
|
writelog("\nWarning: value "+fp2str(vv)+
|
|
" is below lower limit of " +fp2str(vmin)+".\n");
|
|
}
|
|
if (vmax != Undef && vv > vmax + Tiny) {
|
|
writelog("\nWarning: value "+fp2str(vv)+
|
|
" is above upper limit of " +fp2str(vmin)+".\n");
|
|
}
|
|
}
|
|
for (size_t n = 0; n < v.size(); n++) {
|
|
v[n] *= funit;
|
|
}
|
|
return v.size();
|
|
}
|
|
|
|
void getMap(const Cantera::XML_Node& node, std::map<std::string, std::string>& m)
|
|
{
|
|
std::vector<std::string> v;
|
|
getStringArray(node, v);
|
|
for (size_t i = 0; i < v.size(); i++) {
|
|
size_t icolon = v[i].find(":");
|
|
if (icolon == string::npos) {
|
|
throw CanteraError("getMap","missing colon in map entry ("
|
|
+v[i]+")");
|
|
}
|
|
m[v[i].substr(0,icolon)] = v[i].substr(icolon+1, v[i].size());
|
|
}
|
|
}
|
|
|
|
int getPairs(const Cantera::XML_Node& node, std::vector<std::string>& key,
|
|
std::vector<std::string>& val)
|
|
{
|
|
vector<string> v;
|
|
getStringArray(node, v);
|
|
int n = static_cast<int>(v.size());
|
|
for (int i = 0; i < n; i++) {
|
|
size_t icolon = v[i].find(":");
|
|
if (icolon == string::npos) {
|
|
throw CanteraError("getPairs","Missing a colon in the Pair entry ("
|
|
+v[i]+")");
|
|
}
|
|
key.push_back(v[i].substr(0,icolon));
|
|
val.push_back(v[i].substr(icolon+1, v[i].size()));
|
|
}
|
|
return n;
|
|
}
|
|
|
|
void getMatrixValues(const Cantera::XML_Node& node,
|
|
const std::vector<std::string>& keyStringRow,
|
|
const std::vector<std::string>& keyStringCol,
|
|
Cantera::Array2D& retnValues, const bool convert,
|
|
const bool matrixSymmetric)
|
|
{
|
|
if (keyStringRow.size() > retnValues.nRows()) {
|
|
throw CanteraError("getMatrixValues",
|
|
"size of key1 greater than numrows");
|
|
} else if (keyStringCol.size() > retnValues.nColumns()) {
|
|
throw CanteraError("getMatrixValues",
|
|
"size of key2 greater than num cols");
|
|
} else if (matrixSymmetric && retnValues.nRows() != retnValues.nColumns()) {
|
|
throw CanteraError("getMatrixValues",
|
|
"nrow != ncol for a symmetric matrix");
|
|
}
|
|
|
|
/*
|
|
* Get the attributes field, units, from the XML node
|
|
* and determine the conversion factor, funit.
|
|
*/
|
|
doublereal funit = 1.0;
|
|
if (convert && node["units"] != "") {
|
|
funit = toSI(node["units"]);
|
|
}
|
|
|
|
vector<string> v;
|
|
getStringArray(node, v);
|
|
for (size_t i = 0; i < v.size(); i++) {
|
|
size_t icolon = v[i].find(":");
|
|
if (icolon == string::npos) {
|
|
throw CanteraError("getMatrixValues","Missing two colons ("
|
|
+v[i]+")");
|
|
}
|
|
string key1 = v[i].substr(0,icolon);
|
|
string rmm = v[i].substr(icolon+1, v[i].size());
|
|
|
|
icolon = rmm.find(":");
|
|
if (icolon == string::npos) {
|
|
throw CanteraError("getMatrixValues","Missing one colon ("
|
|
+v[i]+")");
|
|
}
|
|
|
|
size_t irow = find(keyStringRow.begin(), keyStringRow.end(), key1)
|
|
- keyStringRow.begin();
|
|
if (irow == keyStringRow.size()) {
|
|
throw CanteraError("getMatrixValues","Row not matched by string: "
|
|
+ key1);
|
|
}
|
|
|
|
string key2 = rmm.substr(0,icolon);
|
|
size_t icol = find(keyStringCol.begin(), keyStringCol.end(), key2)
|
|
- keyStringCol.begin();
|
|
if (icol == keyStringCol.size()) {
|
|
throw CanteraError("getMatrixValues","Col not matched by string: "
|
|
+ key2);
|
|
}
|
|
double dval = fpValueCheck(rmm.substr(icolon+1, rmm.size())) * funit;
|
|
/*
|
|
* Finally, insert the value;
|
|
*/
|
|
retnValues(irow, icol) = dval;
|
|
if (matrixSymmetric) {
|
|
retnValues(icol, irow) = dval;
|
|
}
|
|
}
|
|
}
|
|
|
|
void getStringArray(const Cantera::XML_Node& node, std::vector<std::string>& v)
|
|
{
|
|
tokenizeString(node.value(), v);
|
|
}
|
|
|
|
}
|