Skip to content

Commit e126065

Browse files
committed
Thermodynamic temperature conversions.
Extend the `quantity!` macro to accept a coefficient and optional constant factor in the `$conversion` parameter to support thermodynamic temperature conversions. `Conversion` trait and `from_base`, `to_base`, and `change_base` methods adjusted to support these factors. Resolves #3.
1 parent 826c1a8 commit e126065

10 files changed

+196
-70
lines changed

src/lib.rs

+20-6
Original file line numberDiff line numberDiff line change
@@ -336,27 +336,39 @@ pub trait Conversion<V> {
336336
/// Conversion factor type specific to the underlying storage type.
337337
type T: ConversionFactor<V>;
338338

339-
/// Static [conversion factor][factor] for the given unit to the base unit for the quantity.
339+
/// Coefficient portion of [conversion factor][factor] for converting the given unit to the
340+
/// base unit for the quantity: `(value * coefficient()) + constant()`.
340341
///
341342
/// Default implementation returns `Self::T::one()`.
342343
///
343344
/// [factor]: https://jcgm.bipm.org/vim/en/1.24.html
344345
#[inline(always)]
345-
fn conversion() -> Self::T {
346+
fn coefficient() -> Self::T {
346347
<Self::T as num::One>::one()
347348
}
348349

350+
/// Constant portion of [conversion factor][factor] for converting the given unit to the base
351+
/// unit for the quantity: `(value * coefficient()) + constant()`.
352+
///
353+
/// Default implementation returns `Self::T::zero()`.
354+
///
355+
/// [factor]: https://jcgm.bipm.org/vim/en/1.24.html
356+
#[inline(always)]
357+
fn constant() -> Self::T {
358+
<Self::T as num::Zero>::zero()
359+
}
360+
349361
/// Instance [conversion factor][factor].
350362
///
351-
/// Default implementation returns the static conversion `Self::conversion()`.
363+
/// Default implementation returns the coefficient: `Self::coefficient()`.
352364
///
353365
/// [factor]: https://jcgm.bipm.org/vim/en/1.24.html
354366
#[inline(always)]
355367
fn into_conversion(&self) -> Self::T
356368
where
357369
Self: Sized,
358370
{
359-
Self::conversion()
371+
Self::coefficient()
360372
}
361373
}
362374

@@ -365,9 +377,11 @@ pub trait Conversion<V> {
365377
/// [factor]: https://jcgm.bipm.org/vim/en/1.24.html
366378
#[cfg_attr(rustfmt, rustfmt_skip)]
367379
pub trait ConversionFactor<V>
368-
: lib::ops::Div<Self, Output = Self>
380+
: lib::ops::Add<Self, Output = Self>
381+
+ lib::ops::Sub<Self, Output = Self>
369382
+ lib::ops::Mul<Self, Output = Self>
370-
+ num::One
383+
+ lib::ops::Div<Self, Output = Self>
384+
+ num::Zero + num::One
371385
{
372386
/// Raises a `ConversionFactor<V>` to an integer power.
373387
fn powi(self, e: i32) -> Self;

src/quantity.rs

+61-23
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
/// defining a quantity that has the same dimensions as another quantity but isn't comparable.
1616
/// When not specified [`uom::Kind`](trait.Kind.html) is used.
1717
/// * `$unit`: Unit name (e.g. `meter`, `foot`).
18-
/// * `$conversion`: Conversion from the unit to the base unit of the quantity (e.g. `3.048E-1` to
19-
/// convert `foot` to `meter`).
18+
/// * `$conversion`: Conversion (coefficient and constant factor) from the unit to the base unit of
19+
/// the quantity (e.g. `3.048_E-1` to convert `foot` to `meter`. `1.0_E0, 273.15_E0` to convert
20+
/// `celsius` to `kelvin`.). The coefficient is required and the constant factor is optional.
21+
/// Note that using a unit with a non-zero constant factor is not currently supported as a base
22+
/// unit.
2023
/// * `$abbreviation`: Unit abbreviation (e.g. `"m"`).
2124
/// * `$singular`: Singular unit description (e.g. `"meter"`).
2225
/// * `$plural`: Plural unit description (e.g. `"meters"`).
@@ -97,7 +100,7 @@ macro_rules! quantity {
97100
$(#[$quantity_attr:meta])* quantity: $quantity:ident; $description:expr;
98101
$(#[$dim_attr:meta])* dimension: $system:ident<$($dimension:ident),+>;
99102
units {
100-
$($(#[$unit_attr:meta])* @$unit:ident: $conversion:expr;
103+
$($(#[$unit_attr:meta])* @$unit:ident: $($conversion:expr),+;
101104
$abbreviation:expr, $singular:expr, $plural:expr;)+
102105
}
103106
) => {
@@ -106,7 +109,7 @@ macro_rules! quantity {
106109
$(#[$dim_attr])* dimension: $system<$($dimension),+>;
107110
kind: $crate::Kind;
108111
units {
109-
$($(#[$unit_attr])* @$unit: $conversion; $abbreviation, $singular, $plural;)+
112+
$($(#[$unit_attr])* @$unit: $($conversion),+; $abbreviation, $singular, $plural;)+
110113
}
111114
}
112115
};
@@ -115,7 +118,7 @@ macro_rules! quantity {
115118
$(#[$dim_attr:meta])* dimension: $system:ident<$($dimension:ident),+>;
116119
kind: $kind:ty;
117120
units {
118-
$($(#[$unit_attr:meta])* @$unit:ident: $conversion:expr; $abbreviation:expr,
121+
$($(#[$unit_attr:meta])* @$unit:ident: $($conversion:expr),+; $abbreviation:expr,
119122
$singular:expr, $plural:expr;)+
120123
}
121124
) => {
@@ -168,8 +171,13 @@ macro_rules! quantity {
168171
type T = V;
169172

170173
#[inline(always)]
171-
fn conversion() -> Self::T {
172-
$conversion
174+
fn coefficient() -> Self::T {
175+
quantity!(@coefficient $($conversion),+)
176+
}
177+
178+
#[inline(always)]
179+
fn constant() -> Self::T {
180+
quantity!(@constant $($conversion),+)
173181
}
174182
}
175183

@@ -178,15 +186,24 @@ macro_rules! quantity {
178186

179187
storage_types! {
180188
types: PrimInt, BigInt;
189+
pub type T = $crate::num::rational::Ratio<V>;
190+
191+
#[inline(always)]
192+
fn from_f64(value: f64) -> T {
193+
<T as $crate::num::FromPrimitive>::from_f64(value).unwrap()
194+
}
181195

182196
$(impl $crate::Conversion<V> for super::$unit {
183-
type T = $crate::num::rational::Ratio<V>;
197+
type T = T;
184198

185199
#[inline(always)]
186-
fn conversion() -> Self::T {
187-
use $crate::num::FromPrimitive;
200+
fn coefficient() -> Self::T {
201+
from_f64(quantity!(@coefficient $($conversion),+))
202+
}
188203

189-
Self::T::from_f64($conversion).unwrap()
204+
#[inline(always)]
205+
fn constant() -> Self::T {
206+
from_f64(quantity!(@constant $($conversion),+))
190207
}
191208
}
192209

@@ -195,20 +212,29 @@ macro_rules! quantity {
195212

196213
storage_types! {
197214
types: BigUint;
215+
pub type T = $crate::num::rational::Ratio<V>;
216+
217+
#[inline(always)]
218+
fn from_f64(value: f64) -> T {
219+
use $crate::num::FromPrimitive;
220+
221+
let c = $crate::num::rational::Ratio::<$crate::num::BigInt>::from_f64(value)
222+
.unwrap();
223+
224+
T::new(c.numer().to_biguint().unwrap(), c.denom().to_biguint().unwrap())
225+
}
198226

199227
$(impl $crate::Conversion<V> for super::$unit {
200-
type T = $crate::num::rational::Ratio<V>;
228+
type T = T;
201229

202230
#[inline(always)]
203-
fn conversion() -> Self::T {
204-
use $crate::num::FromPrimitive;
205-
206-
let c = $crate::num::rational::Ratio::<$crate::num::BigInt>::from_f64(
207-
$conversion)
208-
.unwrap();
231+
fn coefficient() -> Self::T {
232+
from_f64(quantity!(@coefficient $($conversion),+))
233+
}
209234

210-
Self::T::new(c.numer().to_biguint().unwrap(),
211-
c.denom().to_biguint().unwrap())
235+
#[inline(always)]
236+
fn constant() -> Self::T {
237+
from_f64(quantity!(@constant $($conversion),+))
212238
}
213239
}
214240

@@ -218,14 +244,22 @@ macro_rules! quantity {
218244
storage_types! {
219245
types: Ratio;
220246

247+
#[inline(always)]
248+
fn from_f64(value: f64) -> V {
249+
<V as $crate::num::FromPrimitive>::from_f64(value).unwrap()
250+
}
251+
221252
$(impl $crate::Conversion<V> for super::$unit {
222253
type T = V;
223254

224255
#[inline(always)]
225-
fn conversion() -> Self::T {
226-
use $crate::num::FromPrimitive;
256+
fn coefficient() -> Self::T {
257+
from_f64(quantity!(@coefficient $($conversion),+))
258+
}
227259

228-
Self::T::from_f64($conversion).unwrap()
260+
#[inline(always)]
261+
fn constant() -> Self::T {
262+
from_f64(quantity!(@constant $($conversion),+))
229263
}
230264
}
231265

@@ -358,4 +392,8 @@ macro_rules! quantity {
358392
#[derive(Clone, Copy, Debug, Hash)]
359393
pub struct $unit;
360394
};
395+
(@coefficient $factor:expr, $const:expr) => { $factor };
396+
(@coefficient $factor:expr) => { $factor };
397+
(@constant $factor:expr, $const:expr) => { $const };
398+
(@constant $factor:expr) => { 0.0 };
361399
}

src/si/temperature_interval.rs

+4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ quantity! {
4343
@attokelvin: prefix!(atto); "aK", "attokelvin", "attokelvins";
4444
@zeptokelvin: prefix!(zepto); "zK", "zeptokelvin", "zeptokelvins";
4545
@yoctokelvin: prefix!(yocto); "yK", "yoctokelvin", "yoctokelvins";
46+
47+
@degree_celsius: 1.0_E0; "°C", "degree Celsius", "degrees Celsius";
48+
@degree_fahrenheit: 5.0_E0 / 9.0_E0; "°F", "degree Fahrenheit", "degrees Fahrenheit";
49+
@degree_rankine: 5.0_E0 / 9.0_E0; "°R", "degree Rankine", "degrees Rankine";
4650
}
4751
}
4852

src/si/thermodynamic_temperature.rs

+5
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ quantity! {
9292
@attokelvin: prefix!(atto); "aK", "attokelvin", "attokelvins";
9393
@zeptokelvin: prefix!(zepto); "zK", "zeptokelvin", "zeptokelvins";
9494
@yoctokelvin: prefix!(yocto); "yK", "yoctokelvin", "yoctokelvins";
95+
96+
@degree_celsius: 1.0_E0, 273.15_E0; "°C", "degree Celsius", "degrees Celsius";
97+
@degree_fahrenheit: 5.0_E0 / 9.0_E0, 459.67_E0; "°F", "degree Fahrenheit",
98+
"degrees Fahrenheit";
99+
@degree_rankine: 5.0_E0 / 9.0_E0; "°R", "degree Rankine", "degrees Rankine";
95100
}
96101
}
97102

src/system.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,8 @@ macro_rules! system {
275275
use $crate::Conversion;
276276
use $crate::ConversionFactor;
277277

278-
(v.into_conversion() $(* U::$name::conversion().powi(D::$symbol::to_i32()))+
279-
/ N::conversion())
278+
(v.into_conversion() $(* U::$name::coefficient().powi(D::$symbol::to_i32()))+
279+
/ N::coefficient() - N::constant())
280280
.value()
281281
}
282282

@@ -293,8 +293,8 @@ macro_rules! system {
293293
use $crate::Conversion;
294294
use $crate::ConversionFactor;
295295

296-
(v.into_conversion() * N::conversion()
297-
/ (V::conversion() $(* U::$name::conversion().powi(D::$symbol::to_i32()))+))
296+
((v.into_conversion() + N::constant()) * N::coefficient()
297+
/ (V::coefficient() $(* U::$name::coefficient().powi(D::$symbol::to_i32()))+))
298298
.value()
299299
}
300300

@@ -313,8 +313,8 @@ macro_rules! system {
313313
use $crate::Conversion;
314314
use $crate::ConversionFactor;
315315

316-
(v.into_conversion() $(* Ur::$name::conversion().powi(D::$symbol::to_i32())
317-
/ Ul::$name::conversion().powi(D::$symbol::to_i32()))+)
316+
(v.into_conversion() $(* Ur::$name::coefficient().powi(D::$symbol::to_i32())
317+
/ Ul::$name::coefficient().powi(D::$symbol::to_i32()))+)
318318
.value()
319319
}}
320320

@@ -1240,7 +1240,8 @@ macro_rules! system {
12401240
/// * `$V`: Underlying value storage type (e.g. `f32`).
12411241
/// * `$U`: Optional. Base units. Pass as a tuple with the desired units: `(meter, kilogram,
12421242
/// second, ampere, kelvin, mole, candela)`. The system's base units will be used if no
1243-
/// value is provided.
1243+
/// value is provided. Note that a unit with a non-zero constant factor is not currently
1244+
/// supported as a base unit.
12441245
///
12451246
/// An example invocation is given below for a meter-kilogram-second system setup in the
12461247
/// module `mks` with a system of quantities name `Q`. The `#[macro_use]` attribute must be

src/tests/asserts.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ storage_types! {
55

66
use tests::*;
77

8-
assert_impl!(q; Quantity<Q<Z0, Z0>, U<V>, V>,
8+
assert_impl!(q; Quantity<Q<Z0, Z0, Z0>, U<V>, V>,
99
Clone, Copy, PartialEq, PartialOrd, Send, Sync);
1010
}
1111

@@ -14,7 +14,7 @@ storage_types! {
1414

1515
use tests::*;
1616

17-
assert_impl!(q; Quantity<Q<Z0, Z0>, U<V>, V>,
17+
assert_impl!(q; Quantity<Q<Z0, Z0, Z0>, U<V>, V>,
1818
Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Send, Sync, ::lib::hash::Hash);
1919
}
2020

@@ -23,6 +23,6 @@ storage_types! {
2323

2424
use tests::*;
2525

26-
assert_impl!(q; Quantity<Q<Z0, Z0>, U<V>, V>,
26+
assert_impl!(q; Quantity<Q<Z0, Z0, Z0>, U<V>, V>,
2727
Clone, Eq, Ord, PartialEq, PartialOrd, Send, Sync, ::lib::hash::Hash);
2828
}

src/tests/mod.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use self::length::{kilometer, meter};
44
use self::mass::kilogram;
5+
use self::thermodynamic_temperature::{degree_fahrenheit, kelvin};
56
use lib::fmt::Debug;
67
use lib::marker::PhantomData;
78
#[allow(unused_imports)]
@@ -19,7 +20,7 @@ use {Conversion, ConversionFactor};
1920
mod length {
2021
quantity! {
2122
quantity: Length; "length";
22-
dimension: Q<P1, Z0>;
23+
dimension: Q<P1, Z0, Z0>;
2324
units {
2425
@kilometer: 1000.0; "km", "kilometer", "kilometers";
2526
@meter: 1.0; "m", "meter", "meters";
@@ -31,21 +32,36 @@ mod length {
3132
mod mass {
3233
quantity! {
3334
quantity: Mass; "mass";
34-
dimension: Q<Z0, P1>;
35+
dimension: Q<Z0, P1, Z0>;
3536
units {
3637
@kilogram: 1000.0 / 1000.0; "kg", "kilogram", "kilograms";
3738
}
3839
}
3940
}
4041

42+
#[macro_use]
43+
mod thermodynamic_temperature {
44+
quantity! {
45+
quantity: ThermodynamicTemperature; "thermodynamic temperature";
46+
dimension: Q<Z0, Z0, P1>;
47+
units {
48+
@kelvin: 1.0; "K", "kelvin", "kelvins";
49+
@degree_fahrenheit: 5.0_E0 / 9.0_E0, 459.67_E0; "°F", "degree Fahrenheit",
50+
"degrees Fahrenheit";
51+
}
52+
}
53+
}
54+
4155
system! {
4256
quantities: Q {
4357
length: meter, L;
4458
mass: kilogram, M;
59+
thermodynamic_temperature: kelvin, Th;
4560
}
4661
units: U {
4762
mod length::Length,
4863
mod mass::Mass,
64+
mod thermodynamic_temperature::ThermodynamicTemperature,
4965
}
5066
}
5167

src/tests/quantities.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,20 @@ storage_types! {
7878
}
7979

8080
#[test]
81-
fn conversion() {
81+
fn coefficient() {
8282
Test::assert_eq(&V::from_f64(1000.0).unwrap(),
83-
&<kilometer as Conversion<V>>::conversion().value());
84-
Test::assert_eq(&V::one(), &<meter as Conversion<V>>::conversion().value());
85-
Test::assert_eq(&V::one(), &<kilogram as Conversion<V>>::conversion().value());
83+
&<kilometer as Conversion<V>>::coefficient().value());
84+
Test::assert_eq(&V::one(), &<meter as Conversion<V>>::coefficient().value());
85+
Test::assert_eq(&V::one(), &<kilogram as Conversion<V>>::coefficient().value());
86+
Test::assert_eq(&V::from_f64(5.0 / 9.0).unwrap(),
87+
&<degree_fahrenheit as Conversion<V>>::coefficient().value());
88+
}
89+
90+
#[test]
91+
fn constant() {
92+
Test::assert_eq(&V::zero(), &<kilogram as Conversion<V>>::constant().value());
93+
Test::assert_eq(&V::from_f64(459.67).unwrap(),
94+
&<degree_fahrenheit as Conversion<V>>::constant().value());
8695
}
8796

8897
#[cfg(feature = "std")]

0 commit comments

Comments
 (0)