/* * @file importCTML.cpp * * $Author$ * $Revision$ * $Date$ */ // Copyright 2002 California Institute of Technology #ifdef WIN32 #pragma warning(disable:4786) #pragma warning(disable:4503) #endif #include "importCTML.h" #include "mix_defs.h" #include // STL includes #include #include #include using namespace std; // Cantera includes #include "speciesThermoTypes.h" #include "ThermoPhase.h" #include "ThermoFactory.h" #include "SpeciesThermoFactory.h" #include "KineticsFactory.h" #include "reaction_defs.h" #include "ReactionData.h" #include "global.h" #include "stringUtils.h" #include "xml.h" #include "ctml.h" using namespace ctml; #include namespace Cantera { typedef vector nodeset_t; typedef XML_Node node_t; static int intValue(string val) { return atoi(stripws(val).c_str()); } static doublereal fpValue(string val) { return atof(stripws(val).c_str()); } /// Number of reactant molecules static int nReacMolecules(ReactionData& r) { return accumulate(r.rstoich.begin(), r.rstoich.end(), 0); } const doublereal DefaultPref = 1.01325e5; // one atm /** * Get an XML tree from a file. If successful, a pointer to the * document root is returned. If not, a null pointer is returned. */ XML_Node* get_XML(string file) { string inname = findInputFile(file); if (inname == "") return 0; ifstream fin(inname.c_str()); XML_Node* rootPtr = new XML_Node; try { rootPtr->build(fin); } catch (...) { return 0; } fin.close(); return rootPtr; } /** * Install a NASA polynomial thermodynamic property * parameterization for species k. */ void installNasaThermo(SpeciesThermo& sp, int k, XML_Node& f) { doublereal tmin, tmid, tmax; tmin = fpValue(f["Tmin"]); tmid = fpValue(f["Tmid"]); tmax = fpValue(f["Tmax"]); vector fa; f.getChildren("floatArray",fa); vector_fp c0, c1; getFloatArray(*fa[0], c0, false); getFloatArray(*fa[1], c1, false); array_fp c(15); c[0] = tmid; doublereal p0 = OneAtm; if ((*fa[0])["title"] == "low") { c[1] = c0[5]; c[2] = c0[6]; copy(c0.begin(), c0.begin()+5, c.begin() + 3); c[8] = c1[5]; c[9] = c1[6]; copy(c1.begin(), c1.begin()+5, c.begin() + 10); } else { c[1] = c1[5]; c[2] = c1[6]; copy(c1.begin(), c1.begin()+5, c.begin() + 3); c[8] = c0[5]; c[9] = c0[6]; copy(c0.begin(), c0.begin()+5, c.begin() + 10); } sp.install(k, NASA, c.begin(), tmin, tmax, p0); } /** * Install a Shomate polynomial thermodynamic property * parameterization for species k. */ void installShomateThermo(SpeciesThermo& sp, int k, XML_Node& f) { doublereal tmin, tmid, tmax; tmin = fpValue(f["Tmin"]); tmid = fpValue(f["Tmid"]); tmax = fpValue(f["Tmax"]); vector fa; f.getChildren("floatArray",fa); vector_fp c0, c1; getFloatArray(*fa[0], c0, false); getFloatArray(*fa[1], c1, false); array_fp c(15); c[0] = tmid; doublereal p0 = OneAtm; if ((*fa[0])["title"] == "low") { copy(c0.begin(), c0.end(), c.begin() + 1); copy(c1.begin(), c1.end(), c.begin() + 8); } else { copy(c1.begin(), c1.end(), c.begin() + 1); copy(c0.begin(), c0.end(), c.begin() + 8); } sp.install(k, SHOMATE, c.begin(), tmin, tmax, p0); } /** * Install a Shomate polynomial thermodynamic property * parameterization for species k. */ void installSimpleThermo(SpeciesThermo& sp, int k, XML_Node& f) { doublereal tmin, tmax; tmin = fpValue(f["Tmin"]); tmax = fpValue(f["Tmax"]); if (tmax == 0.0) tmax = 1.0e30; //map fp; //getFloats(f, fp); vector_fp c(4); c[0] = getFloat(f, "t0", "-"); c[1] = getFloat(f, "h0", "-"); c[2] = getFloat(f, "s0", "-"); c[3] = getFloat(f, "cp0", "-"); doublereal p0 = OneAtm; sp.install(k, SIMPLE, c.begin(), tmin, tmax, p0); } bool installSpecies(int k, XML_Node& s, thermo_t& p, SpeciesThermo& spthermo, int rule) { // get the composition of the species XML_Node& a = s.child("atomArray"); map comp; getMap(a, comp); // check that all elements in the species // exist in 'p' map::const_iterator _b = comp.begin(); for (; _b != comp.end(); ++_b) { if (p.elementIndex(_b->first) < 0) { if (rule == 0) throw CanteraError("installSpecies", "species " + s["name"] + " contains undeclared element " + _b->first); else return false; } } int m, nel = p.nElements(); vector_fp ecomp(nel, 0.0); for (m = 0; m < nel; m++) { ecomp[m] = atoi(comp[p.elementName(m)].c_str()); } /* * Define a map and get all of the floats in the * current XML species block */ map fd; getFloats(s, fd); /* * Set a default for the size parameter to one */ if (fd["size"] == 0.0) fd["size"] = 1.0; p.addUniqueSpecies(s["name"], ecomp.begin(), fd["charge"], fd["size"]); // get thermo XML_Node& thermo = s.child("thermo"); vector tp = thermo.children(); int nc = tp.size(); if (nc == 1) { XML_Node& f = *tp[0]; if (f.name() == "NASA") { installNasaThermo(spthermo, k, f); } else if (f.name() == "Shomate") { installShomateThermo(spthermo, k, f); } else if (f.name() == "const_cp") { installSimpleThermo(spthermo, k, f); } else throw CanteraError("importCTML", "Unsupported species thermo parameterization" " for species "+s["name"]); } else throw CanteraError("importCTML", "Multiple thermo parameterizations given for " "species "+s["name"]); return true; } /** * Get the reactants or products of a reaction. */ bool getReagents(XML_Node& rxn, kinetics_t& kin, int rp, string default_phase, vector_int& spnum, vector_int& stoich, vector_fp& order, int rule) { string rptype; if (rp == 1) rptype = "reactants"; else rptype = "products"; XML_Node& rg = rxn.child(rptype); vector key, val; getPairs(rg, key, val); int ns = key.size(); int stch, isp; doublereal ord; string ph, sp; for (int n = 0; n < ns; n++) { sp = key[n]; ph = ""; //snode["phase"]; //if (ph == "") ph = default_phase; isp = kin.kineticsSpeciesIndex(sp,""); if (isp < 0) { if (rule == 1) return false; else { throw CanteraError("getReagents", "undeclared reactant or product species "+sp); return false; } } spnum.push_back(isp); stch = atoi(val[n].c_str()); stoich.push_back(stch); ord = doublereal(stch); order.push_back(ord); } return true; } void getArrhenius(XML_Node& node, int& order, doublereal& A, doublereal& b, doublereal& E) { // get rxn order to do unit conversion for pre-exponential order = intValue(node["order"]); //nodeset_t c = node.children(); vector abe; getStringArray(node, abe); A = fpValue(abe[0]); b = fpValue(abe[1]); E = fpValue(abe[2]); string u = (*node.parent())["units"]; string eu = (*node.parent())["Eunits"]; doublereal cmult = 1.0; if (u != "") { if (u == "mol,cm,s") cmult = 1.0e-6 / CtMoles_per_mole; else if (u == "molec,cm,s") cmult = 1.0e-6*Avogadro; else throw CanteraError("getArrhenius","unknown units for A"); } A *= pow(cmult, order - 1); doublereal gasConstant = 1.0; if (eu != "") { if (eu == "cal/mol") gasConstant = 1.987; else if (eu == "kcal/mol") gasConstant = 1.987e-3; else if (eu == "J/mol") gasConstant = 8.314; else if (eu == "kJ/mol") gasConstant = 8.314e-3; else if (eu == "K") gasConstant = 1.0; else if (eu == "eV") gasConstant = 1.0/11600.0; else throw CanteraError("getArrhenius", "unknown units for activation energy: "+eu); } E /= gasConstant; } /** * Get falloff parameters for a reaction. */ void getFalloff(node_t& f, ReactionData& rdata) { string type = f["type"]; vector p; getStringArray(f,p); vector_fp c; int np = p.size(); for (int n = 0; n < np; n++) { c.push_back(fpValue(p[n])); } if (type == "Troe") { if (np == 4) rdata.falloffType = TROE4_FALLOFF; else rdata.falloffType = TROE3_FALLOFF; } else if (type == "SRI") { if (np == 5) rdata.falloffType = SRI5_FALLOFF; else rdata.falloffType = SRI3_FALLOFF; } rdata.falloffParameters = c; } /** * Get the enhanced collision efficiencies. It is assumed that the * reaction mechanism is homogeneous, so that all species belong * to phase(0) of 'kin'. */ void getEfficiencies(node_t& eff, kinetics_t& kin, ReactionData& rdata) { // set the default collision efficiency rdata.default_3b_eff = fpValue(eff["default"]); vector key, val; getPairs(eff, key, val); int ne = key.size(); //map e; //getFloats(eff, e, false); //map::const_iterator bb = e.begin(), ee = e.end(); string nm; string phse = kin.thermo(0).id(); int n, k; for (n = 0; n < ne; n++) { // ; bb != ee; ++bb) { nm = key[n];// bb->first; k = kin.kineticsSpeciesIndex(nm, phse); rdata.thirdBodyEfficiencies[k] = fpValue(val[n]); // bb->second; } } /** * Get the rate coefficient for a reaction. */ void getRateCoefficient(node_t& kf, kinetics_t& kin, ReactionData& rdata) { int nc = kf.nChildren(); const nodeset_t& kf_children = kf.children(); vector_fp clow(3,0.0), chigh(3,0.0); int nr = nReacMolecules(rdata); for (int m = 0; m < nc; m++) { node_t& c = *kf_children[m]; string nm = c.name(); int order=0; if (nm == "Arrhenius") { vector_fp coeff(3); getArrhenius(c, order, coeff[0], coeff[1], coeff[2]); if (order == 0) order = nr; if (order == nr || rdata.reactionType == THREE_BODY_RXN) chigh = coeff; else if (order == nr + 1) clow = coeff; else { cerr << "\n\n\n" << endl; kf.write(cerr); throw CanteraError("importCTML", "wrong Arrhenius coeff order"); } } else if (nm == "falloff") { getFalloff(c, rdata); } else if (nm == "efficiencies") { getEfficiencies(c, kin, rdata); } } if (rdata.reactionType == CHEMACT_RXN) rdata.rateCoeffParameters = clow; else rdata.rateCoeffParameters = chigh; if (rdata.reactionType == FALLOFF_RXN) rdata.auxRateCoeffParameters = clow; else if (rdata.reactionType == CHEMACT_RXN) rdata.auxRateCoeffParameters = chigh; } /** * Create a new ThermoPhase object. */ ThermoPhase* newPhase(XML_Node& xmlphase) { XML_Node& th = xmlphase.child("thermo"); string model = th["model"]; ThermoPhase* t = newThermoPhase(model); importPhase(xmlphase, t); return t; } /** * Set the thermodynamic state. */ static void setState(XML_Node& phase, ThermoPhase* th) { if (!phase.hasChild("state")) return; XML_Node state = phase.child("state"); doublereal t, p, rho; string comp = getString(state,"moleFractions"); if (comp != "") th->setMoleFractionsByName(comp); else { comp = getString(state,"massFractions"); if (comp != "") th->setMassFractionsByName(comp); } t = getFloat(state, "temperature", "temperature"); if (t > 0.0) th->setTemperature(t); p = getFloat(state, "pressure", "pressure"); if (p > 0.0) th->setPressure(p); rho = getFloat(state, "density", "density"); if (rho > 0.0) th->setDensity(rho); } /** * Import a phase specification. */ bool importPhase(XML_Node& phase, ThermoPhase* th) { if (phase.name() != "phase") throw CanteraError("importPhase", "Current XML_Node is not a phase element."); th->setID(phase.id()); // set the 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", "unphysical number of dimensions: "+phase["dim"]); th->setNDim(idim); } else th->setNDim(3); // default // equation of state if (phase.hasChild("thermo")) { XML_Node& eos = phase.child("thermo"); if (eos["model"] == "Incompressible") { if (th->eosType() == cIncompressible) { map d; getFloats(eos, d); doublereal rho = d["density"]; th->setParameters(1, &rho); } else { throw CanteraError("importCTML", "wrong equation of state type"); } } else if (eos["model"] == "Surface") { if (th->eosType() == cSurf) { map d; //getFloats(eos, d); doublereal n = fpValue(eos("site_density")); th->setParameters(1, &n); } else { throw CanteraError("importCTML", "wrong equation of state type"); } } } /************************************************* * Add the elements. ************************************************/ // get the declared element names XML_Node& elements = phase.child("elementArray"); vector enames; getStringArray(elements, enames); // // element database defaults to elements.xml string element_database; // = "elements.xml"; if (elements.hasAttrib("datasrc")) element_database = elements["datasrc"]; XML_Node* db = find_XML(element_database,&phase.root(),"","", "elementData"); int nel = enames.size(); int i; string enm; for (i = 0; i < nel; i++) { XML_Node* e = db->findByAttr("name",enames[i]); if (e) { th->addUniqueElement(*e); } else { throw CanteraError("importPhase","no data for element " +enames[i]); } } delete db; db = 0; /*************************************************************** * Add the species. First get the speciesArray element, then * the species database. ***************************************************************/ XML_Node& species = phase.child("speciesArray"); int sprule = 0; if (species.hasChild("skip")) { XML_Node& sk = species.child("skip"); string eskip = sk["element"]; if (eskip == "undeclared") { sprule = 1; } } db = find_XML(species["datasrc"], &phase.root(), species["idRef"], "","speciesData"); /******************************************************* * Set the 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. ******************************************************/ delete &th->speciesThermo(); SpeciesThermo* spth = newSpeciesThermoMgr(db); th->setSpeciesThermo(spth); SpeciesThermo& spthermo = th->speciesThermo(); /* * Get the array of species name strings. */ vector spnames; getStringArray(species, spnames); int nsp = spnames.size(); map declared; string name; int k = 0; for (i = 0; i < nsp; i++) { name = spnames[i]; // Check that every species is only declared once if (declared[name]) { throw CanteraError("importPhase", "duplicate species: "+name); } declared[name] = true; /* * Find the species in the database by name. */ XML_Node* s = db->findByAttr("name",spnames[i]); if (s) { if (installSpecies(k, *s, *th, spthermo, sprule)) ++k; } else { throw CanteraError("importPhase","no data for species " +name); } } th->freezeSpecies(); th->initThermo(); setState(phase, th); th->saveSpeciesData(db); return true; } bool installReaction(int i, XML_Node& r, Kinetics* k, string default_phase, int rule) { Kinetics& kin = *k; ReactionData rdata; rdata.reactionType = ELEMENTARY_RXN; vector_int reac, prod; string eqn, type; int nn, eqlen; vector_fp dummy; eqn = r("equation"); eqlen = eqn.size(); for (nn = 0; nn < eqlen; nn++) { if (eqn[nn] == '[') eqn[nn] = '<'; if (eqn[nn] == ']') eqn[nn] = '>'; } bool ok; // get the reactants ok = getReagents(r, kin, 1, default_phase, rdata.reactants, rdata.rstoich, rdata.order, rule); // get the products ok = ok && getReagents(r, kin, -1, default_phase, rdata.products, rdata.pstoich, dummy, rule); if (!ok) { cout << "skipping " << eqn << endl; return false; } rdata.equation = eqn; rdata.reversible = false; rdata.number = i; rdata.rxn_number = i; string typ = r["type"]; if (typ == "falloff") { rdata.reactionType = FALLOFF_RXN; rdata.falloffType = SIMPLE_FALLOFF; } else if (typ == "chemAct") { rdata.reactionType = CHEMACT_RXN; rdata.falloffType = SIMPLE_FALLOFF; } else if (typ == "threeBody") { rdata.reactionType = THREE_BODY_RXN; } else if (typ != "") throw CanteraError("installReaction", "Unknown reaction type: " + typ); string isrev = r["reversible"]; if (isrev == "yes" || isrev == "true") rdata.reversible = true; getRateCoefficient(r.child("rateCoeff"), kin, rdata); kin.addReaction(rdata); return true; } bool installReactionArrays(XML_Node& p, Kinetics& kin, string default_phase) { vector rarrays; int itot = 0; p.getChildren("reactionArray",rarrays); int na = rarrays.size(); if (na == 0) return false; for (int n = 0; n < na; n++) { XML_Node& rxns = *rarrays[n]; XML_Node* rdata = find_XML(rxns["datasrc"],&rxns.root(), "","","reactionData"); int rxnrule = 0; if (rxns.hasChild("skip")) { XML_Node& sk = rxns.child("skip"); string sskip = sk["species"]; if (sskip == "undeclared") { rxnrule = 1; } } vector incl; rxns.getChildren("include",incl); int ninc = incl.size(); for (int nii = 0; nii < ninc; nii++) { int nrxns = 0; XML_Node& ii = *incl[nii]; vector rxn_ids; string pref = ii["prefix"]; int imin = atoi(ii["min"].c_str()); int imax = atoi(ii["max"].c_str()); if (imin != 0 && imax != 0) { nrxns = imax - imin + 1; for (int nn=0; nnfindID(rxn_ids[i],1); if (r) { if (installReaction(itot, *r, &kin, default_phase, rxnrule)) ++itot; } } } } kin.finalize(); return true; } /** * Import a reaction mechanism for a phase or an interface. */ bool importKinetics(XML_Node& phase, vector th, Kinetics* k) { Kinetics& kin = *k; // This phase will be the default one string default_phase = phase["id"]; // if other phases are involved in the reaction mechanism, // they must be listed in a 'phaseArray' child // element. Homogeneous mechanisms do not need to include a // phaseArray element. vector phase_ids; if (phase.hasChild("phaseArray")) { XML_Node& pa = phase.child("phaseArray"); getStringArray(pa, phase_ids); } phase_ids.push_back(default_phase); int np = phase_ids.size(); int nt = th.size(); // for each referenced phase, attempt to find its id among those // phases specified. bool phase_ok; string phase_id; for (int n = 0; n < np; n++) { phase_id = phase_ids[n]; phase_ok = false; // loop over the supplied 'ThermoPhase' objects representing // phases, to find an object with the same id. for (int m = 0; m < nt; m++) { if (th[m]->id() == phase_id) { phase_ok = true; // if no phase with this id has been added to //the kinetics manager yet, then add this one if (kin.phaseIndex(phase_id) < 0) { kin.addPhase(*th[m]); } } } if (!phase_ok) { throw CanteraError("importKinetics", "phase "+phase_id+" not found."); } } // allocates arrays, etc. Must be called after the phases have // been added to 'kin', so that the number of species in each // phase is known. kin.init(); // Install the reactions. return installReactionArrays(phase, kin, default_phase); } /** * Build a single-phase solution. */ bool buildSolutionFromXML(XML_Node& root, string id, string nm, ThermoPhase* th, Kinetics* k) { XML_Node* x; x = find_XML("", &root, id, "", nm); if (!x) return false; importPhase(*x, th); vector phases(1); phases[0] = th; importKinetics(*x, phases, k); return true; } }