rustyms/
system.rs

1//! The measurement system used in this crate.
2//! A redefinition of the important SI units for them to be stored in a more sensible base unit for MS purposes.
3
4#![allow(clippy::non_canonical_clone_impl)]
5#![allow(clippy::ignored_unit_patterns)]
6#![allow(clippy::float_cmp)]
7use std::ops::{Deref, DerefMut};
8
9use num_traits::Zero;
10use uom::*;
11
12use serde::{Deserialize, Serialize};
13
14use crate::helper_functions;
15
16pub use self::f64::*;
17
18/// The mass quantity in dalton
19#[macro_use]
20pub mod mass {
21    use uom::*;
22
23    quantity! {
24        /// Mass in dalton
25        quantity: Mass; "mass";
26        /// Mass
27        dimension: Q< P1, Z0, Z0>;
28        units {
29            @millidalton: 0.001; "mDa", "millidalton", "millidaltons";
30            @dalton: 1.0; "Da", "dalton", "daltons";
31            @kilodalton: 1_000.0; "kDa", "kilodalton", "kilodaltons";
32            @megadalton: 1_000_000.0; "MDa", "megadalton", "megadaltons";
33        }
34    }
35}
36
37/// The charge quantity in atomic units of charge aka electrons
38#[macro_use]
39pub mod charge {
40    use uom::*;
41
42    quantity! {
43        /// Charge in electrons
44        quantity: Charge; "charge";
45        /// Charge
46        dimension: Q< Z0, P1, Z0>;
47        units {
48            @e: 1.0; "e", "atomic_unit_of_charge", "atomic_units_of_charge";
49        }
50    }
51}
52
53/// The time quantity in seconds
54#[macro_use]
55pub mod time {
56    use uom::*;
57
58    quantity! {
59        /// Time (s)
60        quantity: Time; "time";
61        /// Time
62        dimension: Q< Z0, Z0, P1>;
63        units {
64            @ns: 0.000_000_001; "ns", "nanosecond", "nanoseconds";
65            @μs: 0.000_001; "μs", "microsecond", "microseconds";
66            @ms: 0.001; "ms", "millisecond", "milliseconds";
67            @s: 1.0; "s", "second", "seconds";
68            @min: 60.0; "min", "minute", "minutes";
69            @h: 3600.0; "h", "hour", "hours";
70        }
71    }
72}
73
74/// The mass over charge quantity
75#[macro_use]
76pub mod mass_over_charge {
77    use uom::*;
78
79    quantity! {
80        /// Mass over charge (da/e)
81        quantity: MassOverCharge; "mass_over_charge";
82        /// Mass over charge (da/e)
83        dimension: Q< P1, N1, Z0>;
84        units {
85            @thomson: 1.0; "Th", "thomson", "thomson";
86        }
87    }
88}
89
90/// A unit less quantity for use in general calculations
91#[macro_use]
92pub mod ratio {
93    use uom::*;
94
95    quantity! {
96        /// Unit less quantity for general calculations
97        quantity: Ratio; "ratio";
98        /// Unit less quantity for general calculations
99        dimension: Q< Z0, Z0, Z0>;
100        units {
101            @fraction: 1.0; "⅟", "fraction", "fraction";
102            @percent: 0.01; "%", "percent", "percent";
103            @promille: 0.01; "‰", "promille", "promille";
104            @ppm: 0.000_001; "ppm", "ppm", "ppm";
105            @ppb: 0.000_000_001; "ppb", "ppb", "ppb";
106            @ppt: 0.000_000_000_001; "ppt", "ppt", "ppt";
107            @ppq: 0.000_000_000_000_001; "ppq", "ppq", "ppq";
108        }
109    }
110}
111
112system! {
113    /// Quantities
114    #[doc(hidden)]
115    quantities: Q {
116        mass: dalton, M;
117        charge: e, C;
118        time: s, T;
119    }
120
121    /// Units
122    units: U {
123        mod mass::Mass,
124        mod charge::Charge,
125        mod time::Time,
126        mod mass_over_charge::MassOverCharge,
127        mod ratio::Ratio,
128    }
129}
130
131/// The whole system with f64 as storage type
132#[allow(unused_imports)]
133pub mod f64 {
134    mod mks {
135        pub(super) use super::super::*;
136    }
137
138    Q!(self::mks, f64);
139
140    pub use super::charge::e;
141    pub use super::mass::dalton;
142    pub use super::mass_over_charge::thomson;
143    pub use super::ratio::fraction;
144    pub use super::time::s;
145
146    /// Annotate the given number as being in Da
147    pub fn da(v: f64) -> Mass {
148        Mass::new::<dalton>(v)
149    }
150}
151
152/// All quantities with usize as underlying type
153#[allow(unused_imports)]
154pub mod usize {
155    mod mks {
156        pub(super) use super::super::*;
157    }
158
159    Q!(self::mks, usize);
160
161    pub use super::charge::e;
162    pub use super::mass::dalton;
163    pub use super::mass_over_charge::thomson;
164    pub use super::ratio::fraction;
165    pub use super::time::s;
166}
167
168/// All quantities with isize as underlying type
169#[allow(unused_imports)]
170pub mod isize {
171    mod mks {
172        pub(super) use super::super::*;
173    }
174
175    Q!(self::mks, isize);
176
177    pub use super::charge::e;
178    pub use super::mass::dalton;
179    pub use super::mass_over_charge::thomson;
180    pub use super::ratio::fraction;
181    pub use super::time::s;
182}
183
184impl usize::Charge {
185    /// Convert a usize charge to f64 for computations
186    pub fn to_float(self) -> Charge {
187        Charge::new::<e>(self.value as f64)
188    }
189}
190
191impl isize::Charge {
192    /// Convert an isize charge to f64 for computations
193    pub fn to_float(self) -> Charge {
194        Charge::new::<e>(self.value as f64)
195    }
196}
197
198impl MassOverCharge {
199    /// Absolute ppm error between this mz and the given other
200    pub fn ppm(self, b: Self) -> Ratio {
201        (self - b).abs() / self.abs()
202    }
203
204    /// Signed ppm error between this mz and the given other
205    pub fn signed_ppm(self, b: Self) -> Ratio {
206        (self - b) / self
207    }
208}
209
210impl Mass {
211    /// Absolute ppm error between this mass and the given other
212    pub fn ppm(self, b: Self) -> Ratio {
213        (self - b).abs() / self.abs()
214    }
215
216    /// Signed ppm error between this mass and the given other
217    pub fn signed_ppm(self, b: Self) -> Ratio {
218        (self - b) / self
219    }
220}
221
222/// A wrapper around [`Ratio`] which implements Eq/Ord/Hash to help in auto deriving these on other structs.
223#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
224pub struct OrderedRatio(Ratio);
225
226impl OrderedRatio {
227    /// Use the zero from [`Ratio`] itself
228    pub fn zero() -> Self {
229        Self(Ratio::zero())
230    }
231
232    /// Get a normal [`Ratio`]
233    #[allow(dead_code)]
234    pub fn into_inner(self) -> Ratio {
235        self.0
236    }
237}
238
239impl Default for OrderedRatio {
240    fn default() -> Self {
241        Self::zero()
242    }
243}
244
245impl From<Ratio> for OrderedRatio {
246    fn from(value: Ratio) -> Self {
247        Self(value)
248    }
249}
250
251impl Deref for OrderedRatio {
252    type Target = Ratio;
253    fn deref(&self) -> &Self::Target {
254        &self.0
255    }
256}
257
258impl DerefMut for OrderedRatio {
259    fn deref_mut(&mut self) -> &mut Self::Target {
260        &mut self.0
261    }
262}
263
264impl Ord for OrderedRatio {
265    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
266        self.0.value.total_cmp(&other.0.value)
267    }
268}
269
270impl PartialOrd for OrderedRatio {
271    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
272        Some(self.cmp(other))
273    }
274}
275
276impl Eq for OrderedRatio {}
277
278impl PartialEq for OrderedRatio {
279    fn eq(&self, other: &Self) -> bool {
280        self.cmp(other).is_eq()
281    }
282}
283
284impl std::hash::Hash for OrderedRatio {
285    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
286        helper_functions::f64_bits(self.0.value).hash(state);
287    }
288}
289
290/// A wrapper around [`Mass`] which implements Eq/Ord/Hash to help in auto deriving these on other structs.
291#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
292pub struct OrderedMass(Mass);
293
294impl OrderedMass {
295    /// Use the zero from [`Mass`] itself
296    pub fn zero() -> Self {
297        Self(Mass::zero())
298    }
299
300    /// Get a normal [`Mass`]
301    #[allow(dead_code)]
302    pub fn into_inner(self) -> Mass {
303        self.0
304    }
305}
306
307impl Default for OrderedMass {
308    fn default() -> Self {
309        Self::zero()
310    }
311}
312
313impl From<Mass> for OrderedMass {
314    fn from(value: Mass) -> Self {
315        Self(value)
316    }
317}
318
319impl Deref for OrderedMass {
320    type Target = Mass;
321    fn deref(&self) -> &Self::Target {
322        &self.0
323    }
324}
325
326impl DerefMut for OrderedMass {
327    fn deref_mut(&mut self) -> &mut Self::Target {
328        &mut self.0
329    }
330}
331
332impl Ord for OrderedMass {
333    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
334        self.0.value.total_cmp(&other.0.value)
335    }
336}
337
338impl PartialOrd for OrderedMass {
339    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
340        Some(self.cmp(other))
341    }
342}
343
344impl Eq for OrderedMass {}
345
346impl PartialEq for OrderedMass {
347    fn eq(&self, other: &Self) -> bool {
348        self.cmp(other).is_eq()
349    }
350}
351
352impl std::hash::Hash for OrderedMass {
353    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
354        helper_functions::f64_bits(self.0.value).hash(state);
355    }
356}
357
358/// A wrapper around [`Mass`] which implements Eq/Ord/Hash to help in auto deriving these on other structs.
359#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
360pub struct OrderedMassOverCharge(MassOverCharge);
361
362impl OrderedMassOverCharge {
363    /// Use the zero from [`MassOverCharge`] itself
364    pub fn zero() -> Self {
365        Self(MassOverCharge::zero())
366    }
367
368    /// Get a normal [`MassOverCharge`]
369    #[allow(dead_code)]
370    pub fn into_inner(self) -> MassOverCharge {
371        self.0
372    }
373}
374
375impl Default for OrderedMassOverCharge {
376    fn default() -> Self {
377        Self::zero()
378    }
379}
380
381impl From<MassOverCharge> for OrderedMassOverCharge {
382    fn from(value: MassOverCharge) -> Self {
383        Self(value)
384    }
385}
386
387impl Deref for OrderedMassOverCharge {
388    type Target = MassOverCharge;
389    fn deref(&self) -> &Self::Target {
390        &self.0
391    }
392}
393
394impl DerefMut for OrderedMassOverCharge {
395    fn deref_mut(&mut self) -> &mut Self::Target {
396        &mut self.0
397    }
398}
399
400impl Ord for OrderedMassOverCharge {
401    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
402        self.0.value.total_cmp(&other.0.value)
403    }
404}
405
406impl PartialOrd for OrderedMassOverCharge {
407    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
408        Some(self.cmp(other))
409    }
410}
411
412impl Eq for OrderedMassOverCharge {}
413
414impl PartialEq for OrderedMassOverCharge {
415    fn eq(&self, other: &Self) -> bool {
416        self.cmp(other).is_eq()
417    }
418}
419
420impl std::hash::Hash for OrderedMassOverCharge {
421    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
422        helper_functions::f64_bits(self.0.value).hash(state);
423    }
424}
425
426/// A wrapper around [`Time`] which implements Eq/Ord/Hash to help in auto deriving these on other structs.
427#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
428pub struct OrderedTime(Time);
429
430impl OrderedTime {
431    /// Use the zero from [`Time`] itself
432    pub fn zero() -> Self {
433        Self(Time::zero())
434    }
435
436    /// Get a normal [`Time`]
437    #[allow(dead_code)]
438    pub fn into_inner(self) -> Time {
439        self.0
440    }
441}
442
443impl Default for OrderedTime {
444    fn default() -> Self {
445        Self::zero()
446    }
447}
448
449impl From<Time> for OrderedTime {
450    fn from(value: Time) -> Self {
451        Self(value)
452    }
453}
454
455impl Deref for OrderedTime {
456    type Target = Time;
457    fn deref(&self) -> &Self::Target {
458        &self.0
459    }
460}
461
462impl DerefMut for OrderedTime {
463    fn deref_mut(&mut self) -> &mut Self::Target {
464        &mut self.0
465    }
466}
467
468impl Ord for OrderedTime {
469    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
470        self.0.value.total_cmp(&other.0.value)
471    }
472}
473
474impl PartialOrd for OrderedTime {
475    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
476        Some(self.cmp(other))
477    }
478}
479
480impl Eq for OrderedTime {}
481
482impl PartialEq for OrderedTime {
483    fn eq(&self, other: &Self) -> bool {
484        self.cmp(other).is_eq()
485    }
486}
487
488impl std::hash::Hash for OrderedTime {
489    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
490        helper_functions::f64_bits(self.0.value).hash(state);
491    }
492}