Matlab ODE45. How to change a function inside it while calling it? - matlab

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!

Related

General Newton Method Function File Matlab

I have very limited knowledge on Matlab and I'm trying to make a general Newton Raphson function but every time it comes up with an error saying there are not enough input arguments. My code needs three inputs, f (the function), c0 (the initial guess) and n (the number of steps). This is my code so far:
function [c] = Q3(f,c0,n)
for i=1:n
c(0)=c0;
f=f(x);
fp=diff(f,x);
c(i)=c(i-1)-subs(f,x,c(i-1))/subs(fp,c(i-1));
end
disp(c)
I have this function written in a script file
g=#(x)(sin((pi.*x)/2)+(1/x)-(10.*x));
I then put this into the command window [c]=Q3(g(x),1,n) hoping it would work but obviously my code needs work.
Thanks
This should do the trick, the function g is defined as you stated:
g=#(x)(sin((pi.*x)/2)+(1/x)-(10.*x));
but also you should define n and c0, for example:
clearvars
g=#(x)(sin((pi.*x)/2)+(1/x)-(10.*x));
n=10
c0=1
c=Q3(g,c0,n)
And in another file you write the function for NR:
function [c] = Q3(f,c0,n)
h=1e-4;
c(1)=c0;
for i=1:n
g=f(c(i));
gp=(f(c(i)+h)-f(c(i)-h))/(2*h)
c(i+1)=c(i)-g/gp
end
disp(c)
In this case I choose h=1e-4 for the numerical derivative approximation, but you can change it. I suggest h<1e-2.

matlab plot vectors must be the same lengths

I am writing a program regarding solving the following boundary value problem using shooting-bisection method:
y''-y+x=0, y(0)=y(1)=0.
I first convert this to a system of first order equations, set
y'=z
then I let dydt represent the vector (y',z'), and come up with the script file:
function dydt=shoot(t,y)
dydt=[y(2);y(1)-t]
end
With this, I then came up with the following code:
clear
clc
a=0;
b=1;
alpha=0;
beta=0;
s(1)=(beta-alpha)/(b-a);
s(2)=-1
[G,Y]=ode113('shoot',[a b],[alpha;s(1)]);
[G,Z]=ode113('shoot',[a b],[alpha;s(2)])
hold
tol=1e-4
u=s(1);
v=s(2);
while abs(u-v)>tol;
s(3)=(u+v)/2;
[G,W]=ode113('shoot',[a b],[alpha;s(3)]);
if W(end,1)>0
u=s(3);
else
v=s(3);
end
end
[G,W]=ode113('shoot',[a b],[alpha;s(3)])
plot(G,Y(:,1),'-o', G,Z(:,1),'-o',G,W(:,1),'-o')
Then I run the program, MATLAB said I'm using the plot argument incorrectly, where plot vectors must be the same lengths. I have no idea how to fix this problem. Any help is appreciated.
Your Y, Z and W outputs are from different runs of ode113. The output independents variables, G from each run are different because ode113 is an adaptive solver. There are two ways you can fix this. You can save your G outputs as separate variables:
...
[Gy,Y]=ode113('shoot',[a b],[alpha;s(1)]);
[Gz,Z]=ode113('shoot',[a b],[alpha;s(2)]);
...
[Gw,W]=ode113('shoot',[a b],[alpha;s(3)]);
plot(Gy,Y(:,1),'-o', Gz,Z(:,1),'-o',Gw,W(:,1),'-o');
Or you could specify a fixed set of output points by specifying more than two points for tspan (second argument to ode113):
...
tspan = linspace(a,b,50);
[G,Y]=ode113('shoot',tspan,[alpha;s(1)]);
[G,Z]=ode113('shoot',tspan,[alpha;s(2)]);
...
[G,W]=ode113('shoot',tspan,[alpha;s(3)]);
plot(G,Y(:,1),'-o', G,Z(:,1),'-o',G,W(:,1),'-o');
Unless your version of Matlab is more than 10 years old, you should also specify your integration function, shoot, via a function handle, not a string, i.e.:
[Gw,W]=ode113(#shoot,[a b],[alpha;s(3)]);

matlab for loop within ode solver

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.

Errors when using functions with multiple inputs

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).

Can I change the formula of a symbolic function in MATLAB?

I have the following code:
syms t x;
e=symfun(x-t,[x,t]);
In the problem I want to solve x is a function of t but I only know its value at the given t,so I modeled it here as a variable.I want to differentiate e with respect to time without "losing" x,so that I can then substitute it with x'(t) which is known to me.
In another question of mine here,someone suggested that I write the following:
e=symfun(exp(t)-t,[t]);
and after the differentiation check if I can substitute exp(t) with the value of x'(t).
Is this possible?Is there any other neater way?
I'm really not sure I understand what you're asking (and I didn't understand your other question either), but here's an attempt.
Since, x is a function of time, let's make that explicit by making it what the help and documentation for symfun calls an "abstract" or "arbitrary" symbolic function, i.e., one without a definition. In Matlab R2014b:
syms t x(t);
e = symfun(x-t,t)
which returns
e(t) =
x(t) - t
Taking the derivative of the symfun function e with respect to time:
edot = diff(e,t)
returns
edot(t) =
D(x)(t) - 1
the expression for edot(t) is a function of the derivative of x with respect to time:
xdot = diff(x,t)
which is the abstract symfun:
xdot(t) =
D(x)(t)
Now, I think you want to be able to substitute a specific value for xdot (xdot_given) into e(t) for t at t_given. You should be able to do this just using subs, e.g., something like this:
sums t_given xdot_given;
edot_t_given = subs(edot,{t,xdot},{t_given, xdot_given});
You may not need to substitute t if the only parts of edot that are a function of time are the xdot parts.