femagtools 1.8.7__py3-none-any.whl → 1.8.8__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 +2 -2
- femagtools/amela.py +18 -286
- femagtools/ecloss.py +121 -105
- femagtools/femag.py +5 -3
- femagtools/fsl.py +5 -5
- femagtools/isa7.py +134 -14
- femagtools/leakinduc.py +63 -0
- femagtools/machine/afpm.py +125 -56
- femagtools/machine/effloss.py +13 -1
- femagtools/mcv.py +26 -17
- femagtools/nc.py +16 -14
- femagtools/templates/psi-torq-rot.mako +98 -0
- femagtools/tks.py +1 -1
- femagtools/windings.py +65 -0
- {femagtools-1.8.7.dist-info → femagtools-1.8.8.dist-info}/METADATA +1 -1
- {femagtools-1.8.7.dist-info → femagtools-1.8.8.dist-info}/RECORD +24 -22
- {femagtools-1.8.7.dist-info → femagtools-1.8.8.dist-info}/WHEEL +1 -1
- tests/test_afpm.py +2 -2
- tests/test_amela.py +1 -3
- tests/test_fsl.py +4 -4
- tests/test_nc.py +1 -2
- {femagtools-1.8.7.dist-info → femagtools-1.8.8.dist-info}/LICENSE +0 -0
- {femagtools-1.8.7.dist-info → femagtools-1.8.8.dist-info}/entry_points.txt +0 -0
- {femagtools-1.8.7.dist-info → femagtools-1.8.8.dist-info}/top_level.txt +0 -0
femagtools/__init__.py
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
"""
|
4
4
|
__title__ = 'femagtools'
|
5
|
-
__version__ = '1.8.
|
5
|
+
__version__ = '1.8.8'
|
6
6
|
__author__ = 'Ronald Tanner'
|
7
7
|
__license__ = 'BSD'
|
8
|
-
__copyright__ = 'Copyright 2023-
|
8
|
+
__copyright__ = 'Copyright 2023-2025 Gamma Technology'
|
9
9
|
|
10
10
|
from .asm import read
|
11
11
|
from .bch import Reader
|
femagtools/amela.py
CHANGED
@@ -14,104 +14,6 @@ import subprocess
|
|
14
14
|
# set logging
|
15
15
|
logger = logging.getLogger(__name__)
|
16
16
|
|
17
|
-
def geometry_id(bndx, bndy):
|
18
|
-
'''identify the magnet geometry'''
|
19
|
-
# caculate magnet area
|
20
|
-
xy = 0
|
21
|
-
yx = 0
|
22
|
-
for i in range(len(bndy)):
|
23
|
-
if i < (len(bndy)-1):
|
24
|
-
xy += bndx[i]*bndy[i+1]
|
25
|
-
yx += bndy[i]*bndx[i+1]
|
26
|
-
else:
|
27
|
-
xy += bndx[i]*bndy[0]
|
28
|
-
yx += bndy[i]*bndx[0]
|
29
|
-
area = np.abs(yx-xy)*0.5
|
30
|
-
|
31
|
-
x0, y0 = np.mean(bndx), np.mean(bndy)
|
32
|
-
distances = []
|
33
|
-
for i in range(len(bndx)):
|
34
|
-
for j in range(len(bndy)):
|
35
|
-
dist = np.sqrt((bndx[i] - bndx[j])**2 + (bndy[i] - bndy[j])**2)
|
36
|
-
distances.append([dist, bndx[i], bndx[j], bndy[i], bndy[j]])
|
37
|
-
|
38
|
-
distances.sort(reverse=True)
|
39
|
-
xe = [distances[0][2], distances[2][1], distances[0][1], distances[2][2]]
|
40
|
-
ye = [distances[0][4], distances[2][3], distances[0][3], distances[2][4]]
|
41
|
-
|
42
|
-
# dimension
|
43
|
-
dim = np.zeros((3, 5))
|
44
|
-
x1, y1 = xe[0], ye[0]
|
45
|
-
|
46
|
-
for i in range(3):
|
47
|
-
x2, y2 = xe[i+1], ye[i+1]
|
48
|
-
dim[i, 0] = np.sqrt((x2-x1)**2 + (y2-y1)**2)
|
49
|
-
dim[i, 1:3] = [x1, x2]
|
50
|
-
dim[i, 3:5] = [y1, y2]
|
51
|
-
|
52
|
-
dim = dim[np.lexsort((dim[:, 2], dim[:, 1], dim[:, 0]))]
|
53
|
-
dx12, dy12 = dim[1, 1] - dim[1, 2], dim[1, 3] - dim[1, 4]
|
54
|
-
dx14, dy14 = dim[0, 1] - dim[0, 2], dim[0, 3] - dim[0, 4]
|
55
|
-
|
56
|
-
alp1 = np.arctan2(dy12, dx12)
|
57
|
-
alp2 = np.arctan2(dy14, dx14)
|
58
|
-
|
59
|
-
if alp1 < 0: alp1 += 2*np.pi
|
60
|
-
if alp2 < 0: alp2 += 2*np.pi
|
61
|
-
if alp2 < alp1:
|
62
|
-
alp2 += np.pi
|
63
|
-
alpha = (alp1 + (alp2 - np.pi/2))/2
|
64
|
-
else:
|
65
|
-
alpha = (alp1 + (alp2 - np.pi/2))/2 + np.pi
|
66
|
-
|
67
|
-
wm = dim[1, 0]
|
68
|
-
hm = area/wm
|
69
|
-
|
70
|
-
return dict(wm=wm,
|
71
|
-
hm=hm,
|
72
|
-
x0=x0,
|
73
|
-
y0=y0,
|
74
|
-
area=area,
|
75
|
-
alpha=alpha)
|
76
|
-
|
77
|
-
def tf(b1, b2, alpha):
|
78
|
-
'''Tranformation Matrix'''
|
79
|
-
T = np.array([[cos(alpha), sin(alpha)],
|
80
|
-
[-sin(alpha), cos(alpha)]])
|
81
|
-
if b1.ndim > 1:
|
82
|
-
r = T.dot(((b1.ravel()), (b2.ravel())))
|
83
|
-
return [r[0, :].reshape(*b1.shape),
|
84
|
-
r[1, :].reshape(*b1.shape)]
|
85
|
-
else:
|
86
|
-
return T.dot(((b1), (b2)))
|
87
|
-
|
88
|
-
def transform_coord(geometry, xcp, ycp):
|
89
|
-
'''transform from global coord to local coord'''
|
90
|
-
# transformation
|
91
|
-
elcp = tf(b1=np.array(xcp)-geometry['x0'],
|
92
|
-
b2=np.array(ycp)-geometry['y0'],
|
93
|
-
alpha=geometry['alpha'])
|
94
|
-
return dict(excpl=elcp[0, :]+geometry['wm']/2,
|
95
|
-
eycpl=elcp[1, :]+geometry['hm']/2,
|
96
|
-
excp=np.array(xcp),
|
97
|
-
eycp=np.array(ycp))
|
98
|
-
|
99
|
-
def transform_flux_denstiy(geometry, bx, by):
|
100
|
-
'''transform the magnet flux density to local coordinate system'''
|
101
|
-
# transformation
|
102
|
-
bxy = tf(b1=bx,
|
103
|
-
b2=by,
|
104
|
-
alpha=geometry['alpha'])
|
105
|
-
|
106
|
-
# remove DC component
|
107
|
-
bxf = np.mean(bxy[0].T - np.mean(bxy[0],axis=1).T,axis=1)
|
108
|
-
byf = np.mean(bxy[1].T - np.mean(bxy[1],axis=1).T,axis=1)
|
109
|
-
|
110
|
-
return dict(bxl=bxy[0],
|
111
|
-
byl=bxy[1],
|
112
|
-
bxf=bxf,
|
113
|
-
byf=byf
|
114
|
-
)
|
115
17
|
|
116
18
|
class Amela():
|
117
19
|
'''Run Amela Calculation
|
@@ -133,7 +35,8 @@ class Amela():
|
|
133
35
|
nseglen=0)
|
134
36
|
'''
|
135
37
|
|
136
|
-
def __init__(self, workdir: str, magnet_data: dict,
|
38
|
+
def __init__(self, workdir: str, magnet_data: dict,
|
39
|
+
amela_dir=None):
|
137
40
|
|
138
41
|
self.magn = magnet_data
|
139
42
|
self.workdir = pathlib.Path(workdir)
|
@@ -157,193 +60,11 @@ class Amela():
|
|
157
60
|
if 'nseglen' in self.magn:
|
158
61
|
self.cmd.append(f"--nseglen {self.magn['nseglen']}")
|
159
62
|
|
160
|
-
def get_magnet_data(self, ibeta=None) -> list:
|
161
|
-
'''Extract magnet data from nc file
|
162
|
-
|
163
|
-
Args:
|
164
|
-
ibeta: load case
|
165
|
-
|
166
|
-
Returns:
|
167
|
-
pm_data: list of magnet data
|
168
|
-
|
169
|
-
'''
|
170
|
-
nc_name = self.workdir / self.magn['name']
|
171
|
-
r = femagtools.nc.read(nc_name)
|
172
|
-
|
173
|
-
mag_spels = r.magnet_super_elements()
|
174
|
-
spel_key = [i.key for i in mag_spels]
|
175
|
-
spel_area = [float(i.area())*1e6 for i in mag_spels]
|
176
|
-
|
177
|
-
pm_elem_key = []
|
178
|
-
elem = []
|
179
|
-
pm_node_key = [[] for i in range(len(spel_key))]
|
180
|
-
bndkey = [[] for i in range(len(spel_key))]
|
181
|
-
bx = [[] for i in range(len(spel_key))]
|
182
|
-
by = [[] for i in range(len(spel_key))]
|
183
|
-
xcp = [[] for i in range(len(spel_key))]
|
184
|
-
ycp = [[] for i in range(len(spel_key))]
|
185
|
-
bndx = [[] for i in range(len(spel_key))]
|
186
|
-
bndy = [[] for i in range(len(spel_key))]
|
187
|
-
# prepare data for ialh method
|
188
|
-
wm = []
|
189
|
-
hm = []
|
190
|
-
alpha = []
|
191
|
-
x0 = []
|
192
|
-
y0 = []
|
193
|
-
geometry = []
|
194
|
-
elcp = []
|
195
|
-
bl = []
|
196
|
-
# conductivity and permeability of the magnets
|
197
|
-
cond = 0
|
198
|
-
mur = 0
|
199
|
-
# read boundary nodes
|
200
|
-
for k, i in enumerate(mag_spels):
|
201
|
-
|
202
|
-
cond = i.conduc
|
203
|
-
if cond == 0:
|
204
|
-
cond = 625000
|
205
|
-
logger.info('Magnet conductivity equals 0, using 625000 S/m')
|
206
|
-
|
207
|
-
mur = np.abs(1/i.elements[0].reluc[0])
|
208
|
-
logger.debug('Magnet: mur=%s, conductivity=%s', mur, cond)
|
209
|
-
|
210
|
-
pm_elem_key.append([j.key for j in i.elements])
|
211
|
-
elem.append(len(i.elements))
|
212
|
-
for j in i.elements:
|
213
|
-
for kk in j.vertices:
|
214
|
-
pm_node_key[k].append(kk.key - 1)
|
215
|
-
|
216
|
-
for bnd in i.nodechains:
|
217
|
-
for kk in bnd.nodes:
|
218
|
-
if len(bndkey[k]) > 0:
|
219
|
-
if kk.key != bndkey[k][-1]:
|
220
|
-
bndkey[k].append(kk.key)
|
221
|
-
bndx[k].append(kk.x*1e3)
|
222
|
-
bndy[k].append(kk.y*1e3)
|
223
|
-
else:
|
224
|
-
bndkey[k].append(kk.key)
|
225
|
-
bndx[k].append(kk.x*1e3)
|
226
|
-
bndy[k].append(kk.y*1e3)
|
227
|
-
|
228
|
-
bndkey[k].pop(-1)
|
229
|
-
bndx[k].pop(-1)
|
230
|
-
bndy[k].pop(-1)
|
231
|
-
geo = geometry_id(bndx=bndx[k], bndy=bndy[k])
|
232
|
-
geometry.append(geo)
|
233
|
-
# necessary?
|
234
|
-
hm.append(geo['hm'])
|
235
|
-
wm.append(geo['wm'])
|
236
|
-
x0.append(geo['x0'])
|
237
|
-
y0.append(geo['y0'])
|
238
|
-
alpha.append(geo['alpha'])
|
239
|
-
|
240
|
-
# default load angle (input beta I vs Up)
|
241
|
-
if ibeta is not None:
|
242
|
-
indx = ibeta
|
243
|
-
else:
|
244
|
-
num_cases = r.el_fe_induction_1.shape[3] - 1
|
245
|
-
if 'loadcase' in self.magn:
|
246
|
-
indx = self.magn['loadcase'] - 1
|
247
|
-
else:
|
248
|
-
indx = num_cases
|
249
|
-
if indx == 3:
|
250
|
-
indx = num_cases # avoid error
|
251
|
-
|
252
|
-
# stationary case, no rotation
|
253
|
-
poles = 0
|
254
|
-
try:
|
255
|
-
poles = r.num_poles
|
256
|
-
except AttributeError:
|
257
|
-
pass
|
258
|
-
|
259
|
-
# read mesh and flux density
|
260
|
-
for i in range(len(spel_key)):
|
261
|
-
bx[i].append(np.zeros((elem[i], len(r.pos_el_fe_induction))))
|
262
|
-
by[i].append(np.zeros((elem[i], len(r.pos_el_fe_induction))))
|
263
|
-
for index, j in enumerate(mag_spels[i].elements):
|
264
|
-
xcp[i].append(float(j.center[0]*1e3))
|
265
|
-
ycp[i].append(float(j.center[1]*1e3))
|
266
|
-
theta = np.arctan2(float(j.center[1]), float(j.center[0]))
|
267
|
-
fd = r.flux_density(j, 0, indx)
|
268
|
-
if poles == 0:
|
269
|
-
bx[i][0][index, :] = fd['bx']
|
270
|
-
by[i][0][index, :] = fd['by']
|
271
|
-
else:
|
272
|
-
bx[i][0][index, :] = fd['bx']*np.cos(theta) - \
|
273
|
-
fd['by']*np.sin(theta)
|
274
|
-
by[i][0][index, :] = fd['bx']*np.sin(theta) + \
|
275
|
-
fd['by']*np.cos(theta)
|
276
|
-
|
277
|
-
elcp.append(transform_coord(geometry[i], xcp[i], ycp[i]))
|
278
|
-
bl.append(transform_flux_denstiy(geometry[i], bx[i][0], by[i][0]))
|
279
|
-
|
280
|
-
if poles == 0:
|
281
|
-
freq = self.magn.get('f', r.speed)
|
282
|
-
time_vec = np.linspace(0, 1/freq, len(r.pos_el_fe_induction))
|
283
|
-
pos = dict(time=time_vec.tolist(),
|
284
|
-
freq=freq,
|
285
|
-
t=float(1/freq))
|
286
|
-
# reset num.poles
|
287
|
-
poles = 1
|
288
|
-
else:
|
289
|
-
rpm = self.magn.get('speed', r.speed)
|
290
|
-
ag_sim = r.pos_el_fe_induction[-1] - r.pos_el_fe_induction[0]
|
291
|
-
pos = dict(phi=(r.pos_el_fe_induction*180/pi).tolist(),
|
292
|
-
speed=rpm,
|
293
|
-
t=float(60/rpm*ag_sim/360)) # TODO
|
294
|
-
# prep dictionary for the loss calculation
|
295
|
-
pm_data = []
|
296
|
-
for i in range(len(spel_key)):
|
297
|
-
pm_data.append(dict(name='pm_data_se' + str(spel_key[i]),
|
298
|
-
hm=self.magn.get('hm', hm[i]),
|
299
|
-
wm=self.magn.get('wm', wm[i]),
|
300
|
-
lm=self.magn.get('lm', r.arm_length*1e3),
|
301
|
-
alpha=alpha[i],
|
302
|
-
ls=r.arm_length*1e3,
|
303
|
-
sigma=float(self.magn.get('sigma', cond)),
|
304
|
-
mur=float(self.magn.get('mur', mur)),
|
305
|
-
loadcase=self.magn.get('loadcase', indx),
|
306
|
-
numpoles=poles,
|
307
|
-
nodes=dict(),
|
308
|
-
elements=dict(),
|
309
|
-
bndkeys=bndkey[i],
|
310
|
-
bndx=[float(c) for c in bndx[i]],
|
311
|
-
bndy=[float(c) for c in bndy[i]],
|
312
|
-
bl=bl[i],
|
313
|
-
elcp=elcp[i],
|
314
|
-
area=spel_area[i],
|
315
|
-
spel_key=spel_key[i]))
|
316
|
-
pm_data[i].update(pos)
|
317
|
-
|
318
|
-
for k in range(len(pm_node_key)):
|
319
|
-
for i, j in enumerate(pm_node_key[k]):
|
320
|
-
pm_data[k]['nodes'][str(j + 1)] = dict(
|
321
|
-
key=int(j + 1),
|
322
|
-
x=float(r.nodes[j].x*1e3),
|
323
|
-
y=float(r.nodes[j].y*1e3),
|
324
|
-
Az=float(r.nodes[j].vpot[0])
|
325
|
-
)
|
326
|
-
for i, j in enumerate(pm_elem_key[k]):
|
327
|
-
pm_data[k]['elements'][str(j)] = dict(
|
328
|
-
key=int(j + 1),
|
329
|
-
xcp=xcp[k][i],
|
330
|
-
ycp=ycp[k][i],
|
331
|
-
Bx=bx[k][0][i, :].tolist(),
|
332
|
-
By=by[k][0][i, :].tolist()
|
333
|
-
)
|
334
|
-
|
335
|
-
if len(mag_spels) / r.poles_sim > 1:
|
336
|
-
return pm_data
|
337
|
-
else:
|
338
|
-
return [pm_data[0]]
|
339
|
-
|
340
63
|
def get_magnet_data_all(self, num_op):
|
341
64
|
'''get all magnet data for all loadcases'''
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
pm_data.append(pmd)
|
346
|
-
return pm_data
|
65
|
+
nc_name = self.workdir / self.magn['name']
|
66
|
+
r = femagtools.nc.read(nc_name)
|
67
|
+
return [r.get_magnet_data(ibeta=i) for i in num_op]
|
347
68
|
|
348
69
|
def export_json(self, pm_data: list):
|
349
70
|
'''Export magnet data to json files
|
@@ -352,6 +73,7 @@ class Amela():
|
|
352
73
|
pm_data: list of magnet data
|
353
74
|
|
354
75
|
'''
|
76
|
+
return
|
355
77
|
pm_dir = self.amela_dir / self.magn['name']
|
356
78
|
pm_dir.mkdir(exist_ok=True)
|
357
79
|
for i in pm_data:
|
@@ -396,9 +118,19 @@ class Amela():
|
|
396
118
|
losses
|
397
119
|
'''
|
398
120
|
# get magnet data
|
399
|
-
|
121
|
+
nc_name = self.workdir / self.magn['name']
|
122
|
+
nc = femagtools.nc.read(nc_name)
|
123
|
+
num_cases = nc.el_fe_induction_1.shape[3] - 1
|
124
|
+
if 'loadcase' in self.magn:
|
125
|
+
indx = self.magn['loadcase'] - 1
|
126
|
+
else:
|
127
|
+
indx = num_cases
|
128
|
+
if indx == 3:
|
129
|
+
indx = num_cases # avoid error
|
130
|
+
|
131
|
+
r = nc.get_magnet_data(indx)
|
400
132
|
# export to json
|
401
|
-
self.export_json(r)
|
133
|
+
#self.export_json(r)
|
402
134
|
# run amela
|
403
135
|
calc_method = 'IALH' if ialh else '3DI'
|
404
136
|
cmd = self.cmd + ['--calc', calc_method, self.magn['name'] + '/']
|