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.
@@ -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
+
@@ -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
+