Skip to content

Commit 40cf2ed

Browse files
author
Ingmar Schoegl
committed
[Thermo] make species names case sensitive
* store species information with case sensitive names * enable 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)
1 parent a2c33b9 commit 40cf2ed

4 files changed

Lines changed: 114 additions & 22 deletions

File tree

include/cantera/thermo/Phase.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
// This file is part of Cantera. See License.txt in the top-level directory or
7-
// at http://www.cantera.org/license.txt for license and copyright information.
7+
// at https://cantera.org/license.txt for license and copyright information.
88

99
#ifndef CT_PHASE_H
1010
#define CT_PHASE_H
@@ -747,6 +747,17 @@ class Phase
747747
//! change in state is detected
748748
virtual void invalidateCache();
749749

750+
//! Returns `true` if case sensitive species names are enforced
751+
bool caseSensitiveSpecies() const {
752+
return m_caseSensitiveSpecies;
753+
}
754+
755+
//! Set flag that determines whether case sensitive species are enforced
756+
//! in look-up operations, e.g. speciesIndex
757+
void setCaseSensitiveSpecies(bool cflag = true) {
758+
m_caseSensitiveSpecies = cflag;
759+
}
760+
750761
protected:
751762
//! Cached for saved calculations within each ThermoPhase.
752763
/*!
@@ -794,6 +805,14 @@ class Phase
794805
//! Flag determining behavior when adding species with an undefined element
795806
UndefElement::behavior m_undefinedElementBehavior;
796807

808+
//! Find lowercase species name in m_speciesIndices when case sensitive
809+
//! species names are not enforced. Raise exception if lowercase name
810+
//! is not unique.
811+
size_t findSpeciesLower(const std::string& nameStr) const;
812+
813+
//! Flag determining whether case sensitive species names are enforced
814+
bool m_caseSensitiveSpecies;
815+
797816
private:
798817
XML_Node* m_xml; //!< XML node containing the XML info for this phase
799818

interfaces/cython/cantera/_cantera.pxd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera":
171171
string speciesName(size_t) except +translate_exception
172172
double nAtoms(size_t, size_t) except +translate_exception
173173
void getAtoms(size_t, double*) except +translate_exception
174+
cbool caseSensitiveSpecies()
175+
void setCaseSensitiveSpecies(cbool)
174176

175177
double molecularWeight(size_t) except +translate_exception
176178
double meanMolecularWeight()

interfaces/cython/cantera/thermo.pyx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# This file is part of Cantera. See License.txt in the top-level directory or
2-
# at http://www.cantera.org/license.txt for license and copyright information.
2+
# at https://cantera.org/license.txt for license and copyright information.
33

44
import warnings
55
import weakref
@@ -470,6 +470,13 @@ cdef class ThermoPhase(_SolutionBase):
470470

471471
return index
472472

473+
property case_sensitive_species_names:
474+
"""Enforce case-sensitivity for look up of species names"""
475+
def __get__(self):
476+
return self.thermo.caseSensitiveSpecies()
477+
def __set__(self, val):
478+
self.thermo.setCaseSensitiveSpecies(bool(val))
479+
473480
def species(self, k=None):
474481
"""
475482
Return the `Species` object for species *k*, where *k* is either the

src/thermo/Phase.cpp

Lines changed: 84 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
// This file is part of Cantera. See License.txt in the top-level directory or
7-
// at http://www.cantera.org/license.txt for license and copyright information.
7+
// at https://cantera.org/license.txt for license and copyright information.
88

99
#include "cantera/thermo/Phase.h"
1010
#include "cantera/base/utilities.h"
@@ -21,6 +21,7 @@ Phase::Phase() :
2121
m_kk(0),
2222
m_ndim(3),
2323
m_undefinedElementBehavior(UndefElement::add),
24+
m_caseSensitiveSpecies(true),
2425
m_xml(new XML_Node("phase")),
2526
m_id("<phase>"),
2627
m_temp(0.001),
@@ -172,20 +173,53 @@ void Phase::getAtoms(size_t k, double* atomArray) const
172173
}
173174
}
174175

176+
size_t Phase::findSpeciesLower(const std::string& name) const
177+
{
178+
size_t loc = npos;
179+
std::string nLower = toLowerCopy(name);
180+
for (const auto& k : m_speciesIndices) {
181+
if (toLowerCopy(k.first) == nLower) {
182+
if (loc == npos) {
183+
loc = k.second;
184+
} else {
185+
throw CanteraError("Phase::findSpeciesLower",
186+
"Lowercase species name '{}' is not unique", nLower);
187+
}
188+
}
189+
}
190+
return loc;
191+
}
192+
175193
size_t Phase::speciesIndex(const std::string& nameStr) const
176194
{
177-
size_t loc = getValue(m_speciesIndices, toLowerCopy(nameStr), npos);
195+
size_t loc = npos;
196+
try {
197+
return m_speciesIndices.at(nameStr);
198+
} catch (std::out_of_range&) {
199+
if (!m_caseSensitiveSpecies) {
200+
loc = findSpeciesLower(nameStr);
201+
} else {
202+
// @TODO: trigger deprecation warning
203+
return findSpeciesLower(nameStr);
204+
}
205+
}
178206
if (loc == npos && nameStr.find(':') != npos) {
179207
std::string pn;
180-
std::string sn = toLowerCopy(parseSpeciesName(nameStr, pn));
208+
std::string sn = parseSpeciesName(nameStr, pn);
181209
if (pn == "" || pn == m_name || pn == m_id) {
182-
return getValue(m_speciesIndices, sn, npos);
183-
} else {
184-
return npos;
210+
try {
211+
return m_speciesIndices.at(sn);
212+
} catch (std::out_of_range&) {
213+
if (!m_caseSensitiveSpecies) {
214+
return findSpeciesLower(sn);
215+
} else {
216+
// @TODO: trigger deprecation warning
217+
return findSpeciesLower(sn);
218+
}
219+
}
185220
}
186-
} else {
187-
return loc;
188221
}
222+
return loc;
189223
}
190224

191225
string Phase::speciesName(size_t k) const
@@ -294,10 +328,21 @@ void Phase::setMoleFractionsByName(const compositionMap& xMap)
294328
vector_fp mf(m_kk, 0.0);
295329
for (const auto& sp : xMap) {
296330
try {
297-
mf[m_speciesIndices.at(toLowerCopy(sp.first))] = sp.second;
331+
mf[m_speciesIndices.at(sp.first)] = sp.second;
298332
} catch (std::out_of_range&) {
299-
throw CanteraError("Phase::setMoleFractionsByName",
300-
"Unknown species '{}'", sp.first);
333+
size_t loc = npos;
334+
if (!m_caseSensitiveSpecies) {
335+
loc = findSpeciesLower(sp.first);
336+
} else {
337+
// @TODO: trigger deprecation warning
338+
loc = findSpeciesLower(sp.first);
339+
}
340+
if (loc == npos) {
341+
throw CanteraError("Phase::setMoleFractionsByName",
342+
"Unknown species '{}'", sp.first);
343+
} else {
344+
mf[loc] = sp.second;
345+
}
301346
}
302347
}
303348
setMoleFractions(&mf[0]);
@@ -338,10 +383,21 @@ void Phase::setMassFractionsByName(const compositionMap& yMap)
338383
vector_fp mf(m_kk, 0.0);
339384
for (const auto& sp : yMap) {
340385
try {
341-
mf[m_speciesIndices.at(toLowerCopy(sp.first))] = sp.second;
386+
mf[m_speciesIndices.at(sp.first)] = sp.second;
342387
} catch (std::out_of_range&) {
343-
throw CanteraError("Phase::setMassFractionsByName",
344-
"Unknown species '{}'", sp.first);
388+
size_t loc = npos;
389+
if (!m_caseSensitiveSpecies) {
390+
loc = findSpeciesLower(sp.first);
391+
} else {
392+
// @TODO: trigger deprecation warning
393+
loc = findSpeciesLower(sp.first);
394+
}
395+
if (loc == npos) {
396+
throw CanteraError("Phase::setMassFractionsByName",
397+
"Unknown species '{}'", sp.first);
398+
} else {
399+
mf[loc] = sp.second;
400+
}
345401
}
346402
}
347403
setMassFractions(&mf[0]);
@@ -699,7 +755,8 @@ size_t Phase::addElement(const std::string& symbol, doublereal weight,
699755
}
700756

701757
bool Phase::addSpecies(shared_ptr<Species> spec) {
702-
if (m_species.find(toLowerCopy(spec->name)) != m_species.end()) {
758+
// species names are case sensitive
759+
if (m_species.find(spec->name) != m_species.end()) {
703760
throw CanteraError("Phase::addSpecies",
704761
"Phase '{}' already contains a species named '{}'.",
705762
m_name, spec->name);
@@ -729,8 +786,8 @@ bool Phase::addSpecies(shared_ptr<Species> spec) {
729786
}
730787

731788
m_speciesNames.push_back(spec->name);
732-
m_species[toLowerCopy(spec->name)] = spec;
733-
m_speciesIndices[toLowerCopy(spec->name)] = m_kk;
789+
m_species[spec->name] = spec;
790+
m_speciesIndices[spec->name] = m_kk;
734791
m_speciesCharge.push_back(spec->charge);
735792
size_t ne = nElements();
736793

@@ -794,19 +851,26 @@ void Phase::modifySpecies(size_t k, shared_ptr<Species> spec)
794851
"New species name '{}' does not match existing name '{}'",
795852
spec->name, speciesName(k));
796853
}
797-
const shared_ptr<Species>& old = m_species[toLowerCopy(spec->name)];
854+
const shared_ptr<Species>& old = m_species[spec->name];
798855
if (spec->composition != old->composition) {
799856
throw CanteraError("Phase::modifySpecies",
800857
"New composition for '{}' does not match existing composition",
801858
spec->name);
802859
}
803-
m_species[toLowerCopy(spec->name)] = spec;
860+
m_species[spec->name] = spec;
804861
invalidateCache();
805862
}
806863

807864
shared_ptr<Species> Phase::species(const std::string& name) const
808865
{
809-
return m_species.at(toLowerCopy(name));
866+
try {
867+
return m_species.at(name);
868+
} catch (std::out_of_range&) {
869+
// @TODO: decide on whether to allow for non-case sensitive species
870+
throw CanteraError("Phase::species",
871+
"Unknown species '{}' - case sensitive name is enforced",
872+
name);
873+
}
810874
}
811875

812876
shared_ptr<Species> Phase::species(size_t k) const

0 commit comments

Comments
 (0)