3.9.2.2.4 Fit on one data two peaks with two functions

Version Info

Minimum Origin Version Required: Origin 8 SR1

Examples

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

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

void	NLFit_example4()
{
	// Two functions:
	// (1) Gauss
	string			strFDF1 = okutil_get_origin_path(ORIGIN_PATH_SYSTEM, "FitFunc") + "Gauss.FDF";
	Tree			trFF1;
	if( !nlsf_FDF_to_tree( strFDF1, &trFF1 ))
	{
		out_str("Fail to load FDF function file");
		return;
	}
	
	// (2) Lorentz
	string			strFDF2 = okutil_get_origin_path(ORIGIN_PATH_SYSTEM, "FitFunc") + "Lorentz.FDF";
	Tree			trFF2;
	if( !nlsf_FDF_to_tree( strFDF2, &trFF2 ))
	{
		out_str("Fail to load FDF function file");
		return;	
	}
	
	
	//////////////////////////////////////////////////////////////////////////////
	// 1. Setting the fitting function and parameter sharing for multipeak fit  //

	// The fit object:
	NLFit			fit;
	
	// We will fit to a total of four peaks, two Gaussian and two Lorentzian peaks,
	// with some parameter sharing between peaks.
	// Each function will therefore have the multiplicity of 2 (i.e. one replica in each function).
	//
	// The first function has 4 original paramaters, 3 of which are in replicas unit.
	// Since the function will have multiplicity of 2, this means that per function
	// (i.e. for its two peaks) there are 7 paramaters:
	//		7 = replicas Offset - 1 + nFitFunctionMultiplicity * replicas unit
	// since for both Gauss and Lorentz replicas Offset = 2, replicas unit = 3
	// and we use nFitFunctionMultiplicity = 2.
	int				nFitFunctionMultiplicity = 2;
	
	// To set up parameter sharing between peaks, we use an auxiliary vector of integers:
	// First we will set the first function (Gauss).
	// The first function has 7 parameters
	vector<int>		vnSharingGroupsGauss(7);
	vnSharingGroupsGauss[0] = 0;			// y0 (baseline), not shared
	vnSharingGroupsGauss[1] = 0;			// xc for the first Gauss peak, not shared
	vnSharingGroupsGauss[2] = 1;			// w for the first Gauss peak, shared with vnSharingGroupsLorentz[4] (see below) 
	vnSharingGroupsGauss[3] = 2;			// A for the first Gauss peak, shared with vnSharingGroupsLorentz[2] (see below)
	
	vnSharingGroupsGauss[4] = 0;			// xc for the second Gauss peak, not shared
	vnSharingGroupsGauss[5] = 3;			// w for the second Gauss peak, shared with vnSharingGroupsLorentz[1] (see below)
	vnSharingGroupsGauss[6] = 4;			// A for the second Gauss peak, shared with vnSharingGroupsLorentz[5] (see below)
	// Set the Gauss fitting function with the multiplicity of 2 (i.e. one replica).
	int				nn = fit.SetFunction(trFF1, nFitFunctionMultiplicity, -1, vnSharingGroupsGauss, vnSharingGroupsGauss.GetSize());
	if (nn <= 0)
	{
		out_str("Failed setting fitting function!");
		return;
	}
	
	// The second function does not have the baseline parameter y0 (the baseline parameters are those that
	// do not belong to ther replicas unit, both Gauss and Lorentz have exactly one such paramater - y0).
	// (only one baseline makes sense and it is always associated with the first function)
	// so the total number of params for this function is 6.
	// This is the auxiliary vector of integers for specifying the parameter sharing
	// for the Lorentz peaks:
	// The
	vector<int>		vnSharingGroupsLorentz(6);
	vnSharingGroupsLorentz[0] = 0;			// xc for the first Lorentx peak, not shared
	vnSharingGroupsLorentz[1] = 3;			// w for the first Lorentz peak, shared with vnSharingGroupsGauss[5]
	vnSharingGroupsLorentz[2] = 2;			// A for the first Lorentz peak, shared with vnSharingGroupsGauss[3]
	
	vnSharingGroupsLorentz[3] = 0;			// xc for the second Lorentz peak, not shared
	vnSharingGroupsLorentz[4] = 1;			// w for the second Lorentz peak, shared with vnSharingGroupsGauss[2]
	vnSharingGroupsLorentz[5] = 4;			// A for the second Lorentz peak, shared with vnSharingGroupsGauss[6]
	// Append the Lorentz fitting function with the multiplicity of 2 (i.e. one replica).
	nn = fit.SetFunction(trFF2, nFitFunctionMultiplicity, -1, vnSharingGroupsLorentz, vnSharingGroupsLorentz.GetSize());
	if (nn <= 0)
	{
		out_str("Failed setting fitting function!");
		return;
	}
	
	
	///////////////////////////////////////////////////////////////////////////
	// 3. Setting data for fitting ////////////////////////////////////////////
	Worksheet 	wks = Project.ActiveLayer();
	if( !wks )
	{
		out_str("No active worksheet to get input data");
		return;
	}
	
	DataRange 	dr;
	dr.Add(wks, 0, "X");
	dr.Add(wks, 2, "Y"); // Y data from 3th column (Column C) has four 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");
		return;	
        }
	
	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!");
		return;
	}

	
	///////////////////////////////////////////////////////////////////////////
	// 4. Setting paramaters //////////////////////////////////////////////////

	// Total number of fitting parameters is dependent on the parameter sharing between peaks.
	// It is crucial that the number of parameters as specifed
	// in the SetParams() call below agrees with the numbers of parameters in the functions and with the parameter
	// sharing as specifed in step 2.
	//
	// This is how the parameters are lined up:
	// First all the parameters for the first function. Their total number is the same as the total
	// number of parameters for one Gauss function with one replica, which is, as explained above, 7. Note
	// that there is no paramater sharing between paramaters within Gauss function peaks alone, since all
	// the values in the vector vnSharingGroupsGauss are different (with the exception of 0, which, by
	// denition, means that those parameters are not shared).
	// Now we need to add the parameters for Lorentz. The vector vnSharingGroupsLorentz has 6 elements,
	// but 4 of the 6 have nonzero values which have already appeared in vnSharingGroupsGauss (which means they
	// are shared with some paramaters in Gauss), so only 2 more parameters (vnSharingGroupsLorentz[0]
	// and vnSharingGroupsLorentz[3]) are contributed by Lorentz peaks.
	// That amounts to a total of 9.
	int				nNumParams = 9;
	
	vector			vParams(nNumParams);			// this vector should be initialized to the total number of paramaters
	// The vector vParams will be used to supply the initial values of parameters, and it will also receive
	// the parameter values after fitting.
	// The paramaters are lined up in this vector such that the first come all the parameters for the Gauss, and
	// then the additional ones for the Lorentz:
	vParams[0] = 12;			// the baseline y0 (vnSharingGroupsGauss[0])
	vParams[1] = 6.3;		// xc for the first Gauss peak (vnSharingGroupsGauss[1])
	vParams[2] = 0.1;		// w for the first Gauss peak (vnSharingGroupsGauss[2]) and for the second Lorentz peak (vnSharingGroupsLorentz[4]).
	vParams[3] = 85;		// A for the first Gauss peak (vnSharingGroupsGauss[3]) and for the first Lorentz peak (vnSharingGroupsLorentz[2]).
	vParams[4] = 8;		// xc for the second Gauss peak (vnSharingGroupsGauss[4])
	vParams[5] = 0.1;		// w for the second Gauss peak (vnSharingGroupsGauss[5]) and for the first Lorentz peak (vnSharingGroupsLorentz[1]).
	vParams[6] = 40;			// A for the second Gauss peak (vnSharingGroupsGauss[6]) and for the second Lorentz peak (vnSharingGroupsLorentz[5]).
	vParams[7] = 3.8;		// xc for the first Lorentz peak (vnSharingGroupsLorentz[0])
	vParams[8] = 1.2;		// xc for the second Lorentz peak (vnSharingGroupsLorentz[3])
	
	nn = fit.SetParams(nNumParams, vParams);
	if ( 0 != nn )
	{
		out_str("Invalid paramater setting!");
		return;
	}
	
	///////////////////////////////////////////////////////////////////////////
	// 5. Fitting /////////////////////////////////////////////////////////////
	int				nMaxNumIterations = 100;
	nn = fit.Fit(nMaxNumIterations);
	printf("Fit(%ld) returned: %ld\n", nMaxNumIterations, nn);
	if( 0 == nn )
		printf("Fit Done!\n");	
	
	///////////////////////////////////////////////////////////////////////////
	// 6. 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]);
	}
	
	_calc_fitted_data_for_multi_funcs_fit(trFF1, trFF2, vParams, vX, wks);
	
	
	return;
}

static void	_calc_fitted_data_for_multi_funcs_fit(const TreeNode& trFF1, const TreeNode& trFF2, const vector& vParams, const vector& vX, Worksheet& wks)
{
	vector 		vOnePeakParams; 
	vector		vY1, vY2, vY3, vY4; // Y data for each peak
	int			nNumPts = vX.GetSize();
	
	// calculate for first peak
	vOnePeakParams.SetSize(4); // Gauss and Lorentz original both have 4 parameters
	vOnePeakParams[0] = vParams[0];
	vOnePeakParams[1] = vParams[1];
	vOnePeakParams[2] = vParams[2];
	vOnePeakParams[3] = vParams[3];	

	NumericFunction		NF;
	if(!NF.SetTree(trFF1))
	{
		out_str("Invalid function");
		return;
	}
	
	vY1.SetSize(nNumPts);
	BOOL			bb = NF.Evaluate(vOnePeakParams, vY1, vX, NULL, nNumPts);
	if (!bb)
	{
		out_str("Failed!");
		return;
	}
	
	// calculate for second peak
	vOnePeakParams[0] = vParams[0];
	vOnePeakParams[1] = vParams[4];
	vOnePeakParams[2] = vParams[5];
	vOnePeakParams[3] = vParams[6];	
	
	vY2.SetSize(nNumPts);
	bb = NF.Evaluate(vOnePeakParams, vY2, vX, NULL, nNumPts);
	if (!bb)
	{
		out_str("Failed!");
		return;
	}
	
	// calculate for third peak
	vOnePeakParams[0] = vParams[0];
	vOnePeakParams[1] = vParams[7];
	vOnePeakParams[2] = vParams[5];
	vOnePeakParams[3] = vParams[3];	
	
	if(!NF.SetTree(trFF2))
	{
		out_str("Invalid function");
		return;
	}
	
	vY3.SetSize(nNumPts);
	bb = NF.Evaluate(vOnePeakParams, vY3, vX, NULL, nNumPts);
	if (!bb)
	{
		out_str("Failed!");
		return;
	}
	
	// calculate for fourth peak
	vOnePeakParams[0] = vParams[0];
	vOnePeakParams[1] = vParams[8];
	vOnePeakParams[2] = vParams[2];
	vOnePeakParams[3] = vParams[6];	
	
	vY4.SetSize(nNumPts);
	bb = NF.Evaluate(vOnePeakParams, vY4, vX, NULL, nNumPts);
	if (!bb)
	{
		out_str("Failed!");
		return;
	}
	
	vector			vYOut;
	vYOut = vY1 + vY2 + vY3 + vY4 - vParams[0] * 3; // added fitted value for 4 peaks and reduce duplicate y0
	
	// put fitted data into new column
	int	nCol = wks.AddCol();
	Dataset dsOut(wks, nCol);
	dsOut = vYOut;
	printf("Fitted data putted into Column %s\n", wks.Columns(nCol).GetName());
	
	return;	
}