pyextal.acom.library

Reflection libraries for a candidate orientation.

A library is the set of reflections a given zone axis puts on the detector, together with their in-plane coordinates and a kinematic strength. Building one costs only metric-tensor arithmetic, which is what makes searching over orientations affordable.

Classes

ReflectionLibrary

Reflections of one zone axis, projected onto the detector plane.

Functions

candidate_zone_axes([max_index])

Enumerates primitive low-index zone axes.

_enumerate_hkl(max_index)

All integer hkl in a cube, including (0,0,0).

frame_gmax(mapping, shape)

Largest |g| the detector can reach, in inverse Angstroms.

auto_max_index(gmx, gmax)

Miller search box big enough to reach gmax.

build_library(param, zone[, gmax, max_index, ...])

Builds the reflection library for one zone axis.

visible_holz(param, lib, mapping, shape[, smax, ...])

Finds higher-order-Laue-zone reflections that land on the detector.

min_laue_max(param, zone, gmax[, max_index, cover, limit])

Laue zones a zone axis needs before its library forms a 2D net.

nearest_zone_axis(param, direction[, max_index, tol_deg])

Rational zone axes closest to a beam direction.

Module Contents

class pyextal.acom.library.ReflectionLibrary

Reflections of one zone axis, projected onto the detector plane.

Variables:
  • zone (tuple[int, int, int]) – The zone axis, direct-space indices.

  • xaxis (tuple[int, int, int]) – Reflection defining the in-plane x axis.

  • hkl (np.ndarray) – (n, 3) int Miller indices.

  • xy (np.ndarray) – (n, 2) in-plane coordinates, inverse Angstroms.

  • gmag (np.ndarray) – (n,) |g| in inverse Angstroms.

  • laue (np.ndarray) – (n,) Laue zone index, g . zone; 0 is ZOLZ.

  • weight (np.ndarray) – (n,) kinematic strength |U_g|, 0 for the direct beam.

  • e1 (np.ndarray) – In-plane x unit vector, Cartesian.

  • e2 (np.ndarray) – In-plane y unit vector, Cartesian.

  • n (np.ndarray) – Zone-axis unit vector, Cartesian.

zone: tuple
xaxis: tuple
hkl: numpy.ndarray
xy: numpy.ndarray
gmag: numpy.ndarray
laue: numpy.ndarray
weight: numpy.ndarray
e1: numpy.ndarray
e2: numpy.ndarray
n: numpy.ndarray
__len__()
select(mask)

Returns a new library keeping only the reflections mask selects.

strong(rel=1e-06)

The subset whose kinematic weight is worth matching against.

Extinct and near-extinct reflections are dropped: they may or may not be visible depending on the multiple scattering, so requiring a peak at one, or allowing one to explain a peak, both mislead the orientation search. The direct beam is always kept.

Parameters:

rel (float, optional) – Keep |U_g| above this fraction of the strongest. Defaults to 1e-6.

Returns:

The filtered view.

Return type:

ReflectionLibrary

property zolz

Only the zero-order Laue zone.

Type:

ReflectionLibrary

pyextal.acom.library.candidate_zone_axes(max_index=3)

Enumerates primitive low-index zone axes.

Sign-equivalent axes ([uvw] and [-u-v-w]) give the same pattern, so only one of each pair is returned. Symmetry-equivalent axes are not removed: they score identically, and that degeneracy is a real property of the measurement worth reporting rather than hiding.

Parameters:

max_index (int, optional) – Largest absolute index considered. Defaults to 3.

Returns:

Candidate zone axes, shortest first.

Return type:

list[tuple[int, int, int]]

pyextal.acom.library._enumerate_hkl(max_index)

All integer hkl in a cube, including (0,0,0).

The direct beam belongs in the library: it anchors the detector centre, and the engine’s own beam list always carries it, so a .dat written from a library without it would be rejected.

pyextal.acom.library.frame_gmax(mapping, shape)

Largest |g| the detector can reach, in inverse Angstroms.

Parameters:
  • mapping (DetectorMap) – The fitted detector mapping.

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

Returns:

Distance from the direct beam to the furthest frame corner.

Return type:

float

pyextal.acom.library.auto_max_index(gmx, gmax)

Miller search box big enough to reach gmax.

A reflection (h00) has |g| = h / a, so the box has to grow with the longest cell edge. Too small a box silently truncates the outer reflections.

Parameters:
  • gmx (np.ndarray) – (3, 3) direct-space metric tensor.

  • gmax (float) – Largest |g| wanted, inverse Angstroms.

Returns:

Half-width of the index box.

Return type:

int

pyextal.acom.library.build_library(param, zone, gmax=2.0, max_index=8, laue_max=0, xaxis=None, weights=True, min_weight_rel=0.0)

Builds the reflection library for one zone axis.

Parameters:
  • param (SimParams) – Parsed simulation parameters; supplies the metric tensors and |K|.

  • zone (tuple[int, int, int]) – Zone axis, direct-space indices.

  • gmax (float, optional) – Largest |g| to include, inverse Angstroms. Defaults to 2.0.

  • max_index (int, optional) – Search box for Miller indices. Defaults to 8.

  • laue_max (int, optional) – Highest Laue zone to include. 0 keeps the ZOLZ only; 1 adds FOLZ, and so on. Defaults to 0.

  • xaxis (tuple[int, int, int], optional) – Reflection defining the in-plane x axis. The shortest in-plane reflection is chosen when None; the choice only sets where the fitted rotation angle is measured from.

  • weights (bool, optional) – Look up kinematic |U_g| for each reflection. Defaults to True.

  • min_weight_rel (float, optional) –

    Drop reflections whose |U_g| falls below this fraction of the strongest. Defaults to 0.0, i.e. keep everything.

    In electron diffraction a kinematically forbidden reflection still carries intensity, because multiple scattering routes electrons into it – SrTiO3 (100) is nearly extinct kinematically yet plainly visible. Such reflections therefore have to be predicted and integrated. They are useless for finding the orientation, though, since whether they appear is not something the kinematic weight can say; use ReflectionLibrary.strong() for the matching subset.

Returns:

Reflections with |g| <= gmax in the kept Laue zones.

Return type:

ReflectionLibrary

Raises:

ValueError – If the zone axis admits no in-plane reflection within gmax.

pyextal.acom.library.visible_holz(param, lib, mapping, shape, smax=0.05, laue_max=3, gmax=None, max_index=8)

Finds higher-order-Laue-zone reflections that land on the detector.

Run this once the ZOLZ orientation is settled. A HOLZ reflection is kept when its predicted pixel falls inside the frame and, if smax is given, when its excitation error is small enough for it to be excited at all.

Parameters:
  • param (SimParams) – Parsed simulation parameters.

  • lib (ReflectionLibrary) – The solved ZOLZ library; supplies zone and axes.

  • mapping (DetectorMap) – The fitted detector mapping.

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

  • smax (float, optional) – Largest |s_g| accepted, inverse Angstroms. Defaults to 0.05. Passing None drops the filter, which is rarely what you want: a reflection can project onto the frame while sitting far off the Ewald sphere, so the unfiltered count is dominated by beams that are never excited. For a zone axis the first Laue ring sits at roughly sqrt(2 |K| H), H being the reciprocal layer spacing – if that exceeds the detector’s reach, no HOLZ is excited at all.

  • laue_max (int, optional) – Highest Laue zone to consider. Defaults to 3.

  • gmax (float, optional) – Largest |g|; defaults to what the frame can hold.

  • max_index (int, optional) – Search box for Miller indices. Defaults to 8.

Returns:

The HOLZ reflections predicted to be on the frame.

Return type:

ReflectionLibrary

pyextal.acom.library.min_laue_max(param, zone, gmax, max_index=6, cover=0.8, limit=40)

Laue zones a zone axis needs before its library forms a 2D net.

A reflection’s Laue index is n = g . [uvw]. For a low-index axis the reflections inside a frame nearly all have n = 0, so the ZOLZ alone is a 2D net and laue_max=0 is right. For a high-index axis they are spread over many zones and the ZOLZ collapses: the [1,1,15] ZOLZ of SrTiO3 inside 1.2 A^-1 holds seven reflections, every one on the single row (n,-n,0). Fitting a 2D net to a 1D row is what produced a scale 1.4x too small and indexed (200) as (1,-1,0).

This returns the smallest laue_max whose library spans two dimensions out to cover of the requested gmax. Counting instead to gmax * |[uvw]| – the largest Laue index any reflection inside gmax can carry – gives 71 here rather than 6, because it counts reflections lying nearly along the zone axis, which project onto the pattern centre and form no part of the net.

Parameters:
  • param (SimParams) – Parsed simulation parameters.

  • zone (array-like) – (3,) zone axis, integer Miller indices.

  • gmax (float) – Reflection library extent, A^-1.

  • max_index (int, optional) – Miller index bound. Defaults to 6.

  • cover (float, optional) – Fraction of gmax both in-plane directions must reach. Defaults to 0.8.

  • limit (int, optional) – Give up above this. Defaults to 40.

Returns:

Smallest workable laue_max, or limit if none was found.

Return type:

int

Note

Raising laue_max weakens the zone-axis search in exchange. The ZOLZ restriction is what makes different axes predict different nets; a library carrying every Laue zone projects to something dense enough to match almost anything. Raise it to test a specific high-index axis against a solution you already have, not to widen a blind search.

pyextal.acom.library.nearest_zone_axis(param, direction, max_index=20, tol_deg=None)

Rational zone axes closest to a beam direction.

A pattern taken off zone has a beam direction that is not, in general, along any lattice row. Naming a high-index axis for it means finding the rational directions that come closest – and there are usually several at nearly the same angle, so this returns a ranked list rather than an answer.

Parameters:
  • param (SimParams) – Parsed simulation parameters.

  • direction (array-like) – (3,) beam direction in crystal coordinates, as zone tilted by the refined tiltX/tiltY. Need not be normalised or rational.

  • max_index (int, optional) – Largest index to enumerate. Defaults to 20.

  • tol_deg (float, optional) – Keep only axes within this angle. All are ranked when None.

Returns:

(uvw, angle_deg), closest first.

Return type:

list[tuple]

Note

This is bookkeeping, not a measurement. The projected net of a tilted pattern is the net of the low-index axis it is near, and every rational direction close to that axis reproduces the same reflections with the same Miller indices – verified on the SrTiO3 scan taken 4 degrees off [001], where [1,1,15] with enough Laue zones labelled all 23 shared peaks exactly as [001] did. What the geometry cannot do is say which of them the beam was along. That comes from excitation – a visible HOLZ ring, or the tilt that pyextal.optimize.CoarseOptimize refines against the dynamical simulation.