I've got two second order non-linear differential equations which I need to solve in order to model and plot the motion of a mass hanging on a spring which is swinging like a pendulum. They are here:
I'm using odeToVectorField to rewrite them as first order linear ODEs, then I call ode45 to solve the resulting system of equations. But when I plot the results, it gives me strange answers, like negative radius, etc.
Can anyone help me find where I've messed up my code? Thanks a lot!
My code is as follows:
global m R g B k %declare global variables to use them everywhere
m=1.0; %mass of ball [kg]
k=200; % stiffness of the spring [N/m]
R=0.5; % unstretched length of the spring [m]
g=9.81; % acceleration due to the gravity [m/s^2]
B=0; % coefficient of air drag [kg/m]
syms r(t) f(t)
[V] = odeToVectorField(diff(r,2)== r*((diff(f,1))^2) + (g*cos(f))-(k*(r-R))-B*diff(r,1)*sqrt(diff(r,1)^2 +r^2*diff(f,1)^2),...
diff(f,2)== ((g*sin(f)+2*diff(r,1)*diff(f,1))/-r)-B*diff(f,1)*sqrt(diff(r,1)^2 +r^2*diff(f,1)^2));
F = matlabFunction(V,'vars',{'t','Y'});
%define initial conditions:
theta_0=(70*pi/180);
theta_dot_0=0;
r_0= R + (m*g*cos(theta_0))/k ;
r_dot_0=0;
t_start=0; %start time
t_step=.01; % time step
t_final=5; % final time
%Solve that system of ODEs from [V]
[t , X]=ode45(F, t_start:t_step:t_final , [r_0;r_dot_0;theta_0;theta_dot_0]);
figure(1)
subplot(2,1,1)
plot(t,X(:,1),'LineWidth',2)
xlabel('t');ylabel('r','fontsize',12);
hold on
subplot(2,1,2)
plot(t,X(:,2),'LineWidth',2)
xlabel('t');ylabel('r dot','fontsize',12);
hold on
figure(2)
subplot(2,1,1)
plot(t,X(:,3),'LineWidth',2)
xlabel('t');ylabel('theta [rad]','fontsize',12);
hold on
subplot(2,1,2)
plot(t,X(:,4),'LineWidth',2)
xlabel('t');ylabel('theta dot [rad/s]','fontsize',12);
hold on
Related
I'm trying to redo the Poisson equation so that ∂²Φ /∂x² = 2x² - 0.5x + exp(x). Every time I try and input the right-hand side of the equation it comes up with a syntax error, any help would very much be appreciated.
% Solving of 1D Poisson equation
% using finite differences
% Clearing memory and figures
clear all
clf
% Define 1D numerical model
xsize=100; % Model size in horizontal direction, m
Nx=101; % Number of grid points
dx=xsize/(Nx-1); % Gridstep, m
x=0:dx:xsize; % Coordinates of grid points, m
% Defining global matrixes
L=zeros(Nx,Nx); % Koefficients in the left part
R=zeros(Nx,1); % Right parts of equations
% Composing global matrixes
% by going through all grid points
for j=1:1:Nx
% Discriminating between BC-points and internal points
if(j==1 || j==Nx)
% BC-points
L(j,j)=1; % Left part
R(j)=0; % Right part
else
% Composing Poisson eq. d2T/dx2=1
%
% ---T(j-1)------T(j)------T(j+1)---- STENCIL
%
% 1/dx2*T(j-1)+(-2/dx2)*T(j)+1/dx2*T(j+1)=1
%
% Left part of j-equation
L(j,j-1)=1/dx^2; % T(j-1)
L(j,j)=-2/dx^2; % T(j)
L(j,j+1)=1/dx^2; % T(j+1)
% Right part
R(j)=2x;
end
end
% Solve the matrix
S=L\R;
% Reload solutions
T=zeros(1,Nx); % Create array for temperature, K
for j=1:1:Nx
T(j)=S(j);
end
% Visualize results
figure(1); clf
plot(x,T,'o r') % Plotting results
Again, I appreciate any help in this matter
The problem is clear: "input the right-hand side of the equation it comes up with a syntax error", to correct the error you should properly define the right-hand side of the equation:
R(j)=2*x(j)^2-0.5*x(j) + exp(x(j));
Since it depends on x, you should point to each element j when calculating the coefficients in the for loop on the left and right hand sides of the equation.
Analytical solution of the PDE is:
Changing this line of code and plotting the discretized and analytical solutions produces
% Visualize results
figure(1); clf
plot(x,T,'o r');
hold on
plot(x , 0.166667*x.^4-0.0833333*x.^3-268811714181613573321934397213431784538112*x+exp(x)-1)
which seems correct. Differences are due to discretization itself.
I'm trying to solve the following problem using MATLAB but I faced multiple issues. The plot I obtained doesn't seem right even though I tried to obtain the steady-state solution, I got a plot that doesn't look steady.
The problem I'm trying to solve
The incorrect plot I got.
and here is the code
% system parameters
m=1; k=1; c=.1; wn=sqrt(k/m); z=c/2/sqrt(m*k); wd=wn*sqrt(1-z^2);
% initial conditions
x0=0; v0=0;
%% time
dt=.001; tMax=8*pi; t=0:(tMax-0)/999:tMax;
% input
A=1
omega=(2*pi)/10
F=A/2-(4*A/pi^2)*cos(omega*t); Fw=fft(F);
F=k*A*cos(omega*t); Fw=fft(F);
% normalize
y = F/m;
% compute coefficients proportional to the Fourier series coefficients
Yw = fft(y);
% setup the equations to solve the particular solution of the differential equation
% by the method of undetermined coefficients
N=1000
T=10
k = [0:N/2];
w = 2*pi*k/T;
A = wn*wn-w.*w;
B = 2*z*wn*w;
% solve the equation [A B;-B A][real(Xw); imag(Xw)] = [real(Yw); imag(Yw)] equation
% Note that solution can be obtained by writing [A B;-B A] as a scaling + rotation
% of a 2D vector, which we solve using complex number algebra
C = sqrt(A.*A+B.*B);
theta = acos(A./C);
Ywp = exp(j*theta)./C.*Yw([1:N/2+1]);
% build a hermitian-symmetric spectrum
Xw = [Ywp conj(fliplr(Ywp(2:end-1)))];
% bring back to time-domain (function synthesis from Fourier Series coefficients)
x = ifft(Xw);
figure()
plot(t,x)
Your forcing function doesn't look like the triangle wave in the problem. I edited the %% time section of your code into the following and appeared to give a steady state response.
%% time
TP = 10; % forcing time period (10 s)
dt=.001;
tMax= 3*TP; % needs to be multiple of the time period
t=0:(tMax-0)/999:tMax;
% input
A=1; % Forcing amplitude
omega=(2*pi)/TP;
% forcing is a triangle wave
% generate a triangle wave with min/max values of 0/1.
F = 0*t;
for i = 1:length(t)
if mod(t(i), TP) <= TP/2
F(i) = mod(t(i), TP)/(TP/2);
else
F(i) = 2 - mod(t(i), TP)/(TP/2);
end
end
F = F*A; % scale triangle wave by amplitude
% you can also use MATLAB's sawtooth() function if you have the signal
% processing toolbox
I am trying to determine the pose (x,y,theta) of a differential drive robot using ode45. The code I have below solves for the x position, but I am having an issue with the initial condition. I set it to 0 since at time 0 the robot is assumed to be at the origin, but I get an error. How do I set the initial condition for ode45 such that I get the expected output?
I tried to make the initial conditions vector of the same length as dxdt by setting initial conditions as a 41x1 zeros matrix, but I didn't understand the output and I don't believe I did it correctly.
% Given conditions for a differential drive robot:
B = 20; % (cm) distance along axle between centers of two wheels
r = 10; % (cm) diameter of both wheels
w_l = 5*sin(3*t); % (rad/s) angular rate of left wheel
w_r = 5*sin(3*t); % (rad/s) angular rate of right wheel
v_l = r*w_l; % (cm/s) velocity of left wheel
v_r = r*w_r; % (cm/s) velocity of right wheel
v = (v_r+v_l)/B; % (cm/s) velocity of robot
theta = 90; % constant orientation of robot since trajectory is straight
% Solve differential equation for x:
dxdt = v*cos(theta); % diff equaition for x
tspan = [0 20]; % time period to integrate over
x0 = 0; % initial condition since robot begins at origin
[t,x] = ode45(#(t,y) dxdt, tspan, x0);
I want to solve the differential equation dxdt for 0 to 20 seconds with an initial condition of 0. I expect the output to give me a vector of time from 0 to 20 and an array of for x. The problem I believe lies with the initial condition. MATLAB gives me an error in the live editor telling me, " #(t,y)dxdt returns a vector of length 69, but the length of initial conditions vector is 1. The vector returned by #(t,y)dxdt and the initial conditions vector must have the same number of elements."
The issue is not the initial condition. You need to define dxdt as a function i.e.
% Define differential equation
function derivative = dxdt(t,y)
% Given conditions for a differential drive robot:
B = 20; % (cm) distance along axle between centers of two wheels
r = 10; % (cm) diameter of both wheels
w_l = 5*sin(3*t); % (rad/s) angular rate of left wheel
w_r = 5*sin(3*t); % (rad/s) angular rate of right wheel
v_l = r*w_l; % (cm/s) velocity of left wheel
v_r = r*w_r; % (cm/s) velocity of right wheel
v = (v_r+v_l)/B; % (cm/s) velocity of robot
theta = 90; % constant orientation of robot since trajectory is straight
derivative = v*cos(theta); % diff equation for x
end
Then when you use ode45 you should tell it to pass the t and y variables as arguments to dxdt like
[t,x] = ode45(#(t,y) dxdt(t,y), tspan, x0);
This should then work. In this case, as dxdt only takes the default arguments, you could also write
[t,x] = ode45(#dxdt, tspan, x0);
The error you got indicates that at some point you made dxdt into a vector of length 69, whilst MATLAB was expecting to get back 1 value for dxdt when it passed one t and one y to your dxdt 'function'. Whenever you get errors like this, I'd recommend putting
clear all
`clearvars` % better than clear all - see am304's comment below
at the top of your script to avoid polluting your workspace with previously defined variables.
Edit: Some time after I asked this question, an R package called MonoPoly (available here) came out that does exactly what I want. I highly recommend it.
I have a set of points I want to fit a curve to. The curve must be monotonic (never decreasing in value) i.e. the curve can only go upward or stay flat.
I originally had been polyfitting my results and this had been working great until I found a particular dataset. The polyfit for data in this dataset was non-monotonic.
I did some research and found a possible solution in this post:
Use lsqlin. Constrain the first derivative to be non-negative at both
ends of the domain of interest.
I'm coming from a programming rather than math background so this is a little beyond me. I don't know how to constrain the first derivative to be non-negative as he said. Also, I think in my case I need a curve so I should use lsqcurvefit but I don't know how to constrain it to produce monotonic curves.
Further research turned up this post recommending lsqcurvefit but I can't figure out how to use the important part:
Try this non-linear function F(x) also. You use it together with
lsqcurvefit but it require a start guess on the parameters. But it is
a nice analytic expression to give as a semi-empirical formula in a
paper or a report.
%Monotone function F(x), with c0,c1,c2,c3 varitional constants F(x)=
c3 + exp(c0 - c1^2/(4*c2))sqrt(pi)...
Erfi((c1 + 2*c2*x)/(2*sqrt(c2))))/(2*sqrt(c2))
%Erfi(x)=erf(i*x) (look mathematica) but the function %looks much like
x^3 %derivative f(x), probability density f(x)>=0
f(x)=dF/dx=exp(c0+c1*x+c2*x.^2)
I must have a monotonic curve but I'm not sure how to do it, even with all of this information. Would a random number be enough for a "start guess". Is lsqcurvefit best? How can I use it to produce a best fitting monotonic curve?
Thanks
Here is a simple solution using lsqlin. The derivative constrain is enforced in each data point, this could be easily modified if needed.
Two coefficient matrices are needed, one (C) for least square error calculation and one (A) for derivatives in the data points.
% Following lsqlin's notations
%--------------------------------------------------------------------------
% PRE-PROCESSING
%--------------------------------------------------------------------------
% for reproducibility
rng(125)
degree = 3;
n_data = 10;
% dummy data
x = rand(n_data,1);
d = rand(n_data,1) + linspace(0,1,n_data).';
% limit on derivative - in each data point
b = zeros(n_data,1);
% coefficient matrix
C = nan(n_data, degree+1);
% derivative coefficient matrix
A = nan(n_data, degree);
% loop over polynomial terms
for ii = 1:degree+1
C(:,ii) = x.^(ii-1);
A(:,ii) = (ii-1)*x.^(ii-2);
end
%--------------------------------------------------------------------------
% FIT - LSQ
%--------------------------------------------------------------------------
% Unconstrained
% p1 = pinv(C)*y
p1 = fliplr((C\d).')
p2 = polyfit(x,d,degree)
% Constrained
p3 = fliplr(lsqlin(C,d,-A,b).')
%--------------------------------------------------------------------------
% PLOT
%--------------------------------------------------------------------------
xx = linspace(0,1,100);
plot(x, d, 'x')
hold on
plot(xx, polyval(p1, xx))
plot(xx, polyval(p2, xx),'--')
plot(xx, polyval(p3, xx))
legend('data', 'lsq-pseudo-inv', 'lsq-polyfit', 'lsq-constrained', 'Location', 'southoutside')
xlabel('X')
ylabel('Y')
For the specified input the fitted curves:
Actually this code is more general than what you requested, since the degree of polynomial can be changed as well.
EDIT: enforce derivative constrain in additional points
The issue pointed out in the comments is due to that the derivative checks are enforced only in the data points. Between those no checks are performed. Below is a solution to alleviate this problem. The idea: convert the problem to an unconstrained optimization by using a penalty term.
Note that it is using a term pen to penalize the violation of the derivative check, thus the result is not a true least square error solution. Additionally, the result is dependent on the penalty function.
function lsqfit_constr
% Following lsqlin's notations
%--------------------------------------------------------------------------
% PRE-PROCESSING
%--------------------------------------------------------------------------
% for reproducibility
rng(125)
degree = 3;
% data from comment
x = [0.2096 -3.5761 -0.6252 -3.7951 -3.3525 -3.7001 -3.7086 -3.5907].';
d = [95.7750 94.9917 90.8417 62.6917 95.4250 89.2417 89.4333 82.0250].';
n_data = length(d);
% number of equally spaced points to enforce the derivative
n_deriv = 20;
xd = linspace(min(x), max(x), n_deriv);
% limit on derivative - in each data point
b = zeros(n_deriv,1);
% coefficient matrix
C = nan(n_data, degree+1);
% derivative coefficient matrix
A = nan(n_deriv, degree);
% loop over polynom terms
for ii = 1:degree+1
C(:,ii) = x.^(ii-1);
A(:,ii) = (ii-1)*xd.^(ii-2);
end
%--------------------------------------------------------------------------
% FIT - LSQ
%--------------------------------------------------------------------------
% Unconstrained
% p1 = pinv(C)*y
p1 = (C\d);
lsqe = sum((C*p1 - d).^2);
p2 = polyfit(x,d,degree);
% Constrained
[p3, fval] = fminunc(#error_fun, p1);
% correct format for polyval
p1 = fliplr(p1.')
p2
p3 = fliplr(p3.')
fval
%--------------------------------------------------------------------------
% PLOT
%--------------------------------------------------------------------------
xx = linspace(-4,1,100);
plot(x, d, 'x')
hold on
plot(xx, polyval(p1, xx))
plot(xx, polyval(p2, xx),'--')
plot(xx, polyval(p3, xx))
% legend('data', 'lsq-pseudo-inv', 'lsq-polyfit', 'lsq-constrained', 'Location', 'southoutside')
xlabel('X')
ylabel('Y')
%--------------------------------------------------------------------------
% NESTED FUNCTION
%--------------------------------------------------------------------------
function e = error_fun(p)
% squared error
sqe = sum((C*p - d).^2);
der = A*p;
% penalty term - it is crucial to fine tune it
pen = -sum(der(der<0))*10*lsqe;
e = sqe + pen;
end
end
Gradient free methods might be used to solve the problem by exactly enforcing the derivative constrain, for example:
[p3, fval] = fminsearch(#error_fun, p_ini);
%--------------------------------------------------------------------------
% NESTED FUNCTION
%--------------------------------------------------------------------------
function e = error_fun(p)
% squared error
sqe = sum((C*p - d).^2);
der = A*p;
if any(der<0)
pen = Inf;
else
pen = 0;
end
e = sqe + pen;
end
fmincon with non-linear constraint might be a better choice.
I let you to work out the details and to tune the algorithms. I hope that it is sufficient.
I have a 1D heat diffusion code in Matlab which I was using on a timescale of 10s of years and I am now trying to use the same code to work on a scale of millions of years. Obviously if I keep my timestep the same this will take ages to calculate but if I increase my timestep I encounter numerical stability issues.
My questions are:
How should I approach this problem? What affects the maximum stable timestep? And how do I calculate this?
Many thanks,
Alex
close all
clear all
dx = 4; % discretization step in m
dt = 0.0000001; % timestep in Myrs
h=1000; % height of box in m
nx=h/dx+1;
model_lenth=1; %length of model in Myrs
nt=ceil(model_lenth/dt)+1; % number of tsteps to reach end of model
kappa = 1e-6; % thermal diffusivity
x=0:dx:0+h; % finite difference mesh
T=38+0.05.*x; % initial T=Tm everywhere ...
time=zeros(1,nt);
t=0;
Tnew = zeros(1,nx);
%Lower sill
sill_1_thickness=18;
Sill_1_top_position=590;
Sill_1_top=ceil(Sill_1_top_position/dx);
Sill_1_bottom=ceil((Sill_1_top_position+sill_1_thickness)/dx);
%Upper sill
sill_2_thickness=10;
Sill_2_top_position=260;
Sill_2_top=ceil(Sill_2_top_position/dx);
Sill_2_bottom=ceil((Sill_2_top_position+sill_2_thickness)/dx);
%Temperature of dolerite intrusions
Tm=1300;
T(Sill_1_top:Sill_1_bottom)=Tm; %Apply temperature to intrusion 1
% unit conversion to SI:
secinmyr=24*3600*365*1000000; % dt in sec
dt=dt*secinmyr;
%Plot initial conditions
figure(1), clf
f1 = figure(1); %Make full screen
set(f1,'Units', 'Normalized', 'OuterPosition', [0 0 1 1]);
plot (T,x,'LineWidth',2)
xlabel('T [^oC]')
ylabel('x[m]')
axis([0 1310 0 1000])
title(' Initial Conditions')
set(gca,'YDir','reverse');
%Main calculation
for it=1:nt
%Apply temperature to upper intrusion
if it==10;
T(Sill_2_top:Sill_2_bottom)=Tm;
end
for i = 2:nx-1
Tnew(i) = T(i) + kappa*dt*(T(i+1) - 2*T(i) + T(i-1))/dx/dx;
end
Tnew(1) = T(1);
Tnew(nx) = T(nx);
time(it) = t;
T = Tnew; %Set old Temp to = new temp for next loop
tmyears=(t/secinmyr);
%Plot a figure which updates in the loop of temperature against depth
figure(2), clf
plot (T,x,'LineWidth',2)
xlabel('T [^oC]')
ylabel('x[m]')
title([' Temperature against Depth after ',num2str(tmyears),' Myrs'])
axis([0 1300 0 1000])
set(gca,'YDir','reverse');%Reverse y axis
%Make full screen
f2 = figure(2);
set(f2,'Units', 'Normalized', 'OuterPosition', [0 0 1 1]);
drawnow
t=t+dt;
end
The stability condition for an explicit scheme like FTCS is governed by $r = K dt/dx^2 < 1/2$ or $dt < dx^2/(2K)$ where K is your coefficient of diffusion. This is required in order to make the sign of the 4th order derivative leading truncation error term be negative.
If you do not want to be limited by timestep I suggest using an implicit scheme (albeit at a higher of computational cost than an explicit scheme). This can be achieved simply by using backward Euler for the diffusion term instead of forward Euler. Another option is Crank-Nicholson which is also implicit.
#Isopycnal Oscillation is totally correct in that the maximum stable step is limited in an explicit scheme. Just for reference this is usually referred to as the discrete Fourier number or just Fourier number and can be looked up for different boundary conditions.
also the following may help you for the derivation of the Implicit or Crank-Nicholson scheme and mentions stability Finite-Difference Approximations
to the Heat Equation by Gerald W. Recktenwald.
Sorry I don't have the rep yet to add comments