Example #1
0
def make_generator(
        config: GenConfig) -> Union[PDFGenerator, DebyePDFGenerator]:
    """
    Build a generator according to the information in the GenConfig.

    Parameters
    ----------
    config : GenConfig
        A configuration instance for generator building.

    Returns
    -------
    generator: PDFGenerator or DebyePDFGenerator
        A generator built from GenConfig.

    """
    name = config.name
    stru: Structure = loadStructure(config.stru_file)
    ncpu = config.ncpu

    if config.debye:
        generator = DebyePDFGenerator(name)
    else:
        generator = PDFGenerator(name)

    generator.setStructure(stru, periodic=config.periodic)

    if ncpu:
        pool = multiprocessing.Pool(ncpu)
        generator.parallel(ncpu, mapfunc=pool.imap_unordered)
    else:
        pass

    return generator
Example #2
0
def makeRecipe(ciffile, grdata):
    """Make a recipe to model a crystal-like nanoparticle PDF."""

    # Set up a PDF fit as has been done in other examples.
    pdfprofile = Profile()

    pdfparser = PDFParser()
    pdfparser.parseFile(grdata)
    pdfprofile.loadParsedData(pdfparser)
    pdfprofile.setCalculationRange(xmin = 0.1, xmax = 20)

    pdfcontribution = FitContribution("pdf")
    pdfcontribution.setProfile(pdfprofile, xname = "r")

    pdfgenerator = PDFGenerator("G")
    pdfgenerator.setQmax(30.0)
    stru = CreateCrystalFromCIF(file(ciffile))
    pdfgenerator.setStructure(stru)
    pdfcontribution.addProfileGenerator(pdfgenerator)

    # Register the nanoparticle shape factor.
    from diffpy.srfit.pdf.characteristicfunctions import sphericalCF
    pdfcontribution.registerFunction(sphericalCF, name = "f")

    # Now we set up the fitting equation.
    pdfcontribution.setEquation("f * G")

    # Now make the recipe. Make sure we fit the characteristic function shape
    # parameters, in this case 'psize', which is the diameter of the particle.
    recipe = FitRecipe()
    recipe.addContribution(pdfcontribution)

    phase = pdfgenerator.phase
    for par in phase.sgpars:
        recipe.addVar(par)

    recipe.addVar(pdfcontribution.psize, 20)
    recipe.addVar(pdfgenerator.scale, 1)
    recipe.addVar(pdfgenerator.delta2, 0)
    recipe.B11_0 = 0.1

    return recipe
Example #3
0
def make_generator(genconfig: GenConfig) -> Union[PDFGenerator, DebyePDFGenerator]:
    """
    Build a generator according to the information in the GenConfig.

    Parameters
    ----------
    genconfig : GenConfig
        A configuration instance for generator building.

    Returns
    -------
    generator: PDFGenerator or DebyePDFGenerator
        A generator built from GenConfig.
    """
    generator = DebyePDFGenerator(genconfig.name) if genconfig.debye else PDFGenerator(genconfig.name)
    generator.setStructure(genconfig.structure, periodic=genconfig.structure)
    ncpu = genconfig.ncpu
    if ncpu:
        pool = multiprocessing.Pool(ncpu)
        generator.parallel(ncpu, mapfunc=pool.imap_unordered)
    return generator
Example #4
0
def makeRecipe(ciffile, grdata):
    """Make a recipe to model a crystal-like nanoparticle PDF."""

    # Set up a PDF fit as has been done in other examples.
    pdfprofile = Profile()

    pdfparser = PDFParser()
    pdfparser.parseFile(grdata)
    pdfprofile.loadParsedData(pdfparser)
    pdfprofile.setCalculationRange(xmin=0.1, xmax=20)

    pdfcontribution = FitContribution("pdf")
    pdfcontribution.setProfile(pdfprofile, xname="r")

    pdfgenerator = PDFGenerator("G")
    pdfgenerator.setQmax(30.0)
    stru = CreateCrystalFromCIF(file(ciffile))
    pdfgenerator.setStructure(stru)
    pdfcontribution.addProfileGenerator(pdfgenerator)

    # Register the nanoparticle shape factor.
    from diffpy.srfit.pdf.characteristicfunctions import sphericalCF
    pdfcontribution.registerFunction(sphericalCF, name="f")

    # Now we set up the fitting equation.
    pdfcontribution.setEquation("f * G")

    # Now make the recipe. Make sure we fit the characteristic function shape
    # parameters, in this case 'psize', which is the diameter of the particle.
    recipe = FitRecipe()
    recipe.addContribution(pdfcontribution)

    phase = pdfgenerator.phase
    for par in phase.sgpars:
        recipe.addVar(par)

    recipe.addVar(pdfcontribution.psize, 20)
    recipe.addVar(pdfgenerator.scale, 1)
    recipe.addVar(pdfgenerator.delta2, 0)
    recipe.B11_0 = 0.1

    return recipe
Example #5
0
# prepare profile object with experimental data
profile = Profile()
parser = PDFParser()
parser.parseFile(dataFile)
profile.loadParsedData(parser)

# define range for pdf calculation
rmin = 0.01
rmax = 20
rstep = 0.01

# setup calculation range for the PDF simulation
profile.setCalculationRange(xmin=rmin, xmax=rmax, dx=rstep)

# prepare nucpdf function that simulates the nuclear PDF
nucpdf = PDFGenerator("nucpdf")
nucpdf.setStructure(mno)
nucpdf.setProfile(profile)

### DO AN INITIAL FIT TO THE STRUCTURAL PDF
totpdf = FitContribution('totpdf')
totpdf.addProfileGenerator(nucpdf)
totpdf.setProfile(profile)
totpdf.setEquation("nucscale*nucpdf")

# The FitRecipe does the work of calculating the PDF with the fit variables
# that we give it.
mnofit = FitRecipe()

# give the FitContribution to the FitRecipe
mnofit.addContribution(totpdf)
def makeRecipe(ciffile, datname):
    """Create a fitting recipe for crystalline PDF data."""

    ## The Profile
    # This will be used to store the observed and calculated PDF profile.
    profile = Profile()

    # Load data and add it to the Profile. As before we use a PDFParser. The
    # metadata is still passed to the PDFGenerator later on. The interaction
    # between the PDFGenerator and the metadata does not depend on type of
    # structure being refined.
    parser = PDFParser()
    parser.parseFile(datname)
    profile.loadParsedData(parser)
    profile.setCalculationRange(xmax = 20)

    ## The ProfileGenerator
    # This time we use the CreateCrystalFromCIF method of pyobjcryst.crystal to
    # create a Crystal object. That object is passed to the PDFGenerator as in
    # the previous example.
    generator = PDFGenerator("G")
    stru = CreateCrystalFromCIF(file(ciffile))
    generator.setStructure(stru)
    generator.setQmax(40.0)
    
    ## The FitContribution
    contribution = FitContribution("nickel")
    contribution.addProfileGenerator(generator)
    contribution.setProfile(profile, xname = "r")

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

    ## Configure the fit variables

    # As before, we get a handle to the structure parameter set. In this case,
    # it is a ObjCrystCrystalParSet instance that was created when we called
    # 'setStructure' above. The ObjCrystCrystalParSet has different Parameters
    # and options than the DiffpyStructureParSet used in the last example. See
    # its documentation in diffpy.srfit.structure.objcrystparset.
    phase = generator.phase

    # Here is where we created space group constraints in the previous example.
    # The difference in this example is that the ObjCrystCrystalParSet is aware
    # of space groups, and the DiffpyStructureParSet is not. Constraints are
    # created internally when "sgpars" attribute is called for. These
    # constriants get enforced within the ObjCrystCrystalParSet. Free
    # Parameters are stored within the 'sgpars' member of the
    # ObjCrystCrystalParSet, which is the same as the object returned from
    # 'constrainAsSpaceGroup'.
    #
    # As before, we have one free lattice parameter ('a'). We can simplify
    # things by iterating through all the sgpars.
    for par in phase.sgpars: 
        recipe.addVar(par)
    # set the initial thermal factor to a non-zero value
    assert hasattr(recipe, 'B11_0')
    recipe.B11_0 = 0.1

    # We now select non-structural parameters to refine.
    # This controls the scaling of the PDF.
    recipe.addVar(generator.scale, 1)
    # This is a peak-damping resolution term.
    recipe.addVar(generator.qdamp, 0.01)
    # This is a vibrational correlation term that sharpens peaks at low-r.
    recipe.addVar(generator.delta2, 5)

    # Give the recipe away so it can be used!
    return recipe
def makeRecipe(ciffile_ni, ciffile_si, xdata_ni, ndata_ni, xdata_si,
        xdata_sini):
    """Create a fitting recipe for crystalline PDF data."""

    ## The Profiles
    # We need a profile for each data set.
    xprofile_ni = makeProfile(xdata_ni)
    xprofile_si = makeProfile(xdata_si)
    nprofile_ni = makeProfile(ndata_ni)
    xprofile_sini = makeProfile(xdata_sini)

    ## The ProfileGenerators
    # We create one for each phase and share the phases.
    xgenerator_ni = PDFGenerator("xG_ni")
    stru = CreateCrystalFromCIF(file(ciffile_ni))
    xgenerator_ni.setStructure(stru)
    phase_ni = xgenerator_ni.phase

    xgenerator_si = PDFGenerator("xG_si")
    stru = CreateCrystalFromCIF(file(ciffile_si))
    xgenerator_si.setStructure(stru)
    phase_si = xgenerator_si.phase

    ngenerator_ni = PDFGenerator("nG_ni")
    ngenerator_ni.setPhase(phase_ni)

    xgenerator_sini_ni = PDFGenerator("xG_sini_ni")
    xgenerator_sini_ni.setPhase(phase_ni)

    xgenerator_sini_si = PDFGenerator("xG_sini_si")
    xgenerator_sini_si.setPhase(phase_si)

    ## The FitContributions
    # We one of these for each data set.
    xcontribution_ni = makeContribution("xnickel", xgenerator_ni, xprofile_ni)
    xcontribution_si = makeContribution("xsilicon", xgenerator_si, xprofile_si)
    ncontribution_ni = makeContribution("nnickel", ngenerator_ni, nprofile_ni)
    xcontribution_sini = makeContribution("xsini", xgenerator_sini_ni,
            xprofile_sini)
    xcontribution_sini.addProfileGenerator(xgenerator_sini_si)
    xcontribution_sini.setEquation("scale * (xG_sini_ni +  xG_sini_si)")

    # As explained in another example, we want to minimize using Rw^2.
    xcontribution_ni.setResidualEquation("resv")
    xcontribution_si.setResidualEquation("resv")
    ncontribution_ni.setResidualEquation("resv")
    xcontribution_sini.setResidualEquation("resv")

    # Make the FitRecipe and add the FitContributions.
    recipe = FitRecipe()
    recipe.addContribution(xcontribution_ni)
    recipe.addContribution(xcontribution_si)
    recipe.addContribution(ncontribution_ni)
    recipe.addContribution(xcontribution_sini)

    # Now we vary and constrain Parameters as before.
    for par in phase_ni.sgpars:
        recipe.addVar(par, name = par.name + "_ni")
    delta2_ni = recipe.newVar("delta2_ni", 2.5)
    recipe.constrain(xgenerator_ni.delta2, delta2_ni)
    recipe.constrain(ngenerator_ni.delta2, delta2_ni)
    recipe.constrain(xgenerator_sini_ni.delta2, delta2_ni)

    for par in phase_si.sgpars:
        recipe.addVar(par, name = par.name + "_si")
    delta2_si = recipe.newVar("delta2_si", 2.5)
    recipe.constrain(xgenerator_si.delta2, delta2_si)
    recipe.constrain(xgenerator_sini_si.delta2, delta2_si)

    # Now the experimental parameters
    recipe.addVar(xgenerator_ni.scale, name = "xscale_ni")
    recipe.addVar(xgenerator_si.scale, name = "xscale_si")
    recipe.addVar(ngenerator_ni.scale, name = "nscale_ni")
    recipe.addVar(xcontribution_sini.scale, 1.0, "xscale_sini")
    recipe.newVar("pscale_sini_ni", 0.8)
    recipe.constrain(xgenerator_sini_ni.scale, "pscale_sini_ni")
    recipe.constrain(xgenerator_sini_si.scale, "1 - pscale_sini_ni")

    # The qdamp parameters are too correlated to vary so we fix them based on
    # previous measurments.
    xgenerator_ni.qdamp.value = 0.055
    xgenerator_si.qdamp.value = 0.051
    ngenerator_ni.qdamp.value = 0.030
    xgenerator_sini_ni.qdamp.value = 0.052
    xgenerator_sini_si.qdamp.value = 0.052

    # Give the recipe away so it can be used!
    return recipe
Example #8
0
def makeRecipe(stru1, stru2, datname):
    """Create a fitting recipe for crystalline PDF data."""

    ## The Profile
    profile = Profile()

    # Load data and add it to the profile
    parser = PDFParser()
    parser.parseFile(datname)
    profile.loadParsedData(parser)
    profile.setCalculationRange(xmin=1.5, xmax = 45, dx = 0.1)

    ## The ProfileGenerator
    # In order to fit the core and shell phases simultaneously, we must use two
    # PDFGenerators.
    #
    # The generator for the CdS core. We call it "G_CdS" and will use this name
    # later when we set the fitting equation in the FitContribution.
    generator_cds = PDFGenerator("G_CdS")
    generator_cds.setStructure(stru1)
    generator_cds.setQmax(26)
    generator_cds.qdamp.value = 0.0396
    # The generator for the ZnS shell. We call it "G_ZnS".
    generator_zns = PDFGenerator("G_ZnS")
    generator_zns.setStructure(stru2)
    generator_zns.setQmax(26)
    generator_zns.qdamp.value = 0.0396

    ## The FitContribution
    # Add both generators and the profile to the FitContribution.
    contribution = FitContribution("cdszns")
    contribution.addProfileGenerator(generator_cds)
    contribution.addProfileGenerator(generator_zns)
    contribution.setProfile(profile, xname = "r")

    # Set up the characteristic functions. We use a spherical CF for the core
    # and a spherical shell CF for the shell. Since this is set up as two
    # phases, we implicitly assume that the core-shell correlations contribute
    # very little to the PDF.
    from diffpy.srfit.pdf.characteristicfunctions import sphericalCF, shellCF
    contribution.registerFunction(sphericalCF, name = "f_CdS")
    contribution.registerFunction(shellCF, name = "f_ZnS")

    # Write the fitting equation. We want to sum the PDFs from each phase and
    # multiply it by a scaling factor.
    contribution.setEquation("scale * (f_CdS * G_CdS +  f_ZnS * G_ZnS)")

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

    # Vary the inner radius and thickness of the shell. Constrain the core
    # diameter to twice the shell radius.
    recipe.addVar(contribution.radius, 15)
    recipe.addVar(contribution.thickness, 11)
    recipe.constrain(contribution.psize, "2 * radius")

    ## Configure the fit variables
    # Start by configuring the scale factor and resolution factors.
    # We want the sum of the phase scale factors to be 1.
    recipe.newVar("scale_CdS", 0.7)
    recipe.constrain(generator_cds.scale, "scale_CdS")
    recipe.constrain(generator_zns.scale, "1 - scale_CdS")
    # We also want the resolution factor to be the same on each.

    # Vary the gloabal scale as well.
    recipe.addVar(contribution.scale, 0.3)

    # Now we can configure the structural parameters. We tag the different
    # structural variables so we can easily turn them on and off in the
    # subsequent refinement.
    phase_cds = generator_cds.phase
    for par in phase_cds.sgpars.latpars:
        recipe.addVar(par, name = par.name + "_cds", tag = "lat")
    for par in phase_cds.sgpars.adppars:
        recipe.addVar(par, 1, name = par.name + "_cds", tag = "adp")
    recipe.addVar(phase_cds.sgpars.xyzpars.z_1, name = "z_1_cds", tag = "xyz")
    # Since we know these have stacking disorder, constrain the B33 adps for
    # each atom type.
    recipe.constrain("B33_1_cds", "B33_0_cds")
    recipe.addVar(generator_cds.delta2, name = "delta2_cds", value = 5)

    phase_zns = generator_zns.phase
    for par in phase_zns.sgpars.latpars:
        recipe.addVar(par, name = par.name + "_zns", tag = "lat")
    for par in phase_zns.sgpars.adppars:
        recipe.addVar(par, 1, name = par.name + "_zns", tag = "adp")
    recipe.addVar(phase_zns.sgpars.xyzpars.z_1, name = "z_1_zns", tag = "xyz")
    recipe.constrain("B33_1_zns", "B33_0_zns")
    recipe.addVar(generator_zns.delta2, name = "delta2_zns", value = 2.5)

    # Give the recipe away so it can be used!
    return recipe
def makeRecipe(ciffile, xdatname, ndatname):
    """Create a fitting recipe for crystalline PDF data."""

    ## The Profiles
    # We need a profile for each data set. This means that we will need two
    # FitContributions as well.
    xprofile = Profile()
    nprofile = Profile()

    # Load data and add it to the proper Profile.
    parser = PDFParser()
    parser.parseFile(xdatname)
    xprofile.loadParsedData(parser)
    xprofile.setCalculationRange(xmax = 20)

    parser = PDFParser()
    parser.parseFile(ndatname)
    nprofile.loadParsedData(parser)
    nprofile.setCalculationRange(xmax = 20)

    ## The ProfileGenerators
    # We need one of these for the x-ray data.
    xgenerator = PDFGenerator("G")
    stru = CreateCrystalFromCIF(file(ciffile))
    xgenerator.setStructure(stru)

    # And we need one for the neutron data. We want to refine the same
    # structure object in each PDFGenerator. This would suggest that we add the
    # same Crystal to each. However, if we do that then we will have two
    # Parameters for each Crystal data member (two Parameters for the "a"
    # lattice parameter, etc.), held in different ObjCrystCrystalParSets, each
    # managed by its own PDFGenerator. Thus, changes made to the Crystal
    # through one PDFGenerator will not be known to the other PDFGenerator
    # since their ObjCrystCrystalParSets don't know about each other. The
    # solution is to share ObjCrystCrystalParSets rather than Crystals. This
    # way there is only one Parameter for each Crystal data member. (An
    # alternative to this is to constrain each structure Parameter to be varied
    # to the same variable. The present approach is easier and less error
    # prone.)
    #
    # Tell the neutron PDFGenerator to use the phase from the x-ray
    # PDFGenerator.
    ngenerator = PDFGenerator("G")
    ngenerator.setPhase(xgenerator.phase)

    ## The FitContributions
    # We associate the x-ray PDFGenerator and Profile in one FitContribution...
    xcontribution = FitContribution("xnickel")
    xcontribution.addProfileGenerator(xgenerator)
    xcontribution.setProfile(xprofile, xname = "r")
    # and the neutron objects in another.
    ncontribution = FitContribution("nnickel")
    ncontribution.addProfileGenerator(ngenerator)
    ncontribution.setProfile(nprofile, xname = "r")

    # This example is different than the previous ones in that we are composing
    # a residual function from other residuals (one for the x-ray contribution
    # and one for the neutron contribution). The relative magnitude of these
    # residuals effectively determines the influence of each contribution over
    # the fit. This is a problem in this case because the x-ray data has
    # uncertainty values associated with it (on the order of 1e-4), and the
    # chi^2 residual is proportional to 1 / uncertainty**2. The neutron has no
    # uncertainty, so it's chi^2 is proportional to 1. Thus, my optimizing
    # chi^2 we would give the neutron data practically no weight in the fit. To
    # get around this, we will optimize a different metric.
    #
    # The contribution's residual can be either chi^2, Rw^2, or custom crafted.
    # In this case, we should minimize Rw^2 of each contribution so that each
    # one can contribute roughly equally to the fit.
    xcontribution.setResidualEquation("resv")
    ncontribution.setResidualEquation("resv")

    # Make the FitRecipe and add the FitContributions.
    recipe = FitRecipe()
    recipe.addContribution(xcontribution)
    recipe.addContribution(ncontribution)

    # Now we vary and constrain Parameters as before.
    recipe.addVar(xgenerator.scale, 1, "xscale")
    recipe.addVar(ngenerator.scale, 1, "nscale")
    recipe.addVar(xgenerator.qdamp, 0.01, "xqdamp")
    recipe.addVar(ngenerator.qdamp, 0.01, "nqdamp")
    # delta2 is a non-structual material propery. Thus, we constrain together
    # delta2 Parameter from each PDFGenerator.
    delta2 = recipe.newVar("delta2", 2)
    recipe.constrain(xgenerator.delta2, delta2)
    recipe.constrain(ngenerator.delta2, delta2)

    # We only need to constrain phase properties once since there is a single
    # ObjCrystCrystalParSet for the Crystal.
    phase = xgenerator.phase
    for par in phase.sgpars:
        recipe.addVar(par)
    recipe.B11_0 = 0.1

    # Give the recipe away so it can be used!
    return recipe
Example #10
0
    def testGenerator(self):
        qmax = 27.0
        gen = PDFGenerator()
        gen.setScatteringType('N')
        self.assertEqual('N', gen.getScatteringType())
        gen.setQmax(qmax)
        self.assertAlmostEqual(qmax, gen.getQmax())
        from diffpy.Structure import PDFFitStructure
        stru = PDFFitStructure()
        ciffile = datafile("ni.cif")
        stru.read(ciffile)
        for i in range(4):
            stru[i].Bisoequiv = 1
        gen.setStructure(stru)

        calc = gen._calc
        # Test parameters
        for par in gen.iterPars(recurse = False):
            pname = par.name
            defval = calc._getDoubleAttr(pname)
            self.assertEquals(defval, par.getValue())
            # Test setting values
            par.setValue(1.0)
            self.assertEquals(1.0, par.getValue())
            par.setValue(defval)
            self.assertEquals(defval, par.getValue())

        r = numpy.arange(0, 10, 0.1)
        y = gen(r)

        # Now create a reference PDF. Since the calculator is testing its
        # output, we just have to make sure we can calculate from the
        # PDFGenerator interface.
        from diffpy.srreal.pdfcalculator import PDFCalculator
        calc = PDFCalculator()
        calc.rstep = r[1] - r[0]
        calc.rmin = r[0]
        calc.rmax = r[-1] + 0.5 * calc.rstep
        calc.qmax = qmax
        calc.setScatteringFactorTableByType('N')
        calc.eval(stru)
        yref = calc.pdf

        diff = y - yref
        res = numpy.dot(diff, diff)
        self.assertAlmostEquals(0, res)
        return
def makeRecipe(niciffile, siciffile, datname):
    """Create a fitting recipe for crystalline PDF data."""

    ## The Profile
    profile = Profile()

    # Load data and add it to the profile
    parser = PDFParser()
    parser.parseFile(datname)
    profile.loadParsedData(parser)
    profile.setCalculationRange(xmax = 20)

    ## The ProfileGenerator
    # In order to fit two phases simultaneously, we must use two PDFGenerators.
    # PDFGenerator is designed to take care of as little information as it
    # must. (Don't do too much, and do it well.) A PDFGenerator can generate
    # the signal from only a single phase at a time. So, we will create one
    # PDFGenerator for each phase and compose them within the same
    # FitContribution. Note that both generators will be associated with the
    # same Profile within the FitContribution, so they will both be
    # automatically configured according to the metadata.
    #
    # The generator for the nickel phase. We call it "G_ni" and will use this
    # name later when we set the fitting equation in the FitContribution.
    generator_ni = PDFGenerator("G_ni")
    stru = CreateCrystalFromCIF(file(niciffile))
    generator_ni.setStructure(stru)
    # The generator for the silicon phase. We call it "G_si".
    generator_si = PDFGenerator("G_si")
    stru = CreateCrystalFromCIF(file(siciffile))
    generator_si.setStructure(stru)

    ## The FitContribution
    # Add both generators to the FitContribution. Add the Profile. This will
    # send the metadata to the generators.
    contribution = FitContribution("nisi")
    contribution.addProfileGenerator(generator_ni)
    contribution.addProfileGenerator(generator_si)
    contribution.setProfile(profile, xname = "r")

    # Write the fitting equation. We want to sum the PDFs from each phase and
    # multiply it by a scaling factor. We also want a certain phase scaling
    # relationship between the PDFs which we will enforce with constraints in
    # the FitRecipe.
    contribution.setEquation("scale * (G_ni +  G_si)")

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

    ## Configure the fit variables
    # Start by configuring the scale factor and resolution factors.
    # We want the sum of the phase scale factors to be 1.
    recipe.newVar("scale_ni", 0.1)
    recipe.constrain(generator_ni.scale, "scale_ni")
    recipe.constrain(generator_si.scale, "1 - scale_ni")
    # We also want the resolution factor to be the same on each.
    recipe.newVar("qdamp", 0.03)
    recipe.constrain(generator_ni.qdamp, "qdamp")
    recipe.constrain(generator_si.qdamp, "qdamp")

    # Vary the gloabal scale as well.
    recipe.addVar(contribution.scale, 1)

    # Now we can configure the structural parameters. Since we're using
    # ObjCrystCrystalParSets, the space group constraints are automatically
    # applied to each phase. We must selectively vary the free parameters.
    #
    # First the nickel parameters
    phase_ni = generator_ni.phase
    for par in phase_ni.sgpars:
        recipe.addVar(par, name = par.name + "_ni")
    recipe.addVar(generator_ni.delta2, name = "delta2_ni")
    # Next the silicon parameters
    phase_si = generator_si.phase
    for par in phase_si.sgpars:
        recipe.addVar(par, name = par.name + "_si")
    recipe.addVar(generator_si.delta2, name = "delta2_si")

    # We have prior information from the earlier examples so we'll use it here
    # in the form of restraints.
    #
    # The nickel lattice parameter was measured to be 3.527. The uncertainty
    # values are invalid for that measurement, since the data from which it is
    # derived has no uncertainty. Thus, we will tell the recipe to scale the
    # residual, which means that it will be weighted as much as the average
    # data point during the fit.
    recipe.restrain("a_ni", lb = 3.527, ub = 3.527, scaled = True)
    # Now we do the same with the delta2 and Biso parameters (remember that
    # Biso = 8*pi**2*Uiso)
    recipe.restrain("delta2_ni", lb = 2.22, ub = 2.22, scaled = True)
    recipe.restrain("Biso_0_ni", lb = 0.454, ub = 0.454, scaled = True)
    #
    # We can do the same with the silicon values. We haven't done a thorough
    # job of measuring the uncertainties in the results, so we'll scale these
    # as well.
    recipe.restrain("a_si", lb = 5.430, ub = 5.430, scaled = True)
    recipe.restrain("delta2_si", lb = 3.54, ub = 3.54, scaled = True)
    recipe.restrain("Biso_0_si", lb = 0.645, ub = 0.645, scaled = True)

    # Give the recipe away so it can be used!
    return recipe
Example #12
0
def makeRecipe(stru1, stru2, stru3, datname):
    """Create a fitting recipe for crystalline PDF data."""

    ## The Profile
    profile = Profile()

    # Load data and add it to the profile
    parser = PDFParser()
    parser.parseFile(datname)
    profile.loadParsedData(parser)
    profile.setCalculationRange(xmin=1, xmax = 40, dx = 0.01)

    ## The ProfileGenerator
    generator_MEF_Cryst_B = PDFGenerator("G_MEF_Cryst_B")
    generator_MEF_Cryst_B.setStructure(stru1, periodic = True)
    generator_MEF_Mole_B = DebyePDFGenerator("G_MEF_Mole_B")
    generator_MEF_Mole_B.setStructure(stru2, periodic = False)
    generator_MEF_Intra = DebyePDFGenerator("G_MEF_Intra")
    generator_MEF_Intra.setStructure(stru3, periodic = False)

    ## The FitContribution
    # Add both generators to the FitContribution. Add the Profile. This will
    # send the metadata to the generators.
    contribution = FitContribution("MEF")
    contribution.addProfileGenerator(generator_MEF_Cryst_B)
    contribution.addProfileGenerator(generator_MEF_Mole_B)
    contribution.addProfileGenerator(generator_MEF_Intra)
    contribution.setProfile(profile, xname = "r")
    #write down the fit equation:
    #(G_MEF_Cryst_B - G_MEF_Mole_B) gives the intermolecular PDF, using a larger atomic displacement parameter
    #G_MEF_Intra gives intramolecular PDF, using a smaller atomic displacement parameter.
    #The sum of both parts gives the total PDF.
    contribution.setEquation("scale * (G_MEF_Cryst_B - G_MEF_Mole_B + G_MEF_Intra)")

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

    qdamp = 0.02902
    generator_MEF_Cryst_B.qdamp.value = qdamp
    generator_MEF_Mole_B.qdamp.value = qdamp
    generator_MEF_Intra.qdamp.value = qdamp

    # Vary the gloabal scale as well.
    recipe.addVar(contribution.scale, 1) 

#############################################################################################
############### First the MEF_Cryst_B parameters ############################################
#############################################################################################
    phase_MEF_Cryst_B = generator_MEF_Cryst_B.phase

    lat = phase_MEF_Cryst_B.getLattice()
    atoms = phase_MEF_Cryst_B.getScatterers()
    
    recipe.newVar("Uiso_Inter", 0.05, tag = "T1")
    recipe.newVar("lat_a", 14.556, tag = "lat")
    recipe.newVar("lat_b", 6.811, tag = "lat")
    recipe.newVar("lat_c", 7.657, tag = "lat")
    recipe.newVar("alpha", 119.57, tag = "lat")
    recipe.newVar("beta", 103.93, tag = "lat")
    recipe.newVar("gamma", 91.30, tag = "lat")

    recipe.constrain(lat.a, "lat_a")
    recipe.constrain(lat.b, "lat_b")
    recipe.constrain(lat.c, "lat_c")
    recipe.constrain(lat.alpha, "alpha")
    recipe.constrain(lat.beta, "beta")
    recipe.constrain(lat.gamma, "gamma")
    
    for atom in atoms:
    	if atom.element.title() == "N":
             recipe.constrain(atom.Uiso, "Uiso_Inter")
             
    	elif atom.element.title() == "O":
             recipe.constrain(atom.Uiso, "Uiso_Inter")

    	elif atom.element.title() == "C":
             recipe.constrain(atom.Uiso, "Uiso_Inter")

    	elif atom.element.title() == "H":
             recipe.constrain(atom.Uiso, "Uiso_Inter")

    generator_MEF_Cryst_B.delta2.value = 0


#############################################################################################
############### Second the MEF_Mole_B parameters ############################################
#############################################################################################
    phase_MEF_Mole_B = generator_MEF_Mole_B.phase
    generator_MEF_Mole_B.setQmin(0.0)
    generator_MEF_Mole_B.setQmax(24.0)
    recipe.newVar("zoom_Mole_B", 1, tag = "lat2") 

    lat = phase_MEF_Mole_B.getLattice()
    recipe.constrain(lat.a, "zoom_Mole_B")
    recipe.constrain(lat.b, "zoom_Mole_B")
    recipe.constrain(lat.c, "zoom_Mole_B")
   # Constrain fractional xyz parameters
    atoms = phase_MEF_Mole_B.getScatterers()
   # Constrain ADPs

    for atom in atoms:
    	if atom.element.title() == "C":
             recipe.constrain(atom.Uiso, "Uiso_Inter")

    	elif atom.element.title() == "O":
             recipe.constrain(atom.Uiso, "Uiso_Inter")

    	elif atom.element.title() == "N":
             recipe.constrain(atom.Uiso, "Uiso_Inter")

    	elif atom.element.title() == "H":
             recipe.constrain(atom.Uiso, "Uiso_Inter")

    generator_MEF_Mole_B.delta2.value = 0

#############################################################################################
############### Third the intra molecule parameters##########################################
#############################################################################################
    phase_MEF_Intra = generator_MEF_Intra.phase
    generator_MEF_Intra.setQmin(0.0)
    generator_MEF_Intra.setQmax(24.0)
    recipe.newVar("zoom_Intra", 1, tag = "lat3") 

    lat = phase_MEF_Intra.getLattice()
    recipe.constrain(lat.a, "zoom_Intra")
    recipe.constrain(lat.b, "zoom_Intra")
    recipe.constrain(lat.c, "zoom_Intra")
   # Constrain fractional xyz parameters
    atoms = phase_MEF_Intra.getScatterers()
   # Constrain ADPs
    recipe.newVar("Uiso_Intra", 0.005, tag = "T2") 
    
    for atom in atoms:
    	if atom.element.title() == "C":
             recipe.constrain(atom.Uiso, "Uiso_Intra")

    	elif atom.element.title() == "O":
             recipe.constrain(atom.Uiso, "Uiso_Intra")

    	elif atom.element.title() == "N":
             recipe.constrain(atom.Uiso, "Uiso_Intra")

    	elif atom.element.title() == "H":
             recipe.constrain(atom.Uiso, "Uiso_Intra")

    generator_MEF_Intra.delta2.value = 0

    # Give the recipe away so it can be used!
    return recipe
Example #13
0
def makeRecipe(ciffile, grdata, iqdata):
    """Make complex-modeling recipe where I(q) and G(r) are fit
    simultaneously.

    The fit I(q) is fed into the calculation of G(r), which provides feedback
    for the fit parameters of both.
    
    """

    # Create a PDF contribution as before
    pdfprofile = Profile()
    pdfparser = PDFParser()
    pdfparser.parseFile(grdata)
    pdfprofile.loadParsedData(pdfparser)
    pdfprofile.setCalculationRange(xmin = 0.1, xmax = 20)

    pdfcontribution = FitContribution("pdf")
    pdfcontribution.setProfile(pdfprofile, xname = "r")

    pdfgenerator = PDFGenerator("G")
    pdfgenerator.setQmax(30.0)
    stru = CreateCrystalFromCIF(file(ciffile))
    pdfgenerator.setStructure(stru)
    pdfcontribution.addProfileGenerator(pdfgenerator)
    pdfcontribution.setResidualEquation("resv")

    # Create a SAS contribution as well. We assume the nanoparticle is roughly
    # elliptical.
    sasprofile = Profile()
    sasparser = SASParser()
    sasparser.parseFile(iqdata)
    sasprofile.loadParsedData(sasparser)

    sascontribution = FitContribution("sas")
    sascontribution.setProfile(sasprofile)

    from sans.models.EllipsoidModel import EllipsoidModel
    model = EllipsoidModel()
    sasgenerator = SASGenerator("generator", model)
    sascontribution.addProfileGenerator(sasgenerator)
    sascontribution.setResidualEquation("resv")

    # Now we set up a characteristic function calculator that depends on the
    # sas model.
    cfcalculator = SASCF("f", model)

    # Register the calculator with the pdf contribution and define the fitting
    # equation.
    pdfcontribution.registerCalculator(cfcalculator)
    # The PDF for a nanoscale crystalline is approximated by
    # Gnano = f * Gcryst
    pdfcontribution.setEquation("f * G")

    # Moving on
    recipe = FitRecipe()
    recipe.addContribution(pdfcontribution)
    recipe.addContribution(sascontribution)

    # PDF
    phase = pdfgenerator.phase
    for par in phase.sgpars:
        recipe.addVar(par)

    recipe.addVar(pdfgenerator.scale, 1)
    recipe.addVar(pdfgenerator.delta2, 0)

    # SAS
    recipe.addVar(sasgenerator.scale, 1, name = "iqscale")
    recipe.addVar(sasgenerator.radius_a, 10)
    recipe.addVar(sasgenerator.radius_b, 10)

    # Even though the cfcalculator and sasgenerator depend on the same sas
    # model, we must still constrain the cfcalculator Parameters so that it is
    # informed of changes in the refined parameters.
    recipe.constrain(cfcalculator.radius_a, "radius_a")
    recipe.constrain(cfcalculator.radius_b, "radius_b")

    return recipe
Example #14
0
def makeRecipe(ciffile, datname):
    """Create a fitting recipe for crystalline PDF data."""

    ## The Profile
    # This will be used to store the observed and calculated PDF profile.
    profile = Profile()

    # Load data and add it to the Profile. Unlike in other examples, we use a
    # class (PDFParser) to help us load the data. This class will read the data
    # and relevant metadata from a two- to four-column data file generated
    # with PDFGetX2 or PDFGetN. The metadata will be passed to the PDFGenerator
    # when they are associated in the FitContribution, which saves some
    # configuration steps.
    parser = PDFParser()
    parser.parseFile(datname)
    profile.loadParsedData(parser)
    profile.setCalculationRange(xmax = 20)

    ## The ProfileGenerator
    # The PDFGenerator is for configuring and calculating a PDF profile. Here,
    # we want to refine a Structure object from diffpy.structure. We tell the
    # PDFGenerator that with the 'setStructure' method. All other configuration
    # options will be inferred from the metadata that is read by the PDFParser.
    # In particular, this will set the scattering type (x-ray or neutron), the
    # Qmax value, as well as initial values for the non-structural Parameters.
    generator = PDFGenerator("G")
    stru = Structure()
    stru.read(ciffile)
    generator.setStructure(stru)

    ## The FitContribution
    # Here we associate the Profile and ProfileGenerator, as has been done
    # before.
    contribution = FitContribution("nickel")
    contribution.addProfileGenerator(generator)
    contribution.setProfile(profile, xname = "r")

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

    ## Configure the fit variables

    # The PDFGenerator class holds the ParameterSet associated with the
    # Structure passed above in a data member named "phase". (We could have
    # given the ParameterSet a name other than "phase" when we added it to the
    # PDFGenerator.) The ParameterSet in this case is a StructureParameterSet,
    # the documentation for which is found in the
    # diffpy.srfit.structure.diffpystructure module.
    phase = generator.phase

    # We start by constraining the phase to the known space group. We could do
    # this by hand, but there is a method in diffpy.srfit.structure named
    # 'constrainAsSpaceGroup' for this purpose. The constraints will by default
    # be applied to the sites, the lattice and to the ADPs. See the method
    # documentation for more details. The 'constrainAsSpaceGroup' method may
    # create new Parameters, which it returns in a SpaceGroupParameters object.
    from diffpy.srfit.structure import constrainAsSpaceGroup
    sgpars = constrainAsSpaceGroup(phase, "Fm-3m")

    # The SpaceGroupParameters object returned by 'constrainAsSpaceGroup' holds
    # the free Parameters allowed by the space group constraints. Once a
    # structure is constrained, we need (should) only use the Parameters
    # provided in the SpaceGroupParameters, as the relevant structure
    # Parameters are constrained to these.
    #
    # We know that the space group does not allow for any free sites because
    # each atom is on a special position. There is one free (cubic) lattice
    # parameter and one free (isotropic) ADP. We can access these Parameters in
    # the xyzpars, latpars, and adppars members of the SpaceGroupParameters
    # object.
    for par in sgpars.latpars:
        recipe.addVar(par)
    for par in sgpars.adppars:
        recipe.addVar(par, 0.005)

    # We now select non-structural parameters to refine.
    # This controls the scaling of the PDF.
    recipe.addVar(generator.scale, 1)
    # This is a peak-damping resolution term.
    recipe.addVar(generator.qdamp, 0.01)
    # This is a vibrational correlation term that sharpens peaks at low-r.
    recipe.addVar(generator.delta2, 5)

    # Give the recipe away so it can be used!
    return recipe
# prepare profile object with experimental data
profile = Profile()
parser = PDFParser()
parser.parseFile(dataFile)
profile.loadParsedData(parser)

# define range for pdf calculation
rmin = 0.01
rmax = 20
rstep = 0.01

# setup calculation range for the PDF simulation
profile.setCalculationRange(xmin=rmin, xmax=rmax, dx=rstep)

# prepare nucpdf function that simulates the nuclear PDF
nucpdf = PDFGenerator("nucpdf")
nucpdf.setStructure(mno)
nucpdf.setProfile(profile)

# prepare magpdf function that simulates the magnetic PDF

# Create the Mn2+ magnetic species
mn2p = magSpecies(struc=mno, label='Mn2+', magIdxs=[0,1,2],
                  basisvecs=2.5*np.array([1,0,0]), kvecs=np.array([0,0,1.5]),
                  ffparamkey='Mn2')

# Create and prep the magnetic structure
mstr = magStructure()
mstr.loadSpecies(mn2p)
mstr.makeAll()
Example #16
0
def makerecipe(structure_file, data_file):
    """
    Basic function for creating and properly constraining a fit recipe.

    Parameters
    ----------
    structure_file : Path object or str
        Path to *.cif file, containing a structural model to use to fit the PDF data.
    data_file : Path object or str
        Path to data file containing PDF data to fit against.

    Returns
    -------
    recipe : FitRecipe object
        An initialized fit recipe object, ready for fitting.
    """
    ######## Profile Section ##################
    # Create a Profile object for the experimental dataset.
    # This handles all details about the dataset.
    # We also tell this profile the range and mesh of points in r-space.
    profile = Profile()
    parser = PDFParser()
    parser.parseFile(data_file)
    profile.loadParsedData(parser)

    p_cif = getParser('cif')
    structure = p_cif.parseFile(str(structure_file))
    space_group = p_cif.spacegroup.short_name

    ######## PDF Generator Section ##################
    # Create a PDF Generator object for a periodic structure model.
    # Here we name it "G1" and we give it the structure object.
    # This Generator will later compute the model PDF for the structure
    # object we provide it here.
    generator_crystal1 = PDFGenerator("G1")
    generator_crystal1.setStructure(structure, periodic=True)

    ######## Fit Contribution Section ##################
    # Create a Fit Contribution object, and name it "crystal."
    # We then give the PDF Generator object we created just above
    # to this Fit Contribution object. The Fit Contribution holds
    # the equation used to fit the PDF.
    contribution = FitContribution("crystal")
    contribution.addProfileGenerator(generator_crystal1)

    # Set an equation, within the Fit Contribution, based on your PDF
    # Generators. Here we simply have one Generator, G1, and a scale variable,
    # s1. Using this structure is a very flexible way of adding additional
    # Generators (ie. multiple structural phases), experimental Profiles,
    # PDF characteristic functions (ie. shape envelopes), and more.
    contribution.setEquation("s1*G1")

    # Set the experimental profile, within the Fit Contribution object,
    # to the Profile object we created earlier.
    contribution.setProfile(profile, xname="r")

    ######## Recipe Section ##################
    # Create the Fit Recipe object that holds all the details of the fit,
    # defined in the lines above. We give the Fit Recipe the Fit
    # Contribution we created earlier.
    recipe = FitRecipe()
    recipe.addContribution(contribution)

    # Return the Fit Recipe object to be optimized
    return recipe
Example #17
0
def makeRecipe(stru1, stru2, datname):
    """Create a fitting recipe for crystalline PDF data."""

    ## The Profile
    profile = Profile()

    # Load data and add it to the profile
    parser = PDFParser()
    parser.parseFile(datname)
    profile.loadParsedData(parser)
    profile.setCalculationRange(xmin=1.5, xmax=45, dx=0.1)

    ## The ProfileGenerator
    # In order to fit the core and shell phases simultaneously, we must use two
    # PDFGenerators.
    #
    # The generator for the CdS core. We call it "G_CdS" and will use this name
    # later when we set the fitting equation in the FitContribution.
    generator_cds = PDFGenerator("G_CdS")
    generator_cds.setStructure(stru1)
    generator_cds.setQmax(26)
    generator_cds.qdamp.value = 0.0396
    # The generator for the ZnS shell. We call it "G_ZnS".
    generator_zns = PDFGenerator("G_ZnS")
    generator_zns.setStructure(stru2)
    generator_zns.setQmax(26)
    generator_zns.qdamp.value = 0.0396

    ## The FitContribution
    # Add both generators and the profile to the FitContribution.
    contribution = FitContribution("cdszns")
    contribution.addProfileGenerator(generator_cds)
    contribution.addProfileGenerator(generator_zns)
    contribution.setProfile(profile, xname="r")

    # Set up the characteristic functions. We use a spherical CF for the core
    # and a spherical shell CF for the shell. Since this is set up as two
    # phases, we implicitly assume that the core-shell correlations contribute
    # very little to the PDF.
    from diffpy.srfit.pdf.characteristicfunctions import sphericalCF, shellCF
    contribution.registerFunction(sphericalCF, name="f_CdS")
    contribution.registerFunction(shellCF, name="f_ZnS")

    # Write the fitting equation. We want to sum the PDFs from each phase and
    # multiply it by a scaling factor.
    contribution.setEquation("scale * (f_CdS * G_CdS +  f_ZnS * G_ZnS)")

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

    # Vary the inner radius and thickness of the shell. Constrain the core
    # diameter to twice the shell radius.
    recipe.addVar(contribution.radius, 15)
    recipe.addVar(contribution.thickness, 11)
    recipe.constrain(contribution.psize, "2 * radius")

    ## Configure the fit variables
    # Start by configuring the scale factor and resolution factors.
    # We want the sum of the phase scale factors to be 1.
    recipe.newVar("scale_CdS", 0.7)
    recipe.constrain(generator_cds.scale, "scale_CdS")
    recipe.constrain(generator_zns.scale, "1 - scale_CdS")
    # We also want the resolution factor to be the same on each.

    # Vary the gloabal scale as well.
    recipe.addVar(contribution.scale, 0.3)

    # Now we can configure the structural parameters. We tag the different
    # structural variables so we can easily turn them on and off in the
    # subsequent refinement.
    phase_cds = generator_cds.phase
    for par in phase_cds.sgpars.latpars:
        recipe.addVar(par, name=par.name + "_cds", tag="lat")
    for par in phase_cds.sgpars.adppars:
        recipe.addVar(par, 1, name=par.name + "_cds", tag="adp")
    recipe.addVar(phase_cds.sgpars.xyzpars.z_1, name="z_1_cds", tag="xyz")
    # Since we know these have stacking disorder, constrain the B33 adps for
    # each atom type.
    recipe.constrain("B33_1_cds", "B33_0_cds")
    recipe.addVar(generator_cds.delta2, name="delta2_cds", value=5)

    phase_zns = generator_zns.phase
    for par in phase_zns.sgpars.latpars:
        recipe.addVar(par, name=par.name + "_zns", tag="lat")
    for par in phase_zns.sgpars.adppars:
        recipe.addVar(par, 1, name=par.name + "_zns", tag="adp")
    recipe.addVar(phase_zns.sgpars.xyzpars.z_1, name="z_1_zns", tag="xyz")
    recipe.constrain("B33_1_zns", "B33_0_zns")
    recipe.addVar(generator_zns.delta2, name="delta2_zns", value=2.5)

    # Give the recipe away so it can be used!
    return recipe
Example #18
0
def makeRecipe(ciffile_ni, ciffile_si, xdata_ni, ndata_ni, xdata_si,
               xdata_sini):
    """Create a fitting recipe for crystalline PDF data."""

    ## The Profiles
    # We need a profile for each data set.
    xprofile_ni = makeProfile(xdata_ni)
    xprofile_si = makeProfile(xdata_si)
    nprofile_ni = makeProfile(ndata_ni)
    xprofile_sini = makeProfile(xdata_sini)

    ## The ProfileGenerators
    # We create one for each phase and share the phases.
    xgenerator_ni = PDFGenerator("xG_ni")
    stru = loadCrystal(ciffile_ni)
    xgenerator_ni.setStructure(stru)
    phase_ni = xgenerator_ni.phase

    xgenerator_si = PDFGenerator("xG_si")
    stru = loadCrystal(ciffile_si)
    xgenerator_si.setStructure(stru)
    phase_si = xgenerator_si.phase

    ngenerator_ni = PDFGenerator("nG_ni")
    ngenerator_ni.setPhase(phase_ni)

    xgenerator_sini_ni = PDFGenerator("xG_sini_ni")
    xgenerator_sini_ni.setPhase(phase_ni)

    xgenerator_sini_si = PDFGenerator("xG_sini_si")
    xgenerator_sini_si.setPhase(phase_si)

    ## The FitContributions
    # We one of these for each data set.
    xcontribution_ni = makeContribution("xnickel", xgenerator_ni, xprofile_ni)
    xcontribution_si = makeContribution("xsilicon", xgenerator_si, xprofile_si)
    ncontribution_ni = makeContribution("nnickel", ngenerator_ni, nprofile_ni)
    xcontribution_sini = makeContribution("xsini", xgenerator_sini_ni,
                                          xprofile_sini)
    xcontribution_sini.addProfileGenerator(xgenerator_sini_si)
    xcontribution_sini.setEquation("scale * (xG_sini_ni +  xG_sini_si)")

    # As explained in another example, we want to minimize using Rw^2.
    xcontribution_ni.setResidualEquation("resv")
    xcontribution_si.setResidualEquation("resv")
    ncontribution_ni.setResidualEquation("resv")
    xcontribution_sini.setResidualEquation("resv")

    # Make the FitRecipe and add the FitContributions.
    recipe = FitRecipe()
    recipe.addContribution(xcontribution_ni)
    recipe.addContribution(xcontribution_si)
    recipe.addContribution(ncontribution_ni)
    recipe.addContribution(xcontribution_sini)

    # Now we vary and constrain Parameters as before.
    for par in phase_ni.sgpars:
        recipe.addVar(par, name=par.name + "_ni")
    delta2_ni = recipe.newVar("delta2_ni", 2.5)
    recipe.constrain(xgenerator_ni.delta2, delta2_ni)
    recipe.constrain(ngenerator_ni.delta2, delta2_ni)
    recipe.constrain(xgenerator_sini_ni.delta2, delta2_ni)

    for par in phase_si.sgpars:
        recipe.addVar(par, name=par.name + "_si")
    delta2_si = recipe.newVar("delta2_si", 2.5)
    recipe.constrain(xgenerator_si.delta2, delta2_si)
    recipe.constrain(xgenerator_sini_si.delta2, delta2_si)

    # Now the experimental parameters
    recipe.addVar(xgenerator_ni.scale, name="xscale_ni")
    recipe.addVar(xgenerator_si.scale, name="xscale_si")
    recipe.addVar(ngenerator_ni.scale, name="nscale_ni")
    recipe.addVar(xcontribution_sini.scale, 1.0, "xscale_sini")
    recipe.newVar("pscale_sini_ni", 0.8)
    recipe.constrain(xgenerator_sini_ni.scale, "pscale_sini_ni")
    recipe.constrain(xgenerator_sini_si.scale, "1 - pscale_sini_ni")

    # The qdamp parameters are too correlated to vary so we fix them based on
    # previous measurments.
    xgenerator_ni.qdamp.value = 0.055
    xgenerator_si.qdamp.value = 0.051
    ngenerator_ni.qdamp.value = 0.030
    xgenerator_sini_ni.qdamp.value = 0.052
    xgenerator_sini_si.qdamp.value = 0.052

    # Give the recipe away so it can be used!
    return recipe
Example #19
0
def makeRecipe(stru1, stru2, stru3, datname):
    """Create a fitting recipe for crystalline PDF data."""

    ## The Profile
    profile = Profile()

    # Load data and add it to the profile
    parser = PDFParser()
    parser.parseFile(datname)
    profile.loadParsedData(parser)
    profile.setCalculationRange(xmin=1, xmax=20, dx=0.01)

    ## The ProfileGenerator
    generator_ROY_Cryst_B = PDFGenerator("G_ROY_Cryst_B")
    generator_ROY_Cryst_B.setStructure(stru1, periodic=True)
    generator_ROY_Mole_B = DebyePDFGenerator("G_ROY_Mole_B")
    generator_ROY_Mole_B.setStructure(stru2, periodic=False)
    generator_ROY_Intra = DebyePDFGenerator("G_ROY_Intra")
    generator_ROY_Intra.setStructure(stru3, periodic=False)

    ## The FitContribution
    # Add both generators to the FitContribution. Add the Profile. This will
    # send the metadata to the generators.
    contribution = FitContribution("ROY")
    contribution.addProfileGenerator(generator_ROY_Cryst_B)
    contribution.addProfileGenerator(generator_ROY_Mole_B)
    contribution.addProfileGenerator(generator_ROY_Intra)
    contribution.setProfile(profile, xname="r")

    # Write the fitting equation. We want to sum the PDFs from each phase and
    # multiply it by a scaling factor. We also want a certain phase scaling
    # relationship between the PDFs which we will enforce with constraints in
    # the FitRecipe.
    #from diffpy.srfit.pdf.characteristicfunctions import sphericalCF
    #contribution.registerFunction(sphericalCF, name = "f_IMC")
    contribution.setEquation(
        "scale * (G_ROY_Cryst_B - G_ROY_Mole_B + G_ROY_Intra)")

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

    qdamp = 0.02902
    generator_ROY_Cryst_B.qdamp.value = qdamp
    generator_ROY_Mole_B.qdamp.value = qdamp
    generator_ROY_Intra.qdamp.value = qdamp

    qbroad = 0.017315
    generator_ROY_Cryst_B.qbroad.value = qbroad
    generator_ROY_Mole_B.qbroad.value = qbroad
    generator_ROY_Intra.qbroad.value = qbroad

    # Vary the gloabal scale as well.
    recipe.addVar(contribution.scale, 1)

    #############################################################################################
    ############### First the ROY_Cryst_B parameters ############################################
    #############################################################################################
    phase_ROY_Cryst_B = generator_ROY_Cryst_B.phase

    lat = phase_ROY_Cryst_B.getLattice()
    atoms = phase_ROY_Cryst_B.getScatterers()

    recipe.newVar("Uiso_Inter", 0.05, tag="T1")
    recipe.newVar("lat_a", 3.9453, tag="lat")
    recipe.newVar("lat_b", 18.685, tag="lat")
    recipe.newVar("lat_c", 16.3948, tag="lat")
    #recipe.newVar("alpha", 90, tag = "lat")
    recipe.newVar("beta", 93.83, tag="lat")
    #recipe.newVar("gamma", 90, tag = "lat")

    recipe.constrain(lat.a, "lat_a")
    recipe.constrain(lat.b, "lat_b")
    recipe.constrain(lat.c, "lat_c")
    #recipe.constrain(lat.alpha, "alpha")
    recipe.constrain(lat.beta, "beta")
    #recipe.constrain(lat.gamma, "gamma")

    for atom in atoms:
        if atom.element.title() == "N":
            recipe.constrain(atom.Uiso, "Uiso_Inter")

        elif atom.element.title() == "O":
            recipe.constrain(atom.Uiso, "Uiso_Inter")

        elif atom.element.title() == "C":
            recipe.constrain(atom.Uiso, "Uiso_Inter")

        elif atom.element.title() == "H":
            recipe.constrain(atom.Uiso, "Uiso_Inter")

        elif atom.element.title() == "S":
            recipe.constrain(atom.Uiso, "Uiso_Inter")

    generator_ROY_Cryst_B.delta2.value = 0
    #    recipe.addVar(generator_IMC_Cryst_B.delta2, name = "delta2_IMC_Cryst_B", value =
    #            0, tag = "delta")

    #############################################################################################
    ############### Second the ROY_Mole_B parameters ############################################
    #############################################################################################
    phase_ROY_Mole_B = generator_ROY_Mole_B.phase
    generator_ROY_Mole_B.setQmin(0.0)
    generator_ROY_Mole_B.setQmax(24.0)
    recipe.newVar("zoom_Mole_B", 1, tag="lat2")

    lat = phase_ROY_Mole_B.getLattice()
    recipe.constrain(lat.a, "zoom_Mole_B")
    recipe.constrain(lat.b, "zoom_Mole_B")
    recipe.constrain(lat.c, "zoom_Mole_B")
    # Constrain fractional xyz parameters
    atoms = phase_ROY_Mole_B.getScatterers()
    # Constrain ADPs
    #recipe.newVar("Uiso_Inter", 0.05, tag = "T2")

    for atom in atoms:
        if atom.element.title() == "C":
            recipe.constrain(atom.Uiso, "Uiso_Inter")

        elif atom.element.title() == "O":
            recipe.constrain(atom.Uiso, "Uiso_Inter")

        elif atom.element.title() == "N":
            recipe.constrain(atom.Uiso, "Uiso_Inter")

        elif atom.element.title() == "H":
            recipe.constrain(atom.Uiso, "Uiso_Inter")

        elif atom.element.title() == "S":
            recipe.constrain(atom.Uiso, "Uiso_Inter")

    generator_ROY_Mole_B.delta2.value = 0

    #    recipe.addVar(generator_IMC_Mole_B.delta2, name = "delta2_IMC_Mole_B", value
    #            = 5.66086478091, tag = "delta")

    #############################################################################################
    ############### Third the intra molecule parameters##########################################
    #############################################################################################
    phase_ROY_Intra = generator_ROY_Intra.phase
    generator_ROY_Intra.setQmin(0.0)
    generator_ROY_Intra.setQmax(24.0)
    recipe.newVar("zoom_Intra", 1, tag="lat3")

    lat = phase_ROY_Intra.getLattice()
    recipe.constrain(lat.a, "zoom_Intra")
    recipe.constrain(lat.b, "zoom_Intra")
    recipe.constrain(lat.c, "zoom_Intra")
    # Constrain fractional xyz parameters
    atoms = phase_ROY_Intra.getScatterers()
    # Constrain ADPs
    recipe.newVar("Uiso_Intra", 0.005, tag="T2")

    for atom in atoms:
        if atom.element.title() == "C":
            recipe.constrain(atom.Uiso, "Uiso_Intra")

        elif atom.element.title() == "O":
            recipe.constrain(atom.Uiso, "Uiso_Intra")

        elif atom.element.title() == "N":
            recipe.constrain(atom.Uiso, "Uiso_Intra")

        elif atom.element.title() == "H":
            recipe.constrain(atom.Uiso, "Uiso_Intra")

        elif atom.element.title() == "S":
            recipe.constrain(atom.Uiso, "Uiso_Intra")

    generator_ROY_Intra.delta2.value = 0

    # Give the recipe away so it can be used!
    return recipe
Example #20
0
 def setUp(self):
     global PDFGenerator
     from diffpy.srfit.pdf import PDFGenerator
     self.gen = PDFGenerator()
     return
Example #21
0
def makeRecipe(ciffile, datname):
    """Create a fitting recipe for crystalline PDF data."""

    ## The Profile
    # This will be used to store the observed and calculated PDF profile.
    profile = Profile()

    # Load data and add it to the Profile. As before we use a PDFParser. The
    # metadata is still passed to the PDFGenerator later on. The interaction
    # between the PDFGenerator and the metadata does not depend on type of
    # structure being refined.
    parser = PDFParser()
    parser.parseFile(datname)
    profile.loadParsedData(parser)
    profile.setCalculationRange(xmax=20)

    ## The ProfileGenerator
    # This time we use the CreateCrystalFromCIF method of pyobjcryst.crystal to
    # create a Crystal object. That object is passed to the PDFGenerator as in
    # the previous example.
    generator = PDFGenerator("G")
    stru = CreateCrystalFromCIF(file(ciffile))
    generator.setStructure(stru)
    generator.setQmax(40.0)

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

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

    ## Configure the fit variables

    # As before, we get a handle to the structure parameter set. In this case,
    # it is a ObjCrystCrystalParSet instance that was created when we called
    # 'setStructure' above. The ObjCrystCrystalParSet has different Parameters
    # and options than the DiffpyStructureParSet used in the last example. See
    # its documentation in diffpy.srfit.structure.objcrystparset.
    phase = generator.phase

    # Here is where we created space group constraints in the previous example.
    # The difference in this example is that the ObjCrystCrystalParSet is aware
    # of space groups, and the DiffpyStructureParSet is not. Constraints are
    # created internally when "sgpars" attribute is called for. These
    # constraints get enforced within the ObjCrystCrystalParSet. Free
    # Parameters are stored within the 'sgpars' member of the
    # ObjCrystCrystalParSet, which is the same as the object returned from
    # 'constrainAsSpaceGroup'.
    #
    # As before, we have one free lattice parameter ('a'). We can simplify
    # things by iterating through all the sgpars.
    for par in phase.sgpars:
        recipe.addVar(par)
    # set the initial thermal factor to a non-zero value
    assert hasattr(recipe, 'B11_0')
    recipe.B11_0 = 0.1

    # We now select non-structural parameters to refine.
    # This controls the scaling of the PDF.
    recipe.addVar(generator.scale, 1)
    # This is a peak-damping resolution term.
    recipe.addVar(generator.qdamp, 0.01)
    # This is a vibrational correlation term that sharpens peaks at low-r.
    recipe.addVar(generator.delta2, 5)

    # Give the recipe away so it can be used!
    return recipe
Example #22
0
def makeRecipe(ciffile, grdata, iqdata):
    """Make complex-modeling recipe where I(q) and G(r) are fit
    simultaneously.

    The fit I(q) is fed into the calculation of G(r), which provides feedback
    for the fit parameters of both.

    """

    # Create a PDF contribution as before
    pdfprofile = Profile()
    pdfparser = PDFParser()
    pdfparser.parseFile(grdata)
    pdfprofile.loadParsedData(pdfparser)
    pdfprofile.setCalculationRange(xmin = 0.1, xmax = 20)

    pdfcontribution = FitContribution("pdf")
    pdfcontribution.setProfile(pdfprofile, xname = "r")

    pdfgenerator = PDFGenerator("G")
    pdfgenerator.setQmax(30.0)
    stru = loadCrystal(ciffile)
    pdfgenerator.setStructure(stru)
    pdfcontribution.addProfileGenerator(pdfgenerator)
    pdfcontribution.setResidualEquation("resv")

    # Create a SAS contribution as well. We assume the nanoparticle is roughly
    # elliptical.
    sasprofile = Profile()
    sasparser = SASParser()
    sasparser.parseFile(iqdata)
    sasprofile.loadParsedData(sasparser)
    if all(sasprofile.dy == 0):
        sasprofile.dy[:] = 1

    sascontribution = FitContribution("sas")
    sascontribution.setProfile(sasprofile)

    from sas.models.EllipsoidModel import EllipsoidModel
    model = EllipsoidModel()
    sasgenerator = SASGenerator("generator", model)
    sascontribution.addProfileGenerator(sasgenerator)
    sascontribution.setResidualEquation("resv")

    # Now we set up a characteristic function calculator that depends on the
    # sas model.
    cfcalculator = SASCF("f", model)

    # Register the calculator with the pdf contribution and define the fitting
    # equation.
    pdfcontribution.registerCalculator(cfcalculator)
    # The PDF for a nanoscale crystalline is approximated by
    # Gnano = f * Gcryst
    pdfcontribution.setEquation("f * G")

    # Moving on
    recipe = FitRecipe()
    recipe.addContribution(pdfcontribution)
    recipe.addContribution(sascontribution)

    # PDF
    phase = pdfgenerator.phase
    for par in phase.sgpars:
        recipe.addVar(par)

    recipe.addVar(pdfgenerator.scale, 1)
    recipe.addVar(pdfgenerator.delta2, 0)

    # SAS
    recipe.addVar(sasgenerator.scale, 1, name = "iqscale")
    recipe.addVar(sasgenerator.radius_a, 10)
    recipe.addVar(sasgenerator.radius_b, 10)

    # Even though the cfcalculator and sasgenerator depend on the same sas
    # model, we must still constrain the cfcalculator Parameters so that it is
    # informed of changes in the refined parameters.
    recipe.constrain(cfcalculator.radius_a, "radius_a")
    recipe.constrain(cfcalculator.radius_b, "radius_b")

    return recipe
Example #23
0
def makeRecipe(niciffile, siciffile, datname):
    """Create a fitting recipe for crystalline PDF data."""

    ## The Profile
    profile = Profile()

    # Load data and add it to the profile
    parser = PDFParser()
    parser.parseFile(datname)
    profile.loadParsedData(parser)
    profile.setCalculationRange(xmax=20)

    ## The ProfileGenerator
    # In order to fit two phases simultaneously, we must use two PDFGenerators.
    # PDFGenerator is designed to take care of as little information as it
    # must. (Don't do too much, and do it well.) A PDFGenerator can generate
    # the signal from only a single phase at a time. So, we will create one
    # PDFGenerator for each phase and compose them within the same
    # FitContribution. Note that both generators will be associated with the
    # same Profile within the FitContribution, so they will both be
    # automatically configured according to the metadata.
    #
    # The generator for the nickel phase. We call it "G_ni" and will use this
    # name later when we set the fitting equation in the FitContribution.
    generator_ni = PDFGenerator("G_ni")
    stru = CreateCrystalFromCIF(file(niciffile))
    generator_ni.setStructure(stru)
    # The generator for the silicon phase. We call it "G_si".
    generator_si = PDFGenerator("G_si")
    stru = CreateCrystalFromCIF(file(siciffile))
    generator_si.setStructure(stru)

    ## The FitContribution
    # Add both generators to the FitContribution. Add the Profile. This will
    # send the metadata to the generators.
    contribution = FitContribution("nisi")
    contribution.addProfileGenerator(generator_ni)
    contribution.addProfileGenerator(generator_si)
    contribution.setProfile(profile, xname="r")

    # Write the fitting equation. We want to sum the PDFs from each phase and
    # multiply it by a scaling factor. We also want a certain phase scaling
    # relationship between the PDFs which we will enforce with constraints in
    # the FitRecipe.
    contribution.setEquation("scale * (G_ni +  G_si)")

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

    ## Configure the fit variables
    # Start by configuring the scale factor and resolution factors.
    # We want the sum of the phase scale factors to be 1.
    recipe.newVar("scale_ni", 0.1)
    recipe.constrain(generator_ni.scale, "scale_ni")
    recipe.constrain(generator_si.scale, "1 - scale_ni")
    # We also want the resolution factor to be the same on each.
    recipe.newVar("qdamp", 0.03)
    recipe.constrain(generator_ni.qdamp, "qdamp")
    recipe.constrain(generator_si.qdamp, "qdamp")

    # Vary the gloabal scale as well.
    recipe.addVar(contribution.scale, 1)

    # Now we can configure the structural parameters. Since we're using
    # ObjCrystCrystalParSets, the space group constraints are automatically
    # applied to each phase. We must selectively vary the free parameters.
    #
    # First the nickel parameters
    phase_ni = generator_ni.phase
    for par in phase_ni.sgpars:
        recipe.addVar(par, name=par.name + "_ni")
    recipe.addVar(generator_ni.delta2, name="delta2_ni")
    # Next the silicon parameters
    phase_si = generator_si.phase
    for par in phase_si.sgpars:
        recipe.addVar(par, name=par.name + "_si")
    recipe.addVar(generator_si.delta2, name="delta2_si")

    # We have prior information from the earlier examples so we'll use it here
    # in the form of restraints.
    #
    # The nickel lattice parameter was measured to be 3.527. The uncertainty
    # values are invalid for that measurement, since the data from which it is
    # derived has no uncertainty. Thus, we will tell the recipe to scale the
    # residual, which means that it will be weighted as much as the average
    # data point during the fit.
    recipe.restrain("a_ni", lb=3.527, ub=3.527, scaled=True)
    # Now we do the same with the delta2 and Biso parameters (remember that
    # Biso = 8*pi**2*Uiso)
    recipe.restrain("delta2_ni", lb=2.22, ub=2.22, scaled=True)
    recipe.restrain("Biso_0_ni", lb=0.454, ub=0.454, scaled=True)
    #
    # We can do the same with the silicon values. We haven't done a thorough
    # job of measuring the uncertainties in the results, so we'll scale these
    # as well.
    recipe.restrain("a_si", lb=5.430, ub=5.430, scaled=True)
    recipe.restrain("delta2_si", lb=3.54, ub=3.54, scaled=True)
    recipe.restrain("Biso_0_si", lb=0.645, ub=0.645, scaled=True)

    # Give the recipe away so it can be used!
    return recipe
def makeRecipe(ciffile, xdatname, ndatname):
    """Create a fitting recipe for crystalline PDF data."""

    ## The Profiles
    # We need a profile for each data set. This means that we will need two
    # FitContributions as well.
    xprofile = Profile()
    nprofile = Profile()

    # Load data and add it to the proper Profile.
    parser = PDFParser()
    parser.parseFile(xdatname)
    xprofile.loadParsedData(parser)
    xprofile.setCalculationRange(xmax = 20)

    parser = PDFParser()
    parser.parseFile(ndatname)
    nprofile.loadParsedData(parser)
    nprofile.setCalculationRange(xmax = 20)

    ## The ProfileGenerators
    # We need one of these for the x-ray data.
    xgenerator = PDFGenerator("G")
    stru = loadCrystal(ciffile)
    xgenerator.setStructure(stru)

    # And we need one for the neutron data. We want to refine the same
    # structure object in each PDFGenerator. This would suggest that we add the
    # same Crystal to each. However, if we do that then we will have two
    # Parameters for each Crystal data member (two Parameters for the "a"
    # lattice parameter, etc.), held in different ObjCrystCrystalParSets, each
    # managed by its own PDFGenerator. Thus, changes made to the Crystal
    # through one PDFGenerator will not be known to the other PDFGenerator
    # since their ObjCrystCrystalParSets don't know about each other. The
    # solution is to share ObjCrystCrystalParSets rather than Crystals. This
    # way there is only one Parameter for each Crystal data member. (An
    # alternative to this is to constrain each structure Parameter to be varied
    # to the same variable. The present approach is easier and less error
    # prone.)
    #
    # Tell the neutron PDFGenerator to use the phase from the x-ray
    # PDFGenerator.
    ngenerator = PDFGenerator("G")
    ngenerator.setPhase(xgenerator.phase)

    ## The FitContributions
    # We associate the x-ray PDFGenerator and Profile in one FitContribution...
    xcontribution = FitContribution("xnickel")
    xcontribution.addProfileGenerator(xgenerator)
    xcontribution.setProfile(xprofile, xname = "r")
    # and the neutron objects in another.
    ncontribution = FitContribution("nnickel")
    ncontribution.addProfileGenerator(ngenerator)
    ncontribution.setProfile(nprofile, xname = "r")

    # This example is different than the previous ones in that we are composing
    # a residual function from other residuals (one for the x-ray contribution
    # and one for the neutron contribution). The relative magnitude of these
    # residuals effectively determines the influence of each contribution over
    # the fit. This is a problem in this case because the x-ray data has
    # uncertainty values associated with it (on the order of 1e-4), and the
    # chi^2 residual is proportional to 1 / uncertainty**2. The neutron has no
    # uncertainty, so it's chi^2 is proportional to 1. Thus, my optimizing
    # chi^2 we would give the neutron data practically no weight in the fit. To
    # get around this, we will optimize a different metric.
    #
    # The contribution's residual can be either chi^2, Rw^2, or custom crafted.
    # In this case, we should minimize Rw^2 of each contribution so that each
    # one can contribute roughly equally to the fit.
    xcontribution.setResidualEquation("resv")
    ncontribution.setResidualEquation("resv")

    # Make the FitRecipe and add the FitContributions.
    recipe = FitRecipe()
    recipe.addContribution(xcontribution)
    recipe.addContribution(ncontribution)

    # Now we vary and constrain Parameters as before.
    recipe.addVar(xgenerator.scale, 1, "xscale")
    recipe.addVar(ngenerator.scale, 1, "nscale")
    recipe.addVar(xgenerator.qdamp, 0.01, "xqdamp")
    recipe.addVar(ngenerator.qdamp, 0.01, "nqdamp")
    # delta2 is a non-structual material propery. Thus, we constrain together
    # delta2 Parameter from each PDFGenerator.
    delta2 = recipe.newVar("delta2", 2)
    recipe.constrain(xgenerator.delta2, delta2)
    recipe.constrain(ngenerator.delta2, delta2)

    # We only need to constrain phase properties once since there is a single
    # ObjCrystCrystalParSet for the Crystal.
    phase = xgenerator.phase
    for par in phase.sgpars:
        recipe.addVar(par)
    recipe.B11_0 = 0.1

    # Give the recipe away so it can be used!
    return recipe
Example #25
0
 def setUp(self):
     self.gen = PDFGenerator()
     return