Chapter 4
18 min read
Section 33 of 70

Tight-Binding Model

Quantum Mechanics for Solids

Learning Objectives

Section 4.2 started from plane waves and asked: what happens if I gently switch on the lattice? The lattice answered by opening tiny gaps. That works beautifully for sodium, aluminium, and most simple metals — materials where the valence electrons are almost free.

But take CdSe, MnO, NiO, or any d- or f-electron solid. Their valence electrons are not almost free. They sit close to the parent atomic orbitals; only a small fraction of their wavefunction leaks onto neighbouring sites. Try to expand them in plane waves and you need thousands — the basis is hopelessly inefficient. The physics begs for a different starting point: start from atomic orbitals and let the crystal weakly couple them. That is the tight-binding model, the second pillar of modern band theory and the language in which the Mn 3d-shell of our Mn:CdSe quantum dot will be most cleanly described.

By the end of this section you should be able to:

  1. Construct a Bloch sum from a single atomic orbital and explain why it is the natural basis for a tight-binding Hamiltonian.
  2. Identify the two physical parameters of the simplest TB model — the on-site energy ε0\varepsilon_0 and the hopping integral tt — from their atomic-orbital integrals.
  3. Derive the cosine dispersion ε(k)=ε02tcos(ka)\varepsilon(k) = \varepsilon_0 - 2 t \cos(k a) for the 1D linear chain, and read the bandwidth, group velocity, and effective mass off it without doing any further algebra.
  4. Generalise to two orbitals per cell (bonding/antibonding bands) and to a 2D square lattice ε(k)=2t[cos(kxa)+cos(kya)]\varepsilon(\mathbf{k}) = -2 t [\cos(k_x a) + \cos(k_y a)].
  5. Diagonalise a Bloch Hamiltonian in NumPy, recover the cosine band, and extract the bandwidth and effective mass numerically.
  6. See how VASP exposes the same picture through LOBSTER / Wannier90 hopping integrals, LORBIT-projected DOS, and the COHP curves used to assess covalency.

The Other Extreme: Atoms First, Crystal Second

Imagine a row of hydrogen atoms placed very far apart — so far that their 1s orbitals do not overlap at all. Each electron sits on its own atom; the spectrum is the atomic 1s level, repeated N times. There are no bands. There is no transport. The material is a perfect insulator made of independent atoms.

Now bring the atoms closer. Their 1s tails begin to overlap. An electron on atom n can tunnel to atom n + 1 — quantum mechanics insists on it the moment the wavefunctions touch. The N-fold degeneracy of the isolated atoms splits into a band of width controlled by the overlap. Squeeze the atoms further and the band widens until, eventually, neighbouring bands touch and you reach the nearly-free regime of Section 4.2.

Tight binding and nearly-free electrons are two limits of the same problem. NFE: weak periodic potential perturbing plane waves. TB: weak inter-atomic overlap perturbing atomic orbitals. The full solution interpolates between them — and most real materials live somewhere in the middle, closer to one limit than the other depending on the orbital character.

The Linear Chain of s-Orbitals

Take the simplest non-trivial system: a 1D chain of identical atoms, spacing aa, one s-orbital ϕ(x)\phi(x) per atom, and only nearest-neighbour hopping. Label the atoms by integer nn; the orbital on site nn is ϕn(x)=ϕ(xna)\phi_n(x) = \phi(x - n a). We assume the {ϕn}\{\phi_n\} are orthonormal: ϕmϕn=δmn\langle \phi_m | \phi_n \rangle = \delta_{mn} (this is exact for Wannier functions, an approximation for raw atomic orbitals — we deal with the correction in Section 4.8).

With only on-site and nearest-neighbour matrix elements, the Hamiltonian in this atomic-orbital basis is a giant tridiagonal matrix:

Hmn=ε0δmnt(δm,n+1+δm,n1)H_{mn} = \varepsilon_0\,\delta_{mn} - t\,(\delta_{m,n+1} + \delta_{m,n-1})

Two parameters set the entire model:

  • The on-site energy ε0=ϕnHϕn\varepsilon_0 = \langle \phi_n | H | \phi_n \rangle — the energy of an electron localised on a single atom.
  • The hopping integral t=ϕnHϕn+1t = -\langle \phi_n | H | \phi_{n+1} \rangle — (minus) the matrix element between neighbouring orbitals. The minus sign is convention; with it, t>0t > 0 places the bonding state at the bottom of the band.
The number of parameters is shockingly small. A real 3D solid has ~10²³ atoms and ~10²³ electrons, but the band structure of the valence orbital can be predicted to within a few percent from two numbers. That economy is exactly why tight binding has survived 90 years of competition from more sophisticated methods.

Bloch Sums: Atomic Orbitals That Respect Translation

Translation symmetry forbids us from using the localised ϕn\phi_n directly: a translation by aa turns ϕn\phi_n into ϕn+1\phi_{n+1}, so they are not eigenstates of the translation operator. To get eigenstates — which is what Bloch's theorem demands — we form a Bloch sum:

Φk(x)=1Nneikanϕn(x)\Phi_k(x) = \frac{1}{\sqrt{N}} \sum_{n} e^{i k a n}\,\phi_n(x)

This is just a phase-weighted Fourier-style superposition of the atomic orbitals. Translating xx+ax \to x + a relabels the sum (nn1n \to n - 1) and pulls out a factor eikae^{i k a} — exactly the Bloch phase. So Φk\Phi_k is a Bloch state with crystal momentum kk, built from atomic Lego.

The Bloch sum is the tight-binding analogue of the plane wave eikx/Ve^{i k x}/\sqrt{V} in the NFE picture. Both are translation eigenstates labelled by k, but built from different starting bricks. Same theorem, different basis — and the choice of basis is what makes one calculation easy and the other monstrous.

On-Site Energy and the Hopping Integral

Before computing the band, let's pin down the two parameters with their actual integral definitions, so you know what you are actually computing when VASP or Wannier90 reports them.

The on-site energy

ε0=ϕn(x)H^ϕn(x)dx=ϕnH^ϕn\varepsilon_0 = \int \phi^*_n(x)\,\hat{H}\,\phi_n(x)\,dx = \langle \phi_n | \hat{H} | \phi_n \rangle

For an isolated atom this is just the atomic eigenvalue. In a crystal the surrounding ions shift it slightly — the so-called crystal-field correction — but the change is typically a fraction of tt, so we lump it into the parameter and move on.

The hopping integral

t=ϕn(x)H^ϕn+1(x)dxt = -\int \phi^*_n(x)\,\hat{H}\,\phi_{n+1}(x)\,dx

The size of tt is governed almost entirely by the overlap of the two neighbouring orbitals: teκat \propto e^{-\kappa a} for s-orbitals with decay constant κ\kappa. Doubling the bond length can shrink tt by an order of magnitude. That exponential sensitivity is why pressure, strain, and lattice distortions can change the bandwidth dramatically — and why metal-insulator transitions are so often driven by structural changes.

Order of magnitude estimates. For 3d transition-metal oxides, valence-band hopping is t0.2t \sim 0.20.50.5 eV. For Cd–Se covalent bonds in CdSe, t1t \sim 122 eV. For Mn 3d in Mn:CdSe, the d-d hopping is t0.05t \sim 0.05 eV — ten times smaller, hence the famously narrow Mn d-bands and the strong electron correlation that DFT alone cannot describe (Chapter 6).

Diagonalising the 1D Chain — The Cosine Band

Plug the Bloch sum Φk\Phi_k into the Schrödinger equation. Since the basis has a single orbital per cell, the Hamiltonian projected onto the Bloch sums is a single number:

ε(k)=ΦkH^Φk\varepsilon(k) = \langle \Phi_k | \hat{H} | \Phi_k \rangle

Substitute the definition of Φk\Phi_k, keep only on-site and nearest-neighbour matrix elements, and the sums collapse to:

ε(k)=ε0teikateika=ε02tcos(ka)\varepsilon(k) = \varepsilon_0 - t\,e^{i k a} - t\,e^{-i k a} = \varepsilon_0 - 2 t \cos(k a)

That is the entire band structure of a 1D s-band: a single cosine. Three immediate consequences:

  1. The band sits between ε02t\varepsilon_0 - 2t (at k=0k = 0) and ε0+2t\varepsilon_0 + 2t (at k=±π/ak = \pm \pi/a). The total bandwidth is W=4tW = 4 t.
  2. The band bottom is the bonding Bloch sum — all atoms oscillating in phase. The band top is the antibonding sum — alternating signs.
  3. Near k=0k = 0, expanding the cosine gives ε(k)ε02t+ta2k2\varepsilon(k) \approx \varepsilon_0 - 2 t + t a^2 k^2 — a parabola, just like a free electron, but with a different effective mass.

Interactive: Bloch Amplitude and the Cosine Dispersion

Below: a 9-atom slice of the chain (top), the ε(k)\varepsilon(k) band (bottom), and three sliders. Drag kk first — watch the cyan and orange bars on each atom flip from in-phase (bonding, band bottom) at k=0k = 0 to alternating (antibonding, band top) at k=π/ak = \pi/a. Then change tt and watch the entire band stretch like an accordion: the bandwidth bracket on the right shows you W=4tW = 4t live.

Why the band has the shape it has. The cosine is not an accident of mathematics — it is the Fourier transform of a single “hop” on a 1D lattice. In any nearest-neighbour TB model on any lattice, the dispersion is ε(k)=ε0tδeikδ\varepsilon(\mathbf{k}) = \varepsilon_0 - t\,\sum_{\mathbf{\delta}} e^{i \mathbf{k}\cdot\mathbf{\delta}} where the sum is over neighbour vectors δ\mathbf{\delta}. Choose the neighbours, and the band shape follows.

Reading the Band: Bandwidth, Mass, and Group Velocity

Three quantities can be read straight off the cosine without any further calculation:

QuantityFormulaPhysical meaning
Bandwidth WW = 4 tEnergy span of the band. Sets the temperature scale at which the band is fully active.
Group velocity v_g(k)v_g = (1/ℏ) dε/dk = (2 t a / ℏ) sin(k a)Real-space speed of an electron in state |k⟩. Maximum at k = π/(2a); zero at band edges.
Effective mass m*(k)1/m* = (1/ℏ²) d²ε/dk² = (2 t a²/ℏ²) cos(k a)Curvature of the band. Light at the bottom (k = 0), heavy and NEGATIVE at the top (k = π/a).

The negative effective mass at the band top is not a typo. The upper half of the band has its curvature flipped, so an electron accelerated by an electric field there moves opposite the field. This is exactly the “hole” behaviour of semiconductors — the empty states near the top of a filled valence band act like positive carriers.

Effective mass is not the electron's true mass; it is the inertia experienced by an electron in the periodic potential. A flat band ⇒ huge m* ⇒ heavy carriers, low mobility. Mn 3d bands in Mn:CdSe have m30mem^* \sim 30\,m_e — a direct consequence of the small d-d hopping.

Two Orbitals per Cell — Where Bonding/Antibonding Live

Most real materials have more than one orbital per cell. The smallest meaningful upgrade is two orbitals per cell, labelled AA and BB (think H₂ molecules arranged in a chain, or the alternating Cd / Se sublattices of CdSe). With on-site energies εA\varepsilon_A and εB\varepsilon_B and intra-cell hopping tt, the Bloch Hamiltonian is now 2 × 2:

H(k)=(εAtf(k)tf(k)εB),f(k)=1+eikaH(k) = \begin{pmatrix} \varepsilon_A & -t f(k) \\ -t f^*(k) & \varepsilon_B \end{pmatrix},\quad f(k) = 1 + e^{i k a}

Diagonalising gives two bands:

ε±(k)=εA+εB2±(εAεB2)2+t2f(k)2\varepsilon_\pm(k) = \frac{\varepsilon_A + \varepsilon_B}{2} \pm \sqrt{\left(\frac{\varepsilon_A - \varepsilon_B}{2}\right)^2 + t^2 |f(k)|^2}

When εA=εB\varepsilon_A = \varepsilon_B (a homonuclear chain) the two bands touch at k=π/ak = \pi/a — you have recovered the single cosine band by “unfolding”. When εAεB\varepsilon_A \neq \varepsilon_B a gap of size εAεB|\varepsilon_A - \varepsilon_B| opens at the BZ boundary — the same mechanism that makes ionic crystals (NaCl) and polar semiconductors (CdSe, GaAs) insulators even when the simple counting argument predicts a metal.

For CdSe specifically, the lower (bonding) band is mostly Se 4p in character; the upper (antibonding) band is mostly Cd 5s. The band gap is essentially εCd5sεSe4p|\varepsilon_{Cd\,5s} - \varepsilon_{Se\,4p}| plus the hopping correction. We'll see this clearly in the projected DOS of Chapter 7.

Up a Dimension: The 2D Square Lattice

Stack the chain into a 2D square lattice with one s-orbital per site, lattice constant aa, and nearest-neighbour hopping tt in both xx and yy directions. The Bloch Hamiltonian is again 1 × 1 (one orbital per cell), and summing over the four nearest neighbours gives:

ε(k)=ε02t[cos(kxa)+cos(kya)]\varepsilon(\mathbf{k}) = \varepsilon_0 - 2 t\bigl[\cos(k_x a) + \cos(k_y a)\bigr]

Three special k-points organise the geometry of this band:

Pointk-vectorEnergyWhat it represents
Γ(0, 0)ε₀ − 4 tBand bottom — fully bonding
X(π/a, 0)ε₀Saddle point — flat in one direction, curving in the other
M(π/a, π/a)ε₀ + 4 tBand top — fully antibonding

The saddle point at XX is responsible for the famous logarithmic van Hove singularity in the density of states — a feature that drives high-temperature superconductivity in cuprates and ferromagnetism in many transition-metal oxides.

Interactive: 2D Square-Lattice Band Surface

Left: heatmap of ε(kx,ky)\varepsilon(k_x, k_y) over the entire first Brillouin zone. The dotted contours mark the ε=2t,0,+2t\varepsilon = -2t,\,0,\,+2t iso-energy surfaces. Right: the band along the canonical path ΓXMΓ\Gamma \to X \to M \to \Gamma you will see in every band-structure paper. Drag (kx,ky)(k_x, k_y) with the sliders — the purple dot follows on both panels simultaneously.

Notice the bright yellow square at half-filling ε=0\varepsilon = 0. This is the Fermi surface when the band is exactly half full — one electron per site. It is a perfect square: every k-point on it has its antipode connected by a single reciprocal vector Q=(π/a,π/a)\mathbf{Q} = (\pi/a, \pi/a). That property — perfect nesting — is what destabilises the metallic state of half-filled square-lattice compounds and drives them into antiferromagnetic insulators (the cuprates' parent compounds, the nickelates, etc.).

Code Walk-Through: Building H(k) and the Cosine Band

Time to compute. The script below builds the (1 × 1) Bloch Hamiltonian of the 1D chain, sweeps it across the first Brillouin zone, and verifies the bandwidth and effective-mass formulas analytically. Click any line on the right to see exactly which Python objects, NumPy calls, and physical numbers that line is producing.

1D Tight-Binding Diagonaliser — Interactive Trace
🐍tb_1d_chain.py
1import numpy as np

NumPy is Python's numerical-computing workhorse. We use np.array to hold the (tiny, 1×1) Bloch Hamiltonian, np.linspace to build a uniform k-grid across the first Brillouin zone, np.cos for the cosine dispersion, and np.linalg.eigvalsh to diagonalise. eigvalsh is the right routine because every Bloch Hamiltonian is Hermitian — even our trivial 1×1 one.

EXECUTION STATE
numpy = Numerical-computing library: ndarrays, linear algebra, FFT, random numbers, transcendentals.
as np = Conventional alias so we write np.cos, np.pi, np.linspace, np.linalg.eigvalsh, etc.
3Comment: parameter block in atomic units

Reminds us that ℏ = m_e = 1, so every length is in units of the lattice constant a and every energy in units of ℏ²/(2 m_e a²). Real-world units are restored at the end if needed.

4a = 1.0

Lattice constant of the 1D chain. Setting a = 1 chooses our unit of length. The first Brillouin zone is then [−π, π] in our k-units. Crystallographic results are independent of this choice.

EXECUTION STATE
a = 1.0
→ role = Spacing between neighbouring atoms. Sets the size of the BZ and the period of every Bloch quantity.
5eps0 = 0.0

On-site energy of the single s-orbital we keep on every atom. Physically: the diagonal element ⟨φ_n | H | φ_n⟩ in the atomic-orbital basis, after subtracting the chosen energy zero. Setting it to 0 puts the band centre at zero and lets us read the bandwidth straight off ±2t.

EXECUTION STATE
eps0 = 0.0
→ physical meaning = Energy of an electron localised on a single isolated atom (zero of energy). When we 'turn the lattice on', neighbouring orbitals split this level into a band.
6t = 0.5

The hopping integral. Mathematically t = −⟨φ_n | H | φ_{n±1}⟩, the matrix element of the Hamiltonian between an orbital on site n and its nearest neighbour. Physically it measures how easily an electron tunnels from one atom to the next; large t = strong covalent overlap = wide band, small t = strong localisation = narrow band.

EXECUTION STATE
t = 0.5
→ predicted bandwidth = W = 4t = 2.0 (we'll verify on the last line of the script).
8Comment: defining the Bloch Hamiltonian

Marks the start of the function block. The next three comments derive H(k) so a future reader does not have to dig in a textbook to remember why the formula is what it is.

9Comment: 1×1 because of a single orbital per cell

With one s-orbital per primitive cell, the Bloch basis at fixed k contains exactly one state — the periodic Bloch sum φ_k(x) = Σ_n e^{i k a n} φ(x − n a). H(k) is therefore a single number, not a matrix. The np.array wrapper keeps the API consistent so we can call eigvalsh on it without special-casing.

10Comment: H(k) = eps0 − 2 t cos(k a)

The closed-form result: each Bloch state mixes itself with its two nearest-neighbour copies, picking up a phase factor e^{±ika}. Adding them gives 2 cos(k a). The minus sign comes from the convention t = −⟨φ_n | H | φ_{n±1}⟩ — choose t > 0 and the band bottom sits at k = 0.

11def H_bloch(k) → np.ndarray

Builds the (1×1) Bloch Hamiltonian at wavevector k. Wrapping the scalar value in np.array makes it a proper matrix that np.linalg.eigvalsh can consume — the same pattern scales to multi-orbital cells where H(k) becomes a real matrix.

EXECUTION STATE
⬇ input: k (float) = Wavevector in units of 1/a. We'll evaluate the function at 9 points spanning the first BZ.
⬆ returns = np.ndarray of shape (1, 1) holding eps0 − 2 t cos(k a). At k = 0 it equals −2t (band bottom); at k = ±π it equals +2t (band top).
12Docstring

Documents the function so help(H_bloch) prints something sensible. The parenthetical reminds us we are in atomic units; otherwise eigvalsh's output cannot be interpreted unambiguously.

13return np.array([[eps0 - 2 * t * np.cos(k * a)]])

Computes the single matrix element and packs it as a 2D ndarray of shape (1, 1). Double brackets [[…]] are required: a single bracket would make a 1D array, which eigvalsh would reject.

EXECUTION STATE
📚 np.array(object) = Constructs an ndarray from the supplied list. Nested lists become 2D arrays: rows × columns. Here a single nested list ⇒ shape (1, 1).
📚 np.cos(x) = Element-wise cosine. On a scalar it returns a scalar; on an ndarray, an ndarray of the same shape. Argument is in radians.
→ at k = 0 = eps0 - 2*t*cos(0) = 0 - 2*0.5*1 = -1.000 → band bottom
→ at k = π/2 = eps0 - 2*t*cos(π/2) = 0 - 2*0.5*0 = 0.000 → band centre
→ at k = π = eps0 - 2*t*cos(π) = 0 - 2*0.5*(-1) = +1.000 → band top
⬆ return: H(k) =
[[-1.0000]]   (shape (1, 1), at k = 0)
15Comment: BZ sampling block

Marks the start of the sampling code. We will evaluate H(k) on a uniform k-grid spanning [−π/a, π/a] (the first BZ), diagonalise each, and print the resulting band.

16ks = np.linspace(-np.pi / a, np.pi / a, 9)

Builds a uniform k-grid of 9 points covering the closed interval [−π, π]. We choose 9 because it is small enough to print cleanly and large enough to expose the full cosine shape (-π, ±π/2, 0, ±π/4, etc.).

EXECUTION STATE
📚 np.linspace(start, stop, num) = Returns an ndarray of `num` evenly spaced points from `start` to `stop` (both endpoints INCLUDED). Step = (stop−start)/(num−1).
⬇ arg 1: start = -π/a ≈ -3.1416 = Left edge of the first Brillouin zone.
⬇ arg 2: stop = +π/a ≈ +3.1416 = Right edge of the first BZ. Note linspace INCLUDES this endpoint, unlike arange.
⬇ arg 3: num = 9 = Number of samples ⇒ step = 2π/8 = π/4 ≈ 0.7854.
⬆ result: ks = [-3.1416 -2.3562 -1.5708 -0.7854 0.0000 0.7854 1.5708 2.3562 3.1416]
17print(" k eps(k)")

Prints the column header for the band table. Plain string, no formatting.

18print("-" * 28)

Prints a horizontal divider — the string '-' repeated 28 times. Pure cosmetic separator between header and data rows.

19for k in ks: — sweep across the first Brillouin zone

Iterates over every k-point in the grid. Each pass builds H(k), diagonalises it, and prints a single (k, ε) row.

LOOP TRACE · 9 iterations
k = -3.1416 (= -π)
eig = +1.0000 (band top — antibonding standing wave)
k = -2.3562 (= -3π/4)
eig = +0.7071
k = -1.5708 (= -π/2)
eig = -0.0000 (band centre — signed zero from cos(π/2) ≈ 6e-17)
k = -0.7854 (= -π/4)
eig = -0.7071
k = 0.0000 (= Γ)
eig = -1.0000 (band bottom — bonding state)
k = +0.7854 (= +π/4)
eig = -0.7071
k = +1.5708 (= +π/2)
eig = -0.0000 (signed zero, same reason)
k = +2.3562 (= +3π/4)
eig = +0.7071
k = +3.1416 (= +π)
eig = +1.0000 (band top, periodic image of -π)
20eig = np.linalg.eigvalsh(H_bloch(k))[0]

Builds H(k), passes it to eigvalsh (the Hermitian-eigenvalue routine), and takes the first (only) eigenvalue. For this 1×1 case the eigenvalue is just the matrix entry; the call is structurally identical to the multi-orbital case so you can swap the model later without touching this loop.

EXECUTION STATE
📚 np.linalg.eigvalsh(M) = Returns a sorted 1D ndarray of eigenvalues of a Hermitian matrix M. The trailing 'h' = 'Hermitian' (faster, more accurate than the general eig() because it exploits the symmetry).
⬇ arg: H_bloch(k) = (1, 1) ndarray = [[eps0 - 2 t cos(k a)]]. Trivially Hermitian.
[0] = Index 0 of the returned 1-element array — pulls the scalar eigenvalue out of the 1-D wrapper.
→ at k = 0 = eigvalsh([[−1.0]])[0] = −1.0000 (the band bottom)
→ at k = π = eigvalsh([[+1.0]])[0] = +1.0000 (the band top)
21print(f"{k:+.4f} {eig:+.4f}")

Pretty-prints one (k, eigenvalue) row. The f-string format spec '+.4f' forces a leading sign and 4 decimal places so the columns line up. Output rows form the half-cosine you see in the interactive plot above.

EXECUTION STATE
→ output line at k = 0 = +0.0000 -1.0000
→ full output = k eps(k) ---------------------------- -3.1416 +1.0000 -2.3562 +0.7071 -1.5708 -0.0000 -0.7854 -0.7071 +0.0000 -1.0000 +0.7854 -0.7071 +1.5708 -0.0000 +2.3562 +0.7071 +3.1416 +1.0000
23Comment: bandwidth and effective mass

Section divider. The next two lines extract the headline numbers a chemist or device engineer cares about: the bandwidth (which sets the conductivity) and the band-edge curvature (which sets carrier mobility).

24W = 4 * t

The bandwidth — the energy gap between the band top (+2t at k = ±π/a) and band bottom (−2t at k = 0). For nearest-neighbour hopping in 1D this is exactly 4t. In a Z-coordinated solid the same argument gives W = 2 z t, so doubling the connectivity doubles the bandwidth.

EXECUTION STATE
W = 2.000 ( = 4 * 0.5 )
→ physical meaning = How wide the band is. Wide ⇒ delocalised, metallic, free-electron-like; narrow ⇒ localised, correlated, atomic-like.
25m_star = 1.0 / (2 * t * a ** 2)

Effective mass at the band bottom k = 0. Expand ε(k) around k = 0: ε ≈ −2t + t a² k² + O(k⁴). Comparing with the free-electron expansion ε = ℏ² k²/(2 m*) gives m* = ℏ² / (2 t a²). In atomic units (ℏ = 1) this is just 1/(2 t a²).

EXECUTION STATE
m_star = 1.000 ( = 1 / (2 * 0.5 * 1²) )
** (power op) = Python's exponentiation operator. a ** 2 = a*a; works on both scalars and ndarrays.
→ physical meaning = How heavy a near-band-edge electron 'feels'. Smaller t (narrow band) ⇒ larger m* ⇒ slower carriers ⇒ lower mobility.
26print(f"\nBandwidth W = 4 t = {W:.3f}")

Prints the bandwidth value, prefixed by a newline so it stands away from the band table above. The '.3f' format gives 3 decimals — enough to read 4t to the chosen accuracy.

EXECUTION STATE
→ output = Bandwidth W = 4 t = 2.000
27print(f"Effective mass m* (k = 0) = {m_star:.3f}")

Prints the effective mass next to the bandwidth so the student sees both at a glance. Both numbers are direct consequences of the single parameter t — choose t and the rest of the band is determined.

EXECUTION STATE
→ output = Effective mass m* (k = 0) = 1.000
4 lines without explanation
1import numpy as np
2
3# === Tight-binding parameters (atomic units, hbar = m_e = 1) ===
4a = 1.0           # lattice constant
5eps0 = 0.0        # on-site energy of the s-orbital
6t = 0.5           # nearest-neighbour hopping integral
7
8# === Bloch Hamiltonian H(k) ===
9# For a 1D chain with one s-orbital per cell, the Bloch matrix is 1 x 1:
10#     H(k) = eps0 - 2 * t * cos(k * a)
11def H_bloch(k):
12    """Return the 1x1 Bloch Hamiltonian at wavevector k (atomic units)."""
13    return np.array([[eps0 - 2 * t * np.cos(k * a)]])
14
15# === Sample the band over the first Brillouin zone ===
16ks = np.linspace(-np.pi / a, np.pi / a, 9)
17print("    k          eps(k)")
18print("-" * 28)
19for k in ks:
20    eig = np.linalg.eigvalsh(H_bloch(k))[0]
21    print(f"{k:+.4f}     {eig:+.4f}")
22
23# === Bandwidth and effective mass at k = 0 ===
24W = 4 * t                              # full bandwidth W = 4 t
25m_star = 1.0 / (2 * t * a ** 2)        # m* = hbar^2 / (2 t a^2)
26print(f"\nBandwidth W = 4 t          = {W:.3f}")
27print(f"Effective mass m* (k = 0)  = {m_star:.3f}")

Run the script and you will see the following output:

📝text
1k          eps(k)
2----------------------------
3-3.1416     +1.0000
4-2.3562     +0.7071
5-1.5708     -0.0000
6-0.7854     -0.7071
7+0.0000     -1.0000
8+0.7854     -0.7071
9+1.5708     -0.0000
10+2.3562     +0.7071
11+3.1416     +1.0000
12
13Bandwidth W = 4 t          = 2.000
14Effective mass m* (k = 0)  = 1.000

Three sanity checks to perform before you trust this kind of code on a real material:

  • The band is symmetric in kk (time-reversal symmetry: ε(k)=ε(k)\varepsilon(k) = \varepsilon(-k)) ✓
  • The endpoints k=πk = -\pi and k=+πk = +\pi give identical energies (BZ periodicity) ✓
  • The bandwidth is exactly 4t and the effective mass exactly 1/(2ta2)1/(2 t a^2)
Swap H_bloch for the 2 × 2 Hamiltonian of the previous section, sample the same kk-grid, and you immediately have the band structure of an alternating diatomic chain — the simplest model of a polar semiconductor. Two parameters separate Si from an insulator like NaCl.

Multi-Orbital Crystals: Slater–Koster in One Picture

Real solids have several orbitals per atom (s, p, d …) and their hopping integrals depend on the relative orientation of the bond and the lobes. The systematic accounting is the Slater–Koster scheme (Slater & Koster, 1954). It expresses the matrix element between any two orbitals in a diatomic bond as a linear combination of just four irreducible parameters: (ssσ)(ss\sigma), (spσ)(sp\sigma), (ppσ)(pp\sigma), and (ppπ)(pp\pi) — one per overlap symmetry.

Bond integralSketchSignWhere it dominates
(ssσ)head-on s–s overlapnegative (bonding lower)alkali metals (Na, K)
(spσ)s pointing into a p lobenegativeCd–Se, Si–Si
(ppσ)p–p along the bond axisnegativesecond-period diatomics (N₂, O₂ ground states)
(ppπ)p–p sidewayspositive (antibonding lower)graphene π-bands, transition-metal d–d

Every full tight-binding calculation in this book reduces to: choose the orbital basis, look up (or fit) the Slater-Koster parameters, build a ~10 × 10 Bloch Hamiltonian, and diagonalise on a k-grid. The resulting bands match VASP/PAW DFT bands to a few percent in the valence region.


Tight Binding vs Nearly Free Electrons: Two Views, One Truth

Here is the same band, the same physics, expressed in the languages of the two opposite limits:

AspectNearly free electrons (Section 4.2)Tight binding (this section)
Starting basisplane waves e^{ikr}Bloch sums of atomic orbitals Φ_k(r)
Small parameterlattice potential V_Ginter-site hopping t
Where bands open gapsBZ boundaries via Bragg conditionAnywhere two orbitals are close in energy
Band shape near k = 0parabola ε = ℏ²k²/2m + correctionsparabola ε = ε₀ − 2t + t a² k²
Effective mass≈ m_e for free-electron metalsℏ²/(2 t a²) — controlled by hopping
Best forNa, Al, Cu (sp metals)MnO, NiO, CdSe-d, graphene π, Mn 3d in Mn:CdSe
Computational costmany plane waves needed for localised orbitalstiny matrix per k-point

VASP's plane-wave DFT solves the full problem and is closer in spirit to the NFE side; once it's done, post-processing through Wannier functions translates the result into the TB language so chemists, theorists, and model-Hamiltonian practitioners can see what is going on.


VASP Connection: Wannier Functions, COHP, Projected DOS

VASP uses plane waves, but the post-processing pipeline gives you every tight-binding number you might want. Three workflows are worth knowing.

1. Projected DOS via LORBIT

Setting LORBIT = 11 in INCAR tells VASP to project the Kohn-Sham wavefunctions onto spherical harmonics centred on each ion. The output (PROCAR, DOSCAR) tells you which fraction of each band has s, p, or d character on each atom — the answer to “is this CdSe band Cd 5s or Se 4p?”.

📄ini
1# INCAR — atom- and orbital-resolved DOS / PROCAR
2LORBIT = 11        # decompose by ion AND lm channel (s, py, pz, px, dxy, ...)
3NEDOS  = 3001      # smooth DOS grid
4ISMEAR = -5        # tetrahedron method — accurate DOS for insulators

2. Wannier90 — the actual TB Hamiltonian

With LWANNIER90 = .TRUE. VASP cooperates with the external Wannier90 program to construct maximally localised Wannier functions — the precise mathematical incarnation of the orbitals ϕn\phi_n we have been treating symbolically. Wannier90 then writes the hopping integrals tmn(R)t_{mn}(\mathbf{R}) to a file (seedname_hr.dat) so you can build any H(k)H(\mathbf{k}) you want by Fourier transform — the same equation we wrote out by hand for the 1D chain, just with a few more orbitals and a richer R\mathbf{R}-set.

📝text
1# wannier90.win (excerpt, for the Cd–Se 4 valence + 4 conduction bands)
2num_wann      = 8        # number of Wannier functions to construct
3num_bands     = 16       # bands to disentangle from
4projections
5 Cd:s; Cd:p
6 Se:s; Se:p
7end projections
8
9# Output of interest:
10#   wannier90_hr.dat   — H(R) hopping integrals (this IS the TB model)
11#   wannier90_band.dat — band structure built from H(k) = Σ_R H(R) e^{ikR}

3. LOBSTER + COHP — covalency in numbers

LOBSTER (the “Local Orbital Basis Suite Towards Electronic Structure Reconstruction”) reads VASP's plane-wave wavefunctions and rewrites them in a localised atomic-orbital basis. The output includes Crystal Orbital Hamilton Population (COHP) curves: integrated bonding character as a function of energy. A negative COHP at the Fermi level means the bond is anti-bonding there — a recipe for instability, often a signal of imminent magnetic or structural ordering.

📄ini
1# lobsterin (run AFTER a converged VASP single-point with LORBIT = 11)
2COHPstartEnergy   -10
3COHPendEnergy       5
4basisSet pbeVaspFit2015
5includeOrbitals s p
6cohpBetween atom 1 atom 2
7cohpBetween atom 1 atom 3   # all symmetry-distinct neighbour pairs
For our Mn:CdSe project (Chapter 7) the LOBSTER COHP between Mn and the four neighbouring Se atoms tells you, in one number, how covalent the Mn-Se bond actually is. Pure ionic Mn²⁺ would give zero COHP — the d-orbitals would be perfectly atomic. Realistically you find about 15–20% covalent character, and that small admixture is what mediates the antiferromagnetic super-exchange responsible for the unusual magnetism of dilute magnetic semiconductors.

Summary

  1. The tight-binding model starts from atomic orbitals and lets the lattice couple them through a hopping integral tt. It is the natural language whenever electrons are well-localised (d- and f-bands, 2D oxides, molecular crystals, the Mn d-shell of Mn:CdSe).
  2. Bloch sums Φk=N1/2neikanϕn\Phi_k = N^{-1/2}\sum_n e^{i k a n}\phi_n are the TB analogue of plane waves: translation eigenstates built from atomic Lego.
  3. Two parameters — on-site energy ε0\varepsilon_0 and hopping tt — determine the entire 1D s-band: ε(k)=ε02tcos(ka)\varepsilon(k) = \varepsilon_0 - 2 t \cos(k a). Bandwidth is W=4tW = 4t; effective mass at the bottom is m=2/(2ta2)m^* = \hbar^2/(2 t a^2).
  4. Two orbitals per cell give two bands separated by an on-site energy gap εAεB|\varepsilon_A - \varepsilon_B| — the same mechanism that opens the band gap of polar semiconductors and ionic insulators.
  5. On the 2D square lattice the band is ε(k)=2t[cos(kxa)+cos(kya)]\varepsilon(\mathbf{k}) = -2 t[\cos(k_x a) + \cos(k_y a)] — with a 4t-4t minimum at Γ\Gamma, a saddle point at XX, and a perfect-nesting Fermi surface at half-filling.
  6. NFE and TB are two limits of the same problem. NFE wins for delocalised electrons (Na, Al); TB wins for localised ones (Mn d, oxides). The full DFT calculation done by VASP interpolates between them — and Wannier90, LOBSTER, and LORBIT-projected DOS expose the answer in TB language.

Next section we step back and ask the awkward question: every single-particle picture we have used so far ignores the fact that electrons repel each other. The free electron gas, the NFE avoided crossing, the tight-binding cosine — none of them contain a Coulomb interaction. The genuine many-body problem of ~10²³ interacting electrons is the next mountain to climb, and the path up it is called density functional theory.

Loading comments...