0001 function [gen, gencost] = off2case(gen, gencost, offers, bids, lim)
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
0065
0066
0067 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
0068 MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
0069 QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
0070 [PW_LINEAR, POLYNOMIAL, MODEL, STARTUP, SHUTDOWN, NCOST, COST] = idx_cost;
0071
0072
0073 if nargin < 5
0074 lim = [];
0075 if nargin < 4
0076 bids = [];
0077 end
0078 end
0079 if isfield(offers, 'Q') || isfield(bids, 'Q')
0080 haveQ = 1;
0081 else
0082 haveQ = 0;
0083 end
0084 lim = pricelimits(lim, haveQ);
0085 if isempty(bids)
0086 np = size(offers.P.qty, 2);
0087 bids = struct( 'P', struct('qty', zeros(0,np), 'prc', zeros(0,np)));
0088 end
0089 if haveQ
0090 if ~isfield(bids, 'Q')
0091 bids.Q = struct('qty', [], 'prc', []);
0092 elseif ~isfield(offers, 'Q')
0093 offers.Q = struct('qty', [], 'prc', []);
0094 end
0095 end
0096
0097
0098 ngc = size(gencost, 2);
0099 G = find( ~isload(gen) );
0100 L = find( isload(gen) );
0101 nGL = size(gen, 1);
0102 [idxPo, idxPb, idxQo, idxQb] = idx_vecs(offers, bids, G, L, haveQ);
0103 if haveQ
0104 if size(gencost, 1) == nGL
0105
0106 gencost = [ ...
0107 gencost;
0108 [PW_LINEAR * ones(nGL, 1) gencost(:,[STARTUP SHUTDOWN]) 2*ones(nGL,1) zeros(nGL,ngc-4) ]
0109 ];
0110 gencost(G+nGL, COST+2) = 1;
0111 gencost(L+nGL, COST) = -1;
0112 elseif size(gencost, 1) ~= 2 * nGL
0113 error('gencost should have either %d or %d rows', nGL, 2*nGL);
0114 end
0115 end
0116
0117
0118 if any(idxPo & idxPb)
0119 np = size(offers.P.qty, 2) + size(bids.P.qty, 2);
0120 else
0121 np = max([ size(offers.P.qty, 2) size(bids.P.qty, 2) ]);
0122 end
0123 if haveQ
0124 if any(idxQo & idxQb)
0125 np = max([ np size(offers.Q.qty, 2) + size(bids.Q.qty, 2) ]);
0126 else
0127 np = max([ np size(offers.Q.qty, 2) size(bids.Q.qty, 2) ]);
0128 end
0129 end
0130 np = np + 1;
0131 if any(idxPo + idxPb == 0)
0132 np = max([ np ceil(ngc-NCOST)/2 ]);
0133 end
0134
0135
0136 Pgencost = zeros(nGL, COST + 2*np - 1);
0137 Pgencost(:, MODEL) = PW_LINEAR * ones(nGL, 1);
0138 Pgencost(:, [STARTUP SHUTDOWN]) = gencost(1:nGL, [STARTUP SHUTDOWN]);
0139 if haveQ
0140 Qgencost = Pgencost;
0141 Qgencost(:, [STARTUP SHUTDOWN]) = gencost(nGL+(1:nGL), [STARTUP SHUTDOWN]);
0142 end
0143
0144 for i = 1:nGL
0145
0146 if idxPb(i)
0147 if gen(i, PMIN) >= 0 && any(bids.P.qty(idxPb(i), :))
0148 error('Pmin >= 0, bid not allowed for gen %d', i);
0149 end
0150 [xxPb, yyPb, nPb] = offbid2pwl(bids.P.qty(idxPb(i), :), bids.P.prc(idxPb(i), :), 1, lim.P.min_bid);
0151 else
0152 nPb = 0;
0153 end
0154 if idxPo(i)
0155 if gen(i, PMAX) <= 0 && any(offers.P.qty(idxPo(i), :))
0156 error('Pmax <= 0, offer not allowed for gen %d', i);
0157 end
0158 [xxPo, yyPo, nPo] = offbid2pwl(offers.P.qty(idxPo(i), :), offers.P.prc(idxPo(i), :), 0, lim.P.max_offer);
0159 else
0160 nPo = 0;
0161 end
0162
0163 if haveQ
0164 if idxQb(i)
0165 if gen(i, QMIN) >= 0 && any(bids.Q.qty(idxQb(i), :))
0166 error('Qmin >= 0, reactive bid not allowed for gen %d', i);
0167 end
0168 [xxQb, yyQb, nQb] = offbid2pwl(bids.Q.qty(idxQb(i), :), bids.Q.prc(idxQb(i), :), 1, lim.Q.min_bid);
0169 else
0170 nQb = 0;
0171 end
0172 if idxQo(i)
0173 if gen(i, QMAX) <= 0 && any(offers.Q.qty(idxQo(i), :))
0174 error('Qmax <= 0, reactive offer not allowed for gen %d', i);
0175 end
0176 [xxQo, yyQo, nQo] = offbid2pwl(offers.Q.qty(idxQo(i), :), offers.Q.prc(idxQo(i), :), 0, lim.Q.max_offer);
0177 else
0178 nQo = 0;
0179 end
0180 else
0181 nQb = 0;
0182 nQo = 0;
0183 end
0184
0185
0186 if nPb > 1 && nPo > 1
0187 if xxPb(end) || yyPb(end) || xxPo(1) || yyPo(1)
0188 error('Oops ... these 4 numbers should be zero: %g %g %g %g\n', ...
0189 xxPb(end), yyPb(end), xxPo(1), yyPo(1));
0190 end
0191 xxP = [xxPb xxPo(2:end)];
0192 yyP = [yyPb yyPo(2:end)];
0193 npP = nPb + nPo - 1;
0194 elseif nPb <= 1 && nPo > 1
0195 xxP = xxPo;
0196 yyP = yyPo;
0197 npP = nPo;
0198 elseif nPb > 1 && nPo <= 1
0199 xxP = xxPb;
0200 yyP = yyPb;
0201 npP = nPb;
0202 else
0203 npP = 0;
0204 end
0205
0206
0207 if nQb > 1 && nQo > 1
0208 if xxQb(end) || yyQb(end) || xxQo(1) || yyQo(1)
0209 error('Oops ... these 4 numbers should be zero: %g %g %g %g\n', ...
0210 xxQb(end), yyQb(end), xxQo(1), yyQo(1));
0211 end
0212 xxQ = [xxQb xxQo(2:end)];
0213 yyQ = [yyQb yyQo(2:end)];
0214 npQ = nQb + nQo - 1;
0215 elseif nQb <= 1 && nQo > 1
0216 xxQ = xxQo;
0217 yyQ = yyQo;
0218 npQ = nQo;
0219 elseif nQb > 1 && nQo <= 1
0220 xxQ = xxQb;
0221 yyQ = yyQb;
0222 npQ = nQb;
0223 else
0224 npQ = 0;
0225 end
0226
0227
0228 Pmin = gen(i, PMIN);
0229 Pmax = gen(i, PMAX);
0230 Qmin = gen(i, QMIN);
0231 Qmax = gen(i, QMAX);
0232
0233
0234 if npP
0235
0236 if gen(i, PMAX) > 0
0237 Pmax = max(xxP);
0238 if Pmax < gen(i, PMIN) || Pmax > gen(i, PMAX)
0239 error('offer quantity (%g) must be between max(0,PMIN) (%g) and PMAX (%g)', ...
0240 Pmax, max([0,gen(i, PMIN)]), gen(i, PMAX));
0241 end
0242 end
0243 if gen(i, PMIN) < 0
0244 Pmin = min(xxP);
0245 if Pmin >= gen(i, PMIN) && Pmin <= gen(i, PMAX)
0246 if isload(gen(i, :))
0247 Qmin = gen(i, QMIN) * Pmin / gen(i, PMIN);
0248 Qmax = gen(i, QMAX) * Pmin / gen(i, PMIN);
0249 end
0250 else
0251 error('bid quantity (%g) must be between max(0,-PMAX) (%g) and -PMIN (%g)', ...
0252 -Pmin, max([0 -gen(i, PMAX)]), -gen(i, PMIN));
0253 end
0254 end
0255
0256
0257 Pgencost(i, NCOST) = npP;
0258 Pgencost(i, COST:2:( COST + 2*npP - 2 )) = xxP;
0259 Pgencost(i, (COST+1):2:( COST + 2*npP - 1 )) = yyP;
0260 else
0261
0262 if npQ && ~isload(gen(i,:)) && gen(i, PMIN) <= 0 && gen(i, PMAX) >= 0
0263
0264
0265 Pmin = 0;
0266 Pmax = 0;
0267 Pgencost(i, 1:ngc) = gencost(i, 1:ngc);
0268 else
0269
0270 gen(i, GEN_STATUS) = 0;
0271 end
0272 end
0273
0274
0275 if npQ
0276
0277 if gen(i, QMAX) > 0
0278 Qmax = min([ Qmax max(xxQ) ]);
0279 if Qmax >= gen(i, QMIN) && Qmax <= gen(i, QMAX)
0280 if isload(gen(i, :))
0281 Pmin = gen(i, PMIN) * Qmax / gen(i, QMAX);
0282 end
0283 else
0284 error('reactive offer quantity (%g) must be between max(0,QMIN) (%g) and QMAX (%g)', ...
0285 Qmax, max([0,gen(i, QMIN)]), gen(i, QMAX));
0286 end
0287 end
0288 if gen(i, QMIN) < 0
0289 Qmin = max([ Qmin min(xxQ) ]);
0290 if Qmin >= gen(i, QMIN) && Qmin <= gen(i, QMAX)
0291 if isload(gen(i, :))
0292 Pmin = gen(i, PMIN) * Qmin / gen(i, QMIN);
0293 end
0294 else
0295 error('reactive bid quantity (%g) must be between max(0,-QMAX) (%g) and -QMIN (%g)', ...
0296 -Qmin, max([0 -gen(i, QMAX)]), -gen(i, QMIN));
0297 end
0298 end
0299
0300
0301 Qgencost(i, NCOST) = npQ;
0302 Qgencost(i, COST:2:( COST + 2*npQ - 2 )) = xxQ;
0303 Qgencost(i, (COST+1):2:( COST + 2*npQ - 1 )) = yyQ;
0304 else
0305
0306 if haveQ
0307 if npP && gen(i, QMIN) <= 0 && gen(i, QMAX) >= 0
0308
0309
0310 if isload(gen(i, :)) && (gen(i, QMAX) > 0 || gen(i, QMIN) < 0)
0311
0312 gen(i, GEN_STATUS) = 0;
0313 else
0314 Qmin = 0;
0315 Qmax = 0;
0316 end
0317 Qgencost(i, 1:ngc) = gencost(nGL+i, 1:ngc);
0318 else
0319
0320 gen(i, GEN_STATUS) = 0;
0321 end
0322 end
0323 end
0324
0325 if gen(i, GEN_STATUS)
0326 gen(i, PMIN) = Pmin;
0327 gen(i, PMAX) = Pmax;
0328 gen(i, QMIN) = Qmin;
0329 gen(i, QMAX) = Qmax;
0330 else
0331
0332 Pgencost(i, 1:ngc) = gencost(i, 1:ngc);
0333 if haveQ
0334 Qgencost(i, 1:ngc) = gencost(nGL+i, 1:ngc);
0335 end
0336 end
0337 end
0338 if ~haveQ
0339 Qgencost = zeros(0, size(Pgencost, 2));
0340 end
0341 np = max([ Pgencost(:, NCOST); Qgencost(:, NCOST) ]);
0342 ngc = NCOST + 2*np;
0343 gencost = [ Pgencost(:, 1:ngc); Qgencost(:, 1:ngc) ];
0344
0345
0346
0347 function [xx, yy, n] = offbid2pwl(qty, prc, isbid, lim)
0348
0349 if any(qty < 0)
0350 error('offer/bid quantities must be non-negative');
0351 end
0352
0353
0354 if nargin < 4 || isempty(lim)
0355 valid = find(qty);
0356 else
0357 if isbid
0358 valid = find(qty & prc >= lim);
0359 else
0360 valid = find(qty & prc <= lim);
0361 end
0362 end
0363
0364 if isbid
0365 n = length(valid);
0366 qq = qty(valid(n:-1:1));
0367 pp = prc(valid(n:-1:1));
0368 else
0369 qq = qty(valid);
0370 pp = prc(valid);
0371 end
0372 n = length(qq) + 1;
0373
0374
0375 if n > 1
0376 xx = [0 cumsum(qq)];
0377 yy = [0 cumsum(pp .* qq)];
0378 if isbid
0379 xx = xx - xx(end);
0380 yy = yy - yy(end);
0381 end
0382 else
0383 xx = [];
0384 yy = [];
0385 end
0386
0387
0388 function [idxPo, idxPb, idxQo, idxQb] = idx_vecs(offers, bids, G, L, haveQ)
0389
0390 nG = length(G);
0391 nL = length(L);
0392 nGL = nG + nL;
0393
0394 idxPo = zeros(nGL, 1);
0395 idxPb = zeros(nGL, 1);
0396 idxQo = zeros(nGL, 1);
0397 idxQb = zeros(nGL, 1);
0398
0399
0400 nPo = size(offers.P.qty, 1);
0401 nPb = size( bids.P.qty, 1);
0402 if haveQ
0403 nQo = size(offers.Q.qty, 1);
0404 nQb = size( bids.Q.qty, 1);
0405 end
0406
0407
0408 if any(size(offers.P.qty) ~= size(offers.P.prc))
0409 error('dimensions of offers.P.qty (%d x %d) and offers.P.prc (%d x %d) do not match',...
0410 size(offers.P.qty), size(offers.P.prc));
0411 end
0412 if any(size(bids.P.qty) ~= size(bids.P.prc))
0413 error('dimensions of bids.P.qty (%d x %d) and bids.P.prc (%d x %d) do not match',...
0414 size(bids.P.qty), size(bids.P.prc));
0415 end
0416 if haveQ
0417 if any(size(offers.Q.qty) ~= size(offers.Q.prc))
0418 error('dimensions of offers.Q.qty (%d x %d) and offers.Q.prc (%d x %d) do not match',...
0419 size(offers.Q.qty), size(offers.Q.prc));
0420 end
0421 if any(size(bids.Q.qty) ~= size(bids.Q.prc))
0422 error('dimensions of bids.Q.qty (%d x %d) and bids.Q.prc (%d x %d) do not match',...
0423 size(bids.Q.qty), size(bids.Q.prc));
0424 end
0425 end
0426
0427
0428 if nPo == nGL
0429 idxPo = (1:nGL)';
0430 elseif nPo == nG
0431 idxPo(G) = (1:nG)';
0432 elseif nPo ~= 0
0433 error('number of active power offers must be zero or match either the number of generators or the total number of rows in gen');
0434 end
0435
0436
0437 if nPb == nGL
0438 idxPb = (1:nGL)';
0439 elseif nPb == nL
0440 idxPb(L) = (1:nL)';
0441 elseif nPb ~= 0
0442 error('number of active power bids must be zero or match either the number of dispatchable loads or the total number of rows in gen');
0443 end
0444
0445 if haveQ
0446
0447 if nQo == nGL
0448 idxQo = (1:nGL)';
0449 elseif nQo == nG
0450 idxQo(G) = (1:nG)';
0451 elseif nQo ~= 0
0452 error('number of reactive power offers must be zero or match either the number of generators or the total number of rows in gen');
0453 end
0454
0455
0456 if nQb == nGL
0457 idxQb = (1:nGL)';
0458 elseif nQb ~= 0
0459 error('number of reactive power bids must be zero or match the total number of rows in gen');
0460 end
0461 end