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
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
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
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, 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
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