Nonlinear Curve fitting with integrals

33 views (last 30 days)
I encountered a nonlinear fitting problem, and the fitting formula is shown in Equation (1), which includes two infinite integrals (in practice, the integration range can be set from 0.01E-6 to 200E-6).
In these formulas, except for x and y being vectors, all other variables are scalars, and Rmedian and sigma are the parameters to be fitted.
I found a related post and tried to write the code based on it, but it keeps reporting errors. The error message seems to indicate that the vector dimensions are inconsistent, preventing the operation from proceeding. However, these functions are all calculations for individual scalars.
Error using /
Matrix dimensions must agree.
Error in dsdmain>@(r)1/(2*r*sigma*sqrt(2*pi))*exp(-(log(2*r)-log(2*Rmean)^2)/(2*sigma^2)) (line 13)
gauss = @(r) 1/(2*r*sigma*sqrt(2*pi))* exp( -(log(2*r)-log(2*Rmean)^2)/(2*sigma^2) );
My question is: Can I refer to the content of this post to solve my problem? If yes, what does this error message mean? If not, how should I resolve my problem? (Note: The range of Rmedian is 1E-6 to 5E-6)
After modifying the code according to Walter Roberson and Torsten's suggestion, the program no longer throws errors. But no matter how I set the initial values on my end, it always prompts:
Initial point is a local minimum.
Optimization completed because the size of the gradient at the initial point
is less than the default value of the optimality tolerance.
<stopping criteria details>
theta = initial point
1.0e-05 *
0.2000
0.1000
Optimization completed: The final point is the initial point.
The first-order optimality measure, 0.000000e+00, is less than
options.OptimalityTolerance = 1.000000e-06.
Optimization Metric Options
relative first-order optimality = 0.00e+00 OptimalityTolerance = 1e-06 (default)
I have checked all the formulas and the units of the variables, and I didn't find any problems.
-------------------------------------- Beblow is my code for issue reproduction ----------------------------------------------------------
function testmain()
clc
function Kvec = model(param,xdata)
% Vector of Kd for every xdata:
Kvec = zeros(size(xdata));
Rmean = param(1);
Rstd = param(2);
for i = 1:length(xdata)
model = @(r) unified(xdata(i),r,Rmean,Rstd,delta,Delta,D,lambda);
Kvec(i) = integral(model,0.01E-6,200E-6);
end
end
function s = unified(g,R,Rmean,Rstd,delta,Delta,D,lambda)
%unified Unified fitting model for DSD
% exponentional combined
factor = 1./(2*R*Rstd*sqrt(2*pi)) *2 ; % int(P(r)) = 0.5,1/0.5=2
p1 = -(log(2*R)-log(2*Rmean)).^2/(2*Rstd^2);
c = -2*2.675E8^2*g.^2/D;
tmp = 0;
for il = 1:length(lambda)
a2 = (lambda(il)./R).^2;
an4 = (R/lambda(il)).^4;
Psi = 2+exp(-a2*D*(Delta-delta))-2*exp(-a2*D*delta)-2*exp(-a2*D*Delta)+exp(-a2*D*(Delta+delta));
tmp = tmp+an4./(a2.*R.*R-2).*(2*delta-Psi./(a2*D));
end
p2 = c*tmp;
s = factor.*exp(p1+p2);
end
Delta = 0.075;
delta = 0.002;
D = 0.098E-9;
lambda = [2.0816 5.9404 9.2058 12.4044 15.5792 18.7426 21.8997 25.0528 28.2034 31.3521];
g = [ 0.300616, 0.53884, 0.771392, 1.009616, 1.24784, 1.480392, 1.718616, 1.95684, 2.189392, 2.427616, 2.66584, 2.898392 ];
xdata = g;
ydata = [100, 91.16805426, 80.52955192, 67.97705378, 55.1009735,41.87307917, 30.39638776, 21.13515607, 13.7125649, 8.33083767, 5.146756077, 2.79768609];
ydata = ydata/ydata(1); % normalize
% Inital guess for parameters:
Rmean0 = 2E-6;
Rstd0 = 1E-6;
p0 = [Rmean0;Rstd0];
% lsqcurvefit is in the optimization toolbox.
% fit, from the curve fitting toolbox may be an alternative
theta = lsqcurvefit(@model,p0,xdata,ydata,[0.01E-6;0.1E-6],[10E-6,2E-6])
end

Accepted Answer

Torsten
Torsten on 12 Apr 2025
Edited: Torsten on 13 Apr 2025
This code works for your supplied data:
Delta = 0.075;
delta = 0.002;
D = 0.098E-9;
gamma = 2.675E8;
Rmin = 0.01e-6;
Rmax = 200e-6;
npoints = 100000;
lambda = [2.0816 5.9404 9.2058 12.4044 15.5792 18.7426 21.8997 25.0528 28.2034 31.3521];
xdata = [ 0.300616, 0.53884, 0.771392, 1.009616, 1.24784, 1.480392, 1.718616, 1.95684, 2.189392, 2.427616, 2.66584, 2.898392 ];
ydata = [100,91.16805426,80.52955192,67.97705378,55.1009735,41.87307917,30.39638776,21.13515607,13.7125649,8.33083767,5.146756077,2.79768609]/100;
% Optimize
mu0 = (Rmin + Rmax)/2;
sigma0 = 0.2;
p0 = [mu0,sigma0];
format long
p = lsqcurvefit(@(p,xdata)fun_trapz(p,xdata,Delta,delta,D,gamma,Rmin,Rmax,lambda,npoints),p0,xdata,ydata,[Rmin,0],[Rmax,Inf])
Local minimum possible. lsqcurvefit stopped because the final change in the sum of squares relative to its initial value is less than the value of the function tolerance.
p = 1×2
0.000002705618161 0.001475482837009
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
% Plot results
y = fun_trapz(p,xdata,Delta,delta,D,gamma,Rmin,Rmax,lambda,npoints);
hold on
plot(xdata,ydata)
plot(xdata,y)
hold off
grid on
%
function y = fun_trapz(p,xdata,Delta,delta,D,gamma,Rmin,Rmax,lambda,npoints)
mu = p(1);
sigma = p(2);
y = zeros(size(xdata));
n1 = ceil(npoints*(p(1)-Rmin)/(Rmax-Rmin));
n2 = npoints - n1;
r1 = linspace(Rmin,mu,n1);
r2 = linspace(mu,Rmax,n2);
r = [r1,r2(2:end)];
for i = 1:numel(xdata)
F = fun(r,xdata(i),Delta,delta,D,gamma,Rmin,Rmax,lambda,mu,sigma);
y(i) = trapz(r,F);
end
end
%
function I = fun(r,x,Delta,delta,D,gamma,Rmin,Rmax,lambda,mu,sigma)
alpha = lambda./r.';
Psi = 2 + exp(-alpha.^2*D*(Delta-delta))-...
2*(exp(-alpha.^2*D*Delta)+exp(-alpha.^2*D*delta))+...
exp(-alpha.^2*D*(Delta+delta));
lnE = -2*(gamma*x)^2/D*sum(alpha.^(-4)./(alpha.^2.*(r.').^2-2).*(2*delta-Psi./(alpha.^2*D)),2);
E = exp(lnE);
P = 1./(2*r.'*sigma*sqrt(2*pi)).*exp(-log(r.'/mu).^2/(2*sigma^2));
I = (2*E.*P).';
end
  5 Comments
Shannon
Shannon on 17 Apr 2025
The code I'm using adopted from P313 of https://porousmedia.rice.edu/resources/PhD%20Thesis.pdf , which implements 24-point Gauss-Legendre integration. But my data isn't producing correct results.
Shannon
Shannon on 17 Apr 2025
@Sam Chak How amazing, the Gauss–Kronrod quadrature actually works!

Sign in to comment.

More Answers (2)

Walter Roberson
Walter Roberson on 3 Apr 2025
integral() always passes in a vector of locations to calculate at, unless you use the 'ArrayValued', true option.
sig = integral(f,0.01E-6,200E-6)/integral(gauss,0.01E-6,200E-6);
refers to
f = @(r) gauss(r)*kernel(r,delta,Delta,D)*2;
with the input r being a vector, gauss( r) can be expected to be a vector and kernal(...) can be expected to be a vector, so you have set up for two vectors to be "*" together. As the two vectors are likely the same size, you can expect problems from the * operator which does algebraic matrix multiplication for which the "inner dimensions" must match. You need to use the .* operator.
Meanwhile,
gauss = @(r) 1/(2*r*sigma*sqrt(2*pi))* exp( -(log(2*r)-log(2*Rmean)^2)/(2*sigma^2) );
with the input r being a vector, the 1/(2*r*sigma*sqrt(2*pi)) component is 1/(vector) which is going to fail because the / operator is the matrix-right-divide operator in which the number of columns on the two sides must match. You need to use the ./ operator.
Then the exp() is going to be a vector and you have a * operator again, and that's going to fail because the inner dimensions must be the same size... again you need to be using the .* operator instead of the * operator.
  15 Comments
Torsten
Torsten on 8 Apr 2025
Edited: Torsten on 8 Apr 2025
Yes, that's why I substituted am with R via am = lambda / R
Ah, you solve for the first 10 positive roots for lambda of the equation J_3/2(lambda) = lambda*J_5/2(lambda) .
Is y(i)/y(1) = P(D>=x(i)) where D is the droplet size in mu-m ?
Shannon
Shannon on 9 Apr 2025
R is the droplet size (radius) and the distribution P® is supposed to be a lognornal distribution. Eq(2)

Sign in to comment.


Torsten
Torsten on 8 Apr 2025
Edited: Torsten on 9 Apr 2025
It takes too long to run the code online - try if it produces reasonable results. Maybe you have to set lower and upper bounds for the parameters in "lsqcurvefit".
Note that "mu" and "sigma" are not the expected value and the standard deviation of the Lognormal Distribution. Further I think that "sigma" is dimensionless.
Delta = 0.075;
delta = 0.002;
D = 0.098E-9;
gamma = 2.675E8;
Rmin = 0.01e-6;
Rmax = 200e-6;
lambda = [2.0816 5.9404 9.2058 12.4044 15.5792 18.7426 21.8997 25.0528 28.2034 31.3521];
xdata = [ 0.300616, 0.53884, 0.771392, 1.009616, 1.24784, 1.480392, 1.718616, 1.95684, 2.189392, 2.427616, 2.66584, 2.898392 ];
ydata = [100,91.16805426,80.52955192,67.97705378,55.1009735,41.87307917,30.39638776,21.13515607,13.7125649,8.33083767,5.146756077,2.79768609]/100;
mu0 = (Rmin + Rmax)/2;
sigma0 = 0.2;
% Plot integrand for initial values for mu and sigma
N = 100;
r = linspace(Rmin,Rmax,N);
I = arrayfun(@(x)fun_int(r,x,Delta,delta,D,gamma,Rmin,Rmax,lambda,mu0,sigma0),xdata,'UniformOutput',0);
plot(r,cell2mat(I))
grid on
% Optimize mu and sigma
p0 = [mu0,sigma0];
p = lsqcurvefit(@(p,xdata)fun(p,xdata,Delta,delta,D,gamma,Rmin,Rmax,lambda),p0,xdata,ydata)
function y = fun(p,xdata,Delta,delta,D,gamma,Rmin,Rmax,lambda)
mu = p(1);
sigma = p(2);
y = zeros(numel(xdata),1);
for i = 1:numel(xdata)
F = @(r) fun_int(r,xdata(i),Delta,delta,D,gamma,Rmin,Rmax,lambda,mu,sigma);
y(i) = integral(F,Rmin,Rmax);
end
end
function I = fun_int(r,x,Delta,delta,D,gamma,Rmin,Rmax,lambda,mu,sigma)
I = zeros(numel(r),1);
for i = 1:numel(r)
alpha = lambda/r(i);
Psi = 2 + exp(-alpha.^2*D*(Delta-delta))-...
2*(exp(-alpha.^2*D*Delta)+exp(-alpha.^2*D*delta))+...
exp(-alpha.^2*D*(Delta+delta));
lnE = -2*(gamma*x)^2/D*sum(alpha.^(-4)./(alpha.^2*r(i)^2).*(2*delta-Psi./(alpha.^2*D)));
E = exp(lnE);
P = 1./(2*r(i)*sigma*sqrt(2*pi))*exp(-log(r(i)/mu).^2/(2*sigma^2));
I(i) = 2*E*P;
end
end
  7 Comments
Shannon
Shannon on 11 Apr 2025
I send my data in this post to the authors and they give me the fitting result back. The uncertainty in the width-factor is strange, but at least it can complete the fitting, and the fitted y matches the original y-vector very well.
Shannon
Shannon on 11 Apr 2025
xdata ydata [%] Fitting [%]
0.300616 100 100
0.53884 91.16805426 91.83196659
0.771392 80.52955192 80.6936292
1.009616 67.97705378 67.4689156
1.24784 55.1009735 53.86768785
1.480392 41.87307917 41.40418259
1.718616 30.39638776 30.28783311
1.95684 21.13515607 21.24404364
2.189392 13.7125649 14.45360593
2.427616 8.33083767 9.377799625
2.66584 5.146756077 5.865511151
2.898392 2.79768609 3.587601962

Sign in to comment.

Community Treasure Hunt

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

Start Hunting!