Periodic systems

vibe-qc supports 1D, 2D, and 3D periodicity through a single PeriodicSystem type. Dimensionality is specified by dim {1, 2, 3}; the lattice matrix is always a full-rank 3×3.

See also

  • periodic_methods.md - comparative tour of vibe-qc’s periodic-SCF kernels (BIPOLE, GDF, GPW/GAPW), a bonding-organised example gallery, and the end-to-end surface-reactions workflow.

  • crystal_lattices.md — the 14 Bravais lattices, with visualisations + worked examples for each common lattice (rocksalt, diamond, perovskite, HCP, wurtzite, rutile, corundum, α-quartz, graphene).

  • k_points.md — Monkhorst-Pack mesh generation and Brillouin-zone sampling.

  • multi_k_scf.md — multi-k SCF: current ship state (Γ-only native GDF), parity target, scope caveats.

  • ewald.md — Ewald summation for periodic Coulomb.

Born-von Karman PBC contract

The periodic stack follows the same lattice-general Born-von Karman boundary-condition model used by CRYSTAL-style Gaussian crystal codes:

  • The input lattice is an arbitrary full-rank matrix whose columns are the Cartesian direct-lattice vectors in bohr. There are no cubic-, hexagonal-, or orthorhombic-only branches in the public periodic method interfaces.

  • sysp.reciprocal_lattice() is generated automatically as \(2\pi A^{-T}\), so \(a_i \cdot b_j = 2\pi\delta_{ij}\) for triclinic cells just as for cubic cells.

  • Monkhorst-Pack, Gamma-centred, and explicit k-lists are stored in fractional reciprocal coordinates and converted through that reciprocal matrix.

  • Real-space lattice sums enumerate integer Born-von Karman image vectors and then apply Cartesian cutoff / screening tolerances. Tight production crystals should use the GDF/FFTDF track; direct truncated sums remain a debug/reference path.

  • Bravais centering is represented either by primitive vectors or by additional basis atoms in a conventional cell. The Coulomb, k-point, GDF, FFT, and SCF interfaces should not special-case P, I, F, C, or R centering labels.

Regression coverage pins this contract on representative triclinic, monoclinic, orthorhombic, tetragonal, trigonal, hexagonal, and cubic metrics, plus CRYSTAL-style target structures: FCC diamond, BCC Fe, hexagonal graphene, and Al2O3 stoichiometry in a skew hexagonal cell. The native Γ-GDF RHF/RKS path is also smoke-tested on those 3D crystal metrics and on non-orthogonal 1D / 2D embedding cells.

import numpy as np
from vibeqc import Atom, PeriodicSystem

# 1D chain — column 0 of lattice is the lattice vector, the other two
# columns are implicit vacuum axes with enough separation to decouple
# images.
sysp_1d = PeriodicSystem(
    dim=1,
    lattice=np.diag([4.0, 30.0, 30.0]),
    unit_cell=[Atom(1, [0, 0, 0]), Atom(1, [0, 0, 1.4])],
)

# 2D slab. Prefer vq.slab_2d: give only the two in-plane lattice
# vectors and the atoms at their real z. The third lattice column is
# synthesized for you and is NOT a vacuum gap; the SCF is invariant to
# it. Building a dim=2 PeriodicSystem by hand works too, but then the
# third column is still only bookkeeping.
sysp_2d = vq.slab_2d(
    [2.5, 0.0, 0.0],
    [0.0, 2.5, 0.0],
    [Atom(6, [0, 0, 0])],
)

# 3D bulk — all three columns are lattice vectors.
sysp_3d = PeriodicSystem(
    dim=3,
    lattice=np.diag([5.4, 5.4, 5.4]),       # Si conventional cubic, bohr
    unit_cell=[Atom(14, [0, 0, 0]), Atom(14, [0.25, 0.25, 0.25])],
)

Accessors

sysp.reciprocal_lattice()      # 3×3 matrix, columns = b_i with a_i · b_j = 2π δ_ij
sysp.n_electrons()             # total electrons per unit cell (Z - charge)
sysp.unit_cell_molecule()      # Molecule view for BasisSet() construction

Space-group analysis

from vibeqc import attach_symmetry
attach_symmetry(sysp)           # populates sysp.symmetry via spglib
print(sysp.symmetry)
# SpaceGroup(number=227, symbol='Fd-3m', order=48)

SpaceGroup carries number (International Tables), international_symbol, hall_number, point_group, and the full list of symmetry operations.

POSCAR I/O

from vibeqc import read_poscar, write_poscar

sysp = read_poscar("tests/data/NaCl.poscar")   # returns Crystal
# To convert between Crystal and PeriodicSystem, see the next section.

standard = read_poscar(
    "tests/data/NaCl.poscar",
    symmetrise=True,
    to_primitive=True,
)
sysp_clean = standard.system                  # returns PeriodicSystem

CIF I/O

from vibeqc import read_cif

crystal = read_cif("MgO.cif")                 # returns Crystal

standard = read_cif(
    "MgO.cif",
    symmetrise=True,
    to_primitive=True,
)
sysp_clean = standard.system                  # returns PeriodicSystem

The CIF reader handles cell lengths/angles, fractional _atom_site_* loops, uncertainty suffixes such as 4.210(1), and standard CIF symmetry-operation loops. When symmetry operations are present, asymmetric-unit sites are expanded to the full unit cell before Crystal construction.

Extended-XYZ I/O

Periodic Extended-XYZ stores the lattice in the comment line using the ASE convention: Lattice="a_x a_y a_z b_x ... c_z" with lattice vectors as rows in Ångström. vibe-qc converts that to the internal column-vector bohr convention when periodic mode is requested:

from vibeqc import from_xyz

sysp = from_xyz("MgO.xyz", periodic=True)     # returns PeriodicSystem

standard = from_xyz(
    "MgO.xyz",
    symmetrise=True,
    to_primitive=True,
)
sysp_clean = standard.system                  # returns PeriodicSystem

Plain molecular from_xyz(path) is unchanged and still returns a Molecule. For plain XYZ files that carry no Lattice="..." tag, pass an explicit 3×3 lattice= matrix in Ångström and, if needed, dim=1, dim=2, or dim=3.