1 #------------------------------------------------------------------------------
2 # Copyright (C) 2010 Richard Lincoln
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #------------------------------------------------------------------------------
18 """ Defines an OPF solver using CVXOPT [1].
20 Note: This module is licensed under GNU GPL version 3 due to the
23 [1] Ray Zimmerman, "mipsopf_solver.m", MATPOWER, PSERC Cornell,
24 version 4.0b1, http://www.pserc.cornell.edu/matpower/, Dec 2009
27 #------------------------------------------------------------------------------
29 #------------------------------------------------------------------------------
33 from numpy
import polyval
, polyder
, finfo
, pi
35 from cvxopt
import matrix
, spmatrix
, mul
, sparse
, exp
, solvers
, div
, spdiag
37 from pylon
.solver
import _Solver
, DCOPFSolver
, SFLOW
, PFLOW
, IFLOW
39 #------------------------------------------------------------------------------
41 #------------------------------------------------------------------------------
43 logger
= logging
.getLogger(__name__
)
45 #------------------------------------------------------------------------------
47 #------------------------------------------------------------------------------
49 EPS
= finfo(float).eps
51 #------------------------------------------------------------------------------
52 # "DCCVXOPTSolver" class:
53 #------------------------------------------------------------------------------
55 class DCCVXOPTSolver(DCOPFSolver
):
56 """ Solves DC optimal power flow using CVXOPT.
59 #--------------------------------------------------------------------------
61 #--------------------------------------------------------------------------
63 def __init__(self
, om
, opt
=None, solver
=None):
64 """ Initialises the new DCOPF instance.
66 super(DCOPFSolver
, self
).__init
__(om
, opt
)
68 # Choice of solver (May be None or "mosek" (or "glpk" for linear
69 # formulation)). Specify None to use the Python solver from CVXOPT.
72 if opt
.has_key("verbose"):
73 solvers
.options
["show_progress"] = opt
["verbose"]
74 if opt
.has_key("max_it"):
75 solvers
.options
["maxiters"] = opt
["max_it"]
76 if opt
.has_key("feastol"):
77 solvers
.options
["feastol"] = opt
["feastol"]
78 if opt
.has_key("gradtol"):
79 raise NotImplementedError
80 if opt
.has_key("comptol"):
81 raise NotImplementedError
82 if opt
.has_key("costtol"):
83 raise NotImplementedError
84 if opt
.has_key("max_red"):
85 raise NotImplementedError
86 if opt
.has_key("step_control"):
87 raise NotImplementedError
88 if opt
.has_key("cost_mult"):
89 raise NotImplementedError
92 def _run_opf(self
, P
, q
, AA
, ll
, uu
, xmin
, xmax
, x0
, opt
):
93 """ Solves the either quadratic or linear program.
96 nx
= x0
.shape
[0] # number of variables
97 # add var limits to linear constraints
98 eyex
= spmatrix(1.0, range(nx
), range(nx
))
99 A
= sparse([eyex
, AAcvx
])
100 l
= matrix([-xmin
, ll
])
101 u
= matrix([ xmax
, uu
])
103 Ae
, be
, Ai
, bi
= split_linear_constraints(A
, l
, u
)
107 cvx_sol
= solvers
.qp(P
, q
, Ai
, bi
, Ae
, be
, self
.solver
, {"x": x0
})
109 cvx_sol
= solvers
.lp(q
, Ai
, bi
, Ae
, be
, self
.solver
, {"x": x0
})
114 def _update_case(self
, bs
, ln
, gn
, base_mva
, Bf
, Pfinj
, Va
, Pg
, lmbda
):
115 """ Calculates the result attribute values.
117 for i
, bus
in enumerate(bs
):
118 bus
.v_angle
= Va
[i
] * 180.0 / pi
120 #------------------------------------------------------------------------------
121 # "CVXOPTSolver" class:
122 #------------------------------------------------------------------------------
124 class CVXOPTSolver(_Solver
):
125 """ Solves AC optimal power flow using convex optimization.
128 def __init__(self
, om
, opt
=None, flow_lim
=SFLOW
):
129 """ Initialises a new CVXOPTSolver instance.
131 super(CVXOPTSolver
, self
).__init
__(om
)
133 # Quantity to limit for branch flow constraints ("S", "P" or "I").
134 self
.flow_lim
= flow_lim
136 self
.opt
= {} if opt
is None else opt
138 if self
.opt
.has_key("verbose"):
139 solvers
.options
["show_progress"] = self
.opt
["verbose"]
140 if self
.opt
.has_key("max_it"):
141 solvers
.options
["maxiters"] = self
.opt
["max_it"]
142 if self
.opt
.has_key("feastol"):
143 solvers
.options
["feastol"] = self
.opt
["feastol"]
144 if self
.opt
.has_key("gradtol"):
145 raise NotImplementedError
146 if self
.opt
.has_key("comptol"):
147 raise NotImplementedError
148 if self
.opt
.has_key("costtol"):
149 raise NotImplementedError
150 if self
.opt
.has_key("max_red"):
151 raise NotImplementedError
152 if self
.opt
.has_key("step_control"):
153 raise NotImplementedError
154 if self
.opt
.has_key("cost_mult"):
155 raise NotImplementedError
160 base_mva
= case
.base_mva
161 self
.opt
["cost_mult"] = 1e-4
162 # Unpack the OPF model.
163 bs
, ln
, gn
, _
= self
._unpack
_model
(self
.om
)
164 # Compute problem dimensions.
166 ipol
, _
, nb
, nl
, _
, ny
, nxyz
= self
._dimension
_data
(bs
, ln
, gn
)
167 # The number of non-linear equality constraints.
169 # The number of control variables.
170 # nc = 2 * nb + 2 * ng
171 # Indexes of constrained lines.
172 il
= matrix([i
for i
,l
in enumerate(ln
) if 0.0 < l
.rate_a
< 1e10
])
175 neqnln
= 2 * nb
# no. of non-linear equality constraints
176 niqnln
= 2 * len(il
) # no. of lines with constraints
178 # Linear constraints (l <= A*x <= u).
179 A
, l
, u
= self
.om
.linear_constraints()
181 AA
= spmatrix([], [], [], (0, self
.om
.var_N
))
184 Ae
, be
, Ai
, bi
= split_linear_constraints(AA
, matrix(l
), matrix(u
))
186 _
, xmin
, xmax
= self
._var
_bounds
()
188 # Select an interior initial point for interior point solver.
189 x0
= matrix(self
._initial
_interior
_point
(bs
, gn
, xmin
, xmax
, ny
))
191 # Build admittance matrices.
192 YYbus
, YYf
, YYt
= case
.Y
197 # Optimisation variables.
198 Va
= self
.om
.get_var("Va")
199 Vm
= self
.om
.get_var("Vm")
200 Pg
= self
.om
.get_var("Pg")
201 Qg
= self
.om
.get_var("Qg")
203 def F(x
=None, z
=None):
204 """ Evaluates the objective and nonlinear constraint functions.
207 # Include power mismatch constraint twice to force equality.
208 nln_N
= self
.om
.nln_N
+ neqnln
211 # Evaluate objective function -------------------------------------
213 Pgen
= x
[Pg
.i1
:Pg
.iN
+ 1] # Active generation in p.u.
214 Qgen
= x
[Qg
.i1
:Qg
.iN
+ 1] # Reactive generation in p.u.
216 xx
= matrix([Pgen
, Qgen
]) * base_mva
218 # Evaluate the objective function value.
220 # FIXME: Implement reactive power costs.
221 f0
= sum([g
.total_cost(xx
[i
]) for i
, g
in enumerate(gn
)])
225 # Piecewise linear cost of P and Q.
227 y
= self
.om
.get_var("y")
228 ccost
= spmatrix(matrix(1.0, (ny
, 1)),
229 range(y
.i1
, y
.iN
+ 1),
230 matrix(0.0, (ny
, 1)), size
=(nxyz
, 1)).T
233 ccost
= matrix(0.0, (1, nxyz
))
234 # TODO: Generalised cost term.
236 # Evaluate cost gradient ------------------------------------------
238 iPg
= range(Pg
.i1
, Pg
.iN
+ 1)
239 iQg
= range(Qg
.i1
, Qg
.iN
+ 1)
241 # Polynomial cost of P and Q.
242 df_dPgQg
= matrix(0.0, (2 * ng
, 1)) # w.r.t p.u. Pg and Qg
245 base_mva
* polyval(polyder(list(gn
[i
].p_cost
)), xx
[i
])
247 df0
= matrix(0.0, (nxyz
, 1))
248 df0
[iPg
] = df_dPgQg
[:ng
]
249 df0
[iQg
] = df_dPgQg
[ng
:ng
+ ng
]
251 # Piecewise linear cost of P and Q.
253 # TODO: Generalised cost term.
255 # Evaluate cost Hessian -------------------------------------------
259 # Evaluate nonlinear equality constraints -------------------------
261 for i
, g
in enumerate(gn
):
262 g
.p
= Pgen
[i
] * base_mva
# active generation in MW
263 g
.q
= Qgen
[i
] * base_mva
# reactive generation in MVAr
265 # Rebuild the net complex bus power injection vector in p.u.
266 Sbus
= matrix(case
.getSbus(bs
))
268 Vang
= x
[Va
.i1
:Va
.iN
+ 1]
269 Vmag
= x
[Vm
.i1
:Vm
.iN
+ 1]
270 V
= mul(Vmag
, exp(1j
* Vang
))
272 # Evaluate the power flow equations.
273 mis
= mul(V
, conj(Ybus
* V
)) - Sbus
275 # Equality constraints (power flow).
276 g
= matrix([mis
.real(), # active power mismatch for all buses
277 mis
.imag()]) # reactive power mismatch for all buses
279 # Evaluate nonlinear inequality constraints -----------------------
281 # Inequality constraints (branch flow limits).
282 # (line constraint is actually on square of limit)
283 flow_max
= matrix([(l
.rate_a
/ base_mva
)**2 for l
in ln
])
284 # FIXME: There must be a more elegant way to do this.
285 for i
, rate
in enumerate(flow_max
):
289 if self
.flow_lim
== IFLOW
:
292 # Branch current limits.
293 h
= matrix([(If
* conj(If
)) - flow_max
,
294 (If
* conj(It
)) - flow_max
])
296 i_fbus
= [e
.from_bus
._i
for e
in ln
]
297 i_tbus
= [e
.to_bus
._i
for e
in ln
]
298 # Complex power injected at "from" bus (p.u.).
299 Sf
= mul(V
[i_fbus
], conj(Yf
* V
))
300 # Complex power injected at "to" bus (p.u.).
301 St
= mul(V
[i_tbus
], conj(Yt
* V
))
302 if self
.flow_lim
== PFLOW
: # active power limit, P (Pan Wei)
303 # Branch real power limits.
304 h
= matrix([Sf
.real()**2 - flow_max
,
305 St
.real()**2 - flow_max
])
306 elif self
.flow_lim
== SFLOW
: # apparent power limit, |S|
307 # Branch apparent power limits.
308 h
= matrix([mul(Sf
, conj(Sf
)) - flow_max
,
309 mul(St
, conj(St
)) - flow_max
]).real()
313 # Evaluate partial derivatives of constraints ---------------------
315 iVa
= matrix(range(Va
.i1
, Va
.iN
+ 1))
316 iVm
= matrix(range(Vm
.i1
, Vm
.iN
+ 1))
317 iPg
= matrix(range(Pg
.i1
, Pg
.iN
+ 1))
318 iQg
= matrix(range(Qg
.i1
, Qg
.iN
+ 1))
319 iVaVmPgQg
= matrix([iVa
, iVm
, iPg
, iQg
]).T
321 # Compute partials of injected bus powers.
322 dSbus_dVm
, dSbus_dVa
= dSbus_dV(Ybus
, V
)
324 i_gbus
= [gen
.bus
._i
for gen
in gn
]
325 neg_Cg
= spmatrix(matrix(-1.0, (ng
, 1)),
329 # Transposed Jacobian of the power balance equality constraints.
330 dg
= spmatrix([], [], [], (nxyz
, 2 * nb
))
332 blank
= spmatrix([], [], [], (nb
, ng
))
334 dg
[iVaVmPgQg
, :] = sparse([
335 [dSbus_dVa
.real(), dSbus_dVa
.imag()],
336 [dSbus_dVm
.real(), dSbus_dVm
.imag()],
341 # Compute partials of flows w.r.t V.
342 if self
.flow_lim
== IFLOW
:
343 dFf_dVa
, dFf_dVm
, dFt_dVa
, dFt_dVm
, Ff
, Ft
= \
346 dFf_dVa
, dFf_dVm
, dFt_dVa
, dFt_dVm
, Ff
, Ft
= \
347 dSbr_dV(Yf
, Yt
, V
, bs
, ln
)
348 if self
.flow_lim
== PFLOW
:
349 dFf_dVa
= dFf_dVa
.real
350 dFf_dVm
= dFf_dVm
.real
351 dFt_dVa
= dFt_dVa
.real
352 dFt_dVm
= dFt_dVm
.real
356 # Squared magnitude of flow (complex power, current or real power).
357 df_dVa
, df_dVm
, dt_dVa
, dt_dVm
= \
358 dAbr_dV(dFf_dVa
, dFf_dVm
, dFt_dVa
, dFt_dVm
, Ff
, Ft
)
360 # Construct Jacobian of inequality constraints (branch limits) and
362 dh
= spmatrix([], [], [], (nxyz
, 2 * nl
))
363 dh
[matrix([iVa
, iVm
]).T
, :] = sparse([[df_dVa
, dt_dVa
],
366 f
= matrix([f0
, g
, h
, -g
])
367 df
= matrix([[df0
], [dg
], [dh
], [-dg
]]).T
372 # Evaluate cost Hessian -------------------------------------------
374 nxtra
= nxyz
- (2 * nb
)
376 # Evaluate d2f ----------------------------------------------------
378 d2f_dPg2
= matrix(0.0, (ng
, 1)) # w.r.t p.u. Pg
379 d2f_dQg2
= matrix(0.0, (ng
, 1)) # w.r.t p.u. Qg
382 d2f_dPg2
[i
, 0] = polyval(polyder(list(gn
[i
].p_cost
), 2),
383 Pg
.v0
[i
] * base_mva
) * base_mva
**2
384 # TODO: Reactive power costs.
386 # d2f_dQg2[i] = polyval(polyder(list(gn[i].q_cost), 2),
387 # Qg.v0[i] * base_mva) * base_mva**2
389 i
= matrix([range(Pg
.i1
, Pg
.iN
+ 1), range(Qg
.i1
, Qg
.iN
+ 1)])
391 d2f
= spmatrix(matrix([d2f_dPg2
, d2f_dQg2
]), i
, i
, (nxyz
, nxyz
))
393 # TODO: Generalised cost model.
395 d2f
= d2f
* self
.opt
["cost_mult"]
397 # Evaluate Hessian of power balance constraints -------------------
399 eqnonlin
= z
[1:neqnln
+ 1]
400 nlam
= len(eqnonlin
) / 2
401 lamP
= eqnonlin
[:nlam
]
402 lamQ
= eqnonlin
[nlam
:nlam
+ nlam
]
404 Gpaa
, Gpav
, Gpva
, Gpvv
= d2Sbus_dV2(Ybus
, V
, lamP
)
405 Gqaa
, Gqav
, Gqva
, Gqvv
= d2Sbus_dV2(Ybus
, V
, lamQ
)
407 d2G_1
= sparse([[sparse([[Gpaa
, Gpva
], [Gpav
, Gpvv
]]).real() +
408 sparse([[Gqaa
, Gqva
], [Gqav
, Gqvv
]]).imag()],
409 [spmatrix([], [], [], (2 * nb
, nxtra
))]])
410 d2G_2
= spmatrix([], [], [], (nxtra
, 2 * nb
+ nxtra
))
411 d2G
= sparse([d2G_1
, d2G_2
])
413 # Evaluate Hessian of flow constraints ---------------------------
415 ineqnonlin
= z
[1 + neqnln
:1 + neqnln
+ niqnln
]
417 nmu
= len(ineqnonlin
) / 2
418 muF
= ineqnonlin
[:nmu
]
419 muT
= ineqnonlin
[nmu
:nmu
+ nmu
]
421 if self
.flow_lim
== IFLOW
:
422 dIf_dVa
, dIf_dVm
, dIt_dVa
, dIt_dVm
, If
, It
= \
424 Hfaa
, Hfav
, Hfva
, Hfvv
= \
425 d2AIbr_dV2(dIf_dVa
, dIf_dVm
, If
, Yf
, V
, muF
)
426 Htaa
, Htav
, Htva
, Htvv
= \
427 d2AIbr_dV2(dIt_dVa
, dIt_dVm
, It
, Yt
, V
, muT
)
429 fr
= [e
.from_bus
._i
for e
in ln
]
430 to
= [e
.to_bus
._i
for e
in ln
]
431 # Line-bus connection matrices.
432 Cf
= spmatrix(1.0, range(nl
), fr
, (nl
, nb
))
433 Ct
= spmatrix(1.0, range(nl
), to
, (nl
, nb
))
434 dSf_dVa
, dSf_dVm
, dSt_dVa
, dSt_dVm
, Sf
, St
= \
435 dSbr_dV(Yf
, Yt
, V
, bs
, ln
)
436 if self
.flow_lim
== PFLOW
:
437 Hfaa
, Hfav
, Hfva
, Hfvv
= \
438 d2ASbr_dV2(dSf_dVa
.real(), dSf_dVm
.real(),
439 Sf
.real(), Cf
, Yf
, V
, muF
)
440 Htaa
, Htav
, Htva
, Htvv
= \
441 d2ASbr_dV2(dSt_dVa
.real(), dSt_dVm
.real(),
442 St
.real(), Ct
, Yt
, V
, muT
)
443 elif self
.flow_lim
== SFLOW
:
444 Hfaa
, Hfav
, Hfva
, Hfvv
= \
445 d2ASbr_dV2(dSf_dVa
, dSf_dVm
, Sf
, Cf
, Yf
, V
, muF
)
446 Htaa
, Htav
, Htva
, Htvv
= \
447 d2ASbr_dV2(dSt_dVa
, dSt_dVm
, St
, Ct
, Yt
, V
, muT
)
451 d2H_1
= sparse([[sparse([[Hfaa
, Hfva
], [Hfav
, Hfvv
]]) +
452 sparse([[Htaa
, Htva
], [Htav
, Htvv
]])],
453 [spmatrix([], [], [], (2 * nb
, nxtra
))]])
454 d2H_2
= spmatrix([], [], [], (nxtra
, 2 * nb
+ nxtra
))
455 d2H
= sparse([d2H_1
, d2H_2
])
457 Lxx
= d2f
+ d2G
+ d2H
461 # cp(F, G=None, h=None, dims=None, A=None, b=None, kktsolver=None)
464 # subject to fk(x) <= 0, k = 1, ..., mnl
467 solution
= solvers
.cp(F
, G
=Ai
, h
=bi
, dims
=None, A
=Ae
, b
=be
)
472 def split_linear_constraints(A
, l
, u
):
473 """ Returns the linear equality and inequality constraints.
479 for i
in range(len(l
)):
480 if abs(u
[i
] - l
[i
]) <= EPS
:
482 elif (u
[i
] > 1e10
) and (l
[i
] > -1e10
):
484 elif (l
[i
] <= -1e10
) and (u
[i
] < 1e10
):
486 elif (abs(u
[i
] - l
[i
]) > EPS
) and (u
[i
] < 1e10
) and (l
[i
] > -1e10
):
492 Ai
= sparse([A
[ilt
, :], -A
[igt
, :], A
[ibx
, :], -A
[ibx
, :]])
494 bi
= matrix([u
[ilt
], -l
[igt
], u
[ibx
], -l
[ibx
]])
496 return Ae
, be
, Ai
, bi
498 #--------------------------------------------------------------------------
499 # Partial derivative of power injection w.r.t. voltage:
500 #--------------------------------------------------------------------------
503 """ Computes the partial derivative of power injection w.r.t. voltage.
506 Ray Zimmerman, "dSbus_dV.m", MATPOWER, version 3.2,
507 PSERC (Cornell), http://www.pserc.cornell.edu/matpower/
513 diagVnorm
= spdiag(div(V
, abs(V
))) # Element-wise division.
515 dS_dVm
= diagV
* conj(Y
* diagVnorm
) + conj(diagIbus
) * diagVnorm
516 dS_dVa
= 1j
* diagV
* conj(diagIbus
- Y
* diagV
)
518 return dS_dVm
, dS_dVa
520 #--------------------------------------------------------------------------
521 # Partial derivatives of branch currents w.r.t. voltage.
522 #--------------------------------------------------------------------------
524 def dIbr_dV(Yf
, Yt
, V
):
525 """ Computes partial derivatives of branch currents w.r.t. voltage.
527 Ray Zimmerman, "dIbr_dV.m", MATPOWER, version 4.0b1,
528 PSERC (Cornell), http://www.pserc.cornell.edu/matpower/
532 Vnorm
= div(V
, abs(V
))
534 diagVnorm
= spdiag(Vnorm
)
535 dIf_dVa
= Yf
* 1j
* diagV
536 dIf_dVm
= Yf
* diagVnorm
537 dIt_dVa
= Yt
* 1j
* diagV
538 dIt_dVm
= Yt
* diagVnorm
544 return dIf_dVa
, dIf_dVm
, dIt_dVa
, dIt_dVm
, If
, It
546 #--------------------------------------------------------------------------
547 # Partial derivative of branch power flow w.r.t voltage:
548 #--------------------------------------------------------------------------
550 def dSbr_dV(Yf
, Yt
, V
, buses
, branches
):
551 """ Computes the branch power flow vector and the partial derivative of
552 branch power flow w.r.t voltage.
557 f
= matrix([l
.from_bus
._i
for l
in branches
])
558 t
= matrix([l
.to_bus
._i
for l
in branches
])
564 Vnorm
= div(V
, abs(V
))
566 diagVf
= spdiag(V
[f
])
568 diagVt
= spdiag(V
[t
])
571 diagVnorm
= spdiag(Vnorm
)
575 # Partial derivative of S w.r.t voltage phase angle.
576 dSf_dVa
= 1j
* (conj(diagIf
) *
577 spmatrix(V
[f
], ibr
, f
, size
) - diagVf
* conj(Yf
* diagV
))
579 dSt_dVa
= 1j
* (conj(diagIt
) *
580 spmatrix(V
[t
], ibr
, t
, size
) - diagVt
* conj(Yt
* diagV
))
582 # Partial derivative of S w.r.t. voltage amplitude.
583 dSf_dVm
= diagVf
* conj(Yf
* diagVnorm
) + conj(diagIf
) * \
584 spmatrix(Vnorm
[f
], ibr
, f
, size
)
586 dSt_dVm
= diagVt
* conj(Yt
* diagVnorm
) + conj(diagIt
) * \
587 spmatrix(Vnorm
[t
], ibr
, t
, size
)
589 # Compute power flow vectors.
590 Sf
= mul(V
[f
], conj(If
))
591 St
= mul(V
[t
], conj(It
))
593 return dSf_dVa
, dSf_dVm
, dSt_dVa
, dSt_dVm
, Sf
, St
595 #--------------------------------------------------------------------------
596 # Partial derivative of apparent power flow w.r.t voltage:
597 #--------------------------------------------------------------------------
599 def dAbr_dV(dSf_dVa
, dSf_dVm
, dSt_dVa
, dSt_dVm
, Sf
, St
):
600 """ Partial derivatives of squared flow magnitudes w.r.t voltage.
602 Computes partial derivatives of apparent power w.r.t active and
603 reactive power flows. Partial derivative must equal 1 for lines
604 with zero flow to avoid division by zero errors (1 comes from
607 dAf_dPf
= spdiag(2 * Sf
.real())
608 dAf_dQf
= spdiag(2 * Sf
.imag())
609 dAt_dPt
= spdiag(2 * St
.real())
610 dAt_dQt
= spdiag(2 * St
.imag())
612 # Partial derivative of apparent power magnitude w.r.t voltage
614 dAf_dVa
= dAf_dPf
* dSf_dVa
.real() + dAf_dQf
* dSf_dVa
.imag()
615 dAt_dVa
= dAt_dPt
* dSt_dVa
.real() + dAt_dQt
* dSt_dVa
.imag()
616 # Partial derivative of apparent power magnitude w.r.t. voltage
618 dAf_dVm
= dAf_dPf
* dSf_dVm
.real() + dAf_dQf
* dSf_dVm
.imag()
619 dAt_dVm
= dAt_dPt
* dSt_dVm
.real() + dAt_dQt
* dSt_dVm
.imag()
621 return dAf_dVa
, dAf_dVm
, dAt_dVa
, dAt_dVm
623 #--------------------------------------------------------------------------
624 # Second derivative of power injection w.r.t voltage:
625 #--------------------------------------------------------------------------
627 def d2Sbus_dV2(Ybus
, V
, lam
):
628 """ Computes 2nd derivatives of power injection w.r.t. voltage.
632 diaglam
= spdiag(lam
)
635 A
= spmatrix(mul(lam
, V
), range(n
), range(n
))
639 E
= conj(diagV
) * (D
* diaglam
- spmatrix(D
*lam
, range(n
), range(n
)))
640 F
= C
- A
* spmatrix(conj(Ibus
), range(n
), range(n
))
641 G
= spmatrix(div(matrix(1.0, (n
, 1)), abs(V
)), range(n
), range(n
))
644 Gva
= 1j
* G
* (E
- F
)
646 Gvv
= G
* (C
+ C
.T
) * G
648 return Gaa
, Gav
, Gva
, Gvv
650 #--------------------------------------------------------------------------
651 # Second derivative of complex branch current w.r.t. voltage:
652 #--------------------------------------------------------------------------
654 def d2Ibr_dV2(Ybr
, V
, lam
):
655 """ Computes 2nd derivatives of complex branch current w.r.t. voltage.
658 diaginvVm
= spdiag(div(matrix(1.0, (nb
, 1)), abs(V
)))
660 Haa
= spdiag(mul(-(Ybr
.T
* lam
), V
))
661 Hva
= -1j
* Haa
* diaginvVm
663 Hvv
= spmatrix([], [], [], (nb
, nb
))
665 return Haa
, Hav
, Hva
, Hvv
667 #--------------------------------------------------------------------------
668 # Second derivative of complex power flow w.r.t. voltage:
669 #--------------------------------------------------------------------------
671 def d2Sbr_dV2(Cbr
, Ybr
, V
, lam
):
672 """ Computes 2nd derivatives of complex power flow w.r.t. voltage.
676 diaglam
= spdiag(lam
)
679 A
= Ybr
.H
* diaglam
* Cbr
680 B
= conj(diagV
) * A
* diagV
681 D
= spdiag(mul((A
*V
), conj(V
)))
682 E
= spdiag(mul((A
.T
* conj(V
)), V
))
684 G
= spdiag(div(matrix(1.0, (nb
, 1)), abs(V
)))
687 Hva
= 1j
* G
* (B
- B
.T
- D
+ E
)
691 return Haa
, Hav
, Hva
, Hvv
693 #--------------------------------------------------------------------------
694 # Second derivative of |complex power flow|**2 w.r.t. voltage:
695 #--------------------------------------------------------------------------
697 def d2ASbr_dV2(dSbr_dVa
, dSbr_dVm
, Sbr
, Cbr
, Ybr
, V
, lam
):
698 """ Computes 2nd derivatives of |complex power flow|**2 w.r.t. V.
700 diaglam
= spdiag(lam
)
701 diagSbr_conj
= spdiag(conj(Sbr
))
703 Saa
, Sav
, Sva
, Svv
= d2Sbr_dV2(Cbr
, Ybr
, V
, diagSbr_conj
* lam
)
705 Haa
= 2 * ( Saa
+ dSbr_dVa
.T
* diaglam
* conj(dSbr_dVa
) ).real()
706 Hva
= 2 * ( Sva
+ dSbr_dVm
.T
* diaglam
* conj(dSbr_dVa
) ).real()
707 Hav
= 2 * ( Sav
+ dSbr_dVa
.T
* diaglam
* conj(dSbr_dVm
) ).real()
708 Hvv
= 2 * ( Svv
+ dSbr_dVm
.T
* diaglam
* conj(dSbr_dVm
) ).real()
710 return Haa
, Hav
, Hva
, Hvv
712 #--------------------------------------------------------------------------
713 # Second derivative of |complex current|**2 w.r.t. voltage:
714 #--------------------------------------------------------------------------
716 def d2AIbr_dV2(dIbr_dVa
, dIbr_dVm
, Ibr
, Ybr
, V
, lam
):
717 """ Computes 2nd derivatives of |complex current|**2 w.r.t. V.
719 diaglam
= spdiag(lam
)
720 diagIbr_conj
= spdiag(conj(Ibr
))
722 Iaa
, Iav
, Iva
, Ivv
= d2Ibr_dV2(Ybr
, V
, diagIbr_conj
* lam
)
724 Haa
= 2 * ( Iaa
+ dIbr_dVa
.T
* diaglam
* conj(dIbr_dVa
) ).real()
725 Hva
= 2 * ( Iva
+ dIbr_dVm
.T
* diaglam
* conj(dIbr_dVa
) ).real()
726 Hav
= 2 * ( Iav
+ dIbr_dVa
.T
* diaglam
* conj(dIbr_dVm
) ).real()
727 Hvv
= 2 * ( Ivv
+ dIbr_dVm
.T
* diaglam
* conj(dIbr_dVm
) ).real()
729 return Haa
, Hav
, Hva
, Hvv
731 #------------------------------------------------------------------------------
733 #------------------------------------------------------------------------------
736 """ Returns the complex conjugate of A as a new matrix.
738 return A
.ctrans().trans()
742 """ Converts a sparse SciPy matrix into a sparse CVXOPT matrix.
745 return spmatrix(Bcoo
.data
, Bcoo
.row
.tolist(), Bcoo
.col
.tolist())
747 # EOF -------------------------------------------------------------------------