Nonlinear Curve fitting with integrals
33 views (last 30 days)
Show older comments
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
0 Comments
Accepted Answer
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])
% 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
More Answers (2)
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
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
See Also
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!