Detector and DPC API#

The detector and DPC namespaces own reusable reductions from \(I[R_r,R_c,k_r,k_c]\) to detector-shaped or scan-shaped scientific products. They return numerical arrays; use show_2d for a static figure or a viewer for interactive exploration.

Start with automatic disk fitting#

from quantem.gpu import detector, io
from quantem.core.visualization import show_2d

data = io.load("gold_master.h5")
bf = detector.bf(data)
adf = detector.adf(data)
show_2d([bf, adf], title=["BF", "ADF"], norm="power_sqrt", cmap="inferno")

The first BF/ADF/DF call estimates the disk center and radius from the mean diffraction pattern. Calls on the same encoded acquisition reuse that geometry. Mutable array inputs are fitted again. No prepare or manual fit is needed.

Image

Default detector selection

detector.bf(data)

From the center through one fitted radius

detector.adf(data)

Between one and two fitted radii

detector.df(data)

Outside one fitted radius

Selections stop at the detector boundary. These are sums, not averages. Keep data open until calculations and viewers finish, then call data.close().

Gold mean diffraction with BF and ADF selections beside the scan images

The left panels use shared log contrast. BF and ADF use independent square-root contrast; their displayed brightness is not a shared count scale. See the README plotting workflow to draw the same selection outlines. Pass cmap="inferno" to choose that colormap.

Override the detector selection#

bf = detector.bf(data, bright_field_radius_px=45)  # radius in detector pixels
adf = detector.adf(data, inner_radius_px=60, outer_radius_px=85)
df = detector.df(data, inner_radius_px=60)
adf = detector.adf(data, inner_radius_px=60, outer_radius_px=85, bright_field_center_px=(96, 96))

Supply only the values you know. Missing geometry is fitted automatically; overrides apply to that call and do not replace the cached automatic fit. bright_field_radius_px (BF) and bright_field_edge_radius_px (ADF/DF) always mean the bright-field disk radius in pixels. In ADF, it sets the default one-to-two-radius band; use inner_radius_px and outer_radius_px for explicit limits in pixels, or inner_angle_mrad and outer_angle_mrad in milliradians. The unit is in the keyword name; a bound given both ways raises.

Inputs and outputs#

Call

Input

Output

detector.mean(data)

one supported 4D source

detector-shaped mean diffraction pattern

detector.fit_probe(data)

2D mean diffraction pattern, or a 4D source whose mean pattern is used

(row, column) center and equivalent-area radius, in detector pixels; ValueError when the pattern has no disk

detector.fit_bright_field_disk(data, semiangle_mrad=None)

2D mean diffraction pattern, or a 4D source whose mean pattern is used; optional convergence semiangle (mrad)

BrightFieldDiskFit: bright_field_center_px (row, column), bright_field_edge_radius_px, detector_sampling_mrad (None without a semiangle), method, and show() (the mean pattern with the fitted edge, axes in mrad)

data.calibration.fit_detector_sampling()

a loaded acquisition with a recorded semiangle_mrad

the same BrightFieldDiskFit, computed on the encoded data and recorded on data.calibration (source Fitted (bright-field disk)); SSB and the viewers then read it

detector.disk_edge_radius(mean_dp)

2D mean diffraction pattern

radius in detector pixels at which the bright-field disk falls to half its plateau

detector.detector_sampling_mrad(mean_dp, semiangle_mrad)

2D mean diffraction pattern and the convergence semiangle (mrad)

(row, column) detector sampling in mrad per pixel: semiangle_mrad / disk_edge_radius(mean_dp), the rule SSB, parallax and screening use when none is recorded

detector.bf(data, ...)

4D source; optional geometry overrides

scan-shaped bright-field sum

detector.adf(data, ...)

4D source; optional annular limits

scan-shaped annular dark-field sum

detector.df(data, ...)

4D source; optional inner limit

scan-shaped dark-field sum

dpc.run(data, ...)

4D source plus optional detector mask/rotation

DPCResult with CoM, aligned DPC, phase, and rotation metadata

The current detector convenience functions return NumPy product arrays after backend execution. DPCResult.phase, com_row, com_col, com_row_aligned, and com_col_aligned are float32 scan-shaped arrays.

Inspect the inferred rotation without assembling a notebook-side summary:

dpc_result = dpc.run(data)
dpc_result.report()

The table labels the scan-detector rotation in degrees, the detector-axis swap, and elapsed seconds. DPC’s curl minimum does not resolve the 180° ambiguity; SSB can check the phase polarity before selecting a branch.

Shapes, coordinates, dtypes, and units#

Every input follows (scan_row, scan_column, detector_row, detector_column). Every BF/DF/ADF and DPC product follows (scan_row, scan_column). Mean diffraction follows (detector_row, detector_column).

bright_field_center_px=(row, col) is always detector order. Bounds named *_radius_px are detector pixels. Bounds named *_angle_mrad require convergence-semiangle calibration in the source metadata. The convenience detector products return float32 NumPy arrays, even for integer counts. For exact large integer totals, use the advanced session’s masked_sum_exact described in the integer-product contract. DPC moments and phase products are float32.

Errors and unsupported requests#

  • A milliradian detector limit without semiangle_mrad raises with a corrective instruction; it is never interpreted as pixels.

  • A supplied scan shape whose product differs from the number of diffraction patterns raises rather than reshaping silently.

  • Missing accelerator capability fails explicitly; it is not reported as GPU work after a hidden CPU fallback.

  • Row and column components may not be swapped to match a private launch layout.

Provenance#

An application records the source identity, original and loaded shapes, detector bin/crop, source and accumulation dtypes, detector mask or center/radius, calibration and units, selected backend/device, DPC rotation, rotation flip, and package revision. A binned input produces a binned-source product and must not be labeled native detector resolution.

Inspect the fitted disk#

from quantem.gpu import detector, io

mean_dp = detector.mean(data)
center, radius = detector.fit_probe(mean_dp)
show_2d(mean_dp, norm="log_auto", title="Mean diffraction pattern", cmap="inferno")

fit_probe retains the threshold/centroid estimator and estimates disk geometry, not complex probe phase or aberrations. This explicit call is useful for checking or drawing the geometry. It is optional for ordinary BF/ADF/DF images.

Calculate DPC#

from quantem.gpu import dpc

result = dpc.run(data)
show_2d(result.phase, title="Integrated DPC phase", cmap="inferno")

Supply scan_detector_rotation_deg (with scan_detector_rotation_flipped) to hold the scan-detector rotation fixed, or omit it for the DPC rotation search. Inspect result for the rotation and CoM components: a fitted rotation is scan_detector_rotation_mod180_deg, known only modulo 180 degrees, and a given one is scan_detector_rotation_deg. The fitted value lies in [0, 180]: an unrotated scan fits the end point of the search grid, float32 pi, which reads 180.000005, and 0 and 180 describe the same estimate. DPC does not estimate SSB aberrations.

For a client that needs several launch products from the same source, use screening.prepare() and reuse its small derived state instead of traversing the complete detector volume independently for every product.

Integration boundary#

QuantEM.GPU owns detector geometry, exact reduction arithmetic, CoM/DPC/iDPC math, backend dispatch, and result fields. A consuming application owns when to run the operations, cache admission, memory-policy choices, and presentation.

See BF, DF, and ADF reductions and CoM, DPC, and iDPC for the equations, optimization model, source map, and parity gates.