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.
Related
This works (plots an "empty" figure):
function testme
plot(1)
This works (returns 1):
function testme
plot = #(x)x;
plot(1)
This does not (Error: "Undefined function or variable 'plot'."):
function testme
if 0
plot = #(x)x;
end
plot(1)
What's going on here? Why does rewriting but not redefining an already defined function render the function undefined?
Note 1: this is not specific for builtin functions; the following code returns the same error:
function testme
if 0
myfun = #(x)x;
end
myfun(1)
function x=myfun(x)
x=x*2;
Note 2: the error occurs in a function environment, not in a script; the following code does not return an error (and plots the same empty figure as in the first example):
if 0
plot = #(x)x;
end
plot(1)
Update: For the interested reader, here is some background information
on my original problem. The above examples are just minimum working
samples to illustrate the main issue (which indeed feature dead end if
statements). In practice, I was trying to make a function useable for
colleagues who did not have certain library/toolbox functions
available, by overwriting those functions for simplified custom ones
if they did not exist, as a quick fix. In particular, it concerned
imdilate (and imerode). The function looked something like the
following:
function [myoutputs] = myfunction(myinputs)
% if the images toolbox is not available, use the following simplified
% replacement function
if ~exist('imdilate','file')
imdilate = #(IM,SE)vecdilate(IM,SE);
end
%% The original function goes here, which uses a lot of imdilate(IM,SE).
%% local functions
function M = vecdilate(IM,SE)
% simplified version of imdilate (can only process 1-D vertical arrays)
nSE = size(SE);
nIM = size(IM);
SE = logical(SE); % make logical if it isn't yet
% copy and shift xth column x down. new border entries are 0:
M = repmat([IM;zeros(nSE)],nSE);
M = M(1:end-nSE(1));
M = reshape(M,[size(M,1)/nSE(1) nSE(1)]);
% shrink back to column by taking max of every row:
M = max(M(:,SE),[],2);
M = M(ceil(nSE(1)/2)-1+(1:nIM(1))); % clip to obtain correct size
You might see that the replacement function covers some
functionality of imdilate, but not all, and it might not be as
efficient. The purpose simply was to use function A if it was available, and
function B if it was not. To my surprise however, the former case
returned an error, which eventually resulted in this question. For your interest, I solved the practical problem by renaming the function in the
original code, and by using an if/else statement:
function [myoutputs] = myfunction(myinputs)
% if the images toolbox is not available, use the following simplified
% replacement function
if ~exist('imdilate','file')
mydilate = #(IM,SE)vecdilate(IM,SE);
else
mydilate = #(IM,SE)imdilate(IM,SE);
end
%% The original function goes here, which uses a lot of mydilate(IM,SE).
%% local functions
function M = vecdilate(IM,SE)
etc. etc. etc.
Just-in-time-compilation (JIT) does not mean that there is no compilation and that every line is interpreted separately, so you can still mess with the code;)
The error would also appear if you use a not-defined function, where you woun't even expect the code to run, like
function [] = test()
if false
a = #(x)x;
end
a(1)
end
Scripts are stored command line entries, i.e. the compiler has no choice but to handle every line separately (you may want to think of it as a keyboard macro).
Functions in contrast are encapsulated pieces of code. The compiler (in general) does not expect anything unknown + it thinks that this encapsulated piece of code might be reused. Therefore, it makes sure to do a proper job compile all code once beforehand (if the compile would do this all the time, it is called ahead-of-time compilation).
This becomes in particular obvious when your clear the variables in between:
function [] = test()
if false
plot = #(x)x;
else
clear all % clear vs clear all
end
plot(1)
end
(Note that clear clears all variables but clear all would also clear excising code (see MATLAB Execution Engine))
Have a look at this interesting blog post from Loren
MATLAB provides the best of both worlds by compiling MATLAB code on-the-fly, or just-in-time. MATLAB code is compiled whether it be in classes, functions, scripts, or simply at the command line. There is no explicit compile step for the user to initiate and MATLAB code can be executed in blocks as small as one line at a time. The MATLAB JIT compiler generates native machine level code that is optimized for the MATLAB code being executed and for the specific hardware platform.
Anyway, you should not write dead ends in your code or overwrite (native) functions. It is good to use function handles to overcome this problem, but make sure that you define it for all cases
function [] = test()
if false % dead end definition
fnc = #(x)x;
else
fnc = #plot;
end
fnc(1)
end
I am trying to write an S-Function in Simulink, with the inputs "t" for time, and "LIBs" for the amount of material entering the system. There are 2 outputs. The idea is like this in Simulink.
Simulink model concept
What I want to do is that, at a certain time iteration, the input "LIBs" goes to a different output. But during the iteration, there are some special points at which I don't want the input to go to any output.
The code is like this:
MATLAB
function [Batts_Spent, Batts_N_Spent] = BattsL6Y(t, LIBs)
for t = 2011:6:2035
if t ~= 2005:10:2035
Batts_Spent = LIBs;
end
end
for t = 2015:10:2035
if t ~= 2005:6:2035
Batts_N_Spent = LIBs;
end
end
for t = 2011:6:2035
if t == 2015:10:2035
Batts_Spent = LIBs;
end
end
end
I am sure that this code is not correct, but I don't know how to write it correctly.
And also, even if I have several input and output ports, the S-Function block in the Simulink project still remains only one input and output port. Should I change it to a MATLAB Function block?
In your case t is an input parameter, which makes it look like the function is called for each value of t. Then you have a loop iterating over all values of t. Considering your image, you don't need a loop at all. You can use if in combination with mod. For example:
function [Batts_Spent, Batts_N_Spent] = BattsL6Y(t, LIBs)
if mod(t-2005,6)==0 && t>=2005
Batts_Spent = LIBs;
end
end
This is only "Every 6th time the value of LIBs will be assigned to the first output". This isn't a finished solution but should be sufficient to get you started. You need a second if like this for the other output.
I'm trying to model the respective processes of an internal combustion engine. My current modelling approach is to have different sub functions which model the different processes.
Within each sub function is a Level 2 S-Function which solves the ODEs to give the in cylinder state (pressure, temperature, etc).
The problem that I'm having is that each sub function is enabled depending on the current crank angle which is computed from the current timestep in Simulink. The first process works fine as I manually set the initial values, but then I can't pass the latest in-cylinder state (the output from the first sub function) to the second sub function to use as the initial conditions (it insists on using the initial values I set at the beginning of the simulation).
Is there any way round this? Currently I'm going along a path of global data stores, but haven't had any joy so far.
There are a lot of different ways to solve this problem.
I'll show some of them as examples.
You can create additive output with Unit dalay block like this:
So you can get value of your crank angle from previous timestep and USE IT in formula for solving you equations.
Also you can use some code like this:
if (t == 0)
% equations with your initial values
sred = 0;
else
% equations with other values
y = uOld + myCoeef;
end
Another idea: sometimes I use persistent variables in Matlab function to save values of some variable from previous step. But I think it makes calculation slower.
One more idea - if you have Stateflow you can create chart with two states: first for initial moment with your coefficient and second to solve new equations.
If I understood you in wrong way you can show your code and we'll offer some new ideas!
P.S. Example of my using of S-Function:
My S-Function needs 2 values: Q is calculated in simulink at every step, ro is initial I took from big matrix I loaded from workspace in table and took necessary value depending of time.
So there is no any initial values in S-Function - all needed values I transmit into it from simulink!
Say I have a function foo that can return three values given an input:
function [a,b,c] = foo(input)
The calculations of variables b and c take a long time, so sometimes I may wish to ignore their calculation within foo. If I want to ignore both calculations, I simply call the function like this:
output1 = foo(input);
and then include nargout within foo:
if nargout == 1
% Code to calculate "a" only
else
% Code to calculate other variables
The problem occurs if I want to calculate the last output, but not the second. In that case my function call would be:
[output1,~,output3] = foo(input);
Now if I use nargout within foo to check how many outputs are in the function-call, it will always return 3 because the tilde operator (~) is considered a valid output. Therefore, I cannot use nargout to determine whether or not to calculate the second output, b, within foo.
Is there any other way to do this? I.e., is it possible to check which outputs of a function-call are discarded from within the function itself?
The commenters are basically right; this is not something that can be totally solved by the user unless The MathWorks adds functionality. However, I wrote a small function, istilde (Wayback Machine Archive), a while back that attempts to do what you ask. It works in many cases, but it's really a bit of hack and not a totally robust solution. For example, I did not attempt to get it to work for functions called from the Command Window directly (this could potentially be added with some work). Also, it relies on parsing the actual M-file, which can have issues. See the included demo file for how one might use istilde.
Feel free to edit my code for your needs – just don't use this in any production code due to the robustness issues. Any improvements would be welcome.
when I am doing a function in Matlab. Sometimes I have equations and every one of these have constants. Then, I have to declare these constants inside my function. I wonder if there is a way to call the values of that constants from outside of the function, if I have their values on the workspace.
I don't want to write this values as inputs of my function in the function declaration.
In addition to the solutions provided by Iterator, which are all great, I think you have some other options.
First of all, I would like to warn you about global variables (as Iterator also did): these introduce hidden dependencies and make it much more cumbersome to reuse and debug your code. If your only concern is ease of use when calling the functions, I would suggest you pass along a struct containing those constants. That has the advantage that you can easily save those constants together. Unless you know what you're doing, do yourself a favor and stay away from global variables (and functions such as eval, evalin and assignin).
Next to global, evalin and passing structs, there is another mechanism for global state: preferences. These are to be used when it concerns a nearly immutable setting of your code. These are unfit for passing around actual raw data.
If all you want is a more or less clean syntax for calling a certain function, this can be achieved in a few different ways:
You could use a variable number of parameters. This is the best option when your constants have a default value. I will explain by means of an example, e.g. a regular sine wave y = A*sin(2*pi*t/T) (A is the amplitude, T the period). In MATLAB one would implement this as:
function y = sinewave(t,A,T)
y = A*sin(2*pi*t/T);
When calling this function, we need to provide all parameters. If we extend this function to something like the following, we can omit the A and T parameters:
function y = sinewave(t,A,T)
if nargin < 3
T = 1; % default period is 1
if nargin < 2
A = 1; % default amplitude 1
end
end
y = A*sin(2*pi*t/T);
This uses the construct nargin, if you want to know more, it is worthwhile to consult the MATLAB help for nargin, varargin, varargout and nargout. However, do note that you have to provide a value for A when you want to provide the value of T. There is a more convenient way to get even better behavior:
function y = sinewave(t,A,T)
if ~exists('T','var') || isempty(T)
T = 1; % default period is 1
end
if ~exists('A','var') || isempty(A)
A = 1; % default amplitude 1
end
y = A*sin(2*pi*t/T);
This has the benefits that it is more clear what is happening and you could omit A but still specify T (the same can be done for the previous example, but that gets complicated quite easily when you have a lot of parameters). You can do such things by calling sinewave(1:10,[],4) where A will retain it's default value. If an empty input should be valid, you should use another invalid input (e.g. NaN, inf or a negative value for a parameter that is known to be positive, ...).
Using the function above, all the following calls are equivalent:
t = rand(1,10);
y1 = sinewave(t,1,1);
y2 = sinewave(t,1);
y3 = sinewave(t);
If the parameters don't have default values, you could wrap the function into a function handle which fills in those parameters. This is something you might need to do when you are using some toolboxes that impose constraints onto the functions that are to be used. This is the case in the Optimization Toolbox.
I will consider the sinewave function again, but this time I use the first definition (i.e. without a variable number of parameters). Then you could work with a function handle:
f = #(x)(sinewave(x,1,1));
You can work with f as you would with an other function:
e.g. f(10) will evaluate sinewave(10,1,1).
That way you can write a general function (i.e. sinewave that is as general and simple as possible) but you create a function (handle) on the fly with the constants substituted. This allows you to work with that function, but also prevents global storage of data.
You can of course combine different solutions: e.g. create function handle to a function with a variable number of parameters that sets a certain global variable.
The easiest way to address this is via global variable:
http://www.mathworks.com/help/techdoc/ref/global.html
You can also get the values in other workspaces, including the base or parent workspace, but this is ill-advised, as you do not necessarily know what wraps a given function.
If you want to go that route, take a look at the evalin function:
http://www.mathworks.com/help/techdoc/ref/evalin.html
Still, the standard method is to pass all of the variables you need. You can put these into a struct, if you wish, and only pass the one struct.