femagtools 1.7.8__py3-none-any.whl → 1.8.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 (54) hide show
  1. femagtools/__init__.py +1 -1
  2. femagtools/amela.py +2 -2
  3. femagtools/dxfsl/area.py +142 -9
  4. femagtools/dxfsl/conv.py +2 -9
  5. femagtools/dxfsl/converter.py +33 -9
  6. femagtools/dxfsl/fslrenderer.py +13 -12
  7. femagtools/dxfsl/geom.py +39 -6
  8. femagtools/dxfsl/journal.py +2 -2
  9. femagtools/dxfsl/machine.py +14 -13
  10. femagtools/dxfsl/shape.py +3 -0
  11. femagtools/dxfsl/svgparser.py +31 -4
  12. femagtools/ecloss.py +381 -2
  13. femagtools/femag.py +55 -0
  14. femagtools/fsl.py +74 -47
  15. femagtools/isa7.py +41 -0
  16. femagtools/job.py +2 -2
  17. femagtools/machine/afpm.py +5 -1
  18. femagtools/machine/pm.py +1 -1
  19. femagtools/machine/sm.py +14 -0
  20. femagtools/machine/utils.py +4 -3
  21. femagtools/mcv.py +128 -124
  22. femagtools/me.py +13 -13
  23. femagtools/model.py +14 -1
  24. femagtools/moo/population.py +9 -7
  25. femagtools/nc.py +12 -0
  26. femagtools/plot/__init__.py +1 -1
  27. femagtools/plot/fieldlines.py +1 -1
  28. femagtools/plot/mcv.py +18 -0
  29. femagtools/plot/nc.py +22 -5
  30. femagtools/plot/wdg.py +40 -7
  31. femagtools/svgfsl/converter.py +6 -0
  32. femagtools/templates/gen_hairpin_winding.mako +36 -45
  33. femagtools/templates/gen_winding.mako +7 -0
  34. femagtools/templates/magnetIron.mako +30 -46
  35. femagtools/templates/magnetIron2.mako +39 -0
  36. femagtools/templates/magnetIron3.mako +39 -0
  37. femagtools/templates/magnetIron4.mako +39 -0
  38. femagtools/templates/magnetIron5.mako +39 -0
  39. femagtools/templates/magnetIronV.mako +34 -54
  40. femagtools/templates/magnetSector.mako +32 -47
  41. femagtools/templates/mesh-airgap.mako +12 -6
  42. femagtools/templates/prepare_thermal.mako +354 -0
  43. femagtools/templates/statorRotor3.mako +3 -22
  44. femagtools/windings.py +92 -59
  45. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/METADATA +20 -18
  46. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/RECORD +53 -53
  47. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/WHEEL +1 -1
  48. tests/test_fsl.py +1 -1
  49. tests/test_mcv.py +106 -1
  50. tests/test_windings.py +18 -2
  51. tests/test_mcvwriter.py +0 -96
  52. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/LICENSE +0 -0
  53. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/entry_points.txt +0 -0
  54. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/top_level.txt +0 -0
femagtools/fsl.py CHANGED
@@ -95,12 +95,12 @@ class Builder:
95
95
  if templ in ('statorFsl', 'dxf'):
96
96
  self.fsl_stator = True
97
97
 
98
- if templ != 'dxffile':
98
+ if templ not in ('dxffile', 'svgfile'):
99
99
  return
100
100
 
101
101
  from femagtools.dxfsl.converter import convert
102
102
  logger.info("Conv stator from %s",
103
- model.stator['dxffile']['name'])
103
+ model.stator[templ]['name'])
104
104
  params = {}
105
105
  params['split'] = model.stator[templ].get('split', False)
106
106
  params['show_plots'] = model.stator[templ].get('plot', False)
@@ -109,7 +109,7 @@ class Builder:
109
109
  params['nodedist'] = model.stator.get('nodedist', 1)
110
110
  pos = 'in' if model.external_rotor else 'out'
111
111
  params['part'] = ('stator', pos)
112
- conv = convert(model.stator['dxffile']['name'], **params)
112
+ conv = convert(model.stator[templ]['name'], **params)
113
113
 
114
114
  model.stator['num_slots'] = conv.get('tot_num_slot')
115
115
  model.set_value('poles', conv.get('num_poles'))
@@ -311,7 +311,7 @@ class Builder:
311
311
  if templ == 'dxf':
312
312
  # reuse dxfsl model
313
313
  self.fsl_rotor = True
314
- if templ != 'dxffile':
314
+ if templ not in ('dxffile', 'svgfile'):
315
315
  return
316
316
 
317
317
  from femagtools.dxfsl.converter import convert
@@ -356,23 +356,18 @@ class Builder:
356
356
  if 'thcond' in model.stator:
357
357
  fslcmds += [
358
358
  '-- thermal properties in airgap',
359
- 'if m.zeroangl ~= nil then',
360
- ' ag_cond = 0.063',
361
- ' thcap = 1007',
362
- ' beta = math.pi*m.npols_gen/m.num_poles + m.zeroangl/180*math.pi',
363
- ' xai, yai = pr2c((da1+da2)/4, beta)',
364
- ' def_mat_therm(xai,yai,"cyan",1.19,ag_cond,thcap,1)',
365
- ' xai, yai = pr2c((da1+da2)/4-ag/4, beta)',
366
- ' def_mat_therm(xai,yai,"cyan",1.19,ag_cond,thcap,1)',
367
- ' xai, yai = pr2c((da1+da2)/4+ag/4, beta)',
368
- ' def_mat_therm(xai,yai,"cyan",1.19,ag_cond,thcap,1)',
359
+ 'if m.zeroangl == nil then',
360
+ ' m.zeroangl = 0.0',
361
+ 'end',
362
+ 'beta = math.pi*m.npols_gen/m.num_poles + m.zeroangl/180*math.pi',
369
363
  '',
370
- ' state_of_problem("therm_static") -- thermic boundary conditions',
371
- ' x1,y1 = pd2c(dy2/2,m.zeroangl)',
372
- ' x2,y2 = pd2c(dy1/2,m.zeroangl)',
373
- ' beta = 360*m.npols_gen/m.num_poles',
374
- ' x3,y3 = pd2c(dy1/2,beta+m.zeroangl)',
375
- ' x4,y4 = pd2c(dy2/2,beta+m.zeroangl)',
364
+ 'state_of_problem("therm_static") -- thermic boundary conditions',
365
+ 'x1,y1 = pd2c(dy2/2,m.zeroangl)',
366
+ 'x2,y2 = pd2c(dy1/2,m.zeroangl)',
367
+ 'beta = 360*m.npols_gen/m.num_poles',
368
+ 'x3,y3 = pd2c(dy1/2,beta+m.zeroangl)',
369
+ 'x4,y4 = pd2c(dy2/2,beta+m.zeroangl)',
370
+ 'if m.b_min == 0 then',
376
371
  ' def_bcond_tp(x1,y1,x2,y2,x3,y3,x4,y4, 4)',
377
372
  'end',
378
373
  'state_of_problem("mag_static")']
@@ -441,13 +436,13 @@ class Builder:
441
436
 
442
437
  def create_gen_winding(self, model):
443
438
  try:
444
- if model.winding['wire']['name'] == 'hairpin_winding':
445
- model.winding['wire'].update(
446
- {"num_layers": model.winding["num_layers"]})
447
- genwdg = self.__render(model.winding,
448
- 'gen_' + model.winding['wire'].get('name'))
449
- except KeyError: # not hairpin_winding
439
+ model.winding['wire'].update(
440
+ {"num_layers": model.winding["num_layers"]})
441
+ genwdg = self.__render(model.winding,
442
+ 'gen_' + model.winding['wire'].get('name'))
443
+ except:
450
444
  genwdg = self.__render(model, 'gen_winding')
445
+
451
446
  k = list({'leak_dist_wind',
452
447
  'leak_evol_wind',
453
448
  'leak_tooth_wind'}.intersection(model.winding))
@@ -474,30 +469,31 @@ class Builder:
474
469
  'file_leak:close()'])
475
470
  return genwdg
476
471
 
477
- def prepare_model_with_dxf(self, model):
472
+ def prepare_model_with_dxf_or_svg(self, model):
478
473
  from femagtools.dxfsl.converter import convert
479
- dxfname = model.dxffile.get('name', None)
480
- if not dxfname:
481
- logger.error('Name of dxf-file expected')
474
+ fmt = model.dxffile if hasattr(model, 'dxffile') else model.svgfile
475
+ fname = fmt.get('name', None)
476
+ if not fname:
477
+ logger.error('Name of dxf or svg file expected')
482
478
  return []
483
479
 
484
- if dxfname.split('.')[-1] not in ('dxf', 'svg'): # add svg support
485
- dxfname += '.dxf'
486
- if not os.path.isfile(dxfname):
487
- logger.error('File "%s" not found', dxfname)
488
- raise ValueError(f'File {dxfname} not found')
480
+ if fname.split('.')[-1] not in ('dxf', 'svg'): # add svg support
481
+ fname += fmt[:3]
482
+ if not os.path.isfile(fname):
483
+ logger.error('File "%s" not found', fname)
484
+ raise ValueError(f'File {fname} not found')
489
485
 
490
486
  params = {}
491
- params['split'] = model.dxffile.get('split', False)
492
- params['show_plots'] = model.dxffile.get('plot', False)
487
+ params['split'] = fmt.get('split', False)
488
+ params['show_plots'] = fmt.get('plot', False)
493
489
  params['write_fsl'] = True
494
- params['airgap'] = model.dxffile.get('airgap', 0.0)
495
- params['nodedist'] = model.dxffile.get('nodedist', 1)
496
- params['full_model'] = model.dxffile.get('full_model', False)
497
- params['EESM'] = model.dxffile.get('type', 'PMSM') == 'EESM'
490
+ params['airgap'] = fmt.get('airgap', 0.0)
491
+ params['nodedist'] = fmt.get('nodedist', 1)
492
+ params['full_model'] = fmt.get('full_model', False)
493
+ params['EESM'] = fmt.get('type', 'PMSM') == 'EESM'
498
494
  if params['EESM']:
499
495
  model.rotor['EESM'] = {}
500
- conv = convert(dxfname, **params)
496
+ conv = convert(fname, **params)
501
497
 
502
498
  model.set_value('poles', conv.get('num_poles'))
503
499
  model.set_value('outer_diam', conv.get('dy1') * 1e-3)
@@ -510,6 +506,9 @@ class Builder:
510
506
  setattr(model, 'stator', {})
511
507
  model.stator['num_slots'] = conv.get('tot_num_slot')
512
508
  model.stator['slot_area'] = conv.get('slot_area')
509
+ model.stator['dy1'] = conv.get('dy1')*1e-3
510
+ model.stator['dy2'] = conv.get('dy2')*1e-3
511
+
513
512
  if model.get('num_agnodes', 0) == 0:
514
513
  model.set_value('agndst', conv['agndst']*1e-3)
515
514
  logger.info("num poles %d num slots %d outer diameter %.4f m agndst %.4f mm",
@@ -526,19 +525,39 @@ class Builder:
526
525
 
527
526
  if 'fsl_stator' in conv:
528
527
  self.fsl_stator = True
529
- model.stator['dxf'] = dict(fsl=conv['fsl_stator'])
528
+ th_props = [' ']
529
+ if model.stator.get('thcond', 0):
530
+ th_props = [f'stator_density = {model.stator["density"]}',
531
+ f'stator_thcond = {model.stator["thcond"]}',
532
+ f'stator_thcap = {model.stator["thcap"]}',
533
+ ]
534
+ model.stator['dxf'] = dict(fsl=conv['fsl_stator'] + th_props)
530
535
  if not (hasattr(model, 'magnet') or hasattr(model, 'rotor')):
531
536
  if params['EESM']:
532
537
  setattr(model, 'rotor', {})
533
538
  else:
534
539
  setattr(model, 'magnet', {})
540
+
535
541
  if 'fsl_rotor' in conv:
536
542
  self.fsl_rotor = True
543
+ th_props = ['']
544
+ logger.info(model['magnet'])
537
545
  if hasattr(model, 'magnet'):
538
- model.magnet['dxf'] = dict(fsl=conv['fsl_rotor'])
546
+ if model['magnet'].get('thcond', 0):
547
+ th_props = [f'rotor_density = {model["magnet"]["density"]}',
548
+ f'rotor_thcond = {model["magnet"]["thcond"]}',
549
+ f'rotor_thcap = {model["magnet"]["thcap"]}'
550
+ ]
551
+ model.magnet['dxf'] = dict(fsl=conv['fsl_rotor'] + th_props)
539
552
  if hasattr(model, 'rotor'):
540
553
  model.rotor['dxf'] = dict(fsl=conv['fsl_rotor'])
541
554
 
555
+ def create_thermal_properties(self, model):
556
+ if model.stator.get('thcond') and model.stator.get('thcap') and \
557
+ model.stator.get('density'):
558
+ return self.__render(model, 'prepare_thermal')
559
+ return ['']
560
+
542
561
  def create_model(self, model, magnets=[], condMat=[], ignore_material=False):
543
562
  magnetMat = {}
544
563
  material = ''
@@ -559,8 +578,8 @@ class Builder:
559
578
  magnetMat['magntemp'] = 20
560
579
  if model.is_complete():
561
580
  logger.info("create new model '%s'", model.name)
562
- if model.is_dxffile():
563
- self.prepare_model_with_dxf(model)
581
+ if model.is_dxffile() or model.is_svgfile():
582
+ self.prepare_model_with_dxf_or_svg(model)
564
583
  else:
565
584
  self.prepare_stator(model)
566
585
  if hasattr(model, 'magnet'):
@@ -594,6 +613,13 @@ class Builder:
594
613
  self.create_magnet_model(model))
595
614
  if magnetMat:
596
615
  rotor += self.create_magnet(model, magnetMat)
616
+
617
+ if model['magnet'].get('thcond_magnet', 0):
618
+ th_props = [f'magn_density = {1e3*model["magnet"]["spmaweight_magnet"]}',
619
+ f'magn_thcond = {model["magnet"]["thcond_magnet"]}',
620
+ f'magn_thcap = {model["magnet"]["thcap_magnet"]}'
621
+ ]
622
+ rotor += th_props
597
623
  else:
598
624
  rotor = self.create_rotor_model(
599
625
  model, condMat, ignore_material)
@@ -635,7 +661,8 @@ class Builder:
635
661
  rotor +
636
662
  self.mesh_airgap(model) +
637
663
  self.create_connect_models(model) +
638
- self.create_rotor_winding(model))
664
+ self.create_rotor_winding(model)) + \
665
+ self.create_thermal_properties(model)
639
666
 
640
667
  return (self.open_model(model) +
641
668
  self.create_fe_losses(model) +
femagtools/isa7.py CHANGED
@@ -544,6 +544,10 @@ class Isa7(object):
544
544
  16: [0.8274509803921568, 0.8274509803921568, 0.8274509803921568]} # LIGHGREY
545
545
 
546
546
  def __init__(self, reader):
547
+ try:
548
+ self.state_of_problem = reader.state_of_problem
549
+ except:
550
+ pass
547
551
  self.points = [Point(x, y)
548
552
  for x, y in zip(reader.POINT_ISA_POINT_REC_PT_CO_X,
549
553
  reader.POINT_ISA_POINT_REC_PT_CO_Y)]
@@ -1139,6 +1143,43 @@ class Isa7(object):
1139
1143
  sreg[sr.name] = [0,0,0]
1140
1144
  return sreg
1141
1145
 
1146
+ def get_minmax_temp(self):
1147
+ def node_subregion(subregion_name):
1148
+ node_temperature = []
1149
+ for i in self.subregions:
1150
+ if i.name.lower() == subregion_name:
1151
+ for j in i.elements():
1152
+ for k in j.vertices:
1153
+ node_temperature.append(k.vpot[-1])
1154
+ ndtemp = np.unique(node_temperature)
1155
+ return [np.amax(ndtemp), np.amin(ndtemp), np.mean(ndtemp)]
1156
+
1157
+ zero_temp = [0.0, 0.0, 0.0]
1158
+ component_temperature = dict(styoke=zero_temp, stteeth=zero_temp,
1159
+ magnet=zero_temp, winding=zero_temp)
1160
+ for i in self.subregions:
1161
+ if i.name:
1162
+ sreg_name = i.name.lower()
1163
+ if sreg_name in ('stza', 'stth', 'stteeth'):
1164
+ component_temperature['stteeth'] = node_subregion(sreg_name)
1165
+ elif sreg_name in ('styk', 'styoke', 'stjo'):
1166
+ component_temperature['styoke'] = node_subregion(sreg_name)
1167
+ elif sreg_name == 'iron':
1168
+ component_temperature['iron'] = node_subregion(sreg_name)
1169
+ elif sreg_name == 'pmag':
1170
+ component_temperature['magnet'] = node_subregion(sreg_name)
1171
+ else:
1172
+ pass
1173
+
1174
+ wdg_temps = []
1175
+ for j in self.wdg_elements():
1176
+ for k in j.vertices:
1177
+ wdg_temps.append(k.vpot[-1])
1178
+ wdg_temp = np.unique(wdg_temps)
1179
+ component_temperature['winding'] = [np.amax(wdg_temp),
1180
+ np.amin(wdg_temp),
1181
+ np.mean(wdg_temp)]
1182
+ return component_temperature
1142
1183
 
1143
1184
  def flux_dens(self, x, y, icur, ibeta):
1144
1185
  el = self.get_element(x, y)
femagtools/job.py CHANGED
@@ -112,7 +112,7 @@ class Task(object):
112
112
  if bchfile_list:
113
113
  result = femagtools.bch.Reader()
114
114
  with open(bchfile_list[-1]) as f:
115
- logger.info("Reading %s",
115
+ logger.debug("Reading %s",
116
116
  bchfile_list[-1])
117
117
  result.read(f)
118
118
  if bagdat.exists():
@@ -127,7 +127,7 @@ class Task(object):
127
127
  asm_list = sorted(basedir.glob(
128
128
  '*_[0-9][0-9][0-9].ASM'))
129
129
  if asm_list:
130
- logger.info("Reading %s",
130
+ logger.debug("Reading %s",
131
131
  asm_list[-1])
132
132
  result = femagtools.asm.read(asm_list[-1])
133
133
  if bagdat.exists():
@@ -645,6 +645,9 @@ class AFPM:
645
645
  parameters={
646
646
  'phi_voltage_winding': current_angles})
647
647
  logger.info("Current angles: %s", current_angles)
648
+ elif (simulation['calculationMode'] == 'cogg_calc' and
649
+ 'poc' not in simulation):
650
+ simulation['poc'] = poc.Poc(machine['pole_width'])
648
651
 
649
652
  lresults = self.parstudy(
650
653
  parvardef,
@@ -652,5 +655,6 @@ class AFPM:
652
655
  simulation, engine) # Note: imcomplete machine prevents rebuild
653
656
 
654
657
  results = process(lfe, pole_width, machine, lresults['f'])
655
- results.update(_psidq_ldq(results, nlresults))
658
+ if nlresults:
659
+ results.update(_psidq_ldq(results, nlresults))
656
660
  return results
femagtools/machine/pm.py CHANGED
@@ -148,7 +148,7 @@ class PmRelMachine(object):
148
148
  kr = self.zeta1[0]*freq**3 + self.zeta1[1]*freq**2 + \
149
149
  self.zeta1[2]*freq + self.zeta1[3]
150
150
  kr[kr<1] = 1.
151
- return self.r1*(1 - self.kth1*(self.tcu1 - 20))*kr # ref 20°C
151
+ return self.r1*(1 + self.kth1*(self.tcu1 - 20))*kr # ref 20°C
152
152
  if self.skin_resistance is not None:
153
153
  return self.skin_resistance(self.r1, w, self.tcu1, kth=self.kth1)
154
154
 
femagtools/machine/sm.py CHANGED
@@ -339,6 +339,20 @@ class SynchronousMachine(object):
339
339
 
340
340
  def rstat(self, w):
341
341
  """stator resistance"""
342
+ if isinstance(self.zeta1, list):
343
+ # polyfit from ac loss calculation
344
+ freq = w/2/np.pi
345
+ kr = self.zeta1[0]*freq**3 + self.zeta1[1]*freq**2 + \
346
+ self.zeta1[2]*freq + self.zeta1[3]
347
+ if isinstance(kr, list):
348
+ kr = np.array(kr)
349
+ kr[kr<1.0] = 1.0
350
+ elif isinstance(kr, np.ndarray):
351
+ kr[kr<1.0] = 1.0
352
+ else:
353
+ if kr < 1.0:
354
+ kr = 1.0
355
+ return self.r1*(1 + self.kth1*(self.tcu1 - 20))*kr # ref 20°C
342
356
  sr = self.skin_resistance[0]
343
357
  if sr is not None:
344
358
  return sr(self.r1, w, self.tcu1, kth=self.kth1)
@@ -411,6 +411,7 @@ def dqparident(workdir, engine, temp, machine,
411
411
  elif ('wire_width' in machine[wdgk]) and ('wire_height' in machine[wdgk]):
412
412
  aw = machine[wdgk]['wire_width']*machine[wdgk]['wire_height']
413
413
  else: # wire diameter from slot area
414
+ da1 = machine['bore_diam']
414
415
  aw = 0.75 * fcu * np.pi*da1*hs/Q1/wdg.l/N
415
416
  r1 = wdg_resistance(wdg, N, g, aw, da1, hs, lfe)
416
417
  except (NameError, KeyError):
@@ -489,6 +490,8 @@ def dqparident(workdir, engine, temp, machine,
489
490
  machine['poles'] = 2*results['f'][0]['machine']['p']
490
491
  da1 = 2*results['f'][0]['machine']['fc_radius']
491
492
  wdg = create_wdg(machine)
493
+ if 'bore_diam' in machine:
494
+ da1 = machine['bore_diam']
492
495
  ls1 = 0
493
496
  try:
494
497
  leakages = [float(x)
@@ -569,14 +572,12 @@ def dqparident(workdir, engine, temp, machine,
569
572
  if r1:
570
573
  dqpars['r1'] = r1
571
574
  else:
572
- from .. import nc
573
- model = nc.read(str(pathlib.Path(workdir) / machine['name']))
575
+ model = parvar.femag.read_nc()
574
576
  try:
575
577
  nlayers = wdg.l
576
578
  except UnboundLocalError:
577
579
  wdg = create_wdg(machine)
578
580
  nlayers = wdg.l
579
- da1 = machine['outer_diam']
580
581
  Q1 = wdg.Q
581
582
  istat = 0 if model.get_areas()[0]['slots'] else 1
582
583
  asl = model.get_areas()[istat]['slots']