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

3
# Copyright 1996-2015 PSERC. All rights reserved.
4
# Use of this source code is governed by a BSD-style
5
# license that can be found in the LICENSE file.
6

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

10

11

12 1
"""Runs a power flow.
13
"""
14

15 1
from time import time
16 1
from packaging import version
17 1
from numpy import flatnonzero as find, r_, zeros, argmax, real, setdiff1d
18

19 1
from pandapower.pypower.idx_bus import PD, QD, BUS_TYPE, PQ, REF
20 1
from pandapower.pypower.idx_gen import PG, QG, QMAX, QMIN, GEN_BUS, GEN_STATUS
21 1
from pandapower.pypower.bustypes import bustypes
22 1
from pandapower.pypower.makeSbus import makeSbus
23 1
from pandapower.pypower.pfsoln import pfsoln
24 1
from pandapower.pf.run_newton_raphson_pf import _run_dc_pf
25 1
from pandapower.pf.ppci_variables import _get_pf_variables_from_ppci, _store_results_from_pf_in_ppci
26

27 1
from pandapower.pypower.makeB import makeB
28 1
from pandapower.pypower.ppoption import ppoption
29 1
from pandapower.pypower.fdpf import fdpf
30 1
from pandapower.pypower.gausspf import gausspf
31

32 1
try:
33 1
    import pplog as logging
34 1
except ImportError:
35 1
    import logging
36

37 1
logger = logging.getLogger(__name__)
38

39

40 1
def _runpf_pypower(ppci, options, **kwargs):
41
    """
42
    This is a modified version* of runpf() to run the algorithms gausspf and fdpf from PYPOWER.
43
    See PYPOWER documentation for more information.
44

45
    * mainly the verbose functions and ext2int() int2ext() were deleted
46
    """
47

48
    ##-----  run the power flow  -----
49 1
    t0 = time()
50
    # ToDo: Options should be extracted in every subfunction not here...
51 1
    init_va_degree, ac, numba, recycle, ppopt = _get_options(options, **kwargs)
52

53 1
    if ac:  # AC formulation
54 1
        if init_va_degree == "dc":
55 0
            ppci = _run_dc_pf(ppci)
56 0
            success = True
57

58 1
        ppci, success, bus, gen, branch, it = _ac_runpf(ppci, ppopt, numba, recycle)
59
    else:  ## DC formulation
60 0
        ppci = _run_dc_pf(ppci)
61 0
        success = True
62

63 1
    et = time() - t0
64 1
    ppci = _store_results_from_pf_in_ppci(ppci, bus, gen, branch, success, it, et)
65 1
    return ppci, success
66

67

68 1
def _get_options(options, **kwargs):
69 1
    init_va_degree = options["init_va_degree"]
70 1
    ac = options["ac"]
71 1
    recycle = options["recycle"]
72 1
    numba = options["numba"]
73 1
    enforce_q_lims = options["enforce_q_lims"]
74 1
    tolerance_mva = options["tolerance_mva"]
75 1
    algorithm = options["algorithm"]
76 1
    max_iteration = options["max_iteration"]
77

78
    # algorithms implemented within pypower
79 1
    algorithm_pypower_dict = {'nr': 1, 'fdbx': 2, 'fdxb': 3, 'gs': 4}
80

81 1
    ppopt = ppoption(ENFORCE_Q_LIMS=enforce_q_lims, PF_TOL=tolerance_mva,
82
                     PF_ALG=algorithm_pypower_dict[algorithm], **kwargs)
83 1
    ppopt['PF_MAX_IT'] = max_iteration
84 1
    ppopt['PF_MAX_IT_GS'] = max_iteration
85 1
    ppopt['PF_MAX_IT_FD'] = max_iteration
86 1
    ppopt['VERBOSE'] = 0
87 1
    return init_va_degree, ac, numba, recycle, ppopt
88

89

90 1
def _ac_runpf(ppci, ppopt, numba, recycle):
91 1
    numba, makeYbus = _import_numba_extensions_if_flag_is_true(numba)
92 1
    if ppopt["ENFORCE_Q_LIMS"]:
93 1
        return _run_ac_pf_with_qlims_enforced(ppci, recycle, makeYbus, ppopt)
94
    else:
95 1
        return _run_ac_pf_without_qlims_enforced(ppci, recycle, makeYbus, ppopt)
96

97

98 1
def _import_numba_extensions_if_flag_is_true(numba):
99
    ## check if numba is available and the corresponding flag
100 1
    if numba:
101 1
        try:
102 1
            from numba._version import version_version as nb_version
103
            # get numba Version (in order to use it it must be > 0.25)
104 1
            if version.parse(nb_version) < version.parse("0.25"):
105 0
                logger.warning('Warning: Numba version too old -> Upgrade to a version > 0.25. Numba is disabled\n')
106 0
                numba = False
107

108 0
        except ImportError:
109
            # raise UserWarning('numba cannot be imported. Call runpp() with numba=False!')
110 0
            logger.warning('Warning: Numba cannot be imported. Numba is disabled. Call runpp() with Numba=False!\n')
111 0
            numba = False
112

113 1
    if numba:
114 1
        from pandapower.pf.makeYbus_numba import makeYbus
115
    else:
116 0
        from pandapower.pypower.makeYbus import makeYbus
117

118 1
    return numba, makeYbus
119

120

121 1
def _get_Y_bus(ppci, recycle, makeYbus, baseMVA, bus, branch):
122 1
    if recycle and not recycle["trafo"] and ppci["internal"]["Ybus"].size:
123 0
        Ybus, Yf, Yt = ppci["internal"]['Ybus'], ppci["internal"]['Yf'], ppci["internal"]['Yt']
124
    else:
125
        ## build admittance matrices
126 1
        Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch)
127 1
        ppci["internal"]['Ybus'], ppci["internal"]['Yf'], ppci["internal"]['Yt'] = Ybus, Yf, Yt
128 1
    return ppci, Ybus, Yf, Yt
129

130

131 1
def _run_ac_pf_without_qlims_enforced(ppci, recycle, makeYbus, ppopt):
132 1
    baseMVA, bus, gen, branch, ref, pv, pq, _, gbus, V0, ref_gens = _get_pf_variables_from_ppci(ppci)
133

134 1
    ppci, Ybus, Yf, Yt = _get_Y_bus(ppci, recycle, makeYbus, baseMVA, bus, branch)
135

136
    ## compute complex bus power injections [generation - load]
137 1
    Sbus = makeSbus(baseMVA, bus, gen)
138

139
    ## run the power flow
140 1
    V, success, it = _call_power_flow_function(baseMVA, bus, branch, Ybus, Sbus, V0, ref, pv, pq, ppopt)
141

142
    ## update data matrices with solution
143 1
    bus, gen, branch = pfsoln(baseMVA, bus, gen, branch, Ybus, Yf, Yt, V, ref, ref_gens)
144

145 1
    return ppci, success, bus, gen, branch, it
146

147

148 1
def _run_ac_pf_with_qlims_enforced(ppci, recycle, makeYbus, ppopt):
149 1
    baseMVA, bus, gen, branch, ref, pv, pq, on, gbus, V0, _ = _get_pf_variables_from_ppci(ppci)
150

151 1
    qlim = ppopt["ENFORCE_Q_LIMS"]
152 1
    limited = []  ## list of indices of gens @ Q lims
153 1
    fixedQg = zeros(gen.shape[0])  ## Qg of gens at Q limits
154 1
    it = 0
155
    while True:
156 1
        ppci, success, bus, gen, branch, it_inner = _run_ac_pf_without_qlims_enforced(ppci, recycle,
157
                                                                                      makeYbus, ppopt)
158 1
        it += it_inner
159

160
        ## find gens with violated Q constraints
161 1
        gen_status = gen[:, GEN_STATUS] > 0
162 1
        qg_max_lim = gen[:, QG] > gen[:, QMAX]
163 1
        qg_min_lim = gen[:, QG] < gen[:, QMIN]
164

165 1
        non_refs = (gen[:, QMAX] != 0.) & (gen[:, QMIN] != 0.)
166 1
        mx = find(gen_status & qg_max_lim & non_refs)
167 1
        mn = find(gen_status & qg_min_lim & non_refs)
168

169

170 1
        if len(mx) > 0 or len(mn) > 0:  ## we have some Q limit violations
171
            ## one at a time?
172 1
            if qlim == 2:  ## fix largest violation, ignore the rest
173 0
                k = argmax(r_[gen[mx, QG] - gen[mx, QMAX],
174
                              gen[mn, QMIN] - gen[mn, QG]])
175 0
                if k > len(mx):
176 0
                    mn = mn[k - len(mx)]
177 0
                    mx = []
178
                else:
179 0
                    mx = mx[k]
180 0
                    mn = []
181

182
                    ## save corresponding limit values
183 1
            fixedQg[mx] = gen[mx, QMAX]
184 1
            fixedQg[mn] = gen[mn, QMIN]
185 1
            mx = r_[mx, mn].astype(int)
186

187
            ## convert to PQ bus
188 1
            gen[mx, QG] = fixedQg[mx]  ## set Qg to binding
189 1
            for i in mx:  ## [one at a time, since they may be at same bus]
190 1
                gen[i, GEN_STATUS] = 0  ## temporarily turn off gen,
191 1
                bi = gen[i, GEN_BUS].astype(int)  ## adjust load accordingly,
192 1
                bus[bi, [PD, QD]] = (bus[bi, [PD, QD]] - gen[i, [PG, QG]])
193

194 1
            if len(ref) > 1 and any(bus[gen[mx, GEN_BUS].astype(int), BUS_TYPE] == REF):
195 0
                raise ValueError('Sorry, pandapower cannot enforce Q '
196
                                 'limits for slack buses in systems '
197
                                 'with multiple slacks.')
198

199 1
            changed_gens = gen[mx, GEN_BUS].astype(int)
200 1
            bus[setdiff1d(changed_gens, ref), BUS_TYPE] = PQ  ## & set bus type to PQ
201

202
            ## update bus index lists of each type of bus
203 1
            ref, pv, pq = bustypes(bus, gen)
204

205 1
            limited = r_[limited, mx].astype(int)
206
        else:
207 0
            break  ## no more generator Q limits violated
208

209 1
    if len(limited) > 0:
210
        ## restore injections from limited gens [those at Q limits]
211 1
        gen[limited, QG] = fixedQg[limited]  ## restore Qg value,
212 1
        for i in limited:  ## [one at a time, since they may be at same bus]
213 1
            bi = gen[i, GEN_BUS].astype(int)  ## re-adjust load,
214 1
            bus[bi, [PD, QD]] = bus[bi, [PD, QD]] + gen[i, [PG, QG]]
215 1
            gen[i, GEN_STATUS] = 1  ## and turn gen back on
216

217 1
    return ppci, success, bus, gen, branch, it
218

219

220 1
def _call_power_flow_function(baseMVA, bus, branch, Ybus, Sbus, V0, ref, pv, pq, ppopt):
221 1
    alg = ppopt["PF_ALG"]
222
    # alg == 1 was deleted = nr -> moved as own pandapower solver
223 1
    if alg == 2 or alg == 3:
224 1
        Bp, Bpp = makeB(baseMVA, bus, real(branch), alg)
225 1
        V, success, it = fdpf(Ybus, Sbus, V0, Bp, Bpp, ref, pv, pq, ppopt)
226 1
    elif alg == 4:
227 1
        V, success, it = gausspf(Ybus, Sbus, V0, ref, pv, pq, ppopt)
228
    else:
229 0
        raise ValueError('Only PYPOWERS fast-decoupled, and '
230
                         'Gauss-Seidel power flow algorithms currently '
231
                         'implemented.\n')
232

233 1
    return V, success, it

Read our documentation on viewing source code .

Loading