Approximating derivative of numerical solution (using ode45) within event function - matlab

The issue I have is having to compute the derivative (in real time) of the solution produced by ode45 within the events function.
Some pseudo-code to explain what I'm mean is,
function dx = myfunc(t,x,xevent,ie)
persistent xs
persistent dx2
% xevent is the solution at the event
if isempty(dx2) == 1
dx2 = 0;
end
k = sign(dx2*x(1));
if ie(end) == 1
xs = xevent
elseif ie(end) == 2
xs = xevent
end
dx(1) = x(2);
dx(2) = function of k,x(1),x(2), and xs;
dx2 = dx(2);
end
function [value,isterminal,direction] = myeventfcn(t,x)
% dx2 = some function of x
if dx2*x(1)>=0
position = [dx2*x(1); dx2*x(1)];
isterminal = [1; 1];
direction = [1 ; -1 ];
elseif dx2*x(1)<0
position = [dx2*x(1); dx2*x(1)];
isterminal = [1; 1];
direction = [-1 ; 1 ];
end
I know that if I didn't need to use the solution at the event within myfunc I could just compute dx=myfunc(t,x) within my event function and use dx(2), yet since xevent is used within myfunc I can't input xevent.
I know there is a way of inputting constant parameters within the event function, but since it's the solution at the event location, which also changes, I'm not sure how to go about doing that.
My work around to this is to approximate dx(2) using the solution x. What I wanted to know is if it will be satisfactory to just use a finite difference approximation here, using a small fixed step size relative to the step size od45 takes, which is a variable step size.
As a note, the reason I have myeventfcn split by the if statement is to know what the direction the event is crossed, since it will update within myfunc.
Also, I need to use dx(2) of the previous successful time step and so that's why I have dx2 defined as a persistent variable. I believe having k=sign(dx(2)*x(1)) is okay to do, since my event depends on dx(2)*x(1), and so I won't be introducing any new discontinuities with the sign function.
Thanks for any help!

Related

Metropolis-Hastings in matlab

I am trying to use the Metropolis Hastings algorithm with a random walk sampler to simulate samples from a function $$ in matlab, but something is wrong with my code. The proposal density is the uniform PDF on the ellipse 2s^2 + 3t^2 ≤ 1/4. Can I use the acceptance rejection method to sample from the proposal density?
N=5000;
alpha = #(x1,x2,y1,y2) (min(1,f(y1,y2)/f(x1,x2)));
X = zeros(2,N);
accept = false;
n = 0;
while n < 5000
accept = false;
while ~accept
s = 1-rand*(2);
t = 1-rand*(2);
val = 2*s^2 + 3*t^2;
% check acceptance
accept = val <= 1/4;
end
% and then draw uniformly distributed points checking that u< alpha?
u = rand();
c = u < alpha(X(1,i-1),X(2,i-1),X(1,i-1)+s,X(2,i-1)+t);
X(1,i) = c*s + X(1,i-1);
X(2,i) = c*t + X(2,i-1);
n = n+1;
end
figure;
plot(X(1,:), X(2,:), 'r+');
You may just want to use the native implementation of matlab mhsample.
Regarding your code, there are a few things missing:
- function alpha,
- loop variable i (it might be just n but it is not suited for indexing since it starts at zero).
And you should always allocate memory in matlab if you want to fill it dynamically, i.e. X in your case.
To expand on the suggestions by #max, the code appears to work if you change the i indices to n and replace
n = 0;
with
n = 2;
X(:,1) = [.1,.1];
It would probably be better to assign X(:,1) to random values within your accept region (using the same code you use later), and/or include a burn-in period.
Depending upon what you are going to do with this, it may also make things cleaner to evaluate the argument to sin in the f function to keep it within 0 to 2 pi (likely by shifting the value by 2 pi if it exceeds those bounds)

The Matlab event function doesn't stop the integration

I have a ode system that I solve with Matlab. I want to find the steady state of the system and to do so, I use the event function as described here.
But some times, the solver doesn't stop even if the criterium is achieved.
For example, if you solve the following system with x0 = 10 the solver will stop before 2000 but with x0 = 0.0001 it won't.
The event function (eventfun_t.m)
function [x,isterm,dir] = eventfun_t(t,y)
dy = test_systeme(t,y);
x = norm(dy) - 1e-3;
isterm = 1;
dir = 0; %or -1, doesn't matter
end
The system (test_systeme.m)
function dx = test_systeme(t,x)
v = x./(x+1);
dx = -v;
end
Solve the system
x0 = 10;
eventfonction = #(t,y) eventfun_t(t,y);
optionsode=odeset('Events',eventfonction);
[t x]=ode15s(#(t,x) test_systeme(t,x),[0 2000],x0,optionsode);
I suppose it's because with x0 = 0.0001 norm(dy) is already lower than 1e-3 but in that case, how can I stop the solver without checking by myself ?
The events function checks for sign changes in value. So if value(t=0)<0 and value(0 < t < t_end)<0, then it will never trigger.
A way I've gotten around this is to use conditional statements:
value = 1;
if norm(dy)<1e-3&&t~=0
value = -1;
end
The t~=0 statement allows value to change sign after the first step if it's already less than the theshold.

Solving differential equation for a single time in loop with matlab

I have a Mechanical system with following equation:
xdot = Ax+ Bu
I want to solve this equation in a loop because in every step I need to update u but solvers like ode45 or lsim solving the differential equation for a time interval.
for i = 1:10001
if x(i,:)>= Sin1 & x(i,:)<=Sout2
U(i,:) = Ueq - (K*(S/Alpha))
else
U(i,:) = Ueq - (K*S)
end
% [y(i,:),t,x(i+1,:)]=lsim(sys,U(i,:),(time=i/1000),x(i,:));
or %[t,x] = ode45(#(t,x)furuta(t,x,A,B,U),(time=i/1000),x)
end
Do I have another ways to solve this equation in a loop for a single time(Not single time step).
There are a number of methods for updating and storing data across function calls.
For the ODE suite, I've come to like what is called "closures" for doing that.
A closure is basically a nested function accessing or modifying a variable from its parent function.
The code below makes use of this feature by wrapping the right-hand side function passed to ode45 and the 'OutputFcn' in a parent function called odeClosure().
You'll notice that I am using logical-indexing instead of an if-statement.
Vectors in if-statements will only be true if all elements are true and vice-versa for false.
Therefore, I create a logical array and use it to make the denominator either 1 or Alpha depending on the signal value for each row of x/U.
The 'OutputFcn' storeU() is called after a successful time step by ode45.
The function grows the U storage array and updates it appropriately.
The array U will have the same number of columns as the number of solution points requested by tspan (12 in this made-up example).
If a successful full step leaps over any requested points, the function is called with intermediate all requested times and their associated solution values (so x may be rectangular and not just a vector); this is why I used bsxfun in storeU and not in rhs.
Example function:
function [sol,U] = odeClosure()
% Initilize
% N = 10 ;
A = [ 0,0,1.0000,0; 0,0,0,1.0000;0,1.3975,-3.7330,-0.0010;0,21.0605,-6.4748,-0.0149];
B = [0;0;0.6199;1.0752 ] ;
x0 = [11;11;0;0];
K = 100;
S = [-0.2930;4.5262;-0.5085;1.2232];
Alpha = 0.2 ;
Ueq = [0;-25.0509;6.3149;-4.5085];
U = Ueq;
Sin1 = [-0.0172;-4.0974;-0.0517;-0.2993];
Sout2 = [0.0172 ; 4.0974; 0.0517; 0.2993];
% Solve
options = odeset('OutputFcn', #(t,x,flag) storeU(t,x,flag));
sol = ode45(#(t,x) rhs(t,x),[0,0.01:0.01:0.10,5],x0,options);
function xdot = rhs(~,x)
between = (x >= Sin1) & (x <= Sout2);
uwork = Ueq - K*S./(1 + (Alpha-1).*between);
xdot = A*x + B.*uwork;
end
function status = storeU(t,x,flag)
if isempty(flag)
% grow array
nAdd = length(t) ;
iCol = size(U,2) + (1:nAdd);
U(:,iCol) = 0 ;
% update U
between = bsxfun(#ge,x,Sin1) & bsxfun(#le,x,Sout2);
U(:,iCol) = Ueq(:,ones(1,nAdd)) - K*S./(1 + (Alpha-1).*between);
end
status = 0;
end
end

Fixed Point Iteration

I am new to Matlab and I have to use fixed point iteration to find the x value for the intersection between y = x and y = sqrt(10/x+4), which after graphing it, looks to be around 1.4. I'm using an initial guess of x1 = 0. This is my current Matlab code:
f = #(x)sqrt(10./(x+4));
x1 = 0;
xArray(10) = [];
for i = 1:10
x2 = f(x1);
xArray(i) = x2;
x1 = x1 + 1;
end
plot(xArray);
fprintf('%15.8e\n',xArray);
Now when I run this it seems like my x is approaching 0.8. Can anyone tell me what I am doing wrong?
Well done. You've made a decent start at this.
Lets look at the graphical solution. BTW, this is how I'd have done the graphical part:
ezplot(#(x) x,[-1 3])
hold on
ezplot(#(x) sqrt(10./(x+4)),[-1 3])
grid on
Or, I might subtract the two functions, then looking for a zero of the difference, so where it crosses the x axis.
This is what the fixed point iteration does anyway, trying to solve for x, such that
x = sqrt(10/(x+4))
So how would I change your code to fix it? First of all, I'd want to use more descriptive names for the variables. You don't get charged by the character, and making your code easier to read & follow will pay off greatly in the future for you.
There were a couple of code issues. To initialize a vector, use a form like one of these:
xArray = zeros(1,10);
xArray(1,10) = 0;
Note that if xArray was ALREADY defined because you have been working on this problem, the latter form will only zero out that single element. So the first form is best by a large margin. It affirmatively creates an array, or overwrites an existing array if it is already present in your workspace.
Finally, I like to initialize an array like this with something special, rather than zero, so we can see when an element was overwritten. NaNs are good for this.
Next, there was no need to add one to x1 in your code. Again, I'd strongly suggest using better variable names. It is also a good idea to use comments. Be liberal.
I'd suggest the idea of a convergence tolerance. You can also have an iteration counter.
f = #(x)sqrt(10./(x+4));
% starting value
xcurrent = 0;
% count the iterations, setting a maximum in maxiter, here 25
iter = 0;
maxiter = 25;
% initialize the array to store our iterations
xArray = NaN(1,maxiter);
% convergence tolerance
xtol = 1e-8;
% before we start, the error is set to be BIG. this
% just lets our while loop get through that first iteration
xerr = inf;
% the while will stop if either criterion fails
while (iter < maxiter) && (xerr > xtol)
iter = iter + 1;
xnew = f(xcurrent);
% save each iteration
xArray(iter) = xnew;
% compute the difference between successive iterations
xerr = abs(xnew - xcurrent);
xcurrent = xnew;
end
% retain only the elements of xArray that we actually generated
xArray = xArray(1:iter);
plot(xArray);
fprintf('%15.8e\n',xArray);
What was the result?
1.58113883e+00
1.33856229e+00
1.36863563e+00
1.36479692e+00
1.36528512e+00
1.36522300e+00
1.36523091e+00
1.36522990e+00
1.36523003e+00
1.36523001e+00
1.36523001e+00
For a little more accuracy to see how well we did...
format long g
xcurrent
xcurrent =
1.36523001364783
f(xcurrent)
ans =
1.36523001338436
By the way, it is a good idea to know why the loop terminated. Did it stop for insufficient iterations?
The point of my response here was NOT to do your homework, since you were close to getting it right anyway. The point is to show some considerations on how you might improve your code for future work.
There is no need to add 1 to x1. your output from each iteration is input for next iteration. So, x2 from output of f(x1) should be the new x1. The corrected code would be
for i = 1:10
x2 = f(x1);
xArray(i) = x2;
x1 = x2;
end
f(x)x^3+4*x^2-10 in [1,2] find an approximate root

MATLAB: Saving parameters inside ode45 using 'assignin'

I'm running a set of ODEs with ode45 in MATLAB and I need to save one of the variables (that's not the derivative) for later use. I'm using the function 'assignin' to assign a temporary variable in the base workspace and updating it at each step. This seems to work, however, the size of the array does not match the size of the solution vector acquired from ode45. For example, I have the following nested function:
function [Z,Y] = droplet_momentum(theta,K,G,P,zspan,Y0)
options = odeset('RelTol',1e-7,'AbsTol',1e-7);
[Z,Y] = ode45(#momentum,zspan,Y0,options);
function DY = momentum(z,y)
DY = zeros(4,1);
%Entrained Total Velocity
Ve = sin(theta)*(y(4));
%Total Relative Velocity
Urs = sqrt((y(1) - y(4))^2 + (y(2) - Ve*cos(theta))^2 + (y(3))^2);
%Coefficients
PSI = K*Urs/y(1);
PHI = P*Urs/y(1);
%Liquid Axial Velocity
DY(1) = PSI*sign(y(1) - y(4))*(1 + (1/6)*(abs(y(1) - y(4))*G)^(2/3));
%Liquid Radial Velocity
DY(2) = PSI*sign(y(2) - Ve*cos(theta))*(1 + (1/6)*(abs(y(2) - ...
Ve*cos(theta))*G)^(2/3));
%Liquid Tangential Velocity
DY(3) = PSI*sign(y(3))*(1 + (1/6)*(abs(y(3))*G)^(2/3));
%Gaseous Axial Velocity
DY(4) = (1/z/y(4))*((PHI/z)*sign(y(1) - y(4))*(1 + ...
(1/6)*(abs(y(1) - y(4))*G)^(2/3)) + Ve*Ve - y(4)*y(4));
assignin('base','Ve_step',Ve);
evalin('base','Ve_out(end+1) = Ve_step');
end
end
In the above code, theta (radians), K (negative value), P, & G are constants and for the sake of this example can be taken as any value. Zspan is just the integration time step for the ODE solver and Y0 is the initial conditions vector (4x1). Again, for the sake of this example these can take any reasonable value. Now in the main file, the function is called with the following:
Ve_out = 0;
[Z,Y] = droplet_momentum(theta,K,G,P,zspan,Y0);
Ve_out = Ve_out(2:end);
This method works without complaint from MATLAB, but the problem is that the size of Ve_out is not the same as the size of Z or Y. The reason for this is because MATLAB calls the ODE function multiple times for its algorithm, so the solution is going to be slightly smaller than Ve_out. As am304 suggested, I could just simply calculated DY by giving the ode function a Z and Y vector such as DY = momentum(Z,Y), however, I need to get this working with 'assignin' (or similar method) because another version of this problem has an implicit dependence between DY and Ve and it would be too computationally expensive to calculate DY at every iteration (I will be running this problem for many iterations).
Ok, so let's start off with a quick example of an SSCCE:
function [Z,Y] = khan
options = odeset('RelTol',1e-7,'AbsTol',1e-7);
[Z,Y] = ode45(#momentum,[0 12],[0 0],options);
end
function Dy = momentum(z,y)
Dy = [0 0]';
Dy(1) = 3*y(1) + 2* y(2) - 2;
Dy(2) = y(1) - y(2);
Ve = Dy(1)+ y(2);
assignin('base','Ve_step',Ve);
evalin('base','Ve_out(end+1) = Ve_step;');
assignin('base','T_step',z);
evalin('base','T_out(end+1) = T_step;');
end
By running [Z,Y] = khan as the command line, I get a complete functional code that demonstrates your problem, without all the headaches associated. My patience for this has been exhausted: live and learn.
This seems to work, however, the size of the array does not match the
size of the solution vector acquired from ode45
Note that I added two lines to your code which extracts time variable. From the command prompt, one simply has to run the following to understand what's going on:
Ve_out = [];
T_out = [];
[Z,Y] = khan;
size (Z)
size (T_out)
size (Ve_out)
plot (diff(T_out))
ans =
109 1
ans =
1 163
ans =
1 163
Basically ode45 is an iterative algorithm, which means it will regularly course correct (that's why you regularly see diff(T) = 0). You can't force the algorithm to do what you want, you have to live with it.
So your options are
1. Use a fixed step algorithm
2. Have a function call that reproduces what you want after the ode45 algorithm has done its dirty work. (am304's solution)
3. Collects the data with the time function, then have an algorithm parse through everything to removes the extra data.
Can you not do something like that? Obviously check the sizes of the matrices/vectors are correct and amend the code accordingly.
[Z,Y] = droplet_momentum2(theta,K,G,P,zspan,Y0);
DY = momentum(Z,Y);
Ve = sin(theta)*(0.5*z*DY(4) + y(4));
i.e. once the ODE is solved, computed the derivative DY as a function of Z and Y (which have just been solved by the ODE) and finally Ve.