Skip to content

Commit

Permalink
Add 'clear_cache' options to have_fcn() (fix #65).
Browse files Browse the repository at this point in the history
- Facilitates re-checking for optional functionality after changes to the MATLAB/Octave path.
- Add tests for have_fcn().

Refs: #65
  • Loading branch information
rdzman committed May 30, 2019
1 parent c301dac commit 406842a
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 41 deletions.
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ For change history for [MOST][3], see [most/CHANGES.md](most/CHANGES.md).
Since last release
------------------

#### 5/30/19
- Add `'clear_cache'` options to `have_fcn()` to facilitate re-checking
for optional functionality after changes to the MATLAB/Octave path.
See [issue #65][19].
- Add tests for `have_fcn()`.

#### 5/29/19
- Fix errors caused by running `test_matpower` with YALMIP present
but not MOSEK, SeDuMi or SDPT3.
Expand Down Expand Up @@ -2859,3 +2865,4 @@ First Public Release – *Jun 25, 1997*
[16]: https://github.com/MATPOWER/matpower/issues/57
[17]: https://hub.docker.com/r/matpower/matpower-desktop
[18]: https://github.com/MATPOWER/matpower/blob/master/docker/MATPOWER-Docker.md
[19]: https://github.com/MATPOWER/matpower/issues/65
18 changes: 10 additions & 8 deletions docs/src/MATPOWER-manual/MATPOWER-manual.tex
Original file line number Diff line number Diff line change
Expand Up @@ -6905,13 +6905,11 @@ \subsection{Automated Test Suite}
\code{t\_apply\_changes} & runs tests for \code{apply\_changes} \\ \code{t\_auction\_minopf} & runs tests for \code{auction} using MINOPF\tnote{\dag} \\
\code{t\_auction\_mips} & runs tests for \code{auction} using \mips{} \\
\code{t\_auction\_tspopf\_pdipm} & runs tests for \code{auction} using PDIPM\tnote{\dag} \\
\code{t\_cpf} & runs tests for AC continuation power flow \\
\code{t\_cpf\_cb1} & example CPF callback function for \code{t\_cpf} \\
\code{t\_cpf\_cb2} & example CPF callback function with \code{cb\_args} for \code{t\_cpf} \\
\code{t\_dcline} & runs tests for DC line implementation in \code{toggle\_dcline} \\
\code{t\_ext2int2ext} & runs tests for \code{ext2int} and \code{int2ext} \\
\code{t\_feval\_w\_path} & runs tests for \code{feval\_w\_path} \\
\code{t\_get\_losses} & runs tests for \code{get\_losses} \\
\code{t\_hasPQcap} & runs tests for \code{hasPQcap} \\
\code{t\_have\_fcn} & runs tests for \code{have\_fcn} \\
\code{t\_hessian} & runs tests for 2\textsuperscript{nd} derivative code \\
\code{t\_islands} & runs test for \code{find\_islands} and \code{extract\_islands} \\
\code{t\_jacobian} & runs test for partial derivative code \\
Expand All @@ -6930,12 +6928,9 @@ \subsection{Automated Test Suite}
\code{t\_printpf} & runs tests for \code{printpf} \\
\code{t\_psse} & runs tests for \code{psse2mpc} and related functions \\
\code{t\_qps\_matpower} & runs tests for \code{qps\_matpower} \\
\code{t\_pf} & runs tests for AC and DC power flow \\
\code{t\_pf\_radial} & runs tests for AC power flow for radial distribution systems \\
\code{t\_runmarket} & runs tests for \code{runmarket} \\
\code{t\_scale\_load} & runs tests for \code{scale\_load} \\
\code{t\_total\_load} & runs tests for \code{total\_load} \\
\code{t\_vdep\_load} & runs PF, CPF and OPF tests for voltage dependent (ZIP) loads \\
\code{t\_totcost} & runs tests for \code{totcost} \\
\bottomrule
\end{tabular}
Expand All @@ -6953,13 +6948,18 @@ \subsection{Automated Test Suite}
%\renewcommand{\arraystretch}{1.2}
\centering
\begin{threeparttable}
\caption{\matpower{} OPF Tests}
\caption{\matpower{} Power Flow, CPF and OPF Tests}
\label{tab:opf_tests}
\footnotesize
\begin{tabular}{lp{0.67\textwidth}}
\toprule
name & description \\
\midrule
\code{t\_pf} & runs tests for AC and DC power flow \\
\code{t\_pf\_radial} & runs tests for AC power flow for radial distribution systems \\
\code{t\_cpf} & runs tests for AC continuation power flow \\
\code{t\_cpf\_cb1} & example CPF callback function for \code{t\_cpf} \\
\code{t\_cpf\_cb2} & example CPF callback function with \code{cb\_args} for \code{t\_cpf} \\
\code{t\_opf\_dc\_bpmpd} & runs tests for DC OPF solver using BPMPD\_MEX\tnote{\dag} \\
\code{t\_opf\_dc\_clp} & runs tests for DC OPF solver using \clp{}\tnote{\dag} \\
\code{t\_opf\_dc\_cplex} & runs tests for DC OPF solver using \cplex{}\tnote{\dag} \\
Expand All @@ -6983,6 +6983,8 @@ \subsection{Automated Test Suite}
\code{t\_opf\_tspopf\_tralm} & runs tests for AC OPF solver using TRALM\tnote{\dag} \\
\code{t\_opf\_userfcns} & runs tests for AC OPF with user callback functions for reserves and interface flow limits \\
\code{t\_runopf\_w\_res} & runs tests for AC OPF with fixed reserve requirements \\
\code{t\_dcline} & runs tests for DC line implementation in \code{toggle\_dcline} \\
\code{t\_vdep\_load} & runs PF, CPF and OPF tests for voltage dependent (ZIP) loads \\
\bottomrule
\end{tabular}
\begin{tablenotes}
Expand Down
100 changes: 67 additions & 33 deletions lib/have_fcn.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
% VER_NUM = HAVE_FCN(TAG, 'vnum')
% DATE = HAVE_FCN(TAG, 'date')
% INFO = HAVE_FCN(TAG, 'all')
% HAVE_FCN(TAG, 'clear_cache')
% HAVE_FCN('all', 'clear_cache')
%
% Returns availability, version and release information for optional
% MATPOWER functionality. All information is cached, and the cached values
Expand All @@ -30,6 +32,11 @@
% 1 - turn ON the optional functionality (if available)
% -1 - toggle the ON/OFF state of the optional functionality
%
% Finally, passing 'clear_cache' as the second argument will cause the
% cached information to be cleared for the specified TAG or, if the first
% argument is 'all', for all optional functionality. When calling with
% 'clear_cache' no return value is defined.
%
% Possible values for input TAG and their meanings:
% bpmpd - BP, BPMPD interior point solver
% clp - CLP, LP/QP solver(https://github.com/coin-or/Clp)
Expand Down Expand Up @@ -79,11 +86,6 @@
% if have_fcn('minopf')
% results = runopf(mpc, mpoption('opf.ac.solver', 'MINOPF'));
% end
%
% Optional functionality can also be toggled OFF and ON by calling HAVE_FCN
% with the following syntax,
% TORF = HAVE_FCN(TAG, TOGGLE)
% where TOGGLE takes a numeric value as follows:

% Private tags for internal use only:
% catchme - support for 'catch me' syntax in try/catch constructs
Expand All @@ -93,39 +95,63 @@
% pardiso_legacy - PARDISO v5, individual MEX files for factor, solve, etc
% pardiso_object - PARDISO v6 and later, object interface
% regexp_split - support for 'split' argument to regexp()
% rithmaticker - used for testing HAVE_FCN
%
% The following calling syntaxes are also implemented to set and get the
% entire cache struct and are used during testing only.
% CACHE = HAVE_FCN('all', 'get_cache')
% HAVE_FCN(CACHE, 'set_cache')

% MATPOWER
% Copyright (c) 2004-2016, Power Systems Engineering Research Center (PSERC)
% Copyright (c) 2004-2019, Power Systems Engineering Research Center (PSERC)
% by Ray Zimmerman, PSERC Cornell
%
% This file is part of MATPOWER.
% Covered by the 3-clause BSD License (see LICENSE file for details).
% See http://www.pserc.cornell.edu/matpower/ for more info.

if nargin > 1 && isnumeric(rtype)
toggle = 1;
on_off = rtype;
if on_off < 0
TorF = have_fcn(tag);
on_off = ~TorF;
persistent fcns;

action = 'D'; %% detecting functionality (default)
if nargin > 1
if isnumeric(rtype)
action = 'T'; %% toggling functionality
on_off = rtype;
if on_off < 0 %% flip the toggle
TorF = have_fcn(tag);
on_off = ~TorF;
end
else
switch lower(rtype)
case 'get_cache'
action = 'C'; %% getting cache
rv = fcns;
case 'set_cache'
action = 'C'; %% setting cache
fcns = tag;
case 'clear_cache'
action = 'C'; %% clearing cache
if strcmpi(tag, 'all')
fcns = struct(); %% delete all fields
else
fcns = rmfield(fcns, tag); %% delete field to force re-check
end
end
end
else
toggle = 0;
end

persistent fcns;

if toggle %% change availability
if on_off %% turn on if available
if action == 'T' %% change availability
if on_off %% turn on if available
fcns = rmfield(fcns, tag); %% delete field to force re-check
else %% turn off
else %% turn off
if ~isfield(fcns, tag) %% not yet been checked
TorF = have_fcn(tag); %% cache result first
end
fcns.(tag).av = 0; %% then turn off
end
TorF = have_fcn(tag); %% return cached value
else %% detect availability
TorF = have_fcn(tag); %% return availability
%% (recheck if ON, cached 0 if OFF)
elseif action == 'D' %% detect availability
%% info not yet cached?
if ~isfield(fcns, tag)
%%----- determine installation status, version number, etc. -----
Expand Down Expand Up @@ -593,6 +619,12 @@
elseif have_fcn('octave', 'vnum') >= 3.008
TorF = 1;
end
case 'rithmaticker' %% used for testing HAVE_FCN
TorF = exist('rithmaticker', 'file') == 2;
if TorF
vstr = '3.1.4';
rdate = datestr([2019 5 30 0 0 0], 'dd-mmm-yyyy');
end

%%----- unknown tag -----
otherwise
Expand All @@ -613,18 +645,20 @@
end

%% extract desired values from cache
if nargin < 2 || toggle
rv = fcns.(tag).av;
else
switch lower(rtype)
case 'vstr'
rv = fcns.(tag).vstr;
case 'vnum'
rv = fcns.(tag).vnum;
case 'date'
rv = fcns.(tag).date;
case 'all'
rv = fcns.(tag);
if action ~= 'C' || nargout
if nargin < 2 || action == 'T'
rv = fcns.(tag).av;
else
switch lower(rtype)
case 'vstr'
rv = fcns.(tag).vstr;
case 'vnum'
rv = fcns.(tag).vnum;
case 'date'
rv = fcns.(tag).date;
case 'all'
rv = fcns.(tag);
end
end
end

Expand Down
101 changes: 101 additions & 0 deletions lib/t/t_have_fcn.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
function t_have_fcn(quiet)
%T_HAVE_FCN Tests for HAVE_FCN.

% MATPOWER
% Copyright (c) 2019, Power Systems Engineering Research Center (PSERC)
% by Ray Zimmerman, PSERC Cornell
%
% This file is part of MATPOWER.
% Covered by the 3-clause BSD License (see LICENSE file for details).
% See http://www.pserc.cornell.edu/matpower/ for more info.

if nargin < 1
quiet = 0;
end

n_tests = 20;

t_begin(n_tests, quiet);

%% save cache
saved = have_fcn('all', 'get_cache');

%% test set_cache/get_cache
t = 'set_cache/get_cache';
e = struct('foo', 2, 'bar', 3, 'baz', 'boz');
have_fcn(e, 'set_cache');
t_ok(isequal(have_fcn('all', 'get_cache'), e), t);
have_fcn(64, 'set_cache');
t_ok(isequal(have_fcn('all', 'get_cache'), 64), t);

%% clear cache
have_fcn(struct(), 'set_cache');

%% Matlab vs. Octave
if exist('OCTAVE_VERSION', 'builtin') == 5
t_ok(have_fcn('octave'), 'Octave');
t_ok(~have_fcn('matlab'), 'not Matlab');
else
t_ok(~have_fcn('octave'), 'not Octave');
t_ok(have_fcn('matlab'), 'Matlab');
end

t = '$MATPOWER/t/t_feval_w_path must not be in path';
t_ok(exist('rithmaticker') ~= 2, t);

%% find path to this test file
cwd = pwd;
[p, n, e] = fileparts(which('t_have_fcn'));

%% initially not available
t = 'have_fcn(''rithmaticker'')';
t_ok(have_fcn('rithmaticker') == 0, [t ' : not available']);

%% switch dir so it is available
cd(fullfile(p, 't_feval_w_path'));
cwd2 = pwd;

t = '$MATPOWER/t/t_feval_w_path must be in path';
t_ok(exist('rithmaticker') == 2, t);

%% still not available (cached)
t = 'have_fcn(''rithmaticker'')';
t_ok(have_fcn('rithmaticker') == 0, [t ' : still not available (cached)']);

%% clear cache, check again
have_fcn('rithmaticker', 'clear_cache');
t_ok(have_fcn('rithmaticker') == 1, [t ' : available (cache cleared)']);

cd(cwd);
t = 'successful switch to original working directory';
t_ok(strcmp(cwd, pwd), t);

t = 'have_fcn(''rithmaticker'')';
t_ok(have_fcn('rithmaticker') == 1, [t ' : still available (cached)']);

t = 'have_fcn(''rithmaticker'', ''vstr'')';
t_ok(strcmp(have_fcn('rithmaticker', 'vstr'), '3.1.4'), t);

t = 'have_fcn(''rithmaticker'', ''vnum'')';
t_is(have_fcn('rithmaticker', 'vnum'), 3.001004, 12, t);

t = 'have_fcn(''rithmaticker'', ''date'')';
t_ok(strcmp(have_fcn('rithmaticker', 'date'), '30-May-2019'), t);

t = 'have_fcn(''rithmaticker'', ''all'')';
rv = have_fcn('rithmaticker', 'all');
t_ok(isstruct(rv), [t ' : isstruct']);
t_is(rv.av, 1, 12, [t ' : av']);
t_ok(strcmp(rv.vstr, '3.1.4'), [t ' : vstr']);
t_is(rv.vnum, 3.001004, 12, [t ' : vnum']);
t_ok(strcmp(rv.date, '30-May-2019'), [t ' : date']);

%% clear cache, check again
t = 'have_fcn(''rithmaticker'')';
have_fcn('all', 'clear_cache');
t_ok(have_fcn('rithmaticker') == 0, [t ' : not available (cache cleared)']);

%% restore cache
have_fcn(saved, 'set_cache');

t_end;
1 change: 1 addition & 0 deletions lib/t/test_matpower.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
%% MATPOWER base test
tests{end+1} = 't_test_fcns';
tests{end+1} = 't_nested_struct_copy';
tests{end+1} = 't_have_fcn';
tests{end+1} = 't_feval_w_path';
tests{end+1} = 't_mpoption';
tests{end+1} = 't_loadcase';
Expand Down

0 comments on commit 406842a

Please sign in to comment.