femagtools 1.8.1__py3-none-any.whl → 1.8.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.
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.2'
6
6
  __author__ = 'Ronald Tanner'
7
7
  __license__ = 'BSD'
8
8
  __copyright__ = 'Copyright 2023-2024 Gamma Technology'
femagtools/dxfsl/area.py CHANGED
@@ -100,6 +100,9 @@ class Area(object):
100
100
  def elements(self):
101
101
  return self.area
102
102
 
103
+ def copy_of_elements(self):
104
+ return [e.clone() for e in self.elements() if e]
105
+
103
106
  def list_of_nodes(self):
104
107
  if len(self.area) < 1:
105
108
  return
@@ -1208,6 +1211,7 @@ class Area(object):
1208
1211
 
1209
1212
  angles.sort(reverse=True)
1210
1213
  # calculate orientation (no rectangle check)
1214
+
1211
1215
  l, alpha = angles[0]
1212
1216
  phi = normalise_angle(alpha + np.pi/2)
1213
1217
  logger.debug("alpha = %s, phi = %s", alpha, phi)
@@ -1216,9 +1220,13 @@ class Area(object):
1216
1220
  angle = alpha_angle(mid, phi)
1217
1221
  logger.debug("phi=%s, mid=%s, angle=%s", phi, mid, angle)
1218
1222
 
1219
- if greater(angle, np.pi * 0.5, rtol=1e-5) and \
1223
+ if np.isclose(mid, alpha, rtol=1e-3, atol=1e-3):
1224
+ phi = mid
1225
+ logger.debug("correction of phi=%s", phi)
1226
+ elif greater(angle, np.pi * 0.5, rtol=1e-5) and \
1220
1227
  less(angle, np.pi * 1.5, rtol=1e-5):
1221
1228
  phi = normalise_angle(phi + np.pi)
1229
+ logger.debug("correction of phi=%s", phi)
1222
1230
 
1223
1231
  logger.debug("phi of magnet %s is %s", self.identifier(), phi)
1224
1232
  return phi
@@ -1321,6 +1329,32 @@ class Area(object):
1321
1329
  return np.isclose(angle, border_angle,
1322
1330
  rtol=1e-03, atol=1e-03)
1323
1331
 
1332
+ def set_subregion_parameters(self,
1333
+ is_inner,
1334
+ min_radius,
1335
+ max_radius,
1336
+ startangle,
1337
+ endangle):
1338
+ ag_delta = (max_radius - min_radius) / 500.0
1339
+ if is_inner:
1340
+ self.close_to_ag = greater_equal(self.max_dist + ag_delta, max_radius)
1341
+ close_to_opposition = np.isclose(min_radius, self.min_dist)
1342
+ airgap_radius = max_radius
1343
+ opposite_radius = min_radius
1344
+ else:
1345
+ self.close_to_ag = less_equal(self.min_dist - ag_delta, min_radius)
1346
+ close_to_opposition = np.isclose(max_radius, self.max_dist)
1347
+ airgap_radius = min_radius
1348
+ opposite_radius = max_radius
1349
+
1350
+ airgap_toleranz = (self.max_dist - self.min_dist) / 50.0 # 2%
1351
+
1352
+ self.close_to_startangle = np.isclose(self.min_angle, startangle,
1353
+ 1e-04, 1e-04)
1354
+ self.close_to_endangle = np.isclose(self.max_angle, endangle,
1355
+ 1e-04, 1e-04)
1356
+ self.surface = self.area_size()
1357
+
1324
1358
  def mark_stator_subregions(self,
1325
1359
  is_inner,
1326
1360
  stator_size,
@@ -1791,6 +1825,16 @@ class Area(object):
1791
1825
  for i in a.nested_areas_inside():
1792
1826
  yield i
1793
1827
 
1828
+ def mirror_area(self, center, axis_m, axis_n):
1829
+ elements = []
1830
+ for e in self.area:
1831
+ el = e.mirror_shape(center, axis_m, axis_n)
1832
+ if el:
1833
+ elements.append(el)
1834
+ area = Area(elements, center, 0.0)
1835
+ area.type = self.type
1836
+ return area
1837
+
1794
1838
  def __str__(self):
1795
1839
  return "Area {}\n".format(self.id) + \
1796
1840
  "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")
@@ -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
@@ -874,30 +900,29 @@ def create_femag_parameters(m_inner, m_outer, nodedist=1):
874
900
  parts_inner = int(m_inner.get_symmetry_part())
875
901
  parts_outer = int(m_outer.get_symmetry_part())
876
902
 
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()
903
+ if m_inner.geom.is_rotor():
904
+ geom_slots = geom_outer
905
+ geom_poles = geom_inner
906
+ num_slots = int(m_outer.get_symmetry_part())
907
+ num_poles = int(m_inner.get_symmetry_part())
886
908
  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()
909
+ geom_slots = geom_inner
910
+ geom_poles = geom_outer
911
+ num_slots = int(m_inner.get_symmetry_part())
912
+ num_poles = int(m_outer.get_symmetry_part())
913
+
914
+ slot_area = 0
915
+ slot_area = geom_slots.area_size_of_type(TYPE_WINDINGS)
916
+ num_sl_gen = int(geom_slots.get_symmetry_copies()+1)
917
+ alfa_slot = geom_slots.get_alfa()
918
+ alfa_pole = geom_poles.get_alfa()
894
919
 
895
920
  params['tot_num_slot'] = num_slots
896
921
  params['slot_area'] = slot_area
897
922
  params['num_sl_gen'] = num_sl_gen
898
923
  params['num_poles'] = num_poles
899
924
  params['nodedist'] = nodedist
900
- params['external_rotor'] = parts_inner > parts_outer
925
+ params['external_rotor'] = m_outer.geom.is_rotor()
901
926
  params['dy1'] = 2*geom_outer.max_radius
902
927
  params['da1'] = 2*geom_outer.min_radius
903
928
  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(