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