femagtools 1.8.1__py3-none-any.whl → 1.8.3__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 (49) hide show
  1. femagtools/__init__.py +1 -1
  2. femagtools/dxfsl/area.py +110 -1
  3. femagtools/dxfsl/areabuilder.py +93 -45
  4. femagtools/dxfsl/conv.py +5 -0
  5. femagtools/dxfsl/converter.py +85 -27
  6. femagtools/dxfsl/fslrenderer.py +5 -4
  7. femagtools/dxfsl/functions.py +14 -6
  8. femagtools/dxfsl/geom.py +135 -149
  9. femagtools/dxfsl/journal.py +1 -1
  10. femagtools/dxfsl/machine.py +161 -9
  11. femagtools/dxfsl/shape.py +46 -1
  12. femagtools/dxfsl/svgparser.py +1 -1
  13. femagtools/dxfsl/symmetry.py +143 -38
  14. femagtools/femag.py +64 -61
  15. femagtools/fsl.py +15 -12
  16. femagtools/isa7.py +3 -2
  17. femagtools/machine/__init__.py +5 -4
  18. femagtools/machine/afpm.py +79 -33
  19. femagtools/machine/effloss.py +29 -18
  20. femagtools/machine/sizing.py +192 -13
  21. femagtools/machine/sm.py +34 -36
  22. femagtools/machine/utils.py +2 -2
  23. femagtools/mcv.py +58 -29
  24. femagtools/model.py +4 -3
  25. femagtools/multiproc.py +79 -80
  26. femagtools/parstudy.py +11 -5
  27. femagtools/plot/nc.py +2 -2
  28. femagtools/semi_fea.py +108 -0
  29. femagtools/templates/basic_modpar.mako +0 -3
  30. femagtools/templates/fe-contr.mako +18 -18
  31. femagtools/templates/ld_lq_fast.mako +3 -0
  32. femagtools/templates/mult_cal_fast.mako +3 -0
  33. femagtools/templates/pm_sym_f_cur.mako +4 -1
  34. femagtools/templates/pm_sym_fast.mako +3 -0
  35. femagtools/templates/pm_sym_loss.mako +3 -0
  36. femagtools/templates/psd_psq_fast.mako +3 -0
  37. femagtools/templates/torq_calc.mako +3 -0
  38. femagtools/tks.py +23 -20
  39. femagtools/zmq.py +213 -0
  40. {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/METADATA +3 -3
  41. {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/RECORD +49 -47
  42. {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/WHEEL +1 -1
  43. tests/test_afpm.py +15 -6
  44. tests/test_femag.py +1 -1
  45. tests/test_fsl.py +4 -4
  46. tests/test_mcv.py +21 -15
  47. {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/LICENSE +0 -0
  48. {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/entry_points.txt +0 -0
  49. {femagtools-1.8.1.dist-info → femagtools-1.8.3.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.8.1'
5
+ __version__ = '1.8.3'
6
6
  __author__ = 'Ronald Tanner'
7
7
  __license__ = 'BSD'
8
8
  __copyright__ = 'Copyright 2023-2024 Gamma Technology'
femagtools/dxfsl/area.py CHANGED
@@ -12,6 +12,7 @@ import numpy as np
12
12
  import networkx as nx
13
13
  import logging
14
14
  from .functions import less_equal, less, greater_equal, greater
15
+ from .functions import greater_angle, less_angle
15
16
  from .functions import distance, alpha_angle, alpha_line, min_angle, max_angle
16
17
  from .functions import point, line_m, line_n, intersect_point, points_are_close
17
18
  from .functions import middle_angle, part_of_circle, is_same_angle
@@ -75,6 +76,10 @@ class Area(object):
75
76
  self.start = 0.0
76
77
  self.sym_startangle = 0.0
77
78
  self.sym_endangle = 0.0
79
+ self.sym_upper_left_dist = None
80
+ self.sym_upper_right_dist = None
81
+ self.sym_lower_left_dist = None
82
+ self.sym_lower_right_dist = None
78
83
  self.sym_type = 0
79
84
  self.symmetry = 0
80
85
  self.sym_tolerance = sym_tolerance
@@ -100,6 +105,9 @@ class Area(object):
100
105
  def elements(self):
101
106
  return self.area
102
107
 
108
+ def copy_of_elements(self):
109
+ return [e.clone() for e in self.elements() if e]
110
+
103
111
  def list_of_nodes(self):
104
112
  if len(self.area) < 1:
105
113
  return
@@ -671,6 +679,66 @@ class Area(object):
671
679
  return True
672
680
  return False
673
681
 
682
+ def set_symmetry_parameter(self, center):
683
+ all_list = [(distance(center, n), alpha_line(center, n))
684
+ for n in self.list_of_nodes()]
685
+ mid = middle_angle(self.min_angle, self.max_angle)
686
+ left_list = [(d, a) for d, a in all_list if greater_angle(a, mid)]
687
+ right_list = [(d, a) for d, a in all_list if less_angle(a, mid)]
688
+ left_list.sort()
689
+ right_list.sort()
690
+
691
+ if left_list:
692
+ l_low_d, l_low_a = left_list[0]
693
+ l_up_d, l_up_a = left_list[-1]
694
+ else:
695
+ l_low_d = self.min_dist
696
+ l_up_d = self.max_dist
697
+ if right_list:
698
+ r_low_d, r_low_a = right_list[0]
699
+ r_up_d, r_up_a = right_list[-1]
700
+ else:
701
+ r_low_d = self.min_dist
702
+ r_up_d = self.max_dist
703
+ self.sym_upper_left_dist = l_up_d
704
+ self.sym_upper_right_dist = r_up_d
705
+ self.sym_lower_left_dist = l_low_d
706
+ self.sym_lower_right_dist = r_low_d
707
+
708
+ def is_symmetry_equal(self, area):
709
+ logger.debug("check area %s -- %s", self.get_id(), area.get_id())
710
+
711
+ bad = False
712
+ if not np.isclose(self.sym_lower_left_dist, area.sym_lower_left_dist,
713
+ rtol=5e-1, atol=5e-1):
714
+ logger.debug("Lower left: %s != %s",
715
+ self.sym_lower_left_dist,
716
+ area.sym_lower_left_dist)
717
+ bad = True
718
+
719
+ if not np.isclose(self.sym_lower_right_dist, area.sym_lower_right_dist,
720
+ rtol=5e-1, atol=5e-1):
721
+ logger.debug("Lower right: %s != %s",
722
+ self.sym_lower_right_dist,
723
+ area.sym_lower_right_dist)
724
+ bad = True
725
+
726
+ if not np.isclose(self.sym_upper_left_dist, area.sym_upper_left_dist,
727
+ rtol=5e-1, atol=5e-1):
728
+ logger.debug("Upper left: %s != %s",
729
+ self.sym_upper_left_dist,
730
+ area.sym_upper_left_dist)
731
+ bad = True
732
+
733
+ if not np.isclose(self.sym_upper_right_dist, area.sym_upper_right_dist,
734
+ rtol=5e-1, atol=5e-1):
735
+ logger.debug("Upper right: %s != %s",
736
+ self.sym_upper_right_dist,
737
+ area.sym_upper_right_dist)
738
+ bad = True
739
+
740
+ return not bad
741
+
674
742
  def increment(self, a):
675
743
  if self.is_identical(a):
676
744
  return
@@ -1208,6 +1276,7 @@ class Area(object):
1208
1276
 
1209
1277
  angles.sort(reverse=True)
1210
1278
  # calculate orientation (no rectangle check)
1279
+
1211
1280
  l, alpha = angles[0]
1212
1281
  phi = normalise_angle(alpha + np.pi/2)
1213
1282
  logger.debug("alpha = %s, phi = %s", alpha, phi)
@@ -1216,9 +1285,13 @@ class Area(object):
1216
1285
  angle = alpha_angle(mid, phi)
1217
1286
  logger.debug("phi=%s, mid=%s, angle=%s", phi, mid, angle)
1218
1287
 
1219
- if greater(angle, np.pi * 0.5, rtol=1e-5) and \
1288
+ if np.isclose(mid, alpha, rtol=1e-3, atol=1e-3):
1289
+ phi = mid
1290
+ logger.debug("correction of phi=%s", phi)
1291
+ elif greater(angle, np.pi * 0.5, rtol=1e-5) and \
1220
1292
  less(angle, np.pi * 1.5, rtol=1e-5):
1221
1293
  phi = normalise_angle(phi + np.pi)
1294
+ logger.debug("correction of phi=%s", phi)
1222
1295
 
1223
1296
  logger.debug("phi of magnet %s is %s", self.identifier(), phi)
1224
1297
  return phi
@@ -1321,6 +1394,32 @@ class Area(object):
1321
1394
  return np.isclose(angle, border_angle,
1322
1395
  rtol=1e-03, atol=1e-03)
1323
1396
 
1397
+ def set_subregion_parameters(self,
1398
+ is_inner,
1399
+ min_radius,
1400
+ max_radius,
1401
+ startangle,
1402
+ endangle):
1403
+ ag_delta = (max_radius - min_radius) / 500.0
1404
+ if is_inner:
1405
+ self.close_to_ag = greater_equal(self.max_dist + ag_delta, max_radius)
1406
+ close_to_opposition = np.isclose(min_radius, self.min_dist)
1407
+ airgap_radius = max_radius
1408
+ opposite_radius = min_radius
1409
+ else:
1410
+ self.close_to_ag = less_equal(self.min_dist - ag_delta, min_radius)
1411
+ close_to_opposition = np.isclose(max_radius, self.max_dist)
1412
+ airgap_radius = min_radius
1413
+ opposite_radius = max_radius
1414
+
1415
+ airgap_toleranz = (self.max_dist - self.min_dist) / 50.0 # 2%
1416
+
1417
+ self.close_to_startangle = np.isclose(self.min_angle, startangle,
1418
+ 1e-04, 1e-04)
1419
+ self.close_to_endangle = np.isclose(self.max_angle, endangle,
1420
+ 1e-04, 1e-04)
1421
+ self.surface = self.area_size()
1422
+
1324
1423
  def mark_stator_subregions(self,
1325
1424
  is_inner,
1326
1425
  stator_size,
@@ -1791,6 +1890,16 @@ class Area(object):
1791
1890
  for i in a.nested_areas_inside():
1792
1891
  yield i
1793
1892
 
1893
+ def mirror_area(self, center, axis_m, axis_n):
1894
+ elements = []
1895
+ for e in self.area:
1896
+ el = e.mirror_shape(center, axis_m, axis_n)
1897
+ if el:
1898
+ elements.append(el)
1899
+ area = Area(elements, center, 0.0)
1900
+ area.type = self.type
1901
+ return area
1902
+
1794
1903
  def __str__(self):
1795
1904
  return "Area {}\n".format(self.id) + \
1796
1905
  "distance...............: from {} to {}\n".\
@@ -338,7 +338,7 @@ class EdgeInfo(object):
338
338
  return True # two lines
339
339
 
340
340
  def arc_line_direction_lefthand(self, start_edge, line_edge, builder):
341
- logger.info("begin of arc_line_direction_lefthand")
341
+ logger.debug("begin of arc_line_direction_lefthand")
342
342
  if not self.is_arc():
343
343
  logger.critical("FATAL: unexpected %s at position %s", self.classname(), self.n1)
344
344
  sys.exit(1)
@@ -630,7 +630,7 @@ class AreaBuilder(object):
630
630
  logger.debug("begin of create_inner_corner_auxiliary_areas")
631
631
  if not self.geom.is_inner:
632
632
  logger.debug("end of create_inner_corner_auxiliary_areas: not inner")
633
- return
633
+ return False
634
634
 
635
635
  self.set_edge_attributes()
636
636
 
@@ -647,17 +647,18 @@ class AreaBuilder(object):
647
647
 
648
648
  if start_exists and end_exists:
649
649
  logger.debug("end of create_inner_corner_auxiliary_areas: no aktion")
650
- return
650
+ return False
651
651
 
652
652
  airgap_line, airgap_el = self.get_inner_airgap_line()
653
653
  if not airgap_el:
654
654
  logger.debug("end of create_inner_corner_auxiliary_areas: no airgapline found")
655
- return
655
+ return False
656
656
 
657
657
  logger.debug("airgapline found !!")
658
658
  airgap_nodes = [n for n in airgap_line[1:]]
659
659
  del airgap_nodes[-1]
660
660
 
661
+ created = False
661
662
  if not start_exists:
662
663
  cp = self.geom.start_corners[-1]
663
664
  logger.debug("Start Corner: %s -- %s", cp, start_cp)
@@ -683,6 +684,7 @@ class AreaBuilder(object):
683
684
  n,
684
685
  color='red',
685
686
  linestyle='dotted')
687
+ created = True
686
688
  start_node = self.geom.get_node(start_cp)
687
689
  self.geom.add_edge(cp, start_node, start_line)
688
690
  result = self.get_new_area(start_node, n)
@@ -715,6 +717,7 @@ class AreaBuilder(object):
715
717
  self.geom.add_line(end_cp, n,
716
718
  color='red',
717
719
  linestyle='dotted')
720
+ created = True
718
721
  end_node = self.geom.get_node(end_cp)
719
722
  self.geom.add_edge(cp, end_node, end_line)
720
723
  result = self.get_new_area(n, end_node)
@@ -725,81 +728,126 @@ class AreaBuilder(object):
725
728
  break
726
729
 
727
730
  logger.debug("end of create_inner_corner_auxiliary_areas")
731
+ return created
728
732
 
729
- def get_inner_airgap_line(self):
730
- logger.debug("begin of get_inner_airgap_line")
731
- assert(self.geom.is_inner)
732
- assert(self.geom.area_list)
733
-
734
- area = [a for a in self.geom.area_list if a.close_to_ag_endcorner]
735
- if len(area) != 1:
736
- logger.debug("end of get_inner_airgap_line: %s areas found", len(area))
737
- return [], []
733
+ def get_airgap_line(self, start_node, end_node, area):
734
+ logger.debug("get_airgap_line")
738
735
 
739
- end_corner = self.geom.end_corners[-1]
740
- logger.debug("END CORNER %s", end_corner)
736
+ self.set_edge_attributes()
741
737
 
742
- nodes = [n for n in area[0].list_of_nodes()]
738
+ nodes = [n for n in area.list_of_nodes()]
743
739
  if not nodes:
744
- logger.debug("end of get_inner_airgap_line: no nodes found")
740
+ logger.debug("end of get_airgap_line: no nodes found")
745
741
  return [], []
746
742
 
747
743
  n1 = nodes[0]
748
- if points_are_close(end_corner, n1):
744
+ if points_are_close(start_node, n1):
749
745
  n2 = nodes[-1]
750
746
  else:
751
747
  n2 = n1
752
748
  for n1 in nodes[1:]:
753
- if points_are_close(end_corner, n1):
749
+ if points_are_close(start_node, n1):
754
750
  break
755
751
  n2 = n1
756
752
 
757
- if not points_are_close(end_corner, n1):
758
- logger.debug("end of get_inner_airgap_line: not close to endcorner")
753
+ if not points_are_close(start_node, n1):
754
+ logger.debug("end of get_airgap_line: not close to start-node")
759
755
  return [], []
760
756
 
761
- start_corner = self.geom.start_corners[-1]
762
- logger.debug("START CORNER %s", end_corner)
763
-
764
- logger.debug("EDGE FOUND: %s - %s", n1, n2)
757
+ logger.debug("START EDGE FOUND: %s - %s", n1, n2)
765
758
  nodes = [n1, n2]
766
759
  info = self.get_edge_info(n1, n2)
767
760
  elements = [info.element]
768
761
 
769
- while not points_are_close(start_corner, n2):
762
+ while not points_are_close(end_node, n2):
770
763
  info.set_start_angle()
771
764
  info = self.next_edge_lefthand_side(info)
772
765
  if not info: # bad
773
- return []
766
+ return [], []
774
767
  n2 = info.n2
775
768
  nodes.append(n2)
776
769
  elements.append(info.element)
777
770
 
778
- logger.debug("end of get_inner_airgap_line #%s", len(nodes))
771
+ logger.debug("end of get_airgap_line #%s", len(nodes))
779
772
  return nodes, elements
780
773
 
781
- def get_element_line(self, start_node, end_node):
782
- logger.debug("begin of get_element_line")
774
+ def get_inner_airgap_line(self):
775
+ logger.debug("begin of get_inner_airgap_line")
776
+ assert(self.geom.is_inner)
783
777
  assert(self.geom.area_list)
784
778
 
785
- logger.debug("-- get line from %s to %s", start_node, end_node)
779
+ area = [a for a in self.geom.area_list if a.close_to_ag_endcorner]
780
+ if len(area) != 1:
781
+ logger.debug("end of get_inner_airgap_line: %s areas found", len(area))
782
+ return [], []
786
783
 
787
- logger.debug("EDGE FOUND: %s - %s", n1, n2)
788
- nodes = [n1, n2]
789
- info = self.get_edge_info(n1, n2)
790
- elements = [info.element]
784
+ start_node = self.geom.end_corners[-1]
785
+ logger.debug("START NODE %s", start_node)
786
+ end_node = self.geom.start_corners[-1]
787
+ logger.debug("END NODE %s", end_node)
791
788
 
792
- while not points_are_close(start_corner, n2):
793
- info.set_start_angle()
794
- info = self.next_edge_lefthand_side(info)
795
- if not info: # bad
796
- return []
797
- n2 = info.n2
798
- nodes.append(n2)
799
- elements.append(info.element)
789
+ return self.get_airgap_line(start_node, end_node, area[0])
800
790
 
801
- logger.debug("end of get_inner_airgap_line #%s", len(nodes))
802
- return nodes, elements
791
+ def close_outer_winding_areas(self):
792
+ logger.debug("close_outer_winding_areas")
793
+
794
+ airgap_line, airgap_el = self.get_outer_airgap_line()
795
+ logger.debug("Outer Airgap with %s Nodes", len(airgap_line))
796
+
797
+ if len(airgap_line) < 5:
798
+ return False
799
+
800
+ n1 = None
801
+ dist_n1 = 0.0
802
+
803
+ e_prev = None
804
+ n_prev = airgap_line[0]
805
+ dist_prev = distance(self.geom.center, n_prev)
806
+ alpha_prev = alpha_line(self.geom.center, n_prev)
807
+ alpha_start = alpha_prev
808
+
809
+ lines_created = 0
810
+ for n in airgap_line[1:]:
811
+ dist = distance(self.geom.center, n)
812
+ alpha = alpha_line(self.geom.center, n)
813
+ if not n1:
814
+ if dist > dist_prev and alpha < alpha_prev:
815
+ n1 = n_prev
816
+ dist_n1 = dist_prev
817
+ else:
818
+ if np.isclose(dist_n1, dist, rtol=1e-3, atol=1e-3):
819
+ line = Line(Element(start=n1, end=n))
820
+ if e_prev.intersect_line(line):
821
+ logger.debug("___LINE NOT POSSIBLE___")
822
+ else:
823
+ self.geom.add_line(n1, n, color='red')
824
+ lines_created += 1
825
+ n1 = None
826
+ dist_n1 = 0.0
827
+ if not n1:
828
+ e_prev = self.geom.get_edge_element(n_prev, n)
829
+ n_prev = n
830
+ dist_prev = dist
831
+ alpha_prev = alpha
832
+
833
+ return lines_created > 0
834
+
835
+ def get_outer_airgap_line(self):
836
+ logger.debug("begin of get_outer_airgap_line")
837
+ assert(self.geom.is_outer)
838
+ assert(self.geom.area_list)
839
+
840
+ area = [a for a in self.geom.area_list if a.close_to_ag_startcorner]
841
+ if len(area) != 1:
842
+ logger.debug("end of get_outer_airgap_line: %s areas found", len(area))
843
+ return [], []
844
+
845
+ start_node = self.geom.start_corners[0]
846
+ logger.debug("START NODE %s", start_node)
847
+ end_node = self.geom.end_corners[0]
848
+ logger.debug("END NODE %s", end_node)
849
+
850
+ return self.get_airgap_line(start_node, end_node, area[0])
803
851
 
804
852
  def create_one_area_group(self, areas):
805
853
  logger.debug("begin of create_one_area_group")
femagtools/dxfsl/conv.py CHANGED
@@ -115,6 +115,10 @@ def main():
115
115
  help='create fsl',
116
116
  dest='write_fsl',
117
117
  action="store_true")
118
+ argparser.add_argument('--fsl_single',
119
+ help='create separate fsl for rotor and stator',
120
+ dest='write_fsl_single',
121
+ action="store_true")
118
122
  argparser.add_argument('-v', '--view',
119
123
  help='show a view only',
120
124
  dest='view',
@@ -242,6 +246,7 @@ def main():
242
246
  show_areas=args.show_areas,
243
247
  small_plots=args.small_plots,
244
248
  write_fsl=args.write_fsl,
249
+ write_fsl_single=args.write_fsl_single,
245
250
  write_png=args.write_png,
246
251
  write_id=args.write_id,
247
252
  debug_mode=args.debugger,
@@ -12,6 +12,7 @@ from femagtools.dxfsl.plotrenderer import PlotRenderer
12
12
  from femagtools.dxfsl.concat import Concatenation
13
13
  from femagtools.dxfsl.functions import Timer, middle_angle
14
14
  from femagtools.dxfsl.journal import Journal, getJournal
15
+ from femagtools.dxfsl.area import TYPE_WINDINGS
15
16
  import logging
16
17
  import logging.config
17
18
  import numpy as np
@@ -180,15 +181,25 @@ def build_machine_rotor(machine, inner, mindist, plt, EESM=False, single=False):
180
181
  else:
181
182
  machine_temp = machine
182
183
 
183
- midangle = middle_angle(machine_temp.startangle,
184
- machine_temp.endangle)
185
-
186
184
  plot_geom(False, # for developer
187
185
  plt, machine_temp.geom,
188
- title="Inner Rotor check magnets {}".format(midangle))
186
+ title="Inner Rotor check magnets")
187
+
188
+ machine_slice = machine_temp.get_forced_magnet_slice()
189
+ if machine_slice:
190
+ plot_geom(False, # for developer
191
+ plt, machine_slice.geom,
192
+ title="Rotor Magnet Slice")
193
+
194
+ machine_temp = machine_slice
195
+ machine_temp.geom.set_rotor()
196
+ machine_temp.rebuild_subregions(EESM, single=single)
197
+ plot_geom(False, # for developer
198
+ plt, machine_temp.geom,
199
+ title="Rotor Magnet Slice after Rebuild")
189
200
 
190
201
  rebuild = False
191
- if machine_temp.geom.magnets_in_the_middle(midangle):
202
+ if machine_temp.has_magnets_in_the_middle():
192
203
  logger.debug("Magnets cut")
193
204
  rebuild = machine_temp.create_mirror_lines_outside_magnets()
194
205
  else:
@@ -199,7 +210,7 @@ def build_machine_rotor(machine, inner, mindist, plt, EESM=False, single=False):
199
210
  else:
200
211
  rebuild = machine_temp.create_mirror_lines_outside_magnets()
201
212
  if rebuild:
202
- machine_temp.geom.area_list = []
213
+ machine_temp.geom.create_list_of_areas(delete=True)
203
214
 
204
215
  if machine_temp.create_auxiliary_lines():
205
216
  logger.debug("Auxiliary Lines created: rebuild subregions")
@@ -239,8 +250,7 @@ def build_machine_stator(machine, inner, mindist, plt, EESM=False, single=False)
239
250
  if machine.is_mirrored():
240
251
  plot_geom(False, # for developer
241
252
  plt, machine.previous_machine.geom,
242
- title="Mirrored Stator",
243
- areas=True)
253
+ title="Mirrored Stator")
244
254
 
245
255
  logger.debug("undo mirrored windings")
246
256
  machine_temp = machine.undo_mirror()
@@ -261,6 +271,22 @@ def build_machine_stator(machine, inner, mindist, plt, EESM=False, single=False)
261
271
  plt, machine_temp.geom,
262
272
  title="Nodes reduced")
263
273
 
274
+ machine_slice = machine_temp.get_forced_winding_slice()
275
+ if machine_slice:
276
+ plot_geom(False, # for developer
277
+ plt, machine_slice.geom,
278
+ title="Stator Winding Slice")
279
+
280
+ machine_temp = machine_slice
281
+ machine_temp.geom.set_stator()
282
+ machine_temp.rebuild_subregions(EESM, single=single)
283
+ if machine_temp.has_windings_in_the_middle():
284
+ machine_temp.create_mirror_lines_outside_windings()
285
+
286
+ plot_geom(False, # for developer
287
+ plt, machine_temp.geom,
288
+ title="Stator Winding Slice after Rebuild")
289
+
264
290
  if machine_temp.create_auxiliary_lines():
265
291
  machine_temp.rebuild_subregions(EESM, single=single)
266
292
  plot_geom(False, # for developer
@@ -273,7 +299,7 @@ def build_machine_stator(machine, inner, mindist, plt, EESM=False, single=False)
273
299
  if not machine_temp.is_mirrored():
274
300
  plot_geom(False, # for developer
275
301
  plt, machine_temp.geom,
276
- title="Stator before Boundery Corr")
302
+ title="Stator before Boundary Corr")
277
303
  machine_temp.create_boundary_nodes()
278
304
 
279
305
  plot_geom(False, # for developer
@@ -309,6 +335,7 @@ def convert(dxfile,
309
335
  show_areas=False,
310
336
  small_plots=False,
311
337
  write_fsl=True,
338
+ write_fsl_single=False,
312
339
  write_png=False,
313
340
  write_id=False,
314
341
  full_model=False,
@@ -362,6 +389,9 @@ def convert(dxfile,
362
389
  split_ini = False
363
390
  split_cpy = split
364
391
 
392
+ if write_fsl_single:
393
+ write_fsl = True
394
+
365
395
  try:
366
396
  if input_file.suffix in ['.fem', '.FEM']:
367
397
  from .femparser import femshapes
@@ -693,6 +723,14 @@ def convert(dxfile,
693
723
  fslrenderer = FslRenderer(basename, mtype)
694
724
  inner = fslrenderer.render(machine_inner, inner=True)
695
725
  outer = fslrenderer.render(machine_outer, outer=True)
726
+ if write_fsl_single:
727
+ inner_single = fslrenderer.render(machine_inner, inner=True,
728
+ standalone=True)
729
+ outer_single = fslrenderer.render(machine_outer, outer=True,
730
+ standalone=True)
731
+ else:
732
+ inner_single = None
733
+ outer_single = None
696
734
  if full_model:
697
735
  params['num_sl_gen'] = params.get('tot_num_slot', 0)
698
736
  params['agndst'] = agndst(params.get('da1'),
@@ -703,10 +741,18 @@ def convert(dxfile,
703
741
 
704
742
  if params['external_rotor']:
705
743
  conv['fsl_rotor'] = outer
744
+ if outer_single:
745
+ conv['fsl_rotor_single'] = outer_single
706
746
  conv['fsl_stator'] = inner
747
+ if inner_single:
748
+ conv['fsl_stator_single'] = inner_single
707
749
  else:
708
750
  conv['fsl_rotor'] = inner
751
+ if inner_single:
752
+ conv['fsl_rotor_single'] = inner_single
709
753
  conv['fsl_stator'] = outer
754
+ if outer_single:
755
+ conv['fsl_stator_single'] = outer_single
710
756
 
711
757
  conv['fsl'] = fslrenderer.render_main(
712
758
  machine_inner, machine_outer,
@@ -849,11 +895,24 @@ def convert(dxfile,
849
895
  if write_fsl:
850
896
  logger.debug("Write fsl")
851
897
  if conv and conv['fsl']:
852
- with io.open(basename + '.fsl', 'w', encoding='utf-8') as f:
898
+ with io.open(basename + '.fsl', 'w',
899
+ encoding='utf-8') as f:
853
900
  f.write('\n'.join(conv['fsl']))
854
901
  else:
855
902
  logger.warning("No fsl data available")
856
903
 
904
+ if conv:
905
+ if conv.get('fsl_rotor_single', None):
906
+ with io.open(basename + '_ROTOR.fsl', 'w',
907
+ encoding='utf-8') as f:
908
+ f.write('\n'.join(conv['fsl_rotor_single']))
909
+ del conv['fsl_rotor_single']
910
+ if conv.get('fsl_stator_single', None):
911
+ with io.open(basename + '_STATOR.fsl', 'w',
912
+ encoding='utf-8') as f:
913
+ f.write('\n'.join(conv['fsl_stator_single']))
914
+ del conv['fsl_stator_single']
915
+
857
916
  conv['name'] = basename
858
917
  t = timer.stop("-- all done in %0.4f seconds --", info=True)
859
918
  journal.put('time_total', t)
@@ -874,30 +933,29 @@ def create_femag_parameters(m_inner, m_outer, nodedist=1):
874
933
  parts_inner = int(m_inner.get_symmetry_part())
875
934
  parts_outer = int(m_outer.get_symmetry_part())
876
935
 
877
- slot_area = 0
878
- if parts_inner > parts_outer:
879
- from .area import TYPE_WINDINGS
880
- slot_area = geom_inner.area_size_of_type(TYPE_WINDINGS)
881
- num_slots = int(parts_inner)
882
- num_poles = int(parts_outer)
883
- num_sl_gen = int(geom_inner.get_symmetry_copies()+1)
884
- alfa_slot = geom_inner.get_alfa()
885
- alfa_pole = geom_outer.get_alfa()
936
+ if m_inner.geom.is_rotor():
937
+ geom_slots = geom_outer
938
+ geom_poles = geom_inner
939
+ num_slots = int(m_outer.get_symmetry_part())
940
+ num_poles = int(m_inner.get_symmetry_part())
886
941
  else:
887
- from .area import TYPE_WINDINGS
888
- slot_area = geom_outer.area_size_of_type(TYPE_WINDINGS)
889
- num_slots = int(parts_outer)
890
- num_poles = int(parts_inner)
891
- num_sl_gen = int(geom_outer.get_symmetry_copies()+1)
892
- alfa_slot = geom_outer.get_alfa()
893
- alfa_pole = geom_inner.get_alfa()
942
+ geom_slots = geom_inner
943
+ geom_poles = geom_outer
944
+ num_slots = int(m_inner.get_symmetry_part())
945
+ num_poles = int(m_outer.get_symmetry_part())
946
+
947
+ slot_area = 0
948
+ slot_area = geom_slots.area_size_of_type(TYPE_WINDINGS)
949
+ num_sl_gen = int(geom_slots.get_symmetry_copies()+1)
950
+ alfa_slot = geom_slots.get_alfa()
951
+ alfa_pole = geom_poles.get_alfa()
894
952
 
895
953
  params['tot_num_slot'] = num_slots
896
954
  params['slot_area'] = slot_area
897
955
  params['num_sl_gen'] = num_sl_gen
898
956
  params['num_poles'] = num_poles
899
957
  params['nodedist'] = nodedist
900
- params['external_rotor'] = parts_inner > parts_outer
958
+ params['external_rotor'] = m_outer.geom.is_rotor()
901
959
  params['dy1'] = 2*geom_outer.max_radius
902
960
  params['da1'] = 2*geom_outer.min_radius
903
961
  params['da2'] = 2*geom_inner.max_radius
@@ -236,18 +236,19 @@ class FslRenderer(object):
236
236
  geom.start_max_corner(1)),
237
237
  'hair = 1.0',
238
238
  'parts = {}'.format(machine.get_num_parts()),
239
- 'r1 = {} + hair'.format(geom.max_radius),
239
+ 'rmax = {}'.format(geom.max_radius),
240
+ 'r1 = rmax + hair',
240
241
  'r, phi = c2pr(x0, y0)',
241
242
  'x1, y1 = pr2c(r1, phi)',
242
243
  'x2, y2 = pr2c(r1, {}*math.pi/parts)'.format(slice),
243
244
  f'-- end max corner {geom.end_corners[-1]}',
244
245
  f'-- center {geom.center}',
245
- f'r = {geom.dist_end_max_corner()}',
246
- 'x3, y3 = pr2c(r, {}*math.pi/parts)'.format(slice),
246
+ 'r2 = {}'.format(geom.dist_end_max_corner(mirrored=False)),
247
+ 'x3, y3 = pr2c(r2, {}*math.pi/parts)'.format(slice),
247
248
  'nc_line(x0, y0, x1, y1, 0)',
248
249
  'nc_circle_m(x1, y1, x2, y2, 0.0, 0.0, 0)',
249
250
  'nc_line(x2, y2, x3, y3, 0)',
250
- 'x0, y0 = pr2c(r1 - hair/2, math.pi/parts/2)',
251
+ 'x0, y0 = pr2c(rmax + hair/2, math.pi/parts/2)',
251
252
  'create_mesh_se(x0, y0)',
252
253
  '\n',
253
254
  'outer_da_start = {}'.format(