[Input] Provide context for more errors in YAML processing

This commit is contained in:
Ray Speth 2019-02-06 14:43:33 -05:00
parent b1273301cc
commit d77a5979a8
9 changed files with 271 additions and 98 deletions

View file

@ -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<InputFile>& file);
template<class T>
const T& as() const;
@ -129,9 +132,14 @@ private:
template<class T>
void checkSize(const std::vector<T>& v, size_t nMin, size_t nMax) const;
int m_line;
int m_column;
shared_ptr<InputFile> m_file;
std::string m_key;
std::unique_ptr<boost::any> m_value;
static std::map<std::string, std::string> s_typenames;
friend class InputFileError;
};
// Implicit conversion to vector<AnyValue>
@ -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<InputFile>& 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<std::string, AnyValue> m_data;
UnitSystem m_units;
int m_line;
int m_column;
shared_ptr<InputFile> 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<std::string, std::pair<AnyMap, int>> 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 <typename... Args>
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 <typename... Args>
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<InputFile>& file);
};
}
#ifndef CANTERA_API_NO_BOOST

View file

@ -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<AnyMap>() {
}
return boost::any_cast<AnyMap&>(*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<class T>
void AnyValue::checkSize(const std::vector<T>& 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());
}
}

View file

@ -148,6 +148,7 @@ struct convert<Cantera::AnyMap> {
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<Cantera::AnyMap> {
}
for (const auto& child : node) {
std::string key = child.first.as<std::string>();
const auto& loc = child.second.Mark();
target[key].setLoc(loc.line, loc.column);
if (child.second.IsMap()) {
target[key] = child.second.as<AnyMap>();
} else {
@ -176,6 +179,7 @@ struct convert<Cantera::AnyValue> {
}
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<std::string>();
@ -237,6 +241,13 @@ struct convert<Cantera::AnyValue> {
namespace Cantera
{
class InputFile
{
public:
std::string name;
std::string contents;
};
std::map<std::string, std::string> AnyValue::s_typenames = {
{typeid(double).name(), "double"},
{typeid(long int).name(), "long int"},
@ -250,23 +261,38 @@ std::unordered_map<std::string, std::pair<AnyMap, int>> 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<InputFile>& file)
{
m_file = file;
if (is<AnyMap>()) {
as<AnyMap>().setFile(m_file);
} else if (is<std::vector<AnyValue>>()) {
for (auto& item : asVector<AnyValue>()) {
item.setFile(m_file);
}
} else if (is<std::vector<AnyMap>>()) {
for (auto& item : asVector<AnyMap>()) {
item.setFile(m_file);
}
}
}
std::string AnyValue::type_str() const {
return demangle(type());
}
@ -395,7 +446,8 @@ std::unordered_map<std::string, const AnyMap*> AnyValue::asMap(
for (const auto& item : asVector<AnyMap>()) {
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<std::string, AnyMap*> AnyValue::asMap(const std::string& name
for (auto& item : asVector<AnyMap>()) {
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<InputFile>& 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<InputFile>();
info->name = filename;
setFile(info);
}
void AnyMap::setFileContents(const std::string& contents)
{
auto info = make_shared<InputFile>();
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<AnyMap>();
} 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<AnyMap>();
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<AnyMap>().end();
}
std::string InputFileError::formatError(const std::string& message,
int lineno, int column,
const shared_ptr<InputFile>& 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);
}
}

View file

@ -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) {

View file

@ -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<Reaction> newReaction(const AnyMap& node, const Kinetics& kin)
setupChebyshevReaction(*R, node, kin);
return unique_ptr<Reaction>(move(R));
} else {
throw CanteraError("newReaction", "Unknown reaction type '{}'", type);
throw InputFileError("newReaction", node["type"],
"Unknown reaction type '{}'", type);
}
}

View file

@ -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();

View file

@ -647,7 +647,7 @@ void RedlichKwongMFTP::initThermo()
if (item.second->input.hasKey("equation-of-state")) {
auto eos = item.second->input["equation-of-state"].as<AnyMap>();
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", ""));
}

View file

@ -125,7 +125,7 @@ void StoichSubstance::initThermo()
if (species(0)->input.hasKey("equation-of-state")) {
auto& eos = species(0)->input["equation-of-state"].as<AnyMap>();
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));

View file

@ -406,10 +406,16 @@ void importPhase(XML_Node& phase, ThermoPhase* th)
th->initThermoXML(phase, id);
}
void addDefaultElements(ThermoPhase& thermo, const vector<string>& element_names) {
for (const auto& symbol : element_names) {
thermo.addElement(symbol);
}
}
void addElements(ThermoPhase& thermo, const vector<string>& element_names,
const unordered_map<string, const AnyMap*>& 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<string>& 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<string>()) {
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<string>() && 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<string>(),
rootNode["elements"].asMap("symbol"), true);
rootNode["elements"], true);
} else {
addElements(thermo, phaseNode["elements"].asVector<string>(),
{}, true);
addDefaultElements(thermo, phaseNode["elements"].asVector<string>());
}
} else if (phaseNode["elements"].is<vector<AnyMap>>()) {
// 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());
}