Matlab ode solvers: changing state and specified time

Inserting large discontinuities in your ODE in the way you suggest (and the way illustrated by @macduff) can lead to less precision and longer computation times (especially with ode45ode15s might be a better option or at least make sure that your absolute and relative tolerances are suitable). You’ve effectively produced a very stiff system. If you want to add some number to the ODEs starting at a specific time keep in mind that the solver only evaluates these equations at specific points in time of its own choosing. (Don’t be mislead by the fact that you can obtain fixed step-size outputs by specifying tspan as more than two elements – all of Matlab’s solvers are variable step-size solvers and choose their true steps based on error criteria.)

A better option is to integrate the system piecewise and append the resultant outputs from each run together:

% t = 0 to t = 10, pass parameter a = 0 to add to ODEs
a = 0;
tspan = [0 10];
[T,Y] = ode45(@(t,y)myfun(t,y,a),tspan,y_0);

% t = 10 to t = 20, pass parameter a = 10 to add to ODEs
a = 10;
[t,y] = ode45(@(t,y)myfun(t,y,a),tspan+T(end),Y(end,:));
T = [T;t(2:end)];
Y = [Y;y(2:end,:)];

% t = 20 to t = 30, pass parameter a = 20 to add to ODEs
a = 20;
[t,y] = ode45(@(t,y)myfun(t,y,a),tspan+T(end),Y(end,:));
T = [T;t(2:end)];
Y = [Y;y(2:end,:)];

The Matlab editor may complain about the array T and Y not being preallocated and/or growing, but it’s fine in this case as they’re growing in large chunks only a few times. Alternatively, if you want fixed output step-sizes, you can do this:

dt = 0.01;
T = 0:dt:30;
Y = zeros(length(T),length(y_0));

% t = 0 to t = 10, pass parameter a = 0 to add to ODEs
a = 0;
[~,Y(1:10/dt+1,:)] = ode45(@(t,y)myfun(t,y,a),T(1:10/dt+1),y_0);

% t = 10 to t = 20, pass parameter a = 10 to add to ODEs
a = 10;
[~,Y(10/dt+1:20/dt+1,:)] = ode45(@(t,y)myfun(t,y,a),T(10/dt+1:20/dt+1),Y(10/dt+1,:));

% t = 20 to t = 30, pass parameter a = 20 to add to ODEs
a = 20;
[~,Y(20/dt+1:end,:)] = ode45(@(t,y)myfun(t,y,a),T(20/dt+1:end),Y(20/dt+1,:));

One could easily convert both of the above blocks of code to more compact for loops if desired.

In both cases your ODE function myfun incorporates the parameter a this way:

function ydot = myfun(t,y,a)
    y(1) = ... % add a however you like
    ...

Leave a Comment