I am trying to solve a differential equation with the ode solver ode45 with MATLAB. I have tried using it with other simpler functions and let it plot the function. They all look correct, but when I plug in the function that I need to solve, it fails. The plot starts off at y(0) = 1 but starts decreasing at some point when it should have been an increasing function all the way up to its critical point.
function [xpts,soln] = diffsolver(p1x,p2x,p3x,p1rr,y0)
syms x y
yp = matlabFunction((p3x/p1x) - (p2x/p1x) * y);
[xpts,soln] = ode45(yp,[0 p1rr],y0);
p1x, p2x, and p3x are polynomials and they are passed into this diffsolver function as parameters.
p1rr here is the critical point. The function should diverge after the critical point, so i want to integrate it up to that point.
EDIT: Here is the code that I have before using diffsolver, the above function. I do pade approximation to find the polynomials p1, p2, and p3. Then i find the critical point, which is the root of p1 that is closest to the target (target is specified by user).
I check if the critical point is empty (sometimes there might not be a critical point in some functions). If its not empty, then it uses the above function to solve the differential equation. Then it plots the x- and y- points returned from the above function basically.
function error = padeapprox(m,n,j)
global f df p1 p2 p3 N target
error = 0;
size = m + n + j + 2;
A = zeros(size,size);
for i = 1:m
A((i + 1):size,i) = df(1:(size - i));
end
for i = (m + 1):(m + n + 1)
A((i - m):size,i) = f(1:(size + 1 - i + m));
end
for i = (m + n + 2):size
A(i - (m + n + 1),i) = -1;
end
if det(A) == 0
error = 1;
fprintf('Warning: Matrix is singular.\n');
end
V = -A\df(1:size);
p1 = [1];
for i = 1:m
p1 = [p1; V(i)];
end
p2 = [];
for i = (m + 1):(m + n + 1)
p2 = [p2; V(i)];
end
p3 = [];
for i = (m + n + 2):size
p3 = [p3; V(i)];
end
fx = poly2sym(f(end:-1:1));
dfx = poly2sym(df(end:-1:1));
p1x = poly2sym(p1(end:-1:1));
p2x = poly2sym(p2(end:-1:1));
p3x = poly2sym(p3(end:-1:1));
p3fullx = p1x * dfx + p2x * fx;
p3full = sym2poly(p3fullx); p3full = p3full(end:-1:1);
p1r = roots(p1(end:-1:1));
p1rr = findroots(p1r,target); % findroots eliminates unreal roots and chooses the one closest to the target
if ~isempty(p1rr)
[xpts,soln] = diffsolver(p1x,p2x,p3fullx,p1rr,f(1));
if rcond(A) >= 1e-10
plot(xpts,soln); axis([0 p1rr 0 5]); hold all
end
end
I saw some examples using another function to generate the differential equation but i've tried using the matlabFunction() method with other simpler functions and it seems like it works. Its just that when I try to solve this function, it fails. The solved values start becoming negative when they should all be positive.
I also tried using another solver, dsolve(). But it gives me an implicit solution all the time...
Does anyone have an idea why this is happening? Any advice is appreciated. Thank you!
Since your code seems to work for simpler functions, you could try to increase the accuracy options of the ode45 solver.
This can be achieved by using odeset:
options = odeset('RelTol',1e-10,'AbsTol',1e-10);
[T,Y] = ode45(#function,[tspan],[y0],options);
Related
I've been trying to integrate the function given in the code over "phi" and "r". Therefor I'm using the Matlab function "integral2". The function has a singularity at (r,phi)=(1,0) (here's a picture), which lets the integrator reach the max possible function evaluations and yields and error. I guess this has not to do with the singularity itself but with the very steep descent next to it!?
I'd be happy for any suggestions on how to get the integration working!
Maybe another integrator or a substitution of variables?
Thanks a lot! :)
p = #(r) 1./sqrt(1-r.^2);
x = 1;
z = 1e-12;
nu = .3;
X = #(r,phi) x-r.*cos(phi);
Y = #(r,phi) -r.*sin(phi);
R = #(r,phi) (X(r,phi).^2 + Y(r,phi).^2).^.5;
Rho = #(r,phi) (X(r,phi).^2 + Y(r,phi).^2 + z^2).^.5;
f1 = #(r,phi) (1 - z./Rho(r,phi)).*(X(r,phi).^2 - Y(r,phi).^2)./R(r,phi).^2;
f2 = #(r,phi) f1(r,phi) + z*Y(r,phi).^2./Rho(r,phi).^3;
f3 = #(r,phi) (1 - 2*nu)./R(r,phi).^2.*f2(r,phi) - 3*z*X(r,phi).^2./Rho(r,phi).^5;
fun = #(r,phi) r.*p(r).*f3(r,phi);
sx = 1/2/pi*integral2(fun,0,1,0,pi);
xM(t) = −11.9084 + 57.9117 cos(2πt/87.97), yM(t) = 56.6741 sin(2πt/87.97) and xE(t) = −2.4987 + 149.6041 cos(2πt/365.25), yE(t) = 149.5832 sin(2πt/365.25) represent the positions of Mercury and Earth on an xy-plane, where the sun is at (0,0) and t is the passage of time. I'd like to find the minimum distance between the planets over the next 1000 days by finding the minimum of the function f(t) = ((xE(t) − xM(t))^2 + (yE(t) − yM(t))^2)^(1/2) with a matlab program that implements the secant method.
function [r,N] = SECANT(x0,x1,eps,Nmax,f)
N = 1;
while (N <= Nmax) && (abs(x1-x0) >= eps)
r = x1 - f(x1)*((x1-x0)/(f(x1)-f(x0)));
x0 = x1;
x1 = r;
N = N+1;
end
r
N
The above is what I have for the secant method. I think it should work, but it's possible I made a mistake with it. And this is what I have in matlab to actually solve the problem.
p = #(t) (((-2.4987 + 149.6041*cos(2*pi*t/365.25))-(-11.9084 + 57.9117*cos(2*pi*t/87.97))).^2+((149.5832*sin(2*pi*t/365.25))-(56.6741*sin(2*pi*t/87.97))).^2).^(1/2);
y = SECANT(x0,x1,eps,Nmax,p)
I know that only p is the appropriate thing to pass to the function. I could make up eps (level of precision), and Nmax (maximum number of iterations), but I can't figure out what the parameters x0 and x1 should be.
I am trying to solve a 1D advection equation in Matlab as described in this paper, equations (55)-(57). I am making use of the central difference in equaton (59).
I would ultimately like to get something like figure (2) in the paper, which is the result of solving the advection equation for an advection velocity e(1-k) with k=1, i.e. a stationary wave. However, my code keeps diverging. Here is what I have so far:
%initial parameters
e = 1.0;
k = 0.5;
N = 120;
lx = 120;
%initialization of sine
for x=1:lx
if(x<3*lx/4+1 && x>lx/4+1)
phi(x) = sin(2*pi*(x-1-lx/4)/lx);
else
phi(x) = 0.0;
end
end
%advection loop
for t=1:N
gradPhi = 0.5*(+circshift(phi, [0,-1]) - circshift(phi, [0,+1]));
phiBar = phi + 0.5*k*e*gradPhi;
phiOutbar = circshift(phiBar, [0,-1]);
gradPhi = 0.5*(+circshift(phiOutbar, [0,-1]) - circshift(phiOutbar, [0,+1]));
phi = phiOutbar + 0.5*k*e*gradPhi;
end
plot(phi)
Where is the error in my simple code?
You haven't included the time step dt in your update equation. The term in equation (55) says k * e * dt / 2.
This is making your update really unstable and leading to divergence. For stability, you need CFL of 1, and yours is currently around 120. Try updating your code this way:
dt = 1/120;
%advection loop
for t=1:N/dt
gradPhi = 0.5*(+circshift(phi, [0,-1]) - circshift(phi, [0,+1]));
phiBar = phi + 0.5*dt*k*e*gradPhi;
phiOutbar = circshift(phiBar, [0,-1]);
gradPhi = 0.5*(+circshift(phiOutbar, [0,-1]) - circshift(phiOutbar, [0,+1]));
phi = phiOutbar + 0.5*dt*k*e*gradPhi;
end
I'm trying to solve:
x' = 60*x - 0.2*x*y;
y' = 0.01*x*y - 100* y;
using the fourth-order Runge-Kutta algorithm.
Starting points: x(0) = 8000, y(0) = 300 range: [0,15]
Here's the complete function:
function [xx yy time r] = rk4_m(x,y,step)
A = 0;
B = 15;
h = step;
iteration=0;
t = tic;
xh2 = x;
yh2 = y;
rr = zeros(floor(15/step)-1,1);
xx = zeros(floor(15/step)-1,1);
yy = zeros(floor(15/step)-1,1);
AA = zeros(1, floor(15/step)-1);
while( A < B)
A = A+h;
iteration = iteration + 1;
xx(iteration) = x;
yy(iteration) = y;
AA(iteration) = A;
[x y] = rkstep(x,y,h);
for h2=0:1
[xh2 yh2] = rkstep(xh2,yh2,h/2);
end
r(iteration)=abs(y-yh2);
end
time = toc(t);
xlabel('Range');
ylabel('Value');
hold on
plot(AA,xx,'b');
plot(AA,yy,'g');
plot(AA,r,'r');
fprintf('Solution:\n');
fprintf('x: %f\n', x);
fprintf('y: %f\n', y);
fprintf('A: %f\n', A);
fprintf('Time: %f\n', time);
end
function [xnext, ynext] = rkstep(xcur, ycur, h)
kx1 = f_prim_x(xcur,ycur);
ky1 = f_prim_y(xcur,ycur);
kx2 = f_prim_x(xcur+0.5*h,ycur+0.5*h*kx1);
kx3 = f_prim_x(xcur+0.5*h,ycur+0.5*h*kx2);
kx4 = f_prim_x(xcur+h,ycur+h*kx3);
ky2 = f_prim_y(xcur+0.5*h*ky1,ycur+0.5*h);
ky3 = f_prim_y(xcur+0.5*h*ky2,ycur+0.5*h);
ky4 = f_prim_y(xcur+h*ky2,ycur+h);
xnext = xcur + (1/6)*h*(kx1 + 2*kx2 + 2*kx3 + kx4);
ynext = ycur + (1/6)*h*(ky1 + 2*ky2 + 2*ky3 + ky4);
end
function [fx] = f_prim_x(x,y)
fx = 60*x - 0.2*x*y;
end
function [fy] = f_prim_y(x,y)
fy = 0.01*x*y - 100*y;
end
And I'm running it by executing: [xx yy time] = rk4_m(8000,300,10)
The problem is that everything collapses after 2-3 iterations returning useless results. What am I doing wrong? Or is just this method not appropriate for this kind equation?
The semicolons are intentionally omitted.
Looks like I didn't pay attention to actual h size. It works now! Thanks!
Looks like some form of the Lotka-Volterra equation?
I'm not sure if if your initial condition is [300;8000] or [8000;300] (you specify it both ways above), but regardless, you have an oscillatory system that you're trying to integrate with a large fixed time step that is (much) greater than the period of oscillation. This is why your error explodes. If you try increasing n (say, 1e6), you'll find that eventually you'll get a stable solution (assuming that your Runge-Kutta implementation is otherwise correct).
Is there a reason why you're not using Matlab's builtin ODE solvers, e.g. ode45 or ode15s?
function ode45demo
[t,y]=odeode45(#f,[0 15],[300;8000]);
figure;
plot(t,y);
function ydot=f(t,y)
ydot(1,1) = 60*y(1) - 0.2*y(1)*y(2);
ydot(2,1) = 0.01*y(1)*y(2) - 100*y(2);
You'll find that adaptive step size solvers are much more efficient for these types of oscillatory problems. Because your system has such a high frequency and seems rather stiff, I suggest that you also look at what ode15s gives and/or adjust the 'AbsTol' and 'RelTol' options with odeset.
The immediate problem is that the RK4 code was not completely evolved from the scalar case to the case of two coupled equations. Note that there is no time parameter in the derivative funtions. x and y are both dependent variables and thus get the slope update defined by the derivative functions in every step. Then xcur gets the kx updates and ycur gets the ky updates.
function [xnext, ynext] = rkstep(xcur, ycur, h)
kx1 = f_prim_x(xcur,ycur);
ky1 = f_prim_y(xcur,ycur);
kx2 = f_prim_x(xcur+0.5*h*kx1,ycur+0.5*h*ky1);
ky2 = f_prim_y(xcur+0.5*h*kx1,ycur+0.5*h*ky1);
kx3 = f_prim_x(xcur+0.5*h*kx2,ycur+0.5*h*ky2);
ky3 = f_prim_y(xcur+0.5*h*kx2,ycur+0.5*h*ky2);
kx4 = f_prim_x(xcur+h*kx3,ycur+h*ky3);
ky4 = f_prim_y(xcur+h*kx3,ycur+h*ky3);
xnext = xcur + (1/6)*h*(kx1 + 2*kx2 + 2*kx3 + kx4);
ynext = ycur + (1/6)*h*(ky1 + 2*ky2 + 2*ky3 + ky4);
end
Solving coupled non linear differential equation by Mat-lab or by calculations
equation 1: x'(t) = -a* x(t) /(x(t) + y(t))
equation 2: y'(t) = -b* y(t) /(x(t) + y(t))
I tried in mathematica but got a very comlicated solution.
Solve[{x'[t] == -a* x[t] /(x[t] + y[t]), y'[t] == -b* y[t] /(x[t] + y[t])}, {x, y}, t]
How can I plot it?
My initial conditions are
x(0) = xo
y(0) = yo
Also, a and b are constants.
I have to plot x and y wrt t after inserting values of a and b . ( a= 2 , b =5 say )
A lot of things to note in this situation:
You need to create a function that contains both a and b:
function dy =soProblem(t,y,a,b)
dy=[-a*y(1)/(y(1)+y(2)); -b*y(2)/(y(1)+y(2))];
end
Call the standard ode function:
a = 2;
b = 5; tend = 10; x0 = 1; y0 = 2;
[T,Y] = ode45(#(t,y)soProblem(t,y,a,b),[0 tend],[x0 y0]);
plot (T,Y)
Realize you may have a stiff equation on your hands.
Have fun identifying the ideal function call:
[T15,Y15] = ode15s(#(t,y)soProblem(t,y,a,b),[0 tend],[x0 y0]);
[T23t,Y23t] = ode23t(#(t,y)soProblem(t,y,a,b),[0 tend],[x0 y0]);
[T23tb,Y23tb] = ode23tb(#(t,y)soProblem(t,y,a,b),[0 tend],[x0 y0]);
%note ode23s doesn't converge (or at least takes forever)
plot (T,Y,T15,Y15,T23t,Y23t,T23tb,Y23tb)
Understand why mathematica becomes restless
In mathematica:
Try ndsolve
In matlab:
Create a function file yourfunction.m:
function [Y_prime]=yourfunction(t, Y)
Y_prime=[-2*Y(1)./(Y(1) + Y(2)) -5*Y(2)./(Y(1) + Y(2))];
end
and then
[T,Y] = ode45(yourfunction,[0 t_end],[x0 y0]);
plot(T,Y(:,1));
hold on
plot(T,Y(:,2));