From 4baccc732c5b3f7bb0ff6e31c301ca1535f0f94c Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Sat, 27 Sep 2014 00:09:05 +0000 Subject: [PATCH] [Thermo] Simplify interface for adding elements to a Phase Phase::addElements(string name, ...) is the single entry point for adding elements. It always perform the uniqueness check, and can do the extra additional work required to add elements to a phase that already has species, removing the need for "freezing" the phase's elements. Parsing the XML tree for elements is now handled in "installElements". The variant element-adding methods are deprecated. --- include/cantera/thermo/Elements.h | 2 + include/cantera/thermo/Phase.h | 36 ++--- include/cantera/thermo/ThermoFactory.h | 3 + src/thermo/Elements.cpp | 3 + src/thermo/FixedChemPotSSTP.cpp | 3 +- src/thermo/LatticeSolidPhase.cpp | 4 +- src/thermo/Phase.cpp | 211 +++++++++---------------- src/thermo/ThermoFactory.cpp | 69 +++++++- 8 files changed, 172 insertions(+), 159 deletions(-) diff --git a/include/cantera/thermo/Elements.h b/include/cantera/thermo/Elements.h index f57b275c2..d87841f48 100644 --- a/include/cantera/thermo/Elements.h +++ b/include/cantera/thermo/Elements.h @@ -110,6 +110,8 @@ class XML_Node; * in a particular instantiation of the class. * * @ingroup phases + * @deprecated. Functionality is now part of class Phase. To be removed after + * Cantera 2.2. */ class Elements { diff --git a/include/cantera/thermo/Phase.h b/include/cantera/thermo/Phase.h index ec4540c39..f00269dab 100644 --- a/include/cantera/thermo/Phase.h +++ b/include/cantera/thermo/Phase.h @@ -25,20 +25,6 @@ namespace Cantera * support thermodynamic calculations (see \ref thermoprops). */ -//! Exception class to indicate a fixed set of elements. -/*! - * This class is used to warn the user when the number of elements - * are changed after at least one species is defined. - */ -class ElementsFrozen : public CanteraError -{ -public: - //! Constructor for class - //! @param func Function where the error occurred. - ElementsFrozen(const std::string& func) - : CanteraError(func, "Elements cannot be added after species.") {} -}; - //! Class %Phase is the base class for phases of matter, managing the species and elements in a phase, as well as the //! independent variables of temperature, mass density, species mass/mole fraction, //! and other generalized forces and intrinsic properties (such as electric potential) @@ -658,10 +644,21 @@ public: //! Add an element. //! @param symbol Atomic symbol std::string. //! @param weight Atomic mass in amu. - void addElement(const std::string& symbol, doublereal weight=-12345.0); + //! @param atomicNumber Atomic number of the element (unitless) + //! @param entropy298 Entropy of the element at 298 K and 1 bar in its + //! most stable form. The default is the value ENTROPY298_UNKNOWN, + //! which is interpreted as an unknown, and if used will cause + //! %Cantera to throw an error. + //! @param elem_type Specifies the type of the element constraint + //! equation. This defaults to CT_ELEM_TYPE_ABSPOS, i.e., an element. + //! @return index of the element added + size_t addElement(const std::string& symbol, doublereal weight=-12345.0, + int atomicNumber=0, doublereal entropy298=ENTROPY298_UNKNOWN, + int elem_type=CT_ELEM_TYPE_ABSPOS); //! Add an element from an XML specification. //! @param e Reference to the XML_Node where the element is described. + //! @deprecated. To be removed after Cantera 2.2. void addElement(const XML_Node& e); //! Add an element, checking for uniqueness @@ -676,6 +673,7 @@ public: //! error. //! @param elem_type Specifies the type of the element constraint //! equation. This defaults to CT_ELEM_TYPE_ABSPOS, i.e., an element. + //! @deprecated. Equivalent to addElement. To be removed after Cantera 2.2. void addUniqueElement(const std::string& symbol, doublereal weight=-12345.0, int atomicNumber = 0, doublereal entropy298 = ENTROPY298_UNKNOWN, @@ -685,16 +683,20 @@ public: //! The uniqueness is checked by comparing the string symbol. If not unique, //! nothing is done. //! @param e Reference to the XML_Node where the element is described. + //! @deprecated. To be removed after Cantera 2.2. void addUniqueElement(const XML_Node& e); //! Add all elements referenced in an XML_Node tree //! @param phase Reference to the root XML_Node of a phase + //! @deprecated. To be removed after Cantera 2.2. void addElementsFromXML(const XML_Node& phase); //! Prohibit addition of more elements, and prepare to add species. + //! @deprecated. To be removed after Cantera 2.2. void freezeElements(); //! True if freezeElements has been called. + //! @deprecated. To be removed after Cantera 2.2. bool elementsFrozen(); //! Add an element after elements have been frozen, checking for uniqueness @@ -708,6 +710,7 @@ public: //! if used will cause Cantera to throw an error. //! @param elem_type Specifies the type of the element constraint //! equation. This defaults to CT_ELEM_TYPE_ABSPOS, i.e., an element. + //! @deprecated. Equivalentto addElement. To be removed after Cantera 2.2. size_t addUniqueElementAfterFreeze(const std::string& symbol, doublereal weight, int atomicNumber, doublereal entropy298 = ENTROPY298_UNKNOWN, @@ -814,9 +817,6 @@ private: //! this int is incremented. int m_stateNum; - //! If this is true, then no elements may be added to the object. - bool m_elementsFrozen; - //! Vector of the species names std::vector m_speciesNames; diff --git a/include/cantera/thermo/ThermoFactory.h b/include/cantera/thermo/ThermoFactory.h index cb65704e6..226193703 100644 --- a/include/cantera/thermo/ThermoFactory.h +++ b/include/cantera/thermo/ThermoFactory.h @@ -230,6 +230,9 @@ ThermoPhase* newPhase(const std::string& infile, std::string id); */ bool importPhase(XML_Node& phase, ThermoPhase* th, SpeciesThermoFactory* spfactory = 0); +//! Add the elements given in an XML_Node tree to the specified phase +void installElements(Phase& th, const XML_Node& phaseNode); + //! Install a species into a ThermoPhase object, which defines //! the phase thermodynamics and speciation. /*! diff --git a/src/thermo/Elements.cpp b/src/thermo/Elements.cpp index 6a4d157ca..dd85acd3c 100644 --- a/src/thermo/Elements.cpp +++ b/src/thermo/Elements.cpp @@ -209,6 +209,9 @@ Elements::Elements() : m_elem_type(0), numSubscribers(0) { + warn_deprecated("class Elements", + "Functionality is now part of class Phase. " + "To be removed after Cantera 2.2."); } /* diff --git a/src/thermo/FixedChemPotSSTP.cpp b/src/thermo/FixedChemPotSSTP.cpp index 8597dac7e..2c749e282 100644 --- a/src/thermo/FixedChemPotSSTP.cpp +++ b/src/thermo/FixedChemPotSSTP.cpp @@ -85,8 +85,7 @@ FixedChemPotSSTP::FixedChemPotSSTP(const std::string& Ename, doublereal val) : setID(pname); setName(pname); setNDim(3); - addUniqueElement(Ename, -12345.); - freezeElements(); + addElement(Ename); vector_fp ecomp(nElements(), 0.0); ecomp[0] = 1.0; double chrg = 0.0; diff --git a/src/thermo/LatticeSolidPhase.cpp b/src/thermo/LatticeSolidPhase.cpp index 742fc9fc8..5bc5d8c1f 100644 --- a/src/thermo/LatticeSolidPhase.cpp +++ b/src/thermo/LatticeSolidPhase.cpp @@ -359,7 +359,7 @@ void LatticeSolidPhase::installSlavePhases(Cantera::XML_Node* phaseNode) int an = lp->atomicNumber(es); int e298 = lp->entropyElement298(es); //! @todo Why is this an int instead of a double? int et = lp->elementType(es); - addUniqueElementAfterFreeze(esName, wt, an, e298, et); + addElement(esName, wt, an, e298, et); } const std::vector & spNode = lp->speciesData(); kstart = kk; @@ -397,7 +397,7 @@ void LatticeSolidPhase::installSlavePhases(Cantera::XML_Node* phaseNode) string econ = "LC_"; econ += int2str(n); econ += "_" + id(); - size_t m = addUniqueElementAfterFreeze(econ, 0.0, 0, 0.0, CT_ELEM_TYPE_LATTICERATIO); + size_t m = addElement(econ, 0.0, 0, 0.0, CT_ELEM_TYPE_LATTICERATIO); size_t mm = nElements(); LatticePhase* lp0 = m_lattice[0]; size_t nsp0 = lp0->nSpecies(); diff --git a/src/thermo/Phase.cpp b/src/thermo/Phase.cpp index b46e3f5e0..50f0ec474 100644 --- a/src/thermo/Phase.cpp +++ b/src/thermo/Phase.cpp @@ -9,6 +9,7 @@ #include "cantera/base/vec_functions.h" #include "cantera/base/stringUtils.h" #include "cantera/base/ctml.h" +#include "cantera/thermo/ThermoFactory.h" using namespace std; @@ -25,7 +26,6 @@ Phase::Phase() : m_dens(0.001), m_mmw(0.0), m_stateNum(-1), - m_elementsFrozen(false), m_mm(0), m_elem_type(0), realNumberRangeBehavior_(THROWON_OVERFLOW_DEBUGMODEONLY_CTRB) @@ -42,7 +42,6 @@ Phase::Phase(const Phase& right) : m_dens(0.001), m_mmw(0.0), m_stateNum(-1), - m_elementsFrozen(false), m_mm(0), m_elem_type(0), realNumberRangeBehavior_(THROWON_OVERFLOW_DEBUGMODEONLY_CTRB) @@ -76,7 +75,6 @@ Phase& Phase::operator=(const Phase& right) m_speciesSize = right.m_speciesSize; m_mm = right.m_mm; - m_elementsFrozen = right.m_elementsFrozen; m_atomicWeights = right.m_atomicWeights; m_atomicNumbers = right.m_atomicNumbers; m_elementNames = right.m_elementNames; @@ -695,85 +693,67 @@ doublereal Phase::sum_xlogQ(doublereal* Q) const return m_mmw * Cantera::sum_xlogQ(m_ym.begin(), m_ym.end(), Q); } -void Phase::addElement(const std::string& symbol, doublereal weight) +size_t Phase::addElement(const std::string& symbol, doublereal weight, + int atomic_number, doublereal entropy298, + int elem_type) { + // Look up the atomic weight if not given if (weight == -12345.0) { weight = LookupWtElements(symbol); if (weight < 0.0) { - throw ElementsFrozen("addElement"); + throw CanteraError("Phase::addElement", + "No atomic weight found for element: " + symbol); } } - if (m_elementsFrozen) { - throw ElementsFrozen("addElement"); - return; + + // Check for duplicates + vector::const_iterator iter = find(m_elementNames.begin(), + m_elementNames.end(), + symbol); + if (iter != m_elementNames.end()) { + size_t m = iter - m_elementNames.begin(); + if (m_atomicWeights[m] != weight) { + throw CanteraError("Phase::addElement", + "Duplicate elements (" + symbol + ") have different weights"); + } else { + // Ignore attempt to add duplicate element with the same weight + return m; + } } + + // Add the new element m_atomicWeights.push_back(weight); m_elementNames.push_back(symbol); + m_atomicNumbers.push_back(atomic_number); + m_entropy298.push_back(entropy298); if (symbol == "E") { m_elem_type.push_back(CT_ELEM_TYPE_ELECTRONCHARGE); } else { - m_elem_type.push_back(CT_ELEM_TYPE_ABSPOS); + m_elem_type.push_back(elem_type); } m_mm++; + + // Update species compositions + if (m_kk) { + vector_fp old(m_speciesComp); + m_speciesComp.resize(m_kk*m_mm, 0.0); + for (size_t k = 0; k < m_kk; k++) { + size_t m_old = m_mm - 1; + for (size_t m = 0; m < m_old; m++) { + m_speciesComp[k * m_mm + m] = old[k * (m_old) + m]; + } + m_speciesComp[k * (m_mm) + (m_mm-1)] = 0.0; + } + } + + return m_mm-1; } void Phase::addElement(const XML_Node& e) { - doublereal weight = fpValue(e["atomicWt"]); - string symbol = e["name"]; - addElement(symbol, weight); -} - -void Phase::addUniqueElement(const std::string& symbol, doublereal weight, - int atomic_number, doublereal entropy298, - int elem_type) -{ - if (weight == -12345.0) { - weight = LookupWtElements(symbol); - if (weight < 0.0) { - throw ElementsFrozen("addElement"); - } - } - /* - * First decide if this element has been previously added - * by conducting a string search. If it unique, add it to - * the list. - */ - int ifound = 0; - int i = 0; - for (vector::const_iterator it = m_elementNames.begin(); - it < m_elementNames.end(); ++it, ++i) { - if (*it == symbol) { - ifound = 1; - break; - } - } - if (!ifound) { - if (m_elementsFrozen) { - throw ElementsFrozen("addElement"); - return; - } - m_atomicWeights.push_back(weight); - m_elementNames.push_back(symbol); - m_atomicNumbers.push_back(atomic_number); - m_entropy298.push_back(entropy298); - if (symbol == "E") { - m_elem_type.push_back(CT_ELEM_TYPE_ELECTRONCHARGE); - } else { - m_elem_type.push_back(elem_type); - } - m_mm++; - } else { - if (m_atomicWeights[i] != weight) { - throw CanteraError("AddUniqueElement", - "Duplicate Elements (" + symbol + ") have different weights"); - } - } -} - -void Phase::addUniqueElement(const XML_Node& e) -{ + warn_deprecated("Phase::addElement(XML_Node&)", + "To be removed after Cantera 2.2."); doublereal weight = 0.0; if (e.hasAttrib("atomicWt")) { weight = fpValue(stripws(e["atomicWt"])); @@ -791,101 +771,61 @@ void Phase::addUniqueElement(const XML_Node& e) } } if (weight != 0.0) { - addUniqueElement(symbol, weight, anum, entropy298); + addElement(symbol, weight, anum, entropy298); } else { - addUniqueElement(symbol); + addElement(symbol); } } +void Phase::addUniqueElement(const std::string& symbol, doublereal weight, + int atomic_number, doublereal entropy298, + int elem_type) +{ + warn_deprecated("Phase::addUniqueElement", + "Equivalent to Phase::addElement. " + "To be removed after Cantera 2.2."); + addElement(symbol, weight, atomic_number, entropy298, elem_type); +} + +void Phase::addUniqueElement(const XML_Node& e) +{ + warn_deprecated("Phase::addUniqueElement", + "To be removed after Cantera 2.2."); + addElement(e); +} + void Phase::addElementsFromXML(const XML_Node& phase) { - // get the declared element names - if (! phase.hasChild("elementArray")) { - throw CanteraError("Elements::addElementsFromXML", - "phase xml node doesn't have \"elementArray\" XML Node"); - } - XML_Node& elements = phase.child("elementArray"); - vector enames; - ctml::getStringArray(elements, enames); - - // // element database defaults to elements.xml - string element_database = "elements.xml"; - if (elements.hasAttrib("datasrc")) { - element_database = elements["datasrc"]; - } - - XML_Node* doc = get_XML_File(element_database); - XML_Node* dbe = &doc->child("ctml/elementData"); - - XML_Node& root = phase.root(); - XML_Node* local_db = 0; - if (root.hasChild("ctml")) { - if (root.child("ctml").hasChild("elementData")) { - local_db = &root.child("ctml/elementData"); - } - } - - for (size_t i = 0; i < enames.size(); i++) { - XML_Node* e = 0; - if (local_db) { - e = local_db->findByAttr("name",enames[i]); - } - if (!e) { - e = dbe->findByAttr("name",enames[i]); - } - if (e) { - addUniqueElement(*e); - } else { - throw CanteraError("addElementsFromXML","no data for element " - +enames[i]); - } - } + warn_deprecated("Phase::addElementsFromXML", + "Use 'addElements' function. " + "To be removed after Cantera 2.2."); + installElements(*this, phase); } void Phase::freezeElements() { - m_elementsFrozen = true; + warn_deprecated("Phase::freezeElements", "To be removed after Cantera 2.2."); } bool Phase::elementsFrozen() { - return m_elementsFrozen; + warn_deprecated("Phase::elementsFrozen", "To be removed after Cantera 2.2."); + return false; } size_t Phase::addUniqueElementAfterFreeze(const std::string& symbol, doublereal weight, int atomicNumber, doublereal entropy298, int elem_type) { - size_t ii = elementIndex(symbol); - if (ii != npos) { - return ii; - } - // Check to see that the element isn't really in the list - m_elementsFrozen = false; - addUniqueElement(symbol, weight, atomicNumber, entropy298, elem_type); - m_elementsFrozen = true; - ii = elementIndex(symbol); - if (ii != m_mm-1) { - throw CanteraError("Phase::addElementAfterFreeze()", "index error"); - } - if (m_kk > 0) { - vector_fp old(m_speciesComp); - m_speciesComp.resize(m_kk*m_mm, 0.0); - for (size_t k = 0; k < m_kk; k++) { - size_t m_old = m_mm - 1; - for (size_t m = 0; m < m_old; m++) { - m_speciesComp[k * m_mm + m] = old[k * (m_old) + m]; - } - m_speciesComp[k * (m_mm) + (m_mm-1)] = 0.0; - } - } - return ii; + warn_deprecated("Phase::addUniqueElementAfterFreeze", + "Equivalent to Phase::addElement. " + "To be removed after Cantera 2.2"); + return addElement(symbol, weight, atomicNumber, entropy298, elem_type); } void Phase::addSpecies(const std::string& name_, const doublereal* comp, doublereal charge_, doublereal size_) { - freezeElements(); m_speciesNames.push_back(name_); m_speciesCharge.push_back(charge_); m_speciesSize.push_back(size_); @@ -914,8 +854,7 @@ void Phase::addSpecies(const std::string& name_, const doublereal* comp, } } } else { - addUniqueElementAfterFreeze("E", 0.000545, 0, 0.0, - CT_ELEM_TYPE_ELECTRONCHARGE); + addElement("E", 0.000545, 0, 0.0, CT_ELEM_TYPE_ELECTRONCHARGE); ne = nElements(); eindex = elementIndex("E"); compNew.resize(ne); @@ -979,7 +918,7 @@ void Phase::addUniqueSpecies(const std::string& name_, const doublereal* comp, bool Phase::ready() const { - return (m_kk > 0 && m_elementsFrozen); + return (m_kk > 0); } } // namespace Cantera diff --git a/src/thermo/ThermoFactory.cpp b/src/thermo/ThermoFactory.cpp index 37da68aa9..b79db0b12 100644 --- a/src/thermo/ThermoFactory.cpp +++ b/src/thermo/ThermoFactory.cpp @@ -382,7 +382,7 @@ bool importPhase(XML_Node& phase, ThermoPhase* th, * Add the elements. ***************************************************************/ if (ssConvention != cSS_CONVENTION_SLAVE) { - th->addElementsFromXML(phase); + installElements(*th, phase); } /*************************************************************** @@ -523,6 +523,73 @@ bool importPhase(XML_Node& phase, ThermoPhase* th, return true; } +void installElements(Phase& th, const XML_Node& phaseNode) +{ + // get the declared element names + if (!phaseNode.hasChild("elementArray")) { + throw CanteraError("installElements", + "phase xml node doesn't have \"elementArray\" XML Node"); + } + XML_Node& elements = phaseNode.child("elementArray"); + vector enames; + getStringArray(elements, enames); + + // // element database defaults to elements.xml + string element_database = "elements.xml"; + if (elements.hasAttrib("datasrc")) { + element_database = elements["datasrc"]; + } + + XML_Node* doc = get_XML_File(element_database); + XML_Node* dbe = &doc->child("ctml/elementData"); + + XML_Node& root = phaseNode.root(); + XML_Node* local_db = 0; + if (root.hasChild("ctml")) { + if (root.child("ctml").hasChild("elementData")) { + local_db = &root.child("ctml/elementData"); + } + } + + for (size_t i = 0; i < enames.size(); i++) { + // Find the element data + XML_Node* e = 0; + if (local_db) { + e = local_db->findByAttr("name",enames[i]); + } + if (!e) { + e = dbe->findByAttr("name",enames[i]); + } + if (!e) { + throw CanteraError("addElementsFromXML","no data for element " + +enames[i]); + } + + // Add the element + doublereal weight = 0.0; + if (e->hasAttrib("atomicWt")) { + weight = fpValue(e->attrib("atomicWt")); + } + int anum = 0; + if (e->hasAttrib("atomicNumber")) { + anum = intValue(e->attrib("atomicNumber")); + } + string symbol = e->attrib("name"); + doublereal entropy298 = ENTROPY298_UNKNOWN; + if (e->hasChild("entropy298")) { + XML_Node& e298Node = e->child("entropy298"); + if (e298Node.hasAttrib("value")) { + entropy298 = fpValueCheck(e298Node["value"]); + } + } + if (weight != 0.0) { + th.addElement(symbol, weight, anum, entropy298); + } else { + th.addElement(symbol); + } + } +} + bool installSpecies(size_t k, const XML_Node& s, thermo_t& th, SpeciesThermo* spthermo_ptr, int rule, XML_Node* phaseNode_ptr,