def makeRecipe(): """Make a FitRecipe for fitting three double-gaussian curves to data. The separation and amplitude ratio of the double peaks follows a specific relationship. The peaks are broadend according to their position and they sit on top of a background. We are seeking the absolute locations of the peaks as well as their amplitudes. The independent variable is t. The relationship between the double peaks is sin(t2) / l2 = sin(t1) / l1 amplitude(peak2) = r * amplitude(peak1) The values of l1, l2 and r come from experiment. For this example, we use l1 = 1.012, l2 = 1.0 and r = 0.23. """ ## The Profile # Create a Profile to hold the experimental and calculated signal. profile = Profile() x, y, dy = profile.loadtxt("data/threedoublepeaks.dat") # Create the contribution contribution = FitContribution("peaks") contribution.setProfile(profile, xname = "t") pi = numpy.pi exp = numpy.exp # This is a building-block of our profile function def gaussian(t, mu, sig): return 1/(2*pi*sig**2)**0.5 * exp(-0.5 * ((t-mu)/sig)**2) contribution.registerFunction(gaussian, name = "peakshape") def delta(t, mu): """Calculate a delta-function. We don't have perfect precision, so we must make this a very thin Gaussian. """ sig = t[1] - t[0] return gaussian(t, mu, sig) contribution.registerFunction(delta) # Here is another one bkgdstr = "b0 + b1*t + b2*t**2 + b3*t**3 + b4*t**4 + b5*t**5 + b6*t**6" contribution.registerStringFunction(bkgdstr, "bkgd") # Now define our fitting equation. We will hardcode the peak ratios. contribution.setEquation( "A1 * ( convolve( delta(t, mu11), peakshape(t, c, sig11) ) \ + 0.23*convolve( delta(t, mu12), peakshape(t, c, sig12) ) ) + \ A2 * ( convolve( delta(t, mu21), peakshape(t, c, sig21) ) \ + 0.23*convolve( delta(t, mu22), peakshape(t, c, sig22) ) ) + \ A3 * ( convolve( delta(t, mu31), peakshape(t, c, sig31) ) \ + 0.23*convolve( delta(t, mu32), peakshape(t, c, sig32) ) ) + \ bkgd") # c is the center of the gaussian. contribution.c.value = x[len(x)/2] ## The FitRecipe # The FitRecipe lets us define what we want to fit. It is where we can # create variables, constraints and restraints. recipe = FitRecipe() # Here we tell the FitRecipe to use our FitContribution. When the FitRecipe # calculates its residual function, it will call on the FitContribution to # do part of the work. recipe.addContribution(contribution) # Vary the amplitudes for each double peak recipe.addVar(contribution.A1, 100) recipe.addVar(contribution.A2, 100) recipe.addVar(contribution.A3, 100) # Vary the position of the first of the double peaks recipe.addVar(contribution.mu11, 13.0) recipe.addVar(contribution.mu21, 24.0) recipe.addVar(contribution.mu31, 33.0) # Constrain the position of the second double peak from numpy import sin, arcsin def peakloc(mu): """Calculate the location of the second peak given the first.""" l1 = 1.012 l2 = 1.0 return 180 / pi * arcsin( pi / 180 * l2 * sin(mu) / l1 ) recipe.registerFunction(peakloc) recipe.constrain(contribution.mu12, "peakloc(mu11)") recipe.constrain(contribution.mu22, "peakloc(mu21)") recipe.constrain(contribution.mu32, "peakloc(mu31)") # Vary the width of the peaks. We know the functional form of the peak # broadening. sig0 = recipe.newVar("sig0", 0.001) dsig = recipe.newVar("dsig", 4) def sig(sig0, dsig, mu): """Calculate the peak broadening with respect to position.""" return sig0 * (1 - dsig * mu**2); recipe.registerFunction(sig) recipe.fix("mu") # Now constrain the peak widths to this recipe.sig0.value = 0.001 recipe.dsig.value = 4.0 recipe.constrain(contribution.sig11, "sig(sig0, dsig, mu11)") recipe.constrain(contribution.sig12, "sig(sig0, dsig, mu12)", ns = {"mu12" : contribution.mu12} ) recipe.constrain(contribution.sig21, "sig(sig0, dsig, mu21)") recipe.constrain(contribution.sig22, "sig(sig0, dsig, mu22)", ns = {"mu22" : contribution.mu22} ) recipe.constrain(contribution.sig31, "sig(sig0, dsig, mu31)") recipe.constrain(contribution.sig32, "sig(sig0, dsig, mu32)", ns = {"mu32" : contribution.mu32} ) # Also the background recipe.addVar(contribution.b0, 0, tag = "bkgd") recipe.addVar(contribution.b1, 0, tag = "bkgd") recipe.addVar(contribution.b2, 0, tag = "bkgd") recipe.addVar(contribution.b3, 0, tag = "bkgd") recipe.addVar(contribution.b4, 0, tag = "bkgd") recipe.addVar(contribution.b5, 0, tag = "bkgd") recipe.addVar(contribution.b6, 0, tag = "bkgd") return recipe
plt.title('Line fit using the fmin scalar optimizer') # <demo> --- stop --- # For a converged fit recipe, the details of the fit can be extracted # with the FitResults class. from diffpy.srfit.fitbase import FitResults res = FitResults(rec) print(res) # <demo> --- stop --- # Variables defined in the recipe can be fixed to a constant value. rec.fix(B=0) # The fixed variables can be checked using the "fixednames" and # "fixedvalues" attributes of a recipe. print("free:", rec.names, "-->", rec.names) print("fixed:", rec.fixednames, "-->", rec.fixedvalues) # The fit can be rerun with a constant variable B. leastsq(rec.residual, rec.values) print(FitResults(rec)) plt.plot(linedata.x, linedata.y, 'x', linedata.x, linedata.ycalc, '-') plt.title('Line fit for variable B fixed to B=0') # <demo> --- stop --- # Fixed variables may be released with the "free" function.
plt.title('Line fit using the fmin scalar optimizer') # <demo> --- stop --- # For a converged fit recipe, the details of the fit can be extracted # with the FitResults class. from diffpy.srfit.fitbase import FitResults res = FitResults(recipe) print(res) # <demo> --- stop --- # Variables defined in the recipe can be fixed to a constant value. recipe.fix(lgx0=51) # The fixed variables can be checked using the "fixednames" and # "fixedvalues" attributes of a recipe. print("free:", recipe.names, "-->", recipe.names) print("fixed:", recipe.fixednames, "-->", recipe.fixedvalues) # The fit can be rerun with a constant variable B. leastsq(recipe.residual, recipe.values) print(FitResults(recipe)) plt.plot(profile.x, profile.y, 'x', profile.x, profile.ycalc, '-') plt.title('Line fit for variable x0 fixed to x0=51') # <demo> --- stop --- # Fixed variables may be released with the "free" function.
plt.title('Line fit using the fmin scalar optimizer') # <demo> --- stop --- # For a converged fit recipe, the details of the fit can be extracted # with the FitResults class. from diffpy.srfit.fitbase import FitResults res = FitResults(recipe) print(res) # <demo> --- stop --- # Variables defined in the recipe can be fixed to a constant value. recipe.fix(x0=51) # The fixed variables can be checked using the "fixednames" and # "fixedvalues" attributes of a recipe. print("free:", recipe.names, "-->", recipe.names) print("fixed:", recipe.fixednames, "-->", recipe.fixedvalues) # The fit can be rerun with a constant variable B. leastsq(recipe.residual, recipe.values) print(FitResults(recipe)) plt.plot(profile.x, profile.y, 'x', profile.x, profile.ycalc, '-') plt.title('Line fit for variable x0 fixed to x0=51') # <demo> --- stop --- # Fixed variables may be released with the "free" function.