Re-calibration of a diffraction image with Jupyter

Jupyter notebooks (and jupyterlab) are standard tools to perfrom data analysis. While there are plenty of tutorial on the usage of pyFAI for azimuthal integration, few of then address the need for calculating the geometry of the experimental setup, also because the tool is not yet completely ready…

In this example we will perform the precise calibration of an image which setup is roughly known.

[1]:
%matplotlib inline
# use `widget` for better user experience; `inline` is for documentation generation

import time
from matplotlib.pyplot import subplots
from pyFAI.gui import jupyter
import pyFAI
import fabio
from pyFAI.test.utilstest import UtilsTest
from pyFAI.calibrant import CALIBRANT_FACTORY
from pyFAI.goniometer import SingleGeometry
print(f"Using pyFAI version: {pyFAI.version}")
start_time = time.perf_counter()
/home/jerome/.venv/py311/lib/python3.11/site-packages/pyopencl/cache.py:495: CompilerWarning: Non-empty compiler output encountered. Set the environment variable PYOPENCL_COMPILER_OUTPUT=1 to see more.
  _create_built_program_from_source_cached(
/home/jerome/.venv/py311/lib/python3.11/site-packages/pyopencl/cache.py:499: CompilerWarning: Non-empty compiler output encountered. Set the environment variable PYOPENCL_COMPILER_OUTPUT=1 to see more.
  prg.build(options_bytes, devices)
Using pyFAI version: 2023.9.0-dev0
[2]:
# In this example, we will re-use one of the image used int the test-suite
filename = UtilsTest.getimage("Pilatus1M.edf")
frame = fabio.open(filename).data
# and now display the image
ax = jupyter.display(frame)
../../../_images/usage_tutorial_Recalib_Recalib_notebook_2_0.png
[3]:
# This allow to measure approximatively the position of the beam center ...
x = 200 # x-coordinate of the beam-center in pixels
y = 300 # y-coordinate of the beam-center in pixels
d = 1600 # This is the distance in mm (unit used by Fit2d)
wl = 1e-10 # The wavelength is 1 Å
[4]:
# Definition of the detector and of the calibrant:
pilatus = pyFAI.detector_factory("Pilatus1M")
behenate = CALIBRANT_FACTORY("AgBh")
behenate.wavelength = wl
behenate
[4]:
AgBh Calibrant with 49 reflections at wavelength 1e-10
[5]:
# Set the guessed geometry
initial = pyFAI.geometry.Geometry(detector=pilatus, wavelength=wl)
initial.setFit2D(d,x,y)
initial
[5]:
Detector Pilatus 1M      PixelSize= 1.720e-04, 1.720e-04 m
Wavelength= 1.000000e-10 m
SampleDetDist= 1.600000e+00 m   PONI= 5.160000e-02, 3.440000e-02 m      rot1=0.000000  rot2=0.000000  rot3=0.000000 rad
DirectBeamDist= 1600.000 mm     Center: x=200.000, y=300.000 pix        Tilt= 0.000° tiltPlanRotation= 0.000° 𝛌= 1.000Å
[6]:
# The SingleGeometry object (from goniometer) allows to extract automatically ring and calibrate
sg = SingleGeometry("demo", frame, calibrant=behenate, detector=pilatus, geometry=initial)
sg.extract_cp(max_rings=5)
[6]:
ControlPoints instance containing 5 group of point:
AgBh Calibrant with 49 reflections at wavelength 1e-10
Containing 5 groups of points:
# a ring 0: 181 points
# b ring 1: 206 points
# c ring 2: 151 points
# d ring 3: 133 points
# e ring 4: 67 points
[7]:
#Control point and rings do not overlap well initially (this was a guessed geometry)
ax = jupyter.display(sg=sg)
../../../_images/usage_tutorial_Recalib_Recalib_notebook_7_0.png
[8]:
# Refine the geometry ... here in SAXS geometry, the rotation is fixed in orthogonal setup
sg.geometry_refinement.refine2(fix=["rot1", "rot2", "rot3", "wavelength"])
sg.get_ai()
[8]:
Detector Pilatus 1M      PixelSize= 1.720e-04, 1.720e-04 m
Wavelength= 1.000000e-10 m
SampleDetDist= 1.634725e+00 m   PONI= 4.543593e-02, 3.094243e-02 m      rot1=0.000000  rot2=0.000000  rot3=0.000000 rad
DirectBeamDist= 1634.725 mm     Center: x=179.898, y=264.162 pix        Tilt= 0.000° tiltPlanRotation= 0.000° 𝛌= 1.000Å
[9]:
ax = jupyter.display(sg=sg)
../../../_images/usage_tutorial_Recalib_Recalib_notebook_9_0.png
[10]:
#Save the geometry obtained
sg.geometry_refinement.save("geometry.poni")
with open("geometry.poni") as f:
    print(f.read())
# Nota: C-Order, 1 refers to the Y axis, 2 to the X axis
# Calibration done at Thu Sep  7 15:49:15 2023
poni_version: 2
Detector: Pilatus1M
Detector_config: {}
Distance: 1.6347250811613228
Poni1: 0.04543592596329322
Poni2: 0.03094243146297444
Rot1: 0.0
Rot2: 0.0
Rot3: 0
Wavelength: 1e-10

[11]:
#Use the geometry to perform an azimuthal integration
ai = sg.get_ai()
res = ai.integrate1d(frame, 1000)
ax = jupyter.plot1d(res,calibrant=behenate)

../../../_images/usage_tutorial_Recalib_Recalib_notebook_11_0.png

Conclusion

PyFAI still lacks some good integration into the Jupyter ecosystem, for example to draw a mask or pick control points, but it is nevertheless possible to calibrate an experimental setup when the approximate geometry is known.

[12]:
print(f"Execution time: {time.perf_counter()-start_time:.3f} s")
Execution time: 4.947 s