Skip to content

Major rework based on Sympy and TEMGYM Core - #64

Merged
uellue merged 68 commits into
LiberTEM:mainfrom
uellue:sympysimplify
Oct 2, 2026
Merged

uellue merged 68 commits into
LiberTEM:mainfrom
uellue:sympysimplify

Conversation

@uellue

@uellue uellue commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Builds in large parts on #54

  • Strongly simplify and rework code structure -- "no stone left unturned" compared to version used in https://arxiv.org/abs/2403.08538v1
    • Functional programming style -- immutable values, pure functions
  • Support for descan error
  • Based on reworked TEMGYM core -- thx @DavidLanders95 and @matbryan52 , it is now MUCH better than the old TEMGYM for this purpose!
  • Completely reworked GUI based on LiberTEM-panel-ui
  • Core of the model working in Sympy, thx @Margus2004!
    • Quite a complex machinery to lambdify functions that accept data classes as arguments -- documentation and possibly spin-off TODO!
      • Inspect function arguments to obtain parameters
      • Create symbols for each field in a (JAX) dataclass, including recursion
      • Flatten, lambdify with SymPy, unflatten
  • Full transition to Sympy WIP, see Sympy uellue/Microscope-Calibration#2 for TODOs

uellue and others added 30 commits October 1, 2026 16:43
Basics already work; currently adding a ton of tests (WIP)
Simulate point charge displacement vector field in detector coordinates with model
and validate by guessing CoM parameters with `libertem.udf.com.guess_corrections()`
See LiberTEM/LiberTEM#1775 for discussion!

* Undo sign change between column and pixel y coordinates since that was
  ineffective.
* Add tests to confirm correct behavior of the model
* Add additional tests for CoM consistency
* Make inverted sign the expected behavior
Some functions in TemGymCore rely on `PixelsXY` and `CoordsYX` being arrays,
so we use newly introduced single types here
* Extract mapping matrix from model
* Fast forward simulation with that matrix
* Tests WIP
This allows to better simulate a real scan shift, rotation and scale where
the scan changes relative to the object. Previously, the scan coordinaates
vs. simulated object coordinates were fixed and always identical.

Fix handling of semiconv.

Introduce many tests, incl descan error moving aperture outlines.
Enable float64 everywhere and manually specify inverses for good measure
WIP, flake8 skipped
* Tests for correction and back-projetion
* Adapt OverfocusUDF to work again

FIXME: How to update the parameters of an UDF without changing it? Important to keep plots alive!
* Update UDF to use new functions
* Adapt notebooks to new parameters
* Update tests
* Simulation now includes semiconv, will be important for descan error optimization
The solver converges extremely quickly
The idea is a series of calibration functions to obtain
a fully calibrated parameter set from a number of test measurements.

* Wrapper function to run optax.lbfgs, which works EXTREMELY well on the model.
* High level functions that take meaningful input parameters and starting parameters,
  and return updated parameters. TODO perhaps include the residual in the return value?
* Descan error is WIP, will be based on measurements at different camera lengths.
* Overfocus, rotation and flip TBD, in particular relative to descan error.
One will first have to compensate descan error, and then
adjust other parameters such as flip_y, rotations etc.

This change starts adding functions that allow setting these parameters
while keeping the effect of descan error compensation wrt scan and
detector coordinates the same.

The transformations should be very fast for interactive use. For that reason
they are implemented directly instead of using the solver method.

TODO complete and add tests.
The `adjust_*()` methods allow first compensating the descan error
based on incorrect model parameters, and then adjusting other model
parameters while keeping the descan error compensated, i.e. they change
the descan parameters in such a way that their effect relative to
scan and detector pixel coordinates remains the same.

This is helpful since e.g. calibrating camera length based on known
diffraction angles may need descan compensation if position-averaged
diffraction patterns from a 4D STEM dataset are to be used, and coordinate system
calibration for scan step, scan rotation etc is easier as well if
descan error is removed first.

These methods are implemented as direct transformations rather than using a
solver since they need to be fast enough for immediate response in interactive use.
Also adding a trace() function to reduce boilerplate since we are
just interested in the tracing result from parameters in most cases.

Refactoring all tests WIP
Works in principle, but can confuse downstream users.

I guess it could be better to try preserve the original type of the
input parameter to the optimization routine, but later(TM).
Descan error correction for single camera length works!
uellue added 8 commits October 1, 2026 16:49
* Filter out reflections with low intensity from twothetas list
* Use adjustment instead of deriving in optimization routines to maintain descan error compensation
* Both point list and sharpness optimize detector rotation to keep scan rotation
  as a user-provided parameter
The large excitation error included weird duplicates

Also, consider that diffsims calculates theta, not twotheta

Add test case to check against literature values
* Add display of scale bar angle
* Add display of all model parameters
* Default model moved to Model4DSTEM since generally useful
* `CalibratedDataset` is a combination of dataset with model
* Diff operation for calibrated DM4 dataset
* Serialization and deserialization for calibrated DM4 dataset to allow checkpointing
  for complex analyses
* Correct Z direction (I assume longer CL when increasing Z means moving up)
* Introduce function and test to invert the overfocus to obtain
  the equivalent mapping from specimen to detector
At this point no reason to be backwards compatible IMO.
uellue added a commit to uellue/TemGymCore that referenced this pull request Oct 1, 2026
Chicken egg situation with LiberTEM/Microscope-Calibration#64
on the way.

Tests pass locally with this current version
TODO how to make the model and its parameters very lightweight while
also allowing to install all the heavy GUI etc?
@codecov

codecov Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.61207% with 75 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.79%. Comparing base (6a7292b) to head (67dcf20).

Files with missing lines Patch % Lines
src/microscope_calibration/util/sympy.py 82.17% 14 Missing and 9 partials ⚠️
src/microscope_calibration/common/model.py 90.67% 20 Missing and 2 partials ⚠️
src/microscope_calibration/ui.py 96.54% 10 Missing and 8 partials ⚠️
src/microscope_calibration/udf/stem_overfocus.py 92.50% 3 Missing and 3 partials ⚠️
...rc/microscope_calibration/common/stem_overfocus.py 97.20% 1 Missing and 3 partials ⚠️
.../microscope_calibration/util/stem_overfocus_sim.py 96.49% 1 Missing and 1 partial ⚠️

❗ There is a different number of reports uploaded between BASE (6a7292b) and HEAD (67dcf20). Click for more details.

HEAD has 6 uploads less than BASE
Flag BASE (6a7292b) HEAD (67dcf20)
10 4
Additional details and impacted files
@@             Coverage Diff             @@
##              main      #64      +/-   ##
===========================================
- Coverage   100.00%   94.79%   -5.21%     
===========================================
  Files            5        9       +4     
  Lines          190     1442    +1252     
  Branches        18      108      +90     
===========================================
+ Hits           190     1367    +1177     
- Misses           0       49      +49     
- Partials         0       26      +26     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@uellue

uellue commented Oct 1, 2026 •

Copy link
Copy Markdown
Member Author

Code coverage issue from two missing bits:

  • Exercise UI in tests
  • Run coverage with @jax.jit and @numba.njit deactivated

pre-commit-ci Bot and others added 8 commits October 2, 2026 12:19
Jax requires setting the float64 config early, and it is unclear how
early is early enough... To avoid complication between Flake8 and Ruff,
migrate everything to Ruff, which seems to support a superset of flake8.

* Migrate pre-commit to ruff, sunset flake8 and remove its config
* Also format with `ruff`, should make life easier in particular with agentic coding...
* Migrate `flake8` config to ruff as far as possible
* Apply new style everywhere
Makes life easier in IDEs -- mine seems to run both ruff and flake8...
Prominently, Widget.name -> Widget.label
This should boost coverage for stemoverfocus.py
Gotta pump these coverage numbers up...
...and also exercise the merge_all copied from PickUDF
@uellue

uellue commented Oct 2, 2026

Copy link
Copy Markdown
Member Author

Jax.jit was not harmful for coverage, it turns out.

@uellue
uellue merged commit 740de55 into LiberTEM:main Oct 2, 2026
6 of 8 checks passed
@uellue

uellue commented Oct 2, 2026

Copy link
Copy Markdown
Member Author

I'll just merge since this is essentially a rewrite of alpha code. We can rather review main...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants