[Input] Provide context for more errors in YAML processing
This commit is contained in:
parent
b1273301cc
commit
d77a5979a8
9 changed files with 271 additions and 98 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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", ""));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue