Skip to content

Commit 8f89c3d

Browse files
authored
Implement a number of quality of life improvements for working with quantities of dual numbers (#88)
1 parent 6d2d1da commit 8f89c3d

5 files changed

Lines changed: 84 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [0.10.4] - 2025-06-02
10+
### Added
11+
- Implemented `Display` for `Dimensionless<T>`. [#88](https://github.com/itt-ustutt/quantity/pull/88)
12+
- Implemented `Debug` for `Angle<T>`. [#88](https://github.com/itt-ustutt/quantity/pull/88)
13+
- Added `Quantity<T, U>::inv`. [#88](https://github.com/itt-ustutt/quantity/pull/88)
14+
15+
### Changed
16+
- Generalized implementation of `Quantity<T, U>::abs` to every type with `T: Signed`. [#88](https://github.com/itt-ustutt/quantity/pull/88)
17+
- Generalized implementation of `Quantity::from_vec` and `Quantity::linspace`. [#88](https://github.com/itt-ustutt/quantity/pull/88)
18+
919
## [0.10.3] - 2025-05-07
1020
### Added
1121
- Implemented `Deref` for `Dimensionless<T>`. [#86](https://github.com/itt-ustutt/quantity/pull/86)

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "quantity"
3-
version = "0.10.3"
3+
version = "0.10.4"
44
authors = [
55
"Philipp Rehner <prehner@ethz.ch>",
66
"Gernot Bauer <bauer@itt.uni-stuttgart.de>",
@@ -25,6 +25,7 @@ members = ["si-units", "example/extend_quantity"]
2525

2626
[dependencies]
2727
typenum = "1.17"
28+
num-traits = "0.2"
2829
document-features = "0.2"
2930
## Use N-dimensional arrays from the [ndarray] crate as value of a quantity.
3031
ndarray = { version = "0.16", optional = true }

src/array.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ use ndarray::{
66
};
77
use std::iter::FromIterator;
88
use std::marker::PhantomData;
9+
use std::ops::{Add, Mul, Sub};
910

10-
impl<U> Quantity<Array1<f64>, U> {
11+
impl<T: Copy, U> Quantity<Array1<T>, U> {
1112
/// Create a one-dimensional array from a vector of scalar quantities.
12-
pub fn from_vec(v: Vec<Quantity<f64, U>>) -> Self {
13+
pub fn from_vec(v: Vec<Quantity<T, U>>) -> Self {
1314
Self(v.iter().map(|e| e.0).collect(), PhantomData)
1415
}
1516

@@ -23,10 +24,21 @@ impl<U> Quantity<Array1<f64>, U> {
2324
/// let x = Length::linspace(1.0 * METER, 3.0 * METER, 5);
2425
/// assert_relative_eq!(x, &(arr1(&[1.0, 1.5, 2.0, 2.5, 3.0]) * METER));
2526
/// ```
26-
pub fn linspace(start: Quantity<f64, U>, end: Quantity<f64, U>, n: usize) -> Self {
27-
Self(Array1::linspace(start.0, end.0, n), PhantomData)
27+
pub fn linspace(start: Quantity<T, U>, end: Quantity<T, U>, n: usize) -> Self
28+
where
29+
T: Mul<f64, Output = T> + Sub<Output = T> + Add<Output = T>,
30+
{
31+
Self(
32+
Array1::linspace(0.0, 1.0, n)
33+
.into_iter()
34+
.map(|x| (end.0 - start.0) * x + start.0)
35+
.collect(),
36+
PhantomData,
37+
)
2838
}
39+
}
2940

41+
impl<U> Quantity<Array1<f64>, U> {
3042
/// Create a one-dimensional array with n logarithmically spaced elements from `start` to `end` (inclusive).
3143
///
3244
/// # Example

src/fmt.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ impl<
3636
}
3737
}
3838

39+
impl<Inner: fmt::Display> fmt::Display for Quantity<Inner, _Dimensionless> {
40+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41+
self.0.fmt(f)
42+
}
43+
}
44+
3945
#[cfg(feature = "pyo3")]
4046
pub(crate) trait PrintUnit {
4147
const UNIT: &'static str;
@@ -186,7 +192,15 @@ static PREFIX_SYMBOLS: LazyLock<HashMap<i8, &'static str>> = LazyLock::new(|| {
186192

187193
impl fmt::Display for Angle {
188194
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189-
write!(f, "{}°", self.0.to_degrees())
195+
self.0.to_degrees().fmt(f)?;
196+
write!(f, "°")
197+
}
198+
}
199+
200+
impl<T: fmt::Debug> fmt::Debug for Angle<T> {
201+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202+
self.0.fmt(f)?;
203+
write!(f, " rad")
190204
}
191205
}
192206

@@ -231,4 +245,20 @@ mod tests {
231245
assert_eq!(format!("{}", 0.0 * KELVIN), "0 K");
232246
assert_eq!(format!("{:.2}", 0.0 * PASCAL), "0.00 Pa");
233247
}
248+
249+
#[test]
250+
fn test_fmt_dimensionless() {
251+
assert_eq!(format!("{}", BAR / PASCAL), format!("{}", 1e5));
252+
assert_eq!(
253+
format!("{}", RGAS / (JOULE / (MOL * KELVIN))),
254+
format!("{}", 8.31446261815324)
255+
);
256+
}
257+
258+
#[test]
259+
fn test_fmt_angle() {
260+
assert_eq!(format!("{}", 90.0 * DEGREES), "90°");
261+
assert_eq!(format!("{:.3?}", 45.0 * DEGREES), "0.785 rad");
262+
assert_eq!(format!("{:.2}", 0.5 * RADIANS), "28.65°");
263+
}
234264
}

src/ops.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use super::Quantity;
33
use approx::{AbsDiffEq, RelativeEq};
44
#[cfg(feature = "ndarray")]
55
use ndarray::{Array, ArrayBase, Data, DataMut, DataOwned, Dimension};
6+
use num_traits::{Inv, Signed};
67
use std::marker::PhantomData;
78
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
89
use typenum::{Diff, Integer, Negate, Prod, Quot, Sum, P2, P3};
@@ -401,7 +402,9 @@ impl<U> Quantity<f64, U> {
401402
{
402403
Quantity(self.0.powf(1.0 / R::I32 as f64), PhantomData)
403404
}
405+
}
404406

407+
impl<T, U> Quantity<T, U> {
405408
/// Return the absolute value of `self`.
406409
///
407410
/// # Example
@@ -410,10 +413,31 @@ impl<U> Quantity<f64, U> {
410413
/// # use approx::assert_relative_eq;
411414
/// let t = -50.0 * KELVIN;
412415
/// assert_relative_eq!(t.abs(), &(50.0 * KELVIN));
413-
pub fn abs(self) -> Self {
416+
pub fn abs(self) -> Self
417+
where
418+
T: Signed,
419+
{
414420
Self(self.0.abs(), PhantomData)
415421
}
416422

423+
/// Return the multiplicative inverse of `self`.
424+
///
425+
/// # Example
426+
/// ```
427+
/// # use quantity::PASCAL;
428+
/// # use approx::assert_relative_eq;
429+
/// let t = 5.0 * PASCAL;
430+
/// assert_relative_eq!(t.inv(), &(0.2/PASCAL));
431+
pub fn inv(self) -> Quantity<T, Negate<U>>
432+
where
433+
T: Inv<Output = T>,
434+
U: Neg,
435+
{
436+
Quantity(self.0.inv(), PhantomData)
437+
}
438+
}
439+
440+
impl<U> Quantity<f64, U> {
417441
/// Returns a number that represents the sign of `self`.
418442
///
419443
/// - `1.0` if the number is positive, `+0.0` or `INFINITY`

0 commit comments

Comments
 (0)