femagtools 1.3.0__py3-none-any.whl → 1.3.2__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.
Files changed (47) hide show
  1. femagtools/__init__.py +1 -1
  2. femagtools/airgap.py +11 -37
  3. femagtools/amela.py +148 -13
  4. femagtools/bch.py +19 -3
  5. femagtools/dxfsl/area.py +68 -15
  6. femagtools/dxfsl/converter.py +15 -6
  7. femagtools/dxfsl/fslrenderer.py +13 -8
  8. femagtools/dxfsl/functions.py +1 -1
  9. femagtools/dxfsl/geom.py +415 -62
  10. femagtools/dxfsl/machine.py +97 -5
  11. femagtools/dxfsl/shape.py +46 -2
  12. femagtools/ecloss.py +393 -0
  13. femagtools/femag.py +25 -1
  14. femagtools/fsl.py +3 -2
  15. femagtools/hxy.py +126 -0
  16. femagtools/isa7.py +37 -24
  17. femagtools/machine/__init__.py +14 -13
  18. femagtools/machine/effloss.py +153 -32
  19. femagtools/machine/im.py +137 -56
  20. femagtools/machine/pm.py +584 -202
  21. femagtools/machine/sm.py +218 -64
  22. femagtools/machine/utils.py +12 -8
  23. femagtools/mcv.py +6 -8
  24. femagtools/model.py +11 -1
  25. femagtools/parstudy.py +1 -1
  26. femagtools/plot.py +159 -35
  27. femagtools/templates/afm_rotor.mako +102 -0
  28. femagtools/templates/afm_stator.mako +141 -0
  29. femagtools/templates/airgapinduc.mako +3 -3
  30. femagtools/templates/basic_modpar.mako +23 -2
  31. femagtools/templates/cogg_calc.mako +28 -5
  32. femagtools/templates/cu_losses.mako +1 -1
  33. femagtools/templates/fieldcalc.mako +39 -0
  34. femagtools/templates/gen_winding.mako +52 -47
  35. femagtools/templates/mesh-airgap.mako +43 -0
  36. femagtools/templates/stator3Linear.mako +5 -4
  37. femagtools/templates/therm-dynamic.mako +12 -6
  38. femagtools/templates/therm-static.mako +12 -0
  39. femagtools/templates/torq_calc.mako +2 -4
  40. femagtools/utils.py +45 -0
  41. femagtools/windings.py +2 -1
  42. {femagtools-1.3.0.dist-info → femagtools-1.3.2.dist-info}/METADATA +1 -1
  43. {femagtools-1.3.0.dist-info → femagtools-1.3.2.dist-info}/RECORD +47 -41
  44. {femagtools-1.3.0.dist-info → femagtools-1.3.2.dist-info}/WHEEL +1 -1
  45. {femagtools-1.3.0.dist-info → femagtools-1.3.2.dist-info}/LICENSE +0 -0
  46. {femagtools-1.3.0.dist-info → femagtools-1.3.2.dist-info}/entry_points.txt +0 -0
  47. {femagtools-1.3.0.dist-info → femagtools-1.3.2.dist-info}/top_level.txt +0 -0
femagtools/plot.py CHANGED
@@ -229,7 +229,7 @@ def airgap_fft(airgap, bmin=1e-2, ax=0):
229
229
  pass
230
230
 
231
231
 
232
- def torque(pos, torque, ax=0):
232
+ def torque(pos, torque, title='', ax=0):
233
233
  """creates plot from torque vs position"""
234
234
  k = 20
235
235
  alpha = np.linspace(pos[0], pos[-1],
@@ -242,7 +242,10 @@ def torque(pos, torque, ax=0):
242
242
  unit = 'kNm'
243
243
  if ax == 0:
244
244
  ax = plt.gca()
245
- ax.set_title('Torque / {}'.format(unit))
245
+ if title:
246
+ ax.set_title(title)
247
+ else:
248
+ ax.set_title('Torque / {}'.format(unit))
246
249
  ax.grid(True)
247
250
  ax.plot(pos, [scale*t for t in torque], 'go')
248
251
  ax.plot(alpha, scale*f(alpha))
@@ -896,7 +899,34 @@ def transientsc(bch, title=''):
896
899
  fig.tight_layout(h_pad=2)
897
900
  if title:
898
901
  fig.subplots_adjust(top=0.92)
902
+ return fig
899
903
 
904
+ def transientsc_demag(demag, magnet=0, title='', ax=0):
905
+ """creates a demag plot of a transient short circuit
906
+ Args:
907
+ demag: list of dicts with 'displ', 'H_av', 'H_max', 'lim_hc'
908
+ magnet dict with 'Tmag'
909
+ """
910
+ if ax == 0:
911
+ ax = plt.gca()
912
+ pos = [d['displ'] for d in demag if 'displ' in d]
913
+ hmax = [-d['H_max'] for d in demag if 'H_max' in d]
914
+ havg = [-d['H_av'] for d in demag if 'H_av' in d]
915
+ hclim = [-d['lim_hc'] for d in demag if 'lim_hc' in d]*2
916
+
917
+ ax.set_title('Transient Short Circuit Demagnetization [kA/m]')
918
+ ax.plot(pos, hmax,
919
+ label='H Max {:4.2f} kA/m'.format(max(hmax)))
920
+ ax.plot(pos, havg,
921
+ label='H Avg {:4.2f} kA/m'.format(max(havg)))
922
+ ax.plot([pos[0], pos[-1]], hclim, color='C3', linestyle='dashed',
923
+ label='Hc {:4.2f} kA/m'.format(hclim[0]))
924
+ ax.set_xlabel('Rotor Position / °')
925
+ ax.grid(True)
926
+ if magnet:
927
+ ax.legend(title=f"Magnet Temperature {magnet['Tmag']}°C")
928
+ else:
929
+ ax.legend()
900
930
 
901
931
  def i1beta_torque(i1, beta, torque, title='', ax=0):
902
932
  """creates a surface plot of torque vs i1, beta"""
@@ -1222,7 +1252,7 @@ def _contour(ax, title, elements, values, label='', isa=None):
1222
1252
  if ax == 0:
1223
1253
  ax = plt.gca()
1224
1254
  ax.set_aspect('equal')
1225
- ax.set_title(title, fontsize=18)
1255
+ ax.set_title(title)
1226
1256
  if isa:
1227
1257
  for se in isa.superelements:
1228
1258
  ax.add_patch(Polygon([n.xy
@@ -1236,13 +1266,13 @@ def _contour(ax, title, elements, values, label='', isa=None):
1236
1266
  p = PatchCollection(patches, alpha=1.0, match_original=False)
1237
1267
  p.set_array(np.asarray(values)[valid_values])
1238
1268
  ax.add_collection(p)
1239
- cb = plt.colorbar(p)
1269
+ cb = plt.colorbar(p, shrink=0.9)
1240
1270
  for patch in np.array([Polygon([v.xy for v in e.vertices],
1241
1271
  fc='white', alpha=1.0)
1242
1272
  for e in elements])[np.isnan(values)]:
1243
1273
  ax.add_patch(patch)
1244
1274
  if label:
1245
- cb.set_label(label=label, fontsize=18)
1275
+ cb.set_label(label=label)
1246
1276
  ax.autoscale(enable=True)
1247
1277
  ax.axis('off')
1248
1278
 
@@ -1254,29 +1284,35 @@ def demag(isa, ax=0):
1254
1284
  """
1255
1285
  emag = [e for e in isa.elements if e.is_magnet()]
1256
1286
  demag = np.array([e.demagnetization(isa.MAGN_TEMPERATURE) for e in emag])
1257
- _contour(ax, f'Demagnetization at {isa.MAGN_TEMPERATURE} °C',
1287
+ _contour(ax, f'Demagnetization at {isa.MAGN_TEMPERATURE} °C (max -{np.max(demag):.1f} kA/m)',
1258
1288
  emag, demag, '-H / kA/m', isa)
1259
1289
  logger.info("Max demagnetization %f", np.max(demag))
1260
1290
 
1261
1291
 
1262
- def demag_pos(isa, pos, icur=-1, ibeta=-1, ax=0):
1292
+ def demag_pos(isa, pos=-1, icur=-1, ibeta=-1, ax=0):
1263
1293
  """plot demag of NC/I7/ISA7 model at rotor position
1264
1294
  Args:
1265
1295
  isa: Isa7/NC object
1266
- pos: rotor position in degree
1296
+ pos: rotor position in degree (maximum h if -1)
1267
1297
  icur: cur amplitude index or last index if -1
1268
1298
  ibeta: beta angle index or last index if -1
1269
1299
  """
1270
1300
  emag = [e for e in isa.elements if e.is_magnet()]
1271
1301
  demag = np.array([isa.demagnetization(e, icur, ibeta)[1]
1272
1302
  for e in emag])
1273
- for i, x in enumerate(isa.pos_el_fe_induction):
1274
- if x >= pos/180*np.pi:
1275
- break
1303
+ if pos>=0:
1304
+ for i, x in enumerate(isa.pos_el_fe_induction):
1305
+ if x >= pos/180*np.pi:
1306
+ break
1307
+ else:
1308
+ demagmax = np.max(demag, axis=0)
1309
+ i = np.argmax(demagmax)
1310
+ x = isa.pos_el_fe_induction[i]
1276
1311
 
1277
1312
  hpol = demag[:, i]
1278
1313
  hpol[hpol == 0] = np.nan
1279
- _contour(ax, f'Demagnetization at Pos. {round(x/np.pi*180)}° ({isa.MAGN_TEMPERATURE} °C)',
1314
+ _contour(ax, f'Demagnetization at pos. {round(x/np.pi*180):.1f}°,'
1315
+ f'{isa.MAGN_TEMPERATURE} °C (max -{np.max(hpol):.1f} kA/m)',
1280
1316
  emag, hpol, '-H / kA/m', isa)
1281
1317
  logger.info("Max demagnetization %f kA/m", np.nanmax(hpol))
1282
1318
 
@@ -1297,7 +1333,7 @@ def flux_density(isa, subreg=[], ax=0):
1297
1333
  elements = [e for e in isa.elements]
1298
1334
 
1299
1335
  fluxd = np.array([np.linalg.norm(e.flux_density()) for e in elements])
1300
- _contour(ax, f'Flux Density T', elements, fluxd)
1336
+ _contour(ax, f'Flux Density T (max {np.max(fluxd):.1f} T)', elements, fluxd)
1301
1337
  logger.info("Max flux dens %f", np.max(fluxd))
1302
1338
 
1303
1339
 
@@ -1311,8 +1347,9 @@ def loss_density(isa, subreg=[], ax=0):
1311
1347
  sr = subreg
1312
1348
  else:
1313
1349
  sr = [subreg]
1314
- elements = [e for s in sr for sre in isa.get_subregion(s).elements()
1315
- for e in sre]
1350
+ elements = [e
1351
+ for s in sr
1352
+ for e in isa.get_subregion(s).elements()]
1316
1353
  else:
1317
1354
  elements = [e for e in isa.elements]
1318
1355
 
@@ -1652,15 +1689,26 @@ def characteristics(char, title=''):
1652
1689
  fig.suptitle(title)
1653
1690
 
1654
1691
  n = np.array(char['n'])*60
1655
- pmech = np.array(char['pmech'])*1e-3
1692
+ punit = 'kW'
1693
+ k = 1e-3
1694
+ if max(char['pmech']) > 1e6:
1695
+ punit = 'MW'
1696
+ k = 1e-6
1697
+ pmech = np.array(char['pmech'])*k
1698
+ tunit = 'Nm'
1699
+ if max(char['T']) > 1e3:
1700
+ tunit = 'kNm'
1701
+ tq = np.array(char['T'])*1e-3
1702
+ else:
1703
+ tq = np.array(char['T'])
1656
1704
 
1657
- axs[0, 0].plot(n, np.array(char['T']), 'C0-', label='Torque')
1658
- axs[0, 0].set_ylabel("Torque / Nm")
1705
+ axs[0, 0].plot(n, tq, 'C0-', label='Torque')
1706
+ axs[0, 0].set_ylabel(f"Torque / {tunit}")
1659
1707
  axs[0, 0].grid()
1660
1708
  axs[0, 0].legend(loc='center left')
1661
1709
  ax1 = axs[0, 0].twinx()
1662
1710
  ax1.plot(n, pmech, 'C1-', label='P mech')
1663
- ax1.set_ylabel("Power / kW")
1711
+ ax1.set_ylabel(f"Power / {punit}")
1664
1712
  ax1.legend(loc='lower center')
1665
1713
 
1666
1714
  axs[0, 1].plot(n[1:], np.array(char['u1'][1:]), 'C0-', label='Voltage')
@@ -1697,6 +1745,12 @@ def characteristics(char, title=''):
1697
1745
  pl = np.array(char['losses'])*1e-3
1698
1746
  axs[1, 1].plot(n, plcu, 'C0-', label='Cu Losses')
1699
1747
  axs[1, 1].plot(n, plfe, 'C1-', label='Fe Losses')
1748
+ try:
1749
+ if char['plfw'] and char['plfw'][-1] > 0:
1750
+ plfw = np.array(char['plfw'])*1e-3
1751
+ axs[1, 1].plot(n, plfw, 'C2-', label='Friction + Windage')
1752
+ except KeyError:
1753
+ pass
1700
1754
  axs[1, 1].set_ylabel("Losses / kW")
1701
1755
  axs[1, 1].legend(loc='center left')
1702
1756
  axs[1, 1].grid()
@@ -1724,12 +1778,62 @@ def get_nT_boundary(n, T):
1724
1778
  bnd[1].append((nx, tx))
1725
1779
  return np.array(bnd[0] + bnd[1][::-1])
1726
1780
 
1781
+ def normalize10(v, **kwargs):
1782
+ """
1783
+ Normalizes the input-array using the nearest (ceiling) power of 10.
1784
+
1785
+ Arguments:
1786
+ v: array to normalize
1727
1787
 
1728
- def plot_contour(speed, torque, z, ax, title='', levels=[], clabel=True):
1788
+ Returns:
1789
+ normalized array
1790
+ normalisation factor (power of 10)
1791
+ """
1792
+ n_type = kwargs.get('n_type', 'abs') # 'norm')
1793
+ r_type = kwargs.get('r_type', 'round') # 'floor')
1794
+ if n_type == 'norm':
1795
+ norm = np.log10(np.linalg.norm(v))
1796
+ elif n_type == 'abs':
1797
+ norm = np.log10(np.max(np.abs(v)))
1798
+ else:
1799
+ raise AttributeError('Unknown norm-type. Allowed values are: "norm", "abs".')
1800
+
1801
+ if r_type == 'floor':
1802
+ norm = 10 ** np.floor(norm)
1803
+ elif r_type == 'ceil':
1804
+ norm = 10 ** np.ceil(norm)
1805
+ elif r_type == 'round':
1806
+ norm = 10 ** np.round(norm)
1807
+ else:
1808
+ raise AttributeError('Unknown rounding type. Allowed values are: "floor", "ceil", "round".')
1809
+ # norm = 10**(np.ceil(np.log10(np.max(np.abs(v)))))
1810
+ # norm = 10 ** (np.ceil(np.log10(np.linalg.norm(v))))
1811
+ # norm = 10 ** (np.floor(np.log10(np.max(np.abs(v)))))
1812
+ # norm = 10 ** (np.floor(np.log10(np.linalg.norm(v))))
1813
+ return v / norm, norm
1814
+
1815
+
1816
+ def plot_contour(speed, torque, z, ax, title='', levels=[],
1817
+ clabel=True, cmap='YlOrRd', cbar=False, **kwargs):
1818
+ """ contour plot of speed, torque, z values
1819
+ Arguments:
1820
+ levels: (list of floats)
1821
+ clabel: contour labels if True
1822
+ cmap: colour map
1823
+ cbar: (bool) create color bar if True (default False)
1824
+
1825
+ Note: the x and y axes are scaled
1826
+
1827
+ returns tricontourf, xscale, yscale
1828
+ """
1729
1829
  from matplotlib.path import Path
1730
1830
  from matplotlib.patches import PathPatch
1731
- x = [60*n for n in speed]
1732
- y = torque
1831
+ x = 60*np.asarray(speed)
1832
+ y = np.asarray(torque)
1833
+
1834
+ x, xscale = normalize10(x, **kwargs)
1835
+ y, yscale = normalize10(y, **kwargs)
1836
+
1733
1837
  if not levels:
1734
1838
  if max(z) <= 1:
1735
1839
  if max(z) > 0.96:
@@ -1743,38 +1847,58 @@ def plot_contour(speed, torque, z, ax, title='', levels=[], clabel=True):
1743
1847
  levels.append(np.ceil(max(z)*100)/100)
1744
1848
  else:
1745
1849
  levels = 14
1746
- cont = ax.tricontour(x, y, z,
1747
- linewidths=0.4, levels=levels, colors='k')
1748
- if clabel:
1749
- ax.clabel(cont, inline=True, colors='k', fontsize=8)
1750
- contf = ax.tricontourf(x, y, z,
1751
- levels=levels, cmap='YlOrRd')
1752
1850
  #
1753
1851
  ax.spines['top'].set_color('none')
1754
1852
  ax.spines['right'].set_color('none')
1755
1853
 
1854
+ cont = ax.tricontour(x, y, z,
1855
+ linewidths=0.4, levels=levels,
1856
+ colors='k')
1857
+ contf = ax.tricontourf(x, y, z,
1858
+ levels=levels, cmap=cmap)
1859
+
1756
1860
  clippath = Path(get_nT_boundary(x, y))
1757
1861
  patch = PathPatch(clippath, facecolor='none')
1758
1862
  ax.add_patch(patch)
1759
1863
  for c in cont.collections:
1760
1864
  c.set_clip_path(patch)
1865
+ #ax.plot(x, y, "k.", ms=3)
1866
+ if clabel:
1867
+ ax.clabel(cont, inline=True, colors='k', fontsize=8, inline_spacing=0)
1868
+
1761
1869
  for c in contf.collections:
1762
1870
  c.set_clip_path(patch)
1763
- #ax.plot(x, y, "k.", ms=3)
1871
+
1872
+ if xscale > 1:
1873
+ def format_fn(tick_val, tick_pos):
1874
+ return round(xscale*tick_val)
1875
+ ax.xaxis.set_major_formatter(format_fn)
1876
+ if yscale > 1:
1877
+ def format_fn(tick_val, tick_pos):
1878
+ return round(yscale*tick_val)
1879
+ ax.yaxis.set_major_formatter(format_fn)
1880
+
1764
1881
  ax.set_ylabel('Torque / Nm')
1765
1882
  ax.set_xlabel('Speed / rpm')
1766
1883
  ax.set_title(title)
1767
- return contf
1884
+ if cbar:
1885
+ cfig = ax.get_figure()
1886
+ cfig.colorbar(contf, ax=ax,
1887
+ orientation='vertical')
1888
+ return contf, xscale, yscale
1889
+
1768
1890
 
1769
- def efficiency_map(rmap, ax=0, title='Efficiency Map', clabel=True):
1891
+ def efficiency_map(rmap, ax=0, title='Efficiency Map', clabel=True,
1892
+ cmap='YlOrRd', levels=None, cbar=False):
1770
1893
  if ax == 0:
1771
1894
  fig, ax = plt.subplots(figsize=(12, 12))
1772
- contf = plot_contour(rmap['n'], rmap['T'], rmap['eta'], ax,
1773
- title=title, clabel=clabel)
1774
- return contf
1895
+ return plot_contour(rmap['n'], rmap['T'], rmap['eta'], ax,
1896
+ title=title, clabel=clabel, cmap=cmap,
1897
+ levels=levels, cbar=cbar)
1775
1898
 
1776
1899
 
1777
- def losses_map(rmap, ax=0, title='Losses Map / kW', clabel=True):
1900
+ def losses_map(rmap, ax=0, title='Losses Map / kW', clabel=True,
1901
+ cmap='YlOrRd', cbar=False):
1778
1902
  if ax == 0:
1779
1903
  fig, ax = plt.subplots(figsize=(12, 12))
1780
1904
  return plot_contour(rmap['n'], rmap['T'], np.asarray(rmap['losses'])/1e3, ax,
@@ -0,0 +1,102 @@
1
+ -- Create model of the rotor of the axial flow machine
2
+
3
+ m.yoke_height = ${model['yoke_height']*1e3} -- Yoke height [mm]
4
+ m.magn_height = ${model['magn_height']*1e3} -- Magnet height [mm]
5
+ m.magn_width = ${model['rel_magn_width']*1e2} -- Magnet width: >0:[%]; <0: [mm]
6
+ m.spoke_width = ${model['spoke_width']*1e3} -- Spoke width: >0 magn_width will be calculated
7
+ m.gap_ma_yoke = 0
8
+ m.nodedist = ${model.get('nodedist', 1)}
9
+ --@param m.npols_gen -- Number of poles generated
10
+
11
+
12
+ if (m.model_type == "S2R1") or (m.model_type == "S2R1_all") then
13
+ if (m.yoke_height > 0) then
14
+ m.yoke_height = 0
15
+ print("Warning: m.yoke_height for this typ must be 0. Set to 0!")
16
+ end
17
+ end
18
+
19
+ if (m.yoke_height > 0) then
20
+ mh = m.magn_height
21
+ else
22
+ mh = m.magn_height/2
23
+ end
24
+
25
+ if m.spoke_width > 0 then
26
+ m.magn_width = -(m.pole_width-m.spoke_width)
27
+ end
28
+ if m.magn_width > 0 then
29
+ mw = m.magn_width/100*m.pole_width
30
+ else
31
+ mw = -m.magn_width
32
+ end
33
+
34
+ fml = require("fml")
35
+
36
+ P0 = fml.Point:Create(0, -ag)
37
+
38
+ Lh1 = fml.Line:Create(P0,0)
39
+ Lh2 = fml.Line:Parallel(Lh1,mh)
40
+ Lh3 = fml.Line:Parallel(Lh2,m.yoke_height)
41
+
42
+ Lv1 = fml.Line:Create(P0,90)
43
+ Lv2 = fml.Line:Parallel(Lv1,(m.pole_width-mw)/2)
44
+ Lv3 = fml.Line:Parallel(Lv1,(m.pole_width+mw)/2)
45
+ Lv4 = fml.Line:Parallel(Lv1,m.pole_width)
46
+
47
+ P11 = P0
48
+ P12 = fml.Point:Intersection(Lh2,Lv1)
49
+ P13 = fml.Point:Intersection(Lh3,Lv1)
50
+ P21 = fml.Point:Intersection(Lh1,Lv2)
51
+ P22 = fml.Point:Intersection(Lh2,Lv2)
52
+ P31 = fml.Point:Intersection(Lh1,Lv3)
53
+ P32 = fml.Point:Intersection(Lh2,Lv3)
54
+ P41 = fml.Point:Intersection(Lh1,Lv4)
55
+ P42 = fml.Point:Intersection(Lh2,Lv4)
56
+ P43 = fml.Point:Intersection(Lh3,Lv4)
57
+
58
+ ndt(m.nodedist)
59
+
60
+ nc_line(P11.x,P11.y, P21.x,P21.y, 0)
61
+ nc_line_cont(P31.x,P31.y, 0)
62
+ nc_line_cont(P41.x,P41.y, 0)
63
+
64
+ nc_line(P12.x,P12.y, P22.x,P22.y, 0)
65
+ nc_line_cont(P32.x,P32.y, 0)
66
+ nc_line_cont(P42.x,P42.y, 0)
67
+
68
+ nc_line(P11.x,P11.y, P12.x,P12.y, 0)
69
+ nc_line(P21.x,P21.y, P22.x,P22.y, 0)
70
+ nc_line(P31.x,P31.y, P32.x,P32.y, 0)
71
+ nc_line(P41.x,P41.y, P42.x,P42.y, 0)
72
+
73
+ create_mesh_se((P12.x+P22.x)/2, (P11.y + P12.y)/2)
74
+ create_mesh_se((P22.x+P32.x)/2, (P21.y + P22.y)/2)
75
+ create_mesh_se((P32.x+P42.x)/2, (P31.y + P32.y)/2)
76
+
77
+ if (m.yoke_height > 0) then
78
+ nc_line(P13.x,P13.y, P43.x,P43.y, 0)
79
+ nc_line(P12.x,P12.y, P13.x,P13.y, 0)
80
+ nc_line(P42.x,P42.y, P43.x,P43.y, 0)
81
+ x,y = (P12.x+P42.x)/2,(P42.y+P43.y)/2
82
+ create_mesh_se(x,y)
83
+ def_new_sreg(x,y, "rofe", "blue")
84
+ if (mcvkey_yoke == 'dummy') then
85
+ def_mat_fm(x,y, blue, 1000, 100)
86
+ else
87
+ def_mat_fm_nlin(x,y, blue, mcvkey_yoke, 100, 0)
88
+ end
89
+ end
90
+
91
+ translate_copy_nodechains(P11.x,P11.y, P13.x,P13.y, P43.x,P43.y, P41.x,P41.y, m.npols_gen-1)
92
+
93
+ x = (P12.x+P32.x)/2
94
+ y = (P12.y+P32.y)/2
95
+ for i = 1,m.npols_gen do
96
+ if i%2 == 0 then
97
+ def_mat_pm(x,y, 'red', m.remanenc, m.relperm, 90, "parallel", 0, m.rlen)
98
+ else
99
+ def_mat_pm(x,y, 'green', m.remanenc, m.relperm, 270, "parallel", 0, m.rlen)
100
+ end
101
+ x = x + m.pole_width
102
+ end
@@ -0,0 +1,141 @@
1
+ --[[ Supported AFM Model Types
2
+ "S1R1" -- 1 stator, 1 rotor
3
+ "S1R2" -- 1 stator, 2 rotor, 1 half simulated
4
+ "S1R2_all" -- 1 stator, 2 rotor, all simulated
5
+ "S2R1" -- 2 stator, 1 rotor, 1 half simulated
6
+ "S2R1_all" -- 2 stator, 1 rotor, all simulated
7
+ b--]]
8
+ m.slot_height = ${model['slot_height']*1e3} -- Total slot height [mm]
9
+ m.slot_h1 = ${model['slot_h1']*1e3} -- Slot opening height [mm]
10
+ m.slot_h2 = ${model['slot_h2']*1e3} -- Distance to radius [mm]
11
+ m.st_yoke_height = ${model['yoke_height']*1e3} -- Yoke height [mm]
12
+ m.slot_width = ${model['slot_width']*1e3} -- Slot width [mm]
13
+ m.slot_open_width = ${model['slot_open_width']*1e3} -- Slot opening width [mm]
14
+ m.slot_r1 = ${model['slot_r1']*1e3} -- Slot radius [mm]
15
+ m.slot_r2 = ${model['slot_r2']*1e3} -- Slot radius [mm]
16
+ m.mcvkey_yoke = mcvkey_yoke
17
+ m.nodedist = ${model.get('m.nodedist', 1)} -- Node distance
18
+
19
+ if (m.model_type == "S1R2") or (m.model_type == "S1R2_all") then
20
+ if (m.st_yoke_height > 0) then
21
+ m.st_yoke_height = 0
22
+ print("Warning: m.st_yoke_height for this type must be 0. Set to 0!")
23
+ end
24
+ end
25
+
26
+ if m.st_yoke_height > 0 then
27
+ sh = m.slot_height
28
+ else
29
+ sh = m.slot_height/2
30
+ end
31
+
32
+ fml = require("fml")
33
+
34
+ P0 = fml.Point:Create(0,0)
35
+
36
+ Lh1 = fml.Line:Create(P0,0)
37
+ Lh2 = fml.Line:Parallel(Lh1,-m.slot_h1)
38
+ Lh3 = fml.Line:Parallel(Lh1,-m.slot_h2)
39
+ Lh4 = fml.Line:Parallel(Lh1,-(sh-m.slot_r2))
40
+ Lh5 = fml.Line:Parallel(Lh1,-sh)
41
+ Lh6 = fml.Line:Parallel(Lh1,-(sh+m.st_yoke_height))
42
+
43
+ Lv1 = fml.Line:Create(P0,90)
44
+ m.width_bz = m.pole_width*m.num_poles/m.tot_num_slot
45
+ m.tooth_width = m.width_bz-m.slot_width
46
+ Lv2 = fml.Line:Parallel(Lv1,m.tooth_width/2)
47
+ Lv3 = fml.Line:Parallel(Lv1,m.tooth_width/2+m.slot_r1)
48
+ Lv4 = fml.Line:Parallel(Lv1,m.tooth_width/2+m.slot_r2)
49
+ Lv5 = fml.Line:Parallel(Lv1,m.tooth_width/2+m.slot_width/2-m.slot_open_width/2)
50
+ Lv6 = fml.Line:Parallel(Lv1,m.tooth_width/2+m.slot_width/2)
51
+
52
+ P11 = P0
53
+ P16 = fml.Point:Intersection(Lh6,Lv1)
54
+ P23 = fml.Point:Intersection(Lh3,Lv2)
55
+ P24 = fml.Point:Intersection(Lh4,Lv2)
56
+ P25 = fml.Point:Intersection(Lh5,Lv2)
57
+ P26 = fml.Point:Intersection(Lh6,Lv2)
58
+ P34 = fml.Point:Intersection(Lh4,Lv3)
59
+ P33 = fml.Point:Intersection(Lh3,Lv3)
60
+ P44 = fml.Point:Intersection(Lh4,Lv4)
61
+ P45 = fml.Point:Intersection(Lh5,Lv4)
62
+ P51 = fml.Point:Intersection(Lh1,Lv5)
63
+ P52 = fml.Point:Intersection(Lh2,Lv5)
64
+ P61 = fml.Point:Intersection(Lh1,Lv6)
65
+ P62 = fml.Point:Intersection(Lh2,Lv6)
66
+ P65 = fml.Point:Intersection(Lh5,Lv6)
67
+ P66 = fml.Point:Intersection(Lh6,Lv6)
68
+
69
+ if (m.slot_r1 > 0) then
70
+ C1 = fml.Circle:Create(P33,m.slot_r1)
71
+ P32 = fml.Point:Tangent(P52,C1,2)
72
+ else
73
+ P32 = P23
74
+ end
75
+
76
+ model_size = m.pole_width*m.num_poles*m.num_slots/m.tot_num_slot
77
+ blow_up_wind(0,-model_size/2, model_size,model_size/2)
78
+ ndt(m.nodedist)
79
+
80
+ nc_line(P11.x,P11.y, P52.x,P51.y, 0)
81
+ nc_line_cont(P61.x,P61.y, 0)
82
+ nc_line(P52.x,P52.y, P62.x,P62.y, 0)
83
+ if (m.st_yoke_height > 0) then
84
+ nc_line(P45.x,P45.y, P65.x,P65.y, 0)
85
+ nc_line(P16.x,P16.y, P66.x,P66.y, 0)
86
+ else
87
+ nc_line(P16.x,P16.y, P26.x,P26.y, 0)
88
+ nc_line_cont(P66.x,P66.y, 0)
89
+ end
90
+
91
+ nc_line(P11.x,P11.y, P16.x,P16.y, 0)
92
+ if (m.st_yoke_height > 0) then
93
+ nc_line(P23.x,P23.y, P24.x,P24.y, 0)
94
+ else
95
+ nc_line(P23.x,P23.y, P26.x,P26.y, 0)
96
+ end
97
+ nc_line(P51.x,P51.y, P52.x,P52.y, 0)
98
+ nc_line(P61.x,P61.y, P62.x,P62.y, 0)
99
+ if (m.st_yoke_height > 0) then
100
+ nc_line_cont(P65.x,P65.y, 0)
101
+ nc_line_cont(P66.x,P66.y, 0)
102
+ else
103
+ nc_line_cont(P66.x,P66.y, 0)
104
+ end
105
+ nc_line(P52.x,P52.y, P32.x,P32.y, 0)
106
+
107
+ if (m.slot_r1 > 0) then
108
+ nc_circle_m(P23.x,P23.y, P32.x,P32.y, P33.x,P33.y, m.slot_r1, 0)
109
+ end
110
+ if (m.st_yoke_height > 0 and m.slot_r2 > 0) then
111
+ nc_circle_m(P45.x,P45.y, P24.x,P24.y, P44.x,P44.y, m.slot_r2, 0)
112
+ end
113
+
114
+ if m.slot_h1 > 0 then
115
+ create_mesh_se((P51.x+P61.x)/2, (P51.y + P52.y)/2)
116
+ end
117
+ create_mesh_se((P23.x+P62.x)/2, (P62.y + P65.y)/2)
118
+
119
+ x,y = (P11.x+P26.x)/2,(P11.y+P26.y)/2
120
+ create_mesh_se(x, y)
121
+ def_new_sreg(x,y, "stfe", "blue")
122
+
123
+ x1,y1 = P61.x,P61.y
124
+ x2,y2 = P66.x,P66.y
125
+ mirror_nodechains(x1,y1, x2,y2)
126
+
127
+ x1,y1 = P16.x,P16.y
128
+ x2,y2 = P11.x,P11.y
129
+ x3,y3 = 2*P61.x,P61.y
130
+ x4,y4 = 2*P61.x,P66.y
131
+ translate_copy_nodechains(x1,y1, x2,y2, x3,y3, x4,y4,
132
+ m.num_sl_gen-1)
133
+
134
+ x,y = (P11.x+P26.x)/2,(P11.y+P26.y)/2
135
+ if (m.mcvkey_yoke == 'dummy') then
136
+ def_mat_fm(x,y, blue, 1000, 100)
137
+ else
138
+ def_mat_fm_nlin(x,y, blue, m.mcvkey_yoke, 100, 0)
139
+ end
140
+
141
+ m.middle_line = 1 -- vertical slot parts
@@ -1,6 +1,6 @@
1
- post_models("induct(x)","b") -- Calculate field distribution
1
+ post_models("induct(x)","bagx") -- Calculate field distribution
2
2
  data=io.open("bag.dat","w") -- Output in data file
3
- for i = 1, table.getn(b), 3 do
4
- data:write(string.format("%g %g %g\n",b[i],b[i+1],b[i+2]))
3
+ for i = 1, #bagx, 3 do
4
+ data:write(string.format("%g %g %g\n",bagx[i],bagx[i+1],bagx[i+2]))
5
5
  end
6
6
  io.close(data)
@@ -14,6 +14,7 @@ ag = 0
14
14
  % if model.move_action == 0:
15
15
  % if model.external_rotor:
16
16
  dy2 = ${model.get(['outer_diam'])*1e3}
17
+ % if hasattr(model, 'bore_diam'):
17
18
  % if isinstance(model.get(['bore_diam']), list):
18
19
  <%
19
20
  da2 = '{' + ','.join([str(x*1e3) for x in model.get(['bore_diam'])]) +'}'
@@ -27,9 +28,11 @@ end
27
28
  da2 = ${model.get(['bore_diam'])*1e3}
28
29
  da1 = da2 - 2*ag
29
30
  % endif
31
+ % endif # bore_diam dos not exist
30
32
  dy1 = ${model.get(['inner_diam'])*1e3}
31
33
  % else: # internal rotor
32
34
  dy1 = ${model.get(['outer_diam'])*1e3}
35
+ % if hasattr(model, 'bore_diam'):
33
36
  % if isinstance(model.get(['bore_diam']), list):
34
37
  <%
35
38
  da1 = '{' + ','.join([str(x*1e3) for x in model.get(['bore_diam'])]) +'}'
@@ -43,6 +46,7 @@ end
43
46
  da1 = ${model.get(['bore_diam'])*1e3}
44
47
  da2 = da1 - 2*ag
45
48
  % endif
49
+ % endif
46
50
  % if hasattr(model, 'shaft_diam'):
47
51
  dy2 = ${model.get(['shaft_diam'])*1e3}
48
52
  dsh = ${model.get(['inner_diam'])*1e3}
@@ -66,7 +70,8 @@ m.num_pol_pair = m.num_poles/2
66
70
  m.num_slots = m.num_sl_gen
67
71
  m.npols_gen = m.num_poles * m.num_sl_gen / m.tot_num_slot
68
72
  m.tot_num_sl = m.tot_num_slot
69
- % if model.move_action == 0:
73
+ % if model.move_action == 0: # rotating
74
+ % if hasattr(model, 'bore_diam'):
70
75
  % if isinstance(model.get(['bore_diam']), list):
71
76
  m.fc_radius = (da1[2]/2-ag[2]/2) -- Radius airgap (extern)
72
77
  m.fc_radius1 = (da1[1]/2-ag[1]/2) -- Radius airgap (intern?)
@@ -77,10 +82,26 @@ m.fc_radius1 = m.fc_radius
77
82
  m.sl_radius = m.fc_radius -- radius of sliding area
78
83
  % endif
79
84
  % endif
85
+ % elif hasattr(model, 'pole_width'): # move action linear
86
+ m.pole_width = ${model['pole_width']*1e3}
87
+ % endif
88
+ % if hasattr(model, 'lfe'):
80
89
  m.arm_length = ${model.get(['lfe'])*1e3}
90
+ % endif
81
91
  pre_models("basic_modpar")
82
92
  % endif
83
93
  % if hasattr(model, 'num_agnodes'):
84
- num_agnodes = ${model.num_agnodes}
94
+ num_agnodes = m.npols_gen*${model.num_agnodes}
95
+ % if hasattr(model, 'bore_diam'):
85
96
  agndst = 2*math.pi*m.fc_radius/num_agnodes
97
+ % else:
98
+ if m.pole_width ~= nil then
99
+ agndst = 2*m.pole_width/num_agnodes
100
+ else
101
+ agndst = 1 -- last resort
102
+ end
103
+ % endif
86
104
  % endif
105
+ % if hasattr(model, 'afmtype'):
106
+ m.model_type = "${model['afmtype']}"
107
+ % endif
@@ -1,19 +1,42 @@
1
1
  --
2
2
  -- Cogging
3
3
  --
4
- m.move_action = 0.0 -- rotate
5
4
  % if model.get('lfe', 0):
6
5
  m.arm_length = ${model.get('lfe')*1e3}
7
6
  % endif
8
-
7
+ m.move_action = ${model.get('move_action', 0)}
8
+ % if model.get('move_action', 0) == 0:
9
9
  m.speed = ${model.get('speed')*60}
10
10
  m.skew_angle = ${model.get('skew_angle', 0)}
11
+ m.fc_force_points = 0.0 -- number move points in air gap
12
+ m.phi_start = ${model.get('phi_start', 0)}
13
+ m.range_phi = ${model.get('range_phi', 0)}
14
+ % else:
15
+ m.speed_linear = ${model.get('speed')}
16
+ m.skew_linear = ${model.get('skew_displ',0)}
17
+ m.line = 0
18
+ m.two_pole_wi = 2*m.pole_width
19
+ m.range_x = m.two_pole_wi
20
+ m.range_y = 0.0
21
+
22
+ m.fc_force_points = 5
23
+ m.fcpx_mm1 = m.npols_gen*m.pole_width +1.0
24
+ m.fcpy_mm1 = -ag/2
25
+ m.fcpx_mm2 = -1.0
26
+ m.fcpy_mm2 = m.fcpy_mm1
27
+ m.fcpx_mm3 = m.fcpx_mm2
28
+ m.fcpy_mm3 = -m.magn_height -m.yoke_height -m.gap_ma_yoke -1 -ag
29
+ m.fcpx_mm4 = m.fcpx_mm1
30
+ m.fcpy_mm4 = m.fcpy_mm3
31
+ m.fcpx_mm5 = m.fcpx_mm1
32
+ m.fcpy_mm5 = m.fcpy_mm1
33
+
34
+ --m.npols_gen = 1 -- number of sectors simulated
35
+ % endif
36
+
11
37
  m.nu_skew_steps = ${model.get('num_skew_steps', 0)}
12
38
  m.magn_temp = ${model.get('magn_temp')}
13
39
  m.fc_mult_move_type = 1.0 -- Type of move path in air gap
14
- m.fc_force_points = 0.0 -- number move points in air gap
15
- m.phi_start = ${model.get('phi_start', 0)}
16
- m.range_phi = ${model.get('range_phi', 0)}
17
40
  m.nu_move_steps = ${model.get('num_move_steps', 49)}
18
41
 
19
42
  m.num_par_wdgs = ${model.get('num_par_wdgs',1)}