[Thermo] Allow instantiation of IdealMolalSoln without XML

This commit is contained in:
Ray Speth 2017-02-19 20:29:09 -05:00
parent f8ef4a8b2b
commit a6ac446021
3 changed files with 108 additions and 76 deletions

View file

@ -47,14 +47,8 @@ namespace Cantera
* ThermoPhase, and overloads the virtual methods defined there with ones that
* use expressions appropriate for incompressible mixtures.
*
* The standard concentrations can have three different forms depending on the
* value of the member attribute m_formGC, which is supplied in the XML file.
*
* | m_formGC | ActivityConc | StandardConc |
* | -------- | -------------------------------- | ------------------ |
* | 0 | \f$ {m_k}/ { m^{\Delta}}\f$ | \f$ 1.0 \f$ |
* | 1 | \f$ m_k / (m^{\Delta} V_k)\f$ | \f$ 1.0 / V_k \f$ |
* | 2 | \f$ m_k / (m^{\Delta} V^0_0)\f$ | \f$ 1.0 / V^0_0\f$ |
* The standard concentrations can have three different forms.
* See setStandardConcentrationModel().
*
* \f$ V^0_0 \f$ is the solvent standard molar volume. \f$ m^{\Delta} \f$ is a
* constant equal to a molality of \f$ 1.0 \quad\mbox{gm kmol}^{-1} \f$.
@ -414,6 +408,24 @@ public:
virtual void initThermoXML(XML_Node& phaseNode, const std::string& id="");
virtual void initThermo();
//! Set the standard concentration model.
/*!
* Must be one of 'unity', 'molar_volume', or 'solvent_volume'.
* The default is 'solvent_volume'.
*
* | model | ActivityConc | StandardConc |
* | -------------- | -------------------------------- | ------------------ |
* | unity | \f$ {m_k}/ { m^{\Delta}}\f$ | \f$ 1.0 \f$ |
* | molar_volume | \f$ m_k / (m^{\Delta} V_k)\f$ | \f$ 1.0 / V_k \f$ |
* | solvent_volume | \f$ m_k / (m^{\Delta} V^0_0)\f$ | \f$ 1.0 / V^0_0\f$ |
*/
void setStandardConcentrationModel(const std::string& model);
//! Set cutoff model. Must be one of 'none', 'poly', or 'polyExp'.
void setCutoffModel(const std::string& model);
//! Report the molar volume of species k
/*!
* units - \f$ m^3 kmol^{-1} \f$
@ -436,19 +448,12 @@ protected:
vector_fp m_speciesMolarVolume;
/**
* The standard concentrations can have three different forms depending on
* the value of the member attribute m_formGC, which is supplied in the XML
* file.
*
* | m_formGC | ActivityConc | StandardConc |
* | -------- | -------------------------------- | ------------------ |
* | 0 | \f$ {m_k}/ { m^{\Delta}}\f$ | \f$ 1.0 \f$ |
* | 1 | \f$ m_k / (m^{\Delta} V_k)\f$ | \f$ 1.0 / V_k \f$ |
* | 2 | \f$ m_k / (m^{\Delta} V^0_0)\f$ | \f$ 1.0 / V^0_0\f$ |
* The standard concentrations can have one of three different forms:
* 0 = 'unity', 1 = 'molar_volume', 2 = 'solvent_volume'. See
* setStandardConcentrationModel().
*/
int m_formGC;
public:
//! Cutoff type
int IMS_typeCutoff_;

View file

@ -18,6 +18,7 @@
#include "cantera/thermo/IdealMolalSoln.h"
#include "cantera/thermo/ThermoFactory.h"
#include "cantera/base/ctml.h"
#include "cantera/base/stringUtils.h"
#include <iostream>
namespace Cantera
@ -356,8 +357,7 @@ bool IdealMolalSoln::addSpecies(shared_ptr<Species> spec)
void IdealMolalSoln::initThermoXML(XML_Node& phaseNode, const std::string& id_)
{
// Initialize the whole thermo object, using a virtual function.
initThermo();
MolalityVPSSTP::initThermoXML(phaseNode, id_);
if (id_.size() > 0 && phaseNode.id() != id_) {
throw CanteraError("IdealMolalSoln::initThermo",
@ -374,20 +374,7 @@ void IdealMolalSoln::initThermoXML(XML_Node& phaseNode, const std::string& id_)
// Possible change the form of the standard concentrations
if (thermoNode.hasChild("standardConc")) {
XML_Node& scNode = thermoNode.child("standardConc");
m_formGC = 2;
std::string formString = scNode.attrib("model");
if (formString != "") {
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("IdealMolalSoln::initThermo",
"Unknown standardConc model: " + formString);
}
}
setStandardConcentrationModel(scNode["model"]);
}
// Get the Name of the Solvent:
@ -406,7 +393,6 @@ void IdealMolalSoln::initThermoXML(XML_Node& phaseNode, const std::string& id_)
if (thermoNode.hasChild("activityCoefficients")) {
XML_Node& acNode = thermoNode.child("activityCoefficients");
std::string modelString = acNode.attrib("model");
IMS_typeCutoff_ = 0;
if (modelString != "IdealMolalSoln") {
throw CanteraError("IdealMolalSoln::initThermoXML",
"unknown ActivityCoefficient model: " + modelString);
@ -415,15 +401,7 @@ void IdealMolalSoln::initThermoXML(XML_Node& phaseNode, const std::string& id_)
XML_Node& ccNode = acNode.child("idealMolalSolnCutoff");
modelString = ccNode.attrib("model");
if (modelString != "") {
if (modelString == "polyExp") {
IMS_typeCutoff_ = 2;
} else if (modelString == "poly") {
IMS_typeCutoff_ = 1;
} else {
throw CanteraError("IdealMolalSoln::initThermoXML",
"Unknown idealMolalSolnCutoff form: " + modelString);
}
setCutoffModel(modelString);
if (ccNode.hasChild("gamma_o_limit")) {
IMS_gamma_o_min_ = getFloat(ccNode, "gamma_o_limit");
}
@ -443,6 +421,8 @@ void IdealMolalSoln::initThermoXML(XML_Node& phaseNode, const std::string& id_)
IMS_slopegCut_ = getFloat(ccNode, "slope_g_limit");
}
}
} else {
setCutoffModel("none");
}
}
@ -464,32 +444,45 @@ void IdealMolalSoln::initThermoXML(XML_Node& phaseNode, const std::string& id_)
"Solvent " + solventName +
" should be first species");
}
}
// Now go get the molar volumes
XML_Node& speciesList = phaseNode.child("speciesArray");
XML_Node* speciesDB =
get_XML_NameID("speciesData", speciesList["datasrc"],
&phaseNode.root());
const std::vector<std::string> &sss = speciesNames();
for (size_t k = 0; k < m_kk; k++) {
XML_Node* s = speciesDB->findByAttr("name", sss[k]);
XML_Node* ss = s->findByName("standardState");
m_speciesMolarVolume[k] = getFloat(*ss, "molarVolume", "toSI");
void IdealMolalSoln::initThermo()
{
MolalityVPSSTP::initThermo();
for (size_t k = 0; k < nSpecies(); k++) {
m_speciesMolarVolume[k] = providePDSS(k)->molarVolume();
}
IMS_typeCutoff_ = 2;
if (IMS_typeCutoff_ == 2) {
calcIMSCutoffParams_();
}
MolalityVPSSTP::initThermoXML(phaseNode, id_);
setMoleFSolventMin(1.0E-5);
}
// Set the state
if (phaseNode.hasChild("state")) {
XML_Node& stateNode = phaseNode.child("state");
setStateFromXML(stateNode);
void IdealMolalSoln::setStandardConcentrationModel(const std::string& model)
{
if (ba::iequals(model, "unity")) {
m_formGC = 0;
} else if (ba::iequals(model, "molar_volume")) {
m_formGC = 1;
} else if (ba::iequals(model, "solvent_volume")) {
m_formGC = 2;
} else {
throw CanteraError("IdealSolnGasVPSS::setStandardConcentrationModel",
"Unknown standard concentration model '{}'", model);
}
}
void IdealMolalSoln::setCutoffModel(const std::string& model)
{
if (ba::iequals(model, "none")) {
IMS_typeCutoff_ = 0;
} else if (ba::iequals(model, "poly")) {
IMS_typeCutoff_ = 1;
} else if (ba::iequals(model, "polyexp")) {
IMS_typeCutoff_ = 2;
} else {
throw CanteraError("IdealMolalSoln::setCutoffModel",
"Unknown cutoff model '{}'", model);
}
}

View file

@ -1,12 +1,14 @@
#include "gtest/gtest.h"
#include "cantera/thermo/ThermoFactory.h"
#include "cantera/thermo/PDSSFactory.h"
#include "cantera/thermo/PDSS_ConstVol.h"
#include "cantera/thermo/FixedChemPotSSTP.h"
#include "cantera/thermo/PureFluidPhase.h"
#include "cantera/thermo/WaterSSTP.h"
#include "cantera/thermo/RedlichKwongMFTP.h"
#include "cantera/thermo/IonsFromNeutralVPSSTP.h"
#include "cantera/thermo/IdealSolnGasVPSS.h"
#include "cantera/thermo/IdealMolalSoln.h"
#include "cantera/thermo/NasaPoly2.h"
#include "cantera/thermo/ShomatePoly.h"
#include "cantera/thermo/IdealGasPhase.h"
@ -18,6 +20,14 @@
namespace Cantera
{
shared_ptr<Species> make_species(const std::string& name,
const std::string& composition, const double* nasa_coeffs)
{
auto species = make_shared<Species>(name, parseCompString(composition));
species->thermo.reset(new NasaPoly2(200, 3500, 101325, nasa_coeffs));
return species;
}
class FixedChemPotSstpConstructorTest : public testing::Test
{
};
@ -115,18 +125,13 @@ class ConstructFromScratch : public testing::Test
{
public:
ConstructFromScratch()
: sH2O(new Species("H2O", parseCompString("H:2 O:1")))
, sH2(new Species("H2", parseCompString("H:2")))
, sO2(new Species("O2", parseCompString("O:2")))
, sOH(new Species("OH", parseCompString("H:1 O:1")))
, sCO(new Species("CO", parseCompString("C:1 O:1")))
: sH2O(make_species("H2O", "H:2 O:1", h2o_nasa_coeffs))
, sH2(make_species("H2", "H:2", h2_nasa_coeffs))
, sO2(make_species("O2", "O:2", o2_nasa_coeffs))
, sOH(make_species("OH", "H:1 O:1", oh_nasa_coeffs))
, sCO(make_species("CO", "C:1 O:1", o2_nasa_coeffs))
, sCO2(new Species("CO2", parseCompString("C:1 O:2")))
{
sH2O->thermo.reset(new NasaPoly2(200, 3500, 101325, h2o_nasa_coeffs));
sH2->thermo.reset(new NasaPoly2(200, 3500, 101325, h2_nasa_coeffs));
sO2->thermo.reset(new NasaPoly2(200, 3500, 101325, o2_nasa_coeffs));
sOH->thermo.reset(new NasaPoly2(200, 3500, 101325, oh_nasa_coeffs));
sCO->thermo.reset(new NasaPoly2(200, 3500, 101325, o2_nasa_coeffs));
sCO2->thermo.reset(new ShomatePoly2(200, 3500, 101325, co2_shomate_coeffs));
}
@ -268,13 +273,42 @@ TEST(PureFluidFromScratch, CarbonDioxide)
TEST(WaterSSTP, fromScratch)
{
WaterSSTP water;
auto sH2O = make_shared<Species>("H2O", parseCompString("H:2 O:1"));
sH2O->thermo.reset(new NasaPoly2(200, 3500, 101325, h2o_nasa_coeffs)); // unused
water.addUndefinedElements();
water.addSpecies(sH2O);
water.addSpecies(make_species("H2O", "H:2, O:1", h2o_nasa_coeffs));
water.initThermo();
water.setState_TP(298.15, 1e5);
EXPECT_NEAR(water.enthalpy_mole() / 1e6, -285.83, 2e-2);
}
TEST(IdealMolalSoln, fromScratch)
{
IdealMolalSoln p;
p.addUndefinedElements();
p.addSpecies(make_species("H2O(l)", "H:2, O:1", h2_nasa_coeffs));
p.addSpecies(make_species("CO2(aq)", "C:1, O:2", h2_nasa_coeffs));
p.addSpecies(make_species("H2S(aq)", "H:2, S:1", h2_nasa_coeffs));
p.addSpecies(make_species("CH4(aq)", "C:1, H:4", h2_nasa_coeffs));
size_t k = 0;
for (double v : {1.5, 1.3, 0.1, 0.1}) {
std::unique_ptr<PDSS_ConstVol> ss(new PDSS_ConstVol());
ss->setMolarVolume(v);
p.installPDSS(k++, std::move(ss));
}
p.setStandardConcentrationModel("solvent_volume");
p.setCutoffModel("polyexp");
// These propreties probably shouldn't be public
p.IMS_X_o_cutoff_ = 0.20;
p.IMS_gamma_o_min_ = 0.00001;
p.IMS_gamma_k_min_ = 10.0;
p.IMS_slopefCut_ = 0.6;
p.IMS_slopegCut_ = 0.0;
p.IMS_cCut_ = .05;
p.initThermo();
p.setState_TPM(298.15, OneAtm, "CH4(aq):0.01, H2S(aq):0.03, CO2(aq):0.1");
EXPECT_NEAR(p.enthalpy_mole(), 0.013282, 1e-6);
EXPECT_NEAR(p.gibbs_mole(), -3.8986e7, 1e3);
EXPECT_NEAR(p.density(), 12.058, 1e-3);
}
} // namespace Cantera