TOGGLE_IFLIMS Enable or disable set of interface flow constraints. MPC = TOGGLE_IFLIMS(MPC, 'on') MPC = TOGGLE_IFLIMS(MPC, 'off') Enables or disables a set of OPF userfcn callbacks to implement interface flow limits based on a DC flow model. These callbacks expect to find an 'if' field in the input MPC, where MPC.if is a struct with the following fields: map n x 2, defines each interface in terms of a set of branch indices and directions. Interface I is defined by the set of rows whose 1st col is equal to I. The 2nd column is a branch index multiplied by 1 or -1 respectively for lines whose orientation is the same as or opposite to that of the interface. lims nif x 3, defines the DC model flow limits in MW for specified interfaces. The 2nd and 3rd columns specify the lower and upper limits on the (DC model) flow across the interface, respectively. Normally, the lower limit is negative, indicating a flow in the opposite direction. The 'int2ext' callback also packages up results and stores them in the following output fields of results.if: P - nif x 1, actual flow across each interface in MW mu.l - nif x 1, shadow price on lower flow limit, ($/MW) mu.u - nif x 1, shadow price on upper flow limit, ($/MW) See also ADD_USERFCN, REMOVE_USERFCN, RUN_USERFCN, T_CASE30_USERFCNS.
0001 function mpc = toggle_iflims(mpc, on_off) 0002 %TOGGLE_IFLIMS Enable or disable set of interface flow constraints. 0003 % MPC = TOGGLE_IFLIMS(MPC, 'on') 0004 % MPC = TOGGLE_IFLIMS(MPC, 'off') 0005 % 0006 % Enables or disables a set of OPF userfcn callbacks to implement 0007 % interface flow limits based on a DC flow model. 0008 % 0009 % These callbacks expect to find an 'if' field in the input MPC, where 0010 % MPC.if is a struct with the following fields: 0011 % map n x 2, defines each interface in terms of a set of 0012 % branch indices and directions. Interface I is defined 0013 % by the set of rows whose 1st col is equal to I. The 0014 % 2nd column is a branch index multiplied by 1 or -1 0015 % respectively for lines whose orientation is the same 0016 % as or opposite to that of the interface. 0017 % lims nif x 3, defines the DC model flow limits in MW 0018 % for specified interfaces. The 2nd and 3rd columns specify 0019 % the lower and upper limits on the (DC model) flow 0020 % across the interface, respectively. Normally, the lower 0021 % limit is negative, indicating a flow in the opposite 0022 % direction. 0023 % 0024 % The 'int2ext' callback also packages up results and stores them in 0025 % the following output fields of results.if: 0026 % P - nif x 1, actual flow across each interface in MW 0027 % mu.l - nif x 1, shadow price on lower flow limit, ($/MW) 0028 % mu.u - nif x 1, shadow price on upper flow limit, ($/MW) 0029 % 0030 % See also ADD_USERFCN, REMOVE_USERFCN, RUN_USERFCN, T_CASE30_USERFCNS. 0031 0032 % MATPOWER 0033 % $Id: toggle_iflims.m,v 1.12 2010/04/26 19:45:25 ray Exp $ 0034 % by Ray Zimmerman, PSERC Cornell 0035 % Copyright (c) 2009-2010 by Power System Engineering Research Center (PSERC) 0036 % 0037 % This file is part of MATPOWER. 0038 % See http://www.pserc.cornell.edu/matpower/ for more info. 0039 % 0040 % MATPOWER is free software: you can redistribute it and/or modify 0041 % it under the terms of the GNU General Public License as published 0042 % by the Free Software Foundation, either version 3 of the License, 0043 % or (at your option) any later version. 0044 % 0045 % MATPOWER is distributed in the hope that it will be useful, 0046 % but WITHOUT ANY WARRANTY; without even the implied warranty of 0047 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0048 % GNU General Public License for more details. 0049 % 0050 % You should have received a copy of the GNU General Public License 0051 % along with MATPOWER. If not, see <http://www.gnu.org/licenses/>. 0052 % 0053 % Additional permission under GNU GPL version 3 section 7 0054 % 0055 % If you modify MATPOWER, or any covered work, to interface with 0056 % other modules (such as MATLAB code and MEX-files) available in a 0057 % MATLAB(R) or comparable environment containing parts covered 0058 % under other licensing terms, the licensors of MATPOWER grant 0059 % you additional permission to convey the resulting work. 0060 0061 if strcmp(on_off, 'on') 0062 %% check for proper reserve inputs 0063 if ~isfield(mpc, 'if') || ~isstruct(mpc.if) || ... 0064 ~isfield(mpc.if, 'map') || ... 0065 ~isfield(mpc.if, 'lims') 0066 error('toggle_iflims: case must contain an ''if'' field, a struct defining ''map'' and ''lims'''); 0067 end 0068 0069 %% add callback functions 0070 %% note: assumes all necessary data included in 1st arg (mpc, om, results) 0071 %% so, no additional explicit args are needed 0072 mpc = add_userfcn(mpc, 'ext2int', @userfcn_iflims_ext2int); 0073 mpc = add_userfcn(mpc, 'formulation', @userfcn_iflims_formulation); 0074 mpc = add_userfcn(mpc, 'int2ext', @userfcn_iflims_int2ext); 0075 mpc = add_userfcn(mpc, 'printpf', @userfcn_iflims_printpf); 0076 mpc = add_userfcn(mpc, 'savecase', @userfcn_iflims_savecase); 0077 elseif strcmp(on_off, 'off') 0078 mpc = remove_userfcn(mpc, 'savecase', @userfcn_iflims_savecase); 0079 mpc = remove_userfcn(mpc, 'printpf', @userfcn_iflims_printpf); 0080 mpc = remove_userfcn(mpc, 'int2ext', @userfcn_iflims_int2ext); 0081 mpc = remove_userfcn(mpc, 'formulation', @userfcn_iflims_formulation); 0082 mpc = remove_userfcn(mpc, 'ext2int', @userfcn_iflims_ext2int); 0083 else 0084 error('toggle_iflims: 2nd argument must be either ''on'' or ''off'''); 0085 end 0086 0087 0088 %%----- ext2int ------------------------------------------------------ 0089 function mpc = userfcn_iflims_ext2int(mpc, args) 0090 % 0091 % mpc = userfcn_iflims_ext2int(mpc, args) 0092 % 0093 % This is the 'ext2int' stage userfcn callback that prepares the input 0094 % data for the formulation stage. It expects to find an 'if' field in 0095 % mpc as described above. The optional args are not currently used. 0096 0097 %% initialize some things 0098 ifmap = mpc.if.map; 0099 o = mpc.order; 0100 nl0 = size(o.ext.branch, 1); %% original number of branches 0101 nl = size(mpc.branch, 1); %% number of on-line branches 0102 0103 %% save if.map for external indexing 0104 mpc.order.ext.ifmap = ifmap; 0105 0106 %%----- convert stuff to internal indexing ----- 0107 e2i = zeros(nl0, 1); 0108 e2i(o.branch.status.on) = (1:nl)'; %% ext->int branch index mapping 0109 d = sign(ifmap(:, 2)); 0110 br = abs(ifmap(:, 2)); 0111 ifmap(:, 2) = d .* e2i(br); 0112 ifmap(ifmap(:, 2) == 0, :) = []; %% delete branches that are out 0113 0114 mpc.if.map = ifmap; 0115 0116 0117 %%----- formulation -------------------------------------------------- 0118 function om = userfcn_iflims_formulation(om, args) 0119 % 0120 % om = userfcn_iflims_formulation(om, args) 0121 % 0122 % This is the 'formulation' stage userfcn callback that defines the 0123 % user costs and constraints for interface flow limits. It expects to 0124 % find an 'if' field in the mpc stored in om, as described above. The 0125 % optional args are not currently used. 0126 0127 %% define named indices into data matrices 0128 [F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ... 0129 TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ... 0130 ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch; 0131 0132 %% initialize some things 0133 mpc = get_mpc(om); 0134 [baseMVA, bus, branch] = deal(mpc.baseMVA, mpc.bus, mpc.branch); 0135 ifmap = mpc.if.map; 0136 iflims = mpc.if.lims; 0137 0138 %% form B matrices for DC model 0139 [B, Bf, Pbusinj, Pfinj] = makeBdc(baseMVA, bus, branch); 0140 n = size(Bf, 2); %% dim of theta 0141 0142 %% form constraints 0143 ifidx = unique(iflims(:, 1)); %% interface number list 0144 nifs = length(ifidx); %% number of interfaces 0145 Aif = sparse(nifs, n); 0146 lif = zeros(nifs, 1); 0147 uif = zeros(nifs, 1); 0148 for k = 1:nifs 0149 %% extract branch indices 0150 br = ifmap(ifmap(:, 1) == ifidx(k), 2); 0151 if isempty(br) 0152 error('userfcn_iflims_formulation: interface %d has no in-service branches', k); 0153 end 0154 d = sign(br); 0155 br = abs(br); 0156 Ak = sparse(1, n); %% Ak = sum( d(i) * Bf(i, :) ) 0157 bk = 0; %% bk = sum( d(i) * Pfinj(i) ) 0158 for i = 1:length(br) 0159 Ak = Ak + d(i) * Bf(br(i), :); 0160 bk = bk + d(i) * Pfinj(br(i)); 0161 end 0162 Aif(k, :) = Ak; 0163 lif(k) = iflims(k, 2) / baseMVA - bk; 0164 uif(k) = iflims(k, 3) / baseMVA - bk; 0165 end 0166 0167 %% add interface constraint 0168 om = add_constraints(om, 'iflims', Aif, lif, uif, {'Va'}); %% nifs 0169 0170 0171 %%----- int2ext ------------------------------------------------------ 0172 function results = userfcn_iflims_int2ext(results, args) 0173 % 0174 % results = userfcn_iflims_int2ext(results, args) 0175 % 0176 % This is the 'int2ext' stage userfcn callback that converts everything 0177 % back to external indexing and packages up the results. It expects to 0178 % find an 'if' field in the results struct as described for mpc above. 0179 % It also expects the results to contain solved branch flows and linear 0180 % constraints named 'iflims' which are used to populate output fields 0181 % in results.if. The optional args are not currently used. 0182 0183 %% define named indices into data matrices 0184 [F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ... 0185 TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ... 0186 ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch; 0187 0188 %% get internal ifmap 0189 ifmap = results.if.map; 0190 iflims = results.if.lims; 0191 0192 %%----- convert stuff back to external indexing ----- 0193 results.if.map = results.order.ext.ifmap; 0194 0195 %%----- results post-processing ----- 0196 ifidx = unique(iflims(:, 1)); %% interface number list 0197 nifs = length(ifidx); %% number of interfaces 0198 results.if.P = zeros(nifs, 1); 0199 for k = 1:nifs 0200 %% extract branch indices 0201 br = ifmap(ifmap(:, 1) == ifidx(k), 2); 0202 d = sign(br); 0203 br = abs(br); 0204 results.if.P(k) = sum( d .* results.branch(br, PF) ); 0205 end 0206 results.if.mu.l = results.lin.mu.l.iflims; 0207 results.if.mu.u = results.lin.mu.u.iflims; 0208 0209 0210 %%----- printpf ------------------------------------------------------ 0211 function results = userfcn_iflims_printpf(results, fd, mpopt, args) 0212 % 0213 % results = userfcn_iflims_printpf(results, fd, mpopt, args) 0214 % 0215 % This is the 'printpf' stage userfcn callback that pretty-prints the 0216 % results. It expects a results struct, a file descriptor and a MATPOWER 0217 % options vector. The optional args are not currently used. 0218 0219 %% define named indices into data matrices 0220 [F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ... 0221 TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ... 0222 ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch; 0223 0224 %%----- print results ----- 0225 OUT_ALL = mpopt(32); 0226 % ctol = mpopt(16); %% constraint violation tolerance 0227 ptol = 1e-6; %% tolerance for displaying shadow prices 0228 0229 if OUT_ALL ~= 0 0230 iflims = results.if.lims; 0231 fprintf(fd, '\n================================================================================'); 0232 fprintf(fd, '\n| Interface Flow Limits |'); 0233 fprintf(fd, '\n================================================================================'); 0234 fprintf(fd, '\n Interface Shadow Prc Lower Lim Flow Upper Lim Shadow Prc'); 0235 fprintf(fd, '\n # ($/MW) (MW) (MW) (MW) ($/MW) '); 0236 fprintf(fd, '\n---------- ---------- ---------- ---------- ---------- -----------'); 0237 ifidx = unique(iflims(:, 1)); %% interface number list 0238 nifs = length(ifidx); %% number of interfaces 0239 for k = 1:nifs 0240 fprintf(fd, '\n%6d ', iflims(k, 1)); 0241 if results.if.mu.l(k) > ptol 0242 fprintf(fd, '%14.3f', results.if.mu.l(k)); 0243 else 0244 fprintf(fd, ' - '); 0245 end 0246 fprintf(fd, '%12.2f%12.2f%12.2f', iflims(k, 2), results.if.P(k), iflims(k, 3)); 0247 if results.if.mu.u(k) > ptol 0248 fprintf(fd, '%13.3f', results.if.mu.u(k)); 0249 else 0250 fprintf(fd, ' - '); 0251 end 0252 end 0253 fprintf(fd, '\n'); 0254 end 0255 0256 0257 %%----- savecase ----------------------------------------------------- 0258 function mpc = userfcn_iflims_savecase(mpc, fd, prefix, args) 0259 % 0260 % mpc = userfcn_iflims_savecase(mpc, fd, mpopt, args) 0261 % 0262 % This is the 'savecase' stage userfcn callback that prints the M-file 0263 % code to save the 'if' field in the case file. It expects a 0264 % MATPOWER case struct (mpc), a file descriptor and variable prefix 0265 % (usually 'mpc.'). The optional args are not currently used. 0266 0267 ifmap = mpc.if.map; 0268 iflims = mpc.if.lims; 0269 0270 fprintf(fd, '\n%%%%----- Interface Flow Limit Data -----%%%%\n'); 0271 fprintf(fd, '%%%% interface<->branch map data\n'); 0272 fprintf(fd, '%%\tifnum\tbranchidx (negative defines opposite direction)\n'); 0273 fprintf(fd, '%sif.map = [\n', prefix); 0274 fprintf(fd, '\t%d\t%d;\n', ifmap'); 0275 fprintf(fd, '];\n'); 0276 0277 fprintf(fd, '\n%%%% interface flow limit data (based on DC model)\n'); 0278 fprintf(fd, '%%%% (lower limit should be negative for opposite direction)\n'); 0279 fprintf(fd, '%%\tifnum\tlower\tupper\n'); 0280 fprintf(fd, '%sif.lims = [\n', prefix); 0281 fprintf(fd, '\t%d\t%g\t%g;\n', iflims'); 0282 fprintf(fd, '];\n'); 0283 0284 %% save output fields for solved case 0285 if isfield(mpc.if, 'P') 0286 if exist('serialize', 'file') == 2 0287 fprintf(fd, '\n%%%% solved values\n'); 0288 fprintf(fd, '%sif.P = %s\n', prefix, serialize(mpc.if.P)); 0289 fprintf(fd, '%sif.mu.l = %s\n', prefix, serialize(mpc.if.mu.l)); 0290 fprintf(fd, '%sif.mu.u = %s\n', prefix, serialize(mpc.if.mu.u)); 0291 else 0292 url = 'http://www.mathworks.com/matlabcentral/fileexchange/12063'; 0293 warning('MATPOWER:serialize', ... 0294 'userfcn_iflims_savecase: Cannot save the ''iflims'' output fields without the ''serialize'' function, which is available as a free download from:\n<%s>\n\n', url); 0295 end 0296 end