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}