Home > matpower7.1 > extras > smartmarket > smartmkt.m

smartmkt

PURPOSE ^

SMARTMKT Runs the PowerWeb smart market.

SYNOPSIS ^

function [co, cb, r, dispatch, success] =smartmkt(mpc, offers, bids, mkt, mpopt)

DESCRIPTION ^

SMARTMKT  Runs the PowerWeb smart market.
   [CO, CB, RESULTS, DISPATCH, SUCCESS] = SMARTMKT(MPC, ...
       OFFERS, BIDS, MKT, MPOPT) runs the ISO smart market.

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function [co, cb, r, dispatch, success] = ...
0002             smartmkt(mpc, offers, bids, mkt, mpopt)
0003 %SMARTMKT  Runs the PowerWeb smart market.
0004 %   [CO, CB, RESULTS, DISPATCH, SUCCESS] = SMARTMKT(MPC, ...
0005 %       OFFERS, BIDS, MKT, MPOPT) runs the ISO smart market.
0006 
0007 %   MATPOWER
0008 %   Copyright (c) 1996-2016, Power Systems Engineering Research Center (PSERC)
0009 %   by Ray Zimmerman, PSERC Cornell
0010 %
0011 %   This file is part of MATPOWER Extras.
0012 %   Covered by the 3-clause BSD License (see LICENSE file for details).
0013 %   See https://github.com/MATPOWER/matpower-extras for more info.
0014 
0015 %%-----  initialization  -----
0016 %% default arguments
0017 if nargin < 5
0018     mpopt = mpoption;       %% use default options
0019 end
0020 
0021 %% initialize some stuff
0022 G = find( ~isload(mpc.gen) );       %% real generators
0023 L = find(  isload(mpc.gen) );       %% dispatchable loads
0024 nL = length(L);
0025 if isfield(offers, 'Q') || isfield(bids, 'Q')
0026     haveQ = 1;
0027 else
0028     haveQ = 0;
0029 end
0030 
0031 if haveQ && mkt.auction_type ~= 0 && mkt.auction_type ~= 5
0032     error(['smartmkt: Combined active/reactive power markets ', ...
0033             'are only implemented for auction types 0 and 5']);
0034 end
0035 
0036 %% set power flow formulation based on market
0037 mpopt = mpoption(mpopt, 'model', upper(mkt.OPF));
0038 
0039 %% define named indices into data matrices
0040 [PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ...
0041     VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus;
0042 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
0043     MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
0044     QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
0045 [PW_LINEAR, POLYNOMIAL, MODEL, STARTUP, SHUTDOWN, NCOST, COST] = idx_cost;
0046 [QUANTITY, PRICE, FCOST, VCOST, SCOST, PENALTY] = idx_disp;
0047 
0048 %% set up cost info & generator limits
0049 mkt.lim = pricelimits(mkt.lim, isfield(offers, 'Q') || isfield(bids, 'Q'));
0050 [gen, genoffer] = off2case(mpc.gen, mpc.gencost, offers, bids, mkt.lim);
0051 
0052 %% move Pmin and Pmax limits out slightly to avoid problems
0053 %% with lambdas caused by rounding errors when corner point
0054 %% of cost function lies at exactly Pmin or Pmax
0055 if any(find(genoffer(:, MODEL) == PW_LINEAR))
0056     gg = find( ~isload(gen) );      %% skip dispatchable loads
0057     gen(gg, PMIN) = gen(gg, PMIN) - 100 * mpopt.opf.violation * ones(size(gg));
0058     gen(gg, PMAX) = gen(gg, PMAX) + 100 * mpopt.opf.violation * ones(size(gg));
0059 end
0060 
0061 %%-----  solve the optimization problem  -----
0062 %% attempt OPF
0063 mpc2 = mpc;
0064 mpc2.gen = gen;
0065 mpc2.gencost = genoffer;
0066 [r, success] = uopf(mpc2, mpopt);
0067 r.genoffer = r.gencost;     %% save the gencost used to run the OPF
0068 r.gencost  = mpc.gencost;   %% and restore the original gencost
0069 [bus, gen] = deal(r.bus, r.gen);
0070 if mpopt.verbose && ~success
0071     fprintf('\nSMARTMARKET: non-convergent UOPF');
0072 end
0073 
0074 %%-----  compute quantities, prices & costs  -----
0075 %% compute quantities & prices
0076 ng = size(gen, 1);
0077 if success      %% OPF solved case fine
0078     %% create map of external bus numbers to bus indices
0079     i2e = bus(:, BUS_I);
0080     e2i = sparse(max(i2e), 1);
0081     e2i(i2e) = (1:size(bus, 1))';
0082 
0083     %% get nodal marginal prices from OPF
0084     gbus    = e2i(gen(:, GEN_BUS));                 %% indices of buses w/gens
0085     nPo     = size(offers.P.qty, 2);
0086     nPb     = size(bids.P.qty, 2);
0087     nP      = max([ nPo nPb ]);
0088     lamP    = sparse(1:ng, 1:ng, bus(gbus, LAM_P), ng, ng) * ones(ng, nP);  %% real power prices
0089     lamQ    = sparse(1:ng, 1:ng, bus(gbus, LAM_Q), ng, ng) * ones(ng, nP);  %% reactive power prices
0090     
0091     %% compute fudge factor for lamP to include price of bundled reactive power
0092     pf   = zeros(length(L), 1);                 %% for loads Q = pf * P
0093     Qlim =  (gen(L, QMIN) == 0) .* gen(L, QMAX) + ...
0094             (gen(L, QMAX) == 0) .* gen(L, QMIN);
0095     pf = Qlim ./ gen(L, PMIN);
0096 
0097     gtee_prc.offer = 1;         %% guarantee that cleared offers are >= offers
0098     Poffer = offers.P;
0099     Poffer.lam = lamP(G,1:nPo);
0100     Poffer.total_qty = gen(G, PG);
0101     
0102     Pbid = bids.P;
0103     Pbid.total_qty = -gen(L, PG);
0104     if haveQ
0105         Pbid.lam = lamP(L,1:nPb);   %% use unbundled lambdas
0106         gtee_prc.bid = 0;       %% allow cleared bids to be above bid price
0107     else
0108         Pbid.lam = lamP(L,1:nPb) + sparse(1:nL, 1:nL, pf, nL, nL) * lamQ(L,1:nPb);  %% bundled lambdas
0109         gtee_prc.bid = 1;       %% guarantee that cleared bids are <= bids
0110     end
0111 
0112     [co.P, cb.P] = auction(Poffer, Pbid, mkt.auction_type, mkt.lim.P, gtee_prc);
0113 
0114     if haveQ
0115         nQo = size(offers.Q.qty, 2);
0116         nQb = size(bids.Q.qty, 2);
0117         nQ  = max([ nQo nQb ]);
0118         
0119         %% get nodal marginal prices from OPF
0120         lamQ    = sparse(1:ng, 1:ng, bus(gbus, LAM_Q), ng, ng) * ones(ng, nQ);  %% reactive power prices
0121 
0122         Qoffer = offers.Q;
0123         Qoffer.lam = lamQ(:,1:nQo);     %% use unbundled lambdas
0124         Qoffer.total_qty = (gen(:, QG) > 0) .* gen(:, QG);
0125         
0126         Qbid = bids.Q;
0127         Qbid.lam = lamQ(:,1:nQb);       %% use unbundled lambdas
0128         Qbid.total_qty = (gen(:, QG) < 0) .* -gen(:, QG);
0129 
0130         %% too complicated to scale with mixed bids/offers
0131         %% (only auction_types 0 and 5 allowed)
0132         [co.Q, cb.Q] = auction(Qoffer, Qbid, mkt.auction_type, mkt.lim.Q, gtee_prc);
0133     end
0134 
0135     quantity    = gen(:, PG);
0136     quantityQ   = gen(:, QG);
0137     price       = zeros(ng, 1);
0138     price(G)    = co.P.prc(:, 1);   %% need these for prices for
0139     price(L)    = cb.P.prc(:, 1);   %% gens that are shut down
0140     if nP == 1
0141         k = find( co.P.qty );
0142         price(G(k)) = co.P.prc(k, :);
0143         k = find( cb.P.qty );
0144         price(L(k)) = cb.P.prc(k, :);
0145     else
0146         k = find( sum( co.P.qty' )' );
0147         price(G(k)) = sum( co.P.qty(k, :)' .* co.P.prc(k, :)' )' ./ sum( co.P.qty(k, :)' )';
0148         k = find( sum( cb.P.qty' )' );
0149         price(L(k)) = sum( cb.P.qty(k, :)' .* cb.P.prc(k, :)' )' ./ sum( cb.P.qty(k, :)' )';
0150     end
0151 else        %% did not converge even with imports
0152     quantity    = zeros(ng, 1);
0153     quantityQ   = zeros(ng, 1);
0154     if isempty(mkt.lim.P.max_offer)
0155         price   = NaN(ng, 1);
0156     else
0157         price   = mkt.lim.P.max_offer * ones(ng, 1);
0158     end
0159     co.P.qty = zeros(size(offers.P.qty));
0160     co.P.prc = zeros(size(offers.P.prc));
0161     cb.P.qty = zeros(size(bids.P.qty));
0162     cb.P.prc = zeros(size(bids.P.prc));
0163     if haveQ
0164         co.Q.qty = zeros(size(offers.Q.qty));
0165         co.Q.prc = zeros(size(offers.Q.prc));
0166         cb.Q.qty = zeros(size(bids.Q.qty));
0167         cb.Q.prc = zeros(size(bids.Q.prc));
0168     end
0169 end
0170 
0171 
0172 %% compute costs in $ (note, NOT $/hr)
0173 if size(mpc.gencost, 1) == ng                   %% no reactive costs
0174     pgcost = mpc.gencost;
0175     fcost = mkt.t * totcost(pgcost, zeros(ng, 1));          %% fixed costs
0176     vcost = mkt.t * totcost(pgcost, quantity    ) - fcost;  %% variable costs
0177     scost =   (~mkt.u0 & gen(:, GEN_STATUS) >  0) .* ...
0178                     pgcost(:, STARTUP) + ...                %% startup costs
0179                 ( mkt.u0 & gen(:, GEN_STATUS) <= 0) .* ...
0180                     pgcost(:, SHUTDOWN);                    %% shutdown costs
0181 else    %% size(mpc.gencost, 1) == 2 * ng       %% reactive costs included
0182     pgcost = mpc.gencost(1:ng, :);
0183     qgcost = mpc.gencost(ng+(1:ng), :);
0184     fcost = mkt.t * ( totcost(pgcost, zeros(ng, 1)) + ...
0185                       totcost(qgcost, zeros(ng, 1)) );      %% fixed costs
0186     vcost = mkt.t * ( totcost(pgcost, quantity) + ...
0187                       totcost(qgcost, quantityQ) ) - fcost; %% variable costs
0188     scost = (~mkt.u0 & gen(:, GEN_STATUS) >  0) .* ...
0189                 (pgcost(:, STARTUP) + qgcost(:, STARTUP)) + ... %% startup costs
0190             ( mkt.u0 & gen(:, GEN_STATUS) <= 0) .* ...
0191                 (pgcost(:, SHUTDOWN) + qgcost(:, SHUTDOWN));    %% shutdown costs
0192 end
0193 
0194 %% store in dispatch
0195 dispatch = zeros(ng, PENALTY);
0196 dispatch(:, [QUANTITY PRICE FCOST VCOST SCOST]) = [quantity price fcost vcost scost];

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