[Thermo] add flag that makes species names case sensitive

* store species information with case sensitive names
* retain lookup for non-case sensitive species names, e.g. Phase::speciesIndex
* implement flag that enforces case sensitive species names as a member
variable of Phase
* add exception handling for species that are not uniquely defined unless case
sensitive (e.g. Cs and CS in nasa.cti if cs is specified and case sensitivity
is not enforced)
* deprecate Phase::species(std::string&)
This commit is contained in:
Ingmar Schoegl 2019-08-11 18:41:54 -05:00 committed by Ray Speth
parent e0fe5eed59
commit 2231141e32
5 changed files with 107 additions and 26 deletions

View file

@ -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> 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

View file

@ -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()

View file

@ -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(<int>kk))
elif isinstance(k, (int, float)):
s._assign(self.thermo.species(<int>k))
else:

View file

@ -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("<phase>"),
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<Species> 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<Species> 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<Species> spec)
"New species name '{}' does not match existing name '{}'",
spec->name, speciesName(k));
}
const shared_ptr<Species>& old = m_species[toLowerCopy(spec->name)];
const shared_ptr<Species>& 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<Species> 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<Species> Phase::species(size_t k) const
{
return species(m_speciesNames[k]);
return m_species.at(m_speciesNames[k]);
}
void Phase::ignoreUndefinedElements() {

View file

@ -395,7 +395,7 @@ void GasTransport::setupCollisionIntegral()
void GasTransport::getTransportData()
{
for (size_t k = 0; k < m_thermo->nSpecies(); k++) {
shared_ptr<Species> s = m_thermo->species(m_thermo->speciesName(k));
shared_ptr<Species> s = m_thermo->species(k);
const GasTransportData* sptran =
dynamic_cast<GasTransportData*>(s->transport.get());
if (!sptran) {