Home > matpower7.0 > lib > runcpf.m

runcpf

PURPOSE ^

RUNCPF Runs a full AC continuation power flow

SYNOPSIS ^

function [res, suc] =runcpf(basecasedata, targetcasedata, mpopt, fname, solvedcase)

DESCRIPTION ^

RUNCPF  Runs a full AC continuation power flow
   [RESULTS, SUCCESS] = RUNCPF(BASECASEDATA, TARGETCASEDATA, ...
                                   MPOPT, FNAME, SOLVEDCASE)

   Runs a full AC continuation power flow using a normalized tangent
   predictor and selected parameterization scheme, optionally
   returning a RESULTS struct and SUCCESS flag. Step size can be
   fixed or adaptive.

   Inputs (all are optional):
       BASECASEDATA : either a MATPOWER case struct or a string containing
           the name of the file with the case data defining the base loading
           and generation (default is 'case9')
           (see also CASEFORMAT and LOADCASE)
       TARGETCASEDATA : either a MATPOWER case struct or a string
           containing the name of the file with the case data defining the
           target loading and generation (default is 'case9target')
       MPOPT : MATPOWER options struct to override default options
           can be used to specify the parameterization, output options,
           termination criteria, 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
           cpf - CPF output struct whose content depends on any
                   user callback functions, where default contains fields:
               done_msg - string with message describing cause of
                       continuation termination
               iterations - number of continuation steps performed
               lam - (nsteps+1) row vector of lambda values from
                       correction steps
               lam_hat - (nsteps+1) row vector of lambda values from
                       prediction steps
               max_lam - maximum value of lambda in RESULTS.cpf.lam
               steps - (nsteps+1) row vector of stepsizes taken at each
                       continuation step
               V - (nb x nsteps+1) complex bus voltages from
                       correction steps
               V_hat - (nb x nsteps+1) complex bus voltages from
                       prediction steps
               events - an array of structs of size nevents with the
                   following fields:
                    k - continuation step number at which event was located
                    name - name of event
                    idx - index(es) of critical elements in corresponding
                       event function, e.g. index of generator reaching VAr
                       limit
                    msg - descriptive text detailing the event
       SUCCESS : the success flag can additionally be returned as
           a second output argument

   Calling syntax options:
       results = runcpf;
       results = runcpf(basecasedata, targetcasedata);
       results = runcpf(basecasedata, targetcasedata, mpopt);
       results = runcpf(basecasedata, targetcasedata, mpopt, fname);
       results = runcpf(basecasedata, targetcasedata, mpopt, fname, solvedcase)
       [results, success] = runcpf(...);

   If the 'cpf.enforce_q_lims' option is set to true (default is false) then,
   if any generator reaches its reactive power limits during the AC
   continuation power flow,
       - the corresponding bus is converted to a PQ bus, and the problem
         is modified to eliminate further reactive transfer on this bus
       - the voltage magnitude at the bus will deviate from the specified
         setpoint to satisfy the reactive power limit,
       - if the reference bus is converted to PQ, further real power transfer
         for the bus is also eliminated, and the first remaining PV bus is
         selected as the new slack, resulting in the transfers at both
         reference buses potentially deviating from the specified values
       - if all reference and PV buses are converted to PQ, RUNCPF terminates
         with an infeasibility message.

   If the 'cpf.enforce_p_lims' option is set to true (default is fals) then,
   if any generator reaches its maximum active power limit during the AC
   continuation power flow,
       - the problem is modified to eliminate further active transfer by
         this generator
       - if the generator was at the reference bus, it is converted to PV
         and the first remaining PV bus is selected as the new slack.

   If the 'cpf.enforce_v_lims' option is set to true (default is false)
   then the continuation power flow is set to terminate if any bus voltage
   magnitude exceeds its minimum or maximum limit.

   If the 'cpf.enforce_flow_lims' option is set to true (default is false)
   then the continuation power flow is set to terminate if any line MVA
   flow exceeds its rateA limit.

   Possible CPF termination modes:
       when cpf.stop_at == 'NOSE'
           - Reached steady state loading limit
           - Nose point eliminated by limit induced bifurcation
       when cpf.stop_at == 'FULL'
           - Traced full continuation curve
       when cpf.stop_at == <target_lam_val>
           - Reached desired lambda
       when cpf.enforce_p_lims == true
           - All generators at PMAX
       when cpf.enforce_q_lims == true
           - No REF or PV buses remaining
       when cpf.enforce_v_lims == true
           - Any bus voltage magnitude limit is reached
       when cpf.enforce_flow_lims == true
           - Any branch MVA flow limit is reached
       other
           - Base case power flow did not converge
           - Base and target case have identical load and generation
           - Corrector did not converge
           - Could not locate <event_name> event
           - Too many rollback steps triggered by callbacks

   Examples:
       results = runcpf('case9', 'case9target');
       results = runcpf('case9', 'case9target', ...
                           mpoption('cpf.adapt_step', 1));
       results = runcpf('case9', 'case9target', ...
                           mpoption('cpf.enforce_q_lims', 1));
       results = runcpf('case9', 'case9target', ...
                           mpoption('cpf.stop_at', 'FULL'));

   See also MPOPTION, RUNPF.

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SOURCE CODE ^

0001 function [res, suc] = ...
0002     runcpf(basecasedata, targetcasedata, mpopt, fname, solvedcase)
0003 %RUNCPF  Runs a full AC continuation power flow
0004 %   [RESULTS, SUCCESS] = RUNCPF(BASECASEDATA, TARGETCASEDATA, ...
0005 %                                   MPOPT, FNAME, SOLVEDCASE)
0006 %
0007 %   Runs a full AC continuation power flow using a normalized tangent
0008 %   predictor and selected parameterization scheme, optionally
0009 %   returning a RESULTS struct and SUCCESS flag. Step size can be
0010 %   fixed or adaptive.
0011 %
0012 %   Inputs (all are optional):
0013 %       BASECASEDATA : either a MATPOWER case struct or a string containing
0014 %           the name of the file with the case data defining the base loading
0015 %           and generation (default is 'case9')
0016 %           (see also CASEFORMAT and LOADCASE)
0017 %       TARGETCASEDATA : either a MATPOWER case struct or a string
0018 %           containing the name of the file with the case data defining the
0019 %           target loading and generation (default is 'case9target')
0020 %       MPOPT : MATPOWER options struct to override default options
0021 %           can be used to specify the parameterization, output options,
0022 %           termination criteria, and more (see also MPOPTION).
0023 %       FNAME : name of a file to which the pretty-printed output will
0024 %           be appended
0025 %       SOLVEDCASE : name of file to which the solved case will be saved
0026 %           in MATPOWER case format (M-file will be assumed unless the
0027 %           specified name ends with '.mat')
0028 %
0029 %   Outputs (all are optional):
0030 %       RESULTS : results struct, with the following fields:
0031 %           (all fields from the input MATPOWER case, i.e. bus, branch,
0032 %               gen, etc., but with solved voltages, power flows, etc.)
0033 %           order - info used in external <-> internal data conversion
0034 %           et - elapsed time in seconds
0035 %           success - success flag, 1 = succeeded, 0 = failed
0036 %           cpf - CPF output struct whose content depends on any
0037 %                   user callback functions, where default contains fields:
0038 %               done_msg - string with message describing cause of
0039 %                       continuation termination
0040 %               iterations - number of continuation steps performed
0041 %               lam - (nsteps+1) row vector of lambda values from
0042 %                       correction steps
0043 %               lam_hat - (nsteps+1) row vector of lambda values from
0044 %                       prediction steps
0045 %               max_lam - maximum value of lambda in RESULTS.cpf.lam
0046 %               steps - (nsteps+1) row vector of stepsizes taken at each
0047 %                       continuation step
0048 %               V - (nb x nsteps+1) complex bus voltages from
0049 %                       correction steps
0050 %               V_hat - (nb x nsteps+1) complex bus voltages from
0051 %                       prediction steps
0052 %               events - an array of structs of size nevents with the
0053 %                   following fields:
0054 %                    k - continuation step number at which event was located
0055 %                    name - name of event
0056 %                    idx - index(es) of critical elements in corresponding
0057 %                       event function, e.g. index of generator reaching VAr
0058 %                       limit
0059 %                    msg - descriptive text detailing the event
0060 %       SUCCESS : the success flag can additionally be returned as
0061 %           a second output argument
0062 %
0063 %   Calling syntax options:
0064 %       results = runcpf;
0065 %       results = runcpf(basecasedata, targetcasedata);
0066 %       results = runcpf(basecasedata, targetcasedata, mpopt);
0067 %       results = runcpf(basecasedata, targetcasedata, mpopt, fname);
0068 %       results = runcpf(basecasedata, targetcasedata, mpopt, fname, solvedcase)
0069 %       [results, success] = runcpf(...);
0070 %
0071 %   If the 'cpf.enforce_q_lims' option is set to true (default is false) then,
0072 %   if any generator reaches its reactive power limits during the AC
0073 %   continuation power flow,
0074 %       - the corresponding bus is converted to a PQ bus, and the problem
0075 %         is modified to eliminate further reactive transfer on this bus
0076 %       - the voltage magnitude at the bus will deviate from the specified
0077 %         setpoint to satisfy the reactive power limit,
0078 %       - if the reference bus is converted to PQ, further real power transfer
0079 %         for the bus is also eliminated, and the first remaining PV bus is
0080 %         selected as the new slack, resulting in the transfers at both
0081 %         reference buses potentially deviating from the specified values
0082 %       - if all reference and PV buses are converted to PQ, RUNCPF terminates
0083 %         with an infeasibility message.
0084 %
0085 %   If the 'cpf.enforce_p_lims' option is set to true (default is fals) then,
0086 %   if any generator reaches its maximum active power limit during the AC
0087 %   continuation power flow,
0088 %       - the problem is modified to eliminate further active transfer by
0089 %         this generator
0090 %       - if the generator was at the reference bus, it is converted to PV
0091 %         and the first remaining PV bus is selected as the new slack.
0092 %
0093 %   If the 'cpf.enforce_v_lims' option is set to true (default is false)
0094 %   then the continuation power flow is set to terminate if any bus voltage
0095 %   magnitude exceeds its minimum or maximum limit.
0096 %
0097 %   If the 'cpf.enforce_flow_lims' option is set to true (default is false)
0098 %   then the continuation power flow is set to terminate if any line MVA
0099 %   flow exceeds its rateA limit.
0100 %
0101 %   Possible CPF termination modes:
0102 %       when cpf.stop_at == 'NOSE'
0103 %           - Reached steady state loading limit
0104 %           - Nose point eliminated by limit induced bifurcation
0105 %       when cpf.stop_at == 'FULL'
0106 %           - Traced full continuation curve
0107 %       when cpf.stop_at == <target_lam_val>
0108 %           - Reached desired lambda
0109 %       when cpf.enforce_p_lims == true
0110 %           - All generators at PMAX
0111 %       when cpf.enforce_q_lims == true
0112 %           - No REF or PV buses remaining
0113 %       when cpf.enforce_v_lims == true
0114 %           - Any bus voltage magnitude limit is reached
0115 %       when cpf.enforce_flow_lims == true
0116 %           - Any branch MVA flow limit is reached
0117 %       other
0118 %           - Base case power flow did not converge
0119 %           - Base and target case have identical load and generation
0120 %           - Corrector did not converge
0121 %           - Could not locate <event_name> event
0122 %           - Too many rollback steps triggered by callbacks
0123 %
0124 %   Examples:
0125 %       results = runcpf('case9', 'case9target');
0126 %       results = runcpf('case9', 'case9target', ...
0127 %                           mpoption('cpf.adapt_step', 1));
0128 %       results = runcpf('case9', 'case9target', ...
0129 %                           mpoption('cpf.enforce_q_lims', 1));
0130 %       results = runcpf('case9', 'case9target', ...
0131 %                           mpoption('cpf.stop_at', 'FULL'));
0132 %
0133 %   See also MPOPTION, RUNPF.
0134 
0135 %   MATPOWER
0136 %   Copyright (c) 1996-2017, Power Systems Engineering Research Center (PSERC)
0137 %   by Ray Zimmerman, PSERC Cornell,
0138 %   Shrirang Abhyankar, Argonne National Laboratory,
0139 %   and Alexander Flueck, IIT
0140 %
0141 %   This file is part of MATPOWER.
0142 %   Covered by the 3-clause BSD License (see LICENSE file for details).
0143 %   See https://matpower.org for more info.
0144 
0145 %%-----  initialize  -----
0146 %% define named indices into bus, gen, branch matrices
0147 [PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ...
0148     VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus;
0149 [F_BUS, T_BUS, BR_R, BR_X, BR_B, RATE_A, RATE_B, RATE_C, ...
0150     TAP, SHIFT, BR_STATUS, PF, QF, PT, QT, MU_SF, MU_ST, ...
0151     ANGMIN, ANGMAX, MU_ANGMIN, MU_ANGMAX] = idx_brch;
0152 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ...
0153     MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ...
0154     QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen;
0155 
0156 %% default arguments
0157 if nargin < 5
0158     solvedcase = '';                %% don't save solved case
0159     if nargin < 4
0160         fname = '';                 %% don't print results to a file
0161         if nargin < 3
0162             mpopt = mpoption;       %% use default options
0163             if nargin < 2
0164                 targetcasedata = 'case9target';
0165                 if nargin < 1
0166                     basecasedata = 'case9'; %% default data file is 'case9.m'
0167                 end
0168             end
0169         end
0170     end
0171 end
0172 
0173 %% options
0174 step        = mpopt.cpf.step;              %% continuation step length
0175 parm        = mpopt.cpf.parameterization;  %% parameterization
0176 adapt_step  = mpopt.cpf.adapt_step;        %% use adaptive step size?
0177 qlim        = mpopt.cpf.enforce_q_lims;    %% enforce reactive limits
0178 plim        = mpopt.cpf.enforce_p_lims;    %% enforce active limits
0179 vlim        = mpopt.cpf.enforce_v_lims;    %% enforce voltage magnitude limits
0180 flim        = mpopt.cpf.enforce_flow_lims; %% enforce branch flow limits
0181 
0182 %% register event functions (for event detection)
0183 %% and CPF callback functions (for event handling and other tasks)
0184 cpf_events = [];
0185 cpf_callbacks = [];
0186 %% to handle CPF termination
0187 if ischar(mpopt.cpf.stop_at) && strcmp(mpopt.cpf.stop_at, 'NOSE');
0188     cpf_events   = cpf_register_event(cpf_events, 'NOSE', 'cpf_nose_event', mpopt.cpf.nose_tol, 1);
0189     cpf_callbacks = cpf_register_callback(cpf_callbacks, 'cpf_nose_event_cb', 51);
0190 else        %% FULL or target lambda
0191     cpf_events   = cpf_register_event(cpf_events, 'TARGET_LAM', 'cpf_target_lam_event', mpopt.cpf.target_lam_tol, 1);
0192     cpf_callbacks = cpf_register_callback(cpf_callbacks, 'cpf_target_lam_event_cb', 50);
0193 end
0194 %% to handle branch flow limits
0195 if flim
0196     cpf_events = cpf_register_event(cpf_events, 'FLIM', 'cpf_flim_event', mpopt.cpf.flow_lims_tol, 1);
0197     cpf_callbacks = cpf_register_callback(cpf_callbacks, 'cpf_flim_event_cb', 53);
0198 end
0199 %% to handle voltage limits
0200 if vlim
0201     cpf_events = cpf_register_event(cpf_events, 'VLIM', 'cpf_vlim_event', mpopt.cpf.v_lims_tol, 1);
0202     cpf_callbacks = cpf_register_callback(cpf_callbacks, 'cpf_vlim_event_cb', 52);
0203 end
0204 %% to handle reactive power limits
0205 if qlim
0206     cpf_events = cpf_register_event(cpf_events, 'QLIM', 'cpf_qlim_event', mpopt.cpf.q_lims_tol, 1);
0207     cpf_callbacks = cpf_register_callback(cpf_callbacks, 'cpf_qlim_event_cb', 41);
0208 end
0209 %% to handle active power limits
0210 if plim
0211     cpf_events = cpf_register_event(cpf_events, 'PLIM', 'cpf_plim_event', mpopt.cpf.p_lims_tol, 1);
0212     cpf_callbacks = cpf_register_callback(cpf_callbacks, 'cpf_plim_event_cb', 40);
0213 end
0214 cpf_callbacks = cpf_register_callback(cpf_callbacks, 'cpf_default_callback', 0);
0215 %% user callbacks
0216 if ~isempty(mpopt.cpf.user_callback)
0217     %% convert to cell array, if necessary
0218     if iscell(mpopt.cpf.user_callback)
0219         user_callbacks = mpopt.cpf.user_callback;
0220     else
0221         user_callbacks = {mpopt.cpf.user_callback};
0222     end
0223     for k = 1:length(user_callbacks)
0224         %% convert each to struct, if necessary
0225         if isstruct(user_callbacks{k})
0226             ucb = user_callbacks{k};
0227         else
0228             ucb = struct('fcn', user_callbacks{k});
0229         end
0230         %% set default priority, args if not provided
0231         if ~isfield(ucb, 'priority')
0232             ucb.priority = [];
0233         end
0234         if ~isfield(ucb, 'args')
0235             ucb.args = [];
0236         end
0237         %% register the user callback
0238         cpf_callbacks = cpf_register_callback(cpf_callbacks, ...
0239             ucb.fcn, ucb.priority, ucb.args);
0240     end
0241 end
0242 nef = length(cpf_events);       %% number of event functions registered
0243 ncb = length(cpf_callbacks);    %% number of callback functions registered
0244 
0245 %% set power flow options
0246 if mpopt.verbose > 4
0247     mpopt_pf = mpoption(mpopt, 'verbose', 2);
0248 else
0249     mpopt_pf = mpoption(mpopt, 'verbose', 0);
0250 end
0251 mpopt_pf = mpoption(mpopt_pf, 'pf.enforce_q_lims', qlim);
0252 
0253 %% load base case data
0254 mpcb = loadcase(basecasedata);
0255 
0256 %% clip base active generator outputs to PMAX, if necessary
0257 idx_pmax = [];      %% indices of generators clipped at PMAX
0258 if plim
0259     idx_pmax = find( mpcb.gen(:, GEN_STATUS) > 0 & ...
0260             mpcb.gen(:, PG) - mpcb.gen(:, PMAX) > -mpopt.cpf.p_lims_tol);
0261     if mpopt.verbose && ~isempty(idx_pmax)
0262         fprintf('base case real power output of gen %d reduced from %g to %g MW (PMAX)\n', ...
0263             [idx_pmax mpcb.gen(idx_pmax, PG) mpcb.gen(idx_pmax, PMAX)]');
0264     end
0265     mpcb.gen(idx_pmax, PG) = mpcb.gen(idx_pmax, PMAX);
0266 end
0267 
0268 %% run base case power flow
0269 [rb, success] = runpf(mpcb, mpopt_pf);
0270 if success
0271     done = struct('flag', 0, 'msg', '');
0272     if qlim
0273         %% find buses that were converted to PQ or REF by initial power flow
0274         b2ref = rb.bus(:, BUS_TYPE) == REF & mpcb.bus(:, BUS_TYPE) ~= REF;
0275         b2pq  = rb.bus(:, BUS_TYPE) == PQ  & mpcb.bus(:, BUS_TYPE) ~= PQ;
0276     end
0277     mpcb = rb;      %% update base case with solved power flow
0278 else
0279     done = struct('flag', 1, 'msg', 'Base case power flow did not converge.');
0280     results = rb;
0281     results.cpf = struct();
0282 end
0283 
0284 if ~done.flag
0285     %% read target case data
0286     mpct = loadcase(targetcasedata);
0287     if size(mpct.branch,2) < QT     %% add zero columns to branch for flows if needed
0288       mpct.branch = [ mpct.branch zeros(size(mpct.branch, 1), QT-size(mpct.branch,2)) ];
0289     end
0290 
0291     %% convert both to internal indexing
0292     mpcb = ext2int(mpcb, mpopt);
0293     mpct = ext2int(mpct, mpopt);
0294     i2e_gen = mpcb.order.gen.i2e;
0295     if qlim
0296         b2ref = e2i_data(mpcb, b2ref, 'bus');
0297         b2pq  = e2i_data(mpcb, b2pq,  'bus');
0298     end
0299     nb = size(mpcb.bus, 1);
0300 
0301     %% get bus index lists of each type of bus
0302     [ref, pv, pq] = bustypes(mpcb.bus, mpcb.gen);
0303 
0304     %% generator info
0305     %% find generators that are ON and at voltage-controlled buses
0306     ong = find(mpcb.gen(:, GEN_STATUS) > 0 ...
0307               & mpcb.bus(mpcb.gen(:, GEN_BUS), BUS_TYPE) ~= PQ);
0308     gbus = mpcb.gen(ong, GEN_BUS);   %% what buses are they at?
0309 
0310     %% make sure target case is same as base case w.r.t
0311     %% bus types, GEN_STATUS, Qg and slack Pg
0312     if qlim
0313         bb = find(b2ref | b2pq);
0314         mpct.bus(bb, BUS_TYPE) = mpcb.bus(bb, BUS_TYPE);
0315     end
0316     if any(mpcb.bus(:, BUS_TYPE) ~= mpct.bus(:, BUS_TYPE))
0317         error('runcpf: BUS_TYPE of all buses must be the same in base and target cases');
0318     end
0319     if any(mpcb.gen(:, GEN_STATUS) ~= mpct.gen(:, GEN_STATUS))
0320         error('runcpf: GEN_STATUS of all generators must be the same in base and target cases');
0321     end
0322     mpct.gen(ong, QG) = mpcb.gen(ong, QG);
0323     if qlim
0324         %% find generators that are ON and at buses that were
0325         %% converted to PQ by initial power flow
0326         g2pq  = find(mpcb.gen(:, GEN_STATUS) > 0 & b2pq(mpcb.gen(:, GEN_BUS)));
0327         mpct.gen(g2pq, QG) = mpcb.gen(g2pq, QG);
0328     end
0329     for k = 1:length(ref)
0330         refgen = find(gbus == ref(k));
0331         mpct.gen(ong(refgen), PG) = mpcb.gen(ong(refgen), PG);
0332     end
0333 
0334     %% zero transfers for gens that exceed PMAX limits, if necessary
0335     if plim
0336         idx_pmax = find( mpcb.gen(:, GEN_STATUS) > 0 & ...
0337             mpcb.gen(:, PG)   - mpcb.gen(:, PMAX)   > -mpopt.cpf.p_lims_tol & ...
0338             mpct.gen(:, PG) - mpct.gen(:, PMAX) > -mpopt.cpf.p_lims_tol);
0339         if ~isempty(idx_pmax)
0340             if mpopt.verbose
0341                 fprintf('target case real power output of gen %d reduced from %g to %g MW (PMAX)\n', ...
0342                     [i2e_gen(idx_pmax) mpct.gen(idx_pmax, PG) mpcb.gen(idx_pmax, PG)]');
0343             end
0344             mpct.gen(idx_pmax, PG) = mpcb.gen(idx_pmax, PG);
0345         end
0346     end
0347 
0348     %%-----  run the continuation power flow  -----
0349     t0 = tic;
0350     if mpopt.verbose
0351         v = mpver('all');
0352         fprintf('\nMATPOWER Version %s, %s', v.Version, v.Date);
0353         fprintf(' -- AC Continuation Power Flow\n');
0354         if mpopt.verbose > 1
0355             fprintf('step %3d  :                      lambda = %6.3f, %2d Newton steps\n', 0, 0, mpcb.iterations);
0356         end
0357     end
0358 
0359     %% build admittance matrices
0360     [Ybus, Yf, Yt] = makeYbus(mpcb.baseMVA, mpcb.bus, mpcb.branch);
0361 
0362     %% functions for computing base and target case V-dependent complex bus
0363     %% power injections: (generation - load)
0364     Sbusb = @(Vm)makeSbus(mpcb.baseMVA, mpcb.bus, mpcb.gen, mpopt, Vm);
0365     Sbust = @(Vm)makeSbus(mpct.baseMVA, mpct.bus, mpct.gen, mpopt, Vm);
0366 
0367     %% initialize variables
0368     cont_steps = 0;
0369     lam = 0;
0370     V   = mpcb.bus(:, VM) .* exp(1j * pi/180 * mpcb.bus(:, VA));
0371     rollback = 0;   %% flag to indicate that a step must be rolled back
0372     locating = 0;   %% flag to indicate that an event interval was detected,
0373                     %% but the event has not yet been located
0374     rb_cnt_ef = 0;  %% counter for rollback steps triggered by event function intervals
0375     rb_cnt_cb = 0;  %% counter for rollback steps triggered directly by callbacks
0376 
0377     %% initialize tangent predictor: z = [dx;dlam]
0378     z = [zeros(2*nb, 1); 1];
0379     direction = 1;
0380     z = cpf_tangent(V, lam, Ybus, Sbusb, Sbust, pv, pq, ...
0381                     z, V, lam, parm, direction);
0382 
0383     %% initialize state for current continuation step
0384     cx = struct(...         %% current state
0385         'lam_hat', lam, ...         %% predicted lambda
0386         'V_hat', V, ...             %% predicted V
0387         'lam', lam, ...             %% corrected lambda
0388         'V', V, ...                 %% corrected V
0389         'z', z, ...                 %% normalized tangent predictor
0390         'default_step', step, ...   %% default step size
0391         'default_parm', parm, ...   %% default parameterization
0392         'this_step', [], ...        %% step size for this step only
0393         'this_parm', [], ...        %% parameterization for this step only
0394         'step', step, ...           %% current step size
0395         'parm', parm, ...           %% current parameterization
0396         'events', [], ...           %% event log
0397         'cb', struct(), ...         %% user state, for callbacks (replaces cb_state)
0398         'ef', {cell(nef, 1)} ...    %% event function values
0399     );
0400 
0401     %% input args for callbacks
0402     cb_data = struct( ...
0403         'mpc_base', mpcb, ...       %% MATPOWER case struct - base case
0404         'mpc_target', mpct, ...     %% MATPOWER case struct - target case
0405         'Sbusb', Sbusb, ...         %% function for computing base bus inj
0406         'Sbust', Sbust, ...         %% function for computing target bus inj
0407         'Ybus', Ybus, ...           %% bus admittance matrix
0408         'Yf', Yf, ...               %% branch admittance matrix, from end
0409         'Yt', Yt, ...               %% branch admittance matrix, to end
0410         'ref', ref, ...             %% vector of ref bus indices
0411         'pv', pv, ...               %% vector of PV bus indices
0412         'pq', pq, ...               %% vector of PQ bus indices
0413         'idx_pmax', idx_pmax, ...   %% vector of gen indices of gens at PMAX
0414         'mpopt', mpopt );           %% MATPOWER option struct
0415 
0416     %% initialize event function values
0417     for k = 1:nef
0418         cx.ef{k} = cpf_events(k).fcn(cb_data, cx);
0419     end
0420 
0421     %% invoke callbacks - "initialize" context
0422     for k = 1:ncb
0423         [nx, cx, done, rollback, evnts, cb_data] = cpf_callbacks(k).fcn( ...
0424             cont_steps, cx, cx, cx, done, 0, [], cb_data, cpf_callbacks(k).args);
0425     end
0426 
0427     %% check for case with no transfer
0428     if norm(Sbust(abs(cx.V)) - Sbusb(abs(cx.V))) == 0
0429         done.flag = 1;
0430         done.msg = 'Base case and target case have identical load and generation';
0431     end
0432 
0433     cont_steps = cont_steps + 1;
0434     px = cx;    %% initialize state for previous continuation step
0435     while ~done.flag
0436         %% initialize next candidate with current state
0437         nx = cx;
0438 
0439         %% prediction for next step
0440         [nx.V_hat, nx.lam_hat] = cpf_predictor(cx.V, cx.lam, cx.z, cx.step, cb_data.pv, cb_data.pq);
0441 
0442         %% correction
0443         [nx.V, success, i, nx.lam] = cpf_corrector(Ybus, cb_data.Sbusb, nx.V_hat, cb_data.ref, cb_data.pv, cb_data.pq, ...
0444                     nx.lam_hat, cb_data.Sbust, cx.V, cx.lam, cx.z, cx.step, cx.parm, mpopt_pf);
0445         if ~success     %% corrector failed
0446             done.flag = 1;
0447             done.msg = sprintf('Corrector did not converge in %d iterations.', i);
0448             if mpopt.verbose
0449                 fprintf('step %3d  : stepsize = %-9.3g lambda = %6.3f  corrector did not converge in %d iterations\n', cont_steps, cx.step, nx.lam, i);
0450             end
0451             cont_steps = max(cont_steps - 1, 1);    %% go back to last step, but not to 0
0452             break;
0453         end
0454 
0455         %% compute new tangent direction, based on a previous state: tx
0456         if nx.step == 0     %% if this is a re-do step, cx and nx are the same
0457             tx = px;            %% so use px as the previous state
0458         else                %% otherwise
0459             tx = cx;            %% use cx as the previous state
0460         end
0461         nx.z = cpf_tangent(nx.V, nx.lam, Ybus, cb_data.Sbusb, cb_data.Sbust, ...
0462                             cb_data.pv, cb_data.pq, ...
0463                             cx.z, tx.V, tx.lam, nx.parm, direction);
0464 
0465         %% detect events
0466         for k = 1:nef
0467             nx.ef{k} = cpf_events(k).fcn(cb_data, nx);      %% update event functions
0468         end
0469         [rollback, evnts, nx.ef] = cpf_detect_events(cpf_events, nx.ef, cx.ef, nx.step, mpopt.verbose);
0470 
0471         %% adjust step-size to locate event function zero, if necessary
0472         if rollback                 %% current step overshot
0473             %% rollback and initialize next step size based on rollback and previous
0474             rx = nx;                    %% save state we're rolling back from
0475             rx.evnts = evnts;           %% and critical event info
0476             cx.this_step = evnts.step_scale * rx.step;
0477             cx.this_parm = rx.parm;     %% keep same parameterization as last step
0478             locating = 1;               %% enter "locating" mode (or stay in it)
0479             rb_cnt_ef = rb_cnt_ef + 1;  %% increment rollback counter for ef intervals
0480             if rb_cnt_ef > 26
0481                 done.flag = 1;
0482                 done.msg = sprintf('Could not locate %s event!', evnts.name);
0483             end
0484             if mpopt.verbose > 3
0485                 loc_msg = sprintf('OVERSHOOT  : f = [%g, <<%g>>], step <-- %.4g', ...
0486                             cx.ef{evnts.eidx}(evnts.idx(1)), ...
0487                             rx.ef{evnts.eidx}(evnts.idx(1)), cx.this_step);
0488             end
0489         elseif locating
0490             if evnts(1).zero        %% found the zero!
0491                 %% step size will be reset to previously used default step size
0492                 locating = 0;           %% exit "locating" mode
0493                 rb_cnt_ef = 0;          %% reset rollback counter for ef intervals
0494                 if mpopt.verbose > 3
0495                     loc_msg = sprintf('ZERO!      : f = %g, step <-- %.4g', ...
0496                         nx.ef{rx.evnts.eidx}(rx.evnts.idx(1)), nx.default_step);
0497                 end
0498             else                    %% prev rollback undershot
0499                 %% initialize next step size based on critical event function
0500                 %% values from prev rollback step and current step
0501                 rx_ef = rx.ef{rx.evnts.eidx}(rx.evnts.idx(1));
0502                 cx_ef = nx.ef{rx.evnts.eidx}(rx.evnts.idx(1));
0503                 step_scale = cx_ef / (cx_ef - rx_ef);
0504                 nx.this_step = step_scale * (rx.step - nx.step);
0505                 rb_cnt_ef = 0;          %% reset rollback counter for ef intervals
0506                 if mpopt.verbose > 3
0507                     loc_msg = sprintf('UNDERSHOOT : f [<<%g>>, %g], step <-- %.4g', ...
0508                         cx_ef, rx_ef, nx.this_step);
0509                 end
0510             end
0511         else
0512             loc_msg = '';
0513             direction = 1;
0514         end
0515 
0516         %% invoke callbacks - "iterations" context
0517         rb = rollback;
0518         for k = 1:ncb
0519             [nx, cx, done, rollback, evnts, cb_data] = cpf_callbacks(k).fcn( ...
0520                 cont_steps, nx, cx, px, done, rollback, evnts, cb_data, cpf_callbacks(k).args);
0521         end
0522         if ~rb && rollback      %% rollback triggered by callback (vs event function interval)
0523             rb_cnt_cb = rb_cnt_cb + 1;  %% increment rollback counter for callbacks
0524             if rb_cnt_cb > 26
0525                 done.flag = 1;
0526                 done.msg = 'Too many rollback steps triggered by callbacks!';
0527             end
0528         else
0529             if ~done.flag && evnts(1).zero
0530                 mpce = cpf_current_mpc(cb_data.mpc_base, cb_data.mpc_target, Ybus, Yf, Yt, cb_data.ref, cb_data.pv, cb_data.pq, nx.V, nx.lam, mpopt);
0531                 J = makeJac(mpce);
0532                 opts.tol = 1e-3;
0533                 opts.it = 2*nb;
0534                 %% retain original direction for buses with negative V-Q
0535                 %% sensitivity, otherwise change direction based on tangent
0536                 %% direction and manifold (eigenvalue)
0537                 if isempty(find(nx.z(nb+pq) > 0, 1))
0538                     direction = sign(nx.z(end)*min(real(eigs(J,1,'SR',opts))));
0539                 end
0540             end
0541             rb_cnt_cb = 0;              %% reset rollback counter for callbacks
0542         end
0543 
0544         %% print iteration information
0545         if mpopt.verbose > 1
0546             %% set label for rollback step counter
0547             if rb_cnt_ef
0548                 sub_step = char('a' + rb_cnt_ef - 1);
0549             elseif rb_cnt_cb
0550                 sub_step = char('A' + rb_cnt_cb - 1);
0551             else
0552                 sub_step = ' ';
0553             end
0554             if mpopt.verbose > 4
0555                 fprintf('step %3d%s : stepsize = %-9.3g lambda = %6.3f', cont_steps, sub_step, cx.step, nx.lam);
0556             else
0557                 fprintf('step %3d%s : stepsize = %-9.3g lambda = %6.3f  %2d corrector Newton steps', cont_steps, sub_step, cx.step, nx.lam, i);
0558             end
0559             if rollback
0560                 fprintf(' ^ ROLLBACK\n');
0561             else
0562                 fprintf('\n');
0563             end
0564             if mpopt.verbose > 3 && ~isempty(loc_msg)
0565                 fprintf('    LOCATING -- %s\n', loc_msg);
0566             end
0567         end
0568 
0569         %% log events
0570         for k = 1:length(evnts)
0571             if evnts(k).log
0572                 e = struct( 'k', cont_steps, ...
0573                             'name', evnts(k).name, ...
0574                             'idx', evnts(k).idx, ...
0575                             'msg', evnts(k).msg   );
0576                 if isempty(nx.events)
0577                     nx.events = e;
0578                 else
0579                     nx.events(end+1) = e;
0580                 end
0581             end
0582             if (mpopt.verbose > 2 && evnts(k).log) || ...
0583                     (mpopt.verbose > 3 && evnts(k).eidx)
0584                 fprintf('    %s\n', evnts(k).msg);
0585             end
0586         end
0587         
0588         %% adapt stepsize if requested and not terminating, locating a zero
0589         %% or re-doing a step after changing the problem data
0590         if adapt_step && ~done.flag && ~locating && ~evnts(1).zero && nx.step ~= 0
0591             cpf_error = norm([angle(nx.V(    cb_data.pq)); abs(nx.V(    [cb_data.pv;cb_data.pq])); nx.lam] - ...
0592                              [angle(nx.V_hat(cb_data.pq)); abs(nx.V_hat([cb_data.pv;cb_data.pq])); nx.lam_hat], inf);
0593 
0594             %% new nominal step size is current size * tol/err, but we reduce
0595             %% the change from the current size by a damping factor and limit
0596             %% increases to a factor of 2
0597             step_scale = min(2, 1 + mpopt.cpf.adapt_step_damping * ...
0598                             (mpopt.cpf.adapt_step_tol/cpf_error - 1));
0599             nx.default_step = nx.step * step_scale;
0600 
0601             %% limit step-size
0602             if nx.default_step > mpopt.cpf.step_max
0603                 nx.default_step = mpopt.cpf.step_max;
0604             end
0605             if nx.default_step < mpopt.cpf.step_min
0606                 nx.default_step = mpopt.cpf.step_min;
0607             end
0608         end
0609 
0610         %% if this is a normal step
0611         if ~rollback
0612             px = cx;    %% save current state before update
0613             cx = nx;    %% update current state to next candidate
0614             if ~done.flag
0615                 cont_steps = cont_steps + 1;
0616             end
0617         end
0618     
0619         %% set step size and parameterization, from one-time or defaults
0620         if isempty(cx.this_step)
0621             cx.step = cx.default_step;
0622         else
0623             cx.step = cx.this_step;
0624             cx.this_step = [];      %% disable for next time
0625         end
0626         if isempty(cx.this_parm)
0627             cx.parm = cx.default_parm;
0628         else
0629             cx.parm = cx.this_parm;
0630             cx.this_parm = [];      %% disable for next time
0631         end
0632     end
0633 
0634     %% invoke callbacks - "final" context
0635     cpf_results = struct();     %% initialize results struct
0636     for k = 1:ncb
0637         [nx, cx, done, rollback, evnts, cb_data, cpf_results] = ...
0638             cpf_callbacks(k).fcn(-cont_steps, nx, cx, px, ...
0639                 done, rollback, evnts, cb_data, cpf_callbacks(k).args, cpf_results);
0640     end
0641     cpf_results.events = cx.events;     %% copy eventlog to results
0642 
0643     %% update final case with solution
0644     mpct = cpf_current_mpc(cb_data.mpc_base, cb_data.mpc_target, Ybus, Yf, Yt, cb_data.ref, cb_data.pv, cb_data.pq, cx.V, cx.lam, mpopt);
0645     mpct.et = toc(t0);
0646     mpct.success = success;
0647 
0648     %%-----  output results  -----
0649     %% convert back to original bus numbering & print results
0650     n = size(cpf_results.V, 2);
0651     cpf_results.V_hat = i2e_data(mpct, cpf_results.V_hat, NaN(nb,n), 'bus', 1);
0652     cpf_results.V     = i2e_data(mpct, cpf_results.V,     NaN(nb,n), 'bus', 1);
0653     results = int2ext(mpct);
0654     results.cpf = cpf_results;
0655 
0656     %% zero out result fields of out-of-service gens & branches
0657     if ~isempty(results.order.gen.status.off)
0658       results.gen(results.order.gen.status.off, [PG QG]) = 0;
0659     end
0660     if ~isempty(results.order.branch.status.off)
0661       results.branch(results.order.branch.status.off, [PF QF PT QT]) = 0;
0662     end
0663 end
0664 
0665 results.cpf.done_msg = done.msg;
0666 if mpopt.verbose
0667     fprintf('CPF TERMINATION: %s\n', done.msg);
0668 end
0669 
0670 if fname
0671     [fd, msg] = fopen(fname, 'at');
0672     if fd == -1
0673         error(msg);
0674     else
0675         if mpopt.out.all == 0
0676             printpf(results, fd, mpoption(mpopt, 'out.all', -1));
0677         else
0678             printpf(results, fd, mpopt);
0679         end
0680         fclose(fd);
0681     end
0682 end
0683 printpf(results, 1, mpopt);
0684 
0685 %% save solved case
0686 if solvedcase
0687     savecase(solvedcase, results);
0688 end
0689 
0690 if nargout
0691     res = results;
0692     if nargout > 1
0693         suc = success;
0694     end
0695 % else  %% don't define res, so it doesn't print anything
0696 end

Generated on Mon 24-Jun-2019 15:58:45 by m2html © 2005