Getting Started with probixi¶
Getting started with probixi is hopefully quite easy. The primary ethos of this package is to have statistics handle the work for you; therefore, all you need to provide is your list of files, detector geometry, and your unit cell.
Let’s move through a basic tutorial using the CLI, or skip ahead to Using probixi with Python.
probixi via the CLI¶
Going through the required options:
-i provides the list of files (hdf5/cxi)
-g provides the geometry
-p provides the unit cell
-o provides the output (and inferred format). Use
.streamto generate a crystfel style stream or.dbto generate a duckdb.
probixi -i files.lst -g my_geometry.geom -p my_unit.cell -o output.db --device cuda
And that’s it! There are a few more advanced parameters provided in detail below:
Parameter |
Accepts |
Description |
|---|---|---|
–batch-size |
int |
Frames per batched refinement pass |
–device |
str |
Torch device (e.g. cuda, cpu, mps) |
–devices |
str |
Comma-separated device list for multi-gpu indexing |
–enrich-alpha |
float |
Max probability to accept a frame under –enrich-gate |
–enrich-gate |
NA (flag) |
Drop indexed frames whose predicted spots are not backed by image signal beyond chance - better for high fluence and good diffraction |
–flux-var-floor |
float |
Variance floor as a fraction of the calibrated per-pixel variance, applied under |
–flux-variance |
NA (flag) |
Fit a photon-transfer curve during calibration and whiten each pixel against its own Poisson noise, instead of a frozen variance floor. Intended for XFEL/SFX or jet-intensity-variable data |
–gif |
str/Path |
Returns a gif of the noise model evolution over the seed frames |
–gpus |
int |
Multi-gpu indexing across first N devices (the same as –devices cuda:0,cuda:1…) |
–noise-mode |
str |
|
–panel |
str |
Fallback panel name for peaks that fall outside every geometry panel (inter-panel gaps, or a geometry with no named panels). Peaks inside a defined panel already carry that panel’s name; this only labels the rest. Default |
–peaks-only |
NA (flag) |
Returns crystfel readable .cxi peaks, or a duckdb, depending on |
-q/–quiet |
NA (flag) |
Suppress logger lines |
–render |
str/int |
Recall a frame and write a peaks/index overlay. Either index or ‘image_filename//event’ |
–render-out |
str/Path |
|
–seed-frames |
int |
Number of frames used to calibrate the noise model and detection threshold |
–start |
int |
First frame index (inclusive) |
–stop |
int |
Stop frame index (exclusive) |
–target-noise-peaks |
float |
Auto-calibrate the peak-detection threshold so a signal-free frame yields at most this many spurious noise blobs (default 5). Lower → stricter/cleaner peaks; higher → more sensitive. Acts as an override |
–threads-per-worker |
int |
Threads per GPU worker |
–warmup-frames |
int |
Number of frames observed before learned dead-pixel mask is built |
Recovering Results¶
Given your output options, a .stream or .db file has been generated. The stream is analagous to any other crystfel based stream.
NOTE: As this package evolves, the stream option may be dropped or deprecated in favor of database or straight-to-
.hklmethods. The crystfel stream option exists currently to compare with the method library crystfel contains.
Database¶
Output to a database allows for faster parsing to other libraries, as well as semi-archival storage of all data used in the process. The .db (or .duckdb) output is a DuckDB file written by IndexStream.to_db (via DuckDBOffloader), and it is a relational alternative to the CrystFEL .stream: the run’s metadata, every frame’s per-frame statistics, and the integrated reflections and searched peaks all live in one queryable file.
It is organized into six tables. Three small metadata tables are written once, up front:
Table |
Rows |
What it stores |
|---|---|---|
|
one |
The resolved detector geometry: |
|
one per panel |
Each panel’s |
|
one |
The target unit cell: edges |
The remaining three per-frame tables hold the results, linked by frame_id — a 16-character SHA-1 hash of filename//event that uniquely identifies each frame:
Table |
Rows |
What it stores |
|---|---|---|
|
one per file-event |
The row for every frame, keyed by |
|
one per integrated reflection |
The indexed, box-integrated reflections keyed by |
|
one per searched peak |
The peaks found by the peak-search keyed by |
A few things worth knowing when querying:
Frames that never indexed still get a
framesrow withindexed = FALSEand null statistics, so the indexed rate is simplySELECT AVG(indexed::INT) FROM frames. (Under--start/--stopor multi-GPU runs, each writer only backfills the frame range it owns.)--peaks-onlypopulatespeaks, notreflections. Those frames are written withindexed = FALSEand theirn_peaks/peak_resolution_nm_invset; thereflectionstable stays empty.
Using probixi with Python¶
The CLI is a thin wrapper over the probixi.Probixi pipeline. Getting an indexed stream out takes three steps: construct, calibrate, drain:
from probixi import Probixi
pipe = Probixi(
list_file="files.lst",
geometry_file="my_geometry.geom",
cell_file="my_unit.cell",
device="cuda",
)
# Observe seed frames to learn the noise model and detection threshold.
pipe.calibrate(n_seed=32, target_noise_peaks=5.0)
# Open a lazy stream of solutions and drain it into a DuckDB database
# (the schema described above) or a CrystFEL `.stream`.
stream = pipe.index_stream(pipe.frames(), batch_size=8)
n = stream.to_db(
"output.db",
geometry=pipe.geometry,
cell=pipe.target_cell,
geometry_file="my_geometry.geom",
files=pipe.metadata.files,
)
print(f"indexed {n} frame(s)")
index_streamreturns a lazyIndexStream: no frames are read and nothing is indexed until you drain it. A finalto_db,to_stream,collect, or iteration pulls frames through the pipeline one batch at a time. Because it is lazy, eachIndexResult(and the tensors it carries) lives on the configured torchdevice, so you can still maintain downstream torch-torch connections, and only the writers move data off-device as they serialize. A stream is single-use, re-iterating an already-drained stream yields nothing.