Skip to main content

lcdm_core/
units.rs

1// lcdm-core/src/units.rs
2
3//! Strongly typed units for cosmology to prevent mixing up quantities.
4
5use std::ops::{Add, Div, Mul, Sub};
6
7macro_rules! define_unit {
8    ($name:ident, $desc:expr) => {
9        /// Newtype wrapper around `f64` representing a strongly-typed cosmological quantity.
10        #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
11        #[doc = $desc]
12        pub struct $name(pub f64);
13
14        impl $name {
15            /// Construct a new value of this unit type.
16            pub fn new(val: f64) -> Self {
17                Self(val)
18            }
19
20            /// Extract the underlying numeric value.
21            pub fn value(&self) -> f64 {
22                self.0
23            }
24        }
25
26        // ---------------------------------------------------------------------
27        // Basic arithmetic (same-unit operations)
28        // ---------------------------------------------------------------------
29
30        impl Add for $name {
31            type Output = Self;
32            fn add(self, other: Self) -> Self {
33                Self(self.0 + other.0)
34            }
35        }
36
37        impl Sub for $name {
38            type Output = Self;
39            fn sub(self, other: Self) -> Self {
40                Self(self.0 - other.0)
41            }
42        }
43
44        // ---------------------------------------------------------------------
45        // Scalar multiplication / division
46        // ---------------------------------------------------------------------
47
48        impl Mul<f64> for $name {
49            type Output = Self;
50            fn mul(self, rhs: f64) -> Self {
51                Self(self.0 * rhs)
52            }
53        }
54
55        impl Mul<$name> for f64 {
56            type Output = $name;
57            fn mul(self, rhs: $name) -> $name {
58                $name(self * rhs.0)
59            }
60        }
61
62        impl Div<f64> for $name {
63            type Output = Self;
64            fn div(self, rhs: f64) -> Self {
65                Self(self.0 / rhs)
66            }
67        }
68    };
69}
70
71define_unit!(Mpc, "Distance in megaparsecs (Mpc).");
72define_unit!(Gyr, "Time in gigayears (Gyr).");
73define_unit!(Redshift, "Cosmological redshift z (dimensionless).");
74define_unit!(ScaleFactor, "Scale factor a (dimensionless).");
75define_unit!(KmPerSecMpc, "Hubble parameter in km/s/Mpc.");
76
77// -----------------------------------------------------------------------------
78// Conversions between redshift and scale factor
79// -----------------------------------------------------------------------------
80
81impl From<Redshift> for ScaleFactor {
82    /// Convert redshift to scale factor via a = 1 / (1 + z).
83    fn from(z: Redshift) -> Self {
84        ScaleFactor(1.0 / (1.0 + z.0))
85    }
86}
87
88impl From<ScaleFactor> for Redshift {
89    /// Convert scale factor to redshift via z = 1 / a - 1.
90    fn from(a: ScaleFactor) -> Self {
91        Redshift(1.0 / a.0 - 1.0)
92    }
93}