EXT2INT Converts external to internal indexing. This function performs several different tasks, depending on the arguments passed. 1. [I2E, BUS, GEN, BRANCH, AREAS] = EXT2INT(BUS, GEN, BRANCH, AREAS) [I2E, BUS, GEN, BRANCH] = EXT2INT(BUS, GEN, BRANCH) If the first argument is a matrix, it simply converts from (possibly non-consecutive) external bus numbers to consecutive internal bus numbers which start at 1. Changes are made to BUS, GEN, BRANCH and optionally AREAS matrices, which are returned along with a vector of indices I2E that can be passed to INT2EXT to perform the reverse conversion, where EXTERNAL_BUS_NUMBER = I2E(INTERNAL_BUS_NUMBER) Examples: [i2e, bus, gen, branch, areas] = ext2int(bus, gen, branch, areas); [i2e, bus, gen, branch] = ext2int(bus, gen, branch); 2. MPC = EXT2INT(MPC) If the input is a single MATPOWER case struct, then all isolated buses, off-line generators and branches are removed along with any generators, branches or areas connected to isolated buses. Then the buses are renumbered consecutively, beginning at 1, and the generators are sorted by increasing bus number. All of the related indexing information and the original data matrices are stored in an 'order' field in the struct to be used by INT2EXT to perform the reverse conversions. If the case is already using internal numbering it is returned unchanged. Example: mpc = ext2int(mpc); 3. VAL = EXT2INT(MPC, VAL, ORDERING) VAL = EXT2INT(MPC, VAL, ORDERING, DIM) MPC = EXT2INT(MPC, FIELD, ORDERING) MPC = EXT2INT(MPC, FIELD, ORDERING, DIM) When given a case struct that has already been converted to internal indexing, this function can be used to convert other data structures as well by passing in 2 or 3 extra parameters in addition to the case struct. If the value passed in the 2nd argument is a column vector, it will be converted according to the ORDERING specified by the 3rd argument (described below). If VAL is an n-dimensional matrix, then the optional 4th argument (DIM, default = 1) can be used to specify which dimension to reorder. The return value in this case is the value passed in, converted to internal indexing. If the 2nd argument is a string or cell array of strings, it specifies a field in the case struct whose value should be converted as described above. In this case, the converted value is stored back in the specified field, the original value is saved for later use and the updated case struct is returned. If FIELD is a cell array of strings, they specify nested fields. The 3rd argument, ORDERING, is used to indicate whether the data corresponds to bus-, gen- or branch-ordered data. It can be one of the following three strings: 'bus', 'gen' or 'branch'. For data structures with multiple blocks of data, ordered by bus, gen or branch, they can be converted with a single call by specifying ORDERING as a cell array of strings. Any extra elements, rows, columns, etc. beyond those indicated in ORDERING, are not disturbed. Examples: A_int = ext2int(mpc, A_ext, {'bus','bus','gen','gen'}, 2); Converts an A matrix for user-supplied OPF constraints from external to internal ordering, where the columns of the A matrix correspond to bus voltage angles, then voltage magnitudes, then generator real power injections and finally generator reactive power injections. gencost_int = ext2int(mpc, gencost_ext, {'gen','gen'}, 1); Converts a GENCOST matrix that has both real and reactive power costs (in rows 1--ng and ng+1--2*ng, respectively). mpc = ext2int(mpc, {'reserves', 'cost'}, 'gen'); Reorders rows of mpc.reserves.cost to match internal generator ordering. mpc = ext2int(mpc, {'reserves', 'zones'}, 'gen', 2); Reorders columns of mpc.reserves.zones to match internal generator ordering. The 'order' field of MPC used to store the indexing information needed for subsequent internal to external conversion is structured as: order state 'i' | 'e' ext | int areas bus branch gen gencost A N bus e2i i2e status on off gen e2i i2e status on off branch status on off areas status on off See also INT2EXT.
0001 function [i2e, bus, gen, branch, areas] = ext2int(bus, gen, branch, areas) 0002 %EXT2INT Converts external to internal indexing. 0003 % 0004 % This function performs several different tasks, depending on the 0005 % arguments passed. 0006 % 0007 % 1. [I2E, BUS, GEN, BRANCH, AREAS] = EXT2INT(BUS, GEN, BRANCH, AREAS) 0008 % [I2E, BUS, GEN, BRANCH] = EXT2INT(BUS, GEN, BRANCH) 0009 % 0010 % If the first argument is a matrix, it simply converts from (possibly 0011 % non-consecutive) external bus numbers to consecutive internal bus 0012 % numbers which start at 1. Changes are made to BUS, GEN, BRANCH and 0013 % optionally AREAS matrices, which are returned along with a vector of 0014 % indices I2E that can be passed to INT2EXT to perform the reverse 0015 % conversion, where EXTERNAL_BUS_NUMBER = I2E(INTERNAL_BUS_NUMBER) 0016 % 0017 % Examples: 0018 % [i2e, bus, gen, branch, areas] = ext2int(bus, gen, branch, areas); 0019 % [i2e, bus, gen, branch] = ext2int(bus, gen, branch); 0020 % 0021 % 2. MPC = EXT2INT(MPC) 0022 % 0023 % If the input is a single MATPOWER case struct, then all isolated 0024 % buses, off-line generators and branches are removed along with any 0025 % generators, branches or areas connected to isolated buses. Then the 0026 % buses are renumbered consecutively, beginning at 1, and the 0027 % generators are sorted by increasing bus number. All of the related 0028 % indexing information and the original data matrices are stored in 0029 % an 'order' field in the struct to be used by INT2EXT to perform 0030 % the reverse conversions. If the case is already using internal 0031 % numbering it is returned unchanged. 0032 % 0033 % Example: 0034 % mpc = ext2int(mpc); 0035 % 0036 % 3. VAL = EXT2INT(MPC, VAL, ORDERING) 0037 % VAL = EXT2INT(MPC, VAL, ORDERING, DIM) 0038 % MPC = EXT2INT(MPC, FIELD, ORDERING) 0039 % MPC = EXT2INT(MPC, FIELD, ORDERING, DIM) 0040 % 0041 % When given a case struct that has already been converted to 0042 % internal indexing, this function can be used to convert other data 0043 % structures as well by passing in 2 or 3 extra parameters in 0044 % addition to the case struct. If the value passed in the 2nd 0045 % argument is a column vector, it will be converted according to the 0046 % ORDERING specified by the 3rd argument (described below). If VAL 0047 % is an n-dimensional matrix, then the optional 4th argument (DIM, 0048 % default = 1) can be used to specify which dimension to reorder. 0049 % The return value in this case is the value passed in, converted 0050 % to internal indexing. 0051 % 0052 % If the 2nd argument is a string or cell array of strings, it 0053 % specifies a field in the case struct whose value should be 0054 % converted as described above. In this case, the converted value 0055 % is stored back in the specified field, the original value is 0056 % saved for later use and the updated case struct is returned. 0057 % If FIELD is a cell array of strings, they specify nested fields. 0058 % 0059 % The 3rd argument, ORDERING, is used to indicate whether the data 0060 % corresponds to bus-, gen- or branch-ordered data. It can be one 0061 % of the following three strings: 'bus', 'gen' or 'branch'. For 0062 % data structures with multiple blocks of data, ordered by bus, 0063 % gen or branch, they can be converted with a single call by 0064 % specifying ORDERING as a cell array of strings. 0065 % 0066 % Any extra elements, rows, columns, etc. beyond those indicated 0067 % in ORDERING, are not disturbed. 0068 % 0069 % Examples: 0070 % A_int = ext2int(mpc, A_ext, {'bus','bus','gen','gen'}, 2); 0071 % 0072 % Converts an A matrix for user-supplied OPF constraints from 0073 % external to internal ordering, where the columns of the A 0074 % matrix correspond to bus voltage angles, then voltage 0075 % magnitudes, then generator real power injections and finally 0076 % generator reactive power injections. 0077 % 0078 % gencost_int = ext2int(mpc, gencost_ext, {'gen','gen'}, 1); 0079 % 0080 % Converts a GENCOST matrix that has both real and reactive power 0081 % costs (in rows 1--ng and ng+1--2*ng, respectively). 0082 % 0083 % mpc = ext2int(mpc, {'reserves', 'cost'}, 'gen'); 0084 % 0085 % Reorders rows of mpc.reserves.cost to match internal generator 0086 % ordering. 0087 % 0088 % mpc = ext2int(mpc, {'reserves', 'zones'}, 'gen', 2); 0089 % 0090 % Reorders columns of mpc.reserves.zones to match internal 0091 % generator ordering. 0092 % 0093 % The 'order' field of MPC used to store the indexing information 0094 % needed for subsequent internal to external conversion is structured 0095 % as: 0096 % 0097 % order 0098 % state 'i' | 'e' 0099 % ext | int 0100 % areas 0101 % bus 0102 % branch 0103 % gen 0104 % gencost 0105 % A 0106 % N 0107 % bus 0108 % e2i 0109 % i2e 0110 % status 0111 % on 0112 % off 0113 % gen 0114 % e2i 0115 % i2e 0116 % status 0117 % on 0118 % off 0119 % branch 0120 % status 0121 % on 0122 % off 0123 % areas 0124 % status 0125 % on 0126 % off 0127 % 0128 % See also INT2EXT. 0129 0130 % MATPOWER 0131 % $Id: ext2int.m,v 1.19 2010/04/26 19:45:25 ray Exp $ 0132 % by Ray Zimmerman, PSERC Cornell 0133 % Copyright (c) 1996-2010 by Power System Engineering Research Center (PSERC) 0134 % 0135 % This file is part of MATPOWER. 0136 % See http://www.pserc.cornell.edu/matpower/ for more info. 0137 % 0138 % MATPOWER is free software: you can redistribute it and/or modify 0139 % it under the terms of the GNU General Public License as published 0140 % by the Free Software Foundation, either version 3 of the License, 0141 % or (at your option) any later version. 0142 % 0143 % MATPOWER is distributed in the hope that it will be useful, 0144 % but WITHOUT ANY WARRANTY; without even the implied warranty of 0145 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0146 % GNU General Public License for more details. 0147 % 0148 % You should have received a copy of the GNU General Public License 0149 % along with MATPOWER. If not, see <http://www.gnu.org/licenses/>. 0150 % 0151 % Additional permission under GNU GPL version 3 section 7 0152 % 0153 % If you modify MATPOWER, or any covered work, to interface with 0154 % other modules (such as MATLAB code and MEX-files) available in a 0155 % MATLAB(R) or comparable environment containing parts covered 0156 % under other licensing terms, the licensors of MATPOWER grant 0157 % you additional permission to convey the resulting work. 0158 0159 if isstruct(bus) 0160 mpc = bus; 0161 if nargin == 1 0162 first = ~isfield(mpc, 'order'); 0163 if first || mpc.order.state == 'e' 0164 %% define names for columns to data matrices 0165 [PQ, PV, REF, NONE, BUS_I, BUS_TYPE] = idx_bus; 0166 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS] = idx_gen; 0167 [F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ... 0168 TAP, SHIFT, BR_STATUS] = idx_brch; 0169 [AREA_I, PRICE_REF_BUS] = idx_area; 0170 0171 %% initialize order 0172 if first 0173 status = struct('on', [], ... 0174 'off', [] ); 0175 tmp = struct( ... 0176 'e2i', [], ... 0177 'i2e', [], ... 0178 'status', status ... 0179 ); 0180 o = struct( ... 0181 'ext', struct( ... 0182 'bus', [], ... 0183 'branch', [], ... 0184 'gen', [] ... 0185 ), ... 0186 'bus', tmp, ... 0187 'gen', tmp, ... 0188 'branch', struct('status', status) ... 0189 ); 0190 else 0191 o = mpc.order; 0192 end 0193 0194 %% sizes 0195 nb = size(mpc.bus, 1); 0196 ng = size(mpc.gen, 1); 0197 ng0 = ng; 0198 if isfield(mpc, 'A') && size(mpc.A, 2) < 2*nb + 2*ng 0199 dc = 1; 0200 elseif isfield(mpc, 'N') && size(mpc.N, 2) < 2*nb + 2*ng 0201 dc = 1; 0202 else 0203 dc = 0; 0204 end 0205 0206 %% save data matrices with external ordering 0207 o.ext.bus = mpc.bus; 0208 o.ext.branch = mpc.branch; 0209 o.ext.gen = mpc.gen; 0210 if isfield(mpc, 'areas') 0211 if isempty(mpc.areas) %% if areas field is empty 0212 mpc = rmfield(mpc, 'areas'); %% delete it (so it gets ignored) 0213 else %% otherwise 0214 o.ext.areas = mpc.areas; %% save it 0215 end 0216 end 0217 0218 %% check that all buses have a valid BUS_TYPE 0219 bt = mpc.bus(:, BUS_TYPE); 0220 err = find(~(bt == PQ | bt == PV | bt == REF | bt == NONE)); 0221 if ~isempty(err) 0222 error('ext2int: bus %d has an invalid BUS_TYPE', err); 0223 end 0224 0225 %% determine which buses, branches, gens are connected & in-service 0226 n2i = sparse(mpc.bus(:, BUS_I), ones(nb, 1), 1:nb, max(mpc.bus(:, BUS_I)), 1); 0227 bs = (bt ~= NONE); %% bus status 0228 o.bus.status.on = find( bs ); %% connected 0229 o.bus.status.off = find( ~bs ); %% isolated 0230 gs = ( mpc.gen(:, GEN_STATUS) > 0 & ... %% gen status 0231 bs(n2i(mpc.gen(:, GEN_BUS))) ); 0232 o.gen.status.on = find( gs ); %% on and connected 0233 o.gen.status.off = find( ~gs ); %% off or isolated 0234 brs = ( mpc.branch(:, BR_STATUS) & ... %% branch status 0235 bs(n2i(mpc.branch(:, F_BUS))) & ... 0236 bs(n2i(mpc.branch(:, T_BUS))) ); 0237 o.branch.status.on = find( brs ); %% on and connected 0238 o.branch.status.off = find( ~brs ); 0239 if isfield(mpc, 'areas') 0240 as = bs(n2i(mpc.areas(:, PRICE_REF_BUS))); 0241 o.areas.status.on = find( as ); 0242 o.areas.status.off = find( ~as ); 0243 end 0244 0245 %% delete stuff that is "out" 0246 if ~isempty(o.bus.status.off) 0247 mpc.bus(o.bus.status.off, :) = []; 0248 end 0249 if ~isempty(o.branch.status.off) 0250 mpc.branch(o.branch.status.off, :) = []; 0251 end 0252 if ~isempty(o.gen.status.off) 0253 mpc.gen(o.gen.status.off, :) = []; 0254 end 0255 if isfield(mpc, 'areas') && ~isempty(o.areas.status.off) 0256 mpc.areas(o.areas.status.off, :) = []; 0257 end 0258 0259 %% update size 0260 nb = size(mpc.bus, 1); 0261 0262 %% apply consecutive bus numbering 0263 o.bus.i2e = mpc.bus(:, BUS_I); 0264 o.bus.e2i = sparse(max(o.bus.i2e), 1); 0265 o.bus.e2i(o.bus.i2e) = (1:nb)'; 0266 mpc.bus(:, BUS_I) = o.bus.e2i( mpc.bus(:, BUS_I) ); 0267 mpc.gen(:, GEN_BUS) = o.bus.e2i( mpc.gen(:, GEN_BUS) ); 0268 mpc.branch(:, F_BUS) = o.bus.e2i( mpc.branch(:, F_BUS) ); 0269 mpc.branch(:, T_BUS) = o.bus.e2i( mpc.branch(:, T_BUS) ); 0270 if isfield(mpc, 'areas') 0271 mpc.areas(:, PRICE_REF_BUS) = o.bus.e2i( mpc.areas(:, PRICE_REF_BUS) ); 0272 end 0273 0274 %% reorder gens in order of increasing bus number 0275 [tmp, o.gen.e2i] = sort(mpc.gen(:, GEN_BUS)); 0276 [tmp, o.gen.i2e] = sort(o.gen.e2i); 0277 mpc.gen = mpc.gen(o.gen.e2i, :); 0278 0279 if isfield(o, 'int') 0280 o = rmfield(o, 'int'); 0281 end 0282 o.state = 'i'; 0283 mpc.order = o; 0284 0285 %% update gencost, A and N 0286 if isfield(mpc, 'gencost') 0287 ordering = {'gen'}; %% Pg cost only 0288 if size(mpc.gencost, 1) == 2*ng0 0289 ordering{2} = 'gen'; %% include Qg cost 0290 end 0291 mpc = ext2int(mpc, 'gencost', ordering); 0292 end 0293 if isfield(mpc, 'A') || isfield(mpc, 'N') 0294 if dc 0295 ordering = {'bus', 'gen'}; 0296 else 0297 ordering = {'bus', 'bus', 'gen', 'gen'}; 0298 end 0299 end 0300 if isfield(mpc, 'A') 0301 mpc = ext2int(mpc, 'A', ordering, 2); 0302 end 0303 if isfield(mpc, 'N') 0304 mpc = ext2int(mpc, 'N', ordering, 2); 0305 end 0306 0307 %% execute userfcn callbacks for 'ext2int' stage 0308 if isfield(mpc, 'userfcn') 0309 mpc = run_userfcn(mpc.userfcn, 'ext2int', mpc); 0310 end 0311 end 0312 0313 i2e = mpc; 0314 else %% convert extra data 0315 ordering = branch; %% rename argument 0316 if nargin < 4 0317 dim = 1; 0318 else 0319 dim = areas; %% rename argument 0320 end 0321 if ischar(gen) || iscell(gen) %% field 0322 field = gen; %% rename argument 0323 if ischar(field) 0324 mpc.order.ext.(field) = mpc.(field); 0325 mpc.(field) = ext2int(mpc, mpc.(field), ordering, dim); 0326 else 0327 for k = 1:length(field) 0328 s(k).type = '.'; 0329 s(k).subs = field{k}; 0330 end 0331 mpc.order.ext = subsasgn(mpc.order.ext, s, subsref(mpc, s)); 0332 mpc = subsasgn(mpc, s, ... 0333 ext2int(mpc, subsref(mpc, s), ordering, dim)); 0334 end 0335 i2e = mpc; 0336 else %% value 0337 val = gen; %% rename argument 0338 o = mpc.order; 0339 if ischar(ordering) %% single set 0340 if strcmp(ordering, 'gen') 0341 idx = o.(ordering).status.on(o.(ordering).e2i); 0342 else 0343 idx = o.(ordering).status.on; 0344 end 0345 i2e = get_reorder(val, idx, dim); 0346 else %% multiple sets 0347 b = 0; %% base 0348 for k = 1:length(ordering) 0349 n = size(o.ext.(ordering{k}), 1); 0350 v = get_reorder(val, b+(1:n), dim); 0351 new_v{k} = ext2int(mpc, v, ordering{k}, dim); 0352 b = b + n; 0353 end 0354 n = size(val, dim); 0355 if n > b %% the rest 0356 v = get_reorder(val, b+1:n, dim); 0357 new_v{length(new_v)+1} = v; 0358 end 0359 i2e = cat(dim, new_v{:}); 0360 end 0361 end 0362 end 0363 else %% old form 0364 %% define names for columns to data matrices 0365 [PQ, PV, REF, NONE, BUS_I] = idx_bus; 0366 [GEN_BUS] = idx_gen; 0367 [F_BUS, T_BUS] = idx_brch; 0368 [AREA_I, PRICE_REF_BUS] = idx_area; 0369 0370 %% create map of external bus numbers to bus indices 0371 i2e = bus(:, BUS_I); 0372 e2i = sparse(max(i2e), 1); 0373 e2i(i2e) = (1:size(bus, 1))'; 0374 0375 %% renumber buses consecutively 0376 bus(:, BUS_I) = e2i( bus(:, BUS_I) ); 0377 gen(:, GEN_BUS) = e2i( gen(:, GEN_BUS) ); 0378 branch(:, F_BUS) = e2i( branch(:, F_BUS) ); 0379 branch(:, T_BUS) = e2i( branch(:, T_BUS) ); 0380 if nargin > 3 && nargout > 4 && ~isempty(areas) 0381 areas(:, PRICE_REF_BUS) = e2i( areas(:, PRICE_REF_BUS) ); 0382 end 0383 end