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
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