@@ -18,11 +18,12 @@ def convert_geometric_model(geometric_model) -> ndarray:
1818
1919def calculate_impedance (model ) -> ndarray :
2020 z_primitive = model .build_z_primitive ()
21- z_abc = perform_kron_reduction (z_primitive )
21+ z_abc = perform_kron_reduction (z_primitive , dimension = model .dimension )
22+
2223 return z_abc
2324
2425
25- def perform_kron_reduction (z_primitive : ndarray ) -> ndarray :
26+ def perform_kron_reduction (z_primitive : ndarray , dimension = 3 ) -> ndarray :
2627 """ Reduces the primitive impedance matrix to an equivalent impedance
2728 matrix.
2829
@@ -56,8 +57,10 @@ def perform_kron_reduction(z_primitive: ndarray) -> ndarray:
5657 [Zba, Zbb, Zbc]
5758 [Zca, Zcb, Zcc]
5859 """
59- Ẑpp , Ẑpn = z_primitive [0 :3 , 0 :3 ], z_primitive [0 :3 , 3 :]
60- Ẑnp , Ẑnn = z_primitive [3 :, 0 :3 ], z_primitive [3 :, 3 :]
60+ Ẑpp , Ẑpn = (z_primitive [0 :dimension , 0 :dimension ],
61+ z_primitive [0 :dimension , dimension :])
62+ Ẑnp , Ẑnn = (z_primitive [dimension :, 0 :dimension ],
63+ z_primitive [dimension :, dimension :])
6164 Z_abc = Ẑpp - Ẑpn @ inv (Ẑnn ) @ Ẑnp
6265 return Z_abc
6366
@@ -78,17 +81,11 @@ def __init__(self, model):
7881 self .ω = 2.0 * π * self .ƒ # angular frequency radians / second
7982
8083 def build_z_primitive (self ) -> ndarray :
81- neutral_conductors = sorted ([
82- ph for ph in self .phases
83- if ph .startswith ("N" )
84- ])
85- conductors = ["A" , "B" , "C" ] + neutral_conductors
86-
87- dimension = len (conductors )
84+ dimension = len (self .conductors )
8885 z_primitive = zeros (shape = (dimension , dimension ), dtype = complex )
8986
90- for index_i , phase_i in enumerate (conductors ):
91- for index_j , phase_j in enumerate (conductors ):
87+ for index_i , phase_i in enumerate (self . conductors ):
88+ for index_j , phase_j in enumerate (self . conductors ):
9289 if phase_i not in self .phases or phase_j not in self .phases :
9390 continue
9491 R = self .compute_R (phase_i , phase_j )
@@ -190,8 +187,46 @@ def get_h(self, i):
190187 _ , yᵢ = self .phase_positions [i ]
191188 return yᵢ
192189
190+ @property
191+ def dimension (self ):
192+ return 2 if getattr (self , 'is_secondary' , False ) else 3
193+
194+ @property
195+ def conductors (self ):
196+ neutral_conductors = sorted ([
197+ ph for ph in self .phases
198+ if ph .startswith ("N" )
199+ ])
200+
201+ return ["A" , "B" , "C" ] + neutral_conductors
202+
203+
204+ class ModifiedCarsonsEquations (CarsonsEquations ):
205+ """
206+ Modified Carson's Equation. Two approximations are made:
207+ only the first term of P and the first two terms of Q are considered.
208+ """
209+ number_of_P_terms = 1
210+
211+ def compute_P (self , i , j , number_of_terms = 1 ) -> float :
212+ return super ().compute_P (i , j , self .number_of_P_terms )
213+
214+ def compute_X (self , i , j ) -> float :
215+ Q_first_term = super ().compute_Q (i , j , 1 )
216+
217+ # Simplify equations and don't compute Dᵢⱼ explicitly
218+ kᵢⱼ_Dᵢⱼ_ratio = sqrt (self .ω * self .μ / self .ρ )
219+ ΔX = Q_first_term * 2 + log (2 )
220+
221+ if i == j :
222+ X_o = - log (self .gmr [i ]) - log (kᵢⱼ_Dᵢⱼ_ratio )
223+ else :
224+ X_o = - log (self .compute_d (i , j )) - log (kᵢⱼ_Dᵢⱼ_ratio )
225+
226+ return (X_o + ΔX ) * self .ω * self .μ / (2 * π )
227+
193228
194- class ConcentricNeutralCarsonsEquations (CarsonsEquations ):
229+ class ConcentricNeutralCarsonsEquations (ModifiedCarsonsEquations ):
195230 def __init__ (self , model , * args , ** kwargs ):
196231 super ().__init__ (model )
197232 self .neutral_strand_gmr : Dict [str , float ] = model .neutral_strand_gmr
@@ -246,22 +281,45 @@ def compute_d(self, i, j) -> float:
246281 # Distance between two neutral/phase conductors
247282 return distance_ij
248283
249- def compute_X (self , i , j ) -> float :
250- Q_first_term = super ().compute_Q (i , j , 1 )
251-
252- # Simplify equations and don't compute Dᵢⱼ explicitly
253- kᵢⱼ_Dᵢⱼ_ratio = sqrt (self .ω * self .μ / self .ρ )
254- ΔX = Q_first_term * 2 + log (2 )
255-
256- if i == j :
257- X_o = - log (self .gmr [i ]) - log (kᵢⱼ_Dᵢⱼ_ratio )
258- else :
259- X_o = - log (self .compute_d (i , j )) - log (kᵢⱼ_Dᵢⱼ_ratio )
260-
261- return (X_o + ΔX ) * self .ω * self .μ / (2 * π )
262-
263284 def GMR_cn (self , phase ) -> float :
264285 GMR_s = self .neutral_strand_gmr [phase ]
265286 k = self .neutral_strand_count [phase ]
266287 R = self .radius [phase ]
267288 return (GMR_s * k * R ** (k - 1 ))** (1 / k )
289+
290+
291+ class MultiConductorCarsonsEquations (ModifiedCarsonsEquations ):
292+ def __init__ (self , model ):
293+ super ().__init__ (model )
294+ self .radius : Dict [str , float ] = model .radius
295+ self .insulation_thickness : Dict [str , float ] = \
296+ model .insulation_thickness
297+
298+ def compute_d (self , i , j ) -> float :
299+ # Assumptions:
300+ # 1. All conductors in the cable are touching each other and
301+ # therefore equidistant.
302+ # 2. In case of quadruplex cables, the space between conductors
303+ # which are diagonally positioned is neglected.
304+ return (self .radius [i ] + self .radius [j ]
305+ + self .insulation_thickness [i ] + self .insulation_thickness [j ])
306+
307+ @property
308+ def conductors (self ):
309+ neutral_conductors = sorted ([
310+ ph for ph in self .phases if ph .startswith ("N" )
311+ ])
312+ if self .is_secondary :
313+ conductors = ["S1" , "S2" ] + neutral_conductors
314+ else :
315+ conductors = ["A" , "B" , "C" ] + neutral_conductors
316+
317+ return conductors
318+
319+ @property
320+ def is_secondary (self ):
321+ phase_conductors = [ph for ph in self .phases if not ph .startswith ('N' )]
322+ if phase_conductors == ["S1" , "S2" ]:
323+ return True
324+ else :
325+ return False
0 commit comments