0001 function mpc = toggle_reserves(mpc, on_off)
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064 if strcmp(on_off, 'on')
0065
0066 if ~isfield(mpc, 'reserves') || ~isstruct(mpc.reserves) || ...
0067 ~isfield(mpc.reserves, 'zones') || ...
0068 ~isfield(mpc.reserves, 'req') || ...
0069 ~isfield(mpc.reserves, 'cost')
0070 error('toggle_reserves: case must contain a ''reserves'' field, a struct defining ''zones'', ''req'' and ''cost''');
0071 end
0072
0073
0074
0075
0076 mpc = add_userfcn(mpc, 'ext2int', @userfcn_reserves_ext2int);
0077 mpc = add_userfcn(mpc, 'formulation', @userfcn_reserves_formulation);
0078 mpc = add_userfcn(mpc, 'int2ext', @userfcn_reserves_int2ext);
0079 mpc = add_userfcn(mpc, 'printpf', @userfcn_reserves_printpf);
0080 mpc = add_userfcn(mpc, 'savecase', @userfcn_reserves_savecase);
0081 elseif strcmp(on_off, 'off')
0082 mpc = remove_userfcn(mpc, 'savecase', @userfcn_reserves_savecase);
0083 mpc = remove_userfcn(mpc, 'printpf', @userfcn_reserves_printpf);
0084 mpc = remove_userfcn(mpc, 'int2ext', @userfcn_reserves_int2ext);
0085 mpc = remove_userfcn(mpc, 'formulation', @userfcn_reserves_formulation);
0086 mpc = remove_userfcn(mpc, 'ext2int', @userfcn_reserves_ext2int);
0087 else
0088 error('toggle_reserves: 2nd argument must be either ''on'' or ''off''');
0089 end
0090
0091
0092
0093 function mpc = userfcn_reserves_ext2int(mpc, args)
0094
0095
0096
0097
0098
0099
0100
0101
0102 r = mpc.reserves;
0103 o = mpc.order;
0104 ng0 = size(o.ext.gen, 1);
0105 nrz = size(r.req, 1);
0106 if nrz > 1
0107 mpc.reserves.rgens = any(r.zones);
0108 else
0109 mpc.reserves.rgens = r.zones;
0110 end
0111 igr = find(mpc.reserves.rgens);
0112 ngr = length(igr);
0113
0114
0115 if size(r.zones, 1) ~= nrz
0116 error('userfcn_reserves_ext2int: the number of rows in mpc.reserves.req (%d) and mpc.reserves.zones (%d) must match', nrz, size(r.zones, 1));
0117 end
0118 if size(r.cost, 1) ~= ng0 && size(r.cost, 1) ~= ngr
0119 error('userfcn_reserves_ext2int: the number of rows in mpc.reserves.cost (%d) must equal the total number of generators (%d) or the number of generators able to provide reserves (%d)', size(r.cost, 1), ng0, ngr);
0120 end
0121 if isfield(r, 'qty') && size(r.qty, 1) ~= size(r.cost, 1)
0122 error('userfcn_reserves_ext2int: mpc.reserves.cost (%d x 1) and mpc.reserves.qty (%d x 1) must be the same dimension', size(r.cost, 1), size(r.qty, 1));
0123 end
0124
0125
0126 if size(r.cost, 1) < ng0
0127 mpc.reserves.original.cost = r.cost;
0128 cost = zeros(ng0, 1);
0129 cost(igr) = r.cost;
0130 mpc.reserves.cost = cost;
0131 if isfield(r, 'qty')
0132 mpc.reserves.original.qty = r.qty;
0133 qty = zeros(ng0, 1);
0134 qty(igr) = r.qty;
0135 mpc.reserves.qty = qty;
0136 end
0137 end
0138
0139
0140
0141 if isfield(r, 'qty')
0142 mpc = ext2int(mpc, {'reserves', 'qty'}, 'gen');
0143 end
0144 mpc = ext2int(mpc, {'reserves', 'cost'}, 'gen');
0145 mpc = ext2int(mpc, {'reserves', 'zones'}, 'gen', 2);
0146 mpc = ext2int(mpc, {'reserves', 'rgens'}, 'gen', 2);
0147
0148
0149 mpc.order.ext.reserves.igr = igr;
0150 mpc.reserves.igr = find(mpc.reserves.rgens);
0151
0152
0153
0154 function om = userfcn_reserves_formulation(om, args)
0155
0156
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
0172 MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
0173 QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
0174
0175
0176 mpc = get_mpc(om);
0177 r = mpc.reserves;
0178 igr = r.igr;
0179 ngr = length(igr);
0180 ng = size(mpc.gen, 1);
0181
0182
0183 Rmin = zeros(ngr, 1);
0184 Rmax = Inf * ones(ngr, 1);
0185 k = find(mpc.gen(igr, RAMP_10));
0186 Rmax(k) = mpc.gen(igr(k), RAMP_10);
0187 if isfield(r, 'qty')
0188 k = find(r.qty(igr) < Rmax);
0189 Rmax(k) = r.qty(igr(k));
0190 end
0191 Rmax = Rmax / mpc.baseMVA;
0192
0193
0194 I = speye(ngr);
0195 Ar = [sparse(1:ngr, igr, 1, ngr, ng) I];
0196 ur = mpc.gen(igr, PMAX) / mpc.baseMVA;
0197 lreq = r.req / mpc.baseMVA;
0198
0199
0200 Cw = r.cost(igr) * mpc.baseMVA;
0201
0202
0203 om = add_vars(om, 'R', ngr, [], Rmin, Rmax);
0204 om = add_constraints(om, 'Pg_plus_R', Ar, [], ur, {'Pg', 'R'});
0205 om = add_constraints(om, 'Rreq', r.zones(:, igr), lreq, [], {'R'});
0206 om = add_costs(om, 'Rcost', struct('N', I, 'Cw', Cw), {'R'});
0207
0208
0209
0210 function results = userfcn_reserves_int2ext(results, args)
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223 r = results.reserves;
0224
0225
0226 igr = r.igr;
0227 ng = size(results.gen, 1);
0228
0229
0230
0231 if isfield(r, 'qty')
0232 results = int2ext(results, {'reserves', 'qty'}, 'gen');
0233 end
0234 results = int2ext(results, {'reserves', 'cost'}, 'gen');
0235 results = int2ext(results, {'reserves', 'zones'}, 'gen', 2);
0236 results = int2ext(results, {'reserves', 'rgens'}, 'gen', 2);
0237 results.order.int.reserves.igr = results.reserves.igr;
0238 results.reserves.igr = results.order.ext.reserves.igr;
0239 r = results.reserves;
0240 o = results.order;
0241
0242
0243 igr0 = r.igr;
0244 ng0 = size(o.ext.gen, 1);
0245
0246
0247
0248
0249 [R0, Rl, Ru] = getv(results.om, 'R');
0250 R = zeros(ng, 1);
0251 Rmin = zeros(ng, 1);
0252 Rmax = zeros(ng, 1);
0253 mu_l = zeros(ng, 1);
0254 mu_u = zeros(ng, 1);
0255 mu_Pmax = zeros(ng, 1);
0256 R(igr) = results.var.val.R * results.baseMVA;
0257 Rmin(igr) = Rl * results.baseMVA;
0258 Rmax(igr) = Ru * results.baseMVA;
0259 mu_l(igr) = results.var.mu.l.R / results.baseMVA;
0260 mu_u(igr) = results.var.mu.u.R / results.baseMVA;
0261 mu_Pmax(igr) = results.lin.mu.u.Pg_plus_R / results.baseMVA;
0262
0263
0264 z = zeros(ng0, 1);
0265 results.reserves.R = int2ext(results, R, z, 'gen');
0266 results.reserves.Rmin = int2ext(results, Rmin, z, 'gen');
0267 results.reserves.Rmax = int2ext(results, Rmax, z, 'gen');
0268 results.reserves.mu.l = int2ext(results, mu_l, z, 'gen');
0269 results.reserves.mu.u = int2ext(results, mu_u, z, 'gen');
0270 results.reserves.mu.Pmax = int2ext(results, mu_Pmax, z, 'gen');
0271 results.reserves.prc = z;
0272 for k = igr0
0273 iz = find(r.zones(:, k));
0274 results.reserves.prc(k) = max(results.lin.mu.l.Rreq(iz)) / results.baseMVA;
0275 end
0276 results.reserves.totalcost = results.cost.Rcost;
0277
0278
0279 if isfield(r, 'original')
0280 if isfield(r, 'qty')
0281 results.reserves.qty = r.original.qty;
0282 end
0283 results.reserves.cost = r.original.cost;
0284 results.reserves = rmfield(results.reserves, 'original');
0285 end
0286
0287
0288
0289 function results = userfcn_reserves_printpf(results, fd, mpopt, args)
0290
0291
0292
0293
0294
0295
0296
0297
0298 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
0299 MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
0300 QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
0301
0302
0303 r = results.reserves;
0304 nrz = size(r.req, 1);
0305 OUT_ALL = mpopt(32);
0306 if OUT_ALL ~= 0
0307 fprintf(fd, '\n================================================================================');
0308 fprintf(fd, '\n| Reserves |');
0309 fprintf(fd, '\n================================================================================');
0310 fprintf(fd, '\n Gen Bus Status Reserves Price');
0311 fprintf(fd, '\n # # (MW) ($/MW) Included in Zones ...');
0312 fprintf(fd, '\n---- ----- ------ -------- -------- ------------------------');
0313 for k = r.igr
0314 iz = find(r.zones(:, k));
0315 fprintf(fd, '\n%3d %6d %2d ', k, results.gen(k, GEN_BUS), results.gen(k, GEN_STATUS));
0316 if results.gen(k, GEN_STATUS) > 0 && abs(results.reserves.R(k)) > 1e-6
0317 fprintf(fd, '%10.2f', results.reserves.R(k));
0318 else
0319 fprintf(fd, ' - ');
0320 end
0321 fprintf(fd, '%10.2f ', results.reserves.prc(k));
0322 for i = 1:length(iz)
0323 if i ~= 1
0324 fprintf(fd, ', ');
0325 end
0326 fprintf(fd, '%d', iz(i));
0327 end
0328 end
0329 fprintf(fd, '\n --------');
0330 fprintf(fd, '\n Total:%10.2f Total Cost: $%.2f', ...
0331 sum(results.reserves.R(r.igr)), results.reserves.totalcost);
0332 fprintf(fd, '\n');
0333
0334 fprintf(fd, '\nZone Reserves Price ');
0335 fprintf(fd, '\n # (MW) ($/MW) ');
0336 fprintf(fd, '\n---- -------- --------');
0337 for k = 1:nrz
0338 iz = find(r.zones(k, :));
0339 fprintf(fd, '\n%3d%10.2f%10.2f', k, sum(results.reserves.R(iz)), ...
0340 results.lin.mu.l.Rreq(k) / results.baseMVA);
0341 end
0342 fprintf(fd, '\n');
0343
0344 fprintf(fd, '\n================================================================================');
0345 fprintf(fd, '\n| Reserve Limits |');
0346 fprintf(fd, '\n================================================================================');
0347 fprintf(fd, '\n Gen Bus Status Rmin mu Rmin Reserves Rmax Rmax mu Pmax mu ');
0348 fprintf(fd, '\n # # ($/MW) (MW) (MW) (MW) ($/MW) ($/MW) ');
0349 fprintf(fd, '\n---- ----- ------ -------- -------- -------- -------- -------- --------');
0350 for k = r.igr
0351 fprintf(fd, '\n%3d %6d %2d ', k, results.gen(k, GEN_BUS), results.gen(k, GEN_STATUS));
0352 if results.gen(k, GEN_STATUS) > 0 && results.reserves.mu.l(k) > 1e-6
0353 fprintf(fd, '%10.2f', results.reserves.mu.l(k));
0354 else
0355 fprintf(fd, ' - ');
0356 end
0357 fprintf(fd, '%10.2f', results.reserves.Rmin(k));
0358 if results.gen(k, GEN_STATUS) > 0 && abs(results.reserves.R(k)) > 1e-6
0359 fprintf(fd, '%10.2f', results.reserves.R(k));
0360 else
0361 fprintf(fd, ' - ');
0362 end
0363 fprintf(fd, '%10.2f', results.reserves.Rmax(k));
0364 if results.gen(k, GEN_STATUS) > 0 && results.reserves.mu.u(k) > 1e-6
0365 fprintf(fd, '%10.2f', results.reserves.mu.u(k));
0366 else
0367 fprintf(fd, ' - ');
0368 end
0369 if results.gen(k, GEN_STATUS) > 0 && results.reserves.mu.Pmax(k) > 1e-6
0370 fprintf(fd, '%10.2f', results.reserves.mu.Pmax(k));
0371 else
0372 fprintf(fd, ' - ');
0373 end
0374 end
0375 fprintf(fd, '\n --------');
0376 fprintf(fd, '\n Total:%10.2f', sum(results.reserves.R(r.igr)));
0377 fprintf(fd, '\n');
0378 end
0379
0380
0381
0382 function mpc = userfcn_reserves_savecase(mpc, fd, prefix, args)
0383
0384
0385
0386
0387
0388
0389
0390
0391 r = mpc.reserves;
0392
0393 fprintf(fd, '\n%%%%----- Reserve Data -----%%%%\n');
0394 fprintf(fd, '%%%% reserve zones, element i, j is 1 if gen j is in zone i, 0 otherwise\n');
0395 fprintf(fd, '%sreserves.zones = [\n', prefix);
0396 template = '';
0397 for i = 1:size(r.zones, 2)
0398 template = [template, '\t%d'];
0399 end
0400 template = [template, ';\n'];
0401 fprintf(fd, template, r.zones.');
0402 fprintf(fd, '];\n');
0403
0404 fprintf(fd, '\n%%%% reserve requirements for each zone in MW\n');
0405 fprintf(fd, '%sreserves.req = [\t%g', prefix, r.req(1));
0406 if length(r.req) > 1
0407 fprintf(fd, ';\t%g', r.req(2:end));
0408 end
0409 fprintf(fd, '\t];\n');
0410
0411 fprintf(fd, '\n%%%% reserve costs in $/MW for each gen that belongs to at least 1 zone\n');
0412 fprintf(fd, '%%%% (same order as gens, but skipping any gen that does not belong to any zone)\n');
0413 fprintf(fd, '%sreserves.cost = [\t%g', prefix, r.cost(1));
0414 if length(r.cost) > 1
0415 fprintf(fd, ';\t%g', r.cost(2:end));
0416 end
0417 fprintf(fd, '\t];\n');
0418
0419 if isfield(r, 'qty')
0420 fprintf(fd, '\n%%%% OPTIONAL max reserve quantities for each gen that belongs to at least 1 zone\n');
0421 fprintf(fd, '%%%% (same order as gens, but skipping any gen that does not belong to any zone)\n');
0422 fprintf(fd, '%sreserves.qty = [\t%g', prefix, r.qty(1));
0423 if length(r.qty) > 1
0424 fprintf(fd, ';\t%g', r.qty(2:end));
0425 end
0426 fprintf(fd, '\t];\n');
0427 end
0428
0429
0430 if isfield(r, 'R')
0431 if exist('serialize', 'file') == 2
0432 fprintf(fd, '\n%%%% solved values\n');
0433 fprintf(fd, '%sreserves.R = %s\n', prefix, serialize(r.R));
0434 fprintf(fd, '%sreserves.Rmin = %s\n', prefix, serialize(r.Rmin));
0435 fprintf(fd, '%sreserves.Rmax = %s\n', prefix, serialize(r.Rmax));
0436 fprintf(fd, '%sreserves.mu.l = %s\n', prefix, serialize(r.mu.l));
0437 fprintf(fd, '%sreserves.mu.u = %s\n', prefix, serialize(r.mu.u));
0438 fprintf(fd, '%sreserves.prc = %s\n', prefix, serialize(r.prc));
0439 fprintf(fd, '%sreserves.totalcost = %s\n', prefix, serialize(r.totalcost));
0440 else
0441 url = 'http://www.mathworks.com/matlabcentral/fileexchange/12063';
0442 warning('MATPOWER:serialize', ...
0443 'userfcn_reserves_savecase: Cannot save the ''reserves'' output fields without the ''serialize'' function, which is available as a free download from:\n<%s>\n\n', url);
0444 end
0445 end