[Thermo] Enable creation of ThermoPhase objects from YAML file names

This commit is contained in:
Ray Speth 2019-01-24 15:23:08 -05:00
parent 580b75b2d2
commit 48a32ae8bf
3 changed files with 96 additions and 93 deletions

View file

@ -108,16 +108,42 @@ unique_ptr<ThermoPhase> newPhase(AnyMap& phaseNode, const AnyMap& rootNode)
ThermoPhase* newPhase(const std::string& infile, std::string id)
{
XML_Node* root = get_XML_File(infile);
size_t dot = infile.find_last_of(".");
string extension;
if (dot != npos) {
extension = toLowerCopy(infile.substr(dot+1));
}
if (id == "-") {
id = "";
}
XML_Node* xphase = get_XML_NameID("phase", "#"+id, root);
if (!xphase) {
throw CanteraError("newPhase",
"Couldn't find phase named \"" + id + "\" in file, " + infile);
if (extension == "yml" || extension == "yaml") {
AnyMap root = AnyMap::fromYamlFile(infile);
if (id != "") {
auto phases = root["phases"].asMap("name");
if (phases.find(id) == phases.end()) {
throw CanteraError("newPhase",
"Couldn't find phase named '{}' in file '{}'.", id, infile);
}
unique_ptr<ThermoPhase> t(newThermoPhase(phases[id]->at("thermo").asString()));
setupPhase(*t, *phases[id], root);
return t.release();
} else {
// Use the first phase definition
auto& phase = root["phases"].asVector<AnyMap>().at(0);
unique_ptr<ThermoPhase> t(newThermoPhase(phase["thermo"].asString()));
setupPhase(*t, phase, root);
return t.release();
}
} else {
XML_Node* root = get_XML_File(infile);
XML_Node* xphase = get_XML_NameID("phase", "#"+id, root);
if (!xphase) {
throw CanteraError("newPhase",
"Couldn't find phase named \"" + id + "\" in file, " + infile);
}
return newPhase(*xphase);
}
return newPhase(*xphase);
}
//! Gather a vector of pointers to XML_Nodes for a phase

View file

@ -677,14 +677,30 @@ MultiSpeciesThermo& ThermoPhase::speciesThermo(int k)
void ThermoPhase::initThermoFile(const std::string& inputFile,
const std::string& id)
{
XML_Node* fxml = get_XML_File(inputFile);
XML_Node* fxml_phase = findXMLPhase(fxml, id);
if (!fxml_phase) {
throw CanteraError("ThermoPhase::initThermoFile",
"ERROR: Can not find phase named {} in file"
" named {}", id, inputFile);
size_t dot = inputFile.find_last_of(".");
string extension;
if (dot != npos) {
extension = inputFile.substr(dot+1);
}
if (extension == "yml" || extension == "yaml") {
AnyMap root = AnyMap::fromYamlFile(inputFile);
auto phases = root["phases"].asMap("name");
if (phases.find(id) == phases.end()) {
throw CanteraError("newPhase",
"Couldn't find phase named '{}' in file '{}'.", id, inputFile);
}
setupPhase(*this, *phases[id], root);
} else {
XML_Node* fxml = get_XML_File(inputFile);
XML_Node* fxml_phase = findXMLPhase(fxml, id);
if (!fxml_phase) {
throw CanteraError("ThermoPhase::initThermoFile",
"ERROR: Can not find phase named {} in file"
" named {}", id, inputFile);
}
importPhase(*fxml_phase, this);
}
importPhase(*fxml_phase, this);
}
void ThermoPhase::initThermoXML(XML_Node& phaseNode, const std::string& id)

View file

@ -2,32 +2,36 @@
#include "cantera/thermo/ThermoFactory.h"
#include "cantera/thermo/Elements.h"
#include "cantera/thermo/MolalityVPSSTP.h"
#include "cantera/thermo/IdealGasPhase.h"
using namespace Cantera;
namespace {
shared_ptr<ThermoPhase> newThermo(const std::string& fileName,
const std::string& phaseName)
{
return shared_ptr<ThermoPhase>(newPhase(fileName, phaseName));
}
} // namespace
TEST(ThermoFromYaml, simpleIdealGas)
{
AnyMap infile = AnyMap::fromYamlFile("ideal-gas.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("simple"), infile);
EXPECT_EQ(thermo->nSpecies(), (size_t) 3);
EXPECT_DOUBLE_EQ(thermo->density(), 7.031763356741983);
EXPECT_DOUBLE_EQ(thermo->cp_mass(), 1037.7632754708304);
IdealGasPhase thermo("ideal-gas.yaml", "simple");
EXPECT_EQ(thermo.nSpecies(), (size_t) 3);
EXPECT_DOUBLE_EQ(thermo.density(), 7.031763356741983);
EXPECT_DOUBLE_EQ(thermo.cp_mass(), 1037.7632754708304);
}
TEST(ThermoFromYaml, failDuplicateSpecies)
{
AnyMap infile = AnyMap::fromYamlFile("ideal-gas.yaml");
auto phaseNodes = infile["phases"].asMap("name");
EXPECT_THROW(newPhase(*phaseNodes.at("duplicate-species"), infile),
CanteraError);
EXPECT_THROW(newThermo("ideal-gas.yaml", "duplicate-species"), CanteraError);
}
TEST(ThermoFromYaml, elementOverride)
{
AnyMap infile = AnyMap::fromYamlFile("ideal-gas.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("element-override"), infile);
auto thermo = newThermo("ideal-gas.yaml", "element-override");
EXPECT_EQ(thermo->nElements(), (size_t) 3);
EXPECT_DOUBLE_EQ(thermo->atomicWeight(0), getElementWeight("N"));
EXPECT_DOUBLE_EQ(thermo->atomicWeight(1), getElementWeight("O"));
@ -36,9 +40,7 @@ TEST(ThermoFromYaml, elementOverride)
TEST(ThermoFromYaml, elementFromDifferentFile)
{
AnyMap infile = AnyMap::fromYamlFile("ideal-gas.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("element-remote"), infile);
auto thermo = newThermo("ideal-gas.yaml", "element-remote");
EXPECT_EQ(thermo->nElements(), (size_t) 3);
EXPECT_DOUBLE_EQ(thermo->atomicWeight(0), getElementWeight("N"));
EXPECT_DOUBLE_EQ(thermo->atomicWeight(1), getElementWeight("O"));
@ -47,22 +49,18 @@ TEST(ThermoFromYaml, elementFromDifferentFile)
TEST(ThermoFromYaml, speciesFromDifferentFile)
{
AnyMap infile = AnyMap::fromYamlFile("ideal-gas.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("species-remote"), infile);
EXPECT_EQ(thermo->nElements(), (size_t) 2);
EXPECT_EQ(thermo->nSpecies(), (size_t) 4);
EXPECT_EQ(thermo->species(0)->composition["O"], 2);
EXPECT_EQ(thermo->species(3)->composition["O"], 1);
EXPECT_EQ(thermo->species(2)->name, "NO2");
EXPECT_DOUBLE_EQ(thermo->moleFraction(3), 0.3);
IdealGasPhase thermo("ideal-gas.yaml", "species-remote");
EXPECT_EQ(thermo.nElements(), (size_t) 2);
EXPECT_EQ(thermo.nSpecies(), (size_t) 4);
EXPECT_EQ(thermo.species(0)->composition["O"], 2);
EXPECT_EQ(thermo.species(3)->composition["O"], 1);
EXPECT_EQ(thermo.species(2)->name, "NO2");
EXPECT_DOUBLE_EQ(thermo.moleFraction(3), 0.3);
}
TEST(ThermoFromYaml, speciesAll)
{
AnyMap infile = AnyMap::fromYamlFile("ideal-gas.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("species-all"), infile);
auto thermo = newThermo("ideal-gas.yaml", "species-all");
EXPECT_EQ(thermo->nElements(), (size_t) 3);
EXPECT_EQ(thermo->nSpecies(), (size_t) 6);
EXPECT_EQ(thermo->species(1)->name, "NO");
@ -71,9 +69,7 @@ TEST(ThermoFromYaml, speciesAll)
TEST(ThermoFromYaml, StoichSubstance1)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("NaCl(s)"), infile);
auto thermo = newThermo("thermo-models.yaml", "NaCl(s)");
EXPECT_EQ(thermo->type(), "StoichSubstance");
EXPECT_EQ(thermo->nSpecies(), (size_t) 1);
EXPECT_EQ(thermo->nElements(), (size_t) 2);
@ -83,9 +79,7 @@ TEST(ThermoFromYaml, StoichSubstance1)
TEST(ThermoFromYaml, StoichSubstance2)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("KCl(s)"), infile);
auto thermo = newThermo("thermo-models.yaml", "KCl(s)");
EXPECT_EQ(thermo->type(), "StoichSubstance");
EXPECT_EQ(thermo->nSpecies(), (size_t) 1);
EXPECT_EQ(thermo->nElements(), (size_t) 2);
@ -94,9 +88,7 @@ TEST(ThermoFromYaml, StoichSubstance2)
TEST(ThermoFromYaml, WaterSSTP)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("liquid-water"), infile);
auto thermo = newThermo("thermo-models.yaml", "liquid-water");
EXPECT_EQ(thermo->nSpecies(), (size_t) 1);
thermo->setState_TP(350, 2*OneAtm);
// Regression tests based on XML
@ -106,9 +98,7 @@ TEST(ThermoFromYaml, WaterSSTP)
TEST(ThermoFromYaml, FixedChemPot)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("Li-fixed"), infile);
auto thermo = newThermo("thermo-models.yaml", "Li-fixed");
EXPECT_EQ(thermo->nSpecies(), (size_t) 1);
double mu;
thermo->getChemPotentials(&mu);
@ -117,9 +107,7 @@ TEST(ThermoFromYaml, FixedChemPot)
TEST(ThermoFromYaml, Margules)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("molten-salt-Margules"), infile);
auto thermo = newThermo("thermo-models.yaml", "molten-salt-Margules");
EXPECT_EQ(thermo->type(), "Margules");
// Regression test based on LiKCl_liquid.xml
@ -130,9 +118,7 @@ TEST(ThermoFromYaml, Margules)
TEST(ThermoFromYaml, IdealMolalSoln)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("ideal-molal-aqueous"), infile);
auto thermo = newThermo("thermo-models.yaml", "ideal-molal-aqueous");
EXPECT_EQ(thermo->type(), "IdealMolalSoln");
EXPECT_NEAR(thermo->enthalpy_mole(), 0.013282, 1e-6);
@ -142,9 +128,7 @@ TEST(ThermoFromYaml, IdealMolalSoln)
TEST(ThermoFromYaml, DebyeHuckel_bdot_ak)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("debye-huckel-B-dot-ak"), infile);
auto thermo = newThermo("thermo-models.yaml", "debye-huckel-B-dot-ak");
// Regression test based on XML input file
EXPECT_EQ(thermo->type(), "DebyeHuckel");
@ -168,9 +152,7 @@ TEST(ThermoFromYaml, DebyeHuckel_bdot_ak)
TEST(ThermoFromYaml, DebyeHuckel_beta_ij)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("debye-huckel-beta_ij"), infile);
auto thermo = newThermo("thermo-models.yaml", "debye-huckel-beta_ij");
// Regression test based on XML input file
EXPECT_EQ(thermo->type(), "DebyeHuckel");
@ -194,10 +176,7 @@ TEST(ThermoFromYaml, DebyeHuckel_beta_ij)
TEST(ThermoFromYaml, IonsFromNeutral)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("ions-from-neutral-molecule"), infile);
auto thermo = newThermo("thermo-models.yaml", "ions-from-neutral-molecule");
ASSERT_EQ((int) thermo->nSpecies(), 2);
vector_fp mu(thermo->nSpecies());
thermo->getChemPotentials(mu.data());
@ -211,10 +190,7 @@ TEST(ThermoFromYaml, IonsFromNeutral)
TEST(ThermoFromYaml, IdealSolnGas_gas)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("IdealSolnGas-gas"), infile);
auto thermo = newThermo("thermo-models.yaml", "IdealSolnGas-gas");
thermo->equilibrate("HP");
EXPECT_NEAR(thermo->temperature(), 479.929, 1e-3); // based on h2o2.cti
EXPECT_NEAR(thermo->moleFraction("H2O"), 0.01, 1e-4);
@ -223,10 +199,7 @@ TEST(ThermoFromYaml, IdealSolnGas_gas)
TEST(ThermoFromYaml, IdealSolnGas_liquid)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("IdealSolnGas-liquid"), infile);
auto thermo = newThermo("thermo-models.yaml", "IdealSolnGas-liquid");
thermo->setState_TP(300, OneAtm);
EXPECT_NEAR(thermo->density(), 505.42393940, 2e-8);
EXPECT_NEAR(thermo->gibbs_mole(), -7801634.1184443515, 2e-8);
@ -241,10 +214,7 @@ TEST(ThermoFromYaml, IdealSolnGas_liquid)
TEST(ThermoFromYaml, RedlichKister)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("Redlich-Kister-LiC6"), infile);
auto thermo = newThermo("thermo-models.yaml", "Redlich-Kister-LiC6");
vector_fp chemPotentials(2);
vector_fp dlnActCoeffdx(2);
thermo->setState_TP(298.15, OneAtm);
@ -263,10 +233,7 @@ TEST(ThermoFromYaml, RedlichKister)
TEST(ThermoFromYaml, MaskellSolidSoln)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("MaskellSolidSoln"), infile);
auto thermo = newThermo("thermo-models.yaml", "MaskellSolidSoln");
vector_fp chemPotentials(2);
thermo->getChemPotentials(chemPotentials.data());
EXPECT_NEAR(chemPotentials[0], -4.989677478024063e6, 1e-6);
@ -275,10 +242,7 @@ TEST(ThermoFromYaml, MaskellSolidSoln)
TEST(ThermoFromYaml, HMWSoln)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("HMW-NaCl-electrolyte"), infile);
auto thermo = newThermo("thermo-models.yaml", "HMW-NaCl-electrolyte");
size_t N = thermo->nSpecies();
auto HMW = dynamic_cast<MolalityVPSSTP*>(thermo.get());
vector_fp acMol(N), mf(N), activities(N), moll(N), mu0(N);
@ -305,10 +269,7 @@ TEST(ThermoFromYaml, HMWSoln)
TEST(ThermoFromYaml, HMWSoln_HKFT)
{
AnyMap infile = AnyMap::fromYamlFile("thermo-models.yaml");
auto phaseNodes = infile["phases"].asMap("name");
auto thermo = newPhase(*phaseNodes.at("HMW-NaCl-HKFT"), infile);
auto thermo = newThermo("thermo-models.yaml", "HMW-NaCl-HKFT");
double mvRef[] = {0.01815224, 0.00157182, 0.01954605, 0.00173137, -0.0020266};
double hRef[] = {-2.84097589e+08, -2.38159643e+08, -1.68846908e+08,
3.59728865e+06, -2.29291570e+08};