Home > matpower4.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 %   $Id: off2case.m,v 1.29 2010/05/24 15:51:50 ray Exp $
0039 %   by Ray Zimmerman, PSERC Cornell
0040 %   Copyright (c) 1996-2010 by Power System Engineering Research Center (PSERC)
0041 %
0042 %   This file is part of MATPOWER.
0043 %   See http://www.pserc.cornell.edu/matpower/ for more info.
0044 %
0045 %   MATPOWER is free software: you can redistribute it and/or modify
0046 %   it under the terms of the GNU General Public License as published
0047 %   by the Free Software Foundation, either version 3 of the License,
0048 %   or (at your option) any later version.
0049 %
0050 %   MATPOWER is distributed in the hope that it will be useful,
0051 %   but WITHOUT ANY WARRANTY; without even the implied warranty of
0052 %   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0053 %   GNU General Public License for more details.
0054 %
0055 %   You should have received a copy of the GNU General Public License
0056 %   along with MATPOWER. If not, see <http://www.gnu.org/licenses/>.
0057 %
0058 %   Additional permission under GNU GPL version 3 section 7
0059 %
0060 %   If you modify MATPOWER, or any covered work, to interface with
0061 %   other modules (such as MATLAB code and MEX-files) available in a
0062 %   MATLAB(R) or comparable environment containing parts covered
0063 %   under other licensing terms, the licensors of MATPOWER grant
0064 %   you additional permission to convey the resulting work.
0065 
0066 %% define named indices into data matrices
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 %% default args and stuff
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 %% indices and sizes
0098 ngc = size(gencost, 2);
0099 G = find( ~isload(gen) );       %% real generators
0100 L = find(  isload(gen) );       %% dispatchable loads
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         %% set all reactive costs to zero if not provided
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 %% number of points to define piece-wise linear cost
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)  %% some gens have no offer or bid, use original cost
0132     np = max([ np ceil(ngc-NCOST)/2 ]);
0133 end
0134 
0135 %% initialize new cost matrices
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     %% convert active power bids & offers into piecewise linear segments
0146     if idxPb(i)     %% there is a bid for this unit
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)     %% there is an offer for this unit
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     %% convert reactive power bids & offers into piecewise linear segments
0163     if haveQ
0164         if idxQb(i)     %% there is a bid for this unit
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)     %% there is an offer for this unit
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     %% collect the pwl segments for active power
0186     if nPb > 1 && nPo > 1           %% bid and offer (positive and negative qtys)
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 %% offer only
0195         xxP = xxPo;
0196         yyP = yyPo;
0197         npP = nPo;
0198     elseif  nPb > 1 && nPo <= 1 %% bid only
0199         xxP = xxPb;
0200         yyP = yyPb;
0201         npP = nPb;
0202     else
0203         npP = 0;
0204     end
0205 
0206     %% collect the pwl segments for reactive power
0207     if nQb > 1 && nQo > 1           %% bid and offer (positive and negative qtys)
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 %% offer only
0216         xxQ = xxQo;
0217         yyQ = yyQo;
0218         npQ = nQo;
0219     elseif  nQb > 1 && nQo <= 1 %% bid only
0220         xxQ = xxQb;
0221         yyQ = yyQb;
0222         npQ = nQb;
0223     else
0224         npQ = 0;
0225     end
0226 
0227     %% initialize new gen limits
0228     Pmin = gen(i, PMIN);
0229     Pmax = gen(i, PMAX);
0230     Qmin = gen(i, QMIN);
0231     Qmax = gen(i, QMAX);
0232 
0233     %% update real part of gen and gencost
0234     if npP
0235         %% update gen limits
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         %% update gencost
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         %% no capacity bid/offered for active power
0262         if npQ && ~isload(gen(i,:)) && gen(i, PMIN) <= 0 && gen(i, PMAX) >= 0
0263             %% but we do have a reactive bid/offer and we can dispatch
0264             %% at zero real power without shutting down
0265             Pmin = 0;
0266             Pmax = 0;
0267             Pgencost(i, 1:ngc) = gencost(i, 1:ngc);
0268         else            %% none for reactive either
0269             %% shut down the unit
0270             gen(i, GEN_STATUS) = 0;
0271         end
0272     end
0273 
0274     %% update reactive part of gen and gencost
0275     if npQ
0276         %% update gen limits
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         %% update gencost
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         %% no capacity bid/offered for reactive power
0306         if haveQ
0307             if npP && gen(i, QMIN) <= 0 && gen(i, QMAX) >= 0
0308                 %% but we do have an active bid/offer and we might be able to
0309                 %% dispatch at zero reactive power without shutting down
0310                 if isload(gen(i, :)) && (gen(i, QMAX) > 0 || gen(i, QMIN) < 0)
0311                     %% load w/non-unity power factor, zero Q => must shut down
0312                     gen(i, GEN_STATUS) = 0;
0313                 else    %% can dispatch at zero reactive without shutting down
0314                     Qmin = 0;
0315                     Qmax = 0;
0316                 end
0317                 Qgencost(i, 1:ngc) = gencost(nGL+i, 1:ngc);
0318             else            %% none for reactive either
0319                 %% shut down the unit
0320                 gen(i, GEN_STATUS) = 0;
0321             end
0322         end
0323     end
0324 
0325     if gen(i, GEN_STATUS)       %% running
0326         gen(i, PMIN) = Pmin;    %% update limits
0327         gen(i, PMAX) = Pmax;
0328         gen(i, QMIN) = Qmin;
0329         gen(i, QMAX) = Qmax;
0330     else                        %% shut down
0331         %% do not modify cost
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 %%-----  offbid2pwl()  -----
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 %% strip zero quantities and optionally strip prices beyond lim
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));    %% row vector of quantities
0367     pp = prc(valid(n:-1:1));    %% row vector of prices
0368 else
0369     qq = qty(valid);            %% row vector of quantities
0370     pp = prc(valid);            %% row vector of prices
0371 end
0372 n = length(qq) + 1;             %% number of points to define pwl function
0373 
0374 %% form piece-wise linear total cost function
0375 if n > 1        %% otherwise, leave all cost info zero (specifically NCOST)
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 %%-----  idx_vecs()  -----
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 %% numbers of offers/bids submitted
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 %% make sure dimensions of qty and prc offers/bids match
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 %% active power offer indices
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 %% active power bid indices
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     %% reactive power offer indices
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     %% reactive power bid indices
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

Generated on Mon 26-Jan-2015 15:00:13 by m2html © 2005