Esempio n. 1
0
def makeRecipe(molecule, datname):
    """Create a recipe that uses the DebyePDFGenerator."""

    ## The Profile
    profile = Profile()

    # Load data and add it to the profile
    profile.loadtxt(datname)
    profile.setCalculationRange(xmin=1.2, xmax=8)

    ## The ProfileGenerator
    # Create a DebyePDFGenerator named "G".
    generator = DebyePDFGenerator("G")
    generator.setStructure(molecule)
    # These are metadata needed by the generator
    generator.setQmin(0.68)
    generator.setQmax(22)

    ## The FitContribution
    contribution = FitContribution("bucky")
    contribution.addProfileGenerator(generator)
    contribution.setProfile(profile, xname="r")

    # Make a FitRecipe.
    recipe = FitRecipe()
    recipe.addContribution(contribution)

    # Specify which parameters we want to refine.
    c60 = generator.phase

    # We're not going to refine the ADPs. However, every atom must have a
    # small, but finite ADP for the PDF calculator to work properly.
    atoms = c60.getScatterers()
    for atom in atoms:
        atom.Uiso.value = 0.001

    # Scale factor. We cannot optimize this efficiently, so we take the value
    # from a previous refinement. In general, care must be taken to properly
    # determine the scale of the profile, or to make sure that the residual is
    # not affected by the scale.
    generator.scale.value = 1.24457360e+4

    # Allow every atom to move.  We define the bounds to be a window of radius
    # 0.1 centered on the current value of the position.
    win = 0.1
    for idx, atom in enumerate(atoms):
        xname, yname, zname = getXYZNames(idx)
        recipe.addVar(atom.x, name=xname).boundWindow(win)
        recipe.addVar(atom.y, name=yname).boundWindow(win)
        recipe.addVar(atom.z, name=zname).boundWindow(win)

    return recipe
Esempio n. 2
0
def makeRecipe(molecule, datname):
    """Create a recipe that uses the DebyePDFGenerator."""

    ## The Profile
    profile = Profile()

    # Load data and add it to the profile
    profile.loadtxt(datname)
    profile.setCalculationRange(xmin=1.2, xmax=8)

    ## The ProfileGenerator
    # Create a DebyePDFGenerator named "G".
    generator = DebyePDFGenerator("G")
    generator.setStructure(molecule)
    # These are metadata needed by the generator
    generator.setQmin(0.68)
    generator.setQmax(22)

    ## The FitContribution
    contribution = FitContribution("bucky")
    contribution.addProfileGenerator(generator)
    contribution.setProfile(profile, xname = "r")

    # Make a FitRecipe.
    recipe = FitRecipe()
    recipe.addContribution(contribution)

    # Specify which parameters we want to refine.
    c60 = generator.phase

    # We're not going to refine the ADPs. However, every atom must have a
    # small, but finite ADP for the PDF calculator to work properly.
    atoms = c60.getScatterers()
    for atom in atoms:
        atom.Uiso.value = 0.001

    # Scale factor. We cannot optimize this efficiently, so we take the value
    # from a previous refinement. In general, care must be taken to properly
    # determine the scale of the profile, or to make sure that the residual is
    # not affected by the scale.
    generator.scale.value = 1.24457360e+4

    # Allow every atom to move.  We define the bounds to be a window of radius
    # 0.1 centered on the current value of the position.
    win = 0.1
    for idx, atom in enumerate(atoms):
        xname, yname, zname = getXYZNames(idx)
        recipe.addVar(atom.x, name = xname).boundWindow(win)
        recipe.addVar(atom.y, name = yname).boundWindow(win)
        recipe.addVar(atom.z, name = zname).boundWindow(win)

    return recipe
Esempio n. 3
0
def makeRecipe():
    """Create a recipe that uses the GaussianGenerator.

    This will create a FitContribution that uses the GaussianGenerator,
    associate this with a Profile, and use this to define a FitRecipe.

    """

    ## The Profile
    # Create a Profile to hold the experimental and calculated signal.
    profile = Profile()

    # Load data and add it to the profile. This uses the loadtxt function from
    # numpy.
    profile.loadtxt("data/gaussian.dat")

    ## The ProfileGenerator
    # Create a GaussianGenerator named "g". This will be the name we use to
    # refer to the generator from within the FitContribution equation.  
    generator = GaussianGenerator("g")
    
    ## The FitContribution
    # Create a FitContribution that will associate the Profile with the
    # GaussianGenerator.  The GaussianGenerator will be accessible as an
    # attribute of the FitContribution by its name ("g"). Note that this will
    # set the fitting equation to "g", which calls the GaussianGenerator.
    contribution = FitContribution("g1")
    contribution.addProfileGenerator(generator)
    contribution.setProfile(profile)

    ## The FitRecipe
    # Now we create the FitRecipe and add the FitContribution.
    recipe = FitRecipe()
    recipe.addContribution(contribution)

    # Specify which Parameters we want to vary in the fit.  This will add
    # Variables to the FitRecipe that directly modify the Parameters of the
    # FitContribution.
    # 
    # We create a variable for each Parameter of the GaussianGenerator. Note
    # that the Parameters belong to the GaussianGenerator, not the
    # FitContribution as in gaussianrecipe.py. We initialize parameters as in
    # gaussianrecipe.py so we can expect the same output.
    recipe.addVar(generator.A, 1)
    recipe.addVar(generator.x0, 5)
    recipe.addVar(generator.sigma, name = "sig")
    recipe.sig.value = 1

    # Give the recipe away so it can be used!
    return recipe
Esempio n. 4
0
def makeRecipe():
    """Create a recipe that uses the GaussianGenerator.

    This will create a FitContribution that uses the GaussianGenerator,
    associate this with a Profile, and use this to define a FitRecipe.

    """

    ## The Profile
    # Create a Profile to hold the experimental and calculated signal.
    profile = Profile()

    # Load data and add it to the profile. This uses the loadtxt function from
    # numpy.
    profile.loadtxt("data/gaussian.dat")

    ## The ProfileGenerator
    # Create a GaussianGenerator named "g". This will be the name we use to
    # refer to the generator from within the FitContribution equation.
    generator = GaussianGenerator("g")

    ## The FitContribution
    # Create a FitContribution that will associate the Profile with the
    # GaussianGenerator.  The GaussianGenerator will be accessible as an
    # attribute of the FitContribution by its name ("g"). Note that this will
    # set the fitting equation to "g", which calls the GaussianGenerator.
    contribution = FitContribution("g1")
    contribution.addProfileGenerator(generator)
    contribution.setProfile(profile)

    ## The FitRecipe
    # Now we create the FitRecipe and add the FitContribution.
    recipe = FitRecipe()
    recipe.addContribution(contribution)

    # Specify which Parameters we want to vary in the fit.  This will add
    # Variables to the FitRecipe that directly modify the Parameters of the
    # FitContribution.
    #
    # We create a variable for each Parameter of the GaussianGenerator. Note
    # that the Parameters belong to the GaussianGenerator, not the
    # FitContribution as in gaussianrecipe.py. We initialize parameters as in
    # gaussianrecipe.py so we can expect the same output.
    recipe.addVar(generator.A, 1)
    recipe.addVar(generator.x0, 5)
    recipe.addVar(generator.sigma, name="sig")
    recipe.sig.value = 1

    # Give the recipe away so it can be used!
    return recipe
Esempio n. 5
0
def main():

    p = Profile()
    p.loadtxt("data/gaussian.dat")

    # FitContribution operations
    # "|="  -   Union of necessary components.
    # "<<"  -   Inject a parameter value
    c = FitContribution("g1")
    c |= p
    c |= "A * exp(-0.5*(x-x0)**2/sigma**2)"
    c.A << 0.5
    c.x0 << 5
    c.sigma << 1

    # FitRecipe operations
    # "|="  -   Union of necessary components.
    # "+="  -   Add Parameter or create a new one. Each tuple is a set of
    #           arguments for either setVar or addVar.
    # "*="  -   Constrain a parameter. Think of "*" as a push-pin holding one
    #           parameter's value to that of another.
    # "%="  -   Restrain a parameter or equation. Think of "%" as a rope
    #           loosely tying parameters to a value.
    r = FitRecipe()
    r |= c
    r += (c.A, 0.5), (c.x0, 5), "sig"
    r *= c.sigma, "sig"
    r %= c.A, 0.5, 0.5

    from gaussianrecipe import scipyOptimize

    scipyOptimize(r)

    res = FitResults(r)

    # Print the results.
    res.printResults()

    # Plot the results.
    from gaussianrecipe import plotResults

    plotResults(r)

    return
Esempio n. 6
0
def main():

    p = Profile()
    p.loadtxt("data/gaussian.dat")

    # FitContribution operations
    # "|="  -   Union of necessary components.
    # "<<"  -   Inject a parameter value
    c = FitContribution("g1")
    c |= p
    c |= "A * exp(-0.5*(x-x0)**2/sigma**2)"
    c.A << 0.5
    c.x0 << 5
    c.sigma << 1

    # FitRecipe operations
    # "|="  -   Union of necessary components.
    # "+="  -   Add Parameter or create a new one. Each tuple is a set of
    #           arguments for either setVar or addVar.
    # "*="  -   Constrain a parameter. Think of "*" as a push-pin holding one
    #           parameter's value to that of another.
    # "%="  -   Restrain a parameter or equation. Think of "%" as a rope
    #           loosely tying parameters to a value.
    r = FitRecipe()
    r |= c
    r += (c.A, 0.5), (c.x0, 5), 'sig'
    r *= c.sigma, 'sig'
    r %= c.A, 0.5, 0.5

    from gaussianrecipe import scipyOptimize
    scipyOptimize(r)

    res = FitResults(r)

    # Print the results.
    res.printResults()

    # Plot the results.
    from gaussianrecipe import plotResults
    plotResults(r)

    return
Esempio n. 7
0
def makeRecipe(strufile, datname):
    """Create a recipe that uses the IntensityGenerator.

    This will create a FitContribution that uses the IntensityGenerator,
    associate this with a Profile, and use this to define a FitRecipe.

    """

    ## The Profile
    # Create a Profile. This will hold the experimental and calculated signal.
    profile = Profile()

    # Load data and add it to the profile
    x, y, u = profile.loadtxt(datname)

    ## The ProfileGenerator
    # Create an IntensityGenerator named "I". This will be the name we use to
    # refer to the generator from within the FitContribution equation.  We also
    # need to load the model structure we're using.
    generator = IntensityGenerator("I")
    generator.setStructure(strufile)
    
    ## The FitContribution
    # Create a FitContribution, that will associate the Profile with the
    # ProfileGenerator.  The ProfileGenerator will be accessible as an
    # attribute of the FitContribution by its name ("I").  We also want to tell
    # the FitContribution to name the x-variable of the profile "q", so we can
    # use it in equations with this name.
    contribution = FitContribution("bucky")
    contribution.addProfileGenerator(generator)
    contribution.setProfile(profile, xname = "q")

    # Now we're ready to define the fitting equation for the FitContribution.
    # We need to modify the intensity calculation, and we'll do that from
    # within the fitting equation for the sake of instruction. We want to
    # modify the calculation in three ways.  We want to scale it, add a
    # polynomial background, and broaden the peaks. 
    #
    # There is added benefit for defining these operations outside of the
    # IntensityGenerator. By combining the different parts of the calculation
    # within the fitting equation, the time-consuming iofq calculation is only
    # performed when a structural Parameter is changed. If only non-structural
    # parameters are changed, such as the background and broadening Parameters,
    # then then previously computed iofq value will be used to compute the
    # contribution equation.  The benefit in this is very apparent when
    # refining the recipe with the LM optimizer, which only changes two
    # variables at a time most of the time. Note in the refinement output how
    # many times the residual is calculated, versus how many times iofq is
    # called when using the scipyOptimize function.

    # We will define the background as a string.

    bkgdstr = "b0 + b1*q + b2*q**2 + b3*q**3 + b4*q**4 + b5*q**5 + b6*q**6 +\
               b7*q**7 + b8*q**8 + b9*q**9"

    # This creates a callable equation named "bkgd" within the FitContribution,
    # and turns the polynomial coefficients into Parameters.
    eq = contribution.registerStringFunction(bkgdstr, "bkgd")

    # We will create the broadening function that we need by creating a python
    # function and registering it with the FitContribution.
    pi = numpy.pi
    exp = numpy.exp
    def gaussian(q, q0, width):
        return 1/(2*pi*width**2)**0.5 * exp(-0.5 * ((q-q0)/width)**2)

    # This registers the python function and extracts the name and creates
    # Parameters from the arguments.
    contribution.registerFunction(gaussian)

    # Center the Gaussian so it is not truncated.
    contribution.q0.value = x[len(x)/2]

    # Now we can incorporate the scale and bkgd into our calculation. We also
    # convolve the signal with the Gaussian to broaden it. Recall that we don't
    # need to supply arguments to the registered functions unless we want to
    # make changes to their input values.
    contribution.setEquation("scale * convolve(I, gaussian) + bkgd")

    # Make the FitRecipe and add the FitContribution.
    recipe = FitRecipe()
    recipe.addContribution(contribution)

    # Specify which parameters we want to refine.
    recipe.addVar(contribution.b0, 0)
    recipe.addVar(contribution.b1, 0)
    recipe.addVar(contribution.b2, 0)
    recipe.addVar(contribution.b3, 0)
    recipe.addVar(contribution.b4, 0)
    recipe.addVar(contribution.b5, 0)
    recipe.addVar(contribution.b6, 0)
    recipe.addVar(contribution.b7, 0)
    recipe.addVar(contribution.b8, 0)
    recipe.addVar(contribution.b9, 0)

    # We also want to adjust the scale and the convolution width
    recipe.addVar(contribution.scale, 1)
    recipe.addVar(contribution.width, 0.1)

    # We can also refine structural parameters. Here we extract the
    # DiffpyStructureParSet from the intensity generator and use the parameters
    # like we would any others.
    phase = generator.phase

    # We want to allow for isotropic expansion, so we'll constrain the lattice
    # parameters to the same value (the lattice is cubic). Note that we
    # constrain to the "a" Parameter directly. In previous examples, we
    # constrained to a Variable by name. This has the same effect.
    lattice = phase.getLattice()
    a = lattice.a
    recipe.addVar(a)
    recipe.constrain(lattice.b, a)
    recipe.constrain(lattice.c, a)
    # We want to refine the thermal paramters as well. We will add a new
    # Variable that we call "Uiso" and constrain the atomic Uiso values to
    # this. Note that we don't give Uiso an initial value. The initial value
    # will be inferred from the following constraints.
    Uiso = recipe.newVar("Uiso")
    for atom in phase.getScatterers():
        recipe.constrain(atom.Uiso, Uiso)

    # Give the recipe away so it can be used!
    return recipe
Esempio n. 8
0
def makeRecipe(strufile, datname):
    """Create a recipe that uses the IntensityGenerator.

    This will create a FitContribution that uses the IntensityGenerator,
    associate this with a Profile, and use this to define a FitRecipe.

    """

    ## The Profile
    # Create a Profile. This will hold the experimental and calculated signal.
    profile = Profile()

    # Load data and add it to the profile
    x, y, u = profile.loadtxt(datname)

    ## The ProfileGenerator
    # Create an IntensityGenerator named "I". This will be the name we use to
    # refer to the generator from within the FitContribution equation.  We also
    # need to load the model structure we're using.
    generator = IntensityGenerator("I")
    generator.setStructure(strufile)

    ## The FitContribution
    # Create a FitContribution, that will associate the Profile with the
    # ProfileGenerator.  The ProfileGenerator will be accessible as an
    # attribute of the FitContribution by its name ("I").  We also want to tell
    # the FitContribution to name the x-variable of the profile "q", so we can
    # use it in equations with this name.
    contribution = FitContribution("bucky")
    contribution.addProfileGenerator(generator)
    contribution.setProfile(profile, xname = "q")

    # Now we're ready to define the fitting equation for the FitContribution.
    # We need to modify the intensity calculation, and we'll do that from
    # within the fitting equation for the sake of instruction. We want to
    # modify the calculation in three ways.  We want to scale it, add a
    # polynomial background, and broaden the peaks.
    #
    # There is added benefit for defining these operations outside of the
    # IntensityGenerator. By combining the different parts of the calculation
    # within the fitting equation, the time-consuming iofq calculation is only
    # performed when a structural Parameter is changed. If only non-structural
    # parameters are changed, such as the background and broadening Parameters,
    # then then previously computed iofq value will be used to compute the
    # contribution equation.  The benefit in this is very apparent when
    # refining the recipe with the LM optimizer, which only changes two
    # variables at a time most of the time. Note in the refinement output how
    # many times the residual is calculated, versus how many times iofq is
    # called when using the scipyOptimize function.

    # We will define the background as a string.

    bkgdstr = "b0 + b1*q + b2*q**2 + b3*q**3 + b4*q**4 + b5*q**5 + b6*q**6 +\
               b7*q**7 + b8*q**8 + b9*q**9"

    # This creates a callable equation named "bkgd" within the FitContribution,
    # and turns the polynomial coefficients into Parameters.
    eq = contribution.registerStringFunction(bkgdstr, "bkgd")

    # We will create the broadening function that we need by creating a python
    # function and registering it with the FitContribution.
    pi = numpy.pi
    exp = numpy.exp
    def gaussian(q, q0, width):
        return 1/(2*pi*width**2)**0.5 * exp(-0.5 * ((q-q0)/width)**2)

    # This registers the python function and extracts the name and creates
    # Parameters from the arguments.
    contribution.registerFunction(gaussian)

    # Center the Gaussian so it is not truncated.
    contribution.q0.value = x[len(x)/2]

    # Now we can incorporate the scale and bkgd into our calculation. We also
    # convolve the signal with the Gaussian to broaden it. Recall that we don't
    # need to supply arguments to the registered functions unless we want to
    # make changes to their input values.
    contribution.setEquation("scale * convolve(I, gaussian) + bkgd")

    # Make the FitRecipe and add the FitContribution.
    recipe = FitRecipe()
    recipe.addContribution(contribution)

    # Specify which parameters we want to refine.
    recipe.addVar(contribution.b0, 0)
    recipe.addVar(contribution.b1, 0)
    recipe.addVar(contribution.b2, 0)
    recipe.addVar(contribution.b3, 0)
    recipe.addVar(contribution.b4, 0)
    recipe.addVar(contribution.b5, 0)
    recipe.addVar(contribution.b6, 0)
    recipe.addVar(contribution.b7, 0)
    recipe.addVar(contribution.b8, 0)
    recipe.addVar(contribution.b9, 0)

    # We also want to adjust the scale and the convolution width
    recipe.addVar(contribution.scale, 1)
    recipe.addVar(contribution.width, 0.1)

    # We can also refine structural parameters. Here we extract the
    # DiffpyStructureParSet from the intensity generator and use the parameters
    # like we would any others.
    phase = generator.phase

    # We want to allow for isotropic expansion, so we'll constrain the lattice
    # parameters to the same value (the lattice is cubic). Note that we
    # constrain to the "a" Parameter directly. In previous examples, we
    # constrained to a Variable by name. This has the same effect.
    lattice = phase.getLattice()
    a = lattice.a
    recipe.addVar(a)
    recipe.constrain(lattice.b, a)
    recipe.constrain(lattice.c, a)
    # We want to refine the thermal parameters as well. We will add a new
    # Variable that we call "Uiso" and constrain the atomic Uiso values to
    # this. Note that we don't give Uiso an initial value. The initial value
    # will be inferred from the following constraints.
    Uiso = recipe.newVar("Uiso")
    for atom in phase.getScatterers():
        recipe.constrain(atom.Uiso, Uiso)

    # Give the recipe away so it can be used!
    return recipe
Esempio n. 9
0
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
Esempio n. 10
0
def makeRecipe(strufile, datname1, datname2):
    """Create a recipe that uses the IntensityGenerator.

    We will create two FitContributions that use the IntensityGenerator from
    npintensitygenerator.py and associate each of these with a Profile, and use
    this to define a FitRecipe.

    Both simulated data sets come from the same structure. We're going to make
    two FitContributions that are identical, except for the profile that is
    held in each. We're going to assure that the structures are identical by
    using the same DiffpyStructureParSet (which is generated by the
    IntensityGenerator when we load the structure) in both generators.

    """

    ## The Profiles
    # Create two Profiles for the two FitContributions.
    profile1 = Profile()
    profile2 = Profile()

    # Load data into the Profiles
    profile1.loadtxt(datname1)
    x, y, u = profile2.loadtxt(datname2)

    ## The ProfileGenerators
    # Create two IntensityGenerators named "I". There will not be a name
    # conflict, since the name is only meaningful within the FitContribution
    # that holds the ProfileGenerator.  Load the structure into one and make
    # sure that the second ProfileGenerator is using the same
    # DiffyStructureParSet.  This will assure that both ProfileGenerators are
    # using the exact same Parameters, and underlying Structure object in the
    # calculation of the profile.
    generator1 = IntensityGenerator("I")
    generator1.setStructure(strufile)
    generator2 = IntensityGenerator("I")
    generator2.addParameterSet(generator1.phase)

    ## The FitContributions
    # Create the FitContributions.
    contribution1 = FitContribution("bucky1")
    contribution1.addProfileGenerator(generator1)
    contribution1.setProfile(profile1, xname = "q")
    contribution2 = FitContribution("bucky2")
    contribution2.addProfileGenerator(generator2)
    contribution2.setProfile(profile2, xname = "q")

    # Now we're ready to define the fitting equation for each FitContribution.
    # The functions registered below will be independent, even though they take
    # the same form and use the same Parameter names.  By default, Parameters
    # in different contributions are different Parameters even if they have the
    # same names.  FitContributions are isolated namespaces than only share
    # information if you tell them to by using addParameter or addParameterSet.
    bkgdstr = "b0 + b1*q + b2*q**2 + b3*q**3 + b4*q**4 + b5*q**5 + b6*q**6 +\
               b7*q**7 +b8*q**8 + b9*q**9"

    contribution1.registerStringFunction(bkgdstr, "bkgd")
    contribution2.registerStringFunction(bkgdstr, "bkgd")

    # We will create the broadening function by registering a python function.
    pi = numpy.pi
    exp = numpy.exp
    def gaussian(q, q0, width):
        return 1/(2*pi*width**2)**0.5 * exp(-0.5 * ((q-q0)/width)**2)

    contribution1.registerFunction(gaussian)
    contribution2.registerFunction(gaussian)
    # Center the gaussian
    contribution1.q0.value = x[len(x) // 2]
    contribution2.q0.value = x[len(x) // 2]

    # Now we can incorporate the scale and bkgd into our calculation. We also
    # convolve the signal with the gaussian to broaden it.
    contribution1.setEquation("scale * convolve(I, gaussian) + bkgd")
    contribution2.setEquation("scale * convolve(I, gaussian) + bkgd")

    # Make a FitRecipe and associate the FitContributions.
    recipe = FitRecipe()
    recipe.addContribution(contribution1)
    recipe.addContribution(contribution2)

    # Specify which Parameters we want to refine. We want to refine the
    # background that we just defined in the FitContributions. We have to do
    # this separately for each FitContribution. We tag the variables so it is
    # easy to retrieve the background variables.
    recipe.addVar(contribution1.b0, 0, name = "b1_0", tag = "bcoeffs1")
    recipe.addVar(contribution1.b1, 0, name = "b1_1", tag = "bcoeffs1")
    recipe.addVar(contribution1.b2, 0, name = "b1_2", tag = "bcoeffs1")
    recipe.addVar(contribution1.b3, 0, name = "b1_3", tag = "bcoeffs1")
    recipe.addVar(contribution1.b4, 0, name = "b1_4", tag = "bcoeffs1")
    recipe.addVar(contribution1.b5, 0, name = "b1_5", tag = "bcoeffs1")
    recipe.addVar(contribution1.b6, 0, name = "b1_6", tag = "bcoeffs1")
    recipe.addVar(contribution1.b7, 0, name = "b1_7", tag = "bcoeffs1")
    recipe.addVar(contribution1.b8, 0, name = "b1_8", tag = "bcoeffs1")
    recipe.addVar(contribution1.b9, 0, name = "b1_9", tag = "bcoeffs1")
    recipe.addVar(contribution2.b0, 0, name = "b2_0", tag = "bcoeffs2")
    recipe.addVar(contribution2.b1, 0, name = "b2_1", tag = "bcoeffs2")
    recipe.addVar(contribution2.b2, 0, name = "b2_2", tag = "bcoeffs2")
    recipe.addVar(contribution2.b3, 0, name = "b2_3", tag = "bcoeffs2")
    recipe.addVar(contribution2.b4, 0, name = "b2_4", tag = "bcoeffs2")
    recipe.addVar(contribution2.b5, 0, name = "b2_5", tag = "bcoeffs2")
    recipe.addVar(contribution2.b6, 0, name = "b2_6", tag = "bcoeffs2")
    recipe.addVar(contribution2.b7, 0, name = "b2_7", tag = "bcoeffs2")
    recipe.addVar(contribution2.b8, 0, name = "b2_8", tag = "bcoeffs2")
    recipe.addVar(contribution2.b9, 0, name = "b2_9", tag = "bcoeffs2")

    # We also want to adjust the scale and the convolution width
    recipe.addVar(contribution1.scale, 1, name = "scale1")
    recipe.addVar(contribution1.width, 0.1, name = "width1")
    recipe.addVar(contribution2.scale, 1, name = "scale2")
    recipe.addVar(contribution2.width, 0.1, name = "width2")

    # We can also refine structural parameters. We only have to do this once,
    # since each generator holds the same DiffpyStructureParSet.
    phase = generator1.phase
    lattice = phase.getLattice()
    a = recipe.addVar(lattice.a)
    # We want to allow for isotropic expansion, so we'll make constraints for
    # that.
    recipe.constrain(lattice.b, a)
    recipe.constrain(lattice.c, a)
    # We want to refine the thermal parameters as well. We will add a new
    # variable that we call "Uiso" and constrain the atomic Uiso values to
    # this. Note that we don't give Uiso an initial value. The initial value
    # will be inferred from the subsequent constraints.
    Uiso = recipe.newVar("Uiso")
    for atom in phase.getScatterers():
        recipe.constrain(atom.Uiso, Uiso)

    # Give the recipe away so it can be used!
    return recipe
Esempio n. 11
0
def makeRecipe(molecule, datname):
    """Create a recipe that uses the DebyePDFGenerator."""

    ## The Profile
    profile = Profile()

    # Load data and add it to the profile
    profile.loadtxt(datname)
    profile.setCalculationRange(xmin=1.2, xmax=8)

    ## The ProfileGenerator
    # Create a DebyePDFGenerator named "G".
    generator = DebyePDFGenerator("G")
    generator.setStructure(molecule)
    # These are metadata needed by the generator
    generator.setQmin(0.68)
    generator.setQmax(22)

    ## The FitContribution
    contribution = FitContribution("bucky")
    contribution.addProfileGenerator(generator)
    contribution.setProfile(profile, xname = "r")

    # Make a FitRecipe.
    recipe = FitRecipe()
    recipe.addContribution(contribution)

    # Specify which parameters we want to refine. We'll be using the
    # MoleculeParSet within the generator, so let's get a handle to it. See the
    # diffpy.srfit.structure.objcryststructure module for more information
    # about the MoleculeParSet hierarchy.
    c60 = generator.phase

    # First, the isotropic thermal displacement factor.
    Biso = recipe.newVar("Biso")
    for atom in c60.getScatterers():

        # We have defined a 'center' atom that is a dummy, which means that it
        # has no scattering power. It is only used as a reference point for
        # our bond length. We don't want to constrain it.
        if not atom.isDummy():
            recipe.constrain(atom.Biso, Biso)

    # We need to let the molecule expand. If we were modeling it as a crystal,
    # we could let the unit cell expand. For instruction purposes, we use a
    # Molecule to model C60, and molecules have different modeling options than
    # crystals. To make the molecule expand from a central point, we will
    # constrain the distance from each atom to a dummy center atom that was
    # created with the molecule, and allow that distance to vary. (We could
    # also let the nearest-neighbor bond lengths vary, but that would be much
    # more difficult to set up.)
    center = c60.center
    # Create a new Parameter that represents the radius of the molecule. Note
    # that we don't give it an initial value. Since the variable is being
    # directly constrained to further below, its initial value will be inferred
    # from the constraint.
    radius = recipe.newVar("radius")
    for i, atom in enumerate(c60.getScatterers()):

        if atom.isDummy():
            continue

        # This creates a Parameter that moves the second atom according to the
        # bond length. Note that each Parameter needs a unique name.
        par = c60.addBondLengthParameter("rad%i"%i, center, atom)
        recipe.constrain(par, radius)

    # Add the correlation term, scale. The scale is too short to effectively
    # determine qdamp.
    recipe.addVar(generator.delta2, 2)
    recipe.addVar(generator.scale, 1.3e4)

    # Give the recipe away so it can be used!
    return recipe
Esempio n. 12
0
def makeRecipe():
    """Make a FitRecipe for fitting a Gaussian curve to data.

    The instructions for what we want to refine, and how to refine it will be
    defined within a FitRecipe instance. The job of a FitRecipe is to collect
    and associate all the data, the fitting equations, fitting variables,
    constraints and restraints. The configured recipe provides a 'residual'
    function and the initial variable values that an optimizer can use to
    refine the variables to minimize the disagreement between the calculated
    profile and the data.
    
    Once we define the FitRecipe, we can send it an optimizer to be optimized.
    See the 'scipyOptimize' function.
    
    """
        
    ## The Profile
    # Create a Profile to hold the experimental and calculated signal.
    profile = Profile()

    # Load data and add it to the profile. This uses the loadtxt function from
    # numpy.
    profile.loadtxt("data/gaussian.dat")

    ## The FitContribution
    # The FitContribution associates the Profile with a fitting equation. The
    # FitContribution also stores the parameters of the fitting equation. We
    # give our FitContribution then name "g1". We will be able to access the
    # FitContribution by that name within the FitRecipe.
    contribution = FitContribution("g1")
    # Tell the FitContribution about the Profile. The FitContribution will give
    # us access to the data held within the Profile. Here, we can tell it what
    # name we want to use for the independent variable. We tell it to use the
    # name "x".
    contribution.setProfile(profile, xname="x")

    # Now we need to create a fitting equation. We do that by writing out the
    # equation as a string. The FitContribution will turn this into a callable
    # function internally. In the process, it extracts all the parameters from
    # the equation (A, x, x0, sigma) and turns them into Parameter objects
    # internally. These objects can be accessed as attributes of the
    # contribution by name.  Since we told the contribution that our
    # independent variable is named "x", this value will be substituted into
    # the fitting equation whenever it is called.
    contribution.setEquation("A * exp(-0.5*(x-x0)**2/sigma**2)")

    # To demonstrate how these parameters are used, we will give "A" an initial
    # value. Note that Parameters are not numbers, but are containers for
    # numbers. To get or modify the value of a parameter, use its 'value'
    # attribute.  Parameters also have a 'name' attribute.
    contribution.A.value = 1.0

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

    # Specify which Parameters we want to vary in the fit.  This will add
    # Variables to the FitRecipe that directly modify the Parameters of the
    # FitContribution.
    # 
    # Here we create a Variable for the 'A' Parameter from our fit equation.
    # The resulting Variable will be named 'A' as well, but it will be accessed
    # via the FitRecipe.
    recipe.addVar(contribution.A)
    # Here we create the Variable for 'x0' and give it an initial value of 5.
    recipe.addVar(contribution.x0, 5)
    # Here we create a Variable named 'sig', which is tied to the 'sigma'
    # Parameter of our FitContribution. We give it an initial value through the
    # FitRecipe instance.
    recipe.addVar(contribution.sigma, name = "sig")
    recipe.sig.value = 1

    return recipe
Esempio n. 13
0
def makeRecipe(strufile, datname1, datname2):
    """Create a recipe that uses the IntensityGenerator.

    We will create two FitContributions that use the IntensityGenerator from
    npintensitygenerator.py and associate each of these with a Profile, and use
    this to define a FitRecipe.

    Both simulated data sets come from the same structure. We're going to make
    two FitContributions that are identical, except for the profile that is
    held in each. We're going to assure that the structures are identical by
    using the same DiffpyStructureParSet (which is generated by the
    IntensityGenerator when we load the structure) in both generators.

    """

    ## The Profiles
    # Create two Profiles for the two FitContributions.
    profile1 = Profile()
    profile2 = Profile()

    # Load data into the Profiles
    profile1.loadtxt(datname1)
    x, y, u = profile2.loadtxt(datname2)

    ## The ProfileGenerators
    # Create two IntensityGenerators named "I". There will not be a name
    # conflict, since the name is only meaningful within the FitContribution
    # that holds the ProfileGenerator.  Load the structure into one and make
    # sure that the second ProfileGenerator is using the same
    # DiffyStructureParSet.  This will assure that both ProfileGenerators are
    # using the exact same Parameters, and underlying Structure object in the
    # calculation of the profile.
    generator1 = IntensityGenerator("I")
    generator1.setStructure(strufile)
    generator2 = IntensityGenerator("I")
    generator2.addParameterSet(generator1.phase)

    ## The FitContributions
    # Create the FitContributions.
    contribution1 = FitContribution("bucky1")
    contribution1.addProfileGenerator(generator1)
    contribution1.setProfile(profile1, xname="q")
    contribution2 = FitContribution("bucky2")
    contribution2.addProfileGenerator(generator2)
    contribution2.setProfile(profile2, xname="q")

    # Now we're ready to define the fitting equation for each FitContribution.
    # The functions registered below will be independent, even though they take
    # the same form and use the same Parameter names.  By default, Parameters
    # in different contributions are different Parameters even if they have the
    # same names.  FitContributions are isolated namespaces than only share
    # information if you tell them to by using addParameter or addParameterSet.
    bkgdstr = "b0 + b1*q + b2*q**2 + b3*q**3 + b4*q**4 + b5*q**5 + b6*q**6 +\
               b7*q**7 +b8*q**8 + b9*q**9"

    contribution1.registerStringFunction(bkgdstr, "bkgd")
    contribution2.registerStringFunction(bkgdstr, "bkgd")

    # We will create the broadening function by registering a python function.
    pi = numpy.pi
    exp = numpy.exp

    def gaussian(q, q0, width):
        return 1 / (2 * pi * width**2)**0.5 * exp(-0.5 * ((q - q0) / width)**2)

    contribution1.registerFunction(gaussian)
    contribution2.registerFunction(gaussian)
    # Center the gaussian
    contribution1.q0.value = x[len(x) / 2]
    contribution2.q0.value = x[len(x) / 2]

    # Now we can incorporate the scale and bkgd into our calculation. We also
    # convolve the signal with the gaussian to broaden it.
    contribution1.setEquation("scale * convolve(I, gaussian) + bkgd")
    contribution2.setEquation("scale * convolve(I, gaussian) + bkgd")

    # Make a FitRecipe and associate the FitContributions.
    recipe = FitRecipe()
    recipe.addContribution(contribution1)
    recipe.addContribution(contribution2)

    # Specify which Parameters we want to refine. We want to refine the
    # background that we just defined in the FitContributions. We have to do
    # this separately for each FitContribution. We tag the variables so it is
    # easy to retrieve the background variables.
    recipe.addVar(contribution1.b0, 0, name="b1_0", tag="bcoeffs1")
    recipe.addVar(contribution1.b1, 0, name="b1_1", tag="bcoeffs1")
    recipe.addVar(contribution1.b2, 0, name="b1_2", tag="bcoeffs1")
    recipe.addVar(contribution1.b3, 0, name="b1_3", tag="bcoeffs1")
    recipe.addVar(contribution1.b4, 0, name="b1_4", tag="bcoeffs1")
    recipe.addVar(contribution1.b5, 0, name="b1_5", tag="bcoeffs1")
    recipe.addVar(contribution1.b6, 0, name="b1_6", tag="bcoeffs1")
    recipe.addVar(contribution1.b7, 0, name="b1_7", tag="bcoeffs1")
    recipe.addVar(contribution1.b8, 0, name="b1_8", tag="bcoeffs1")
    recipe.addVar(contribution1.b9, 0, name="b1_9", tag="bcoeffs1")
    recipe.addVar(contribution2.b0, 0, name="b2_0", tag="bcoeffs2")
    recipe.addVar(contribution2.b1, 0, name="b2_1", tag="bcoeffs2")
    recipe.addVar(contribution2.b2, 0, name="b2_2", tag="bcoeffs2")
    recipe.addVar(contribution2.b3, 0, name="b2_3", tag="bcoeffs2")
    recipe.addVar(contribution2.b4, 0, name="b2_4", tag="bcoeffs2")
    recipe.addVar(contribution2.b5, 0, name="b2_5", tag="bcoeffs2")
    recipe.addVar(contribution2.b6, 0, name="b2_6", tag="bcoeffs2")
    recipe.addVar(contribution2.b7, 0, name="b2_7", tag="bcoeffs2")
    recipe.addVar(contribution2.b8, 0, name="b2_8", tag="bcoeffs2")
    recipe.addVar(contribution2.b9, 0, name="b2_9", tag="bcoeffs2")

    # We also want to adjust the scale and the convolution width
    recipe.addVar(contribution1.scale, 1, name="scale1")
    recipe.addVar(contribution1.width, 0.1, name="width1")
    recipe.addVar(contribution2.scale, 1, name="scale2")
    recipe.addVar(contribution2.width, 0.1, name="width2")

    # We can also refine structural parameters. We only have to do this once,
    # since each generator holds the same DiffpyStructureParSet.
    phase = generator1.phase
    lattice = phase.getLattice()
    a = recipe.addVar(lattice.a)
    # We want to allow for isotropic expansion, so we'll make constraints for
    # that.
    recipe.constrain(lattice.b, a)
    recipe.constrain(lattice.c, a)
    # We want to refine the thermal parameters as well. We will add a new
    # variable that we call "Uiso" and constrain the atomic Uiso values to
    # this. Note that we don't give Uiso an initial value. The initial value
    # will be inferred from the subsequent constraints.
    Uiso = recipe.newVar("Uiso")
    for atom in phase.getScatterers():
        recipe.constrain(atom.Uiso, Uiso)

    # Give the recipe away so it can be used!
    return recipe
Esempio n. 14
0
def makeRecipe():
    """Make a FitRecipe for fitting a Gaussian curve to data.

    The instructions for what we want to refine, and how to refine it will be
    defined within a FitRecipe instance. The job of a FitRecipe is to collect
    and associate all the data, the fitting equations, fitting variables,
    constraints and restraints. The configured recipe provides a 'residual'
    function and the initial variable values that an optimizer can use to
    refine the variables to minimize the disagreement between the calculated
    profile and the data.

    Once we define the FitRecipe, we can send it an optimizer to be optimized.
    See the 'scipyOptimize' function.

    """

    ## The Profile
    # Create a Profile to hold the experimental and calculated signal.
    profile = Profile()

    # Load data and add it to the profile. This uses the loadtxt function from
    # numpy.
    profile.loadtxt("data/gaussian.dat")

    ## The FitContribution
    # The FitContribution associates the Profile with a fitting equation. The
    # FitContribution also stores the parameters of the fitting equation. We
    # give our FitContribution then name "g1". We will be able to access the
    # FitContribution by that name within the FitRecipe.
    contribution = FitContribution("g1")
    # Tell the FitContribution about the Profile. The FitContribution will give
    # us access to the data held within the Profile. Here, we can tell it what
    # name we want to use for the independent variable. We tell it to use the
    # name "x".
    contribution.setProfile(profile, xname="x")

    # Now we need to create a fitting equation. We do that by writing out the
    # equation as a string. The FitContribution will turn this into a callable
    # function internally. In the process, it extracts all the parameters from
    # the equation (A, x, x0, sigma) and turns them into Parameter objects
    # internally. These objects can be accessed as attributes of the
    # contribution by name.  Since we told the contribution that our
    # independent variable is named "x", this value will be substituted into
    # the fitting equation whenever it is called.
    contribution.setEquation("A * exp(-0.5*(x-x0)**2/sigma**2)")

    # To demonstrate how these parameters are used, we will give "A" an initial
    # value. Note that Parameters are not numbers, but are containers for
    # numbers. To get or modify the value of a parameter, use its 'value'
    # attribute.  Parameters also have a 'name' attribute.
    contribution.A.value = 1.0

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

    # Specify which Parameters we want to vary in the fit.  This will add
    # Variables to the FitRecipe that directly modify the Parameters of the
    # FitContribution.
    #
    # Here we create a Variable for the 'A' Parameter from our fit equation.
    # The resulting Variable will be named 'A' as well, but it will be accessed
    # via the FitRecipe.
    recipe.addVar(contribution.A)
    # Here we create the Variable for 'x0' and give it an initial value of 5.
    recipe.addVar(contribution.x0, 5)
    # Here we create a Variable named 'sig', which is tied to the 'sigma'
    # Parameter of our FitContribution. We give it an initial value through the
    # FitRecipe instance.
    recipe.addVar(contribution.sigma, name="sig")
    recipe.sig.value = 1

    return recipe