femagtools 1.8.4__py3-none-any.whl → 1.8.6__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- femagtools/__init__.py +1 -1
- femagtools/bch.py +5 -2
- femagtools/dxfsl/area.py +3 -0
- femagtools/dxfsl/conv.py +1 -8
- femagtools/dxfsl/converter.py +60 -147
- femagtools/dxfsl/geom.py +49 -0
- femagtools/femag.py +5 -2
- femagtools/machine/afpm.py +339 -12
- femagtools/machine/utils.py +2 -1
- femagtools/multiproc.py +2 -1
- femagtools/plot/__init__.py +1 -0
- femagtools/plot/bch.py +57 -3
- femagtools/plot/char.py +19 -8
- femagtools/plot/machine.py +100 -0
- femagtools/poc.py +10 -0
- femagtools/utils.py +3 -1
- femagtools/zmq.py +22 -5
- {femagtools-1.8.4.dist-info → femagtools-1.8.6.dist-info}/METADATA +1 -1
- {femagtools-1.8.4.dist-info → femagtools-1.8.6.dist-info}/RECORD +23 -22
- {femagtools-1.8.4.dist-info → femagtools-1.8.6.dist-info}/LICENSE +0 -0
- {femagtools-1.8.4.dist-info → femagtools-1.8.6.dist-info}/WHEEL +0 -0
- {femagtools-1.8.4.dist-info → femagtools-1.8.6.dist-info}/entry_points.txt +0 -0
- {femagtools-1.8.4.dist-info → femagtools-1.8.6.dist-info}/top_level.txt +0 -0
femagtools/machine/afpm.py
CHANGED
@@ -14,6 +14,8 @@ from .. import femag
|
|
14
14
|
from scipy.interpolate import make_interp_spline, RegularGridInterpolator, RectBivariateSpline
|
15
15
|
from scipy.integrate import quad
|
16
16
|
import copy
|
17
|
+
from matplotlib.colors import to_rgb
|
18
|
+
from multiprocessing import Pool
|
17
19
|
|
18
20
|
logger = logging.getLogger(__name__)
|
19
21
|
|
@@ -53,7 +55,8 @@ def _integrate(radius, pos, val):
|
|
53
55
|
interp = RegularGridInterpolator((radius, pos), val)
|
54
56
|
def func(x, y):
|
55
57
|
return interp((x, y))
|
56
|
-
return [quad(func, radius[0], radius[-1],
|
58
|
+
return [quad(func, radius[0], radius[-1],
|
59
|
+
args=(p,), limit=200)[0]
|
57
60
|
for p in pos]
|
58
61
|
|
59
62
|
|
@@ -61,7 +64,7 @@ def _integrate1d(radius, val):
|
|
61
64
|
interp = make_interp_spline(radius, val, k=1)
|
62
65
|
def func(x):
|
63
66
|
return interp((x))
|
64
|
-
return quad(func, radius[0], radius[-1])[0]
|
67
|
+
return quad(func, radius[0], radius[-1], limit=100)[0]
|
65
68
|
|
66
69
|
def ld_interpol(i1, beta, v):
|
67
70
|
'''interpolate Ld at beta angle 0°, -180°'''
|
@@ -151,7 +154,17 @@ def parident(workdir, engine, temp, machine,
|
|
151
154
|
machine[wdgk].get('num_par_wdgs', 1))
|
152
155
|
|
153
156
|
p = machine['poles']
|
154
|
-
|
157
|
+
if np.isscalar(machine['magnet']['afm_rotor']['rel_magn_width']):
|
158
|
+
num_slices = kwargs.get('num_slices', 3)
|
159
|
+
rmagw = num_slices*[machine['magnet']['afm_rotor']['rel_magn_width']]
|
160
|
+
else:
|
161
|
+
rmagw = machine['magnet']['afm_rotor']['rel_magn_width']
|
162
|
+
if len(rmagw) == 1:
|
163
|
+
num_slices = kwargs.get('num_slices', 3)
|
164
|
+
rmagw = num_slices*list(rmagw)
|
165
|
+
else:
|
166
|
+
num_slices = len(rmagw)
|
167
|
+
|
155
168
|
lfe = get_arm_lengths(machine['outer_diam'],
|
156
169
|
machine['inner_diam'],
|
157
170
|
num_slices)
|
@@ -171,6 +184,8 @@ def parident(workdir, engine, temp, machine,
|
|
171
184
|
"decision_vars": [
|
172
185
|
{"values": pole_width,
|
173
186
|
"name": "pole_width"},
|
187
|
+
{"values": rmagw,
|
188
|
+
"name": "magnet.afm_rotor.rel_magn_width"},
|
174
189
|
{"values": lfe,
|
175
190
|
"name": "lfe"},
|
176
191
|
{"values": linspeed, "name": "speed"}
|
@@ -215,9 +230,11 @@ def parident(workdir, engine, temp, machine,
|
|
215
230
|
else:
|
216
231
|
nlresults = {"x": [], "f": []}
|
217
232
|
i = 0
|
218
|
-
|
233
|
+
|
234
|
+
for pw, le, sp, rmw in zip(pole_width, lfe, linspeed, rmagw):
|
219
235
|
nlmachine = {k: machine[k] for k in machine}
|
220
236
|
nlmachine['pole_width'] = pw
|
237
|
+
nlmachine['magnet']['afm_rotor']['rel_magn_width'] = rmw
|
221
238
|
nlmachine['lfe'] = le
|
222
239
|
nlcalc.update({"speed": sp})
|
223
240
|
nlsubdir = f'{workdir}/{i}'
|
@@ -236,10 +253,11 @@ def parident(workdir, engine, temp, machine,
|
|
236
253
|
current_angles = nlresults['f'][0]['current_angles']
|
237
254
|
results = []
|
238
255
|
i = 0
|
239
|
-
for l, pw in zip(lfe, pole_width):
|
256
|
+
for l, pw, rmw in zip(lfe, pole_width, rmagw):
|
240
257
|
mpart = {k: machine[k] for k in machine if k != 'afm_rotor'}
|
241
258
|
mpart['pole_width'] = pw
|
242
259
|
mpart['lfe'] = l
|
260
|
+
mpart['magnet']['afm_rotor']['rel_magn_width'] = rmw
|
243
261
|
subdir = f"{workdir}/{i}"
|
244
262
|
|
245
263
|
simulation = dict(
|
@@ -295,12 +313,23 @@ def parident(workdir, engine, temp, machine,
|
|
295
313
|
i += 1
|
296
314
|
|
297
315
|
postp = []
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
316
|
+
if kwargs.get('use_multiprocessing', True):
|
317
|
+
with Pool() as p:
|
318
|
+
for r in p.starmap(process,
|
319
|
+
[(lfe, pole_width, machine, bch)
|
320
|
+
for bch in zip(*[r['f']
|
321
|
+
for r in results])]):
|
322
|
+
torque = np.mean(r.pop('torque'))
|
323
|
+
r['torque'] = torque
|
324
|
+
r.update(_psidq_ldq(r, nlresults))
|
325
|
+
postp.append(r)
|
326
|
+
else:
|
327
|
+
for r in [process(lfe, pole_width, machine, bch)
|
328
|
+
for bch in zip(*[r['f'] for r in results])]:
|
329
|
+
torque = np.mean(r.pop('torque'))
|
330
|
+
r['torque'] = torque
|
331
|
+
r.update(_psidq_ldq(r, nlresults))
|
332
|
+
postp.append(r)
|
304
333
|
|
305
334
|
r1 = postp[0]['r1']
|
306
335
|
i1 = [r['i1'] for r in postp][::num_beta_steps]
|
@@ -395,6 +424,7 @@ def process(lfe, pole_width, machine, bch):
|
|
395
424
|
n = len(rotpos[0])
|
396
425
|
currents = [bch[0]['flux'][k][0]['current_k'][:n]
|
397
426
|
for k in bch[0]['flux']]
|
427
|
+
|
398
428
|
if len(pole_width) > 1:
|
399
429
|
# check homogenity:
|
400
430
|
if np.diff([len(d) for d in displ]).any():
|
@@ -426,6 +456,9 @@ def process(lfe, pole_width, machine, bch):
|
|
426
456
|
for ux in bch[0]['flux'][k][0]['voltage_dpsi'][:-1]]
|
427
457
|
for k in bch[0]['flux']}
|
428
458
|
emf = [voltage[k][:n] for k in voltage]
|
459
|
+
fluxxy = {k: [scale_factor * np.array(flx)
|
460
|
+
for flx in bch[0]['flux'][k][0]['flux_k']]
|
461
|
+
for k in bch[0]['flux']}
|
429
462
|
flux = [fluxxy[k][:n] for k in fluxxy]
|
430
463
|
|
431
464
|
pos = (rotpos[0]/np.pi*180)
|
@@ -484,7 +517,6 @@ def process(lfe, pole_width, machine, bch):
|
|
484
517
|
except KeyError as exc:
|
485
518
|
#logger.warning("missing key %s", exc)
|
486
519
|
pass
|
487
|
-
|
488
520
|
return {
|
489
521
|
'weights': weights.tolist(),
|
490
522
|
'pos': pos.tolist(), 'r1': r1,
|
@@ -598,6 +630,287 @@ def _get_copper_losses(scale_factor, bch):
|
|
598
630
|
return 0 # noload calc has no winding losses
|
599
631
|
|
600
632
|
|
633
|
+
def _set_plot_attributes(ax):
|
634
|
+
ax.set_aspect('equal')
|
635
|
+
for loc, spine in ax.spines.items():
|
636
|
+
spine.set_color('none') # don't draw spine
|
637
|
+
ax.yaxis.set_ticks([])
|
638
|
+
ax.xaxis.set_ticks([])
|
639
|
+
|
640
|
+
|
641
|
+
def _get_colors(colors, delta):
|
642
|
+
if delta == 0.0:
|
643
|
+
return colors
|
644
|
+
new_colors = []
|
645
|
+
for col in colors:
|
646
|
+
rgb = to_rgb(col)
|
647
|
+
r, g, b = rgb
|
648
|
+
col = (max(0.0, min(r+delta, 1.0)),
|
649
|
+
max(0.0, min(g+delta, 1.0)),
|
650
|
+
max(0.0, min(b+delta, 1.0)))
|
651
|
+
new_colors.append(col)
|
652
|
+
return new_colors
|
653
|
+
|
654
|
+
|
655
|
+
def _draw_vertical_magnets(ax,
|
656
|
+
poles,
|
657
|
+
xr, yr,
|
658
|
+
Rr,
|
659
|
+
xoff, yoff,
|
660
|
+
delta=0.0):
|
661
|
+
color = ['green', 'red']
|
662
|
+
color = _get_colors(color, delta)
|
663
|
+
for i in range(poles):
|
664
|
+
ax.fill(xr+xoff, yr+yoff,
|
665
|
+
facecolor=color[i%2], edgecolor=color[i%2])
|
666
|
+
xr, yr = np.dot(Rr, [xr, yr])
|
667
|
+
return
|
668
|
+
|
669
|
+
|
670
|
+
def _draw_vertical_slots(ax,
|
671
|
+
Q,
|
672
|
+
r,
|
673
|
+
alpha,
|
674
|
+
xoff, yoff,
|
675
|
+
delta=0.0):
|
676
|
+
color = ['skyblue', 'blue']
|
677
|
+
color = _get_colors(color, delta)
|
678
|
+
taus = 2*np.pi/Q
|
679
|
+
for n in range(Q):
|
680
|
+
beta0 = np.linspace(n*taus, n*taus + taus/2-alpha[0], 5)
|
681
|
+
beta1 = np.linspace(n*taus, n*taus + taus/2-alpha[1], 5)
|
682
|
+
xr = np.concatenate((
|
683
|
+
r[0]*np.cos(beta0), r[1]*np.cos(beta1[::-1])))+xoff
|
684
|
+
yr = np.concatenate((
|
685
|
+
r[0]*np.sin(beta0), r[1]*np.sin(beta1[::-1])))+yoff
|
686
|
+
ax.fill(xr, yr, color=color[0])
|
687
|
+
beta0 = np.linspace(n*taus + taus/2+alpha[0], (n+1)*taus, 5)
|
688
|
+
beta1 = np.linspace(n*taus + taus/2+alpha[1], (n+1)*taus, 5)
|
689
|
+
xr = np.concatenate((
|
690
|
+
r[0]*np.cos(beta0), r[1]*np.cos(beta1[::-1])))
|
691
|
+
yr = np.concatenate((
|
692
|
+
r[0]*np.sin(beta0), r[1]*np.sin(beta1[::-1])))
|
693
|
+
ax.fill(xr, yr, color=color[0])
|
694
|
+
|
695
|
+
|
696
|
+
def vertical_plot(machine, ax):
|
697
|
+
"""plots afpm stator and rotor (vertical section)
|
698
|
+
Args:
|
699
|
+
dy1, dy1: float outer, inner diameter
|
700
|
+
rel_magn_width: float rel magnet width 0..1
|
701
|
+
Q: number of stator slots
|
702
|
+
poles: number of poles
|
703
|
+
slot_width: width of stator slot
|
704
|
+
"""
|
705
|
+
logger.debug("begin of vertical_plot()")
|
706
|
+
|
707
|
+
model_type = machine['afmtype'][0:4]
|
708
|
+
dy1 = machine['outer_diam']*1e3
|
709
|
+
dy2 = machine['inner_diam']*1e3
|
710
|
+
rel_magn_width = max(machine['magnet']['afm_rotor']['rel_magn_width'])
|
711
|
+
Q = machine['stator']['num_slots']
|
712
|
+
slot_width = machine['stator']['afm_stator']['slot_width']*1e3
|
713
|
+
poles = machine['poles']
|
714
|
+
|
715
|
+
# prepare Magnets
|
716
|
+
theta = np.linspace(np.pi/poles*(1-rel_magn_width),
|
717
|
+
np.pi/poles*(1+rel_magn_width),
|
718
|
+
10)
|
719
|
+
xr = np.concatenate((dy1/2 * np.cos(theta), dy2/2 * np.cos(theta[::-1])))
|
720
|
+
yr = np.concatenate((dy1/2 * np.sin(theta), dy2/2 * np.sin(theta[::-1])))
|
721
|
+
rtheta = 2*np.pi/poles
|
722
|
+
Rr = np.array([
|
723
|
+
[np.cos(rtheta), -np.sin(rtheta)],
|
724
|
+
[np.sin(rtheta), np.cos(rtheta)]
|
725
|
+
])
|
726
|
+
|
727
|
+
# prepare Slots
|
728
|
+
taus = 2*np.pi/Q
|
729
|
+
r = np.array([dy2/2, dy1/2])
|
730
|
+
alpha = np.arctan2(slot_width/2, r)
|
731
|
+
|
732
|
+
yoff = 0.0
|
733
|
+
xoff = 0.0
|
734
|
+
y_shift = -2
|
735
|
+
x_shift = dy1-dy2
|
736
|
+
ma_delta = 0.0 # color
|
737
|
+
sl_delta = 0.0 # color
|
738
|
+
|
739
|
+
# Draw
|
740
|
+
if model_type in ("S1R2"): # 2 rotor
|
741
|
+
_draw_vertical_magnets(ax, poles, xr, yr, Rr, xoff, yoff, delta=-0.1)
|
742
|
+
yoff += y_shift
|
743
|
+
xoff += x_shift
|
744
|
+
|
745
|
+
if model_type in ("S2R1"): # 2 stator
|
746
|
+
sl_delta = -0.1
|
747
|
+
|
748
|
+
_draw_vertical_slots(ax, Q, r, alpha, xoff, yoff, delta=sl_delta)
|
749
|
+
yoff += y_shift
|
750
|
+
xoff += x_shift
|
751
|
+
|
752
|
+
if model_type in ("S1R2"): # 2 rotor
|
753
|
+
ma_delta = 0.1
|
754
|
+
|
755
|
+
_draw_vertical_magnets(ax, poles, xr, yr, Rr, xoff, yoff, delta=ma_delta)
|
756
|
+
yoff += y_shift
|
757
|
+
xoff += x_shift
|
758
|
+
|
759
|
+
if model_type in ("S2R1"): # 2 stator
|
760
|
+
sl_delta = 0.0
|
761
|
+
_draw_vertical_slots(ax, Q, r, alpha, xoff, yoff, delta=sl_delta)
|
762
|
+
|
763
|
+
_set_plot_attributes(ax)
|
764
|
+
logger.debug("end of vertical_plot()")
|
765
|
+
|
766
|
+
|
767
|
+
IRON_NO = 0
|
768
|
+
IRON_UP = 1
|
769
|
+
IRON_DOWN = 2
|
770
|
+
|
771
|
+
def _draw_horizontal_magnets(ax,
|
772
|
+
poles,
|
773
|
+
magn_height,
|
774
|
+
magn_width,
|
775
|
+
yoke_height,
|
776
|
+
Q,
|
777
|
+
g,
|
778
|
+
taus,
|
779
|
+
dy2,
|
780
|
+
yoff=0.0,
|
781
|
+
iron=IRON_NO
|
782
|
+
):
|
783
|
+
color = ['green', 'red']
|
784
|
+
xy = (0, Q//g*taus, Q//g*taus, 0)
|
785
|
+
|
786
|
+
if iron == IRON_UP:
|
787
|
+
yy = (yoff-yoke_height,
|
788
|
+
yoff-yoke_height,
|
789
|
+
yoff,
|
790
|
+
yoff)
|
791
|
+
yoff -= yoke_height
|
792
|
+
ax.fill(xy, yy, color='skyblue')
|
793
|
+
|
794
|
+
taum = dy2*np.pi/poles
|
795
|
+
ym = np.array([yoff-magn_height,
|
796
|
+
yoff-magn_height,
|
797
|
+
yoff,
|
798
|
+
yoff])
|
799
|
+
yoff -= magn_height
|
800
|
+
|
801
|
+
for n in range(poles//g):
|
802
|
+
xl = taum*n + taum*(1 - magn_width)
|
803
|
+
xr = taum*(n + 1) - taum*(1 - magn_width)
|
804
|
+
xm = (xl, xr, xr, xl)
|
805
|
+
ax.fill(xm, ym, color=color[n%2])
|
806
|
+
|
807
|
+
if iron == IRON_DOWN:
|
808
|
+
yy = (yoff-yoke_height,
|
809
|
+
yoff-yoke_height,
|
810
|
+
yoff,
|
811
|
+
yoff)
|
812
|
+
yoff -= yoke_height
|
813
|
+
ax.fill(xy, yy, color='skyblue')
|
814
|
+
return yoff
|
815
|
+
|
816
|
+
|
817
|
+
TOOTH_UP = 0
|
818
|
+
TOOTH_DOWN = 1
|
819
|
+
TOOTH_ONLY = 2
|
820
|
+
|
821
|
+
|
822
|
+
def _draw_horizontal_slots(ax,
|
823
|
+
slot_height, slot_width, yoke_height,
|
824
|
+
Q, g, taus,
|
825
|
+
yoff=0.0,
|
826
|
+
tooth=TOOTH_DOWN):
|
827
|
+
if not tooth == TOOTH_ONLY:
|
828
|
+
xx = (0, Q//g*taus, Q//g*taus, 0)
|
829
|
+
if tooth == TOOTH_DOWN:
|
830
|
+
yy = (yoff-yoke_height,
|
831
|
+
yoff-yoke_height,
|
832
|
+
yoff,
|
833
|
+
yoff)
|
834
|
+
else:
|
835
|
+
yy = (yoff-slot_height,
|
836
|
+
yoff-slot_height,
|
837
|
+
yoff-slot_height-yoke_height,
|
838
|
+
yoff-slot_height-yoke_height)
|
839
|
+
ax.fill(xx, yy, color='skyblue')
|
840
|
+
|
841
|
+
yt = (yoff-slot_height-yoke_height,
|
842
|
+
yoff-slot_height-yoke_height,
|
843
|
+
yoff, yoff)
|
844
|
+
for n in range(Q//g):
|
845
|
+
xt = np.array((n*taus, n*taus+(taus-slot_width)/2,
|
846
|
+
n*taus+(taus-slot_width)/2, n*taus))
|
847
|
+
ax.fill(xt, yt, color='skyblue')
|
848
|
+
xt += slot_width + (taus-slot_width)/2
|
849
|
+
ax.fill(xt, yt, color='skyblue')
|
850
|
+
return yoff - slot_height - yoke_height
|
851
|
+
|
852
|
+
|
853
|
+
def horizontal_plot(machine, ax):
|
854
|
+
logger.debug("begin of horizontal_plot()")
|
855
|
+
|
856
|
+
model_type = machine['afmtype'][0:4]
|
857
|
+
dy1 = machine['outer_diam']*1e3
|
858
|
+
dy2 = machine['inner_diam']*1e3
|
859
|
+
rel_magn_width = max(machine['magnet']['afm_rotor']['rel_magn_width'])
|
860
|
+
magn_height = machine['magnet']['afm_rotor']['magn_height']*1e3
|
861
|
+
magn_yoke_height = machine['magnet']['afm_rotor']['yoke_height']*1e3
|
862
|
+
|
863
|
+
Q = machine['stator']['num_slots']
|
864
|
+
slot_width = machine['stator']['afm_stator']['slot_width']*1e3
|
865
|
+
poles = machine['poles']
|
866
|
+
m = 3
|
867
|
+
slot_height = machine['stator']['afm_stator']['slot_height']*1e3
|
868
|
+
if model_type in ('S2R1', 'S2R1_all'):
|
869
|
+
slot_height /= 2
|
870
|
+
yoke_height = machine['stator']['afm_stator']['yoke_height']*1e3
|
871
|
+
ag = machine['airgap']*1e3
|
872
|
+
|
873
|
+
g = np.gcd(Q, m*poles)//m
|
874
|
+
taus = dy2*np.pi/Q
|
875
|
+
|
876
|
+
yoff = 0.0
|
877
|
+
if model_type in ('S1R2', 'S1R2_all'): # 2 rotor
|
878
|
+
yoff = _draw_horizontal_magnets(ax, poles,
|
879
|
+
magn_height, rel_magn_width,
|
880
|
+
magn_yoke_height,
|
881
|
+
Q, g, taus, dy2,
|
882
|
+
yoff=yoff,
|
883
|
+
iron=IRON_UP)
|
884
|
+
yoff -= ag
|
885
|
+
|
886
|
+
tooth = TOOTH_ONLY if model_type in ('S1R2', 'S1R2_all') else TOOTH_DOWN
|
887
|
+
yoff = _draw_horizontal_slots(ax,
|
888
|
+
slot_height, slot_width, yoke_height,
|
889
|
+
Q, g, taus,
|
890
|
+
yoff=yoff,
|
891
|
+
tooth=tooth)
|
892
|
+
yoff -= ag
|
893
|
+
|
894
|
+
iron = IRON_DOWN if model_type in ('S1R1', 'S1R2', 'S1R2_all') else IRON_NO
|
895
|
+
yoff = _draw_horizontal_magnets(ax, poles,
|
896
|
+
magn_height, rel_magn_width,
|
897
|
+
magn_yoke_height,
|
898
|
+
Q, g, taus, dy2,
|
899
|
+
yoff=yoff,
|
900
|
+
iron=iron)
|
901
|
+
yoff -= ag
|
902
|
+
|
903
|
+
if model_type in ('S2R1', 'S2R1_all'): # 2 rotor
|
904
|
+
yoff = _draw_horizontal_slots(ax,
|
905
|
+
slot_height, slot_width, yoke_height,
|
906
|
+
Q, g, taus,
|
907
|
+
yoff=yoff,
|
908
|
+
tooth=TOOTH_UP)
|
909
|
+
|
910
|
+
_set_plot_attributes(ax)
|
911
|
+
logger.debug("end of horizontal_plot()")
|
912
|
+
|
913
|
+
|
601
914
|
class AFPM:
|
602
915
|
"""Axial Flux PM
|
603
916
|
Arguments:
|
@@ -636,6 +949,14 @@ class AFPM:
|
|
636
949
|
except KeyError:
|
637
950
|
raise ValueError("missing key afmtype")
|
638
951
|
|
952
|
+
if np.isscalar(machine['magnet']['afm_rotor']['rel_magn_width']):
|
953
|
+
rmagw = num_slices*[machine['magnet']['afm_rotor']['rel_magn_width']]
|
954
|
+
else:
|
955
|
+
rmagw = machine['magnet']['afm_rotor']['rel_magn_width']
|
956
|
+
if len(rmagw) == 1:
|
957
|
+
rmagw = num_slices*list(rmagw)
|
958
|
+
elif num_slices != len(rmagw):
|
959
|
+
num_slices = len(rmagw)
|
639
960
|
lfe = get_arm_lengths(machine['outer_diam'],
|
640
961
|
machine['inner_diam'],
|
641
962
|
num_slices)
|
@@ -659,11 +980,17 @@ class AFPM:
|
|
659
980
|
"name": "pole_width"},
|
660
981
|
{"values": lfe,
|
661
982
|
"name": "lfe"},
|
983
|
+
{"values": rmagw,
|
984
|
+
"name": "magnet.afm_rotor.rel_magn_width"},
|
662
985
|
{"values": linspeed, "name": "speed"}
|
663
986
|
]
|
664
987
|
}
|
988
|
+
|
665
989
|
machine['pole_width'] = np.pi * machine['inner_diam']/machine['poles']
|
666
990
|
machine['lfe'] = machine['outer_diam'] - machine['inner_diam']
|
991
|
+
machine['magnet']['afm_rotor']['rel_magn_width'] = max(
|
992
|
+
machine['magnet']['afm_rotor']['rel_magn_width'])
|
993
|
+
|
667
994
|
simulation['skew_displ'] = (simulation.get('skew_angle', 0)/180 * np.pi
|
668
995
|
* machine['inner_diam'])
|
669
996
|
nlresults = {}
|
femagtools/machine/utils.py
CHANGED
@@ -366,7 +366,8 @@ def dqparident(workdir, engine, temp, machine,
|
|
366
366
|
num_beta_steps: number of current steps (default 7 per quadrant)
|
367
367
|
speed: rotor speed in 1/s (default 160/p)
|
368
368
|
i1_max: maximum current in A rms (default approx 3*i1nom)
|
369
|
-
period_frac: fraction of rotating angle (default 6)
|
369
|
+
period_frac: (int) fraction of rotating angle (default 6)
|
370
|
+
dqtype: (str) type of identification: 'ldq' (default), 'psidq'
|
370
371
|
cmd: femag executable
|
371
372
|
"""
|
372
373
|
import pathlib
|
femagtools/multiproc.py
CHANGED
@@ -56,7 +56,8 @@ class ProgressLogger(threading.Thread):
|
|
56
56
|
["progress_logger",
|
57
57
|
f"{self.numTot}:{numOf}:{percent}:{' '.join(summary)}"])
|
58
58
|
else:
|
59
|
-
|
59
|
+
# TODO: log message might be misleading
|
60
|
+
logger.debug('collecting FE losses ...')
|
60
61
|
return
|
61
62
|
|
62
63
|
def stop(self):
|
femagtools/plot/__init__.py
CHANGED
femagtools/plot/bch.py
CHANGED
@@ -22,6 +22,26 @@ except ImportError: # ModuleNotFoundError:
|
|
22
22
|
logger = logging.getLogger("femagtools.plot.bch")
|
23
23
|
|
24
24
|
|
25
|
+
def find_peaks_and_valleys(t, y):
|
26
|
+
""" return peaks and valleys of y with maximum amplitude
|
27
|
+
"""
|
28
|
+
peaks = (np.diff(np.sign(np.diff(y))) < 0).nonzero()[0] + 1
|
29
|
+
if len(peaks > 0):
|
30
|
+
ip = np.argmax(y[peaks])
|
31
|
+
pv = {'yp': y[peaks][ip], 'tp': t[peaks][ip]}
|
32
|
+
else:
|
33
|
+
pv = {'yp': [], 'tp': []}
|
34
|
+
valleys = (np.diff(np.sign(np.diff(y))) > 0).nonzero()[0] + 1
|
35
|
+
if len(valleys > 0):
|
36
|
+
iv = np.argmin(y[valleys])
|
37
|
+
pv.update({'yv': y[valleys][iv], 'tv': t[valleys][iv]})
|
38
|
+
else:
|
39
|
+
pv.update({'yv': [], 'tv': []})
|
40
|
+
pv.update({'peaks': y[peaks], 'valleys': y[valleys],
|
41
|
+
'tpeaks': t[peaks], 'tvalleys': t[valleys]})
|
42
|
+
return pv
|
43
|
+
|
44
|
+
|
25
45
|
def _create_3d_axis():
|
26
46
|
"""creates a subplot with 3d projection if one does not already exist"""
|
27
47
|
from matplotlib.projections import get_projection_class
|
@@ -511,30 +531,62 @@ def transientsc(bch, title=''):
|
|
511
531
|
ax.grid(True)
|
512
532
|
istat = np.array([bch.scData[i]
|
513
533
|
for i in ('ia', 'ib', 'ic')])
|
534
|
+
pv = [find_peaks_and_valleys(
|
535
|
+
np.array(bch.scData['time']), i1)
|
536
|
+
for i1 in istat]
|
537
|
+
try:
|
538
|
+
ipvmax = np.argmax(
|
539
|
+
[y['yp'] if np.abs(y['yp']) > np.abs(y['yv']) else y['yv']
|
540
|
+
for y in pv])
|
541
|
+
imax = pv[ipvmax]['yp'] if np.abs(pv[ipvmax]['yp']) > np.abs(pv[ipvmax]['yv']) else pv[ipvmax]['yv']
|
542
|
+
except KeyError:
|
543
|
+
pass
|
514
544
|
if np.max(istat) > 4000:
|
515
545
|
istat *= 1e-3
|
546
|
+
imax *= 1e-3
|
516
547
|
ax.set_title('Currents / kA')
|
517
548
|
else:
|
518
549
|
ax.set_title('Currents / A')
|
519
550
|
|
520
551
|
for i, iph in zip(('ia', 'ib', 'ic'), istat):
|
521
552
|
ax.plot(bch.scData['time'], iph, label=i)
|
553
|
+
try:
|
554
|
+
ax.plot([pv[ipvmax]['tp']], [imax], '.')
|
555
|
+
ax.annotate(f'Imax = {imax:.1f}',
|
556
|
+
xy=(pv[ipvmax]['tp'], imax),
|
557
|
+
xytext=(pv[ipvmax]['tp']+0.01, imax))
|
558
|
+
except NameError:
|
559
|
+
pass
|
522
560
|
ax.set_xlabel('Time / s')
|
523
561
|
ax.legend()
|
524
562
|
|
525
563
|
row = 2
|
526
564
|
plt.subplot(rows, cols, row)
|
527
565
|
ax = plt.gca()
|
528
|
-
|
566
|
+
pv = find_peaks_and_valleys(
|
567
|
+
np.array(bch.scData['time']), np.array(bch.scData['torque']))
|
568
|
+
try:
|
569
|
+
tqmax = pv['yp'] if np.abs(pv['yp']) > np.abs(pv['yv']) else pv['yv']
|
570
|
+
tp = pv['tp'] if np.abs(pv['yp']) > np.abs(pv['yv']) else pv['tv']
|
571
|
+
except KeyError:
|
572
|
+
pass
|
529
573
|
torque = np.array(bch.scData['torque'])
|
530
574
|
if np.max(torque) > 4000:
|
531
575
|
torque *= 1e-3
|
576
|
+
tqmax *= 1e-3
|
532
577
|
ax.set_title('Torque / kNm')
|
533
578
|
else:
|
534
579
|
ax.set_title('Torque / Nm')
|
535
580
|
|
536
581
|
ax.grid(True)
|
537
582
|
ax.plot(bch.scData['time'], torque)
|
583
|
+
try:
|
584
|
+
ax.plot([tp], [tqmax], '.')
|
585
|
+
ax.annotate(f'Tmax = {tqmax:.1f}',
|
586
|
+
xy=(tp, tqmax),
|
587
|
+
xytext=(tp+0.01, tqmax))
|
588
|
+
except NameError:
|
589
|
+
pass
|
538
590
|
ax.set_xlabel('Time / s')
|
539
591
|
|
540
592
|
fig.tight_layout(h_pad=2)
|
@@ -560,8 +612,10 @@ def transientsc_demag(demag, magnet=0, title='', ax=0):
|
|
560
612
|
label='H Max {:4.2f} kA/m'.format(max(hmax)))
|
561
613
|
ax.plot(pos, havg,
|
562
614
|
label='H Avg {:4.2f} kA/m'.format(max(havg)))
|
563
|
-
|
564
|
-
|
615
|
+
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
|
+
|
565
619
|
ax.set_xlabel('Rotor Position / °')
|
566
620
|
ax.grid(True)
|
567
621
|
if magnet:
|
femagtools/plot/char.py
CHANGED
@@ -333,10 +333,14 @@ def _plot_contour(speed, torque, z, ax, title='', levels=[],
|
|
333
333
|
clippath = Path(_get_nT_boundary(x, y))
|
334
334
|
patch = PathPatch(clippath, facecolor='none')
|
335
335
|
ax.add_patch(patch)
|
336
|
-
|
337
|
-
c.
|
338
|
-
|
339
|
-
c.
|
336
|
+
try:
|
337
|
+
for c in cont.collections:
|
338
|
+
c.set_clip_path(patch)
|
339
|
+
for c in contf.collections:
|
340
|
+
c.set_clip_path(patch)
|
341
|
+
except AttributeError: # matplotlib >= 3.10
|
342
|
+
cont.set_clip_path(patch)
|
343
|
+
contf.set_clip_path(patch)
|
340
344
|
|
341
345
|
if xscale > 1:
|
342
346
|
def format_fn(tick_val, tick_pos):
|
@@ -367,9 +371,16 @@ def efficiency_map(rmap, ax=0, title='', clabel=True,
|
|
367
371
|
|
368
372
|
|
369
373
|
def losses_map(rmap, ax=0, title='Losses Map / kW', clabel=True,
|
370
|
-
cmap='YlOrRd', cbar=False):
|
374
|
+
cmap='YlOrRd', cbar=False, key='losses'):
|
375
|
+
"""
|
376
|
+
plot losses map
|
377
|
+
Args:
|
378
|
+
rmap: (dict) result of efficiency_losses_map
|
379
|
+
key: (str) type of losses: 'plfe1', 'plfe2', 'plmag', 'plcu1', 'plcu2', 'plfric', 'losses';
|
380
|
+
"""
|
381
|
+
|
371
382
|
if ax == 0:
|
372
383
|
fig, ax = plt.subplots(figsize=(12, 12))
|
373
|
-
return _plot_contour(rmap['n'], rmap['T'], np.asarray(rmap[
|
374
|
-
|
375
|
-
|
384
|
+
return _plot_contour(rmap['n'], rmap['T'], np.asarray(rmap[key])/1e3, ax,
|
385
|
+
title=title, levels=14, clabel=clabel,
|
386
|
+
cmap=cmap, cbar=cbar)
|