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.
- femagtools/__init__.py +1 -1
- femagtools/amela.py +2 -2
- femagtools/dxfsl/area.py +142 -9
- femagtools/dxfsl/conv.py +2 -9
- femagtools/dxfsl/converter.py +33 -9
- femagtools/dxfsl/fslrenderer.py +13 -12
- femagtools/dxfsl/geom.py +39 -6
- femagtools/dxfsl/journal.py +2 -2
- femagtools/dxfsl/machine.py +14 -13
- femagtools/dxfsl/shape.py +3 -0
- femagtools/dxfsl/svgparser.py +31 -4
- femagtools/ecloss.py +381 -2
- femagtools/femag.py +55 -0
- femagtools/fsl.py +74 -47
- femagtools/isa7.py +41 -0
- femagtools/job.py +2 -2
- femagtools/machine/afpm.py +5 -1
- femagtools/machine/pm.py +1 -1
- femagtools/machine/sm.py +14 -0
- femagtools/machine/utils.py +4 -3
- femagtools/mcv.py +128 -124
- femagtools/me.py +13 -13
- femagtools/model.py +14 -1
- femagtools/moo/population.py +9 -7
- femagtools/nc.py +12 -0
- femagtools/plot/__init__.py +1 -1
- femagtools/plot/fieldlines.py +1 -1
- femagtools/plot/mcv.py +18 -0
- femagtools/plot/nc.py +22 -5
- femagtools/plot/wdg.py +40 -7
- femagtools/svgfsl/converter.py +6 -0
- femagtools/templates/gen_hairpin_winding.mako +36 -45
- femagtools/templates/gen_winding.mako +7 -0
- femagtools/templates/magnetIron.mako +30 -46
- femagtools/templates/magnetIron2.mako +39 -0
- femagtools/templates/magnetIron3.mako +39 -0
- femagtools/templates/magnetIron4.mako +39 -0
- femagtools/templates/magnetIron5.mako +39 -0
- femagtools/templates/magnetIronV.mako +34 -54
- femagtools/templates/magnetSector.mako +32 -47
- femagtools/templates/mesh-airgap.mako +12 -6
- femagtools/templates/prepare_thermal.mako +354 -0
- femagtools/templates/statorRotor3.mako +3 -22
- femagtools/windings.py +92 -59
- {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/METADATA +20 -18
- {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/RECORD +53 -53
- {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/WHEEL +1 -1
- tests/test_fsl.py +1 -1
- tests/test_mcv.py +106 -1
- tests/test_windings.py +18 -2
- tests/test_mcvwriter.py +0 -96
- {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/LICENSE +0 -0
- {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/entry_points.txt +0 -0
- {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
|
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[
|
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[
|
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
|
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
|
360
|
-
'
|
361
|
-
'
|
362
|
-
'
|
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
|
-
'
|
371
|
-
'
|
372
|
-
'
|
373
|
-
'
|
374
|
-
'
|
375
|
-
'
|
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
|
-
|
445
|
-
model.winding[
|
446
|
-
|
447
|
-
|
448
|
-
|
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
|
472
|
+
def prepare_model_with_dxf_or_svg(self, model):
|
478
473
|
from femagtools.dxfsl.converter import convert
|
479
|
-
|
480
|
-
|
481
|
-
|
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
|
485
|
-
|
486
|
-
if not os.path.isfile(
|
487
|
-
logger.error('File "%s" not found',
|
488
|
-
raise ValueError(f'File {
|
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'] =
|
492
|
-
params['show_plots'] =
|
487
|
+
params['split'] = fmt.get('split', False)
|
488
|
+
params['show_plots'] = fmt.get('plot', False)
|
493
489
|
params['write_fsl'] = True
|
494
|
-
params['airgap'] =
|
495
|
-
params['nodedist'] =
|
496
|
-
params['full_model'] =
|
497
|
-
params['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(
|
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
|
-
|
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
|
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.
|
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.
|
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.
|
130
|
+
logger.debug("Reading %s",
|
131
131
|
asm_list[-1])
|
132
132
|
result = femagtools.asm.read(asm_list[-1])
|
133
133
|
if bagdat.exists():
|
femagtools/machine/afpm.py
CHANGED
@@ -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
|
-
|
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
|
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)
|
femagtools/machine/utils.py
CHANGED
@@ -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
|
-
|
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']
|