UnitSystem class supports default source units for conversions

This commit is contained in:
Ray Speth 2018-11-18 23:22:01 -05:00
parent c9b7de3b70
commit da097631e3
3 changed files with 110 additions and 0 deletions

View file

@ -63,6 +63,7 @@ private:
double m_temperature_dim;
double m_current_dim;
double m_quantity_dim;
double m_pressure_dim; //!< pseudo-dimension to track explicit pressure units
friend class UnitSystem;
};
@ -70,6 +71,11 @@ private:
//! Unit conversion utility
/*!
* Provides functions for converting dimensional values from a given unit system.
* The main use is for converting values specified in input files to Cantera's
* native unit system, which is SI units except for the use of kmol as the base
* unit of quantity, i.e. kilogram, meter, second, kelvin, ampere, and kmol.
*
* String representations of units can be written using multiplication,
* division, and exponentiation. Spaces are ignored. Positive, negative, and
* decimal exponents are permitted. Examples:
@ -86,10 +92,45 @@ private:
class UnitSystem
{
public:
//! Create a unit system with the specified default units
UnitSystem(std::initializer_list<std::string> units={});
//! Set the default units to convert from when explicit units are not
//! provided. Defaults can be set for mass, length, time, quantity, and
//! pressure. Conversion using the pressure unit is done only when the
//! target units explicitly contain pressure units.
//!
//! * To use SI+kmol: `setDefaults({"kg", "m", "s", "kmol"});`
//! * To use CGS+mol: `setDefaults({"cm", "g", "mol"});`
void setDefaults(std::initializer_list<std::string> units);
//! Convert `value` from the units of `src` to the units of `dest`.
double convert(double value, const std::string& src,
const std::string& dest) const;
double convert(double value, const Units& src, const Units& dest) const;
//! Convert `value` from this unit system (defined by `setDefaults`) to the
//! specified units.
double convert(double value, const std::string& dest) const;
double convert(double value, const Units& dest) const;
private:
//! Factor to convert mass from this unit system to kg
double m_mass_factor;
//! Factor to convert length from this unit system to meters
double m_length_factor;
//! Factor to convert time from this unit system to seconds
double m_time_factor;
//! Factor to convert energy from this unit system to Joules
double m_energy_factor;
//! Factor to convert pressure from this unit system to Pa
double m_pressure_factor;
//! Factor to convert quantity from this unit system to kmol
double m_quantity_factor;
};
}

View file

@ -105,7 +105,13 @@ Units::Units(double factor, double mass, double length, double time,
, m_temperature_dim(temperature)
, m_current_dim(current)
, m_quantity_dim(quantity)
, m_pressure_dim(0)
{
if (mass != 0 && length == -mass && time == -2 * mass
&& temperature == 0 && current == 0 && quantity == 0) {
// Dimension looks like Pa^n
m_pressure_dim = mass;
}
}
Units::Units(const std::string& name)
@ -116,6 +122,7 @@ Units::Units(const std::string& name)
, m_temperature_dim(0)
, m_current_dim(0)
, m_quantity_dim(0)
, m_pressure_dim(0)
{
size_t start = 0;
while (true) {
@ -182,6 +189,7 @@ Units& Units::operator*=(const Units& other)
m_temperature_dim += other.m_temperature_dim;
m_current_dim += other.m_current_dim;
m_quantity_dim += other.m_quantity_dim;
m_pressure_dim += other.m_pressure_dim;
return *this;
}
@ -202,6 +210,41 @@ std::string Units::str() const {
}
UnitSystem::UnitSystem(std::initializer_list<std::string> units)
: m_mass_factor(1.0)
, m_length_factor(1.0)
, m_time_factor(1.0)
, m_energy_factor(1.0)
, m_pressure_factor(1.0)
, m_quantity_factor(1.0)
{
setDefaults(units);
}
void UnitSystem::setDefaults(std::initializer_list<std::string> units)
{
for (const auto& name : units) {
auto unit = Units(name);
if (unit.convertible(knownUnits.at("kg"))) {
m_mass_factor = unit.factor();
} else if (unit.convertible(knownUnits.at("m"))) {
m_length_factor = unit.factor();
} else if (unit.convertible(knownUnits.at("s"))) {
m_time_factor = unit.factor();
} else if (unit.convertible(knownUnits.at("kmol"))) {
m_quantity_factor = unit.factor();
} else if (unit.convertible(knownUnits.at("Pa"))) {
m_pressure_factor = unit.factor();
} else if (unit.convertible(knownUnits.at("K"))
|| unit.convertible(knownUnits.at("A"))) {
// Do nothing -- no other scales are supported for temperature and current
} else {
throw CanteraError("UnitSystem::setDefaults",
"Unable to match unit '{}' to a basic dimension", name);
}
}
}
double UnitSystem::convert(double value, const std::string& src,
const std::string& dest) const
{
@ -218,4 +261,19 @@ double UnitSystem::convert(double value, const Units& src,
return value * src.factor() / dest.factor();
}
double UnitSystem::convert(double value, const std::string& dest) const
{
return convert(value, Units(dest));
}
double UnitSystem::convert(double value, const Units& dest) const
{
return value / dest.factor()
* pow(m_mass_factor, dest.m_mass_dim - dest.m_pressure_dim)
* pow(m_length_factor, dest.m_length_dim + dest.m_pressure_dim)
* pow(m_time_factor, dest.m_time_dim + 2*dest.m_pressure_dim)
* pow(m_quantity_factor, dest.m_quantity_dim)
* pow(m_pressure_factor, dest.m_pressure_dim);
}
}

View file

@ -38,3 +38,14 @@ TEST(Units, prefixes) {
EXPECT_DOUBLE_EQ(U.convert(1.0, "m^2", "cm^2"), 1e4);
EXPECT_DOUBLE_EQ(U.convert(1.0, "m/s", "km/hr"), 3.6);
}
TEST(Units, with_defaults) {
UnitSystem U({"cm", "g", "mol", "atm"});
EXPECT_DOUBLE_EQ(U.convert(1.0, "m"), 0.01);
EXPECT_DOUBLE_EQ(U.convert(1.0, "kmol/m^3"), 1000);
EXPECT_DOUBLE_EQ(U.convert(1.0, "kg/kmol"), 1.0);
EXPECT_DOUBLE_EQ(U.convert(1.0, "cm^2"), 1.0);
EXPECT_DOUBLE_EQ(U.convert(1.0, "Pa"), 101325);
EXPECT_DOUBLE_EQ(U.convert(1.0, "hPa"), 1013.25);
EXPECT_DOUBLE_EQ(U.convert(1.0, "Pa*m^6/kmol"), 101325*1e-12*1000);
}