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