Recommended resources
The freely available resources this lesson will direct you to.
Hello! In our last lesson, we analytically derived the band structure of graphene and discovered the fascinating emergence of massless Dirac fermions. That derivation was specific to the honeycomb lattice. Today, we'll learn a far more general and powerful approach: how to write a program to calculate the band structure for any lattice described by a tight-binding model.
This is a fundamental skill in computational condensed matter physics. It allows us to move beyond simple, analytically solvable models and tackle the electronic structure of complex, real-world materials. Our goal is to implement the numerical diagonalization of a tight-binding Hamiltonian and use it to plot band structures.
1. The Algorithm: From Real Space to Band Structure
Last time, we constructed a 2x2 Bloch Hamiltonian for graphene. The general numerical procedure to obtain a band structure follows these steps:
-
Define the Crystal Structure:
- Specify the real-space lattice vectors, .
- Specify the positions of all atoms within the unit cell (the basis), .
-
Define the Hamiltonian:
- Set the on-site energy for each orbital in the basis, .
- Define the hopping parameters between orbitals. This is a list of connections: from orbital in the home unit cell to orbital in a cell displaced by a lattice vector .
-
Construct the Bloch Hamiltonian Matrix :
For a given momentum vector , the matrix elements of the Bloch Hamiltonian are given by a Fourier sum over all real-space hoppings:The on-site energies form the diagonal elements where and .
-
Diagonalize and Plot:
- Define a path of high-symmetry points in the Brillouin Zone (e.g., ).
- For each along the path, construct and diagonalize the matrix .
- The eigenvalues of are the energy bands .
- Plot the eigenvalues as a function of the distance along the k-path.
Manually constructing and diagonalizing these matrices can be tedious and error-prone. Fortunately, excellent software packages exist to automate this process. We will focus on one such package for Python called PythTB.
2. Introduction to PythTB
PythTB (Python Tight-Binding) is a package designed to make setting up and solving tight-binding models intuitive. The video below is a tutorial by one of the package's authors, Sinisa Coh. It provides a hands-on guide that covers all the essential steps of our algorithm.
Tight-binding models (hands-on PythTB)
The video 'Tight-binding models (hands-on PythTB)' from ICTP Condensed Matter and Statistical Physics is an excellent tutorial that we will use as the core of this lesson.
Please watch from the beginning up to 16:31. The video will guide you through the fundamental concepts of PythTB: Defining the model: How to specify dimensionality, lattice vectors, and orbital positions (00:00 - 07:22). Defining the Hamiltonian: How to set on-site energies and hopping parameters, paying attention to the convention for specifying hoppings between different unit cells (07:22 - 16:31). Focus on understanding the logic of how the code represents the physical system. The conventions used here are common to many tight-binding codes.
To summarize the key ideas from the video:
- You create a
tb_modelobject, specifying the real-space dimension, k-space dimension, lattice vectors, and orbital positions. - Orbital positions are given in fractional coordinates relative to the lattice vectors.
- You use
set_onsite()to define the diagonal elements of the Hamiltonian. - You use
set_hop(t, i, j, R)to define a hopping of amplitudetfrom orbitaliin the home cell (cell[0,0,...]) to orbitaljin the cell displaced by the lattice vectorR. - The code automatically handles the hermitian conjugate hopping (e.g., from
jin cell-Rtoiin the home cell).
3. Practical Implementation: Graphene
Now, let's apply this to a system you're familiar with: graphene. The next segment of the video includes an interactive quiz where the presenter challenges the audience to construct the tight-binding models for various lattices, culminating in graphene.
Tight-binding models (hands-on PythTB)
Let's continue with the PythTB tutorial and see how to construct the graphene Hamiltonian.
Watch the segment from 27:30 to 38:32. This part focuses on setting up the tight-binding model for graphene. Pay close attention to the choices for: Lattice vectors (triangular Bravais lattice). Orbital positions (two atoms, A and B, in the basis). The minimal set of hoppings needed to define the nearest-neighbor model.
The key insight for implementing graphene is to recognize its structure as a triangular Bravais lattice with a two-atom basis.

To implement this in PythTB, you would:
- Define the primitive lattice vectors for the triangular lattice, e.g., and .
- Define two orbitals in the basis. If we place atom A at the origin
[0.0, 0.0], atom B is located at a position like[x, y]which you need to express in fractional coordinates of and . - Set on-site energies for both orbitals to 0.
- Define the three nearest-neighbor hoppings. For an A atom at the origin, one B neighbor is in the same unit cell (
R=[0,0]), while the other two are in adjacent unit cells (e.g.,R=[-1,0]andR=[0,-1], depending on your choice of vectors and basis).
Once the model is defined, solving it is straightforward.
Tight-binding models (hands-on PythTB)
Finally, let's see how to solve the model and generate the band structure plot.
Watch from 38:32 to 42:38. This short segment shows how to define a k-path, call the solver, and plot the resulting eigenvalues, reproducing the famous Dirac cones.
4. Code Examples and Your Turn
Reading about an API is one thing, but seeing it in a script is another. The following resource provides a complete, runnable Python script for generating a band structure.
Easy ways to generate 'teaching' band structures in Python?
This Stack Exchange answer by Leopold Talirz provides a concise and well-commented Python script using PythTB to calculate the band structure of a 2D square lattice with s and p orbitals.
Read through the Python code in the first answer. Notice the clear mapping between the concepts we've discussed and the code: tb_model(...) to define the lattice and orbitals. set_onsite(...) and set_hop(...) to build the Hamiltonian. k_path(...) to define the path in the Brillouin zone. solve_all(...) to get the eigenvalues. matplotlib calls to plot the results.
Now, it's time to put this into practice.
Test your understanding!
In the previous lesson, we saw that breaking the sublattice symmetry in graphene by adding different on-site energies to site A and to site B opens a band gap.
Your task: Modify the PythTB graphene model (conceptually, or by adapting the script from the resource above) to include this effect. Which line(s) of code would you change, and what would the new set_onsite() call look like?
Show answer
You would modify the set_onsite() call. Assuming orbital 0 is site A and orbital 1 is site B, the original call was likely my_model.set_onsite([0.0, 0.0]) or simply setting the default.
The new call would be:my_model.set_onsite([Delta, -Delta])
This simple change would cause the two bands to separate at the K point, opening a gap of size , transforming the semimetal into an insulator. This demonstrates the power of numerical modeling: complex physical effects can often be simulated with very simple changes to the code.
5. Alternative and Advanced Implementations
While PythTB is excellent for its simplicity, other powerful libraries exist. The Stack Exchange post (LINK) also shows an example using sisl, a more verbose but highly flexible package that can even import Hamiltonians from DFT calculations.
Furthermore, given your experience with Julia, it's worth knowing that similar ecosystems exist there. The reference below is a PhD thesis that uses Julia for tight-binding calculations, including for complex systems like twisted bilayer graphene.
Theoretical description of angle-resolved photoemission...
The thesis 'Theoretical description of angle-resolved photoemission...' by M. F. C. R. de Melo uses Julia to implement tight-binding models for advanced systems.
You don't need to read this in depth, but I encourage you to skim Section 3.7 'Numerical Methods' and the code snippets in Appendix C. Notice how the logic is the same: define a Hamiltonian, generate a k-path, and loop through the k-points to diagonalize the Hamiltonian. This shows how the fundamental algorithm you've learned today is implemented in a different language and used in a research context.
This Julia example highlights the use of libraries like KrylovKit.jl for efficient diagonalization of the large, sparse matrices that arise in complex systems (like the supercells needed for twisted graphene). This is the same underlying principle of numerical linear algebra used in all professional-grade simulation software.
Conclusion
In this lesson, we bridged the gap between analytical theory and computational practice. You have learned the complete workflow for numerically calculating and plotting the electronic band structure of any material that can be described by a tight-binding model.
Key Takeaways:
- The numerical calculation of a band structure involves defining the lattice and Hamiltonian in real space, then constructing and diagonalizing the Bloch Hamiltonian matrix for a series of vectors.
- Python packages like
PythTBandsislprovide high-level, intuitive interfaces to perform these calculations, abstracting away the low-level matrix construction. - The core steps in a typical script involve: defining the model (
tb_model), setting parameters (set_onsite,set_hop), defining a k-path (k_path), solving (solve_all), and plotting. - You now have the practical tools to reproduce the band structures we derived analytically (like for the 1D chain or graphene) and to explore entirely new ones.
Preview of the Next Lesson:
Now that we can compute the full energy spectrum , we can calculate other important electronic properties. In the next lesson, we will learn how to compute the electronic density of states (DOS) from the band structure, a quantity that is directly related to many experimental measurements.