diff --git a/include/cantera/thermo/IdealSolidSolnPhase.h b/include/cantera/thermo/IdealSolidSolnPhase.h index 98e737064..05316b5c0 100644 --- a/include/cantera/thermo/IdealSolidSolnPhase.h +++ b/include/cantera/thermo/IdealSolidSolnPhase.h @@ -578,6 +578,22 @@ public: virtual void initThermoXML(XML_Node& phaseNode, const std::string& id); virtual void setToEquilState(const doublereal* lambda_RT); + //! Set the form for the standard and generalized concentrations + /*! + * Must be one of 'unity', 'molar_volume', or 'solvent_volume'. + * The default is 'unity'. + * + * | m_formGC | GeneralizedConc | StandardConc | + * | ----------- | --------------- | ------------ | + * | unity | X_k | 1.0 | + * | molar_volume | X_k / V_k | 1.0 / V_k | + * | solvent_volume | X_k / V_N | 1.0 / V_N | + * + * The value and form of the generalized concentration will affect + * reaction rate constants involving species in this phase. + */ + void setStandardConcentrationModel(const std::string& model); + /** * Report the molar volume of species k * @@ -603,16 +619,9 @@ protected: virtual void compositionChanged(); /** - * Format for the generalized concentrations. - * - * | m_formGC | GeneralizedConc | StandardConc | - * | ----------- | --------------- | ------------ | - * | 0 (default) | X_k | 1.0 | - * | 1 | X_k / V_k | 1.0 / V_k | - * | 2 | X_k / V_N | 1.0 / V_N | - * - * The value and form of the generalized concentration will affect - * reaction rate constants involving species in this phase. + * The standard concentrations can have one of three different forms: + * 0 = 'unity', 1 = 'molar_volume', 2 = 'solvent_volume'. See + * setStandardConcentrationModel(). */ int m_formGC; diff --git a/src/thermo/IdealSolidSolnPhase.cpp b/src/thermo/IdealSolidSolnPhase.cpp index 5c1fda1bb..df5000566 100644 --- a/src/thermo/IdealSolidSolnPhase.cpp +++ b/src/thermo/IdealSolidSolnPhase.cpp @@ -359,7 +359,12 @@ bool IdealSolidSolnPhase::addSpecies(shared_ptr spec) m_s0_R.push_back(0.0); m_pe.push_back(0.0);; m_pp.push_back(0.0); - m_speciesMolarVolume.push_back(0.0); + if (spec->extra.hasKey("molar_volume")) { + m_speciesMolarVolume.push_back(spec->extra["molar_volume"].asDouble()); + } else { + throw CanteraError("IdealSolidSolnPhase::addSpecies", + "Molar volume not specified for species '{}'", spec->name); + } } return added; } @@ -391,34 +396,12 @@ void IdealSolidSolnPhase::initThermoXML(XML_Node& phaseNode, const std::string& // // if (phaseNode.hasChild("standardConc")) { - XML_Node& scNode = phaseNode.child("standardConc"); - string formString = scNode.attrib("model"); - if (ba::iequals(formString, "unity")) { - m_formGC = 0; - } else if (ba::iequals(formString, "molar_volume")) { - m_formGC = 1; - } else if (ba::iequals(formString, "solvent_volume")) { - m_formGC = 2; - } else { - throw CanteraError("IdealSolidSolnPhase::initThermoXML", - "Unknown standardConc model: " + formString); - } + setStandardConcentrationModel(phaseNode.child("standardConc")["model"]); } else { throw CanteraError("IdealSolidSolnPhase::initThermoXML", "Unspecified standardConc model"); } - // 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_); } @@ -440,6 +423,20 @@ void IdealSolidSolnPhase::setToEquilState(const doublereal* lambda_RT) setState_PX(pres, &m_pp[0]); } +void IdealSolidSolnPhase::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("IdealSolidSolnPhase::setStandardConcentrationModel", + "Unknown standard concentration model '{}'", model); + } +} + double IdealSolidSolnPhase::speciesMolarVolume(int k) const { return m_speciesMolarVolume[k]; diff --git a/test/thermo/phaseConstructors.cpp b/test/thermo/phaseConstructors.cpp index 6066110b8..3e935df2c 100644 --- a/test/thermo/phaseConstructors.cpp +++ b/test/thermo/phaseConstructors.cpp @@ -15,6 +15,8 @@ #include "cantera/thermo/LatticePhase.h" #include "cantera/thermo/StoichSubstance.h" #include "cantera/thermo/LatticeSolidPhase.h" +#include "cantera/thermo/IdealSolidSolnPhase.h" + #include "cantera/thermo/NasaPoly2.h" #include "cantera/thermo/ConstCpPoly.h" #include "cantera/thermo/ShomatePoly.h" @@ -479,4 +481,24 @@ TEST(LatticeSolidPhase, fromScratch) } } +TEST(IdealSolidSolnPhase, fromScratch) +{ + // Regression test based fictitious XML input file + IdealSolidSolnPhase p; + p.addUndefinedElements(); + auto sp1 = make_species("sp1", "C:2, H:2", o2_nasa_coeffs); + sp1->extra["molar_volume"] = 1.5; + auto sp2 = make_species("sp2", "C:1", h2o_nasa_coeffs); + sp2->extra["molar_volume"] = 1.3; + auto sp3 = make_species("sp3", "H:2", h2_nasa_coeffs); + sp3->extra["molar_volume"] = 0.1; + for (auto& s : {sp1, sp2, sp3}) { + p.addSpecies(s); + } + p.setState_TPX(500, 2e5, "sp1:0.1, sp2:0.89, sp3:0.01"); + EXPECT_NEAR(p.density(), 10.1786978, 1e-6); + EXPECT_NEAR(p.enthalpy_mass(), -15642803.3884617, 1e-4); + EXPECT_NEAR(p.gibbs_mole(), -313642293.1654253, 1e-4); +} + } // namespace Cantera