-
Notifications
You must be signed in to change notification settings - Fork 466
/
makePTDF.py
83 lines (63 loc) · 2.82 KB
/
makePTDF.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# -*- coding: utf-8 -*-
# Copyright 1996-2015 PSERC. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
# Copyright (c) 2016-2020 by University of Kassel and Fraunhofer Institute for Energy Economics
# and Energy System Technology (IEE), Kassel. All rights reserved.
"""Builds the DC PTDF matrix for a given choice of slack.
"""
from sys import stderr
from numpy import zeros, arange, isscalar, dot, ix_, flatnonzero as find
import numpy as np
from numpy.linalg import solve
from .idx_bus import BUS_TYPE, REF, BUS_I
from .makeBdc import makeBdc
def makePTDF(baseMVA, bus, branch, slack=None):
"""Builds the DC PTDF matrix for a given choice of slack.
Returns the DC PTDF matrix for a given choice of slack. The matrix is
C{nbr x nb}, where C{nbr} is the number of branches and C{nb} is the
number of buses. The C{slack} can be a scalar (single slack bus) or an
C{nb x 1} column vector of weights specifying the proportion of the
slack taken up at each bus. If the C{slack} is not specified the
reference bus is used by default.
For convenience, C{slack} can also be an C{nb x nb} matrix, where each
column specifies how the slack should be handled for injections
at that bus.
@see: L{makeLODF}
@author: Ray Zimmerman (PSERC Cornell)
"""
## use reference bus for slack by default
if slack is None:
slack = find(bus[:, BUS_TYPE] == REF)
slack = slack[0]
## set the slack bus to be used to compute initial PTDF
if isscalar(slack):
slack_bus = slack
else:
slack_bus = 0 ## use bus 1 for temp slack bus
nb = bus.shape[0]
nbr = branch.shape[0]
noref = arange(1, nb) ## use bus 1 for voltage angle reference
noslack = find(arange(nb) != slack_bus)
## check that bus numbers are equal to indices to bus (one set of bus numbers)
if any(bus[:, BUS_I] != arange(nb)):
stderr.write('makePTDF: buses must be numbered consecutively')
## compute PTDF for single slack_bus
Bbus, Bf, _, _ = makeBdc(bus, branch)
Bbus, Bf = np.real(Bbus.toarray()), np.real(Bf.toarray())
H = zeros((nbr, nb))
H[:, noslack] = solve( Bbus[ix_(noslack, noref)].T, Bf[:, noref].T ).T
# = Bf[:, noref] * inv(Bbus[ix_(noslack, noref)])
## distribute slack, if requested
if not isscalar(slack):
if len(slack.shape) == 1: ## slack is a vector of weights
slack = slack / sum(slack) ## normalize weights
## conceptually, we want to do ...
## H = H * (eye(nb, nb) - slack * ones((1, nb)))
## ... we just do it more efficiently
v = dot(H, slack)
for k in range(nb):
H[:, k] = H[:, k] - v
else:
H = dot(H, slack)
return H