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()) {
 
 1512        fmt_append(b, 
"{}", iter->first);
 
 1515    while (iter != this->
end()) {
 
 1516        fmt_append(b, 
", {}", iter->first);
 
 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) {
 
 1926    fmt_append(b, 
"|  Line |\n");
 
 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) {
 
 1939            fmt_append(b, 
"> {: 5d} > {}\n", i+1, line);
 
 1940            fmt_append(b, 
"{:>{}}\n", 
"^", column + 11);
 
 1942        } 
else if ((lineno + 4 > i && lineno < i + 6) ||
 
 1943                   (lineno2 + 4 > i && lineno2 < i + 6)) {
 
 1944            if (lastShown >= 0 && i - lastShown > 1) {
 
 1945                fmt_append(b, 
"...\n");
 
 1947            fmt_append(b, 
"| {: 5d} | {}\n", i+1, line);
 
 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);
 
 1984        fmt_append(b, 
"{}\n", message);
 
 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);
 
 1990        fmt_append(b, 
"\n");
 
 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;
 
 2006    fmt_append(b, message);
 
 2007    fmt_append(b, 
"\n");
 
 2008    fmt_append(b, 
"On line {} of {}:\n", node.
m_line+1, filename);
 
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.
 
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...