I am trying to evaluate a duffing oscillator in MATLAB with multiple inputs, and I am getting an odd error I was hoping someone might be able to help me with.
Here is the code:
% file duffing.m
function dy=duffing(t,y,a,b,d,g,w)
dy=[y(2); -a*y(1)^3-b*y(1)-d*y(2)+g*cos(w*t)];
end
And the file that calls the duffing function:
t=0:0.01:100;
%duffing oscillator
y0=[2,0];
a=1;
b=-1;
d=0.2;
w=1;
g=0.1;
% duffing oscillator
[t,y]=ode45('duffing',t,y0,a,b,d,g,w);
When I run it, I get the following error:
Error using odearguments (line 92)
DUFFING returns a vector of length 1, but the length of initial conditions vector is 2. The vector returned by DUFFING and the initial conditions vector must have the same number of elements.
But when I define the function duffing as
function dy=duffing(t,y)
a=1;
b=-1;
d=0.2;
w=1;
dy=[y(2); -a*y(1)^3-b*y(1)-d*y(2)+g*cos(w*t)];
end
and pass in
[t y]=ode45('duffing',t,y0);
with the same y0 as above, it runs fine and I can plot it.
What am I doing wrong?
ode45 takes as input a function of two variables only. If you want to pass more inputs, you have to create a little anonymous function, like this:
a=1;
b=-1;
d=0.2;
w=1;
g=0.1;
% duffing oscillator
[t,y]=ode45(#(t,y) duffing(t,y,a,b,d,g,w),t,y0);
The syntax for ode45 is:
function varargout = ode45(ode,tspan,y0,options,varargin)
The fourth argument, options, is an options struct created with odeset. The additional inputs need to be the fifth and later, and the function needs to be modified. The official way is to use a function handle or global variables, but here's how to do it with arguments to ode45.
options = odeset('RelTol',1e-4);
[t,y]=ode45('duffing',t,y0,options,a,b,d,g,w);
Then you have to modify duffing with a dummy argument to be compatible with the calling convention of odearguments.m (f(t,y,'',p1,p2...)):
function dy=duffing(t,y,~,a,b,d,g,w)
dy=[y(2); -a*y(1)^3-b*y(1)-d*y(2)+g*cos(w*t)];
end
The official function handle solution looks something like this:
myDuffing = #(t,y) duffing(t,y,a,b,d,g,w);
Then call ode45 the same way as your second approach, but with myDuffing (see David's solution for this method).
Related
Using Matlab, I have the following code:
s = tf('s')
K=0.5;
H= 1/(s*(s+2));
Hcl=feedback(K*H,1)
ilaplace(Hcl)
rltool(H)
I want to get the inverse laplace transform of the unity closed-loop system.
rltool(H) generates automatically the unity closed-loop system. That's why I pass the open-loop system as an argument.
When I run this code, Matlab gives an error about ilaplace:
"Check for incorrect argument data type or missing argument in call to function 'ilaplace'."
Can someone help me how to use ilaplace and rltool concurrently
I have found a solution for this problem.
syms s;
K=1/2;
H= 1/(s*(s+2))
Hcl=simplify(K*H/(1+K*H))
P=poles(Hcl)
ilaplace(Hcl)
H = syms2tf(Hcl)
rltool(H)
ilaplace works with symbolic expressions and not with a transfer function. By converting the symbolic expression to a transfer function midway of the code, I can use both functions in the same code.
Notice I've added the function simplify and changed the beginning from s=tf('s') to syms s
The function syms2tf() can be found here
I am going to solve an inverse problem, AX=b, using conjugate gradient method in MATLAB. I want to use pcg function in MATLAB and as I know instead of matrix A I can use a function.
I have a function for example afun which has some entries. In the documents, I have seen that the afun function is entered in pcg function without entries, however, when I do the same, the error not enough input arguments appears. I use a code like this:
b = afun(ent1,ent2);
x = pcg(#afun,b,tol,max_iter);
How should I use my function in pcg?
According to the documentation, the function handle should the have the signature afun(x) and return A*x.
Your function apparently takes two inputs... You need to use a anonymous function to wrap the call, something like this:
% I dont know what these ent1/ent2 represent exactly,
% so you must complete the ".." part first
fcn = #(x) afun(x, ..)
% now you can call PCG
x = pcg(fcn, b, tol, maxiter);
There is a doc page explaining how to parameterize functions to pass extra args using function handles.
i am using ODE solver for solving my system of ODE. Now i also want to calculate sensitivity of various parameters by varying them.
My basic ODE solver program is something like:
Function:
function F = ODE_site(t,y)
%%Parameter block
k1f=1e8;
k1b=1e5; %%and so on
%%Elementary rate block
r1=k1f*y(1)-k1b*P*y(5);
and so on
%%Mass balance block
F=[ r1-r5-r78+r86 ;
and so on ];
%% End of function
The main ODE solver looks like:
optode = odeset('NonNegative',1:41,'Abstol',1E-20,'RelTol',1E-20);
y0=zeros(41,1);
y0(41) = 1;
[t,y]=ode15s(#ODE_site,[0,50000000000],y0,optode);
%% Plus some other lines to plot the solution
%% End of ODE solver
Now, I want to introduce a for loop in the main solver but the problem is if i use for loop in the main solver, it cannot pass the command to the function. I tried to use global to assign the variable to all over the environment but it didn't work. How can i assign the for loop for both the ODE solver code and function?
Any help will be highly appreciated.
Thanks,
Mamun
Define the parameters, which you want to modify as addional input parameter to the function:
function F = ODE_site(t,y,param)
%%Parameter block
k1f=param(1);
k1b=param(2); %%and so on
%%Elementary rate block
r1=k1f*y(1)-k1b*P*y(5);
and so on
%%Mass balance block
F=[ r1-r5-r78+r86 ;
and so on ];
%% End of function
You can hand over the parameters to your function in ode15() after the optimisation Parameters:
optode = odeset('NonNegative',1:41,'Abstol',1E-20,'RelTol',1E-20);
y0=zeros(41,1);
y0(41) = 1;
para=[1e5, 1e8]
[t,y]=ode15s(#ODE_site,[0,50000000000],y0,optode,para);
Two notes:
*) Code is untested. Since your example was not executable I didn't care to make a running example too.
*) It looks like you are solving chemical reaction kinetics. Most likely you could further optimize your code by using matrix and vector operations.
So instead of usning separate variables r1, r2, r3, .. you could store it in one vector r(1), r(2), r(3), ...
Your last line, than could be written by the product of the reaction vector with the stoechiometric matrix.
The functions w_d(Y,T) and q(tg,tm) are being used in a function called by the ODE solver described below:
function dPdh=gasstep1(~,P)
global rho_solid W_B S e height_dryer c_a c_b tm_o
dPdh=zeros(size(P));
Y=P(1);tg=P(2);
% Mass Balance
dPdh(1)=rho_solid*(1-e)*S*height_dryer/W_B*w_d(Y,tg);
% Enthalpy balance
dPdh(2)=-rho_solid*(1-e)*S*height_dryer/W_B*(1/(c_b+c_a*Y))...
*(q(tg,tm_o)+(c_a*(tg-tm_o)*w_d(Y,tg)));
dPdh=[dPdh(1)
dPdh(2)];
end
The ODE solver is called in the following manner:
tspan=linspace(0,height_dryer,100);
format long
[h,P]=ode45(#(h,P)gasstep1(h,P),tspan,[Y_o tg_o]);
The values from the generated arrays and P are used in a for loop to compute variables:
X1=zeros(1,Nh);Tm=zeros(1,Nh);
X1(1,1)=X_o;Tm(1,1)=tm_o;
for i=1:Nh-1
% Mass balance
X1(1,i+1)=X1(1,i)-dt*((P(i+1,1)-P(i,1))/(h(i+1)-h(i)))*(W_B/S)*(1/(rho_solid*(1-e)));
% Enthalpy balance
Tm(1,i+1)=Tm(1,i)+dt*(1/(c_s+c_al*X1(1,i)))*(q(((P(i+1,2)+P(i,2))/2),Tm(1,i))-...
((c_a-c_al)*(Tm(1,i))+hv(Tm(1,i)))*w_d((P(i+1,1)-P(i,1)), ((P(i+1,2)+P(i,2))/2)));
end
The doubt I have is whether the variables in the function w_d and q also change when I call the ODE solver in a loop. I found a method to implement the ODE solver in a loop on the mathworks website (link). I have found help provided when ODE parameters need to be changed (link).
I think my final code should look something like:
for j=2:Nt
for i=1:Nh-1
% compute X1 & Tm
end
% change variables in function w_d and q
% [call the ODE]
end
Could anyone please give me an idea on how to start the loop which would help me to complete the code.
The links you pointed to should answer your question: you could make w_d and q parameters of the gasstep function, and use the function parameterization as in the second link you posted.
From the code you posteed, I understand that you want w_d and q to be dependent on X1 and Tm, right? I'm going to assume that's the case. If they depend on other parameters, just use those instead of X1 and Tm in my explanation:
make gasstep1 dependent on X1 and Tm and pass them along to w_d and q: your gasstep1 function now has 4 arguments: gasstep1(h, P, X1, Tm), and w_d and q also (w_d(Y, tg, X1, Tm) for instance). Then you only need to create gasstep2 as such:
gasstep2 = #(h,P) gasstep1(h,P,X1,Tm)
and you can call ode45 on gasstep2!
Be careful that in your final code, if you write it like you presented above, X1 and Tm are going to depend on P, which needs to be initialized!
I know how to use ode15s or other ode solver in MATLAB, what I'm not sure about, is this code(from CellML) that seems vague to me:
[VOI, STATES] = ode15s(#(VOI, STATES)computeRates(VOI, STATES, CONSTANTS), tspan, INIT_STATES, options);
More specifilcly, what is the meaning of the following (?):
#(VOI, STATES)computeRates(VOI, STATES, CONSTANTS)
The header of the function, "computeRates", is the following:
function [RATES, ALGEBRAIC] = computeRates(t, STATES, CONSTANTS)
I know "#computeRates" meanse the handle of the function, but what is the meaning of
#(VOI, STATES)computeRates(VOI, STATES, CONSTANTS)
Why has it put (VOI, STATES) between # and "computeRates" ?
By the way, According the MATLAB help, if we want to integrate of the following function:
function dy = rigid(t,y)
dy = zeros(3,1); % a column vector
dy(1) = y(2) * y(3);
dy(2) = -y(1) * y(3);
dy(3) = -0.51 * y(1) * y(2);
we only need to write:
options = odeset('RelTol',1e-4,'AbsTol',[1e-4 1e-4 1e-5]);
[T,Y] = ode45(#rigid,[0 12],[0 1 1],options)
As R. M. correctly points out, what you are seeing used in that code is an anonymous function. Creating one is done in the following way:
fhandle = #(arglist) expr
Where arglist is a list of input arguments that are used in the computation of the function expression expr.
What you may be confused about is why the code requires that an anonymous function be created instead of just using a function handle for an existing function. The MATLAB solver routines like ode15s and ode45 will only pass two inputs to the function handle passed to them: a scalar t and a column vector y. If you have a situation where you want more parameters to be passed to the function to define its behavior, you have to supply the function with those parameters in other ways as described in the documentation for parameterizing functions.
Anonymous functions are one way to do this. In your example, you can see that the function computeRates takes a third argument CONSTANTS that supplies the function with extra parameters. When the anonymous function is made, this third input is frozen at the value(s) it contained at that moment. The anonymous function therefore acts as a wrapper that makes a three-input function behave like a two-input function so that it can be used by the solver routines, supplying the wrapped function with the extra inputs it needs that the solver routines can't pass to it.
Those are called anonymous functions, and let you create short, nifty functions on the fly without having to create a separate m file. The two variables between the parentheses after the # symbol are the inputs to the function. What follows it is the definition of the function. For example,
f=#(x,y)x+y;%# define an anonymous function to add the two inputs
f(2,3)
ans =
5