# dimensional_quantity
Dimensional quantity: checking dimensions of physical quantities in compile time using generic const expressions
* [131 predefined dimensional quantities for International System of Units (SI)](si::extended::f64).
* [Dimensional units of measure](si::extended::f64::units_of_measure).
* [Dimensional physical constants (CODATA 2022 values)](si::extended::f64::constants).
* [Non-dimensional SI prefixes: metric (kilo, micro, giga, etc.) and binary (Kibi, Mebi, ...)](prefix).
## Usage
* This crate is **experimental** and uses **unstable** [`generic_const_exprs`](https://github.com/rust-lang/rust/issues/76560)
feature and can only be compiled with **nightly** toolchain.
* If you need a **stable** solution, please check excellent [dimensioned](https://github.com/paholg/dimensioned) and [uom](https://github.com/iliekturtles/uom) crates.
* The only implemented underlying storage types are f64 and f32.
To use this crate, first add this to your `Cargo.toml`:
```toml
[dependencies]
dimensional_quantity = "0.1"
```
then enable *`generic_const_exprs`* feature in your crate:
```rust
#![feature(generic_const_exprs)]
```
and build it with nightly toolchain using ```cargo +nightly```:
```bash
cargo +nightly build
```
or add `rust-toolchain.toml` file with the following content to your project
```toml
[toolchain]
channel = "nightly"
```
## Features
### Serialization/deserialization with Serde
```toml
[dependencies]
dimensional_quantity = {version = "0.1", features = ["use_serde"]}
```
### Decimal storage type
```toml
[dependencies]
dimensional_quantity = {version = "0.1", features = ["decimal"]}
```
## Examples
### Creating dimensional quantities from f64
```rust
#![feature(generic_const_exprs)]
use dimensional_quantity::si::extended::f64::quantities::{Velocity};
use dimensional_quantity::si::extended::f64::units_of_measure::velocity::{MILLIMETER_PER_SECOND};
// This will create velocity of 10 m/s, default units are SI units
let v1: Velocity = Velocity::new(10.0);
// This method is constant and works also at compile time:
const SPEED_OF_SOUND: Velocity = Velocity::new(343.0);
// Various units of length are available at
// dimensional_quantity::si::extended::f64::units_of_measure::*
// Units are just constant dimensional quantities
// For example, MILLIMETER unit is defined as
// pub const MILLIMETER: Length = Length::new(1.0E-3);
// One way is using new_with_unit method:
let v2: Velocity = Velocity::new_with_unit(10_000.0, MILLIMETER_PER_SECOND);
// Another is just multiplying f64 with unit:
let v3: Velocity = 10_000.0 * MILLIMETER_PER_SECOND;
// Any of the above methods lead to the same result:
assert_eq!(v1, v2);
assert_eq!(v1, v3);
```
### Converting dimensional quantities back to f64
```rust
use dimensional_quantity::si::extended::f64::quantities::{Velocity};
use dimensional_quantity::si::extended::f64::units_of_measure::velocity::{MILLIMETER_PER_SECOND};
// Getting f64 value of dimensional quantity in SI units:
// Velocity of 10 m/s
let v1: Velocity = Velocity::new(10.0);
// Velocity of 343 m/s
const SPEED_OF_SOUND: Velocity = Velocity::new(343.0);
let v1_value_is_si_units = v1.get_with_si_unit();
// also possible at compile time:
const SPEED_OF_SOUND_IN_SI_UNITS:f64 = SPEED_OF_SOUND.get_with_si_unit();
assert_eq!(SPEED_OF_SOUND_IN_SI_UNITS, 343.0);
assert_eq!(v1_value_is_si_units, 10.0);
// Getting f64 value of dimensional quantity in arbitrary units is possible:
let v1_value_is_mm_per_second = v1.get_with_unit(MILLIMETER_PER_SECOND);
assert_eq!(v1_value_is_mm_per_second, 10_000.0);
```
### Mathematical operations with dimensional quantities
```rust
#![feature(generic_const_exprs)]
use dimensional_quantity::si::extended::f64::quantities::{Area, Energy, Length, Mass, ReciprocalLength, Velocity};
use dimensional_quantity::si::extended::f64::units_of_measure::length::{METER, MICROMETER};
use dimensional_quantity::si::extended::f64::units_of_measure::reciprocal_length::{RECIPROCAL_CENTIMETER};
use core::f64::consts::PI;
let width: Length = 5.0 * METER;
let height: Length = 8.0 * METER;
// Quantities can be multiplied or divided by f64 floating numbers,
// resulting in quantities of same dimension
let double_height: Length = height * 2.0;
let half_width = width / 2.0;
assert_eq!(double_height, Length::new(16.0));
assert_eq!(half_width, Length::new(2.5));
// Dividing f64 by dimensional quantity returns reciprocal quantity:
let red_light_wavelength: Length = 0.65 * MICROMETER;
let red_light_k: ReciprocalLength = 2.0 * PI / red_light_wavelength;
let red_light_k_in_reciprocal_cm = red_light_k.get_with_unit(RECIPROCAL_CENTIMETER);
assert_eq!(red_light_k_in_reciprocal_cm, 966_64.389_341_224_39);
// Most quantities of same dimension (except ones containing ThermodynamicTemperatures,
// that will be discussed below) can be added or subtracted:
let perimeter: Length = 2.0 * (width + height);
// AddAssign and SubAssign operators are supported
let mut width_minus_height = Length::new(0.0);
width_minus_height += width;
width_minus_height -= height;
assert_eq!(perimeter, Length::new(26.0));
// Dimensional quantities can be multiplied and divided:
let area_1: Area = width * height;
let area_2: Area = half_width * double_height;
assert_eq!(area_1, area_2);
let height_1 :Length = area_1 / width;
assert_eq!(height_1, height);
// Dimensional quantities can also be raised to an integer power during compile time
let v: Velocity = Velocity::new(10.0);
let m: Mass = Mass::new(5.0); // 5 kg
let e: Energy = m * v.powi::<2>() / 2.0;
assert_eq!(e, Energy::new(250.0));
```
Attempting to add, subtract, or assign quantities with mismatching dimensions will results in compile-time error:
```compile_fail
#![feature(generic_const_exprs)]
use dimensional_quantity::si::extended::{Area, Length};
let length: Length = Length::new(10.0);
let area: Area = length.powi<2>();
// Type mismatch: can not add Length and Area:
let fail_1 = length + area;
// Type mismatch: can not subtract Area from Length:
let fail_2 = length - area;
// Type mismatch: can not assign Area to Length:
let fail_3: Length = area;
```
### Creating new quantities and tests
If some quantity or unit are not implemented in [predefined dimensional quantities](si::extended::f64) and [predefined units of measure](si::extended::f64::units_of_measure),
then a new quantity and corresponding units of measure can be defined as follows:
```rust
#![feature(generic_const_exprs)]
use dimensional_quantity::si::extended::f64::quantities::{Information, Volume};
use dimensional_quantity::si::extended::f64::Quantity;
// New quantity: amount of information per unit of volume, standard unit of measure: Bit per cubic meter, B ⋅ m-3:
pub type VolumetricInformationDensity = Quantity<
-3, // Length
0, // Mass
0, // Time
0, // ElectricCurrent
0, // ThermodynamicTemperature
0, // AmountOfSubstance
0, // LuminousIntensity
0, // TemperatureInterval
0, // Angle
0, // SolidAngle
1, // Information
>;
pub const GIGABIT_PER_CUBIC_METER: VolumetricInformationDensity = VolumetricInformationDensity::new(1.0_E9);
let information_density_1: VolumetricInformationDensity = 5.0 * GIGABIT_PER_CUBIC_METER;
let information_density_2: VolumetricInformationDensity = Information::new(5.0_E9) / Volume::new(1.0);
assert_eq!(information_density_1, information_density_2);
```
## Definition of quantities and units of measure
Quantities and corresponding units are defined at `src/si/quantities_definition/*toml` files.
For example, definition of Area quantity is:
```toml
name = "Area"
symbol = "Area"
snake_case_name = "area"
short_dim_formula = "L2"
long_dim_formula = "Length2"
units_formula = "m2"
[dimensions]
length = 2
mass = 0
time = 0
electric_current = 0
thermodynamic_temperature = 0
temperature_interval = 0
amount_of_substance = 0
luminous_intensity = 0
angle = 0
solid_angle = 0
information = 0
[units.SQUARE_METER]
multiplier = "1.0E0"
symbol = "m²"
singular = "square meter"
plural = "square meters"
[units.SQUARE_CENTIMETER]
multiplier = "1.0_E-4"
symbol = "cm²"
singular = "square centimeter"
plural = "square centimeters"
```
## Licence
Licensed under either of Apache License, Version 2.0 or MIT license (LICENSE-MIT or ) at your option.
License: MIT OR Apache-2.0