femagtools 1.7.8__py3-none-any.whl → 1.8.0__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 (54) hide show
  1. femagtools/__init__.py +1 -1
  2. femagtools/amela.py +2 -2
  3. femagtools/dxfsl/area.py +142 -9
  4. femagtools/dxfsl/conv.py +2 -9
  5. femagtools/dxfsl/converter.py +33 -9
  6. femagtools/dxfsl/fslrenderer.py +13 -12
  7. femagtools/dxfsl/geom.py +39 -6
  8. femagtools/dxfsl/journal.py +2 -2
  9. femagtools/dxfsl/machine.py +14 -13
  10. femagtools/dxfsl/shape.py +3 -0
  11. femagtools/dxfsl/svgparser.py +31 -4
  12. femagtools/ecloss.py +381 -2
  13. femagtools/femag.py +55 -0
  14. femagtools/fsl.py +74 -47
  15. femagtools/isa7.py +41 -0
  16. femagtools/job.py +2 -2
  17. femagtools/machine/afpm.py +5 -1
  18. femagtools/machine/pm.py +1 -1
  19. femagtools/machine/sm.py +14 -0
  20. femagtools/machine/utils.py +4 -3
  21. femagtools/mcv.py +128 -124
  22. femagtools/me.py +13 -13
  23. femagtools/model.py +14 -1
  24. femagtools/moo/population.py +9 -7
  25. femagtools/nc.py +12 -0
  26. femagtools/plot/__init__.py +1 -1
  27. femagtools/plot/fieldlines.py +1 -1
  28. femagtools/plot/mcv.py +18 -0
  29. femagtools/plot/nc.py +22 -5
  30. femagtools/plot/wdg.py +40 -7
  31. femagtools/svgfsl/converter.py +6 -0
  32. femagtools/templates/gen_hairpin_winding.mako +36 -45
  33. femagtools/templates/gen_winding.mako +7 -0
  34. femagtools/templates/magnetIron.mako +30 -46
  35. femagtools/templates/magnetIron2.mako +39 -0
  36. femagtools/templates/magnetIron3.mako +39 -0
  37. femagtools/templates/magnetIron4.mako +39 -0
  38. femagtools/templates/magnetIron5.mako +39 -0
  39. femagtools/templates/magnetIronV.mako +34 -54
  40. femagtools/templates/magnetSector.mako +32 -47
  41. femagtools/templates/mesh-airgap.mako +12 -6
  42. femagtools/templates/prepare_thermal.mako +354 -0
  43. femagtools/templates/statorRotor3.mako +3 -22
  44. femagtools/windings.py +92 -59
  45. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/METADATA +20 -18
  46. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/RECORD +53 -53
  47. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/WHEEL +1 -1
  48. tests/test_fsl.py +1 -1
  49. tests/test_mcv.py +106 -1
  50. tests/test_windings.py +18 -2
  51. tests/test_mcvwriter.py +0 -96
  52. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/LICENSE +0 -0
  53. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/entry_points.txt +0 -0
  54. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/top_level.txt +0 -0
femagtools/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  """
4
4
  __title__ = 'femagtools'
5
- __version__ = '1.7.8'
5
+ __version__ = '1.8.0'
6
6
  __author__ = 'Ronald Tanner'
7
7
  __license__ = 'BSD'
8
8
  __copyright__ = 'Copyright 2023-2024 Gamma Technology'
femagtools/amela.py CHANGED
@@ -200,10 +200,10 @@ class Amela():
200
200
  for k, i in enumerate(mag_spels):
201
201
 
202
202
  cond = i.conduc
203
- if cond == 0:
203
+ if cond == 0:
204
204
  cond = 625000
205
205
  logger.info('Magnet conductivity equals 0, using 625000 S/m')
206
-
206
+
207
207
  mur = np.abs(1/i.elements[0].reluc[0])
208
208
  logger.debug('Magnet: mur=%s, conductivity=%s', mur, cond)
209
209
 
femagtools/dxfsl/area.py CHANGED
@@ -171,6 +171,142 @@ class Area(object):
171
171
  if e1.n1 == e2.n1 and e1.n2 == e2.n2:
172
172
  yield e1
173
173
 
174
+ def reduce_line_nodes(self, geom, mindist=0.01):
175
+ """reduces number of nodes (lines only)
176
+ https://rdp.readthedocs.io/en/latest
177
+ Note: this feature is deactivated silently
178
+ if the rdp package is not installed.
179
+ """
180
+ try:
181
+ import rdp
182
+ except ModuleNotFoundError:
183
+ return 0
184
+
185
+ def is_valid_line_(n1, e):
186
+ if not isinstance(e, Line):
187
+ return False
188
+ if e.has_attribute('del'):
189
+ return False
190
+ return True
191
+
192
+ def reduce_nodes_(lines, mindist):
193
+ if not len(lines) > 1:
194
+ return 0
195
+
196
+ nodes = [n1 for n1, n2, e in lines]
197
+ n1, n2, e = lines[-1]
198
+ nodes.append(n2)
199
+
200
+ remaining_nodes = rdp.rdp(nodes,
201
+ epsilon=mindist)
202
+ nodes_deleted = len(nodes) - len(remaining_nodes)
203
+ if not nodes_deleted:
204
+ return 0
205
+
206
+ for n1, n2, e in lines:
207
+ e.set_attribute('del')
208
+ e.set_my_color('yellow')
209
+ geom.remove_edge(e)
210
+
211
+ n1 = remaining_nodes[0]
212
+ for n2 in remaining_nodes[1:]:
213
+ geom.add_line(n1, n2)
214
+ n1 = n2
215
+
216
+ self.area = []
217
+ return nodes_deleted
218
+
219
+ # -----
220
+ nodes_deleted = 0
221
+ lines = []
222
+ for n1, n2, e in self.list_of_elements():
223
+ if not is_valid_line_(n1, e):
224
+ nodes_deleted += reduce_nodes_(lines, mindist)
225
+ lines = []
226
+ elif not geom.num_of_neighbors(n1) == 2:
227
+ nodes_deleted += reduce_nodes_(lines, mindist)
228
+ lines = [(n1, n2, e)]
229
+ else:
230
+ lines.append((n1, n2, e))
231
+
232
+ nodes_deleted += reduce_nodes_(lines, mindist)
233
+ return nodes_deleted
234
+
235
+ def reduce_element_nodes(self, geom, mindist=0.01):
236
+ """reduces number of nodes (lines only)
237
+ https://rdp.readthedocs.io/en/latest
238
+ Note: this feature is deactivated silently
239
+ if the rdp package is not installed.
240
+ """
241
+ corners = geom.start_corners + geom.end_corners
242
+
243
+ try:
244
+ import rdp
245
+ except ModuleNotFoundError:
246
+ return 0
247
+
248
+ def is_valid_element_(n1, e):
249
+ return not e.has_attribute('del')
250
+
251
+ def reduce_nodes_(elements, mindist):
252
+ if not len(elements) > 1:
253
+ return 0
254
+
255
+ old_nodes = []
256
+ for n1, n2, e in elements:
257
+ old_nodes.append(n1)
258
+ nodes = [n for n in e.get_nodes(parts=24)]
259
+ if len(nodes) > 2:
260
+ if points_are_close(n1, nodes[0]):
261
+ old_nodes += nodes[1:-1]
262
+ elif points_are_close(n2, nodes[0]):
263
+ nodes.reverse()
264
+ old_nodes += nodes[1:-1]
265
+ n1, n2, e = elmts[-1]
266
+ old_nodes.append(n2)
267
+
268
+ new_nodes = rdp.rdp(old_nodes,
269
+ epsilon=mindist)
270
+ nodes_deleted = len(old_nodes) - len(new_nodes)
271
+ if not nodes_deleted:
272
+ return 0
273
+
274
+ for n1, n2, e in elements:
275
+ e.set_attribute('del')
276
+ e.set_my_color('yellow')
277
+ geom.remove_edge(e)
278
+
279
+ n1 = new_nodes[0]
280
+ for n2 in new_nodes[1:]:
281
+ geom.add_line(n1, n2)
282
+ n1 = n2
283
+
284
+ self.area = []
285
+ return nodes_deleted
286
+
287
+ # -----
288
+ nodes_deleted = 0
289
+ tiny_mindist = 0.05
290
+ elmts = []
291
+ has_tiny = False
292
+ for n1, n2, e in self.list_of_elements():
293
+ if not is_valid_element_(n1, e):
294
+ if has_tiny:
295
+ nodes_deleted += reduce_nodes_(elmts, mindist)
296
+ elmts = []
297
+ elif not geom.num_of_neighbors(n1) == 2 or n1 in corners:
298
+ if has_tiny:
299
+ nodes_deleted += reduce_nodes_(elmts, mindist)
300
+ has_tiny = e.is_tiny(tiny_mindist)
301
+ elmts = [(n1, n2, e)]
302
+ else:
303
+ if e.is_tiny(tiny_mindist):
304
+ has_tiny = True
305
+ elmts.append((n1, n2, e))
306
+ if has_tiny:
307
+ nodes_deleted += reduce_nodes_(elmts, mindist)
308
+ return nodes_deleted
309
+
174
310
  def virtual_nodes(self, render=False, parts=64):
175
311
  if len(self.area) < 2:
176
312
  return
@@ -337,15 +473,9 @@ class Area(object):
337
473
  return False
338
474
 
339
475
  def minmax_dist_from_center(self, center):
340
- rmin = 1e20
341
- rmax = 0
342
- for n in self.list_of_nodes():
343
- r = np.linalg.norm(np.array(n)-center)
344
- if r < rmin:
345
- rmin = r
346
- if r > rmax:
347
- rmax = r
348
- return rmin, rmax
476
+ nodes = np.array([n for e in self.area for n in e.get_nodes()])
477
+ return (np.min(np.linalg.norm(nodes-center, axis=1)),
478
+ np.max(np.linalg.norm(nodes-center, axis=1)))
349
479
 
350
480
  def minmax_angle_dist_from_center(self, center, dist):
351
481
  circ = Circle(Element(center=center, radius=dist))
@@ -959,11 +1089,14 @@ class Area(object):
959
1089
  self.legend())
960
1090
 
961
1091
  def remove_edges(self, g, ndec):
1092
+ r = 0
962
1093
  for e in self.area:
963
1094
  try:
964
1095
  g.remove_edge(e.node1(ndec), e.node2(ndec))
1096
+ r+=1
965
1097
  except Exception:
966
1098
  continue
1099
+ return r
967
1100
 
968
1101
  def is_circle(self):
969
1102
  e = self.area[0]
femagtools/dxfsl/conv.py CHANGED
@@ -9,18 +9,13 @@ import os
9
9
  import io
10
10
  import femagtools
11
11
  from femagtools.dxfsl.converter import convert
12
- from femagtools.dxfsl.journal import Journal, getJournal
13
12
  import argparse
14
13
  import logging
15
14
  import logging.config
16
15
 
17
16
  logger = logging.getLogger(__name__)
18
- journal = None
19
-
20
17
 
21
18
  def main():
22
- global journal
23
-
24
19
  argparser = argparse.ArgumentParser(
25
20
  description='Process DXF file and create a plot or FSL file.')
26
21
  argparser.add_argument('dxfile',
@@ -187,8 +182,6 @@ def main():
187
182
  logger.info("Python: %s", sys.version)
188
183
  sys.exit(0)
189
184
 
190
- journal = getJournal(name='converter_journal', aktiv=args.journal)
191
-
192
185
  if args.airgap > 0.0:
193
186
  if args.airgap2 > 0.0:
194
187
  logger.info("Airgap is set from {} to {}"
@@ -252,7 +245,8 @@ def main():
252
245
  write_png=args.write_png,
253
246
  write_id=args.write_id,
254
247
  debug_mode=args.debugger,
255
- full_model=args.full_model)
248
+ full_model=args.full_model,
249
+ write_journal=args.journal)
256
250
  keys = ('tot_num_slot', 'num_sl_gen', 'num_poles', 'nodedist',
257
251
  'dy1', 'da1', 'da2', 'dy2', 'agndst', 'name')
258
252
  logger.info("%s", {k: res[k] for k in keys if k in res})
@@ -261,7 +255,6 @@ def main():
261
255
  basename = os.path.basename(args.dxfile).split('.')[0]
262
256
  with io.open(basename + '.fsl', 'w', encoding='utf-8') as f:
263
257
  f.write('\n'.join(res['fsl']))
264
- journal.write_journal()
265
258
 
266
259
  if __name__ == "__main__":
267
260
  loglevel = logging.INFO
@@ -3,6 +3,7 @@
3
3
  """
4
4
  import os
5
5
  from pathlib import Path
6
+ from femagtools import __version__
6
7
  from femagtools.dxfsl.geom import Geometry
7
8
  from femagtools.dxfsl.shape import Shape
8
9
  from femagtools.dxfsl.fslrenderer import FslRenderer, agndst
@@ -220,16 +221,33 @@ def build_machine_stator(machine, inner, mindist, plt, EESM=False, single=False)
220
221
  if not machine.geom.is_stator():
221
222
  logger.debug("Rotor with windings")
222
223
 
223
- if machine.has_mirrored_windings():
224
+ if machine.is_mirrored():
225
+ plot_geom(False, # for developer
226
+ plt, machine.previous_machine.geom,
227
+ title="Mirrored Stator",
228
+ areas=True)
229
+
224
230
  logger.debug("undo mirrored windings")
225
231
  machine_temp = machine.undo_mirror()
226
232
  machine_temp.delete_tiny_elements(mindist)
227
233
  machine_temp.geom.set_stator()
228
234
  machine_temp.search_stator_subregions(single=single)
229
- machine_temp.create_mirror_lines_outside_windings()
235
+ if not machine_temp.has_windings_in_the_middle():
236
+ logger.debug("Back to the mirrored machine")
237
+ machine_temp = machine # undo
238
+ else:
239
+ machine_temp.create_mirror_lines_outside_windings()
230
240
  else:
231
241
  machine_temp = machine
232
- if machine_temp.create_auxiliary_lines():
242
+
243
+ rebuild = machine_temp.create_auxiliary_lines()
244
+ if machine_temp.geom.reduce_element_nodes(mindist):
245
+ plot_geom(False, # for developer
246
+ plt, machine_temp.geom,
247
+ title="Nodes reduced",
248
+ areas=False)
249
+ rebuild = True
250
+ if rebuild:
233
251
  machine_temp.rebuild_subregions(EESM, single=single)
234
252
 
235
253
  if inner:
@@ -270,7 +288,8 @@ def convert(dxfile,
270
288
  write_png=False,
271
289
  write_id=False,
272
290
  full_model=False,
273
- debug_mode=False):
291
+ debug_mode=False,
292
+ write_journal=False):
274
293
  layers = ()
275
294
  conv = {}
276
295
 
@@ -281,14 +300,19 @@ def convert(dxfile,
281
300
 
282
301
  basename = input_file.stem
283
302
  if part:
284
- logger.info("***** start processing %s (%s) *****", basename, part)
303
+ logger.info("***** start processing %s (%s) [%s] *****",
304
+ basename,
305
+ part,
306
+ __version__)
285
307
  else:
286
- logger.info("***** start processing %s *****", basename)
308
+ logger.info("***** start processing %s [%s] *****",
309
+ basename,
310
+ __version__)
287
311
  timer = Timer(start_it=True)
288
312
 
289
- journal = getJournal(name='converter', aktiv=debug_mode)
313
+ journal = getJournal(name='converter_journal', aktiv=write_journal)
290
314
  journal.get_journal(input_file.name)
291
- journal.put_filename(str(input_file.resolve()))
315
+ journal.set_filename(str(input_file.resolve()))
292
316
  journal.set('success', False)
293
317
  journal.write_journal()
294
318
 
@@ -718,7 +742,6 @@ def convert(dxfile,
718
742
  p,
719
743
  EESM=EESM,
720
744
  single=True)
721
-
722
745
  params = create_femag_parameters_stator(machine,
723
746
  part[1])
724
747
  else:
@@ -801,6 +824,7 @@ def convert(dxfile,
801
824
  t = timer.stop("-- all done in %0.4f seconds --", info=True)
802
825
  journal.put('time_total', t)
803
826
  journal.set('success', True)
827
+ journal.write_journal()
804
828
  return conv
805
829
 
806
830
 
@@ -162,17 +162,16 @@ class FslRenderer(object):
162
162
  geom.split_all_lines_longer_than(split_len)
163
163
  self.content = []
164
164
 
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)]
165
+ MAXDST=4.0
166
+ NUMLEVELS=10
167
+ NDT0=1.1
168
+ # ndt list format [ (rdistx, ndtx) ...]
169
+ # where
170
+ # - rdistx is rel dist from airgap (range 0 .. NUMLEVELS-1/NUMLEVELS)
171
+ # - ndtx nodedist (range NDT0 .. (NUMLEVELS-1/NUMLEVELS))*(MAXDST-1.1) + NDT0)
172
+ ndt_list = [(1.1*nl/NUMLEVELS, nl/NUMLEVELS*(MAXDST-1.1)+NDT0)
173
+ for nl in range(NUMLEVELS+1)]
174
+
176
175
  dist = geom.max_radius - geom.min_radius
177
176
  el_sorted = self.sorted_elements(geom, inner)
178
177
 
@@ -182,7 +181,7 @@ class FslRenderer(object):
182
181
  if ndt_list[n][0] < d_percent:
183
182
  self.agndst = ndt_list[n][1] * self.agndst
184
183
  self.content.append('\nndt({}*agndst)\n'.
185
- format(ndt_list[n][1]))
184
+ format(round(ndt_list[n][1], 1)))
186
185
  while ndt_list[n][0] < d_percent:
187
186
  n += 1
188
187
  e.render(self)
@@ -233,6 +232,8 @@ class FslRenderer(object):
233
232
  'r, phi = c2pr(x0, y0)',
234
233
  'x1, y1 = pr2c(r1, phi)',
235
234
  'x2, y2 = pr2c(r1, {}*math.pi/parts)'.format(slice),
235
+ f'-- end max corner {geom.end_corners[-1]}',
236
+ f'-- center {geom.center}',
236
237
  f'r = {geom.dist_end_max_corner()}',
237
238
  'x3, y3 = pr2c(r, {}*math.pi/parts)'.format(slice),
238
239
  'nc_line(x0, y0, x1, y1, 0)',
femagtools/dxfsl/geom.py CHANGED
@@ -91,11 +91,12 @@ def create_geometry(new_elements, split=False):
91
91
 
92
92
 
93
93
  def intersect_and_split(inp_elements, rtol, atol):
94
- logger.info("Load input elements ... ")
94
+ logger.info("Load input elements ... %s", len(inp_elements))
95
95
  out_elements = []
96
96
  for e in inp_elements:
97
97
  out_size = len(out_elements)
98
98
  intersect_and_split_element(e, out_elements, 0, out_size, rtol, atol)
99
+ logger.debug(" done", e)
99
100
  return out_elements
100
101
 
101
102
 
@@ -650,9 +651,9 @@ class Geometry(object):
650
651
  return True
651
652
  return False
652
653
 
653
- def get_edge(self, eg):
654
+ def get_edge(self, obj):
654
655
  return [[e[0], e[1], e[2]['object']] for e in self.g.edges(data=True)
655
- if e[2]['object'] is eg]
656
+ if e[2]['object'] is obj]
656
657
 
657
658
  def get_edge_element(self, n1, n2):
658
659
  e_dict = self.g.get_edge_data(n1, n2)
@@ -679,9 +680,9 @@ class Geometry(object):
679
680
  assert(len(e) == 1)
680
681
  self._remove_edge(e[0][0], e[0][1])
681
682
 
682
- def remove_edges(self, edges):
683
- for e in edges:
684
- self.remove_edge(e)
683
+ def remove_edges(self, objs):
684
+ for o in objs:
685
+ self.remove_edge(o)
685
686
 
686
687
  def _remove_node(self, n):
687
688
  for nbr in self.g.neighbors(n):
@@ -810,6 +811,10 @@ class Geometry(object):
810
811
  def get_neighbors(self, n):
811
812
  return [nbr for nbr in self.g.neighbors(n)]
812
813
 
814
+ def num_of_neighbors(self, n):
815
+ nbrs = [nbr for nbr in self.g.neighbors(n)]
816
+ return len(nbrs)
817
+
813
818
  def angle_nodes(self, center, angle, rtol, atol):
814
819
  if np.isclose(abs(angle), np.pi, rtol, atol):
815
820
  angle_func = positive_angle
@@ -1843,6 +1848,24 @@ class Geometry(object):
1843
1848
  return [h for (k, h) in legend.items()]
1844
1849
  return []
1845
1850
 
1851
+ def reduce_winding_nodes(self, mindist=0.01):
1852
+ return self.reduce_element_nodes(mindist=mindist,
1853
+ area_types=(AREA.TYPE_WINDINGS,))
1854
+
1855
+ def reduce_element_nodes(self, mindist=0.01, area_types=()):
1856
+ timer = Timer(start_it=True)
1857
+ nodes_deleted = 0
1858
+ for area in self.list_of_areas():
1859
+ if not area_types or area.type in area_types:
1860
+ nodes_deleted += area.reduce_element_nodes(self, mindist)
1861
+
1862
+ t = timer.stop("-- {} nodes deleted in %0.4f seconds --".format(nodes_deleted))
1863
+ self.journal.put('time_deleting_nodes', t)
1864
+ if nodes_deleted:
1865
+ self.journal.put('nodes_deleted', nodes_deleted)
1866
+ self.area_list = []
1867
+ return nodes_deleted > 0
1868
+
1846
1869
  def render_areagroups(self, renderer):
1847
1870
  if not self.areagroup_list:
1848
1871
  return
@@ -3587,6 +3610,16 @@ class Geometry(object):
3587
3610
  return True
3588
3611
  return False
3589
3612
 
3613
+ def windings_in_the_middle(self, midangle):
3614
+ wdg_areas = [a for a in self.list_of_areas()
3615
+ if a.is_winding()]
3616
+ logger.info("%s windings in geom", len(wdg_areas))
3617
+ for a in wdg_areas:
3618
+ if greater(a.max_angle, midangle) and \
3619
+ less(a.min_angle, midangle):
3620
+ return True
3621
+ return False
3622
+
3590
3623
  def looking_for_corners(self):
3591
3624
  if self.is_inner:
3592
3625
  logger.debug("looking_for_corners: inner")
@@ -121,8 +121,8 @@ class Journal(object):
121
121
  data_list.append(val)
122
122
  self.data[name] = data_list
123
123
 
124
- def put_filename(self, val):
125
- self.put('filename', val)
124
+ def set_filename(self, val):
125
+ self.set('filename', val)
126
126
 
127
127
  def put_areas(self, val):
128
128
  self.put('areas', val)
@@ -5,14 +5,15 @@
5
5
  from __future__ import print_function
6
6
  import numpy as np
7
7
  import logging
8
- from .shape import Element, Circle, Arc, Line, Shape
9
- from .corner import Corner
8
+ from femagtools.dxfsl.shape import Element, Circle, Arc, Line, Shape
9
+ from femagtools.dxfsl.corner import Corner
10
10
  from femagtools.dxfsl.symmetry import Symmetry
11
- from .functions import point, points_are_close, distance
12
- from .functions import alpha_angle, normalise_angle, middle_angle, third_angle
13
- from .functions import alpha_line, line_m, line_n, mirror_point
14
- from .functions import within_interval, part_of_circle
15
- from .functions import less, less_equal, greater, greater_equal
11
+ import femagtools.dxfsl.area as AREA
12
+ from femagtools.dxfsl.functions import point, points_are_close, distance
13
+ from femagtools.dxfsl.functions import alpha_angle, normalise_angle, middle_angle, third_angle
14
+ from femagtools.dxfsl.functions import alpha_line, line_m, line_n, mirror_point
15
+ from femagtools.dxfsl.functions import within_interval, part_of_circle
16
+ from femagtools.dxfsl.functions import less, less_equal, greater, greater_equal
16
17
  logger = logging.getLogger('femagtools.geom')
17
18
 
18
19
 
@@ -283,11 +284,6 @@ class Machine(object):
283
284
  logger.debug("end of copy_mirror")
284
285
  return machine
285
286
 
286
- def has_mirrored_windings(self):
287
- if not self.is_mirrored():
288
- return False
289
- return self.geom.area_close_to_endangle(2) > 0
290
-
291
287
  def undo_mirror(self):
292
288
  assert(self.is_mirrored())
293
289
  assert(self.previous_machine)
@@ -533,7 +529,7 @@ class Machine(object):
533
529
  def repair_hull_geom(self, geom, startangle, endangle):
534
530
  logger.debug('begin repair_hull_geom (%s, %s)', startangle, endangle)
535
531
 
536
- rtol = 1e-4
532
+ rtol = 1e-3
537
533
  atol = 1e-4
538
534
  c_corner = Corner(self.center, self.center)
539
535
  start_c_added, start_corners = geom.get_corner_list(self.center, startangle,
@@ -1138,6 +1134,11 @@ class Machine(object):
1138
1134
 
1139
1135
  return 0.0
1140
1136
 
1137
+ def has_windings_in_the_middle(self):
1138
+ midangle = middle_angle(self.startangle,
1139
+ self.endangle)
1140
+ return self.geom.windings_in_the_middle(midangle)
1141
+
1141
1142
  def create_mirror_lines_outside_windings(self):
1142
1143
  logger.debug("create_mirror_lines_outside_windings")
1143
1144
 
femagtools/dxfsl/shape.py CHANGED
@@ -54,6 +54,9 @@ class Shape(object):
54
54
  def classname(self):
55
55
  return "Shape"
56
56
 
57
+ def set_my_color(self, color):
58
+ self.my_color = color
59
+
57
60
  def get_my_color(self):
58
61
  if hasattr(self, 'my_color'):
59
62
  return self.my_color
@@ -37,10 +37,17 @@ def get_angles(sweep, center, p1, p2):
37
37
  def get_shapes(path):
38
38
  """return list of node elements (A, L)"""
39
39
  state = ''
40
+ prevstate = ''
40
41
  p = []
41
- for s in [s for s in re.split('([AML])|,|\\s+',path) if s]:
42
+ for s in [s for s in re.split('([AMLHV])|,|\\s+', path) if s]:
42
43
  if state == '':
43
- state = s[0]
44
+ s = s.upper()
45
+ if s in ('A','M','L','H','V'):
46
+ state = s
47
+ prevstate = s
48
+ else: # wild guess
49
+ p.append(float(s))
50
+ state = prevstate
44
51
  elif state == 'M':
45
52
  p.append(float(s))
46
53
  if len(p) == 2:
@@ -52,7 +59,7 @@ def get_shapes(path):
52
59
  if len(p) == 2:
53
60
  p2 = np.array(p)
54
61
  logger.debug("Line %s -> %s",
55
- p1, p2)
62
+ p1, p2)
56
63
  yield Line(Element(start=p1, end=p2))
57
64
  p1 = p2.copy()
58
65
  p = []
@@ -66,7 +73,7 @@ def get_shapes(path):
66
73
  center = get_center(r, p1, p2, sweep)
67
74
  start, end = get_angles(sweep, center, p1, p2)
68
75
  logger.debug("Arc center %s r %f %f -> %f",
69
- center, r, start, end)
76
+ center, r, start, end)
70
77
  yield Arc(Element(center=center,
71
78
  radius=r,
72
79
  start_angle=start*180/np.pi,
@@ -74,14 +81,34 @@ def get_shapes(path):
74
81
  p1 = p2.copy()
75
82
  p = []
76
83
  state = ''
84
+ elif state == 'H':
85
+ logger.debug("h %s", s)
86
+ p2 = np.array((float(s), 0))
87
+ yield Line(Element(start=p1, end=p2))
88
+ p1 = p2.copy()
89
+ p = []
90
+ state = ''
91
+ elif state == 'V':
92
+ logger.debug("V %s", s)
93
+ p2 = np.array((0, float(s)))
94
+ yield Line(Element(start=p1, end=p2))
95
+ p1 = p2.copy()
96
+ p = []
97
+ state = ''
77
98
  else:
78
99
  raise ValueError(f"unsupported path {state}")
79
100
 
80
101
 
81
102
  def svgshapes(svgfile):
82
103
  svg = ET.parse(svgfile)
104
+ bcolor = re.compile('fill:([^;]+)')
105
+ sr = 0
83
106
  for p in svg.findall(".//{http://www.w3.org/2000/svg}path"):
107
+ m = bcolor.search(p.get('style'))
108
+ if m:
109
+ logger.info("subregion %d: %s", sr, m.groups()[0])
84
110
  yield from get_shapes(p.get('d'))
111
+ sr += 1
85
112
  for p in svg.findall(".//{http://www.w3.org/2000/svg}line"):
86
113
  yield Line(Element(start=[float(p.get('x1')), float(p.get('y1'))],
87
114
  end=[float(p.get('x2')), float(p.get('y2'))]))