Tools to calibrate a (scanning) transmission electron microscope from data, with a focus on 4D-STEM. The package recovers the geometric calibration of a 4D-STEM experiment — scan and detector rotation, handedness (flip), camera length, scan pixel pitch, overfocus and descan error — and uses it to correct and reconstruct datasets.
The scientific background is described in the preprint https://arxiv.org/abs/2403.08538.
Status: alpha, under active development. The API may change without notice.
Model4DSTEM(microscope_calibration.common.model) holds the full geometric calibration of a 4D-STEM experiment and traces rays through it using TemGymCore. The model is written for SymPy, which gives exact symbolic solutions.Model4DSTEM.lambdify(modules)derives versions for NumPy and JAX (automatic differentiation, JIT) from it, so it can be used to reason about ray paths and to build fast solvers and optimizers.- Optimizers (
microscope_calibration.util.optimize) fit calibration parameters such as camera length, scan pixel pitch and descan error to reference data. - LiberTEM UDFs (
microscope_calibration.udf) apply a calibration to real datasets, e.g. to reconstruct overfocused 4D-STEM data. - Simulator (
microscope_calibration.util.stem_overfocus_sim) generates synthetic overfocused 4D-STEM data from a model. - Interactive GUI (
microscope_calibration.ui) to adjust a calibration against a displayed dataset, optionally with diffraction rings computed from a CIF crystal structure.
Requires Python >= 3.14. The package is not on PyPI yet. Install it from GitHub with all optional features:
pip install "microscope-calibration[common,diffraction,gui] @ git+https://github.com/LiberTEM/Microscope-Calibration"To install a specific release, append @<tag> to the URL.
The core dependencies only cover Model4DSTEM. Optional extras:
common: LiberTEM, Numba, SciPy, optimizers etc. for UDFs, simulator and fittingdiffraction: crystallography support to compute diffraction angles from CIF filesgui: the interactive calibration interfacetest: test dependencies
The notebooks in examples/ are currently the best documentation:
model.ipynb: basic use ofModel4DSTEMto reason about ray paths, with exact symbolic (SymPy) and numerical (JAX + Optimistix) solutions.numeric.ipynb: numeric versions ofModel4DSTEMfor NumPy and JAX withModel4DSTEM.lambdify(), including broadcasting and derivatives.generate.ipynb: simulate an overfocused 4D-STEM dataset.stem_overfocus.ipynb: interactive calibration of a series of real datasets, including descan error, camera length using diffraction rings, and overfocus. The data will be published on Zenodo.
Install from a clone of the repository in editable mode:
git clone https://github.com/LiberTEM/Microscope-Calibration
cd Microscope-Calibration
pip install -e .[test,common,diffraction,gui]
pytest tests/
pre-commit run --all-filesThe pre-commit hooks include a pyright type check that uses the active environment, so install the package as above before running them.
Some functions are derived symbolically from Model4DSTEM.trace() with SymPy, for example how
the descan error has to change in the Model4DSTEM.adjust_*() methods to keep its effect in
pixel coordinates constant, and the ray transfer matrix that the transformation matrices for
projection and detector correction are calculated from. This keeps trace() the single source
of truth for the model.
Since the derivation takes a few seconds per function, the result is rendered as plain Python
code into src/microscope_calibration/common/_generated.py and committed to the repository.
Don't edit that file by hand.
The functions to generate are registered in GENERATED_FUNCTIONS of the modules that use
them, e.g. src/microscope_calibration/common/model.py, and collected in
src/microscope_calibration/common/generate.py. After changing the model or a registry,
regenerate the file and commit the result:
python -m microscope_calibration.common.generatetests/test_codegen.py checks that the committed code is up to date by repeating the
derivation and comparing the results symbolically:
pytest tests/test_codegen.pyThe generated code is only valid for Model4DSTEM itself. Subclasses derive these functions
from their own trace() at runtime instead, with a RuntimeDerivationWarning. Please read the
docstring of Model4DSTEM before subclassing it. It explains the requirements and how a
subclass can use the existing generated code or its own.
JAX uses float32 by default. Its precision is not sufficient for electron optics, which
combines lengths in the picometer range, such as the electron wavelength, with lengths in the
meter range, such as the camera length. This package activates float64 on import with
jax.config.update("jax_enable_x64", True). If you use JAX yourself, activate float64 the same
way before any other JAX code runs, and don't deactivate it. The solvers in
microscope_calibration.util.optimize check this and raise an error otherwise. You can check
it yourself with microscope_calibration.util.optimize.check_float64().
If you use this software, please cite it via its Zenodo record (DOI to be added on first release) and the preprint https://arxiv.org/abs/2403.08538. Citation metadata is available in CITATION.cff.
GPL-3.0, see LICENSE.
An earlier, much simpler version of this package accompanied the preprint https://arxiv.org/abs/2403.08538 and was deposited at https://doi.org/10.5281/zenodo.10418769. The current code is a substantial rewrite and is not compatible with that version.
Changes since that deposition, before the rewrite:
- Fixed definition of camera length in the simulator to match the figure in https://arxiv.org/pdf/2403.08538.pdf, PR #17. Previously, the camera length was defined from the focus point, not the specimen plane. See also TemGym/TemGym#33 for the corresponding update in TemGym. Note that the TemGym model used for calculation was correct, only the alternative manual ray tracing implementation was affected.