Add case-sensitive/lowercase logic to Phase::species

Further:
* revert unit tests to previous species definitions (some case mis-matches)
* remove non-essential comments
* opt to maintain case-sensitive species maps with lowercase as fallback
This commit is contained in:
Ingmar Schoegl 2019-09-19 12:24:26 -05:00 committed by Ray Speth
parent 76dd997692
commit 717635d3e6
6 changed files with 60 additions and 49 deletions

View file

@ -707,10 +707,6 @@ 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
@ -809,15 +805,15 @@ 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:
//! Find lowercase species name in m_speciesIndices when case sensitive
//! species names are not enforced and a user specifies a non-canonical
//! species name. Raise exception if lowercase name is not unique.
size_t findSpeciesLower(const std::string& nameStr) const;
XML_Node* m_xml; //!< XML node containing the XML info for this phase
//! ID of the phase. This is the value of the ID attribute of the XML

View file

@ -123,7 +123,7 @@ class TestThermoPhase(utilities.CanteraTest):
self.assertEqual(list(X[0,:,0]), list(Y))
def test_setCompositionString(self):
self.phase.X = 'H2:1.0, O2:1.0'
self.phase.X = 'h2:1.0, o2:1.0'
X = self.phase.X
self.assertNear(X[0], 0.5)
self.assertNear(X[3], 0.5)
@ -161,7 +161,7 @@ class TestThermoPhase(utilities.CanteraTest):
self.assertNear(Y[3], 0.75)
def test_getCompositionDict(self):
self.phase.X = 'OH:1e-9, O2:0.4, AR:0.6'
self.phase.X = 'oh:1e-9, O2:0.4, AR:0.6'
self.assertEqual(len(self.phase.mole_fraction_dict(1e-7)), 2)
self.assertEqual(len(self.phase.mole_fraction_dict()), 3)
@ -202,7 +202,7 @@ class TestThermoPhase(utilities.CanteraTest):
self.phase.Y = {'H2':1.0, 'O2':'xx'}
def test_setCompositionSlice(self):
self.phase['H2', 'O2'].X = 0.1, 0.9
self.phase['h2', 'o2'].X = 0.1, 0.9
X = self.phase.X
self.assertNear(X[0], 0.1)
self.assertNear(X[3], 0.9)

View file

@ -183,7 +183,9 @@ size_t Phase::findSpeciesLower(const std::string& name) const
loc = k.second;
} else {
throw CanteraError("Phase::findSpeciesLower",
"Lowercase species name '{}' is not unique", nLower);
"Lowercase species name '{}' is not unique. "
"Set Phase::caseSensitiveSpecies to true to "
"enforce case sensitive species names", nLower);
}
}
}
@ -193,23 +195,23 @@ size_t Phase::findSpeciesLower(const std::string& name) const
size_t Phase::speciesIndex(const std::string& nameStr) const
{
size_t loc = npos;
try {
return m_speciesIndices.at(nameStr);
} catch (std::out_of_range&) {
if (!m_caseSensitiveSpecies) {
loc = findSpeciesLower(nameStr);
}
std::map<std::string, size_t>::const_iterator it;
it = m_speciesIndices.find(nameStr);
if (it != m_speciesIndices.end()) {
return it->second;
} else if (!m_caseSensitiveSpecies) {
loc = findSpeciesLower(nameStr);
}
if (loc == npos && nameStr.find(':') != npos) {
std::string pn;
std::string sn = parseSpeciesName(nameStr, pn);
if (pn == "" || pn == m_name || pn == m_id) {
try {
return m_speciesIndices.at(sn);
} catch (std::out_of_range&) {
if (!m_caseSensitiveSpecies) {
return findSpeciesLower(sn);
}
it = m_speciesIndices.find(nameStr);
if (it != m_speciesIndices.end()) {
return it->second;
} else if (!m_caseSensitiveSpecies) {
return findSpeciesLower(sn);
}
}
}
@ -320,20 +322,14 @@ void Phase::setMoleFractions_NoNorm(const doublereal* const x)
void Phase::setMoleFractionsByName(const compositionMap& xMap)
{
vector_fp mf(m_kk, 0.0);
for (const auto& sp : xMap) {
try {
mf[m_speciesIndices.at(sp.first)] = sp.second;
} catch (std::out_of_range&) {
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;
}
size_t loc = speciesIndex(sp.first);
if (loc != npos) {
mf[loc] = sp.second;
} else {
throw CanteraError("Phase::setMoleFractionsByName",
"Unknown species '{}'", sp.first);
}
}
setMoleFractions(&mf[0]);
@ -743,7 +739,6 @@ size_t Phase::addElement(const std::string& symbol, doublereal weight,
}
bool Phase::addSpecies(shared_ptr<Species> spec) {
// species names are case sensitive
if (m_species.find(spec->name) != m_species.end()) {
throw CanteraError("Phase::addSpecies",
"Phase '{}' already contains a species named '{}'.",
@ -849,13 +844,35 @@ void Phase::modifySpecies(size_t k, shared_ptr<Species> spec)
invalidateCache();
}
//! @deprecated To be removed after Cantera 2.5.
shared_ptr<Species> Phase::species(const std::string& name) const
{
warn_deprecated("Phase::species(std::string&)",
"To be removed after Cantera 2.5. "
"Use Phase::species(size_t) instead.");
return m_species.at(name);
std::map<std::string, shared_ptr<Species> >::const_iterator it;
it = m_species.find(name);
if (it != m_species.end()) {
return it->second;
} else if (!m_caseSensitiveSpecies) {
std::string nLower = toLowerCopy(name);
bool found = false;
shared_ptr<Species> sp;
for (const auto& k : m_species) {
if (toLowerCopy(k.first) == nLower) {
if (!found) {
found = true;
sp = k.second;
} else {
throw CanteraError("Phase::species",
"Lowercase species name '{}' is not unique. "
"Set Phase::caseSensitiveSpecies to true to "
"enforce case sensitive species names", nLower);
}
}
}
return sp;
} else {
throw CanteraError("Phase::setMassFractionsByName",
"Unknown species '{}'", name);
}
}
shared_ptr<Species> Phase::species(size_t k) const

View file

@ -710,8 +710,6 @@ vector<double> RedlichKwongMFTP::getCoeff(const std::string& iName)
// based on crit properties T_c and P_c:
for (size_t isp = 0; isp < nDatabase; isp++) {
XML_Node& acNodeDoc = doc->child(isp);
// not enforcing case sensitive species names as this is external
// to CTI or YAML input files
std::string iNameLower = toLowerCopy(iName);
std::string dbName = toLowerCopy(acNodeDoc.attrib("name"));

View file

@ -40,7 +40,7 @@ TEST_F(TestThermoMethods, getMoleFractionsByName)
EXPECT_DOUBLE_EQ(X["H2"], 0.3);
EXPECT_DOUBLE_EQ(X["AR"], 0.5);
thermo->setMoleFractionsByName("OH:1e-9, O2:0.2, H2:0.3, AR:0.5");
thermo->setMoleFractionsByName("OH:1e-9, O2:0.2, h2:0.3, AR:0.5");
X = thermo->getMoleFractionsByName();
EXPECT_EQ(X.size(), (size_t) 4);

View file

@ -299,7 +299,7 @@ TEST_F(ConstructFromScratch, addUndefinedElements)
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")));
ASSERT_EQ((size_t) 2, p.nAtoms(p.speciesIndex("co2"), p.elementIndex("O")));
p.setMassFractionsByName("H2:0.5, CO2:0.5");
ASSERT_DOUBLE_EQ(0.5, p.massFraction("CO2"));
}