-
-
Notifications
You must be signed in to change notification settings - Fork 410
Save case sensitive species names #689
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
a2c33b9
84cbab3
cacffb7
919cadb
55e7a30
fb9c749
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,49 @@ 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 { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than falling back to an O(N) search anytime a non-canonical species name is provided, I wonder if the following might be a cleaner solution:
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure about this suggestion. The second (lower-case) species map would contain nearly identical information; in cases like this, I usually keep the information in a single representation, as it is easier to maintain. While the suggested implementation would be faster if a user opts to use species names with names other than those specified in the input file, I doubt that it has significant real-world benefits. Assuming that those searches are typically done during problem setup, there is little computational overhead (in repeated operations, it is always faster to select species by index). I am also unsure about auto-magically flipping flags, as those decisions may be best left to the user? Weighing cost and benefit, I am suggesting to leave the implementation as is.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, there is a benefit to not introducing a new data structure. However, avoiding that requires several instances of this kind of convoluted try/catch construct (with I think another one needed in I think using the extra data structure would allow us to avoid adding There's also some value in not generating exceptions for circumstances that aren't really exceptional (at least in C++). I'm less concerned about the performance implication than with the ability to run in gdb and break at an error by simply doing I would also note that My thought on automatically enabling case sensitivity when there are species that can only be resolved this way was that that might be less surprising to the user than having to enable it explicitly. There may also be issues with initializing phases with species that are only distinguishable by case. For instance, what if a reaction is specified (in the input file) that contains one of these species? Adding the reaction requires looking up the indices of the involved species, and this needs to happen before the user has the chance to set the case sensitive species flag.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @speth ... thanks for your comments (plus catching the other access-species-by-name case for Your comment regarding C++ exceptions makes a lot of sense. In the current revision, I opted to replace I do realize that the kinetics managers make extensive use of Regarding the automatic flags: my thought was that in most cases, case sensitive species are used by default internally and the fallback solution is rarely used. I updated the error message to explicitly direct users towards the Finally, at this point the case-sensitive/lowercase logic is exclusively handled within
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Regarding the "automatic flags", I had to play around with this a bit to realize how this was now working - an exception is only thrown when case sensitivity is disabled and you specify a name which is not a case-sensitive match for any species. So my concern about setting up reactions is moot, and I agree that there is no need to automatically toggle this flag. I still think the |
||
| 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; | ||
| std::map<std::string, size_t>::const_iterator it; | ||
|
|
||
| it = m_speciesIndices.find(nameStr); | ||
| if (it != m_speciesIndices.end()) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is a good place to use
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
|
@@ -292,10 +322,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); | ||
| } | ||
|
|
@@ -338,10 +370,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); | ||
|
ischoegl marked this conversation as resolved.
Outdated
|
||
| } | ||
| if (loc == npos) { | ||
| throw CanteraError("Phase::setMassFractionsByName", | ||
| "Unknown species '{}'", sp.first); | ||
| } else { | ||
| mf[loc] = sp.second; | ||
| } | ||
| } | ||
| } | ||
| setMassFractions(&mf[0]); | ||
|
|
@@ -699,7 +739,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()) { | ||
|
ischoegl marked this conversation as resolved.
|
||
| throw CanteraError("Phase::addSpecies", | ||
| "Phase '{}' already contains a species named '{}'.", | ||
| m_name, spec->name); | ||
|
|
@@ -729,8 +769,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 +834,50 @@ 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)); | ||
| 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); | ||
| } | ||
|
ischoegl marked this conversation as resolved.
|
||
| } | ||
|
|
||
| shared_ptr<Species> Phase::species(size_t k) const | ||
| { | ||
| return species(m_speciesNames[k]); | ||
| return m_species.at(m_speciesNames[k]); | ||
| } | ||
|
|
||
| void Phase::ignoreUndefinedElements() { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.