femagtools 1.7.3__py3-none-any.whl → 1.7.4__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/airgap.py +11 -0
- femagtools/bch.py +28 -20
- femagtools/dxfsl/fslrenderer.py +3 -2
- femagtools/fsl.py +8 -17
- femagtools/heat_source_network.py +403 -0
- femagtools/machine/pm.py +78 -4
- femagtools/machine/utils.py +109 -51
- femagtools/model.py +12 -1
- femagtools/parstudy.py +4 -5
- femagtools/plot/char.py +5 -2
- femagtools/templates/basic_modpar.mako +2 -0
- femagtools/templates/{therm-static.mako → therm_static.mako} +15 -13
- femagtools/windings.py +5 -2
- {femagtools-1.7.3.dist-info → femagtools-1.7.4.dist-info}/METADATA +2 -2
- {femagtools-1.7.3.dist-info → femagtools-1.7.4.dist-info}/RECORD +22 -20
- {femagtools-1.7.3.dist-info → femagtools-1.7.4.dist-info}/WHEEL +1 -1
- tests/test_heat_source_network.py +21 -0
- tests/test_machine.py +4 -4
- {femagtools-1.7.3.dist-info → femagtools-1.7.4.dist-info}/LICENSE +0 -0
- {femagtools-1.7.3.dist-info → femagtools-1.7.4.dist-info}/entry_points.txt +0 -0
- {femagtools-1.7.3.dist-info → femagtools-1.7.4.dist-info}/top_level.txt +0 -0
femagtools/__init__.py
CHANGED
femagtools/airgap.py
CHANGED
@@ -52,3 +52,14 @@ def read(filename, pmod=0):
|
|
52
52
|
return(dict())
|
53
53
|
|
54
54
|
return fft(bag[0], bag[1], pmod)
|
55
|
+
|
56
|
+
if __name__ == '__main__':
|
57
|
+
import sys
|
58
|
+
import matplotlib.pyplot as plt
|
59
|
+
import femagtools.plot.fluxdens
|
60
|
+
ag = read(sys.argv[1])
|
61
|
+
fig, axs = plt.subplots(nrows=2)
|
62
|
+
femagtools.plot.fluxdens.airgap(ag, ax=axs[0])
|
63
|
+
femagtools.plot.fluxdens.airgap_fft(ag, ax=axs[1])
|
64
|
+
fig.tight_layout()
|
65
|
+
plt.show()
|
femagtools/bch.py
CHANGED
@@ -70,7 +70,7 @@ def sttylosses(losses):
|
|
70
70
|
except KeyError:
|
71
71
|
pass
|
72
72
|
return d
|
73
|
-
|
73
|
+
|
74
74
|
if sregs == {'StYoke', 'StTeeth'}:
|
75
75
|
return hysteddy('StYoke', 'StTeeth', losses)
|
76
76
|
if sregs == {'StJo', 'StZa'}:
|
@@ -95,9 +95,9 @@ def sttylosses(losses):
|
|
95
95
|
return l
|
96
96
|
return {}
|
97
97
|
|
98
|
-
def losses_mapping_external_rotor(losses):
|
98
|
+
def losses_mapping_external_rotor(losses):
|
99
99
|
styoke = 'rotor'
|
100
|
-
rotor = 'Iron'
|
100
|
+
rotor = 'Iron'
|
101
101
|
d = {}
|
102
102
|
try:
|
103
103
|
d['styoke'] = losses[styoke]
|
@@ -1046,7 +1046,7 @@ class Reader:
|
|
1046
1046
|
'''
|
1047
1047
|
diff = np.floor(np.abs(np.diff(idList)))
|
1048
1048
|
if idList[-1] == 0 and len(idList) > 2 and \
|
1049
|
-
|
1049
|
+
diff[-1] == 0:
|
1050
1050
|
idList = idList[:-1]
|
1051
1051
|
return idList
|
1052
1052
|
|
@@ -1062,7 +1062,8 @@ class Reader:
|
|
1062
1062
|
m.append([floatnan(x) for x in rec])
|
1063
1063
|
|
1064
1064
|
m = np.array(m).T
|
1065
|
-
|
1065
|
+
d = np.diff(m[1])
|
1066
|
+
ncols = (len(d)+1)//(len(d[d < 0])+1)
|
1066
1067
|
if ncols == 1 and len(m[1]) > 1 and m[1][0] != m[1][1]: # simple correction
|
1067
1068
|
ncols = 2
|
1068
1069
|
iq = np.reshape(m[1], (-1, ncols))[0]
|
@@ -1071,7 +1072,8 @@ class Reader:
|
|
1071
1072
|
ncols = ncols-1
|
1072
1073
|
|
1073
1074
|
id = np.reshape(m[0], (-1, ncols)).T[0]
|
1074
|
-
id
|
1075
|
+
if id[0] >= 0:
|
1076
|
+
id = self.__removeTrailingZero(id)
|
1075
1077
|
nrows = len(id)
|
1076
1078
|
if nrows > 1 and id[nrows-1] < id[nrows-2]:
|
1077
1079
|
nrows = nrows-1
|
@@ -1096,7 +1098,8 @@ class Reader:
|
|
1096
1098
|
m.append([floatnan(x) for x in rec])
|
1097
1099
|
|
1098
1100
|
m = np.array(m).T
|
1099
|
-
|
1101
|
+
d = np.diff(m[1])
|
1102
|
+
ncols = (len(d)+1)//(len(d[d < 0])+1)
|
1100
1103
|
if ncols == 1 and len(m[1]) > 1 and m[1][0] != m[1][1]: # simple correction
|
1101
1104
|
ncols = 2
|
1102
1105
|
iq = np.linspace(np.min(m[1]), np.max(m[1]), ncols)
|
@@ -1105,7 +1108,8 @@ class Reader:
|
|
1105
1108
|
ncols = ncols-1
|
1106
1109
|
|
1107
1110
|
id = np.reshape(m[0], (-1, ncols)).T[0]
|
1108
|
-
id
|
1111
|
+
if id[0] >= 0:
|
1112
|
+
id = self.__removeTrailingZero(id)
|
1109
1113
|
nrows = len(id)
|
1110
1114
|
if nrows > 1 and id[nrows-1] < id[nrows-2]:
|
1111
1115
|
nrows = nrows-1
|
@@ -1168,7 +1172,7 @@ class Reader:
|
|
1168
1172
|
for name in content[2].split('\t') if name][2:]
|
1169
1173
|
# outer rotor motor
|
1170
1174
|
if 'Iron' in subregs and \
|
1171
|
-
'Rotor' in subregs:
|
1175
|
+
'Rotor' in subregs:
|
1172
1176
|
self.external_rotor = True
|
1173
1177
|
logger.info("Stator Subregions: %s", subregs)
|
1174
1178
|
speed = float(content[0].split()[-1])/60.
|
@@ -1184,7 +1188,11 @@ class Reader:
|
|
1184
1188
|
if not m:
|
1185
1189
|
return
|
1186
1190
|
m = np.array(m).T
|
1187
|
-
|
1191
|
+
d = np.diff(m[1])
|
1192
|
+
if self.ldq:
|
1193
|
+
ncols = (len(d)+1)//(len(d[d > 0])+1)
|
1194
|
+
else:
|
1195
|
+
ncols = (len(d)+1)//(len(d[d < 0])+1)
|
1188
1196
|
if ncols == 1 and len(m[1]) > 1 and m[1][0] != m[1][1]:
|
1189
1197
|
ncols = 2
|
1190
1198
|
id = np.reshape(m[0], (-1, ncols)).T[0]
|
@@ -1229,9 +1237,9 @@ class Reader:
|
|
1229
1237
|
(nrows, ncols)).T.tolist()
|
1230
1238
|
for k, v in zip(cols, m[2:])})
|
1231
1239
|
ls['speed'] = speed
|
1232
|
-
if self.external_rotor:
|
1240
|
+
if self.external_rotor:
|
1233
1241
|
ls.update(losses_mapping_external_rotor(ls))
|
1234
|
-
else:
|
1242
|
+
else:
|
1235
1243
|
ls.update(sttylosses(ls))
|
1236
1244
|
if self.ldq:
|
1237
1245
|
self.ldq['losses'] = ls
|
@@ -1487,9 +1495,9 @@ class Reader:
|
|
1487
1495
|
def __read_losses(self, content):
|
1488
1496
|
losses = {}
|
1489
1497
|
i = 0
|
1490
|
-
# check if external rotor
|
1498
|
+
# check if external rotor
|
1491
1499
|
if self.weights[0][1] == 0.0 and \
|
1492
|
-
self.weights[0][-1] > 0:
|
1500
|
+
self.weights[0][-1] > 0:
|
1493
1501
|
self.external_rotor = True
|
1494
1502
|
# find results for angle:
|
1495
1503
|
while True:
|
@@ -1548,8 +1556,8 @@ class Reader:
|
|
1548
1556
|
losses['total'] += losses['staza']+losses['stajo']
|
1549
1557
|
elif len(rec) == 1:
|
1550
1558
|
t = l.split(':')[-1].strip()
|
1551
|
-
if t == 'Iron':
|
1552
|
-
if self.external_rotor:
|
1559
|
+
if t == 'Iron':
|
1560
|
+
if self.external_rotor:
|
1553
1561
|
losses['rotfe'] = floatnan(rec[0])
|
1554
1562
|
losses['total'] += losses['rotfe']
|
1555
1563
|
else:
|
@@ -1561,14 +1569,14 @@ class Reader:
|
|
1561
1569
|
continue
|
1562
1570
|
|
1563
1571
|
if _rotloss.search(l):
|
1564
|
-
if l.find('StZa') > -1:
|
1572
|
+
if l.find('StZa') > -1:
|
1565
1573
|
self.external_rotor = True
|
1566
1574
|
rec = self.__findNums(content[i+2])
|
1567
1575
|
if len(rec) == 1:
|
1568
|
-
if self.external_rotor:
|
1576
|
+
if self.external_rotor:
|
1569
1577
|
losses['staza'] = floatnan(rec[0])
|
1570
1578
|
losses['total'] += losses['staza']
|
1571
|
-
else:
|
1579
|
+
else:
|
1572
1580
|
rotfe = floatnan(rec[0])
|
1573
1581
|
losses['rotfe'] += rotfe
|
1574
1582
|
losses['total'] += rotfe
|
@@ -1602,7 +1610,7 @@ class Reader:
|
|
1602
1610
|
if content[i+1].split() == ['rotf', '----']:
|
1603
1611
|
losses['rotfe'] = sum([floatnan(x) for x in rec])
|
1604
1612
|
losses['total'] += losses['rotfe']
|
1605
|
-
|
1613
|
+
|
1606
1614
|
if content[i+1].split() == ['Iron', '----']: # external rotor
|
1607
1615
|
losses['rotfe'] = sum([floatnan(x) for x in rec])
|
1608
1616
|
losses['total'] += losses['rotfe']
|
femagtools/dxfsl/fslrenderer.py
CHANGED
@@ -189,7 +189,7 @@ class FslRenderer(object):
|
|
189
189
|
'-- min_corner = {}, {}'.format(
|
190
190
|
geom.start_min_corner(0),
|
191
191
|
geom.start_min_corner(1)),
|
192
|
-
'-- max_corner = {}, {}'.format(
|
192
|
+
'-- max_corner start = {}, {}'.format(
|
193
193
|
geom.start_max_corner(0),
|
194
194
|
geom.start_max_corner(1)),
|
195
195
|
'\n']
|
@@ -211,11 +211,12 @@ class FslRenderer(object):
|
|
211
211
|
'r, phi = c2pr(x0, y0)',
|
212
212
|
'x1, y1 = pr2c(r1, phi)',
|
213
213
|
'x2, y2 = pr2c(r1, {}*math.pi/parts)'.format(slice),
|
214
|
+
f'r = {geom.dist_end_max_corner()}',
|
214
215
|
'x3, y3 = pr2c(r, {}*math.pi/parts)'.format(slice),
|
215
216
|
'nc_line(x0, y0, x1, y1, 0)',
|
216
217
|
'nc_circle_m(x1, y1, x2, y2, 0.0, 0.0, 0)',
|
217
218
|
'nc_line(x2, y2, x3, y3, 0)',
|
218
|
-
'x0, y0 = pr2c(r1 - hair/2, math.pi/parts)',
|
219
|
+
'x0, y0 = pr2c(r1 - hair/2, math.pi/parts/2)',
|
219
220
|
'create_mesh_se(x0, y0)',
|
220
221
|
'\n',
|
221
222
|
'outer_da_start = {}'.format(
|
femagtools/fsl.py
CHANGED
@@ -106,6 +106,7 @@ class Builder:
|
|
106
106
|
params['show_plots'] = model.stator[templ].get('plot', False)
|
107
107
|
params['write_fsl'] = True
|
108
108
|
params['airgap'] = -1.0
|
109
|
+
params['nodedist'] = model.stator.get('nodedist', 1)
|
109
110
|
pos = 'in' if model.external_rotor else 'out'
|
110
111
|
params['part'] = ('stator', pos)
|
111
112
|
conv = convert(model.stator['dxffile']['name'], **params)
|
@@ -491,6 +492,11 @@ class Builder:
|
|
491
492
|
if not hasattr(model, 'stator'):
|
492
493
|
setattr(model, 'stator', {})
|
493
494
|
model.stator['num_slots'] = conv.get('tot_num_slot')
|
495
|
+
if model.get('num_agnodes', 0) == 0:
|
496
|
+
model.set_value('agndst', conv['agndst']*1e-3)
|
497
|
+
logger.info("num poles %d num slots %d outer diameter %.4f m agndst %.4f mm",
|
498
|
+
model.poles, model.stator['num_slots'],
|
499
|
+
model.outer_diam, model.agndst*1e3)
|
494
500
|
if params['full_model']:
|
495
501
|
model.stator['num_slots_gen'] = model.stator['num_slots']
|
496
502
|
else:
|
@@ -531,21 +537,6 @@ class Builder:
|
|
531
537
|
logger.info("create new model '%s'", model.name)
|
532
538
|
if model.is_dxffile():
|
533
539
|
self.prepare_model_with_dxf(model)
|
534
|
-
if model.get('num_agnodes', 0) == 0:
|
535
|
-
from femagtools.dxfsl.fslrenderer import agndst
|
536
|
-
ag = model.get('airgap')
|
537
|
-
model.set_value(
|
538
|
-
'agndst',
|
539
|
-
agndst(model.get('bore_diam'),
|
540
|
-
model.get('bore_diam') - 2*ag,
|
541
|
-
model.stator.get('num_slots'),
|
542
|
-
model.get('poles'),
|
543
|
-
model.dxffile.get('nodedist')))
|
544
|
-
|
545
|
-
logger.info(" num poles %d num slots %d outer diameter %.4f m",
|
546
|
-
model.poles, model.stator['num_slots'],
|
547
|
-
model.outer_diam)
|
548
|
-
|
549
540
|
else:
|
550
541
|
self.prepare_stator(model)
|
551
542
|
if hasattr(model, 'magnet'):
|
@@ -553,9 +544,9 @@ class Builder:
|
|
553
544
|
self.prepare_diameter(model)
|
554
545
|
if self.fsl_stator and model.get('num_agnodes', 0) == 0:
|
555
546
|
from femagtools.dxfsl.fslrenderer import agndst
|
556
|
-
if model.get('agndst'):
|
547
|
+
if model.get('agndst',0):
|
557
548
|
pass
|
558
|
-
else:
|
549
|
+
else:
|
559
550
|
ag = model.get('airgap')
|
560
551
|
model.set_value(
|
561
552
|
'agndst',
|
@@ -0,0 +1,403 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
heat source network analysis
|
4
|
+
|
5
|
+
@author: werner b. vetter
|
6
|
+
"""
|
7
|
+
|
8
|
+
import xml.etree.ElementTree as ET
|
9
|
+
import numpy as np
|
10
|
+
import json
|
11
|
+
|
12
|
+
|
13
|
+
class HeatSourceNetwork:
|
14
|
+
#Read the json-file with the heat source network definition
|
15
|
+
|
16
|
+
#If you modify the netlist, you must call the command "process()" again
|
17
|
+
|
18
|
+
#Parameter:
|
19
|
+
# filename (str): Filename of heat source network with extension (.hsn)
|
20
|
+
|
21
|
+
def __init__(self, netlist):
|
22
|
+
self.netlist = netlist
|
23
|
+
self.process()
|
24
|
+
|
25
|
+
def get_node_names(self):
|
26
|
+
return [n['Name'] for n in self.netlist['Nodes']]
|
27
|
+
|
28
|
+
def process(self):
|
29
|
+
#Set up independent admittance matrix and the source vector
|
30
|
+
|
31
|
+
self.nodes = []
|
32
|
+
for branch in self.netlist['Branches']:
|
33
|
+
branch_nodes = branch['Nodes']
|
34
|
+
for nd in range(len(branch_nodes)):
|
35
|
+
node = branch_nodes[nd]
|
36
|
+
if not(node in self.nodes):
|
37
|
+
self.nodes.append(node)
|
38
|
+
self.nodes.sort()
|
39
|
+
|
40
|
+
self.G = np.zeros((len(self.nodes)-1,len(self.nodes)-1))
|
41
|
+
self.P = np.zeros(len(self.nodes)-1)
|
42
|
+
|
43
|
+
for branch in self.netlist['Branches']:
|
44
|
+
ind1 = self.nodes.index(branch['Nodes'][0])-1
|
45
|
+
ind2 = self.nodes.index(branch['Nodes'][1])-1
|
46
|
+
if branch['Type'] == 'R_th':
|
47
|
+
if ind1 != -1:
|
48
|
+
self.G[ind1,ind1] = self.G[ind1,ind1] + 1/branch['val']
|
49
|
+
if ind2 != -1:
|
50
|
+
self.G[ind2,ind2] = self.G[ind2,ind2] + 1/branch['val']
|
51
|
+
if ind1 != -1 and ind2 != -1:
|
52
|
+
self.G[ind1,ind2] = self.G[ind1,ind2] - 1/branch['val']
|
53
|
+
self.G[ind2,ind1] = self.G[ind2,ind1] - 1/branch['val']
|
54
|
+
|
55
|
+
if branch['Type'] == 'Power_Source':
|
56
|
+
if ind1 == -1:
|
57
|
+
self.P[ind2] = self.P[ind2] + branch['val']
|
58
|
+
if ind2 == -1:
|
59
|
+
self.P[ind1] = self.P[ind1] + branch['val']
|
60
|
+
|
61
|
+
if branch['Type'] == 'Temperature_Source':
|
62
|
+
if ind1 == -1:
|
63
|
+
self.P[ind2] = self.P[ind2] + branch['T_val']/branch['R_val']
|
64
|
+
self.G[ind2,ind2] = self.G[ind2,ind2] + 1/branch['R_val']
|
65
|
+
if ind2 == -1:
|
66
|
+
self.P[ind1] = self.P[ind1] + branch['T_val']/branch['R_val']
|
67
|
+
self.G[ind1,ind1] = self.G[ind1,ind1] + 1/branch['R_val']
|
68
|
+
|
69
|
+
|
70
|
+
def solve(self):
|
71
|
+
#Solve the system of equations
|
72
|
+
|
73
|
+
self.T = np.linalg.solve(self.G, self.P)
|
74
|
+
return(self.T)
|
75
|
+
|
76
|
+
def draw(self,filename):
|
77
|
+
#Creates an xml file of the network that can be displayed with draw.io
|
78
|
+
|
79
|
+
#Parameter:
|
80
|
+
# filename (str): Filename of diagram with extension (.xml)
|
81
|
+
|
82
|
+
HeatSourceDiagram.draw(self.netlist,filename)
|
83
|
+
return()
|
84
|
+
|
85
|
+
|
86
|
+
def read(filename):
|
87
|
+
with open(filename) as fp:
|
88
|
+
netlist = json.load(fp)
|
89
|
+
return HeatSourceNetwork(netlist)
|
90
|
+
|
91
|
+
|
92
|
+
class HeatSourceDiagram:
|
93
|
+
#Init a diagram to create a diagram of a heat source netlist
|
94
|
+
def __init__(self):
|
95
|
+
self.mxfile = self.createFile()
|
96
|
+
self.diagram = self.addDiagram(self.mxfile)
|
97
|
+
self.graph = self.addGraphModel(self.diagram)
|
98
|
+
self.root = self.addRoot(self.graph)
|
99
|
+
mxCell_dict = {"id":"0"}
|
100
|
+
self.addCell(self.root,mxCell_dict)
|
101
|
+
mxCell_dict = {"id":"1", "parent":"0"}
|
102
|
+
self.addCell(self.root,mxCell_dict)
|
103
|
+
|
104
|
+
|
105
|
+
def writeFile(self,filename):
|
106
|
+
tree = ET.ElementTree(self.mxfile)
|
107
|
+
tree.write(filename, encoding="utf-8")
|
108
|
+
|
109
|
+
|
110
|
+
def createFile(self):
|
111
|
+
mxfile = ET.Element('mxfile')
|
112
|
+
mxfile.attrib = {"host":"Electron",
|
113
|
+
"modified":"2023-03-08T08:47:19.445Z",
|
114
|
+
"agent":"5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/20.8.16 Chrome/106.0.5249.199 Electron/21.4.0 Safari/537.36",
|
115
|
+
"etag":"5VmoL6vL6aGyHSSOdqAj",
|
116
|
+
"version":"20.8.16",
|
117
|
+
"type":"device"
|
118
|
+
}
|
119
|
+
return mxfile
|
120
|
+
|
121
|
+
def addDiagram(self,parent):
|
122
|
+
diagram = ET.SubElement(parent, 'diagram')
|
123
|
+
diagram.attrib = {"name":"Seite-1",
|
124
|
+
"id":"5XRmyqCaqz2rHzUy15r-"
|
125
|
+
}
|
126
|
+
return diagram
|
127
|
+
|
128
|
+
def addGraphModel(self,parent):
|
129
|
+
mxGraphModel = ET.SubElement(parent, 'mxGraphModel')
|
130
|
+
mxGraphModel.attrib = {"dx":"770",
|
131
|
+
"dy":"569",
|
132
|
+
"grid":"1",
|
133
|
+
"gridSize":"10",
|
134
|
+
"guides":"1",
|
135
|
+
"tooltips":"1",
|
136
|
+
"connect":"1",
|
137
|
+
"arrows":"1",
|
138
|
+
"fold":"1",
|
139
|
+
"page":"1",
|
140
|
+
"pageScale":"1",
|
141
|
+
"pageWidth":"827",
|
142
|
+
"pageHeight":"1169",
|
143
|
+
"math":"0",
|
144
|
+
"shadow":"0"
|
145
|
+
}
|
146
|
+
return mxGraphModel
|
147
|
+
|
148
|
+
def addRoot(self,parent):
|
149
|
+
root = ET.SubElement(parent, 'root')
|
150
|
+
root.attrib = {}
|
151
|
+
return root
|
152
|
+
|
153
|
+
def addCell(self,parent,cell_dict):
|
154
|
+
mxCell = ET.SubElement(parent, 'mxCell')
|
155
|
+
mxCell.attrib = cell_dict
|
156
|
+
return mxCell
|
157
|
+
|
158
|
+
def addGeometry(self,parent,geometry_dict):
|
159
|
+
mxGeometry = ET.SubElement(parent, 'mxGeometry')
|
160
|
+
mxGeometry.attrib = geometry_dict
|
161
|
+
return mxGeometry
|
162
|
+
|
163
|
+
def addPntArray(self,parent,pntArray):
|
164
|
+
array = ET.SubElement(parent, 'Array')
|
165
|
+
array.attrib = {"as":"points"}
|
166
|
+
mxPoint = ET.SubElement(array, 'mxPoint')
|
167
|
+
if isinstance(pntArray[0], list):
|
168
|
+
for i in range(len(pntArray)):
|
169
|
+
pnt = pntArray[i]
|
170
|
+
mxPoint.attrib = {"x":str(pnt[0]), "y":str(pnt[1])}
|
171
|
+
else:
|
172
|
+
pnt = pntArray
|
173
|
+
mxPoint.attrib = {"x":str(pnt[0]), "y":str(pnt[1])}
|
174
|
+
return array
|
175
|
+
|
176
|
+
def addNode(self,nodeKey,x,y):
|
177
|
+
mxCell_dict = {"id":"node_"+str(nodeKey),
|
178
|
+
"value":str(nodeKey),
|
179
|
+
"style":"shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];fillColor=none;resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;verticalAlign=top;spacingBottom=0;spacingTop=-5;fontFamily=Verdana;fontSize=12;",
|
180
|
+
"vertex":"1",
|
181
|
+
"parent":"1"
|
182
|
+
}
|
183
|
+
cell = self.addCell(self.root,mxCell_dict)
|
184
|
+
mxGeometry_dict = {"x":str(x-20),
|
185
|
+
"y":str(y-20),
|
186
|
+
"width":"40",
|
187
|
+
"height":"40",
|
188
|
+
"as":"geometry"
|
189
|
+
}
|
190
|
+
geometry = self.addGeometry(cell,mxGeometry_dict)
|
191
|
+
|
192
|
+
return geometry
|
193
|
+
|
194
|
+
def addPowerSource(self,name,value,x,y,rotation=0):
|
195
|
+
mxCell_dict = {"id":name,
|
196
|
+
"value":"Name = "+name+"\nLosses = "+str(value),
|
197
|
+
"style":"shape=stencil(vVXhboMgEH4a/i4I8QEWur7AHqCh9DZJFcyJ7fb2Q8CtteJWs42YmPsOvrvv44yEi66SLRBGjWyA8A1hbCN2okcE43bPtkfls8xvqGK2oDTG5xizMZZdC8pF8CRRy32dTnYO7RHO+uAShzYVoHZDlj8R+uj3DA8XyhrjSbQ13VXmIu/JpDb+LH2LZKn8e4oeyhi3vkIDDjC1HdEvlQkgbPvjSsW6Smy+EhceyQnmYi/V8RVtbw6z/UVPla0teiC+QzXC+DasZX043FXOw/kLng5Ajjq2llOc0cXFi0VYKZiGtdxVK4fpu0mM6caeIOdHuUg9MtTaXDCU91AEYzINLvi5SlpxV2fz4j5H4j/UQV3rtstbez2txa9N6zp3J858U37e3inJX7gbjt18cgGNf4QAfAA=);whiteSpace=wrap;html=1;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;rotation="+str(rotation)+";",
|
198
|
+
"vertex":"1",
|
199
|
+
"parent":"1"
|
200
|
+
}
|
201
|
+
cell = self.addCell(self.root,mxCell_dict)
|
202
|
+
mxGeometry_dict = {"x":str(x-60),
|
203
|
+
"y":str(y-30),
|
204
|
+
"width":"120",
|
205
|
+
"height":"60",
|
206
|
+
"as":"geometry"
|
207
|
+
}
|
208
|
+
geometry = self.addGeometry(cell,mxGeometry_dict)
|
209
|
+
|
210
|
+
return geometry
|
211
|
+
|
212
|
+
|
213
|
+
def addTemperatureSource(self,name,T,Rth,x,y,rotation=0):
|
214
|
+
mxCell_dict = {"id":name,
|
215
|
+
"value":"Name = "+name+"\nT = "+str(T)+"\nRth = "+str(Rth),
|
216
|
+
"style":"shape=stencil(vVXbboMwDP2avE65jO15YusPdO9TSt0RNSQohHb7+4UEtpaSbGUVERKynRz7HJuAWN6UvAZEseIVIPaMKH2FqgbDbWvgba1bU7gwdTvKECYYB/sYbDrYvKmhsMF54EbwjexPNtboPRzF1vYYQpVghO2i7AXhJ7ene1heaKUciNCqOYucxB0YF8qdxR8BrE//2Vt3WbAdB1GBBdOXHbw/NHsHoqs/ZyLzMtHpTCx3nhhhlm94sX83ulXbyfqCpoWW2jhHePtsiLKVX2l+putVTMPpBo8HIAYdSosxjvBi+U4bmEkY+5Wuqubd9F0EhnClDxDTI0tCDwhSqBOE7BoIL0ykwISes6iRqyqbJvc9EkuwAylF3cSlPZ9WcrNpnaXu4//FJWxBcXdCyoW+KTIamvsZ2txC3lEZDzfASFNJt6jrwK9tSl+pFzen94Y/u3d8AQ==);whiteSpace=wrap;html=1;verticalAlign=top;spacing=0;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;rotation="+str(rotation)+";",
|
217
|
+
"vertex":"1",
|
218
|
+
"parent":"1"
|
219
|
+
}
|
220
|
+
cell = self.addCell(self.root,mxCell_dict)
|
221
|
+
mxGeometry_dict = {"x":str(x-60),
|
222
|
+
"y":str(y-30),
|
223
|
+
"width":"120",
|
224
|
+
"height":"60",
|
225
|
+
"as":"geometry"
|
226
|
+
}
|
227
|
+
geometry = self.addGeometry(cell,mxGeometry_dict)
|
228
|
+
|
229
|
+
return geometry
|
230
|
+
|
231
|
+
def addResistor(self,name,value,x,y,rotation=0):
|
232
|
+
mxCell_dict = {"id":name,
|
233
|
+
"value":"Name = "+name+"\nR = "+str(value),
|
234
|
+
"style":"shape=stencil(vVRtbsMgDD0NfycC6wEq1h5gN6Cpt6AmEAFtt9sXMOlniNpoGoqU+Dl5fs+2QrhwjeyBMKplB4R/EMY+wSnnjQ2PAW8QfKcYHjFkNMfS9VB7BA/SKrlpATPOW7ODo9r6TKF0A1b5mOUrQpfhnXhxURutA4ky2t1krvKBTCodvqU/SJbL/+bobYFxHyp04MEiXiF68ZYBwtZPV6rmVWLjlbgISMkwFxtZ776t2evtqD7saW3aOB2K91SNML5OZ9qfjbMq9XB8wHfzLzGjspLhgi0uvoyFmX5pOtOqehmX7yExpDtzgFI72CT1wNAqfcWweIUiNaYgcKKfs6xVLykbN3feiP9wd7uod+oHIXlVqz9d1YeNTCj+JRNwAg==);whiteSpace=wrap;html=1;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;rotation="+str(rotation)+";",
|
235
|
+
"vertex":"1",
|
236
|
+
"parent":"1"
|
237
|
+
}
|
238
|
+
cell = self.addCell(self.root,mxCell_dict)
|
239
|
+
mxGeometry_dict = {"x":str(x-60),
|
240
|
+
"y":str(y-10),
|
241
|
+
"width":"120",
|
242
|
+
"height":"20",
|
243
|
+
"as":"geometry"
|
244
|
+
}
|
245
|
+
geometry = self.addGeometry(cell,mxGeometry_dict)
|
246
|
+
|
247
|
+
return geometry
|
248
|
+
|
249
|
+
def addCapacitor(self,name,value,x,y,rotation=0):
|
250
|
+
mxCell_dict = {"id":name,
|
251
|
+
"value":"Name = "+name+"\nC = "+str(value),
|
252
|
+
"style":"shape=stencil(zVXRUsMgEPwaXh0C+tBHB+1/UHoapgkwBFv9+wJHtaYhajqOMplJbjfs3d6RCeFiaKUDwqiRPRD+QBgT0kmlg/XxORItorcUwwOGjJZYDg5UQHAvvZabDpAZgrc7OOhtKBLatOB1SCx/JPQ+vpMuLpQ1Jopoa4ZPzBkfxaQ2cS99RbGS/q1EN3cYu5ihhwAe8QbRD3MFIGz97UzNskxsOhMXEakZ5mIj1e7Z2xeznawPe6psl6ZD8Z6zEcbXec3782lWtR5OD3g0/5oyVlYzXLHFxZP1sNAvzWu+KifT4bsgTnRv91BrB5uVPil02pwprH4ikRtTKXCmn4usNc315t5PxL9zN2r79YP76rD/3eAWeBtL/Iq5vO3iY84o/mEycAQ=);whiteSpace=wrap;html=1;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;rotation="+str(rotation)+";",
|
253
|
+
"vertex":"1",
|
254
|
+
"parent":"1"
|
255
|
+
}
|
256
|
+
cell = self.addCell(self.root,mxCell_dict)
|
257
|
+
mxGeometry_dict = {"x":str(x-60),
|
258
|
+
"y":str(y-18.75),
|
259
|
+
"width":"120",
|
260
|
+
"height":"37.5",
|
261
|
+
"as":"geometry"
|
262
|
+
}
|
263
|
+
geometry = self.addGeometry(cell,mxGeometry_dict)
|
264
|
+
|
265
|
+
return geometry
|
266
|
+
|
267
|
+
def addConnection(self,name,startID,startPnt,endID,endPnt,pntArray=0):
|
268
|
+
# Pnt = [X,Y] gibt die relative Position des Anschlusspunktes an
|
269
|
+
# X=0 => links
|
270
|
+
# X=1 => rechts
|
271
|
+
# Y=0 => oben
|
272
|
+
# Y=1 => unten
|
273
|
+
mxCell_dict = {"id":name,
|
274
|
+
"value":"",
|
275
|
+
"style":"endArrow=none;entryX="+str(endPnt[0])+";entryY="+str(endPnt[1])+";exitX="+str(startPnt[0])+";exitY="+str(startPnt[1])+";html=1;rounded=0;entryDx=0;entryDy=0;entryPerimeter=0;exitDx=0;exitDy=0;",
|
276
|
+
"edge":"1",
|
277
|
+
"parent":"1",
|
278
|
+
"source":startID,
|
279
|
+
"target":endID
|
280
|
+
}
|
281
|
+
cell = self.addCell(self.root,mxCell_dict)
|
282
|
+
mxGeometry_dict = {"width":"50",
|
283
|
+
"height":"50",
|
284
|
+
"relative":"1",
|
285
|
+
"as":"geometry"
|
286
|
+
}
|
287
|
+
geometry = self.addGeometry(cell,mxGeometry_dict)
|
288
|
+
|
289
|
+
if isinstance(pntArray, list):
|
290
|
+
self.addPntArray(geometry,pntArray)
|
291
|
+
|
292
|
+
return
|
293
|
+
|
294
|
+
def draw(netlist,file):
|
295
|
+
#Creates an xml file of the netlist that can be displayed with draw.io
|
296
|
+
|
297
|
+
#Parameter:
|
298
|
+
# netlist (list): List of branches
|
299
|
+
# file (str): Filename of diagram with extension (.xml)
|
300
|
+
|
301
|
+
# list of nodes
|
302
|
+
nodes = []
|
303
|
+
for branch in netlist['Branches']:
|
304
|
+
branch_nodes = branch['Nodes']
|
305
|
+
for nd in range(len(branch_nodes)):
|
306
|
+
node = branch_nodes[nd]
|
307
|
+
if not(node in nodes):
|
308
|
+
nodes.append(node)
|
309
|
+
nodes.sort()
|
310
|
+
|
311
|
+
#list of connections
|
312
|
+
connections = [None] * len(nodes)
|
313
|
+
for node in nodes:
|
314
|
+
connection = []
|
315
|
+
for branch in netlist['Branches']:
|
316
|
+
branch_nodes = branch['Nodes']
|
317
|
+
if node in branch_nodes:
|
318
|
+
for nd in branch_nodes:
|
319
|
+
if nd!=node and not(nd in connection):
|
320
|
+
connection.append(nd)
|
321
|
+
i = nodes.index(node)
|
322
|
+
connections[i]=connection
|
323
|
+
|
324
|
+
diagram = HeatSourceDiagram()
|
325
|
+
|
326
|
+
#determine nodes position
|
327
|
+
nd_pos = []
|
328
|
+
xstep = 250
|
329
|
+
ystep = 250
|
330
|
+
xpos = 0
|
331
|
+
ypos = ystep*len(connections)
|
332
|
+
nd_pos.append([xpos,ypos])
|
333
|
+
def_nd = [0]
|
334
|
+
ypos = ypos-ystep
|
335
|
+
for i in range(len(connections)):
|
336
|
+
#new nodes in connection
|
337
|
+
new_nd = []
|
338
|
+
for nd in connections[i]:
|
339
|
+
if nd>i and not(nd in new_nd) and not(nd in def_nd) :
|
340
|
+
new_nd.append(nd)
|
341
|
+
xpos = -xstep*(1+(len(new_nd)-1)/2)
|
342
|
+
draw = False
|
343
|
+
for nd in new_nd:
|
344
|
+
xpos = xpos+xstep
|
345
|
+
nd_pos.append([xpos,ypos])
|
346
|
+
def_nd.append(nd)
|
347
|
+
draw = True
|
348
|
+
#next level
|
349
|
+
if draw:
|
350
|
+
ypos = ypos-ystep
|
351
|
+
|
352
|
+
|
353
|
+
#draw nodes
|
354
|
+
for nd in def_nd:
|
355
|
+
i = def_nd.index(nd)
|
356
|
+
diagram.addNode(nd,nd_pos[i][0],nd_pos[i][1])
|
357
|
+
|
358
|
+
# draw branches
|
359
|
+
for branch in netlist['Branches']:
|
360
|
+
#print(branch)
|
361
|
+
xb = 0
|
362
|
+
yb = 0
|
363
|
+
for nd in branch['Nodes']:
|
364
|
+
i = def_nd.index(nd)
|
365
|
+
xb = xb+nd_pos[i][0]/len(branch['Nodes'])
|
366
|
+
yb = yb+nd_pos[i][1]/len(branch['Nodes'])
|
367
|
+
if len(branch['Nodes'])==2:
|
368
|
+
i1 = def_nd.index(branch['Nodes'][0])
|
369
|
+
i2 = def_nd.index(branch['Nodes'][1])
|
370
|
+
dx = nd_pos[i1][0]-nd_pos[i2][0]
|
371
|
+
dy = nd_pos[i1][1]-nd_pos[i2][1]
|
372
|
+
angle = np.arctan2(dy,dx)*180/np.pi
|
373
|
+
else:
|
374
|
+
angle = 0
|
375
|
+
|
376
|
+
if branch['Type']=='R_th':
|
377
|
+
diagram.addResistor(branch['Name'],branch['val'],xb,yb,angle)
|
378
|
+
|
379
|
+
if branch['Type']=='C_th':
|
380
|
+
diagram.addCapacitor(branch['Name'],branch['val'],xb,yb,angle)
|
381
|
+
|
382
|
+
if branch['Type']=='Power_Source':
|
383
|
+
diagram.addPowerSource(branch['Name'],branch['val'],xb,yb,angle)
|
384
|
+
|
385
|
+
if branch['Type']=='Temperature_Source':
|
386
|
+
diagram.addTemperatureSource(branch['Name'],branch['T_val'],branch['R_val'],xb,yb,angle)
|
387
|
+
|
388
|
+
# draw connections
|
389
|
+
num_con = 0
|
390
|
+
for branch in netlist['Branches']:
|
391
|
+
num_con = num_con+1
|
392
|
+
branch_id = branch['Name']
|
393
|
+
y_offset = 0.5
|
394
|
+
diagram.addConnection("connect"+str(num_con),\
|
395
|
+
"node_"+str(branch['Nodes'][0]),[0,0],branch_id,[1,y_offset])
|
396
|
+
num_con = num_con+1
|
397
|
+
diagram.addConnection("connect"+str(num_con),\
|
398
|
+
"node_"+str(branch['Nodes'][1]),[0,0],branch_id,[0,y_offset])
|
399
|
+
|
400
|
+
|
401
|
+
diagram.writeFile(file)
|
402
|
+
|
403
|
+
return
|