Using funcxy with Commonly-Used Diffraction Software
The general xy morph funcxy
can be used to tune parameters
of many popular diffraction software functions.
Below, we give templates for how one can use funcxy
with PDFgetx3
and PyFai.
Getting a Better PDF with PDFgetx3
In PDFgetx3, the PDFGetter
takes in a 1D diffraction
pattern I(Q) and returns a PDF G(r).
- There are many parameters you can specify, such as
qmin
: Lower Q-cutoff for the Fourier transform giving the PDFqmax
: Upper Q-cutoff for the Fourier transform giving the PDFqmaxinst
: Upper Q-boundary for meaningful signalrpoly
: Approximately the low-r bound of meaningful G(r) values
Furthermore, you can supply a background file backgroundfile
and subtract a scaled version of the background file by the
scaling factor bgscale
.
We will showcase an example of how one would refine over the
PDFGetter
parameters using funcxy
to obtain a PDF.
Let’s say you have a measured I(Q) with Q in angstroms of a lead
nanoparticle (composition PbS) named sample.chi
taken on a
glass background. We want to match a target calculated PDF G(r)
stored in a file named target.cgr
.
Let’s also say we have a measured I(Q) of the
glass background background.chi
.
from diffpy.pdfgetx.pdfgetter import PDFGetter
from diffpy.morph.morphpy import morph_arrays
from diffpy.utils.parsers.loaddata import loadData
pg = PDFGetter()
backgroundfile = loadData("background.chi")
composition = "PbS"
def wrap(x, y, **kwargs):
xy_out = pg.__call__(
x=x, y=y, dataformat="QA",
composition=composition,
backgroundfile=backgroundfile,
**kwargs
)
r = xy_out[0]
gr = xy_out[1]
return (r, gr)
sample_iq = loadData("sample.chi")
target_gr = loadData("target.cgr")
params_to_morph = {
"bgscale": 1.0,
"qmin": 0.0, "qmax": 25.0,
"qmaxinst": 25.0, "rpoly": 0.9
}
morph_info, morphed_gr = morph_arrays(
sample_iq, target_gr,
funcxy=(wrap, params_to_morph)
)
You can now plot morphed_gr
against your target_gr
to see
how well your morphing refinement of the PDF-getting parameters
as done!
To see what the refined values of the parameters are,
print out morph_info
.
You can freely add and remove entries in
params_to_morph
to include or not include them as
parameters to refine over.
If you expect to see thermal effect differences between your
measured PDF and target_gr
, you can also include
the stretch
, scale
, and smear
morphs in your
call to morph_arrays
.
Performing Detector Calibration with PyFai
When performing azimuthal integration, it is important to ensure your beam center and detector distances are calibrated. However, it is possible that they have shifted across measurements. Here, we will use morphing to the rescue!
Let’s say we just measured a diffraction pattern stored
as a NumPy object in diffraction_image.npy
, but some
of the detector geometries are off.
Our azimuthally integrated sample.chi
looks a bit off.
Before this measurement, you measured an amazing
I(Q) pattern target.chi
with a perfectly calibrated
sample-to-detector distance and beam center.
We will use morphing to try to match the integration of
the 2D pattern to the target 1D function.
For the integration, we will need some information, such as
the wavelength of the beam,
the size of each pixel in the 2D image
(pixel1
is the horizontal length in meters and
pixel2
is the vertical length in meters),
and a guess of the beam center.
This information can be found on the
PyFai documentation.
For our example, let’s say we have a 1024``x``1024
pixel image
where each pixel is a 100
micron by 100
micron region, and
our wavelength was 1.11
angstroms.
import numpy as np
import pyFAI.integrator.azimuthal as pyfai
import pyFAI.detectors as pfd
from diffpy.morph.morphpy import morph_arrays
from diffpy.utils.parsers.loaddata import loadData
pattern_2d = np.load("diffraction_image.npy")
wavelength = 0.1110e-9 # in m
pixel1 = 1e-4 # in m
pixel2 = 1e-4 # in m
cent_x = 511 # in number of pixels
cent_y = 511 # in number of pixels
ai = pyfai.AzimuthalIntegrator()
ai.wavelength = wavelength
detector = pfd.Detector()
detector.max_shape = pattern_2d.shape
def wrap(x, y, sample_to_detector_dist, cent_offset_x, cent_offset_y):
detector.pixel1 = pixel1
detector.pixel2 = pixel2
ai.detector = detector
ai.setFit2D(
directDist=sample_to_detector_dist,
centerX=cent_x+cent_offset_x,
centerY=cent_y+cent_offset_y
)
return ai.integrate1D_ng(
pattern_2d,
npt=1000, unit="q_A^-1",
method="mean"
)
params_to_morph = {
"sample_to_detector_dist": 60, # in mm
"cent_offset_x": 0, # in number of pixels
"cent_offset_y": 0 # in number of pixels
}
sample_chi = loadData("sample.chi")
target_chi = loadData("target.chi")
morph_info, morphed_chi = morph_arrays(
sample_chi, target_chi,
funcxy=(wrap, params_to_morph)
)
You can now plot morphed_chi
against your target_chi
to see if the refinement has helped in the calibration!
To see the calibrated values, you can print out morph_info
.
If you would like to morph over other PyFai parameters
(e.g. rot1
, tilt
, wavelength
),
you can adjust the wrapper function wrap
to take in
these parameters.