Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.13.0] - 2026-01-06
### Changed
- Use const generics instead of types from `typenum` to represent units. [#95](https://github.com/itt-ustutt/quantity/pull/95)

## [0.12.2] - 2025-12-04
### Fixed
- Also updated `num-dual` dependency to 0.13 to fix incorrect dependency resolution for downstream crates. [#95](https://github.com/itt-ustutt/quantity/pull/95)
Expand Down
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "quantity"
version = "0.12.2"
version = "0.13.0"
authors = [
"Philipp Rehner <prehner@ethz.ch>",
"Gernot Bauer <bauer@itt.uni-stuttgart.de>",
Expand All @@ -24,7 +24,6 @@ rustdoc-args = ["--html-in-header", "./src/docs-header.html"]
members = ["si-units", "example/extend_quantity"]

[dependencies]
typenum = "1.17"
num-traits = "0.2"
document-features = "0.2"
## Use N-dimensional arrays from the [ndarray] crate as value of a quantity.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Add this to your `Cargo.toml`:

```
[dependencies]
quantity = "0.12"
quantity = "0.13"
```

## Examples
Expand All @@ -24,7 +24,7 @@ Calculate pressure of an ideal gas.

```rust
let temperature = 25.0 * CELSIUS;
let volume = 1.5 * METER.powi::<P3>();
let volume = 1.5 * METER.powi::<3>();
let moles = 75.0 * MOL;
let pressure = moles * RGAS * temperature / volume;
println!("{:.5}", pressure); // 123.94785 kPa
Expand All @@ -36,15 +36,15 @@ Calculate the gravitational pull of the moon on the earth.
let mass_earth = 5.9724e24 * KILOGRAM;
let mass_moon = 7.346e22 * KILOGRAM;
let distance = 383.398 * KILO * METER;
let force = G * mass_earth * mass_moon / distance.powi::<P2>();
let force = G * mass_earth * mass_moon / distance.powi::<2>();
println!("{:.5e}", force); // 1.99208e26 N
```

Calculate the pressure distribution in the atmosphere using the barometric formula.

```rust
let z = Quantity::linspace(1.0 * METER, 70.0 * KILO * METER, 10);
let g = 9.81 * METER / SECOND.powi::<P2>();
let g = 9.81 * METER / SECOND.powi::<2>();
let m = 28.949 * GRAM / MOL;
let t = 10.0 * CELSIUS;
let p0 = BAR;
Expand Down
75 changes: 75 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use std::env;
use std::fmt::Write;
use std::fs;
use std::path::Path;

fn main() {
// Generate Neg, Add, Mul, Div, Sub impls for Const<const N: i8>
// Limiting results of operations to values within [min, max]

// range of exponents
// use i32 for results that would overflow i8, we will limit to min/max in loop
let min: i32 = -20;
let max: i32 = 20;

let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("const_impls.rs");
let mut out = String::new();

// go over all exponent combinations
// for each operation, limit results to min/max
for a in min..=max {
// negation
let neg = -a;
if neg >= min && neg <= max {
writeln!(
&mut out,
"impl Neg for Const<{a}> {{ type Output = Const<{neg}>; fn neg(self) -> Self::Output {{ Const }} }}"
).unwrap();
}

for b in min..=max {
// addition
let sum = a + b;
if sum >= min && sum <= max {
writeln!(
&mut out,
"impl Add<Const<{b}>> for Const<{a}> {{ type Output = Const<{sum}>; fn add(self, _: Const<{b}>) -> Self::Output {{ Const }} }}"
).unwrap();
}

// subtraction
let diff = a - b;
if diff >= min && diff <= max {
writeln!(
&mut out,
"impl Sub<Const<{b}>> for Const<{a}> {{ type Output = Const<{diff}>; fn sub(self, _: Const<{b}>) -> Self::Output {{ Const }} }}"
).unwrap();
}

// multiplication
let mul = a * b;
if mul >= min && mul <= max {
writeln!(
&mut out,
"impl Mul<Const<{b}>> for Const<{a}> {{ type Output = Const<{mul}>; fn mul(self, _: Const<{b}>) -> Self::Output {{ Const }} }}"
).unwrap();
}

// division
// check: don't divide by 0 and only allow for integer results
if b != 0 && a % b == 0 {
let div = a / b;
if div >= min && div <= max {
writeln!(
&mut out,
"impl Div<Const<{b}>> for Const<{a}> {{ type Output = Const<{div}>; fn div(self, _: Const<{b}>) -> Self::Output {{ Const }} }}"
).unwrap();
}
}
}
}

fs::write(&dest_path, out).unwrap();
println!("cargo:rerun-if-changed=build.rs");
}
45 changes: 11 additions & 34 deletions src/ad.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use super::Quantity;
use super::{Diff, Quantity};
use nalgebra::{DefaultAllocator, Dim, OMatrix, OVector, U1, allocator::Allocator};
use num_dual::{
Dual, Dual2, Dual2Vec, Dual3, DualNum, DualStruct, DualVec, Gradients, HyperDual, HyperDualVec,
HyperHyperDual, Real,
};
use std::ops::Sub;
use typenum::Diff;

impl<D, F, T: DualStruct<D, F>, U> DualStruct<D, F> for Quantity<T, U> {
type Real = Quantity<T::Real, U>;
Expand Down Expand Up @@ -338,32 +337,10 @@ where
#[cfg(test)]
mod test_num_dual {
use super::*;
use crate::{Area, Length, METER, Temperature, Volume};
use crate::{Area, Length, METER, Volume};
use approx::assert_relative_eq;
use nalgebra::{SMatrix, SVector, vector};
use num_dual::{Dual64, ImplicitDerivative, ImplicitFunction};
use typenum::{P2, P3};

struct MyArgs<D> {
temperature: Temperature<D>,
}

impl<D: DualNum<f64> + Copy> DualStruct<D, f64> for MyArgs<D> {
type Real = MyArgs<f64>;
type Inner = MyArgs<D::Inner>;

fn re(&self) -> Self::Real {
MyArgs {
temperature: self.temperature.re(),
}
}

fn from_inner(inner: &Self::Inner) -> Self {
MyArgs {
temperature: Temperature::from_inner(&inner.temperature),
}
}
}

struct AreaImplicit;
impl ImplicitFunction<f64> for AreaImplicit {
Expand Down Expand Up @@ -403,27 +380,27 @@ mod test_num_dual {
fn test_derivative() {
let (v, dv) = first_derivative(volume, 5.0 * METER);
println!("{v}\t{dv:3}");
assert_eq!(v, 125.0 * METER.powi::<P3>());
assert_eq!(dv, 75.0 * METER.powi::<P2>());
assert_eq!(v, 125.0 * METER.powi::<3>());
assert_eq!(dv, 75.0 * METER.powi::<2>());

let (v, dv, d2v) = second_derivative(volume, 5.0 * METER);
println!("{v}\t{dv:3}\t\t{d2v}");
assert_eq!(v, 125.0 * METER.powi::<P3>(),);
assert_eq!(dv, 75.0 * METER.powi::<P2>(),);
assert_eq!(v, 125.0 * METER.powi::<3>(),);
assert_eq!(dv, 75.0 * METER.powi::<2>(),);
assert_eq!(d2v, 30.0 * METER);

let (v, dv_dx, dv_dh, d2v) =
second_partial_derivative(volume2, (5.0 * METER, 20.0 * METER));
println!("{v}\t{dv_dx:3}\t{dv_dh:3}\t{d2v}");
assert_eq!(v, 500.0 * METER.powi::<P3>(),);
assert_eq!(dv_dx, 200.0 * METER.powi::<P2>(),);
assert_eq!(dv_dh, 25.0 * METER.powi::<P2>(),);
assert_eq!(v, 500.0 * METER.powi::<3>(),);
assert_eq!(dv_dx, 200.0 * METER.powi::<2>(),);
assert_eq!(dv_dh, 25.0 * METER.powi::<2>(),);
assert_eq!(d2v, 10.0 * METER);

let (v, dv, d2v, d3v) = third_derivative(volume, 5.0 * METER);
println!("{v}\t{dv:3}\t\t{d2v}\t{d3v}");
assert_eq!(v, 125.0 * METER.powi::<P3>(),);
assert_eq!(dv, 75.0 * METER.powi::<P2>(),);
assert_eq!(v, 125.0 * METER.powi::<3>(),);
assert_eq!(dv, 75.0 * METER.powi::<2>(),);
assert_eq!(d2v, 30.0 * METER);
assert_eq!(d3v.into_value(), 6.0);
}
Expand Down
Loading