Solving with ODE45 in Matlab with a variable which is timestep dependent. - matlab

I've been solving a pretty simple set of ODEs which, for a particular case, produce results as I'd expect. The required output is a graph of the 'population' which is just a manipulation of the results output by ODE.
The issue is that I have some variable del which must be slowly incremented as time goes on (slowing being on the order of del_increment/time_increment << Omega^2, where Omega is set to 1). I can happily produce what I've been calling the 'static' case where I have a for loop incrementing del outside of ode45, however this is really just the solution for a static del value over a time period.
I'm struggling to see how I can have ODE45 increment del values [between a max and min] within the function as I have the restriction of small changes of del with time. This would show one plot, displaying small oscillations at zero population and a gradual increase to population =1, as opposed to my current multiple plots for each particular del value.
I've given what I've written below, and I hope this explanation makes sense.
del=-10:0.1:10; %start at large minus, with some small increment
Omega=1;
tmax=10; %integration time
for ret=1:length(del)
y=[0 0 1]; % starting conditions
[t,y]=ode45(#(t,y) psfour(t,y,del(ret)),[0 tmax],y);
plot(t,y(:,1)) % plot of one of the solutions
axis([0 max(t) -1 1])
p_11=0.5*(1+y(:,3)); %calculation of the population of an energy level
figure(2)
plot(t,p_11)
axis([0 max(t) 0 1])
pause(0.05) % asthetic pause so I can see results
end
and my function
function dydt = react(t,y,del)
dydt = zeros(size(y));
Omega=1;
A = y(1);
B = y(2);
C = y(3);
dydt(1) = del*B;
dydt(2) = -del*A + Omega*C;
dydt(3) = -Omega*B;
% del=del + Omega*t;
end

Related

how to replace the ode45 method with the runge-kutta in this matlab?

I tried everything and looked everywhere but can't find any solution for my question.
clc
clear all
%% Solving the Ordinary Differential Equation
G = 6.67408e-11; %Gravitational constant
M = 10; %Mass of the fixed object
r = 1; %Distance between the objects
tspan = [0 100000]; %Time Progression from 0 to 100000s
conditions = [1;0]; %y0= 1m apart, v0=0 m/s
F=#(t,y)var_r(y,G,M,r);
[t,y]=ode45(F,tspan,conditions); %ODE solver algorithm
%%part1: Plotting the Graph
% plot(t,y(:,1)); %Plotting the Graph
% xlabel('time (s)')
% ylabel('distance (m)')
%% part2: Animation of Results
plot(0,0,'b.','MarkerSize', 40);
hold on %to keep the first graph
for i=1:length(t)
k = plot(y(i,1),0,'r.','MarkerSize', 12);
pause(0.05);
axis([-1 2 -2 2]) %Defining the Axis
xlabel('X-axis') %X-Axis Label
ylabel('Y-axis') %Y-Axis Label
delete(k)
end
function yd=var_r(y,G,M,r) %function of variable r
g = (G*M)/(r + y(1))^2;
yd = [y(2); -g];
end
this is the code where I'm trying to replace the ode45 with the runge kutta method but its giving me errors. my runge kutta function:
function y = Runge_Kutta(f,x0,xf,y0,h)
n= (xf-x0)/h;
y=zeros(n+1,1);
x=(x0:h:xf);
y(1) = y0;
for i=1:n
k1 = f(x(i),y(i));
k2= f(x(i)+ h/2 , y(i) +h*(k1)/2);
y(i+1) = y(i)+(h*k2);
end
plot(x,y,'-.M')
legend('RKM')
title ('solution of y(x)');
xlabel('x');
ylabel('y(x)')
hold on
end
Before converting your ode45( ) solution to manually written RK scheme, it doesn't even look like your ode45( ) solution is correct. It appears you have a gravitational problem set up where the initial velocity is 0 so a small object will simply fall into a large mass M on a line (rectilinear motion), and that is why you have scalar position and velocity.
Going with this assumption, r is something you should be calculating on the fly, not using as a fixed input to the derivative function. E.g., I would have expected something like this:
F=#(t,y)var_r(y,G,M); % get rid of r
:
function yd=var_r(y,G,M) % function of current position y(1) and velocity y(2)
g = (G*M)/y(1)^2; % gravity accel based on current position
yd = [y(2); -g]; % assumes y(1) is positive, so acceleration is negative
end
The small object must start with a positive initial position for the derivative code to be valid as you have it written. As the small object falls into the large mass M, the above will only hold until it hits the surface or atmosphere of M. Or if you model M as a point mass, then this scheme will become increasingly difficult to integrate correctly because the acceleration becomes large without bound as the small mass gets very close to the point mass M. You would definitely need a variable step size approach in this case. The solution becomes invalid if it goes "through" mass M. In fact, once the speed gets too large the whole setup becomes invalid because of relativistic effects.
Maybe you could explain in more detail if your system is supposed to be set up this way, and what the purpose of the integration is. If it is really supposed to be a 2D or 3D problem, then more states need to be added.
For your manual Runge-Kutta code, you completely forgot to integrate the velocity so this is going to fail miserably. You need to carry a 2-element state from step to step, not a scalar as you are currently doing. E.g., something like this:
y=zeros(2,n+1); % 2-element state as columns of the y variable
x=(x0:h:xf);
y(:,1) = y0; % initial state is the first 2-element column
% change all the scalar y(i) to column y(:,i)
for i=1:n
k1 = f(x(i),y(:,i));
k2= f(x(i)+ h/2 , y(:,i) +h*(k1)/2);
y(:,i+1) = y(:,i)+(h*k2);
end
plot(x,y(1,:),'-.M') % plot the position part of the solution
This is all assuming the f that gets passed in is the same F you have in your original code.
y(1) is the first scalar element in the data structure of y (this counts in column-first order). You want to generate in y a list of column vectors, as your ODE is a system with state dimension 2. Thus you need to generate y with that format, y=zeros(length(x0),n+1); and then address the list entries as matrix columns y(:,1)=x0 and the same modification in every place where you extract or assign a list entry.
Matlab introduce various short-cuts that, if used consequently, lead to contradictions (I think the script-hater rant (german) is still valid in large parts). Essentially, unlike in other systems, Matlab gives direct access to the underlying data structure of matrices. y(k) is the element of the underlying flat array (that is interpreted column-first in Matlab like in Fortran, unlike, e.g., Numpy where it is row-first).
Only the two-index access is to the matrix with its dimensions. So y(:,k) is the k-th matrix column and y(k,:) the k-th matrix row. The single-index access is nice for row or column vectors, but leads immediately to problems when collecting such vectors in lists, as these lists are automatically matrices.

How to speed up the plotting of graphs using pause()?

I understand that when plotting an equation for x iterations that when you use a pause with a decimal number you can speed up the time it takes to go from one iteration to the next. My question is there a way to speed it up even more? Basically I am running a upwind 1D advection equation and my plot is doing pretty slow even when I put a pause of say 0.0001. Any advice on making the speed of this program increase for plotting or would I just need to let it run its course.
Here is the code I am using:
clear;
clc;
%Set initial values
xmin=0;
xmax=1;
N=101; %Amount of segments
dt= 0.0001; % Time step
t=0; % t initial
tmax=2; % Run this test until t=2
u=1; %Velocity
dx = (xmax - xmin)/100 %finding delta x from the given information
x =xmin-dx : dx : xmax+dx; %setting the x values that will be plugged in
h0= exp(-(x- 0.5).^2/0.01); %Initial gaussian profile for t=0
h = h0;
hp1=h0;
nsteps =tmax/dt; % total number of steps taken
for n=1 : nsteps
h(1)=h(end-2); %Periodic B.C
h(end)=h(2);
for i =2 : N+1
if u>0
hp1(i) = h(i) - u*dt/dx *( h(i)-h(i-1)); %Loop to solve the FOU
elseif u<0
hp1(i) = h(i) - u*dt/dx*(h(i+1)-h(i)); %downwind
end
end
t=t+dt; %Increase t after each iteration
h= hp1; %have the new hp1 equal to h for the next step
initial= exp(-(x- 0.5).^2/0.01); % The initial plot when t =0
%hold on
plot(x,initial,'*') %plot initial vs moving
plot(x,h,'o-')
pause(0.0001);
%hold off
figure(2)
plot(x,initial) %plot end value
end
Isn't this "speedup" due to pause() flushing the graphic event buffer like drawnow, but apparently doing it faster? So it is not the length of the pause doing any work (in fact, I don't think the resolution is in the millisecond range on many machines) but just the command itself.
The thing really slowing down your code is those for loops. You should try to change the code to calculate the segments in parallel instead.

Performance analysis of series summation in Matlab

I'm writing a Matlab program to compute pi through the summation of series
A = Sum of a_i from i=1 to N
where
pi/4 = 1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 + 1/13 ...
To compute pi through the series summation, the suggested approach is to set
a_i = (-1)^(i+1)/(2i-1)
In order to do this, I wrote the program below
n=100;
f=[];
for jj=1:n
ii=1:jj;
f=[f 4*sum( ((-1).^(ii+1))./(2.*ii-1) )];
end;
hold on
plot(f)
title('Computing of \pi using a finite sum')
xlabel('Number of summations')
ylabel('Estimated value of \pi')
plot(1:size(f,2),ones(size(f))*pi)
This program shows that the series approximation is somewhat accurate near N=80.
I am now attempting to adjust my program so that the y-axis displays total calculation time T_N and the x-axis displays N (the number of summations). The total calculation time T_N should increase as N increases. Ideally, I am looking to have the graph display something close to a linear relationship between T(N) and N
To do this, I have adjusted my original program as follows
n=100;
f=[];
tic
for jj=1:n
ii=1:jj;
f=[f 4*sum( ((-1).^(ii+1))./(2.*ii-1) )];
end;
hold on
plot(f)
title('Time it takes to sum \pi using N summations')
xlabel('Number of summations (N)')
ylabel('Total Time (T_N)')
plot(1:size(f,2),toc)
slope = polyfit(1:size(f,2),toc,1);
This looks wrong. I must have incorrectly applied the built-in timing functions in Matlab (tic and toc). So, I am going to analyze my code and ask two questions -
How could I adjust my code above so that the y-axis correctly displays the total calculation time per the summation N? It looks like I did something wrong in plot(1:size(f,2),toc).
After I get the y-axis to display the correct total calculation time (T_N), I should be able to use the polyfit command to find the slope of T(N)/N. This will give me a linear relationship between T(N) and N. I could then use the value of slope = polyfit(1:size(f,2),toc,1) to compute
t_N = a + b*N
where t_N is computed for every value of N and b is the slope calculated through the polyfit command.
I think that I should be able to find the values of a and b after I correctly display the y-axis and correctly reference the polyfit command.
There are several things that can be improved in your code:
f should be preallocated, so as not to waste time in repeatedly assigning memory.
tic should be called within the loop, in order to restart the stopwatch timer.
When you call toc you get the current time from the last tic. The time spent should be stored in a vector (also preallocated).
Since the computations you want to time are very fast, measuring the time they take is very unrealiable. The computations should be repeated many times, so the measured time is larger and you get better accuracy. Even better would be to use timeit (see below).
You cannot plot the time and the results in the same figure, because the scales are too different.
The code incorporating these changes is:
n = 100;
f = NaN(1,n); % preallocate
times = NaN(1,n); % preallocate
repeat_factor = 1e4; % repeat computations for better time accuracy
for jj=1:n
tic % initiallize time count
for repeat = 1:repeat_factor % repeat many times for better time accuracy
ii=1:jj;
f(jj) = 4*sum( ((-1).^(ii+1))./(2.*ii-1) ); % store value
end
times(jj) = toc; % store time
end
times = times / repeat_factor; % divide by repeat factor
plot(f)
title('Time it takes to sum \pi using N summations')
xlabel('Number of summations (N)')
ylabel('Total Time (T_N)')
figure % new figure for time
plot(1:size(f,2), times)
p = polyfit(1:size(f,2),times,1);
slope = p(1);
Using timeit for measuring the time will probably give improved accuracy (but not very good because, as mentioned above, the computations you want to time are very fast). To use timeit you need to define a function with the code to be timed. The simplest way is to use an anonymous function without input arguments. See code below.
n = 100;
f = NaN(1,n); % preallocate
times = NaN(1,n); % preallocate
for jj=1:n
ii=1:jj;
fun = #() 4*sum( ((-1).^(ii+1))./(2.*ii-1) );
f(jj) = fun(); % store value
times(jj) = timeit(fun); % measure and store time
end
plot(f)
title('Time it takes to sum \pi using N summations')
xlabel('Number of summations (N)')
ylabel('Total Time (T_N)')
figure % new figure for time
plot(1:size(f,2), times)
p = polyfit(1:size(f,2),times,1);
slope = p(1);
If I understand your problem correctly, I think there are two different issues here. First, you plot your result function then the elapsed time which is several orders of magnitude smaller than pi:
hold on
plot(f) % <---- Comment me out!
...stuff...
plot(1:size(f,2),toc)
Secondly, you need to store the execution time of each pass of the loop:
n=100;
f=[];
telapsed = zeros(1,n);
tic
for jj=1:n
ii=1:jj;
f=[f 4*sum( ((-1).^(ii+1))./(2.*ii-1) )];
telapsed(jj) = toc;
end
hold on
% plot(f)
title('Time it takes to sum \pi using N summations')
xlabel('Number of summations (N)')
ylabel('Total Time (T_N)')
plot(1:n,telapsed)
slope = polyfit(1:n,telapsed,1);
Note the new polyfit expression for slope of the execution time. Does that help?

Optimization of motion law using Opti-toolbox in Matlab

I have a question concerning the numerical derivation of an ideal motion law using the Opti-toolbox in Matlab.
There are many constraints to follow, but the main principle of the target function looks like this:
This function is periodic, but only 1 period is used.
All variables are dimensionless and the number of points is n (which is an odd number).
At 0 and 1, the function yields 0.
In the middle, at (n+1)/2, the function yields e.g. 0.3.
The function needs to monotonically increase in the first halve, and decrease in the second half.
The velocity is needs to be between -0.8 and 1.
The peak acceleration will be limited when an other cost function is used.
I want to have an as low as possible peak value for the acceleration. Later this cost function will be changed.
My attempt using the Opti-toolbox looks like this:
clear all
close all
%n is number of grid points, only odd numbers allowed!
n=11;
% parameters
tijd=[0:(1/(n-1)):1];
velmin=-0.8;
velmax=1;
accmax=100;
accmin=-100;
%First order derivative:
%Last value doesn't contribute, because function is periodic
%Implementation using FE: deltax=delta_i+1-delta_i-1/2*deltax
%With deltax=(1/(n-1))
A_1=zeros(n-1,n);
A_1(2:n-1,1:n-2)=A_1(2:n-1,1:n-2)-eye(n-2);
A_1(1:n-2,2:n-1)=A_1(1:n-2,2:n-1)+eye(n-2);
A_1(1,n-1)=-1;
A_1(n-1,1)=1;
A_1=A_1*(n-1)/2;
%Lower bound and upper bound + monotonically in and decreasing
lb_111=0.1*ones((n-1)/2,1);
ub_111=velmax*ones((n-1)/2,1);
lb_112=velmin*ones((n-1)/2,1);
ub_112=-0.1*ones((n-1)/2,1);
ub_1=[ub_111; ub_112];
lb_1=[lb_111; lb_112];
%2nd order derivative, same as before.
A_12=zeros(n-1,n);
A_12(2:n-1,1:n-2)=A_12(2:n-1,1:n-2)+eye(n-2);
A_12(1:n-2,2:n-1)=A_12(1:n-2,2:n-1)+eye(n-2);
A_12(1:n-1,1:n-1)=A_12(1:n-1,1:n-1)-2*eye(n-1);
A_12(1,n-1)=1;
A_12(n-1,1)=1;
A_12=A_12*((n-1)^2);
lb_12=accmin*ones(n-1,1);
ub_12=accmax*ones(n-1,1);
%First and last values are 0.
A_2=zeros(2,n);
A_2(1,1)=1;
A_2(2,n)=1;
lb_2=zeros(2,1);
ub_2=zeros(2,1);
%Middle value is known
A_4=zeros(1,n);
A_4(1,(n+1)/2)=1;
lb_4=zeros(1,1);
lb_4(1)=0.3;
ub_4=zeros(1,1);
ub_4(1)=0.3;
%Main derivatives
%1st
af_1=#(x) A_1*x;
%2nd
af_2=#(x) A_12*x;
%Will be needed later, not relevant.
% a_3 = zeros(n-1,n);
% a_3(1:(n-1)/2,(n+1)/2:n-1)=a_3(1:(n-1)/2,(n+1)/2:n-1)+eye((n-1)/2);
% a_3((n+1)/2:n-1,1:(n-1)/2)=a_3((n+1)/2:n-1,1:(n-1)/2)+eye((n-1)/2);
% a_fase=#(x) [a_3*x; x((n+1)/2)];
%Put all linear constraints together
A = [A_4;A_1;A_12;A_2];
lb = [lb_4;lb_1;lb_12;lb_2];
ub = [ub_4;ub_1;ub_12;ub_2];
%Cost function
f = #(x) norm(af_2(x),inf);
%Non linear function later
%nlcon = #(x) [norm(g(x),inf)];
%cl = [-15];
%cu = [15];
%Estimate x0
temp = fliplr(tijd);
x_0 = [tijd(1:(n+1)/2) temp(((n+1)/2+1):end)];
%Options in solver
opts = optiset('solver','ipopt','display','iter');
Opt = opti('fun',f,'lin',A,lb,ub,'x0',x_0,'options',opts);
%Extract solution
delta1 = solve(Opt);
%delta2=a_fase(delta1);
vel1=af_1(delta1);
%vel2=af_1(delta2);
acc1=af_2(delta1);
%acc2=af_2(delta2);
%Tas=g(delta1);
%%%%%%%%%%%%%%%%
%PLOTS
%%%%%%%%%%%%%%%%
figure
subplot(2,2,1);
plot(tijd,delta1)
title('positie')
subplot(2,2,2);
plot(tijd,[vel1;vel1(1)])
title('snelheid')
subplot(2,2,3);
plot(tijd,[acc1;acc1(1)])
title('acceleratie')
subplot(2,2,4);
%plot(tijd,Tas)
title('Askoppel')
But this yields in an infeasible problem.
Does someone has an idea what I'm doing wrong?
See comment:
Result:
Motion result

Using event function in matlab ode45 for multi-dimensional state vector

I have a set of odes written in matrix form as $X' = AX$; I also have a desired value of the states $X_des$. $X$ is a five dimensional vector. I want to stop the integration after all the states reach their desired values (or atleast close to them by $1e{-3}$). How do I use event function in matlab to do this? (All the help I have seen are about 1 dimensional states)
PS: I know for sure that all the states approach their desired values after long time. I just want to stop the integration once they are $1e{-3}$ within the desired values.
First, I presume that you're aware that you can use the matrix exponential (expm in Matlab) to solve your system of linear differential equations directly.
There are many ways to accomplish what you're trying to do. They all depend a bit on your system, how it behaves, and the particular event you want to capture. Here's a small example for a 2-by-2 system of linear differential equations:
function multipleeventsdemo
A = [-1 1;1 -2]; % Example A matrix
tspan = [0 50]; % Initial and final time
x0 = [1;1]; % Initial conditions
f = #(t,y)A*y; % ODE function
thresh = 0; % Threshold value
tol = 1e-3; % Tolerance on threshold
opts = odeset('Events',#(t,y)events(t,y,thresh,tol)); % Create events function
[t,y] = ode45(f,tspan,x0,opts); % Integrate with options
figure;
plot(t,y);
function [value,isterminal,direction] = events(t,y,thresh,tol)
value = y-thresh-tol;
isterminal = all(y-thresh-tol<=0)+zeros(size(y)); % Change termination condition
direction = -1;
Integration is stopped when both states are within tol of thresh. This is accomplished by adjusting the isterminal output of the events function. Note that separate tolerance and threshold variables isn't really necessary – you simply need to define the crossing value.
If your system oscillates as it approaches it's steady state (if A has complex eigenvalues), then you'll need to do more work. But you questions doesn't indicate this. And again, numerical integration may not be the easiest/best way to solve your problem which such a system. Here is how you could use expm in conjunction with a bit of symbolic math:
A = [-1 1;1 -2];
x0 = [1;1];
tol = 1e-3;
syms t_sym
y = simplify(expm(A*t_sym)*x0) % Y as a function of t
t0 = NaN(1,length(x0));
for i = 1:length(x0)
sol = double(solve(y(i)==tol,t_sym)) % Solve for t when y(i) equal to tol
if ~isempty(sol) % Could be no solution, then NaN
t0(i) = max(sol); % Or more than one solution, take largest
end
end
f = matlabFunction(y); % Create vectorized function of t
t_vec = linspace(0,max(t0),1e2); % Time vector
figure;
plot(t_vec,f(t_vec));
This will only work for fairly small A, however, because of the symbolic math. Numerical approaches using expm are also possible and likely more scalable.