ltbams 0.9.9__py3-none-any.whl → 1.0.2__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 +206 -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 +231 -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.2.dist-info/METADATA +215 -0
- ltbams-1.0.2.dist-info/RECORD +188 -0
- {ltbams-0.9.9.dist-info → ltbams-1.0.2.dist-info}/WHEEL +1 -1
- ltbams-1.0.2.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.2.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,513 @@
|
|
1
|
+
"""
|
2
|
+
Runs a full AC continuation power flow.
|
3
|
+
"""
|
4
|
+
# --- run cpf ---
|
5
|
+
import logging
|
6
|
+
|
7
|
+
import numpy as np
|
8
|
+
from numpy import flatnonzero as find
|
9
|
+
|
10
|
+
import scipy.sparse as sp
|
11
|
+
|
12
|
+
from andes.shared import deg2rad
|
13
|
+
from andes.utils.misc import elapsed
|
14
|
+
|
15
|
+
import ams.pypower.utils as putil
|
16
|
+
import ams.pypower.io as pio
|
17
|
+
import ams.pypower.routines.opffcns as opfcn
|
18
|
+
from ams.pypower.idx import IDX
|
19
|
+
from ams.pypower.make import (makeSbus, makeYbus, dSbus_dV)
|
20
|
+
from ams.pypower.routines.pflow import newtonpf, pfsoln
|
21
|
+
from ams.pypower.core import ppoption
|
22
|
+
import ams.pypower.routines.cpf_callbacks as cpf_callbacks
|
23
|
+
|
24
|
+
from ams.shared import inf, nan
|
25
|
+
|
26
|
+
logger = logging.getLogger(__name__)
|
27
|
+
|
28
|
+
|
29
|
+
def runcpf(casedata, ppopt=None, scale=1.2):
|
30
|
+
"""
|
31
|
+
Runs a full AC continuation power flow.
|
32
|
+
"""
|
33
|
+
ppopt = ppoption(ppopt)
|
34
|
+
|
35
|
+
# options
|
36
|
+
verbose = ppopt["VERBOSE"]
|
37
|
+
step = ppopt["CPF_STEP"]
|
38
|
+
parameterization = ppopt["CPF_PARAMETERIZATION"]
|
39
|
+
adapt_step = ppopt["CPF_ADAPT_STEP"]
|
40
|
+
cb_args = ppopt["CPF_USER_CALLBACK_ARGS"]
|
41
|
+
|
42
|
+
# set up callbacks
|
43
|
+
callback_names = ["cpf_default_callback"]
|
44
|
+
if len(ppopt["CPF_USER_CALLBACK"]) > 0:
|
45
|
+
if isinstance(ppopt["CPF_USER_CALLBACK"], list):
|
46
|
+
callback_names = np.r_[callback_names, ppopt["CPF_USER_CALLBACK"]]
|
47
|
+
else:
|
48
|
+
callback_names.append(ppopt["CPF_USER_CALLBACK"])
|
49
|
+
callbacks = []
|
50
|
+
for callback_name in callback_names:
|
51
|
+
callbacks.append(getattr(cpf_callbacks, callback_name))
|
52
|
+
|
53
|
+
# read base case data
|
54
|
+
ppcbase = pio.loadcase(casedata)
|
55
|
+
nb = ppcbase["bus"].shape[0]
|
56
|
+
|
57
|
+
# add zero columns to branch for flows if needed
|
58
|
+
if ppcbase["branch"].shape[1] < IDX.branch.QT:
|
59
|
+
ppcbase["branch"] = np.c_[ppcbase["branch"],
|
60
|
+
np.zeros((ppcbase["branch"].shape[0],
|
61
|
+
IDX.branch.QT - ppcbase["branch"].shape[1] + 1))]
|
62
|
+
|
63
|
+
# convert to internal indexing
|
64
|
+
ppcbase = opfcn.ext2int(ppcbase)
|
65
|
+
baseMVAb, busb, genb, branchb = \
|
66
|
+
ppcbase["baseMVA"], ppcbase["bus"], ppcbase["gen"], ppcbase["branch"]
|
67
|
+
|
68
|
+
# get bus index lists of each type of bus
|
69
|
+
ref, pv, pq = putil.bustypes(busb, genb)
|
70
|
+
|
71
|
+
# generator info
|
72
|
+
onb = find(genb[:, IDX.gen.GEN_STATUS] > 0) # which generators are on?
|
73
|
+
gbusb = genb[onb, IDX.gen.GEN_BUS].astype(int) # what buses are they at?
|
74
|
+
|
75
|
+
# scale the load and generation of base case as target case
|
76
|
+
ppctarget = pio.loadcase(casedata)
|
77
|
+
ppctarget["bus"][:, IDX.bus.PD] *= scale
|
78
|
+
ppctarget["bus"][:, IDX.bus.QD] *= scale
|
79
|
+
ppctarget["gen"][:, IDX.gen.PG] *= scale
|
80
|
+
ppctarget["gen"][:, IDX.gen.QG] *= scale
|
81
|
+
|
82
|
+
# add zero columns to branch for flows if needed
|
83
|
+
if ppctarget["branch"].shape[1] < IDX.branch.QT:
|
84
|
+
ppctarget["branch"] = np.c_[ppctarget["branch"],
|
85
|
+
np.zeros((ppctarget["branch"].shape[0],
|
86
|
+
IDX.branch.QT - ppctarget["branch"].shape[1] + 1))]
|
87
|
+
|
88
|
+
# convert to internal indexing
|
89
|
+
ppctarget = opfcn.ext2int(ppctarget)
|
90
|
+
baseMVAt, bust, gent, brancht = \
|
91
|
+
ppctarget["baseMVA"], ppctarget["bus"], ppctarget["gen"], ppctarget["branch"]
|
92
|
+
|
93
|
+
# get bus index lists of each type of bus
|
94
|
+
# ref, pv, pq = putil.bustypes(bust, gent)
|
95
|
+
|
96
|
+
# generator info
|
97
|
+
ont = find(gent[:, IDX.gen.GEN_STATUS] > 0) # which generators are on?
|
98
|
+
# gbust = gent[ont, IDX.gen.GEN_BUS].astype(int) # what buses are they at?
|
99
|
+
|
100
|
+
# ----- run the power flow -----
|
101
|
+
t0, _ = elapsed() # start timer
|
102
|
+
|
103
|
+
# initial state
|
104
|
+
# V0 = np.ones((bus.shape[0]) ## flat start
|
105
|
+
V0 = busb[:, IDX.bus.VM] * np.exp((1j * deg2rad * busb[:, IDX.bus.VA]))
|
106
|
+
vcb = np.ones(V0.shape) # create mask of voltage-controlled buses
|
107
|
+
vcb[pq] = 0 # exclude PQ buses
|
108
|
+
k = find(vcb[gbusb]) # in-service gens at v-c buses
|
109
|
+
V0[gbusb[k]] = genb[onb[k], IDX.gen.VG] / abs(V0[gbusb[k]]) * V0[gbusb[k]]
|
110
|
+
|
111
|
+
# build admittance matrices
|
112
|
+
Ybus, Yf, Yt = makeYbus(baseMVAb, busb, branchb)
|
113
|
+
|
114
|
+
# compute base case complex bus power injections (generation - load)
|
115
|
+
Sbusb = makeSbus(baseMVAb, busb, genb)
|
116
|
+
# compute target case complex bus power injections (generation - load)
|
117
|
+
Sbust = makeSbus(baseMVAt, bust, gent)
|
118
|
+
|
119
|
+
# scheduled transfer
|
120
|
+
Sxfr = Sbust - Sbusb
|
121
|
+
|
122
|
+
# Run the base case power flow solution
|
123
|
+
lam = 0
|
124
|
+
logger.info('Solve base case power flow')
|
125
|
+
V, success, iterations = newtonpf(Ybus, Sbusb, V0, ref, pv, pq, ppopt)
|
126
|
+
|
127
|
+
logger.info('Start full AC CPF')
|
128
|
+
logger.info('%2d: lambda = %6.3f, %2d Newton steps' % (0, 0, iterations))
|
129
|
+
|
130
|
+
lamprv = lam # lam at previous step
|
131
|
+
Vprv = V # V at previous step
|
132
|
+
continuation = 1
|
133
|
+
cont_steps = 0
|
134
|
+
|
135
|
+
# input args for callbacks
|
136
|
+
cb_data = dict(ppc_base=ppcbase, ppc_target=ppctarget,
|
137
|
+
Sxfr=Sxfr, Ybus=Ybus, Yf=Yf, Yt=Yt,
|
138
|
+
ref=ref, pv=pv, pq=pq, ppopt=ppopt)
|
139
|
+
cb_state = {}
|
140
|
+
|
141
|
+
# invoke callbacks
|
142
|
+
for k in range(len(callbacks)):
|
143
|
+
cb_state, _ = callbacks[k](cont_steps, V, lam, V, lam,
|
144
|
+
cb_data, cb_state, cb_args)
|
145
|
+
|
146
|
+
if np.linalg.norm(Sxfr) == 0:
|
147
|
+
logger.error('base case and target case have identical load and generation!')
|
148
|
+
|
149
|
+
continuation = 0
|
150
|
+
V0 = V
|
151
|
+
lam0 = lam
|
152
|
+
|
153
|
+
# tangent predictor z = [dx;dlam]
|
154
|
+
z = np.zeros(2*len(V)+1)
|
155
|
+
z[-1] = 1.0
|
156
|
+
while continuation:
|
157
|
+
cont_steps = cont_steps + 1
|
158
|
+
# prediction for next step
|
159
|
+
V0, lam0, z = cpf_predictor(V, lam, Ybus, Sxfr, pv, pq, step, z,
|
160
|
+
Vprv, lamprv, parameterization)
|
161
|
+
|
162
|
+
# save previous voltage, lambda before updating
|
163
|
+
Vprv = V
|
164
|
+
lamprv = lam
|
165
|
+
|
166
|
+
# correction
|
167
|
+
V, success, i, lam = cpf_corrector(Ybus, Sbusb, V0, ref, pv, pq,
|
168
|
+
lam0, Sxfr, Vprv, lamprv, z,
|
169
|
+
step, parameterization, ppopt)
|
170
|
+
|
171
|
+
if not success:
|
172
|
+
continuation = 0
|
173
|
+
logger.warning('%2d: lambda = %6.3f, corrector did not converge in %d iterations' % (
|
174
|
+
cont_steps, lam, i))
|
175
|
+
break
|
176
|
+
|
177
|
+
logger.info('%2d: lambda = %6.3f, %2d corrector Newton steps' %
|
178
|
+
(cont_steps, lam, i))
|
179
|
+
|
180
|
+
# invoke callbacks
|
181
|
+
for k in range(len(callbacks)):
|
182
|
+
cb_state, _ = callbacks[k](cont_steps, V, lam, V0, lam0,
|
183
|
+
cb_data, cb_state, cb_args)
|
184
|
+
|
185
|
+
if isinstance(ppopt["CPF_STOP_AT"], str):
|
186
|
+
if ppopt["CPF_STOP_AT"].upper() == "FULL":
|
187
|
+
if abs(lam) < 1e-8: # traced the full continuation curve
|
188
|
+
logger.info('Reached steady state loading limit in %d continuation steps' % cont_steps)
|
189
|
+
continuation = 0
|
190
|
+
elif lam < lamprv and lam - step < 0: # next step will overshoot
|
191
|
+
step = lam # modify step-size
|
192
|
+
parameterization = 1 # change to natural parameterization
|
193
|
+
adapt_step = False # disable step-adaptivity
|
194
|
+
|
195
|
+
else: # == 'NOSE'
|
196
|
+
if lam < lamprv: # reached the nose point
|
197
|
+
logger.info('Reached steady state loading limit in %d continuation steps' % cont_steps)
|
198
|
+
continuation = 0
|
199
|
+
|
200
|
+
else:
|
201
|
+
if lam < lamprv:
|
202
|
+
logger.info('Reached steady state loading limit in %d continuation steps' % cont_steps)
|
203
|
+
continuation = 0
|
204
|
+
elif abs(ppopt["CPF_STOP_AT"] - lam) < 1e-8: # reached desired lambda
|
205
|
+
logger.info('Reached desired lambda %3.2f in %d continuation steps' % (
|
206
|
+
ppopt["CPF_STOP_AT"], cont_steps))
|
207
|
+
continuation = 0
|
208
|
+
# will reach desired lambda in next step
|
209
|
+
elif lam + step > ppopt["CPF_STOP_AT"]:
|
210
|
+
step = ppopt["CPF_STOP_AT"] - lam # modify step-size
|
211
|
+
parameterization = 1 # change to natural parameterization
|
212
|
+
adapt_step = False # disable step-adaptivity
|
213
|
+
|
214
|
+
if adapt_step and continuation:
|
215
|
+
pvpq = np.r_[pv, pq]
|
216
|
+
# Adapt stepsize
|
217
|
+
cpf_error = np.linalg.norm(np.r_[np.angle(V[pq]), abs(
|
218
|
+
V[pvpq]), lam] - np.r_[np.angle(V0[pq]), abs(V0[pvpq]), lam0], inf)
|
219
|
+
if cpf_error < ppopt["CPF_ERROR_TOL"]:
|
220
|
+
# Increase stepsize
|
221
|
+
step = step * ppopt["CPF_ERROR_TOL"] / cpf_error
|
222
|
+
if step > ppopt["CPF_STEP_MAX"]:
|
223
|
+
step = ppopt["CPF_STEP_MAX"]
|
224
|
+
else:
|
225
|
+
# decrese stepsize
|
226
|
+
step = step * ppopt["CPF_ERROR_TOL"] / cpf_error
|
227
|
+
if step < ppopt["CPF_STEP_MIN"]:
|
228
|
+
step = ppopt["CPF_STEP_MIN"]
|
229
|
+
|
230
|
+
# invoke callbacks
|
231
|
+
if success:
|
232
|
+
cpf_results = {}
|
233
|
+
for k in range(len(callbacks)):
|
234
|
+
cb_state, cpf_results = callbacks[k](cont_steps, V, lam, V0, lam0,
|
235
|
+
cb_data, cb_state, cb_args, results=cpf_results, is_final=True)
|
236
|
+
else:
|
237
|
+
cpf_results = {}
|
238
|
+
cpf_results["iterations"] = i
|
239
|
+
|
240
|
+
# update bus and gen matrices to reflect the loading and generation
|
241
|
+
# at the noise point
|
242
|
+
bust[:, IDX.bus.PD] = busb[:, IDX.bus.PD] + lam * (bust[:, IDX.bus.PD] - busb[:, IDX.bus.PD])
|
243
|
+
bust[:, IDX.bus.QD] = busb[:, IDX.bus.QD] + lam * (bust[:, IDX.bus.QD] - busb[:, IDX.bus.QD])
|
244
|
+
gent[:, IDX.gen.PG] = genb[:, IDX.gen.PG] + lam * (gent[:, IDX.gen.PG] - genb[:, IDX.gen.PG])
|
245
|
+
|
246
|
+
# update data matrices with solution
|
247
|
+
bust, gent, brancht = pfsoln(baseMVAt, bust, gent, brancht,
|
248
|
+
Ybus, Yf, Yt, V, ref, pv, pq)
|
249
|
+
|
250
|
+
ppctarget["et"] = elapsed(t0)
|
251
|
+
ppctarget["success"] = success
|
252
|
+
|
253
|
+
# ----- output results -----
|
254
|
+
# convert back to original bus numbering & print results
|
255
|
+
ppctarget["bus"], ppctarget["gen"], ppctarget["branch"] = bust, gent, brancht
|
256
|
+
if success:
|
257
|
+
n = cpf_results["iterations"] + 1
|
258
|
+
cpf_results["V_p"] = opfcn.i2e_data(
|
259
|
+
ppctarget, cpf_results["V_p"], np.full((nb, n), nan), "bus", 0)
|
260
|
+
cpf_results["V_c"] = opfcn.i2e_data(
|
261
|
+
ppctarget, cpf_results["V_c"], np.full((nb, n), nan), "bus", 0)
|
262
|
+
results = opfcn.int2ext(ppctarget)
|
263
|
+
results["cpf"] = cpf_results
|
264
|
+
|
265
|
+
# zero out result fields of out-of-service gens & branches
|
266
|
+
if len(results["order"]["gen"]["status"]["off"]) > 0:
|
267
|
+
results["gen"][np.ix_(results["order"]["gen"]
|
268
|
+
["status"]["off"], [IDX.gen.QF, IDX.gen.QG])] = 0
|
269
|
+
|
270
|
+
if len(results["order"]["branch"]["status"]["off"]) > 0:
|
271
|
+
results["branch"][np.ix_(results["order"]["branch"]
|
272
|
+
["status"]["off"], [IDX.branch.PF, IDX.branch.QF, IDX.branch.PT, IDX.branch.QT])] = 0
|
273
|
+
|
274
|
+
if ppopt["CPF_PLOT_LEVEL"]:
|
275
|
+
import matplotlib.pyplot as plt
|
276
|
+
plt.show()
|
277
|
+
sstats = dict(solver_name='PYPOWER', num_iters=cpf_results["iterations"])
|
278
|
+
return results, success, sstats
|
279
|
+
|
280
|
+
|
281
|
+
def cpf_predictor(V, lam, Ybus, Sxfr, pv, pq,
|
282
|
+
step, z, Vprv, lamprv, parameterization):
|
283
|
+
# sizes
|
284
|
+
pvpq = np.r_[pv, pq]
|
285
|
+
nb = len(V)
|
286
|
+
npv = len(pv)
|
287
|
+
npq = len(pq)
|
288
|
+
|
289
|
+
# compute Jacobian for the power flow equations
|
290
|
+
dSbus_dVm, dSbus_dVa = dSbus_dV(Ybus, V)
|
291
|
+
|
292
|
+
j11 = dSbus_dVa[np.array([pvpq]).T, pvpq].real
|
293
|
+
j12 = dSbus_dVm[np.array([pvpq]).T, pq].real
|
294
|
+
j21 = dSbus_dVa[np.array([pq]).T, pvpq].imag
|
295
|
+
j22 = dSbus_dVm[np.array([pq]).T, pq].imag
|
296
|
+
|
297
|
+
J = sp.vstack([
|
298
|
+
sp.hstack([j11, j12]),
|
299
|
+
sp.hstack([j21, j22])
|
300
|
+
], format="csr")
|
301
|
+
|
302
|
+
dF_dlam = -np.r_[Sxfr[pvpq].real, Sxfr[pq].imag].reshape((-1, 1))
|
303
|
+
dP_dV, dP_dlam = cpf_p_jac(parameterization, z, V, lam, Vprv, lamprv, pv, pq)
|
304
|
+
|
305
|
+
# linear operator for computing the tangent predictor
|
306
|
+
J = sp.vstack([
|
307
|
+
sp.hstack([J, dF_dlam]),
|
308
|
+
sp.hstack([dP_dV, dP_dlam])
|
309
|
+
], format="csr")
|
310
|
+
|
311
|
+
Vaprv = np.angle(V)
|
312
|
+
Vmprv = abs(V)
|
313
|
+
|
314
|
+
# compute normalized tangent predictor
|
315
|
+
s = np.zeros(npv+2*npq+1)
|
316
|
+
s[-1] = 1
|
317
|
+
z[np.r_[pvpq, nb+pq, 2*nb]] = sp.linalg.spsolve(J, s)
|
318
|
+
z = z / np.linalg.norm(z)
|
319
|
+
|
320
|
+
Va0 = Vaprv
|
321
|
+
Vm0 = Vmprv
|
322
|
+
lam0 = lam
|
323
|
+
|
324
|
+
# prediction for next step
|
325
|
+
Va0[pvpq] = Vaprv[pvpq] + step * z[pvpq]
|
326
|
+
Vm0[pq] = Vmprv[pq] + step * z[nb+pq]
|
327
|
+
lam0 = lam + step * z[2*nb]
|
328
|
+
V0 = Vm0 * np.exp((1j * Va0))
|
329
|
+
|
330
|
+
return V0, lam0, z
|
331
|
+
|
332
|
+
|
333
|
+
def cpf_corrector(Ybus, Sbus, V0, ref, pv, pq,
|
334
|
+
lam0, Sxfr, Vprv, lamprv, z,
|
335
|
+
step, parameterization, ppopt):
|
336
|
+
|
337
|
+
# default arguments
|
338
|
+
if ppopt is None:
|
339
|
+
ppopt = ppoption(ppopt)
|
340
|
+
|
341
|
+
# options
|
342
|
+
verbose = ppopt["VERBOSE"]
|
343
|
+
tol = ppopt["PF_TOL"]
|
344
|
+
max_it = ppopt["PF_MAX_IT"]
|
345
|
+
|
346
|
+
# initialize
|
347
|
+
converged = 0
|
348
|
+
i = 0
|
349
|
+
V = V0
|
350
|
+
Va = np.angle(V)
|
351
|
+
Vm = abs(V)
|
352
|
+
lam = lam0
|
353
|
+
|
354
|
+
# set up indexing for updating V
|
355
|
+
pvpq = np.r_[pv, pq]
|
356
|
+
npv = len(pv)
|
357
|
+
npq = len(pq)
|
358
|
+
nb = len(V)
|
359
|
+
j1 = 0
|
360
|
+
j2 = npv # j1:j2 - V angle of pv buses
|
361
|
+
j3 = j2
|
362
|
+
j4 = j2 + npq # j1:j2 - V angle of pv buses
|
363
|
+
j5 = j4
|
364
|
+
j6 = j4 + npq # j5:j6 - V mag of pq buses
|
365
|
+
j7 = j6
|
366
|
+
j8 = j6 + 1 # j7:j8 - lambda
|
367
|
+
|
368
|
+
# evaluate F(x0, lam0), including Sxfr transfer/loading
|
369
|
+
|
370
|
+
mis = V * np.conj(Ybus.dot(V)) - Sbus - lam*Sxfr
|
371
|
+
F = np.r_[mis[pvpq].real, mis[pq].imag]
|
372
|
+
|
373
|
+
# evaluate P(x0, lambda0)
|
374
|
+
P = cpf_p(parameterization, step, z, V, lam, Vprv, lamprv, pv, pq)
|
375
|
+
|
376
|
+
# augment F(x, lambda) with P(x, lambda)
|
377
|
+
F = np.r_[F, P]
|
378
|
+
|
379
|
+
# check tolerance
|
380
|
+
normF = np.linalg.norm(F, inf)
|
381
|
+
logger.debug('CPF correction')
|
382
|
+
logger.debug('%2d: |F(x)| = %.10g', i, normF)
|
383
|
+
if normF < tol:
|
384
|
+
converged = 1
|
385
|
+
|
386
|
+
# do Newton iterations
|
387
|
+
while not converged and i < max_it:
|
388
|
+
# update iteration counter
|
389
|
+
i = i + 1
|
390
|
+
|
391
|
+
# evaluate Jacobian
|
392
|
+
dSbus_dVm, dSbus_dVa = dSbus_dV(Ybus, V)
|
393
|
+
|
394
|
+
j11 = dSbus_dVa[np.array([pvpq]).T, pvpq].real
|
395
|
+
j12 = dSbus_dVm[np.array([pvpq]).T, pq].real
|
396
|
+
j21 = dSbus_dVa[np.array([pq]).T, pvpq].imag
|
397
|
+
j22 = dSbus_dVm[np.array([pq]).T, pq].imag
|
398
|
+
|
399
|
+
J = sp.vstack([
|
400
|
+
sp.hstack([j11, j12]),
|
401
|
+
sp.hstack([j21, j22])
|
402
|
+
], format="csr")
|
403
|
+
|
404
|
+
dF_dlam = -np.r_[Sxfr[pvpq].real, Sxfr[pq].imag].reshape((-1, 1))
|
405
|
+
dP_dV, dP_dlam = cpf_p_jac(parameterization, z, V, lam, Vprv, lamprv, pv, pq)
|
406
|
+
|
407
|
+
# augment J with real/imag -Sxfr and z^T
|
408
|
+
J = sp.vstack([
|
409
|
+
sp.hstack([J, dF_dlam]),
|
410
|
+
sp.hstack([dP_dV, dP_dlam])
|
411
|
+
], format="csr")
|
412
|
+
|
413
|
+
# compute update step
|
414
|
+
dx = -1 * sp.linalg.spsolve(J, F)
|
415
|
+
|
416
|
+
# update voltage
|
417
|
+
if npv:
|
418
|
+
Va[pv] = Va[pv] + dx[j1:j2]
|
419
|
+
if npq:
|
420
|
+
Va[pq] = Va[pq] + dx[j3:j4]
|
421
|
+
Vm[pq] = Vm[pq] + dx[j5:j6]
|
422
|
+
V = Vm * np.exp((1j * Va))
|
423
|
+
Vm = abs(V)
|
424
|
+
Va = np.angle(V)
|
425
|
+
|
426
|
+
# update lambda
|
427
|
+
lam = lam + dx[j7:j8]
|
428
|
+
|
429
|
+
# evalute F(x, lam)
|
430
|
+
mis = V * np.conj(Ybus.dot(V)) - Sbus - lam*Sxfr
|
431
|
+
F = np.r_[mis[pv].real, mis[pq].real, mis[pq].imag]
|
432
|
+
|
433
|
+
# evaluate P(x, lambda)
|
434
|
+
P = cpf_p(parameterization, step, z, V, lam, Vprv, lamprv, pv, pq)
|
435
|
+
|
436
|
+
# augment F(x, lambda) with P(x, lambda)
|
437
|
+
F = np.r_[F, P]
|
438
|
+
|
439
|
+
# check for convergence
|
440
|
+
normF = np.linalg.norm(F, inf)
|
441
|
+
logger.debug('%2d: |F(x)| = %.10g', i, normF)
|
442
|
+
|
443
|
+
if normF < tol:
|
444
|
+
converged = 1
|
445
|
+
|
446
|
+
if not converged:
|
447
|
+
logger.error('Newton''s method corrector did not converge in %d iterations.' % i)
|
448
|
+
|
449
|
+
return V, converged, i, lam
|
450
|
+
|
451
|
+
|
452
|
+
def cpf_p_jac(parameterization, z, V, lam, Vprv, lamprv, pv, pq):
|
453
|
+
if parameterization == 1:
|
454
|
+
npv = len(pv)
|
455
|
+
npq = len(pq)
|
456
|
+
dP_dV = np.zeros(npv+2*npq)
|
457
|
+
if lam >= lamprv:
|
458
|
+
dP_dlam = 1.0
|
459
|
+
else:
|
460
|
+
dP_dlam = -1.0
|
461
|
+
|
462
|
+
elif parameterization == 2:
|
463
|
+
pvpq = np.r_[pv, pq]
|
464
|
+
|
465
|
+
Va = np.angle(V)
|
466
|
+
Vm = abs(V)
|
467
|
+
Vaprv = np.angle(Vprv)
|
468
|
+
Vmprv = abs(Vprv)
|
469
|
+
dP_dV = 2 * (np.r_[Va[pvpq], Vm[pq]] -
|
470
|
+
np.r_[Vaprv[pvpq], Vmprv[pq]])
|
471
|
+
if lam == lamprv:
|
472
|
+
dP_dlam = 1.0
|
473
|
+
else:
|
474
|
+
dP_dlam = 2 * (lam - lamprv)
|
475
|
+
|
476
|
+
elif parameterization == 3:
|
477
|
+
nb = len(V)
|
478
|
+
dP_dV = z[np.r_[pv, pq, nb+pq]]
|
479
|
+
dP_dlam = z[2 * nb]
|
480
|
+
|
481
|
+
return dP_dV, dP_dlam
|
482
|
+
|
483
|
+
|
484
|
+
def cpf_p(parameterization, step, z, V, lam, Vprv, lamprv, pv, pq):
|
485
|
+
# evaluate P(x0, lambda0)
|
486
|
+
if parameterization == 1:
|
487
|
+
if lam >= lamprv:
|
488
|
+
P = lam - lamprv - step
|
489
|
+
else:
|
490
|
+
P = lamprv - lam - step
|
491
|
+
|
492
|
+
elif parameterization == 2:
|
493
|
+
pvpq = np.r_[pv, pq]
|
494
|
+
|
495
|
+
Va = np.angle(V)
|
496
|
+
Vm = abs(V)
|
497
|
+
Vaprv = np.angle(Vprv)
|
498
|
+
Vmprv = abs(Vprv)
|
499
|
+
P = sum(np.square(np.r_[Va[pvpq], Vm[pq], lam] -
|
500
|
+
np.r_[Vaprv[pvpq], Vmprv[pq], lamprv])) - np.square(step)
|
501
|
+
|
502
|
+
elif parameterization == 3:
|
503
|
+
pvpq = np.r_[pv, pq]
|
504
|
+
|
505
|
+
nb = len(V)
|
506
|
+
Va = np.angle(V)
|
507
|
+
Vm = abs(V)
|
508
|
+
Vaprv = np.angle(Vprv)
|
509
|
+
Vmprv = abs(Vprv)
|
510
|
+
P = np.dot(z[np.r_[pvpq, nb+pq, 2*nb]].reshape((1, -1)),
|
511
|
+
np.array(np.r_[Va[pvpq], Vm[pq], lam] - np.r_[Vaprv[pvpq], Vmprv[pq], lamprv]).T) - step
|
512
|
+
|
513
|
+
return P
|
@@ -0,0 +1,114 @@
|
|
1
|
+
"""Callback functions for CPF
|
2
|
+
"""
|
3
|
+
|
4
|
+
from numpy import c_, r_, amax, argmax, array
|
5
|
+
from scipy.sparse import issparse
|
6
|
+
|
7
|
+
import sys
|
8
|
+
import os
|
9
|
+
|
10
|
+
|
11
|
+
def cpf_default_callback(k, V_c, lam_c, V_p, lam_p, cb_data, cb_state, cb_args, results=None, is_final=False):
|
12
|
+
"""Default callback function for CPF
|
13
|
+
"""
|
14
|
+
|
15
|
+
# initialize plotting options
|
16
|
+
plot_level = cb_data["ppopt"]["CPF_PLOT_LEVEL"]
|
17
|
+
plot_bus = cb_data["ppopt"]["CPF_PLOT_BUS"]
|
18
|
+
|
19
|
+
if plot_level:
|
20
|
+
try:
|
21
|
+
import matplotlib.pyplot as plt
|
22
|
+
except ImportError:
|
23
|
+
logger.debug(
|
24
|
+
"Error: Please install \"Matpotlib\" package for plotting function!\n")
|
25
|
+
|
26
|
+
if plot_bus == '': # no bus specified
|
27
|
+
if len(cb_data["Sxfr"][cb_data["pq"]]) != 0:
|
28
|
+
idx = argmax(abs(cb_data["Sxfr"][cb_data["pq"]]), axis=0)
|
29
|
+
idx = cb_data["pq"][idx]
|
30
|
+
else: # or bus 1 if there are none
|
31
|
+
idx = 0
|
32
|
+
idx_e = int(cb_data["ppc_target"]["order"]["bus"]["i2e"][idx])
|
33
|
+
|
34
|
+
else:
|
35
|
+
idx_e = plot_bus
|
36
|
+
if issparse(cb_data["ppc_target"]["order"]["bus"]["e2i"][idx_e]):
|
37
|
+
idx = int(cb_data["ppc_target"]["order"]
|
38
|
+
["bus"]["e2i"][idx_e].toarray())
|
39
|
+
else:
|
40
|
+
idx = int(cb_data["ppc_target"]["order"]["bus"]["e2i"][idx_e])
|
41
|
+
|
42
|
+
if idx == 0:
|
43
|
+
logger.debug(
|
44
|
+
"cpf_default_callback: %d is not a valid bus number for PPOPT[\"CPF_PLOT_BUS\"]\n" % idx_e)
|
45
|
+
|
46
|
+
# ----- FINAL call -----
|
47
|
+
if is_final:
|
48
|
+
# assemble results struct
|
49
|
+
results = cb_state
|
50
|
+
results["max_lam"] = amax(cb_state["lam_c"])
|
51
|
+
results["iterations"] = k
|
52
|
+
|
53
|
+
# finish final lambda-V nose curve plot
|
54
|
+
if plot_level:
|
55
|
+
plt.plot(cb_state["lam_c"],
|
56
|
+
abs(cb_state["V_c"][idx, :]), '-', color=(0.25, 0.25, 1))
|
57
|
+
plt.axis([0, amax([1, amax(cb_state["lam_p"]), amax(cb_state["lam_c"])])*1.05, 0,
|
58
|
+
amax([1, amax(abs(cb_state["V_p"][idx])), amax(abs(cb_state["V_c"][idx]))*1.05])])
|
59
|
+
plt.pause(sys.float_info.epsilon)
|
60
|
+
plt.ioff()
|
61
|
+
|
62
|
+
elif k == 0:
|
63
|
+
# initialize state
|
64
|
+
cb_state = {
|
65
|
+
"V_p": V_p,
|
66
|
+
"lam_p": lam_p,
|
67
|
+
"V_c": V_c,
|
68
|
+
"lam_c": lam_c,
|
69
|
+
"iterations": 0
|
70
|
+
}
|
71
|
+
|
72
|
+
# initialize lambda-V nose curve plot
|
73
|
+
if plot_level:
|
74
|
+
plt.ion()
|
75
|
+
plt.plot(cb_state["lam_p"],
|
76
|
+
abs(cb_state["V_p"][idx]), '-', color=(0.25, 0.25, 1))
|
77
|
+
plt.title('Voltage at Bus %d' % idx_e)
|
78
|
+
plt.xlabel('$\\lambda$')
|
79
|
+
plt.ylabel('Voltage Magnitude')
|
80
|
+
plt.axis([0, amax([1, amax(cb_state["lam_p"]), amax(cb_state["lam_c"])])*1.05, 0,
|
81
|
+
amax([1, amax(abs(cb_state["V_p"][idx])), amax(abs(cb_state["V_c"][idx]))*1.05])])
|
82
|
+
plt.pause(sys.float_info.epsilon)
|
83
|
+
|
84
|
+
# ----- ITERATION call -----
|
85
|
+
else:
|
86
|
+
# update state
|
87
|
+
cb_state["V_p"] = c_[cb_state["V_p"], V_p]
|
88
|
+
cb_state["lam_p"] = r_[cb_state["lam_p"], lam_p]
|
89
|
+
cb_state["V_c"] = c_[cb_state["V_c"], V_c]
|
90
|
+
cb_state["lam_c"] = r_[cb_state["lam_c"], lam_c]
|
91
|
+
cb_state["iterations"] = k
|
92
|
+
|
93
|
+
# plot single step of the lambda-V nose curve
|
94
|
+
if plot_level > 1:
|
95
|
+
plt.plot(r_[cb_state["lam_c"][k-1], cb_state["lam_p"][k]],
|
96
|
+
r_[abs(cb_state["V_c"][idx, k-1]),
|
97
|
+
abs(cb_state["V_p"][idx, k])],
|
98
|
+
'-', color=0.85*array([1, 0.75, 0.75]))
|
99
|
+
plt.plot(r_[cb_state["lam_p"][k], cb_state["lam_c"][k]],
|
100
|
+
r_[abs(cb_state["V_p"][idx, k]),
|
101
|
+
abs(cb_state["V_c"][idx, k])],
|
102
|
+
'-', color=0.85*array([0.75, 1, 0.75]))
|
103
|
+
plt.plot(cb_state["lam_p"][k], abs(cb_state["V_p"][idx, k]),
|
104
|
+
'x', color=0.85*array([1, 0.75, 0.75]))
|
105
|
+
plt.plot(cb_state["lam_c"][k], abs(
|
106
|
+
cb_state["V_c"][idx, k]), '-o', markerfacecolor='none', color=(0.25, 0.25, 1))
|
107
|
+
plt.axis([0, amax([1, amax(cb_state["lam_p"]), amax(cb_state["lam_c"])])*1.05, 0,
|
108
|
+
amax([1, amax(abs(cb_state["V_p"][idx])), amax(abs(cb_state["V_c"][idx]))*1.05])])
|
109
|
+
plt.pause(sys.float_info.epsilon)
|
110
|
+
|
111
|
+
if plot_level > 2:
|
112
|
+
os.system("pause")
|
113
|
+
|
114
|
+
return cb_state, results
|