Fitting dispersive line shapes in MPF2 using user-defined functions
mmcanally
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
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
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
January 5, 2015 at 03:30 pm - Permalink