Typed periodic lattice geometry for condensed-matter models.
The library describes geometry and periodic relations. It intentionally does not store hopping amplitudes, exchange constants, Peierls phases, flux values, or Hamiltonian matrices.
- directed bonds declared once in a reference unit cell;
- arbitrary hashable
typelabels for sites, bonds, and plaquettes; - oriented plaquettes, so up/down triangles remain different geometry classes;
- exact integer
cell_shiftfor every bond; - compressed
super_idxfor finite-cell boundary crossings; - multi-edges and nontrivial periodic self-bonds are preserved;
- read-only, NumPy-backed compiled arrays;
- no mandatory graph library, SciPy, Rust, or Cython dependency.
Install the NumPy-only core, or add optional plotting and Brillouin-zone support:
python -m pip install latticegeom
python -m pip install "latticegeom[plot,bz]"For a source checkout:
python -m pip install -e ".[test,plot,bz,docs]"
python -m pytest -qThe numerical runtime dependency is only NumPy. Matplotlib is an optional dependency used by the plotting layer; SciPy is imported only when constructing a two-dimensional Wigner--Seitz Brillouin zone.
from latticegeom import triangular
lat = triangular(
extent=(4, 4),
pbc=True,
bond_types=("a", "b", "c"),
)
print(lat.n_sites) # 16
print(lat.bond_types) # ('a', 'b', 'c')
print(lat.plaquette_types) # ('triangle_up', 'triangle_down')
# Fast columnar access
src = lat.bonds.source
dst = lat.bonds.target
bond_type = lat.bonds.type_id
super_idx = lat.bonds.super_idxCommon constructors include chain, square, triangular, triangular_nnn,
honeycomb, kagome, arbitrary-dimensional grid/hypercubic, and the 3D bcc,
fcc, diamond, and pyrochlore lattices.
Primitive and finite-supercell reciprocal objects are named explicitly. A regular mesh
samples a continuous reciprocal cell, whereas allowed_momenta() returns the discrete
momenta permitted by the finite PBC axes.
k = lat.k_from_coordinates([0.2, 0.3], cell="primitive")
q = lat.reciprocal_coordinates(k, cell="primitive")
k_mesh = lat.sample_kmesh((80, 80), cell="primitive")
finite_k = lat.allowed_momenta(centered=True)Boundary phases use the already-compressed super_idx directly:
phase_by_super = lat.translation_phases(k, sign=-1)
phase_by_bond = phase_by_super[lat.bonds.super_idx]This evaluates geometry-derived exp(-1j * k @ translation) factors; it does not
assign amplitudes or construct a Hamiltonian. lat.unit_bond_phases(k, gauge="cell")
similarly returns one phase per unit-cell bond. Use gauge="position" when the phase
convention includes sublattice offsets.
With the bz extra installed, the primitive or folded first BZ is an explicit object:
bz = lat.brillouin_zone(cell="primitive")
vertices = bz.vertices
inside = bz.contains(k_mesh)
first_bz_mesh = bz.sample((100, 100))The object is not cached on Lattice, so one sampling request cannot silently change
another calculation.
Plotting is deliberately optional and returns a regular Matplotlib axes without calling
show(). Bond arrows preserve declared direction, while site, bond, and plaquette
colors follow their geometry type labels.
import matplotlib.pyplot as plt
from latticegeom import triangular
lat = triangular((3, 3), pbc=True)
ax = lat.plot(
draw_plaquettes=True,
site_labels=True,
periodic="unfold",
plaquette_values=None, # external values may be supplied without being stored
)
plt.show()Boundary-crossing relations have three display modes:
periodic="unfold"(default) places the endpoint in its actual periodic image;periodic="hide"omits crossing bonds and plaquettes;periodic="fold"connects to the wrapped finite site with a dashed line.
unfold is the most faithful view for checking super_idx; fold is compact but can
draw visually long bonds through the cell. Visible crossing relations are dashed.
Other useful switches include include_reverse, arrows, plaquette_arrows (to show
stored loop orientation), show_boundary, show_unit_cell, show_basis, and
projection=(i, j) for a lattice embedded above two Cartesian dimensions. Run
python examples/plot_geometry.py for a typed triangular/Kagome example.
Compiled relation tables can be filtered without converting to Python graph objects:
rows = lat.find_bonds(
type="a",
source=0,
cell_shift=(1, 0),
crossing=False,
)
boundary = lat.plaquette_boundary(0)
source = boundary.source
target = boundary.target
cell_shift = boundary.cell_shiftPlaquetteBoundary follows the stored vertex orientation and gives one exact integer
relation per perimeter edge. Additional geometric queries include
plaquette_centers(), plaquette_signed_areas(), and
lat.plaquettes.indices(type=..., crossing=...).
External bond or flux results can be annotated without placing model data on the lattice:
lat.plot(
bond_values=phase_by_bond,
plaquette_values=flux,
value_format=".3f",
)Declarations remain immutable, while UnitCellBuilder provides a temporary mutable
construction layer for multilayers and other composed cells:
from latticegeom import UnitCellBuilder
builder = UnitCellBuilder(lower_cell)
upper_sites = builder.extend(
lower_cell,
offset=(0.0, 0.3),
site_type_map=lambda kind: ("upper", kind),
)
builder.add_bond(0, upper_sites[0], (0, 0), "interlayer")
bilayer_cell = builder.build()translate_unit_cell and merge_unit_cells cover simple composition. For a magnetic
or tilted crystallographic supercell, an integer transformation remaps sites, bonds,
and plaquettes exactly:
from latticegeom import Lattice, make_unit_cell_supercell
new_basis, new_cell = make_unit_cell_supercell(
lat.basis,
lat.unit_cell,
[[2, 1], [0, 1]],
)
enlarged = Lattice(new_basis, new_cell, extent=(3, 3), pbc=True)Distance-based neighbor inference is an explicit authoring tool, not a Lattice
construction feature. It lives outside the top-level API so importing or constructing a
lattice can never trigger it accidentally:
from latticegeom import Site
from latticegeom.tools import discover_neighbors
draft = discover_neighbors(
basis=((1.0, 0.0), (0.5, 3**0.5 / 2.0)),
sites=[Site((0.0, 0.0), "A")],
shells=2,
)
print(draft.summary())
print(draft.render_python(type_by_shell={1: "nearest", 2: "next_nearest"}))
# After reviewing the output, write fixed Bond(...) declarations once.
draft.freeze(
"fixed_triangular_bonds.py",
type_by_shell={1: "nearest", 2: "next_nearest"},
)Every discovery call emits a reminder to review and freeze the result. freeze() refuses
to overwrite an existing file unless overwrite=True, and the generated file contains
only explicit Bond(...) declarations—no runtime search call.
Distance shells are geometric, not physical. Equal bond lengths can still have different
hopping or exchange amplitudes. Pass classify=candidate_to_type to inspect each
candidate's source, target, integer cell_shift, Cartesian vector, distance, and
one-based shell, or edit the generated source manually. Canonical orientations are
deterministic but carry no inferred hopping direction.
The search supports skew bases, multiple sublattices, and lower-dimensional lattices in a
larger Cartesian embedding. It expands the integer translation range until a geometric
bound proves that no requested neighbor can still be missing. max_distance= is available
as an explicit alternative to shells=. Shell grouping uses a relative tolerance by
default so the result does not depend on whether positions are measured in lattice units,
angstroms, or metres; rtol= and atol= remain explicit controls.
from math import sqrt
from latticegeom import Bond, Lattice, Plaquette, Site, UnitCell
cell = UnitCell(
sites=[Site((0.0, 0.0))],
bonds=[
Bond(0, 0, cell_shift=(1, 0), type=1),
Bond(0, 0, cell_shift=(0, 1), type=2),
Bond(0, 0, cell_shift=(-1, 1), type=3),
],
plaquettes=[
Plaquette(
[(0, (0, 0)), (0, (1, 0)), (0, (0, 1))],
type="triangle_up",
),
Plaquette(
[(0, (0, 0)), (0, (1, -1)), (0, (1, 0))],
type="triangle_down",
),
],
)
lat = Lattice(
basis=((1.0, 0.0), (0.5, sqrt(3.0) / 2.0)),
unit_cell=cell,
extent=(8, 8),
pbc=True,
)Plaquette vertex order is retained. The closing edge is implicit, so the first vertex must not be repeated at the end.
These values answer different questions:
lat.bonds.cell_shift[i]is the exact primitive-cell displacement in the infinite lattice. It is the same geometric relation regardless of finite extent.lat.bonds.super_idx[i]indexes the boundary crossing of that concrete finite bond.lat.supercell_shifts[super_idx]is the integer number of simulation supercells crossed.lat.supercell_translations[super_idx]is the corresponding Cartesian translation.
This supports block or Bloch-phase assembly without putting Hamiltonian data into the geometry layer:
for super_idx, translation in enumerate(lat.supercell_translations):
rows = lat.bonds.indices(super_idx=super_idx)
if not len(rows):
continue
source = lat.bonds.source[rows]
target = lat.bonds.target[rows]
kind = lat.bonds.type_id[rows]
# A downstream model assigns amplitudes and exp(-1j * k @ translation).lat.reverse_super_idx maps every boundary class to its negative. Reverse bonds can be
obtained without storing a second copy:
reverse = lat.reversed_bonds()src/latticegeom/ package (public API plus private compilation pipeline)
src/latticegeom/tools/ explicit one-shot authoring and code-generation tools
tests/ geometry and boundary-condition tests
examples/ runnable usage examples
docs/ design notes
tmp/ ignored reference implementations
Inside the package, lattice.py contains the public object and queries, while the
staged finite-geometry implementation lives in the private _compiler.py module.
Composition lives in builders.py, reciprocal-space derivations in reciprocal.py,
optional Matplotlib rendering in plotting.py, and one-shot neighbor discovery in
tools/neighbors.py.
The initial API was informed by experience with NetKet-style lattice graphs, but the core here is a clean NumPy-based implementation centered on explicit integer periodic geometry.
examples/double_kagome.py shows how the six-site example from the reference work can
be expressed with explicit integer shifts and independently typed bonds/plaquettes.
The publishable documentation is available in both languages:
- Online documentation
- 在线简体中文文档
- English documentation source
- 简体中文文档源码
- Installation and quick start
- Core concepts
- Geometry guide
- Reciprocal space
- Visualization
- One-shot neighbor discovery
- API reference
Build the bilingual site locally with python -m mkdocs serve. LatticeGeom is
currently an alpha-stage library: its geometry invariants are tested, while the public
API may still evolve before a stable 1.0 release.
Bug reports, focused pull requests, and new lattice definitions are welcome. See CONTRIBUTING.md for the development workflow and project boundaries.
If LatticeGeom contributes to published work, cite the software using the metadata in CITATION.cff. GitHub can export the citation in common bibliography formats from that file.
LatticeGeom is released under the MIT License.