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