function mpck = extract_islands(mpc, groups, k, custom) EXTRACT_ISLANDS Extracts each island in a network with islands MPC_ARRAY = EXTRACT_ISLANDS(MPC) MPC_ARRAY = EXTRACT_ISLANDS(MPC, GROUPS) MPC_K = EXTRACT_ISLANDS(MPC, K) MPC_K = EXTRACT_ISLANDS(MPC, GROUPS, K) MPC_K = EXTRACT_ISLANDS(MPC, K, CUSTOM) MPC_K = EXTRACT_ISLANDS(MPC, GROUPS, K, CUSTOM) Returns a cell array of MATPOWER case structs for each island in the input case struct. If the optional second argument is a cell array GROUPS it is assumed to be a cell array of vectors of bus indices for each island (as returned by FIND_ISLANDS). Providing the GROUPS avoids the need for another traversal of the network connectivity and can save a significant amount of time on very large systems. If an additional argument K is included, it indicates which island(s) to return and the return value is a single case struct, rather than a cell array. If K is a scalar or vector, it it specifies the index(indices) of the island(s) to include in the resulting case file. K can also be the string 'all' which will include all islands. This is the same as simply eliminating all isolated buses. A final optional argument CUSTOM is a struct that can be used to indicate custom fields of MPC from which to extract data corresponding to buses generators, branches or DC lines. It has the following structure: CUSTOM.<ORDERING>{DIM} = FIELDS <ORDERING> is either 'bus', 'gen', 'branch' or 'dcline' and indicates that dimension DIM of FIELDS has dimensions corresponding to this <ORDERING> and should have the appropriate dimension extracted as well. FIELDS is a cell array, where each element is either a single string (field name of MPC) or a cell array of strings (nested fields of MPC). Examples: Extract each island into it's own case struct: mpc_list = extract_islands(mpc); Extract the 2nd (that is, 2nd largest) island: mpc2 = extract_islands(mpc, 2); Extract the first and 3rd islands without a re-traverals of the network: groups = find_islands(mpc); mpc1 = extract_islands(mpc, groups, 1); mpc3 = extract_islands(mpc, groups, 3); Extract the 2nd island, including custom fields, where mpc.bus_name{b} contains the name of bus b, and mpc.genfuel{g}, mpc.emissions.rate(g, :), and mpc.genloc(:, g) contain, respectively, the generator's fuel type, emission rates and location coordinates: custom.bus{1} = {'bus_name'}; custom.gen{1} = {'genfuel', {'emissions', 'rate'}}; custom.gen{2} = {'genloc'}; mpc = extract_islands(mpc, 1, custom); See also FIND_ISLANDS, CASE_INFO, CONNECTED_COMPONENTS.
0001 function mpck = extract_islands(mpc, varargin) 0002 %function mpck = extract_islands(mpc, groups, k, custom) 0003 %EXTRACT_ISLANDS Extracts each island in a network with islands 0004 % MPC_ARRAY = EXTRACT_ISLANDS(MPC) 0005 % MPC_ARRAY = EXTRACT_ISLANDS(MPC, GROUPS) 0006 % MPC_K = EXTRACT_ISLANDS(MPC, K) 0007 % MPC_K = EXTRACT_ISLANDS(MPC, GROUPS, K) 0008 % MPC_K = EXTRACT_ISLANDS(MPC, K, CUSTOM) 0009 % MPC_K = EXTRACT_ISLANDS(MPC, GROUPS, K, CUSTOM) 0010 % 0011 % Returns a cell array of MATPOWER case structs for each island in 0012 % the input case struct. If the optional second argument is a cell 0013 % array GROUPS it is assumed to be a cell array of vectors of bus 0014 % indices for each island (as returned by FIND_ISLANDS). Providing 0015 % the GROUPS avoids the need for another traversal of the network 0016 % connectivity and can save a significant amount of time on very 0017 % large systems. If an additional argument K is included, it indicates 0018 % which island(s) to return and the return value is a single case 0019 % struct, rather than a cell array. If K is a scalar or vector, it 0020 % it specifies the index(indices) of the island(s) to include in 0021 % the resulting case file. K can also be the string 'all' which 0022 % will include all islands. This is the same as simply eliminating 0023 % all isolated buses. 0024 % 0025 % A final optional argument CUSTOM is a struct that can be used to 0026 % indicate custom fields of MPC from which to extract data 0027 % corresponding to buses generators, branches or DC lines. It has 0028 % the following structure: 0029 % 0030 % CUSTOM.<ORDERING>{DIM} = FIELDS 0031 % 0032 % <ORDERING> is either 'bus', 'gen', 'branch' or 'dcline' and 0033 % indicates that dimension DIM of FIELDS has dimensions 0034 % corresponding to this <ORDERING> and should have the appropriate 0035 % dimension extracted as well. FIELDS is a cell array, where 0036 % each element is either a single string (field name of MPC) or 0037 % a cell array of strings (nested fields of MPC). 0038 % 0039 % Examples: 0040 % Extract each island into it's own case struct: 0041 % mpc_list = extract_islands(mpc); 0042 % 0043 % Extract the 2nd (that is, 2nd largest) island: 0044 % mpc2 = extract_islands(mpc, 2); 0045 % 0046 % Extract the first and 3rd islands without a re-traverals of the 0047 % network: 0048 % groups = find_islands(mpc); 0049 % mpc1 = extract_islands(mpc, groups, 1); 0050 % mpc3 = extract_islands(mpc, groups, 3); 0051 % 0052 % Extract the 2nd island, including custom fields, where 0053 % mpc.bus_name{b} contains the name of bus b, and mpc.genfuel{g}, 0054 % mpc.emissions.rate(g, :), and mpc.genloc(:, g) contain, 0055 % respectively, the generator's fuel type, emission rates and 0056 % location coordinates: 0057 % custom.bus{1} = {'bus_name'}; 0058 % custom.gen{1} = {'genfuel', {'emissions', 'rate'}}; 0059 % custom.gen{2} = {'genloc'}; 0060 % mpc = extract_islands(mpc, 1, custom); 0061 % 0062 % See also FIND_ISLANDS, CASE_INFO, CONNECTED_COMPONENTS. 0063 0064 % MATPOWER 0065 % Copyright (c) 2012-2016, Power Systems Engineering Research Center (PSERC) 0066 % by Ray Zimmerman, PSERC Cornell 0067 % 0068 % This file is part of MATPOWER. 0069 % Covered by the 3-clause BSD License (see LICENSE file for details). 0070 % See http://www.pserc.cornell.edu/matpower/ for more info. 0071 0072 %% define named indices into data matrices 0073 [PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ... 0074 VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus; 0075 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ... 0076 MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ... 0077 QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen; 0078 [F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ... 0079 TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ... 0080 ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch; 0081 c = idx_dcline; 0082 0083 %% set up connectivity matrices 0084 nb = size(mpc.bus, 1); %% number of buses 0085 nl = size(mpc.branch, 1); %% number of branches 0086 if isfield(mpc, 'dcline') %% number of DC lines 0087 ndc = size(mpc.dcline, 1); 0088 else 0089 ndc = 0; 0090 end 0091 ng = size(mpc.gen, 1); %% number of dispatchable injections 0092 0093 e2i = sparse(mpc.bus(:, BUS_I), ones(nb, 1), 1:nb, max(mpc.bus(:, BUS_I)), 1); 0094 C_on = sparse(1:nl, e2i(mpc.branch(:, F_BUS)), -mpc.branch(:, BR_STATUS), nl, nb) + ... 0095 sparse(1:nl, e2i(mpc.branch(:, T_BUS)), mpc.branch(:, BR_STATUS), nl, nb); 0096 C = sparse(1:nl, e2i(mpc.branch(:, F_BUS)), -1, nl, nb) + ... 0097 sparse(1:nl, e2i(mpc.branch(:, T_BUS)), 1, nl, nb); 0098 if ndc 0099 Cdc_on = sparse(1:ndc, e2i(mpc.dcline(:, c.F_BUS)), -mpc.dcline(:, c.BR_STATUS), ndc, nb) + ... 0100 sparse(1:ndc, e2i(mpc.dcline(:, c.T_BUS)), mpc.dcline(:, c.BR_STATUS), ndc, nb); 0101 Cdc = sparse(1:ndc, e2i(mpc.dcline(:, c.F_BUS)), -1, ndc, nb) + ... 0102 sparse(1:ndc, e2i(mpc.dcline(:, c.T_BUS)), 1, ndc, nb); 0103 end 0104 Cg_on = sparse(1:ng, e2i(mpc.gen(:, GEN_BUS)), mpc.gen(:, GEN_STATUS), ng, nb); 0105 Cg = sparse(1:ng, e2i(mpc.gen(:, GEN_BUS)), 1, ng, nb); 0106 0107 if nnz(C) 0108 n = length(varargin); 0109 if n >= 1 && iscell(varargin{1}) 0110 groups = varargin{1}; 0111 z = 1; 0112 else 0113 groups = {}; 0114 z = 0; 0115 end 0116 if z+1 <= n 0117 k = varargin{z+1}; 0118 else 0119 k = []; 0120 end 0121 if z+2 <= n 0122 custom = varargin{z+2}; 0123 else 0124 custom = struct(); 0125 end 0126 0127 %% find islands, if not provided 0128 if isempty(groups) 0129 groups = connected_components(C_on); 0130 end 0131 0132 %% check inputs 0133 if isempty(k) 0134 g1 = 1; 0135 gn = length(groups); 0136 else 0137 if ischar(k) 0138 if strcmp(upper(k), 'ALL') 0139 k = (1:length(groups))'; 0140 else 0141 error('extract_islands: K = ''%s'' is not a valid input', k); 0142 end 0143 end 0144 if max(k) > length(groups) 0145 error('extract_islands: cannot extract island %d, network has only %d islands', ... 0146 max(k), length(groups)); 0147 end 0148 if length(k) > 1 %% extract multiple islands as one case 0149 tmpgroup = groups{k(1)}; 0150 for j = 2:length(k) 0151 tmpgroup = union(tmpgroup, groups{k(j)}); 0152 end 0153 groups = { tmpgroup }; 0154 g1 = 1; 0155 gn = 1; 0156 else %% extract single island 0157 g1 = k; 0158 gn = k; 0159 end 0160 end 0161 0162 %% extract islands 0163 for i = g1:gn 0164 kk = i-g1+1; 0165 b = groups{i}; %% buses in group i 0166 %% branches with both ends in group i 0167 ibr = find(sum(abs(C(:, b)), 2) & ~sum(C(:, b), 2)); 0168 ig = find(sum(Cg(:, b), 2)); %% gens in group i 0169 %% DC lines with both ends in group i 0170 if ndc 0171 idc = find(sum(abs(Cdc(:, b)), 2) & ~sum(Cdc(:, b), 2)); 0172 else 0173 idc = []; 0174 end 0175 0176 mpck{kk} = mpc; 0177 mpck{kk}.bus = mpc.bus(b, :); 0178 mpck{kk}.branch = mpc.branch(ibr, :); 0179 mpck{kk}.gen = mpc.gen(ig, :); 0180 if isfield(mpck{kk}, 'gencost') 0181 if size(mpck{kk}.gencost, 1) == 2*ng 0182 mpck{kk}.gencost = mpc.gencost([ig; ng+ig], :); 0183 else 0184 mpck{kk}.gencost = mpc.gencost(ig, :); 0185 end 0186 end 0187 if ndc 0188 mpck{kk}.dcline = mpc.dcline(idc, :); 0189 if isfield(mpck{kk}, 'dclinecost') 0190 mpck{kk}.dclinecost = mpc.dclinecost(idc, :); 0191 end 0192 end 0193 0194 %% handle custom fields 0195 orderings = {'bus', 'gen', 'branch', 'dcline'}; 0196 indexes = {b, ig, ibr, idc}; 0197 0198 for n = 1:length(orderings) 0199 ord = orderings{n}; 0200 if isfield(custom, ord) 0201 for dim = 1:length(custom.(ord)) 0202 for j = 1:length(custom.(ord){dim}) 0203 s = []; 0204 field = custom.(ord){dim}{j}; 0205 if ischar(field) 0206 field = { field }; 0207 end 0208 0209 tmp = mpck{kk}; %% check this for presence of sub-fields 0210 skip = 0; 0211 for i = 1:length(field) 0212 s(i).type = '.'; 0213 s(i).subs = field{i}; 0214 if isfield(tmp, field{i}) && ~isempty(tmp.(field{i})) 0215 %% have sub-field, continue 0216 tmp = tmp.(field{i}); 0217 else 0218 %% sub-field doesn't exist, skip it 0219 skip = 1; 0220 break; 0221 end 0222 end 0223 if ~skip 0224 mpck{kk} = subsasgn(mpck{kk}, s, get_reorder(subsref(mpck{kk}, s), indexes{n}, dim)); 0225 end 0226 end 0227 end 0228 end 0229 end 0230 end 0231 0232 %% convert from cell array to single MATPOWER case struct as appropriate 0233 if ~isempty(k) 0234 mpck = mpck{1}; 0235 end 0236 else 0237 mpck = []; 0238 end