SpeciesThermoInterpType* newSpeciesThermoInterpType(const XML_Node& thermo)
{
    // Get the children of the thermo XML node. In the next bit of code we take out the comments that
    // may have been children of the thermo XML node by doing a selective copy.
    // These shouldn't interfere with the algorithm at any point.
    const std::vector<XML_Node*>& tpWC = thermo.children();
    std::vector<XML_Node*> tp;
    for (size_t i = 0; i < tpWC.size(); i++) {
        if (!(tpWC[i])->isComment()) {
            tp.push_back(tpWC[i]);
        }
    }

    std::string thermoType = lowercase(tp[0]->name());

    for (size_t i = 1; i < tp.size(); i++) {
        if (lowercase(tp[i]->name()) != thermoType) {
            throw CanteraError("newSpeciesThermoInterpType",
                "Encountered unsupported mixed species thermo parameterizations");
        }
    }
    if ((tp.size() > 2 && thermoType != "nasa9") ||
        (tp.size() > 1 && (thermoType == "const_cp" ||
                           thermoType == "mu0" ||
                           thermoType == "adsorbate"))) {
        throw CanteraError("newSpeciesThermoInterpType",
            "Too many regions in thermo parameterization.");
    }

    std::string model = lowercase(thermo["model"]);
    if (model == "mineraleq3") {
        if (thermoType != "mineq3") {
            throw CanteraError("SpeciesThermoFactory::installThermoForSpecies",
                               "confused: expected MinEQ3");
        }
        return newShomateForMineralEQ3(*tp[0]);
    } else if (thermoType == "shomate") {
        return newShomateThermoFromXML(tp);
    } else if (thermoType == "const_cp") {
        return newConstCpThermoFromXML(*tp[0]);
    } else if (thermoType == "nasa") {
        return newNasaThermoFromXML(tp);
    } else if (thermoType == "mu0") {
        return newMu0ThermoFromXML(*tp[0]);
    } else if (thermoType == "nasa9") {
        return newNasa9ThermoFromXML(tp);
    } else if (thermoType == "adsorbate") {
        return newAdsorbateThermoFromXML(*tp[0]);
    } else if (thermoType == "statmech") {
        return newStatMechThermoFromXML(*tp[0]);
    } else if (model == "hkft" || model == "ionfromneutral") {
        // Some PDSS species use the 'thermo' node, but don't specify a
        // SpeciesThermoInterpType parameterization. This function needs to just
        // ignore this data.
        return 0;
    } else {
        throw CanteraError("newSpeciesThermoInterpType",
            "Unknown species thermo model '" + thermoType + "'.");
    }
}
SpeciesThermoInterpType* newSpeciesThermoInterpType(const XML_Node& speciesNode)
{
    /*
     * Check to see that the species block has a thermo block
     * before processing. Throw an error if not there.
     */
    if (!(speciesNode.hasChild("thermo"))) {
        throw UnknownSpeciesThermoModel("installThermoForSpecies",
                                        speciesNode["name"], "<nonexistent>");
    }
    const XML_Node& thermo = speciesNode.child("thermo");

    // Get the children of the thermo XML node. In the next bit of code we take out the comments that
    // may have been children of the thermo XML node by doing a selective copy.
    // These shouldn't interfere with the algorithm at any point.
    const std::vector<XML_Node*>& tpWC = thermo.children();
    std::vector<XML_Node*> tp;
    for (size_t i = 0; i < tpWC.size(); i++) {
        if (!(tpWC[i])->isComment()) {
            tp.push_back(tpWC[i]);
        }
    }

    std::string thermoType = lowercase(tp[0]->name());
    std::string specName = speciesNode["name"];

    for (size_t i = 1; i < tp.size(); i++) {
        if (lowercase(tp[i]->name()) != thermoType) {
            throw CanteraError("newSpeciesThermoInterpType",
                "Encounter unsupported mixed species thermo parameterizations "
                "for species '" + specName + "'.");
        }
    }
    if ((tp.size() > 2 && thermoType != "nasa9") ||
        (tp.size() > 1 && (thermoType == "const_cp" ||
                           thermoType == "mu0" ||
                           thermoType == "adsorbate"))) {
        throw CanteraError("newSpeciesThermoInterpType",
            "Too many regions in thermo parameterization for species '" +
            specName + "'.");
    }

    if (thermo["model"] == "MineralEQ3") {
        if (thermoType != "mineq3") {
            throw CanteraError("SpeciesThermoFactory::installThermoForSpecies",
                               "confused: expedted MinEQ3");
        }
        return newShomateForMineralEQ3(specName, *tp[0]);
    } else if (thermoType == "shomate") {
        return newShomateThermoFromXML(specName, tp);
    } else if (thermoType == "const_cp") {
        return newConstCpThermoFromXML(specName, *tp[0]);
    } else if (thermoType == "nasa") {
        return newNasaThermoFromXML(specName, tp);
    } else if (thermoType == "mu0") {
        return newMu0ThermoFromXML(specName, *tp[0]);
    } else if (thermoType == "nasa9") {
        return newNasa9ThermoFromXML(specName, tp);
    } else if (thermoType == "adsorbate") {
        return newAdsorbateThermoFromXML(specName, *tp[0]);
    } else if (thermoType == "statmech") {
        return newStatMechThermoFromXML(specName, *tp[0]);
    } else {
        throw UnknownSpeciesThermoModel("installThermoForSpecies",
                                        specName, thermoType);
    }
}