The .geom parser now takes the masking information the format defines: - mask arrays: mask/mask_file/mask_good/mask_bad (mask 0) and maskN_data/maskN_file/maskN_goodbits/ maskN_badbits for N = 0..7; global keys hold for the panels after them, a panel may override. A mask that is the same on every panel is one whole-frame mask, otherwise one per panel, applied on that panel's pixels only. - a relative mask_file is looked for in the working directory (as CrystFEL does), then beside the .geom. - bad regions (bad*): a lab-plane box (min_x..max_y) or a fs/ss range on a panel (or, old syntax, on the whole frame); mask_edge_pixels and no_index. JFJochXFELReader already had the static pixel mask and the per-frame mask path for these; the masks go through them unchanged. A mask whose path holds % (one per shot group) is applied frame by frame like a 3D one. Where the geometry names mask arrays, the data file's own Cheetah masks (CXI mask_shared and per-shot mask with Cheetah's generic bad bits, /metadata/pixelmask) are not added: the geometry says which bits matter (e.g. an AGIPD mask_bad that includes bit 32). --mask-good / --mask-bad still add their masks to these. Tests: [CrystFELGeometry] (new keys, per-panel override, relative mask file), JFJochXFELReader_* (new GeometryMasks case on a synthetic CXI + .geom), [JFJochMultiReader], JFJochXTCReader*. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
159 lines
13 KiB
Markdown
159 lines
13 KiB
Markdown
# What Rugnux reads
|
|
|
|
Which data `rugnux` opens, before anything is typed. The short answer: an HDF5 master (NXmx or
|
|
DECTRIS, from any facility), a PILATUS miniCBF sweep, a marCCD sweep or an SMV sweep (ADSC or Rigaku d\*TREK) — nothing else
|
|
is read, so any other format has to be converted to one of these first.
|
|
|
|
**Input** is files, never a directory: an HDF5 master file, or one frame of a PILATUS miniCBF, marCCD
|
|
or SMV sweep. For rotation data one input is **one sweep of one crystal** — Rugnux does not combine sweeps or crystals in a run; process each
|
|
sweep to its own `_unmerged.mtz` and merge them downstream
|
|
(see [Taking the data onward](RUGNUX_INTEGRATION.md#taking-the-data-onward)).
|
|
|
|
Stills are given as all the files of the dataset, which the shell expands from a glob and which are
|
|
read in the order given: `rugnux run1_master.h5 run2_master.h5` (or `rugnux *_master.h5`) for
|
|
several Jungfraujoch/NXmx collections of one setup, `rugnux run*.h5` for SACLA, `rugnux *.cxi` for a
|
|
Cheetah CXI set and `rugnux *.xtc` for LCLS streams. Several files must all be of one format; a
|
|
directory is refused.
|
|
|
|
* **HDF5 master (NXmx-based)** — a file written by [`jfjoch_writer`](JFJOCH_WRITER.md), a DECTRIS
|
|
EIGER master, or an NXmx master written by another facility's toolchain. Lengths are taken in the
|
|
unit the file declares, the image size from the image array's own shape, and each data file's
|
|
images at the path the master's own link names, so masters that state single values as one-element
|
|
arrays, compose their images as a virtual dataset over the master itself, or keep them somewhere
|
|
other than `/entry/data/data` all open. Where the NXmx spellings are absent the pre-NXmx ones are
|
|
tried, so a DECTRIS firmware 1.x master opens too — including its goniometer, which used to be
|
|
missed and the sweep read as stills. Images may be `bitshuffle`+LZ4/Zstd or the HDF Group's plain
|
|
LZ4 (filter 32004); any other filter is named in the error.
|
|
* **PILATUS miniCBF sweep** — one frame per file, read natively with no conversion and no libcbf.
|
|
Name **any frame of the sweep** and the whole sweep is processed: the frames are the ones matching
|
|
that frame's template (prefix plus digit count), so a directory holding two sweeps is not spliced
|
|
into one crystal. The geometry, the rotation axis and the detector mounting come from the
|
|
header, including the imgCIF axis table where the header carries one (see
|
|
[Detector geometry](DETECTOR_GEOMETRY.md)). A raw CBF carries no analysis results, so
|
|
`--mode scale` — which re-scales the reflections stored in a `_process.h5` — does not accept one.
|
|
**Gzipped frames (`.cbf.gz`) are read directly**, with no decompression step: EMBL Hamburg's
|
|
beamlines write that form by default, and expanding a sweep first would double the disk it needs.
|
|
The two forms count as separate sweeps.
|
|
* **marCCD sweep** — what Rayonix MX-series and mar Mosaic detectors write, and what a decade of
|
|
deposited CCD data is archived as: an uncompressed TIFF with the instrument header in the gap
|
|
before the pixels. One frame per file, read natively. Naming a frame selects the
|
|
sweep exactly as for miniCBF, under either naming scheme these detectors use — a numbered stem
|
|
(`xtal_1_00042.mccd`) or the frame number as the file extension (`D1.042`). The distance, beam
|
|
centre, pixel size, wavelength and the circle that turned come from the header; the rotation
|
|
axis' direction does not, because the format has nowhere to state it, so the run settles its sign
|
|
from the data as it does for a miniCBF that carries no axis table. A CCD frame marks no untrusted
|
|
pixels, so the sweep starts with nothing masked. Like a raw CBF, it carries no analysis results
|
|
and `--mode scale` does not accept one.
|
|
* **SMV sweep** — what ADSC Quantum detectors wrote and what Rayonix and others still write, so most
|
|
archived CCD data from the 2000s is in this form: an ASCII `KEY=value;` block between braces, then
|
|
the pixels. One frame per file, read natively, the sweep selected exactly as above. The beam centre
|
|
it states is in **millimetres** and is converted here; its convention varies between writers, and a
|
|
file that states it transposed indexes nothing until the run's own beam-centre measurement replaces
|
|
it, which it does automatically. The saturation value is read where the header states one
|
|
(`CCD_IMAGE_SATURATION` or `SATURATED_VALUE`); where it does not, overloads are judged on the 16-bit
|
|
container alone and the reader says so once per sweep.
|
|
**Rigaku d\*TREK headers** — what Saturn CCDs on rotating-anode home sources write, recognised by
|
|
their `DETECTOR_NAMES` key — are read in their own vocabulary: the pixel size and the point of
|
|
normal incidence from the spatial-distortion record, the image directions from the detector and
|
|
distortion vectors (so a mirrored or turned image keeps its hand), the distance and the detector
|
|
circles, including a swung **2θ arm**, from the detector goniometer, the spindle and its angles from
|
|
the rotation record, and the wavelength from the scan record. Pixel values above 32767 are the
|
|
format's encoded overflows and are expanded with the header's compression ratio.
|
|
|
|
Support for the two CCD formats — marCCD and SMV — is **very limited and provided as-is**; they
|
|
are read so that a CCD sweep does not need a conversion step to be processed. Only the header
|
|
fields named above are read, and the headers vary by site and vintage, so no promise is made that
|
|
every writer's dialect opens. The HDF5 and miniCBF paths are the maintained ones.
|
|
|
|
### Serial XFEL inputs
|
|
|
|
Stills from the XFEL serial-crystallography pipelines are read straight from the files those
|
|
pipelines write, **without a geometry file**. Each reader takes from the file what it states; what
|
|
the file does not state is either measured by rugnux or given on the command line. Every run is read
|
|
at **one wavelength** (the mean of the per-shot photon energies where the file states them), the
|
|
**beam centre is measured** (the file's centre, the background-isotropy measurement and the
|
|
point-symmetry capture are indexed on a sample of frames and the best is kept), and a SACLA or CXI
|
|
file that states **no gain** is converted to photons with the gain its background shows (`--gain`
|
|
overrides; XTC frames stay in detector units unless `--gain` is given).
|
|
The saturation level is read off the frames where the file states none.
|
|
|
|
| Format | Read from the file | Measured by rugnux | Given on the command line |
|
|
|---|---|---|---|
|
|
| **Cheetah HDF5 with per-shot groups and a panel table** (`/tag-N/data`, sensors stacked in one frame, their placement in `/metadata` - as SACLA writes for the MPCCD) | the sensors' positions and in-plane angles, pixel size, nominal camera length and pixel mask (`/metadata`); each shot's photon energy | beam centre, gain, saturation; with a cell, the camera length (the stated one is nominal) and each sensor's placement | nothing; `--detector-distance` to give the camera length |
|
|
| **Cheetah CXI, single-panel detector** (`/entry_1/data_1/data`, square frames, e.g. a Rayonix at PAL-XFEL) | pixel size, photon energy per shot, camera length, `mask_shared` | beam centre, gain, saturation | `--detector-distance` where the stated camera length is wrong |
|
|
| **LCLS-I XTC, Rayonix** (Camera::FrameV1 events; one or more streams) | photon energy per event (electron-beam record), pixel size (Rayonix model and binning), the camera's pixel offset | beam centre, saturation | `--detector-distance` (not recorded in XTC); `--pixel-size`/`--wavelength` only where the XTC lacks them |
|
|
|
|
The SACLA sensor positions in `/metadata` are the detector's nominal metrology, a few pixels from a
|
|
per-beamtime refined geometry; the stills geometry refinement places each sensor as a module (see
|
|
[Module geometry](#module-geometry)).
|
|
|
|
What is **refused**, with a message rather than a wrong answer:
|
|
|
|
* a Cheetah CXI file of a **tiled detector** (CSPAD, AGIPD, ePix, JUNGFRAU, MPCCD stored as a slab of
|
|
panels): the file does not say where the panels lie;
|
|
* **multi-panel XTC** (CSPAD, ePix, JUNGFRAU at LCLS), stored raw with per-module calibration, and an XTC
|
|
stream holding images of the same size from two cameras;
|
|
* **unconverted detector data** in general (raw ADC values with gain stages and pedestals);
|
|
* **per-shot wavelengths**: they are averaged, never followed shot by shot;
|
|
* files of one run that store their frames differently (size, type, compression) or place the sensors
|
|
differently.
|
|
|
|
Damaged or compressed XTC images are left out, and so is a last event that a partly downloaded stream
|
|
ends inside. An XFEL dataset that has already been assembled into single images in an NXmx master
|
|
opens like any other HDF5 input.
|
|
|
|
`--crystfel-geometry <file>` places the panels of an HDF5/CXI input by a CrystFEL geometry file. Taken
|
|
from it are the module geometry (panel pixel ranges, fast/slow directions, corners, pixel size,
|
|
per-panel `coffset`, the frames' data path), optionally the camera length, and the pixels to leave out:
|
|
the mask arrays (`mask`, `mask_file`, `mask_good`, `mask_bad`, or up to eight as `maskN_data`,
|
|
`maskN_file`, `maskN_goodbits`, `maskN_badbits`; a panel may state its own), the bad regions (`bad*`,
|
|
a box in the laboratory plane or a pixel range on a panel), `mask_edge_pixels` and `no_index`. A mask
|
|
dataset with one plane per frame, or with `%` in its path, is applied frame by frame. A relative
|
|
`mask_file` is looked for in the working directory, as CrystFEL does, and then beside the geometry file.
|
|
Where the geometry names mask arrays, the data file's own (the CXI `mask_shared` and per-shot mask, a
|
|
panel table's `/metadata/pixelmask`) are not taken: the geometry says which bits matter. Everything else
|
|
it may hold (gain, saturation, photon energy, value flags) is ignored, with a note in the log. DIALS
|
|
`.expt` files are not read.
|
|
|
|
A mask array the data files do not hold themselves - e.g. a per-run pixel mask beside the frames - is
|
|
given with `--mask-good [<file>:]<dataset>[@<bits>]` (a pixel is usable only where the array is non-zero,
|
|
or has all of `<bits>`) or `--mask-bad [<file>:]<dataset>[@<bits>]` (unusable where non-zero, or where it
|
|
has any of `<bits>`, e.g. `@0xfeff`), in the frames' own layout, as stored. Without `<file>` the dataset is
|
|
read from each data file - a per-frame mask stored beside the frames. Both may be given more than once,
|
|
and add to the masks the data files or the geometry file state (the CXI `mask_shared` and per-shot mask,
|
|
the `/metadata/pixelmask` of a panel-table file, a `--crystfel-geometry` file's masks). A gain the background shows at or below one detector unit
|
|
per photon is not applied: such frames stay in detector units.
|
|
|
|
### Module geometry
|
|
|
|
A detector of several modules is described module by module: where each module's pixels are in the image
|
|
rugnux reads, and where they really are on the detector plane. The modules come from an explicit
|
|
geometry, never from the image:
|
|
|
|
* an NXmx file with one `NXdetector_module` group per module (`/entry/instrument/detector/module_0`,
|
|
`module_1`, ...: what a run writes as `<prefix>_modules.h5`; the masters Jungfraujoch writes still state
|
|
the image as one module);
|
|
* the panels the XFEL readers know (the SACLA sensors from `/metadata`, the panels of a
|
|
`--crystfel-geometry` file); each panel is put on the image whole, at a whole pixel, and its fraction of
|
|
a pixel and small turn are its placement;
|
|
* `--module-geometry <file>`: an NXmx file with such groups (what a run writes as `<prefix>_modules.h5`)
|
|
or a CrystFEL geometry file whose panels are laid on the image (`fs` = column, `ss` = row, corners
|
|
relative to the beam). It replaces the input's own modules - the way to give the module layout of a
|
|
master written before the modules were recorded.
|
|
|
|
Spots are mapped from the image onto the plane, and predictions from the plane back onto the image for
|
|
integration, so the image itself is never re-assembled. With a cell given, the stills geometry refinement
|
|
places each module with enough spots on its frames' lattices as a rigid block (shift and in-plane turn,
|
|
`--refine-modules`, on by default) where that predicts held-out spots better; modules move only relative
|
|
to one another, the beam centre, distance and crystal orientations carrying what all of them share. The
|
|
placement is used from there on for the panels of a foreign multi-panel detector (the sensors of a
|
|
panel-table file, the panels of a `--crystfel-geometry` file); for a JUNGFRAU (an NXmx master) it is
|
|
measured and reported only. Either way it is written to `<prefix>_modules.h5`, which a later run reads
|
|
back with `--module-geometry`.
|
|
|
|
Spots are always found by `rugnux` itself, including for the two-pass rotation first pass — the
|
|
spot lists a dataset may already carry were found online, at the acquisition's threshold and with
|
|
its ice-band spots already discarded, so reusing them would hide the spot-finding settings from
|
|
the lattice search.
|
|
|