1+ %==========================================================================
2+ %
3+ % odeRK Fixed-step ODE solver using the Runge-Kutta methods.
4+ %
5+ % [t,y] = odeRK(f,[t0,tf],y0,h)
6+ % [t,y] = odeRK(f,{t0,C},y0,h)
7+ % [t,y] = odeRK(__,method)
8+ % [t,y] = odeRK(__,method,wb)
9+ %
10+ % See also odeAB, odeABM.
11+ %
12+ % Copyright © 2021 Tamas Kis
13+ % Last Update: 2022-06-04
14+ % Website: https://tamaskis.github.io
15+ % Contact: tamas.a.kis@outlook.com
16+ %
17+ % TOOLBOX DOCUMENTATION:
18+ % https://tamaskis.github.io/ODE_Solver_Toolbox-MATLAB/
19+ %
20+ % TECHNICAL DOCUMENTATION:
21+ % https://tamaskis.github.io/documentation/Fixed_Step_ODE_Solvers.pdf
22+ %
23+ %--------------------------------------------------------------------------
24+ %
25+ % ------
26+ % INPUT:
27+ % ------
28+ % f - (1×1 function_handle) dy/dt = f(t,y) --> multivariate,
29+ % vector-valued function (f : ℝ×ℝᵖ → ℝᵖ) defining ODE
30+ % I - defines interval over which to solve the ODE, 2 options:
31+ % --> [t0,tf] - (1×2 double) initial and final times
32+ % --> {t0,C} - (1×2 cell) initial time, t₀, and function
33+ % handle for condition function, C(t,y)
34+ % (C : ℝ×ℝᵖ → 𝔹)
35+ % y0 - (p×1 double) initial condition, y₀ = y(t₀)
36+ % h - (1×1 double) step size
37+ % method - (char) (OPTIONAL) Runge-Kutta method --> 'Euler', 'RK2',
38+ % 'RK2 Heun', 'RK2 Ralston', 'RK3', 'RK3 Heun', 'RK3 Ralston',
39+ % 'SSPRK3', 'RK4', 'RK4 Ralston', 'RK4 3/8' (defaults to 'RK4')
40+ % wb - (1×1 logical or char) (OPTIONAL) waitbar parameters
41+ % --> input as "true" if you want waitbar with default
42+ % message displayed
43+ % --> input as a char array storing a message if you want a
44+ % custom message displayed on the waitbar
45+ %
46+ % -------
47+ % OUTPUT:
48+ % -------
49+ % t - ((N+1)×1 double) time vector
50+ % y - ((N+1)×p double) matrix storing time history of state vector
51+ %
52+ % -----
53+ % NOTE:
54+ % -----
55+ % --> p = dimension of state vector (for the scalar case, p = 1)
56+ % --> N+1 = length of time vector
57+ % --> The ith row of "y" is the TRANSPOSE of the state vector (i.e. the
58+ % solution corresponding to the ith time in "t"). This convention is
59+ % chosen to match the convention used by MATLAB's ODE suite.
60+ %
61+ %==========================================================================
62+ function [t,y] = ode(f,I,y0,h,method,wb)
63+
64+ % --------------------
65+ % Time detection mode.
66+ % --------------------
67+
68+ if ~iscell(I)
69+
70+ % extracts initial and final times
71+ t0 = I(1);
72+ tf = I(2);
73+
74+ % makes step size negative if t0 > tf
75+ if (t0 > tf)
76+ h = -h;
77+ end
78+
79+ % defines condition function
80+ C = @(t,y) t <= tf;
81+
82+ % indicates that final time is known
83+ final_time_known = true;
84+
85+ % number of subintervals between sample times
86+ N = ceil((tf-t0)/h);
87+
88+ % turns waitbar on with custom message
89+ if (nargin == 6) && ischar(wb)
90+ display_waitbar = true;
91+ [wb,prop] = initialize_waitbar(msg);
92+
93+ % turns waitbar on with default message
94+ elseif (nargin == 6) && wb
95+ display_waitbar = true;
96+ [wb,prop] = initialize_waitbar('Solving ODE...');
97+
98+ % turns waitbar off otherwise
99+ else
100+ display_waitbar = false;
101+
102+ end
103+
104+ % ---------------------
105+ % Event detection mode.
106+ % ---------------------
107+
108+ else
109+
110+ % extracts initial time and condition function
111+ t0 = I{1};
112+ C = I{2};
113+
114+ % indicates that final time is unknown
115+ final_time_known = false;
116+
117+ end
118+
119+ % -------------------
120+ % Integration method.
121+ % -------------------
122+
123+ % defaults integration method to RK4
124+ if (nargin < 5) || isempty(method)
125+ method = 'RK4';
126+ end
127+
128+ % sets propagation function for single-step methods
129+ if strcmpi(method,'RK1_euler')
130+ propagate = @(t,y) RK1_euler(f,t,y,h);
131+ elseif strcmpi(method,'RK2')
132+ propagate = @(t,y) RK2(f,t,y,h);
133+ elseif strcmpi(method,'RK2_heun')
134+ propagate = @(t,y) RK2_heun(f,t,y,h);
135+ elseif strcmpi(method,'RK2_ralston')
136+ propagate = @(t,y) RK2_ralston(f,t,y,h);
137+ elseif strcmpi(method,'RK3')
138+ propagate = @(t,y) RK3(f,t,y,h);
139+ elseif strcmpi(method,'RK3_heun')
140+ propagate = @(t,y) RK3_heun(f,t,y,h);
141+ elseif strcmpi(method,'RK3_ralston')
142+ propagate = @(t,y) RK3_ralston(f,t,y,h);
143+ elseif strcmpi(method,'SSPRK3')
144+ propagate = @(t,y) SSPRK3(f,t,y,h);
145+ elseif strcmpi(method,'RK4')
146+ propagate = @(t,y) RK4(f,t,y,h);
147+ elseif strcmpi(method,'RK4_38')
148+ propagate = @(t,y) RK4_38(f,t,y,h);
149+ elseif strcmpi(method,'RK4_ralston')
150+ propagate = @(t,y) RK4_ralston(f,t,y,h);
151+ end
152+
153+ % sets propagation function for multi-step predictor methods
154+ if strcmpi(method,'AB2')
155+ propagate = @(t,F) AB2(f,t,F,h);
156+ elseif strcmpi(method,'AB3')
157+ propagate = @(t,F) AB3(f,t,F,h);
158+ elseif strcmpi(method,'AB4')
159+ propagate = @(t,F) AB5(f,t,F,h);
160+ elseif strcmpi(method,'AB3')
161+ propagate = @(t,F) AB3(f,t,F,h);
162+ elseif strcmpi(method,'AB3')
163+ propagate = @(t,F) AB3(f,t,F,h);
164+ elseif strcmpi(method,'AB3')
165+ propagate = @(t,F) AB3(f,t,F,h);
166+ elseif strcmpi(method,'AB3')
167+ propagate = @(t,F) AB3(f,t,F,h);
168+ elseif strcmpi(method,'AB3')
169+ propagate = @(t,F) AB3(f,t,F,h);
170+ end
171+
172+ % determines if the method is a single-step method
173+ if strcmpi(method(1:2),'RK')
174+ single_step = true;
175+ else
176+ single_step = false;
177+ end
178+
179+ % determines order for multistep methods
180+ if ~single_step
181+ m = str2double(method(end));
182+ end
183+
184+ % --------------------------------------------------
185+ % Preallocates arrays and stores initial conditions.
186+ % --------------------------------------------------
187+
188+ % preallocates time vector and solution matrix
189+ t = zeros(10000,1);
190+ y = zeros(length(y0),length(t));
191+
192+ % stores initial conditions
193+ t(1) = t0;
194+ y(:,1) = y0;
195+
196+ % ---------------------------------------
197+ % Solves ODE (using single-step methods).
198+ % ---------------------------------------
199+
200+ if single_step
201+
202+ % state vector propagation while condition is satisfied
203+ n = 1;
204+ while C(t(n),y(:,n))
205+
206+ % expands t and y if needed
207+ if (n+1) > length(t)
208+ [t,y] = expand_solution_arrays(t,y);
209+ end
210+
211+ % state vector propagated to next sample time
212+ y(:,n+1) = propagate(t(n),y(:,n));
213+
214+ % increments time and loop index
215+ t(n+1) = t(n)+h;
216+ n = n+1;
217+
218+ % updates waitbar
219+ if final_time_known && display_waitbar
220+ prop = update_waitbar(n,N,wb,prop);
221+ end
222+
223+ end
224+
225+ % --------------------------------------
226+ % Solves ODE (using multi-step methods).
227+ % --------------------------------------
228+
229+ else
230+
231+ % time vector for first m sample times
232+ t(1:m) = (t0:h:(t0+(m-1)*h))';
233+
234+ % propagating state vector using RK4 for first "m" sample times
235+ for n = 1:m
236+ y(:,n+1) = RK4(f,t(n),y(:,n),h);
237+ end
238+
239+ % initializes F matrix (stores function evaluations for first m
240+ % sample times in first m columns, state vector at (m+1)th sample
241+ % time in (m+1)th column)
242+ F = zeros(length(y0),m+1);
243+ for n = 1:m
244+ F(:,n) = f(t(n),y(:,n));
245+ end
246+ F(:,n+1) = y(:,m+1);
247+
248+ % state vector propagation while condition is satisfied
249+ n = 1;
250+ while C(t(n),y(:,n))
251+
252+ % expands t and y if needed
253+ if (n+1) > length(t)
254+ [t,y] = expand_solution_arrays(t,y);
255+ end
256+
257+ % updates F matrix
258+ F = propagate(t(n),F);
259+
260+ % extracts/stores state vector propagated to next sample time
261+ y(:,n+1) = F(:,m+1);
262+
263+ % increments time and loop index
264+ t(n+1) = t(n)+h;
265+ n = n+1;
266+
267+ end
268+
269+ end
270+
271+ % -----------------
272+ % Final formatting.
273+ % -----------------
274+
275+ % trims arrays
276+ y = y(:,1:(n-1));
277+ t = t(1:(n-1));
278+
279+ % transposes solution matrix so it is returned in "standard form"
280+ y = y';
281+
282+ % closes waitbar
283+ if final_time_known && display_waitbar
284+ close(wb);
285+ end
286+
287+ end
0 commit comments