Search code examples
matlabmathematical-optimizationnumerical-methodssolvernonlinear-functions

fsolve/fzero: No solution found, appears regular


I am trying to do the following algorithm using fsolve or fzero:

K5=8.37e-2
P=1

Choose an A
    S2=(4*K5/A)^(2/3)
    S6=3*S2
    S8=4*S2

    SO2 = (5*P)/149 - (101*S2)/149 - (293*S6)/149 - (389*S8)/149
    H2O = (40*P)/149 + (556*S2)/447 + (636*S6)/149 + (2584*S8)/447
    H2S = 2*SO2

    newA = (H2O)^2/(SO2)^3

Repeat until newA=oldA

The main thing to solve is K5=1/4 * A * S2^3/2. It is from this that S2 is calculated in the first place.

So here is what I did in Matlab:

function MultipleNLEexample
clear, clc, format short g, format compact

Aguess = 300000; % initial guess

options = optimoptions('fsolve','Display','iter','TolFun',[1e-9],'TolX',[1e-9]); % Option to display output
xsolv=fsolve(@MNLEfun,Aguess,options); 

[~,ans]=MNLEfun(xsolv)

%- - - - - - - - - - - - - - - - - - - - - -
function varargout = MNLEfun(A); 
    K5 = 8.37e-2;

    S2 = (4*K5/A)^(2/3);
    S6 = 3*S2;
    S8 = 4*S2;

    P=1; %atm

    SO2 = (5*P)/149 - (101*S2)/149 - (293*S6)/149 - (389*S8)/149;
    H2O = (40*P)/149 + (556*S2)/447 + (636*S6)/149 + (2584*S8)/447;
    newA=H2O^2/SO2^3;
    fx=1/4*newA*S2^(3/2)-K5;

    varargout{1} = fx;
    if nargout>1
        H2S = 2*SO2;
        varargout{2} = ((2*S2+6*S6+8*S8)/(2*S2+6*S6+8*S8+H2S+SO2)*100);
    end

I cannot get my code to run, I get the following error: No solution found.

fsolve stopped because the problem appears regular as measured by the gradient, but the vector of function values is not near zero as measured by the selected value of the function tolerance.

I have tried setting the tolerances as low as 1e-20 but that did not change anything.


Solution

  • The way your system is set up, it is actually convenient to plot it and observe its behavior. I vectorized your function and plottedf(x) = MLNEfun(x)-x, where the output of MLNE(x) is newA. Effectively, you are interested in a fixed point of your system.

    What I observe is this:

    singularity

    There is a singularity, and a root crossing, at A ~ 3800. We can use fzero since it is a bracketed root solver, and give it very tight bounds on the solution fzero(@(x)MLNEfun(x)-x, [3824,3825]) which produces 3.8243e+03. This is a couple order of magnitudes from your starting guess. There is no solution to your system near ~3e5.

    Update In my haste, I failed to zoom in on the plot, which shows another (well-behaved) root at 1.3294e+04. It is up to you to decide which is the physically meaningful one(s). Everything I say below still applies. Just start your guess near the solution you're interested in.

    In response to comment Since you want to perform this for varying values of K, then your best bet is to stick with fzero so long as you are solving for one variable, instead of fsolve. The reason behind this is that fsolve uses variants of Newton's method, which are not bracketed and will struggle to find solutions at singular points like this. fzero on the other hand, uses Brent's method which is guaranteed to find a root (if it exists) within a bracket. It is also much more well behaved near singular points.

    MATLAB's implementation of fzero also searches for a bracketing interval before carrying out Brent's method. So, if you provide a single starting guess sufficiently close to the root, it should find it for you. Sample output from fzero below:

    fzero(@(x)MLNEfun(x)-x, 3000, optimset('display', 'iter'))
    
    Search for an interval around 3000 containing a sign change:
     Func-count    a          f(a)             b          f(b)        Procedure
        1            3000       -616789          3000       -616789   initial interval
        3         2915.15       -433170       3084.85       -905801   search
        5            2880       -377057          3120  -1.07362e+06   search
        7         2830.29       -311972       3169.71  -1.38274e+06   search
        9            2760       -241524          3240  -2.03722e+06   search
       11         2660.59       -171701       3339.41  -3.80346e+06   search
       13            2520       -109658          3480  -1.16164e+07   search
       15         2321.18      -61340.4       3678.82   -1.7387e+08   search
       17            2040      -29142.6          3960   2.52373e+08   search
    
    Search for a zero in the interval [2040, 3960]:
     Func-count    x          f(x)             Procedure
       17            2040      -29142.6        initial
       18         2040.22      -29158.9        interpolation
       19         3000.11       -617085        bisection
       20         3480.06  -1.16224e+07        bisection
       21            3960   2.52373e+08        bisection
       22         3720.03  -4.83826e+08        interpolation
    ....
       87         3824.32  -5.46204e+48        bisection
       88         3824.32   1.03576e+50        bisection
       89         3824.32   1.03576e+50        interpolation
    
    Current point x may be near a singular point. The interval [2040, 3960] 
    reduced to the requested tolerance and the function changes sign in the interval,
    but f(x) increased in magnitude as the interval reduced.
    
    ans =
    
       3.8243e+03