#!/usr/bin/env python
########################################################################
#
# diffpy.srfit by DANSE Diffraction group
# Simon J. L. Billinge
# (c) 2009 The Trustees of Columbia University
# in the City of New York. All rights reserved.
#
# File coded by: Chris Farrow
#
# See AUTHORS.txt for a list of people who contributed.
# See LICENSE_DANSE.txt for license information.
#
########################################################################
"""Example of fitting the Debye recipe to experimental Debye-Waller factors.
This is an extension of example in debyemodel.py. The recipe we create will
simultaneously fit the low and high temperature parts of the experimental data
with the same Debye temperature, but different offsets. This example also
introduces constraints.
Instructions
Run the example and then read through the 'makeRecipeII' code to learn about
multi-profile recipes. You will learn how to constrain related information
between the fit contributions.
Extensions
- Play with the fit ranges and see if you can improve the fit.
- The Debye temperature parameters from the two contributions can be
constrained without creating a new variable. Try to figure out how that is
done.
"""
from diffpy.srfit.fitbase import FitRecipe, FitResults
from debyemodel import makeRecipe, scipyOptimize
####### Example Code
def makeRecipeII():
"""Make a recipe for fitting low and high temperature regions.
We will fit the low and high temperature parts of Debye curve
simultaneously with the same Debye temperature, but different offsets.
We will make two FitRecipes using the makeRecipe function from
debyemodel.py and extract the configured FitContribution from each. We will
use different fitting ranges for each FitContribution and constrain the
Debye temperature in each FitContribution to be the same.
"""
# We'll throw these away. We just want the FitContributions that are
# configured within the recipes.
m1 = makeRecipe()
m2 = makeRecipe()
# These are the FitContributions (we named them "pb" in the debyemodel
# example).
lowT = m1.pb
highT = m2.pb
# Let's rename the FitContributions to something more meaningful for this
# example.
lowT.name = "lowT"
highT.name = "highT"
# Now create a fresh FitRecipe to work with and add to it the two
# FitContributions.
recipe = FitRecipe()
recipe.addContribution(lowT)
recipe.addContribution(highT)
# Change the fit ranges of the Profiles embedded within the
# FitContributions. We want to fit one of the contributions at low
# temperature, and one at high.
lowT.profile.setCalculationRange(0, 150)
highT.profile.setCalculationRange(400, 500)
# Vary the offset from each FitContribution separately, while keeping the
# Debye temperatures the same. We give each offset variable a different
# name in the recipe so it retains its identity.
recipe.addVar(recipe.lowT.offset, name = "lowToffset")
recipe.addVar(recipe.highT.offset, name = "highToffset")
# We create a new Variable and use the recipe's "constrain" method to
# associate the Debye temperature parameters with that variable.
recipe.newVar("thetaD", 100)
recipe.constrain(recipe.lowT.thetaD, "thetaD")
recipe.constrain(recipe.highT.thetaD, "thetaD")
return recipe
def plotResults(recipe):
"""Display the results contained within a refined FitRecipe."""
# The variable values are returned in the order in which the variables were
# added to the FitRecipe.
lowToffset, highToffset, thetaD = recipe.getValues()
# We want to extend the fitting range to its full extent so we can get a
# nice full plot.
recipe.lowT.profile.setCalculationRange(xmin='obs', xmax='obs')
recipe.highT.profile.setCalculationRange(xmin='obs', xmax='obs')
T = recipe.lowT.profile.x
U = recipe.lowT.profile.y
# We can use a FitContribution's 'evaluateEquation' method to evaluate
# expressions involving the Parameters and other aspects of the
# FitContribution. Here we evaluate the fitting equation, which is always
# accessed using the name "eq". We access it this way (rather than through
# the Profile's ycalc attribute) because we changed the calculation range
# above, and we therefore need to recalculate the profile.
lowU = recipe.lowT.evaluateEquation("eq")
highU = recipe.highT.evaluateEquation("eq")
# Now we can plot this.
import pylab
pylab.plot(T,U,'o',label="Pb $U_{iso}$ Data")
lbl1 = r"$T_d$=%3.1f K, lowToff=%1.5f $\AA^2$"% (abs(thetaD),lowToffset)
lbl2 = r"$T_d$=%3.1f K, highToff=%1.5f $\AA^2$"% (abs(thetaD),highToffset)
pylab.plot(T,lowU,label=lbl1)
pylab.plot(T,highU,label=lbl2)
pylab.xlabel("T (K)")
pylab.ylabel(r"$U_{iso} (\AA^2)$")
pylab.legend(loc = (0.0,0.8))
pylab.show()
return
def main():
# Create the recipe
recipe = makeRecipeII()
# Refine using the optimizer of your choice
scipyOptimize(recipe)
# Get the results in a FitResults object.
res = FitResults(recipe)
# Print the results
res.printResults()
# Plot the results
plotResults(recipe)
return
if __name__ == "__main__":
main()
# End of file