tbkit 0.2.0__py3-none-any.whl
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/__init__.py +27 -0
- tbkit/dos.py +65 -0
- tbkit/error_handling.py +988 -0
- tbkit/graphene.py +173 -0
- tbkit/kspace.py +563 -0
- tbkit/lattice.py +424 -0
- tbkit/lattices.py +126 -0
- tbkit/plot.py +690 -0
- tbkit/propagation.py +338 -0
- tbkit/save.py +98 -0
- tbkit/system.py +744 -0
- tbkit-0.2.0.dist-info/METADATA +260 -0
- tbkit-0.2.0.dist-info/RECORD +16 -0
- tbkit-0.2.0.dist-info/WHEEL +5 -0
- tbkit-0.2.0.dist-info/licenses/LICENSE +29 -0
- tbkit-0.2.0.dist-info/top_level.txt +1 -0
tbkit/lattice.py
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from numpy.typing import NDArray
|
|
5
|
+
import matplotlib.pyplot as plt
|
|
6
|
+
from matplotlib.figure import Figure
|
|
7
|
+
import tbkit.error_handling as error_handling
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
PI = np.pi
|
|
11
|
+
COOR_DTYPE = [('x', 'f8'), ('y', 'f8'), ('tag', 'U1')]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
#################################
|
|
15
|
+
# CLASS LATTICE
|
|
16
|
+
#################################
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Lattice():
|
|
20
|
+
r'''
|
|
21
|
+
Build up 1D or 2D lattice.
|
|
22
|
+
Lattice is defined by the discrete operation:
|
|
23
|
+
|
|
24
|
+
.. math::
|
|
25
|
+
|
|
26
|
+
\mathbf{R} = n_1\mathbf{a}_1 + n_2\mathbf{a}_2
|
|
27
|
+
|
|
28
|
+
where :math:`\mathbf{a}_1` and :math:`\mathbf{a}_2` are the two primitive
|
|
29
|
+
vectors and :math:`n_1` and :math:`n_2` are the number of unit cells along
|
|
30
|
+
:math:`\mathbf{a}_1` and :math:`\mathbf{a}_2`.
|
|
31
|
+
|
|
32
|
+
:param unit_cell: List of dictionaries.
|
|
33
|
+
One dictionary per site within the unit cell. Each dictionary has two keys:
|
|
34
|
+
|
|
35
|
+
* 'tag', one-character string. Label of the associated sublattice.
|
|
36
|
+
* 'r0', Tuple. Position.
|
|
37
|
+
:param prim_vec: List of tuples.
|
|
38
|
+
Define the primitive vectors. List of one/two tuples for 1D/2D respectively:
|
|
39
|
+
|
|
40
|
+
* Tuple, cartesian coordinate of the primitive vector :math:`\mathbf{a}_1`.
|
|
41
|
+
* Tuple, cartesian coordinate of the primitive vector :math:`\mathbf{a}_2`.
|
|
42
|
+
|
|
43
|
+
Example usage::
|
|
44
|
+
|
|
45
|
+
# Line-Centered Square lattice
|
|
46
|
+
unit_cell = [{'tag': 'a', 'r0': (0., 0.)}, {'tag': 'a', 'r0': (0., 1.)}]
|
|
47
|
+
prim_vec = [(0, 2), (2, 0)]
|
|
48
|
+
lat = lattice(unit_cell=unit_cell, prim_vec=prim_vec)
|
|
49
|
+
'''
|
|
50
|
+
|
|
51
|
+
def __init__(self, unit_cell: list[dict], prim_vec: list[tuple[float, float]]) -> None:
|
|
52
|
+
error_handling.unit_cell(unit_cell)
|
|
53
|
+
error_handling.prim_vec(prim_vec)
|
|
54
|
+
self.unit_cell = unit_cell
|
|
55
|
+
self.prim_vec = prim_vec
|
|
56
|
+
self.tags = np.unique(np.array([dic['tag'] for dic in self.unit_cell]))
|
|
57
|
+
self.n1, self.n2 = 0, 0
|
|
58
|
+
self.coor = np.array([], dtype=COOR_DTYPE)
|
|
59
|
+
self.sites = 0
|
|
60
|
+
|
|
61
|
+
def get_lattice(self, n1: int, n2: int = 1) -> None:
|
|
62
|
+
r'''
|
|
63
|
+
Get the lattice positions.
|
|
64
|
+
|
|
65
|
+
:param n1: Positive Integer.
|
|
66
|
+
Number of unit cells along :math:`\mathbf{a}_1`.
|
|
67
|
+
:param n2: Positive Integer. Default value 1.
|
|
68
|
+
Number of unit cells along :math:`\mathbf{a}_2`.
|
|
69
|
+
|
|
70
|
+
Example usage::
|
|
71
|
+
|
|
72
|
+
# Line-Centered Square lattice
|
|
73
|
+
unit_cell = [{'tag': 'a', 'r0': (0., 0.)}, {'tag': 'a', 'r0': (0., 1.)}]
|
|
74
|
+
prim_vec = [(0, 2), (2, 0)]
|
|
75
|
+
lat = lattice(unit_cell=unit_cell, prim_vec=prim_vec)
|
|
76
|
+
lat.get_lattice(n1=4, n2=5)
|
|
77
|
+
'''
|
|
78
|
+
error_handling.get_lattice(self.prim_vec, n1, n2)
|
|
79
|
+
sites_uc = len(self.unit_cell)
|
|
80
|
+
sites_tag = n1*n2
|
|
81
|
+
self.sites = sites_uc * sites_tag
|
|
82
|
+
self.coor = np.empty(self.sites, dtype=COOR_DTYPE)
|
|
83
|
+
self.n1, self.n2 = n1, n2
|
|
84
|
+
x = self.prim_vec[0][0] * np.arange(n1, dtype='f8')
|
|
85
|
+
y = self.prim_vec[0][1] * np.arange(n1, dtype='f8')
|
|
86
|
+
xx = np.empty(n1*n2)
|
|
87
|
+
yy = np.empty(n1*n2)
|
|
88
|
+
xx[:n1] = x
|
|
89
|
+
yy[:n1] = y
|
|
90
|
+
for i in range(1, n2):
|
|
91
|
+
xx[i*n1: (i+1)*n1] = x + i * self.prim_vec[1][0]
|
|
92
|
+
yy[i*n1: (i+1)*n1] = y + i * self.prim_vec[1][1]
|
|
93
|
+
for i, dic in enumerate(self.unit_cell):
|
|
94
|
+
self.coor['x'][i*sites_tag: (i+1)*sites_tag] = xx + dic['r0'][0]
|
|
95
|
+
self.coor['y'][i*sites_tag: (i+1)*sites_tag] = yy + dic['r0'][1]
|
|
96
|
+
self.coor['tag'][i*sites_tag: (i+1)*sites_tag] = dic['tag']
|
|
97
|
+
self.coor = np.sort(self.coor, order=('y', 'x'))
|
|
98
|
+
|
|
99
|
+
def add_sites(self, coor: NDArray) -> None:
|
|
100
|
+
'''
|
|
101
|
+
Add sites.
|
|
102
|
+
|
|
103
|
+
:param coor: Structured array with keys: {'x', 'y', 'tag'}.
|
|
104
|
+
|
|
105
|
+
Example usage::
|
|
106
|
+
|
|
107
|
+
# Square lattice
|
|
108
|
+
unit_cell = [{'tag': 'a', 'r0': (0., 0.)}]
|
|
109
|
+
prim_vec = [(0, 1), (1, 0)]
|
|
110
|
+
lat = lattice(unit_cell=unit_cell, prim_vec=prim_vec)
|
|
111
|
+
lat.get_lattice(n1=2, n2=2)
|
|
112
|
+
coor = np.array([(-1., -1, 'b'), (-2., -2, 'c')],
|
|
113
|
+
dtype=[('x', 'f8'), ('y', 'f8'), ('tag', 'U1')])
|
|
114
|
+
lat.add_sites(coor)
|
|
115
|
+
'''
|
|
116
|
+
error_handling.coor(coor)
|
|
117
|
+
self.coor = np.concatenate([self.coor, coor])
|
|
118
|
+
self.sites += len(coor)
|
|
119
|
+
self.tags = np.unique(np.concatenate([self.tags, coor['tag']]))
|
|
120
|
+
self.coor = np.sort(self.coor, order=('y', 'x'))
|
|
121
|
+
|
|
122
|
+
def remove_sites(self, index: list[int]) -> None:
|
|
123
|
+
'''
|
|
124
|
+
Remove sites defined by their indices
|
|
125
|
+
(use method lattice.plot(plt_index=True)
|
|
126
|
+
to get access to the site indices).
|
|
127
|
+
|
|
128
|
+
:param index: List. Site indices to be removed.
|
|
129
|
+
|
|
130
|
+
Example usage::
|
|
131
|
+
|
|
132
|
+
# Square lattice
|
|
133
|
+
unit_cell = [{'tag': 'a', 'r0': (0., 0.)}]
|
|
134
|
+
prim_vec = [(0, 1), (1, 0)]
|
|
135
|
+
lat = lattice(unit_cell=unit_cell, prim_vec=prim_vec)
|
|
136
|
+
lat.get_lattice(n1=2, n2=2)
|
|
137
|
+
lat.remove_sites([0, 2])
|
|
138
|
+
'''
|
|
139
|
+
error_handling.empty_coor(self.coor)
|
|
140
|
+
error_handling.remove_sites(index, self.sites)
|
|
141
|
+
mask = np.ones(self.sites, bool)
|
|
142
|
+
mask[index] = False
|
|
143
|
+
self.coor = self.coor[mask]
|
|
144
|
+
self.sites = self.coor.size
|
|
145
|
+
|
|
146
|
+
def remove_dangling(self) -> None:
|
|
147
|
+
'''
|
|
148
|
+
Remove dangling sites
|
|
149
|
+
(sites connected with just another site).
|
|
150
|
+
'''
|
|
151
|
+
error_handling.empty_coor(self.coor)
|
|
152
|
+
while True:
|
|
153
|
+
dif_x = self.coor['x'] - self.coor['x'].reshape(self.sites, 1)
|
|
154
|
+
dif_y = self.coor['y'] - self.coor['y'].reshape(self.sites, 1)
|
|
155
|
+
dis = np.sqrt(dif_x ** 2 + dif_y ** 2)
|
|
156
|
+
dis_unique = np.unique(dis)
|
|
157
|
+
len_hop = dis_unique[1]
|
|
158
|
+
ind = np.argwhere(np.isclose(dis, len_hop))
|
|
159
|
+
dang = []
|
|
160
|
+
for i in range(self.sites):
|
|
161
|
+
if (ind[:, 0] == i).sum() == 1:
|
|
162
|
+
dang.append(i)
|
|
163
|
+
self.coor = np.delete(self.coor, dang, axis=0)
|
|
164
|
+
self.sites -= len(dang)
|
|
165
|
+
if dang == []:
|
|
166
|
+
break
|
|
167
|
+
|
|
168
|
+
def shift_x(self, shift: float) -> None:
|
|
169
|
+
'''
|
|
170
|
+
Shift the x coordinates.
|
|
171
|
+
|
|
172
|
+
:param shift: Real number. Shift value.
|
|
173
|
+
'''
|
|
174
|
+
error_handling.empty_coor(self.coor)
|
|
175
|
+
error_handling.real_number(shift, 'shift')
|
|
176
|
+
self.coor['x'] += shift
|
|
177
|
+
|
|
178
|
+
def shift_y(self, shift: float) -> None:
|
|
179
|
+
'''
|
|
180
|
+
Shift by *delta_x* the x coordinates.
|
|
181
|
+
|
|
182
|
+
:param shift: Real number. Shift value.
|
|
183
|
+
'''
|
|
184
|
+
error_handling.empty_coor(self.coor)
|
|
185
|
+
error_handling.real_number(shift, 'shift')
|
|
186
|
+
self.coor['y'] += shift
|
|
187
|
+
|
|
188
|
+
def change_sign_x(self) -> None:
|
|
189
|
+
'''
|
|
190
|
+
Change x coordinates sign.
|
|
191
|
+
'''
|
|
192
|
+
error_handling.empty_coor(self.coor)
|
|
193
|
+
self.coor['x'] *= -1
|
|
194
|
+
|
|
195
|
+
def change_sign_y(self) -> None:
|
|
196
|
+
'''
|
|
197
|
+
Change y coordinates sign.
|
|
198
|
+
'''
|
|
199
|
+
error_handling.empty_coor(self.coor)
|
|
200
|
+
self.coor['y'] *= -1
|
|
201
|
+
|
|
202
|
+
def boundary_line(self, cx: float, cy: float, co: float) -> None:
|
|
203
|
+
r'''
|
|
204
|
+
Select sites according to :math:`c_yy+c_xx > c_0`.
|
|
205
|
+
|
|
206
|
+
:param cx: Real number. cx value.
|
|
207
|
+
:param cy: Real number. cy value.
|
|
208
|
+
:param co: Real number. co value.
|
|
209
|
+
'''
|
|
210
|
+
error_handling.empty_coor(self.coor)
|
|
211
|
+
error_handling.real_number(cx, 'cx')
|
|
212
|
+
error_handling.real_number(cy, 'cy')
|
|
213
|
+
error_handling.real_number(co, 'co')
|
|
214
|
+
self.coor = self.coor[cy * self.coor['y'] + cx * self.coor['x'] > co]
|
|
215
|
+
self.sites = len(self.coor)
|
|
216
|
+
|
|
217
|
+
def ellipse_in(self, rx: float, ry: float, x0: float, y0: float) -> None:
|
|
218
|
+
r'''
|
|
219
|
+
Select sites according to
|
|
220
|
+
|
|
221
|
+
.. math::
|
|
222
|
+
|
|
223
|
+
(x-x_0)^2/a^2+(y-y_0)^2/b^2 < 1\, .
|
|
224
|
+
|
|
225
|
+
:param list_hop: List of Dictionary (see set_hopping definition).
|
|
226
|
+
:param rx: Positive Real number. Radius along :math:`x`.
|
|
227
|
+
:param ry: Positive Real number. Radius along :math:`y`.
|
|
228
|
+
:param x0: Real number. :math:`x` center.
|
|
229
|
+
:param y0: Real number. :math:`y` center.
|
|
230
|
+
'''
|
|
231
|
+
error_handling.empty_coor(self.coor)
|
|
232
|
+
error_handling.positive_real(rx, 'rx')
|
|
233
|
+
error_handling.positive_real(ry, 'ry')
|
|
234
|
+
error_handling.real_number(x0, 'x0')
|
|
235
|
+
error_handling.real_number(y0, 'y0')
|
|
236
|
+
self.coor = self.coor[(self.coor['x'] -x0) ** 2 / rx ** 2 + \
|
|
237
|
+
(self.coor['y'] -y0) ** 2 / ry ** 2 < 1.]
|
|
238
|
+
self.sites = len(self.coor)
|
|
239
|
+
|
|
240
|
+
def ellipse_out(self, rx: float, ry: float, x0: float, y0: float) -> None:
|
|
241
|
+
r'''
|
|
242
|
+
Select sites according to
|
|
243
|
+
|
|
244
|
+
.. math::
|
|
245
|
+
|
|
246
|
+
(x-x_0)^2/a^2+(y-y_0)^2/b^2 > 1\, .
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
:param list_hop: List of Dictionary (see set_hopping definition).
|
|
250
|
+
:param rx: Positive Real number. Radius along :math:`x`.
|
|
251
|
+
:param ry: Positive Real number. Radius along :math:`y`.
|
|
252
|
+
:param x0: Real number. :math:`x` center.
|
|
253
|
+
:param y0: Real number. :math:`y` center.
|
|
254
|
+
'''
|
|
255
|
+
error_handling.empty_coor(self.coor)
|
|
256
|
+
error_handling.positive_real(rx, 'rx')
|
|
257
|
+
error_handling.positive_real(ry, 'ry')
|
|
258
|
+
error_handling.real_number(x0, 'x0')
|
|
259
|
+
error_handling.real_number(y0, 'y0')
|
|
260
|
+
self.coor = self.coor[(self.coor['x'] -x0) ** 2 / rx ** 2 + \
|
|
261
|
+
(self.coor['y'] -y0) ** 2 / ry ** 2 > 1.]
|
|
262
|
+
self.sites = len(self.coor)
|
|
263
|
+
|
|
264
|
+
def center(self) -> None:
|
|
265
|
+
'''
|
|
266
|
+
Fix the center of mass of the lattice at (0, 0).
|
|
267
|
+
'''
|
|
268
|
+
error_handling.empty_coor(self.coor)
|
|
269
|
+
self.coor['x'] -= np.mean(self.coor['x'])
|
|
270
|
+
self.coor['y'] -= np.mean(self.coor['y'])
|
|
271
|
+
|
|
272
|
+
def rotation(self, theta: float) -> None:
|
|
273
|
+
r'''
|
|
274
|
+
Rotate the lattice structure by the angle :math:`\theta`.
|
|
275
|
+
|
|
276
|
+
:param theta: Rotation angle in degrees.
|
|
277
|
+
'''
|
|
278
|
+
error_handling.empty_coor(self.coor)
|
|
279
|
+
error_handling.real_number(theta, 'theta')
|
|
280
|
+
theta *= PI / 360
|
|
281
|
+
for dic in self.unit_cell:
|
|
282
|
+
x = self.coor['x'] - dic['r0'][0]
|
|
283
|
+
y = self.coor['y'] - dic['r0'][1]
|
|
284
|
+
self.coor['x'] = x * np.cos(theta) - y * np.sin(theta) + dic['r0'][0]
|
|
285
|
+
self.coor['y'] = y * np.cos(theta) + x* np.sin(theta) + dic['r0'][1]
|
|
286
|
+
|
|
287
|
+
def clean_coor(self) -> None:
|
|
288
|
+
'''
|
|
289
|
+
Keep only the sites with different coordinates.
|
|
290
|
+
'''
|
|
291
|
+
error_handling.empty_coor(self.coor)
|
|
292
|
+
coor = self.coor[['x', 'y']].copy()
|
|
293
|
+
coor['x'], coor['y'] = self.coor['x'].round(4), self.coor['y'].round(4)
|
|
294
|
+
_, idx = np.unique(coor, return_index=True)
|
|
295
|
+
self.coor = self.coor[idx]
|
|
296
|
+
self.sites = len(self.coor)
|
|
297
|
+
|
|
298
|
+
def __add__(self, other: 'Lattice') -> 'Lattice':
|
|
299
|
+
'''
|
|
300
|
+
Overloading operator +.
|
|
301
|
+
'''
|
|
302
|
+
error_handling.lat(other)
|
|
303
|
+
error_handling.empty_coor(self.coor)
|
|
304
|
+
error_handling.empty_coor(other.coor)
|
|
305
|
+
coor = np.concatenate([self.coor, other.coor])
|
|
306
|
+
tags = np.concatenate([self.tags, other.tags])
|
|
307
|
+
lat = lattice(unit_cell=self.unit_cell, prim_vec=self.prim_vec)
|
|
308
|
+
lat.add_sites(coor)
|
|
309
|
+
lat.sites = self.sites + other.sites
|
|
310
|
+
lat.tags = np.unique(tags)
|
|
311
|
+
return lat
|
|
312
|
+
|
|
313
|
+
def __iadd__(self, other: 'Lattice') -> 'Lattice':
|
|
314
|
+
'''
|
|
315
|
+
Overloading operator +=.
|
|
316
|
+
'''
|
|
317
|
+
error_handling.lat(other)
|
|
318
|
+
error_handling.empty_coor(self.coor)
|
|
319
|
+
error_handling.empty_coor(other.coor)
|
|
320
|
+
self.coor = np.concatenate([self.coor, other.coor])
|
|
321
|
+
self.sites += other.sites
|
|
322
|
+
self.tags = np.unique(np.concatenate([self.tags, other.tags]))
|
|
323
|
+
return self
|
|
324
|
+
|
|
325
|
+
def __sub__(self, other: 'Lattice') -> 'Lattice':
|
|
326
|
+
'''
|
|
327
|
+
Overloading operator -.
|
|
328
|
+
|
|
329
|
+
.. note::
|
|
330
|
+
|
|
331
|
+
The tags are not considered in the lattice subtraction.
|
|
332
|
+
'''
|
|
333
|
+
error_handling.lat(other)
|
|
334
|
+
error_handling.empty_coor(self.coor)
|
|
335
|
+
error_handling.empty_coor(other.coor)
|
|
336
|
+
boo = np.zeros(self.sites, bool)
|
|
337
|
+
for c in other.coor:
|
|
338
|
+
boo += np.isclose(c['x'], self.coor['x']) & np.isclose(c['y'], self.coor['y'])
|
|
339
|
+
coor = self.coor[np.logical_not(boo)]
|
|
340
|
+
lat = lattice(unit_cell=self.unit_cell, prim_vec=self.prim_vec)
|
|
341
|
+
lat.add_sites(coor)
|
|
342
|
+
lat.sites = len(lat.coor)
|
|
343
|
+
lat.tags = self.tags
|
|
344
|
+
return lat
|
|
345
|
+
|
|
346
|
+
def __isub__(self, other: 'Lattice') -> 'Lattice':
|
|
347
|
+
'''
|
|
348
|
+
Overloading operator -=.
|
|
349
|
+
|
|
350
|
+
.. note::
|
|
351
|
+
|
|
352
|
+
The tags are not considered in the lattice subtraction.
|
|
353
|
+
'''
|
|
354
|
+
error_handling.lat(other)
|
|
355
|
+
error_handling.empty_coor(self.coor)
|
|
356
|
+
error_handling.empty_coor(other.coor)
|
|
357
|
+
boo = np.zeros(self.sites, bool)
|
|
358
|
+
for c in other.coor:
|
|
359
|
+
boo += np.isclose(c['x'], self.coor['x']) & np.isclose(c['y'], self.coor['y'])
|
|
360
|
+
self.coor = self.coor[np.logical_not(boo)]
|
|
361
|
+
self.sites = int(np.sum(np.logical_not(boo)))
|
|
362
|
+
return self
|
|
363
|
+
|
|
364
|
+
def plot(
|
|
365
|
+
self,
|
|
366
|
+
ms: float = 20,
|
|
367
|
+
fs: float = 20,
|
|
368
|
+
plt_index: bool = False,
|
|
369
|
+
axis: bool = False,
|
|
370
|
+
figsize: tuple[float, float] | None = None,
|
|
371
|
+
) -> Figure:
|
|
372
|
+
'''
|
|
373
|
+
Plot lattice in hopping space.
|
|
374
|
+
|
|
375
|
+
:param ms: Positive number. Default value 20. Markersize.
|
|
376
|
+
:param fs: Positve number. Default value 20. Fontsize.
|
|
377
|
+
:param plt_index: Boolean. Default value False. Plot site labels.
|
|
378
|
+
:param axis: Boolean. Default value False. Plot axis.
|
|
379
|
+
:param figsize: Tuple. Default value None. Figsize.
|
|
380
|
+
|
|
381
|
+
:returns:
|
|
382
|
+
* **fig** -- Figure.
|
|
383
|
+
'''
|
|
384
|
+
error_handling.empty_coor(self.coor)
|
|
385
|
+
error_handling.positive_real(ms, 'ms')
|
|
386
|
+
error_handling.positive_real(fs, 'fs')
|
|
387
|
+
error_handling.boolean(plt_index, 'plt_index')
|
|
388
|
+
error_handling.boolean(axis, 'axis')
|
|
389
|
+
if figsize is None:
|
|
390
|
+
figsize = (5, 5)
|
|
391
|
+
error_handling.list_tuple_2elem(figsize, 'figsize')
|
|
392
|
+
error_handling.positive_real(figsize[0], 'figsize[0]')
|
|
393
|
+
error_handling.positive_real(figsize[1], 'figsize[1]')
|
|
394
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
395
|
+
# plot sites
|
|
396
|
+
colors = ['b', 'r', 'g', 'y', 'm', 'k']
|
|
397
|
+
for color, tag in zip(colors, self.tags):
|
|
398
|
+
plt.plot(self.coor['x'][self.coor['tag'] == tag],
|
|
399
|
+
self.coor['y'][self.coor['tag'] == tag],
|
|
400
|
+
'o', color=color, ms=ms, markeredgecolor='none')
|
|
401
|
+
ax.set_aspect('equal')
|
|
402
|
+
ax.set_xlim([np.min(self.coor['x'])-1., np.max(self.coor['x'])+1.])
|
|
403
|
+
ax.set_ylim([np.min(self.coor['y'])-1., np.max(self.coor['y'])+1.])
|
|
404
|
+
if not axis:
|
|
405
|
+
ax.axis('off')
|
|
406
|
+
# plot indices
|
|
407
|
+
if plt_index:
|
|
408
|
+
indices = ['{}'.format(i) for i in range(self.sites)]
|
|
409
|
+
for l, x, y in zip(indices, self.coor['x'], self.coor['y']):
|
|
410
|
+
plt.annotate(l, xy=(x, y), xytext=(0, 0),
|
|
411
|
+
textcoords='offset points',
|
|
412
|
+
ha='right', va='bottom', size=fs)
|
|
413
|
+
plt.draw()
|
|
414
|
+
return fig
|
|
415
|
+
|
|
416
|
+
def show(self) -> None:
|
|
417
|
+
"""
|
|
418
|
+
Emulate Matplotlib method plt.show().
|
|
419
|
+
"""
|
|
420
|
+
plt.show()
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
# Backward-compatible lowercase alias (pre-0.2 API).
|
|
424
|
+
lattice = Lattice
|
tbkit/lattices.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A small library of common 2D Bravais lattices, ready to feed into
|
|
3
|
+
:class:`tbkit.lattice.Lattice`, :class:`tbkit.system.System`, or
|
|
4
|
+
:class:`tbkit.kspace.KSpace`.
|
|
5
|
+
|
|
6
|
+
Each function returns a fresh ``Lattice`` instance with *unit_cell* and
|
|
7
|
+
*prim_vec* already set (call ``get_lattice`` yourself to build a finite
|
|
8
|
+
flake, or hand it straight to ``KSpace`` for a periodic/band-structure
|
|
9
|
+
calculation). Nearest-neighbor sites are a distance *a* apart.
|
|
10
|
+
|
|
11
|
+
Example usage::
|
|
12
|
+
|
|
13
|
+
import tbkit.lattices as lattices
|
|
14
|
+
lat = lattices.kagome()
|
|
15
|
+
lat.get_lattice(n1=6, n2=6)
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from math import sqrt
|
|
20
|
+
|
|
21
|
+
from tbkit.lattice import Lattice
|
|
22
|
+
import tbkit.error_handling as error_handling
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _lat(unit_cell: list[dict], prim_vec: list[tuple[float, float]]) -> Lattice:
|
|
26
|
+
return Lattice(unit_cell=unit_cell, prim_vec=prim_vec)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def chain(a: float = 1.) -> Lattice:
|
|
30
|
+
'''
|
|
31
|
+
1D chain: one site per unit cell.
|
|
32
|
+
|
|
33
|
+
:param a: Positive real number. Default value 1. Lattice constant.
|
|
34
|
+
'''
|
|
35
|
+
error_handling.positive_real(a, 'a')
|
|
36
|
+
return _lat([{'tag': 'a', 'r0': (0., 0.)}], [(a, 0.)])
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def square(a: float = 1.) -> Lattice:
|
|
40
|
+
'''
|
|
41
|
+
Square lattice: one site per unit cell.
|
|
42
|
+
|
|
43
|
+
:param a: Positive real number. Default value 1. Lattice constant.
|
|
44
|
+
'''
|
|
45
|
+
error_handling.positive_real(a, 'a')
|
|
46
|
+
return _lat([{'tag': 'a', 'r0': (0., 0.)}], [(a, 0.), (0., a)])
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def triangular(a: float = 1.) -> Lattice:
|
|
50
|
+
'''
|
|
51
|
+
Triangular lattice: one site per unit cell.
|
|
52
|
+
|
|
53
|
+
:param a: Positive real number. Default value 1. Lattice constant.
|
|
54
|
+
'''
|
|
55
|
+
error_handling.positive_real(a, 'a')
|
|
56
|
+
return _lat([{'tag': 'a', 'r0': (0., 0.)}],
|
|
57
|
+
[(a, 0.), (0.5*a, 0.5*sqrt(3)*a)])
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def honeycomb(a: float = 1.) -> Lattice:
|
|
61
|
+
'''
|
|
62
|
+
Honeycomb lattice (e.g. graphene): two sites per unit cell, nearest
|
|
63
|
+
neighbors a distance *a* apart. See also :class:`tbkit.graphene.GrapheneLattice`
|
|
64
|
+
for ready-made finite flakes of various shapes.
|
|
65
|
+
|
|
66
|
+
:param a: Positive real number. Default value 1. Nearest-neighbor distance.
|
|
67
|
+
'''
|
|
68
|
+
error_handling.positive_real(a, 'a')
|
|
69
|
+
dx, dy = 0.5*sqrt(3)*a, 0.5*a
|
|
70
|
+
unit_cell = [{'tag': 'a', 'r0': (0., 0.)}, {'tag': 'b', 'r0': (dx, dy)}]
|
|
71
|
+
prim_vec = [(2*dx, 0.), (dx, 1.5*a)]
|
|
72
|
+
return _lat(unit_cell, prim_vec)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def kagome(a: float = 1.) -> Lattice:
|
|
76
|
+
'''
|
|
77
|
+
Kagome lattice: three sites per unit cell (tags 'a', 'b', 'c'), arranged
|
|
78
|
+
as corner-sharing triangles on a triangular Bravais lattice. With
|
|
79
|
+
uniform nearest-neighbor hopping, this lattice famously has an exactly
|
|
80
|
+
flat band (at E = -2t for hopping amplitude t).
|
|
81
|
+
|
|
82
|
+
:param a: Positive real number. Default value 1. Nearest-neighbor distance.
|
|
83
|
+
|
|
84
|
+
Example usage (nearest-neighbor hopping, for :class:`tbkit.kspace.KSpace`)::
|
|
85
|
+
|
|
86
|
+
lat = lattices.kagome()
|
|
87
|
+
kag = KSpace(lat)
|
|
88
|
+
kag.set_hopping([{'i': 0, 'j': 1, 'R': (0, 0), 't': t},
|
|
89
|
+
{'i': 0, 'j': 1, 'R': (-1, 0), 't': t},
|
|
90
|
+
{'i': 0, 'j': 2, 'R': (0, 0), 't': t},
|
|
91
|
+
{'i': 0, 'j': 2, 'R': (0, -1), 't': t},
|
|
92
|
+
{'i': 1, 'j': 2, 'R': (0, 0), 't': t},
|
|
93
|
+
{'i': 1, 'j': 2, 'R': (1, -1), 't': t}])
|
|
94
|
+
'''
|
|
95
|
+
error_handling.positive_real(a, 'a')
|
|
96
|
+
a1 = (2*a, 0.)
|
|
97
|
+
a2 = (a, sqrt(3)*a)
|
|
98
|
+
unit_cell = [{'tag': 'a', 'r0': (0., 0.)},
|
|
99
|
+
{'tag': 'b', 'r0': (a, 0.)},
|
|
100
|
+
{'tag': 'c', 'r0': (0.5*a, 0.5*sqrt(3)*a)}]
|
|
101
|
+
return _lat(unit_cell, [a1, a2])
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def lieb(a: float = 1.) -> Lattice:
|
|
105
|
+
'''
|
|
106
|
+
Lieb lattice: three sites per unit cell (tag 'a': corner site; tags
|
|
107
|
+
'b', 'c': edge-center sites) on a square Bravais lattice. With uniform
|
|
108
|
+
nearest-neighbor hopping, this lattice famously has an exactly flat
|
|
109
|
+
band (at E = 0), squeezed between two dispersive bands.
|
|
110
|
+
|
|
111
|
+
:param a: Positive real number. Default value 1. Nearest-neighbor distance.
|
|
112
|
+
|
|
113
|
+
Example usage (nearest-neighbor hopping, for :class:`tbkit.kspace.KSpace`)::
|
|
114
|
+
|
|
115
|
+
lat = lattices.lieb()
|
|
116
|
+
lb = KSpace(lat)
|
|
117
|
+
lb.set_hopping([{'i': 0, 'j': 1, 'R': (0, 0), 't': t},
|
|
118
|
+
{'i': 0, 'j': 1, 'R': (-1, 0), 't': t},
|
|
119
|
+
{'i': 0, 'j': 2, 'R': (0, 0), 't': t},
|
|
120
|
+
{'i': 0, 'j': 2, 'R': (0, -1), 't': t}])
|
|
121
|
+
'''
|
|
122
|
+
error_handling.positive_real(a, 'a')
|
|
123
|
+
unit_cell = [{'tag': 'a', 'r0': (0., 0.)},
|
|
124
|
+
{'tag': 'b', 'r0': (a, 0.)},
|
|
125
|
+
{'tag': 'c', 'r0': (0., a)}]
|
|
126
|
+
return _lat(unit_cell, [(2*a, 0.), (0., 2*a)])
|