Add functions for converting units from AnyValue instances

This commit is contained in:
Ray Speth 2018-11-18 23:28:48 -05:00
parent 65121becac
commit 224c46ebbb
3 changed files with 104 additions and 0 deletions

View file

@ -16,6 +16,8 @@
namespace Cantera
{
class AnyValue;
//! A representation of the units associated with a dimensional quantity.
/*!
* Used for converting quantities between unit systems and checking for
@ -116,11 +118,27 @@ public:
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;
//! Convert a generic AnyValue node to the units specified in `dest`. If the
//! input is a double, convert it using the default units. If the input is a
//! string, treat this as a dimensioned value, e.g. '988 kg/m^3' and convert
//! from the specified units.
double convert(const AnyValue& val, const std::string& dest) const;
double convert(const AnyValue& val, const Units& dest) const;
//! Convert an array of AnyValue nodes to the units specified in `dest`. For
//! each node, if the value is a double, convert it using the default units,
//! and if it is a string, treat it as a value with the given dimensions.
vector_fp convert(const std::vector<AnyValue>& vals,
const std::string& dest) const;
vector_fp convert(const std::vector<AnyValue>& vals,
const Units& dest) const;
//! Convert `value` from the units of `src` to the units of `dest`, allowing
//! for the different dimensions that can be used for molar energies
double convertMolarEnergy(double value, const std::string& src,
@ -130,6 +148,12 @@ public:
//! specified units
double convertMolarEnergy(double value, const std::string& dest) const;
//! Convert a generic AnyValue node to the units specified in `dest`. If the
//! input is a double, convert it using the default units. If the input is a
//! string, treat this as a dimensioned value, e.g. '2.7e4 J/kmol' and
//! convert from the specified units.
double convertMolarEnergy(const AnyValue& val, const std::string& dest) const;
private:
//! Factor to convert mass from this unit system to kg
double m_mass_factor;

View file

@ -291,6 +291,57 @@ double UnitSystem::convert(double value, const Units& dest) const
* pow(m_pressure_factor, dest.m_pressure_dim);
}
static std::pair<double, std::string> split_unit(const AnyValue& v) {
if (v.is<std::string>()) {
// Should be a value and units, separated by a space, e.g. '2e4 J/kmol'
std::string val_units = v.asString();
size_t space = val_units.find(" ");
if (space == npos) {
throw CanteraError("UnitSystem::convert",
"Couldn't parse '{}' as a space-separated value/unit pair\n",
val_units);
}
return {fpValueCheck(val_units.substr(0, space)),
val_units.substr(space+1)};
} else {
// Just a value
return {v.asDouble(), ""};
}
}
double UnitSystem::convert(const AnyValue& v, const std::string& dest) const
{
return convert(v, Units(dest));
}
double UnitSystem::convert(const AnyValue& v, const Units& dest) const
{
auto val_units = split_unit(v);
if (val_units.second.empty()) {
// Just a value, so convert using default units
return convert(val_units.first, dest);
} else {
// Both source and destination units are explicit
return convert(val_units.first, Units(val_units.second), dest);
}
}
vector_fp UnitSystem::convert(const std::vector<AnyValue>& vals,
const std::string& dest) const
{
return convert(vals, Units(dest));
}
vector_fp UnitSystem::convert(const std::vector<AnyValue>& vals,
const Units& dest) const
{
vector_fp out;
for (const auto& val : vals) {
out.emplace_back(convert(val, dest));
}
return out;
}
double UnitSystem::convertMolarEnergy(double value, const std::string& src,
const std::string& dest) const
{
@ -322,6 +373,7 @@ double UnitSystem::convertMolarEnergy(double value, const std::string& src,
return value;
}
double UnitSystem::convertMolarEnergy(double value, const std::string& dest) const
{
Units udest(dest);
@ -337,4 +389,17 @@ double UnitSystem::convertMolarEnergy(double value, const std::string& dest) con
}
}
double UnitSystem::convertMolarEnergy(const AnyValue& v,
const std::string& dest) const
{
auto val_units = split_unit(v);
if (val_units.second.empty()) {
// Just a value, so convert using default units
return convertMolarEnergy(val_units.first, dest);
} else {
// Both source and destination units are explicit
return convertMolarEnergy(val_units.first, val_units.second, dest);
}
}
}

View file

@ -1,5 +1,6 @@
#include "gtest/gtest.h"
#include "cantera/base/Units.h"
#include "cantera/base/AnyMap.h"
using namespace Cantera;
@ -67,3 +68,17 @@ TEST(Units, activation_energies) {
EXPECT_DOUBLE_EQ(U.convertMolarEnergy(2000, "K"), 2000);
EXPECT_DOUBLE_EQ(U.convertMolarEnergy(2000, "J/kmol"), 2000 * GasConstant);
}
TEST(Units, from_anymap) {
AnyMap m = AnyMap::fromYamlString(
"{p: 12 bar, v: 10, A: 1 cm^2, V: 1,"
" k1: [5e2, 2, 29000], k2: [1e14, -1, 1300 cal/kmol]}");
UnitSystem U({"mm", "min", "atm"});
EXPECT_DOUBLE_EQ(U.convert(m["p"], "Pa"), 12e5);
EXPECT_DOUBLE_EQ(U.convert(m["v"], "cm/min"), 1.0);
EXPECT_DOUBLE_EQ(U.convert(m["A"], "mm^2"), 100);
EXPECT_DOUBLE_EQ(U.convert(m["V"], "m^3"), 1e-9);
auto k1 = m["k1"].asVector<AnyValue>();
EXPECT_DOUBLE_EQ(U.convert(k1[0], "m^3/kmol"), 1e-9*5e2);
EXPECT_DOUBLE_EQ(U.convertMolarEnergy(k1[2], "J/kmol"), 29000);
}