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.
Files changed (38) hide show
  1. femagtools/__init__.py +1 -1
  2. femagtools/bch.py +14 -1
  3. femagtools/dxfsl/area.py +2 -3
  4. femagtools/dxfsl/converter.py +10 -2
  5. femagtools/dxfsl/fslrenderer.py +1 -2
  6. femagtools/dxfsl/functions.py +24 -27
  7. femagtools/dxfsl/geom.py +116 -80
  8. femagtools/dxfsl/machine.py +16 -7
  9. femagtools/dxfsl/plotrenderer.py +2 -2
  10. femagtools/dxfsl/shape.py +68 -17
  11. femagtools/femag.py +21 -3
  12. femagtools/fsl.py +14 -2
  13. femagtools/machine/__init__.py +13 -33
  14. femagtools/machine/afpm.py +22 -21
  15. femagtools/machine/pm.py +22 -21
  16. femagtools/machine/utils.py +112 -58
  17. femagtools/mcv.py +27 -1
  18. femagtools/model.py +4 -2
  19. femagtools/nc.py +7 -0
  20. femagtools/opt.py +1 -1
  21. femagtools/parstudy.py +5 -2
  22. femagtools/plot/__init__.py +1 -0
  23. femagtools/plot/bch.py +2 -0
  24. femagtools/plot/fieldlines.py +37 -0
  25. femagtools/templates/basic_modpar.mako +8 -0
  26. femagtools/templates/bertotti.mako +40 -0
  27. femagtools/templates/modified_steinmetz.mako +39 -0
  28. femagtools/ts.py +1 -1
  29. femagtools/utils.py +7 -1
  30. {femagtools-1.5.7.dist-info → femagtools-1.6.0.dist-info}/METADATA +1 -1
  31. {femagtools-1.5.7.dist-info → femagtools-1.6.0.dist-info}/RECORD +38 -35
  32. tests/test_bchreader.py +12 -1
  33. tests/test_femag.py +1 -1
  34. tests/test_fsl.py +1 -1
  35. {femagtools-1.5.7.dist-info → femagtools-1.6.0.dist-info}/LICENSE +0 -0
  36. {femagtools-1.5.7.dist-info → femagtools-1.6.0.dist-info}/WHEEL +0 -0
  37. {femagtools-1.5.7.dist-info → femagtools-1.6.0.dist-info}/entry_points.txt +0 -0
  38. {femagtools-1.5.7.dist-info → femagtools-1.6.0.dist-info}/top_level.txt +0 -0
@@ -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
- try:
307
- for k in ('styoke_hyst', 'stteeth_hyst',
308
- 'styoke_eddy', 'stteeth_eddy',
309
- 'rotor_hyst', 'rotor_eddy',
310
- 'magnet'):
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
- fpip['losses']['speed'] = dqpars[0]['losses']['speed']
320
- for f in ('hf', 'ef'):
321
- if f in dqpars[0]['losses']:
322
- fpip['losses'][f] = dqpars[0]['losses'][f]
323
- except KeyError:
324
- pass
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
- da1 = machine['bore_diam']
355
- Q1 = machine['stator']['num_slots']
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
- Jmax = 20e6 # max current density in A/m2
370
- f = machine[wdgk].get('fillfac', 0.42)
371
- Acu = f*0.5*np.pi*(da1+hs)*hs
372
- i1_max = round(Acu/Q1/N*Jmax/10)*10
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
- period_frac = kwargs.get('period_frac', 6)
375
- if machine.get('external_rotor', False):
376
- period_frac = 1 # TODO: missing femag support
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
- wpar = {'Q': machine['stator']['num_slots'],
380
- 'm': machine[wdgk]['num_phases'],
381
- 'p': machine['poles']//2}
382
-
383
- if 'coil_span' in machine[wdgk]:
384
- wpar['yd'] = machine[wdgk]['coil_span']
385
- if 'num_layers' in machine[wdgk]:
386
- wpar['l'] = machine[wdgk]['num_layers']
387
-
388
- wdg = windings.Winding(wpar)
389
-
390
- if 'wire_gauge' in machine[wdgk]:
391
- aw = machine[wdgk]['wire_gauge']
392
- elif 'dia_wire' in machine[wdgk]:
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', 160/machine['poles']),
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
- losskeys = ('styoke_hyst', 'stteeth_hyst', 'styoke_eddy',
461
- 'stteeth_eddy', 'rotor_hyst', 'rotor_eddy',
462
- 'magnet')
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((np.array(results['f'][i+1]['ldq']['losses'][k])[:-1, :],
467
- np.array(results['f'][i]['ldq']['losses'][k]))).tolist()
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 != 'material' and isinstance(self.magnet[k], dict):
390
- return k
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
- simulation['range_phi'] = 720/model.get('poles')
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}_{model.poles}p.poc"
190
+ simulation['pocfilename'] = f"{model.name}.poc"
188
191
  fea = femagtools.model.FeaModel(simulation)
189
192
 
190
193
  prob = femagtools.moproblem.FemagMoProblem(decision_vars,
@@ -17,3 +17,4 @@ from .nc import spel, mesh, demag, demag_pos, \
17
17
  from .mcv import mcv_hbj, mcv_muer, felosses
18
18
  from .phasor import i1beta_phasor, iqd_phasor, phasor
19
19
  from .wdg import mmf, mmf_fft, zoneplan, winding_factors, winding
20
+ from .fieldlines import fieldlines
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 srlossenergy / time
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.5.7
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