.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/tight_binding/plot_brillouin_zones_and_high_symmetry_paths.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_tight_binding_plot_brillouin_zones_and_high_symmetry_paths.py: Brillouin Zones and Standard High-Symmetry Paths ===================================================== L. Brillouin (1930) gave the zone that carries his name: the set of :math:`\mathbf{k}` closer to the origin than to any other reciprocal lattice vector :math:`\mathbf{G}`, the Wigner-Seitz cell of the reciprocal lattice. Its faces are the planes where Bragg reflection :math:`|\mathbf{k}| = |\mathbf{k} - \mathbf{G}|` opens band gaps, and its corners and face centres are the points of highest symmetry, where bands reach their extrema, cross or are degenerate. A band structure is therefore drawn along a path through them. W. Setyawan and S. Curtarolo (2010) fixed one standard path per Bravais lattice, so that band structures from different codes can be compared at a glance. :func:`tbkit.kspace.high_symmetry_path` recognizes the Bravais lattice from the primitive vectors, whatever their choice and orientation, and returns the standard path; :func:`tbkit.kspace.cubic_path` builds any path of a cubic crystal from the tables of high-symmetry points. This example checks that every point it returns lies on the zone boundary, that the path does not depend on the choice of primitive vectors, and that the s-band of the fcc and bcc lattices takes its closed-form values at the labelled points. .. GENERATED FROM PYTHON SOURCE LINES 25-55 .. code-block:: Python from itertools import product import numpy as np import matplotlib.pyplot as plt from tbkit.lattice import Lattice from tbkit.kspace import KSpace, cubic_path, high_symmetry_path, reciprocal_vectors from tbkit.plot import Plot from tbkit.system import System def lattice_2d(prim_vec): return Lattice(unit_cell=[{'tag': 'a', 'r0': (0., 0.)}], prim_vec=prim_vec) def zone_check(points, prim_vec): ''' For each k-point: is it in the closed Wigner-Seitz cell (no G closer than the origin), and on its boundary (some G exactly as close)? ''' rec = np.array(reciprocal_vectors(prim_vec)) gs = np.array([n @ rec for n in product(range(-3, 4), repeat=2) if any(n)]) inside, boundary = [], [] for k in points: d0, dg = k @ k, np.sum((k - gs) ** 2, axis=1) inside.append(bool(np.all(d0 <= dg + 1e-9))) boundary.append(bool(np.any(np.isclose(d0, dg)))) return inside, boundary .. GENERATED FROM PYTHON SOURCE LINES 56-64 The five 2D Bravais lattices -------------------------------- Square, rectangular, hexagonal, centred rectangular and oblique. Each path starts and ends at :math:`\Gamma`. Every other point is on the boundary of the zone: equidistant from :math:`\Gamma` and from at least one other reciprocal lattice vector, and closer to :math:`\Gamma` than to any. The oblique lattice has no symmetry beyond inversion, so its path follows the edges of half the zone (:math:`X, H, C, H_1, Y`). .. GENERATED FROM PYTHON SOURCE LINES 64-83 .. code-block:: Python s3 = np.sqrt(3.) bravais = {'square': [(1., 0.), (0., 1.)], 'rectangular': [(1., 0.), (0., 1.6)], 'hexagonal': [(1., 0.), (0.5, s3 / 2)], 'centred rectangular': [(1., 0.), (0.5, 1.3)], 'oblique': [(1., 0.), (0.3, 1.2)]} # The five lattices in real space, with their primitive vectors # :math:`\mathbf{a}_1` and :math:`\mathbf{a}_2` (arrows): fig_lat, axes = plt.subplots(1, 5, figsize=(17, 3.6)) for ax, (name, prim_vec) in zip(axes, bravais.items()): patch = lattice_2d(prim_vec) patch.get_lattice(n1=5, n2=5) Plot(System(patch)).lattice(ms=7, ax=ax) for a in prim_vec: ax.annotate('', xy=a, xytext=(0., 0.), arrowprops=dict(arrowstyle='->', color='C3', lw=2)) ax.set_title(name) .. image-sg:: /api/gallery/tight_binding/images/sphx_glr_plot_brillouin_zones_and_high_symmetry_paths_001.png :alt: square, rectangular, hexagonal, centred rectangular, oblique :srcset: /api/gallery/tight_binding/images/sphx_glr_plot_brillouin_zones_and_high_symmetry_paths_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 84-85 Their Brillouin zones and high-symmetry paths: .. GENERATED FROM PYTHON SOURCE LINES 85-116 .. code-block:: Python fig_bz, axes = plt.subplots(1, 5, figsize=(17, 3.9)) for ax, (name, prim_vec) in zip(axes, bravais.items()): points, labels = high_symmetry_path(lattice_2d(prim_vec)) inside, boundary = zone_check(points, prim_vec) print('{:<20} {:<36} on the boundary: {}'.format( name, ' '.join(lab.strip('$').replace('\\', '') for lab in labels), boundary)) assert all(inside) assert not boundary[0] and not boundary[-1] # Gamma, the centre assert all(boundary[1:-1]) # draw the zone: the Bragg planes of the nearest reciprocal vectors rec = np.array(reciprocal_vectors(prim_vec)) kmax = 1.15 * max(np.linalg.norm(p) for p in points) kx = np.linspace(-kmax, kmax, 401) kxx, kyy = np.meshgrid(kx, kx) kk = np.stack([kxx, kyy], axis=-1) gs = np.array([n @ rec for n in product(range(-2, 3), repeat=2) if any(n)]) margin = np.min(np.sum((kk[..., None, :] - gs) ** 2, axis=-1), axis=-1) - np.sum(kk ** 2, axis=-1) ax.contourf(kxx, kyy, margin, levels=[0., np.inf], colors=['#dce8f5']) ax.contour(kxx, kyy, margin, levels=[0.], colors='#3b76af') path = np.array(points) ax.plot(path[:, 0], path[:, 1], 'o-', color='#c53a32', lw=2, ms=5) for p, lab in zip(points[:-1], labels[:-1]): ax.annotate(lab, p, textcoords='offset points', xytext=(4, 4), fontsize=13) ax.set_aspect('equal') ax.set_xticks([]) ax.set_yticks([]) ax.set_title(name) fig_bz.suptitle('First Brillouin zones (blue) and the standard paths (red)') fig_bz.tight_layout() .. image-sg:: /api/gallery/tight_binding/images/sphx_glr_plot_brillouin_zones_and_high_symmetry_paths_002.png :alt: First Brillouin zones (blue) and the standard paths (red), square, rectangular, hexagonal, centred rectangular, oblique :srcset: /api/gallery/tight_binding/images/sphx_glr_plot_brillouin_zones_and_high_symmetry_paths_002.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none square Gamma X M Gamma on the boundary: [False, True, True, False] rectangular Gamma X S Y Gamma on the boundary: [False, True, True, True, False] hexagonal Gamma M K Gamma on the boundary: [False, True, True, False] centred rectangular Gamma X H C H_1 Y Gamma on the boundary: [False, True, True, True, True, True, False] oblique Gamma X H C H_1 Y Gamma on the boundary: [False, True, True, True, True, True, False] .. GENERATED FROM PYTHON SOURCE LINES 118-126 The path does not depend on the primitive vectors -------------------------------------------------------- :math:`(\mathbf{a}_1, \mathbf{a}_2)` and :math:`(\mathbf{a}_1, \mathbf{a}_1 + \mathbf{a}_2)` span the same triangular lattice, and so does any rotation of them. The Bravais lattice is recognized from a reduced basis, so the three give the same :math:`M` and :math:`K`, at :math:`|M| = 2\pi/(\sqrt3 a)` and :math:`|K| = 4\pi/(3a)` from :math:`\Gamma`. .. GENERATED FROM PYTHON SOURCE LINES 126-136 .. code-block:: Python a1, a2 = np.array([1., 0.]), np.array([0.5, s3 / 2]) rot = np.array([[np.cos(0.3), -np.sin(0.3)], [np.sin(0.3), np.cos(0.3)]]) for basis in ([a1, a2], [a1, a1 + a2], [rot @ a1, rot @ a2]): points, labels = high_symmetry_path(lattice_2d([tuple(v) for v in basis])) assert labels == [r'$\Gamma$', 'M', 'K', r'$\Gamma$'] assert np.isclose(np.linalg.norm(points[1]), 2 * np.pi / s3) assert np.isclose(np.linalg.norm(points[2]), 4 * np.pi / 3) print('|M| = 2 pi/(sqrt3 a) and |K| = 4 pi/(3a) for three bases of the triangular lattice.') .. rst-class:: sphx-glr-script-out .. code-block:: none |M| = 2 pi/(sqrt3 a) and |K| = 4 pi/(3a) for three bases of the triangular lattice. .. GENERATED FROM PYTHON SOURCE LINES 137-154 fcc and bcc: the s band at the labelled points --------------------------------------------------- One s orbital per site with nearest-neighbour hopping :math:`-t`. On the fcc lattice (12 neighbours at :math:`a/\sqrt2`), .. math:: E(\mathbf{k}) = -4t\left[\cos\tfrac{k_xa}{2}\cos\tfrac{k_ya}{2} + \cos\tfrac{k_ya}{2}\cos\tfrac{k_za}{2} + \cos\tfrac{k_za}{2}\cos\tfrac{k_xa}{2}\right], so :math:`E(\Gamma) = -12t`, :math:`E(X) = E(W) = 4t`, :math:`E(L) = 0` and :math:`E(K) = E(U) = (4\sqrt2 - 2)t`. On the bcc lattice (8 neighbours at :math:`\sqrt3a/2`), :math:`E = -8t\cos\tfrac{k_xa}{2}\cos\tfrac{k_ya}{2}\cos\tfrac{k_za}{2}`: :math:`-8t` at :math:`\Gamma`, :math:`+8t` at :math:`H`, and 0 at :math:`N` and :math:`P`. The primitive vectors given here are not the conventional cubic axes; the path is found anyway. .. GENERATED FROM PYTHON SOURCE LINES 154-184 .. code-block:: Python a, t = 1., 1. cells = {'fcc': [(0., a/2, a/2), (a/2, 0., a/2), (a/2, a/2, 0.)], 'bcc': [(-a/2, a/2, a/2), (a/2, -a/2, a/2), (a/2, a/2, -a/2)]} expected = {'fcc': {r'$\Gamma$': -12., 'X': 4., 'W': 4., 'L': 0., 'K': 4 * np.sqrt(2.) - 2, 'U': 4 * np.sqrt(2.) - 2}, 'bcc': {r'$\Gamma$': -8., 'H': 8., 'N': 0., 'P': 0.}} fig_3d, axes = plt.subplots(1, 2, figsize=(12, 4.5)) for ax, (name, prim_vec) in zip(axes, cells.items()): lat = Lattice(unit_cell=[{'tag': 'a', 'r0': (0., 0., 0.)}], prim_vec=prim_vec) ks = KSpace(lat) ks.set_hopping([{'n': 1, 't': -t}]) points, labels = high_symmetry_path(lat) energies = ks.get_bands(np.array(points))[:, 0] for lab, e in zip(labels, energies): assert np.isclose(e, expected[name][lab] * t, atol=1e-12), (name, lab, e) print('{}: {}'.format(name, ', '.join('E({}) = {:+.4f}'.format( lab.strip('$').replace('\\', ''), e) for lab, e in zip(labels, energies)))) dist, en = ks.k_path(points, nk=60) ax.plot(dist, en[:, 0], lw=2) for node in ks.nodes: ax.axvline(node, color='k', lw=0.5) ax.set_xticks(ks.nodes) ax.set_xticklabels(labels, fontsize=13) ax.set_xlim(dist[0], dist[-1]) ax.set_ylabel('$E/t$') ax.set_title('{}: s band along the Setyawan-Curtarolo path'.format(name)) fig_3d.tight_layout() .. image-sg:: /api/gallery/tight_binding/images/sphx_glr_plot_brillouin_zones_and_high_symmetry_paths_003.png :alt: fcc: s band along the Setyawan-Curtarolo path, bcc: s band along the Setyawan-Curtarolo path :srcset: /api/gallery/tight_binding/images/sphx_glr_plot_brillouin_zones_and_high_symmetry_paths_003.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none fcc: E(Gamma) = -12.0000, E(X) = +4.0000, E(W) = +4.0000, E(K) = +3.6569, E(Gamma) = -12.0000, E(L) = +0.0000, E(U) = +3.6569, E(W) = +4.0000, E(L) = +0.0000, E(K) = +3.6569 bcc: E(Gamma) = -8.0000, E(H) = +8.0000, E(N) = +0.0000, E(Gamma) = -8.0000, E(P) = -0.0000, E(H) = +8.0000 .. GENERATED FROM PYTHON SOURCE LINES 185-197 Other paths of cubic crystals --------------------------------- Tables give the high-symmetry points of cubic crystals in units of :math:`2\pi/a` along the cubic axes, :math:`a` the side of the conventional cube. :func:`~tbkit.kspace.cubic_path` turns them into k-points. From the Setyawan-Curtarolo table of the fcc lattice it rebuilds the path of ``high_symmetry_path``. Any other path follows from the same points, e.g. along the whole cube diagonal: the s band of fcc rises from :math:`-12t` at :math:`\Gamma` to 0 at :math:`L` and comes back to :math:`-12t` at :math:`(1, 1, 1)\,2\pi/a`, a reciprocal lattice vector of fcc (:math:`L` is the midpoint of the hexagonal face it bisects), so an image of :math:`\Gamma`. .. GENERATED FROM PYTHON SOURCE LINES 197-214 .. code-block:: Python G = r'$\Gamma$' fcc_table = {G: (0, 0, 0), 'X': (0, 1, 0), 'W': (.5, 1, 0), 'K': (.75, .75, 0), 'L': (.5, .5, .5), 'U': (.25, 1, .25), r"$\Gamma'$": (1, 1, 1)} fcc_lat = Lattice(unit_cell=[{'tag': 'a', 'r0': (0., 0., 0.)}], prim_vec=cells['fcc']) standard, names = high_symmetry_path(fcc_lat) rebuilt, _ = cubic_path(fcc_table, names, a=a) assert np.allclose(np.array(rebuilt), np.array(standard)) print('cubic_path rebuilds the fcc path from its table of points.') fcc = KSpace(fcc_lat) fcc.set_hopping([{'n': 1, 't': -t}]) diagonal, labels = cubic_path(fcc_table, [G, 'L', r"$\Gamma'$"], a=a) e_diag = fcc.get_bands(np.array(diagonal))[:, 0] assert np.allclose(e_diag, [-12. * t, 0., -12. * t]) dist, en = fcc.k_path(diagonal, nk=60) fig_diag = fcc.plot_bands(node_labels=labels, figsize=(5.5, 4.)) .. image-sg:: /api/gallery/tight_binding/images/sphx_glr_plot_brillouin_zones_and_high_symmetry_paths_004.png :alt: plot brillouin zones and high symmetry paths :srcset: /api/gallery/tight_binding/images/sphx_glr_plot_brillouin_zones_and_high_symmetry_paths_004.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none cubic_path rebuilds the fcc path from its table of points. .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.531 seconds) .. _sphx_glr_download_api_gallery_tight_binding_plot_brillouin_zones_and_high_symmetry_paths.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_brillouin_zones_and_high_symmetry_paths.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_brillouin_zones_and_high_symmetry_paths.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_brillouin_zones_and_high_symmetry_paths.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_