femagtools 1.7.3__py3-none-any.whl → 1.7.5__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.
@@ -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
femagtools/machine/pm.py CHANGED
@@ -5,9 +5,10 @@ import logging
5
5
  import warnings
6
6
  import numpy as np
7
7
  import numpy.linalg as la
8
- from .utils import iqd, betai1, skin_resistance, dqparident, KTH
8
+ from .utils import iqd, betai1, skin_resistance, dqparident, KTH, K
9
9
  import scipy.optimize as so
10
10
  import scipy.interpolate as ip
11
+ import scipy.integrate as ig
11
12
  from functools import partial
12
13
 
13
14
  logger = logging.getLogger(__name__)
@@ -1392,6 +1393,11 @@ class PmRelMachinePsidq(PmRelMachine):
1392
1393
 
1393
1394
  self._psid = ip.RectBivariateSpline(iq, id, psid).ev
1394
1395
  self._psiq = ip.RectBivariateSpline(iq, id, psiq).ev
1396
+ # used for transient
1397
+ self.psid = psid
1398
+ self.psiq = psiq
1399
+ self.id = id
1400
+ self.iq = iq
1395
1401
  try:
1396
1402
  pfe = kwargs['losses']
1397
1403
  if 'styoke_excess' in pfe and np.any(pfe['styoke_excess']):
@@ -1451,10 +1457,12 @@ class PmRelMachinePsidq(PmRelMachine):
1451
1457
  return iqmax, np.max(self.idrange)
1452
1458
 
1453
1459
  def iqd_plfe1(self, iq, id, f1):
1454
- stator_losskeys = ['styoke_eddy', 'styoke_hyst',
1455
- 'stteeth_eddy', 'stteeth_hyst']
1460
+ stator_losskeys = [k for k in ['styoke_eddy', 'styoke_hyst',
1461
+ 'stteeth_eddy', 'stteeth_hyst']
1462
+ if k in self._losses]
1456
1463
  if self.bertotti:
1457
- stator_losskeys += ['styoke_excess', 'stteeth_excess']
1464
+ stator_losskeys += [k for k in ('styoke_excess', 'stteeth_excess')
1465
+ if k in self._losses]
1458
1466
  return np.sum([
1459
1467
  self._losses[k](iq, id)*(f1/self.fo)**self.plexp[k] for
1460
1468
  k in tuple(stator_losskeys)], axis=0)
@@ -1478,3 +1486,70 @@ class PmRelMachinePsidq(PmRelMachine):
1478
1486
 
1479
1487
  def betai1_plmag(self, beta, i1, f1):
1480
1488
  return self.iqd_plmag(*iqd(beta, i1), f1)
1489
+
1490
+
1491
+ ### EXPERIMENTAL
1492
+
1493
+ def transient(self, u1, tload, speed,
1494
+ fault_type=3, # 'LLL', 'LL', 'LG',
1495
+ tend=0.1, nsamples=200):
1496
+
1497
+ tshort = 0
1498
+ w1 = 2*np.pi*self.p*speed
1499
+ i0 = self.iqd_torque(tload)
1500
+ res = so.minimize(
1501
+ np.linalg.norm, i0, method='SLSQP',
1502
+ constraints=(
1503
+ {'type': 'ineq',
1504
+ 'fun': lambda iqd: self.tmech_iqd(*iqd, speed) - tload},
1505
+ {'type': 'ineq',
1506
+ 'fun': lambda iqd: np.sqrt(2)*u1
1507
+ - la.norm(self.uqd(w1, *iqd))}))
1508
+ iqx, idx = res.x
1509
+ uq0, ud0 = self.uqd(w1, iqx, idx)
1510
+ logger.info("transient: Torque %f Nm, Speed %f rpm, Curr %f A",
1511
+ tload, speed*60, betai1(iqx, idx)[1])
1512
+ if fault_type == 3: # 3 phase short circuit
1513
+ USC = lambda t: np.zeros(2)
1514
+ else: # 2 phase short circuit
1515
+ #ustat = np.array([K(w1*x).dot((uq, ud)) for x in t])
1516
+ USC = lambda t: np.array([
1517
+ uq0/2*(1+np.cos(2*w1*t)),
1518
+ uq0/2*np.sin(2*w1*t)])
1519
+ U = lambda t: (uq0, ud0) if t < tshort else USC(t)
1520
+
1521
+ psid = ip.RectBivariateSpline(self.iq, self.id, self.psid, kx=3, ky=3)
1522
+ psiq = ip.RectBivariateSpline(self.iq, self.id, self.psiq, kx=3, ky=3)
1523
+ #ld = ip.RectBivariateSpline(iq, id, dqpars['psidq'][0]['ld'], kx=3, ky=3)
1524
+ #lq = ip.RectBivariateSpline(iq, id, dqpars['psidq'][0]['lq'], kx=3, ky=3)
1525
+ #psim = ip.RectBivariateSpline(iq, id, dqpars['psidq'][0]['psim'], kx=3, ky=3)
1526
+ #def didtl(t, iqd):
1527
+ # lqd = lq(*iqd)[0,0], ld(*iqd)[0,0]
1528
+ # return [
1529
+ # (uq-r1*iqd[0] -w1 * lqd[1]*iqd[1] - w1*psim(*iqd)[0,0])/lqd[0],
1530
+ # (ud-r1*iqd[1] +w1*lqd[0]*iqd[0])/lqd[1]]
1531
+
1532
+ def didt(t, iqd):
1533
+ uq, ud = U(t)
1534
+ ldd = psid(*iqd, dx=0, dy=1)[0,0]
1535
+ lqq = psiq(*iqd, dx=1, dy=0)[0,0]
1536
+ ldq = psid(*iqd, dx=1, dy=0)[0,0]
1537
+ lqd = psiq(*iqd, dx=0, dy=1)[0,0]
1538
+ psi = psid(*iqd)[0,0], psiq(*iqd)[0,0]
1539
+ return [
1540
+ (-ldd*psi[0]*w1 + ldd*(uq-self.r1*iqd[0])
1541
+ - lqd*psi[1]*w1 - lqd*(ud-self.r1*iqd[1]))/(ldd*lqq - ldq*lqd),
1542
+ (ldq*psi[0]*w1 - ldq*(uq-self.r1*iqd[0])
1543
+ + lqq*psi[1]*w1 + lqq*(ud-self.r1*iqd[1]))/(ldd*lqq - ldq*lqd)]
1544
+
1545
+ t = np.linspace(0, tend, nsamples)
1546
+ Y0 = iqx, idx
1547
+ sol = ig.solve_ivp(didt, (t[0], t[-1]), Y0, dense_output=True)
1548
+ y = sol.sol(t).T
1549
+
1550
+ return {
1551
+ 't': t.tolist(),
1552
+ 'iq': y[:,0], 'id': y[:,1],
1553
+ 'istat': np.array([K(w1*x[0]).dot((x[1][1], x[1][0]))
1554
+ for x in zip(t, y)]).T.tolist(),
1555
+ 'torque': [self.torque_iqd(*iqd) for iqd in y]}