femagtools 1.6.5__py3-none-any.whl → 1.6.6__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 +24 -4
- femagtools/dxfsl/areabuilder.py +152 -5
- femagtools/dxfsl/concat.py +7 -7
- femagtools/dxfsl/converter.py +20 -7
- femagtools/dxfsl/dxfparser.py +359 -0
- femagtools/dxfsl/femparser.py +78 -0
- femagtools/dxfsl/geom.py +14 -426
- femagtools/dxfsl/journal.py +15 -23
- femagtools/dxfsl/shape.py +1 -1
- femagtools/dxfsl/svgparser.py +90 -0
- femagtools/dxfsl/symmetry.py +183 -74
- femagtools/isa7.py +6 -5
- femagtools/machine/effloss.py +1 -1
- femagtools/machine/im.py +2 -1
- femagtools/machine/pm.py +71 -12
- femagtools/machine/sm.py +4 -2
- femagtools/plot/nc.py +3 -3
- femagtools/svgfsl/converter.py +74 -0
- {femagtools-1.6.5.dist-info → femagtools-1.6.6.dist-info}/METADATA +5 -1
- {femagtools-1.6.5.dist-info → femagtools-1.6.6.dist-info}/RECORD +26 -21
- {femagtools-1.6.5.dist-info → femagtools-1.6.6.dist-info}/entry_points.txt +1 -0
- tests/test_dxfsl.py +12 -0
- {femagtools-1.6.5.dist-info → femagtools-1.6.6.dist-info}/LICENSE +0 -0
- {femagtools-1.6.5.dist-info → femagtools-1.6.6.dist-info}/WHEEL +0 -0
- {femagtools-1.6.5.dist-info → femagtools-1.6.6.dist-info}/top_level.txt +0 -0
femagtools/dxfsl/symmetry.py
CHANGED
@@ -12,9 +12,10 @@ import logging
|
|
12
12
|
import sys
|
13
13
|
from femagtools.dxfsl.shape import Element, Line
|
14
14
|
from femagtools.dxfsl.area import Area
|
15
|
-
from femagtools.dxfsl.functions import alpha_angle, positive_angle
|
15
|
+
from femagtools.dxfsl.functions import alpha_angle, positive_angle, is_same_angle
|
16
16
|
from femagtools.dxfsl.functions import min_angle, max_angle, gcd, point
|
17
|
-
from femagtools.dxfsl.functions import less_equal, less
|
17
|
+
from femagtools.dxfsl.functions import less_equal, less, points_are_close
|
18
|
+
from femagtools.dxfsl.functions import line_m, line_n, mirror_point
|
18
19
|
|
19
20
|
logger = logging.getLogger('femagtools.symmetry')
|
20
21
|
|
@@ -38,10 +39,18 @@ class Symmetry(object):
|
|
38
39
|
self.geom = geom
|
39
40
|
self.startangle = startangle
|
40
41
|
self.endangle = endangle
|
42
|
+
self.delta_check_count = 0
|
43
|
+
self.delta_angle_korr = 0.0
|
41
44
|
self.rtol = rtol
|
42
45
|
self.atol = atol
|
43
|
-
|
44
|
-
|
46
|
+
self.full = False
|
47
|
+
if np.isclose(self.startangle, self.endangle):
|
48
|
+
self.alpha = 2.0*np.pi
|
49
|
+
self.full = True
|
50
|
+
else:
|
51
|
+
self.alpha = alpha_angle(self.startangle, self.endangle)
|
52
|
+
self.full = False
|
53
|
+
logger.debug("Symmetry(alpha=%s, rtol=%s, atol=%s)", self.alpha, rtol, atol)
|
45
54
|
|
46
55
|
def __str__(self):
|
47
56
|
return "rtol: {}\n".format(self.rtol) + \
|
@@ -62,6 +71,10 @@ class Symmetry(object):
|
|
62
71
|
return False
|
63
72
|
return True
|
64
73
|
|
74
|
+
def calc_mid_angle(self, a):
|
75
|
+
return positive_angle(alpha_angle(self.startangle,
|
76
|
+
a.get_mid_angle(self.geom.center)))
|
77
|
+
|
65
78
|
def find_symmetry(self):
|
66
79
|
arealist = self.geom.list_of_areas()
|
67
80
|
logger.debug("begin of Symmetry::find_symmetry: %s areas available", len(arealist))
|
@@ -71,18 +84,17 @@ class Symmetry(object):
|
|
71
84
|
|
72
85
|
areas = []
|
73
86
|
for a in arealist:
|
74
|
-
areas.append((a.get_alpha(),
|
75
|
-
a.
|
76
|
-
a.
|
77
|
-
a
|
87
|
+
areas.append((round(a.get_alpha(self.geom.center), 3),
|
88
|
+
round(a.min_dist, 1),
|
89
|
+
round(a.height, 1),
|
90
|
+
self.calc_mid_angle(a),
|
78
91
|
a))
|
79
|
-
areas.sort()
|
80
|
-
parts_possible = None
|
92
|
+
areas.sort(reverse=True)
|
81
93
|
|
82
|
-
a0_alpha,
|
94
|
+
a0_alpha, a0_min_dist, a0_height, a0_mid_angle, a0 = areas[0]
|
83
95
|
equal_areas = [(a0_mid_angle, a0)]
|
84
|
-
|
85
|
-
for a1_alpha,
|
96
|
+
check_rslt = []
|
97
|
+
for a1_alpha, a1_min_dist, a1_height, a1_mid_angle, a1 in areas[1:]:
|
86
98
|
if self.equal_area(a0_min_dist, a0_height, a0_alpha,
|
87
99
|
a1_min_dist, a1_height, a1_alpha,
|
88
100
|
rtol=0.01, atol=0.05):
|
@@ -92,10 +104,8 @@ class Symmetry(object):
|
|
92
104
|
equal_areas.append((a1_mid_angle, a1))
|
93
105
|
else:
|
94
106
|
parts = self.check_delta(equal_areas)
|
95
|
-
|
96
|
-
|
97
|
-
else:
|
98
|
-
parts_possible = self.calc_parts(parts_possible, parts)
|
107
|
+
check_rslt.append((a0.area_size(), parts, len(equal_areas), a0))
|
108
|
+
|
99
109
|
equal_areas = [(a1_mid_angle, a1)]
|
100
110
|
a0_min_dist = a1_min_dist
|
101
111
|
a0_height = a1_height
|
@@ -103,105 +113,119 @@ class Symmetry(object):
|
|
103
113
|
a0 = a1
|
104
114
|
|
105
115
|
parts = self.check_delta(equal_areas)
|
106
|
-
|
107
|
-
parts_possible = parts
|
108
|
-
else:
|
109
|
-
parts_possible = self.calc_parts(parts_possible, parts)
|
116
|
+
check_rslt.append((a0.area_size(), parts, len(equal_areas), a0))
|
110
117
|
|
111
|
-
|
112
|
-
|
113
|
-
if parts_possible < 2:
|
118
|
+
parts = self.get_symmetry_parts(check_rslt)
|
119
|
+
if parts < 2:
|
114
120
|
logger.debug("end of Symmetry::find_symmetry: no symmetry")
|
115
|
-
return
|
116
|
-
|
121
|
+
return parts
|
122
|
+
|
117
123
|
self.geom.clear_cut_lines()
|
118
|
-
for alpha in self.symmetry_lines(
|
124
|
+
for alpha in self.symmetry_lines(parts,
|
119
125
|
self.startangle,
|
120
126
|
self.endangle):
|
121
|
-
|
127
|
+
plus = self.geom.max_radius / 10
|
128
|
+
min_radius = max(10, self.geom.min_radius - plus)
|
122
129
|
p1 = point(self.geom.center, min_radius, alpha)
|
123
|
-
p2 = point(self.geom.center, self.geom.max_radius+
|
130
|
+
p2 = point(self.geom.center, self.geom.max_radius + plus, alpha)
|
124
131
|
line = Line(Element(start=p1, end=p2))
|
125
132
|
line.init_attributes(color='green')
|
126
133
|
self.geom.add_cut_line(line)
|
127
|
-
|
128
|
-
logger.debug("end of Symmetry::find_symmetry")
|
129
|
-
return
|
134
|
+
|
135
|
+
logger.debug("end of Symmetry::find_symmetry: -> %s", parts)
|
136
|
+
return parts
|
130
137
|
|
131
138
|
def check_delta(self, area_list):
|
132
|
-
logger.debug("
|
139
|
+
logger.debug("begin of check_delta: %s equal areas", len(area_list))
|
133
140
|
if not area_list:
|
141
|
+
logger.debug("end of check_delta: no areas")
|
134
142
|
return None
|
135
143
|
|
136
144
|
if len(area_list) == 1:
|
137
145
|
mid_angle, a = area_list[0]
|
138
|
-
|
139
|
-
|
146
|
+
alpha = a.get_alpha(self.geom.center)
|
147
|
+
if np.isclose(alpha, self.alpha):
|
148
|
+
logger.debug("end of check_delta: one area from start to end")
|
140
149
|
return None # ok
|
141
150
|
|
151
|
+
self.delta_check_count += 1
|
142
152
|
area_list.sort()
|
143
|
-
start = self.startangle
|
144
|
-
mid_angle, a = area_list[0]
|
145
153
|
|
146
|
-
|
154
|
+
mid_angle, a = area_list[0]
|
155
|
+
delta = positive_angle(mid_angle * 2)
|
156
|
+
delta_total = mid_angle
|
147
157
|
delta_list = [delta]
|
148
|
-
|
158
|
+
|
159
|
+
logger.debug("First mid = %s, delta = %s", mid_angle, delta)
|
149
160
|
logger.debug("%s: d=%s, h=%s, a=%s, mid=%s, delta=%s",
|
150
161
|
a.identifier(),
|
151
162
|
a.min_dist,
|
152
163
|
a.height,
|
153
|
-
a.get_alpha(),
|
154
|
-
|
164
|
+
a.get_alpha(self.geom.center),
|
165
|
+
mid_angle,
|
155
166
|
delta)
|
156
|
-
logger.debug(" min=%s, max%s",
|
157
|
-
a.min_angle,
|
158
|
-
a.max_angle)
|
159
167
|
|
160
|
-
|
168
|
+
geom_alpha = alpha_angle(self.startangle, self.endangle)
|
169
|
+
geom_alpha = positive_angle(geom_alpha)
|
170
|
+
|
171
|
+
start_angle = mid_angle
|
161
172
|
for mid_angle, a in area_list[1:]:
|
162
|
-
|
173
|
+
delta_angle = alpha_angle(start_angle, mid_angle)
|
174
|
+
delta = positive_angle(delta_angle)
|
175
|
+
delta_total += delta_angle
|
163
176
|
delta_list.append(delta)
|
164
177
|
|
165
178
|
logger.debug("%s: d=%s, h=%s, a=%s, mid=%s, delta=%s",
|
166
179
|
a.identifier(),
|
167
180
|
a.min_dist,
|
168
181
|
a.height,
|
169
|
-
a.get_alpha(),
|
170
|
-
|
182
|
+
a.get_alpha(self.geom.center),
|
183
|
+
mid_angle,
|
171
184
|
delta)
|
172
|
-
|
173
|
-
a.min_angle,
|
174
|
-
a.max_angle)
|
175
|
-
|
176
|
-
start = mid_angle
|
185
|
+
start_angle = mid_angle
|
177
186
|
|
178
|
-
|
187
|
+
delta_angle = alpha_angle(start_angle, geom_alpha)
|
188
|
+
delta = positive_angle(delta_angle * 2)
|
189
|
+
delta_total += delta_angle
|
179
190
|
delta_list.append(delta)
|
180
191
|
logger.debug("final delta=%s", delta)
|
181
192
|
|
193
|
+
if not np.isclose(geom_alpha, delta_total):
|
194
|
+
logger.debug("-- deltas: %s", delta_list)
|
195
|
+
logger.debug("end of check_delta: BAD DELTA %s, (expected %s)",
|
196
|
+
delta_angle, geom_alpha)
|
197
|
+
return 0 # very bad
|
198
|
+
|
182
199
|
sz = len(delta_list)
|
183
200
|
mid = int(sz / 2)
|
184
201
|
ix1 = 0
|
185
202
|
ix2 = sz - 1
|
203
|
+
first_last_bad = False
|
186
204
|
for ix1 in range(0, mid):
|
187
205
|
if not np.isclose(delta_list[ix1], delta_list[ix2], rtol=1e-3, atol=1e-3):
|
188
|
-
|
189
|
-
|
206
|
+
if self.full and \
|
207
|
+
self.delta_check_count == 1 and \
|
208
|
+
ix1 == 0 and \
|
209
|
+
self.delta_angle_korr == 0.0:
|
210
|
+
first_last_bad = True
|
211
|
+
else:
|
212
|
+
logger.debug("end of check_delta: NO SYM")
|
213
|
+
return 0
|
190
214
|
ix2 -= 1
|
191
215
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
logger.debug("
|
204
|
-
|
216
|
+
if first_last_bad:
|
217
|
+
delta_korr = (delta_list[0] + delta_list[-1]) / 2.0
|
218
|
+
logger.debug("STARTANGLE CORRECTION")
|
219
|
+
self.delta_angle_korr = (delta_korr - delta_list[0]) / 2
|
220
|
+
logger.debug("-- delta[0] from %s to %s", delta_list[0], delta_korr)
|
221
|
+
logger.debug("Delta Angle Korr = %s", self.delta_angle_korr)
|
222
|
+
delta_list[0] = delta_korr
|
223
|
+
delta_list[-1] = delta_korr
|
224
|
+
assert(self.full)
|
225
|
+
self.startangle = self.startangle - self.delta_angle_korr
|
226
|
+
self.endangle = self.endangle - self.delta_angle_korr
|
227
|
+
logger.debug("New startangle = %s", self.startangle)
|
228
|
+
logger.debug("Delta List: %s", delta_list)
|
205
229
|
|
206
230
|
d1 = delta_list[0]
|
207
231
|
d1_count = 1
|
@@ -212,9 +236,10 @@ class Symmetry(object):
|
|
212
236
|
d1_count += 1
|
213
237
|
|
214
238
|
if d1_count == len(delta_list):
|
215
|
-
logger.debug("SYMMETRY FOUND")
|
239
|
+
logger.debug("end of check_delta: SYMMETRY FOUND")
|
216
240
|
return d1_count -1 # very simple
|
217
241
|
if len(delta_list) < 2:
|
242
|
+
logger.debug("end of check_delta: One delta only ?!")
|
218
243
|
return 0
|
219
244
|
|
220
245
|
logger.debug("index of delta %s = %s", d1, inx_list)
|
@@ -227,15 +252,51 @@ class Symmetry(object):
|
|
227
252
|
return 0
|
228
253
|
x1 = x2
|
229
254
|
|
230
|
-
logger.debug("SYMMETRY FOUND")
|
255
|
+
logger.debug("end of check_delta: SYMMETRY FOUND")
|
231
256
|
return len(inx_list) -1
|
232
257
|
|
258
|
+
def get_symmetry_parts(self, check_rslt):
|
259
|
+
max_size = 0
|
260
|
+
max_areas = 0
|
261
|
+
parts_possible = None
|
262
|
+
|
263
|
+
check_rslt.sort(reverse=True)
|
264
|
+
for size, parts, count, area in check_rslt:
|
265
|
+
logger.debug("Result: %s, %s, %s", size, parts, count)
|
266
|
+
|
267
|
+
for size, parts, count, area in check_rslt:
|
268
|
+
if parts is not None and parts > 0:
|
269
|
+
max_size = max(max_size, size)
|
270
|
+
max_areas = max(max_areas, count)
|
271
|
+
|
272
|
+
logger.debug("max size: %s, max areas: %s", max_size, max_areas)
|
273
|
+
|
274
|
+
for size, parts, count, area in check_rslt:
|
275
|
+
if parts is not None and parts <= 1: # critical
|
276
|
+
if count <= max(1, max_areas / 5):
|
277
|
+
if size < max_size / 25:
|
278
|
+
parts = None
|
279
|
+
|
280
|
+
parts_possible = self.calc_parts(parts_possible, parts)
|
281
|
+
|
282
|
+
if parts_possible is None:
|
283
|
+
parts_possible = 0
|
284
|
+
return parts_possible
|
285
|
+
|
233
286
|
def calc_parts(self, parts1, parts2):
|
234
287
|
logger.debug("Calc symmetry Parts (%s, %s)", parts1, parts2)
|
235
288
|
if parts2 is None:
|
289
|
+
logger.debug("return %s parts", parts1)
|
236
290
|
return parts1
|
237
|
-
|
238
|
-
|
291
|
+
if parts1 is None:
|
292
|
+
logger.debug("return %s parts", parts2)
|
293
|
+
return parts2
|
294
|
+
if parts1 == 0 or parts2 == 0:
|
295
|
+
logger.debug("return %s parts", 0)
|
296
|
+
return 0
|
297
|
+
parts = gcd(parts1, parts2)
|
298
|
+
logger.debug("return %s parts", parts)
|
299
|
+
return parts
|
239
300
|
|
240
301
|
def symmetry_lines(self, parts, startangle, endangle):
|
241
302
|
logger.debug("begin symmetry_lines from %s to %s",
|
@@ -249,7 +310,10 @@ class Symmetry(object):
|
|
249
310
|
while less(start, endangle):
|
250
311
|
yield start
|
251
312
|
start += delta
|
252
|
-
|
313
|
+
|
314
|
+
if is_same_angle(startangle, endangle):
|
315
|
+
yield start
|
316
|
+
|
253
317
|
# Damit man anschliessend ohne Umstände schneiden kann.
|
254
318
|
self.geom.sym_startangle = startangle
|
255
319
|
self.geom.sym_endangle = startangle + delta
|
@@ -259,3 +323,48 @@ class Symmetry(object):
|
|
259
323
|
self.geom.sym_area.sym_startangle = self.geom.sym_startangle
|
260
324
|
self.geom.sym_area.sym_endangle = self.geom.sym_endangle
|
261
325
|
logger.debug("end symmetry_lines")
|
326
|
+
|
327
|
+
def check_symmetry_of_mirror(self, mirror_geom, mirrorangle):
|
328
|
+
logger.debug("begin of Symmetry::check_symmetry_of_mirror")
|
329
|
+
assert(mirror_geom is not None)
|
330
|
+
|
331
|
+
axis_p = point(self.geom.center, self.geom.max_radius, mirrorangle)
|
332
|
+
axis_m = line_m(self.geom.center, axis_p)
|
333
|
+
axis_n = line_n(self.geom.center, axis_m)
|
334
|
+
|
335
|
+
def counterpart_found(node, nodes, rtol, atol):
|
336
|
+
hits = 0
|
337
|
+
for n in nodes:
|
338
|
+
if points_are_close(node, n, rtol, atol):
|
339
|
+
logger.debug(" ---- %s is %s", node, n)
|
340
|
+
return True
|
341
|
+
return False
|
342
|
+
|
343
|
+
def check_differences(geom, mirror_geom):
|
344
|
+
geom_ag_nodes = []
|
345
|
+
geom_nodes = [n for n in geom.g.nodes() if not (n in geom_ag_nodes)]
|
346
|
+
|
347
|
+
hit = 0
|
348
|
+
for n in geom_nodes:
|
349
|
+
mirror_n = mirror_point(n, geom.center, axis_m, axis_n)
|
350
|
+
if counterpart_found(mirror_n,
|
351
|
+
mirror_geom.g.nodes(),
|
352
|
+
self.rtol,
|
353
|
+
self.atol):
|
354
|
+
hit += 1
|
355
|
+
min_nodes = min(len(geom_nodes), int(len(geom_nodes) * 0.95) + 1)
|
356
|
+
logger.debug("Nodes=%s, Counterparts=%s", len(geom_nodes), hit)
|
357
|
+
if hit < min_nodes:
|
358
|
+
return hit / len(geom_nodes)
|
359
|
+
else:
|
360
|
+
return 1.0
|
361
|
+
|
362
|
+
# ----------------
|
363
|
+
logger.debug("check geom - mirror")
|
364
|
+
f1 = check_differences(self.geom, mirror_geom)
|
365
|
+
logger.debug("check mirror - geom")
|
366
|
+
f2 = check_differences(mirror_geom, self.geom)
|
367
|
+
logger.debug("Factor 1: %s, 2: %s", f1, f2)
|
368
|
+
ok = f1 > 0.97 and f2 > 0.97
|
369
|
+
logger.debug("end of Symmetry::check_symmetry_of_mirror => %s", ok)
|
370
|
+
return ok
|
femagtools/isa7.py
CHANGED
@@ -868,14 +868,14 @@ class Isa7(object):
|
|
868
868
|
except AttributeError:
|
869
869
|
pass
|
870
870
|
|
871
|
-
try:
|
871
|
+
try:
|
872
872
|
flx_fac = 1000
|
873
|
-
if isinstance(reader.el_fe_induction_1, list):
|
873
|
+
if isinstance(reader.el_fe_induction_1, list):
|
874
874
|
pass
|
875
|
-
else:
|
876
|
-
if reader.el_fe_induction_1.dtype == 'int16':
|
875
|
+
else:
|
876
|
+
if reader.el_fe_induction_1.dtype == 'int16':
|
877
877
|
flx_fac = 1000
|
878
|
-
else:
|
878
|
+
else:
|
879
879
|
flx_fac = 1
|
880
880
|
el_fe_ind = [np.array(reader.el_fe_induction_1).T/flx_fac,
|
881
881
|
np.array(reader.el_fe_induction_2).T/flx_fac]
|
@@ -1432,6 +1432,7 @@ class Element(BaseEntity):
|
|
1432
1432
|
"""return temperature of this element"""
|
1433
1433
|
return sum([v.vpot[1] for v in self.vertices])/len(self.vertices)
|
1434
1434
|
|
1435
|
+
|
1435
1436
|
class SuperElement(BaseEntity):
|
1436
1437
|
def __init__(self, key, sr_key, elements, nodechains, color,
|
1437
1438
|
nc_keys, mcvtype, condtype, conduc, length,
|
femagtools/machine/effloss.py
CHANGED
@@ -238,7 +238,7 @@ def efficiency_losses_map(eecpars, u1, T, temp, n, npoints=(60, 40),
|
|
238
238
|
with_pmconst=with_pmconst, with_tmech=with_tmech) # braking mode
|
239
239
|
|
240
240
|
if kwargs.get('mesh_func', 0):
|
241
|
-
ntmesh = kwargs['mesh_func'](r['n'], r['T'],
|
241
|
+
ntmesh = kwargs['mesh_func'](r['n_type'], r['n'], r['T'],
|
242
242
|
rb['n'], rb['T'], npoints)
|
243
243
|
else:
|
244
244
|
ntmesh = _generate_mesh(r['n'], r['T'],
|
femagtools/machine/im.py
CHANGED
@@ -622,6 +622,7 @@ def parident(workdir, engine, f1, u1, wdgcon,
|
|
622
622
|
i_max_fact: (float) factor for maximum current to calculate no_load flux (default=2.5)
|
623
623
|
templatedirs: (list of str) names of directories to search for templates
|
624
624
|
"""
|
625
|
+
cmd = kwargs.get("cmd", None)
|
625
626
|
CON = {'open': 0, 'wye': 1, 'star': 1, 'delta': 2}
|
626
627
|
p = machine['poles']//2
|
627
628
|
slip = 1e-2
|
@@ -676,7 +677,7 @@ def parident(workdir, engine, f1, u1, wdgcon,
|
|
676
677
|
|
677
678
|
parstudy = femagtools.parstudy.ParameterStudy(
|
678
679
|
workdir, condMat=condMat,
|
679
|
-
magnetizingCurves=magnetizingCurves)
|
680
|
+
magnetizingCurves=magnetizingCurves, cmd=cmd)
|
680
681
|
|
681
682
|
builder = femagtools.fsl.Builder(kwargs.get('templatedirs', []))
|
682
683
|
model = femagtools.model.MachineModel(m)
|
femagtools/machine/pm.py
CHANGED
@@ -69,6 +69,7 @@ class PmRelMachine(object):
|
|
69
69
|
self.rotor_mass = 0
|
70
70
|
self.kth1 = KTH
|
71
71
|
self.bertotti = False
|
72
|
+
self.max_torque = 0.0
|
72
73
|
self.losskeys = ['styoke_hyst', 'stteeth_hyst',
|
73
74
|
'styoke_eddy', 'stteeth_eddy',
|
74
75
|
'rotor_hyst', 'rotor_eddy',
|
@@ -165,6 +166,46 @@ class PmRelMachine(object):
|
|
165
166
|
return iq, 0, self.torque_iqd(iq, 0)
|
166
167
|
|
167
168
|
def iqd_tmech(self, torque, n, iqd0=0, with_mtpa=True):
|
169
|
+
"""return minimum d-q-current for shaft torque"""
|
170
|
+
if np.abs(torque) < 1e-2:
|
171
|
+
return (0, 0)
|
172
|
+
if np.isscalar(iqd0):
|
173
|
+
tx = self.tmech_iqd(self.io[0], 0, n)
|
174
|
+
iq0 = min(0.9*self.i1range[1]/np.sqrt(2),
|
175
|
+
np.abs(torque)/tx*self.io[0])
|
176
|
+
if torque < 0:
|
177
|
+
i0 = (-iq0, 0)
|
178
|
+
else:
|
179
|
+
i0 = (iq0, 0)
|
180
|
+
logger.debug("initial guess i0 %f -> %s tx %f torque %f",
|
181
|
+
self.io[0], i0, tx, torque)
|
182
|
+
else:
|
183
|
+
i0 = iqd0
|
184
|
+
|
185
|
+
if with_mtpa:
|
186
|
+
k=0
|
187
|
+
while k < 6:
|
188
|
+
res = so.minimize(
|
189
|
+
lambda iqd: la.norm(iqd), i0, method='SLSQP',
|
190
|
+
constraints=({'type': 'eq',
|
191
|
+
'fun': lambda iqd:
|
192
|
+
self.tmech_iqd(*iqd, n) - torque}))
|
193
|
+
if res.success:
|
194
|
+
return res.x
|
195
|
+
# make new initial guess:
|
196
|
+
tx = self.tmech_iqd(*i0, n)
|
197
|
+
logger.debug("k %d new guess i0 %s tx %f torque %f",
|
198
|
+
k, i0, tx, torque)
|
199
|
+
i0=(min(0.9*self.i1range[1]/np.sqrt(2), torque/tx*i0[0]), 0)
|
200
|
+
k += 1
|
201
|
+
raise ValueError(
|
202
|
+
f'Torque {torque} speed {n} {i0} {res.message}')
|
203
|
+
def tqiq(iq):
|
204
|
+
return torque - self.tmech_iqd(float(iq), 0, n)
|
205
|
+
iq = so.fsolve(tqiq, (i0[0],))[0]
|
206
|
+
return iq, 0, self.tmech_iqd(iq, 0, n)
|
207
|
+
|
208
|
+
def iqd_tmech0(self, torque, n, iqd0=0, with_mtpa=True):
|
168
209
|
"""return minimum d-q-current for shaft torque"""
|
169
210
|
if np.abs(torque) < 1e-2:
|
170
211
|
return (0, 0)
|
@@ -681,17 +722,24 @@ class PmRelMachine(object):
|
|
681
722
|
iq, id, T = self.mtpa(i1max)
|
682
723
|
w1type = self.w1_umax(u1max, iq, id)
|
683
724
|
Pmax = w1type/self.p*T
|
684
|
-
w1max = 2*np.pi*speedmax*self.p
|
685
725
|
# check max speed:
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
726
|
+
sp = speedmax
|
727
|
+
while sp > w1type/2/np.pi/self.p:
|
728
|
+
w1max = 2*np.pi*sp*self.p
|
729
|
+
try:
|
730
|
+
if with_pmconst:
|
731
|
+
iq, id, tq = self.iqd_pmech_imax_umax(
|
732
|
+
sp, Pmax, i1max, u1max,
|
733
|
+
with_mtpa, with_tmech)
|
734
|
+
else:
|
735
|
+
iq, id, tq = self.iqd_imax_umax(
|
736
|
+
i1max, w1max, u1max,
|
737
|
+
T, with_mtpv=False,
|
738
|
+
with_tmech=with_tmech)
|
739
|
+
break
|
740
|
+
except ValueError:
|
741
|
+
sp -= 5e-2*speedmax
|
742
|
+
speedmax = sp
|
695
743
|
i1 = betai1(iq, id)[1]
|
696
744
|
if (abs(i1max) >= i1
|
697
745
|
and round(u1max, 1) >= round(np.linalg.norm(
|
@@ -843,7 +891,7 @@ class PmRelMachine(object):
|
|
843
891
|
def characteristics(self, T, n, u1max, nsamples=60,
|
844
892
|
with_mtpv=True, with_mtpa=True,
|
845
893
|
with_pmconst=True, with_tmech=True,
|
846
|
-
with_torque_corr=False):
|
894
|
+
with_torque_corr=False, **kwargs):
|
847
895
|
"""calculate torque speed characteristics.
|
848
896
|
return dict with list values of
|
849
897
|
id, iq, n, T, ud, uq, u1, i1,
|
@@ -863,6 +911,9 @@ class PmRelMachine(object):
|
|
863
911
|
r = dict(id=[], iq=[], uq=[], ud=[], u1=[], i1=[], T=[],
|
864
912
|
beta=[], gamma=[], phi=[], cosphi=[], pmech=[], n=[])
|
865
913
|
|
914
|
+
if kwargs.get('i1max', 0):
|
915
|
+
w1type, T = self.w1_imax_umax(kwargs['i1max'], u1max)
|
916
|
+
|
866
917
|
if np.isscalar(T):
|
867
918
|
tmax = self.torquemax(self.i1range[1])
|
868
919
|
tmin = 0
|
@@ -923,6 +974,9 @@ class PmRelMachine(object):
|
|
923
974
|
if speedrange[-1] < speedrange[-2]:
|
924
975
|
speedrange = speedrange[:-1]
|
925
976
|
logger.info("Speedrange T=%g Nm %s", Tf, speedrange)
|
977
|
+
if speedrange[-1] < nmax:
|
978
|
+
logger.warning("adjusted nmax %f -> %f", nmax, speedrange[-1])
|
979
|
+
|
926
980
|
n3 = speedrange[-1]
|
927
981
|
nstab = [int(nsamples*(x1-x2)/n3)
|
928
982
|
for x1, x2 in zip(speedrange[1:],
|
@@ -1135,7 +1189,12 @@ class PmRelMachineLdq(PmRelMachine):
|
|
1135
1189
|
if np.any(beta[beta > np.pi]):
|
1136
1190
|
beta[beta > np.pi] = beta - 2*np.pi
|
1137
1191
|
|
1138
|
-
self.
|
1192
|
+
self.betarange = min(beta), max(beta)
|
1193
|
+
if min(beta) < -np.pi/2 and max(beta) > -np.pi/2:
|
1194
|
+
self.io = iqd(-np.pi/4, np.max(i1)/2)
|
1195
|
+
else:
|
1196
|
+
self.io = iqd((np.min(beta)+max(beta))/2, np.max(i1)/2)
|
1197
|
+
|
1139
1198
|
kx = ky = 3
|
1140
1199
|
if len(i1) < 4:
|
1141
1200
|
ky = len(i1)-1
|
femagtools/machine/sm.py
CHANGED
@@ -46,6 +46,7 @@ def parident(workdir, engine, machine,
|
|
46
46
|
speed: rotor speed in 1/s (default 160/p)
|
47
47
|
i1_max: maximum current in A rms (default approx 3*i1nom)
|
48
48
|
"""
|
49
|
+
cmd = kwargs.get('cmd', None)
|
49
50
|
da1 = machine['outer_diam']
|
50
51
|
Q1 = machine['stator']['num_slots']
|
51
52
|
if 'statorRotor3' in machine['stator']:
|
@@ -59,7 +60,7 @@ def parident(workdir, engine, machine,
|
|
59
60
|
N = machine[wdgk]['num_wires']
|
60
61
|
i1_max = round(0.28*np.pi*hs*(da1+hs)/Q1/N*Jmax*1e5)*10 * \
|
61
62
|
machine[wdgk].get('num_par_wdgs', 1)
|
62
|
-
|
63
|
+
|
63
64
|
ifnom = machine['rotor']['ifnom']
|
64
65
|
exc_logspace = True
|
65
66
|
if exc_logspace:
|
@@ -78,7 +79,7 @@ def parident(workdir, engine, machine,
|
|
78
79
|
|
79
80
|
parvar = parstudy.List(
|
80
81
|
workdir, condMat=condMat,
|
81
|
-
magnetizingCurves=magnetizingCurves)
|
82
|
+
magnetizingCurves=magnetizingCurves, cmd=cmd)
|
82
83
|
|
83
84
|
simulation = dict(
|
84
85
|
calculationMode=kwargs.get('calculationMode',
|
@@ -240,6 +241,7 @@ class SynchronousMachine(object):
|
|
240
241
|
self.kth1 = KTH
|
241
242
|
self.kth2 = KTH
|
242
243
|
self.skin_resistance = [None, None]
|
244
|
+
self.kpmag = 1
|
243
245
|
# here you can set user defined functions for calculating the skin-resistance,
|
244
246
|
# according to the current frequency w. First function in list is for stator, second for rotor.
|
245
247
|
# If None, the femagtools intern default implementation is used.
|
femagtools/plot/nc.py
CHANGED
@@ -149,8 +149,7 @@ def __elements_of_subreg(isa, subreg):
|
|
149
149
|
else:
|
150
150
|
sr = [subreg]
|
151
151
|
for s in sr:
|
152
|
-
|
153
|
-
yield e
|
152
|
+
yield from isa.get_subregion(s).elements()
|
154
153
|
else:
|
155
154
|
for e in isa.elements:
|
156
155
|
yield e
|
@@ -232,7 +231,8 @@ def flux_density_pos(isa, ipos, subreg=[], icur=-1, ibeta=-1, cmap=DEFAULT_CMAP,
|
|
232
231
|
ibeta: beta angle index or last index if -1
|
233
232
|
|
234
233
|
"""
|
235
|
-
elements = [e for e in __elements_of_subreg(isa, subreg)
|
234
|
+
elements = [e for e in __elements_of_subreg(isa, subreg)
|
235
|
+
if e not in isa.airgap_center_elements]
|
236
236
|
b = []
|
237
237
|
for e in elements:
|
238
238
|
fd = isa.flux_density(e, icur, ibeta)
|