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.

../../_images/allin1.png

(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.