diff --git a/include/cantera/thermo/Phase.h b/include/cantera/thermo/Phase.h index d6f3a6781..9f9c1d7ea 100644 --- a/include/cantera/thermo/Phase.h +++ b/include/cantera/thermo/Phase.h @@ -4,7 +4,7 @@ */ // This file is part of Cantera. See License.txt in the top-level directory or -// at http://www.cantera.org/license.txt for license and copyright information. +// at https://cantera.org/license.txt for license and copyright information. #ifndef CT_PHASE_H #define CT_PHASE_H @@ -707,6 +707,10 @@ public: //! Return the Species object for the named species. Changes to this object //! do not affect the ThermoPhase object until the #modifySpecies function //! is called. + /*! + * @deprecated To be removed after Cantera 2.5. Replaceable with + * Phase::species(size_t) via Phase::speciesIndex(std::string&). + */ shared_ptr species(const std::string& name) const; //! Return the Species object for species whose index is *k*. Changes to @@ -747,6 +751,17 @@ public: //! change in state is detected virtual void invalidateCache(); + //! Returns `true` if case sensitive species names are enforced + bool caseSensitiveSpecies() const { + return m_caseSensitiveSpecies; + } + + //! Set flag that determines whether case sensitive species are enforced + //! in look-up operations, e.g. speciesIndex + void setCaseSensitiveSpecies(bool cflag = true) { + m_caseSensitiveSpecies = cflag; + } + protected: //! Cached for saved calculations within each ThermoPhase. /*! @@ -794,6 +809,14 @@ protected: //! Flag determining behavior when adding species with an undefined element UndefElement::behavior m_undefinedElementBehavior; + //! Find lowercase species name in m_speciesIndices when case sensitive + //! species names are not enforced. Raise exception if lowercase name + //! is not unique. + size_t findSpeciesLower(const std::string& nameStr) const; + + //! Flag determining whether case sensitive species names are enforced + bool m_caseSensitiveSpecies; + private: XML_Node* m_xml; //!< XML node containing the XML info for this phase diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 26eff9b62..af5f9c147 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -165,12 +165,13 @@ cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera": # species properties size_t nSpecies() - shared_ptr[CxxSpecies] species(string) except +translate_exception shared_ptr[CxxSpecies] species(size_t) except +translate_exception size_t speciesIndex(string) except +translate_exception string speciesName(size_t) except +translate_exception double nAtoms(size_t, size_t) except +translate_exception void getAtoms(size_t, double*) except +translate_exception + cbool caseSensitiveSpecies() + void setCaseSensitiveSpecies(cbool) double molecularWeight(size_t) except +translate_exception double meanMolecularWeight() diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index b8db5d685..f54d6b332 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -1,5 +1,5 @@ # This file is part of Cantera. See License.txt in the top-level directory or -# at http://www.cantera.org/license.txt for license and copyright information. +# at https://cantera.org/license.txt for license and copyright information. import warnings import weakref @@ -470,6 +470,13 @@ cdef class ThermoPhase(_SolutionBase): return index + property case_sensitive_species_names: + """Enforce case-sensitivity for look up of species names""" + def __get__(self): + return self.thermo.caseSensitiveSpecies() + def __set__(self, val): + self.thermo.setCaseSensitiveSpecies(bool(val)) + def species(self, k=None): """ Return the `Species` object for species *k*, where *k* is either the @@ -483,7 +490,8 @@ cdef class ThermoPhase(_SolutionBase): s = Species(init=False) if isinstance(k, (str, bytes)): - s._assign(self.thermo.species(stringify(k))) + kk = self.thermo.speciesIndex(stringify(k)) + s._assign(self.thermo.species(kk)) elif isinstance(k, (int, float)): s._assign(self.thermo.species(k)) else: diff --git a/src/thermo/Phase.cpp b/src/thermo/Phase.cpp index 880bdfae9..ffe01fe10 100644 --- a/src/thermo/Phase.cpp +++ b/src/thermo/Phase.cpp @@ -4,7 +4,7 @@ */ // This file is part of Cantera. See License.txt in the top-level directory or -// at http://www.cantera.org/license.txt for license and copyright information. +// at https://cantera.org/license.txt for license and copyright information. #include "cantera/thermo/Phase.h" #include "cantera/base/utilities.h" @@ -21,6 +21,7 @@ Phase::Phase() : m_kk(0), m_ndim(3), m_undefinedElementBehavior(UndefElement::add), + m_caseSensitiveSpecies(false), m_xml(new XML_Node("phase")), m_id(""), m_temp(0.001), @@ -172,20 +173,47 @@ void Phase::getAtoms(size_t k, double* atomArray) const } } +size_t Phase::findSpeciesLower(const std::string& name) const +{ + size_t loc = npos; + std::string nLower = toLowerCopy(name); + for (const auto& k : m_speciesIndices) { + if (toLowerCopy(k.first) == nLower) { + if (loc == npos) { + loc = k.second; + } else { + throw CanteraError("Phase::findSpeciesLower", + "Lowercase species name '{}' is not unique", nLower); + } + } + } + return loc; +} + size_t Phase::speciesIndex(const std::string& nameStr) const { - size_t loc = getValue(m_speciesIndices, toLowerCopy(nameStr), npos); + size_t loc = npos; + try { + return m_speciesIndices.at(nameStr); + } catch (std::out_of_range&) { + if (!m_caseSensitiveSpecies) { + loc = findSpeciesLower(nameStr); + } + } if (loc == npos && nameStr.find(':') != npos) { std::string pn; - std::string sn = toLowerCopy(parseSpeciesName(nameStr, pn)); + std::string sn = parseSpeciesName(nameStr, pn); if (pn == "" || pn == m_name || pn == m_id) { - return getValue(m_speciesIndices, sn, npos); - } else { - return npos; + try { + return m_speciesIndices.at(sn); + } catch (std::out_of_range&) { + if (!m_caseSensitiveSpecies) { + return findSpeciesLower(sn); + } + } } - } else { - return loc; } + return loc; } string Phase::speciesName(size_t k) const @@ -294,10 +322,18 @@ void Phase::setMoleFractionsByName(const compositionMap& xMap) vector_fp mf(m_kk, 0.0); for (const auto& sp : xMap) { try { - mf[m_speciesIndices.at(toLowerCopy(sp.first))] = sp.second; + mf[m_speciesIndices.at(sp.first)] = sp.second; } catch (std::out_of_range&) { - throw CanteraError("Phase::setMoleFractionsByName", - "Unknown species '{}'", sp.first); + size_t loc = npos; + if (!m_caseSensitiveSpecies) { + loc = findSpeciesLower(sp.first); + } + if (loc == npos) { + throw CanteraError("Phase::setMoleFractionsByName", + "Unknown species '{}'", sp.first); + } else { + mf[loc] = sp.second; + } } } setMoleFractions(&mf[0]); @@ -338,10 +374,18 @@ void Phase::setMassFractionsByName(const compositionMap& yMap) vector_fp mf(m_kk, 0.0); for (const auto& sp : yMap) { try { - mf[m_speciesIndices.at(toLowerCopy(sp.first))] = sp.second; + mf[m_speciesIndices.at(sp.first)] = sp.second; } catch (std::out_of_range&) { - throw CanteraError("Phase::setMassFractionsByName", - "Unknown species '{}'", sp.first); + size_t loc = npos; + if (!m_caseSensitiveSpecies) { + loc = findSpeciesLower(sp.first); + } + if (loc == npos) { + throw CanteraError("Phase::setMassFractionsByName", + "Unknown species '{}'", sp.first); + } else { + mf[loc] = sp.second; + } } } setMassFractions(&mf[0]); @@ -699,7 +743,8 @@ size_t Phase::addElement(const std::string& symbol, doublereal weight, } bool Phase::addSpecies(shared_ptr spec) { - if (m_species.find(toLowerCopy(spec->name)) != m_species.end()) { + // species names are case sensitive + if (m_species.find(spec->name) != m_species.end()) { throw CanteraError("Phase::addSpecies", "Phase '{}' already contains a species named '{}'.", m_name, spec->name); @@ -729,8 +774,8 @@ bool Phase::addSpecies(shared_ptr spec) { } m_speciesNames.push_back(spec->name); - m_species[toLowerCopy(spec->name)] = spec; - m_speciesIndices[toLowerCopy(spec->name)] = m_kk; + m_species[spec->name] = spec; + m_speciesIndices[spec->name] = m_kk; m_speciesCharge.push_back(spec->charge); size_t ne = nElements(); @@ -794,24 +839,28 @@ void Phase::modifySpecies(size_t k, shared_ptr spec) "New species name '{}' does not match existing name '{}'", spec->name, speciesName(k)); } - const shared_ptr& old = m_species[toLowerCopy(spec->name)]; + const shared_ptr& old = m_species[spec->name]; if (spec->composition != old->composition) { throw CanteraError("Phase::modifySpecies", "New composition for '{}' does not match existing composition", spec->name); } - m_species[toLowerCopy(spec->name)] = spec; + m_species[spec->name] = spec; invalidateCache(); } +//! @deprecated To be removed after Cantera 2.5. shared_ptr Phase::species(const std::string& name) const { - return m_species.at(toLowerCopy(name)); + warn_deprecated("Phase::species(std::string&)", + "To be removed after Cantera 2.5. " + "Use Phase::species(size_t) instead."); + return m_species.at(name); } shared_ptr Phase::species(size_t k) const { - return species(m_speciesNames[k]); + return m_species.at(m_speciesNames[k]); } void Phase::ignoreUndefinedElements() { diff --git a/src/transport/GasTransport.cpp b/src/transport/GasTransport.cpp index f8447a884..6cc8222bc 100644 --- a/src/transport/GasTransport.cpp +++ b/src/transport/GasTransport.cpp @@ -395,7 +395,7 @@ void GasTransport::setupCollisionIntegral() void GasTransport::getTransportData() { for (size_t k = 0; k < m_thermo->nSpecies(); k++) { - shared_ptr s = m_thermo->species(m_thermo->speciesName(k)); + shared_ptr s = m_thermo->species(k); const GasTransportData* sptran = dynamic_cast(s->transport.get()); if (!sptran) {