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.
@@ -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], args=(p,))[0]
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
- num_slices = kwargs.get('num_slices', 3)
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
- for pw, le, sp in zip(pole_width, lfe, linspeed):
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
- for results in [process(lfe, pole_width, machine, bch)
299
- for bch in zip(*[r['f'] for r in results])]:
300
- torque = np.mean(results.pop('torque'))
301
- results['torque'] = torque
302
- results.update(_psidq_ldq(results, nlresults))
303
- postp.append(results)
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 = {}
@@ -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
- logger.info('collecting FE losses ...')
59
+ # TODO: log message might be misleading
60
+ logger.debug('collecting FE losses ...')
60
61
  return
61
62
 
62
63
  def stop(self):
@@ -1,6 +1,7 @@
1
1
  """Creating plots
2
2
 
3
3
  """
4
+ from .machine import machine
4
5
  from .fluxdens import airgap, airgap_fft
5
6
  from .bch import torque, torque_fft, force, force_fft, \
6
7
  fluxdens_surface, winding_current, winding_flux, \
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
- scale = 1
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
- ax.plot([pos[0], pos[-1]], hclim, color='C3', linestyle='dashed',
564
- label='Hc {:4.2f} kA/m'.format(hclim[0]))
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
- for c in cont.collections:
337
- c.set_clip_path(patch)
338
- for c in contf.collections:
339
- c.set_clip_path(patch)
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['losses'])/1e3, ax,
374
- title=title, levels=14, clabel=clabel,
375
- cmap=cmap, cbar=cbar)
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)