Non-orthogonal basis set ======================== In this tutorial, we show how to deal with non-orthogonal basis set in diagonalization-based calculations taking monolayer graphene as the example. The script can be found in ``examples/advanced/non_ortho.py``. First of all, we import the necessary packages: .. code-block:: python import numpy as np import tbplas as tb Then we define the following function to generate the overlap for graphene: .. code-block:: python def make_overlap(prim_cell: tb.PrimitiveCell, on_site: float = 1.0, hop: float = 0.0) -> tb.PrimitiveCell: """ Make an overlap for given primitive cell. :param prim_cell: primitive cell from which the overlap will be generated :param on_site: on-site term of the overlap :param hop: hopping terms of the overlap :return: overlap with the same numbers of orbitals and hopping terms as the model """ overlap = tb.PrimitiveCell(prim_cell.lat_vec, prim_cell.origin, 1.0) for i in range(prim_cell.num_orb): orbital = prim_cell.orbitals[i] overlap.add_orbital(orbital.position, on_site) overlap.add_hopping((0, 0), 0, 1, hop) overlap.add_hopping((1, 0), 1, 0, hop) overlap.add_hopping((0, 1), 1, 0, hop) return overlap The overlap is actually an instance of the :class:`.PrimitiveCell` class sharing the same lattice vectors, lattice origin and orbital positions. The `on-site` and `hopping` terms represent the overlap of the basis functions themselves, and the overlap between different basis functions, respectively. In the orthogonal case, the `on-site` overlap exactly 1, while the `hopping` overlap is exactly 0. In the non-orthogonal case, the `on-site`` overlap is never 1.0, and the `hopping` overlap is non-zero. We check the effects of orthogonality of basis functions by calculating the band structure: .. code-block:: python def main(): prim_cell = tb.make_graphene_diamond() overlap = make_overlap(prim_cell, on_site=1.0, hop=0.0) #overlap = make_overlap(prim_cell, on_site=0.8, hop=0.1) solver = tb.DiagSolver(prim_cell, overlap) k_points = np.array([ [0.0, 0.0, 0.0], [2./3, 1./3, 0.0], [0.5, 0.0, 0.0], [0.0, 0.0, 0.0] ]) k_path, k_idx = tb.gen_kpath(k_points, (1000, 1000, 1000)) solver.config.prefix = "graphene" solver.config.k_points = k_path timer = tb.Timer() timer.tic("bands") k_len, bands = solver.calc_bands() timer.toc("bands") if solver.is_master: timer.report_total_time() vis = tb.Visualizer() vis.plot_bands(k_len, bands, k_idx, ["G", "K", "M", "G"]) if __name__ == "__main__": main() We firstly set ``on-site`` to 1.0 and ``hop`` to 0.0, i.e., the basis set is orthogonal. Then we set ``on-site`` to 0.8 and ``hop`` to 0.1 to mimic a non-orthogonal basis set. The ``overlap`` instance is passed as the second argument when constructing the solver. Subsequential steps are the same as the orthogonal case. The output is shown in the figure below. It is clear that the non-orthogonal basis set significantly reshapes the band structure, including both the energy range and the dispersion. .. figure:: images/non_ortho/bands.png :align: center :scale: 40% Band structure of monolayer graphene: (a) orthogonal basis set; (b) non-orthogonal basis set.