I have defined a bunch of constants. I want to use these in local functions.
The function can't access these values and I get the error
Undefined function or variable 'g'.
I have tried shifting the position of the functions
Also tried to make the constants global so every local function knows them
**% Define constants**
K=0.5; % minor loss coefficient (square edged)
g=9.8; % gravity in m/s2
**%call function**
f1= 2 (value obtained from external function)
v1=velocity1(f1);
**% define function**
function v1=velocity1(f)
v1= sqrt((2*g*h)/(1+(f*(L./D))+K));
end
>> LvsQ_plot
Undefined function or variable 'g'.
Error in LvsQ_plot>velocity1 (line 48)
v1= sqrt((2*g*h)/(1+(f*(L./D))+K));
Error in LvsQ_plot (line 31)
v1=velocity1(f1);
There are multiple approaches passing constants to MATLAB functions
Defining variables as global is a simple solution.
Using global makes the variable "visible" to all functions and scripts. The downside of using global, is that it's not extendable, and prevents code reuse.
From academic software engineering perspective, you shouldn't use global variables at all. Assuming your code is used for solving a specific problem and not going to be extended or reused, using global is permitted.
Using global:
Declare the constants as global before initialization:
global K g
K=0.5; % minor loss coefficient (square edged)
g=9.8; % gravity in m/s2
Declare the variables as global in any function that uses them:
function v1=velocity1(f)
global K g
v1= sqrt((2*g*h)/(1+(f*(L./D))+K));
end
Using nested functions:
Instead of using global, you can use "nested functions" approach - an inner function can access the variables of an outer function.
Define your main script as a function, and velocity1 as an inner function:
function main()
%main is the outer function, and velocity1 is an inner function
K=0.5; % minor loss coefficient (square edged)
g=9.8; % gravity in m/s2
h=1;L=3;D=4;
f1= 2; %(value obtained from external function)
v1=velocity1(f1);
%Inner function:
function v1=velocity1(f)
v1= sqrt((2*g*h)/(1+(f*(L./D))+K));
end
end
Passing a struct of parameters to the function:
A common solution is MATLAB is passing a struct with constant parameters to all functions that uses them:
param.K=0.5; % minor loss coefficient (square edged)
param.g=9.8; % gravity in m/s2
param.h=1;param.L=3;param.D=4;
f1= 2; %(value obtained from external function)
v1=velocity1(f1, param);
function v1=velocity1(f, par)
K = par.K;
g = par.g;
h=par.h;L=par.L;D=par.D;
v1= sqrt((2*g*h)/(1+(f*(L./D))+K));
end
There are other approaches, but I can't list them all...
Related
I have a use case as follows:
Inside F.m I have a function F that takes as its argument a 2 x 1 matrix x. F needs to matrix multiply the matrix kmat by x. kmat is a variable that is generated by a script.
So, what I did was set kmat to be global in the script:
global kmat;
kmat = rand(2);
In F.m:
function result = F(x)
global kmat;
result = kmat*x;
end
Then finally, in the script I have (x_0 has already been defined as an appropriate 2 x 1 matrix, and tstart and tend are positive integers):
xs = ode45(F, [tstart, tend], x_0);
However, this is causing the error:
Error using F (line 3)
Not enough input arguments.
Error in script (line 12)
xs = ode45(F, [tstart, tend], x_0);
What is going on here, and what can I do to fix it? Alternatively, what is the right way to pass kmat to F?
Firstly, the proper way to handle kmat is to make it an input argument to F.m
function result = F(x,kmat)
result = kmat*x;
end
Secondly, the input function to ode45 must be a function with inputs t and x (possibly vectors, t is the dependent variable and x is the dependent). Since your F function doesn't have t as an input argument, and you have an extra parameter kmat, you have to make a small anonymous function when you call ode45
ode45(#(t,x) F(x,kmat),[tstart tend],x_0)
If your derivative function was function result=derivative(t,x), then you simply do ode45(#derivative,[tstart tend],x_0) as Erik said.
I believe F in ode45(F,...) should be a function handle, i.e. #F. Also, you can have a look at this page of the MATLAB documentation for different methods to pass extra parameters to functions.
My Question: Given a function handle, does matlab parse the string each time it needs to evaluate it, or just once and then it caches it?
Example
Consider the ingenious function
function [] = foo(func)
for j=1:1e4
func(j);
end
and the script
func1 = #(x) 5*abs(x)^2
function foo(func1);
At run-time, Matlab needs to interpret #(x) 5*abs(x)^2 as a function. In this example, does it do it once, or a thousand times?
First of all #(x)cos(x) is not a string, it is an anonymous function declaration. When you create an anonymous function, MATLAB essentially creates a function object which contains all of the information it needs to run. This anonymous function can then be passed around to various functions or even saved to a file. As such, it is only constructed once and evaluated many times.
When evaluated, MATLAB does not do any caching, so calling the same anonymous function with the same inputs many times causes the contents of the anonymous function to be evaluated each time.
If you want to get more information about your anonymous function, including the local workspace of the function, you can use the functions function
f = #(x)cos(x);
functions(f)
% function: '#(x)cos(x)'
% type: 'anonymous'
% file: ''
% workspace: {[1x1 struct]}
% within_file_path: '__base_function'
That being said, in your example, it could really be reduced to a function handle rather than an anonymous function since you pass all input arguments directly to cos without modifying them. As you can see, this has a different internal representation and from some preliminary benchmarks, it seems to be marginally faster.
f = #cos
functions(f)
% function: 'cos'
% type: 'simple'
% file: ''
And a quick benchmark
function benchit
fprintf('Anonymous function: %0.4f\n', timeit(#option1));
fprintf('Function handle: %0.4f\n', timeit(#option2));
end
function option2()
f = #(x)cos(x);
for k = 1:10000
f(k);
end
end
function option1()
f = #cos;
for k = 1:10000
f(k);
end
end
And the results (not really a huge difference)
Anonymous function: 0.0056
Function handle: 0.0049
A few more things
When creating the anonymous function, the anonymous function declaration must still adhere to all of MATLAB's standard syntax rules otherwise it won't be created. For example, the following would throw an error during anonymous function creation since it is invalid syntax
func = #(x)thing]
Error: Unbalanced or unexpected parenthesis or bracket.
When you evaluate an anonymous function (after it's successful creation), it's just like evaluating any other function in that the anonymous function can throw an error and the error depends upon the input.
func = #(x) x + [1 2];
func([3 4])
% 4 6
% Now we're going to pass an array that isn't 1 x 2
func([5 6 7])
Matrix dimensions must agree.
Error in #(x)x+[1,2]
function yprime=example1(t , y)
yprime=cos(t)./(2*y-2);
Then type
>> [t,y] =ode45(#example1, [0, 4*pi],3);
>> plot(t , y)
On the line ode45(#example...). Why isn't it ode(#45(t,y)example...)?. How can [0, 4*pi] and 3 be passed into the derivative (i.e. example1) if the input is missing?
The # operator can create two (maybe more) different types of handles: simple and anonymous. A simple function handle is one that directly references a function file and has no other levels of in-direction. An anonymous function is a handle that is itself a (very simple) function and possesses its own workspace for constant storage, closures, and other purposes. The difference can be seen using the functions function:
>> f1 = #example1
f1 =
#example1
>> f2 = #(t,x) example1(t,x)
f2 =
#(t,x)example1(t,x)
>> functions(f1)
ans =
function: 'example1'
type: 'simple'
file: 'C:\Development\example1.m'
>> functions(f2)
ans =
function: '#(t,x)example1(t,x)'
type: 'anonymous'
file: ''
workspace: {[1x1 struct]}
within_file_path: '__base_function'
Anonymous functions add a bit of overhead due to them being more than just pointers to functions and are therefore only really needed if you're parameterizing functions.
Regardless of the creation, ode45 and its kin will always attempt to pass the t and y argument pair to the handle you pass it via feval, and the argument list is only required if you are using anonymous functions versus direct file handle references.
That is just how the ode45 function in MATLAB works.
ode45(#function, [start, end] for t, initial value of y).
So in the example above, function is example1, t0 will be 0, tn (last point) will be 4*pi, and the initial value of y is 3.
The most important part of using ode45 is properly setting up the function. Notice how the function is set for the dy/dt. Because of this, given an initial point, it can generate the rest of the points for y at time t because it can calculate the change in y as t increases.
I am trying to implement the following code in Matlab:
v0=eps;
t0=0; tf=10;
[t,v]=ode45(#const_pow, [t0 tf], v0);
And the const_pow function is:
function f=const_pow(t,v)
f=(P/v-F0-c*v^2)/m;
end
While I am running the program, the following error shows up:
Error using ==> odearguments at 103
CONST_POW returns a vector of length 0, but the length of initial
conditions vector is 1. The vector returned by CONST_POW and the
initial conditions vector must have the same number of elements.
Any suggestions on where the error might be?
From your comments, you're defining some variables globally. In order to access them inside const_pow, you need to tell that function to use the global variables, e.g.:
function f=const_pow(t,v)
global P F0 c m
f=(P/v-F0-c*v^2)/m;
end
Without doing this, the function will not touch the global variables (this is to stop you accidentally changing their values). For example:
function f=const_pow(t,v)
global P F0 c
m = 23.7; % this does not change the global value of m
f=(P/v-F0-c*v^2)/m;
end
An alternative to passing a number of variables without using globals is to define a structure with your required data:
data.P = % initial values
data.F0 =
data.c =
data.m =
This also gives you a little more flexibility if there is more than one set of options. Or, you could pass the variables in directly. To do this with ode45, you would need to parameterize the function using an anonymous function:
function f=const_pow(t,v,P,F0,c,m)
% and so on
end
With P,F0,c,m pre-defined:
odefun = #(t,v) const_pow(t,v,P,F0,c,m); % anonymous function
Then:
[t,v]=ode45(odefun, [t0 tf], v0);
i am trying to solve a differential equation using ode45, I have a function in which one of the parameters has to vary by specific step, here is my function:
function f=RSJ(y,t,M1,P,M2,E,current)
f=(current/P)-(M1/P)*sin(y)+(M2/P)*sin(y+E);
P, M1, M2 & E are numerical constants, current is the parameter which I should resolve this differential equation for several cases, for example current=0:1:10
how can I do such a thing?
Use a closure (a.k.a. anonymous or lambda function):
% declare t, y, tspan and y0
% [...]
current = 6e-7 : 1e-8 : 8.5e-7;
for k=1:length(current)
f = #(y, t, M1, P, M2, E) (current(k)/P)-(M1/P)*sin(y)+(M2/P)*sin(y+E);
[t{k}, y{k}] = ode45(f, tspan, y0);
end
A quick and dirty solution. Define current as a global variable (you need to do this in both the base workspace and in the function) and use a for loop, e.g.
current_vector=1e-7*(6:0.1:8.5);
global current
for k=1:length(current_vector)
current = current_vector(k);
[t{k},y{k}]=ode45(f,<tspan>,<y0>)
end
Replace <tspan> and <y0> by the appropriate values/variables. I assume the other constants are defined in the body of the function, in which case your function should look something like this:
function f=RSJ(t,y)
global current
M1 = ... % etc...
f=(current/P)-(M1/P)*sin(y)+(M2/P)*sin(y+E);
end
BTW, I don't see any explicit dependence on time t in your function...