Reconstruction scan protocol#
This page describes how a reconstruction scan is prepared, run, recorded, and read. Reconstruction scan format specifies the files. For examples, see Reconstruct a wire scan.
prepare_scan() checks the inputs and publishes the first catalog. reconstruct_point() writes one point file, and reconstruct_scan() runs the full workflow using local worker processes. ScanReader reads a catalog, PointReader reads a point file, and validate_scan_file() checks a catalog.
Preparation and tasks#
prepare_scan() takes the input files in manifest order, the scan directory, the geometry, and the reconstruction settings. Before writing, it validates the settings, detector slot, point IDs, output filenames, and destination. It then reads each input’s metadata without loading frames and creates one PointTask for each point whose input can be read. Input paths are made absolute against the current directory; symbolic links are not resolved.
The first catalog is published before any point is computed, and it contains every input. An input that cannot be read, or that has invalid metadata, is recorded as a failed point with its error and gets no task. The other points continue.
For inputs that arrive during acquisition, prepare_scan([], directory, **settings) publishes an empty running catalog. PreparedScan.add(path, point_id=...) inspects one ready input and appends its task. The caller establishes that the input is complete and immutable. Admission reads metadata, uses the frozen settings and geometry, and does not publish or dispatch. Persist the updated control state before publishing or handing the task to a worker. A failed admission raises without changing the registry or consuming an index. Duplicate identities, sources, conflicting output names, and existing point outputs or private files are refused.
An appended task keeps its registration index even when inputs arrive out of acquisition order. PreparedScan.outcomes returns a tuple of current point statuses in this order without opening files. Earlier task and outcome tuples remain snapshots. An empty run can be cancelled or closed as failed, but cannot finish successfully. The convenience runner reconstruct_scan still requires at least one input.
Each task contains the absolute input and output paths, the point identity, the detector slot, the validated reconstruction settings, and the complete geometry XML. It also contains the values found during inspection: the frame shape, the depth count and bounds, the pixel type, the selected raw-slice range, the scan number, the sample position, and the incident energy. A missing acquisition value is None in the task and the fill value of its catalog column. Tasks contain only strings, numbers, and plain containers, so they can be pickled or converted to JSON.
The worker process opens its own input and creates its own native state. Before it creates an output file, it compares the output description, raw-slice range, and acquisition values with the prepared task. If they differ, the point fails. The comparison reads only metadata; raw pixel values are not compared. Treat tasks as read-only. The coordinator keeps its own copy of the prepared request and rejects changed tasks, and completed files that do not match the request.
Exactly one coordinator, the PreparedScan returned by prepare_scan, writes scan.h5. Workers write only the point files of their tasks. A scheduler other than reconstruct_scan() uses the coordinator in this order:
Call
record_dispatch()when handing a task to a worker. This sets its status towriting.The worker calls
reconstruct_point()with the task. It writes the frames to the point file and returns only a smallPointOutcome.Call
record()as outcomes arrive, in any order.Call
finish()after every dispatched task has an outcome. Passcancelled=Trueif some tasks were never dispatched; they becomeunattempted.
The scheduler assigns each task once and records a failed outcome for a task whose worker was lost; the coordinator provides no retry. Leaving the coordinator’s context without finish records the run as failed, dispatched points without an outcome as interrupted, and the other points as unattempted. A standalone point, reconstructed without a scan, is prepared with the same rules and written by the same writer. It has no manifest index.
Status and completeness#
/points/statuses and the root status attribute of scan.h5 are HDF5 enumerated types with these codes and names.
Code |
Point status |
Meaning |
|---|---|---|
0 |
|
Not started. This is also the value of an entry that was never written. |
1 |
|
Dispatched for reconstruction; awaiting a recorded outcome. |
2 |
|
The point file is published. |
3 |
|
An expected input or reconstruction failure. The error text is in |
4 |
|
Dispatched, but the run ended before an outcome was recorded. |
5 |
|
The run ended before the point was dispatched. |
Code |
Run status |
Meaning |
|---|---|---|
0 |
|
The coordinator is running the scan. |
1 |
|
Every point was attempted. Individual points can have failed. |
2 |
|
The run stopped on request after its running points finished. |
3 |
|
A shared failure stopped the run, or the coordinator was closed before the run was finished. |
The coordinator keeps the run state in memory and updates it at each dispatch and outcome. It publishes pending changes at most once every five seconds. After preparation, successful completion, cancellation, and failure, it publishes immediately. It does not rewrite an unchanged catalog.
reconstruct_scan() checks for a due snapshot while it waits for workers. An external scheduler should call snapshot() in its polling loop, in addition to recording dispatches and outcomes. The method returns quickly when no snapshot is due. snapshot(force=True) publishes pending changes immediately. No background thread writes the catalog, so a coordinator that is blocked outside these calls does not publish on schedule.
The coordinator writes each snapshot to a private .partial file in the scan directory, closes and validates it, and moves it over scan.h5. The first snapshot is published before any point is computed and has the run status running, so a published catalog can have any run status. A finished or cancelled catalog contains only the point statuses complete, failed, interrupted, and unattempted. A catalog reader keeps the snapshot that it loaded; create a new reader to see later changes.
A worker writes each point file to a private .partial file beside its final path, closes it, validates it against its task, and publishes it without replacing another file. A published point file is complete and is not changed again. reconstruct_point() handles input and reconstruction failures differently from output failures:
Failure |
Examples |
Result |
|---|---|---|
Input or reconstruction of the point |
Missing or unreadable input, invalid metadata, an input changed since preparation, a stripe-buffer budget too small for one row, a native reconstruction error |
A |
Output of the point |
The private file cannot be created or written, validation or publication fails, the final file or another worker’s private file already exists |
|
When you call record() with a complete outcome, the coordinator validates the published file against the prepared task. It checks the point ID, manifest index, shape, depth bounds, pixel type, the settings recorded as given, the embedded geometry, the source path, selected raw-slice range, and acquisition metadata without reading pixels. Outcomes for unknown tasks and duplicate outcomes are rejected.
Point files and the catalog are published separately. If a run stops after a point file is published but before the next catalog snapshot, the file can be read directly, although the catalog does not list it as complete. Readers use the catalog status; they do not search the directory for other completed files. Without caller-managed checkpoints and receipts, a process crash loses changes since the last snapshot. Durability after a power loss is not guaranteed.
Structural validation of a catalog checks the root attributes, the required datasets, their dtypes, enumerated types, shapes, units, and fill values, the catalog length, the status codes, the point IDs and paths, the consistency of each catalog row, and that /point_files contains a link to each complete point and to no other. It reads only scan.h5 and does not change it. A scan is complete when it has at least one point and every point has the status complete, so the catalog of a cancelled or partly failed run can pass validation and contain incomplete points.
Checkpoints and receipt replay#
export_state() returns detached control state that round-trips through strict JSON (allow_nan=False). It contains the frozen recipe and geometry XML, registered task descriptions, identities, outcomes, absolute paths, and package/native version tags. It contains no image arrays, open files, or native handles. Treat its versioned structure as opaque. The method does not write a sidecar or publish a catalog.
The application owns checkpoint storage, durable worker receipts, queue reconciliation, and exclusive access to each run. Save registrations before dispatch or publication, and persist each worker’s PointOutcome before calling record. Reconstruct JSON receipts with PointOutcome(**values) when loading them.
restore() accepts an unfinished checkpoint and optional durable outcomes. It validates completed point files against their saved tasks without reopening raw inputs or the original geometry file. It permits an older catalog snapshot and exact repeated receipts; it rejects conflicting or unknown receipts, changed identities, unavailable completed files, and terminal catalog progress absent from the receipts. Restore requires the same mount paths and package/native version tags. It does not migrate state or establish that running workers have stopped.
Restore does not write files, dispatch tasks, or remove private files. An unrecorded final output requires explicit receipt reconciliation; it is never silently adopted or overwritten. A surviving .partial file is preserved, and a later reconstruction attempt refuses to replace it. Once restoration succeeds, reconcile pending and dispatched tasks with the scheduler before submitting more work, and call snapshot to publish the recovered view. This is a caller-managed recovery mechanism, not an automatic retry service.
Readers#
PointReader, PerDepthReader, and ArrayPoint have the same frame and region methods for inspection and plotting. They read a point file, per-depth files, and an in-memory array.
ScanReader loads a catalog snapshot when it is created, then closes the catalog file. It lists the point IDs, statuses, and metadata in manifest order without opening point files, and resolves each point path against the scan directory. point() opens only the file of that point, and compares its point ID, manifest index, shape, dtype, and physical depth bounds with the catalog.
PointReader owns the handle of one point file. It reads the scan number, sample position, incident energy, and detector region from the metadata copied from the input. For a missing field, it uses the default that reconstruction uses: the frame size for Nx and Ny, (0, 0) for the origin, and 1 for each binning factor. Open it as a context manager in the process that uses it; it cannot be sent to another process. A point opened through a ScanReader is closed when the caller closes it or closes that ScanReader. PerDepthReader orders the files by their physical depths and opens at most one file at a time. It calculates the totals and the reconstructed reference from the stored pixels when they are requested; raw references are not available.
frame() reads one frame, region() reads a rectangular selection over a depth range, and iter_blocks() reads successive depth blocks within a byte budget that the caller gives. A request for a reference image that is not available raises InputError. Returned arrays belong to the caller.
region() allocates the requested selection, so choose bounded depth slices for a large region. The inspection functions in Inspect a reconstructed point read bounded depth blocks.