femagtools 1.8.5__py3-none-any.whl → 1.8.7__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -49,7 +49,9 @@ def fft(pos, y, pmod=0):
49
49
  nmax = min(9*npoles, N//2)
50
50
 
51
51
  alfa0 = np.angle(Y[i])
52
+ alfa = np.angle(Y[:nmax])
52
53
 
53
54
  return {'a': a, 'a0': a0, 'T0': T0, 'alfa0': alfa0,
55
+ 'alfa': alfa,
54
56
  'nue': (2*np.abs(Y[:nmax])/N).tolist(),
55
57
  'yi': yx.tolist()}
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', 1)
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
- if response[0] in self.header or b'' in self.header:
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.5
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