femagtools 1.8.5__py3-none-any.whl → 1.8.7__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/bch.py +16 -9
- femagtools/dxfsl/area.py +3 -0
- femagtools/dxfsl/conv.py +1 -8
- femagtools/dxfsl/converter.py +62 -144
- femagtools/dxfsl/geom.py +49 -0
- femagtools/femag.py +13 -72
- femagtools/fsl.py +6 -3
- femagtools/isa7.py +47 -3
- femagtools/losscoeffs.py +29 -3
- femagtools/machine/afpm.py +82 -21
- femagtools/mcv.py +162 -7
- femagtools/multiproc.py +2 -1
- femagtools/parstudy.py +3 -1
- femagtools/plot/__init__.py +1 -0
- femagtools/plot/bch.py +172 -36
- femagtools/plot/machine.py +100 -0
- femagtools/plot/nc.py +13 -0
- femagtools/poc.py +10 -0
- femagtools/shortcircuit.py +378 -0
- femagtools/templates/psi-torq-rem-rot.mako +127 -0
- femagtools/utils.py +2 -0
- femagtools/zmq.py +22 -4
- {femagtools-1.8.5.dist-info → femagtools-1.8.7.dist-info}/METADATA +1 -1
- {femagtools-1.8.5.dist-info → femagtools-1.8.7.dist-info}/RECORD +30 -27
- tests/test_nc.py +11 -0
- {femagtools-1.8.5.dist-info → femagtools-1.8.7.dist-info}/LICENSE +0 -0
- {femagtools-1.8.5.dist-info → femagtools-1.8.7.dist-info}/WHEEL +0 -0
- {femagtools-1.8.5.dist-info → femagtools-1.8.7.dist-info}/entry_points.txt +0 -0
- {femagtools-1.8.5.dist-info → femagtools-1.8.7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,378 @@
|
|
1
|
+
import pathlib
|
2
|
+
import logging
|
3
|
+
import json
|
4
|
+
import numpy as np
|
5
|
+
import scipy.optimize as so
|
6
|
+
from scipy.interpolate import make_interp_spline
|
7
|
+
import scipy.integrate as ig
|
8
|
+
import femagtools.parstudy
|
9
|
+
|
10
|
+
logger = logging.getLogger('shortcircuit')
|
11
|
+
|
12
|
+
def _parstudy_list(femag, result_func):
|
13
|
+
workdir = femag.workdir
|
14
|
+
magnetMat = femag.magnets
|
15
|
+
magnetizingCurves = femag.magnetizingCurves
|
16
|
+
condMat = femag.condMat
|
17
|
+
templatedirs = femag.templatedirs
|
18
|
+
cmd = femag.cmd
|
19
|
+
return femagtools.parstudy.List(
|
20
|
+
workdir, condMat=condMat, magnets=magnetMat,
|
21
|
+
magnetizingCurves=magnetizingCurves,
|
22
|
+
cmd=cmd, result_func=result_func)
|
23
|
+
|
24
|
+
def get_shortCircuit_parameters(bch, nload):
|
25
|
+
"""extracts shortciruit parameters from bch"""
|
26
|
+
try:
|
27
|
+
if nload < 0:
|
28
|
+
nload = 0
|
29
|
+
if nload > 2:
|
30
|
+
nload = 2
|
31
|
+
if nload > 0:
|
32
|
+
dqld = bch.dqPar['ld']
|
33
|
+
dqlq = bch.dqPar['lq']
|
34
|
+
dqpsim = bch.dqPar['psim']
|
35
|
+
if len(dqld) <= nload or len(dqlq) <= nload or len(dqpsim) <= nload:
|
36
|
+
ld = dqld[-1]/bch.armatureLength
|
37
|
+
lq = dqlq[-1]/bch.armatureLength
|
38
|
+
psim = dqpsim[-1]/bch.armatureLength
|
39
|
+
else:
|
40
|
+
ld = dqld[nload-1]/bch.armatureLength
|
41
|
+
lq = dqlq[nload-1]/bch.armatureLength
|
42
|
+
psim = dqpsim[nload-1]/bch.armatureLength
|
43
|
+
else:
|
44
|
+
ld = bch.machine['ld']/bch.armatureLength
|
45
|
+
lq = bch.machine['lq']/bch.armatureLength
|
46
|
+
psim = bch.machine['psim']/bch.armatureLength
|
47
|
+
return dict(
|
48
|
+
r1=bch.machine['r1'],
|
49
|
+
ld=ld,
|
50
|
+
lq=lq,
|
51
|
+
Hk=bch.magnet['demag_hx'],
|
52
|
+
Tmag=bch.magnet['Tmag'],
|
53
|
+
psim=psim,
|
54
|
+
num_pol_pair=bch.machine['p'],
|
55
|
+
fc_radius=bch.machine['fc_radius'],
|
56
|
+
lfe=bch.armatureLength/1e3,
|
57
|
+
pocfilename=bch.machine['pocfile'],
|
58
|
+
num_par_wdgs=bch.machine.get('num_par_wdgs', 1),
|
59
|
+
calculationMode='shortcircuit')
|
60
|
+
except (KeyError, AttributeError, IndexError):
|
61
|
+
raise ValueError("missing pm/Rel-Sim results")
|
62
|
+
|
63
|
+
|
64
|
+
def find_peaks_and_valleys(t, y):
|
65
|
+
""" return peak and valley of y with maximum amplitude
|
66
|
+
"""
|
67
|
+
peaks = (np.diff(np.sign(np.diff(y))) < 0).nonzero()[0] + 1
|
68
|
+
if len(peaks>0):
|
69
|
+
ip = np.argmax(y[peaks])
|
70
|
+
pv = {'ip': y[peaks][ip], 'tp': t[peaks][ip]}
|
71
|
+
else:
|
72
|
+
pv = {'ip': [], 'tp': []}
|
73
|
+
valleys = (np.diff(np.sign(np.diff(y))) > 0).nonzero()[0] + 1
|
74
|
+
if len(valleys>0):
|
75
|
+
iv = np.argmin(y[valleys])
|
76
|
+
pv.update({'iv': y[valleys][iv], 'tv': t[valleys][iv]})
|
77
|
+
else:
|
78
|
+
pv.update({'iv': [], 'tv': []})
|
79
|
+
pv.update({'peaks': y[peaks], 'valleys': y[valleys],
|
80
|
+
'tpeaks': t[peaks], 'tvalleys': t[valleys]})
|
81
|
+
return pv
|
82
|
+
|
83
|
+
def shortcircuit(femag, machine, bch, simulation, engine=0):
|
84
|
+
scdata = {}
|
85
|
+
calcmode = simulation.get('calculationMode', '')
|
86
|
+
simulation.update(
|
87
|
+
get_shortCircuit_parameters(bch,
|
88
|
+
simulation.get('initial', 2)))
|
89
|
+
if 'speed' not in simulation:
|
90
|
+
simulation['speed'] = bch.dqPar['speed']
|
91
|
+
if simulation.get('sc_type', 3) == 3:
|
92
|
+
logger.info("3phase short circuit simulation")
|
93
|
+
builder = femagtools.fsl.Builder(femag.templatedirs)
|
94
|
+
fslcmds = (builder.open_model(femag.model) +
|
95
|
+
builder.create_shortcircuit(simulation))
|
96
|
+
fslfile = 'shortcircuit.fsl'
|
97
|
+
(pathlib.Path(femag.workdir)/fslfile).write_text(
|
98
|
+
'\n'.join(fslcmds),
|
99
|
+
encoding='latin1', errors='ignore')
|
100
|
+
femag.run(fslfile) # options?
|
101
|
+
bchfile = femag.get_bch_file(femag.modelname)
|
102
|
+
if bchfile:
|
103
|
+
bchsc = femagtools.bch.Reader()
|
104
|
+
logger.info("Read BCH %s", bchfile)
|
105
|
+
bchsc.read(pathlib.Path(bchfile).read_text(
|
106
|
+
encoding='latin1', errors='ignore'))
|
107
|
+
bchsc.scData['demag'] = bchsc.demag
|
108
|
+
if simulation.get('sim_demagn', 0):
|
109
|
+
d = {'displ': [d['displ']
|
110
|
+
for d in bchsc.demag if 'displ' in d],
|
111
|
+
'H_max': [d['H_max']
|
112
|
+
for d in bchsc.demag if 'H_max' in d],
|
113
|
+
'H_av': [d['H_av']
|
114
|
+
for d in bchsc.demag if 'H_av' in d]}
|
115
|
+
simulation['i1max'] = bchsc.scData['iks']
|
116
|
+
bchsc.scData['demag'] = demag(
|
117
|
+
femag, machine, simulation, engine)
|
118
|
+
bchsc.scData['demag'].update(d)
|
119
|
+
scdata = bchsc.scData
|
120
|
+
#for w in bch.flux:
|
121
|
+
# try:
|
122
|
+
# bch.flux[w] += bchsc.flux[w]
|
123
|
+
# bch.flux_fft[w] += bchsc.flux_fft[w]
|
124
|
+
# except (KeyError, IndexError):
|
125
|
+
# logging.debug(
|
126
|
+
# "No additional flux data in sc simulation")
|
127
|
+
# break
|
128
|
+
|
129
|
+
if simulation.get('sc_type', 3) == 2:
|
130
|
+
if 'i1max' not in simulation:
|
131
|
+
# just a wild guess
|
132
|
+
simulation['i1max'] = 4.5*bch.machine['i1']
|
133
|
+
logger.info("2phase short circuit simulation i1max = %.0f",
|
134
|
+
simulation['i1max'])
|
135
|
+
scdata = shortcircuit_2phase(femag, machine, simulation, engine)
|
136
|
+
|
137
|
+
else:
|
138
|
+
logger.warning("Empty shortcircuit results for type %d",
|
139
|
+
simulation.get('sc_type', 'unknown'))
|
140
|
+
# must reset calcmode
|
141
|
+
if calcmode:
|
142
|
+
simulation['calculationMode'] = calcmode
|
143
|
+
else:
|
144
|
+
del simulation['calculationMode']
|
145
|
+
return scdata
|
146
|
+
|
147
|
+
def sc_result_func(task):
|
148
|
+
basedir = pathlib.Path(task.directory)
|
149
|
+
psitorq = np.loadtxt(basedir/'psi-torq-rot.dat')
|
150
|
+
pos = np.unique(psitorq[:, 0])
|
151
|
+
ncurs = psitorq[:, 0].shape[0]//pos.shape[0]
|
152
|
+
ire = psitorq[:ncurs, 1:4]
|
153
|
+
psire = np.reshape(psitorq[:, 4:7], (-1, ncurs, 3))
|
154
|
+
torq = np.reshape(psitorq[:, 7], (-1, ncurs))
|
155
|
+
return {'pos': pos.tolist(), 'ire': ire.tolist(),
|
156
|
+
'psire': psire.tolist(), 'torq': torq.tolist()}
|
157
|
+
|
158
|
+
|
159
|
+
def shortcircuit_2phase(femag, machine, simulation, engine=0):
|
160
|
+
i1max = simulation['i1max']
|
161
|
+
num_cur_steps = 4
|
162
|
+
i1 = np.linspace(0, i1max, num_cur_steps)
|
163
|
+
i1vec = np.concat((-i1[::-1], i1[1:]))
|
164
|
+
num_par_wdgs = machine['winding'].get('num_par_wdgs', 1)
|
165
|
+
flux_sim = {
|
166
|
+
'calculationMode': 'psi-torq-rot',
|
167
|
+
'i1max': i1max,
|
168
|
+
'curvec': [],
|
169
|
+
'num_par_wdgs': num_par_wdgs}
|
170
|
+
|
171
|
+
if engine:
|
172
|
+
parstudy = _parstudy_list(femag, sc_result_func)
|
173
|
+
parvardef = {
|
174
|
+
"decision_vars": [
|
175
|
+
{"values": i1vec, "name": "curvec"}]
|
176
|
+
}
|
177
|
+
results = parstudy(parvardef, machine, flux_sim, engine)
|
178
|
+
|
179
|
+
ire = np.array([r['ire'][0] for r in results['f']])
|
180
|
+
pos = np.array(results['f'][0]['pos'])
|
181
|
+
phi = pos*np.pi/180
|
182
|
+
torq = np.hstack([r['torq'] for r in results['f']])
|
183
|
+
psire = np.hstack([r['psire'] for r in results['f']])
|
184
|
+
else:
|
185
|
+
simulation.update(flux_sim)
|
186
|
+
simulation['curvec'] = i1vec.tolist()
|
187
|
+
results = femag(machine, simulation)
|
188
|
+
class Task:
|
189
|
+
def __init__(self, workdir):
|
190
|
+
self.directory = workdir
|
191
|
+
results = sc_result_func(Task(femag.workdir))
|
192
|
+
ire = np.array(results['ire'])
|
193
|
+
pos = np.array(results['pos'])
|
194
|
+
torq = np.array(results['torq'])
|
195
|
+
psire = np.array(results['psire'])
|
196
|
+
|
197
|
+
#with open('results.json', 'w') as fp:
|
198
|
+
# json.dump({'ire': ire.tolist(), 'pos': pos.tolist(),
|
199
|
+
# 'torq': torq.tolist(), 'psire': psire.tolist()}, fp)
|
200
|
+
logger.info("move steps %d currents %s", len(pos), ire[:,0])
|
201
|
+
|
202
|
+
Ai = [femagtools.utils.fft(pos, psire[:, k, 0])['a']
|
203
|
+
for k in range(np.shape(psire)[1])]
|
204
|
+
A = make_interp_spline(ire[:,0], Ai)
|
205
|
+
A0i = [femagtools.utils.fft(pos, psire[:, k, 0])['a0']
|
206
|
+
for k in range(np.shape(psire)[1])]
|
207
|
+
A0 = make_interp_spline(ire[:,0], A0i)
|
208
|
+
Bi = [femagtools.utils.fft(pos, psire[:, k, 1])['a']
|
209
|
+
for k in range(np.shape(psire)[1]-1, -1, -1)]
|
210
|
+
B = make_interp_spline(ire[::-1,1], Bi)
|
211
|
+
B0i = [femagtools.utils.fft(pos, psire[:, k, 1])['a0']
|
212
|
+
for k in range(np.shape(psire)[1]-1, -1, -1)]
|
213
|
+
B0 = make_interp_spline(ire[::-1,1], B0i)
|
214
|
+
alfa0_ai = [femagtools.utils.fft(pos, psire[:, k, 0])['alfa0']
|
215
|
+
for k in range(np.shape(psire)[1])]
|
216
|
+
alfa0_a = make_interp_spline(ire[:,0], alfa0_ai)
|
217
|
+
alfa0_bi = [femagtools.utils.fft(pos, psire[:, k, 1])['alfa0']
|
218
|
+
for k in range(np.shape(psire)[1]-1, -1, -1)]
|
219
|
+
alfa0_b = make_interp_spline(ire[::-1,1], alfa0_bi)
|
220
|
+
|
221
|
+
Tqi = [femagtools.utils.fft(pos, torq[:, k])['a']
|
222
|
+
for k in range(np.shape(torq)[1])]
|
223
|
+
Tq = make_interp_spline(ire[:, 0], Tqi)
|
224
|
+
Tq0i = [femagtools.utils.fft(pos, torq[:, k])['a0']
|
225
|
+
for k in range(np.shape(torq)[1])]
|
226
|
+
Tq0 = make_interp_spline(ire[:, 0], Tq0i)
|
227
|
+
alfa0_t = [femagtools.utils.fft(pos, torq[:, k])['alfa0']
|
228
|
+
for k in range(np.shape(torq)[1])]
|
229
|
+
|
230
|
+
T0 = np.mean([femagtools.utils.fft(pos, psire[:, k, 0])['T0']
|
231
|
+
for k in range(np.shape(psire)[1])])
|
232
|
+
pp = 360/T0
|
233
|
+
|
234
|
+
def torque(phi, i):
|
235
|
+
try:
|
236
|
+
alfa0 = np.ones(len(i))*np.mean(alfa0_t)
|
237
|
+
alfa0[i < 0] = alfa0_t[0]
|
238
|
+
alfa0[i > 0] = alfa0_t[-1]
|
239
|
+
except TypeError:
|
240
|
+
alfa0 = np.mean(alfa0_t)
|
241
|
+
if i < 0:
|
242
|
+
alfa0 = alfa0_t[0]
|
243
|
+
if i > 0:
|
244
|
+
alfa0 = alfa0_t[-1]
|
245
|
+
return Tq(i)*np.cos(pp*phi+alfa0) + Tq0(i)
|
246
|
+
|
247
|
+
def psia(phi, i):
|
248
|
+
return A(i)*np.cos(pp*phi+alfa0_a(i))+A0(i)
|
249
|
+
|
250
|
+
def psib(phi, i):
|
251
|
+
return B(i)*np.cos(pp*phi+alfa0_b(i))+B0(i)
|
252
|
+
|
253
|
+
def dpsiadi(phi,i):
|
254
|
+
return A(i, nu=1)*np.cos(pp*phi+alfa0_a(i))+A0(i,nu=1)
|
255
|
+
def dpsiadphi(phi,i):
|
256
|
+
return -pp*A(i)*np.sin(pp*phi+alfa0_a(i))
|
257
|
+
def dpsibdi(phi,i):
|
258
|
+
return B(i, nu=1)*np.cos(pp*phi+alfa0_b(i))+B0(i,nu=1)
|
259
|
+
def dpsibdphi(phi,i):
|
260
|
+
return -pp*B(i)*np.sin(pp*phi+alfa0_b(i))
|
261
|
+
|
262
|
+
speed = simulation['speed']
|
263
|
+
r1 = simulation['r1']
|
264
|
+
l1s = simulation.get('l1s',0)
|
265
|
+
wm = 2*np.pi*speed
|
266
|
+
w1 = pp*wm
|
267
|
+
|
268
|
+
def didt(t, y):
|
269
|
+
return [((2*r1*y[0] + wm*(
|
270
|
+
dpsiadphi(y[1],y[0]) - dpsibdphi(y[1],-y[0])))/
|
271
|
+
(-dpsiadi(y[1],y[0]) - dpsibdi(y[1],-y[0]) -2*l1s)),
|
272
|
+
wm]
|
273
|
+
tmin = simulation.get('tstart', 0)
|
274
|
+
tmax = simulation.get('simultime', 0.1)
|
275
|
+
nsamples = simulation.get('nsamples', 400)
|
276
|
+
t = np.linspace(tmin, tmax, nsamples)
|
277
|
+
|
278
|
+
def func(x):
|
279
|
+
return B(0)*np.sin(pp*x+alfa0_b(0)) - A(0)*np.sin(pp*x+alfa0_a(0))
|
280
|
+
phi0 = so.fsolve(func, [0])[0]
|
281
|
+
|
282
|
+
Y0 = [0, phi0]
|
283
|
+
sol = ig.solve_ivp(didt, (t[0], t[-1]), Y0, dense_output=True)
|
284
|
+
ia = sol.sol(t).T[:, 0]
|
285
|
+
pv = find_peaks_and_valleys(t, ia)
|
286
|
+
iap = pv['tp'], pv['ip']
|
287
|
+
iav = pv['tv'], pv['iv']
|
288
|
+
iac = pv['tpeaks'][-1], pv['peaks'][-1]
|
289
|
+
|
290
|
+
logger.info("Ia %.1f %.1f %.1f (phi0 %.4f)",
|
291
|
+
iap[1], iav[1], iac[1], phi0)
|
292
|
+
|
293
|
+
def func(x):
|
294
|
+
y = torque(wm*t+phi0+x, ia)
|
295
|
+
pv = find_peaks_and_valleys(t, y)
|
296
|
+
return pv['peaks'][-1] + pv['valleys'][-1]
|
297
|
+
|
298
|
+
dphi = so.fsolve(func, [0])[0]
|
299
|
+
torque = torque(wm*t+phi0+dphi, ia)
|
300
|
+
pv = find_peaks_and_valleys(t, torque)
|
301
|
+
tp = pv['tp'], pv['ip']
|
302
|
+
tv = pv['tv'], pv['iv']
|
303
|
+
tc = pv['tpeaks'][-1], pv['peaks'][-1]
|
304
|
+
logger.info("Torque %.1f %.1f %.1f (dphi %.4f)",
|
305
|
+
tp[1], tv[1], tc[1], dphi)
|
306
|
+
|
307
|
+
scData = {
|
308
|
+
'ia': ia.tolist(),
|
309
|
+
'ib': (-ia).tolist(),
|
310
|
+
'ic': np.zeros(ia.shape).tolist(),
|
311
|
+
'time': t.tolist(),
|
312
|
+
'torque': torque.tolist(),
|
313
|
+
'speed': speed,
|
314
|
+
'ikd': iac[1],
|
315
|
+
'tkd': tc[1],
|
316
|
+
'iks': iap[1] if iap[1] > abs(iav[1]) else iav[1],
|
317
|
+
'tks': tp[1] if tp[1] > abs(tv[1]) else tv[1]
|
318
|
+
}
|
319
|
+
scData['peakWindingCurrents'] = [scData['iks'],
|
320
|
+
-scData['iks'], 0]
|
321
|
+
if simulation.get('sim_demagn', 0):
|
322
|
+
scData['demag'] = demag(femag, machine, simulation, engine)
|
323
|
+
return scData
|
324
|
+
|
325
|
+
def dm_result_func(task):
|
326
|
+
basedir = pathlib.Path(task.directory)
|
327
|
+
i1rr = []
|
328
|
+
for f in sorted(basedir.glob('psi-torq-rem-rot-*.dat')):
|
329
|
+
ptr = np.loadtxt(f)
|
330
|
+
i1rr.append((np.max(ptr.T[1:4]), np.min(ptr.T[-1])))
|
331
|
+
return i1rr
|
332
|
+
|
333
|
+
def demag(femag, machine, simulation, engine=0):
|
334
|
+
"""demag simulation using psi-torq-rem-rot"""
|
335
|
+
logger.info("Demagnetization processing")
|
336
|
+
i1max = simulation['i1max']
|
337
|
+
i1min = simulation.get('i1min', i1max/4)
|
338
|
+
num_steps = 7
|
339
|
+
b = (i1min-i1max)/np.log(i1min/i1max)
|
340
|
+
a = i1max/b
|
341
|
+
i1tab = [b*(a+np.log(x))
|
342
|
+
for x in np.linspace(i1min/i1max, 1,
|
343
|
+
num_steps)]
|
344
|
+
|
345
|
+
if simulation.get('sc_type', 3) == 3:
|
346
|
+
curvec = [[-a/2, a, -a/2] for a in i1tab]
|
347
|
+
else:
|
348
|
+
curvec = [[a, -a, 0] for a in i1tab]
|
349
|
+
simulation.update({
|
350
|
+
'calculationMode': 'psi-torq-rem-rot',
|
351
|
+
'curvec': curvec})
|
352
|
+
if engine:
|
353
|
+
parstudy = _parstudy_list(femag, dm_result_func)
|
354
|
+
parvardef = {
|
355
|
+
"decision_vars": [
|
356
|
+
{"values": curvec, "name": "curvec"}]
|
357
|
+
}
|
358
|
+
results = parstudy(parvardef, machine, simulation, engine)
|
359
|
+
i1rr = np.vstack(
|
360
|
+
((0, 1),
|
361
|
+
np.array(results['f']).reshape((-1, 2))))
|
362
|
+
else:
|
363
|
+
class Task:
|
364
|
+
def __init__(self, workdir):
|
365
|
+
self.directory = workdir
|
366
|
+
_ = femag(machine, simulation)
|
367
|
+
i1rr = np.vstack(
|
368
|
+
[(0, 1), dm_result_func(Task(femag.workdir))])
|
369
|
+
i1, rr = np.array(i1rr).T
|
370
|
+
dmag = {'Hk': simulation['Hk'],
|
371
|
+
'Tmag': simulation['Tmag'],
|
372
|
+
'i1': i1.tolist(),
|
373
|
+
'rr': rr.tolist()}
|
374
|
+
# critical current
|
375
|
+
if np.min(rr) < 0.99:
|
376
|
+
k = np.where(rr < 0.99)[0][0]
|
377
|
+
dmag['i1c'] = i1[k]
|
378
|
+
return dmag
|
@@ -0,0 +1,127 @@
|
|
1
|
+
-- calculate flux linkages, torque, and rel remanence (magstatic mode)
|
2
|
+
--
|
3
|
+
-- model:
|
4
|
+
-- Hk (kA/m) knee point field strength
|
5
|
+
-- curvec (A) phase current amplitude samples (list of [ía, ib, ic])
|
6
|
+
-- num_par_wdgs (number of parallel winding groups, default 1)
|
7
|
+
-- fc_radius (m) radius of airgap center
|
8
|
+
--
|
9
|
+
-- creates file psi-torq-rem-rot.dat in current directory with columns:
|
10
|
+
-- displ curr1 curr2 curr3 psi1 psi2 psi3 torq rrem
|
11
|
+
|
12
|
+
function gcd(a, b)
|
13
|
+
return b==0 and a or gcd(b,a%b)
|
14
|
+
end
|
15
|
+
|
16
|
+
function dmg(ek, Br, alfam, murm, Bd, Bq, Hd, Hq, alfahm, Hk)
|
17
|
+
if Hd < Hk then
|
18
|
+
muem = murm*4*math.pi*1e-7
|
19
|
+
Brn = Bd - Hk*1e3*muem
|
20
|
+
if Br < 1e-5 then
|
21
|
+
Brn = 1e-5
|
22
|
+
end
|
23
|
+
return Brn, alfam, 1
|
24
|
+
end
|
25
|
+
return Br, alfam, 1
|
26
|
+
end
|
27
|
+
|
28
|
+
function calc_flux_torq_rem(phi, curvec)
|
29
|
+
for k=1,3 do
|
30
|
+
def_curr_wdg(k, curvec[k]/a, 0)
|
31
|
+
end
|
32
|
+
|
33
|
+
dRR = 0
|
34
|
+
RR = 0
|
35
|
+
maxit=m.num_nonl_it
|
36
|
+
maxcop=m.error_perm -- err_perm in %
|
37
|
+
permode='restore'
|
38
|
+
repeat
|
39
|
+
calc_field_single({
|
40
|
+
maxit=maxit, maxcop=maxcop, -- err_perm in %
|
41
|
+
permode=permode})
|
42
|
+
if(maxit > 1) then
|
43
|
+
maxit = 1
|
44
|
+
permode='actual'
|
45
|
+
maxcop = 0.05
|
46
|
+
end
|
47
|
+
stat, RR, dRR = calc_demag(5, Hk)
|
48
|
+
--printf("%g %g", RR, dRR)
|
49
|
+
until math.abs(dRR) < 1e-5
|
50
|
+
|
51
|
+
psi = {}
|
52
|
+
for k=1,3 do
|
53
|
+
psir, psii = flux_winding_wk(k)
|
54
|
+
psi[k] = {ksym*psir/a*m.arm_length, ksym*psii/a*m.arm_length}
|
55
|
+
end
|
56
|
+
|
57
|
+
fr, ft, tq, fx, fy = force_torque()
|
58
|
+
|
59
|
+
return psi, tq, RR
|
60
|
+
end
|
61
|
+
%if model.get('fc_radius', 0):
|
62
|
+
if m.fc_radius == nil then
|
63
|
+
m.fc_radius = ${model['fc_radius']*1e3}
|
64
|
+
end
|
65
|
+
%endif
|
66
|
+
%if type(model.get('curvec')[0]) is list:
|
67
|
+
curvec = {${','.join(['{'+','.join([str(x) for x in y])+'}' for y in model['curvec']])}} -- A
|
68
|
+
%else:
|
69
|
+
curvec = {{${','.join([str(x) for x in model['curvec']])}}} -- A
|
70
|
+
%endif
|
71
|
+
a=${model.get('num_par_wdgs', 1)} -- parallel branches
|
72
|
+
|
73
|
+
ksym = m.num_poles/m.npols_gen
|
74
|
+
|
75
|
+
if num_agnodes ~= nil then
|
76
|
+
dphi = 360/num_agnodes -- ndst[2] -- deg
|
77
|
+
else
|
78
|
+
post_models("nodedistance", "ndst" )
|
79
|
+
dphi = ndst[2] -- deg
|
80
|
+
end
|
81
|
+
nodes = math.floor(360/m.num_poles/dphi+0.5)
|
82
|
+
printf("Nodes in airgap total %g, Nodes per pole: %d", 360/dphi, nodes)
|
83
|
+
-- find a valid number of steps for a rotation:
|
84
|
+
nrot = nodes
|
85
|
+
while( nrot%2) == 0 do
|
86
|
+
nrot = nrot//2
|
87
|
+
end
|
88
|
+
-- HcB = Brem*tempcoefbr*(magn_temp-20)+1)/muerel/12.565e-7
|
89
|
+
-- Hcmin = HcJ*tempcoefhc*(magn_temp-20.0)+1)/HcB*1e2 -- limit of demagnetization in
|
90
|
+
Hk = ${model.get('Hk', -999)}
|
91
|
+
Q1 = get_dev_data("num_slots")
|
92
|
+
p = m.num_poles//2
|
93
|
+
dphi = 360//gcd(Q1, p)/nrot
|
94
|
+
print(string.format(" rotation steps: %d current steps: %d\n", nrot, #curvec))
|
95
|
+
|
96
|
+
phi = 0
|
97
|
+
-- initialize rotate
|
98
|
+
rotate({
|
99
|
+
airgap = m.fc_radius, -- air gap radius
|
100
|
+
region = "inside", -- region to rotate
|
101
|
+
mode = "save" -- save initial model state
|
102
|
+
})
|
103
|
+
|
104
|
+
for i=1, #curvec do
|
105
|
+
print(string.format(" current: %d/%d %g, %g, %g\n",
|
106
|
+
i, #curvec, curvec[i][1], curvec[i][2], curvec[i][3]))
|
107
|
+
|
108
|
+
file_psi = io.open("psi-torq-rem-rot-"..i..".dat","w")
|
109
|
+
for n=1,nrot+1 do
|
110
|
+
psi, tq, rr = calc_flux_torq_rem(phi, curvec[i])
|
111
|
+
file_psi:write(string.format("%g ", phi))
|
112
|
+
for k=1, 3 do
|
113
|
+
file_psi:write(string.format("%g ", curvec[i][k]))
|
114
|
+
end
|
115
|
+
for k=1, 3 do
|
116
|
+
file_psi:write(string.format("%g ", psi[k][1]))
|
117
|
+
end
|
118
|
+
file_psi:write(string.format("%g ", tq))
|
119
|
+
file_psi:write(string.format("%g ", rr))
|
120
|
+
file_psi:write("\n")
|
121
|
+
|
122
|
+
phi = n*dphi
|
123
|
+
rotate({angle=phi, mode="absolute"})
|
124
|
+
end
|
125
|
+
file_psi:close()
|
126
|
+
rotate({mode = "reset"}) -- restore the initial state (discard any changes)
|
127
|
+
end
|
femagtools/utils.py
CHANGED
femagtools/zmq.py
CHANGED
@@ -80,6 +80,9 @@ class SubscriberTask(threading.Thread):
|
|
80
80
|
notify = None
|
81
81
|
notify_send_header = set()
|
82
82
|
notify_send_data = dict()
|
83
|
+
# progress, xydata and this entries of this list
|
84
|
+
# will send only only once per timestep
|
85
|
+
simple_data = ['license']
|
83
86
|
|
84
87
|
def __init__(self, **kwargs):
|
85
88
|
threading.Thread.__init__(self)
|
@@ -92,7 +95,8 @@ class SubscriberTask(threading.Thread):
|
|
92
95
|
self.header = kwargs.get('header')
|
93
96
|
self.num_cur_steps = kwargs.get('num_cur_steps', None)
|
94
97
|
SubscriberTask.curve_label = kwargs.get('curve_label', '')
|
95
|
-
SubscriberTask.timestep = kwargs.get('timestep',
|
98
|
+
SubscriberTask.timestep = kwargs.get('timestep', 2)
|
99
|
+
|
96
100
|
if not self.host:
|
97
101
|
self.host = 'localhost'
|
98
102
|
if not self.header:
|
@@ -145,7 +149,7 @@ class SubscriberTask(threading.Thread):
|
|
145
149
|
SubscriberTask.percent_list = []
|
146
150
|
|
147
151
|
def send_notify():
|
148
|
-
logger.debug(f"Send loop: {SubscriberTask.notify_send_loop}")
|
152
|
+
logger.debug(f"Send loop: {SubscriberTask.notify_send_loop}, timestep: {SubscriberTask.timestep}")
|
149
153
|
while SubscriberTask.notify_send_loop:
|
150
154
|
if 'progress_logger' in SubscriberTask.notify_send_header:
|
151
155
|
# collect data from different threads
|
@@ -153,13 +157,20 @@ class SubscriberTask(threading.Thread):
|
|
153
157
|
numTot = len(SubscriberTask.percent_list)
|
154
158
|
d = json.loads(SubscriberTask.notify_send_data.get('progress_logger')[1])
|
155
159
|
d['percent'] = sum(SubscriberTask.percent_list) / numTot
|
156
|
-
d['subtitle'] = f"{SubscriberTask.percent_list.count(100)} of {numTot}"
|
160
|
+
d['subtitle'] = f"{SubscriberTask.percent_list.count(100)} of {numTot}" if numTot > 1 else ''
|
157
161
|
SubscriberTask.notify(['progress_logger', json.dumps(d)])
|
158
162
|
if 'xyplot' in SubscriberTask.notify_send_header:
|
159
163
|
SubscriberTask.notify([s.decode('latin1')
|
160
164
|
for s in SubscriberTask.notify_send_data.get('xyplot')])
|
161
165
|
SubscriberTask.notify_send_header.remove('xyplot')
|
162
166
|
|
167
|
+
# simple
|
168
|
+
for sdata in SubscriberTask.simple_data:
|
169
|
+
if sdata in SubscriberTask.notify_send_header:
|
170
|
+
SubscriberTask.notify([s.decode('latin1')
|
171
|
+
for s in SubscriberTask.notify_send_data.get(sdata)])
|
172
|
+
SubscriberTask.notify_send_header.remove(sdata)
|
173
|
+
|
163
174
|
time.sleep(abs(SubscriberTask.timestep))
|
164
175
|
logger.debug(f"Send Finished loop: {SubscriberTask.notify_send_loop}")
|
165
176
|
|
@@ -196,7 +207,14 @@ class SubscriberTask(threading.Thread):
|
|
196
207
|
SubscriberTask.notify_send_header.add('xyplot')
|
197
208
|
continue
|
198
209
|
|
199
|
-
|
210
|
+
# simple
|
211
|
+
for sdata in SubscriberTask.simple_data:
|
212
|
+
if response[0] == sdata.encode() and sdata.encode() in self.header:
|
213
|
+
SubscriberTask.notify_send_header.add(sdata)
|
214
|
+
SubscriberTask.notify_send_data[sdata] = response
|
215
|
+
continue
|
216
|
+
|
217
|
+
if response[0] not in self.header:
|
200
218
|
self.notify([s.decode('latin1') for s in response])
|
201
219
|
|
202
220
|
except Exception:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: femagtools
|
3
|
-
Version: 1.8.
|
3
|
+
Version: 1.8.7
|
4
4
|
Summary: Python API for FEMAG
|
5
5
|
Author-email: Ronald Tanner <tar@semafor.ch>, Dapu Zhang <dzhang@gtisoft.com>, Beat Holm <hob@semafor.ch>, Günther Amsler <amg@semafor.ch>, Nicolas Mauchle <mau@semafor.ch>
|
6
6
|
License: Copyright (c) 2016-2023, Semafor Informatik & Energie AG, Basel
|