pyextal.superspace.symmetry

Superspace symmetry operations.

A (3+d) superspace operation is a 3D operation (R | tau) together with its action (epsilon | tau_i) on the internal coordinates. The internal part is not free: it is determined by R through how R rearranges the modulation wave vectors,

R^T q_j = sum_k epsilon_jk q_k + G_j, G_j a reciprocal lattice vector,

and epsilon_for() solves that for epsilon and G. Getting this wrong is a quiet failure – the structure factors come out plausible and the symmetry-equivalent reflections simply do not match.

Scope

transform_reflection() is general: the G_j terms fold cleanly into the main indices. expand_atoms() is not – it is implemented only for operations with every G_j = 0, because a nonzero G_j makes the phase relation between symmetry-equivalent modulation functions depend on the atom’s own position, which needs the full superspace-group machinery to get right. It raises rather than guessing. For a structure where that bites, list the atoms in P1; that is what tas2 does.

Classes

SuperspaceOperation

One symmetry operation of a (3+d) superspace group.

Functions

epsilon_for(R, q[, max_epsilon, tol])

Solve R^T q_j = sum_k epsilon_jk q_k + G_j for integers.

transform_reflection(op, H)

Apply a superspace operation to reflection indices.

expand_atoms(atoms, ops, lat[, tol])

Generate the symmetry-equivalent atoms and their modulation functions.

Module Contents

class pyextal.superspace.symmetry.SuperspaceOperation

One symmetry operation of a (3+d) superspace group.

Variables:
  • R (np.ndarray) – (3, 3) integer rotation on direct fractional coordinates.

  • tau (np.ndarray) – (3,) translation part, fractional.

  • epsilon (np.ndarray) – (d, d) integer action on internal coordinates.

  • tau_i (np.ndarray) – (d,) internal translation.

  • G (np.ndarray) – (d, 3) integer reciprocal lattice vectors from the q-transformation; all zero for the well-behaved settings.

  • name (str) – Optional label.

R: numpy.ndarray
tau: numpy.ndarray = None
epsilon: numpy.ndarray = None
tau_i: numpy.ndarray = None
G: numpy.ndarray = None
name: str = ''
__post_init__()
property d

Number of modulation dimensions.

Type:

int

resolve(lat, max_epsilon=3, tol=1e-06)

Fill in epsilon and G from the lattice, if not already given.

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

  • max_epsilon (int) – Half-width of the integer search for epsilon.

  • tol (float) – Tolerance on the residual being a lattice vector.

Returns:

A new operation with the internal part filled in.

Return type:

SuperspaceOperation

Raises:

ValueError – If R does not map the q-set onto itself.

pyextal.superspace.symmetry.epsilon_for(R, q, max_epsilon=3, tol=1e-06)

Solve R^T q_j = sum_k epsilon_jk q_k + G_j for integers.

A 3D operation is only a symmetry of a modulated structure if it permutes the Fourier module, which is what this checks. The search is a small exhaustive one over integer epsilon rows – d <= 3 and the entries are tiny, so there are at most a few hundred candidates.

The decomposition is not unique when the q-vectors are dependent modulo the main lattice, which is exactly the 1T-TaS2 case: with three CDW arms satisfying q1 + q2 + q3 = c*, any solution can be shifted by a multiple of that relation. The smallest one is returned – minimum |epsilon| first, then minimum |G| – which picks out the plain permutation with G = 0 rather than some equivalent monster. Taking whichever candidate an unordered search happened to reach first would make the answer depend on iteration order.

Parameters:
  • R (array-like) – (3, 3) integer rotation on direct fractional coordinates.

  • q (array-like) – (d, 3) modulation wave vectors.

  • max_epsilon (int) – Half-width of the integer search per component.

  • tol (float) – How close the residual has to be to an integer vector.

Returns:

(epsilon, G), shapes (d, d) and (d, 3).

Return type:

tuple[np.ndarray, np.ndarray]

Raises:

ValueError – If some R^T q_j cannot be written this way, meaning R is not a symmetry of the modulated structure.

pyextal.superspace.symmetry.transform_reflection(op, H)

Apply a superspace operation to reflection indices.

From R^T S(H) = (R^T h + G^T m) + sum_k (epsilon^T m)_k q_k, so the main part picks up the G contribution and the satellite part transforms by epsilon^T. Valid for any G.

Parameters:
  • op (SuperspaceOperation) – Operation with its internal part resolved.

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

Returns:

(n, 3+d) transformed indices.

Return type:

np.ndarray

Raises:

ValueError – If op has no internal part, or the widths disagree.

pyextal.superspace.symmetry.expand_atoms(atoms, ops, lat, tol=1e-06)

Generate the symmetry-equivalent atoms and their modulation functions.

Under (R | tau) an atom moves to R r + tau and its displacement field becomes u'(xbar) = R u(epsilon^-1 (xbar - tau_i)). Written out in harmonics, the index maps as n -> epsilon^-T n and the amplitudes mix by the phase phi = 2 pi n' . tau_i:

A' = R (A cos phi + B sin phi)
B' = R (-A sin phi + B cos phi)
Parameters:
  • atoms (list[ModulatedAtom]) – The asymmetric unit.

  • ops (list[SuperspaceOperation]) – Operations, resolved or not.

  • lat (SuperspaceLattice) – Supplies the q-vectors for resolving.

  • tol (float) – Tolerance for recognising a duplicate position.

Returns:

The expanded set, duplicates removed.

Return type:

list[ModulatedAtom]

Raises:

NotImplementedError – If any operation has a nonzero G. See the module docstring – list the structure in P1 instead.