Fit on one data two peaks with Gauss function with parameters W and A are shared

Version Info

Minimum Origin Version Required: Origin 8 SR0


// before running this function, import \Samples\Curve Fitting\Multiple Peaks.dat to worksheet.

#include <Origin.h>
#include <FDFTree.h>
#include <ONLSF.h>

void       NLFit_example3()
        string                  strFDF = okutil_get_origin_path(ORIGIN_PATH_SYSTEM, "FitFunc") + "Gauss.FDF";
        Tree                    trFF;
        if( !nlsf_FDF_to_tree( strFDF, &trFF ))
                out_str("Fail to load FDF function file");
        // 1. Setting the fitting function and parameter sharing for multipeak fit  //
        // The fit object:
        NLFit                   fit;
        // We will fit two peaks using the same fitting function. We call this "one-replica" case.
        // This means that in addition to one full set of paramaters for the first peak, there are
        // also additional paramaters, but only those within the "replicas unit", for the second peak.
        // Only the fitting functions that allow replicas in its .FDF file can be used for multipeak fitting.
        // The [CONTROLS] section of the .FDF file must contain the following two entries to enable replicas:
        //                Duplicate Offset=2
        //                Duplicate Unit=3
        // (the actual values may be different for different functions). The value Duplicate Offset is the 1-offset index
        // of the first function paramater which belongs to the replicas unit. Duplicate Unit is the size of the replicas
        // unit. The following must hold for all such functions:
        //                total number of parameters for the function = Duplicate Offset - 1 + Duplicate Unit
        // If the above two entries are not present, then the following two entries must be present to allow
        // multipeak fitting:
        //                Replicas Offset=2
        //                Replicas Unit=3
        // The meanings are the same as described above. If you create your own fitting function that you wish
        // to use for multipeak fitting, you MUST use the latter entries.

        // The multiplicity of the function is equal to the number of peaks.
        int                                nFitFunctionMultiplicity = 2;

        // We want also to specify parameter sharing between peaks. This is accomplished via the following array:
        vector<int>                vnSharingGroups(7);
        // The size of this vector for specifying sharing between peaks must be equal to:
        //                replicas Offset - 1 + nFitFunctionMultiplicity * replicas unit
        // In the case of the Gauss function with one replica (i.e. with two peaks) this is:
        //                2 - 1 + 2 * 3 = 7
        // This means that in the vector vnSharingGroups there is one element for each function parameter, including
        // replicas, if any: first all the function parameters for the first peak, followed by only the replicated
        // parameters (i.e. those in the replicas unit) for the additional peaks.
        // The meaning of the array vnSharingGroups: for each function paramater including replicas the value
        // says whether the argument is shared or not, and if shared, how it is shared.
        //                If the value for a particular argument is 0 or less, the parameter is not shared between replicas.
        //                If the value is greater than 0, it is shared with all the other paramaters whose value in this vector
        //                is the same. This value is the so-called group index. The concrete value is not important, as long as
        //                it is greater than 0.
        // We will share paramaters w and A (they are offset 2 and offset 3 in the parameter list for the Gauss function)
        // respectively, that is: w from the first peak will be the same as w for the second peak, whereas
        // A for the first peak will be the same as A for the second peak:
        vnSharingGroups[0] = 0;                        // y0 ("baseline"), not shared
        vnSharingGroups[1] = 0;                        // xc for the first peak, not shared
        vnSharingGroups[2] = 1;                        // w, shared (group index 1) with vnSharingGroups[5] 
        vnSharingGroups[3] = 2;                        // A, shared (group index 2) with vnSharingGroups[6]
        vnSharingGroups[4] = 0;                        // xc for the second peak (i.e. for the first replica), not shared
        vnSharingGroups[5] = 1;                        // w, shared (group index 1) with vnSharingGroups[2]
        vnSharingGroups[6] = 2;                        // A, shared (group index 2) with vnSharingGroups[3]
        // Set the fitting function with the multiplicity of 2 (i.e. one replica).
        int                                nn = fit.SetFunction(trFF, nFitFunctionMultiplicity, -1, vnSharingGroups, vnSharingGroups.GetSize());
        if (nn <= 0)
                out_str("Failed setting fitting function!");

        // 2. Setting data for fitting ////////////////////////////////////////////
        Worksheet       wks = Project.ActiveLayer();
        if( !wks )
                out_str("No active worksheet to get input data");
        DataRange       dr;
        dr.Add(wks, 0, "X");
        dr.Add(wks, 4, "Y"); // Y data from 5th column (Column E) has two peaks
        DWORD           dwRules = DRR_GET_DEPENDENT | DRR_NO_FACTORS;
        int                nNumData = dr.GetNumData(dwRules);
        ASSERT( 1 == nNumData );
        if( nNumData <= 0 || nNumData > 1)
                out_str("Not proper input data to do fit");
        DWORD                   dwPlotID;
        vector                  vX, vY;
        dr.GetData( dwRules, 0, &dwPlotID, NULL, &vY, &vX);
        NLSFONEDATA             stDep, stIndep; 
        stIndep.pdData = vX;
        stIndep.nSize = vX.GetSize();
        stDep.pdData = vY;
        stDep.nSize = vY.GetSize();    
        nn = fit.SetData(1, &stDep, &stIndep);
        if (nn != 0)
                out_str("SetData() failed!");
        // 3. Setting paramaters //////////////////////////////////////////////////

        // Total number of fitting parameters is 5, which is 7 (see above) reduced by two due to two
        // shared paramaters.
        int                                nNumParams = 5;
        vector                  vParams(nNumParams);                  // this vector should be initialized to the total number of paramaters
        // This vector will be used both to set initial paramater values, and
        // to receive the parameter values after fitting:
        vParams[0] = 1.66;                             // y0, not shared 
        vParams[1] = 2.5;                              // xc, not shared
        vParams[2] = 0.3;                              // w, shared (group 1)
        vParams[3] = 50;                               // A, shared (group 2)
        vParams[4] = 7.5;                              // xc for the second peak, not shared
        nn = fit.SetParams(nNumParams, vParams);
        if ( 0 != nn )
                out_str("Invalid paramater setting!");
        // 4. Fitting /////////////////////////////////////////////////////////////
        int                                nMaxNumIterations = 100;
        nn = fit.Fit(nMaxNumIterations);
        printf("Fit(%ld) returned: %ld\n", nMaxNumIterations, nn);
        if( 0 == nn )
                printf("Fit Done!\n");
        // 5. Dump all the results and compare with what was expected /////////////
        for (int iparam = 0; iparam < nNumParams; iparam++)
                printf("param index = %d   value obtained = %lf\n", iparam + 1, vParams[iparam]);