8#include "cantera/base/yaml.h"
12#include <boost/algorithm/string.hpp>
15#include <unordered_set>
17namespace ba = boost::algorithm;
23std::mutex yaml_cache_mutex;
24std::mutex yaml_field_order_mutex;
27bool isFloat(
const std::string& val)
31 std::string str = ba::trim_copy(val);
40 if (ch ==
'+' || ch ==
'-') {
42 if (str.size() == 1) {
46 for (
size_t i = istart; i < str.size(); i++) {
50 }
else if (ch ==
'.') {
58 }
else if (ch ==
'e' || ch ==
'E') {
60 if (numExp > 1 || numDigit == 0 || i == str.size() - 1) {
64 if (ch ==
'+' || ch ==
'-') {
65 if (i + 1 == str.size() - 1) {
77bool isInt(
const std::string& val)
79 std::string str = ba::trim_copy(val);
85 if (ch ==
'+' || ch ==
'-') {
87 if (str.size() == 1) {
91 for (
size_t i = istart; i < str.size(); i++) {
92 if (!isdigit(str[i])) {
99bool isBool(
const std::string& val) {
100 std::string str = ba::trim_copy(val);
101 return (val ==
"true" || val ==
"True" || val ==
"false" || val ==
"False");
104enum class Type : char {
114Type operator|(Type lhs, Type rhs)
116 return Type(
static_cast<char>(lhs) |
static_cast<char>(rhs));
119Type elementTypes(
const YAML::Node& node)
122 Type types = Type::Unknown;
123 for (
const auto& el : node) {
125 types = types | Type::Map;
126 }
else if (el.IsSequence()) {
127 types = types | Type::Sequence;
128 }
else if (el.IsScalar()) {
129 std::string nodestr = el.as<std::string>();
130 if (isInt(nodestr)) {
131 types = types | Type::Integer;
132 }
else if (isFloat(nodestr)) {
133 types = types | Type::Double;
134 }
else if (isBool(nodestr)) {
135 types = types | Type::Bool;
137 types = types | Type::String;
146 long int precision = 15;
147 auto& userPrecision = precisionSource.
getMetadata(
"precision");
148 if (userPrecision.is<
long int>()) {
149 precision = userPrecision.
asInt();
154string formatDouble(
double x,
long int precision)
164 bool useExp = std::abs(x) < 1e-2 || std::abs(x) >= 1e4;
169 s0 = fmt::format(fmt::format(
"{:.{}e}", x, precision));
171 last = s0.size() - 5;
172 if (s0[last + 1] ==
'e') {
174 }
else if (s0[last] ==
'e') {
177 last = s0.find(
'e') - 1;
180 log10x =
static_cast<int>(std::floor(std::log10(std::abs(x))));
181 s0 = fmt::format(
"{:.{}f}", x, precision - log10x);
182 last = s0.size() - 1;
184 if (s0[last - 2] ==
'0' && s0[last - 1] ==
'0' && s0[last] <
'5') {
186 }
else if (s0[last - 2] ==
'9' && s0[last - 1] ==
'9' && s0[last] >
'4') {
188 }
else if (s0[last - 1] ==
'0' && s0[last] ==
'0') {
197 if (s0[last - 1] ==
'0') {
200 s1 = fmt::format(fmt::format(
"{:.{}e}", x, precision - 2));
202 s1 = fmt::format(
"{:.{}f}", x, precision - log10x - 2);
204 size_t digit = last - 2;
205 while (s1[digit] ==
'0' && s1[digit - 1] !=
'.') {
211 size_t eloc = s1.find(
'e');
212 s0 = string(s1.begin() + eloc, s1.end());
214 s1 = string(s1.begin(), s1.begin() + digit + 1);
225 bool isActivationEnergy;
226 AnyValue::unitConverter converter;
228 bool operator==(
const Quantity& other)
const {
229 return value == other.value && units == other.units
230 && isActivationEnergy == other.isActivationEnergy;
241static const int max_line_length = 87;
250 target.
setLoc(node.Mark().line, node.Mark().column);
251 if (node.IsSequence()) {
253 target[
"items"] = node.as<
AnyValue>();
255 }
else if (!node.IsMap()) {
256 std::string text = YAML::Dump(node);
257 if (text.size() > 300) {
261 "YAML node is not a map. Node begins with:\n'''\n{}\n'''", text);
263 for (
const auto& child : node) {
264 std::string key = child.first.as<std::string>();
265 const auto& loc = child.second.Mark();
267 if (child.second.IsMap()) {
278YAML::Emitter& operator<<(YAML::Emitter& out,
const AnyMap& rhs)
280 bool flow = rhs.
getBool(
"__flow__",
false);
283 out << YAML::BeginMap;
285 for (
const auto& item : rhs.ordered()) {
286 const auto& name = item.first;
287 const auto& value = item.second;
289 bool foundType =
true;
290 if (value.
is<
double>()) {
291 valueStr = formatDouble(value.
asDouble(), getPrecision(value));
292 }
else if (value.
is<
string>()) {
294 }
else if (value.
is<
long int>()) {
295 valueStr = fmt::format(
"{}", value.
asInt());
296 }
else if (value.
is<
bool>()) {
297 valueStr = fmt::format(
"{}", value.
asBool());
305 if (width + name.size() + valueStr.size() + 4 > max_line_length) {
306 out << YAML::Newline;
311 width += name.size() + valueStr.size() + 4;
314 out << YAML::Newline;
321 out << YAML::BeginMap;
322 for (
const auto& item : rhs.ordered()) {
333 size_t endline = str0.rfind(
'\n');
341 if (endline == str1.size() - 1) {
342 str1.erase(endline, 1);
343 endline = str1.rfind(
'\n');
349 while (str1[endline + len] ==
' ') {
352 while (str1[endline - 1] ==
' ') {
358 str1.replace(endline, len,
"\n");
360 endline = str1.rfind(
'\n', endline - 1);
362 out << YAML::Literal << str1;
369void emitFlowVector(YAML::Emitter& out,
const vector<double>& v,
long int precision)
372 out << YAML::BeginSeq;
375 string xstr = formatDouble(x, precision);
377 if (width + xstr.size() > max_line_length) {
378 out << YAML::Newline;
382 width += xstr.size() + 2;
393 out << YAML::BeginSeq;
395 for (
const auto& x : v) {
396 string xstr = fmt::format(
"{}", x);
398 if (width + xstr.size() > max_line_length) {
399 out << YAML::Newline;
403 width += xstr.size() + 2;
408YAML::Emitter& operator<<(YAML::Emitter& out,
const AnyValue& rhs)
411 if (rhs.
is<
string>()) {
413 }
else if (rhs.
is<
double>()) {
414 out << formatDouble(rhs.
asDouble(), getPrecision(rhs));
415 }
else if (rhs.
is<
long int>()) {
417 }
else if (rhs.
is<
bool>()) {
420 throw CanteraError(
"operator<<(YAML::Emitter&, AnyValue&)",
421 "Don't know how to encode value of type '{}' with key '{}'",
426 }
else if (rhs.
is<vector<AnyMap>>()) {
428 }
else if (rhs.
is<vector<double>>()) {
430 }
else if (rhs.
is<vector<string>>()) {
432 }
else if (rhs.
is<vector<long int>>()) {
434 }
else if (rhs.
is<vector<bool>>()) {
436 }
else if (rhs.
is<vector<Cantera::AnyValue>>()) {
438 }
else if (rhs.
is<vector<vector<double>>>()) {
439 const auto& v = rhs.
asVector<vector<double>>();
440 long int precision = getPrecision(rhs);
441 out << YAML::BeginSeq;
442 for (
const auto& u : v) {
446 }
else if (rhs.
is<vector<vector<string>>>()) {
447 const auto& v = rhs.
asVector<vector<string>>();
448 out << YAML::BeginSeq;
449 for (
const auto& u : v) {
453 }
else if (rhs.
is<vector<vector<long int>>>()) {
454 const auto& v = rhs.
asVector<vector<long int>>();
455 out << YAML::BeginSeq;
456 for (
const auto& u : v) {
460 }
else if (rhs.
is<vector<vector<bool>>>()) {
461 const auto& v = rhs.
asVector<vector<bool>>();
462 out << YAML::BeginSeq;
463 for (
const auto& u : v) {
468 throw CanteraError(
"operator<<(YAML::Emitter&, AnyValue&)",
469 "Don't know how to encode value of type '{}' with key '{}'",
483 target.
setLoc(node.Mark().line, node.Mark().column);
484 if (node.IsScalar()) {
486 std::string nodestr = node.as<std::string>();
487 if (node.Tag() ==
"!") {
492 }
else if (isInt(nodestr)) {
494 target = node.
as<
long int>();
495 }
catch (YAML::BadConversion&) {
499 target = node.
as<
double>();
501 }
else if (isFloat(nodestr)) {
503 }
else if (isBool(nodestr)) {
504 target = node.
as<
bool>();
509 }
else if (node.IsSequence()) {
511 Type types = elementTypes(node);
512 if (types == Type::Integer) {
513 target = node.
as<std::vector<long int>>();
514 }
else if (types == (Type::Integer | Type::Double) || types == Type::Double) {
516 }
else if (types == Type::String) {
517 target = node.
as<std::vector<std::string>>();
518 }
else if (types == Type::Bool) {
519 target = node.
as<std::vector<bool>>();
520 }
else if (types == Type::Map) {
521 target = node.
as<std::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<std::vector<std::vector<long int>>>();
530 }
else if (subtypes == (Type::Integer | Type::Double) || subtypes == Type::Double) {
531 target = node.
as<std::vector<std::vector<double>>>();
532 }
else if (subtypes == Type::String) {
533 target = node.
as<std::vector<std::vector<std::string>>>();
534 }
else if (subtypes == Type::Bool) {
535 target = node.
as<std::vector<std::vector<bool>>>();
537 target = node.
as<std::vector<AnyValue>>();
541 target = node.
as<std::vector<AnyValue>>();
544 }
else if (node.IsMap()) {
547 }
else if (node.IsNull()) {
559std::unordered_map<std::string, std::pair<AnyMap, int>>
AnyMap::s_cache;
571void AnyBase::setLoc(
int line,
int column)
590 , m_value(new boost::any{})
591 , m_equals(eq_comparer<size_t>)
594AnyValue::~AnyValue() =
default;
596AnyValue::AnyValue(
AnyValue const& other)
599 , m_value(new boost::any{*other.m_value})
600 , m_equals(other.m_equals)
606 , m_key(std::move(other.m_key))
607 , m_value(std::move(other.m_value))
608 , m_equals(other.m_equals)
613 if (
this == &other) {
616 AnyBase::operator=(other);
619 m_equals = other.m_equals;
624 if (
this == &other) {
627 AnyBase::operator=(std::move(other));
630 m_equals = other.m_equals;
634bool AnyValue::operator==(
const AnyValue& other)
const
639bool AnyValue::operator!=(
const AnyValue& other)
const
646 return as<AnyMap>()[key];
651 return as<AnyMap>().at(key);
655 return (is<AnyMap>() && as<AnyMap>().
hasKey(key));
669 }
else if (
is<std::vector<AnyValue>>()) {
670 for (
auto& item : asVector<AnyValue>()) {
673 }
else if (
is<std::vector<AnyMap>>()) {
674 for (
auto& item : asVector<AnyMap>()) {
689 return is<double>() || is<long int>() || is<std::string>() || is<bool>();
694AnyValue::AnyValue(
const std::string& value)
695 : m_value(new boost::any{value})
696 , m_equals(eq_comparer<std::string>)
699AnyValue::AnyValue(
const char* value)
700 : m_value(new boost::any{std::string(value)})
701 , m_equals(eq_comparer<std::string>)
704AnyValue &AnyValue::operator=(
const std::string &value) {
706 m_equals = eq_comparer<std::string>;
710AnyValue &AnyValue::operator=(
const char *value) {
712 m_equals = eq_comparer<std::string>;
717 return as<std::string>();
720bool AnyValue::operator==(
const std::string& other)
const
722 if (
m_value->type() ==
typeid(std::string)) {
723 return boost::any_cast<std::string>(*
m_value) == other;
729bool AnyValue::operator!=(
const std::string& other)
const
731 return !(*
this == other);
734bool operator==(
const std::string& lhs,
const AnyValue& rhs)
739bool operator!=(
const std::string& lhs,
const AnyValue& rhs)
748 m_equals = eq_comparer<Quantity>;
753 m_equals = eq_comparer<Quantity>;
760 m_equals = eq_comparer<Quantity>;
765 *
m_value = Quantity{value,
Units(0.0),
false, converter};
766 m_equals = eq_comparer<Quantity>;
770bool AnyValue::is<vector<double>>()
const
772 if (m_value->type() ==
typeid(vector<double>)) {
774 }
else if (m_value->type() ==
typeid(vector<AnyValue>)) {
775 for (
const auto& item : as<vector<AnyValue>>()) {
776 if (!(item.is<
double>()
777 || (item.is<Quantity>() && item.as<Quantity>().value.is<
double>())))
790AnyValue::AnyValue(
double value)
791 : m_value(new boost::any{value})
792 , m_equals(eq_comparer<double>)
795AnyValue &AnyValue::operator=(
double value) {
797 m_equals = eq_comparer<double>;
809bool AnyValue::operator==(
const double& other)
const
811 if (
m_value->type() ==
typeid(
double)) {
812 return boost::any_cast<double>(*
m_value) == other;
813 }
else if (
m_value->type() ==
typeid(
long int)) {
814 return boost::any_cast<long int>(*
m_value) == other;
820bool AnyValue::operator!=(
const double& other)
const
822 return !(*
this == other);
825bool operator==(
const double& lhs,
const AnyValue& rhs)
830bool operator!=(
const double& lhs,
const AnyValue& rhs)
837AnyValue::AnyValue(
bool value)
838 : m_value(new boost::any{value})
839 , m_equals(eq_comparer<bool>)
842AnyValue &AnyValue::operator=(
bool value) {
844 m_equals = eq_comparer<bool>;
858AnyValue::AnyValue(
long int value)
859 : m_value(new boost::any{value})
860 , m_equals(eq_comparer<long int>)
863AnyValue::AnyValue(
int value)
864 : m_value(new boost::any{static_cast<long int>(value)})
865 , m_equals(eq_comparer<long int>)
868AnyValue &AnyValue::operator=(
long int value) {
870 m_equals = eq_comparer<long int>;
874AnyValue &AnyValue::operator=(
int value) {
875 *
m_value =
static_cast<long int>(value);
876 m_equals = eq_comparer<long int>;
881 return as<long int>();
885 return as<long int>();
888bool AnyValue::operator==(
const long int& other)
const
890 if (
m_value->type() ==
typeid(
long int)) {
891 return boost::any_cast<long int>(*
m_value) == other;
892 }
else if (
m_value->type() ==
typeid(
double)) {
893 return boost::any_cast<double>(*
m_value) == other;
899bool AnyValue::operator!=(
const long int& other)
const
901 return !(*
this == other);
904bool AnyValue::operator==(
const int& other)
const
906 return *
this ==
static_cast<long int>(other);
909bool AnyValue::operator!=(
const int& other)
const
911 return *
this !=
static_cast<long int>(other);
914bool operator==(
const long int& lhs,
const AnyValue& rhs)
919bool operator!=(
const long int& lhs,
const AnyValue& rhs)
924bool operator==(
const int& lhs,
const AnyValue& rhs)
929bool operator!=(
const int& lhs,
const AnyValue& rhs)
936AnyValue::AnyValue(
const AnyMap& value)
937 : m_value(new boost::any{value})
938 , m_equals(eq_comparer<
AnyMap>)
943 m_equals = eq_comparer<AnyMap>;
949 m_equals = eq_comparer<AnyMap>;
954 const std::string& name)
const
956 std::unordered_map<std::string, const AnyMap*> mapped;
957 for (
const auto& item : asVector<AnyMap>()) {
958 auto key = item[name].asString();
959 if (mapped.count(key)) {
961 "Duplicate key '{}'", key);
963 mapped.emplace(std::make_pair(key, &item));
968std::unordered_map<std::string, AnyMap*>
AnyValue::asMap(
const std::string& name)
970 std::unordered_map<std::string, AnyMap*> mapped;
971 for (
auto& item : asVector<AnyMap>()) {
972 auto key = item.at(name).asString();
973 if (mapped.count(key)) {
975 "Duplicate key '{}'", key);
977 mapped.emplace(std::make_pair(key, &item));
984 if (
is<std::vector<AnyMap>>()) {
986 return asVector<AnyMap>().
at(0);
988 for (
auto& item : asVector<AnyMap>()) {
989 if (item.hasKey(key) && item[key] == value) {
994 "List does not contain a map where '{}' = '{}'", key, value);
995 }
else if (is<AnyMap>()) {
996 if (value ==
"" || (
hasKey(key) && as<AnyMap>()[key] == value)) {
1000 "Map does not contain a key where '{}' = '{}'", key, value);
1002 }
else if (is<void>()) {
1004 "Key '{}' not found",
m_key);
1007 "Element is not a mapping or list of mappings.\n"
1008 "Looking for a mapping with key '{}' = '{}'", key, value);
1015 if (
is<std::vector<AnyMap>>()) {
1017 return asVector<AnyMap>().
at(0);
1019 for (
auto& item : asVector<AnyMap>()) {
1020 if (item.hasKey(key) && item[key] == value) {
1026 auto& vec = asVector<AnyMap>();
1029 vec.push_back(std::move(child));
1033 "List does not contain a map where '{}' = '{}'", key, value);
1035 }
else if (is<AnyMap>()) {
1036 if (value ==
"" || (
hasKey(key) && as<AnyMap>()[key] == value)) {
1037 return as<AnyMap>();
1038 }
else if (create) {
1040 newChild[key] = value;
1041 std::vector<AnyMap> nodes{std::move(as<AnyMap>()), std::move(newChild)};
1042 operator=(std::move(nodes));
1043 return asVector<AnyMap>().back();
1046 "Map does not contain a key where '{}' = '{}'", key, value);
1048 }
else if (is<void>() && create) {
1051 operator=(std::move(child));
1052 return as<AnyMap>();
1053 }
else if (is<void>()) {
1055 "Key '{}' not found",
m_key);
1058 "Element is not a mapping or list of mappings.\n"
1059 "Looking for a mapping with key '{}' = '{}'", key, value);
1065 if (
is<std::vector<AnyMap>>()) {
1069 for (
auto& item : asVector<AnyMap>()) {
1070 if (item.hasKey(key) && item[key] == value) {
1075 }
else if (is<AnyMap>()) {
1076 if (value ==
"" || (
hasKey(key) && as<AnyMap>()[key] == value)) {
1094 AnyMap& m = as<AnyMap>();
1096 if (m.
getBool(
"__unconvertible__",
false)) {
1101 throw CanteraError(
"AnyValue::applyUnits",
"AnyMap contains values"
1102 " that cannot be converted to non-default unit systems\n(probably"
1103 " reaction rates not associated with a Kinetics object)");
1108 }
else if (
is<std::vector<AnyMap>>()) {
1109 auto& list = as<std::vector<AnyMap>>();
1110 if (list.size() && list[0].hasKey(
"units") && list[0].size() == 1) {
1113 auto deltaUnits = list[0][
"units"];
1114 list[0].m_data.erase(
"units");
1115 for (
auto& item : list) {
1116 if (item.hasKey(
"units")) {
1117 if (item.size() == 1) {
1120 "Found units entry as not the first item in a list.");
1123 auto& childUnits = item[
"units"].as<
AnyMap>();
1124 for (
auto& jtem : deltaUnits) {
1125 if (!childUnits.hasKey(jtem.first)) {
1126 childUnits[jtem.first] = jtem.second;
1130 }
else if (item.hasKey(
"__units__")) {
1132 auto& childUnits = item[
"__units__"].as<
AnyMap>();
1133 for (
auto& jtem : deltaUnits) {
1134 if (!childUnits.hasKey(jtem.first)) {
1135 childUnits[jtem.first] = jtem.second;
1139 item[
"__units__"] = deltaUnits;
1144 list.erase(list.begin());
1147 for (
auto& item : list) {
1149 if (item.size() == 1 && item.hasKey(
"units")) {
1151 "Found units entry as not the first item in a list.");
1153 item.applyUnits(units);
1156 }
else if (
is<vector<AnyValue>>()) {
1157 for (
auto& v :
as<vector<AnyValue>>()) {
1158 v.applyUnits(units);
1160 }
else if (is<Quantity>()) {
1161 auto& Q = as<Quantity>();
1163 Q.converter(Q.value, *units);
1164 m_equals = Q.value.m_equals;
1167 m_value = std::move(Q.value.m_value);
1168 }
else if (Q.value.is<
double>()) {
1169 if (Q.isActivationEnergy) {
1170 *
this = Q.value.as<
double>() / units->convertActivationEnergyTo(1.0, Q.units);
1172 *
this = Q.value.as<
double>() / units->convertTo(1.0, Q.units);
1175 double factor = 1.0 / units->convertTo(1.0, Q.units);
1176 auto& old = Q.value.asVector<
double>();
1178 scale(old.begin(), old.end(), converted.begin(), factor);
1179 *
this = std::move(converted);
1181 throw CanteraError(
"AnyValue::applyUnits",
"Don't know how to "
1182 "convert Quantity with held type '{}' in key '{}'",
1183 Q.value.type_str(),
m_key);
1190 as<AnyMap>().setFlowStyle();
1196const std::vector<AnyValue>& AnyValue::asVector<AnyValue>(
size_t nMin,
size_t nMax)
const
1198 if (!
is<std::vector<AnyValue>>()) {
1199 std::vector<AnyValue> v;
1200 if (
is<std::vector<double>>()) {
1201 for (
const auto& el : asVector<double>()) {
1205 }
else if (
is<std::vector<long int>>()) {
1206 for (
const auto& el : asVector<long int>()) {
1210 }
else if (
is<std::vector<std::string>>()) {
1211 for (
const auto& el : asVector<std::string>()) {
1219 const auto& vv = as<std::vector<AnyValue>>();
1220 m_equals = eq_comparer<std::vector<AnyValue>>;
1221 checkSize(vv, nMin, nMax);
1226std::vector<AnyValue>& AnyValue::asVector<AnyValue>(
size_t nMin,
size_t nMax)
1228 auto& v =
const_cast<std::vector<AnyValue>&
>(
1229 const_cast<const AnyValue*
>(
this)->asVector<AnyValue>());
1230 checkSize(v, nMin, nMax);
1235const std::vector<double>& AnyValue::asVector<double>(
size_t nMin,
size_t nMax)
const
1237 if (
is<std::vector<long int>>()) {
1238 std::vector<double> v;
1239 for (
const auto& el : asVector<long int>()) {
1244 const auto& vv = as<std::vector<double>>();
1245 m_equals = eq_comparer<std::vector<double>>;
1246 checkSize(vv, nMin, nMax);
1251std::vector<double>& AnyValue::asVector<double>(
size_t nMin,
size_t nMax)
1253 if (
is<std::vector<long int>>()) {
1254 std::vector<double> v;
1255 for (
const auto& el : asVector<long int>()) {
1260 auto& vv = as<std::vector<double>>();
1261 m_equals = eq_comparer<std::vector<double>>;
1262 checkSize(vv, nMin, nMax);
1267const std::vector<vector_fp>& AnyValue::asVector<vector_fp>(
size_t nMin,
size_t nMax)
const
1269 if (
is<std::vector<std::vector<long int>>>()) {
1270 std::vector<vector_fp> v;
1271 for (
const auto& outer :
asVector<std::vector<long int>>()) {
1273 for (
const auto& inner : outer) {
1274 v.back().push_back(inner);
1279 const auto& vv = as<std::vector<vector_fp>>();
1280 m_equals = eq_comparer<std::vector<vector_fp>>;
1281 checkSize(vv, nMin, nMax);
1286std::vector<vector_fp>& AnyValue::asVector<vector_fp>(
size_t nMin,
size_t nMax)
1288 if (
is<std::vector<std::vector<long int>>>()) {
1289 std::vector<vector_fp> v;
1290 for (
const auto& outer :
asVector<std::vector<long int>>()) {
1292 for (
const auto& inner : outer) {
1293 v.back().push_back(inner);
1298 auto& vv = as<std::vector<vector_fp>>();
1299 m_equals = eq_comparer<std::vector<vector_fp>>;
1300 checkSize(vv, nMin, nMax);
1305const std::vector<AnyMap>& AnyValue::asVector<AnyMap>(
size_t nMin,
size_t nMax)
const
1308 std::vector<AnyMap> v;
1309 v.push_back(std::move(as<AnyMap>()));
1311 }
else if (
is<std::vector<AnyValue>>() && asVector<AnyValue>().
empty()) {
1312 *
m_value = std::vector<AnyMap>();
1314 const auto& vv = as<std::vector<AnyMap>>();
1315 checkSize(vv, nMin, nMax);
1320std::vector<AnyMap>& AnyValue::asVector<AnyMap>(
size_t nMin,
size_t nMax)
1323 std::vector<AnyMap> v;
1324 v.push_back(std::move(as<AnyMap>()));
1326 }
else if (
is<std::vector<AnyValue>>() && asVector<AnyValue>().
empty()) {
1327 *
m_value = std::vector<AnyMap>();
1329 auto& vv = as<std::vector<AnyMap>>();
1330 checkSize(vv, nMin, nMax);
1343 const auto& iter =
m_data.find(key);
1344 if (iter ==
m_data.end()) {
1363 return iter->second;
1371 }
catch (std::out_of_range&) {
1373 "Key '{}' not found.\nExisting keys: {}", key,
keys_str());
1387 value.
setLoc(line, column);
1395 }
catch (std::out_of_range&) {
1397 "Key '{}' not found.\nExisting keys: {}", key,
keys_str());
1403 return m_data.size() == 0;
1423 for (
const auto& item : other) {
1424 if (!keepExisting || !
hasKey(item.first)) {
1425 (*this)[item.first] = item.second;
1432 fmt::memory_buffer b;
1433 auto iter = this->
begin();
1434 if (iter != this->
end()) {
1435 fmt_append(b,
"{}", iter->first);
1438 while (iter != this->
end()) {
1439 fmt_append(b,
", {}", iter->first);
1442 return to_string(b);
1448 for (
auto& item :
m_data) {
1461 (*m_metadata)[key] = value;
1481 (*m_metadata)[item.first] = item.second;
1489 return (
hasKey(key)) ?
m_data.at(key).asBool() : default_;
1494 return (
hasKey(key)) ?
m_data.at(key).asDouble() : default_;
1499 return (
hasKey(key)) ?
m_data.at(key).asInt() : default_;
1503 const std::string& default_)
const
1505 return (
hasKey(key)) ?
m_data.at(key).asString() : default_;
1519 double default_)
const
1529 size_t nMin,
size_t nMax)
const
1531 return units().
convert(
at(key).asVector<AnyValue>(nMin, nMax), dest);
1534AnyMap::Iterator::Iterator(
1535 const std::unordered_map<std::string, AnyValue>::const_iterator& start,
1536 const std::unordered_map<std::string, AnyValue>::const_iterator& stop)
1540 while (m_iter != m_stop
1541 && ba::starts_with(m_iter->first,
"__")
1542 && ba::ends_with(m_iter->first,
"__")) {
1550 while (m_iter != m_stop
1551 && ba::starts_with(m_iter->first,
"__")
1552 && ba::ends_with(m_iter->first,
"__")) {
1559AnyMap::OrderedProxy::OrderedProxy(
const AnyMap& data)
1564 m_units.reset(
new std::pair<const string, AnyValue>{
"units",
m_data->at(
"__units__")});
1565 m_units->second.setFlowStyle();
1566 m_ordered.emplace_back(std::pair<int, int>{-2, 0},
m_units.get());
1571 for (
auto& item : *
m_data) {
1572 const auto& order = item.second.order();
1573 if (order.first == -1) {
1574 head = std::min(head, order.second);
1575 tail = std::max(tail, order.second);
1577 m_ordered.emplace_back(order, &item);
1579 std::sort(m_ordered.begin(), m_ordered.end());
1583 if (
m_data->hasKey(
"__type__")) {
1584 bool order_changed =
false;
1585 const auto& itemType =
m_data->at(
"__type__").asString();
1586 std::unique_lock<std::mutex> lock(yaml_field_order_mutex);
1589 for (
auto& item : m_ordered) {
1590 if (item.first.first >= 0) {
1595 if (item.second->first == key) {
1596 item.first.second = --head;
1597 order_changed =
true;
1604 for (
auto& item : m_ordered) {
1605 if (item.first.first >= 0) {
1610 if (item.second->first == key) {
1611 item.first.second = ++tail;
1612 order_changed =
true;
1618 if (order_changed) {
1619 std::sort(m_ordered.begin(), m_ordered.end());
1626 return OrderedIterator(m_ordered.begin(), m_ordered.end());
1631 return OrderedIterator(m_ordered.end(), m_ordered.end());
1634AnyMap::OrderedIterator::OrderedIterator(
1635 const AnyMap::OrderedProxy::OrderVector::const_iterator& start,
1636 const AnyMap::OrderedProxy::OrderVector::const_iterator& stop)
1642bool AnyMap::operator==(
const AnyMap& other)
const
1646 for (
auto& item : *
this) {
1647 if (!other.
hasKey(item.first)) {
1652 for (
auto & item : other) {
1653 if (!
hasKey(item.first) || item.second !=
at(item.first)) {
1660bool AnyMap::operator!=(
const AnyMap& other)
const
1675 if (
hasKey(
"__units__")) {
1677 m_units->setDefaults(
m_data[
"__units__"].asMap<std::string>());
1681 for (
auto& item :
m_data) {
1682 item.second.applyUnits(
m_units);
1688 if (
hasKey(
"__units__")) {
1690 m_data[
"__units__"][item.first] = item.second;
1699 (*this)[
"__flow__"] = flow;
1703 const vector<vector<string>>& specs)
1705 std::unique_lock<std::mutex> lock(yaml_field_order_mutex);
1706 for (
const auto& spec : specs) {
1707 if (spec.at(0) ==
"head") {
1709 }
else if (spec.at(0) ==
"tail") {
1713 "Unknown ordering rule '{}'", spec.at(0));
1722 if (
s_cache.count(fullName)) {
1730 YAML::Node node = YAML::Load(yaml);
1731 amap = node.as<
AnyMap>();
1732 }
catch (YAML::Exception& err) {
1734 fake.
setLoc(err.mark.line, err.mark.column);
1744 const std::string& parent_name)
1746 std::string fullName;
1748 size_t islash = parent_name.find_last_of(
"/\\");
1749 if (islash !=
npos) {
1750 std::string parent_path = parent_name.substr(0, islash);
1751 if (std::ifstream(parent_path +
"/" + name).good()) {
1752 fullName = parent_path +
"/" + name;
1756 if (fullName.empty()) {
1762 int mtime = get_modified_time(fullName);
1763 std::unique_lock<std::mutex> lock(yaml_cache_mutex);
1764 auto iter =
s_cache.find(fullName);
1765 if (iter !=
s_cache.end() && iter->second.second == mtime) {
1766 return iter->second.first;
1769 if (!std::ifstream(fullName).good()) {
1770 throw CanteraError(
"AnyMap::fromYamlFile",
"Input file '{}' not found "
1771 "on the Cantera search path.", name);
1775 auto& cache_item =
s_cache[fullName];
1776 cache_item.second = mtime;
1778 YAML::Node node = YAML::LoadFile(fullName);
1779 cache_item.first = node.as<
AnyMap>();
1781 cache_item.first.applyUnits();
1782 }
catch (YAML::Exception& err) {
1785 fake.
setLoc(err.mark.line, err.mark.column);
1792 cache_item.first[
"__file__"] = fullName;
1794 if (cache_item.first.hasKey(
"deprecated")) {
1799 return cache_item.first;
1802std::string AnyMap::toYamlString()
const
1807 out << YAML::Newline;
1820void formatInputFile(fmt::memory_buffer& b,
const shared_ptr<AnyMap>& metadata,
1821 const std::string& filename,
int lineno,
int column,
int lineno2=-1,
int column2=-1)
1823 if (lineno2 == -1) {
1828 fmt_append(b,
"| Line |\n");
1829 if (!metadata->hasKey(
"file-contents")) {
1831 std::stringstream buffer;
1832 buffer << infile.rdbuf();
1833 (*metadata)[
"file-contents"] = buffer.str();
1838 std::stringstream contents((*metadata)[
"file-contents"].asString());
1839 while (std::getline(contents, line)) {
1840 if (i == lineno || i == lineno2) {
1841 fmt_append(b,
"> {: 5d} > {}\n", i+1, line);
1842 fmt_append(b,
"{:>{}}\n",
"^", column + 11);
1844 }
else if ((lineno + 4 > i && lineno < i + 6) ||
1845 (lineno2 + 4 > i && lineno2 < i + 6)) {
1846 if (lastShown >= 0 && i - lastShown > 1) {
1847 fmt_append(b,
"...\n");
1849 fmt_append(b,
"| {: 5d} | {}\n", i+1, line);
1857std::string InputFileError::formatError(
const std::string& message,
1858 int lineno,
int column,
1859 const shared_ptr<AnyMap>& metadata)
1864 std::string filename = metadata->getString(
"filename",
"input string");
1866 fmt::memory_buffer b;
1867 fmt_append(b,
"Error on line {} of {}:\n{}\n", lineno+1, filename, message);
1868 formatInputFile(b, metadata, filename, lineno, column);
1869 return to_string(b);
1872std::string InputFileError::formatError2(
const std::string& message,
1873 int line1,
int column1,
1874 const shared_ptr<AnyMap>& metadata1,
1875 int line2,
int column2,
1876 const shared_ptr<AnyMap>& metadata2)
1878 if (!metadata1 || !metadata2) {
1881 std::string filename1 = metadata1->getString(
"filename",
"input string");
1882 std::string filename2 = metadata2->getString(
"filename",
"input string");
1884 fmt::memory_buffer b;
1885 if (filename1 == filename2) {
1886 fmt_append(b,
"Error on lines {} and {} of {}:\n",
1887 std::min(line1, line2) + 1, std::max(line1, line2) + 1, filename1);
1888 fmt_append(b,
"{}\n", message);
1889 formatInputFile(b, metadata1, filename1, line1, column1, line2, column2);
1891 fmt_append(b,
"Error on line {} of {} and line {} of {}:\n{}\n",
1892 line1+1, filename1, line2+1, filename2, message);
1893 formatInputFile(b, metadata1, filename1, line1, column1);
1894 fmt_append(b,
"\n");
1895 formatInputFile(b, metadata2, filename2, line2, column2);
1898 return to_string(b);
1902 const std::string& message)
1909 std::string filename = node.
m_metadata->getString(
"filename",
"input string");
1910 fmt::memory_buffer b;
1911 fmt_append(b, message);
1912 fmt_append(b,
"\n");
1913 fmt_append(b,
"On line {} of {}:\n", node.
m_line+1, filename);
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.
const AnyValue & getMetadata(const std::string &key) const
Get a value from the metadata applicable to the AnyMap tree containing this node.
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 std::string &source, const AnyBase &node, const std::string &message)
A deprecation warning for syntax in an input file.
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.
AnyValue & operator[](const std::string &key)
Get the value of the item stored in key.
Iterator begin() const
Defined to allow use with range-based for loops.
size_t size() const
Returns the number of elements in this map.
std::unordered_map< std::string, AnyValue > m_data
The stored data.
vector_fp convertVector(const std::string &key, const std::string &units, size_t nMin=npos, size_t nMax=npos) const
Convert a vector of dimensional values.
const AnyValue & at(const std::string &key) const
Get the value of the item stored in key.
void copyMetadata(const AnyMap &other)
Copy metadata including input line/column from an existing AnyMap.
static std::unordered_map< std::string, std::vector< std::string > > s_tailFields
Information about fields that should appear last when outputting to YAML.
std::shared_ptr< UnitSystem > m_units
The default units that are used to convert stored values.
double convert(const std::string &key, const std::string &units) const
Convert the item stored by the given key to the units specified in units.
AnyValue & createForYaml(const std::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...
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.
void applyUnits()
Use the supplied UnitSystem to set the default units, and recursively process overrides from nodes na...
const std::string & getString(const std::string &key, const std::string &default_) const
If key exists, return it as a string, otherwise return default_.
static std::unordered_map< std::string, std::vector< std::string > > s_headFields
Information about fields that should appear first when outputting to YAML.
bool getBool(const std::string &key, bool default_) const
If key exists, return it as a bool, otherwise return default_.
static void clearCachedFile(const std::string &filename)
Remove the specified file from the input cache if it is present.
static AnyMap fromYamlFile(const std::string &name, const std::string &parent_name="")
Create an AnyMap from a YAML file.
long int getInt(const std::string &key, long int default_) const
If key exists, return it as a long int, otherwise return default_.
void erase(const std::string &key)
Erase the value held by key.
double getDouble(const std::string &key, double default_) const
If key exists, return it as a double, otherwise return default_.
static bool addOrderingRules(const std::string &objectType, const std::vector< std::vector< std::string > > &specs)
Add global rules for setting the order of elements when outputting AnyMap objects to YAML.
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.
std::string keys_str() const
Return a string listing the keys in this AnyMap, for use in error messages, for example.
static std::unordered_map< std::string, std::pair< AnyMap, int > > s_cache
Cache for previously-parsed input (YAML) files.
void clear()
Erase all items in the mapping.
static AnyMap fromYamlString(const std::string &yaml)
Create an AnyMap from a string containing a YAML document.
bool hasKey(const std::string &key) const
Returns true if the map contains an item named key.
void update(const AnyMap &other, bool keepExisting=true)
Add items from other to this AnyMap.
void setMetadata(const std::string &key, const AnyValue &value)
Set a metadata value that applies to this AnyMap and its children.
void setUnits(const UnitSystem &units)
Set the unit system for this AnyMap.
A wrapper for a variable whose type is determined at runtime.
AnyValue & operator[](const std::string &key)
If this AnyValue is an AnyMap, return the value stored in key.
bool hasMapWhere(const std::string &key, const std::string &value) const
Returns true when getMapWhere() would succeed.
void setKey(const std::string &key)
Set the name of the key storing this value in an AnyMap.
std::unique_ptr< boost::any > m_value
The held value.
const std::string & asString() const
Return the held value, if it is a string.
void setQuantity(double value, const std::string &units, bool is_act_energy=false)
Assign a scalar quantity with units as a string, for example {3.0, "m^2"}.
bool & asBool()
Return the held value, if it is a bool.
bool empty() const
Return boolean indicating whether AnyValue is empty.
const std::vector< T > & asVector(size_t nMin=npos, size_t nMax=npos) const
Return the held value, if it is a vector of type T.
long int & asInt()
Return the held value, if it is a long int.
void applyUnits(shared_ptr< UnitSystem > &units)
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,...
std::string type_str() const
Returns a string specifying the type of the held value.
std::pair< int, int > order() const
Return values used to determine the sort order when outputting to YAML.
void setFlowStyle(bool flow=true)
void propagateMetadata(shared_ptr< AnyMap > &file)
Propagate metadata to any child elements.
std::string m_key
Key of this value in a parent AnyMap
bool is() const
Returns true if the held value is of the specified type.
AnyMap & getMapWhere(const std::string &key, const std::string &value, bool create=false)
Treating the value as vector<AnyMap>, return the item where the given key has the specified value.
bool hasKey(const std::string &key) const
Returns true if this AnyValue is an AnyMap and that map contains a key with the given name.
std::map< std::string, T > asMap() const
Return the held AnyMap as a std::map where all of the values have the specified type.
const T & as() const
Get the value of this key as the specified type.
Base class for exceptions thrown by Cantera classes.
An error indicating that an unimplemented function has been called.
double convert(double value, const std::string &src, const std::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.
This file contains definitions for utility functions and text for modules, inputfiles,...
Namespace for the Cantera kernel.
const size_t npos
index returned by functions to indicate "no position"
doublereal fpValue(const std::string &val)
Translate a string into one doublereal value.
void scale(InputIter begin, InputIter end, OutputIter out, S scale_factor)
Multiply elements of an array by a scale factor.
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
std::string demangle(const std::type_info &type)
Convert a type name to a human readable string, using boost::core::demangle if available.
Contains declarations for string manipulation functions within Cantera.
Various templated functions that carry out common vector operations (see Templated Utility Functions)...