Mass spring damper calculation in Matlab using Runge Kutta - matlab

I get always an Error: Index exceeds matrix dimensions.
Error in rhs (line 13)
xdot_2 = -(b/m)*x(2) - (k/m)*x(1) + force(t)/m;
Error in FinalProject2 (line 20)
result = rhs(100,x0);
I have a mass spring damper system with this to achieve:
Initially releasing it is release and just once it touch the ground, to see what is it’s initial poistion. Then a force is introduced to see how much it compress and reacts. Finally this force is released to see how much time it will take to recover.
Calculate the potential, and kinetic energy of the system (spring gravity and mass)
once the force is removed and until the system stops
Calculate the energy lost by the damping once the force is removed and until the
system stops.
Get the characteristic function of damping of the damper, ie, the function describing
the motion as it decays
Calculate as accurately as posible the crossing points by y (substract the initial
compresion) of the final position y once the force is removed and until the system
stops.
this is my code:
clc, close all, clear *
t_start = 0;
t_end = 1000; %final time in seconds.
t =t_start:0.001:t_end;
k = 1500.0; % as spring constant N/m,
b = 30; %the damping constant Ns/m,
m = 10.0; %the mass of the device,
F = 0.0; %as the input force N
g = 9.81; %as the gravity constant m/s^2
initial_position = 0;
initial_speed = 0;
x0 = [initial_position initial_speed];
result = rhs(100,x0);
with this as my rhs function:
function xdot=rhs(t,x)
k = 1500.0; % as spring constant N/m,
b = 30; %the damping constant Ns/m,
m = 10.0; %the mass of the device,
force = 0.0; %as the input force N
%g = 9.81; %as the gravity constant m/s^2
xdot_1 = x(2);
xdot_2 = -(b/m)*x(2) - (k/m)*x(1) + force(t)/m;
xdot = [xdot_1 ; xdot_2 ];
end
so what I am doing wrong? Can someone help me by calculating those values? Would be a great help :)
EDIT: my RK function:
% Classical fourth-order Runge-Kutta method
function [t,y] = rk(yprime, tspan, y0, h)
t0 = tspan(1); tfinal = tspan(end);
% set up the t values at which we will approximate the solution
t = [t0:h:tfinal]';
% include tfinal even if h does not evenly divide tfinal-t0
if t(end)~=tfinal, t=[t tfinal]; end
m = length(t);
y = [y0 zeros(length(y0), length(t)-1)];
for i=1:(m-1)
k1 = feval(yprime,t(i),y(:,i));
k2 = feval(yprime,t(i)+0.5*h, y(:,i)+(h.*k1)/2);
k3 = feval(yprime,t(i)+0.5*h, y(:,i)+(h.*k2)/2);
k4 = feval(yprime,t(i)+h, y(:,i)+h.*k3);
y(:,i+1) = y(:,i)+(h*(k1+2*k2+2*k3+k4))/6;
end
and I call it with this:
yprime=rk(#(t,x) rhs(t,x,force));
tspan = [0 10];
y0 = 0;
h = 0.1;
[t,y] = rk(yprime, tspan, y0, h);
figure(1), clf
plot(t,y,'b.','markersize',15);

Related

Negative values obtained in the solution of the 1D advection-dispersion equation using FD method

I am trying to solve the 1D ADE
This is my code so far:
clc; clear; close all
%Input parameters
Ao = 1; %Initial value
L = 0.08; %Column length [m]
nx = 40; %spatial gridpoints
dx = L/nx; %Length step size [m]
T = 20/24; %End time [days]
nt = 100; %temporal gridpoints
dt = T/nt; %Time step size [days]
Vel = dx/dt; %Velocity in each cell [m/day]
alpha = 0.002; %Dispersivity [m]
De = alpha*Vel; % Dispersion coeff. [m2/day]
%Gridblocks
x = 0:dx:L;
t = 0:dt:T;
%Initial and boundary conditions
f = #(x) x; % initial cond.
% boundary conditions
g1 = #(t) Ao;
g2 = #(t) 0;
%Initialization
A = zeros(nx+1, nt+1);
A(:,1) = f(x);
A(1,:) = g1(t);
gamma = dt/(dx^2);
beta = dt/dx;
% Implementation of the explicit method
for j= 1:nt-1 % Time Loop
for i= 2:nx-1 % Space Loop
A(i,j+1) = (A(i-1,j))*(Vel*beta + De*gamma)...
+ A(i,j)*(1-2*De*gamma-Vel*beta) + A(i+1,j)*(De*gamma);
end
% Insert boundary conditions for i = 1 and i = N
A(2,j+1) = A(1,j)*(Vel*beta + De*gamma) + A(2,j)*(1-2*De*gamma-Vel*beta) + A(3,j)*(De*gamma);
A(nx,j+1) = A(nx-1,j)*(Vel*beta + 2*De*gamma) + A(nx,j)*(1-2*De*gamma-Vel*beta)+ (2*De*gamma*dx*g2(t));
end
figure
plot(t, A(end,:), 'r*', 'MarkerSize', 2)
title('A Vs time profile (Using FDM)')
xlabel('t'),ylabel('A')
Now, I have been able to solve the problem using MATLAB’s pdepe function (see plot), but I am trying to compare the result with the finite difference method (implemented in the code above). I am however getting negative values of the dependent variable, but I am not sure what exactly I could be doing wrong. I therefore will really appreciate if anyone can help me out here. Many thanks in anticipation.
PS: I can post the code I used for the pdepe if anyone would like to see it.

Slow time integration of system ODE under specific conditions

I am trying to solve the system of ODEs below. I am applying the finite differences to z, so I get a system of ODE that I can solve with a solver like ode45.
The problem is that under the conditions that interest me integration is too slow (time step is very small) and I get no result. I would like to know what is the underlying problem and if there is any way to solve it. I have tried:
using different solvers (tried all of them)
using dimensionless variables
using the pdepe function of Matlab to solve the PDE system directly without applying finite differences myself. Same problem occurs.
Full code (using the parameters that do not work)
function transport_model()
global parameter L Di epsb Q Cfeed K tfinal npz npt F ui h
parameter = [0.044511*432.75 432.75]; % isotherm parameters
L = 0.4; % m, column length
epsb = 0.367; % column bulk porosity
ui = 4e4; % m/s, velocity
Cfeed = 200; % feed concentration
K = 0.0782 % s-1, mass transfer coefficient
tfinal = 400; % min, final time for calculation
npz = 50; % number of discretization points in z
npt = 20;
F = (1-epsb)/epsb;
tspan = 0:tfinal/(npt-1):tfinal;
y0 = zeros(2*npz,1);
h = L/(npz-1);
sol = ode15s(#sedo, tspan, y0);
t = sol.x;
C1 = sol.y(1:npz,:);
q1 = sol.y((npz+1):end,:);
plot(t, C1(end,:))
xlim([0 tfinal])
ylim([0 Cfeed])
function DyDt = sedo(t,y)
global Cfeed K npz F ui h
N = npz;
y(1) = Cfeed;
DyDt = zeros(2*N,1);
% Forward finite differences
DyDt(1) = -ui * 1/(2*h)*(-y(3)+4*y(2)-3*y(1)) - F * K * (y(1) - ilangmuir(y(N+1)));
DyDt(N+1) = K * (y(1) - ilangmuir(y(N+1)));
% Central finite differences
for i=2:N-1
DyDt(i) = -ui * 1/(2*h)*(y(i+1)-y(i-1)) - F * K * (y(i) - ilangmuir(y(N+i)));
DyDt(N+i) = K * (y(i) - ilangmuir(y(N+i)));
end
% Backward finite differences
DyDt(N) = -ui * 1/(2*h)*(3*y(N)-4*y(N-1)+y(N-2)) - F * K * (y(N) - ilangmuir(y(N+N)));
DyDt(2*N) = K * (y(N) - ilangmuir(y(N+N)));
function c = ilangmuir(q)
% langmuir solved for c
global parameter
a = parameter(1);
b = parameter(2);
c = q /(a - b * q);
However, if I try to model a different case (different conditions) I get the correct response. So I am assuming this is a numerical problem caused by the conditions I am trying to model and not an error in the model. For instance, if I use the conditions below I get the correct response.
parameter = [0.044511*432.75 432.75]; % isotherm parameters
L = 0.4; % m, column length
epsb = 0.367; % column bulk porosity
ui = 0.0056; % m/s, velocity
Cfeed = 0.0025; % feed concentration
K = 0.0782 % s-1, mass transfer coefficient
tfinal = 4000; % min, final time for calculation
npz = 50; % number of discretization points in z
npt = 20;

Solving the Damped Harmonic Oscillator ODE as a first order system using midpoint methods

The exact solution of the damped harmonic oscillator
$$x'' + 2\gamma x' + \omega^2 x = 0, \quad x(0)=x_0, \quad x'(0)=-\gamma x_0$$
with $0 < \gamma < \omega$ is
$$x(t)= x_0 e^{-\gamma t} \cos(\beta t) \quad \text{where} \quad \beta:=\sqrt{\omega^2 - \gamma^2}$$
Notice that this second order ODE can be written as a first order system by making the substitutions:
$x' = y$ and,
$y' = -2\gamma y - \omega^2 x$
I want to solve the system using the method:
$$\dfrac{ x_{n+1} - x_{n-1} }{2h} = y_n \quad \quad \dfrac{y_{n+1} - y_{n-1}}{2h} = -2\gamma y_n - \omega^2 x_n.$$
which is an explicit midpoint rule. This is the code that I constructed for the problem, but it is not giving me the correct result. My plot has no harmonic behavior as I would anticipate.
function resonance
omega = 1; % resonant frequency = sqrt(k/m)
a = 0.2; % drag coeficient per unit mass
b = 0.1; % driving amplitude per unit mass
omega0 = 1.2; % driving frequency
tBegin = 0; % time begin
tEnd = 80; % time end
x0 = 0.2; % initial position
v0 = 0.8; % initial velocity
a = omega^2; % calculate a coeficient from resonant frequency
% Use Runge-Kutta 45 integrator to solve the ODE
[t,w] = ode45(#derivatives, [tBegin tEnd], [x0 v0]);
x = w(:,1); % extract positions from first column of w matrix
v = w(:,2); % extract velocities from second column of w matrix
plot(t,x);
title('Damped, Driven Harmonic Oscillator');
ylabel('position (m)');
xlabel('time (s)');
% Function defining derivatives dx/dt and dv/dt
% uses the parameters a, b, A, omega0 in main program but changeth them not
function derivs = derivatives(tf,wf)
xf = wf(1); % wf(1) stores x
vf = wf(2); % wf(2) stores v
dxdt = vf; % set dx/dt = velocity
dvdt = xf + 2 * b * vf + a * tf; % set dv/dt = acceleration
derivs = [dxdt; dvdt]; % return the derivatives
end
end
Also, my apologies about the formatting. I am use to math stackexchange, and the LaTeX style formatting doesn't seem to be applicable here and I do not know how to put my math in the math environment.
You missed a sign, it should be
dvdt = - ( xf + 2 * b * vf + a * tf ); % set dv/dt = acceleration
However, the whole expression is at odds with the previously stated equation,
x'' + 2*b*x' * a*x = 0
should result in
dvdt = - ( 2*b*vf + a*xf ); % set dv/dt = acceleration
But then again you have defined a twice, so change w2=omega^2 to get
dvdt = - ( 2*b*vf + w2*xf + a ); % set dv/dt = acceleration

MATLAB - passing a sinusoidal forcing function to ode45

I'm new to Matlab and am really struggling even to get to grips with the basics.
I've got a function, myspring, that solves position and velocity of a mass/spring system with damping and a driving force. I can specify values for the spring stiffness (k), damping coefficient (c), and mass (m), in the command window prior to running ode45. What I am unable to do is to define a forcing function (even something simple like g = sin(t)) and use that as the forcing function, rather than having it written into the myspring function.
Can anyone help? Here's my function:
function pdot = myspring(t,p,c,k,m)
w = sqrt(k/m);
g = sin(t); % This is the forcing function
pdot = zeros(size(p));
pdot(1) = p(2);
pdot(2) = g - c*p(2) - (w^2)*p(1);
end
and how I'm using it in the command window:
>> k = 2; c = 2; m = 4;
>> tspan = linspace(0,10,100);
>> x0 = [1 0];
>> [t,x] = ode45(#(t,p)myspring(t,p,c,k,m),tspan,x0);
That works, but what I want should look something like this (I imagine):
function pdot = myspring(t,p,c,k,m,g)
w = sqrt(k/m);
pdot = zeros(size(p));
pdot(1) = p(2);
pdot(2) = g - c*p(2) - (w^2)*p(1);
end
Then
g = sin(t);
[t,x] = ode45(#(t,p)myspring(t,p,c,k,m,g),tspan,x0);
But what I get is this
In an assignment A(:) = B, the number of elements in A and B must be the same.
Error in myspring (line 7)
pdot(2) = g - c*p(2) - (w^2)*p(1);
Error in #(t,p)myspring(t,p,c,k,m,g)
Error in odearguments (line 87)
f0 = feval(ode,t0,y0,args{:}); % ODE15I sets args{1} to yp0.
Error in ode45 (line 115)
odearguments(FcnHandlesUsed, solver_name, ode, tspan, y0, options, varargin);
Horchler, thank you for the reply. I can do as you suggested and it works. I am now faced with another problem that I hope you could advise me on.
I have a script that calculates the force on a structure due to wave interaction using Morison's equation. I gave it an arbitrary time span to begin with. I would like to use the force F that script calculates as the input driving force to myspring. Here is my Morison script:
H = 3.69; % Wave height (m)
A = H/2; % Wave amplitude (m)
Tw = 9.87; % Wave period (s)
omega = (2*pi)/Tw; % Angular frequency (rad/s)
lambda = 128.02; % Wavelength (m)
k = (2*pi)/lambda; % Wavenumber (1/m)
dw = 25; % Water depth (m)
Cm = 2; % Mass coefficient (-)
Cd = 0.7; % Drag coefficient (-)
rho = 1025; % Density of water (kg/m^3)
D = 3; % Diameter of structure (m)
x = 0; % Fix the position at x = 0 for simplicity
t = linspace(0,6*pi,n); % Define the vector t with n time steps
eta = A*cos(k*x - omega*t); % Create the surface elevation vector with size equal to t
F_M = zeros(1,n); % Initiate an inertia force vector with same dimension as t
F_D = zeros(1,n); % Initiate a drag force vector with same dimension as t
F = zeros(1,n); % Initiate a total force vector with same dimension as t
fun_inertia = #(z)cosh(k*(z+dw)); % Define the inertia function to be integrated
fun_drag = #(z)(cosh(k*(z+dw)).*abs(cosh(k*(z+dw)))); % Define the drag function to be integrated
for i = 1:n
F_D(i) = abs(((H*pi)/Tw) * (1/sinh(k*dw)) * cos(k*x - omega*t(i))) * ((H*pi)/Tw) * (1/sinh(k*dw)) * cos(k*x - omega*t(i)) * integral(fun_drag,-dw,eta(i));
F_M(i) = (Cm*rho*pi*(D^2)/4) * ((2*H*pi^2)/(Tw^2)) * (1/(sin(k*dw))) * sin(k*x - omega*t(i)) * integral(fun_inertia,-dw,eta(i));
F(i) = F_D(i) + F_M(i);
end
Any further advice would be much appreciated.
You can't pre-calculate your forcing function. It depends on time, which ode45 determines. You need to define g as a function and pass a handle to it into your integration function:
...
g = #(t)sin(t);
[t,x] = ode45(#(t,p)myspring(t,p,c,k,m,g),tspan,x0);
And then call it I n your integration function, passing in the current time:
...
pdot(2) = g(t) - c*p(2) - (w^2)*p(1);
...

Using ode45 in Matlab

I'm trying to simulate the time behavior for a physical process governed by a system of ODEs. When I switch the width of the input pulse from 20 to 19, there is no depletion of the y(1) state, which doesn't make sense physically. What am I doing wrong? Am I using ode45 incorrectly?
function test
width = 20;
center = 100;
tspan = 0:0.1:center+50*(width/2);
[t,y] = ode45(#ODEsystem,tspan,[1 0 0 0]);
plot(t,y(:,1),'k*',t,y(:,2),'k:',t,y(:,3),'k--',t,y(:,4),'k');
hold on;
axis([center-3*(width/2) center+50*(width/2) -0.1 1.1])
xlabel('Time')
ylabel('Relative values')
legend({'y1','y2','y3','y4'});
function dy = ODEsystem(t,y)
k1 = 0.1;
k2 = 0.000333;
k3 = 0.1;
dy = zeros(size(y));
% rectangular pulse
I = rectpuls(t-center,width);
% ODE system
dy(1) = -k1*I*y(1);
dy(2) = k1*I*y(1) - k2*y(2);
dy(3) = k2*y(2) - k3*I*y(3);
dy(4) = k3*I*y(3);
end
end
You are changing the parameters of your your ODEs discontinuously in time. This results in a very stiff system and less accurate, or even completely wrong, results. In this case, because the your ODE is so simple when I = 0, an adaptive solver like ode45 will take very large steps. Thus, there's a high probability that it will step right over the region where you inject the impulse and never see it. See my answer here if you're confused as to why the code in your question misses the pulse even though you've specified tspan to have (output) steps of just 0.1.
In general it is a bad idea to have any discontinuities (if statements, abs, min/max, functions like rectpuls, etc.) in your integration function. Instead, you need to break up the integration and calculate your results piecewise in time. Here's a modified version of your code that implements this:
function test_fixed
width = 19;
center = 100;
t = 0:0.1:center+50*(width/2);
I = rectpuls(t-center,width); % Removed from ODE function, kept if wanted for plotting
% Before pulse
tspan = t(t<=center-width/2);
y0 = [1 0 0 0];
[~,out] = ode45(#(t,y)ODEsystem(t,y,0),tspan,y0); % t pre-calculated, no need to return
y = out;
% Pulse
tspan = t(t>=center-width/2&t<=center+width/2);
y0 = out(end,:); % Initial conditions same as last stage from previous integration
[~,out] = ode45(#(t,y)ODEsystem(t,y,1),tspan,y0);
y = [y;out(2:end,:)]; % Append new data removing identical initial condition
% After pulse
tspan = t(t>=center+width/2);
y0 = out(end,:);
[~,out] = ode45(#(t,y)ODEsystem(t,y,0),tspan,y0);
y = [y;out(2:end,:)];
plot(t,y(:,1),'k*',t,y(:,2),'k:',t,y(:,3),'k--',t,y(:,4),'k');
hold on;
axis([center-3*(width/2) center+50*(width/2) -0.1 1.1])
xlabel('Time')
ylabel('Relative values')
legend({'y1','y2','y3','y4'});
function dy = ODEsystem(t,y,I)
k1 = 0.1;
k2 = 0.000333;
k3 = 0.1;
dy = zeros(size(y));
% ODE system
dy(1) = -k1*I*y(1);
dy(2) = k1*I*y(1) - k2*y(2);
dy(3) = k2*y(2) - k3*I*y(3);
dy(4) = k3*I*y(3);
end
end
See also my answer to a similar question.