///|
pub enum UnitDimension {
Mass
Amount
Volume
Length
Pressure
Energy
Power
Temperature
Time
Dimensionless
} derive(Debug, Eq)
///|
pub enum MeasureUnit {
Kilogram
Gram
Tonne
KgPerHour
Mol
Kmol
Liter
CubicMeter
CubicMeterPerHour
Meter
Pascal
KiloPascal
Bar
Joule
KiloJoule
KiloWatt
Celsius
Kelvin
Second
Minute
Hour
Percent
Ratio
} derive(Debug, Eq)
///|
pub enum QuantityError {
IncompatibleDimensions(UnitDimension, UnitDimension)
BelowAbsoluteZero
InvalidValue
} derive(Debug, Eq)
///|
pub struct Quantity {
value : Float
unit : MeasureUnit
} derive(Debug, Eq)
///|
pub fn quantity(value : Float, unit : MeasureUnit) -> Quantity {
{ value, unit }
}
///|
pub fn MeasureUnit::dimension(self : MeasureUnit) -> UnitDimension {
match self {
Kilogram | Gram | Tonne | KgPerHour => Mass
Mol | Kmol => Amount
Liter | CubicMeter | CubicMeterPerHour => Volume
Meter => Length
Pascal | KiloPascal | Bar => Pressure
Joule | KiloJoule => Energy
KiloWatt => Power
Celsius | Kelvin => Temperature
Second | Minute | Hour => Time
Percent | Ratio => Dimensionless
}
}
///|
fn MeasureUnit::scale_to_base(self : MeasureUnit) -> Float {
match self {
Kilogram => 1.0
Gram => 0.001
Tonne => 1000.0
KgPerHour => 1.0
Mol => 1.0
Kmol => 1000.0
Liter => 0.001
CubicMeter | CubicMeterPerHour => 1.0
Meter => 1.0
Pascal => 1.0
KiloPascal => 1000.0
Bar => 100000.0
Joule => 1.0
KiloJoule => 1000.0
KiloWatt => 1.0
Celsius | Kelvin => 1.0
Second => 1.0
Minute => 60.0
Hour => 3600.0
Percent => 0.01
Ratio => 1.0
}
}
///|
pub fn MeasureUnit::symbol(self : MeasureUnit) -> String {
match self {
Kilogram => "kg"
Gram => "g"
Tonne => "t"
KgPerHour => "kg/h"
Mol => "mol"
Kmol => "kmol"
Liter => "L"
CubicMeter => "m3"
CubicMeterPerHour => "m3/h"
Meter => "m"
Pascal => "Pa"
KiloPascal => "kPa"
Bar => "bar"
Joule => "J"
KiloJoule => "kJ"
KiloWatt => "kW"
Celsius => "C"
Kelvin => "K"
Second => "s"
Minute => "min"
Hour => "h"
Percent => "%"
Ratio => "ratio"
}
}
///|
pub fn Quantity::validate(
self : Quantity,
) -> Result[MeasureUnit, QuantityError] {
if self.unit is Celsius && self.value < -273.15 {
Err(BelowAbsoluteZero)
} else if self.unit is Kelvin && self.value < 0.0 {
Err(BelowAbsoluteZero)
} else if self.value != self.value {
Err(InvalidValue)
} else {
Ok(self.unit)
}
}
///|
pub fn Quantity::convert_to(
self : Quantity,
target : MeasureUnit,
) -> Result[Float, QuantityError] {
if self.unit.dimension() != target.dimension() {
return Err(
IncompatibleDimensions(self.unit.dimension(), target.dimension()),
)
}
match self.validate() {
Err(error) => Err(error)
Ok(_) =>
match self.unit.dimension() {
Temperature =>
if self.unit is Celsius && target is Kelvin {
Ok(self.value + 273.15)
} else if self.unit is Kelvin && target is Celsius {
Ok(self.value - 273.15)
} else {
Ok(self.value)
}
_ => Ok(self.value * self.unit.scale_to_base() / target.scale_to_base())
}
}
}
///|
pub fn Quantity::in_unit(
self : Quantity,
target : MeasureUnit,
) -> Result[Quantity, QuantityError] {
match self.convert_to(target) {
Ok(value) => Ok({ value, unit: target })
Err(error) => Err(error)
}
}
///|
pub fn Quantity::add(
self : Quantity,
other : Quantity,
) -> Result[Quantity, QuantityError] {
match other.convert_to(self.unit) {
Ok(value) => Ok({ value: self.value + value, unit: self.unit })
Err(error) => Err(error)
}
}
///|
pub fn Quantity::subtract(
self : Quantity,
other : Quantity,
) -> Result[Quantity, QuantityError] {
match other.convert_to(self.unit) {
Ok(value) => Ok({ value: self.value - value, unit: self.unit })
Err(error) => Err(error)
}
}
///|
pub fn Quantity::scale(self : Quantity, factor : Float) -> Quantity {
{ value: self.value * factor, unit: self.unit }
}
///|
pub fn Quantity::format(self : Quantity, decimals : Int) -> String {
"\{self.value} \{self.unit.symbol()} (\{decimals} dp)"
}
///|
pub fn parse_unit(text : String) -> MeasureUnit? {
match text.trim() {
"kg" => Some(Kilogram)
"g" => Some(Gram)
"t" => Some(Tonne)
"kg/h" => Some(KgPerHour)
"mol" => Some(Mol)
"kmol" => Some(Kmol)
"L" => Some(Liter)
"m3" => Some(CubicMeter)
"m3/h" => Some(CubicMeterPerHour)
"m" => Some(Meter)
"Pa" => Some(Pascal)
"kPa" => Some(KiloPascal)
"bar" => Some(Bar)
"J" => Some(Joule)
"kJ" => Some(KiloJoule)
"kW" => Some(KiloWatt)
"C" => Some(Celsius)
"K" => Some(Kelvin)
"s" => Some(Second)
"min" => Some(Minute)
"h" => Some(Hour)
"%" => Some(Percent)
"ratio" => Some(Ratio)
_ => None
}
}
///|
pub fn bar() -> MeasureUnit {
Bar
}
///|
pub fn pascal() -> MeasureUnit {
Pascal
}
///|
pub fn kelvin() -> MeasureUnit {
Kelvin
}
///|
pub fn celsius() -> MeasureUnit {
Celsius
}
///|
pub fn kg_per_hour() -> MeasureUnit {
KgPerHour
}
///|
pub fn compatible(left : MeasureUnit, right : MeasureUnit) -> Bool {
left.dimension() == right.dimension()
}
///|
pub fn base_unit(dimension : UnitDimension) -> MeasureUnit {
match dimension {
Mass => Kilogram
Amount => Mol
Volume => CubicMeter
Length => Meter
Pressure => Pascal
Energy => Joule
Power => KiloWatt
Temperature => Kelvin
Time => Second
Dimensionless => Ratio
}
}