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_label{b} contains a label for bus b, and mpc.gen_name{g}, mpc.emissions.rate(g, :), and mpc.genloc(:, g) contain, respectively, the generator's name, emission rates and location coordinates: custom.bus{1} = {'bus_label'}; custom.gen{1} = {'gen_name', {'emissions', 'rate'}}; custom.gen{2} = {'genloc'}; mpc = extract_islands(mpc, 1, custom); Note: Fields bus_name, gentype and genfuel are handled automatically and do not need to be included in 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_label{b} contains a label for bus b, and mpc.gen_name{g}, 0054 % mpc.emissions.rate(g, :), and mpc.genloc(:, g) contain, 0055 % respectively, the generator's name, emission rates and 0056 % location coordinates: 0057 % custom.bus{1} = {'bus_label'}; 0058 % custom.gen{1} = {'gen_name', {'emissions', 'rate'}}; 0059 % custom.gen{2} = {'genloc'}; 0060 % mpc = extract_islands(mpc, 1, custom); 0061 % 0062 % Note: Fields bus_name, gentype and genfuel are handled automatically 0063 % and do not need to be included in custom. 0064 % 0065 % See also FIND_ISLANDS, CASE_INFO, CONNECTED_COMPONENTS. 0066 0067 % MATPOWER 0068 % Copyright (c) 2012-2016, Power Systems Engineering Research Center (PSERC) 0069 % by Ray Zimmerman, PSERC Cornell 0070 % 0071 % This file is part of MATPOWER. 0072 % Covered by the 3-clause BSD License (see LICENSE file for details). 0073 % See https://matpower.org for more info. 0074 0075 %% define named indices into data matrices 0076 [PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ... 0077 VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus; 0078 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ... 0079 MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ... 0080 QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen; 0081 [F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ... 0082 TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ... 0083 ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch; 0084 c = idx_dcline; 0085 0086 %% set up connectivity matrices 0087 nb = size(mpc.bus, 1); %% number of buses 0088 nl = size(mpc.branch, 1); %% number of branches 0089 if isfield(mpc, 'dcline') %% number of DC lines 0090 ndc = size(mpc.dcline, 1); 0091 else 0092 ndc = 0; 0093 end 0094 ng = size(mpc.gen, 1); %% number of dispatchable injections 0095 0096 e2i = sparse(mpc.bus(:, BUS_I), ones(nb, 1), 1:nb, max(mpc.bus(:, BUS_I)), 1); 0097 C_on = sparse(1:nl, e2i(mpc.branch(:, F_BUS)), -mpc.branch(:, BR_STATUS), nl, nb) + ... 0098 sparse(1:nl, e2i(mpc.branch(:, T_BUS)), mpc.branch(:, BR_STATUS), nl, nb); 0099 C = sparse(1:nl, e2i(mpc.branch(:, F_BUS)), -1, nl, nb) + ... 0100 sparse(1:nl, e2i(mpc.branch(:, T_BUS)), 1, nl, nb); 0101 if ndc 0102 Cdc_on = sparse(1:ndc, e2i(mpc.dcline(:, c.F_BUS)), -mpc.dcline(:, c.BR_STATUS), ndc, nb) + ... 0103 sparse(1:ndc, e2i(mpc.dcline(:, c.T_BUS)), mpc.dcline(:, c.BR_STATUS), ndc, nb); 0104 Cdc = sparse(1:ndc, e2i(mpc.dcline(:, c.F_BUS)), -1, ndc, nb) + ... 0105 sparse(1:ndc, e2i(mpc.dcline(:, c.T_BUS)), 1, ndc, nb); 0106 end 0107 Cg_on = sparse(1:ng, e2i(mpc.gen(:, GEN_BUS)), mpc.gen(:, GEN_STATUS), ng, nb); 0108 Cg = sparse(1:ng, e2i(mpc.gen(:, GEN_BUS)), 1, ng, nb); 0109 0110 if nnz(C) 0111 n = length(varargin); 0112 if n >= 1 && iscell(varargin{1}) 0113 groups = varargin{1}; 0114 z = 1; 0115 else 0116 groups = {}; 0117 z = 0; 0118 end 0119 if z+1 <= n 0120 k = varargin{z+1}; 0121 else 0122 k = []; 0123 end 0124 if z+2 <= n 0125 custom = varargin{z+2}; 0126 else 0127 custom = struct(); 0128 end 0129 0130 %% find islands, if not provided 0131 if isempty(groups) 0132 groups = connected_components(C_on); 0133 end 0134 0135 %% check inputs 0136 if isempty(k) 0137 g1 = 1; 0138 gn = length(groups); 0139 else 0140 if ischar(k) 0141 if strcmp(upper(k), 'ALL') 0142 k = (1:length(groups))'; 0143 else 0144 error('extract_islands: K = ''%s'' is not a valid input', k); 0145 end 0146 end 0147 if max(k) > length(groups) 0148 error('extract_islands: cannot extract island %d, network has only %d islands', ... 0149 max(k), length(groups)); 0150 end 0151 if length(k) > 1 %% extract multiple islands as one case 0152 tmpgroup = groups{k(1)}; 0153 for j = 2:length(k) 0154 tmpgroup = union(tmpgroup, groups{k(j)}); 0155 end 0156 groups = { tmpgroup }; 0157 g1 = 1; 0158 gn = 1; 0159 else %% extract single island 0160 g1 = k; 0161 gn = k; 0162 end 0163 end 0164 0165 %% extract islands 0166 for i = g1:gn 0167 kk = i-g1+1; 0168 b = groups{i}; %% buses in group i 0169 %% branches with both ends in group i 0170 ibr = find(sum(abs(C(:, b)), 2) & ~sum(C(:, b), 2)); 0171 ig = find(sum(Cg(:, b), 2)); %% gens in group i 0172 %% DC lines with both ends in group i 0173 if ndc 0174 idc = find(sum(abs(Cdc(:, b)), 2) & ~sum(Cdc(:, b), 2)); 0175 else 0176 idc = []; 0177 end 0178 0179 mpck{kk} = mpc; 0180 mpck{kk}.bus = mpc.bus(b, :); 0181 mpck{kk}.branch = mpc.branch(ibr, :); 0182 mpck{kk}.gen = mpc.gen(ig, :); 0183 if isfield(mpck{kk}, 'gencost') 0184 if size(mpck{kk}.gencost, 1) == 2*ng 0185 mpck{kk}.gencost = mpc.gencost([ig; ng+ig], :); 0186 else 0187 mpck{kk}.gencost = mpc.gencost(ig, :); 0188 end 0189 end 0190 if isfield(mpck{kk}, 'gentype') 0191 mpck{kk}.gentype = mpc.gentype(ig); 0192 end 0193 if isfield(mpck{kk}, 'genfuel') 0194 mpck{kk}.genfuel = mpc.genfuel(ig); 0195 end 0196 if isfield(mpck{kk}, 'bus_name') 0197 mpck{kk}.bus_name = mpc.bus_name(b); 0198 end 0199 if ndc 0200 mpck{kk}.dcline = mpc.dcline(idc, :); 0201 if isfield(mpck{kk}, 'dclinecost') 0202 mpck{kk}.dclinecost = mpc.dclinecost(idc, :); 0203 end 0204 end 0205 0206 %% handle custom fields 0207 orderings = {'bus', 'gen', 'branch', 'dcline'}; 0208 indices = {b, ig, ibr, idc}; 0209 0210 for n = 1:length(orderings) 0211 ord = orderings{n}; 0212 if isfield(custom, ord) 0213 for dim = 1:length(custom.(ord)) 0214 for j = 1:length(custom.(ord){dim}) 0215 s = []; 0216 field = custom.(ord){dim}{j}; 0217 if ischar(field) 0218 field = { field }; 0219 end 0220 0221 tmp = mpck{kk}; %% check this for presence of sub-fields 0222 skip = 0; 0223 for i = 1:length(field) 0224 s(i).type = '.'; 0225 s(i).subs = field{i}; 0226 if isfield(tmp, field{i}) && ~isempty(tmp.(field{i})) 0227 %% have sub-field, continue 0228 tmp = tmp.(field{i}); 0229 else 0230 %% sub-field doesn't exist, skip it 0231 skip = 1; 0232 break; 0233 end 0234 end 0235 if ~skip 0236 mpck{kk} = subsasgn(mpck{kk}, s, get_reorder(subsref(mpck{kk}, s), indices{n}, dim)); 0237 end 0238 end 0239 end 0240 end 0241 end 0242 end 0243 0244 %% convert from cell array to single MATPOWER case struct as appropriate 0245 if ~isempty(k) 0246 mpck = mpck{1}; 0247 end 0248 else 0249 mpck = []; 0250 end