I'm working on the numerical integration to a discontinuous ODE in MATLAB, but I'm not getting the results I intended to get.
I have a 2-dof system of two masses connected via springs. (see 2-dof system)
My function in MATLAB is of the form:
function [ xdot ] = two_springs(t, x, m1, m2, c, g, k)
% x(1) = x1
% x(2) = x1'
% x(3) = x2
% x(4) = x2'
delta = max(x(3,1) - x(1,1), 0); % (Discontinuous)
F = k * delta^(3 / 2);
xdot(1,1) = x(2,1);
xdot(2,1) = (-F + m1 * g) / m1;
xdot(3,1) = x(4,1);
xdot(4,1) = (F - c * x(3,1) + m2 * g) / m2;
end
Afterwards I plot force F over time.
But no matter which solver I use ( ode45, ode23, ode113, ode15s, ode23s,...) I get a failure because the step size was reduced below the smallest value allowed.
The topic: MATLAB- ode solver: Unable to meet integration tolerances did unfortunately not help me.
Does anyone have an idea what else I could try?
Thank you very much for your help!
tine-lore
Related
I have stumbled upon this matlab code that solves this ODE
y'''(t) + a y(t) = -b y''(t) + u(t)
but I am confused by the ode_system function definition, specifically by the y(2) y(3) part. I would greatly appreciate if someone can shed some light
y(2) y(3) part in the ode_system function confuses me and how it contributes to overaal solution
% Define the parameters a and b
a = 1;
b = 2;
% Define the time horizon [0,1]
time_horizon = [0, 1];
% Define the initial conditions for y, y', and y''
initials = [0; 0; 0];
% Define the function handle for the input function u(t)
%sin(t) is a common example of a time-varying function.
% You can change the definition of u to any other function of time,
% such as a constant, a step function, or a more complex function, depending on your needs
u = #(t) sin(t);
% Define the function handle for the system of ODEs
odefunction = #(t, y) ode_system(t, y, a, b, u);
% Solve the ODEs using ode45
[t, y] = ode45(odefunction, time_horizon, initials);
% Plot the solution
plot(t, y(:,1), '-', 'LineWidth', 2);
xlabel('t');
ylabel('y');
function dydt = ode_system(t, y, a, b, u)
%Define the system of ODEs
dydt = [y(2); y(3); -b*y(3) + u(t) - a*y(1)];
end
This is more of a maths question than a Matlab one.
We would like to rewrite our ODE equation so that there is a single time derivative on the left-hand side and no derivatives on the right.
Currently we have:
y'''(t)+ay(t)=-by''(t)+u(t)
By letting z = y' and x = z' (= y''), we can rewrite this as:
x'(t)+a y(t)=-b x(t)+u(t)
So now we have 3 equations in the form:
y' = z
z' = x
x' = -b * x + u - a *y
We can also think of this as a vector equation where v = (y, z, x).
The right-hand side would then be,
v(1)' = v(2)
v(2)' = v(3)
v(3)' = -b * v(3) + u - a * v(1)
which is what you have in the question.
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;
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);
...
I have difficulties simulating an object discribed by the following state space equations in simulink:
The right hand side of the state space equation is described by the funcion below.
function dxdt = RHS( t, x, F)
% parameters
b = 1.22; % cart friction coeffitient
c = 0.0027; %pendulum friction coeffitient
g = 9.81; % gravity
M = 0.548+0.022*2; % cart weight
m = 0.031*2; %pendulum masses
I = 0.046;%0.02*0.025/12+0.02*0.12^2+0.011*0.42^2; % moment of inertia
l = 0.1313;
% x(1) = theta
% x(2) = theta_dot
% x(3) = x
% x(4) = x_dot
dxdt = [x(2);
(-(M+m)*c*x(2)-(M+m)*g*l*sin(x(1))-m^2*l^2*x(2)^2*sin(x(1))*cos(x(1))+m*l*b*x(4)*cos(x(1))-m*l*cos(x(1))*F)/(I*(m+M)+m*M*l^2+m^2*l^2*sin(x(1))^2);
x(4);
(F - b*x(4) + l*m*x(2)^2*sin(x(1)) + (l*m*cos(x(1))*(c*x(2)*(M + m) + g*l*sin(x(1))*(M + m) + F*l*m*cos(x(1)) + l^2*m^2*x(2)^2*cos(x(1))*sin(x(1)) - b*l*m*x(4)*cos(x(1))))/(I*(M + m) + l^2*m^2*sin(x(1))^2 + M*l^2*m))/(M + m)];
end
The coresponding rk4 function with a simple visualisation is shown below.
function [wi, ti] = rk4 ( RHS, t0, x0, tf, N )
%RK4 approximate the solution of the initial value problem
%
% x'(t) = RHS( t, x ), x(t0) = x0
%
% using the classical fourth-order Runge-Kutta method - this
% routine will work for a system of first-order equations as
% well as for a single equation
%
% calling sequences:
% [wi, ti] = rk4 ( RHS, t0, x0, tf, N )
% rk4 ( RHS, t0, x0, tf, N )
%
% inputs:
% RHS string containing name of m-file defining the
% right-hand side of the differential equation; the
% m-file must take two inputs - first, the value of
% the independent variable; second, the value of the
% dependent variable
% t0 initial value of the independent variable
% x0 initial value of the dependent variable(s)
% if solving a system of equations, this should be a
% row vector containing all initial values
% tf final value of the independent variable
% N number of uniformly sized time steps to be taken to
% advance the solution from t = t0 to t = tf
%
% output:
% wi vector / matrix containing values of the approximate
% solution to the differential equation
% ti vector containing the values of the independent
% variable at which an approximate solution has been
% obtained
%
% x(1) = theta
% x(2) = theta_dot
% x(3) = x
% x(4) = x_dot
t0 = 0; tf = 5; x0 = [pi/2; 0; 0; 0]; N = 400;
neqn = length ( x0 );
ti = linspace ( t0, tf, N+1 );
wi = [ zeros( neqn, N+1 ) ];
wi(1:neqn, 1) = x0';
h = ( tf - t0 ) / N;
% force
u = 0.0;
%init visualisation
h_cart = plot(NaN, NaN, 'Marker', 'square', 'color', 'red', 'LineWidth', 6);
hold on
h_pend = plot(NaN, NaN, 'bo', 'LineWidth', 3);
axis([-5 5 -5 5]);
axis manual;
xlim([-5 5]);
ylim([-5 5]);
for i = 1:N
k1 = h * feval ( 'RHS', t0, x0, u );
k2 = h * feval ( 'RHS', t0 + (h/2), x0 + (k1/2), u);
k3 = h * feval ( 'RHS', t0 + h/2, x0 + k2/2, u);
k4 = h * feval ( 'RHS', t0 + h, x0 + k3, u);
x0 = x0 + ( k1 + 2*k2 + 2*k3 + k4 ) / 6;
t0 = t0 + h;
% model output
wi(1:neqn,i+1) = x0';
% model visualisation
%plotting cart
l = 2;
set(h_cart, 'XData', x0(3), 'YData', 0, 'LineWidth', 5);
%plotting pendulum
%hold on;
set(h_pend, 'XData', sin(x0(1))*l+x0(3), 'YData', -cos(x0(1))*l, 'LineWidth', 2);
%hold off;
% regulator
pause(0.02);
end;
figure;
plot(ti, wi);
legend('theta', 'theta`', 'x', 'x`');
This gives realistic looking results for a pendulum on a cart.
Now to the problem.
I wanted to recreate the exact same equations in simulink. I thought it is going to be as easy as creating the following simulink model.
where I fill the fcn blocks with the second and fourth equation from the RHS file. Like this.
(-(M+m)*c*u(2)-(M+m)*g*l*sin(u(1))-m^2*l^2*u(2)^2*sin(u(1))*cos(u(1))+m*l*b*u(3)*cos(u(1))-m*l*cos(u(1))*u(4))/(I*(m+M)+m*M*l^2+m^2*l^2*sin(u(1))^2)
(u(5) - b*u(4) + l*m*u(2)^2*sin(u(1)) + (l*m*cos(u(1))*(c*u(2)*(M + m) + g*l*sin(u(1))*(M + m) + u(5)*l*m*cos(u(1)) + l^2*m^2*u(2)^2*cos(u(1))*sin(u(1)) - b*l*m*u(4)*cos(u(1))))/(I*(M + m) + l^2*m^2*sin(u(1))^2 + M*l^2*m))/(M + m)
The problem is this doesn't give the correct results from above, but the one below
Does anybody know what I do incorrectly?
Edit:After #am304 comment I decided to add the following information. I changed the setting for the simulink solver to use the fixed-step rk4 solver, so as to get the same results. The second integrator3 from the model above has been initialized to pi/2.
Edit2: If somebody wants to check out the simulink model for themselves click on the link to download the file.
Edit3: As you can read in the answer below the problem was trivial. You can download the correct model here
I looked through your Simulink model and it seems you may have mixed up the two functions you were using. You used the theta_dd function where you meant to put x_dd and vice versa.
In your model, you also force x_d to be set to a constant value 0. I assume you actually meant to set the initial condition to 0, which you can see is done via the Integrator block. x_d (as an input to f) should be your state vector which is also an output of your integrators. This is just a consequence of what you define x_d to be, the integral of x_dd. This is how RK4 works as well; you use an initial state vector first and then use the predicted state vector to drive the next RK4 step.
The resulting output from the scope (i've outputted your whole state vector here) is as follows and looks like what you expect:
I do not think I should link externally to the simulink file so if you would like a copy of the file you can open a chat and ask for it. Otherwise the picture above should be sufficient enough to help you reproduce the same results.
I'm trying to write an implementation of Newton algorithm in Matlab.
When I call up my function using formula:
result = NewtonMethod(x(1).^2 - 2.1*x(1).^4 + (x(1).^6)/3 + x(1)*x(2) - 4*x(2).^2 + 4*x(2).^4, [0 0], 0.1, 10)
I've got an error message:
??? Undefined function or method 'hessian' for input arguments of type 'double'.
Error in ==> NewtonMethod at 13
H = hessian(f, x0);
I've got no idea what is wrong. Maybe someone more familiar with Matlab can help me.
Below it's my code:
function xnext = NewtonMethod(f, x0, eps, maxSteps)
% x0 - starting point (2 – dimensional vector)
% H - matrix of second derivatives (Hessian)
% eps - required tolerance of calculations
% maxSteps - length of step
x = x0;
for n=1:maxSteps
% determine the hessian H at the starting point x0,
H = hessian(f, x0);
% determine the gradient of the goal function gradf at the point x,
gradF = gradient(f, x);
% determine next point
xnext = x - inv(H) * x * gradF;
if abs(xnext - x) < eps
return %found
else
x = xnext; %update
end
end
It's my first contact with Matlab.
Update:
Now I've got an error:
??? Error using ==> mupadmex
Error in MuPAD command: Index exceeds matrix dimensions.
Error in ==> sym.sym>sym.subsref at 1381
B = mupadmex('symobj::subsref',A.s,inds{:});
I typed:
syms x
result = NewtonMethod(x(1).^2 - 2.1*x(1).^4 + (x(1).^6)/3 + x(1)*x(2) - 4*x(2).^2 + 4*x(2).^4, [0 0], 0.1, 10)
x(1).^2 - 2.1*x(1).^4 + (x(1).^6)/3 + x(1)*x(2) - 4*x(2).^2 + 4*x(2).^4
Is reduced to a double before the NewtonMethod function is called, so when your code reaches hessian(f, x0), you're passing it two double arguments, which is not a supported syntax.
Review the notes on properly specifying a symbolic function, and pass that into NewtonMethod.
It's been a long time since I've done numerical optimization, but take a look at the following:
function xn = NewtonMethod(f, x0, eps, maxSteps)
% x0 - starting point (2 – dimensional vector)
% H - matrix of second derivatives (Hessian)
% eps - required tolerance of calculations
% maxSteps - length of step
syms x y
H = hessian(f);
gradF = gradient(f);
xi = x0;
for i=1:maxSteps
% evaluate f at xi
zi = subs(f, [x,y], xi);
% determine the hessian H at the starting point x0,
hi = subs(H, [x,y], xi);
% determine the gradient of the goal function gradf at the point x,
gi = subs(gradF, [x,y], xi);
% determine next point
ss = 0.5; % step size
xn = xi - ss.* (inv(hi) * gi);
% evaluate f at xn
zn = subs(f, [x,y], xn);
% some debugging spam
zd = zn - zi; % the change in the value of the
si = sprintf('[%6.3f, %6.3f]', xi); % function from xi -> xn
sn = sprintf('[%6.3f, %6.3f]', xn);
printf('Step %3d: %s=%9.4f -> %s=%9.4f : zd=%9.4f\n', i, si, zi, sn, zn, zd);
% stopping condition
if abs(xi - xn) < eps
return %found
else
xi = xn; %update
end
end
And called with
result = NewtonMethod(f, [0; 1], 0.001, 100)