.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/topology/plot_edge_states.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_api_gallery_topology_plot_edge_states.py: Edge States on Ribbons =========================== Cutting a periodic 2D model into a ribbon (periodic in one direction, finite/open in the other -- see :func:`tbkit.kspace.ribbon`) is the standard way to see edge physics in a tight-binding band structure. Two classic examples: 1. A zigzag graphene ribbon has a partially-flat band pinned at E=0, built entirely from states localized on the two edges. 2. Adding Kane-Mele intrinsic spin-orbit coupling (see :doc:`plot_haldane_topology` for the closely related Haldane model) opens a bulk gap and produces *helical* edge states: gapless, spin-momentum-locked modes that cross the bulk gap, one Kramers pair per edge. This is the original model of a 2D (Z2) topological insulator. .. GENERATED FROM PYTHON SOURCE LINES 19-39 .. code-block:: Python import numpy as np import matplotlib.pyplot as plt from tbkit.lattice import Lattice from tbkit.kspace import ribbon, PAULI 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)] lat = Lattice(unit_cell=unit_cell, prim_vec=prim_vec) t1 = 1. graphene_hop = [{'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}] width = 30 .. GENERATED FROM PYTHON SOURCE LINES 40-42 Zigzag graphene ribbon: the E=0 edge flat band -------------------------------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 42-61 .. code-block:: Python # A narrow version of the same ribbon, drawn: periodic left-to-right and # open top and bottom. It is those two zigzag edges that carry the states # found below. rib_small = ribbon(lat, graphene_hop, width=6, direction=1) # ribbon() stacks its rows along the other primitive vector, which here has # a component along the periodic direction too, so tiling its cell as-is # draws a slanted parallelogram. Sliding each site back by whole multiples # of a1 is the same lattice, drawn as the strip one pictures. a1 = np.array(rib_small.lat.prim_vec[0]) folded = [{'tag': d['tag'], 'r0': tuple(np.array(d['r0']) - round(np.dot(d['r0'], a1)/(a1 @ a1))*a1)} for d in rib_small.lat.unit_cell] lat_draw = Lattice(unit_cell=folded, prim_vec=rib_small.lat.prim_vec) lat_draw.get_lattice(n1=9) vis = System(lat_draw) vis.set_hopping([{'n': 1, 't': t1}]) fig_rib = Plot(vis).lattice(plt_hop=True, ms=9, figsize=(8, 4.5)) .. image-sg:: /api/gallery/topology/images/sphx_glr_plot_edge_states_001.png :alt: plot edge states :srcset: /api/gallery/topology/images/sphx_glr_plot_edge_states_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 62-63 The ribbon actually diagonalized is 30 cells wide: .. GENERATED FROM PYTHON SOURCE LINES 63-83 .. code-block:: Python rib = ribbon(lat, graphene_hop, width=width, direction=1) ks = np.linspace(-np.pi, np.pi, 400) en = rib.get_bands(ks[:, None]) tol = 1e-2 # a finite-width ribbon splits the edge-state degeneracy by an # amount exponentially small in the width, so "zero energy" # needs a little tolerance away from the zone boundary. n_zero_modes = np.sum(np.any(np.abs(en) < tol, axis=1)) print('Zigzag ribbon: {} k-points host a (near-)zero mode (out of {}), ' 'vs. the 1/3 expected analytically.'.format(n_zero_modes, len(ks))) assert n_zero_modes > len(ks) // 4 fig, ax = plt.subplots() ax.plot(ks, en, 'b', lw=0.7) ax.set_xlabel('$k$') ax.set_ylabel('$E$') ax.set_title('Zigzag graphene ribbon ({} unit cells wide)'.format(width)) ax.set_xlim([ks[0], ks[-1]]) .. image-sg:: /api/gallery/topology/images/sphx_glr_plot_edge_states_002.png :alt: Zigzag graphene ribbon (30 unit cells wide) :srcset: /api/gallery/topology/images/sphx_glr_plot_edge_states_002.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Zigzag ribbon: 138 k-points host a (near-)zero mode (out of 400), vs. the 1/3 expected analytically. (-3.141592653589793, 3.141592653589793) .. GENERATED FROM PYTHON SOURCE LINES 84-94 Kane-Mele ribbon: helical edge states inside a spin-orbit gap -------------------------------------------------------------------- The intrinsic (sz-conserving) spin-orbit term opens a bulk gap, but a Kramers pair of edge states still crosses zero energy somewhere in the Brillouin zone (its momentum is set by where the bulk Dirac points K, K' project onto this ribbon's 1D Brillouin zone) -- Kramers' theorem forbids gapping out a single pair of counter-propagating edge modes, which is the hallmark of a Z2 topological insulator (contrast with the zigzag ribbon above, where *nothing* protects the flat band from a generic perturbation). .. GENERATED FROM PYTHON SOURCE LINES 94-122 .. code-block:: Python lam = 0.1 kane_mele_hop = list(graphene_hop) for R in [(1, 0), (0, 1), (1, -1)]: kane_mele_hop.append({'i': 0, 'j': 0, 'R': R, 't': 1j*lam*PAULI['z']}) kane_mele_hop.append({'i': 1, 'j': 1, 'R': R, 't': -1j*lam*PAULI['z']}) rib_km = ribbon(lat, kane_mele_hop, width=width, direction=1, spin=True) en_km = rib_km.get_bands(ks[:, None]) min_abs_e_per_k = np.min(np.abs(en_km), axis=1) i_cross = np.argmin(min_abs_e_per_k) print('Kane-Mele ribbon: edge-state crossing near k={:.4f}, E={:.5f}.' .format(ks[i_cross], en_km[i_cross, np.argmin(np.abs(en_km[i_cross]))])) assert min_abs_e_per_k[i_cross] < 0.01 # deep in the "bulk" region of k (far from the edge-state crossing), the # gap must be fully open. bulk_gap = min_abs_e_per_k[np.argmin(np.abs(ks))] # at k=0 print('Bulk gap at k=0 (far from the edge-state crossing): {:.4f}.'.format(bulk_gap)) assert bulk_gap > 0.5 fig2, ax2 = plt.subplots() ax2.plot(ks, en_km, 'b', lw=0.7) ax2.set_xlabel('$k$') ax2.set_ylabel('$E$') ax2.set_ylim([-1., 1.]) ax2.set_title('Kane-Mele ribbon: helical edge states in the bulk gap') ax2.set_xlim([ks[0], ks[-1]]) .. image-sg:: /api/gallery/topology/images/sphx_glr_plot_edge_states_003.png :alt: Kane-Mele ribbon: helical edge states in the bulk gap :srcset: /api/gallery/topology/images/sphx_glr_plot_edge_states_003.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Kane-Mele ribbon: edge-state crossing near k=-1.8188, E=0.00161. Bulk gap at k=0 (far from the edge-state crossing): 1.0095. (-3.141592653589793, 3.141592653589793) .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.867 seconds) .. _sphx_glr_download_api_gallery_topology_plot_edge_states.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_edge_states.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_edge_states.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_edge_states.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_