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 1
import numpy as np
7 1
import pandapower.auxiliary as aux
8 1
from pandapower.build_branch import _switch_branches, _branches_with_oos_buses, _build_branch_ppc
9 1
from pandapower.build_bus import _build_bus_ppc, _calc_pq_elements_and_add_on_ppc, \
10
_calc_shunts_and_add_on_ppc, _add_gen_impedances_ppc, _add_motor_impedances_ppc
11 1
from pandapower.build_gen import _build_gen_ppc, _check_voltage_setpoints_at_same_bus, \
12
    _check_voltage_angles_at_same_bus, _check_for_reference_bus
13 1
from pandapower.opf.make_objective import _make_objective
14 1
from pandapower.pypower.idx_area import PRICE_REF_BUS
15 1
from pandapower.pypower.idx_brch import F_BUS, T_BUS, BR_STATUS
16 1
from pandapower.pypower.idx_bus import NONE, BUS_I, BUS_TYPE
17 1
from pandapower.pypower.idx_gen import GEN_BUS, GEN_STATUS
18 1
from pandapower.pypower.run_userfcn import run_userfcn
19

20

21 1
def _pd2ppc(net, sequence=None):
22
    """
23
    Converter Flow:
24
        1. Create an empty pypower datatructure
25
        2. Calculate loads and write the bus matrix
26
        3. Build the gen (Infeeder)- Matrix
27
        4. Calculate the line parameter and the transformer parameter,
28
           and fill it in the branch matrix.
29
           Order: 1st: Line values, 2nd: Trafo values
30
        5. if opf: make opf objective (gencost)
31
        6. convert internal ppci format for pypower powerflow / 
32
        opf without out of service elements and rearanged buses
33

34
    INPUT:
35
        **net** - The pandapower format network
36
        **sequence** - Used for three phase analysis
37
        ( 0 - Zero Sequence
38
          1 - Positive Sequence
39
          2 - Negative Sequence
40
        ) 
41

42
    OUTPUT:
43
        **ppc** - The simple matpower format network. Which consists of:
44
                  ppc = {
45
                        "baseMVA": 1., *float*
46
                        "version": 2,  *int*
47
                        "bus": np.array([], dtype=float),
48
                        "branch": np.array([], dtype=np.complex128),
49
                        "gen": np.array([], dtype=float),
50
                        "gencost" =  np.array([], dtype=float), only for OPF
51
                        "internal": {
52
                              "Ybus": np.array([], dtype=np.complex128)
53
                              , "Yf": np.array([], dtype=np.complex128)
54
                              , "Yt": np.array([], dtype=np.complex128)
55
                              , "branch_is": np.array([], dtype=bool)
56
                              , "gen_is": np.array([], dtype=bool)
57
                              }
58
        **ppci** - The "internal" pypower format network for PF calculations
59
        
60
    """
61
    # select elements in service (time consuming, so we do it once)
62 1
    net["_is_elements"] = aux._select_is_elements_numba(net, sequence=sequence)
63

64
    # Gets network configurations
65 1
    mode = net["_options"]["mode"]
66 1
    check_connectivity = net["_options"]["check_connectivity"]
67 1
    calculate_voltage_angles = net["_options"]["calculate_voltage_angles"]
68

69 1
    ppc = _init_ppc(net, mode=mode, sequence=sequence)
70

71
    # generate ppc['bus'] and the bus lookup
72 1
    _build_bus_ppc(net, ppc)
73 1
    if sequence == 0:
74 1
        from pandapower.pd2ppc_zero import _add_ext_grid_sc_impedance_zero, _build_branch_ppc_zero
75
        # Adds external grid impedance for 3ph and sc calculations in ppc0
76 1
        _add_ext_grid_sc_impedance_zero(net, ppc)
77
        # Calculates ppc0 branch impedances from branch elements
78 1
        _build_branch_ppc_zero(net, ppc)
79
    else:
80
        # Calculates ppc1/ppc2 branch impedances from branch elements  
81 1
        _build_branch_ppc(net, ppc)
82

83
    # Adds P and Q for loads / sgens in ppc['bus'] (PQ nodes)
84 1
    if mode == "sc":
85 1
        _add_gen_impedances_ppc(net, ppc)
86 1
        _add_motor_impedances_ppc(net, ppc)
87
    else:
88 1
        _calc_pq_elements_and_add_on_ppc(net, ppc, sequence=sequence)
89
        # adds P and Q for shunts, wards and xwards (to PQ nodes)
90 1
        _calc_shunts_and_add_on_ppc(net, ppc)
91

92
    # adds auxilary buses for open switches at branches
93 1
    _switch_branches(net, ppc)
94

95
    # Adds auxilary buses for in service lines with out of service buses.
96
    # Also deactivates lines if they are connected to two out of service buses
97 1
    _branches_with_oos_buses(net, ppc)
98

99 1
    if check_connectivity:
100 1
        if sequence in [None, 1, 2]:
101
            # sets islands (multiple isolated nodes) out of service
102 1
            if "opf" in mode:
103 1
                net["_isolated_buses"], _, _ = aux._check_connectivity_opf(ppc)
104
            else:
105 1
                net["_isolated_buses"], _, _ = aux._check_connectivity(ppc)
106 1
            net["_is_elements_final"] = aux._select_is_elements_numba(net,
107
                                                                      net._isolated_buses, sequence)
108
        else:
109 1
            ppc["bus"][net._isolated_buses, BUS_TYPE] = NONE
110 1
        net["_is_elements"] = net["_is_elements_final"]
111
    else:
112
        # sets buses out of service, which aren't connected to branches / REF buses
113 1
        aux._set_isolated_buses_out_of_service(net, ppc)
114

115 1
    _build_gen_ppc(net, ppc)
116

117 1
    if "pf" in mode:
118 1
        _check_for_reference_bus(ppc)
119

120 1
    aux._replace_nans_with_default_limits(net, ppc)
121

122
    # generates "internal" ppci format (for powerflow calc) 
123
    # from "external" ppc format and updates the bus lookup
124
    # Note: Also reorders buses and gens in ppc
125 1
    ppci = _ppc2ppci(ppc, net)
126

127 1
    if mode == "pf":
128
        # check if any generators connected to the same bus have different voltage setpoints
129 1
        _check_voltage_setpoints_at_same_bus(ppc)
130 1
        if calculate_voltage_angles:
131 1
            _check_voltage_angles_at_same_bus(net, ppci)
132

133 1
    if mode == "opf":
134
        # make opf objective
135 1
        ppci = _make_objective(ppci, net)
136

137 1
    return ppc, ppci
138

139

140 1
def _init_ppc(net, mode="pf", sequence=None):
141
    # init empty ppc
142 1
    ppc = {"baseMVA": net.sn_mva
143
        , "version": 2
144
        , "bus": np.array([], dtype=float)
145
        , "branch": np.array([], dtype=np.complex128)
146
        , "gen": np.array([], dtype=float)
147
        , "internal": {
148
            "Ybus": np.array([], dtype=np.complex128)
149
            , "Yf": np.array([], dtype=np.complex128)
150
            , "Yt": np.array([], dtype=np.complex128)
151
            , "branch_is": np.array([], dtype=bool)
152
            , "gen_is": np.array([], dtype=bool)
153
            , "DLF": np.array([], dtype=np.complex128)
154
            , "buses_ord_bfs_nets": np.array([], dtype=float)
155
        }
156
           }
157 1
    if mode == "opf":
158
        # additional fields in ppc
159 1
        ppc["gencost"] = np.array([], dtype=float)
160 1
    net["_ppc"] = ppc
161

162 1
    if sequence is None:
163 1
        net["_ppc"] = ppc
164
    else:
165 1
        ppc["sequence"] = int(sequence)
166 1
        net["_ppc%s" % sequence] = ppc
167 1
    return ppc
168

169

170 1
def _ppc2ppci(ppc, net, ppci=None):
171
    """
172
    Creates the ppci which is used to run the power flow / OPF...
173
    The ppci is similar to the ppc except that:
174
    1. it contains no out of service elements
175
    2. buses are sorted
176

177
    Parameters
178
    ----------
179
    ppc - the ppc
180
    net - the pandapower net
181

182
    Returns
183
    -------
184
    ppci - the "internal" ppc
185

186
    """
187
    # get empty ppci
188 1
    if ppci is None:
189 1
        ppci = _init_ppc(net, mode=net["_options"]["mode"])
190
    # BUS Sorting and lookups
191
    # get bus_lookup
192 1
    bus_lookup = net["_pd2ppc_lookups"]["bus"]
193
    # get OOS busses and place them at the end of the bus array
194
    # (there are no OOS busses in the ppci)
195 1
    oos_busses = ppc['bus'][:, BUS_TYPE] == NONE
196 1
    ppci['bus'] = ppc['bus'][~oos_busses]
197
    # in ppc the OOS busses are included and at the end of the array
198 1
    ppc['bus'] = np.vstack([ppc['bus'][~oos_busses], ppc['bus'][oos_busses]])
199

200
    # generate bus_lookup_ppc_ppci (ppc -> ppci lookup)
201 1
    ppc_former_order = (ppc['bus'][:, BUS_I]).astype(int)
202 1
    aranged_buses = np.arange(len(ppc["bus"]))
203

204
    # lookup ppc former order -> consecutive order
205 1
    e2i = np.zeros(len(ppc["bus"]), dtype=int)
206 1
    e2i[ppc_former_order] = aranged_buses
207

208
    # save consecutive indices in ppc and ppci
209 1
    ppc['bus'][:, BUS_I] = aranged_buses
210 1
    ppci['bus'][:, BUS_I] = ppc['bus'][:len(ppci['bus']), BUS_I]
211

212
    # update lookups (pandapower -> ppci internal)
213 1
    _update_lookup_entries(net, bus_lookup, e2i, "bus")
214

215 1
    if 'areas' in ppc:
216 0
        if len(ppc["areas"]) == 0:  # if areas field is empty
217 0
            del ppc['areas']  # delete it (so it's ignored)
218

219
    # bus types
220 1
    bt = ppc["bus"][:, BUS_TYPE]
221

222
    # update branch, gen and areas bus numbering
223 1
    ppc['gen'][:, GEN_BUS] = e2i[np.real(ppc["gen"][:, GEN_BUS]).astype(int)].copy()
224 1
    ppc["branch"][:, F_BUS] = e2i[np.real(ppc["branch"][:, F_BUS]).astype(int)].copy()
225 1
    ppc["branch"][:, T_BUS] = e2i[np.real(ppc["branch"][:, T_BUS]).astype(int)].copy()
226

227
    # Note: The "update branch, gen and areas bus numbering" does the same as:
228
    # ppc['gen'][:, GEN_BUS] = get_indices(ppc['gen'][:, GEN_BUS], bus_lookup_ppc_ppci)
229
    # ppc["branch"][:, F_BUS] = get_indices(ppc["branch"][:, F_BUS], bus_lookup_ppc_ppci)
230
    # ppc["branch"][:, T_BUS] = get_indices( ppc["branch"][:, T_BUS], bus_lookup_ppc_ppci)
231
    # but faster...
232

233 1
    if 'areas' in ppc:
234 0
        ppc["areas"][:, PRICE_REF_BUS] = \
235
            e2i[np.real(ppc["areas"][:, PRICE_REF_BUS]).astype(int)].copy()
236

237
    # initialize gen lookups
238 1
    for element, (f, t) in net._gen_order.items():
239 1
        _build_gen_lookups(net, element, f, t)
240

241
    # determine which buses, branches, gens are connected and
242
    # in-service
243 1
    n2i = ppc["bus"][:, BUS_I].astype(int)
244 1
    bs = (bt != NONE)  # bus status
245

246 1
    gs = ((ppc["gen"][:, GEN_STATUS] > 0) &  # gen status
247
          bs[n2i[np.real(ppc["gen"][:, GEN_BUS]).astype(int)]])
248 1
    ppci["internal"]["gen_is"] = gs
249

250 1
    brs = (np.real(ppc["branch"][:, BR_STATUS]).astype(int) &  # branch status
251
           bs[n2i[np.real(ppc["branch"][:, F_BUS]).astype(int)]] &
252
           bs[n2i[np.real(ppc["branch"][:, T_BUS]).astype(int)]]).astype(bool)
253 1
    ppci["internal"]["branch_is"] = brs
254

255 1
    if 'areas' in ppc:
256 0
        ar = bs[n2i[ppc["areas"][:, PRICE_REF_BUS].astype(int)]]
257
        # delete out of service areas
258 0
        ppci["areas"] = ppc["areas"][ar]
259

260
    # select in service elements from ppc and put them in ppci
261 1
    ppci["branch"] = ppc["branch"][brs]
262

263 1
    ppci["gen"] = ppc["gen"][gs]
264

265 1
    if 'dcline' in ppc:
266 1
        ppci['dcline'] = ppc['dcline']
267
    # execute userfcn callbacks for 'ext2int' stage
268 1
    if 'userfcn' in ppci:
269 0
        ppci = run_userfcn(ppci['userfcn'], 'ext2int', ppci)
270

271 1
    if net._pd2ppc_lookups["ext_grid"] is not None:
272 1
        ref_gens = np.setdiff1d(net._pd2ppc_lookups["ext_grid"], np.array([-1]))
273
    else:
274 1
        ref_gens = np.array([])
275 1
    if np.any(net.gen.slack.values[net._is_elements["gen"]]):
276 1
        slack_gens = np.array(net.gen.index)[net._is_elements["gen"] \
277
                                             & net.gen["slack"].values]
278 1
        ref_gens = np.append(ref_gens, net._pd2ppc_lookups["gen"][slack_gens])
279 1
    ppci["internal"]["ref_gens"] = ref_gens.astype(int)
280 1
    return ppci
281

282

283 1
def _update_lookup_entries(net, lookup, e2i, element):
284 1
    valid_bus_lookup_entries = lookup >= 0
285
    # update entries
286 1
    lookup[valid_bus_lookup_entries] = e2i[lookup[valid_bus_lookup_entries]]
287 1
    aux._write_lookup_to_net(net, element, lookup)
288

289

290 1
def _build_gen_lookups(net, element, f, t):
291 1
    in_service = net._is_elements[element]
292 1
    if "controllable" in element:
293 1
        pandapower_index = net[element.split("_")[0]].index.values[in_service]
294
    else:
295 1
        pandapower_index = net[element].index.values[in_service]
296 1
    ppc_index = np.arange(f, t)
297 1
    if len(pandapower_index) > 0:
298 1
        _init_lookup(net, element, pandapower_index, ppc_index)
299

300

301 1
def _init_lookup(net, lookup_name, pandapower_index, ppc_index):
302
    # init lookup
303 1
    lookup = -np.ones(max(pandapower_index) + 1, dtype=int)
304

305
    # update lookup
306 1
    lookup[pandapower_index] = ppc_index
307

308 1
    aux._write_lookup_to_net(net, lookup_name, lookup)

Read our documentation on viewing source code .

Loading