e2nIEE / pandapower
1
# Copyright (c) 1996-2015 PSERC. All rights reserved.
2
# Use of this source code is governed by a BSD-style
3
# license that can be found in the LICENSE file.
4

5 1
"""Computes partial derivatives of power flows w.r.t. voltage.
6
"""
7

8 1
from numpy import conj, arange, diag, zeros, asmatrix, asarray
9 1
from scipy.sparse import issparse, csr_matrix as sparse
10

11 1
from pandapower.pypower.idx_brch import F_BUS, T_BUS
12

13 1
def dSbr_dV(branch, Yf, Yt, V):
14
    """Computes partial derivatives of power flows w.r.t. voltage.
15

16
    returns four matrices containing partial derivatives of the complex
17
    branch power flows at "from" and "to" ends of each branch w.r.t voltage
18
    magnitude and voltage angle respectively (for all buses). If C{Yf} is a
19
    sparse matrix, the partial derivative matrices will be as well. Optionally
20
    returns vectors containing the power flows themselves. The following
21
    explains the expressions used to form the matrices::
22

23
        If = Yf * V;
24
        Sf = diag(Vf) * conj(If) = diag(conj(If)) * Vf
25

26
    Partials of V, Vf & If w.r.t. voltage angles::
27
        dV/dVa  = j * diag(V)
28
        dVf/dVa = sparse(range(nl), f, j*V(f)) = j * sparse(range(nl), f, V(f))
29
        dIf/dVa = Yf * dV/dVa = Yf * j * diag(V)
30

31
    Partials of V, Vf & If w.r.t. voltage magnitudes::
32
        dV/dVm  = diag(V / abs(V))
33
        dVf/dVm = sparse(range(nl), f, V(f) / abs(V(f))
34
        dIf/dVm = Yf * dV/dVm = Yf * diag(V / abs(V))
35

36
    Partials of Sf w.r.t. voltage angles::
37
        dSf/dVa = diag(Vf) * conj(dIf/dVa)
38
                        + diag(conj(If)) * dVf/dVa
39
                = diag(Vf) * conj(Yf * j * diag(V))
40
                        + conj(diag(If)) * j * sparse(range(nl), f, V(f))
41
                = -j * diag(Vf) * conj(Yf * diag(V))
42
                        + j * conj(diag(If)) * sparse(range(nl), f, V(f))
43
                = j * (conj(diag(If)) * sparse(range(nl), f, V(f))
44
                        - diag(Vf) * conj(Yf * diag(V)))
45

46
    Partials of Sf w.r.t. voltage magnitudes::
47
        dSf/dVm = diag(Vf) * conj(dIf/dVm)
48
                        + diag(conj(If)) * dVf/dVm
49
                = diag(Vf) * conj(Yf * diag(V / abs(V)))
50
                        + conj(diag(If)) * sparse(range(nl), f, V(f)/abs(V(f)))
51

52
    Derivations for "to" bus are similar.
53

54
    For more details on the derivations behind the derivative code used
55
    in PYPOWER information, see:
56

57
    [TN2]  R. D. Zimmerman, "AC Power Flows, Generalized OPF Costs and
58
    their Derivatives using Complex Matrix Notation", MATPOWER
59
    Technical Note 2, February 2010.
60
    U{http://www.pserc.cornell.edu/matpower/TN2-OPF-Derivatives.pdf}
61

62
    @author: Ray Zimmerman (PSERC Cornell)
63
    """
64
    ## define
65 1
    f = branch[:, F_BUS].real.astype(int)       ## list of "from" buses
66 1
    t = branch[:, T_BUS].real.astype(int)       ## list of "to" buses
67 1
    nl = len(f)
68 1
    nb = len(V)
69 1
    il = arange(nl)
70 1
    ib = arange(nb)
71

72 1
    Vnorm = V / abs(V)
73

74 1
    if issparse(Yf):
75
        ## compute currents
76 1
        If = Yf * V
77 1
        It = Yt * V
78

79 1
        diagVf = sparse((V[f], (il, il)))
80 1
        diagIf = sparse((If, (il, il)))
81 1
        diagVt = sparse((V[t], (il, il)))
82 1
        diagIt = sparse((It, (il, il)))
83 1
        diagV  = sparse((V, (ib, ib)))
84 1
        diagVnorm = sparse((Vnorm, (ib, ib)))
85

86 1
        shape = (nl, nb)
87
        # Partial derivative of S w.r.t voltage phase angle.
88 1
        dSf_dVa = 1j * (conj(diagIf) *
89
            sparse((V[f], (il, f)), shape) - diagVf * conj(Yf * diagV))
90

91 1
        dSt_dVa = 1j * (conj(diagIt) *
92
            sparse((V[t], (il, t)), shape) - diagVt * conj(Yt * diagV))
93

94
        # Partial derivative of S w.r.t. voltage amplitude.
95 1
        dSf_dVm = diagVf * conj(Yf * diagVnorm) + conj(diagIf) * \
96
            sparse((Vnorm[f], (il, f)), shape)
97

98 1
        dSt_dVm = diagVt * conj(Yt * diagVnorm) + conj(diagIt) * \
99
            sparse((Vnorm[t], (il, t)), shape)
100
    else:  ## dense version
101
        ## compute currents
102 0
        If = asarray( Yf * asmatrix(V).T ).flatten()
103 0
        It = asarray( Yt * asmatrix(V).T ).flatten()
104

105 0
        diagVf      = asmatrix( diag(V[f]) )
106 0
        diagIf      = asmatrix( diag(If) )
107 0
        diagVt      = asmatrix( diag(V[t]) )
108 0
        diagIt      = asmatrix( diag(It) )
109 0
        diagV       = asmatrix( diag(V) )
110 0
        diagVnorm   = asmatrix( diag(Vnorm) )
111 0
        temp1       = asmatrix( zeros((nl, nb), complex) )
112 0
        temp2       = asmatrix( zeros((nl, nb), complex) )
113 0
        temp3       = asmatrix( zeros((nl, nb), complex) )
114 0
        temp4       = asmatrix( zeros((nl, nb), complex) )
115 0
        for i in range(nl):
116 0
            fi, ti = f[i], t[i]
117 0
            temp1[i, fi] = V[fi].item()
118 0
            temp2[i, fi] = Vnorm[fi].item()
119 0
            temp3[i, ti] = V[ti].item()
120 0
            temp4[i, ti] = Vnorm[ti].item()
121

122 0
        dSf_dVa = 1j * (conj(diagIf) * temp1 - diagVf * conj(Yf * diagV))
123 0
        dSf_dVm = diagVf * conj(Yf * diagVnorm) + conj(diagIf) * temp2
124 0
        dSt_dVa = 1j * (conj(diagIt) * temp3 - diagVt * conj(Yt * diagV))
125 0
        dSt_dVm = diagVt * conj(Yt * diagVnorm) + conj(diagIt) * temp4
126

127
    # Compute power flow vectors.
128 1
    Sf = V[f] * conj(If)
129 1
    St = V[t] * conj(It)
130

131 1
    return dSf_dVa, dSf_dVm, dSt_dVa, dSt_dVm, Sf, St

Read our documentation on viewing source code .

Loading