1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 """Enable or disable set of interface flow constraints.
18 """
19
20 from sys import stderr
21
22 from pprint import pprint
23
24 from numpy import zeros, arange, unique, sign, delete, flatnonzero as find
25
26 from scipy.sparse import lil_matrix, csr_matrix as sparse
27
28 from pypower.add_userfcn import add_userfcn
29 from pypower.remove_userfcn import remove_userfcn
30 from pypower.makeBdc import makeBdc
31 from pypower.idx_brch import PF
32
33
35 """Enable or disable set of interface flow constraints.
36
37 Enables or disables a set of OPF userfcn callbacks to implement
38 interface flow limits based on a DC flow model.
39
40 These callbacks expect to find an 'if' field in the input C{ppc}, where
41 C{ppc['if']} is a dict with the following fields:
42 - C{map} C{n x 2}, defines each interface in terms of a set of
43 branch indices and directions. Interface I is defined
44 by the set of rows whose 1st col is equal to I. The
45 2nd column is a branch index multiplied by 1 or -1
46 respectively for lines whose orientation is the same
47 as or opposite to that of the interface.
48 - C{lims} C{nif x 3}, defines the DC model flow limits in MW
49 for specified interfaces. The 2nd and 3rd columns specify
50 the lower and upper limits on the (DC model) flow
51 across the interface, respectively. Normally, the lower
52 limit is negative, indicating a flow in the opposite
53 direction.
54
55 The 'int2ext' callback also packages up results and stores them in
56 the following output fields of C{results['if']}:
57 - C{P} - C{nif x 1}, actual flow across each interface in MW
58 - C{mu.l} - C{nif x 1}, shadow price on lower flow limit, ($/MW)
59 - C{mu.u} - C{nif x 1}, shadow price on upper flow limit, ($/MW)
60
61 @see: L{add_userfcn}, L{remove_userfcn}, L{run_userfcn},
62 L{t.t_case30_userfcns}.
63
64 @author: Ray Zimmerman (PSERC Cornell)
65 @author: Richard Lincoln
66 """
67 if on_off == 'on':
68
69 if ('if' not in ppc) | (not isinstance(ppc['if'], dict)) | \
70 ('map' not in ppc['if']) | \
71 ('lims' not in ppc['if']):
72 stderr.write('toggle_iflims: case must contain an \'if\' field, a struct defining \'map\' and \'lims\'')
73
74
75
76
77 ppc = add_userfcn(ppc, 'ext2int', userfcn_iflims_ext2int)
78 ppc = add_userfcn(ppc, 'formulation', userfcn_iflims_formulation)
79 ppc = add_userfcn(ppc, 'int2ext', userfcn_iflims_int2ext)
80 ppc = add_userfcn(ppc, 'printpf', userfcn_iflims_printpf)
81 ppc = add_userfcn(ppc, 'savecase', userfcn_iflims_savecase)
82 elif on_off == 'off':
83 ppc = remove_userfcn(ppc, 'savecase', userfcn_iflims_savecase)
84 ppc = remove_userfcn(ppc, 'printpf', userfcn_iflims_printpf)
85 ppc = remove_userfcn(ppc, 'int2ext', userfcn_iflims_int2ext)
86 ppc = remove_userfcn(ppc, 'formulation', userfcn_iflims_formulation)
87 ppc = remove_userfcn(ppc, 'ext2int', userfcn_iflims_ext2int)
88 else:
89 stderr.write('toggle_iflims: 2nd argument must be either \'on\' or \'off\'')
90
91 return ppc
92
93
95 """This is the 'ext2int' stage userfcn callback that prepares the input
96 data for the formulation stage. It expects to find an 'if' field in
97 ppc as described above. The optional args are not currently used.
98 """
99
100 ifmap = ppc['if']['map']
101 o = ppc['order']
102 nl0 = o['ext']['branch'].shape[0]
103 nl = ppc['branch'].shape[0]
104
105
106 ppc['order']['ext']['ifmap'] = ifmap
107
108
109 e2i = zeros(nl0)
110 e2i[o['branch']['status']['on']] = arange(nl)
111 d = sign(ifmap[:, 1])
112 br = abs(ifmap[:, 1]).astype(int)
113 ifmap[:, 1] = d * e2i[br]
114
115 ifmap = delete(ifmap, find(ifmap[:, 1] == 0), 0)
116
117 ppc['if']['map'] = ifmap
118
119 return ppc
120
121
166
167
169 """This is the 'int2ext' stage userfcn callback that converts everything
170 back to external indexing and packages up the results. It expects to
171 find an 'if' field in the C{results} dict as described for ppc above.
172 It also expects the results to contain solved branch flows and linear
173 constraints named 'iflims' which are used to populate output fields
174 in C{results['if']}. The optional args are not currently used.
175 """
176
177 ifmap = results['if']['map']
178 iflims = results['if']['lims']
179
180
181 results['if']['map'] = results['order']['ext']['ifmap']
182
183
184 ifidx = unique(iflims[:, 0])
185 nifs = len(ifidx)
186 results['if']['P'] = zeros(nifs)
187 for k in range(nifs):
188
189 br = ifmap[ifmap[:, 0] == ifidx[k], 1]
190 d = sign(br)
191 br = abs(br)
192 results['if']['P'][k] = sum( d * results['branch'][br, PF] )
193
194 if 'mu' not in results['if']:
195 results['if']['mu'] = {}
196 results['if']['mu']['l'] = results['lin']['mu']['l']['iflims']
197 results['if']['mu']['u'] = results['lin']['mu']['u']['iflims']
198
199 return results
200
201
203 """This is the 'printpf' stage userfcn callback that pretty-prints the
204 results. It expects a C{results} dict, a file descriptor and a PYPOWER
205 options vector. The optional args are not currently used.
206 """
207
208 OUT_ALL = ppopt['OUT_ALL']
209
210 ptol = 1e-6
211
212 if OUT_ALL != 0:
213 iflims = results['if']['lims']
214 fd.write('\n================================================================================')
215 fd.write('\n| Interface Flow Limits |')
216 fd.write('\n================================================================================')
217 fd.write('\n Interface Shadow Prc Lower Lim Flow Upper Lim Shadow Prc')
218 fd.write('\n # ($/MW) (MW) (MW) (MW) ($/MW) ')
219 fd.write('\n---------- ---------- ---------- ---------- ---------- -----------')
220 ifidx = unique(iflims[:, 0])
221 nifs = len(ifidx)
222 for k in range(nifs):
223 fd.write('\n%6d ', iflims(k, 1))
224 if results['if']['mu']['l'][k] > ptol:
225 fd.write('%14.3f' % results['if']['mu']['l'][k])
226 else:
227 fd.write(' - ')
228
229 fd.write('%12.2f%12.2f%12.2f' % (iflims[k, 1], results['if']['P'][k], iflims[k, 2]))
230 if results['if']['mu']['u'][k] > ptol:
231 fd.write('%13.3f' % results['if']['mu']['u'][k])
232 else:
233 fd.write(' - ')
234
235 fd.write('\n')
236
237 return results
238
239
241 """This is the 'savecase' stage userfcn callback that prints the Python
242 file code to save the 'if' field in the case file. It expects a
243 PYPOWER case dict (ppc), a file descriptor and variable prefix
244 (usually 'ppc'). The optional args are not currently used.
245 """
246 ifmap = ppc['if']['map']
247 iflims = ppc['if']['lims']
248
249 fd.write('\n####----- Interface Flow Limit Data -----####\n')
250 fd.write('#### interface<->branch map data\n')
251 fd.write('##\tifnum\tbranchidx (negative defines opposite direction)\n')
252 fd.write('%sif.map = [\n' % prefix)
253 fd.write('\t%d\t%d;\n' % ifmap.T)
254 fd.write('];\n')
255
256 fd.write('\n#### interface flow limit data (based on DC model)\n')
257 fd.write('#### (lower limit should be negative for opposite direction)\n')
258 fd.write('##\tifnum\tlower\tupper\n')
259 fd.write('%sif.lims = [\n' % prefix)
260 fd.write('\t%d\t%g\t%g;\n' % iflims.T)
261 fd.write('];\n')
262
263
264 if ('P' in ppc['if']):
265 fd.write('\n#### solved values\n')
266 fd.write('%sif.P = %s\n' % (prefix, pprint(ppc['if']['P'])))
267 fd.write('%sif.mu.l = %s\n' % (prefix, pprint(ppc['if']['mu']['l'])))
268 fd.write('%sif.mu.u = %s\n' % (prefix, pprint(ppc['if']['mu']['u'])))
269
270 return ppc
271