I have a system of ODE equations which I want to solve, but there is a tricky part that when the system reaches steady-state, I would like to change the value of one (or more) parameters. For example, consider the following:
function dydt = diff(t,x,params)
F = params(1);
G = params(2);
dydt = zeros(2,1);
dydt(1) = F*x(1) - G*x(1)*x(2);
dydt(2) = (F-G)*x(2);
end
I would like my code to work such that when the system has reached steady-state, the value of F is changed to 10 and the value of G is changed to 2, for example. I was thinking of detecting the values of dydt(1) and dydt(2) by using, for example,
if norm(dydt)<1
F = 10;
G = 2;
end
How do I do that for ODE expression in Matlab? If I put this if condition before the ODE expression, I the value of dydt will always be zero. But if I put this If condition after the ODE expression, the If conditions will have no use to correct the ODE expression.
Thank you!
A parameter of an ODE is assumed to be fixed and does not depend on the state of the system. What you're trying to do is simulate a piecewise continuous ODE. This sort of problem is usually solved with event location (assuming a solution exists). You need to stop the simulation at the key point, change your parameters, and restart a new simulation with initial conditions the same as those at the end of the previous.
Here's a little example with your ODE function. I don't know your initial conditions, initial parameter values, or other settings, so this is just to demonstrate this scheme:
function eventsdemo
params = [-1.5 1];
tspan = [0 10];
x0 = [1;1];
opts = odeset('Events',#events);
[t1,x1] = ode45(#(t,x)f(t,x,params),tspan,x0,opts); % Simulate with events
% Change parameters, set initial conditions based on end of previous run
params = [1.5 1];
x0 = x1(end,:);
tspan = [t1(end) 10];
[t2,x2] = ode45(#(t,x)f(t,x,params),tspan,x0); % Simulate again
% Concatenate results, removing duplicate points
t = [t1;t2(2:end)];
x = [x1;x2(2:end,:)];
figure;
plot(t,x);
hold on;
plot(t2(1),x2(1,:),'k*'); % Plot event location
function dxdt = f(t,x,params) %#ok<INUSL>
F = params(1);
G = params(2);
dxdt = [F*x(1) - G*x(1)*x(2);
(F-G)*x(2)];
function [value,isterminal,direction] = events(t,x) %#ok<INUSL>
value = norm(x)-1e-3; % Don't try to detect exact zero for asymptotics
isterminal = true;
direction = -1;
In this case the solution asymptotically approaches a stable fixed point at (0,0). It's important to not try to detect (0,0) exactly as the solution may never reach that point. You can instead use a small tolerance. However, depending on your system, it's possible that choosing this tolerance could impact the behavior after you change parameters. You could also consider reformulating this problem as a boundary value problem. I don't know what you're trying to do with this system so I can't say much else (and it would probably be off-topic for this site).
Related
I need to plot out a function based on roots that I can use poly() to get the polynomial for. However, whenever I run the function, it gives me an error. I want to be able to fix the error so I can continue onto my further goals.
I need to plot the function with respect to the domain of time I provide, plot the function with respect to the sampling index nVec multiplied by the sampling rate deltaT, then plot the discrete signal. I am at a stop right now because I cannot plot the continuous signal (the first plot). It consistently gives me an error. I have tried changing the roots (rootsVec) from negative inclusion to purely positive so I can obtain the equation from poly() as well as put the sampling rate for the increment in time but to no avail.
function [tVec,nVec,xVec] =
fxNthOrderPolyDTSignal(domainVec,noOfSamples,rootsVec)
DSIntervals = noOfSamples - 1;
deltaT = (max(domainVec) - min(domainVec))/DSIntervals;
%time = input("Please input a specific time within the domain: ");
nVec = min(domainVec)/deltaT;
tVec = min(domainVec) + (nVec * deltaT);
Eqtn = poly(rootsVec);
x = linspace(domainVec(1),0.5,max(domainVec));
figure(1)
plot(Eqtn(x),x)
figure(2)
plot(Eqtn,(nVec*deltaT))
end
The expected result is simply a plot of the the signal with the following input arguments:
domainVec = [-10, 10] (this is the time where the signal exists);
noOfSamples = 30;
rootsVec = [-3, 8] (aka a second order polynomial);
The actual result is the following error: Array indices must be positive integers or logical values.
Check this code, with a proper naming of variables, more Signal Processing style...
With n=10 you will start to see the discretization.
function [t,x,dt] = f(tlim,n,p)
%% Function (Press 'Run Section' from here)
% Parameters (Delete these for function to work)
tlim=[-10 10];
n=30;
p=[-3 8];
% Polinomial Coefficients from Roots
a=poly(p);
% Sampling Time
dt=(tlim(2)-tlim(1))/n;
% Evaluate
t=linspace(tlim(1),tlim(2),n)';
x=a(1)*t.^0+a(2)*t.^1+a(3)*t.^2;
% Plot
plot(t,x)
I figured it out. This was the correct answer to my problem. Thank you for your help!
function [tVec,nVec,xVec] =
fxNthOrderPolyDTSignal(domainVec,noOfSamples,rootsVec)
deltaT = (max(domainVec) - domainVec(1))/noOfSamples;
nVec = [0 : noOfSamples];
tVec = [];
for i = nVec
tVec(end + 1) = min(domainVec) + (i*deltaT);
end
nomial = poly(rootsVec);
xVec = polyval(nomial,tVec);
subplot(3,1,1)
plot(tVec,xVec)
title('x(t)')
subplot(3,1,2)
stem(tVec,xVec)
title('x[n]')
subplot(3,1,3)
stem(tVec,tVec*0)
title('x(nDeltaT)')
end
I'm using fzero function to solve a non-linear equation depending on one parameter
and I'm not satisfied with my method. I have these issues:
1) Can for loop for the parameter be avoided ?
2) In order to avoid complex solutions I first have to pre-compute valid interval for fzero.
Is there is a better solution here ?
If I reduce the parameter step size the execution time becomes slow. If I don’t pre-compute
the interval I get an error "Function values at interval endpoints must be finite and real."
in Matlab and "fzero: not a valid initial bracketing" in Octave.
Here is the code
% solve y = 90-asind(n*(sind(90-asind(sind(a0)/n)-y)))
% set the equation paramaters
n=1.48; a0=0:0.1:60;
% loop over a0
for i = 1:size(a0,2)
% for each a0 find where the argument of outer asind()
% will not give complex solutions, i.e. argument is between 1 and -1
fun1 = #(y) n*(sind(90-asind(sind(a0(i))/n)-y))-0.999;
y1 = fzero(fun1,[0 90]);
fun2 = #(y) n*(sind(90-asind(sind(a0(i))/n)-y))+0.999;
y2 = fzero(fun2,[0 90]);
% use y1, y2 as limits in fzero interval
fun3 = #(y) 90-asind(n*(sind(90-asind(sind(a0(i))/n)-y)))-y;
y(i) = fzero(fun3, [y1 y2]);
end
% plot the result
figure; plot(y); grid minor;
xlabel('Incident ray angle [Deg]');
ylabel('Lens surface tangent angle');
With Matlab, I obtained the plot below with the following simplified loop.
for i = 1:size(a0,2)
fun3 = #(y) sind(90-y) - n*(sind(90-asind(sind(a0(i))/n)-y));
y(i) = fzero(fun3, [0,90]);
end
The difference is in the form of equation: I replaced 90-y = asind(something) with sin(90-y) = something. When "something" is greater than 1 in absolute value, the former version throws an error due to complex value of asind. The latter proceeds normally, recognizing that this is not a solution (sin(90-y) can't be equal to something that is greater than 1).
No precomputing of the bracket was necessary, [0,90] simply worked. Another change I made was in the plot: plot(a0,y) instead of plot(y), to get the correct horizontal axis.
And you can't avoid for loop here, nor should you worry about it. Vectorization means eliminating loops where the content is a low-level operation that can be done en masse by operating on some C array. But fzero is totally not that. If the code takes long to run, it's because solving a bunch of equations takes long, not because there's a for loop.
So I was given a second order boundary value problem with y(0)=5 and y(20)=8. We are supposed to use the shooting method with ode45 to approximate the solution and compare it to the actual solution. I have already converted the equation to a first order equation and found the analytic solution (y1). We are supposed to have the user input guesses of -1 and -.5, but when I do this the graph doesn't resemble the actual at all. I have commented out the while loop we are supposed to use to get the solution to within a tolerance because I need the first part to work out. I'm not really sure what the issue is...
f.m file
function xp=f(t,x)
xp=zeros(2,1);
xp(1)=x(2);
xp(2)=exp(-5*t)-x(2)-9.81*sin(x(1));
main.m file
clear
clc
syms t;
c = -1.783049298*10^(-4);
d = 7.000178305;
y1 = c*exp((1/7+2*sqrt(2)/7)*t)+d*exp((1/7-2*sqrt(2)/7)*t)+t-2;
errta = 1;
prompt = 'Enter a guess for the slope';
z = input(prompt);
guesses(1) = z;
[t,x]=ode45('f',[0,20],[5,z]);
result(1) = x(length(x),1);
z = input(prompt);
guesses(2) = z;
[t,x]=ode45('f',[0,20],[5,z]);
result(2) = x(length(x),1);
% while errta>0.000001
% z = (guesses(2)+guesses(1))/2;
%
% [t,x]=ode45('f',[0,20],[5,z]);
% if x(length(x),1)>8
% guesses(2)=z;
% end
%
% if x(length(x),1)<8
% guesses(1)=z;
% end
%
% errta = abs(-.82857121689-z);
% end
plot(t,x(:,1),'r')
title('ode45 vs actual')
hold on
ezplot(y1,[0,20])
hold off
x''+x'+9.81*sin(x)=exp(-5*t)
as used in the odefunc f is a damped pendulum with some exponentially decreasing forcing. As such one can expect that at t=20 an equilibrium or stationary point is reached. Those are precisely known as x=2*pi*k. Thus the mapping from the initial velocity to the final position will resemble a step function and the bisection function will find the almost vertical segment where the final value 8 resides on. But probably not the point where the final value is exactly 8. Make a plot for initial velocities ranging from 5 t0 10 to see this effect.
The exact solution named y1 belongs to the differential equation
7*x''-2*x'-x=-t
The relation between initial velocity and final position is nearly linear with the solution v0=-0.828571216889 which appears to be independent of the step size of the numerical solver. This value is also consistent with the proposed initial guesses.
I have a set of odes written in matrix form as $X' = AX$; I also have a desired value of the states $X_des$. $X$ is a five dimensional vector. I want to stop the integration after all the states reach their desired values (or atleast close to them by $1e{-3}$). How do I use event function in matlab to do this? (All the help I have seen are about 1 dimensional states)
PS: I know for sure that all the states approach their desired values after long time. I just want to stop the integration once they are $1e{-3}$ within the desired values.
First, I presume that you're aware that you can use the matrix exponential (expm in Matlab) to solve your system of linear differential equations directly.
There are many ways to accomplish what you're trying to do. They all depend a bit on your system, how it behaves, and the particular event you want to capture. Here's a small example for a 2-by-2 system of linear differential equations:
function multipleeventsdemo
A = [-1 1;1 -2]; % Example A matrix
tspan = [0 50]; % Initial and final time
x0 = [1;1]; % Initial conditions
f = #(t,y)A*y; % ODE function
thresh = 0; % Threshold value
tol = 1e-3; % Tolerance on threshold
opts = odeset('Events',#(t,y)events(t,y,thresh,tol)); % Create events function
[t,y] = ode45(f,tspan,x0,opts); % Integrate with options
figure;
plot(t,y);
function [value,isterminal,direction] = events(t,y,thresh,tol)
value = y-thresh-tol;
isterminal = all(y-thresh-tol<=0)+zeros(size(y)); % Change termination condition
direction = -1;
Integration is stopped when both states are within tol of thresh. This is accomplished by adjusting the isterminal output of the events function. Note that separate tolerance and threshold variables isn't really necessary – you simply need to define the crossing value.
If your system oscillates as it approaches it's steady state (if A has complex eigenvalues), then you'll need to do more work. But you questions doesn't indicate this. And again, numerical integration may not be the easiest/best way to solve your problem which such a system. Here is how you could use expm in conjunction with a bit of symbolic math:
A = [-1 1;1 -2];
x0 = [1;1];
tol = 1e-3;
syms t_sym
y = simplify(expm(A*t_sym)*x0) % Y as a function of t
t0 = NaN(1,length(x0));
for i = 1:length(x0)
sol = double(solve(y(i)==tol,t_sym)) % Solve for t when y(i) equal to tol
if ~isempty(sol) % Could be no solution, then NaN
t0(i) = max(sol); % Or more than one solution, take largest
end
end
f = matlabFunction(y); % Create vectorized function of t
t_vec = linspace(0,max(t0),1e2); % Time vector
figure;
plot(t_vec,f(t_vec));
This will only work for fairly small A, however, because of the symbolic math. Numerical approaches using expm are also possible and likely more scalable.
I am working on a problem involves my using the Euler Method to approximate the differential equation df/dt= af(t)−b[f(t)]^2, both when b=0 and when b is not zero; and I am to compare the analytic solution to the approximate solution when b=0.
f(1) = 1000;
t(1)= 0;
a = 10;
b = 0 ;
dt = 0.01;
Nsteps = 10/dt;
for i = 2:Nsteps
t(i) = dt + t(i-1);
%f(i) = f(i-1)*(1 + dt*(a - b*f(i-1)));
f(i) = f(i-1)*(1 + a*dt);
end
plot(t,f,'r-')
hold on
fa= a*exp(a*t)
plot(t,fa,'bo')
When b=0, the solution to the differential equation is f(t)=c*exp(at). When I apply the initial condition, that f(0) = 1000, then the differential equation becomes f(t)=1000*exp(at). Now, my professor said that a differential equation has an analytic solution, no matter what time step you use, the graph of analytic solution and the approximation (Euler's Method) will coincide. So, I expected the two graphs to overlap. I attached a picture of what I got.
Why did this occur? In order to get the graphs to overlap, I changed 1000 to 10, which is a=10, just for the heck of it. When I did this, the two overlapped. I don't understand. What am I doing incorrectly?
Why should the numerical solution give the same answer as the analytical one? Looking at pixels overlapping on the screen is not a very precise way to discern anything. You should examine the error between the two (absolute and/or relative). You might also want to examine what happens when you change the step size. And you might want to play with a linear system as well. You don't need to integrate out so far to see these effects – just setting t equal 0.1 or 1 suffices. Here is some better-formatted code to work with:
t0 = 0;
dt = 0.01;
tf = 0.1;
t = t0:dt:tf; % No need to integrate t in for loop for fixed time step
lt = length(t);
f = zeros(1,lt); % Pre-allocate f
f0 = 1000; % Initial condition
f(1) = f0;
a = 10;
for i = 1:lt-1
f(i+1) = f(i) + a*f(i)*dt;
%f(i+1) = f(i) + a*dt; % Alternative linear system to try
end
% Analytic solution
fa = f0*exp(a*t);
%fa = f0+a*t; % Alternative linear system to try
figure;
plot(t,f,'r-',t,fa,'bo')
% Plot absolute error
figure;
plot(t,abs(f-fa))
% Plot relative error
figure;
plot(t,abs(f-fa)./fa)
You're also not preallocating any of your arrays which makes you code very inefficient. My code does. Read about that here.
Much more beyond this is really off-topic for this site, which is focussed on programming rather than mathematics. If you really have questions about the numerical details that aren't answered by reading your text book (or the Wikipedia page for the Euler method) then you should ask them at Math.StackExchange.
Numerical methods are not precise and there is always an error between numerical and analytical solution. As Euler's method is first order method, global truncation error is proportional to step of integration step.