femagtools 1.8.4__py3-none-any.whl → 1.8.6__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 +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)
|