Cantera  3.1.0
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 {"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
83const 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
106namespace Cantera
107{
108
109Units::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
131Units::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
199bool 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
223Units 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
234string 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
290bool 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
300 && m_energy_dim == other.m_energy_dim;
301}
302
303double 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
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
352void 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
362void 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
393UnitSystem::UnitSystem(std::initializer_list<string> units)
394{
395 setDefaults(units);
396}
397
398map<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
428void 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
472void 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
521void 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
538double UnitSystem::convert(double value, const string& src, const string& dest) const
539{
540 return convert(value, Units(src), Units(dest));
541}
542
543double 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
554double UnitSystem::convertTo(double value, const string& dest) const
555{
556 return convertTo(value, Units(dest));
557}
558
559double 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)
567 * pow(m_energy_factor, dest.m_energy_dim);
568}
569
570double UnitSystem::convertFrom(double value, const string& dest) const
571{
572 return convertFrom(value, Units(dest));
573}
574
575double 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)
583 * pow(m_energy_factor, -src.m_energy_dim);
584}
585
586static 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
604double 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
615double 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
631double 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
657vector<double> UnitSystem::convert(const vector<AnyValue>& vals,
658 const string& dest) const
659{
660 return convert(vals, Units(dest));
661}
662
663vector<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
673double 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
705double UnitSystem::convertActivationEnergyTo(double value, const string& dest) const
706{
707 return convertActivationEnergyTo(value, Units(dest));
708}
709
710double 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"))) {
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
725double 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"))) {
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
740double 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 }
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 }
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:431
A wrapper for a variable whose type is determined at runtime.
Definition AnyMap.h:87
const string & asString() const
Return the held value, if it is a string.
Definition AnyMap.cpp:782
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: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:595
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...