Using Improved Euler Method in Matlab - matlab

I am trying to solve a 2nd order differential equation in Matlab. I was able to do this using the forward Euler method, but since this requires quite a small time step to get accurate results I have looked into some other options. More specifically the Improved Euler method (Heun's method).
I understand the principle of Improved Euler method, that it first estimates the velocity and then uses that information to correct it to the current condition. But I am not totally sure if what I have written is totally correct.
1)Can you check if my code utilizes the Improved Euler method correctly?
2)In my code, the last line before the end, the second B(ii) should be B(ii+1)?
I have written a simplified code for both options. Here it is:
t = 0:0.01:100;
dt = t(2)-t(1); % Time step
%Constants%
M = 20000;
m_a = 10000;
c= 15000;
k_spring = 40000;
B = rand(1,length(t)+1);
%% Forward Euler Method %%
x = zeros(1,length(t)+1); % Pre-allocation
u = zeros(1,length(t)+1); % Pre-allocation
x(1) = 1; % Initial condition
u(1) = 0; % Initial condition
for ii = 1:length(t)
x(ii+1) = x(ii) + dt*u(ii);
u(ii+1) = u(ii) + dt * ((1/(M+m_a)) * -(c+k_spring+B(ii))*x(ii));
end
%% Improved Euler Method %%
x1 = zeros(1,length(t)+1); % Pre-allocation
u1 = zeros(1,length(t)+1); % Pre-allocation
x1(1) = 1; % Initial condition
u1(1) = 0; % Initial condition
for ii = 1:length(t)
x1(ii+1) = x1(ii) + dt*u1(ii);
u1(ii+1) = u1(ii) + dt * ((1/(M+m_a)) * -(c+k_spring+B(ii))*x1(ii)); %Estimate
u1(ii+1) = u1(ii) + (dt/2) * ( ((1/(M+m_a)) * -(c+k_spring+B(ii))*x1(ii)) + ((1/(M+m_a)) * -(c+k_spring+B(ii))*x1(ii+1)) ); %Correction
end
Thanks!

You should follow the principal programming idea to make things that are used repeatedly into extra procedures. Suppose you did so and the function evaluating the ODE function is called odefunc.
function [dotx, dotu] = odefunc(x,u,B)
dotx = u;
dotu =(1/(M+m_a)) * -(c+k_spring+B)*x);
end
Then
for ii = 1:length(t)
%% Predictor
[dotx1,dotu1] = odefunc(x1(ii), u1(ii), B(ii));
%% One corrector step
[dotx2,dotu2] = odefunc(x1(ii)+dotx1*dt, u1(ii)+dotu1*dt, B(ii+1));
x1(ii+1) = x1(ii) + 0.5*(dotx1+dotx2)*dt;
u1(ii+1) = u1(ii) + 0.5*(dotu1+dotu2)*dt;
end

Related

How to avoid tf() command using State-space models in Matlab

I'm trying to avoid the function tf() from Matlab since it requires specific toolboxes to run.
The transfer function that I'm using is quite simple. Is the model for a heatsink temperature.
H(s) = (Rth/Tau)/(s + 1/Tau)
In order to avoid the tf() function, I've tried to substitute the transfer function with a state space model coded in Matlab.
I've used the function ss() to get te values of A,B,C and D. And I've tried to compare the results from tf() and my function.
Here's the code that I've used:
Rth = 8.3220e-04; % ºC/W
Tau = 0.0025; % s
P = rand(1,10)*1000; % Losses = input
t = 0:1:length(P)-1; % Time array
%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Transfer function %%%
%%%%%%%%%%%%%%%%%%%%%%%%%
H = tf([0 Rth/Tau],[1 1/Tau]);
Transfer_func = lsim(H,P,t);
figure, plot(Transfer_func),grid on,grid minor, title('Transfer func')
%%%%%%%%%%%%%%%%%%%%%%%%%
%%% My función ss %%%
%%%%%%%%%%%%%%%%%%%%%%%%%
% Preallocate for speed
x(1:length(P)) = 0;
y(1:length(P)) = 0;
u = P;
sys = ss(H);
A = sys.A;
B = sys.B;
C = sys.C;
D = sys.D;
for k = 1:length(u)
x(k+1) = A*x(k) + B*u(k);
y(k) = C*x(k) + D*u(k);
end
figure, plot(y), grid on,grid minor, title('With my función')
I know that the values from A,B,C and D are ok, since I've checked them using
H = tf([0 Rth/Tau],[1 1/Tau]);
sys = ss(H);
state_space_sys = ss(sys.A,sys.B,sys.C,sys.D);
state_space = lsim(state_space_sys,P,t);
figure, plot(state_space),grid on,grid minor, title('State space')
As you can see, the results obtained from my funtion and the function tf() are very different.
Is there any mistakes on the approach?
If it's not possible to avoid the tf() function in this way, is there any other way?
At the end, I found another solution. I'm posting this here, so if someone has the same problem, can use this approach.
If you take the transfer function, and develop it, we reach to the following expresion
H(s) = deltaT(s)/P(s) = (Rth/Tau)/(s + 1/Tau)
deltaT(s) * (s + 1/Tau) = (Rth/Tau) * P(s)
deltaT(s) * s = (Rth/Tau) * P(s) - deltaT(s)/Tau
Now, we know that 1/s is equal to integrate. So in the end, we have to integrate the right side of the equation. The code would be like this.
Cth = Tau/Rth;
deltaT = zeros(size(P));
for i = 2:length(P)
deltaT(i) = (1/Cth * (P(i)-deltaT(i-1)/Rth))*(time(i)-time(i-1)) + deltaT(i-1);
end
This integral has the same output as the function tf().

I can't fix the problem of my PID simulation code(I think it's about difference of the differentiator)

I'm trying to build a PID controller, its differential part multiply by a low-pass filter, witch transform func is 1/(r*s+1) (r = 4*Ts ).
I use forward difference to discrete the controller, and use state space function to handle the object, I want a step response of this system.
And I used pid() feedback() step() functions to check if I'm right.
And yet I was wrong, here is the response.
I tried only PI control.
You can see it's almost perfect. So I think the problem is about differential. But I really can't find it out. I already have checked the forward difference many times.
I tried backward difference and got same curve.
T = 0.05;
Cp = 6;
Ci = 1;
Cd = 7;
P = zeros(1, n+1);
I = zeros(1, n+1);
D = zeros(1, n+1);
r = 4*T;
for i = 1:n
if i == 1
e(i) = U(i)-0; % error of time domain, U is step function
P(i) = Cp*e(i);
I(i) = 0;
D(i) = 0;
else
e(i) = U(i)-y(i-1);
P(i) = Cp*e(i);
I(i) = Ci*T*e(i-1)+I(i-1); % forward
D(i) = (Cd*(e(i)-e(i-1))-D(i-1)*(T-r))/r; % forward
% I(i) = T*Ci*e(i)+I(i-1); % backward
% D(i) = (Cd*(e(i)-e(i-1))+r*D(i-1))/(r+T); % backward
% I(i) = (T*(e(i)+e(i-1))+2*I(i-1))/2; % Bilinear
end
u(i) = P(i) + I(i) + D(i);
x(:,i+1) = x(:,i)+T*(Ao*x(:,i)+Bo*u(i));
y(i) = Co*x(:,i);
end
% this how I get the right curve.
PIDF = pid(Cp, Ci, Cd, r);
fb = feedback(PIDF*G_sysc, 1); % G_sysc is trans func of object.
step(fb, N(1:n)*T, 'blue');
I expect should be almost same with the Step Response.
Alright... I have fingered why it's different out by myself. Because the function feedback() set the error as 0 at the first point, and next point, the error would be step function - output = 1, so the differential will be a big positive, that cause the output convergence quicker than my PID controller.
Now I only want to know how do I edit the pid() and feedback() functions to set the first error to be 1, cause this is more common sense.

midpoint rule for matlab

Hello I was asked to create a matlab code for the midpoint rule. What I have is the code for eulers method, so I have to make some modifications, but I am struggling to do it I have the following
function H = heun(f,a,b,ya,M)
h = (b-a)/M;
T = zeros(1,M+1);
Y = zeros(1,M+1);
T = a:h:b;
Y(1) = ya;
for j = 1 : M
k1=feval(f,T(j),Y(j));
k2=feval(f,T(j+1),Y(j)+h*k1);
Y(j+1)=Y(j)+(h/2)*(k1+k2);
end
H = [T' Y'];
function f = dif1(t,y)
f=(t-y)/2;
So yea my function is y'=(t-y)/2 and the interval and other stuff i define in the command window..
If it is possible to make the for loop into a mid point rule I think that is the way to go, any help would be appreciated.
Below is an implementation in MATLAB I have done of the Euler's Method for solving a pair of coupled 1st order DE's. It solves a harmonic oscillator of represented by the following:
y1(t+h) = y1(t) + h*y2(t)
y2(t+h) = y2(t) + h*(-A/M y1(t) -B/M y1(t)/|y1(t)|)
% Do the integration using the Euler Method
while(T<=T1)
% Update the position of the pt mass using current velocity
Y1(i+1) = Y1(i) + H*Y2(i);
% Update the velocity of the pt mass checking if we are turning
% within the C/A band
if ( (abs(Y2(i) < th) && abs(Y1(i)) < C/A) )
Y2(i+1) = Y2(i);
else
Y2(i+1) = Y2(i) + H * ( ((-A/M)*Y1(i)) - (B/M)*sign(Y2(i)) );
end
% Incriment the time by H
T = T + H;
% Increase the looping index variable
i = i + 1;
end
Without explicitly solving YOUR homework question, I hope this example helps. A few things to note: the if statement is accounting for static friction in this particular example -- so disregard that and only look at what happens after the 'else'.
Also note that I have initial conditions y1(0) and y2(0) defined as Y1(i=1) and Y2(i=1), so starting with Yj(i+1) gives Yj(2). Note that T1 is the ending time of the simulation.
Below is the same problem using the Improved Euler Method. If you derive the update equations for this system you should be able to walk through the code.
% Do the integration using the Improved Euler Method
% Ki_j = K^i_j
while(T<=T1)
% Calculate K^i_j's
K1_1 = Y2(i);
% Must check if we are turning within C/A
if ( (abs(Y2(i) < th) && abs(Y1(i)) < C/A) )
K1_2 = 0;
else
K1_2 = (-A/M)*Y1(i) - (B/M)*sign(Y2(i));
end
K2_1 = Y2(i)+H*K1_2;
% Checking if we are turning within C/A
if ( (abs(Y2(i) < th) && abs(Y1(i)) < C/A) )
K2_2 = 0;
else
K2_2 = (-A/M)*(Y1(i) + H*K1_1) - (B/M)*sign(Y2(i)+ H*K1_2);
end
% Update the position and velocity
Y1(i+1) = Y1(i)+ (H/2)*(K1_1+K2_1);
Y2(i+1) = Y2(i) + (H/2)*(K1_2+K2_2);
% Incriment the time by H
T = T + H;
% Increase the looping index variable
i = i + 1;
end

Solving Coupled partial differential equations of stiff nature using MATLAB

I want to solve coupled partial differential equations of first order, which are of stiff nature. I have coded in MATLAB to solve this pde's, I have used Method of line to convert PDE into ODE, and i have used beam and warmings(second order upwind) method to discritize the spatial derivative. The discretization method is total variation diminishing(TVD) to eliminate the oscillation. But rather using TVD and ode15s solver to integrate resultant stiff ode's the resultant plot is oscillatory(not smooth). What should i do to eliminate this oscillation and get correct results.
I have attached my MATLAB code.. please see it and suggest some improvement.
∂y(1)/∂t=-0.1 ∂y(1)/∂x + (0.5*e^(15*(y(2)⁄(1+y(2))))*(1- y(1))
∂y(2)/∂t=-0.1 ∂y(2)/∂x - (0.4*e^(15*(y(2)⁄(1+y(2))))*(1- y(1))-0.4
Initial condition: at t = 0 y(1)= y(2)=0
Boundary condition: y(1)= y(2) = 0 at x=0
I have attached my MATLAB code.. please see it and suggest some improvement.
function brussode(N)
if nargin<1
N = 149;
end
tspan = [0 10];
m = 0.00035
t = (1:N)/(N+1)*m;
y0 = [repmat(0,1,N); repmat(0,1,N)];
p = 0.5
q = 0.4
options = odeset('Vectorized','on','JPattern',jpattern(N));
[t,y] = ode15s(#f,tspan,y0,options);
a = size(y,2)
u = y(:,1:2:end);
x = (1:N)/(N+1);
figure;
%surf(x,t(end,:),u);
plot(x,u(end,:))
xlabel('space');
ylabel('solution');
zlabel('solution u');
%--------------------------------------------------------------
%Nested function -- N is provided by the outer function.
%
function dydt = f(t,y)
%Derivative function
dydt = zeros(2*N,size(y,2)); %preallocate dy/dt
x = (1:N)/(N+1);
% Evaluate the 2 components of the function at one edge of the grid
% (with edge conditions).
i = 1;
%y(1,:) = 0;
%y(2,:) = 0;
dydt(i,:) = -0.1*(N+1)*(y(i+2,:)-0)+ (0.01/2)*m*((N+1).^3)*(y(i+2,:)-0) + p*exp(15*(0/(1+0)))*(1-0);
dydt(i+1,:) = -0.1*(N+1)*(y(i+3,:)-0)+ (0.01/2)*m*((N+1).^3)*(y(i+3,:)-0) - q*exp(15*(0/(1+0)))*(1-0)+0.25;
i = 3;
%y(1,:) = 0;
%y(2,:) = 0;
dydt(i,:) = -0.1*(N+1)*(y(i+2,:)-y(i,:)) + (0.01/2)*m*((N+1).^3)*(y(i+3,:)-y(i,:)) + p*exp(15*(y(i+1,:)/(1+y(i+1,:))))*(1-y(i,:));
dydt(i+1,:) = -0.1*(N+1)*(y(i+3,:)-y(i+1,:)) + (0.01/2)*m*((N+1).^3)*(y(i+3,:)-y(i,:)) - q*exp(15*(y(i+1,:)/(1+y(i+1,:))))*(1-y(i,:))+0.25;
%Evaluate the 2 components of the function at all interior grid
%points.
i = 5:2:2*N;
%y(1,:) = 0;
% y(2,:) = 0;
dydt(i,:) = (-0.1/2)*(N+1)*(3*y(i,:)-4*y(i-2,:)+y(i-4,:)) +(0.01/2)*m*((N+1).^3)*(y(i,:)-2*y(i-2,:)+y(i-4,:))+ p*exp(15*(y(i+1,:)/(1+y(i+1,:))))*(1-y(i,:));
dydt(i+1,:) = (-0.1/2)*(N+1)*(3*y(i+1,:)-4*y(i-1,:)+y(i-3,:))+(0.01/2)*m*((N+1).^3)*(y(i+1,:)-2*y(i-1,:)+y(i-3,:)) - q*exp(15*(y(i+1,:)/(1+y(i+1,:))))*(1-y(i,:))+0.25;
end
%-------------------------------------------------------------
end %brussode
%-------------------------------------------------------------
% Subfunction -- the sparsity pattern
%
function S = jpattern(N)
% Jacobian sparsity patter
B = ones(2*N,5);
B(2:2:2*N,2) = zeros(N,1);
B(1:2:2*N-1,4) = zeros(N,1);
S = spdiags(B,-2:2,2*N,2*N);
end
%-------------------------------------------------------------

Solving PDE with Matlab

`sol = pdepe(m,#ParticleDiffusionpde,#ParticleDiffusionic,#ParticleDiffusionbc,x,t);
% Extract the first solution component as u.
u = sol(:,:,:);
function [c,f,s] = ParticleDiffusionpde(x,t,u,DuDx)
global Ds
c = 1/Ds;
f = DuDx;
s = 0;
function u0 = ParticleDiffusionic(x)
global qo
u0 = qo;
function [pl,ql,pr,qr] = ParticleDiffusionbc(xl,ul,xr,ur,t,x)
global Ds K n
global Amo Gc kf rhop
global uavg
global dr R nr
sum = 0;
for i = 1:1:nr-1
r1 = (i-1)*dr; % radius at i
r2 = i * dr; % radius at i+1
r1 = double(r1); % convert to double precision
r2 = double(r2);
sum = sum + (dr / 2 * (r1*ul+ r2*ur));
end;
uavg = 3/R^3 * sum;
ql = 1;
pl = 0;
qr = 1;
pr = -((kf/(Ds.*rhop)).*(Amo - Gc.*uavg - ((double(ur/K)).^2).^(n/2) ));`
dq(r,t)/dt = Ds( d2q(r,t)/dr2 + (2/r)*dq(r,t)/dr )
q(r, t=0) = 0
dq(r=0, t)/dr = 0
dq(r=dp/2, t)/dr = (kf/Ds*rhop) [C(t) - Cp(at r = dp/2)]
q = solid phase concentration of trace compound in a particle with radius dp/2
C = bulk liquid concentration of trace compound
Cp = trace compound concentration at particle surface
I want to solve the above pde with initial and boundary conditions given. Tried Matlab's pdepe, but does not work satisfactorily. Maybe the boundary conditions is creating problem for me. I also used this isotherm equation for equilibrium: q = K*Cp^(1/n). This is convection-diffusion equation but i could not find any write ups that addresses solving this type of equation properly.
There are two problems with the current implementation.
Incorrect Source Term
The PDE you are attempting to solve has the form
which has the equivalent form
where the last term arises due to the factor of 2 in the original PDE.
The last term needs to be incorporated into pdepe via a source term.
Calculation of q average
The current implementation attempts to calculate the average value of q using the left and right values of q passed to the boundary condition function.
This is incorrect.
The average value of q needs to be calculated from a vector of up-to-date values of the quantity.
However, we have the complication that the only function to receive all mesh values is ParticleDiffusionpde; however, the mesh values passed to that function are not guaranteed to be from the mesh we provided.
Solution: use events (as described in the pdepe documentation).
This is a hack since the event function is meant to detect zero-crossings, but it has the advantage that the function is given all values of q on the mesh we provide.
So, the working example below (you'll notice I set all of the parameters to 1 since I didn't know better) uses the events function to update a variable qStore that can be accessed by the boundary condition function (see here for an explanation), and the boundary condition function performs a vectorized trapezoidal integration for the average calculation.
Working Example
function [] = ParticleDiffusion()
% Parameters
Ds = 1;
q0 = 0;
K = 1;
n = 1;
Amo = 1;
Gc = 1;
kf = 1;
rhop = 1;
% Space
rMesh = linspace(0,1,10);
rMesh = rMesh(:) ;
dr = rMesh(2) - rMesh(1) ;
% Time
tSpan = linspace(0,1,10);
% Vector to store current u-value
qStore = zeros(size(rMesh));
options.Events = #(m,t,x,y) events(m,t,x,y);
% Solve
[sol,~,~,~,~] = pdepe(1,#ParticleDiffusionpde,#ParticleDiffusionic,#ParticleDiffusionbc,rMesh,tSpan,options);
% Use the events function to update qStore
function [value,isterminal,direction] = events(m,~,~,y)
qStore = y; % Value of q on rMesh
value = m; % Since m is constant, it will never be zero (no event detection)
isterminal = 0; % Continue integration
direction = 0; % Detect all zero crossings (not important)
end
function [c,f,s] = ParticleDiffusionpde(r,~,~,DqDr)
% Define the capacity, flux, and source
c = 1/Ds;
f = DqDr;
s = DqDr./r;
end
function u0 = ParticleDiffusionic(~)
u0 = q0;
end
function [pl,ql,pr,qr] = ParticleDiffusionbc(~,~,R,ur,~)
% Calculate average value of current solution
qL = qStore(1:end-1);
qR = qStore(2: end );
total = sum((qL.*rMesh(1:end-1) + qR.*rMesh(2:end))) * dr/2;
qavg = 3/R^3 * total;
% Left boundary
pl = 0;
ql = 1;
% Right boundary
qr = 1;
pr = -(kf/(Ds.*rhop)).*(Amo - Gc.*qavg - (ur/K).^n);
end
end