PageRenderTime 53ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/cvs_final/octave-forge/extra/ode/inst/ode45.m

#
MATLAB | 343 lines | 176 code | 34 blank | 133 comment | 29 complexity | 3f9cc1e24b9d48f69782be2d406d1ce6 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1, GPL-3.0, LGPL-3.0
  1. % Copyright (C) 2001, 2000 Marc Compere
  2. %
  3. % This program is free software; you can redistribute it and/or modify it
  4. % under the terms of the GNU General Public License as published by
  5. % the Free Software Foundation; either version 2, or (at your option)
  6. % any later version.
  7. %
  8. % This program is distributed in the hope that it will be useful, but
  9. % WITHOUT ANY WARRANTY; without even the implied warranty of
  10. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. % General Public License for more details at www.gnu.org/copyleft/gpl.html.
  12. %
  13. % This file is intended for use with Octave.
  14. % --------------------------------------------------------------------
  15. % ode45 (v1.15) integrates a system of ordinary differential equations using
  16. % 4th & 5th order embedded formulas from Dormand & Prince or Fehlberg.
  17. %
  18. % The Fehlberg 4(5) pair is established and works well, however, the
  19. % Dormand-Prince 4(5) pair minimizes the local truncation error in the
  20. % 5th-order estimate which is what is used to step forward (local extrapolation.)
  21. % Generally it produces more accurate results and costs roughly the same
  22. % computationally. The Dormand-Prince pair is the default.
  23. %
  24. % This is a 4th-order accurate integrator therefore the local error normally
  25. % expected is O(h^5). However, because this particular implementation
  26. % uses the 5th-order estimate for x_out (i.e. local extrapolation) moving
  27. % forward with the 5th-order estimate should yield local error of O(h^6).
  28. %
  29. % The order of the RK method is the order of the local *truncation* error, d,
  30. % which is the principle error term in the portion of the Taylor series
  31. % expansion that gets dropped, or intentionally truncated. This is different
  32. % from the local error which is the difference between the estimated solution
  33. % and the actual, or true solution. The local error is used in stepsize
  34. % selection and may be approximated by the difference between two estimates of
  35. % different order, l(h) = x_(O(h+1)) - x_(O(h)). With this definition, the
  36. % local error will be as large as the error in the lower order method.
  37. % The local truncation error is within the group of terms that gets multipled
  38. % by h when solving for a solution from the general RK method. Therefore, the
  39. % order-p solution created by the RK method will be roughly accurate to O(h^(p+1))
  40. % since the local truncation error shows up in the solution as h*d, which is
  41. % h times an O(h^(p)) term, or rather O(h^(p+1)).
  42. % Summary: For an order-p accurate RK method,
  43. % - the local truncation error is O(h^p)
  44. % - the local error used for stepsize adjustment and that
  45. % is actually realized in a solution is O(h^(p+1))
  46. %
  47. % This requires 6 function evaluations per integration step.
  48. %
  49. % Both the Dormand-Prince and Fehlberg 4(5) coefficients are from a tableu in
  50. % U.M. Ascher, L.R. Petzold, Computer Methods for Ordinary Differential Equations
  51. % and Differential-Agebraic Equations, Society for Industrial and Applied Mathematics
  52. % (SIAM), Philadelphia, 1998
  53. %
  54. % The error estimate formula and slopes are from
  55. % Numerical Methods for Engineers, 2nd Ed., Chappra & Cannle, McGraw-Hill, 1985
  56. %
  57. % Usage:
  58. % [t_out, x_out] = ode45(FUN,tspan,x0,pair,ode_fcn_format,tol,trace,count,hmax)
  59. %
  60. % INPUTS:
  61. % FUN - String containing name of user-supplied problem description.
  62. % Call: xprime = fun(t,x) where FUN = 'fun'.
  63. % t - Time (scalar).
  64. % x - Solution column-vector.
  65. % xprime - Returned derivative COLUMN-vector; xprime(i) = dx(i)/dt.
  66. % tspan - [ tstart, tfinal ]
  67. % x0 - Initial value COLUMN-vector.
  68. % pair - flag specifying which integrator coefficients to use:
  69. % 0 --> use Dormand-Prince 4(5) pair (default)
  70. % 1 --> use Fehlberg pair 4(5) pair
  71. % ode_fcn_format - this specifies if the user-defined ode function is in
  72. % the form: xprime = fun(t,x) (ode_fcn_format=0, default)
  73. % or: xprime = fun(x,t) (ode_fcn_format=1)
  74. % Matlab's solvers comply with ode_fcn_format=0 while
  75. % Octave's lsode() and sdirk4() solvers comply with ode_fcn_format=1.
  76. % tol - The desired accuracy. (optional, default: tol = 1.e-6).
  77. % trace - If nonzero, each step is printed. (optional, default: trace = 0).
  78. % count - if nonzero, variable 'rhs_counter' is initalized, made global
  79. % and counts the number of state-dot function evaluations
  80. % 'rhs_counter' is incremented in here, not in the state-dot file
  81. % simply make 'rhs_counter' global in the file that calls ode45
  82. % hmax - limit the maximum stepsize to be less than or equal to hmax
  83. % N_est_acc_steps - an estimate of how many accepted steps there may be;
  84. % used to preallocate memory for the [t_out,x_out] solutions
  85. %
  86. % OUTPUTS:
  87. % t_out - array of discretized times points (an Nsteps_acc by 1 column-vector).
  88. % x_out - solution, one solution row-vector per t_out-value (an Nsteps_acc by Nstates matrix)
  89. % Nsteps_acc - total number of accepted integration steps + 1
  90. % Nsteps_rej - total number of rejected integration steps
  91. %
  92. % The result can be displayed by: plot(t_out, x_out).
  93. %
  94. % The following relationship should hold after a completed run:
  95. % rhs_counter == (Nsteps_acc-1+Nsteps_rej)*6+1
  96. %
  97. % Marc Compere
  98. % CompereM@asme.org
  99. % created : 06 October 1999
  100. % modified: 03 July 2001
  101. function [tout,xout,Nsteps_acc,Nsteps_rej] = ode45(FUN,tspan,x0,pair,ode_fcn_format,tol,trace,count,hmax,N_est_acc_steps)
  102. if nargin < 10, N_est_acc_steps = (tspan(2)-tspan(1))*1e3; end
  103. if nargin < 9, hmax = (tspan(2) - tspan(1))/2.5; end
  104. if nargin < 8, count = 0; end
  105. if nargin < 7, trace = 0; end
  106. if nargin < 6, tol = 1.e-6; end
  107. if nargin < 5, ode_fcn_format = 0; end
  108. if nargin < 4, pair = 0; end
  109. pow = 1/6; % see p.91 in the Ascher & Petzold reference for more infomation.
  110. if (pair==0),
  111. % Use the Dormand-Prince 4(5) coefficients:
  112. a_(1,1)=0;
  113. a_(2,1)=1/5;
  114. a_(3,1)=3/40; a_(3,2)=9/40;
  115. a_(4,1)=44/45; a_(4,2)=-56/15; a_(4,3)=32/9;
  116. a_(5,1)=19372/6561; a_(5,2)=-25360/2187; a_(5,3)=64448/6561; a_(5,4)=-212/729;
  117. a_(6,1)=9017/3168; a_(6,2)=-355/33; a_(6,3)=46732/5247; a_(6,4)=49/176; a_(6,5)=-5103/18656;
  118. a_(7,1)=35/384; a_(7,2)=0; a_(7,3)=500/1113; a_(7,4)=125/192; a_(7,5)=-2187/6784; a_(7,6)=11/84;
  119. % 4th order b-coefficients
  120. b4_(1,1)=5179/57600; b4_(2,1)=0; b4_(3,1)=7571/16695; b4_(4,1)=393/640; b4_(5,1)=-92097/339200; b4_(6,1)=187/2100; b4_(7,1)=1/40;
  121. % 5th order b-coefficients
  122. b5_(1,1)=35/384; b5_(2,1)=0; b5_(3,1)=500/1113; b5_(4,1)=125/192; b5_(5,1)=-2187/6784; b5_(6,1)=11/84; b5_(7,1)=0;
  123. for i=1:7,
  124. c_(i)=sum(a_(i,:));
  125. end
  126. else, % pair==1 so use the Fehlberg 4(5) coefficients:
  127. a_(1,1)=0;
  128. a_(2,1)=1/4;
  129. a_(3,1)=3/32; a_(3,2)=9/32;
  130. a_(4,1)=1932/2197; a_(4,2)=-7200/2197; a_(4,3)=7296/2197;
  131. a_(5,1)=439/216; a_(5,2)=-8; a_(5,3)=3680/513; a_(5,4)=-845/4104;
  132. a_(6,1)=-8/27; a_(6,2)=2; a_(6,3)=-3544/2565; a_(6,4)=1859/4104; a_(6,5)=-11/40;
  133. % 4th order b-coefficients (guaranteed to be a column vector)
  134. b4_(1,1)=25/216; b4_(2,1)=0; b4_(3,1)=1408/2565; b4_(4,1)=2197/4104; b4_(5,1)=-1/5;
  135. % 5th order b-coefficients (also guaranteed to be a column vector)
  136. b5_(1,1)=16/135; b5_(2,1)=0; b5_(3,1)=6656/12825; b5_(4,1)=28561/56430; b5_(5,1)=-9/50; b5_(6,1)=2/55;
  137. for i=1:6,
  138. c_(i)=sum(a_(i,:));
  139. end
  140. end % if (pair==0)
  141. % Initialization
  142. t0 = tspan(1);
  143. tfinal = tspan(2);
  144. t = t0;
  145. hmin = (tfinal - t)/1e20;
  146. h = min((tfinal - t)/100,hmax); % initial step size guess
  147. x = x0(:); % ensure x is a column vector
  148. Nstates = size(x,1);
  149. tout = zeros(N_est_acc_steps,1); % preallocating memory is not only faster but, for long
  150. xout = zeros(N_est_acc_steps,Nstates); % simulations, prevents disappointing surprises later
  151. Nsteps_rej = 0;
  152. Nsteps_acc = 1;
  153. tout(Nsteps_acc) = t; % first output time
  154. xout(Nsteps_acc,:) = x.'; % first output solution = IC's
  155. if count==1,
  156. global rhs_counter
  157. if ~exist('rhs_counter'),rhs_counter=0; end
  158. end % if count
  159. if trace
  160. clc, t, h %, x
  161. end
  162. if (pair==0),
  163. k_ = x*zeros(1,7); % k_ needs to be initialized as an Nx7 matrix where N=number of rows in x
  164. % (just for speed so octave doesn't need to allocate more memory at each stage value)
  165. % Compute the first stage prior to the main loop. This is part of the Dormand-Prince pair caveat.
  166. % Normally, during the main loop the last stage for x(k) is the first stage for computing x(k+1).
  167. % So, the very first integration step requires 7 function evaluations, then each subsequent step
  168. % 6 function evaluations because the first stage is simply assigned from the last step's last stage.
  169. % note: you can see this by the last element in c_ is 1.0, thus t+c_(7)*h = t+h, ergo, the next step.
  170. if (ode_fcn_format==0), % (default)
  171. k_(:,1)=feval(FUN,t,x); % first stage
  172. else, % ode_fcn_format==1
  173. k_(:,1)=feval(FUN,x,t);
  174. end % if (ode_fcn_format==1)
  175. % increment rhs_counter
  176. if (count==1), rhs_counter = rhs_counter + 1; end
  177. % The main loop using Dormand-Prince 4(5) pair
  178. while (t < tfinal) & (h >= hmin),
  179. if t + h > tfinal, h = tfinal - t; end
  180. % Compute the slopes by computing the k(:,j+1)'th column based on the previous k(:,1:j) columns
  181. % notes: k_ needs to end up as an Nxs, a_ is 7x6, which is s by (s-1),
  182. % s is the number of intermediate RK stages on [t (t+h)] (Dormand-Prince has s=7 stages)
  183. if (ode_fcn_format==0), % (default)
  184. for j = 1:6,
  185. k_(:,j+1) = feval(FUN, t+c_(j+1)*h, x+h*k_(:,1:j)*a_(j+1,1:j)');
  186. end
  187. else, % ode_fcn_format==1
  188. for j = 1:6,
  189. k_(:,j+1) = feval(FUN, x+h*k_(:,1:j)*a_(j+1,1:j)', t+c_(j+1)*h);
  190. end
  191. end % if (ode_fcn_format==1)
  192. % increment rhs_counter
  193. if (count==1), rhs_counter = rhs_counter + 6; end
  194. % compute the 4th order estimate
  195. x4=x + h* (k_*b4_); % k_ is Nxs (or Nx7) and b4_ is a 7x1
  196. % compute the 5th order estimate
  197. x5=x + h*(k_*b5_); % k_ is Nxs (or Nx7) and b5_ is a 7x1
  198. % estimate the local truncation error
  199. gamma1 = x5 - x4;
  200. % Estimate the error and the acceptable error
  201. delta = norm(gamma1,'inf'); % actual error
  202. tau = tol*max(norm(x,'inf'),1.0); % allowable error
  203. % Update the solution only if the error is acceptable
  204. if (delta<=tau),
  205. t = t + h;
  206. x = x5; % <-- using the higher order estimate is called 'local extrapolation'
  207. Nsteps_acc = Nsteps_acc + 1;
  208. tout(Nsteps_acc) = t;
  209. xout(Nsteps_acc,:) = x.';
  210. % Assign the last stage for x(k) as the first stage for computing x(k+1).
  211. % This is part of the Dormand-Prince pair caveat.
  212. % k_(:,7) has already been computed, so use it instead of recomputing it
  213. % again as k_(:,1) during the next step.
  214. k_(:,1)=k_(:,7);
  215. else, % unacceptable integration step
  216. Nsteps_rej = Nsteps_rej + 1;
  217. end % if (delta<=tau)
  218. if trace
  219. home, t, h, Nsteps_acc, Nsteps_rej
  220. end % if trace
  221. % Update the step size
  222. if (delta==0.0),
  223. delta = 1e-16;
  224. end % if (delta==0.0)
  225. h = min(hmax, 0.8*h*(tau/delta)^pow);
  226. end % while (t < tfinal) & (h >= hmin)
  227. else, % pair==1 --> use RK-Fehlberg 4(5) pair
  228. k_ = x*zeros(1,6); % k_ needs to be initialized as an Nx6 matrix where N=number of rows in x
  229. % (just for speed so octave doesn't need to allocate more memory at each stage value)
  230. % The main loop using Dormand-Prince 4(5) pair
  231. while (t < tfinal) & (h >= hmin),
  232. if t + h > tfinal, h = tfinal - t; end
  233. % Compute the slopes by computing the k(:,j+1)'th column based on the previous k(:,1:j) columns
  234. % notes: k_ needs to end up as an Nx6, a_ is 6x5, which is s by (s-1), (RK-Fehlberg has s=6 stages)
  235. % s is the number of intermediate RK stages on [t (t+h)]
  236. if (ode_fcn_format==0), % (default)
  237. k_(:,1)=feval(FUN,t,x); % first stage
  238. else, % ode_fcn_format==1
  239. k_(:,1)=feval(FUN,x,t);
  240. end % if (ode_fcn_format==1)
  241. if (ode_fcn_format==0), % (default)
  242. for j = 1:5,
  243. k_(:,j+1) = feval(FUN, t+c_(j+1)*h, x+h*k_(:,1:j)*a_(j+1,1:j)');
  244. end
  245. else, % ode_fcn_format==1
  246. for j = 1:5,
  247. k_(:,j+1) = feval(FUN, x+h*k_(:,1:j)*a_(j+1,1:j)', t+c_(j+1)*h);
  248. end
  249. end % if (ode_fcn_format==1)
  250. % increment rhs_counter
  251. if (count==1), rhs_counter = rhs_counter + 6; end
  252. % compute the 4th order estimate
  253. x4=x + h* (k_(:,1:5)*b4_); % k_(:,1:5) is an Nx5 and b4_ is a 5x1
  254. % compute the 5th order estimate
  255. x5=x + h*(k_*b5_); % k_ is the same as k_(:,1:6) and is an Nx6 and b5_ is a 6x1
  256. % estimate the local truncation error
  257. gamma1 = x5 - x4;
  258. % Estimate the error and the acceptable error
  259. delta = norm(gamma1,'inf'); % actual error
  260. tau = tol*max(norm(x,'inf'),1.0); % allowable error
  261. % Update the solution only if the error is acceptable
  262. if (delta<=tau),
  263. t = t + h;
  264. x = x5; % <-- using the higher order estimate is called 'local extrapolation'
  265. Nsteps_acc = Nsteps_acc + 1;
  266. tout(Nsteps_acc) = t;
  267. xout(Nsteps_acc,:) = x.';
  268. else, % unacceptable integration step
  269. Nsteps_rej = Nsteps_rej + 1;
  270. end % if (delta<=tau)
  271. if trace
  272. home, t, h, Nsteps_acc, Nsteps_rej
  273. end % if trace
  274. % Update the step size
  275. if (delta==0.0),
  276. delta = 1e-16;
  277. end % if (delta==0.0)
  278. h = min(hmax, 0.8*h*(tau/delta)^pow);
  279. end % while (t < tfinal) & (h >= hmin)
  280. end % if (pair==0),
  281. % if necessary, trim outputs
  282. if (Nsteps_acc<N_est_acc_steps),
  283. tout = tout(1:Nsteps_acc);
  284. xout = xout(1:Nsteps_acc,:);
  285. end % if (Nsteps_acc<N_est_acc_steps)
  286. if (t < tfinal),
  287. disp('Step size grew too small.')
  288. t, h, Nsteps_acc, Nsteps_rej
  289. end