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