femagtools 1.8.2__py3-none-any.whl → 1.8.3__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|