femagtools 1.7.6__py3-none-any.whl → 1.7.8__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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  """
4
4
  __title__ = 'femagtools'
5
- __version__ = '1.7.6'
5
+ __version__ = '1.7.8'
6
6
  __author__ = 'Ronald Tanner'
7
7
  __license__ = 'BSD'
8
8
  __copyright__ = 'Copyright 2023-2024 Gamma Technology'
femagtools/bch.py CHANGED
@@ -62,9 +62,12 @@ def sttylosses(losses):
62
62
  d['stteeth'] = losses[teeth]
63
63
  try:
64
64
  d['styoke_hyst'] = losses[yoke+'_hyst']
65
- d['stteeth_hyst'] = losses[teeth+'_hyst']
66
65
  d['styoke_eddy'] = losses[yoke+'_eddy']
66
+ d['stteeth_hyst'] = losses[teeth+'_hyst']
67
67
  d['stteeth_eddy'] = losses[teeth+'_eddy']
68
+ except KeyError:
69
+ pass
70
+ try:
68
71
  d['styoke_excess'] = losses[yoke+'_excess']
69
72
  d['stteeth_excess'] = losses[teeth+'_excess']
70
73
  except KeyError:
@@ -306,6 +309,13 @@ class Reader:
306
309
  'torque': tq.tolist()}]
307
310
  except (KeyError, IndexError):
308
311
  pass
312
+
313
+ # check number of phases
314
+ try:
315
+ if 'm' not in self.machine:
316
+ self.machine['m'] = len(self.current_angles)
317
+ except:
318
+ pass
309
319
 
310
320
  return self
311
321
 
@@ -640,7 +640,8 @@ def convert(dxfile,
640
640
  logger.warning("it's not possible to create fsl-file")
641
641
  return None
642
642
 
643
- fslrenderer = FslRenderer(basename)
643
+ mtype = 'EESM' if EESM else 'PMSM'
644
+ fslrenderer = FslRenderer(basename, mtype)
644
645
  inner = fslrenderer.render(machine_inner, inner=True)
645
646
  outer = fslrenderer.render(machine_outer, outer=True)
646
647
  if full_model:
@@ -652,14 +653,13 @@ def convert(dxfile,
652
653
  params.get('nodedist'))
653
654
 
654
655
  if params['external_rotor']:
655
- conv['fsl_magnet'] = outer
656
+ conv['fsl_rotor'] = outer
656
657
  conv['fsl_stator'] = inner
657
658
  else:
658
- conv['fsl_magnet'] = inner
659
+ conv['fsl_rotor'] = inner
659
660
  conv['fsl_stator'] = outer
660
661
 
661
662
  conv['fsl'] = fslrenderer.render_main(
662
- machine,
663
663
  machine_inner, machine_outer,
664
664
  inner, outer,
665
665
  params)
@@ -791,7 +791,8 @@ def convert(dxfile,
791
791
  logger.warning("it's not possible to create fsl-file")
792
792
  return None
793
793
 
794
- fslrenderer = FslRenderer(basename)
794
+ mtype = 'EESM' if EESM else 'PMSM'
795
+ fslrenderer = FslRenderer(basename, mtype)
795
796
  conv['fsl'] = fslrenderer.render(machine, inner, outer)
796
797
 
797
798
  if params is not None:
@@ -815,13 +816,18 @@ def create_femag_parameters(m_inner, m_outer, nodedist=1):
815
816
  parts_inner = int(m_inner.get_symmetry_part())
816
817
  parts_outer = int(m_outer.get_symmetry_part())
817
818
 
819
+ slot_area = 0
818
820
  if parts_inner > parts_outer:
821
+ from .area import TYPE_WINDINGS
822
+ slot_area = geom_inner.area_size_of_type(TYPE_WINDINGS)
819
823
  num_slots = int(parts_inner)
820
824
  num_poles = int(parts_outer)
821
825
  num_sl_gen = int(geom_inner.get_symmetry_copies()+1)
822
826
  alfa_slot = geom_inner.get_alfa()
823
827
  alfa_pole = geom_outer.get_alfa()
824
828
  else:
829
+ from .area import TYPE_WINDINGS
830
+ slot_area = geom_outer.area_size_of_type(TYPE_WINDINGS)
825
831
  num_slots = int(parts_outer)
826
832
  num_poles = int(parts_inner)
827
833
  num_sl_gen = int(geom_outer.get_symmetry_copies()+1)
@@ -829,6 +835,7 @@ def create_femag_parameters(m_inner, m_outer, nodedist=1):
829
835
  alfa_pole = geom_inner.get_alfa()
830
836
 
831
837
  params['tot_num_slot'] = num_slots
838
+ params['slot_area'] = slot_area
832
839
  params['num_sl_gen'] = num_sl_gen
833
840
  params['num_poles'] = num_poles
834
841
  params['nodedist'] = nodedist
@@ -866,6 +873,7 @@ def create_femag_parameters_stator(motor, position):
866
873
  else:
867
874
  params['dy1'] = 2*motor.geom.max_radius
868
875
  params['da1'] = 2*motor.geom.min_radius
876
+ params['slot_area'] = motor.slot_area()
869
877
  return params
870
878
 
871
879
 
@@ -879,4 +887,5 @@ def create_femag_parameters_rotor(motor, position):
879
887
  else:
880
888
  params['dy1'] = 2*motor.geom.max_radius
881
889
  params['da1'] = 2*motor.geom.min_radius
890
+ params['slot_area'] = motor.slot_area()
882
891
  return params
@@ -9,6 +9,7 @@
9
9
  """
10
10
  import numpy as np
11
11
  from .shape import Shape
12
+ from .area import TYPE_TOOTH, TYPE_YOKE
12
13
  import logging
13
14
  from .. import __version__
14
15
  logger = logging.getLogger(__name__)
@@ -52,12 +53,13 @@ def agndst(da1, da2, Q, p, nodedist=1):
52
53
  class FslRenderer(object):
53
54
  """a model that can created by FSL"""
54
55
 
55
- def __init__(self, name):
56
+ def __init__(self, name, mtype):
56
57
  self.model = name
57
58
  self.mirror_axis = None
58
59
  self.fm_nlin = None
59
60
  self.shaft = None
60
61
  self.agndst = 1
62
+ self.mtype = mtype
61
63
 
62
64
  def mirror_nodechains(self, p0, p1):
63
65
  self.mirror_axis = np.array((p0, p1)).ravel().tolist()
@@ -91,7 +93,7 @@ class FslRenderer(object):
91
93
  p2 = (center[0] + radius*np.cos(endangle),
92
94
  center[1] + radius*np.sin(endangle))
93
95
  self.content.append(
94
- u"nc_circle_m({}, {}, {}, {}, {}, {}, {})".format(
96
+ "nc_circle_m({}, {}, {}, {}, {}, {}, {})".format(
95
97
  p1[0], p1[1], p2[0], p2[1],
96
98
  center[0], center[1], num))
97
99
 
@@ -118,7 +120,7 @@ class FslRenderer(object):
118
120
  n0 = nodes[0]
119
121
  for n1 in nodes[1:]:
120
122
  self.content.append(
121
- u"nc_line({}, {}, {}, {}, {}) -- ellipse".format(
123
+ "nc_line({}, {}, {}, {}, {}) -- ellipse".format(
122
124
  n0[0], n0[1], n1[0], n1[1], 0))
123
125
  n0 = n1
124
126
 
@@ -134,12 +136,12 @@ class FslRenderer(object):
134
136
  return
135
137
  if e.has_attribute('auxline'):
136
138
  self.content.append(
137
- u"nc_line({}, {}, {}, {}, {}) -- auxiliary".format(
139
+ "nc_line({}, {}, {}, {}, {}) -- auxiliary".format(
138
140
  p1[0], p1[1], p2[0], p2[1], num))
139
141
  return
140
142
 
141
143
  self.content.append(
142
- u"nc_line({}, {}, {}, {}, {})".format(
144
+ "nc_line({}, {}, {}, {}, {})".format(
143
145
  p1[0], p1[1], p2[0], p2[1], num))
144
146
 
145
147
  def sorted_elements(self, geom, inner=False):
@@ -156,10 +158,21 @@ class FslRenderer(object):
156
158
  '''create fsl statements with nodechains'''
157
159
  machine.set_alfa_and_corners()
158
160
  geom = machine.geom
159
- geom.split_lines_longer_than(geom.max_radius/4)
161
+ split_len = (geom.max_radius - geom.min_radius) / 4
162
+ geom.split_all_lines_longer_than(split_len)
160
163
  self.content = []
161
164
 
162
- ndt_list = [(0.2, 1.5), (0.45, 2), (0.7, 3.0), (1.1, 3.0)]
165
+ ndt_list = [(0.00, 1.1),
166
+ (0.05, 1.5),
167
+ (0.10, 1.7),
168
+ (0.15, 2.0),
169
+ (0.20, 2.3),
170
+ (0.30, 2.7),
171
+ (0.40, 3.1),
172
+ (0.50, 3.5),
173
+ (0.70, 4.5),
174
+ (0.85, 5.5),
175
+ (1.10, 5.5)]
163
176
  dist = geom.max_radius - geom.min_radius
164
177
  el_sorted = self.sorted_elements(geom, inner)
165
178
 
@@ -184,6 +197,15 @@ class FslRenderer(object):
184
197
  '-- num_slots = {}'.format(
185
198
  machine.get_num_slots())
186
199
  ]
200
+ # fix stator subregions
201
+ sregs = [a.type for a in geom.list_of_areas()
202
+ if a.type in (TYPE_YOKE, TYPE_TOOTH)]
203
+ if len(sregs) > 0 and set(sregs) == {TYPE_TOOTH}:
204
+ for a in geom.list_of_areas():
205
+ if a.type == TYPE_TOOTH:
206
+ a.type = TYPE_YOKE
207
+ logger.debug("FIXED STATOR SUBREGION")
208
+
187
209
  self.content += ['-- min_radius = {}'.format(geom.min_radius),
188
210
  '-- max_radius = {}'.format(geom.max_radius),
189
211
  '-- min_corner = {}, {}'.format(
@@ -224,14 +246,16 @@ class FslRenderer(object):
224
246
  'outer_da_end = {}'.format(
225
247
  geom.dist_end_min_corner())
226
248
  ]
227
-
228
- self.content += ['\n',
249
+ if self.mtype == 'PMSM':
250
+ self.content += [
229
251
  'xmag = {}',
230
252
  'ymag = {}',
231
- 'mag_orient = {}',
253
+ 'mag_orient = {}']
254
+ self.content += ['\n',
232
255
  'mag_exists = 0',
233
256
  'if mcvkey_yoke == nil then',
234
257
  ' mcvkey_yoke = "dummy"',
258
+ ' ur = 1000.0',
235
259
  'end',
236
260
  'x0_iron_tooth, y0_iron_tooth = 0.0, 0.0',
237
261
  'x0_iron_yoke, y0_iron_yoke = 0.0, 0.0',
@@ -242,23 +266,27 @@ class FslRenderer(object):
242
266
  num_windings = 0
243
267
  num_magnets = 0
244
268
  magor = []
269
+
245
270
  for area in geom.list_of_areas():
246
271
  if area.number_of_elements() > 1:
247
272
  p = area.get_point_inside(geom)
248
273
  if p:
249
- self.content.append(u"x0, y0 = {}, {}".format(p[0], p[1]))
250
- # self.content.append(u"point(x0, y0, red, 4)") # for debugging
251
- self.content.append(u"create_mesh_se(x0, y0)")
274
+ self.content.append("x0, y0 = {}, {}".format(p[0], p[1]))
275
+ # self.content.append("point(x0, y0, red, 4)") # for debugging
276
+ self.content.append("create_mesh_se(x0, y0)")
252
277
 
253
278
  if area.is_winding():
254
279
  if area.type not in subregions:
255
280
  subregions[area.type] = 1
256
- num_windings += 1
257
- rmin, rmax = area.minmax_dist_from_center((0,0))
258
- self.content.append(
259
- f'rcoil_{num_windings} = {rmin}, {rmax}')
260
- self.content.append('m.xcoil_{}, m.ycoil_{} = x0, y0'.
261
- format(num_windings, num_windings))
281
+ if self.mtype == 'PMSM' or outer:
282
+ num_windings += 1
283
+ rmin, rmax = area.minmax_dist_from_center((0,0))
284
+ self.content.append(
285
+ f'rcoil_{num_windings} = {rmin}, {rmax}')
286
+ self.content.append('m.xcoil_{}, m.ycoil_{} = x0, y0'.
287
+ format(num_windings, num_windings))
288
+ else:
289
+ self.content.append('m.xcoil_r, m.ycoil_r = x0, y0')
262
290
 
263
291
  elif area.is_magnet():
264
292
  if area.type not in subregions:
@@ -296,10 +324,10 @@ class FslRenderer(object):
296
324
  self.content.append(
297
325
  'x0_shaft, y0_shaft = x0, y0')
298
326
 
299
- self.content.append(u"\n")
327
+ self.content.append("\n")
300
328
 
301
- txt = [u"if x0_iron_yoke > 0.0 then",
302
- u" if mcvkey_yoke ~= 'dummy' then",
329
+ txt = ["if x0_iron_yoke > 0.0 then",
330
+ " if mcvkey_yoke ~= 'dummy' then",
303
331
  ' def_mat_fm_nlin(x0_iron_yoke, y0_iron_yoke, "blue", mcvkey_yoke, 100)',
304
332
  ' else',
305
333
  ' def_mat_fm(x0_iron_yoke, y0_iron_yoke, ur, 100)',
@@ -307,21 +335,21 @@ class FslRenderer(object):
307
335
  'end\n']
308
336
  self.content.append('\n'.join(txt))
309
337
 
310
- txt = [u"if x0_iron_tooth > 0.0 then",
311
- u" if(x0_iron_yoke == 0 and mcvkey_yoke ~= 'dummy') then",
312
- u" def_mat_fm_nlin(x0_iron_tooth, y0_iron_tooth, 'blue', mcvkey_yoke, 100)",
313
- u" else",
314
- u" if (mcvkey_teeth ~= 'dummy' and mcvkey_teeth ~= nil) then",
315
- u" def_mat_fm_nlin(x0_iron_tooth, y0_iron_tooth, 'blue', mcvkey_teeth, 100)",
316
- u" else",
317
- u" def_mat_fm(x0_iron_tooth, y0_iron_tooth, ur, 100)",
318
- u" end",
319
- u" end",
338
+ txt = ["if x0_iron_tooth > 0.0 then",
339
+ " if(x0_iron_yoke == 0 and mcvkey_yoke ~= 'dummy') then",
340
+ " def_mat_fm_nlin(x0_iron_tooth, y0_iron_tooth, 'blue', mcvkey_yoke, 100)",
341
+ " else",
342
+ " if (mcvkey_teeth ~= 'dummy' and mcvkey_teeth ~= nil) then",
343
+ " def_mat_fm_nlin(x0_iron_tooth, y0_iron_tooth, 'blue', mcvkey_teeth, 100)",
344
+ " else",
345
+ " def_mat_fm(x0_iron_tooth, y0_iron_tooth, ur, 100)",
346
+ " end",
347
+ " end",
320
348
  'end\n']
321
349
  self.content.append('\n'.join(txt))
322
350
 
323
- txt = [u"if x0_shaft > 0.0 then",
324
- u" if mcvkey_shaft ~= 'dummy' then",
351
+ txt = ['if x0_shaft > 0.0 then',
352
+ " if mcvkey_shaft ~= 'dummy' then",
325
353
  ' def_mat_fm_nlin(x0_shaft, y0_shaft, "lightgrey", mcvkey_shaft, 100)',
326
354
  ' else',
327
355
  ' def_mat_fm(x0_shaft, y0_shaft, ur, 100)',
@@ -410,15 +438,15 @@ class FslRenderer(object):
410
438
  self.content.append('\nx0, y0 = {}, {}'. format(
411
439
  self.fm_nlin[0], self.fm_nlin[1]))
412
440
 
413
- mat = [u"if fm_nlin_mcvfile ~= 'dummy' then",
414
- u" if fm_nlin_mcvfile == 'air' then",
415
- u" def_mat_fm(x0,y0, 1.0, fm_nlin_rlen)",
416
- u" else",
417
- u" def_mat_fm_nlin(x0,y0, fm_nlin_colour, fm_nlin_mcvfile, fm_nlin_rlen)",
418
- u" end",
419
- u"else",
420
- u" def_mat_fm(x0,y0, 1000.0, fm_nlin_rlen)",
421
- u"end"]
441
+ mat = ["if fm_nlin_mcvfile ~= 'dummy' then",
442
+ " if fm_nlin_mcvfile == 'air' then",
443
+ " def_mat_fm(x0,y0, 1.0, fm_nlin_rlen)",
444
+ " else",
445
+ " def_mat_fm_nlin(x0,y0, fm_nlin_colour, fm_nlin_mcvfile, fm_nlin_rlen)",
446
+ " end",
447
+ "else",
448
+ " def_mat_fm(x0,y0, 1000.0, fm_nlin_rlen)",
449
+ "end"]
422
450
  self.content.append('\n'.join(mat))
423
451
 
424
452
  if self.shaft:
@@ -494,7 +522,6 @@ class FslRenderer(object):
494
522
  return self.content
495
523
 
496
524
  def render_main(self,
497
- motor,
498
525
  m_inner, m_outer,
499
526
  inner, outer,
500
527
  params):
@@ -514,6 +541,27 @@ class FslRenderer(object):
514
541
  m_outer.num_of_layers(),
515
542
  2)
516
543
  self.agndst = params.get('agndst', 0.1)
544
+ excwin = []
545
+ if self.mtype == 'EESM':
546
+ excwin = [
547
+ 'r,beta = c2pr(m.xcoil_r, m.ycoil_r)',
548
+ 'phi = math.pi/m.num_poles',
549
+ 'alpha = phi-beta',
550
+ 'm.num_wires = 1',
551
+ 'dir = {"wi", "wo"}',
552
+ 'xcoil, ycoil = pr2c(r, phi - alpha)',
553
+ 'def_new_wdg(xcoil, ycoil, "violet", "Exc", m.num_wires, 10.0, dir[1])',
554
+ 'xcoil, ycoil = pr2c(r, phi + alpha)',
555
+ 'add_to_wdg(xcoil, ycoil, wsamekey, dir[2], "wser")',
556
+ 'for i = 2, m.npols_gen do',
557
+ ' n = (i+1) % 2 + 1',
558
+ ' phi = phi + 2*math.pi/m.num_poles',
559
+ ' xcoil, ycoil = pr2c(r, phi - alpha)',
560
+ ' add_to_wdg(xcoil, ycoil, wsamekey, dir[n], "wser")',
561
+ ' xcoil, ycoil = pr2c(r, phi + alpha)',
562
+ ' n = i % 2 + 1',
563
+ ' add_to_wdg(xcoil, ycoil, wsamekey, dir[n], "wser")',
564
+ 'end']
517
565
  return [
518
566
  '-- generated from DXF by femagtools {}'.format(__version__),
519
567
  'exit_on_error = false',
@@ -601,4 +649,4 @@ class FslRenderer(object):
601
649
  ' pre_models("Gen_winding")',
602
650
  ' pre_models("gen_pocfile")',
603
651
  'end\n',
604
- 'save_model(cont)\n']
652
+ 'save_model(cont)\n'] + excwin
@@ -435,6 +435,14 @@ def points_on_arc(center, radius, startangle, endangle, parts=8):
435
435
  center[1] + radius * np.sin(alpha))
436
436
 
437
437
 
438
+ def points_on_line(p1, p2, parts=2):
439
+ x_dist = (p2[0] - p1[0]) / parts
440
+ y_dist = (p2[1] - p1[1]) / parts
441
+ x, y = p1
442
+ for i in range(1, parts):
443
+ yield (x + i*x_dist, y + i*y_dist)
444
+
445
+
438
446
  class Timer(object):
439
447
  def __init__(self, start_it=False):
440
448
  self.starttime = None
femagtools/dxfsl/geom.py CHANGED
@@ -34,7 +34,7 @@ from .functions import middle_point_of_line, middle_point_of_arc
34
34
  from .functions import middle_angle, positive_angle
35
35
  from .functions import normalise_angle, is_same_angle
36
36
  from .functions import part_of_circle, gcd
37
- from .functions import point_on_arc, nodes_are_equal
37
+ from .functions import point_on_arc, points_on_line, nodes_are_equal
38
38
  from .functions import area_size
39
39
  import io
40
40
  import time
@@ -749,6 +749,38 @@ class Geometry(object):
749
749
  rtol=self.rtol,
750
750
  atol=self.atol)
751
751
 
752
+ def split_all_lines_longer_than(self, length):
753
+ """split lines longer than length"""
754
+ new_lines = []
755
+ rem_lines = []
756
+ elist = self.elements_and_nodes(Line)
757
+
758
+ for p1, p2, line in elist:
759
+ line_len = line.length()
760
+ if line_len < length:
761
+ continue
762
+ d = abs(distance(self.center, p1) - distance(self.center, p2))
763
+ parts = 3
764
+ if d > line_len / 2:
765
+ parts = 5
766
+ elif d > line_len / 3:
767
+ parts = 4
768
+ p_start = p1
769
+
770
+ for p_next in points_on_line(p1, p2, parts=parts):
771
+ new_lines.append(Line(Element(start=p_start, end=p_next)))
772
+ p_start = p_next
773
+
774
+ new_lines.append(Line(Element(start=p_start, end=p2)))
775
+ rem_lines.append((p1, p2))
776
+
777
+ for p1, p2 in rem_lines:
778
+ self._remove_edge(p1, p2)
779
+ for new_line in new_lines:
780
+ self.add_element(new_line,
781
+ rtol=self.rtol,
782
+ atol=self.atol)
783
+
752
784
  def circles(self):
753
785
  """return list of circle nodes"""
754
786
  return [n[1]['object'] for n in self.g.nodes(data=True)
@@ -3574,6 +3606,10 @@ class Geometry(object):
3574
3606
  return len([area for area in self.list_of_areas()
3575
3607
  if area.is_type(type)])
3576
3608
 
3609
+ def area_size_of_type(self, type):
3610
+ return sum([area.surface for area in self.list_of_areas()
3611
+ if area.is_type(type)])*1e-3
3612
+
3577
3613
  def num_of_windings(self):
3578
3614
  return self.num_areas_of_type(AREA.TYPE_WINDINGS)
3579
3615
 
@@ -590,6 +590,10 @@ class Machine(object):
590
590
  return w*2
591
591
  return w
592
592
 
593
+ def slot_area(self):
594
+ from .area import TYPE_WINDINGS
595
+ return self.geom.area_size_of_type(TYPE_WINDINGS)
596
+
593
597
  def find_symmetry(self, sym_tolerance, is_inner, is_outer, plt):
594
598
  logger.debug("begin of find_symmetry")
595
599
  if self.radius <= 0.0:
femagtools/femag.py CHANGED
@@ -366,9 +366,9 @@ class BaseFemag(object):
366
366
  with open(os.path.join(self.workdir, 'FEMAG-FSL.log')) as f:
367
367
  for l in f:
368
368
  if l.startswith('New model') or l.startswith('Load model'):
369
- model = l.split('"')[1]
370
- break
371
- return model
369
+ return l.split('"')[1]
370
+
371
+ raise ValueError(f"Model not found in {self.workdir}/'FEMAG-FSL.log'")
372
372
 
373
373
  def readResult(self, simulation, bch=None):
374
374
  if simulation: