tbkit 0.4.1__tar.gz → 0.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. {tbkit-0.4.1/tbkit.egg-info → tbkit-0.4.2}/PKG-INFO +8 -2
  2. {tbkit-0.4.1 → tbkit-0.4.2}/README.md +7 -1
  3. {tbkit-0.4.1 → tbkit-0.4.2}/pyproject.toml +1 -1
  4. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/__init__.py +1 -1
  5. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/error_handling.py +10 -0
  6. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/kspace.py +96 -0
  7. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/transport.py +80 -19
  8. {tbkit-0.4.1 → tbkit-0.4.2/tbkit.egg-info}/PKG-INFO +8 -2
  9. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit.egg-info/SOURCES.txt +1 -0
  10. tbkit-0.4.2/tests/test_surface_spectral.py +182 -0
  11. {tbkit-0.4.1 → tbkit-0.4.2}/LICENSE +0 -0
  12. {tbkit-0.4.1 → tbkit-0.4.2}/setup.cfg +0 -0
  13. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/bdg.py +0 -0
  14. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/bridges.py +0 -0
  15. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/dos.py +0 -0
  16. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/exceptional.py +0 -0
  17. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/floquet.py +0 -0
  18. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/graphene.py +0 -0
  19. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/higher_order.py +0 -0
  20. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/io.py +0 -0
  21. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/kpm.py +0 -0
  22. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/lattice.py +0 -0
  23. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/lattices.py +0 -0
  24. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/meanfield.py +0 -0
  25. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/moire.py +0 -0
  26. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/neighbours.py +0 -0
  27. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/occupation.py +0 -0
  28. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/optics.py +0 -0
  29. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/orbital.py +0 -0
  30. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/plot.py +0 -0
  31. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/propagation.py +0 -0
  32. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/save.py +0 -0
  33. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/slater_koster.py +0 -0
  34. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit/system.py +0 -0
  35. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit.egg-info/dependency_links.txt +0 -0
  36. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit.egg-info/requires.txt +0 -0
  37. {tbkit-0.4.1 → tbkit-0.4.2}/tbkit.egg-info/top_level.txt +0 -0
  38. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_bridges.py +0 -0
  39. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_correlations.py +0 -0
  40. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_dos.py +0 -0
  41. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_error_handling.py +0 -0
  42. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_error_handling_structures.py +0 -0
  43. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_exceptional.py +0 -0
  44. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_floquet.py +0 -0
  45. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_graphene.py +0 -0
  46. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_green_occupation.py +0 -0
  47. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_hall.py +0 -0
  48. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_higher_order.py +0 -0
  49. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_interactions.py +0 -0
  50. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_io.py +0 -0
  51. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_kpm.py +0 -0
  52. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_kspace.py +0 -0
  53. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_large_lattices.py +0 -0
  54. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_lattice.py +0 -0
  55. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_lattices.py +0 -0
  56. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_magnetic.py +0 -0
  57. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_moire.py +0 -0
  58. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_neighbours.py +0 -0
  59. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_non_hermitian_bands.py +0 -0
  60. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_optics.py +0 -0
  61. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_orbitals.py +0 -0
  62. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_plot.py +0 -0
  63. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_propagation.py +0 -0
  64. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_regressions.py +0 -0
  65. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_save.py +0 -0
  66. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_system.py +0 -0
  67. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_three_d.py +0 -0
  68. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_topology.py +0 -0
  69. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_transport.py +0 -0
  70. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_transport_response.py +0 -0
  71. {tbkit-0.4.1 → tbkit-0.4.2}/tests/test_wannier90.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tbkit
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Summary: A Python package to build and solve Tight-Binding models.
5
5
  Author-email: Charles Poli <cpoli374@gmail.com>
6
6
  Maintainer-email: cpoli <cpoli374@gmail.com>
@@ -97,6 +97,9 @@ topology alongside superconductivity.
97
97
  Kane-Mele) and Zeeman splitting.
98
98
  * **Edge states**: cut a ribbon (periodic in one direction, finite in the
99
99
  other) out of any periodic model, to see edge/surface physics.
100
+ * **Surface spectral functions** of semi-infinite crystals (iterative
101
+ surface Green's function): edge and surface states, e.g. the surface
102
+ Dirac cone of a 3D topological insulator, without a finite slab.
100
103
  * **Density of states**, Gaussian- or Lorentzian-broadened, from either a
101
104
  real-space spectrum or a Brillouin-zone mesh.
102
105
  * A small library of ready-made lattices (chain, square, triangular,
@@ -116,7 +119,7 @@ topology alongside superconductivity.
116
119
  |----------------------------|---------------------------------------------------------|
117
120
  | `tbkit.Lattice` | Define and manipulate site positions and sublattices. |
118
121
  | `tbkit.System` | Build the real-space Hamiltonian from a `Lattice` and solve it. |
119
- | `tbkit.KSpace` | Build and solve the Bloch Hamiltonian of a periodic `Lattice`; bands, Berry curvature/Chern numbers, anomalous and spin Hall conductivities, ribbons, DOS. |
122
+ | `tbkit.KSpace` | Build and solve the Bloch Hamiltonian of a periodic `Lattice`; bands, Berry curvature/Chern numbers, anomalous and spin Hall conductivities, ribbons, surface spectral functions, DOS. |
120
123
  | `tbkit.Plot` | Plot lattices, spectra, eigenstates, and the density of states. |
121
124
  | `tbkit.Propagation` | Time-evolve a wavepacket. |
122
125
  | `tbkit.Save` | Save figures/animations to disk. |
@@ -292,6 +295,7 @@ thumbnailed example gallery under `docs/source/api/gallery/`.
292
295
  | [`topology/plot_tknn_hofstadter.py`](examples/topology/plot_tknn_hofstadter.py) | TKNN: Chern numbers of the Hofstadter bands from magnetic supercells, and the Diophantine equation. |
293
296
  | [`topology/plot_kane_mele_z2.py`](examples/topology/plot_kane_mele_z2.py) | The Kane-Mele Z2 invariant from Wannier-centre flow. |
294
297
  | [`topology/plot_fu_kane_parity.py`](examples/topology/plot_fu_kane_parity.py) | The Fu-Kane parity criterion on the BHZ model, checked against the Wannier flow. |
298
+ | [`topology/plot_3d_topological_insulator.py`](examples/topology/plot_3d_topological_insulator.py) | 3D topological insulators: the strong index and the surface Dirac cone, from the surface spectral function of a semi-infinite crystal. |
295
299
  | [`topology/plot_quantum_geometry.py`](examples/topology/plot_quantum_geometry.py) | The quantum metric and Berry curvature of a Bloch band. |
296
300
  | [`topology/plot_tenfold_way.py`](examples/topology/plot_tenfold_way.py) | Symmetry classes of tight-binding models: the tenfold way. |
297
301
  | [`topology/plot_local_chern_marker.py`](examples/topology/plot_local_chern_marker.py) | The local Chern marker of a finite, disordered Haldane flake. |
@@ -345,6 +349,8 @@ Rendered docs (tutorial, API reference, example gallery): https://cpoli.github.i
345
349
  each one linked to the corresponding **tbkit** functionality and example
346
350
  above.
347
351
  * `docs/source/tbkit.rst` -- the API reference (auto-generated from docstrings).
352
+ * [`ROADMAP.md`](ROADMAP.md) -- what is planned next, and what tbkit
353
+ does not do yet (compared with Kwant and PythTB).
348
354
 
349
355
  Build the HTML docs with `cd docs && make html` (output in `docs/build/html`).
350
356
 
@@ -69,6 +69,9 @@ topology alongside superconductivity.
69
69
  Kane-Mele) and Zeeman splitting.
70
70
  * **Edge states**: cut a ribbon (periodic in one direction, finite in the
71
71
  other) out of any periodic model, to see edge/surface physics.
72
+ * **Surface spectral functions** of semi-infinite crystals (iterative
73
+ surface Green's function): edge and surface states, e.g. the surface
74
+ Dirac cone of a 3D topological insulator, without a finite slab.
72
75
  * **Density of states**, Gaussian- or Lorentzian-broadened, from either a
73
76
  real-space spectrum or a Brillouin-zone mesh.
74
77
  * A small library of ready-made lattices (chain, square, triangular,
@@ -88,7 +91,7 @@ topology alongside superconductivity.
88
91
  |----------------------------|---------------------------------------------------------|
89
92
  | `tbkit.Lattice` | Define and manipulate site positions and sublattices. |
90
93
  | `tbkit.System` | Build the real-space Hamiltonian from a `Lattice` and solve it. |
91
- | `tbkit.KSpace` | Build and solve the Bloch Hamiltonian of a periodic `Lattice`; bands, Berry curvature/Chern numbers, anomalous and spin Hall conductivities, ribbons, DOS. |
94
+ | `tbkit.KSpace` | Build and solve the Bloch Hamiltonian of a periodic `Lattice`; bands, Berry curvature/Chern numbers, anomalous and spin Hall conductivities, ribbons, surface spectral functions, DOS. |
92
95
  | `tbkit.Plot` | Plot lattices, spectra, eigenstates, and the density of states. |
93
96
  | `tbkit.Propagation` | Time-evolve a wavepacket. |
94
97
  | `tbkit.Save` | Save figures/animations to disk. |
@@ -264,6 +267,7 @@ thumbnailed example gallery under `docs/source/api/gallery/`.
264
267
  | [`topology/plot_tknn_hofstadter.py`](examples/topology/plot_tknn_hofstadter.py) | TKNN: Chern numbers of the Hofstadter bands from magnetic supercells, and the Diophantine equation. |
265
268
  | [`topology/plot_kane_mele_z2.py`](examples/topology/plot_kane_mele_z2.py) | The Kane-Mele Z2 invariant from Wannier-centre flow. |
266
269
  | [`topology/plot_fu_kane_parity.py`](examples/topology/plot_fu_kane_parity.py) | The Fu-Kane parity criterion on the BHZ model, checked against the Wannier flow. |
270
+ | [`topology/plot_3d_topological_insulator.py`](examples/topology/plot_3d_topological_insulator.py) | 3D topological insulators: the strong index and the surface Dirac cone, from the surface spectral function of a semi-infinite crystal. |
267
271
  | [`topology/plot_quantum_geometry.py`](examples/topology/plot_quantum_geometry.py) | The quantum metric and Berry curvature of a Bloch band. |
268
272
  | [`topology/plot_tenfold_way.py`](examples/topology/plot_tenfold_way.py) | Symmetry classes of tight-binding models: the tenfold way. |
269
273
  | [`topology/plot_local_chern_marker.py`](examples/topology/plot_local_chern_marker.py) | The local Chern marker of a finite, disordered Haldane flake. |
@@ -317,6 +321,8 @@ Rendered docs (tutorial, API reference, example gallery): https://cpoli.github.i
317
321
  each one linked to the corresponding **tbkit** functionality and example
318
322
  above.
319
323
  * `docs/source/tbkit.rst` -- the API reference (auto-generated from docstrings).
324
+ * [`ROADMAP.md`](ROADMAP.md) -- what is planned next, and what tbkit
325
+ does not do yet (compared with Kwant and PythTB).
320
326
 
321
327
  Build the HTML docs with `cd docs && make html` (output in `docs/build/html`).
322
328
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tbkit"
7
- version = "0.4.1"
7
+ version = "0.4.2"
8
8
  description = "A Python package to build and solve Tight-Binding models."
9
9
  readme = "README.md"
10
10
  license = "BSD-3-Clause"
@@ -6,7 +6,7 @@
6
6
 
7
7
  """tbkit: build and solve Tight-Binding models."""
8
8
 
9
- __version__ = "0.4.1"
9
+ __version__ = "0.4.2"
10
10
 
11
11
  __all__ = [
12
12
  "Lattice", "System", "Plot", "Propagation", "Save", "KSpace",
@@ -1546,6 +1546,16 @@ def lead_direction(direction):
1546
1546
  raise ValueError('\n\nParameter direction must be +1 or -1.\n')
1547
1547
 
1548
1548
 
1549
+ def surface_side(side):
1550
+ '''
1551
+ Check parameter *side* of *KSpace.surface_spectral_function*: +1 or -1.
1552
+
1553
+ :raises ValueError: Parameter side must be +1 or -1.
1554
+ '''
1555
+ if isinstance(side, bool) or side not in (1, -1):
1556
+ raise ValueError('\n\nParameter side must be +1 or -1.\n')
1557
+
1558
+
1549
1559
  def nearest_cells(n):
1550
1560
  '''
1551
1561
  Check that a lead hopping reaches at most the neighbouring cells.
@@ -13,6 +13,7 @@ import tbkit.dos as dos
13
13
  import tbkit.occupation as occupation
14
14
  import tbkit.neighbours as neighbours
15
15
  from tbkit.lattice import Lattice
16
+ from tbkit.transport import _green_surface_bulk
16
17
 
17
18
 
18
19
  PI = np.pi
@@ -1881,6 +1882,101 @@ class KSpace():
1881
1882
  return np.array([[np.trace(p0 @ dp[mu] @ dp[nu]) for nu in range(self.dim)]
1882
1883
  for mu in range(self.dim)])
1883
1884
 
1885
+ def surface_spectral_function(
1886
+ self, ks: ArrayLike, energies: ArrayLike, direction: int, side: int = 1,
1887
+ eta: float = 1e-2, bulk: bool = False, max_iter: int = 10000,
1888
+ ) -> NDArray[np.float64]:
1889
+ r'''
1890
+ Get the spectral function of the surface of a semi-infinite crystal,
1891
+
1892
+ .. math::
1893
+
1894
+ A_s(\mathbf{k}_\parallel, E) = -\frac{1}{\pi}\,
1895
+ \mathrm{Im}\,\mathrm{Tr}\, G_{00}(\mathbf{k}_\parallel, E + i\eta)\, ,
1896
+
1897
+ with :math:`G_{00}` the Green's function of the outermost unit cell:
1898
+ the crystal fills the cells :math:`n\,\mathbf{a}_d`,
1899
+ :math:`n = 0, 1, 2, \dots` along the primitive vector
1900
+ :math:`\mathbf{a}_d` (``lat.prim_vec[direction]``), times *side*, and
1901
+ is periodic along the others. It comes from the Lopez Sancho-Rubio
1902
+ decimation (see *transport.surface_green*) of the chain of principal
1903
+ layers (as many cells as the longest hopping along
1904
+ :math:`\mathbf{a}_d` spans), so the crystal is truly semi-infinite: no
1905
+ finite slab, and no states of the opposite surface. It is the
1906
+ tight-binding picture of an ARPES map -- surface states (edge states,
1907
+ in 2D; the end state of a 1D chain) show up as sharp lines inside the
1908
+ bulk gaps. With *bulk*, the same quantity for a unit cell deep inside
1909
+ the crystal: the bulk bands projected on the surface Brillouin zone.
1910
+
1911
+ :param ks: Real array, shape (nk, dim). k-points, in the coordinates
1912
+ of *get_ham* (e.g. from a *k_path* of the surface Brillouin zone).
1913
+ Only :math:`\mathbf{k}_\parallel` matters: the result does not
1914
+ depend on the component along the reciprocal vector
1915
+ :math:`\mathbf{b}_d`.
1916
+ :param energies: Real number or real array. Energies :math:`E`.
1917
+ :param direction: Integer, between 0 and dim-1. The primitive vector
1918
+ normal to the surface (the one along which the crystal is
1919
+ semi-infinite).
1920
+ :param side: +1 or -1. Default value 1. The crystal extends along
1921
+ :math:`+\mathbf{a}_d` (the surface faces :math:`-\mathbf{a}_d`) or
1922
+ along :math:`-\mathbf{a}_d` (the opposite surface).
1923
+ :param eta: Positive real. Default value 1e-2. Broadening: the
1924
+ width of the lines, and the decay length (about :math:`v/\eta`
1925
+ cells) over which the bulk is resolved.
1926
+ :param bulk: Boolean. Default value False. If True, the spectral
1927
+ function of a bulk unit cell instead of the surface one.
1928
+ :param max_iter: Positive integer. Default value 10000. Maximum
1929
+ number of decimation steps.
1930
+
1931
+ :returns:
1932
+ * **A** -- Real ndarray, shape (nk, len(energies)).
1933
+
1934
+ Example usage::
1935
+
1936
+ # the (001) surface of a 3D model, along a path of the surface zone
1937
+ A = ks.surface_spectral_function(k_path, np.linspace(-1, 1, 201), direction=2)
1938
+ '''
1939
+ error_handling.no_overlap(self._overlap_hop)
1940
+ ks = np.atleast_2d(np.asarray(ks, dtype='f8'))
1941
+ error_handling.ks(ks, self.dim)
1942
+ error_handling.frequencies(energies, 'energies')
1943
+ energies = np.atleast_1d(np.asarray(energies, dtype='f8'))
1944
+ error_handling.direction(direction, self.dim)
1945
+ error_handling.surface_side(side)
1946
+ error_handling.positive_real(eta, 'eta')
1947
+ error_handling.boolean(bulk, 'bulk')
1948
+ error_handling.positive_int(max_iter, 'max_iter')
1949
+ # H_m(k): the hoppings from a cell to the cell m a_d away, with the
1950
+ # phase of their in-plane part only (independent of k along b_d)
1951
+ a_dir = np.array(self.lat.prim_vec[direction], dtype='f8')
1952
+ layers = {}
1953
+ for (i, j, n, t), (_, _, R, _) in zip(self._hop_cells(), self._hop):
1954
+ layers.setdefault(n[direction], []).append((i, j, R - n[direction] * a_dir, t))
1955
+ k_cart = ks @ self.k_basis.T
1956
+ blocks = {m: self._bloch_sum(hops, k_cart)[0] for m, hops in layers.items()}
1957
+ blocks[0] = blocks.get(0, 0.) + (np.diag(self.onsite) + self._onsite_offdiag)[None]
1958
+ width = max([abs(m) for m in layers] + [1])
1959
+ norb, nk = self.norb, len(ks)
1960
+
1961
+ def layer(shift):
1962
+ # block (p, q) couples cell p of a principal layer to cell q of
1963
+ # the layer *shift* cells further into the crystal
1964
+ out = np.zeros((nk, width, norb, width, norb), 'c16')
1965
+ for p in range(width):
1966
+ for q in range(width):
1967
+ m = side * (q - p + shift)
1968
+ if m in blocks:
1969
+ out[:, p, :, q, :] = blocks[m]
1970
+ return out.reshape(nk, width * norb, width * norb)
1971
+ h0, alpha, beta = layer(0), layer(width), layer(-width)
1972
+ spec = np.empty((nk, len(energies)))
1973
+ for n in range(nk):
1974
+ gs, gb = _green_surface_bulk(energies + 1j * eta, h0[n], alpha[n], beta[n], 1e-12,
1975
+ max_iter, 'surface_spectral_function')
1976
+ g = gb if bulk else gs
1977
+ spec[n] = -np.trace(g[:, :norb, :norb], axis1=1, axis2=2).imag / PI
1978
+ return spec
1979
+
1884
1980
  def plot_dos(
1885
1981
  self,
1886
1982
  nk: int | tuple[int, int] = 30,
@@ -77,33 +77,94 @@ def surface_green(
77
77
  error_handling.positive_real(eta, 'eta')
78
78
  error_handling.positive_real(tol, 'tol')
79
79
  error_handling.positive_int(max_iter, 'max_iter')
80
+ gs, _ = _green_surface_bulk(np.array([energy + 1j * eta]), h0, v, v.conj().T, tol, max_iter,
81
+ 'surface_green')
82
+ return gs[0]
83
+
84
+
85
+ def _green_surface_bulk(
86
+ z: NDArray[np.complex128], h0: NDArray[np.complex128], alpha: NDArray[np.complex128],
87
+ beta: NDArray[np.complex128], tol: float, max_iter: int, name: str,
88
+ ) -> tuple[NDArray[np.complex128], NDArray[np.complex128]]:
89
+ r'''
90
+ Private function. The surface and bulk Green's functions of one cell
91
+ of the semi-infinite chain of *_decimation* (*h0*, *alpha*, *beta* of
92
+ shape (m, m)) at the complex energies *z*, shapes (nz, m, m).
93
+
94
+ The first decimation step inverts :math:`z - h_0`: at an energy equal to
95
+ an eigenvalue of :math:`h_0` (E = 0 for a plain chain) that is
96
+ :math:`\sim1/\eta`, and the precision is lost. Regrouping n = 1, 2 or 3
97
+ cells into one (the same chain) moves those energies: at each energy,
98
+ the best-conditioned grouping is used.
99
+ '''
80
100
  m = len(h0)
81
- # The first decimation step inverts E + i eta - h0: at an energy equal to
82
- # an eigenvalue of h0 (E = 0 for a plain chain) that is ~1/eta, and the
83
- # precision is lost. Regrouping n = 1, 2 or 3 cells into one (the same
84
- # lead) moves those energies: use the best-conditioned grouping.
85
- best = None
86
- for n in (1, 2, 3):
87
- hn = np.kron(np.eye(n), h0) + np.kron(np.eye(n, k=1), v) + np.kron(np.eye(n, k=-1), v.conj().T)
88
- vn = np.kron(np.eye(n, k=1 - n), v) if n > 1 else v.copy()
89
- smin = np.linalg.svd((energy + 1j * eta) * np.eye(n * m) - hn, compute_uv=False)[-1]
90
- if best is None or smin > best[0] * (1. + 1e-12):
91
- best = (smin, hn, vn)
92
- _, hn, vn = best
93
- z = (energy + 1j * eta) * np.eye(len(hn))
94
- eps_s, eps = hn.copy(), hn.copy()
95
- alpha, beta = vn.copy(), vn.conj().T.copy()
101
+ # Scaling cell n by r**n is a similarity transform that leaves the
102
+ # Green's function of every cell unchanged: balancing the couplings of a
103
+ # non-reciprocal chain keeps the renormalized ones from overflowing
104
+ # (r = 1 when beta = alpha^dagger).
105
+ n_a, n_b = np.linalg.norm(alpha), np.linalg.norm(beta)
106
+ if n_a > 0 and n_b > 0:
107
+ r = np.sqrt(n_b / n_a)
108
+ alpha, beta = alpha * r, beta / r
109
+ groups, best, choice = [], None, np.zeros(len(z), int)
110
+ for c, n in enumerate((1, 2, 3)):
111
+ hn = (np.kron(np.eye(n), h0) + np.kron(np.eye(n, k=1), alpha)
112
+ + np.kron(np.eye(n, k=-1), beta))
113
+ an = np.kron(np.eye(n, k=1 - n), alpha)
114
+ bn = np.kron(np.eye(n, k=n - 1), beta)
115
+ groups.append((hn, an, bn))
116
+ smin = np.linalg.svd(z[:, None, None] * np.eye(n * m)[None] - hn, compute_uv=False)[:, -1]
117
+ if best is None:
118
+ best = smin
119
+ else:
120
+ better = smin > best * (1. + 1e-12)
121
+ choice[better], best = c, np.where(better, smin, best)
122
+ gs, gb = np.empty((len(z), m, m), 'c16'), np.empty((len(z), m, m), 'c16')
123
+ for c, (hn, an, bn) in enumerate(groups):
124
+ sel = choice == c
125
+ if np.any(sel):
126
+ s_, b_ = _decimation(z[sel], hn, an, bn, tol, max_iter, name)
127
+ gs[sel], gb[sel] = s_[:, :m, :m], b_[:, :m, :m]
128
+ return gs, gb
129
+
130
+
131
+ def _decimation(
132
+ z: NDArray[np.complex128], h0: NDArray[np.complex128], alpha: NDArray[np.complex128],
133
+ beta: NDArray[np.complex128], tol: float, max_iter: int, name: str,
134
+ ) -> tuple[NDArray[np.complex128], NDArray[np.complex128]]:
135
+ r'''
136
+ Private function. The Lopez Sancho-Rubio decimation of a semi-infinite
137
+ chain of cells :math:`0, 1, 2, \dots` (:math:`H_{nn} = h_0`,
138
+ :math:`H_{n,n+1} = \alpha`, :math:`H_{n+1,n} = \beta`, with
139
+ :math:`\beta = \alpha^\dagger` unless the chain is non-reciprocal) at
140
+ many complex energies *z* (shape (nz,)) at once; *h0*, *alpha* and *beta*
141
+ have shape (m, m) or (nz, m, m). Used by *surface_green* and
142
+ *KSpace.surface_spectral_function*, which validate the arguments. It
143
+ stops when the renormalized couplings are below *tol* at every energy
144
+ (a RuntimeError naming *name* after *max_iter* iterations).
145
+
146
+ :returns:
147
+ * **gs** -- Complex ndarray, shape (nz, m, m). Surface Green's function (cell 0).
148
+ * **gb** -- Complex ndarray, shape (nz, m, m). Bulk Green's function
149
+ (one cell of the infinite chain).
150
+ '''
151
+ m = h0.shape[-1]
152
+ zi = np.asarray(z, dtype='c16')[:, None, None] * np.eye(m)[None]
153
+ eps_s = np.broadcast_to(h0, zi.shape).astype('c16')
154
+ eps = eps_s.copy()
155
+ alpha = np.broadcast_to(alpha, zi.shape).astype('c16')
156
+ beta = np.broadcast_to(beta, zi.shape).astype('c16')
96
157
  for _ in range(max_iter):
97
- g = LA.inv(z - eps)
158
+ g = np.linalg.inv(zi - eps)
98
159
  ag, bg = alpha @ g, beta @ g
99
160
  eps_s = eps_s + ag @ beta
100
161
  eps = eps + ag @ beta + bg @ alpha
101
162
  alpha, beta = ag @ alpha, bg @ beta
102
- if np.linalg.norm(alpha) + np.linalg.norm(beta) < tol:
163
+ if np.all(np.linalg.norm(alpha, axis=(1, 2)) + np.linalg.norm(beta, axis=(1, 2)) < tol):
103
164
  break
104
165
  else:
105
- error_handling.converged(False, 'surface_green')
106
- return LA.inv(z - eps_s)[:m, :m]
166
+ error_handling.converged(False, name)
167
+ return np.linalg.inv(zi - eps_s), np.linalg.inv(zi - eps)
107
168
 
108
169
 
109
170
  def lead_from_kspace(ks, direction: int = 1) -> tuple[NDArray[np.complex128], NDArray[np.complex128]]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tbkit
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Summary: A Python package to build and solve Tight-Binding models.
5
5
  Author-email: Charles Poli <cpoli374@gmail.com>
6
6
  Maintainer-email: cpoli <cpoli374@gmail.com>
@@ -97,6 +97,9 @@ topology alongside superconductivity.
97
97
  Kane-Mele) and Zeeman splitting.
98
98
  * **Edge states**: cut a ribbon (periodic in one direction, finite in the
99
99
  other) out of any periodic model, to see edge/surface physics.
100
+ * **Surface spectral functions** of semi-infinite crystals (iterative
101
+ surface Green's function): edge and surface states, e.g. the surface
102
+ Dirac cone of a 3D topological insulator, without a finite slab.
100
103
  * **Density of states**, Gaussian- or Lorentzian-broadened, from either a
101
104
  real-space spectrum or a Brillouin-zone mesh.
102
105
  * A small library of ready-made lattices (chain, square, triangular,
@@ -116,7 +119,7 @@ topology alongside superconductivity.
116
119
  |----------------------------|---------------------------------------------------------|
117
120
  | `tbkit.Lattice` | Define and manipulate site positions and sublattices. |
118
121
  | `tbkit.System` | Build the real-space Hamiltonian from a `Lattice` and solve it. |
119
- | `tbkit.KSpace` | Build and solve the Bloch Hamiltonian of a periodic `Lattice`; bands, Berry curvature/Chern numbers, anomalous and spin Hall conductivities, ribbons, DOS. |
122
+ | `tbkit.KSpace` | Build and solve the Bloch Hamiltonian of a periodic `Lattice`; bands, Berry curvature/Chern numbers, anomalous and spin Hall conductivities, ribbons, surface spectral functions, DOS. |
120
123
  | `tbkit.Plot` | Plot lattices, spectra, eigenstates, and the density of states. |
121
124
  | `tbkit.Propagation` | Time-evolve a wavepacket. |
122
125
  | `tbkit.Save` | Save figures/animations to disk. |
@@ -292,6 +295,7 @@ thumbnailed example gallery under `docs/source/api/gallery/`.
292
295
  | [`topology/plot_tknn_hofstadter.py`](examples/topology/plot_tknn_hofstadter.py) | TKNN: Chern numbers of the Hofstadter bands from magnetic supercells, and the Diophantine equation. |
293
296
  | [`topology/plot_kane_mele_z2.py`](examples/topology/plot_kane_mele_z2.py) | The Kane-Mele Z2 invariant from Wannier-centre flow. |
294
297
  | [`topology/plot_fu_kane_parity.py`](examples/topology/plot_fu_kane_parity.py) | The Fu-Kane parity criterion on the BHZ model, checked against the Wannier flow. |
298
+ | [`topology/plot_3d_topological_insulator.py`](examples/topology/plot_3d_topological_insulator.py) | 3D topological insulators: the strong index and the surface Dirac cone, from the surface spectral function of a semi-infinite crystal. |
295
299
  | [`topology/plot_quantum_geometry.py`](examples/topology/plot_quantum_geometry.py) | The quantum metric and Berry curvature of a Bloch band. |
296
300
  | [`topology/plot_tenfold_way.py`](examples/topology/plot_tenfold_way.py) | Symmetry classes of tight-binding models: the tenfold way. |
297
301
  | [`topology/plot_local_chern_marker.py`](examples/topology/plot_local_chern_marker.py) | The local Chern marker of a finite, disordered Haldane flake. |
@@ -345,6 +349,8 @@ Rendered docs (tutorial, API reference, example gallery): https://cpoli.github.i
345
349
  each one linked to the corresponding **tbkit** functionality and example
346
350
  above.
347
351
  * `docs/source/tbkit.rst` -- the API reference (auto-generated from docstrings).
352
+ * [`ROADMAP.md`](ROADMAP.md) -- what is planned next, and what tbkit
353
+ does not do yet (compared with Kwant and PythTB).
348
354
 
349
355
  Build the HTML docs with `cd docs && make html` (output in `docs/build/html`).
350
356
 
@@ -60,6 +60,7 @@ tests/test_plot.py
60
60
  tests/test_propagation.py
61
61
  tests/test_regressions.py
62
62
  tests/test_save.py
63
+ tests/test_surface_spectral.py
63
64
  tests/test_system.py
64
65
  tests/test_three_d.py
65
66
  tests/test_topology.py
@@ -0,0 +1,182 @@
1
+ """
2
+ Surface spectral functions of semi-infinite crystals (KSpace.surface_spectral_function).
3
+ """
4
+ import unittest
5
+
6
+ import numpy as np
7
+
8
+ import tbkit.lattices as lattices
9
+ from tbkit.lattice import Lattice
10
+ from tbkit.kspace import KSpace, PAULI, ribbon
11
+
12
+ CUBIC = [(1., 0., 0.), (0., 1., 0.), (0., 0., 1.)]
13
+ SIGMA = [PAULI['x'], PAULI['y'], PAULI['z']]
14
+ # Haldane-like model with a third-neighbour-cell bond along a1, so the
15
+ # principal layer normal to a2 is one cell and normal to a1 two cells
16
+ T2 = 0.2j
17
+ HONEY_HOPS = [{'i': 0, 'j': 1, 'R': (0, 0), 't': 1.}, {'i': 0, 'j': 1, 'R': (-1, 0), 't': 1.},
18
+ {'i': 0, 'j': 1, 'R': (0, -1), 't': 1.},
19
+ {'i': 0, 'j': 0, 'R': (1, 0), 't': T2}, {'i': 0, 'j': 0, 'R': (0, -1), 't': T2},
20
+ {'i': 0, 'j': 0, 'R': (-1, 1), 't': T2},
21
+ {'i': 1, 'j': 1, 'R': (1, 0), 't': -T2}, {'i': 1, 'j': 1, 'R': (0, -1), 't': -T2},
22
+ {'i': 1, 'j': 1, 'R': (-1, 1), 't': -T2},
23
+ {'i': 0, 'j': 0, 'R': (2, 0), 't': 0.1}]
24
+ HONEY_ONSITE = {'a': 0.3, 'b': -0.3}
25
+
26
+
27
+ def chain(t=1.):
28
+ ks = KSpace(lattices.chain())
29
+ ks.set_hopping([{'i': 0, 'j': 0, 'R': (1,), 't': t}])
30
+ return ks
31
+
32
+
33
+ def ti_hops():
34
+ '''Hoppings of the cubic 3D topological insulator m(k) tau_z + sum_i sin k_i tau_x sigma_i.'''
35
+ hops = []
36
+ for d in range(3):
37
+ R = tuple(int(x) for x in np.eye(3, dtype=int)[d])
38
+ hops += [{'i': 0, 'j': 0, 'R': R, 't': PAULI['0']}, {'i': 1, 'j': 1, 'R': R, 't': -PAULI['0']},
39
+ {'i': 0, 'j': 1, 'R': R, 't': -0.5j * SIGMA[d]},
40
+ {'i': 1, 'j': 0, 'R': R, 't': -0.5j * SIGMA[d]}]
41
+ return hops
42
+
43
+
44
+ def ti_onsite(M):
45
+ return {'s': (M - 6) * PAULI['0'], 'p': -(M - 6) * PAULI['0']}
46
+
47
+
48
+ TI_LAT = Lattice(unit_cell=[{'tag': 's', 'r0': (0., 0., 0.)}, {'tag': 'p', 'r0': (0., 0., 0.)}],
49
+ prim_vec=CUBIC)
50
+
51
+
52
+ def topological_insulator(M):
53
+ ks = KSpace(TI_LAT, spin=True)
54
+ ks.set_hopping(ti_hops())
55
+ ks.set_onsite(ti_onsite(M))
56
+ return ks
57
+
58
+
59
+ def slab_spectral(H, rows, energies, eta):
60
+ '''-Im Tr G / pi over the orbitals *rows* of a finite slab Hamiltonian.'''
61
+ n = len(H)
62
+ return np.array([-np.trace(np.linalg.inv((e + 1j * eta) * np.eye(n) - H)[np.ix_(rows, rows)]).imag
63
+ for e in energies]) / np.pi
64
+
65
+
66
+ class TestSurfaceSpectralFunction(unittest.TestCase):
67
+
68
+ def test_chain_end_and_bulk(self):
69
+ # semi-infinite chain: A_s = sqrt(4 - E^2)/(2 pi), A_b = 1/(pi sqrt(4 - E^2)),
70
+ # including E = 0, where the cell Hamiltonian is singular
71
+ ch = chain()
72
+ E = np.linspace(-1.9, 1.9, 9)
73
+ for eta in (1e-6, 1e-9):
74
+ A = ch.surface_spectral_function([[0.]], E, 0, eta=eta)[0]
75
+ self.assertTrue(np.allclose(A, np.sqrt(4 - E ** 2) / (2 * np.pi), atol=1e-6))
76
+ B = ch.surface_spectral_function([[0.]], E, 0, eta=eta, bulk=True)[0]
77
+ self.assertTrue(np.allclose(B, 1 / (np.pi * np.sqrt(4 - E ** 2)), atol=1e-5))
78
+ # both ends alike; nothing outside the band; one number for a scalar energy
79
+ self.assertTrue(np.allclose(ch.surface_spectral_function([[0.]], E, 0, side=-1, eta=1e-6)[0],
80
+ np.sqrt(4 - E ** 2) / (2 * np.pi), atol=1e-6))
81
+ self.assertLess(ch.surface_spectral_function([[0.]], 2.5, 0, eta=1e-6)[0, 0], 1e-5)
82
+ self.assertEqual(ch.surface_spectral_function([0.], 0.5, 0).shape, (1, 1))
83
+
84
+ def test_spectral_sum_rule(self):
85
+ # int A_s dE = number of orbitals of the surface cell
86
+ ch = chain()
87
+ E = np.linspace(-60., 60., 24001)
88
+ A = ch.surface_spectral_function([[0.]], E, 0, eta=0.05)[0]
89
+ self.assertAlmostEqual(np.sum(A) * (E[1] - E[0]), 1., delta=2e-3)
90
+
91
+ def test_matches_thick_ribbon(self):
92
+ # the outer cell of a wide ribbon (eta resolves ~ 1/eta cells) is the
93
+ # semi-infinite surface, on both edges, normal to either primitive vector
94
+ lat = lattices.honeycomb()
95
+ ks = KSpace(lat)
96
+ ks.set_hopping(HONEY_HOPS)
97
+ ks.set_onsite(HONEY_ONSITE)
98
+ E = np.linspace(-3., 3., 13)
99
+ eta, width = 0.05, 200
100
+ k = np.array([0.37, 0.61]) @ ks.rec_vec_k
101
+ for direction in (0, 1):
102
+ rib = ribbon(lat, HONEY_HOPS, width, direction=direction, onsite=HONEY_ONSITE)
103
+ a_par = np.array(lat.prim_vec[1 - direction])
104
+ H = rib.get_ham([k @ a_par / np.linalg.norm(a_par)])
105
+ for side, rows in ((1, [0, 1]), (-1, [len(H) - 2, len(H) - 1])):
106
+ A = ks.surface_spectral_function([k], E, direction, side, eta=eta)[0]
107
+ self.assertTrue(np.allclose(A, slab_spectral(H, rows, E, eta), atol=1e-10))
108
+ # only k along the surface matters
109
+ A2 = ks.surface_spectral_function([k + 0.3 * ks.rec_vec_k[direction]], E, direction,
110
+ side, eta=eta)[0]
111
+ self.assertTrue(np.allclose(A, A2, atol=1e-12))
112
+
113
+ def test_matches_thick_slab_3d(self):
114
+ # a spinful 3D model against slabs cut normal to a2 (slab k = (k_x, k_z))
115
+ # and to a3 (slab k = (k_x, k_y)): the four orbitals of the outer cell
116
+ ti = topological_insulator(2.)
117
+ E, eta = np.linspace(-3., 3., 13), 0.1
118
+ for direction, k_slab, k in ((1, [0.4, -0.3], [0.4, 1.3, -0.3]), (2, [0.2, -0.1], [0.2, -0.1, 0.])):
119
+ slab = ribbon(TI_LAT, ti_hops(), 120, direction=direction, onsite=ti_onsite(2.), spin=True)
120
+ A = ti.surface_spectral_function([k], E, direction, eta=eta)[0]
121
+ ref = slab_spectral(slab.get_ham(k_slab), [0, 1, 2, 3], E, eta)
122
+ self.assertTrue(np.allclose(A, ref, atol=1e-10))
123
+
124
+ def test_topological_insulator_surface_dirac_cone(self):
125
+ kx = np.linspace(-0.6, 0.6, 121)
126
+ ks = np.column_stack([kx, 0 * kx, 0 * kx])
127
+ E = np.array([-0.4, 0., 0.2, 0.4])
128
+ ti = topological_insulator(2.)
129
+ A = ti.surface_spectral_function(ks, E, 2, eta=0.02)
130
+ bulk = ti.surface_spectral_function(ks, E, 2, eta=0.02, bulk=True)
131
+ self.assertLess(bulk.max(), 0.05) # all four energies in the bulk gap
132
+ self.assertGreater(A[60, 1], 10.) # the Dirac point at the zone centre
133
+ for n, e in ((2, 0.2), (3, 0.4), (0, -0.4)):
134
+ # exactly two peaks, at k = +-|E| (velocity 1)
135
+ col = A[:, n]
136
+ peaks = [i for i in range(1, len(kx) - 1)
137
+ if col[i] > col[i - 1] and col[i] > col[i + 1] and col[i] > 1.]
138
+ self.assertEqual(len(peaks), 2)
139
+ self.assertTrue(np.allclose(np.abs(kx[peaks]), abs(e), atol=0.02))
140
+ # no surface states in the gap of the trivial insulator
141
+ trivial = topological_insulator(-2.)
142
+ self.assertLess(trivial.surface_spectral_function(ks, E, 2, eta=0.02).max(), 0.05)
143
+
144
+ def test_non_reciprocal_chain(self):
145
+ # Hatano-Nelson: the surface Green's function obeys g = 1/(z - tR tL g),
146
+ # that of a Hermitian chain with t = sqrt(tR tL)
147
+ hn = KSpace(lattices.chain())
148
+ hn.set_hopping([{'i': 0, 'j': 0, 'R': (1,), 't': 2.}, {'i': 0, 'j': 0, 'R': (-1,), 't': 0.5}],
149
+ hermitian=False)
150
+ E = np.linspace(-1.5, 1.5, 7)
151
+ self.assertTrue(np.allclose(hn.surface_spectral_function([[0.]], E, 0, eta=1e-3),
152
+ chain().surface_spectral_function([[0.]], E, 0, eta=1e-3)))
153
+
154
+ def test_decoupled_layers(self):
155
+ # no hopping along the surface normal: each layer is an isolated chain
156
+ lat = lattices.square()
157
+ ks = KSpace(lat)
158
+ ks.set_hopping([{'i': 0, 'j': 0, 'R': (1, 0), 't': 1.}])
159
+ kx, E = 0.7, np.linspace(-3., 3., 7)
160
+ A = ks.surface_spectral_function([[kx, 0.]], E, 1, eta=0.1)[0]
161
+ e0 = 2 * np.cos(kx)
162
+ self.assertTrue(np.allclose(A, 0.1 / np.pi / ((E - e0) ** 2 + 0.01)))
163
+
164
+ def test_errors(self):
165
+ ch = chain()
166
+ f = ch.surface_spectral_function
167
+ self.assertRaises(ValueError, f, [[0., 0.]], 0., 0)
168
+ self.assertRaises(TypeError, f, [[0.]], [0.1j], 0)
169
+ self.assertRaises(ValueError, f, [[0.]], [], 0)
170
+ self.assertRaises(ValueError, f, [[0.]], 0., 1)
171
+ self.assertRaises(TypeError, f, [[0.]], 0., 0.)
172
+ self.assertRaises(ValueError, f, [[0.]], 0., 0, 0)
173
+ self.assertRaises(ValueError, f, [[0.]], 0., 0, True)
174
+ self.assertRaises(ValueError, f, [[0.]], 0., 0, 1, -1.)
175
+ self.assertRaises(TypeError, f, [[0.]], 0., 0, 1, 0.1, 1)
176
+ self.assertRaises(RuntimeError, f, [[0.]], 0.5, 0, 1, 1e-9, False, 2)
177
+ ch.set_overlap([{'i': 0, 'j': 0, 'R': (1,), 't': 0.1}])
178
+ self.assertRaises(ValueError, f, [[0.]], 0., 0)
179
+
180
+
181
+ if __name__ == '__main__':
182
+ unittest.main()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes