Build complex primitive cells

In this tutorial, we demonstrate how to construct complex primitive cells using the Python-based modeling tools. The scripts are located at examples/prim_cell/model/graphene_rect.py, examples/prim_cell/model/graphene_nr.py and examples/prim_cell/model/graphene_vac.py. TBPLaS offers two sets of modeling tools: Python-based and Cython-based. Python-based tools are designed for models of moderate size, and all work at the PrimitiveCell level. The PrimitiveCell class offers the following methods to build complex models:

  • remove_orbitals

  • remove_hopping

  • trim

  • apply_pbc

  • reset_lattice

  • rotate

remove_orbitals() and remove_hopping() removes orbitals and hopping terms from the primitive cell, respectively. Dangling orbitals and hopping terms may remain in the cell after the removal, and can be trimmed with the trim() method. apply_pbc() makes a periodic primitive cell non-periodic by removing hopping terms to neighboring cells along given directions. reset_lattice() resets the lattice vectors of the primitive cell, while rotate() rotates the primitive cell with respect to z-axis.

Additionally, the tbplas.builder.advanced module offers the following modeling functions:

  • extend_prim_cell

  • reshape_prim_cell

  • spiral_prim_cell

  • make_hetero_layer

  • merge_prim_cell

extend_prim_cell() replicates the primitive cell along \(a\), \(b\), and \(c\) directions. reshape_prim_cell() reshapes the primitive cell to new lattice vectors. spiral_prim_cell(), make_hetero_layer() and merge_prim_cell() are functions specially designed for building hetero-structures. Their usage will be discussed in Build hetero-structure.

Construct graphene nano-ribbon

Make rectangular cell

To make graphene nano-ribbons we need the rectangular primitive cell. We can built it from scratch as:

import math
import tbplas as tb

# Generate lattice vectors
sqrt3 = math.sqrt(3)
a = 2.46
cc_bond = sqrt3 / 3 * a
vectors = tb.gen_lattice_vectors(sqrt3 * cc_bond, 3 * cc_bond)

# Create cell and add orbitals
rect_cell = tb.PrimitiveCell(vectors)
rect_cell.add_orbital((0, 0))
rect_cell.add_orbital((0, 2 / 3.))
rect_cell.add_orbital((1 / 2., 1 / 6.))
rect_cell.add_orbital((1 / 2., 1 / 2.))

# Add hopping terms
rect_cell.add_hopping([0, 0], 0, 2, -2.7)
rect_cell.add_hopping([0, 0], 2, 3, -2.7)
rect_cell.add_hopping([0, 0], 3, 1, -2.7)
rect_cell.add_hopping([0, 1], 1, 0, -2.7)
rect_cell.add_hopping([1, 0], 3, 1, -2.7)
rect_cell.add_hopping([1, 0], 2, 0, -2.7)

# Plot the cell
rect_cell.plot()

But the function reshape_prim_cell() offers a more convenient approach. In the figure we show the relation of the lattices of rectangular cell to diamond-shaped cell:

../../_images/rect_diamond.png

Rectangular and diamond-shaped primitive cells of monolayer graphene. The rectangular cell is indicated with blue rectangle, with lattice vectors (\(a_1\prime\) and \(a_2\prime\)) shown as solid arrows.

It is clear that:

  • \(a_1\prime = a_1\)

  • \(a_2\prime = -a_1 + 2a_2\)

  • \(a_3\prime = a_3\)

The last relation is not explicitly shown in the figure, but required by TBPLaS since all primitive cells are implemented as three-dimensional internally. From the relation we can construct the rectangular cell as:

import numpy as np
import tbplas as tb

# Import diamond-shaped primitive cell from materials repository
cell = tb.make_graphene_diamond()

# Define conversion matrix of lattice vectors
lat_sc = np.array([[1, 0, 0], [-1, 2, 0], [0, 0, 1]])

# Reshape the primitive cell
rect_cell = tb.reshape_prim_cell(cell, lat_sc)

# Plot the cell
rect_cell.plot()

Here cell is the diamond-shaped primitive cell. lat_sc is the conversion matrix of lattice vectors. By changing the conversion matrix we can reshape the primitive cell to different shapes, which is particular useful for constructing hetero-structures. We will show it in Build hetero-structure.

Extend rectangular cell

To produce a graphene nano-ribbon with desired width we need to extend the rectangular cell. We do this by calling the extend_prim_cell() function:

gnr_am = tb.extend_prim_cell(rect_cell, dim=(3, 3, 1))
gnr_am.plot(with_conj=False)

Here we extend the rectangular cell along \(a\) and \(b\) directions by 3 times through the dim parameter. The output is shown as below:

../../_images/gnr.png

Extend rectangular primitive cell and graphene nano-ribbon with armchair edges (GNR-AM) or zigag edges (GNR-ZZ).

Impose non-periodic boundary condition

The extend rectangular cell is periodic along \(a\) and \(b\) directions, i.e., it is two-dimensional. But graphene nano-ribbons are one-dimensional. We can impose non-periodic boundary conditions along specific direction by calling the apply_pbc() method:

gnr_am.apply_pbc(pbc=(False, True, False))
gnr_am.plot(with_conj=False)

Here we enforce the cell to be periodic only along \(b\) direction, yielding a nano-ribbon with armchair edges, as shown in the middle panel of the figure shown above. We can also enforce the cell to be periodic along \(a\) direction to make a nano-ribbon with zigzag edges:

gnr_zz = tb.extend_prim_cell(rect_cell, dim=(3, 3, 1))
gnr_zz.apply_pbc(pbc=(True, False, False))
gnr_zz.plot(with_conj=False)

Note that apply_pbc() does not return a new primitive cell as other functions. Instead, the original primitive cell is modified. So we need to extend the rectangular cell again before calling apply_pbc().

Finally we can evaluate the band structure of armchair-edged nano-ribbon with:

# Armchair nano-ribbon
k_points = np.array([
    [0.0, -0.5, 0.0],
    [0.0, 0.0, 0.0],
    [0.0, 0.5, 0.0],
])
k_label = ["X", "G", "X"]
k_path, k_idx = tb.gen_kpath(k_points, [40, 40])
k_len, bands = tb.calc_bands(gnr_am, k_path, prefix="gnr_am")
vis = tb.Visualizer()
vis.plot_bands(k_len, bands, k_idx, k_label)

For zigzag-edged nano-ribbon, replace k_points with:

# Zigzag nano-ribbon
k_points = np.array([
    [-0.5, 0.0, 0.0],
    [0.0, 0.0, 0.0],
    [0.5, 0.0, 0.0],
])
k_label = ["X", "G", "X"]
k_path, k_idx = tb.gen_kpath(k_points, [40, 40])
k_len, bands = tb.calc_bands(gnr_zz, k_path, prefix="gnr_zz")
vis.plot_bands(k_len, bands, k_idx, k_label)

The band structures should look like:

../../_images/gnr_bands.png

Band structures of armchair and zigag-edged graphene nano-ribbons.

It is consistent with the literature: zigzag-edged graphene nano-ribbons are always metallic, while armchair-edged graphene nano-ribbons can be either metallic or semi-conducting.

Remove orbitals and hopping terms

Remove orbitals

To demonstrate the usage of remove_orbitals() and remove_hopping() we need to import the diamond-shaped primitive cell of graphene and extend it by 3 times along \(a\) and \(b\) directions:

import tbplas as tb

cell = tb.make_graphene_diamond()
cell = tb.extend_prim_cell(cell, dim=(3, 3, 1))
cell.plot(with_conj=False)

The output is shown in the right panel of the figure:

../../_images/rm_orb.png

Structure of extended graphene primitive cell before and after removing orbitals and after trimming dangling terms. Blue circle indicates the dangling orbital.

We remove orbital #8 and #14 with the following commands:

cell.remove_orbitals([8, 14])
cell.plot(with_conj=False)

The output is shown in the middle panel. Obviously, orbital #8 and #14 have been removed. However, orbital #9 becomes dangling, since there is only one hopping term associated with it. We can remove the orbital and associated hopping terms with the trim() method:

cell.trim()
cell.plot(with_conj=False)

Note that trim() does not return a new primitive cell, but modifies the original cell in-place. The output is shown in the right panel. The dangling orbital and hopping term are removed after calling the function.

Remove hopping terms

From the extended cell we can also remove hopping terms, e.g. \((0, 0) \rightarrow (0, 0), i=3, j=8\) and \((0, 0) \rightarrow (0, 0), i=8, j=9\) with the following commands:

cell = tb.make_graphene_diamond()
cell = tb.extend_prim_cell(cell, dim=(3, 3, 1))
cell.remove_hopping(rn=(0, 0), orb_i=3, orb_j=8)
cell.remove_hopping(rn=(0, 0), orb_i=8, orb_j=9)
cell.plot(with_conj=False)

The output is shown in the left panel of the figure:

../../_images/rm_hop.png

Structure of extended graphene primitive cell after removing hopping and after trimming dangling terms. Blue circle indicates the dangling orbital.

Similarly, we can remove dangling terms in the same way:

cell.trim()
cell.plot(with_conj=False)

The output is shown in the right panel.