#si-units #units #physics #science #measurement

simple-si-units

A Rust library providing base SI Units and common conversions. SI Units are provided as templated types so that you can write APIs that enforce correct units

14 releases (5 breaking)

new 0.7.3 Mar 17, 2023
0.5.1 Feb 21, 2023
0.3.1 Nov 7, 2022

#33 in Science

Download history 5/week @ 2022-12-04 5/week @ 2022-12-11 1/week @ 2022-12-18 5/week @ 2022-12-25 1/week @ 2023-01-08 5/week @ 2023-01-15 3/week @ 2023-01-22 94/week @ 2023-01-29 25/week @ 2023-02-05 18/week @ 2023-02-12 74/week @ 2023-02-19 44/week @ 2023-02-26 10/week @ 2023-03-05 20/week @ 2023-03-12

148 downloads per month

MPL-2.0 license

540KB
11K SLoC

simple-si-units

GitHub Workflow Build Status GitHub Workflow Test Status codecov Crate.io Redistribution license

This Rust library provides compiler-checked types for the standard set of SI units, as specified by the US National Institute of Standards and Technology (this project is not officially endorsed by NIST).

What's included?

  • Official standard SI Units
  • Common secondary units, such as velocity
  • Implements operators to automatically convert between units with basic arithmatic (eg distance / time = velocity)
  • Units are templated so that you can choose whether to use f32 or f64 or other number-like type as your concrete number type.
  • Optional, limited integration with uom

Units

This crate provides types for the following units. Other kinds of quantities not listed below (eg jolt) are beyond the scope of this crate.

Base SI units (and standard unit of measure):

  • Distance, aka Length (meters)
  • Mass (kilogram)
  • Time (seconds)
  • Temperature (kelvin)
  • Amount, aka Quantity (moles)
  • Current (amperes)
  • Luminosity (candela)

Derived units:

  • Angle (rad)
  • Angular Velocity (rad/s)
  • Angular Acceleration (rad/s^2)
  • Moment of Inertia (kg.m^2)
  • Angular Momentum (kg.m^2.rad/s)
  • Torque (kg.m^2/s^2, aka N.m)
  • Solid Angle (sr)
  • Frequency (1/s, aka Hz)
  • Area (m^2)
  • Area Density (kg.m^2)
  • Volume (m^3)
  • Density (kg/L)
  • Velocity (m/s)
  • Acceleration (m/s^2)
  • Momentum (kg.m/s)
  • Force (kg.m/s^2, aka N)
  • Pressure (N/m^2, aka Pa)
  • Energy (kg.m^2/s^2, aka J)
  • Charge, aka Coulomb (A.s, aka C)
  • Power, aka Watt (J/s, aka W)
  • Voltage (W/A, aka V)
  • Resistance (V/A, aka Ohm)
  • Conductance (1/ohm, aka S)
  • Capacitance (C/V, aka F)
  • Inductance (Wb/A, aka H)
  • Magnetic Flux (V.s, aka Wb)
  • Magnetic Flux Density (Wb/m^2, aka T)
  • Catalytic Activity (mol/s)
  • Concentration (mol/m^3)
  • Luminous Flux (cd.sr, aka lm)
  • Illuminance (lm/m^2, aka lux)
  • Radioactivity (1/s, aka Bq)
  • Absorbed Dose (J/kg, aka Gy)
  • Dose Equivalent (J/kg, aka Sv)

What's NOT included?

  • Not supporting dimensional analysis
  • Not providing an exhaustive list of all possible unit types (but you can use this library to implement them yourself)
  • Not supporting unusual number types (eg integers)
  • Not aiming for full integration with uom

Roadmap

The version of this library will be incremented to reflect progress through the various milestones. The goal is to reach version 1.0 (API stable) as quickly as practical.

  • V0.1.0 (Done!) - Finish README and claim crates.io namespace
  • V0.2.0 (Done!) - Scope declaration
  • V0.3.0 (Done!) - Design API
  • V0.4.0 (Done!) - Unit and API tests
  • V0.5.0 (Done!) - Base SI units (distance, mass, time, temperature, amount, electric current, luminosity)
  • V0.6.0 (Done!) - Common secondary units (velocity, acceleration, energy, etc.)
  • V0.7.0 (In progress...) - Full test coverage of all types of units
  • V0.8.0 - Optional Into and From conversion to/from uom types
  • V0.9.0 - Full documentation coverage
  • V1.0.0 - Done
  • V1.1.0 - Add inverse of all provided units that don't already have an inverse equivalent (eg InverseArea = 1/Area)

How it works

For each type of unit (eg Distance), Simple SI Units provides a generic struct to represent the unit and which implements common type conversion. For example, dividing a Distance by a Time results in a Velocity:

use simple_si_units::base::{Distance, Mass};
use simple_si_units::mechanical::{Acceleration};
pub fn calc_gravity(mass: Mass<f64>, dist: Distance<f64>) -> Acceleration<f64>{
	const G: f64 = 6.67408e-11; // m3 kg-1 s-2
	let d_squared = dist * dist;
	return Acceleration::from_mps2(G * mass.to_kg() / d_squared.to_m2())
}

fn main(){
	let a = calc_gravity(Mass::from_solar_mass(1.0), Distance::from_au(1.0));
	println!("Solar gravity at Earth orbital distance: {}", a);
}

Since these structs use generic type templates for the internal data type, you can use any number-like data type with these structs, including num_complex::Complex and num_bigfloat::BigFloat (see caveat section below regarding primitive types other than f64).

Adding your own units

Simple SI Units does not provide an exhaustive list of possible units of measure. To create your own units, use the UnitStruct procedural macro and NumLike trait bundle (NumLike is just shorthand for Sized+std::ops::*<Output=Self>, you could instead use the Num trait from the num-traits crate if you prefer):

use simple_si_units::{UnitStruct, NumLike};
#[derive(UnitStruct, Debug, Copy, Clone)]
struct HyperVelocity<T: NumLike>{
	square_meters_per_second: T
}

fn weighted_sum<T: NumLike>(a: HyperVelocity<T>, b: HyperVelocity<T>, weight: f64) -> HyperVelocity<T> where
	T:NumLike + From<f64>
{
	return weight*a + (1.-weight)*b;
}

Caveats

There are a few limitations owing to the Rust compiler's lack of type specialization in stable Rust, the most notable of which is that some functions won't work with f32 as the input type.

Primitive types other than f64

Many of the member functions will only work with number types that implement From<f64> (because they need to multiply by an internal coefficient of type f64), so while you can still instantiate these structs with f32 and other primitive types (eg Mass{kg: 1.1_f32} will work), you will have to wrap primitive types other than f64 to use the constructor functions (eg Mass::from_g(1100_f32) will not work). Thus to use f32 or other primitives which are not convertible from f64, you will need to wrap them with an implementation of From<f64>. For example:

struct MyFloat32 {
    x: f32
}
impl MyFloat32 {
    pub fn new(n: f32) -> Self{return Self{x: n}}
}
impl From<f64> for MyFloat32 {
    fn from(n: f64) -> Self {return Self::new(n as f32)}
}
impl std::ops::Add<Self> for MyFloat32 {
    type Output = Self;
    fn add(self, rhs: Self) -> Self::Output {Self{ x: self.x + rhs.x }}
}
impl std::ops::Sub<Self> for MyFloat32 {
    type Output = Self;
    fn sub(self, rhs: Self) -> Self::Output {Self{ x: self.x - rhs.x }}
}
impl std::ops::Div<Self> for MyFloat32 {
    type Output = Self;
    fn div(self, rhs: Self) -> Self::Output {Self{ x: self.x / rhs.x}}
}
impl std::ops::Mul<Self> for MyFloat32 {
    type Output = Self;
    fn mul(self, rhs: Self) -> Self::Output {Self{ x: self.x * rhs.x }}
}
fn my_fn() -> Mass<MyFloat32>{
    let m = Mass::from_g(MyFloat32::new(1100_f32));
    return m * MyFloat32::new(0.5);
}

License

This library is open source, licensed under the Mozilla Public License version 2.0. In summary, you may include this source code as-is in both open-source and proprietary projects without requesting permission from me, but if you modify the source code from this library then you must make your modified version of this library available under an open-source license.

Dependencies

~0.7–2.5MB
~73K SLoC