11from collections import defaultdict
22from itertools import islice
3+ from typing import Dict , Iterable , Iterator , Tuple
34
45from numpy import arctan , cos , log , sin , sqrt , zeros
6+ from numpy import ndarray
57from numpy import pi as π
68from numpy .linalg import inv
79
810
9- def convert_geometric_model (geometric_model ):
11+ def convert_geometric_model (geometric_model ) -> ndarray :
1012 carsons_model = CarsonsEquations (geometric_model )
1113
1214 z_primitive = carsons_model .build_z_primitive ()
1315 z_abc = perform_kron_reduction (z_primitive )
1416 return z_abc
1517
1618
17- def calculate_impedance (model ):
19+ def calculate_impedance (model ) -> ndarray :
1820 z_primitive = model .build_z_primitive ()
1921 z_abc = perform_kron_reduction (z_primitive )
2022 return z_abc
2123
2224
23- def perform_kron_reduction (z_primitive ) :
25+ def perform_kron_reduction (z_primitive : ndarray ) -> ndarray :
2426 """ Reduces the primitive impedance matrix to an equivalent impedance
2527 matrix.
2628
@@ -66,38 +68,36 @@ class CarsonsEquations():
6668 μ = 4 * π * 1e-7 # permeability, Henry / meter
6769
6870 def __init__ (self , model ):
69- self .phases = model .phases
70- self .phase_positions = model .wire_positions
71- self .gmr = model .geometric_mean_radius
72- self .r = model .resistance
71+ self .phases : Iterable [str ] = model .phases
72+ self .phase_positions : Dict [str , Tuple [float , float ]] = \
73+ model .wire_positions
74+ self .gmr : Dict [str , float ] = model .geometric_mean_radius
75+ self .r : Dict [str , float ] = model .resistance
7376
7477 self .ƒ = getattr (model , 'frequency' , 60 )
7578 self .ω = 2.0 * π * self .ƒ # angular frequency radians / second
7679
77- def build_z_primitive (self ):
78- abc_conductors = [
79- ph if ph in self .phases
80- else None for ph in ("A" , "B" , "C" )
81- ]
80+ def build_z_primitive (self ) -> ndarray :
8281 neutral_conductors = sorted ([
8382 ph for ph in self .phases
8483 if ph .startswith ("N" )
8584 ])
86- conductors = abc_conductors + neutral_conductors
85+ conductors = [ "A" , "B" , "C" ] + neutral_conductors
8786
8887 dimension = len (conductors )
8988 z_primitive = zeros (shape = (dimension , dimension ), dtype = complex )
9089
9190 for index_i , phase_i in enumerate (conductors ):
9291 for index_j , phase_j in enumerate (conductors ):
93- if phase_i is not None and phase_j is not None :
94- R = self .compute_R (phase_i , phase_j )
95- X = self .compute_X (phase_i , phase_j )
96- z_primitive [index_i , index_j ] = complex (R , X )
92+ if phase_i not in self .phases or phase_j not in self .phases :
93+ continue
94+ R = self .compute_R (phase_i , phase_j )
95+ X = self .compute_X (phase_i , phase_j )
96+ z_primitive [index_i , index_j ] = complex (R , X )
9797
9898 return z_primitive
9999
100- def compute_R (self , i , j ):
100+ def compute_R (self , i , j ) -> float :
101101 rᵢ = self .r [i ]
102102 ΔR = self .μ * self .ω / π * self .compute_P (i , j )
103103
@@ -106,7 +106,7 @@ def compute_R(self, i, j):
106106 else :
107107 return ΔR
108108
109- def compute_X (self , i , j ):
109+ def compute_X (self , i , j ) -> float :
110110 Qᵢⱼ = self .compute_Q (i , j )
111111 ΔX = self .μ * self .ω / π * Qᵢⱼ
112112
@@ -124,11 +124,11 @@ def compute_X(self, i, j):
124124
125125 return X_o + ΔX
126126
127- def compute_P (self , i , j , number_of_terms = 1 ):
127+ def compute_P (self , i , j , number_of_terms = 1 ) -> float :
128128 terms = islice (self .compute_P_terms (i , j ), number_of_terms )
129129 return sum (terms )
130130
131- def compute_P_terms (self , i , j ):
131+ def compute_P_terms (self , i , j ) -> Iterator [ float ] :
132132 yield π / 8.0
133133
134134 kᵢⱼ = self .compute_k (i , j )
@@ -140,11 +140,11 @@ def compute_P_terms(self, i, j):
140140 yield kᵢⱼ ** 3 / (45 * sqrt (2 )) * cos (3 * θᵢⱼ )
141141 yield - π * kᵢⱼ ** 4 * cos (4 * θᵢⱼ ) / 1536
142142
143- def compute_Q (self , i , j , number_of_terms = 2 ):
143+ def compute_Q (self , i , j , number_of_terms = 2 ) -> float :
144144 terms = islice (self .compute_Q_terms (i , j ), number_of_terms )
145145 return sum (terms )
146146
147- def compute_Q_terms (self , i , j ):
147+ def compute_Q_terms (self , i , j ) -> Iterator [ float ] :
148148 yield - 0.0386
149149
150150 kᵢⱼ = self .compute_k (i , j )
@@ -157,30 +157,31 @@ def compute_Q_terms(self, i, j):
157157 yield - kᵢⱼ ** 4 / 384 * θᵢⱼ * sin (4 * θᵢⱼ )
158158 yield - kᵢⱼ ** 4 / 384 * cos (4 * θᵢⱼ ) * (log (2 / kᵢⱼ ) + 1.0895 )
159159
160- def compute_k (self , i , j ):
160+ def compute_k (self , i , j ) -> float :
161161 Dᵢⱼ = self .compute_D (i , j )
162162 return Dᵢⱼ * sqrt (self .ω * self .μ / self .ρ )
163163
164- def compute_θ (self , i , j ):
164+ def compute_θ (self , i , j ) -> float :
165165 xᵢ , _ = self .phase_positions [i ]
166166 xⱼ , _ = self .phase_positions [j ]
167167 xᵢⱼ = abs (xⱼ - xᵢ )
168168 hᵢ , hⱼ = self .get_h (i ), self .get_h (j )
169169
170170 return arctan (xᵢⱼ / (hᵢ + hⱼ ))
171171
172- def compute_d (self , i , j ):
172+ def compute_d (self , i , j ) -> float :
173173 return self .calculate_distance (
174174 self .phase_positions [i ],
175- self .phase_positions [j ])
175+ self .phase_positions [j ],
176+ )
176177
177- def compute_D (self , i , j ):
178+ def compute_D (self , i , j ) -> float :
178179 xⱼ , yⱼ = self .phase_positions [j ]
179180
180181 return self .calculate_distance (self .phase_positions [i ], (xⱼ , - yⱼ ))
181182
182183 @staticmethod
183- def calculate_distance (positionᵢ , positionⱼ ):
184+ def calculate_distance (positionᵢ , positionⱼ ) -> float :
184185 xᵢ , yᵢ = positionᵢ
185186 xⱼ , yⱼ = positionⱼ
186187 return sqrt ((xᵢ - xⱼ )** 2 + (yᵢ - yⱼ )** 2 )
@@ -193,16 +194,21 @@ def get_h(self, i):
193194class ConcentricNeutralCarsonsEquations (CarsonsEquations ):
194195 def __init__ (self , model , * args , ** kwargs ):
195196 super ().__init__ (model )
196- self .neutral_strand_gmr = model .neutral_strand_gmr
197- self .neutral_strand_count = defaultdict (
198- lambda : None , model .neutral_strand_count )
199- self .neutral_strand_resistance = model .neutral_strand_resistance
200- self .radius = defaultdict (lambda : None , {
201- phase : (diameter_over_neutral -
202- model .neutral_strand_diameter [phase ]) / 2
203- for phase , diameter_over_neutral
204- in model .diameter_over_neutral .items ()
205- })
197+ self .neutral_strand_gmr : Dict [str , float ] = model .neutral_strand_gmr
198+ self .neutral_strand_count : Dict [str , float ] = defaultdict (
199+ lambda : None ,
200+ model .neutral_strand_count
201+ )
202+ self .neutral_strand_resistance : Dict [str , float ] = \
203+ model .neutral_strand_resistance
204+ self .radius : Dict [str , float ] = defaultdict (
205+ lambda : None , {
206+ phase : (diameter_over_neutral -
207+ model .neutral_strand_diameter [phase ]) / 2
208+ for phase , diameter_over_neutral
209+ in model .diameter_over_neutral .items ()
210+ }
211+ )
206212 self .phase_positions .update ({
207213 f"N{ phase } " : self .phase_positions [phase ]
208214 for phase in self .phase_positions .keys ()
@@ -217,7 +223,7 @@ def __init__(self, model, *args, **kwargs):
217223 })
218224 return
219225
220- def compute_d (self , i , j ):
226+ def compute_d (self , i , j ) -> float :
221227 I , J = set (i ), set (j )
222228 r = self .radius [i ] or self .radius [j ]
223229
@@ -240,7 +246,7 @@ def compute_d(self, i, j):
240246 # Distance between two neutral/phase conductors
241247 return distance_ij
242248
243- def compute_X (self , i , j ):
249+ def compute_X (self , i , j ) -> float :
244250 Q_first_term = super ().compute_Q (i , j , 1 )
245251
246252 # Simplify equations and don't compute Dᵢⱼ explicitly
@@ -254,7 +260,7 @@ def compute_X(self, i, j):
254260
255261 return (X_o + ΔX ) * self .ω * self .μ / (2 * π )
256262
257- def GMR_cn (self , phase ):
263+ def GMR_cn (self , phase ) -> float :
258264 GMR_s = self .neutral_strand_gmr [phase ]
259265 k = self .neutral_strand_count [phase ]
260266 R = self .radius [phase ]
0 commit comments