Super Quick Fit  Quick Fit with more options, derived values output and userfunction support
Thu, 04/07/2022  07:48 am
This project supplants the builtin Quick Fit, which is user to quickly fit data displayed in a graph. Super Quick Fit provides all functionality of Quick Fit and adds several convenient features. I tried my best to test everything thoroughly, but sometimes bugs slip through. Bug reports and suggestions for new features are always welcome. I am happy to add more useful settings, peak shapes or derived values if I receive feedback. Currently, 2D fit support may not be perfect since I don't have any meaningful data to test everything. If you have suggestions for improvement of 2D fitting or can test this functionality in detail, then please let me know.
More settings:
 Offset: Force to Zero  The 'offset value' of the fit function (most often the baseline value y0) is held at zero.
 Offset: Force to Minimum  The 'offset value' held at a minimum. The minimum is either determined by the minimum value from placed cursors or the minimum of the data (depending on the fit function this could be either a x or y minimum).
 Fit: Between Cursors  Same as for Quick Fit. The fit range is constrained by the cursors.
 Fit: Within Axis Range  The fit range is constrained to the current top / bottom (and for images left / right) axis scale of the graph.
 Weight from Error Bar Wave  Same as for Quick Fit. The weight wave is taken from the errorbar wave of the trace.
 Clean up Output Waves + Variables  Deletes output waves (other than the 'fit_' wave), strings and variables after the fit. Use this if you are only interested in values printed in the history (and the graph's text box) and otherwise want to keep folders from accumulating W_coef, W_sigma waves etc..
 Result: Plot Full Range  The fit is plotted over the full axis range of the graph.
 Result: Add TextBox  Adds a standard text box with results to the graph
 Textbox Preferences  Same as for Quick Fit. Choose what you would like to have printed into the result textbox.
Settings are saved in the current experiment (as the string root:QuickFitMenu_settings) to keep used settings consistent for each experiment file. But the lastused settings are also persistent and are loaded for each new Igor session, which makes it possible to set up your favorite settings and keep using them.
Additional Fit Functions:
(All functions other than EMG are also usable in the CurveFit dialog)
 FermiEdge_Line  Fermi–Dirac distribution combined with a linear slope.
Equation: f(E) = y0 + (m*E+A) / (exp((EEf)/kT)+1)
 LineX  A line defined with the xintercept (x0) as direct coefficient. Use this function if you want to determine xintercepts with a low uncertainty, especially of lines with steep slope. The normal Line function is instead based on yintercepts and bad at finding xintercepts.
Equation: f(x) = m*(xx0)
 PAD_Beta  Angular distribution function, e.g., for analyzing photoemission experiments.
Equation: f(theta) = Scale*(1+Beta*legendreA(2,0,cos((thetatheta_0)*(Pi/180))))
 DoubleGauss  A combination of two Gaussian peaks to fit doublets etc. This fit will likely not work well if your data range does not contain two clear 'peak' features.
Equation: f(x) = y0 + Area1 * Gauss(x,Loc1,Width1) + Area2 * Gauss(x,Loc2,Width2)
 ExpModGauss  Exponentially Modified Gaussian (EMG) peak shape. A oftenused asymmetric peak shape. Equal to the same function offered in the Multipeak Fit package.
Output of Derived Values:
Additional parameters such as FWHM, area, height etc. (where applicable) are printed in history and the graph's text box. Currently, the following functions output derived parameters: LineX, DoubleGauss, ExpModGauss, Line, Gauss, Lor(entz), and Voigt. You can also set up your own function to output additional derived values after a fit (see below).
Adding Your Own Fit Function:
Adding functions is easy. You only need a normal (such as created with the curve fit dialog) or allatonce fit function and a second function which calculates the initial guesses. Let's look at a simple example, such as the LineX function included in the package:
//CurveFitDialog/ Equation:
//CurveFitDialog/ f(x) = m*(xx0)
//CurveFitDialog/ End of Equation
//CurveFitDialog/ Independent Variables 1
//CurveFitDialog/ x
//CurveFitDialog/ Coefficients 2
//CurveFitDialog/ w[0] = x0
//CurveFitDialog/ w[1] = m
return w[1]*(xw[0])
End
To create initial guesses and at the same time make this function appear in the Super Quick Fit menu, you need to provide a second function which has the name YourFitFuncName+"_prepareCoef", which has a special structure SuperQuickFitStruct as input. In our example the function needs to be called LineX_prepareCoef, like this:
Make/D/O/N=2 W_coef = {0,1}
Wave s.cw = W_coef
return 0
End
The function needs to create a wave with the correct number of coefficients (2 in the example) and initial guesses which are likely make the fit succeed. The created coefficient wave then needs to be fed into the wave assignment s.cw. By the way, you don't have to call this wave W_coef. But if you choose this standard name, the coefficient wave will be properly deleted if you have the 'cleanup' setting active. Now you can select the function from the Super Quick Fit menu.
So far, the initial guesses are simply x0 = 0 and m = 1. This may be too simple for a good guess. You could, for example, access the input data to calculate more reasonable guesses. The full function for LineX looks like this:
Duplicate/free s.data, dif
Differentiate dif/D=dif
WaveStats/Q/R=[s.pntMin,s.pntMax] dif
Variable slope = (WaveMax(s.data)  WaveMin(s.data)) > 0 ? V_max : V_min
Make/D/O/N=2 W_coef = {(s.holdMode == 1 ? 0 : s.xBase),slope}
Wave s.cw = W_coef
return 0
End
Here, the slope m is taken from the derivative of the input data (=> wave s.data). Also, the offset x0 is calculated from the data provided by the fit kernel. Here, s.holdMode is the current offset setting (1 means offsets are forced to zero) and s.xBase is the current x minimum (either from the cursor or the minimum of the input data). This makes sure that the x0 coefficient is properly set to fix the line to the cursor or to zero.
By the way, the 'force offset' setting is by default applied to the first coefficient. If you want to hold other coefficients, you need to provide an appropriate hold string to s.holdStr (e.g., s.holdStr = "00001" fixes the fifth coefficient). The full structure definition looks like this:
Wave cw // coef wave
Wave sw // covariance matrix (M_Covar)
Wave data // data wave (1D or 2D)
Wave xw // x wave
Wave yw // y wave (for images; not supported yet)
int32 pntMin // start point of fit range
int32 pntMax // end point of fit range
int32 pntMinY // start point in y direction (for images)
int32 pntMaxY // end point in y direction (for images)
double xBase // x baseline / start offset
double yBase // y baseline / start offset
int16 holdMode // current hold setting: [0] = no hold, [1] = force o zero, [2] = force to min
int16 plotFull // setting for plot over full range
int16 doTextbox // setting for textbox drawing
String holdStr // hold string for the fit
EndStructure
Adding Your Own Derived Values Function:
Super Quick Fit supports additional data processing after the fit is done, such as calculating derived values from the fit results. For this, create a function named YourFitFuncName+"_derivedVals". In principle, you can do anything in this function, but the main purpose was intended to return a string to print into the history. Let's again look at an example (from LineX):
Variable yCross = s.cw[1]*s.cw[0]
Variable yCross_err = yCross * sqrt( (s.sw[0][0]/s.cw[0]^2) + (s.sw[1][1]/s.cw[1]^2) + 2*s.sw[0][1]/(s.cw[0]*s.cw[1]) )
String printStr = ""
sPrintf printStr, "Derived values:\r\tYintercept\t= %g ± %g\r", yCross, (numtype(yCross_err) != 0 ? 0 : yCross_err)
if (s.doTextbox)
AppendText "\t"+printStr
endif
return printStr
End
This function calculates the yintercept for the line from the fit coefficients (s.cw) and the covariance matrix (s.sw) and then prepares an output string printStr with the result. Here, the result is also added to the default text box, but you can of course omit this step. All available data is again received from the same SuperQuickFitStruct. You can also add code for a followup analysis of the result, saving of the data to disk or whatever you like here. Basically, you can add anything you want to have done after invoking the fit from the menu.
Project Details
Current Project Release
Release File:  Super Quick Fit_v1.03.zip 
Version:  IGOR.8.00.x1.03 
Version Date:  Thu, 07/14/2022  03:00 am 
Version Major:  1 
Version Patch Level:  03 
OS Compatibility:  Windows MacIntel 
Release Notes: 

Forum
Support
Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More
This looks really cool, chozo!
May 11, 2022 at 09:23 am  Permalink
John, thank you for your comment. I am happy that you found this interesting. At first, I just wanted to add in some functions to the Quick Fit menu, but it was not possible to modify the official menu structure. The I started my own submenu, which quickly grew into this full 'replacement'. :) Would be great if some features would find their way into the official Quick Fit at some point. I think not many people will find this package after all.
May 12, 2022 at 12:35 am  Permalink
Looking at the screen shots, and reading some of the description, it is clear you have added userdefine fit functions. You must also have some autoguessing code for those functions. The reason the builtin Quick Fit menu doesn't list any userdefined functions is the lack of a way to provide autoguessing. So I guess that would be a necessary enhancement to make that a possibility.
What does Offset: Force to Minimum do?
You have also organized the fit functions into similar groups, instead of simply listing them in the order that appears in Igor, which is simply the order of an internal table. That includes interpolating some user functions between builtin functions. That is only possible because you know which userdefined fit functions are there, and where they go!
May 12, 2022 at 09:17 am  Permalink
Yes, the userdefined fit functions need an additional function which provides the initial guesses. I have chosen the special name FitFuncName+"_prepareCoef" for this. I could imagine that  should you think about implementing something like this into Quick Fit  as soon as users provide such a specially named function in the right format the fit function will appear in the quickfit menu. That's how I do it here. The code simply scans for functions with this ending and then adds these fits to the menu automatically.
And no, while I have organized the Igorown fit functions into categories, userfunctions all appear at the top of the list and not inbetween official functions. All fit functions after the first break are Igor's fit functions. For this, I have also an internal list of all official functions. But user functions are simply automatically added to the top of the list if a function ending with "_prepareCoef" is present.
Force to Minimum sets and holds the offset coefficient (mostly Y0, or X0 in some cases) to either the minimum of the data (within the fit range) or, if cursors are present, to the minimum cursor value. This can for example be used to fix the Line's 'a' coefficient to the data's first point. The name is a bit ambiguous, because I thought users can choose themselves what is meant by 'offset' and prepare their guessing function accordingly. Also, this is of course problematic when cursors are also used to define the fit range. But it is just a first proposal, and may be refined in the future.
May 12, 2022 at 08:44 pm  Permalink
I was thinking about the possibility of adding a line to the special comments in a fit function that names a guessing function.
You must have developed a standard guess function format?
May 13, 2022 at 09:22 am  Permalink
That's right. The function has the format:
// create coef wave with your guesses here ...
Wave s.cw = coefWave
End
SuperQuickFitStruct provides all kinds of parameters like the current settings, waves and fit range which can be used to find a good guess. The minimal output is to pass a created coef wave with the correct number of points to s.cw at the end.
May 13, 2022 at 05:31 pm  Permalink