Skip to main content

conspire/units/scale/
mod.rs

1//! The scales a quantity may be named in.
2//!
3//! A unit says what kind of thing a number is; a scale says how big the number
4//! is against that kind. Only the kind is worth carrying, so a scale is spent
5//! where the quantity is built and the value is held in the base scale
6//! thereafter. Every constructor and reader below is a `const fn` over a factor
7//! known at compile time, so naming a scale costs nothing at run time.
8//!
9//! A quantity is read back the way it was written, by naming a scale rather
10//! than supplying one, so the base scale is only ever a matter between the
11//! quantity and itself.
12//!
13//! The base scales are SI, coherent throughout: metres, kilograms, seconds and
14//! kelvin, so pascals, newtons, joules and watts follow. A factor is only
15//! correct against the rest of that set, which is what the tests check by
16//! crossing from one unit to another rather than within one.
17
18#[cfg(test)]
19mod test;
20
21use super::{
22    Acceleration, Action, Amount, Area, Charge, Density, Dimensionless, Energy, Entropy, Force,
23    ForcePerLength, Length, Mass, MolarEnergy, MolarEntropy, PowerPerLengthTemperature, Rate,
24    ReciprocalAmount, ReciprocalTemperature, SpecificEnergy, Stress, StressPerLength, Temperature,
25    Time, Velocity, Viscosity, Volume,
26};
27use crate::math::{Quantity, TensorRank0};
28
29/// The kelvin a celsius is measured from.
30const ZERO_CELSIUS: TensorRank0 = 273.15;
31
32/// A scale named as the constructor that spends it.
33pub type Scale<U> = fn(TensorRank0) -> Quantity<U>;
34
35/// The scale a length is named in, where it is named in words rather than in a
36/// type.
37///
38/// A file that says what its numbers are in has to say it as text, so the words
39/// are matched here and nowhere else. A word that is not among them is nothing
40/// rather than a guess, since a length taken in the wrong scale is wrong by a
41/// factor rather than by a little, and a file that troubled to say something is
42/// the last place to stop listening.
43pub fn length_scale(label: &str) -> Option<Scale<Length>> {
44    Some(match label.trim().to_lowercase().as_str() {
45        "m" | "meter" | "meters" | "metre" | "metres" => Length::meters,
46        "mm" | "millimeter" | "millimeters" | "millimetre" | "millimetres" => Length::millimeters,
47        "cm" | "centimeter" | "centimeters" | "centimetre" | "centimetres" => Length::centimeters,
48        "um" | "\u{b5}m" | "\u{3bc}m" | "micrometer" | "micrometers" | "micrometre"
49        | "micrometres" | "micron" | "microns" => Length::micrometers,
50        "nm" | "nanometer" | "nanometers" | "nanometre" | "nanometres" => Length::nanometers,
51        "km" | "kilometer" | "kilometers" | "kilometre" | "kilometres" => Length::kilometers,
52        "in" | "inch" | "inches" => Length::inches,
53        "ft" | "foot" | "feet" => Length::feet,
54        _ => return None,
55    })
56}
57
58//
59// A reader is named alongside the constructor it undoes rather than derived
60// from it, since deriving one identifier from another is what a dependency
61// would be for and this crate has none.
62//
63macro_rules! scales {
64    ($($unit:ident { $($name:ident / $reader:ident = $factor:expr, $doc:expr),+ $(,)? })+) => {
65        $(
66            impl $unit {
67                $(
68                    #[doc = concat!("A quantity of ", $doc, ".")]
69                    pub const fn $name(value: TensorRank0) -> Quantity<$unit> {
70                        Quantity::new(value * $factor)
71                    }
72                )+
73            }
74            impl Quantity<$unit> {
75                $(
76                    #[doc = concat!("How many ", $doc, " the quantity is.")]
77                    pub const fn $reader(&self) -> TensorRank0 {
78                        self.value() / $factor
79                    }
80                )+
81            }
82        )+
83    };
84}
85
86scales!(
87    Length {
88        meters / in_meters = 1.0, "metres",
89        millimeters / in_millimeters = 1e-3, "millimetres",
90        micrometers / in_micrometers = 1e-6, "micrometres",
91        nanometers / in_nanometers = 1e-9, "nanometres",
92        centimeters / in_centimeters = 1e-2, "centimetres",
93        kilometers / in_kilometers = 1e3, "kilometres",
94        inches / in_inches = 2.54e-2, "inches",
95        feet / in_feet = 3.048e-1, "feet",
96    }
97    Area {
98        square_meters / in_square_meters = 1.0, "square metres",
99        square_millimeters / in_square_millimeters = 1e-6, "square millimetres",
100        square_centimeters / in_square_centimeters = 1e-4, "square centimetres",
101    }
102    Volume {
103        cubic_meters / in_cubic_meters = 1.0, "cubic metres",
104        cubic_millimeters / in_cubic_millimeters = 1e-9, "cubic millimetres",
105        cubic_centimeters / in_cubic_centimeters = 1e-6, "cubic centimetres",
106        liters / in_liters = 1e-3, "litres",
107    }
108    Time {
109        seconds / in_seconds = 1.0, "seconds",
110        milliseconds / in_milliseconds = 1e-3, "milliseconds",
111        microseconds / in_microseconds = 1e-6, "microseconds",
112        minutes / in_minutes = 6e1, "minutes",
113        hours / in_hours = 3.6e3, "hours",
114    }
115    Rate {
116        per_second / in_per_second = 1.0, "reciprocal seconds",
117        per_minute / in_per_minute = 1.0 / 6e1, "reciprocal minutes",
118        per_hour / in_per_hour = 1.0 / 3.6e3, "reciprocal hours",
119        hertz / in_hertz = 1.0, "hertz",
120    }
121    Stress {
122        pascals / in_pascals = 1.0, "pascals",
123        kilopascals / in_kilopascals = 1e3, "kilopascals",
124        megapascals / in_megapascals = 1e6, "megapascals",
125        gigapascals / in_gigapascals = 1e9, "gigapascals",
126        bars / in_bars = 1e5, "bars",
127        psi / in_psi = 6.894_757_293_168_361e3, "pounds per square inch",
128        ksi / in_ksi = 6.894_757_293_168_361e6, "kips per square inch",
129    }
130    Force {
131        newtons / in_newtons = 1.0, "newtons",
132        millinewtons / in_millinewtons = 1e-3, "millinewtons",
133        kilonewtons / in_kilonewtons = 1e3, "kilonewtons",
134        meganewtons / in_meganewtons = 1e6, "meganewtons",
135        pounds_force / in_pounds_force = 4.448_221_615_260_5, "pounds force",
136    }
137    Energy {
138        joules / in_joules = 1.0, "joules",
139        millijoules / in_millijoules = 1e-3, "millijoules",
140        kilojoules / in_kilojoules = 1e3, "kilojoules",
141        electronvolts / in_electronvolts = 1.602_176_634e-19, "electronvolts",
142    }
143    Entropy {
144        joules_per_kelvin / in_joules_per_kelvin = 1.0, "joules per kelvin",
145    }
146    Action {
147        joule_seconds / in_joule_seconds = 1.0, "joule seconds",
148    }
149    Amount {
150        moles / in_moles = 1.0, "moles",
151        millimoles / in_millimoles = 1e-3, "millimoles",
152    }
153    ReciprocalAmount {
154        per_mole / in_per_mole = 1.0, "reciprocal moles",
155    }
156    MolarEntropy {
157        joules_per_mole_kelvin / in_joules_per_mole_kelvin = 1.0, "joules per mole kelvin",
158    }
159    MolarEnergy {
160        joules_per_mole / in_joules_per_mole = 1.0, "joules per mole",
161        kilojoules_per_mole / in_kilojoules_per_mole = 1e3, "kilojoules per mole",
162    }
163    ForcePerLength {
164        newtons_per_meter / in_newtons_per_meter = 1.0, "newtons per metre",
165        piconewtons_per_nanometer / in_piconewtons_per_nanometer = 1e-3,
166            "piconewtons per nanometre",
167    }
168    Velocity {
169        meters_per_second / in_meters_per_second = 1.0, "metres per second",
170        millimeters_per_second / in_millimeters_per_second = 1e-3, "millimetres per second",
171        kilometers_per_hour / in_kilometers_per_hour = 1.0 / 3.6, "kilometres per hour",
172    }
173    Acceleration {
174        meters_per_second_squared / in_meters_per_second_squared = 1.0,
175            "metres per second squared",
176        standard_gravities / in_standard_gravities = 9.806_65, "standard gravities",
177    }
178    Mass {
179        kilograms / in_kilograms = 1.0, "kilograms",
180        grams / in_grams = 1e-3, "grams",
181        milligrams / in_milligrams = 1e-6, "milligrams",
182        tonnes / in_tonnes = 1e3, "tonnes",
183        pounds_mass / in_pounds_mass = 4.535_923_7e-1, "pounds mass",
184    }
185    Density {
186        kilograms_per_cubic_meter / in_kilograms_per_cubic_meter = 1.0,
187            "kilograms per cubic metre",
188        grams_per_cubic_centimeter / in_grams_per_cubic_centimeter = 1e3,
189            "grams per cubic centimetre",
190        tonnes_per_cubic_millimeter / in_tonnes_per_cubic_millimeter = 1e12,
191            "tonnes per cubic millimetre",
192    }
193    SpecificEnergy {
194        joules_per_kilogram / in_joules_per_kilogram = 1.0, "joules per kilogram",
195    }
196    Charge {
197        coulombs / in_coulombs = 1.0, "coulombs",
198    }
199    Viscosity {
200        pascal_seconds / in_pascal_seconds = 1.0, "pascal seconds",
201        poise / in_poise = 1e-1, "poise",
202        centipoise / in_centipoise = 1e-3, "centipoise",
203    }
204    Temperature {
205        kelvin / in_kelvin = 1.0, "kelvin",
206    }
207    ReciprocalTemperature {
208        per_kelvin / in_per_kelvin = 1.0, "reciprocal kelvin",
209        per_celsius / in_per_celsius = 1.0, "reciprocal degrees celsius",
210    }
211    StressPerLength {
212        pascals_per_meter / in_pascals_per_meter = 1.0, "pascals per metre",
213        megapascals_per_millimeter / in_megapascals_per_millimeter = 1e9,
214            "megapascals per millimetre",
215    }
216    PowerPerLengthTemperature {
217        watts_per_meter_kelvin / in_watts_per_meter_kelvin = 1.0, "watts per metre kelvin",
218    }
219    Dimensionless {
220        percent / in_percent = 1e-2, "percent",
221    }
222);
223
224impl Dimensionless {
225    /// A quantity of no unit at all.
226    ///
227    /// Named here rather than in the table, since a row names a reader as well
228    /// and there is no word to read a ratio back in. A quantity of no unit is
229    /// the number it holds, which [`value`](Quantity::value) already gives.
230    pub const fn of(value: TensorRank0) -> Quantity<Dimensionless> {
231        Quantity::new(value)
232    }
233}
234
235impl Temperature {
236    /// A quantity of degrees celsius.
237    ///
238    /// A celsius is a kelvin offset rather than scaled, so this names a
239    /// temperature and not a difference between two. A difference is the same
240    /// number of either, and is the [`kelvin`](Self::kelvin) it already is.
241    pub const fn celsius(value: TensorRank0) -> Quantity<Temperature> {
242        Quantity::new(value + ZERO_CELSIUS)
243    }
244    /// A quantity of degrees fahrenheit.
245    ///
246    /// Offset as well as scaled, so the same caution applies as for
247    /// [`celsius`](Self::celsius).
248    pub const fn fahrenheit(value: TensorRank0) -> Quantity<Temperature> {
249        Quantity::new((value - 32.0) * 5.0 / 9.0 + ZERO_CELSIUS)
250    }
251}
252
253impl Quantity<Temperature> {
254    /// How many degrees celsius the temperature is.
255    pub const fn in_celsius(&self) -> TensorRank0 {
256        self.value() - ZERO_CELSIUS
257    }
258    /// How many degrees fahrenheit the temperature is.
259    pub const fn in_fahrenheit(&self) -> TensorRank0 {
260        (self.value() - ZERO_CELSIUS) * 9.0 / 5.0 + 32.0
261    }
262}