frequenz_microgrid/quantity/
reactive_power.rs

1// License: MIT
2// Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3
4//! This module defines the `ReactivePower` quantity and its operations.
5
6use super::{Current, Voltage};
7
8qty_ctor! {
9    #[doc = "A physical quantity representing reactive power."]
10    ReactivePower => {
11        (from_millivolt_amperes_reactive, as_millivolt_amperes_reactive, "mVAR", 1e-3),
12        (from_volt_amperes_reactive, as_volt_amperes_reactive, "VAR", 1e0),
13        (from_kilovolt_amperes_reactive, as_kilovolt_amperes_reactive, "kVAR", 1e3),
14        (from_megavolt_amperes_reactive, as_megavolt_amperes_reactive, "MVAR", 1e6),
15        (from_gigavolt_amperes_reactive, as_gigavolt_amperes_reactive, "GVAR", 1e9),
16    }
17}
18
19impl std::ops::Div<Voltage> for ReactivePower {
20    type Output = Current;
21
22    fn div(self, voltage: Voltage) -> Self::Output {
23        Current::from_amperes(self.as_volt_amperes_reactive() / voltage.as_volts())
24    }
25}
26
27impl std::ops::Div<Current> for ReactivePower {
28    type Output = Voltage;
29
30    fn div(self, current: Current) -> Self::Output {
31        Voltage::from_volts(self.as_volt_amperes_reactive() / current.as_amperes())
32    }
33}
34
35#[cfg(test)]
36mod tests {
37    use crate::quantity::{
38        Percentage, Quantity as _, ReactivePower, Voltage, test_utils::assert_f32_eq,
39    };
40
41    #[test]
42    fn test_reactive_power() {
43        let reactive_power_1 = ReactivePower::from_volt_amperes_reactive(1000.0);
44        assert_f32_eq(
45            reactive_power_1.as_millivolt_amperes_reactive(),
46            1_000_000.0,
47        );
48        assert_f32_eq(reactive_power_1.as_volt_amperes_reactive(), 1000.0);
49        assert_f32_eq(reactive_power_1.as_kilovolt_amperes_reactive(), 1.0);
50        assert_f32_eq(reactive_power_1.as_megavolt_amperes_reactive(), 0.001);
51        assert_f32_eq(reactive_power_1.as_gigavolt_amperes_reactive(), 0.000_001);
52
53        let reactive_power_2 = ReactivePower::from_millivolt_amperes_reactive(1_200_000.0);
54        assert_f32_eq(reactive_power_2.as_volt_amperes_reactive(), 1200.0);
55        let reactive_power_2 = ReactivePower::from_kilovolt_amperes_reactive(1.2);
56        assert_f32_eq(reactive_power_2.as_volt_amperes_reactive(), 1200.0);
57        let reactive_power_2 = ReactivePower::from_megavolt_amperes_reactive(0.0012);
58        assert_f32_eq(reactive_power_2.as_volt_amperes_reactive(), 1200.0);
59        let reactive_power_2 = ReactivePower::from_gigavolt_amperes_reactive(0.000_001_2);
60        assert_f32_eq(reactive_power_2.as_volt_amperes_reactive(), 1200.0);
61
62        assert!(reactive_power_1 < reactive_power_2);
63        assert!(reactive_power_2 > reactive_power_1);
64
65        assert_f32_eq(
66            (reactive_power_1 + reactive_power_2).as_volt_amperes_reactive(),
67            2200.0,
68        );
69        assert_f32_eq(
70            (reactive_power_2 - reactive_power_1).as_volt_amperes_reactive(),
71            200.0,
72        );
73        assert_f32_eq((reactive_power_1 * 2.0).as_volt_amperes_reactive(), 2000.0);
74        assert_f32_eq((reactive_power_2 / 2.0).as_volt_amperes_reactive(), 600.0);
75        assert_f32_eq(
76            (reactive_power_2 * Percentage::from_percentage(50.0)).as_volt_amperes_reactive(),
77            600.0,
78        );
79        assert_f32_eq(reactive_power_2 / reactive_power_1, 1.2);
80        assert_f32_eq(ReactivePower::zero().as_volt_amperes_reactive(), 0.0);
81    }
82
83    #[test]
84    fn test_reactive_power_voltage_current() {
85        let reactive_power = ReactivePower::from_kilovolt_amperes_reactive(1.0);
86        let voltage = Voltage::from_volts(1000.0);
87
88        let current = reactive_power / voltage;
89        assert_f32_eq(current.as_amperes(), 1.0);
90
91        let voltage_calculated = reactive_power / current;
92        assert_f32_eq(voltage_calculated.as_volts(), 1000.0);
93    }
94
95    #[test]
96    fn test_reactive_power_formatting() {
97        let s = |value| ReactivePower::from_volt_amperes_reactive(value).to_string();
98        let p = |value, prec| {
99            format!(
100                "{:.prec$}",
101                ReactivePower::from_volt_amperes_reactive(value),
102                prec = prec
103            )
104        };
105        assert_eq!(s(0.0), "0 mVAR");
106
107        assert_eq!(s(1.558), "1.558 VAR");
108        assert_eq!(p(1.558, 1), "1.6 VAR");
109
110        assert_eq!(s(1.5508), "1.551 VAR");
111        assert_eq!(p(1.5508, 5), "1.5508 VAR");
112
113        assert_eq!(s(2030.0), "2.03 kVAR");
114
115        assert_eq!(s(2_030_022.0), "2.03 MVAR");
116        assert_eq!(s(2_030_022_123.0), "2.03 GVAR");
117        assert_eq!(p(2_030_022_123.0, 6), "2.030022 GVAR");
118
119        assert_eq!(s(-1.558), "-1.558 VAR");
120        assert_eq!(p(-1.558, 1), "-1.6 VAR");
121
122        assert_eq!(s(-2030.0), "-2.03 kVAR");
123        assert_eq!(p(-2030.0, 1), "-2 kVAR");
124
125        assert_eq!(s(-2_030_022.0), "-2.03 MVAR");
126        assert_eq!(p(-2_030_022.0, 6), "-2.030022 MVAR");
127
128        assert_eq!(s(-2_030_022_123.0), "-2.03 GVAR");
129        assert_eq!(p(-2_030_022_123.0, 6), "-2.030022 GVAR");
130    }
131}