Interfaces
In this tutorial, we shown how to use the interfaces to build the primitive cell from the output of
Wannier90 and LAMMPS. The scripts can be found at examples/interface
. We begin with importing
the necessary packages:
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import KDTree
from ase import io
import tbplas as tb
For LAMMPS, we need the ase
library to read in the structure, and the KDTree
class from the
scipy
library to search and add hopping terms. So they must be installed before starting this
tutorial. For Wannier90, no other libraries are required.
Wannier90
The wan2pc()
function can read in the output of Wannier90, namely seedname_centres.xyz
,
seedname_hr.dat
, seedname.win
and seedname_wsvec.dat
, where seedname
is the shared
prefix of the files, centres.xyz
contains the positions of the Wannier functions, hr.dat
contains the on-site energies and hopping terms, win
is the input file of Wannier90, and
wsvec.dat
contains information the Wigner-Seitz cell. Only the seedname
is required as input.
We demonstrate the usage of this function by constructing the 8-orbital primitive cell of graphene
and evaluating its band structure:
1cell = tb.wan2pc("graphene")
2k_points = np.array([
3 [0.0, 0.0, 0.0],
4 [1./3, 1./3, 0.0],
5 [1./2, 0.0, 0.0],
6 [0.0, 0.0, 0.0],
7])
8k_label = ["G", "K", "M", "G"]
9k_path, k_idx = tb.gen_kpath(k_points, [40, 40, 40])
10k_len, bands = cell.calc_bands(k_path)
11
12num_bands = bands.shape[1]
13for i in range(num_bands):
14 plt.plot(k_len, bands[:, i], color="r", linewidth=1.0)
15for idx in k_idx:
16 plt.axvline(k_len[idx], color="k", linewidth=1.0)
17plt.xlim((0, np.amax(k_len)))
18plt.xticks(k_len[k_idx], k_label)
19plt.xlabel("k / (1/nm)")
20plt.ylabel("Energy (eV)")
21plt.tight_layout()
22plt.show()
The output is shown in the left panel, where the Dirac cone at \(\mathbf{K}\)-point is perfectly reproduced.

(a) Band structure of monolayer graphene imported from the output of Wannier90. (b) Plot of a large bilayer graphene primitive cell imported from the output of LAMMPS.
LAMMPS
Unlike Wannier90, the output of LAMMPS contains only the atomic positions. So we must add the orbitals and hopping terms manually. We show this by constructing a large bilayer graphene primitive cell:
1# Read the last image of lammps dump
2atoms = io.read("struct.atom", format="lammps-dump-text", index=-1)
3
4# Get cell lattice vectors in Angstroms
5lattice_vectors = atoms.cell
6
7# Create the cell
8prim_cell = tb.PrimitiveCell(lat_vec=atoms.cell, unit=tb.ANG)
9
10# Add orbitals
11for atom in atoms:
12 prim_cell.add_orbital(atom.scaled_position)
13
14# Get Cartesian Coordinates of all orbitals
15orb_pos_ang = prim_cell.orb_pos_ang
16
17# Detect nearest neighbours using KDTree
18kd_tree = KDTree(orb_pos_ang)
19pairs = kd_tree.query_pairs(r=1.45)
20
21# Add hopping terms
22for pair in pairs:
23 prim_cell.add_hopping((0, 0, 0), pair[0], pair[1], energy=-2.7)
24
25# Plot the cell
26prim_cell.plot(with_cells=False, with_orbitals=False, hop_as_arrows=False)
In line 2 we read in the structure with the read
function of ase.io
module. Then we extract
the lattice vectors from the cell
attribute of ase Atoms
class and create the primitive cell
in line 5-8. After that, we loop over the atoms in the structure and add one \(p_z\) orbital for
each atom. The fractional coordinate of the orbital can be extracted from the scaled_position
attribute of ase Atom
class. Then we get the Cartesian coordinates of the orbitals from the
orb_pos_ang
attribute and create a kd_tree
from the KDTree
class. The orbital pairs with
hopping distances of 1.45 \(\overset{\circ}{\mathrm {A}}\) (length of C-C bond) are obtained
with the query_pairs
method. Then we add the hopping terms according to the orbital pairs and
plot the cell. Note that for simplicity, we consider only the hopping terms within the (0, 0, 0)-th
primitive cell. The output is shown in the right panel of the figure.