Ejemplo n.º 1
0
def main():
    """Set up and refine the recipe."""

    # Make the data and the recipe
    cdsciffile = "data/CdS.cif"
    znsciffile = "data/ZnS.cif"
    data = "data/CdS_ZnS_nano.gr"

    # Make the recipe
    stru1 = CreateCrystalFromCIF(file(cdsciffile))
    stru2 = CreateCrystalFromCIF(file(znsciffile))
    recipe = makeRecipe(stru1, stru2, data)
    from diffpy.srfit.fitbase.fithook import PlotFitHook
    recipe.pushFitHook(PlotFitHook())
    recipe.fithooks[0].verbose = 3

    # Optimize - we do this in steps to help convergence
    recipe.fix("all")

    # Start with the lattice parameters. In makeRecipe, these were tagged with
    # "lat". Here is how we use that.
    recipe.free("lat")
    leastsq(recipe.residual, recipe.values, maxfev=50)

    # Now the scale and phase fraction.
    recipe.free("scale", "scale_CdS")
    leastsq(recipe.residual, recipe.values, maxfev=50)

    # The ADPs.
    recipe.free("adp")
    leastsq(recipe.residual, recipe.values, maxfev=100)

    # The delta2 parameters.
    recipe.free("delta2_cds", "delta2_zns")
    leastsq(recipe.residual, recipe.values, maxfev=50)

    # The shape parameters.
    recipe.free("radius", "thickness")
    leastsq(recipe.residual, recipe.values, maxfev=50)

    # The positional parameters.
    recipe.free("xyz")
    leastsq(recipe.residual, recipe.values)

    # Generate and print the FitResults
    res = FitResults(recipe)
    res.printResults()

    # Plot!
    plotResults(recipe)
    return
Ejemplo n.º 2
0
    def loadStrufile(self, filename, stype='diffpy', periodic=None):
        '''
        read and parse a structure file
        '''
        self.filename = filename
        ext = os.path.splitext(filename)[1]

        if periodic != None:
            self.periodic = periodic
        else:
            # detect periodicity using file type
            if ext in ['.cif']:
                periodic = True
            else:
                periodic = False
            self.periodic = periodic

        # read the file
        if stype == 'diffpy':
            self.rawstru = loadStructure(filename)
            self.rawstype = stype
            self.parseDiffpyStru(self.rawstru)
        elif stype == 'objcryst':
            if ext == '.cif':
                self.rawstru = CreateCrystalFromCIF(file(filename))
                self.rawstype = stype
            else:
                raise TypeError('Cannot read file!')
        else:
            raise TypeError('Cannot read file!')
        return
Ejemplo n.º 3
0
def loadCrystal(filename):
    """Load pyobjcryst Crystal object from a CIF file.

    filename -- CIF file to be loaded

    Return a new Crystal object.

    See pyobjcryst.crystal.CreateCrystalFromCIF for additional
    options for constructing Crystal object from CIF data.
    """
    from pyobjcryst.crystal import CreateCrystalFromCIF
    with open(filename, 'rb') as fp:
        rv = CreateCrystalFromCIF(fp)
    return rv
Ejemplo n.º 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
Ejemplo n.º 5
0
def expandSymmetry(crystal):
    """Expand a crystal to P1 symmetry.

    This requires diffpy.Structure to be installed. This uses file IO transfer
    data, so there is some inherent precision loss.

    This returns a new structure.

    """

    # Create a string from a diffpy Structure, which is in P1 symmetry, and
    # load this as a Crystal.
    stru = crystalToDiffpyStructure(crystal)

    cifstr = stru.writeStr(format="cif")
    from cStringIO import StringIO
    buf = StringIO(cifstr)

    from pyobjcryst.crystal import CreateCrystalFromCIF
    p1 = CreateCrystalFromCIF(buf)
    return p1
Ejemplo n.º 6
0
    def _testPutAtomsInMolecule(self):
        """Make sure this utility method is correct."""

        from math import floor
        f = lambda v: v - floor(v)
        import glob
        from pyobjcryst.tests.pyobjcrysttestutils import datafile
        pat = os.path.join(datafile(''), '*.cif')

        for fname in glob.glob(pat):
            print fname

            c = CreateCrystalFromCIF(file(fname))

            from diffpy.Structure import Structure
            s = Structure(filename=fname)

            # Get positions from unmodified structure
            pos1 = []
            scl = c.GetScatteringComponentList()
            for s in scl:
                xyz = map(f, [s.X, s.Y, s.Z])
                xyz = c.FractionalToOrthonormalCoords(*xyz)
                pos1.append(xyz)

            # Get positions from molecular structure
            putAtomsInMolecule(c)
            pos2 = []
            scl = c.GetScatteringComponentList()
            for s in scl:
                xyz = map(f, [s.X, s.Y, s.Z])
                xyz = c.FractionalToOrthonormalCoords(*xyz)
                pos2.append(xyz)

            # Now compare positions
            self.assertEqual(len(pos1), len(pos2))

            for p1, p2 in zip(pos1, pos2):
                for i in range(3):
                    self.assertAlmostEqual(p1[i], p2[i])

        return
Ejemplo n.º 7
0
def makeRecipe(niciffile, siciffile, datname):
    """Create a fitting recipe for crystalline PDF data."""

    # Load data and add it to the profile
    contribution = PDFContribution("nisi")
    contribution.loadData(datname)
    contribution.setCalculationRange(xmax=20)

    stru = CreateCrystalFromCIF(file(niciffile))
    contribution.addStructure("ni", stru)

    stru = CreateCrystalFromCIF(file(siciffile))
    contribution.addStructure("si", stru)

    # 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(contribution.ni.scale, "scale_ni")
    recipe.constrain(contribution.si.scale, "1 - scale_ni")
    # We also want the resolution factor to be the same on each. This is done
    # for free by the PDFContribution. We simply need to add it to the recipe.
    recipe.addVar(contribution.qdamp, 0.03)

    # 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.
    # Note that ni is the name of the PDFGenerator that was automatically
    # created by the PDFContribution. We selected this name in addStructure
    # above.
    phase_ni = contribution.ni.phase
    for par in phase_ni.sgpars:
        recipe.addVar(par, name=par.name + "_ni")
    recipe.addVar(contribution.ni.delta2, name="delta2_ni")
    # Next the silicon parameters
    phase_si = contribution.si.phase
    for par in phase_si.sgpars:
        recipe.addVar(par, name=par.name + "_si")
    recipe.addVar(contribution.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
Ejemplo n.º 8
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
Ejemplo n.º 9
0
def get_pyobjcryst_sphalerite():
    from pyobjcryst.crystal import CreateCrystalFromCIF
    crst = CreateCrystalFromCIF(open('datafiles/sphalerite.cif'))
    return crst
Ejemplo n.º 10
0
def loadObjCrystCrystal(filename):
    from pyobjcryst.crystal import CreateCrystalFromCIF
    fullpath = datafile(filename)
    crst = CreateCrystalFromCIF(open(fullpath))
    return crst
Ejemplo n.º 11
0
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
Ejemplo n.º 12
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
Ejemplo n.º 13
0
Uisodefault = 0.005

# configure options parsing
parser = optparse.OptionParser("%prog [options]\n" + __doc__)
parser.add_option("--pyobjcryst",
                  action="store_true",
                  help="Use pyobjcryst to load the CIF file.")
parser.allow_interspersed_args = True
opts, args = parser.parse_args(sys.argv[1:])

# load menthol structure and make sure Uiso values are non-zero
if opts.pyobjcryst:
    # use pyobjcryst if requested by the user
    from pyobjcryst.crystal import CreateCrystalFromCIF
    from numpy import pi
    menthol = CreateCrystalFromCIF(open(mentholcif))
    for sc in menthol.GetScatteringComponentList():
        sp = sc.mpScattPow
        sp.Biso = sp.Biso or 8 * pi**2 * Uisodefault
else:
    # or use diffpy.Structure by default
    menthol = Structure(filename=mentholcif)
    for a in menthol:
        a.Uisoequiv = a.Uisoequiv or Uisodefault

# configuration of a PDF calculator
cfg = {
    'qmax': 25,
    'rmin': 0,
    'rmax': 30,
}
Ejemplo n.º 14
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 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
Ejemplo n.º 15
0
class StructureExt(object):

    _params = None
    _extparams = None

    def __init__(self, name='stru', filename=None, loadstype=None, periodic=None, optimizied=False, **kwargs):
        self.name = name
        self.rawstru = None
        self.rawstype = None
        self.stru = None
        self.periodic = periodic
        self.optimized = optimizied

        self.n = None
        self.element = None
        self.occ = None
        self.xyz = None
        self.xyz_c = None
        self.uij_c = None
        self._uiso = None
        self.anisotropy = None

        self.lat = None

        self._params = {}
        self._extparams = {}
        for k, v in kwargs.iteritems():
            setattr(self, k, v)
        
        if filename != None:
            self.loadStrufile(filename, loadstype, periodic)
        return

    def _getUiso(self):
        if self._uiso != None:
            rv = self._uiso
        else:
            rv = np.sum(self.uij_c, axis=(1, 2)) / 3
        return rv

    def _setUiso(self, value):
        self.anisotropy = np.ones(self.n, dtype=bool)
        self.uij_c = value.reshape(value.shape[0], 1, 1) * np.identity(3).reshape(1, 3, 3)
        self._uiso = value
        return

    uiso = property(_getUiso, _setUiso, "Uiso")

    def convertStru(self, stype='diffpy', mode='xyz', periodic=None):
        '''
        convert stru to stype
        '''
        if stype == 'diffpy':
            if self.rawstype == 'diffpy':
                rv = self.addProp(self.rawstru)
            else:
                rv = self.convertDiffpyStru(mode)
        elif stype == 'periodic':
            rv = self.convertPeriodicStru(mode)
        elif stype == 'objcryst':
            if self.rawstype == 'objcryst':
                rv = self.addProp(self.rawstru)
            else:
                rv = self.convertObjcrystStru(mode)
        elif stype == 'struext':
            rv = self
        else:
            raise TypeError('stype error')
        return rv

    def loadStrufile(self, filename, stype='diffpy', periodic=None):
        '''
        read and parse a structure file
        '''
        self.filename = filename
        ext = os.path.splitext(filename)[1]

        if periodic != None:
            self.periodic = periodic
        else:
            # detect periodicity using file type
            if ext in ['.cif']:
                periodic = True
            else:
                periodic = False
            self.periodic = periodic

        # read the file
        if stype == 'diffpy':
            self.rawstru = loadStructure(filename)
            self.rawstype = stype
            self.parseDiffpyStru(self.rawstru)
        elif stype == 'objcryst':
            if ext == '.cif':
                self.rawstru = CreateCrystalFromCIF(file(filename))
                self.rawstype = stype
            else:
                raise TypeError('Cannot read file!')
        else:
            raise TypeError('Cannot read file!')
        return

    def exportStru(self, filename, format='cif', stype='diffpy'):
        '''
        Save structure to file in the specified format
        
        :param filename: str, name of the file
        :param stype: str, the type of stru file to export
        
        :return: None

        Note: available structure formats can be obtained by:
            from Parsers import formats
        '''
        from diffpy.Structure.Parsers import getParser
        p = getParser(format)
        p.filename = filename
        stru = self.convertStru(stype)
        s = p.tostring(stru)
        f = open(filename, 'wb')
        f.write(s)
        f.close()
        return

    ### Tools ###

    def addProp(self, stru):
        '''
        add properties to the stru
        '''
        for par in ['name', '_params', '_extparams', 'periodic', 'optimized']:
            setattr(stru, par, getattr(self, par))
        stru.title = self.name
        stru.parent = self
        return stru

    ###########################################################
    # parse functions
    ###########################################################

    def parseDiffpyStru(self, stru=None):
        '''
        parse stru and store the information to self.xxx
        '''
        stru = self.rawstru if stru == None else stru

        n = len(stru)
        ulist = np.concatenate([0.001 * np.eye(3, 3) if (np.sum(u) == 0) else u for u in stru.U]).reshape(n, 3, 3)
        stru.U = ulist

        self.element = stru.element
        self.occ = stru.occupancy
        self.xyz = stru.xyz
        self.xyz_c = stru.xyz_cartn
        self.uij_c = stru.U
        self.anisotropy = stru.anisotropy
        self.n = len(self.anisotropy)

        self.lat = stru.lattice.abcABG()
        return

    def parseObjcrystStru(self, stru=None):
        '''
        parse stru and store the information to self.xxx
        FIXME: not complete
        '''
        # raise TypeError('parse a objcryst object is not reliable')
        stru = self.rawstru if stru == None else stru

        n = stru.GetNbScatterer()
        self.n = n

        lat = stru.GetLatticePar()
        self.lat = [lat[0], lat[1], lat[2], np.degrees(lat[3]), np.degrees(lat[4]), np.degrees(lat[5])]
        self.element = []
        self.occ = []
        self.xyz = []
        self.uij_c = []
        self.anisotropy = []
        for i in range(n):
            atom = stru.GetScatterer(i)
            st = atom.GetScatteringPower()
            self.element.append(st.GetSymbol())
            self.occ.append(atom.Occupancy)
            self.xyz.append([atom.X, atom.Y, atom.Z])
            bij_c = np.array([[st.B11, st.B12, st.B13],
                              [st.B12, st.B22, st.B12],
                              [st.B13, st.B12, st.B33]])
            biso = st.Biso
            if bij_c.sum() < 1.0e-10:
                self.anisotropy.append(False)
                self.uij_c.append(biso / np.pi ** 2 / 8 * np.identity(3))
            else:
                self.anisotropy.append(True)
                self.uij_c.append(bij_c / np.pi ** 2 / 8)

        self.xyz = np.array(self.xyz)
        self.occ = np.array(self.occ)
        self.uij_c = np.array(self.uij_c)

        sstru = self.convertDiffpyStru('xyz')
        self.parseDiffpyStru(sstru)
        return

    ###########################################################
    # convert functions
    ###########################################################

    def convertPeriodicStru(self, mode='xyz'):
        '''
        conver the self.xxx to PeriodicStructureAdapter
        
        :param mode: 'xyz' or 'xyz_c',
            'xyz': pass fractional xyz and covert to Cartesian xyz
            'xyz_c': pass Cartesian xyz directly 
        '''
        rv = PeriodicStructureAdapter()
        if mode == 'xyz':
            rv.setLatPar(*self.lat)

        del rv[:]
        rv.reserve(self.n)
        aa = AdapterAtom()
        for ele, occ, aniso in itertools.izip(self.element, self.occ, self.anisotropy):
            aa.atomtype = ele
            aa.occupancy = occ
            aa.anisotropy = bool(aniso)
            rv.append(aa)

        if mode == 'xyz':
            for a, xyz, uij_c in itertools.izip(rv, self.xyz, self.uij_c):
                a.xyz_cartn = xyz
                a.uij_cartn = uij_c
                rv.toCartesian(a)
        elif mode == 'xyz_c':
            for a, xyz_c, uij_c in itertools.izip(rv, self.xyz_c, self.uij_c):
                a.xyz_cartn = xyz_c
                a.uij_cartn = uij_c
        # if np.allclose(np.array(self.lat), np.array([1.0, 1.0, 1.0, 90.0, 90.0, 90.0])):
        #    rv = nosymmetry(rv)
        return self.addProp(rv)

    def convertDiffpyStru(self, mode='xyz'):
        '''
        convert self.xxx to diffpy
        
        :param mode: 'xyz' or 'xyz_c',
            'xyz': pass fractional xyz
            'xyz_c': pass Cartesian xyz directly
        '''
        rv = Structure()
        if mode == 'xyz':
            rv.lattice.setLatPar(*self.lat)

        aa = Atom()
        for i in range(self.n):
            rv.append(aa, copy=True)

        rv.element = self.element
        rv.occupancy = self.occ
        rv.anisotropy = self.anisotropy
        rv.U = self.uij_c
        if mode == 'xyz':
            rv.xyz = self.xyz
        elif mode == 'xyz_c':
            rv.xyz_cartn = self.xyz_c
        rv.title = self.name
        return self.addProp(rv)

    def convertObjcrystStru(self, mode='xyz_c'):
        '''
        convert self.xxx to objcryst object
        
        only applied to non-periodic structure
        '''
        if self.periodic:
            # raise TypeError('Cannot convert to periodic structure')
            if self.rawstype == 'diffpy':
                cif = self.rawstru.write('temp.cif', 'cif')
                objcryst = CreateCrystalFromCIF(file('temp.cif'))
                rv = objcryst
                os.remove('temp.cif')
        else:
            c = Crystal(1, 1, 1, "P1")
            c.SetName(self.name)
            m = Molecule(c, self.name)
            c.AddScatterer(m)

            for i in range(self.n):
                ele = self.element[i]
                sp = ScatteringPowerAtom(self.element[i], ele)
                if self.anisotropy[i]:
                    uij = self.uij_c[i]
                    sp.B11 = uij[0, 0]
                    sp.B22 = uij[1, 1]
                    sp.B33 = uij[2, 2]
                    sp.B12 = uij[0, 1]
                    sp.B13 = uij[0, 2]
                    sp.B23 = uij[1, 2]
                else:
                    biso = np.sum(self.uij_c[i].diagonal()) / 3 * (8 * np.pi ** 2)
                    sp.SetBiso(biso)
                if mode == 'xyz':
                    x, y, z = map(float, self.xyz[i])
                else:
                    x, y, z = map(float, self.xyz_c[i])
                a = m.AddAtom(x, y, z, sp, "%s%i" % (ele, i + 1))
                a.Occupancy = self.occ[i]
            rv = m
        return self.addProp(rv)

    def superCell(self, mno, stru=None, replace=False):
        from diffpy.Structure.expansion import supercell
        if stru == None:
            stru = self.convertDiffpyStru('xyz')
            newstru = supercell(stru, mno)

        if replace:
            self.rawstru = newstru
            self.rawstype = 'diffpy'
            self.parseDiffpyStru(newstru)
        return self.addProp(newstru)
Ejemplo n.º 16
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 = 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