Home > matpower5.1 > extras > smartmarket > off2case.m

off2case

PURPOSE ^

OFF2CASE Updates case variables gen & gencost from quantity & price offers.

SYNOPSIS ^

function [gen, gencost] = off2case(gen, gencost, offers, bids, lim)

DESCRIPTION ^

OFF2CASE  Updates case variables gen & gencost from quantity & price offers.
   [GEN, GENCOST] = OFF2CASE(GEN, GENCOST, OFFERS, BIDS, LIM) updates
   GEN & GENCOST variables based on the OFFERS and BIDS supplied, where each
   is a struct (or BIDS can be an empty matrix) with field 'P' (active power
   offer/bid) and optional field 'Q' (reactive power offer/bid), each of which
   is another struct with fields 'qty' and 'prc', m x n matrices of quantity
   and price offers/bids, respectively. There are m offers with n blocks each.
   For OFFERS, m can be equal to the number of actual generators (not including
   dispatchable loads) or the total number of rows in the GEN matrix (including
   dispatchable loads). For BIDS, m can be equal to the number of dispatchable
   loads or the total number of rows in the GEN matrix. Non-zero offer (bid)
   quantities for GEN matrix entries where Pmax <= 0 (Pmin >= 0) produce an
   error. Similarly for Q.
   
   E.g.
       OFFERS.P.qty - m x n, active power quantity offers, m offers, n blocks
               .prc - m x n, active power price offers
             .Q.qty - m x n, reactive power quantity offers
               .prc - m x n, reactive power price offers

   These values are used to update PMIN, PMAX, QMIN, QMAX and GEN_STATUS
   columns of the GEN matrix and all columns of the GENCOST matrix except
   STARTUP and SHUTDOWN.

   The last argument, LIM is a struct with the following fields,
   all of which are optional:
       LIM.P.min_bid
            .max_offer
          .Q.min_bid
            .max_offer
   Any price offers (bids) for real power above (below) LIM.P.max_offer
   (LIM.P.min_bid) will be treated as being withheld. Likewise for Q.

   See also CASE2OFF.

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 function [gen, gencost] = off2case(gen, gencost, offers, bids, lim)
0002 %OFF2CASE  Updates case variables gen & gencost from quantity & price offers.
0003 %   [GEN, GENCOST] = OFF2CASE(GEN, GENCOST, OFFERS, BIDS, LIM) updates
0004 %   GEN & GENCOST variables based on the OFFERS and BIDS supplied, where each
0005 %   is a struct (or BIDS can be an empty matrix) with field 'P' (active power
0006 %   offer/bid) and optional field 'Q' (reactive power offer/bid), each of which
0007 %   is another struct with fields 'qty' and 'prc', m x n matrices of quantity
0008 %   and price offers/bids, respectively. There are m offers with n blocks each.
0009 %   For OFFERS, m can be equal to the number of actual generators (not including
0010 %   dispatchable loads) or the total number of rows in the GEN matrix (including
0011 %   dispatchable loads). For BIDS, m can be equal to the number of dispatchable
0012 %   loads or the total number of rows in the GEN matrix. Non-zero offer (bid)
0013 %   quantities for GEN matrix entries where Pmax <= 0 (Pmin >= 0) produce an
0014 %   error. Similarly for Q.
0015 %
0016 %   E.g.
0017 %       OFFERS.P.qty - m x n, active power quantity offers, m offers, n blocks
0018 %               .prc - m x n, active power price offers
0019 %             .Q.qty - m x n, reactive power quantity offers
0020 %               .prc - m x n, reactive power price offers
0021 %
0022 %   These values are used to update PMIN, PMAX, QMIN, QMAX and GEN_STATUS
0023 %   columns of the GEN matrix and all columns of the GENCOST matrix except
0024 %   STARTUP and SHUTDOWN.
0025 %
0026 %   The last argument, LIM is a struct with the following fields,
0027 %   all of which are optional:
0028 %       LIM.P.min_bid
0029 %            .max_offer
0030 %          .Q.min_bid
0031 %            .max_offer
0032 %   Any price offers (bids) for real power above (below) LIM.P.max_offer
0033 %   (LIM.P.min_bid) will be treated as being withheld. Likewise for Q.
0034 %
0035 %   See also CASE2OFF.
0036 
0037 %   MATPOWER
0038 %   Copyright (c) 1996-2015 by Power System Engineering Research Center (PSERC)
0039 %   by Ray Zimmerman, PSERC Cornell
0040 %
0041 %   $Id: off2case.m 2644 2015-03-11 19:34:22Z ray $
0042 %
0043 %   This file is part of MATPOWER.
0044 %   Covered by the 3-clause BSD License (see LICENSE file for details).
0045 %   See http://www.pserc.cornell.edu/matpower/ for more info.
0046 
0047 %% define named indices into data matrices
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 %% default args and stuff
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 %% indices and sizes
0079 ngc = size(gencost, 2);
0080 G = find( ~isload(gen) );       %% real generators
0081 L = find(  isload(gen) );       %% dispatchable loads
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         %% set all reactive costs to zero if not provided
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 %% number of points to define piece-wise linear cost
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)  %% some gens have no offer or bid, use original cost
0113     np = max([ np ceil(ngc-NCOST)/2 ]);
0114 end
0115 
0116 %% initialize new cost matrices
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     %% convert active power bids & offers into piecewise linear segments
0127     if idxPb(i)     %% there is a bid for this unit
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)     %% there is an offer for this unit
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     %% convert reactive power bids & offers into piecewise linear segments
0144     if haveQ
0145         if idxQb(i)     %% there is a bid for this unit
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)     %% there is an offer for this unit
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     %% collect the pwl segments for active power
0167     if nPb > 1 && nPo > 1           %% bid and offer (positive and negative qtys)
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 %% offer only
0176         xxP = xxPo;
0177         yyP = yyPo;
0178         npP = nPo;
0179     elseif  nPb > 1 && nPo <= 1 %% bid only
0180         xxP = xxPb;
0181         yyP = yyPb;
0182         npP = nPb;
0183     else
0184         npP = 0;
0185     end
0186 
0187     %% collect the pwl segments for reactive power
0188     if nQb > 1 && nQo > 1           %% bid and offer (positive and negative qtys)
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 %% offer only
0197         xxQ = xxQo;
0198         yyQ = yyQo;
0199         npQ = nQo;
0200     elseif  nQb > 1 && nQo <= 1 %% bid only
0201         xxQ = xxQb;
0202         yyQ = yyQb;
0203         npQ = nQb;
0204     else
0205         npQ = 0;
0206     end
0207 
0208     %% initialize new gen limits
0209     Pmin = gen(i, PMIN);
0210     Pmax = gen(i, PMAX);
0211     Qmin = gen(i, QMIN);
0212     Qmax = gen(i, QMAX);
0213 
0214     %% update real part of gen and gencost
0215     if npP
0216         %% update gen limits
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         %% update gencost
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         %% no capacity bid/offered for active power
0243         if npQ && ~isload(gen(i,:)) && gen(i, PMIN) <= 0 && gen(i, PMAX) >= 0
0244             %% but we do have a reactive bid/offer and we can dispatch
0245             %% at zero real power without shutting down
0246             Pmin = 0;
0247             Pmax = 0;
0248             Pgencost(i, 1:ngc) = gencost(i, 1:ngc);
0249         else            %% none for reactive either
0250             %% shut down the unit
0251             gen(i, GEN_STATUS) = 0;
0252         end
0253     end
0254 
0255     %% update reactive part of gen and gencost
0256     if npQ
0257         %% update gen limits
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         %% update gencost
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         %% no capacity bid/offered for reactive power
0287         if haveQ
0288             if npP && gen(i, QMIN) <= 0 && gen(i, QMAX) >= 0
0289                 %% but we do have an active bid/offer and we might be able to
0290                 %% dispatch at zero reactive power without shutting down
0291                 if isload(gen(i, :)) && (gen(i, QMAX) > 0 || gen(i, QMIN) < 0)
0292                     %% load w/non-unity power factor, zero Q => must shut down
0293                     gen(i, GEN_STATUS) = 0;
0294                 else    %% can dispatch at zero reactive without shutting down
0295                     Qmin = 0;
0296                     Qmax = 0;
0297                 end
0298                 Qgencost(i, 1:ngc) = gencost(nGL+i, 1:ngc);
0299             else            %% none for reactive either
0300                 %% shut down the unit
0301                 gen(i, GEN_STATUS) = 0;
0302             end
0303         end
0304     end
0305 
0306     if gen(i, GEN_STATUS)       %% running
0307         gen(i, PMIN) = Pmin;    %% update limits
0308         gen(i, PMAX) = Pmax;
0309         gen(i, QMIN) = Qmin;
0310         gen(i, QMAX) = Qmax;
0311     else                        %% shut down
0312         %% do not modify cost
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 %%-----  offbid2pwl()  -----
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 %% strip zero quantities and optionally strip prices beyond lim
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));    %% row vector of quantities
0348     pp = prc(valid(n:-1:1));    %% row vector of prices
0349 else
0350     qq = qty(valid);            %% row vector of quantities
0351     pp = prc(valid);            %% row vector of prices
0352 end
0353 n = length(qq) + 1;             %% number of points to define pwl function
0354 
0355 %% form piece-wise linear total cost function
0356 if n > 1        %% otherwise, leave all cost info zero (specifically NCOST)
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 %%-----  idx_vecs()  -----
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 %% numbers of offers/bids submitted
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 %% make sure dimensions of qty and prc offers/bids match
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 %% active power offer indices
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 %% active power bid indices
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     %% reactive power offer indices
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     %% reactive power bid indices
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

Generated on Fri 20-Mar-2015 18:23:34 by m2html © 2005