Asymmetric units¶
A crystal is mostly repetition. ASUStructure represents the part that is not:
a space group, one site per symmetry orbit, and the values of that site’s free
parameters. Expanding it produces the full unit cell; recognizing a full cell
produces it back.
from httk.atomistic import UnitcellStructureView
from httk.core import load
asu = load("nacl.cif") # a CIF's native, declared symmetry
structure = UnitcellStructureView(asu) # the full cell, exactly
ASUStructure is part of StructureLike, so it can be passed anywhere a
structure is accepted, and expansion is exact and lazy — reading the space
group never generates the cell.
Canonicalization¶
Two crystals that are the same up to origin, cell-basis choice, site order, or
setting have many ASUStructure descriptions. canonicalize collapses that
freedom exactly: given an ASUStructure it returns the single deterministic,
highest-symmetry representative, using only exact rational arithmetic.
from httk.atomistic import canonicalize
result = canonicalize(asu) # exact input, exact answer
canonical = result.asu
An exact P1 supercell — any multiplicity, diagonal or sheared — is collapsed to
its unique primitive description before the search, so canonicalize returns the
same answer whichever cell you hand it. That collapse fires only on exact
rational invariance; a noisy supercell whose copies merely nearly coincide is
snapped instead by canonical_asu below, within its tolerance.
For measured input — coordinates carrying noise — use canonical_asu, the
one-liner that recognizes the symmetry within a tolerance (with spglib) and then
canonicalizes the result exactly:
from httk.atomistic import canonical_asu
from httk.core import load
asu = canonical_asu(load("measured.cif")) # noisy input, canonical answer
It sweeps recognition over a few tolerance multiples and keeps the highest-symmetry model whose atoms still sit within the base tolerance of the input, so a boundary flip can be rescued without accepting extra noise. It always works from the coordinates, so it can raise a declared symmetry the geometry supports — or lower one it does not, at the derived tolerance.
By default (lift=False) it returns the canonical representative of the
recognized symmetry: fully deterministic, all representational freedom removed,
and cheap — the cost is essentially recognition. It does not hunt for
pseudosymmetry above what recognition found. Pass lift=True to additionally run
the exact upward search for higher symmetry the recognition missed; that is exact
too but can be slow — minutes and beyond for low-symmetry, many-atom cells. Use
the default for bulk sweeps over many structures; use lift=True when you are
specifically hunting the maximal (pseudo)symmetry of one crystal.
Only the recognition step is floating-point: which symmetry is accepted near a tolerance boundary can vary across platforms or spglib builds, but the exact canonicalization erases spglib’s representational freedom, so how an accepted symmetry is represented never does. Free-parameter values are least-squares fits of the measured coordinates: two noisy measurements of the same crystal reach the same Wyckoff choices but slightly different rational parameter values.
From the command line¶
The same operations are available as httk symmetry, taking a structure file
(CIF, POSCAR) and printing a human-readable report; -o saves the result.
$ httk symmetry info nacl.cif # declared group, cell, Wyckoff occupation
$ httk symmetry info measured.poscar --recognize # also recognize symmetry from the geometry
$ httk symmetry canonicalize nacl.cif -o out.cif # canonical form of noisy input (spglib), saved
$ httk symmetry canonicalize nacl.cif --exact # exact, spglib-free (needs declared symmetry)
$ httk symmetry representations nacl.cif --target 166 # list distinct forms in a related group
canonicalize defaults to the tolerant canonical_asu path (--lift searches
upward for higher pseudosymmetry); --exact runs the exact canonicalize on
input that already carries declared symmetry. rerepresent --target N re-expresses
one crystal in a reachable group. Every subcommand accepts --tolerance X (a
Cartesian distance) and reports operator errors — a missing spglib, an unrelated
target — to stderr with a nonzero exit.
The full guide, Asymmetric units in detail, covers what an asymmetric unit holds, arbitrary and untabulated settings, the exactness contract of expansion, tolerance-bearing recognition, round-tripping, reading CIFs, serving symmetry over OPTIMADE, and the symmetry tables.