diff --git a/include/cantera/base/AnyMap.h b/include/cantera/base/AnyMap.h index 8a809dfb2..d533b73c5 100644 --- a/include/cantera/base/AnyMap.h +++ b/include/cantera/base/AnyMap.h @@ -23,6 +23,7 @@ namespace Cantera { class AnyMap; +class InputFile; //! A wrapper for a variable whose type is determined at runtime /*! @@ -52,6 +53,8 @@ public: // The value knows the name of its corresponding key in order to provide // comprehensible error messages. void setKey(const std::string& key); + void setLoc(int line, int column); + void setFile(shared_ptr& file); template const T& as() const; @@ -129,9 +132,14 @@ private: template void checkSize(const std::vector& v, size_t nMin, size_t nMax) const; + int m_line; + int m_column; + shared_ptr m_file; std::string m_key; std::unique_ptr m_value; static std::map s_typenames; + + friend class InputFileError; }; // Implicit conversion to vector @@ -252,6 +260,10 @@ public: //! Return a string listing the keys in this AnyMap, e.g. for use in error //! messages std::string keys_str() const; + void setLoc(int line, int column); + void setFile(shared_ptr& file); + void setFileName(const std::string& filename); + void setFileContents(const std::string& contents); bool getBool(const std::string& key, bool default_) const; long int getInt(const std::string& key, long int default_) const; @@ -327,6 +339,9 @@ public: private: std::unordered_map m_data; UnitSystem m_units; + int m_line; + int m_column; + shared_ptr m_file; //! Cache for previously-parsed input (YAML) files. The key is the full path //! to the file, and the second element of the value is the last-modified @@ -334,12 +349,45 @@ private: static std::unordered_map> s_cache; friend class AnyValue; + friend class InputFileError; }; // Define begin() and end() to allow use with range-based for loops AnyMap::const_iterator begin(const AnyValue& v); AnyMap::const_iterator end(const AnyValue& v); +class InputFileError : public CanteraError +{ +public: + template + InputFileError(const std::string& procedure, const AnyValue& node, + const std::string& message, const Args&... args) + : CanteraError( + procedure, + formatError(fmt::format(message, args...), + node.m_line, node.m_column, node.m_file)) + { + } + + template + InputFileError(const std::string& procedure, const AnyMap& node, + const std::string& message, const Args&... args) + : CanteraError( + procedure, + formatError(fmt::format(message, args...), + node.m_line, node.m_column, node.m_file)) + { + } + + virtual std::string getClass() const { + return "InputFileError"; + } +protected: + static std::string formatError(const std::string& message, + int line, int column, + const shared_ptr& file); +}; + } #ifndef CANTERA_API_NO_BOOST diff --git a/include/cantera/base/AnyMap.inl.h b/include/cantera/base/AnyMap.inl.h index dd9c09aac..6982c708c 100644 --- a/include/cantera/base/AnyMap.inl.h +++ b/include/cantera/base/AnyMap.inl.h @@ -24,11 +24,12 @@ const T &AnyValue::as() const { } catch (boost::bad_any_cast&) { if (m_value->type() == typeid(void)) { // Values that have not been set are of type 'void' - throw CanteraError("AnyValue::as", "Key '{}' not found", m_key); + throw InputFileError("AnyValue::as", *this, + "Key '{}' not found", m_key); } else { - throw CanteraError("AnyValue::as", - "Key '{}' contains a '{}',\nnot a '{}'.", - m_key, demangle(m_value->type()), demangle(typeid(T))); + throw InputFileError("AnyValue::as", *this, + "Key '{}' contains a '{}',\nnot a '{}'", + m_key, demangle(m_value->type()), demangle(typeid(T))); } } } @@ -44,11 +45,12 @@ T &AnyValue::as() { } catch (boost::bad_any_cast&) { if (m_value->type() == typeid(void)) { // Values that have not been set are of type 'void' - throw CanteraError("AnyValue::as", "Key '{}' not found", m_key); + throw InputFileError("AnyValue::as", *this, + "Key '{}' not found", m_key); } else { - throw CanteraError("AnyValue::as", - "Key '{}' contains a '{}',\nnot a '{}'.", - m_key, demangle(m_value->type()), demangle(typeid(T))); + throw InputFileError("AnyValue::as", *this, + "Key '{}' contains a '{}',\nnot a '{}'", + m_key, demangle(m_value->type()), demangle(typeid(T))); } } } @@ -108,7 +110,7 @@ inline AnyMap& AnyValue::as() { } return boost::any_cast(*m_value); } catch (boost::bad_any_cast&) { - throw CanteraError("AnyValue::as", + throw InputFileError("AnyValue::as", *this, "value of key '{}' is a '{}',\nnot an 'AnyMap'.", m_key, demangle(m_value->type())); } @@ -128,14 +130,14 @@ template void AnyValue::checkSize(const std::vector& v, size_t nMin, size_t nMax) const { if (nMin != npos && nMax == npos && v.size() != nMin) { - throw CanteraError("AnyValue::checkSize", "Expected array '{}' " - "to have length {}, but found an array of length {}.", - m_key, nMin, v.size()); + throw InputFileError("AnyValue::checkSize", *this, + "Expected array '{}' to have length {}, but found " + "an array of length {}.", m_key, nMin, v.size()); } else if (nMin != npos && nMax != npos && (v.size() < nMin || v.size() > nMax)) { - throw CanteraError("AnyValue::checkSize", - "Expected array '{}' to have from {} to {} elements, but found an " - " array of length {}.", m_key, nMin, nMax, v.size()); + throw InputFileError("AnyValue::checkSize", *this, + "Expected array '{}' to have from {} to {} elements, but found " + "an array of length {}.", m_key, nMin, nMax, v.size()); } } diff --git a/src/base/AnyMap.cpp b/src/base/AnyMap.cpp index e22bb9b4a..582749846 100644 --- a/src/base/AnyMap.cpp +++ b/src/base/AnyMap.cpp @@ -148,6 +148,7 @@ struct convert { throw NotImplementedError("AnyMap::encode"); } static bool decode(const Node& node, Cantera::AnyMap& target) { + target.setLoc(node.Mark().line, node.Mark().column); if (!node.IsMap()) { std::string text = YAML::Dump(node); if (text.size() > 300) { @@ -158,6 +159,8 @@ struct convert { } for (const auto& child : node) { std::string key = child.first.as(); + const auto& loc = child.second.Mark(); + target[key].setLoc(loc.line, loc.column); if (child.second.IsMap()) { target[key] = child.second.as(); } else { @@ -176,6 +179,7 @@ struct convert { } static bool decode(const Node& node, Cantera::AnyValue& target) { + target.setLoc(node.Mark().line, node.Mark().column); if (node.IsScalar()) { // Scalar nodes are int, doubles, or strings std::string nodestr = node.as(); @@ -237,6 +241,13 @@ struct convert { namespace Cantera { +class InputFile +{ +public: + std::string name; + std::string contents; +}; + std::map AnyValue::s_typenames = { {typeid(double).name(), "double"}, {typeid(long int).name(), "long int"}, @@ -250,23 +261,38 @@ std::unordered_map> AnyMap::s_cache; // Methods of class AnyValue AnyValue::AnyValue() - : m_key() + : m_line(-1) + , m_column(-1) + , m_key() , m_value(new boost::any{}) {} AnyValue::~AnyValue() = default; -AnyValue::AnyValue(AnyValue const& other): m_key(other.m_key), - m_value(new boost::any{*other.m_value}) { +AnyValue::AnyValue(AnyValue const& other) + : m_line(other.m_line) + , m_column(other.m_column) + , m_file(other.m_file) + , m_key(other.m_key) + , m_value(new boost::any{*other.m_value}) +{ } -AnyValue::AnyValue(AnyValue&& other): m_key(std::move(other.m_key)), - m_value(std::move(other.m_value)) { +AnyValue::AnyValue(AnyValue&& other) + : m_line(other.m_line) + , m_column(other.m_column) + , m_file(std::move(other.m_file)) + , m_key(std::move(other.m_key)) + , m_value(std::move(other.m_value)) +{ } AnyValue& AnyValue::operator=(AnyValue const& other) { if (this == &other) return *this; + m_line = other.m_line; + m_column = other.m_column; + m_file = other.m_file; m_key = other.m_key; m_value.reset(new boost::any{*other.m_value}); return *this; @@ -275,6 +301,9 @@ AnyValue& AnyValue::operator=(AnyValue const& other) { AnyValue& AnyValue::operator=(AnyValue&& other) { if (this == &other) return *this; + m_line = other.m_line; + m_column = other.m_column; + m_file = std::move(other.m_file); m_key = std::move(other.m_key); m_value = std::move(other.m_value); return *this; @@ -300,6 +329,28 @@ const std::type_info &AnyValue::type() const { return m_value->type(); } +void AnyValue::setLoc(int line, int column) +{ + m_line = line; + m_column = column; +} + +void AnyValue::setFile(shared_ptr& file) +{ + m_file = file; + if (is()) { + as().setFile(m_file); + } else if (is>()) { + for (auto& item : asVector()) { + item.setFile(m_file); + } + } else if (is>()) { + for (auto& item : asVector()) { + item.setFile(m_file); + } + } +} + std::string AnyValue::type_str() const { return demangle(type()); } @@ -395,7 +446,8 @@ std::unordered_map AnyValue::asMap( for (const auto& item : asVector()) { auto key = item[name].asString(); if (mapped.count(key)) { - throw CanteraError("AnyValue::asMap", "Duplicate key '{}'", key); + throw InputFileError("AnyValue::asMap", *this, + "Duplicate key '{}'", key); } mapped.emplace(std::make_pair(key, &item)); } @@ -408,7 +460,8 @@ std::unordered_map AnyValue::asMap(const std::string& name for (auto& item : asVector()) { auto key = item.at(name).asString(); if (mapped.count(key)) { - throw CanteraError("AnyValue::asMap", "Duplicate key '{}'", key); + throw InputFileError("AnyValue::asMap", *this, + "Duplicate key '{}'", key); } mapped.emplace(std::make_pair(key, &item)); } @@ -431,7 +484,7 @@ void AnyValue::applyUnits(const UnitSystem& units) for (auto& item : list) { // Any additional units declarations are errors if (item.size() == 1 && item.hasKey("units")) { - throw CanteraError("AnyValue::applyUnits", + throw InputFileError("AnyValue::applyUnits", item, "Found units entry as not the first item in a list."); } item.applyUnits(newUnits); @@ -441,6 +494,11 @@ void AnyValue::applyUnits(const UnitSystem& units) } else { // Simple downward propagation of the current units for (auto& item : list) { + // Any later units declarations are errors + if (item.size() == 1 && item.hasKey("units")) { + throw InputFileError("AnyValue::applyUnits", item, + "Found units entry as not the first item in a list."); + } item.applyUnits(units); } } @@ -579,6 +637,12 @@ AnyValue& AnyMap::operator[](const std::string& key) // G++ 4.7 is dropped. AnyValue& value = m_data.insert({key, AnyValue()}).first->second; value.setKey(key); + if (m_file) { + // Approximate location, useful mainly if this insertion is going to + // immediately result in an error that needs to be reported. + value.setLoc(m_line, m_column); + value.setFile(m_file); + } return value; } else { // Return an already-existing item @@ -591,7 +655,7 @@ const AnyValue& AnyMap::operator[](const std::string& key) const try { return m_data.at(key); } catch (std::out_of_range& err) { - throw CanteraError("AnyMap::operator[]", + throw InputFileError("AnyMap::operator[]", *this, "Key '{}' not found.\nExisting keys: {}", key, keys_str()); } } @@ -601,7 +665,7 @@ const AnyValue& AnyMap::at(const std::string& key) const try { return m_data.at(key); } catch (std::out_of_range& err) { - throw CanteraError("AnyMap::at", + throw InputFileError("AnyMap::at", *this, "Key '{}' not found.\nExisting keys: {}", key, keys_str()); } } @@ -631,6 +695,34 @@ std::string AnyMap::keys_str() const return to_string(b); } +void AnyMap::setLoc(int line, int column) +{ + m_line = line; + m_column = column; +} + +void AnyMap::setFile(shared_ptr& file) +{ + m_file = file; + for (auto& item : m_data) { + item.second.setFile(m_file); + } +} + +void AnyMap::setFileName(const std::string& filename) +{ + auto info = make_shared(); + info->name = filename; + setFile(info); +} + +void AnyMap::setFileContents(const std::string& contents) +{ + auto info = make_shared(); + info->contents = contents; + setFile(info); +} + bool AnyMap::getBool(const std::string& key, bool default_) const { return (hasKey(key)) ? m_data.at(key).asBool() : default_; @@ -690,44 +782,18 @@ void AnyMap::applyUnits(const UnitSystem& units) { } } - -// Generate an error message which shows the error location with surrounding -// lines for context -std::string formatYamlError(YAML::Exception& err, - std::istream& contents, const std::string& filename="") -{ - std::string line; - fmt::memory_buffer b; - format_to(b, "Error at line {} of", err.mark.line+1); - if (filename.empty()) { - format_to(b, " input string:\n"); - } else { - format_to(b, "\n{}:\n", filename); - } - format_to(b, "{}\n", err.msg); - format_to(b, "| Line |\n"); - int i = 0; - while (std::getline(contents, line)) { - if (err.mark.line == i) { - format_to(b, "> {: 5d} > {}\n", i+1, line); - format_to(b, "{:>{}}\n", "^", err.mark.column + 11); - } else if (err.mark.line + 4 > i && err.mark.line < i + 6) { - format_to(b, "| {: 5d} | {}\n", i+1, line); - } - i++; - } - return to_string(b); -} - AnyMap AnyMap::fromYamlString(const std::string& yaml) { AnyMap amap; try { YAML::Node node = YAML::Load(yaml); amap = node.as(); } catch (YAML::Exception& err) { - std::stringstream ss_yaml(yaml); - throw CanteraError("AnyMap::fromYamlString", formatYamlError(err, ss_yaml)); + AnyMap fake; + fake.setLoc(err.mark.line, err.mark.column); + fake.setFileContents(yaml); + throw InputFileError("AnyMap::fromYamlString", fake, err.msg); } + amap.setFileContents(yaml); amap.applyUnits(UnitSystem()); return amap; } @@ -769,12 +835,14 @@ AnyMap AnyMap::fromYamlFile(const std::string& name, try { YAML::Node node = YAML::LoadFile(fullName); cache_item.first = node.as(); + cache_item.first.setFileName(fullName); cache_item.first.applyUnits(UnitSystem()); } catch (YAML::Exception& err) { - std::ifstream infile(fullName); s_cache.erase(fullName); - throw CanteraError("AnyMap::fromYamlFile", - formatYamlError(err, infile, name)); + AnyMap fake; + fake.setLoc(err.mark.line, err.mark.column); + fake.setFileName(fullName); + throw InputFileError("AnyMap::fromYamlFile", fake, err.msg); } catch (CanteraError& err) { s_cache.erase(fullName); throw; @@ -793,4 +861,42 @@ AnyMap::const_iterator end(const AnyValue& v) { return v.as().end(); } +std::string InputFileError::formatError(const std::string& message, + int lineno, int column, + const shared_ptr& file) +{ + if (!file) { + return message; + } + + fmt::memory_buffer b; + format_to(b, "Error on line {} of", lineno+1); + if (file->name.empty()) { + format_to(b, " input string:\n"); + } else { + format_to(b, " {}:\n", file->name); + } + format_to(b, "{}\n", message); + format_to(b, "| Line |\n"); + if (file->contents.empty()) { + std::ifstream infile(findInputFile(file->name)); + std::stringstream buffer; + buffer << infile.rdbuf(); + file->contents = buffer.str(); + } + std::string line; + int i = 0; + std::stringstream contents(file->contents); + while (std::getline(contents, line)) { + if (lineno == i) { + format_to(b, "> {: 5d} > {}\n", i+1, line); + format_to(b, "{:>{}}\n", "^", column + 11); + } else if (lineno + 4 > i && lineno < i + 6) { + format_to(b, "| {: 5d} | {}\n", i+1, line); + } + i++; + } + return to_string(b); +} + } diff --git a/src/kinetics/KineticsFactory.cpp b/src/kinetics/KineticsFactory.cpp index 945f21490..024ee2c46 100644 --- a/src/kinetics/KineticsFactory.cpp +++ b/src/kinetics/KineticsFactory.cpp @@ -146,8 +146,9 @@ void addReactions(Kinetics& kin, const AnyMap& phaseNode, const AnyMap& rootNode kin.skipUndeclaredSpecies(true); kin.skipUndeclaredThirdBodies(true); } else if (rules[i] != "none") { - throw CanteraError("setupKinetics", "Unknown rule '{}' for adding " - "species from the '{}' section.", rules[i], sections[i]); + throw InputFileError("setupKinetics", phaseNode.at("reactions"), + "Unknown rule '{}' for adding species from the '{}' section.", + rules[i], sections[i]); } const auto& slash = boost::ifind_last(sections[i], "/"); if (slash) { diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index f76313824..b0eaa9537 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -475,13 +475,18 @@ void setupReaction(Reaction& R, const AnyMap& node) } else if (last_used == i-2) { // Species with no stoich. coefficient stoich = 1.0; } else if (last_used == i-3) { // Stoich. coefficient and species - stoich = fpValueCheck(tokens[i-2]); + try { + stoich = fpValueCheck(tokens[i-2]); + } catch (CanteraError& err) { + throw InputFileError("fpValueCheck", node["equation"], + err.getMessage()); + } } else { - throw CanteraError("setupReaction", "Error parsing reaction " - "string '{}'.\nCurrent token: '{}'\nlast_used: '{}'", + throw InputFileError("setupReaction", node["equation"], + "Error parsing reaction string '{}'.\n" + "Current token: '{}'\nlast_used: '{}'", node["equation"].asString(), - tokens[i], (last_used == npos) ? "n/a" : tokens[last_used] - ); + tokens[i], (last_used == npos) ? "n/a" : tokens[last_used]); } if (reactants) { @@ -561,7 +566,7 @@ void setupThreeBodyReaction(ThreeBodyReaction& R, const AnyMap& node, { setupElementaryReaction(R, node, kin); if (R.reactants.count("M") != 1 || R.products.count("M") != 1) { - throw CanteraError("setupThreeBodyReaction", + throw InputFileError("setupThreeBodyReaction", node["equation"], "Reaction equation '{}' does not contain third body 'M'", node["equation"].asString()); } @@ -614,13 +619,13 @@ void setupFalloffReaction(FalloffReaction& R, const AnyMap& node, // Equation must contain a third body, and it must appear on both sides if (third_body == "") { - throw CanteraError("setupFalloffReaction", "Reactants for reaction " - "'{}' do not contain a pressure-dependent third body", - node["equation"].asString()); + throw InputFileError("setupFalloffReaction", node["equation"], + "Reactants for reaction '{}' do not contain a pressure-dependent " + "third body", node["equation"].asString()); } else if (R.products.count(third_body) == 0) { - throw CanteraError("setupFalloffReaction", "Unable to match third body " - "'{}' in reactants and products of reaction '{}'", - third_body, node["equation"].asString()); + throw InputFileError("setupFalloffReaction", node["equation"], + "Unable to match third body '{}' in reactants and products of " + "reaction '{}'", third_body, node["equation"].asString()); } // Remove the dummy species @@ -804,8 +809,9 @@ void setupInterfaceReaction(InterfaceReaction& R, const AnyMap& node, kin.thermo().input().getBool("Motz-Wise", false)); R.sticking_species = node.getString("sticking-species", ""); } else { - throw CanteraError("setupInterfaceReaction", "Reaction must include " - "either a 'rate-constant' or 'sticking-coefficient' node."); + throw InputFileError("setupInterfaceReaction", node, + "Reaction must include either a 'rate-constant' or" + " 'sticking-coefficient' node."); } if (node.hasKey("coverage-dependencies")) { @@ -1010,7 +1016,8 @@ unique_ptr newReaction(const AnyMap& node, const Kinetics& kin) setupChebyshevReaction(*R, node, kin); return unique_ptr(move(R)); } else { - throw CanteraError("newReaction", "Unknown reaction type '{}'", type); + throw InputFileError("newReaction", node["type"], + "Unknown reaction type '{}'", type); } } diff --git a/src/thermo/HMWSoln.cpp b/src/thermo/HMWSoln.cpp index 4c09ccc16..97689b136 100644 --- a/src/thermo/HMWSoln.cpp +++ b/src/thermo/HMWSoln.cpp @@ -714,8 +714,8 @@ void HMWSoln::initThermo() vector_fp Cphi = getSizedVector(item, "Cphi", nCoeffs); if (beta0.size() != beta1.size() || beta0.size() != beta2.size() || beta0.size() != Cphi.size()) { - throw CanteraError("HMWSoln::initThermo", "Inconsistent" - " binary salt array sizes ({}, {}, {}, {})", + throw InputFileError("HMWSoln::initThermo", item, + "Inconsistent binary salt array sizes ({}, {}, {}, {})", beta0.size(), beta1.size(), beta2.size(), Cphi.size()); } double alpha1 = item["alpha1"].asDouble(); diff --git a/src/thermo/RedlichKwongMFTP.cpp b/src/thermo/RedlichKwongMFTP.cpp index 2d4b426aa..e950ed550 100644 --- a/src/thermo/RedlichKwongMFTP.cpp +++ b/src/thermo/RedlichKwongMFTP.cpp @@ -647,7 +647,7 @@ void RedlichKwongMFTP::initThermo() if (item.second->input.hasKey("equation-of-state")) { auto eos = item.second->input["equation-of-state"].as(); if (eos.getString("model", "") != "Redlich-Kwong") { - throw CanteraError("RedlichKwongMFTP::initThermo", + throw InputFileError("RedlichKwongMFTP::initThermo", eos, "Expected species equation of state to be 'Redlich-Kwong', " "but got '{}' instead", eos.getString("model", "")); } diff --git a/src/thermo/StoichSubstance.cpp b/src/thermo/StoichSubstance.cpp index 051b9f39f..88b8e94f5 100644 --- a/src/thermo/StoichSubstance.cpp +++ b/src/thermo/StoichSubstance.cpp @@ -125,7 +125,7 @@ void StoichSubstance::initThermo() if (species(0)->input.hasKey("equation-of-state")) { auto& eos = species(0)->input["equation-of-state"].as(); if (eos.getString("model", "") != "constant-volume") { - throw CanteraError("StoichSubstance::initThermo", + throw InputFileError("StoichSubstance::initThermo", eos, "fixed-stoichiometry model requires constant-volume species " "model for species '{}'", speciesName(0)); } @@ -136,7 +136,7 @@ void StoichSubstance::initThermo() } else if (eos.hasKey("molar-volume")) { setMolarDensity(1.0 / eos.convert("molar-volume", "m^3/kmol")); } else { - throw CanteraError("StoichSubstance::initThermo", + throw InputFileError("StoichSubstance::initThermo", eos, "equation-of-state entry for species '{}' is missing 'density'," " 'molar-volume' or 'molar-density' specification", speciesName(0)); diff --git a/src/thermo/ThermoFactory.cpp b/src/thermo/ThermoFactory.cpp index fb545936f..4ad970ff7 100644 --- a/src/thermo/ThermoFactory.cpp +++ b/src/thermo/ThermoFactory.cpp @@ -406,10 +406,16 @@ void importPhase(XML_Node& phase, ThermoPhase* th) th->initThermoXML(phase, id); } +void addDefaultElements(ThermoPhase& thermo, const vector& element_names) { + for (const auto& symbol : element_names) { + thermo.addElement(symbol); + } +} + void addElements(ThermoPhase& thermo, const vector& element_names, - const unordered_map& local_elements, - bool allow_default) + const AnyValue& elements, bool allow_default) { + const auto& local_elements = elements.asMap("symbol"); for (const auto& symbol : element_names) { if (local_elements.count(symbol)) { auto& element = *local_elements.at(symbol); @@ -420,7 +426,8 @@ void addElements(ThermoPhase& thermo, const vector& element_names, } else if (allow_default) { thermo.addElement(symbol); } else { - throw CanteraError("addElements", "Element '{}' not found", symbol); + throw InputFileError("addElements", elements, + "Element '{}' not found", symbol); } } } @@ -431,7 +438,12 @@ void addSpecies(ThermoPhase& thermo, const AnyValue& names, const AnyValue& spec // 'names' is a list of species names which should be found in 'species' const auto& species_nodes = species.asMap("name"); for (const auto& name : names.asVector()) { - thermo.addSpecies(newSpecies(*species_nodes.at(name))); + if (species_nodes.count(name)) { + thermo.addSpecies(newSpecies(*species_nodes.at(name))); + } else { + throw InputFileError("addSpecies", species, + "Could not find a species named '{}'.", name); + } } } else if (names.is() && names.asString() == "all") { // The keyword 'all' means to add all species from this source @@ -439,7 +451,7 @@ void addSpecies(ThermoPhase& thermo, const AnyValue& names, const AnyValue& spec thermo.addSpecies(newSpecies(item)); } } else { - throw CanteraError("addSpecies", + throw InputFileError("addSpecies", names, "Could not parse species declaration of type '{}'", names.type_str()); } } @@ -463,10 +475,9 @@ void setupPhase(ThermoPhase& thermo, AnyMap& phaseNode, const AnyMap& rootNode) // 'elements' is a list of element symbols if (rootNode.hasKey("elements")) { addElements(thermo, phaseNode["elements"].asVector(), - rootNode["elements"].asMap("symbol"), true); + rootNode["elements"], true); } else { - addElements(thermo, phaseNode["elements"].asVector(), - {}, true); + addDefaultElements(thermo, phaseNode["elements"].asVector()); } } else if (phaseNode["elements"].is>()) { // Each item in 'elements' is a map with one item, where the key is @@ -481,20 +492,18 @@ void setupPhase(ThermoPhase& thermo, AnyMap& phaseNode, const AnyMap& rootNode) std::string node(slash.end(), source.end()); const AnyMap elements = AnyMap::fromYamlFile(fileName, rootNode.getString("__file__", "")); - addElements(thermo, names, - elements[node].asMap("symbol"), false); + addElements(thermo, names, elements.at(node), false); } else if (rootNode.hasKey(source)) { - addElements(thermo, names, - rootNode[source].asMap("symbol"), false); + addElements(thermo, names, rootNode.at(source), false); } else if (source == "default") { - addElements(thermo, names, {}, true); + addDefaultElements(thermo, names); } else { - throw CanteraError("setupPhase", + throw InputFileError("setupPhase", elemNode, "Could not find elements section named '{}'", source); } } } else { - throw CanteraError("setupPhase", + throw InputFileError("setupPhase", phaseNode["elements"], "Could not parse elements declaration of type '{}'", phaseNode["elements"].type_str()); } @@ -533,12 +542,12 @@ void setupPhase(ThermoPhase& thermo, AnyMap& phaseNode, const AnyMap& rootNode) // source is in the current file addSpecies(thermo, names, rootNode[source]); } else { - throw CanteraError("setupPhase", + throw InputFileError("setupPhase", speciesNode, "Could not find species section named '{}'", source); } } } else { - throw CanteraError("setupPhase", + throw InputFileError("setupPhase", phaseNode["species"], "Could not parse species declaration of type '{}'", phaseNode["species"].type_str()); }