Flyscan dichroism#
A dichro flyscan measurement is a pair of scans taken at the two phase-plate
settings; the map of interest is the difference between them. polartools
covers this in three layers:
Module |
Role |
|---|---|
Reduce one scan: position stream → per-image |
|
Pair scans up, regrid them, difference, align, stitch and animate |
|
The |
Expected layout#
The reduction addresses files by scan number inside a sample folder:
<folder>/pos_stream/scan_000490.h5 interferometer positions and I0
<folder>/eiger/scan_000490.h5 area-detector frames
<folder>/vortex/scan_000490.h5 fluorescence spectra
That pattern is process_flyscan.FNAME_FORMAT; pass fname_format= to any of
the reduction functions if your directory is laid out differently.
The GUI#
flyscan-dichro-gui [--folder NdFeB] [--cr 490] [--cl 491] [--config FILE]
It loads two scans — circular-right and circular-left — draws their per-image
scalar maps side by side, and computes a (CL - CR) difference map
interpolated onto a common (x, y) grid. Either polarization can be disabled,
in which case only that map is drawn and the difference panel stays blank, so
the single-scan case is covered by the same window.
Eiger and Vortex each get a tab, with their own ROI and their own copy of the
three maps. --cr and --cl accept a single scan, a comma-separated list, or
a strided range: 490, 490,492,494, 754-764:2.
Settings#
Settings live in a JSON file rather than in QSettings, because the folder,
ROIs, scan numbers and colour limits worth keeping are the ones for the
analysis directory being worked in — a second experiment should not inherit
them.
The file is read at startup (./flyscan_gui_dichro.config.json unless
--config names another), but nothing is written until the Autosave button
in the status bar is pressed, so launching in a directory leaves no trace in
it. The destination can be changed while running; neither it nor the Autosave
state carries over to the next run, by design, so a session never writes
somewhere forgotten.
Scripting the same reduction#
Everything the GUI draws is available without it.
from polartools.process_flyscan import reduce_position_stream, process_images
folder = "/data/2026-3/NdFeB"
# Per-image positions and monitor, grouped by detector trigger.
groups = reduce_position_stream(490, folder)
groups.xs, groups.ys, groups.i0s # one value per image
# Eiger ROI sum, read in batches so a long scan doesn't land in memory at once.
roi_sum = process_images(490, folder, roi=((678, 888), (333, 480)), batch=100)
process_vortex does the same for a fluorescence ROI, and
read_scan_geometry returns the beam centre, wavelength and detector distance
recorded for the scan.
For the pair-wise work:
from polartools.dichro_maps import (
build_series,
find_pairs,
pair_difference,
plot_difference_series,
)
roi = ((678, 888), (333, 480))
# Consecutive scans sharing a label value but differing in phase plate,
# returned as [(label, scan_a, scan_b), ...] with scan_a the lower number.
pairs = find_pairs(range(490, 520), "magnet_field", folder)
label, scan_a, scan_b = pairs[0]
XG, YG, difference, mean = pair_difference(scan_a, scan_b, roi, folder)
# Every pair at once, ordered by the label rather than by scan number.
series = build_series(pairs, roi, folder)
plot_difference_series(series, "NdFeB dichroic difference")
dichro_maps also carries the corrections the raw maps need: align_series
undoes the xeryon stage runout row by row, stitch_x joins x-adjacent tiles
using each point only once, realign_pair re-derives a difference with the
two polarizations aligned, and write_gif animates a series.
Note
process_flyscan and dichro_maps read HDF5 files written with LZ4 and
bitshuffle compression. They import hdf5plugin at module level to register
those codecs — don’t remove that import, even though nothing references the
name afterwards.
See also#
Scan viewers — the step-scan browser and its image viewer
Image processing — ROI and curvature reduction for step scans