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