Cantera  3.1.0a1
Units.cpp
Go to the documentation of this file.
1 //! @file Units.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/Units.h"
8 #include "cantera/base/global.h"
10 #include "cantera/base/AnyMap.h"
11 #include "cantera/base/utilities.h"
12 #include <regex>
13 
14 namespace {
15 using namespace Cantera;
16 
17 const map<string, Units> knownUnits{
18  {"", Units(1.0)},
19  {"1", Units(1.0)},
20 
21  // Mass [M]
22  {"kg", Units(1.0, 1, 0, 0)},
23  {"g", Units(1e-3, 1, 0, 0)},
24 
25  // Length [L]
26  {"m", Units(1.0, 0, 1, 0)},
27  {"micron", Units(1e-6, 0, 1, 0)},
28  {"angstrom", Units(1e-10, 0, 1, 0)},
29  {"Å", Units(1e-10, 0, 1, 0)},
30 
31  // Time [T]
32  {"s", Units(1.0, 0, 0, 1)},
33  {"min", Units(60, 0, 0, 1)},
34  {"hr", Units(3600, 0, 0, 1)},
35 
36  // Temperature [K]
37  {"K", Units(1.0, 0, 0, 0, 1)},
38  {"C", Units(1.0, 0, 0, 0, 1)},
39 
40  // Current [A]
41  {"A", Units(1.0, 0, 0, 0, 0, 1)},
42 
43  // Quantity [Q]
44  {"mol", Units(1e-3, 0, 0, 0, 0, 0, 1)},
45  {"gmol", Units(1e-3, 0, 0, 0, 0, 0, 1)},
46  {"mole", Units(1e-3, 0, 0, 0, 0, 0, 1)},
47  {"kmol", Units(1.0, 0, 0, 0, 0, 0, 1)},
48  {"kgmol", Units(1.0, 0, 0, 0, 0, 0, 1)},
49  {"molec", Units(1.0/Avogadro, 0, 0, 0, 0, 0, 1)},
50 
51  // Energy [M*L^2/T^2]
52  {"J", Units(1.0, 1, 2, -2)},
53  {"cal", Units(4.184, 1, 2, -2)},
54  {"erg", Units(1e-7, 1, 2, -2)},
55  {"eV", Units(ElectronCharge, 1, 2, -2)},
56 
57  // Force [M*L/T^2]
58  {"N", Units(1.0, 1, 1, -2)},
59  {"dyn", Units(1e-5, 1, 1, -2)},
60 
61  // Pressure [M/L/T^2]
62  {"Pa", Units(1.0, 1, -1, -2)},
63  {"atm", Units(OneAtm, 1, -1, -2)},
64  {"bar", Units(1.0e5, 1, -1, -2)},
65  {"dyn/cm^2", Units(0.1, 1, -1, -2)},
66 
67  // Volume [L^3]
68  {"m^3", Units(1.0, 0, 3, 0)},
69  {"liter", Units(0.001, 0, 3, 0)},
70  {"L", Units(0.001, 0, 3, 0)},
71  {"l", Units(0.001, 0, 3, 0)},
72  {"cc", Units(1.0e-6, 0, 3, 0)},
73 
74  // Other electrical units
75  {"ohm", Units(1.0, 1, 2, -3, 0, -2)}, // kg*m^2/s^3/A^2
76  {"V", Units(1.0, 1, 2, -3, 0, -1)}, // kg*m^2/s^3/A
77  {"coulomb", Units(1.0, 0, 0, 1, 0, 1)}, // A*s
78 
79  //! Activation energy units [M*L^2/T^2/Q]
80  {"J/kmol", Units(1.0, 1, 2, -2, 0, 0, -1)},
81 };
82 
83 const map<string, double> prefixes{
84  {"Y", 1e24},
85  {"Z", 1e21},
86  {"E", 1e18},
87  {"P", 1e15},
88  {"T", 1e12},
89  {"G", 1e9},
90  {"M", 1e6},
91  {"k", 1e3},
92  {"h", 1e2},
93  {"d", 1e-1},
94  {"c", 1e-2},
95  {"m", 1e-3},
96  {"u", 1e-6},
97  {"n", 1e-9},
98  {"p", 1e-12},
99  {"f", 1e-15},
100  {"a", 1e-18},
101  {"z", 1e-21},
102  {"y", 1e-24}
103 };
104 }
105 
106 namespace Cantera
107 {
108 
109 Units::Units(double factor, double mass, double length, double time,
110  double temperature, double current, double quantity)
111  : m_factor(factor)
112  , m_mass_dim(mass)
113  , m_length_dim(length)
114  , m_time_dim(time)
115  , m_temperature_dim(temperature)
116  , m_current_dim(current)
117  , m_quantity_dim(quantity)
118 {
119  if (mass != 0 && length == -mass && time == -2 * mass
120  && temperature == 0 && current == 0 && quantity == 0) {
121  // Dimension looks like Pa^n
122  m_pressure_dim = mass;
123  } else if (mass != 0 && length == 2 * mass && time == -2 * mass
124  && temperature == 0 && current == 0 && quantity == 0)
125  {
126  // Dimension looks like J^n
127  m_energy_dim = mass;
128  }
129 }
130 
131 Units::Units(const string& name, bool force_unity)
132 {
133  size_t start = 0;
134 
135  // Determine factor
136  static std::regex regexp("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)");
137  std::smatch matched;
138  std::regex_search(name, matched, regexp);
139  if (matched.size()) {
140  string factor = *matched.begin();
141  if (name.find(factor) == 0) {
143  start = factor.size();
144  }
145  }
146 
147  while (true) {
148  // Split into groups of the form 'unit^exponent'
149  size_t stop = name.find_first_of("*/", start);
150  size_t caret = name.find('^', start);
151  if (caret > stop) {
152  // No caret in this group
153  caret = npos;
154  }
155  string unit = trimCopy(
156  name.substr(start, std::min(caret, stop) - start));
157 
158  double exponent = 1.0;
159  if (caret != npos) {
160  exponent = fpValueCheck(name.substr(caret+1, stop-caret-1));
161  }
162  if (start != 0 && name[start-1] == '/') {
163  // This unit is in the denominator
164  exponent = -exponent;
165  }
166 
167  if (knownUnits.find(unit) != knownUnits.end()) {
168  // Incorporate the unit defined by the current group
169  *this *= knownUnits.at(unit).pow(exponent);
170  } else {
171  // See if the unit looks like a prefix + base unit
172  string prefix = unit.substr(0, 1);
173  string suffix = unit.substr(1);
174  if (prefixes.find(prefix) != prefixes.end() &&
175  knownUnits.find(suffix) != knownUnits.end()) {
176  Units u = knownUnits.at(suffix);
177  u.scale(prefixes.at(prefix));
178  *this *= u.pow(exponent);
179  } else {
180  throw CanteraError("Units::Units(string)",
181  "Unknown unit '{}' in unit string '{}'", unit, name);
182  }
183  }
184 
185  start = stop+1;
186  if (stop == npos) {
187  break;
188  }
189  }
190 
191  if (force_unity && (std::abs(m_factor - 1.) > SmallNumber)) {
192  throw CanteraError("Units::Units(string)",
193  "Detected non-unity conversion factor:\n"
194  "input '{}' is equivalent to '{}' where the conversion factor is '{}'",
195  name, str(), m_factor);
196  }
197 }
198 
199 bool Units::convertible(const Units& other) const
200 {
201  return (m_mass_dim == other.m_mass_dim &&
202  m_length_dim == other.m_length_dim &&
203  m_time_dim == other.m_time_dim &&
204  m_temperature_dim == other.m_temperature_dim &&
205  m_current_dim == other.m_current_dim &&
206  m_quantity_dim == other.m_quantity_dim);
207 }
208 
210 {
211  m_factor *= other.m_factor;
212  m_mass_dim += other.m_mass_dim;
213  m_length_dim += other.m_length_dim;
214  m_time_dim += other.m_time_dim;
215  m_temperature_dim += other.m_temperature_dim;
216  m_current_dim += other.m_current_dim;
217  m_quantity_dim += other.m_quantity_dim;
219  m_energy_dim += other.m_energy_dim;
220  return *this;
221 }
222 
223 Units Units::pow(double exponent) const
224 {
225  return Units(std::pow(m_factor, exponent),
226  m_mass_dim * exponent,
227  m_length_dim * exponent,
228  m_time_dim * exponent,
229  m_temperature_dim * exponent,
230  m_current_dim * exponent,
231  m_quantity_dim * exponent);
232 }
233 
234 string Units::str(bool skip_unity) const
235 {
236  map<string, double> dims{
237  {"kg", m_mass_dim},
238  {"m", m_length_dim},
239  {"s", m_time_dim},
240  {"K", m_temperature_dim},
241  {"A", m_current_dim},
242  {"kmol", m_quantity_dim},
243  };
244 
245  string num = "";
246  string den = "";
247  for (auto const& [dimension, exponent] : dims) {
248  int rounded = (int)round(exponent);
249  if (exponent == 0.) {
250  // skip
251  } else if (exponent == 1.) {
252  num.append(fmt::format(" * {}", dimension));
253  } else if (exponent == -1.) {
254  den.append(fmt::format(" / {}", dimension));
255  } else if (exponent == rounded && rounded > 0) {
256  num.append(fmt::format(" * {}^{}", dimension, rounded));
257  } else if (exponent == rounded) {
258  den.append(fmt::format(" / {}^{}", dimension, -rounded));
259  } else if (exponent > 0) {
260  num.append(fmt::format(" * {}^{}", dimension, exponent));
261  } else {
262  den.append(fmt::format(" / {}^{}", dimension, -exponent));
263  }
264  }
265 
266  if (skip_unity && (std::abs(m_factor - 1.) < SmallNumber)) {
267  if (num.size()) {
268  return fmt::format("{}{}", num.substr(3), den);
269  }
270  // print '1' as the numerator is empty
271  return fmt::format("1{}", den);
272  }
273 
274  string factor;
275  if (m_factor == round(m_factor)) {
276  // ensure that fmt::format does not round to integer
277  factor = fmt::format("{:.1f}", m_factor);
278  } else {
279  factor = fmt::format("{}", m_factor);
280  }
281 
282  if (num.size()) {
283  // concatenate factor, numerator and denominator (skipping leading '*')
284  return fmt::format("{} {}{}", factor, num.substr(3), den);
285  }
286 
287  return fmt::format("{}{}", factor, den);
288 }
289 
290 bool Units::operator==(const Units& other) const
291 {
292  return m_factor == other.m_factor
293  && m_mass_dim == other.m_mass_dim
294  && m_length_dim == other.m_length_dim
295  && m_time_dim == other.m_time_dim
296  && m_temperature_dim == other.m_temperature_dim
297  && m_current_dim == other.m_current_dim
298  && m_quantity_dim == other.m_quantity_dim
299  && m_pressure_dim == other.m_pressure_dim
300  && m_energy_dim == other.m_energy_dim;
301 }
302 
303 double Units::dimension(const string& primary) const
304 {
305  if (primary == "mass") {
306  return m_mass_dim;
307  } else if (primary == "length") {
308  return m_length_dim;
309  } else if (primary == "time") {
310  return m_time_dim;
311  } else if (primary == "temperature") {
312  return m_temperature_dim;
313  } else if (primary == "current") {
314  return m_current_dim;
315  } else if (primary == "quantity") {
316  return m_quantity_dim;
317  } else {
318  throw CanteraError("Units::dimension",
319  "Unknown primary unit '{}'.", primary);
320  }
321 }
322 
324 {
325  if (!stack.size()) {
326  return Units(0);
327  }
328  return stack[0].first;
329 }
330 
331 void UnitStack::setStandardUnits(Units& standardUnits)
332 {
333  if (!stack.size()) {
334  stack.emplace_back(standardUnits, 0.);
335  return;
336  }
337  if (stack[0].second != 0.) {
338  throw CanteraError("UnitStack::setStandardUnit",
339  "Standard unit is already defined.");
340  }
341  stack[0].first = standardUnits;
342 }
343 
345 {
346  if (!stack.size()) {
347  return NAN;
348  }
349  return stack[0].second;
350 }
351 
352 void UnitStack::join(double exponent)
353 {
354  if (!stack.size()) {
355  throw CanteraError("UnitStack::join",
356  "Standard unit is not defined.");
357  }
358  stack[0].second += exponent;
359 
360 }
361 
362 void UnitStack::update(const Units& units, double exponent)
363 {
364  bool found = false;
365  for (auto& [current_unit, current_exp] : stack) {
366  if (current_unit == units) {
367  current_exp += exponent;
368  found = true;
369  break;
370  }
371  }
372  if (!found) {
373  stack.emplace_back(units, exponent);
374  }
375 }
376 
378 {
379  if (!stack.size()) {
380  return Units(0.);
381  }
382  Units out = Units(1.);
383  for (auto& [units, exponent] : stack) {
384  if (exponent == 1) {
385  out *= units;
386  } else {
387  out *= units.pow(exponent);
388  }
389  }
390  return out;
391 }
392 
393 UnitSystem::UnitSystem(std::initializer_list<string> units)
394 {
395  setDefaults(units);
396 }
397 
398 map<string, string> UnitSystem::defaults() const
399 {
400  // Unit system defaults
401  map<string, string> units{
402  {"mass", "kg"},
403  {"length", "m"},
404  {"time", "s"},
405  {"quantity", "kmol"},
406  {"pressure", "Pa"},
407  {"energy", "J"},
408  {"temperature", "K"},
409  {"current", "A"},
410  {"activation-energy", "J / kmol"},
411  };
412 
413  // Overwrite entries that have conversion factors
414  for (const auto& [dimension, default_unit] : m_defaults) {
415  units[dimension] = default_unit;
416  }
417 
418  // Activation energy follows specified energy and quantity units
419  // unless given explicitly
420  if (!m_defaults.count("activation-energy")) {
421  units["activation-energy"] = fmt::format(
422  "{} / {}", units["energy"], units["quantity"]);
423  }
424 
425  return units;
426 }
427 
428 void UnitSystem::setDefaults(std::initializer_list<string> units)
429 {
430  for (const auto& name : units) {
431  auto unit = Units(name);
432  if (unit.convertible(knownUnits.at("kg"))) {
433  m_mass_factor = unit.factor();
434  m_defaults["mass"] = name;
435  } else if (unit.convertible(knownUnits.at("m"))) {
436  m_length_factor = unit.factor();
437  m_defaults["length"] = name;
438  } else if (unit.convertible(knownUnits.at("s"))) {
439  m_time_factor = unit.factor();
440  m_defaults["time"] = name;
441  } else if (unit.convertible(knownUnits.at("kmol"))) {
442  m_quantity_factor = unit.factor();
443  m_defaults["quantity"] = name;
444  } else if (unit.convertible(knownUnits.at("Pa"))) {
445  m_pressure_factor = unit.factor();
446  m_defaults["pressure"] = name;
447  } else if (unit.convertible(knownUnits.at("J"))) {
448  m_energy_factor = unit.factor();
449  m_defaults["energy"] = name;
450  } else if (unit.convertible(knownUnits.at("K"))) {
451  // Do nothing -- no other scales are supported for temperature
452  if (unit.factor() != 1.) {
453  throw CanteraError("UnitSystem::setDefaults", "Temperature scales "
454  "with non-unity conversion factor from Kelvin are not supported.");
455  }
456  } else if (unit.convertible(knownUnits.at("A"))) {
457  // Do nothing -- no other scales are supported for current
458  if (unit.factor() != 1.) {
459  throw CanteraError("UnitSystem::setDefaults", "Current scales "
460  "with non-unity conversion factor from Ampere are not supported.");
461  }
462  } else {
463  throw CanteraError("UnitSystem::setDefaults",
464  "Unable to match unit '{}' to a basic dimension", name);
465  }
466  }
469  }
470 }
471 
472 void UnitSystem::setDefaults(const map<string, string>& units)
473 {
474  for (const auto& [dimension, name] : units) {
475  Units unit(name);
476  if (dimension == "mass" && unit.convertible(knownUnits.at("kg"))) {
477  m_mass_factor = unit.factor();
478  m_defaults["mass"] = name;
479  } else if (dimension == "length" && unit.convertible(knownUnits.at("m"))) {
480  m_length_factor = unit.factor();
481  m_defaults["length"] = name;
482  } else if (dimension == "time" && unit.convertible(knownUnits.at("s"))) {
483  m_time_factor = unit.factor();
484  m_defaults["time"] = name;
485  } else if (dimension == "temperature" && unit.convertible(knownUnits.at("K"))) {
486  // do nothing - no other temperature scales are supported
487  if (unit.factor() != 1.) {
488  throw CanteraError("UnitSystem::setDefaults", "Temperature scales "
489  "with non-unity conversion factor from Kelvin are not supported.");
490  }
491  } else if (dimension == "current" && unit.convertible(knownUnits.at("A"))) {
492  // do nothing - no other current scales are supported
493  if (unit.factor() != 1.) {
494  throw CanteraError("UnitSystem::setDefaults", "Current scales "
495  "with non-unity conversion factor from Ampere are not supported.");
496  }
497  } else if (dimension == "quantity" && unit.convertible(knownUnits.at("kmol"))) {
498  m_quantity_factor = unit.factor();
499  m_defaults["quantity"] = name;
500  } else if (dimension == "pressure" && unit.convertible(knownUnits.at("Pa"))) {
501  m_pressure_factor = unit.factor();
502  m_defaults["pressure"] = name;
503  } else if (dimension == "energy" && unit.convertible(knownUnits.at("J"))) {
504  m_energy_factor = unit.factor();
505  m_defaults["energy"] = name;
506  } else if (dimension == "activation-energy") {
507  // handled separately to allow override
508  } else {
509  throw CanteraError("UnitSystem::setDefaults",
510  "Unable to set default unit for '{}' to '{}' ({}).",
511  dimension, name, unit.str());
512  }
513  }
514  if (units.find("activation-energy") != units.end()) {
515  setDefaultActivationEnergy(units.at("activation-energy"));
516  } else if (!m_explicit_activation_energy) {
518  }
519 }
520 
521 void UnitSystem::setDefaultActivationEnergy(const string& e_units)
522 {
523  Units u(e_units);
524  m_defaults["activation-energy"] = e_units;
525  if (u.convertible(Units("J/kmol"))) {
527  } else if (u.convertible(knownUnits.at("K"))) {
529  } else if (u.convertible(knownUnits.at("eV"))) {
531  } else {
532  throw CanteraError("Units::setDefaultActivationEnergy",
533  "Unable to match unit '{}' to a unit of activation energy", e_units);
534  }
536 }
537 
538 double UnitSystem::convert(double value, const string& src, const string& dest) const
539 {
540  return convert(value, Units(src), Units(dest));
541 }
542 
543 double UnitSystem::convert(double value, const Units& src,
544  const Units& dest) const
545 {
546  if (!src.convertible(dest)) {
547  throw CanteraError("UnitSystem::convert",
548  "Incompatible units:\n Units({}) and\n Units({})",
549  src.str(), dest.str());
550  }
551  return value * src.factor() / dest.factor();
552 }
553 
554 double UnitSystem::convertTo(double value, const string& dest) const
555 {
556  return convertTo(value, Units(dest));
557 }
558 
559 double UnitSystem::convertTo(double value, const Units& dest) const
560 {
561  return value / dest.factor()
562  * pow(m_mass_factor, dest.m_mass_dim - dest.m_pressure_dim - dest.m_energy_dim)
563  * pow(m_length_factor, dest.m_length_dim + dest.m_pressure_dim - 2*dest.m_energy_dim)
564  * pow(m_time_factor, dest.m_time_dim + 2*dest.m_pressure_dim + 2*dest.m_energy_dim)
565  * pow(m_quantity_factor, dest.m_quantity_dim)
566  * pow(m_pressure_factor, dest.m_pressure_dim)
567  * pow(m_energy_factor, dest.m_energy_dim);
568 }
569 
570 double UnitSystem::convertFrom(double value, const string& dest) const
571 {
572  return convertFrom(value, Units(dest));
573 }
574 
575 double UnitSystem::convertFrom(double value, const Units& src) const
576 {
577  return value * src.factor()
578  * pow(m_mass_factor, -src.m_mass_dim + src.m_pressure_dim + src.m_energy_dim)
579  * pow(m_length_factor, -src.m_length_dim - src.m_pressure_dim + 2*src.m_energy_dim)
580  * pow(m_time_factor, -src.m_time_dim - 2*src.m_pressure_dim - 2*src.m_energy_dim)
581  * pow(m_quantity_factor, -src.m_quantity_dim)
582  * pow(m_pressure_factor, -src.m_pressure_dim)
583  * pow(m_energy_factor, -src.m_energy_dim);
584 }
585 
586 static pair<double, string> split_unit(const AnyValue& v) {
587  if (v.is<string>()) {
588  // Should be a value and units, separated by a space, for example '2e4 J/kmol'
589  string val_units = v.asString();
590  size_t space = val_units.find(" ");
591  if (space == npos) {
592  throw CanteraError("split_unit (UnitSystem)",
593  "Couldn't parse '{}' as a space-separated value/unit pair\n",
594  val_units);
595  }
596  return {fpValueCheck(val_units.substr(0, space)),
597  val_units.substr(space+1)};
598  } else {
599  // Just a value
600  return {v.asDouble(), ""};
601  }
602 }
603 
604 double UnitSystem::convert(const AnyValue& v, const string& dest) const
605 {
606  try {
607  return convert(v, Units(dest));
608  } catch (InputFileError&) {
609  throw; // already have input file context from convert(AnyValue, Units)
610  } catch (CanteraError& err) {
611  throw InputFileError("UnitSystem::convert", v, err.getMessage());
612  }
613 }
614 
615 double UnitSystem::convert(const AnyValue& v, const Units& dest) const
616 {
617  try {
618  auto [value, units] = split_unit(v);
619  if (units.empty()) {
620  // Just a value, so convert using default units
621  return convertTo(value, dest);
622  } else {
623  // Both source and destination units are explicit
624  return convert(value, Units(units), dest);
625  }
626  } catch (CanteraError& err) {
627  throw InputFileError("UnitSystem::convert", v, err.getMessage());
628  }
629 }
630 
631 double UnitSystem::convertRateCoeff(const AnyValue& v, const Units& dest) const
632 {
633  if (dest.factor() != 0) {
634  // If the destination units are defined, no special handling is required
635  return convert(v, dest);
636  }
637 
638  auto [value, units] = split_unit(v);
639  if (units.empty()) {
640  if (m_length_factor == 1.0 && m_quantity_factor == 1.0) {
641  // Input is a number in the default mks+kmol system, so no conversion is
642  // required
643  return value;
644  }
645  } else {
646  Units sourceUnits(units);
647  if (fabs(sourceUnits.factor() - 1.0) < 1e-14) {
648  // Input is explicitly in the mks+kmol system, so no conversion is required
649  return value;
650  }
651  }
652  throw InputFileError("UnitSystem::convertRateCoeff", v,
653  "Unable to convert value with non-default units to undefined units,\n"
654  "likely while creating a standalone ReactionRate object.");
655 }
656 
657 vector<double> UnitSystem::convert(const vector<AnyValue>& vals,
658  const string& dest) const
659 {
660  return convert(vals, Units(dest));
661 }
662 
663 vector<double> UnitSystem::convert(const vector<AnyValue>& vals,
664  const Units& dest) const
665 {
666  vector<double> out;
667  for (const auto& val : vals) {
668  out.emplace_back(convert(val, dest));
669  }
670  return out;
671 }
672 
673 double UnitSystem::convertActivationEnergy(double value, const string& src,
674  const string& dest) const
675 {
676  // Convert to J/kmol
677  Units usrc(src);
678  if (usrc.convertible(Units("J/kmol"))) {
679  value *= usrc.factor();
680  } else if (usrc.convertible(Units("K"))) {
681  value *= GasConstant * usrc.factor();
682  } else if (usrc.convertible(Units("eV"))) {
683  value *= Avogadro * usrc.factor();
684  } else {
685  throw CanteraError("UnitSystem::convertActivationEnergy",
686  "Don't understand units '{}' as an activation energy", src);
687  }
688 
689  // Convert from J/kmol
690  Units udest(dest);
691  if (udest.convertible(Units("J/kmol"))) {
692  value /= udest.factor();
693  } else if (udest.convertible(Units("K"))) {
694  value /= GasConstant * udest.factor();
695  } else if (udest.convertible(Units("eV"))) {
696  value /= Avogadro * udest.factor();
697  } else {
698  throw CanteraError("UnitSystem::convertActivationEnergy",
699  "Don't understand units '{}' as an activation energy", dest);
700  }
701 
702  return value;
703 }
704 
705 double UnitSystem::convertActivationEnergyTo(double value, const string& dest) const
706 {
707  return convertActivationEnergyTo(value, Units(dest));
708 }
709 
710 double UnitSystem::convertActivationEnergyTo(double value,
711  const Units& dest) const
712 {
713  if (dest.convertible(Units("J/kmol"))) {
714  return value * m_activation_energy_factor / dest.factor();
715  } else if (dest.convertible(knownUnits.at("K"))) {
716  return value * m_activation_energy_factor / GasConstant;
717  } else if (dest.convertible(knownUnits.at("eV"))) {
718  return value * m_activation_energy_factor / (Avogadro * dest.factor());
719  } else {
720  throw CanteraError("UnitSystem::convertActivationEnergyTo",
721  "'{}' is not a unit of activation energy", dest.str());
722  }
723 }
724 
725 double UnitSystem::convertActivationEnergyFrom(double value, const string& src) const
726 {
727  Units usrc(src);
728  if (usrc.convertible(Units("J/kmol"))) {
729  return value * usrc.factor() / m_activation_energy_factor;
730  } else if (usrc.convertible(knownUnits.at("K"))) {
731  return value * GasConstant / m_activation_energy_factor;
732  } else if (usrc.convertible(knownUnits.at("eV"))) {
733  return value * Avogadro * usrc.factor() / m_activation_energy_factor;
734  } else {
735  throw CanteraError("UnitSystem::convertActivationEnergyFrom",
736  "'{}' is not a unit of activation energy", src);
737  }
738 }
739 
740 double UnitSystem::convertActivationEnergy(const AnyValue& v, const string& dest) const
741 {
742  try {
743  auto [value, units] = split_unit(v);
744  if (units.empty()) {
745  // Just a value, so convert using default units
746  return convertActivationEnergyTo(value, dest);
747  } else {
748  // Both source and destination units are explicit
749  return convertActivationEnergy(value, units, dest);
750  }
751  } catch (CanteraError& err) {
752  throw InputFileError(
753  "UnitSystem::convertActivationEnergy", v, err.getMessage());
754  }
755 }
756 
758 {
759  AnyMap delta;
760  // Create a local alias because the template specialization can't be deduced
761  // automatically
762  const auto& get = getValue<string, string>;
763  if (m_mass_factor != other.m_mass_factor) {
764  delta["mass"] = get(m_defaults, "mass", "kg");
765  }
766  if (m_length_factor != other.m_length_factor) {
767  delta["length"] = get(m_defaults, "length", "m");
768  }
769  if (m_time_factor != other.m_time_factor) {
770  delta["time"] = get(m_defaults, "time", "s");
771  }
772  if (m_pressure_factor != other.m_pressure_factor) {
773  delta["pressure"] = get(m_defaults, "pressure", "Pa");
774  }
775  if (m_energy_factor != other.m_energy_factor) {
776  delta["energy"] = get(m_defaults, "energy", "J");
777  }
778  if (m_quantity_factor != other.m_quantity_factor) {
779  delta["quantity"] = get(m_defaults, "quantity", "kmol");
780  }
784  {
785  delta["activation-energy"] = get(m_defaults, "activation-energy", "J/kmol");
786  }
787  return delta;
788 }
789 
790 }
Header for unit conversion utilities, which are used to translate user input from input files (See In...
A map of string keys to values whose type can vary at runtime.
Definition: AnyMap.h:427
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
double & asDouble()
Return the held value as a double, if it is a double or a long int.
Definition: AnyMap.cpp:824
bool is() const
Returns true if the held value is of the specified type.
Definition: AnyMap.inl.h:68
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:66
virtual string getMessage() const
Method overridden by derived classes to format the error message.
Error thrown for problems processing information contained in an AnyMap or AnyValue.
Definition: AnyMap.h:738
Unit conversion utility.
Definition: Units.h:169
double m_activation_energy_factor
Factor to convert activation energy from this unit system to J/kmol.
Definition: Units.h:283
bool m_explicit_activation_energy
True if activation energy units are set explicitly, rather than as a combination of energy and quanti...
Definition: Units.h:290
double convertFrom(double value, const string &src) const
Convert value from the specified src units to units appropriate for this unit system (defined by setD...
Definition: Units.cpp:570
double convertActivationEnergyTo(double value, const string &dest) const
Convert value to the units specified by dest from the default activation energy units.
Definition: Units.cpp:705
double m_time_factor
Factor to convert time from this unit system to seconds.
Definition: Units.h:274
double convertTo(double value, const string &dest) const
Convert value to the specified dest units from the appropriate units for this unit system (defined by...
Definition: Units.cpp:554
double m_pressure_factor
Factor to convert pressure from this unit system to Pa.
Definition: Units.h:277
UnitSystem()
Default constructor for unit system (needed as VS2019 does not recognize an optional argument with a ...
Definition: Units.h:176
double m_energy_factor
Factor to convert energy from this unit system to J.
Definition: Units.h:280
double convertRateCoeff(const AnyValue &val, const Units &dest) const
Convert a generic AnyValue node representing a reaction rate coefficient to the units specified in de...
Definition: Units.cpp:631
double m_length_factor
Factor to convert length from this unit system to meters.
Definition: Units.h:271
double convertActivationEnergyFrom(double value, const string &src) const
Convert value from the units specified by src to the default activation energy units.
Definition: Units.cpp:725
map< string, string > m_defaults
Map of dimensions (mass, length, etc.) to names of specified default units.
Definition: Units.h:294
double convertActivationEnergy(double value, const string &src, const string &dest) const
Convert value from the units of src to the units of dest, allowing for the different dimensions that ...
Definition: Units.cpp:673
void setDefaults(std::initializer_list< string > units)
Set the default units to convert from when explicit units are not provided.
Definition: Units.cpp:428
double m_mass_factor
Factor to convert mass from this unit system to kg.
Definition: Units.h:268
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
double m_quantity_factor
Factor to convert quantity from this unit system to kmol.
Definition: Units.h:286
AnyMap getDelta(const UnitSystem &other) const
Get the changes to the defaults from other to this UnitSystem.
Definition: Units.cpp:757
map< string, string > defaults() const
Return default units used by the unit system.
Definition: Units.cpp:398
void setDefaultActivationEnergy(const string &e_units)
Set the default units to convert from when using the convertActivationEnergy function.
Definition: Units.cpp:521
A representation of the units associated with a dimensional quantity.
Definition: Units.h:35
double m_energy_dim
pseudo-dimension to track explicit energy units
Definition: Units.h:85
double m_pressure_dim
pseudo-dimension to track explicit pressure units
Definition: Units.h:84
void scale(double k)
Scale the unit by the factor k
Definition: Units.h:75
double m_factor
conversion factor to Cantera base units
Definition: Units.h:77
Units pow(double exponent) const
Raise these Units to a power, changing both the conversion factor and the dimensions of these Units.
Definition: Units.cpp:223
Units & operator*=(const Units &other)
Multiply two Units objects, combining their conversion factors and dimensions.
Definition: Units.cpp:209
string str(bool skip_unity=true) const
Provide a string representation of these Units.
Definition: Units.cpp:234
double dimension(const string &primary) const
Return dimension of primary unit component ("mass", "length", "time", "temperature",...
Definition: Units.cpp:303
bool convertible(const Units &other) const
Returns true if the specified Units are dimensionally consistent.
Definition: Units.cpp:199
double factor() const
Return the factor for converting from this unit to Cantera's base units.
Definition: Units.h:53
Units(double factor=1.0, double mass=0, double length=0, double time=0, double temperature=0, double current=0, double quantity=0)
Create a Units object with the specified dimensions.
Definition: Units.cpp:109
Definitions for the classes that are thrown when Cantera experiences an error condition (also contain...
This file contains definitions for utility functions and text for modules, inputfiles and logging,...
string trimCopy(const string &input)
Trim.
double fpValueCheck(const string &val)
Translate a string into one double value, with error checking.
const double Avogadro
Avogadro's Number [number/kmol].
Definition: ct_defs.h:81
const double OneAtm
One atmosphere [Pa].
Definition: ct_defs.h:96
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition: ct_defs.h:120
const double ElectronCharge
Elementary charge [C].
Definition: ct_defs.h:90
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
const double SmallNumber
smallest number to compare to zero.
Definition: ct_defs.h:158
Contains declarations for string manipulation functions within Cantera.
double standardExponent() const
Effective exponent of standard unit.
Definition: Units.cpp:344
void update(const Units &units, double exponent)
Update exponent of item with matching units; if it does not exist, add unit-exponent pair at end of s...
Definition: Units.cpp:362
Units standardUnits() const
Get standard unit used by UnitStack.
Definition: Units.cpp:323
Units product() const
Calculate product of units-exponent stack.
Definition: Units.cpp:377
void join(double exponent)
Join (update) exponent of standard units, where the updated exponent is the sum of the pre-existing e...
Definition: Units.cpp:352
vector< pair< Units, double > > stack
Stack uses vector of pairs.
Definition: Units.h:140
void setStandardUnits(Units &standardUnits)
Set standard units.
Definition: Units.cpp:331
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...