Fitting dispersive line shapes in MPF2 using user-defined functions

Hi all,

Relatively new to using Igor Pro and I've run into an issue in fitting some of my spectral data.

My spectral data typically consists of between 3 and 12 dispersive line shapes (specifically Fano) that I'd like to fit.

Using the basic curve fitting tool I can enter in expressions that have variables to fit up to five Fano peaks, however I've got some spectra that have N>5. The limitation in using the basic curve fitting tool comes from having too long of an expression for more than five peaks. This seemed like a great place to use MPF2 since it also spits out residuals and has a nice UI. So I programmed what I thought were the appropriate functions for a Fano line shape and tried testing out (repeatedly) on a sample set of two peaks. Unfortunately, I can never get the procedure to move forward with any fits that look remotely close to fitting the data. Side-by-side in the .pxp is an example of a satisfactory fit using the brute force Curve Fitting dialog and a two peak Fano fitting function.

Any advice would be greatly appreciated.

Thanks, and happy holidays/New Year!

Mike

My Fano inputs for basic curve fitting and MPF2:

//Dialog from the manual/simple curve fitting commands under Analysis->Curve Fitting for a two peak Fano profile
Function FanoFit2(w,x) : FitFunc
	Wave w
	Variable x

	//CurveFitDialog/ These comments were created by the Curve Fitting dialog. Altering them will
	//CurveFitDialog/ make the function less convenient to work with in the Curve Fitting dialog.
	//CurveFitDialog/ Equation:
	//CurveFitDialog/ f(x) = (A1*(((q+((x-z1)/(g1/2)))^2)/(1+((x-z1)/(g1/2))^2)))+(A2*(((q+((x-z2)/(g2/2)))^2)/(1+((x-z2)/(g2/2))^2)))+m*x+c*x^2+d 
	//CurveFitDialog/ End of Equation
	//CurveFitDialog/ Independent Variables 1
	//CurveFitDialog/ x
	//CurveFitDialog/ Coefficients 10
	//CurveFitDialog/ w[0] = q
	//CurveFitDialog/ w[1] = m
	//CurveFitDialog/ w[2] = c
	//CurveFitDialog/ w[3] = d
	//CurveFitDialog/ w[4] = A1
	//CurveFitDialog/ w[5] = g1
	//CurveFitDialog/ w[6] = z1
	//CurveFitDialog/ w[7] = A2
	//CurveFitDialog/ w[8] = g2
	//CurveFitDialog/ w[9] = z2

	return (w[4]*(((w[0]+((x-w[6])/(w[5]/2)))^2)/(1+((x-w[6])/(w[5]/2))^2)))+(w[7]*(((w[0]+((x-w[9])/(w[8]/2)))^2)/(1+((x-w[9])/(w[8]/2))^2)))+w[1]*x+w[2]*x^2+w[3] 
End

//(12/27/14) Attempt to add Fano peak fitting functionality to MPF2 package of Igor Pro template taken from manual and YonatanHo IgorExchange forum post 
// ********* FANO *********
Function/S Fano_PeakFuncInfo(InfoDesired)
	Variable InfoDesired
 
	String info=""
 
	Switch (InfoDEsired)
		case PeakFuncInfo_ParamNames:
			info = "Location;Gamma;Amplitude;q" //Location=peak center, Amplitude=peak amplitude, Gamma=Gamma parameter of the Fano line shape, q=Fano asymmetry parameter   
			break;							//
		case PeakFuncInfo_PeakFName:        // BUT you must maintain their order of apperance in the parameter w[] wave!
			info = "Fano_Peak"
			break;
		case PeakFuncInfo_GaussConvFName:
			info = "SimpleLorentzianGuess"
			break;
		case PeakFuncInfo_ParameterFunc:
			info = "Fano_PeakParams"
			break;
		case PeakFuncInfo_DerivedParamNames:
			info = "Location;Gamma;Amplitude;q" //
			break;
		default:
			break;
	endSwitch
 
	return info
end
 
Function SimpleLorentzianGuess(w) // Let's assume that a simple Lorentzian is a good guess to start with... (Derivative of Lorentzian might be better but how to implement?)
	Wave w
	//Note that the wave is already pre-filled with parameters from the automatic or "by-hand" guess from the side of MPF
	//assuming a possibly asymmetric Gaussian.
	//Parameters w[0],w[1],w[2], are corresponding to position, width, and height respectively,
 
	Redimension/N=4w
	// w[0] = Position==>Location				
	// w[1] = Width==>Gamma				
	// w[2] = Height==>Amplitude
	// w[3] = Constant ==> Fano asymmetry parameter (Note: no relation to parameters of Lorentzian/Gaussian guess)
 
	// width of Lorenzian needs to be modified from the estimated Gaussian width (Remember: MPF2 uses Gaussians for auto initial guess)
	w[2] = w[1]*w[2]*sqrt(pi)
 
	return 0
end
 
 
 
Function Fano_Peak(w, yw, xw)  // The w wave is a 4 object wave that has the fit parameters. Use w[0] to w[3], i.e.;
									 // w[0] = Location
									 // w[1] = Gamma
								       // w[2] = Amplitude
									 // w[3] = Fano asymmetry parameter
	
	Wave w, yw, xw
 
	yw = w[2]*(((w[3]+((x-w[0])/(w[1]/2)))^2)/(1+((x-w[0])/(w[1]/2))^2)) //Taken from Nano Lett 2012 
end
 
Function Fano_PeakParams(cw, sw, outWave) //cw is the wave parameter from the fit, 
	Wave cw, sw, outWave						 //sw is the standard deviations for the parameters - both are 12 wave parameters as defined in "PeakFuncInfo_DerivedParamNames"
 
 
	//Location (Central position of dispersive lineshape)- 
	outWave[0][0] = cw[0]			// This is actually the peak center of a Lorentian. <- Probably the big issue right now in failing. Could a derivative of Lorentzian be used for initial guess instead?
	outWave[0][1] = sqrt(sw[0][0])  // The error is treated accordingly.
 
	//Amplitude (Take a guess)
	outWave[2][0] = cw[2]
	outWave[2][1] =  NaN
 
	//Gamma (linewidth)
	outWave[1][0] =  cw[1]
	outWave[2][1] = NaN
 
	//q (Fano asymmetry parameter)
	outWave[3][0] = cw[3]
	outWave[3][1] = NaN
End
FanoFittingIGOR3.pxp (144.54 KB)
I answered this via a tech support incident. Here is what I said:

The basic problem with your attempt was using "x" in the fit function equation instead of "xw". I also added an arbitrary initialization of the q factor, simply setting it to -0.7 which seems to be about appropriate judging from the regular fit you included in the experiment file.

The function to turn the auto-locate info into a good initial guess will probably never work well, as auto-locate expects peaks to look sort of like a Gaussian, that is, it doesn't expect the "derivative of a peak" shape that has a peak followed by a valley.

I see that in your simple fit function there is only one value of q. There isn't a good way to achieve that in MPF2. I managed it for your two-peak fit by entering this in the inter-peak constraint box:

P0K3>P1K3;P0K3<P1K3;


That makes the q for peak 0 both greater and less than the q for peak 1, a tricky way to get an equality constraint without holding the coefficients. You would need such a list having similar expressions for every pair between peak 0 and all the other peaks.

I have attached a modification of the experiment file that has my code changes (small but important!) and my fit that looks a lot like your simple curve fit in Graph0.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com
FanoFittingIGOR3_JW.pxp (134.41 KB)