void Domain1D::restore(const XML_Node& dom, doublereal* soln, int loglevel) { vector_fp values; vector<XML_Node*> nodes; dom.getChildren("floatArray", nodes); for (size_t i = 0; i < nodes.size(); i++) { string title = nodes[i]->attrib("title"); getFloatArray(*nodes[i], values, false); if (values.size() != nComponents()) { throw CanteraError("Domain1D::restore", "Got an array of length " + int2str(values.size()) + " when one of length " + int2str(nComponents()) + "was expected."); } if (title == "abstol_transient") { m_atol_ts = values; } else if (title == "reltol_transient") { m_rtol_ts = values; } else if (title == "abstol_steady") { m_atol_ss = values; } else if (title == "reltol_steady") { m_rtol_ss = values; } else { throw CanteraError("Domain1D::restore", "Got an unexpected array, '" + title + "'"); } } }
void getIntegers(const XML_Node& node, std::map<std::string, int>& v) { std::vector<XML_Node*> f = node.getChildren("integer"); for (size_t i = 0; i < f.size(); i++) { const XML_Node& fi = *f[i]; if (fi["min"] != "" && fi["max"] != "") { v[fi["title"]] = fi.int_value(); } } }
void Sim1D::restore(const std::string& fname, const std::string& id, int loglevel) { ifstream s(fname.c_str()); if (!s) throw CanteraError("Sim1D::restore", "could not open input file "+fname); XML_Node root; root.build(s); s.close(); XML_Node* f = root.findID(id); if (!f) { throw CanteraError("Sim1D::restore","No solution with id = "+id); } vector<XML_Node*> xd; f->getChildren("domain", xd); if (xd.size() != m_nd) { throw CanteraError("Sim1D::restore", "Solution does not contain the " " correct number of domains. Found " + int2str(xd.size()) + "expected " + int2str(m_nd) + ".\n"); } size_t sz = 0; for (size_t m = 0; m < m_nd; m++) { if (loglevel > 0 && xd[m]->attrib("id") != domain(m).id()) { writelog("Warning: domain names do not match: '" + (*xd[m])["id"] + + "' and '" + domain(m).id() + "'\n"); } sz += domain(m).nComponents() * intValue((*xd[m])["points"]); } m_x.resize(sz); m_xnew.resize(sz); for (size_t m = 0; m < m_nd; m++) { domain(m).restore(*xd[m], DATA_PTR(m_x) + domain(m).loc(), loglevel); } resize(); finalize(); }
void Domain1D::restore(const XML_Node& dom, doublereal* soln, int loglevel) { vector_fp values; vector<XML_Node*> nodes; dom.getChildren("floatArray", nodes); for (size_t i = 0; i < nodes.size(); i++) { string title = nodes[i]->attrib("title"); getFloatArray(*nodes[i], values, false); if (values.size() != nComponents()) { if (loglevel > 0) { writelog("Warning: Domain1D::restore: Got an array of length " + int2str(values.size()) + " when one of length " + int2str(nComponents()) + " was expected. " + "Tolerances for individual species may not be preserved.\n"); } // The number of components will differ when restoring from a // mechanism with a different number of species. Assuming that // tolerances are the same for all species, we can just copy the // tolerance from the last species. if (!values.empty()) { values.resize(nComponents(), values[values.size()-1]); } else { // If the tolerance vector is empty, just leave the defaults // in place. continue; } } if (title == "abstol_transient") { m_atol_ts = values; } else if (title == "reltol_transient") { m_rtol_ts = values; } else if (title == "abstol_steady") { m_atol_ss = values; } else if (title == "reltol_steady") { m_rtol_ss = values; } else { throw CanteraError("Domain1D::restore", "Got an unexpected array, '" + title + "'"); } } }
void importPhase(XML_Node& phase, ThermoPhase* th) { // Check the the supplied XML node in fact represents a phase. if (phase.name() != "phase") { throw CanteraError("importPhase", "Current const XML_Node named, " + phase.name() + ", is not a phase element."); } // In this section of code, we get the reference to the phase XML tree // within the ThermoPhase object. Then, we clear it and fill it with the // current information that we are about to use to construct the object. We // will then be able to resurrect the information later by calling xml(). th->setXMLdata(phase); // set the id attribute of the phase to the 'id' attribute in the XML tree. th->setID(phase.id()); th->setName(phase.id()); // Number of spatial dimensions. Defaults to 3 (bulk phase) if (phase.hasAttrib("dim")) { int idim = intValue(phase["dim"]); if (idim < 1 || idim > 3) { throw CanteraError("importPhase", "phase, " + th->id() + ", has unphysical number of dimensions: " + phase["dim"]); } th->setNDim(idim); } else { th->setNDim(3); // default } // Set equation of state parameters. The parameters are specific to each // subclass of ThermoPhase, so this is done by method setParametersFromXML // in each subclass. const XML_Node& eos = phase.child("thermo"); if (phase.hasChild("thermo")) { th->setParametersFromXML(eos); } else { throw CanteraError("importPhase", " phase, " + th->id() + ", XML_Node does not have a \"thermo\" XML_Node"); } VPStandardStateTP* vpss_ptr = 0; int ssConvention = th->standardStateConvention(); if (ssConvention == cSS_CONVENTION_VPSS) { vpss_ptr = dynamic_cast <VPStandardStateTP*>(th); if (vpss_ptr == 0) { throw CanteraError("importPhase", "phase, " + th->id() + ", was VPSS, but dynamic cast failed"); } } // Add the elements. if (ssConvention != cSS_CONVENTION_SLAVE) { installElements(*th, phase); } // Add the species. // // Species definitions may be imported from multiple sources. For each one, // a speciesArray element must be present. vector<XML_Node*> sparrays = phase.getChildren("speciesArray"); if (ssConvention != cSS_CONVENTION_SLAVE && sparrays.empty()) { throw CanteraError("importPhase", "phase, " + th->id() + ", has zero \"speciesArray\" XML nodes.\n" + " There must be at least one speciesArray nodes " "with one or more species"); } vector<XML_Node*> dbases; vector_int sprule(sparrays.size(),0); // Default behavior when importing from CTI/XML is for undefined elements to // be treated as an error th->throwUndefinedElements(); // loop over the speciesArray elements for (size_t jsp = 0; jsp < sparrays.size(); jsp++) { const XML_Node& speciesArray = *sparrays[jsp]; // If the speciesArray element has a child element // // <skip element="undeclared"> // // then set sprule[jsp] to 1, so that any species with an undeclared // element will be quietly skipped when importing species. Additionally, // if the skip node has the following attribute: // // <skip species="duplicate"> // // then duplicate species names will not cause Cantera to throw an // exception. Instead, the duplicate entry will be discarded. if (speciesArray.hasChild("skip")) { const XML_Node& sk = speciesArray.child("skip"); string eskip = sk["element"]; if (eskip == "undeclared") { sprule[jsp] = 1; } string dskip = sk["species"]; if (dskip == "duplicate") { sprule[jsp] += 10; } } // Get a pointer to the node containing the species definitions for the // species declared in this speciesArray element. This may be in the // local file containing the phase element, or may be in another file. XML_Node* db = get_XML_Node(speciesArray["datasrc"], &phase.root()); if (db == 0) { throw CanteraError("importPhase()", " Can not find XML node for species database: " + speciesArray["datasrc"]); } // add this node to the list of species database nodes. dbases.push_back(db); } // Now, collect all the species names and all the XML_Node * pointers for // those species in a single vector. This is where we decide what species // are to be included in the phase. The logic is complicated enough that we // put it in a separate routine. std::vector<XML_Node*> spDataNodeList; std::vector<std::string> spNamesList; vector_int spRuleList; formSpeciesXMLNodeList(spDataNodeList, spNamesList, spRuleList, sparrays, dbases, sprule); size_t nsp = spDataNodeList.size(); if (ssConvention == cSS_CONVENTION_SLAVE && nsp > 0) { throw CanteraError("importPhase()", "For Slave standard states, " "number of species must be zero: {}", nsp); } for (size_t k = 0; k < nsp; k++) { XML_Node* s = spDataNodeList[k]; AssertTrace(s != 0); if (spRuleList[k]) { th->ignoreUndefinedElements(); } th->addSpecies(newSpecies(*s)); if (vpss_ptr) { const XML_Node* const ss = s->findByName("standardState"); std::string ss_model = (ss) ? ss->attrib("model") : "ideal-gas"; unique_ptr<PDSS> kPDSS(newPDSS(ss_model)); kPDSS->setParametersFromXML(*s); vpss_ptr->installPDSS(k, std::move(kPDSS)); } th->saveSpeciesData(k, s); } // Done adding species. Perform any required subclass-specific // initialization. th->initThermo(); // Perform any required subclass-specific initialization that requires the // XML phase object std::string id = ""; th->initThermoXML(phase, id); }
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(); }
bool importPhase(XML_Node& phase, ThermoPhase* th, SpeciesThermoFactory* spfactory) { // Check the the supplied XML node in fact represents a phase. if (phase.name() != "phase") { throw CanteraError("importPhase", "Current const XML_Node named, " + phase.name() + ", is not a phase element."); } /* * In this section of code, we get the reference to the * phase xml tree within the ThermoPhase object. Then, * we clear it and fill it with the current information that * we are about to use to construct the object. We will then * be able to resurrect the information later by calling xml(). */ th->setXMLdata(phase); // set the id attribute of the phase to the 'id' attribute in the XML tree. th->setID(phase.id()); th->setName(phase.id()); // Number of spatial dimensions. Defaults to 3 (bulk phase) if (phase.hasAttrib("dim")) { int idim = intValue(phase["dim"]); if (idim < 1 || idim > 3) throw CanteraError("importPhase", "phase, " + th->id() + ", has unphysical number of dimensions: " + phase["dim"]); th->setNDim(idim); } else { th->setNDim(3); // default } // Set equation of state parameters. The parameters are // specific to each subclass of ThermoPhase, so this is done // by method setParametersFromXML in each subclass. const XML_Node& eos = phase.child("thermo"); if (phase.hasChild("thermo")) { th->setParametersFromXML(eos); } else { throw CanteraError("importPhase", " phase, " + th->id() + ", XML_Node does not have a \"thermo\" XML_Node"); } VPStandardStateTP* vpss_ptr = 0; int ssConvention = th->standardStateConvention(); if (ssConvention == cSS_CONVENTION_VPSS) { vpss_ptr = dynamic_cast <VPStandardStateTP*>(th); if (vpss_ptr == 0) { throw CanteraError("importPhase", "phase, " + th->id() + ", was VPSS, but dynamic cast failed"); } } // if no species thermo factory was supplied, use the default one. if (!spfactory) { spfactory = SpeciesThermoFactory::factory(); } /*************************************************************** * Add the elements. ***************************************************************/ if (ssConvention != cSS_CONVENTION_SLAVE) { installElements(*th, phase); } /*************************************************************** * Add the species. * * Species definitions may be imported from multiple * sources. For each one, a speciesArray element must be * present. ***************************************************************/ vector<XML_Node*> sparrays; phase.getChildren("speciesArray", sparrays); if (ssConvention != cSS_CONVENTION_SLAVE) { if (sparrays.empty()) { throw CanteraError("importPhase", "phase, " + th->id() + ", has zero \"speciesArray\" XML nodes.\n" + " There must be at least one speciesArray nodes " "with one or more species"); } } vector<XML_Node*> dbases; vector_int sprule(sparrays.size(),0); // loop over the speciesArray elements for (size_t jsp = 0; jsp < sparrays.size(); jsp++) { const XML_Node& speciesArray = *sparrays[jsp]; // If the speciesArray element has a child element // // <skip element="undeclared"> // // then set sprule[jsp] to 1, so that any species with an undeclared // element will be quietly skipped when importing species. Additionally, // if the skip node has the following attribute: // // <skip species="duplicate"> // // then duplicate species names will not cause Cantera to throw an // exception. Instead, the duplicate entry will be discarded. if (speciesArray.hasChild("skip")) { const XML_Node& sk = speciesArray.child("skip"); string eskip = sk["element"]; if (eskip == "undeclared") { sprule[jsp] = 1; } string dskip = sk["species"]; if (dskip == "duplicate") { sprule[jsp] += 10; } } // Get a pointer to the node containing the species // definitions for the species declared in this // speciesArray element. This may be in the local file // containing the phase element, or may be in another // file. XML_Node* db = get_XML_Node(speciesArray["datasrc"], &phase.root()); if (db == 0) { throw CanteraError("importPhase()", " Can not find XML node for species database: " + speciesArray["datasrc"]); } // add this node to the list of species database nodes. dbases.push_back(db); } // Now, collect all the species names and all the XML_Node * pointers // for those species in a single vector. This is where we decide what // species are to be included in the phase. // The logic is complicated enough that we put it in a separate routine. std::vector<XML_Node*> spDataNodeList; std::vector<std::string> spNamesList; std::vector<int> spRuleList; formSpeciesXMLNodeList(spDataNodeList, spNamesList, spRuleList, sparrays, dbases, sprule); // Decide whether the the phase has a variable pressure ss or not SpeciesThermo* spth = 0; VPSSMgr* vp_spth = 0; if (ssConvention == cSS_CONVENTION_TEMPERATURE) { // Create a new species thermo manager. Function // 'newSpeciesThermoMgr' looks at the species in the database // to see what thermodynamic property parameterizations are // used, and selects a class that can handle the // parameterizations found. spth = newSpeciesThermoMgr(spDataNodeList); // install it in the phase object th->setSpeciesThermo(spth); } else if (ssConvention == cSS_CONVENTION_SLAVE) { /* * No species thermo manager for this type */ } else if (ssConvention == cSS_CONVENTION_VPSS) { vp_spth = newVPSSMgr(vpss_ptr, &phase, spDataNodeList); vpss_ptr->setVPSSMgr(vp_spth); spth = vp_spth->SpeciesThermoMgr(); th->setSpeciesThermo(spth); } else { throw CanteraError("importPhase()", "unknown convention"); } size_t k = 0; size_t nsp = spDataNodeList.size(); if (ssConvention == cSS_CONVENTION_SLAVE) { if (nsp > 0) { throw CanteraError("importPhase()", "For Slave standard states, number of species must be zero: " + int2str(nsp)); } } for (size_t i = 0; i < nsp; i++) { XML_Node* s = spDataNodeList[i]; AssertTrace(s != 0); bool ok = installSpecies(k, *s, *th, spth, spRuleList[i], &phase, vp_spth, spfactory); if (ok) { th->saveSpeciesData(k, s); ++k; } } if (ssConvention == cSS_CONVENTION_SLAVE) { th->installSlavePhases(&phase); } // Done adding species. Perform any required subclass-specific // initialization. th->initThermo(); // Perform any required subclass-specific initialization // that requires the XML phase object std::string id = ""; th->initThermoXML(phase, id); return true; }
bool installReactionArrays(const XML_Node& p, Kinetics& kin, std::string default_phase, bool check_for_duplicates) { int itot = 0; // Search the children of the phase element for the XML element named // reactionArray. If we can't find it, then return signaling having not // found any reactions. Apparently, we allow multiple reactionArray elements // here Each one will be processed sequentially, with the end result being // purely additive. vector<XML_Node*> rarrays = p.getChildren("reactionArray"); if (rarrays.empty()) { return false; } for (size_t n = 0; n < rarrays.size(); n++) { // Go get a reference to the current XML element, reactionArray. We will // process this element now. const XML_Node& rxns = *rarrays[n]; // The reactionArray element has an attribute called, datasrc. The value // of the attribute is the XML element comprising the top of the tree of // reactions for the phase. Find this datasrc element starting with the // root of the current XML node. const XML_Node* rdata = get_XML_Node(rxns["datasrc"], &rxns.root()); // If the reactionArray element has a child element named "skip", and if // the attribute of skip called "species" has a value of "undeclared", // we will set rxnrule.skipUndeclaredSpecies to 'true'. rxnrule is // passed to the routine that parses each individual reaction so that // the parser will skip all reactions containing an undefined species // without throwing an error. // // Similarly, an attribute named "third_bodies" with the value of // "undeclared" will skip undeclared third body efficiencies (while // retaining the reaction and any other efficiencies). if (rxns.hasChild("skip")) { const XML_Node& sk = rxns.child("skip"); if (sk["species"] == "undeclared") { kin.skipUndeclaredSpecies(true); } if (sk["third_bodies"] == "undeclared") { kin.skipUndeclaredThirdBodies(true); } } // Search for child elements called include. We only include a reaction // if it's tagged by one of the include fields. Or, we include all // reactions if there are no include fields. vector<XML_Node*> incl = rxns.getChildren("include"); vector<XML_Node*> allrxns = rdata->getChildren("reaction"); // if no 'include' directive, then include all reactions if (incl.empty()) { for (size_t i = 0; i < allrxns.size(); i++) { checkElectrochemReaction(p,kin,*allrxns[i]); kin.addReaction(newReaction(*allrxns[i])); ++itot; } } else { for (size_t nii = 0; nii < incl.size(); nii++) { const XML_Node& ii = *incl[nii]; string imin = ii["min"]; string imax = ii["max"]; string::size_type iwild = string::npos; if (imax == imin) { iwild = imin.find("*"); if (iwild != string::npos) { imin = imin.substr(0,iwild); imax = imin; } } for (size_t i = 0; i < allrxns.size(); i++) { const XML_Node* r = allrxns[i]; string rxid; if (r) { rxid = r->attrib("id"); if (iwild != string::npos) { rxid = rxid.substr(0,iwild); } // To decide whether the reaction is included or not we // do a lexical min max and operation. This sometimes // has surprising results. if ((rxid >= imin) && (rxid <= imax)) { checkElectrochemReaction(p,kin,*r); kin.addReaction(newReaction(*r)); ++itot; } } } } } } if (check_for_duplicates) { kin.checkDuplicates(); } return true; }