Cantera  3.1.0
Loading...
Searching...
No Matches
AnyMap.cpp
Go to the documentation of this file.
1//! @file AnyMap.cpp
2
3// This file is part of Cantera. See License.txt in the top-level directory or
4// at https://cantera.org/license.txt for license and copyright information.
5
7#include "application.h"
8#include "cantera/base/yaml.h"
10#include "cantera/base/global.h"
12#include <boost/algorithm/string.hpp>
13#include <fstream>
14#include <mutex>
15#include <unordered_set>
16
17namespace ba = boost::algorithm;
18
19namespace { // helper functions
20
21std::mutex yaml_cache_mutex;
22std::mutex yaml_field_order_mutex;
23using namespace Cantera;
24
25bool isFloat(const string& val)
26{
27 // This function duplicates the logic of fpValueCheck, but doesn't throw
28 // exceptions if the string isn't a float
29 string str = ba::trim_copy(val);
30 if (str.empty()) {
31 return false;
32 }
33 int numDot = 0;
34 int numExp = 0;
35 int istart = 0;
36 int numDigit = 0;
37 char ch = str[0];
38 if (ch == '+' || ch == '-') {
39 istart = 1;
40 if (str.size() == 1) {
41 return false;
42 }
43 }
44 for (size_t i = istart; i < str.size(); i++) {
45 ch = str[i];
46 if (isdigit(ch)) {
47 numDigit++;
48 } else if (ch == '.') {
49 numDot++;
50 if (numDot > 1) {
51 return false;
52 }
53 if (numExp > 0) {
54 return false;
55 }
56 } else if (ch == 'e' || ch == 'E') {
57 numExp++;
58 if (numExp > 1 || numDigit == 0 || i == str.size() - 1) {
59 return false;
60 }
61 ch = str[i+1];
62 if (ch == '+' || ch == '-') {
63 if (i + 1 == str.size() - 1) {
64 return false;
65 }
66 i++;
67 }
68 } else {
69 return false;
70 }
71 }
72 return true;
73}
74
75bool isInt(const string& val)
76{
77 string str = ba::trim_copy(val);
78 if (str.empty()) {
79 return false;
80 }
81 int istart = 0;
82 char ch = str[0];
83 if (ch == '+' || ch == '-') {
84 istart = 1;
85 if (str.size() == 1) {
86 return false;
87 }
88 }
89 for (size_t i = istart; i < str.size(); i++) {
90 if (!isdigit(str[i])) {
91 return false;
92 }
93 }
94 return true;
95}
96
97bool isBool(const string& val) {
98 string str = ba::trim_copy(val);
99 return (val == "true" || val == "True" || val == "false" || val == "False");
100}
101
102bool dunder(const string& s) {
103 return ba::starts_with(s, "__") && ba::ends_with(s, "__");
104}
105
106enum class Type : char {
107 Unknown = 0,
108 Integer = 1,
109 Double = 2,
110 String = 4,
111 Bool = 8,
112 Map = 16,
113 Sequence = 32
114};
115
116// Map items of type `Exclude` are skipped in any iteration / output
117struct Exclude {};
118bool operator==(const Exclude& lhs, const Exclude& rhs) {
119 return true;
120}
121
122Type operator|(Type lhs, Type rhs)
123{
124 return Type(static_cast<char>(lhs) | static_cast<char>(rhs));
125}
126
127Type elementTypes(const YAML::Node& node)
128{
129 // See what kinds of elements we have:
130 Type types = Type::Unknown;
131 for (const auto& el : node) {
132 if (el.IsMap()) {
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() == "!") {
139 // Prevent implicit conversion of quoted strings to numeric types
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;
147 } else {
148 types = types | Type::String;
149 }
150 }
151 }
152 return types;
153}
154
155long int getPrecision(const Cantera::AnyValue& precisionSource)
156{
157 long int precision = 15;
158 auto& userPrecision = precisionSource.getMetadata("precision");
159 if (userPrecision.is<long int>()) {
160 precision = userPrecision.asInt();
161 }
162 return precision;
163}
164
165string formatDouble(double x, long int precision)
166{
167 // This function ensures that trailing zeros resulting from round-off error
168 // are removed. Values are only rounded if at least three digits are removed,
169 // or the displayed value has multiple trailing zeros.
170 if (x == 0.0) {
171 return "0.0";
172 }
173
174 // Build string with full precision
175 bool useExp = std::abs(x) < 1e-2 || std::abs(x) >= 1e4;
176 int log10x = 0;
177 size_t last;
178 string s0;
179 if (useExp) {
180 s0 = fmt::format(fmt::runtime(fmt::format("{:.{}e}", x, precision)));
181 // last digit of significand
182 last = s0.size() - 5;
183 if (s0[last + 1] == 'e') {
184 // pass - most values use four letter exponent (examples: e+05, e-03)
185 } else if (s0[last] == 'e') {
186 last--; // exponents larger than e+99 or smaller than e-99 (example: e+100)
187 } else {
188 last = s0.find('e') - 1; // backstop; slower, but will always work
189 }
190 } else {
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; // last digit
194 }
195 if (s0[last - 2] == '0' && s0[last - 1] == '0' && s0[last] < '5') {
196 // Value ending in '00x' and should be rounded down
197 } else if (s0[last - 2] == '9' && s0[last - 1] == '9' && s0[last] > '4') {
198 // Value ending in '99y' and should be rounded up
199 } else if (s0[last - 1] == '0' && s0[last] == '0') {
200 // Remove trailing zeros
201 } else {
202 // Value should not be rounded / do not round last digit
203 return s0;
204 }
205
206 // Remove trailing zeros
207 string s1;
208 if (s0[last - 1] == '0') {
209 s1 = s0; // Recycle original string
210 } else if (useExp) {
211 s1 = fmt::format(fmt::runtime(fmt::format("{:.{}e}", x, precision - 2)));
212 } else {
213 s1 = fmt::format("{:.{}f}", x, precision - log10x - 2);
214 }
215 size_t digit = last - 2;
216 while (s1[digit] == '0' && s1[digit - 1] != '.') {
217 digit--;
218 }
219
220 // Assemble rounded value and return
221 if (useExp) {
222 size_t eloc = s1.find('e');
223 s0 = string(s1.begin() + eloc, s1.end());
224 }
225 s1 = string(s1.begin(), s1.begin() + digit + 1);
226 if (useExp) {
227 return s1 + s0;
228 }
229 return s1;
230}
231
232struct Quantity
233{
234 AnyValue value;
235 Units units;
236 bool isActivationEnergy;
237 AnyValue::unitConverter converter;
238
239 bool operator==(const Quantity& other) const {
240 return value == other.value && units == other.units
241 && isActivationEnergy == other.isActivationEnergy;
242 }
243};
244
246
247} // end anonymous namespace
248
249namespace YAML { // YAML converters
250
251using namespace Cantera;
252static const int max_line_length = 87;
253
254template<>
255struct convert<Cantera::AnyMap> {
256 static Node encode(const Cantera::AnyMap& rhs) {
257 throw NotImplementedError("AnyMap::encode");
258 }
259
260 static bool decode(const Node& node, Cantera::AnyMap& target) {
261 target.setLoc(node.Mark().line, node.Mark().column);
262 if (node.Style() == YAML::EmitterStyle::Flow) {
263 target.setFlowStyle();
264 }
265 if (node.IsSequence()) {
266 // Convert a top-level list to a map with the key "items"
267 target["items"] = node.as<AnyValue>();
268 return true;
269 } else if (!node.IsMap()) {
270 string text = YAML::Dump(node);
271 if (text.size() > 300) {
272 text.resize(300);
273 }
274 throw CanteraError("YAML::convert<AnyMap>",
275 "YAML node is not a map. Node begins with:\n'''\n{}\n'''", text);
276 }
277 for (const auto& child : node) {
278 string key = child.first.as<string>();
279 const auto& loc = child.second.Mark();
280 AnyValue& value = target.createForYaml(key, loc.line, loc.column);
281 if (child.second.IsMap()) {
282 value = child.second.as<AnyMap>();
283 } else {
284 value = child.second.as<AnyValue>();
285 value.setKey(key);
286 }
287 }
288 return true;
289 }
290};
291
292YAML::Emitter& operator<<(YAML::Emitter& out, const AnyMap& rhs)
293{
294 bool flow = rhs.getBool("__flow__", false);
295 if (flow) {
296 out << YAML::Flow;
297 out << YAML::BeginMap;
298 size_t width = 15;
299 for (const auto& [name, value] : rhs.ordered(true)) {
300 string valueStr;
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)) {
308 // Quote strings that look like numbers to preserve their type
309 needsQuotes = true;
310 }
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());
315 } else {
316 foundType = false;
317 }
318
319 if (foundType) {
320 // Check if this item will fit on the current line, including spaces
321 // for delimiters and whitespace. If not, wrap to the next line.
322 if (width + name.size() + valueStr.size() + 4 > max_line_length) {
323 out << YAML::Newline;
324 width = 15;
325 }
326 out << name;
327 if (needsQuotes) {
328 out << YAML::SingleQuoted;
329 }
330 out << valueStr;
331 width += name.size() + valueStr.size() + 4;
332 } else {
333 // Put items of an unknown (compound) type on a line alone
334 out << YAML::Newline;
335 out << name;
336 out << value;
337 width = 99; // Force newline after this item as well
338 }
339 }
340 } else {
341 out << YAML::BeginMap;
342 for (const auto& [key, value] : rhs.ordered(true)) {
343 out << key;
344 out << value;
345 }
346 }
347 out << YAML::EndMap;
348 return out;
349}
350
351//! Write YAML strings spanning multiple lines if input includes endline '\n'
352void emitString(YAML::Emitter& out, const string& str0) {
353 if (str0.rfind('\n') == string::npos) {
354 if (isFloat(str0)) {
355 out << YAML::SingleQuoted;
356 }
357 out << str0;
358 return;
359 }
360
361 // Remove leading and trailing whitespace
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;
366}
367
368//! Write a vector in YAML "flow" style, wrapping lines to avoid exceeding the
369//! preferred maximum line length (set by `max_line_length`). Specialized for
370//! `vector<double>` to be able to use the custom `formatDouble` function with
371//! a given precision.
372void emitFlowVector(YAML::Emitter& out, const vector<double>& v, long int precision)
373{
374 out << YAML::Flow;
375 out << YAML::BeginSeq;
376 size_t width = 15; // wild guess, but no better value is available
377 for (auto& x : v) {
378 string xstr = formatDouble(x, precision);
379 // Wrap to the next line if this item would exceed the target line length
380 if (width + xstr.size() > max_line_length) {
381 out << YAML::Newline;
382 width = 15;
383 }
384 out << xstr;
385 width += xstr.size() + 2; // Update width including comma and space
386 }
387 out << YAML::EndSeq;
388}
389
390//! Write a vector in YAML "flow" style, wrapping lines to avoid exceeding the
391//! preferred maximum line length (set by `max_line_length`). Specialized for
392//! `vector<string>` to quote strings that look like numeric values
393void emitFlowVector(YAML::Emitter& out, const vector<string>& v)
394{
395 out << YAML::Flow;
396 out << YAML::BeginSeq;
397 size_t width = 15; // wild guess, but no better value is available
398 for (const string& x : v) {
399 // Wrap to the next line if this item would exceed the target line length
400 if (width + x.size() > max_line_length) {
401 out << YAML::Newline;
402 width = 15;
403 }
404 if (isFloat(x)) {
405 out << SingleQuoted;
406 width += 2;
407 }
408 out << x;
409 width += x.size() + 2;
410 }
411 out << YAML::EndSeq;
412}
413
414//! Write a vector in YAML "flow" style, wrapping lines to avoid exceeding the
415//! preferred maximum line length (set by `max_line_length`).
416template <typename T>
417void emitFlowVector(YAML::Emitter& out, const vector<T>& v)
418{
419 out << YAML::Flow;
420 out << YAML::BeginSeq;
421 size_t width = 15; // wild guess, but no better value is available
422 for (const T& x : v) {
423 string xstr = fmt::format("{}", x);
424 // Wrap to the next line if this item would exceed the target line length
425 if (width + xstr.size() > max_line_length) {
426 out << YAML::Newline;
427 width = 15;
428 }
429 out << xstr;
430 width += xstr.size() + 2;
431 }
432 out << YAML::EndSeq;
433}
434
435YAML::Emitter& operator<<(YAML::Emitter& out, const AnyValue& rhs)
436{
437 if (rhs.isScalar()) {
438 if (rhs.is<string>()) {
439 emitString(out, rhs.asString());
440 } else if (rhs.is<double>()) {
441 out << formatDouble(rhs.asDouble(), getPrecision(rhs));
442 } else if (rhs.is<long int>()) {
443 out << rhs.asInt();
444 } else if (rhs.is<bool>()) {
445 out << rhs.asBool();
446 } else {
447 throw CanteraError("operator<<(YAML::Emitter&, AnyValue&)",
448 "Don't know how to encode value of type '{}' with key '{}'",
449 rhs.type_str(), rhs.m_key);
450 }
451 } else if (rhs.is<AnyMap>()) {
452 out << rhs.as<AnyMap>();
453 } else if (rhs.is<vector<AnyMap>>()) {
454 out << rhs.asVector<AnyMap>();
455 } else if (rhs.is<vector<double>>()) {
456 emitFlowVector(out, rhs.asVector<double>(), getPrecision(rhs));
457 } else if (rhs.is<vector<string>>()) {
458 emitFlowVector(out, rhs.asVector<string>());
459 } else if (rhs.is<vector<long int>>()) {
460 emitFlowVector(out, rhs.asVector<long int>());
461 } else if (rhs.is<vector<bool>>()) {
462 emitFlowVector(out, rhs.asVector<bool>());
463 } else if (rhs.is<vector<Cantera::AnyValue>>()) {
464 out << rhs.asVector<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) {
470 emitFlowVector(out, u, precision);
471 }
472 out << YAML::EndSeq;
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) {
477 emitFlowVector(out, u);
478 }
479 out << YAML::EndSeq;
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) {
484 emitFlowVector(out, u);
485 }
486 out << YAML::EndSeq;
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) {
491 emitFlowVector(out, u);
492 }
493 out << YAML::EndSeq;
494 } else {
495 throw CanteraError("operator<<(YAML::Emitter&, AnyValue&)",
496 "Don't know how to encode value of type '{}' with key '{}'",
497 rhs.type_str(), rhs.m_key);
498 }
499 return out;
500}
501
502
503template<>
504struct convert<Cantera::AnyValue> {
505 static Node encode(const Cantera::AnyValue& rhs) {
506 throw NotImplementedError("");
507 }
508
509 static bool decode(const Node& node, Cantera::AnyValue& target) {
510 target.setLoc(node.Mark().line, node.Mark().column);
511 if (node.IsScalar()) {
512 // Scalar nodes are int, doubles, or strings
513 string nodestr = node.as<string>();
514 if (node.Tag() == "!") {
515 // Prevent quoted strings from being implicitly converted to
516 // numeric types. For example, the quoted YAML string '12345' should not
517 // be interpreted as an integer
518 target = nodestr;
519 } else if (isInt(nodestr)) {
520 try {
521 target = node.as<long int>();
522 } catch (YAML::BadConversion&) {
523 // This exception is raised if the value doesn't fit in a
524 // long int, in which case we would rather store it
525 // (possibly inexactly) as a double.
526 target = node.as<double>();
527 }
528 } else if (isFloat(nodestr)) {
529 target = fpValue(nodestr);
530 } else if (isBool(nodestr)) {
531 target = node.as<bool>();
532 } else {
533 target = nodestr;
534 }
535 return true;
536 } else if (node.IsSequence()) {
537 // Convert sequences of the same element type to vectors of that type
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>()));
545 }
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) {
554 // Create nested vectors when data types are compatible
555 Type subtypes = Type::Unknown;
556 for (const auto& el : node) {
557 subtypes = subtypes | elementTypes(el);
558 }
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>()));
567 }
568 }
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>>>();
574 } else {
575 target = node.as<vector<AnyValue>>();
576 }
577 } else {
578 // If types are different, create a vector of generic values
579 target = node.as<vector<AnyValue>>();
580 }
581 return true;
582 } else if (node.IsMap()) {
583 target = node.as<AnyMap>();
584 return true;
585 } else if (node.IsNull()) {
586 target = Empty;
587 return true;
588 }
589 return false;
590 }
591};
592
593}
594
595namespace Cantera {
596
597std::unordered_map<string,
598 pair<AnyMap, std::filesystem::file_time_type>> AnyMap::s_cache;
599
600std::unordered_map<string, vector<string>> AnyMap::s_headFields;
601std::unordered_map<string, vector<string>> AnyMap::s_tailFields;
602
603// Methods of class AnyBase
604
605AnyBase& AnyBase::operator=(const AnyBase& other)
606{
607 m_metadata = other.m_metadata;
608 // Copy location information only if it's been set from an input file. Otherwise,
609 // the ordering information at the destination is more important to preserve.
610 if (other.m_line != -1 && other.m_column >= 0) {
611 m_line = other.m_line;
612 m_column = other.m_column;
613 }
614 return *this;
615}
616
617void AnyBase::setLoc(int line, int column)
618{
619 m_line = line;
620 m_column = column;
621}
622
623const AnyValue& AnyBase::getMetadata(const string& key) const
624{
625 if (m_metadata && m_metadata->hasKey(key)) {
626 return m_metadata->at(key);
627 } else {
628 return Empty;
629 }
630}
631
632// Methods of class AnyValue
633
634AnyValue::AnyValue()
635 : m_equals(eq_comparer<size_t>)
636{}
637
638AnyValue::~AnyValue() = default;
639
640bool AnyValue::operator==(const AnyValue& other) const
641{
642 return m_equals(m_value, other.m_value);
643}
644
645bool AnyValue::operator!=(const AnyValue& other) const
646{
647 return !m_equals(m_value, other.m_value);
648}
649
651{
652 return as<AnyMap>()[key];
653}
654
655const AnyValue& AnyValue::operator[](const string& key) const
656{
657 return as<AnyMap>().at(key);
658}
659
660bool AnyValue::hasKey(const string& key) const {
661 return (is<AnyMap>() && as<AnyMap>().hasKey(key));
662}
663
664void AnyValue::setKey(const string &key) { m_key = key; }
665
666const std::type_info &AnyValue::type() const {
667 return m_value.type();
668}
669
670void AnyValue::propagateMetadata(shared_ptr<AnyMap>& metadata)
671{
672 m_metadata = metadata;
673 if (is<AnyMap>()) {
674 as<AnyMap>().propagateMetadata(m_metadata);
675 } else if (is<vector<AnyValue>>()) {
676 for (auto& item : asVector<AnyValue>()) {
677 item.propagateMetadata(m_metadata);
678 }
679 } else if (is<vector<AnyMap>>()) {
680 for (auto& item : asVector<AnyMap>()) {
681 item.propagateMetadata(m_metadata);
682 }
683 }
684}
685
686string AnyValue::type_str() const {
687 return demangle(type());
688}
689
690bool AnyValue::empty() const {
691 return !m_value.has_value();
692}
693
694bool AnyValue::isScalar() const {
695 return is<double>() || is<long int>() || is<string>() || is<bool>();
696}
697
698size_t AnyValue::vectorSize() const {
699 if (isVector<double>()) {
700 return as<vector<double>>().size();
701 }
702 if (isVector<long int>()) {
703 return as<vector<long int>>().size();
704 }
705 if (isVector<string>()) {
706 return as<vector<string>>().size();
707 }
708 if (isVector<bool>()) {
709 return as<vector<bool>>().size();
710 }
711 return npos;
712}
713
714pair<size_t, size_t> AnyValue::matrixShape() const {
715 if (isVector<vector<double>>()) {
716 auto& mat = as<vector<vector<double>>>();
717 if (isMatrix<double>()) {
718 if (mat.size()) {
719 return {mat.size(), mat[0].size()};
720 }
721 return {mat.size(), 0};
722 }
723 return {mat.size(), npos};
724 }
725 if (isVector<vector<long int>>()) {
726 auto& mat = as<vector<vector<long int>>>();
727 if (isMatrix<long int>()) {
728 if (mat.size()) {
729 return {mat.size(), mat[0].size()};
730 }
731 return {mat.size(), 0};
732 }
733 return {mat.size(), npos};
734 }
735 if (isVector<vector<string>>()) {
736 auto& mat = as<vector<vector<string>>>();
737 if (isMatrix<string>()) {
738 if (mat.size()) {
739 return {mat.size(), mat[0].size()};
740 }
741 return {mat.size(), 0};
742 }
743 return {mat.size(), npos};
744 }
745 if (isVector<vector<bool>>()) {
746 auto& mat = as<vector<vector<bool>>>();
747 if (isMatrix<bool>()) {
748 if (mat.size()) {
749 return {mat.size(), mat[0].size()};
750 }
751 return {mat.size(), 0};
752 }
753 return {mat.size(), npos};
754 }
755 return {npos, npos};
756}
757
758// Specializations for "string" and "const char*"
759
760AnyValue::AnyValue(const string& value)
761 : m_value{value}
762 , m_equals(eq_comparer<string>)
763{}
764
765AnyValue::AnyValue(const char* value)
766 : m_value{string(value)}
767 , m_equals(eq_comparer<string>)
768{}
769
770AnyValue &AnyValue::operator=(const string &value) {
771 m_value = value;
772 m_equals = eq_comparer<string>;
773 return *this;
774}
775
776AnyValue &AnyValue::operator=(const char *value) {
777 m_value = string(value);
778 m_equals = eq_comparer<string>;
779 return *this;
780}
781
782const string &AnyValue::asString() const {
783 return as<string>();
784}
785
786bool AnyValue::operator==(const string& other) const
787{
788 if (m_value.type() == typeid(string)) {
789 return std::any_cast<string>(m_value) == other;
790 } else {
791 return false;
792 }
793}
794
795bool AnyValue::operator!=(const string& other) const
796{
797 return !(*this == other);
798}
799
800bool operator==(const string& lhs, const AnyValue& rhs)
801{
802 return rhs == lhs;
803}
804
805bool operator!=(const string& lhs, const AnyValue& rhs)
806{
807 return rhs != lhs;
808}
809
810// Specialization for "Quantity"
811
812void AnyValue::setQuantity(double value, const string& units, bool is_act_energy) {
813 m_value = Quantity{AnyValue(value), Units(units), is_act_energy, {}};
814 m_equals = eq_comparer<Quantity>;
815}
816
817void AnyValue::setQuantity(double value, const Units& units) {
818 m_value = Quantity{AnyValue(value), units, false, {}};
819 m_equals = eq_comparer<Quantity>;
820}
821
822void AnyValue::setQuantity(const vector<double>& values, const string& units) {
823 AnyValue v;
824 v = values;
825 m_value = Quantity{v, Units(units), false, {}};
826 m_equals = eq_comparer<Quantity>;
827}
828
829void AnyValue::setQuantity(const AnyValue& value, const unitConverter& converter)
830{
831 m_value = Quantity{value, Units(0.0), false, converter};
832 m_equals = eq_comparer<Quantity>;
833}
834
835template<>
836bool AnyValue::is<vector<double>>() const
837{
838 if (m_value.type() == typeid(vector<double>)) {
839 return true;
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>())))
844 {
845 return false;
846 }
847 }
848 return true;
849 } else {
850 return false;
851 }
852}
853
854// Specializations for "double"
855
856AnyValue::AnyValue(double value)
857 : m_value{value}
858 , m_equals(eq_comparer<double>)
859{}
860
861AnyValue &AnyValue::operator=(double value) {
862 m_value = value;
863 m_equals = eq_comparer<double>;
864 return *this;
865}
866
868 return as<double>();
869}
870
871const double& AnyValue::asDouble() const {
872 return as<double>();
873}
874
875bool AnyValue::operator==(const double& other) const
876{
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;
881 } else {
882 return false;
883 }
884}
885
886bool AnyValue::operator!=(const double& other) const
887{
888 return !(*this == other);
889}
890
891bool operator==(const double& lhs, const AnyValue& rhs)
892{
893 return rhs == lhs;
894}
895
896bool operator!=(const double& lhs, const AnyValue& rhs)
897{
898 return rhs != lhs;
899}
900
901// Specializations for "bool"
902
903AnyValue::AnyValue(bool value)
904 : m_value{value}
905 , m_equals(eq_comparer<bool>)
906{}
907
908AnyValue &AnyValue::operator=(bool value) {
909 m_value = value;
910 m_equals = eq_comparer<bool>;
911 return *this;
912}
913
915 return as<bool>();
916}
917
918const bool& AnyValue::asBool() const {
919 return as<bool>();
920}
921
922// Specializations for "long int" and "int"
923
924AnyValue::AnyValue(long int value)
925 : m_value{value}
926 , m_equals(eq_comparer<long int>)
927{}
928
929AnyValue::AnyValue(int value)
930 : m_value{static_cast<long int>(value)}
931 , m_equals(eq_comparer<long int>)
932{}
933
934AnyValue &AnyValue::operator=(long int value) {
935 m_value = value;
936 m_equals = eq_comparer<long int>;
937 return *this;
938}
939
940AnyValue &AnyValue::operator=(int value) {
941 m_value = static_cast<long int>(value);
942 m_equals = eq_comparer<long int>;
943 return *this;
944}
945
946long int& AnyValue::asInt() {
947 return as<long int>();
948}
949
950const long int& AnyValue::asInt() const {
951 return as<long int>();
952}
953
954bool AnyValue::operator==(const long int& other) const
955{
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;
960 } else {
961 return false;
962 }
963}
964
965bool AnyValue::operator!=(const long int& other) const
966{
967 return !(*this == other);
968}
969
970bool AnyValue::operator==(const int& other) const
971{
972 return *this == static_cast<long int>(other);
973}
974
975bool AnyValue::operator!=(const int& other) const
976{
977 return *this != static_cast<long int>(other);
978}
979
980bool operator==(const long int& lhs, const AnyValue& rhs)
981{
982 return rhs == lhs;
983}
984
985bool operator!=(const long int& lhs, const AnyValue& rhs)
986{
987 return rhs != lhs;
988}
989
990bool operator==(const int& lhs, const AnyValue& rhs)
991{
992 return rhs == lhs;
993}
994
995bool operator!=(const int& lhs, const AnyValue& rhs)
996{
997 return rhs != lhs;
998}
999
1000// Specializations for "AnyMap"
1001
1002AnyValue::AnyValue(const AnyMap& value)
1003 : m_value{value}
1004 , m_equals(eq_comparer<AnyMap>)
1005{}
1006
1007AnyValue& AnyValue::operator=(const AnyMap& value) {
1008 m_value = value;
1009 m_equals = eq_comparer<AnyMap>;
1010 return *this;
1011}
1012
1013AnyValue& AnyValue::operator=(AnyMap&& value) {
1014 m_value = std::move(value);
1015 m_equals = eq_comparer<AnyMap>;
1016 return *this;
1017}
1018
1020 AnyValue v;
1021 v.m_value = Exclude();
1022 v.m_equals = eq_comparer<Exclude>;
1023 return v;
1024}
1025
1026std::unordered_map<string, const AnyMap*> AnyValue::asMap(const string& name) const
1027{
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)) {
1032 throw InputFileError("AnyValue::asMap", *this,
1033 "Duplicate key '{}'", key);
1034 }
1035 mapped.emplace(std::make_pair(key, &item));
1036 }
1037 return mapped;
1038}
1039
1040std::unordered_map<string, AnyMap*> AnyValue::asMap(const string& name)
1041{
1042 std::unordered_map<string, AnyMap*> mapped;
1043 for (auto& item : asVector<AnyMap>()) {
1044 auto key = item.at(name).asString();
1045 if (mapped.count(key)) {
1046 throw InputFileError("AnyValue::asMap", *this,
1047 "Duplicate key '{}'", key);
1048 }
1049 mapped.emplace(std::make_pair(key, &item));
1050 }
1051 return mapped;
1052}
1053
1054const AnyMap& AnyValue::getMapWhere(const string& key, const string& value) const
1055{
1056 if (is<vector<AnyMap>>()) {
1057 if (value == "") {
1058 return asVector<AnyMap>().at(0);
1059 }
1060 for (auto& item : asVector<AnyMap>()) {
1061 if (item.hasKey(key) && item[key] == value) {
1062 return item;
1063 }
1064 }
1065 throw InputFileError("AnyValue::getMapWhere", *this,
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>();
1070 } else {
1071 throw InputFileError("AnyValue::getMapWhere", *this,
1072 "Map does not contain a key where '{}' = '{}'", key, value);
1073 }
1074 } else if (is<void>()) {
1075 throw InputFileError("AnyValue::getMapWhere", *this,
1076 "Key '{}' not found", m_key);
1077 } else {
1078 throw InputFileError("AnyValue::getMapWhere", *this,
1079 "Element is not a mapping or list of mappings.\n"
1080 "Looking for a mapping with key '{}' = '{}'", key, value);
1081 }
1082}
1083
1084AnyMap& AnyValue::getMapWhere(const string& key, const string& value, bool create)
1085{
1086 if (is<vector<AnyMap>>()) {
1087 if (value == "") {
1088 return asVector<AnyMap>().at(0);
1089 }
1090 for (auto& item : asVector<AnyMap>()) {
1091 if (item.hasKey(key) && item[key] == value) {
1092 return item;
1093 }
1094 }
1095 if (create) {
1096 // If the map wasn't found, insert it
1097 auto& vec = asVector<AnyMap>();
1098 AnyMap child;
1099 child[key] = value;
1100 vec.push_back(std::move(child));
1101 return vec.back();
1102 } else {
1103 throw InputFileError("AnyValue::getMapWhere", *this,
1104 "List does not contain a map where '{}' = '{}'", key, value);
1105 }
1106 } else if (is<AnyMap>()) {
1107 if (value == "" || (hasKey(key) && as<AnyMap>()[key] == value)) {
1108 return as<AnyMap>();
1109 } else if (create) {
1110 AnyMap newChild;
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();
1115 } else {
1116 throw InputFileError("AnyValue::getMapWhere", *this,
1117 "Map does not contain a key where '{}' = '{}'", key, value);
1118 }
1119 } else if (is<void>() && create) {
1120 AnyMap child;
1121 child[key] = value;
1122 operator=(std::move(child));
1123 return as<AnyMap>();
1124 } else if (is<void>()) {
1125 throw InputFileError("AnyValue::getMapWhere", *this,
1126 "Key '{}' not found", m_key);
1127 } else {
1128 throw InputFileError("AnyValue::getMapWhere", *this,
1129 "Element is not a mapping or list of mappings.\n"
1130 "Looking for a mapping with key '{}' = '{}'", key, value);
1131 }
1132}
1133
1134bool AnyValue::hasMapWhere(const string& key, const string& value) const
1135{
1136 if (is<vector<AnyMap>>()) {
1137 if (value == "") {
1138 return true;
1139 }
1140 for (auto& item : asVector<AnyMap>()) {
1141 if (item.hasKey(key) && item[key] == value) {
1142 return true;
1143 }
1144 }
1145 return false;
1146 } else if (is<AnyMap>()) {
1147 if (value == "" || (hasKey(key) && as<AnyMap>()[key] == value)) {
1148 return true;
1149 } else {
1150 return false;
1151 }
1152 } else {
1153 return false;
1154 }
1155}
1156
1157pair<int, int> AnyValue::order() const
1158{
1159 return {m_line, m_column};
1160}
1161
1162void AnyValue::applyUnits(shared_ptr<UnitSystem>& units)
1163{
1164 if (is<AnyMap>()) {
1165 AnyMap& m = as<AnyMap>();
1166
1167 if (m.getBool("__unconvertible__", false)) {
1168 AnyMap delta = units->getDelta(UnitSystem());
1169 if (delta.hasKey("length") || delta.hasKey("quantity")
1170 || delta.hasKey("time"))
1171 {
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)");
1175 }
1176 }
1177 // Units declaration applicable to this map
1178 m.applyUnits(units);
1179 } else if (is<vector<AnyMap>>()) {
1180 auto& list = as<vector<AnyMap>>();
1181 if (list.size() && list[0].hasKey("units") && list[0].size() == 1) {
1182 // First item in the list is a units declaration, which applies to
1183 // the items in the list
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) {
1189 // Any additional units declarations are errors
1190 throw InputFileError("AnyValue::applyUnits", item,
1191 "Found units entry as not the first item in a list.");
1192 } else {
1193 // Merge with a child units declaration
1194 auto& childUnits = item["units"].as<AnyMap>();
1195 for (auto& [dimension, unit] : deltaUnits) {
1196 if (!childUnits.hasKey(dimension)) {
1197 childUnits[dimension] = unit;
1198 }
1199 }
1200 }
1201 } else if (item.hasKey("__units__")) {
1202 // Merge with a child units declaration
1203 auto& childUnits = item["__units__"].as<AnyMap>();
1204 for (auto& [dimension, unit] : deltaUnits) {
1205 if (!childUnits.hasKey(dimension)) {
1206 childUnits[dimension] = unit;
1207 }
1208 }
1209 } else {
1210 item["__units__"] = deltaUnits;
1211 }
1212 item.applyUnits(units);
1213 }
1214 // Remove the "units" map after it has been applied
1215 list.erase(list.begin());
1216 } else {
1217 // Simple downward propagation of the current units
1218 for (auto& item : list) {
1219 // Any later units declarations are errors
1220 if (item.size() == 1 && item.hasKey("units")) {
1221 throw InputFileError("AnyValue::applyUnits", item,
1222 "Found units entry as not the first item in a list.");
1223 }
1224 item.applyUnits(units);
1225 }
1226 }
1227 } else if (is<vector<AnyValue>>()) {
1228 for (auto& v : as<vector<AnyValue>>()) {
1229 v.applyUnits(units);
1230 }
1231 } else if (is<Quantity>()) {
1232 auto& Q = as<Quantity>();
1233 if (Q.converter) {
1234 Q.converter(Q.value, *units);
1235 m_equals = Q.value.m_equals;
1236 // Replace the value last since Q is a reference to m_value and won't be
1237 // valid after this
1238 m_value = Q.value.m_value;
1239 } else if (Q.value.is<double>()) {
1240 if (Q.isActivationEnergy) {
1241 *this = Q.value.as<double>() / units->convertActivationEnergyTo(1.0, Q.units);
1242 } else {
1243 *this = Q.value.as<double>() / units->convertTo(1.0, Q.units);
1244 }
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);
1251 } else {
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);
1255 }
1256 }
1257}
1258
1260{
1261 as<AnyMap>().setFlowStyle();
1262}
1263
1264// Explicit template specializations to allow certain conversions
1265
1266template<>
1267const vector<AnyValue>& AnyValue::asVector<AnyValue>(size_t nMin, size_t nMax) const
1268{
1269 if (!is<vector<AnyValue>>()) {
1270 vector<AnyValue> v;
1271 if (is<vector<double>>()) {
1272 for (const auto& el : asVector<double>()) {
1273 v.push_back(AnyValue(el));
1274 }
1275 const_cast<AnyValue*>(this)->m_value = v;
1276 } else if (is<vector<long int>>()) {
1277 for (const auto& el : asVector<long int>()) {
1278 v.push_back(AnyValue(el));
1279 }
1280 const_cast<AnyValue*>(this)->m_value = v;
1281 } else if (is<vector<string>>()) {
1282 for (const auto& el : asVector<string>()) {
1283 v.push_back(AnyValue(el));
1284 }
1285 const_cast<AnyValue*>(this)->m_value = v;
1286 }
1287 // If none of these special cases match, the value won't be replaced,
1288 // and an exception will be thrown.
1289 }
1290 const auto& vv = as<vector<AnyValue>>();
1291 m_equals = eq_comparer<vector<AnyValue>>;
1292 checkSize(vv, nMin, nMax);
1293 return vv;
1294}
1295
1296template<>
1297vector<AnyValue>& AnyValue::asVector<AnyValue>(size_t nMin, size_t nMax)
1298{
1299 auto& v = const_cast<vector<AnyValue>&>(
1300 const_cast<const AnyValue*>(this)->asVector<AnyValue>());
1301 checkSize(v, nMin, nMax);
1302 return v;
1303}
1304
1305template<>
1306const vector<double>& AnyValue::asVector<double>(size_t nMin, size_t nMax) const
1307{
1308 if (is<vector<long int>>()) {
1309 vector<double> v;
1310 for (const auto& el : asVector<long int>()) {
1311 v.push_back(el);
1312 }
1313 const_cast<AnyValue*>(this)->m_value = v;
1314 }
1315 const auto& vv = as<vector<double>>();
1316 m_equals = eq_comparer<vector<double>>;
1317 checkSize(vv, nMin, nMax);
1318 return vv;
1319}
1320
1321template<>
1322vector<double>& AnyValue::asVector<double>(size_t nMin, size_t nMax)
1323{
1324 if (is<vector<long int>>()) {
1325 vector<double> v;
1326 for (const auto& el : asVector<long int>()) {
1327 v.push_back(el);
1328 }
1329 m_value = v;
1330 }
1331 auto& vv = as<vector<double>>();
1332 m_equals = eq_comparer<vector<double>>;
1333 checkSize(vv, nMin, nMax);
1334 return vv;
1335}
1336
1337template<>
1338const vector<vector<double>>& AnyValue::asVector<vector<double>>(size_t nMin, size_t nMax) const
1339{
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);
1346 }
1347 }
1348 const_cast<AnyValue*>(this)->m_value = v;
1349 }
1350 const auto& vv = as<vector<vector<double>>>();
1351 m_equals = eq_comparer<vector<vector<double>>>;
1352 checkSize(vv, nMin, nMax);
1353 return vv;
1354}
1355
1356template<>
1357vector<vector<double>>& AnyValue::asVector<vector<double>>(size_t nMin, size_t nMax)
1358{
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);
1365 }
1366 }
1367 m_value = v;
1368 }
1369 auto& vv = as<vector<vector<double>>>();
1370 m_equals = eq_comparer<vector<vector<double>>>;
1371 checkSize(vv, nMin, nMax);
1372 return vv;
1373}
1374
1375template<>
1376const vector<AnyMap>& AnyValue::asVector<AnyMap>(size_t nMin, size_t nMax) const
1377{
1378 if (is<AnyMap>()) {
1379 vector<AnyMap> v;
1380 v.push_back(std::move(as<AnyMap>()));
1381 const_cast<AnyValue*>(this)->m_value = std::move(v);
1382 } else if (is<vector<AnyValue>>() && asVector<AnyValue>().empty()) {
1383 const_cast<AnyValue*>(this)->m_value = vector<AnyMap>();
1384 }
1385 const auto& vv = as<vector<AnyMap>>();
1386 checkSize(vv, nMin, nMax);
1387 return vv;
1388}
1389
1390template<>
1391vector<AnyMap>& AnyValue::asVector<AnyMap>(size_t nMin, size_t nMax)
1392{
1393 if (is<AnyMap>()) {
1394 vector<AnyMap> v;
1395 v.push_back(std::move(as<AnyMap>()));
1396 m_value = std::move(v);
1397 } else if (is<vector<AnyValue>>() && asVector<AnyValue>().empty()) {
1398 m_value = vector<AnyMap>();
1399 }
1400 auto& vv = as<vector<AnyMap>>();
1401 checkSize(vv, nMin, nMax);
1402 return vv;
1403}
1404
1405// Methods of class AnyMap
1406
1407AnyMap::AnyMap()
1408 : m_units(new UnitSystem())
1409{
1410}
1411
1412AnyValue& AnyMap::operator[](const string& key)
1413{
1414 const auto& iter = m_data.find(key);
1415 if (iter == m_data.end()) {
1416 // Create a new key to return
1417 AnyValue& value = m_data.emplace(key, AnyValue()).first->second;
1418 value.setKey(key);
1419 if (m_metadata) {
1421 }
1422
1423 // A pseudo-location used to set the ordering when outputting to
1424 // YAML so nodes added this way will come before nodes from YAML,
1425 // with insertion order preserved.
1426 value.setLoc(-1, m_column);
1427 m_column += 10;
1428
1429 return value;
1430 } else {
1431 // Return an already-existing item
1432 return iter->second;
1433 }
1434}
1435
1436const AnyValue& AnyMap::operator[](const string& key) const
1437{
1438 try {
1439 return m_data.at(key);
1440 } catch (std::out_of_range&) {
1441 throw InputFileError("AnyMap::operator[]", *this,
1442 "Key '{}' not found.\nExisting keys: {}", key, keys_str());
1443 }
1444}
1445
1446AnyValue& AnyMap::createForYaml(const string& key, int line, int column)
1447{
1448 AnyValue& value = m_data.emplace(key, AnyValue()).first->second;
1449 value.setKey(key);
1450 if (m_metadata) {
1452 }
1453
1454 value.setLoc(line, column);
1455 return value;
1456}
1457
1458const AnyValue& AnyMap::at(const string& key) const
1459{
1460 try {
1461 return m_data.at(key);
1462 } catch (std::out_of_range&) {
1463 throw InputFileError("AnyMap::at", *this,
1464 "Key '{}' not found.\nExisting keys: {}", key, keys_str());
1465 }
1466}
1467
1468bool AnyMap::empty() const
1469{
1470 // Iterate to check for non-hidden, non-excluded elements
1471 for ([[maybe_unused]] const auto& item : *this) {
1472 return false;
1473 }
1474 return true;
1475}
1476
1477bool AnyMap::hasKey(const string& key) const
1478{
1479 auto iter = m_data.find(key);
1480 return (iter != m_data.end() && !iter->second.is<Exclude>());
1481}
1482
1483void AnyMap::erase(const string& key)
1484{
1485 m_data.erase(key);
1486}
1487
1489{
1490 m_data.clear();
1491}
1492
1493void AnyMap::update(const AnyMap& other, bool keepExisting)
1494{
1495 for (const auto& [key, value] : other.ordered()) {
1496 if (!keepExisting || m_data.count(key) == 0) {
1497 (*this)[key] = value;
1498 }
1499 }
1500}
1501
1502void AnyMap::exclude(const string& key)
1503{
1504 m_data[key] = AnyValue::exclude();
1505}
1506
1507string AnyMap::keys_str() const
1508{
1509 fmt::memory_buffer b;
1510 auto iter = this->begin();
1511 if (iter != this->end()) {
1512 fmt_append(b, "{}", iter->first);
1513 ++iter;
1514 }
1515 while (iter != this->end()) {
1516 fmt_append(b, ", {}", iter->first);
1517 ++iter;
1518 }
1519 return to_string(b);
1520}
1521
1522set<string> AnyMap::keys() const
1523{
1524 set<string> out;
1525 auto iter = this->begin();
1526 while (iter != this->end()) {
1527 out.insert(iter->first);
1528 ++iter;
1529 }
1530 return out;
1531}
1532
1533void AnyMap::propagateMetadata(shared_ptr<AnyMap>& metadata)
1534{
1535 m_metadata = metadata;
1536 for (auto& [name, value] : m_data) {
1537 value.propagateMetadata(m_metadata);
1538 }
1539}
1540
1541void AnyMap::setMetadata(const string& key, const AnyValue& value)
1542{
1543 if (m_metadata) {
1544 // Fork the metadata tree at this point to avoid affecting parent nodes
1545 m_metadata = make_shared<AnyMap>(*m_metadata);
1546 } else {
1547 m_metadata = make_shared<AnyMap>();
1548 }
1549 (*m_metadata)[key] = value;
1551}
1552
1554{
1555 m_line = other.m_line;
1556 m_column = other.m_column;
1557 if (!other.m_metadata) {
1558 return;
1559 }
1560
1561 if (m_metadata) {
1562 // Fork the metadata tree at this point to avoid affecting parent nodes
1563 m_metadata = make_shared<AnyMap>(*m_metadata);
1564 } else {
1565 m_metadata = make_shared<AnyMap>();
1566 }
1567
1568 for (const auto& [key, value] : *other.m_metadata) {
1569 (*m_metadata)[key] = value;
1570 }
1571
1573}
1574
1575bool AnyMap::getBool(const string& key, bool default_) const
1576{
1577 return (hasKey(key)) ? m_data.at(key).asBool() : default_;
1578}
1579
1580double AnyMap::getDouble(const string& key, double default_) const
1581{
1582 return (hasKey(key)) ? m_data.at(key).asDouble() : default_;
1583}
1584
1585long int AnyMap::getInt(const string& key, long int default_) const
1586{
1587 return (hasKey(key)) ? m_data.at(key).asInt() : default_;
1588}
1589
1590const string& AnyMap::getString(const string& key, const string& default_) const
1591{
1592 return (hasKey(key)) ? m_data.at(key).asString() : default_;
1593}
1594
1595double AnyMap::convert(const string& key, const string& dest) const
1596{
1597 return units().convert(at(key), dest);
1598}
1599
1600double AnyMap::convert(const string& key, const Units& dest) const
1601{
1602 return units().convert(at(key), dest);
1603}
1604
1605double AnyMap::convert(const string& key, const string& dest,
1606 double default_) const
1607{
1608 if (hasKey(key)) {
1609 return units().convert(at(key), dest);
1610 } else {
1611 return default_;
1612 }
1613}
1614
1615vector<double> AnyMap::convertVector(const string& key, const string& dest,
1616 size_t nMin, size_t nMax) const
1617{
1618 return units().convert(at(key).asVector<AnyValue>(nMin, nMax), dest);
1619}
1620
1621AnyMap::Iterator::Iterator(
1622 const std::unordered_map<string, AnyValue>::const_iterator& start,
1623 const std::unordered_map<string, AnyValue>::const_iterator& stop)
1624{
1625 m_iter = start;
1626 m_stop = stop;
1627 while (m_iter != m_stop && (dunder(m_iter->first) || m_iter->second.is<Exclude>())) {
1628 ++m_iter;
1629 }
1630}
1631
1632AnyMap::Iterator& AnyMap::Iterator::operator++()
1633{
1634 ++m_iter;
1635 while (m_iter != m_stop && (dunder(m_iter->first) || m_iter->second.is<Exclude>())) {
1636 ++m_iter;
1637 }
1638 return *this;
1639}
1640
1641
1642AnyMap::OrderedProxy::OrderedProxy(const AnyMap& data, bool withUnits)
1643 : m_data(&data)
1644{
1645 // Units always come first
1646 if (withUnits && m_data->hasKey("__units__")
1647 && m_data->at("__units__").as<AnyMap>().size())
1648 {
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());
1653 }
1654
1655 int head = 0; // sort key of the first programmatically-added item
1656 int tail = 0; // sort key of the last programmatically-added item
1657 for (auto& item : *m_data) {
1658 const auto& order = item.second.order();
1659 if (order.first == -1) { // Item is not from an input file
1660 head = std::min(head, order.second);
1661 tail = std::max(tail, order.second);
1662 }
1663 m_ordered.emplace_back(order, &item);
1664 }
1665 std::sort(m_ordered.begin(), m_ordered.end());
1666
1667 // Adjust sort keys for items that should moved to the beginning or end of
1668 // the list
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);
1673 if (AnyMap::s_headFields.count(itemType)) {
1674 for (const auto& key : AnyMap::s_headFields[itemType]) {
1675 for (auto& [order, item] : m_ordered) {
1676 if (order.first >= 0) {
1677 // This and following items come from an input file and
1678 // should not be re-ordered
1679 break;
1680 }
1681 if (item->first == key) {
1682 order.second = --head;
1683 order_changed = true;
1684 }
1685 }
1686 }
1687 }
1688 if (AnyMap::s_tailFields.count(itemType)) {
1689 for (const auto& key : AnyMap::s_tailFields[itemType]) {
1690 for (auto& [order, item] : m_ordered) {
1691 if (order.first >= 0) {
1692 // This and following items come from an input file and
1693 // should not be re-ordered
1694 break;
1695 }
1696 if (item->first == key) {
1697 order.second = ++tail;
1698 order_changed = true;
1699 }
1700 }
1701 }
1702 }
1703
1704 if (order_changed) {
1705 std::sort(m_ordered.begin(), m_ordered.end());
1706 }
1707 }
1708}
1709
1710AnyMap::OrderedIterator AnyMap::OrderedProxy::begin() const
1711{
1712 return OrderedIterator(m_ordered.begin(), m_ordered.end());
1713}
1714
1715AnyMap::OrderedIterator AnyMap::OrderedProxy::end() const
1716{
1717 return OrderedIterator(m_ordered.end(), m_ordered.end());
1718}
1719
1720AnyMap::OrderedIterator::OrderedIterator(
1721 const AnyMap::OrderedProxy::OrderVector::const_iterator& start,
1722 const AnyMap::OrderedProxy::OrderVector::const_iterator& stop)
1723{
1724 m_iter = start;
1725 m_stop = stop;
1726}
1727
1728size_t AnyMap::size() const
1729{
1730 size_t n = 0;
1731 // Iterate to count only non-hidden, non-excluded elements
1732 for ([[maybe_unused]] const auto& item : *this) {
1733 n++;
1734 }
1735 return n;
1736}
1737
1738bool AnyMap::operator==(const AnyMap& other) const
1739{
1740 // First, make sure that 'other' has all of the non-hidden keys that are in
1741 // this map
1742 for (auto& [key, value] : *this) {
1743 if (!other.hasKey(key)) {
1744 return false;
1745 }
1746 }
1747 // Then check for equality, using the non-hidden keys from 'other'
1748 for (auto & [key, value] : other) {
1749 if (!hasKey(key) || value != at(key)) {
1750 return false;
1751 }
1752 }
1753 return true;
1754}
1755
1756bool AnyMap::operator!=(const AnyMap& other) const
1757{
1758 return !(*this == other);
1759}
1760
1762{
1764}
1765
1766void AnyMap::applyUnits(shared_ptr<UnitSystem>& units) {
1767 if (hasKey("units")) {
1768 m_data["__units__"] = std::move(m_data["units"]);
1769 m_data.erase("units");
1770 }
1771 if (hasKey("__units__")) {
1772 m_units = make_shared<UnitSystem>(*units);
1773 m_units->setDefaults(m_data["__units__"].asMap<string>());
1774 } else {
1775 m_units = units;
1776 }
1777 for (auto& [name, item] : m_data) {
1778 item.applyUnits(m_units);
1779 }
1780}
1781
1783{
1784 if (hasKey("__units__")) {
1785 for (const auto& [dimension, value] : units.getDelta(*m_units)) {
1786 m_data["__units__"][dimension] = value;
1787 }
1788 } else {
1789 m_data["__units__"] = units.getDelta(*m_units);
1790 }
1791 m_units = make_shared<UnitSystem>(units);
1792}
1793
1794void AnyMap::setFlowStyle(bool flow) {
1795 m_data["__flow__"] = flow;
1796}
1797
1798bool AnyMap::addOrderingRules(const string& objectType,
1799 const vector<vector<string>>& specs)
1800{
1801 std::unique_lock<std::mutex> lock(yaml_field_order_mutex);
1802 for (const auto& spec : specs) {
1803 if (spec.at(0) == "head") {
1804 s_headFields[objectType].insert(s_headFields[objectType].begin(),
1805 spec.at(1));
1806 } else if (spec.at(0) == "tail") {
1807 s_tailFields[objectType].push_back(spec.at(1));
1808 } else {
1809 throw CanteraError("AnyMap::addOrderingRules",
1810 "Unknown ordering rule '{}'", spec.at(0));
1811 }
1812 }
1813 return true;
1814}
1815
1816void AnyMap::clearCachedFile(const string& filename)
1817{
1818 string fullName = findInputFile(filename);
1819 if (s_cache.count(fullName)) {
1820 s_cache.erase(fullName);
1821 }
1822}
1823
1824AnyMap AnyMap::fromYamlString(const string& yaml) {
1825 AnyMap amap;
1826 try {
1827 YAML::Node node = YAML::Load(yaml);
1828 amap = node.as<AnyMap>();
1829 } catch (YAML::Exception& err) {
1830 AnyMap fake;
1831 fake.setLoc(err.mark.line, err.mark.column);
1832 fake.setMetadata("file-contents", AnyValue(yaml));
1833 throw InputFileError("AnyMap::fromYamlString", fake, err.msg);
1834 }
1835 amap.setMetadata("file-contents", AnyValue(yaml));
1836 amap.setLoc(0, 0);
1837 amap.applyUnits();
1838 return amap;
1839}
1840
1841AnyMap AnyMap::fromYamlFile(const string& name, const string& parent_name)
1842{
1843 string fullName;
1844 // See if a file with this name exists in a path relative to the parent file
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;
1850 }
1851 }
1852 // Otherwise, search the Cantera include path for the file
1853 if (fullName.empty()) {
1854 fullName = findInputFile(name);
1855 }
1856
1857 // Check for an already-parsed YAML file with the same last-modified time,
1858 // and return that if possible
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;
1864 }
1865
1866 if (!std::ifstream(fullName).good()) {
1867 throw CanteraError("AnyMap::fromYamlFile", "Input file '{}' not found "
1868 "on the Cantera search path.", name);
1869 }
1870
1871 // Generate an AnyMap from the YAML file and store it in the cache
1872 auto& [cache_item, cache_time] = s_cache[fullName];
1873 cache_time = mtime;
1874 try {
1875 YAML::Node node = YAML::LoadFile(fullName);
1876 cache_item = node.as<AnyMap>();
1877 cache_item.setMetadata("filename", AnyValue(fullName));
1878 cache_item.applyUnits();
1879 } catch (YAML::Exception& err) {
1880 s_cache.erase(fullName);
1881 AnyMap fake;
1882 fake.setLoc(err.mark.line, err.mark.column);
1883 fake.setMetadata("filename", AnyValue(fullName));
1884 throw InputFileError("AnyMap::fromYamlFile", fake, err.msg);
1885 } catch (CanteraError&) {
1886 s_cache.erase(fullName);
1887 throw;
1888 }
1889 cache_item["__file__"] = fullName;
1890 cache_item.setLoc(0, 0);
1891
1892 if (cache_item.hasKey("deprecated")) {
1893 warn_deprecated(fullName, cache_item["deprecated"].asString());
1894 }
1895
1896 // Return a copy of the AnyMap
1897 return cache_item;
1898}
1899
1900string AnyMap::toYamlString() const
1901{
1902 YAML::Emitter out;
1903 const_cast<AnyMap*>(this)->applyUnits();
1904 out << *this;
1905 out << YAML::Newline;
1906 return out.c_str();
1907}
1908
1910 return v.as<AnyMap>().begin();
1911}
1912
1913AnyMap::Iterator end(const AnyValue& v) {
1914 return v.as<AnyMap>().end();
1915}
1916
1917namespace {
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)
1920{
1921 if (lineno2 == -1) {
1922 lineno2 = lineno;
1923 column2 = column;
1924 }
1925
1926 fmt_append(b, "| Line |\n");
1927 if (!metadata->hasKey("file-contents")) {
1928 std::ifstream infile(findInputFile(filename));
1929 std::stringstream buffer;
1930 buffer << infile.rdbuf();
1931 (*metadata)["file-contents"] = buffer.str();
1932 }
1933 string line;
1934 int i = 0;
1935 int lastShown = -1;
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);
1941 lastShown = i;
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");
1946 }
1947 fmt_append(b, "| {: 5d} | {}\n", i+1, line);
1948 lastShown = i;
1949 }
1950 i++;
1951 }
1952}
1953}
1954
1955string InputFileError::formatError(const string& message, int lineno, int column,
1956 const shared_ptr<AnyMap>& metadata)
1957{
1958 if (!metadata) {
1959 return message;
1960 }
1961 string filename = metadata->getString("filename", "input string");
1962
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);
1967}
1968
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)
1973{
1974 if (!metadata1 || !metadata2) {
1975 return message;
1976 }
1977 string filename1 = metadata1->getString("filename", "input string");
1978 string filename2 = metadata2->getString("filename", "input string");
1979
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);
1986 } else {
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);
1992 }
1993
1994 return to_string(b);
1995}
1996
1997void warn_deprecated(const string& source, const AnyBase& node, const string& message)
1998{
1999 if (!node.m_metadata) {
2000 warn_deprecated(source, message);
2001 return;
2002 }
2003
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);
2009 formatInputFile(b, node.m_metadata, filename, node.m_line, node.m_column);
2010 warn_deprecated(source, to_string(b));
2011}
2012
2013}
void emitString(YAML::Emitter &out, const string &str0)
Write YAML strings spanning multiple lines if input includes endline ' '.
Definition AnyMap.cpp:352
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...
Definition AnyMap.cpp:372
Base class defining common data possessed by both AnyMap and AnyValue objects.
Definition AnyMap.h:34
int m_column
If m_line >= 0, the column where this value occurs in the input file.
Definition AnyMap.h:56
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.
Definition AnyMap.cpp:617
int m_line
The line where this value occurs in the input file.
Definition AnyMap.h:52
friend void warn_deprecated(const string &source, const AnyBase &node, const string &message)
A deprecation warning for syntax in an input file.
Definition AnyMap.cpp:1997
const AnyValue & getMetadata(const string &key) const
Get a value from the metadata applicable to the AnyMap tree containing this node.
Definition AnyMap.cpp:623
shared_ptr< AnyMap > m_metadata
Metadata relevant to an entire AnyMap tree, such as information about.
Definition AnyMap.h:60
Defined to allow use with range-based for loops.
Definition AnyMap.h:549
Defined to allow the OrderedProxy class to be used with range-based for loops.
Definition AnyMap.h:603
A map of string keys to values whose type can vary at runtime.
Definition AnyMap.h:431
static AnyMap fromYamlString(const string &yaml)
Create an AnyMap from a string containing a YAML document.
Definition AnyMap.cpp:1824
Iterator begin() const
Defined to allow use with range-based for loops.
Definition AnyMap.h:572
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...
Definition AnyMap.cpp:1446
set< string > keys() const
Return an unordered set of keys.
Definition AnyMap.cpp:1522
size_t size() const
Returns the number of elements in this map.
Definition AnyMap.cpp:1728
void exclude(const string &key)
Mark key as excluded from this map.
Definition AnyMap.cpp:1502
long int getInt(const string &key, long int default_) const
If key exists, return it as a long int, otherwise return default_.
Definition AnyMap.cpp:1585
void copyMetadata(const AnyMap &other)
Copy metadata including input line/column from an existing AnyMap.
Definition AnyMap.cpp:1553
static std::unordered_map< string, pair< AnyMap, std::filesystem::file_time_type > > s_cache
Cache for previously-parsed input (YAML) files.
Definition AnyMap.h:721
double getDouble(const string &key, double default_) const
If key exists, return it as a double, otherwise return default_.
Definition AnyMap.cpp:1580
bool hasKey(const string &key) const
Returns true if the map contains an item named key.
Definition AnyMap.cpp:1477
const UnitSystem & units() const
Return the default units that should be used to convert stored values.
Definition AnyMap.h:640
Iterator end() const
Defined to allow use with range-based for loops.
Definition AnyMap.h:577
bool empty() const
Return boolean indicating whether AnyMap is empty.
Definition AnyMap.cpp:1468
static void clearCachedFile(const string &filename)
Remove the specified file from the input cache if it is present.
Definition AnyMap.cpp:1816
void applyUnits()
Use the supplied UnitSystem to set the default units, and recursively process overrides from nodes na...
Definition AnyMap.cpp:1761
static std::unordered_map< string, vector< string > > s_headFields
Information about fields that should appear first when outputting to YAML.
Definition AnyMap.h:726
double convert(const string &key, const string &units) const
Convert the item stored by the given key to the units specified in units.
Definition AnyMap.cpp:1595
void setMetadata(const string &key, const AnyValue &value)
Set a metadata value that applies to this AnyMap and its children.
Definition AnyMap.cpp:1541
AnyValue & operator[](const string &key)
Get the value of the item stored in key.
Definition AnyMap.cpp:1412
OrderedProxy ordered(bool withUnits=false) const
Return a proxy object that allows iteration in an order determined by the order of insertion,...
Definition AnyMap.h:629
void setFlowStyle(bool flow=true)
Use "flow" style when outputting this AnyMap to YAML.
Definition AnyMap.cpp:1794
void propagateMetadata(shared_ptr< AnyMap > &file)
Propagate metadata to any child elements.
Definition AnyMap.cpp:1533
bool getBool(const string &key, bool default_) const
If key exists, return it as a bool, otherwise return default_.
Definition AnyMap.cpp:1575
static std::unordered_map< string, vector< string > > s_tailFields
Information about fields that should appear last when outputting to YAML.
Definition AnyMap.h:731
void clear()
Erase all items in the mapping.
Definition AnyMap.cpp:1488
shared_ptr< UnitSystem > m_units
The default units that are used to convert stored values.
Definition AnyMap.h:715
const string & getString(const string &key, const string &default_) const
If key exists, return it as a string, otherwise return default_.
Definition AnyMap.cpp:1590
void erase(const string &key)
Erase the value held by key.
Definition AnyMap.cpp:1483
static AnyMap fromYamlFile(const string &name, const string &parent_name="")
Create an AnyMap from a YAML file.
Definition AnyMap.cpp:1841
std::unordered_map< string, AnyValue > m_data
The stored data.
Definition AnyMap.h:712
const AnyValue & at(const string &key) const
Get the value of the item stored in key.
Definition AnyMap.cpp:1458
void update(const AnyMap &other, bool keepExisting=true)
Add items from other to this AnyMap.
Definition AnyMap.cpp:1493
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.
Definition AnyMap.cpp:1798
void setUnits(const UnitSystem &units)
Set the unit system for this AnyMap.
Definition AnyMap.cpp:1782
string keys_str() const
Return a string listing the keys in this AnyMap, for use in error messages, for example.
Definition AnyMap.cpp:1507
vector< double > convertVector(const string &key, const string &units, size_t nMin=npos, size_t nMax=npos) const
Convert a vector of dimensional values.
Definition AnyMap.cpp:1615
A wrapper for a variable whose type is determined at runtime.
Definition AnyMap.h:87
const string & asString() const
Return the held value, if it is a string.
Definition AnyMap.cpp:782
bool isVector() const
Returns true if the held value is a vector of the specified type, such as vector<double>.
Definition AnyMap.inl.h:75
void setKey(const string &key)
Set the name of the key storing this value in an AnyMap.
Definition AnyMap.cpp:664
pair< int, int > order() const
Return values used to determine the sort order when outputting to YAML.
Definition AnyMap.cpp:1157
bool hasMapWhere(const string &key, const string &value) const
Returns true when getMapWhere() would succeed.
Definition AnyMap.cpp:1134
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"}.
Definition AnyMap.cpp:812
bool hasKey(const string &key) const
Returns true if this AnyValue is an AnyMap and that map contains a key with the given name.
Definition AnyMap.cpp:660
map< string, T > asMap() const
Return the held AnyMap as a map where all of the values have the specified type.
Definition AnyMap.inl.h:162
bool & asBool()
Return the held value, if it is a bool.
Definition AnyMap.cpp:914
bool empty() const
Return boolean indicating whether AnyValue is empty.
Definition AnyMap.cpp:690
pair< size_t, size_t > matrixShape() const
Returns rows and columns of a matrix.
Definition AnyMap.cpp:714
size_t vectorSize() const
Returns size of the held vector.
Definition AnyMap.cpp:698
long int & asInt()
Return the held value, if it is a long int.
Definition AnyMap.cpp:946
void applyUnits(shared_ptr< UnitSystem > &units)
See AnyMap::applyUnits()
Definition AnyMap.cpp:1162
const std::type_info & type() const
Returns the type of the held value.
Definition AnyMap.cpp:666
double & asDouble()
Return the held value as a double, if it is a double or a long int.
Definition AnyMap.cpp:867
static AnyValue exclude()
Definition AnyMap.cpp:1019
bool isScalar() const
Returns true if the held value is a scalar type (such as double, long int, string,...
Definition AnyMap.cpp:694
AnyValue & operator[](const string &key)
If this AnyValue is an AnyMap, return the value stored in key.
Definition AnyMap.cpp:650
string m_key
Key of this value in a parent AnyMap
Definition AnyMap.h:305
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.
Definition AnyMap.cpp:1084
void setFlowStyle(bool flow=true)
See AnyMap::setFlowStyle()
Definition AnyMap.cpp:1259
void propagateMetadata(shared_ptr< AnyMap > &file)
Propagate metadata to any child elements.
Definition AnyMap.cpp:670
std::any m_value
The held value.
Definition AnyMap.h:308
bool is() const
Returns true if the held value is of the specified type.
Definition AnyMap.inl.h:68
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.
Definition AnyMap.inl.h:109
const T & as() const
Get the value of this key as the specified type.
Definition AnyMap.inl.h:16
string type_str() const
Returns a string specifying the type of the held value.
Definition AnyMap.cpp:686
Base class for exceptions thrown by Cantera classes.
Error thrown for problems processing information contained in an AnyMap or AnyValue.
Definition AnyMap.h:749
An error indicating that an unimplemented function has been called.
Unit conversion utility.
Definition Units.h:169
double convert(double value, const string &src, const string &dest) const
Convert value from the units of src to the units of dest.
Definition Units.cpp:538
AnyMap getDelta(const UnitSystem &other) const
Get the changes to the defaults from other to this UnitSystem.
Definition Units.cpp:757
A representation of the units associated with a dimensional quantity.
Definition Units.h:35
void fmt_append(fmt::memory_buffer &b, const std::string &tmpl, Args... args)
Versions 6.2.0 and 6.2.1 of fmtlib do not include this define before they include windows....
Definition fmt.h:29
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.
Definition global.cpp:213
string findInputFile(const string &name)
Find an input file.
Definition global.cpp:169
void scale(InputIter begin, InputIter end, OutputIter out, S scale_factor)
Multiply elements of an array by a scale factor.
Definition utilities.h:104
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
const size_t npos
index returned by functions to indicate "no position"
Definition ct_defs.h:180
Contains declarations for string manipulation functions within Cantera.
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...