#
# (c) 2015-2018, ETH Zurich, Institut fuer Theoretische Physik
# Author: Dominik Gresch <greschd@gmx.ch>
# pylint: disable=too-many-lines,invalid-name
"""
Implements the Model class, which describes a tight-binding model.
"""
from __future__ import annotations
import re
import os
import copy
import time
import warnings
import itertools
import contextlib
import typing as ty
import collections as co
import h5py
import numpy as np
import numpy.typing as npt
import scipy.linalg as la
from scipy.special import factorial
from fsc.hdf5_io import subscribe_hdf5, HDF5Enabled
if ty.TYPE_CHECKING:
# Replace with typing.Literal once Python 3.7 support is dropped.
from typing import Literal
import symmetry_representation # pylint: disable=unused-import
from .kdotp import KdotpModel
from .exceptions import (
TbmodelsException,
ParseExceptionMarker,
SymmetrizeExceptionMarker,
)
from . import _check_compatibility
from . import _sparse_matrix as sp
__all__ = ("Model",)
HoppingType = ty.Dict[ty.Tuple[int, ...], ty.Any]
[docs]@subscribe_hdf5("tbmodels.model", check_on_load=False)
class Model(HDF5Enabled):
"""
A class describing a tight-binding model. It contains methods for modifying the model, evaluating the Hamiltonian or eigenvalues at specific k-points, and writing to and from different file formats.
Parameters
----------
on_site :
On-site energy of the states. This is equivalent to having a
hopping within the same state and the same unit cell (diagonal
terms of the R=(0, 0, 0) hopping matrix). The length of the list
must be the same as the number of states.
hop :
Hopping matrices, as a dict containing the corresponding lattice
vector R as a key.
size :
Number of states. Defaults to the size of the hopping matrices,
if such are given.
dim :
Dimension of the tight-binding model. By default, the dimension
is guessed from the other parameters if possible.
occ :
Number of occupied states.
pos :
Positions of the orbitals, in reduced coordinates. By default,
all orbitals are set to be at the origin, i.e. at [0., 0., 0.].
uc :
Unit cell of the system. The unit cell vectors are given as rows
in a ``dim`` x ``dim`` array
contains_cc :
Specifies whether the hopping matrices and on-site energies are
given fully (``contains_cc=True``), or the complex conjugate
should be added for each term to obtain the full model. The
``on_site`` parameter is not affected by this.
cc_check_tolerance :
Tolerance when checking if the complex conjugate values (if
given) match.
sparse :
Specifies whether the hopping matrices should be saved in sparse
format.
"""
def __init__( # pylint: disable=missing-function-docstring
self,
*,
on_site: ty.Optional[ty.Sequence[float]] = None,
hop: ty.Optional[HoppingType] = None,
size: ty.Optional[int] = None,
dim: ty.Optional[int] = None,
occ: ty.Optional[int] = None,
pos: ty.Optional[ty.Sequence[ty.Sequence[float]]] = None,
uc: ty.Optional[npt.ArrayLike] = None,
contains_cc: bool = True,
cc_check_tolerance: float = 1e-12,
sparse: bool = False,
):
if hop is None:
hop = dict()
# ---- SPARSITY ----
self._sparse: bool
self._matrix_type: ty.Callable[..., ty.Any]
self.set_sparse(sparse)
# ---- SIZE ----
self._init_size(size=size, on_site=on_site, hop=hop, pos=pos)
# ---- DIMENSION ----
self._init_dim(dim=dim, hop=hop, pos=pos, uc=uc)
# ---- UNIT CELL ----
self.uc = None if uc is None else np.array(uc) # implicit copy
# ---- HOPPING TERMS AND POSITIONS ----
self._init_hop_pos(
on_site=on_site,
hop=hop,
pos=pos,
contains_cc=contains_cc,
cc_check_tolerance=cc_check_tolerance,
)
# ---- CONSISTENCY CHECK FOR SIZE ----
self._check_size_hop()
# ---- CONSISTENCY CHECK FOR DIM ----
self._check_dim()
# ---- OCCUPATION NR ----
self.occ = None if (occ is None) else int(occ)
# ---------------- INIT HELPER FUNCTIONS --------------------------------#
def _init_size(self, size, on_site, hop, pos):
"""
Sets the size of the system (number of orbitals).
"""
if size is not None:
self.size = size
elif on_site is not None:
self.size = len(on_site)
elif pos is not None:
self.size = len(pos)
elif hop:
self.size = next(iter(hop.values())).shape[0]
else:
raise ValueError(
"Empty hoppings dictionary supplied and no size, on-site energies or positions given. Cannot determine the size of the system."
)
def _init_dim(self, dim, hop, pos, uc):
r"""
Sets the system's dimensionality.
"""
if dim is not None:
self.dim = dim
elif pos is not None:
self.dim = len(pos[0])
elif hop:
self.dim = len(next(iter(hop.keys())))
elif uc is not None:
self.dim = len(uc[0])
else:
raise ValueError(
"No dimension specified and no positions, hoppings, or unit cell are given. The dimensionality of the system cannot be determined."
)
self._zero_vec = tuple([0] * self.dim)
def _init_hop_pos(self, on_site, hop, pos, contains_cc, cc_check_tolerance):
"""
Sets the hopping terms and positions, mapping the positions to the UC (and changing the hoppings accordingly) if necessary.
"""
# The double-constructor is needed to avoid a double-constructor in the sparse to-array
# but still allow for the dtype argument.
hop = {
tuple(key): self._matrix_type(self._matrix_type(value), dtype=complex)
for key, value in hop.items()
}
# positions
if pos is None:
self.pos = np.zeros((self.size, self.dim))
elif len(pos) == self.size and all(len(p) == self.dim for p in pos):
pos, hop = self._map_to_uc(pos, hop)
self.pos = np.array(pos) # implicit copy
else:
if len(pos