How to work variables outside function
3 views (last 30 days)
Show older comments
Commented: Walter Roberson on 7 Oct 2022
I have an issue here. I'm working with the built-in Differential Equation Solver ode45 and I have it like:
Vs = 14 * (1852 / 3600); % [m/s] Ship approach speed (reasonable value)
t = (0:250); % Manouevre time
y0 = [Vs 0 0 0 0 0 0]; % Inital condition - surge valocity is equal to the approach speed
option = menu('Choose a Manoeuvre:','Straight Run','Exit Menu');
if option == 1 % Choosing Straight Run
[T,Y] = ode45 ('StraightRun' , t , y0); % Calls Straight Run Function
figure % 1 % Ship Trajectory
axis([0 2500 -500 500])
plot(Y(:,4) , Y(:,5));
xlabel('X pos [m]');
ylabel('Y pos [m]');
legend('Ship Trajectory in Straight Run');
elseif option == 2 % Exit the Menu
in which everything is well defined and working fine.
y0 is the array displaying the initial conditions,
t is time.
StraightRun is a function:
function [dy] = StraightRun(t,y)
What I'm hoping for is that I am able to read a few variables outside the function like, for the X and Y position, I have the need to define X_AH and X_AS inside StraightRun.
How can I plot a graph of those (and other) variables outside the function. Without them having to go through ode45.
Walter Roberson on 6 Oct 2022
People often ask to be able to get a copy outside the ode function of all of the values they computed inside the ode function. It is understandable that people ask that... but it turns out to be the wrong thing to do in the great majority of cases.
ode45() and similar functions always internally have a "current" point -- a current time and current set of boundary conditions. At each step, the ode*() functions have a current step size, h. The ode*() evaluate the ode function at several different carefully chosen locations near the current point, with the locations determined by the stepsize relative to the current point. For example they might evaluate at (t*(1+h), boundary*(1+h/5)), (t*(1+2*h), boundary*(1-h/7)) and others. These are not points that are intended to become current points: these are more like a blind person swinging their cane in a careful pattern to figure out the general shape of the land. Using the results of evaluating at several points, ode*() makes a prediction about yet another point, and then it tests whether the prediction is "close enough" to the actual value. If it is close enough to the actual then ode*() "accepts" the point of prediction as the new point, and increases the step-size; if the prediction is not "close enough" then ode*() "rejects" the point and reduces the step size and tries again from where it was. In steep areas it might end up shrinking the stepsize a number of times in a row -- and at each attempt ode45() is evaluating the ode function at 6 different locations.
ode*() records the values at each "accepted" point. But by default, unless the user knew to set the Refine option to 1 (which I had never even heard of until recently), ode*() also takes the ode point and the newly accepted point, and generates several intermediate points that it records in the output, without ever having evaluated the function at those locations. So there are usually output points recorded for which ode*() never evaluated the ode function (so there could not be a record of the values of the intermediate variables at those locations)... and the complete record of values of intermediate variables would include evaluations at lots of locations where ode*() never intended the point to be accepted.
A record of the intermediate variables would therefore typically be incomplete (the function never having been evaluated for 3/4 of the output locations by default), and the record of intermediate variables would typically have a lot of junk, places never intended to be permanent.
Yes, you might potentially want a complete record of the intermdiate variables if you intended to do a surface plot of the shape of the surface. But mostly the complete record would be too useless for practical work.
For these reasons, what the great majority of people should do is not try to record the intermediate variables during ode*() evaluation, and instead should afterwards take each of the output t and y rows and calculate the intermediate variables related to those positions.
Walter Roberson on 7 Oct 2022
In that case you probably have a singularity.
In some cases, singularities are unavoidable. For example if you were calculating force of gravity between two objects in which the center of gravity is right at the surface, and one is falling towards the other, then unless something else acts to disturb the system, G*m1*m2/r^2 is going to eventually encounter r = 0 and you might not be able to recover from the infinity. In such cases, it is inherent in the system that with those equations a singularity will occur... unless, that is, due to round-off error the system steps over the singularity without noticing it, and so generates what is effectively the wrong answer...
In other cases, singularities are path dependent. A ball might roll very close to a hole without quite falling in, but the numeric approximation of the system might lead it to fall in while a higher resolution approximation might have it miss the hole. In some cases, reducing the tolerance to accept points can force it to be more accurate (but taking longer to compute). In other cases, there just might not be any practical double precision solution -- the band of velocities to miss the hole might be +/- 1 part in 10^20 of a particular value for example, and you cannot drive a double precision simulation that precisely. (Running an ode using the symbolic toolbox might help in such cases.)
Find more on Programming in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!Start Hunting!