I have the following simple ODE:
dx/dt=-1
With initial condition x(0)=5, I am interested in when x(t)==1. So I have the following events function:
function [value,isterminal,direction] = test_events(t,x)
value = x-1;
isterminal = 0;
direction = 0;
end
This should produce an event at t=4. However, if I run the following code I get two events, one at t=4, and one at the nearby location t=4+5.7e-14:
options = odeset('Events',#test_events);
sol = ode45(#(t,x)-1,[0 10],5,options);
fprintf('%.16f\n',sol.xe)
% 4.0000000000000000
% 4.0000000000000568
If I run similar codes to find when x(t)==0 or x(t)==-1 (value = x; or value = x+1; respectively), I have only one event. Why does this generate two events?
UPDATE: If the options structure is changed to the following:
options = odeset('Events',#test_events,'RelTol',1e-4);
...then the ODE only returns one event at t=4+5.7e-14. If 'RelTol' is set to 1e-5, it returns one event at t=4. If 'RelTol' is set to 1e-8, it returns the same two events as the default ('RelTol'=1e-3). Additionally, changing the initial condition from x(0)=5 to x(0)=4 produces a single event, but setting x(0)=4 and 'RelTol'=1e-8 produces two events.
UPDATE 2: Observing the sol.x and sol.y outputs (t and x, respectively), the time progresses as integers [0 1 2 3 4 5 6 7...], and x progresses as integers up until x(t=5) like so: [5 4 3 2 1 1.11e-16 -1.000 -2.000...]. This indicates that there is something that occurs between t=4 and t=5 that creates a 'bump' in the ODE solution. Why?
One speculation that might explain how rounding errors could occur in this simple problem: The solution is interpolated between the internal steps using the evaluations k_n of the ODE derivatives function, also called "dense output". The theoretical form is
b_1(u)k_1 + b_2(u)k_2 + ...b_s(u)k_s
where 0 <= u<= 1 it the parameter over the interval between the internal points, that is, t = (1-u)*t_k+u*t_{k+1}.
The coefficient polynomials are non-trivial. While in the example all the k_i=1 are constant, the evaluation of the sum b_1(u)+...+b_s(u) can accumulate rounding errors that become visible in the solution value close to a root, even if y_k and y_{k+1} are exact. In that range of accumulated floating point noise, the value might oscillate around the root, leading to the detection of multiple zero crossings.
Related
My post is related to the following questions:
Avoid division by zero between matrices in MATLAB
How to stop MATLAB from rounding extremely small values to 0?
I am writing a matlab function that I am exporting with codegen. When codegen executes division between two numbers, both primitive doubles, codegen mentions that the result is a type of :Inf x :Inf. Here is my code snip:
travel_distance = stop_position - start_position;
duration = stop_time - start_time;
velocity = (travel_distance / duration);
Neither travel_distance or duration variables are zero. During codegen, when I examine the variables of travel_distance and duration, they are both :Inf x 1. However, velocity is showing as :Inf x :Inf . This is being shown also for the (travel_distance / duration) block of code. I suspect that I am running into the scenario mentioned by the author in the second link, which mentions this quote:
MATLAB will not change the value to 0. However, it is possible that the result if using the value in an operation is indistinguishable from using 0
I've tried several things to try and solve my problem, and am still getting the same thing. For example:
% increment by a small number
velocity = ((travel_distance + 0.0001) / (duration + 0.0001));
% check if nan, and set to 0
velocity(isnan(velocity)) = 0;
% check if nan or inf and set to 0
if (isnan(velocity) || isinf(velocity))
velocity = 0;
end
I'd actually expect that the travel_distance, duration, and velocity are all of type 1x1, since I know these should be primitive results.
What can I do to ensure matlab performs codegen correctly, by allowing the velocity variable to be either an :Inf x 1 or a 1x1? ( Double or Int output is fine )
I don't think this is related to division by zero, as shown by the attempts you made at avoiding that. :Inf x 1 refers to a vector of unknown length, and :Inf x :Inf to a matrix of unknown size. If duration is a vector, then travel_distance / duration is trying to solve a system of linear equations.
If you use ./ (element-wise division) instead of /, then Codegen might be able to generate the right code:
velocity = travel_distance ./ duration;
I've been trying to implement the following integral in MATLAB
Given a number n, I wrote the code that returns an array with n elements, containing approximations of each integral.
First, I tried this using a 'for' loop and the recurrence relationship on the first line. But from the 20th integral and above the values are completely wrong (correct to 0 significant figures and wrong sign).
The same goes if I use the explicit formula on the second line and two 'for' loops.
As n grows larger, so does the error on the approximations.
So the main issue here is that I haven't found a way to minimize the error as much as possible.
Any ideas? Thanks in advance.
Here is an example of the code and the resulting values, using the second formula:
This integral, for positive values of n, cannot have values >1 or <0
First attempt:
I tried the iterative method and found interesting thing. The approximation may not be true for all n. In fact if I keep track of (n-1)*I(n-1) in each loop I can see
I = zeros(20,3);
I(1,1) = 1-1/exp(1);
for ii = 2:20
I(ii,2) = ii-1;
I(ii,3) = (ii-1)*I(ii-1,1);
I(ii,1) = 1-I(ii,3);
end
There is some problem around n=18. In fact, I18 = 0.05719 and 18*I18 = 1.029 which is larger than 1. I don't think there is any numerical error or number overflow in this procedure.
Second attempt:
To make sure the maths is correct (I verified twice on paper) I used trapz to numerically evaluate the integral, and n=18 didn't cause any problem.
>> x = linspace(0,1,1+1e4);
>> f = #(n) exp(-1)*exp(x).*x.^(n-1);
>> f = #(n) exp(-1)*exp(x).*x.^(n-1)*1e-4;
>> trapz(f(5))
ans =
1.708934160520510e-01
>> trapz(f(17))
ans =
5.571936009790170e-02
>> trapz(f(18))
ans =
5.277113416899408e-02
>>
A closer look is as follows. I18 is slightly different (to the 4th significant digit) between the (stable) numerical method and (unstable) iterative method. 18*I18 is therefore possible to exceed 1.
I = zeros(20,3);
I(1,1) = 1-1/exp(1);
for ii = 2:20
I(ii,2) = ii-1;
I(ii,3) = (ii-1)*I(ii-1,1);
I(ii,1) = 1-I(ii,3);
end
J = zeros(20,3);
x = linspace(0,1,1+1e4);
f = #(n) exp(-1)*exp(x).*x.^(n-1)*1e-4;
J(1,1) = trapz(f(1));
for jj = 2:20
J(jj,1) = trapz(f(jj));
J(jj,2) = jj-1;
J(jj,3) = (jj-1)*J(jj-1,1);
end
I suspect there is an error in each iterative step due to the nature of numerical computations. If the iteration is long, the error propagates and, unfortunately in this case, amplifies rapidly. In order to verify this, I combined the above two methods into a hybrid algo. For most of the time the iterative way is used, and once in a while a numerical integral is evaluated from scratch without relying on previous iterations.
K = zeros(40,4);
K(1,1) = 1-1/exp(1);
for kk = 2:40
K(kk,2) = trapz(f(kk));
K(kk,3) = (kk-1)*K(kk-1,1);
K(kk,4) = 1-K(kk,3);
if mod(kk,5) == 0
K(kk,1) = K(kk,2);
else
K(kk,1) = K(kk,4);
end
end
If the iteration lasts more than 4 steps, error amplification will be large enough to invert the sign, and starts nonrecoverable oscillation.
The code should be able to explain all the data structures. Anyway, let me put some focus here. The second column is the result of trapz, which is the numerical integral done on the non-iterative integration definition of I(n). The third column is (n-1)*I(n-1) and should be always positive and less than 1. The forth column is 1-(n-1)*I(n-1) and should always be positive. The first column is the choice I have made between the trapz result and iterative result, to be the "true" value of I(n).
As can be seen here, in each iteration there is a small error compared to the independent numerical way. The error grows in the 3rd and 4th iteration and finally breaks the thing in its 5th. This is observed around n=25, under the case that I pick the numerical result in every 5 loops since the beginning.
Conclusion: There is nothing wrong with any definition of this integral. However the numerical error when evaluating the expressions is unfortunately aggregating, hence limiting the way you can perform the computation.
The event location function fail in finding an easy event when solving a 4 variables system of ode. Here's the code:
options1 = odeset('RelTol',1e-5,'AbsTol',1e-9,'Events',#evento1);
[T_ode1,X_ode1,te]=ode15s(#Seno,[0 2],[0 0 0 0 0],options1);
function [y] = Seno(t,x)
%Parameters
V=20;
R=1e6;
epsilon=8.87e-12;
d=4.5e-5;
k_sp=10;
gamma1=0.04;
gamma2=0.1;
m=66e-3;
A=0.1;
omega=80;
h=3.8e-6;
l=2e-3;
N=142;
%Variable redefinition
%x=[x,xpunto,q,y,ypunto]
X=x(1);
Xp=x(2);
Q=x(3);
Y=x(4);
Yp=x(5);
%sistema eq differenziali
y(1)=Xp; %y1(1)=position1
y(2)=-(2*k_sp*X/m)-(gamma1*Xp/m)+((epsilon*2*d*h*N*l)*X*V^2/((d^2-X^2)^2))+A*sin(omega*t); %y1(2)=velocity1
y(3)=1/R*(V-Q*(d^2-X^2)/(epsilon*2*d*h*N*l)); %y1(3)=charge
y(4)=Yp; %position 2
y(5)=-gamma2*Yp; %velocity2
y=y';
end
function [condition,ends,directions] = evento1(t,y)
a=2e-6;
c=2e-6;
b=1.5e-6;
condition= [(y(1)^2)-(a+c)^2, (y(4)^2)-(y(1)+b)^2, (y(4)^2)-(y(1)-b)^2];
ends = [1, 1, 1]; % Halt integration
directions = [1, -1, 1];
end
Setting all the initial condition to 0, as you can see, the first event that the event function should find is when the tird condition when y(1) pass for 1.5e-6 (y(4) is 0). Unfortunally ode ignore that event and stop the solution when the 1st one is satisfied.
I can't see why this happen! I tryed the debugging mode and the systems properly pass across 1.5e-6 but doesn't consider it as an event (ie it doesn't start to evaluate the solution in more points near the event).
Thanks for your time and sorry for my english.
As I mentioned in the comments, you can plot your event conditions after simulating the system:
options1 = odeset('RelTol',1e-5,'AbsTol',1e-9,'Events',#evento1);
[T_ode1,X_ode1,te] = ode15s(#Seno,[0 2],[0 0 0 0 0],options1);
a = 2e-6;
c = 2e-6;
b = 1.5e-6;
y = X_ode1;
condition = [y(:,1).^2-(a+c)^2 y(:,4).^2-(y(:,1)+b).^2 y(:,4).^2-(y(:,1)-b).^2];
figure;
plot(T_ode1,condition);
hold on;
plot(T_ode1([1 end]),[0 0],'k--',te,0,'k*');
legend('Condition 1','Condition 2','Condition 3','Location','W');
xlabel('Time');
ylabel('State');
ylim([-1.8e-11 2e-12]);
This results in a plot that looks like this:
If you zoom in on the plot you'll see that the third condition (yellow) never crosses zero and thus never triggers an event. Eventually the first event (blue) crosses zero and does trigger an event. Adjusting the integration tolerances doesn't appear to change this behavior (at best the third condition might asymptotically touch, but not cross, zero). If you want the third condition to trigger an event, you'll either need to change your parameters, change the condition, change your initial conditions, or change your ODEs.
I'm not sure if or why you think the third condition should cross zero, but if the system is numerically sensitive, then you may need to compensate for this by specifying parameter more precisely or artificially biasing the point of zero crossing.
In my textbook I have encountered an example of a function I am supposed to use when specifying an event location for a system of ODEs. The function example is as follows:
function [value, isterminal, dircn] = proj(t,z,flag);
g = 9.81;
if nargin < 3 | isempty(flag)
value = [z(2); 0; z(4); -g];
else
switch flag
case 'events'
value = z(3);
isterminal = 1;
dircn = -1;
otherwise
error('function not programmed for this event');
end
end
There is one part of the logic here which I don't understand. Say that I activate the "events" option and then run ode45. How, then, can ode45 actually read the system of equations (which is specified in the above function as value = [z(2); 0; z(4); -g];)? I have ran ode45 based on the above function, after specifying tspan and inital conditions of course, and it works like a charm. But I don't understand how ode45 can read the system properly when it is only presented in the "if"-part of the script above.
If anyone can explain the logic here, I would greatly appreciate it!
Well I think I can explain some parts. As I wrote above it is strange that the dimension of value changes.
given your statespace and the names of your variables it looks like 2 dimensional motion.
in the case of no flag it seems that state space is:
horizontal position (x)
horizontal speed (vx)
vertical position (y)
vertical speed (vy)
correction
It seems that ode can send 'events' when you specify them. So your function outputs 3rd component of the state space. Look at this site explaining it: http://www.mathworks.de/help/techdoc/math/f1-662913.html
unless you specify it beforehand ode can't send 'events' to the function so this part will not be called.
But your function won't work anyway - as the derivative would need to have same dimension as the statespace (4x1). But has only 1x1.
But I really don't know what you mean by "specifying an event location". Maybe the secret is hidden there.
With some creativity I think you could use the function to extract the 3rd component of the state space.
The answer is in the if command:
if nargin < 3 | isempty(flag)
value = [z(2); 0; z(4); -g];
else
If number of arguments is less than 3 or if variable flag is empty, then set variable value to [z(2); 0; z(4); -g]. Otherwise, if variable flag is 'events', then set variable value to z(3), and when flag is not 'events', report an error. So this function always assigns some return value for variable value or, reports an error using error command.
I can supplement a bit more information about how we proceed after writing the function above. We then define, say:
tspan = [0 6];
z0 = [0, 5*cos(pi/4), 0, 5*sin(pi/4)];
options = odeset('events','on');
[t y] = ode42('proj',tspan,z0,options)
By writing this the output is 5 columns, 1 column for tspan , and 1 column for each of the z-values (z(1), z(2), z(3) and z(4)). When z(3) hits zero, the calculations are terminated since the "event" occurs.
If, on the other hand I do not include the options = odeset('events','on') line, and simply write:
[t y] = ode42('proj',tspan,z0)
The calculations are performed for the entire tspan range.
Yet, I still fail to see how ode42 is capable of calculating all the output vectors when we activate "events" since it then looks logical me that MatLab should only execute the "else"-statement in the "proj"-function. And in this part of the function, the actual system of the differential equations is not included.
I'm trying to plot the response of a control system which has two unit steps - one a t=0 and one at t=0.3
Here is the code I'm using:
% Set up the transfer function
numer = [K*K_m*r];
denom = [(L_m*J_e) (L_m*b_m+R_m*J_e) (K_b*K_m+R_m*b_m) K*K_m*r];
transfun = tf(numer, denom);
% Apply a step function at t=0
[y,T] = step(transfun, 0:0.001:0.5);
% Second step function at t=0.3
[y_b, T_b] = step(transfun, 0.3:0.001:0.5);
% How to add the above two outputs together?
plot( ???? )
I've searched google, but the only solutions I can find are for systems with multiple inputs (MIMO). This system has only one input, I just want to use superposition principal to add them together.
When I try adding the two vectors together I get "Vectors must be the same lengths."
What you're trying to do isn't the step response. The step response measures the system's ability to track a constant input, and what you're proposing isn't a constant input.
To do what you want, look into the lsim function and the gensig function.
(Side note: As for why you couldn't add your vectors, the first goes from 0 to 0.5 in 0.001 increments, and the other goes from 0.3 to 0.5 in 0.001 increments. Of course they're going to be different lengths! If you're really intent on adding these together, do something like:
[y_b, T_b] = step(transfun, 0:0.001:0.5);
y_b(T_b<0.3) = 0;
I can't test this at the moment, but if that doesn't work, try:
y_b(0:0.001:0.3) = 0;
)