e2nIEE / pandapower
1
# -*- coding: utf-8 -*-
2

3
# Copyright (c) 2016-2021 by University of Kassel and Fraunhofer Institute for Energy Economics
4
# and Energy System Technology (IEE), Kassel. All rights reserved.
5

6

7 1
import numpy as np
8 1
from numpy import complex128
9 1
import pandas as pd
10 1
from pandapower.auxiliary import _sum_by_group, sequence_to_phase, _sum_by_group_nvals
11 1
from pandapower.pypower.idx_bus import VM, VA, PD, QD, LAM_P, LAM_Q, BASE_KV,NONE
12

13 1
from pandapower.pypower.idx_gen import PG, QG
14 1
from pandapower.build_bus import _get_motor_pq, _get_symmetric_pq_of_unsymetric_element
15

16

17 1
def _set_buses_out_of_service(ppc):
18

19 1
    disco = np.where(ppc["bus"][:, 1] == NONE)[0]
20 1
    ppc["bus"][disco, VM] = np.nan
21 1
    ppc["bus"][disco, VA] = np.nan
22 1
    ppc["bus"][disco, PD] = 0
23 1
    ppc["bus"][disco, QD] = 0
24

25

26 1
def _get_bus_v_results(net, ppc, suffix=None):
27 1
    ac = net["_options"]["ac"]
28 1
    bus_idx = _get_bus_idx(net)
29

30 1
    res_table = "res_bus" if suffix is None else "res_bus%s" % suffix
31 1
    if ac:
32 1
        net[res_table]["vm_pu"] = ppc["bus"][bus_idx][:, VM]
33
    # voltage angles
34 1
    net[res_table]["va_degree"] = ppc["bus"][bus_idx][:, VA]
35

36

37 1
def _get_bus_v_results_3ph(net, ppc0, ppc1, ppc2):
38 1
    ac = net["_options"]["ac"]
39 1
    V012_pu = _V012_from_ppc012(net, ppc0, ppc1, ppc2)
40
    # Uncomment for results in kV instead of pu
41
    # bus_base_kv = ppc0["bus"][:,BASE_KV]/np.sqrt(3)
42
    # V012_pu = V012_pu*bus_base_kv
43

44 1
    Vabc_pu = sequence_to_phase(V012_pu)
45

46 1
    if ac:
47 1
        net["res_bus_3ph"]["vm_a_pu"] = np.abs(Vabc_pu[0, :].flatten())
48 1
        net["res_bus_3ph"]["vm_b_pu"] = np.abs(Vabc_pu[1, :].flatten())
49 1
        net["res_bus_3ph"]["vm_c_pu"] = np.abs(Vabc_pu[2, :].flatten())
50
    # voltage angles
51 1
    net["res_bus_3ph"]["va_a_degree"] = np.angle(Vabc_pu[0, :].flatten())*180/np.pi
52 1
    net["res_bus_3ph"]["va_b_degree"] = np.angle(Vabc_pu[1, :].flatten())*180/np.pi
53 1
    net["res_bus_3ph"]["va_c_degree"] = np.angle(Vabc_pu[2, :].flatten())*180/np.pi
54 1
    net["res_bus_3ph"]["unbalance_percent"] = np.abs(V012_pu[2, :]/V012_pu[1, :])*100
55 1
    net["res_bus_3ph"].index = net["bus"].index
56

57

58 1
def _V012_from_ppc012(net, ppc0, ppc1, ppc2):
59 1
    bus_idx = _get_bus_idx(net)
60 1
    V012_pu = np.zeros((3, len(bus_idx)), dtype=complex128)
61 1
    V012_pu[0, :] = ppc0["bus"][bus_idx][:, VM] * np.exp(1j * np.deg2rad(ppc0["bus"][bus_idx][:, VA]))
62 1
    V012_pu[1, :] = ppc1["bus"][bus_idx][:, VM] * np.exp(1j * np.deg2rad(ppc1["bus"][bus_idx][:, VA]))
63 1
    V012_pu[2, :] = ppc2["bus"][bus_idx][:, VM] * np.exp(1j * np.deg2rad(ppc2["bus"][bus_idx][:, VA]))
64 1
    return V012_pu
65

66

67 1
def _get_bus_idx(net):
68 1
    bus_lookup = net["_pd2ppc_lookups"]["bus"]
69 1
    ppi = net["bus"].index.values
70 1
    bus_idx = bus_lookup[ppi]
71 1
    return bus_idx
72

73

74 1
def _get_opf_marginal_prices(net, ppc):
75 1
    bus_idx = _get_bus_idx(net)
76 1
    net["res_bus"]["lam_p"] = ppc["bus"][bus_idx][:, LAM_P]
77 1
    net["res_bus"]["lam_q"] = ppc["bus"][bus_idx][:, LAM_Q]
78

79

80 1
def _get_bus_results(net, ppc, bus_pq):
81 1
    ac = net["_options"]["ac"]
82 1
    mode = net["_options"]["mode"]
83

84
    # write sum of p and q values to bus
85 1
    net["res_bus"]["p_mw"].values[:] = bus_pq[:, 0]
86 1
    if ac:
87 1
        net["res_bus"]["q_mvar"].values[:] = bus_pq[:, 1]
88

89
    # opf variables
90 1
    if mode == "opf":
91 1
        _get_opf_marginal_prices(net, ppc)
92

93
    # update index in res bus bus
94 1
    net["res_bus"].index = net["bus"].index
95

96

97 1
def _get_bus_results_3ph(net, bus_pq):
98 1
    ac = net["_options"]["ac"]
99

100
    # write sum of p and q values to bus
101 1
    net["res_bus_3ph"]["p_a_mw"] = bus_pq[:, 0]
102 1
    net["res_bus_3ph"]["p_b_mw"] = bus_pq[:, 2]
103 1
    net["res_bus_3ph"]["p_c_mw"] = bus_pq[:, 4]
104 1
    if ac:
105 1
        net["res_bus_3ph"]["q_a_mvar"] = bus_pq[:, 1]
106 1
        net["res_bus_3ph"]["q_b_mvar"] = bus_pq[:, 3]
107 1
        net["res_bus_3ph"]["q_c_mvar"] = bus_pq[:, 5]
108

109
    # Todo: OPF
110

111
    # update index in res bus bus
112
    # net["res_bus"].index = net["bus"].index
113 1
    net["res_bus_3ph"].index = net["bus"].index
114

115 1
def write_voltage_dependend_load_results(net, p, q, b):
116 1
    l = net["load"]
117 1
    _is_elements = net["_is_elements"]
118

119 1
    if len(l) > 0:
120 1
        load_is = _is_elements["load"]
121 1
        scaling = l["scaling"].values
122 1
        bus_lookup = net["_pd2ppc_lookups"]["bus"]
123 1
        lidx = bus_lookup[l["bus"].values]
124

125 1
        voltage_depend_loads = net["_options"]["voltage_depend_loads"]
126

127 1
        cz = l["const_z_percent"].values / 100.
128 1
        ci = l["const_i_percent"].values / 100.
129 1
        cp = 1. - (cz + ci)
130

131
        # constant power
132 1
        pl = l["p_mw"].values * scaling * load_is * cp
133 1
        net["res_load"]["p_mw"] = pl
134 1
        p = np.hstack([p, pl])
135

136 1
        ql = l["q_mvar"].values * scaling * load_is * cp
137 1
        net["res_load"]["q_mvar"] = ql
138 1
        q = np.hstack([q, ql])
139

140 1
        b = np.hstack([b, l["bus"].values])
141

142 1
        if voltage_depend_loads:
143
            # constant impedance and constant current
144 1
            vm_l = net["_ppc"]["bus"][lidx, 7]
145 1
            volt_depend = ci * vm_l + cz * vm_l ** 2
146 1
            pl = l["p_mw"].values * scaling * load_is * volt_depend
147 1
            net["res_load"]["p_mw"] += pl
148 1
            p = np.hstack([p, pl])
149

150 1
            ql = l["q_mvar"].values * scaling * load_is * volt_depend
151 1
            net["res_load"]["q_mvar"] += ql
152 1
            q = np.hstack([q, ql])
153

154 1
            b = np.hstack([b, l["bus"].values])
155 1
        return p, q, b
156

157

158 1
def write_pq_results_to_element(net, ppc, element, suffix=None):
159
    """
160
    get p_mw and q_mvar for a specific pq element ("load", "sgen"...).
161
    This function basically writes values element table to res_element table
162
    :param net: pandapower net
163
    :param element: element name (str)
164
    :return:
165
    """
166
    # info from net
167 1
    _is_elements = net["_is_elements"]
168 1
    ac = net["_options"]["ac"]
169

170
    # info element
171 1
    el_data = net[element]
172 1
    res_ = "res_%s" % element
173 1
    if suffix is not None:
174 1
        res_ += "_%s"%suffix
175 1
    ctrl_ = "%s_controllable" % element
176

177 1
    is_controllable = False
178 1
    if ctrl_ in _is_elements:
179 1
        controlled_elements = net[element][net._is_elements[ctrl_]].index
180 1
        gen_idx = net._pd2ppc_lookups[ctrl_][controlled_elements]
181 1
        gen_sign = 1 if element == "sgen" else -1
182 1
        is_controllable = True
183

184 1
    if element == "motor":
185 1
        p_mw, q_mvar = _get_motor_pq(net)
186 1
        net[res_]["p_mw"].values[:] = p_mw
187 1
        net[res_]["q_mvar"].values[:] = q_mvar
188 1
        return net
189 1
    elif element.startswith("asymmetric"):
190 1
        p_mw, q_mvar = _get_symmetric_pq_of_unsymetric_element(net, element)
191 1
        net[res_]["p_mw"].values[:] = p_mw
192 1
        net[res_]["q_mvar"].values[:] = q_mvar
193 1
        return net
194

195
    # Wards and xwards have different names in their element table, but not in res table. Also no scaling -> Fix...
196 1
    p_mw = "ps_mw" if element in ["ward", "xward"] else "p_mw"
197 1
    q_mvar = "qs_mvar" if element in ["ward", "xward"] else "q_mvar"
198 1
    scaling = el_data["scaling"].values if element not in ["ward", "xward"] else 1.0
199

200 1
    element_in_service = _is_elements[element]
201

202
    # P result in mw to element
203 1
    net[res_]["p_mw"].values[:] = el_data[p_mw].values * scaling * element_in_service
204 1
    if is_controllable:
205 1
        net[res_]["p_mw"].loc[controlled_elements] = ppc["gen"][gen_idx, PG] * gen_sign
206

207 1
    if ac:
208
        # Q result in mvar to element
209 1
        net[res_]["q_mvar"].values[:] = el_data[q_mvar].values * scaling * element_in_service
210 1
        if is_controllable:
211 1
            net[res_]["q_mvar"].loc[controlled_elements] = ppc["gen"][gen_idx, QG] * gen_sign
212 1
    return net
213

214

215 1
def write_pq_results_to_element_3ph(net, element):
216
    """
217
    get p_mw and q_mvar for a specific pq element ("load", "sgen"...).
218
    This function basically writes values element table to res_element table
219

220
    :param net: pandapower net
221
    :param element: element name (str)
222
    :return:
223
    """
224

225
    # info from net
226 1
    _is_elements = net["_is_elements"]
227 1
    ac = net["_options"]["ac"]
228

229
    # info element
230 1
    el_data = net[element]
231 1
    res_ = "res_" + element+"_3ph"
232

233 1
    scaling = el_data["scaling"].values
234

235 1
    element_in_service = _is_elements[element]
236

237 1
    net[res_]["p_a_mw"] = pd.Series((el_data["p_mw"].values/3)\
238
    * scaling * element_in_service) if element in[ "load","sgen"] else\
239
    pd.Series(el_data["p_a_mw"].values * scaling * element_in_service)
240

241 1
    net[res_]["p_b_mw"] = pd.Series((el_data["p_mw"].values/3) \
242
    * scaling * element_in_service)if element in[ "load","sgen"]  else\
243
    pd.Series(el_data["p_b_mw"].values * scaling * element_in_service)
244

245 1
    net[res_]["p_c_mw"] = pd.Series((el_data["p_mw"].values/3) \
246
       * scaling * element_in_service) if element in[ "load","sgen"]  else\
247
       pd.Series(el_data["p_c_mw"].values * scaling * element_in_service)
248 1
    if ac:
249
        # Q result in kvar to element
250 1
        net[res_]["q_a_mvar"] = pd.Series((el_data["q_mvar"].values/3)\
251
    * scaling * element_in_service) if element in[ "load","sgen"]  else\
252
    pd.Series(el_data["q_a_mvar"].values * scaling * element_in_service)
253

254 1
        net[res_]["q_b_mvar"] = pd.Series((el_data["q_mvar"].values/3)\
255
    * scaling * element_in_service) if element in[ "load","sgen"]  else\
256
    pd.Series(el_data["q_b_mvar"].values * scaling * element_in_service)
257

258 1
        net[res_]["q_c_mvar"] = pd.Series((el_data["q_mvar"].values/3)\
259
    * scaling * element_in_service) if element in[ "load","sgen"]  else\
260
    pd.Series(el_data["q_c_mvar"].values * scaling * element_in_service)
261

262
    # update index of result table
263 1
    net[res_].index = net[element].index
264 1
    return net
265

266

267 1
def get_p_q_b(net, element, suffix=None):
268 1
    ac = net["_options"]["ac"]
269 1
    res_ = "res_" + element
270 1
    if suffix != None:
271 1
        res_ += "_%s"%suffix
272

273
    # bus values are needed for stacking
274 1
    b = net[element]["bus"].values
275 1
    p = net[res_]["p_mw"]
276 1
    q = net[res_]["q_mvar"] if ac else np.zeros_like(p)
277 1
    return p, q, b
278

279 1
def get_p_q_b_3ph(net, element):
280 1
    ac = net["_options"]["ac"]
281 1
    res_ = "res_" + element+"_3ph"
282

283
    # bus values are needed for stacking
284 1
    b = net[element]["bus"].values
285 1
    pA = net[res_]["p_a_mw"]
286 1
    pB = net[res_]["p_b_mw"]
287 1
    pC = net[res_]["p_c_mw"]
288 1
    qA = net[res_]["q_a_mvar"] if ac else np.zeros_like(pA)
289 1
    qB = net[res_]["q_b_mvar"] if ac else np.zeros_like(pB)
290 1
    qC = net[res_]["q_c_mvar"] if ac else np.zeros_like(pC)
291 1
    return pA, qA, pB, qB, pC, qC, b
292

293

294 1
def _get_p_q_results(net, ppc, bus_lookup_aranged):
295 1
    bus_pq = np.zeros(shape=(len(net["bus"].index), 2), dtype=np.float)
296 1
    b, p, q = np.array([]), np.array([]), np.array([])
297

298 1
    ac = net["_options"]["ac"]
299 1
    elements = ["load", "motor", "sgen", "storage", "ward", "xward",
300
                "asymmetric_load", "asymmetric_sgen"]
301

302 1
    if net["_options"]["voltage_depend_loads"] and ac:
303
        # voltage dependend loads need special treatment here
304

305 1
        p, q, b = write_voltage_dependend_load_results(net, p, q, b)
306 1
        elements.remove("load")
307

308 1
    for element in elements:
309 1
        if len(net[element]):
310 1
            write_pq_results_to_element(net, ppc, element)
311 1
            p_el, q_el, bus_el = get_p_q_b(net, element)
312 1
            if element.endswith("sgen"):
313 1
                p = np.hstack([p, -p_el])
314 1
                q = np.hstack([q, -q_el])
315
            else:
316 1
                p = np.hstack([p, p_el])
317 1
                q = np.hstack([q, q_el])
318 1
            b = np.hstack([b, bus_el])
319

320 1
    if not ac:
321 1
        q = np.zeros(len(p))
322

323
    # sum pq results from every element to be written to net['bus'] later on
324 1
    b_pp, vp, vq = _sum_by_group(b.astype(int), p, q)
325 1
    b_ppc = bus_lookup_aranged[b_pp]
326 1
    bus_pq[b_ppc, 0] = vp
327 1
    bus_pq[b_ppc, 1] = vq
328 1
    return bus_pq
329

330 1
def _get_p_q_results_3ph(net, bus_lookup_aranged):
331
    # results to be filled (bus, p in kw, q in kvar)
332 1
    bus_pq = np.zeros(shape=(len(net["bus"].index), 6), dtype=np.float)
333 1
    b, pA, pB, pC, qA, qB, qC = np.array([]), np.array([]), np.array([]), np.array([]), \
334
                                np.array([]), np.array([]), np.array([])
335

336 1
    ac = net["_options"]["ac"]
337
    # Todo: Voltage dependent loads
338 1
    elements = ["storage", "sgen", "load"]
339 1
    elements_3ph = ["asymmetric_load", "asymmetric_sgen"]
340 1
    for element in elements:
341 1
        sign = -1 if element in ['sgen','asymmetric_sgen'] else 1
342 1
        if len(net[element]):
343 1
            write_pq_results_to_element(net, net._ppc1, element, suffix="3ph")
344 1
            p_el, q_el, bus_el = get_p_q_b(net, element, suffix="3ph")
345 1
            pA = np.hstack([pA, sign * p_el/3])
346 1
            pB = np.hstack([pB, sign * p_el/3])
347 1
            pC = np.hstack([pC, sign * p_el/3])
348 1
            qA = np.hstack([qA, sign * q_el/3 if ac else np.zeros(len(p_el/3))])
349 1
            qB = np.hstack([qB, sign * q_el/3 if ac else np.zeros(len(p_el/3))])
350 1
            qC = np.hstack([qC, sign * q_el/3 if ac else np.zeros(len(p_el/3))])
351 1
            b = np.hstack([b, bus_el])
352 1
    for element in elements_3ph:
353 1
        sign = -1 if element in ['sgen','asymmetric_sgen'] else 1
354 1
        if len(net[element]):
355 1
            write_pq_results_to_element_3ph(net, element)
356 1
            p_el_A, q_el_A, p_el_B, q_el_B, p_el_C, q_el_C, bus_el = get_p_q_b_3ph(net, element)
357 1
            pA = np.hstack([pA, sign * p_el_A])
358 1
            pB = np.hstack([pB, sign * p_el_B])
359 1
            pC = np.hstack([pC, sign * p_el_C])
360 1
            qA = np.hstack([qA, sign * q_el_A if ac else np.zeros(len(p_el_A))])
361 1
            qB = np.hstack([qB, sign * q_el_B if ac else np.zeros(len(p_el_B))])
362 1
            qC = np.hstack([qC, sign * q_el_C if ac else np.zeros(len(p_el_C))])
363 1
            b = np.hstack([b, bus_el])
364

365
    # sum pq results from every element to be written to net['bus'] later on
366 1
    b_pp, vp_A, vq_A, vp_B, vq_B, vp_C, vq_C = _sum_by_group_nvals(b.astype(int), pA, qA, pB, qB, pC, qC)
367 1
    b_ppc = bus_lookup_aranged[b_pp]
368 1
    bus_pq[b_ppc, 0] = vp_A
369 1
    bus_pq[b_ppc, 1] = vq_A
370 1
    bus_pq[b_ppc, 2] = vp_B
371 1
    bus_pq[b_ppc, 3] = vq_B
372 1
    bus_pq[b_ppc, 4] = vp_C
373 1
    bus_pq[b_ppc, 5] = vq_C
374 1
    return bus_pq
375

376 1
def _get_shunt_results(net, ppc, bus_lookup_aranged, bus_pq):
377 1
    ac = net["_options"]["ac"]
378

379 1
    b, p, q = np.array([]), np.array([]), np.array([])
380 1
    _is_elements = net["_is_elements"]
381 1
    bus_lookup = net["_pd2ppc_lookups"]["bus"]
382

383 1
    s = net["shunt"]
384 1
    if len(s) > 0:
385 1
        sidx = bus_lookup[s["bus"].values]
386 1
        shunt_is = _is_elements["shunt"]
387 1
        u_shunt = ppc["bus"][sidx, VM]
388 1
        step = s["step"]
389 1
        v_ratio = (ppc["bus"][sidx, BASE_KV] / net["shunt"]["vn_kv"].values) ** 2
390 1
        u_shunt = np.nan_to_num(u_shunt)
391 1
        p_shunt = u_shunt ** 2 * net["shunt"]["p_mw"].values * shunt_is * v_ratio * step
392 1
        net["res_shunt"]["p_mw"].values[:] = p_shunt
393 1
        p = np.hstack([p, p_shunt])
394 1
        if ac:
395 1
            net["res_shunt"]["vm_pu"].values[:] = u_shunt
396 1
            q_shunt = u_shunt ** 2 * net["shunt"]["q_mvar"].values * shunt_is * v_ratio * step
397 1
            net["res_shunt"]["q_mvar"].values[:] = q_shunt
398 1
            q = np.hstack([q, q_shunt])
399 1
        b = np.hstack([b, s["bus"].values])
400

401 1
    w = net["ward"]
402 1
    if len(w) > 0:
403 1
        widx = bus_lookup[w["bus"].values]
404 1
        ward_is = _is_elements["ward"]
405 1
        u_ward = ppc["bus"][widx, VM]
406 1
        u_ward = np.nan_to_num(u_ward)
407 1
        p_ward = u_ward ** 2 * net["ward"]["pz_mw"].values * ward_is
408 1
        net["res_ward"]["p_mw"].values[:] = net["res_ward"]["p_mw"].values + p_ward
409 1
        p = np.hstack([p, p_ward])
410 1
        if ac:
411 1
            net["res_ward"]["vm_pu"].values[:] = u_ward
412 1
            q_ward = u_ward ** 2 * net["ward"]["qz_mvar"].values * ward_is
413 1
            net["res_ward"]["q_mvar"].values[:] = net["res_ward"]["q_mvar"].values + q_ward
414 1
            q = np.hstack([q, q_ward])
415 1
        b = np.hstack([b, w["bus"].values])
416

417 1
    xw = net["xward"]
418 1
    if len(xw) > 0:
419 1
        widx = bus_lookup[xw["bus"].values]
420 1
        xward_is = _is_elements["xward"]
421 1
        u_xward = ppc["bus"][widx, VM]
422 1
        u_xward = np.nan_to_num(u_xward)
423 1
        p_xward = u_xward ** 2 * net["xward"]["pz_mw"].values * xward_is
424 1
        net["res_xward"]["p_mw"].values[:] = net["res_xward"]["p_mw"].values + p_xward
425 1
        p = np.hstack([p, p_xward])
426 1
        if ac:
427 1
            net["res_xward"]["vm_pu"].values[:] = u_xward
428 1
            q_xward = u_xward ** 2 * net["xward"]["qz_mvar"].values * xward_is
429 1
            net["res_xward"]["q_mvar"].values[:] = net["res_xward"]["q_mvar"].values + q_xward
430 1
            q = np.hstack([q, q_xward])
431 1
        b = np.hstack([b, xw["bus"].values])
432

433 1
    if not ac:
434 1
        q = np.zeros(len(p))
435 1
    b_pp, vp, vq = _sum_by_group(b.astype(int), p, q)
436 1
    b_ppc = bus_lookup_aranged[b_pp]
437

438 1
    bus_pq[b_ppc, 0] += vp
439 1
    if ac:
440 1
        bus_pq[b_ppc, 1] += vq

Read our documentation on viewing source code .

Loading