femagtools 1.7.5__py3-none-any.whl → 1.7.7__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.
Files changed (38) hide show
  1. femagtools/__init__.py +1 -1
  2. femagtools/bch.py +11 -1
  3. femagtools/dxfsl/area.py +108 -7
  4. femagtools/dxfsl/conv.py +15 -0
  5. femagtools/dxfsl/converter.py +44 -20
  6. femagtools/dxfsl/fslrenderer.py +93 -42
  7. femagtools/dxfsl/functions.py +8 -0
  8. femagtools/dxfsl/geom.py +126 -18
  9. femagtools/dxfsl/machine.py +30 -9
  10. femagtools/femag.py +3 -3
  11. femagtools/fsl.py +73 -48
  12. femagtools/isa7.py +2 -2
  13. femagtools/machine/effloss.py +2 -0
  14. femagtools/machine/pm.py +198 -42
  15. femagtools/machine/sm.py +294 -253
  16. femagtools/machine/utils.py +5 -14
  17. femagtools/model.py +32 -2
  18. femagtools/moo/algorithm.py +6 -0
  19. femagtools/nc.py +2 -0
  20. femagtools/opt.py +2 -1
  21. femagtools/plot/bch.py +19 -5
  22. femagtools/plot/char.py +4 -4
  23. femagtools/plot/nc.py +21 -4
  24. femagtools/plot/wdg.py +38 -26
  25. femagtools/templates/gen_hairpin_winding.mako +209 -0
  26. femagtools/templates/gen_winding.mako +8 -9
  27. femagtools/templates/magnetIron.mako +32 -6
  28. femagtools/templates/mesh-airgap.mako +9 -0
  29. femagtools/templates/rotor_winding.mako +10 -6
  30. femagtools/templates/statorRotor3.mako +8 -5
  31. femagtools/windings.py +31 -18
  32. {femagtools-1.7.5.dist-info → femagtools-1.7.7.dist-info}/METADATA +1 -1
  33. {femagtools-1.7.5.dist-info → femagtools-1.7.7.dist-info}/RECORD +38 -37
  34. {femagtools-1.7.5.dist-info → femagtools-1.7.7.dist-info}/WHEEL +1 -1
  35. tests/test_windings.py +1 -1
  36. {femagtools-1.7.5.dist-info → femagtools-1.7.7.dist-info}/LICENSE +0 -0
  37. {femagtools-1.7.5.dist-info → femagtools-1.7.7.dist-info}/entry_points.txt +0 -0
  38. {femagtools-1.7.5.dist-info → femagtools-1.7.7.dist-info}/top_level.txt +0 -0
femagtools/fsl.py CHANGED
@@ -42,7 +42,7 @@ class Builder:
42
42
  default_filters=['decode.utf8'])
43
43
 
44
44
  self.fsl_stator = False
45
- self.fsl_magnet = False
45
+ self.fsl_rotor = False
46
46
 
47
47
  def create_wdg_def(self, model):
48
48
  name = 'winding'
@@ -216,7 +216,7 @@ class Builder:
216
216
  .format(model.magnet.get('mcvkey_shaft', 'dummy'))]
217
217
 
218
218
  if 'magnetFsl' in model.magnet:
219
- self.fsl_magnet = True
219
+ self.fsl_rotor = True
220
220
  # obsolete
221
221
  if 'parameter' in model.magnet['magnetFsl']:
222
222
  return mcv + self.render_template(
@@ -242,10 +242,10 @@ class Builder:
242
242
  magmodel['mcvkey_magnet'] = model.get_mcvkey_magnet()
243
243
  if templ == 'dxf':
244
244
  return mcv + [
245
- u'xmag = {}',
246
- u'ymag = {}',
247
- u'mag_orient = {}',
248
- u'ndt(agndst)'] + model.magnet['dxf']['fsl']
245
+ 'xmag = {}',
246
+ 'ymag = {}',
247
+ 'mag_orient = {}',
248
+ 'ndt(agndst)'] + model.magnet['dxf']['fsl']
249
249
 
250
250
  return mcv + self.render_rotor(magmodel, templ)
251
251
 
@@ -267,12 +267,16 @@ class Builder:
267
267
  rotmodel['winding_inside'] = not model.external_rotor
268
268
  culosses = self.create_cu_losses(rotmodel, condMat)
269
269
 
270
- rotmodel.update(model.rotor[templ])
271
270
  rotmodel['is_rotor'] = True # just in case for the template
271
+ if templ == 'EESM':
272
+ if 'dxf' in rotmodel:
273
+ return mcv + ['ndt(agndst)'] + rotmodel['dxf']['fsl']
274
+ templ = 'rot_hsm'
275
+ rotmodel.update(model.rotor[templ])
272
276
  return mcv + culosses + self.render_rotor(rotmodel, templ)
273
277
 
274
278
  def create_rotor_winding(self, model):
275
- if hasattr(model, 'rotor') and model.rotortype() == 'rot_hsm':
279
+ if hasattr(model, 'rotor') and model.rotortype() == 'EESM':
276
280
  return self.render_rotor(model.rotor, 'rotor_winding')
277
281
  return []
278
282
 
@@ -306,21 +310,22 @@ class Builder:
306
310
 
307
311
  if templ == 'dxf':
308
312
  # reuse dxfsl model
309
- self.fsl_magnet = True
313
+ self.fsl_rotor = True
310
314
  if templ != 'dxffile':
311
315
  return
312
316
 
313
317
  from femagtools.dxfsl.converter import convert
314
318
  params = {}
315
- params['split'] = model.magnet[templ].get('split', False)
316
- params['show_plots'] = model.magnet[templ].get('plot', False)
319
+ rotor = model.magnet
320
+ params['split'] = rotor[templ].get('split', False)
321
+ params['show_plots'] = rotor[templ].get('plot', False)
317
322
  params['write_fsl'] = True
318
323
  params['airgap'] = -1.0
319
324
  pos = 'out' if model.external_rotor else 'in'
320
325
  params['part'] = ('rotor', pos)
321
326
  logger.info("Conv rotor from %s",
322
- model.magnet[templ]['name'])
323
- conv = convert(model.magnet[templ]['name'], **params)
327
+ rotor[templ]['name'])
328
+ conv = convert(rotor[templ]['name'], **params)
324
329
  model.set_value('poles', int(conv.get('num_poles')))
325
330
  self.set_diameter_parameter(model, conv)
326
331
  if model.get('da2'):
@@ -328,9 +333,9 @@ class Builder:
328
333
  ag = (model.get('bore_diam') - model.get('da2')/1e3)/2
329
334
  model.set_value('airgap', ag)
330
335
 
331
- model.magnet['dxf'] = dict(fsl=conv['fsl'])
332
- self.fsl_magnet = True
333
- del model.magnet[templ]
336
+ rotor['dxf'] = dict(fsl=conv['fsl'])
337
+ self.fsl_rotor = True
338
+ del rotor[templ]
334
339
 
335
340
  def render_rotor(self, magmodel, templ):
336
341
  fslcode = self.__render(magmodel, templ, magnet=True)
@@ -351,23 +356,25 @@ class Builder:
351
356
  if 'thcond' in model.stator:
352
357
  fslcmds += [
353
358
  '-- thermal properties in airgap',
354
- 'ag_cond = 0.063',
355
- 'thcap = 1007',
356
- 'beta = math.pi*m.npols_gen/m.num_poles + m.zeroangl/180*math.pi',
357
- 'xai, yai = pr2c((da1+da2)/4, beta)',
358
- 'def_mat_therm(xai,yai,"cyan",1.19,ag_cond,thcap,1)',
359
- 'xai, yai = pr2c((da1+da2)/4-ag/4, beta)',
360
- 'def_mat_therm(xai,yai,"cyan",1.19,ag_cond,thcap,1)',
361
- 'xai, yai = pr2c((da1+da2)/4+ag/4, beta)',
362
- 'def_mat_therm(xai,yai,"cyan",1.19,ag_cond,thcap,1)',
359
+ 'if m.zeroangl ~= nil then',
360
+ ' ag_cond = 0.063',
361
+ ' thcap = 1007',
362
+ ' beta = math.pi*m.npols_gen/m.num_poles + m.zeroangl/180*math.pi',
363
+ ' xai, yai = pr2c((da1+da2)/4, beta)',
364
+ ' def_mat_therm(xai,yai,"cyan",1.19,ag_cond,thcap,1)',
365
+ ' xai, yai = pr2c((da1+da2)/4-ag/4, beta)',
366
+ ' def_mat_therm(xai,yai,"cyan",1.19,ag_cond,thcap,1)',
367
+ ' xai, yai = pr2c((da1+da2)/4+ag/4, beta)',
368
+ ' def_mat_therm(xai,yai,"cyan",1.19,ag_cond,thcap,1)',
363
369
  '',
364
- 'state_of_problem("therm_static") -- thermic boundary conditions',
365
- 'x1,y1 = pd2c(dy2/2,m.zeroangl)',
366
- 'x2,y2 = pd2c(dy1/2,m.zeroangl)',
367
- 'beta = 360*m.npols_gen/m.num_poles',
368
- 'x3,y3 = pd2c(dy1/2,beta+m.zeroangl)',
369
- 'x4,y4 = pd2c(dy2/2,beta+m.zeroangl)',
370
- 'def_bcond_tp(x1,y1,x2,y2,x3,y3,x4,y4, 4)',
370
+ ' state_of_problem("therm_static") -- thermic boundary conditions',
371
+ ' x1,y1 = pd2c(dy2/2,m.zeroangl)',
372
+ ' x2,y2 = pd2c(dy1/2,m.zeroangl)',
373
+ ' beta = 360*m.npols_gen/m.num_poles',
374
+ ' x3,y3 = pd2c(dy1/2,beta+m.zeroangl)',
375
+ ' x4,y4 = pd2c(dy2/2,beta+m.zeroangl)',
376
+ ' def_bcond_tp(x1,y1,x2,y2,x3,y3,x4,y4, 4)',
377
+ 'end',
371
378
  'state_of_problem("mag_static")']
372
379
  return fslcmds
373
380
  return []
@@ -409,10 +416,10 @@ class Builder:
409
416
  cond = condMat.find(windings['material'])
410
417
  if not cond:
411
418
  raise FslBuilderError(
412
- 'conductor material {} not found'.format(
413
- windings['material']))
419
+ 'conductor material {} not found in {}'.format(
420
+ windings['material'], condMat))
414
421
  windings['cuconduct'] = cond['elconduct']
415
- for k in ('thcond', 'thcap'):
422
+ for k in ('thcond', 'thcap', 'spmaweight'):
416
423
  if k in cond:
417
424
  windings[k] = cond[k]
418
425
 
@@ -433,7 +440,14 @@ class Builder:
433
440
  return []
434
441
 
435
442
  def create_gen_winding(self, model):
436
- genwdg = self.__render(model, 'gen_winding')
443
+ try:
444
+ if model.winding['wire']['name'] == 'hairpin_winding':
445
+ model.winding['wire'].update(
446
+ {"num_layers": model.winding["num_layers"]})
447
+ genwdg = self.__render(model.winding,
448
+ 'gen_' + model.winding['wire'].get('name'))
449
+ except KeyError: # not hairpin_winding
450
+ genwdg = self.__render(model, 'gen_winding')
437
451
  k = list({'leak_dist_wind',
438
452
  'leak_evol_wind',
439
453
  'leak_tooth_wind'}.intersection(model.winding))
@@ -480,6 +494,9 @@ class Builder:
480
494
  params['airgap'] = model.dxffile.get('airgap', 0.0)
481
495
  params['nodedist'] = model.dxffile.get('nodedist', 1)
482
496
  params['full_model'] = model.dxffile.get('full_model', False)
497
+ params['EESM'] = model.dxffile.get('type', 'PMSM') == 'EESM'
498
+ if params['EESM']:
499
+ model.rotor['EESM'] = {}
483
500
  conv = convert(dxfname, **params)
484
501
 
485
502
  model.set_value('poles', conv.get('num_poles'))
@@ -492,6 +509,7 @@ class Builder:
492
509
  if not hasattr(model, 'stator'):
493
510
  setattr(model, 'stator', {})
494
511
  model.stator['num_slots'] = conv.get('tot_num_slot')
512
+ model.stator['slot_area'] = conv.get('slot_area')
495
513
  if model.get('num_agnodes', 0) == 0:
496
514
  model.set_value('agndst', conv['agndst']*1e-3)
497
515
  logger.info("num poles %d num slots %d outer diameter %.4f m agndst %.4f mm",
@@ -509,11 +527,17 @@ class Builder:
509
527
  if 'fsl_stator' in conv:
510
528
  self.fsl_stator = True
511
529
  model.stator['dxf'] = dict(fsl=conv['fsl_stator'])
512
- if not hasattr(model, 'magnet'):
513
- setattr(model, 'magnet', {})
514
- if 'fsl_magnet' in conv:
515
- self.fsl_magnet = True
516
- model.magnet['dxf'] = dict(fsl=conv['fsl_magnet'])
530
+ if not (hasattr(model, 'magnet') or hasattr(model, 'rotor')):
531
+ if params['EESM']:
532
+ setattr(model, 'rotor', {})
533
+ else:
534
+ setattr(model, 'magnet', {})
535
+ if 'fsl_rotor' in conv:
536
+ self.fsl_rotor = True
537
+ if hasattr(model, 'magnet'):
538
+ model.magnet['dxf'] = dict(fsl=conv['fsl_rotor'])
539
+ if hasattr(model, 'rotor'):
540
+ model.rotor['dxf'] = dict(fsl=conv['fsl_rotor'])
517
541
 
518
542
  def create_model(self, model, magnets=[], condMat=[], ignore_material=False):
519
543
  magnetMat = {}
@@ -563,10 +587,9 @@ class Builder:
563
587
  'remanenc', 1.2)
564
588
  model['magnet']['relperm'] = magnetMat.get('relperm', 1.05)
565
589
  model['magnet']['rlen'] = magnetMat.get('rlen', 1.0)
566
- for k in ('thcond', 'thcap'):
590
+ for k in ('spmaweight', 'thcond', 'thcap'):
567
591
  if k in magnetMat:
568
- model['magnet'][k] = magnetMat[k]
569
-
592
+ model['magnet'][k+'_magnet'] = magnetMat[k]
570
593
  rotor = (self.create_magnet(model) +
571
594
  self.create_magnet_model(model))
572
595
  if magnetMat:
@@ -714,11 +737,10 @@ class Builder:
714
737
  return self.__render(model, 'colorgrad')
715
738
 
716
739
  def mesh_airgap(self, model):
717
- if ((self.fsl_stator and self.fsl_magnet) or
740
+ if ((self.fsl_stator and self.fsl_rotor) or
718
741
  model.get('num_agnodes', 0)):
719
742
  return self.__render(model, 'mesh-airgap')
720
- else:
721
- return []
743
+ return []
722
744
 
723
745
  def create(self, model, sim, magnets=None, condMat=[]):
724
746
  "create model and analysis function"
@@ -769,6 +791,9 @@ class Builder:
769
791
  return (fslmodel + self.create_analysis(sim) +
770
792
  ['save_model("close")'])
771
793
 
794
+ def create_detailed_wire(self, params, templ):
795
+ return self.__render(params, templ)
796
+
772
797
  def __render(self, model, templ, stator=False, magnet=False):
773
798
  if templ.split('.')[-1] in ('fsl', 'mako'):
774
799
  try:
@@ -788,7 +813,7 @@ class Builder:
788
813
  if stator:
789
814
  self.fsl_stator = True
790
815
  if magnet:
791
- self.fsl_magnet = True
816
+ self.fsl_rotor = True
792
817
 
793
818
  return template.render_unicode(model=model).split('\n')
794
819
 
femagtools/isa7.py CHANGED
@@ -528,7 +528,7 @@ class Isa7(object):
528
528
 
529
529
  color = {1: [1.0, 0.0, 0.0], # RED
530
530
  2: [0.0, 1.0, 0.0], # GREEN
531
- 3: [1.0, 1.0, 0.0], # DARKYELLOW
531
+ 3: [1.0, 0.8, 0.0], # DARKYELLOW
532
532
  4: [0.0, 0.5019607843137255, 1.0], # BLUE
533
533
  5: [0.9803921568627451, 0.0, 1.0], # MAGENTA
534
534
  6: [0.0, 1.0, 0.8235294117647058], # CYAN
@@ -749,7 +749,7 @@ class Isa7(object):
749
749
  for e in self.elements])
750
750
 
751
751
  for a in ('FC_RADIUS', 'pole_pairs', 'poles_sim',
752
- 'delta_node_angle', 'speed',
752
+ 'layers', 'coil_span', 'delta_node_angle', 'speed',
753
753
  'MAGN_TEMPERATURE', 'BR_TEMP_COEF',
754
754
  'MA_SPEZ_WEIGHT', 'CU_SPEZ_WEIGHT'):
755
755
  v = getattr(reader, a, '')
@@ -323,6 +323,8 @@ def efficiency_losses_map(eecpars, u1, T, temp, n, npoints=(60, 40),
323
323
  plcu1 = m.iqd_plcu1(iqd[0], iqd[1], 2*np.pi*f1)
324
324
  plcu2 = m.iqd_plcu2(*iqd)
325
325
  tfric = m.tfric
326
+ logger.info("Iex %f %f",
327
+ np.min(iqd[2]), np.max(iqd[2]))
326
328
  else:
327
329
  plfe1 = np.array(r['plfe1'])
328
330
  plfe2 = np.zeros(ntmesh.shape[1])
femagtools/machine/pm.py CHANGED
@@ -5,7 +5,7 @@ import logging
5
5
  import warnings
6
6
  import numpy as np
7
7
  import numpy.linalg as la
8
- from .utils import iqd, betai1, skin_resistance, dqparident, KTH, K
8
+ from .utils import iqd, betai1, skin_resistance, dqparident, KTH, K, T
9
9
  import scipy.optimize as so
10
10
  import scipy.interpolate as ip
11
11
  import scipy.integrate as ig
@@ -14,6 +14,38 @@ from functools import partial
14
14
  logger = logging.getLogger(__name__)
15
15
 
16
16
 
17
+ def find_peaks_and_valleys(t, iabc, tshort):
18
+ """ return peaks and valleys of phase current with maximum amplitude
19
+ """
20
+ iph = iabc[np.argmax([np.max(np.abs(iph))
21
+ for iph in iabc])]
22
+ ts = t[t>tshort]
23
+ Z = iph[t>tshort]
24
+ peaks = (np.diff(np.sign(np.diff(Z))) < 0).nonzero()[0] + 1
25
+ if len(peaks>0):
26
+ p = {'ip': Z[peaks].tolist(), 'tp': ts[peaks].tolist()}
27
+ else:
28
+ p = {'ip': [], 'tp': []}
29
+ valleys = (np.diff(np.sign(np.diff(Z))) > 0).nonzero()[0] + 1
30
+ if len(valleys>0):
31
+ v = {'iv': Z[valleys].tolist(), 'tv': ts[valleys].tolist()}
32
+ else:
33
+ v = {'iv': [], 'tv': []}
34
+ try:
35
+ cs = ip.CubicSpline(ts[peaks], Z[peaks])
36
+ p.update({'i': cs(ts).tolist(), 't': ts.tolist()})
37
+ except ValueError as e:
38
+ logger.warning("no peaks in current: %d",
39
+ len(peaks))
40
+ try:
41
+ cs = ip.CubicSpline(ts[valleys], Z[valleys])
42
+ v.update({'i': cs(ts).tolist(), 't': ts.tolist()})
43
+ except ValueError as e:
44
+ logger.warning("no valleys in current: %d",
45
+ len(valleys))
46
+ return p, v
47
+
48
+
17
49
  def parident(workdir, engine, temp, machine,
18
50
  magnetizingCurves, magnetMat, condMat,
19
51
  **kwargs):
@@ -109,6 +141,14 @@ class PmRelMachine(object):
109
141
  def rstat(self, w):
110
142
  """stator resistance
111
143
  """
144
+ if isinstance(self.zeta1, list):
145
+ logger.info("setup ac loss parameters...")
146
+ # polyfit from ac loss calculation
147
+ freq = w/2/np.pi
148
+ kr = self.zeta1[0]*freq**3 + self.zeta1[1]*freq**2 + \
149
+ self.zeta1[2]*freq + self.zeta1[3]
150
+ kr[kr<1] = 1.
151
+ return self.r1*(1 - self.kth1*(self.tcu1 - 20))*kr # ref 20°C
112
152
  if self.skin_resistance is not None:
113
153
  return self.skin_resistance(self.r1, w, self.tcu1, kth=self.kth1)
114
154
 
@@ -915,7 +955,7 @@ class PmRelMachine(object):
915
955
  i1max: max. phase current (RMS)
916
956
  """
917
957
  r = dict(id=[], iq=[], uq=[], ud=[], u1=[], i1=[], T=[],
918
- beta=[], gamma=[], phi=[], cosphi=[], pmech=[], n=[])
958
+ beta=[], gamma=[], phi=[], cosphi=[], pmech=[], n=[], type_op=[])
919
959
 
920
960
  if kwargs.get('i1max', 0):
921
961
  w1type, T = self.w1_imax_umax(kwargs['i1max'], u1max)
@@ -1000,6 +1040,7 @@ class PmRelMachine(object):
1000
1040
  r['n'].append(nx)
1001
1041
  r['T'].append(Tf)
1002
1042
 
1043
+ r['type_op'] = list(betai1(iq, id))
1003
1044
  Pmax = 2*np.pi*n1*Tf
1004
1045
  for ns, nu, iv in zip(nstab[1:], speedrange[2:], interv):
1005
1046
  # find id, iq, torque in fieldweakening range
@@ -1487,14 +1528,62 @@ class PmRelMachinePsidq(PmRelMachine):
1487
1528
  def betai1_plmag(self, beta, i1, f1):
1488
1529
  return self.iqd_plmag(*iqd(beta, i1), f1)
1489
1530
 
1531
+ def ldlqpsim(self):
1532
+ def ext_array(id, iq, a):
1533
+ """extend array a if current is 0 at edge
1534
+ id: list of n id values
1535
+ iq: list of m iq values
1536
+ a: nxm array to extend"""
1537
+ if id[0] == 0:
1538
+ y = np.array(a)[:, 1].reshape((-1, 1))
1539
+ m = np.hstack((y, a))
1540
+ elif id[-1] == 0:
1541
+ y = np.array(a)[:, -2].reshape((-1, 1))
1542
+ m = np.hstack((a, y))
1543
+ else:
1544
+ m = np.array(a)
1545
+
1546
+ if iq[0] == 0:
1547
+ return np.concatenate(([m[1]], m))
1548
+ elif iq[-1] == 0:
1549
+ return np.concatenate((m, [m[-2]]))
1550
+
1551
+ return m
1552
+
1553
+ idn = np.append(self.id, -self.id[-2])
1554
+ iqz = np.where(self.iq == 0.)[0][0]
1555
+ if iqz in {0, len(self.iq)-1}:
1556
+ iqn = np.insert(self.iq, 0, -self.iq[1])
1557
+ elif iqz == len(self.iq)-1:
1558
+ iqn = np.append(self.iq, -self.iq[iqz-1])
1559
+ else:
1560
+ iqn = np.array(self.iq)
1561
+ psid2 = ext_array(self.id, self.iq, self.psid)
1562
+ psiq2 = ext_array(self.id, self.iq, self.psiq)
1563
+
1564
+ # create n x m matrix of currents
1565
+ id = np.ones(psid2.shape) * idn
1566
+ iq = (np.ones(psid2.shape).T * iqn).T
1567
+
1568
+ # calculate ec model parameters
1569
+ psim = (np.ones(psid2.shape).T * psid2[id == 0.]).T
1570
+ nz = np.any(id != 0., axis=0)
1571
+ ld = ((psid2-psim)[:, nz])/id[:, nz]
1572
+ nz = np.any(iq != 0., axis=1)
1573
+ lq = (psiq2[nz, :])/iq[nz, :]
1574
+
1575
+ # create interpolation functions
1576
+ return (ip.RectBivariateSpline(iq[:, 0], id[0][id[0] != 0], ld),
1577
+ ip.RectBivariateSpline(iq[:, 0][iq[:, 0] != 0], id[0], lq),
1578
+ ip.RectBivariateSpline(iq[:, 0], id[0], psim))
1579
+
1490
1580
 
1491
1581
  ### EXPERIMENTAL
1492
1582
 
1493
1583
  def transient(self, u1, tload, speed,
1494
1584
  fault_type=3, # 'LLL', 'LL', 'LG',
1495
- tend=0.1, nsamples=200):
1496
-
1497
- tshort = 0
1585
+ tshort=0, tend=0.1, nsamples=200):
1586
+ ns = round(tshort/tend*nsamples), round((tend-tshort)/tend*nsamples)
1498
1587
  w1 = 2*np.pi*self.p*speed
1499
1588
  i0 = self.iqd_torque(tload)
1500
1589
  res = so.minimize(
@@ -1507,49 +1596,116 @@ class PmRelMachinePsidq(PmRelMachine):
1507
1596
  - la.norm(self.uqd(w1, *iqd))}))
1508
1597
  iqx, idx = res.x
1509
1598
  uq0, ud0 = self.uqd(w1, iqx, idx)
1599
+ psid = ip.RectBivariateSpline(self.iq, self.id, self.psid, kx=3, ky=3)
1600
+ psiq = ip.RectBivariateSpline(self.iq, self.id, self.psiq, kx=3, ky=3)
1510
1601
  logger.info("transient: Torque %f Nm, Speed %f rpm, Curr %f A",
1511
1602
  tload, speed*60, betai1(iqx, idx)[1])
1603
+ #_ld, _lq, _psim = self.ldlqpsim()
1604
+ #Ld = _ld(iqx, idx)[0, 0]
1605
+ #Lq = _lq(iqx, idx)[0, 0]
1606
+ #psim = _psim(iqx, idx)[0, 0]
1607
+ #logger.info("idx %f iqx %f, Ld %f, Lq %f, psim %f",
1608
+ # idx, iqx, Ld, Lq, psim)
1512
1609
  if fault_type == 3: # 3 phase short circuit
1513
- USC = lambda t: np.zeros(2)
1610
+ Y0 = iqx, idx
1611
+ def U(t):
1612
+ return (uq0, ud0) if t < tshort else (0, 0)
1613
+ def didt(t, iqd):
1614
+ uq, ud = U(t)
1615
+ ldd = psid(*iqd, dx=0, dy=1)[0,0]
1616
+ lqq = psiq(*iqd, dx=1, dy=0)[0,0]
1617
+ ldq = psid(*iqd, dx=1, dy=0)[0,0]
1618
+ lqd = psiq(*iqd, dx=0, dy=1)[0,0]
1619
+ psi = psid(*iqd)[0,0], psiq(*iqd)[0,0]
1620
+ return [
1621
+ (-ldd*psi[0]*w1 + ldd*(uq-self.r1*iqd[0])
1622
+ - lqd*psi[1]*w1 - lqd*(ud-self.r1*iqd[1]))/(ldd*lqq - ldq*lqd),
1623
+ (ldq*psi[0]*w1 - ldq*(uq-self.r1*iqd[0])
1624
+ + lqq*psi[1]*w1 + lqq*(ud-self.r1*iqd[1]))/(ldd*lqq - ldq*lqd)]
1625
+ #def didtl(t, iqd):
1626
+ # lqd = lq(*iqd)[0,0], ld(*iqd)[0,0]
1627
+ # return [
1628
+ # (uq-r1*iqd[0] -w1 * ld*iqd[1] - w1*psim(*iqd)[0,0])/lq,
1629
+ # (ud-r1*iqd[1] +w1 * lq*iqd[0])/ld]
1514
1630
  else: # 2 phase short circuit
1515
- #ustat = np.array([K(w1*x).dot((uq, ud)) for x in t])
1516
- USC = lambda t: np.array([
1517
- uq0/2*(1+np.cos(2*w1*t)),
1518
- uq0/2*np.sin(2*w1*t)])
1519
- U = lambda t: (uq0, ud0) if t < tshort else USC(t)
1520
-
1521
- psid = ip.RectBivariateSpline(self.iq, self.id, self.psid, kx=3, ky=3)
1522
- psiq = ip.RectBivariateSpline(self.iq, self.id, self.psiq, kx=3, ky=3)
1523
- #ld = ip.RectBivariateSpline(iq, id, dqpars['psidq'][0]['ld'], kx=3, ky=3)
1524
- #lq = ip.RectBivariateSpline(iq, id, dqpars['psidq'][0]['lq'], kx=3, ky=3)
1525
- #psim = ip.RectBivariateSpline(iq, id, dqpars['psidq'][0]['psim'], kx=3, ky=3)
1526
- #def didtl(t, iqd):
1527
- # lqd = lq(*iqd)[0,0], ld(*iqd)[0,0]
1528
- # return [
1529
- # (uq-r1*iqd[0] -w1 * lqd[1]*iqd[1] - w1*psim(*iqd)[0,0])/lqd[0],
1530
- # (ud-r1*iqd[1] +w1*lqd[0]*iqd[0])/lqd[1]]
1531
-
1532
- def didt(t, iqd):
1533
- uq, ud = U(t)
1534
- ldd = psid(*iqd, dx=0, dy=1)[0,0]
1535
- lqq = psiq(*iqd, dx=1, dy=0)[0,0]
1536
- ldq = psid(*iqd, dx=1, dy=0)[0,0]
1537
- lqd = psiq(*iqd, dx=0, dy=1)[0,0]
1538
- psi = psid(*iqd)[0,0], psiq(*iqd)[0,0]
1539
- return [
1540
- (-ldd*psi[0]*w1 + ldd*(uq-self.r1*iqd[0])
1541
- - lqd*psi[1]*w1 - lqd*(ud-self.r1*iqd[1]))/(ldd*lqq - ldq*lqd),
1542
- (ldq*psi[0]*w1 - ldq*(uq-self.r1*iqd[0])
1543
- + lqq*psi[1]*w1 + lqq*(ud-self.r1*iqd[1]))/(ldd*lqq - ldq*lqd)]
1544
-
1545
- t = np.linspace(0, tend, nsamples)
1546
- Y0 = iqx, idx
1631
+ _ld, _lq, _psim = self.ldlqpsim()
1632
+ Ld = _ld(iqx, idx)[0, 0]
1633
+ Lq = _lq(iqx, idx)[0, 0]
1634
+ psim = _psim(iqx, idx)[0, 0]
1635
+ Y0 = (0,)
1636
+ def didt(t, i):
1637
+ gamma = w1*t
1638
+ iqd = [2/3*i*(-np.sin(gamma) + np.sin(gamma+2*np.pi/3)),
1639
+ 2/3*i*(np.cos(gamma) + np.cos(gamma+2*np.pi/3))]
1640
+ ldd = psid(*iqd, dx=0, dy=1)[0,0]
1641
+ lqq = psiq(*iqd, dx=1, dy=0)[0,0]
1642
+ ldq = psid(*iqd, dx=1, dy=0)[0,0]
1643
+ lqd = psiq(*iqd, dx=0, dy=1)[0,0]
1644
+ psi = psid(*iqd)[0, 0], psiq(*iqd)[0, 0]
1645
+ A = ((ldd-lqq)*np.cos(2*gamma + np.pi/3)
1646
+ - (ldq+lqd)*np.sin(2*gamma + np.pi/3) + lqq + ldd)
1647
+ B = 2/3*w1*((ldd-lqq)*np.sin(2*gamma + np.pi/3)
1648
+ + (ldq+lqd)*np.cos(2*gamma + np.pi/3)
1649
+ + ldq - lqd) + 2*self.r1
1650
+ C = np.sqrt(3)*w1*(psi[0]*np.sin(gamma + np.pi/6)
1651
+ + psi[1]*np.cos(gamma + np.pi/6))
1652
+ return -(B*i + C)/A
1653
+
1654
+ #def didt2(t, i):
1655
+ # gamma = w1*t
1656
+ # idy, iqy = T(gamma).dot([i[0], -i[0], 0])
1657
+ # ua - ub = 0; ia = -ib; ic = 0
1658
+ # B = np.sqrt(3)*psim*np.cos(gamma + np.pi/6)
1659
+ # A = 2*Ld*np.cos(gamma + np.pi/6)**2 + 2*Lq*np.sin(gamma + np.pi/6)**2
1660
+ # dAdt = 4*w1*np.cos(gamma+np.pi/6)*np.sin(gamma+np.pi/6)*(Ld - Lq)
1661
+ # dBdt = np.sqrt(3)*w1*psim*np.sin(gamma+np.pi/6)
1662
+
1663
+ # return -(i*dAdt + dBdt + 2*self.r1*i)/A
1664
+
1665
+ t = np.linspace(tshort, tend, ns[1])
1547
1666
  sol = ig.solve_ivp(didt, (t[0], t[-1]), Y0, dense_output=True)
1548
1667
  y = sol.sol(t).T
1549
1668
 
1669
+ t = np.linspace(0, tend, nsamples)
1670
+ if fault_type == 3: # 3 phase short circuit
1671
+ if ns[0] > 0:
1672
+ iqd = np.vstack(
1673
+ (np.ones((ns[0], 2)) * (iqx, idx), y))
1674
+ else:
1675
+ iqd = y
1676
+ iabc = np.array([K(w1*x[0]).dot((x[1][1], x[1][0]))
1677
+ for x in zip(t, iqd)]).T
1678
+ peaks, valleys = find_peaks_and_valleys(t, iabc, tshort)
1679
+
1680
+ #iqx, idx = iqd[-1, 0], iqd[-1, 1],
1681
+ #Ld = _ld(iqx, idx)[0, 0]
1682
+ #Lq = _lq(iqx, idx)[0, 0]
1683
+ #psim = _psim(iqx, idx)[0, 0]
1684
+ #logger.info("idx %f iqx %f, Ld %f, Lq %f, psim %f",
1685
+ # idx, iqx, Ld, Lq, psim)
1686
+ return {
1687
+ 't': t.tolist(),
1688
+ 'iq': iqd[:,0], 'id': iqd[:,1],
1689
+ 'istat': iabc.tolist(),
1690
+ 'peaks': peaks,
1691
+ 'valleys': valleys,
1692
+ 'torque': [self.torque_iqd(*x) for x in iqd]}
1693
+ if ns[0] > 0:
1694
+ iabc = np.hstack(
1695
+ (np.array(
1696
+ [K(w1*t).dot((idx, iqx))
1697
+ for t in np.linspace(0, tshort, ns[0])]).T,
1698
+ [y[:, 0], (-y)[:, 0], np.zeros(ns[1])]))
1699
+ else:
1700
+ iabc = np.array(
1701
+ [y[:, 0], (-y)[:, 0], np.zeros(len(t))])
1702
+ peaks, valleys = find_peaks_and_valleys(t, iabc, tshort)
1703
+ idq = np.array([T(w1*x[0]).dot(x[1])
1704
+ for x in zip(t, iabc.T)]).T
1550
1705
  return {
1551
1706
  't': t.tolist(),
1552
- 'iq': y[:,0], 'id': y[:,1],
1553
- 'istat': np.array([K(w1*x[0]).dot((x[1][1], x[1][0]))
1554
- for x in zip(t, y)]).T.tolist(),
1555
- 'torque': [self.torque_iqd(*iqd) for iqd in y]}
1707
+ 'iq': idq[1], 'id': idq[0],
1708
+ 'istat': iabc.tolist(),
1709
+ 'peaks': peaks,
1710
+ 'valleys': valleys,
1711
+ 'torque': self.torque_iqd(idq[1], idq[0])}