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.
@@ -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