femagtools 1.8.6__py3-none-any.whl → 1.8.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 -7
- femagtools/femag.py +8 -70
- femagtools/fsl.py +6 -3
- femagtools/isa7.py +47 -3
- femagtools/losscoeffs.py +29 -3
- femagtools/machine/afpm.py +15 -6
- femagtools/mcv.py +162 -7
- femagtools/parstudy.py +3 -1
- femagtools/plot/bch.py +126 -44
- femagtools/plot/nc.py +13 -0
- femagtools/shortcircuit.py +378 -0
- femagtools/templates/psi-torq-rem-rot.mako +127 -0
- {femagtools-1.8.6.dist-info → femagtools-1.8.7.dist-info}/METADATA +1 -1
- {femagtools-1.8.6.dist-info → femagtools-1.8.7.dist-info}/RECORD +20 -18
- tests/test_nc.py +11 -0
- {femagtools-1.8.6.dist-info → femagtools-1.8.7.dist-info}/LICENSE +0 -0
- {femagtools-1.8.6.dist-info → femagtools-1.8.7.dist-info}/WHEEL +0 -0
- {femagtools-1.8.6.dist-info → femagtools-1.8.7.dist-info}/entry_points.txt +0 -0
- {femagtools-1.8.6.dist-info → femagtools-1.8.7.dist-info}/top_level.txt +0 -0
femagtools/plot/bch.py
CHANGED
@@ -515,80 +515,151 @@ def cogging(bch, title=''):
|
|
515
515
|
fig.subplots_adjust(top=0.92)
|
516
516
|
|
517
517
|
|
518
|
-
def
|
519
|
-
"""
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
518
|
+
def demagnetization(demag, ax=0):
|
519
|
+
"""plot rel. remanence vs. current"""
|
520
|
+
if ax == 0:
|
521
|
+
ax = plt.gca()
|
522
|
+
scale = 1
|
523
|
+
unit = 'A'
|
524
|
+
if np.max(demag['i1']) > 25e3:
|
525
|
+
scale = 1e-3
|
526
|
+
unit = 'kA'
|
527
|
+
i1 = [scale*x for x in demag['i1']]
|
528
|
+
ax.plot(i1, demag['rr'], 'o', color='C0')
|
529
|
+
ax.plot(i1, demag['rr'], color='C0')
|
530
|
+
rrmin = 0.6
|
531
|
+
if demag.get('i1c', 0):
|
532
|
+
Icrit = scale*demag['i1c']
|
533
|
+
Hk = demag['Hk']
|
534
|
+
Tmag = demag['Tmag']
|
535
|
+
di = 0.05*np.max(i1)
|
536
|
+
rrmin = min(0.6, np.min(demag['rr']))
|
537
|
+
ax.plot([Icrit, Icrit], [rrmin, 1], 'k--')
|
538
|
+
ax.annotate(
|
539
|
+
f'Icrit = {Icrit:.1f}{unit}\nHk = {Hk:.1f} kA/m\nTmag={Tmag:.1f} °C',
|
540
|
+
xy=(Icrit, rrmin),
|
541
|
+
xytext=(Icrit-di, rrmin+0.1*(1-rrmin)), ha='right',
|
542
|
+
bbox={'facecolor': 'white',
|
543
|
+
'edgecolor': 'white'})
|
544
|
+
ax.set_ylim([rrmin, 1.01])
|
545
|
+
ax.set_ylabel('Rel. Remanence')
|
546
|
+
ax.set_xlabel(f'Phase Current / {unit}')
|
547
|
+
ax.grid()
|
548
|
+
|
549
|
+
|
550
|
+
def transientsc_currents(scData, ax=0, title='', set_xlabel=True):
|
551
|
+
"""plot transient shortcircuit currents vs time"""
|
552
|
+
if ax == 0:
|
553
|
+
ax = plt.gca()
|
531
554
|
ax.grid(True)
|
532
|
-
|
555
|
+
if title:
|
556
|
+
ax.set_title(title)
|
557
|
+
istat = np.array([scData[i]
|
533
558
|
for i in ('ia', 'ib', 'ic')])
|
534
559
|
pv = [find_peaks_and_valleys(
|
535
|
-
np.array(
|
560
|
+
np.array(scData['time']), i1)
|
536
561
|
for i1 in istat]
|
537
562
|
try:
|
538
563
|
ipvmax = np.argmax(
|
539
564
|
[y['yp'] if np.abs(y['yp']) > np.abs(y['yv']) else y['yv']
|
540
|
-
for y in pv])
|
565
|
+
for y in pv if y['yp']])
|
541
566
|
imax = pv[ipvmax]['yp'] if np.abs(pv[ipvmax]['yp']) > np.abs(pv[ipvmax]['yv']) else pv[ipvmax]['yv']
|
567
|
+
iac = [pv[ipvmax]['tpeaks'][-1], pv[ipvmax]['peaks'][-1]]
|
542
568
|
except KeyError:
|
543
569
|
pass
|
544
570
|
if np.max(istat) > 4000:
|
545
571
|
istat *= 1e-3
|
546
|
-
|
547
|
-
|
572
|
+
try:
|
573
|
+
imax *= 1e-3
|
574
|
+
iac[1] *= 1e-3
|
575
|
+
except NameError:
|
576
|
+
pass
|
577
|
+
ax.set_ylabel('Currents / kA')
|
548
578
|
else:
|
549
|
-
ax.
|
579
|
+
ax.set_ylabel('Currents / A')
|
550
580
|
|
551
581
|
for i, iph in zip(('ia', 'ib', 'ic'), istat):
|
552
|
-
ax.plot(
|
582
|
+
ax.plot(scData['time'], iph, label=i)
|
553
583
|
try:
|
554
584
|
ax.plot([pv[ipvmax]['tp']], [imax], '.')
|
585
|
+
ax.plot([iac[0]], [iac[1]], '.')
|
586
|
+
dtx = (scData['time'][-1]-scData['time'][0])/75
|
587
|
+
dy = imax/25
|
555
588
|
ax.annotate(f'Imax = {imax:.1f}',
|
556
589
|
xy=(pv[ipvmax]['tp'], imax),
|
557
|
-
xytext=(pv[ipvmax]['tp']+
|
590
|
+
xytext=(pv[ipvmax]['tp']+dtx, imax-dy))
|
591
|
+
dy = iac[1]/25
|
592
|
+
ax.annotate(f'I = {iac[1]:.1f}',
|
593
|
+
xy=iac,
|
594
|
+
xytext=(iac[0]+dtx, iac[1]-dy))
|
558
595
|
except NameError:
|
559
596
|
pass
|
560
|
-
|
597
|
+
if set_xlabel:
|
598
|
+
ax.set_xlabel('Time / s')
|
561
599
|
ax.legend()
|
562
600
|
|
563
|
-
|
564
|
-
|
565
|
-
|
601
|
+
|
602
|
+
def transientsc_torque(scData, ax=0, title='', set_xlabel=True):
|
603
|
+
"""plot transient shortcircuit torque vs time"""
|
604
|
+
if ax == 0:
|
605
|
+
ax = plt.gca()
|
606
|
+
if title:
|
607
|
+
ax.set_title(title)
|
566
608
|
pv = find_peaks_and_valleys(
|
567
|
-
np.array(
|
609
|
+
np.array(scData['time']), np.array(scData['torque']))
|
568
610
|
try:
|
569
611
|
tqmax = pv['yp'] if np.abs(pv['yp']) > np.abs(pv['yv']) else pv['yv']
|
570
612
|
tp = pv['tp'] if np.abs(pv['yp']) > np.abs(pv['yv']) else pv['tv']
|
571
|
-
|
613
|
+
tc = [pv['tpeaks'][-1], pv['peaks'][-1]]
|
614
|
+
except (KeyError, ValueError):
|
572
615
|
pass
|
573
|
-
torque = np.array(
|
616
|
+
torque = np.array(scData['torque'])
|
574
617
|
if np.max(torque) > 4000:
|
575
618
|
torque *= 1e-3
|
576
|
-
|
577
|
-
|
619
|
+
try:
|
620
|
+
tqmax *= 1e-3
|
621
|
+
tc[1] *= 1e-3
|
622
|
+
except NameError:
|
623
|
+
pass
|
624
|
+
ax.set_ylabel('Torque / kNm')
|
578
625
|
else:
|
579
|
-
ax.
|
626
|
+
ax.set_ylabel('Torque / Nm')
|
580
627
|
|
581
628
|
ax.grid(True)
|
582
|
-
ax.plot(
|
629
|
+
ax.plot(scData['time'], torque)
|
583
630
|
try:
|
584
631
|
ax.plot([tp], [tqmax], '.')
|
632
|
+
ax.plot([tc[0]], [tc[1]], '.')
|
633
|
+
dtx = (scData['time'][-1]-scData['time'][0])/75
|
634
|
+
dy = tqmax/25
|
585
635
|
ax.annotate(f'Tmax = {tqmax:.1f}',
|
586
636
|
xy=(tp, tqmax),
|
587
|
-
xytext=(tp+
|
637
|
+
xytext=(tp+dtx, tqmax-dy))
|
638
|
+
dy = tc[1]/25
|
639
|
+
ax.annotate(f'T = {tc[1]:.1f}',
|
640
|
+
xy=tc,
|
641
|
+
xytext=(tc[0]+dtx, tc[1]))
|
588
642
|
except NameError:
|
589
643
|
pass
|
590
|
-
|
644
|
+
if set_xlabel:
|
645
|
+
ax.set_xlabel('Time / s')
|
646
|
+
|
647
|
+
def transientsc(bch, title=''):
|
648
|
+
"""creates a transient short circuit plot"""
|
649
|
+
try:
|
650
|
+
scData = bch.scData
|
651
|
+
except AttributeError:
|
652
|
+
scData = bch
|
653
|
+
cols = 1
|
654
|
+
rows = 2
|
655
|
+
htitle = 1.5 if title else 0
|
656
|
+
fig, axs = plt.subplots(nrows=rows, ncols=cols, sharex=True,
|
657
|
+
figsize=(10, 3*rows + htitle))
|
658
|
+
if title:
|
659
|
+
fig.suptitle(title, fontsize=16)
|
591
660
|
|
661
|
+
transientsc_currents(scData, axs[0], set_xlabel=False)
|
662
|
+
transientsc_torque(scData, axs[1])
|
592
663
|
fig.tight_layout(h_pad=2)
|
593
664
|
if title:
|
594
665
|
fig.subplots_adjust(top=0.92)
|
@@ -602,24 +673,35 @@ def transientsc_demag(demag, magnet=0, title='', ax=0):
|
|
602
673
|
"""
|
603
674
|
if ax == 0:
|
604
675
|
ax = plt.gca()
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
676
|
+
if type(d) == list:
|
677
|
+
pos = [d['displ'] for d in demag if 'displ' in d]
|
678
|
+
hmax = [-d['H_max'] for d in demag if 'H_max' in d]
|
679
|
+
havg = [-d['H_av'] for d in demag if 'H_av' in d]
|
680
|
+
hclim = [-d['lim_hc'] for d in demag if 'lim_hc' in d][0]
|
681
|
+
else:
|
682
|
+
pos = demag['displ']
|
683
|
+
hmax = demag['H_max']
|
684
|
+
havg = demag['H_av']
|
685
|
+
hclim = demag['Hk']
|
610
686
|
ax.set_title('Transient Short Circuit Demagnetization [kA/m]')
|
611
687
|
ax.plot(pos, hmax,
|
612
688
|
label='H Max {:4.2f} kA/m'.format(max(hmax)))
|
613
689
|
ax.plot(pos, havg,
|
614
690
|
label='H Avg {:4.2f} kA/m'.format(max(havg)))
|
615
691
|
if len(hclim) > 1:
|
616
|
-
ax.plot([pos[0], pos[-1]], hclim, color='C3',
|
617
|
-
|
618
|
-
|
692
|
+
ax.plot([pos[0], pos[-1]], [hclim,hclim], color='C3',
|
693
|
+
linestyle='dashed',
|
694
|
+
label='Hc {:4.2f} kA/m'.format(hclim))
|
695
|
+
if 'Tmag' in demag:
|
696
|
+
Tmag = demag['Tmag']
|
697
|
+
elif magnet:
|
698
|
+
Tmag = magnet['Tmag']
|
699
|
+
else:
|
700
|
+
Tmag = ''
|
619
701
|
ax.set_xlabel('Rotor Position / °')
|
620
702
|
ax.grid(True)
|
621
|
-
if
|
622
|
-
ax.legend(title=f"Magnet Temperature {
|
703
|
+
if Tmag:
|
704
|
+
ax.legend(title=f"Magnet Temperature {Tmag}°C")
|
623
705
|
else:
|
624
706
|
ax.legend()
|
625
707
|
|
femagtools/plot/nc.py
CHANGED
@@ -130,6 +130,19 @@ def demag(isa, cmap=DEFAULT_CMAP, ax=0):
|
|
130
130
|
logger.info("Max demagnetization %f", np.max(demag))
|
131
131
|
|
132
132
|
|
133
|
+
def remanence(isa, cmap=DEFAULT_CMAP, ax=0):
|
134
|
+
"""plot remanence of NC/I7/ISA7 model
|
135
|
+
Args:
|
136
|
+
isa: Isa7/NC object
|
137
|
+
"""
|
138
|
+
emag = [e for e in isa.elements if e.is_magnet()]
|
139
|
+
rem = np.linalg.norm([e.remanence(isa.MAGN_TEMPERATURE)
|
140
|
+
for e in emag], axis=1)
|
141
|
+
_contour(ax, f'Remanence at {isa.MAGN_TEMPERATURE} °C (min {np.min(rem):.1f} T)',
|
142
|
+
emag, rem, 'T', cmap, isa)
|
143
|
+
logger.info("Min remanence %f", np.min(rem))
|
144
|
+
|
145
|
+
|
133
146
|
def demag_pos(isa, pos=-1, icur=-1, ibeta=-1, cmap=DEFAULT_CMAP, ax=0):
|
134
147
|
"""plot demag of NC/I7/ISA7 model at rotor position
|
135
148
|
Args:
|
@@ -0,0 +1,378 @@
|
|
1
|
+
import pathlib
|
2
|
+
import logging
|
3
|
+
import json
|
4
|
+
import numpy as np
|
5
|
+
import scipy.optimize as so
|
6
|
+
from scipy.interpolate import make_interp_spline
|
7
|
+
import scipy.integrate as ig
|
8
|
+
import femagtools.parstudy
|
9
|
+
|
10
|
+
logger = logging.getLogger('shortcircuit')
|
11
|
+
|
12
|
+
def _parstudy_list(femag, result_func):
|
13
|
+
workdir = femag.workdir
|
14
|
+
magnetMat = femag.magnets
|
15
|
+
magnetizingCurves = femag.magnetizingCurves
|
16
|
+
condMat = femag.condMat
|
17
|
+
templatedirs = femag.templatedirs
|
18
|
+
cmd = femag.cmd
|
19
|
+
return femagtools.parstudy.List(
|
20
|
+
workdir, condMat=condMat, magnets=magnetMat,
|
21
|
+
magnetizingCurves=magnetizingCurves,
|
22
|
+
cmd=cmd, result_func=result_func)
|
23
|
+
|
24
|
+
def get_shortCircuit_parameters(bch, nload):
|
25
|
+
"""extracts shortciruit parameters from bch"""
|
26
|
+
try:
|
27
|
+
if nload < 0:
|
28
|
+
nload = 0
|
29
|
+
if nload > 2:
|
30
|
+
nload = 2
|
31
|
+
if nload > 0:
|
32
|
+
dqld = bch.dqPar['ld']
|
33
|
+
dqlq = bch.dqPar['lq']
|
34
|
+
dqpsim = bch.dqPar['psim']
|
35
|
+
if len(dqld) <= nload or len(dqlq) <= nload or len(dqpsim) <= nload:
|
36
|
+
ld = dqld[-1]/bch.armatureLength
|
37
|
+
lq = dqlq[-1]/bch.armatureLength
|
38
|
+
psim = dqpsim[-1]/bch.armatureLength
|
39
|
+
else:
|
40
|
+
ld = dqld[nload-1]/bch.armatureLength
|
41
|
+
lq = dqlq[nload-1]/bch.armatureLength
|
42
|
+
psim = dqpsim[nload-1]/bch.armatureLength
|
43
|
+
else:
|
44
|
+
ld = bch.machine['ld']/bch.armatureLength
|
45
|
+
lq = bch.machine['lq']/bch.armatureLength
|
46
|
+
psim = bch.machine['psim']/bch.armatureLength
|
47
|
+
return dict(
|
48
|
+
r1=bch.machine['r1'],
|
49
|
+
ld=ld,
|
50
|
+
lq=lq,
|
51
|
+
Hk=bch.magnet['demag_hx'],
|
52
|
+
Tmag=bch.magnet['Tmag'],
|
53
|
+
psim=psim,
|
54
|
+
num_pol_pair=bch.machine['p'],
|
55
|
+
fc_radius=bch.machine['fc_radius'],
|
56
|
+
lfe=bch.armatureLength/1e3,
|
57
|
+
pocfilename=bch.machine['pocfile'],
|
58
|
+
num_par_wdgs=bch.machine.get('num_par_wdgs', 1),
|
59
|
+
calculationMode='shortcircuit')
|
60
|
+
except (KeyError, AttributeError, IndexError):
|
61
|
+
raise ValueError("missing pm/Rel-Sim results")
|
62
|
+
|
63
|
+
|
64
|
+
def find_peaks_and_valleys(t, y):
|
65
|
+
""" return peak and valley of y with maximum amplitude
|
66
|
+
"""
|
67
|
+
peaks = (np.diff(np.sign(np.diff(y))) < 0).nonzero()[0] + 1
|
68
|
+
if len(peaks>0):
|
69
|
+
ip = np.argmax(y[peaks])
|
70
|
+
pv = {'ip': y[peaks][ip], 'tp': t[peaks][ip]}
|
71
|
+
else:
|
72
|
+
pv = {'ip': [], 'tp': []}
|
73
|
+
valleys = (np.diff(np.sign(np.diff(y))) > 0).nonzero()[0] + 1
|
74
|
+
if len(valleys>0):
|
75
|
+
iv = np.argmin(y[valleys])
|
76
|
+
pv.update({'iv': y[valleys][iv], 'tv': t[valleys][iv]})
|
77
|
+
else:
|
78
|
+
pv.update({'iv': [], 'tv': []})
|
79
|
+
pv.update({'peaks': y[peaks], 'valleys': y[valleys],
|
80
|
+
'tpeaks': t[peaks], 'tvalleys': t[valleys]})
|
81
|
+
return pv
|
82
|
+
|
83
|
+
def shortcircuit(femag, machine, bch, simulation, engine=0):
|
84
|
+
scdata = {}
|
85
|
+
calcmode = simulation.get('calculationMode', '')
|
86
|
+
simulation.update(
|
87
|
+
get_shortCircuit_parameters(bch,
|
88
|
+
simulation.get('initial', 2)))
|
89
|
+
if 'speed' not in simulation:
|
90
|
+
simulation['speed'] = bch.dqPar['speed']
|
91
|
+
if simulation.get('sc_type', 3) == 3:
|
92
|
+
logger.info("3phase short circuit simulation")
|
93
|
+
builder = femagtools.fsl.Builder(femag.templatedirs)
|
94
|
+
fslcmds = (builder.open_model(femag.model) +
|
95
|
+
builder.create_shortcircuit(simulation))
|
96
|
+
fslfile = 'shortcircuit.fsl'
|
97
|
+
(pathlib.Path(femag.workdir)/fslfile).write_text(
|
98
|
+
'\n'.join(fslcmds),
|
99
|
+
encoding='latin1', errors='ignore')
|
100
|
+
femag.run(fslfile) # options?
|
101
|
+
bchfile = femag.get_bch_file(femag.modelname)
|
102
|
+
if bchfile:
|
103
|
+
bchsc = femagtools.bch.Reader()
|
104
|
+
logger.info("Read BCH %s", bchfile)
|
105
|
+
bchsc.read(pathlib.Path(bchfile).read_text(
|
106
|
+
encoding='latin1', errors='ignore'))
|
107
|
+
bchsc.scData['demag'] = bchsc.demag
|
108
|
+
if simulation.get('sim_demagn', 0):
|
109
|
+
d = {'displ': [d['displ']
|
110
|
+
for d in bchsc.demag if 'displ' in d],
|
111
|
+
'H_max': [d['H_max']
|
112
|
+
for d in bchsc.demag if 'H_max' in d],
|
113
|
+
'H_av': [d['H_av']
|
114
|
+
for d in bchsc.demag if 'H_av' in d]}
|
115
|
+
simulation['i1max'] = bchsc.scData['iks']
|
116
|
+
bchsc.scData['demag'] = demag(
|
117
|
+
femag, machine, simulation, engine)
|
118
|
+
bchsc.scData['demag'].update(d)
|
119
|
+
scdata = bchsc.scData
|
120
|
+
#for w in bch.flux:
|
121
|
+
# try:
|
122
|
+
# bch.flux[w] += bchsc.flux[w]
|
123
|
+
# bch.flux_fft[w] += bchsc.flux_fft[w]
|
124
|
+
# except (KeyError, IndexError):
|
125
|
+
# logging.debug(
|
126
|
+
# "No additional flux data in sc simulation")
|
127
|
+
# break
|
128
|
+
|
129
|
+
if simulation.get('sc_type', 3) == 2:
|
130
|
+
if 'i1max' not in simulation:
|
131
|
+
# just a wild guess
|
132
|
+
simulation['i1max'] = 4.5*bch.machine['i1']
|
133
|
+
logger.info("2phase short circuit simulation i1max = %.0f",
|
134
|
+
simulation['i1max'])
|
135
|
+
scdata = shortcircuit_2phase(femag, machine, simulation, engine)
|
136
|
+
|
137
|
+
else:
|
138
|
+
logger.warning("Empty shortcircuit results for type %d",
|
139
|
+
simulation.get('sc_type', 'unknown'))
|
140
|
+
# must reset calcmode
|
141
|
+
if calcmode:
|
142
|
+
simulation['calculationMode'] = calcmode
|
143
|
+
else:
|
144
|
+
del simulation['calculationMode']
|
145
|
+
return scdata
|
146
|
+
|
147
|
+
def sc_result_func(task):
|
148
|
+
basedir = pathlib.Path(task.directory)
|
149
|
+
psitorq = np.loadtxt(basedir/'psi-torq-rot.dat')
|
150
|
+
pos = np.unique(psitorq[:, 0])
|
151
|
+
ncurs = psitorq[:, 0].shape[0]//pos.shape[0]
|
152
|
+
ire = psitorq[:ncurs, 1:4]
|
153
|
+
psire = np.reshape(psitorq[:, 4:7], (-1, ncurs, 3))
|
154
|
+
torq = np.reshape(psitorq[:, 7], (-1, ncurs))
|
155
|
+
return {'pos': pos.tolist(), 'ire': ire.tolist(),
|
156
|
+
'psire': psire.tolist(), 'torq': torq.tolist()}
|
157
|
+
|
158
|
+
|
159
|
+
def shortcircuit_2phase(femag, machine, simulation, engine=0):
|
160
|
+
i1max = simulation['i1max']
|
161
|
+
num_cur_steps = 4
|
162
|
+
i1 = np.linspace(0, i1max, num_cur_steps)
|
163
|
+
i1vec = np.concat((-i1[::-1], i1[1:]))
|
164
|
+
num_par_wdgs = machine['winding'].get('num_par_wdgs', 1)
|
165
|
+
flux_sim = {
|
166
|
+
'calculationMode': 'psi-torq-rot',
|
167
|
+
'i1max': i1max,
|
168
|
+
'curvec': [],
|
169
|
+
'num_par_wdgs': num_par_wdgs}
|
170
|
+
|
171
|
+
if engine:
|
172
|
+
parstudy = _parstudy_list(femag, sc_result_func)
|
173
|
+
parvardef = {
|
174
|
+
"decision_vars": [
|
175
|
+
{"values": i1vec, "name": "curvec"}]
|
176
|
+
}
|
177
|
+
results = parstudy(parvardef, machine, flux_sim, engine)
|
178
|
+
|
179
|
+
ire = np.array([r['ire'][0] for r in results['f']])
|
180
|
+
pos = np.array(results['f'][0]['pos'])
|
181
|
+
phi = pos*np.pi/180
|
182
|
+
torq = np.hstack([r['torq'] for r in results['f']])
|
183
|
+
psire = np.hstack([r['psire'] for r in results['f']])
|
184
|
+
else:
|
185
|
+
simulation.update(flux_sim)
|
186
|
+
simulation['curvec'] = i1vec.tolist()
|
187
|
+
results = femag(machine, simulation)
|
188
|
+
class Task:
|
189
|
+
def __init__(self, workdir):
|
190
|
+
self.directory = workdir
|
191
|
+
results = sc_result_func(Task(femag.workdir))
|
192
|
+
ire = np.array(results['ire'])
|
193
|
+
pos = np.array(results['pos'])
|
194
|
+
torq = np.array(results['torq'])
|
195
|
+
psire = np.array(results['psire'])
|
196
|
+
|
197
|
+
#with open('results.json', 'w') as fp:
|
198
|
+
# json.dump({'ire': ire.tolist(), 'pos': pos.tolist(),
|
199
|
+
# 'torq': torq.tolist(), 'psire': psire.tolist()}, fp)
|
200
|
+
logger.info("move steps %d currents %s", len(pos), ire[:,0])
|
201
|
+
|
202
|
+
Ai = [femagtools.utils.fft(pos, psire[:, k, 0])['a']
|
203
|
+
for k in range(np.shape(psire)[1])]
|
204
|
+
A = make_interp_spline(ire[:,0], Ai)
|
205
|
+
A0i = [femagtools.utils.fft(pos, psire[:, k, 0])['a0']
|
206
|
+
for k in range(np.shape(psire)[1])]
|
207
|
+
A0 = make_interp_spline(ire[:,0], A0i)
|
208
|
+
Bi = [femagtools.utils.fft(pos, psire[:, k, 1])['a']
|
209
|
+
for k in range(np.shape(psire)[1]-1, -1, -1)]
|
210
|
+
B = make_interp_spline(ire[::-1,1], Bi)
|
211
|
+
B0i = [femagtools.utils.fft(pos, psire[:, k, 1])['a0']
|
212
|
+
for k in range(np.shape(psire)[1]-1, -1, -1)]
|
213
|
+
B0 = make_interp_spline(ire[::-1,1], B0i)
|
214
|
+
alfa0_ai = [femagtools.utils.fft(pos, psire[:, k, 0])['alfa0']
|
215
|
+
for k in range(np.shape(psire)[1])]
|
216
|
+
alfa0_a = make_interp_spline(ire[:,0], alfa0_ai)
|
217
|
+
alfa0_bi = [femagtools.utils.fft(pos, psire[:, k, 1])['alfa0']
|
218
|
+
for k in range(np.shape(psire)[1]-1, -1, -1)]
|
219
|
+
alfa0_b = make_interp_spline(ire[::-1,1], alfa0_bi)
|
220
|
+
|
221
|
+
Tqi = [femagtools.utils.fft(pos, torq[:, k])['a']
|
222
|
+
for k in range(np.shape(torq)[1])]
|
223
|
+
Tq = make_interp_spline(ire[:, 0], Tqi)
|
224
|
+
Tq0i = [femagtools.utils.fft(pos, torq[:, k])['a0']
|
225
|
+
for k in range(np.shape(torq)[1])]
|
226
|
+
Tq0 = make_interp_spline(ire[:, 0], Tq0i)
|
227
|
+
alfa0_t = [femagtools.utils.fft(pos, torq[:, k])['alfa0']
|
228
|
+
for k in range(np.shape(torq)[1])]
|
229
|
+
|
230
|
+
T0 = np.mean([femagtools.utils.fft(pos, psire[:, k, 0])['T0']
|
231
|
+
for k in range(np.shape(psire)[1])])
|
232
|
+
pp = 360/T0
|
233
|
+
|
234
|
+
def torque(phi, i):
|
235
|
+
try:
|
236
|
+
alfa0 = np.ones(len(i))*np.mean(alfa0_t)
|
237
|
+
alfa0[i < 0] = alfa0_t[0]
|
238
|
+
alfa0[i > 0] = alfa0_t[-1]
|
239
|
+
except TypeError:
|
240
|
+
alfa0 = np.mean(alfa0_t)
|
241
|
+
if i < 0:
|
242
|
+
alfa0 = alfa0_t[0]
|
243
|
+
if i > 0:
|
244
|
+
alfa0 = alfa0_t[-1]
|
245
|
+
return Tq(i)*np.cos(pp*phi+alfa0) + Tq0(i)
|
246
|
+
|
247
|
+
def psia(phi, i):
|
248
|
+
return A(i)*np.cos(pp*phi+alfa0_a(i))+A0(i)
|
249
|
+
|
250
|
+
def psib(phi, i):
|
251
|
+
return B(i)*np.cos(pp*phi+alfa0_b(i))+B0(i)
|
252
|
+
|
253
|
+
def dpsiadi(phi,i):
|
254
|
+
return A(i, nu=1)*np.cos(pp*phi+alfa0_a(i))+A0(i,nu=1)
|
255
|
+
def dpsiadphi(phi,i):
|
256
|
+
return -pp*A(i)*np.sin(pp*phi+alfa0_a(i))
|
257
|
+
def dpsibdi(phi,i):
|
258
|
+
return B(i, nu=1)*np.cos(pp*phi+alfa0_b(i))+B0(i,nu=1)
|
259
|
+
def dpsibdphi(phi,i):
|
260
|
+
return -pp*B(i)*np.sin(pp*phi+alfa0_b(i))
|
261
|
+
|
262
|
+
speed = simulation['speed']
|
263
|
+
r1 = simulation['r1']
|
264
|
+
l1s = simulation.get('l1s',0)
|
265
|
+
wm = 2*np.pi*speed
|
266
|
+
w1 = pp*wm
|
267
|
+
|
268
|
+
def didt(t, y):
|
269
|
+
return [((2*r1*y[0] + wm*(
|
270
|
+
dpsiadphi(y[1],y[0]) - dpsibdphi(y[1],-y[0])))/
|
271
|
+
(-dpsiadi(y[1],y[0]) - dpsibdi(y[1],-y[0]) -2*l1s)),
|
272
|
+
wm]
|
273
|
+
tmin = simulation.get('tstart', 0)
|
274
|
+
tmax = simulation.get('simultime', 0.1)
|
275
|
+
nsamples = simulation.get('nsamples', 400)
|
276
|
+
t = np.linspace(tmin, tmax, nsamples)
|
277
|
+
|
278
|
+
def func(x):
|
279
|
+
return B(0)*np.sin(pp*x+alfa0_b(0)) - A(0)*np.sin(pp*x+alfa0_a(0))
|
280
|
+
phi0 = so.fsolve(func, [0])[0]
|
281
|
+
|
282
|
+
Y0 = [0, phi0]
|
283
|
+
sol = ig.solve_ivp(didt, (t[0], t[-1]), Y0, dense_output=True)
|
284
|
+
ia = sol.sol(t).T[:, 0]
|
285
|
+
pv = find_peaks_and_valleys(t, ia)
|
286
|
+
iap = pv['tp'], pv['ip']
|
287
|
+
iav = pv['tv'], pv['iv']
|
288
|
+
iac = pv['tpeaks'][-1], pv['peaks'][-1]
|
289
|
+
|
290
|
+
logger.info("Ia %.1f %.1f %.1f (phi0 %.4f)",
|
291
|
+
iap[1], iav[1], iac[1], phi0)
|
292
|
+
|
293
|
+
def func(x):
|
294
|
+
y = torque(wm*t+phi0+x, ia)
|
295
|
+
pv = find_peaks_and_valleys(t, y)
|
296
|
+
return pv['peaks'][-1] + pv['valleys'][-1]
|
297
|
+
|
298
|
+
dphi = so.fsolve(func, [0])[0]
|
299
|
+
torque = torque(wm*t+phi0+dphi, ia)
|
300
|
+
pv = find_peaks_and_valleys(t, torque)
|
301
|
+
tp = pv['tp'], pv['ip']
|
302
|
+
tv = pv['tv'], pv['iv']
|
303
|
+
tc = pv['tpeaks'][-1], pv['peaks'][-1]
|
304
|
+
logger.info("Torque %.1f %.1f %.1f (dphi %.4f)",
|
305
|
+
tp[1], tv[1], tc[1], dphi)
|
306
|
+
|
307
|
+
scData = {
|
308
|
+
'ia': ia.tolist(),
|
309
|
+
'ib': (-ia).tolist(),
|
310
|
+
'ic': np.zeros(ia.shape).tolist(),
|
311
|
+
'time': t.tolist(),
|
312
|
+
'torque': torque.tolist(),
|
313
|
+
'speed': speed,
|
314
|
+
'ikd': iac[1],
|
315
|
+
'tkd': tc[1],
|
316
|
+
'iks': iap[1] if iap[1] > abs(iav[1]) else iav[1],
|
317
|
+
'tks': tp[1] if tp[1] > abs(tv[1]) else tv[1]
|
318
|
+
}
|
319
|
+
scData['peakWindingCurrents'] = [scData['iks'],
|
320
|
+
-scData['iks'], 0]
|
321
|
+
if simulation.get('sim_demagn', 0):
|
322
|
+
scData['demag'] = demag(femag, machine, simulation, engine)
|
323
|
+
return scData
|
324
|
+
|
325
|
+
def dm_result_func(task):
|
326
|
+
basedir = pathlib.Path(task.directory)
|
327
|
+
i1rr = []
|
328
|
+
for f in sorted(basedir.glob('psi-torq-rem-rot-*.dat')):
|
329
|
+
ptr = np.loadtxt(f)
|
330
|
+
i1rr.append((np.max(ptr.T[1:4]), np.min(ptr.T[-1])))
|
331
|
+
return i1rr
|
332
|
+
|
333
|
+
def demag(femag, machine, simulation, engine=0):
|
334
|
+
"""demag simulation using psi-torq-rem-rot"""
|
335
|
+
logger.info("Demagnetization processing")
|
336
|
+
i1max = simulation['i1max']
|
337
|
+
i1min = simulation.get('i1min', i1max/4)
|
338
|
+
num_steps = 7
|
339
|
+
b = (i1min-i1max)/np.log(i1min/i1max)
|
340
|
+
a = i1max/b
|
341
|
+
i1tab = [b*(a+np.log(x))
|
342
|
+
for x in np.linspace(i1min/i1max, 1,
|
343
|
+
num_steps)]
|
344
|
+
|
345
|
+
if simulation.get('sc_type', 3) == 3:
|
346
|
+
curvec = [[-a/2, a, -a/2] for a in i1tab]
|
347
|
+
else:
|
348
|
+
curvec = [[a, -a, 0] for a in i1tab]
|
349
|
+
simulation.update({
|
350
|
+
'calculationMode': 'psi-torq-rem-rot',
|
351
|
+
'curvec': curvec})
|
352
|
+
if engine:
|
353
|
+
parstudy = _parstudy_list(femag, dm_result_func)
|
354
|
+
parvardef = {
|
355
|
+
"decision_vars": [
|
356
|
+
{"values": curvec, "name": "curvec"}]
|
357
|
+
}
|
358
|
+
results = parstudy(parvardef, machine, simulation, engine)
|
359
|
+
i1rr = np.vstack(
|
360
|
+
((0, 1),
|
361
|
+
np.array(results['f']).reshape((-1, 2))))
|
362
|
+
else:
|
363
|
+
class Task:
|
364
|
+
def __init__(self, workdir):
|
365
|
+
self.directory = workdir
|
366
|
+
_ = femag(machine, simulation)
|
367
|
+
i1rr = np.vstack(
|
368
|
+
[(0, 1), dm_result_func(Task(femag.workdir))])
|
369
|
+
i1, rr = np.array(i1rr).T
|
370
|
+
dmag = {'Hk': simulation['Hk'],
|
371
|
+
'Tmag': simulation['Tmag'],
|
372
|
+
'i1': i1.tolist(),
|
373
|
+
'rr': rr.tolist()}
|
374
|
+
# critical current
|
375
|
+
if np.min(rr) < 0.99:
|
376
|
+
k = np.where(rr < 0.99)[0][0]
|
377
|
+
dmag['i1c'] = i1[k]
|
378
|
+
return dmag
|