Boundary value problem in MATLAB, issue with fsolve - matlab

I am trying to solve and ODE with boundary conditions at 0 and end, ws.mat can be downloaded at https://gofile.io/?c=RYzPsO
clear all
clc
global omega eta_max_ode eta F T Tp
eta_max_ode = 10;
omega=0.76;
load('ws','T','Tp','F','eta')
IC=[0,1];
opt = optimset('Display','off','TolFun',1E-20);
FI = fsolve(#(FI) eval_boundary_UVWTI(FI),IC,opt)
[eta_ode_i, fg_ode_i] = solve_UVWTI(FI);
sol_i = [fg_ode_i];
eta_i = eta_ode_i;
plot(eta_i,sol_i(:,1))
function [dfi]=UVWTI(t,fi)
global omega eta_max_ode eta F T Tp
for i=1:length(eta)
if eta(i)<t
else
inde=i;
break
end
end
A11=0;
A12=1;
A21=0;
A22=-(T(inde)^(1-omega))*F(inde)-omega*Tp(inde)/T(inde)+Tp(inde)/T(inde);
dfi=zeros(2,1);
dfi(1)=A11*fi(1)+A12*fi(2);
dfi(2)=A21*fi(1)+A22*fi(2);
end
function [eta_ode_i, fg_ode_i] = solve_UVWTI(FI)
global eta_max_ode eta
options = odeset('RelTol',1e-9,'AbsTol',1e-9);
[eta_ode_i, fg_ode_i] = ode45(#UVWTI,eta,FI,options);
size(fg_ode_i);
end
function [gi] = eval_boundary_UVWTI(FI)
% Get the solution to the ODE with inital condition F
[eta_ode_i, fg_ode_i] = solve_UVWTI(FI);
% Get the function values (for BCs) at the starting/end points
w_start = fg_ode_i(1,1); %w(0) = 0
w_end = fg_ode_i(end,1); %w(inf) = 0
% Evaluate the boundary function
gi = [
w_start
w_end - 1
];
end
I obtained the correct behaviour this is the solution tending to a constant. However, I should get sol_i(:,1) tending to 1 and fsolve does not seem to calculate the correct initial condition so that this happen. What's wrong in the code? eval_boundary_UVWTI() seems to be correct

Related

How do I change the value of a parameter in ODE solver, such as ODE45, within MATLAB?

I have to solve this equation in matlab with ode45 when T is in range [0,12]:
I want that when:
T > Tend, switch is 0,
(T < Tin and dT/dt > 0), switch is 1.
I see the documentation here but I can't understand how do it.
clc;
clear all;
close all;
global Q Tamb n swicth
Tamb = 0;
Q = 1000;
n = 1/3;
Ti = 0;
swicth = 1;
T_end = 2000;
maxT = 12;
tspan = [0 maxT];
CI = [Ti];
[TOUT,YOUT] = ode45(#odefun,tspan, CI); <--------- ??
odefun function:
function T = odefun(t, Tin)
global Q Tamb n swicth
T = (swicth*Q - (Tin - Tamb)^n);
end
As I said above, I don't want to pass a parameter to a function! Perhaps the previous title was misleading, but I think my question was clear. However I try to explain better. Ode45 numerical integrate my function, so it gives me the value of a function at time t_i (f(t_i)) and this is repeated for each t_i in [0:12]. I want, based on the value of the function at a time t_i, to change the value of the switch for the integration at time t_(i+1).[The rules is explain above].

Solving coupled nonlinear differential equations

I have a differential equation that is as follows:
%d/dt [x;y] = [m11 m12;m11 m12][x;y]
mat = #(t) sin(cos(w*t))
m11 = mat(t) + 5 ;
m12 = 5;
m21 = -m12 ;
m22 = -m11 ;
So I have that my matrix is specifically dependent on t. For some reason, I am having a super difficult time solving this with ode45. My thoughts were to do as follows ( I want to solve for x,y at a time T that was defined):
t = linspace(0,T,100) ; % Arbitrary 100
x0 = (1 0); %Init cond
[tf,xf] = ode45(#ddt,t,x0)
function xprime = ddt(t,x)
ddt = [m11*x(1)+m12*x(2) ; m12*x(1)+m12*x(2) ]
end
The first error I get is that
Undefined function or variable 'M11'.
Is there a cleaner way I could be doing this ?
I'm assuming you're running this within a script, which means that your function ddt is a local function instead of a nested function. That means it doesn't have access to your matrix variables m11, etc. Another issue is that you will want to be evaluating your matrix variables at the specific value of t within ddt, which your current code doesn't do.
Here's an alternative way to set things up that should work for you:
% Define constants:
w = 1;
T = 10;
t = linspace(0, T, 100);
x0 = [1 0];
% Define anonymous functions:
fcn = #(t) sin(cos(w*t));
M = {#(t) fcn(t)+5, 5; -5 #(t) -fcn(t)-5};
ddt = #(t, x) [M{1, 1}(t)*x(1)+M{2, 1}*x(2); M{1, 2}*x(1)+M{2, 2}(t)*x(2)];
% Solve equations:
[tf, xf] = ode45(ddt, t, x0);
One glaring error is that the return value of function ddt is xprime, not ddt. Then as mentioned in the previous answer, mm1 at the time of definition should give an error as t is not defined. But even if there is a t value available at definition, it is not the same t the procedure ddt is called with.
mat = #(t) sin(cos(w*t))
function xprime = ddt(t,x)
a = mat(t) + 5 ;
b = 5;
ddt = [ a, b; -b, -a]*x
end
should work also as inner procedure.

Solving Coupled partial differential equations of stiff nature using MATLAB

I want to solve coupled partial differential equations of first order, which are of stiff nature. I have coded in MATLAB to solve this pde's, I have used Method of line to convert PDE into ODE, and i have used beam and warmings(second order upwind) method to discritize the spatial derivative. The discretization method is total variation diminishing(TVD) to eliminate the oscillation. But rather using TVD and ode15s solver to integrate resultant stiff ode's the resultant plot is oscillatory(not smooth). What should i do to eliminate this oscillation and get correct results.
I have attached my MATLAB code.. please see it and suggest some improvement.
∂y(1)/∂t=-0.1 ∂y(1)/∂x + (0.5*e^(15*(y(2)⁄(1+y(2))))*(1- y(1))
∂y(2)/∂t=-0.1 ∂y(2)/∂x - (0.4*e^(15*(y(2)⁄(1+y(2))))*(1- y(1))-0.4
Initial condition: at t = 0 y(1)= y(2)=0
Boundary condition: y(1)= y(2) = 0 at x=0
I have attached my MATLAB code.. please see it and suggest some improvement.
function brussode(N)
if nargin<1
N = 149;
end
tspan = [0 10];
m = 0.00035
t = (1:N)/(N+1)*m;
y0 = [repmat(0,1,N); repmat(0,1,N)];
p = 0.5
q = 0.4
options = odeset('Vectorized','on','JPattern',jpattern(N));
[t,y] = ode15s(#f,tspan,y0,options);
a = size(y,2)
u = y(:,1:2:end);
x = (1:N)/(N+1);
figure;
%surf(x,t(end,:),u);
plot(x,u(end,:))
xlabel('space');
ylabel('solution');
zlabel('solution u');
%--------------------------------------------------------------
%Nested function -- N is provided by the outer function.
%
function dydt = f(t,y)
%Derivative function
dydt = zeros(2*N,size(y,2)); %preallocate dy/dt
x = (1:N)/(N+1);
% Evaluate the 2 components of the function at one edge of the grid
% (with edge conditions).
i = 1;
%y(1,:) = 0;
%y(2,:) = 0;
dydt(i,:) = -0.1*(N+1)*(y(i+2,:)-0)+ (0.01/2)*m*((N+1).^3)*(y(i+2,:)-0) + p*exp(15*(0/(1+0)))*(1-0);
dydt(i+1,:) = -0.1*(N+1)*(y(i+3,:)-0)+ (0.01/2)*m*((N+1).^3)*(y(i+3,:)-0) - q*exp(15*(0/(1+0)))*(1-0)+0.25;
i = 3;
%y(1,:) = 0;
%y(2,:) = 0;
dydt(i,:) = -0.1*(N+1)*(y(i+2,:)-y(i,:)) + (0.01/2)*m*((N+1).^3)*(y(i+3,:)-y(i,:)) + p*exp(15*(y(i+1,:)/(1+y(i+1,:))))*(1-y(i,:));
dydt(i+1,:) = -0.1*(N+1)*(y(i+3,:)-y(i+1,:)) + (0.01/2)*m*((N+1).^3)*(y(i+3,:)-y(i,:)) - q*exp(15*(y(i+1,:)/(1+y(i+1,:))))*(1-y(i,:))+0.25;
%Evaluate the 2 components of the function at all interior grid
%points.
i = 5:2:2*N;
%y(1,:) = 0;
% y(2,:) = 0;
dydt(i,:) = (-0.1/2)*(N+1)*(3*y(i,:)-4*y(i-2,:)+y(i-4,:)) +(0.01/2)*m*((N+1).^3)*(y(i,:)-2*y(i-2,:)+y(i-4,:))+ p*exp(15*(y(i+1,:)/(1+y(i+1,:))))*(1-y(i,:));
dydt(i+1,:) = (-0.1/2)*(N+1)*(3*y(i+1,:)-4*y(i-1,:)+y(i-3,:))+(0.01/2)*m*((N+1).^3)*(y(i+1,:)-2*y(i-1,:)+y(i-3,:)) - q*exp(15*(y(i+1,:)/(1+y(i+1,:))))*(1-y(i,:))+0.25;
end
%-------------------------------------------------------------
end %brussode
%-------------------------------------------------------------
% Subfunction -- the sparsity pattern
%
function S = jpattern(N)
% Jacobian sparsity patter
B = ones(2*N,5);
B(2:2:2*N,2) = zeros(N,1);
B(1:2:2*N-1,4) = zeros(N,1);
S = spdiags(B,-2:2,2*N,2*N);
end
%-------------------------------------------------------------

Pass extra variable parameters to ode15s function (MATLAB)

I'm trying to solve a system of ordinary differential equations in MATLAB.
I have a simple equation:
dy = -k/M *x - c/M *y+ F/M.
This is defined in my ode function test2.m, dependant on the values X and t. I want to trig 'F' with a signal, generated by my custom function squaresignal.m. The output hereof, is the variable u, spanding from 0 to 1, as it is a smooth heaviside function. - Think square wave. The inputs in squaresignal.m, is t and f.
u=squaresignal(t,f)
These values are to be used inside my function test2, in order to enable or disable variable 'F' with the value u==1 (enable). Disable for all other values.
My ode function test2.m reads:
function dX = test2(t ,X, u)
x = X (1) ;
y = X (2) ;
M = 10;
k = 50;
c = 10;
F = 300;
if u == 1
F = F;
else
F = 0,
end
dx = y ;
dy = -k/M *x - c/M *y+ F/M ;
dX = [ dx dy ]';
end
And my runscript reads:
clc
clear all
tstart = 0;
tend = 10;
tsteps = 0.01;
tspan = [0 10];
t = [tstart:tsteps:tend];
f = 2;
u = squaresignal(t,f)
for ii = 1:length(u)
options=odeset('maxstep',tsteps,'outputfcn',#odeplot);
[t,X]=ode15s(#(t,X)test2(t,X,u(ii)),[tstart tend],[0 0],u);
end
figure (1);
plot(t,X(:,1))
figure (2);
plot(t,X(:,2));
However, the for-loop does not seem to do it's magic. I still only get F=0, instead of F=F, at times when u==1. And i know, that u is equal to one at some times, because the output of squaresignal.m is visible to me.
So the real question is this. How do i properly pass my variable u, to my function test2.m, and use it there to trig F? Is it possible that the squaresignal.m should be inside the odefunction test2.m instead?
Here's an example where I pass a variable coeff to the differential equation:
function [T,Q] = main()
t_span = [0 10];
q0 = [0.1; 0.2]; % initial state
ode_options = odeset(); % currently no options... You could add some here
coeff = 0.3; % The parameter we wish to pass to the differential eq.
[T,Q] = ode15s(#(t,q)diffeq(t,q,coeff),t_span,q0, ode_options);
end
function dq = diffeq(t,q,coeff)
% Preallocate vector dq
dq = zeros(length(q),1);
% Update dq:
dq(1) = q(2);
dq(2) = -coeff*sin(q(1));
end
EDIT:
Could this be the problem?
tstart = 0;
tend = 10;
tsteps = 0.01;
tspan = [0 10];
t = [tstart:tsteps:tend];
f = 2;
u = squaresignal(t,f)
Here you create a time vector t which has nothing to do with the time vector returned by the ODE solver! This means that at first we have t[2]=0.01 but once you ran your ODE solver, t[2] can be anything. So yes, if you want to load an external signal source depending on time, then you need to call your squaresignal.m from within the differential equation and pass the solver's current time t! Your code should look like this (note that I'm passing f now as an additional argument to the diffeq):
function dX = test2(t ,X, f)
x = X (1) ;
y = X (2) ;
M = 10;
k = 50;
c = 10;
F = 300;
u = squaresignal(t,f)
if u == 1
F = F;
else
F = 0,
end
dx = y ;
dy = -k/M *x - c/M *y+ F/M ;
dX = [ dx dy ]';
end
Note however that matlab's ODE solvers do not like at all what you're doing here. You are drastically (i.e. non-smoothly) changing the dynamics of your system. What you should do is to use one of the following:
a) events if you want to trigger some behaviour (like termination) depending on the integrated variable x or
b) If you want to trigger the behaviour based on the time t, you should segment your integration into different parts where the differential equation does not vary during one segment. You can then resume your integration by using the current state and time as x0 and t0 for the next run of ode15s. Of course this only works of you're external signal source u is something simple like a step funcion or square wave. In case of the square wave you would only integrate for a timespan during which the wave does not jump. And then exactly at the time of the jump you start another integration with altered differential equations.

Solving PDE with Matlab

`sol = pdepe(m,#ParticleDiffusionpde,#ParticleDiffusionic,#ParticleDiffusionbc,x,t);
% Extract the first solution component as u.
u = sol(:,:,:);
function [c,f,s] = ParticleDiffusionpde(x,t,u,DuDx)
global Ds
c = 1/Ds;
f = DuDx;
s = 0;
function u0 = ParticleDiffusionic(x)
global qo
u0 = qo;
function [pl,ql,pr,qr] = ParticleDiffusionbc(xl,ul,xr,ur,t,x)
global Ds K n
global Amo Gc kf rhop
global uavg
global dr R nr
sum = 0;
for i = 1:1:nr-1
r1 = (i-1)*dr; % radius at i
r2 = i * dr; % radius at i+1
r1 = double(r1); % convert to double precision
r2 = double(r2);
sum = sum + (dr / 2 * (r1*ul+ r2*ur));
end;
uavg = 3/R^3 * sum;
ql = 1;
pl = 0;
qr = 1;
pr = -((kf/(Ds.*rhop)).*(Amo - Gc.*uavg - ((double(ur/K)).^2).^(n/2) ));`
dq(r,t)/dt = Ds( d2q(r,t)/dr2 + (2/r)*dq(r,t)/dr )
q(r, t=0) = 0
dq(r=0, t)/dr = 0
dq(r=dp/2, t)/dr = (kf/Ds*rhop) [C(t) - Cp(at r = dp/2)]
q = solid phase concentration of trace compound in a particle with radius dp/2
C = bulk liquid concentration of trace compound
Cp = trace compound concentration at particle surface
I want to solve the above pde with initial and boundary conditions given. Tried Matlab's pdepe, but does not work satisfactorily. Maybe the boundary conditions is creating problem for me. I also used this isotherm equation for equilibrium: q = K*Cp^(1/n). This is convection-diffusion equation but i could not find any write ups that addresses solving this type of equation properly.
There are two problems with the current implementation.
Incorrect Source Term
The PDE you are attempting to solve has the form
which has the equivalent form
where the last term arises due to the factor of 2 in the original PDE.
The last term needs to be incorporated into pdepe via a source term.
Calculation of q average
The current implementation attempts to calculate the average value of q using the left and right values of q passed to the boundary condition function.
This is incorrect.
The average value of q needs to be calculated from a vector of up-to-date values of the quantity.
However, we have the complication that the only function to receive all mesh values is ParticleDiffusionpde; however, the mesh values passed to that function are not guaranteed to be from the mesh we provided.
Solution: use events (as described in the pdepe documentation).
This is a hack since the event function is meant to detect zero-crossings, but it has the advantage that the function is given all values of q on the mesh we provide.
So, the working example below (you'll notice I set all of the parameters to 1 since I didn't know better) uses the events function to update a variable qStore that can be accessed by the boundary condition function (see here for an explanation), and the boundary condition function performs a vectorized trapezoidal integration for the average calculation.
Working Example
function [] = ParticleDiffusion()
% Parameters
Ds = 1;
q0 = 0;
K = 1;
n = 1;
Amo = 1;
Gc = 1;
kf = 1;
rhop = 1;
% Space
rMesh = linspace(0,1,10);
rMesh = rMesh(:) ;
dr = rMesh(2) - rMesh(1) ;
% Time
tSpan = linspace(0,1,10);
% Vector to store current u-value
qStore = zeros(size(rMesh));
options.Events = #(m,t,x,y) events(m,t,x,y);
% Solve
[sol,~,~,~,~] = pdepe(1,#ParticleDiffusionpde,#ParticleDiffusionic,#ParticleDiffusionbc,rMesh,tSpan,options);
% Use the events function to update qStore
function [value,isterminal,direction] = events(m,~,~,y)
qStore = y; % Value of q on rMesh
value = m; % Since m is constant, it will never be zero (no event detection)
isterminal = 0; % Continue integration
direction = 0; % Detect all zero crossings (not important)
end
function [c,f,s] = ParticleDiffusionpde(r,~,~,DqDr)
% Define the capacity, flux, and source
c = 1/Ds;
f = DqDr;
s = DqDr./r;
end
function u0 = ParticleDiffusionic(~)
u0 = q0;
end
function [pl,ql,pr,qr] = ParticleDiffusionbc(~,~,R,ur,~)
% Calculate average value of current solution
qL = qStore(1:end-1);
qR = qStore(2: end );
total = sum((qL.*rMesh(1:end-1) + qR.*rMesh(2:end))) * dr/2;
qavg = 3/R^3 * total;
% Left boundary
pl = 0;
ql = 1;
% Right boundary
qr = 1;
pr = -(kf/(Ds.*rhop)).*(Amo - Gc.*qavg - (ur/K).^n);
end
end