The script looks like this. I am not allowed ot upload the notebook itself.
# Import the packages we'll need
%matplotlib notebook
from matplotlib.pyplot import *
import string
from collections import OrderedDict
import numpy as np
from pyobjcryst import loadCrystal
from diffpy.srfit.pdf import DebyePDFGenerator, PDFGenerator, PDFParser
from diffpy.srfit.fitbase import Profile
from diffpy.srfit.fitbase import FitContribution, FitRecipe
from diffpy.srfit.fitbase import FitResults, initializeRecipe
from diffpy.Structure import Structure
from diffpy.Structure import loadStructure
# Define functions for optimization and plotting that we will use later
def scipyOptimize(recipe):
from scipy.optimize.minpack import leastsq
print("Fit using scipy's LM optimizer")
leastsq(recipe.residual, recipe.getValues())
return
def plotRecipe(recipe, ax=None):
"""Plot recipe in the specified axes `ax`.
If `ax` is None, create a new figure.
Return `ax`
"""
if ax is None:
fig, ax = subplots()
r = recipe.pdf.profile.x
g = recipe.pdf.profile.y
gcalc = recipe.pdf.evaluate()
diffzero = -0.8 * max(g) * np.ones_like(g)
diff = g - gcalc + diffzero
ax.plot(r,g,'o',label="G(r) Data", mfc='none', mec='blue')
ax.plot(r, gcalc,'r-',label="G(r) Fit")
ax.plot(r,diff,'g-',label="G(r) diff")
ax.plot(r, diffzero,'k-')
ax.set_xlabel("$r (\AA)$")
ax.set_ylabel("$G (\AA^{-2})$")
ax.legend(loc=1)
return ax
# Import the data and make it a PDFprofile.
pdfprofile = Profile()
pdfparser = PDFParser()
pdfparser.parseFile(grdata)
pdfprofile.loadParsedData(pdfparser)
# Setup the PDFgenerator 1 that calculates the PDF from the CIF-file
pdfgenerator_crystal = PDFGenerator("G1")
pdfgenerator_crystal.setQmax(22.0)
pdfgenerator_crystal.setQmin(0.6)
# Load structure from the CIF file.
ciffile = "tet_ZrO2.cif"
crystal_structure = loadCrystal(ciffile)
pdfgenerator_crystal.setStructure(crystal_structure)
# Setup the PDFgenerator 2 that calculates the PDF from the xyz-file.
pdfgenerator_cluster = DebyePDFGenerator("G2")
pdfgenerator_cluster.setQmax(22.0)
pdfgenerator_cluster.setQmin(0.6)
# Load structure from the xyz-file
xyzfile = "ZrPO.xyz"
xyz_structure = loadStructure(xyzfile)
pdfgenerator_cluster.setStructure(xyz_structure, periodic = False)
# Add the profile and both generators to the PDFcontribution
pdfcontribution = FitContribution("pdf")
pdfcontribution.setProfile(pdfprofile, xname="r")
pdfcontribution.addProfileGenerator(pdfgenerator_crystal)
pdfcontribution.addProfileGenerator(pdfgenerator_cluster)
# Setup correction scaling due to finite spherical shape. SP diameter from PDFgui
from diffpy.srfit.pdf.characteristicfunctions import sphericalCF
pdfcontribution.registerFunction(sphericalCF, name="f1", argnames=['r', 'psize1'])
pdfcontribution.psize1 << 70;
# Use scaling factors proportional to molar content
pdfcontribution.setEquation('mc1*G1*f1 + mc2*G2')
# Define the recipe to do the fit and add it to the PDFcontribution
recipe = FitRecipe()
recipe.addContribution(pdfcontribution)
# Avoid too much output during fitting
recipe.clearFitHooks()
# Add the two scale factors.
recipe.addVar(pdfcontribution.mc1, 1.0, tag = "scale")
recipe.addVar(pdfcontribution.mc2, 1.0, tag = 'scale')
# set qdamp, qbroad for the instrument
qdamp = 0.04
qbroad = 0.01
# Add the instrumental parameters to the two generators
pdfgenerator_crystal.qdamp.value = qdamp
pdfgenerator_crystal.qbroad.value = qbroad
pdfgenerator_cluster.qdamp.value = qdamp
pdfgenerator_cluster.qbroad.value = qbroad
# Add the delta2 parameters for the two phases, and make sure they cannot take unphysical values
recipe.addVar(pdfgenerator_crystal.delta2, 2, name = "delta2_crystal", tag = "delta2")
#Start by have delta2 for the cluster 0
recipe.addVar(pdfgenerator_cluster.delta2, 0, name = "delta2_cluster", tag = "delta2")
# Uncommment below to contrain the delta2s to tke the same value
#recipe.constrain("delta2_crystal","delta2_cluster")
# Add the psize variable for diameter of the spherical nanoparticle.
recipe.addVar(pdfcontribution.psize1);
# Add the structural paramters using space group constaints for the crystal
# Ignore the ADP-s which will be set later.
phase_crystal = pdfgenerator_crystal.phase
sgpars_crystal = phase_crystal.sgpars
for par in sgpars_crystal.latpars:
recipe.addVar(par, name=
par.name+'_s', tag='cell')
for par in sgpars_crystal.xyzpars:
lclabel = par.par.obj.GetName().lower()
lcsymbol = lclabel.rstrip(string.digits)
# name the variables
tags = ['xyz', 'xyz_' + lclabel, 'xyz_' + lcsymbol]
recipe.addVar(par, name=name, tags=tags)
phase_crystal = pdfgenerator_crystal.phase
sgpars_crystal = phase_crystal.sgpars
# Use the same isotropic displacement parameters for all Zr in the crystal
initial_Biso_Zr = 1.0
recipe.newVar(name='Biso_Zr', value=initial_Biso_Zr, tags=['adp_zr'])
oxypars = [s for s in phase_crystal.scatterers if s.element.startswith('Zr')]
for a in oxypars:
recipe.constrain(a.Biso, 'Biso_Zr')
# Use the same isotropic displacement parameters for all oxygens
initial_Biso_O = 1.0
recipe.newVar(name='Biso_O', value=initial_Biso_O, tags=['adp_o'])
oxypars = [s for s in phase_crystal.scatterers if s.element.startswith('O')]
for a in oxypars:
recipe.constrain(a.Biso, 'Biso_O')
# Add ADP and "cell" for the cluster
phase_cluster = pdfgenerator_cluster.phase
atoms = phase_cluster.getScatterers()
#Add "zoomscale" to the cluster. Zoomscale1 is a factor that is multiplied on all x-coordinates and so on.
# Can change the distances between the atoms in the cluster
lat = phase_cluster.getLattice()
recipe.newVar("zoomscale1", 1.0, tag = "lat")
recipe.newVar("zoomscale2", 1.0, tag = "lat")
recipe.newVar("zoomscale3", 1,0, tag = "lat")
recipe.constrain(lat.a, 'zoomscale1')
recipe.constrain(lat.b, 'zoomscale2')
recipe.constrain(lat.c, 'zoomscale3')
# Set the ADP of the cluster atoms. For now the O and C have the same ADP
Zr_cluster = recipe.newVar("Zr_Biso_cluster", 0.5, tag = 'adp_zr')
O_cluster = recipe.newVar("O_Biso_cluster", 0.1, tag = 'adp_o')
for atom in atoms:
if atom.element.title() == "Zr":
recipe.constrain(atom.Biso, Zr_cluster)
elif atom.element.title() == "O" or "C":
recipe.constrain(atom.Biso, O_cluster)
# Uncomment the line below to contrain the O in the crystal and O in the cluster to be the same.
#recipe.constrain(O_cluster,'Biso_O_s')
pos_limit1 = 0.04
#Add refinable parameters for the Zr atoms in the xyz-structure
for atom in atoms:
if atom.element == 'Zr':
recipe.addVar(atom.x, name=
atom.name+'_x1', tag='xyz1')
recipe.addVar(atom.y, name=
atom.name+'_y1', tag='xyz1')
recipe.addVar(atom.z, name=
atom.name+'_z1', tag='xyz1')
#restrain atomic positions
lbx = atom.x.value-pos_limit1
ubx = atom.x.value+pos_limit1
recipe.restrain(atom.x, lb=lbx, ub = ubx, sig=0.001)
lby = atom.y.value-pos_limit1
uby = atom.y.value+pos_limit1
recipe.restrain(atom.y, lb=lby, ub = uby, sig=0.001)
lbz = atom.z.value-pos_limit1
ubz = atom.z.value+pos_limit1
recipe.restrain(atom.y, lb=lby, ub = uby, sig=0.001)
# Set the r-range to fit
pdfprofile.setCalculationRange(xmin = 1.5, xmax = 60)
# First refine only the scale
recipe.fix('all') # Fix the parameters written in the bracket, in this case all of them
recipe.free('scale', 'cell', 'lat') #Free the parameters with the tag 'scale' and 'xyz'
scipyOptimize(recipe) # Do the least-square refinement
recipe.free('adp_zr') #Free the parameters with the tag 'adp_zr'
scipyOptimize(recipe) # Do the least-square refinement
recipe.free('delta2_crystal') ##Free the parameter delta2_crystal
scipyOptimize(recipe) # Do the least-square refinement again with the extra freed parameter
recipe.free('psize1') ##Free the parameter psize1
scipyOptimize(recipe) # Do the least-square refinement again with the extra freed parameter
recipe.free('Biso_O') ##Free the parameter Biso_O
scipyOptimize(recipe) # Do the least-square refinement again with the extra freed parameter
recipe.free('xyz') ##Free the parameters with the tag 'xyz'
scipyOptimize(recipe) # Do the least-square refinement again with the extra freed parameter
print(FitResults(recipe)) # Print the results from the fit
plotRecipe(recipe); # Plot the fit
# Export the fit for plotting and plot the two contributions to the fit independently
r = recipe.pdf.profile.x
g = recipe.pdf.profile.y
gcalc = recipe.pdf.evaluate()
diffzero = -0.8 * max(g) * np.ones_like(g)
diff = g - gcalc + diffzero
g1 = recipe.pdf.evaluateEquation('mc1 * G1 * f1')
g2 = recipe.pdf.evaluateEquation('mc2 * G2*10')
#Do the actual plotting
fig, ax = subplots()
plotRecipe(recipe, ax);
ax.plot(r, g1+10, label='crystal')
ax.plot(r, g2+15, label='cluster')
ax.legend();