Lattice reduction (Niggli)

Niggli reduction puts a fully three-dimensional periodic cell into a canonical primitive lattice setting. The operation changes the basis vectors, but not the lattice they span. It is useful when cells need to be compared by lattice shape or passed to algorithms that expect a reduced cell.

Exactness

niggli_reduce() reads the exact rational Gram matrix returned by metric(). The Krivý–Gruber steps use epsilon = 0: every comparison, including boundary equalities and special conditions, is an exact comparison of fractions.Fraction values. Recognition is a separate, tolerant operation; Niggli reduction is not.

The input must have periodicity == (True, True, True). A slab, wire, or molecular frame is not a lattice in all three directions and is refused.

The transform convention

Cell vectors are rows. If C = result.transform, then

basis_reduced = C * basis_original

C is an integer matrix with determinant +1. For row-vector fractional coordinates, Cartesian positions are preserved by

coordinates_reduced = coordinates_original * C.inv()

The structure-level niggli_reduced() operation applies that remapping exactly and wraps the coordinates into [0, 1).

Cell and structure use

Reduce a cell directly:

>>> from httk.atomistic import Cell, niggli_reduce, is_niggli_reduced
>>> result = niggli_reduce(Cell([[1, 0, 0], [0, 1, 0], [1, 0, 1]]))
>>> result.transform.det().to_fraction()
Fraction(1, 1)
>>> is_niggli_reduced(result.cell)
True

For a structure, use niggli_reduced(structure). Site order and count are unchanged, species, charge, composition, assemblies, and Cartesian site moments are carried through, and recorded cell and coordinate precision is widened by the exact induced matrix norms.

To compare the original and reduced structures with the package’s same-basis same_crystal() predicate, transform the reduced structure back first:

from httk.atomistic import UnitcellStructureView, build_supercell, niggli_reduced, same_crystal

reduced = niggli_reduced(original)
inverse = reduced.transform.inv()
restored = build_supercell(reduced.structure, inverse)
same_crystal(UnitcellStructureView(original), UnitcellStructureView(restored.structure))

The algorithm follows I. Krivý and B. Gruber, “A unified algorithm for determining the reduced (Niggli) cell”, Acta Crystallographica Section A 32 (1976), 297–298, and the exact change-of-basis and stabilization formulation described by R. W. Grosse-Kunstleve, N. K. Sauter, and P. D. Adams, “Numerically stable algorithms for the computation of reduced unit cells”, Acta Crystallographica Section A 60 (2004), 1–6.