frequenz_microgrid/quantity/
power.rs

1// License: MIT
2// Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3
4//! This module defines the `Power` quantity and its operations.
5
6use super::{Current, Energy, Voltage};
7
8qty_ctor! {
9    #[doc = "A physical quantity representing active power."]
10    Power => {
11        (from_milliwatts, as_milliwatts, "mW", 1e-3),
12        (from_watts, as_watts, "W", 1e0),
13        (from_kilowatts, as_kilowatts, "kW", 1e3),
14        (from_megawatts, as_megawatts, "MW", 1e6),
15        (from_gigawatts, as_gigawatts, "GW", 1e9),
16    }
17}
18
19impl std::ops::Div<Voltage> for Power {
20    type Output = Current;
21
22    fn div(self, voltage: Voltage) -> Self::Output {
23        Current::from_amperes(self.as_watts() / voltage.as_volts())
24    }
25}
26
27impl std::ops::Div<Current> for Power {
28    type Output = Voltage;
29
30    fn div(self, current: Current) -> Self::Output {
31        Voltage::from_volts(self.as_watts() / current.as_amperes())
32    }
33}
34
35impl std::ops::Mul<std::time::Duration> for Power {
36    type Output = Energy;
37
38    fn mul(self, duration: std::time::Duration) -> Self::Output {
39        Energy::from_watthours(self.as_watts() * duration.as_secs_f32() / 3600.0)
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::Power;
46    use super::{Current, Energy, Voltage};
47    use crate::quantity::Percentage;
48    use crate::quantity::{Quantity as _, test_utils::assert_f32_eq};
49
50    #[test]
51    fn test_power() {
52        let power_1 = Power::from_watts(1000.0);
53        assert_f32_eq(power_1.as_milliwatts(), 1_000_000.0);
54        assert_f32_eq(power_1.as_watts(), 1000.0);
55        assert_f32_eq(power_1.as_kilowatts(), 1.0);
56        assert_f32_eq(power_1.as_megawatts(), 0.001);
57        assert_f32_eq(power_1.as_gigawatts(), 0.000_001);
58
59        let power_2 = Power::from_milliwatts(1_200_000.0);
60        assert_f32_eq(power_2.as_watts(), 1200.0);
61        let power_2 = Power::from_kilowatts(1.2);
62        assert_f32_eq(power_2.as_watts(), 1200.0);
63        let power_2 = Power::from_megawatts(0.001_2);
64        assert_f32_eq(power_2.as_watts(), 1200.0);
65        let power_2 = Power::from_gigawatts(0.000_001_2);
66        assert_f32_eq(power_2.as_watts(), 1200.0);
67
68        assert!(power_1 < power_2);
69        assert!(power_2 > power_1);
70
71        assert_f32_eq((power_1 + power_2).as_watts(), 2200.0);
72        assert_f32_eq((power_2 - power_1).as_watts(), 200.0);
73        assert_f32_eq((power_2 * 2.0).as_watts(), 2400.0);
74        assert_f32_eq((power_2 / 2.0).as_watts(), 600.0);
75        assert_f32_eq(
76            (power_2 * Percentage::from_percentage(80.0)).as_watts(),
77            960.0,
78        );
79        assert_f32_eq(power_2 / power_1, 1.2);
80
81        assert_f32_eq(Power::zero().as_watts(), 0.0);
82    }
83
84    #[test]
85    fn test_power_voltage_current() {
86        let power = Power::from_kilowatts(2.0);
87        let voltage = Voltage::from_volts(400.0);
88        let current = Current::from_amperes(5.0);
89
90        let computed_current = power / voltage;
91        assert_f32_eq(computed_current.as_amperes(), 5.0);
92
93        let computed_voltage = power / current;
94        assert_f32_eq(computed_voltage.as_volts(), 400.0);
95    }
96
97    #[test]
98    fn test_power_energy_duration() {
99        let power = Power::from_kilowatts(0.5);
100        let duration = std::time::Duration::from_secs(7200);
101        let energy = Energy::from_kilowatthours(1.0);
102
103        let computed_energy = power * duration;
104        assert_f32_eq(computed_energy.as_kilowatthours(), 1.0);
105
106        let computed_power = energy / duration;
107        assert_f32_eq(computed_power.as_kilowatts(), 0.5);
108    }
109
110    #[test]
111    fn test_power_formatting() {
112        let s = |value| Power::from_watts(value).to_string();
113        let p = |value, prec| format!("{:.prec$}", Power::from_watts(value), prec = prec);
114        assert_eq!(s(0.0), "0 mW");
115
116        assert_eq!(s(1.558), "1.558 W");
117        assert_eq!(p(1.558, 1), "1.6 W");
118
119        assert_eq!(s(1.5508), "1.551 W");
120        assert_eq!(p(1.5508, 5), "1.5508 W");
121
122        assert_eq!(s(2030.0), "2.03 kW");
123
124        assert_eq!(s(2_030_022.0), "2.03 MW");
125        assert_eq!(s(2_030_022_123.0), "2.03 GW");
126        assert_eq!(p(2_030_022_123.0, 6), "2.030022 GW");
127
128        assert_eq!(s(-1.558), "-1.558 W");
129        assert_eq!(p(-1.558, 1), "-1.6 W");
130
131        assert_eq!(s(-2030.0), "-2.03 kW");
132        assert_eq!(p(-2030.0, 1), "-2 kW");
133
134        assert_eq!(s(-2_030_022.0), "-2.03 MW");
135        assert_eq!(s(-2_030_022_123.0), "-2.03 GW");
136        assert_eq!(p(-2_030_022_123.0, 6), "-2.030022 GW");
137    }
138}