femagtools 1.8.18__py3-none-any.whl → 1.8.20__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.18'
5
+ __version__ = '1.8.20'
6
6
  __author__ = 'Ronald Tanner'
7
7
  __license__ = 'BSD'
8
8
  __copyright__ = 'Copyright 2023-2025 Gamma Technology'
femagtools/bch.py CHANGED
@@ -271,7 +271,7 @@ class Reader:
271
271
  return self.flux[0]['displ'][1]-self.flux[0]['displ'][0]
272
272
  return None
273
273
 
274
- def read(self, content):
274
+ def read(self, content, **kwargs):
275
275
  """read bch file
276
276
 
277
277
  Args:
@@ -297,7 +297,10 @@ class Reader:
297
297
  if k == title2[:len(k)]:
298
298
  title = title2[:len(k)]
299
299
  if title in self.dispatch:
300
- self.dispatch[title](self, s)
300
+ if title == 'Airgap Induction Br':
301
+ self.dispatch[title](self, s, kwargs.get('ignore_airgap_induction_with_c_step_section', True))
302
+ else:
303
+ self.dispatch[title](self, s)
301
304
 
302
305
  if len(self.weights) > 0:
303
306
  w = list(zip(*self.weights))
@@ -887,7 +890,7 @@ class Reader:
887
890
  self.flux_fft[self.wdg] = []
888
891
  self.flux_fft[self.wdg].append(flux_fft)
889
892
 
890
- def __read_airgapInduction(self, content):
893
+ def __read_airgapInduction(self, content, ignore_c_step_section=True):
891
894
  "read and append airgapInduction section"
892
895
  import scipy.integrate as si
893
896
  import math
@@ -918,7 +921,9 @@ class Reader:
918
921
  i1beta = True
919
922
  continue
920
923
  if line.startswith("C_STEP"):
921
- return # ignore this section
924
+ if ignore_c_step_section:
925
+ return # ignore this section
926
+ continue
922
927
  try:
923
928
  rec = self.__findNums(line)
924
929
  if len(rec) == 10:
@@ -966,6 +971,10 @@ class Reader:
966
971
  self.airgapInduction['iq'] = iq
967
972
  self.airgapInduction['id'] = id
968
973
  nrows = len(self.airgapInduction['id'])
974
+ if not ignore_c_step_section:
975
+ # min 1 required (section with C_STEP)
976
+ nrows = max(1, nrows)
977
+ ncols = max(1, ncols)
969
978
 
970
979
  try:
971
980
  self.airgapInduction['an'] = [np.reshape(an[j][:nrows*ncols],
@@ -999,6 +1008,14 @@ class Reader:
999
1008
  [1:])
1000
1009
  self.airgapInduction['Bm'] = zip(*zip(*self.airgapInduction['Bm'])
1001
1010
  [1:])
1011
+ elif not ignore_c_step_section:
1012
+ # section with C_STEP and a single data linex
1013
+ self.airgapInduction['an'] = [self.airgapInduction['an'][i][0:]
1014
+ for i in range(4)]
1015
+ self.airgapInduction['bn'] = [self.airgapInduction['bn'][i][0:]
1016
+ for i in range(4)]
1017
+ self.airgapInduction['Ba'] = self.airgapInduction['Ba'][0:]
1018
+ self.airgapInduction['Bm'] = self.airgapInduction['Bm'][0:]
1002
1019
  except ValueError:
1003
1020
  print(self.airgapInduction['i1'])
1004
1021
 
@@ -1858,12 +1875,12 @@ class Reader:
1858
1875
  return self.__str__()
1859
1876
 
1860
1877
 
1861
- def read(filename):
1878
+ def read(filename, **kwargs):
1862
1879
  """Read BCH/BATCH results from file *filename*."""
1863
1880
  import io
1864
1881
  bchresults = Reader()
1865
1882
  with io.open(filename, encoding='latin1', errors='ignore') as f:
1866
- bchresults.read(f.readlines())
1883
+ bchresults.read(f.readlines(), **kwargs)
1867
1884
  return bchresults
1868
1885
 
1869
1886
 
femagtools/dxfsl/area.py CHANGED
@@ -105,6 +105,12 @@ class Area(object):
105
105
  def elements(self):
106
106
  return self.area
107
107
 
108
+ def is_element_in_area(self, el):
109
+ for e in self.elements():
110
+ if el.has_same_nodes(e):
111
+ return True
112
+ return False
113
+
108
114
  def copy_of_elements(self):
109
115
  return [e.clone() for e in self.elements() if e]
110
116
 
@@ -1818,7 +1824,7 @@ class Area(object):
1818
1824
  logger.debug(">>> air is a circle")
1819
1825
  return self.type
1820
1826
 
1821
- self.set_close_to_start_end_angles(self, 0.0, alpha)
1827
+ self.set_close_to_start_end_angles(0.0, alpha)
1822
1828
 
1823
1829
  if self.is_magnet_rectangle():
1824
1830
  self.type = TYPE_MAGNET_RECT # magnet embedded
@@ -1863,6 +1869,11 @@ class Area(object):
1863
1869
  nodes = [n for n in self.virtual_nodes(parts=4)]
1864
1870
  return area_size(nodes)
1865
1871
 
1872
+ def get_area_size(self):
1873
+ if self.surface == 0.0:
1874
+ self.surface = self.area_size()
1875
+ return self.surface
1876
+
1866
1877
  def set_surface(self, mirrored):
1867
1878
  self.surface = self.area_size()
1868
1879
  if self.close_to_endangle and mirrored:
@@ -16,7 +16,7 @@ from femagtools.dxfsl.area import Area, TYPE_AIR
16
16
  from femagtools.dxfsl.functions import points_are_close, nodes_are_equal, distance
17
17
  from femagtools.dxfsl.functions import normalise_angle, positive_angle, point
18
18
  from femagtools.dxfsl.functions import alpha_line, alpha_points, alpha_angle
19
- from femagtools.dxfsl.functions import less, is_same_angle
19
+ from femagtools.dxfsl.functions import less, less_equal, greater, is_same_angle
20
20
  from femagtools.dxfsl.functions import Timer
21
21
  from femagtools.dxfsl.journal import getJournal
22
22
  import io
@@ -135,7 +135,7 @@ class EdgeInfo(object):
135
135
  self.angle = positive_angle(alpha_angle(start_angle, self.n1_angle_ingoing()))
136
136
  logger.debug("set_direction_angle: angle is %s", self.angle)
137
137
 
138
- if is_same_angle(0.0, self.angle, atol=0.008): # 1/2 degree
138
+ if is_same_angle(0.0, self.angle, atol=0.015): # 1/2 degree
139
139
  # reverse direction
140
140
  logger.debug("set_direction_angle: reverse direction( nearly 180 degrees)")
141
141
 
@@ -218,7 +218,7 @@ class EdgeInfo(object):
218
218
  myself_angle = self.angle
219
219
  other_angle = nbr_edge.angle
220
220
 
221
- if is_same_angle(0.0, myself_angle):
221
+ if is_same_angle(0.0, myself_angle, atol=0.015):
222
222
  # 360 or 0 degrees => turn 180 degrees
223
223
  logger.debug("-- ATTENTION: myself %s turns nearly 180 degrees", self.classname())
224
224
  logger.debug(" the angle is %s", myself_angle)
@@ -261,7 +261,7 @@ class EdgeInfo(object):
261
261
  logger.debug("#4: end of myself_direction_lefthand: ==> %s", left)
262
262
  return left
263
263
 
264
- if is_same_angle(0.0, other_angle, atol=0.008): # 1/2 degree
264
+ if is_same_angle(0.0, other_angle, atol=0.015): # 1/2 degree
265
265
  # 360 or 0 degrees => turn 180 degrees
266
266
  logger.debug("-- ATTENTION: other %s turns nearly 180 degrees", nbr_edge.classname())
267
267
  logger.debug(" the angle is %s", other_angle)
@@ -288,7 +288,7 @@ class EdgeInfo(object):
288
288
 
289
289
  logger.debug("-- angles: myself = %s, other = %s",
290
290
  myself_angle, other_angle)
291
- if not is_same_angle(myself_angle, other_angle, atol=0.008): # 1/2 degree
291
+ if not is_same_angle(myself_angle, other_angle, atol=0.015): # 1/2 degree
292
292
  logger.debug("-- angles are different")
293
293
  left = myself_angle > other_angle
294
294
  log_lefthand(left, self, nbr_edge)
@@ -778,7 +778,7 @@ class AreaBuilder(object):
778
778
  assert(self.geom.area_list)
779
779
  return self.get_lower_border_line()
780
780
 
781
- def close_outer_winding_areas(self):
781
+ def close_outer_winding_areas(self, color='red'):
782
782
  logger.debug("begin close_outer_winding_areas")
783
783
 
784
784
  airgap_line, airgap_el = self.get_outer_airgap_line()
@@ -787,6 +787,15 @@ class AreaBuilder(object):
787
787
  if len(airgap_line) < 5:
788
788
  return False
789
789
 
790
+ distlist = [distance(self.geom.center, n) for n in airgap_line]
791
+ min_dist = min(distlist)
792
+ max_dist = max(distlist)
793
+ geom_height = self.geom.max_radius - self.geom.min_radius
794
+ line_height = max_dist - min_dist
795
+ if line_height < geom_height * 0.2:
796
+ return 0
797
+
798
+ logger.debug("NODES: %s TO %s", airgap_line[0], airgap_line[-1])
790
799
  n1 = None
791
800
  dist_n1 = 0.0
792
801
 
@@ -795,22 +804,29 @@ class AreaBuilder(object):
795
804
  dist_prev = distance(self.geom.center, n_prev)
796
805
  alpha_prev = alpha_line(self.geom.center, n_prev)
797
806
  alpha_start = alpha_prev
798
-
799
807
  lines_created = 0
808
+ dist_ag = self.geom.min_radius
809
+ logger.debug("MIN RADIUS DIST: %s",dist_ag)
800
810
  for n in airgap_line[1:]:
801
811
  dist = distance(self.geom.center, n)
802
812
  alpha = alpha_line(self.geom.center, n)
813
+ logger.debug("Node %s, dist=%s, alpha=%s", n, dist, alpha)
803
814
  if not n1:
804
- if dist > dist_prev and alpha < alpha_prev:
805
- n1 = n_prev
806
- dist_n1 = dist_prev
815
+ logger.debug("Previous, dist=%s, alpha=%s", dist_prev, alpha_prev)
816
+ if greater(dist_prev, dist_ag, rtol=1e-3, atol=1e-3):
817
+ if greater(dist, dist_prev, rtol=1e-3, atol=1e-3) and \
818
+ less(alpha, alpha_prev, rtol=1e-4, atol=1e-5):
819
+ n1 = n_prev
820
+ dist_n1 = dist_prev
821
+ logger.debug("NODE1: %s (%s)", n1, dist_n1)
807
822
  else:
808
- if np.isclose(dist_n1, dist, rtol=1e-3, atol=1e-3):
823
+ if less_equal(dist, dist_n1, rtol=1e-3, atol=1e-2):
824
+ logger.debug("NODE2: %s (%s)", n, dist)
809
825
  line = Line(Element(start=n1, end=n))
810
826
  if e_prev.intersect_line(line):
811
827
  logger.debug("___LINE NOT POSSIBLE___")
812
828
  else:
813
- self.geom.add_line(n1, n, color='red')
829
+ self.geom.add_line(n1, n, color=color)
814
830
  lines_created += 1
815
831
  n1 = None
816
832
  dist_n1 = 0.0
@@ -229,7 +229,7 @@ def build_machine_rotor(machine, inner, mindist, plt, EESM=False, single=False):
229
229
  else:
230
230
  rebuild = machine_temp.create_mirror_lines_outside_magnets()
231
231
  if rebuild:
232
- machine_temp.geom.create_list_of_areas(delete=True)
232
+ machine_temp.rebuild_subregions(EESM, single=single)
233
233
 
234
234
  if machine_temp.create_auxiliary_lines():
235
235
  logger.debug("Auxiliary Lines created: rebuild subregions")
@@ -286,6 +286,8 @@ def build_machine_stator(machine, inner, mindist, plt, EESM=False, single=False)
286
286
  machine_temp.create_mirror_lines_outside_windings()
287
287
  else:
288
288
  machine_temp = machine
289
+ if machine_temp.has_windings_in_the_middle():
290
+ machine_temp.create_mirror_lines_outside_windings()
289
291
 
290
292
  if machine_temp.geom.reduce_element_nodes(mindist):
291
293
  machine_temp.rebuild_subregions(EESM, single=single)
@@ -309,6 +311,9 @@ def build_machine_stator(machine, inner, mindist, plt, EESM=False, single=False)
309
311
  plt, machine_temp.geom,
310
312
  title="Stator Winding Slice after Rebuild")
311
313
 
314
+ if machine_temp.remove_tiny_air_areas():
315
+ machine_temp.rebuild_subregions(EESM, single=single)
316
+
312
317
  if machine_temp.create_auxiliary_lines():
313
318
  machine_temp.rebuild_subregions(EESM, single=single)
314
319
  plot_geom(False, # for developer
@@ -1037,7 +1042,7 @@ def create_femag_parameters(m_inner, m_outer, nodedist=1):
1037
1042
  alfa_pole = geom_poles.get_alfa()
1038
1043
 
1039
1044
  params['tot_num_slot'] = num_slots
1040
- params['slot_area'] = slot_area
1045
+ params['slot_area'] = float(slot_area)
1041
1046
  params['num_sl_gen'] = num_sl_gen
1042
1047
  params['num_poles'] = num_poles
1043
1048
  params['nodedist'] = nodedist
@@ -246,6 +246,37 @@ class FslRenderer(object):
246
246
  'outer_da_end = {}'.format(
247
247
  geom.dist_end_min_corner())
248
248
  ]
249
+ if inner:
250
+ r = np.linalg.norm(geom.start_corners[0])
251
+ if not np.isclose(geom.min_radius, r, rtol=1e-3):
252
+ slice = 1 if machine.is_mirrored() else 2
253
+ self.content += [
254
+ '-- create air layer inside',
255
+ 'x0, y0 = {}, {}'.format(
256
+ geom.start_min_corner(0),
257
+ geom.start_min_corner(1)),
258
+ 'hair = 1.0',
259
+ 'parts = {}'.format(machine.get_num_parts()),
260
+ 'rmin = {}'.format(geom.min_radius),
261
+ 'r1 = rmin - hair',
262
+ 'r, phi = c2pr(x0, y0)',
263
+ 'x1, y1 = pr2c(r1, phi)',
264
+ 'x2, y2 = pr2c(r1, {}*math.pi/parts)'.format(slice),
265
+ 'x3, y3 = pr2c(r, {}*math.pi/parts)'.format(slice),
266
+ 'nc_line(x0, y0, x1, y1, 0)',
267
+ 'nc_circle_m(x1, y1, x2, y2, 0.0, 0.0, 0)',
268
+ 'nc_line(x2, y2, x3, y3, 0)',
269
+ 'x0, y0 = pr2c(rmin - hair/2, math.pi/parts/2)',
270
+ 'create_mesh_se(x0, y0)',
271
+ '\n']
272
+ else:
273
+ self.content += [
274
+ 'parts = {}'.format(machine.get_num_parts()),
275
+ 'x0, y0 = {}, {}'.format(
276
+ geom.start_min_corner(0),
277
+ geom.start_min_corner(1)),
278
+ 'r1, phi = c2pr(x0, y0)']
279
+
249
280
  if self.mtype == 'PMSM':
250
281
  self.content += [
251
282
  'xmag = {}',
@@ -256,6 +287,9 @@ class FslRenderer(object):
256
287
  'if mcvkey_yoke == nil then',
257
288
  ' mcvkey_yoke = "dummy"',
258
289
  ' ur = 1000.0',
290
+ ' else if mcvkey_teeth == nil then',
291
+ ' mcvkey_teeth = mcvkey_yoke',
292
+ 'end',
259
293
  'end',
260
294
  'x0_iron_tooth, y0_iron_tooth = 0.0, 0.0',
261
295
  'x0_iron_yoke, y0_iron_yoke = 0.0, 0.0',
@@ -324,7 +358,7 @@ class FslRenderer(object):
324
358
  self.content.append(
325
359
  'x0_shaft, y0_shaft = x0, y0')
326
360
 
327
- self.content.append("create_mesh(x0, y0)\n")
361
+ self.content.append("create_mesh()\n")
328
362
 
329
363
  txt = ["if x0_iron_yoke > 0.0 then",
330
364
  " if mcvkey_yoke ~= 'dummy' then",
@@ -387,22 +421,24 @@ class FslRenderer(object):
387
421
  # angle after mirroring
388
422
  self.content.append('alfa = {}\n'.format(geom.get_alfa()))
389
423
 
390
- self.content += ['-- rotate',
391
- 'x1, y1 = {}, {}'.format(
392
- geom.start_corners[0][0],
393
- geom.start_corners[0][1])] # min xy1
394
424
  if outer:
395
- self.content.append('x2, y2 = pr2c(r1, 0.0)')
425
+ self.content += ['-- rotate',
426
+ 'x1, y1 = {}, {}'.format(
427
+ geom.start_corners[0][0],
428
+ geom.start_corners[0][1]), # min xy1
429
+ 'x2, y2 = pr2c(r1, 0.0)']
396
430
  else:
397
- self.content.append('x2, y2 = {}, {}'.format(
398
- geom.start_corners[1][0],
399
- geom.start_corners[1][1])) # max xy1
431
+ self.content += ['-- rotate',
432
+ 'x1, y1 = pr2c(r1, 0.0)',
433
+ 'x2, y2 = {}, {}'.format(
434
+ geom.start_corners[1][0],
435
+ geom.start_corners[1][1])] # min xy1
400
436
 
401
437
  if geom.is_mirrored():
402
438
  self.content.append('x3, y3 = pr2c(x2, alfa)')
403
439
  self.content.append('x4, y4 = pr2c(x1, alfa)')
404
440
  elif outer:
405
- self.content += ['x3, y3 = pr2c(r1, 2*math.pi/parts+phi)',
441
+ self.content += ['x3, y3 = pr2c(r1, 2*math.pi/parts)',
406
442
  'x4, y4 = {}, {}'.format(
407
443
  geom.end_corners[0][0],
408
444
  geom.end_corners[0][1])] # min xy4
@@ -410,10 +446,7 @@ class FslRenderer(object):
410
446
  self.content += ['x3, y3 = {}, {}'.format(
411
447
  geom.end_corners[-1][0],
412
448
  geom.end_corners[-1][1]), # min xy3
413
- 'x4, y4 = {}, {}'.format(
414
- geom.end_corners[0][0],
415
- geom.end_corners[0][1]), # min xy4
416
- 'def_bcond_vpo(x4, y4, x1, y1)']
449
+ 'x4, y4 = pr2c(r1, 2*math.pi/parts)']
417
450
 
418
451
  self.content.append('if parts_gen > 1 then')
419
452
  if geom.corners_dont_match():
femagtools/dxfsl/geom.py CHANGED
@@ -1182,7 +1182,7 @@ class Geometry(object):
1182
1182
  nx.set_edge_attributes(self.g, False, 1)
1183
1183
  nx.set_edge_attributes(self.g, False, 2)
1184
1184
 
1185
- def create_list_of_areas(self, main=False, delete=False):
1185
+ def create_list_of_areas(self, main=False, delete=False, nolog=True):
1186
1186
  """ return list of areas for each node and their neighbors
1187
1187
  """
1188
1188
  if delete: # clear list of areas
@@ -1193,7 +1193,7 @@ class Geometry(object):
1193
1193
  # list already available
1194
1194
  return
1195
1195
 
1196
- areabuilder = AreaBuilder(geom=self)
1196
+ areabuilder = AreaBuilder(geom=self, nolog=nolog)
1197
1197
  areabuilder.create_list_of_areas(main=main)
1198
1198
  self.area_list = areabuilder.area_list
1199
1199
  logger.debug("area list created")
@@ -3337,6 +3337,31 @@ class Geometry(object):
3337
3337
  return [a for a in self.list_of_areas()
3338
3338
  if a.is_type(type)]
3339
3339
 
3340
+ def is_tooth(self, a, windings, wdg_min_dist, wdg_max_dist):
3341
+ if a.around_windings(windings, self):
3342
+ if a.close_to_ag:
3343
+ return True
3344
+ if self.is_inner:
3345
+ if greater(a.min_dist, wdg_min_dist, atol=1e-1):
3346
+ return True
3347
+ else:
3348
+ if less(a.max_dist, wdg_max_dist, atol=1e-1):
3349
+ return True
3350
+ return False
3351
+
3352
+ def between_airgap_and_winding(self, a,
3353
+ wdg_min_angle,
3354
+ wdg_max_angle,
3355
+ wdg_min_dist,
3356
+ wdg_max_dist):
3357
+ if greater_equal(a.min_angle, wdg_min_angle) and \
3358
+ less_equal(a.max_angle, wdg_max_angle):
3359
+ if self.is_inner:
3360
+ return less_equal(wdg_max_dist, a.min_dist)
3361
+ else:
3362
+ return less_equal(a.max_dist, wdg_min_dist)
3363
+ return False
3364
+
3340
3365
  def collect_windings(self):
3341
3366
  logger.debug("begin of collect_windings")
3342
3367
  good_windings = self.get_windings(AREA.TYPE_WINDINGS)
@@ -3409,7 +3434,7 @@ class Geometry(object):
3409
3434
  max_size, max_w = windings_surface[0]
3410
3435
  for sz, w in windings_surface[1:]:
3411
3436
  logger.debug("winding size = %s", sz)
3412
- if sz / max_size < 0.70:
3437
+ if sz / max_size < 0.60:
3413
3438
  w.set_type(AREA.TYPE_AIR)
3414
3439
  if sz / max_size < 0.2:
3415
3440
  windings_found -= 1
@@ -3459,7 +3484,7 @@ class Geometry(object):
3459
3484
  air_areas = [a for a in self.list_of_areas()
3460
3485
  if a.is_type(AREA.TYPE_AIR_OR_IRON)]
3461
3486
  for a in air_areas:
3462
- if a.around_windings(windings, self):
3487
+ if a.around_windings(windings, self) and less(a.min_dist, wdg_max_dist - 0.5):
3463
3488
  logger.debug("Area %s", a.identifier())
3464
3489
  logger.debug(" - air-angle min/max = %s/%s",
3465
3490
  a.min_air_angle,
@@ -3481,7 +3506,6 @@ class Geometry(object):
3481
3506
  a.close_to_ag):
3482
3507
  a.set_type(AREA.TYPE_AIR) # air
3483
3508
  continue
3484
-
3485
3509
  a.set_type(AREA.TYPE_TOOTH) # iron shaft (Zahn)
3486
3510
  continue
3487
3511
 
@@ -3510,14 +3534,28 @@ class Geometry(object):
3510
3534
  a.set_type(AREA.TYPE_TOOTH) # iron shaft (Zahn)
3511
3535
  else:
3512
3536
  logger.debug("#5 not around windings")
3513
- a.set_type(AREA.TYPE_TOOTH) # iron shaft (Zahn)
3537
+ if self.between_airgap_and_winding(a,
3538
+ wdg_min_angle, wdg_max_angle,
3539
+ wdg_min_dist, wdg_max_dist):
3540
+ a.set_type(AREA.TYPE_AIR) # air
3541
+ else:
3542
+ if self.is_inner:
3543
+ if greater(a.min_dist, wdg_min_dist, atol=1e-1):
3544
+ a.set_type(AREA.TYPE_TOOTH) # iron shaft (Zahn)
3545
+ else:
3546
+ a.set_type(AREA.TYPE_YOKE) # iron shaft (Joch)
3547
+ else:
3548
+ if less(a.max_dist, wdg_max_dist, atol=1e-1):
3549
+ a.set_type(AREA.TYPE_TOOTH) # iron shaft (Zahn)
3550
+ else:
3551
+ a.set_type(AREA.TYPE_YOKE) # iron shaft (Joch)
3514
3552
 
3515
3553
  # yoke or shaft ?
3516
3554
  iron_areas = [a for a in self.list_of_areas()
3517
3555
  if a.is_type(AREA.TYPE_YOKE)]
3518
3556
  for a in iron_areas:
3519
3557
  if a.around_windings(windings, self):
3520
- if less(a.min_dist, wdg_max_dist):
3558
+ if less(a.min_dist, wdg_max_dist - 0.5):
3521
3559
  if less_equal(a.max_dist, wdg_max_dist):
3522
3560
  a.set_type(AREA.TYPE_TOOTH) # iron shaft (Zahn)
3523
3561
  else:
@@ -3922,6 +3960,15 @@ class Geometry(object):
3922
3960
  area.mark_airgap_corners(start_cp, end_cp)
3923
3961
  return
3924
3962
 
3963
+ def get_areas_of_type(self, types=()):
3964
+ return [area for area in self.list_of_areas()
3965
+ if area.type in types]
3966
+
3967
+ def get_areas_of_irons(self):
3968
+ return self.get_areas_of_type((AREA.TYPE_IRON,
3969
+ AREA.TYPE_YOKE,
3970
+ AREA.TYPE_TOOTH,))
3971
+
3925
3972
  def num_areas_of_type(self, types=()):
3926
3973
  return len([area for area in self.list_of_areas()
3927
3974
  if area.type in types])
@@ -4268,6 +4315,16 @@ class Geometry(object):
4268
4315
  for e in self.elements():
4269
4316
  e.adjust_points()
4270
4317
 
4318
+ def split_with_point(self, p):
4319
+ for e in self.elements(Shape):
4320
+ elements = e.split([p])
4321
+ if elements:
4322
+ self.remove_edge(e)
4323
+ for e in elements:
4324
+ self.add_element(e, rtol=self.rtol, atol=self.atol)
4325
+ return True
4326
+ return False
4327
+
4271
4328
  def split_and_get_intersect_points(self, el, aktion=True, include_end=True):
4272
4329
  logger.debug("begin of split_and_get_intersect_points")
4273
4330
  rtol = 1e-03
@@ -4424,38 +4481,6 @@ class Geometry(object):
4424
4481
  builder = AreaBuilder(geom=self)
4425
4482
  return builder.create_inner_corner_auxiliary_areas(startangle, endangle)
4426
4483
 
4427
- def analyse_airgap_line(self, inner):
4428
- if inner: # TODO
4429
- return False
4430
-
4431
- areas = self.list_of_areas()
4432
- builder = AreaBuilder(geom=self)
4433
- ag_nodes, ag_el = builder.get_outer_airgap_line()
4434
- if not ag_nodes:
4435
- logger.warning("Fatal: No nodes found")
4436
- return False
4437
-
4438
- distlist = [distance(self.center, n) for n in ag_nodes]
4439
- min_dist = min(distlist)
4440
- max_dist = max(distlist)
4441
- logger.debug("Airgap min/max from center: %s/%s", min_dist, max_dist)
4442
- for a in areas:
4443
- logger.debug("%s: min/max from center: %s/%s", a.identifier(), a.min_dist, a.max_dist)
4444
- logger.debug("%s: min/max x: %s/%s, y: %s/%s",
4445
- a.identifier(),
4446
- a.min_x, a.max_x,
4447
- a.min_y, a.max_y)
4448
- if less_equal(a.min_x, -min_dist) and greater_equal(a.max_x, min_dist) and \
4449
- less_equal(a.min_y, -min_dist) and greater_equal(a.max_y, min_dist):
4450
- logger.debug("%s: around center", a.identifier())
4451
- continue
4452
- if np.isclose(a.min_dist, min_dist, rtol=1e-3, atol=1e-2):
4453
- continue
4454
- if less_equal(a.max_dist, max_dist, rtol=1e-3, atol=1e-2):
4455
- return False
4456
-
4457
- return builder.close_outer_winding_areas()
4458
-
4459
4484
  def adjust_outer_hull_for_symmetry(self):
4460
4485
  logger.debug("adjust_outer_hull_for_symmetry()")
4461
4486
  areas = self.list_of_areas()
@@ -4778,6 +4803,52 @@ class Geometry(object):
4778
4803
  return
4779
4804
  logger.warning("WARNING: airgap connecting nodes do not aline")
4780
4805
 
4806
+ def remove_area(self, area):
4807
+ logger.debug("Remove Area %s", area.identifier())
4808
+ touching_areas = [(a.surface, a) for a in self.list_of_areas()
4809
+ if a.id != area.id and a.is_touching(area)]
4810
+ if not touching_areas:
4811
+ self.remove_edges(area.area)
4812
+ return
4813
+ touching_areas.sort(reverse=True)
4814
+ sz, touching_a = touching_areas[0]
4815
+ for e in area.area:
4816
+ if touching_a.is_element_in_area(e):
4817
+ self.remove_edge(e)
4818
+
4819
+ def remove_tiny_air_areas(self):
4820
+ logger.debug("remove_tiny_air_areas()")
4821
+ air_areas = [a for a in self.list_of_areas() if a.is_air()]
4822
+ if not air_areas:
4823
+ return False
4824
+
4825
+ def is_in_touch(air, area_list):
4826
+ for a in area_list:
4827
+ if air.is_touching(a):
4828
+ return True
4829
+ if air.is_inside(a, self):
4830
+ return True
4831
+ return False
4832
+
4833
+ areas = [a for a in self.list_of_areas() if a.is_winding() or a.is_magnet()]
4834
+ if not areas:
4835
+ return False
4836
+
4837
+ tiny_areas = [a for a in air_areas if not is_in_touch(a, areas)]
4838
+ geom_surface = self.area_size()
4839
+ angle_areas = [a for a in tiny_areas
4840
+ if (a.close_to_startangle or a.close_to_endangle)]
4841
+ #inside_areas = [a for a in tiny_areas
4842
+ # if not (a.close_to_startangle or a.close_to_endangle) and \
4843
+ # a.get_area_size() < geom_surface / 200]
4844
+ tiny_areas = angle_areas
4845
+ if not tiny_areas:
4846
+ return False
4847
+
4848
+ for a in tiny_areas:
4849
+ self.remove_area(a)
4850
+ return True
4851
+
4781
4852
  def print_nodes(self):
4782
4853
  print("=== List of Nodes ({}) ===".format(self.number_of_nodes()))
4783
4854
  for n in self.g.nodes():