Mercurial > hg > octave-nkf
changeset 20760:45151de7423f
maint: Clean up implementations of ode45.m, odeget.m, odeset.m.
* ode45.m: Match variable names in function to docstring.
Don't use '...' line continuation unless strictly necessary.
Remove incorrect isa check for inline function.
Use '( )' around switch statement test variable.
Use printf, rather than fprintf, where possible.
* odeget.m: Match variable names in function to docstring.
Rephrase error messages to begin with 'odeget:'.
Use try/catch blocks to speed up "fast" and "fast_not_empty" code.
Use rows() rather than "size (xxx, 1)".
Use isempty rather than "size (xxx, 1) == 0".
Use printf, rather than fprintf, where possible.
Use unwind_protect block in BIST tests to restore warning state.
* odeset.m: Match variable names in function to docstring.
Use rows() rather than "size (xxx, 1)".
Use isempty rather than "size (xxx, 1) == 0".
Use printf, rather than fprintf, where possible.
Eliminate for loop with cell2struct for odestruct initialization.
Introduce temporary vars oldstruct, newstruct to clarify code.
Restate some error messages to begin with 'odeset:'.
Use cellfun to quickl check that all field inputs are strings.
Use unwind_protect block in BIST tests to restore warning state.
author | Rik <rik@octave.org> |
---|---|
date | Mon, 05 Oct 2015 11:59:18 -0700 |
parents | eb9e2d187ed2 |
children | b7ac1e94266e |
files | scripts/ode/ode45.m scripts/ode/odeget.m scripts/ode/odeset.m |
diffstat | 3 files changed, 246 insertions(+), 252 deletions(-) [+] |
line wrap: on
line diff
--- a/scripts/ode/ode45.m +++ b/scripts/ode/ode45.m @@ -1,6 +1,6 @@ -## Copyright (C) 2006-2012, Thomas Treichl <treichl@users.sourceforge.net> +## Copyright (C) 2014, Jacopo Corno <jacopo.corno@gmail.com> ## Copyright (C) 2013, Roberto Porcu' <roberto.porcu@polimi.it> -## Copyright (C) 2014, Jacopo Corno <jacopo.corno@gmail.com> +## Copyright (C) 2006-2012, Thomas Treichl <treichl@users.sourceforge.net> ## ## This file is part of Octave. ## @@ -72,19 +72,21 @@ ## @seealso{odeset, odeget} ## @end deftypefn -function varargout = ode45 (vfun, vslot, vinit, varargin) +function varargout = ode45 (vfun, vtrange, vinit, varargin) - vorder = 5; # runge_kutta_45_dorpri uses local extrapolation + vorder = 5; # runge_kutta_45_dorpri uses local extrapolation vsolver = "ode45"; - if (nargin == 0) # Check number and types of all input arguments + ## FIXME: Octave core function's usually do print_usage () + ## rather than displaying full help string when improperly called. + if (nargin == 0) # Check number and types of all input arguments help (vsolver); - error ("OdePkg:InvalidArgument", ... + error ("OdePkg:InvalidArgument", "number of input arguments must be greater than zero"); endif if (nargin < 3) - print_usage; + print_usage (); endif if (nargin >= 4) @@ -105,63 +107,63 @@ vodeoptions.vfunarguments = {}; endif - if (! isvector (vslot) || ! isnumeric (vslot)) - error ("OdePkg:InvalidArgument", ... + if (! isvector (vtrange) || ! isnumeric (vtrange)) + error ("OdePkg:InvalidArgument", "second input argument must be a valid vector"); endif - if (length (vslot) < 2 && ... - (isempty (vodeoptions.TimeStepSize) ... - || isempty (vodeoptions.TimeStepNumber))) - error ("OdePkg:InvalidArgument", ... + if (length (vtrange) < 2 + && (isempty (vodeoptions.TimeStepSize) + || isempty (vodeoptions.TimeStepNumber))) + error ("OdePkg:InvalidArgument", "second input argument must be a valid vector"); - elseif (vslot(2) == vslot(1)) - error ("OdePkg:InvalidArgument", ... + elseif (vtrange(2) == vtrange(1)) + error ("OdePkg:InvalidArgument", "second input argument must be a valid vector"); else - vodeoptions.vdirection = sign (vslot(2) - vslot(1)); + vodeoptions.vdirection = sign (vtrange(2) - vtrange(1)); endif - vslot = vslot(:); + vtrange = vtrange(:); if (! isvector (vinit) || ! isnumeric (vinit)) - error ("OdePkg:InvalidArgument", ... + error ("OdePkg:InvalidArgument", "third input argument must be a valid numerical value"); endif vinit = vinit(:); - if (! (isa (vfun, "function_handle") || isa (vfun, "inline"))) - error ("OdePkg:InvalidArgument", ... + if (! (isa (vfun, "function_handle"))) + error ("OdePkg:InvalidArgument", "first input argument must be a valid function handle"); endif ## Start preprocessing, have a look which options are set in vodeoptions, ## check if an invalid or unused option is set - if (isempty (vodeoptions.TimeStepNumber) ... + if (isempty (vodeoptions.TimeStepNumber) && isempty (vodeoptions.TimeStepSize)) integrate_func = "adaptive"; vodeoptions.vstepsizefixed = false; - elseif (! isempty (vodeoptions.TimeStepNumber) ... + elseif (! isempty (vodeoptions.TimeStepNumber) && ! isempty (vodeoptions.TimeStepSize)) integrate_func = "n_steps"; vodeoptions.vstepsizefixed = true; if (sign (vodeoptions.TimeStepSize) != vodeoptions.vdirection) - warning ("OdePkg:InvalidArgument", ... - "option 'TimeStepSize' has a wrong sign", ... + warning ("OdePkg:InvalidArgument", + "option 'TimeStepSize' has a wrong sign", "it will be corrected automatically"); vodeoptions.TimeStepSize = (-1)*vodeoptions.TimeStepSize; endif - elseif (isempty (vodeoptions.TimeStepNumber) ... + elseif (isempty (vodeoptions.TimeStepNumber) && ! isempty (vodeoptions.TimeStepSize)) integrate_func = "const"; vodeoptions.vstepsizefixed = true; if (sign (vodeoptions.TimeStepSize) != vodeoptions.vdirection) - warning ("OdePkg:InvalidArgument", ... - "option 'TimeStepSize' has a wrong sign", ... + warning ("OdePkg:InvalidArgument", + "option 'TimeStepSize' has a wrong sign", "it will be corrected automatically"); vodeoptions.TimeStepSize = (-1)*vodeoptions.TimeStepSize; endif else - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "assuming an adaptive integrate function"); integrate_func = "adaptive"; endif @@ -173,11 +175,11 @@ ## This option can be set by the user to another value than default value. if (isempty (vodeoptions.RelTol) && ! vodeoptions.vstepsizefixed) vodeoptions.RelTol = 1e-3; - warning ("OdePkg:InvalidArgument", ... - "Option 'RelTol' not set, new value %f is used", ... + warning ("OdePkg:InvalidArgument", + "Option 'RelTol' not set, new value %f is used", vodeoptions.RelTol); elseif (! isempty (vodeoptions.RelTol) && vodeoptions.vstepsizefixed) - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "Option 'RelTol' will be ignored if fixed time stamps are given"); endif @@ -185,11 +187,11 @@ ## This option can be set by the user to another value than default value. if (isempty (vodeoptions.AbsTol) && ! vodeoptions.vstepsizefixed) vodeoptions.AbsTol = 1e-6; - warning ("OdePkg:InvalidArgument", ... - "Option 'AbsTol' not set, new value %f is used", ... + warning ("OdePkg:InvalidArgument", + "Option 'AbsTol' not set, new value %f is used", vodeoptions.AbsTol); elseif (! isempty (vodeoptions.AbsTol) && vodeoptions.vstepsizefixed) - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "Option 'AbsTol' will be ignored if fixed time stamps are given"); else vodeoptions.AbsTol = vodeoptions.AbsTol(:); # Create column vector @@ -210,7 +212,7 @@ vodeoptions.vhavenonnegative = true; else vodeoptions.vhavenonnegative = false; - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "Option 'NonNegative' will be ignored if mass matrix is set"); endif else @@ -257,12 +259,13 @@ ## This option can be set by the user to another value than default value. if (isempty (vodeoptions.InitialStep) && strcmp (integrate_func, "adaptive")) vodeoptions.InitialStep = ... - vodeoptions.vdirection * starting_stepsize (vorder, vfun, vslot(1), vinit, + vodeoptions.vdirection * starting_stepsize (vorder, vfun, vtrange(1), + vinit, vodeoptions.AbsTol, vodeoptions.RelTol, vodeoptions.vnormcontrol); - warning ("OdePkg:InvalidArgument", ... - "option 'InitialStep' not set, estimated value %f is used", ... + warning ("OdePkg:InvalidArgument", + "option 'InitialStep' not set, estimated value %f is used", vodeoptions.InitialStep); elseif(isempty (vodeoptions.InitialStep)) vodeoptions.InitialStep = odeget (vodeoptions, "TimeStepSize"); @@ -271,9 +274,9 @@ ## Implementation of the option MaxStep has been finished. This option ## can be set by the user to another value than default value. if (isempty (vodeoptions.MaxStep) && ! vodeoptions.vstepsizefixed) - vodeoptions.MaxStep = abs (vslot(end) - vslot(1)) / 10; - warning ("OdePkg:InvalidArgument", ... - "Option 'MaxStep' not set, new value %f is used", ... + vodeoptions.MaxStep = abs (vtrange(end) - vtrange(1)) / 10; + warning ("OdePkg:InvalidArgument", + "Option 'MaxStep' not set, new value %f is used", vodeoptions.MaxStep); endif @@ -289,27 +292,27 @@ ## by this solver because this solver uses an explicit Runge-Kutta method ## and therefore no Jacobian calculation is necessary. if (! isequal (vodeoptions.Jacobian, vodetemp.Jacobian)) - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "option 'Jacobian' will be ignored by this solver"); endif if (! isequal (vodeoptions.JPattern, vodetemp.JPattern)) - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "option 'JPattern' will be ignored by this solver"); endif if (! isequal (vodeoptions.Vectorized, vodetemp.Vectorized)) - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "option 'Vectorized' will be ignored by this solver"); endif if (! isequal (vodeoptions.NewtonTol, vodetemp.NewtonTol)) - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "option 'NewtonTol' will be ignored by this solver"); endif if (! isequal (vodeoptions.MaxNewtonIterations,vodetemp.MaxNewtonIterations)) - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "option 'MaxNewtonIterations' will be ignored by this solver"); endif @@ -335,27 +338,27 @@ ## Other options that are not used by this solver. ## Print a warning message to tell the user that the option is ignored. if (! isequal (vodeoptions.MvPattern, vodetemp.MvPattern)) - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "option 'MvPattern' will be ignored by this solver"); endif if (! isequal (vodeoptions.MassSingular, vodetemp.MassSingular)) - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "option 'MassSingular' will be ignored by this solver"); endif if (! isequal (vodeoptions.InitialSlope, vodetemp.InitialSlope)) - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "option 'InitialSlope' will be ignored by this solver"); endif if (! isequal (vodeoptions.MaxOrder, vodetemp.MaxOrder)) - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "option 'MaxOrder' will be ignored by this solver"); endif if (! isequal (vodeoptions.BDF, vodetemp.BDF)) - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "option 'BDF' will be ignored by this solver"); endif @@ -374,28 +377,28 @@ endif endif - switch integrate_func + switch (integrate_func) case "adaptive" - solution = integrate_adaptive (@runge_kutta_45_dorpri, ... - vorder, vfun, vslot, vinit, SubOpts); + solution = integrate_adaptive (@runge_kutta_45_dorpri, + vorder, vfun, vtrange, vinit, SubOpts); case "n_steps" - solution = integrate_n_steps (@runge_kutta_45_dorpri, ... - vfun, vslot(1), vinit, ... - vodeoptions.TimeStepSize, ... + solution = integrate_n_steps (@runge_kutta_45_dorpri, + vfun, vtrange(1), vinit, + vodeoptions.TimeStepSize, vodeoptions.TimeStepNumber, SubOpts); case "const" - solution = integrate_const (@runge_kutta_45_dorpri, ... - vfun, vslot, vinit, ... + solution = integrate_const (@runge_kutta_45_dorpri, + vfun, vtrange, vinit, vodeoptions.TimeStepSize, SubOpts); endswitch ## Postprocessing, do whatever when terminating integration algorithm if (vodeoptions.vhaveoutputfunction) # Cleanup plotter - feval (vodeoptions.OutputFcn, solution.t(end), ... + feval (vodeoptions.OutputFcn, solution.t(end), solution.x(end,:)', "done", vodeoptions.vfunarguments{:}); endif if (vodeoptions.vhaveeventfunction) # Cleanup event function handling - odepkg_event_handle (vodeoptions.Events, solution.t(end), ... + odepkg_event_handle (vodeoptions.Events, solution.t(end), solution.x(end,:)', "done", vodeoptions.vfunarguments{:}); endif @@ -404,22 +407,25 @@ if (strcmp (vodeoptions.Stats, "on")) vhavestats = true; vnsteps = solution.vcntloop-2; # vcntloop from 2..end - vnfailed = (solution.vcntcycles-1)-(solution.vcntloop-2)+1; # vcntcycl from 1..end + vnfailed = (solution.vcntcycles-1)-(vnsteps)+1; # vcntcycl from 1..end vnfevals = 7*(solution.vcntcycles-1); # number of ode evaluations vndecomps = 0; # number of LU decompositions vnpds = 0; # number of partial derivatives vnlinsols = 0; # no. of linear systems solutions ## Print cost statistics if no output argument is given if (nargout == 0) - vmsg = fprintf (1, "Number of successful steps: %d\n", vnsteps); - vmsg = fprintf (1, "Number of failed attempts: %d\n", vnfailed); - vmsg = fprintf (1, "Number of function calls: %d\n", vnfevals); + printf ("Number of successful steps: %d\n", vnsteps); + printf ("Number of failed attempts: %d\n", vnfailed); + printf ("Number of function calls: %d\n", vnfevals); endif else vhavestats = false; endif - if (nargout == 1) # Sort output variables, depends on nargout + if (nargout == 2) + varargout{1} = solution.t; # Time stamps are first output argument + varargout{2} = solution.x; # Results are second output argument + elseif (nargout == 1) varargout{1}.x = solution.t; # Time stamps are saved in field x varargout{1}.y = solution.x; # Results are saved in field y varargout{1}.solver = vsolver; # Solver name is saved in field solver @@ -429,7 +435,7 @@ varargout{1}.ye = solution.vevent{4}; # Results when an event occurred endif if (vhavestats) - varargout{1}.stats = struct; + varargout{1}.stats = struct (); varargout{1}.stats.nsteps = vnsteps; varargout{1}.stats.nfailed = vnfailed; varargout{1}.stats.nfevals = vnfevals; @@ -437,15 +443,10 @@ varargout{1}.stats.ndecomps = vndecomps; varargout{1}.stats.nlinsols = vnlinsols; endif - elseif (nargout == 2) - varargout{1} = solution.t; # Time stamps are first output argument - varargout{2} = solution.x; # Results are second output argument elseif (nargout == 5) - varargout{1} = solution.t; # Same as (nargout == 2) - varargout{2} = solution.x; # Same as (nargout == 2) - varargout{3} = []; # LabMat doesn't accept lines like - varargout{4} = []; # varargout{3} = varargout{4} = []; - varargout{5} = []; + varargout = cell (1,5); + varargout{1} = solution.t; + varargout{2} = solution.x; if (vodeoptions.vhaveeventfunction) varargout{3} = solution.vevent{3}; # Time info when an event occurred varargout{4} = solution.vevent{4}; # Results when an event occurred @@ -618,13 +619,13 @@ %!test # Events option, now stop integration %! vopt = odeset ("Events", @fevn, "NormControl", "on"); %! vsol = ode45 (@fpol, [0 10], [2 0], vopt); -%! assert ([vsol.ie, vsol.xe, vsol.ye], ... -%! [2.0, 2.496110, -0.830550, -2.677589], 6e-1); +%! assert ([vsol.ie, vsol.xe, vsol.ye], +%! [2.0, 2.496110, -0.830550, -2.677589], 6e-1); %!test # Events option, five output arguments %! vopt = odeset ("Events", @fevn, "NormControl", "on"); %! [vt, vy, vxe, vye, vie] = ode45 (@fpol, [0 10], [2 0], vopt); -%! assert ([vie, vxe, vye], ... -%! [2.0, 2.496110, -0.830550, -2.677589], 6e-1); +%! assert ([vie, vxe, vye], +%! [2.0, 2.496110, -0.830550, -2.677589], 6e-1); %!test # Jacobian option %! vopt = odeset ("Jacobian", @fjac); %! vsol = ode45 (@fpol, [0 2], [2 0], vopt);
--- a/scripts/ode/odeget.m +++ b/scripts/ode/odeget.m @@ -18,79 +18,71 @@ ## <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn {Function File} {@var{option} =} odeget (@var{ode_opt}, @var{field}) -## @deftypefnx {Function File} {@var{option} =} odeget (@var{ode_opt}, @var{field}, @var{default}) +## @deftypefn {Function File} {@var{val} =} odeget (@var{ode_opt}, @var{field}) +## @deftypefnx {Function File} {@var{val} =} odeget (@var{ode_opt}, @var{field}, @var{default}) ## ## Query the value of the property @var{field} in the ODE options structure ## @var{ode_opt}. ## -## If this function is called with two input arguments and the first input -## argument @var{ode_opt} is of type structure array and the second input -## argument @var{field} is of type string then return the option value -## @var{res} that is specified by the option name @var{field} in the ODE -## option structure @var{ode_opt}. +## If called with two input arguments and the first input argument @var{ode_opt} +## is a structure and the second input argument @var{field} is a string then +## return the option value @var{val} that is specified by the option name +## @var{field} in the ODE option structure @var{ode_opt}. ## -## Optionally if this function is called with a third input argument then -## return the default value @var{default} if @var{field} is not set in the -## structure @var{ode_opt}. +## If called called with an optional third input argument then return the +## default value @var{default} if @var{field} is not set in the structure +## @var{ode_opt}. ## @seealso{odeset} ## @end deftypefn +## FIXME: 4th input argument 'opt' is undocumented. + ## Note: 2006-10-22, Thomas Treichl ## We cannot create a function of the form odeget (@var{odestruct}, ## @var{name1}, @var{name2}) because we would get a mismatch with ## the function form 1 like described above. -function res = odeget (ode_opt, field, default, opt) +function val = odeget (ode_opt, field, default = [], opt) - ## Check number and types of input arguments - if (nargin == 1) - error ("OdePkg:InvalidArgument", - "input arguments number must be at least 2") + if (nargin == 1 || nargin > 4) + print_usage (); endif + ## Shortcut for empty options structures if (isempty (ode_opt)) - if (nargin == 2) - res = []; - return + if (nargin < 3) + val = []; + else + val = default; endif - res = default; - return + return; endif if (! isstruct (ode_opt)) - error ("OdePkg:InvalidArgument", - "first input argument must be a valid ODE_STRUCT."); - endif - - if (! ischar (field)) - error ("OdePkg:InvalidArgument", - "second input argument must be a string."); - endif - - if (nargin == 2) - default = []; + error ("odeget: ODE_OPT must be a valid ODE_STRUCT"); + elseif (! ischar (field)) + error ("odeget: FIELD must be a string"); endif - if ((nargin == 4) - && strcmp (lower (deblank (opt)), "fast")) - if (isfield (ode_opt, field)) - res = ode_opt.(field); - else - res = default; - endif - return + if (nargin == 4 && strcmpi (opt, "fast")) + try + val = ode_opt.(field); + catch + val = default; + end_try_catch + return; endif - if ((nargin == 4) - && strcmp (lower (deblank (opt)), "fast_not_empty")) - if (isfield (ode_opt, field) - && ! isempty (ode_opt.(field)) ) - res = ode_opt.(field); - else - res = default; - endif - return + if (nargin == 4 && strcmpi (opt, "fast_not_empty")) + try + val = ode_opt.(field); + if (isempty (val)) + val = default; + endif + catch + val = default; + end_try_catch + return; endif ## check if the given struct is a valid OdePkg struct @@ -110,43 +102,44 @@ while (1) pos = fuzzy_compare (field, options); - if (size (pos, 1) == 0) # no matching for the given option + if (isempty (pos)) # no match for the given option if (nargin == 2) - error ("OdePkg:InvalidArgument", - "invalid property. No property found with name '%s'.", field); + error ("odeget: invalid property. No property found with name '%s'", + field); endif warning ("odeget:NoExactMatching", "no property found with name '%s'. ", "Assuming default value.", field); - res = default; - return + val = default; + return; endif - if (size (pos, 1) == 1) # one matching + if (rows (pos) == 1) # one matching if (! strcmp (lower (deblank (field)), lower (deblank (options(pos,:)))) ) - warning ("OdePkg:InvalidArgument", + warning ("odeget:InvalidArgument", "no exact matching for '%s'. ", - "Assuming you was intending '%s'.", + "Assuming you were intending '%s'.", field, deblank (options(pos,:))); endif - res = ode_opt.(deblank (options(pos,:))); - if (isempty (res)) - res = default; - return + val = ode_opt.(deblank (options(pos,:))); + if (isempty (val)) + val = default; endif - return + return; endif + ## FIXME: Do we really need interactive selection? + ## Matlab doesn't appear to offer this. ## if there are more matching, ask the user to be more precise - warning ("OdePkg:InvalidArgument", ... + warning ("OdePkg:InvalidArgument", "no exact matching for '%s'. %d possible fields were found.", - field, size (pos, 1)); - for j = 1:(size (pos, 1)) - fprintf ("%s\n", deblank (options(pos(j),:))); + field, rows (pos)); + for j = 1:(rows (pos)) + printf ("%s\n", deblank (options(pos(j),:))); endfor do - fprintf ("Please insert field name again.\n"); + printf ("Please insert field name again.\n"); field = input ("New field name: "); until (ischar (field)) endwhile @@ -156,22 +149,23 @@ %!demo %! # Return the manually changed value RelTol of the OdePkg options -%! # strutcure A. If RelTol wouldn't have been changed then an +%! # structure A. If RelTol wouldn't have been changed then an %! # empty matrix value would have been returned. %! %! A = odeset ("RelTol", 1e-1, "AbsTol", 1e-2); %! odeget (A, "RelTol", []) -%! ## Turn off output of warning messages for all tests, turn them on -%! ## again if the last test is called -%! warning ("off", "OdePkg:InvalidArgument"); -%!test assert (odeget (odeset (), "RelTol"), []); -%!test assert (odeget (odeset (), "RelTol", 10), 10); -%!test assert (odeget (odeset (), "Stats"), []); -%!test assert (odeget (odeset (), "Stats", "on"), "on"); -%!test assert (odeget (odeset (), "AbsTol", 1.e-6, "fast"), []); -%!test assert (odeget (odeset (), "AbsTol", 1.e-6, "fast_not_empty"), 1.e-6); -%!test assert (odeget (odeset (), "AbsTol", 1e-9), 1e-9); -%! -%! warning ("on", "OdePkg:InvalidArgument"); +%!test +%! wstate = warning ("off", "OdePkg:InvalidArgument"); +%! unwind_protect +%! assert (odeget (odeset (), "RelTol"), []); +%! assert (odeget (odeset (), "RelTol", 10), 10); +%! assert (odeget (odeset (), "Stats"), []); +%! assert (odeget (odeset (), "Stats", "on"), "on"); +%! assert (odeget (odeset (), "AbsTol", 1e-6, "fast"), []); +%! assert (odeget (odeset (), "AbsTol", 1e-6, "fast_not_empty"), 1e-6); +%! assert (odeget (odeset (), "AbsTol", 1e-9), 1e-9); +%! unwind_protect_cleanup +%! warning (wstate); +%! end_unwind_protect
--- a/scripts/ode/odeset.m +++ b/scripts/ode/odeset.m @@ -49,15 +49,15 @@ ## @seealso{odeget} ## @end deftypefn -function opt = odeset (varargin) +function odestruct = odeset (varargin) - ## Check number and types of all input arguments + ## Special calling syntax to display defaults if (nargin == 0 && nargout == 0) - print_options; + print_options (); return; endif - ## Creating a vector of OdePkg possible fields + ## Column vector of all possible OdePkg fields fields = ["AbsTol"; "Algorithm"; "BDF"; "Choice"; "Eta"; "Events"; "Explicit"; "InexactSolver"; "InitialSlope"; "InitialStep"; "Jacobian";"JConstant";"JPattern";"Mass"; "MassConstant"; @@ -68,27 +68,25 @@ "Restart"; "Stats"; "TimeStepNumber"; "TimeStepSize"; "UseJacobian"; "Vectorized"]; - fields_nb = size (fields, 1); + fields_nb = rows (fields); ## initialize output - opt = []; - for i = 1:1:fields_nb - opt.(deblank (fields(i,:))) = []; - endfor + odestruct = cell2struct (cell (rows (fields), 1), cellstr (fields)); - opt.Refine = 0; - opt.OutputSave = 1; + odestruct.Refine = 0; + odestruct.OutputSave = 1; if (nargin == 0 && nargout == 1) return; endif - ode_fields = fieldnames (opt); + ode_fields = fieldnames (odestruct); if (isstruct (varargin{1})) - ode_struct_value_check (varargin{1}); + oldstruct = varargin{1}; + ode_struct_value_check (oldstruct); - optA_fields = fieldnames (varargin{1}); + optA_fields = fieldnames (oldstruct); optA_f_nb = length (optA_fields); ## loop on first struct options for updating @@ -97,12 +95,12 @@ while (1) pos = fuzzy_compare (name, fields); - if (size (pos, 1) == 0) + if (isempty (pos)) warning ("OdePkg:InvalidArgument", "no property found with name '%s'", name); endif - if (size (pos, 1) == 1) + if (rows (pos) == 1) if (! strcmp (lower (deblank (name)), lower (deblank (fields(pos,:))))) warning ("OdePkg:InvalidArgument", "no exact matching for ", @@ -110,28 +108,30 @@ name, deblank (fields(pos,:))); endif - opt.(deblank (fields(pos,:))) = varargin{1}.(optA_fields{i}); - break + odestruct.(deblank (fields(pos,:))) = oldstruct.(optA_fields{i}); + break; endif + ## FIXME: Do we really need interactive selection? ## if there are more matching, ask the user to be more precise warning ("OdePkg:InvalidArgument", "no exact matching for '%s'. %d possible fields were found", name, size(pos, 1)); - for j = 1:(size (pos, 1)) - fprintf ("%s\n", deblank (fields(pos(j),:))); + for j = 1:(rows (pos)) + printf ("%s\n", deblank (fields(pos(j),:))); endfor do - fprintf ("Please insert field name again\n"); + disp ("Please insert field name again"); name = input ("New field name: "); until (ischar (name)) endwhile endfor if (nargin == 2 && isstruct (varargin{2})) - ode_struct_value_check (varargin{2}); + newstruct = varargin{2}; + ode_struct_value_check (newstruct); - optB_fields = fieldnames (varargin{2}); + optB_fields = fieldnames (newstruct); optB_f_nb = length (optB_fields); ## update the first struct with the values in the second one @@ -140,31 +140,32 @@ while (1) pos = fuzzy_compare (name, fields); - if (size (pos, 1) == 0) - warning ("OdePkg:InvalidArgument", ... + if (isempty (pos)) + warning ("OdePkg:InvalidArgument", "no property found with name '%s'", name); endif - if (size(pos, 1) == 1) - if (! strcmp (lower (deblank (name)), ... + if (rows (pos) == 1) + if (! strcmp (lower (deblank (name)), lower (deblank (fields(pos,:))))) warning ("OdePkg:InvalidArgument", "no exact matching for ", "'%s'. Assuming you were intending '%s'", name, deblank (fields(pos,:))); endif - opt.(deblank (fields(pos,:))) = varargin{2}.(optB_fields{i}); - break + odestruct.(deblank (fields(pos,:))) = newstruct.(optB_fields{i}); + break; endif + ## FIXME: Do we really need interactive selection? ## if there are more matching, ask the user to be more precise warning ("OdePkg:InvalidArgument", "no exact matching for '%s'. ", "%d possible fields were found", - name, size (pos, 1)); - for j = 1:(size (pos, 1)) - fprintf ("%s\n", deblank (fields(pos(j),:))); + name, rows (pos)); + for j = 1:(rows (pos)) + printf ("%s\n", deblank (fields(pos(j),:))); endfor do - fprintf ("Please insert field name again\n"); + disp ("Please insert field name again"); name = input ("New field name: "); until (ischar (name)) endwhile @@ -175,117 +176,114 @@ ## if the second argument is not a struct, ## pass new values of the OdePkg options to the first struct if (mod (nargin, 2) != 1) - error ("OdePkg:InvalidArgument", - "odeset expects an odd number of input arguments", - " when the first is a ODE_STRUCT"); + error ("odeset: FIELD/VALUE arguments must occur in pairs"); + endif + + if (! all (cellfun ("isclass", varargin(2:2:end), "char"))) + error ("odeset: All FIELD names must be strings"); endif ## loop on the input arguments for i = 2:2:(nargin - 1) - - if (! ischar(varargin{i})) - error ("OdePkg:InvalidArgument", - "not all odd input arguments are strings"); - endif - name = varargin{i}; while (1) pos = fuzzy_compare (name, fields); - if (size (pos, 1) == 0) + if (isempty (pos)) error ("OdePkg:InvalidArgument", "no property found with name '%s'", name); endif - if (size (pos, 1) == 1) - if (! strcmp (lower (deblank (name)), lower (deblank (fields(pos,:))))) + if (rows (pos) == 1) + if (! strcmp (lower (deblank (name)), + lower (deblank (fields(pos,:))))) warning ("OdePkg:InvalidArgument", "no exact matching for '%s'. ", "%d possible fields were found", - name, size (pos, 1)); + name, rows (pos)); endif - opt.(deblank (fields(pos,:))) = varargin{i+1}; - break + odestruct.(deblank (fields(pos,:))) = varargin{i+1}; + break; endif + ## FIXME: Do we really need interactive selection? ## if there are more matching, ask the user to be more precise warning ("OdePkg:InvalidArgument", "no exact matching for '%s'. ", "%d possible fields were found", - name, size (pos, 1)); - for j = 1:(size (pos, 1)) - fprintf ("%s\n", deblank (fields(pos(j),:))); + name, rows (pos)); + for j = 1:(rows (pos)) + printf ("%s\n", deblank (fields(pos(j),:))); endfor do - fprintf ("Please insert field name again\n"); + disp ("Please insert field name again"); name = input ("New field name: "); until (ischar (name)) endwhile endfor ## check if all has been done gives a valid OdePkg struct - ode_struct_value_check (opt); + ode_struct_value_check (odestruct); return; endif ## first input argument was not a struct if (mod (nargin, 2) != 0) - error ("OdePkg:InvalidArgument", "odeset expects an even number ", - "of input arguments when the first is a string"); + error ("odeset: FIELD/VALUE arguments must occur in pairs"); + endif + + if (! all (cellfun ("isclass", varargin(1:2:end), "char"))) + error ("odeset: All FIELD names must be strings"); endif for i = 1:2:(nargin-1) - if (! ischar (varargin{i})) - error ("OdePkg:InvalidArgument", - "not all even input arguments are strings"); - endif - name = varargin{i}; while (1) pos = fuzzy_compare (name, fields); - if (size (pos, 1) == 0) + if (isempty (pos)) error ("OdePkg:InvalidArgument", "invalid property. No property found with name '%s'", name); endif - if (size (pos, 1) == 1) + if (rows (pos) == 1) if (! strcmp (lower (deblank (name)), lower (deblank (fields(pos,:))))) warning ("OdePkg:InvalidArgument", "no exact matching for ", "'%s'. Assuming you were intending '%s'", name, deblank (fields(pos,:))); endif - opt.(deblank (fields(pos,:))) = varargin{i+1}; - break + odestruct.(deblank (fields(pos,:))) = varargin{i+1}; + break; endif + ## FIXME: Do we really need interactive selection? ## if there are more matching, ask the user to be more precise warning ("OdePkg:InvalidArgument", "no exact matching for '%s'. ", "%d possible fields were found", - name, size (pos, 1)); - for j = 1:(size (pos, 1)) - fprintf ("%s\n", deblank (fields(pos(j),:))); + name, rows (pos)); + for j = 1:rows (pos) + printf ("%s\n", deblank (fields(pos(j),:))); endfor do - fprintf ("Please insert field name again\n"); + disp ("Please insert field name again"); name = input ("New field name: "); until (ischar (name)) endwhile endfor ## check if all has been done gives a valid OdePkg struct - ode_struct_value_check (opt); + ode_struct_value_check (odestruct); endfunction ## function useful to print all the possible options function print_options () - printf ("These following are all possible options.\n", - "Default values are put in square brackets.\n\n"); - - disp (" AbsTol: scalar or vector, >0, [1.e-6]"); + disp ("These following are all possible options."); + disp ("Default values are put in square brackets."); + disp (""); + disp (" AbsTol: scalar or vector, >0, [1e-6]"); disp (" Algorithm: string, {['gmres'], 'pcg', 'bicgstab'}"); disp (" BDF: binary, {'on', ['off']}"); disp (" Choice: switch, {[1], 2}"); @@ -301,7 +299,7 @@ disp (" Mass: matrix or function_handle, []"); disp (" MassConstant: binary, {'on', ['off']}"); disp (" MassSingular: switch, {'yes', ['maybe'], 'no'}"); - disp ("MaxNewtonIterations: scalar, integer, >0, [1.e3]"); + disp ("MaxNewtonIterations: scalar, integer, >0, [1e3]"); disp (" MaxOrder: switch, {1, 2, 3, 4, [5]}"); disp (" MaxStep: scalar, >0, []"); disp (" MStateDependence: switch, {'none', ['weak'], 'strong'}"); @@ -315,7 +313,7 @@ disp (" PolynomialDegree: scalar, integer, >0, []"); disp (" QuadratureOrder: scalar, integer, >0, []"); disp (" Refine: scalar, integer, >0, []"); - disp (" RelTol: scalar, >0, [1.e-3]"); + disp (" RelTol: scalar, >0, [1e-3]"); disp (" Restart: scalar, integer, >0, [20]"); disp (" Stats: binary, {'on', ['off']}"); disp (" TimeStepNumber: scalar, integer, >0, []"); @@ -330,34 +328,35 @@ %! # A new OdePkg options structure with default values is created. %! %! odeoptA = odeset (); -%! + %!demo %! # A new OdePkg options structure with manually set options -%! # "AbsTol" and "RelTol" is created. +%! # for "AbsTol" and "RelTol" is created. %! %! odeoptB = odeset ("AbsTol", 1e-2, "RelTol", 1e-1); -%! + %!demo -%! # A new OdePkg options structure from odeoptB is created with +%! # A new OdePkg options structure is created from odeoptB with %! # a modified value for option "NormControl". -%! + %! odeoptB = odeset ("AbsTol", 1e-2, "RelTol", 1e-1); %! odeoptC = odeset (odeoptB, "NormControl", "on"); ## All tests that are needed to check if a correct resp. valid option ## has been set are implemented in ode_struct_value_check.m. -%! ## Turn off output of warning messages for all tests, turn them on -%! ## again if the last test is called -%! warning ("off", "OdePkg:InvalidArgument"); -%!test odeoptA = odeset (); -%!test odeoptB = odeset ("AbsTol", 1e-2, "RelTol", 1e-1); -%! if (odeoptB.AbsTol != 1e-2), error; endif -%! if (odeoptB.RelTol != 1e-1), error; endif -%!test odeoptB = odeset ("AbsTol", 1e-2, "RelTol", 1e-1); -%! odeoptC = odeset (odeoptB, "NormControl", "on"); -%!test odeoptB = odeset ("AbsTol", 1e-2, "RelTol", 1e-1); -%! odeoptC = odeset (odeoptB, "NormControl", "on"); -%! odeoptD = odeset (odeoptC, odeoptB); -%! -%! warning ("on", "OdePkg:InvalidArgument"); +%!test +%! wstate = warning ("off", "OdePkg:InvalidArgument"); +%! unwind_protect +%! odeoptA = odeset (); +%! ## FIXME: no assert check on odeoptA +%! odeoptB = odeset ("AbsTol", 1e-2, "RelTol", 1e-1); +%! assert (odeoptB.AbsTol, 1e-2); +%! assert (odeoptB.RelTol, 1e-1); +%! odeoptC = odeset (odeoptB, "NormControl", "on"); +%! ## FIXME: no assert check on odeoptC +%! odeoptD = odeset (odeoptC, odeoptB); +%! ## FIXME: no assert check on odeoptD +%! unwind_protect_cleanup +%! warning (wstate); +%! end_unwind_protect