femagtools 1.6.8__py3-none-any.whl → 1.7.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 (50) hide show
  1. femagtools/__init__.py +2 -2
  2. femagtools/bch.py +1 -1
  3. femagtools/dxfsl/area.py +334 -332
  4. femagtools/dxfsl/areabuilder.py +131 -10
  5. femagtools/dxfsl/conv.py +27 -9
  6. femagtools/dxfsl/converter.py +390 -125
  7. femagtools/dxfsl/corner.py +3 -0
  8. femagtools/dxfsl/femparser.py +1 -1
  9. femagtools/dxfsl/fslrenderer.py +290 -246
  10. femagtools/dxfsl/functions.py +4 -2
  11. femagtools/dxfsl/geom.py +1120 -886
  12. femagtools/dxfsl/journal.py +53 -22
  13. femagtools/dxfsl/machine.py +250 -74
  14. femagtools/dxfsl/plotrenderer.py +34 -3
  15. femagtools/dxfsl/shape.py +380 -103
  16. femagtools/dxfsl/symmetry.py +679 -110
  17. femagtools/femag.py +27 -2
  18. femagtools/forcedens.py +65 -40
  19. femagtools/fsl.py +71 -28
  20. femagtools/losscoeffs.py +46 -0
  21. femagtools/machine/effloss.py +8 -1
  22. femagtools/machine/im.py +3 -1
  23. femagtools/machine/pm.py +11 -7
  24. femagtools/machine/sizing.py +14 -11
  25. femagtools/machine/sm.py +114 -33
  26. femagtools/machine/utils.py +38 -34
  27. femagtools/model.py +12 -2
  28. femagtools/moo/population.py +1 -1
  29. femagtools/parstudy.py +17 -1
  30. femagtools/plot/__init__.py +1 -1
  31. femagtools/plot/char.py +24 -7
  32. femagtools/plot/forcedens.py +56 -29
  33. femagtools/plot/mcv.py +4 -1
  34. femagtools/plot/phasor.py +6 -1
  35. femagtools/poc.py +17 -10
  36. femagtools/templates/cogg_calc.mako +7 -1
  37. femagtools/templates/displ_stator_rotor.mako +33 -0
  38. femagtools/templates/fieldcalc.mako +10 -16
  39. femagtools/templates/pm_sym_f_cur.mako +1 -1
  40. femagtools/tks.py +3 -9
  41. {femagtools-1.6.8.dist-info → femagtools-1.7.0.dist-info}/LICENSE +1 -0
  42. {femagtools-1.6.8.dist-info → femagtools-1.7.0.dist-info}/METADATA +7 -4
  43. {femagtools-1.6.8.dist-info → femagtools-1.7.0.dist-info}/RECORD +50 -49
  44. tests/engines/__init__.py +0 -20
  45. tests/geom/__init__.py +0 -20
  46. tests/moo/__init__.py +0 -20
  47. tests/test_model.py +8 -1
  48. {femagtools-1.6.8.dist-info → femagtools-1.7.0.dist-info}/WHEEL +0 -0
  49. {femagtools-1.6.8.dist-info → femagtools-1.7.0.dist-info}/entry_points.txt +0 -0
  50. {femagtools-1.6.8.dist-info → femagtools-1.7.0.dist-info}/top_level.txt +0 -0
femagtools/dxfsl/area.py CHANGED
@@ -15,8 +15,8 @@ from .functions import less_equal, less, greater_equal, greater
15
15
  from .functions import distance, alpha_angle, alpha_line, min_angle, max_angle
16
16
  from .functions import point, line_m, line_n, intersect_point, points_are_close
17
17
  from .functions import middle_angle, part_of_circle, is_same_angle
18
- from .functions import area_size
19
- from .shape import Element, Shape, Line, Arc, Circle, is_Circle
18
+ from .functions import area_size, normalise_angle, positive_angle
19
+ from .shape import Element, Shape, Line, Arc, Circle, is_Circle, is_Line, is_Arc
20
20
 
21
21
  logger = logging.getLogger('femagtools.area')
22
22
 
@@ -27,11 +27,26 @@ logger = logging.getLogger('femagtools.area')
27
27
 
28
28
  area_number = 0
29
29
 
30
+ TYPE_AIR = 0
31
+ TYPE_IRON = 1
32
+ TYPE_WINDINGS = 2
33
+ TYPE_MAGNET_AIRGAP = 3
34
+ TYPE_MAGNET_RECT = 4
35
+ TYPE_YOKE = 5
36
+ TYPE_TOOTH = 6
37
+ TYPE_MAGNET_OR_AIR = 8
38
+ TYPE_AIR_OR_IRON = 9
39
+ TYPE_MAGNET_OR_IRON = 9
40
+ TYPE_SHAFT = 10
41
+ TYPE_MAGNET_RECT_NEAR_AIRGAP = 11
42
+ TYPE_WINDINGS_OR_AIR = 12
43
+ TYPE_MAGNET_UNDEFINED = 99
44
+ TYPE_GROUP = 20
30
45
 
31
46
  class Area(object):
32
47
  def __init__(self, area, center, sym_tolerance):
33
48
  self.area = area
34
- self.type = 0 # material
49
+ self.type = -1 # material
35
50
  self.phi = 0.0
36
51
  self.min_angle = 0.0
37
52
  self.max_angle = 0.0
@@ -43,9 +58,13 @@ class Area(object):
43
58
  self.close_to_startangle = False
44
59
  self.close_to_endangle = False
45
60
  self.mag_rectangle = False
61
+ self.mag_width = 0.0
46
62
  self.min_dist = 99999.0
47
63
  self.max_dist = 0.0
48
- self.minmax_xy = [0,0,0,0]
64
+ self.min_x = None
65
+ self.max_x = None
66
+ self.min_y = None
67
+ self.max_y = None
49
68
  self.height = 0.0
50
69
  self.alpha = 0.0
51
70
  self.count = 1
@@ -63,6 +82,7 @@ class Area(object):
63
82
  area_number += 1
64
83
  self.id = area_number
65
84
  self.areas_inside = {}
85
+ self.areas_of_group = []
66
86
 
67
87
  def identifier(self):
68
88
  return "{}-{}".format(self.id, self.type)
@@ -180,88 +200,94 @@ class Area(object):
180
200
  last_point = next_nodes[-1]
181
201
 
182
202
  def legend(self):
183
- if self.type == 1:
203
+ if self.type == TYPE_IRON:
184
204
  return 'Iron'
185
- if self.type == 2:
205
+ if self.type == TYPE_WINDINGS:
186
206
  return 'Windings'
187
- if self.type == 3 or self.type == 4:
207
+ if self.type == TYPE_MAGNET_AIRGAP or self.type == TYPE_MAGNET_RECT:
188
208
  return 'Magnet'
189
- if self.type == 5:
209
+ if self.type == TYPE_YOKE:
190
210
  return 'Yoke'
191
- if self.type == 6:
211
+ if self.type == TYPE_TOOTH:
192
212
  return 'Tooth'
193
- if self.type == 10:
213
+ if self.type == TYPE_SHAFT:
194
214
  return 'Shaft'
195
215
  return ''
196
216
 
197
217
  def name(self):
198
- if self.type == 1:
218
+ if self.type == TYPE_IRON:
199
219
  return 'Iron'
200
- if self.type == 2:
220
+ if self.type == TYPE_WINDINGS:
201
221
  return 'Wndg'
202
- if self.type == 3 or self.type == 4:
222
+ if self.type == TYPE_MAGNET_AIRGAP or self.type == TYPE_MAGNET_RECT:
203
223
  return 'Mag'
204
- if self.type == 5:
224
+ if self.type == TYPE_YOKE:
205
225
  return 'StJo'
206
- if self.type == 6:
226
+ if self.type == TYPE_TOOTH:
207
227
  return 'StZa'
208
- if self.type == 10:
228
+ if self.type == TYPE_SHAFT:
209
229
  return 'Shft'
210
230
  return ''
211
231
 
212
232
  def color(self):
213
- if self.type == 1:
233
+ if self.type == TYPE_IRON:
214
234
  return 'cyan'
215
- if self.type == 2:
235
+ if self.type == TYPE_WINDINGS:
216
236
  return 'green'
217
- if self.type == 3 or self.type == 4:
237
+ if self.type == TYPE_MAGNET_AIRGAP or self.type == TYPE_MAGNET_RECT:
218
238
  return 'red'
219
- if self.type == 5:
239
+ if self.type == TYPE_YOKE:
220
240
  return 'cyan'
221
- if self.type == 6:
241
+ if self.type == TYPE_TOOTH:
222
242
  return 'skyblue'
223
- if self.type == 10:
243
+ if self.type == TYPE_SHAFT:
224
244
  return 'lightgrey'
225
245
  return 'white'
226
246
 
227
247
  def color_alpha(self):
228
- if self.type == 1:
248
+ if self.type == TYPE_IRON:
229
249
  return 0.3
230
- if self.type == 2:
250
+ if self.type == TYPE_WINDINGS:
231
251
  return 1.0
232
- if self.type == 3 or self.type == 4:
252
+ if self.type == TYPE_MAGNET_AIRGAP or self.type == TYPE_MAGNET_RECT:
233
253
  return 1.0
234
- if self.type == 5:
254
+ if self.type == TYPE_YOKE:
235
255
  return 0.5
236
- if self.type == 6:
256
+ if self.type == TYPE_TOOTH:
237
257
  return 1.0
238
- if self.type == 10:
258
+ if self.type == TYPE_SHAFT:
239
259
  return 0.8
240
260
  return 1.0
241
261
 
242
262
  def is_iron(self):
243
- return self.type == 1 or self.type == 5 or self.type == 6
263
+ return \
264
+ self.type == TYPE_IRON or \
265
+ self.type == TYPE_YOKE or \
266
+ self.type == TYPE_TOOTH
244
267
 
245
268
  def is_stator_iron_yoke(self):
246
- return self.type == 5
269
+ return self.type == TYPE_YOKE
247
270
 
248
271
  def is_stator_iron_tooth(self):
249
- return self.type == 6
272
+ return self.type == TYPE_TOOTH
250
273
 
251
274
  def is_rotor_iron(self):
252
- return self.type == 1
275
+ return self.type == TYPE_IRON
253
276
 
254
277
  def is_winding(self):
255
- return self.type == 2
278
+ return self.type == TYPE_WINDINGS
256
279
 
257
280
  def is_magnet(self):
258
- return self.type == 3 or self.type == 4
281
+ return self.type == TYPE_MAGNET_AIRGAP or self.type == TYPE_MAGNET_RECT
259
282
 
260
283
  def is_shaft(self):
261
- return self.type == 10
284
+ return self.type == TYPE_SHAFT
262
285
 
263
286
  def is_air(self):
264
- return self.type == 0
287
+ return self.type == TYPE_AIR
288
+
289
+ def is_type(self, type):
290
+ return self.type == type
265
291
 
266
292
  def set_type(self, t):
267
293
  self.type = t
@@ -270,7 +296,7 @@ class Area(object):
270
296
  if not self.area:
271
297
  return
272
298
 
273
- self.minmax_xy = self.minmax()
299
+ self.min_x, self.max_x, self.min_y, self.max_y = self.minmax()
274
300
  s = self.area[0]
275
301
  mm_angle = s.minmax_angle_from_center(center)
276
302
  self.min_angle = mm_angle[0]
@@ -289,8 +315,10 @@ class Area(object):
289
315
  self.alpha = round(alpha_angle(self.min_angle, self.max_angle), 3)
290
316
 
291
317
  def center_is_inside(self, center):
292
- if self.minmax_xy[0] < center[0] and self.minmax_xy[1] > center[0] and \
293
- self.minmax_xy[2] < center[1] and self.minmax_xy[3] > center[1]:
318
+ if less(self.min_x, center[0], rtol=1e-03, atol=1e-04) and \
319
+ greater(self.max_x, center[0], rtol=1e-03, atol=1e-04) and \
320
+ less(self.min_y, center[1], rtol=1e-03, atol=1e-04) and \
321
+ greater(self.max_y, center[1], rtol=1e-03, atol=1e-04):
294
322
  return True
295
323
  return False
296
324
 
@@ -329,9 +357,8 @@ class Area(object):
329
357
  return False
330
358
 
331
359
  line = Line(Element(start=p1, end=p2))
332
- plist = self.intersect_points(line)
360
+ plist = self.get_intersect_points(line)
333
361
  points = len(plist)
334
- plist.sort()
335
362
 
336
363
  aux_lines = [e for e in self.area if e.has_attribute('auxline')]
337
364
  if aux_lines:
@@ -358,6 +385,15 @@ class Area(object):
358
385
  def is_touching_both_sides(self):
359
386
  return (self.close_to_startangle and self.close_to_endangle)
360
387
 
388
+ def is_in_touch_with_area(self, geom, a):
389
+ n1 = self.area[0].n1
390
+ n2 = a.area[0].n2
391
+ try:
392
+ return nx.has_path(geom.g, n1, n2)
393
+ except nx.NetworkXError:
394
+ logger.warning("has_path() failed")
395
+ return False
396
+
361
397
  def has_connection(self, geom, a, ndec):
362
398
  assert(self.area)
363
399
  assert(a.area)
@@ -635,11 +671,21 @@ class Area(object):
635
671
  return True
636
672
  return False
637
673
 
638
- def intersect_points(self, line):
674
+ def get_intersect_points(self, line, rtol=1.e-4, atol=1e-3):
639
675
  points = []
640
676
  for e in self.area:
641
- points += e.intersect_line(line, include_end=True)
642
- return points
677
+ points += e.intersect_line(line, rtol=rtol, atol=atol, include_end=True)
678
+ if not points:
679
+ return []
680
+ points = [(x, y) for x, y in points]
681
+ points.sort()
682
+ p1 = points[0]
683
+ pts = [p1]
684
+ for p2 in points[1:]:
685
+ if not points_are_close(p1, p2, rtol=rtol, atol=atol):
686
+ pts.append(p2)
687
+ p1 = p2
688
+ return pts
643
689
 
644
690
  def is_point_inside(self, pt):
645
691
  for e in self.area:
@@ -647,14 +693,49 @@ class Area(object):
647
693
  return True
648
694
  return False
649
695
 
696
+ def the_area_is_inside_area(self, a):
697
+ p = a.area[0].n1
698
+ return self.the_point_is_inside_area(p)
699
+
700
+ def the_point_is_inside_area(self, p):
701
+ x, y = p
702
+ if less_equal(x, self.min_x) or greater_equal(x, self.max_x):
703
+ return False
704
+ if less_equal(y, self.min_y) or greater_equal(y, self.max_y):
705
+ return False
706
+
707
+ p1 = (self.min_x - 5, y)
708
+ p2 = (self.max_x + 5, y)
709
+ line = Line(Element(start=p1, end=p2))
710
+ pts = self.get_intersect_points(line)
711
+ if len(pts) % 2 != 0:
712
+ return False
713
+
714
+ c = 0
715
+ for ax, ay in pts:
716
+ if not less(ax, x):
717
+ if c % 2 != 1:
718
+ return False
719
+ break
720
+ c += 1
721
+
722
+ if not c < len(pts):
723
+ return False
724
+
725
+ ax, ay = pts[c]
726
+ if not less(x, ax):
727
+ return False
728
+ return True
729
+
650
730
  def get_best_point_inside(self, geom):
651
- mm = self.minmax_xy
652
- px1 = mm[0]-5
653
- px2 = mm[1]+5
731
+ px1 = self.min_x - 5
732
+ px2 = self.max_x + 5
654
733
 
655
- y_dist = mm[3] - mm[2]
734
+ y_dist = self.max_y - self.min_y
656
735
  step = y_dist / 6
657
- y_list = np.arange(mm[2] + step*0.3, mm[3] - step*0.3, step)
736
+ y_list = np.arange(self.min_y + step * 0.3,
737
+ self.max_y - step * 0.3,
738
+ step)
658
739
 
659
740
  lines = []
660
741
  for y in y_list:
@@ -745,10 +826,9 @@ class Area(object):
745
826
 
746
827
  def get_point_inside(self, geom):
747
828
  """return point inside area"""
748
- mm = self.minmax_xy
749
- y = (mm[2]+mm[3])/2
750
- p1 = (mm[0]-5, y)
751
- p2 = (mm[1]+5, y)
829
+ y = (self.min_y + self.max_y) / 2
830
+ p1 = (self.min_x - 5, y)
831
+ p2 = (self.max_x + 5, y)
752
832
  line = Line(Element(start=p1, end=p2))
753
833
 
754
834
  points = []
@@ -820,6 +900,32 @@ class Area(object):
820
900
  renderer.fill(x, y, color, alpha)
821
901
  return True
822
902
 
903
+ def magnet_arrow_length(self):
904
+ if self.is_type(TYPE_MAGNET_AIRGAP):
905
+ return (self.max_dist - self.min_dist) * 0.9
906
+ if self.is_type(TYPE_MAGNET_RECT):
907
+ return self.mag_width
908
+ return 0.0
909
+
910
+ def render_magnet_phi(self, renderer, length):
911
+ if not self.is_magnet():
912
+ return
913
+ p1 = None
914
+ p2 = None
915
+ if self.is_type(TYPE_MAGNET_AIRGAP):
916
+ mid = middle_angle(self.min_angle, self.max_angle)
917
+ d = self.min_dist + (self.max_dist - self.min_dist) * 0.3
918
+ p1 = point((0.0, 0.0), d, mid)
919
+ if self.is_type(TYPE_MAGNET_RECT):
920
+ x = self.min_x + (self.max_x - self.min_x) / 2
921
+ y = self.min_y + (self.max_y - self.min_y) / 2
922
+ p1 = (x, y)
923
+ if p1 is None:
924
+ return
925
+ p2 = point(p1, length, self.phi)
926
+ renderer.arrow(p1, p2, linewidth=1.2)
927
+ return
928
+
823
929
  def render_legend(self, renderer):
824
930
  return renderer.new_legend_handle(self.color(),
825
931
  self.color_alpha(),
@@ -867,25 +973,16 @@ class Area(object):
867
973
  return False
868
974
  return True
869
975
 
870
- def has_round_edges(self):
871
- arcs = 0
872
- for e in self.area:
873
- # if isinstance(e, Line):
874
- # if not np.isclose(angle, alpha_line(center, e.p1)):
875
- # return False
876
- # if not np.isclose(angle, alpha_line(center, e.p2)):
877
- # return False
878
- if isinstance(e, Arc):
879
- arcs += 1
880
-
881
- return arcs > 0
882
-
883
976
  def is_shaft_area(self, center):
884
977
  logger.debug("Begin of check shaft")
885
978
 
886
- if not self.is_touching_both_sides():
887
- logger.debug("End of check shaft: don't touch both sides")
888
- return False
979
+ #if not self.is_touching_both_sides():
980
+ # logger.debug("End of check shaft: don't touch both sides")
981
+ # return False
982
+
983
+ if np.isclose(0.0, self.min_dist, rtol=1e-6, atol=1e-4):
984
+ logger.debug("End of check shaft: ok (node in center)")
985
+ return True
889
986
 
890
987
  for n in self.list_of_nodes():
891
988
  a = alpha_line(center, n)
@@ -900,247 +997,143 @@ class Area(object):
900
997
  continue
901
998
  logger.debug("End of check shaft: no")
902
999
  return False
1000
+
903
1001
  logger.debug("End of check shaft: ok")
904
1002
  return True
905
1003
 
906
- def is_rectangle(self):
907
- lines = [[c, e.m(99999.0), e.length()]
908
- for c, e in enumerate(self.area)
909
- if isinstance(e, Line)]
910
- lines.sort()
911
-
912
- line_count = 1
913
- m_first = 0.0
914
- m_prev = 999.999999
915
- c_prev = -99
916
- m_all = []
917
- for c, m, l in lines:
918
- if c_prev >= 0:
919
- if np.isclose(m_prev, m, atol=0.001):
920
- if c_prev+1 != c:
921
- # Gleiche Steigung, aber keine Verlängerung
922
- line_count += 1
923
- m_all.append(m_prev)
924
- else:
925
- line_count += 1
926
- m_all.append(m_prev)
927
- else:
928
- m_first = m
1004
+ def get_magnet_line_angles(self):
1005
+ lines = [e for e in self.area if is_Line(e)]
1006
+ if len(lines) < 4:
1007
+ logger.debug("get_magnet_line_angles: only %s lines", len(lines))
1008
+ return []
929
1009
 
930
- m_prev = m
931
- c_prev = c
1010
+ angles = []
1011
+ prev_angle = lines[0].get_positive_angle()
1012
+ logger.debug("first angle = %s", prev_angle)
1013
+ prev_length = lines[0].length()
932
1014
 
933
- m_all.append(m_prev)
1015
+ for line in lines[1:]:
1016
+ this_angle = line.get_positive_angle()
1017
+ logger.debug("next angle = %s", this_angle)
1018
+ this_length = line.length()
934
1019
 
935
- if np.isclose(m_prev, m_first, atol=0.001):
936
- line_count -= 1
1020
+ if np.isclose(prev_angle, this_angle, rtol=1e-04, atol=1e-02):
1021
+ # same direction
1022
+ prev_length += this_length
1023
+ else:
1024
+ angles.append((prev_length, prev_angle))
1025
+ prev_angle = this_angle
1026
+ prev_length = this_length
937
1027
 
938
- if line_count == 4:
939
- logger.debug("is_rectangle: m={}".format(m_all))
940
- if not np.isclose(m_all[0], m_all[2], atol=0.001):
941
- return False
942
- if not np.isclose(m_all[1], m_all[3], atol=0.001):
943
- return False
944
- return True
1028
+ if not angles:
1029
+ logger.debug("get_magnet_line_angles: only one angle")
1030
+ return []
945
1031
 
946
- return False
1032
+ this_length, this_angle = angles[0]
1033
+ if not np.isclose(prev_angle, this_angle, rtol=1e-04, atol=1e-02):
1034
+ angles.append((prev_length, prev_angle))
1035
+ else:
1036
+ prev_length += this_length
1037
+ angles[0] = (prev_length, prev_angle)
947
1038
 
948
- def is_mag_rectangle(self):
949
- lines_ceml = [[c, e, e.m(99999.0), e.length()]
950
- for c, e in enumerate(self.area)]
951
- # c = Count
952
- # e = Element
953
- # m = Steigung
954
- # l = Länge
955
- # L = class Line
956
- if len(lines_ceml) < 4:
957
- return False
1039
+ l, first_angle = angles[0]
1040
+ l, last_angle = angles[-1]
1041
+ if np.isclose(first_angle, last_angle, rtol=1e-04, atol=1e-02):
1042
+ del angles[-1]
1043
+ return angles
958
1044
 
959
- logger.debug("=== BEGIN OF is_mag_rectangle() [{} lines]"
960
- .format(len(lines_ceml)))
961
-
962
- c_prev = lines_ceml[0][0]
963
- a_prev = 999
964
- p = None
965
-
966
- e0 = lines_ceml[0][1]
967
- e0_p1 = e0.p1
968
- e0_p2 = e0.p2
969
- L_prev = isinstance(e0, Line)
970
- l_prev = lines_ceml[0][3]
971
- m_prev = lines_ceml[0][2]
972
-
973
- e1 = lines_ceml[1][1]
974
- e1_p1 = e1.p1
975
- e1_p2 = e1.p2
976
-
977
- if (points_are_close(e0_p2, e1_p1, atol=1e-02) or
978
- points_are_close(e0_p2, e1_p2, atol=1e-02)):
979
- a_prev = alpha_line(e0_p1, e0_p2)
980
- p = e0_p2
981
- elif (points_are_close(e0_p1, e1_p1, atol=1e-02) or
982
- points_are_close(e0_p1, e1_p2, atol=1e-02)):
983
- a_prev = alpha_line(e0_p2, e0_p1)
984
- p = e0_p1
985
- else:
986
- logger.error(
987
- "ERROR: is_mag_rectangle(): points are not close together")
988
- logger.error(" e0 p1={}, p2={}".format(e0_p1, e0_p2))
989
- logger.error(" e1 p1={}, p2={}".format(e1_p1, e1_p2))
990
- return False
1045
+ def get_magnet_phi(self, angles):
1046
+ if not angles:
1047
+ return 0.0
991
1048
 
992
- def alpha_current(p, e):
993
- if points_are_close(p, e.p1, atol=1e-02):
994
- return e.p2, alpha_line(e.p1, e.p2), isinstance(e, Line)
995
- if points_are_close(p, e.p2, atol=1e-02):
996
- return e.p1, alpha_line(e.p2, e.p1), isinstance(e, Line)
997
- logger.error(
998
- "ERROR: is_mag_rectangle(): points are not close together")
999
- logger.error(" p={}, p1={}, p2={}".format(p, e.p1, e.p2))
1000
- return None, None, False
1001
-
1002
- lines_clamL = []
1003
- for c, e, m, l in lines_ceml[1:]:
1004
- p, a_curr, L_curr = alpha_current(p, e)
1005
- if not p:
1006
- return False
1049
+ angles.sort(reverse=True)
1050
+ # calculate orientation (no rectangle check)
1051
+ l, alpha = angles[0]
1052
+ phi = normalise_angle(alpha + np.pi/2)
1053
+ logger.debug("alpha = %s, phi = %s", alpha, phi)
1007
1054
 
1008
- if is_same_angle(a_prev, a_curr, atol=0.01):
1009
- # its the same angle and both are Lines
1010
- # assert(np.isclose(m_prev, m, atol=0.001))
1011
- if c_prev+1 != c:
1012
- logger.debug(" - ok, but not an extension")
1013
- # ..., but not an extension
1014
- lines_clamL.append([c_prev, l_prev, a_prev, m])
1015
- l_prev = e.length()
1016
- else:
1017
- # ... and an extension
1018
- l_prev += e.length()
1019
- logger.debug(" - ok, it's an extension")
1020
- else:
1021
- # it's a different angle
1022
- logger.debug(" - diff, angle {} and {} not equal "
1023
- .format(a_prev, a_curr))
1024
- lines_clamL.append([c_prev, l_prev, a_prev, m_prev, L_prev])
1025
- l_prev = e.length()
1026
-
1027
- a_prev = a_curr
1028
- L_prev = L_curr
1029
- m_prev = m
1030
- c_prev = c
1031
-
1032
- lines_clamL.append([c_prev, l_prev, a_prev, m_prev, L_prev])
1033
- if np.isclose(lines_clamL[0][2], lines_clamL[-1][2], atol=0.001):
1034
- # Gleicher Winkel am Anfang und am Ende
1035
- lines_clamL[0][1] += lines_clamL[-1][1] # length
1036
- del lines_clamL[-1]
1037
- logger.debug(" > last entry deleted")
1038
-
1039
- if len(lines_clamL) < 4:
1040
- logger.debug("=== END OF is_mag_rectangle(): NO RECTANGLE #1")
1041
- return False
1055
+ mid = middle_angle(self.min_angle, self.max_angle)
1056
+ angle = alpha_angle(mid, phi)
1057
+ logger.debug("phi=%s, mid=%s, angle=%s", phi, mid, angle)
1042
1058
 
1043
- lines_lmcL = [[l, m, c, L] for c, l, a, m, L in lines_clamL]
1044
- lines_lmcL.sort(reverse=True)
1045
-
1046
- if not np.isclose(lines_lmcL[0][1], lines_lmcL[1][1], atol=0.05):
1047
- # Die Steigungen der zwei längsten Linien müssen gleich sein
1048
- logger.debug("--- m %s <> %s ---",
1049
- lines_lmcL[0][1],
1050
- lines_lmcL[1][1])
1051
- logger.debug("--- l %s, %s, %s ---",
1052
- lines_lmcL[0][0],
1053
- lines_lmcL[1][0],
1054
- lines_lmcL[2][0])
1055
- logger.debug("=== END OF is_mag_rectangle(): NO RECTANGLE #2")
1056
- return False
1059
+ if greater(angle, np.pi * 0.5, rtol=1e-5) and \
1060
+ less(angle, np.pi * 1.5, rtol=1e-5):
1061
+ phi = normalise_angle(phi + np.pi)
1057
1062
 
1058
- def excursion_to_same_direction(clam):
1059
- if len(clam) < 4:
1060
- return False
1063
+ logger.debug("phi of magnet %s is %s", self.identifier(), phi)
1064
+ return phi
1061
1065
 
1062
- alpha = alpha_angle(clam[0][2], clam[1][2])
1063
- clockwise = not alpha < np.pi
1066
+ def get_magnet_orientation(self):
1067
+ logger.debug("get magnet orientation for %s", self.identifier())
1068
+ if self.is_type(TYPE_MAGNET_RECT):
1069
+ angles = self.get_magnet_line_angles()
1070
+ return self.get_magnet_phi(angles)
1064
1071
 
1065
- angle_prev = clam[1][2]
1066
- for c, l, angle_curr, m, t in clam[2:]:
1067
- alpha = alpha_angle(angle_prev, angle_curr)
1068
- if clockwise:
1069
- if alpha < np.pi:
1070
- return False
1072
+ if self.is_type(TYPE_MAGNET_AIRGAP):
1073
+ if self.close_to_endangle:
1074
+ if self.close_to_startangle:
1075
+ return middle_angle(self.min_angle, self.max_angle)
1071
1076
  else:
1072
- if alpha > np.pi:
1073
- return False
1074
- angle_prev = angle_curr
1075
- return True # end of all_lines_with_same_direction()
1077
+ return self.max_angle
1078
+ return middle_angle(self.min_angle, self.max_angle)
1076
1079
 
1077
- lines_cmL = [[c, m, L] for l, m, c, L in lines_lmcL[0:4]]
1078
- lines_cmL.sort()
1080
+ return 0.0
1079
1081
 
1080
- if np.isclose(lines_cmL[0][1], lines_cmL[2][1], atol=0.001):
1081
- if not (lines_cmL[0][2] and lines_cmL[2][2]):
1082
- logger.debug("=== END OF is_mag_rectangle(): not 2 lines #1")
1083
- return False
1084
- ok = excursion_to_same_direction(lines_clamL)
1085
- logger.debug("=== END OF is_mag_rectangle(): OK = {} #1"
1086
- .format(ok))
1087
- return ok
1088
- if np.isclose(lines_cmL[1][1], lines_cmL[3][1], atol=0.001):
1089
- if not (lines_cmL[1][2] and lines_cmL[3][2]):
1090
- logger.debug("=== END OF is_mag_rectangle(): not 2 lines #2")
1091
- return False
1082
+ def is_magnet_rectangle(self):
1083
+ angles = self.get_magnet_line_angles()
1092
1084
 
1093
- ok = excursion_to_same_direction(lines_clamL)
1094
- logger.debug("=== END OF is_mag_rectangle(): OK = {} #2"
1095
- .format(ok))
1096
- return ok
1085
+ if len(angles) != 4:
1086
+ logger.debug("is_magnet_rectangle: %s angles, not 4", len(angles))
1087
+ return False
1097
1088
 
1098
- logger.debug("=== END OF is_mag_rectangle(): NO RECTANGLE #3")
1099
- return False
1089
+ for l, a in angles:
1090
+ logger.debug("+ magnet_rectangle: alpha=%s, length=%s", a, l)
1100
1091
 
1101
- def get_mag_orient_rectangle(self):
1102
- lines = [[e.m(99999.0), e.length(), alpha_line(e.p1, e.p2)]
1103
- for e in self.area
1104
- if isinstance(e, Line)]
1105
- lines.sort()
1106
-
1107
- m_prev = 999.999999
1108
- a_prev = 0.0
1109
- l_total = 0.0
1110
- line_length = []
1111
- for m, l, a in lines:
1112
- if np.isclose(m_prev, m):
1113
- l_total += l
1114
- else:
1115
- if l_total > 0.0:
1116
- line_length.append((l_total, m_prev, a_prev))
1117
- l_total = l
1118
- m_prev = m
1119
- a_prev = a
1120
-
1121
- if l_total > 0.0:
1122
- line_length.append((l_total, m_prev, a_prev))
1123
- line_length.sort(reverse=True)
1124
-
1125
- alpha = line_length[0][2]
1126
- if alpha < 0.0:
1127
- alpha += np.pi
1128
- alpha = alpha + np.pi/2
1129
- if alpha > np.pi:
1130
- alpha = alpha - np.pi
1131
- return alpha
1132
-
1133
- def get_mag_orientation(self):
1134
- if self.mag_rectangle:
1135
- return self.get_mag_orient_rectangle()
1092
+ length_0, angle_0 = angles[0]
1093
+ length_1, angle_1 = angles[1]
1094
+ length_2, angle_2 = angles[2]
1095
+ length_3, angle_3 = angles[3]
1136
1096
 
1137
- if self.close_to_endangle:
1138
- if self.close_to_startangle:
1139
- return middle_angle(self.min_angle, self.max_angle)
1140
- else:
1141
- return self.max_angle
1097
+ if not np.isclose(angle_0, angle_2, rtol=1e-03, atol=0.05):
1098
+ logger.debug("is_magnet_rectangle: angles %s and %s not equal",
1099
+ angle_0, angle_2)
1100
+ return False
1101
+
1102
+ if not np.isclose(angle_1, angle_3, rtol=1e-03, atol=0.05):
1103
+ logger.debug("is_magnet_rectangle: angles %s and %s not equal",
1104
+ angle_1, angle_3)
1105
+ return False
1106
+
1107
+ if angle_0 > angle_1:
1108
+ a0 = angle_0
1109
+ a1 = angle_1 + np.pi/2
1142
1110
  else:
1143
- return middle_angle(self.min_angle, self.max_angle)
1111
+ a0 = angle_1
1112
+ a1 = angle_0 + np.pi/2
1113
+ if not np.isclose(a0, a1, rtol=1e-03, atol=0.05):
1114
+ logger.debug("is_magnet_rectangle: not a rectange (%s neq %s)", a0, a1)
1115
+ return False
1116
+
1117
+ turn_left = False
1118
+ turn_right = False
1119
+ for n1, n2, e in self.list_of_elements():
1120
+ if is_Arc(e):
1121
+ if e.get_node_number(n1) == 1:
1122
+ turn_left = True
1123
+ else:
1124
+ turn_right = True
1125
+
1126
+ if turn_left and turn_right:
1127
+ logger.debug("is_magnet_rectangle: arcs with different directions")
1128
+ return False
1129
+
1130
+ self.phi = self.get_magnet_phi(angles)
1131
+ angles.sort()
1132
+ l, alpha = angles[0]
1133
+ self.mag_width = l
1134
+ logger.debug("Area %s is a rectangle with phi %s",
1135
+ self.identifier(), self.phi)
1136
+ return True
1144
1137
 
1145
1138
  def around_windings(self, areas, geom):
1146
1139
  for a in areas:
@@ -1164,6 +1157,10 @@ class Area(object):
1164
1157
  return True
1165
1158
  return False
1166
1159
 
1160
+ def is_close_to_border(self, angle, border_angle):
1161
+ return np.isclose(angle, border_angle,
1162
+ rtol=1e-03, atol=1e-03)
1163
+
1167
1164
  def mark_stator_subregions(self,
1168
1165
  is_inner,
1169
1166
  stator_size,
@@ -1175,7 +1172,7 @@ class Area(object):
1175
1172
  alpha = round(alpha, 6)
1176
1173
 
1177
1174
  if self.is_circle():
1178
- self.type = 0 # air
1175
+ self.type = TYPE_AIR # air
1179
1176
  return self.type
1180
1177
 
1181
1178
  ag_delta = (r_out - r_in) / 500.0
@@ -1216,24 +1213,24 @@ class Area(object):
1216
1213
 
1217
1214
  if self.has_iron_separator():
1218
1215
  logger.debug("***** iron (has iron separator)\n")
1219
- self.type = 1 # iron
1216
+ self.type = TYPE_IRON # iron
1220
1217
  return self.type
1221
1218
 
1222
1219
  if is_inner:
1223
1220
  # looking for shaft
1224
1221
  if close_to_opposition and not self.close_to_ag:
1225
1222
  if self.is_shaft_area(center):
1226
- self.type = 10 # shaft
1223
+ self.type = TYPE_SHAFT # shaft
1227
1224
  logger.debug("***** shaft (close to opposition)\n")
1228
1225
  return self.type
1229
1226
 
1230
1227
  if close_to_opposition:
1231
- self.type = 5 # iron yoke (Joch)
1228
+ self.type = TYPE_YOKE # iron yoke (Joch)
1232
1229
  logger.debug("***** iron yoke #1\n")
1233
1230
  return self.type
1234
1231
 
1235
1232
  if self.close_to_startangle and self.close_to_endangle:
1236
- self.type = 5 # iron yoke (Joch)
1233
+ self.type = TYPE_YOKE # iron yoke (Joch)
1237
1234
  logger.debug("***** iron yoke #2\n")
1238
1235
  return self.type
1239
1236
 
@@ -1250,21 +1247,21 @@ class Area(object):
1250
1247
 
1251
1248
  if self.alpha / air_alpha > 2:
1252
1249
  logger.debug("***** windings near airgap\n")
1253
- self.type = 2 # windings
1250
+ self.type = TYPE_WINDINGS # windings
1254
1251
  else:
1255
- self.type = 9 # air or iron near windings and near airgap?
1252
+ self.type = TYPE_AIR_OR_IRON # air or iron near windings and near airgap?
1256
1253
  logger.debug("***** air or iron ??\n")
1257
1254
  return self.type
1258
1255
 
1259
1256
  if self.close_to_startangle:
1260
1257
  if self.is_half_circle(center, self.min_angle):
1261
- self.type = 0 # air
1258
+ self.type = TYPE_AIR # air
1262
1259
  logger.debug("***** air (part of a circle)\n")
1263
1260
  return self.type
1264
1261
 
1265
1262
  if self.close_to_endangle:
1266
1263
  if self.is_half_circle(center, self.max_angle):
1267
- self.type = 0 # air
1264
+ self.type = TYPE_AIR # air
1268
1265
  logger.debug("***** air (part of a circle)\n")
1269
1266
  return self.type
1270
1267
 
@@ -1282,32 +1279,32 @@ class Area(object):
1282
1279
  if self.min_angle > 0.001:
1283
1280
  if self.max_angle < alpha - 0.001:
1284
1281
  if bad_winding_position():
1285
- self.type = 12 # windings or air
1282
+ self.type = TYPE_WINDINGS_OR_AIR # windings or air
1286
1283
  logger.debug("***** windings or air #1\n")
1287
1284
  else:
1288
- self.type = 2 # windings
1285
+ self.type = TYPE_WINDINGS # windings
1289
1286
  logger.debug("***** windings #1\n")
1290
1287
  return self.type
1291
1288
  if mirrored:
1292
1289
  if bad_winding_position():
1293
- self.type = 12 # windings or air
1290
+ self.type = TYPE_WINDINGS_OR_AIR # windings or air
1294
1291
  logger.debug("***** windings or air #2\n")
1295
1292
  else:
1296
- self.type = 2 # windings
1293
+ self.type = TYPE_WINDINGS # windings
1297
1294
  logger.debug("***** windings #2\n")
1298
1295
  return self.type
1299
1296
 
1300
- self.type = 0 # air
1297
+ self.type = TYPE_AIR # air
1301
1298
  logger.debug("***** air #3")
1302
1299
 
1303
1300
  if self.close_to_startangle or self.close_to_endangle:
1304
1301
  f = self.surface / stator_size
1305
1302
  if f < 0.02: # area_size less then 2 percent of stator size
1306
1303
  # Luftloch
1307
- self.type = 0 # air
1304
+ self.type = TYPE_AIR # air
1308
1305
  logger.debug("***** small area => air\n")
1309
1306
  else:
1310
- self.type = 9 # air or iron near windings and near airgap?
1307
+ self.type = TYPE_AIR_OR_IRON # air or iron near windings and near airgap?
1311
1308
  logger.debug("***** air or iron close to border\n")
1312
1309
  return self.type
1313
1310
 
@@ -1315,32 +1312,34 @@ class Area(object):
1315
1312
  return 0
1316
1313
 
1317
1314
  def mark_rotor_subregions(self, is_inner, mirrored, alpha,
1318
- center, r_in, r_out):
1315
+ center, r_in, r_out,
1316
+ startangle,
1317
+ endangle):
1319
1318
  logger.debug("mark_rotor_subregions")
1320
1319
 
1321
1320
  alpha = round(alpha, 6)
1322
1321
 
1323
1322
  if self.is_circle():
1324
- self.type = 0 # air
1323
+ self.type = TYPE_AIR # air
1325
1324
  logger.debug(">>> air is a circle")
1326
1325
  return self.type
1327
1326
 
1328
1327
  if is_inner:
1329
- self.close_to_ag = np.isclose(r_out, self.max_dist, atol=0.005)
1328
+ self.close_to_ag = np.isclose(r_out, self.max_dist, rtol=1e-9, atol=0.005)
1330
1329
  close_to_opposition = greater_equal(r_in * 1.05, self.min_dist)
1331
1330
  airgap_radius = r_out
1332
1331
  opposite_radius = r_in
1333
1332
  airgap_toleranz = -(self.max_dist - self.min_dist) / 50.0 # 2%
1334
1333
  else:
1335
- self.close_to_ag = np.isclose(r_in, self.min_dist, atol=0.005)
1334
+ self.close_to_ag = np.isclose(r_in, self.min_dist, rtol=1e-9, atol=0.005)
1336
1335
  close_to_opposition = greater_equal(self.max_dist * 1.05, r_out)
1337
1336
  airgap_radius = r_in
1338
1337
  opposite_radius = r_out
1339
1338
  airgap_toleranz = (self.max_dist - self.min_dist) / 50.0 # 2%
1340
1339
 
1341
- self.close_to_startangle = np.isclose(self.min_angle, 0.0,
1340
+ self.close_to_startangle = np.isclose(self.min_angle, startangle,
1342
1341
  1e-04, 1e-04)
1343
- self.close_to_endangle = np.isclose(self.max_angle, alpha,
1342
+ self.close_to_endangle = np.isclose(self.max_angle, endangle,
1344
1343
  1e-04, 1e-04)
1345
1344
 
1346
1345
  logger.debug("\n***** mark_rotor_subregions [{}] *****"
@@ -1359,28 +1358,29 @@ class Area(object):
1359
1358
 
1360
1359
  if self.has_iron_separator():
1361
1360
  logger.debug("***** iron (has iron separator)\n")
1362
- self.type = 1 # iron
1361
+ self.type = TYPE_IRON # iron
1363
1362
  return self.type
1364
1363
 
1365
1364
  if is_inner:
1366
1365
  # looking for shaft
1367
1366
  if close_to_opposition and not self.close_to_ag:
1367
+ logger.debug("-- check for shaft")
1368
1368
  if self.is_shaft_area(center):
1369
- self.type = 10 # shaft
1369
+ self.type = TYPE_SHAFT # shaft
1370
1370
  logger.debug("***** shaft (close to opposition)\n")
1371
1371
  return self.type
1372
1372
 
1373
1373
  if close_to_opposition:
1374
- self.type = 1 # iron
1374
+ self.type = TYPE_IRON # iron
1375
1375
  logger.debug("***** iron (close to opposition)\n")
1376
1376
  return self.type
1377
1377
 
1378
1378
  if self.close_to_startangle and self.close_to_endangle:
1379
- self.type = 1 # iron
1379
+ self.type = TYPE_IRON # iron
1380
1380
  logger.debug("***** iron (close to both sides)\n")
1381
1381
  return self.type
1382
1382
 
1383
- self.mag_rectangle = self.is_mag_rectangle()
1383
+ self.mag_rectangle = self.is_magnet_rectangle()
1384
1384
 
1385
1385
  if self.close_to_ag:
1386
1386
  mm = self.minmax_angle_dist_from_center(center,
@@ -1389,47 +1389,50 @@ class Area(object):
1389
1389
  air_alpha = round(alpha_angle(mm[0], mm[1]), 3)
1390
1390
  logger.debug(" - air_alpha : {}".format(air_alpha))
1391
1391
 
1392
+ if self.mag_rectangle:
1393
+ self.type = TYPE_MAGNET_RECT_NEAR_AIRGAP # magnet near airgap
1394
+ logger.debug("***** magnet (airgap, embedded, phi={})\n".
1395
+ format(self.phi))
1396
+ return self.type
1397
+
1392
1398
  if air_alpha / alpha < 0.2:
1393
- self.phi = self.get_mag_orientation()
1394
- self.type = 8 # air or magnet ?
1399
+ self.type = TYPE_MAGNET_OR_AIR # air or magnet ?
1395
1400
  logger.debug("***** air #1 (close to airgap)\n")
1396
1401
  return self.type
1397
1402
 
1398
1403
  if air_alpha / alpha > 0.6:
1399
- self.phi = self.get_mag_orientation()
1400
- self.type = 3 # magnet
1404
+ self.type = TYPE_MAGNET_AIRGAP # magnet (no rectangle)
1401
1405
  logger.debug("***** magnet (close to airgap)\n")
1402
1406
  else:
1403
- self.phi = self.get_mag_orientation()
1404
- self.type = 9 # iron or magnet ?
1407
+ self.type = TYPE_MAGNET_OR_IRON # iron or magnet ?
1405
1408
  logger.debug("***** iron or magnet(close to airgap)\n")
1406
1409
  return self.type
1407
1410
 
1408
1411
  if self.mag_rectangle:
1409
- self.phi = self.get_mag_orientation()
1410
- self.type = 4 # magnet embedded
1412
+ # phi is already calculated and set
1413
+ self.type = TYPE_MAGNET_RECT # magnet embedded
1411
1414
  logger.debug("***** magnet (embedded, phi={})\n".format(
1412
1415
  self.phi))
1413
1416
  return self.type
1414
1417
 
1415
1418
  if not (self.close_to_startangle or self.close_to_endangle):
1416
- self.type = 0 # air
1419
+ self.type = TYPE_AIR # air
1417
1420
  logger.debug("***** air (somewhere)\n")
1418
1421
  return self.type
1419
1422
 
1420
1423
  if self.close_to_startangle:
1421
1424
  if self.is_half_circle(center, self.min_angle):
1422
- self.type = 0 # air
1425
+ self.type = TYPE_AIR # air
1423
1426
  logger.debug("***** air (part of a circle)\n")
1424
1427
  return self.type
1425
1428
 
1426
1429
  if self.close_to_endangle:
1427
1430
  if self.is_half_circle(center, self.max_angle):
1428
- self.type = 0 # air
1431
+ self.type = TYPE_AIR # air
1429
1432
  logger.debug("***** air (part of a circle)\n")
1430
1433
  return self.type
1431
1434
 
1432
- self.type = 0 # air
1435
+ self.type = TYPE_AIR # air
1433
1436
  logger.debug("***** air (remains)\n")
1434
1437
  return self.type
1435
1438
 
@@ -1438,33 +1441,32 @@ class Area(object):
1438
1441
  logger.debug("mark_unknown_subregions")
1439
1442
 
1440
1443
  if self.is_circle():
1441
- self.type = 0 # air
1444
+ self.type = TYPE_AIR # air
1442
1445
  logger.debug(">>> air is a circle")
1443
1446
  return self.type
1444
1447
 
1445
1448
  self.close_to_startangle = np.isclose(self.min_angle, 0.0)
1446
1449
  self.close_to_endangle = np.isclose(self.max_angle, alpha)
1447
1450
 
1448
- if self.is_mag_rectangle():
1449
- self.type = 4 # magnet embedded
1451
+ if self.is_magnet_rectangle():
1452
+ self.type = TYPE_MAGNET_RECT # magnet embedded
1450
1453
  logger.debug(">>> magnet embedded")
1451
- self.phi = self.get_mag_orient_rectangle()
1452
1454
  return self.type
1453
1455
 
1454
1456
  close_to_max_radius = np.isclose(r_out, self.max_dist)
1455
1457
  close_to_min_radius = np.isclose(r_in, self.min_dist)
1456
1458
 
1457
1459
  if close_to_max_radius and close_to_min_radius:
1458
- self.type = 1 # iron
1460
+ self.type = TYPE_IRON # iron
1459
1461
  logger.debug(">>> iron close to min- and max-radius")
1460
1462
  return self.type
1461
1463
 
1462
1464
  if self.close_to_startangle and self.close_to_endangle:
1463
- self.type = 1 # iron
1465
+ self.type = TYPE_IRON # iron
1464
1466
  logger.debug(">>> iron close to start- and end-angle")
1465
1467
  return self.type
1466
1468
 
1467
- self.type = 0 # air
1469
+ self.type = TYPE_AIR # air
1468
1470
  logger.debug(">>> air remains")
1469
1471
  return self.type
1470
1472