8 #include "cantera/base/yaml.h"
12 #include <boost/algorithm/string.hpp>
15 #include <unordered_set>
17 namespace ba = boost::algorithm;
21 std::mutex yaml_cache_mutex;
22 std::mutex yaml_field_order_mutex;
25 bool 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) {
75 bool 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])) {
97 bool isBool(
const string& val) {
98 string str = ba::trim_copy(val);
99 return (val ==
"true" || val ==
"True" || val ==
"false" || val ==
"False");
102 enum class Type : char {
112 Type operator|(Type lhs, Type rhs)
114 return Type(
static_cast<char>(lhs) |
static_cast<char>(rhs));
117 Type elementTypes(
const YAML::Node& node)
120 Type types = Type::Unknown;
121 for (
const auto& el : node) {
123 types = types | Type::Map;
124 }
else if (el.IsSequence()) {
125 types = types | Type::Sequence;
126 }
else if (el.IsScalar()) {
127 string nodestr = el.as<
string>();
128 if (isInt(nodestr)) {
129 types = types | Type::Integer;
130 }
else if (isFloat(nodestr)) {
131 types = types | Type::Double;
132 }
else if (isBool(nodestr)) {
133 types = types | Type::Bool;
135 types = types | Type::String;
144 long int precision = 15;
145 auto& userPrecision = precisionSource.
getMetadata(
"precision");
146 if (userPrecision.is<
long int>()) {
147 precision = userPrecision.
asInt();
152 string formatDouble(
double x,
long int precision)
162 bool useExp = std::abs(x) < 1e-2 || std::abs(x) >= 1e4;
167 s0 = fmt::format(fmt::format(
"{:.{}e}", x, precision));
169 last = s0.size() - 5;
170 if (s0[last + 1] ==
'e') {
172 }
else if (s0[last] ==
'e') {
175 last = s0.find(
'e') - 1;
178 log10x =
static_cast<int>(std::floor(std::log10(std::abs(x))));
179 s0 = fmt::format(
"{:.{}f}", x, precision - log10x);
180 last = s0.size() - 1;
182 if (s0[last - 2] ==
'0' && s0[last - 1] ==
'0' && s0[last] <
'5') {
184 }
else if (s0[last - 2] ==
'9' && s0[last - 1] ==
'9' && s0[last] >
'4') {
186 }
else if (s0[last - 1] ==
'0' && s0[last] ==
'0') {
195 if (s0[last - 1] ==
'0') {
198 s1 = fmt::format(fmt::format(
"{:.{}e}", x, precision - 2));
200 s1 = fmt::format(
"{:.{}f}", x, precision - log10x - 2);
202 size_t digit = last - 2;
203 while (s1[digit] ==
'0' && s1[digit - 1] !=
'.') {
209 size_t eloc = s1.find(
'e');
210 s0 = string(s1.begin() + eloc, s1.end());
212 s1 = string(s1.begin(), s1.begin() + digit + 1);
223 bool isActivationEnergy;
224 AnyValue::unitConverter converter;
226 bool operator==(
const Quantity& other)
const {
227 return value == other.value && units == other.units
228 && isActivationEnergy == other.isActivationEnergy;
239 static const int max_line_length = 87;
248 target.
setLoc(node.Mark().line, node.Mark().column);
249 if (node.IsSequence()) {
251 target[
"items"] = node.as<
AnyValue>();
253 }
else if (!node.IsMap()) {
254 string text = YAML::Dump(node);
255 if (text.size() > 300) {
259 "YAML node is not a map. Node begins with:\n'''\n{}\n'''", text);
261 for (
const auto& child : node) {
262 string key = child.first.as<
string>();
263 const auto& loc = child.second.Mark();
265 if (child.second.IsMap()) {
276 YAML::Emitter& operator<<(YAML::Emitter& out,
const AnyMap& rhs)
278 bool flow = rhs.
getBool(
"__flow__",
false);
281 out << YAML::BeginMap;
283 for (
const auto& [name, value] : rhs.ordered()) {
285 bool foundType =
true;
286 if (value.
is<
double>()) {
287 valueStr = formatDouble(value.
asDouble(), getPrecision(value));
288 }
else if (value.
is<
string>()) {
290 }
else if (value.
is<
long int>()) {
291 valueStr = fmt::format(
"{}", value.
asInt());
292 }
else if (value.
is<
bool>()) {
293 valueStr = fmt::format(
"{}", value.
asBool());
301 if (width + name.size() + valueStr.size() + 4 > max_line_length) {
302 out << YAML::Newline;
307 width += name.size() + valueStr.size() + 4;
310 out << YAML::Newline;
317 out << YAML::BeginMap;
318 for (
const auto& [key, value] : rhs.ordered()) {
329 size_t endline = str0.rfind(
'\n');
337 if (endline == str1.size() - 1) {
338 str1.erase(endline, 1);
339 endline = str1.rfind(
'\n');
345 while (str1[endline +
len] ==
' ') {
348 while (str1[endline - 1] ==
' ') {
354 str1.replace(endline,
len,
"\n");
356 endline = str1.rfind(
'\n', endline - 1);
358 out << YAML::Literal << str1;
365 void emitFlowVector(YAML::Emitter& out,
const vector<double>& v,
long int precision)
368 out << YAML::BeginSeq;
371 string xstr = formatDouble(x, precision);
373 if (width + xstr.size() > max_line_length) {
374 out << YAML::Newline;
378 width += xstr.size() + 2;
385 template <
typename T>
389 out << YAML::BeginSeq;
391 for (
const T& x : v) {
392 string xstr = fmt::format(
"{}", x);
394 if (width + xstr.size() > max_line_length) {
395 out << YAML::Newline;
399 width += xstr.size() + 2;
404 YAML::Emitter& operator<<(YAML::Emitter& out,
const AnyValue& rhs)
407 if (rhs.
is<
string>()) {
409 }
else if (rhs.
is<
double>()) {
410 out << formatDouble(rhs.
asDouble(), getPrecision(rhs));
411 }
else if (rhs.
is<
long int>()) {
413 }
else if (rhs.
is<
bool>()) {
416 throw CanteraError(
"operator<<(YAML::Emitter&, AnyValue&)",
417 "Don't know how to encode value of type '{}' with key '{}'",
422 }
else if (rhs.
is<vector<AnyMap>>()) {
424 }
else if (rhs.
is<vector<double>>()) {
426 }
else if (rhs.
is<vector<string>>()) {
428 }
else if (rhs.
is<vector<long int>>()) {
430 }
else if (rhs.
is<vector<bool>>()) {
432 }
else if (rhs.
is<vector<Cantera::AnyValue>>()) {
434 }
else if (rhs.
is<vector<vector<double>>>()) {
435 const auto& v = rhs.
asVector<vector<double>>();
436 long int precision = getPrecision(rhs);
437 out << YAML::BeginSeq;
438 for (
const auto& u : v) {
442 }
else if (rhs.
is<vector<vector<string>>>()) {
443 const auto& v = rhs.
asVector<vector<string>>();
444 out << YAML::BeginSeq;
445 for (
const auto& u : v) {
449 }
else if (rhs.
is<vector<vector<long int>>>()) {
450 const auto& v = rhs.
asVector<vector<long int>>();
451 out << YAML::BeginSeq;
452 for (
const auto& u : v) {
456 }
else if (rhs.
is<vector<vector<bool>>>()) {
457 const auto& v = rhs.
asVector<vector<bool>>();
458 out << YAML::BeginSeq;
459 for (
const auto& u : v) {
464 throw CanteraError(
"operator<<(YAML::Emitter&, AnyValue&)",
465 "Don't know how to encode value of type '{}' with key '{}'",
479 target.
setLoc(node.Mark().line, node.Mark().column);
480 if (node.IsScalar()) {
482 string nodestr = node.as<
string>();
483 if (node.Tag() ==
"!") {
488 }
else if (isInt(nodestr)) {
490 target = node.
as<
long int>();
491 }
catch (YAML::BadConversion&) {
495 target = node.
as<
double>();
497 }
else if (isFloat(nodestr)) {
499 }
else if (isBool(nodestr)) {
500 target = node.
as<
bool>();
505 }
else if (node.IsSequence()) {
507 Type types = elementTypes(node);
508 if (types == Type::Integer) {
509 target = node.
as<vector<long int>>();
510 }
else if (types == (Type::Integer | Type::Double) || types == Type::Double) {
511 vector<double> values;
512 for (
const auto& elem : node) {
513 values.push_back(
fpValue(elem.as<
string>()));
515 target = std::move(values);
516 }
else if (types == Type::String) {
517 target = node.
as<vector<string>>();
518 }
else if (types == Type::Bool) {
519 target = node.
as<vector<bool>>();
520 }
else if (types == Type::Map) {
521 target = node.
as<vector<AnyMap>>();
522 }
else if (types == Type::Sequence) {
524 Type subtypes = Type::Unknown;
525 for (
const auto& el : node) {
526 subtypes = subtypes | elementTypes(el);
528 if (subtypes == Type::Integer) {
529 target = node.
as<vector<vector<long int>>>();
530 }
else if (subtypes == (Type::Integer | Type::Double) || subtypes == Type::Double) {
531 vector<vector<double>> values;
532 for (
const auto& row : node) {
533 values.emplace_back();
534 for (
const auto& value : row) {
535 values.back().push_back(
fpValue(value.
as<
string>()));
538 target = std::move(values);
539 }
else if (subtypes == Type::String) {
540 target = node.
as<vector<vector<string>>>();
541 }
else if (subtypes == Type::Bool) {
542 target = node.
as<vector<vector<bool>>>();
544 target = node.
as<vector<AnyValue>>();
548 target = node.
as<vector<AnyValue>>();
551 }
else if (node.IsMap()) {
554 }
else if (node.IsNull()) {
566 std::unordered_map<string,
567 pair<AnyMap, std::filesystem::file_time_type>> AnyMap::s_cache;
569 std::unordered_map<string, vector<string>> AnyMap::s_headFields;
570 std::unordered_map<string, vector<string>> AnyMap::s_tailFields;
574 void AnyBase::setLoc(
int line,
int column)
580 const AnyValue& AnyBase::getMetadata(
const string& key)
const
582 if (m_metadata && m_metadata->hasKey(key)) {
583 return m_metadata->at(key);
592 : m_equals(eq_comparer<size_t>)
595 AnyValue::~AnyValue() =
default;
597 bool AnyValue::operator==(
const AnyValue& other)
const
602 bool AnyValue::operator!=(
const AnyValue& other)
const
609 return as<AnyMap>()[key];
614 return as<AnyMap>().at(key);
618 return (is<AnyMap>() && as<AnyMap>().
hasKey(key));
632 }
else if (
is<vector<AnyValue>>()) {
633 for (
auto& item : asVector<AnyValue>()) {
636 }
else if (
is<vector<AnyMap>>()) {
637 for (
auto& item : asVector<AnyMap>()) {
652 return is<double>() || is<long int>() || is<string>() || is<bool>();
656 if (isVector<double>()) {
657 return as<vector<double>>().size();
659 if (isVector<long int>()) {
660 return as<vector<long int>>().size();
662 if (isVector<string>()) {
663 return as<vector<string>>().size();
665 if (isVector<bool>()) {
666 return as<vector<bool>>().size();
673 auto& mat = as<vector<vector<double>>>();
674 if (isMatrix<double>()) {
676 return {mat.size(), mat[0].size()};
678 return {mat.size(), 0};
680 return {mat.size(),
npos};
683 auto& mat = as<vector<vector<long int>>>();
684 if (isMatrix<long int>()) {
686 return {mat.size(), mat[0].size()};
688 return {mat.size(), 0};
690 return {mat.size(),
npos};
693 auto& mat = as<vector<vector<string>>>();
694 if (isMatrix<string>()) {
696 return {mat.size(), mat[0].size()};
698 return {mat.size(), 0};
700 return {mat.size(),
npos};
703 auto& mat = as<vector<vector<bool>>>();
704 if (isMatrix<bool>()) {
706 return {mat.size(), mat[0].size()};
708 return {mat.size(), 0};
710 return {mat.size(),
npos};
717 AnyValue::AnyValue(
const string& value)
719 , m_equals(eq_comparer<string>)
722 AnyValue::AnyValue(
const char* value)
723 : m_value{string(value)}
724 , m_equals(eq_comparer<string>)
727 AnyValue &AnyValue::operator=(
const string &value) {
729 m_equals = eq_comparer<string>;
733 AnyValue &AnyValue::operator=(
const char *value) {
735 m_equals = eq_comparer<string>;
743 bool AnyValue::operator==(
const string& other)
const
745 if (
m_value.type() ==
typeid(
string)) {
746 return std::any_cast<string>(
m_value) == other;
752 bool AnyValue::operator!=(
const string& other)
const
754 return !(*
this == other);
757 bool operator==(
const string& lhs,
const AnyValue& rhs)
762 bool operator!=(
const string& lhs,
const AnyValue& rhs)
771 m_equals = eq_comparer<Quantity>;
776 m_equals = eq_comparer<Quantity>;
783 m_equals = eq_comparer<Quantity>;
788 m_value = Quantity{value,
Units(0.0),
false, converter};
789 m_equals = eq_comparer<Quantity>;
793 bool AnyValue::is<vector<double>>()
const
795 if (m_value.type() ==
typeid(vector<double>)) {
797 }
else if (m_value.type() ==
typeid(vector<AnyValue>)) {
798 for (
const auto& item : as<vector<AnyValue>>()) {
799 if (!(item.is<
double>()
800 || (item.is<Quantity>() && item.as<Quantity>().value.is<
double>())))
813 AnyValue::AnyValue(
double value)
815 , m_equals(eq_comparer<double>)
818 AnyValue &AnyValue::operator=(
double value) {
820 m_equals = eq_comparer<double>;
832 bool AnyValue::operator==(
const double& other)
const
834 if (
m_value.type() ==
typeid(
double)) {
835 return std::any_cast<double>(
m_value) == other;
836 }
else if (
m_value.type() ==
typeid(
long int)) {
837 return std::any_cast<long int>(
m_value) == other;
843 bool AnyValue::operator!=(
const double& other)
const
845 return !(*
this == other);
848 bool operator==(
const double& lhs,
const AnyValue& rhs)
853 bool operator!=(
const double& lhs,
const AnyValue& rhs)
860 AnyValue::AnyValue(
bool value)
862 , m_equals(eq_comparer<bool>)
865 AnyValue &AnyValue::operator=(
bool value) {
867 m_equals = eq_comparer<bool>;
881 AnyValue::AnyValue(
long int value)
883 , m_equals(eq_comparer<long int>)
886 AnyValue::AnyValue(
int value)
887 : m_value{static_cast<long int>(value)}
888 , m_equals(eq_comparer<long int>)
891 AnyValue &AnyValue::operator=(
long int value) {
893 m_equals = eq_comparer<long int>;
897 AnyValue &AnyValue::operator=(
int value) {
898 m_value =
static_cast<long int>(value);
899 m_equals = eq_comparer<long int>;
904 return as<long int>();
908 return as<long int>();
911 bool AnyValue::operator==(
const long int& other)
const
913 if (
m_value.type() ==
typeid(
long int)) {
914 return std::any_cast<long int>(
m_value) == other;
915 }
else if (
m_value.type() ==
typeid(
double)) {
916 return std::any_cast<double>(
m_value) == other;
922 bool AnyValue::operator!=(
const long int& other)
const
924 return !(*
this == other);
927 bool AnyValue::operator==(
const int& other)
const
929 return *
this ==
static_cast<long int>(other);
932 bool AnyValue::operator!=(
const int& other)
const
934 return *
this !=
static_cast<long int>(other);
937 bool operator==(
const long int& lhs,
const AnyValue& rhs)
942 bool operator!=(
const long int& lhs,
const AnyValue& rhs)
947 bool operator==(
const int& lhs,
const AnyValue& rhs)
952 bool operator!=(
const int& lhs,
const AnyValue& rhs)
959 AnyValue::AnyValue(
const AnyMap& value)
961 , m_equals(eq_comparer<
AnyMap>)
966 m_equals = eq_comparer<AnyMap>;
972 m_equals = eq_comparer<AnyMap>;
978 std::unordered_map<string, const AnyMap*> mapped;
979 for (
const auto& item : asVector<AnyMap>()) {
980 auto key = item[name].asString();
981 if (mapped.count(key)) {
983 "Duplicate key '{}'", key);
985 mapped.emplace(std::make_pair(key, &item));
990 std::unordered_map<string, AnyMap*>
AnyValue::asMap(
const string& name)
992 std::unordered_map<string, AnyMap*> mapped;
993 for (
auto& item : asVector<AnyMap>()) {
994 auto key = item.at(name).asString();
995 if (mapped.count(key)) {
997 "Duplicate key '{}'", key);
999 mapped.emplace(std::make_pair(key, &item));
1006 if (
is<vector<AnyMap>>()) {
1008 return asVector<AnyMap>().
at(0);
1010 for (
auto& item : asVector<AnyMap>()) {
1011 if (item.hasKey(key) && item[key] == value) {
1016 "List does not contain a map where '{}' = '{}'", key, value);
1017 }
else if (is<AnyMap>()) {
1018 if (value ==
"" || (
hasKey(key) && as<AnyMap>()[key] == value)) {
1019 return as<AnyMap>();
1022 "Map does not contain a key where '{}' = '{}'", key, value);
1024 }
else if (is<void>()) {
1026 "Key '{}' not found",
m_key);
1029 "Element is not a mapping or list of mappings.\n"
1030 "Looking for a mapping with key '{}' = '{}'", key, value);
1036 if (
is<vector<AnyMap>>()) {
1038 return asVector<AnyMap>().
at(0);
1040 for (
auto& item : asVector<AnyMap>()) {
1041 if (item.hasKey(key) && item[key] == value) {
1047 auto& vec = asVector<AnyMap>();
1050 vec.push_back(std::move(child));
1054 "List does not contain a map where '{}' = '{}'", key, value);
1056 }
else if (is<AnyMap>()) {
1057 if (value ==
"" || (
hasKey(key) && as<AnyMap>()[key] == value)) {
1058 return as<AnyMap>();
1059 }
else if (create) {
1061 newChild[key] = value;
1062 vector<AnyMap> nodes{std::move(as<AnyMap>()), std::move(newChild)};
1063 operator=(std::move(nodes));
1064 return asVector<AnyMap>().back();
1067 "Map does not contain a key where '{}' = '{}'", key, value);
1069 }
else if (is<void>() && create) {
1072 operator=(std::move(child));
1073 return as<AnyMap>();
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>>()) {
1090 for (
auto& item : asVector<AnyMap>()) {
1091 if (item.hasKey(key) && item[key] == value) {
1096 }
else if (is<AnyMap>()) {
1097 if (value ==
"" || (
hasKey(key) && as<AnyMap>()[key] == value)) {
1115 AnyMap& m = as<AnyMap>();
1117 if (m.
getBool(
"__unconvertible__",
false)) {
1122 throw CanteraError(
"AnyValue::applyUnits",
"AnyMap contains values"
1123 " that cannot be converted to non-default unit systems\n(probably"
1124 " reaction rates not associated with a Kinetics object)");
1129 }
else if (
is<vector<AnyMap>>()) {
1130 auto& list = as<vector<AnyMap>>();
1131 if (list.size() && list[0].hasKey(
"units") && list[0].size() == 1) {
1134 auto deltaUnits = list[0][
"units"];
1135 list[0].m_data.erase(
"units");
1136 for (
auto& item : list) {
1137 if (item.hasKey(
"units")) {
1138 if (item.size() == 1) {
1141 "Found units entry as not the first item in a list.");
1144 auto& childUnits = item[
"units"].as<
AnyMap>();
1145 for (
auto& [dimension, unit] : deltaUnits) {
1146 if (!childUnits.hasKey(dimension)) {
1147 childUnits[dimension] = unit;
1151 }
else if (item.hasKey(
"__units__")) {
1153 auto& childUnits = item[
"__units__"].as<
AnyMap>();
1154 for (
auto& [dimension, unit] : deltaUnits) {
1155 if (!childUnits.hasKey(dimension)) {
1156 childUnits[dimension] = unit;
1160 item[
"__units__"] = deltaUnits;
1165 list.erase(list.begin());
1168 for (
auto& item : list) {
1170 if (item.size() == 1 && item.hasKey(
"units")) {
1172 "Found units entry as not the first item in a list.");
1174 item.applyUnits(units);
1177 }
else if (
is<vector<AnyValue>>()) {
1178 for (
auto& v :
as<vector<AnyValue>>()) {
1179 v.applyUnits(units);
1181 }
else if (is<Quantity>()) {
1182 auto& Q = as<Quantity>();
1184 Q.converter(Q.value, *units);
1185 m_equals = Q.value.m_equals;
1189 }
else if (Q.value.is<
double>()) {
1190 if (Q.isActivationEnergy) {
1191 *
this = Q.value.as<
double>() / units->convertActivationEnergyTo(1.0, Q.units);
1193 *
this = Q.value.as<
double>() / units->convertTo(1.0, Q.units);
1195 }
else if (Q.value.is<vector<double>>()) {
1196 double factor = 1.0 / units->convertTo(1.0, Q.units);
1197 auto& old = Q.value.asVector<
double>();
1198 vector<double> converted(old.size());
1199 scale(old.begin(), old.end(), converted.begin(), factor);
1200 *
this = std::move(converted);
1202 throw CanteraError(
"AnyValue::applyUnits",
"Don't know how to "
1203 "convert Quantity with held type '{}' in key '{}'",
1204 Q.value.type_str(),
m_key);
1211 as<AnyMap>().setFlowStyle();
1217 const vector<AnyValue>& AnyValue::asVector<AnyValue>(
size_t nMin,
size_t nMax)
const
1219 if (!
is<vector<AnyValue>>()) {
1221 if (
is<vector<double>>()) {
1222 for (
const auto& el : asVector<double>()) {
1226 }
else if (
is<vector<long int>>()) {
1227 for (
const auto& el : asVector<long int>()) {
1231 }
else if (
is<vector<string>>()) {
1232 for (
const auto& el : asVector<string>()) {
1240 const auto& vv = as<vector<AnyValue>>();
1241 m_equals = eq_comparer<vector<AnyValue>>;
1242 checkSize(vv, nMin, nMax);
1247 vector<AnyValue>& AnyValue::asVector<AnyValue>(
size_t nMin,
size_t nMax)
1249 auto& v =
const_cast<vector<AnyValue>&
>(
1250 const_cast<const AnyValue*
>(
this)->asVector<AnyValue>());
1251 checkSize(v, nMin, nMax);
1256 const vector<double>& AnyValue::asVector<double>(
size_t nMin,
size_t nMax)
const
1258 if (
is<vector<long int>>()) {
1260 for (
const auto& el : asVector<long int>()) {
1265 const auto& vv = as<vector<double>>();
1266 m_equals = eq_comparer<vector<double>>;
1267 checkSize(vv, nMin, nMax);
1272 vector<double>& AnyValue::asVector<double>(
size_t nMin,
size_t nMax)
1274 if (
is<vector<long int>>()) {
1276 for (
const auto& el : asVector<long int>()) {
1281 auto& vv = as<vector<double>>();
1282 m_equals = eq_comparer<vector<double>>;
1283 checkSize(vv, nMin, nMax);
1288 const vector<vector<double>>& AnyValue::asVector<vector<double>>(
size_t nMin,
size_t nMax)
const
1290 if (is<vector<vector<long int>>>()) {
1291 vector<vector<double>> v;
1292 for (
const auto& outer : asVector<vector<long int>>()) {
1293 v.push_back(vector<double>());
1294 for (
const auto& inner : outer) {
1295 v.back().push_back(inner);
1298 const_cast<AnyValue*
>(
this)->m_value = v;
1300 const auto& vv = as<vector<vector<double>>>();
1301 m_equals = eq_comparer<vector<vector<double>>>;
1302 checkSize(vv, nMin, nMax);
1307 vector<vector<double>>& AnyValue::asVector<vector<double>>(
size_t nMin,
size_t nMax)
1309 if (is<vector<vector<long int>>>()) {
1310 vector<vector<double>> v;
1311 for (
const auto& outer : asVector<vector<long int>>()) {
1312 v.push_back(vector<double>());
1313 for (
const auto& inner : outer) {
1314 v.back().push_back(inner);
1319 auto& vv = as<vector<vector<double>>>();
1320 m_equals = eq_comparer<vector<vector<double>>>;
1321 checkSize(vv, nMin, nMax);
1326 const vector<AnyMap>& AnyValue::asVector<AnyMap>(
size_t nMin,
size_t nMax)
const
1330 v.push_back(std::move(as<AnyMap>()));
1332 }
else if (
is<vector<AnyValue>>() && asVector<AnyValue>().empty()) {
1335 const auto& vv = as<vector<AnyMap>>();
1336 checkSize(vv, nMin, nMax);
1341 vector<AnyMap>& AnyValue::asVector<AnyMap>(
size_t nMin,
size_t nMax)
1345 v.push_back(std::move(as<AnyMap>()));
1347 }
else if (
is<vector<AnyValue>>() && asVector<AnyValue>().
empty()) {
1350 auto& vv = as<vector<AnyMap>>();
1351 checkSize(vv, nMin, nMax);
1364 const auto& iter =
m_data.find(key);
1365 if (iter ==
m_data.end()) {
1382 return iter->second;
1390 }
catch (std::out_of_range&) {
1392 "Key '{}' not found.\nExisting keys: {}", key,
keys_str());
1404 value.
setLoc(line, column);
1412 }
catch (std::out_of_range&) {
1414 "Key '{}' not found.\nExisting keys: {}", key,
keys_str());
1420 return m_data.size() == 0;
1440 for (
const auto& [key, value] : other) {
1441 if (!keepExisting || !
hasKey(key)) {
1442 (*this)[key] = value;
1449 fmt::memory_buffer b;
1450 auto iter = this->
begin();
1451 if (iter != this->
end()) {
1455 while (iter != this->
end()) {
1459 return to_string(b);
1465 auto iter = this->
begin();
1466 while (iter != this->
end()) {
1467 out.insert(iter->first);
1476 for (
auto& [name, value] :
m_data) {
1489 (*m_metadata)[key] = value;
1508 for (
const auto& [key, value] : *other.
m_metadata) {
1509 (*m_metadata)[key] = value;
1517 return (
hasKey(key)) ?
m_data.at(key).asBool() : default_;
1522 return (
hasKey(key)) ?
m_data.at(key).asDouble() : default_;
1527 return (
hasKey(key)) ?
m_data.at(key).asInt() : default_;
1532 return (
hasKey(key)) ?
m_data.at(key).asString() : default_;
1546 double default_)
const
1556 size_t nMin,
size_t nMax)
const
1558 return units().
convert(
at(key).asVector<AnyValue>(nMin, nMax), dest);
1561 AnyMap::Iterator::Iterator(
1562 const std::unordered_map<string, AnyValue>::const_iterator& start,
1563 const std::unordered_map<string, AnyValue>::const_iterator& stop)
1567 while (m_iter != m_stop
1568 && ba::starts_with(m_iter->first,
"__")
1569 && ba::ends_with(m_iter->first,
"__")) {
1577 while (m_iter != m_stop
1578 && ba::starts_with(m_iter->first,
"__")
1579 && ba::ends_with(m_iter->first,
"__")) {
1586 AnyMap::OrderedProxy::OrderedProxy(
const AnyMap& data)
1591 m_units = make_unique<pair<const string, AnyValue>>(
1592 "units",
m_data->at(
"__units__"));
1593 m_units->second.setFlowStyle();
1594 m_ordered.emplace_back(pair<int, int>{-2, 0},
m_units.get());
1599 for (
auto& item : *
m_data) {
1600 const auto& order = item.second.order();
1601 if (order.first == -1) {
1602 head = std::min(head, order.second);
1603 tail = std::max(tail, order.second);
1605 m_ordered.emplace_back(order, &item);
1607 std::sort(m_ordered.begin(), m_ordered.end());
1611 if (
m_data->hasKey(
"__type__")) {
1612 bool order_changed =
false;
1613 const auto& itemType =
m_data->at(
"__type__").asString();
1614 std::unique_lock<std::mutex> lock(yaml_field_order_mutex);
1617 for (
auto& [order, item] : m_ordered) {
1618 if (order.first >= 0) {
1623 if (item->first == key) {
1624 order.second = --head;
1625 order_changed =
true;
1632 for (
auto& [order, item] : m_ordered) {
1633 if (order.first >= 0) {
1638 if (item->first == key) {
1639 order.second = ++tail;
1640 order_changed =
true;
1646 if (order_changed) {
1647 std::sort(m_ordered.begin(), m_ordered.end());
1654 return OrderedIterator(m_ordered.begin(), m_ordered.end());
1659 return OrderedIterator(m_ordered.end(), m_ordered.end());
1662 AnyMap::OrderedIterator::OrderedIterator(
1663 const AnyMap::OrderedProxy::OrderVector::const_iterator& start,
1664 const AnyMap::OrderedProxy::OrderVector::const_iterator& stop)
1670 bool AnyMap::operator==(
const AnyMap& other)
const
1674 for (
auto& [key, value] : *
this) {
1675 if (!other.
hasKey(key)) {
1680 for (
auto & [key, value] : other) {
1681 if (!
hasKey(key) || value !=
at(key)) {
1688 bool AnyMap::operator!=(
const AnyMap& other)
const
1703 if (
hasKey(
"__units__")) {
1709 for (
auto& [name, item] :
m_data) {
1716 if (
hasKey(
"__units__")) {
1718 m_data[
"__units__"][dimension] = value;
1727 (*this)[
"__flow__"] = flow;
1731 const vector<vector<string>>& specs)
1733 std::unique_lock<std::mutex> lock(yaml_field_order_mutex);
1734 for (
const auto& spec : specs) {
1735 if (spec.at(0) ==
"head") {
1737 }
else if (spec.at(0) ==
"tail") {
1741 "Unknown ordering rule '{}'", spec.at(0));
1750 if (
s_cache.count(fullName)) {
1758 YAML::Node node = YAML::Load(yaml);
1759 amap = node.as<
AnyMap>();
1760 }
catch (YAML::Exception& err) {
1762 fake.
setLoc(err.mark.line, err.mark.column);
1775 size_t islash = parent_name.find_last_of(
"/\\");
1776 if (islash !=
npos) {
1777 string parent_path = parent_name.substr(0, islash);
1778 if (std::ifstream(parent_path +
"/" + name).good()) {
1779 fullName = parent_path +
"/" + name;
1783 if (fullName.empty()) {
1789 auto mtime = std::filesystem::last_write_time(fullName);
1790 std::unique_lock<std::mutex> lock(yaml_cache_mutex);
1791 auto iter =
s_cache.find(fullName);
1792 if (iter !=
s_cache.end() && iter->second.second == mtime) {
1793 return iter->second.first;
1796 if (!std::ifstream(fullName).good()) {
1797 throw CanteraError(
"AnyMap::fromYamlFile",
"Input file '{}' not found "
1798 "on the Cantera search path.", name);
1802 auto& [cache_item, cache_time] =
s_cache[fullName];
1805 YAML::Node node = YAML::LoadFile(fullName);
1806 cache_item = node.as<
AnyMap>();
1808 cache_item.applyUnits();
1809 }
catch (YAML::Exception& err) {
1812 fake.
setLoc(err.mark.line, err.mark.column);
1819 cache_item[
"__file__"] = fullName;
1821 if (cache_item.hasKey(
"deprecated")) {
1829 string AnyMap::toYamlString()
const
1834 out << YAML::Newline;
1847 void formatInputFile(fmt::memory_buffer& b,
const shared_ptr<AnyMap>& metadata,
1848 const string& filename,
int lineno,
int column,
int lineno2=-1,
int column2=-1)
1850 if (lineno2 == -1) {
1856 if (!metadata->hasKey(
"file-contents")) {
1858 std::stringstream buffer;
1859 buffer << infile.rdbuf();
1860 (*metadata)[
"file-contents"] = buffer.str();
1865 std::stringstream contents((*metadata)[
"file-contents"].asString());
1866 while (std::getline(contents, line)) {
1867 if (i == lineno || i == lineno2) {
1871 }
else if ((lineno + 4 > i && lineno < i + 6) ||
1872 (lineno2 + 4 > i && lineno2 < i + 6)) {
1873 if (lastShown >= 0 && i - lastShown > 1) {
1884 string InputFileError::formatError(
const string& message,
int lineno,
int column,
1885 const shared_ptr<AnyMap>& metadata)
1890 string filename = metadata->getString(
"filename",
"input string");
1892 fmt::memory_buffer b;
1893 fmt_append(b,
"Error on line {} of {}:\n{}\n", lineno+1, filename, message);
1894 formatInputFile(b, metadata, filename, lineno, column);
1895 return to_string(b);
1898 string InputFileError::formatError2(
const string& message,
int line1,
int column1,
1899 const shared_ptr<AnyMap>& metadata1,
1900 int line2,
int column2,
1901 const shared_ptr<AnyMap>& metadata2)
1903 if (!metadata1 || !metadata2) {
1906 string filename1 = metadata1->getString(
"filename",
"input string");
1907 string filename2 = metadata2->getString(
"filename",
"input string");
1909 fmt::memory_buffer b;
1910 if (filename1 == filename2) {
1911 fmt_append(b,
"Error on lines {} and {} of {}:\n",
1912 std::min(line1, line2) + 1, std::max(line1, line2) + 1, filename1);
1914 formatInputFile(b, metadata1, filename1, line1, column1, line2, column2);
1916 fmt_append(b,
"Error on line {} of {} and line {} of {}:\n{}\n",
1917 line1+1, filename1, line2+1, filename2, message);
1918 formatInputFile(b, metadata1, filename1, line1, column1);
1920 formatInputFile(b, metadata2, filename2, line2, column2);
1923 return to_string(b);
1933 string filename = node.
m_metadata->getString(
"filename",
"input string");
1934 fmt::memory_buffer b;
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...
void emitString(YAML::Emitter &out, const string &str0)
Write YAML strings spanning multiple lines if input includes endline ' '.
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.
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.
size_t size() const
Returns the number of elements in 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.
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.
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.
const UnitSystem & units() const
Return the default units that should be used to convert stored values.
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.
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.
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, 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.
U len(const T &container)
Get the size of a container, cast to a signed integer type.
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...