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.
- femagtools/__init__.py +1 -1
- femagtools/dxfsl/area.py +65 -0
- femagtools/dxfsl/conv.py +5 -0
- femagtools/dxfsl/converter.py +34 -1
- femagtools/dxfsl/functions.py +14 -6
- femagtools/dxfsl/geom.py +12 -12
- femagtools/dxfsl/journal.py +1 -1
- femagtools/dxfsl/symmetry.py +28 -8
- femagtools/femag.py +64 -61
- femagtools/fsl.py +5 -2
- femagtools/isa7.py +3 -2
- femagtools/machine/afpm.py +43 -23
- femagtools/machine/effloss.py +29 -18
- femagtools/machine/sizing.py +4 -3
- femagtools/machine/sm.py +34 -36
- femagtools/mcv.py +56 -26
- femagtools/multiproc.py +79 -80
- femagtools/parstudy.py +10 -4
- femagtools/semi_fea.py +108 -0
- femagtools/templates/basic_modpar.mako +0 -3
- femagtools/templates/fe-contr.mako +18 -18
- femagtools/templates/ld_lq_fast.mako +3 -0
- femagtools/templates/mult_cal_fast.mako +3 -0
- femagtools/templates/pm_sym_f_cur.mako +4 -1
- femagtools/templates/pm_sym_fast.mako +3 -0
- femagtools/templates/pm_sym_loss.mako +3 -0
- femagtools/templates/psd_psq_fast.mako +3 -0
- femagtools/templates/torq_calc.mako +3 -0
- femagtools/tks.py +23 -20
- femagtools/zmq.py +213 -0
- {femagtools-1.8.2.dist-info → femagtools-1.8.3.dist-info}/METADATA +3 -3
- {femagtools-1.8.2.dist-info → femagtools-1.8.3.dist-info}/RECORD +40 -38
- {femagtools-1.8.2.dist-info → femagtools-1.8.3.dist-info}/WHEEL +1 -1
- tests/test_afpm.py +15 -6
- tests/test_femag.py +1 -1
- tests/test_fsl.py +4 -4
- tests/test_mcv.py +20 -14
- {femagtools-1.8.2.dist-info → femagtools-1.8.3.dist-info}/LICENSE +0 -0
- {femagtools-1.8.2.dist-info → femagtools-1.8.3.dist-info}/entry_points.txt +0 -0
- {femagtools-1.8.2.dist-info → femagtools-1.8.3.dist-info}/top_level.txt +0 -0
femagtools/__init__.py
CHANGED
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,
|
femagtools/dxfsl/converter.py
CHANGED
@@ -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',
|
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)
|
femagtools/dxfsl/functions.py
CHANGED
@@ -85,16 +85,24 @@ def alpha_angle(startangle, endangle):
|
|
85
85
|
return angle - 2.0*np.pi
|
86
86
|
|
87
87
|
|
88
|
-
def
|
88
|
+
def less_angle(alpha1, alpha2):
|
89
89
|
angle = alpha_angle(alpha1, alpha2)
|
90
|
-
|
91
|
-
return alpha2
|
92
|
-
return alpha1
|
90
|
+
return (angle < np.pi or angle > 2.0*np.pi)
|
93
91
|
|
94
92
|
|
95
|
-
def
|
93
|
+
def greater_angle(alpha1, alpha2):
|
96
94
|
angle = alpha_angle(alpha1, alpha2)
|
97
|
-
|
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,
|
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,
|
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,
|
2419
|
+
if np.isclose(startangle, angle_p2, rtol=rtol, atol=atol):
|
2420
2420
|
return True
|
2421
|
-
return np.isclose(endangle, angle_p2,
|
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,
|
2425
|
+
if np.isclose(startangle, angle_p1, rtol=rtol, atol=atol):
|
2426
2426
|
return True
|
2427
|
-
return np.isclose(endangle, angle_p1,
|
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,
|
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,
|
2432
|
+
return np.isclose(startangle, angle_p2, rtol=rtol, atol=atol)
|
2433
2433
|
|
2434
|
-
if np.isclose(endangle, angle_p1,
|
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,
|
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)
|
femagtools/dxfsl/journal.py
CHANGED
femagtools/dxfsl/symmetry.py
CHANGED
@@ -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
|
-
|
129
|
-
|
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
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
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],
|
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
|
-
|
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
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
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
|
-
|
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],
|
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.
|
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
|
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
|
819
|
-
|
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:
|