Encoding system of differential equations into a vector and approximate solutions - matlab

I am trying to solve a system of differential equations by the Euler's method. First, I have encoded my system into a vector. Then I pass the initial conditions to the function ode_Euler.
However, there's somehting wrong about my attempt. I am getting this error:
>> nm06p03a
Unable to perform assignment because the size of the left side is 1-by-2 and the size of the right side is 2-by-2.
Error in ode_Euler (line 11)
y(k+1,:)= y(k,:) +h*feval(f,t(k),y(k,:));
Error in nm06p03a (line 12)
tic, [tE,xE]=ode_Euler(f,tspan,x0,N); t_Euler=toc;
This is my code so far:
clear, clf
f=#(t,x)[-x(2)+x(1)*x(2); x(1)-(0.5.*x(1).^2)+(0.5.*x(2).^2)]; %Encoding system of differential equations into a vector
t0=0; tf=10;
tspan=[t0 tf];
N=100;
x0s=[0.2 0]; % A matrix consisting of initial points
for iter=1:size(x0s,1)
x0=x0s(iter,:);
tic, [tE,xE]=ode_Euler(f,tspan,x0,N); t_Euler=toc;
subplot(220+iter),
plot(tE,xE,'r:')
legend('ode_ Euler')
end
Here is the Euler's method:
function [t,y]=ode_Euler(f,tspan,y0,N)
if nargin<4|N<=0, N=100; end
if nargin<3, y0=0; end
h=(tspan(2)-tspan(1))/N;
t=tspan(1)+[0:N]'*h;
y(1,:)=y0(:)'; %make it a row vector
for k=1:N
y(k+1,:)= y(k,:) +h*feval(f,t(k),y(k,:));
end
When I use this other method ode_Heun, I get the same error:
function [t,y]=ode_Heun(f,tspan,y0,N)
if nargin<4|N<=0, N=100; end
if nargin<3, y0=0; end
h=(tspan(2)-tspan(1))/N; % Step-size
t=tspan(1)+[0:N]'*h; % Time vector
y(1,:)=y0(:)'; % make the initial value a row vector
for k=1:N
fk= feval(f,t(k),y(k,:)); y(k+1,:)= y(k,:)+h*fk; % Eq.(6.2.3)
y(k+1,:)= y(k,:) +h/2*(fk +feval(f,t(k+1),y(k+1,:))); % Eq.(6.2.4)
end
Can I get some help to understand the problem with my code?

y(k,:) is a row vector, while the return value of f is a column vector. Per broadcasting rules the sum of a row and a column vector is a matrix as the sum of the matrices of repeated row and column vectors.
This is not very logical in the context of vector and matrix operations, but can make sense in the processing of (finite) sequences of vectors. Unfortunately that distinction is not realized and enforced in the type system.

Related

Plotting multiple vectors of different lengths on the same figure

I am currently working on a project in MATLAB that compares two different numerical methods (direct and iterative), each for two different matrix sizes, to the actual analytic solution of the system. The first .m file included below provides an implementation of a tridiagonal matrix solver, where the main function plots a solution for two different sizes of tridiagonal matrices: a 2500x2500 matrix and 50x50 matrix.
function main
n=50;
for i=1:2
di=2*ones(n,1);
up=-ones(n,1);
lo=-ones(n,1);
b=ones(n,1)/(n*n);
[subl,du,supu]=tridiag_factor(lo,di,up);
usol = tridiag_solve(subl,du,supu,b);
%figure(i)
plot(usol);
hold on;
title(['tridiag ' num2str(n) ''])
n=2500;
end
function [subl,du,supu]=tridiag_factor(subd,d,supd)
n=length(d);
for i=1:n-1
subd(i)=subd(i)/d(i);
d(i+1)=d(i+1)-subd(i)*supd(i);
end
subl=subd; supu=supd; du=d;
function x = tridiag_solve(subl,du,supu,b)
% forward solve
n=length(b); z=b;
z(1)=b(1);
for i=2:n
z(i)=b(i)-subl(i)*z(i-1);
end
% back solve
x(n)=z(n);
for i=n-1:-1:1
x(i)=(z(i)-supu(i)*x(i+1))/du(i);
end
I have included a hold on statement after the first usol is plotted so I can capture the second usol on the same figure during the second iteration of the for loop. The next piece of code is a Jacobi method that iteratively solves a system for a tridiagonal matrix. In my case, I have constructed two tridiagonal matrices of different sizes which I will provide after this piece of code:
function [xf,r] = jacobi(A,b,x,tol,K)
%
% The function jacobi applies Jacobi's method to solve A*x = b.
% On entry:
% A coefficient matrix of the linear system;
% b right-hand side vector of the linear system;
% x initial approximation;
% eps accuracy requirement: sx=top when norm(dx) < eps;
% K maximal number of steps allowed.
% On return:
% x approximation for the solution of A*x = b;
% r residual vector last used to update x,
% if success, then norm(r) < eps.[
%
n = size(A,1);
fprintf('Running the method of Jacobi...\n');
for k = 1:K
% r = b - A*x;
for i=1:n
sum=0;
for j = 1:n
if j~=i
sum=sum+A(i,j)*x(j);
end
end
x(i)=(b(i)-sum)/(A(i,i));
end;
k=k+1;
r = b - A*x;
% fprintf(' norm(r) = %.4e\n', norm(r));
if (norm(r) < tol)
fprintf('Succeeded in %d steps\n', k);
return;
end;
end
fprintf('Failed to reached accuracy requirement in %d steps.\n', K);
xf=x;
%r(j) = r(j)/A(j,j);
%x(j) = x(j) + r(j);
Now, lastly, here is my code for the two tridiagonal matrices (and other related information for each system corresponding to the appropriate matrices) I wish to use in my two jacobi function calls:
% First tridiagonal matrix
n=50;
A_50=full(gallery('tridiag', n, -1, 2, -1));
% Second tridiagonal matrix
n=2500;
A_2500=full(gallery('tridiag', n, -1, 2, -1));
K=10000;
tol=1e-6;
b_A50=ones(length(A_50), 1);
for i=1:length(A_50)
b_A50(i,1)=0.0004;
end
x_A50=ones(length(A_50),1);
b_A2500=ones(length(A_2500), 1);
for i=1:length(A_2500)
b_A2500(i,1)= 1.6e-7;
end
x_A25000=ones(length(A_2500),1);
As stated in the question header, I need to plot the vectors produced from jacobi(A_50,b_A50,x_A50,tol,K), jacobi(A_2500,b_A2500,x_A25000,tol,K) and a mathematical function y=#(x) -0.5*(x-1)*x all on the same figure along with the two usol (for n=50 and n=2500) produced by the main function shown in the first piece of code, but I have had no luck due to the different lengths of vectors. I understand this is probably an easy fix, but of course my novice MATLAB skills are not sufficing.
Note: I understand x_A50 and x_A2500 are trivial choices for x, but I figured while I ask for help I better keep it simple for now and not create any more issues in my code.
In MATLAB traces sent to same plot must have same length.
I have allocated just one variable containing all traces, for the respective tridiagonals and resulting traces out of your jacobi function.
I have shortened from 2500 to 250, and the reason is that with 2500, compared to 50, the tridiag traces, and the short jacobi are so flat compared to the last trace than one has to recur to dB scale to find them on same graph window as you asked.
1st generate all data, and then plot all data.
So here you have the script plotting all traces in same graph :
clear all;close all;clc
n=[50 250];
us_ol=zeros(numel(n),max(n));
% generate data
for k=[1:1:numel(n)]
di=2*ones(n(k),1);
up=-ones(n(k),1);
lo=-ones(n(k),1);
b=ones(n(k),1)/(n(k)*n(k));
[subl,du,supu]=tridiag_factor(lo,di,up);
us_ol0 = tridiag_solve(subl,du,supu,b);
us_ol(k,[1:numel(us_ol0)])=us_ol0;
end
n_us_ol=[1:1:size(us_ol,2)];
str1=['tridiag ' num2str(n(1))];
str2=['tridiag ' num2str(n(2))];
legend(str1,str2);
grid on
% the jacobi traces
nstp=1e3;
tol=1e-6;
A1=zeros(max(n),max(n),numel(n));
for k=1:1:numel(n)
A0=full(gallery('tridiag', n(k), -1, 2, -1));
A1([1:1:size(A0,1)],[1:1:size(A0,2)],k)=A0;
end
b_A1=ones(max(n),max(n),numel(n));
for k=1:1:numel(n)
for i=1:n(k)
b_A1(i,1,k)=0.0004;
end
end
n_A1=[1:1:size(A1,1)];
jkb=zeros(numel(n),max(n));
for k=1:1:numel(n)
A0=A1([1:n(k)],[1:n(k)],k);
b_A0=b_A1([1:n(k)],[1:n(k)],k);
n0=[1:1:n(k)];
jkb0=jacobi(A0,b_A0,n0',tol,nstp)
jkb(k,[1:numel(jkb0)])=jkb0';
end
% plot data
figure(1)
ax1=gca
plot(ax1,n_us_ol,us_ol(1,:),n_us_ol,us_ol(2,:));
hold(ax1,'on')
plot(ax1,n_A1,jkb(1,:),n_A1,jkb(2,:))
grid on
legend('3/1','3/2','jkb1','jkb2')
title('3diags and jakobians graph')
As mentioned above, one has to zoom to find some of the traces
One way to combine really small traces with large traces is to use Y log scale
figure(2)
ax2=gca
plot(ax2,n_us_ol,10*log10(us_ol(1,:)),n_us_ol,10*log10(us_ol(2,:)));
hold(ax2,'on')
plot(ax2,n_A1,10*log10(jkb(1,:)),n_A1,10*log10(jkb(2,:)))
grid on
legend('3/1[dB]','3/2[dB]','jkb1[dB]','jkb2[dB]')
title('3diags and jakobians graph in dB')

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.

Problem in 2D heat equation solution using FDM in Matlab

I am trying to solve the 2D time dependent heat equation using finite difference method in Matlab. The code is below:
%Spatial variable on x direction
Lx=1;
delta=0.1;
xmin=-Lx/2;
xmax=Lx/2;
Nx=(xmax-xmin)/delta;
x=linspace(xmin,xmax,Nx);
%Spatial variable on y direction
Ly=1;
delta=0.1;
ymin=-Ly/2;
ymax=Ly/2;
Ny=(ymax-ymin)/delta;
y=linspace(ymin,ymax,Ny);
%Total matrix size
N = (Nx * Ny);
%Time variable
dt=0.002;
tmin=0;
tmax=1;
nt=(tmax-tmin)/dt;
tspan=linspace(tmin,tmax,nt);
%Create a meshgrid
[X,Y] = meshgrid(x,y);
% Defining initial state:
T0=exp(-(X.^2+Y.^2));
%reshape the initial condition to a vector
T_reshape = reshape(T0,N,1);
% Constructing the 1D spatial matrix
A=zeros(N,N);
I = eye(N);
%the diagonal elements
for m=1:N %the number of rows
for n=1:N %the number of columns
if (m==n)
A(m,n)=-2/delta^2;
end
%Boundary conditions: A(1,N)==A(N,1)
if(n==N)&&(m==1)
A(m,n)=1;
end
if(n==1)&&(m==N)
A(m,n)=1;
end
end
end
%the off-diagonal elements
for n=1:N-1
A(n+1,n)=1/delta^2; %the value of each lower off-diagonal elements
end
for n=2:N
A(n-1,n)=1/delta^2; %the value of each upper off-diagonal element
end
%create the 2D matrix
B = kron(A,I)+kron(I,A);
% Solve the equation
[Time,Tem]=ode45('dTDistribution',tspan,T_reshape,[],B,delta);
The function that is being called here is as following:
%Define the function
function dT=dTDistribution(tspan,T_reshape,dummy,B,delta)
dT = B.*T_reshape;
end
My problem is that the dimension of my matrix B is different than the dimensions of the initial condition T_reshape, therefore, the multiplication of B.*T_reshape won't be possible. I'm wondering how can I change the dimension of T_reshape to make the multiplication valid. Hope anyone could help.
Thank you.
Thank you for looking at my problem, but I have figured out the mistake in the code. Since A is the 1D matrix, then its size should be either (Nx,Nx) or (Ny,Ny) and then when taking the tensor product to get B the 2D matrix its size will be (N,N). However in the code, A has the size of (N,N) and as a result B is blowing up making the multiplication afterwards not possible.
Thanks.

Vectors must be the same length error

I'm trying to plot an integral L, with respect to Xdot, but I keep getting vectors must be the same length error, I don't know how to fix it however. My code is shown below. You can see at the bottom, the loop for the sum only goes up to 99, while the x_1 goes up to 100. If I change the loop however, I get an error "index exceeds matrix dimensions"
% The solution for this part is based on the Euler method
f=0; %initializing the force row vector
f_1=0.5; %initializing the first derivative of force
x=1; % intializing the mass displacement%
x_1(1)=0; % initializing the first derivative of mass displacement
t=0; % initializing the time row vector
j=1; % initializing a`enter code here`n index used in iterations
a=0;
b=10;
N=100;
h=(b-a)/N;
for j = 0:N-1
f_2=-1*sin(f); %obtain the second derivative of the force
f_1=f_1+f_2*h; %obtain the first derivative of the force
f=f+f_1*h; % obtain the value of force and concatenate it with
%preceding force row vector the element
x_2=f-0.1*x_1-x-x^3; %obtain the second derivative of the mass displacement
x_1=x_1+x_2*h; % obtain the first derivative of the mass displacement
x=x+x_1*h; % obtain the current value of mass displacement and
%concatenate it with preceding mass displacement row vector the element
t=t+h; % obtain the current value of time and concatenate it with
%preceding time row vector the element
j=j+1; %increment the index iterator by one
v(j)=x;
w(j)=t;
m(j)=x_1
end
sum = 0; %%Trapezoidal method to find L, sum is L, put this at the end of your script
for i = 1:size(m,2)-1
sum = sum + h*(m(i+1)^2+m(i)^2)/2;
L(i) = sum
end
plot (m,L, 'r')
How about you shorten your x by one when plotting?
plot (m(1:end-1),L, 'r')

Matlab vectorizing equations and matrix multiplication

I have a program that currently uses a for loop to iterate through a set of functions. I've tried using parfor but that only works on the university's version of Matlab. I'd like to vectorize the handling of this so that a for loop isn't necessary. The equations I'm using basically call different types of Bessel functions and are contained in separate functions.
Here's what I'm trying to do:
For each value of m, build a vector of matrix elements for each required matrix. Then build each full matrix. I think this is working correctly.
Where it's throwing an error is on the final matrix multiplication... even if I just multiply the left 2x2 by the middle 2x2 I get the dreaded error:
??? Error using ==> mtimes
Inner matrix dimensions must agree.
Error in ==> #(m)CL(m)*CM(m)*CR(m)
% Vector for summation. 1 row, 301 columns with data from 0->300
m_max=301;
m=[0:m_max-1];
% Build the 300 elements for the left 2x2 matrix.
CL_11=#(m) H1(m,alpha1);
CL_12=#(m) H2(m,alpha1);
CL_21=#(m) n1*dH1(m,alpha1);
CL_22=#(m) n1*dH2(m,alpha1);
% Build the 300 elements for the middle 2x2 matrix.
CM_11=#(m) n1*dH2(m,alpha2);
CM_12=#(m) -1*H2(m,alpha2);
CM_21=#(m) -1*n1*dH1(m,alpha2);
CM_22=#(m) H1(m,alpha2);
% Build the 300 elements for the right 2x1 matrix.
CR_11=#(m) J(m,alpha3);
CR_21=#(m) n2*dJ(m,alpha3);
% Build the left (CL), middle (CM) and right (CR) matrices.
CL=#(m) [CL_11(m) CL_12(m);CL_21(m) CL_22(m)];
CM=#(m) [CM_11(m) CM_12(m);CM_21(m) CM_22(m)];
CR=#(m) [CR_11(m);CR_21(m)];
% Build the vector containing the products of each triplet of
% matrices.
C=#(m) CL(m)*CM(m)*CR(m);
cl=CL(m)
cm=CM(m)
cr=CR(m)
c=CL(m)*CM(m)*CR(m)
If you have any suggestions or recommendations, I'd greatly appreciate it! I'm still a newbie with Matlab and am trying to develop a higher level of ability with use of matrices and vectors.
Thanks!!
Your matrices are not 2x2. When you do CL_11(m) with m a 1x300 vector, CL_11(m) will be 1x300 as well. Thus CL(m) is 2x301. To get around this, you have to calculate the matrices one-by-one. There are two approaches here.
c=arrayfun(C,m,'UniformOutput',false)
will return a cell array, and so c{1} corresponds to m(1), c{2} to m(2), etc.
On the other hand, you can do
for i=1:m_max
c(:,:,i)=C(m(i));
end
and then c(:,:,i) corresponds to m(1), etc.
I'm not sure which version will be faster, but you can test it easily enough with your code.
If you go through the symbolic toolbox you can construct a function that is easier to handle.
%% symbolic
CL = sym('CL',[2,2])
CM = sym('CM',[2,2])
CR = sym('CR',[2,1])
r = CL*CM*CR
f = matlabFunction(r)
%% use some simple functions so it can be calculated as example
CL_11=#(m) m+1;
CL_12=#(m) m;
CL_21=#(m) m-1;
CL_22=#(m) m+2;
CM_11=#(m) m;
CM_12=#(m) m;
CM_21=#(m) 2*m;
CM_22=#(m) 2*m;
CR_11=#(m) m;
CR_21=#(m) 1-m;
%% here the substitution happens:
fh = #(m) f(CL_11(m),CL_12(m),CL_21(m),CL_22(m),CM_11(m),CM_12(m),CM_21(m),CM_22(m),CR_11(m),CR_21(m))
Out of interest I did a small speed test:
N=1e5;
v = 1:N;
tic
% .... insert symbolic stuff from above
r1 = fh(v);
t1=toc % gives 0.0842s for me
vs
CL=#(m) [CL_11(m) CL_12(m);CL_21(m) CL_22(m)];
CM=#(m) [CM_11(m) CM_12(m);CM_21(m) CM_22(m)];
CR=#(m) [CR_11(m);CR_21(m)];
C=#(m) CL(m)*CM(m)*CR(m);
tic
r2 =arrayfun(C,v,'UniformOutput',false);
t2=toc % gives 7.6874s for me
and
tic
r3 = nan(2,N);
for i=1:N
r3(:,i)=C(v(i));
end
t3=toc % 8.1503s for me