femagtools 1.7.0__py3-none-any.whl → 1.7.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.7.0'
5
+ __version__ = '1.7.2'
6
6
  __author__ = 'Ronald Tanner'
7
7
  __license__ = 'BSD'
8
8
  __copyright__ = 'Copyright 2023-2024 Gamma Technology'
femagtools/bch.py CHANGED
@@ -70,7 +70,7 @@ def sttylosses(losses):
70
70
  except KeyError:
71
71
  pass
72
72
  return d
73
-
73
+
74
74
  if sregs == {'StYoke', 'StTeeth'}:
75
75
  return hysteddy('StYoke', 'StTeeth', losses)
76
76
  if sregs == {'StJo', 'StZa'}:
@@ -95,6 +95,21 @@ def sttylosses(losses):
95
95
  return l
96
96
  return {}
97
97
 
98
+ def losses_mapping_external_rotor(losses):
99
+ styoke = 'rotor'
100
+ rotor = 'Iron'
101
+ d = {}
102
+ try:
103
+ d['styoke_hyst'] = losses[styoke+'_hyst']
104
+ d['rotor_hyst'] = losses[rotor+'_hyst']
105
+ d['styoke_eddy'] = losses[styoke+'_eddy']
106
+ d['rotor_eddy'] = losses[rotor+'_eddy']
107
+ d['styoke_excess'] = losses[styoke+'_excess']
108
+ d['rotor_excess'] = losses[rotor+'_excess']
109
+ except KeyError:
110
+ pass
111
+ return d
112
+
98
113
 
99
114
  def _readSections(f):
100
115
  """return list of bch sections
@@ -1150,6 +1165,10 @@ class Reader:
1150
1165
  m = []
1151
1166
  subregs = [name.split()[-1]
1152
1167
  for name in content[2].split('\t') if name][2:]
1168
+ # outer rotor motor
1169
+ if 'Iron' in subregs and \
1170
+ 'Rotor' in subregs:
1171
+ self.external_rotor = True
1153
1172
  logger.info("Stator Subregions: %s", subregs)
1154
1173
  speed = float(content[0].split()[-1])/60.
1155
1174
  logger.info('losses for speed %f', speed)
@@ -1209,7 +1228,10 @@ class Reader:
1209
1228
  (nrows, ncols)).T.tolist()
1210
1229
  for k, v in zip(cols, m[2:])})
1211
1230
  ls['speed'] = speed
1212
- ls.update(sttylosses(ls))
1231
+ if self.external_rotor:
1232
+ ls.update(losses_mapping_external_rotor(ls))
1233
+ else:
1234
+ ls.update(sttylosses(ls))
1213
1235
  if self.ldq:
1214
1236
  self.ldq['losses'] = ls
1215
1237
  elif self.psidq:
@@ -1464,6 +1486,10 @@ class Reader:
1464
1486
  def __read_losses(self, content):
1465
1487
  losses = {}
1466
1488
  i = 0
1489
+ # check if external rotor
1490
+ if self.weights[0][1] == 0.0 and \
1491
+ self.weights[0][-1] > 0:
1492
+ self.external_rotor = True
1467
1493
  # find results for angle:
1468
1494
  while True:
1469
1495
  try:
@@ -1521,8 +1547,12 @@ class Reader:
1521
1547
  losses['total'] += losses['staza']+losses['stajo']
1522
1548
  elif len(rec) == 1:
1523
1549
  t = l.split(':')[-1].strip()
1524
- if t == 'Iron':
1525
- losses['staza'] = floatnan(rec[0])
1550
+ if t == 'Iron':
1551
+ if self.external_rotor:
1552
+ losses['rotfe'] = floatnan(rec[0])
1553
+ losses['total'] += losses['rotfe']
1554
+ else:
1555
+ losses['staza'] = floatnan(rec[0])
1526
1556
  else:
1527
1557
  losses['stajo'] += floatnan(rec[0])
1528
1558
  losses['total'] += losses['staza']+losses['stajo']
@@ -1530,11 +1560,17 @@ class Reader:
1530
1560
  continue
1531
1561
 
1532
1562
  if _rotloss.search(l):
1563
+ if l.find('StZa') > -1:
1564
+ self.external_rotor = True
1533
1565
  rec = self.__findNums(content[i+2])
1534
1566
  if len(rec) == 1:
1535
- rotfe = floatnan(rec[0])
1536
- losses['rotfe'] += rotfe
1537
- losses['total'] += rotfe
1567
+ if self.external_rotor:
1568
+ losses['staza'] = floatnan(rec[0])
1569
+ losses['total'] += losses['staza']
1570
+ else:
1571
+ rotfe = floatnan(rec[0])
1572
+ losses['rotfe'] += rotfe
1573
+ losses['total'] += rotfe
1538
1574
  i += 3
1539
1575
  continue
1540
1576
 
@@ -1565,6 +1601,10 @@ class Reader:
1565
1601
  if content[i+1].split() == ['rotf', '----']:
1566
1602
  losses['rotfe'] = sum([floatnan(x) for x in rec])
1567
1603
  losses['total'] += losses['rotfe']
1604
+
1605
+ if content[i+1].split() == ['Iron', '----']: # external rotor
1606
+ losses['rotfe'] = sum([floatnan(x) for x in rec])
1607
+ losses['total'] += losses['rotfe']
1568
1608
  i += 4
1569
1609
  continue
1570
1610
 
@@ -1622,7 +1662,10 @@ class Reader:
1622
1662
  l.find('RoZa') > -1:
1623
1663
  k = 'staza'
1624
1664
  elif l.find('Iron') > -1 and l.find('Stator') > -1:
1625
- k = 'staza'
1665
+ if self.external_rotor:
1666
+ k = 'rotor'
1667
+ else:
1668
+ k = 'staza'
1626
1669
  elif l.find('Iron') > -1:
1627
1670
  if self.external_rotor:
1628
1671
  k = 'staza'
femagtools/dxfsl/area.py CHANGED
@@ -81,6 +81,7 @@ class Area(object):
81
81
  global area_number
82
82
  area_number += 1
83
83
  self.id = area_number
84
+ self.is_child = False
84
85
  self.areas_inside = {}
85
86
  self.areas_of_group = []
86
87
 
@@ -431,12 +432,14 @@ class Area(object):
431
432
  d = distance(p1, p2)
432
433
  gap_list.append((d, (p1, p2), dist_id, a.get_id()))
433
434
 
434
- d, p1, p2 = a.get_nearest_point(center, radius, rightangle)
435
- if p1:
436
- gap_list.append((d, (p1, p2), dist_id, a.get_id()))
437
- d, p1, p2 = a.get_nearest_point(center, radius, leftangle)
438
- if p1:
439
- gap_list.append((d, (p1, p2), dist_id, a.get_id()))
435
+ if rightangle is not None:
436
+ d, p1, p2 = a.get_nearest_point(center, radius, rightangle)
437
+ if p1:
438
+ gap_list.append((d, (p1, p2), dist_id, a.get_id()))
439
+ if leftangle is not None:
440
+ d, p1, p2 = a.get_nearest_point(center, radius, leftangle)
441
+ if p1:
442
+ gap_list.append((d, (p1, p2), dist_id, a.get_id()))
440
443
  if not gap_list:
441
444
  return []
442
445
  gap_list.sort()
@@ -1553,74 +1556,6 @@ class Area(object):
1553
1556
  for i in a.nested_areas_inside():
1554
1557
  yield i
1555
1558
 
1556
- def crunch_area(self, geom):
1557
- n1_prev = None
1558
- e_prev = None
1559
- logger.debug("crunch area %s", self.identifier())
1560
-
1561
- if self.is_circle():
1562
- return 0
1563
-
1564
- c = 0
1565
- for n1, n2, e in self.list_of_elements():
1566
- if e_prev is not None:
1567
- if len([nbr for nbr in geom.g.neighbors(n1)]) == 2:
1568
- e_new = e_prev.concatenate(n1_prev, n2, e)
1569
- if e_new is not None:
1570
- e_prev_dict = geom.g.get_edge_data(n1_prev, n1)
1571
- e_dict = geom.g.get_edge_data(n1, n2)
1572
-
1573
- logger.debug("--> remove from %s to %s [%s, %s, %s]",
1574
- n1_prev,
1575
- n1,
1576
- e_prev_dict[0],
1577
- e_prev_dict[1],
1578
- e_prev_dict[2])
1579
- logger.debug(" remove %s", e_prev)
1580
- geom.remove_edge(e_prev)
1581
-
1582
- logger.debug("--> remove from %s to %s [%s, %s, %s]",
1583
- n1,
1584
- n2,
1585
- e_dict[0],
1586
- e_dict[1],
1587
- e_dict[2])
1588
- logger.debug(" remove %s", e)
1589
- if e.get_node_number(n1) == 1:
1590
- flag1 = e_dict[1]
1591
- flag2 = e_dict[2]
1592
- else:
1593
- flag1 = e_dict[2]
1594
- flag2 = e_dict[1]
1595
- geom.remove_edge(e)
1596
-
1597
- logger.debug("--> add from %s to %s",
1598
- n1_prev,
1599
- n2)
1600
- logger.debug(" add %s", e_new)
1601
- geom.add_edge(n1_prev, n2, e_new)
1602
-
1603
- e_new_dict = geom.g.get_edge_data(n1_prev, n2)
1604
- e_new_dict[0] = True
1605
- if e_new.get_node_number(n1_prev) == 1:
1606
- e_new_dict[1] = flag1
1607
- e_new_dict[2] = flag2
1608
- else:
1609
- e_new_dict[1] = flag2
1610
- e_new_dict[2] = flag1
1611
-
1612
- logger.debug(" new dict: [%s, %s, %s]",
1613
- e_new_dict[0],
1614
- e_new_dict[1],
1615
- e_new_dict[2])
1616
- e_prev = e_new
1617
- c += 1
1618
- continue
1619
-
1620
- n1_prev = n1
1621
- e_prev = e
1622
- return c
1623
-
1624
1559
  def __str__(self):
1625
1560
  return "Area {}\n".format(self.id) + \
1626
1561
  "distance...............: from {} to {}\n".\
@@ -33,7 +33,7 @@ def disable_logging():
33
33
  logger.debug("Logging level %s disabled", original_log_level)
34
34
 
35
35
  # Set the log level to a higher level, e.g., WARNING or CRITICAL
36
- logging.disable(logging.CRITICAL)
36
+ logging.disable(logging.ERROR)
37
37
 
38
38
 
39
39
  def enable_logging():
@@ -338,7 +338,11 @@ 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.debug("begin of arc_line_direction_lefthand")
341
+ logger.info("begin of arc_line_direction_lefthand")
342
+ if not self.is_arc():
343
+ logger.critical("FATAL: unexpected %s at position %s", self.classname(), self.n1)
344
+ sys.exit(1)
345
+
342
346
  assert(self.is_arc())
343
347
  assert(line_edge.is_line())
344
348
  start_edge.log_edge(":::: start")
@@ -844,10 +848,12 @@ class AreaBuilder(object):
844
848
  logger.debug("=== Area Group List ===")
845
849
  self.set_edge_attributes()
846
850
  errors = 0
851
+ disable_logging()
847
852
  for k in group_keys:
848
853
  if not self.build_group_area(group_list[k]):
849
854
  logger.warning("Creation of an areagroup failed")
850
855
  errors += 1
856
+ enable_logging()
851
857
 
852
858
  t = timer.stop("areagroups created in %0.4f seconds")
853
859
  logger.debug("end of create_area_groups: %s groups created",
@@ -26,6 +26,9 @@ def plot_geom(doit, plt, geom, title="Plot", areas=True):
26
26
  logger.info("Prepare Plot %s", title)
27
27
  plt.render_elements(geom, Shape,
28
28
  draw_inside=areas,
29
+ draw_groups=False,
30
+ draw_phi=True,
31
+ critical=True,
29
32
  title=title,
30
33
  show=True,
31
34
  with_corners=True,
@@ -165,6 +168,7 @@ def build_machine_rotor(machine, inner, mindist, plt, single=False):
165
168
  if machine.is_mirrored():
166
169
  logger.debug("Rotor is mirrored")
167
170
  machine_temp = machine.undo_mirror()
171
+ machine_temp.delete_tiny_elements(mindist)
168
172
  machine_temp.geom.set_rotor()
169
173
  machine_temp.search_rotor_subregions(single=single)
170
174
  else:
@@ -198,7 +202,6 @@ def build_machine_rotor(machine, inner, mindist, plt, single=False):
198
202
  machine_temp.rebuild_subregions(single=single)
199
203
 
200
204
  machine_temp.geom.recalculate_magnet_orientation()
201
- machine_temp.delete_tiny_elements(mindist)
202
205
  if inner:
203
206
  machine_temp.create_inner_corner_areas()
204
207
 
@@ -220,6 +223,7 @@ def build_machine_stator(machine, inner, mindist, plt, single=False):
220
223
  if machine.has_mirrored_windings():
221
224
  logger.debug("undo mirrored windings")
222
225
  machine_temp = machine.undo_mirror()
226
+ machine_temp.delete_tiny_elements(mindist)
223
227
  machine_temp.geom.set_stator()
224
228
  machine_temp.search_stator_subregions(single=single)
225
229
  machine_temp.create_mirror_lines_outside_windings()
@@ -228,7 +232,6 @@ def build_machine_stator(machine, inner, mindist, plt, single=False):
228
232
  if machine_temp.create_auxiliary_lines():
229
233
  machine_temp.rebuild_subregions(single=single)
230
234
 
231
- machine_temp.delete_tiny_elements(mindist)
232
235
  if inner:
233
236
  machine_temp.create_inner_corner_areas()
234
237
 
@@ -245,7 +248,7 @@ def build_machine_stator(machine, inner, mindist, plt, single=False):
245
248
  def convert(dxfile,
246
249
  rtol=1e-04,
247
250
  atol=1e-03,
248
- mindist=0.0,
251
+ mindist=0.01,
249
252
  symtol=0.001,
250
253
  sympart=0,
251
254
  split=False,
@@ -271,9 +274,13 @@ def convert(dxfile,
271
274
  conv = {}
272
275
 
273
276
  input_file = Path(dxfile)
277
+ if not input_file.is_file():
278
+ logger.error("File %s is not available", input_file)
279
+ sys.exit(1)
280
+
274
281
  basename = input_file.stem
275
282
  if part:
276
- logger.info("***** start processing %s *****", basename)
283
+ logger.info("***** start processing %s (%s) *****", basename, part)
277
284
  else:
278
285
  logger.info("***** start processing %s *****", basename)
279
286
  timer = Timer(start_it=True)
@@ -281,7 +288,8 @@ def convert(dxfile,
281
288
  journal = getJournal(name='converter', aktiv=debug_mode)
282
289
  journal.get_journal(input_file.name)
283
290
  journal.put_filename(str(input_file.resolve()))
284
- journal.put('success', False)
291
+ journal.set('success', False)
292
+ journal.write_journal()
285
293
 
286
294
  if part:
287
295
  if part[0] not in ('rotor', 'stator'):
@@ -527,6 +535,9 @@ def convert(dxfile,
527
535
  p)
528
536
  machine_inner.sync_with_counterpart(machine_outer)
529
537
 
538
+ machine_inner.search_critical_elements(mindist)
539
+ machine_outer.search_critical_elements(mindist)
540
+
530
541
  logger.info("***** END of work: %s *****", basename)
531
542
 
532
543
  if machine_inner.geom.is_rotor():
@@ -674,6 +685,7 @@ def convert(dxfile,
674
685
  rows=3, # rows
675
686
  cols=2, # cols
676
687
  num=3) # start num
688
+ machine.delete_tiny_elements(mindist)
677
689
 
678
690
  if da > 0.0 or dy > 0.0:
679
691
  if inner:
@@ -709,7 +721,6 @@ def convert(dxfile,
709
721
  else:
710
722
  machine.search_subregions(single=True)
711
723
 
712
- machine.delete_tiny_elements(mindist)
713
724
  machine.create_inner_corner_areas()
714
725
 
715
726
  logger.info("***** END of work: %s *****", basename)
@@ -727,6 +738,7 @@ def convert(dxfile,
727
738
  with_nodes=False,
728
739
  neighbors=False,
729
740
  write_id=write_id,
741
+ draw_phi=True,
730
742
  fill_areas=True)
731
743
  elif small_plots:
732
744
  #p.figure().suptitle(input_file.name, fontsize=16)
@@ -737,6 +749,7 @@ def convert(dxfile,
737
749
  with_nodes=False,
738
750
  neighbors=False,
739
751
  write_id=write_id,
752
+ draw_phi=True,
740
753
  fill_areas=True)
741
754
 
742
755
  if show_plots or small_plots:
@@ -765,11 +778,12 @@ def convert(dxfile,
765
778
  fslrenderer = FslRenderer(basename)
766
779
  conv['fsl'] = fslrenderer.render(machine, inner, outer)
767
780
 
768
- conv.update(params)
781
+ if params is not None:
782
+ conv.update(params)
769
783
  conv['name'] = basename
770
784
  t = timer.stop("-- all done in %0.4f seconds --", info=True)
771
785
  journal.put('time_total', t)
772
- journal.put('success', True)
786
+ journal.set('success', True)
773
787
  return conv
774
788
 
775
789
 
@@ -550,7 +550,7 @@ class FslRenderer(object):
550
550
  'ndt(agndst)',
551
551
  'r1 = m.fc_radius - ag/6',
552
552
  'x1, y1 = pr2c(r1, alfa)',
553
- 'n = math.floor(r1*alfa/agndst + 1.5)',
553
+ 'n = math.floor(m.fc_radius*alfa/agndst + 1.5)',
554
554
  'nc_circle_m(r1, 0, x1, y1, 0.0, 0.0, n)\n',
555
555
  'r2 = m.fc_radius + ag/6',
556
556
  'x2, y2 = pr2c(r2, alfa)',
femagtools/dxfsl/geom.py CHANGED
@@ -283,6 +283,7 @@ class Geometry(object):
283
283
  self.has_magnets = False
284
284
  self.journal = getJournal()
285
285
  self.area_errors = 0
286
+ self.critical_points = []
286
287
  i = 0
287
288
 
288
289
  logger.debug("Geometry(split=%s, concat=%s, connect=%s, delete=%s, adjust=%s, main=%s,",
@@ -1829,6 +1830,10 @@ class Geometry(object):
1829
1830
  for area in magnets:
1830
1831
  area.render_magnet_phi(renderer, length)
1831
1832
 
1833
+ def render_critical(self, renderer):
1834
+ for e in self.critical_points:
1835
+ e.render(renderer, 'darkred')
1836
+
1832
1837
  def get_points_in_iron(self):
1833
1838
  points = []
1834
1839
  for area in self.list_of_areas():
@@ -2459,6 +2464,49 @@ class Geometry(object):
2459
2464
  # logger.info(" ++ %s", id)
2460
2465
  logger.debug("end set_areas_inside_for_all_areas")
2461
2466
 
2467
+ def set_groups_inside_for_all_areas(self):
2468
+ logger.debug("begin set_groups_inside_for_all_areas")
2469
+
2470
+ groups_inside = {}
2471
+ groups = {}
2472
+ areas_outside = []
2473
+ for area in self.list_of_areas():
2474
+ grouplist = [a for a in self.areagroup_list
2475
+ if area.is_inside(a, self)]
2476
+ if not grouplist:
2477
+ continue
2478
+ groups_inside = {g.id: g for g in grouplist}
2479
+ area.areas_inside = groups_inside
2480
+ areas_outside.append(area)
2481
+ for g in grouplist:
2482
+ alist = groups.get(g.id, [])
2483
+ alist.append(area)
2484
+ groups[g.id] = alist
2485
+
2486
+ outside_id = [a.id for a in areas_outside]
2487
+ logger.debug("Areas outside: %s", outside_id)
2488
+
2489
+ for id in groups.keys():
2490
+ if len(groups[id]) > 1:
2491
+ logger.warning("Attention: nested groups of areas")
2492
+ self.journal.put("warning", "nested groups of areas")
2493
+ areas = groups[id]
2494
+ main_area = areas[0]
2495
+ main_size = main_area.area_size()
2496
+ for a in areas[1:]:
2497
+ sz = a.area_size()
2498
+ if sz < main_size:
2499
+ main_area = a
2500
+ main_size = sz
2501
+ assert(main_area is not None)
2502
+ main_area.is_child = True
2503
+ for area in areas:
2504
+ if area.id != main_area.id:
2505
+ del area.areas_inside[id]
2506
+
2507
+ logger.debug("end set_areas_inside_for_all_areas")
2508
+ return areas_outside
2509
+
2462
2510
  def get_minmax_magnet(self):
2463
2511
  logger.debug("get_minmax_magnet")
2464
2512
  maglist = [a for a in self.list_of_areas() if a.is_magnet()]
@@ -2472,21 +2520,26 @@ class Geometry(object):
2472
2520
  def create_auxiliary_lines(self, rightangle, leftangle):
2473
2521
  logger.debug("begin of create_auxiliary_lines")
2474
2522
  timer = Timer(start_it=True)
2523
+ done = False
2475
2524
 
2476
- # for future (next release)
2477
- # area_list = self.list_of_areas()
2478
- # builder = AreaBuilder(geom=self)
2479
- # builder.create_area_groups(area_list)
2480
- # self.areagroup_list = builder.area_list
2481
-
2482
- self.set_areas_inside_for_all_areas()
2483
-
2484
- logger.debug("-> start create_auxiliary_lines")
2525
+ if True: # new style
2526
+ logger.debug("-> start create_auxiliary_lines")
2527
+ area_list = self.list_of_areas()
2528
+ builder = AreaBuilder(geom=self)
2529
+ builder.create_area_groups(area_list)
2530
+ self.areagroup_list = builder.area_list
2531
+ area_list = self.set_groups_inside_for_all_areas()
2532
+ for area in area_list:
2533
+ if self.create_aux_lines(area, rightangle, leftangle):
2534
+ done = True
2535
+ else:
2536
+ logger.debug("-> start create_auxiliary_lines")
2537
+ self.set_areas_inside_for_all_areas()
2538
+ done = False
2539
+ for area in self.list_of_areas():
2540
+ if self.create_aux_lines(area, rightangle, leftangle):
2541
+ done = True
2485
2542
 
2486
- done = False
2487
- for area in self.list_of_areas():
2488
- if self.create_aux_lines(area, rightangle, leftangle):
2489
- done = True
2490
2543
  t = timer.stop("-- auxiliary lines in %0.4f seconds --")
2491
2544
  self.journal.put('time_auxiliary_lines', t)
2492
2545
  logger.debug("end of create_auxiliary_lines")
@@ -2505,6 +2558,10 @@ class Geometry(object):
2505
2558
 
2506
2559
  aux_color = 'red'
2507
2560
  aux_linestyle = 'dotted'
2561
+ if area.is_child:
2562
+ logger.debug("Area %s is a child of another nested area", area.id)
2563
+ rightangle = None
2564
+ leftangle = None
2508
2565
 
2509
2566
  areas_border = {a.get_id(): a for a in areas_inside
2510
2567
  if area.has_connection(self, a, ndec)}
@@ -2861,7 +2918,7 @@ class Geometry(object):
2861
2918
  edges = self.search_tiny_elements(mindist)
2862
2919
  if not edges:
2863
2920
  logger.debug("-- no tiny elements found")
2864
- return
2921
+ return 0
2865
2922
 
2866
2923
  deleted = 0
2867
2924
  for edge in edges:
@@ -2901,8 +2958,27 @@ class Geometry(object):
2901
2958
 
2902
2959
  if deleted:
2903
2960
  logger.debug("%s tiny elements deleted", deleted)
2961
+ self.journal.put("tiny_elements_deleted", deleted)
2904
2962
  logger.debug("end of delete_tiny_elements")
2905
- return
2963
+ return deleted
2964
+
2965
+ def search_critical_elements(self, mindist):
2966
+ for n in self.g.nodes():
2967
+ nbrs = self.get_neighbors(n)
2968
+ if len(nbrs) < 3:
2969
+ continue
2970
+ critical_point = False
2971
+ critical_dist = 9999
2972
+ for nbr in nbrs:
2973
+ e = self.get_edge_element(n, nbr)
2974
+ if e.is_tiny(mindist):
2975
+ critical_point = True
2976
+ critical_dist = min(critical_dist, e.length())
2977
+ if critical_point:
2978
+ logger.debug("Warning: maybe critical point %s", n)
2979
+ self.journal.put("maybe_critical_points", (n, critical_dist))
2980
+ c = Circle(Element(center=n, radius=1))
2981
+ self.critical_points.append(c)
2906
2982
 
2907
2983
  def check_shaft_area(self, shaft):
2908
2984
  for a in self.list_of_areas():
@@ -3319,7 +3395,8 @@ class Geometry(object):
3319
3395
  if builder.create_area_groups(areas):
3320
3396
  return # bad
3321
3397
  group_list = builder.area_list
3322
- self.areagroup_list = group_list
3398
+ # for debugging
3399
+ # self.areagroup_list = group_list
3323
3400
  for group in group_list:
3324
3401
  if not group.is_magnet_rectangle():
3325
3402
  logger.debug("Warning: group is not a rectangle")
@@ -102,6 +102,11 @@ class Journal(object):
102
102
  if self.get_total('appendices_deleted') > 0:
103
103
  self.put_warning("Problem with appendices")
104
104
 
105
+ def set(self, name, val):
106
+ if not self.data:
107
+ return
108
+ self.data[name] = val
109
+
105
110
  def put(self, name, val):
106
111
  if not self.data:
107
112
  return
@@ -1069,7 +1069,10 @@ class Machine(object):
1069
1069
  return self.geom.has_magnets
1070
1070
 
1071
1071
  def delete_tiny_elements(self, mindist):
1072
- self.geom.delete_tiny_elements(mindist)
1072
+ return self.geom.delete_tiny_elements(mindist)
1073
+
1074
+ def search_critical_elements(self, mindist):
1075
+ self.geom.search_critical_elements(mindist)
1073
1076
 
1074
1077
  def create_arc(self, radius,
1075
1078
  color='red', linestyle='dotted',
@@ -207,6 +207,7 @@ class PlotRenderer(object):
207
207
  draw_inside = kwargs.get('draw_inside', False)
208
208
  draw_groups = kwargs.get('draw_groups', False)
209
209
  draw_phi = kwargs.get('draw_phi', False)
210
+ critical = kwargs.get('critical', False)
210
211
  fill_areas = kwargs.get('fill_areas', False)
211
212
  write_id = kwargs.get('write_id', False)
212
213
  with_legend = kwargs.get('with_legend', True)
@@ -308,6 +309,9 @@ class PlotRenderer(object):
308
309
  if draw_phi:
309
310
  geom.render_magnet_phi(self)
310
311
 
312
+ if critical:
313
+ geom.render_critical(self)
314
+
311
315
  if with_center and geom.center:
312
316
  self.point(geom.center, 'o', color='darkgreen')
313
317
  x_min = min(x_min, geom.center[0]-5)
femagtools/machine/pm.py CHANGED
@@ -569,10 +569,9 @@ class PmRelMachine(object):
569
569
  if abs(du) > 0.1:
570
570
  logger.debug('oops? iqd_imax_umax one more torque reduction')
571
571
  if with_tmech:
572
- iq, id = self.iqd_tmech(torque, w1/2/np.pi/self.p,
573
- iq, id)[:2]
572
+ iq, id = self.iqd_tmech_umax(torque, w1, u1max)[:2]
574
573
  else:
575
- iq, id = self.iqd_torque(torque)[:2]
574
+ iq, id = self.iqd_torque_umax(torque, w1, u1max)[:2]
576
575
  if with_mtpv:
577
576
  try:
578
577
  if with_tmech:
@@ -779,7 +778,7 @@ class PmRelMachine(object):
779
778
  maxtorque=T > 0)[:2])
780
779
  w, _, ier, _ = so.fsolve(voltw1, wx, full_output=True)
781
780
  logger.debug("fsolve ier %d T %f w %f w1 %f", ier, T, w, wx)
782
- if ier in (1, 4, 5):
781
+ if ier == 1: #in (1, 4, 5):
783
782
  if abs(w[0]) <= wx:
784
783
  self.check_extrapolation = True
785
784
  return [w/2/np.pi/self.p
@@ -788,6 +787,8 @@ class PmRelMachine(object):
788
787
  wl += (wu-wl)/4
789
788
  else:
790
789
  wl = w[0]
790
+ else:
791
+ break
791
792
  except ValueError as e:
792
793
  logger.debug("%s: wx %f wl %f wu %f", e, wx, wl, wu)
793
794
  wl = wx
@@ -5,6 +5,8 @@
5
5
  """
6
6
  import numpy as np
7
7
  import logging
8
+ from ..windings import Winding
9
+ from .utils import wdg_resistance
8
10
 
9
11
  logger = logging.getLogger("femagools.machine.sizing")
10
12
 
@@ -14,7 +16,7 @@ PM_DEFAULTS = dict(
14
16
  eta=0.92, # efficiency
15
17
  cos_phi=0.95, # power factor
16
18
  m=3, # number of phases
17
- ui_u=0.62, # U ind / U
19
+ ui_u=0.64, # U ind / U
18
20
  lda=1.33, # length/taup ratio 0.5 .. sqrt(p)
19
21
  J=3.8e6, # current density 3 .. 6 A/mm²
20
22
  sigmas=17e3, # shear force 10 .. 45 kN/m2
@@ -25,10 +27,10 @@ PM_DEFAULTS = dict(
25
27
  mag_width=0.8, # rel magnet width 0.6 .. 1
26
28
  Hc=700, # max. coercitive field strength, 500 .. 900 kA/m
27
29
  brem=1.15, # remanence 0.3 .. 1.3 T
28
- demag=1.5, # safety factor for demagnetisation
30
+ demag=6, # safety factor for demagnetisation (nom current)
29
31
  external_rotor=False,
30
32
  coil_span=0,
31
- hfe=1e-3 # iron height between magnet and airgap (IPM)
33
+ hfe=1.5e-3 # iron height between magnet and airgap (IPM)
32
34
  )
33
35
  """default sizing parameters for PM"""
34
36
 
@@ -88,10 +90,10 @@ def iron_losses(dy, lfe, hy, wt, ht, q1, B, f):
88
90
  return Wfe * dens*vol
89
91
 
90
92
 
91
- def wdg_resistance(w1, l, d):
92
- S = 56e6 # conductivity of copper 1/Ohm m
93
- a = np.pi*d**2/4
94
- return 2*w1*l/S/a
93
+ #def wdg_resistance(w1, l, d):
94
+ # S = 56e6 # conductivity of copper 1/Ohm m
95
+ # a = np.pi*d**2/4
96
+ # return 2*w1*l/S/a
95
97
 
96
98
 
97
99
  def check_symmetry_conditions(Q1, p, layers, m):
@@ -202,18 +204,22 @@ def _stator_slots(par, slots):
202
204
  par['Q1'] = Q1
203
205
  r = get_stator_dimensions(par)
204
206
  hb = r['hns']/r['bns']
207
+ logging.debug(f"Q1 {Q1} hs/bs {hb}")
205
208
  if hb > 5:
206
209
  break
207
- if hb > 3:
208
- q.append((Q1, Q1//np.gcd(Q1, p), abs(4-hb), r['kw']))
210
+ if hb > 2.5:
211
+ # ideal height/width ratio: 3.2
212
+ q.append((Q1, Q1//np.gcd(Q1, p), abs(3.2-hb), r['kw']))
209
213
 
210
214
  if q:
211
215
  qhbmin = np.argmin(q, axis=0)
212
- logger.debug("q %s", q)
213
- # check sim factor, height/width ratio, winding factor
216
+ logger.debug("q %s qhbmin %s", q, qhbmin)
217
+ # check sim factor, height/width ratio
214
218
  if qhbmin[1] == len(q)-1:
215
- return q[qhbmin[1]][0]
219
+ # last has smallest sim factor
220
+ return q[qhbmin[1]][0]
216
221
  elif q[qhbmin[1]][2] < q[qhbmin[1]+1][2]:
222
+ # select ideal height/width ratio (3.2)
217
223
  return q[qhbmin[1]][0]
218
224
  return q[qhbmin[1]+1][0]
219
225
  return slotset[0]
@@ -222,7 +228,6 @@ def _stator_slots(par, slots):
222
228
  def get_stator_dimensions(par, slots=[]):
223
229
  # Check symmetry
224
230
  if 'Q1' in par:
225
- from ..windings import Winding
226
231
  wdg = Winding({'Q': par['Q1'], 'p': par['p'], 'm': 3})
227
232
  # nominal (rated) operating parameters
228
233
  pnom = par['pnom']
@@ -237,7 +242,8 @@ def get_stator_dimensions(par, slots=[]):
237
242
 
238
243
  # pole width
239
244
  taup = np.pi * Da/(2*p)
240
- lfe = taup*lda
245
+ lfe_q = par.get('lfe_q', 0.001)
246
+ lfe = lfe_q * round(taup*lda/lfe_q)
241
247
 
242
248
  if 'udc' in par:
243
249
  u1nom = 0.9*par['udc']/np.sqrt(2)/np.sqrt(3) # phase voltage
@@ -254,8 +260,6 @@ def get_stator_dimensions(par, slots=[]):
254
260
  else:
255
261
  Q1 = par['Q1']
256
262
  coil_span = par.get('coil_span', 0)
257
- if np.gcd(Q1, 2*p) == coil_span:
258
- coil_span = 0
259
263
  layers = 1 if coil_span == 0 else 2
260
264
  req_poles = np.gcd(Q1, 2*p)
261
265
  if req_poles != 2*p:
@@ -282,7 +286,7 @@ def get_stator_dimensions(par, slots=[]):
282
286
  kw = kwz * kws
283
287
 
284
288
  # flux density amplitude of base harmonic
285
- if 'brem' in par:
289
+ if 'mag_width' in par:
286
290
  mag_width = par['mag_width']
287
291
  Bd1 = 4.0/np.pi*Ba*np.sin(np.pi/2.0*mag_width)
288
292
  else:
@@ -293,8 +297,36 @@ def get_stator_dimensions(par, slots=[]):
293
297
  # stator winding
294
298
  psi1 = 2.0/np.pi*taup*lfe*Bd1
295
299
 
300
+ # number of turns per phase, first estimation
296
301
  Ui = Ui_U * u1nom
297
- N = round(np.sqrt(2)*Ui/(2*np.pi*f1nom*kw*psi1), 1)
302
+ N = np.sqrt(2)*Ui/(2*np.pi*f1nom*kw*psi1)
303
+
304
+ # coils per phase and pole
305
+ # q = Q/2/p/m
306
+ # num wires per coil side (number of coil groups a)
307
+ # n = a*N / 2 / p / q
308
+
309
+ # feasible number of turns per coil...
310
+ ncoils = Q1 // 2 // m * layers
311
+ ngroups = [1] + [g for g in range(2, layers*p + 1) if layers * p % g == 0]
312
+ ndiff = [abs(N - ncoils // a * a * round(N / ncoils))
313
+ for a in ngroups]
314
+ logger.debug("N %f ngroups %s ndiffs %s",
315
+ N, ngroups, ndiff)
316
+ a_calc = ngroups[np.argmin(ndiff)]
317
+ a = par.get("a", a_calc)
318
+ if not a in ngroups:
319
+ logger.warning("Check given number %s of parallel wdg groups. Valid ngroups are: %s",
320
+ a, ngroups)
321
+ num_wires = round(a * N / ncoils)
322
+
323
+ # correction of number of turns per phase
324
+ N_old = N
325
+ N = num_wires * ncoils / a
326
+
327
+ # correction of voltage
328
+ Ui = Ui/N_old*N
329
+ u1nom = Ui/Ui_U
298
330
 
299
331
  # current loading
300
332
  # A = np.sqrt(2)*sigmas/kw/Ba
@@ -310,8 +342,14 @@ def get_stator_dimensions(par, slots=[]):
310
342
  bns = taus*(Da+2*hs1) - bds
311
343
 
312
344
  hns = (-bns + np.sqrt(bns**2 + 4*ans*np.tan(taus)))/2/np.tan(taus)
313
- hys = psi1/2/lfe*By
345
+ hys = psi1/2/lfe/By
314
346
 
347
+ aw = ans * kq / layers / num_wires
348
+ # round wire: pi*d²/4
349
+ dwire = 2 * np.sqrt(aw/np.pi)
350
+ wdg = Winding({'Q': Q1, 'p': p, 'm': 3, 'yd': coil_span, 'l': layers})
351
+ r1 = wdg_resistance(wdg, num_wires, a, aw, Da, hns, lfe)
352
+ relculen = 1.4
315
353
  # airgap and yoke diameter
316
354
  airgap = par['airgap']
317
355
  if par['external_rotor']:
@@ -336,9 +374,11 @@ def get_stator_dimensions(par, slots=[]):
336
374
  trv=round(1e-3*pnom/(2*np.pi*speednom)/(np.pi*Da1**2/4*lfe), 1),
337
375
  w1=int(N),
338
376
  kw=round(kw, 4),
339
- q=qt,
377
+ q=wdg.q,
340
378
  i1=round(I1, 3), # np.pi*Da1*A/2/m/N
341
- psi1=round(psi1, 5))
379
+ psi1=round(psi1, 5),
380
+ u1=u1nom,
381
+ ui=Ui)
342
382
 
343
383
  slotwidth = np.floor(0.3*np.pi*Da1/Q1*2000+0.5)/2000.0
344
384
  tw = bds # np.pi*(Da1+hns)/Q1 - bns
@@ -375,25 +415,14 @@ def get_stator_dimensions(par, slots=[]):
375
415
  wedge_width1=0, # bns1,
376
416
  wedge_width2=0,
377
417
  middle_line=0 if layers < 2 else middle_line))
378
- # coils per phase and pole
379
- # q = Q/2/p/m
380
- # num wires per coil side (number of coil groups a)
381
- # n = a*N / 2 / p / q
382
- ncoils = Q1//2//m*layers
383
- ngroups = [1] + [g for g in range(2, p+1) if p%g == 0]
384
- a = ngroups[np.argmin([abs(N-ncoils//a*round(a*N/ncoils))
385
- for a in ngroups])]
386
- num_wires = round(a*N/ncoils)
387
- dwire = 2*np.sqrt(ans*kq/layers/np.pi/num_wires)
388
- relculen = 1.4
389
- r1 = wdg_resistance(N, relculen*lfe, dwire)/a
418
+
390
419
  r['pcu'] = round(m*r1*I1**2, 1)
391
420
 
392
421
  r['winding'] = dict(
393
422
  wire_diam=round(dwire, 5),
394
423
  num_phases=m,
395
424
  cufilfact=kq,
396
- culength=1.4,
425
+ culength=relculen,
397
426
  num_par_wdgs=a,
398
427
  num_layers=layers,
399
428
  resistance=round(r1, 4),
@@ -413,8 +442,7 @@ def _get_magnet_height(I1, N, kw, par):
413
442
  Hc = Hc*1e3 # unit kA/m
414
443
  # Safety Factor for demagnetization
415
444
  demag = par['demag']
416
- I1max = 4*I1
417
- THETA1 = m/np.pi*np.sqrt(2)*I1max*N*kw/p
445
+ THETA1 = m/np.pi*np.sqrt(2)*I1*N*kw/p
418
446
  # minimal magnet height with safety factor
419
447
  hM = THETA1/Hc*demag
420
448
  Br = par['brem']
@@ -698,6 +726,7 @@ def ipm(pnom: float, speed: float, p: int, **kwargs) -> dict:
698
726
  pnom=pnom, speed=speed, p=p)
699
727
  par.update(kwargs)
700
728
  _set_pm_defaults(par)
729
+ par.pop('mag_width')
701
730
 
702
731
  # stator and magnet parameters of interior mounted magnet machine
703
732
  r = get_stator_dimensions(par)
femagtools/machine/sm.py CHANGED
@@ -102,6 +102,8 @@ def parident(workdir, engine, machine,
102
102
  num_cur_steps=kwargs.get('num_cur_steps', 5),
103
103
  num_beta_steps=kwargs.get('num_beta_steps', 13),
104
104
  num_par_wdgs=machine[wdgk].get('num_par_wdgs', 1),
105
+ skew_angle=kwargs.get('skew_angle', 0.0),
106
+ num_skew_steps=kwargs.get('num_skew_steps', 0.0),
105
107
  period_frac=kwargs.get('period_frac', 6),
106
108
  speed=kwargs.get('speed', 50))
107
109
 
@@ -338,9 +340,9 @@ class SynchronousMachine(object):
338
340
  def torquemax(self, i1, iex):
339
341
  "returns maximum torque of i1, iex (nan if i1 out of range)"
340
342
  def torquei1b(b):
341
- return -self.torque_iqd(*iqd(b[0], i1), iex)
343
+ return self.torque_iqd(*iqd(b[0], i1), iex)
342
344
  res = so.minimize(torquei1b, (0,))
343
- return -res.fun
345
+ return res.fun
344
346
 
345
347
  def torquemin(self, i1, iex):
346
348
  "returns minimum torque of i1, iex (nan if i1 out of range)"
@@ -576,7 +578,7 @@ class SynchronousMachine(object):
576
578
  i1max: max. phase current (RMS)
577
579
  """
578
580
  if kwargs.get('i1max', 0):
579
- w1type, T = self.w1_imax_umax(kwargs['i1max'], u1max)
581
+ w1type, T = self.w1_imax_umax(kwargs['i1max'], u1max)
580
582
  try:
581
583
  iq, id, iex = self.iqd_torque(T)
582
584
  except ValueError:
femagtools/nc.py CHANGED
@@ -195,7 +195,7 @@ class Reader(object):
195
195
  grp.variables['delta_node_angle'].getValue().data)
196
196
  self.poles_sim = int(grp.variables['poles_sim'].getValue().data)
197
197
  self.slots = int(grp.variables['num_slots'].getValue().data)
198
- self.arm_length = int(grp.variables['arm_length'].getValue().data)
198
+ self.arm_length = float(grp.variables['arm_length'].getValue().data)
199
199
  except:
200
200
  pass
201
201
 
@@ -281,10 +281,10 @@ class Reader(object):
281
281
  cw = float(mcgrp.variables['cw'][i].data)
282
282
  cw_freq_exp = float(mcgrp.variables['cw_exp'][i].data)
283
283
  cw_ind_exp = float(mcgrp.variables['ind_exp'][i].data)
284
- try:
284
+ try:
285
285
  ce = float(mcgrp.variables['ce'][i].data)
286
286
  cw_ind_beta_exp = float(mcgrp.variables['ind_beta_exp'][i].data)
287
- except:
287
+ except:
288
288
  pass
289
289
  spec_weight = float(mcgrp.variables['spec_weight'][i].data)
290
290
  fillfactor = float(mcgrp.variables['fillfac'][i].data)
@@ -8,7 +8,17 @@
8
8
  """
9
9
  import numpy as np
10
10
  import matplotlib.pyplot as plt
11
- from matplotlib import colormaps
11
+ try:
12
+ from matplotlib import colormaps
13
+ cmap_viridis = colormaps['viridis']
14
+ cmap_jet = colormaps['jet']
15
+ cmap_YlOrBr = colormaps['YlOrBr']
16
+ except: # older matplotlib
17
+ from matplotlib import cm
18
+ cmap_viridis = cm.viridis
19
+ cmap_jet = cm.jet
20
+ cmap_YlOrBr = cm.YlOrBr
21
+
12
22
 
13
23
  def _create_3d_axis():
14
24
  """creates a subplot with 3d projection if one does not already exist"""
@@ -24,7 +34,7 @@ def _create_3d_axis():
24
34
  plt.subplot(111, projection='3d')
25
35
 
26
36
 
27
- def _plot_surface(ax, x, y, z, labels, azim=None, cmap=colormaps['viridis']):
37
+ def _plot_surface(ax, x, y, z, labels, azim=None, cmap=cmap_viridis):
28
38
  """helper function for surface plots"""
29
39
  # ax.tick_params(axis='both', which='major', pad=-3)
30
40
  assert np.size(x) > 1 and np.size(y) > 1 and np.size(z) > 1
@@ -60,7 +70,7 @@ def forcedens(title, pos, fdens, ax=0):
60
70
  ax.set_ylabel('Force Density / kN/m²')
61
71
 
62
72
 
63
- def forcedens_surface(fdens, attr='FN', ax=0, cmap=colormaps['jet']):
73
+ def forcedens_surface(fdens, attr='FN', ax=0, cmap=cmap_jet):
64
74
  if ax == 0:
65
75
  _create_3d_axis()
66
76
  ax = plt.gca()
@@ -72,7 +82,7 @@ def forcedens_surface(fdens, attr='FN', ax=0, cmap=colormaps['jet']):
72
82
  ('Rotor pos/°', 'Pos/°', f'{attr} / kN/m²'),
73
83
  cmap=cmap)
74
84
 
75
- def forcedens_contour(fdens, attr='FN', ax=0, cmap=colormaps['jet']):
85
+ def forcedens_contour(fdens, attr='FN', ax=0, cmap=cmap_jet):
76
86
  if ax == 0:
77
87
  ax = plt.gca()
78
88
  z = 1e-3*np.array(getattr(fdens, attr))
@@ -91,7 +101,7 @@ def forcedens_contour(fdens, attr='FN', ax=0, cmap=colormaps['jet']):
91
101
  cbar.ax.set_ylabel('kN/m²')
92
102
 
93
103
  def forcedens_fft(title, fdens, harmmax=(), #(200, 40),
94
- cmap=colormaps['YlOrBr'],
104
+ cmap=cmap_YlOrBr,
95
105
  ax=0):
96
106
  """plot force densities FFT
97
107
  Args:
femagtools/tks.py CHANGED
@@ -21,6 +21,17 @@ numPattern = re.compile(r'([+-]?\d+(?:\.\d+)?(?:[eE][+-]\d+)?)')
21
21
  HBpattern = re.compile(r'H.+\s+B')
22
22
  BPpattern = re.compile(r'B.+\s+P')
23
23
 
24
+ _tranlate = {
25
+ "ch": "Hysteresis Loss Factor",
26
+ "cw": "Eddy Current Loss Factor",
27
+ "ce": "Excess Loss Factor",
28
+ "ch_freq": "Hyteresis Exponent",
29
+ "cw_freq": "Eddy Current Exponent",
30
+ "b_coeff": "Induction Loss Exponent",
31
+ "alpha": "Induction Loss Exponent (Bertotti)",
32
+ "Bo": "Reference Induction",
33
+ "fo": "Reference Frequency",
34
+ }
24
35
 
25
36
  def readlist(section):
26
37
  x = []
@@ -109,7 +120,8 @@ class Reader(object):
109
120
  self.cw = z[2]
110
121
  self.cw_freq = z[3]
111
122
  self.b_coeff = z[4]
112
-
123
+ self.jordan = {'ch': z[0], 'cw': z[2], 'ch_freq': z[1], 'cw_freq': z[3], 'b_coeff': z[4],
124
+ 'Bo': self.Bo, 'fo': self.fo}
113
125
  z = lc.fitsteinmetz(
114
126
  self.losses['f'],
115
127
  self.losses['B'],
@@ -122,11 +134,15 @@ class Reader(object):
122
134
  self.losses['cw_freq'] = z[1]
123
135
  self.losses['b_coeff'] = z[2]
124
136
 
137
+ self.steinmetz = {'cw': z[0], 'cw_freq': z[1], 'b_coeff': z[2],
138
+ 'Bo': self.Bo, 'fo': self.fo}
139
+
125
140
  self.losses['Bo'] = self.Bo
126
141
  self.losses['fo'] = self.fo
127
142
  z = lc.fit_bertotti(self.losses['f'],
128
143
  self.losses['B'], pfe)
129
- self.bertotti = {'ch': z[0], 'alpha': 2.0, 'cw': z[1], 'ce': z[2]}
144
+ self.bertotti = {'ch': z[0], 'cw': z[1], 'ce': z[2],
145
+ 'alpha': 2.0, 'Bo': 1, 'fo': 1}
130
146
  logger.info("Bertotti loss coeffs %s", z)
131
147
 
132
148
  # must normalize pfe matrix:
@@ -156,7 +172,27 @@ class Reader(object):
156
172
  'b_coeff': self.b_coeff,
157
173
  'rho': self.rho,
158
174
  'losses': self.losses}
159
-
175
+
176
+ def tableview(Reader):
177
+ """pretty print loss coeff table"""
178
+ losscoeff = [Reader.jordan, Reader.steinmetz, Reader.bertotti]
179
+ # Title
180
+ strlen = 101
181
+ print('='*strlen)
182
+ print('| {:^34} '.format(' ') + '| {:^18} | {:^18} | {:^18} |'.format(*["Jordan", "Steinmetz", 'Bertotti']))
183
+ print('='*strlen)
184
+ # data
185
+ for key, item in _tranlate.items():
186
+ fout = ''
187
+ for i in losscoeff:
188
+ if key in i:
189
+ fout += '| ' + f'{i[key]:^18.8e} '
190
+ else:
191
+ tmp = '-'
192
+ fout += '| ' + f'{tmp:^18} '
193
+ print(f'| {item:^34}' + ' ' + fout + '|')
194
+ print('='*strlen)
195
+ return
160
196
 
161
197
  def read(filename, filecontent=None):
162
198
  """read Thyssen File TKS and return mc dict"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: femagtools
3
- Version: 1.7.0
3
+ Version: 1.7.2
4
4
  Summary: Python API for FEMAG
5
5
  Author-email: Ronald Tanner <tar@semafor.ch>, Dapu Zhang <dzhang@gtisoft.com>, Beat Holm <hob@semafor.ch>, Günther Amsler <amg@semafor.ch>, Nicolas Mauchle <mau@semafor.ch>
6
6
  License: Copyright (c) 2016-2023, Semafor Informatik & Energie AG, Basel
@@ -1,9 +1,9 @@
1
- femagtools/__init__.py,sha256=EYWHBDOxDAUDvC6zezPTQAc32r4rSi4VgkvM4coG6f8,1600
1
+ femagtools/__init__.py,sha256=ndVtdwfscAu-jgdCzn_dAXiywoE6BRKaYEF9FkFp7s0,1600
2
2
  femagtools/airgap.py,sha256=ZCIZTYf6BbuNYx68y9fUDBQo3taN8RuGl8q-jJ7ygiA,1577
3
3
  femagtools/amazon.py,sha256=O1ICuv21XDAJi1qK1Sigs2TdS6hDZP19OzvmE2t76wU,12069
4
4
  femagtools/amela.py,sha256=pHjfXzpANI-7oz8MtrqNcyDZ6PxVM91vCJuvYhHy1rk,13891
5
5
  femagtools/asm.py,sha256=CiL0KWaF4P7O6-VwmGLdva_icwmPrPiI-TFQ19XYTKk,7660
6
- femagtools/bch.py,sha256=-TBHcXNf6bSvcDxi0qnVJV7ZQryDrsY6SCwwQLRyRQo,71680
6
+ femagtools/bch.py,sha256=a0Ak4xN5yYlt-0QCjLymYfVmsdYBNBCp7ZNORcaKWTs,73375
7
7
  femagtools/bchxml.py,sha256=KrRjAdrUPZXmiWvonu9HhpG_NvImMBpiXWTL4iSr4kE,3142
8
8
  femagtools/condor.py,sha256=J8z9iBdvrGu3I1eFNoyKV8AXzRoTEPGLSak6cXUQxAM,10766
9
9
  femagtools/conductor.py,sha256=rXO7c7Qh_s7JpgILmLd4IbG64vP6Eh143YF9u25Mdwg,1076
@@ -34,34 +34,34 @@ femagtools/model.py,sha256=7_LWgUQY3W-n4RBvPXbTY2FhfgiR0xlSSKTv2JqUX08,15618
34
34
  femagtools/moproblem.py,sha256=kOP8pRdD8YXz28_M2WKnFgl3eeJ7tqg49ohoazsmUOg,2825
35
35
  femagtools/multiproc.py,sha256=7mJF-VU1NrJkENyg8eHtDrDRNBwLPK43phZv3ehm9BU,8435
36
36
  femagtools/mxw2msh.py,sha256=CIIqAvfs8U-A0OfuOAoDaqNSmoMSHSI_tW1CPFRCP5E,2151
37
- femagtools/nc.py,sha256=TC7LY1FAL43ntA0IlGzUXhUsB05-xWzeZ1yP0q4L0A4,14532
37
+ femagtools/nc.py,sha256=6O3lWuX2SX8IuVM0rr55L2UywrI1MmXzJOgUtUmcg4A,14532
38
38
  femagtools/netlist.py,sha256=CSCl8setLZ_L8DCnNWaNA3-wLe1yo-fmzARZoVvYfaA,2052
39
39
  femagtools/ntib.py,sha256=76g1ZO3Fq_kN-HTMBvaKvJmMMlJMyEPFeNAcJPq3w7Y,3099
40
40
  femagtools/opt.py,sha256=snQlPNT5hksCgj0TBjFBhSCERMHHGMv1xc4uFSxptfQ,8687
41
41
  femagtools/parstudy.py,sha256=K78fd7ILhYjGgkauTXc_M8Plnynee0WFH20iyuoMXSo,19523
42
42
  femagtools/poc.py,sha256=wMwOxMhPLFRiGPMsKQwWWuGr6UZPzRBajhfVMfXptNU,6794
43
- femagtools/tks.py,sha256=UQMgOsPTGPiK9V0-e6wapmdSdiSGAlkBvoqnVKbSRI0,6049
43
+ femagtools/tks.py,sha256=xStMu6P1xsc36tS53FKnbL6i2ZTVnCNXyTVw8_gjepo,7436
44
44
  femagtools/ts.py,sha256=x9aCMVASjdBZuyI2pJGMyi1dveGFd_pWQ20cZ-l_moc,47216
45
45
  femagtools/utils.py,sha256=1SX5s21xyW8u0NF_Hjs7DByyCTdLm9VRArLburTyHR0,1581
46
46
  femagtools/vbf.py,sha256=9XGfhftmD9carU8ByQ5DwqoR4daq5mJ39eMqruwml0Q,2444
47
47
  femagtools/vtu.py,sha256=Sf83dHIfCKY2km-MIUHKKoj-JKN4PDX7kkPLZXyIYY4,10723
48
48
  femagtools/windings.py,sha256=6ioABZRWQ3xA4kmEz2NUkXh-C-FmW9YYkjQKs5YakQs,22197
49
49
  femagtools/dxfsl/__init__.py,sha256=MywcCdpKPKs4qJBJJgeDsikJFJ2P48dbTuNk303f5pM,76
50
- femagtools/dxfsl/area.py,sha256=tfkTwnrkFcw9OVYGXFvrsDMAka06Ed_mplJ8Cp5FaIg,59234
51
- femagtools/dxfsl/areabuilder.py,sha256=QBjfFyAnhZQN5LmqkNnxauPRLhiAGar0S1nsf4NT6y8,34001
50
+ femagtools/dxfsl/area.py,sha256=cYnDtnEuwuDW5R1NKV0s8PUzzQzxhgCUktFqHAjP9K8,56581
51
+ femagtools/dxfsl/areabuilder.py,sha256=Siu11yRcNJiSCWwc865-OvuVhSmLtRQWysbe1-rUcN0,34197
52
52
  femagtools/dxfsl/concat.py,sha256=F6scwesxyOmfmKQ5kGspNCxA71Yz6QgxFL7lTj3hsaI,13385
53
53
  femagtools/dxfsl/conv.py,sha256=GeRYUvbdcP5vKtNm5S5gU1yyMboGkGNyOqzkHpYbEEo,10086
54
- femagtools/dxfsl/converter.py,sha256=ihWGlLyuE5CmQL9lk-KilgbcMhzvKUltTHyHOoLG95g,31835
54
+ femagtools/dxfsl/converter.py,sha256=ZxOfF3bUQ2VXRjnUwDaAis3eRI-SiNEuM0UnSDpwda8,32353
55
55
  femagtools/dxfsl/corner.py,sha256=-XPBcnEau-2-SRHLYzlBqCQGaFfgm_DH2qR1mSaFoAs,1311
56
56
  femagtools/dxfsl/dumprenderer.py,sha256=n4AvInjvGIaC2iKZtQaYXXDyJVSQ3uEOFOLD4-xfKRY,1861
57
57
  femagtools/dxfsl/dxfparser.py,sha256=kyXG0kZfNyOgn96MqBgP8RhOQhppfB5NbyRNNybs1C0,13451
58
58
  femagtools/dxfsl/femparser.py,sha256=O8940Q1Mz8MKng6W8M3s9KfTvhDLJ56tfQWtZEW3xMM,2134
59
- femagtools/dxfsl/fslrenderer.py,sha256=DBE7qDlwIE5K8qD8ne7iNyaEVwHrpyis2TX1spteLsA,24979
59
+ femagtools/dxfsl/fslrenderer.py,sha256=ftI-nHv76ghAm3fkg_7ZAj-p-sWlIHYt7SbbsFvYWyY,24988
60
60
  femagtools/dxfsl/functions.py,sha256=F0AjzHfIfq3v-mhDzUOeq3zeYQCEsJl1-XpEyJQsSnQ,11805
61
- femagtools/dxfsl/geom.py,sha256=1IQGcPj7Fjj88EKqjd_UadoOCqeLraR9R0hOcbEkEIQ,162242
62
- femagtools/dxfsl/journal.py,sha256=_ZNBnM1gtl0eW6eV_sZ6nCpBNOsF_rnla0st8bKbeks,4159
63
- femagtools/dxfsl/machine.py,sha256=0NjJ9-ar820rkKV3lqCJ95-4iiAEdn5WhsmoD3w2bps,49228
64
- femagtools/dxfsl/plotrenderer.py,sha256=xoDT2Xh2N-OONACGOL8K6LHTHH5aRQ4EvohGt--EOD0,13713
61
+ femagtools/dxfsl/geom.py,sha256=-hPErBLyAsDh-aq2s54psFqG2AaNr9jVeKMeEVUQF5E,165356
62
+ femagtools/dxfsl/journal.py,sha256=S17B7wsrq5QzIUbjgg0ntvnpgH0thHq9aQXO7GdYlQQ,4265
63
+ femagtools/dxfsl/machine.py,sha256=3leWkjdMCLX2HgMhw8gNdxTKROhNCKU7KQrDVbBQ8DI,49337
64
+ femagtools/dxfsl/plotrenderer.py,sha256=q2cORuxJEf1Ws6oCY9c0gF6N3kDrcI6WOz3u5Vl6R_c,13823
65
65
  femagtools/dxfsl/shape.py,sha256=ZmWw7VTSJs5rcY1Lh99MKo804mO7dSGHdKMIh-nVBow,59904
66
66
  femagtools/dxfsl/svgparser.py,sha256=R8V2V5jE6JyVfzshJVaPgsSuMlQf_pwJTFvrTrKDxZ0,2905
67
67
  femagtools/dxfsl/symmetry.py,sha256=0WeDq_z42A_cjG4m_vREDWyx-wqHqXDkoCT2eXh2EMo,38084
@@ -69,9 +69,9 @@ femagtools/machine/__init__.py,sha256=U8W65K7jr7jDdC1KnJh0WjYd8DFaLnIFVvlh-TKcV9
69
69
  femagtools/machine/afpm.py,sha256=hNyDFRLGmCuWRPZl_u1ztJ4pA-Y_mxLaVvg3UJkzRuE,24766
70
70
  femagtools/machine/effloss.py,sha256=sLB8AXYVAE_AL1Ca41A2EDbn9_7u6xNjaYFjmRPoTDg,13578
71
71
  femagtools/machine/im.py,sha256=3Y54AHMZfAjkvgexx2E-5jxNWzaVQ-SyaETCh7gNBYA,38008
72
- femagtools/machine/pm.py,sha256=N4UEpOEYpvJzGjWt4SLOpgMrjzoSYyuxpi-8wlT1U5A,58565
73
- femagtools/machine/sizing.py,sha256=YsvQye2hN-v_GLDeODmFrHkNQlkNWpQ9CkrZ0maS93k,23328
74
- femagtools/machine/sm.py,sha256=ubS7mK2dibhwUpGwyY-23hoZbLrVqAtPeXVmqF657lg,37781
72
+ femagtools/machine/pm.py,sha256=JQn2vnPBvGF9mUnqnKiVisHZnjhpzIbaBkqUxKN5leE,58589
73
+ femagtools/machine/sizing.py,sha256=nWCfxbyWfbw5-7xu0qZ6zjWNquEPn3fUH-fQeGb6QUc,24307
74
+ femagtools/machine/sm.py,sha256=G4fHmZngQSRN9Dum7mHaf36b_CvD-u_AQogIFixlnys,37899
75
75
  femagtools/machine/utils.py,sha256=ckPOfaaieRbThMKkBtCtoCMsehdbePH8xNoWPmNoS3Y,18782
76
76
  femagtools/moo/__init__.py,sha256=zinmWEOrsEz6DmMX0Dbn4t6_1UR-p4bEGqyR1wUQk_Q,175
77
77
  femagtools/moo/algorithm.py,sha256=lNEf0Bur4yFpIJeLtAC3oIus8sOMWTb7jepFlD28YzE,5445
@@ -85,7 +85,7 @@ femagtools/plot/bch.py,sha256=4f9Q-JZtj9WMjxFUXMXNaSdWdDXN_Be4-F9kOU45xns,28525
85
85
  femagtools/plot/char.py,sha256=SB40YJ5seVQmWTAdrfDQZiEZH3jIB11HUY5SJL_HJcY,12032
86
86
  femagtools/plot/fieldlines.py,sha256=QtKF4nhnQ_FHHGh9Qez3GVmym0CLhW1ZyIKtk4pzos4,1136
87
87
  femagtools/plot/fluxdens.py,sha256=NlexRJ3f_8CgKoWrV82ZIsAXPrLhwj98uOe8_fUks7A,1082
88
- femagtools/plot/forcedens.py,sha256=aBxMUnMkE5Ost5BQuAYI28aN9bC2gWZN65xHqwFsuhw,3849
88
+ femagtools/plot/forcedens.py,sha256=Vloi9czy7qbGXI-Vm7Cow6IfHTsFhCLI1YWduFOR55c,4075
89
89
  femagtools/plot/mcv.py,sha256=NwDBe40PQgKqWh_rM89k-vsxRmvyjetv4y7elXzTPZU,3099
90
90
  femagtools/plot/nc.py,sha256=yq-uYppAEUePbo1SAaBKNKla3cyqdLh5z9ArhtwPYZY,9548
91
91
  femagtools/plot/phasor.py,sha256=5QG1GkXKVksc8P6Q4thKADf6W1l8rDKeArIHFYvbXlw,4858
@@ -166,7 +166,7 @@ tests/test_afpm.py,sha256=OE-ULFcp_fBQ7SyRacDmDGbq8ULFHLbXXx3MHJN1C1c,11031
166
166
  tests/test_airgap_induction.py,sha256=cmpy1og59oWEyCO4oF_zDuc1DUwCbdeebNL1ujpZza4,1065
167
167
  tests/test_amela.py,sha256=IsGah_O8qWsudZOnWL0pjH9S40O03oH_XfDtLgnzBMk,637
168
168
  tests/test_asm.py,sha256=NAMJ2Km4zi6byhooq5E7GZbkFeSNSTaLpJWt-jQF5cE,1398
169
- tests/test_bchreader.py,sha256=byqjDa_329cCgQFhLHLOgxiwZ0UMzvWb-rVjvN9wFQA,15497
169
+ tests/test_bchreader.py,sha256=yFpjN0MIv_o_OD8XH9HxlOpcNtL79K_3bXMeupxO648,16843
170
170
  tests/test_conductor.py,sha256=T7bmuSdI2Fowwm2Ht-8D0Qnfs1_lx1aoi9RhC2p5nYU,332
171
171
  tests/test_convert.py,sha256=oseuhm3Iw-9b4WzBjA_Og2lgXPqKYicLUnfadq5Yick,6497
172
172
  tests/test_dxfsl.py,sha256=DBAjtwNAwcB7VYZBjul7xf4ovu14n3Z0fxyreVzAW4A,383
@@ -193,7 +193,7 @@ tests/test_nc.py,sha256=kIFUOlFy_jQUjLnS9wuy6c2YkPOrKyXlMtGxDIJnXsw,4205
193
193
  tests/test_parident.py,sha256=WWCyRgqqX3bcp0Q7Pwxz4dN2kklIm5E7kBvqkO9m718,1340
194
194
  tests/test_parstudy.py,sha256=wk7WfYQrx5dtv6MnmWCfNAEInvRKsbVXYEUIIR9zLbQ,3200
195
195
  tests/test_pocfile.py,sha256=eiMLBRQxDnHIGiqku6EXcQ3fb7wGIxhXmb20iyLlPRU,5816
196
- tests/test_sizing.py,sha256=8tIT8DG1c28aSwkPD4qPryKk3sU2JhXWLYYxhmDQMbM,1131
196
+ tests/test_sizing.py,sha256=ThgH3xscoDDqDsyECXQzsU16xOeUOx83_K2RFwRBeiw,1133
197
197
  tests/test_sm.py,sha256=FjcAbx4n7kLH00kjnKoGMk6t4_V9TJzIaxM21MkrOjE,83524
198
198
  tests/test_tksreader.py,sha256=8QtPAzxPJbkpxd1Nw2I7ggaTaKaL4WY55JJBHkZAzus,766
199
199
  tests/test_ts.py,sha256=tR2x5cKU9gw2fUprzaPgPbCvmDOHDO36JUPCCoTlY7Y,1833
@@ -210,9 +210,9 @@ tests/moo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
210
210
  tests/moo/test_algorithm.py,sha256=Em8sFm2vzPmuIzRrBBnUQLU_TYuJHSf-kEeozw0XeX4,2563
211
211
  tests/moo/test_population.py,sha256=FvX9LRCxQx0_E2GxHQ5vKwOYFBQiNbT6Lmv5GmNWjTQ,5471
212
212
  tests/moo/test_problem.py,sha256=ALeP4u7g-dFhfwWL8vxivdrrYzVKPjHMCAXzzgyNZbs,467
213
- femagtools-1.7.0.dist-info/LICENSE,sha256=NaQe4uvkszQPJmiRPHecfk-Ab9VSRXo8xQLGNVHTeFo,1362
214
- femagtools-1.7.0.dist-info/METADATA,sha256=g4DD1r1-fho5dUKJw-PG64StC94fHJCUiBpqT0c0Js8,6147
215
- femagtools-1.7.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
216
- femagtools-1.7.0.dist-info/entry_points.txt,sha256=jrvOkZPiN44u1sASeu271VRaVIv5V-uRpN0_N5U_R8c,248
217
- femagtools-1.7.0.dist-info/top_level.txt,sha256=Ri4YWtU8MZTzNje9IKyXhTakNbsrCynuWdon4Yq94Dc,17
218
- femagtools-1.7.0.dist-info/RECORD,,
213
+ femagtools-1.7.2.dist-info/LICENSE,sha256=NaQe4uvkszQPJmiRPHecfk-Ab9VSRXo8xQLGNVHTeFo,1362
214
+ femagtools-1.7.2.dist-info/METADATA,sha256=lsExsTeOX67OhhhQnuXXoRqoNYGVLvjtHkKqTonSFsk,6147
215
+ femagtools-1.7.2.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
216
+ femagtools-1.7.2.dist-info/entry_points.txt,sha256=jrvOkZPiN44u1sASeu271VRaVIv5V-uRpN0_N5U_R8c,248
217
+ femagtools-1.7.2.dist-info/top_level.txt,sha256=Ri4YWtU8MZTzNje9IKyXhTakNbsrCynuWdon4Yq94Dc,17
218
+ femagtools-1.7.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (70.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
tests/test_bchreader.py CHANGED
@@ -317,6 +317,25 @@ class BchReaderTest(unittest.TestCase):
317
317
  self.assertEqual(bch.psidq['losses']['styoke_excess'][0], [2.235, 2.235, 2.839])
318
318
  self.assertEqual(bch.psidq['losses']['stteeth_excess'][0], [6.58, 6.576, 7.176])
319
319
  self.assertEqual(bch.psidq['losses']['rotor_excess'][0], [0.0, 0.0, 0.0])
320
-
320
+
321
+ def test_read_pm_sym_outer_rotor(self):
322
+ bch = self.read_bch('pm_sym_fast_outer_rotor.BATCH')
323
+ self.assertEqual(bch.losses[-1]['staza'], 54.219)
324
+ self.assertEqual(bch.losses[-1]['stajo'], 8.853)
325
+ self.assertEqual(bch.losses[-1]['rotfe'], 0.091)
326
+ self.assertEqual(list(bch.losses[-1]['fft']['rotor']['order_mech']), [12, 24])
327
+ self.assertEqual(list(bch.losses[-1]['fft']['rotor']['eddy']), [0.057, 0.033])
328
+ self.assertEqual(list(bch.losses[-1]['fft']['staza']['order_mech']), [4, 12, 20, 28, 36, 44, 52])
329
+ self.assertEqual(list(bch.losses[-1]['fft']['staza']['eddy']), [45.215, 4.582, 2.881, 0.839, 0.425, 0.197, 0.059])
330
+ self.assertEqual(list(bch.losses[-1]['fft']['stajo']['order_mech']), [4, 12, 20, 28, 36])
331
+ self.assertEqual(list(bch.losses[-1]['fft']['stajo']['eddy']), [7.845, 0.64, 0.311, 0.044, 0.011])
332
+
333
+ def test_read_ldlq_outer_rotor(self):
334
+ bch = self.read_bch('ldlq_outer_rotor.BATCH')
335
+ self.assertEqual(bch.ldq['losses']['styoke_eddy'][0], [63.32, 39.69, 42.85, 65.62])
336
+ self.assertEqual(bch.ldq['losses']['styoke_hyst'][0], [0.0, 0.0, 0.0, 0.0])
337
+ self.assertEqual(bch.ldq['losses']['rotor_eddy'][0], [0.05937, 0.4195, 1.74, 2.705])
338
+ self.assertEqual(bch.ldq['losses']['rotor_hyst'][0], [0.0, 0.0, 0.0, 0.0])
339
+
321
340
  if __name__ == '__main__':
322
341
  unittest.main()
tests/test_sizing.py CHANGED
@@ -16,7 +16,7 @@ def test_im():
16
16
  sigmas=fs, Ba=0.77,
17
17
  cos_phi=0.8, eta=0.8,
18
18
  lda=0.9, rtype='statorRotor3')
19
- assert round(r['outer_diam'], 3) == 0.19
19
+ assert round(r['outer_diam'], 3) == 0.182
20
20
  assert r['stator']['num_slots'] == 36
21
21
 
22
22
 
@@ -31,9 +31,9 @@ def test_spm():
31
31
 
32
32
  r = femagtools.machine.sizing.spm(P, n, p, udc=udc,
33
33
  Hc=700, sigmas=fs, brem=1.1, Ba=0.77,
34
- cos_phi=0.7, eta=0.8, demag=1.7,
34
+ cos_phi=0.7, eta=0.8, demag=6.8,
35
35
  lda=0.9)
36
- assert round(r['outer_diam'], 3) == 0.19
36
+ assert round(r['outer_diam'], 3) == 0.181
37
37
  assert r['stator']['num_slots'] == 24
38
38
 
39
39