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/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