Home > matpower7.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-2016, Power Systems Engineering Research Center (PSERC)
0039 %   by Ray Zimmerman, PSERC Cornell
0040 %
0041 %   This file is part of MATPOWER Extras.
0042 %   Covered by the 3-clause BSD License (see LICENSE file for details).
0043 %   See https://github.com/MATPOWER/matpower-extras for more info.
0044 
0045 %% define named indices into data matrices
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 %% default args and stuff
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 %% indices and sizes
0077 ngc = size(gencost, 2);
0078 G = find( ~isload(gen) );       %% real generators
0079 L = find(  isload(gen) );       %% dispatchable loads
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         %% set all reactive costs to zero if not provided
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 %% number of points to define piece-wise linear cost
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)  %% some gens have no offer or bid, use original cost
0111     np = max([ np ceil(ngc-NCOST)/2 ]);
0112 end
0113 
0114 %% initialize new cost matrices
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     %% convert active power bids & offers into piecewise linear segments
0125     if idxPb(i)     %% there is a bid for this unit
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)     %% there is an offer for this unit
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     %% convert reactive power bids & offers into piecewise linear segments
0142     if haveQ
0143         if idxQb(i)     %% there is a bid for this unit
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)     %% there is an offer for this unit
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     %% collect the pwl segments for active power
0165     if nPb > 1 && nPo > 1           %% bid and offer (positive and negative qtys)
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 %% offer only
0174         xxP = xxPo;
0175         yyP = yyPo;
0176         npP = nPo;
0177     elseif  nPb > 1 && nPo <= 1 %% bid only
0178         xxP = xxPb;
0179         yyP = yyPb;
0180         npP = nPb;
0181     else
0182         npP = 0;
0183     end
0184 
0185     %% collect the pwl segments for reactive power
0186     if nQb > 1 && nQo > 1           %% bid and offer (positive and negative qtys)
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 %% offer only
0195         xxQ = xxQo;
0196         yyQ = yyQo;
0197         npQ = nQo;
0198     elseif  nQb > 1 && nQo <= 1 %% bid only
0199         xxQ = xxQb;
0200         yyQ = yyQb;
0201         npQ = nQb;
0202     else
0203         npQ = 0;
0204     end
0205 
0206     %% initialize new gen limits
0207     Pmin = gen(i, PMIN);
0208     Pmax = gen(i, PMAX);
0209     Qmin = gen(i, QMIN);
0210     Qmax = gen(i, QMAX);
0211 
0212     %% update real part of gen and gencost
0213     if npP
0214         %% update gen limits
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         %% update gencost
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         %% no capacity bid/offered for active power
0241         if npQ && ~isload(gen(i,:)) && gen(i, PMIN) <= 0 && gen(i, PMAX) >= 0
0242             %% but we do have a reactive bid/offer and we can dispatch
0243             %% at zero real power without shutting down
0244             Pmin = 0;
0245             Pmax = 0;
0246             Pgencost(i, 1:ngc) = gencost(i, 1:ngc);
0247         else            %% none for reactive either
0248             %% shut down the unit
0249             gen(i, GEN_STATUS) = 0;
0250         end
0251     end
0252 
0253     %% update reactive part of gen and gencost
0254     if npQ
0255         %% update gen limits
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         %% update gencost
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         %% no capacity bid/offered for reactive power
0285         if haveQ
0286             if npP && gen(i, QMIN) <= 0 && gen(i, QMAX) >= 0
0287                 %% but we do have an active bid/offer and we might be able to
0288                 %% dispatch at zero reactive power without shutting down
0289                 if isload(gen(i, :)) && (gen(i, QMAX) > 0 || gen(i, QMIN) < 0)
0290                     %% load w/non-unity power factor, zero Q => must shut down
0291                     gen(i, GEN_STATUS) = 0;
0292                 else    %% can dispatch at zero reactive without shutting down
0293                     Qmin = 0;
0294                     Qmax = 0;
0295                 end
0296                 Qgencost(i, 1:ngc) = gencost(nGL+i, 1:ngc);
0297             else            %% none for reactive either
0298                 %% shut down the unit
0299                 gen(i, GEN_STATUS) = 0;
0300             end
0301         end
0302     end
0303 
0304     if gen(i, GEN_STATUS)       %% running
0305         gen(i, PMIN) = Pmin;    %% update limits
0306         gen(i, PMAX) = Pmax;
0307         gen(i, QMIN) = Qmin;
0308         gen(i, QMAX) = Qmax;
0309     else                        %% shut down
0310         %% do not modify cost
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 %%-----  offbid2pwl()  -----
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 %% strip zero quantities and optionally strip prices beyond lim
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));    %% row vector of quantities
0346     pp = prc(valid(n:-1:1));    %% row vector of prices
0347 else
0348     qq = qty(valid);            %% row vector of quantities
0349     pp = prc(valid);            %% row vector of prices
0350 end
0351 n = length(qq) + 1;             %% number of points to define pwl function
0352 
0353 %% form piece-wise linear total cost function
0354 if n > 1        %% otherwise, leave all cost info zero (specifically NCOST)
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 %%-----  idx_vecs()  -----
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 %% numbers of offers/bids submitted
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 %% make sure dimensions of qty and prc offers/bids match
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 %% active power offer indices
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 %% active power bid indices
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     %% reactive power offer indices
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     %% reactive power bid indices
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

Generated on Fri 09-Oct-2020 11:21:31 by m2html © 2005