In how-do-i-solve-an-ode-with-time-dependent-parameters-in-matlab
Q: Consider the following ODE with time-dependent parameters:
y'(t) + f(t)y(t) = g(t)
and the given initial condition:
y(0) = 1
This is an example of an ODE with time-dependent terms. Suppose the time-dependent terms are defined only through the set of data points given in two vectors. Which of the MATLAB ODE solvers should I use, and how do I set up this problem?
A: This enhancement has been incorporated in Release 14 Service Pack 3 (R14SP3). For previous product releases, read below for any possible workarounds
My question one is: I use Release 14 Service Pack 3 (R14SP3) indeed. But the answer does not mention how to deal with it. How can I implement time dependent parameters in ODE45?
My question two is: Because I do not know how to implement time dependent parameters in ODE45 in (R14SP3). I plan to use the old method of interpolation in the answer of that page. But why do we need interpolation as we already know the exact form of time-dependent parameters.
My question three is: Jan Simon in the comment points out its flaw of interpolation. I have implemented the method suggested by Jan Simon using "integration per loop". But Jan Simon's method treat the variable as constant inside ODE45. So How to do integration inside the ode45 "h" intervals as well? I know how to do integration in my defined interval, but the ode45 will treat the variable as constant when running its own intervals. For example, doing integration per loop for each timestep:
function Integrationperloop
tResult = [];
xResult = [];
tStep = [0 1 2 3 4 5 6 7 8 9 10 11];
x0 = [1 2 3];
for index = 2:numel(tStep)
% Integrate:
beta = 1 + exp(-0.20*tStep(index - 1))
af = #(t,x) f(t, x, beta);
t = tStep(index-1:index);
[t, x] = ode45(af, t, x0);
% Collect the results:
tResult = cat(1, tResult, t);
xResult = cat(1, xResult, x);
% Final value of x is initial value for next step:
x0 = x(end, :);
end
%This is the derivative
function dx = f(t,x, beta)
dx = [-0.5*x(1)-beta*x(1)*x(3); ...
beta*x(1)*x(3) - x(2); ...
x(2) - x(3)];
However in the code above ode45 will treat the variable as constant when running its own intervals. In the link how-do-i-solve-an-ode-with-time-dependent-parameters-in-matlab, MathWorks Support Team suggest using the enhancement in R14S13 or using interpolation. If using interpolation, when ODE45 is running the time-dependent variable changes using interpolation.
Related
I am trying to solve simultaneous second order differential equations to find the concentration of a tracer (molecule) at different stages of a bioreactor. The stages are arranged in series.
Context: Bioreactor that we are working with, is a Rotating Biological Contractor. Here is an example. The tracer molecule is injected at the first stage at time t=0 and our objective is to find how the concentration of the tracer molecule varies with respect to time in each stage.
The second order ODE that we are working with can be found here: https://imgur.com/a/KS4Od
I tried to simplify the equation for 4 stages (2nd and 3rd pic in imgur album) and have tried to solve it using MATLAB. Here is the code for it:
P2 = 1; P3 = 5; C0 = 30; P4 = 2;
f = #(t,x)[x(2); (C0+P4*x(7)-x(1)-P3*x(2))/P2;
x(4); (x(1)-x(3)-P3*x(4))/P2;
x(6); (x(3)-x(5)-P3*x(6))/P2;
x(8); (x(5)-x(7)-P3*x(8))/P2];
t= linspace(0,40); init = [0 0 0 0 0 0 0 0];
[t Y] = ode45(f,t,init);
plot(t,Y(:,1),'r-',t,Y(:,3),'b-',t,Y(:,5),'k-',t,Y(:,7),'m-')
legend('C1','C2','C3','C4')
Our aim is to know how the concentration varies in the 4th stage. It is supposed to look like this Residence time distribution or something similar.
I need to know whether its possible to use "for loop" for "n" stages in series and solve the equation. Ideally, only inputs should be no. of stages, time interval, initial concentration and constants. Assume whatever values for constants, initial conc. and time interval.
Could someone please guide me through solving this? I would really appreciate your help.
Instead of the anonymous/lambda definition of f, use a more traditional function which allows you to employ loops.
n = 4
function dotx = f(t,x)
dotx = zeros(2*n,1)
dotx(1) = x(2);
dotx(2) = (C0+P4*x(7)-x(1)-P3*x(2))/P2
for k = 2:n
dotx(2*k-1) = x(2*k)
dotx(2*k) = (x(2*k-3)-x(2*k-1)-P3*x(2*k))/P2
end
end
init = zeros(2*n,1)
One may have to change row/column format for x, dotx.
this is my first time posting, so if you need anymore info or I havn't done something correct please let me know!
I want to plot a system of ODES, which would be no problem as I would do the following;
F=#(t,x) [
.the RHS of my first order ODES.
];
[t x]=ode45(F,[Range], [Initial conditions]);
Where a vector would be created containing x'(1), x'(2),... where x(1), x(2) would be the time dependant variable.
However my system is a little more complicated. I have 6 differential equations which are made up of equations that include differential equations and the time dependant variables. For example, the RHS of my ODES are something like
2*x(2)*x'(1)*f(a)
Where f(a) could be another function based on constants, a.
I have set up my code in the following order;
Constants, then equations of the form f(a), then my differential equations followed by the ode45 solver and the plot commands. However I am getting several errors "Undefined function or variable" as early equations depend on variables/equations that are not defined until later.
Thank you ever so much for you help :)
As suggested, here an example of the type of my code;
`%Constants
a=34
b=31
c=20
%Equations
A=b*cos(2*pi)
B=a*EQ1
C=c*x(2)
%DifferentialEquations
EQ1=x(1)*A
EQ2=(EQ3-EQ1)*(B-C)
EQ3=x(2)*x(3)
F=#(t,x) [EQ1;EQ2;EQ3;];[t x]=ode45(F,[0 10], [0 0 0 ]);
Provides the error.
Produces the undefined function or variable 'DEQ1A'.
Based on the comment you are trying to use a variable before it is defined. In MATLAB you need to define your variables and functions before you use them. Because the equations are functions of each other you can use anonymous functions to define F.
Example:
a=34;
b=31;
c=20;
A = b*cos(2*pi);
EQ1 = #(x) x(1)*A;
B = #(x) a*EQ1(x);
C = #(x) c*x(2);
EQ3 = #(x) x(2)*x(3);
EQ2 = #(x) (EQ3(x) - EQ1(x))*(B(x)-C(x));
F = #(t,x) [EQ1(x);EQ2(x);EQ3(x)];
[t,x] = ode45(F,[0 10], [0 0 0]);
I have the following ODE:
x_dot = 3*x.^0.5-2*x.^1.5 % (Equation 1)
I am using ode45 to solve it. My solution is given as a vector of dim(k x 1) (usually k = 41, which is given by the tspan).
On the other hand, I have made a model that approximates the model from (1), but in order to compare how accurate this second model is, I want to solve it (solve the second ODE) by means of ode45. My problem is that this second ode is given discrete:
x_dot = f(x) % (Equation 2)
f is discrete and not a continuous function like in (1). The values I have for f are:
0.5644
0.6473
0.7258
0.7999
0.8697
0.9353
0.9967
1.0540
1.1072
1.1564
1.2016
1.2429
1.2803
1.3138
1.3435
1.3695
1.3917
1.4102
1.4250
1.4362
1.4438
1.4477
1.4482
1.4450
1.4384
1.4283
1.4147
1.3977
1.3773
1.3535
1.3263
1.2957
1.2618
1.2246
1.1841
1.1403
1.0932
1.0429
0.9893
0.9325
0.8725
What I want now is to solve this second ode using ode45. Hopefully I will get a solution very similar that the one from (1). How can I solve a discrete ode applying ode45? Is it possible to use ode45? Otherwise I can use Runge-Kutta but I want to be fair comparing the two methods, which means that I have to solve them by the same way.
You can use interp1 to create an interpolated lookup table function:
fx = [0.5644 0.6473 0.7258 0.7999 0.8697 0.9353 0.9967 1.0540 1.1072 1.1564 ...
1.2016 1.2429 1.2803 1.3138 1.3435 1.3695 1.3917 1.4102 1.4250 1.4362 ...
1.4438 1.4477 1.4482 1.4450 1.4384 1.4283 1.4147 1.3977 1.3773 1.3535 ...
1.3263 1.2957 1.2618 1.2246 1.1841 1.1403 1.0932 1.0429 0.9893 0.9325 0.8725];
x = 0:0.25:10
f = #(xq)interp1(x,fx,xq);
Then you should be able to use ode45 as normal:
tspan = [0 1];
x0 = 2;
xout = ode45(#(t,x)f(x),tspan,x0);
Note that you did not specify what values of of x your function (fx here) is evaluated over so I chose zero to ten. You'll also not want to use the copy-and-pasted values from the command window of course because they only have four decimal places of accuracy. Also, note that because ode45 required the inputs t and then x, I created a separate anonymous function using f, but f can created with an unused t input if desired.
We have an equation similar to the Fredholm integral equation of second kind.
To solve this equation we have been given an iterative solution that is guaranteed to converge for our specific equation. Now our only problem consists in implementing this iterative prodedure in MATLAB.
For now, the problematic part of our code looks like this:
function delta = delta(x,a,P,H,E,c,c0,w)
delt = #(x)delta_a(x,a,P,H,E,c0,w);
for i=1:500
delt = #(x)delt(x) - 1/E.*integral(#(xi)((c(1)-c(2)*delt(xi))*ms(xi,x,a,P,H,w)),0,a-0.001);
end
delta=delt;
end
delta_a is a function of x, and represent the initial value of the iteration. ms is a function of x and xi.
As you might see we want delt to depend on both x (before the integral) and xi (inside of the integral) in the iteration. Unfortunately this way of writing the code (with the function handle) does not give us a numerical value, as we wish. We can't either write delt as two different functions, one of x and one of xi, since xi is not defined (until integral defines it). So, how can we make sure that delt depends on xi inside of the integral, and still get a numerical value out of the iteration?
Do any of you have any suggestions to how we might solve this?
Using numerical integration
Explanation of the input parameters: x is a vector of numerical values, all the rest are constants. A problem with my code is that the input parameter x is not being used (I guess this means that x is being treated as a symbol).
It looks like you can do a nesting of anonymous functions in MATLAB:
f =
#(x)2*x
>> ff = #(x) f(f(x))
ff =
#(x)f(f(x))
>> ff(2)
ans =
8
>> f = ff;
>> f(2)
ans =
8
Also it is possible to rebind the pointers to the functions.
Thus, you can set up your iteration like
delta_old = #(x) delta_a(x)
for i=1:500
delta_new = #(x) delta_old(x) - integral(#(xi),delta_old(xi))
delta_old = delta_new
end
plus the inclusion of your parameters...
You may want to consider to solve a discretized version of your problem.
Let K be the matrix which discretizes your Fredholm kernel k(t,s), e.g.
K(i,j) = int_a^b K(x_i, s) l_j(s) ds
where l_j(s) is, for instance, the j-th lagrange interpolant associated to the interpolation nodes (x_i) = x_1,x_2,...,x_n.
Then, solving your Picard iterations is as simple as doing
phi_n+1 = f + K*phi_n
i.e.
for i = 1:N
phi = f + K*phi
end
where phi_n and f are the nodal values of phi and f on the (x_i).
I have all the data and an ODE system of three equations which has 9 unknown coefficients (a1, a2,..., a9).
dS/dt = a1*S+a2*D+a3*F
dD/dt = a4*S+a5*D+a6*F
dF/dt = a7*S+a8*D+a9*F
t = [1 2 3 4 5]
S = [17710 18445 20298 22369 24221]
D = [1357.33 1431.92 1448.94 1388.33 1468.95]
F = [104188 104792 112097 123492 140051]
How to find these coefficients (a1,..., a9) of an ODE using Matlab?
I can't spend too much time on this, but basically you need to use math to reduce the equation to something more meaningful:
your equation is of the order
dx/dt = A*x
ergo the solution is
x(t-t0) = exp(A*(t-t0)) * x(t0)
Thus
exp(A*(t-t0)) = x(t-t0) * Pseudo(x(t0))
Pseudo is the Moore-Penrose Pseudo-Inverse.
EDIT: Had a second look at my solution, and I didn't calculate the pseudo-inverse properly.
Basically, Pseudo(x(t0)) = x(t0)'*inv(x(t0)*x(t0)'), as x(t0) * Pseudo(x(t0)) equals the identity matrix
Now what you need to do is assume each time step (1 to 2, 2 to 3, 3 to 4) is an experiment (therefore t-t0=1), so the solution would be to:
1- Build your pseudo inverse:
xt = [S;D;F];
xt0 = xt(:,1:4);
xInv = xt0'*inv(xt0*xt0');
2- Get exponential result
xt1 = xt(:,2:5);
expA = xt1 * xInv;
3- Get the logarithm of the matrix:
A = logm(expA);
And since t-t0= 1, A is our solution.
And a simple proof to check
[t, y] = ode45(#(t,x) A*x,[1 5], xt(1:3,1));
plot (t,y,1:5, xt,'x')
You have a linear, coupled system of ordinary differential equations,
y' = Ay with y = [S(t); D(t); F(t)]
and you're trying to solve the inverse problem,
A = unknown
Interesting!
First line of attack
For given A, it is possible to solve such systems analytically (read the wiki for example).
The general solution for 3x3 design matrices A take the form
[S(t) D(t) T(t)].' = c1*V1*exp(r1*t) + c2*V2*exp(r2*t) + c3*V3*exp(r3*t)
with V and r the eigenvectors and eigenvalues of A, respectively, and c scalars that are usually determined by the problem's initial values.
Therefore, there would seem to be two steps to solve this problem:
Find vectors c*V and scalars r that best-fit your data
reconstruct A from the eigenvalues and eigenvectors.
However, going down this road is treaturous. You'd have to solve the non-linear least-squares problem for the sum-of-exponentials equation you have (using lsqcurvefit, for example). That would give you vectors c*V and scalars r. You'd then have to unravel the constants c somehow, and reconstruct the matrix A with V and r.
So, you'd have to solve for c (3 values), V (9 values), and r (3 values) to build the 3x3 matrix A (9 values) -- that seems too complicated to me.
Simpler method
There is a simpler way; use brute-force:
function test
% find
[A, fval] = fminsearch(#objFcn, 10*randn(3))
end
function objVal = objFcn(A)
% time span to be integrated over
tspan = [1 2 3 4 5];
% your desired data
S = [17710 18445 20298 22369 24221 ];
D = [1357.33 1431.92 1448.94 1388.33 1468.95 ];
F = [104188 104792 112097 123492 140051 ];
y_desired = [S; D; F].';
% solve the ODE
y0 = y_desired(1,:);
[~,y_real] = ode45(#(~,y) A*y, tspan, y0);
% objective function value: sum of squared quotients
objVal = sum((1 - y_real(:)./y_desired(:)).^2);
end
So far so good.
However, I tried both the complicated way and the brute-force approach above, but I found it very difficult to get the squared error anywhere near satisfyingly small.
The best solution I could find, after numerous attempts:
A =
1.216731997197118e+000 2.298119167536851e-001 -2.050312097914556e-001
-1.357306715497143e-001 -1.395572220988427e-001 2.607184719979916e-002
5.837808840775175e+000 -2.885686207763313e+001 -6.048741083713445e-001
fval =
3.868360951628554e-004
Which isn't bad at all :) But I would've liked a solution that was less difficult to find...