Note
Go to the end to download the full example code.
The Haldane Model: Berry Curvature and a Topological Phase Transition#
The Haldane model is honeycomb graphene with a complex next-nearest-neighbor hopping \(t_2 e^{i\phi}\) (breaking time-reversal symmetry, e.g. via a staggered flux pattern with zero net flux) and a staggered sublattice onsite energy \(\pm M\) (breaking inversion symmetry). It is the first model shown to realize a Chern insulator – a gapped phase with quantized Hall conductance and no net magnetic field.
At the Dirac points \(K\) and \(K'\) the complex hopping opens
gaps of \(2|M \pm 3\sqrt{3}\,t_2\sin\phi|\), so the topological/trivial
phase boundary sits at Haldane’s \(|M| = 3\sqrt{3}\,t_2\,|\sin\phi|\):
the lower band’s Chern number, computed via
chern_number(), is \(\pm 1\) for \(|M|\)
below that (\(+1\) here, with \(\phi=\pi/2\)), 0 above it.
import numpy as np
import matplotlib.pyplot as plt
from tbkit.lattice import Lattice
from tbkit.kspace import KSpace, reciprocal_vectors, high_symmetry_path
from tbkit.system import System
from tbkit.plot import Plot
DX, DY = 0.5 * 3 ** 0.5, 0.5
unit_cell = [{'tag': 'a', 'r0': (0., 0.)}, {'tag': 'b', 'r0': (DX, DY)}]
prim_vec = [(2*DX, 0.), (DX, 1.5)]
t1, t2, phi = 1., 0.2, np.pi / 2
M_c = 3 * np.sqrt(3) * t2 * abs(np.sin(phi)) # critical mass (see module docstring)
def haldane(M):
'''Build the Haldane model with staggered onsite energy +-M.'''
lat = Lattice(unit_cell=unit_cell, prim_vec=prim_vec)
hal = KSpace(lat)
hal.set_hopping([{'i': 0, 'j': 1, 'R': (0, 0), 't': t1},
{'i': 0, 'j': 1, 'R': (-1, 0), 't': t1},
{'i': 0, 'j': 1, 'R': (0, -1), 't': t1}])
# next-nearest-neighbor hopping t2*exp(+i*phi) along the three
# second-neighbor vectors a2, -a1, a1-a2 (120 degrees apart, so the
# model keeps the lattice's C3 symmetry; the reverse bonds, added as
# Hermitian conjugates, carry exp(-i*phi)). Same 3 vectors for both
# sublattices, but with opposite chirality (t2*exp(+-i*phi)) -- this
# circulating "staggered flux" is what breaks time-reversal symmetry
# without any net magnetic field through the unit cell.
for R in [(0, 1), (-1, 0), (1, -1)]:
hal.set_hopping([{'i': 0, 'j': 0, 'R': R, 't': t2*np.exp(1j*phi)}])
hal.set_hopping([{'i': 1, 'j': 1, 'R': R, 't': t2*np.exp(-1j*phi)}])
hal.set_onsite({'a': M, 'b': -M})
return hal
The lattice#
The Haldane model lives on the plain honeycomb lattice – two orbitals per unit cell, drawn in two colours below, with the nearest-neighbor bonds shown. What the model adds is invisible in this picture: a second-neighbor hopping within each sublattice, complex and of opposite chirality on the two, which is why it breaks time-reversal symmetry without any net flux through the cell.

Chern number across the topological phase transition#
masses = np.linspace(0., 2*M_c, 21)
chern = [haldane(M).chern_number(bands=[0], nk=40) for M in masses]
# The gaps at K and K' are 2|M + M_c| and 2|M - M_c|: Haldane's formula,
# checked directly. The one at K' closes at M = M_c.
b1, b2 = (np.array(v) for v in reciprocal_vectors(prim_vec))
K_pt = (b1 - b2) / 3
for M in [0., 0.5*M_c, 1.5*M_c]:
en_K = np.linalg.eigvalsh(haldane(M).get_ham(K_pt))
en_Kp = np.linalg.eigvalsh(haldane(M).get_ham(-K_pt))
assert np.isclose(en_K[1] - en_K[0], 2*abs(M + M_c), atol=1e-10)
assert np.isclose(en_Kp[1] - en_Kp[0], 2*abs(M - M_c), atol=1e-10)
print('Critical mass M_c = 3*sqrt(3)*t2*sin(phi) = {:.4f}'.format(M_c))
print('Chern number at M=0 (topological): {:.4f}'.format(chern[0]))
print('Chern number at M=2*M_c (trivial): {:.4f}'.format(chern[-1]))
assert np.isclose(chern[0], 1., atol=1e-2)
assert np.isclose(chern[-1], 0., atol=1e-2)
print('Phase transition reproduced: C = 1 (topological) -> C = 0 (trivial). OK')
fig, ax = plt.subplots()
ax.plot(masses/M_c, chern, 'o-b')
ax.axvline(1., color='k', ls='--', lw=1)
ax.set_xlabel('$M/M_c$')
ax.set_ylabel('Chern number (lower band)')
ax.set_title('Haldane model: topological phase transition')

Critical mass M_c = 3*sqrt(3)*t2*sin(phi) = 1.0392
Chern number at M=0 (topological): 1.0000
Chern number at M=2*M_c (trivial): -0.0000
Phase transition reproduced: C = 1 (topological) -> C = 0 (trivial). OK
Text(0.5, 1.0, 'Haldane model: topological phase transition')
Berry curvature in the topological phase#
The total flux of berry_curvature() through
the zone is \(2\pi\): the Chern number. With a gap this large
(\(2M_c \approx 2t\)) the curvature is spread over the whole zone
rather than concentrated at the Dirac points.
plot_berry_curvature() draws it as a density
over the first Brillouin zone, the hexagon whose corners are K and K’.

Band structure in the topological phase#
high_symmetry_path() recognizes the hexagonal
lattice and returns the path \(\Gamma\) - M - K - \(\Gamma\).

Total running time of the script: (0 minutes 0.233 seconds)