Calibration of a diffraction setup using Jupyter notebooks

This notebook presents a very simple GUI for doing the calibration of diffraction setup within the Jupyter environment with Matplotlib and Ipywidgets.

Despite this is in the cookbook section, this tutorial requires advanced Python programming knowledge and some good understanding of PyFAI.

The basic idea is to port, more or less directly, the pyFAI-calib interface which was done with matplotlib into the Jupyter notebook.

The PeakPicker class has been addapted and this notebook presents mainly its usage.

Most credits go Philipp Hans for the adaptation of the origin PeakPicker class to Jupyter.

[1]:
%matplotlib nbagg
import pyFAI
import logging

import pyFAI.test.utilstest
import fabio
from matplotlib.pyplot import subplots
from ipywidgets import *
from pyFAI.gui import jupyter
from pyFAI.gui.jupyter.peak_picker import PeakPicker
from pyFAI.geometryRefinement import GeometryRefinement
logging.basicConfig(level=logging.INFO)
print(f"PyFAI version {pyFAI.version}")
WARNING:pyFAI.gui.matplotlib:matplotlib already loaded, setting its backend may not work
PyFAI version 0.21.0-dev0
[2]:
# Some parameters like the wavelength, the calibrant and the diffraction image (downloaded from internet)
wavelength = 1e-10
pilatus = pyFAI.detector_factory("Pilatus1M")
AgBh = pyFAI.calibrant.CALIBRANT_FACTORY("AgBh")
AgBh.wavelength = wavelength

img = pyFAI.test.utilstest.UtilsTest.getimage("Pilatus1M.edf")
fimg = fabio.open(img)
image = fimg.data
[3]:
#Simple display of the image:
_ = jupyter.display(image)
[4]:
#PeakPicker is used in the same way as the one in the CLI.
pp = PeakPicker(data=fimg.data, calibrant=AgBh, wavelength=wavelength, detector=pilatus)
pp.gui(log=True)
[5]:
# Display a summary of control points
pp.points
[5]:
ControlPoints instance containing 2 group of point:
AgBh Calibrant with 49 reflections at wavelength 1e-10
Containing 2 groups of points:
# a ring 0: 47 points
# b ring 1: 56 points
[6]:
gr = GeometryRefinement(pp.points.getList(),
                        detector=pilatus,
                        calibrant=AgBh,
                        wavelength=wavelength)
print("Before refinement: parameters are guessed from the ellipse fitting")
print(gr)
print(f"Cost={gr.chi2()}")
Before refinement: parameters are guessed from the ellipse fitting
Detector Pilatus 1M      PixelSize= 1.720e-04, 1.720e-04 m
Wavelength= 1.000000e-10m
SampleDetDist= 1.626147e+00m    PONI= 2.708786e-02, 3.348170e-02m       rot1=0.001543  rot2= 0.011011  rot3= 0.000000 rad
DirectBeamDist= 1626.248mm      Center: x=180.074, y=261.592 pix        Tilt=0.637 deg  tiltPlanRotation= 97.976 deg
Cost=9.887803776029084e-06
[7]:
# Reset the detector to be normal to the incident beam
gr.rot1=0
gr.rot2=0
gr.rot3=0
print(gr)
print(f"Cost={gr.chi2()}")
Detector Pilatus 1M      PixelSize= 1.720e-04, 1.720e-04 m
Wavelength= 1.000000e-10m
SampleDetDist= 1.626147e+00m    PONI= 2.708786e-02, 3.348170e-02m       rot1=0.000000  rot2= 0.000000  rot3= 0.000000 rad
DirectBeamDist= 1626.147mm      Center: x=194.661, y=157.488 pix        Tilt=0.000 deg  tiltPlanRotation= 0.000 deg
Cost=0.00880358236262962
[8]:
# Geometry refinement with some constrains
gr.refine3(fix=["wavelength", "rot1", "rot2", "rot3"])
print(gr)
print(f"Cost={gr.chi2()}")
Detector Pilatus 1M      PixelSize= 1.720e-04, 1.720e-04 m
Wavelength= 1.000000e-10m
SampleDetDist= 1.634709e+00m    PONI= 4.543052e-02, 3.095243e-02m       rot1=0.000000  rot2= 0.000000  rot3= 0.000000 rad
DirectBeamDist= 1634.709mm      Center: x=179.956, y=264.131 pix        Tilt=0.000 deg  tiltPlanRotation= 0.000 deg
Cost=9.301553574504124e-08
[9]:
# Validate the calibration by overlaying the ring position in Figure #2
pp.contour(gr.array_from_unit(scale=False))
Visually check that the overlaid dashed curve on the Debye-Sherrer rings of the image
Check also for correct indexing of rings
[10]:
gr.save("jupyter.poni")
gr.get_config()
[10]:
OrderedDict([('poni_version', 2),
             ('detector', 'Pilatus1M'),
             ('detector_config', OrderedDict()),
             ('dist', 1.6347089410819242),
             ('poni1', 0.04543052458127472),
             ('poni2', 0.030952434520236285),
             ('rot1', 0.0),
             ('rot2', 0.0),
             ('rot3', 0.0),
             ('wavelength', 1e-10)])
[11]:
ai = pyFAI.load(gr)
ai
[11]:
Detector Pilatus 1M  PixelSize= 1.720e-04, 1.720e-04 m
Wavelength= 1.000000e-10m
SampleDetDist= 1.634709e+00m        PONI= 4.543052e-02, 3.095243e-02m       rot1=0.000000  rot2= 0.000000  rot3= 0.000000 rad
DirectBeamDist= 1634.709mm  Center: x=179.956, y=264.131 pix        Tilt=0.000 deg  tiltPlanRotation= 0.000 deg

Conclusion

This short notebook shows how to interact with a calibration image to pick some control-point from the Debye-Scherrer ring and to perform the calibration of the experimental setup.