Example #1
0
  void 
  VPSSMgr_ConstVol::initThermoXML(XML_Node& phaseNode, std::string id) {
    VPSSMgr::initThermoXML(phaseNode, id);
   
    XML_Node& speciesList = phaseNode.child("speciesArray");
    XML_Node* speciesDB = get_XML_NameID("speciesData", speciesList["datasrc"],
					 &phaseNode.root());
    const vector<string>&sss = m_vptp_ptr->speciesNames();

    for (int k = 0; k < m_kk; k++) {
      const XML_Node* s =  speciesDB->findByAttr("name", sss[k]);
      if (!s) {
	throw CanteraError("VPSSMgr_ConstVol::initThermoXML",
			   "no species Node for species " + sss[k]);
      }
      const XML_Node *ss = s->findByName("standardState");
      if (!ss) {
	throw CanteraError("VPSSMgr_ConstVol::initThermoXML",
			   "no standardState Node for species " + s->name());
      }
      std::string model = (*ss)["model"];
      if (model != "constant_incompressible" && model != "constantVolume") {
	throw CanteraError("VPSSMgr_ConstVol::initThermoXML",
			   "standardState model for species isn't constant_incompressible: " + s->name());
      }
      m_Vss[k] = getFloat(*ss, "molarVolume", "toSI");
    }   
  }
Example #2
0
    status_t DLL_EXPORT ctbuildsolutionfromxml(char* src, integer* ixml, char* id, 
        integer* ith, integer* ikin, ftnlen lensrc, ftnlen lenid) {

        XML_Node* root = 0;
        if (*ixml > 0) root = _xml(ixml);

        thermo_t* t = _fth(ith);
        kinetics_t* k = _fkin(ikin);

        Kinetics& kin = *k;
        XML_Node *x, *r=0;
        if (root) r = &root->root();
	std::string srcS = f2string(src, lensrc);
	std::string idS  = f2string(id, lenid);
	if (srcS != "") {
           x = get_XML_Node(srcS, r);
	} else {
           x = get_XML_Node(idS, r);
	}
        // x = find_XML(f2string(src, lensrc), r, f2string(id,lenid), "", "phase");
        if (!x) return 0;
        importPhase(*x, t);
        kin.addPhase(*t);
        kin.init();
        installReactionArrays(*x, kin, x->id());
        t->setState_TP(300.0, OneAtm);
        if (r) {
            if (&x->root() != &r->root()) delete &x->root();
        }
        else delete &x->root();
        return 0;
    }
Example #3
0
void installElements(Phase& th, const XML_Node& phaseNode)
{
    // get the declared element names
    if (!phaseNode.hasChild("elementArray")) {
        throw CanteraError("installElements",
                           "phase XML node doesn't have \"elementArray\" XML Node");
    }
    XML_Node& elements = phaseNode.child("elementArray");
    vector<string> 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* doc = get_XML_File(element_database);
    XML_Node* dbe = &doc->child("elementData");

    XML_Node& root = phaseNode.root();
    XML_Node* local_db = 0;
    if (root.hasChild("elementData")) {
        local_db = &root.child("elementData");
    }

    for (size_t i = 0; i < enames.size(); i++) {
        // Find the element data
        XML_Node* e = 0;
        if (local_db) {
            e = local_db->findByAttr("name",enames[i]);
        }
        if (!e) {
            e = dbe->findByAttr("name",enames[i]);
        }
        if (!e) {
            throw CanteraError("addElementsFromXML","no data for element "
                               +enames[i]);
        }

        // Add the element
        doublereal weight = 0.0;
        if (e->hasAttrib("atomicWt")) {
            weight = fpValue(e->attrib("atomicWt"));
        }
        int anum = 0;
        if (e->hasAttrib("atomicNumber")) {
            anum = intValue(e->attrib("atomicNumber"));
        }
        string symbol = e->attrib("name");
        doublereal entropy298 = ENTROPY298_UNKNOWN;
        if (e->hasChild("entropy298")) {
            XML_Node& e298Node = e->child("entropy298");
            if (e298Node.hasAttrib("value")) {
                entropy298 = fpValueCheck(e298Node["value"]);
            }
        }
        th.addElement(symbol, weight, anum, entropy298);
    }
}
void VPSSMgr_Water_HKFT::initThermoXML(XML_Node& phaseNode,
                                       const std::string& id)
{
    VPSSMgr::initThermoXML(phaseNode, id);
    XML_Node& speciesList = phaseNode.child("speciesArray");
    XML_Node* speciesDB = get_XML_NameID("speciesData", speciesList["datasrc"],
                                         &phaseNode.root());
    m_waterSS->setState_TP(300., OneAtm);
    m_Vss[0] = (m_waterSS->density()) / m_vptp_ptr->molecularWeight(0);

    for (size_t k = 1; k < m_kk; k++) {
        string name = m_vptp_ptr->speciesName(k);
        const XML_Node* s = speciesDB->findByAttr("name", name);
        if (!s) {
            throw CanteraError("VPSSMgr_Water_HKFT::initThermoXML",
                               "No species Node for species " + name);
        }
        const XML_Node* ss = s->findByName("standardState");
        if (!ss) {
            throw CanteraError("VPSSMgr_Water_HKFT::initThermoXML",
                               "No standardState Node for species " + name);
        }
        std::string model = lowercase(ss->attrib("model"));
        if (model != "hkft") {
            throw CanteraError("VPSSMgr_Water_HKFT::initThermoXML",
                               "Standard state model for a solute species isn't "
                               "the HKFT standard state model: " + name);
        }
    }
}
Example #5
0
void Phase::addElementsFromXML(const XML_Node& phase)
{
    // get the declared element names
    if (! phase.hasChild("elementArray")) {
        throw CanteraError("Elements::addElementsFromXML",
                           "phase xml node doesn't have \"elementArray\" XML Node");
    }
    XML_Node& elements = phase.child("elementArray");
    vector<string> enames;
    ctml::getStringArray(elements, enames);

    // // element database defaults to elements.xml
    string element_database = "elements.xml";
    if (elements.hasAttrib("datasrc")) {
        element_database = elements["datasrc"];
    }

    XML_Node* doc = get_XML_File(element_database);
    XML_Node* dbe = &doc->child("ctml/elementData");

    XML_Node& root = phase.root();
    XML_Node* local_db = 0;
    if (root.hasChild("ctml")) {
        if (root.child("ctml").hasChild("elementData")) {
            local_db = &root.child("ctml/elementData");
        }
    }

    int nel = static_cast<int>(enames.size());
    int i;
    string enm;
    XML_Node* e = 0;
    for (i = 0; i < nel; i++) {
        e = 0;
        if (local_db) {
            //writelog("looking in local database.");
            e = local_db->findByAttr("name",enames[i]);
            //if (!e) writelog(enames[i]+" not found.");
        }
        if (!e) {
            e = dbe->findByAttr("name",enames[i]);
        }
        if (e) {
            addUniqueElement(*e);
        } else {
            throw CanteraError("addElementsFromXML","no data for element "
                               +enames[i]);
        }
    }
}
Example #6
0
    int buildSolutionFromXML(char* src, int ixml, char* id,
                             int ith, int ikin)
    {
        try {
            XML_Node* root = 0;
            if (ixml > 0) {
                root = &XmlCabinet::item(ixml);
            }

            ThermoPhase& t = ThermoCabinet::item(ith);
            Kinetics& kin = KineticsCabinet::item(ikin);
            XML_Node* r = 0;
            if (root) {
                r = &root->root();
            }
            XML_Node* x = get_XML_Node(src, r);
            if (!x) {
                return false;
            }
            importPhase(*x, &t);
            kin.addPhase(t);
            kin.init();
            installReactionArrays(*x, kin, x->id());
            t.setState_TP(300.0, OneAtm);
            if (r) {
                if (&x->root() != &r->root()) {
                    delete &x->root();
                }
            } else {
                delete &x->root();
            }
            return 0;
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
    }
Example #7
0
void Phase::setXMLdata(XML_Node& xmlPhase)
{
    XML_Node* xroot = &xmlPhase.root();
    XML_Node *root_xml = new XML_Node();
    xroot->copy(root_xml);
    if (m_xml) {
       XML_Node *rOld = &m_xml->root();
       delete rOld;
       m_xml = 0;
    }
    m_xml = findXMLPhase(root_xml, xmlPhase.id());
    if (!m_xml) {
        throw CanteraError("Phase::setXMLdata()", "XML 'phase' node not found");
    }
    if (&m_xml->root() != root_xml) {
        throw CanteraError("Phase::setXMLdata()", "Root XML node not found");
    }
}
Example #8
0
    status_t ctbuildsolutionfromxml(char* src, integer* ixml, char* id,
                                    integer* ith, integer* ikin, ftnlen lensrc, ftnlen lenid)
    {
        try {
            XML_Node* root = 0;
            if (*ixml > 0) {
                root = _xml(ixml);
            }

            thermo_t* t = _fth(ith);
            Kinetics* k = _fkin(ikin);

            Kinetics& kin = *k;
            XML_Node* x, *r=0;
            if (root) {
                r = &root->root();
            }
            std::string srcS = f2string(src, lensrc);
            std::string idS  = f2string(id, lenid);
            if (srcS != "") {
                x = get_XML_Node(srcS, r);
            } else {
                x = get_XML_Node(idS, r);
            }
            if (!x) {
                return 0;
            }
            importPhase(*x, t);
            kin.addPhase(*t);
            kin.init();
            installReactionArrays(*x, kin, x->id());
            t->setState_TP(300.0, OneAtm);
            if (r) {
                if (&x->root() != &r->root()) {
                    delete &x->root();
                }
            } else {
                delete &x->root();
            }
        } catch (...) {
            return handleAllExceptions(-1, ERR);
        }
        return 0;
    }
void
VPSSMgr_Water_ConstVol::initThermoXML(XML_Node& phaseNode, const std::string& id)
{
    VPSSMgr::initThermoXML(phaseNode, id);

    XML_Node& speciesList = phaseNode.child("speciesArray");
    XML_Node* speciesDB = get_XML_NameID("speciesData", speciesList["datasrc"],
                                         &phaseNode.root());
    const vector<string>&sss = m_vptp_ptr->speciesNames();


    if (!m_waterSS) {
        throw CanteraError("VPSSMgr_Water_ConstVol::initThermoXML",
                           "bad dynamic cast");
    }

    m_waterSS->setState_TP(300., OneAtm);
    m_Vss[0] = (m_waterSS->density())      / m_vptp_ptr->molecularWeight(0);

    for (size_t k = 1; k < m_kk; k++) {
        const XML_Node* s =  speciesDB->findByAttr("name", sss[k]);
        if (!s) {
            throw CanteraError("VPSSMgr_Water_ConstVol::initThermoXML",
                               "no species Node for species " + sss[k]);
        }
        const XML_Node* ss = s->findByName("standardState");
        if (!ss) {
            std::string sName = s->operator[]("name");
            throw CanteraError("VPSSMgr_Water_ConstVol::initThermoXML",
                               "no standardState Node for species " + sName);
        }
        std::string model = (*ss)["model"];
        if (model != "constant_incompressible") {
            std::string sName = s->operator[]("name");
            throw CanteraError("VPSSMgr_Water_ConstVol::initThermoXML",
                               "standardState model for species isn't "
                               "constant_incompressible: " + sName);
        }
        m_Vss[k] = ctml::getFloat(*ss, "molarVolume", "toSI");
    }
}
void LatticePhase::initThermoXML(XML_Node& phaseNode, const std::string& id_)
{
    if (!id_.empty() && id_ != phaseNode.id()) {
        throw CanteraError("LatticePhase::initThermoXML",
                           "ids don't match");
    }

    // Check on the thermo field. Must have:
    // <thermo model="Lattice" />
    if (phaseNode.hasChild("thermo")) {
        XML_Node& thNode = phaseNode.child("thermo");
        std::string mString = thNode.attrib("model");
        if (lowercase(mString) != "lattice") {
            throw CanteraError("LatticePhase::initThermoXML",
                               "Unknown thermo model: " + mString);
        }
    } else {
        throw CanteraError("LatticePhase::initThermoXML",
                           "Unspecified thermo model");
    }

    // Now go get the molar volumes. use the default if not found
    XML_Node& speciesList = phaseNode.child("speciesArray");
    XML_Node* speciesDB = get_XML_NameID("speciesData", speciesList["datasrc"], &phaseNode.root());

    for (size_t k = 0; k < m_kk; k++) {
        m_speciesMolarVolume[k] = m_site_density;
        XML_Node* s = speciesDB->findByAttr("name", speciesName(k));
        if (!s) {
            throw CanteraError(" LatticePhase::initThermoXML", "database problems");
        }
        XML_Node* ss = s->findByName("standardState");
        if (ss && ss->findByName("molarVolume")) {
            m_speciesMolarVolume[k] = getFloat(*ss, "molarVolume", "toSI");
        }
    }

    // Call the base initThermo, which handles setting the initial state.
    ThermoPhase::initThermoXML(phaseNode, id_);
}
Example #11
0
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);
}
Example #12
0
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;
}
void IdealSolidSolnPhase::initThermoXML(XML_Node& phaseNode, const std::string& id_)
{
    if (id_.size() > 0 && phaseNode.id() != id_) {
        throw CanteraError("IdealSolidSolnPhase::initThermoXML",
                           "phasenode and Id are incompatible");
    }

    /*
     * Check on the thermo field. Must have:
     * <thermo model="IdealSolidSolution" />
     */
    if (phaseNode.hasChild("thermo")) {
        XML_Node& thNode = phaseNode.child("thermo");
        string mString = thNode.attrib("model");
        if (lowercase(mString) != "idealsolidsolution") {
            throw CanteraError("IdealSolidSolnPhase::initThermoXML",
                               "Unknown thermo model: " + mString);
        }
    } else {
        throw CanteraError("IdealSolidSolnPhase::initThermoXML",
                           "Unspecified thermo model");
    }

    /*
     * Form of the standard concentrations. Must have one of:
     *
     *     <standardConc model="unity" />
     *     <standardConc model="molar_volume" />
     *     <standardConc model="solvent_volume" />
     */
    if (phaseNode.hasChild("standardConc")) {
        XML_Node& scNode = phaseNode.child("standardConc");
        string formStringa = scNode.attrib("model");
        string formString = lowercase(formStringa);
        if (formString == "unity") {
            m_formGC = 0;
        } else if (formString == "molar_volume") {
            m_formGC = 1;
        } else if (formString == "solvent_volume") {
            m_formGC = 2;
        } else {
            throw CanteraError("IdealSolidSolnPhase::initThermoXML",
                               "Unknown standardConc model: " + formStringa);
        }
    } else {
        throw CanteraError("IdealSolidSolnPhase::initThermoXML",
                           "Unspecified standardConc model");
    }

    /*
     * Initialize all of the lengths now that we know how many species
     * there are in the phase.
     */
    initLengths();
    /*
     * Now go get the molar volumes
     */
    XML_Node& speciesList = phaseNode.child("speciesArray");
    XML_Node* speciesDB = get_XML_NameID("speciesData", speciesList["datasrc"],
                                         &phaseNode.root());

    for (size_t k = 0; k < m_kk; k++) {
        XML_Node* s = speciesDB->findByAttr("name", speciesName(k));
        XML_Node* ss = s->findByName("standardState");
        m_speciesMolarVolume[k] = getFloat(*ss, "molarVolume", "toSI");
    }

    /*
     * Call the base initThermo, which handles setting the initial
     * state.
     */
    ThermoPhase::initThermoXML(phaseNode, id_);
}