femagtools 1.8.7__py3-none-any.whl → 1.8.9__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 CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  """
4
4
  __title__ = 'femagtools'
5
- __version__ = '1.8.7'
5
+ __version__ = '1.8.9'
6
6
  __author__ = 'Ronald Tanner'
7
7
  __license__ = 'BSD'
8
- __copyright__ = 'Copyright 2023-2024 Gamma Technology'
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, amela_dir=None):
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
- pm_data = []
343
- for i in num_op:
344
- pmd = self.get_magnet_data(ibeta=i)
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
- r = self.get_magnet_data()
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'] + '/']