CPF_DEFAULT_CALLBACK Default callback function for CPF [NX, CX, DONE, ROLLBACK, EVNTS, CB_DATA, RESULTS] = CPF_DEFAULT_CALLBACK(K, NX, CX, PX, DONE, ROLLBACK, EVNTS, ... CB_DATA, CB_ARGS, RESULTS) Default callback function used by RUNCPF that collects the resulst and optionally, plots the nose curve. Inputs and outputs are defined below, with the RESULTS argument being optional, used only for the final call when K is negative. Inputs: K - continuation step iteration count NX - next state (corresponding to proposed next step), struct with the following fields: lam_hat - value of LAMBDA from predictor V_hat - vector of complex bus voltages from predictor lam - value of LAMBDA from corrector V - vector of complex bus voltages from corrector z - normalized tangent predictor default_step - default step size default_parm - default parameterization this_step - step size for this step only this_parm - paramterization for this step only step - current step size parm - current parameterization events - struct array, event log cb - user state, for callbacks (replaces CB_STATE), the user may add fields containing any information the callback function would like to pass from one invokation to the next, taking care not to step on fields being used by other callbacks, such as the 'default' field used by this default callback ef - cell array of event function values CX - current state (corresponding to most recent successful step) (same structure as NX) PX - previous state (corresponding to last successful step prior to CX) DONE - struct, with flag to indicate CPF termination and reason, with fields: flag - termination flag, 1 => terminate, 0 => continue msg - string containing reason for termination ROLLBACK - scalar flag to indicate that the current step should be rolled back and retried with a different step size, etc. EVNTS - struct array listing any events detected for this step, see CPF_DETECT_EVENTS for details CB_DATA - struct containing potentially useful "static" data, with the following fields (all based on internal indexing): mpc_base - MATPOWER case struct of base state mpc_target - MATPOWER case struct of target state Sbusb - handle of function returning nb x 1 vector of complex base case injections in p.u. and derivatives w.r.t. |V| Sbust - handle of function returning nb x 1 vector of complex target case injections in p.u. and derivatives w.r.t. |V| Ybus - bus admittance matrix Yf - branch admittance matrix, "from" end of branches Yt - branch admittance matrix, "to" end of branches pv - vector of indices of PV buses pq - vector of indices of PQ buses ref - vector of indices of REF buses idx_pmax - vector of generator indices for generators fixed at their PMAX limits mpopt - MATPOWER options struct CB_ARGS - arbitrary data structure containing callback arguments RESULTS - initial value of output struct to be assigned to CPF field of results struct returned by RUNCPF Outputs: (all are updated versions of the corresponding input arguments) NX - user state ('cb' field ) should be updated here if ROLLBACK is false CX - may contain updated 'this_step' or 'this_parm' values to be used if ROLLBACK is true DONE - callback may have requested termination and set the msg field ROLLBACK - callback can request a rollback step, even if it was not indicated by an event function EVNTS - msg field for a given event may be updated CB_DATA - this data should only be modified if the underlying problem has been changed (e.g. generator limit reached) and should always be followed by a step of zero length, i.e. set NX.this_step to 0 It is the job of any callback modifying CB_DATA to ensure that all data in CB_DATA is kept consistent. RESULTS - updated version of RESULTS input arg This function is called in three different contexts, distinguished by the value of K, as follows: (1) initial - called with K = 0, without RESULTS input/output args, after base power flow, before 1st CPF step. (2) iterations - called with K > 0, without RESULTS input/output args, at each iteration, after predictor-corrector step (3) final - called with K < 0, with RESULTS input/output args, after exiting predictor-corrector loop, inputs identical to last iteration call, except K which is negated User Defined CPF Callback Functions: The user can define their own callback functions which take the same form and are called in the same contexts as CPF_DEFAULT_CALLBACK. These are specified via the MATPOWER option 'cpf.user_callback'. This option can be a string containing the name of the callback function, or a struct with the following fields, where all but the first are optional: 'fcn' - string with name of callback function 'priority' - numerical value specifying callback priority (default = 20, see CPF_REGISTER_CALLBACK for details) 'args' - arbitrary value (any type) passed to the callback as CB_ARGS each time it is invoked Multiple user callbacks can be registered by assigning a cell array of such strings and/or structs to the 'cpf.user_callback' option. See also RUNCPF, CPF_REGISTER_CALLBACK.
0001 function [nx, cx, done, rollback, evnts, cb_data, results] = ... 0002 cpf_default_callback(k, nx, cx, px, done, rollback, evnts, ... 0003 cb_data, cb_args, results) 0004 %CPF_DEFAULT_CALLBACK Default callback function for CPF 0005 % [NX, CX, DONE, ROLLBACK, EVNTS, CB_DATA, RESULTS] = 0006 % CPF_DEFAULT_CALLBACK(K, NX, CX, PX, DONE, ROLLBACK, EVNTS, ... 0007 % CB_DATA, CB_ARGS, RESULTS) 0008 % 0009 % Default callback function used by RUNCPF that collects the resulst and 0010 % optionally, plots the nose curve. Inputs and outputs are defined below, 0011 % with the RESULTS argument being optional, used only for the final call 0012 % when K is negative. 0013 % 0014 % Inputs: 0015 % K - continuation step iteration count 0016 % NX - next state (corresponding to proposed next step), struct with 0017 % the following fields: 0018 % lam_hat - value of LAMBDA from predictor 0019 % V_hat - vector of complex bus voltages from predictor 0020 % lam - value of LAMBDA from corrector 0021 % V - vector of complex bus voltages from corrector 0022 % z - normalized tangent predictor 0023 % default_step - default step size 0024 % default_parm - default parameterization 0025 % this_step - step size for this step only 0026 % this_parm - paramterization for this step only 0027 % step - current step size 0028 % parm - current parameterization 0029 % events - struct array, event log 0030 % cb - user state, for callbacks (replaces CB_STATE), the user may 0031 % add fields containing any information the callback function 0032 % would like to pass from one invokation to the next, taking 0033 % care not to step on fields being used by other callbacks, 0034 % such as the 'default' field used by this default callback 0035 % ef - cell array of event function values 0036 % CX - current state (corresponding to most recent successful step) 0037 % (same structure as NX) 0038 % PX - previous state (corresponding to last successful step prior to CX) 0039 % DONE - struct, with flag to indicate CPF termination and reason, 0040 % with fields: 0041 % flag - termination flag, 1 => terminate, 0 => continue 0042 % msg - string containing reason for termination 0043 % ROLLBACK - scalar flag to indicate that the current step should be 0044 % rolled back and retried with a different step size, etc. 0045 % EVNTS - struct array listing any events detected for this step, 0046 % see CPF_DETECT_EVENTS for details 0047 % CB_DATA - struct containing potentially useful "static" data, 0048 % with the following fields (all based on internal indexing): 0049 % mpc_base - MATPOWER case struct of base state 0050 % mpc_target - MATPOWER case struct of target state 0051 % Sbusb - handle of function returning nb x 1 vector of complex 0052 % base case injections in p.u. and derivatives w.r.t. |V| 0053 % Sbust - handle of function returning nb x 1 vector of complex 0054 % target case injections in p.u. and derivatives w.r.t. |V| 0055 % Ybus - bus admittance matrix 0056 % Yf - branch admittance matrix, "from" end of branches 0057 % Yt - branch admittance matrix, "to" end of branches 0058 % pv - vector of indices of PV buses 0059 % pq - vector of indices of PQ buses 0060 % ref - vector of indices of REF buses 0061 % idx_pmax - vector of generator indices for generators fixed 0062 % at their PMAX limits 0063 % mpopt - MATPOWER options struct 0064 % CB_ARGS - arbitrary data structure containing callback arguments 0065 % RESULTS - initial value of output struct to be assigned to 0066 % CPF field of results struct returned by RUNCPF 0067 % 0068 % Outputs: 0069 % (all are updated versions of the corresponding input arguments) 0070 % NX - user state ('cb' field ) should be updated here if ROLLBACK 0071 % is false 0072 % CX - may contain updated 'this_step' or 'this_parm' values to be used 0073 % if ROLLBACK is true 0074 % DONE - callback may have requested termination and set the msg field 0075 % ROLLBACK - callback can request a rollback step, even if it was not 0076 % indicated by an event function 0077 % EVNTS - msg field for a given event may be updated 0078 % CB_DATA - this data should only be modified if the underlying problem 0079 % has been changed (e.g. generator limit reached) and should always 0080 % be followed by a step of zero length, i.e. set NX.this_step to 0 0081 % It is the job of any callback modifying CB_DATA to ensure that 0082 % all data in CB_DATA is kept consistent. 0083 % RESULTS - updated version of RESULTS input arg 0084 % 0085 % This function is called in three different contexts, distinguished by 0086 % the value of K, as follows: 0087 % (1) initial - called with K = 0, without RESULTS input/output args, 0088 % after base power flow, before 1st CPF step. 0089 % (2) iterations - called with K > 0, without RESULTS input/output args, 0090 % at each iteration, after predictor-corrector step 0091 % (3) final - called with K < 0, with RESULTS input/output args, after 0092 % exiting predictor-corrector loop, inputs identical to last 0093 % iteration call, except K which is negated 0094 % 0095 % User Defined CPF Callback Functions: 0096 % The user can define their own callback functions which take 0097 % the same form and are called in the same contexts as 0098 % CPF_DEFAULT_CALLBACK. These are specified via the MATPOWER 0099 % option 'cpf.user_callback'. This option can be a string containing 0100 % the name of the callback function, or a struct with the following 0101 % fields, where all but the first are optional: 0102 % 'fcn' - string with name of callback function 0103 % 'priority' - numerical value specifying callback priority 0104 % (default = 20, see CPF_REGISTER_CALLBACK for details) 0105 % 'args' - arbitrary value (any type) passed to the callback 0106 % as CB_ARGS each time it is invoked 0107 % Multiple user callbacks can be registered by assigning a cell array 0108 % of such strings and/or structs to the 'cpf.user_callback' option. 0109 % 0110 % See also RUNCPF, CPF_REGISTER_CALLBACK. 0111 0112 % MATPOWER 0113 % Copyright (c) 2013-2016, Power Systems Engineering Research Center (PSERC) 0114 % by Ray Zimmerman, PSERC Cornell 0115 % 0116 % This file is part of MATPOWER. 0117 % Covered by the 3-clause BSD License (see LICENSE file for details). 0118 % See https://matpower.org for more info. 0119 0120 %% skip if rollback, except if it is a FINAL call 0121 if rollback && k > 0 0122 return; 0123 end 0124 0125 %% initialize variables 0126 step = nx.step; 0127 V = nx.V; 0128 lam = nx.lam; 0129 V_hat = nx.V_hat; 0130 lam_hat = nx.lam_hat; 0131 0132 %%----- initialize/update state/results ----- 0133 if k == 0 %% INITIAL call 0134 %% initialize state 0135 cxx = struct( 'V_hat', V_hat, ... 0136 'lam_hat', lam_hat, ... 0137 'V', V, ... 0138 'lam', lam, ... 0139 'steps', step, ... 0140 'iterations', 0 ); 0141 nxx = cxx; 0142 cx.cb.default = cxx; %% update current callback state 0143 nx.cb.default = nxx; %% updatenext callback state 0144 else 0145 nxx = nx.cb.default; %% get next callback state 0146 if k > 0 %% ITERATION call 0147 %% update state 0148 nxx.V_hat = [nxx.V_hat V_hat]; 0149 nxx.lam_hat = [nxx.lam_hat lam_hat]; 0150 nxx.V = [nxx.V V]; 0151 nxx.lam = [nxx.lam lam]; 0152 nxx.steps = [nxx.steps step]; 0153 nxx.iterations = k; 0154 nx.cb.default = nxx; %% update next callback state 0155 else %% FINAL call 0156 %% assemble results struct 0157 results.V_hat = nxx.V_hat; 0158 results.lam_hat = nxx.lam_hat; 0159 results.V = nxx.V; 0160 results.lam = nxx.lam; 0161 results.steps = nxx.steps; 0162 results.iterations = -k; 0163 results.max_lam = max(nxx.lam); 0164 end 0165 end 0166 0167 %%----- plot continuation curve ----- 0168 %% initialize plotting options 0169 plot_level = cb_data.mpopt.cpf.plot.level; 0170 plot_bus = cb_data.mpopt.cpf.plot.bus; 0171 plot_bus_default = 0; 0172 if plot_level 0173 if isempty(plot_bus) && ~isfield(nxx, 'plot_bus_default') %% no bus specified 0174 %% pick PQ bus with largest transfer 0175 Sxfr = cb_data.Sbust(abs(V)) - cb_data.Sbusb(abs(V)); 0176 [junk, idx] = max(Sxfr(cb_data.pq)); 0177 if isempty(idx) %% or bus 1 if there are none 0178 idx = 1; 0179 else 0180 idx = cb_data.pq(idx(1)); 0181 end 0182 idx_e = cb_data.mpc_target.order.bus.i2e(idx); 0183 0184 %% save it to keep it from changing in subsequent calls 0185 plot_bus_default = idx_e; 0186 else 0187 if isempty(plot_bus) 0188 idx_e = nxx.plot_bus_default; %% external bus number, saved 0189 else 0190 idx_e = plot_bus; %% external bus number, provided 0191 end 0192 if any(idx_e > length(cb_data.mpc_target.order.bus.e2i)) 0193 kk = find(idx_e > length(cb_data.mpc_target.order.bus.e2i)); 0194 error('cpf_default_callback: %d is not a valid bus number for MPOPT.cpf.plot.bus', idx_e(kk(1))); 0195 end 0196 idx = full(cb_data.mpc_target.order.bus.e2i(idx_e)); 0197 if any(idx == 0) 0198 kk = find(idx == 0); 0199 error('cpf_default_callback: %d is not a valid bus number for MPOPT.cpf.plot.bus', idx_e(kk(1))); 0200 end 0201 end 0202 nplots = length(idx_e); 0203 0204 %% set bounds for plot axes 0205 xmin = 0; 0206 xmax = max([max(nxx.lam_hat); max(nxx.lam)]); 0207 ymin = min([min(min(abs(nxx.V_hat(idx, :)))); min(min(abs(nxx.V(idx, :))))]); 0208 ymax = max([max(max(abs(nxx.V_hat(idx, :)))); max(max(abs(nxx.V(idx, :))))]); 0209 if xmax < xmin + cb_data.mpopt.cpf.step / 100; 0210 xmax = xmin + cb_data.mpopt.cpf.step / 100; 0211 end 0212 if ymax - ymin < 2e-5; 0213 ymax = ymax + 1e-5; 0214 ymin = ymin - 1e-5; 0215 end 0216 xmax = xmax * 1.05; 0217 ymax = ymax + 0.05 * (ymax-ymin); 0218 ymin = ymin - 0.05 * (ymax-ymin); 0219 0220 %%----- INITIAL call ----- 0221 if k == 0 0222 %% save default plot bus in the state so we don't have to detect it 0223 %% each time, since we don't want it to change in the middle of the run 0224 if plot_bus_default 0225 cx.cb.default.plot_bus_default = plot_bus_default; 0226 end 0227 0228 %% initialize lambda-V nose curve plot 0229 axis([xmin xmax ymin ymax]); 0230 plot(cxx.lam_hat(1), abs(cxx.V_hat(idx,1)), '-', 'Color', [0.25 0.25 1]); 0231 if nplots > 1 0232 title('Voltage at Multiple Buses'); 0233 else 0234 title(sprintf('Voltage at Bus %d', idx_e)); 0235 end 0236 xlabel('\lambda'); 0237 ylabel('Voltage Magnitude'); 0238 hold on; 0239 %%----- ITERATION call ----- 0240 elseif k > 0 0241 %% plot single step of the lambda-V nose curve 0242 if plot_level > 1 0243 axis([xmin xmax ymin ymax]); 0244 for kk = 1:nplots 0245 %% prediction line 0246 plot([nxx.lam(k); nxx.lam_hat(k+1)], ... 0247 [abs(nxx.V(idx(kk),k)); abs(nxx.V_hat(idx(kk),k+1))], '-', ... 0248 'Color', 0.85*[1 0.75 0.75]); 0249 %% correction line 0250 plot([nxx.lam_hat(k+1); nxx.lam(k+1)], ... 0251 [abs(nxx.V_hat(idx(kk),k+1)); abs(nxx.V(idx(kk),k+1))], '-', ... 0252 'Color', 0.85*[0.75 1 0.75]); 0253 %% prediciton point 0254 plot(nxx.lam_hat(k+1), abs(nxx.V_hat(idx(kk),k+1)), 'x', ... 0255 'Color', 0.85*[1 0.75 0.75]); 0256 %% correction point 0257 plot(nxx.lam(k+1)', abs(nxx.V(idx(kk),k+1))', '-o', ... 0258 'Color', [0.25 0.25 1]); 0259 drawnow; 0260 end 0261 if plot_level > 2 0262 pause; 0263 end 0264 end 0265 %%----- FINAL call ----- 0266 else % k < 0 0267 %% finish final lambda-V nose curve plot 0268 axis([xmin xmax ymin ymax]); 0269 %% curve of corrected points 0270 if isprop(gca, 'ColorOrderIndex') 0271 set(gca, 'ColorOrderIndex', 1); %% start over with color 1 0272 end 0273 hp = plot(nxx.lam', abs(nxx.V(idx,:))', '-'); 0274 if nplots > 1 0275 leg = cell(nplots, 1); 0276 for kk = 1:nplots 0277 leg{kk} = sprintf('Bus %d', idx_e(kk)); 0278 end 0279 legend(hp, leg); 0280 end 0281 hold off; 0282 end 0283 end