femagtools 1.7.2__py3-none-any.whl → 1.7.4__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/airgap.py +11 -0
- femagtools/bch.py +29 -20
- femagtools/dxfsl/converter.py +2 -1
- femagtools/dxfsl/fslrenderer.py +4 -2
- femagtools/fsl.py +18 -24
- femagtools/heat_source_network.py +403 -0
- femagtools/machine/pm.py +78 -4
- femagtools/machine/utils.py +109 -51
- femagtools/model.py +12 -1
- femagtools/parstudy.py +4 -5
- femagtools/plot/char.py +5 -2
- femagtools/templates/basic_modpar.mako +2 -0
- femagtools/templates/{therm-static.mako → therm_static.mako} +15 -13
- femagtools/windings.py +5 -2
- {femagtools-1.7.2.dist-info → femagtools-1.7.4.dist-info}/METADATA +2 -2
- {femagtools-1.7.2.dist-info → femagtools-1.7.4.dist-info}/RECORD +23 -21
- {femagtools-1.7.2.dist-info → femagtools-1.7.4.dist-info}/WHEEL +1 -1
- tests/test_heat_source_network.py +21 -0
- tests/test_machine.py +4 -4
- {femagtools-1.7.2.dist-info → femagtools-1.7.4.dist-info}/LICENSE +0 -0
- {femagtools-1.7.2.dist-info → femagtools-1.7.4.dist-info}/entry_points.txt +0 -0
- {femagtools-1.7.2.dist-info → femagtools-1.7.4.dist-info}/top_level.txt +0 -0
femagtools/__init__.py
CHANGED
femagtools/airgap.py
CHANGED
@@ -52,3 +52,14 @@ def read(filename, pmod=0):
|
|
52
52
|
return(dict())
|
53
53
|
|
54
54
|
return fft(bag[0], bag[1], pmod)
|
55
|
+
|
56
|
+
if __name__ == '__main__':
|
57
|
+
import sys
|
58
|
+
import matplotlib.pyplot as plt
|
59
|
+
import femagtools.plot.fluxdens
|
60
|
+
ag = read(sys.argv[1])
|
61
|
+
fig, axs = plt.subplots(nrows=2)
|
62
|
+
femagtools.plot.fluxdens.airgap(ag, ax=axs[0])
|
63
|
+
femagtools.plot.fluxdens.airgap_fft(ag, ax=axs[1])
|
64
|
+
fig.tight_layout()
|
65
|
+
plt.show()
|
femagtools/bch.py
CHANGED
@@ -70,7 +70,7 @@ def sttylosses(losses):
|
|
70
70
|
except KeyError:
|
71
71
|
pass
|
72
72
|
return d
|
73
|
-
|
73
|
+
|
74
74
|
if sregs == {'StYoke', 'StTeeth'}:
|
75
75
|
return hysteddy('StYoke', 'StTeeth', losses)
|
76
76
|
if sregs == {'StJo', 'StZa'}:
|
@@ -95,11 +95,12 @@ def sttylosses(losses):
|
|
95
95
|
return l
|
96
96
|
return {}
|
97
97
|
|
98
|
-
def losses_mapping_external_rotor(losses):
|
98
|
+
def losses_mapping_external_rotor(losses):
|
99
99
|
styoke = 'rotor'
|
100
|
-
rotor = 'Iron'
|
100
|
+
rotor = 'Iron'
|
101
101
|
d = {}
|
102
102
|
try:
|
103
|
+
d['styoke'] = losses[styoke]
|
103
104
|
d['styoke_hyst'] = losses[styoke+'_hyst']
|
104
105
|
d['rotor_hyst'] = losses[rotor+'_hyst']
|
105
106
|
d['styoke_eddy'] = losses[styoke+'_eddy']
|
@@ -1045,7 +1046,7 @@ class Reader:
|
|
1045
1046
|
'''
|
1046
1047
|
diff = np.floor(np.abs(np.diff(idList)))
|
1047
1048
|
if idList[-1] == 0 and len(idList) > 2 and \
|
1048
|
-
|
1049
|
+
diff[-1] == 0:
|
1049
1050
|
idList = idList[:-1]
|
1050
1051
|
return idList
|
1051
1052
|
|
@@ -1061,7 +1062,8 @@ class Reader:
|
|
1061
1062
|
m.append([floatnan(x) for x in rec])
|
1062
1063
|
|
1063
1064
|
m = np.array(m).T
|
1064
|
-
|
1065
|
+
d = np.diff(m[1])
|
1066
|
+
ncols = (len(d)+1)//(len(d[d < 0])+1)
|
1065
1067
|
if ncols == 1 and len(m[1]) > 1 and m[1][0] != m[1][1]: # simple correction
|
1066
1068
|
ncols = 2
|
1067
1069
|
iq = np.reshape(m[1], (-1, ncols))[0]
|
@@ -1070,7 +1072,8 @@ class Reader:
|
|
1070
1072
|
ncols = ncols-1
|
1071
1073
|
|
1072
1074
|
id = np.reshape(m[0], (-1, ncols)).T[0]
|
1073
|
-
id
|
1075
|
+
if id[0] >= 0:
|
1076
|
+
id = self.__removeTrailingZero(id)
|
1074
1077
|
nrows = len(id)
|
1075
1078
|
if nrows > 1 and id[nrows-1] < id[nrows-2]:
|
1076
1079
|
nrows = nrows-1
|
@@ -1095,7 +1098,8 @@ class Reader:
|
|
1095
1098
|
m.append([floatnan(x) for x in rec])
|
1096
1099
|
|
1097
1100
|
m = np.array(m).T
|
1098
|
-
|
1101
|
+
d = np.diff(m[1])
|
1102
|
+
ncols = (len(d)+1)//(len(d[d < 0])+1)
|
1099
1103
|
if ncols == 1 and len(m[1]) > 1 and m[1][0] != m[1][1]: # simple correction
|
1100
1104
|
ncols = 2
|
1101
1105
|
iq = np.linspace(np.min(m[1]), np.max(m[1]), ncols)
|
@@ -1104,7 +1108,8 @@ class Reader:
|
|
1104
1108
|
ncols = ncols-1
|
1105
1109
|
|
1106
1110
|
id = np.reshape(m[0], (-1, ncols)).T[0]
|
1107
|
-
id
|
1111
|
+
if id[0] >= 0:
|
1112
|
+
id = self.__removeTrailingZero(id)
|
1108
1113
|
nrows = len(id)
|
1109
1114
|
if nrows > 1 and id[nrows-1] < id[nrows-2]:
|
1110
1115
|
nrows = nrows-1
|
@@ -1167,7 +1172,7 @@ class Reader:
|
|
1167
1172
|
for name in content[2].split('\t') if name][2:]
|
1168
1173
|
# outer rotor motor
|
1169
1174
|
if 'Iron' in subregs and \
|
1170
|
-
'Rotor' in subregs:
|
1175
|
+
'Rotor' in subregs:
|
1171
1176
|
self.external_rotor = True
|
1172
1177
|
logger.info("Stator Subregions: %s", subregs)
|
1173
1178
|
speed = float(content[0].split()[-1])/60.
|
@@ -1183,7 +1188,11 @@ class Reader:
|
|
1183
1188
|
if not m:
|
1184
1189
|
return
|
1185
1190
|
m = np.array(m).T
|
1186
|
-
|
1191
|
+
d = np.diff(m[1])
|
1192
|
+
if self.ldq:
|
1193
|
+
ncols = (len(d)+1)//(len(d[d > 0])+1)
|
1194
|
+
else:
|
1195
|
+
ncols = (len(d)+1)//(len(d[d < 0])+1)
|
1187
1196
|
if ncols == 1 and len(m[1]) > 1 and m[1][0] != m[1][1]:
|
1188
1197
|
ncols = 2
|
1189
1198
|
id = np.reshape(m[0], (-1, ncols)).T[0]
|
@@ -1228,9 +1237,9 @@ class Reader:
|
|
1228
1237
|
(nrows, ncols)).T.tolist()
|
1229
1238
|
for k, v in zip(cols, m[2:])})
|
1230
1239
|
ls['speed'] = speed
|
1231
|
-
if self.external_rotor:
|
1240
|
+
if self.external_rotor:
|
1232
1241
|
ls.update(losses_mapping_external_rotor(ls))
|
1233
|
-
else:
|
1242
|
+
else:
|
1234
1243
|
ls.update(sttylosses(ls))
|
1235
1244
|
if self.ldq:
|
1236
1245
|
self.ldq['losses'] = ls
|
@@ -1486,9 +1495,9 @@ class Reader:
|
|
1486
1495
|
def __read_losses(self, content):
|
1487
1496
|
losses = {}
|
1488
1497
|
i = 0
|
1489
|
-
# check if external rotor
|
1498
|
+
# check if external rotor
|
1490
1499
|
if self.weights[0][1] == 0.0 and \
|
1491
|
-
self.weights[0][-1] > 0:
|
1500
|
+
self.weights[0][-1] > 0:
|
1492
1501
|
self.external_rotor = True
|
1493
1502
|
# find results for angle:
|
1494
1503
|
while True:
|
@@ -1547,8 +1556,8 @@ class Reader:
|
|
1547
1556
|
losses['total'] += losses['staza']+losses['stajo']
|
1548
1557
|
elif len(rec) == 1:
|
1549
1558
|
t = l.split(':')[-1].strip()
|
1550
|
-
if t == 'Iron':
|
1551
|
-
if self.external_rotor:
|
1559
|
+
if t == 'Iron':
|
1560
|
+
if self.external_rotor:
|
1552
1561
|
losses['rotfe'] = floatnan(rec[0])
|
1553
1562
|
losses['total'] += losses['rotfe']
|
1554
1563
|
else:
|
@@ -1560,14 +1569,14 @@ class Reader:
|
|
1560
1569
|
continue
|
1561
1570
|
|
1562
1571
|
if _rotloss.search(l):
|
1563
|
-
if l.find('StZa') > -1:
|
1572
|
+
if l.find('StZa') > -1:
|
1564
1573
|
self.external_rotor = True
|
1565
1574
|
rec = self.__findNums(content[i+2])
|
1566
1575
|
if len(rec) == 1:
|
1567
|
-
if self.external_rotor:
|
1576
|
+
if self.external_rotor:
|
1568
1577
|
losses['staza'] = floatnan(rec[0])
|
1569
1578
|
losses['total'] += losses['staza']
|
1570
|
-
else:
|
1579
|
+
else:
|
1571
1580
|
rotfe = floatnan(rec[0])
|
1572
1581
|
losses['rotfe'] += rotfe
|
1573
1582
|
losses['total'] += rotfe
|
@@ -1601,7 +1610,7 @@ class Reader:
|
|
1601
1610
|
if content[i+1].split() == ['rotf', '----']:
|
1602
1611
|
losses['rotfe'] = sum([floatnan(x) for x in rec])
|
1603
1612
|
losses['total'] += losses['rotfe']
|
1604
|
-
|
1613
|
+
|
1605
1614
|
if content[i+1].split() == ['Iron', '----']: # external rotor
|
1606
1615
|
losses['rotfe'] = sum([floatnan(x) for x in rec])
|
1607
1616
|
losses['total'] += losses['rotfe']
|
femagtools/dxfsl/converter.py
CHANGED
@@ -643,7 +643,8 @@ def convert(dxfile,
|
|
643
643
|
params['agndst'] = agndst(params.get('da1'),
|
644
644
|
params.get('da2'),
|
645
645
|
params.get('tot_num_slot'),
|
646
|
-
params.get('num_poles'),
|
646
|
+
params.get('num_poles'),
|
647
|
+
params.get('nodedist'))
|
647
648
|
|
648
649
|
if params['external_rotor']:
|
649
650
|
conv['fsl_magnet'] = outer
|
femagtools/dxfsl/fslrenderer.py
CHANGED
@@ -35,6 +35,7 @@ def agndst(da1, da2, Q, p, nodedist=1):
|
|
35
35
|
num_nodes = [i*lcm for i in range(nmin, nmax) if i*lcm%6==0]
|
36
36
|
dagset = [2*np.pi/p/i for i in num_nodes]
|
37
37
|
i = max(np.argmin(np.abs(np.array(dagset) - np.arctan2(ag, r))), 1)
|
38
|
+
# nodedist 0.5, 2, 4, 6
|
38
39
|
nd = min(round(nodedist), i)
|
39
40
|
try:
|
40
41
|
logger.info("Num nodes/p %d Num nodes/slot %g nodedist %g",
|
@@ -188,7 +189,7 @@ class FslRenderer(object):
|
|
188
189
|
'-- min_corner = {}, {}'.format(
|
189
190
|
geom.start_min_corner(0),
|
190
191
|
geom.start_min_corner(1)),
|
191
|
-
'-- max_corner = {}, {}'.format(
|
192
|
+
'-- max_corner start = {}, {}'.format(
|
192
193
|
geom.start_max_corner(0),
|
193
194
|
geom.start_max_corner(1)),
|
194
195
|
'\n']
|
@@ -210,11 +211,12 @@ class FslRenderer(object):
|
|
210
211
|
'r, phi = c2pr(x0, y0)',
|
211
212
|
'x1, y1 = pr2c(r1, phi)',
|
212
213
|
'x2, y2 = pr2c(r1, {}*math.pi/parts)'.format(slice),
|
214
|
+
f'r = {geom.dist_end_max_corner()}',
|
213
215
|
'x3, y3 = pr2c(r, {}*math.pi/parts)'.format(slice),
|
214
216
|
'nc_line(x0, y0, x1, y1, 0)',
|
215
217
|
'nc_circle_m(x1, y1, x2, y2, 0.0, 0.0, 0)',
|
216
218
|
'nc_line(x2, y2, x3, y3, 0)',
|
217
|
-
'x0, y0 = pr2c(r1 - hair/2, math.pi/parts)',
|
219
|
+
'x0, y0 = pr2c(r1 - hair/2, math.pi/parts/2)',
|
218
220
|
'create_mesh_se(x0, y0)',
|
219
221
|
'\n',
|
220
222
|
'outer_da_start = {}'.format(
|
femagtools/fsl.py
CHANGED
@@ -106,6 +106,7 @@ class Builder:
|
|
106
106
|
params['show_plots'] = model.stator[templ].get('plot', False)
|
107
107
|
params['write_fsl'] = True
|
108
108
|
params['airgap'] = -1.0
|
109
|
+
params['nodedist'] = model.stator.get('nodedist', 1)
|
109
110
|
pos = 'in' if model.external_rotor else 'out'
|
110
111
|
params['part'] = ('stator', pos)
|
111
112
|
conv = convert(model.stator['dxffile']['name'], **params)
|
@@ -352,7 +353,7 @@ class Builder:
|
|
352
353
|
'-- thermal properties in airgap',
|
353
354
|
'ag_cond = 0.063',
|
354
355
|
'thcap = 1007',
|
355
|
-
'beta =
|
356
|
+
'beta = math.pi*m.npols_gen/m.num_poles + m.zeroangl/180*math.pi',
|
356
357
|
'xai, yai = pr2c((da1+da2)/4, beta)',
|
357
358
|
'def_mat_therm(xai,yai,"cyan",1.19,ag_cond,thcap,1)',
|
358
359
|
'xai, yai = pr2c((da1+da2)/4-ag/4, beta)',
|
@@ -491,6 +492,11 @@ class Builder:
|
|
491
492
|
if not hasattr(model, 'stator'):
|
492
493
|
setattr(model, 'stator', {})
|
493
494
|
model.stator['num_slots'] = conv.get('tot_num_slot')
|
495
|
+
if model.get('num_agnodes', 0) == 0:
|
496
|
+
model.set_value('agndst', conv['agndst']*1e-3)
|
497
|
+
logger.info("num poles %d num slots %d outer diameter %.4f m agndst %.4f mm",
|
498
|
+
model.poles, model.stator['num_slots'],
|
499
|
+
model.outer_diam, model.agndst*1e3)
|
494
500
|
if params['full_model']:
|
495
501
|
model.stator['num_slots_gen'] = model.stator['num_slots']
|
496
502
|
else:
|
@@ -531,21 +537,6 @@ class Builder:
|
|
531
537
|
logger.info("create new model '%s'", model.name)
|
532
538
|
if model.is_dxffile():
|
533
539
|
self.prepare_model_with_dxf(model)
|
534
|
-
if model.get('num_agnodes', 0) == 0:
|
535
|
-
from femagtools.dxfsl.fslrenderer import agndst
|
536
|
-
ag = model.get('airgap')
|
537
|
-
model.set_value(
|
538
|
-
'agndst',
|
539
|
-
agndst(model.get('bore_diam'),
|
540
|
-
model.get('bore_diam') - 2*ag,
|
541
|
-
model.stator.get('num_slots'),
|
542
|
-
model.get('poles'),
|
543
|
-
model.stator.get('nodedist') or 1.0))
|
544
|
-
|
545
|
-
logger.info(" num poles %d num slots %d outer diameter %.4f m",
|
546
|
-
model.poles, model.stator['num_slots'],
|
547
|
-
model.outer_diam)
|
548
|
-
|
549
540
|
else:
|
550
541
|
self.prepare_stator(model)
|
551
542
|
if hasattr(model, 'magnet'):
|
@@ -553,14 +544,17 @@ class Builder:
|
|
553
544
|
self.prepare_diameter(model)
|
554
545
|
if self.fsl_stator and model.get('num_agnodes', 0) == 0:
|
555
546
|
from femagtools.dxfsl.fslrenderer import agndst
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
547
|
+
if model.get('agndst',0):
|
548
|
+
pass
|
549
|
+
else:
|
550
|
+
ag = model.get('airgap')
|
551
|
+
model.set_value(
|
552
|
+
'agndst',
|
553
|
+
agndst(model.get('bore_diam'),
|
554
|
+
model.get('bore_diam') - 2*ag,
|
555
|
+
model.stator.get('num_slots'),
|
556
|
+
model.get('poles'),
|
557
|
+
model.stator.get('nodedist') or 1.0))
|
564
558
|
|
565
559
|
model.set_num_slots_gen()
|
566
560
|
if hasattr(model, 'magnet'):
|