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