pyextal.larbed ============== .. py:module:: pyextal.larbed .. autoapi-nested-parse:: Extraction of LARBED rocking curves from raw 4D-STEM data. This is the stage that runs before refinement: it reads a raw EMPAD scan, indexes the reflections on a reciprocal-space grid, and integrates each one into a 2D rocking curve. The output feeds :class:`pyextal.dinfo.LARBEDDiffractionInfo` directly via :meth:`LARBEDAnalysis.to_dinfo`, or via an HDF5 file with :meth:`LARBEDAnalysis.save_h5`. Merged in from the standalone PyLARBED package. .. rubric:: Example >>> from pyextal.larbed import LARBEDAnalysis >>> larbed = LARBEDAnalysis("scan_x256_y256.raw") >>> larbed.load_data(type=0) >>> larbed.average_data() >>> larbed.assign_gvector((46, 48), (64, 36)) >>> larbed.calculate_grid_vectors(crop=2, order=3, Adjust=True) >>> larbed.calculate_g_vectors((1, 3, -2), (4, 2, 0)) >>> larbed.IntegrateLarbed(ri=3, ro=(6, 8), g=580, m=0.47, Variance=True) >>> dinfo = larbed.to_dinfo("si110.dat", thickness=1000, gl=37) Submodules ---------- .. toctree:: :maxdepth: 1 /autoapi/pyextal/larbed/analysis/index /autoapi/pyextal/larbed/calibration/index /autoapi/pyextal/larbed/empad/index /autoapi/pyextal/larbed/peaks/index Classes ------- .. autoapisummary:: pyextal.larbed.LARBEDAnalysis pyextal.larbed.LARBEDCalibration Functions --------- .. autoapisummary:: pyextal.larbed.ReadRaw pyextal.larbed.AlignByBeam pyextal.larbed.ApertureSum pyextal.larbed.ApertureSumVariance pyextal.larbed.BackgroundFitWeights pyextal.larbed.EstimatePixelNoise pyextal.larbed.AnnularSum pyextal.larbed.AnnularVar pyextal.larbed.Precession pyextal.larbed.Precession_Angled pyextal.larbed.FEM_StackAverage pyextal.larbed.radial_profile pyextal.larbed.find_nearest_peak pyextal.larbed.getGridVectors pyextal.larbed.getGridVectors_Adjust pyextal.larbed.getGridVectorsHOLZ pyextal.larbed.get_gvectors Package Contents ---------------- .. py:class:: LARBEDAnalysis(file_name) Turns a raw EMPAD scan into one rocking curve per reflection. The usual sequence is :meth:`load_data`, :meth:`average_data` (or :meth:`align_data`), :meth:`assign_gvector`, :meth:`calculate_grid_vectors`, :meth:`calculate_g_vectors`, and :meth:`IntegrateLarbed`. The result can then be handed to the refinement stage with :meth:`to_dinfo` or :meth:`save_h5`, or written as loose ``.npy`` files with :meth:`save_larbed`. :ivar file_name: Path to the raw ``.raw`` file. :vartype file_name: str :ivar data: The 4D stack (scan_x, scan_y, det_x, det_y). :vartype data: np.ndarray :ivar center: Direct-beam position on the detector. :vartype center: tuple[float, float] :ivar IntRadius: Aperture half-width used by :meth:`align_data` and :meth:`ZeroLarbed`. :vartype IntRadius: int :ivar grid: Reciprocal-lattice nodes as ``(index, pixel)`` pairs. :vartype grid: list :ivar g_vectors: Miller index of each grid node. :vartype g_vectors: list :ivar Store_Larbed: Integrated intensity map per reflection. :vartype Store_Larbed: list :ivar Store_LarbedVariance: Propagated variance per reflection. :vartype Store_LarbedVariance: list :ivar Store_bkg: Subtracted background per reflection. :vartype Store_bkg: list :ivar Store_bkg_info: With a fitted background, ``n_fit`` (fit pixels) and ``noise_gain`` per reflection. :vartype Store_bkg_info: list :ivar noise: The detector noise model measured by :meth:`estimate_noise`. :vartype noise: dict .. py:attribute:: file_name .. py:attribute:: data :value: [] .. py:attribute:: average_raw :value: None .. py:attribute:: average_aligned :value: None .. py:attribute:: aligned :value: False .. py:attribute:: g_grid :value: None .. py:attribute:: g_vectors :value: None .. py:attribute:: Larbed :value: [] .. py:attribute:: LarbedDeconvolved :value: [] .. py:attribute:: gg_pixels :value: [] .. py:attribute:: mtf_2d :value: None .. py:attribute:: mixing :value: None .. py:attribute:: center :value: (64, 64) .. py:attribute:: IntRadius :value: 4 .. py:attribute:: IntZero :value: [] .. py:attribute:: noise :value: None .. py:property:: detector_shape Detector shape (det_x, det_y) of the loaded stack. :type: tuple[int, int] .. py:method:: load_data(type=0) Reads the raw file and centres the direct beam on the detector midpoint. :param type: Detector layout, passed to :func:`pyextal.larbed.empad.ReadRaw`. Defaults to 0. :type type: int, optional .. py:method:: align_data(iterations=3) Aligns the stack on the direct beam and stores before/after averages. :param iterations: Number of alignment passes. Defaults to 3. :type iterations: int, optional .. py:method:: deconv_DPstack(mtf_2d, background=0, niter=100, varB=6.29, delta=0, A=1, g=580, m=None) Deconvolves every diffraction pattern in the stack against the MTF. :param mtf_2d: The modulation transfer function. :type mtf_2d: np.ndarray :param background: Pattern background. Defaults to 0. :type background: float, optional :param niter: Lucy-Richardson iterations. Defaults to 100. :type niter: int, optional :param varB: Background variance. Defaults to 6.29. :type varB: float, optional :param delta: Squared percentage gain error. Defaults to 0. :type delta: float, optional :param A: Fano/conversion-gain parameter. Defaults to 1. :type A: float, optional :param g: Detector gain. Defaults to 580. :type g: float, optional :param m: Mixing factor. Derived from ``mtf_2d`` when None. Defaults to None. :type m: float, optional .. py:method:: deconv_Larbedstack(background=0, niter=100, varB=6.29, delta=0.0, A=1, g=1, mtf_2d=None, pad_size=None, m=1) Deconvolves the integrated rocking curves against the MTF. .. note:: Legacy. Because the rocking curves are already integrated over an aperture, deconvolution mainly adds noise; this step is normally skipped. It also discards the propagated variance. :param background: Background level. Defaults to 0. :type background: float, optional :param niter: Lucy-Richardson iterations. Defaults to 100. :type niter: int, optional :param varB: Background variance. Defaults to 6.29. :type varB: float, optional :param delta: Squared percentage gain error. Defaults to 0.0. :type delta: float, optional :param A: Fano/conversion-gain parameter. Defaults to 1. :type A: float, optional :param g: Detector gain. Defaults to 1. :type g: float, optional :param mtf_2d: MTF to use. Falls back to ``self.mtf_2d``. Defaults to None. :type mtf_2d: np.ndarray, optional :param pad_size: Edge padding applied before deconvolution. Defaults to None (no padding). :type pad_size: int, optional :param m: Mixing factor. Defaults to 1. :type m: float, optional .. py:method:: average_data() Averages the stack without aligning it. .. py:method:: assign_gvector(g1, g2, g3=None, g4=None) Records the detector pixel positions of the basis reflections. Supply two basis vectors for a zone-axis grid, or four to include HOLZ reflections. :param g1: Pixel position of the first basis reflection. :type g1: tuple[float, float] :param g2: Pixel position of the second. :type g2: tuple[float, float] :param g3: Pixel position of the third. Defaults to None. :type g3: tuple[float, float], optional :param g4: Pixel position of the fourth. Defaults to None. :type g4: tuple[float, float], optional .. py:method:: calculate_g_vectors(g1, g2, g3=None, g4=None) Assigns a Miller index to every node of the pixel grid. The number of basis vectors must match what was passed to :meth:`assign_gvector`. :param g1: Miller index of the first basis reflection. :type g1: tuple[int, int, int] :param g2: Miller index of the second. :type g2: tuple[int, int, int] :param g3: Miller index of the third. Defaults to None. :type g3: tuple[int, int, int], optional :param g4: Miller index of the fourth. Defaults to None. :type g4: tuple[int, int, int], optional .. py:method:: calculate_grid_vectors(crop=1, order=3, Adjust=True) Builds the reciprocal-space grid of reflections on the detector. Dispatches on how many basis vectors :meth:`assign_gvector` was given: four selects the HOLZ grid, two selects either the peak-snapped or the plain lattice grid depending on ``Adjust``. :param crop: Half-width of the peak search window used when ``Adjust`` is True. Defaults to 1. :type crop: int, optional :param order: Grid extent. Must be a 4-tuple for the HOLZ grid, an int otherwise. Defaults to 3. :type order: int | tuple[int, int, int, int], optional :param Adjust: Snap each two-vector grid node onto the nearest observed peak. Defaults to True. :type Adjust: bool, optional .. py:method:: plot_averages(ratio=0.2) Plots the stack average before and after alignment. :param ratio: Fraction of the maximum used as the display ceiling. Defaults to 0.2. :type ratio: float, optional .. py:method:: ZeroLarbed(ratio=1) Integrates and plots the rocking curve of the direct beam. :param ratio: Fraction of the maximum used as the display ceiling. Defaults to 1. :type ratio: float, optional .. py:method:: plot_grid_vectors(ratio=0.2) Overlays the reflection grid on the stack average. :param ratio: Fraction of the maximum used as the display ceiling. Defaults to 0.2. :type ratio: float, optional .. py:method:: IntegrateLarbed(ri=4, ro=(0, 0), g=1, m=1, DQE=1, varB=0.0, Variance=False, bkg_fit=None, mask_neighbours=True, clip=True, rho=None) Integrates each reflection into a rocking curve. Reflections whose background-subtracted sum is nowhere positive are dropped from ``grid`` and ``g_vectors`` as well as from the output. By default the background is the mean of the ``ro`` annulus. That mean is biased high next to a bright reflection, whose convex tail runs through the annulus, and the clipping at zero then wipes out much of a weak rocking curve. With ``bkg_fit`` set, a polynomial surface is fitted instead, for each reflection on each frame, to the annulus pixels that are outside the aperture and (with ``mask_neighbours``) away from the other reflections; the fitted surface summed under the aperture is what gets subtracted. See :func:`pyextal.larbed.empad.BackgroundFitWeights`. :param ri: Aperture half-width in pixels. Defaults to 4. :type ri: int, optional :param ro: Inner and outer radii of the background annulus. ``(0, 0)`` disables background subtraction. Defaults to (0, 0). :type ro: tuple[int, int], optional :param g: Detector gain. Defaults to 1. :type g: float, optional :param m: Mixing factor. Defaults to 1. :type m: float, optional :param DQE: Detective quantum efficiency. Defaults to 1. :type DQE: float, optional :param varB: Per-pixel background variance. Defaults to 0.0. :type varB: float, optional :param Variance: Also propagate the counting variance. Defaults to False. :type Variance: bool, optional :param bkg_fit: Fit the background as a polynomial of order 0 (constant), 1 (plane) or 2 (quadratic) instead of using the annulus mean. Order 2 needs an annulus spanning a range of radii that stays clear of the neighbouring reflections, e.g. ``ri=3, ro=(4, 6)``. Defaults to None (annulus mean). :type bkg_fit: int, optional :param mask_neighbours: With ``bkg_fit``, leave pixels within ``ri`` of the other grid reflections out of the fit. Defaults to True. :type mask_neighbours: bool, optional :param clip: Set negative intensities to zero. Passing False keeps the noise around zero unbiased; note that the jaxGeo ``oversub`` model and :mod:`pyextal.acom.quality` were written for clipped data. Defaults to True. :type clip: bool, optional :param rho: Inter-pixel noise correlation kernel from :meth:`estimate_noise`. The variance then includes the covariance between neighbouring pixels, both inside the aperture and between the aperture and the background-fit pixels. Needs ``bkg_fit`` or no background. Defaults to None (independent pixels). :type rho: np.ndarray, optional :raises ValueError: If ``bkg_fit`` is set without a background annulus, or ``rho`` with the annulus-mean background. .. py:method:: estimate_noise(**kwargs) Measures the detector noise model from the scan itself. See :func:`pyextal.larbed.empad.EstimatePixelNoise`. Pass the result to :meth:`IntegrateLarbed` as ``m=noise['gm'] / g`` and ``rho=noise['rho']``. :param \*\*kwargs: Passed to :func:`pyextal.larbed.empad.EstimatePixelNoise`. :returns: ``gm``, ``rho``, ``gain_sum`` and ``n_pixels``; also stored as ``self.noise``. :rtype: dict .. py:method:: plot_IntegratedLarbed(index, Variance=False) Plots one integrated rocking curve, or all of them. :param index: Index of the reflection, or ``'all'``. :type index: int | str :param Variance: Plot the variance map instead. Defaults to False. :type Variance: bool, optional .. py:method:: deconvolve_image(index, niter=100, varB=6.29, delta=0, A=1, g=580) Deconvolves a single integrated rocking curve against the MTF. :param index: Index of the reflection. :type index: int :param niter: Lucy-Richardson iterations. Defaults to 100. :type niter: int, optional :param varB: Background variance. Defaults to 6.29. :type varB: float, optional :param delta: Squared percentage gain error. Defaults to 0. :type delta: float, optional :param A: Fano/conversion-gain parameter. Defaults to 1. :type A: float, optional :param g: Detector gain. Defaults to 580. :type g: float, optional :returns: The deconvolved rocking curve. :rtype: np.ndarray .. py:method:: plot_larbed(rotation=-22, order_option=1, order=6, Circle_radius=8, a_expansion=1.6, ratio=0.2, Variance=False) Plots every rocking curve at the position of its reflection. :param rotation: Rotation applied to each rocking curve in degrees, aligning the tilt-scan axes with the pattern. Defaults to -22. :type rotation: float, optional :param order_option: ``0`` filters reflections by the largest Miller index, ``1`` by the length of the g-vector. Defaults to 1. :type order_option: int, optional :param order: Cutoff for the filter above. Defaults to 6. :type order: int, optional :param Circle_radius: Radius of each rocking-curve disk. Defaults to 8. :type Circle_radius: float, optional :param a_expansion: Scaling applied to the disk positions. Defaults to 1.6. :type a_expansion: float, optional :param ratio: Fraction of the display maximum used as the ceiling. Defaults to 0.2. :type ratio: float, optional :param Variance: Plot variance maps instead of intensities. Defaults to False. :type Variance: bool, optional .. py:method:: stack() Packs the integrated results into the arrays the refinement stage wants. :returns: The intensity stack shaped (n_beams, ny, nx), the matching variance stack (None if :meth:`IntegrateLarbed` was run with ``Variance=False``), and the Miller indices shaped (n_beams, 3). :rtype: tuple[np.ndarray, np.ndarray | None, np.ndarray] :raises RuntimeError: If :meth:`IntegrateLarbed` has not been run. .. py:method:: to_dinfo(datpath, thickness, gl, tiltX=0.0, tiltY=0.0) Builds a :class:`pyextal.dinfo.LARBEDDiffractionInfo` from the results. This is the direct handoff into the refinement stage, replacing a :meth:`save_larbed` / ``np.load`` round-trip. :param datpath: Path to the crystal ``.dat`` file. :type datpath: str | pathlib.Path :param thickness: Initial sample thickness estimate in Angstroms. :type thickness: float :param gl: Tilt step size, as the number of steps spanning the x-axis reflection. See ``LARBED_Calibration.md``. :type gl: float :param tiltX: Initial tilt about x in degrees. Defaults to 0.0. :type tiltX: float, optional :param tiltY: Initial tilt about y in degrees. Defaults to 0.0. :type tiltY: float, optional :returns: Ready to pass to :class:`pyextal.roi.LARBEDROI`. :rtype: pyextal.dinfo.LARBEDDiffractionInfo .. py:method:: save_h5(h5path, *, prefix='', crystal_dat='', date='') Writes the integrated results into the ``/raw`` group of an HDF5 file. This is the same file the pre-refinement and JAX refinement stages append to, so one dataset lives in one file end to end. :param h5path: Path to the HDF5 file, created if absent. :type h5path: str | pathlib.Path :param prefix: Acquisition prefix recorded as provenance. Defaults to "". :type prefix: str, optional :param crystal_dat: Name of the crystal ``.dat`` file recorded as provenance. Defaults to "". :type crystal_dat: str, optional :param date: Acquisition date. Defaults to today. :type date: str, optional .. py:method:: save_larbed(filename) Writes the integrated results as loose ``.npy`` files. :param filename: Path prefix. Four files are written, suffixed ``_Store_Larbed.npy``, ``_Store_LarbedDeconvolved.npy``, ``_Store_LarbedVariance.npy``, and ``_g_vectors.npy``. :type filename: str .. py:method:: out_larbed() Returns the integrated results as Python lists. :returns: ``(Store_Larbed, Store_bkg, Store_LarbedVariance, g_vectors)``. :rtype: tuple .. py:class:: LARBEDCalibration(file_name) Measures the detector point-spread function and the tilt-scan step length. The input is a reference scan taken with no sample and descan off, so only the direct beam is present and it walks across the detector. The usual sequence is :meth:`load_data`, :meth:`find_peaks`, :meth:`calculate_average_step_length`, :meth:`crop_peaks`, :meth:`interpolate`, and :meth:`calculate_mtf`. :ivar data: The 4D reference stack. :vartype data: np.ndarray :ivar peaks: Fitted (x, y) beam position at each scan step. :vartype peaks: np.ndarray :ivar average_step_length: Mean beam displacement per scan step, in detector pixels. :vartype average_step_length: float :ivar probe_image: The interpolated probe shape. :vartype probe_image: np.ndarray :ivar mtf_2d: The modulation transfer function, in the Fourier domain. :vartype mtf_2d: np.ndarray .. py:attribute:: file_name .. py:attribute:: data :value: [] .. py:attribute:: crop_data :value: [] .. py:attribute:: probe_image :value: None .. py:attribute:: mtf_2d :value: None .. py:attribute:: peaks :value: None .. py:attribute:: roi_size :value: None .. py:method:: load_data(type=0) Reads the reference scan and displays its middle pattern. :param type: Detector layout, passed to :func:`pyextal.larbed.empad.ReadRaw`. Defaults to 0. :type type: int, optional .. py:method:: meanimage() Averages the reference stack. :returns: The mean 2D pattern. :rtype: np.ndarray .. py:method:: find_peaks(start=1, end=255) Fits the beam position in every pattern of a square scan sub-region. :param start: First scan index of the sub-region. Defaults to 1. :type start: int, optional :param end: One past the last scan index. Defaults to 255. :type end: int, optional .. py:method:: crop_peaks(crop_size=20) Crops and sub-pixel aligns a patch around each fitted beam position. :param crop_size: Side length of each patch in pixels. Defaults to 20. :type crop_size: int, optional .. py:method:: calculate_average_step_length() Measures the mean beam displacement per scan step, in detector pixels. This is the quantity that converts the tilt scan into reciprocal space; combined with the detector calibration and the length of the x-axis reflection it gives the ``gl`` passed to the refinement stage. .. py:method:: interpolate() Rescales the averaged probe patch by the measured step length. .. py:method:: calculate_mtf(pad_size=256) Transforms the probe image into a normalised Fourier-domain MTF. The result is suitable for :func:`pyextal.LucyRichardson.lucy_Richardson` with ``mode='fourier'``. :param pad_size: Size the probe image is zero-padded to before the transform. Defaults to 256. :type pad_size: int, optional .. py:function:: ReadRaw(fname, type=0) Reads an EMPAD ``.raw`` file into a 4D array. :param fname: Path to the ``.raw`` file. :type fname: str | pathlib.Path :param type: 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. :type type: int, optional :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. :rtype: np.ndarray .. py:function:: AlignByBeam(matrix, x, y, crop=4, reiterate=1) Aligns every pattern in the stack on the beam nearest a given position. :param matrix: 4D diffraction stack. :type matrix: np.ndarray :param x: Target position along the first detector axis. :type x: int :param y: Target position along the second detector axis. :type y: int :param crop: Half-width of the search window around (x, y). Defaults to 4. :type crop: int, optional :param reiterate: Number of alignment passes. Defaults to 1. :type reiterate: int, optional :returns: The aligned stack. :rtype: np.ndarray .. py:function:: 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. :param matrix: 4D diffraction stack. :type matrix: np.ndarray :param center_x: Aperture centre along the first detector axis. :type center_x: int :param center_y: Aperture centre along the second detector axis. :type center_y: int :param radius: Aperture half-width in pixels. ``0`` gives a single-pixel (point) virtual detector. Defaults to 0. :type radius: int, optional :param bkg: 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). :type bkg: tuple[int, int], optional :param fit: ``(fi, fj, w, ...)`` from :func:`BackgroundFitWeights`; subtracts the fitted background instead of the annulus mean. Defaults to None. :type fit: tuple, optional :param clip: Set negative results to zero. Defaults to True. :type clip: bool, optional :returns: The background-subtracted rocking curve and the subtracted background, both shaped (scan_x, scan_y). :rtype: tuple[np.ndarray, np.ndarray] .. py:function:: 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 :func:`ApertureSum`. The per-pixel variance follows the detector noise model used elsewhere in pyextal (see :func:`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 :func:`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. :param matrix: 4D diffraction stack. :type matrix: np.ndarray :param center_x: Aperture centre along the first detector axis. :type center_x: int :param center_y: Aperture centre along the second detector axis. :type center_y: int :param g: Detector gain. Defaults to 1. :type g: float, optional :param m: Mixing factor. Defaults to 1. :type m: float, optional :param DQE: Detective quantum efficiency. Defaults to 1. :type DQE: float, optional :param radius: Aperture half-width in pixels. Defaults to 0. :type radius: int, optional :param bkg: Inner and outer radii of the background annulus, whose variance is propagated into the result. Ignored when `fit` is given. Defaults to (0, 0). :type bkg: tuple[int, int], optional :param varB: Per-pixel background (readout) variance. Defaults to 0.0, which reproduces the original PyLARBED behaviour. :type varB: float, optional :param fit: ``(fi, fj, w, ...)`` from :func:`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. :type fit: tuple, optional :param rho: 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). :type rho: np.ndarray, optional :returns: The variance of the rocking curve, shaped (scan_x, scan_y). :rtype: np.ndarray :raises ValueError: If `rho` is combined with the annulus-mean background. .. py:function:: 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. :param center_x: Aperture centre along the first detector axis. :type center_x: int :param center_y: Aperture centre along the second detector axis. :type center_y: int :param radius: Aperture half-width in pixels. :type radius: int :param ri: Inner radius of the fit annulus in pixels. :type ri: int :param ro: Outer radius of the fit annulus in pixels. :type ro: int :param order: Polynomial order, 0 (constant), 1 (plane) or 2 (quadratic). Defaults to 2. :type order: int, optional :param shape: Detector shape as (n_x, n_y). Defaults to ``DEFAULT_DETECTOR_SHAPE``. :type shape: tuple[int, int], optional :param exclude: Pixel positions of other reflections whose surroundings are left out of the fit. Defaults to (). :type exclude: Iterable[tuple[float, float]], optional :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. :rtype: 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. .. py:function:: 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 :func:`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. :param matrix: 4D diffraction stack (scan_x, scan_y, det_x, det_y). :type matrix: np.ndarray :param max_offset: Largest pixel offset ``R`` of the correlation kernel. Defaults to 3. :type max_offset: int, optional :param counts: Range of effective counts per pixel used. Defaults to (5, 100). :type counts: tuple[float, float], optional :param axis: Scan axis to difference along. Defaults to 1. :type axis: int, optional :param stride: Use every `stride`-th scan line across the other axis, to bound the cost. Defaults to 4. :type stride: int, optional :param n_samples: Random samples per chunk for the variance-versus-intensity fit. Defaults to 400000. :type n_samples: int, optional :param seed: Seed for that subsampling. Defaults to 0. :type seed: int, optional :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``). :rtype: dict :raises ValueError: If no intensity bin falls in the `counts` range. .. py:function:: AnnularSum(matrix, center_x, center_y, ri, ro) Averages counts within an annular aperture. :param matrix: 4D diffraction stack. :type matrix: np.ndarray :param center_x: Annulus centre along the first detector axis. :type center_x: int :param center_y: Annulus centre along the second detector axis. :type center_y: int :param ri: Inner radius in pixels. :type ri: int :param ro: Outer radius in pixels. :type ro: int :returns: Mean intensity per scan position, shaped (scan_x, scan_y). :rtype: np.ndarray .. py:function:: AnnularVar(matrix, center_x, center_y, g, m, DQE, ri, ro) Computes the variance of the intensity inside an annular aperture. :param matrix: 4D diffraction stack. :type matrix: np.ndarray :param center_x: Annulus centre along the first detector axis. :type center_x: int :param center_y: Annulus centre along the second detector axis. :type center_y: int :param g: Detector gain. Unused; kept for signature compatibility. :type g: float :param m: Mixing factor. Unused; kept for signature compatibility. :type m: float :param DQE: Detective quantum efficiency. Unused; kept for signature compatibility. :type DQE: float :param ri: Inner radius in pixels. :type ri: int :param ro: Outer radius in pixels. :type ro: int :returns: Variance across the annulus per scan position. :rtype: np.ndarray .. py:function:: Precession(matrix, center_x, center_y, ri, ro) Averages diffraction patterns over an annulus of scan positions. :param matrix: 4D diffraction stack. :type matrix: np.ndarray :param center_x: Annulus centre along the first scan axis. :type center_x: int :param center_y: Annulus centre along the second scan axis. :type center_y: int :param ri: Inner radius in scan steps. :type ri: int :param ro: Outer radius in scan steps. :type ro: int :returns: The averaged 2D diffraction pattern. :rtype: np.ndarray .. py:function:: Precession_Angled(matrix, center_x, center_y, ri, ro, angle1, angle2) Averages diffraction patterns over an angular sector of an annulus. :param matrix: 4D diffraction stack. :type matrix: np.ndarray :param center_x: Annulus centre along the first scan axis. :type center_x: int :param center_y: Annulus centre along the second scan axis. :type center_y: int :param ri: Inner radius in scan steps. :type ri: int :param ro: Outer radius in scan steps. :type ro: int :param angle1: Start of the angular range in degrees. :type angle1: float :param angle2: End of the angular range in degrees. :type angle2: float :returns: The averaged 2D diffraction pattern. :rtype: np.ndarray .. py:function:: FEM_StackAverage(matrix, center_x, center_y, Avg_img=None, rot_average=False) Computes a fluctuation electron microscopy variance image over the stack. :param matrix: 4D diffraction stack. :type matrix: np.ndarray :param center_x: Direct-beam position along the first detector axis. :type center_x: int :param center_y: Direct-beam position along the second detector axis. :type center_y: int :param Avg_img: Stack to average for the normalisation image. Defaults to None. :type Avg_img: np.ndarray, optional :param rot_average: Add a rotationally averaged contribution. Defaults to False. :type rot_average: bool, optional :returns: The FEM image. :rtype: np.ndarray .. py:function:: radial_profile(data, center) Computes the azimuthally averaged radial profile of an image. :param data: 2D image. :type data: np.ndarray :param center: Centre of the profile. :type center: tuple[int, int] :returns: Mean intensity as a function of integer radius. :rtype: np.ndarray .. py:function:: find_nearest_peak(vector, matrix, crop=4) Finds the brightest pixel within a window around a predicted position. :param vector: Predicted (x, y) position. :type vector: tuple[float, float] :param matrix: 2D image to search, typically the stack average. :type matrix: np.ndarray :param crop: Half-width of the search window. Defaults to 4. :type crop: int, optional :returns: Index of the brightest pixel. :rtype: tuple[int, int] .. py:function:: getGridVectors(x0, y0, x2, y2, x1, y1, order=3, shape=DEFAULT_DETECTOR_SHAPE) Builds a reciprocal-lattice grid from two basis vectors. :param x0: Direct-beam position along the first detector axis. :type x0: float :param y0: Direct-beam position along the second detector axis. :type y0: float :param x2: First basis-vector tip, first axis. :type x2: float :param y2: First basis-vector tip, second axis. :type y2: float :param x1: Second basis-vector tip, first axis. :type x1: float :param y1: Second basis-vector tip, second axis. :type y1: float :param order: Grid extent, giving indices -order..order along each basis vector. Defaults to 3. :type order: int, optional :param shape: Detector shape used to reject off-detector nodes. Defaults to (124, 124). :type shape: tuple[int, int], optional :returns: ``((i, j), (x, y))`` pairs of lattice index and pixel position. :rtype: list .. py:function:: 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. :param x0: Direct-beam position along the first detector axis. :type x0: float :param y0: Direct-beam position along the second detector axis. :type y0: float :param x1: First basis-vector tip, first axis. :type x1: float :param y1: First basis-vector tip, second axis. :type y1: float :param x2: Second basis-vector tip, first axis. :type x2: float :param y2: Second basis-vector tip, second axis. :type y2: float :param average_after: Averaged 2D pattern used to locate peaks. :type average_after: np.ndarray :param order: Grid extent along each basis vector. Defaults to 4. :type order: int, optional :param crop: Half-width of the peak search window. Defaults to 2. :type crop: int, optional :param shape: Detector shape used to reject off-detector nodes. Defaults to (124, 124). :type shape: tuple[int, int], optional :returns: ``((i, j), (x, y))`` pairs, with (x, y) snapped to observed peaks. :rtype: list .. py:function:: 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. :param x0: Direct-beam position along the first detector axis. :type x0: float :param y0: Direct-beam position along the second detector axis. :type y0: float :param x1: Tips of the four basis vectors. :type x1: float :param y1: Tips of the four basis vectors. :type y1: float :param x2: Tips of the four basis vectors. :type x2: float :param y2: Tips of the four basis vectors. :type y2: float :param x3: Tips of the four basis vectors. :type x3: float :param y3: Tips of the four basis vectors. :type y3: float :param x4: Tips of the four basis vectors. :type x4: float :param y4: Tips of the four basis vectors. :type y4: float :param order1: Extent along the first basis vector. Defaults to 3. :type order1: int, optional :param order2: Extent along the second basis vector. Defaults to 3. :type order2: int, optional :param order3: Extent along the third basis vector. Defaults to 3. :type order3: int, optional :param order4: Extent along the fourth basis vector. Defaults to 3. :type order4: int, optional :param shape: Detector shape used to reject off-detector nodes. Defaults to (124, 124). :type shape: tuple[int, int], optional :returns: ``((i, j, k, l), (x, y))`` pairs of lattice index and pixel position. :rtype: list .. py:function:: get_gvectors(g1, g2, grid, g3=None, g4=None) Converts lattice indices on a pixel grid into Miller indices. :param g1: Miller index of the first basis vector. :type g1: tuple[int, int, int] :param g2: Miller index of the second basis vector. :type g2: tuple[int, int, int] :param grid: Grid produced by :func:`getGridVectors`, :func:`getGridVectors_Adjust`, or :func:`getGridVectorsHOLZ`. :type grid: list :param g3: Miller index of the third basis vector. Supply together with ``g4`` for a four-vector (HOLZ) grid. Defaults to None. :type g3: tuple[int, int, int], optional :param g4: Miller index of the fourth basis vector. Defaults to None. :type g4: tuple[int, int, int], optional :returns: One Miller index per grid node. :rtype: list[tuple[int, int, int]]