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

Sign up to get free protection for your applications and to get access to all the features.
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 transientsc(bch, title=''):
519
- """creates a transient short circuit plot"""
520
- cols = 1
521
- rows = 2
522
- htitle = 1.5 if title else 0
523
- fig, ax = plt.subplots(nrows=rows, ncols=cols,
524
- figsize=(10, 3*rows + htitle))
525
- if title:
526
- fig.suptitle(title, fontsize=16)
527
-
528
- row = 1
529
- plt.subplot(rows, cols, row)
530
- ax = plt.gca()
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
- istat = np.array([bch.scData[i]
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(bch.scData['time']), i1)
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
- imax *= 1e-3
547
- ax.set_title('Currents / kA')
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.set_title('Currents / A')
579
+ ax.set_ylabel('Currents / A')
550
580
 
551
581
  for i, iph in zip(('ia', 'ib', 'ic'), istat):
552
- ax.plot(bch.scData['time'], iph, label=i)
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']+0.01, imax))
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
- ax.set_xlabel('Time / s')
597
+ if set_xlabel:
598
+ ax.set_xlabel('Time / s')
561
599
  ax.legend()
562
600
 
563
- row = 2
564
- plt.subplot(rows, cols, row)
565
- ax = plt.gca()
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(bch.scData['time']), np.array(bch.scData['torque']))
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
- except KeyError:
613
+ tc = [pv['tpeaks'][-1], pv['peaks'][-1]]
614
+ except (KeyError, ValueError):
572
615
  pass
573
- torque = np.array(bch.scData['torque'])
616
+ torque = np.array(scData['torque'])
574
617
  if np.max(torque) > 4000:
575
618
  torque *= 1e-3
576
- tqmax *= 1e-3
577
- ax.set_title('Torque / kNm')
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.set_title('Torque / Nm')
626
+ ax.set_ylabel('Torque / Nm')
580
627
 
581
628
  ax.grid(True)
582
- ax.plot(bch.scData['time'], torque)
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+0.01, tqmax))
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
- ax.set_xlabel('Time / s')
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
- pos = [d['displ'] for d in demag if 'displ' in d]
606
- hmax = [-d['H_max'] for d in demag if 'H_max' in d]
607
- havg = [-d['H_av'] for d in demag if 'H_av' in d]
608
- hclim = [-d['lim_hc'] for d in demag if 'lim_hc' in d]*2
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', linestyle='dashed',
617
- label='Hc {:4.2f} kA/m'.format(hclim[0]))
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 magnet:
622
- ax.legend(title=f"Magnet Temperature {magnet['Tmag']}°C")
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