pydae 0.56.4__py3-none-any.whl → 0.57__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.
- pydae/__init__.py +1 -1
- pydae/bmapu/bmapu_builder.py +681 -681
- pydae/bmapu/bmapu_builder_line_exp.py +564 -0
- pydae/bmapu/lines/lib_dtr.py +425 -0
- pydae/bmapu/lines/lines.py +237 -1
- pydae/bmapu/lines/temp.py +1823 -0
- pydae/bmapu/lines/temp_ini_cffi.c +1280 -0
- pydae/bmapu/lines/temp_run_cffi.c +1280 -0
- pydae/bmapu/lines/temp_trap_cffi.c +971 -0
- pydae/bmapu/lines/temp_xy_0.json +7 -0
- pydae/bmapu/lines/xy_0.json +7 -0
- pydae/bmapu/pvs/pv_string.py +647 -0
- pydae/build_cffi.py +1 -1
- pydae/build_v2.py +29 -18
- pydae/models/pendulum/api_test.http +106 -0
- pydae/models/pendulum/dae_api.py +107 -0
- pydae/models/pendulum/dashboard.py +211 -0
- pydae/models/pendulum/temp.py +1882 -0
- pydae/models/pendulum/temp_ini_cffi.c +1247 -0
- pydae/models/pendulum/temp_run_cffi.c +1247 -0
- pydae/models/pendulum/temp_trap_cffi.c +950 -0
- pydae/svg_tools/bmapu_tooltips.ipynb +119 -0
- pydae/svg_tools/svg_tools.py +11 -4
- pydae/temp.py +1 -1
- pydae/temp_ini_cffi.c +4 -0
- pydae/temp_run_cffi.c +4 -0
- pydae/temp_trap_cffi.c +4 -0
- pydae/templates/class_dae_template_api.py +1857 -0
- pydae/utils/dates.py +233 -0
- {pydae-0.56.4.dist-info → pydae-0.57.dist-info}/METADATA +4 -2
- {pydae-0.56.4.dist-info → pydae-0.57.dist-info}/RECORD +34 -15
- {pydae-0.56.4.dist-info → pydae-0.57.dist-info}/WHEEL +1 -1
- {pydae-0.56.4.dist-info → pydae-0.57.dist-info/licenses}/COPYING +0 -0
- {pydae-0.56.4.dist-info → pydae-0.57.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
# Importing required libraries
|
|
2
|
+
import numpy as np
|
|
3
|
+
from scipy.optimize import fsolve, minimize, NonlinearConstraint
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class grid:
|
|
8
|
+
def __init__(self, nodes, lines, pros):
|
|
9
|
+
self.nodes = self.add_nodes(nodes)
|
|
10
|
+
self.lines = self.add_lines(lines, self.nodes)
|
|
11
|
+
self.pros = self.add_pros(pros, self.nodes)
|
|
12
|
+
self.pf = pf(self)
|
|
13
|
+
self.ofo = ofo(self)
|
|
14
|
+
self.opf = opf(self)
|
|
15
|
+
|
|
16
|
+
def add_nodes(self, nodes):
|
|
17
|
+
nodes_list = list()
|
|
18
|
+
for item in nodes:
|
|
19
|
+
nodes_list.append(node(item['id'], item['slack']))
|
|
20
|
+
return nodes_list
|
|
21
|
+
|
|
22
|
+
def add_lines(self, lines, nodes):
|
|
23
|
+
lines_list = list()
|
|
24
|
+
for item in lines:
|
|
25
|
+
lines_list.append(line(item['id'], item['From'], item['To'], item['R'], item['X'], nodes))
|
|
26
|
+
return lines_list
|
|
27
|
+
|
|
28
|
+
def add_pros(self, pros, nodes):
|
|
29
|
+
pros_list = list()
|
|
30
|
+
for item in pros:
|
|
31
|
+
pros_list.append(prosumer(item['id'], item['Node'], item['P'], item['Q'], nodes))
|
|
32
|
+
return pros_list
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class opf:
|
|
36
|
+
def __init__(self, net):
|
|
37
|
+
self.net = net
|
|
38
|
+
|
|
39
|
+
def set_params(self, params):
|
|
40
|
+
self.inputs, self.outputs = [], []
|
|
41
|
+
for item in params:
|
|
42
|
+
if item['type'] == 'input':
|
|
43
|
+
name = 'ofo-' + item['type'] + '-' + item['signal'] + '-' + item['element'] + '-' + str(item['node'])
|
|
44
|
+
self.inputs.append((name, item['bounds']))
|
|
45
|
+
self.net.pros.append(prosumer(name, item['node'], 0, 0, self.net.nodes))
|
|
46
|
+
self.net.pros[-1].bnds = item['bounds']
|
|
47
|
+
if item['type'] == 'output':
|
|
48
|
+
name = 'ofo-' + item['type'] + '-' + item['signal'] + '-' + item['element'] + '-' + str(item[item['element']])
|
|
49
|
+
self.outputs.append((name, item['bounds']))
|
|
50
|
+
self.n_in = len(self.inputs)
|
|
51
|
+
self.n_out = len(self.outputs)
|
|
52
|
+
|
|
53
|
+
def set_inputs(self, inputs):
|
|
54
|
+
in_base = self.get_inputs()
|
|
55
|
+
for k, v in zip(in_base.keys(), inputs):
|
|
56
|
+
in_base[k] = v
|
|
57
|
+
for in_item in in_base:
|
|
58
|
+
_, _, mag, elem, ref = in_item.split('-')
|
|
59
|
+
pro = next((item for item in self.net.pros if item.ref == in_item), None)
|
|
60
|
+
setattr(pro, mag, in_base[in_item])
|
|
61
|
+
|
|
62
|
+
def get_inputs(self):
|
|
63
|
+
in_base = {item[0]: 0 for item in self.inputs}
|
|
64
|
+
self.net.pf.solve_pf()
|
|
65
|
+
for in_item in in_base:
|
|
66
|
+
_, _, mag, elem, ref = in_item.split('-')
|
|
67
|
+
pro = next((item for item in self.net.pros if item.ref == in_item), None)
|
|
68
|
+
in_base[in_item] = getattr(pro, mag)
|
|
69
|
+
return in_base
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_outputs(self):
|
|
73
|
+
self.output_data = []
|
|
74
|
+
for item in [output[0] for output in self.outputs]:
|
|
75
|
+
_, _, mag, elem, ref = item.split('-')
|
|
76
|
+
if elem == 'node':
|
|
77
|
+
self.output_data.append(np.abs(getattr(self.net.nodes[int(ref)], mag)))
|
|
78
|
+
if elem == 'line':
|
|
79
|
+
self.output_data.append(np.abs(getattr(self.net.lines[int(ref)], mag)))
|
|
80
|
+
return self.output_data
|
|
81
|
+
|
|
82
|
+
def obj_function(self, x):
|
|
83
|
+
self.set_inputs(x)
|
|
84
|
+
self.net.pf.solve_pf()
|
|
85
|
+
y = self.get_outputs()
|
|
86
|
+
return self.f_u @ x + self.f_y @ y
|
|
87
|
+
|
|
88
|
+
def y_constraints(self, x):
|
|
89
|
+
self.set_inputs(x)
|
|
90
|
+
self.net.pf.solve_pf()
|
|
91
|
+
y = self.get_outputs()
|
|
92
|
+
return self.C @ y - self.d
|
|
93
|
+
|
|
94
|
+
def solve_opf(self, obj):
|
|
95
|
+
self.C = np.block([[ np.eye(self.n_out)],
|
|
96
|
+
[-np.eye(self.n_out)]])
|
|
97
|
+
self.d = np.array([item[1][1] for item in self.outputs] + [-item[1][0] for item in self.outputs])
|
|
98
|
+
self.f_u, self.f_y = obj
|
|
99
|
+
y_constr = NonlinearConstraint(self.y_constraints, -np.inf, 0)
|
|
100
|
+
sol = minimize(self.obj_function,
|
|
101
|
+
np.array(list(self.get_inputs().values())),
|
|
102
|
+
bounds=[(item[1][0], item[1][1]) for item in self.inputs],
|
|
103
|
+
constraints=(y_constr,))
|
|
104
|
+
print(f'OPF success: {sol.success}, obj: {sol.fun}\n')
|
|
105
|
+
return sol
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class ofo:
|
|
113
|
+
def __init__(self, net):
|
|
114
|
+
self.net = net
|
|
115
|
+
|
|
116
|
+
def set_params(self, params):
|
|
117
|
+
self.inputs, self.outputs = [], []
|
|
118
|
+
for item in params:
|
|
119
|
+
if item['type'] == 'input':
|
|
120
|
+
name = 'ofo-' + item['type'] + '-' + item['signal'] + '-' + item['element'] + '-' + str(item['node'])
|
|
121
|
+
self.inputs.append((name, item['bounds']))
|
|
122
|
+
self.net.pros.append(prosumer(name, item['node'], 0, 0, self.net.nodes))
|
|
123
|
+
self.net.pros[-1].bnds = item['bounds']
|
|
124
|
+
if item['type'] == 'output':
|
|
125
|
+
name = 'ofo-' + item['type'] + '-' + item['signal'] + '-' + item['element'] + '-' + str(item[item['element']])
|
|
126
|
+
self.outputs.append((name, item['bounds']))
|
|
127
|
+
self.n_in = len(self.inputs)
|
|
128
|
+
self.n_out = len(self.outputs)
|
|
129
|
+
|
|
130
|
+
def reset(self):
|
|
131
|
+
u = self.get_inputs()
|
|
132
|
+
for k, v in zip(u.keys(), np.zeros(len(u))):
|
|
133
|
+
u[k] = v
|
|
134
|
+
self.set_inputs(u)
|
|
135
|
+
self.net.pf.solve_pf()
|
|
136
|
+
y = self.get_outputs()
|
|
137
|
+
|
|
138
|
+
def set_inputs(self, inputs):
|
|
139
|
+
for in_item in inputs:
|
|
140
|
+
if len(in_item) == 2:
|
|
141
|
+
_, _, mag, elem, ref = in_item[0].split('-')
|
|
142
|
+
pro = next((item for item in self.net.pros if item.ref == in_item[0]), None)
|
|
143
|
+
setattr(pro, mag, inputs[in_item])
|
|
144
|
+
else:
|
|
145
|
+
_, _, mag, elem, ref = in_item.split('-')
|
|
146
|
+
pro = next((item for item in self.net.pros if item.ref == in_item), None)
|
|
147
|
+
setattr(pro, mag, inputs[in_item])
|
|
148
|
+
|
|
149
|
+
def get_inputs(self):
|
|
150
|
+
in_base = {item[0]: 0 for item in self.inputs}
|
|
151
|
+
self.net.pf.solve_pf()
|
|
152
|
+
for in_item in in_base:
|
|
153
|
+
_, _, mag, elem, ref = in_item.split('-')
|
|
154
|
+
pro = next((item for item in self.net.pros if item.ref == in_item), None)
|
|
155
|
+
in_base[in_item] = getattr(pro, mag)
|
|
156
|
+
return in_base
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def get_outputs(self):
|
|
160
|
+
self.output_data = []
|
|
161
|
+
for item in [output[0] for output in self.outputs]:
|
|
162
|
+
_, _, mag, elem, ref = item.split('-')
|
|
163
|
+
if elem == 'node':
|
|
164
|
+
self.output_data.append(np.abs(getattr(self.net.nodes[int(ref)], mag)))
|
|
165
|
+
if elem == 'line':
|
|
166
|
+
self.output_data.append(np.abs(getattr(self.net.lines[int(ref)], mag)))
|
|
167
|
+
return self.output_data
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def compute_H(self):
|
|
171
|
+
self.net.pf.solve_pf()
|
|
172
|
+
self.H = np.zeros((self.n_out, self.n_in))
|
|
173
|
+
in_base = self.get_inputs()
|
|
174
|
+
for idx in range(self.n_in):
|
|
175
|
+
self.H[:, idx] = self.get_outputs()
|
|
176
|
+
for idx, key in enumerate(in_base):
|
|
177
|
+
in_base[key] += 1
|
|
178
|
+
self.set_inputs(in_base)
|
|
179
|
+
self.net.pf.solve_pf()
|
|
180
|
+
self.H[:, idx] = - self.H[:, idx] + self.get_outputs()
|
|
181
|
+
in_base[key] -= 1
|
|
182
|
+
self.set_inputs(in_base)
|
|
183
|
+
self.net.pf.solve_pf()
|
|
184
|
+
|
|
185
|
+
def solve_ofo(self, obj, alpha = 0.01, rho = 1, max_iter = 10):
|
|
186
|
+
self.reset()
|
|
187
|
+
self.compute_H()
|
|
188
|
+
self.C = np.block([[ np.eye(self.n_out)],
|
|
189
|
+
[-np.eye(self.n_out)]])
|
|
190
|
+
self.d = np.array([item[1][1] for item in self.outputs] + [-item[1][0] for item in self.outputs])
|
|
191
|
+
self.mu = np.zeros(self.C.shape[0])
|
|
192
|
+
self.s = np.zeros(self.C.shape[0])
|
|
193
|
+
self.f_u, self.f_y = obj
|
|
194
|
+
self.in_bnds = [(item[1][0], item[1][1]) for item in self.inputs]
|
|
195
|
+
|
|
196
|
+
# Data aquisition
|
|
197
|
+
hist = {'u_h': [], 'y_h': [], 'loss_h': [], 'of_h': [], 'mu_h': [], 'H_h': []}
|
|
198
|
+
actual_inputs = np.array(list(self.get_inputs().values()))
|
|
199
|
+
actual_outputs = np.array(self.get_outputs())
|
|
200
|
+
|
|
201
|
+
for it in range(max_iter):
|
|
202
|
+
# Verbose
|
|
203
|
+
print(f'Iteration {it}')
|
|
204
|
+
|
|
205
|
+
# Updating slack variables
|
|
206
|
+
self.s = np.clip((-(1/rho)*self.mu - self.C @ actual_outputs + self.d), a_min = 0, a_max=None)
|
|
207
|
+
|
|
208
|
+
# Updating primal variables
|
|
209
|
+
delta_u = self.f_u + self.H.T @ self.f_y + \
|
|
210
|
+
self.H.T @ self.C.T @ (self.mu + rho*(self.C @ actual_outputs + self.s - self.d))
|
|
211
|
+
actual_inputs = actual_inputs - alpha*delta_u
|
|
212
|
+
actual_inputs = np.clip(actual_inputs, a_min = [bnds[0] for bnds in self.in_bnds], a_max = [bnds[1] for bnds in self.in_bnds])
|
|
213
|
+
actual_inputs_dict = self.get_inputs()
|
|
214
|
+
for k, v in zip(actual_inputs_dict.keys(), actual_inputs):
|
|
215
|
+
actual_inputs_dict[k] = v
|
|
216
|
+
self.set_inputs(actual_inputs_dict)
|
|
217
|
+
|
|
218
|
+
# Solving power flow and receiving outputs
|
|
219
|
+
self.net.pf.solve_pf()
|
|
220
|
+
actual_outputs = np.array(self.get_outputs())
|
|
221
|
+
|
|
222
|
+
# Updating sensitivity martix
|
|
223
|
+
self.compute_H()
|
|
224
|
+
|
|
225
|
+
# Updating dual variables
|
|
226
|
+
self.mu += rho*(self.C @ actual_outputs - self.d + self.s)
|
|
227
|
+
|
|
228
|
+
# Saving data
|
|
229
|
+
hist['u_h'].append(actual_inputs.copy())
|
|
230
|
+
hist['y_h'].append(actual_outputs.copy())
|
|
231
|
+
hist['mu_h'].append(self.mu.copy())
|
|
232
|
+
hist['H_h'].append(self.H.copy())
|
|
233
|
+
|
|
234
|
+
print(f'OFO final value: {self.f_u @ actual_inputs + self.f_y @ actual_outputs}\n')
|
|
235
|
+
return hist
|
|
236
|
+
|
|
237
|
+
class pf:
|
|
238
|
+
def __init__(self, net):
|
|
239
|
+
self.net = net
|
|
240
|
+
|
|
241
|
+
def solve_pf(self, verbose = False):
|
|
242
|
+
x0 = [1,0]*len(self.net.nodes)
|
|
243
|
+
sol, infodict, ier, mesg = fsolve(self.test_x, x0, full_output = True)
|
|
244
|
+
if verbose:
|
|
245
|
+
print(mesg)
|
|
246
|
+
index = 0
|
|
247
|
+
for node in self.net.nodes:
|
|
248
|
+
node.U = complex(sol[index], sol[index+1])
|
|
249
|
+
index += 2
|
|
250
|
+
for line in self.net.lines:
|
|
251
|
+
line.compute_I()
|
|
252
|
+
for line in self.net.lines:
|
|
253
|
+
line.compute_PQ()
|
|
254
|
+
return sol, infodict, ier, mesg
|
|
255
|
+
|
|
256
|
+
def compute_res(self):
|
|
257
|
+
residual = []
|
|
258
|
+
for node in self.net.nodes:
|
|
259
|
+
residual.append(node.check())
|
|
260
|
+
residual_rx = []
|
|
261
|
+
for item in residual:
|
|
262
|
+
residual_rx.append(np.real(item))
|
|
263
|
+
residual_rx.append(np.imag(item))
|
|
264
|
+
return residual_rx
|
|
265
|
+
|
|
266
|
+
def test_x(self, x):
|
|
267
|
+
self.assign_x(x)
|
|
268
|
+
self.compute_I()
|
|
269
|
+
res = self.compute_res()
|
|
270
|
+
return res
|
|
271
|
+
|
|
272
|
+
def assign_x(self, x):
|
|
273
|
+
index = 0
|
|
274
|
+
for node in self.net.nodes:
|
|
275
|
+
node.U = complex(x[index], x[index + 1])
|
|
276
|
+
index += 2
|
|
277
|
+
|
|
278
|
+
def compute_I(self):
|
|
279
|
+
for line in self.net.lines:
|
|
280
|
+
line.I = (line.nodes[0].U - line.nodes[1].U)/line.Z
|
|
281
|
+
for node in self.net.nodes: # +: inyeccion, -: demanda
|
|
282
|
+
node.I = np.conj(np.sum([complex(p.P, p.Q) for p in node.pros])/node.U)
|
|
283
|
+
|
|
284
|
+
class node:
|
|
285
|
+
def __init__(self, ref, slack):
|
|
286
|
+
self.ref = ref
|
|
287
|
+
self.slack = slack
|
|
288
|
+
self.lines = list()
|
|
289
|
+
self.U = complex(1, 0)
|
|
290
|
+
self.pros = []
|
|
291
|
+
|
|
292
|
+
def check(self):
|
|
293
|
+
if self.slack:
|
|
294
|
+
residual = self.U - complex(1, 0)
|
|
295
|
+
else:
|
|
296
|
+
I_agregada = 0
|
|
297
|
+
I_agregada += self.I
|
|
298
|
+
for line in self.lines:
|
|
299
|
+
if line.nodes[0] == self:
|
|
300
|
+
I_agregada -= line.I
|
|
301
|
+
else:
|
|
302
|
+
I_agregada += line.I
|
|
303
|
+
residual = I_agregada
|
|
304
|
+
return residual
|
|
305
|
+
|
|
306
|
+
class line:
|
|
307
|
+
def __init__(self, ref, From, To, R, X, nodes_list):
|
|
308
|
+
self.ref = ref
|
|
309
|
+
self.Z = complex(R, X)
|
|
310
|
+
self.G, self.B = np.real(1/self.Z), -np.imag(1/self.Z)
|
|
311
|
+
self.Y = 1/self.Z
|
|
312
|
+
self.nodes = [next((item for item in nodes_list if item.ref == From), None),
|
|
313
|
+
next((item for item in nodes_list if item.ref == To), None)]
|
|
314
|
+
self.nodes[0].lines.append(self)
|
|
315
|
+
self.nodes[1].lines.append(self)
|
|
316
|
+
|
|
317
|
+
def compute_I(self):
|
|
318
|
+
self.I = (self.nodes[0].U - self.nodes[1].U)/self.Z
|
|
319
|
+
|
|
320
|
+
def compute_PQ(self):
|
|
321
|
+
self.P = np.real(self.nodes[0].U*np.conj(self.I))
|
|
322
|
+
self.Q = np.imag(self.nodes[0].U*np.conj(self.I))
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class prosumer:
|
|
326
|
+
def __init__(self, ref, node_id, P, Q, nodes_list):
|
|
327
|
+
self.ref = ref
|
|
328
|
+
self.P = P
|
|
329
|
+
self.Q = Q
|
|
330
|
+
self.node = next((item for item in nodes_list if item.ref == node_id), None)
|
|
331
|
+
self.node.pros.append(self)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
class DTR_line:
|
|
335
|
+
def __init__(self):
|
|
336
|
+
self.D = 27.7e-3
|
|
337
|
+
self.d = 3.08e-3
|
|
338
|
+
self.sigma = 5.670373e-8
|
|
339
|
+
self.epsilon = 0.8
|
|
340
|
+
self.alpha = 0.8
|
|
341
|
+
self.Tc_max = 95
|
|
342
|
+
self.Rac = 1.04*0.000071873143*(1+0.000937474*(self.Tc_max-20));
|
|
343
|
+
self.h = 1
|
|
344
|
+
self.g = 9.81
|
|
345
|
+
|
|
346
|
+
def compute_I(self, Tamb, v_w, ang_w, G):
|
|
347
|
+
self.Tamb = Tamb
|
|
348
|
+
self.v_w = v_w
|
|
349
|
+
self.ang_w = ang_w
|
|
350
|
+
self.G = G
|
|
351
|
+
|
|
352
|
+
self.compute_Pr()
|
|
353
|
+
self.compute_Pc()
|
|
354
|
+
self.compute_Ps()
|
|
355
|
+
|
|
356
|
+
self.I = np.sqrt( (self.Pr + self.Pc - self.Ps)/self.Rac )
|
|
357
|
+
return self.I
|
|
358
|
+
|
|
359
|
+
def compute_Pr(self):
|
|
360
|
+
self.Pr = np.pi*self.D*self.sigma*self.epsilon*((self.Tc_max + 273.15)**4 - (self.Tamb + 273.15)**4)
|
|
361
|
+
return self.Pr
|
|
362
|
+
|
|
363
|
+
def compute_Pc(self):
|
|
364
|
+
Tf = 0.5*(self.Tamb + self.Tc_max)
|
|
365
|
+
lmb = 2.368e-2 + 7.23e-5*Tf - 2.763e-8*Tf**2
|
|
366
|
+
cf = 1.9327e-10*Tf**4 - 7.9999e-7*Tf**3 + 1.1407e-3*Tf**2 - 0.4489*Tf + 1057.5
|
|
367
|
+
Rs = self.d / (2*(self.D - self.d))
|
|
368
|
+
|
|
369
|
+
mu_f = (17.239 + 4.635e-2*Tf - 2.03e-5*Tf**2) * 1e-6
|
|
370
|
+
dens = (1.293 - 1.525e-4*self.h + 6.379e-9*self.h**2) / (1 + 0.00367*Tf)
|
|
371
|
+
uf = mu_f / dens
|
|
372
|
+
Re = self.v_w * self.D / uf
|
|
373
|
+
|
|
374
|
+
B, n = (0.178, 0.633) if (Re > 2650 and Rs <= 0.05) else ((0.048, 0.8) if Re > 2650 else (0.641, 0.471))
|
|
375
|
+
Nu90 = B * Re**n
|
|
376
|
+
|
|
377
|
+
angle = self.ang_w if self.ang_w <= 180 else self.ang_w - 180
|
|
378
|
+
seno = np.abs(np.sin(np.radians(angle)))
|
|
379
|
+
delta = 0.42 + (0.68 * seno**1.08 if angle <= 24 else 0.58 * seno**0.9)
|
|
380
|
+
Nu_forz = Nu90 * delta
|
|
381
|
+
|
|
382
|
+
Gr = self.D**3 * (self.Tc_max - self.Tamb) * self.g / ((Tf + 273.15) * uf**2)
|
|
383
|
+
Pr = cf * mu_f / lmb
|
|
384
|
+
prod = Gr * Pr
|
|
385
|
+
|
|
386
|
+
if 0.1 < prod < 1e2:
|
|
387
|
+
A, m = 1.02, 0.148
|
|
388
|
+
elif 1e2 <= prod < 1e4:
|
|
389
|
+
A, m = 0.85, 0.188
|
|
390
|
+
elif 1e4 <= prod < 1e7:
|
|
391
|
+
A, m = 0.48, 0.25
|
|
392
|
+
elif 1e7 <= prod < 1e12:
|
|
393
|
+
A, m = 0.125, 0.333
|
|
394
|
+
else:
|
|
395
|
+
A, m = 0, 0
|
|
396
|
+
Nu_nat = A * prod**m
|
|
397
|
+
|
|
398
|
+
Nu = np.max([Nu_nat, Nu_forz])
|
|
399
|
+
|
|
400
|
+
self.Pc = np.pi * lmb * (self.Tc_max - self.Tamb) * Nu
|
|
401
|
+
return self.Pc
|
|
402
|
+
|
|
403
|
+
def compute_Ps(self):
|
|
404
|
+
self.Ps = self.alpha*self.G*self.D
|
|
405
|
+
return self.Ps
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
|
pydae/bmapu/lines/lines.py
CHANGED
|
@@ -6,6 +6,154 @@ Created on Thu August 10 23:52:55 2022
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
|
+
import sympy as sym
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def add_lines(self):
|
|
13
|
+
|
|
14
|
+
self.g_line_monitors = []
|
|
15
|
+
self.y_line_monitors = []
|
|
16
|
+
|
|
17
|
+
for line in self.lines:
|
|
18
|
+
|
|
19
|
+
it = self.it
|
|
20
|
+
|
|
21
|
+
bus_j = line['bus_j']
|
|
22
|
+
bus_k = line['bus_k']
|
|
23
|
+
|
|
24
|
+
idx_j = self.buses_list.index(bus_j)
|
|
25
|
+
idx_k = self.buses_list.index(bus_k)
|
|
26
|
+
|
|
27
|
+
self.A[it,idx_j] = 1
|
|
28
|
+
self.A[it,idx_k] =-1
|
|
29
|
+
self.A[it+1,idx_j] = 1
|
|
30
|
+
self.A[it+2,idx_k] = 1
|
|
31
|
+
|
|
32
|
+
line_name = f"{bus_j}_{bus_k}"
|
|
33
|
+
g_jk = sym.Symbol(f"g_{line_name}", real=True)
|
|
34
|
+
b_jk = sym.Symbol(f"b_{line_name}", real=True)
|
|
35
|
+
bs_jk = sym.Symbol(f"bs_{line_name}", real=True)
|
|
36
|
+
self.G_primitive[it,it] = g_jk
|
|
37
|
+
self.B_primitive[it,it] = b_jk
|
|
38
|
+
self.B_primitive[it+1,it+1] = bs_jk/2
|
|
39
|
+
self.B_primitive[it+2,it+2] = bs_jk/2
|
|
40
|
+
|
|
41
|
+
if not 'thermal' in line:
|
|
42
|
+
line.update({'thermal':False})
|
|
43
|
+
|
|
44
|
+
if 'X_pu' in line:
|
|
45
|
+
if 'S_mva' in line: S_line = 1e6*line['S_mva']
|
|
46
|
+
R = line['R_pu']*self.sys['S_base']/S_line # in pu of the system base
|
|
47
|
+
X = line['X_pu']*self.sys['S_base']/S_line # in pu of the system base
|
|
48
|
+
G = R/(R**2+X**2)
|
|
49
|
+
B = -X/(R**2+X**2)
|
|
50
|
+
self.params_grid.update({f"g_{line_name}":G})
|
|
51
|
+
self.params_grid.update({f'b_{line_name}':B})
|
|
52
|
+
|
|
53
|
+
if 'X' in line:
|
|
54
|
+
bus_idx = self.buses_list.index(line['bus_j'])
|
|
55
|
+
U_base = self.buses[bus_idx]['U_kV']*1000
|
|
56
|
+
Z_base = U_base**2/self.sys['S_base']
|
|
57
|
+
R = line['R']/Z_base # in pu of the system base
|
|
58
|
+
X = line['X']/Z_base # in pu of the system base
|
|
59
|
+
G = R/(R**2+X**2)
|
|
60
|
+
B = -X/(R**2+X**2)
|
|
61
|
+
self.params_grid.update({f"g_{line_name}":G})
|
|
62
|
+
self.params_grid.update({f'b_{line_name}':B})
|
|
63
|
+
|
|
64
|
+
if 'X_km' in line:
|
|
65
|
+
bus_idx = self.buses_list.index(line['bus_j'])
|
|
66
|
+
U_base = self.buses[bus_idx]['U_kV']*1000
|
|
67
|
+
Z_base = U_base**2/self.sys['S_base']
|
|
68
|
+
if line['thermal']:
|
|
69
|
+
R = sym.Symbol(f"R_{line_name}", real=True)
|
|
70
|
+
R_N = line['R_km']*line['km']/Z_base # in pu of the system base
|
|
71
|
+
self.u_grid.update({str(R):R_N})
|
|
72
|
+
else:
|
|
73
|
+
R = line['R_km']*line['km']/Z_base # in pu of the system base
|
|
74
|
+
|
|
75
|
+
X = line['X_km']*line['km']/Z_base # in pu of the system base
|
|
76
|
+
G = R/(R**2+X**2)
|
|
77
|
+
B = -X/(R**2+X**2)
|
|
78
|
+
self.params_grid.update({f"g_{line_name}":G})
|
|
79
|
+
self.params_grid.update({f'b_{line_name}':B})
|
|
80
|
+
|
|
81
|
+
self.params_grid.update({f'bs_{line_name}':0.0})
|
|
82
|
+
if 'Bs_pu' in line:
|
|
83
|
+
if 'S_mva' in line: S_line = 1e6*line['S_mva']
|
|
84
|
+
Bs = line['Bs_pu']*S_line/self.sys['S_base'] # in pu of the system base
|
|
85
|
+
bs = Bs
|
|
86
|
+
self.params_grid[f'bs_{line_name}'] = bs
|
|
87
|
+
|
|
88
|
+
if 'Bs_km' in line:
|
|
89
|
+
bus_idx = self.buses_list.index(line['bus_j'])
|
|
90
|
+
U_base = self.buses[bus_idx]['U_kV']*1000
|
|
91
|
+
Z_base = U_base**2/self.sys['S_base']
|
|
92
|
+
Y_base = 1.0/Z_base
|
|
93
|
+
Bs = line['Bs_km']*line['km']/Y_base # in pu of the system base
|
|
94
|
+
bs = Bs
|
|
95
|
+
self.params_grid[f'bs_{line_name}'] = bs
|
|
96
|
+
|
|
97
|
+
it += 3
|
|
98
|
+
|
|
99
|
+
if 'monitor' in line:
|
|
100
|
+
if line['monitor']:
|
|
101
|
+
|
|
102
|
+
bus_j = line['bus_j']
|
|
103
|
+
bus_k = line['bus_k']
|
|
104
|
+
|
|
105
|
+
line_name = f"{bus_j}_{bus_k}"
|
|
106
|
+
|
|
107
|
+
idx_j = self.buses_list.index(bus_j)
|
|
108
|
+
idx_k = self.buses_list.index(bus_k)
|
|
109
|
+
|
|
110
|
+
V_j = sym.Symbol(f"V_{bus_j}", real=True)
|
|
111
|
+
V_k = sym.Symbol(f"V_{bus_k}", real=True)
|
|
112
|
+
theta_j = sym.Symbol(f"theta_{bus_j}", real=True)
|
|
113
|
+
theta_k = sym.Symbol(f"theta_{bus_k}", real=True)
|
|
114
|
+
|
|
115
|
+
b_ij_p = 0.0
|
|
116
|
+
if f'bs_{line_name}' in self.params_grid:
|
|
117
|
+
b_ij_p = self.params_grid[f'bs_{line_name}']
|
|
118
|
+
|
|
119
|
+
G_jk = g_jk
|
|
120
|
+
B_jk = b_jk
|
|
121
|
+
theta_jk = theta_j - theta_k
|
|
122
|
+
P_line_to = V_j*V_k*(G_jk*sym.cos(theta_jk) + B_jk*sym.sin(theta_jk)) - V_j**2*(G_jk)
|
|
123
|
+
Q_line_to = V_j*V_k*(G_jk*sym.sin(theta_jk) - B_jk*sym.cos(theta_jk)) + V_j**2*(B_jk) + V_j**2*(bs_jk/2)
|
|
124
|
+
P_line_from = V_j*V_k*(G_jk*sym.cos(-theta_jk) + B_jk*sym.sin(-theta_jk)) - V_k**2*(G_jk)
|
|
125
|
+
Q_line_from = V_j*V_k*(G_jk*sym.sin(-theta_jk) - B_jk*sym.cos(-theta_jk)) + V_k**2*(B_jk) + V_k**2*(bs_jk/2)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# h_grid.update({f"p_line_{bus_j}_{bus_k}":P_line_to})
|
|
129
|
+
# h_grid.update({f"q_line_{bus_j}_{bus_k}":Q_line_to})
|
|
130
|
+
# h_grid.update({f"p_line_{bus_k}_{bus_j}":P_line_from})
|
|
131
|
+
# h_grid.update({f"q_line_{bus_k}_{bus_j}":Q_line_from})
|
|
132
|
+
p_line_to_pu,q_line_to_pu = sym.symbols(f"p_line_pu_{bus_j}_{bus_k},q_line_pu_{bus_j}_{bus_k}", real=True)
|
|
133
|
+
p_line_from_pu,q_line_from_pu = sym.symbols(f"p_line_pu_{bus_k}_{bus_j},q_line_pu_{bus_k}_{bus_j}", real=True)
|
|
134
|
+
|
|
135
|
+
self.g_line_monitors += [p_line_to_pu - P_line_to]
|
|
136
|
+
self.g_line_monitors += [q_line_to_pu - Q_line_to]
|
|
137
|
+
self.g_line_monitors += [p_line_from_pu - P_line_from]
|
|
138
|
+
self.g_line_monitors += [q_line_from_pu - Q_line_from]
|
|
139
|
+
|
|
140
|
+
self.y_line_monitors += [p_line_to_pu,q_line_to_pu,p_line_from_pu,q_line_from_pu]
|
|
141
|
+
|
|
142
|
+
U_base = self.buses[idx_j]['U_kV']*1000
|
|
143
|
+
I_base = self.S_base/(np.sqrt(3)*U_base)
|
|
144
|
+
|
|
145
|
+
self.h_grid.update({f'p_line_{bus_j}_{bus_k}':p_line_to_pu*self.S_base})
|
|
146
|
+
self.h_grid.update({f'q_line_{bus_j}_{bus_k}':q_line_to_pu*self.S_base})
|
|
147
|
+
self.h_grid.update({f'p_line_{bus_k}_{bus_j}':p_line_from_pu*self.S_base})
|
|
148
|
+
self.h_grid.update({f'q_line_{bus_k}_{bus_j}':q_line_from_pu*self.S_base})
|
|
149
|
+
|
|
150
|
+
I_j_k_pu = ( p_line_to_pu**2 + q_line_to_pu**2)**0.5/V_j
|
|
151
|
+
I_k_j_pu = (p_line_from_pu**2 + q_line_from_pu**2)**0.5/V_k
|
|
152
|
+
|
|
153
|
+
self.h_grid.update({f'I_line_{bus_j}_{bus_k}':I_j_k_pu*I_base})
|
|
154
|
+
self.h_grid.update({f'I_line_{bus_k}_{bus_j}':I_k_j_pu*I_base})
|
|
155
|
+
|
|
156
|
+
|
|
9
157
|
|
|
10
158
|
def change_line(model,bus_j,bus_k, *args,**kwagrs):
|
|
11
159
|
line = kwagrs
|
|
@@ -62,4 +210,92 @@ def get_line_current(model,bus_j,bus_k, units='A'):
|
|
|
62
210
|
I_b = S_b/(np.sqrt(3)*U_b)
|
|
63
211
|
I_j_k = I_j_k_pu*I_b
|
|
64
212
|
|
|
65
|
-
return I_j_k
|
|
213
|
+
return I_j_k
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def test_line_pu_build():
|
|
217
|
+
|
|
218
|
+
from pydae.bmapu import bmapu_builder
|
|
219
|
+
from pydae.build_v2 import build_mkl,build_numba
|
|
220
|
+
|
|
221
|
+
data = {
|
|
222
|
+
"system":{"name":"temp","S_base":100e6, "K_p_agc":0.01,"K_i_agc":0.01, "K_xif":0.01},
|
|
223
|
+
"buses":[{"name":"1", "P_W":0.0,"Q_var":0.0,"U_kV":20.0},
|
|
224
|
+
{"name":"2", "P_W":0.0,"Q_var":0.0,"U_kV":20.0}],
|
|
225
|
+
"lines":[{"bus_j":"1", "bus_k":"2", "X_pu":0.15,"R_pu":0.0,"Bs_pu":0.5, "S_mva":900.0, "monitor":True}],
|
|
226
|
+
"sources":[{"bus":"1","type":"vsource","V_mag_pu":1.0,"V_ang_rad":0.0,"K_delta":0.1}]
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
grid = bmapu_builder.bmapu(data)
|
|
230
|
+
grid.uz_jacs = False
|
|
231
|
+
grid.verbose = True
|
|
232
|
+
grid.construct(f'temp')
|
|
233
|
+
build_numba(grid.sys_dict)
|
|
234
|
+
|
|
235
|
+
def test_line_pu_ini():
|
|
236
|
+
|
|
237
|
+
import temp
|
|
238
|
+
|
|
239
|
+
model = temp.model()
|
|
240
|
+
model.ini({'P_2':0e6, "K_xif":0.01},'xy_0.json')
|
|
241
|
+
print(f"V_1 = {model.get_value('V_1'):2.2f}, V_2 = {model.get_value('V_2'):2.2f}")
|
|
242
|
+
|
|
243
|
+
model.report_y()
|
|
244
|
+
|
|
245
|
+
def test_line_km_build():
|
|
246
|
+
|
|
247
|
+
from pydae.bmapu import bmapu_builder
|
|
248
|
+
from pydae.build_v2 import build_mkl,build_numba
|
|
249
|
+
|
|
250
|
+
R_km = 0.0268 # (Ω/km)
|
|
251
|
+
X_km = 0.2766 # (Ω/km)
|
|
252
|
+
B_km = 4.59e-6 # (℧-1/Km)
|
|
253
|
+
|
|
254
|
+
N_parallel = 44.0
|
|
255
|
+
|
|
256
|
+
Lenght_km = 22000.0/N_parallel
|
|
257
|
+
|
|
258
|
+
data = {
|
|
259
|
+
"system":{"name":"temp","S_base":100e6, "K_p_agc":0.01,"K_i_agc":0.01, "K_xif":0.01},
|
|
260
|
+
"buses":[{"name":"1", "P_W":0.0,"Q_var":0.0,"U_kV":400.0},
|
|
261
|
+
{"name":"2", "P_W":5000e6,"Q_var":0.0,"U_kV":400.0}],
|
|
262
|
+
"lines":[{"bus_j":"1", "bus_k":"2", 'X_km':X_km/44, 'R_km':R_km/44, 'Bs_km':B_km*44, "km":Lenght_km, "monitor":True}],
|
|
263
|
+
"sources":[{"bus":"1","type":"vsource","V_mag_pu":1.0,"V_ang_rad":0.0,"K_delta":0.1}]
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
grid = bmapu_builder.bmapu(data)
|
|
267
|
+
grid.uz_jacs = False
|
|
268
|
+
grid.verbose = True
|
|
269
|
+
grid.construct(f'temp')
|
|
270
|
+
build_numba(grid.sys_dict)
|
|
271
|
+
|
|
272
|
+
def test_line_km_ini():
|
|
273
|
+
|
|
274
|
+
import temp
|
|
275
|
+
|
|
276
|
+
model = temp.model()
|
|
277
|
+
model.ini({'P_2':5000e6,'Q_2':0, "K_xif":0.01},'xy_0.json')
|
|
278
|
+
print(f"V_1 = {model.get_value('V_1'):2.2f}, V_2 = {model.get_value('V_2'):2.2f}")
|
|
279
|
+
|
|
280
|
+
print("report_y:")
|
|
281
|
+
model.report_y()
|
|
282
|
+
print("-----------------------------------------------------------------------")
|
|
283
|
+
print("report_z:")
|
|
284
|
+
model.report_z()
|
|
285
|
+
|
|
286
|
+
U_1 = 400e3
|
|
287
|
+
U_2 = 400e3
|
|
288
|
+
Bs = 4.59e-6*22000
|
|
289
|
+
Q = U_1**2*Bs/2 + U_2**2*Bs/2
|
|
290
|
+
print(f"Q = {Q/1e6:2.2f} Mvar")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
if __name__ == "__main__":
|
|
294
|
+
|
|
295
|
+
#test_line_pu_build()
|
|
296
|
+
#test_line_pu_ini()
|
|
297
|
+
|
|
298
|
+
test_line_km_build()
|
|
299
|
+
test_line_km_ini()
|
|
300
|
+
|
|
301
|
+
|