femagtools 1.8.7__py3-none-any.whl → 1.8.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
femagtools/isa7.py CHANGED
@@ -13,6 +13,37 @@ import numpy as np
13
13
  logger = logging.getLogger('femagtools.isa7')
14
14
 
15
15
 
16
+ def Trot(alpha):
17
+ return np.array([[np.cos(alpha), np.sin(alpha)],
18
+ [-np.sin(alpha), np.cos(alpha)]])
19
+
20
+ def transform_coord(geometry, xy):
21
+ '''transform from global coord to local coord'''
22
+ ecpl = Trot(geometry['alpha']).dot((xy-geometry['cxy']).T).T
23
+ return dict(ecpl=(ecpl + (geometry['w']/2,
24
+ geometry['h']/2)).T,
25
+ ecp=np.asarray(xy).T)
26
+
27
+
28
+ def transform_flux_density(alpha, bxy):
29
+ '''transform the magnet flux density to local coordinate system'''
30
+ def tf(b1, b2, alpha):
31
+ if b1.ndim > 1:
32
+ r = Trot(alpha).dot(((b1.ravel()), (b2.ravel())))
33
+ return [r[0, :].reshape(*b1.shape),
34
+ r[1, :].reshape(*b1.shape)]
35
+ else:
36
+ return Trot(alpha).dot(((b1), (b2)))
37
+
38
+ b = tf(b1=bxy[:, 0, :], b2=bxy[:, 1, :], alpha=alpha)
39
+
40
+ # remove DC component
41
+ bxf = np.mean(b[0].T - np.mean(b[0], axis=1).T, axis=1)
42
+ byf = np.mean(b[1].T - np.mean(b[1], axis=1).T, axis=1)
43
+ return {'bxyl': np.asarray(b),
44
+ 'bxyf': np.array([bxf, byf])}
45
+
46
+
16
47
  def jordanpfe(Bxnu, Bynu, fnu, losscoeffs, axr):
17
48
  """example of custom core loss calculation
18
49
  Args:
@@ -72,6 +103,11 @@ class ElType(Enum):
72
103
  SquareRectangle = 4
73
104
  """Types of elements"""
74
105
 
106
+ class MoveType(Enum):
107
+ Rotate = 0
108
+ Linear = 1
109
+ """Types of rotor movement"""
110
+
75
111
  class Reader(object):
76
112
  """
77
113
  Open and Read I7/ISA7 file
@@ -328,6 +364,7 @@ class Reader(object):
328
364
  HI, num_move_ar, self.ANGL_I_UP,
329
365
  num_par_wdgs, cur_control) = self.next_block("f")[:8]
330
366
  self.NUM_PAR_WDGS = int(num_par_wdgs)
367
+ self.move_action = move_action # rotate 0, linear 1
331
368
  self.arm_length = arm_length # unit is m
332
369
  self.skip_block(2)
333
370
  self.skip_block(30 * 30)
@@ -752,13 +789,14 @@ class Isa7(object):
752
789
  self.element_pos = np.array([e.center
753
790
  for e in self.elements])
754
791
 
755
- for a in ('FC_RADIUS', 'pole_pairs', 'poles_sim',
792
+ for a in ('FC_RADIUS', 'pole_pairs', 'poles_sim', 'move_action',
756
793
  'layers', 'coil_span', 'delta_node_angle', 'speed',
757
794
  'MAGN_TEMPERATURE', 'BR_TEMP_COEF',
758
795
  'MA_SPEZ_WEIGHT', 'CU_SPEZ_WEIGHT'):
759
- v = getattr(reader, a, '')
760
- if v:
761
- setattr(self, a, v)
796
+ try:
797
+ setattr(self, a, getattr(reader, a))
798
+ except AttributeError:
799
+ pass
762
800
  if getattr(reader, 'pole_pairs', 0):
763
801
  self.num_poles = 2*self.pole_pairs
764
802
  if getattr(reader, 'slots', 0):
@@ -771,7 +809,7 @@ class Isa7(object):
771
809
 
772
810
  self.airgap_inner_elements = [] # used for rotate
773
811
  self.airgap_outer_elements = []
774
- if hasattr(self, 'FC_RADIUS'): # Note: cosys r/phi only
812
+ if getattr(self, 'FC_RADIUS', 0) > 0: # Note: cosys r/phi only
775
813
  # TODO: handle multiple airgaps
776
814
  airgap_center_elements = []
777
815
  for n in self.nodes:
@@ -1251,13 +1289,94 @@ class Isa7(object):
1251
1289
  try:
1252
1290
  poles_sim = self.poles_sim
1253
1291
  except:
1254
-
1255
1292
  poles_sim = poles
1256
1293
 
1257
1294
  scale_factor = poles/poles_sim
1258
-
1259
1295
  return scale_factor
1260
1296
 
1297
+ def get_magnet_data(self, ibeta=0, icur=0) -> list:
1298
+ '''Extract magnet data from nc file
1299
+
1300
+ Args:
1301
+ nc: nc object
1302
+ icur, ibeta: load case
1303
+
1304
+ Returns:
1305
+ pm_data: list of magnet data
1306
+ '''
1307
+ mag_spels = self.magnet_super_elements()
1308
+ if len(mag_spels) / self.poles_sim == 1:
1309
+ mag_spels = [mag_spels[0]]
1310
+
1311
+ # prepare data for ialh method
1312
+ # conductivity and permeability of the magnets
1313
+ cond = 0
1314
+ mur = 0
1315
+ # read boundary nodes
1316
+ for se in mag_spels:
1317
+ cond = se.conduc
1318
+ if cond <= 0:
1319
+ cond = 625000
1320
+ logging.warning('Magnet conductivity <= 0, using 625000 S/m')
1321
+ mur = np.abs(1/se.elements[0].reluc[0])
1322
+ logging.debug('Magnet: mur=%s, conductivity=%s', mur, cond)
1323
+
1324
+ # stationary case, no rotation
1325
+ poles = 0
1326
+ try:
1327
+ poles = self.num_poles
1328
+ except AttributeError:
1329
+ pass
1330
+
1331
+ if poles == 0: # no rotation
1332
+ freq = self.speed
1333
+ time_vec = np.linspace(0, 1/freq, len(self.pos_el_fe_induction))
1334
+ pos = dict(time=time_vec.tolist(),
1335
+ freq=freq,
1336
+ t=float(1/freq))
1337
+ # reset num.poles
1338
+ poles = 1
1339
+ else:
1340
+ if self.move_action == 0:
1341
+ phi = self.pos_el_fe_induction*180/np.pi
1342
+ pos = dict(phi=phi,
1343
+ speed=self.speed)
1344
+ else:
1345
+ pos = dict(displ=self.pos_el_fe_induction,
1346
+ speed=self.speed)
1347
+ # prep dictionary for the loss calculation
1348
+ pm_data = []
1349
+ for i, se in enumerate(mag_spels):
1350
+ ecp = [e.center for e in se.elements]
1351
+ geometry = se.get_rect_geom()
1352
+
1353
+ bxy = []
1354
+ for e in se.elements:
1355
+ theta = np.arctan2(float(e.center[1]),
1356
+ float(e.center[0]))
1357
+ fd = self.flux_density(e, icur, ibeta)
1358
+ bxy.append(Trot(-theta).dot((fd['bx'], fd['by'])))
1359
+ #= np.moveaxis(bxy, 1, 0)
1360
+ pd = dict(name='pm_data_se' + str(se.key),
1361
+ hm=geometry['h'],
1362
+ wm=geometry['w'],
1363
+ lm=self.arm_length,
1364
+ alpha=geometry['alpha'],
1365
+ ls=self.arm_length,
1366
+ sigma=cond,
1367
+ mur=mur,
1368
+ loadcase=ibeta,
1369
+ numpoles=poles,
1370
+ bl=transform_flux_density(geometry['alpha'],
1371
+ np.array(bxy)),
1372
+ elcp=transform_coord(geometry, ecp),
1373
+ area=se.area(),
1374
+ spel_key=se.key)
1375
+ pd.update(pos)
1376
+
1377
+ pm_data.append(pd)
1378
+ return pm_data
1379
+
1261
1380
 
1262
1381
  class Point(object):
1263
1382
  def __init__(self, x, y):
@@ -1514,7 +1633,7 @@ class SuperElement(BaseEntity):
1514
1633
 
1515
1634
  def get_rect_geom(self):
1516
1635
  """return rectangle parameters of superelement:
1517
- x0, y0: center coordinates
1636
+ cxy: center coordinates
1518
1637
  w, h: width and height
1519
1638
  alpha: angle of main axis"""
1520
1639
  bxy = np.array([n.xy for b in self.nodechains
@@ -1523,13 +1642,14 @@ class SuperElement(BaseEntity):
1523
1642
  cxy = np.mean(bxy, axis=0)
1524
1643
  # corner points: calculate angles
1525
1644
  b = np.vstack((bxy[-1], bxy, bxy[0]))
1526
- a = np.arctan2(b[1:, 1]-b[:-1, 1],
1527
- b[1:, 0]- b[:-1, 0])
1528
- peaks = np.where(np.abs(np.diff(a)) > 1)[0]
1645
+ db = np.diff(b, axis=0)
1646
+ a = np.arctan2(db[:, 1], db[:, 0])
1647
+ da = np.abs(np.diff(a))
1648
+ peaks = np.where((da < 6) & (da > 1))[0]
1529
1649
  c = bxy[peaks]
1530
- # width and height
1650
+ # width and height (distances between corners)
1531
1651
  dxy = np.linalg.norm(np.vstack(
1532
- (bxy[:-1, :] - bxy[1:, :],
1652
+ (np.diff(bxy, axis=0),
1533
1653
  bxy[-1, :] - bxy[0, :])), axis=1)
1534
1654
  dc = (np.sum(dxy[peaks[0]:peaks[1]]),
1535
1655
  np.sum(dxy[peaks[1]:peaks[2]]),
@@ -1545,7 +1665,7 @@ class SuperElement(BaseEntity):
1545
1665
  alpha = np.arctan2(c[i+1, 1]-c[i, 1], c[i+1, 0]-c[i, 0])
1546
1666
  if alpha < 0:
1547
1667
  alpha += np.pi
1548
- return {'w': w, 'h': h, 'x0': cxy[0], 'y0': cxy[1],
1668
+ return {'w': w, 'h': h, 'cxy': cxy,
1549
1669
  'area': area, 'alpha': alpha}
1550
1670
 
1551
1671
 
@@ -0,0 +1,63 @@
1
+ # -*- coding: utf-8 -*-
2
+ '''Calculate leakage inductances'''
3
+ import numpy as np
4
+ import logging
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ def end_wdg_leak_ind_round_wires(l_ew, p, q, m, y, taup, w1, layers, num_par_wdgs, slot_height):
10
+ '''returns end winding leakage inductance per phase per end winding side (DE or NDE)'''
11
+ mue0 = 4*np.pi*1e-7
12
+ if layers == 2:
13
+ lambda_ew = 0.34*q*(1 - 2*y*taup/(np.pi*l_ew*m*q))
14
+ if layers == 1:
15
+ lambda_ew = q*(0.67 - 0.43*(taup + slot_height*np.pi/(2*p))/l_ew)
16
+ L_ew = mue0*2/(p*q)*(w1/num_par_wdgs)**2*l_ew*lambda_ew
17
+ return L_ew
18
+
19
+
20
+ def end_wdg_leak_ind_hairpin_wires(): #TODO
21
+ L_ew = 0
22
+ return L_ew
23
+
24
+
25
+ #def slot_leakage_inductance_round_wires(p, q, w1, num_par_wdgs, layers):
26
+ # '''returns slot leakage inductance per phase'''
27
+ # mue0 = 4*np.pi*1e-7
28
+ # if layers == 1:
29
+ # lambda_slot = 0 # tbc
30
+ # if layers == 2:
31
+ # t1 = b2/bs
32
+ # t2 = b1/b2
33
+ # t12 = b1/b2
34
+ # kt1 = (4*t1**2 - t1**4 - 4*np.log(t1) -3)/(4*(1 - t1)*(1 - t1**2)**2) if t1 != 1 else 0
35
+ # kt2 = (4*t2**2 - t2**4 - 4*np.log(t2) - 3)/(4*(1 - t2)*(1 - t2**2)**2) if t2 != 1 else 0
36
+ # kt12 = (t12**2 - 2*np.log(t12) - 1)/(2*(1 - t12)*(1 - t12**2)) if t12 != 1 else 0
37
+ # const = 0.1424 + 0.5*np.arcsin(np.sqrt(1 - (bo/b1)**2)) + ho/bo
38
+ # lambda_t = h2/b2*kt2 + const
39
+ # lambda_b = h3/bs*kt1 + h2/(b2-b1)*np.log(b2/b1) + const if b2 != b1 else h3/bs*kt1 + const
40
+ # lambda_tb = h2/b2*kt12 + const
41
+ # lambda_slot = lambda_t + lambda_b + lambda_tb
42
+ # L_slot = mue0*2/(p*q)*(w1/num_par_wdgs)**2*lambda_slot
43
+ # return L_slot
44
+
45
+
46
+ def slot_leak_ind_fea(): #TODO
47
+ '''returns slot and tooth tip leakage inductance'''
48
+ # make a single slot model with detailed windings
49
+ # run current through windings
50
+ # L_slot = flux / current
51
+ # scale to get values per phase
52
+ L_slot = 0
53
+ return L_slot
54
+
55
+
56
+ def harm_leak_ind(E_fft, order_fft, freq, Ia): # needs to be validated
57
+ '''returns harmonic leakage inductance per phase'''
58
+ L_harm = []
59
+ for ii in range(1, len(order_fft)): # skip 1st order
60
+ L_harm.append(E_fft[ii] / Ia / (2*np.pi*freq*order_fft[ii]))
61
+
62
+ return sum(L_harm)
63
+