Source code for diffpes.tightb.hamiltonian
"""Build tight-binding Hamiltonians in JAX.
Extended Summary
----------------
The module provides a minimal Slater-Koster Hamiltonian builder and convenience
functions for creating test models (graphene, 1D chain). The
Hamiltonian is fully JAX-traceable so that ``jax.grad`` can
differentiate eigenvalues with respect to hopping parameters.
Routine Listings
----------------
:func:`build_hamiltonian_k`
Build the Bloch Hamiltonian H(k) at a single k-point.
"""
import jax.numpy as jnp
from beartype import beartype
from jaxtyping import Array, Complex, Float, jaxtyped
[docs]
@jaxtyped(typechecker=beartype)
def build_hamiltonian_k(
k: Float[Array, " 3"],
hopping_params: Float[Array, " H"],
hopping_indices: tuple,
n_orbitals: int,
lattice_vectors: Float[Array, "3 3"], # noqa: ARG001
) -> Complex[Array, "O O"]:
r"""Build the Bloch Hamiltonian H(k) at a single k-point.
.. math::
H_{ij}(\mathbf{k}) = \sum_{\mathbf{R}} t_{ij,\mathbf{R}}
\exp(i \mathbf{k} \cdot \mathbf{R})
where the sum runs over lattice vectors R defined by the
hopping indices. The function makes the result Hermitian:
``H = (H_raw + H_raw^dag) / 2``.
The function computes the Bloch sum entirely in fractional coordinates.
``k`` uses reciprocal lattice units, and each hopping ``R`` uses direct
lattice units. Therefore, ``k_frac . R_frac`` includes the metric. The
Cartesian phase equals
``exp(2 pi i k_frac . R_frac)``. This stage does not need the lattice
matrix. The model retains it for Cartesian conversions.
The function iterates over each hopping in Python because the hopping count
is small and static. For each hopping, it computes the Bloch phase. It adds
the corresponding amplitude to ``H[orb_i, orb_j]``.
After the accumulation, the function applies ``(H + H^dag) / 2``. A
user-supplied hopping list can contain only the upper triangle. This
operation fills the lower triangle symmetrically. It also corrects
floating-point rounding that can prevent ``jnp.linalg.eigh`` from working.
:see: :class:`~.test_hamiltonian.TestBuildHamiltonianK`
Parameters
----------
k : Float[Array, " 3"]
k-point in fractional coordinates.
hopping_params : Float[Array, " H"]
Hopping amplitudes (differentiable).
hopping_indices : tuple
``(orb_i, orb_j, (R_x, R_y, R_z))`` per hopping.
n_orbitals : int
Number of orbitals in the unit cell.
lattice_vectors : Float[Array, "3 3"]
Real-space lattice vectors (rows).
Returns
-------
H_k : Complex[Array, "O O"]
Hermitian Hamiltonian matrix.
Notes
-----
The hopping amplitudes ``hopping_params`` are plain real floats.
Complex spin-orbit hoppings require ``complex128`` values and no automatic
Hermitian completion.
The ``@jaxtyped`` and ``@beartype`` decorators detect shape and dtype
errors when the caller invokes the function.
"""
h_idx: int
orb_i: int
orb_j: int
R_ijk: Array
H: Complex[Array, "O O"] = jnp.zeros(
(n_orbitals, n_orbitals), dtype=jnp.complex128
)
for h_idx, (orb_i, orb_j, R_ijk) in enumerate(hopping_indices):
t: Float[Array, " "] = hopping_params[h_idx]
R_frac: Float[Array, " 3"] = jnp.array(R_ijk, dtype=jnp.float64)
phase: Complex[Array, " "] = jnp.exp(2j * jnp.pi * jnp.dot(k, R_frac))
H = H.at[orb_i, orb_j].add(t * phase)
H = (H + H.conj().T) / 2.0
H_k: Complex[Array, "O O"] = H
return H_k
__all__: list[str] = [
"build_hamiltonian_k",
]