CPF_DETECT_EVENTS Detect events from event function values [ROLLBACK, CRITICAL_EVENTS, CEF] = CPF_DETECT_EVENTS(CPF_EVENTS, CEF, PEF, STEP, VERBOSE) Inputs: CPF_EVENTS : struct containing info about registered CPF event fcns CEF : cell array of Current Event Function values PEF : cell array of Previous Event Function values STEP : current step size VERBOSE : 0 = no output, otherwise level of verbose output Outputs: ROLLBACK : flag indicating whether any event has requested a rollback step CRITICAL_EVENTS : struct array containing information about any detected events, with fields: eidx : event index, in list of registered events 0 if no event detected name : name of event function, empty if none detected zero : 1 if zero has been detected, 0 otherwise (interval detected or no event detected) idx : index(es) of critical elements in event function step_scale : linearly interpolated estimate of scaling factor for current step size required to reach event zero log : 1 log the event in the results, 0 don't log the event (set to 1 for zero events, 0 otherwise, can be modified by callbacks) msg : event message, set to something generic like 'ZERO detected for TARGET_LAM event' or 'INTERVAL detected for QLIM(3) event', but intended to be changed/updated by callbacks CEF : cell array of Current Event Function values
0001 function [rollback, evnts, cef] = cpf_detect_events(cpf_events, cef, pef, step, verbose) 0002 %CPF_DETECT_EVENTS Detect events from event function values 0003 % [ROLLBACK, CRITICAL_EVENTS, CEF] = CPF_DETECT_EVENTS(CPF_EVENTS, CEF, PEF, STEP, VERBOSE) 0004 % 0005 % Inputs: 0006 % CPF_EVENTS : struct containing info about registered CPF event fcns 0007 % CEF : cell array of Current Event Function values 0008 % PEF : cell array of Previous Event Function values 0009 % STEP : current step size 0010 % VERBOSE : 0 = no output, otherwise level of verbose output 0011 % 0012 % Outputs: 0013 % ROLLBACK : flag indicating whether any event has requested a 0014 % rollback step 0015 % CRITICAL_EVENTS : struct array containing information about any 0016 % detected events, with fields: 0017 % eidx : event index, in list of registered events 0018 % 0 if no event detected 0019 % name : name of event function, empty if none detected 0020 % zero : 1 if zero has been detected, 0 otherwise 0021 % (interval detected or no event detected) 0022 % idx : index(es) of critical elements in event function 0023 % step_scale : linearly interpolated estimate of scaling factor 0024 % for current step size required to reach event zero 0025 % log : 1 log the event in the results, 0 don't log the event 0026 % (set to 1 for zero events, 0 otherwise, can be 0027 % modified by callbacks) 0028 % msg : event message, set to something generic like 0029 % 'ZERO detected for TARGET_LAM event' or 0030 % 'INTERVAL detected for QLIM(3) event', but intended 0031 % to be changed/updated by callbacks 0032 % CEF : cell array of Current Event Function values 0033 0034 % MATPOWER 0035 % Copyright (c) 2016-2017, Power Systems Engineering Research Center (PSERC) 0036 % by Ray Zimmerman, PSERC Cornell 0037 % and Shrirang Abhyankar, Argonne National Laboratory 0038 % 0039 % This file is part of MATPOWER. 0040 % Covered by the 3-clause BSD License (see LICENSE file for details). 0041 % See https://matpower.org for more info. 0042 0043 %% initialize result variables 0044 rollback = 0; 0045 evnts = struct( ... 0046 'eidx', 0, ... 0047 'name', '', ... 0048 'zero', 0, ... 0049 'idx', 0, ... 0050 'step_scale', 1, ... 0051 'log', 0, ... 0052 'msg', '' ... 0053 ); 0054 0055 %% other initialization 0056 i = 1; %% index into evnts struct 0057 nef = length(cef); %% number of event functions 0058 0059 %% detect events, first look for event intervals for events requesting rollback 0060 for eidx = 1:nef 0061 if ~cpf_events(eidx).locate %% if event doesn't request rollback to locate zero 0062 continue; %% skip to next event 0063 end 0064 0065 %% current and previous event function signs 0066 c_sign = sign(cef{eidx}); 0067 p_sign = sign(pef{eidx}); 0068 0069 %% if there's been a sign change and we aren't within event tolerance ... 0070 idx = find( abs(c_sign) == 1 & c_sign == -p_sign & ... 0071 abs(cef{eidx}) > cpf_events(eidx).tol ); 0072 if ~isempty(idx) 0073 if step == 0 %% if it's a "repeat" step (e.g. after bus type changes) 0074 %% ... make this one the critical one and call it a ZERO event 0075 evnts.eidx = eidx; 0076 evnts.name = cpf_events(eidx).name; 0077 evnts.zero = 1; 0078 evnts.idx = idx; 0079 evnts.step_scale = 1; 0080 evnts.log = 1; 0081 evnts.msg = 'ZERO (BIFURCATION)'; 0082 i = i + 1; 0083 break; 0084 else 0085 %% ... compute step size scaling factors and find index of smallest one 0086 [step_scale, j] = ... 0087 min(pef{eidx}(idx) ./ (pef{eidx}(idx) - cef{eidx}(idx)) ); 0088 0089 %% if it's smaller than the current critical one ... 0090 if step_scale < evnts.step_scale 0091 %% ... make this one the critical one 0092 evnts.eidx = eidx; 0093 evnts.name = cpf_events(eidx).name; 0094 evnts.zero = 0; 0095 evnts.idx = idx(j); 0096 evnts.step_scale = step_scale; 0097 evnts.log = 0; 0098 evnts.msg = 'INTERVAL'; 0099 rollback = 1; %% signal that a rollback event has been detected 0100 end 0101 end 0102 end 0103 end 0104 0105 %% if no rollback events were detected 0106 if rollback == 0 0107 %% search for event zeros 0108 for eidx = 1:nef 0109 %% if there's an event zero ... 0110 idx = find( abs(cef{eidx}) <= cpf_events(eidx).tol ); 0111 if ~isempty(idx) 0112 %% set event function to exactly zero 0113 %% (to prevent possible INTERVAL detection again on next step) 0114 cef{eidx}(idx) = 0; 0115 0116 %% ... make this one the critical one 0117 evnts(i).eidx = eidx; 0118 evnts(i).name = cpf_events(eidx).name; 0119 evnts(i).zero = 1; 0120 evnts(i).idx = idx; 0121 evnts(i).step_scale = 1; 0122 evnts(i).log = 1; 0123 evnts(i).msg = 'ZERO'; 0124 i = i + 1; 0125 end 0126 end 0127 0128 %% and if no zeros were detected 0129 if i == 1 0130 %% search for intervals for non-rollback events 0131 for eidx = 1:nef 0132 %% current and previous event function signs 0133 c_sign = sign(cef{eidx}); 0134 p_sign = sign(pef{eidx}); 0135 0136 %% if there's been a sign change ... 0137 idx = find( abs(c_sign) == 1 & c_sign == -p_sign ); 0138 if ~isempty(idx) 0139 %% ... compute step size scaling factors ... 0140 step_scale = pef{eidx}(idx) ./ (pef{eidx}(idx) - cef{eidx}(idx)); 0141 0142 %% ... and save the info as an interval detection 0143 evnts(i).eidx = eidx; 0144 evnts(i).name = cpf_events(eidx).name; 0145 evnts(i).zero = 0; 0146 evnts(i).idx = idx; 0147 evnts(i).step_scale = step_scale; 0148 evnts(i).log = 0; 0149 evnts(i).msg = 'INTERVAL'; 0150 i = i + 1; 0151 end 0152 end 0153 end 0154 end 0155 0156 %% update msgs 0157 if evnts(1).eidx 0158 for i = 1:length(evnts) 0159 if length(cef{evnts(i).eidx}) > 1 0160 s1 = sprintf('(%d)', evnts(i).idx); 0161 else 0162 s1 = ''; 0163 end 0164 if rollback 0165 s2 = sprintf('ROLLBACK by %g', evnts(i).step_scale); 0166 else 0167 s2 = 'CONTINUE'; 0168 end 0169 evnts(i).msg = sprintf('%s detected for %s%s event : %s', ... 0170 evnts(i).msg, evnts(i).name, s1, s2); 0171 end 0172 end