1 #------------------------------------------------------------------------------
2 # Copyright (C) 1996-2010 Power System Engineering Research Center (PSERC)
3 # Copyright (C) 2007-2010 Richard Lincoln
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #------------------------------------------------------------------------------
18 """ Defines a solver for DC power flow.
21 #------------------------------------------------------------------------------
23 #------------------------------------------------------------------------------
29 from numpy
import array
, linalg
, pi
, r_
, ix_
31 from scipy
.sparse
.linalg
import spsolve
33 from pylon
.case
import REFERENCE
, PV
, PQ
35 #------------------------------------------------------------------------------
37 #------------------------------------------------------------------------------
39 logger
= logging
.getLogger(__name__
)
41 #------------------------------------------------------------------------------
43 #------------------------------------------------------------------------------
46 """ Solves DC power flow.
48 Based on dcpf.m from MATPOWER by Ray Zimmerman, developed at PSERC
49 Cornell. See U{http://www.pserc.cornell.edu/matpower/} for more info.
52 #--------------------------------------------------------------------------
54 #--------------------------------------------------------------------------
56 def __init__(self
, case
):
57 """ Initialises a DCPF instance.
62 #: Vector of voltage phase angles.
67 """ Solves a DC power flow.
70 logger
.info("Starting DC power flow [%s]." % case
.name
)
73 self
.case
.index_buses()
75 # Find the index of the refence bus.
76 ref_idx
= self
._get
_reference
_index
(case
)
80 # Build the susceptance matrices.
81 B
, Bsrc
, p_businj
, p_srcinj
= case
.Bdc
82 # Get the vector of initial voltage angles.
83 v_angle_guess
= self
._get
_v
_angle
_guess
(case
)
84 # Calculate the new voltage phase angles.
85 v_angle
, p_ref
= self
._get
_v
_angle
(case
, B
, v_angle_guess
, p_businj
,
87 logger
.debug("Bus voltage phase angles: \n%s" % v_angle
)
88 self
.v_angle
= v_angle
90 # Push the results to the case.
91 self
._update
_model
(case
, B
, Bsrc
, v_angle
, p_srcinj
, p_ref
, ref_idx
)
93 logger
.info("DC power flow completed in %.3fs." % (time
.time() - t0
))
97 #--------------------------------------------------------------------------
98 # Reference bus index:
99 #--------------------------------------------------------------------------
101 def _get_reference_index(self
, case
):
102 """ Returns the index of the reference bus.
104 refs
= [bus
._i
for bus
in case
.connected_buses
if bus
.type == REFERENCE
]
108 logger
.error("Single swing bus required for DCPF.")
111 #--------------------------------------------------------------------------
112 # Build voltage phase angle guess vector:
113 #--------------------------------------------------------------------------
115 def _get_v_angle_guess(self
, case
):
116 """ Make the vector of voltage phase guesses.
118 v_angle
= array([bus
.v_angle
* (pi
/ 180.0)
119 for bus
in case
.connected_buses
])
122 #--------------------------------------------------------------------------
123 # Calculate voltage angles:
124 #--------------------------------------------------------------------------
126 def _get_v_angle(self
, case
, B
, v_angle_guess
, p_businj
, iref
):
127 """ Calculates the voltage phase angles.
129 buses
= case
.connected_buses
131 pv_idxs
= [bus
._i
for bus
in buses
if bus
.type == PV
]
132 pq_idxs
= [bus
._i
for bus
in buses
if bus
.type == PQ
]
133 pvpq_idxs
= pv_idxs
+ pq_idxs
134 pvpq_rows
= [[i
] for i
in pvpq_idxs
]
136 # Get the susceptance matrix with the column and row corresponding to
137 # the reference bus removed.
138 Bpvpq
= B
[pvpq_rows
, pvpq_idxs
]
140 Bref
= B
[pvpq_rows
, [iref
]]
142 # Bus active power injections (generation - load) adjusted for phase
143 # shifters and real shunts.
144 p_surplus
= array([case
.s_surplus(v
).real
for v
in buses
])
145 g_shunt
= array([bus
.g_shunt
for bus
in buses
])
146 Pbus
= (p_surplus
- p_businj
- g_shunt
) / case
.base_mva
148 Pbus
.shape
= len(Pbus
), 1
151 b
= Pbus
[pvpq_idxs
] - Bref
* v_angle_guess
[iref
]
153 # x, res, rank, s = linalg.lstsq(A.todense(), b)
156 # Insert the reference voltage angle of the slack bus.
157 v_angle
= r_
[x
[:iref
], v_angle_guess
[iref
], x
[iref
:]]
159 return v_angle
, Pbus
[iref
]
161 #--------------------------------------------------------------------------
162 # Update model with solution:
163 #--------------------------------------------------------------------------
165 def _update_model(self
, case
, B
, Bsrc
, v_angle
, p_srcinj
, p_ref
, ref_idx
):
166 """ Updates the case with values computed from the voltage phase
170 base_mva
= case
.base_mva
171 buses
= case
.connected_buses
172 branches
= case
.online_branches
174 p_from
= (Bsrc
* v_angle
+ p_srcinj
) * base_mva
177 for i
, branch
in enumerate(branches
):
178 branch
.p_from
= p_from
[i
]
179 branch
.p_to
= p_to
[i
]
183 for j
, bus
in enumerate(buses
):
184 bus
.v_angle
= v_angle
[j
] * (180 / pi
)
185 bus
.v_magnitude
= 1.0
187 # Update Pg for swing generator.
188 g_ref
= [g
for g
in case
.generators
if g
.bus
== buses
[iref
]][0]
189 # Pg = Pinj + Pload + Gs
190 # newPg = oldPg + newPinj - oldPinj
191 p_inj
= (B
[iref
, :] * v_angle
- p_ref
) * base_mva
194 # EOF -------------------------------------------------------------------------