changeset 22639:7efa2d0e22c9 stable

More Matlab-compatible implementation of OutputFcn. Clean up odeplot.m. * ode23.m, ode45.m: Call OutputFcn with null inputs when using "done" flag. Rewrite BIST OutputFcn fout to follow expected conventions. * integrate_adaptive.m: Rename variable pltret to stop_solve. Fix issue where only final result of OutputFcn could stop integration. * odeplot.m: Rewrite docstring. Rename output variable to stop_solve. Always return false from function since odeplot never interrupts integration. Set x limits of plot in "init" for better performance.
author Rik <rik@octave.org>
date Tue, 18 Oct 2016 11:31:13 -0700
parents 0128795eeac6
children 58c1c6aeb737
files scripts/ode/ode23.m scripts/ode/ode45.m scripts/ode/odeplot.m scripts/ode/private/integrate_adaptive.m
diffstat 4 files changed, 81 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/ode/ode23.m	Mon Oct 17 14:03:45 2016 -0700
+++ b/scripts/ode/ode23.m	Tue Oct 18 11:31:13 2016 -0700
@@ -243,8 +243,7 @@
 
   ## Postprocessing, do whatever when terminating integration algorithm
   if (odeopts.haveoutputfunction)  # Cleanup plotter
-    feval (odeopts.OutputFcn, solution.t(end),
-           solution.x(end,:)', "done", odeopts.funarguments{:});
+    feval (odeopts.OutputFcn, [], [], "done", odeopts.funarguments{:});
   endif
   if (! isempty (odeopts.Events))   # Cleanup event function handling
     ode_event_handler (odeopts.Events, solution.t(end),
@@ -360,15 +359,21 @@
 %!  mas = sparse ([1, 0; 0, 1]);    # A sparse dummy matrix
 %!endfunction
 %!function out = fout (t, y, flag, varargin)
-%!  if (regexp (char (flag), "init") == 1)
-%!    if (any (size (t) != [2, 1])) error ('"fout" step "init"'); endif
+%!  out = false;
+%!  if (strcmp (flag, "init"))
+%!    if (! isequal (size (t), [2, 1]))
+%!      error ('fout: step "init"');
+%!    endif
 %!  elseif (isempty (flag))
-%!    if (any (size (t) != [1, 1])) error ('"fout" step "calc"'); endif
-%!    out = false;
-%!  elseif (regexp (char (flag), "done") == 1)
-%!    if (any (size (t) != [1, 1])) error ('"fout" step "done"'); endif
+%!    if (! isequal (size (t), [1, 1]))
+%!      error ('fout: step "calc"');
+%!    endif
+%!  elseif (strcmp (flag, "done"))
+%!    if (! isempty (t))
+%!      warning ('fout: step "done"');
+%!    endif
 %!  else
-%!    error ('"fout" invalid flag');
+%!    error ("fout: invalid flag <%s>", flag);
 %!  endif
 %!endfunction
 %!
--- a/scripts/ode/ode45.m	Mon Oct 17 14:03:45 2016 -0700
+++ b/scripts/ode/ode45.m	Tue Oct 18 11:31:13 2016 -0700
@@ -224,8 +224,7 @@
 
   ## Postprocessing, do whatever when terminating integration algorithm
   if (odeopts.haveoutputfunction)  # Cleanup plotter
-    feval (odeopts.OutputFcn, solution.t(end),
-           solution.x(end,:)', "done", odeopts.funarguments{:});
+    feval (odeopts.OutputFcn, [], [], "done", odeopts.funarguments{:});
   endif
   if (! isempty (odeopts.Events))   # Cleanup event function handling
     ode_event_handler (odeopts.Events, solution.t(end),
@@ -341,15 +340,21 @@
 %!  mas = sparse ([1, 0; 0, 1]);   # A sparse dummy matrix
 %!endfunction
 %!function out = fout (t, y, flag, varargin)
-%!  if (regexp (char (flag), "init") == 1)
-%!    if (any (size (t) != [2, 1])) error ('"fout" step "init"'); endif
+%!  out = false;
+%!  if (strcmp (flag, "init"))
+%!    if (! isequal (size (t), [2, 1]))
+%!      error ('fout: step "init"');
+%!    endif
 %!  elseif (isempty (flag))
-%!    if (any (size (t) != [1, 1])) error ('"fout" step "calc"'); endif
-%!    out = false;
-%!  elseif (regexp (char (flag), 'done') == 1)
-%!    if (any (size (t) != [1, 1])) error ('"fout" step "done"'); endif
+%!    if (! isequal (size (t), [1, 1]))
+%!      error ('fout: step "calc"');
+%!    endif
+%!  elseif (strcmp (flag, "done"))
+%!    if (! isempty (t))
+%!      warning ('fout: step "done"');
+%!    endif
 %!  else
-%!    error ('"fout" invalid flag');
+%!    error ("fout: invalid flag <%s>", flag);
 %!  endif
 %!endfunction
 %!
--- a/scripts/ode/odeplot.m	Mon Oct 17 14:03:45 2016 -0700
+++ b/scripts/ode/odeplot.m	Tue Oct 18 11:31:13 2016 -0700
@@ -19,38 +19,34 @@
 ## Author: Thomas Treichl <treichl@users.sourceforge.net>
 
 ## -*- texinfo -*-
-## @deftypefn {} {@var{retval} =} odeplot (@var{t}, @var{y}, @var{flag})
+## @deftypefn {} {@var{stop_solve} =} odeplot (@var{t}, @var{y}, @var{flag})
 ##
-## Open a new figure window and plot input @var{y} over time during the
-## solving of an ode problem.
+## Open a new figure window and plot the solution of an ode problem at each
+## time step during the integration.
 ##
-## The input @var{y} is a column vector.  The types and values of the input
-## parameter @var{t} and output parameter @var{ret} depend on the input
-## @var{flag} that is of type string.  If @var{flag} is
+## The types and values of the input parameters @var{t} and @var{y} depend on
+## the input @var{flag} that is of type string.  Valid values of @var{flag}
+## are:
 ##
 ## @table @option
 ## @item @qcode{"init"}
-## then @var{t} must be a column vector of length 2 with the first and
-## the last time step;  Nothing is returned from this function.
+## The input @var{t} must be a column vector of length 2 with the first and
+## last time step (@code{[@var{tfirst} @var{tlast}]}.  The input @var{y}
+## contains the initial conditions for the ode problem (@var{y0}).
 ##
 ## @item @qcode{""}
-## then @var{t} must be a scalar double specifying the actual time step;
-## The return value is false (resp. value 0) for @qcode{"not stop solving"}.
+## The input @var{t} must be a scalar double specifying the time for which
+## the solution in input @var{y} was calculated.
 ##
 ## @item @qcode{"done"}
-## then @var{t} must be a scalar double specifying the last time step;
-## Nothing is returned from this function.
+## The inputs should be empty, but are ignored if they are present.
 ## @end table
 ##
-## This function is called by an ode solver function if it was specified in
-## an options structure with @code{odeset}.  This function is an internal
-## helper function; It should never be necessary for this function to be
-## directly called by a user.  There is only minimal error detection
-## implemented in order to to achieve the highest performance.
+## @code{odeplot} always returns false, i.e., don't stop the ode solver.
 ##
-## For example, solve an anonymous implementation of the
+## Example: solve an anonymous implementation of the
 ## @nospell{@qcode{"Van der Pol"}} equation and display the results while
-## solving
+## solving.
 ##
 ## @example
 ## @group
@@ -61,19 +57,31 @@
 ## @end group
 ## @end example
 ##
-## @seealso{odeset, odeget}
+## Background Information:
+## This function is called by an ode solver function if it was specified in
+## the @qcode{"OutputFcn"} property of an options structure created with
+## @code{odeset}.  The ode solver will initially call the function with the
+## syntax @code{odeplot ([@var{tfirst}, @var{tlast}], @var{y0}, "init")}.  The
+## function initializes internal variables, creates a new figure window, and
+## sets the x limits of the plot.  Subsequently, at each time step during the
+## integration the ode solver calls @code{odeplot (@var{t}, @var{y}, [])}.
+## At the end of the solution the ode solver calls
+## @code{odeplot ([], [], "end")} so that odeplot can perform any clean-up
+## actions required.
+## @seealso{odeset, odeget, ode23, ode45}
 ## @end deftypefn
 
-function retval = odeplot (t, y, flag)
+function stop_solve = odeplot (t, y, flag)
 
   ## No input argument checking is done for better performance
   persistent hlines num_lines told yold;
   persistent idx = 1;   # Don't remove.  Required for Octave parser.
 
+  ## odeplot never stops the integration
+  stop_solve = false;
+
   if (isempty (flag))
     ## Default case, plot and return a value
-    ## FALSE for "not stopping the integration"
-    ## TRUE  for "stopping the integration"
     idx += 1;
     told(idx,1) = t(1,1);
     yold(:,idx) = y(:,1);
@@ -85,14 +93,14 @@
     retval = false;
 
   elseif (strcmp (flag, "init"))
-    ## Nothing to return
     ## t is either the time slot [tstart tstop] or [t0, t1, ..., tn]
-    ## y is the initial value vector "init"
+    ## y is the initial value vector for the ode solution
     idx = 1;
     told = t(1,1);
     yold = y(:,1);
     figure ();
     hlines = plot (told, yold, "-", "marker", ".", "markersize", 9);
+    xlim ([t(1), t(end)]);  # Fix limits which also speeds up plotting
     num_lines = numel (hlines);
 
   elseif (strcmp (flag, "done"))
--- a/scripts/ode/private/integrate_adaptive.m	Mon Oct 17 14:03:45 2016 -0700
+++ b/scripts/ode/private/integrate_adaptive.m	Tue Oct 18 11:31:13 2016 -0700
@@ -96,8 +96,8 @@
     else
       solution.retout = x;
     endif
-    feval (options.OutputFcn, tspan, solution.retout,
-           "init", options.funarguments{:});
+    feval (options.OutputFcn, tspan, solution.retout, "init",
+           options.funarguments{:});
   endif
 
   ## Initialize the EventFcn
@@ -186,7 +186,7 @@
           endif
 
           ## Call OutputFcn only if a valid result has been found.
-          ## Stop integration if function returns false.
+          ## Stop integration if function returns true.
           if (options.haveoutputfunction)
             cnt = options.Refine + 1;
             approxtime = linspace (t_old, t_new, cnt);
@@ -196,12 +196,16 @@
             if (! isempty (options.OutputSel))
               approxvals = approxvals(options.OutputSel, :);
             endif
+            stop_solve = false;
             for ii = 1:numel (approxtime)
-              pltret = feval (options.OutputFcn, approxtime(ii),
-                              approxvals(:, ii), [],
-                              options.funarguments{:});
+              stop_solve = feval (options.OutputFcn,
+                                  approxtime(ii), approxvals(:, ii), [],
+                                  options.funarguments{:});
+              if (stop_solve)
+                break;  # break from inner loop
+              endif
             endfor
-            if (pltret)  # Leave main loop
+            if (stop_solve)  # Leave main loop
               solution.unhandledtermination = false;
               break;
             endif
@@ -230,7 +234,7 @@
         endif
 
         ## Call OutputFcn only if a valid result has been found.
-        ## Stop integration if function returns false.
+        ## Stop integration if function returns true.
         if (options.haveoutputfunction)
           cnt = options.Refine + 1;
           approxtime = linspace (t_old, t_new, cnt);
@@ -240,11 +244,16 @@
           if (! isempty (options.OutputSel))
             approxvals = approxvals(options.OutputSel, :);
           endif
+          stop_solve = false;
           for ii = 1:numel (approxtime)
-            pltret = feval (options.OutputFcn, approxtime(ii),
-                            approxvals(:, ii), [], options.funarguments{:});
+            stop_solve = feval (options.OutputFcn,
+                                approxtime(ii), approxvals(:, ii), [],
+                                options.funarguments{:});
+            if (stop_solve)
+              break;  # break from inner loop
+            endif
           endfor
-          if (pltret)  # Leave main loop
+          if (stop_solve)  # Leave main loop
             solution.unhandledtermination = false;
             break;
           endif