femagtools 1.8.2__py3-none-any.whl → 1.8.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. femagtools/__init__.py +1 -1
  2. femagtools/dxfsl/area.py +65 -0
  3. femagtools/dxfsl/conv.py +5 -0
  4. femagtools/dxfsl/converter.py +34 -1
  5. femagtools/dxfsl/functions.py +14 -6
  6. femagtools/dxfsl/geom.py +12 -12
  7. femagtools/dxfsl/journal.py +1 -1
  8. femagtools/dxfsl/symmetry.py +28 -8
  9. femagtools/femag.py +64 -61
  10. femagtools/fsl.py +5 -2
  11. femagtools/isa7.py +3 -2
  12. femagtools/machine/afpm.py +43 -23
  13. femagtools/machine/effloss.py +29 -18
  14. femagtools/machine/sizing.py +4 -3
  15. femagtools/machine/sm.py +34 -36
  16. femagtools/mcv.py +56 -26
  17. femagtools/multiproc.py +79 -80
  18. femagtools/parstudy.py +10 -4
  19. femagtools/semi_fea.py +108 -0
  20. femagtools/templates/basic_modpar.mako +0 -3
  21. femagtools/templates/fe-contr.mako +18 -18
  22. femagtools/templates/ld_lq_fast.mako +3 -0
  23. femagtools/templates/mult_cal_fast.mako +3 -0
  24. femagtools/templates/pm_sym_f_cur.mako +4 -1
  25. femagtools/templates/pm_sym_fast.mako +3 -0
  26. femagtools/templates/pm_sym_loss.mako +3 -0
  27. femagtools/templates/psd_psq_fast.mako +3 -0
  28. femagtools/templates/torq_calc.mako +3 -0
  29. femagtools/tks.py +23 -20
  30. femagtools/zmq.py +213 -0
  31. {femagtools-1.8.2.dist-info → femagtools-1.8.3.dist-info}/METADATA +3 -3
  32. {femagtools-1.8.2.dist-info → femagtools-1.8.3.dist-info}/RECORD +40 -38
  33. {femagtools-1.8.2.dist-info → femagtools-1.8.3.dist-info}/WHEEL +1 -1
  34. tests/test_afpm.py +15 -6
  35. tests/test_femag.py +1 -1
  36. tests/test_fsl.py +4 -4
  37. tests/test_mcv.py +20 -14
  38. {femagtools-1.8.2.dist-info → femagtools-1.8.3.dist-info}/LICENSE +0 -0
  39. {femagtools-1.8.2.dist-info → femagtools-1.8.3.dist-info}/entry_points.txt +0 -0
  40. {femagtools-1.8.2.dist-info → femagtools-1.8.3.dist-info}/top_level.txt +0 -0
femagtools/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  """
4
4
  __title__ = 'femagtools'
5
- __version__ = '1.8.2'
5
+ __version__ = '1.8.3'
6
6
  __author__ = 'Ronald Tanner'
7
7
  __license__ = 'BSD'
8
8
  __copyright__ = 'Copyright 2023-2024 Gamma Technology'
femagtools/dxfsl/area.py CHANGED
@@ -12,6 +12,7 @@ import numpy as np
12
12
  import networkx as nx
13
13
  import logging
14
14
  from .functions import less_equal, less, greater_equal, greater
15
+ from .functions import greater_angle, less_angle
15
16
  from .functions import distance, alpha_angle, alpha_line, min_angle, max_angle
16
17
  from .functions import point, line_m, line_n, intersect_point, points_are_close
17
18
  from .functions import middle_angle, part_of_circle, is_same_angle
@@ -75,6 +76,10 @@ class Area(object):
75
76
  self.start = 0.0
76
77
  self.sym_startangle = 0.0
77
78
  self.sym_endangle = 0.0
79
+ self.sym_upper_left_dist = None
80
+ self.sym_upper_right_dist = None
81
+ self.sym_lower_left_dist = None
82
+ self.sym_lower_right_dist = None
78
83
  self.sym_type = 0
79
84
  self.symmetry = 0
80
85
  self.sym_tolerance = sym_tolerance
@@ -674,6 +679,66 @@ class Area(object):
674
679
  return True
675
680
  return False
676
681
 
682
+ def set_symmetry_parameter(self, center):
683
+ all_list = [(distance(center, n), alpha_line(center, n))
684
+ for n in self.list_of_nodes()]
685
+ mid = middle_angle(self.min_angle, self.max_angle)
686
+ left_list = [(d, a) for d, a in all_list if greater_angle(a, mid)]
687
+ right_list = [(d, a) for d, a in all_list if less_angle(a, mid)]
688
+ left_list.sort()
689
+ right_list.sort()
690
+
691
+ if left_list:
692
+ l_low_d, l_low_a = left_list[0]
693
+ l_up_d, l_up_a = left_list[-1]
694
+ else:
695
+ l_low_d = self.min_dist
696
+ l_up_d = self.max_dist
697
+ if right_list:
698
+ r_low_d, r_low_a = right_list[0]
699
+ r_up_d, r_up_a = right_list[-1]
700
+ else:
701
+ r_low_d = self.min_dist
702
+ r_up_d = self.max_dist
703
+ self.sym_upper_left_dist = l_up_d
704
+ self.sym_upper_right_dist = r_up_d
705
+ self.sym_lower_left_dist = l_low_d
706
+ self.sym_lower_right_dist = r_low_d
707
+
708
+ def is_symmetry_equal(self, area):
709
+ logger.debug("check area %s -- %s", self.get_id(), area.get_id())
710
+
711
+ bad = False
712
+ if not np.isclose(self.sym_lower_left_dist, area.sym_lower_left_dist,
713
+ rtol=5e-1, atol=5e-1):
714
+ logger.debug("Lower left: %s != %s",
715
+ self.sym_lower_left_dist,
716
+ area.sym_lower_left_dist)
717
+ bad = True
718
+
719
+ if not np.isclose(self.sym_lower_right_dist, area.sym_lower_right_dist,
720
+ rtol=5e-1, atol=5e-1):
721
+ logger.debug("Lower right: %s != %s",
722
+ self.sym_lower_right_dist,
723
+ area.sym_lower_right_dist)
724
+ bad = True
725
+
726
+ if not np.isclose(self.sym_upper_left_dist, area.sym_upper_left_dist,
727
+ rtol=5e-1, atol=5e-1):
728
+ logger.debug("Upper left: %s != %s",
729
+ self.sym_upper_left_dist,
730
+ area.sym_upper_left_dist)
731
+ bad = True
732
+
733
+ if not np.isclose(self.sym_upper_right_dist, area.sym_upper_right_dist,
734
+ rtol=5e-1, atol=5e-1):
735
+ logger.debug("Upper right: %s != %s",
736
+ self.sym_upper_right_dist,
737
+ area.sym_upper_right_dist)
738
+ bad = True
739
+
740
+ return not bad
741
+
677
742
  def increment(self, a):
678
743
  if self.is_identical(a):
679
744
  return
femagtools/dxfsl/conv.py CHANGED
@@ -115,6 +115,10 @@ def main():
115
115
  help='create fsl',
116
116
  dest='write_fsl',
117
117
  action="store_true")
118
+ argparser.add_argument('--fsl_single',
119
+ help='create separate fsl for rotor and stator',
120
+ dest='write_fsl_single',
121
+ action="store_true")
118
122
  argparser.add_argument('-v', '--view',
119
123
  help='show a view only',
120
124
  dest='view',
@@ -242,6 +246,7 @@ def main():
242
246
  show_areas=args.show_areas,
243
247
  small_plots=args.small_plots,
244
248
  write_fsl=args.write_fsl,
249
+ write_fsl_single=args.write_fsl_single,
245
250
  write_png=args.write_png,
246
251
  write_id=args.write_id,
247
252
  debug_mode=args.debugger,
@@ -335,6 +335,7 @@ def convert(dxfile,
335
335
  show_areas=False,
336
336
  small_plots=False,
337
337
  write_fsl=True,
338
+ write_fsl_single=False,
338
339
  write_png=False,
339
340
  write_id=False,
340
341
  full_model=False,
@@ -388,6 +389,9 @@ def convert(dxfile,
388
389
  split_ini = False
389
390
  split_cpy = split
390
391
 
392
+ if write_fsl_single:
393
+ write_fsl = True
394
+
391
395
  try:
392
396
  if input_file.suffix in ['.fem', '.FEM']:
393
397
  from .femparser import femshapes
@@ -719,6 +723,14 @@ def convert(dxfile,
719
723
  fslrenderer = FslRenderer(basename, mtype)
720
724
  inner = fslrenderer.render(machine_inner, inner=True)
721
725
  outer = fslrenderer.render(machine_outer, outer=True)
726
+ if write_fsl_single:
727
+ inner_single = fslrenderer.render(machine_inner, inner=True,
728
+ standalone=True)
729
+ outer_single = fslrenderer.render(machine_outer, outer=True,
730
+ standalone=True)
731
+ else:
732
+ inner_single = None
733
+ outer_single = None
722
734
  if full_model:
723
735
  params['num_sl_gen'] = params.get('tot_num_slot', 0)
724
736
  params['agndst'] = agndst(params.get('da1'),
@@ -729,10 +741,18 @@ def convert(dxfile,
729
741
 
730
742
  if params['external_rotor']:
731
743
  conv['fsl_rotor'] = outer
744
+ if outer_single:
745
+ conv['fsl_rotor_single'] = outer_single
732
746
  conv['fsl_stator'] = inner
747
+ if inner_single:
748
+ conv['fsl_stator_single'] = inner_single
733
749
  else:
734
750
  conv['fsl_rotor'] = inner
751
+ if inner_single:
752
+ conv['fsl_rotor_single'] = inner_single
735
753
  conv['fsl_stator'] = outer
754
+ if outer_single:
755
+ conv['fsl_stator_single'] = outer_single
736
756
 
737
757
  conv['fsl'] = fslrenderer.render_main(
738
758
  machine_inner, machine_outer,
@@ -875,11 +895,24 @@ def convert(dxfile,
875
895
  if write_fsl:
876
896
  logger.debug("Write fsl")
877
897
  if conv and conv['fsl']:
878
- with io.open(basename + '.fsl', 'w', encoding='utf-8') as f:
898
+ with io.open(basename + '.fsl', 'w',
899
+ encoding='utf-8') as f:
879
900
  f.write('\n'.join(conv['fsl']))
880
901
  else:
881
902
  logger.warning("No fsl data available")
882
903
 
904
+ if conv:
905
+ if conv.get('fsl_rotor_single', None):
906
+ with io.open(basename + '_ROTOR.fsl', 'w',
907
+ encoding='utf-8') as f:
908
+ f.write('\n'.join(conv['fsl_rotor_single']))
909
+ del conv['fsl_rotor_single']
910
+ if conv.get('fsl_stator_single', None):
911
+ with io.open(basename + '_STATOR.fsl', 'w',
912
+ encoding='utf-8') as f:
913
+ f.write('\n'.join(conv['fsl_stator_single']))
914
+ del conv['fsl_stator_single']
915
+
883
916
  conv['name'] = basename
884
917
  t = timer.stop("-- all done in %0.4f seconds --", info=True)
885
918
  journal.put('time_total', t)
@@ -85,16 +85,24 @@ def alpha_angle(startangle, endangle):
85
85
  return angle - 2.0*np.pi
86
86
 
87
87
 
88
- def max_angle(alpha1, alpha2):
88
+ def less_angle(alpha1, alpha2):
89
89
  angle = alpha_angle(alpha1, alpha2)
90
- if angle < np.pi or angle > 2.0*np.pi:
91
- return alpha2
92
- return alpha1
90
+ return (angle < np.pi or angle > 2.0*np.pi)
93
91
 
94
92
 
95
- def min_angle(alpha1, alpha2):
93
+ def greater_angle(alpha1, alpha2):
96
94
  angle = alpha_angle(alpha1, alpha2)
97
- if angle < np.pi or angle > 2.0*np.pi:
95
+ return not (angle < np.pi or angle > 2.0*np.pi)
96
+
97
+
98
+ def max_angle(alpha1, alpha2):
99
+ if greater_angle(alpha1, alpha2):
100
+ return alpha1
101
+ return alpha2
102
+
103
+
104
+ def min_angle(alpha1, alpha2):
105
+ if less_angle(alpha1, alpha2):
98
106
  return alpha1
99
107
  return alpha2
100
108
 
femagtools/dxfsl/geom.py CHANGED
@@ -1986,7 +1986,7 @@ class Geometry(object):
1986
1986
  startangle=0.0,
1987
1987
  endangle=np.pi/2)
1988
1988
 
1989
- elif np.isclose(width, height*2, self.rtol, self.atol):
1989
+ elif np.isclose(width, height*2, rtol=1e-2, atol=1e-2):
1990
1990
  radius = width/2
1991
1991
  self.set_center([mm[1]-height, mm[2]])
1992
1992
  logger.info("check for half machine")
@@ -2005,7 +2005,7 @@ class Geometry(object):
2005
2005
  startangle=np.pi,
2006
2006
  endangle=0.0)
2007
2007
 
2008
- elif np.isclose(width*2, height, self.rtol, self.atol):
2008
+ elif np.isclose(width*2, height, rtol=1e-2, atol=1e-2):
2009
2009
  radius = width
2010
2010
  logger.info("check for half machine")
2011
2011
  self.set_center([mm[1], mm[3]-width])
@@ -2409,31 +2409,31 @@ class Geometry(object):
2409
2409
  logger.debug("end check_airgap: return %s", len(area_id_list) > 1)
2410
2410
  return len(area_id_list) > 1 # bad
2411
2411
 
2412
- def is_border_line(self, center, startangle, endangle, e, atol):
2412
+ def is_border_line(self, center, startangle, endangle, e, rtol=1e-3, atol=1e-3):
2413
2413
  if isinstance(e, Line):
2414
2414
  if np.isclose(startangle, endangle):
2415
2415
  return False # full
2416
2416
 
2417
2417
  if points_are_close(center, e.p1):
2418
2418
  angle_p2 = alpha_line(center, e.p2)
2419
- if np.isclose(startangle, angle_p2, 1e-3, atol):
2419
+ if np.isclose(startangle, angle_p2, rtol=rtol, atol=atol):
2420
2420
  return True
2421
- return np.isclose(endangle, angle_p2, 1e-3, atol)
2421
+ return np.isclose(endangle, angle_p2, rtol=rtol, atol=atol)
2422
2422
 
2423
2423
  if points_are_close(center, e.p2):
2424
2424
  angle_p1 = alpha_line(center, e.p1)
2425
- if np.isclose(startangle, angle_p1, 1e-3, atol):
2425
+ if np.isclose(startangle, angle_p1, rtol=rtol, atol=atol):
2426
2426
  return True
2427
- return np.isclose(endangle, angle_p1, 1e-3, atol)
2427
+ return np.isclose(endangle, angle_p1, rtol=rtol, atol=atol)
2428
2428
 
2429
2429
  angle_p1 = alpha_line(center, e.p1)
2430
- if np.isclose(startangle, angle_p1, 1e-3, atol):
2430
+ if np.isclose(startangle, angle_p1, rtol=rtol, atol=atol):
2431
2431
  angle_p2 = alpha_line(center, e.p2)
2432
- return np.isclose(startangle, angle_p2, 1e-3, atol)
2432
+ return np.isclose(startangle, angle_p2, rtol=rtol, atol=atol)
2433
2433
 
2434
- if np.isclose(endangle, angle_p1, 1e-3, atol):
2434
+ if np.isclose(endangle, angle_p1, rtol=rtol, atol=atol):
2435
2435
  angle_p2 = alpha_line(center, e.p2)
2436
- return np.isclose(endangle, angle_p2, 1e-3, atol)
2436
+ return np.isclose(endangle, angle_p2, rtol=rtol, atol=atol)
2437
2437
  return False
2438
2438
 
2439
2439
  def get_gaplist(self, center):
@@ -2457,7 +2457,7 @@ class Geometry(object):
2457
2457
  """
2458
2458
  gaplist = []
2459
2459
  for e in self.elements(Shape):
2460
- if not self.is_border_line(center, startangle, endangle, e, atol):
2460
+ if not self.is_border_line(center, startangle, endangle, e, atol=atol):
2461
2461
  gaplist += [e.minmax_from_center(center)]
2462
2462
  else:
2463
2463
  min_r, max_r = e.minmax_from_center(center)
@@ -18,7 +18,7 @@ logger = logging.getLogger('femagtools.journal')
18
18
  journal = None
19
19
 
20
20
  #############################
21
- # concat #
21
+ # journal #
22
22
  #############################
23
23
 
24
24
 
@@ -102,6 +102,7 @@ class Symmetry(object):
102
102
  a.get_mid_angle(self.geom.center)))
103
103
 
104
104
  def area_list_entry(self, a):
105
+ a.set_symmetry_parameter(self.geom.center)
105
106
  return (round(a.get_alpha(self.geom.center), 3),
106
107
  round(a.min_dist, 1),
107
108
  round(a.height, 1),
@@ -124,19 +125,34 @@ class Symmetry(object):
124
125
  equal_areas = [(a0_mid_angle, a0)]
125
126
  check_rslt = []
126
127
  for a1_alpha, a1_min_dist, a1_height, a1_mid_angle, a1 in areas[1:]:
127
- if self.equal_area(a0_min_dist, a0_height, a0_alpha,
128
- a1_min_dist, a1_height, a1_alpha,
129
- rtol=0.001, atol=0.05):
128
+ if (self.equal_area(a0_min_dist, a0_height, a0_alpha,
129
+ a1_min_dist, a1_height, a1_alpha,
130
+ rtol=0.001, atol=0.05)):
130
131
  a0_min_dist = (a0_min_dist + a1_min_dist) / 2
131
132
  a0_height = (a0_height + a1_height) / 2
132
133
  a0_alpha = (a0_alpha + a1_alpha) / 2
133
134
  equal_areas.append((a1_mid_angle, a1))
134
135
  else:
135
- rslt = self.check_delta(equal_areas)
136
- areasize = a0.area_size()
137
- rslt['area'] = a0
138
- rslt['areasize'] = areasize
139
- check_rslt.append((areasize, rslt))
136
+ # alpha Wechsel
137
+ id_list = []
138
+ for i in range(len(equal_areas)):
139
+ mid_angle0, area0 = equal_areas[i]
140
+ if area0.get_id() in id_list:
141
+ continue
142
+ equal_areas_check = [(mid_angle0, area0)]
143
+ for mid_angle1, area1 in equal_areas[i+1:]:
144
+ if area1.get_id() in id_list:
145
+ continue
146
+ if area0.is_symmetry_equal(area1):
147
+ equal_areas_check.append((mid_angle1, area1))
148
+ id_list.append(area1.get_id())
149
+
150
+ rslt = self.check_delta(equal_areas_check)
151
+ areasize = a0.area_size()
152
+ rslt['area'] = a0
153
+ rslt['areasize'] = areasize
154
+ check_rslt.append((areasize, rslt))
155
+
140
156
  equal_areas = [(a1_mid_angle, a1)]
141
157
  a0_min_dist = a1_min_dist
142
158
  a0_height = a1_height
@@ -154,6 +170,7 @@ class Symmetry(object):
154
170
  if inside:
155
171
  areas = [self.area_list_entry(a) for a in self.geom.list_of_areas()
156
172
  if not a.close_to_ag]
173
+ areas.sort(reverse=True)
157
174
  else:
158
175
  areas = self.build_area_list((AREA.TYPE_WINDINGS,))
159
176
 
@@ -228,6 +245,8 @@ class Symmetry(object):
228
245
  return 0
229
246
 
230
247
  check_rslt = self.build_results(areas)
248
+ logger.debug("%s results available", len(check_rslt))
249
+ [logger.debug("Result: %s", rslt) for rslt in check_rslt]
231
250
 
232
251
  parts, start_delta = self.get_symmetry_parts(check_rslt)
233
252
  if parts < 2:
@@ -248,6 +267,7 @@ class Symmetry(object):
248
267
  result = {'areas': len(area_list),
249
268
  'startdelta': 0.0,
250
269
  'slices': None}
270
+ result['area_id_list'] = [a.get_id() for m, a in area_list]
251
271
  if not area_list:
252
272
  logger.debug("end of check_delta: no areas")
253
273
  return result
femagtools/femag.py CHANGED
@@ -27,6 +27,7 @@ import femagtools.fsl
27
27
  import femagtools.config
28
28
  import femagtools.ecloss
29
29
  import femagtools.forcedens
30
+ import femagtools.zmq
30
31
 
31
32
  from femagtools import ntib
32
33
 
@@ -180,15 +181,22 @@ class BaseFemag(object):
180
181
  def copy_winding_file(self, name, wdg):
181
182
  wdg.write(name, self.workdir)
182
183
 
183
- def copy_magnetizing_curves(self, model, dir=None, recsin=''):
184
+ def copy_magnetizing_curves(self, model, dir=None, recsin='', feloss=''):
184
185
  """extract mc names from model and write files into workdir or dir if given
185
186
 
186
187
  Return:
187
188
  list of extracted mc names (:obj:`list`)
188
189
  """
189
190
  dest = dir if dir else self.workdir
191
+ if isinstance(feloss, (int, float)):
192
+ try:
193
+ feloss = {1: 'jordan', 11: 'bertotti'}[int(feloss)]
194
+ except KeyError:
195
+ feloss = ''
190
196
  return [self.magnetizingCurves.writefile(m[0], dest,
191
- fillfac=m[1], recsin=recsin)
197
+ fillfac=m[1],
198
+ recsin=recsin,
199
+ feloss=feloss)
192
200
  for m in model.set_magcurves(
193
201
  self.magnetizingCurves, self.magnets)]
194
202
 
@@ -212,9 +220,11 @@ class BaseFemag(object):
212
220
  self.model = femagtools.model.MachineModel(machine)
213
221
  self.modelname = self.model.name
214
222
  recsin = ''
223
+ feloss = ''
215
224
  if simulation:
216
225
  recsin = simulation.get('recsin', '')
217
- self.copy_magnetizing_curves(self.model, recsin=recsin)
226
+ feloss = simulation.get('calc_fe_loss', '')
227
+ self.copy_magnetizing_curves(self.model, recsin=recsin, feloss=feloss)
218
228
  try:
219
229
  if 'wdgdef' in self.model.winding:
220
230
  self.model.winding['wdgfile'] = self.create_wdg_def(
@@ -429,12 +439,15 @@ class BaseFemag(object):
429
439
  """
430
440
  Read the modelname from the Femag Log file
431
441
  """
432
- with open(os.path.join(self.workdir, 'FEMAG-FSL.log')) as f:
433
- for l in f:
434
- if l.startswith('New model') or l.startswith('Load model'):
435
- return l.split('"')[1]
442
+ try:
443
+ with open(os.path.join(self.workdir, 'FEMAG-FSL.log')) as f:
444
+ for l in f:
445
+ if l.startswith('New model') or l.startswith('Load model'):
446
+ return l.split('"')[1]
447
+ except FileNotFoundError:
448
+ pass
436
449
 
437
- raise ValueError(f"Model not found in {self.workdir}/'FEMAG-FSL.log'")
450
+ return list(pathlib.Path(self.workdir).glob('*.PROT'))[0].stem
438
451
 
439
452
  def readResult(self, simulation, bch=None):
440
453
  if simulation:
@@ -715,55 +728,6 @@ class FemagTask(threading.Thread):
715
728
  self.returncode = self.proc.wait()
716
729
 
717
730
 
718
- class SubscriberTask(threading.Thread):
719
- def __init__(self, port, host, notify):
720
- threading.Thread.__init__(self)
721
- context = zmq.Context.instance()
722
- self.subscriber = context.socket(zmq.SUB)
723
- if not host:
724
- host = 'localhost'
725
- self.subscriber.connect(f'tcp://{host}:{port}')
726
- self.subscriber.setsockopt(zmq.SUBSCRIBE, b'')
727
- self.controller = zmq.Context.instance().socket(zmq.PULL)
728
- self.controller_url = 'inproc://publisher'
729
- self.controller.bind(self.controller_url)
730
- self.poller = zmq.Poller()
731
- self.poller.register(self.subscriber, zmq.POLLIN)
732
- self.poller.register(self.controller, zmq.POLLIN)
733
- self.logger = logger
734
- self.notify = notify
735
-
736
- def stop(self):
737
- socket = zmq.Context.instance().socket(zmq.PUSH)
738
- socket.connect(self.controller_url)
739
- socket.send(b"quit")
740
- socket.close()
741
-
742
- def run(self):
743
- self.logger.info("subscriber is ready")
744
- while True:
745
- socks = dict(self.poller.poll())
746
- if socks.get(self.subscriber) == zmq.POLLIN:
747
- try:
748
- response = self.subscriber.recv_multipart()
749
- # Sometimes femag send messages with only len = 1. These messages must be ignored
750
- if len(response) < 2:
751
- continue
752
- self.notify([s.decode('latin1') for s in response])
753
-
754
- except Exception:
755
- self.logger.error(
756
- "error in subscription message processing", exc_info=True)
757
-
758
- if socks.get(self.controller) == zmq.POLLIN:
759
- req = self.controller.recv()
760
- self.logger.info("subscriber %s", req)
761
- break
762
- self.subscriber.close()
763
- self.controller.close()
764
- self.logger.debug("subscriber stopped")
765
-
766
-
767
731
  class ZmqFemag(BaseFemag):
768
732
  """Invoke and control execution of FEMAG with ZeroMQ
769
733
 
@@ -838,8 +802,8 @@ class ZmqFemag(BaseFemag):
838
802
  """attaches a notify function"""
839
803
  logger.info("Subscribe on '%s' port %d", self.femaghost, self.port+1)
840
804
  if self.subscriber is None:
841
- self.subscriber = SubscriberTask(
842
- self.port+1, self.femaghost, notify)
805
+ self.subscriber = femagtools.zmq.SubscriberTask(
806
+ port=self.port+1, host=self.femaghost, notify=notify)
843
807
  self.subscriber.start()
844
808
  else:
845
809
  # reattach?
@@ -1149,6 +1113,38 @@ class ZmqFemag(BaseFemag):
1149
1113
  logger.warning(response[0])
1150
1114
  return [s.decode('latin1') for s in response]
1151
1115
 
1116
+ def airgap_flux_density(self, pmod):
1117
+ # try to read bag.dat
1118
+ agr = self.getfile("bag.dat")
1119
+ status = json.loads(agr[0])['status']
1120
+ if status == 'ok':
1121
+ datfile = os.path.join(self.workdir, 'bag.dat')
1122
+ with open(datfile, 'wb') as bagfile:
1123
+ bagfile.write(agr[1])
1124
+ agi = ag.read(datfile, pmod)
1125
+ else:
1126
+ import numpy as np
1127
+ # try to read model file (TODO download with getfile)
1128
+ nc = self.read_nc()
1129
+ ag_elmnts = nc.airgap_center_elements
1130
+ logger.info("Airgap elements %d scale_factor %f",
1131
+ len(ag_elmnts), nc.scale_factor())
1132
+ if len(ag_elmnts) < 1:
1133
+ raise ValueError("Missing airgap elements")
1134
+ scf = 360/nc.scale_factor()/ag_elmnts[-1].center[0]
1135
+ pos = np.array([e.center[0]*scf for e in ag_elmnts])
1136
+ bxy = np.array([e.flux_density() for e in ag_elmnts]).T
1137
+ if np.max(bxy[0]) > np.max(bxy[1]):
1138
+ agi = ag.fft(pos, bxy[0], pmod)
1139
+ else:
1140
+ agi = ag.fft(pos, bxy[1], pmod)
1141
+ return dict(Bamp=agi['Bamp'],
1142
+ phi0=agi['phi0'],
1143
+ angle=agi['pos'],
1144
+ angle_fft=agi['pos'],
1145
+ B=agi['B'],
1146
+ B_fft=agi['B_fft'])
1147
+
1152
1148
  def interrupt(self):
1153
1149
  """send push message to control port to stop current calculation"""
1154
1150
  context = zmq.Context.instance()
@@ -1165,7 +1161,7 @@ class ZmqFemag(BaseFemag):
1165
1161
  wdg.write(name, self.workdir)
1166
1162
  self.upload(os.path.join(self.workdir, name+'.WID'))
1167
1163
 
1168
- def copy_magnetizing_curves(self, model, dir=None, recsin=''):
1164
+ def copy_magnetizing_curves(self, model, dir=None, recsin='', feloss=''):
1169
1165
  """extract mc names from model and write files into workdir or dir if given
1170
1166
  and upload to Femag
1171
1167
 
@@ -1175,9 +1171,16 @@ class ZmqFemag(BaseFemag):
1175
1171
  dest = dir if dir else self.workdir
1176
1172
  mc_names = [m for m in model.set_magcurves(
1177
1173
  self.magnetizingCurves, self.magnets)]
1174
+ if isinstance(feloss, (int, float)):
1175
+ try:
1176
+ feloss = {1: 'jordan', 11: 'bertotti'}[int(feloss)]
1177
+ except KeyError:
1178
+ feloss = ''
1178
1179
  for m in mc_names:
1179
1180
  f = self.magnetizingCurves.writefile(m[0], dest,
1180
- fillfac=m[1], recsin=recsin)
1181
+ fillfac=m[1],
1182
+ recsin=recsin,
1183
+ feloss=feloss)
1181
1184
  self.upload(os.path.join(dest, f))
1182
1185
  return mc_names
1183
1186
 
femagtools/fsl.py CHANGED
@@ -556,7 +556,7 @@ class Builder:
556
556
  if 'fsl_rotor' in conv:
557
557
  self.fsl_rotor = True
558
558
  th_props = ['']
559
- logger.info(model['magnet'])
559
+ logger.debug(model['magnet'])
560
560
  if hasattr(model, 'magnet'):
561
561
  if model['magnet'].get('thcond', 0):
562
562
  th_props = [f'rotor_density = {model["magnet"]["density"]}',
@@ -730,7 +730,7 @@ class Builder:
730
730
  custom_fefunc = ['']
731
731
  if pfefunc:
732
732
  sim['loss_funct'] = 1 # 3?
733
- if pfefunc == 'bertotti' or 'modified_steinmetz':
733
+ if pfefunc in ('bertotti', 'modified_steinmetz'):
734
734
  custom_fefunc = self.__render(sim['PVFE_FSL'], pfefunc)
735
735
  else:
736
736
  custom_fefunc = pfefunc.split('\n')
@@ -750,6 +750,9 @@ class Builder:
750
750
  revert_displ = self.create_displ_stator_rotor(
751
751
  sim['eccentricity'])
752
752
 
753
+ if sim.get('calculationMode') == 'pm_sym_f_cur':
754
+ if 'nload_ex_cur' in sim.keys(): # convert obsolete key
755
+ sim['noload_ex_cur'] = sim.pop('nload_ex_cur')
753
756
  felosses = custom_fefunc + self.create_fe_losses(sim)
754
757
  fslcalc = (displ_stator_rotor
755
758
  + self.__render(sim, sim.get('calculationMode'))
femagtools/isa7.py CHANGED
@@ -815,8 +815,9 @@ class Isa7(object):
815
815
  airgap_positions.append(axy[0][0])
816
816
  self.airgap_center_elements.append(se.elements)
817
817
 
818
- if len(self.airgap_center_elements) == 1:
819
- self.airgap_center_elements = self.airgap_center_elements[0]
818
+ if self.airgap_center_elements:
819
+ # TODO check airgap center
820
+ self.airgap_center_elements = self.airgap_center_elements[-1]
820
821
  if horiz:
821
822
  airgap_positions.append(np.min(nxy[:, 1]))
822
823
  else: