I want to stop solving a differential equation in Matlab if it takes more than a designated amount of time. I tried the following,but it doesn't work...
options = odeset('AbsTol',1e-8,'RelTol',1e-5);
RUNTIME=5;
timerID = tic;
while (toc(timerID) < RUNTIME)
[t_pae,x_th_pae] = ode15s(#prosomoiwsh,[0 t_end],[80*pi/180;0;130*pi/180;0;th_initial(1);th_initial(2);th_initial(3);th_initial(4)],options);
end
How can I solve this?
UPDATE :
I tried what horchler suggested and now my code looks like this :
interupt_time = 20;
outputFun= #(t,y,flag)interuptFun(t,y,flag,interupt_time);
options = odeset('AbsTol',1e-5,'RelTol',1e-5,'OutputFcn',outputFun);
try
[t_pae,x_b_th_pae] = ode15s(#prosomoiwsh,[0 t_end],[0;0;0;th_initial(1);th_initial(2);th_initial(3);th_initial(4);th_initial(5);th_initial(6);th_initial(7);th_initial(8);th_initial(9);th_initial(10);th_initial(11);th_initial(12)],options);
u_pae=compute_input(epsilon,ke,kv,dD,IwF,IwG,IwD,IbF,IbG,IbD,p,m,t_pae,x_b_th_pae);
catch ME
if strcmp(ME.identifier,'interuptFun:Interupt')
disp(ME.message);
input('got caught')
else
rethrow(ME); % It's possible the error was due to something else
end
end
function dx_b_th = prosomoiwsh(t,x_b_th)
...
end
The outputFun is exactly the same as the one horchler suggested. But now it times out every single time,while previously this only happened occasionally. What am I doing wrong?
First, you can't do anything like what you're trying. All that will happen is that ode15s will run for as long as it needs and return it's result as usual. You need a way of interrupting the ODE solver in the middle of its execution when a certain designated amount of time has been exceeded. Unfortunately, you didn't provide runnable code, so I'll have to provide general suggestions that you can adapt to your problem.
One way to do this is via an Output function. This is a function that gets called after every (successful) integration step of a Matlab solver. There are are several examples of Output functions that you can examine the code of to see what you can do: odeplot, odeprint, odephas2, and odephas3. Just type edit odeplot in your command window to see the code.
Below is an example of how you might use this capability using the built-in example function vdp1000 that's referred to in the help for ode15s. You need to create a persistent variable to save the initial time, initialize it, and then check the elapsed time relative to a parameter that you pass in. Then you throw an error to abort ode15s if the elapsed time exceeds the designated value. A try-catch statement can be used to catch the error.
function ode_interupt_demo
tspan = [0 3000]; y0 = [2;0];
interupt_time = 0.05;
outputFun= #(t,y,flag)interuptFun(t,y,flag,interupt_time);
opts = odeset('AbsTol',1e-8,'RelTol',1e-5,'OutputFcn',outputFun);
try
[t,y] = ode15s(#vdp1000,tspan,y0,opts);
catch ME
if strcmp(ME.identifier,'interuptFun:Interupt')
disp(ME.message);
% Do other things
else
rethrow(ME); % It's possible the error was due to something else
end
end
function status = interuptFun(t,y,flag,interupt_time) %#ok<INUSL>
persistent INIT_TIME;
status = 0;
switch(flag)
case 'init'
INIT_TIME = tic;
case 'done'
clear INIT_TIME;
otherwise
elapsed_time = toc(INIT_TIME);
if elapsed_time > interupt_time
clear INIT_TIME;
str = sprintf('%.6f',elapsed_time);
error('interuptFun:Interupt',...
['Interupted integration. Elapsed time is ' str ' seconds.']);
end
end
One disadvantage of this approach is that you will not get a return value from ode15s. This may or may not be desirable in your application. There are likely ways around this, but they'll require more code. One easy thing that you can do is have the error message return the last time and state of the system (when it was interrupted) using the t and y values passed into the output function. It's also possible that Event functions could be used to terminate integration based on elapsed time – those might allow you to still return output from ode15s.
Related
Suppose in func.m we have
function out = func(in)
for i=1:1000
out=i;
end
end
and after entering >> out = func; in the Matlab cmd, the user interrupts the command execution with Ctrl+C.
Instead of having out equal some integer between 1 and 1000, out is not available in the Global Workspace. If out had been defined before calling out = func;, it would not be updated.
Is there a way to make out available in the Global Workspace upon user interruption and during execution of func as long as it is defined within func?
And if there is a way, will the same method work for cases where a function is interrupted because an error is thrown?
(It may seem trivial if the convenience gained is about a single function. After all, writing to and later reading from harddrive is always an option -- though not an efficient one. Over the years, it's been quite a number of functions where such a functionality would be convenient for me and increase my productivity. So I finally asked.)
So here's a solution that allows to interrupt a loop with a press of a button on a message box.... not ctrl-c, but an alternative way to interrupt a loop:
function out=interrupted_loop_example(in)
f = msgbox('break the loop?') ;
%---------- this is a sample loop:
out=in;
while(~breakloop(f))
out=out+1;
end
%--------------------------------
% clean up:
if ishandle(f) ; delete(f) ; end % to kill the msgbox
% helper function
function x = breakloop(f)
drawnow ; % allowing matlab to detect a button was pressed
x = ~ishandle(f) ;
copy the function, run it, e.g. interrupted_loop_example(100) , and see the ans when you press the button, basically deciding when to break the loop. naturally there will be a toll on performance this way, but you didn't mention anything about performance in your question.
In a MATLAB-function the following code is used:
function stuff()
if a == 2
do1();
else
do2();
end
end
This code is placed inside a simulation-loop and gets called 1000 times or more per second. The if-statement does only matter in the first call of the function, after that either do1 or do2 are used, the variable a will not change any more.
How do I prevent to waste processing time with this if-statement? Basically, how do I tell Matlab, to not check the if-statement any more, and just call the one function, that gets selected in the first call to stuff?
Contrary to your beliefs this is not a problem, the compiler (should) automatically does this optimization for you. See e.g. Loop-invariant code motion.
What you can do to help the compiler is to move the calculation of the check outside as a flag, e.g.
flag = a==2;
for i = 1:100
stuff(flag)
end
Then you only have to do the calculation once and it is clear to the compiler that the value does not change.
NOTE: Obviously, if your check really is a==2, this would not make much of a difference.
EDIT: I have not been able to definitely verify that MATLAB does this automatically. However, this is only the first layer of optimization that is done for you. All modern processors use what is called a Branch predictor, see e.g. this brilliant answer Why is processing a sorted array faster than processing an unsorted array?, or this wiki page. In short, the processor guesses the result of the if-statement, if it is correct, everything goes faster. I think it is fair to say that the processor guesses correctly in all of your cases.
TLDR: Do not wory about it.
Given the comments above, it seems what you are actually looking for is a way to dynamically chose the function to be run in your simulation. This choice should be dynamic (you do not know which function to use at runtime) but the choice should only be done once. This is easily achievable using function handles: https://www.mathworks.com/help/matlab/function-handles.html
Here is an example:
function dynamicSimulation()
if ( rand() > 0.5 ) % determine which function should be called dynamically
sim=#func1;
else
sim=#func2;
end
other_params = [];
for k = 1:5 % run the simulation
sim( k, other_params );
end
end
function func1( index, other_params )
fprintf( 'Index=%d: Simulating using function 1\n', index );
end
function func2( index, other_params )
fprintf( 'Index=%d: Simulating using function 2\n', index );
end
If you run this several times you will notice that the (random) choice of func1 or func2 will mean you do not get the same function being run each time, although the same one is used for the entire simulation.
I reckon you don't waste much time on checking the validity that if statement. However, since you specifically mention it only checks for the first iteration: why not get that out? So instead of:
for ii = 1:10
if ii == 1
k = 1;
else
k = k + 1;
end
end
You could do
k = 1;
for ii = 2:10
k = k + 1;
end
Thus eliminating the check.
NB: this scales badly of course, but as it is only a single iteration here I consider it a good option.
I run a Matlab code with an iteration loop, and during each iteration it sample random numbers and uses the function intlinprog. My issue is that, due to the large amount of data I provide to the intlinprog function and to the stochastic values I assign to part of its variables, some of the iterations take a really long time.
My code is more or less like this:
rounds = 1E3;
Total_PF = zeros(rounds,4893);
for i=1:rounds
i
cT = zeros (4894,1);
cT(4894,1) = 1;
xint = linspace(1,4893,4893);
xint = xint';
AT = rand(4,4894);
beT = ones(4,1);
lb = zeros(4894,1);
ub = ones (4894,1);
ub(4894,1) = Inf;
[x] = intlinprog(cT,xint,AT,beT,[],[],lb,ub);
Total_PF(i,:)= (x(1:length(x)-1)');
end
Now in the minimal working example I provided, all the iterations are quite fast, but in my real code, sometimes intlinprog takes really long time ( I mean hours) to do a single iteration.
Therefore, I was wondering: is there a way to break the intlinprog while the intlinprog line is being executed? I was thinking that it may be done by modifying the matlab function but first of all I do not know if I am allowed to do it, secondly I am afraid that may be very dangerous.
This is very difficult to do effectively.
You could try using a timer object to watch the value. However, since you are using inherent Matlab functions and not executing external functions, you could set a time limit value and utilize tic and toc in a while loop while you execute the intlinprog and checking the value of toc against your time limit and break your code if toc exceeds the limit.
I have an ODE for calculating how acidicity changes. Everything is working just fine, only I would like to change a constant whenever acidicity reaches a critical point. It is supposed to be some kind of irreversible effect I wish to simulate.
My constants are coming from a structure file (c) I load once in the ODE function.
[Time,Results] = ode15s(#(x, c) f1(x, c),[0 c.length],x0,options);
The main problem I have here is not telling Matlab to change the constant but remember if it happened already during the simulation once. so Matlab should take the irreversibly changed constant rather than the one I supply at the beginning.
I would like to write a flag that is saved while the ODE is running and an if condition, "if flag exists, change constant". How to do that?
UPDATE I:
PROBLEM SOLVED
Here a first own solution, it is not polished and requires a structure file approach. Which means, the constants which should suddenly changed upon an event, must be struct files which are handed in the ODE function into the function that should be evaluated (look above for the ODE syntax). The function accepts the inputs like this:
function [OUTPUT] = f1(t, x, c)
% So here, the constants all start with c. followed by the variable name. (because they are structs instead of globals)
%% write a flag when that event happens
if c.someODEevent <= 999 && exist ('flag.txt') == 0
dlmwrite ('flag.txt',1);
end
%% next iteration will either write the flag again or not. more importantly, if it exists, the constant will change because of this.
if exist ('flag.txt') == 2
c.changingconstant = c.changingconstant/2;
end
end
Please look into Horchlers kind answer where you have to take care that such a step may introduce inaccuracies and you have to be careful to check if your code does what it is supposed to do.
To do this accurately, you should use event detection within the ODE solver. I can't give you a specific answer because you've only provided the ode15s call it in your question, but you'll need to write an events function and then specify it via odeset. Something like this:
function acidity_main
% load c data
...
x0 = ...
options = odeset('Events',#events); % add any other options too
% integrate until critical value and stop
[Time1,Results1] = ode15s(#(x,c)f1(x,c),[0 c.length],x0,options);
x0 = Results(end,:); % set new initial conditions
% pass new parameters -it's not clear how you're passing parameters
% if desired, change options to turn off events for faster integration
[Time2,Results2] = ode15s(#(x,c)f1(x,c),[0 c.length],x0,options);
% append outputs, last of 1 is same as first of 2
Time = [Time1;Time2(2:end)];
Results = [Results1;Results2(2:end,:)];
...
function y=f1(x,c)
% your integration function
...
function [value,isterminal,direction] = events(x,c)
value = ... % crossing condition, evaluates to zero at event condition
isterminal = 1; % stop integration when event detected
direction = ... % see documentation
You'll want to use the events to integrate right to the point where the "acidicity reaches a critical point" and stop the integration. Then call ode15s again with the new value and continue the integration. This may seem crude, but it how this sort of thing can be done accurately.
You can see an example of basic event detection here. Type ballode in your command window to see the code for this. You can see a slightly more complex version of this demo in my answer here. Here's an example of using events to accurately change an ODE at specified times (rather than your case of specified state values).
Note: I find it strange that you're passing what you call "constants", c, as the second argument to ode15s. This function has strict input argument requirements: the first is the independent variable (often time), and the second is the array of state variables (same as your initial condition vector). Also if f1 only takes two arguments, #(x,c)f1(x,c) is superfluous – it's sufficient to pass in #f1.
I want to have a waitbar for an operation that takes quite a while. Here is my code:
h = waitbar(0,'Please wait...');
for i=1:counterend
waitbar(i/waitbarcounter)
Atemp = At+i*step;
handle = #(M) 1/M^2*((2/(gamma+1))*(1+(gamma-1)*M^2/2))^((gamma+1)/(gamma-1))-(Atemp/At)^2;
Mach = fzero(handle, 5);
Aplot(i) = Atemp/At;
Tplot(i) = Tc / (1+(gamma-1)*Mach^2/2);
Mplot(i) = Mach;
plot(Aplot, Tplot)
end
close(h)
The error Matlab gives is:
??? Error using ==> waitbar at 249
Improper arguments for waitbar
After investigation, I am sure that this error must occur because of the sorrounding code in the loop.
Note: The loop works fine without the waitbar.
Running
counterend = 10000;
>> h = waitbar(0,'Please wait...');
for i=1:counterend
waitbar(i/counterend)
end
close(h);
Works as expected on 2007a / Windows XP.
On a side note, it would help knowing what countered is defined as. Something quick to check would be to ensure that you are not passing it a CELL.
Running
counterend = {10000};
h = waitbar(0,'Please wait...');
for i=1:counterend
waitbar(i/counterend)
end
close(h);
Yields a different error (see below) in 2007a, but this error message may have changed in 2008.
??? Undefined function or method
'_colonobj' for input arguments of
type 'cell'.
My last bit of advice would be caution you on the use of waitbar for large arrays/data sets. While I think it is important to inform the user of the progress, to me there is also a concern for how much time is added to the loop. Working with arrays that have 100k+ entries, I became a religious user of the Profiler to see where the time was really being spent. I can tell you the time is not in the calculation of the i/X, it was all in updating the waitbar's display. To soften the blow of the update/drawnow, I only updated the waitbar every 100 to 1000 entry which helped tremendously.
EDIT: Updated response to match latest code
I first started to attack this problem at the anonymous function, having problems with them in the past it's a personal vendetta of mine. When looking into the function I found that you are using gamma, do you have this defined as a constant (constant to the loop / function)? The reason I ask is that 'gamma' is a Matlab function and was giving me errors when trying to run your function by itself. Below I have modified you code slightly and this does run fine here. If any of the assumptions I have made are wrong please let me know.
In addition, if you did intend to use the gamma function, your function is missing any arguments to it. Hope this helps!
clc
h = waitbar(0,'Please wait...');
counterend = 1000;
waitbarcounter = counterend;
g_amma = 7;
At = 34;
step = 2;
Tc = 42;
for i=1:counterend
waitbar(i/waitbarcounter)
Atemp = At+i*step;
handle = #(M) 1/M^2*((2/(g_amma+1))*(1+(g_amma-1)*M^2/2))^((g_amma+1)/(g_amma-1))-(Atemp/At)^2;
Mach = fzero(handle, 5);
Aplot(i) = Atemp/At;
Tplot(i) = Tc / (1+(g_amma-1)*Mach^2/2);
Mplot(i) = Mach;
plot(Aplot, Tplot)
end
close(h)
Regards,
Adam
I have checked waitbar on R2008b. So far, the only ways I was able to reproduce your error was by having i/counterend evaluate to an array with multiple rows (a 1x2 vector gives interesting results), and by closing the waitbar before calling waitbar(i/counterend).
I do not get any error running the following:
h = waitbar(0,'Please wait...');
counterend = 1000;
for i=1:counterend
waitbar(i/counterend)
end
close(h)
Could you make sure that the small example above runs without error? If yes, please check that the waitbar is not closed during the execution of the loop, and that counterend is a scalar (use dbstop if error to stop execution of your code at the time of the error).
If the above example does not work without error, you should use which waitbar to check that you are using Matlab's waitbar, and not any of the many updated version from the Matlab File Exchange.
Running
counterned=1000;
h = waitbar(0,'Please wait...');
for i=1:counterend
waitbar(i/counterend)
end
close(h)
works perfectly as expected on MATLAB R2009a on Windows XP.
The above runs fine on R2008a on XP also.
However, you get the error you describe if you kill the waitbar window before the next waitbar command comes around. If you want to be nice about it you should check if the handle h is still valid before issuing waitbar.
I prefer to use progressbar written by Steve Hoelzer on the MATLAB FEX. I haven't had any problems with it.
Your suppose to use the handle that you created with your first line of code when you want to update the waiter,
Waiter(it/itmax,h,'progress')