Metadata-Version: 2.4
Name: latticegeom
Version: 0.1.0
Summary: Typed periodic lattice geometry for condensed-matter models
Author-email: Lost-MSth <python@lost-msth.cn>
Maintainer-email: Lost-MSth <python@lost-msth.cn>
License-Expression: MIT
Project-URL: Homepage, https://github.com/Lost-MSth/LatticeGeom
Project-URL: Documentation, https://lost-msth.github.io/LatticeGeom/
Project-URL: Repository, https://github.com/Lost-MSth/LatticeGeom.git
Project-URL: Issues, https://github.com/Lost-MSth/LatticeGeom/issues
Project-URL: Changelog, https://github.com/Lost-MSth/LatticeGeom/blob/main/CHANGELOG.md
Keywords: condensed-matter,geometry,lattice,physics,periodic-boundary-conditions
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Physics
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.23
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Provides-Extra: plot
Requires-Dist: matplotlib>=3.7; extra == "plot"
Provides-Extra: bz
Requires-Dist: scipy>=1.10; extra == "bz"
Provides-Extra: docs
Requires-Dist: mkdocs<2,>=1.6; extra == "docs"
Dynamic: license-file

# LatticeGeom

[![PyPI](https://img.shields.io/pypi/v/latticegeom.svg)](https://pypi.org/project/latticegeom/)
[![Python](https://img.shields.io/pypi/pyversions/latticegeom.svg)](https://pypi.org/project/latticegeom/)
[![CI](https://github.com/Lost-MSth/LatticeGeom/actions/workflows/ci.yml/badge.svg)](https://github.com/Lost-MSth/LatticeGeom/actions/workflows/ci.yml)
[![Documentation](https://github.com/Lost-MSth/LatticeGeom/actions/workflows/docs.yml/badge.svg)](https://lost-msth.github.io/LatticeGeom/)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Lost-MSth/LatticeGeom/blob/main/LICENSE)

[English](https://github.com/Lost-MSth/LatticeGeom/blob/main/README.md) |
[简体中文](https://github.com/Lost-MSth/LatticeGeom/blob/main/README.zh-CN.md)

**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:

```bash
python -m pip install latticegeom
python -m pip install "latticegeom[plot,bz]"
```

For a source checkout:

```bash
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

```python
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.

```python
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:

```python
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:

```python
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.

```python
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:

```python
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:

```python
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:

```python
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:

```python
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:

```python
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

```python
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:

```python
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:

```python
reverse = lat.reversed_bonds()
```

## Repository layout

```text
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:

- [Online documentation](https://lost-msth.github.io/LatticeGeom/)
- [在线简体中文文档](https://lost-msth.github.io/LatticeGeom/zh/)
- [English documentation source](https://github.com/Lost-MSth/LatticeGeom/blob/main/docs/index.md)
- [简体中文文档源码](https://github.com/Lost-MSth/LatticeGeom/blob/main/docs/zh/index.md)
- [Installation and quick start](https://lost-msth.github.io/LatticeGeom/getting-started/)
- [Core concepts](https://lost-msth.github.io/LatticeGeom/concepts/)
- [Geometry guide](https://lost-msth.github.io/LatticeGeom/geometry/)
- [Reciprocal space](https://lost-msth.github.io/LatticeGeom/reciprocal-space/)
- [Visualization](https://lost-msth.github.io/LatticeGeom/visualization/)
- [One-shot neighbor discovery](https://lost-msth.github.io/LatticeGeom/neighbor-discovery/)
- [API reference](https://lost-msth.github.io/LatticeGeom/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.

## Contributing

Bug reports, focused pull requests, and new lattice definitions are welcome. See
[CONTRIBUTING.md](https://github.com/Lost-MSth/LatticeGeom/blob/main/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](https://github.com/Lost-MSth/LatticeGeom/blob/main/CITATION.cff). GitHub can export the citation in common bibliography
formats from that file.

## License

LatticeGeom is released under the
[MIT License](https://github.com/Lost-MSth/LatticeGeom/blob/main/LICENSE).
