8#include "cantera/base/yaml.h"
12#include <boost/algorithm/string.hpp>
15#include <unordered_set>
17namespace ba = boost::algorithm;
21std::mutex yaml_cache_mutex;
22std::mutex yaml_field_order_mutex;
25bool isFloat(
const string& val)
29 string str = ba::trim_copy(val);
38 if (ch ==
'+' || ch ==
'-') {
40 if (str.size() == 1) {
44 for (
size_t i = istart; i < str.size(); i++) {
48 }
else if (ch ==
'.') {
56 }
else if (ch ==
'e' || ch ==
'E') {
58 if (numExp > 1 || numDigit == 0 || i == str.size() - 1) {
62 if (ch ==
'+' || ch ==
'-') {
63 if (i + 1 == str.size() - 1) {
75bool isInt(
const string& val)
77 string str = ba::trim_copy(val);
83 if (ch ==
'+' || ch ==
'-') {
85 if (str.size() == 1) {
89 for (
size_t i = istart; i < str.size(); i++) {
90 if (!isdigit(str[i])) {
97bool isBool(
const string& val) {
98 string str = ba::trim_copy(val);
99 return (val ==
"true" || val ==
"True" || val ==
"false" || val ==
"False");
102bool dunder(
const string& s) {
103 return ba::starts_with(s,
"__") && ba::ends_with(s,
"__");
106enum class Type :
char {
118bool operator==(
const Exclude& lhs,
const Exclude& rhs) {
122Type operator|(Type lhs, Type rhs)
124 return Type(
static_cast<char>(lhs) |
static_cast<char>(rhs));
127Type elementTypes(
const YAML::Node& node)
130 Type types = Type::Unknown;
131 for (
const auto& el : node) {
133 types = types | Type::Map;
134 }
else if (el.IsSequence()) {
135 types = types | Type::Sequence;
136 }
else if (el.IsScalar()) {
137 string nodestr = el.as<
string>();
138 if (el.Tag() ==
"!") {
140 types = types | Type::String;
141 }
else if (isInt(nodestr)) {
142 types = types | Type::Integer;
143 }
else if (isFloat(nodestr)) {
144 types = types | Type::Double;
145 }
else if (isBool(nodestr)) {
146 types = types | Type::Bool;
148 types = types | Type::String;
157 long int precision = 15;
158 auto& userPrecision = precisionSource.
getMetadata(
"precision");
159 if (userPrecision.is<
long int>()) {
160 precision = userPrecision.
asInt();
165string formatDouble(
double x,
long int precision)
175 bool useExp = std::abs(x) < 1e-2 || std::abs(x) >= 1e4;
180 s0 = fmt::format(fmt::runtime(fmt::format(
"{:.{}e}", x, precision)));
182 last = s0.size() - 5;
183 if (s0[last + 1] ==
'e') {
185 }
else if (s0[last] ==
'e') {
188 last = s0.find(
'e') - 1;
191 log10x =
static_cast<int>(std::floor(std::log10(std::abs(x))));
192 s0 = fmt::format(
"{:.{}f}", x, precision - log10x);
193 last = s0.size() - 1;
195 if (s0[last - 2] ==
'0' && s0[last - 1] ==
'0' && s0[last] <
'5') {
197 }
else if (s0[last - 2] ==
'9' && s0[last - 1] ==
'9' && s0[last] >
'4') {
199 }
else if (s0[last - 1] ==
'0' && s0[last] ==
'0') {
208 if (s0[last - 1] ==
'0') {
211 s1 = fmt::format(fmt::runtime(fmt::format(
"{:.{}e}", x, precision - 2)));
213 s1 = fmt::format(
"{:.{}f}", x, precision - log10x - 2);
215 size_t digit = last - 2;
216 while (s1[digit] ==
'0' && s1[digit - 1] !=
'.') {
222 size_t eloc = s1.find(
'e');
223 s0 = string(s1.begin() + eloc, s1.end());
225 s1 = string(s1.begin(), s1.begin() + digit + 1);
236 bool isActivationEnergy;
237 AnyValue::unitConverter converter;
239 bool operator==(
const Quantity& other)
const {
240 return value == other.value && units == other.units
241 && isActivationEnergy == other.isActivationEnergy;
252static const int max_line_length = 87;
261 target.
setLoc(node.Mark().line, node.Mark().column);
262 if (node.Style() == YAML::EmitterStyle::Flow) {
265 if (node.IsSequence()) {
267 target[
"items"] = node.as<
AnyValue>();
269 }
else if (!node.IsMap()) {
270 string text = YAML::Dump(node);
271 if (text.size() > 300) {
275 "YAML node is not a map. Node begins with:\n'''\n{}\n'''", text);
277 for (
const auto& child : node) {
278 string key = child.first.as<
string>();
279 const auto& loc = child.second.Mark();
281 if (child.second.IsMap()) {
292YAML::Emitter& operator<<(YAML::Emitter& out,
const AnyMap& rhs)
294 bool flow = rhs.
getBool(
"__flow__",
false);
297 out << YAML::BeginMap;
299 for (
const auto& [name, value] : rhs.ordered(true)) {
301 bool foundType =
true;
302 bool needsQuotes =
false;
303 if (value.is<
double>()) {
304 valueStr = formatDouble(value.asDouble(), getPrecision(value));
305 }
else if (value.is<
string>()) {
306 valueStr = value.asString();
307 if (isFloat(valueStr)) {
311 }
else if (value.is<
long int>()) {
312 valueStr = fmt::format(
"{}", value.asInt());
313 }
else if (value.is<
bool>()) {
314 valueStr = fmt::format(
"{}", value.asBool());
322 if (width + name.size() + valueStr.size() + 4 > max_line_length) {
323 out << YAML::Newline;
328 out << YAML::SingleQuoted;
331 width += name.size() + valueStr.size() + 4;
334 out << YAML::Newline;
341 out << YAML::BeginMap;
342 for (
const auto& [key, value] : rhs.ordered(true)) {
353 if (str0.rfind(
'\n') == string::npos) {
355 out << YAML::SingleQuoted;
362 size_t left = str0.find_first_not_of(
"\n\t ");
363 size_t right = str0.find_last_not_of(
"\n\t ");
364 string str1 = str0.substr(left, right - left + 1);
365 out << YAML::Literal << str1;
372void emitFlowVector(YAML::Emitter& out,
const vector<double>& v,
long int precision)
375 out << YAML::BeginSeq;
378 string xstr = formatDouble(x, precision);
380 if (width + xstr.size() > max_line_length) {
381 out << YAML::Newline;
385 width += xstr.size() + 2;
396 out << YAML::BeginSeq;
398 for (
const string& x : v) {
400 if (width + x.size() > max_line_length) {
401 out << YAML::Newline;
409 width += x.size() + 2;
420 out << YAML::BeginSeq;
422 for (
const T& x : v) {
423 string xstr = fmt::format(
"{}", x);
425 if (width + xstr.size() > max_line_length) {
426 out << YAML::Newline;
430 width += xstr.size() + 2;
435YAML::Emitter& operator<<(YAML::Emitter& out,
const AnyValue& rhs)
438 if (rhs.
is<
string>()) {
440 }
else if (rhs.
is<
double>()) {
441 out << formatDouble(rhs.
asDouble(), getPrecision(rhs));
442 }
else if (rhs.
is<
long int>()) {
444 }
else if (rhs.
is<
bool>()) {
447 throw CanteraError(
"operator<<(YAML::Emitter&, AnyValue&)",
448 "Don't know how to encode value of type '{}' with key '{}'",
453 }
else if (rhs.
is<vector<AnyMap>>()) {
455 }
else if (rhs.
is<vector<double>>()) {
457 }
else if (rhs.
is<vector<string>>()) {
459 }
else if (rhs.
is<vector<long int>>()) {
461 }
else if (rhs.
is<vector<bool>>()) {
463 }
else if (rhs.
is<vector<Cantera::AnyValue>>()) {
465 }
else if (rhs.
is<vector<vector<double>>>()) {
466 const auto& v = rhs.
asVector<vector<double>>();
467 long int precision = getPrecision(rhs);
468 out << YAML::BeginSeq;
469 for (
const auto& u : v) {
473 }
else if (rhs.
is<vector<vector<string>>>()) {
474 const auto& v = rhs.
asVector<vector<string>>();
475 out << YAML::BeginSeq;
476 for (
const auto& u : v) {
480 }
else if (rhs.
is<vector<vector<long int>>>()) {
481 const auto& v = rhs.
asVector<vector<long int>>();
482 out << YAML::BeginSeq;
483 for (
const auto& u : v) {
487 }
else if (rhs.
is<vector<vector<bool>>>()) {
488 const auto& v = rhs.
asVector<vector<bool>>();
489 out << YAML::BeginSeq;
490 for (
const auto& u : v) {
495 throw CanteraError(
"operator<<(YAML::Emitter&, AnyValue&)",
496 "Don't know how to encode value of type '{}' with key '{}'",
510 target.
setLoc(node.Mark().line, node.Mark().column);
511 if (node.IsScalar()) {
513 string nodestr = node.as<
string>();
514 if (node.Tag() ==
"!") {
519 }
else if (isInt(nodestr)) {
521 target = node.
as<
long int>();
522 }
catch (YAML::BadConversion&) {
526 target = node.
as<
double>();
528 }
else if (isFloat(nodestr)) {
530 }
else if (isBool(nodestr)) {
531 target = node.
as<
bool>();
536 }
else if (node.IsSequence()) {
538 Type types = elementTypes(node);
539 if (types == Type::Integer) {
540 target = node.
as<vector<long int>>();
541 }
else if (types == (Type::Integer | Type::Double) || types == Type::Double) {
542 vector<double> values;
543 for (
const auto& elem : node) {
544 values.push_back(
fpValue(elem.as<
string>()));
546 target = std::move(values);
547 }
else if (types == Type::String) {
548 target = node.
as<vector<string>>();
549 }
else if (types == Type::Bool) {
550 target = node.
as<vector<bool>>();
551 }
else if (types == Type::Map) {
552 target = node.
as<vector<AnyMap>>();
553 }
else if (types == Type::Sequence) {
555 Type subtypes = Type::Unknown;
556 for (
const auto& el : node) {
557 subtypes = subtypes | elementTypes(el);
559 if (subtypes == Type::Integer) {
560 target = node.
as<vector<vector<long int>>>();
561 }
else if (subtypes == (Type::Integer | Type::Double) || subtypes == Type::Double) {
562 vector<vector<double>> values;
563 for (
const auto& row : node) {
564 values.emplace_back();
565 for (
const auto& value : row) {
566 values.back().push_back(
fpValue(value.as<
string>()));
569 target = std::move(values);
570 }
else if (subtypes == Type::String) {
571 target = node.
as<vector<vector<string>>>();
572 }
else if (subtypes == Type::Bool) {
573 target = node.
as<vector<vector<bool>>>();
575 target = node.
as<vector<AnyValue>>();
579 target = node.
as<vector<AnyValue>>();
582 }
else if (node.IsMap()) {
585 }
else if (node.IsNull()) {
597std::unordered_map<string,
635 : m_equals(eq_comparer<size_t>)
638AnyValue::~AnyValue() =
default;
640bool AnyValue::operator==(
const AnyValue& other)
const
645bool AnyValue::operator!=(
const AnyValue& other)
const
652 return as<AnyMap>()[key];
657 return as<AnyMap>().at(key);
661 return (is<AnyMap>() && as<AnyMap>().
hasKey(key));
675 }
else if (
is<vector<AnyValue>>()) {
676 for (
auto& item : asVector<AnyValue>()) {
679 }
else if (
is<vector<AnyMap>>()) {
680 for (
auto& item : asVector<AnyMap>()) {
695 return is<double>() || is<long int>() || is<string>() || is<bool>();
699 if (isVector<double>()) {
700 return as<vector<double>>().size();
702 if (isVector<long int>()) {
703 return as<vector<long int>>().size();
705 if (isVector<string>()) {
706 return as<vector<string>>().size();
708 if (isVector<bool>()) {
709 return as<vector<bool>>().size();
716 auto& mat = as<vector<vector<double>>>();
717 if (isMatrix<double>()) {
719 return {mat.size(), mat[0].size()};
721 return {mat.size(), 0};
723 return {mat.size(),
npos};
726 auto& mat = as<vector<vector<long int>>>();
727 if (isMatrix<long int>()) {
729 return {mat.size(), mat[0].size()};
731 return {mat.size(), 0};
733 return {mat.size(),
npos};
736 auto& mat = as<vector<vector<string>>>();
737 if (isMatrix<string>()) {
739 return {mat.size(), mat[0].size()};
741 return {mat.size(), 0};
743 return {mat.size(),
npos};
746 auto& mat = as<vector<vector<bool>>>();
747 if (isMatrix<bool>()) {
749 return {mat.size(), mat[0].size()};
751 return {mat.size(), 0};
753 return {mat.size(),
npos};
760AnyValue::AnyValue(
const string& value)
762 , m_equals(eq_comparer<string>)
765AnyValue::AnyValue(
const char* value)
766 : m_value{string(value)}
767 , m_equals(eq_comparer<string>)
770AnyValue &AnyValue::operator=(
const string &value) {
772 m_equals = eq_comparer<string>;
776AnyValue &AnyValue::operator=(
const char *value) {
778 m_equals = eq_comparer<string>;
786bool AnyValue::operator==(
const string& other)
const
788 if (
m_value.type() ==
typeid(
string)) {
789 return std::any_cast<string>(
m_value) == other;
795bool AnyValue::operator!=(
const string& other)
const
797 return !(*
this == other);
800bool operator==(
const string& lhs,
const AnyValue& rhs)
805bool operator!=(
const string& lhs,
const AnyValue& rhs)
814 m_equals = eq_comparer<Quantity>;
819 m_equals = eq_comparer<Quantity>;
826 m_equals = eq_comparer<Quantity>;
831 m_value = Quantity{value,
Units(0.0),
false, converter};
832 m_equals = eq_comparer<Quantity>;
836bool AnyValue::is<vector<double>>()
const
838 if (m_value.type() ==
typeid(vector<double>)) {
840 }
else if (m_value.type() ==
typeid(vector<AnyValue>)) {
841 for (
const auto& item : as<vector<
AnyValue>>()) {
842 if (!(item.is<
double>()
843 || (item.is<Quantity>() && item.as<Quantity>().value.is<
double>())))
856AnyValue::AnyValue(
double value)
858 , m_equals(eq_comparer<double>)
861AnyValue &AnyValue::operator=(
double value) {
863 m_equals = eq_comparer<double>;
875bool AnyValue::operator==(
const double& other)
const
877 if (
m_value.type() ==
typeid(
double)) {
878 return std::any_cast<double>(
m_value) == other;
879 }
else if (
m_value.type() ==
typeid(
long int)) {
880 return std::any_cast<long int>(
m_value) == other;
886bool AnyValue::operator!=(
const double& other)
const
888 return !(*
this == other);
891bool operator==(
const double& lhs,
const AnyValue& rhs)
896bool operator!=(
const double& lhs,
const AnyValue& rhs)
903AnyValue::AnyValue(
bool value)
905 , m_equals(eq_comparer<bool>)
908AnyValue &AnyValue::operator=(
bool value) {
910 m_equals = eq_comparer<bool>;
924AnyValue::AnyValue(
long int value)
926 , m_equals(eq_comparer<long int>)
929AnyValue::AnyValue(
int value)
930 : m_value{static_cast<long int>(value)}
931 , m_equals(eq_comparer<long int>)
934AnyValue &AnyValue::operator=(
long int value) {
936 m_equals = eq_comparer<long int>;
940AnyValue &AnyValue::operator=(
int value) {
941 m_value =
static_cast<long int>(value);
942 m_equals = eq_comparer<long int>;
947 return as<long int>();
951 return as<long int>();
954bool AnyValue::operator==(
const long int& other)
const
956 if (
m_value.type() ==
typeid(
long int)) {
957 return std::any_cast<long int>(
m_value) == other;
958 }
else if (
m_value.type() ==
typeid(
double)) {
959 return std::any_cast<double>(
m_value) == other;
965bool AnyValue::operator!=(
const long int& other)
const
967 return !(*
this == other);
970bool AnyValue::operator==(
const int& other)
const
972 return *
this ==
static_cast<long int>(other);
975bool AnyValue::operator!=(
const int& other)
const
977 return *
this !=
static_cast<long int>(other);
980bool operator==(
const long int& lhs,
const AnyValue& rhs)
985bool operator!=(
const long int& lhs,
const AnyValue& rhs)
990bool operator==(
const int& lhs,
const AnyValue& rhs)
995bool operator!=(
const int& lhs,
const AnyValue& rhs)
1002AnyValue::AnyValue(
const AnyMap& value)
1004 , m_equals(eq_comparer<
AnyMap>)
1009 m_equals = eq_comparer<AnyMap>;
1015 m_equals = eq_comparer<AnyMap>;
1022 v.m_equals = eq_comparer<Exclude>;
1028 std::unordered_map<string, const AnyMap*> mapped;
1029 for (
const auto& item : asVector<AnyMap>()) {
1030 auto key = item[name].asString();
1031 if (mapped.count(key)) {
1033 "Duplicate key '{}'", key);
1035 mapped.emplace(std::make_pair(key, &item));
1040std::unordered_map<string, AnyMap*>
AnyValue::asMap(
const string& name)
1042 std::unordered_map<string, AnyMap*> mapped;
1044 auto key = item.at(name).asString();
1045 if (mapped.count(key)) {
1047 "Duplicate key '{}'", key);
1049 mapped.emplace(std::make_pair(key, &item));
1056 if (
is<vector<AnyMap>>()) {
1058 return asVector<AnyMap>().
at(0);
1061 if (item.hasKey(key) && item[key] == value) {
1066 "List does not contain a map where '{}' = '{}'", key, value);
1067 }
else if (is<AnyMap>()) {
1068 if (value ==
"" || (
hasKey(key) && as<AnyMap>()[key] == value)) {
1069 return as<AnyMap>();
1072 "Map does not contain a key where '{}' = '{}'", key, value);
1074 }
else if (is<void>()) {
1076 "Key '{}' not found",
m_key);
1079 "Element is not a mapping or list of mappings.\n"
1080 "Looking for a mapping with key '{}' = '{}'", key, value);
1086 if (
is<vector<AnyMap>>()) {
1088 return asVector<AnyMap>().
at(0);
1090 for (
auto& item : asVector<AnyMap>()) {
1091 if (item.hasKey(key) && item[key] == value) {
1097 auto& vec = asVector<AnyMap>();
1100 vec.push_back(std::move(child));
1104 "List does not contain a map where '{}' = '{}'", key, value);
1106 }
else if (is<AnyMap>()) {
1107 if (value ==
"" || (
hasKey(key) && as<AnyMap>()[key] == value)) {
1108 return as<AnyMap>();
1109 }
else if (create) {
1111 newChild[key] = value;
1112 vector<AnyMap> nodes{std::move(as<AnyMap>()), std::move(newChild)};
1113 operator=(std::move(nodes));
1114 return asVector<AnyMap>().back();
1117 "Map does not contain a key where '{}' = '{}'", key, value);
1119 }
else if (is<void>() && create) {
1122 operator=(std::move(child));
1123 return as<AnyMap>();
1124 }
else if (is<void>()) {
1126 "Key '{}' not found",
m_key);
1129 "Element is not a mapping or list of mappings.\n"
1130 "Looking for a mapping with key '{}' = '{}'", key, value);
1136 if (
is<vector<AnyMap>>()) {
1140 for (
auto& item : asVector<AnyMap>()) {
1141 if (item.hasKey(key) && item[key] == value) {
1146 }
else if (is<AnyMap>()) {
1147 if (value ==
"" || (
hasKey(key) && as<AnyMap>()[key] == value)) {
1165 AnyMap& m = as<AnyMap>();
1167 if (m.
getBool(
"__unconvertible__",
false)) {
1172 throw CanteraError(
"AnyValue::applyUnits",
"AnyMap contains values"
1173 " that cannot be converted to non-default unit systems\n(probably"
1174 " reaction rates not associated with a Kinetics object)");
1179 }
else if (
is<vector<AnyMap>>()) {
1180 auto& list = as<vector<AnyMap>>();
1181 if (list.size() && list[0].hasKey(
"units") && list[0].size() == 1) {
1184 auto deltaUnits = list[0][
"units"];
1185 list[0].m_data.erase(
"units");
1186 for (
auto& item : list) {
1187 if (item.hasKey(
"units")) {
1188 if (item.size() == 1) {
1191 "Found units entry as not the first item in a list.");
1194 auto& childUnits = item[
"units"].as<
AnyMap>();
1195 for (
auto& [dimension, unit] : deltaUnits) {
1196 if (!childUnits.hasKey(dimension)) {
1197 childUnits[dimension] = unit;
1201 }
else if (item.hasKey(
"__units__")) {
1203 auto& childUnits = item[
"__units__"].as<
AnyMap>();
1204 for (
auto& [dimension, unit] : deltaUnits) {
1205 if (!childUnits.hasKey(dimension)) {
1206 childUnits[dimension] = unit;
1210 item[
"__units__"] = deltaUnits;
1215 list.erase(list.begin());
1218 for (
auto& item : list) {
1220 if (item.size() == 1 && item.hasKey(
"units")) {
1222 "Found units entry as not the first item in a list.");
1224 item.applyUnits(units);
1227 }
else if (
is<vector<AnyValue>>()) {
1228 for (
auto& v : as<vector<AnyValue>>()) {
1229 v.applyUnits(units);
1231 }
else if (is<Quantity>()) {
1232 auto& Q = as<Quantity>();
1234 Q.converter(Q.value, *units);
1235 m_equals = Q.value.m_equals;
1239 }
else if (Q.value.is<
double>()) {
1240 if (Q.isActivationEnergy) {
1241 *
this = Q.value.as<
double>() / units->convertActivationEnergyTo(1.0, Q.units);
1243 *
this = Q.value.as<
double>() / units->convertTo(1.0, Q.units);
1245 }
else if (Q.value.is<vector<double>>()) {
1246 double factor = 1.0 / units->convertTo(1.0, Q.units);
1247 auto& old = Q.value.asVector<
double>();
1248 vector<double> converted(old.size());
1249 scale(old.begin(), old.end(), converted.begin(), factor);
1250 *
this = std::move(converted);
1252 throw CanteraError(
"AnyValue::applyUnits",
"Don't know how to "
1253 "convert Quantity with held type '{}' in key '{}'",
1254 Q.value.type_str(),
m_key);
1261 as<AnyMap>().setFlowStyle();
1267const vector<AnyValue>& AnyValue::asVector<AnyValue>(
size_t nMin,
size_t nMax)
const
1269 if (!
is<vector<AnyValue>>()) {
1271 if (
is<vector<double>>()) {
1272 for (
const auto& el :
asVector<double>()) {
1276 }
else if (
is<vector<long int>>()) {
1277 for (
const auto& el :
asVector<long int>()) {
1281 }
else if (
is<vector<string>>()) {
1282 for (
const auto& el :
asVector<string>()) {
1290 const auto& vv = as<vector<AnyValue>>();
1291 m_equals = eq_comparer<vector<AnyValue>>;
1292 checkSize(vv, nMin, nMax);
1297vector<AnyValue>& AnyValue::asVector<AnyValue>(
size_t nMin,
size_t nMax)
1299 auto& v =
const_cast<vector<AnyValue>&
>(
1300 const_cast<const AnyValue*
>(
this)->asVector<AnyValue>());
1301 checkSize(v, nMin, nMax);
1306const vector<double>& AnyValue::asVector<double>(
size_t nMin,
size_t nMax)
const
1308 if (
is<vector<long int>>()) {
1310 for (
const auto& el :
asVector<long int>()) {
1315 const auto& vv = as<vector<double>>();
1316 m_equals = eq_comparer<vector<double>>;
1317 checkSize(vv, nMin, nMax);
1322vector<double>& AnyValue::asVector<double>(
size_t nMin,
size_t nMax)
1324 if (
is<vector<long int>>()) {
1326 for (
const auto& el :
asVector<long int>()) {
1331 auto& vv = as<vector<double>>();
1332 m_equals = eq_comparer<vector<double>>;
1333 checkSize(vv, nMin, nMax);
1338const vector<vector<double>>& AnyValue::asVector<vector<double>>(
size_t nMin,
size_t nMax)
const
1340 if (is<vector<vector<long int>>>()) {
1341 vector<vector<double>> v;
1342 for (
const auto& outer : asVector<vector<long int>>()) {
1343 v.push_back(vector<double>());
1344 for (
const auto& inner : outer) {
1345 v.back().push_back(inner);
1348 const_cast<AnyValue*
>(
this)->m_value = v;
1350 const auto& vv = as<vector<vector<double>>>();
1351 m_equals = eq_comparer<vector<vector<double>>>;
1352 checkSize(vv, nMin, nMax);
1357vector<vector<double>>& AnyValue::asVector<vector<double>>(
size_t nMin,
size_t nMax)
1359 if (is<vector<vector<long int>>>()) {
1360 vector<vector<double>> v;
1361 for (
const auto& outer : asVector<vector<long int>>()) {
1362 v.push_back(vector<double>());
1363 for (
const auto& inner : outer) {
1364 v.back().push_back(inner);
1369 auto& vv = as<vector<vector<double>>>();
1370 m_equals = eq_comparer<vector<vector<double>>>;
1371 checkSize(vv, nMin, nMax);
1376const vector<AnyMap>& AnyValue::asVector<AnyMap>(
size_t nMin,
size_t nMax)
const
1380 v.push_back(std::move(as<AnyMap>()));
1382 }
else if (
is<vector<AnyValue>>() && asVector<AnyValue>().empty()) {
1385 const auto& vv = as<vector<AnyMap>>();
1386 checkSize(vv, nMin, nMax);
1391vector<AnyMap>& AnyValue::asVector<AnyMap>(
size_t nMin,
size_t nMax)
1395 v.push_back(std::move(as<AnyMap>()));
1397 }
else if (
is<vector<AnyValue>>() && asVector<AnyValue>().
empty()) {
1400 auto& vv = as<vector<AnyMap>>();
1401 checkSize(vv, nMin, nMax);
1414 const auto& iter =
m_data.find(key);
1415 if (iter ==
m_data.end()) {
1432 return iter->second;
1440 }
catch (std::out_of_range&) {
1442 "Key '{}' not found.\nExisting keys: {}", key,
keys_str());
1454 value.
setLoc(line, column);
1462 }
catch (std::out_of_range&) {
1464 "Key '{}' not found.\nExisting keys: {}", key,
keys_str());
1471 for ([[maybe_unused]]
const auto& item : *
this) {
1479 auto iter =
m_data.find(key);
1480 return (iter !=
m_data.end() && !iter->second.is<Exclude>());
1495 for (
const auto& [key, value] : other.
ordered()) {
1496 if (!keepExisting ||
m_data.count(key) == 0) {
1497 (*this)[key] = value;
1509 fmt::memory_buffer b;
1510 auto iter = this->
begin();
1511 if (iter != this->
end()) {
1515 while (iter != this->
end()) {
1519 return to_string(b);
1525 auto iter = this->
begin();
1526 while (iter != this->
end()) {
1527 out.insert(iter->first);
1536 for (
auto& [name, value] :
m_data) {
1549 (*m_metadata)[key] = value;
1568 for (
const auto& [key, value] : *other.
m_metadata) {
1569 (*m_metadata)[key] = value;
1577 return (
hasKey(key)) ?
m_data.at(key).asBool() : default_;
1582 return (
hasKey(key)) ?
m_data.at(key).asDouble() : default_;
1587 return (
hasKey(key)) ?
m_data.at(key).asInt() : default_;
1592 return (
hasKey(key)) ?
m_data.at(key).asString() : default_;
1606 double default_)
const
1616 size_t nMin,
size_t nMax)
const
1618 return units().
convert(
at(key).asVector<AnyValue>(nMin, nMax), dest);
1621AnyMap::Iterator::Iterator(
1622 const std::unordered_map<string, AnyValue>::const_iterator& start,
1623 const std::unordered_map<string, AnyValue>::const_iterator& stop)
1627 while (m_iter != m_stop && (dunder(m_iter->first) || m_iter->second.is<Exclude>())) {
1635 while (m_iter != m_stop && (dunder(m_iter->first) || m_iter->second.is<Exclude>())) {
1642AnyMap::OrderedProxy::OrderedProxy(
const AnyMap& data,
bool withUnits)
1646 if (withUnits &&
m_data->hasKey(
"__units__")
1649 m_units = make_unique<pair<const string, AnyValue>>(
1650 "units",
m_data->at(
"__units__"));
1651 m_units->second.setFlowStyle();
1652 m_ordered.emplace_back(pair<int, int>{-2, 0},
m_units.get());
1657 for (
auto& item : *
m_data) {
1658 const auto& order = item.second.order();
1659 if (order.first == -1) {
1660 head = std::min(head, order.second);
1661 tail = std::max(tail, order.second);
1663 m_ordered.emplace_back(order, &item);
1665 std::sort(m_ordered.begin(), m_ordered.end());
1669 if (
m_data->hasKey(
"__type__")) {
1670 bool order_changed =
false;
1671 const auto& itemType =
m_data->at(
"__type__").asString();
1672 std::unique_lock<std::mutex> lock(yaml_field_order_mutex);
1675 for (
auto& [order, item] : m_ordered) {
1676 if (order.first >= 0) {
1681 if (item->first == key) {
1682 order.second = --head;
1683 order_changed =
true;
1690 for (
auto& [order, item] : m_ordered) {
1691 if (order.first >= 0) {
1696 if (item->first == key) {
1697 order.second = ++tail;
1698 order_changed =
true;
1704 if (order_changed) {
1705 std::sort(m_ordered.begin(), m_ordered.end());
1712 return OrderedIterator(m_ordered.begin(), m_ordered.end());
1717 return OrderedIterator(m_ordered.end(), m_ordered.end());
1720AnyMap::OrderedIterator::OrderedIterator(
1721 const AnyMap::OrderedProxy::OrderVector::const_iterator& start,
1722 const AnyMap::OrderedProxy::OrderVector::const_iterator& stop)
1732 for ([[maybe_unused]]
const auto& item : *
this) {
1738bool AnyMap::operator==(
const AnyMap& other)
const
1742 for (
auto& [key, value] : *this) {
1743 if (!other.
hasKey(key)) {
1748 for (
auto & [key, value] : other) {
1749 if (!
hasKey(key) || value !=
at(key)) {
1756bool AnyMap::operator!=(
const AnyMap& other)
const
1758 return !(*
this == other);
1771 if (
hasKey(
"__units__")) {
1777 for (
auto& [name, item] :
m_data) {
1784 if (
hasKey(
"__units__")) {
1786 m_data[
"__units__"][dimension] = value;
1795 m_data[
"__flow__"] = flow;
1799 const vector<vector<string>>& specs)
1801 std::unique_lock<std::mutex> lock(yaml_field_order_mutex);
1802 for (
const auto& spec : specs) {
1803 if (spec.at(0) ==
"head") {
1806 }
else if (spec.at(0) ==
"tail") {
1810 "Unknown ordering rule '{}'", spec.at(0));
1819 if (
s_cache.count(fullName)) {
1827 YAML::Node node = YAML::Load(yaml);
1828 amap = node.as<
AnyMap>();
1829 }
catch (YAML::Exception& err) {
1831 fake.
setLoc(err.mark.line, err.mark.column);
1845 size_t islash = parent_name.find_last_of(
"/\\");
1846 if (islash !=
npos) {
1847 string parent_path = parent_name.substr(0, islash);
1848 if (std::ifstream(parent_path +
"/" + name).good()) {
1849 fullName = parent_path +
"/" + name;
1853 if (fullName.empty()) {
1859 auto mtime = std::filesystem::last_write_time(fullName);
1860 std::unique_lock<std::mutex> lock(yaml_cache_mutex);
1861 auto iter =
s_cache.find(fullName);
1862 if (iter !=
s_cache.end() && iter->second.second == mtime) {
1863 return iter->second.first;
1866 if (!std::ifstream(fullName).good()) {
1867 throw CanteraError(
"AnyMap::fromYamlFile",
"Input file '{}' not found "
1868 "on the Cantera search path.", name);
1872 auto& [cache_item, cache_time] =
s_cache[fullName];
1875 YAML::Node node = YAML::LoadFile(fullName);
1876 cache_item = node.as<
AnyMap>();
1878 cache_item.applyUnits();
1879 }
catch (YAML::Exception& err) {
1882 fake.
setLoc(err.mark.line, err.mark.column);
1889 cache_item[
"__file__"] = fullName;
1890 cache_item.setLoc(0, 0);
1892 if (cache_item.hasKey(
"deprecated")) {
1900string AnyMap::toYamlString()
const
1905 out << YAML::Newline;
1918void formatInputFile(fmt::memory_buffer& b,
const shared_ptr<AnyMap>& metadata,
1919 const string& filename,
int lineno,
int column,
int lineno2=-1,
int column2=-1)
1921 if (lineno2 == -1) {
1927 if (!metadata->hasKey(
"file-contents")) {
1929 std::stringstream buffer;
1930 buffer << infile.rdbuf();
1931 (*metadata)[
"file-contents"] = buffer.str();
1936 std::stringstream contents((*metadata)[
"file-contents"].asString());
1937 while (std::getline(contents, line)) {
1938 if (i == lineno || i == lineno2) {
1942 }
else if ((lineno + 4 > i && lineno < i + 6) ||
1943 (lineno2 + 4 > i && lineno2 < i + 6)) {
1944 if (lastShown >= 0 && i - lastShown > 1) {
1955string InputFileError::formatError(
const string& message,
int lineno,
int column,
1956 const shared_ptr<AnyMap>& metadata)
1961 string filename = metadata->getString(
"filename",
"input string");
1963 fmt::memory_buffer b;
1964 fmt_append(b,
"Error on line {} of {}:\n{}\n", lineno+1, filename, message);
1965 formatInputFile(b, metadata, filename, lineno, column);
1966 return to_string(b);
1969string InputFileError::formatError2(
const string& message,
int line1,
int column1,
1970 const shared_ptr<AnyMap>& metadata1,
1971 int line2,
int column2,
1972 const shared_ptr<AnyMap>& metadata2)
1974 if (!metadata1 || !metadata2) {
1977 string filename1 = metadata1->getString(
"filename",
"input string");
1978 string filename2 = metadata2->getString(
"filename",
"input string");
1980 fmt::memory_buffer b;
1981 if (filename1 == filename2) {
1982 fmt_append(b,
"Error on lines {} and {} of {}:\n",
1983 std::min(line1, line2) + 1, std::max(line1, line2) + 1, filename1);
1985 formatInputFile(b, metadata1, filename1, line1, column1, line2, column2);
1987 fmt_append(b,
"Error on line {} of {} and line {} of {}:\n{}\n",
1988 line1+1, filename1, line2+1, filename2, message);
1989 formatInputFile(b, metadata1, filename1, line1, column1);
1991 formatInputFile(b, metadata2, filename2, line2, column2);
1994 return to_string(b);
2004 string filename = node.
m_metadata->getString(
"filename",
"input string");
2005 fmt::memory_buffer b;
void emitString(YAML::Emitter &out, const string &str0)
Write YAML strings spanning multiple lines if input includes endline ' '.
void emitFlowVector(YAML::Emitter &out, const vector< double > &v, long int precision)
Write a vector in YAML "flow" style, wrapping lines to avoid exceeding the preferred maximum line len...
Base class defining common data possessed by both AnyMap and AnyValue objects.
int m_column
If m_line >= 0, the column where this value occurs in the input file.
void setLoc(int line, int column)
For values which are derived from an input file, set the line and column of this value in that file.
int m_line
The line where this value occurs in the input file.
friend void warn_deprecated(const string &source, const AnyBase &node, const string &message)
A deprecation warning for syntax in an input file.
const AnyValue & getMetadata(const string &key) const
Get a value from the metadata applicable to the AnyMap tree containing this node.
shared_ptr< AnyMap > m_metadata
Metadata relevant to an entire AnyMap tree, such as information about.
Defined to allow use with range-based for loops.
Defined to allow the OrderedProxy class to be used with range-based for loops.
A map of string keys to values whose type can vary at runtime.
static AnyMap fromYamlString(const string &yaml)
Create an AnyMap from a string containing a YAML document.
Iterator begin() const
Defined to allow use with range-based for loops.
AnyValue & createForYaml(const string &key, int line, int column)
Used to create a new item which will be populated from a YAML input string, where the item with key o...
set< string > keys() const
Return an unordered set of keys.
size_t size() const
Returns the number of elements in this map.
void exclude(const string &key)
Mark key as excluded from this map.
long int getInt(const string &key, long int default_) const
If key exists, return it as a long int, otherwise return default_.
void copyMetadata(const AnyMap &other)
Copy metadata including input line/column from an existing AnyMap.
static std::unordered_map< string, pair< AnyMap, std::filesystem::file_time_type > > s_cache
Cache for previously-parsed input (YAML) files.
double getDouble(const string &key, double default_) const
If key exists, return it as a double, otherwise return default_.
bool hasKey(const string &key) const
Returns true if the map contains an item named key.
const UnitSystem & units() const
Return the default units that should be used to convert stored values.
Iterator end() const
Defined to allow use with range-based for loops.
bool empty() const
Return boolean indicating whether AnyMap is empty.
static void clearCachedFile(const string &filename)
Remove the specified file from the input cache if it is present.
void applyUnits()
Use the supplied UnitSystem to set the default units, and recursively process overrides from nodes na...
static std::unordered_map< string, vector< string > > s_headFields
Information about fields that should appear first when outputting to YAML.
double convert(const string &key, const string &units) const
Convert the item stored by the given key to the units specified in units.
void setMetadata(const string &key, const AnyValue &value)
Set a metadata value that applies to this AnyMap and its children.
AnyValue & operator[](const string &key)
Get the value of the item stored in key.
OrderedProxy ordered(bool withUnits=false) const
Return a proxy object that allows iteration in an order determined by the order of insertion,...
void setFlowStyle(bool flow=true)
Use "flow" style when outputting this AnyMap to YAML.
void propagateMetadata(shared_ptr< AnyMap > &file)
Propagate metadata to any child elements.
bool getBool(const string &key, bool default_) const
If key exists, return it as a bool, otherwise return default_.
static std::unordered_map< string, vector< string > > s_tailFields
Information about fields that should appear last when outputting to YAML.
void clear()
Erase all items in the mapping.
shared_ptr< UnitSystem > m_units
The default units that are used to convert stored values.
const string & getString(const string &key, const string &default_) const
If key exists, return it as a string, otherwise return default_.
void erase(const string &key)
Erase the value held by key.
static AnyMap fromYamlFile(const string &name, const string &parent_name="")
Create an AnyMap from a YAML file.
std::unordered_map< string, AnyValue > m_data
The stored data.
const AnyValue & at(const string &key) const
Get the value of the item stored in key.
void update(const AnyMap &other, bool keepExisting=true)
Add items from other to this AnyMap.
static bool addOrderingRules(const string &objectType, const vector< vector< string > > &specs)
Add global rules for setting the order of elements when outputting AnyMap objects to YAML.
void setUnits(const UnitSystem &units)
Set the unit system for this AnyMap.
string keys_str() const
Return a string listing the keys in this AnyMap, for use in error messages, for example.
vector< double > convertVector(const string &key, const string &units, size_t nMin=npos, size_t nMax=npos) const
Convert a vector of dimensional values.
A wrapper for a variable whose type is determined at runtime.
const string & asString() const
Return the held value, if it is a string.
bool isVector() const
Returns true if the held value is a vector of the specified type, such as vector<double>.
void setKey(const string &key)
Set the name of the key storing this value in an AnyMap.
pair< int, int > order() const
Return values used to determine the sort order when outputting to YAML.
bool hasMapWhere(const string &key, const string &value) const
Returns true when getMapWhere() would succeed.
void setQuantity(double value, const string &units, bool is_act_energy=false)
Assign a scalar quantity with units as a string, for example {3.0, "m^2"}.
bool hasKey(const string &key) const
Returns true if this AnyValue is an AnyMap and that map contains a key with the given name.
map< string, T > asMap() const
Return the held AnyMap as a map where all of the values have the specified type.
bool & asBool()
Return the held value, if it is a bool.
bool empty() const
Return boolean indicating whether AnyValue is empty.
pair< size_t, size_t > matrixShape() const
Returns rows and columns of a matrix.
size_t vectorSize() const
Returns size of the held vector.
long int & asInt()
Return the held value, if it is a long int.
void applyUnits(shared_ptr< UnitSystem > &units)
See AnyMap::applyUnits()
const std::type_info & type() const
Returns the type of the held value.
double & asDouble()
Return the held value as a double, if it is a double or a long int.
static AnyValue exclude()
bool isScalar() const
Returns true if the held value is a scalar type (such as double, long int, string,...
AnyValue & operator[](const string &key)
If this AnyValue is an AnyMap, return the value stored in key.
string m_key
Key of this value in a parent AnyMap
AnyMap & getMapWhere(const string &key, const string &value, bool create=false)
Treating the value as vector<AnyMap>, return the item where the given key has the specified value.
void setFlowStyle(bool flow=true)
See AnyMap::setFlowStyle()
void propagateMetadata(shared_ptr< AnyMap > &file)
Propagate metadata to any child elements.
std::any m_value
The held value.
bool is() const
Returns true if the held value is of the specified type.
const vector< T > & asVector(size_t nMin=npos, size_t nMax=npos) const
Return the held value, if it is a vector of type T.
const T & as() const
Get the value of this key as the specified type.
string type_str() const
Returns a string specifying the type of the held value.
Base class for exceptions thrown by Cantera classes.
An error indicating that an unimplemented function has been called.
double convert(double value, const string &src, const string &dest) const
Convert value from the units of src to the units of dest.
AnyMap getDelta(const UnitSystem &other) const
Get the changes to the defaults from other to this UnitSystem.
A representation of the units associated with a dimensional quantity.
void fmt_append(fmt::memory_buffer &b, const std::string &tmpl, Args... args)
Versions 6.2.0 and 6.2.1 of fmtlib do not include this define before they include windows....
This file contains definitions for utility functions and text for modules, inputfiles and logging,...
double fpValue(const string &val)
Translate a string into one double value.
string demangle(const std::type_info &type)
Convert a type name to a human readable string, using boost::core::demangle if available.
void scale(InputIter begin, InputIter end, OutputIter out, S scale_factor)
Multiply elements of an array by a scale factor.
Namespace for the Cantera kernel.
const size_t npos
index returned by functions to indicate "no position"
Contains declarations for string manipulation functions within Cantera.
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...