pydae 0.56.5__py3-none-any.whl → 0.57.1__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.
@@ -1,682 +1,682 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- Created on Thu Feb 25 23:52:55 2021
4
-
5
- @author: jmmau
6
- """
7
-
8
- import numpy as np
9
- import sympy as sym
10
- import json
11
- import os
12
- import hjson
13
- from pydae.bmapu.syns.syns import add_syns
14
- from pydae.bmapu.vscs.vscs import add_vscs
15
- from pydae.bmapu.vsgs.vsgs import add_vsgs
16
- from pydae.bmapu.wecs.wecs import add_wecs
17
- from pydae.bmapu.pvs.pvs import add_pvs
18
- from pydae.bmapu.loads.loads import add_loads
19
- from pydae.bmapu.sources.sources import add_sources
20
- from pydae.bmapu.miscellaneous.miscellaneous import add_miscellaneous
21
- from pydae.bmapu.pods.pods import add_pods
22
- from pydae.bmapu.miscellaneous.banks import add_banks
23
-
24
- import pydae.build_cffi as db
25
- from pydae.build_v2 import builder
26
-
27
- import requests
28
-
29
- # todo:
30
- # S_base can't be modified becuase impedances in element base are computed
31
- # in S_base only in the build
32
- class bmapu:
33
- '''
34
-
35
-
36
- Parameters
37
- ----------
38
- data_input : string or dict
39
- File path to the system data information or dictionary with the information.
40
-
41
- Returns
42
- -------
43
- dict
44
- Dictionary with the equations for pydae.
45
-
46
- {
47
- 'sys':{'name':'pf_1','S_base':100e6},
48
- 'buses':[{'name':'GRI','P_W':0.0,'Q_var':0.0,'U_kV':66.0, 'type':'slack'},
49
- {'name':'POI','P_W':0.0,'Q_var':0.0,'U_kV':66.0},
50
- {'name':'PMV','P_W':0.0,'Q_var':0.0,'U_kV':20.0}],
51
- 'lines':[{'bus_j':'GRI','bus_k':'POI','X_km':0.4,'R_km':0.12,'km':20},
52
- {'bus_j':'POI','bus_k':'PMV','X_pu':0.04,'R_pu':0.01, 'S_mva':50.0}]
53
- }
54
-
55
-
56
- '''
57
-
58
- def __init__(self,data_input='',testing=False):
59
-
60
- if type(data_input) == str:
61
- if 'http' in data_input:
62
- url = data_input
63
- resp = requests.get(url)
64
- data = hjson.loads(resp.text)
65
- else:
66
- if os.path.splitext(data_input)[1] == '.json':
67
- with open(data_input,'r') as fobj:
68
- data = json.loads(fobj.read().replace("'",'"'))
69
- if os.path.splitext(data_input)[1] == '.hjson':
70
- with open(data_input,'r') as fobj:
71
- data = hjson.loads(fobj.read().replace("'",'"'))
72
- elif type(data_input) == dict:
73
- data = data_input
74
-
75
- self.data = data
76
-
77
- if not 'lines' in self.data:
78
- self.data['lines'] = []
79
- if not 'shunts' in self.data:
80
- self.data['shunts'] = []
81
- if not 'transformers' in self.data:
82
- self.data['transformers'] = []
83
- if not 'loads' in self.data:
84
- self.data['loads'] = []
85
-
86
- self.system = data['system']
87
- self.buses = data['buses']
88
- self.lines = data['lines']
89
- self.shunts = data['shunts']
90
- self.transformers = data['transformers']
91
- self.loads = data['loads']
92
-
93
- self.x_grid = []
94
- self.f_grid = []
95
-
96
- self.params_grid = {'S_base':self.system['S_base']}
97
- self.S_base = sym.Symbol("S_base", real=True)
98
- self.N_bus = len(self.buses)
99
- self.N_branch = 3*len(self.lines) + len(self.shunts) + 2*len(self.transformers)
100
-
101
- self.dae = {'f':[],'g':[],'x':[],'y_ini':[],'y_run':[],
102
- 'u_ini_dict':{},'u_run_dict':{},'params_dict':{},
103
- 'h_dict':{},'xy_0_dict':{}}
104
-
105
- self.uz_jacs = True
106
- self.verbose = False
107
- self.testing = testing
108
-
109
- def contruct_grid(self):
110
-
111
- N_branch = self.N_branch
112
- N_bus = self.N_bus
113
- sys = self.system
114
-
115
- S_base = sym.Symbol('S_base', real=True)
116
-
117
- xy_0_dict_grid = {}
118
- u_grid = {}
119
- h_grid = {}
120
-
121
- A = sym.zeros(N_branch,N_bus)
122
- G_primitive = sym.zeros(N_branch,N_branch)
123
- B_primitive = sym.zeros(N_branch,N_branch)
124
- buses_list = [bus['name'] for bus in self.buses]
125
- it = 0
126
- for line in self.lines:
127
-
128
- bus_j = line['bus_j']
129
- bus_k = line['bus_k']
130
-
131
- idx_j = buses_list.index(bus_j)
132
- idx_k = buses_list.index(bus_k)
133
-
134
- A[it,idx_j] = 1
135
- A[it,idx_k] =-1
136
- A[it+1,idx_j] = 1
137
- A[it+2,idx_k] = 1
138
-
139
- line_name = f"{bus_j}_{bus_k}"
140
- g_jk = sym.Symbol(f"g_{line_name}", real=True)
141
- b_jk = sym.Symbol(f"b_{line_name}", real=True)
142
- bs_jk = sym.Symbol(f"bs_{line_name}", real=True)
143
- G_primitive[it,it] = g_jk
144
- B_primitive[it,it] = b_jk
145
- B_primitive[it+1,it+1] = bs_jk/2
146
- B_primitive[it+2,it+2] = bs_jk/2
147
-
148
- if not 'thermal' in line:
149
- line.update({'thermal':False})
150
-
151
- if 'X_pu' in line:
152
- if 'S_mva' in line: S_line = 1e6*line['S_mva']
153
- R = line['R_pu']*sys['S_base']/S_line # in pu of the system base
154
- X = line['X_pu']*sys['S_base']/S_line # in pu of the system base
155
- G = R/(R**2+X**2)
156
- B = -X/(R**2+X**2)
157
- self.params_grid.update({f"g_{line_name}":G})
158
- self.params_grid.update({f'b_{line_name}':B})
159
-
160
- if 'X' in line:
161
- bus_idx = buses_list.index(line['bus_j'])
162
- U_base = self.buses[bus_idx]['U_kV']*1000
163
- Z_base = U_base**2/sys['S_base']
164
- R = line['R']/Z_base # in pu of the system base
165
- X = line['X']/Z_base # in pu of the system base
166
- G = R/(R**2+X**2)
167
- B = -X/(R**2+X**2)
168
- self.params_grid.update({f"g_{line_name}":G})
169
- self.params_grid.update({f'b_{line_name}':B})
170
-
171
- if 'X_km' in line:
172
- bus_idx = buses_list.index(line['bus_j'])
173
- U_base = self.buses[bus_idx]['U_kV']*1000
174
- Z_base = U_base**2/sys['S_base']
175
- if line['thermal']:
176
- R = sym.Symbol(f"R_{line_name}", real=True)
177
- R_N = line['R_km']*line['km']/Z_base # in pu of the system base
178
- u_grid.update({str(R):R_N})
179
- else:
180
- R = line['R_km']*line['km']/Z_base # in pu of the system base
181
-
182
- X = line['X_km']*line['km']/Z_base # in pu of the system base
183
- G = R/(R**2+X**2)
184
- B = -X/(R**2+X**2)
185
- self.params_grid.update({f"g_{line_name}":G})
186
- self.params_grid.update({f'b_{line_name}':B})
187
-
188
- self.params_grid.update({f'bs_{line_name}':0.0})
189
- if 'Bs_pu' in line:
190
- if 'S_mva' in line: S_line = 1e6*line['S_mva']
191
- Bs = line['Bs_pu']*S_line/sys['S_base'] # in pu of the system base
192
- bs = Bs
193
- self.params_grid[f'bs_{line_name}'] = bs
194
-
195
- if 'Bs_km' in line:
196
- bus_idx = buses_list.index(line['bus_j'])
197
- U_base = self.buses[bus_idx]['U_kV']*1000
198
- Z_base = U_base**2/sys['S_base']
199
- Y_base = 1.0/Z_base
200
- Bs = line['Bs_km']*line['km']/Y_base # in pu of the system base
201
- bs = Bs
202
- self.params_grid[f'bs_{line_name}'] = bs
203
-
204
- it += 3
205
-
206
- for trafo in self.transformers:
207
-
208
- bus_j = trafo['bus_j']
209
- bus_k = trafo['bus_k']
210
-
211
- idx_j = buses_list.index(bus_j)
212
- idx_k = buses_list.index(bus_k)
213
-
214
- A[it,idx_j] = 1
215
- A[it+1,idx_k] = 1
216
-
217
- trafo_name = f"{bus_j}_{bus_k}"
218
- g_jk = sym.Symbol(f"g_cc_{trafo_name}", real=True)
219
- b_jk = sym.Symbol(f"b_cc_{trafo_name}", real=True)
220
- tap = sym.Symbol(f"tap_{trafo_name}", real=True)
221
- ang = sym.Symbol(f"ang_{trafo_name}", real=True)
222
- a_s = tap*sym.cos(ang)
223
- b_s = tap*sym.sin(ang)
224
-
225
- Y_cc = g_jk + sym.I*b_jk
226
-
227
-
228
- Y_primitive =sym.Matrix([[ Y_cc/(a_s**2+b_s**2),-Y_cc/(a_s-sym.I*b_s)],
229
- [-Y_cc/(a_s+sym.I*b_s), Y_cc]])
230
-
231
-
232
- G_primitive[it:it+2,it:it+2] = sym.re(Y_primitive)
233
- B_primitive[it:it+2,it:it+2] = sym.im(Y_primitive)
234
-
235
-
236
- if 'X_pu' in trafo:
237
- if 'S_mva' in trafo: S_trafo = 1e6*trafo['S_mva']
238
- R = trafo['R_pu']*sys['S_base']/S_trafo # in pu of the system base
239
- X = trafo['X_pu']*sys['S_base']/S_trafo # in pu of the system base
240
- G = R/(R**2+X**2)
241
- B = -X/(R**2+X**2)
242
- self.params_grid.update({f"g_cc_{trafo_name}":G})
243
- self.params_grid.update({f'b_cc_{trafo_name}':B})
244
- tap_m = 1.0
245
- if 'tap_m' in trafo:
246
- tap_m = trafo['tap_m']
247
- self.params_grid.update({f'tap_{trafo_name}':tap_m})
248
- self.params_grid.update({f'ang_{trafo_name}':0.0})
249
-
250
-
251
- if 'X' in trafo:
252
- bus_idx = buses_list.index(trafo['bus_j'])
253
- U_base = self.buses[bus_idx]['U_kV']*1000
254
- Z_base = U_base**2/sys['S_base']
255
- R = trafo['R']/Z_base # in pu of the system base
256
- X = trafo['X']/Z_base # in pu of the system base
257
- G = R/(R**2+X**2)
258
- B = -X/(R**2+X**2)
259
- self.params_grid.update({f"g_cc_{trafo_name}":G})
260
- self.params_grid.update({f'b_cc_{trafo_name}':B})
261
- self.params_grid.update({f'tap_{trafo_name}':1.0})
262
- self.params_grid.update({f'ang_{trafo_name}':0.0})
263
-
264
- it += 2
265
-
266
-
267
- for shunt in self.shunts:
268
-
269
- bus_j = shunt['bus']
270
- idx_j = buses_list.index(bus_j)
271
-
272
- A[it,idx_j] = 1
273
-
274
- shunt_name = f"{bus_j}"
275
- g_j = sym.Symbol(f"g_shunt_{shunt_name}", real=True)
276
- b_j = sym.Symbol(f"b_shunt_{shunt_name}", real=True)
277
- G_primitive[it,it] = g_j
278
- B_primitive[it,it] = b_j
279
-
280
-
281
- if 'X_pu' in shunt:
282
- if 'S_mva' in shunt: S_line = 1e6*shunt['S_mva']
283
- R = shunt['R_pu']*sys['S_base']/S_line # in pu of the system base
284
- X = shunt['X_pu']*sys['S_base']/S_line # in pu of the system base
285
- G = R/(R**2+X**2)
286
- B = -X/(R**2+X**2)
287
- self.params_grid.update({f"g_shunt_{shunt_name}":G})
288
- self.params_grid.update({f'b_shunt_{shunt_name}':B})
289
-
290
- if 'X' in shunt:
291
- U_base = self.buses[idx_j]['U_kV']*1000
292
- Z_base = U_base**2/sys['S_base']
293
- R = shunt['R']/Z_base # in pu of the system base
294
- X = shunt['X']/Z_base # in pu of the system base
295
- G = R/(R**2+X**2)
296
- B = -X/(R**2+X**2)
297
- self.params_grid.update({f"g_shunt_{shunt_name}":G})
298
- self.params_grid.update({f'b_shunt_{shunt_name}':B})
299
-
300
- it += 1
301
-
302
- G = A.T * G_primitive * A
303
- B = A.T * B_primitive * A
304
-
305
- sin = sym.sin
306
- cos = sym.cos
307
- y_grid = []
308
- g = sym.zeros(2*N_bus,1)
309
-
310
- for j in range(N_bus):
311
- bus_j_name = buses_list[j]
312
- P_j = sym.Symbol(f"P_{bus_j_name}", real=True)
313
- Q_j = sym.Symbol(f"Q_{bus_j_name}", real=True)
314
- g[2*j] = -P_j/S_base
315
- g[2*j+1] = -Q_j/S_base
316
- for k in range(N_bus):
317
-
318
- bus_k_name = buses_list[k]
319
- V_j = sym.Symbol(f"V_{bus_j_name}", real=True)
320
- V_k = sym.Symbol(f"V_{bus_k_name}", real=True)
321
- theta_j = sym.Symbol(f"theta_{bus_j_name}", real=True)
322
- theta_k = sym.Symbol(f"theta_{bus_k_name}", real=True)
323
- g[2*j] += V_j*V_k*(G[j,k]*cos(theta_j - theta_k) + B[j,k]*sin(theta_j - theta_k))
324
- g[2*j+1] += V_j*V_k*(G[j,k]*sin(theta_j - theta_k) - B[j,k]*cos(theta_j - theta_k))
325
- h_grid.update({f"V_{bus_j_name}":V_j})
326
- bus = self.buses[j]
327
- bus_name = bus['name']
328
- if 'type' in bus:
329
- if bus['type'] == 'slack':
330
- y_grid += [P_j]
331
- y_grid += [Q_j]
332
- u_grid.update({f"V_{bus_name}":1.0})
333
- u_grid.update({f"theta_{bus_name}":0.0})
334
- else:
335
- y_grid += [V_j]
336
- y_grid += [theta_j]
337
- u_grid.update({f"P_{bus_name}":bus['P_W']})
338
- u_grid.update({f"Q_{bus_name}":bus['Q_var']})
339
- xy_0_dict_grid.update({str(V_j):1.0,str(theta_j):0.0})
340
-
341
- self.params_grid.update({f'U_{bus_name}_n':bus['U_kV']*1000})
342
- g_grid = list(g)
343
-
344
- if False:
345
- v_sym_list = []
346
- for bus in buses_list:
347
- V_m = sym.Symbol(f'V_{bus}',real=True)
348
- V_a = sym.Symbol(f'theta_{bus}',real=True)
349
- v_sym_list += [V_m*sym.exp(sym.I*V_a)]
350
-
351
- sym.Matrix(v_sym_list)
352
-
353
- I_lines = (G_primitive+1j*B_primitive) * A * sym.Matrix(v_sym_list)
354
-
355
- it = 0
356
- for line in self.lines:
357
- I_jk_r = sym.Symbol(f"I_{line['bus_j']}_{line['bus_k']}_r", real=True)
358
- I_jk_i = sym.Symbol(f"I_{line['bus_j']}_{line['bus_k']}_i", real=True)
359
- g_grid += [-I_jk_r + sym.re(I_lines[it])]
360
- g_grid += [-I_jk_i + sym.im(I_lines[it])]
361
- y_grid += [I_jk_r]
362
- y_grid += [I_jk_i]
363
- it += 1
364
-
365
- for line in self.lines:
366
-
367
- bus_j = line['bus_j']
368
- bus_k = line['bus_k']
369
-
370
- line_name = f"{bus_j}_{bus_k}"
371
-
372
- idx_j = buses_list.index(bus_j)
373
- idx_k = buses_list.index(bus_k)
374
-
375
- V_j = sym.Symbol(f"V_{bus_j}", real=True)
376
- V_k = sym.Symbol(f"V_{bus_k}", real=True)
377
- theta_j = sym.Symbol(f"theta_{bus_j}", real=True)
378
- theta_k = sym.Symbol(f"theta_{bus_k}", real=True)
379
-
380
- b_ij_p = 0.0
381
- if f'bs_{line_name}' in self.params_grid:
382
- b_ij_p = self.params_grid[f'bs_{line_name}']
383
-
384
- G_jk = G[idx_j,idx_k]
385
- B_jk = B[idx_j,idx_k]
386
- theta_jk = theta_j - theta_k
387
- P_line_to = V_j*V_k*(G_jk*sym.cos(theta_jk) + B_jk*sym.sin(theta_jk)) - V_j**2*(G_jk)
388
- Q_line_to = V_j*V_k*(G_jk*sym.sin(theta_jk) - B_jk*sym.cos(theta_jk)) + V_j**2*(B_jk)
389
- P_line_from = V_j*V_k*(G_jk*sym.cos(-theta_jk) + B_jk*sym.sin(-theta_jk)) - V_k**2*(G_jk)
390
- Q_line_from = V_j*V_k*(G_jk*sym.sin(-theta_jk) - B_jk*sym.cos(-theta_jk)) + V_k**2*(B_jk)
391
-
392
- if 'monitor' in line:
393
- if line['monitor']:
394
- # h_grid.update({f"p_line_{bus_j}_{bus_k}":P_line_to})
395
- # h_grid.update({f"q_line_{bus_j}_{bus_k}":Q_line_to})
396
- # h_grid.update({f"p_line_{bus_k}_{bus_j}":P_line_from})
397
- # h_grid.update({f"q_line_{bus_k}_{bus_j}":Q_line_from})
398
- 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)
399
- 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)
400
-
401
- g_grid += [p_line_to_pu - P_line_to]
402
- g_grid += [q_line_to_pu - Q_line_to]
403
- g_grid += [p_line_from_pu - P_line_from]
404
- g_grid += [q_line_from_pu - Q_line_from]
405
-
406
- y_grid += [p_line_to_pu,q_line_to_pu,p_line_from_pu,q_line_from_pu]
407
-
408
- U_base = self.buses[idx_j]['U_kV']*1000
409
- I_base = S_base/(np.sqrt(3)*U_base)
410
-
411
- h_grid.update({f'p_line_{bus_j}_{bus_k}':p_line_to_pu*S_base})
412
- h_grid.update({f'q_line_{bus_j}_{bus_k}':q_line_to_pu*S_base})
413
- h_grid.update({f'p_line_{bus_k}_{bus_j}':p_line_from_pu*S_base})
414
- h_grid.update({f'q_line_{bus_k}_{bus_j}':q_line_from_pu*S_base})
415
-
416
- I_j_k_pu = ( p_line_to_pu**2 + q_line_to_pu**2)**0.5/V_j
417
- I_k_j_pu = (p_line_from_pu**2 + q_line_from_pu**2)**0.5/V_k
418
-
419
- h_grid.update({f'I_line_{bus_j}_{bus_k}':I_j_k_pu*I_base})
420
- h_grid.update({f'I_line_{bus_k}_{bus_j}':I_k_j_pu*I_base})
421
-
422
- for bus in self.buses:
423
- if 'monitor' in bus:
424
- if bus['monitor']:
425
- U_base = bus['U_kV']*1000
426
- V = sym.Symbol(f"V_{bus['name']}", real=True)
427
- h_grid.update({f"U_{bus['name']}":V*U_base})
428
-
429
-
430
-
431
- self.dae['f'] += []
432
- self.dae['g'] += g_grid
433
- self.dae['x'] += []
434
- self.dae['y_ini'] += y_grid
435
- self.dae['y_run'] += y_grid
436
- self.dae['u_ini_dict'].update(u_grid)
437
- self.dae['u_run_dict'].update(u_grid)
438
- self.dae['h_dict'].update(h_grid)
439
- self.dae['params_dict'].update(self.params_grid)
440
- self.dae['xy_0_dict'].update(xy_0_dict_grid)
441
-
442
- self.A_incidence = A
443
- self.G_primitive = G_primitive
444
- self.B_primitive = B_primitive
445
-
446
-
447
- self.N_syn = 0
448
- self.N_gformers = 0
449
-
450
- self.generators_list = []
451
- self.generators_id_list = []
452
-
453
- # COI
454
- omega_coi = sym.Symbol("omega_coi", real=True)
455
-
456
- self.H_total = 0
457
- self.omega_coi_numerator = 0.0
458
- self.omega_coi_denominator = 0.0
459
-
460
- self.dae['xy_0_dict'].update({str(omega_coi):1.0})
461
-
462
- self.buses = self.data['buses']
463
- self.buses_list = [bus['name'] for bus in self.buses]
464
-
465
-
466
- def construct(self, name):
467
-
468
- self.contruct_grid()
469
-
470
- # omega_coi = sym.Symbol("omega_coi", real=True)
471
- #
472
- # self.dae['g'] += [ -omega_coi + self.omega_coi_numerator/self.omega_coi_denominator]
473
- # self.dae['y_ini'] += [ omega_coi]
474
- # self.dae['y_run'] += [ omega_coi]
475
-
476
-
477
-
478
- #grid = bmapu_builder.bmapu(data)
479
- if 'syns' in self.data:
480
- add_syns(self)
481
- if 'vscs' in self.data:
482
- add_vscs(self)
483
- if 'vsgs' in self.data:
484
- add_vsgs(self)
485
- if 'sources' in self.data:
486
- add_sources(self)
487
- if 'wecs' in self.data:
488
- add_wecs(self)
489
- if 'pvs' in self.data:
490
- add_pvs(self)
491
- if 'loads' in self.data:
492
- add_loads(self)
493
- if 'pods' in self.data:
494
- add_pods(self)
495
- if 'banks' in self.data:
496
- add_banks(self)
497
-
498
- add_miscellaneous(self)
499
-
500
- #add_vsgs(grid)
501
- omega_coi = sym.Symbol("omega_coi", real=True)
502
-
503
- self.dae['g'] += [ -omega_coi + self.omega_coi_numerator/self.omega_coi_denominator]
504
- self.dae['y_ini'] += [ omega_coi]
505
- self.dae['y_run'] += [ omega_coi]
506
-
507
- # secondary frequency control
508
- xi_freq = sym.Symbol("xi_freq", real=True)
509
- p_agc = sym.Symbol("p_agc", real=True)
510
- K_p_agc = sym.Symbol("K_p_agc", real=True)
511
- K_i_agc = sym.Symbol("K_i_agc", real=True)
512
- K_xif = sym.Symbol("K_xif", real=True)
513
-
514
- epsilon_freq = 1-omega_coi
515
- g_agc = [ -p_agc + K_p_agc*epsilon_freq + K_i_agc*xi_freq ]
516
- y_agc = [ p_agc]
517
- x_agc = [ xi_freq]
518
- f_agc = [epsilon_freq - K_xif*xi_freq]
519
-
520
- self.dae['g'] += g_agc
521
- self.dae['y_ini'] += y_agc
522
- self.dae['y_run'] += y_agc
523
- self.dae['f'] += f_agc
524
- self.dae['x'] += x_agc
525
- self.dae['params_dict'].update({'K_p_agc':self.system['K_p_agc'],'K_i_agc':self.system['K_i_agc']})
526
-
527
- if 'K_xif' in self.system:
528
- self.dae['params_dict'].update({'K_xif':self.system['K_xif']})
529
- else:
530
- self.dae['params_dict'].update({'K_xif':0.0})
531
-
532
-
533
- with open('xy_0.json','w') as fobj:
534
- fobj.write(json.dumps(self.dae['xy_0_dict'],indent=4))
535
-
536
- with open(f'{name}_xy_0.json','w') as fobj:
537
- fobj.write(json.dumps(self.dae['xy_0_dict'],indent=4))
538
-
539
- self.sys_dict = {'name':name,'uz_jacs':self.uz_jacs,
540
- 'params_dict':self.dae['params_dict'],
541
- 'f_list':self.dae['f'],
542
- 'g_list':self.dae['g'] ,
543
- 'x_list':self.dae['x'],
544
- 'y_ini_list':self.dae['y_ini'],
545
- 'y_run_list':self.dae['y_run'],
546
- 'u_run_dict':self.dae['u_run_dict'],
547
- 'u_ini_dict':self.dae['u_ini_dict'],
548
- 'h_dict':self.dae['h_dict']}
549
- if self.testing:
550
- self.sys_dict.update({'testing':True})
551
-
552
-
553
- def compile(self, API=False):
554
-
555
- bldr = db.builder(self.sys_dict,verbose=self.verbose);
556
- bldr.build(API=API)
557
-
558
- def compile_mkl(self, name):
559
-
560
- b = builder(self.sys_dict,verbose=self.verbose)
561
- b.sparse = True
562
- b.mkl = True
563
- b.uz_jacs = False
564
- b.dict2system()
565
- b.functions()
566
- b.jacobians()
567
- b.cwrite()
568
- b.template()
569
- b.compile_mkl()
570
-
571
- def build(self, name ='', API=False):
572
- if name == '':
573
- print('Error: name is not provided.')
574
- self.construct(name)
575
- self.compile(API=False)
576
-
577
- def build_mkl_win(self, name =''):
578
- if name == '':
579
- print('Error: name is not provided.')
580
- self.construct(name)
581
- self.compile_mkl(name)
582
-
583
- def checker(self):
584
-
585
- if not 'syns' in self.data: self.data.update({'syns':[]})
586
- if not 'vscs' in self.data: self.data.update({'vscs':[]})
587
- if not 'vsgs' in self.data: self.data.update({'vsgs':[]})
588
- if not 'genapes' in self.data: self.data.update({'genapes':[]})
589
- if not 'sources' in self.data: self.data.update({'sources':[]})
590
-
591
-
592
- K_deltas_n = 0
593
- for item in self.data['syns']:
594
- if item['K_delta'] > 0.0:
595
- K_deltas_n += 1
596
- for item in self.data['vscs']:
597
- if item['K_delta'] > 0.0:
598
- K_deltas_n += 1
599
- for item in self.data['vsgs']:
600
- if item['K_delta'] > 0.0:
601
- K_deltas_n += 1
602
- for item in self.data['genapes']:
603
- if item['K_delta'] > 0.0:
604
- K_deltas_n += 1
605
- for item in self.data['sources']:
606
- if item['type'] > 'vsource':
607
- K_deltas_n += 1
608
-
609
- if K_deltas_n == 0:
610
- print('One generator must have K_delta > 0.0')
611
- if K_deltas_n > 1:
612
- print('Only one generator must have K_delta > 0.0')
613
-
614
- if len(self.data['genapes']) > 0:
615
- if self.data['system']['K_p_agc'] != 0.0:
616
- print('With a genape in the system K_p_agc must be set to 0.0')
617
- if self.data['system']['K_i_agc'] != 0.0:
618
- print('With a genape in the system K_i_agc must be set to 0.0')
619
- if not self.data['system']['K_xif'] > 0.0:
620
- print('With a genape in the system K_xif must be set larger than 0.0')
621
-
622
-
623
- for item in self.data['syns']:
624
- if 'gov' in item:
625
- if 'K_imw' in item['gov']:
626
- if item['gov']['p_c'] > 0.0 and item['gov']['K_imw'] == 0.0:
627
- print(f"Synchornous machine at bus {item['bus']} has p_c > 0, therefore K_imw should be larger than 0")
628
-
629
-
630
- for item in self.data['syns']:
631
- if 'avr' in item:
632
- if 'sexs' in item['avr']:
633
- if item['avr']['K_ai'] <= 0.0:
634
- print(f"AVR of a synchornous machine at bus {item['bus']} must have constant K_ai larger than 0")
635
-
636
-
637
-
638
-
639
- if __name__ == "__main__":
640
-
641
- data = {
642
- "sys":{"name":"k12p6","S_base":100e6, "K_p_agc":0.01,"K_i_agc":0.01},
643
- "buses":[{"name":"1", "P_W":0.0,"Q_var":0.0,"U_kV":20.0},
644
- {"name":"2", "P_W":0.0,"Q_var":0.0,"U_kV":20.0}],
645
- "lines":[{"bus_j":"1", "bus_k":"2", "X_pu":0.15,"R_pu":0.0, "S_mva":900.0}]
646
- }
647
-
648
- grid = bmapu(data)
649
-
650
- from syns.syns import add_syns
651
- add_syns(grid,'hola')
652
-
653
- # data = {
654
- # "sys":{"name":"k12p6","S_base":100e6, "K_p_agc":0.01,"K_i_agc":0.01},
655
- # "buses":[{"name":"1", "P_W":0.0,"Q_var":0.0,"U_kV":20.0},
656
- # {"name":"2", "P_W":0.0,"Q_var":0.0,"U_kV":20.0},
657
- # {"name":"3", "P_W":0.0,"Q_var":0.0,"U_kV":20.0},
658
- # {"name":"4", "P_W":0.0,"Q_var":0.0,"U_kV":20.0},
659
- # {"name":"5", "P_W":0.0,"Q_var":0.0,"U_kV":230.0},
660
- # {"name":"6", "P_W":0.0,"Q_var":0.0,"U_kV":230.0},
661
- # {"name":"7", "P_W":-967e6,"Q_var":100e6,"U_kV":230.0},
662
- # {"name":"8", "P_W":0.0,"Q_var":0.0,"U_kV":230.0},
663
- # {"name":"9", "P_W":-1767e6,"Q_var":250e6,"U_kV":230.0},
664
- # {"name":"10","P_W":0.0,"Q_var":0.0,"U_kV":230.0},
665
- # {"name":"11","P_W":0.0,"Q_var":0.0,"U_kV":230.0}
666
- # ],
667
- # "lines":[{"bus_j":"1", "bus_k":"5", "X_pu":0.15,"R_pu":0.0, "S_mva":900.0},
668
- # {"bus_j":"2", "bus_k":"6", "X_pu":0.15,"R_pu":0.0, "S_mva":900.0},
669
- # {"bus_j":"3", "bus_k":"11","X_pu":0.15,"R_pu":0.0, "S_mva":900.0},
670
- # {"bus_j":"4", "bus_k":"10","X_pu":0.15,"R_pu":0.0, "S_mva":900.0},
671
- # {"bus_j":"5", "bus_k":"6", "X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":25},
672
- # {"bus_j":"6", "bus_k":"7", "X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":10},
673
- # {"bus_j":"7", "bus_k":"8", "X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":110},
674
- # {"bus_j":"7", "bus_k":"8", "X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":110},
675
- # {"bus_j":"8", "bus_k":"9", "X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":110},
676
- # {"bus_j":"8", "bus_k":"9", "X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":110},
677
- # {"bus_j":"9", "bus_k":"10","X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":10},
678
- # {"bus_j":"10","bus_k":"11","X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":25}]
679
- # }
680
-
681
- # bpu_obj = bpu(data_input=data)
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Thu Feb 25 23:52:55 2021
4
+
5
+ @author: jmmau
6
+ """
7
+
8
+ import numpy as np
9
+ import sympy as sym
10
+ import json
11
+ import os
12
+ import hjson
13
+ from pydae.bmapu.syns.syns import add_syns
14
+ from pydae.bmapu.vscs.vscs import add_vscs
15
+ from pydae.bmapu.vsgs.vsgs import add_vsgs
16
+ from pydae.bmapu.wecs.wecs import add_wecs
17
+ from pydae.bmapu.pvs.pvs import add_pvs
18
+ from pydae.bmapu.loads.loads import add_loads
19
+ from pydae.bmapu.sources.sources import add_sources
20
+ from pydae.bmapu.miscellaneous.miscellaneous import add_miscellaneous
21
+ from pydae.bmapu.pods.pods import add_pods
22
+ from pydae.bmapu.miscellaneous.banks import add_banks
23
+
24
+ import pydae.build_cffi as db
25
+ from pydae.build_v2 import builder
26
+
27
+ import requests
28
+
29
+ # todo:
30
+ # S_base can't be modified becuase impedances in element base are computed
31
+ # in S_base only in the build
32
+ class bmapu:
33
+ '''
34
+
35
+
36
+ Parameters
37
+ ----------
38
+ data_input : string or dict
39
+ File path to the system data information or dictionary with the information.
40
+
41
+ Returns
42
+ -------
43
+ dict
44
+ Dictionary with the equations for pydae.
45
+
46
+ {
47
+ 'sys':{'name':'pf_1','S_base':100e6},
48
+ 'buses':[{'name':'GRI','P_W':0.0,'Q_var':0.0,'U_kV':66.0, 'type':'slack'},
49
+ {'name':'POI','P_W':0.0,'Q_var':0.0,'U_kV':66.0},
50
+ {'name':'PMV','P_W':0.0,'Q_var':0.0,'U_kV':20.0}],
51
+ 'lines':[{'bus_j':'GRI','bus_k':'POI','X_km':0.4,'R_km':0.12,'km':20},
52
+ {'bus_j':'POI','bus_k':'PMV','X_pu':0.04,'R_pu':0.01, 'S_mva':50.0}]
53
+ }
54
+
55
+
56
+ '''
57
+
58
+ def __init__(self,data_input='',testing=False):
59
+
60
+ if type(data_input) == str:
61
+ if 'http' in data_input:
62
+ url = data_input
63
+ resp = requests.get(url)
64
+ data = hjson.loads(resp.text)
65
+ else:
66
+ if os.path.splitext(data_input)[1] == '.json':
67
+ with open(data_input,'r') as fobj:
68
+ data = json.loads(fobj.read().replace("'",'"'))
69
+ if os.path.splitext(data_input)[1] == '.hjson':
70
+ with open(data_input,'r') as fobj:
71
+ data = hjson.loads(fobj.read().replace("'",'"'))
72
+ elif type(data_input) == dict:
73
+ data = data_input
74
+
75
+ self.data = data
76
+
77
+ if not 'lines' in self.data:
78
+ self.data['lines'] = []
79
+ if not 'shunts' in self.data:
80
+ self.data['shunts'] = []
81
+ if not 'transformers' in self.data:
82
+ self.data['transformers'] = []
83
+ if not 'loads' in self.data:
84
+ self.data['loads'] = []
85
+
86
+ self.system = data['system']
87
+ self.buses = data['buses']
88
+ self.lines = data['lines']
89
+ self.shunts = data['shunts']
90
+ self.transformers = data['transformers']
91
+ self.loads = data['loads']
92
+
93
+ self.x_grid = []
94
+ self.f_grid = []
95
+
96
+ self.params_grid = {'S_base':self.system['S_base']}
97
+ self.S_base = sym.Symbol("S_base", real=True)
98
+ self.N_bus = len(self.buses)
99
+ self.N_branch = 3*len(self.lines) + len(self.shunts) + 2*len(self.transformers)
100
+
101
+ self.dae = {'f':[],'g':[],'x':[],'y_ini':[],'y_run':[],
102
+ 'u_ini_dict':{},'u_run_dict':{},'params_dict':{},
103
+ 'h_dict':{},'xy_0_dict':{}}
104
+
105
+ self.uz_jacs = True
106
+ self.verbose = False
107
+ self.testing = testing
108
+
109
+ def contruct_grid(self):
110
+
111
+ N_branch = self.N_branch
112
+ N_bus = self.N_bus
113
+ sys = self.system
114
+
115
+ S_base = sym.Symbol('S_base', real=True)
116
+
117
+ xy_0_dict_grid = {}
118
+ u_grid = {}
119
+ h_grid = {}
120
+
121
+ A = sym.zeros(N_branch,N_bus)
122
+ G_primitive = sym.zeros(N_branch,N_branch)
123
+ B_primitive = sym.zeros(N_branch,N_branch)
124
+ buses_list = [bus['name'] for bus in self.buses]
125
+ it = 0
126
+ for line in self.lines:
127
+
128
+ bus_j = line['bus_j']
129
+ bus_k = line['bus_k']
130
+
131
+ idx_j = buses_list.index(bus_j)
132
+ idx_k = buses_list.index(bus_k)
133
+
134
+ A[it,idx_j] = 1
135
+ A[it,idx_k] =-1
136
+ A[it+1,idx_j] = 1
137
+ A[it+2,idx_k] = 1
138
+
139
+ line_name = f"{bus_j}_{bus_k}"
140
+ g_jk = sym.Symbol(f"g_{line_name}", real=True)
141
+ b_jk = sym.Symbol(f"b_{line_name}", real=True)
142
+ bs_jk = sym.Symbol(f"bs_{line_name}", real=True)
143
+ G_primitive[it,it] = g_jk
144
+ B_primitive[it,it] = b_jk
145
+ B_primitive[it+1,it+1] = bs_jk/2
146
+ B_primitive[it+2,it+2] = bs_jk/2
147
+
148
+ if not 'thermal' in line:
149
+ line.update({'thermal':False})
150
+
151
+ if 'X_pu' in line:
152
+ if 'S_mva' in line: S_line = 1e6*line['S_mva']
153
+ R = line['R_pu']*sys['S_base']/S_line # in pu of the system base
154
+ X = line['X_pu']*sys['S_base']/S_line # in pu of the system base
155
+ G = R/(R**2+X**2)
156
+ B = -X/(R**2+X**2)
157
+ self.params_grid.update({f"g_{line_name}":G})
158
+ self.params_grid.update({f'b_{line_name}':B})
159
+
160
+ if 'X' in line:
161
+ bus_idx = buses_list.index(line['bus_j'])
162
+ U_base = self.buses[bus_idx]['U_kV']*1000
163
+ Z_base = U_base**2/sys['S_base']
164
+ R = line['R']/Z_base # in pu of the system base
165
+ X = line['X']/Z_base # in pu of the system base
166
+ G = R/(R**2+X**2)
167
+ B = -X/(R**2+X**2)
168
+ self.params_grid.update({f"g_{line_name}":G})
169
+ self.params_grid.update({f'b_{line_name}':B})
170
+
171
+ if 'X_km' in line:
172
+ bus_idx = buses_list.index(line['bus_j'])
173
+ U_base = self.buses[bus_idx]['U_kV']*1000
174
+ Z_base = U_base**2/sys['S_base']
175
+ if line['thermal']:
176
+ R = sym.Symbol(f"R_{line_name}", real=True)
177
+ R_N = line['R_km']*line['km']/Z_base # in pu of the system base
178
+ u_grid.update({str(R):R_N})
179
+ else:
180
+ R = line['R_km']*line['km']/Z_base # in pu of the system base
181
+
182
+ X = line['X_km']*line['km']/Z_base # in pu of the system base
183
+ G = R/(R**2+X**2)
184
+ B = -X/(R**2+X**2)
185
+ self.params_grid.update({f"g_{line_name}":G})
186
+ self.params_grid.update({f'b_{line_name}':B})
187
+
188
+ self.params_grid.update({f'bs_{line_name}':0.0})
189
+ if 'Bs_pu' in line:
190
+ if 'S_mva' in line: S_line = 1e6*line['S_mva']
191
+ Bs = line['Bs_pu']*S_line/sys['S_base'] # in pu of the system base
192
+ bs = Bs
193
+ self.params_grid[f'bs_{line_name}'] = bs
194
+
195
+ if 'Bs_km' in line:
196
+ bus_idx = buses_list.index(line['bus_j'])
197
+ U_base = self.buses[bus_idx]['U_kV']*1000
198
+ Z_base = U_base**2/sys['S_base']
199
+ Y_base = 1.0/Z_base
200
+ Bs = line['Bs_km']*line['km']/Y_base # in pu of the system base
201
+ bs = Bs
202
+ self.params_grid[f'bs_{line_name}'] = bs
203
+
204
+ it += 3
205
+
206
+ for trafo in self.transformers:
207
+
208
+ bus_j = trafo['bus_j']
209
+ bus_k = trafo['bus_k']
210
+
211
+ idx_j = buses_list.index(bus_j)
212
+ idx_k = buses_list.index(bus_k)
213
+
214
+ A[it,idx_j] = 1
215
+ A[it+1,idx_k] = 1
216
+
217
+ trafo_name = f"{bus_j}_{bus_k}"
218
+ g_jk = sym.Symbol(f"g_cc_{trafo_name}", real=True)
219
+ b_jk = sym.Symbol(f"b_cc_{trafo_name}", real=True)
220
+ tap = sym.Symbol(f"tap_{trafo_name}", real=True)
221
+ ang = sym.Symbol(f"ang_{trafo_name}", real=True)
222
+ a_s = tap*sym.cos(ang)
223
+ b_s = tap*sym.sin(ang)
224
+
225
+ Y_cc = g_jk + sym.I*b_jk
226
+
227
+
228
+ Y_primitive =sym.Matrix([[ Y_cc/(a_s**2+b_s**2),-Y_cc/(a_s-sym.I*b_s)],
229
+ [-Y_cc/(a_s+sym.I*b_s), Y_cc]])
230
+
231
+
232
+ G_primitive[it:it+2,it:it+2] = sym.re(Y_primitive)
233
+ B_primitive[it:it+2,it:it+2] = sym.im(Y_primitive)
234
+
235
+
236
+ if 'X_pu' in trafo:
237
+ if 'S_mva' in trafo: S_trafo = 1e6*trafo['S_mva']
238
+ R = trafo['R_pu']*sys['S_base']/S_trafo # in pu of the system base
239
+ X = trafo['X_pu']*sys['S_base']/S_trafo # in pu of the system base
240
+ G = R/(R**2+X**2)
241
+ B = -X/(R**2+X**2)
242
+ self.params_grid.update({f"g_cc_{trafo_name}":G})
243
+ self.params_grid.update({f'b_cc_{trafo_name}':B})
244
+ tap_m = 1.0
245
+ if 'tap_m' in trafo:
246
+ tap_m = trafo['tap_m']
247
+ self.params_grid.update({f'tap_{trafo_name}':tap_m})
248
+ self.params_grid.update({f'ang_{trafo_name}':0.0})
249
+
250
+
251
+ if 'X' in trafo:
252
+ bus_idx = buses_list.index(trafo['bus_j'])
253
+ U_base = self.buses[bus_idx]['U_kV']*1000
254
+ Z_base = U_base**2/sys['S_base']
255
+ R = trafo['R']/Z_base # in pu of the system base
256
+ X = trafo['X']/Z_base # in pu of the system base
257
+ G = R/(R**2+X**2)
258
+ B = -X/(R**2+X**2)
259
+ self.params_grid.update({f"g_cc_{trafo_name}":G})
260
+ self.params_grid.update({f'b_cc_{trafo_name}':B})
261
+ self.params_grid.update({f'tap_{trafo_name}':1.0})
262
+ self.params_grid.update({f'ang_{trafo_name}':0.0})
263
+
264
+ it += 2
265
+
266
+
267
+ for shunt in self.shunts:
268
+
269
+ bus_j = shunt['bus']
270
+ idx_j = buses_list.index(bus_j)
271
+
272
+ A[it,idx_j] = 1
273
+
274
+ shunt_name = f"{bus_j}"
275
+ g_j = sym.Symbol(f"g_shunt_{shunt_name}", real=True)
276
+ b_j = sym.Symbol(f"b_shunt_{shunt_name}", real=True)
277
+ G_primitive[it,it] = g_j
278
+ B_primitive[it,it] = b_j
279
+
280
+
281
+ if 'X_pu' in shunt:
282
+ if 'S_mva' in shunt: S_line = 1e6*shunt['S_mva']
283
+ R = shunt['R_pu']*sys['S_base']/S_line # in pu of the system base
284
+ X = shunt['X_pu']*sys['S_base']/S_line # in pu of the system base
285
+ G = R/(R**2+X**2)
286
+ B = -X/(R**2+X**2)
287
+ self.params_grid.update({f"g_shunt_{shunt_name}":G})
288
+ self.params_grid.update({f'b_shunt_{shunt_name}':B})
289
+
290
+ if 'X' in shunt:
291
+ U_base = self.buses[idx_j]['U_kV']*1000
292
+ Z_base = U_base**2/sys['S_base']
293
+ R = shunt['R']/Z_base # in pu of the system base
294
+ X = shunt['X']/Z_base # in pu of the system base
295
+ G = R/(R**2+X**2)
296
+ B = -X/(R**2+X**2)
297
+ self.params_grid.update({f"g_shunt_{shunt_name}":G})
298
+ self.params_grid.update({f'b_shunt_{shunt_name}':B})
299
+
300
+ it += 1
301
+
302
+ G = A.T * G_primitive * A
303
+ B = A.T * B_primitive * A
304
+
305
+ sin = sym.sin
306
+ cos = sym.cos
307
+ y_grid = []
308
+ g = sym.zeros(2*N_bus,1)
309
+
310
+ for j in range(N_bus):
311
+ bus_j_name = buses_list[j]
312
+ P_j = sym.Symbol(f"P_{bus_j_name}", real=True)
313
+ Q_j = sym.Symbol(f"Q_{bus_j_name}", real=True)
314
+ g[2*j] = -P_j/S_base
315
+ g[2*j+1] = -Q_j/S_base
316
+ for k in range(N_bus):
317
+
318
+ bus_k_name = buses_list[k]
319
+ V_j = sym.Symbol(f"V_{bus_j_name}", real=True)
320
+ V_k = sym.Symbol(f"V_{bus_k_name}", real=True)
321
+ theta_j = sym.Symbol(f"theta_{bus_j_name}", real=True)
322
+ theta_k = sym.Symbol(f"theta_{bus_k_name}", real=True)
323
+ g[2*j] += V_j*V_k*(G[j,k]*cos(theta_j - theta_k) + B[j,k]*sin(theta_j - theta_k))
324
+ g[2*j+1] += V_j*V_k*(G[j,k]*sin(theta_j - theta_k) - B[j,k]*cos(theta_j - theta_k))
325
+ h_grid.update({f"V_{bus_j_name}":V_j})
326
+ bus = self.buses[j]
327
+ bus_name = bus['name']
328
+ if 'type' in bus:
329
+ if bus['type'] == 'slack':
330
+ y_grid += [P_j]
331
+ y_grid += [Q_j]
332
+ u_grid.update({f"V_{bus_name}":1.0})
333
+ u_grid.update({f"theta_{bus_name}":0.0})
334
+ else:
335
+ y_grid += [V_j]
336
+ y_grid += [theta_j]
337
+ u_grid.update({f"P_{bus_name}":bus['P_W']})
338
+ u_grid.update({f"Q_{bus_name}":bus['Q_var']})
339
+ xy_0_dict_grid.update({str(V_j):1.0,str(theta_j):0.0})
340
+
341
+ self.params_grid.update({f'U_{bus_name}_n':bus['U_kV']*1000})
342
+ g_grid = list(g)
343
+
344
+ if False:
345
+ v_sym_list = []
346
+ for bus in buses_list:
347
+ V_m = sym.Symbol(f'V_{bus}',real=True)
348
+ V_a = sym.Symbol(f'theta_{bus}',real=True)
349
+ v_sym_list += [V_m*sym.exp(sym.I*V_a)]
350
+
351
+ sym.Matrix(v_sym_list)
352
+
353
+ I_lines = (G_primitive+1j*B_primitive) * A * sym.Matrix(v_sym_list)
354
+
355
+ it = 0
356
+ for line in self.lines:
357
+ I_jk_r = sym.Symbol(f"I_{line['bus_j']}_{line['bus_k']}_r", real=True)
358
+ I_jk_i = sym.Symbol(f"I_{line['bus_j']}_{line['bus_k']}_i", real=True)
359
+ g_grid += [-I_jk_r + sym.re(I_lines[it])]
360
+ g_grid += [-I_jk_i + sym.im(I_lines[it])]
361
+ y_grid += [I_jk_r]
362
+ y_grid += [I_jk_i]
363
+ it += 1
364
+
365
+ for line in self.lines:
366
+
367
+ bus_j = line['bus_j']
368
+ bus_k = line['bus_k']
369
+
370
+ line_name = f"{bus_j}_{bus_k}"
371
+
372
+ idx_j = buses_list.index(bus_j)
373
+ idx_k = buses_list.index(bus_k)
374
+
375
+ V_j = sym.Symbol(f"V_{bus_j}", real=True)
376
+ V_k = sym.Symbol(f"V_{bus_k}", real=True)
377
+ theta_j = sym.Symbol(f"theta_{bus_j}", real=True)
378
+ theta_k = sym.Symbol(f"theta_{bus_k}", real=True)
379
+
380
+ b_ij_p = 0.0
381
+ if f'bs_{line_name}' in self.params_grid:
382
+ b_ij_p = self.params_grid[f'bs_{line_name}']
383
+
384
+ G_jk = G[idx_j,idx_k]
385
+ B_jk = B[idx_j,idx_k]
386
+ theta_jk = theta_j - theta_k
387
+ P_line_to = V_j*V_k*(G_jk*sym.cos(theta_jk) + B_jk*sym.sin(theta_jk)) - V_j**2*(G_jk)
388
+ Q_line_to = V_j*V_k*(G_jk*sym.sin(theta_jk) - B_jk*sym.cos(theta_jk)) + V_j**2*(B_jk)
389
+ P_line_from = V_j*V_k*(G_jk*sym.cos(-theta_jk) + B_jk*sym.sin(-theta_jk)) - V_k**2*(G_jk)
390
+ Q_line_from = V_j*V_k*(G_jk*sym.sin(-theta_jk) - B_jk*sym.cos(-theta_jk)) + V_k**2*(B_jk)
391
+
392
+ if 'monitor' in line:
393
+ if line['monitor']:
394
+ # h_grid.update({f"p_line_{bus_j}_{bus_k}":P_line_to})
395
+ # h_grid.update({f"q_line_{bus_j}_{bus_k}":Q_line_to})
396
+ # h_grid.update({f"p_line_{bus_k}_{bus_j}":P_line_from})
397
+ # h_grid.update({f"q_line_{bus_k}_{bus_j}":Q_line_from})
398
+ 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)
399
+ 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)
400
+
401
+ g_grid += [p_line_to_pu - P_line_to]
402
+ g_grid += [q_line_to_pu - Q_line_to]
403
+ g_grid += [p_line_from_pu - P_line_from]
404
+ g_grid += [q_line_from_pu - Q_line_from]
405
+
406
+ y_grid += [p_line_to_pu,q_line_to_pu,p_line_from_pu,q_line_from_pu]
407
+
408
+ U_base = self.buses[idx_j]['U_kV']*1000
409
+ I_base = S_base/(np.sqrt(3)*U_base)
410
+
411
+ h_grid.update({f'p_line_{bus_j}_{bus_k}':p_line_to_pu*S_base})
412
+ h_grid.update({f'q_line_{bus_j}_{bus_k}':q_line_to_pu*S_base})
413
+ h_grid.update({f'p_line_{bus_k}_{bus_j}':p_line_from_pu*S_base})
414
+ h_grid.update({f'q_line_{bus_k}_{bus_j}':q_line_from_pu*S_base})
415
+
416
+ I_j_k_pu = ( p_line_to_pu**2 + q_line_to_pu**2)**0.5/V_j
417
+ I_k_j_pu = (p_line_from_pu**2 + q_line_from_pu**2)**0.5/V_k
418
+
419
+ h_grid.update({f'I_line_{bus_j}_{bus_k}':I_j_k_pu*I_base})
420
+ h_grid.update({f'I_line_{bus_k}_{bus_j}':I_k_j_pu*I_base})
421
+
422
+ for bus in self.buses:
423
+ if 'monitor' in bus:
424
+ if bus['monitor']:
425
+ U_base = bus['U_kV']*1000
426
+ V = sym.Symbol(f"V_{bus['name']}", real=True)
427
+ h_grid.update({f"U_{bus['name']}":V*U_base})
428
+
429
+
430
+
431
+ self.dae['f'] += []
432
+ self.dae['g'] += g_grid
433
+ self.dae['x'] += []
434
+ self.dae['y_ini'] += y_grid
435
+ self.dae['y_run'] += y_grid
436
+ self.dae['u_ini_dict'].update(u_grid)
437
+ self.dae['u_run_dict'].update(u_grid)
438
+ self.dae['h_dict'].update(h_grid)
439
+ self.dae['params_dict'].update(self.params_grid)
440
+ self.dae['xy_0_dict'].update(xy_0_dict_grid)
441
+
442
+ self.A_incidence = A
443
+ self.G_primitive = G_primitive
444
+ self.B_primitive = B_primitive
445
+
446
+
447
+ self.N_syn = 0
448
+ self.N_gformers = 0
449
+
450
+ self.generators_list = []
451
+ self.generators_id_list = []
452
+
453
+ # COI
454
+ omega_coi = sym.Symbol("omega_coi", real=True)
455
+
456
+ self.H_total = 0
457
+ self.omega_coi_numerator = 0.0
458
+ self.omega_coi_denominator = 0.0
459
+
460
+ self.dae['xy_0_dict'].update({str(omega_coi):1.0})
461
+
462
+ self.buses = self.data['buses']
463
+ self.buses_list = [bus['name'] for bus in self.buses]
464
+
465
+
466
+ def construct(self, name):
467
+
468
+ self.contruct_grid()
469
+
470
+ # omega_coi = sym.Symbol("omega_coi", real=True)
471
+ #
472
+ # self.dae['g'] += [ -omega_coi + self.omega_coi_numerator/self.omega_coi_denominator]
473
+ # self.dae['y_ini'] += [ omega_coi]
474
+ # self.dae['y_run'] += [ omega_coi]
475
+
476
+
477
+
478
+ #grid = bmapu_builder.bmapu(data)
479
+ if 'syns' in self.data:
480
+ add_syns(self)
481
+ if 'vscs' in self.data:
482
+ add_vscs(self)
483
+ if 'vsgs' in self.data:
484
+ add_vsgs(self)
485
+ if 'sources' in self.data:
486
+ add_sources(self)
487
+ if 'wecs' in self.data:
488
+ add_wecs(self)
489
+ if 'pvs' in self.data:
490
+ add_pvs(self)
491
+ if 'loads' in self.data:
492
+ add_loads(self)
493
+ if 'pods' in self.data:
494
+ add_pods(self)
495
+ if 'banks' in self.data:
496
+ add_banks(self)
497
+
498
+ add_miscellaneous(self)
499
+
500
+ #add_vsgs(grid)
501
+ omega_coi = sym.Symbol("omega_coi", real=True)
502
+
503
+ self.dae['g'] += [ -omega_coi + self.omega_coi_numerator/self.omega_coi_denominator]
504
+ self.dae['y_ini'] += [ omega_coi]
505
+ self.dae['y_run'] += [ omega_coi]
506
+
507
+ # secondary frequency control
508
+ xi_freq = sym.Symbol("xi_freq", real=True)
509
+ p_agc = sym.Symbol("p_agc", real=True)
510
+ K_p_agc = sym.Symbol("K_p_agc", real=True)
511
+ K_i_agc = sym.Symbol("K_i_agc", real=True)
512
+ K_xif = sym.Symbol("K_xif", real=True)
513
+
514
+ epsilon_freq = 1-omega_coi
515
+ g_agc = [ -p_agc + K_p_agc*epsilon_freq + K_i_agc*xi_freq ]
516
+ y_agc = [ p_agc]
517
+ x_agc = [ xi_freq]
518
+ f_agc = [epsilon_freq - K_xif*xi_freq]
519
+
520
+ self.dae['g'] += g_agc
521
+ self.dae['y_ini'] += y_agc
522
+ self.dae['y_run'] += y_agc
523
+ self.dae['f'] += f_agc
524
+ self.dae['x'] += x_agc
525
+ self.dae['params_dict'].update({'K_p_agc':self.system['K_p_agc'],'K_i_agc':self.system['K_i_agc']})
526
+
527
+ if 'K_xif' in self.system:
528
+ self.dae['params_dict'].update({'K_xif':self.system['K_xif']})
529
+ else:
530
+ self.dae['params_dict'].update({'K_xif':0.0})
531
+
532
+
533
+ with open('xy_0.json','w') as fobj:
534
+ fobj.write(json.dumps(self.dae['xy_0_dict'],indent=4))
535
+
536
+ with open(f'{name}_xy_0.json','w') as fobj:
537
+ fobj.write(json.dumps(self.dae['xy_0_dict'],indent=4))
538
+
539
+ self.sys_dict = {'name':name,'uz_jacs':self.uz_jacs,
540
+ 'params_dict':self.dae['params_dict'],
541
+ 'f_list':self.dae['f'],
542
+ 'g_list':self.dae['g'] ,
543
+ 'x_list':self.dae['x'],
544
+ 'y_ini_list':self.dae['y_ini'],
545
+ 'y_run_list':self.dae['y_run'],
546
+ 'u_run_dict':self.dae['u_run_dict'],
547
+ 'u_ini_dict':self.dae['u_ini_dict'],
548
+ 'h_dict':self.dae['h_dict']}
549
+ if self.testing:
550
+ self.sys_dict.update({'testing':True})
551
+
552
+
553
+ def compile(self, API=False):
554
+
555
+ bldr = db.builder(self.sys_dict,verbose=self.verbose);
556
+ bldr.build(API=API)
557
+
558
+ def compile_mkl(self, name):
559
+
560
+ b = builder(self.sys_dict,verbose=self.verbose)
561
+ b.sparse = True
562
+ b.mkl = True
563
+ b.uz_jacs = False
564
+ b.dict2system()
565
+ b.functions()
566
+ b.jacobians()
567
+ b.cwrite()
568
+ b.template()
569
+ b.compile_mkl()
570
+
571
+ def build(self, name ='', API=False):
572
+ if name == '':
573
+ print('Error: name is not provided.')
574
+ self.construct(name)
575
+ self.compile(API=False)
576
+
577
+ def build_mkl_win(self, name =''):
578
+ if name == '':
579
+ print('Error: name is not provided.')
580
+ self.construct(name)
581
+ self.compile_mkl(name)
582
+
583
+ def checker(self):
584
+
585
+ if not 'syns' in self.data: self.data.update({'syns':[]})
586
+ if not 'vscs' in self.data: self.data.update({'vscs':[]})
587
+ if not 'vsgs' in self.data: self.data.update({'vsgs':[]})
588
+ if not 'genapes' in self.data: self.data.update({'genapes':[]})
589
+ if not 'sources' in self.data: self.data.update({'sources':[]})
590
+
591
+
592
+ K_deltas_n = 0
593
+ for item in self.data['syns']:
594
+ if item['K_delta'] > 0.0:
595
+ K_deltas_n += 1
596
+ for item in self.data['vscs']:
597
+ if item['K_delta'] > 0.0:
598
+ K_deltas_n += 1
599
+ for item in self.data['vsgs']:
600
+ if item['K_delta'] > 0.0:
601
+ K_deltas_n += 1
602
+ for item in self.data['genapes']:
603
+ if item['K_delta'] > 0.0:
604
+ K_deltas_n += 1
605
+ for item in self.data['sources']:
606
+ if item['type'] > 'vsource':
607
+ K_deltas_n += 1
608
+
609
+ if K_deltas_n == 0:
610
+ print('One generator must have K_delta > 0.0')
611
+ if K_deltas_n > 1:
612
+ print('Only one generator must have K_delta > 0.0')
613
+
614
+ if len(self.data['genapes']) > 0:
615
+ if self.data['system']['K_p_agc'] != 0.0:
616
+ print('With a genape in the system K_p_agc must be set to 0.0')
617
+ if self.data['system']['K_i_agc'] != 0.0:
618
+ print('With a genape in the system K_i_agc must be set to 0.0')
619
+ if not self.data['system']['K_xif'] > 0.0:
620
+ print('With a genape in the system K_xif must be set larger than 0.0')
621
+
622
+
623
+ for item in self.data['syns']:
624
+ if 'gov' in item:
625
+ if 'K_imw' in item['gov']:
626
+ if item['gov']['p_c'] > 0.0 and item['gov']['K_imw'] == 0.0:
627
+ print(f"Synchornous machine at bus {item['bus']} has p_c > 0, therefore K_imw should be larger than 0")
628
+
629
+
630
+ for item in self.data['syns']:
631
+ if 'avr' in item:
632
+ if 'sexs' in item['avr']:
633
+ if item['avr']['K_ai'] <= 0.0:
634
+ print(f"AVR of a synchornous machine at bus {item['bus']} must have constant K_ai larger than 0")
635
+
636
+
637
+
638
+
639
+ if __name__ == "__main__":
640
+
641
+ data = {
642
+ "sys":{"name":"k12p6","S_base":100e6, "K_p_agc":0.01,"K_i_agc":0.01},
643
+ "buses":[{"name":"1", "P_W":0.0,"Q_var":0.0,"U_kV":20.0},
644
+ {"name":"2", "P_W":0.0,"Q_var":0.0,"U_kV":20.0}],
645
+ "lines":[{"bus_j":"1", "bus_k":"2", "X_pu":0.15,"R_pu":0.0, "S_mva":900.0}]
646
+ }
647
+
648
+ grid = bmapu(data)
649
+
650
+ from syns.syns import add_syns
651
+ add_syns(grid,'hola')
652
+
653
+ # data = {
654
+ # "sys":{"name":"k12p6","S_base":100e6, "K_p_agc":0.01,"K_i_agc":0.01},
655
+ # "buses":[{"name":"1", "P_W":0.0,"Q_var":0.0,"U_kV":20.0},
656
+ # {"name":"2", "P_W":0.0,"Q_var":0.0,"U_kV":20.0},
657
+ # {"name":"3", "P_W":0.0,"Q_var":0.0,"U_kV":20.0},
658
+ # {"name":"4", "P_W":0.0,"Q_var":0.0,"U_kV":20.0},
659
+ # {"name":"5", "P_W":0.0,"Q_var":0.0,"U_kV":230.0},
660
+ # {"name":"6", "P_W":0.0,"Q_var":0.0,"U_kV":230.0},
661
+ # {"name":"7", "P_W":-967e6,"Q_var":100e6,"U_kV":230.0},
662
+ # {"name":"8", "P_W":0.0,"Q_var":0.0,"U_kV":230.0},
663
+ # {"name":"9", "P_W":-1767e6,"Q_var":250e6,"U_kV":230.0},
664
+ # {"name":"10","P_W":0.0,"Q_var":0.0,"U_kV":230.0},
665
+ # {"name":"11","P_W":0.0,"Q_var":0.0,"U_kV":230.0}
666
+ # ],
667
+ # "lines":[{"bus_j":"1", "bus_k":"5", "X_pu":0.15,"R_pu":0.0, "S_mva":900.0},
668
+ # {"bus_j":"2", "bus_k":"6", "X_pu":0.15,"R_pu":0.0, "S_mva":900.0},
669
+ # {"bus_j":"3", "bus_k":"11","X_pu":0.15,"R_pu":0.0, "S_mva":900.0},
670
+ # {"bus_j":"4", "bus_k":"10","X_pu":0.15,"R_pu":0.0, "S_mva":900.0},
671
+ # {"bus_j":"5", "bus_k":"6", "X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":25},
672
+ # {"bus_j":"6", "bus_k":"7", "X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":10},
673
+ # {"bus_j":"7", "bus_k":"8", "X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":110},
674
+ # {"bus_j":"7", "bus_k":"8", "X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":110},
675
+ # {"bus_j":"8", "bus_k":"9", "X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":110},
676
+ # {"bus_j":"8", "bus_k":"9", "X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":110},
677
+ # {"bus_j":"9", "bus_k":"10","X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":10},
678
+ # {"bus_j":"10","bus_k":"11","X_km":0.529,"R_km":0.0529,"Bs_km":2.1e-6,"km":25}]
679
+ # }
680
+
681
+ # bpu_obj = bpu(data_input=data)
682
682