Can I use a struct in an anonymous function?

I am solving a PDE via finite volume methods. As a result, I am left with a set of ODEs which I solve with ode15s. I have a bunch of constants which I use in the function defining the derivative, and I insert these by the inclusion of a struct to the function. When I run the ode15s I obtain an error which pertains to the inclusion of the struct as an input to the function. I don't really want to include the constants within the function as that seems "messy", so I would prefer to do it via a struct.
%This is a code to solve the isothermal sintering equations.
%%---Physical parameters---
N = 251; %This is the number of cells-1
L = 1; %This is the initial length of the rod
%%---Initial conditions
%Length
X=linspace(0,L,N); %This is the partition of the initial length
%Density
rho_0_int=0.5+0.1*sin(6*pi*X);
rho_0=0.5*(rho_0_int(1:N-1)+rho_0_int(2:N));
%Amount of mass
h=cumtrapz(X,rho_0_int);
%Initial velocity
u0=zeros(N-1,1);
% Define the system of equations as a function
y0 = [1./rho_0'; u0];
% Finite Volume Method solution loop
tspan = [0 3]; % You can adjust this based on the time range you're interested in
[t, y] = ode15s(@ode_system, tspan, y0);
nu=y(:,1:N-1);
u=y(:,N:2*N-2);
Tspan=length(y(:,1));
for i=1:Tspan
plot(u(i,:))
pause(0.05);
end
function dydt = ode_system(t, y)
g = 9.81; %Acceleration due to gravity.
K = 1e-2; %This is the Laplace constant from the sintering potential.
eta_0 = 0.005; %The shear stress of the skeleton
T = 2.7; %This is the time of sintering.
rho_m = 1; %This is the density of the individual metallic particles.
N = 251; %This is the number of cells-1
L = 1; %Initial length of rod
X=linspace(0,L,N); %This is the partition of the initial length
%Density
rho_0_int=0.5+0.1*sin(6*pi*X);
rho_0=0.5*(rho_0_int(1:N-1)+rho_0_int(2:N));
%Amount of mass
h=cumtrapz(X,rho_0_int);
M=h(N);
U=M/(rho_m*T);
a_1=T/(U*M);
a_2=T*rho_m/M^2;
a_3=g*T/U;
dh=diff(h);
nu = y(1:N-1)'; % u(t)
u = y(N:2*N-2)'; % v(t)
%Compute the left and right specific volumes:
nu_left=15*nu(1)/8-5*nu(2)/4+3*nu(3)/8;
nu_right=15*nu(N-3)/8-5*nu(N-2)/5+3*nu(N-1)/8;
%Compute the fluxes on the interfaces
nu_half=NaN(1,N);
nu_half(2:N-1)=0.5*(nu(2:N-1)+nu(1:N-2));
nu_half(1)=nu_left;
nu_half(N)=nu_right;
%Fluxes for nu equation
nu_flux=zeros(N,1);
nu_flux(2:N-1)=0.5*(u(1:N-2)+u(2:N-1));
du_dh_left=-P_L(K,nu_left)*nu_left/zeta(nu_left);
du_dh_right=-P_L(K,nu_right)*nu_right/zeta(nu_right);
nu_flux(1)=-3*du_dh_left*dh(1)/8+9*u(1)/8-u(2)/8;
nu_flux(N)=3*du_dh_right*dh(N-1)/8-9*u(N-1)/8+u(N-2)/8;
%Fluxes for u equation
u_grad=2*(u(2:N-1)-u(1:N-2))./(dh(2:N-1)+dh(1:N-2));
u_flux=zeros(1,N);
u_flux(2:N-1)=a_1*P_L(K,nu_half(2:N-1))+(a_2*zeta(nu_half(2:N-1))./nu_half(2:N-1)).*u_grad;
% The system of equationsY
dnu_dt = nu_flux(2:N)-nu_flux(1:N-1);
du_dt =u_flux(2:N)'-u_flux(1:N-1)';
% Return the derivatives
dydt = [dnu_dt; du_dt];
end
function y=P_L(K,nu)
y=K./nu;
end
function y=zeta(nu)
y=(2/3)*(nu).^(-2)./(nu-1);
end

 採用された回答

Steven Lord
Steven Lord 2024 年 10 月 2 日

1 投票

When I run the ode15s I obtain an error which pertains to the inclusion of the struct as an input to the function.
Please show us the full and exact text of that error message, including all the text displayed in red in the Command Window (and if there are any warning messages displayed in orange, please show us those too.) The exact text may be useful and/or necessary to determine what's going on and how to avoid the warning and/or error.
At a quick glance, though, you're not actually including the struct as an input to your ODE function.
[t, y] = ode15s(@ode_system, tspan, y0);
function dydt = ode_system(t, y, S)
As you're calling it, ode15s will call your ode_system function with two input arguments, t and y. You have not told it that it should pass the struct S into that function; use one of the techniques shown on this documentation page to do so. [I suspect you expected that MATLAB would automatically "figure out" it should pass the struct from the caller's workspace into ode_system based on the name. It does not.]

17 件のコメント

Mat
Mat 2024 年 10 月 2 日
Not enough input arguments.
Error in sintering_FV>ode_system (line 31)
S.M
Error in odearguments (line 92)
f0 = ode(t0,y0,args{:}); % ODE15I sets args{1} to yp0.
Error in ode15s (line 148)
odearguments(odeIsFuncHandle, odeTreatAsMFile, solver_name, ode, tspan, y0, options, varargin);
Error in sintering_FV (line 28)
[t, y] = ode15s(@ode_system, tspan, y0,S);
Steven Lord
Steven Lord 2024 年 10 月 2 日
My "quick glance" was correct. Parameterize the function in your ode15s call using one of the techniques on the documentation page I linked.
Mat
Mat 2024 年 10 月 2 日
The problem is that I can't really use an anonymous function. So the page you linked is irrelevant. Would I have to use a nested function? This seems a bit of a faff to my mind.
Stephen23
Stephen23 2024 年 10 月 2 日
編集済み: Stephen23 2024 年 10 月 2 日
"The problem is that I can't really use an anonymous function"
What exactly is stopping you from using an anonymous function?
Mat
Mat 2024 年 10 月 2 日
Isn't trivially obvious? The function isn't a one-liner. The whole idea suggested is flawed. Let me explain. The function I use has three inputs, t, y, and S, the struct of variables. That struct comes from the main program. It has to be inserted in the main function, so you still have the issue that issue that was mentioned, ode15s requires sometimg with two inputs, not three.
The other solution is to make all the constants I use global vaiables. That makes the problem go away.
Voss
Voss 2024 年 10 月 2 日
To parameterize using an anonymous function in this case, replace
[t, y] = ode15s(@ode_system, tspan, y0);
with
[t, y] = ode15s(@(t,y)ode_system(t,y,S), tspan, y0);
which is analogous to the example from the documentation page:
You also can use anonymous functions to call more complicated objective functions that you define in a function file. For example, suppose you have a file named cubicpoly.m with this function definition:
function y = cubicpoly(x,b,c)
y = x^3 + b*x + c;
end
At the command line, define b and c, and then call fzero with an anonymous function that invokes cubicpoly:
b = 2;
c = 3.5;
x = fzero(@(x) cubicpoly(x,b,c),0)
However, you'll still run into errors because of problems in the ode_system function itself, which include:
(1) the line
nu_half=NaN(1:S.N);
% ^
should probably be
nu_half=NaN(1,S.N);
% ^
(2) N is undefined and should be S.N throughout the function I imagine
(3) the line
u_grad=2*(u(2:S.N-2)-u(1:S.N-2))./(dh(2:S.N-1)+dh(1:S.N-2));
% ^
will throw an error about incompatible array sizes. I'm guessing it should be
u_grad=2*(u(2:S.N-1)-u(1:S.N-2))./(dh(2:S.N-1)+dh(1:S.N-2));
% ^
(4) an error that the size of the initial conditions vector does not match the size of the results vector, and that's where I stopped trying to debug.
Stephen23
Stephen23 2024 年 10 月 2 日
"Isn't trivially obvious?"
Not to me.
"The function isn't a one-liner"
Totally irrelevant.
"The whole idea suggested is flawed."
I doubt that, it has always worked for me.
"Let me explain. The function I use has three inputs, t, y, and S, the struct of variables. That struct comes from the main program. It has to be inserted in the main function, so you still have the issue that issue that was mentioned, ode15s requires sometimg with two inputs, not three."
That is exactly what function parameterization via an anonymous function achieves:
[t, y] = ode15s(@(t,y)ode_system(t,y,S), tspan, y0);
ODE15S calls a function with two input arguments and ODE_SYSTEM is called with three inputs, including the structure S from the calling workspace. So far I do not see what the problem is.
"The other solution is to make all the constants I use global vaiables. That makes the problem go away."
Do not use global variables for this. Parameterizing the function using an anonymous function is recommended.
Bruno Luong
Bruno Luong 2024 年 10 月 2 日
編集済み: Bruno Luong 2024 年 10 月 2 日
@Mat "Isn't trivially obvious? The function isn't a one-liner. "
You then miss the main principal use case of anonymous function.
You must have a two level of functions; A main one that do the works with many lines of code as you like - a standard function then with extra parameters, then the anonymous serves only as a wrapper to your ode solver with standard calling interface as required by the ode solver but passes extra parameter(s) to the main long function.
You have TWO functions in cascade to work with: the short anonymous that invokes the long main function.
Mat
Mat 2024 年 10 月 3 日
@Stephen23 What worked was put all the variables within the function. There are other issues but I have something that works
Walter Roberson
Walter Roberson 2024 年 10 月 3 日
@Mat comments to Steven Lord:
Not relevant to question
Walter Roberson
Walter Roberson 2024 年 10 月 3 日
Steven Lord's response appears to be entirely correct to me.
Bruno Luong
Bruno Luong 2024 年 10 月 3 日
It takes no less than 6 MVPs to show Mat the light.
Steven Lord
Steven Lord 2024 年 10 月 4 日
I suspect that was a response to a flag I had added to the original question after the code had been edited away.
The code has been restored to the question so I have removed my flag.
Stephen23
Stephen23 2024 年 10 月 5 日
編集済み: Stephen23 2024 年 10 月 6 日
"What worked was put all the variables within the function. "
There is no need to do that. You wrote in your question that "I don't really want to include the constants within the function as that seems "messy", so I would prefer to do it via a struct", which seems a perfectly reasonable approach to me. The documented, recommended solution works, everyone here uses it. Several people have shown you the exact code to use. So far nothing in this thread indicates why it would not work for you.
In contrast you have not shown your modified code (so we have no idea what you attempted, or what you did wrong).
In any case, I would not define those parameters inside the function.
Mat
Mat 2024 年 10 月 7 日
"There is no need to do that."
Turns out there was.
The issue is ode15s, where it uses 2 inputs rather than three as I origianally thought. The other solution is to use global variables.
Bruno Luong
Bruno Luong 2024 年 10 月 7 日
編集済み: Bruno Luong 2024 年 10 月 7 日
Put the parameter variables in a single extra structure, then dispatche it using input structure within your inner function. This is less messy and more efficient than calling the anonymous function with a long list of parameters.
Advantage: When you want to add or remove parameters you only need to change the structure, not the calling interface.
You could do similar with a specific own object class and put parameters in properties when you instantiate the object, but if you are not comfortable with structure no need to work with OOP.
Stephen23
Stephen23 2024 年 10 月 7 日
編集済み: Stephen23 2024 年 10 月 8 日
"Turns out there was."
Turns out there is not.
"The issue is ode15s, where it uses 2 inputs rather than three as I origianally thought...."
As everyone here keeps advising you, that is exactly what function parameterization is for:
Show us your current code and we will show you how.

サインインしてコメントする。

その他の回答 (1 件)

Mat
Mat 2024 年 10 月 4 日

0 投票

Solution has been posted.

3 件のコメント

Bruno Luong
Bruno Luong 2024 年 10 月 7 日
"Solution has been posted."
Where?
Bruno Luong
Bruno Luong 2024 年 10 月 10 日
This is question not a solution, do I miss anything?
@Steven Lord answer should be accepted.

サインインしてコメントする。

カテゴリ

ヘルプ センター および File ExchangeProgramming についてさらに検索

製品

リリース

R2024a

質問済み:

Mat
2024 年 10 月 2 日

コメント済み:

2024 年 10 月 10 日

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by