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.
- femagtools/__init__.py +1 -1
- femagtools/bch.py +11 -1
- femagtools/dxfsl/area.py +108 -7
- femagtools/dxfsl/conv.py +15 -0
- femagtools/dxfsl/converter.py +44 -20
- femagtools/dxfsl/fslrenderer.py +93 -42
- femagtools/dxfsl/functions.py +8 -0
- femagtools/dxfsl/geom.py +126 -18
- femagtools/dxfsl/machine.py +30 -9
- femagtools/femag.py +3 -3
- femagtools/fsl.py +73 -48
- femagtools/isa7.py +2 -2
- femagtools/machine/effloss.py +2 -0
- femagtools/machine/pm.py +198 -42
- femagtools/machine/sm.py +294 -253
- femagtools/machine/utils.py +5 -14
- femagtools/model.py +32 -2
- femagtools/moo/algorithm.py +6 -0
- femagtools/nc.py +2 -0
- femagtools/opt.py +2 -1
- femagtools/plot/bch.py +19 -5
- femagtools/plot/char.py +4 -4
- femagtools/plot/nc.py +21 -4
- femagtools/plot/wdg.py +38 -26
- femagtools/templates/gen_hairpin_winding.mako +209 -0
- femagtools/templates/gen_winding.mako +8 -9
- femagtools/templates/magnetIron.mako +32 -6
- femagtools/templates/mesh-airgap.mako +9 -0
- femagtools/templates/rotor_winding.mako +10 -6
- femagtools/templates/statorRotor3.mako +8 -5
- femagtools/windings.py +31 -18
- {femagtools-1.7.5.dist-info → femagtools-1.7.7.dist-info}/METADATA +1 -1
- {femagtools-1.7.5.dist-info → femagtools-1.7.7.dist-info}/RECORD +38 -37
- {femagtools-1.7.5.dist-info → femagtools-1.7.7.dist-info}/WHEEL +1 -1
- tests/test_windings.py +1 -1
- {femagtools-1.7.5.dist-info → femagtools-1.7.7.dist-info}/LICENSE +0 -0
- {femagtools-1.7.5.dist-info → femagtools-1.7.7.dist-info}/entry_points.txt +0 -0
- {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.
|
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.
|
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
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
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() == '
|
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.
|
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
|
-
|
316
|
-
params['
|
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
|
-
|
323
|
-
conv = convert(
|
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
|
-
|
332
|
-
self.
|
333
|
-
del
|
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
|
-
'
|
355
|
-
'
|
356
|
-
'
|
357
|
-
'
|
358
|
-
'
|
359
|
-
'xai,
|
360
|
-
'
|
361
|
-
'xai,
|
362
|
-
'
|
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
|
-
|
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
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
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.
|
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
|
-
|
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.
|
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,
|
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, '')
|
femagtools/machine/effloss.py
CHANGED
@@ -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
|
-
|
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
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
(
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
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':
|
1553
|
-
'istat':
|
1554
|
-
|
1555
|
-
'
|
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])}
|