femagtools 1.8.18__py3-none-any.whl → 1.8.20__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 +23 -6
- femagtools/dxfsl/area.py +12 -1
- femagtools/dxfsl/areabuilder.py +28 -12
- femagtools/dxfsl/converter.py +7 -2
- femagtools/dxfsl/fslrenderer.py +47 -14
- femagtools/dxfsl/geom.py +110 -39
- femagtools/dxfsl/machine.py +107 -2
- femagtools/dxfsl/shape.py +7 -0
- femagtools/femag.py +3 -3
- femagtools/isa7.py +25 -22
- femagtools/machine/__init__.py +2 -2
- femagtools/machine/effloss.py +8 -0
- femagtools/machine/pm.py +18 -7
- femagtools/machine/sm.py +17 -6
- femagtools/mcv.py +4 -1
- femagtools/model.py +7 -1
- femagtools/shortcircuit.py +8 -7
- femagtools/templates/psi-torq-rem.mako +4 -4
- femagtools/templates/statorKS1.mako +43 -0
- {femagtools-1.8.18.dist-info → femagtools-1.8.20.dist-info}/METADATA +1 -1
- {femagtools-1.8.18.dist-info → femagtools-1.8.20.dist-info}/RECORD +26 -25
- {femagtools-1.8.18.dist-info → femagtools-1.8.20.dist-info}/WHEEL +0 -0
- {femagtools-1.8.18.dist-info → femagtools-1.8.20.dist-info}/entry_points.txt +0 -0
- {femagtools-1.8.18.dist-info → femagtools-1.8.20.dist-info}/licenses/LICENSE +0 -0
- {femagtools-1.8.18.dist-info → femagtools-1.8.20.dist-info}/top_level.txt +0 -0
femagtools/dxfsl/machine.py
CHANGED
@@ -646,7 +646,7 @@ class Machine(object):
|
|
646
646
|
self.geom.set_virtual_start_end_corners()
|
647
647
|
|
648
648
|
self.geom.looking_for_corners()
|
649
|
-
create_areas = self.geom.
|
649
|
+
create_areas = self.geom.close_outer_winding_areas()
|
650
650
|
if self.geom.adjust_outer_hull_for_symmetry():
|
651
651
|
create_areas = True
|
652
652
|
|
@@ -1284,6 +1284,30 @@ class Machine(object):
|
|
1284
1284
|
def search_critical_elements(self, mindist):
|
1285
1285
|
self.geom.search_critical_elements(mindist)
|
1286
1286
|
|
1287
|
+
def create_arc_element(self,
|
1288
|
+
radius,
|
1289
|
+
startangle,
|
1290
|
+
endangle):
|
1291
|
+
arc = Arc(Element(center=self.center,
|
1292
|
+
radius=radius,
|
1293
|
+
start_angle=startangle*180/np.pi,
|
1294
|
+
end_angle=endangle*180/np.pi),
|
1295
|
+
color='darkred',
|
1296
|
+
linestyle='dotted')
|
1297
|
+
|
1298
|
+
node1 = self.geom.find_the_node(arc.p1)
|
1299
|
+
if node1 is None:
|
1300
|
+
self.geom.split_with_point(arc.p1)
|
1301
|
+
node1 = self.geom.find_the_node(arc.p1)
|
1302
|
+
node2 = self.geom.find_the_node(arc.p2)
|
1303
|
+
if node2 is None:
|
1304
|
+
self.geom.split_with_point(arc.p2)
|
1305
|
+
node2 = self.geom.find_the_node(arc.p2)
|
1306
|
+
assert(node1 is not None)
|
1307
|
+
assert(node2 is not None)
|
1308
|
+
logger.debug("ARC Node1=%s, Node2=%s", node1, node2)
|
1309
|
+
self.geom.add_element(arc, rtol=1e-3, atol=1e-3)
|
1310
|
+
|
1287
1311
|
def create_arc(self, radius,
|
1288
1312
|
color='red', linestyle='dotted',
|
1289
1313
|
attr=None):
|
@@ -1344,6 +1368,79 @@ class Machine(object):
|
|
1344
1368
|
self.endangle)
|
1345
1369
|
return self.geom.possible_magnet_in_the_middle(midangle)
|
1346
1370
|
|
1371
|
+
def separate_tooth_and_yoke(self, midangle):
|
1372
|
+
logger.debug("begin separate_tooth_and_yoke")
|
1373
|
+
wdg_list = self.geom.get_areas_of_type((AREA.TYPE_WINDINGS,))
|
1374
|
+
wdg_max_dist = 0.0
|
1375
|
+
wdg = None
|
1376
|
+
for w in wdg_list:
|
1377
|
+
if w.max_dist > wdg_max_dist:
|
1378
|
+
wdg_max_dist = w.max_dist
|
1379
|
+
wdg = w
|
1380
|
+
|
1381
|
+
tooth_list = self.geom.get_areas_of_type((AREA.TYPE_TOOTH,))
|
1382
|
+
tooth_max_dist = 0.0
|
1383
|
+
for tooth in tooth_list:
|
1384
|
+
tooth_max_dist = max(tooth_max_dist, tooth.max_dist)
|
1385
|
+
tooth_height = tooth_max_dist - self.geom.min_radius
|
1386
|
+
wdg_height = wdg_max_dist - self.geom.min_radius
|
1387
|
+
|
1388
|
+
logger.debug("HEIGHT TOOTH=%s, WINDINGS=%s", tooth_height, wdg_height)
|
1389
|
+
|
1390
|
+
if greater_equal(tooth_height, wdg_height * 0.9) and \
|
1391
|
+
less_equal(tooth_height, wdg_height * 1.1):
|
1392
|
+
logger.debug("Tooths are ok")
|
1393
|
+
return False # tooth ok
|
1394
|
+
|
1395
|
+
iron_list = self.geom.get_areas_of_irons()
|
1396
|
+
tooth_list = [iron for iron in iron_list
|
1397
|
+
if iron.close_to_ag and greater(iron.max_dist, wdg_max_dist, rtol=1e-2, atol=1e-2)]
|
1398
|
+
logger.debug("max winding dist = %s", wdg_max_dist)
|
1399
|
+
logger.debug("tooths = %s", len(tooth_list))
|
1400
|
+
wdg_nodes = [(distance(self.center, n), n) for n in wdg.list_of_nodes()]
|
1401
|
+
wdg_nodes.sort(reverse=True)
|
1402
|
+
|
1403
|
+
top_nodes = []
|
1404
|
+
other_nodes = []
|
1405
|
+
for d, n in wdg_nodes:
|
1406
|
+
if np.isclose(d, wdg.max_dist, rtol=1e-2, atol=1e-1):
|
1407
|
+
top_nodes.append((alpha_line(self.center, n), d, n))
|
1408
|
+
else:
|
1409
|
+
other_nodes.append((d, alpha_line(self.center, n), n))
|
1410
|
+
|
1411
|
+
a_right, d_right, n_right = (None, None, None)
|
1412
|
+
a_left, d_left, n_left = (None, None, None)
|
1413
|
+
|
1414
|
+
if not top_nodes:
|
1415
|
+
other_nodes.sort(reverse=True)
|
1416
|
+
for d, a, n in other_nodes:
|
1417
|
+
if a > midangle:
|
1418
|
+
if not a_left:
|
1419
|
+
a_left = a
|
1420
|
+
d_left = d
|
1421
|
+
n_left = n
|
1422
|
+
else:
|
1423
|
+
if not a_right:
|
1424
|
+
a_right = a
|
1425
|
+
d_right = d
|
1426
|
+
n_right = n
|
1427
|
+
if a_left and a_right:
|
1428
|
+
break
|
1429
|
+
if not (a_left and a_right):
|
1430
|
+
logger.debug("end separate_tooth_and_yoke: no arcs possible")
|
1431
|
+
return False # bad luck
|
1432
|
+
else:
|
1433
|
+
top_nodes.sort()
|
1434
|
+
a_right, d_right, n_right = top_nodes[0]
|
1435
|
+
a_left, d_left, n_left = top_nodes[0-1]
|
1436
|
+
node_right = self.geom.find_the_node(n_right)
|
1437
|
+
node_left = self.geom.find_the_node(n_left)
|
1438
|
+
|
1439
|
+
self.create_arc_element(d_right, self.startangle, a_right)
|
1440
|
+
self.create_arc_element(d_left, a_left, self.endangle)
|
1441
|
+
logger.debug("end separate_tooth_and_yoke")
|
1442
|
+
return True
|
1443
|
+
|
1347
1444
|
def create_mirror_lines_outside_windings(self):
|
1348
1445
|
logger.debug("create_mirror_lines_outside_windings")
|
1349
1446
|
|
@@ -1351,6 +1448,9 @@ class Machine(object):
|
|
1351
1448
|
logger.debug("end create_mirror_lines_outside_windings: not done")
|
1352
1449
|
return
|
1353
1450
|
|
1451
|
+
midangle = middle_angle(self.startangle, self.endangle)
|
1452
|
+
self.separate_tooth_and_yoke(midangle)
|
1453
|
+
|
1354
1454
|
radius = self.radius+10
|
1355
1455
|
ag_list = self.geom.detect_airgaps(self.center,
|
1356
1456
|
self.startangle, self.endangle,
|
@@ -1359,7 +1459,6 @@ class Machine(object):
|
|
1359
1459
|
radius_list = [(ag[0], (ag[0] + ag[1]) / 2, ag[1]) for ag in ag_list]
|
1360
1460
|
radius_list.sort(reverse=True)
|
1361
1461
|
|
1362
|
-
midangle = middle_angle(self.startangle, self.endangle)
|
1363
1462
|
line = Line(
|
1364
1463
|
Element(start=self.center,
|
1365
1464
|
end=point(self.center, radius, midangle)))
|
@@ -1491,3 +1590,9 @@ class Machine(object):
|
|
1491
1590
|
self.geom.check_airgap_connecting_nodes(m_outer.geom,
|
1492
1591
|
self.startangle,
|
1493
1592
|
self.endangle)
|
1593
|
+
|
1594
|
+
def remove_tiny_air_areas(self):
|
1595
|
+
if not self.geom.remove_tiny_air_areas():
|
1596
|
+
return False
|
1597
|
+
self.repair_hull()
|
1598
|
+
return True
|
femagtools/dxfsl/shape.py
CHANGED
@@ -90,6 +90,13 @@ class Shape(object):
|
|
90
90
|
self.n2 = n1
|
91
91
|
self.n1 = n2
|
92
92
|
|
93
|
+
def has_same_nodes(self, e):
|
94
|
+
if points_are_close(e.n1, self.n1):
|
95
|
+
return points_are_close(e.n2, self.n2)
|
96
|
+
if points_are_close(e.n1, self.n2):
|
97
|
+
return points_are_close(e.n2, self.n1)
|
98
|
+
return False
|
99
|
+
|
93
100
|
"""an abstract geometry with 2 points"""
|
94
101
|
|
95
102
|
def start(self):
|
femagtools/femag.py
CHANGED
@@ -424,7 +424,7 @@ class BaseFemag(object):
|
|
424
424
|
return sorted(pathlib.Path(self.workdir).glob('*.PROT'),
|
425
425
|
key=lambda x: x.stat().st_mtime, reverse=True)[0].stem
|
426
426
|
|
427
|
-
def readResult(self,
|
427
|
+
def readResult(self, simulation, bch=None, machine=None):
|
428
428
|
if simulation:
|
429
429
|
if simulation['calculationMode'] == "fieldcalc":
|
430
430
|
nc = self.read_nc()
|
@@ -656,7 +656,7 @@ class Femag(BaseFemag):
|
|
656
656
|
pass
|
657
657
|
|
658
658
|
if simulation:
|
659
|
-
return self.readResult(machine, simulation)
|
659
|
+
return self.readResult(machine=machine, simulation=simulation)
|
660
660
|
|
661
661
|
return {'status': 'ok', 'message': self.modelname,
|
662
662
|
'model': self.model.props()}
|
@@ -1196,6 +1196,6 @@ class ZmqFemag(BaseFemag):
|
|
1196
1196
|
if r['status'] == 'ok':
|
1197
1197
|
bch = femagtools.bch.Reader()
|
1198
1198
|
bch.read(content.decode('latin1'))
|
1199
|
-
bch = self.readResult(simulation, bch)
|
1199
|
+
bch = self.readResult(simulation=simulation, bch=bch)
|
1200
1200
|
return bch
|
1201
1201
|
raise FemagError(r['message'])
|
femagtools/isa7.py
CHANGED
@@ -1359,28 +1359,31 @@ class Isa7(object):
|
|
1359
1359
|
# prep dictionary for the loss calculation
|
1360
1360
|
pm_data = []
|
1361
1361
|
for i, se in enumerate(mag_spels):
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1362
|
+
try:
|
1363
|
+
ecp = [e.center for e in se.elements]
|
1364
|
+
geometry = se.get_rect_geom()
|
1365
|
+
|
1366
|
+
#= np.moveaxis(bxy, 1, 0)
|
1367
|
+
pd = dict(name='pm_data_se' + str(se.key),
|
1368
|
+
hm=geometry['h'],
|
1369
|
+
wm=geometry['w'],
|
1370
|
+
lm=self.arm_length,
|
1371
|
+
alpha=geometry['alpha'],
|
1372
|
+
ls=self.arm_length,
|
1373
|
+
sigma=cond,
|
1374
|
+
mur=mur,
|
1375
|
+
loadcase=ibeta,
|
1376
|
+
numpoles=poles,
|
1377
|
+
elcp=transform_coord(geometry, ecp),
|
1378
|
+
area=se.area(),
|
1379
|
+
spel_key=se.key)
|
1380
|
+
if ibeta != None:
|
1381
|
+
pd.update({'bl': self.get_magnet_flux_density(se, icur, ibeta)})
|
1382
|
+
pd.update(pos)
|
1383
|
+
|
1384
|
+
pm_data.append(pd)
|
1385
|
+
except IndexError as e:
|
1386
|
+
logger.warning("se %d magnet geometry ignored: %s", i, e)
|
1384
1387
|
return pm_data
|
1385
1388
|
|
1386
1389
|
def get_magnet_flux_density(self, se, icur, ibeta) -> list:
|
femagtools/machine/__init__.py
CHANGED
@@ -32,7 +32,7 @@ def create_from_eecpars(temp, eecpars, lfe=1, wdg=1):
|
|
32
32
|
PM, EESM or IM"""
|
33
33
|
rlfe = lfe
|
34
34
|
rwdg = wdg
|
35
|
-
opts = {k: eecpars[k] for k in ('zeta1', 'gam', 'kh', 'kpfe',
|
35
|
+
opts = {k: eecpars[k] for k in ('zeta1', 'gam', 'kh', 'kpfe', 'kpfe_s', 'kpfe_r',
|
36
36
|
'kfric_b', 'kpmag') if k in eecpars}
|
37
37
|
try:
|
38
38
|
opts['rotor_mass'] = rlfe*eecpars['rotor_mass']
|
@@ -51,7 +51,7 @@ def create_from_eecpars(temp, eecpars, lfe=1, wdg=1):
|
|
51
51
|
smpars = copy.deepcopy(eecpars)
|
52
52
|
smpars['tcu1'] = temp[0]
|
53
53
|
smpars['tcu2'] = temp[1]
|
54
|
-
# external inductances
|
54
|
+
# external inductances
|
55
55
|
opts["ls"] = eecpars.get('ls1', 0)*rwdg**2
|
56
56
|
if 'ldq' in smpars:
|
57
57
|
machine = SynchronousMachineLdq(smpars, lfe=rlfe, wdg=rwdg, **opts)
|
femagtools/machine/effloss.py
CHANGED
@@ -372,6 +372,10 @@ def efficiency_losses_map(eecpars, u1, T, temp, n, npoints=(60, 40),
|
|
372
372
|
if isinstance(m, PmRelMachine):
|
373
373
|
plfe1 = m.kpfe*m.iqd_plfe1(*iqd, f1)
|
374
374
|
plfe2 = m.kpfe*m.iqd_plfe2(iqd[0], iqd[1], f1)
|
375
|
+
if hasattr(m, 'kpfe_s'):
|
376
|
+
plfe1 = m.kpfe_s*m.iqd_plfe1(*iqd, f1)
|
377
|
+
if hasattr(m, 'kpfe_r'):
|
378
|
+
plfe2 = m.kpfe_r*m.iqd_plfe2(iqd[0], iqd[1], f1)
|
375
379
|
plmag = m.kpmag*m.iqd_plmag(iqd[0], iqd[1], f1)
|
376
380
|
plcu1 = m.iqd_plcu1(iqd[0], iqd[1], 2*np.pi*f1)
|
377
381
|
plcu2 = m.iqd_plcu2(*iqd)
|
@@ -385,6 +389,10 @@ def efficiency_losses_map(eecpars, u1, T, temp, n, npoints=(60, 40),
|
|
385
389
|
elif isinstance(m, SynchronousMachine):
|
386
390
|
plfe1 = m.kpfe*m.iqd_plfe1(*iqd, f1)
|
387
391
|
plfe2 = m.kpfe*m.iqd_plfe2(*iqd, f1)
|
392
|
+
if hasattr(m, 'kpfe_s'):
|
393
|
+
plfe1 = m.kpfe_s*m.iqd_plfe1(*iqd, f1)
|
394
|
+
if hasattr(m, 'kpfe_r'):
|
395
|
+
plfe2 = m.kpfe_r*m.iqd_plfe2(*iqd, f1)
|
388
396
|
plmag = np.zeros_like(plfe2)
|
389
397
|
plcu1 = m.iqd_plcu1(iqd[0], iqd[1], f1)
|
390
398
|
try:
|
femagtools/machine/pm.py
CHANGED
@@ -97,7 +97,7 @@ class PmRelMachine(object):
|
|
97
97
|
self.zeta1 = 0.2
|
98
98
|
self.gam = 0.7
|
99
99
|
self.kh = 2
|
100
|
-
self.kpfe = 1 # iron loss factor
|
100
|
+
self.kpfe = 1 # common iron loss factor
|
101
101
|
self.kpmag = 1 # magnet loss factor
|
102
102
|
self.kfric_b = 1
|
103
103
|
self.rotor_mass = 0
|
@@ -181,9 +181,10 @@ class PmRelMachine(object):
|
|
181
181
|
if np.abs(torque) < 1e-2:
|
182
182
|
return (0, 0)
|
183
183
|
if np.isscalar(iqd0):
|
184
|
-
i0 = self.io
|
184
|
+
i0 = list(self.io)
|
185
185
|
if torque<0:
|
186
186
|
i0[0] = -i0[0]
|
187
|
+
i0 = tuple(i0)
|
187
188
|
else:
|
188
189
|
i0 = iqd0
|
189
190
|
if with_mtpa:
|
@@ -258,7 +259,13 @@ class PmRelMachine(object):
|
|
258
259
|
and friction windage losses"""
|
259
260
|
if n > 1e-3:
|
260
261
|
f1 = self.p*n
|
261
|
-
|
262
|
+
plfe1 = self.kpfe * self.iqd_plfe1(iq, id, f1)
|
263
|
+
plfe2 = self.kpfe * self.iqd_plfe2(iq, id, f1)
|
264
|
+
if hasattr(self, 'kpfe_s'):
|
265
|
+
plfe1 = self.kpfe_s * self.iqd_plfe1(iq, id, f1)
|
266
|
+
if hasattr(self, 'kpfe_r'):
|
267
|
+
plfe2 = self.kpfe_r * self.iqd_plfe2(iq, id, f1)
|
268
|
+
plfe = plfe1 + plfe2
|
262
269
|
pmag = self.kpmag * self.iqd_plmag(iq, id, f1)
|
263
270
|
return (plfe + pmag + self.pfric(n))/(2*np.pi*n)
|
264
271
|
return 0
|
@@ -927,10 +934,10 @@ class PmRelMachine(object):
|
|
927
934
|
plfe1 = self.iqd_plfe1(iq, id, f1)
|
928
935
|
plfe2 = self.iqd_plfe2(iq, id, f1)
|
929
936
|
plmag = self.iqd_plmag(iq, id, f1)
|
930
|
-
plfe = plfe1 + plfe2
|
937
|
+
plfe = plfe1 + plfe2
|
931
938
|
plfric = self.pfric(n)
|
932
939
|
plcu = self.betai1_plcu(i1, 2 * np.pi * f1)
|
933
|
-
pltotal = plfe + plcu + plfric
|
940
|
+
pltotal = plfe + plcu + plfric + plmag
|
934
941
|
p1 = pmech + pltotal
|
935
942
|
if np.abs(pmech) < 1e-12:
|
936
943
|
eta = 0 # power to low for eta calculation
|
@@ -1153,11 +1160,15 @@ class PmRelMachine(object):
|
|
1153
1160
|
f1 = np.array(r['n'])*self.p
|
1154
1161
|
plfe1 = self.kpfe*self.iqd_plfe1(np.array(r['iq']), np.array(r['id']), f1)
|
1155
1162
|
plfe2 = self.kpfe*self.iqd_plfe2(np.array(r['iq']), np.array(r['id']), f1)
|
1163
|
+
if hasattr(self, 'kpfe_s'):
|
1164
|
+
plfe1 = self.kpfe_s*self.iqd_plfe1(np.array(r['iq']), np.array(r['id']), f1)
|
1165
|
+
if hasattr(self, 'kpfe_r'):
|
1166
|
+
plfe2 = self.kpfe_r*self.iqd_plfe2(np.array(r['iq']), np.array(r['id']), f1)
|
1156
1167
|
plmag = self.kpmag*self.iqd_plmag(np.array(r['iq']), np.array(r['id']), f1)
|
1157
|
-
plfe = plfe1 + plfe2
|
1168
|
+
plfe = plfe1 + plfe2
|
1158
1169
|
plcu = self.betai1_plcu(np.array(r['i1']), 2*np.pi*f1)
|
1159
1170
|
plfw = self.pfric(2*np.pi*f1)
|
1160
|
-
pltotal = plfe + plcu + plfw
|
1171
|
+
pltotal = plfe + plcu + plfw + plmag
|
1161
1172
|
r['pmech'] = pmech.tolist()
|
1162
1173
|
r['plfe'] = plfe.tolist()
|
1163
1174
|
r['plcu'] = plcu.tolist()
|
femagtools/machine/sm.py
CHANGED
@@ -22,7 +22,7 @@ eecdefaults = {
|
|
22
22
|
'tcu2': 20,
|
23
23
|
'rotor_mass': 0,
|
24
24
|
'kfric_b': 1,
|
25
|
-
'kpfe': 1 # iron loss factor
|
25
|
+
'kpfe': 1 # common iron loss factor
|
26
26
|
}
|
27
27
|
|
28
28
|
logger = logging.getLogger('sm')
|
@@ -76,7 +76,13 @@ def parident(workdir, engine, machine,
|
|
76
76
|
raise ValueError('i1_max missing')
|
77
77
|
i1_max = kwargs['i1_max']
|
78
78
|
|
79
|
-
|
79
|
+
|
80
|
+
if "magnetFsl" in machine["magnet"]:
|
81
|
+
rotorkey = "magnet"
|
82
|
+
else:
|
83
|
+
rotorkey = "rotor"
|
84
|
+
|
85
|
+
ifnom = machine[rotorkey]['ifnom']
|
80
86
|
exc_logspace = True
|
81
87
|
ifmin, ifmax = ifnom/4, 1.4*ifnom
|
82
88
|
if exc_logspace:
|
@@ -197,7 +203,7 @@ def parident(workdir, engine, machine,
|
|
197
203
|
if simulation['calculationMode'] == 'ld_lq_fast':
|
198
204
|
dqpars = dict(m=3, p=b['machine']['p'],
|
199
205
|
r1=float(r1),
|
200
|
-
r2=machine[
|
206
|
+
r2=machine[rotorkey].get('resistance', 1),
|
201
207
|
rotor_mass=rotor_mass, kfric_b=1,
|
202
208
|
ldq=[dict(
|
203
209
|
ex_current=b['machine']['ex_current'],
|
@@ -214,7 +220,7 @@ def parident(workdir, engine, machine,
|
|
214
220
|
else:
|
215
221
|
dqpars = dict(m=3, p=b['machine']['p'],
|
216
222
|
r1=r1,
|
217
|
-
r2=machine[
|
223
|
+
r2=machine[rotorkey].get('resistance', 1),
|
218
224
|
rotor_mass=rotor_mass, kfric_b=1,
|
219
225
|
psidq=[dict(
|
220
226
|
ex_current=b['machine']['ex_current'],
|
@@ -387,8 +393,13 @@ class SynchronousMachine(object):
|
|
387
393
|
and friction windage losses"""
|
388
394
|
if n > 1e-3:
|
389
395
|
f1 = self.p*n
|
390
|
-
|
391
|
-
|
396
|
+
plfe1 = self.kpfe * self.iqd_plfe1(iq, id, iex, f1)
|
397
|
+
plfe2 = self.kpfe * self.iqd_plfe2(iq, id, iex, f1)
|
398
|
+
if hasattr(self, 'kpfe_s'):
|
399
|
+
plfe1 = self.kpfe_s * self.iqd_plfe1(iq, id, iex, f1)
|
400
|
+
if hasattr(self, 'kpfe_r'):
|
401
|
+
plfe2 = self.kpfe_r * self.iqd_plfe2(iq, id, iex, f1)
|
402
|
+
plfe = plfe1 + plfe2
|
392
403
|
return (plfe + self.pfric(n))/(2*np.pi*n)
|
393
404
|
return 0
|
394
405
|
|
femagtools/mcv.py
CHANGED
@@ -1173,7 +1173,10 @@ class MagnetizingCurve(object):
|
|
1173
1173
|
mcv = self.find_by_name(name)
|
1174
1174
|
if not mcv:
|
1175
1175
|
bname = name
|
1176
|
-
filename =
|
1176
|
+
filename = name if pathlib.Path(name).suffix.upper() == ext \
|
1177
|
+
else ''.join((name, ext))
|
1178
|
+
if name == 'dummy':
|
1179
|
+
return filename
|
1177
1180
|
# check fillfac and readmcv
|
1178
1181
|
if not fillfac or fillfac == 1.0:
|
1179
1182
|
try:
|
femagtools/model.py
CHANGED
@@ -477,8 +477,14 @@ class MachineModel(Model):
|
|
477
477
|
model = {k: getattr(self, k) for k in keys if hasattr(self, k)}
|
478
478
|
if hasattr(self, 'stator'):
|
479
479
|
model['stator'] = {k: self.stator[k]
|
480
|
-
for k in ('num_slots',
|
480
|
+
for k in ('num_slots',
|
481
|
+
'num_slots_gen',
|
482
|
+
'slot_area')
|
481
483
|
if k in self.stator}
|
484
|
+
|
485
|
+
for k in model:
|
486
|
+
if isinstance(model[k], np.float64):
|
487
|
+
model[k] = float(model[k])
|
482
488
|
return model
|
483
489
|
|
484
490
|
class FeaModel(Model):
|
femagtools/shortcircuit.py
CHANGED
@@ -108,11 +108,11 @@ def shortcircuit(femag, machine, bch, simulation, engine=0):
|
|
108
108
|
bchsc.scData['demag'] = bchsc.demag
|
109
109
|
if simulation.get('sim_demagn', 0):
|
110
110
|
dd = {'displ': [d['displ']
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
111
|
+
for d in bchsc.demag if 'displ' in d],
|
112
|
+
'H_max': [d['H_max']
|
113
|
+
for d in bchsc.demag if 'H_max' in d],
|
114
|
+
'H_av': [d['H_av']
|
115
|
+
for d in bchsc.demag if 'H_av' in d]}
|
116
116
|
x1 = bchsc.demag[0]['current_1']
|
117
117
|
x2 = bchsc.demag[0]['current_2']
|
118
118
|
def func(phi):
|
@@ -121,6 +121,7 @@ def shortcircuit(femag, machine, bch, simulation, engine=0):
|
|
121
121
|
i1max = x1/np.cos(phi)
|
122
122
|
|
123
123
|
phirot = dd['displ'][0]/180*np.pi
|
124
|
+
logger.info("i1max %g phi %g phirot %g", i1max, phi, phirot)
|
124
125
|
bchsc.scData['demag'] = demag(
|
125
126
|
femag, machine, simulation,
|
126
127
|
i1max, phirot, phi, engine)
|
@@ -319,7 +320,7 @@ def shortcircuit_2phase(femag, machine, simulation, engine=0):
|
|
319
320
|
# rotor position at maximum current:
|
320
321
|
trot = min(iav[0], iap[0])
|
321
322
|
phirot = wm*trot + phi0
|
322
|
-
logger.
|
323
|
+
logger.debug("phirot %.1f", phirot)
|
323
324
|
|
324
325
|
scData = {
|
325
326
|
'ia': ia.tolist(),
|
@@ -345,7 +346,7 @@ def demag(femag, machine, simulation, i1max, phirot, phi, engine=0):
|
|
345
346
|
"""demag simulation using psi-torq-rem-rot"""
|
346
347
|
logger.info("Demagnetization processing")
|
347
348
|
i1min = simulation.get('i1min', abs(i1max/3))
|
348
|
-
num_steps = 7
|
349
|
+
num_steps = simulation.get('num_demag_cur_steps', 7)
|
349
350
|
b = (i1min-abs(i1max))/np.log(i1min/abs(i1max))
|
350
351
|
a = abs(i1max)/b
|
351
352
|
xtab = np.linspace(i1min/abs(i1max),
|
@@ -82,14 +82,14 @@ rotate({
|
|
82
82
|
mode = "save" -- save initial model state
|
83
83
|
})
|
84
84
|
|
85
|
+
rotate({angle=phi, mode="absolute"})
|
86
|
+
|
85
87
|
file_psi = io.open("psi-torq-rem.dat","w")
|
86
88
|
for i=1, #curvec do
|
87
|
-
print(string.format(" current: %d/%d %g, %g, %g\n",
|
88
|
-
i, #curvec, curvec[i][1], curvec[i][2], curvec[i][3]))
|
89
|
-
|
90
|
-
rotate({angle=phi, mode="absolute"})
|
91
89
|
psi, tq, rr = calc_flux_torq_rem(curvec[i])
|
92
90
|
|
91
|
+
print(string.format(" current: %d/%d %g, %g, %g torque %g rr %g\n",
|
92
|
+
i, #curvec, curvec[i][1], curvec[i][2], curvec[i][3], tq, rr))
|
93
93
|
file_psi:write(string.format("%g ", phi))
|
94
94
|
for k=1, 3 do
|
95
95
|
file_psi:write(string.format("%g ", curvec[i][k]))
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
m.yoke_diam = dy1
|
3
|
+
m.inside_diam = da1
|
4
|
+
|
5
|
+
m.wdg_location = -1 -- for gen_windings
|
6
|
+
|
7
|
+
m.slot_angle = ${model['slot_angle']*1e3}
|
8
|
+
m.slot_height = ${model['slot_height']*1e3}
|
9
|
+
m.slot_h1 = ${model['slot_h1']*1e3}
|
10
|
+
m.slot_h2 = ${model['slot_h2']*1e3}
|
11
|
+
m.slot_bk = ${model['slot_bk']*1e3}
|
12
|
+
m.slot_width = ${model['slot_width']*1e3}
|
13
|
+
m.slot_r1 = ${model['slot_r1']*1e3}
|
14
|
+
m.slot_r2 = ${model['slot_r2']*1e3}
|
15
|
+
m.middle_line = ${model.get('middle_line',0)}
|
16
|
+
m.tooth_width = ${model.get('tooth_width',0)*1e3}
|
17
|
+
m.slot_topwidth = ${model.get('slot_topwidth',0)*1e3}
|
18
|
+
|
19
|
+
m.zeroangl = ${model.get('zeroangle',0)}
|
20
|
+
m.rlength = ${model.get('rlength',1)*100}
|
21
|
+
|
22
|
+
% if model.get('ac_loss', False):
|
23
|
+
m.ac_loss = ${model.get('ac_loss', 6)}
|
24
|
+
% endif
|
25
|
+
m.mcvkey_yoke = mcvkey_yoke
|
26
|
+
|
27
|
+
pre_models("STATOR_KS1")
|
28
|
+
|
29
|
+
if mcvkey_teeth ~= nil then
|
30
|
+
if m.inside_diam > m.yoke_diam then
|
31
|
+
r = (m.inside_diam - m.slot_height)/2
|
32
|
+
else
|
33
|
+
r = (m.inside_diam + m.slot_height)/2
|
34
|
+
end
|
35
|
+
x0, y0 = pr2c(r, 2*math.pi/m.tot_num_slot + m.zeroangl/180*math.pi)
|
36
|
+
def_mat_fm_nlin(x0, y0, "blue", mcvkey_teeth, m.rlength)
|
37
|
+
end
|
38
|
+
|
39
|
+
%if model.get('thcond', 0) and model.get('thcap', 0):
|
40
|
+
stator_thcond = ${model.get('thcond', 24)}
|
41
|
+
stator_thcap = ${model.get('thcap', 480)}
|
42
|
+
stator_density = ${model.get('density', 7700)}
|
43
|
+
%endif
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: femagtools
|
3
|
-
Version: 1.8.
|
3
|
+
Version: 1.8.20
|
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
|