Returning V_FitError from graph fitting inside 'Execute'
euaruksakul
Execute FuncFit_String
. I can add FuncFit_String+=";print V_FitError"
at the end to print the result out during the loop but adding ";MyVariable=V_FitError"
doesn't work.. Is there any solution to this? Thank you very much.
Execute
method?MyVariable = V_FitError
will not work in an execute statement essentially because the commands run in a separate environment from the main loop and are unaware of local variables in the main loop.You could use a
try ... catch ... end try
approach inside your fit function, create a global that records the status, and read that global after the execute statements runs.But, to return to the top question, the best approach is to avoid the
Execute
method. Are you aware of proto-type functions?--
J. J. Weimer
Chemistry / Chemical & Materials Engineering, UAH
April 21, 2018 at 09:41 am - Permalink
Execute cmd
NVAR V_FitError // Create reference to global variable created by FuncFit
Variable MyVariable = V_FitError
If you can do what you need to do without using Execute, that is a better solution. If the fitType parameter to CurveFit or FuncFit has to be computed at runtime, then that is a legitimate reason to use Execute.
April 21, 2018 at 11:56 am - Permalink
Execute FitFunc ...
is new to me.--
J. J. Weimer
Chemistry / Chemical & Materials Engineering, UAH
April 21, 2018 at 02:04 pm - Permalink
Perhaps related to what jjweimer was getting at when asking about prototype functions, the fit type parameter can also be dealt with in a user function with
FUNCREF
i.e., I think it's the case that any user-defined fit function could be called this way
WAVE wv,coefwave
String fitNameSTr
FUNCREF fits_protoFunc f = $fitNameStr
FuncFit f, coefwave,wv
end
April 22, 2018 at 08:27 am - Permalink
The reason to use 'Execute FuncFit_String' is that users can select arbitrary number of peaks in multi-peak fit GUI that I wrote (to extract peak parameters in spectromicroscopy volume with x-y for image pixel coordinate and z for XPS spectrum). I use a loop and FuncFit_String+=",{func_n, coef_n, keyword =value }" ... to construct the fitting command, but still face the 400 characters limit of Execute so the code cannot really handle too many peaks.
I have never learned about proto-type function before. I just tried to read the Help in Igor but still don't understand how it works. Will this proto-type function help in my situation?
Thank you very much again!
April 21, 2018 at 09:38 pm - Permalink
In Igor7 the limit for the command line, and therefore for Execute is, if memory serves, 1000 bytes, and in Igor8, now in beta testing, it is 2500 bytes.
I think there may be a way to do your fitting without using Execute, even in Igor6. Go to the reference help for the FuncFit operation and then search for "Fitting Sums of Fit Functions". If you are attempting to fit a variable number of peaks you will need to use the {string =fitSpecStr} format.
April 21, 2018 at 11:17 pm - Permalink
April 22, 2018 at 03:33 am - Permalink
Execute
to allow the fit function name to vary at run time. I agree with hrodstein that it's not helpful in this situation. I wasn't aware of the {fitFuncSpec } option, which looks perfect for both your case and for allowing the fit function to vary at run time. I had mistakenly thought you might in part be usingApril 22, 2018 at 04:59 pm - Permalink
April 22, 2018 at 09:14 pm - Permalink
April 23, 2018 at 12:34 am - Permalink
April 23, 2018 at 02:53 am - Permalink
displayhelptopic "All-At-Once Fitting Functions"
Explicit multi-threading might also be helpful to compute the individual peaks inside the fitfunction.
HJ
April 23, 2018 at 04:40 am - Permalink
In the meantime, the approach that @Olelytken suggests is one that I used many years ago to fit XPS multiplets prior to structure fit functions. I built the coefficient wave and used a
for ... endfor
loop to add up peaks.--
J. J. Weimer
Chemistry / Chemical & Materials Engineering, UAH
April 23, 2018 at 06:13 am - Permalink
Using an all-at-once fit function as HJDrescher suggests is a very good idea since it will allow you to add the different peak components together with FastOP, which is extremely fast. It also means you don't have to go through the for loop for each data point, but only once for the whole wave.
You can improve the speed of your fit by orders of magnitudes by playing around with exactly how you code your calculations. Be warned! You can spend an enormous amount of time on that. For the XPS spectra I typically work with (~100 data points, 2-4 peaks) I can fit 100-1000 spectra per second on my very old office computer, that includes fitting individual Shirley backgrounds for each peak and letting all parameters free. By fixing parameters, especially the position and FWHM, I can fit much faster, and I could still significantly improve the speed of my code if I really wanted to, but it is a lot of work.
If you could average all spectra from your entire image and fit the averaged spectrum to get peak positions, FWHM and all the other parameters, you could then fit each individual spectra by only allowing the area of each peak to change. That could be extremely fast.
You can find my XPS procedure here: http://www.igorexchange.com/project/EccentricXPS, but because I have played around with improving the speed of the fit it is not easy to look at the code and follow what happens within the fit function. Below, I added a simplified version with no asymmetry of my fit function for a single peak. You will find it below the easier-to-follow functions. Here I also added the execution time of the individual bits and you can see that erf(x) is the slowest step at 61 microseconds.
// Returns a Gaussian peak normalized to an area of one
Variable x, Position, FWHM
// f(x) = sqrt( 4 * ln(2) / pi ) / FWHM * exp( -4 * ln(2) * (( x - Position ) / FWHM )^2 )
// sqrt( 4 ln(2) / pi ) = 0.939437
// 4*ln(2) = 2.77259
Return 0.939437/FWHM*exp(-2.77259*((x-Position)/FWHM)^2)
end
ThreadSafe Static Function XPSFitAssGaussShirley(x, Position, FWHM)
// Calculates the Shirley background (integrated area) of a Gaussian peak with an area of one
Variable x, Position, FWHM
// f(x) = 0.5 * ( Erf(sqrt( 4 * ln(2) ) * ( x - Position ) / FWHM ) + 1 )
// sqrt( 4 ln(2) ) = 1.66511
Return 0.5*(Erf(1.66511*(x-Position)/FWHM)+1)
end
ThreadSafe Static Function XPSFitAssLorentz(x, Position, FWHM)
// Returns a Lorentzian peak normalised to an area of one
Variable x, Position, FWHM
// 2 / pi * FWHM / (4 * ( x - Position )^2 + FWHM^2 )
// 2/pi = 0.63662
Return 0.63662*FWHM/(4*(x-Position)^2+FWHM^2)
end
ThreadSafe Static Function XPSFitAssLorentzShirley(x, Position, FWHM)
// Calculates the Shirley background (integrated area) of a Lorentzian peak with an area of one
Variable x, Position, FWHM
// 1 / pi * atan( 2 * ( x - Position ) / FWHM ) + 0.5
// 1/pi = 0.31831
Return 0.31831*atan(2*(x-Position)/FWHM)+0.5
end
ThreadSafe Static Function XPSFitAssFitFuncAAOOnePeak(Position, PeakArea, FWHM, GL, ShirleyCoef, xw, R)
// Calculates a single peak for the all-at-once fit function. Since this function optimized for speed the math is almost impossible to follow.
Variable Position, PeakArea, FWHM, GL, ShirleyCoef
Wave/D xw, R
Variable c1=0, c2=0, Const=0
Duplicate/O/FREE/D R, A, A2, B, C // 8 us
// The simple calculation for a symmetric pseudo-Voigt peak with the corresponding Shirley background. This will also be the starting point for the asymmetric peak
c1=2/FWHM
c2=c1*Position
FastOP A=(c1)*xw-(c2) // 0.8 us
A2=A^2 // 15 us
Const=0.5*PeakArea*ShirleyCoef
// Doesn't calculate the Gaussian component, if the peak is pure Lorentzian
if (GL!=1)
// Calculates the Gaussian component of the peak
FastOP B=(-0.693148)*A2 // 0.6 us
B=exp(B) // 12 us
c2=(1-GL)*PeakArea
c1=0.939437*c2/FWHM
// Doesn't calculate the Shirley background if the Shirley coefficient is zero
if (ShirleyCoef!=0)
// Calculates the Shirley background for the Gaussian component
FastOP C=(0.832555)*A // 0.5 us
C=erf(C) // 61 us
c2=c2*0.5*ShirleyCoef
// Adds the peak and background together
FastOP R=(c1)*B+(c2)*C // 1.5 us
else
// Only adds the peak and not the background
FastOP R=(c1)*B
endif
// Doesn't calculate the Lorentzian component, if the peak is pure Gaussian
if (GL!=0)
// Calculates the Lorentzian component of the peak
FastOP B=A2+(1) // 0.7 us
B=1/B // 8.5 us
c2=PeakArea*GL
c1=c2*0.63662/FWHM
// Adds the peak to the result wave
FastOP R=R+(c1)*B // 1.4 us
// Doesn't calculate the Shirley background if the Shirley coefficient is zero
if (ShirleyCoef!=0)
// Calculates the Shirley background for the Lorentzian component
C=atan(A) // 16 us
c2=c2*0.31831*ShirleyCoef
// Adds the background to the result wave
FastOP R=R+(c2)*C // 1.3 us
endif
endif
else
// Calculates the Lorentzian component of the peak
FastOP B=A2+(1)
B=1/B
c1=PeakArea*0.63662/FWHM
// Doesn't calculate the Shirley background if the Shirley coefficient is zero
if (ShirleyCoef!=0)
// Calculates the Shirley background for the Lorentzian component
C=atan(A)
c2=PeakArea*0.31831*ShirleyCoef
// Adds the peak and background together
FastOP R=(c1)*B+(c2)*C
else
// Only adds the peak and not the background
FastOP R=(c1)*B
endif
endif
// Returns the constant offset, which should be added to the wave
Return Const
end
April 23, 2018 at 07:56 am - Permalink
olelytken - It is really amazing to see your very nice work in trying to minimize the processing time. I already learned many new Igor tricks from looking at your example. The idea to integrate all spectrum and fit for initial values seems really nice. We are using PEEM and there is still iso-chromaticity issue (peak at each different pixel is shifted by a different amount) but once I find out how to cope with that I the code should run really faster.
April 23, 2018 at 10:23 pm - Permalink