ac_power/pq/
power.rs

1// Copyright 2023 Enphase Energy, Inc and Universal Interoperability for
2// Grid-Forming Inverters (UNIFI) Consortium.
3//
4//    Licensed under the Apache License, Version 2.0 (the "License");
5//    you may not use this file except in compliance with the License.
6//    You may obtain a copy of the License at
7//
8//        http://www.apache.org/licenses/LICENSE-2.0
9//
10//    Unless required by applicable law or agreed to in writing, software
11//    distributed under the License is distributed on an "AS IS" BASIS,
12//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13//    See the License for the specific language governing permissions and
14//    limitations under the License.
15
16use crate::constants::FRAC_1_SQRT_3;
17use crate::newtypes::{Current, Voltage};
18use crate::pq::Pq;
19use crate::reference_frames::{Abc, AlphaBeta, AlphaBeta0, Dq, Dq0};
20use core::ops::Mul;
21
22impl Mul<Abc<Current>> for Abc<Voltage> {
23    fn mul(self, rhs: Abc<Current>) -> Pq {
24        let p = self.a * rhs.a + self.b * rhs.b + self.c * rhs.c;
25        let q = FRAC_1_SQRT_3
26            * ((self.a - self.b) * rhs.c + (self.b - self.c) * rhs.a + (self.c - self.a) * rhs.b);
27        Pq { p, q }
28    }
29    type Output = Pq;
30}
31impl Mul<Abc<Voltage>> for Abc<Current> {
32    fn mul(self, rhs: Abc<Voltage>) -> Pq {
33        rhs * self
34    }
35    type Output = Pq;
36}
37
38impl Mul<AlphaBeta<Current>> for AlphaBeta<Voltage> {
39    fn mul(self, rhs: AlphaBeta<Current>) -> Pq {
40        let p = 1.5 * (self.alpha * rhs.alpha + self.beta * rhs.beta);
41        let q = 1.5 * (self.beta * rhs.alpha - self.alpha * rhs.beta);
42        Pq { p, q }
43    }
44    type Output = Pq;
45}
46
47impl Mul<AlphaBeta<Voltage>> for AlphaBeta<Current> {
48    fn mul(self, rhs: AlphaBeta<Voltage>) -> Pq {
49        rhs * self
50    }
51    type Output = Pq;
52}
53
54impl Mul<AlphaBeta0<Current>> for AlphaBeta0<Voltage> {
55    fn mul(self, rhs: AlphaBeta0<Current>) -> Pq {
56        let p = 1.5 * (self.alpha * rhs.alpha + self.beta * rhs.beta + 2.0 * self.zero * rhs.zero);
57        let q = 1.5 * (self.beta * rhs.alpha - self.alpha * rhs.beta);
58        Pq { p, q }
59    }
60    type Output = Pq;
61}
62
63impl Mul<AlphaBeta0<Voltage>> for AlphaBeta0<Current> {
64    fn mul(self, rhs: AlphaBeta0<Voltage>) -> Pq {
65        rhs * self
66    }
67    type Output = Pq;
68}
69
70impl Mul<Dq0<Current>> for Dq0<Voltage> {
71    fn mul(self, rhs: Dq0<Current>) -> Pq {
72        let p = 1.5 * (self.d * rhs.d + self.q * rhs.q + 2.0 * self.zero * rhs.zero);
73        let q = 1.5 * (self.q * rhs.d - self.d * rhs.q);
74        Pq { p, q }
75    }
76    type Output = Pq;
77}
78
79impl Mul<Dq0<Voltage>> for Dq0<Current> {
80    fn mul(self, rhs: Dq0<Voltage>) -> Pq {
81        rhs * self
82    }
83    type Output = Pq;
84}
85
86impl Mul<Dq<Current>> for Dq<Voltage> {
87    fn mul(self, rhs: Dq<Current>) -> Pq {
88        let p = 1.5 * (self.d * rhs.d + self.q * rhs.q);
89        let q = 1.5 * (self.q * rhs.d - self.d * rhs.q);
90        Pq { p, q }
91    }
92    type Output = Pq;
93}
94
95impl Mul<Dq<Voltage>> for Dq<Current> {
96    fn mul(self, rhs: Dq<Voltage>) -> Pq {
97        rhs * self
98    }
99    type Output = Pq;
100}
101
102#[cfg(test)]
103mod tests {
104
105    use super::*;
106    use crate::trig::cos_sin;
107    use crate::trig::Theta;
108    use approx::assert_abs_diff_eq;
109
110    // helper function to assert pq approximate equality
111    fn check_pqs(pq0: Pq, pq1: Pq) {
112        assert_abs_diff_eq!(f32::from(pq0.p), f32::from(pq1.p), epsilon = 0.01,);
113        assert_abs_diff_eq!(f32::from(pq0.q), f32::from(pq1.q), epsilon = 0.01,);
114    }
115
116    #[test]
117    fn multiply() {
118        let v_theta = Theta::from_degrees(20.0);
119        let i_theta = Theta::from_degrees(45.0);
120        let (cos, sin) = cos_sin(v_theta);
121
122        let v_mag = Voltage::from(240.0);
123        let i_mag = Current::from(1.0);
124        let v_zero = Voltage::from(10.0);
125        let i_zero = Current::from(-2.0);
126        let v_abc = Abc::from_polar(v_mag, v_theta) + v_zero;
127        let i_abc = Abc::from_polar(i_mag, i_theta) + i_zero;
128        let pq_abc = v_abc * i_abc;
129
130        let v_alpha_beta0 = AlphaBeta0::from(v_abc);
131        let i_alpha_beta0 = AlphaBeta0::from(i_abc);
132        let pq_alpha_beta0 = v_alpha_beta0 * i_alpha_beta0;
133
134        let v_dq0 = v_abc.to_dq0(cos, sin);
135        let i_dq0 = i_abc.to_dq0(cos, sin);
136        let pq_dq0 = v_dq0 * i_dq0;
137
138        check_pqs(pq_abc, pq_alpha_beta0);
139        check_pqs(pq_abc, pq_dq0);
140    }
141}