SCALE_LOAD Scales fixed and/or dispatchable loads. MPC = SCALE_LOAD(LOAD, MPC); MPC = SCALE_LOAD(LOAD, MPC, LOAD_ZONE) MPC = SCALE_LOAD(LOAD, MPC, LOAD_ZONE, OPT) BUS = SCALE_LOAD(LOAD, BUS); [BUS, GEN] = SCALE_LOAD(LOAD, BUS, GEN, LOAD_ZONE, OPT) [BUS, GEN, GENCOST] = ... SCALE_LOAD(LOAD, BUS, GEN, LOAD_ZONE, OPT, GENCOST) Scales active (and optionally reactive) loads in each zone by a zone-specific ratio, i.e. R(k) for zone k. Inputs are ... LOAD - Each element specifies the amount of scaling for the corresponding load zone, either as a direct scale factor or as a target quantity. If there are nz load zones this vector has nz elements. MPC - standard MATPOWER case struct or case file name BUS - standard BUS matrix with nb rows, where the fixed active and reactive loads available for scaling are specified in columns PD and QD GEN - (optional) standard GEN matrix with ng rows, where the dispatchable loads available for scaling are specified by columns PG, QG, PMIN, QMIN and QMAX (in rows for which ISLOAD(GEN) returns true). If GEN is empty, it assumes there are no dispatchable loads. LOAD_ZONE - (optional) nb element vector where the value of each element is either zero or the index of the load zone to which the corresponding bus belongs. If LOAD_ZONE(b) = k then the loads at bus b will be scaled according to the value of LOAD(k). If LOAD_ZONE(b) = 0, the loads at bus b will not be modified. If LOAD_ZONE is empty, the default is determined by the dimensions of the LOAD vector. If LOAD is a scalar, a single system-wide zone including all buses is used, i.e. LOAD_ZONE = ONES(nb, 1). If LOAD is a vector, the default LOAD_ZONE is defined as the areas specified in the BUS matrix, i.e. LOAD_ZONE = BUS(:, BUS_AREA), and LOAD should have dimension = MAX(BUS(:, BUS_AREA)). OPT - (optional) struct with three possible fields, 'scale', 'pq' and 'which' that determine the behavior as follows: OPT.scale (default is 'FACTOR') 'FACTOR' : LOAD consists of direct scale factors, where LOAD(k) = scale factor R(k) for zone k 'QUANTITY' : LOAD consists of target quantities, where LOAD(k) = desired total active load in MW for zone k after scaling by an appropriate R(k) OPT.pq (default is 'PQ') 'PQ' : scale both active and reactive loads 'P' : scale only active loads OPT.which (default is 'BOTH' if GEN is provided, else 'FIXED') 'FIXED' : scale only fixed loads 'DISPATCHABLE' : scale only dispatchable loads 'BOTH' : scale both fixed and dispatchable loads OPT.cost : (default = -1) flag to include cost in scaling or not -1 : include cost if gencost is available 0 : do not include cost 1 : include cost (error if gencost not available) GENCOST - (optional) standard GENCOST matrix with ng (or 2*ng) rows, where the dispatchable load rows are determined by the GEN matrix. If included, the quantity axis of the marginal "cost" or benefit function of any dispatchable loads will be scaled with the size of the load itself (using MODCOST twice, once with MODTYPE equal to SCALE_F and once with SCALE_X). Examples: Scale all real and reactive fixed loads up by 10%. bus = scale_load(1.1, bus); Scale all active loads (fixed and dispatchable) at the first 10 buses so their total equals 100 MW, and at next 10 buses so their total equals 50 MW. load_zone = zeros(nb, 1); load_zone(1:10) = 1; load_zone(11:20) = 2; opt = struct('pq', 'P', 'scale', 'QUANTITY'); dmd = [100; 50]; [bus, gen] = scale_load(dmd, bus, gen, load_zone, opt); See also TOTAL_LOAD.
0001 function [bus, gen, gencost] = scale_load(dmd, bus, gen, load_zone, opt, gencost) 0002 %SCALE_LOAD Scales fixed and/or dispatchable loads. 0003 % MPC = SCALE_LOAD(LOAD, MPC); 0004 % MPC = SCALE_LOAD(LOAD, MPC, LOAD_ZONE) 0005 % MPC = SCALE_LOAD(LOAD, MPC, LOAD_ZONE, OPT) 0006 % BUS = SCALE_LOAD(LOAD, BUS); 0007 % [BUS, GEN] = SCALE_LOAD(LOAD, BUS, GEN, LOAD_ZONE, OPT) 0008 % [BUS, GEN, GENCOST] = ... 0009 % SCALE_LOAD(LOAD, BUS, GEN, LOAD_ZONE, OPT, GENCOST) 0010 % 0011 % Scales active (and optionally reactive) loads in each zone by a 0012 % zone-specific ratio, i.e. R(k) for zone k. Inputs are ... 0013 % 0014 % LOAD - Each element specifies the amount of scaling for the 0015 % corresponding load zone, either as a direct scale factor 0016 % or as a target quantity. If there are nz load zones this 0017 % vector has nz elements. 0018 % 0019 % MPC - standard MATPOWER case struct or case file name 0020 % 0021 % BUS - standard BUS matrix with nb rows, where the fixed active 0022 % and reactive loads available for scaling are specified in 0023 % columns PD and QD 0024 % 0025 % GEN - (optional) standard GEN matrix with ng rows, where the 0026 % dispatchable loads available for scaling are specified by 0027 % columns PG, QG, PMIN, QMIN and QMAX (in rows for which 0028 % ISLOAD(GEN) returns true). If GEN is empty, it assumes 0029 % there are no dispatchable loads. 0030 % 0031 % LOAD_ZONE - (optional) nb element vector where the value of 0032 % each element is either zero or the index of the load zone 0033 % to which the corresponding bus belongs. If LOAD_ZONE(b) = k 0034 % then the loads at bus b will be scaled according to the 0035 % value of LOAD(k). If LOAD_ZONE(b) = 0, the loads at bus b 0036 % will not be modified. If LOAD_ZONE is empty, the default is 0037 % determined by the dimensions of the LOAD vector. If LOAD is 0038 % a scalar, a single system-wide zone including all buses is 0039 % used, i.e. LOAD_ZONE = ONES(nb, 1). If LOAD is a vector, the 0040 % default LOAD_ZONE is defined as the areas specified in the 0041 % BUS matrix, i.e. LOAD_ZONE = BUS(:, BUS_AREA), and LOAD 0042 % should have dimension = MAX(BUS(:, BUS_AREA)). 0043 % 0044 % OPT - (optional) struct with three possible fields, 'scale', 0045 % 'pq' and 'which' that determine the behavior as follows: 0046 % 0047 % OPT.scale (default is 'FACTOR') 0048 % 'FACTOR' : LOAD consists of direct scale factors, where 0049 % LOAD(k) = scale factor R(k) for zone k 0050 % 'QUANTITY' : LOAD consists of target quantities, where 0051 % LOAD(k) = desired total active load in MW for 0052 % zone k after scaling by an appropriate R(k) 0053 % 0054 % OPT.pq (default is 'PQ') 0055 % 'PQ' : scale both active and reactive loads 0056 % 'P' : scale only active loads 0057 % 0058 % OPT.which (default is 'BOTH' if GEN is provided, else 'FIXED') 0059 % 'FIXED' : scale only fixed loads 0060 % 'DISPATCHABLE' : scale only dispatchable loads 0061 % 'BOTH' : scale both fixed and dispatchable loads 0062 % 0063 % OPT.cost : (default = -1) flag to include cost in scaling or not 0064 % -1 : include cost if gencost is available 0065 % 0 : do not include cost 0066 % 1 : include cost (error if gencost not available) 0067 % 0068 % GENCOST - (optional) standard GENCOST matrix with ng (or 2*ng) 0069 % rows, where the dispatchable load rows are determined by 0070 % the GEN matrix. If included, the quantity axis of the marginal 0071 % "cost" or benefit function of any dispatchable loads will be 0072 % scaled with the size of the load itself (using MODCOST twice, 0073 % once with MODTYPE equal to SCALE_F and once with SCALE_X). 0074 % 0075 % Examples: 0076 % Scale all real and reactive fixed loads up by 10%. 0077 % 0078 % bus = scale_load(1.1, bus); 0079 % 0080 % Scale all active loads (fixed and dispatchable) at the first 10 0081 % buses so their total equals 100 MW, and at next 10 buses so their 0082 % total equals 50 MW. 0083 % 0084 % load_zone = zeros(nb, 1); 0085 % load_zone(1:10) = 1; 0086 % load_zone(11:20) = 2; 0087 % opt = struct('pq', 'P', 'scale', 'QUANTITY'); 0088 % dmd = [100; 50]; 0089 % [bus, gen] = scale_load(dmd, bus, gen, load_zone, opt); 0090 % 0091 % See also TOTAL_LOAD. 0092 0093 % MATPOWER 0094 % Copyright (c) 2004-2016, Power Systems Engineering Research Center (PSERC) 0095 % by Ray Zimmerman, PSERC Cornell 0096 % 0097 % This file is part of MATPOWER. 0098 % Covered by the 3-clause BSD License (see LICENSE file for details). 0099 % See https://matpower.org for more info. 0100 0101 %% define constants 0102 [PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ... 0103 VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus; 0104 %% purposely being backward compatible with older MATPOWER 0105 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, ... 0106 PMAX, PMIN, MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN] = idx_gen; 0107 0108 %%----- process inputs ----- 0109 if ischar(bus) %% passing in case as file name string 0110 bus = loadcase(bus); 0111 end 0112 if isstruct(bus) 0113 use_mpc = 1; 0114 if nargin < 4 0115 load_zone = struct; 0116 if nargin < 3 0117 gen = []; 0118 end 0119 end 0120 %% shift and reassign inputs 0121 opt = load_zone; 0122 load_zone = gen; 0123 mpc = bus; 0124 gen = mpc.gen; 0125 bus = mpc.bus; 0126 if isfield(mpc, 'gencost') 0127 gencost = mpc.gencost; 0128 else 0129 gencost = []; 0130 end 0131 if nargout > 1 0132 error('scale_load: too many output arguments') 0133 end 0134 else 0135 use_mpc = 0; 0136 if nargin < 6 0137 gencost = []; 0138 if nargin < 5 0139 opt = struct; 0140 if nargin < 4 0141 load_zone = []; 0142 if nargin < 3 0143 gen = []; 0144 end 0145 end 0146 end 0147 end 0148 end 0149 0150 %% fill out and check opt 0151 if isempty(gen) 0152 opt.which = 'FIXED'; 0153 end 0154 if ~isfield(opt, 'pq') 0155 opt.pq = 'PQ'; %% 'PQ' or 'P' 0156 end 0157 if ~isfield(opt, 'which') 0158 opt.which = 'BOTH'; %% 'FIXED', 'DISPATCHABLE' or 'BOTH' 0159 end 0160 if ~isfield(opt, 'scale') 0161 opt.scale = 'FACTOR'; %% 'FACTOR' or 'QUANTITY' 0162 end 0163 if ~isfield(opt, 'cost') 0164 opt.cost = -1; %% -1, 0, or 1 0165 end 0166 if ~strcmp(opt.pq, 'P') && ~strcmp(opt.pq, 'PQ') 0167 error('scale_load: opt.pq must equal ''PQ'' or ''P'''); 0168 end 0169 if opt.which(1) ~= 'F' && opt.which(1) ~= 'D' && opt.which(1) ~= 'B' 0170 error('scale_load: opt.which should be ''FIXED'', ''DISPATCHABLE'' or ''BOTH'''); 0171 end 0172 if opt.scale(1) ~= 'F' && opt.scale(1) ~= 'Q' 0173 error('scale_load: opt.scale should be ''FACTOR'' or ''QUANTITY'''); 0174 end 0175 if isempty(gen) && opt.which(1) ~= 'F' 0176 error('scale_load: need gen matrix to scale dispatchable loads'); 0177 end 0178 if opt.cost == -1 0179 if isempty(gencost) 0180 opt.cost = 0; 0181 else 0182 opt.cost = 1; 0183 end 0184 end 0185 if ~use_mpc && nargout < 3 && opt.cost 0186 error('scale_load: missing gencost as output argument'); 0187 end 0188 if nargout > 2 && isempty(gencost) 0189 error('scale_load: missing gencost as input argument'); 0190 end 0191 0192 %% create dispatchable load connection matrix 0193 nb = size(bus, 1); %% number of buses 0194 if ~isempty(gen) 0195 ng = size(gen, 1); 0196 is_ld = isload(gen) & gen(:, GEN_STATUS) > 0; 0197 ld = find(is_ld); 0198 0199 %% create map of external bus numbers to bus indices 0200 i2e = bus(:, BUS_I); 0201 e2i = sparse(max(i2e), 1); 0202 e2i(i2e) = (1:nb)'; 0203 0204 Cld = sparse(e2i(gen(:, GEN_BUS)), (1:ng)', is_ld, nb, ng); 0205 else 0206 ng = []; 0207 ld = []; 0208 end 0209 0210 if isempty(load_zone) 0211 if length(dmd) == 1 %% make a single zone of all load buses 0212 load_zone = zeros(nb, 1); %% initialize 0213 load_zone(bus(:, PD) ~= 0 | bus(:, QD) ~= 0) = 1; %% FIXED loads 0214 if ~isempty(gen) 0215 load_zone(e2i(gen(ld, GEN_BUS))) = 1; %% DISPATCHABLE loads 0216 end 0217 else %% use areas defined in bus data as zones 0218 load_zone = bus(:, BUS_AREA); 0219 end 0220 end 0221 0222 %% check load_zone to make sure it's consistent with size of load vector 0223 if max(load_zone) > length(dmd) 0224 error('scale_load: load vector must have a value for each load zone specified'); 0225 end 0226 0227 %%----- compute scale factors for each zone ----- 0228 scale = dmd; 0229 Pdd = zeros(nb, 1); %% dispatchable P at each bus 0230 if opt.scale(1) == 'Q' %% 'QUANTITY' 0231 %% find load capacity from dispatchable loads 0232 if ~isempty(gen) 0233 Pdd = -Cld * gen(:, PMIN); 0234 end 0235 0236 %% compute scale factors 0237 for k = 1:length(dmd) 0238 idx = find( load_zone == k ); 0239 fixed = sum(bus(idx, PD)); 0240 dispatchable = sum(Pdd(idx)); 0241 total = fixed + dispatchable; 0242 if opt.which(1) == 'B' %% 'BOTH' 0243 if total ~= 0 0244 scale(k) = dmd(k) / total; 0245 elseif dmd(k) == total 0246 scale(k) = 1; 0247 else 0248 error('scale_load: impossible to make zone %d load equal %g by scaling non-existent loads', k, dmd(k)); 0249 end 0250 elseif opt.which(1) == 'F' %% 'FIXED' 0251 if fixed ~= 0 0252 scale(k) = (dmd(k) - dispatchable) / fixed; 0253 elseif dmd(k) == dispatchable 0254 scale(k) = 1; 0255 else 0256 error('scale_load: impossible to make zone %d load equal %g by scaling non-existent fixed load', k, dmd(k)); 0257 end 0258 elseif opt.which(1) == 'D' %% 'DISPATCHABLE' 0259 if dispatchable ~= 0 0260 scale(k) = (dmd(k) - fixed) / dispatchable; 0261 elseif dmd(k) == fixed 0262 scale(k) = 1; 0263 else 0264 error('scale_load: impossible to make zone %d load equal %g by scaling non-existent dispatchable load', k, dmd(k)); 0265 end 0266 end 0267 end 0268 end 0269 0270 %%----- do the scaling ----- 0271 %% fixed loads 0272 if opt.which(1) ~= 'D' %% includes 'FIXED', not 'DISPATCHABLE' only 0273 for k = 1:length(scale) 0274 idx = find( load_zone == k ); 0275 bus(idx, PD) = bus(idx, PD) * scale(k); 0276 if strcmp(opt.pq, 'PQ') 0277 bus(idx, QD) = bus(idx, QD) * scale(k); 0278 end 0279 end 0280 end 0281 0282 %% dispatchable loads 0283 if opt.which(1) ~= 'F' %% includes 'DISPATCHABLE', not 'FIXED' only 0284 for k = 1:length(scale) 0285 idx = find( load_zone == k ); 0286 i = find(ismember(e2i(gen(ld, GEN_BUS)), idx)); 0287 ig = ld(i); 0288 0289 gen(ig, [PG PMIN]) = gen(ig, [PG PMIN]) * scale(k); 0290 if opt.cost 0291 gencost(ig, :) = modcost(gencost(ig, :), scale(k), 'SCALE_F'); 0292 gencost(ig, :) = modcost(gencost(ig, :), scale(k), 'SCALE_X'); 0293 end 0294 if strcmp(opt.pq, 'PQ') 0295 gen(ig, [QG QMIN QMAX]) = gen(ig, [QG QMIN QMAX]) * scale(k); 0296 if opt.cost 0297 [pcost, qcost] = pqcost(gencost, ng); 0298 if ~isempty(qcost) 0299 qcost(ig, :) = modcost(qcost(ig, :), scale(k), 'SCALE_F'); 0300 qcost(ig, :) = modcost(qcost(ig, :), scale(k), 'SCALE_X'); 0301 gencost = [pcost; qcost]; 0302 end 0303 end 0304 end 0305 end 0306 end 0307 0308 %% re-package outputs if necessary 0309 if use_mpc 0310 mpc.bus = bus; 0311 mpc.gen = gen; 0312 if opt.cost 0313 mpc.gencost = gencost; 0314 end 0315 bus = mpc; 0316 end