Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion include/cantera/thermo/Phase.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -747,6 +747,17 @@ class Phase
//! 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.
/*!
Expand Down Expand Up @@ -794,7 +805,15 @@ class Phase
//! Flag determining behavior when adding species with an undefined element
UndefElement::behavior m_undefinedElementBehavior;

//! 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
Expand Down Expand Up @@ -840,6 +859,9 @@ class Phase
//! Map of species names to indices
std::map<std::string, size_t> m_speciesIndices;

//! Map of lower-case species names to indices
std::map<std::string, size_t> m_speciesLower;

size_t m_mm; //!< Number of elements.
vector_fp m_atomicWeights; //!< element atomic weights (kg kmol-1)
vector_int m_atomicNumbers; //!< element atomic numbers
Expand Down
2 changes: 2 additions & 0 deletions interfaces/cython/cantera/_cantera.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera":
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()
Expand Down
33 changes: 33 additions & 0 deletions interfaces/cython/cantera/test/test_thermo.py
Original file line number Diff line number Diff line change
Expand Up @@ -1253,6 +1253,39 @@ def test_stringify_bad(self):
with self.assertRaises(AttributeError):
ct.Solution(3)

def test_case_sensitive_names(self):
gas = ct.Solution('h2o2.xml')
self.assertFalse(gas.case_sensitive_species_names)
self.assertTrue(gas.species_index('h2') == 0)
gas.X = 'h2:.5, o2:.5'
self.assertNear(gas.X[0], 0.5)
gas.Y = 'h2:.5, o2:.5'
self.assertNear(gas.Y[0], 0.5)

gas.case_sensitive_species_names = True
self.assertTrue(gas.case_sensitive_species_names)
with self.assertRaises(ValueError):
gas.species_index('h2')
with self.assertRaisesRegex(ct.CanteraError, 'Unknown species'):
gas.X = 'h2:1.0, o2:1.0'
with self.assertRaisesRegex(ct.CanteraError, 'Unknown species'):
gas.Y = 'h2:1.0, o2:1.0'

gas_cti = """ideal_gas(
name="gas",
elements=" S C Cs ",
species=" nasa: all ",
options=["skip_undeclared_elements"],
initial_state=state(temperature=300, pressure=(1, "bar"))
)"""
ct.suppress_thermo_warnings(True)
gas = ct.Solution(source=gas_cti)
with self.assertRaisesRegex(ct.CanteraError, 'is not unique'):
gas.species_index('cs')
gas.case_sensitive_species_names = True
with self.assertRaises(ValueError):
gas.species_index('cs')


class TestElement(utilities.CanteraTest):
@classmethod
Expand Down
9 changes: 8 additions & 1 deletion interfaces/cython/cantera/thermo.pyx
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
87 changes: 66 additions & 21 deletions src/thermo/Phase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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),
Expand Down Expand Up @@ -172,20 +173,48 @@ void Phase::getAtoms(size_t k, double* atomArray) const
}
}

size_t Phase::findSpeciesLower(const std::string& name) const
{
std::string nLower = toLowerCopy(name);
size_t loc = npos;
auto it = m_speciesLower.find(nLower);
if (it == m_speciesLower.end()) {
return npos;
} else {
loc = it->second;
if (loc == npos) {
throw CanteraError("Phase::findSpeciesLower",
"Lowercase species name '{}' is not unique. "
"Set Phase::caseSensitiveSpecies to true to "
"enforce case sensitive species names", 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;

auto it = m_speciesIndices.find(nameStr);
if (it != m_speciesIndices.end()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a good place to use auto instead of writing out the complex iterator type.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✨ ... didn't know that one.

return it->second;
} else 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;
it = m_speciesIndices.find(nameStr);
if (it != m_speciesIndices.end()) {
return it->second;
} else if (!m_caseSensitiveSpecies) {
return findSpeciesLower(sn);
}
}
} else {
return loc;
}
return loc;
}

string Phase::speciesName(size_t k) const
Expand Down Expand Up @@ -292,10 +321,12 @@ 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(toLowerCopy(sp.first))] = sp.second;
} catch (std::out_of_range&) {
size_t loc = speciesIndex(sp.first);
if (loc != npos) {
mf[loc] = sp.second;
} else {
throw CanteraError("Phase::setMoleFractionsByName",
"Unknown species '{}'", sp.first);
}
Expand Down Expand Up @@ -337,9 +368,10 @@ 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;
} catch (std::out_of_range&) {
size_t loc = speciesIndex(sp.first);
if (loc != npos) {
mf[loc] = sp.second;
} else {
throw CanteraError("Phase::setMassFractionsByName",
"Unknown species '{}'", sp.first);
}
Expand Down Expand Up @@ -699,7 +731,7 @@ 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()) {
if (m_species.find(spec->name) != m_species.end()) {
throw CanteraError("Phase::addSpecies",
"Phase '{}' already contains a species named '{}'.",
m_name, spec->name);
Expand Down Expand Up @@ -729,11 +761,18 @@ 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();

std::string nLower = toLowerCopy(spec->name);
if (m_speciesLower.find(nLower) == m_speciesLower.end()) {
m_speciesLower[nLower] = m_kk;
} else {
m_speciesLower[nLower] = npos;
}

double wt = 0.0;
const vector_fp& aw = atomicWeights();
if (spec->charge != 0.0) {
Expand Down Expand Up @@ -794,24 +833,30 @@ 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();
}

shared_ptr<Species> Phase::species(const std::string& name) const
{
return m_species.at(toLowerCopy(name));
size_t k = speciesIndex(name);
if (k != npos) {
return m_species.at(speciesName(k));
} else {
throw CanteraError("Phase::setMassFractionsByName",
"Unknown species '{}'", 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() {
Expand Down
11 changes: 9 additions & 2 deletions src/thermo/RedlichKwongMFTP.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! @file RedlichKwongMFTP.cpp

// 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/RedlichKwongMFTP.h"
#include "cantera/thermo/ThermoFactory.h"
Expand Down Expand Up @@ -718,7 +718,8 @@ vector<double> RedlichKwongMFTP::getCoeff(const std::string& iName)
if (iNameLower == dbName) {
// Read from database and calculate a and b coefficients
double vParams;
double T_crit, P_crit;
double T_crit=0.;
double P_crit=0.;

if (acNodeDoc.hasChild("Tc")) {
vParams = 0.0;
Expand All @@ -734,6 +735,9 @@ vector<double> RedlichKwongMFTP::getCoeff(const std::string& iName)
"Critical Temperature must be positive ");
}
T_crit = vParams;
} else {
throw CanteraError("RedlichKwongMFTP::GetCoeff",
"Critical Temperature not in database ");
}
if (acNodeDoc.hasChild("Pc")) {
vParams = 0.0;
Expand All @@ -749,6 +753,9 @@ vector<double> RedlichKwongMFTP::getCoeff(const std::string& iName)
"Critical Pressure must be positive ");
}
P_crit = vParams;
} else {
throw CanteraError("RedlichKwongMFTP::GetCoeff",
"Critical Pressure not in database ");
}

//Assuming no temperature dependence
Expand Down
2 changes: 1 addition & 1 deletion src/transport/GasTransport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down