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 = loadCrystal(cdsciffile) stru2 = loadCrystal(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
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 = loadCrystal(cdsciffile) stru2 = loadCrystal(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
def to_crystal(dct: dict, keys=("genresults", 0, "stru_str")) -> Crystal: """Load the information in the dictionary to a crystasl object. The info is a string of cif file.""" with TemporaryDirectory() as temp_dir: cif_file = Path(temp_dir) / "temp.cif" cif_file.write_text(get_value(dct, keys)) crystal = loadCrystal(str(cif_file)) return crystal
def test_isotropy(self): """check PDFRecipeFactory.make() with `isotropy` option. """ crst = loadCrystal(self.cifbtoc) self.factory.isotropy = True recipe = self.factory.make(crst, self.r, self.g) bnames = [n for n in recipe.names if n.startswith('B')] self.assertEqual(3, len(bnames)) self.assertTrue(all(n.startswith('Biso') for n in bnames)) self.assertEqual(self.factory.fbbiso, recipe.Biso_0.value) return
def test_nyquist(self): """check PDFRecipeFactory.make() with `nyquist` option. """ crst = loadCrystal(self.cifbtoc) self.factory.nyquist = True self.assertRaises(ValueError, self.factory.make, crst, self.r, self.g) meta = dict(qmax=26) recnq = self.factory.make(crst, self.r, self.g, meta=meta) rgrid = recnq.cpdf.r.value self.assertTrue(rgrid[1] - rgrid[0] > 0.1) return
def test_biso_fallback(self): """check if Biso fallback in PDFRecipeFactory is applied. """ self.factory.fbbiso = 0.37 crst = loadCrystal(self.cifbtoc) # zero all B values in the model spreg = crst.GetScatteringPowerRegistry() for i in range(spreg.GetNb()): sp = spreg.GetObj(i) sp.B11 = sp.B22 = sp.B33 = 0.0 recipe = self.factory.make(crst, self.r, self.g) spba = spreg.GetObj(0) fbbiso = self.factory.fbbiso self.assertEqual(fbbiso, spba.Biso) spo = spreg.GetObj(2) self.assertEqual(fbbiso, spo.B11) self.assertEqual(fbbiso, spo.B22) self.assertEqual(fbbiso, spo.B33) return
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 = loadCrystal(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 = loadCrystal(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 test_bto_tetragonal(self): """check standard factory configuration on tetragonal BaTiO3. """ crst = loadCrystal(self.cifbtot) meta = dict(qmax=26) recipe = self.factory.make(crst, self.r, self.g, meta=meta) # count cell-parameter variables recipe.fix('all') recipe.free('lattice') self.assertEqual(['a', 'c'], recipe.names) # count position-related variables recipe.fix('all') recipe.free('positions') self.assertEqual(4, len(recipe.names)) self.assertTrue(all(n.startswith('z') for n in recipe.names)) # count ADP-related variables recipe.fix('all') recipe.free('adps') self.assertEqual(9, len(recipe.names)) return
def test_bto_cubic(self): """check standard factory configuration on cubic BaTiO3. """ crst = loadCrystal(self.cifbtoc) meta = dict(qmax=26, stype='N') recipe = self.factory.make(crst, self.r, self.g, meta=meta) self.assertEqual(1, recipe.cpdf.r.value[0]) self.assertEqual(20, recipe.cpdf.r.value[-1]) # check the effect of meta entries calc = recipe.cpdf.cif._calc self.assertEqual(26, calc.qmax) self.assertEqual('N', calc.scatteringfactortable.radiationType()) # should have 4 parameters for B factors, 2 are isotropic bnames = [n for n in recipe.names if n.startswith('B')] self.assertEqual(4, len(bnames)) self.assertEqual(2, len([n for n in bnames if n.startswith('Biso')])) # test type checking stru = loadStructure(self.cifbtoc) self.assertRaises(TypeError, self.factory.make, stru, self.r, self.g, meta) return
def loadcifdata(filename): from pyobjcryst import loadCrystal fullpath = datafile(filename) crst = loadCrystal(fullpath) return crst
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
def get_pyobjcryst_sphalerite(): from pyobjcryst import loadCrystal crst = loadCrystal('datafiles/sphalerite.cif') return crst
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
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 = loadCrystal(niciffile) contribution.addStructure("ni", stru) stru = loadCrystal(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
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 = loadCrystal(niciffile) generator_ni.setStructure(stru) # The generator for the silicon phase. We call it "G_si". generator_si = PDFGenerator("G_si") stru = loadCrystal(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
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 = loadCrystal(niciffile) contribution.addStructure("ni", stru) stru = loadCrystal(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
import numpy as np from pyobjcryst import loadCrystal from diffpy.srfit.pdf import PDFContribution from diffpy.srfit.fitbase import Profile, FitRecipe, FitResults nphcrystal = loadCrystal('naphthalene.cif') pdfcntb = PDFContribution('pdfcntb') pdfcntb.loadData('naphthalene.gr') pdfcntb.qdamp = 0.06 pdfcntb.setCalculationRange(1.1, 25) pdfcntb.addStructure('nph', nphcrystal) nphfit = FitRecipe() nphfit.clearFitHooks() nphfit.addContribution(pdfcntb) nphfit.addVar(pdfcntb.scale, name='scale') nphfit.addVar(pdfcntb.nph.delta2, value=1.0) nphase = pdfcntb.nph.phase # unit cell parameters nphfit.addVar(nphase.a) nphfit.addVar(nphase.b) nphfit.addVar(nphase.c) # cell-angle beta is in radians in ObjCryst Crystal # we will refine angle in degrees. nphfit.newVar('beta', value=np.degrees(nphase.beta.value)) nphfit.constrain(nphase.beta, 'radians(beta)') # all carbon species have the same displacement parameter, # it is sufficient to add constraint for the C1 atom
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 import loadCrystal from numpy import pi menthol = loadCrystal(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, }
NI_IMG = load_img(NI_IMG_FILE) KAPTON_IMG = load_img(KAPTON_IMG_FILE) NI_GR = load_array(NI_GR_FILE) NI_CHI = load_array(NI_CHI_FILE) NI_FGR = load_array(NI_FGR_FILE) NI_CONFIG = PDFConfig() NI_CONFIG.readConfig(NI_GR_FILE) NI_PDFGETTER = PDFGetter(NI_CONFIG) AI = pyFAI.load(NI_PONI_FILE) MASK = numpy.load(MASK_FILE) BLACK_IMG = load_img(BLACK_IMG_FILE) WHITE_IMG = load_img(WHITE_IMG_FILE) # model file ZRP_CIF_FILE = resource_filename('tests', 'test_data/ZrP_cif_file.cif') NI_CIF_FILE = resource_filename("tests", "test_data/Ni_cif_file.cif") NI_CRYSTAL = loadCrystal(NI_CIF_FILE) ZRP_CRYSTAL = loadCrystal(ZRP_CIF_FILE) NI_DIFFPY = loadStructure(NI_CIF_FILE) NI_MOLECULE = Molecule(NI_CRYSTAL) DB = { 'Ni_img_file': NI_IMG_FILE, 'Ni_img': NI_IMG, 'Kapton_img_file': KAPTON_IMG_FILE, 'Kapton_img': KAPTON_IMG, 'Ni_poni_file': NI_PONI_FILE, 'Ni_gr_file': NI_GR_FILE, 'Ni_chi_file': NI_CHI_FILE, 'Ni_fgr_file': NI_FGR_FILE, 'ai': AI, 'Ni_gr': NI_GR,
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
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 = loadCrystal(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, 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 = loadCrystal(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
from pdfstream.io import load_img, load_data NI_PONI = resource_filename('tests', 'test_data/Ni_poni_file.poni') NI_GR = resource_filename('tests', 'test_data/Ni_gr_file.gr') NI_CHI = resource_filename('tests', 'test_data/Ni_chi_file.chi') NI_FGR = resource_filename('tests', 'test_data/Ni_fgr_file.fgr') NI_IMG = resource_filename('tests', 'test_data/Ni_img_file.tiff') NI_CIF = resource_filename('tests', 'test_data/Ni_cif_file.cif') KAPTON_IMG = resource_filename('tests', 'test_data/Kapton_img_file.tiff') BLACK_IMG = resource_filename('tests', 'test_data/black_img.tiff') WHITE_IMG = resource_filename('tests', 'test_data/white_img.tiff') NI_CONFIG = PDFConfig() NI_CONFIG.readConfig(NI_GR) NI_PDFGETTER = PDFGetter(NI_CONFIG) ZRP_CIF = resource_filename('tests', 'test_data/ZrP.cif') NI_CRYSTAL = loadCrystal(NI_CIF) ZRP_CRYSTAL = loadCrystal(ZRP_CIF) NI_DIFFPY = loadStructure(NI_CIF) DB = { 'Ni_img_file': NI_IMG, 'Ni_img': load_img(NI_IMG), 'Kapton_img_file': KAPTON_IMG, 'Kapton_img': load_img(KAPTON_IMG), 'Ni_poni_file': NI_PONI, 'Ni_gr_file': NI_GR, 'Ni_chi_file': NI_CHI, 'Ni_fgr_file': NI_FGR, 'ai': pyFAI.load(NI_PONI), 'Ni_gr': load_data(NI_GR).T, 'Ni_chi': load_data(NI_CHI).T,
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