pyextal.superspace.structurefactor

The modulated structure factor.

This is item 2: the change to how sfact works. For an unmodulated crystal a structure factor is a sum over atoms. For a modulated one each atom is smeared over the internal coordinate, and the sum becomes a sum of integrals:

F(H) = sum_mu f_mu(s) T_mu(s) Int_[0,1]^d dxbar P_mu(xbar)

exp{ 2 pi i [ h . r_mu + m . xbar + S(H) . u_mu(xbar) ] }

with h the integer main index, m the satellite index, S(H) the effective, non-integral reciprocal index, and u_mu the displacement field. Setting every u = 0 and m = 0 collapses it to the ordinary expression, which is one of the tests.

Why quadrature rather than Bessel functions

For a single harmonic displacive modulation the integral has the closed form J_-m(2 pi S.A), and textbooks present it that way. That form does not survive several harmonics, or an occupational modulation multiplying a displacive one, and the published 1T-TaS2 refinement used 21 harmonics on Ta. The integrand is smooth and periodic, so the plain rectangle rule converges exponentially in the number of points – there is no accuracy left on the table, and it handles every case with one code path. bessel_reference() provides the closed form so the quadrature can be checked against it.

The one thing that will silently go wrong is an under-resolved grid: the integrand’s bandwidth grows with satellite order and with modulation amplitude, and too few points aliases high harmonics onto low ones. auto_n_internal() sizes the grid from both, and structure_factor() uses it by default.

Not implemented: modulated ADPs. B is a constant per atom. A modulated ADP would slot into the integrand next to P, but nothing here needs it yet and an untested implementation is worse than an honest gap.

Functions

internal_grid(d, n)

Uniform sampling of the internal unit cell [0, 1)^d.

auto_n_internal(atoms, hkl, max_order[, safety, lo, hi])

Choose a grid fine enough that the quadrature does not alias.

structure_factor(lat, atoms, H[, kind, n_internal, ...])

Structure factors of a modulated structure.

bessel_reference(lat, H, amplitude[, element, ...])

Closed-form structure factor for one atom with a single harmonic.

Module Contents

pyextal.superspace.structurefactor.internal_grid(d, n)

Uniform sampling of the internal unit cell [0, 1)^d.

Parameters:
  • d (int) – Number of modulation dimensions.

  • n (int) – Points per dimension.

Returns:

(n**d, d) internal coordinates.

Return type:

np.ndarray

pyextal.superspace.structurefactor.auto_n_internal(atoms, hkl, max_order, safety=4, lo=16, hi=256)

Choose a grid fine enough that the quadrature does not alias.

The integrand carries exp(2 pi i m.xbar), contributing bandwidth max|m|, and exp(2 pi i S.u(xbar)), whose Jacobi-Anger expansion has significant terms out to roughly |z| + 4 in each harmonic present, where z = 2 pi max|S.u|. The rectangle rule is exact below n/2, so the grid is sized at safety times the estimated bandwidth.

Parameters:
  • atoms (list[ModulatedAtom]) – Supplies the modulation amplitudes.

  • hkl (np.ndarray) – (n, 3) effective reciprocal indices.

  • max_order (int) – Largest satellite order in the reflection set.

  • safety (int) – Multiplier on the estimated bandwidth.

  • lo (int) – Floor on the returned value.

  • hi (int) – Ceiling, to keep n**d affordable.

Returns:

Points per internal dimension, rounded up to even.

Return type:

int

pyextal.superspace.structurefactor.structure_factor(lat, atoms, H, kind='electron', n_internal=None, max_points=4000000, s_floor=1e-05)

Structure factors of a modulated structure.

Parameters:
  • lat (SuperspaceLattice) – Supplies the metric and the q-vectors.

  • atoms (list[ModulatedAtom]) – The structure, listed in P1 – every atom of the basic cell, not just the asymmetric unit.

  • H (array-like) – (n, 3+d) integer superspace indices.

  • kind (str) – 'electron' for Mott-Bethe scattering factors, matching the Bloch-wave engine, or 'xray' for the Cromer-Mann values.

  • n_internal (int, optional) – Points per internal dimension. Sized by auto_n_internal() when None.

  • max_points (int) – Reflections are processed in chunks so that chunk * n_internal**d stays under this, bounding memory.

  • s_floor (float) – Lower clamp on s = |g|/2, as the engine uses.

Returns:

(n,) complex structure factors, in the units of the chosen scattering factor.

Return type:

np.ndarray

Raises:

ValueError – If kind is not recognised, or H has the wrong width.

pyextal.superspace.structurefactor.bessel_reference(lat, H, amplitude, element='TA', position=(0.0, 0.0, 0.0), kind='electron', s_floor=1e-05)

Closed-form structure factor for one atom with a single harmonic.

For u(xbar) = A sin(2 pi xbar) in (3+1)D, Jacobi-Anger gives

Int_0^1 exp{2 pi i [m xbar + S.A sin(2 pi xbar)]} dxbar = J_-m(2 pi S.A)

exactly. This is the analytic check on the quadrature in structure_factor(); see test_superspace.py.

Parameters:
  • lat (SuperspaceLattice) – Must be (3+1)-dimensional.

  • H (array-like) – (n, 4) superspace indices.

  • amplitude (array-like) – (3,) sine amplitude in fractional coordinates.

  • element (str) – Element symbol for the scattering factor.

  • position (array-like) – (3,) fractional position of the single atom.

  • kind (str) – 'electron' or 'xray'.

  • s_floor (float) – Lower clamp on s.

Returns:

(n,) complex structure factors.

Return type:

np.ndarray

Raises:

ValueError – If lat is not (3+1)-dimensional.