/* * Returns a double value for the child named 'name' of element 'parent'. If * 'type' is supplied and matches a known unit type, unit * conversion to SI will be done if the child element has an attribute * 'units'. * * Note, it's an error for the child element not to exist. * * Example: * * Code snipet: * @verbatum const XML_Node &State_XMLNode; doublereal pres = OneAtm; if (state_XMLNode.hasChild("pressure")) { pres = getFloat(State_XMLNode, "pressure", "toSI"); } @endverbatum * * reads the corresponding XML file: * @verbatum <state> <pressure units="Pa"> 101325.0 </pressure> <\state> @endverbatum * * @param parent reference to the XML_Node object of the parent XML element * @param name Name of the XML child element * @param type String type. Currently known types are "toSI" and "actEnergy", * and "" , for no conversion. The default value is "" * which implies that no conversion is allowed. */ 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); doublereal x, x0, x1, fctr = 1.0; string units, vmin, vmax; x = atof(node().c_str()); x0 = Undef; x1 = Undef; units = node["units"]; vmin = node["min"]; vmax = node["max"]; if (vmin != "") { x0 = atof(vmin.c_str()); if (x < x0 - Tiny) { writelog("\nWarning: value "+node()+" is below lower limit of " +vmin+".\n"); } } if (node["max"] != "") { x1 = atof(vmax.c_str()); if (x > x1 + Tiny) { writelog("\nWarning: value "+node()+" is above upper limit of " +vmax+".\n"); } } // Note, most type's 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 explicity 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; }
/* * Returns a doublereal value for the child named 'name' of element 'parent'. If * 'type' is supplied and matches a known unit type, unit * conversion to SI will be done if the child element has an attribute * 'units'. * * * * Example: * * Code snipet: * @verbatim const XML_Node &State_XMLNode; doublereal pres = OneAtm; bool exists = getOptionalFloat(State_XMLNode, "pressure", pres, "toSI"); @endverbatim * * reads the corresponding XML file: * @verbatim <state> <pressure units="Pa"> 101325.0 </pressure> <\state> @endverbatim * * @param parent reference to the XML_Node object of the parent XML element * @param name Name of the XML child element * @param fltRtn Float Return. It will be overridden if the XML * element exists. * @param type String type. Currently known types are "toSI" * and "actEnergy", * and "" , for no conversion. The default value is "", * which implies that no conversion is allowed. * * @return returns true if the child element named "name" exists */ doublereal getFloatDefaultUnits(const Cantera::XML_Node& parent, std::string name, std::string defaultUnits, 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"); } doublereal val = getFloat(parent, name, type); val /= fctr; return val; }
void MineralEQ3::initThermoXML(XML_Node& phaseNode, const std::string& id_) { /* * Find the Thermo XML node */ if (!phaseNode.hasChild("thermo")) { throw CanteraError("HMWSoln::initThermoXML", "no thermo XML node"); } std::vector<const XML_Node*> xspecies = speciesData(); const XML_Node* xsp = xspecies[0]; XML_Node* aStandardState = 0; if (xsp->hasChild("standardState")) { aStandardState = &xsp->child("standardState"); } else { throw CanteraError("MineralEQ3::initThermoXML", "no standard state mode"); } doublereal volVal = 0.0; string smodel = (*aStandardState)["model"]; if (smodel != "constantVolume") { throw CanteraError("MineralEQ3::initThermoXML", "wrong standard state mode"); } if (aStandardState->hasChild("V0_Pr_Tr")) { XML_Node& aV = aStandardState->child("V0_Pr_Tr"); string Aunits = ""; double Afactor = toSI("cm3/gmol"); if (aV.hasAttrib("units")) { Aunits = aV.attrib("units"); Afactor = toSI(Aunits); } volVal = ctml::getFloat(*aStandardState, "V0_Pr_Tr"); m_V0_pr_tr= volVal; volVal *= Afactor; m_speciesSize[0] = volVal; } else { throw CanteraError("MineralEQ3::initThermoXML", "wrong standard state mode"); } doublereal rho = molecularWeight(0) / volVal; setDensity(rho); const XML_Node& sThermo = xsp->child("thermo"); const XML_Node& MinEQ3node = sThermo.child("MinEQ3"); m_deltaG_formation_pr_tr = ctml::getFloatDefaultUnits(MinEQ3node, "DG0_f_Pr_Tr", "cal/gmol", "actEnergy"); m_deltaH_formation_pr_tr = ctml::getFloatDefaultUnits(MinEQ3node, "DH0_f_Pr_Tr", "cal/gmol", "actEnergy"); m_Entrop_pr_tr = ctml::getFloatDefaultUnits(MinEQ3node, "S0_Pr_Tr", "cal/gmol/K"); m_a = ctml::getFloatDefaultUnits(MinEQ3node, "a", "cal/gmol/K"); m_b = ctml::getFloatDefaultUnits(MinEQ3node, "b", "cal/gmol/K2"); m_c = ctml::getFloatDefaultUnits(MinEQ3node, "c", "cal-K/gmol"); convertDGFormation(); }
void MineralEQ3::convertDGFormation() { // Ok let's get the element compositions and conversion factors. doublereal totalSum = 0.0; for (size_t m = 0; m < nElements(); m++) { double na = nAtoms(0, m); if (na > 0.0) { totalSum += na * LookupGe(elementName(m)); } } // Ok, now do the calculation. Convert to joules kmol-1 doublereal dg = m_deltaG_formation_pr_tr * toSI("cal/gmol"); //! Store the result into an internal variable. m_Mu0_pr_tr = dg + totalSum; double Hcalc = m_Mu0_pr_tr + 298.15 * m_Entrop_pr_tr * toSI("cal/gmol"); double DHjmol = m_deltaH_formation_pr_tr * toSI("kal/gmol"); // If the discrepancy is greater than 100 cal gmol-1, print an error if (fabs(Hcalc -DHjmol) > 100 * toSI("cal/gmol")) { throw CanteraError("installMinEQ3asShomateThermoFromXML()", "DHjmol is not consistent with G and S: {} vs {}", Hcalc, DHjmol); } }
void BtLineEdit::lineChanged(Unit::unitDisplay oldUnit, Unit::unitScale oldScale) { // This is where it gets hard double val = -1.0; QString amt; bool force = Brewtarget::hasUnits(text()); bool ok = false; bool wasChanged = sender() == this; // editingFinished happens on focus being lost, regardless of anything // being changed. I am hoping this short circuits properly and we do // nothing if nothing changed. if ( sender() == this && ! isModified() ) { return; } if (text().isEmpty()) { return; } // The idea here is we need to first translate the field into a known // amount (aka to SI) and then into the unit we want. switch( _type ) { case Unit::Mass: case Unit::Volume: case Unit::Temp: case Unit::Time: case Unit::Density: val = toSI(oldUnit,oldScale,force); amt = displayAmount(val,3); break; case Unit::Color: val = toSI(oldUnit,oldScale,force); amt = displayAmount(val,0); break; case Unit::String: amt = text(); break; case Unit::None: default: val = Brewtarget::toDouble(text(),&ok); if ( ! ok ) Brewtarget::logW( QString("%1: failed to convert %2 (%3:%4) to double").arg(Q_FUNC_INFO).arg(text()).arg(_section).arg(_editField) ); amt = displayAmount(val); } QLineEdit::setText(amt); if ( wasChanged ) { emit textModified(); } }
/* * Returns a std::map containing a keyed values for child XML_Nodes * of the current node with the name, "float". * In the keyed mapping there will be a list of titles vs. values * for all of the XML nodes. * The float XML_nodes are expected to be in a particular form created * by the function addFloat(). One value per XML_node is expected. * * * Example: * * Code snipet: * @verbatum const XML_Node &State_XMLNode; std::map<std::string,double> v; bool convert = true; getFloats(State_XMLNode, v, convert); @endverbatum * * reads the corresponding XML file: * * @verbatum <state> <float title="a1" units="m3"> 32.4 <\float> <float title="a2" units="cm3"> 1. <\float> <float title="a3"> 100. <\float> <\state> @endverbatum * * Will produce the mapping: * * v["a1"] = 32.4 * v["a2"] = 1.0E-6 * v["a3"] = 100. * * * @param node Current XML node to get the values from * @param v Output map of the results. * @param convert Turn on conversion to SI units */ void getFloats(const Cantera::XML_Node& node, std::map<std::string, double>& v, const bool convert) { std::vector<XML_Node*> f; node.getChildren("float",f); int n = static_cast<int>(f.size()); doublereal x, x0, x1, fctr; std::string typ, title, units, vmin, vmax; for (int i = 0; i < n; i++) { const XML_Node& fi = *(f[i]); x = atof(fi().c_str()); x0 = Undef; x1 = Undef; typ = fi["type"]; title = fi["title"]; units = fi["units"]; vmin = fi["min"]; vmax = fi["max"]; if (vmin != "") { x0 = atof(vmin.c_str()); if (x < x0 - Tiny) { writelog("\nWarning: value "+fi()+" is below lower limit of " +vmin+".\n"); } } if (fi["max"] != "") { x1 = atof(vmax.c_str()); if (x > x1 + Tiny) { writelog("\nWarning: value "+fi()+" is above upper limit of " +vmax+".\n"); } } fctr = (convert ? toSI(units) : 1.0); // toSI(typ,units); v[title] = fctr*x; } }
doublereal getFloatCurrent(const XML_Node& node, const std::string& type) { doublereal fctr = 1.0; doublereal x = node.fp_value(); 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.value()+" is below lower limit of " +vmin+".\n"); } if (node["max"] != "" && x > fpValue(vmax) + Tiny) { writelog("\nWarning: value "+node.value()+" 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; }
TEST_F(ConstructFromScratch, RedlichKwongMFTP) { RedlichKwongMFTP p; p.addSpecies(sCO2); p.addSpecies(sH2O); p.addSpecies(sH2); double fa = toSI("bar-cm6/mol2"); double fb = toSI("cm3/mol"); p.setBinaryCoeffs("H2", "H2O", 4 * fa, 40 * fa); p.setSpeciesCoeffs("CO2", 7.54e7 * fa, -4.13e4 * fa, 27.80 * fb); p.setBinaryCoeffs("CO2", "H2O", 7.897e7 * fa, 0.0); p.setSpeciesCoeffs("H2O", 1.7458e8 * fa, -8e4 * fa, 18.18 * fb); p.setSpeciesCoeffs("H2", 30e7 * fa, -330e4 * fa, 31 * fb); p.initThermo(); p.setMoleFractionsByName("CO2:0.9998, H2O:0.0002"); p.setState_TP(300, 200 * OneAtm); EXPECT_NEAR(p.pressure(), 200 * OneAtm, 1e-5); // Arbitrary regression test values EXPECT_NEAR(p.density(), 892.421, 2e-3); EXPECT_NEAR(p.enthalpy_mole(), -404848642.3797, 1e-3); }
/* * This is similar to atof(). However, the second token * is interpreted as an MKS units string and a conversion * factor to MKS is applied. * * Example * " 1.0 atm" * * results in the number 1.01325e5 * * @param strSI string to be converted. One or two tokens * * @return returns a converted double */ doublereal strSItoDbl(const std::string& strSI) { std::vector<std::string> v; tokenizeString(strSI, v); doublereal fp = 1.0; int n = v.size(); if (n > 2 || n < 1) { throw CanteraError("strSItoDbl", "number of tokens is too high"); } else if (n == 2) { fp = toSI(v[1]); } doublereal val = atofCheck(v[0].c_str()); return (val * fp); }
void MineralEQ3::initThermoXML(XML_Node& phaseNode, const std::string& id_) { // Find the Thermo XML node if (!phaseNode.hasChild("thermo")) { throw CanteraError("HMWSoln::initThermoXML", "no thermo XML node"); } const XML_Node* xsp = speciesData()[0]; XML_Node* aStandardState = 0; if (xsp->hasChild("standardState")) { aStandardState = &xsp->child("standardState"); } else { throw CanteraError("MineralEQ3::initThermoXML", "no standard state mode"); } doublereal volVal = 0.0; if (aStandardState->attrib("model") != "constantVolume") { throw CanteraError("MineralEQ3::initThermoXML", "wrong standard state mode"); } if (aStandardState->hasChild("V0_Pr_Tr")) { XML_Node& aV = aStandardState->child("V0_Pr_Tr"); double Afactor = toSI("cm3/gmol"); if (aV.hasAttrib("units")) { Afactor = toSI(aV.attrib("units")); } volVal = getFloat(*aStandardState, "V0_Pr_Tr"); m_V0_pr_tr= volVal; volVal *= Afactor; } else { throw CanteraError("MineralEQ3::initThermoXML", "wrong standard state mode"); } setDensity(molecularWeight(0) / volVal); const XML_Node& MinEQ3node = xsp->child("thermo").child("MinEQ3"); m_deltaG_formation_pr_tr = getFloat(MinEQ3node, "DG0_f_Pr_Tr", "actEnergy") / actEnergyToSI("cal/gmol"); m_deltaH_formation_pr_tr = getFloat(MinEQ3node, "DH0_f_Pr_Tr", "actEnergy") / actEnergyToSI("cal/gmol"); m_Entrop_pr_tr = getFloat(MinEQ3node, "S0_Pr_Tr", "toSI") / toSI("cal/gmol/K"); m_a = getFloat(MinEQ3node, "a", "toSI") / toSI("cal/gmol/K"); m_b = getFloat(MinEQ3node, "b", "toSI") / toSI("cal/gmol/K2"); m_c = getFloat(MinEQ3node, "c", "toSI") / toSI("cal-K/gmol"); convertDGFormation(); }
/* * This function will read a child node to the current XML node, with the * name "floatArray". It will have a title attribute, and the body * of the XML node will be filled out with a comma separated list of * doublereals. * Get an array of floats from the XML Node. The argument field * is assumed to consist of an arbitrary number of comma * separated floats, with an arbitrary amount of white space * separating each field. * If the node array has an units attribute field, then * the units are used to convert the floats, iff convert is true. * * Example: * * Code snipet: * @verbatum const XML_Node &State_XMLNode; vector_fp v; bool convert = true; unitsString = ""; nodeName="floatArray"; getFloatArray(State_XMLNode, v, convert, unitsString, nodeName); @endverbatum * * reads the corresponding XML file: * * @verbatum <state> <floatArray units="m3"> 32.4, 1, 100. <\floatArray> <\state> @endverbatum * * Will produce the vector * * v[0] = 32.4 * v[1] = 1.0 * v[2] = 100. * * * @param node XML parent node of the floatArray * @param v Output vector of floats containing the floatArray information. * @param convert Conversion to SI is carried out if this boolean is * True. The default is true. * @param typeString String name of the type attribute. This is an optional * parameter. The default is to have an empty string. * The only string that is recognized is actEnergy. * Anything else has no effect. This affects what * units converter is used. * @param nodeName XML Name of the XML node to read. * The default value for the node name is floatArray */ void getFloatArray(const Cantera::XML_Node& node, vector_fp& v, const bool convert, const std::string unitsString, const std::string nodeName) { string::size_type icom; string numstr; doublereal dtmp; string nn = node.name(); if (nn != nodeName) throw CanteraError("getFloatArray", "wrong xml element type/name: was expecting " + nodeName + "but accessed " + node.name()); v.clear(); doublereal vmin = Undef, vmax = Undef; doublereal funit = 1.0; /* * Get the attributes field, units, from the XML node */ std::string units = node["units"]; if (units != "" && convert) { if (unitsString == "actEnergy" && units != "") { funit = actEnergyToSI(units); } else if (unitsString != "" && units != "") { funit = toSI(units); } } if (node["min"] != "") vmin = atofCheck(node["min"].c_str()); if (node["max"] != "") vmax = atofCheck(node["max"].c_str()); doublereal vv; std::string val = node.value(); while (1 > 0) { icom = val.find(','); if (icom != string::npos) { numstr = val.substr(0,icom); val = val.substr(icom+1,val.size()); dtmp = atofCheck(numstr.c_str()); v.push_back(dtmp); } 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 * possibilty in for backwards compatibility. */ int nlen = strlen(val.c_str()); if (nlen > 0) { dtmp = atofCheck(val.c_str()); v.push_back(dtmp); } break; } 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"); } } int nv = v.size(); for (int n = 0; n < nv; n++) { v[n] *= funit; } }
/* * Each pair consists of nonwhite-space characters. * The first two ":" found in the pair string is used to separate * the string into three parts. The first part is called the first * key. The second part is the second key. Both parts must match * an entry in the keyString1 and keyString2, respectively, * in order to provide a location to * place the object in the matrix. * The third part is called the value. It is expected to be * a double. It is translated into a double and placed into the * correct location in the matrix. * * Warning: No spaces are allowed in each triplet. Quotes are part * of the string. * Example * keyString = red, blue, black, green * <xmlNode> * red:green:112 * blue:black:3.3E-23 * * </xmlNode> * * Returns: * retnValues(0, 3) = 112 * retnValues(1, 2) = 3.3E-23 * * * @param node XML Node containing the information for the matrix * @param keyStringRow Key string for the row * @param keyStringCol Key string for the column entries * @param returnValues Return Matrix. * @param convert If this is true, and if the node has a units * attribute, then conversion to si units is carried * out. Default is true. * @param matrixSymmetric If true entries are made so that the matrix * is always symmetric. Default is false. */ 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) { int szKey1 = keyStringRow.size(); int szKey2 = keyStringCol.size(); int nrow = retnValues.nRows(); int ncol = retnValues.nColumns(); if (szKey1 > nrow) { throw CanteraError("getMatrixValues", "size of key1 greater than numrows"); } if (szKey2 > ncol) { throw CanteraError("getMatrixValues", "size of key2 greater than num cols"); } if (matrixSymmetric) { if (nrow != ncol) { 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; string units = node["units"]; if (units != "" && convert) { funit = toSI(units); } string key1; string key2; string rmm; string val; vector<string> v; getStringArray(node, v); int icol, irow; int n = static_cast<int>(v.size()); string::size_type icolon; for (int i = 0; i < n; i++) { icolon = v[i].find(":"); if (icolon == string::npos) { throw CanteraError("getMatrixValues","Missing two colons (" +v[i]+")"); } key1 = v[i].substr(0,icolon); rmm = v[i].substr(icolon+1, v[i].size()); icolon = rmm.find(":"); if (icolon == string::npos) { throw CanteraError("getMatrixValues","Missing one colon (" +v[i]+")"); } key2 = rmm.substr(0,icolon); val = rmm.substr(icolon+1, rmm.size()); icol = -1; irow = -1; for (int j = 0; j < szKey1; j++) { if (key1 == keyStringRow[j]) { irow = j; break; } } if (irow == -1) { throw CanteraError("getMatrixValues","Row not matched by string: " + key1); } for (int j = 0; j < szKey2; j++) { if (key2 == keyStringCol[j]) { icol = j; break; } } if (icol == -1) { throw CanteraError("getMatrixValues","Col not matched by string: " + key2); } double dval = atofCheck(val.c_str()); dval *= funit; /* * Finally, insert the value; */ retnValues(irow, icol) = dval; if (matrixSymmetric) { retnValues(icol, irow) = dval; } } }
void BtLineEdit::lineChanged(Unit::unitDisplay oldUnit, Unit::unitScale oldScale) { // This is where it gets hard double val = -1.0; QString amt; bool force = false; bool ok = false; // editingFinished happens on focus being lost, regardless of anything // being changed. I am hoping this short circuits properly and we do // nothing if nothing changed. if ( sender() == this && ! isModified() ) { return; } // If we are here because somebody else sent the signal (ie, a label) or we // generated the signal but nothing has changed then don't try to guess the // units. if ( sender() != this ) { force = true; } if ( _section.isEmpty() ) initializeSection(); if ( _property.isEmpty() ) initializeProperties(); if (text().isEmpty()) { return; } // The idea here is we need to first translate the field into a known // amount (aka to SI) and then into the unit we want. switch( _type ) { case MASS: case VOLUME: case TEMPERATURE: case TIME: val = toSI(oldUnit,oldScale,force); amt = displayAmount(val,3); break; case DENSITY: case COLOR: val = toSI(oldUnit,oldScale,force); amt = displayAmount(val,0); break; case STRING: amt = text(); break; case GENERIC: default: val = Brewtarget::toDouble(text(),&ok); if ( ! ok ) Brewtarget::logW( QString("BtLineEdit::lineChanged: failed to convert %1 toDouble").arg(text()) ); amt = displayAmount(val); } QLineEdit::setText(amt); if ( ! force ) { emit textModified(); } }
void 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) { 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; } } }
size_t getFloatArray(const XML_Node& node, std::vector<doublereal> & v, const bool convert, const std::string& unitsString, const std::string& nodeName) { const XML_Node* readNode = &node; if (node.name() != nodeName) { vector<XML_Node*> ll = node.getChildren(nodeName); if (ll.size() == 0) { throw CanteraError("getFloatArray", "wrong XML element type/name: was expecting " + nodeName + "but accessed " + node.name()); } else { readNode = ll[0]; ll = readNode->getChildren("floatArray"); 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(); }
TEST(HMWSoln, fromScratch_HKFT) { HMWSoln p; auto sH2O = make_species("H2O(l)", "H:2, O:1", h2oliq_nasa_coeffs); auto sNa = make_species("Na+", "Na:1, E:-1", 0.0, 298.15, -125.5213, 333.15, -125.5213, 1e5); sNa->charge = 1; auto sCl = make_species("Cl-", "Cl:1, E:1", 0.0, 298.15, -52.8716, 333.15, -52.8716, 1e5); sCl->charge = -1; auto sH = make_species("H+", "H:1, E:-1", 0.0, 298.15, 0.0, 333.15, 0.0, 1e5); sH->charge = 1; auto sOH = make_species("OH-", "O:1, H:1, E:1", 0.0, 298.15, -91.523, 333.15, -91.523, 1e5); sOH->charge = -1; for (auto& s : {sH2O, sNa, sCl, sH, sOH}) { p.addSpecies(s); } double h0[] = {-57433, Undef, 0.0, -54977}; double g0[] = {Undef, -31379, 0.0, -37595}; double s0[] = {13.96, 13.56, Undef, -2.56}; double a[][4] = {{0.1839, -228.5, 3.256, -27260}, {0.4032, 480.1, 5.563, -28470}, {0.0, 0.0, 0.0, 0.0}, {0.12527, 7.38, 1.8423, -27821}}; double c[][2] = {{18.18, -29810}, {-4.4, -57140}, {0.0, 0.0}, {4.15, -103460}}; double omega[] = {33060, 145600, 0.0, 172460}; std::unique_ptr<PDSS_Water> ss(new PDSS_Water()); p.installPDSS(0, std::move(ss)); for (size_t k = 0; k < 4; k++) { std::unique_ptr<PDSS_HKFT> ss(new PDSS_HKFT()); if (h0[k] != Undef) { ss->setDeltaH0(h0[k] * toSI("cal/gmol")); } if (g0[k] != Undef) { ss->setDeltaG0(g0[k] * toSI("cal/gmol")); } if (s0[k] != Undef) { ss->setS0(s0[k] * toSI("cal/gmol/K")); } a[k][0] *= toSI("cal/gmol/bar"); a[k][1] *= toSI("cal/gmol"); a[k][2] *= toSI("cal-K/gmol/bar"); a[k][3] *= toSI("cal-K/gmol"); c[k][0] *= toSI("cal/gmol/K"); c[k][1] *= toSI("cal-K/gmol"); ss->set_a(a[k]); ss->set_c(c[k]); ss->setOmega(omega[k] * toSI("cal/gmol")); p.installPDSS(k+1, std::move(ss)); } p.setPitzerTempModel("complex"); p.setA_Debye(-1); p.initThermo(); set_hmw_interactions(p); p.setMolalitiesByName("Na+:6.0954 Cl-:6.0954 H+:2.1628E-9 OH-:1.3977E-6"); p.setState_TP(50 + 273.15, 101325); size_t N = p.nSpecies(); vector_fp mv(N), h(N), mu(N), ac(N), acoeff(N); p.getPartialMolarVolumes(mv.data()); p.getPartialMolarEnthalpies(h.data()); p.getChemPotentials(mu.data()); p.getActivities(ac.data()); p.getActivityCoefficients(acoeff.data()); double mvRef[] = {0.01815224, 0.00157182, 0.01954605, 0.00173137, -0.0020266}; for (size_t k = 0; k < N; k++) { EXPECT_NEAR(mv[k], mvRef[k], 2e-8); } }