Mercurial > octave
changeset 22900:ee1c77705fcd
Refactor code for ode solvers and private functions
* scripts/ode/decic.m: Add function decic.m for consistent
initial value computation.
* scripts/ode/private/odedefaults.m: Add function which
returns default options for every solver.
* scripts/ode/private/odemergeopts.m: Add function which
merges user option struct with default struct.
* scripts/ode/module.mk: Add functions to function list.
* scripts/ode/ode23.m: Refactor code for ode23.m.
Integrate only adaptively.
* scripts/ode/ode45.m: Refactor code for ode45.m.
Integrate only adaptively.
* scripts/ode/odeget.m: Avoid using ode_struct_value_check
and known_option_names.
* scripts/ode/odeset.m: Use inputParser in odeset.
* scripts/ode/private/integrate_adaptive.m: use fewer variables.
* scripts/ode/private/integrate_const.m: use fewer variables.
* scripts/ode/private/integrate_n_steps.m: use fewer variables.
author | Francesco Faccio <francesco.faccio@mail.polimi.it> |
---|---|
date | Tue, 23 Aug 2016 02:31:51 +0200 |
parents | 31bd8a50d44c |
children | 4c56f3ffec04 |
files | scripts/ode/decic.m scripts/ode/module.mk scripts/ode/ode23.m |
diffstat | 3 files changed, 228 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/ode/decic.m Tue Aug 23 02:31:51 2016 +0200 @@ -0,0 +1,223 @@ +## Copyright (C) 2016, Francesco Faccio <francesco.faccio@mail.polimi.it> +## +## This file is part of Octave. +## +## Octave is free software; you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or (at +## your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## <http://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {} {[@var{y0_new}, @var{yp0_new}] =} decic (@var{fun}, @var{t0}, @var{y0}, @var{fixed_y0}, @var{yp0}, @var{fixed_yp0}) +## @deftypefnx {} {[@var{y0_new}, @var{yp0_new}] =} decic (@var{fun}, @var{t0}, @var{y0}, @var{fixed_y0}, @var{yp0}, @var{fixed_yp0}, @var{options}) +## @deftypefnx {} {[@var{y0_new}, @var{yp0_new}, @var{resnorm}] =} decic (@dots{}) +## +## Compute consistent initial conditions @var{y0_new} and @var{yp0_new}, given +## initial guesses @var{y0} and @var{yp0}. Choose a maximum of +## @code{length(@var{y0})} components between @var{fixed_y0} and +## @var{fixed_yp0} as fixed values. +## +## @var{fun} is a function handle. The function must accept three inputs where +## the first is time @var{t}, the second is a column vector of unknowns @var{y} +## and the third is a column vector of unknowns @var{yp}. +## +## @var{t0} is the initial time such that @code{@var{fun}(@var{t0}, +## @var{y0_new}, @var{yp0_new}) = 0}, specified as a scalar. +## +## @var{y0} is a vector used as initial guess for @var{y}. +## +## @var{fixed_y0} is a vector which specifies the components of @var{y0} to be +## hold fixed. Choose a maximum of @code{length(@var{y0})} components between +## @var{fixed_y0} and @var{fixed_yp0} as fixed values. +## Set @var{fixed_y0}(i) component to 1 if you want to fix the value of +## @var{y0}(i). +## Set @var{fixed_y0}(i) component to 0 if you want to allow the value of +## @var{y0}(i) to change. +## +## @var{yp0} is a vector used as initial guess for @var{yp}. +## +## @var{fixed_yp0} is a vector which specifies the components of @var{yp0} to +## be hold fixed. Choose a maximum of @code{length(@var{yp0})} components +## between @var{fixed_y0} and @var{fixed_yp0} as fixed values. +## Set @var{fixed_yp0}(i) component to 1 if you want to fix the value of +## @var{yp0}(i). +## Set @var{fixed_yp0}(i) component to 0 if you want to allow the value of +## @var{yp0}(i) to change. +## +## The optional seventh argument @var{options} is a structure array. +## Use @code{odeset} to generate this structure. The relevant options are +## @code{RelTol} and @code{AbsTol} which specify the error thresholds used to +## compute the initial conditions. +## +## The function typically returns two outputs. Variable @var{y0_new} is a +## column vector and contains the consistent initial value of y. The +## output @var{yp0_new} is a column vector and contains the consistent initial +## value of yp. +## +## The optional third output @var{resnorm} is the vector of norm of the +## residuals. If @var{resnorm} is small, @code{decic} has successfully computed +## the initial conditions. If the value of @var{resnorm} is large, use +## @code{RelTol} and @code{AbsTol} to adjust it. +## +## Example: Compute initial conditions of @nospell{Robetson}'s equations: +## +## @example +## @group +## function res = robertsidae(@var{t}, @var{y}, @var{yp}) +## res = [-(@var{yp}(1) + 0.04*@var{y}(1) - 1e4*@var{y}(2)*@var{y}(3)); +## -(@var{yp}(2) - 0.04*@var{y}(1) + 1e4*@var{y}(2)*@var{y}(3) + +## 3e7*@var{y}(2)^2); +## @var{y}(1) + @var{y}(2) + @var{y}(3) - 1]; +## endfunction +## @end group +## [@var{y0_new},@var{yp0_new}] = decic (@@robertsidae, 0, [1; 0; 0], [1; 1; 0], +## [-1e-4; 1; 0], [0; 0; 0]); +## @end example +## @seealso{odeset} +## @end deftypefn + +function [y0_new, yp0_new, resnrm] = decic (odefun, t0, y0, fixed_y0, yp0, + fixed_yp0, options) + + if (nargin < 6 || nargin > 7 || nargout > 3) + print_usage (); + endif + + #Check input + if (! isa (odefun, "function_handle")) + error ("Octave:invalid-input-arg", + "decic: FUN must be a valid function handle"); + endif + + if (! isnumeric (t0) || numel (t0) != 1) + error ("Octave:invalid-input-arg", + "decic: INIT must be a numeric scalar value"); + endif + + if (! isnumeric (y0) || ! isvector (y0) || ! isnumeric (fixed_y0) || + ! isvector (fixed_y0) || ! isnumeric (yp0) || ! isvector (yp0)|| + ! isnumeric (fixed_yp0) || ! isvector (fixed_yp0)) + error ("Octave:invalid-input-arg", + "decic: y0, fixed_y0, yp0 and fixed_yp0 must be numeric vectors"); + + elseif (! isequal (numel (y0), numel (fixed_y0), numel (yp0), + numel (fixed_yp0))) + error ("Octave:invalid-input-arg", + "decic: length of y0, fixed_y0, yp0 and fixed_yp0 must be equal"); + endif + + for i = 1:numel (y0) + if (! (fixed_y0(i) == 0 || fixed_y0(i) == 1) || ! (fixed_yp0(i) == 0 + || fixed_yp0(i) == 1)) + error ("Octave:invalid-input-arg", + "decic: fixed_y0 and fixed_yp0 must be boolean vectors"); + endif + endfor + + n = numel (y0); + nl = sum (~fixed_y0); + nu = sum (~fixed_yp0); + + if (n - nl - nu > 0) + error ("Octave:invalid-input-arg", + "decic: you cannot fix more than length(y0) components"); + endif + + #Set default value + TolFun = 0; + TolX = eps; + + #Check AbsTol and RelTol + if (nargin == 7) + if (! isempty (options.AbsTol)) + if (! isscalar (options.AbsTol)) + error ("Octave:invalid-input-arg", + "decic: AbsTol must be a scalar value"); + else + TolFun = options.AbsTol; + endif + endif + + if (! isempty (options.RelTol)) + if (! isscalar (options.RelTol)) + error ("Octave:invalid-input-arg", + "decic: RelTol must be a scalar value"); + else + TolX = options.RelTol; + endif + endif + endif + + x0 = [y0(~fixed_y0); yp0(~fixed_yp0)]; + opt = optimset ('tolfun', TolFun, 'tolx', TolX, 'display', 'iter-detailed'); + x = fminunc (@(x) objective (x, t0, y0, fixed_y0, yp0, fixed_yp0, nl, nu, + odefun), + x0, opt); + + y0_new = y0; + yp0_new = yp0; + + y0_new(~fixed_y0) = x(1:nl); + yp0_new(~fixed_yp0) = x(nl+1:nl+nu); + resnrm = odefun (t0, y0_new, yp0_new); + +endfunction + +function res = objective (x, t0, y0, fixed_y0, yp0, + fixed_yp0, nl, nu, odefun) + y = y0; + y(~fixed_y0) = x(1:nl); + yp = yp0; + yp(~fixed_yp0) = x(nl+1:nl+nu); + res = sqrt (sum (odefun (t0, y, yp) .^ 2)); +endfunction + +%!function res = rob (t, y, yp) +%! res =[-(yp(1) + 0.04*y(1) - 1e4*y(2)*y(3)); -(yp(2) - 0.04*y(1) + +%! + 1e4*y(2)*y(3) + 3e7*y(2)^2); y(1) + y(2) + y(3) - 1]; +%!endfunction + +%!test # Without options +%! ref1 = [1;0;0]; +%! ref2 = [-4e-2; 4e-2; 0]; +%! [ynew,ypnew] = decic (@rob,0,[1;0;0],[1;1;0],[23;110;0],[0;0;1]); +%! assert ([ynew(1:end), ypnew(1:end)], [ref1(1:end), ref2(1:end)], 1e-10); +%!test # With options +%! ref1 = [1;0;0]; +%! ref2 = [-4e-2; 4e-2; 0]; +%! opt = odeset ("AbsTol", 1e-8, "RelTol", 1e-4); +%! [ynew,ypnew] = decic (@rob,0,[1;0;0],[1;1;0],[23;110;0],[0;0;1],opt); +%! assert ([ynew(1:end), ypnew(1:end)], [ref1(1:end), ref2(1:end)], 1e-5); + +## Test input validation +%!error decic () +%!error decic (1) +%!error decic (1,2) +%!error decic (1,2,3,4,5,6,7,8) +%!error <FUN must be a valid function handle> +%! decic (1, 0, [1; 0; 0], [1; 1; 0], [-1e-4; 1; 0], [0; 0; 0]); +%!error <INIT must be a numeric scalar value> +%! decic (@rob, [1, 1], [1; 0; 0], [1; 1; 0], [-1e-4; 1; 0], [0; 0; 0]); +%!error <length of y0, fixed_y0, yp0 and fixed_yp0 must be equal> +%! decic (@rob, 0, [0; 0], [1; 1; 0], [-1e-4; 1; 0], [0; 0; 0]); +%!error <y0, fixed_y0, yp0 and fixed_yp0 must be numeric vectors> +%! decic (@rob, 0, [1; 0; 0], [1; 0],"", [0; 0; 0]); +%!error <y0, fixed_y0, yp0 and fixed_yp0 must be numeric vectors> +%! decic (@rob, 0, [1; 0; 0], [1; 1; 0], [-1e-4; 1; 0], [0; 0; "1"]); +%!error <fixed_y0 and fixed_yp0 must be boolean vectors> +%! decic (@rob, 0, [1; 0; 0], [5; 5; 0], [-1e-4; 1; 0], [0; 0; 0]); +%!error <fixed_y0 and fixed_yp0 must be boolean vectors> +%! decic (@rob, 0, [1; 0; 0], [1; 1; 0], [-1e-4; 1; 0], [0; 4; 0]); +%!error #too many components fixed +%! decic (@rob, 0, [1; 0; 0], [1; 1; 0], [-1e-4; 1; 0], [0; 1; 1]); + +
--- a/scripts/ode/module.mk Tue Aug 23 02:13:28 2016 +0200 +++ b/scripts/ode/module.mk Tue Aug 23 02:31:51 2016 +0200 @@ -15,11 +15,14 @@ scripts/ode/private/starting_stepsize.m scripts_ode_FCN_FILES = \ + scripts/ode/ode15i.m \ + scripts/ode/ode15s.m \ scripts/ode/ode23.m \ scripts/ode/ode45.m \ scripts/ode/odeset.m \ scripts/ode/odeget.m \ - scripts/ode/odeplot.m + scripts/ode/odeplot.m \ + scripts/ode/decic.m scripts_odedir = $(fcnfiledir)/ode
--- a/scripts/ode/ode23.m Tue Aug 23 02:13:28 2016 +0200 +++ b/scripts/ode/ode23.m Tue Aug 23 02:31:51 2016 +0200 @@ -151,6 +151,7 @@ "ode23: FUN must be a valid function handle"); endif + ## Start preprocessing, have a look which options are set in odeopts, ## check if an invalid or unused option is set. [defaults, classes, attributes] = odedefaults (numel (init),