femagtools 1.7.1__py3-none-any.whl → 1.7.2__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/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  """
4
4
  __title__ = 'femagtools'
5
- __version__ = '1.7.1'
5
+ __version__ = '1.7.2'
6
6
  __author__ = 'Ronald Tanner'
7
7
  __license__ = 'BSD'
8
8
  __copyright__ = 'Copyright 2023-2024 Gamma Technology'
femagtools/bch.py CHANGED
@@ -70,7 +70,7 @@ def sttylosses(losses):
70
70
  except KeyError:
71
71
  pass
72
72
  return d
73
-
73
+
74
74
  if sregs == {'StYoke', 'StTeeth'}:
75
75
  return hysteddy('StYoke', 'StTeeth', losses)
76
76
  if sregs == {'StJo', 'StZa'}:
@@ -95,6 +95,21 @@ def sttylosses(losses):
95
95
  return l
96
96
  return {}
97
97
 
98
+ def losses_mapping_external_rotor(losses):
99
+ styoke = 'rotor'
100
+ rotor = 'Iron'
101
+ d = {}
102
+ try:
103
+ d['styoke_hyst'] = losses[styoke+'_hyst']
104
+ d['rotor_hyst'] = losses[rotor+'_hyst']
105
+ d['styoke_eddy'] = losses[styoke+'_eddy']
106
+ d['rotor_eddy'] = losses[rotor+'_eddy']
107
+ d['styoke_excess'] = losses[styoke+'_excess']
108
+ d['rotor_excess'] = losses[rotor+'_excess']
109
+ except KeyError:
110
+ pass
111
+ return d
112
+
98
113
 
99
114
  def _readSections(f):
100
115
  """return list of bch sections
@@ -1150,6 +1165,10 @@ class Reader:
1150
1165
  m = []
1151
1166
  subregs = [name.split()[-1]
1152
1167
  for name in content[2].split('\t') if name][2:]
1168
+ # outer rotor motor
1169
+ if 'Iron' in subregs and \
1170
+ 'Rotor' in subregs:
1171
+ self.external_rotor = True
1153
1172
  logger.info("Stator Subregions: %s", subregs)
1154
1173
  speed = float(content[0].split()[-1])/60.
1155
1174
  logger.info('losses for speed %f', speed)
@@ -1209,7 +1228,10 @@ class Reader:
1209
1228
  (nrows, ncols)).T.tolist()
1210
1229
  for k, v in zip(cols, m[2:])})
1211
1230
  ls['speed'] = speed
1212
- ls.update(sttylosses(ls))
1231
+ if self.external_rotor:
1232
+ ls.update(losses_mapping_external_rotor(ls))
1233
+ else:
1234
+ ls.update(sttylosses(ls))
1213
1235
  if self.ldq:
1214
1236
  self.ldq['losses'] = ls
1215
1237
  elif self.psidq:
@@ -1464,6 +1486,10 @@ class Reader:
1464
1486
  def __read_losses(self, content):
1465
1487
  losses = {}
1466
1488
  i = 0
1489
+ # check if external rotor
1490
+ if self.weights[0][1] == 0.0 and \
1491
+ self.weights[0][-1] > 0:
1492
+ self.external_rotor = True
1467
1493
  # find results for angle:
1468
1494
  while True:
1469
1495
  try:
@@ -1521,8 +1547,12 @@ class Reader:
1521
1547
  losses['total'] += losses['staza']+losses['stajo']
1522
1548
  elif len(rec) == 1:
1523
1549
  t = l.split(':')[-1].strip()
1524
- if t == 'Iron':
1525
- losses['staza'] = floatnan(rec[0])
1550
+ if t == 'Iron':
1551
+ if self.external_rotor:
1552
+ losses['rotfe'] = floatnan(rec[0])
1553
+ losses['total'] += losses['rotfe']
1554
+ else:
1555
+ losses['staza'] = floatnan(rec[0])
1526
1556
  else:
1527
1557
  losses['stajo'] += floatnan(rec[0])
1528
1558
  losses['total'] += losses['staza']+losses['stajo']
@@ -1530,11 +1560,17 @@ class Reader:
1530
1560
  continue
1531
1561
 
1532
1562
  if _rotloss.search(l):
1563
+ if l.find('StZa') > -1:
1564
+ self.external_rotor = True
1533
1565
  rec = self.__findNums(content[i+2])
1534
1566
  if len(rec) == 1:
1535
- rotfe = floatnan(rec[0])
1536
- losses['rotfe'] += rotfe
1537
- losses['total'] += rotfe
1567
+ if self.external_rotor:
1568
+ losses['staza'] = floatnan(rec[0])
1569
+ losses['total'] += losses['staza']
1570
+ else:
1571
+ rotfe = floatnan(rec[0])
1572
+ losses['rotfe'] += rotfe
1573
+ losses['total'] += rotfe
1538
1574
  i += 3
1539
1575
  continue
1540
1576
 
@@ -1565,6 +1601,10 @@ class Reader:
1565
1601
  if content[i+1].split() == ['rotf', '----']:
1566
1602
  losses['rotfe'] = sum([floatnan(x) for x in rec])
1567
1603
  losses['total'] += losses['rotfe']
1604
+
1605
+ if content[i+1].split() == ['Iron', '----']: # external rotor
1606
+ losses['rotfe'] = sum([floatnan(x) for x in rec])
1607
+ losses['total'] += losses['rotfe']
1568
1608
  i += 4
1569
1609
  continue
1570
1610
 
@@ -1622,7 +1662,10 @@ class Reader:
1622
1662
  l.find('RoZa') > -1:
1623
1663
  k = 'staza'
1624
1664
  elif l.find('Iron') > -1 and l.find('Stator') > -1:
1625
- k = 'staza'
1665
+ if self.external_rotor:
1666
+ k = 'rotor'
1667
+ else:
1668
+ k = 'staza'
1626
1669
  elif l.find('Iron') > -1:
1627
1670
  if self.external_rotor:
1628
1671
  k = 'staza'
@@ -550,7 +550,7 @@ class FslRenderer(object):
550
550
  'ndt(agndst)',
551
551
  'r1 = m.fc_radius - ag/6',
552
552
  'x1, y1 = pr2c(r1, alfa)',
553
- 'n = math.floor(r1*alfa/agndst + 1.5)',
553
+ 'n = math.floor(m.fc_radius*alfa/agndst + 1.5)',
554
554
  'nc_circle_m(r1, 0, x1, y1, 0.0, 0.0, n)\n',
555
555
  'r2 = m.fc_radius + ag/6',
556
556
  'x2, y2 = pr2c(r2, alfa)',
femagtools/machine/pm.py CHANGED
@@ -569,10 +569,9 @@ class PmRelMachine(object):
569
569
  if abs(du) > 0.1:
570
570
  logger.debug('oops? iqd_imax_umax one more torque reduction')
571
571
  if with_tmech:
572
- iq, id = self.iqd_tmech(torque, w1/2/np.pi/self.p,
573
- iq, id)[:2]
572
+ iq, id = self.iqd_tmech_umax(torque, w1, u1max)[:2]
574
573
  else:
575
- iq, id = self.iqd_torque(torque)[:2]
574
+ iq, id = self.iqd_torque_umax(torque, w1, u1max)[:2]
576
575
  if with_mtpv:
577
576
  try:
578
577
  if with_tmech:
@@ -779,7 +778,7 @@ class PmRelMachine(object):
779
778
  maxtorque=T > 0)[:2])
780
779
  w, _, ier, _ = so.fsolve(voltw1, wx, full_output=True)
781
780
  logger.debug("fsolve ier %d T %f w %f w1 %f", ier, T, w, wx)
782
- if ier in (1, 4, 5):
781
+ if ier == 1: #in (1, 4, 5):
783
782
  if abs(w[0]) <= wx:
784
783
  self.check_extrapolation = True
785
784
  return [w/2/np.pi/self.p
@@ -788,6 +787,8 @@ class PmRelMachine(object):
788
787
  wl += (wu-wl)/4
789
788
  else:
790
789
  wl = w[0]
790
+ else:
791
+ break
791
792
  except ValueError as e:
792
793
  logger.debug("%s: wx %f wl %f wu %f", e, wx, wl, wu)
793
794
  wl = wx
@@ -5,6 +5,8 @@
5
5
  """
6
6
  import numpy as np
7
7
  import logging
8
+ from ..windings import Winding
9
+ from .utils import wdg_resistance
8
10
 
9
11
  logger = logging.getLogger("femagools.machine.sizing")
10
12
 
@@ -14,7 +16,7 @@ PM_DEFAULTS = dict(
14
16
  eta=0.92, # efficiency
15
17
  cos_phi=0.95, # power factor
16
18
  m=3, # number of phases
17
- ui_u=0.62, # U ind / U
19
+ ui_u=0.64, # U ind / U
18
20
  lda=1.33, # length/taup ratio 0.5 .. sqrt(p)
19
21
  J=3.8e6, # current density 3 .. 6 A/mm²
20
22
  sigmas=17e3, # shear force 10 .. 45 kN/m2
@@ -25,10 +27,10 @@ PM_DEFAULTS = dict(
25
27
  mag_width=0.8, # rel magnet width 0.6 .. 1
26
28
  Hc=700, # max. coercitive field strength, 500 .. 900 kA/m
27
29
  brem=1.15, # remanence 0.3 .. 1.3 T
28
- demag=1.5, # safety factor for demagnetisation
30
+ demag=6, # safety factor for demagnetisation (nom current)
29
31
  external_rotor=False,
30
32
  coil_span=0,
31
- hfe=1e-3 # iron height between magnet and airgap (IPM)
33
+ hfe=1.5e-3 # iron height between magnet and airgap (IPM)
32
34
  )
33
35
  """default sizing parameters for PM"""
34
36
 
@@ -88,10 +90,10 @@ def iron_losses(dy, lfe, hy, wt, ht, q1, B, f):
88
90
  return Wfe * dens*vol
89
91
 
90
92
 
91
- def wdg_resistance(w1, l, d):
92
- S = 56e6 # conductivity of copper 1/Ohm m
93
- a = np.pi*d**2/4
94
- return 2*w1*l/S/a
93
+ #def wdg_resistance(w1, l, d):
94
+ # S = 56e6 # conductivity of copper 1/Ohm m
95
+ # a = np.pi*d**2/4
96
+ # return 2*w1*l/S/a
95
97
 
96
98
 
97
99
  def check_symmetry_conditions(Q1, p, layers, m):
@@ -202,18 +204,22 @@ def _stator_slots(par, slots):
202
204
  par['Q1'] = Q1
203
205
  r = get_stator_dimensions(par)
204
206
  hb = r['hns']/r['bns']
207
+ logging.debug(f"Q1 {Q1} hs/bs {hb}")
205
208
  if hb > 5:
206
209
  break
207
- if hb > 3:
208
- q.append((Q1, Q1//np.gcd(Q1, p), abs(4-hb), r['kw']))
210
+ if hb > 2.5:
211
+ # ideal height/width ratio: 3.2
212
+ q.append((Q1, Q1//np.gcd(Q1, p), abs(3.2-hb), r['kw']))
209
213
 
210
214
  if q:
211
215
  qhbmin = np.argmin(q, axis=0)
212
- logger.debug("q %s", q)
213
- # check sim factor, height/width ratio, winding factor
216
+ logger.debug("q %s qhbmin %s", q, qhbmin)
217
+ # check sim factor, height/width ratio
214
218
  if qhbmin[1] == len(q)-1:
215
- return q[qhbmin[1]][0]
219
+ # last has smallest sim factor
220
+ return q[qhbmin[1]][0]
216
221
  elif q[qhbmin[1]][2] < q[qhbmin[1]+1][2]:
222
+ # select ideal height/width ratio (3.2)
217
223
  return q[qhbmin[1]][0]
218
224
  return q[qhbmin[1]+1][0]
219
225
  return slotset[0]
@@ -222,7 +228,6 @@ def _stator_slots(par, slots):
222
228
  def get_stator_dimensions(par, slots=[]):
223
229
  # Check symmetry
224
230
  if 'Q1' in par:
225
- from ..windings import Winding
226
231
  wdg = Winding({'Q': par['Q1'], 'p': par['p'], 'm': 3})
227
232
  # nominal (rated) operating parameters
228
233
  pnom = par['pnom']
@@ -237,7 +242,8 @@ def get_stator_dimensions(par, slots=[]):
237
242
 
238
243
  # pole width
239
244
  taup = np.pi * Da/(2*p)
240
- lfe = taup*lda
245
+ lfe_q = par.get('lfe_q', 0.001)
246
+ lfe = lfe_q * round(taup*lda/lfe_q)
241
247
 
242
248
  if 'udc' in par:
243
249
  u1nom = 0.9*par['udc']/np.sqrt(2)/np.sqrt(3) # phase voltage
@@ -254,8 +260,6 @@ def get_stator_dimensions(par, slots=[]):
254
260
  else:
255
261
  Q1 = par['Q1']
256
262
  coil_span = par.get('coil_span', 0)
257
- if np.gcd(Q1, 2*p) == coil_span:
258
- coil_span = 0
259
263
  layers = 1 if coil_span == 0 else 2
260
264
  req_poles = np.gcd(Q1, 2*p)
261
265
  if req_poles != 2*p:
@@ -282,7 +286,7 @@ def get_stator_dimensions(par, slots=[]):
282
286
  kw = kwz * kws
283
287
 
284
288
  # flux density amplitude of base harmonic
285
- if 'brem' in par:
289
+ if 'mag_width' in par:
286
290
  mag_width = par['mag_width']
287
291
  Bd1 = 4.0/np.pi*Ba*np.sin(np.pi/2.0*mag_width)
288
292
  else:
@@ -293,8 +297,36 @@ def get_stator_dimensions(par, slots=[]):
293
297
  # stator winding
294
298
  psi1 = 2.0/np.pi*taup*lfe*Bd1
295
299
 
300
+ # number of turns per phase, first estimation
296
301
  Ui = Ui_U * u1nom
297
- N = round(np.sqrt(2)*Ui/(2*np.pi*f1nom*kw*psi1), 1)
302
+ N = np.sqrt(2)*Ui/(2*np.pi*f1nom*kw*psi1)
303
+
304
+ # coils per phase and pole
305
+ # q = Q/2/p/m
306
+ # num wires per coil side (number of coil groups a)
307
+ # n = a*N / 2 / p / q
308
+
309
+ # feasible number of turns per coil...
310
+ ncoils = Q1 // 2 // m * layers
311
+ ngroups = [1] + [g for g in range(2, layers*p + 1) if layers * p % g == 0]
312
+ ndiff = [abs(N - ncoils // a * a * round(N / ncoils))
313
+ for a in ngroups]
314
+ logger.debug("N %f ngroups %s ndiffs %s",
315
+ N, ngroups, ndiff)
316
+ a_calc = ngroups[np.argmin(ndiff)]
317
+ a = par.get("a", a_calc)
318
+ if not a in ngroups:
319
+ logger.warning("Check given number %s of parallel wdg groups. Valid ngroups are: %s",
320
+ a, ngroups)
321
+ num_wires = round(a * N / ncoils)
322
+
323
+ # correction of number of turns per phase
324
+ N_old = N
325
+ N = num_wires * ncoils / a
326
+
327
+ # correction of voltage
328
+ Ui = Ui/N_old*N
329
+ u1nom = Ui/Ui_U
298
330
 
299
331
  # current loading
300
332
  # A = np.sqrt(2)*sigmas/kw/Ba
@@ -312,6 +344,12 @@ def get_stator_dimensions(par, slots=[]):
312
344
  hns = (-bns + np.sqrt(bns**2 + 4*ans*np.tan(taus)))/2/np.tan(taus)
313
345
  hys = psi1/2/lfe/By
314
346
 
347
+ aw = ans * kq / layers / num_wires
348
+ # round wire: pi*d²/4
349
+ dwire = 2 * np.sqrt(aw/np.pi)
350
+ wdg = Winding({'Q': Q1, 'p': p, 'm': 3, 'yd': coil_span, 'l': layers})
351
+ r1 = wdg_resistance(wdg, num_wires, a, aw, Da, hns, lfe)
352
+ relculen = 1.4
315
353
  # airgap and yoke diameter
316
354
  airgap = par['airgap']
317
355
  if par['external_rotor']:
@@ -336,9 +374,11 @@ def get_stator_dimensions(par, slots=[]):
336
374
  trv=round(1e-3*pnom/(2*np.pi*speednom)/(np.pi*Da1**2/4*lfe), 1),
337
375
  w1=int(N),
338
376
  kw=round(kw, 4),
339
- q=qt,
377
+ q=wdg.q,
340
378
  i1=round(I1, 3), # np.pi*Da1*A/2/m/N
341
- psi1=round(psi1, 5))
379
+ psi1=round(psi1, 5),
380
+ u1=u1nom,
381
+ ui=Ui)
342
382
 
343
383
  slotwidth = np.floor(0.3*np.pi*Da1/Q1*2000+0.5)/2000.0
344
384
  tw = bds # np.pi*(Da1+hns)/Q1 - bns
@@ -375,25 +415,14 @@ def get_stator_dimensions(par, slots=[]):
375
415
  wedge_width1=0, # bns1,
376
416
  wedge_width2=0,
377
417
  middle_line=0 if layers < 2 else middle_line))
378
- # coils per phase and pole
379
- # q = Q/2/p/m
380
- # num wires per coil side (number of coil groups a)
381
- # n = a*N / 2 / p / q
382
- ncoils = Q1//2//m*layers
383
- ngroups = [1] + [g for g in range(2, p+1) if p%g == 0]
384
- a = ngroups[np.argmin([abs(N-ncoils//a*round(a*N/ncoils))
385
- for a in ngroups])]
386
- num_wires = round(a*N/ncoils)
387
- dwire = 2*np.sqrt(ans*kq/layers/np.pi/num_wires)
388
- relculen = 1.4
389
- r1 = wdg_resistance(N, relculen*lfe, dwire)/a
418
+
390
419
  r['pcu'] = round(m*r1*I1**2, 1)
391
420
 
392
421
  r['winding'] = dict(
393
422
  wire_diam=round(dwire, 5),
394
423
  num_phases=m,
395
424
  cufilfact=kq,
396
- culength=1.4,
425
+ culength=relculen,
397
426
  num_par_wdgs=a,
398
427
  num_layers=layers,
399
428
  resistance=round(r1, 4),
@@ -413,8 +442,7 @@ def _get_magnet_height(I1, N, kw, par):
413
442
  Hc = Hc*1e3 # unit kA/m
414
443
  # Safety Factor for demagnetization
415
444
  demag = par['demag']
416
- I1max = 4*I1
417
- THETA1 = m/np.pi*np.sqrt(2)*I1max*N*kw/p
445
+ THETA1 = m/np.pi*np.sqrt(2)*I1*N*kw/p
418
446
  # minimal magnet height with safety factor
419
447
  hM = THETA1/Hc*demag
420
448
  Br = par['brem']
@@ -698,6 +726,7 @@ def ipm(pnom: float, speed: float, p: int, **kwargs) -> dict:
698
726
  pnom=pnom, speed=speed, p=p)
699
727
  par.update(kwargs)
700
728
  _set_pm_defaults(par)
729
+ par.pop('mag_width')
701
730
 
702
731
  # stator and magnet parameters of interior mounted magnet machine
703
732
  r = get_stator_dimensions(par)
femagtools/machine/sm.py CHANGED
@@ -102,6 +102,8 @@ def parident(workdir, engine, machine,
102
102
  num_cur_steps=kwargs.get('num_cur_steps', 5),
103
103
  num_beta_steps=kwargs.get('num_beta_steps', 13),
104
104
  num_par_wdgs=machine[wdgk].get('num_par_wdgs', 1),
105
+ skew_angle=kwargs.get('skew_angle', 0.0),
106
+ num_skew_steps=kwargs.get('num_skew_steps', 0.0),
105
107
  period_frac=kwargs.get('period_frac', 6),
106
108
  speed=kwargs.get('speed', 50))
107
109
 
@@ -338,9 +340,9 @@ class SynchronousMachine(object):
338
340
  def torquemax(self, i1, iex):
339
341
  "returns maximum torque of i1, iex (nan if i1 out of range)"
340
342
  def torquei1b(b):
341
- return -self.torque_iqd(*iqd(b[0], i1), iex)
343
+ return self.torque_iqd(*iqd(b[0], i1), iex)
342
344
  res = so.minimize(torquei1b, (0,))
343
- return -res.fun
345
+ return res.fun
344
346
 
345
347
  def torquemin(self, i1, iex):
346
348
  "returns minimum torque of i1, iex (nan if i1 out of range)"
@@ -576,7 +578,7 @@ class SynchronousMachine(object):
576
578
  i1max: max. phase current (RMS)
577
579
  """
578
580
  if kwargs.get('i1max', 0):
579
- w1type, T = self.w1_imax_umax(kwargs['i1max'], u1max)
581
+ w1type, T = self.w1_imax_umax(kwargs['i1max'], u1max)
580
582
  try:
581
583
  iq, id, iex = self.iqd_torque(T)
582
584
  except ValueError:
femagtools/nc.py CHANGED
@@ -195,7 +195,7 @@ class Reader(object):
195
195
  grp.variables['delta_node_angle'].getValue().data)
196
196
  self.poles_sim = int(grp.variables['poles_sim'].getValue().data)
197
197
  self.slots = int(grp.variables['num_slots'].getValue().data)
198
- self.arm_length = int(grp.variables['arm_length'].getValue().data)
198
+ self.arm_length = float(grp.variables['arm_length'].getValue().data)
199
199
  except:
200
200
  pass
201
201
 
@@ -281,10 +281,10 @@ class Reader(object):
281
281
  cw = float(mcgrp.variables['cw'][i].data)
282
282
  cw_freq_exp = float(mcgrp.variables['cw_exp'][i].data)
283
283
  cw_ind_exp = float(mcgrp.variables['ind_exp'][i].data)
284
- try:
284
+ try:
285
285
  ce = float(mcgrp.variables['ce'][i].data)
286
286
  cw_ind_beta_exp = float(mcgrp.variables['ind_beta_exp'][i].data)
287
- except:
287
+ except:
288
288
  pass
289
289
  spec_weight = float(mcgrp.variables['spec_weight'][i].data)
290
290
  fillfactor = float(mcgrp.variables['fillfac'][i].data)
@@ -8,7 +8,17 @@
8
8
  """
9
9
  import numpy as np
10
10
  import matplotlib.pyplot as plt
11
- from matplotlib import colormaps
11
+ try:
12
+ from matplotlib import colormaps
13
+ cmap_viridis = colormaps['viridis']
14
+ cmap_jet = colormaps['jet']
15
+ cmap_YlOrBr = colormaps['YlOrBr']
16
+ except: # older matplotlib
17
+ from matplotlib import cm
18
+ cmap_viridis = cm.viridis
19
+ cmap_jet = cm.jet
20
+ cmap_YlOrBr = cm.YlOrBr
21
+
12
22
 
13
23
  def _create_3d_axis():
14
24
  """creates a subplot with 3d projection if one does not already exist"""
@@ -24,7 +34,7 @@ def _create_3d_axis():
24
34
  plt.subplot(111, projection='3d')
25
35
 
26
36
 
27
- def _plot_surface(ax, x, y, z, labels, azim=None, cmap=colormaps['viridis']):
37
+ def _plot_surface(ax, x, y, z, labels, azim=None, cmap=cmap_viridis):
28
38
  """helper function for surface plots"""
29
39
  # ax.tick_params(axis='both', which='major', pad=-3)
30
40
  assert np.size(x) > 1 and np.size(y) > 1 and np.size(z) > 1
@@ -60,7 +70,7 @@ def forcedens(title, pos, fdens, ax=0):
60
70
  ax.set_ylabel('Force Density / kN/m²')
61
71
 
62
72
 
63
- def forcedens_surface(fdens, attr='FN', ax=0, cmap=colormaps['jet']):
73
+ def forcedens_surface(fdens, attr='FN', ax=0, cmap=cmap_jet):
64
74
  if ax == 0:
65
75
  _create_3d_axis()
66
76
  ax = plt.gca()
@@ -72,7 +82,7 @@ def forcedens_surface(fdens, attr='FN', ax=0, cmap=colormaps['jet']):
72
82
  ('Rotor pos/°', 'Pos/°', f'{attr} / kN/m²'),
73
83
  cmap=cmap)
74
84
 
75
- def forcedens_contour(fdens, attr='FN', ax=0, cmap=colormaps['jet']):
85
+ def forcedens_contour(fdens, attr='FN', ax=0, cmap=cmap_jet):
76
86
  if ax == 0:
77
87
  ax = plt.gca()
78
88
  z = 1e-3*np.array(getattr(fdens, attr))
@@ -91,7 +101,7 @@ def forcedens_contour(fdens, attr='FN', ax=0, cmap=colormaps['jet']):
91
101
  cbar.ax.set_ylabel('kN/m²')
92
102
 
93
103
  def forcedens_fft(title, fdens, harmmax=(), #(200, 40),
94
- cmap=colormaps['YlOrBr'],
104
+ cmap=cmap_YlOrBr,
95
105
  ax=0):
96
106
  """plot force densities FFT
97
107
  Args:
femagtools/tks.py CHANGED
@@ -21,6 +21,17 @@ numPattern = re.compile(r'([+-]?\d+(?:\.\d+)?(?:[eE][+-]\d+)?)')
21
21
  HBpattern = re.compile(r'H.+\s+B')
22
22
  BPpattern = re.compile(r'B.+\s+P')
23
23
 
24
+ _tranlate = {
25
+ "ch": "Hysteresis Loss Factor",
26
+ "cw": "Eddy Current Loss Factor",
27
+ "ce": "Excess Loss Factor",
28
+ "ch_freq": "Hyteresis Exponent",
29
+ "cw_freq": "Eddy Current Exponent",
30
+ "b_coeff": "Induction Loss Exponent",
31
+ "alpha": "Induction Loss Exponent (Bertotti)",
32
+ "Bo": "Reference Induction",
33
+ "fo": "Reference Frequency",
34
+ }
24
35
 
25
36
  def readlist(section):
26
37
  x = []
@@ -109,7 +120,8 @@ class Reader(object):
109
120
  self.cw = z[2]
110
121
  self.cw_freq = z[3]
111
122
  self.b_coeff = z[4]
112
-
123
+ self.jordan = {'ch': z[0], 'cw': z[2], 'ch_freq': z[1], 'cw_freq': z[3], 'b_coeff': z[4],
124
+ 'Bo': self.Bo, 'fo': self.fo}
113
125
  z = lc.fitsteinmetz(
114
126
  self.losses['f'],
115
127
  self.losses['B'],
@@ -122,11 +134,15 @@ class Reader(object):
122
134
  self.losses['cw_freq'] = z[1]
123
135
  self.losses['b_coeff'] = z[2]
124
136
 
137
+ self.steinmetz = {'cw': z[0], 'cw_freq': z[1], 'b_coeff': z[2],
138
+ 'Bo': self.Bo, 'fo': self.fo}
139
+
125
140
  self.losses['Bo'] = self.Bo
126
141
  self.losses['fo'] = self.fo
127
142
  z = lc.fit_bertotti(self.losses['f'],
128
143
  self.losses['B'], pfe)
129
- self.bertotti = {'ch': z[0], 'alpha': 2.0, 'cw': z[1], 'ce': z[2]}
144
+ self.bertotti = {'ch': z[0], 'cw': z[1], 'ce': z[2],
145
+ 'alpha': 2.0, 'Bo': 1, 'fo': 1}
130
146
  logger.info("Bertotti loss coeffs %s", z)
131
147
 
132
148
  # must normalize pfe matrix:
@@ -156,7 +172,27 @@ class Reader(object):
156
172
  'b_coeff': self.b_coeff,
157
173
  'rho': self.rho,
158
174
  'losses': self.losses}
159
-
175
+
176
+ def tableview(Reader):
177
+ """pretty print loss coeff table"""
178
+ losscoeff = [Reader.jordan, Reader.steinmetz, Reader.bertotti]
179
+ # Title
180
+ strlen = 101
181
+ print('='*strlen)
182
+ print('| {:^34} '.format(' ') + '| {:^18} | {:^18} | {:^18} |'.format(*["Jordan", "Steinmetz", 'Bertotti']))
183
+ print('='*strlen)
184
+ # data
185
+ for key, item in _tranlate.items():
186
+ fout = ''
187
+ for i in losscoeff:
188
+ if key in i:
189
+ fout += '| ' + f'{i[key]:^18.8e} '
190
+ else:
191
+ tmp = '-'
192
+ fout += '| ' + f'{tmp:^18} '
193
+ print(f'| {item:^34}' + ' ' + fout + '|')
194
+ print('='*strlen)
195
+ return
160
196
 
161
197
  def read(filename, filecontent=None):
162
198
  """read Thyssen File TKS and return mc dict"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: femagtools
3
- Version: 1.7.1
3
+ Version: 1.7.2
4
4
  Summary: Python API for FEMAG
5
5
  Author-email: Ronald Tanner <tar@semafor.ch>, Dapu Zhang <dzhang@gtisoft.com>, Beat Holm <hob@semafor.ch>, Günther Amsler <amg@semafor.ch>, Nicolas Mauchle <mau@semafor.ch>
6
6
  License: Copyright (c) 2016-2023, Semafor Informatik & Energie AG, Basel
@@ -1,9 +1,9 @@
1
- femagtools/__init__.py,sha256=J7PZIYNxvDe7bKOlKKN7KqkIMJCh3y9ziNTJ9-b6-xY,1600
1
+ femagtools/__init__.py,sha256=ndVtdwfscAu-jgdCzn_dAXiywoE6BRKaYEF9FkFp7s0,1600
2
2
  femagtools/airgap.py,sha256=ZCIZTYf6BbuNYx68y9fUDBQo3taN8RuGl8q-jJ7ygiA,1577
3
3
  femagtools/amazon.py,sha256=O1ICuv21XDAJi1qK1Sigs2TdS6hDZP19OzvmE2t76wU,12069
4
4
  femagtools/amela.py,sha256=pHjfXzpANI-7oz8MtrqNcyDZ6PxVM91vCJuvYhHy1rk,13891
5
5
  femagtools/asm.py,sha256=CiL0KWaF4P7O6-VwmGLdva_icwmPrPiI-TFQ19XYTKk,7660
6
- femagtools/bch.py,sha256=-TBHcXNf6bSvcDxi0qnVJV7ZQryDrsY6SCwwQLRyRQo,71680
6
+ femagtools/bch.py,sha256=a0Ak4xN5yYlt-0QCjLymYfVmsdYBNBCp7ZNORcaKWTs,73375
7
7
  femagtools/bchxml.py,sha256=KrRjAdrUPZXmiWvonu9HhpG_NvImMBpiXWTL4iSr4kE,3142
8
8
  femagtools/condor.py,sha256=J8z9iBdvrGu3I1eFNoyKV8AXzRoTEPGLSak6cXUQxAM,10766
9
9
  femagtools/conductor.py,sha256=rXO7c7Qh_s7JpgILmLd4IbG64vP6Eh143YF9u25Mdwg,1076
@@ -34,13 +34,13 @@ femagtools/model.py,sha256=7_LWgUQY3W-n4RBvPXbTY2FhfgiR0xlSSKTv2JqUX08,15618
34
34
  femagtools/moproblem.py,sha256=kOP8pRdD8YXz28_M2WKnFgl3eeJ7tqg49ohoazsmUOg,2825
35
35
  femagtools/multiproc.py,sha256=7mJF-VU1NrJkENyg8eHtDrDRNBwLPK43phZv3ehm9BU,8435
36
36
  femagtools/mxw2msh.py,sha256=CIIqAvfs8U-A0OfuOAoDaqNSmoMSHSI_tW1CPFRCP5E,2151
37
- femagtools/nc.py,sha256=TC7LY1FAL43ntA0IlGzUXhUsB05-xWzeZ1yP0q4L0A4,14532
37
+ femagtools/nc.py,sha256=6O3lWuX2SX8IuVM0rr55L2UywrI1MmXzJOgUtUmcg4A,14532
38
38
  femagtools/netlist.py,sha256=CSCl8setLZ_L8DCnNWaNA3-wLe1yo-fmzARZoVvYfaA,2052
39
39
  femagtools/ntib.py,sha256=76g1ZO3Fq_kN-HTMBvaKvJmMMlJMyEPFeNAcJPq3w7Y,3099
40
40
  femagtools/opt.py,sha256=snQlPNT5hksCgj0TBjFBhSCERMHHGMv1xc4uFSxptfQ,8687
41
41
  femagtools/parstudy.py,sha256=K78fd7ILhYjGgkauTXc_M8Plnynee0WFH20iyuoMXSo,19523
42
42
  femagtools/poc.py,sha256=wMwOxMhPLFRiGPMsKQwWWuGr6UZPzRBajhfVMfXptNU,6794
43
- femagtools/tks.py,sha256=UQMgOsPTGPiK9V0-e6wapmdSdiSGAlkBvoqnVKbSRI0,6049
43
+ femagtools/tks.py,sha256=xStMu6P1xsc36tS53FKnbL6i2ZTVnCNXyTVw8_gjepo,7436
44
44
  femagtools/ts.py,sha256=x9aCMVASjdBZuyI2pJGMyi1dveGFd_pWQ20cZ-l_moc,47216
45
45
  femagtools/utils.py,sha256=1SX5s21xyW8u0NF_Hjs7DByyCTdLm9VRArLburTyHR0,1581
46
46
  femagtools/vbf.py,sha256=9XGfhftmD9carU8ByQ5DwqoR4daq5mJ39eMqruwml0Q,2444
@@ -56,7 +56,7 @@ femagtools/dxfsl/corner.py,sha256=-XPBcnEau-2-SRHLYzlBqCQGaFfgm_DH2qR1mSaFoAs,13
56
56
  femagtools/dxfsl/dumprenderer.py,sha256=n4AvInjvGIaC2iKZtQaYXXDyJVSQ3uEOFOLD4-xfKRY,1861
57
57
  femagtools/dxfsl/dxfparser.py,sha256=kyXG0kZfNyOgn96MqBgP8RhOQhppfB5NbyRNNybs1C0,13451
58
58
  femagtools/dxfsl/femparser.py,sha256=O8940Q1Mz8MKng6W8M3s9KfTvhDLJ56tfQWtZEW3xMM,2134
59
- femagtools/dxfsl/fslrenderer.py,sha256=DBE7qDlwIE5K8qD8ne7iNyaEVwHrpyis2TX1spteLsA,24979
59
+ femagtools/dxfsl/fslrenderer.py,sha256=ftI-nHv76ghAm3fkg_7ZAj-p-sWlIHYt7SbbsFvYWyY,24988
60
60
  femagtools/dxfsl/functions.py,sha256=F0AjzHfIfq3v-mhDzUOeq3zeYQCEsJl1-XpEyJQsSnQ,11805
61
61
  femagtools/dxfsl/geom.py,sha256=-hPErBLyAsDh-aq2s54psFqG2AaNr9jVeKMeEVUQF5E,165356
62
62
  femagtools/dxfsl/journal.py,sha256=S17B7wsrq5QzIUbjgg0ntvnpgH0thHq9aQXO7GdYlQQ,4265
@@ -69,9 +69,9 @@ femagtools/machine/__init__.py,sha256=U8W65K7jr7jDdC1KnJh0WjYd8DFaLnIFVvlh-TKcV9
69
69
  femagtools/machine/afpm.py,sha256=hNyDFRLGmCuWRPZl_u1ztJ4pA-Y_mxLaVvg3UJkzRuE,24766
70
70
  femagtools/machine/effloss.py,sha256=sLB8AXYVAE_AL1Ca41A2EDbn9_7u6xNjaYFjmRPoTDg,13578
71
71
  femagtools/machine/im.py,sha256=3Y54AHMZfAjkvgexx2E-5jxNWzaVQ-SyaETCh7gNBYA,38008
72
- femagtools/machine/pm.py,sha256=N4UEpOEYpvJzGjWt4SLOpgMrjzoSYyuxpi-8wlT1U5A,58565
73
- femagtools/machine/sizing.py,sha256=BYaPEWrU1IDbnyRzC2MlQBA0MXaiK8sx1wD7Pn21XX8,23328
74
- femagtools/machine/sm.py,sha256=ubS7mK2dibhwUpGwyY-23hoZbLrVqAtPeXVmqF657lg,37781
72
+ femagtools/machine/pm.py,sha256=JQn2vnPBvGF9mUnqnKiVisHZnjhpzIbaBkqUxKN5leE,58589
73
+ femagtools/machine/sizing.py,sha256=nWCfxbyWfbw5-7xu0qZ6zjWNquEPn3fUH-fQeGb6QUc,24307
74
+ femagtools/machine/sm.py,sha256=G4fHmZngQSRN9Dum7mHaf36b_CvD-u_AQogIFixlnys,37899
75
75
  femagtools/machine/utils.py,sha256=ckPOfaaieRbThMKkBtCtoCMsehdbePH8xNoWPmNoS3Y,18782
76
76
  femagtools/moo/__init__.py,sha256=zinmWEOrsEz6DmMX0Dbn4t6_1UR-p4bEGqyR1wUQk_Q,175
77
77
  femagtools/moo/algorithm.py,sha256=lNEf0Bur4yFpIJeLtAC3oIus8sOMWTb7jepFlD28YzE,5445
@@ -85,7 +85,7 @@ femagtools/plot/bch.py,sha256=4f9Q-JZtj9WMjxFUXMXNaSdWdDXN_Be4-F9kOU45xns,28525
85
85
  femagtools/plot/char.py,sha256=SB40YJ5seVQmWTAdrfDQZiEZH3jIB11HUY5SJL_HJcY,12032
86
86
  femagtools/plot/fieldlines.py,sha256=QtKF4nhnQ_FHHGh9Qez3GVmym0CLhW1ZyIKtk4pzos4,1136
87
87
  femagtools/plot/fluxdens.py,sha256=NlexRJ3f_8CgKoWrV82ZIsAXPrLhwj98uOe8_fUks7A,1082
88
- femagtools/plot/forcedens.py,sha256=aBxMUnMkE5Ost5BQuAYI28aN9bC2gWZN65xHqwFsuhw,3849
88
+ femagtools/plot/forcedens.py,sha256=Vloi9czy7qbGXI-Vm7Cow6IfHTsFhCLI1YWduFOR55c,4075
89
89
  femagtools/plot/mcv.py,sha256=NwDBe40PQgKqWh_rM89k-vsxRmvyjetv4y7elXzTPZU,3099
90
90
  femagtools/plot/nc.py,sha256=yq-uYppAEUePbo1SAaBKNKla3cyqdLh5z9ArhtwPYZY,9548
91
91
  femagtools/plot/phasor.py,sha256=5QG1GkXKVksc8P6Q4thKADf6W1l8rDKeArIHFYvbXlw,4858
@@ -166,7 +166,7 @@ tests/test_afpm.py,sha256=OE-ULFcp_fBQ7SyRacDmDGbq8ULFHLbXXx3MHJN1C1c,11031
166
166
  tests/test_airgap_induction.py,sha256=cmpy1og59oWEyCO4oF_zDuc1DUwCbdeebNL1ujpZza4,1065
167
167
  tests/test_amela.py,sha256=IsGah_O8qWsudZOnWL0pjH9S40O03oH_XfDtLgnzBMk,637
168
168
  tests/test_asm.py,sha256=NAMJ2Km4zi6byhooq5E7GZbkFeSNSTaLpJWt-jQF5cE,1398
169
- tests/test_bchreader.py,sha256=byqjDa_329cCgQFhLHLOgxiwZ0UMzvWb-rVjvN9wFQA,15497
169
+ tests/test_bchreader.py,sha256=yFpjN0MIv_o_OD8XH9HxlOpcNtL79K_3bXMeupxO648,16843
170
170
  tests/test_conductor.py,sha256=T7bmuSdI2Fowwm2Ht-8D0Qnfs1_lx1aoi9RhC2p5nYU,332
171
171
  tests/test_convert.py,sha256=oseuhm3Iw-9b4WzBjA_Og2lgXPqKYicLUnfadq5Yick,6497
172
172
  tests/test_dxfsl.py,sha256=DBAjtwNAwcB7VYZBjul7xf4ovu14n3Z0fxyreVzAW4A,383
@@ -193,7 +193,7 @@ tests/test_nc.py,sha256=kIFUOlFy_jQUjLnS9wuy6c2YkPOrKyXlMtGxDIJnXsw,4205
193
193
  tests/test_parident.py,sha256=WWCyRgqqX3bcp0Q7Pwxz4dN2kklIm5E7kBvqkO9m718,1340
194
194
  tests/test_parstudy.py,sha256=wk7WfYQrx5dtv6MnmWCfNAEInvRKsbVXYEUIIR9zLbQ,3200
195
195
  tests/test_pocfile.py,sha256=eiMLBRQxDnHIGiqku6EXcQ3fb7wGIxhXmb20iyLlPRU,5816
196
- tests/test_sizing.py,sha256=NJ0FPlHx88lvqITSJ0sypE9HENd09WOMRRWea6a7zrk,1132
196
+ tests/test_sizing.py,sha256=ThgH3xscoDDqDsyECXQzsU16xOeUOx83_K2RFwRBeiw,1133
197
197
  tests/test_sm.py,sha256=FjcAbx4n7kLH00kjnKoGMk6t4_V9TJzIaxM21MkrOjE,83524
198
198
  tests/test_tksreader.py,sha256=8QtPAzxPJbkpxd1Nw2I7ggaTaKaL4WY55JJBHkZAzus,766
199
199
  tests/test_ts.py,sha256=tR2x5cKU9gw2fUprzaPgPbCvmDOHDO36JUPCCoTlY7Y,1833
@@ -210,9 +210,9 @@ tests/moo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
210
210
  tests/moo/test_algorithm.py,sha256=Em8sFm2vzPmuIzRrBBnUQLU_TYuJHSf-kEeozw0XeX4,2563
211
211
  tests/moo/test_population.py,sha256=FvX9LRCxQx0_E2GxHQ5vKwOYFBQiNbT6Lmv5GmNWjTQ,5471
212
212
  tests/moo/test_problem.py,sha256=ALeP4u7g-dFhfwWL8vxivdrrYzVKPjHMCAXzzgyNZbs,467
213
- femagtools-1.7.1.dist-info/LICENSE,sha256=NaQe4uvkszQPJmiRPHecfk-Ab9VSRXo8xQLGNVHTeFo,1362
214
- femagtools-1.7.1.dist-info/METADATA,sha256=nm1a5JYXOmQ6qXk7AgIZES-VbkKq9kVeSqok7Jx3u7I,6147
215
- femagtools-1.7.1.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
216
- femagtools-1.7.1.dist-info/entry_points.txt,sha256=jrvOkZPiN44u1sASeu271VRaVIv5V-uRpN0_N5U_R8c,248
217
- femagtools-1.7.1.dist-info/top_level.txt,sha256=Ri4YWtU8MZTzNje9IKyXhTakNbsrCynuWdon4Yq94Dc,17
218
- femagtools-1.7.1.dist-info/RECORD,,
213
+ femagtools-1.7.2.dist-info/LICENSE,sha256=NaQe4uvkszQPJmiRPHecfk-Ab9VSRXo8xQLGNVHTeFo,1362
214
+ femagtools-1.7.2.dist-info/METADATA,sha256=lsExsTeOX67OhhhQnuXXoRqoNYGVLvjtHkKqTonSFsk,6147
215
+ femagtools-1.7.2.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
216
+ femagtools-1.7.2.dist-info/entry_points.txt,sha256=jrvOkZPiN44u1sASeu271VRaVIv5V-uRpN0_N5U_R8c,248
217
+ femagtools-1.7.2.dist-info/top_level.txt,sha256=Ri4YWtU8MZTzNje9IKyXhTakNbsrCynuWdon4Yq94Dc,17
218
+ femagtools-1.7.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.1.0)
2
+ Generator: setuptools (70.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
tests/test_bchreader.py CHANGED
@@ -317,6 +317,25 @@ class BchReaderTest(unittest.TestCase):
317
317
  self.assertEqual(bch.psidq['losses']['styoke_excess'][0], [2.235, 2.235, 2.839])
318
318
  self.assertEqual(bch.psidq['losses']['stteeth_excess'][0], [6.58, 6.576, 7.176])
319
319
  self.assertEqual(bch.psidq['losses']['rotor_excess'][0], [0.0, 0.0, 0.0])
320
-
320
+
321
+ def test_read_pm_sym_outer_rotor(self):
322
+ bch = self.read_bch('pm_sym_fast_outer_rotor.BATCH')
323
+ self.assertEqual(bch.losses[-1]['staza'], 54.219)
324
+ self.assertEqual(bch.losses[-1]['stajo'], 8.853)
325
+ self.assertEqual(bch.losses[-1]['rotfe'], 0.091)
326
+ self.assertEqual(list(bch.losses[-1]['fft']['rotor']['order_mech']), [12, 24])
327
+ self.assertEqual(list(bch.losses[-1]['fft']['rotor']['eddy']), [0.057, 0.033])
328
+ self.assertEqual(list(bch.losses[-1]['fft']['staza']['order_mech']), [4, 12, 20, 28, 36, 44, 52])
329
+ self.assertEqual(list(bch.losses[-1]['fft']['staza']['eddy']), [45.215, 4.582, 2.881, 0.839, 0.425, 0.197, 0.059])
330
+ self.assertEqual(list(bch.losses[-1]['fft']['stajo']['order_mech']), [4, 12, 20, 28, 36])
331
+ self.assertEqual(list(bch.losses[-1]['fft']['stajo']['eddy']), [7.845, 0.64, 0.311, 0.044, 0.011])
332
+
333
+ def test_read_ldlq_outer_rotor(self):
334
+ bch = self.read_bch('ldlq_outer_rotor.BATCH')
335
+ self.assertEqual(bch.ldq['losses']['styoke_eddy'][0], [63.32, 39.69, 42.85, 65.62])
336
+ self.assertEqual(bch.ldq['losses']['styoke_hyst'][0], [0.0, 0.0, 0.0, 0.0])
337
+ self.assertEqual(bch.ldq['losses']['rotor_eddy'][0], [0.05937, 0.4195, 1.74, 2.705])
338
+ self.assertEqual(bch.ldq['losses']['rotor_hyst'][0], [0.0, 0.0, 0.0, 0.0])
339
+
321
340
  if __name__ == '__main__':
322
341
  unittest.main()
tests/test_sizing.py CHANGED
@@ -31,9 +31,9 @@ def test_spm():
31
31
 
32
32
  r = femagtools.machine.sizing.spm(P, n, p, udc=udc,
33
33
  Hc=700, sigmas=fs, brem=1.1, Ba=0.77,
34
- cos_phi=0.7, eta=0.8, demag=1.7,
34
+ cos_phi=0.7, eta=0.8, demag=6.8,
35
35
  lda=0.9)
36
- assert round(r['outer_diam'], 3) == 0.18
36
+ assert round(r['outer_diam'], 3) == 0.181
37
37
  assert r['stator']['num_slots'] == 24
38
38
 
39
39