pyextal.larbed.empad

Raw EMPAD I/O, virtual-aperture integration, and reciprocal-grid construction.

These are the low-level routines behind pyextal.larbed.LARBEDAnalysis. They operate on a 4D-STEM stack indexed matrix[scan_x, scan_y, det_x, det_y], where the first two axes are tilt/scan positions and the last two are detector pixels.

Note on axis naming: throughout this module x is the first (slow) axis of the detector and y the second, matching the original PyLARBED convention. It is not the screen-space x/y of a plotted image.

Attributes

Functions

_in_bounds(x, y, shape)

Checks that a detector coordinate is inside the usable pixel range.

ReadRaw(fname[, type])

Reads an EMPAD .raw file into a 4D array.

FEM_StackAverage(matrix, center_x, center_y[, ...])

Computes a fluctuation electron microscopy variance image over the stack.

_aperture_pixels(center_x, center_y, radius, shape)

Lists the on-detector pixels of a square aperture.

BackgroundFitWeights(center_x, center_y, radius, ri, ro)

Linear weights giving the fitted background summed under an aperture.

ApertureSum(matrix, center_x, center_y[, radius, bkg, ...])

Sums counts within a square aperture, optionally subtracting a background.

ApertureSumVariance(matrix, center_x, center_y[, g, ...])

Propagates counting variance through the same aperture as ApertureSum().

_correlation_matrix(pixels, rho)

Pairwise noise correlation between pixels, looked up in a kernel.

EstimatePixelNoise(matrix[, max_offset, counts, axis, ...])

Measures the per-pixel noise model and the inter-pixel noise correlation.

AlignByBeam(matrix, x, y[, crop, reiterate])

Aligns every pattern in the stack on the beam nearest a given position.

getGridVectors(x0, y0, x2, y2, x1, y1[, order, shape])

Builds a reciprocal-lattice grid from two basis vectors.

getGridVectorsHOLZ(x0, y0, x1, y1, x2, y2, x3, y3, x4, y4)

Builds a reciprocal-lattice grid from four basis vectors, including HOLZ.

find_nearest_peak(vector, matrix[, crop])

Finds the brightest pixel within a window around a predicted position.

getGridVectors_Adjust(x0, y0, x1, y1, x2, y2, ...[, ...])

Builds a two-vector grid and snaps each node onto the nearest observed peak.

AnnularSum(matrix, center_x, center_y, ri, ro)

Averages counts within an annular aperture.

AnnularVar(matrix, center_x, center_y, g, m, DQE, ri, ro)

Computes the variance of the intensity inside an annular aperture.

Precession(matrix, center_x, center_y, ri, ro)

Averages diffraction patterns over an annulus of scan positions.

Precession_Angled(matrix, center_x, center_y, ri, ro, ...)

Averages diffraction patterns over an angular sector of an annulus.

radial_profile(data, center)

Computes the azimuthally averaged radial profile of an image.

get_gvectors(g1, g2, grid[, g3, g4])

Converts lattice indices on a pixel grid into Miller indices.

Module Contents

pyextal.larbed.empad.DEFAULT_DETECTOR_SHAPE = (124, 124)
pyextal.larbed.empad._in_bounds(x, y, shape)

Checks that a detector coordinate is inside the usable pixel range.

A one-pixel border is excluded on every side, so a (124, 124) detector admits coordinates 1..122 inclusive.

Parameters:
  • x (float) – Coordinate along the first detector axis.

  • y (float) – Coordinate along the second detector axis.

  • shape (tuple[int, int]) – Detector shape as (n_x, n_y).

Returns:

True if the coordinate is usable.

Return type:

bool

pyextal.larbed.empad.ReadRaw(fname, type=0)

Reads an EMPAD .raw file into a 4D array.

Parameters:
  • fname (str | pathlib.Path) – Path to the .raw file.

  • type (int, optional) – Detector layout. 0 reads the standard 128x130 frame and crops the two trailing metadata rows to 124x124. 1 reads an already-cropped 124x124 frame. Defaults to 0.

Returns:

A (scan_x, scan_y, det_x, det_y) array with NaNs zeroed, negatives clipped to zero, and a 1e-10 floor applied so logarithms are safe.

Return type:

np.ndarray

pyextal.larbed.empad.FEM_StackAverage(matrix, center_x, center_y, Avg_img=None, rot_average=False)

Computes a fluctuation electron microscopy variance image over the stack.

Parameters:
  • matrix (np.ndarray) – 4D diffraction stack.

  • center_x (int) – Direct-beam position along the first detector axis.

  • center_y (int) – Direct-beam position along the second detector axis.

  • Avg_img (np.ndarray, optional) – Stack to average for the normalisation image. Defaults to None.

  • rot_average (bool, optional) – Add a rotationally averaged contribution. Defaults to False.

Returns:

The FEM image.

Return type:

np.ndarray

pyextal.larbed.empad._aperture_pixels(center_x, center_y, radius, shape)

Lists the on-detector pixels of a square aperture.

This is the one aperture definition shared by ApertureSum(), ApertureSumVariance() and BackgroundFitWeights().

Parameters:
  • center_x (int) – Aperture centre along the first detector axis.

  • center_y (int) – Aperture centre along the second detector axis.

  • radius (int) – Aperture half-width in pixels.

  • shape (tuple[int, int]) – Detector shape as (n_x, n_y).

Returns:

Pixel coordinates, in summation order.

Return type:

list[tuple[int, int]]

pyextal.larbed.empad.BackgroundFitWeights(center_x, center_y, radius, ri, ro, order=2, shape=DEFAULT_DETECTOR_SHAPE, exclude=())

Linear weights giving the fitted background summed under an aperture.

A polynomial surface of the given order is fitted by unweighted least squares to the pixels of the annulus ri <= d <= ro that are neither in the aperture itself nor within radius (square half-width) of a node in exclude. Because the fit is linear, the fitted surface summed over the aperture pixels is data[fit pixels] @ w for a fixed w, so applying it to the stack fits every frame independently and exactly at once.

Note

On a symmetric annulus the plane terms cancel at the centre, so order=1 only differs from order=0 when masking or the detector edge makes the fit region asymmetric. order=2 corrects the curvature of a neighbour’s tail, but only if the annulus spans a range of radii: on a 1-px ring the isotropic curvature is nearly indistinguishable from the constant and the fit extrapolates noisily. The outer radius should still stay clear of the neighbouring cores, where a quadratic no longer describes the tail. For a Lorentzian tail from a reflection 10-20 px away, ri=3 with an annulus of (4, 6) cut the bias of the annulus mean by 10-30x, while (4, 8) overshot when the neighbour was only 10 px away.

Parameters:
  • center_x (int) – Aperture centre along the first detector axis.

  • center_y (int) – Aperture centre along the second detector axis.

  • radius (int) – Aperture half-width in pixels.

  • ri (int) – Inner radius of the fit annulus in pixels.

  • ro (int) – Outer radius of the fit annulus in pixels.

  • order (int, optional) – Polynomial order, 0 (constant), 1 (plane) or 2 (quadratic). Defaults to 2.

  • shape (tuple[int, int], optional) – Detector shape as (n_x, n_y). Defaults to DEFAULT_DETECTOR_SHAPE.

  • exclude (Iterable[tuple[float, float]], optional) – Pixel positions of other reflections whose surroundings are left out of the fit. Defaults to ().

Returns:

(fi, fj, w, gain) – the fit-pixel coordinates along each detector axis, their weights, and the noise gain ||w|| / (count / sqrt(n_fit)), which is 1 for a plain mean over the same pixels and grows as the fit extrapolates.

Return type:

tuple[np.ndarray, np.ndarray, np.ndarray, float]

Raises:

ValueError – If order is not 0, 1 or 2, or the fit region has too few pixels to determine the polynomial.

pyextal.larbed.empad.ApertureSum(matrix, center_x, center_y, radius=0, bkg=(0, 0), fit=None, clip=True)

Sums counts within a square aperture, optionally subtracting a background.

Parameters:
  • matrix (np.ndarray) – 4D diffraction stack.

  • center_x (int) – Aperture centre along the first detector axis.

  • center_y (int) – Aperture centre along the second detector axis.

  • radius (int, optional) – Aperture half-width in pixels. 0 gives a single-pixel (point) virtual detector. Defaults to 0.

  • bkg (tuple[int, int], optional) – Inner and outer radii of an annulus whose mean intensity is subtracted. (0, 0) disables background subtraction. Ignored when fit is given. Defaults to (0, 0).

  • fit (tuple, optional) – (fi, fj, w, ...) from BackgroundFitWeights(); subtracts the fitted background instead of the annulus mean. Defaults to None.

  • clip (bool, optional) – Set negative results to zero. Defaults to True.

Returns:

The background-subtracted rocking curve and the subtracted background, both shaped (scan_x, scan_y).

Return type:

tuple[np.ndarray, np.ndarray]

pyextal.larbed.empad.ApertureSumVariance(matrix, center_x, center_y, g=1, m=1, DQE=1, radius=0, bkg=(0, 0), varB=0.0, fit=None, rho=None)

Propagates counting variance through the same aperture as ApertureSum().

The per-pixel variance follows the detector noise model used elsewhere in pyextal (see pyextal.LucyRichardson.sgnoise()): a signal-dependent term g*m*I/DQE plus a constant readout term g*m*varB. Summing over count independent pixels scales the constant term by count.

Charge sharing makes neighbouring pixels’ noise correlated. Given the correlation kernel rho (see EstimatePixelNoise()), the variance is instead the full quadratic form u^T C u, with u = +1 on the aperture, -w on the fit pixels and C_kl = rho(k - l) * sigma_k * sigma_l. This includes the covariance between the aperture and the fit pixels next to it, which the independent-pixel sum leaves out.

Parameters:
  • matrix (np.ndarray) – 4D diffraction stack.

  • center_x (int) – Aperture centre along the first detector axis.

  • center_y (int) – Aperture centre along the second detector axis.

  • g (float, optional) – Detector gain. Defaults to 1.

  • m (float, optional) – Mixing factor. Defaults to 1.

  • DQE (float, optional) – Detective quantum efficiency. Defaults to 1.

  • radius (int, optional) – Aperture half-width in pixels. Defaults to 0.

  • bkg (tuple[int, int], optional) – Inner and outer radii of the background annulus, whose variance is propagated into the result. Ignored when fit is given. Defaults to (0, 0).

  • varB (float, optional) – Per-pixel background (readout) variance. Defaults to 0.0, which reproduces the original PyLARBED behaviour.

  • fit (tuple, optional) – (fi, fj, w, ...) from BackgroundFitWeights(). The same noise model is propagated through the fit weights, sum(w**2 * var_pixel); the fit pixels are disjoint from the aperture, so without rho the two terms add. Defaults to None.

  • rho (np.ndarray, optional) – Inter-pixel noise correlation, a (2R+1, 2R+1) kernel indexed by pixel offset with rho[R, R] = 1. Pairs further apart than R are taken as uncorrelated. Needs either fit or no background, because the annulus mean’s variance is estimated from its spatial spread rather than propagated. Defaults to None (independent pixels).

Returns:

The variance of the rocking curve, shaped (scan_x, scan_y).

Return type:

np.ndarray

Raises:

ValueError – If rho is combined with the annulus-mean background.

pyextal.larbed.empad._correlation_matrix(pixels, rho)

Pairwise noise correlation between pixels, looked up in a kernel.

Parameters:
  • pixels (np.ndarray) – Pixel coordinates, shaped (n, 2).

  • rho (np.ndarray) – Correlation kernel, shaped (2R+1, 2R+1).

Returns:

The (n, n) correlation matrix; zero beyond the kernel.

Return type:

np.ndarray

pyextal.larbed.empad.EstimatePixelNoise(matrix, max_offset=3, counts=(5, 100), axis=1, stride=4, n_samples=400000, seed=0)

Measures the per-pixel noise model and the inter-pixel noise correlation.

Neighbouring scan positions see nearly the same pattern, so the second difference I[s-1] - 2 I[s] + I[s+1] along the scan removes the signal and leaves noise with six times the per-pixel variance. Its spread against intensity gives var = gm * I, and its correlation between nearby detector pixels gives rho.

Only pixels whose mean is between counts[0] and counts[1] effective counts (I / gm) are used. Below that, the zero-clipping in ReadRaw() truncates the noise; above it, real changes of the rocking curve between scan positions leak into the second difference.

Note

Difference along the fast scan axis (the default, axis=1), whose neighbours are adjacent in time. On EMPAD scans the slow axis carries line-to-line jumps that are not counting noise.

Parameters:
  • matrix (np.ndarray) – 4D diffraction stack (scan_x, scan_y, det_x, det_y).

  • max_offset (int, optional) – Largest pixel offset R of the correlation kernel. Defaults to 3.

  • counts (tuple[float, float], optional) – Range of effective counts per pixel used. Defaults to (5, 100).

  • axis (int, optional) – Scan axis to difference along. Defaults to 1.

  • stride (int, optional) – Use every stride-th scan line across the other axis, to bound the cost. Defaults to 4.

  • n_samples (int, optional) – Random samples per chunk for the variance-versus-intensity fit. Defaults to 400000.

  • seed (int, optional) – Seed for that subsampling. Defaults to 0.

Returns:

gm (per-pixel variance over intensity, i.e. g*m), rho (the (2R+1, 2R+1) correlation kernel, rho[R, R] = 1), gain_sum (gm * rho.sum(), the variance per unit intensity of a sum over a region much larger than the kernel), and n_pixels (the number of pixel samples behind rho).

Return type:

dict

Raises:

ValueError – If no intensity bin falls in the counts range.

pyextal.larbed.empad.AlignByBeam(matrix, x, y, crop=4, reiterate=1)

Aligns every pattern in the stack on the beam nearest a given position.

Parameters:
  • matrix (np.ndarray) – 4D diffraction stack.

  • x (int) – Target position along the first detector axis.

  • y (int) – Target position along the second detector axis.

  • crop (int, optional) – Half-width of the search window around (x, y). Defaults to 4.

  • reiterate (int, optional) – Number of alignment passes. Defaults to 1.

Returns:

The aligned stack.

Return type:

np.ndarray

pyextal.larbed.empad.getGridVectors(x0, y0, x2, y2, x1, y1, order=3, shape=DEFAULT_DETECTOR_SHAPE)

Builds a reciprocal-lattice grid from two basis vectors.

Parameters:
  • x0 (float) – Direct-beam position along the first detector axis.

  • y0 (float) – Direct-beam position along the second detector axis.

  • x2 (float) – First basis-vector tip, first axis.

  • y2 (float) – First basis-vector tip, second axis.

  • x1 (float) – Second basis-vector tip, first axis.

  • y1 (float) – Second basis-vector tip, second axis.

  • order (int, optional) – Grid extent, giving indices -order..order along each basis vector. Defaults to 3.

  • shape (tuple[int, int], optional) – Detector shape used to reject off-detector nodes. Defaults to (124, 124).

Returns:

((i, j), (x, y)) pairs of lattice index and pixel position.

Return type:

list

pyextal.larbed.empad.getGridVectorsHOLZ(x0, y0, x1, y1, x2, y2, x3, y3, x4, y4, order1=3, order2=3, order3=3, order4=3, shape=DEFAULT_DETECTOR_SHAPE)

Builds a reciprocal-lattice grid from four basis vectors, including HOLZ.

Parameters:
  • x0 (float) – Direct-beam position along the first detector axis.

  • y0 (float) – Direct-beam position along the second detector axis.

  • x1 (float) – Tips of the four basis vectors.

  • y1 (float) – Tips of the four basis vectors.

  • x2 (float) – Tips of the four basis vectors.

  • y2 (float) – Tips of the four basis vectors.

  • x3 (float) – Tips of the four basis vectors.

  • y3 (float) – Tips of the four basis vectors.

  • x4 (float) – Tips of the four basis vectors.

  • y4 (float) – Tips of the four basis vectors.

  • order1 (int, optional) – Extent along the first basis vector. Defaults to 3.

  • order2 (int, optional) – Extent along the second basis vector. Defaults to 3.

  • order3 (int, optional) – Extent along the third basis vector. Defaults to 3.

  • order4 (int, optional) – Extent along the fourth basis vector. Defaults to 3.

  • shape (tuple[int, int], optional) – Detector shape used to reject off-detector nodes. Defaults to (124, 124).

Returns:

((i, j, k, l), (x, y)) pairs of lattice index and pixel position.

Return type:

list

pyextal.larbed.empad.find_nearest_peak(vector, matrix, crop=4)

Finds the brightest pixel within a window around a predicted position.

Parameters:
  • vector (tuple[float, float]) – Predicted (x, y) position.

  • matrix (np.ndarray) – 2D image to search, typically the stack average.

  • crop (int, optional) – Half-width of the search window. Defaults to 4.

Returns:

Index of the brightest pixel.

Return type:

tuple[int, int]

pyextal.larbed.empad.getGridVectors_Adjust(x0, y0, x1, y1, x2, y2, average_after, order=4, crop=2, shape=DEFAULT_DETECTOR_SHAPE)

Builds a two-vector grid and snaps each node onto the nearest observed peak.

Parameters:
  • x0 (float) – Direct-beam position along the first detector axis.

  • y0 (float) – Direct-beam position along the second detector axis.

  • x1 (float) – First basis-vector tip, first axis.

  • y1 (float) – First basis-vector tip, second axis.

  • x2 (float) – Second basis-vector tip, first axis.

  • y2 (float) – Second basis-vector tip, second axis.

  • average_after (np.ndarray) – Averaged 2D pattern used to locate peaks.

  • order (int, optional) – Grid extent along each basis vector. Defaults to 4.

  • crop (int, optional) – Half-width of the peak search window. Defaults to 2.

  • shape (tuple[int, int], optional) – Detector shape used to reject off-detector nodes. Defaults to (124, 124).

Returns:

((i, j), (x, y)) pairs, with (x, y) snapped to observed peaks.

Return type:

list

pyextal.larbed.empad.AnnularSum(matrix, center_x, center_y, ri, ro)

Averages counts within an annular aperture.

Parameters:
  • matrix (np.ndarray) – 4D diffraction stack.

  • center_x (int) – Annulus centre along the first detector axis.

  • center_y (int) – Annulus centre along the second detector axis.

  • ri (int) – Inner radius in pixels.

  • ro (int) – Outer radius in pixels.

Returns:

Mean intensity per scan position, shaped (scan_x, scan_y).

Return type:

np.ndarray

pyextal.larbed.empad.AnnularVar(matrix, center_x, center_y, g, m, DQE, ri, ro)

Computes the variance of the intensity inside an annular aperture.

Parameters:
  • matrix (np.ndarray) – 4D diffraction stack.

  • center_x (int) – Annulus centre along the first detector axis.

  • center_y (int) – Annulus centre along the second detector axis.

  • g (float) – Detector gain. Unused; kept for signature compatibility.

  • m (float) – Mixing factor. Unused; kept for signature compatibility.

  • DQE (float) – Detective quantum efficiency. Unused; kept for signature compatibility.

  • ri (int) – Inner radius in pixels.

  • ro (int) – Outer radius in pixels.

Returns:

Variance across the annulus per scan position.

Return type:

np.ndarray

pyextal.larbed.empad.Precession(matrix, center_x, center_y, ri, ro)

Averages diffraction patterns over an annulus of scan positions.

Parameters:
  • matrix (np.ndarray) – 4D diffraction stack.

  • center_x (int) – Annulus centre along the first scan axis.

  • center_y (int) – Annulus centre along the second scan axis.

  • ri (int) – Inner radius in scan steps.

  • ro (int) – Outer radius in scan steps.

Returns:

The averaged 2D diffraction pattern.

Return type:

np.ndarray

pyextal.larbed.empad.Precession_Angled(matrix, center_x, center_y, ri, ro, angle1, angle2)

Averages diffraction patterns over an angular sector of an annulus.

Parameters:
  • matrix (np.ndarray) – 4D diffraction stack.

  • center_x (int) – Annulus centre along the first scan axis.

  • center_y (int) – Annulus centre along the second scan axis.

  • ri (int) – Inner radius in scan steps.

  • ro (int) – Outer radius in scan steps.

  • angle1 (float) – Start of the angular range in degrees.

  • angle2 (float) – End of the angular range in degrees.

Returns:

The averaged 2D diffraction pattern.

Return type:

np.ndarray

pyextal.larbed.empad.radial_profile(data, center)

Computes the azimuthally averaged radial profile of an image.

Parameters:
  • data (np.ndarray) – 2D image.

  • center (tuple[int, int]) – Centre of the profile.

Returns:

Mean intensity as a function of integer radius.

Return type:

np.ndarray

pyextal.larbed.empad.get_gvectors(g1, g2, grid, g3=None, g4=None)

Converts lattice indices on a pixel grid into Miller indices.

Parameters:
  • g1 (tuple[int, int, int]) – Miller index of the first basis vector.

  • g2 (tuple[int, int, int]) – Miller index of the second basis vector.

  • grid (list) – Grid produced by getGridVectors(), getGridVectors_Adjust(), or getGridVectorsHOLZ().

  • g3 (tuple[int, int, int], optional) – Miller index of the third basis vector. Supply together with g4 for a four-vector (HOLZ) grid. Defaults to None.

  • g4 (tuple[int, int, int], optional) – Miller index of the fourth basis vector. Defaults to None.

Returns:

One Miller index per grid node.

Return type:

list[tuple[int, int, int]]