femagtools 1.5.7__py3-none-any.whl → 1.6.0__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 +14 -1
- femagtools/dxfsl/area.py +2 -3
- femagtools/dxfsl/converter.py +10 -2
- femagtools/dxfsl/fslrenderer.py +1 -2
- femagtools/dxfsl/functions.py +24 -27
- femagtools/dxfsl/geom.py +116 -80
- femagtools/dxfsl/machine.py +16 -7
- femagtools/dxfsl/plotrenderer.py +2 -2
- femagtools/dxfsl/shape.py +68 -17
- femagtools/femag.py +21 -3
- femagtools/fsl.py +14 -2
- femagtools/machine/__init__.py +13 -33
- femagtools/machine/afpm.py +22 -21
- femagtools/machine/pm.py +22 -21
- femagtools/machine/utils.py +112 -58
- femagtools/mcv.py +27 -1
- femagtools/model.py +4 -2
- femagtools/nc.py +7 -0
- femagtools/opt.py +1 -1
- femagtools/parstudy.py +5 -2
- femagtools/plot/__init__.py +1 -0
- femagtools/plot/bch.py +2 -0
- femagtools/plot/fieldlines.py +37 -0
- femagtools/templates/basic_modpar.mako +8 -0
- femagtools/templates/bertotti.mako +40 -0
- femagtools/templates/modified_steinmetz.mako +39 -0
- femagtools/ts.py +1 -1
- femagtools/utils.py +7 -1
- {femagtools-1.5.7.dist-info → femagtools-1.6.0.dist-info}/METADATA +1 -1
- {femagtools-1.5.7.dist-info → femagtools-1.6.0.dist-info}/RECORD +38 -35
- tests/test_bchreader.py +12 -1
- tests/test_femag.py +1 -1
- tests/test_fsl.py +1 -1
- {femagtools-1.5.7.dist-info → femagtools-1.6.0.dist-info}/LICENSE +0 -0
- {femagtools-1.5.7.dist-info → femagtools-1.6.0.dist-info}/WHEEL +0 -0
- {femagtools-1.5.7.dist-info → femagtools-1.6.0.dist-info}/entry_points.txt +0 -0
- {femagtools-1.5.7.dist-info → femagtools-1.6.0.dist-info}/top_level.txt +0 -0
femagtools/machine/utils.py
CHANGED
@@ -303,11 +303,12 @@ def dqpar_interpol(xfit, dqpars, ipkey='temperature'):
|
|
303
303
|
else:
|
304
304
|
fpip[k] = ip.interp1d(
|
305
305
|
x, m, fill_value='extrapolate')(xfit).T
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
306
|
+
|
307
|
+
for k in ('styoke_hyst', 'stteeth_hyst',
|
308
|
+
'styoke_eddy', 'stteeth_eddy',
|
309
|
+
'rotor_hyst', 'rotor_eddy',
|
310
|
+
'magnet'):
|
311
|
+
try:
|
311
312
|
m = np.array([f['losses'][k] for f in sorted_dqpars]).T
|
312
313
|
if len(x) > 2:
|
313
314
|
fpip['losses'][k] = np.array(
|
@@ -316,12 +317,13 @@ def dqpar_interpol(xfit, dqpars, ipkey='temperature'):
|
|
316
317
|
else:
|
317
318
|
fpip['losses'][k] = ip.interp1d(
|
318
319
|
x, m, fill_value='extrapolate')(xfit).T
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
320
|
+
except KeyError:
|
321
|
+
pass
|
322
|
+
|
323
|
+
fpip['losses']['speed'] = dqpars[0]['losses']['speed']
|
324
|
+
for f in ('hf', 'ef'):
|
325
|
+
if f in dqpars[0]['losses']:
|
326
|
+
fpip['losses'][f] = dqpars[0]['losses'][f]
|
325
327
|
return x, fpip
|
326
328
|
|
327
329
|
|
@@ -351,52 +353,70 @@ def dqparident(workdir, engine, temp, machine,
|
|
351
353
|
import pathlib
|
352
354
|
|
353
355
|
wdgk = 'windings' if 'windings' in machine else 'winding'
|
354
|
-
|
355
|
-
|
356
|
+
def create_wdg(machine):
|
357
|
+
wpar = {'Q': machine['stator']['num_slots'],
|
358
|
+
'm': machine[wdgk]['num_phases'],
|
359
|
+
'p': machine['poles']//2}
|
360
|
+
|
361
|
+
if 'coil_span' in machine[wdgk]:
|
362
|
+
wpar['yd'] = machine[wdgk]['coil_span']
|
363
|
+
if 'num_layers' in machine[wdgk]:
|
364
|
+
wpar['l'] = machine[wdgk]['num_layers']
|
365
|
+
|
366
|
+
return windings.Winding(wpar)
|
367
|
+
try:
|
368
|
+
defspeed = 160/machine['poles']
|
369
|
+
except KeyError:
|
370
|
+
if kwargs.get('speed', 0) == 0:
|
371
|
+
raise ValueError('rated speed missing')
|
372
|
+
defspeed = kwargs['speed']
|
373
|
+
|
356
374
|
lfe = machine['lfe']
|
357
375
|
g = machine[wdgk].get('num_par_wdgs', 1)
|
358
|
-
slotmodel = [k for k in machine['stator'] if isinstance(
|
359
|
-
machine['stator'][k], dict)][-1]
|
360
|
-
if slotmodel == 'stator1':
|
361
|
-
hs = machine['stator']['stator1']['slot_rf1'] - \
|
362
|
-
machine['stator']['stator1']['tip_rh1']
|
363
|
-
else:
|
364
|
-
dy1 = machine['outer_diam']
|
365
|
-
hs = machine['stator'][slotmodel].get(
|
366
|
-
'slot_height', 0.6*(dy1-da1)/2)
|
367
|
-
|
368
376
|
N = machine[wdgk]['num_wires']
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
377
|
+
if 'cufilfact' in machine[wdgk]:
|
378
|
+
fcu = machine[wdgk]['cufilfact']
|
379
|
+
elif 'fillfac' in machine[wdgk]:
|
380
|
+
fcu = machine[wdgk]['fillfac']
|
381
|
+
else:
|
382
|
+
fcu = 0.42
|
383
|
+
|
384
|
+
try: # calc basic dimensions if not fsl or dxf model
|
385
|
+
Q1 = machine['stator']['num_slots']
|
386
|
+
da1 = machine['bore_diam']
|
387
|
+
slotmodel = [k for k in machine['stator'] if isinstance(
|
388
|
+
machine['stator'][k], dict)][-1]
|
389
|
+
if slotmodel == 'stator1':
|
390
|
+
hs = machine['stator']['stator1']['slot_rf1'] - \
|
391
|
+
machine['stator']['stator1']['tip_rh1']
|
392
|
+
else:
|
393
|
+
dy1 = machine['outer_diam']
|
394
|
+
hs = machine['stator'][slotmodel].get(
|
395
|
+
'slot_height', 0.6*(dy1-da1)/2)
|
373
396
|
|
374
|
-
|
375
|
-
|
376
|
-
|
397
|
+
Jmax = 30e6 # max current density in A/m2
|
398
|
+
Acu = fcu*0.5*np.pi*(da1+hs)*hs
|
399
|
+
i1_max = round(Acu/Q1/N*Jmax/10)*10
|
400
|
+
except KeyError:
|
401
|
+
if kwargs.get('i1_max', 0) == 0:
|
402
|
+
raise ValueError('i1_max missing')
|
403
|
+
i1_max = kwargs['i1_max']
|
377
404
|
|
378
405
|
# winding resistance
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
aw = np.pi*machine[wdgk].get('dia_wire', 1e-3)**2/4
|
394
|
-
elif ('wire_width' in machine[wdgk]) and ('wire_height' in machine[wdgk]):
|
395
|
-
aw = machine['windings']['wire_width']*machine[wdgk]['wire_height']
|
396
|
-
else: # wire diameter from slot area
|
397
|
-
aw = 0.75 * machine[wdgk].get('cufilfact', 0.45)*\
|
398
|
-
np.pi*da1*hs/Q1/wpar['l']/N
|
399
|
-
r1 = wdg_resistance(wdg, N, g, aw, da1, hs, lfe)
|
406
|
+
try:
|
407
|
+
wdg = create_wdg(machine)
|
408
|
+
|
409
|
+
if 'wire_gauge' in machine[wdgk]:
|
410
|
+
aw = machine[wdgk]['wire_gauge']
|
411
|
+
elif 'dia_wire' in machine[wdgk]:
|
412
|
+
aw = np.pi*machine[wdgk].get('dia_wire', 1e-3)**2/4
|
413
|
+
elif ('wire_width' in machine[wdgk]) and ('wire_height' in machine[wdgk]):
|
414
|
+
aw = machine['windings']['wire_width']*machine[wdgk]['wire_height']
|
415
|
+
else: # wire diameter from slot area
|
416
|
+
aw = 0.75 * fcu * np.pi*da1*hs/Q1/wdg.l/N
|
417
|
+
r1 = wdg_resistance(wdg, N, g, aw, da1, hs, lfe)
|
418
|
+
except (NameError, KeyError):
|
419
|
+
r1 = 0 # cannot calc winding resistance
|
400
420
|
|
401
421
|
n = len(temp)
|
402
422
|
parvardef = {
|
@@ -415,6 +435,10 @@ def dqparident(workdir, engine, temp, machine,
|
|
415
435
|
leakfile = pathlib.Path(workdir) / 'end_wind_leak.dat'
|
416
436
|
leakfile.unlink(missing_ok=True)
|
417
437
|
|
438
|
+
period_frac = kwargs.get('period_frac', 6)
|
439
|
+
if machine.get('external_rotor', False):
|
440
|
+
period_frac = 1 # TODO: missing femag support
|
441
|
+
|
418
442
|
simulation = dict(
|
419
443
|
calculationMode='ld_lq_fast',
|
420
444
|
i1_max=kwargs.get('i1_max', i1_max),
|
@@ -426,12 +450,15 @@ def dqparident(workdir, engine, temp, machine,
|
|
426
450
|
num_par_wdgs=machine[wdgk].get('num_par_wdgs', 1),
|
427
451
|
num_cur_steps=kwargs.get('num_cur_steps', 5),
|
428
452
|
num_beta_steps=kwargs.get('num_beta_steps', 7),
|
429
|
-
speed=kwargs.get('speed',
|
453
|
+
speed=kwargs.get('speed', defspeed),
|
430
454
|
period_frac=period_frac)
|
431
455
|
|
432
456
|
# TODO: cleanup() # remove previously created files in workdir
|
433
457
|
# start calculation
|
434
458
|
results = parvar(parvardef, machine, simulation, engine)
|
459
|
+
if 'poles' not in machine:
|
460
|
+
machine['poles'] = 2*results['f'][0]['machine']['p']
|
461
|
+
da1 = 2*results['f'][0]['machine']['fc_radius']
|
435
462
|
#import json
|
436
463
|
#with open('results.json', 'w') as fp:
|
437
464
|
# json.dump(results, fp)
|
@@ -457,14 +484,25 @@ def dqparident(workdir, engine, temp, machine,
|
|
457
484
|
np.array(results['f'][i]['ldq'][k]))).tolist()
|
458
485
|
for k in ('psid', 'psiq', 'torque', 'ld', 'lq', 'psim')})
|
459
486
|
ldq.append(d)
|
460
|
-
|
461
|
-
|
462
|
-
|
487
|
+
# collect existing losses only
|
488
|
+
losskeymap = {'magnet': 'magnet'}
|
489
|
+
losskeys = [k for k in results['f'][0]['ldq']['losses']
|
490
|
+
if len(k.split('_')) > 1] + ['magnet']
|
491
|
+
for k in losskeys:
|
492
|
+
if k.find('_') > -1:
|
493
|
+
pref, post = k.split('_')
|
494
|
+
if pref.lower() == 'stza':
|
495
|
+
losskeymap[k] = 'stteeth_'+post
|
496
|
+
elif pref.lower() == 'stjo':
|
497
|
+
losskeymap[k] = 'styoke_'+post
|
498
|
+
else:
|
499
|
+
losskeymap[k] = k
|
463
500
|
for i in range(0, len(results['f']), 2):
|
464
501
|
j = i//2
|
465
502
|
ldq[j]['temperature'] = results['x'][0][i]
|
466
|
-
ldq[j]['losses'] = {k: np.vstack(
|
467
|
-
|
503
|
+
ldq[j]['losses'] = {losskeymap[k]: np.vstack(
|
504
|
+
(np.array(results['f'][i+1]['ldq']['losses'][k])[:-1, :],
|
505
|
+
np.array(results['f'][i]['ldq']['losses'][k]))).tolist()
|
468
506
|
for k in losskeys}
|
469
507
|
ldq[j]['losses']['speed'] = results['f'][i]['ldq']['losses']['speed']
|
470
508
|
for k in ('hf', 'ef'):
|
@@ -473,10 +511,26 @@ def dqparident(workdir, engine, temp, machine,
|
|
473
511
|
dqpars = {
|
474
512
|
'm': machine[wdgk]['num_phases'],
|
475
513
|
'p': machine['poles']//2,
|
476
|
-
'r1': machine[wdgk].get('resistance', r1),
|
477
514
|
'ls1': ls1,
|
478
515
|
"rotor_mass": rotor_mass, "kfric_b": 1,
|
479
516
|
'ldq': ldq}
|
517
|
+
if 'resistance' in machine[wdgk]:
|
518
|
+
dqpars['r1'] = machine[wdgk]['resistance']
|
519
|
+
else:
|
520
|
+
if r1:
|
521
|
+
dqpars['r1'] = r1
|
522
|
+
else:
|
523
|
+
from .. import nc
|
524
|
+
model = nc.read(str(pathlib.Path(workdir) / machine['name']))
|
525
|
+
Q1 = model.num_slots
|
526
|
+
#machine['stator']['num_slots'] = Q1
|
527
|
+
wdg = create_wdg(machine)
|
528
|
+
istat = 0 if model.get_areas()[0]['slots'] else 1
|
529
|
+
asl = model.get_areas()[istat]['slots']
|
530
|
+
# diameter of wires
|
531
|
+
aw = fcu*asl/Q1/wdg.l/N
|
532
|
+
hs = asl/(np.pi*da1/3)
|
533
|
+
dqpars['r1'] = wdg_resistance(wdg, N, g, aw, da1, hs, lfe)
|
480
534
|
|
481
535
|
if 'current_angles' in results['f'][0]:
|
482
536
|
dqpars['current_angles'] = results['f'][0]['current_angles']
|
femagtools/mcv.py
CHANGED
@@ -50,6 +50,7 @@ transl = dict(
|
|
50
50
|
rho='mc1_fe_spez_weigth',
|
51
51
|
ch='mc1_ch_factor',
|
52
52
|
cw='mc1_cw_factor',
|
53
|
+
ce='mc1_ce_factor',
|
53
54
|
ch_freq='mc1_ch_freq_factor',
|
54
55
|
cw_freq='mc1_cw_freq_factor',
|
55
56
|
fillfac='mc1_fillfac',
|
@@ -58,6 +59,7 @@ transl = dict(
|
|
58
59
|
bsat='mc1_bsat',
|
59
60
|
Bo='mc1_base_induction',
|
60
61
|
b_coeff='mc1_induction_factor',
|
62
|
+
b_beta_coeff='mc1_induction_beta_factor',
|
61
63
|
fo='mc1_base_frequency',
|
62
64
|
curve=[dict(
|
63
65
|
hi='mc1_hi',
|
@@ -227,9 +229,12 @@ class Mcv(object):
|
|
227
229
|
self.MC1_BASE_INDUCTION = 1.5
|
228
230
|
self.MC1_CH_FACTOR = 0.0
|
229
231
|
self.MC1_CW_FACTOR = 0.0
|
232
|
+
self.MC1_CE_FACTOR = 0.0
|
230
233
|
self.MC1_CH_FREQ_FACTOR = 0.0
|
231
234
|
self.MC1_CW_FREQ_FACTOR = 0.0
|
232
235
|
self.MC1_INDUCTION_FACTOR = 0.0
|
236
|
+
self.MC1_INDUCTION_BETA_FACTOR = 0.0
|
237
|
+
|
233
238
|
self.MC1_FE_SPEZ_WEIGTH = 7.65
|
234
239
|
self.MC1_FE_SAT_MAGNETIZATION = 2.15
|
235
240
|
|
@@ -237,9 +242,11 @@ class Mcv(object):
|
|
237
242
|
self.mc1_base_induction = self.MC1_BASE_INDUCTION
|
238
243
|
self.mc1_ch_factor = self.MC1_CH_FACTOR
|
239
244
|
self.mc1_cw_factor = self.MC1_CW_FACTOR
|
245
|
+
self.mc1_ce_factor = self.MC1_CE_FACTOR
|
240
246
|
self.mc1_ch_freq_factor = self.MC1_CH_FREQ_FACTOR
|
241
247
|
self.mc1_cw_freq_factor = self.MC1_CW_FREQ_FACTOR
|
242
248
|
self.mc1_induction_factor = self.MC1_INDUCTION_FACTOR
|
249
|
+
self.mc1_induction_beta_factor = self.MC1_INDUCTION_BETA_FACTOR
|
243
250
|
self.mc1_fe_spez_weigth = self.MC1_FE_SPEZ_WEIGTH
|
244
251
|
self.mc1_fe_sat_magnetization = self.MC1_FE_SAT_MAGNETIZATION
|
245
252
|
|
@@ -519,6 +526,13 @@ class Writer(Mcv):
|
|
519
526
|
float(self.mc1_fe_sat_magnetization)])
|
520
527
|
|
521
528
|
if not hasattr(self, 'losses') or not self.losses:
|
529
|
+
# new variables: ce factor for bertotti losses
|
530
|
+
# b_beta_coeff for modified steinmetz
|
531
|
+
try:
|
532
|
+
self.writeBlock([float(self.mc1_ce_factor),
|
533
|
+
float(self.mc1_induction_beta_factor)])
|
534
|
+
except:
|
535
|
+
pass
|
522
536
|
return
|
523
537
|
|
524
538
|
try:
|
@@ -585,7 +599,7 @@ class Writer(Mcv):
|
|
585
599
|
logger.info('Losses n freq %d n ind %d', nfreq, nind)
|
586
600
|
except Exception as e:
|
587
601
|
logger.error("Exception %s", e, exc_info=True)
|
588
|
-
|
602
|
+
|
589
603
|
def writeMcv(self, filename, fillfac=None, recsin=''):
|
590
604
|
# windows needs this strip to remove '\r'
|
591
605
|
filename = filename.strip()
|
@@ -857,6 +871,16 @@ class Reader(Mcv):
|
|
857
871
|
if not self.losses['f'] or not self.losses['pfe']:
|
858
872
|
self.losses = {}
|
859
873
|
|
874
|
+
self.ce = 0
|
875
|
+
self.b_beta_coeff = 0
|
876
|
+
try:
|
877
|
+
if not self.losses['f'][0]:
|
878
|
+
self.fp.seek(-16, 1)
|
879
|
+
res = self.readBlock([float]*2)
|
880
|
+
self.ce, self.b_beta_coeff = res[0], res[1]
|
881
|
+
except:
|
882
|
+
pass
|
883
|
+
|
860
884
|
def get_results(self):
|
861
885
|
result = {
|
862
886
|
'name': self.name,
|
@@ -873,6 +897,8 @@ class Reader(Mcv):
|
|
873
897
|
'ch': self.ch,
|
874
898
|
'ch_freq': self.ch_freq,
|
875
899
|
'cw': self.cw,
|
900
|
+
'ce': self.ce,
|
901
|
+
'b_beta_coeff': self.b_beta_coeff,
|
876
902
|
'cw_freq': self.cw_freq,
|
877
903
|
'b_coeff': self.b_coeff,
|
878
904
|
'rho': self.rho,
|
femagtools/model.py
CHANGED
@@ -379,6 +379,7 @@ class MachineModel(Model):
|
|
379
379
|
def rotortype(self):
|
380
380
|
"""return type of rotor slot"""
|
381
381
|
for k in self.rotor:
|
382
|
+
# anything that is a dict must represent the type
|
382
383
|
if isinstance(self.rotor[k], dict):
|
383
384
|
return k
|
384
385
|
raise AttributeError("Missing rotor model in {}".format(self.magnet))
|
@@ -386,8 +387,9 @@ class MachineModel(Model):
|
|
386
387
|
def magnettype(self):
|
387
388
|
"""return type of magnet slot"""
|
388
389
|
for k in self.magnet:
|
389
|
-
if k
|
390
|
-
|
390
|
+
if k not in {'material', 'temp_prop'}:
|
391
|
+
if isinstance(self.magnet[k], dict):
|
392
|
+
return k
|
391
393
|
raise AttributeError("Missing magnet model in {}".format(self.magnet))
|
392
394
|
|
393
395
|
def is_complete(self):
|
femagtools/nc.py
CHANGED
@@ -262,6 +262,7 @@ class Reader(object):
|
|
262
262
|
ce_ind_exp = 1.5
|
263
263
|
ke = 0.0
|
264
264
|
khml = 0.65
|
265
|
+
cw_ind_beta_exp = 0
|
265
266
|
|
266
267
|
lctype = ['magn_curve']*lmc + ['Outside', 'Inside']
|
267
268
|
for i in range(lmc+2):
|
@@ -280,6 +281,11 @@ class Reader(object):
|
|
280
281
|
cw = float(mcgrp.variables['cw'][i].data)
|
281
282
|
cw_freq_exp = float(mcgrp.variables['cw_exp'][i].data)
|
282
283
|
cw_ind_exp = float(mcgrp.variables['ind_exp'][i].data)
|
284
|
+
try:
|
285
|
+
ce = float(mcgrp.variables['ce'][i].data)
|
286
|
+
cw_ind_beta_exp = float(mcgrp.variables['ind_beta_exp'][i].data)
|
287
|
+
except:
|
288
|
+
pass
|
283
289
|
spec_weight = float(mcgrp.variables['spec_weight'][i].data)
|
284
290
|
fillfactor = float(mcgrp.variables['fillfac'][i].data)
|
285
291
|
shapefactor = 1.0
|
@@ -327,6 +333,7 @@ class Reader(object):
|
|
327
333
|
"cw_freq_exp": cw_freq_exp,
|
328
334
|
"cw_ind_exp": cw_ind_exp,
|
329
335
|
"ce": ce,
|
336
|
+
"cw_ind_beta_exp": cw_ind_beta_exp,
|
330
337
|
"ce_freq_exp": ce_freq_exp,
|
331
338
|
"ce_ind_exp": ce_ind_exp,
|
332
339
|
"ke": ke,
|
femagtools/opt.py
CHANGED
@@ -82,7 +82,7 @@ class Optimizer(object):
|
|
82
82
|
task.add_file(self.fea['pocfilename'],
|
83
83
|
self.fea['poc'].content())
|
84
84
|
if 'stateofproblem' in self.fea:
|
85
|
-
task.set_stateofproblem(fea['stateofproblem'])
|
85
|
+
task.set_stateofproblem(self.fea['stateofproblem'])
|
86
86
|
tstart = time.time()
|
87
87
|
ntasks = engine.submit()
|
88
88
|
status = engine.join()
|
femagtools/parstudy.py
CHANGED
@@ -181,10 +181,13 @@ class ParameterStudy(object):
|
|
181
181
|
simulation['lfe'] = model.lfe
|
182
182
|
simulation['move_action'] = model.move_action
|
183
183
|
simulation['phi_start'] = 0.0
|
184
|
-
|
184
|
+
try:
|
185
|
+
simulation['range_phi'] = 720/model.get('poles')
|
186
|
+
except AttributeError: # if dxf or pure fsl model
|
187
|
+
simulation['range_phi'] = 0.0
|
185
188
|
simulation.update(model.winding)
|
186
189
|
if 'pocfilename' not in simulation:
|
187
|
-
simulation['pocfilename'] = f"{model.name}
|
190
|
+
simulation['pocfilename'] = f"{model.name}.poc"
|
188
191
|
fea = femagtools.model.FeaModel(simulation)
|
189
192
|
|
190
193
|
prob = femagtools.moproblem.FemagMoProblem(decision_vars,
|
femagtools/plot/__init__.py
CHANGED
femagtools/plot/bch.py
CHANGED
@@ -813,6 +813,7 @@ def main():
|
|
813
813
|
ext = args.filename.split('.')[-1].upper()
|
814
814
|
if ext.startswith('MC'):
|
815
815
|
import femagtools.mcv
|
816
|
+
from femagtools.plot.mcv import mcv_hbj, mcv_muer
|
816
817
|
mcv = femagtools.mcv.read(sys.argv[1])
|
817
818
|
|
818
819
|
if mcv['mc1_type'] in (femagtools.mcv.MAGCRV, femagtools.mcv.ORIENT_CRV):
|
@@ -836,6 +837,7 @@ def main():
|
|
836
837
|
|
837
838
|
if ext.startswith('PLT'):
|
838
839
|
import femagtools.forcedens
|
840
|
+
from femagtools.plot.forcedens import forcedens, forcedens_fft
|
839
841
|
fdens = femagtools.forcedens.read(args.filename)
|
840
842
|
cols = 1
|
841
843
|
rows = 2
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import re
|
2
|
+
import xml.etree.ElementTree as ET
|
3
|
+
import matplotlib.pyplot as plt
|
4
|
+
import numpy as np
|
5
|
+
import matplotlib.lines as mpl
|
6
|
+
|
7
|
+
rgbpat = re.compile('rgb\((\d+),(\d+),(\d+)\)')
|
8
|
+
|
9
|
+
def fieldlines(svgfilename, ax=0):
|
10
|
+
"""plot fieldlines from svg file"""
|
11
|
+
lines = []
|
12
|
+
cols = []
|
13
|
+
tree = ET.parse(svgfilename)
|
14
|
+
root = tree.getroot()
|
15
|
+
for line in root.findall('svg:line',
|
16
|
+
{'svg': 'http://www.w3.org/2000/svg'}):
|
17
|
+
lines.append(
|
18
|
+
((float(line.attrib['x1']), float(line.attrib['x2'])),
|
19
|
+
(float(line.attrib['y1']), float(line.attrib['y2']))))
|
20
|
+
cols.append(
|
21
|
+
[int(x)/256
|
22
|
+
for x in rgbpat.findall(line.attrib['stroke'])[0]])
|
23
|
+
a = np.array(lines)
|
24
|
+
xmax, xmin = np.max(a[:, 0]), np.min(a[:, 0])
|
25
|
+
ymax, ymin = np.max(a[:, 1]), np.min(a[:, 1])
|
26
|
+
|
27
|
+
if ax == 0:
|
28
|
+
ax = plt.gca()
|
29
|
+
ax.set_frame_on(False)
|
30
|
+
ax.set_aspect(1)
|
31
|
+
ax.set_xlim((xmin, xmax))
|
32
|
+
ax.set_ylim((ymax, ymin)) # upside down
|
33
|
+
ax.set_yticks([])
|
34
|
+
ax.set_xticks([])
|
35
|
+
|
36
|
+
for l, c in zip(lines, cols):
|
37
|
+
ax.add_line(mpl.Line2D(l[0], l[1], color=c))
|
@@ -88,6 +88,14 @@ m.pole_width = ${model['pole_width']*1e3}
|
|
88
88
|
% if hasattr(model, 'lfe'):
|
89
89
|
m.arm_length = ${model.get(['lfe'])*1e3}
|
90
90
|
% endif
|
91
|
+
% if hasattr(model, 'lfe'):
|
92
|
+
m.arm_length = ${model.get(['lfe'])*1e3}
|
93
|
+
% endif
|
94
|
+
% if hasattr(model, 'winding'):
|
95
|
+
% if 'num_par_wdgs' in model.winding:
|
96
|
+
m.num_par_wdgs = ${model.winding['num_par_wdgs']}
|
97
|
+
% endif
|
98
|
+
% endif
|
91
99
|
pre_models("basic_modpar")
|
92
100
|
% endif
|
93
101
|
% if hasattr(model, 'num_agnodes'):
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
function pfe(Bx,By,kh,fnu,km,z,d)
|
3
|
+
|
4
|
+
-- Bx flux density x component
|
5
|
+
-- By flux density y component
|
6
|
+
-- kh Hysterese Factor
|
7
|
+
-- fnu frequency
|
8
|
+
-- km material factor
|
9
|
+
-- ph return for hysterese losses
|
10
|
+
-- pw return for eddy current losses
|
11
|
+
-- iret status
|
12
|
+
|
13
|
+
-- Parameter
|
14
|
+
|
15
|
+
basfrq=${model['base_frequency']} -- Base Frequency for ch and cw [Hz]
|
16
|
+
basind=${model['base_induction']} -- Base Induction (Peak) [T]
|
17
|
+
ch=${model['ch']} -- Fe Hysteresis Coefficient ch [W/kg]
|
18
|
+
cw=${model['cw']} -- Fe Eddy Current Coefficient cw [W/kg]
|
19
|
+
ce=${model['ce']} -- Fe Excess Coefficient cw [W/kg]
|
20
|
+
hyscoef=${model['alpha']} -- Hysteresis Frequency Coefficient
|
21
|
+
spweight= ${model['special_weight']} -- Specific Weight Iron [gr/m3]
|
22
|
+
fillfact=${model['fillfac']} -- Fillfactor Iron <= 1
|
23
|
+
|
24
|
+
-- Bertotti Iron Loss Model
|
25
|
+
-- pvfe = ch*f*(b**alpha) + cw*f**2*(b**2) + ce*f**1.5*(b**1.5)
|
26
|
+
hxx = Bx/fillfact -- Bx
|
27
|
+
hyy = By/fillfact -- By
|
28
|
+
b21 = math.sqrt(hxx*hxx+hyy*hyy)
|
29
|
+
b = b21/basind
|
30
|
+
|
31
|
+
hi = fnu/basfrq
|
32
|
+
hcw = hi^2
|
33
|
+
hce = hi^1.5
|
34
|
+
|
35
|
+
ph = kh*spweight*km*ch*hi*(b^hyscoef) -- [W/m3]
|
36
|
+
pw = spweight*km*cw*hcw*(b^2) -- [W/m3]
|
37
|
+
pe = spweight*km*ce*hce*(b^1.5)
|
38
|
+
iret=1
|
39
|
+
return ph, pw, pe, iret
|
40
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
function pfe(Bx,By,kh,fnu,km,z,d)
|
3
|
+
|
4
|
+
-- Bx flux density x component
|
5
|
+
-- By flux density y component
|
6
|
+
-- kh Hysterese Factor
|
7
|
+
-- fnu frequency
|
8
|
+
-- km material factor
|
9
|
+
-- ph return for hysterese losses
|
10
|
+
-- pw return for eddy current losses
|
11
|
+
-- iret status
|
12
|
+
|
13
|
+
-- Parameter
|
14
|
+
|
15
|
+
basfrq=${model['base_frequency']} -- Base Frequency for ch and cw [Hz]
|
16
|
+
basind=${model['base_induction']} -- Base Induction (Peak) [T]
|
17
|
+
ch=${model['ch']} -- Fe Hysteresis Coefficient ch [W/kg]
|
18
|
+
cw=${model['cw']} -- Fe Eddy Current Coefficient cw [W/kg]
|
19
|
+
alpha=${model['alpha']} -- Hysteresis Frequency Coefficient Alpha
|
20
|
+
beta=${model['beta']} -- Hysteresis Frequency Coefficient Beta
|
21
|
+
spweight= ${model['special_weight']} -- Specific Weight Iron [gr/m3]
|
22
|
+
fillfact=${model['fillfac']} -- Fillfactor Iron <= 1
|
23
|
+
|
24
|
+
-- Modified Steinmetz Iron Loss Model
|
25
|
+
-- pvfe = ch*hi*(b**(alpha + beta*b)) + cw*(hi**2)*(b**2)
|
26
|
+
hxx = Bx/fillfact -- Bx
|
27
|
+
hyy = By/fillfact -- By
|
28
|
+
b21 = math.sqrt(hxx*hxx+hyy*hyy)
|
29
|
+
|
30
|
+
b = b21/basind
|
31
|
+
hi = fnu/basfrq
|
32
|
+
coeff = alpha+beta*b
|
33
|
+
ph = kh*spweight*km*ch*hi*(b^coeff) -- [W/m3]
|
34
|
+
pw = spweight*km*cw*(b^2)*(hi^2) -- [W/m3]
|
35
|
+
pe = 0.0
|
36
|
+
|
37
|
+
iret=1
|
38
|
+
return ph, pw, pe, iret
|
39
|
+
end
|
femagtools/ts.py
CHANGED
@@ -292,7 +292,7 @@ class Losses(object):
|
|
292
292
|
srname = srname+' '
|
293
293
|
|
294
294
|
time = self.times.vector[-1]-self.times.vector[0]
|
295
|
-
return
|
295
|
+
return self.ohm_lossenergy_sr(self.nc.get_subregion(srname)) / time
|
296
296
|
|
297
297
|
def ohm_lossenergy(self, start=0.0, end=0.0):
|
298
298
|
'''Ohmic loss energy of all subregions
|
femagtools/utils.py
CHANGED
@@ -15,7 +15,13 @@ def fft(pos, y, pmod=0):
|
|
15
15
|
if pmod:
|
16
16
|
negative_periodic = pmod % 2
|
17
17
|
else:
|
18
|
-
negative_periodic = np.abs(y[0] - y[-1])/np.max(y) > 1
|
18
|
+
#negative_periodic = np.abs(y[0] - y[-1])/np.max(y) > 1
|
19
|
+
# count zero crossings
|
20
|
+
ypos = np.asarray(y) > 0
|
21
|
+
nypos = ~ypos
|
22
|
+
nzc = len(((ypos[:-1] & nypos[1:])
|
23
|
+
| (nypos[:-1] & ypos[1:])).nonzero()[0])
|
24
|
+
negative_periodic = nzc == 0 or nzc % 2 == 1
|
19
25
|
|
20
26
|
if negative_periodic:
|
21
27
|
yx = np.concatenate(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: femagtools
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.6.0
|
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
|