frequenz_microgrid/quantity/
energy.rs

1// License: MIT
2// Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3
4//! This module defines the `Energy` quantity and its operations.
5
6use super::Power;
7
8qty_ctor! {
9    #[doc = "A physical quantity representing energy."]
10    Energy => {
11        (from_milliwatthours, as_milliwatthours, "mWh", 1e-3),
12        (from_watthours, as_watthours, "Wh", 1e0),
13        (from_kilowatthours, as_kilowatthours, "kWh", 1e3),
14        (from_megawatthours, as_megawatthours, "MWh", 1e6),
15        (from_gigawatthours, as_gigawatthours, "GWh", 1e9),
16    }
17}
18
19impl std::ops::Div<Power> for Energy {
20    type Output = std::time::Duration;
21
22    fn div(self, power: Power) -> Self::Output {
23        let seconds = (self.as_watthours() / power.as_watts()) * 3600.0;
24        std::time::Duration::from_secs_f32(seconds)
25    }
26}
27
28impl std::ops::Div<std::time::Duration> for Energy {
29    type Output = Power;
30
31    fn div(self, duration: std::time::Duration) -> Self::Output {
32        Power::from_watts(self.as_watthours() * 3600.0 / duration.as_secs_f32())
33    }
34}
35
36#[cfg(test)]
37mod tests {
38    use crate::quantity::{Percentage, Power, Quantity as _, test_utils::assert_f32_eq};
39
40    use super::Energy;
41
42    #[test]
43    fn test_energy() {
44        let energy_1 = Energy::from_watthours(1000.0);
45
46        assert_f32_eq(energy_1.as_milliwatthours(), 1_000_000.0);
47        assert_f32_eq(energy_1.as_watthours(), 1000.0);
48        assert_f32_eq(energy_1.as_kilowatthours(), 1.0);
49        assert_f32_eq(energy_1.as_megawatthours(), 0.001);
50        assert_f32_eq(energy_1.as_gigawatthours(), 0.000_001);
51
52        let energy_2 = Energy::from_milliwatthours(1_200_000.0);
53        assert_f32_eq(energy_2.as_watthours(), 1200.0);
54
55        let energy_2 = Energy::from_kilowatthours(1.2);
56        assert_f32_eq(energy_2.as_watthours(), 1200.0);
57
58        let energy_2 = Energy::from_megawatthours(0.0012);
59        assert_f32_eq(energy_2.as_watthours(), 1200.0);
60
61        let energy_2 = Energy::from_gigawatthours(0.000_001_2);
62        assert_f32_eq(energy_2.as_watthours(), 1200.0);
63
64        assert!(energy_1 < energy_2);
65        assert!(energy_2 > energy_1);
66
67        assert_f32_eq((energy_1 + energy_2).as_watthours(), 2200.0);
68        assert_f32_eq((energy_2 - energy_1).as_watthours(), 200.0);
69        assert_f32_eq((energy_2 * 2.0).as_watthours(), 2400.0);
70        assert_f32_eq(
71            (energy_2 * Percentage::from_percentage(50.0)).as_watthours(),
72            600.0,
73        );
74        assert_f32_eq((energy_2 / 3.0).as_watthours(), 400.0);
75        assert_f32_eq(energy_2 / energy_1, 1.2);
76
77        assert_f32_eq(Energy::zero().as_watthours(), 0.0);
78    }
79
80    #[test]
81    fn test_energy_power_duration() {
82        let energy = Energy::from_kilowatthours(1.0);
83        let power = Power::from_kilowatts(0.5);
84
85        let duration = energy / power;
86        assert_f32_eq(duration.as_secs_f32(), 7200.0);
87
88        let power_calculated = energy / duration;
89        assert_f32_eq(power_calculated.as_kilowatts(), 0.5);
90    }
91
92    #[test]
93    fn test_energy_formatting() {
94        let s = |value| Energy::from_watthours(value).to_string();
95        let p = |value, prec| format!("{:.prec$}", Energy::from_watthours(value), prec = prec);
96
97        assert_eq!(s(0.0), "0 mWh");
98        assert_eq!(s(1.558), "1.558 Wh");
99        assert_eq!(p(1.558, 1), "1.6 Wh");
100
101        assert_eq!(s(0.001558), "1.558 mWh");
102        assert_eq!(p(0.001558, 1), "1.6 mWh");
103
104        assert_eq!(s(1.5508), "1.551 Wh");
105        assert_eq!(p(1.5508, 5), "1.5508 Wh");
106
107        assert_eq!(s(0.0015508), "1.551 mWh");
108        assert_eq!(p(0.0015508, 5), "1.5508 mWh");
109
110        assert_eq!(s(1030.04487), "1.03 kWh");
111        assert_eq!(p(1030.04487, 1), "1 kWh");
112
113        assert_eq!(s(2_030_022.0), "2.03 MWh");
114        assert_eq!(s(2_030_022_123.0), "2.03 GWh");
115        assert_eq!(p(2_030_022_123.0, 6), "2.030022 GWh");
116
117        assert_eq!(s(-1.558), "-1.558 Wh");
118        assert_eq!(p(-1.558, 1), "-1.6 Wh");
119
120        assert_eq!(s(-1030.04487), "-1.03 kWh");
121        assert_eq!(p(-1030.04487, 1), "-1 kWh");
122
123        assert_eq!(s(-2_030_022.0), "-2.03 MWh");
124        assert_eq!(p(-2_030_022.0, 1), "-2 MWh");
125
126        assert_eq!(s(-2_030_022_123.0), "-2.03 GWh");
127        assert_eq!(p(-2_030_022_123.0, 6), "-2.030022 GWh");
128    }
129}