Reconstruction#

lauelab.reconstruct provides an in-process Reconstructor and supported subprocess wrappers. The CPU executable, reconstructN_cpu, ships with the package. The CUDA executable, reconstructN_gpu, requires a separate CUDA build and must be available on PATH unless you pass its path explicitly.

See Reconstruct a wire scan for usage and Depth reconstruction for the calculation.

Use reconstruct_scan() to reconstruct points in local worker processes. It writes one HDF5 file per point and a shared scan.h5 catalog, then returns a ScanResult.

For an external scheduler, call prepare_scan() to get a PreparedScan coordinator and serializable PointTask objects. Workers execute tasks with reconstruct_point() and return a PointOutcome. You can also call reconstruct_point directly to write a standalone point.

To index a depth image, select it with ScanFrame, also importable from this module. To produce per-depth files for other tools, use export_per_depth(). Reconstruction scan format specifies the layouts, statuses, and pixel values.

Every other path returns a ReconstructionResult. Runtime failures are recorded in that result. Invalid arguments and setup failures that occur before processing raise exceptions. The native path does not support the executable’s -F parameter-file option or distortion maps.

The subprocess environment defaults OPENBLAS_NUM_THREADS to 1. The reconstruction programs link OpenBLAS through GSL but do not call BLAS, so extra OpenBLAS workers only consume CPU time. An existing caller setting is preserved. The num_threads argument independently controls the CPU program’s OpenMP reconstruction threads. The executable’s image_range (-f/-l) options apply only to scans stored as one file per image; multi-image HDF5 input used by Reconstructor has no file range.

Behaviour changes in this release#

The reconstructN_cpu option -n <tag> previously had no effect because the normalization-vector length was taken from an HDF5 status code. It now scales each scan frame by entry1/<tag>, falling back to <tag> at the file root, divided by 102 for mA and 88100 for cnt3. The in-process path applies the same divisors but reads only entry1/<tag>, and it raises InputError when that vector is missing or shorter than the scan. The executable silently skips normalization in the same case. This asymmetry is deliberate: the in-process path reports a request it cannot honour.

A wire axis exactly parallel to the positioner x axis, with a zero wire rotation vector, now uses the identity transformation in both reconstruction paths. Earlier reconstructN_cpu releases produced non-finite values for this valid zero-rotation geometry.

class lauelab.reconstruct.Reconstructor(geometry, detector, *, depth_range, resolution=1.0, wire_edge='leading', percent_brightest=100.0, normalization=None, norm_exponent=None, norm_threshold=None, cosmic_filter=False, output_pixel_type=None, num_threads=None, rows_per_stripe=None, memory_limit_mb=8192)[source]#

Reusable in-process wire-scan reconstructor.

Parameters:
  • geometry (Geometry or pathlib.Path) – Parsed geometry or path to a geometry XML file. The geometry must contain a complete wire section.

  • detector (int) – Active detector slot in geometry. This is a physical geometry slot, not an ordinal position among active detectors.

  • depth_range (tuple of float) – Inclusive (start, end) sample depths in µm along the incident beam, relative to the Si origin in geometry. Values must be finite and nondecreasing. Equal endpoints request one depth.

  • resolution (float) – Distance between reconstructed depths in µm. The default is 1.0. The value must be positive and finite.

  • wire_edge (str) – Wire edge or edges used for reconstruction: "leading", "trailing", or "both". The default is "leading". "both" defaults file output to pixel-type code 1 when output_pixel_type is omitted.

  • percent_brightest (float) – Percentage of the brightest intensity-map pixels retained by the reconstruction mask. The default is 100.0. The value must be greater than 0 and at most 100.

  • normalization (str or None) – HDF5 vector below entry1 used to scale file-input frames. The default is None. "mA" values are divided by 102 and "cnt3" values by 88100; other tags have no fixed divisor. A missing or short vector raises InputError. This parameter does not apply to reconstruct_array(); pass its scale argument instead.

  • norm_exponent (float or None) – Exponent normalization applied from the intensity map. The default is None. Values must be greater than 0 and at most 5. This normalization applies to file and array input.

  • norm_threshold (float or None) – Positive intensity threshold for exponent normalization. The default is None; in that case, the threshold is the mean plus five standard deviations of the lowest half of the intensity-map pixels.

  • cosmic_filter (bool) – Apply the executable-compatible cosmic-ray filter before reconstruction. The default is False.

  • output_pixel_type (int or None) – Output-file pixel type. The default is None. Code 0 is numpy.float32, 1 is numpy.int32, 2 is numpy.int16, 3 is numpy.uint16, 5 is numpy.float64, 6 is numpy.int8, and 7 is numpy.uint8. File input defaults to the input type when it has a corresponding code, otherwise to code 5; wire_edge="both" defaults to code 1. This parameter does not alter arrays returned by reconstruct_array().

  • num_threads (int or None) – Positive OpenMP thread count for each reconstruction call. The default is None, which estimates physical cores from Linux SMT topology and otherwise uses the logical CPU count.

  • rows_per_stripe (int or None) – Positive number of image rows processed per stripe. The default is None, which uses at most 256 rows and may use fewer to satisfy memory_limit_mb. An explicit size must also fit the limit.

  • memory_limit_mb (int) – Positive stripe-buffer limit in MiB. The default is 8192. It includes scan-output conversion and stripe-reduction buffers. It does not include retained result images, frame-sized maps/references, native per-thread scratch, or HDF5 library buffers; it is not an RSS limit. A budget too small for one row raises InputError.

Notes

Invalid arguments and failures before stripe processing raise an exception. After processing starts, an expected reconstruction or I/O failure returns a ReconstructionResult with success=False and partial progress.

reconstruct(path, output_base=None, *, return_images=False)[source]#

Reconstruct one HDF5 point, optionally writing per-depth files.

Parameters:
  • path (pathlib.Path or str) – Input 34-ID-E multi-image HDF5 file.

  • output_base (pathlib.Path, str, or None) – Output filename prefix. The default is None. When supplied, one HDF5 file is written per depth plus <output_base>summary.txt. Existing output files are replaced.

  • return_images (bool) – Retain the unscaled reconstructed images in memory. The default is False. This is independent of writing output files.

Returns:

ReconstructionResult – Reconstruction status, output paths, depth coordinates, intensity totals, timings, and optional images.

Raises:
  • InputError – If the input path, file metadata, geometry, or options are invalid, or setup fails before stripe processing begins.

  • MemoryError – If allocation fails before stripe processing begins.

Return type:

ReconstructionResult

reconstruct_array(images, wire_xyz, *, intensity_map=None, positioner='none', image_geometry=None, scale=None)[source]#

Reconstruct aligned in-memory images and raw wire positions.

Parameters:
  • images (numpy.ndarray) – Numeric array with shape (N, rows, columns). numpy.uint16 input remains uint16; every other numeric dtype is converted to contiguous numpy.float64 storage.

  • wire_xyz (numpy.ndarray) – Raw wire positions with shape (N + 1, 3) in the acquisition coordinate system. No file-format bookkeeping offset is applied.

  • intensity_map (numpy.ndarray or None) – Intensity map with shape (rows, columns) used for the bright-pixel mask and exponent normalization. The default is None, which uses the first image.

  • positioner (str) – Historical correction applied to wire_xyz: "none", "pm500", or "alio". The default is "none".

  • image_geometry (ImageGeometry or None) – Full-detector dimensions and ROI mapping. The default is None, which describes an unbinned, zero-based full frame whose detector size is exactly (columns, rows). Binned images and detector ROIs require an explicit geometry; start and group use unbinned pixels.

  • scale (numpy.ndarray or None) – Per-image dimensionless scale factors with shape (N,). The default is None. This is the array-path equivalent of the constructor’s HDF5 normalization vector.

Returns:

ReconstructionResult – A result whose images field is an unscaled numpy.float64 array. Array reconstruction does not write files.

Return type:

ReconstructionResult

Notes

The constructor’s normalization and output_pixel_type parameters do not apply on this path. norm_exponent still applies through intensity_map.

Raises:
  • InputError – If an array shape, dtype, positioner, geometry, or scale is invalid, or setup fails before stripe processing begins.

  • MemoryError – If allocation fails before stripe processing begins.

class lauelab.reconstruct.ImageGeometry(nx_full, ny_full, start=(0, 0), group=(1, 1), n_rows=None, n_cols=None)[source]#

Detector ROI geometry in unbinned-pixel coordinates.

Parameters:
  • nx_full (int) – Full detector width in unbinned pixels.

  • ny_full (int) – Full detector height in unbinned pixels.

  • start (tuple of int) – Zero-based (x, y) coordinate of the ROI origin in unbinned pixels. The default is (0, 0).

  • group (tuple of int) – (x, y) detector binning factors. The default is (1, 1).

  • n_rows (int or None) – Binned image height. The default is None, which uses ny_full // group[1].

  • n_cols (int or None) – Binned image width. The default is None, which uses nx_full // group[0].

property shape: tuple[int, int]#

Binned image shape as (rows, columns).

class lauelab.reconstruct.StripeTiming(row_start, row_stop, read_seconds, compute_seconds, write_seconds)[source]#

Elapsed I/O and native compute time for one row stripe.

row_start#

Zero-based first image row in the stripe.

Type:

int

row_stop#

Exclusive image-row stop index.

Type:

int

read_seconds#

Input read time in seconds.

Type:

float

compute_seconds#

Native reconstruction time in seconds.

Type:

float

write_seconds#

Output write time in seconds, or 0 when no files are written.

Type:

float

class lauelab.reconstruct.ReconstructionResult(success, output_files, log, error=None, command='', return_code=0, images=None, depth_um=None, depth_intensity=None, timings=None, last_completed_stripe=None)[source]#

Result from subprocess or in-process wire-scan reconstruction.

The first six fields preserve the positional constructor interface of the former named tuple. Native-only arrays and stripe timings are None on the subprocess path.

success#

True when reconstruction completed. Invalid arguments and setup failures raise instead of returning a result. Failures after native stripe processing starts set this field to False and preserve available partial progress.

Type:

bool

output_files#

Paths written by this call. For in-process file reconstruction these are the per-depth HDF5 files followed by the summary file. The list can contain partial output after a runtime failure.

Type:

list of str

log#

Standard output captured from a subprocess. The in-process path returns an empty string.

Type:

str

error#

Standard error from an unsuccessful subprocess or the in-process runtime error message. None indicates no reported error.

Type:

str or None

command#

Executed subprocess command. The in-process path returns "liblaue".

Type:

str

return_code#

Subprocess exit status. The in-process path returns 0 on success and -1 for a runtime failure represented by this result.

Type:

int

images#

Retained reconstructed images with shape (n_depths, rows, columns) and dtype numpy.float64. These values are not multiplied by the integer-output normalization rescale. They are present only when the in-process path retains images.

Type:

numpy.ndarray or None

depth_um#

Sample depths with shape (n_depths,) and dtype numpy.float64, in µm along the incident beam relative to the Si origin in the geometry file. This field is native-only.

Type:

numpy.ndarray or None

depth_intensity#

Sum of each unscaled reconstructed image, with shape (n_depths,) and dtype numpy.float64. This field is native-only.

Type:

numpy.ndarray or None

timings#

Per-stripe read, native compute, and write timings in seconds. This field is native-only.

Type:

list of StripeTiming or None

last_completed_stripe#

Zero-based index of the last stripe fully written to every output file, or the last stripe computed when no files are written. None means no stripe completed. This field is native-only.

Type:

int or None

ReconstructionResult is a dataclass with the same six leading fields as the former named tuple. Attribute access and positional construction are unchanged; tuple unpacking is not supported.

lauelab.reconstruct.reconstruct_points(paths, output_dir, *, geometry, detector, workers=None, threads_per_worker=16, **reconstructor_kwargs)[source]#

Reconstruct point files in spawn-based worker processes.

Each worker creates one reusable Reconstructor. Spawn avoids forking a process after the OpenMP runtime has initialized.

Parameters:
  • paths – Sequence of input 34-ID-E multi-image HDF5 point paths. Results preserve this order.

  • output_dir (pathlib.Path or str) – Directory for reconstructed files. Each point uses <stem>_ as its output filename prefix. The directory is created after options are validated.

  • geometry (Geometry, pathlib.Path, or str) – Parsed geometry or path to a geometry XML file.

  • detector (int) – Active physical detector slot in geometry.

  • workers (int or None) – Positive worker-process count. The default is None, which uses the physical-core estimate divided by threads_per_worker, with at least one worker.

  • threads_per_worker (int) – Positive OpenMP thread count used by each worker. The default is 16.

  • **reconstructor_kwargs – Keyword arguments for Reconstructor, including the required depth_range. Do not pass num_threads; use threads_per_worker instead.

Returns:

list of ReconstructionResult – One result per input path, in input order. An expected input, memory, or I/O failure sets success=False for that point without stopping the remaining points.

Raises:
  • ValueError – If a worker or thread count is invalid, or num_threads is passed.

  • InputError – If the shared reconstructor configuration is invalid.

lauelab.reconstruct.prepare_scan(paths, directory, *, geometry, detector, point_ids=None, compression=None, **reconstructor_kwargs)[source]#

Inspect scan inputs, publish the initial catalog, and return a coordinator.

Parameters:
  • paths (Sequence[str | PathLike]) – Input 34-ID-E multi-image HDF5 point files, in manifest order. Each is made absolute against the current directory.

  • directory (pathlib.Path or str) – Scan directory. It must not exist or must be empty; it receives scan.h5 and points/<input stem>.h5 for each input.

  • geometry (Geometry, pathlib.Path, or str) – Parsed geometry or path to a geometry XML file with a wire section. Its text is embedded in the catalog and in every task.

  • detector (int) – Active physical detector slot in geometry.

  • point_ids (Sequence[str] | None) – Sequence of unique, non-empty strings corresponding to paths, or None, which uses the input stems. IDs are independent of the point filenames.

  • compression (str or None) – "gzip" compresses the stored frames losslessly; the default is None.

  • **reconstructor_kwargs – Keyword arguments for Reconstructor, including the required depth_range, except num_threads.

Returns:

PreparedScan – The coordinator of the scan. scan.h5 already lists every input; a point whose input cannot be read is recorded as failed and has no task.

Raises:
  • InputError – If there are no inputs; the shared configuration, point_ids, or compression is invalid; num_threads is passed; or an input stem is unsafe or two inputs would write the same point file.

  • FileExistsError – If directory exists and is not an empty directory.

  • OSError – If the scan directory or catalog cannot be written.

Return type:

PreparedScan

Notes

All shared settings and destinations are checked before writing. Input inspection reads metadata without loading frames.

class lauelab.reconstruct.PreparedScan(directory, tasks, run_values, catalog)[source]#

Track point outcomes and publish the scan catalog.

Created by prepare_scan(). The coordinator is the only writer of scan.h5: it holds the catalog in memory and batches changes into snapshots at most once every five seconds. Workers publish their own point files. Preparation and finalization publish immediately. Use it as a context manager in the process that prepared it; it cannot be sent to another process. Send its tasks instead.

An external scheduler uses it in this order: record_dispatch() when a task is handed to a worker, record() with the worker’s outcome, and finish() after recording an outcome for every dispatched task. Assign each task once. If a worker is lost, record a failed outcome for its task; the coordinator does not retry it.

directory#

Absolute scan directory.

Type:

pathlib.Path

path#

scan.h5 in directory.

Type:

pathlib.Path

tasks#

Tasks of the points whose inputs passed inspection, in manifest order. Points that failed inspection are recorded in the catalog and have no task.

Type:

tuple of PointTask

snapshot(*, force=False)[source]#

Publish pending changes when five seconds have elapsed since publication.

Dispatch and outcome recording call this automatically. An external scheduler should also call it in its polling loop while waiting for workers, so pending changes become visible during long-running points. No background thread writes the catalog. reconstruct_scan handles this polling itself.

Set force=True to publish pending changes immediately. An unchanged catalog is never rewritten. Return whether a snapshot was published. Publication errors propagate; the previous snapshot remains available and pending changes remain in memory.

record_dispatch(task)[source]#

Record that task was handed to a worker, publishing a snapshot if due.

The point’s status becomes "writing" until its outcome is recorded.

Raises:
  • InputError – If the task is not one of this scan’s tasks, was already dispatched or recorded, or the run is no longer running.

  • OSError – If the catalog cannot be published.

record(outcome)[source]#

Record the outcome of one task, publishing a snapshot if due.

A complete outcome is accepted only when its point file is published at the task’s output path and records the task’s identity, output description, scientific settings, geometry, source path, input selection, and acquisition metadata. Pixel values are not read. Outcomes can be recorded in any order.

Raises:
  • InputError – If the outcome matches no task of this scan, the point was already recorded, the run is no longer running, or the outcome is neither complete nor failed with an error.

  • InvalidScanFile – If the point file of a complete outcome is invalid or disagrees with its task.

  • OSError – If the point file cannot be opened or the catalog cannot be published. The previous catalog snapshot is kept.

finish(*, cancelled=False)[source]#

Record the end of the run, publish the final catalog, and return the result.

Parameters:

cancelled (bool) – True when the run stopped before every task was dispatched. Points that were never dispatched become "unattempted".

Raises:
  • InputError – If a dispatched task has no recorded outcome; if points were never dispatched and cancelled is false; or if the run already ended.

  • OSError – If the catalog cannot be published.

close()[source]#

Mark an unfinished run as failed and publish its final catalog.

Dispatched points without an outcome become "interrupted" and points never dispatched become "unattempted". Safe to call more than once; it does nothing after finish(). Leaving the context never reports a successful run.

class lauelab.reconstruct.PointTask(point_id, index, source, output, detector, settings, geometry_path, geometry_xml, compression, image_shape, n_depths, depth_bounds_um, pixel_type, raw_slices, scan_number, sample_position, energy_kev)[source]#

Serializable inputs and settings for reconstructing one point.

Tasks contain strings, numbers, and plain containers, so they can be pickled for a process pool or MPI. To serialize as JSON, first convert with dataclasses.asdict(); rebuild with PointTask(**values). Each worker opens its own input and creates its own native state. Open files, native handles, and callbacks are never included in a task.

point_id#

Identifier assigned to the point.

Type:

str

index#

Zero-based manifest index in its scan, or None for a standalone point.

Type:

int or None

source#

Absolute path of the input 34-ID-E multi-image HDF5 file.

Type:

str

output#

Absolute path of the point file to publish.

Type:

str

detector#

Physical detector slot in the geometry.

Type:

int

settings#

Reconstructor keyword arguments other than num_threads, validated and normalized. Treat as read-only.

Type:

dict

geometry_path#

Geometry file path as given, for provenance.

Type:

str

geometry_xml#

Complete geometry XML captured during preparation and used for every point, even if the original geometry file changes later.

Type:

str

compression#

None or "gzip" for the stored frames.

Type:

str or None

image_shape#

(rows, columns) of the input frames at preparation.

Type:

tuple of int

n_depths#

Number of reconstructed depths at preparation.

Type:

int

depth_bounds_um#

First and last physical depth in µm at preparation.

Type:

tuple of float

pixel_type#

Stored pixel-type code at preparation.

Type:

int

raw_slices#

Half-open range of selected input slices at preparation.

Type:

tuple of int

scan_number#

Acquisition scan number.

Type:

int or None

sample_position#

Sample (x, y, z) in µm; missing components are None.

Type:

tuple of float or None

energy_kev#

Incident energy in keV, when available.

Type:

float or None

Notes

Before reconstruction, the worker checks that the output description, input selection, and acquisition metadata still match the prepared task. Raw pixel content is not verified.

lauelab.reconstruct.reconstruct_point(task, output=None, *, num_threads=None, **parameters)[source]#

Reconstruct one point in the calling process and publish its point file.

Call it with a PointTask from prepare_scan(), or with an input file, an output path, and the parameters of a standalone point. No worker process is started.

Parameters:
  • task (PointTask, pathlib.Path, or str) – A prepared task, or the input 34-ID-E multi-image HDF5 file of a standalone point.

  • output (pathlib.Path, str, or None) – Point file to write for a standalone point. It must not exist; its directory is created when needed. Omit it with a task, which carries its own output path.

  • num_threads (int or None) – Positive OpenMP thread count. The default is None, which estimates the physical cores.

  • **parameters – For a standalone point only: geometry, detector, optional point_id (default: the input stem) and compression, and the Reconstructor keyword arguments, including the required depth_range.

Returns:

PointOutcome – "complete" with the published path, or "failed" with the error of an expected input or reconstruction failure. A failed point publishes nothing. Pixels are not returned; read them with PointReader.

Raises:
  • InputError – If the arguments are inconsistent or the parameters of a standalone point are invalid, or its input cannot be read.

  • ReconstructionError – If the point file cannot be created, written, validated, or published, including when the output or another worker’s private file already exists. The private file is removed; a published file is never replaced.

Return type:

PointOutcome

Notes

The point file is written to <output>.partial beside the final path, closed, validated, and published. The input is refused as a failed point if its output description, selected raw-slice range, or acquisition metadata differ from the task. Raw pixel content is not verified.

lauelab.reconstruct.reconstruct_scan(paths, directory, *, geometry, detector, point_ids=None, compression=None, workers=1, threads_per_worker=None, progress=None, should_stop=None, **reconstructor_kwargs)[source]#

Reconstruct wire-scan points into a scan directory with local worker processes.

The scan is prepared with prepare_scan(), each point is reconstructed by reconstruct_point() in one of workers spawned processes, and the coordinator records outcomes in memory as they arrive. Pending catalog changes are published at five-second intervals and immediately at the end of the run. Completed point files can be read while the scan runs. Pixels stay in the worker that computed them.

Parameters:
  • paths (Sequence[str | PathLike]) – As for prepare_scan().

  • directory (str | PathLike) – As for prepare_scan().

  • geometry – As for prepare_scan().

  • detector (int) – As for prepare_scan().

  • point_ids (Sequence[str] | None) – As for prepare_scan().

  • compression (str | None) – As for prepare_scan().

  • workers (int) – Number of worker processes, at least 1. Each worker reconstructs one point at a time and holds that point’s stripe buffers, so memory demand grows with workers.

  • threads_per_worker (int or None) – OpenMP threads of each worker. The default is None, which divides the estimated physical cores among the workers. Storage conversion can overlap reconstruction, so workers * threads_per_worker is not a strict limit on runnable threads.

  • progress (Callable[[PointOutcome], None] | None) – Callable or None. It is called in the calling process with the PointOutcome of each point when it is recorded, including points whose input failed inspection.

  • should_stop (Callable[[], bool] | None) – Callable or None, polled in the calling process between outcomes. When it returns True, no further point starts, points that have not started are withdrawn, and running points finish.

  • **reconstructor_kwargs – Reconstructor keyword arguments, including the required depth_range, except num_threads.

Returns:

ScanResult – scan.h5 and one outcome per point in manifest order. An expected failure of one point’s input or reconstruction is recorded for that point and does not stop the run. A stopped run is cancelled and its remaining points are "unattempted". Check every outcome.

Raises:
  • InputError – If the shared configuration, workers, threads_per_worker, or a callback is invalid, or as prepare_scan() raises.

  • FileExistsError – If directory exists and is not empty.

  • ReconstructionError – If a point file cannot be written or published, or a worker process ends unexpectedly. No new point starts; completed point files are kept, and the catalog records the run as failed.

  • OSError – If the catalog cannot be published. The last valid snapshot is kept.

Return type:

ScanResult

Notes

A first interrupt (Ctrl-C, or a notebook kernel interrupt) acts like should_stop: the run reports that running points are finishing, and returns a cancelled result once they have. A second interrupt terminates the workers and raises KeyboardInterrupt; their points are recorded as interrupted and their private files may remain. In the main thread, Python’s default SIGINT handler is temporarily replaced and then restored. A handler installed by the caller is left in place.

class lauelab.reconstruct.ScanResult(path, cancelled, outcomes)[source]#

Result of a reconstruction scan.

path#

Path to the scan.h5 catalog.

Type:

pathlib.Path

cancelled#

True when a stop request or an interrupt ended the run early.

Type:

bool

outcomes#

One outcome for each manifest entry, in manifest order, including points whose input failed inspection.

Type:

tuple of PointOutcome

property complete: bool#

Whether every point is complete.

class lauelab.reconstruct.PointOutcome(index, point_id, status, error=None, seconds=0.0, output=None)[source]#

Status and output of one point.

index#

Zero-based manifest index, or None for a standalone point.

Type:

int or None

point_id#

Identifier assigned to the point.

Type:

str

status#

"complete" or "failed" from reconstruct_point(). A scan result also reports "interrupted" and "unattempted" points.

Type:

str

error#

Error message when reconstruction failed.

Type:

str or None

seconds#

Wall time spent on the point, including reading and writing.

Type:

float

output#

Absolute output path when the point completed successfully.

Type:

str or None

class lauelab.reconstruct.ScanReader(path)[source]#

Read the catalog of a reconstruction scan.

On creation, the reader loads a snapshot of scan.h5 and closes the catalog file. Create a new reader to see later updates. You can list points and their metadata without opening any point files.

Parameters:

path (pathlib.Path or str) – scan.h5 of a directory written by prepare_scan().

path#
Type:

pathlib.Path

directory#

Directory containing scan.h5; point paths are relative to it.

Type:

pathlib.Path

run_status#

"running", "finished", "cancelled", or "failed".

Type:

str

points#

Catalog in manifest order.

Type:

tuple of PointEntry

Raises:
  • OSError – If the file cannot be opened as HDF5.

  • InvalidScanFile – If the file is not a valid catalog of a supported version, including a file in the retired single-file layout.

close()[source]#

Close every point reader returned by point(). Safe to call more than once.

property point_ids: tuple[str, ...]#

Point IDs in manifest order, including incomplete points.

point_path(point_id)[source]#

Return the point file of point_id, resolved against directory.

The path is assigned during preparation; the file may not yet exist.

Raises:

KeyError – If the scan has no point with this ID.

point(point_id)[source]#

Open the point file of a complete point.

Only this point’s file is opened. The returned reader belongs to the caller, who may close it; closing this ScanReader also closes it.

Raises:
  • KeyError – If the scan has no point with this ID.

  • InputError – If the catalog snapshot does not list the point as complete.

  • InvalidScanFile – If the point file is invalid or its metadata disagrees with the catalog.

class lauelab.reconstruct.PointReader(path)[source]#

Read frames, regions, and metadata from a reconstructed point file.

Open the reader in the process that will use it, and close it with a context manager or close(). The open reader cannot be sent to another process. All required metadata is in the point file, so you can read it without the catalog, raw input, or original geometry file. Image reads load only the requested frames or region and return arrays that remain usable after the reader closes.

Parameters:

path (pathlib.Path or str) – A point file, for example points/Twin2_wire_1.h5 of a scan.

path#
Type:

pathlib.Path

point_id#

Point ID recorded in the file.

Type:

str

manifest_index#

Zero-based position in the scan the point was prepared in, or None for a standalone point.

Type:

int or None

shape#

(n_depths, ny, nx). frame(i)[y, x] is the pixel at (x, y).

Type:

tuple of int

dtype#

Stored pixel dtype.

Type:

numpy.dtype

depth_um#

Physical depth of each frame in µm along the incident beam relative to the geometry’s Si origin, shape (n_depths,).

Type:

numpy.ndarray

detector_id#

Detector identifier from the input, or an empty string.

Type:

str

detector_size#

Full detector (x, y) size in unbinned pixels.

Type:

tuple of int

start, group

Zero-based (x, y) frame origin in unbinned pixels and (x, y) binning factors, as lauelab.indexing.Indexer.index() takes them.

Type:

tuple of int

norm_rescale#

Factor applied to computed values before they were stored.

Type:

float

norm_threshold#

Exponent-normalization threshold that was used.

Type:

float or None

raw_slices#

Half-open range of input slices used for the raw reference images.

Type:

tuple of int

source_path#

Absolute path of the input file when the point was reconstructed.

Type:

str

scan_number#

Acquisition scan number.

Type:

int or None

sample_position#

Sample (x, y, z) in µm in the acquisition coordinate system. Missing components are NaN.

Type:

tuple of float

energy_kev#

Incident energy in keV.

Type:

float or None

settings#

Requested reconstruction settings by name, such as "depth_range" and "wire_edge", and "geometry_path". A setting that was not given is None.

Type:

dict

Raises:
  • OSError – If the file cannot be opened as HDF5.

  • InvalidScanFile – If the file is not a complete point file of a supported version, including a scan catalog or a file in the retired single-file layout.

close()[source]#

Close the file. Safe to call more than once.

geometry_xml()[source]#

Return the complete geometry XML used during reconstruction.

frame(depth_index)[source]#

Return the stored frame at zero-based depth_index, shape (rows, columns).

region(bounds, depths=None)[source]#

Return stored pixels in half-open bounds through a range of depths.

Parameters:
  • bounds (tuple of int) – (y0, y1, x0, x1) in stored-image pixels. The region must lie inside the image and contain at least one pixel.

  • depths (slice or None) – Depth indices with a step of 1. The default is every depth.

Returns:

numpy.ndarray – Shape (n_selected_depths, y1 - y0, x1 - x0) in the stored dtype.

Return type:

numpy.ndarray

iter_blocks(max_bytes)[source]#

Yield (first_depth_index, frames) blocks of at most max_bytes.

Blocks cover every depth in order. max_bytes must hold at least one frame.

reference(name)[source]#

Return reference image "first_raw", "sum_raw", or "sum_reconstructed".

The raw images hold detector counts before filtering and normalization. "sum_reconstructed" is the sum of the stored frames.

depth_intensity()[source]#

Return the sum of the stored pixels of each frame, shape (n_depths,).

The dtype is numpy.int64 for integer pixels and numpy.float64 otherwise.

computed_depth_intensity()[source]#

Return the sum of each computed frame before scaling and conversion.

These numpy.float64 values equal ReconstructionResult. depth_intensity. They differ from depth_intensity() whenever storage changed a pixel.

class lauelab.reconstruct.PerDepthReader(paths, point_id='per-depth')[source]#

Inspect one point stored as individual reconstructed HDF5 frames.

Parameters:
  • paths – Iterable of per-depth HDF5 paths from Reconstructor, reconstruct, or export_per_depth. Supply only one point’s files, without its summary text file. Files are ordered by their embedded physical depth, not their filename. Depths must be finite and unique; frame shape, dtype, detector mapping and normalization must agree.

  • point_id (str) – Caller-assigned point identity; default "per-depth".

Notes

Use as a context manager. Metadata inspection reads no pixels, and each data operation opens and closes at most one file at a time. Closing the reader prevents further reads. Treat the source files as immutable while using it. Every returned array is owned by the caller.

shape is (depth, y, x); dtype describes stored pixels, and depth_um holds physical depths in µm. detector_size, start and group use the same unbinned (x, y) convention as PointReader. norm_rescale and norm_threshold describe the stored conversion; reading never applies it again. Only the reconstructed sum reference is available. Raw references raise InputError.

close()[source]#

Prevent further reads. Safe to call more than once.

frame(depth_index)[source]#

Return one stored (y, x) frame at a zero-based depth index.

region(bounds, depths=None)[source]#

Return a region through depth, as PointReader.region() does.

iter_blocks(max_bytes)[source]#

Yield (first_depth_index, frames) under a pixel byte budget.

max_bytes must hold at least one frame. Only one returned block is allocated per iteration; callers control how many blocks they keep.

depth_intensity()[source]#

Sum stored pixels per depth, reading one frame at a time.

reference(name)[source]#

Sum reconstructed frames; raw references are unavailable.

class lauelab.reconstruct.PointEntry(index, point_id, status, error, path, source_path, scan_number, sample_position, energy_kev, shape, depth_bounds_um, dtype)[source]#

Metadata for one point from the scan catalog, loaded without reading pixels.

index#

Zero-based manifest index.

Type:

int

point_id#

Stable identity of the point within the scan.

Type:

str

status#

"pending", "writing", "complete", "failed", "interrupted", or "unattempted". The catalog makes a point available for reading once its status is "complete".

Type:

str

error#

Error message for a failed point; empty otherwise.

Type:

str

path#

Location of the point file relative to the directory of scan.h5, with / separators. Assigned during preparation, including for points that later fail.

Type:

str

source_path#

Absolute path of the input file.

Type:

str

scan_number#

Acquisition scan number.

Type:

int or None

sample_position#

Sample (x, y, z) in µm in the acquisition coordinate system. Missing components are NaN.

Type:

tuple of float

energy_kev#

Incident energy in keV.

Type:

float or None

shape#

(n_depths, rows, columns); all zero for a point whose input could not be read.

Type:

tuple of int

depth_bounds_um#

First and last physical depth in µm; NaN when shape is zero.

Type:

tuple of float

dtype#

Stored pixel dtype.

Type:

numpy.dtype or None

property complete: bool#

Whether the catalog lists this point as complete.

lauelab.reconstruct.validate_scan_file(path)[source]#

Check the structure of a closed scan.h5 with bounded reads.

Parameters:

path (pathlib.Path or str) – Catalog to check. Its writer must have closed it.

Returns:

ScanFileSummary – Run status and point counts. Structural validity does not mean that every point is complete; check ScanFileSummary.complete.

Raises:
  • OSError – If the file cannot be opened as HDF5.

  • InvalidScanFile – If the format marker or version is wrong, including the retired single-file layout; a dataset is missing or has the wrong dtype or shape; catalog rows disagree in length; a status code is unknown, or a finished or cancelled run has a pending or writing point; point IDs or point paths are empty, unsafe, or repeat; or a catalog row is inconsistent.

Return type:

ScanFileSummary

Notes

The check reads scan.h5 only. Point files are not opened, and the file remains unchanged.

lauelab.reconstruct.export_per_depth(path, output_base)[source]#

Write a point file as LaueGo-compatible per-depth files.

Output follows the Reconstructor.reconstruct() naming convention: <output_base><i>.h5 for depth index i and <output_base>summary.txt. Pixels are copied from the point file in their stored dtype without conversion or rescaling. The export also copies physical depths, detector metadata, and normalization provenance. All data comes from the point file; reconstruction is not repeated.

Parameters:
  • path (pathlib.Path or str) – A point file, such as points/<input stem>.h5 of a scan directory.

  • output_base (pathlib.Path or str) – Output filename prefix, not a directory. Existing files are replaced.

Returns:

list of str – The per-depth paths in depth order followed by the summary path.

Raises:
  • InvalidScanFile – If path is not a complete point file.

  • OSError – If the point file cannot be opened or an output cannot be written. Files already written are left in place, and the point file is unchanged.

Return type:

list[str]

Notes

The summary reports $executionTime as zero, because the export did not run reconstruction. $rows_at_one_time is the stripe height used during reconstruction.

class lauelab.reconstruct.ScanFileSummary(run_status, n_points, n_pending, n_writing, n_complete, n_failed, n_interrupted, n_unattempted, lauelab_version)[source]#

Run status and point counts returned by validate_scan_file().

run_status#

"running", "finished", "cancelled", or "failed".

Type:

str

n_points, n_pending, n_writing, n_complete, n_failed, n_interrupted, n_unattempted

Point counts by status.

Type:

int

complete#

True when every point is complete. A valid catalog can be incomplete.

Type:

bool

lauelab_version#

Package version that wrote the catalog.

Type:

str

lauelab.reconstruct.reconstruct(input_file, output_file, geometry, depth_range, resolution=1.0, *, image_range=None, verbose=1, percent_brightest=100.0, wire_edge='leading', memory_limit_mb=8192, executable=None, timeout=7200, normalization=None, output_pixel_type=None, distortion_map=None, detector_number=0, wire_depths_file=None, num_threads=None, rows_per_stripe=None, cosmic_filter=False, norm_exponent=None, norm_threshold=None)[source]#

Reconstruct wire-scan data with the native CPU executable.

Parameters:
  • input_file (str | Path) – Path to the input HDF5 file.

  • output_file (str | Path) – Base path for output files, without an extension.

  • geometry (str | Path) – Path to the geometry XML file.

  • depth_range (Tuple[float, float]) – Start and end depths in micrometres. The start must be less than the end.

  • resolution (float) – Depth resolution in micrometres. The default is 1.0.

  • image_range (Tuple[int, int] | None) – First and last image indices. By default, the executable processes its full input range.

  • verbose (int) – Native verbosity level from 0 through 3. The default is 1.

  • percent_brightest (float) – Percentage of the brightest pixels to process. The default is 100.0.

  • wire_edge (str) – Wire edge. Use "leading", "trailing", or "both". The corresponding short forms "l", "t", and "b" are also accepted.

  • memory_limit_mb (int) – Native memory limit in MB. The default is 8192.

  • executable (str | None) – Path to reconstructN_cpu. By default, the function searches the installed package and then PATH.

  • timeout (int) – Process timeout in seconds. The default is 7200.

  • normalization (str | None) – Native normalization tag.

  • output_pixel_type (int | None) – Native WinView output pixel type from 0 through 7.

  • distortion_map (str | None) – Path to a distortion-map file.

  • detector_number (int) – Detector number passed to the native executable. The default is 0.

  • wire_depths_file (str | None) – Path to a file that contains wire-depth corrections.

  • num_threads (int | None) – Number of OpenMP threads. By default, the executable selects the count.

  • rows_per_stripe (int | None) – Rows processed per stripe. By default, the executable uses its own value.

  • cosmic_filter (bool) – Enable native cosmic-ray filtering.

  • norm_exponent (float | None) – Exponent for image-intensity scaling.

  • norm_threshold (float | None) – Threshold for image-intensity scaling.

Returns:

ReconstructionResult – Process status, output paths, captured output, command, and return code.

Raises:
  • FileNotFoundError – If the CPU executable is not in the installed package or on PATH.

  • RuntimeError – If the executable does not produce the expected help output.

  • ValueError – If depth_range or wire_edge is invalid.

Return type:

ReconstructionResult

lauelab.reconstruct.reconstruct_gpu(input_file, output_file, geometry, depth_range, resolution=1.0, *, image_range=None, verbose=1, percent_brightest=100.0, wire_edge='leading', memory_limit_mb=8192, executable=None, timeout=7200, normalization=None, output_pixel_type=None, distortion_map=None, detector_number=0, wire_depths_file=None, cuda_rows=8)[source]#

Reconstruct wire-scan data with the native CUDA executable.

The CUDA program does not support cosmic-ray filtering, norm_exponent, or norm_threshold. Use reconstruct() when you need those options.

Parameters:
  • input_file (str | Path) – Path to the input HDF5 file.

  • output_file (str | Path) – Base path for output files, without an extension.

  • geometry (str | Path) – Path to the geometry XML file.

  • depth_range (Tuple[float, float]) – Start and end depths in micrometres. The start must be less than the end.

  • resolution (float) – Depth resolution in micrometres. The default is 1.0.

  • image_range (Tuple[int, int] | None) – First and last image indices. By default, the executable processes its full input range.

  • verbose (int) – Native verbosity level from 0 through 3. The default is 1.

  • percent_brightest (float) – Percentage of the brightest pixels to process. The default is 100.0.

  • wire_edge (str) – Wire edge. Use "leading", "trailing", or "both". The corresponding short forms "l", "t", and "b" are also accepted.

  • memory_limit_mb (int) – Native memory limit in MB. The default is 8192.

  • executable (str | None) – Path to reconstructN_gpu. By default, the function searches the installed package and then PATH.

  • timeout (int) – Process timeout in seconds. The default is 7200.

  • normalization (str | None) – Native normalization tag.

  • output_pixel_type (int | None) – Native WinView output pixel type from 0 through 7.

  • distortion_map (str | None) – Path to a distortion-map file.

  • detector_number (int) – Detector number passed to the native executable. The default is 0.

  • wire_depths_file (str | None) – Path to a file that contains wire-depth corrections for each pixel.

  • cuda_rows (int) – Number of CUDA rows to process. The default is 8.

Returns:

ReconstructionResult – Process status, output paths, captured output, command, and return code.

Raises:
  • FileNotFoundError – If the CUDA executable is not in the installed package or on PATH.

  • RuntimeError – If the executable does not produce the expected help output.

  • ValueError – If depth_range or wire_edge is invalid.

Return type:

ReconstructionResult

lauelab.reconstruct.find_executable()[source]#

Find the native CPU reconstruction executable.

Returns:

str – Path to reconstructN_cpu in the installed package or on PATH.

Raises:

FileNotFoundError – If the executable cannot be found.

Return type:

str

lauelab.reconstruct.find_gpu_executable()[source]#

Find the native CUDA reconstruction executable.

Returns:

str – Path to reconstructN_gpu in the installed package or on PATH.

Raises:

FileNotFoundError – If the executable cannot be found.

Return type:

str

lauelab.reconstruct.gpu_available()[source]#

Report whether the native CUDA reconstruction executable is usable.

Returns:

bool – True if reconstructN_gpu is available and passes validation.

Return type:

bool

Wire metadata used by reconstruction is documented as lauelab.indexing.WireGeometry.

Inspection#

lauelab.reconstruct.inspection provides square ROI placement, depth traces, normalization, positive-sample selection for logarithmic axes, and reference images. See Inspect a reconstructed point for examples. These functions operate independently of the plotting library. Every returned array is a new, read-only array that the caller owns. ROI bounds are half-open (y0, y1, x0, x1) in stored-image pixels.

lauelab.reconstruct.inspection.square_bounds(size, x, y, image_shape)[source]#

Place a square ROI of size pixels at a click and return its bounds.

Parameters:
  • size (int) – Side of the square in stored-image pixels; a positive integer.

  • x (float) – Click position in zero-based stored-image pixel coordinates, where an integer is a pixel centre and pixel (x, y) covers x - 0.5 to x + 0.5.

  • y (float) – Click position in zero-based stored-image pixel coordinates, where an integer is a pixel centre and pixel (x, y) covers x - 0.5 to x + 0.5.

  • image_shape (tuple of int) – (rows, columns) of the stored image.

Returns:

tuple of int – Half-open (y0, y1, x0, x1) selecting image[y0:y1, x0:x1], which holds exactly size * size pixels.

Raises:

InputError – If size is not a positive integer, the click is not finite, or the square would extend outside the image.

Return type:

tuple[int, int, int, int]

Notes

The square’s centre is the legal centre nearest the click: an integer for an odd size and a half-integer for an even size. A click at equal distance from two legal centres takes the lower coordinate. On each axis the first pixel is ceil(click - size / 2).

lauelab.reconstruct.inspection.bounds_center(bounds)[source]#

Return the (x, y) centre of half-open (y0, y1, x0, x1) bounds.

lauelab.reconstruct.inspection.depth_trace(point, bounds=None, *, label=None, max_bytes=67108864)[source]#

Return the stored intensity of a region through depth.

Parameters:
  • point (PointReader, PerDepthReader, or ArrayPoint)

  • bounds (tuple of int or None) – Half-open (y0, y1, x0, x1) in stored-image pixels, or None for the full frame. A scan point uses its embedded reduction without reading pixels; per-depth files and arrays are reduced on demand. An ROI reads only its selected pixels in bounded depth blocks.

  • label (str or None) – Name for the trace. The default names the full frame or the bounds.

  • max_bytes (int) – Maximum pixel bytes read at once for an ROI; default 64 MiB. Must hold at least one depth plane of the ROI. The returned 1D trace and storage-library buffers are additional. Ignored for the full-frame trace, which uses the reader’s reduction.

Raises:

InputError – If the bounds are not four integers selecting at least one pixel inside the image.

lauelab.reconstruct.inspection.roi_traces(point, rois, *, max_bytes=67108864)[source]#

Return one DepthTrace per named region, in the given order.

rois maps a caller-chosen name to half-open bounds. An empty mapping returns an empty dict and is not an error. max_bytes bounds each ROI’s pixel reads as in depth_trace(); ROIs are reduced in sequence.

lauelab.reconstruct.inspection.reference_image(point, kind='sum_reconstructed')[source]#

Return a reference image with its source kind and pixel-value convention.

Parameters:
Raises:

InputError – If kind is unknown or the point does not hold that image.

class lauelab.reconstruct.inspection.DepthTrace(values, depth_um, point_id, bounds, label)[source]#

Intensity of one region through the reconstructed depths of a point.

values#

Sum of the stored pixels in the region at each depth, shape (n_depths,). numpy.int64 for an integer stored dtype and numpy.float64 otherwise; exact in the integer case. Values are signed, and a sum can be zero or negative.

Type:

numpy.ndarray

depth_um#

Physical depth of each sample in µm, shape (n_depths,).

Type:

numpy.ndarray

point_id#

Point the trace belongs to.

Type:

str

bounds#

Half-open (y0, y1, x0, x1) of the region, or None for the full frame.

Type:

tuple of int or None

label#

Caller-facing name of the region.

Type:

str

property depth_index: numpy.ndarray#

Zero-based depth index of each sample, the alternative x axis.

property n_pixels: int#

Number of stored pixels summed at each depth, or 0 for the full frame.

normalized()[source]#

Divide the trace by its own maximum. See NormalizedTrace.

log_samples()[source]#

Select the samples a logarithmic axis can show. See LogSamples.

class lauelab.reconstruct.inspection.NormalizedTrace(values, reason)[source]#

Normalized trace and an explanation when normalization is unavailable.

values#

trace / max(trace) as numpy.float64, with the sign of each sample preserved and the maximum equal to 1. None when the maximum is zero or negative.

Type:

numpy.ndarray or None

reason#

Why values is None, or an empty string.

Type:

str

class lauelab.reconstruct.inspection.LogSamples(kept, n_omitted)[source]#

Positive-sample indices and the count of omitted nonpositive samples.

kept#

Indices of the samples that are positive, in order.

Type:

numpy.ndarray

n_omitted#

Number of zero or negative samples omitted from a logarithmic plot.

Type:

int

class lauelab.reconstruct.inspection.ReferenceImage(image, kind, values, point_id, label)[source]#

Reference image with its source kind and pixel-value convention.

image#

(rows, columns) array the caller owns.

Type:

numpy.ndarray

kind#

"first_raw", "sum_raw", or "sum_reconstructed".

Type:

str

values#

"raw" for detector counts before filtering and normalization, or "stored" for a sum of the stored reconstructed frames.

Type:

str

point_id#
Type:

str

label#

Caller-facing description of kind.

Type:

str

class lauelab.reconstruct.inspection.ArrayPoint(images, depth_um, point_id='array')[source]#

The point-access interface over an in-memory (depth, y, x) stack.

Use it to inspect ReconstructionResult.images or any stack the same way as a point in a scan file. Inspection uses the array’s existing dtype and values.

Parameters:
  • images (numpy.ndarray) – Numeric array with shape (n_depths, rows, columns).

  • depth_um (numpy.ndarray) – Physical depth of each frame in µm, shape (n_depths,).

  • point_id (str) – Name for the point in prepared data. The default is "array".

Notes

The available reference image is "sum_reconstructed". Requests for raw reference images raise InputError.

frame(depth_index)[source]#

Return frame depth_index as a new array.

region(bounds, depths=None)[source]#

Return pixels in half-open bounds through depths as a new array.

depth_intensity()[source]#

Return the sum of each frame in the accumulator dtype.

iter_blocks(max_bytes)[source]#

Yield owned (first_depth_index, frames) blocks under a byte budget.

The budget must hold one frame; the original caller-owned array is additional. Callers control how many returned blocks they retain.