17const map<string, Units> knownUnits{
22 {
"kg",
Units(1.0, 1, 0, 0)},
23 {
"g",
Units(1e-3, 1, 0, 0)},
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)},
32 {
"s",
Units(1.0, 0, 0, 1)},
33 {
"min",
Units(60, 0, 0, 1)},
34 {
"hr",
Units(3600, 0, 0, 1)},
37 {
"K",
Units(1.0, 0, 0, 0, 1)},
38 {
"C",
Units(1.0, 0, 0, 0, 1)},
41 {
"A",
Units(1.0, 0, 0, 0, 0, 1)},
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)},
52 {
"J",
Units(1.0, 1, 2, -2)},
53 {
"cal",
Units(4.184, 1, 2, -2)},
54 {
"erg",
Units(1e-7, 1, 2, -2)},
58 {
"N",
Units(1.0, 1, 1, -2)},
59 {
"dyn",
Units(1e-5, 1, 1, -2)},
62 {
"Pa",
Units(1.0, 1, -1, -2)},
64 {
"bar",
Units(1.0e5, 1, -1, -2)},
65 {
"dyn/cm^2",
Units(0.1, 1, -1, -2)},
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)},
76 {
"ohm",
Units(1.0, 1, 2, -3, 0, -2)},
77 {
"V",
Units(1.0, 1, 2, -3, 0, -1)},
78 {
"coulomb",
Units(1.0, 0, 0, 1, 0, 1)},
79 {
"townsend",
Units(1.0e-21, 1, 4, -3, 0, -1)},
80 {
"Td",
Units(1.0e-21, 1, 4, -3, 0, -1)},
83 {
"J/kmol",
Units(1.0, 1, 2, -2, 0, 0, -1)},
86const map<string, double> prefixes{
113 double temperature,
double current,
double quantity)
116 , m_length_dim(length)
118 , m_temperature_dim(temperature)
119 , m_current_dim(current)
120 , m_quantity_dim(quantity)
122 if (mass != 0 && length == -mass && time == -2 * mass
123 && temperature == 0 && current == 0 && quantity == 0) {
126 }
else if (mass != 0 && length == 2 * mass && time == -2 * mass
127 && temperature == 0 && current == 0 && quantity == 0)
139 static std::regex regexp(
"[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)");
141 std::regex_search(name, matched, regexp);
142 if (matched.size()) {
143 string factor = *matched.begin();
144 if (name.find(
factor) == 0) {
152 size_t stop = name.find_first_of(
"*/", start);
153 size_t caret = name.find(
'^', start);
159 name.substr(start, std::min(caret, stop) - start));
161 double exponent = 1.0;
163 exponent =
fpValueCheck(name.substr(caret+1, stop-caret-1));
165 if (start != 0 && name[start-1] ==
'/') {
167 exponent = -exponent;
170 if (knownUnits.find(unit) != knownUnits.end()) {
172 *
this *= knownUnits.at(unit).pow(exponent);
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);
184 "Unknown unit '{}' in unit string '{}'", unit, name);
196 "Detected non-unity conversion factor:\n"
197 "input '{}' is equivalent to '{}' where the conversion factor is '{}'",
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);
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;
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);
239 map<string, double> dims{
243 {
"K", m_temperature_dim},
244 {
"A", m_current_dim},
245 {
"kmol", m_quantity_dim},
250 for (
auto const& [
dimension, exponent] : dims) {
251 int rounded = (int)round(exponent);
252 if (exponent == 0.) {
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));
265 den.append(fmt::format(
" / {}^{}",
dimension, -exponent));
271 return fmt::format(
"{}{}", num.substr(3), den);
274 return fmt::format(
"1{}", den);
287 return fmt::format(
"{} {}{}",
factor, num.substr(3), den);
290 return fmt::format(
"{}{}",
factor, den);
293bool Units::operator==(
const Units& other)
const
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
308 if (primary ==
"mass") {
310 }
else if (primary ==
"length") {
312 }
else if (primary ==
"time") {
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;
322 "Unknown primary unit '{}'.", primary);
331 return stack[0].first;
340 if (
stack[0].second != 0.) {
342 "Standard unit is already defined.");
352 return stack[0].second;
359 "Standard unit is not defined.");
361 stack[0].second += exponent;
368 for (
auto& [current_unit, current_exp] :
stack) {
369 if (current_unit == units) {
370 current_exp += exponent;
376 stack.emplace_back(units, exponent);
386 for (
auto& [units, exponent] :
stack) {
390 out *= units.
pow(exponent);
404 map<string, string> units{
408 {
"quantity",
"kmol"},
411 {
"temperature",
"K"},
413 {
"activation-energy",
"J / kmol"},
417 for (
const auto& [dimension, default_unit] :
m_defaults) {
418 units[dimension] = default_unit;
424 units[
"activation-energy"] = fmt::format(
425 "{} / {}", units[
"energy"], units[
"quantity"]);
433 for (
const auto& name : units) {
434 auto unit =
Units(name);
435 if (unit.convertible(knownUnits.at(
"kg"))) {
438 }
else if (unit.convertible(knownUnits.at(
"m"))) {
441 }
else if (unit.convertible(knownUnits.at(
"s"))) {
444 }
else if (unit.convertible(knownUnits.at(
"kmol"))) {
447 }
else if (unit.convertible(knownUnits.at(
"Pa"))) {
450 }
else if (unit.convertible(knownUnits.at(
"J"))) {
453 }
else if (unit.convertible(knownUnits.at(
"K"))) {
455 if (unit.factor() != 1.) {
456 throw CanteraError(
"UnitSystem::setDefaults",
"Temperature scales "
457 "with non-unity conversion factor from Kelvin are not supported.");
459 }
else if (unit.convertible(knownUnits.at(
"A"))) {
461 if (unit.factor() != 1.) {
462 throw CanteraError(
"UnitSystem::setDefaults",
"Current scales "
463 "with non-unity conversion factor from Ampere are not supported.");
467 "Unable to match unit '{}' to a basic dimension", name);
477 for (
const auto& [dimension, name] : units) {
479 if (dimension ==
"mass" && unit.
convertible(knownUnits.at(
"kg"))) {
482 }
else if (dimension ==
"length" && unit.
convertible(knownUnits.at(
"m"))) {
485 }
else if (dimension ==
"time" && unit.
convertible(knownUnits.at(
"s"))) {
488 }
else if (dimension ==
"temperature" && unit.
convertible(knownUnits.at(
"K"))) {
490 if (unit.
factor() != 1.) {
491 throw CanteraError(
"UnitSystem::setDefaults",
"Temperature scales "
492 "with non-unity conversion factor from Kelvin are not supported.");
494 }
else if (dimension ==
"current" && unit.
convertible(knownUnits.at(
"A"))) {
496 if (unit.
factor() != 1.) {
497 throw CanteraError(
"UnitSystem::setDefaults",
"Current scales "
498 "with non-unity conversion factor from Ampere are not supported.");
500 }
else if (dimension ==
"quantity" && unit.
convertible(knownUnits.at(
"kmol"))) {
503 }
else if (dimension ==
"pressure" && unit.
convertible(knownUnits.at(
"Pa"))) {
506 }
else if (dimension ==
"energy" && unit.
convertible(knownUnits.at(
"J"))) {
509 }
else if (dimension ==
"activation-energy") {
513 "Unable to set default unit for '{}' to '{}' ({}).",
514 dimension, name, unit.
str());
517 if (units.find(
"activation-energy") != units.end()) {
536 "Unable to match unit '{}' to a unit of activation energy", e_units);
547 const Units& dest)
const
551 "Incompatible units:\n Units({}) and\n Units({})",
564 return value / dest.
factor()
580 return value * src.
factor()
589static pair<double, string> split_unit(
const AnyValue& v) {
590 if (v.
is<
string>()) {
593 size_t space = val_units.find(
" ");
596 "Couldn't parse '{}' as a space-separated value/unit pair\n",
600 val_units.substr(space+1)};
621 auto [value, units] = split_unit(v);
641 auto [value, units] = split_unit(v);
649 Units sourceUnits(units);
650 if (fabs(sourceUnits.
factor() - 1.0) < 1e-14) {
656 "Unable to convert value with non-default units to undefined units,\n"
657 "likely while creating a standalone ReactionRate object.");
661 const string& dest)
const
667 const Units& dest)
const
670 for (
const auto& val : vals) {
671 out.emplace_back(
convert(val, dest));
677 const string& dest)
const
688 throw CanteraError(
"UnitSystem::convertActivationEnergy",
689 "Don't understand units '{}' as an activation energy", src);
701 throw CanteraError(
"UnitSystem::convertActivationEnergy",
702 "Don't understand units '{}' as an activation energy", dest);
714 const Units& dest)
const
720 }
else if (dest.
convertible(knownUnits.at(
"eV"))) {
723 throw CanteraError(
"UnitSystem::convertActivationEnergyTo",
724 "'{}' is not a unit of activation energy", dest.
str());
735 }
else if (usrc.
convertible(knownUnits.at(
"eV"))) {
738 throw CanteraError(
"UnitSystem::convertActivationEnergyFrom",
739 "'{}' is not a unit of activation energy", src);
746 auto [value, units] = split_unit(v);
756 "UnitSystem::convertActivationEnergy", v, err.
getMessage());
765 const auto& get = getValue<string, string>;
767 delta[
"mass"] = get(
m_defaults,
"mass",
"kg");
770 delta[
"length"] = get(
m_defaults,
"length",
"m");
776 delta[
"pressure"] = get(
m_defaults,
"pressure",
"Pa");
779 delta[
"energy"] = get(
m_defaults,
"energy",
"J");
782 delta[
"quantity"] = get(
m_defaults,
"quantity",
"kmol");
788 delta[
"activation-energy"] = get(
m_defaults,
"activation-energy",
"J/kmol");
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.
A wrapper for a variable whose type is determined at runtime.
const string & asString() const
Return the held value, if it is a string.
double & asDouble()
Return the held value as a double, if it is a double or a long int.
bool is() const
Returns true if the held value is of the specified type.
Base class for exceptions thrown by Cantera classes.
virtual string getMessage() const
Method overridden by derived classes to format the error message.
double m_activation_energy_factor
Factor to convert activation energy from this unit system to J/kmol.
bool m_explicit_activation_energy
True if activation energy units are set explicitly, rather than as a combination of energy and quanti...
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...
double convertActivationEnergyTo(double value, const string &dest) const
Convert value to the units specified by dest from the default activation energy units.
double m_time_factor
Factor to convert time from this unit system to seconds.
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...
double m_pressure_factor
Factor to convert pressure from this unit system to Pa.
UnitSystem()
Default constructor for unit system (needed as VS2019 does not recognize an optional argument with a ...
double m_energy_factor
Factor to convert energy from this unit system to J.
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...
double m_length_factor
Factor to convert length from this unit system to meters.
double convertActivationEnergyFrom(double value, const string &src) const
Convert value from the units specified by src to the default activation energy units.
map< string, string > m_defaults
Map of dimensions (mass, length, etc.) to names of specified default units.
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 ...
void setDefaults(std::initializer_list< string > units)
Set the default units to convert from when explicit units are not provided.
double m_mass_factor
Factor to convert mass from this unit system to kg.
double convert(double value, const string &src, const string &dest) const
Convert value from the units of src to the units of dest.
double m_quantity_factor
Factor to convert quantity from this unit system to kmol.
AnyMap getDelta(const UnitSystem &other) const
Get the changes to the defaults from other to this UnitSystem.
map< string, string > defaults() const
Return default units used by the unit system.
void setDefaultActivationEnergy(const string &e_units)
Set the default units to convert from when using the convertActivationEnergy function.
A representation of the units associated with a dimensional quantity.
double m_energy_dim
pseudo-dimension to track explicit energy units
double m_pressure_dim
pseudo-dimension to track explicit pressure units
void scale(double k)
Scale the unit by the factor k
double m_factor
conversion factor to Cantera base units
Units pow(double exponent) const
Raise these Units to a power, changing both the conversion factor and the dimensions of these Units.
Units & operator*=(const Units &other)
Multiply two Units objects, combining their conversion factors and dimensions.
string str(bool skip_unity=true) const
Provide a string representation of these Units.
double dimension(const string &primary) const
Return dimension of primary unit component ("mass", "length", "time", "temperature",...
bool convertible(const Units &other) const
Returns true if the specified Units are dimensionally consistent.
double factor() const
Return the factor for converting from this unit to Cantera's base units.
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.
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].
const double OneAtm
One atmosphere [Pa].
const double GasConstant
Universal Gas Constant [J/kmol/K].
const double ElectronCharge
Elementary charge [C].
Namespace for the Cantera kernel.
const size_t npos
index returned by functions to indicate "no position"
const double SmallNumber
smallest number to compare to zero.
Contains declarations for string manipulation functions within Cantera.
double standardExponent() const
Effective exponent of standard unit.
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...
Units standardUnits() const
Get standard unit used by UnitStack.
Units product() const
Calculate product of units-exponent stack.
void join(double exponent)
Join (update) exponent of standard units, where the updated exponent is the sum of the pre-existing e...
vector< pair< Units, double > > stack
Stack uses vector of pairs.
void setStandardUnits(Units &standardUnits)
Set standard units.
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...