Skip to main content

LatticeGeom

PyPI Python CI Documentation License: MIT

English | 简体中文

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.

Design goals

  • directed bonds declared once in a reference unit cell;
  • arbitrary hashable type labels for sites, bonds, and plaquettes;
  • oriented plaquettes, so up/down triangles remain different geometry classes;
  • exact integer cell_shift for every bond;
  • compressed super_idx for 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.

Installation

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 -q

The 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.

Quick start

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_idx

Common constructors include chain, square, triangular, triangular_nnn, honeycomb, kagome, arbitrary-dimensional grid/hypercubic, and the 3D bcc, fcc, diamond, and pyrochlore lattices.

Reciprocal geometry and Bloch phases

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.

Inspect the geometry visually

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.

Query exact bonds and plaquette boundaries

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_shift

PlaquetteBoundary 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",
)

Compose and enlarge unit cells

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)

Discover neighbors once, then freeze them

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.

Declare custom geometry

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.

cell_shift and super_idx

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()

Repository layout

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.

Documentation

The publishable documentation is available in both languages:

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.

Contributing

Bug reports, focused pull requests, and new lattice definitions are welcome. See CONTRIBUTING.md for the development workflow and project boundaries.

Citation

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.

License

LatticeGeom is released under the MIT License.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

latticegeom-0.1.0.tar.gz (86.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

latticegeom-0.1.0-py3-none-any.whl (48.3 kB view details)

Uploaded Python 3

File details

Details for the file latticegeom-0.1.0.tar.gz.

File metadata

  • Download URL: latticegeom-0.1.0.tar.gz
  • Upload date:
  • Size: 86.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for latticegeom-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7e30f209e6b49e86b0f9fe97b6692506e626f4ab0961d11ee71197f008808685
MD5 99a3f575453a0a16ae0733c35cd29b7f
BLAKE2b-256 030f6c27934093145adb95140819b033649512409a3d5040cc1bfb4251bc35a6

See more details on using hashes here.

Provenance

The following attestation bundles were made for latticegeom-0.1.0.tar.gz:

Publisher: publish-to-pypi.yml on Lost-MSth/LatticeGeom

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file latticegeom-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: latticegeom-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 48.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for latticegeom-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 451523b1c684465ecfd233627fb0b6d5d74dc61ba4847c7aaf13631fecfd7316
MD5 15376a1ff60ae6b4cc211c03b06b249d
BLAKE2b-256 06655faac14238e7064835c6192031f594fc974b8017591845dd07d5714537ed

See more details on using hashes here.

Provenance

The following attestation bundles were made for latticegeom-0.1.0-py3-none-any.whl:

Publisher: publish-to-pypi.yml on Lost-MSth/LatticeGeom

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page