Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download
Project: topocm
Views: 387
1
# Copyright 2016 Christoph Groth (INAC / CEA Grenoble).
2
#
3
# This file is subject to the 2-clause BSD license as found at
4
# http://kwant-project.org/license.
5
6
"""Replace symmetries of Kwant builders with momentum parameters to the
7
system."""
8
9
import sys
10
import collections
11
import cmath
12
import numpy as np
13
import tinyarray as ta
14
15
import kwant
16
from kwant.builder import herm_conj
17
18
19
if sys.version_info >= (3, 0):
20
def _hashable(obj):
21
return isinstance(obj, collections.Hashable)
22
else:
23
def _hashable(obj):
24
return (isinstance(obj, collections.Hashable)
25
and not isinstance(obj, np.ndarray))
26
27
28
def _memoize(f):
29
"""Decorator to memoize a function that works even with unhashable args.
30
31
This decorator will even work with functions whose args are not hashable.
32
The cache key is made up by the hashable arguments and the ids of the
33
non-hashable args. It is up to the user to make sure that non-hashable
34
args do not change during the lifetime of the decorator.
35
36
This decorator will keep reevaluating functions that return None.
37
"""
38
def lookup(*args):
39
key = tuple(arg if _hashable(arg) else id(arg) for arg in args)
40
result = cache.get(key)
41
if result is None:
42
cache[key] = result = f(*args)
43
return result
44
cache = {}
45
return lookup
46
47
48
def wraparound(builder, keep=None):
49
"""Replace translational symmetries by momentum parameters.
50
51
A new Builder instance is returned. By default, each symmetry is replaced
52
by one scalar momentum parameter that is appended to the already existing
53
arguments of the system. Optionally, one symmetry may be kept by using the
54
`keep` argument.
55
"""
56
57
@_memoize
58
def bind_site(val):
59
assert callable(val)
60
return lambda a, *args: val(a, *args[:mnp])
61
62
@_memoize
63
def bind_hopping_as_site(elem, val):
64
def f(a, *args):
65
phase = cmath.exp(1j * ta.dot(elem, args[mnp:]))
66
v = val(a, sym.act(elem, a), *args[:mnp]) if callable(val) else val
67
pv = phase * v
68
return pv + herm_conj(pv)
69
return f
70
71
@_memoize
72
def bind_hopping(elem, val):
73
def f(a, b, *args):
74
phase = cmath.exp(1j * ta.dot(elem, args[mnp:]))
75
v = val(a, sym.act(elem, b), *args[:mnp]) if callable(val) else val
76
return phase * v
77
return f
78
79
@_memoize
80
def bind_sum(*vals):
81
return lambda *args: sum((val(*args) if callable(val) else val)
82
for val in vals)
83
84
if keep is None:
85
ret = kwant.Builder()
86
sym = builder.symmetry
87
else:
88
periods = list(builder.symmetry.periods)
89
ret = kwant.Builder(kwant.TranslationalSymmetry(periods.pop(keep)))
90
sym = kwant.TranslationalSymmetry(*periods)
91
92
sites = {}
93
hops = collections.defaultdict(list)
94
95
mnp = -len(sym.periods) # Used by the bound functions above.
96
97
# Store lists of values, so that multiple values can be assigned to the
98
# same site or hopping.
99
for site, val in builder.site_value_pairs():
100
sites[site] = [bind_site(val) if callable(val) else val]
101
102
for hop, val in builder.hopping_value_pairs():
103
a, b = hop
104
b_dom = sym.which(b)
105
b_wa = sym.act(-b_dom, b)
106
107
if a == b_wa:
108
# The hopping gets wrapped-around into an onsite Hamiltonian.
109
# Since site `a` already exists in the system, we can simply append.
110
sites[a].append(bind_hopping_as_site(b_dom, val))
111
else:
112
# The hopping remains a hopping.
113
if b != b_wa or callable(val):
114
# The hopping got wrapped-around or is a function.
115
val = bind_hopping(b_dom, val)
116
117
# Make sure that there is only one entry for each hopping
118
# (pointing in one direction).
119
if (b_wa, a) in hops:
120
assert (a, b_wa) not in hops
121
if callable(val):
122
assert not isinstance(val, kwant.builder.HermConjOfFunc)
123
val = kwant.builder.HermConjOfFunc(val)
124
else:
125
val = kwant.builder.herm_conj(val)
126
127
hops[b_wa, a].append(val)
128
else:
129
hops[a, b_wa].append(val)
130
131
# Copy stuff into result builder, converting lists of more than one element
132
# into summing functions.
133
for site, vals in sites.items():
134
ret[site] = vals[0] if len(vals) == 1 else bind_sum(*vals)
135
136
for hop, vals in hops.items():
137
ret[hop] = vals[0] if len(vals) == 1 else bind_sum(*vals)
138
139
return ret
140
141
142
def plot_bands_2d(syst, args=(), momenta=(31, 31)):
143
144
"""Plot the bands of a system with two wrapped-around symmetries."""
145
from mpl_toolkits.mplot3d import Axes3D
146
from matplotlib import pyplot
147
148
if not isinstance(syst, kwant.system.FiniteSystem):
149
raise TypeError("Need a system without symmetries.")
150
151
fig = pyplot.figure()
152
ax = fig.gca(projection='3d')
153
kxs = np.linspace(-np.pi, np.pi, momenta[0])
154
kys = np.linspace(-np.pi, np.pi, momenta[1])
155
156
energies = [[np.sort(np.linalg.eigvalsh(syst.hamiltonian_submatrix(
157
args + (kx, ky), sparse=False)).real)
158
for ky in kys] for kx in kxs]
159
energies = np.array(energies)
160
161
mesh_x, mesh_y = np.meshgrid(kxs, kys)
162
for i in range(energies.shape[-1]):
163
ax.plot_wireframe(mesh_x, mesh_y, energies[:, :, i],
164
rstride=1, cstride=1)
165
166
pyplot.show()
167
168
169
def _simple_syst(lat, E=0, t=1+1j):
170
"""Create a builder for a simple infinite system."""
171
sym = kwant.TranslationalSymmetry(lat.vec((1, 0)), lat.vec((0, 1)))
172
# Build system with 2d periodic BCs. This system cannot be finalized in
173
# Kwant <= 1.2.
174
syst = kwant.Builder(sym)
175
syst[lat.shape(lambda p: True, (0, 0))] = E
176
syst[lat.neighbors(1)] = t
177
return syst
178
179
180
def test_consistence_with_bands(kx=1.9, nkys=31):
181
kys = np.linspace(-np.pi, np.pi, nkys)
182
for lat in [kwant.lattice.honeycomb(), kwant.lattice.square()]:
183
syst = _simple_syst(lat)
184
wa_keep_1 = wraparound(syst, keep=1).finalized()
185
wa_keep_none = wraparound(syst).finalized()
186
187
bands = kwant.physics.Bands(wa_keep_1, (kx,))
188
energies_a = [bands(ky) for ky in
189
(kys if kwant.__version__ > "1.0" else reversed(kys))]
190
191
energies_b = []
192
for ky in kys:
193
H = wa_keep_none.hamiltonian_submatrix((kx, ky), sparse=False)
194
evs = np.sort(np.linalg.eigvalsh(H).real)
195
energies_b.append(evs)
196
197
np.testing.assert_almost_equal(energies_a, energies_b)
198
199
200
def test_opposite_hoppings():
201
lat = kwant.lattice.square()
202
203
for val in [1j, lambda a, b: 1j]:
204
syst = kwant.Builder(kwant.TranslationalSymmetry((1, 1)))
205
syst[ (lat(x, 0) for x in [-1, 0]) ] = 0
206
syst[lat(0, 0), lat(-1, 0)] = val
207
syst[lat(-1, 0), lat(-1, -1)] = val
208
209
fsyst = wraparound(syst).finalized()
210
np.testing.assert_almost_equal(fsyst.hamiltonian_submatrix([0]), 0)
211
212
213
def test_value_types(k=(-1.1, 0.5), E=0, t=1):
214
for lat in [kwant.lattice.honeycomb(), kwant.lattice.square()]:
215
syst = wraparound(_simple_syst(lat, E, t)).finalized()
216
H = syst.hamiltonian_submatrix(k, sparse=False)
217
for E1, t1 in [(float(E), float(t)),
218
(np.array([[E]], float), np.array([[1]], float)),
219
(ta.array([[E]], float), ta.array([[1]], float))]:
220
for E2 in [E1, lambda a: E1]:
221
for t2 in [t1, lambda a, b: t1]:
222
syst = wraparound(_simple_syst(lat, E2, t2)).finalized()
223
H_alt = syst.hamiltonian_submatrix(k, sparse=False)
224
np.testing.assert_equal(H_alt, H)
225
226
227
def test():
228
test_consistence_with_bands()
229
test_opposite_hoppings()
230
test_value_types()
231
232
233
def demo():
234
"""Calculate and plot the band structure of graphene."""
235
lat = kwant.lattice.honeycomb()
236
syst = wraparound(_simple_syst(lat)).finalized()
237
plot_bands_2d(syst)
238
239
240
if __name__ == '__main__':
241
test()
242
demo()
243