17const std::map<std::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 {
"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)},
75 {
"ohm",
Units(1.0, 1, 2, -3, 0, -2)},
76 {
"V",
Units(1.0, 1, 2, -3, 0, -1)},
77 {
"coulomb",
Units(1.0, 0, 0, 1, 0, 1)},
80 {
"J/kmol",
Units(1.0, 1, 2, -2, 0, 0, -1)},
83const std::map<std::string, double> prefixes{
110 double temperature,
double current,
double quantity)
113 , m_length_dim(length)
115 , m_temperature_dim(temperature)
116 , m_current_dim(current)
117 , m_quantity_dim(quantity)
121 if (mass != 0 && length == -mass && time == -2 * mass
122 && temperature == 0 && current == 0 && quantity == 0) {
125 }
else if (mass != 0 && length == 2 * mass && time == -2 * mass
126 && temperature == 0 && current == 0 && quantity == 0)
138 , m_temperature_dim(0)
147 static std::regex regexp(
"[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)");
149 std::regex_search(name, matched, regexp);
150 if (matched.size()) {
151 std::string
factor = *matched.begin();
152 if (name.find(
factor) == 0) {
160 size_t stop = name.find_first_of(
"*/", start);
161 size_t caret = name.find(
'^', start);
167 name.substr(start, std::min(caret, stop) - start));
169 double exponent = 1.0;
171 exponent =
fpValueCheck(name.substr(caret+1, stop-caret-1));
173 if (start != 0 && name[start-1] ==
'/') {
175 exponent = -exponent;
178 if (knownUnits.find(unit) != knownUnits.end()) {
180 *
this *= knownUnits.at(unit).pow(exponent);
183 std::string prefix = unit.substr(0, 1);
184 std::string suffix = unit.substr(1);
185 if (prefixes.find(prefix) != prefixes.end() &&
186 knownUnits.find(suffix) != knownUnits.end()) {
187 Units u = knownUnits.at(suffix);
188 u.
scale(prefixes.at(prefix));
189 *
this *= u.
pow(exponent);
192 "Unknown unit '{}' in unit string '{}'", unit, name);
204 "Detected non-unity conversion factor:\n"
205 "input '{}' is equivalent to '{}' where the conversion factor is '{}'",
212 return (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);
223 m_mass_dim += other.m_mass_dim;
224 m_length_dim += other.m_length_dim;
225 m_time_dim += other.m_time_dim;
226 m_temperature_dim += other.m_temperature_dim;
227 m_current_dim += other.m_current_dim;
228 m_quantity_dim += other.m_quantity_dim;
237 m_mass_dim * exponent,
238 m_length_dim * exponent,
239 m_time_dim * exponent,
240 m_temperature_dim * exponent,
241 m_current_dim * exponent,
242 m_quantity_dim * exponent);
247 std::map<std::string, double> dims{
251 {
"K", m_temperature_dim},
252 {
"A", m_current_dim},
253 {
"kmol", m_quantity_dim},
256 std::string num =
"";
257 std::string den =
"";
258 for (
auto const& dim : dims) {
259 int rounded = (int)round(dim.second);
260 if (dim.second == 0.) {
262 }
else if (dim.second == 1.) {
263 num.append(fmt::format(
" * {}", dim.first));
264 }
else if (dim.second == -1.) {
265 den.append(fmt::format(
" / {}", dim.first));
266 }
else if (dim.second == rounded && rounded > 0) {
267 num.append(fmt::format(
" * {}^{}", dim.first, rounded));
268 }
else if (dim.second == rounded) {
269 den.append(fmt::format(
" / {}^{}", dim.first, -rounded));
270 }
else if (dim.second > 0) {
271 num.append(fmt::format(
" * {}^{}", dim.first, dim.second));
273 den.append(fmt::format(
" / {}^{}", dim.first, -dim.second));
279 return fmt::format(
"{}{}", num.substr(3), den);
282 return fmt::format(
"1{}", den);
295 return fmt::format(
"{} {}{}",
factor, num.substr(3), den);
298 return fmt::format(
"{}{}",
factor, den);
301bool Units::operator==(
const Units& other)
const
304 && m_mass_dim == other.m_mass_dim
305 && m_length_dim == other.m_length_dim
306 && m_time_dim == other.m_time_dim
307 && m_temperature_dim == other.m_temperature_dim
308 && m_current_dim == other.m_current_dim
309 && m_quantity_dim == other.m_quantity_dim
316 if (primary ==
"mass") {
318 }
else if (primary ==
"length") {
320 }
else if (primary ==
"time") {
322 }
else if (primary ==
"temperature") {
323 return m_temperature_dim;
324 }
else if (primary ==
"current") {
325 return m_current_dim;
326 }
else if (primary ==
"quantity") {
327 return m_quantity_dim;
330 "Unknown primary unit '{}'.", primary);
339 return stack[0].first;
348 if (
stack[0].second != 0.) {
350 "Standard unit is already defined.");
360 return stack[0].second;
367 "Standard unit is not defined.");
369 stack[0].second += exponent;
376 for (
auto& item :
stack) {
377 if (item.first == units) {
378 item.second += exponent;
384 stack.emplace_back(units, exponent);
394 for (
auto& item :
stack) {
395 if (item.second == 1) {
398 out *= item.first.
pow(item.second);
406 , m_length_factor(1.0)
408 , m_pressure_factor(1.0)
409 , m_energy_factor(1.0)
410 , m_activation_energy_factor(1.0)
411 , m_quantity_factor(1.0)
412 , m_explicit_activation_energy(false)
420 std::map<std::string, std::string> units{
424 {
"quantity",
"kmol"},
427 {
"temperature",
"K"},
429 {
"activation-energy",
"J / kmol"},
440 units[
"activation-energy"] = fmt::format(
441 "{} / {}", units[
"energy"], units[
"quantity"]);
449 for (
const auto& name : units) {
450 auto unit =
Units(name);
451 if (unit.convertible(knownUnits.at(
"kg"))) {
454 }
else if (unit.convertible(knownUnits.at(
"m"))) {
457 }
else if (unit.convertible(knownUnits.at(
"s"))) {
460 }
else if (unit.convertible(knownUnits.at(
"kmol"))) {
463 }
else if (unit.convertible(knownUnits.at(
"Pa"))) {
466 }
else if (unit.convertible(knownUnits.at(
"J"))) {
469 }
else if (unit.convertible(knownUnits.at(
"K"))) {
471 if (unit.factor() != 1.) {
472 throw CanteraError(
"UnitSystem::setDefaults",
"Temperature scales "
473 "with non-unity conversion factor from Kelvin are not supported.");
475 }
else if (unit.convertible(knownUnits.at(
"A"))) {
477 if (unit.factor() != 1.) {
478 throw CanteraError(
"UnitSystem::setDefaults",
"Current scales "
479 "with non-unity conversion factor from Ampere are not supported.");
483 "Unable to match unit '{}' to a basic dimension", name);
493 for (
const auto& item : units) {
494 auto& name = item.first;
495 Units unit(item.second);
496 if (name ==
"mass" && unit.
convertible(knownUnits.at(
"kg"))) {
499 }
else if (name ==
"length" && unit.
convertible(knownUnits.at(
"m"))) {
502 }
else if (name ==
"time" && unit.
convertible(knownUnits.at(
"s"))) {
505 }
else if (name ==
"temperature" && unit.
convertible(knownUnits.at(
"K"))) {
507 if (unit.
factor() != 1.) {
508 throw CanteraError(
"UnitSystem::setDefaults",
"Temperature scales "
509 "with non-unity conversion factor from Kelvin are not supported.");
511 }
else if (name ==
"current" && unit.
convertible(knownUnits.at(
"A"))) {
513 if (unit.
factor() != 1.) {
514 throw CanteraError(
"UnitSystem::setDefaults",
"Current scales "
515 "with non-unity conversion factor from Ampere are not supported.");
517 }
else if (name ==
"quantity" && unit.
convertible(knownUnits.at(
"kmol"))) {
520 }
else if (name ==
"pressure" && unit.
convertible(knownUnits.at(
"Pa"))) {
523 }
else if (name ==
"energy" && unit.
convertible(knownUnits.at(
"J"))) {
526 }
else if (name ==
"activation-energy") {
530 "Unable to set default unit for '{}' to '{}' ({}).",
531 name, item.second, unit.
str());
534 if (units.find(
"activation-energy") != units.end()) {
553 "Unable to match unit '{}' to a unit of activation energy", e_units);
559 const std::string& dest)
const
565 const Units& dest)
const
569 "Incompatible units:\n Units({}) and\n Units({})",
582 return value / dest.
factor()
598 return value * src.
factor()
607static std::pair<double, std::string> split_unit(
const AnyValue& v) {
608 if (v.
is<std::string>()) {
610 std::string val_units = v.
asString();
611 size_t space = val_units.find(
" ");
614 "Couldn't parse '{}' as a space-separated value/unit pair\n",
618 val_units.substr(space+1)};
632 auto val_units = split_unit(v);
633 if (val_units.second.empty()) {
638 return convert(val_units.first,
Units(val_units.second), dest);
643 const std::string& dest)
const
649 const Units& dest)
const
652 for (
const auto& val : vals) {
653 out.emplace_back(
convert(val, dest));
659 const std::string& dest)
const
670 throw CanteraError(
"UnitSystem::convertActivationEnergy",
671 "Don't understand units '{}' as an activation energy", src);
683 throw CanteraError(
"UnitSystem::convertActivationEnergy",
684 "Don't understand units '{}' as an activation energy", dest);
691 const std::string& dest)
const
697 const Units& dest)
const
703 }
else if (dest.
convertible(knownUnits.at(
"eV"))) {
706 throw CanteraError(
"UnitSystem::convertActivationEnergyTo",
707 "'{}' is not a unit of activation energy", dest.
str());
712 const std::string& src)
const
719 }
else if (usrc.
convertible(knownUnits.at(
"eV"))) {
722 throw CanteraError(
"UnitSystem::convertActivationEnergyFrom",
723 "'{}' is not a unit of activation energy", src);
728 const std::string& dest)
const
730 auto val_units = split_unit(v);
731 if (val_units.second.empty()) {
745 const auto& get = getValue<std::string, std::string>;
747 delta[
"mass"] = get(
m_defaults,
"mass",
"kg");
750 delta[
"length"] = get(
m_defaults,
"length",
"m");
756 delta[
"pressure"] = get(
m_defaults,
"pressure",
"Pa");
759 delta[
"energy"] = get(
m_defaults,
"energy",
"J");
762 delta[
"quantity"] = get(
m_defaults,
"quantity",
"kmol");
768 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 std::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.
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 m_time_factor
Factor to convert time from this unit system to seconds.
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 m_length_factor
Factor to convert length from this unit system to meters.
double convert(double value, const std::string &src, const std::string &dest) const
Convert value from the units of src to the units of dest.
void setDefaultActivationEnergy(const std::string &e_units)
Set the default units to convert from when using the convertActivationEnergy function.
double convertFrom(double value, const std::string &src) const
Convert value from the specified src units to units appropriate for this unit system (defined by setD...
double convertActivationEnergy(double value, const std::string &src, const std::string &dest) const
Convert value from the units of src to the units of dest, allowing for the different dimensions that ...
std::map< std::string, std::string > defaults() const
Return default units used by the unit system.
double m_mass_factor
Factor to convert mass from this unit system to kg.
double convertTo(double value, const std::string &dest) const
Convert value to the specified dest units from the appropriate units for this unit system (defined by...
double m_quantity_factor
Factor to convert quantity from this unit system to kmol.
double convertActivationEnergyTo(double value, const std::string &dest) const
Convert value to the units specified by dest from the default activation energy units.
void setDefaults(std::initializer_list< std::string > units)
Set the default units to convert from when explicit units are not provided.
double convertActivationEnergyFrom(double value, const std::string &src) const
Convert value from the units specified by src to the default activation energy units.
AnyMap getDelta(const UnitSystem &other) const
Get the changes to the defaults from other to this UnitSystem.
std::map< std::string, std::string > m_defaults
Map of dimensions (mass, length, etc.) to names of specified default units.
A representation of the units associated with a dimensional quantity.
double m_energy_dim
pseudo-dimension to track explicit energy units
std::string str(bool skip_unity=true) const
Provide a string representation of these 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.
bool convertible(const Units &other) const
Returns true if the specified Units are dimensionally consistent.
double dimension(const std::string &primary) const
Return dimension of primary unit component ("mass", "length", "time", "temperature",...
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,...
Namespace for the Cantera kernel.
const size_t npos
index returned by functions to indicate "no position"
const double Avogadro
Avogadro's Number [number/kmol].
const double OneAtm
One atmosphere [Pa].
const double SmallNumber
smallest number to compare to zero.
std::string trimCopy(const std::string &input)
Trim.
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
const double GasConstant
Universal Gas Constant [J/kmol/K].
const double ElectronCharge
Elementary charge [C].
doublereal fpValueCheck(const std::string &val)
Translate a string into one doublereal value, with error checking.
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...
std::vector< std::pair< Units, double > > stack
Stack uses vector of pairs.
void setStandardUnits(Units &standardUnits)
Set standard units.
Various templated functions that carry out common vector operations (see Templated Utility Functions)...