CoCalc Public Filesw4_haldane / haldane_model.ipynbOpen with one click!
Authors: Irfan , Anton Akhmerov, Bas Nijholt, Tomas Rosdahl, Jay Sau, Bernard van Heck
Compute Environment: Ubuntu 18.04 (Deprecated)
In [1]:
import sys sys.path.append('../code') from init_mooc_nb import * init_notebook() import scipy from wraparound import wraparound %output size=150 pi_ticks = [(-np.pi, r'$-\pi$'), (0, '0'), (np.pi, r'$\pi$')] def haldane(w=20, boundary='zigzag'): def ribbon_shape_zigzag(pos): return (-0.5 / np.sqrt(3) - 0.1 <= pos[1] < np.sqrt(3) * w / 2 + 0.01) def ribbon_shape_armchair(pos): return (-1 <= pos[0] < w) def onsite(site, p): if site.family == a: return p.m else: return -p.m def nn_hopping(site1, site2, p): return p.t def nnn_hopping(site1, site2, p): return 1j * p.t_2 lat = kwant.lattice.honeycomb() a, b = lat.sublattices nnn_hoppings_a = (((-1, 0), a, a), ((0, 1), a, a), ((1, -1), a, a)) nnn_hoppings_b = (((1, 0), b, b), ((0, -1), b, b), ((-1, 1), b, b)) if boundary == 'zigzag': sys = kwant.Builder(kwant.TranslationalSymmetry((1, 0))) sys[lat.shape(ribbon_shape_zigzag, (0, 0))] = onsite elif boundary == 'armchair': sys = kwant.Builder(kwant.TranslationalSymmetry((0, np.sqrt(3)))) sys[lat.shape(ribbon_shape_armchair, (0, 0))] = onsite else: sys = kwant.Builder(kwant.TranslationalSymmetry(*lat.prim_vecs)) sys[lat.shape(lambda pos: True, (0, 0))] = onsite sys[lat.neighbors()] = nn_hopping sys[[kwant.builder.HoppingKind(*hopping) for hopping in nnn_hoppings_a]] = nnn_hopping sys[[kwant.builder.HoppingKind(*hopping) for hopping in nnn_hoppings_b]] = nnn_hopping return sys def Qi_Wu_Zhang(): def onsite(site, p): return - p.mu * pauli.sz def hopx(site1, site2, p): return - 0.5j * p.delta * pauli.sy - p.t * pauli.sz def hopy(site1, site2, p): return - 1j * p.gamma * pauli.sx - p.gamma * pauli.sz lat = kwant.lattice.square() sys = kwant.Builder(kwant.TranslationalSymmetry(*lat.prim_vecs)) sys[lat.shape(lambda pos: True, (0, 0))] = onsite sys[kwant.HoppingKind((1,0), lat)] = hopx sys[kwant.HoppingKind((0,1), lat)] = hopy return sys def berry_curvature(sys, p, ks, num_filled_bands=1): """Berry curvature of a system. Parameters: ----------- sys : kwant.Builder A 2D infinite system. p : SimpleNamespace The arguments expected by the system. ks : 1D array-like Values of momentum grid to be used for Berry curvature calculation. num_filled_bands : int The number of filled bands. Returns: -------- bc : 2D array Berry curvature on each square in a `ks x ks` grid. """ # Calculate an array of eigenvectors. B = np.array(sys.symmetry.periods).T A = B.dot(np.linalg.inv(B.T.dot(B))) sys = wraparound(sys).finalized() def energy(kx, ky): k = np.array([kx, ky]) kx, ky = np.linalg.solve(A, k) H = sys.hamiltonian_submatrix([p, kx, ky], sparse=False) return scipy.linalg.eigh(H)[1] vectors = np.array([[energy(kx, ky)[:, :num_filled_bands] for kx in ks] for ky in ks]) # The actual Berry curvature calculation vectors_x = np.roll(vectors, 1, 0) vectors_xy = np.roll(vectors_x, 1, 1) vectors_y = np.roll(vectors, 1, 1) shifted_vecs = [vectors, vectors_x, vectors_xy, vectors_y] v_shape = vectors.shape shifted_vecs = [i.reshape(-1, v_shape[-2], v_shape[-1]) for i in shifted_vecs] dets = np.ones(len(shifted_vecs[0]), dtype=complex) for vec, shifted in zip(shifted_vecs, np.roll(shifted_vecs, 1, 0)): dets *= [np.linalg.det(a.T.conj().dot(b)) for a, b in zip(vec, shifted)] bc = np.angle(dets).reshape(int(np.sqrt(len(dets))), -1) bc = (bc + np.pi / 2) % (np.pi) - np.pi / 2 return bc def plot_berry_curvature(sys, p, ks=None, title=None): if ks is None: ks = np.linspace(-np.pi, np.pi, 150, endpoint=False) bc = berry_curvature(sys, p, ks)[1:-1,1:-1] vmax = max(np.abs(bc).min(), np.abs(bc).max()) kwargs = {'bounds': (ks.min(), ks.min(), ks.max(), ks.max()), 'kdims': [r'$k_x$', r'$k_y$']} if callable(title): kwargs['label'] = title(p) plot = {'xticks': pi_ticks, 'yticks': pi_ticks} style = {'clims': [-vmax, vmax]} return holoviews.Image(bc, **kwargs)(plot=plot, style=style) def title(p): title = r'$t={:.2}$, $t_2={:.2}$, $M={:.2}$' return title.format(p.t, p.t_2, p.m)
Populated the namespace with: np, matplotlib, kwant, holoviews, init_notebook, interact, display_html, plt, pf, SimpleNamespace, pprint_matrix, scientific_number, pretty_fmt_complex from code/edx_components: MoocVideo, PreprintReference, MoocDiscussion, MoocCheckboxesAssessment, MoocMultipleChoiceAssessment, MoocPeerAssessment, MoocSelfAssessment from code/functions: spectrum, hamiltonian_array, h_k, pauli
HoloViewsJS, MatplotlibJS successfully loaded in this cell.

Press this button to show/hide the code used in the notebook:

Intro

Duncan Haldane from Princeton University will teach us about an interesting two dimensional toy-model which he introduced in 1988, and which has become a prototype for the anomalous quantum Hall effect.

In [2]:
MoocVideo("7nVO4uMm-do", src_location='4.2-intro')

We will now study the model in detail, starting from the beginning. Along the way, we will also learn about the Chern number, the bulk topological invariant of a quantum Hall state.

Dirac cones in graphene

In the last chapter we saw how it is possible to obtain a quantum Hall state by coupling one-dimensional systems. At the end, our recipe was to first obtain a Dirac cone, add a mass term to it and finally to make this mass change sign. Following this recipe we were able to obtain chiral edge states without applying an external magnetic field.

There is a real (and a very important) two-dimensional system which has Dirac cones: graphene. So in this chapter we will take graphene and make it into a topological system with chiral edge states.

Graphene is a single layer of carbon atoms arranged in a honeycomb lattice. It is a triangular lattice with two atoms per unit cell, type AA and type BB, represented by red and blue sites in the figure:

Hence, the wave function in a unit cell can be written as a vector (ΨA,ΨB)T(\Psi_A, \Psi_B)^T of amplitudes on the two sites AA and BB. Taking a simple tight-binding model where electrons can hop between neighboring sites with hopping strength tt, one obtains the Bloch Hamiltonian:

H0(k)=(0h(k)h(k)0), H_0(\mathbf{k})= \begin{pmatrix} 0 & h(\mathbf{k}) \\ h^\dagger(\mathbf{k}) & 0 \end{pmatrix}\,,

with k=(kx,ky)\mathbf{k}=(k_x, k_y) and

h(k)=t1iexp(ikai).h(\mathbf{k}) = t_1\,\sum_i\,\exp\,\left(i\,\mathbf{k}\cdot\mathbf{a}_i\right)\,.

Here ai\mathbf{a}_i are the three vectors in the figure, connecting nearest neighbors of the lattice [we set the lattice spacing to one, so that for instance a1=(1,0)\mathbf{a}_1=(1,0)]. Introducing a set of Pauli matrices σ\sigma which act on the sublattice degree of freedom, we can write the Hamiltonian in a compact form as

H0(k)=t1i[σxcos(kai)σysin(kai)].H_0(\mathbf{k}) = t_1\,\sum_i\,\left[\sigma_x\,\cos(\mathbf{k}\cdot\mathbf{a}_i)-\sigma_y \,\sin(\mathbf{k}\cdot\mathbf{a}_i)\right]\,.

The energy spectrum E(k)=±h(k)E(\mathbf{k}) = \pm \,\left|h(\mathbf{k})\right| gives rise to the famous band structure of graphene, with the two bands touching at the six corners of the Brillouin zone:

In [3]:
p = SimpleNamespace(t=1.0, t_2=0.0, m=0.0, phi=np.pi/2) sys = haldane(boundary='infinite') k = (4 / 3) * np.linspace(-np.pi, np.pi, 150) spectrum(sys, p, k_x=k, k_y=k, title=title)

Only two of these six Dirac cones are really distinct, the ones at K=(2π/3,2π/33)\mathbf{K}=(2\pi/3, 2\pi/3\sqrt{3}) and K=(2π/3,2π/33)\mathbf{K}'=(2\pi/3, -2\pi/3\sqrt{3}). All the others can be obtained by adding some reciprocal lattice vector to K\mathbf{K} and K\mathbf{K}'.

Discrete symmetries of graphene

The symmetries of graphene were discussed intensively in the video, so let's review them.

As we already said in our first week, graphene is the prototype of a system with sublattice symmetry, which makes the Hamiltonian block off-diagonal with respect to the two sublattices. The sublattice symmetry reads

σzH0(k)σz=H0(k).\sigma_z\,H_0(\mathbf{k})\,\sigma_z = -H_0(\mathbf{k})\,.

Sublattice symmetry is only approximate, and it is consequence of the nearest neighbor tight-binding model. Just like the inversion symmetry mentioned in the video, it protects the Dirac points and needs to be broken in order to open a gap.

In addition to sublattice and inversion symmetry, the honeycomb lattice also has a three-fold rotation symmetry around the center of the unit cell. This symmetry is important to make the Dirac cones appear in the first place, but it will not play a role in all that follows.

Finally, there is time-reversal symmetry, which at the moment is perfectly preserved in our tight-binding model. Since we are not considering the spin degree of freedom of the electrons, the time-reversal symmetry operator in real space is just complex conjugation. In momentum space representation, time-reversal symmetry reads

H0(k)=H0(k). H_0(\mathbf{k}) = H_0^*(-\mathbf{k})\,.

It's important to note that time-reversal symmetry sends K\mathbf{K} into K\mathbf{K}' and therefore it exchanges the two Dirac cones.

The product of (approximate) sublattice and time-reversal symmetries yields a further discrete symmetry, a particle-hole symmetry σzH(k)σz=H0(k)\sigma_z H^*(-\mathbf{k})\,\sigma_z = -H_0(\mathbf{k}).

Making graphene topological

Let's recall that our goal is to make our graphene sheet enter a quantum Hall state, with chiral edge states. The first necessary step is to make the bulk of the system gapped.

How can we open a gap in graphene? The Dirac points are protected by both sublattice (inversion) and time-reversal symmetry. So there are many ways we can think of to open an energy gap at K\mathbf{K} and K\mathbf{K}'.

First try

The easiest way to break sublattice symmetry is to assign an opposite onsite energy MM or M-M to the AA or BB sites respectively. The Hamiltonian is then given by

H0(k)+Mσz. H_0(\mathbf{k}) + M\,\sigma_z\,.

This leads to a gapped spectrum,

E(k)=±h(k)2+M2.E(\mathbf{k})=\pm \sqrt{\left|h(\mathbf{k})\right|^2 + M^2}\,.

However, we quickly realize that by doing this we end up in a rather boring situation. Taking the limit Mt1\left|M\right| \gg t_1, we obtain electronic states which are localized in one of the two sublattices AA or BB, independent of the sign of MM. Most importantly, there is no trace of edge states.

It's easy to see why this mass term is hopeless: it preserves time-reversal symmetry. And with the time-reversal symmetry present, it is definitely impossible to obtain chiral edge states.

Second try

There is another, more ingenious way to gap out the Dirac cones in graphene, which is the essence of today's model. It involves adding imaginary second-nearest neighbor hoppings, with the following distinctive pattern:

With the direction of the arrow, we denote the direction in which the hopping is +it2+it_2 (it is it2-it_2 in the opposite direction).

Note the following things about these hoppings:

  • they are purely imaginary and, furthermore, they all have the same chirality, in the sense that they all follow the orientation of your right hand, if the thumb points out from the screen.
  • they couple sites of same type: AA with AA and BB with BB.

These characteristics tell us that the new hoppings break both time-reversal symmetry and sublattice symmetry. Now the full Hamiltonian becomes

H(k)=H0(k)+Mσz+2t2iσzsin(kbi). H(\mathbf{k}) = H_0(\mathbf{k})+ M\sigma_z + 2t_2\sum_i\,\sigma_z\,\sin(\mathbf{k}\cdot\mathbf{b}_i)\,.

The last term changes sign under time-reversal symmetry, breaking it. This is the Hamiltonian of the Haldane model.

Let's see what happens to the system when these special second neighbor hoppings are turned on:

In [4]:
p = SimpleNamespace(t=1.0, m=0.2, phi=np.pi/2, t_2=None) sys = haldane(boundary='infinite') k = (4 / 3) * np.linspace(-np.pi, np.pi, 101) kwargs = {'k_x': k, 'k_y': k, 'title': title} t_2s = np.linspace(0, 0.10, 11) holoviews.HoloMap({p.t_2: spectrum(sys, p, **kwargs) for p.t_2 in t_2s}, kdims=[r'$t_2$'])