From 5bc4a12e2e16a7d204b3fd7750becc113b487860 Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Fri, 17 Oct 2014 23:44:37 +0000 Subject: [PATCH] Add options for handling Species containing undefined elements --- include/cantera/thermo/Phase.h | 26 +++++++- include/cantera/thermo/ThermoPhase.h | 2 +- src/thermo/Phase.cpp | 39 ++++++++++-- src/thermo/ThermoPhase.cpp | 9 ++- test/thermo/phaseConstructors.cpp | 94 +++++++++++++++++++++++++++- 5 files changed, 158 insertions(+), 12 deletions(-) diff --git a/include/cantera/thermo/Phase.h b/include/cantera/thermo/Phase.h index 29feb245a..51308b40b 100644 --- a/include/cantera/thermo/Phase.h +++ b/include/cantera/thermo/Phase.h @@ -14,6 +14,7 @@ namespace Cantera { + /** * @defgroup phases Models of Phases of Matter * @@ -717,7 +718,10 @@ public: doublereal entropy298 = ENTROPY298_UNKNOWN, int elem_type = CT_ELEM_TYPE_ABSPOS); - virtual void addSpecies(const Species& spec); + //! Add a Species to this Phase. Returns `true` if the species was + //! successfully added, or `false` if the species was ignored. + //! @see ignoreUndefinedElements addUndefinedElements throwUndefinedElements + virtual bool addSpecies(const Species& spec); void addSpecies(const std::string& name, const doublereal* comp, doublereal charge = 0.0, doublereal size = 1.0); @@ -733,6 +737,23 @@ public: void addUniqueSpecies(const std::string& name, const doublereal* comp, doublereal charge = 0.0, doublereal size = 1.0); + + //! Set behavior when adding a species containing undefined elements to just + //! skip the species. + void ignoreUndefinedElements(); + + //! Set behavior when adding a species containing undefined elements to add + //! those elements to the phase. + void addUndefinedElements(); + + //! Set the behavior when adding a species containing undefined elements to + //! throw an exception. This is the default behavior. + void throwUndefinedElements(); + + struct UndefElement { enum behavior { + error, ignore, add + }; }; + //!@} end group adding species and elements //! Returns a bool indicating wether the object is ready for use @@ -782,6 +803,9 @@ protected: std::map m_species; + //! Flag determining behavior when adding species with an undefined element + UndefElement::behavior m_undefinedElementBehavior; + private: XML_Node* m_xml; //!< XML node containing the XML info for this phase diff --git a/include/cantera/thermo/ThermoPhase.h b/include/cantera/thermo/ThermoPhase.h index 6a5b0b324..b15419267 100644 --- a/include/cantera/thermo/ThermoPhase.h +++ b/include/cantera/thermo/ThermoPhase.h @@ -1278,7 +1278,7 @@ public: //@{ using Phase::addSpecies; - virtual void addSpecies(const Species& spec); + virtual bool addSpecies(const Species& spec); //! Store a reference pointer to the XML tree containing the species data //! for this phase. diff --git a/src/thermo/Phase.cpp b/src/thermo/Phase.cpp index e98746132..69f4d260c 100644 --- a/src/thermo/Phase.cpp +++ b/src/thermo/Phase.cpp @@ -19,6 +19,7 @@ namespace Cantera Phase::Phase() : m_kk(0), m_ndim(3), + m_undefinedElementBehavior(UndefElement::error), m_xml(new XML_Node("phase")), m_id(""), m_name(""), @@ -35,6 +36,7 @@ Phase::Phase() : Phase::Phase(const Phase& right) : m_kk(0), m_ndim(3), + m_undefinedElementBehavior(right.m_undefinedElementBehavior), m_xml(0), m_id(""), m_name(""), @@ -60,6 +62,7 @@ Phase& Phase::operator=(const Phase& right) // Handle our own data m_kk = right.m_kk; m_ndim = right.m_ndim; + m_undefinedElementBehavior = right.m_undefinedElementBehavior; m_temp = right.m_temp; m_dens = right.m_dens; m_mmw = right.m_mmw; @@ -817,17 +820,30 @@ size_t Phase::addUniqueElementAfterFreeze(const std::string& symbol, return addElement(symbol, weight, atomicNumber, entropy298, elem_type); } -void Phase::addSpecies(const Species& spec) { +bool Phase::addSpecies(const Species& spec) { m_species[spec.name] = spec; vector_fp comp(nElements()); for (map::const_iterator iter = spec.composition.begin(); iter != spec.composition.end(); iter++) { size_t m = elementIndex(iter->first); - if (m == npos) { - throw CanteraError("Phase::addSpecies", - "Species '" + spec.name + "' contains an " - "undefined element '" + iter->first + "'."); + if (m == npos) { // Element doesn't exist in this phase + switch (m_undefinedElementBehavior) { + case UndefElement::ignore: + return false; + + case UndefElement::add: + addElement(iter->first); + comp.resize(nElements()); + m = elementIndex(iter->first); + break; + + case UndefElement::error: + default: + throw CanteraError("Phase::addSpecies", + "Species '" + spec.name + "' contains an " + "undefined element '" + iter->first + "'."); + } } comp[m] = iter->second; } @@ -886,6 +902,7 @@ void Phase::addSpecies(const Species& spec) { m_y.push_back(0.0); m_ym.push_back(0.0); } + return true; } void Phase::addSpecies(const std::string& name_, const doublereal* comp, @@ -929,6 +946,18 @@ void Phase::addUniqueSpecies(const std::string& name_, const doublereal* comp, addSpecies(name_, comp, charge_, size_); } +void Phase::ignoreUndefinedElements() { + m_undefinedElementBehavior = UndefElement::ignore; +} + +void Phase::addUndefinedElements() { + m_undefinedElementBehavior = UndefElement::add; +} + +void Phase::throwUndefinedElements() { + m_undefinedElementBehavior = UndefElement::error; +} + bool Phase::ready() const { return (m_kk > 0); diff --git a/src/thermo/ThermoPhase.cpp b/src/thermo/ThermoPhase.cpp index 885864803..70c2db1e1 100644 --- a/src/thermo/ThermoPhase.cpp +++ b/src/thermo/ThermoPhase.cpp @@ -695,10 +695,13 @@ void ThermoPhase::installSlavePhases(Cantera::XML_Node* phaseNode) { } -void ThermoPhase::addSpecies(const Species& spec) +bool ThermoPhase::addSpecies(const Species& spec) { - Phase::addSpecies(spec); - m_spthermo->install_STIT(spec.thermo().duplMyselfAsSpeciesThermoInterpType()); + bool added = Phase::addSpecies(spec); + if (added) { + m_spthermo->install_STIT(spec.thermo().duplMyselfAsSpeciesThermoInterpType()); + } + return added; } void ThermoPhase::saveSpeciesData(const size_t k, const XML_Node* const data) diff --git a/test/thermo/phaseConstructors.cpp b/test/thermo/phaseConstructors.cpp index ec75d429e..0ef826fdc 100644 --- a/test/thermo/phaseConstructors.cpp +++ b/test/thermo/phaseConstructors.cpp @@ -1,11 +1,12 @@ #include "gtest/gtest.h" #include "cantera/thermo/FixedChemPotSSTP.h" #include "cantera/thermo/ThermoFactory.h" -#include "cantera/thermo/SpeciesThermoFactory.h" -#include "cantera/thermo/speciesThermoTypes.h" +#include "cantera/thermo/NasaPoly2.h" #include "cantera/thermo/IdealGasPhase.h" #include "cantera/base/ctml.h" +#include "cantera/base/stringUtils.h" #include +#include "thermo_data.h" namespace Cantera { @@ -100,4 +101,93 @@ TEST_F(ChemkinConversionTest, FailedConversion) { } #endif +class ConstructFromScratch : public testing::Test +{ +public: + ConstructFromScratch() + : sH2O("H2O", parseCompString("H:2 O:1"), + new NasaPoly2(0, 200, 3500, 101325, h2o_nasa_coeffs)) + , sH2("H2", parseCompString("H:2"), + new NasaPoly2(0, 200, 3500, 101325, h2_nasa_coeffs)) + , sO2("O2", parseCompString("O:2"), + new NasaPoly2(0, 200, 3500, 101325, o2_nasa_coeffs)) + , sOH("OH", parseCompString("H:1 O:1"), + new NasaPoly2(0, 200, 3500, 101325, oh_nasa_coeffs)) + , sCO("CO", parseCompString("C:1 O:1"), + new NasaPoly2(0, 200, 3500, 101325, o2_nasa_coeffs)) + , sCO2("CO2", parseCompString("C:1 O:2"), + new NasaPoly2(0, 200, 3500, 101325, h2o_nasa_coeffs)) + { + } + + IdealGasPhase p; + Species sH2O, sH2, sO2, sOH, sCO, sCO2; +}; + +TEST_F(ConstructFromScratch, AddElements) +{ + p.addElement("H"); + p.addElement("O"); + ASSERT_EQ((size_t) 2, p.nElements()); + ASSERT_EQ("H", p.elementName(0)); + ASSERT_EQ((size_t) 1, p.elementIndex("O")); +} + +TEST_F(ConstructFromScratch, AddSpeciesDefaultBehavior) +{ + p.addElement("H"); + p.addElement("O"); + p.addSpecies(sH2O); + p.addSpecies(sH2); + + ASSERT_EQ((size_t) 2, p.nSpecies()); + + p.addSpecies(sO2); + p.addSpecies(sOH); + + ASSERT_EQ((size_t) 4, p.nSpecies()); + ASSERT_EQ("H2", p.speciesName(1)); + ASSERT_EQ(2, p.nAtoms(2, 1)); // O in O2 + ASSERT_EQ(2, p.nAtoms(0, 0)); // H in H2O + ASSERT_THROW(p.addSpecies(sCO), CanteraError); +} + +TEST_F(ConstructFromScratch, ignoreUndefinedElements) +{ + p.addElement("H"); + p.addElement("O"); + p.ignoreUndefinedElements(); + + p.addSpecies(sO2); + p.addSpecies(sOH); + ASSERT_EQ((size_t) 2, p.nSpecies()); + + p.addSpecies(sCO); + p.addSpecies(sCO2); + ASSERT_EQ((size_t) 2, p.nSpecies()); + ASSERT_EQ((size_t) 2, p.nElements()); + ASSERT_EQ(npos, p.speciesIndex("CO2")); +} + +TEST_F(ConstructFromScratch, addUndefinedElements) +{ + p.addElement("H"); + p.addElement("O"); + p.addUndefinedElements(); + + p.addSpecies(sH2); + p.addSpecies(sOH); + ASSERT_EQ((size_t) 2, p.nSpecies()); + ASSERT_EQ((size_t) 2, p.nElements()); + + p.addSpecies(sCO); + p.addSpecies(sCO2); + ASSERT_EQ((size_t) 4, p.nSpecies()); + ASSERT_EQ((size_t) 3, p.nElements()); + ASSERT_EQ((size_t) 1, p.nAtoms(p.speciesIndex("CO2"), p.elementIndex("C"))); + ASSERT_EQ((size_t) 2, p.nAtoms(p.speciesIndex("CO2"), p.elementIndex("O"))); + p.setMassFractionsByName("H2:0.5, CO2:0.5"); + ASSERT_FLOAT_EQ(0.5, p.massFraction("CO2")); +} + } // namespace Cantera