pyextal.superspace.saed¶
Kinematic zone-axis diffraction patterns from a modulated structure.
Item 4: the SAED-like pattern. Intensities are |F(H)|^2 weighted by a
relrod shape factor, which is what decides whether a satellite is actually
visible: a reflection sitting out of the zero-order Laue zone contributes only
in so far as its relrod, of length ~1/t for a specimen of thickness t, reaches
the Ewald sphere.
That weighting is not decoration. 1T-TaS2’s commensurate CDW has a 1/3 c*
component, which puts every first-order satellite out of the [001] ZOLZ; whether
they show up in a real pattern is entirely a question of thickness and
excitation error. Plotting |F|^2 alone would draw them at full strength and
say something false about the experiment.
The projection geometry is borrowed read-only from pyextal.acom.geometry,
which already gets the handedness right and needs no changes for this: its
helpers take float reflection indices, and a satellite’s effective index is
exactly that.
Classes¶
A computed zone-axis pattern. |
Functions¶
|
Compute a kinematic zone-axis pattern. |
|
In-plane coordinates of superspace reflections at a zone axis. |
|
Marker areas for a diffraction pattern, saturating the direct beam. |
|
Draw a pattern, distinguishing main reflections from satellite orders. |
|
The pseudo-Voigt point spread the JAX refinement fits, on a pixel grid. |
|
Render a pattern as a 2D intensity image, the way a plate records it. |
|
The intensity |
|
Draw a rendered pattern as an image. |
Module Contents¶
- class pyextal.superspace.saed.DiffractionPattern(H, xy, intensity, order, sg, F, zone, xaxis, satlen=None)¶
A computed zone-axis pattern.
- Variables:
H (np.ndarray) – (n, 3+d) superspace indices.
xy (np.ndarray) – (n, 2) in-plane coordinates, inverse Angstrom.
intensity (np.ndarray) – (n,) relative intensities, normalised so the strongest diffracted beam is 1. The direct beam is far above that and is meant to be clipped when plotted.
order (np.ndarray) – (n,) satellite order, ranked by modulation length so symmetry-equivalent satellites share it. See
indexing.satlen (np.ndarray) – (n,)
|sum_j m_j q_j|in inverse Angstrom.sg (np.ndarray) – (n,) excitation errors, inverse Angstrom.
F (np.ndarray) – (n,) complex structure factors.
zone (tuple) – The zone axis.
xaxis (tuple) – The in-plane reference reflection.
- satlen¶
- property index_sum¶
(n,)
sum |m_j|; not symmetry-invariant, see order.- Type:
np.ndarray
- __len__()¶
- __repr__()¶
- visible(rel=0.0001)¶
Reflections above a relative intensity threshold.
The threshold is measured against the strongest diffracted beam, not the direct one – intensities are already normalised that way, and the direct beam is orders of magnitude above it, so scaling to that would cut everything.
- Parameters:
rel (float) – Fraction of the strongest diffracted intensity.
- Returns:
Boolean mask.
- Return type:
np.ndarray
- brightest(n=20, exclude_origin=True)¶
Indices of the strongest reflections, for a quick look at a list.
- Parameters:
n (int) – How many to return.
exclude_origin (bool) – Skip the direct beam, which dominates.
- Returns:
Indices into the pattern arrays.
- Return type:
np.ndarray
- pyextal.superspace.saed.saed_pattern(lat, atoms, zone=(0, 0, 1), gmax=1.6, max_index=8, max_order=4, thickness=200.0, hv=200.0, xaxis=None, kind='electron', sg_max=None, n_internal=None, condition=None, merge=None)¶
Compute a kinematic zone-axis pattern.
- Parameters:
lat (SuperspaceLattice) – The superspace lattice.
atoms (list[ModulatedAtom]) – Structure, listed in P1.
zone (tuple) – Zone axis, direct-space indices.
gmax (float) – Resolution limit, inverse Angstrom.
max_index (int) – Half-width of the
(h, k, l)search box.max_order (int) – Largest satellite order.
thickness (float) – Specimen thickness in Angstrom, setting the relrod length
1/t. Larger means a thinner slice of reciprocal space contributes, so out-of-zone satellites fade.hv (float) – Accelerating voltage in kV, for the Ewald sphere radius.
xaxis (tuple, optional) – In-plane reference reflection; the shortest in-plane main reflection when None.
kind (str) –
'electron'or'xray'scattering factors.sg_max (float, optional) – Discard reflections with
|s_g|above this.5 / thicknesswhen None, past which the relrod contributes essentially nothing.n_internal (int, optional) – Internal quadrature grid; auto when None.
condition (callable, optional) – Reflection condition, passed to
enumerate_reflections().merge (bool, optional) – Add the structure factors of superspace indices that are the same physical reflection before squaring. Only commensurate lattices have any, and for them it is not optional – the intensity there is
|F_1 + F_2|^2, and treating the indices separately splits it arbitrarily and destroys the pattern’s symmetry. Decided fromSuperspaceLattice.is_commensurate()when None, which is the right answer either way.
- Returns:
The pattern.
- Return type:
- Raises:
ValueError – If the zone admits no in-plane reflection within gmax.
- pyextal.superspace.saed.project_pattern(lat, H, zone=(0, 0, 1), xaxis=None)¶
In-plane coordinates of superspace reflections at a zone axis.
Shared by every route that produces a pattern – kinematic, the superspace engine, the exact supercell – so their images can be laid side by side and compared pixel for pixel. Two routes computing the same reflection at slightly different coordinates would be a subtle and very annoying bug.
- Parameters:
lat (SuperspaceLattice) – The lattice.
H (array-like) – (n, 3+d) superspace indices.
zone (tuple) – Zone axis, direct-space indices.
xaxis (tuple, optional) – In-plane reference reflection. The shortest in-plane main reflection when None, which keeps the axis in the basic structure’s terms so it survives a change of q.
- Returns:
(xy, xaxis)with xy (n, 2) in inverse Angstrom.- Return type:
tuple[np.ndarray, tuple]
- Raises:
ValueError – If the zone admits no in-plane main reflection.
- pyextal.superspace.saed.marker_sizes(intensity, size=260, gamma=0.35, floor=1.5)¶
Marker areas for a diffraction pattern, saturating the direct beam.
Two things make a linear area-to-intensity map useless here. The direct beam is normalised against the strongest diffracted beam and the Mott-Bethe factor diverges as
s -> 0, so its “intensity” is of order 1e7 – scaled linearly it becomes a single marker larger than the axes, and the whole plot comes out a solid block of colour. And satellites are ~1e-3 of a main reflection, so anything that renders the mains sensibly renders the satellites as nothing.Intensity is therefore clipped at 1 (the direct beam saturates, exactly as it does on a recorded plate) and raised to gamma, the power stretch normally used to display a diffraction pattern.
- Parameters:
intensity (array-like) – Relative intensities, mains near 1.
size (float) – Marker area for a saturated reflection.
gamma (float) – Power stretch. 1.0 is linear; smaller lifts weak reflections into view.
floor (float) – Smallest marker area, so a very weak reflection is still a visible dot rather than nothing.
- Returns:
Marker areas in points squared.
- Return type:
np.ndarray
- pyextal.superspace.saed.plot_pattern(pattern, ax=None, size=260, rel_min=0.0001, label_orders=True, colors=None, annotate=0, title=None, gamma=0.35)¶
Draw a pattern, distinguishing main reflections from satellite orders.
Marker area is a power-stretched, saturating function of intensity; see
marker_sizes()for why a linear map does not work.- Parameters:
pattern (DiffractionPattern) – What to draw.
ax (matplotlib.axes.Axes, optional) – Target axes; a new figure when None.
size (float) – Marker area for a saturated reflection.
rel_min (float) – Relative intensity below which a reflection is dropped.
label_orders (bool) – Show a legend keyed by satellite order.
colors (dict, optional) – Satellite order -> colour.
annotate (int) – Label this many of the strongest reflections with their superspace index. Zero draws none.
title (str, optional) – Axes title.
gamma (float) – Power stretch on intensity; smaller lifts the satellites.
- Returns:
The axes drawn on.
- Return type:
matplotlib.axes.Axes
- pyextal.superspace.saed.pseudo_voigt_kernel(radius, sigma, gamma)¶
The pseudo-Voigt point spread the JAX refinement fits, on a pixel grid.
Identical in form to
jaxEngine.postProcess.create_pseudo_voigt_kernel, including the Thompson-Cox-Hastings width combination and the eta approximation, so a sigma and gamma refined against experimental data can be handed straight torender_image()and mean the same thing. Widths are in pixels.The kernel is tapered to zero on a disc rather than left square. Its Lorentzian part decays only as 1/r^2, so at any affordable radius it is still a per cent or so of the peak at the edge – and truncating that squarely puts a visible square halo around every strong reflection in the convolved image. A raised-cosine taper over the outer quarter removes it and costs nothing, since the discarded intensity is renormalised back.
- Parameters:
radius (int) – Kernel half-width; the kernel is (2r+1) square, with the corners tapered away.
sigma (float) – Gaussian width, pixels.
gamma (float) – Lorentzian width, pixels.
- Returns:
(2r+1, 2r+1) kernel summing to 1.
- Return type:
np.ndarray
- pyextal.superspace.saed.render_image(pattern, shape=(512, 512), extent=None, sigma=2.0, gamma=1.0, radius=None, rel_min=0.0, include_direct=True)¶
Render a pattern as a 2D intensity image, the way a plate records it.
A scatter plot draws markers whose area someone chose; this puts intensity on a pixel grid, so the result is an image in the same sense as the engine’s own diffraction output – something you can threshold, integrate, line-scan or compare against a recorded pattern.
Each reflection’s intensity is spread bilinearly over the four pixels around its position, so a peak that lands between pixels keeps its centroid and its total counts rather than snapping to a grid point. The whole image is then convolved with the pseudo-Voigt of
pseudo_voigt_kernel().The direct beam is orders of magnitude above everything else, so plot the result on a log or power scale –
plot_image()does.- Parameters:
pattern (DiffractionPattern) – What to render.
shape (tuple) –
(rows, cols)in pixels.extent (float, optional) – Half-width of the field in inverse Angstrom. Taken from the pattern, with a margin, when None.
sigma (float) – Gaussian width of the point spread, in pixels.
gamma (float) – Lorentzian width, in pixels.
radius (int, optional) – Kernel half-width.
ceil(6*max(sigma, gamma))when None – generous because the Lorentzian component has long tails;pseudo_voigt_kernel()tapers whatever is left.rel_min (float) – Drop reflections below this fraction of the strongest diffracted beam before rendering.
include_direct (bool) – Render the direct beam. It saturates everything; turning it off is often the more readable picture.
- Returns:
The
(rows, cols)image, and the(left, right, bottom, top)extent in inverse Angstrom, ready forimshow(..., extent=...).- Return type:
tuple[np.ndarray, tuple]
- pyextal.superspace.saed.saturation_level(pattern, shape=(512, 512), extent=None, sigma=2.0, gamma=1.0, radius=None, rel_min=0.0)¶
The intensity
plot_image()clips at when it chooses for itself.The peak of the strongest diffracted beam once rendered and convolved. Useful for putting two patterns on one scale: plot_image picks this separately for each, so two panels drawn side by side are each normalised to their own maximum and their brightnesses cannot be compared. Compute it once and pass it to both as vmax:
level = saturation_level(kinematic, shape=(650, 650), sigma=3.0) plot_image(kinematic, vmax=level, shape=(650, 650), sigma=3.0) plot_image(dynamical, vmax=level, shape=(650, 650), sigma=3.0)
The render arguments must match the ones plot_image is given, since the convolution spreads a fixed number of counts and the peak height therefore depends on the grid and the kernel.
- Parameters:
pattern (DiffractionPattern) – The pattern.
shape (tuple) – Image size in pixels.
extent (float, optional) – Half-width of the field, inverse Angstrom.
sigma (float) – Gaussian width of the point spread, pixels.
gamma (float) – Lorentzian width, pixels.
radius (int, optional) – Kernel half-width.
rel_min (float) – Intensity cutoff before rendering.
- Returns:
Peak image intensity excluding the direct beam.
- Return type:
float
- pyextal.superspace.saed.plot_image(pattern, ax=None, shape=(512, 512), extent=None, sigma=2.0, gamma=1.0, radius=None, stretch=0.4, cmap='inferno', include_direct=True, saturate=True, title=None, rel_min=0.0, colorbar=False, vmax=None)¶
Draw a rendered pattern as an image.
Two things have to be handled or the picture is a black square with one white dot. The direct beam is some seven orders of magnitude above the strongest diffracted beam, so the display is scaled to the diffracted maximum and the centre is allowed to saturate – which is what a recorded plate does anyway. And satellites are ~1e-3 of a main reflection, so a power stretch lifts them into view.
- Parameters:
pattern (DiffractionPattern) – What to draw.
ax (matplotlib.axes.Axes, optional) – Target axes.
shape (tuple) – Image size in pixels.
extent (float, optional) – Half-width of the field, inverse Angstrom.
sigma (float) – Gaussian width of the point spread, pixels.
gamma (float) – Lorentzian width, pixels.
radius (int, optional) – Kernel half-width.
stretch (float) – Display power. 1.0 is linear; smaller lifts the weak reflections. 0.4 shows satellites without washing out the mains.
cmap (str) – Colormap.
include_direct (bool) – Render the direct beam at all.
saturate (bool) – Scale the display to the strongest diffracted beam and let the direct beam clip. Turning this off scales to the true maximum, which is the direct beam, and shows little else.
title (str, optional) – Axes title.
rel_min (float) – Intensity cutoff before rendering.
colorbar (bool) – Add a colorbar keyed to the saturation level.
vmax (float, optional) –
Intensity that maps to full scale, in the units of the returned image; everything above it clips. Chosen automatically when None – the strongest diffracted beam, which is what saturate describes.
Pass it to brighten (smaller) or dim (larger) a plot, and above all to put several patterns on one scale: the automatic choice is made per pattern, so side-by-side panels are otherwise each normalised to their own maximum and cannot be compared.
saturation_level()computes the automatic value without drawing anything.
- Returns:
(ax, image)with image the unscaled intensity array.- Return type:
tuple
- Raises:
ValueError – If vmax is not positive.