ltbams 0.9.9__py3-none-any.whl → 1.0.2a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ams/__init__.py +4 -11
- ams/_version.py +3 -3
- ams/cases/5bus/pjm5bus_demo.xlsx +0 -0
- ams/cases/5bus/pjm5bus_jumper.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced.json +1062 -0
- ams/cases/5bus/pjm5bus_uced.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced_esd1.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced_ev.xlsx +0 -0
- ams/cases/ieee123/ieee123.xlsx +0 -0
- ams/cases/ieee123/ieee123_regcv1.xlsx +0 -0
- ams/cases/ieee14/ieee14.json +1166 -0
- ams/cases/ieee14/ieee14.raw +92 -0
- ams/cases/ieee14/ieee14_conn.xlsx +0 -0
- ams/cases/ieee14/ieee14_uced.xlsx +0 -0
- ams/cases/ieee39/ieee39.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_esd1.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_pvd1.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_vis.xlsx +0 -0
- ams/cases/matpower/benchmark.json +1594 -0
- ams/cases/matpower/case118.m +787 -0
- ams/cases/matpower/case14.m +129 -0
- ams/cases/matpower/case300.m +1315 -0
- ams/cases/matpower/case39.m +205 -0
- ams/cases/matpower/case5.m +62 -0
- ams/cases/matpower/case_ACTIVSg2000.m +9460 -0
- ams/cases/npcc/npcc.m +644 -0
- ams/cases/npcc/npcc_uced.xlsx +0 -0
- ams/cases/pglib/pglib_opf_case39_epri__api.m +243 -0
- ams/cases/wecc/wecc.m +714 -0
- ams/cases/wecc/wecc_uced.xlsx +0 -0
- ams/cli.py +6 -0
- ams/core/__init__.py +2 -0
- ams/core/documenter.py +652 -0
- ams/core/matprocessor.py +782 -0
- ams/core/model.py +330 -0
- ams/core/param.py +322 -0
- ams/core/service.py +918 -0
- ams/core/symprocessor.py +224 -0
- ams/core/var.py +59 -0
- ams/extension/__init__.py +5 -0
- ams/extension/eva.py +401 -0
- ams/interface.py +1085 -0
- ams/io/__init__.py +133 -0
- ams/io/json.py +82 -0
- ams/io/matpower.py +406 -0
- ams/io/psse.py +6 -0
- ams/io/pypower.py +103 -0
- ams/io/xlsx.py +80 -0
- ams/main.py +81 -4
- ams/models/__init__.py +24 -0
- ams/models/area.py +40 -0
- ams/models/bus.py +52 -0
- ams/models/cost.py +169 -0
- ams/models/distributed/__init__.py +3 -0
- ams/models/distributed/esd1.py +71 -0
- ams/models/distributed/ev.py +60 -0
- ams/models/distributed/pvd1.py +67 -0
- ams/models/group.py +231 -0
- ams/models/info.py +26 -0
- ams/models/line.py +238 -0
- ams/models/renewable/__init__.py +5 -0
- ams/models/renewable/regc.py +119 -0
- ams/models/reserve.py +94 -0
- ams/models/shunt.py +14 -0
- ams/models/static/__init__.py +2 -0
- ams/models/static/gen.py +165 -0
- ams/models/static/pq.py +61 -0
- ams/models/timeslot.py +69 -0
- ams/models/zone.py +49 -0
- ams/opt/__init__.py +12 -0
- ams/opt/constraint.py +175 -0
- ams/opt/exprcalc.py +127 -0
- ams/opt/expression.py +188 -0
- ams/opt/objective.py +174 -0
- ams/opt/omodel.py +432 -0
- ams/opt/optzbase.py +192 -0
- ams/opt/param.py +156 -0
- ams/opt/var.py +233 -0
- ams/pypower/__init__.py +8 -0
- ams/pypower/_compat.py +9 -0
- ams/pypower/core/__init__.py +8 -0
- ams/pypower/core/pips.py +894 -0
- ams/pypower/core/ppoption.py +244 -0
- ams/pypower/core/ppver.py +18 -0
- ams/pypower/core/solver.py +2451 -0
- ams/pypower/eps.py +6 -0
- ams/pypower/idx.py +174 -0
- ams/pypower/io.py +604 -0
- ams/pypower/make/__init__.py +11 -0
- ams/pypower/make/matrices.py +665 -0
- ams/pypower/make/pdv.py +506 -0
- ams/pypower/routines/__init__.py +7 -0
- ams/pypower/routines/cpf.py +513 -0
- ams/pypower/routines/cpf_callbacks.py +114 -0
- ams/pypower/routines/opf.py +1803 -0
- ams/pypower/routines/opffcns.py +1946 -0
- ams/pypower/routines/pflow.py +852 -0
- ams/pypower/toggle.py +1098 -0
- ams/pypower/utils.py +293 -0
- ams/report.py +212 -50
- ams/routines/__init__.py +23 -0
- ams/routines/acopf.py +117 -0
- ams/routines/cpf.py +65 -0
- ams/routines/dcopf.py +241 -0
- ams/routines/dcpf.py +209 -0
- ams/routines/dcpf0.py +196 -0
- ams/routines/dopf.py +150 -0
- ams/routines/ed.py +312 -0
- ams/routines/pflow.py +255 -0
- ams/routines/pflow0.py +113 -0
- ams/routines/routine.py +1033 -0
- ams/routines/rted.py +519 -0
- ams/routines/type.py +160 -0
- ams/routines/uc.py +376 -0
- ams/shared.py +63 -9
- ams/system.py +61 -22
- ams/utils/__init__.py +3 -0
- ams/utils/misc.py +77 -0
- ams/utils/paths.py +257 -0
- docs/Makefile +21 -0
- docs/make.bat +35 -0
- docs/source/_templates/autosummary/base.rst +5 -0
- docs/source/_templates/autosummary/class.rst +35 -0
- docs/source/_templates/autosummary/module.rst +65 -0
- docs/source/_templates/autosummary/module_toctree.rst +66 -0
- docs/source/api.rst +102 -0
- docs/source/conf.py +203 -0
- docs/source/examples/index.rst +34 -0
- docs/source/genmodelref.py +61 -0
- docs/source/genroutineref.py +47 -0
- docs/source/getting_started/copyright.rst +20 -0
- docs/source/getting_started/formats/index.rst +20 -0
- docs/source/getting_started/formats/matpower.rst +183 -0
- docs/source/getting_started/formats/psse.rst +46 -0
- docs/source/getting_started/formats/pypower.rst +223 -0
- docs/source/getting_started/formats/xlsx.png +0 -0
- docs/source/getting_started/formats/xlsx.rst +23 -0
- docs/source/getting_started/index.rst +76 -0
- docs/source/getting_started/install.rst +234 -0
- docs/source/getting_started/overview.rst +26 -0
- docs/source/getting_started/testcase.rst +45 -0
- docs/source/getting_started/verification.rst +13 -0
- docs/source/images/curent.ico +0 -0
- docs/source/images/dcopf_time.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_NameOnTrans.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_Transparent.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_Transparent_Name.png +0 -0
- docs/source/images/sponsors/doe.png +0 -0
- docs/source/index.rst +108 -0
- docs/source/modeling/example.rst +159 -0
- docs/source/modeling/index.rst +17 -0
- docs/source/modeling/model.rst +210 -0
- docs/source/modeling/routine.rst +122 -0
- docs/source/modeling/system.rst +51 -0
- docs/source/release-notes.rst +398 -0
- ltbams-1.0.2a1.dist-info/METADATA +210 -0
- ltbams-1.0.2a1.dist-info/RECORD +188 -0
- {ltbams-0.9.9.dist-info → ltbams-1.0.2a1.dist-info}/WHEEL +1 -1
- ltbams-1.0.2a1.dist-info/top_level.txt +3 -0
- tests/__init__.py +0 -0
- tests/test_1st_system.py +33 -0
- tests/test_addressing.py +40 -0
- tests/test_andes_mats.py +61 -0
- tests/test_case.py +266 -0
- tests/test_cli.py +34 -0
- tests/test_export_csv.py +89 -0
- tests/test_group.py +83 -0
- tests/test_interface.py +216 -0
- tests/test_io.py +32 -0
- tests/test_jumper.py +27 -0
- tests/test_known_good.py +267 -0
- tests/test_matp.py +437 -0
- tests/test_model.py +54 -0
- tests/test_omodel.py +119 -0
- tests/test_paths.py +22 -0
- tests/test_report.py +251 -0
- tests/test_repr.py +21 -0
- tests/test_routine.py +178 -0
- tests/test_rtn_dcopf.py +101 -0
- tests/test_rtn_dcpf.py +77 -0
- tests/test_rtn_ed.py +279 -0
- tests/test_rtn_pflow.py +219 -0
- tests/test_rtn_rted.py +273 -0
- tests/test_rtn_uc.py +248 -0
- tests/test_service.py +73 -0
- ltbams-0.9.9.dist-info/LICENSE +0 -692
- ltbams-0.9.9.dist-info/METADATA +0 -859
- ltbams-0.9.9.dist-info/RECORD +0 -14
- ltbams-0.9.9.dist-info/top_level.txt +0 -1
- {ltbams-0.9.9.dist-info → ltbams-1.0.2a1.dist-info}/entry_points.txt +0 -0
ams/pypower/core/pips.py
ADDED
@@ -0,0 +1,894 @@
|
|
1
|
+
"""
|
2
|
+
Python Interior Point Solver (PIPS).
|
3
|
+
"""
|
4
|
+
import logging
|
5
|
+
|
6
|
+
import numpy as np
|
7
|
+
from numpy import flatnonzero as find
|
8
|
+
|
9
|
+
import scipy.sparse as sp
|
10
|
+
from scipy.sparse import csr_matrix as c_sparse
|
11
|
+
|
12
|
+
from andes.shared import deg2rad, rad2deg
|
13
|
+
|
14
|
+
from ams.shared import inf
|
15
|
+
|
16
|
+
from ams.pypower.eps import EPS
|
17
|
+
from ams.pypower.idx import IDX
|
18
|
+
from ams.pypower.utils import sub2ind
|
19
|
+
from ams.pypower.make import makeYbus
|
20
|
+
from ams.pypower.routines.opffcns import (opf_costfcn, opf_consfcn,
|
21
|
+
opf_hessfcn,)
|
22
|
+
|
23
|
+
|
24
|
+
logger = logging.getLogger(__name__)
|
25
|
+
|
26
|
+
|
27
|
+
def pips(f_fcn, x0=None, A=None, l=None, u=None, xmin=None, xmax=None,
|
28
|
+
gh_fcn=None, hess_fcn=None, opt=None):
|
29
|
+
"""
|
30
|
+
Primal-dual interior point method for NLP (nonlinear programming).
|
31
|
+
|
32
|
+
Minimize a function F(X) beginning from a starting point x0, subject to
|
33
|
+
optional linear and nonlinear constraints and variable bounds:
|
34
|
+
|
35
|
+
min f(x)
|
36
|
+
x
|
37
|
+
|
38
|
+
subject to:
|
39
|
+
|
40
|
+
g(x) = 0 (nonlinear equalities)
|
41
|
+
h(x) <= 0 (nonlinear inequalities)
|
42
|
+
l <= A*x <= u (linear constraints)
|
43
|
+
xmin <= x <= xmax (variable bounds)
|
44
|
+
|
45
|
+
Note: The calling syntax is almost identical to that of FMINCON from
|
46
|
+
MathWorks' Optimization Toolbox. The main difference is that the linear
|
47
|
+
constraints are specified with A, l, u instead of A, B,
|
48
|
+
Aeq, Beq. The functions for evaluating the objective function,
|
49
|
+
constraints and Hessian are identical.
|
50
|
+
|
51
|
+
Example from http://en.wikipedia.org/wiki/Nonlinear_programming:
|
52
|
+
>>> from numpy import array, r_, float64, dot
|
53
|
+
>>> from scipy.sparse import csr_matrix
|
54
|
+
>>> def f2(x):
|
55
|
+
... f = -x[0] * x[1] - x[1] * x[2]
|
56
|
+
... df = -r_[x[1], x[0] + x[2], x[1]]
|
57
|
+
... # actually not used since 'hess_fcn' is provided
|
58
|
+
... d2f = -array([[0, 1, 0], [1, 0, 1], [0, 1, 0]], float64)
|
59
|
+
... return f, df, d2f
|
60
|
+
>>> def gh2(x):
|
61
|
+
... h = dot(array([[1, -1, 1],
|
62
|
+
... [1, 1, 1]]), x**2) + array([-2.0, -10.0])
|
63
|
+
... dh = 2 * csr_matrix(array([[ x[0], x[0]],
|
64
|
+
... [-x[1], x[1]],
|
65
|
+
... [ x[2], x[2]]]))
|
66
|
+
... g = array([])
|
67
|
+
... dg = None
|
68
|
+
... return h, g, dh, dg
|
69
|
+
>>> def hess2(x, lam, cost_mult=1):
|
70
|
+
... mu = lam["ineqnonlin"]
|
71
|
+
... a = r_[dot(2 * array([1, 1]), mu), -1, 0]
|
72
|
+
... b = r_[-1, dot(2 * array([-1, 1]), mu),-1]
|
73
|
+
... c = r_[0, -1, dot(2 * array([1, 1]), mu)]
|
74
|
+
... Lxx = csr_matrix(array([a, b, c]))
|
75
|
+
... return Lxx
|
76
|
+
>>> x0 = array([1, 1, 0], float64)
|
77
|
+
>>> solution = pips(f2, x0, gh_fcn=gh2, hess_fcn=hess2)
|
78
|
+
>>> round(solution["f"], 11) == -7.07106725919
|
79
|
+
True
|
80
|
+
>>> solution["output"]["iterations"]
|
81
|
+
8
|
82
|
+
|
83
|
+
All parameters are optional except f_fcn and x0.
|
84
|
+
|
85
|
+
Parameters:
|
86
|
+
----------
|
87
|
+
f_fcn : callable
|
88
|
+
Function that evaluates the objective function, its gradients
|
89
|
+
and Hessian for a given value of x. If there are
|
90
|
+
nonlinear constraints, the Hessian information is provided
|
91
|
+
by the 'hess_fcn' argument and is not required here.
|
92
|
+
x0 : array, optional
|
93
|
+
Starting value of optimization vector x.
|
94
|
+
A : csr_matrix, optional
|
95
|
+
Optional linear constraints.
|
96
|
+
l : array, optional
|
97
|
+
Optional linear constraints. Default values are -Inf.
|
98
|
+
u : array, optional
|
99
|
+
Optional linear constraints. Default values are Inf.
|
100
|
+
xmin : array, optional
|
101
|
+
Optional lower bounds on the x variables, defaults are
|
102
|
+
-Inf.
|
103
|
+
xmax : array, optional
|
104
|
+
Optional upper bounds on the x variables, defaults are
|
105
|
+
Inf.
|
106
|
+
gh_fcn : callable, optional
|
107
|
+
Function that evaluates the optional nonlinear constraints
|
108
|
+
and their gradients for a given value of x.
|
109
|
+
hess_fcn : callable, optional
|
110
|
+
Handle to function that computes the Hessian of the
|
111
|
+
Lagrangian for given values of x, lambda and mu,
|
112
|
+
where lambda and mu are the multipliers on the
|
113
|
+
equality and inequality constraints, g and h,
|
114
|
+
respectively.
|
115
|
+
opt : dict, optional
|
116
|
+
Optional options dictionary with the following keys, all of
|
117
|
+
which are also optional (default values shown in parentheses)
|
118
|
+
- 'verbose' (False) - Controls level of progress output
|
119
|
+
displayed
|
120
|
+
- 'feastol' (1e-6) - termination tolerance for feasibility
|
121
|
+
condition
|
122
|
+
- 'gradtol' (1e-6) - termination tolerance for gradient
|
123
|
+
condition
|
124
|
+
- 'comptol' (1e-6) - termination tolerance for
|
125
|
+
complementarity condition
|
126
|
+
- 'costtol' (1e-6) - termination tolerance for cost
|
127
|
+
condition
|
128
|
+
- 'max_it' (150) - maximum number of iterations
|
129
|
+
- 'step_control' (False) - set to True to enable step-size
|
130
|
+
control
|
131
|
+
- 'max_red' (20) - maximum number of step-size reductions if
|
132
|
+
step-control is on
|
133
|
+
- 'cost_mult' (1.0) - cost multiplier used to scale the
|
134
|
+
objective function for improved conditioning. Note: This
|
135
|
+
value is also passed as the 3rd argument to the Hessian
|
136
|
+
evaluation function so that it can appropriately scale the
|
137
|
+
objective function term in the Hessian of the Lagrangian.
|
138
|
+
|
139
|
+
Returns:
|
140
|
+
--------
|
141
|
+
dict
|
142
|
+
The solution dictionary has the following keys:
|
143
|
+
- 'x' - solution vector
|
144
|
+
- 'f' - final objective function value
|
145
|
+
- 'converged' - exit status
|
146
|
+
- True = first order optimality conditions satisfied
|
147
|
+
- False = maximum number of iterations reached
|
148
|
+
- None = numerically failed
|
149
|
+
- 'output' - output dictionary with keys:
|
150
|
+
- 'iterations' - number of iterations performed
|
151
|
+
- 'hist' - list of arrays with trajectories of the
|
152
|
+
following: feascond, gradcond, compcond, costcond, gamma,
|
153
|
+
stepsize, obj, alphap, alphad
|
154
|
+
- 'message' - exit message
|
155
|
+
- 'lmbda' - dictionary containing the Langrange and Kuhn-Tucker
|
156
|
+
multipliers on the constraints, with keys:
|
157
|
+
- 'eqnonlin' - nonlinear equality constraints
|
158
|
+
- 'ineqnonlin' - nonlinear inequality constraints
|
159
|
+
- 'mu_l' - lower (left-hand) limit on linear constraints
|
160
|
+
- 'mu_u' - upper (right-hand) limit on linear constraints
|
161
|
+
- 'lower' - lower bound on optimization variables
|
162
|
+
- 'upper' - upper bound on optimization variables
|
163
|
+
|
164
|
+
Notes
|
165
|
+
-----
|
166
|
+
1. Ported by Richard Lincoln from the MATLAB Interior Point Solver (MIPS)
|
167
|
+
(v1.9) by Ray Zimmerman. MIPS is distributed as part of the MATPOWER
|
168
|
+
project, developed at PSERC, Cornell.
|
169
|
+
|
170
|
+
Reference:
|
171
|
+
|
172
|
+
[1] "On the Computation and Application of Multi-period Security-Constrained
|
173
|
+
Optimal Power Flow for Real-time Electricity Market Operations",
|
174
|
+
Cornell University, May 2007.
|
175
|
+
|
176
|
+
[2] H. Wang, C. E. Murillo-Sanchez, R. D. Zimmerman, R. J. Thomas, "On Computational
|
177
|
+
Issues of Market-Based Optimal Power Flow", IEEE Transactions on Power Systems,
|
178
|
+
Vol. 22, No. 3, Aug. 2007, pp. 1185-1193.
|
179
|
+
|
180
|
+
Author
|
181
|
+
------
|
182
|
+
Ray Zimmerman (PSERC Cornell)
|
183
|
+
"""
|
184
|
+
if isinstance(f_fcn, dict): # problem dict
|
185
|
+
p = f_fcn
|
186
|
+
f_fcn = p['f_fcn']
|
187
|
+
x0 = p['x0']
|
188
|
+
if 'opt' in p:
|
189
|
+
opt = p['opt']
|
190
|
+
if 'hess_fcn' in p:
|
191
|
+
hess_fcn = p['hess_fcn']
|
192
|
+
if 'gh_fcn' in p:
|
193
|
+
gh_fcn = p['gh_fcn']
|
194
|
+
if 'xmax' in p:
|
195
|
+
xmax = p['xmax']
|
196
|
+
if 'xmin' in p:
|
197
|
+
xmin = p['xmin']
|
198
|
+
if 'u' in p:
|
199
|
+
u = p['u']
|
200
|
+
if 'l' in p:
|
201
|
+
l = p['l']
|
202
|
+
if 'A' in p:
|
203
|
+
A = p['A']
|
204
|
+
|
205
|
+
nx = x0.shape[0] # number of variables
|
206
|
+
nA = A.shape[0] if A is not None else 0 # number of original linear constr
|
207
|
+
|
208
|
+
# default argument values
|
209
|
+
if l is None or len(l) == 0:
|
210
|
+
l = -inf * np.ones(nA)
|
211
|
+
if u is None or len(u) == 0:
|
212
|
+
u = inf * np.ones(nA)
|
213
|
+
if xmin is None or len(xmin) == 0:
|
214
|
+
xmin = -inf * np.ones(x0.shape[0])
|
215
|
+
if xmax is None or len(xmax) == 0:
|
216
|
+
xmax = inf * np.ones(x0.shape[0])
|
217
|
+
if gh_fcn is None:
|
218
|
+
nonlinear = False
|
219
|
+
gn = np.array([])
|
220
|
+
hn = np.array([])
|
221
|
+
else:
|
222
|
+
nonlinear = True
|
223
|
+
|
224
|
+
if opt is None:
|
225
|
+
opt = {}
|
226
|
+
# options
|
227
|
+
if "feastol" not in opt:
|
228
|
+
opt["feastol"] = 1e-06
|
229
|
+
if "gradtol" not in opt:
|
230
|
+
opt["gradtol"] = 1e-06
|
231
|
+
if "comptol" not in opt:
|
232
|
+
opt["comptol"] = 1e-06
|
233
|
+
if "costtol" not in opt:
|
234
|
+
opt["costtol"] = 1e-06
|
235
|
+
if "max_it" not in opt:
|
236
|
+
opt["max_it"] = 150
|
237
|
+
if "max_red" not in opt:
|
238
|
+
opt["max_red"] = 20
|
239
|
+
if "step_control" not in opt:
|
240
|
+
opt["step_control"] = False
|
241
|
+
if "cost_mult" not in opt:
|
242
|
+
opt["cost_mult"] = 1
|
243
|
+
if "verbose" not in opt:
|
244
|
+
opt["verbose"] = 0
|
245
|
+
|
246
|
+
# initialize history
|
247
|
+
hist = []
|
248
|
+
|
249
|
+
# constants
|
250
|
+
xi = 0.99995
|
251
|
+
sigma = 0.1
|
252
|
+
z0 = 1
|
253
|
+
alpha_min = 1e-8
|
254
|
+
rho_min = 0.95
|
255
|
+
rho_max = 1.05
|
256
|
+
mu_threshold = 1e-5
|
257
|
+
|
258
|
+
# initialize
|
259
|
+
i = 0 # iteration counter
|
260
|
+
converged = False # flag
|
261
|
+
eflag = False # exit flag
|
262
|
+
|
263
|
+
# add var limits to linear constraints
|
264
|
+
eyex = sp.eye(nx, nx, format="csr")
|
265
|
+
AA = eyex if A is None else sp.vstack([eyex, A], "csr")
|
266
|
+
ll = np.r_[xmin, l]
|
267
|
+
uu = np.r_[xmax, u]
|
268
|
+
|
269
|
+
# split up linear constraints
|
270
|
+
ieq = find(np.abs(uu - ll) <= EPS)
|
271
|
+
igt = find((uu >= 1e10) & (ll > -1e10))
|
272
|
+
ilt = find((ll <= -1e10) & (uu < 1e10))
|
273
|
+
ibx = find((np.abs(uu - ll) > EPS) & (uu < 1e10) & (ll > -1e10))
|
274
|
+
# zero-sized sparse matrices unsupported
|
275
|
+
Ae = AA[ieq, :] if len(ieq) else None
|
276
|
+
if len(ilt) or len(igt) or len(ibx):
|
277
|
+
idxs = [(1, ilt), (-1, igt), (1, ibx), (-1, ibx)]
|
278
|
+
Ai = sp.vstack([sig * AA[idx, :] for sig, idx in idxs if len(idx)], 'csr')
|
279
|
+
else:
|
280
|
+
Ai = None
|
281
|
+
be = uu[ieq]
|
282
|
+
bi = np.r_[uu[ilt], -ll[igt], uu[ibx], -ll[ibx]]
|
283
|
+
|
284
|
+
# evaluate cost f(x0) and constraints g(x0), h(x0)
|
285
|
+
x = x0
|
286
|
+
f, df = f_fcn(x) # cost
|
287
|
+
f = f * opt["cost_mult"]
|
288
|
+
df = df * opt["cost_mult"]
|
289
|
+
if nonlinear:
|
290
|
+
hn, gn, dhn, dgn = gh_fcn(x) # nonlinear constraints
|
291
|
+
h = hn if Ai is None else np.r_[hn, Ai * x - bi] # inequality constraints
|
292
|
+
g = gn if Ae is None else np.r_[gn, Ae * x - be] # equality constraints
|
293
|
+
|
294
|
+
if (dhn is None) and (Ai is None):
|
295
|
+
dh = None
|
296
|
+
elif dhn is None:
|
297
|
+
dh = Ai.T
|
298
|
+
elif Ai is None:
|
299
|
+
dh = dhn
|
300
|
+
else:
|
301
|
+
dh = sp.hstack([dhn, Ai.T])
|
302
|
+
|
303
|
+
if (dgn is None) and (Ae is None):
|
304
|
+
dg = None
|
305
|
+
elif dgn is None:
|
306
|
+
dg = Ae.T
|
307
|
+
elif Ae is None:
|
308
|
+
dg = dgn
|
309
|
+
else:
|
310
|
+
dg = sp.hstack([dgn, Ae.T])
|
311
|
+
else:
|
312
|
+
h = -bi if Ai is None else Ai * x - bi # inequality constraints
|
313
|
+
g = -be if Ae is None else Ae * x - be # equality constraints
|
314
|
+
dh = None if Ai is None else Ai.T # 1st derivative of inequalities
|
315
|
+
dg = None if Ae is None else Ae.T # 1st derivative of equalities
|
316
|
+
|
317
|
+
# some dimensions
|
318
|
+
neq = g.shape[0] # number of equality constraints
|
319
|
+
niq = h.shape[0] # number of inequality constraints
|
320
|
+
neqnln = gn.shape[0] # number of nonlinear equality constraints
|
321
|
+
niqnln = hn.shape[0] # number of nonlinear inequality constraints
|
322
|
+
nlt = len(ilt) # number of upper bounded linear inequalities
|
323
|
+
ngt = len(igt) # number of lower bounded linear inequalities
|
324
|
+
nbx = len(ibx) # number of doubly bounded linear inequalities
|
325
|
+
|
326
|
+
# initialize gamma, lam, mu, z, e
|
327
|
+
gamma = 1 # barrier coefficient
|
328
|
+
lam = np.zeros(neq)
|
329
|
+
z = z0 * np.ones(niq)
|
330
|
+
mu = z0 * np.ones(niq)
|
331
|
+
k = find(h < -z0)
|
332
|
+
z[k] = -h[k]
|
333
|
+
k = find((gamma / z) > z0)
|
334
|
+
mu[k] = gamma / z[k]
|
335
|
+
e = np.ones(niq)
|
336
|
+
|
337
|
+
# check tolerance
|
338
|
+
f0 = f
|
339
|
+
if opt["step_control"]:
|
340
|
+
L = f + np.dot(lam, g) + np.dot(mu, h + z) - gamma * sum(np.log(z))
|
341
|
+
|
342
|
+
Lx = df.copy()
|
343
|
+
Lx = Lx + dg * lam if dg is not None else Lx
|
344
|
+
Lx = Lx + dh * mu if dh is not None else Lx
|
345
|
+
|
346
|
+
maxh = np.zeros(1) if len(h) == 0 else max(h)
|
347
|
+
|
348
|
+
gnorm = np.linalg.norm(g, inf) if len(g) else 0.0
|
349
|
+
lam_norm = np.linalg.norm(lam, inf) if len(lam) else 0.0
|
350
|
+
mu_norm = np.linalg.norm(mu, inf) if len(mu) else 0.0
|
351
|
+
znorm = np.linalg.norm(z, inf) if len(z) else 0.0
|
352
|
+
feascond = \
|
353
|
+
max([gnorm, maxh]) / (1 + max([np.linalg.norm(x, inf), znorm]))
|
354
|
+
gradcond = \
|
355
|
+
np.linalg.norm(Lx, inf) / (1 + max([lam_norm, mu_norm]))
|
356
|
+
compcond = np.dot(z, mu) / (1 + np.linalg.norm(x, inf))
|
357
|
+
costcond = np.abs(f - f0) / (1 + np.abs(f0))
|
358
|
+
|
359
|
+
# save history
|
360
|
+
hist.append({'feascond': feascond, 'gradcond': gradcond,
|
361
|
+
'compcond': compcond, 'costcond': costcond, 'gamma': gamma,
|
362
|
+
'stepsize': 0, 'obj': f / opt["cost_mult"], 'alphap': 0, 'alphad': 0})
|
363
|
+
|
364
|
+
s = '-sc' if opt["step_control"] else ''
|
365
|
+
headers = ' n objective step size'
|
366
|
+
headers += ' feascond gradcond'
|
367
|
+
headers += ' compcond costcond'
|
368
|
+
head_line = "=== ======== ====== "
|
369
|
+
head_line += " ======= ======="
|
370
|
+
head_line += " ======= ======="
|
371
|
+
logger.debug(headers)
|
372
|
+
logger.debug(head_line)
|
373
|
+
logger.debug("%3d %12.8g %12g %12g %12g %12g %12g" %
|
374
|
+
(i, (f / opt["cost_mult"]), 0, feascond, gradcond,
|
375
|
+
compcond, costcond))
|
376
|
+
|
377
|
+
if feascond < opt["feastol"] and gradcond < opt["gradtol"] and \
|
378
|
+
compcond < opt["comptol"] and costcond < opt["costtol"]:
|
379
|
+
converged = True
|
380
|
+
|
381
|
+
# do Newton iterations
|
382
|
+
while (not converged) and (i < opt["max_it"]):
|
383
|
+
# update iteration counter
|
384
|
+
i += 1
|
385
|
+
|
386
|
+
# compute update step
|
387
|
+
lmbda = {"eqnonlin": lam[range(neqnln)],
|
388
|
+
"ineqnonlin": mu[range(niqnln)]}
|
389
|
+
if nonlinear:
|
390
|
+
if hess_fcn is None:
|
391
|
+
logger.error("pips: Hessian evaluation via finite differences "
|
392
|
+
"not yet implemented.\nPlease provide "
|
393
|
+
"your own hessian evaluation function.")
|
394
|
+
Lxx = hess_fcn(x, lmbda, opt["cost_mult"])
|
395
|
+
else:
|
396
|
+
_, _, d2f = f_fcn(x, True) # cost
|
397
|
+
Lxx = d2f * opt["cost_mult"]
|
398
|
+
rz = range(len(z))
|
399
|
+
zinvdiag = c_sparse((1.0 / z, (rz, rz))) if len(z) else None
|
400
|
+
rmu = range(len(mu))
|
401
|
+
mudiag = c_sparse((mu, (rmu, rmu))) if len(mu) else None
|
402
|
+
dh_zinv = None if dh is None else dh * zinvdiag
|
403
|
+
M = Lxx if dh is None else Lxx + dh_zinv * mudiag * dh.T
|
404
|
+
N = Lx if dh is None else Lx + dh_zinv * (mudiag * h + gamma * e)
|
405
|
+
|
406
|
+
Ab = c_sparse(M) if dg is None else sp.vstack([
|
407
|
+
sp.hstack([M, dg]),
|
408
|
+
sp.hstack([dg.T, c_sparse((neq, neq))])
|
409
|
+
])
|
410
|
+
bb = np.r_[-N, -g]
|
411
|
+
|
412
|
+
dxdlam = sp.linalg.spsolve(Ab.tocsr(), bb)
|
413
|
+
|
414
|
+
if np.any(np.isnan(dxdlam)):
|
415
|
+
print('\nNumerically Failed\n')
|
416
|
+
eflag = -1
|
417
|
+
break
|
418
|
+
|
419
|
+
dx = dxdlam[:nx]
|
420
|
+
dlam = dxdlam[nx:nx + neq]
|
421
|
+
dz = -h - z if dh is None else -h - z - dh.T * dx
|
422
|
+
dmu = -mu if dh is None else -mu + zinvdiag * (gamma * e - mudiag * dz)
|
423
|
+
|
424
|
+
# optional step-size control
|
425
|
+
sc = False
|
426
|
+
if opt["step_control"]:
|
427
|
+
x1 = x + dx
|
428
|
+
|
429
|
+
# evaluate cost, constraints, derivatives at x1
|
430
|
+
f1, df1 = f_fcn(x1) # cost
|
431
|
+
f1 = f1 * opt["cost_mult"]
|
432
|
+
df1 = df1 * opt["cost_mult"]
|
433
|
+
if nonlinear:
|
434
|
+
hn1, gn1, dhn1, dgn1 = gh_fcn(x1) # nonlinear constraints
|
435
|
+
|
436
|
+
h1 = hn1 if Ai is None else np.r_[hn1, Ai * x1 - bi] # ieq constraints
|
437
|
+
g1 = gn1 if Ae is None else np.r_[gn1, Ae * x1 - be] # eq constraints
|
438
|
+
|
439
|
+
# 1st der of ieq
|
440
|
+
if (dhn1 is None) and (Ai is None):
|
441
|
+
dh1 = None
|
442
|
+
elif dhn1 is None:
|
443
|
+
dh1 = Ai.T
|
444
|
+
elif Ai is None:
|
445
|
+
dh1 = dhn1
|
446
|
+
else:
|
447
|
+
dh1 = sp.hstack([dhn1, Ai.T])
|
448
|
+
|
449
|
+
# 1st der of eqs
|
450
|
+
if (dgn1 is None) and (Ae is None):
|
451
|
+
dg1 = None
|
452
|
+
elif dgn is None:
|
453
|
+
dg1 = Ae.T
|
454
|
+
elif Ae is None:
|
455
|
+
dg1 = dgn1
|
456
|
+
else:
|
457
|
+
dg1 = sp.hstack([dgn1, Ae.T])
|
458
|
+
else:
|
459
|
+
h1 = -bi if Ai is None else Ai * x1 - bi # inequality constraints
|
460
|
+
g1 = -be if Ae is None else Ae * x1 - be # equality constraints
|
461
|
+
|
462
|
+
dh1 = dh # 1st derivative of inequalities
|
463
|
+
dg1 = dg # 1st derivative of equalities
|
464
|
+
|
465
|
+
# check tolerance
|
466
|
+
Lx1 = df1
|
467
|
+
Lx1 = Lx1 + dg1 * lam if dg1 is not None else Lx1
|
468
|
+
Lx1 = Lx1 + dh1 * mu if dh1 is not None else Lx1
|
469
|
+
|
470
|
+
maxh1 = np.zeros(1) if len(h1) == 0 else max(h1)
|
471
|
+
|
472
|
+
g1norm = np.linalg.norm(g1, inf) if len(g1) else 0.0
|
473
|
+
lam1_norm = np.linalg.norm(lam, inf) if len(lam) else 0.0
|
474
|
+
mu1_norm = np.linalg.norm(mu, inf) if len(mu) else 0.0
|
475
|
+
z1norm = np.linalg.norm(z, inf) if len(z) else 0.0
|
476
|
+
|
477
|
+
feascond1 = max([g1norm, maxh1]) / \
|
478
|
+
(1 + max([np.linalg.norm(x1, inf), z1norm]))
|
479
|
+
gradcond1 = np.linalg.norm(Lx1, inf) / (1 + max([lam1_norm, mu1_norm]))
|
480
|
+
|
481
|
+
if (feascond1 > feascond) and (gradcond1 > gradcond):
|
482
|
+
sc = True
|
483
|
+
if sc:
|
484
|
+
alpha = 1.0
|
485
|
+
for j in range(opt["max_red"]):
|
486
|
+
dx1 = alpha * dx
|
487
|
+
x1 = x + dx1
|
488
|
+
f1, _ = f_fcn(x1) # cost
|
489
|
+
f1 = f1 * opt["cost_mult"]
|
490
|
+
if nonlinear:
|
491
|
+
hn1, gn1, _, _ = gh_fcn(x1) # nonlinear constraints
|
492
|
+
h1 = hn1 if Ai is None else np.r_[hn1, Ai * x1 - bi] # inequality constraints
|
493
|
+
g1 = gn1 if Ae is None else np.r_[gn1, Ae * x1 - be] # equality constraints
|
494
|
+
else:
|
495
|
+
h1 = -bi if Ai is None else Ai * x1 - bi # inequality constraints
|
496
|
+
g1 = -be if Ae is None else Ae * x1 - be # equality constraints
|
497
|
+
|
498
|
+
L1 = f1 + np.dot(lam, g1) + np.dot(mu, h1 + z) - gamma * sum(np.log(z))
|
499
|
+
|
500
|
+
logger.info(" %3d %10.5f" % (-j, np.linalg.norm(dx1)))
|
501
|
+
|
502
|
+
rho = (L1 - L) / (np.dot(Lx, dx1) + 0.5 * np.dot(dx1, Lxx * dx1))
|
503
|
+
|
504
|
+
if (rho > rho_min) and (rho < rho_max):
|
505
|
+
break
|
506
|
+
else:
|
507
|
+
alpha = alpha / 2.0
|
508
|
+
dx = alpha * dx
|
509
|
+
dz = alpha * dz
|
510
|
+
dlam = alpha * dlam
|
511
|
+
dmu = alpha * dmu
|
512
|
+
|
513
|
+
# do the update
|
514
|
+
k = find(dz < 0.0)
|
515
|
+
alphap = min([xi * min(z[k] / -dz[k]), 1]) if len(k) else 1.0
|
516
|
+
k = find(dmu < 0.0)
|
517
|
+
alphad = min([xi * min(mu[k] / -dmu[k]), 1]) if len(k) else 1.0
|
518
|
+
x = x + alphap * dx
|
519
|
+
z = z + alphap * dz
|
520
|
+
lam = lam + alphad * dlam
|
521
|
+
mu = mu + alphad * dmu
|
522
|
+
if niq > 0:
|
523
|
+
gamma = sigma * np.dot(z, mu) / niq
|
524
|
+
|
525
|
+
# evaluate cost, constraints, derivatives
|
526
|
+
f, df = f_fcn(x) # cost
|
527
|
+
f = f * opt["cost_mult"]
|
528
|
+
df = df * opt["cost_mult"]
|
529
|
+
if nonlinear:
|
530
|
+
hn, gn, dhn, dgn = gh_fcn(x) # nln constraints
|
531
|
+
h = hn if Ai is None else np.r_[hn, Ai * x - bi] # ieq constr
|
532
|
+
g = gn if Ae is None else np.r_[gn, Ae * x - be] # eq constr
|
533
|
+
|
534
|
+
if (dhn is None) and (Ai is None):
|
535
|
+
dh = None
|
536
|
+
elif dhn is None:
|
537
|
+
dh = Ai.T
|
538
|
+
elif Ai is None:
|
539
|
+
dh = dhn
|
540
|
+
else:
|
541
|
+
dh = sp.hstack([dhn, Ai.T])
|
542
|
+
|
543
|
+
if (dgn is None) and (Ae is None):
|
544
|
+
dg = None
|
545
|
+
elif dgn is None:
|
546
|
+
dg = Ae.T
|
547
|
+
elif Ae is None:
|
548
|
+
dg = dgn
|
549
|
+
else:
|
550
|
+
dg = sp.hstack([dgn, Ae.T])
|
551
|
+
else:
|
552
|
+
h = -bi if Ai is None else Ai * x - bi # inequality constraints
|
553
|
+
g = -be if Ae is None else Ae * x - be # equality constraints
|
554
|
+
# 1st derivatives are constant, still dh = Ai.T, dg = Ae.T
|
555
|
+
|
556
|
+
Lx = df
|
557
|
+
Lx = Lx + dg * lam if dg is not None else Lx
|
558
|
+
Lx = Lx + dh * mu if dh is not None else Lx
|
559
|
+
|
560
|
+
if len(h) == 0:
|
561
|
+
maxh = np.zeros(1)
|
562
|
+
else:
|
563
|
+
maxh = max(h)
|
564
|
+
|
565
|
+
gnorm = np.linalg.norm(g, inf) if len(g) else 0.0
|
566
|
+
lam_norm = np.linalg.norm(lam, inf) if len(lam) else 0.0
|
567
|
+
mu_norm = np.linalg.norm(mu, inf) if len(mu) else 0.0
|
568
|
+
znorm = np.linalg.norm(z, inf) if len(z) else 0.0
|
569
|
+
feascond = \
|
570
|
+
max([gnorm, maxh]) / (1 + max([np.linalg.norm(x, inf), znorm]))
|
571
|
+
gradcond = \
|
572
|
+
np.linalg.norm(Lx, inf) / (1 + max([lam_norm, mu_norm]))
|
573
|
+
compcond = np.dot(z, mu) / (1 + np.linalg.norm(x, inf))
|
574
|
+
costcond = float(np.abs(f - f0) / (1 + np.abs(f0)))
|
575
|
+
|
576
|
+
hist.append({'feascond': feascond, 'gradcond': gradcond,
|
577
|
+
'compcond': compcond, 'costcond': costcond, 'gamma': gamma,
|
578
|
+
'stepsize': np.linalg.norm(dx), 'obj': f / opt["cost_mult"],
|
579
|
+
'alphap': alphap, 'alphad': alphad})
|
580
|
+
|
581
|
+
logger.debug("%3d %12.8g %10.5g %12g %12g %12g %12g" %
|
582
|
+
(i, (f / opt["cost_mult"]), np.linalg.norm(dx), feascond, gradcond,
|
583
|
+
compcond, costcond))
|
584
|
+
|
585
|
+
if feascond < opt["feastol"] and gradcond < opt["gradtol"] and \
|
586
|
+
compcond < opt["comptol"] and costcond < opt["costtol"]:
|
587
|
+
converged = True
|
588
|
+
else:
|
589
|
+
if np.any(np.isnan(x)) or (alphap < alpha_min) or \
|
590
|
+
(alphad < alpha_min) or (gamma < EPS) or (gamma > 1.0 / EPS):
|
591
|
+
eflag = -1
|
592
|
+
break
|
593
|
+
f0 = f
|
594
|
+
|
595
|
+
if opt["step_control"]:
|
596
|
+
L = f + np.dot(lam, g) + np.dot(mu, (h + z)) - gamma * sum(np.log(z))
|
597
|
+
|
598
|
+
if not converged:
|
599
|
+
logger.debug("Did not converge in %d iterations." % i)
|
600
|
+
|
601
|
+
# package results
|
602
|
+
if eflag != -1:
|
603
|
+
eflag = converged
|
604
|
+
|
605
|
+
if eflag == 0:
|
606
|
+
message = 'Did not converge'
|
607
|
+
elif eflag == 1:
|
608
|
+
message = 'Converged'
|
609
|
+
elif eflag == -1:
|
610
|
+
message = 'Numerically failed'
|
611
|
+
else:
|
612
|
+
raise
|
613
|
+
|
614
|
+
output = {"iterations": i, "hist": hist, "message": message}
|
615
|
+
|
616
|
+
# zero out multipliers on non-binding constraints
|
617
|
+
mu[find((h < -opt["feastol"]) & (mu < mu_threshold))] = 0.0
|
618
|
+
|
619
|
+
# un-scale cost and prices
|
620
|
+
f = f / opt["cost_mult"]
|
621
|
+
lam = lam / opt["cost_mult"]
|
622
|
+
mu = mu / opt["cost_mult"]
|
623
|
+
|
624
|
+
# re-package multipliers into struct
|
625
|
+
lam_lin = lam[neqnln:neq] # lambda for linear constraints
|
626
|
+
mu_lin = mu[niqnln:niq] # mu for linear constraints
|
627
|
+
kl = find(lam_lin < 0.0) # lower bound binding
|
628
|
+
ku = find(lam_lin > 0.0) # upper bound binding
|
629
|
+
|
630
|
+
mu_l = np.zeros(nx + nA)
|
631
|
+
mu_l[ieq[kl]] = -lam_lin[kl]
|
632
|
+
mu_l[igt] = mu_lin[nlt:nlt + ngt]
|
633
|
+
mu_l[ibx] = mu_lin[nlt + ngt + nbx:nlt + ngt + nbx + nbx]
|
634
|
+
|
635
|
+
mu_u = np.zeros(nx + nA)
|
636
|
+
mu_u[ieq[ku]] = lam_lin[ku]
|
637
|
+
mu_u[ilt] = mu_lin[:nlt]
|
638
|
+
mu_u[ibx] = mu_lin[nlt + ngt:nlt + ngt + nbx]
|
639
|
+
|
640
|
+
lmbda = {'mu_l': mu_l[nx:], 'mu_u': mu_u[nx:],
|
641
|
+
'lower': mu_l[:nx], 'upper': mu_u[:nx]}
|
642
|
+
|
643
|
+
if niqnln > 0:
|
644
|
+
lmbda['ineqnonlin'] = mu[:niqnln]
|
645
|
+
if neqnln > 0:
|
646
|
+
lmbda['eqnonlin'] = lam[:neqnln]
|
647
|
+
|
648
|
+
# lmbda = {"eqnonlin": lam[:neqnln], 'ineqnonlin': mu[:niqnln],
|
649
|
+
# "mu_l": mu_l[nx:], "mu_u": mu_u[nx:],
|
650
|
+
# "lower": mu_l[:nx], "upper": mu_u[:nx]}
|
651
|
+
|
652
|
+
solution = {"x": x, "f": f, "eflag": converged,
|
653
|
+
"output": output, "lmbda": lmbda}
|
654
|
+
|
655
|
+
return solution
|
656
|
+
|
657
|
+
|
658
|
+
def pipsver(*args):
|
659
|
+
"""
|
660
|
+
Return PIPS version information for the current installation.
|
661
|
+
|
662
|
+
Author: Ray Zimmerman (PSERC Cornell)
|
663
|
+
|
664
|
+
Returns
|
665
|
+
-------
|
666
|
+
dict
|
667
|
+
A dictionary containing PIPS version information with the following keys:
|
668
|
+
- 'Name': Name of the software (PIPS)
|
669
|
+
- 'Version': Version number (1.0)
|
670
|
+
- 'Release': Release information (empty string)
|
671
|
+
- 'Date': Date of release (07-Feb-2011)
|
672
|
+
|
673
|
+
Author
|
674
|
+
------
|
675
|
+
Ray Zimmerman (PSERC Cornell)
|
676
|
+
"""
|
677
|
+
ver = {'Name': 'PIPS',
|
678
|
+
'Version': '1.0',
|
679
|
+
'Release': '',
|
680
|
+
'Date': '07-Feb-2011'}
|
681
|
+
|
682
|
+
return ver
|
683
|
+
|
684
|
+
|
685
|
+
def pipsopf_solver(om, ppopt, out_opt=None):
|
686
|
+
"""
|
687
|
+
Solves AC optimal power flow using PIPS.
|
688
|
+
|
689
|
+
Inputs are an OPF model object, a PYPOWER options vector, and
|
690
|
+
a dict containing keys (can be empty) for each of the desired
|
691
|
+
optional output fields.
|
692
|
+
|
693
|
+
Outputs are a `results` dict, a `success` flag, and a `raw` output dict.
|
694
|
+
|
695
|
+
Parameters
|
696
|
+
----------
|
697
|
+
om : object
|
698
|
+
OPF model object.
|
699
|
+
ppopt : dict
|
700
|
+
PYPOWER options vector.
|
701
|
+
out_opt : dict, optional
|
702
|
+
Dictionary containing keys for optional output fields (default is None).
|
703
|
+
|
704
|
+
Returns
|
705
|
+
-------
|
706
|
+
dict
|
707
|
+
A `results` dictionary containing various fields, including optimization
|
708
|
+
variables, objective function value, and shadow prices on constraints.
|
709
|
+
bool
|
710
|
+
A `success` flag indicating whether the solver converged successfully.
|
711
|
+
dict
|
712
|
+
A `raw` output dictionary in the form returned by MINOS, containing
|
713
|
+
information about optimization variables, constraint multipliers, solver
|
714
|
+
termination code, and solver-specific output information.
|
715
|
+
|
716
|
+
Notes
|
717
|
+
-----
|
718
|
+
The `results` dictionary contains fields such as 'order', 'x', 'f', 'mu',
|
719
|
+
where 'mu' includes shadow prices on variables and constraints.
|
720
|
+
|
721
|
+
The `success` flag is `True` if the solver converged successfully, and `False`
|
722
|
+
otherwise.
|
723
|
+
|
724
|
+
The `raw` output dictionary includes information specific to the solver.
|
725
|
+
|
726
|
+
Author
|
727
|
+
------
|
728
|
+
Ray Zimmerman (PSERC Cornell)
|
729
|
+
|
730
|
+
Carlos E. Murillo-Sanchez (PSERC Cornell & Universidad Autonoma de Manizales)
|
731
|
+
"""
|
732
|
+
# ----- initialization -----
|
733
|
+
# optional output
|
734
|
+
if out_opt is None:
|
735
|
+
out_opt = {}
|
736
|
+
|
737
|
+
# options
|
738
|
+
verbose = ppopt['VERBOSE']
|
739
|
+
feastol = ppopt['PDIPM_FEASTOL']
|
740
|
+
gradtol = ppopt['PDIPM_GRADTOL']
|
741
|
+
comptol = ppopt['PDIPM_COMPTOL']
|
742
|
+
costtol = ppopt['PDIPM_COSTTOL']
|
743
|
+
max_it = ppopt['PDIPM_MAX_IT']
|
744
|
+
max_red = ppopt['SCPDIPM_RED_IT']
|
745
|
+
step_control = (ppopt['OPF_ALG'] == 565) # OPF_ALG == 565, PIPS-sc
|
746
|
+
if feastol == 0:
|
747
|
+
feastol = ppopt['OPF_VIOLATION']
|
748
|
+
opt = {'feastol': feastol,
|
749
|
+
'gradtol': gradtol,
|
750
|
+
'comptol': comptol,
|
751
|
+
'costtol': costtol,
|
752
|
+
'max_it': max_it,
|
753
|
+
'max_red': max_red,
|
754
|
+
'step_control': step_control,
|
755
|
+
'cost_mult': 1e-4,
|
756
|
+
'verbose': verbose}
|
757
|
+
|
758
|
+
# unpack data
|
759
|
+
ppc = om.get_ppc()
|
760
|
+
baseMVA, bus, gen, branch, gencost = \
|
761
|
+
ppc["baseMVA"], ppc["bus"], ppc["gen"], ppc["branch"], ppc["gencost"]
|
762
|
+
vv, _, nn, _ = om.get_idx()
|
763
|
+
|
764
|
+
# problem dimensions
|
765
|
+
nb = bus.shape[0] # number of buses
|
766
|
+
nl = branch.shape[0] # number of branches
|
767
|
+
ny = om.getN('var', 'y') # number of piece-wise linear costs
|
768
|
+
|
769
|
+
# linear constraints
|
770
|
+
A, l, u = om.linear_constraints()
|
771
|
+
|
772
|
+
# bounds on optimization vars
|
773
|
+
_, xmin, xmax = om.getv()
|
774
|
+
|
775
|
+
# build admittance matrices
|
776
|
+
Ybus, Yf, Yt = makeYbus(baseMVA, bus, branch)
|
777
|
+
|
778
|
+
# try to select an interior initial point
|
779
|
+
ll, uu = xmin.copy(), xmax.copy()
|
780
|
+
ll[xmin == -inf] = -1e10 # replace Inf with numerical proxies
|
781
|
+
uu[xmax == inf] = 1e10
|
782
|
+
x0 = (ll + uu) / 2
|
783
|
+
Varefs = bus[bus[:, IDX.bus.BUS_TYPE] == IDX.bus.REF, IDX.bus.VA] * deg2rad
|
784
|
+
# angles set to first reference angle
|
785
|
+
x0[vv["i1"]["Va"]:vv["iN"]["Va"]] = Varefs[0]
|
786
|
+
if ny > 0:
|
787
|
+
ipwl = find(gencost[:, IDX.cost.MODEL] == IDX.cost.PW_LINEAR)
|
788
|
+
# PQ = np.r_[gen[:, PMAX], gen[:, QMAX]]
|
789
|
+
# c = totcost(gencost[ipwl, :], PQ[ipwl])
|
790
|
+
# largest y-value in CCV data
|
791
|
+
c = gencost.flatten('F')[sub2ind(gencost.shape, ipwl, IDX.cost.NCOST+2*gencost[ipwl, IDX.cost.NCOST])]
|
792
|
+
x0[vv["i1"]["y"]:vv["iN"]["y"]] = max(c) + 0.1 * abs(max(c))
|
793
|
+
# x0[vv["i1"]["y"]:vv["iN"]["y"]] = c + 0.1 * abs(c)
|
794
|
+
|
795
|
+
# find branches with flow limits
|
796
|
+
il = find((branch[:, IDX.branch.RATE_A] != 0) & (branch[:, IDX.branch.RATE_A] < 1e10))
|
797
|
+
nl2 = len(il) # number of constrained lines
|
798
|
+
|
799
|
+
# ----- run opf -----
|
800
|
+
def f_fcn(x, return_hessian=False): return opf_costfcn(x, om, return_hessian)
|
801
|
+
def gh_fcn(x): return opf_consfcn(x, om, Ybus, Yf[il, :], Yt[il, :], ppopt, il)
|
802
|
+
|
803
|
+
def hess_fcn(
|
804
|
+
x, lmbda, cost_mult): return opf_hessfcn(
|
805
|
+
x, lmbda, om, Ybus, Yf[il, :],
|
806
|
+
Yt[il, :],
|
807
|
+
ppopt, il, cost_mult)
|
808
|
+
|
809
|
+
solution = pips(f_fcn, x0, A, l, u, xmin, xmax, gh_fcn, hess_fcn, opt)
|
810
|
+
x, f, info, lmbda, output = solution["x"], solution["f"], \
|
811
|
+
solution["eflag"], solution["lmbda"], solution["output"]
|
812
|
+
|
813
|
+
success = (info > 0)
|
814
|
+
|
815
|
+
# update solution data
|
816
|
+
Va = x[vv["i1"]["Va"]:vv["iN"]["Va"]]
|
817
|
+
Vm = x[vv["i1"]["Vm"]:vv["iN"]["Vm"]]
|
818
|
+
Pg = x[vv["i1"]["Pg"]:vv["iN"]["Pg"]]
|
819
|
+
Qg = x[vv["i1"]["Qg"]:vv["iN"]["Qg"]]
|
820
|
+
|
821
|
+
V = Vm * np.exp(1j * Va)
|
822
|
+
|
823
|
+
# ----- calculate return values -----
|
824
|
+
# update voltages & generator outputs
|
825
|
+
bus[:, IDX.bus.VA] = Va * rad2deg
|
826
|
+
|
827
|
+
bus[:, IDX.bus.VM] = Vm
|
828
|
+
gen[:, IDX.gen.PG] = Pg * baseMVA
|
829
|
+
gen[:, IDX.gen.QG] = Qg * baseMVA
|
830
|
+
gen[:, IDX.gen.VG] = Vm[gen[:, IDX.gen.GEN_BUS].astype(int)]
|
831
|
+
|
832
|
+
# compute branch flows
|
833
|
+
Sf = V[branch[:, IDX.branch.F_BUS].astype(int)] * np.conj(Yf * V) # cplx pwr at "from" bus, p["u"].
|
834
|
+
St = V[branch[:, IDX.branch.T_BUS].astype(int)] * np.conj(Yt * V) # cplx pwr at "to" bus, p["u"].
|
835
|
+
branch[:, IDX.branch.PF] = Sf.real * baseMVA
|
836
|
+
branch[:, IDX.branch.QF] = Sf.imag * baseMVA
|
837
|
+
branch[:, IDX.branch.PT] = St.real * baseMVA
|
838
|
+
branch[:, IDX.branch.QT] = St.imag * baseMVA
|
839
|
+
|
840
|
+
# line constraint is actually on square of limit
|
841
|
+
# so we must fix multipliers
|
842
|
+
muSf = np.zeros(nl)
|
843
|
+
muSt = np.zeros(nl)
|
844
|
+
if len(il) > 0:
|
845
|
+
muSf[il] = \
|
846
|
+
2 * lmbda["ineqnonlin"][:nl2] * branch[il, IDX.branch.RATE_A] / baseMVA
|
847
|
+
muSt[il] = \
|
848
|
+
2 * lmbda["ineqnonlin"][nl2:nl2+nl2] * branch[il, IDX.branch.RATE_A] / baseMVA
|
849
|
+
|
850
|
+
# update Lagrange multipliers
|
851
|
+
bus[:, IDX.bus.MU_VMAX] = lmbda["upper"][vv["i1"]["Vm"]:vv["iN"]["Vm"]]
|
852
|
+
bus[:, IDX.bus.MU_VMIN] = lmbda["lower"][vv["i1"]["Vm"]:vv["iN"]["Vm"]]
|
853
|
+
gen[:, IDX.gen.MU_PMAX] = lmbda["upper"][vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA
|
854
|
+
gen[:, IDX.gen.MU_PMIN] = lmbda["lower"][vv["i1"]["Pg"]:vv["iN"]["Pg"]] / baseMVA
|
855
|
+
gen[:, IDX.gen.MU_QMAX] = lmbda["upper"][vv["i1"]["Qg"]:vv["iN"]["Qg"]] / baseMVA
|
856
|
+
gen[:, IDX.gen.MU_QMIN] = lmbda["lower"][vv["i1"]["Qg"]:vv["iN"]["Qg"]] / baseMVA
|
857
|
+
|
858
|
+
bus[:, IDX.bus.LAM_P] = \
|
859
|
+
lmbda["eqnonlin"][nn["i1"]["Pmis"]:nn["iN"]["Pmis"]] / baseMVA
|
860
|
+
bus[:, IDX.bus.LAM_Q] = \
|
861
|
+
lmbda["eqnonlin"][nn["i1"]["Qmis"]:nn["iN"]["Qmis"]] / baseMVA
|
862
|
+
branch[:, IDX.branch.MU_SF] = muSf / baseMVA
|
863
|
+
branch[:, IDX.branch.MU_ST] = muSt / baseMVA
|
864
|
+
|
865
|
+
# package up results
|
866
|
+
nlnN = om.getN('nln')
|
867
|
+
|
868
|
+
# extract multipliers for nonlinear constraints
|
869
|
+
kl = find(lmbda["eqnonlin"] < 0)
|
870
|
+
ku = find(lmbda["eqnonlin"] > 0)
|
871
|
+
nl_mu_l = np.zeros(nlnN)
|
872
|
+
nl_mu_u = np.r_[np.zeros(2*nb), muSf, muSt]
|
873
|
+
nl_mu_l[kl] = -lmbda["eqnonlin"][kl]
|
874
|
+
nl_mu_u[ku] = lmbda["eqnonlin"][ku]
|
875
|
+
|
876
|
+
mu = {
|
877
|
+
'var': {'l': lmbda["lower"], 'u': lmbda["upper"]},
|
878
|
+
'nln': {'l': nl_mu_l, 'u': nl_mu_u},
|
879
|
+
'lin': {'l': lmbda["mu_l"], 'u': lmbda["mu_u"]}}
|
880
|
+
|
881
|
+
results = ppc
|
882
|
+
results["bus"], results["branch"], results["gen"], \
|
883
|
+
results["om"], results["x"], results["mu"], results["f"] = \
|
884
|
+
bus, branch, gen, om, x, mu, f
|
885
|
+
|
886
|
+
pimul = np.r_[
|
887
|
+
results["mu"]["nln"]["l"] - results["mu"]["nln"]["u"],
|
888
|
+
results["mu"]["lin"]["l"] - results["mu"]["lin"]["u"],
|
889
|
+
-np.ones(int(ny > 0)),
|
890
|
+
results["mu"]["var"]["l"] - results["mu"]["var"]["u"],
|
891
|
+
]
|
892
|
+
raw = {'xr': x, 'pimul': pimul, 'info': info, 'output': output}
|
893
|
+
|
894
|
+
return results, success, raw
|