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.
Files changed (191) hide show
  1. ams/__init__.py +4 -11
  2. ams/_version.py +3 -3
  3. ams/cases/5bus/pjm5bus_demo.xlsx +0 -0
  4. ams/cases/5bus/pjm5bus_jumper.xlsx +0 -0
  5. ams/cases/5bus/pjm5bus_uced.json +1062 -0
  6. ams/cases/5bus/pjm5bus_uced.xlsx +0 -0
  7. ams/cases/5bus/pjm5bus_uced_esd1.xlsx +0 -0
  8. ams/cases/5bus/pjm5bus_uced_ev.xlsx +0 -0
  9. ams/cases/ieee123/ieee123.xlsx +0 -0
  10. ams/cases/ieee123/ieee123_regcv1.xlsx +0 -0
  11. ams/cases/ieee14/ieee14.json +1166 -0
  12. ams/cases/ieee14/ieee14.raw +92 -0
  13. ams/cases/ieee14/ieee14_conn.xlsx +0 -0
  14. ams/cases/ieee14/ieee14_uced.xlsx +0 -0
  15. ams/cases/ieee39/ieee39.xlsx +0 -0
  16. ams/cases/ieee39/ieee39_uced.xlsx +0 -0
  17. ams/cases/ieee39/ieee39_uced_esd1.xlsx +0 -0
  18. ams/cases/ieee39/ieee39_uced_pvd1.xlsx +0 -0
  19. ams/cases/ieee39/ieee39_uced_vis.xlsx +0 -0
  20. ams/cases/matpower/benchmark.json +1594 -0
  21. ams/cases/matpower/case118.m +787 -0
  22. ams/cases/matpower/case14.m +129 -0
  23. ams/cases/matpower/case300.m +1315 -0
  24. ams/cases/matpower/case39.m +205 -0
  25. ams/cases/matpower/case5.m +62 -0
  26. ams/cases/matpower/case_ACTIVSg2000.m +9460 -0
  27. ams/cases/npcc/npcc.m +644 -0
  28. ams/cases/npcc/npcc_uced.xlsx +0 -0
  29. ams/cases/pglib/pglib_opf_case39_epri__api.m +243 -0
  30. ams/cases/wecc/wecc.m +714 -0
  31. ams/cases/wecc/wecc_uced.xlsx +0 -0
  32. ams/cli.py +6 -0
  33. ams/core/__init__.py +2 -0
  34. ams/core/documenter.py +652 -0
  35. ams/core/matprocessor.py +782 -0
  36. ams/core/model.py +330 -0
  37. ams/core/param.py +322 -0
  38. ams/core/service.py +918 -0
  39. ams/core/symprocessor.py +224 -0
  40. ams/core/var.py +59 -0
  41. ams/extension/__init__.py +5 -0
  42. ams/extension/eva.py +401 -0
  43. ams/interface.py +1085 -0
  44. ams/io/__init__.py +133 -0
  45. ams/io/json.py +82 -0
  46. ams/io/matpower.py +406 -0
  47. ams/io/psse.py +6 -0
  48. ams/io/pypower.py +103 -0
  49. ams/io/xlsx.py +80 -0
  50. ams/main.py +81 -4
  51. ams/models/__init__.py +24 -0
  52. ams/models/area.py +40 -0
  53. ams/models/bus.py +52 -0
  54. ams/models/cost.py +169 -0
  55. ams/models/distributed/__init__.py +3 -0
  56. ams/models/distributed/esd1.py +71 -0
  57. ams/models/distributed/ev.py +60 -0
  58. ams/models/distributed/pvd1.py +67 -0
  59. ams/models/group.py +231 -0
  60. ams/models/info.py +26 -0
  61. ams/models/line.py +238 -0
  62. ams/models/renewable/__init__.py +5 -0
  63. ams/models/renewable/regc.py +119 -0
  64. ams/models/reserve.py +94 -0
  65. ams/models/shunt.py +14 -0
  66. ams/models/static/__init__.py +2 -0
  67. ams/models/static/gen.py +165 -0
  68. ams/models/static/pq.py +61 -0
  69. ams/models/timeslot.py +69 -0
  70. ams/models/zone.py +49 -0
  71. ams/opt/__init__.py +12 -0
  72. ams/opt/constraint.py +175 -0
  73. ams/opt/exprcalc.py +127 -0
  74. ams/opt/expression.py +188 -0
  75. ams/opt/objective.py +174 -0
  76. ams/opt/omodel.py +432 -0
  77. ams/opt/optzbase.py +192 -0
  78. ams/opt/param.py +156 -0
  79. ams/opt/var.py +233 -0
  80. ams/pypower/__init__.py +8 -0
  81. ams/pypower/_compat.py +9 -0
  82. ams/pypower/core/__init__.py +8 -0
  83. ams/pypower/core/pips.py +894 -0
  84. ams/pypower/core/ppoption.py +244 -0
  85. ams/pypower/core/ppver.py +18 -0
  86. ams/pypower/core/solver.py +2451 -0
  87. ams/pypower/eps.py +6 -0
  88. ams/pypower/idx.py +174 -0
  89. ams/pypower/io.py +604 -0
  90. ams/pypower/make/__init__.py +11 -0
  91. ams/pypower/make/matrices.py +665 -0
  92. ams/pypower/make/pdv.py +506 -0
  93. ams/pypower/routines/__init__.py +7 -0
  94. ams/pypower/routines/cpf.py +513 -0
  95. ams/pypower/routines/cpf_callbacks.py +114 -0
  96. ams/pypower/routines/opf.py +1803 -0
  97. ams/pypower/routines/opffcns.py +1946 -0
  98. ams/pypower/routines/pflow.py +852 -0
  99. ams/pypower/toggle.py +1098 -0
  100. ams/pypower/utils.py +293 -0
  101. ams/report.py +212 -50
  102. ams/routines/__init__.py +23 -0
  103. ams/routines/acopf.py +117 -0
  104. ams/routines/cpf.py +65 -0
  105. ams/routines/dcopf.py +241 -0
  106. ams/routines/dcpf.py +209 -0
  107. ams/routines/dcpf0.py +196 -0
  108. ams/routines/dopf.py +150 -0
  109. ams/routines/ed.py +312 -0
  110. ams/routines/pflow.py +255 -0
  111. ams/routines/pflow0.py +113 -0
  112. ams/routines/routine.py +1033 -0
  113. ams/routines/rted.py +519 -0
  114. ams/routines/type.py +160 -0
  115. ams/routines/uc.py +376 -0
  116. ams/shared.py +63 -9
  117. ams/system.py +61 -22
  118. ams/utils/__init__.py +3 -0
  119. ams/utils/misc.py +77 -0
  120. ams/utils/paths.py +257 -0
  121. docs/Makefile +21 -0
  122. docs/make.bat +35 -0
  123. docs/source/_templates/autosummary/base.rst +5 -0
  124. docs/source/_templates/autosummary/class.rst +35 -0
  125. docs/source/_templates/autosummary/module.rst +65 -0
  126. docs/source/_templates/autosummary/module_toctree.rst +66 -0
  127. docs/source/api.rst +102 -0
  128. docs/source/conf.py +206 -0
  129. docs/source/examples/index.rst +34 -0
  130. docs/source/genmodelref.py +61 -0
  131. docs/source/genroutineref.py +47 -0
  132. docs/source/getting_started/copyright.rst +20 -0
  133. docs/source/getting_started/formats/index.rst +20 -0
  134. docs/source/getting_started/formats/matpower.rst +183 -0
  135. docs/source/getting_started/formats/psse.rst +46 -0
  136. docs/source/getting_started/formats/pypower.rst +223 -0
  137. docs/source/getting_started/formats/xlsx.png +0 -0
  138. docs/source/getting_started/formats/xlsx.rst +23 -0
  139. docs/source/getting_started/index.rst +76 -0
  140. docs/source/getting_started/install.rst +231 -0
  141. docs/source/getting_started/overview.rst +26 -0
  142. docs/source/getting_started/testcase.rst +45 -0
  143. docs/source/getting_started/verification.rst +13 -0
  144. docs/source/images/curent.ico +0 -0
  145. docs/source/images/dcopf_time.png +0 -0
  146. docs/source/images/sponsors/CURENT_Logo_NameOnTrans.png +0 -0
  147. docs/source/images/sponsors/CURENT_Logo_Transparent.png +0 -0
  148. docs/source/images/sponsors/CURENT_Logo_Transparent_Name.png +0 -0
  149. docs/source/images/sponsors/doe.png +0 -0
  150. docs/source/index.rst +108 -0
  151. docs/source/modeling/example.rst +159 -0
  152. docs/source/modeling/index.rst +17 -0
  153. docs/source/modeling/model.rst +210 -0
  154. docs/source/modeling/routine.rst +122 -0
  155. docs/source/modeling/system.rst +51 -0
  156. docs/source/release-notes.rst +398 -0
  157. ltbams-1.0.2.dist-info/METADATA +215 -0
  158. ltbams-1.0.2.dist-info/RECORD +188 -0
  159. {ltbams-0.9.9.dist-info → ltbams-1.0.2.dist-info}/WHEEL +1 -1
  160. ltbams-1.0.2.dist-info/top_level.txt +3 -0
  161. tests/__init__.py +0 -0
  162. tests/test_1st_system.py +33 -0
  163. tests/test_addressing.py +40 -0
  164. tests/test_andes_mats.py +61 -0
  165. tests/test_case.py +266 -0
  166. tests/test_cli.py +34 -0
  167. tests/test_export_csv.py +89 -0
  168. tests/test_group.py +83 -0
  169. tests/test_interface.py +216 -0
  170. tests/test_io.py +32 -0
  171. tests/test_jumper.py +27 -0
  172. tests/test_known_good.py +267 -0
  173. tests/test_matp.py +437 -0
  174. tests/test_model.py +54 -0
  175. tests/test_omodel.py +119 -0
  176. tests/test_paths.py +22 -0
  177. tests/test_report.py +251 -0
  178. tests/test_repr.py +21 -0
  179. tests/test_routine.py +178 -0
  180. tests/test_rtn_dcopf.py +101 -0
  181. tests/test_rtn_dcpf.py +77 -0
  182. tests/test_rtn_ed.py +279 -0
  183. tests/test_rtn_pflow.py +219 -0
  184. tests/test_rtn_rted.py +273 -0
  185. tests/test_rtn_uc.py +248 -0
  186. tests/test_service.py +73 -0
  187. ltbams-0.9.9.dist-info/LICENSE +0 -692
  188. ltbams-0.9.9.dist-info/METADATA +0 -859
  189. ltbams-0.9.9.dist-info/RECORD +0 -14
  190. ltbams-0.9.9.dist-info/top_level.txt +0 -1
  191. {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