Home > matpower6.0 > runpf.m

runpf

PURPOSE ^

RUNPF Runs a power flow.

SYNOPSIS ^

function [MVAbase, bus, gen, branch, success, et] =runpf(casedata, mpopt, fname, solvedcase)

DESCRIPTION ^

RUNPF  Runs a power flow.
   [RESULTS, SUCCESS] = RUNPF(CASEDATA, MPOPT, FNAME, SOLVEDCASE)

   Runs a power flow (full AC Newton's method by default), optionally
   returning a RESULTS struct and SUCCESS flag.

   Inputs (all are optional):
       CASEDATA : either a MATPOWER case struct or a string containing
           the name of the file with the case data (default is 'case9')
           (see also CASEFORMAT and LOADCASE)
       MPOPT : MATPOWER options struct to override default options
           can be used to specify the solution algorithm, output options
           termination tolerances, and more (see also MPOPTION).
       FNAME : name of a file to which the pretty-printed output will
           be appended
       SOLVEDCASE : name of file to which the solved case will be saved
           in MATPOWER case format (M-file will be assumed unless the
           specified name ends with '.mat')

   Outputs (all are optional):
       RESULTS : results struct, with the following fields:
           (all fields from the input MATPOWER case, i.e. bus, branch,
               gen, etc., but with solved voltages, power flows, etc.)
           order - info used in external <-> internal data conversion
           et - elapsed time in seconds
           success - success flag, 1 = succeeded, 0 = failed
       SUCCESS : the success flag can additionally be returned as
           a second output argument

   Calling syntax options:
       results = runpf;
       results = runpf(casedata);
       results = runpf(casedata, mpopt);
       results = runpf(casedata, mpopt, fname);
       results = runpf(casedata, mpopt, fname, solvedcase);
       [results, success] = runpf(...);

       Alternatively, for compatibility with previous versions of MATPOWER,
       some of the results can be returned as individual output arguments:

       [baseMVA, bus, gen, branch, success, et] = runpf(...);

   If the pf.enforce_q_lims option is set to true (default is false) then, if
   any generator reactive power limit is violated after running the AC power
   flow, the corresponding bus is converted to a PQ bus, with Qg at the
   limit, and the case is re-run. The voltage magnitude at the bus will
   deviate from the specified value in order to satisfy the reactive power
   limit. If the reference bus is converted to PQ, the first remaining PV
   bus will be used as the slack bus for the next iteration. This may
   result in the real power output at this generator being slightly off
   from the specified values.

   Examples:
       results = runpf('case30');
       results = runpf('case30', mpoption('pf.enforce_q_lims', 1));

   See also RUNDCPF.

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function [MVAbase, bus, gen, branch, success, et] = ...
0002                 runpf(casedata, mpopt, fname, solvedcase)
0003 %RUNPF  Runs a power flow.
0004 %   [RESULTS, SUCCESS] = RUNPF(CASEDATA, MPOPT, FNAME, SOLVEDCASE)
0005 %
0006 %   Runs a power flow (full AC Newton's method by default), optionally
0007 %   returning a RESULTS struct and SUCCESS flag.
0008 %
0009 %   Inputs (all are optional):
0010 %       CASEDATA : either a MATPOWER case struct or a string containing
0011 %           the name of the file with the case data (default is 'case9')
0012 %           (see also CASEFORMAT and LOADCASE)
0013 %       MPOPT : MATPOWER options struct to override default options
0014 %           can be used to specify the solution algorithm, output options
0015 %           termination tolerances, and more (see also MPOPTION).
0016 %       FNAME : name of a file to which the pretty-printed output will
0017 %           be appended
0018 %       SOLVEDCASE : name of file to which the solved case will be saved
0019 %           in MATPOWER case format (M-file will be assumed unless the
0020 %           specified name ends with '.mat')
0021 %
0022 %   Outputs (all are optional):
0023 %       RESULTS : results struct, with the following fields:
0024 %           (all fields from the input MATPOWER case, i.e. bus, branch,
0025 %               gen, etc., but with solved voltages, power flows, etc.)
0026 %           order - info used in external <-> internal data conversion
0027 %           et - elapsed time in seconds
0028 %           success - success flag, 1 = succeeded, 0 = failed
0029 %       SUCCESS : the success flag can additionally be returned as
0030 %           a second output argument
0031 %
0032 %   Calling syntax options:
0033 %       results = runpf;
0034 %       results = runpf(casedata);
0035 %       results = runpf(casedata, mpopt);
0036 %       results = runpf(casedata, mpopt, fname);
0037 %       results = runpf(casedata, mpopt, fname, solvedcase);
0038 %       [results, success] = runpf(...);
0039 %
0040 %       Alternatively, for compatibility with previous versions of MATPOWER,
0041 %       some of the results can be returned as individual output arguments:
0042 %
0043 %       [baseMVA, bus, gen, branch, success, et] = runpf(...);
0044 %
0045 %   If the pf.enforce_q_lims option is set to true (default is false) then, if
0046 %   any generator reactive power limit is violated after running the AC power
0047 %   flow, the corresponding bus is converted to a PQ bus, with Qg at the
0048 %   limit, and the case is re-run. The voltage magnitude at the bus will
0049 %   deviate from the specified value in order to satisfy the reactive power
0050 %   limit. If the reference bus is converted to PQ, the first remaining PV
0051 %   bus will be used as the slack bus for the next iteration. This may
0052 %   result in the real power output at this generator being slightly off
0053 %   from the specified values.
0054 %
0055 %   Examples:
0056 %       results = runpf('case30');
0057 %       results = runpf('case30', mpoption('pf.enforce_q_lims', 1));
0058 %
0059 %   See also RUNDCPF.
0060 
0061 %   MATPOWER
0062 %   Copyright (c) 1996-2016, Power Systems Engineering Research Center (PSERC)
0063 %   by Ray Zimmerman, PSERC Cornell
0064 %   Enforcing of generator Q limits inspired by contributions
0065 %   from Mu Lin, Lincoln University, New Zealand (1/14/05).
0066 %
0067 %   This file is part of MATPOWER.
0068 %   Covered by the 3-clause BSD License (see LICENSE file for details).
0069 %   See http://www.pserc.cornell.edu/matpower/ for more info.
0070 
0071 %%-----  initialize  -----
0072 %% define named indices into bus, gen, branch matrices
0073 [PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ...
0074     VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus;
0075 [F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ...
0076     TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ...
0077     ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch;
0078 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
0079     MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
0080     QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
0081 
0082 %% default arguments
0083 if nargin < 4
0084     solvedcase = '';                %% don't save solved case
0085     if nargin < 3
0086         fname = '';                 %% don't print results to a file
0087         if nargin < 2
0088             mpopt = mpoption;       %% use default options
0089             if nargin < 1
0090                 casedata = 'case9'; %% default data file is 'case9.m'
0091             end
0092         end
0093     end
0094 end
0095 
0096 %% options
0097 qlim = mpopt.pf.enforce_q_lims;         %% enforce Q limits on gens?
0098 dc = strcmp(upper(mpopt.model), 'DC');  %% use DC formulation?
0099 
0100 %% read data
0101 mpc = loadcase(casedata);
0102 
0103 %% add zero columns to branch for flows if needed
0104 if size(mpc.branch,2) < QT
0105   mpc.branch = [ mpc.branch zeros(size(mpc.branch, 1), QT-size(mpc.branch,2)) ];
0106 end
0107 
0108 %% convert to internal indexing
0109 mpc = ext2int(mpc);
0110 [baseMVA, bus, gen, branch] = deal(mpc.baseMVA, mpc.bus, mpc.gen, mpc.branch);
0111 
0112 %% get bus index lists of each type of bus
0113 [ref, pv, pq] = bustypes(bus, gen);
0114 
0115 %% generator info
0116 on = find(gen(:, GEN_STATUS) > 0);      %% which generators are on?
0117 gbus = gen(on, GEN_BUS);                %% what buses are they at?
0118 
0119 %%-----  run the power flow  -----
0120 t0 = clock;
0121 its = 0;            %% total iterations
0122 if mpopt.verbose > 0
0123     v = mpver('all');
0124     fprintf('\nMATPOWER Version %s, %s', v.Version, v.Date);
0125 end
0126 if dc                               %% DC formulation
0127     if mpopt.verbose > 0
0128       fprintf(' -- DC Power Flow\n');
0129     end
0130     %% initial state
0131     Va0 = bus(:, VA) * (pi/180);
0132     
0133     %% build B matrices and phase shift injections
0134     [B, Bf, Pbusinj, Pfinj] = makeBdc(baseMVA, bus, branch);
0135     
0136     %% compute complex bus power injections (generation - load)
0137     %% adjusted for phase shifters and real shunts
0138     Pbus = real(makeSbus(baseMVA, bus, gen)) - Pbusinj - bus(:, GS) / baseMVA;
0139     
0140     %% "run" the power flow
0141     [Va, success] = dcpf(B, Pbus, Va0, ref, pv, pq);
0142     its = 1;
0143     
0144     %% update data matrices with solution
0145     branch(:, [QF, QT]) = zeros(size(branch, 1), 2);
0146     branch(:, PF) = (Bf * Va + Pfinj) * baseMVA;
0147     branch(:, PT) = -branch(:, PF);
0148     bus(:, VM) = ones(size(bus, 1), 1);
0149     bus(:, VA) = Va * (180/pi);
0150     %% update Pg for slack generator (1st gen at ref bus)
0151     %% (note: other gens at ref bus are accounted for in Pbus)
0152     %%      Pg = Pinj + Pload + Gs
0153     %%      newPg = oldPg + newPinj - oldPinj
0154     refgen = zeros(size(ref));
0155     for k = 1:length(ref)
0156         temp = find(gbus == ref(k));
0157         refgen(k) = on(temp(1));
0158     end
0159     gen(refgen, PG) = gen(refgen, PG) + (B(ref, :) * Va - Pbus(ref)) * baseMVA;
0160 else                                %% AC formulation
0161     alg = upper(mpopt.pf.alg);
0162     if mpopt.verbose > 0
0163         switch alg
0164             case 'NR'
0165                 solver = 'Newton';
0166             case 'FDXB'
0167                 solver = 'fast-decoupled, XB';
0168             case 'FDBX'
0169                 solver = 'fast-decoupled, BX';
0170             case 'GS'
0171                 solver = 'Gauss-Seidel';
0172             otherwise
0173                 solver = 'unknown';
0174         end
0175         fprintf(' -- AC Power Flow (%s)\n', solver);
0176     end
0177     %% initial state
0178     % V0    = ones(size(bus, 1), 1);            %% flat start
0179     V0  = bus(:, VM) .* exp(sqrt(-1) * pi/180 * bus(:, VA));
0180     vcb = ones(size(V0));           %% create mask of voltage-controlled buses
0181     vcb(pq) = 0;                    %% exclude PQ buses
0182     k = find(vcb(gbus));            %% in-service gens at v-c buses
0183     V0(gbus(k)) = gen(on(k), VG) ./ abs(V0(gbus(k))).* V0(gbus(k));
0184     
0185     if qlim
0186         ref0 = ref;                         %% save index and angle of
0187         Varef0 = bus(ref0, VA);             %%   original reference bus(es)
0188         limited = [];                       %% list of indices of gens @ Q lims
0189         fixedQg = zeros(size(gen, 1), 1);   %% Qg of gens at Q limits
0190     end
0191 
0192     %% build admittance matrices
0193     [Ybus, Yf, Yt] = makeYbus(baseMVA, bus, branch);
0194     
0195     repeat = 1;
0196     while (repeat)
0197         %% function for computing V dependent complex bus power injections
0198         %% (generation - load)
0199         Sbus = @(Vm)makeSbus(baseMVA, bus, gen, mpopt, Vm);
0200         
0201         %% run the power flow
0202         switch alg
0203             case 'NR'
0204                 [V, success, iterations] = newtonpf(Ybus, Sbus, V0, ref, pv, pq, mpopt);
0205             case {'FDXB', 'FDBX'}
0206                 [Bp, Bpp] = makeB(baseMVA, bus, branch, alg);
0207                 [V, success, iterations] = fdpf(Ybus, Sbus, V0, Bp, Bpp, ref, pv, pq, mpopt);
0208             case 'GS'
0209                 if (~isempty(mpopt.exp.sys_wide_zip_loads.pw) && ...
0210                         any(mpopt.exp.sys_wide_zip_loads.pw(2:3))) || ...
0211                         (~isempty(mpopt.exp.sys_wide_zip_loads.qw) && ...
0212                         any(mpopt.exp.sys_wide_zip_loads.qw(2:3)))
0213                     warning('runpf: Gauss-Seidel algorithm does not support ZIP load model. Converting to constant power loads.')
0214                     mpopt = mpoption(mpopt, 'exp.sys_wide_zip_loads', ...
0215                                     struct('pw', [], 'qw', []));
0216                 end
0217                 [V, success, iterations] = gausspf(Ybus, Sbus([]), V0, ref, pv, pq, mpopt);
0218             otherwise
0219                 error('runpf: Only Newton''s method, fast-decoupled, and Gauss-Seidel power flow algorithms currently implemented.');
0220         end
0221         its = its + iterations;
0222         
0223         %% update data matrices with solution
0224         [bus, gen, branch] = pfsoln(baseMVA, bus, gen, branch, Ybus, Yf, Yt, V, ref, pv, pq, mpopt);
0225         
0226         if qlim             %% enforce generator Q limits
0227             %% find gens with violated Q constraints
0228             mx = find( gen(:, GEN_STATUS) > 0 ...
0229                     & gen(:, QG) > gen(:, QMAX) + mpopt.opf.violation );
0230             mn = find( gen(:, GEN_STATUS) > 0 ...
0231                     & gen(:, QG) < gen(:, QMIN) - mpopt.opf.violation );
0232             
0233             if ~isempty(mx) || ~isempty(mn)  %% we have some Q limit violations
0234                 %% first check for INFEASIBILITY
0235                 infeas = union(mx', mn')';  %% transposes handle fact that
0236                     %% union of scalars is a row vector
0237                 remaining = find( gen(:, GEN_STATUS) > 0 & ...
0238                                 ( bus(gen(:, GEN_BUS), BUS_TYPE) == PV | ...
0239                                   bus(gen(:, GEN_BUS), BUS_TYPE) == REF ));
0240                 if length(infeas) == length(remaining) && all(infeas == remaining) && ...
0241                         (isempty(mx) || isempty(mn))
0242                     %% all remaining PV/REF gens are violating AND all are
0243                     %% violating same limit (all violating Qmin or all Qmax)
0244                     if mpopt.verbose
0245                         fprintf('All %d remaining gens exceed their Q limits : INFEASIBLE PROBLEM\n', length(infeas));
0246                     end
0247                     success = 0;
0248                     break;
0249                 end
0250 
0251                 %% one at a time?
0252                 if qlim == 2    %% fix largest violation, ignore the rest
0253                     [junk, k] = max([gen(mx, QG) - gen(mx, QMAX);
0254                                      gen(mn, QMIN) - gen(mn, QG)]);
0255                     if k > length(mx)
0256                         mn = mn(k-length(mx));
0257                         mx = [];
0258                     else
0259                         mx = mx(k);
0260                         mn = [];
0261                     end
0262                 end
0263 
0264                 if mpopt.verbose && ~isempty(mx)
0265                     fprintf('Gen %d at upper Q limit, converting to PQ bus\n', mx);
0266                 end
0267                 if mpopt.verbose && ~isempty(mn)
0268                     fprintf('Gen %d at lower Q limit, converting to PQ bus\n', mn);
0269                 end
0270                 
0271                 %% save corresponding limit values
0272                 fixedQg(mx) = gen(mx, QMAX);
0273                 fixedQg(mn) = gen(mn, QMIN);
0274                 mx = [mx;mn];
0275                 
0276                 %% convert to PQ bus
0277                 gen(mx, QG) = fixedQg(mx);      %% set Qg to binding limit
0278                 gen(mx, GEN_STATUS) = 0;        %% temporarily turn off gen,
0279                 for i = 1:length(mx)            %% (one at a time, since
0280                     bi = gen(mx(i), GEN_BUS);   %%  they may be at same bus)
0281                     bus(bi, [PD,QD]) = ...      %% adjust load accordingly,
0282                         bus(bi, [PD,QD]) - gen(mx(i), [PG,QG]);
0283                 end
0284                 if length(ref) > 1 && any(bus(gen(mx, GEN_BUS), BUS_TYPE) == REF)
0285                     error('runpf: Sorry, MATPOWER cannot enforce Q limits for slack buses in systems with multiple slacks.');
0286                 end
0287                 bus(gen(mx, GEN_BUS), BUS_TYPE) = PQ;   %% & set bus type to PQ
0288                 
0289                 %% update bus index lists of each type of bus
0290                 ref_temp = ref;
0291                 [ref, pv, pq] = bustypes(bus, gen);
0292                 %% previous line can modify lists to select new REF bus
0293                 %% if there was none, so we should update bus with these
0294                 %% just to keep them consistent
0295                 if ref ~= ref_temp
0296                     bus(ref, BUS_TYPE) = REF;
0297                     bus( pv, BUS_TYPE) = PV;
0298                     if mpopt.verbose
0299                         fprintf('Bus %d is new slack bus\n', ref);
0300                     end
0301                 end
0302                 limited = [limited; mx];
0303             else
0304                 repeat = 0; %% no more generator Q limits violated
0305             end
0306         else
0307             repeat = 0;     %% don't enforce generator Q limits, once is enough
0308         end
0309     end
0310     if qlim && ~isempty(limited)
0311         %% restore injections from limited gens (those at Q limits)
0312         gen(limited, QG) = fixedQg(limited);    %% restore Qg value,
0313         for i = 1:length(limited)               %% (one at a time, since
0314             bi = gen(limited(i), GEN_BUS);      %%  they may be at same bus)
0315             bus(bi, [PD,QD]) = ...              %% re-adjust load,
0316                 bus(bi, [PD,QD]) + gen(limited(i), [PG,QG]);
0317         end
0318         gen(limited, GEN_STATUS) = 1;               %% and turn gen back on
0319         if ref ~= ref0
0320             %% adjust voltage angles to make original ref bus correct
0321             bus(:, VA) = bus(:, VA) - bus(ref0, VA) + Varef0;
0322         end
0323     end
0324 end
0325 mpc.et = etime(clock, t0);
0326 mpc.success = success;
0327 mpc.iterations = its;
0328 
0329 %%-----  output results  -----
0330 %% convert back to original bus numbering & print results
0331 [mpc.bus, mpc.gen, mpc.branch] = deal(bus, gen, branch);
0332 results = int2ext(mpc);
0333 
0334 %% zero out result fields of out-of-service gens & branches
0335 if ~isempty(results.order.gen.status.off)
0336   results.gen(results.order.gen.status.off, [PG QG]) = 0;
0337 end
0338 if ~isempty(results.order.branch.status.off)
0339   results.branch(results.order.branch.status.off, [PF QF PT QT]) = 0;
0340 end
0341 
0342 if fname
0343     [fd, msg] = fopen(fname, 'at');
0344     if fd == -1
0345         error(msg);
0346     else
0347         if mpopt.out.all == 0
0348             printpf(results, fd, mpoption(mpopt, 'out.all', -1));
0349         else
0350             printpf(results, fd, mpopt);
0351         end
0352         fclose(fd);
0353     end
0354 end
0355 printpf(results, 1, mpopt);
0356 
0357 %% save solved case
0358 if solvedcase
0359     savecase(solvedcase, results);
0360 end
0361 
0362 if nargout == 1 || nargout == 2
0363     MVAbase = results;
0364     bus = success;
0365 elseif nargout > 2
0366     [MVAbase, bus, gen, branch, et] = ...
0367         deal(results.baseMVA, results.bus, results.gen, results.branch, results.et);
0368 % else  %% don't define MVAbase, so it doesn't print anything
0369 end

Generated on Fri 16-Dec-2016 12:45:37 by m2html © 2005