tbkit 0.2.0__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.
- tbkit-0.2.0/LICENSE +29 -0
- tbkit-0.2.0/PKG-INFO +260 -0
- tbkit-0.2.0/README.md +234 -0
- tbkit-0.2.0/pyproject.toml +36 -0
- tbkit-0.2.0/setup.cfg +4 -0
- tbkit-0.2.0/tbkit/__init__.py +27 -0
- tbkit-0.2.0/tbkit/dos.py +65 -0
- tbkit-0.2.0/tbkit/error_handling.py +988 -0
- tbkit-0.2.0/tbkit/graphene.py +173 -0
- tbkit-0.2.0/tbkit/kspace.py +563 -0
- tbkit-0.2.0/tbkit/lattice.py +424 -0
- tbkit-0.2.0/tbkit/lattices.py +126 -0
- tbkit-0.2.0/tbkit/plot.py +690 -0
- tbkit-0.2.0/tbkit/propagation.py +338 -0
- tbkit-0.2.0/tbkit/save.py +98 -0
- tbkit-0.2.0/tbkit/system.py +744 -0
- tbkit-0.2.0/tbkit.egg-info/PKG-INFO +260 -0
- tbkit-0.2.0/tbkit.egg-info/SOURCES.txt +29 -0
- tbkit-0.2.0/tbkit.egg-info/dependency_links.txt +1 -0
- tbkit-0.2.0/tbkit.egg-info/requires.txt +12 -0
- tbkit-0.2.0/tbkit.egg-info/top_level.txt +1 -0
- tbkit-0.2.0/tests/test_dos.py +45 -0
- tbkit-0.2.0/tests/test_error_handling.py +403 -0
- tbkit-0.2.0/tests/test_graphene.py +69 -0
- tbkit-0.2.0/tests/test_kspace.py +412 -0
- tbkit-0.2.0/tests/test_lattice.py +292 -0
- tbkit-0.2.0/tests/test_lattices.py +85 -0
- tbkit-0.2.0/tests/test_plot.py +183 -0
- tbkit-0.2.0/tests/test_propagation.py +122 -0
- tbkit-0.2.0/tests/test_save.py +76 -0
- tbkit-0.2.0/tests/test_system.py +565 -0
tbkit-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014, Charles Poli
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
tbkit-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tbkit
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A Python package to build and solve Tight-Binding models, for research and education.
|
|
5
|
+
Author-email: Charles Poli <cpoli374@gmail.com>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/cpoli/tbkit
|
|
8
|
+
Classifier: Intended Audience :: Education
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: numpy>=1.24
|
|
16
|
+
Requires-Dist: scipy>=1.10
|
|
17
|
+
Requires-Dist: matplotlib>=3.7
|
|
18
|
+
Provides-Extra: test
|
|
19
|
+
Requires-Dist: pytest>=7; extra == "test"
|
|
20
|
+
Requires-Dist: pytest-cov>=4; extra == "test"
|
|
21
|
+
Provides-Extra: docs
|
|
22
|
+
Requires-Dist: sphinx>=7; extra == "docs"
|
|
23
|
+
Requires-Dist: pydata-sphinx-theme>=0.15; extra == "docs"
|
|
24
|
+
Requires-Dist: sphinx-gallery>=0.15; extra == "docs"
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# tbkit — a Tight-Binding package for research and education
|
|
28
|
+
|
|
29
|
+
[](https://github.com/cpoli/tbkit/actions/workflows/tests.yml)
|
|
30
|
+
[](https://cpoli.github.io/tbkit/)
|
|
31
|
+
[](pyproject.toml)
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+
|
|
36
|
+
**tbkit** is a Python package to build and solve Tight-Binding models, written
|
|
37
|
+
in vectorized NumPy/SciPy. It aims to make the mechanics of Tight-Binding
|
|
38
|
+
models — lattices, hoppings, Hamiltonians, spectra, band structures —
|
|
39
|
+
explicit and easy to inspect, so it works as well for teaching as for
|
|
40
|
+
research prototyping.
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
* **Real space**: build arbitrarily complex finite lattices (flakes,
|
|
45
|
+
ribbons, disordered structures, defects) site by site or via boolean
|
|
46
|
+
selections (ellipses, half-planes, unions/differences of lattices), then
|
|
47
|
+
diagonalize the resulting Hamiltonian directly.
|
|
48
|
+
* **Reciprocal space**: build the Bloch Hamiltonian `H(k)` of the infinite
|
|
49
|
+
periodic lattice from a small set of intra-unit-cell hoppings, and compute
|
|
50
|
+
band structures along a k-path through high-symmetry points.
|
|
51
|
+
* **Topology**: Berry curvature and Chern numbers of a group of bands, by
|
|
52
|
+
the gauge-invariant Fukui-Hatsugai-Suzuki lattice method.
|
|
53
|
+
* **Spin**: optional spin-1/2 degree of freedom on every site, with 2x2
|
|
54
|
+
(Pauli-matrix) hoppings/onsite terms, for spin-orbit coupling (Rashba,
|
|
55
|
+
Kane-Mele) and Zeeman splitting.
|
|
56
|
+
* **Edge states**: cut a ribbon (periodic in one direction, finite in the
|
|
57
|
+
other) out of any periodic model, to see edge/surface physics.
|
|
58
|
+
* **Density of states**, Gaussian- or Lorentzian-broadened, from either a
|
|
59
|
+
real-space spectrum or a Brillouin-zone mesh.
|
|
60
|
+
* A small library of ready-made lattices (chain, square, triangular,
|
|
61
|
+
honeycomb, kagome, Lieb).
|
|
62
|
+
* Complex-valued onsite energies and hoppings; **Hermitian and non-Hermitian**
|
|
63
|
+
Tight-Binding Hamiltonians.
|
|
64
|
+
* Multiple sublattices, with hoppings addressed by neighbor order (1st,
|
|
65
|
+
2nd, 3rd-nearest neighbor, ...), by angle, or by sublattice-pair tag.
|
|
66
|
+
* Built-in patterns for onsite disorder, hopping disorder, dimerization,
|
|
67
|
+
strain, and an orbital magnetic field (Peierls substitution).
|
|
68
|
+
* Time propagation of a wavepacket under the Tight-Binding Hamiltonian
|
|
69
|
+
(Crank-Nicolson).
|
|
70
|
+
|
|
71
|
+
**tbkit** is organized as a small set of composable classes and modules:
|
|
72
|
+
|
|
73
|
+
| Class / module | Purpose |
|
|
74
|
+
|----------------------------|---------------------------------------------------------|
|
|
75
|
+
| `tbkit.Lattice` | Define and manipulate site positions and sublattices. |
|
|
76
|
+
| `tbkit.System` | Build the real-space Hamiltonian from a `Lattice` and solve it. |
|
|
77
|
+
| `tbkit.KSpace` | Build and solve the Bloch Hamiltonian of a periodic `Lattice`; bands, Berry curvature/Chern numbers, ribbons, DOS. |
|
|
78
|
+
| `tbkit.Plot` | Plot lattices, spectra, eigenstates, and the density of states. |
|
|
79
|
+
| `tbkit.Propagation` | Time-evolve a wavepacket. |
|
|
80
|
+
| `tbkit.Save` | Save figures/animations to disk. |
|
|
81
|
+
| `tbkit.lattices` | Ready-made common lattices. |
|
|
82
|
+
| `tbkit.dos` | Broadened density of states from a set of eigenenergies. |
|
|
83
|
+
|
|
84
|
+
## Install
|
|
85
|
+
|
|
86
|
+
Requires Python >= 3.10.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
git clone https://github.com/cpoli/tbkit
|
|
90
|
+
cd tbkit
|
|
91
|
+
pip install -e .
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
or, to also install the tools needed to run the test suite:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pip install -e ".[test]"
|
|
98
|
+
pytest tests/
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Quick start
|
|
102
|
+
|
|
103
|
+
Real-space flake, nearest-neighbor square lattice:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from tbkit.lattice import Lattice
|
|
107
|
+
from tbkit.system import System
|
|
108
|
+
|
|
109
|
+
lat = Lattice(unit_cell=[{'tag': 'a', 'r0': (0., 0.)}],
|
|
110
|
+
prim_vec=[(1., 0.), (0., 1.)])
|
|
111
|
+
lat.get_lattice(n1=10, n2=10)
|
|
112
|
+
|
|
113
|
+
sys = System(lat)
|
|
114
|
+
sys.set_hopping([{'n': 1, 't': 1.}])
|
|
115
|
+
sys.get_ham()
|
|
116
|
+
sys.get_eig()
|
|
117
|
+
print(sys.en)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Graphene band structure (reciprocal space):
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
import numpy as np
|
|
124
|
+
from tbkit.lattice import Lattice
|
|
125
|
+
from tbkit.kspace import KSpace, reciprocal_vectors
|
|
126
|
+
|
|
127
|
+
DX, DY = 0.5 * 3 ** 0.5, 0.5
|
|
128
|
+
unit_cell = [{'tag': 'a', 'r0': (0., 0.)}, {'tag': 'b', 'r0': (DX, DY)}]
|
|
129
|
+
prim_vec = [(2 * DX, 0.), (DX, 1.5)]
|
|
130
|
+
lat = Lattice(unit_cell=unit_cell, prim_vec=prim_vec)
|
|
131
|
+
|
|
132
|
+
gra = KSpace(lat)
|
|
133
|
+
gra.set_hopping([{'i': 0, 'j': 1, 'R': (0, 0), 't': 1.},
|
|
134
|
+
{'i': 0, 'j': 1, 'R': (-1, 0), 't': 1.},
|
|
135
|
+
{'i': 0, 'j': 1, 'R': (0, -1), 't': 1.}])
|
|
136
|
+
|
|
137
|
+
b1, b2 = (np.array(v) for v in reciprocal_vectors(prim_vec))
|
|
138
|
+
Gamma, K, M = np.zeros(2), (b1 - b2) / 3, b1 / 2
|
|
139
|
+
gra.k_path([Gamma, K, M, Gamma], nk=60)
|
|
140
|
+
fig = gra.plot_bands(node_labels=[r'$\Gamma$', 'K', 'M', r'$\Gamma$'])
|
|
141
|
+
fig.savefig('graphene_bands.png')
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
A magnetic field is added with `System.set_magnetic_field` (uniform field) or
|
|
145
|
+
`System.set_peierls_phase` (arbitrary vector potential), applied to the
|
|
146
|
+
hoppings *after* `set_hopping`/`set_hopping_manual`:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
sys.set_hopping_manual(hop_dict)
|
|
150
|
+
sys.set_magnetic_field(alpha=0.01) # flux quanta per unit area
|
|
151
|
+
sys.get_ham()
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
See [`examples/magnetic_field/plot_magnetic_field.py`](examples/magnetic_field/plot_magnetic_field.py) for a full
|
|
155
|
+
worked example (an Aharonov-Bohm ring, reproducing the textbook result that
|
|
156
|
+
the spectrum is periodic in the enclosed flux with period one flux quantum).
|
|
157
|
+
|
|
158
|
+
A Chern number is the Berry curvature of a group of bands, integrated over
|
|
159
|
+
the Brillouin zone:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
chern = gra.chern_number(bands=[0], nk=40) # ~0 for plain graphene
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
See [`examples/topology/plot_haldane_topology.py`](examples/topology/plot_haldane_topology.py) for the
|
|
166
|
+
Haldane model (the first Chern insulator), reproducing its topological
|
|
167
|
+
phase transition (Chern number 1 -> 0) and Berry-curvature map.
|
|
168
|
+
|
|
169
|
+
A spin-1/2 degree of freedom is added with `KSpace(lat, spin=True)`; onsite
|
|
170
|
+
values and hoppings then also accept 2x2 (Pauli) matrices:
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from tbkit.kspace import KSpace, PAULI
|
|
174
|
+
|
|
175
|
+
kmele = KSpace(lat, spin=True)
|
|
176
|
+
kmele.set_hopping([{'i': 0, 'j': 0, 'R': (1, 0), 't': 1j*lam*PAULI['z']}]) # intrinsic SOC
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
A ribbon -- periodic in one direction, finite in the other, the standard
|
|
180
|
+
way to see edge states -- is cut out of a periodic model with
|
|
181
|
+
`tbkit.kspace.ribbon`:
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from tbkit.kspace import ribbon
|
|
185
|
+
|
|
186
|
+
rib = ribbon(lat, list_hop, width=30, direction=1) # 30 unit cells wide
|
|
187
|
+
fig = rib.plot_bands()
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
See [`examples/topology/plot_edge_states.py`](examples/topology/plot_edge_states.py) for the zigzag
|
|
191
|
+
graphene ribbon's zero-energy edge band, and the Kane-Mele ribbon's helical
|
|
192
|
+
edge states crossing a spin-orbit gap.
|
|
193
|
+
|
|
194
|
+
## Examples
|
|
195
|
+
|
|
196
|
+
`examples/` is organized as a [Sphinx-Gallery](https://sphinx-gallery.github.io/)
|
|
197
|
+
source tree, one topic per subfolder, each with a `README.rst` blurb. Every
|
|
198
|
+
`plot_*.py` script is self-contained and runnable directly
|
|
199
|
+
(`python examples/<section>/<script>.py`), and checks its own key numeric
|
|
200
|
+
claims with `assert` before plotting -- nothing is asserted in the docs
|
|
201
|
+
that isn't also verified in code. Building the docs (`pip install -e ".[docs]"`
|
|
202
|
+
then `cd docs && make html`) renders these same scripts into an executed,
|
|
203
|
+
thumbnailed example gallery under `docs/source/api/gallery/`.
|
|
204
|
+
|
|
205
|
+
| Script | What it shows |
|
|
206
|
+
|---------------------------------------------------------------------------|----------------|
|
|
207
|
+
| [`tight_binding/plot_graphene_bands.py`](examples/tight_binding/plot_graphene_bands.py) | Real-space flake + reciprocal-space band structure; graphene's Dirac point and Wallace's 1947 linear dispersion. |
|
|
208
|
+
| [`tight_binding/plot_visualizing_a_model.py`](examples/tight_binding/plot_visualizing_a_model.py) | `tbkit.plot.Plot`: lattice, spectrum with sublattice polarization, density of states, eigenstate intensity. |
|
|
209
|
+
| [`magnetic_field/plot_magnetic_field.py`](examples/magnetic_field/plot_magnetic_field.py) | Peierls substitution; an Aharonov-Bohm ring's flux-periodic spectrum. |
|
|
210
|
+
| [`magnetic_field/plot_hofstadter_butterfly.py`](examples/magnetic_field/plot_hofstadter_butterfly.py) | The fractal spectrum of a lattice threaded by a continuously swept flux. |
|
|
211
|
+
| [`magnetic_field/plot_landau_levels.py`](examples/magnetic_field/plot_landau_levels.py) | Landau levels: a square lattice's non-relativistic ladder vs. graphene's relativistic sqrt(n) ladder and zero mode. |
|
|
212
|
+
| [`topology/plot_ssh_model.py`](examples/topology/plot_ssh_model.py) | The SSH model: bulk gap closing and topologically protected edge states. |
|
|
213
|
+
| [`disorder/plot_anderson_localization.py`](examples/disorder/plot_anderson_localization.py) | Anderson localization: IPR vs. disorder strength, extended vs. localized states. |
|
|
214
|
+
| [`topology/plot_haldane_topology.py`](examples/topology/plot_haldane_topology.py) | The Haldane model: Berry curvature, Chern number, topological phase transition. |
|
|
215
|
+
| [`flat_bands/plot_flat_bands.py`](examples/flat_bands/plot_flat_bands.py) | Exactly flat bands on the kagome and Lieb lattices. |
|
|
216
|
+
| [`topology/plot_kagome_chern_band.py`](examples/topology/plot_kagome_chern_band.py) | Gapping the kagome flat band into a Chern insulator (C=-1) with complex nearest-neighbor hopping. |
|
|
217
|
+
| [`topology/plot_edge_states.py`](examples/topology/plot_edge_states.py) | Zigzag graphene ribbon edge band; Kane-Mele helical edge states. |
|
|
218
|
+
| [`dynamics/plot_bloch_oscillations.py`](examples/dynamics/plot_bloch_oscillations.py) | Wannier-Stark ladder, its localization, and Bloch oscillations under a uniform tilt. |
|
|
219
|
+
| [`topology/plot_thouless_pump.py`](examples/topology/plot_thouless_pump.py) | The Rice-Mele model as a Thouless quantum pump: quantized Chern number and polarization winding. |
|
|
220
|
+
|
|
221
|
+
The `examples/` directory also has five older Jupyter notebooks (graphene
|
|
222
|
+
flakes, kagome/Lieb/dumbbell lattices, disorder, strain, time propagation)
|
|
223
|
+
predating the 0.2 API refresh below.
|
|
224
|
+
|
|
225
|
+
## Documentation
|
|
226
|
+
|
|
227
|
+
Rendered docs (tutorial, API reference, example gallery): https://cpoli.github.io/tbkit/
|
|
228
|
+
|
|
229
|
+
* [`docs/source/tutorial.rst`](docs/source/tutorial.rst) -- a narrative walkthrough of the
|
|
230
|
+
package, from building a lattice through topology, spin-orbit coupling,
|
|
231
|
+
and edge states.
|
|
232
|
+
* [`docs/source/history.rst`](docs/source/history.rst) -- a chronology of the breakthroughs
|
|
233
|
+
behind Tight-Binding theory (Bloch's theorem through the Kane-Mele model),
|
|
234
|
+
each one linked to the corresponding **tbkit** functionality and example
|
|
235
|
+
above.
|
|
236
|
+
* `docs/source/tbkit.rst` -- the API reference (auto-generated from docstrings).
|
|
237
|
+
|
|
238
|
+
Build the HTML docs with `cd docs && make html` (output in `docs/build/html`).
|
|
239
|
+
|
|
240
|
+
## A note on the API
|
|
241
|
+
|
|
242
|
+
Version 0.2 modernized the package to run on current Python/NumPy/SciPy and
|
|
243
|
+
cleaned up the API:
|
|
244
|
+
|
|
245
|
+
* Sublattice tags are plain one-character **strings** (`'a'`) rather than
|
|
246
|
+
byte strings (`b'a'`).
|
|
247
|
+
* Classes are named in `PascalCase` (`Lattice`, `System`, ...) rather than
|
|
248
|
+
lowercase names identical to their module (`lattice.lattice`,
|
|
249
|
+
`system.system`, ...), which used to make `import tbkit.lattice as lattice`
|
|
250
|
+
silently bind the wrong object.
|
|
251
|
+
|
|
252
|
+
For continuity, the pre-0.2 lowercase class names (`lattice`, `system`,
|
|
253
|
+
`plot`, `propagation`, `save`) remain available as aliases of the new
|
|
254
|
+
classes, so `from tbkit.lattice import lattice` still works. Example
|
|
255
|
+
notebooks predating 0.2 still use byte-string tags (`b'a'`) and will need
|
|
256
|
+
that one mechanical change to run on the current version.
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
BSD 3-Clause, see [LICENSE](LICENSE).
|
tbkit-0.2.0/README.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# tbkit — a Tight-Binding package for research and education
|
|
2
|
+
|
|
3
|
+
[](https://github.com/cpoli/tbkit/actions/workflows/tests.yml)
|
|
4
|
+
[](https://cpoli.github.io/tbkit/)
|
|
5
|
+
[](pyproject.toml)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
**tbkit** is a Python package to build and solve Tight-Binding models, written
|
|
11
|
+
in vectorized NumPy/SciPy. It aims to make the mechanics of Tight-Binding
|
|
12
|
+
models — lattices, hoppings, Hamiltonians, spectra, band structures —
|
|
13
|
+
explicit and easy to inspect, so it works as well for teaching as for
|
|
14
|
+
research prototyping.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
* **Real space**: build arbitrarily complex finite lattices (flakes,
|
|
19
|
+
ribbons, disordered structures, defects) site by site or via boolean
|
|
20
|
+
selections (ellipses, half-planes, unions/differences of lattices), then
|
|
21
|
+
diagonalize the resulting Hamiltonian directly.
|
|
22
|
+
* **Reciprocal space**: build the Bloch Hamiltonian `H(k)` of the infinite
|
|
23
|
+
periodic lattice from a small set of intra-unit-cell hoppings, and compute
|
|
24
|
+
band structures along a k-path through high-symmetry points.
|
|
25
|
+
* **Topology**: Berry curvature and Chern numbers of a group of bands, by
|
|
26
|
+
the gauge-invariant Fukui-Hatsugai-Suzuki lattice method.
|
|
27
|
+
* **Spin**: optional spin-1/2 degree of freedom on every site, with 2x2
|
|
28
|
+
(Pauli-matrix) hoppings/onsite terms, for spin-orbit coupling (Rashba,
|
|
29
|
+
Kane-Mele) and Zeeman splitting.
|
|
30
|
+
* **Edge states**: cut a ribbon (periodic in one direction, finite in the
|
|
31
|
+
other) out of any periodic model, to see edge/surface physics.
|
|
32
|
+
* **Density of states**, Gaussian- or Lorentzian-broadened, from either a
|
|
33
|
+
real-space spectrum or a Brillouin-zone mesh.
|
|
34
|
+
* A small library of ready-made lattices (chain, square, triangular,
|
|
35
|
+
honeycomb, kagome, Lieb).
|
|
36
|
+
* Complex-valued onsite energies and hoppings; **Hermitian and non-Hermitian**
|
|
37
|
+
Tight-Binding Hamiltonians.
|
|
38
|
+
* Multiple sublattices, with hoppings addressed by neighbor order (1st,
|
|
39
|
+
2nd, 3rd-nearest neighbor, ...), by angle, or by sublattice-pair tag.
|
|
40
|
+
* Built-in patterns for onsite disorder, hopping disorder, dimerization,
|
|
41
|
+
strain, and an orbital magnetic field (Peierls substitution).
|
|
42
|
+
* Time propagation of a wavepacket under the Tight-Binding Hamiltonian
|
|
43
|
+
(Crank-Nicolson).
|
|
44
|
+
|
|
45
|
+
**tbkit** is organized as a small set of composable classes and modules:
|
|
46
|
+
|
|
47
|
+
| Class / module | Purpose |
|
|
48
|
+
|----------------------------|---------------------------------------------------------|
|
|
49
|
+
| `tbkit.Lattice` | Define and manipulate site positions and sublattices. |
|
|
50
|
+
| `tbkit.System` | Build the real-space Hamiltonian from a `Lattice` and solve it. |
|
|
51
|
+
| `tbkit.KSpace` | Build and solve the Bloch Hamiltonian of a periodic `Lattice`; bands, Berry curvature/Chern numbers, ribbons, DOS. |
|
|
52
|
+
| `tbkit.Plot` | Plot lattices, spectra, eigenstates, and the density of states. |
|
|
53
|
+
| `tbkit.Propagation` | Time-evolve a wavepacket. |
|
|
54
|
+
| `tbkit.Save` | Save figures/animations to disk. |
|
|
55
|
+
| `tbkit.lattices` | Ready-made common lattices. |
|
|
56
|
+
| `tbkit.dos` | Broadened density of states from a set of eigenenergies. |
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
Requires Python >= 3.10.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
git clone https://github.com/cpoli/tbkit
|
|
64
|
+
cd tbkit
|
|
65
|
+
pip install -e .
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
or, to also install the tools needed to run the test suite:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install -e ".[test]"
|
|
72
|
+
pytest tests/
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Quick start
|
|
76
|
+
|
|
77
|
+
Real-space flake, nearest-neighbor square lattice:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from tbkit.lattice import Lattice
|
|
81
|
+
from tbkit.system import System
|
|
82
|
+
|
|
83
|
+
lat = Lattice(unit_cell=[{'tag': 'a', 'r0': (0., 0.)}],
|
|
84
|
+
prim_vec=[(1., 0.), (0., 1.)])
|
|
85
|
+
lat.get_lattice(n1=10, n2=10)
|
|
86
|
+
|
|
87
|
+
sys = System(lat)
|
|
88
|
+
sys.set_hopping([{'n': 1, 't': 1.}])
|
|
89
|
+
sys.get_ham()
|
|
90
|
+
sys.get_eig()
|
|
91
|
+
print(sys.en)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Graphene band structure (reciprocal space):
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
import numpy as np
|
|
98
|
+
from tbkit.lattice import Lattice
|
|
99
|
+
from tbkit.kspace import KSpace, reciprocal_vectors
|
|
100
|
+
|
|
101
|
+
DX, DY = 0.5 * 3 ** 0.5, 0.5
|
|
102
|
+
unit_cell = [{'tag': 'a', 'r0': (0., 0.)}, {'tag': 'b', 'r0': (DX, DY)}]
|
|
103
|
+
prim_vec = [(2 * DX, 0.), (DX, 1.5)]
|
|
104
|
+
lat = Lattice(unit_cell=unit_cell, prim_vec=prim_vec)
|
|
105
|
+
|
|
106
|
+
gra = KSpace(lat)
|
|
107
|
+
gra.set_hopping([{'i': 0, 'j': 1, 'R': (0, 0), 't': 1.},
|
|
108
|
+
{'i': 0, 'j': 1, 'R': (-1, 0), 't': 1.},
|
|
109
|
+
{'i': 0, 'j': 1, 'R': (0, -1), 't': 1.}])
|
|
110
|
+
|
|
111
|
+
b1, b2 = (np.array(v) for v in reciprocal_vectors(prim_vec))
|
|
112
|
+
Gamma, K, M = np.zeros(2), (b1 - b2) / 3, b1 / 2
|
|
113
|
+
gra.k_path([Gamma, K, M, Gamma], nk=60)
|
|
114
|
+
fig = gra.plot_bands(node_labels=[r'$\Gamma$', 'K', 'M', r'$\Gamma$'])
|
|
115
|
+
fig.savefig('graphene_bands.png')
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
A magnetic field is added with `System.set_magnetic_field` (uniform field) or
|
|
119
|
+
`System.set_peierls_phase` (arbitrary vector potential), applied to the
|
|
120
|
+
hoppings *after* `set_hopping`/`set_hopping_manual`:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
sys.set_hopping_manual(hop_dict)
|
|
124
|
+
sys.set_magnetic_field(alpha=0.01) # flux quanta per unit area
|
|
125
|
+
sys.get_ham()
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
See [`examples/magnetic_field/plot_magnetic_field.py`](examples/magnetic_field/plot_magnetic_field.py) for a full
|
|
129
|
+
worked example (an Aharonov-Bohm ring, reproducing the textbook result that
|
|
130
|
+
the spectrum is periodic in the enclosed flux with period one flux quantum).
|
|
131
|
+
|
|
132
|
+
A Chern number is the Berry curvature of a group of bands, integrated over
|
|
133
|
+
the Brillouin zone:
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
chern = gra.chern_number(bands=[0], nk=40) # ~0 for plain graphene
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
See [`examples/topology/plot_haldane_topology.py`](examples/topology/plot_haldane_topology.py) for the
|
|
140
|
+
Haldane model (the first Chern insulator), reproducing its topological
|
|
141
|
+
phase transition (Chern number 1 -> 0) and Berry-curvature map.
|
|
142
|
+
|
|
143
|
+
A spin-1/2 degree of freedom is added with `KSpace(lat, spin=True)`; onsite
|
|
144
|
+
values and hoppings then also accept 2x2 (Pauli) matrices:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from tbkit.kspace import KSpace, PAULI
|
|
148
|
+
|
|
149
|
+
kmele = KSpace(lat, spin=True)
|
|
150
|
+
kmele.set_hopping([{'i': 0, 'j': 0, 'R': (1, 0), 't': 1j*lam*PAULI['z']}]) # intrinsic SOC
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
A ribbon -- periodic in one direction, finite in the other, the standard
|
|
154
|
+
way to see edge states -- is cut out of a periodic model with
|
|
155
|
+
`tbkit.kspace.ribbon`:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from tbkit.kspace import ribbon
|
|
159
|
+
|
|
160
|
+
rib = ribbon(lat, list_hop, width=30, direction=1) # 30 unit cells wide
|
|
161
|
+
fig = rib.plot_bands()
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
See [`examples/topology/plot_edge_states.py`](examples/topology/plot_edge_states.py) for the zigzag
|
|
165
|
+
graphene ribbon's zero-energy edge band, and the Kane-Mele ribbon's helical
|
|
166
|
+
edge states crossing a spin-orbit gap.
|
|
167
|
+
|
|
168
|
+
## Examples
|
|
169
|
+
|
|
170
|
+
`examples/` is organized as a [Sphinx-Gallery](https://sphinx-gallery.github.io/)
|
|
171
|
+
source tree, one topic per subfolder, each with a `README.rst` blurb. Every
|
|
172
|
+
`plot_*.py` script is self-contained and runnable directly
|
|
173
|
+
(`python examples/<section>/<script>.py`), and checks its own key numeric
|
|
174
|
+
claims with `assert` before plotting -- nothing is asserted in the docs
|
|
175
|
+
that isn't also verified in code. Building the docs (`pip install -e ".[docs]"`
|
|
176
|
+
then `cd docs && make html`) renders these same scripts into an executed,
|
|
177
|
+
thumbnailed example gallery under `docs/source/api/gallery/`.
|
|
178
|
+
|
|
179
|
+
| Script | What it shows |
|
|
180
|
+
|---------------------------------------------------------------------------|----------------|
|
|
181
|
+
| [`tight_binding/plot_graphene_bands.py`](examples/tight_binding/plot_graphene_bands.py) | Real-space flake + reciprocal-space band structure; graphene's Dirac point and Wallace's 1947 linear dispersion. |
|
|
182
|
+
| [`tight_binding/plot_visualizing_a_model.py`](examples/tight_binding/plot_visualizing_a_model.py) | `tbkit.plot.Plot`: lattice, spectrum with sublattice polarization, density of states, eigenstate intensity. |
|
|
183
|
+
| [`magnetic_field/plot_magnetic_field.py`](examples/magnetic_field/plot_magnetic_field.py) | Peierls substitution; an Aharonov-Bohm ring's flux-periodic spectrum. |
|
|
184
|
+
| [`magnetic_field/plot_hofstadter_butterfly.py`](examples/magnetic_field/plot_hofstadter_butterfly.py) | The fractal spectrum of a lattice threaded by a continuously swept flux. |
|
|
185
|
+
| [`magnetic_field/plot_landau_levels.py`](examples/magnetic_field/plot_landau_levels.py) | Landau levels: a square lattice's non-relativistic ladder vs. graphene's relativistic sqrt(n) ladder and zero mode. |
|
|
186
|
+
| [`topology/plot_ssh_model.py`](examples/topology/plot_ssh_model.py) | The SSH model: bulk gap closing and topologically protected edge states. |
|
|
187
|
+
| [`disorder/plot_anderson_localization.py`](examples/disorder/plot_anderson_localization.py) | Anderson localization: IPR vs. disorder strength, extended vs. localized states. |
|
|
188
|
+
| [`topology/plot_haldane_topology.py`](examples/topology/plot_haldane_topology.py) | The Haldane model: Berry curvature, Chern number, topological phase transition. |
|
|
189
|
+
| [`flat_bands/plot_flat_bands.py`](examples/flat_bands/plot_flat_bands.py) | Exactly flat bands on the kagome and Lieb lattices. |
|
|
190
|
+
| [`topology/plot_kagome_chern_band.py`](examples/topology/plot_kagome_chern_band.py) | Gapping the kagome flat band into a Chern insulator (C=-1) with complex nearest-neighbor hopping. |
|
|
191
|
+
| [`topology/plot_edge_states.py`](examples/topology/plot_edge_states.py) | Zigzag graphene ribbon edge band; Kane-Mele helical edge states. |
|
|
192
|
+
| [`dynamics/plot_bloch_oscillations.py`](examples/dynamics/plot_bloch_oscillations.py) | Wannier-Stark ladder, its localization, and Bloch oscillations under a uniform tilt. |
|
|
193
|
+
| [`topology/plot_thouless_pump.py`](examples/topology/plot_thouless_pump.py) | The Rice-Mele model as a Thouless quantum pump: quantized Chern number and polarization winding. |
|
|
194
|
+
|
|
195
|
+
The `examples/` directory also has five older Jupyter notebooks (graphene
|
|
196
|
+
flakes, kagome/Lieb/dumbbell lattices, disorder, strain, time propagation)
|
|
197
|
+
predating the 0.2 API refresh below.
|
|
198
|
+
|
|
199
|
+
## Documentation
|
|
200
|
+
|
|
201
|
+
Rendered docs (tutorial, API reference, example gallery): https://cpoli.github.io/tbkit/
|
|
202
|
+
|
|
203
|
+
* [`docs/source/tutorial.rst`](docs/source/tutorial.rst) -- a narrative walkthrough of the
|
|
204
|
+
package, from building a lattice through topology, spin-orbit coupling,
|
|
205
|
+
and edge states.
|
|
206
|
+
* [`docs/source/history.rst`](docs/source/history.rst) -- a chronology of the breakthroughs
|
|
207
|
+
behind Tight-Binding theory (Bloch's theorem through the Kane-Mele model),
|
|
208
|
+
each one linked to the corresponding **tbkit** functionality and example
|
|
209
|
+
above.
|
|
210
|
+
* `docs/source/tbkit.rst` -- the API reference (auto-generated from docstrings).
|
|
211
|
+
|
|
212
|
+
Build the HTML docs with `cd docs && make html` (output in `docs/build/html`).
|
|
213
|
+
|
|
214
|
+
## A note on the API
|
|
215
|
+
|
|
216
|
+
Version 0.2 modernized the package to run on current Python/NumPy/SciPy and
|
|
217
|
+
cleaned up the API:
|
|
218
|
+
|
|
219
|
+
* Sublattice tags are plain one-character **strings** (`'a'`) rather than
|
|
220
|
+
byte strings (`b'a'`).
|
|
221
|
+
* Classes are named in `PascalCase` (`Lattice`, `System`, ...) rather than
|
|
222
|
+
lowercase names identical to their module (`lattice.lattice`,
|
|
223
|
+
`system.system`, ...), which used to make `import tbkit.lattice as lattice`
|
|
224
|
+
silently bind the wrong object.
|
|
225
|
+
|
|
226
|
+
For continuity, the pre-0.2 lowercase class names (`lattice`, `system`,
|
|
227
|
+
`plot`, `propagation`, `save`) remain available as aliases of the new
|
|
228
|
+
classes, so `from tbkit.lattice import lattice` still works. Example
|
|
229
|
+
notebooks predating 0.2 still use byte-string tags (`b'a'`) and will need
|
|
230
|
+
that one mechanical change to run on the current version.
|
|
231
|
+
|
|
232
|
+
## License
|
|
233
|
+
|
|
234
|
+
BSD 3-Clause, see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tbkit"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "A Python package to build and solve Tight-Binding models, for research and education."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "BSD-3-Clause"
|
|
11
|
+
authors = [{ name = "Charles Poli", email = "cpoli374@gmail.com" }]
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"numpy>=1.24",
|
|
15
|
+
"scipy>=1.10",
|
|
16
|
+
"matplotlib>=3.7",
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Intended Audience :: Education",
|
|
20
|
+
"Intended Audience :: Science/Research",
|
|
21
|
+
"Topic :: Scientific/Engineering :: Physics",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/cpoli/tbkit"
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
test = ["pytest>=7", "pytest-cov>=4"]
|
|
30
|
+
docs = ["sphinx>=7", "pydata-sphinx-theme>=0.15", "sphinx-gallery>=0.15"]
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.packages.find]
|
|
33
|
+
include = ["tbkit*"]
|
|
34
|
+
|
|
35
|
+
[tool.coverage.run]
|
|
36
|
+
source = ["tbkit"]
|
tbkit-0.2.0/setup.cfg
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Copyright 2014 Charles Poli.
|
|
2
|
+
#
|
|
3
|
+
# This file is part of TBKIT. It is subject to the license terms in the
|
|
4
|
+
# LICENSE file found in the top-level directory of this distribution and at
|
|
5
|
+
# https://github.com/cpoli/tbkit.
|
|
6
|
+
|
|
7
|
+
"""tbkit: build and solve Tight-Binding models."""
|
|
8
|
+
|
|
9
|
+
__version__ = "0.2.0"
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"Lattice", "System", "Plot", "Propagation", "Save", "KSpace",
|
|
13
|
+
"reciprocal_vectors", "error_handling",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
# NOTE: these are explicit imports, not `from tbkit.<module> import *`.
|
|
17
|
+
# A wildcard import here would rebind the `tbkit.<module>` submodule
|
|
18
|
+
# attributes to the classes they define (since e.g. tbkit/lattice.py both
|
|
19
|
+
# *is* the submodule `tbkit.lattice` and defines a `lattice` alias of the
|
|
20
|
+
# same name), breaking `import tbkit.lattice as lattice`-style imports.
|
|
21
|
+
from tbkit.lattice import Lattice
|
|
22
|
+
from tbkit.system import System
|
|
23
|
+
from tbkit.plot import Plot
|
|
24
|
+
from tbkit.propagation import Propagation
|
|
25
|
+
from tbkit.save import Save
|
|
26
|
+
from tbkit.kspace import KSpace, reciprocal_vectors
|
|
27
|
+
import tbkit.error_handling
|
tbkit-0.2.0/tbkit/dos.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Density of states from a set of eigenenergies, real-space
|
|
3
|
+
(:class:`tbkit.system.System`) or reciprocal-space
|
|
4
|
+
(:class:`tbkit.kspace.KSpace`, sampled over a k-mesh).
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
from numpy.typing import ArrayLike, NDArray
|
|
10
|
+
|
|
11
|
+
import tbkit.error_handling as error_handling
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def density_of_states(
|
|
15
|
+
energies: ArrayLike,
|
|
16
|
+
e_grid: ArrayLike | None = None,
|
|
17
|
+
broadening: float = 0.05,
|
|
18
|
+
kernel: str = 'gaussian',
|
|
19
|
+
) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
20
|
+
r'''
|
|
21
|
+
Get the density of states, broadened by a Gaussian or Lorentzian
|
|
22
|
+
kernel of width *broadening*:
|
|
23
|
+
|
|
24
|
+
.. math::
|
|
25
|
+
|
|
26
|
+
\rho(E) = \sum_n g(E-E_n)\, ,\quad
|
|
27
|
+
g(x) = \frac{1}{\sqrt{2\pi}\sigma}e^{-x^2/2\sigma^2}\ \text{(gaussian)}
|
|
28
|
+
\ \text{or}\
|
|
29
|
+
g(x) = \frac{1}{\pi}\frac{\sigma}{x^2+\sigma^2}\ \text{(lorentzian)}
|
|
30
|
+
|
|
31
|
+
Each level contributes a kernel of unit area, so
|
|
32
|
+
:math:`\int\rho(E)dE` equals the number of levels in *energies*,
|
|
33
|
+
for an *e_grid* wide enough to contain the tails.
|
|
34
|
+
|
|
35
|
+
:param energies: Array of (real) eigenenergies. Any shape (e.g. the
|
|
36
|
+
*en* attribute of **System**, or of **KSpace** after *get_bands*
|
|
37
|
+
over a k-mesh -- flattened automatically).
|
|
38
|
+
:param e_grid: Real ndarray. Default value None. Energies at which to
|
|
39
|
+
evaluate the density of states. If None, a grid of 401 points
|
|
40
|
+
spanning ``[min(energies)-3*broadening, max(energies)+3*broadening]``
|
|
41
|
+
is used.
|
|
42
|
+
:param broadening: Positive real number. Default value 0.05. Kernel width
|
|
43
|
+
:math:`\sigma`.
|
|
44
|
+
:param kernel: String. Default value 'gaussian'. 'gaussian' or 'lorentzian'.
|
|
45
|
+
|
|
46
|
+
:returns:
|
|
47
|
+
* **e_grid** -- Real ndarray. The energy grid used.
|
|
48
|
+
* **dos** -- Real ndarray, same shape as *e_grid*. Density of states.
|
|
49
|
+
'''
|
|
50
|
+
error_handling.ndarray_empty(np.asarray(energies), 'energies')
|
|
51
|
+
error_handling.positive_real(broadening, 'broadening')
|
|
52
|
+
error_handling.dos_kernel(kernel)
|
|
53
|
+
energies = np.asarray(energies).real.astype('f8').ravel()
|
|
54
|
+
if e_grid is None:
|
|
55
|
+
pad = 3 * broadening
|
|
56
|
+
e_grid = np.linspace(energies.min() - pad, energies.max() + pad, 401)
|
|
57
|
+
else:
|
|
58
|
+
error_handling.ndarray_empty(np.asarray(e_grid), 'e_grid')
|
|
59
|
+
e_grid = np.asarray(e_grid, dtype='f8')
|
|
60
|
+
diff = e_grid[:, None] - energies[None, :]
|
|
61
|
+
if kernel == 'gaussian':
|
|
62
|
+
weight = np.exp(-diff**2 / (2*broadening**2)) / (broadening*np.sqrt(2*np.pi))
|
|
63
|
+
else:
|
|
64
|
+
weight = (broadening/np.pi) / (diff**2 + broadening**2)
|
|
65
|
+
return e_grid, weight.sum(axis=1)
|