1 %# Copyright (C) 2006-2012, Thomas Treichl <treichl@users.sourceforge.net>
2 %# OdePkg - A package for solving ordinary differential equations and more
4 %# This program is free software; you can redistribute it and/or modify
5 %# it under the terms of the GNU General Public License as published by
6 %# the Free Software Foundation; either version 2 of the License, or
7 %# (at your option) any later version.
9 %# This program is distributed in the hope that it will be useful,
10 %# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 %# GNU General Public License for more details.
14 %# You should have received a copy of the GNU General Public License
15 %# along with this program; If not, see <http://www.gnu.org/licenses/>.
18 %# @deftypefn {Function File} {[@var{}] =} ode23 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])
19 %# @deftypefnx {Command} {[@var{sol}] =} ode23 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])
20 %# @deftypefnx {Command} {[@var{t}, @var{y}, [@var{xe}, @var{ye}, @var{ie}]] =} ode23 (@var{@@fun}, @var{slot}, @var{init}, [@var{opt}], [@var{par1}, @var{par2}, @dots{}])
22 %# This function file can be used to solve a set of non--stiff ordinary differential equations (non--stiff ODEs) or non--stiff differential algebraic equations (non--stiff DAEs) with the well known explicit Runge--Kutta method of order (2,3).
24 %# If this function is called with no return argument then plot the solution over time in a figure window while solving the set of ODEs that are defined in a function and specified by the function handle @var{@@fun}. The second input argument @var{slot} is a double vector that defines the time slot, @var{init} is a double vector that defines the initial values of the states, @var{opt} can optionally be a structure array that keeps the options created with the command @command{odeset} and @var{par1}, @var{par2}, @dots{} can optionally be other input arguments of any type that have to be passed to the function defined by @var{@@fun}.
26 %# If this function is called with one return argument then return the solution @var{sol} of type structure array after solving the set of ODEs. The solution @var{sol} has the fields @var{x} of type double column vector for the steps chosen by the solver, @var{y} of type double column vector for the solutions at each time step of @var{x}, @var{solver} of type string for the solver name and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector that keep the informations of the event function if an event function handle is set in the option argument @var{opt}.
28 %# If this function is called with more than one return argument then return the time stamps @var{t}, the solution values @var{y} and optionally the extended time stamp information @var{xe}, the extended solution information @var{ye} and the extended index information @var{ie} all of type double column vector.
30 %# For example, solve an anonymous implementation of the Van der Pol equation
33 %# fvdb = @@(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)];
35 %# vopt = odeset ("RelTol", 1e-3, "AbsTol", 1e-3, \
36 %# "NormControl", "on", "OutputFcn", @@odeplot);
37 %# ode23 (fvdb, [0 20], [2 0], vopt);
44 %# 20010703 the function file "ode23.m" was written by Marc Compere
45 %# under the GPL for the use with this software. This function has been
46 %# taken as a base for the following implementation.
47 %# 20060810, Thomas Treichl
48 %# This function was adapted to the new syntax that is used by the
49 %# new OdePkg for Octave and is compatible to Matlab's ode23.
51 function [varargout] = ode23 (vfun, vslot, vinit, varargin)
53 if (nargin == 0) %# Check number and types of all input arguments
55 error ('OdePkg:InvalidArgument', ...
56 'Number of input arguments must be greater than zero');
61 elseif ~(isa (vfun, 'function_handle') || isa (vfun, 'inline'))
62 error ('OdePkg:InvalidArgument', ...
63 'First input argument must be a valid function handle');
65 elseif (~isvector (vslot) || length (vslot) < 2)
66 error ('OdePkg:InvalidArgument', ...
67 'Second input argument must be a valid vector');
69 elseif (~isvector (vinit) || ~isnumeric (vinit))
70 error ('OdePkg:InvalidArgument', ...
71 'Third input argument must be a valid numerical value');
75 if (~isstruct (varargin{1}))
76 %# varargin{1:len} are parameters for vfun
78 vfunarguments = varargin;
80 elseif (length (varargin) > 1)
81 %# varargin{1} is an OdePkg options structure vopt
82 vodeoptions = odepkg_structure_check (varargin{1}, 'ode23');
83 vfunarguments = {varargin{2:length(varargin)}};
85 else %# if (isstruct (varargin{1}))
86 vodeoptions = odepkg_structure_check (varargin{1}, 'ode23');
91 else %# if (nargin == 3)
96 %# Start preprocessing, have a look which options are set in
97 %# vodeoptions, check if an invalid or unused option is set
98 vslot = vslot(:).'; %# Create a row vector
99 vinit = vinit(:).'; %# Create a row vector
100 if (length (vslot) > 2) %# Step size checking
101 vstepsizefixed = true;
103 vstepsizefixed = false;
106 %# Get the default options that can be set with 'odeset' temporarily
109 %# Implementation of the option RelTol has been finished. This option
110 %# can be set by the user to another value than default value.
111 if (isempty (vodeoptions.RelTol) && ~vstepsizefixed)
112 vodeoptions.RelTol = 1e-6;
113 warning ('OdePkg:InvalidArgument', ...
114 'Option "RelTol" not set, new value %f is used', vodeoptions.RelTol);
115 elseif (~isempty (vodeoptions.RelTol) && vstepsizefixed)
116 warning ('OdePkg:InvalidArgument', ...
117 'Option "RelTol" will be ignored if fixed time stamps are given');
120 %# Implementation of the option AbsTol has been finished. This option
121 %# can be set by the user to another value than default value.
122 if (isempty (vodeoptions.AbsTol) && ~vstepsizefixed)
123 vodeoptions.AbsTol = 1e-6;
124 warning ('OdePkg:InvalidArgument', ...
125 'Option "AbsTol" not set, new value %f is used', vodeoptions.AbsTol);
126 elseif (~isempty (vodeoptions.AbsTol) && vstepsizefixed)
127 warning ('OdePkg:InvalidArgument', ...
128 'Option "AbsTol" will be ignored if fixed time stamps are given');
130 vodeoptions.AbsTol = vodeoptions.AbsTol(:); %# Create column vector
133 %# Implementation of the option NormControl has been finished. This
134 %# option can be set by the user to another value than default value.
135 if (strcmp (vodeoptions.NormControl, 'on')) vnormcontrol = true;
136 else vnormcontrol = false; end
138 %# Implementation of the option NonNegative has been finished. This
139 %# option can be set by the user to another value than default value.
140 if (~isempty (vodeoptions.NonNegative))
141 if (isempty (vodeoptions.Mass)), vhavenonnegative = true;
143 vhavenonnegative = false;
144 warning ('OdePkg:InvalidArgument', ...
145 'Option "NonNegative" will be ignored if mass matrix is set');
147 else vhavenonnegative = false;
150 %# Implementation of the option OutputFcn has been finished. This
151 %# option can be set by the user to another value than default value.
152 if (isempty (vodeoptions.OutputFcn) && nargout == 0)
153 vodeoptions.OutputFcn = @odeplot;
154 vhaveoutputfunction = true;
155 elseif (isempty (vodeoptions.OutputFcn)), vhaveoutputfunction = false;
156 else vhaveoutputfunction = true;
159 %# Implementation of the option OutputSel has been finished. This
160 %# option can be set by the user to another value than default value.
161 if (~isempty (vodeoptions.OutputSel)), vhaveoutputselection = true;
162 else vhaveoutputselection = false; end
164 %# Implementation of the option OutputSave has been finished. This
165 %# option can be set by the user to another value than default value.
166 if (isempty (vodeoptions.OutputSave)), vodeoptions.OutputSave = 1;
169 %# Implementation of the option Refine has been finished. This option
170 %# can be set by the user to another value than default value.
171 if (vodeoptions.Refine > 0), vhaverefine = true;
172 else vhaverefine = false; end
174 %# Implementation of the option Stats has been finished. This option
175 %# can be set by the user to another value than default value.
177 %# Implementation of the option InitialStep has been finished. This
178 %# option can be set by the user to another value than default value.
179 if (isempty (vodeoptions.InitialStep) && ~vstepsizefixed)
180 vodeoptions.InitialStep = (vslot(1,2) - vslot(1,1)) / 10;
181 vodeoptions.InitialStep = vodeoptions.InitialStep / 10^vodeoptions.Refine;
182 warning ('OdePkg:InvalidArgument', ...
183 'Option "InitialStep" not set, new value %f is used', vodeoptions.InitialStep);
186 %# Implementation of the option MaxStep has been finished. This option
187 %# can be set by the user to another value than default value.
188 if (isempty (vodeoptions.MaxStep) && ~vstepsizefixed)
189 vodeoptions.MaxStep = abs (vslot(1,2) - vslot(1,1)) / 10;
190 warning ('OdePkg:InvalidArgument', ...
191 'Option "MaxStep" not set, new value %f is used', vodeoptions.MaxStep);
194 %# Implementation of the option Events has been finished. This option
195 %# can be set by the user to another value than default value.
196 if (~isempty (vodeoptions.Events)), vhaveeventfunction = true;
197 else vhaveeventfunction = false; end
199 %# The options 'Jacobian', 'JPattern' and 'Vectorized' will be ignored
200 %# by this solver because this solver uses an explicit Runge-Kutta
201 %# method and therefore no Jacobian calculation is necessary
202 if (~isequal (vodeoptions.Jacobian, vodetemp.Jacobian))
203 warning ('OdePkg:InvalidArgument', ...
204 'Option "Jacobian" will be ignored by this solver');
206 if (~isequal (vodeoptions.JPattern, vodetemp.JPattern))
207 warning ('OdePkg:InvalidArgument', ...
208 'Option "JPattern" will be ignored by this solver');
210 if (~isequal (vodeoptions.Vectorized, vodetemp.Vectorized))
211 warning ('OdePkg:InvalidArgument', ...
212 'Option "Vectorized" will be ignored by this solver');
214 if (~isequal (vodeoptions.NewtonTol, vodetemp.NewtonTol))
215 warning ('OdePkg:InvalidArgument', ...
216 'Option "NewtonTol" will be ignored by this solver');
218 if (~isequal (vodeoptions.MaxNewtonIterations,...
219 vodetemp.MaxNewtonIterations))
220 warning ('OdePkg:InvalidArgument', ...
221 'Option "MaxNewtonIterations" will be ignored by this solver');
224 %# Implementation of the option Mass has been finished. This option
225 %# can be set by the user to another value than default value.
226 if (~isempty (vodeoptions.Mass) && isnumeric (vodeoptions.Mass))
227 vhavemasshandle = false; vmass = vodeoptions.Mass; %# constant mass
228 elseif (isa (vodeoptions.Mass, 'function_handle'))
229 vhavemasshandle = true; %# mass defined by a function handle
230 else %# no mass matrix - creating a diag-matrix of ones for mass
231 vhavemasshandle = false; %# vmass = diag (ones (length (vinit), 1), 0);
234 %# Implementation of the option MStateDependence has been finished.
235 %# This option can be set by the user to another value than default
237 if (strcmp (vodeoptions.MStateDependence, 'none'))
238 vmassdependence = false;
239 else vmassdependence = true;
242 %# Other options that are not used by this solver. Print a warning
243 %# message to tell the user that the option(s) is/are ignored.
244 if (~isequal (vodeoptions.MvPattern, vodetemp.MvPattern))
245 warning ('OdePkg:InvalidArgument', ...
246 'Option "MvPattern" will be ignored by this solver');
248 if (~isequal (vodeoptions.MassSingular, vodetemp.MassSingular))
249 warning ('OdePkg:InvalidArgument', ...
250 'Option "MassSingular" will be ignored by this solver');
252 if (~isequal (vodeoptions.InitialSlope, vodetemp.InitialSlope))
253 warning ('OdePkg:InvalidArgument', ...
254 'Option "InitialSlope" will be ignored by this solver');
256 if (~isequal (vodeoptions.MaxOrder, vodetemp.MaxOrder))
257 warning ('OdePkg:InvalidArgument', ...
258 'Option "MaxOrder" will be ignored by this solver');
260 if (~isequal (vodeoptions.BDF, vodetemp.BDF))
261 warning ('OdePkg:InvalidArgument', ...
262 'Option "BDF" will be ignored by this solver');
265 %# Starting the initialisation of the core solver ode23
266 vtimestamp = vslot(1,1); %# timestamp = start time
267 vtimelength = length (vslot); %# length needed if fixed steps
268 vtimestop = vslot(1,vtimelength); %# stop time = last value
269 %# 20110611, reported by Nils Strunk
270 %# Make it possible to solve equations from negativ to zero,
271 %# eg. vres = ode23 (@(t,y) y, [-2 0], 2);
272 vdirection = sign (vtimestop - vtimestamp); %# Direction flag
275 if (sign (vodeoptions.InitialStep) == vdirection)
276 vstepsize = vodeoptions.InitialStep;
277 else %# Fix wrong direction of InitialStep.
278 vstepsize = - vodeoptions.InitialStep;
280 vminstepsize = (vtimestop - vtimestamp) / (1/eps);
281 else %# If step size is given then use the fixed time steps
282 vstepsize = vslot(1,2) - vslot(1,1);
283 vminstepsize = sign (vstepsize) * eps;
286 vretvaltime = vtimestamp; %# first timestamp output
287 vretvalresult = vinit; %# first solution output
289 %# Initialize the OutputFcn
290 if (vhaveoutputfunction)
291 if (vhaveoutputselection) vretout = vretvalresult(vodeoptions.OutputSel);
292 else vretout = vretvalresult; end
293 feval (vodeoptions.OutputFcn, vslot.', ...
294 vretout.', 'init', vfunarguments{:});
297 %# Initialize the EventFcn
298 if (vhaveeventfunction)
299 odepkg_event_handle (vodeoptions.Events, vtimestamp, ...
300 vretvalresult.', 'init', vfunarguments{:});
303 vpow = 1/3; %# 20071016, reported by Luis Randez
304 va = [ 0, 0, 0; %# The Runge-Kutta-Fehlberg 2(3) coefficients
305 1/2, 0, 0; %# Coefficients proved on 20060827
306 -1, 2, 0]; %# See p.91 in Ascher & Petzold
307 vb2 = [0; 1; 0]; %# 2nd and 3rd order
308 vb3 = [1/6; 2/3; 1/6]; %# b-coefficients
311 %# The solver main loop - stop if the endpoint has been reached
312 vcntloop = 2; vcntcycles = 1; vu = vinit; vk = vu.' * zeros(1,3);
313 vcntiter = 0; vunhandledtermination = true; vcntsave = 2;
314 while ((vdirection * (vtimestamp) < vdirection * (vtimestop)) && ...
315 (vdirection * (vstepsize) >= vdirection * (vminstepsize)))
317 %# Hit the endpoint of the time slot exactely
318 if (vdirection * (vtimestamp + vstepsize) > vdirection * vtimestop)
319 %# vstepsize = vtimestop - vdirection * vtimestamp;
320 %# 20110611, reported by Nils Strunk
321 %# The endpoint of the time slot must be hit exactly,
322 %# eg. vsol = ode23 (@(t,y) y, [0 -1], 1);
323 vstepsize = vdirection * abs (abs (vtimestop) - abs (vtimestamp));
326 %# Estimate the three results when using this solver
328 vthetime = vtimestamp + vc(j,1) * vstepsize;
329 vtheinput = vu.' + vstepsize * vk(:,1:j-1) * va(j,1:j-1).';
330 if (vhavemasshandle) %# Handle only the dynamic mass matrix,
331 if (vmassdependence) %# constant mass matrices have already
332 vmass = feval ... %# been set before (if any)
333 (vodeoptions.Mass, vthetime, vtheinput, vfunarguments{:});
334 else %# if (vmassdependence == false)
335 vmass = feval ... %# then we only have the time argument
336 (vodeoptions.Mass, vthetime, vfunarguments{:});
338 vk(:,j) = vmass \ feval ...
339 (vfun, vthetime, vtheinput, vfunarguments{:});
342 (vfun, vthetime, vtheinput, vfunarguments{:});
346 %# Compute the 2nd and the 3rd order estimation
347 y2 = vu.' + vstepsize * (vk * vb2);
348 y3 = vu.' + vstepsize * (vk * vb3);
349 if (vhavenonnegative)
350 vu(vodeoptions.NonNegative) = abs (vu(vodeoptions.NonNegative));
351 y2(vodeoptions.NonNegative) = abs (y2(vodeoptions.NonNegative));
352 y3(vodeoptions.NonNegative) = abs (y3(vodeoptions.NonNegative));
354 if (vhaveoutputfunction && vhaverefine)
355 vSaveVUForRefine = vu;
358 %# Calculate the absolute local truncation error and the acceptable error
361 vdelta = abs (y3 - y2);
362 vtau = max (vodeoptions.RelTol * abs (vu.'), vodeoptions.AbsTol);
364 vdelta = norm (y3 - y2, Inf);
365 vtau = max (vodeoptions.RelTol * max (norm (vu.', Inf), 1.0), ...
368 else %# if (vstepsizefixed == true)
369 vdelta = 1; vtau = 2;
372 %# If the error is acceptable then update the vretval variables
373 if (all (vdelta <= vtau))
374 vtimestamp = vtimestamp + vstepsize;
375 vu = y3.'; %# MC2001: the higher order estimation as "local extrapolation"
376 %# Save the solution every vodeoptions.OutputSave steps
377 if (mod (vcntloop-1,vodeoptions.OutputSave) == 0)
378 vretvaltime(vcntsave,:) = vtimestamp;
379 vretvalresult(vcntsave,:) = vu;
380 vcntsave = vcntsave + 1;
382 vcntloop = vcntloop + 1; vcntiter = 0;
384 %# Call plot only if a valid result has been found, therefore this
385 %# code fragment has moved here. Stop integration if plot function
387 if (vhaveoutputfunction)
388 for vcnt = 0:vodeoptions.Refine %# Approximation between told and t
389 if (vhaverefine) %# Do interpolation
390 vapproxtime = (vcnt + 1) * vstepsize / (vodeoptions.Refine + 2);
391 vapproxvals = vSaveVUForRefine.' + vapproxtime * (vk * vb3);
392 vapproxtime = (vtimestamp - vstepsize) + vapproxtime;
395 vapproxtime = vtimestamp;
397 if (vhaveoutputselection)
398 vapproxvals = vapproxvals(vodeoptions.OutputSel);
400 vpltret = feval (vodeoptions.OutputFcn, vapproxtime, ...
401 vapproxvals, [], vfunarguments{:});
402 if vpltret %# Leave refinement loop
406 if (vpltret) %# Leave main loop
407 vunhandledtermination = false;
412 %# Call event only if a valid result has been found, therefore this
413 %# code fragment has moved here. Stop integration if veventbreak is
415 if (vhaveeventfunction)
417 odepkg_event_handle (vodeoptions.Events, vtimestamp, ...
418 vu(:), [], vfunarguments{:});
419 if (~isempty (vevent{1}) && vevent{1} == 1)
420 vretvaltime(vcntloop-1,:) = vevent{3}(end,:);
421 vretvalresult(vcntloop-1,:) = vevent{4}(end,:);
422 vunhandledtermination = false; break;
425 end %# If the error is acceptable ...
427 %# Update the step size for the next integration step
429 %# 20080425, reported by Marco Caliari
430 %# vdelta cannot be negative (because of the absolute value that
431 %# has been introduced) but it could be 0, then replace the zeros
432 %# with the maximum value of vdelta
433 vdelta(find (vdelta == 0)) = max (vdelta);
434 %# It could happen that max (vdelta) == 0 (ie. that the original
435 %# vdelta was 0), in that case we double the previous vstepsize
436 vdelta(find (vdelta == 0)) = max (vtau) .* (0.4 ^ (1 / vpow));
439 vstepsize = min (vodeoptions.MaxStep, ...
440 min (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow));
442 vstepsize = max (- vodeoptions.MaxStep, ...
443 max (0.8 * vstepsize * (vtau ./ vdelta) .^ vpow));
446 else %# if (vstepsizefixed)
447 if (vcntloop <= vtimelength)
448 vstepsize = vslot(vcntloop) - vslot(vcntloop-1);
449 else %# Get out of the main integration loop
454 %# Update counters that count the number of iteration cycles
455 vcntcycles = vcntcycles + 1; %# Needed for cost statistics
456 vcntiter = vcntiter + 1; %# Needed to find iteration problems
458 %# Stop solving because the last 1000 steps no successful valid
459 %# value has been found
460 if (vcntiter >= 5000)
461 error (['Solving has not been successful. The iterative', ...
462 ' integration loop exited at time t = %f before endpoint at', ...
463 ' tend = %f was reached. This happened because the iterative', ...
464 ' integration loop does not find a valid solution at this time', ...
465 ' stamp. Try to reduce the value of "InitialStep" and/or', ...
466 ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop);
471 %# Check if integration of the ode has been successful
472 if (vdirection * vtimestamp < vdirection * vtimestop)
473 if (vunhandledtermination == true)
474 error ('OdePkg:InvalidArgument', ...
475 ['Solving has not been successful. The iterative', ...
476 ' integration loop exited at time t = %f', ...
477 ' before endpoint at tend = %f was reached. This may', ...
478 ' happen if the stepsize grows smaller than defined in', ...
479 ' vminstepsize. Try to reduce the value of "InitialStep" and/or', ...
480 ' "MaxStep" with the command "odeset".\n'], vtimestamp, vtimestop);
482 warning ('OdePkg:InvalidArgument', ...
483 ['Solver has been stopped by a call of "break" in', ...
484 ' the main iteration loop at time t = %f before endpoint at', ...
485 ' tend = %f was reached. This may happen because the @odeplot', ...
486 ' function returned "true" or the @event function returned "true".'], ...
487 vtimestamp, vtimestop);
491 %# Postprocessing, do whatever when terminating integration algorithm
492 if (vhaveoutputfunction) %# Cleanup plotter
493 feval (vodeoptions.OutputFcn, vtimestamp, ...
494 vu.', 'done', vfunarguments{:});
496 if (vhaveeventfunction) %# Cleanup event function handling
497 odepkg_event_handle (vodeoptions.Events, vtimestamp, ...
498 vu.', 'done', vfunarguments{:});
500 %# Save the last step, if not already saved
501 if (mod (vcntloop-2,vodeoptions.OutputSave) ~= 0)
502 vretvaltime(vcntsave,:) = vtimestamp;
503 vretvalresult(vcntsave,:) = vu;
506 %# Print additional information if option Stats is set
507 if (strcmp (vodeoptions.Stats, 'on'))
509 vnsteps = vcntloop-2; %# vcntloop from 2..end
510 vnfailed = (vcntcycles-1)-(vcntloop-2)+1; %# vcntcycl from 1..end
511 vnfevals = 3*(vcntcycles-1); %# number of ode evaluations
512 vndecomps = 0; %# number of LU decompositions
513 vnpds = 0; %# number of partial derivatives
514 vnlinsols = 0; %# no. of solutions of linear systems
515 %# Print cost statistics if no output argument is given
517 vmsg = fprintf (1, 'Number of successful steps: %d\n', vnsteps);
518 vmsg = fprintf (1, 'Number of failed attempts: %d\n', vnfailed);
519 vmsg = fprintf (1, 'Number of function calls: %d\n', vnfevals);
525 if (nargout == 1) %# Sort output variables, depends on nargout
526 varargout{1}.x = vretvaltime; %# Time stamps are saved in field x
527 varargout{1}.y = vretvalresult; %# Results are saved in field y
528 varargout{1}.solver = 'ode23'; %# Solver name is saved in field solver
529 if (vhaveeventfunction)
530 varargout{1}.ie = vevent{2}; %# Index info which event occured
531 varargout{1}.xe = vevent{3}; %# Time info when an event occured
532 varargout{1}.ye = vevent{4}; %# Results when an event occured
535 varargout{1}.stats = struct;
536 varargout{1}.stats.nsteps = vnsteps;
537 varargout{1}.stats.nfailed = vnfailed;
538 varargout{1}.stats.nfevals = vnfevals;
539 varargout{1}.stats.npds = vnpds;
540 varargout{1}.stats.ndecomps = vndecomps;
541 varargout{1}.stats.nlinsols = vnlinsols;
543 elseif (nargout == 2)
544 varargout{1} = vretvaltime; %# Time stamps are first output argument
545 varargout{2} = vretvalresult; %# Results are second output argument
546 elseif (nargout == 5)
547 varargout{1} = vretvaltime; %# Same as (nargout == 2)
548 varargout{2} = vretvalresult; %# Same as (nargout == 2)
549 varargout{3} = []; %# LabMat doesn't accept lines like
550 varargout{4} = []; %# varargout{3} = varargout{4} = [];
552 if (vhaveeventfunction)
553 varargout{3} = vevent{3}; %# Time info when an event occured
554 varargout{4} = vevent{4}; %# Results when an event occured
555 varargout{5} = vevent{2}; %# Index info which event occured
560 %! # We are using the "Van der Pol" implementation for all tests that
561 %! # are done for this function. We also define a Jacobian, Events,
562 %! # pseudo-Mass implementation. For further tests we also define a
563 %! # reference solution (computed at high accuracy) and an OutputFcn
564 %!function [ydot] = fpol (vt, vy, varargin) %# The Van der Pol
565 %! ydot = [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)];
566 %!function [vjac] = fjac (vt, vy, varargin) %# its Jacobian
567 %! vjac = [0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2];
568 %!function [vjac] = fjcc (vt, vy, varargin) %# sparse type
569 %! vjac = sparse ([0, 1; -1 - 2 * vy(1) * vy(2), 1 - vy(1)^2]);
570 %!function [vval, vtrm, vdir] = feve (vt, vy, varargin)
571 %! vval = fpol (vt, vy, varargin); %# We use the derivatives
572 %! vtrm = zeros (2,1); %# that's why component 2
573 %! vdir = ones (2,1); %# seems to not be exact
574 %!function [vval, vtrm, vdir] = fevn (vt, vy, varargin)
575 %! vval = fpol (vt, vy, varargin); %# We use the derivatives
576 %! vtrm = ones (2,1); %# that's why component 2
577 %! vdir = ones (2,1); %# seems to not be exact
578 %!function [vmas] = fmas (vt, vy)
579 %! vmas = [1, 0; 0, 1]; %# Dummy mass matrix for tests
580 %!function [vmas] = fmsa (vt, vy)
581 %! vmas = sparse ([1, 0; 0, 1]); %# A sparse dummy matrix
582 %!function [vref] = fref () %# The computed reference sol
583 %! vref = [0.32331666704577, -1.83297456798624];
584 %!function [vout] = fout (vt, vy, vflag, varargin)
585 %! if (regexp (char (vflag), 'init') == 1)
586 %! if (any (size (vt) ~= [2, 1])) error ('"fout" step "init"'); end
587 %! elseif (isempty (vflag))
588 %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "calc"'); end
590 %! elseif (regexp (char (vflag), 'done') == 1)
591 %! if (any (size (vt) ~= [1, 1])) error ('"fout" step "done"'); end
592 %! else error ('"fout" invalid vflag');
595 %! %# Turn off output of warning messages for all tests, turn them on
596 %! %# again if the last test is called
597 %!error %# input argument number one
598 %! warning ('off', 'OdePkg:InvalidArgument');
599 %! B = ode23 (1, [0 25], [3 15 1]);
600 %!error %# input argument number two
601 %! B = ode23 (@fpol, 1, [3 15 1]);
602 %!error %# input argument number three
603 %! B = ode23 (@flor, [0 25], 1);
604 %!test %# one output argument
605 %! vsol = ode23 (@fpol, [0 2], [2 0]);
606 %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3);
607 %! assert (isfield (vsol, 'solver'));
608 %! assert (vsol.solver, 'ode23');
609 %!test %# two output arguments
610 %! [vt, vy] = ode23 (@fpol, [0 2], [2 0]);
611 %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3);
612 %!test %# five output arguments and no Events
613 %! [vt, vy, vxe, vye, vie] = ode23 (@fpol, [0 2], [2 0]);
614 %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3);
615 %! assert ([vie, vxe, vye], []);
616 %!test %# anonymous function instead of real function
617 %! fvdb = @(vt,vy) [vy(2); (1 - vy(1)^2) * vy(2) - vy(1)];
618 %! vsol = ode23 (fvdb, [0 2], [2 0]);
619 %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3);
620 %!test %# extra input arguments passed through
621 %! vsol = ode23 (@fpol, [0 2], [2 0], 12, 13, 'KL');
622 %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3);
623 %!test %# empty OdePkg structure *but* extra input arguments
625 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt, 12, 13, 'KL');
626 %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3);
627 %!error %# strange OdePkg structure
628 %! vopt = struct ('foo', 1);
629 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt);
630 %!test %# Solve vdp in fixed step sizes
631 %! vsol = ode23 (@fpol, [0:0.1:2], [2 0]);
632 %! assert (vsol.x(:), [0:0.1:2]');
633 %! assert (vsol.y(end,:), fref, 1e-3);
634 %!test %# Solve in backward direction starting at t=0
635 %! vref = [-1.205364552835178, 0.951542399860817];
636 %! vsol = ode23 (@fpol, [0 -2], [2 0]);
637 %! assert ([vsol.x(end), vsol.y(end,:)], [-2, vref], 1e-3);
638 %!test %# Solve in backward direction starting at t=2
639 %! vref = [-1.205364552835178, 0.951542399860817];
640 %! vsol = ode23 (@fpol, [2 -2], fref);
641 %! assert ([vsol.x(end), vsol.y(end,:)], [-2, vref], 1e-3);
642 %!test %# Solve another anonymous function in backward direction
643 %! vref = [-1, 0.367879437558975];
644 %! vsol = ode23 (@(t,y) y, [0 -1], 1);
645 %! assert ([vsol.x(end), vsol.y(end,:)], vref, 1e-3);
646 %!test %# Solve another anonymous function below zero
647 %! vref = [0, 14.77810590694212];
648 %! vsol = ode23 (@(t,y) y, [-2 0], 2);
649 %! assert ([vsol.x(end), vsol.y(end,:)], vref, 1e-3);
650 %!test %# AbsTol option
651 %! vopt = odeset ('AbsTol', 1e-5);
652 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt);
653 %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3);
654 %!test %# AbsTol and RelTol option
655 %! vopt = odeset ('AbsTol', 1e-8, 'RelTol', 1e-8);
656 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt);
657 %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3);
658 %!test %# RelTol and NormControl option -- higher accuracy
659 %! vopt = odeset ('RelTol', 1e-8, 'NormControl', 'on');
660 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt);
661 %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-6);
662 %!test %# Keeps initial values while integrating
663 %! vopt = odeset ('NonNegative', 2);
664 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt);
665 %! assert ([vsol.x(end), vsol.y(end,:)], [2, 2, 0], 1e-1);
666 %!test %# Details of OutputSel and Refine can't be tested
667 %! vopt = odeset ('OutputFcn', @fout, 'OutputSel', 1, 'Refine', 5);
668 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt);
669 %!test %# Details of OutputSave can't be tested
670 %! vopt = odeset ('OutputSave', 1, 'OutputSel', 1);
671 %! vsla = ode23 (@fpol, [0 2], [2 0], vopt);
672 %! vopt = odeset ('OutputSave', 2);
673 %! vslb = ode23 (@fpol, [0 2], [2 0], vopt);
674 %! assert (length (vsla.x) > length (vslb.x))
675 %!test %# Stats must add further elements in vsol
676 %! vopt = odeset ('Stats', 'on');
677 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt);
678 %! assert (isfield (vsol, 'stats'));
679 %! assert (isfield (vsol.stats, 'nsteps'));
680 %!test %# InitialStep option
681 %! vopt = odeset ('InitialStep', 1e-8);
682 %! vsol = ode23 (@fpol, [0 0.2], [2 0], vopt);
683 %! assert ([vsol.x(2)-vsol.x(1)], [1e-8], 1e-9);
684 %!test %# MaxStep option
685 %! vopt = odeset ('MaxStep', 1e-2);
686 %! vsol = ode23 (@fpol, [0 0.2], [2 0], vopt);
687 %! assert ([vsol.x(5)-vsol.x(4)], [1e-2], 1e-2);
688 %!test %# Events option add further elements in vsol
689 %! vopt = odeset ('Events', @feve);
690 %! vsol = ode23 (@fpol, [0 10], [2 0], vopt);
691 %! assert (isfield (vsol, 'ie'));
692 %! assert (vsol.ie, [2; 1; 2; 1]);
693 %! assert (isfield (vsol, 'xe'));
694 %! assert (isfield (vsol, 'ye'));
695 %!test %# Events option, now stop integration
696 %! vopt = odeset ('Events', @fevn, 'NormControl', 'on');
697 %! vsol = ode23 (@fpol, [0 10], [2 0], vopt);
698 %! assert ([vsol.ie, vsol.xe, vsol.ye], ...
699 %! [2.0, 2.496110, -0.830550, -2.677589], 1e-3);
700 %!test %# Events option, five output arguments
701 %! vopt = odeset ('Events', @fevn, 'NormControl', 'on');
702 %! [vt, vy, vxe, vye, vie] = ode23 (@fpol, [0 10], [2 0], vopt);
703 %! assert ([vie, vxe, vye], ...
704 %! [2.0, 2.496110, -0.830550, -2.677589], 1e-3);
705 %!test %# Jacobian option
706 %! vopt = odeset ('Jacobian', @fjac);
707 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt);
708 %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3);
709 %!test %# Jacobian option and sparse return value
710 %! vopt = odeset ('Jacobian', @fjcc);
711 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt);
712 %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3);
714 %! %# test for JPattern option is missing
715 %! %# test for Vectorized option is missing
716 %! %# test for NewtonTol option is missing
717 %! %# test for MaxNewtonIterations option is missing
719 %!test %# Mass option as function
720 %! vopt = odeset ('Mass', @fmas);
721 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt);
722 %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3);
723 %!test %# Mass option as matrix
724 %! vopt = odeset ('Mass', eye (2,2));
725 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt);
726 %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3);
727 %!test %# Mass option as sparse matrix
728 %! vopt = odeset ('Mass', sparse (eye (2,2)));
729 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt);
730 %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3);
731 %!test %# Mass option as function and sparse matrix
732 %! vopt = odeset ('Mass', @fmsa);
733 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt);
734 %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3);
735 %!test %# Mass option as function and MStateDependence
736 %! vopt = odeset ('Mass', @fmas, 'MStateDependence', 'strong');
737 %! vsol = ode23 (@fpol, [0 2], [2 0], vopt);
738 %! assert ([vsol.x(end), vsol.y(end,:)], [2, fref], 1e-3);
739 %!test %# Set BDF option to something else than default
740 %! vopt = odeset ('BDF', 'on');
741 %! [vt, vy] = ode23 (@fpol, [0 2], [2 0], vopt);
742 %! assert ([vt(end), vy(end,:)], [2, fref], 1e-3);
744 %! %# test for MvPattern option is missing
745 %! %# test for InitialSlope option is missing
746 %! %# test for MaxOrder option is missing
748 %! warning ('on', 'OdePkg:InvalidArgument');
750 %# Local Variables: ***