Skip to content

Commit a60dc9c

Browse files
committed
enh: commensurate cell constuction for twisted bilayers
1 parent b6e09ca commit a60dc9c

File tree

3 files changed

+104
-0
lines changed

3 files changed

+104
-0
lines changed

sisl/geom/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
:noindex:
4545
.. autofunction:: graphene
4646
:noindex:
47+
.. autofunction:: bilayer
48+
:noindex:
4749
4850
4951
Nanotube
@@ -64,5 +66,6 @@
6466
from .flat import *
6567
from .nanotube import *
6668
from .special import *
69+
from .bilayer import *
6770

6871
__all__ = [s for s in dir() if not s.startswith('_')]

sisl/geom/bilayer.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import numpy as np
2+
3+
from sisl import Atom, geom, Cuboid
4+
5+
__all__ = ['bilayer']
6+
7+
8+
def bilayer(bond, atom=None, twist=(5, 6), separation=3.35, return_angle=False, layer='both'):
9+
""" Commensurate unit cell of a twisted (hexagonal) bilayer structure.
10+
11+
This routine follows the prescription of twisted bilayer graphene found in
12+
Laissardiere et al., Nano Lett. 10, 804-808 (2010).
13+
14+
15+
Parameters
16+
----------
17+
bond : float
18+
length between atoms in nano-tube
19+
atom : Atom(6)
20+
bilayer atoms
21+
twist : (int, int)
22+
coordinates (n, m) defining the twist angle (theta)
23+
separation : float
24+
distance between the two layers
25+
return_angle : bool
26+
computed twist angle is returned in addition to the geometry instance
27+
layer : {'both', 'bottom', 'top'}
28+
control which layer(s) to include in geometry
29+
"""
30+
if atom is None:
31+
atom = Atom(Z=6, R=bond * 1.01)
32+
33+
# Compute twist angle
34+
m, n = twist
35+
if m < n:
36+
m, n = n, m
37+
38+
if not (isinstance(n, int) and isinstance(m, int)):
39+
raise RuntimeError('twist coordinates need to be integers!')
40+
41+
cos_theta = (n ** 2 + 4 * n * m + m ** 2) / (2 * (n ** 2 + n * m + m ** 2) )
42+
theta = np.arccos(cos_theta) * 360 / (2 * np.pi)
43+
44+
# Build structure
45+
graphene = geom.graphene(bond=bond, atom=atom)
46+
47+
# Choose a repetition that should assure both layers extend over the unit cell
48+
rep = 4 * (n + m)
49+
50+
# Construct bottom layer
51+
bottom = graphene.tile(rep, axis=0).tile(rep, axis=1)
52+
bottom = bottom.move(-bottom.center())
53+
bottom.cell[0] = n * graphene.cell[0] + m * graphene.cell[1]
54+
bottom.cell[1] = -m * graphene.cell[0] + (n + m) * graphene.cell[1]
55+
56+
# Construct top layer
57+
top = bottom.move([0, 0, separation]).rotate(theta, [0, 0, 1])
58+
top.cell[:] = bottom.cell[:]
59+
60+
# Compute number of atoms per layer
61+
natoms = 2 * (n ** 2 + n * m + m ** 2)
62+
63+
# Which layers to include
64+
if layer.lower() == 'bottom':
65+
bilayer = bottom
66+
elif layer.lower() == 'top':
67+
bilayer = top
68+
else:
69+
bilayer = bottom.add(top)
70+
natoms *= 2
71+
72+
# Remove atoms outside cell
73+
cell_box = Cuboid(bilayer.cell[:], center=bilayer.sc.center())
74+
outside_idx = []
75+
for i in bilayer:
76+
if not cell_box.within(bilayer[i]):
77+
outside_idx.append(i)
78+
bilayer = bilayer.remove(outside_idx)
79+
80+
# Center geometry around first atom
81+
bilayer = bilayer.move(-bilayer.xyz[0])
82+
83+
# Rotate whole cell
84+
vec = bilayer.cell[0] + bilayer.cell[1]
85+
vec_costh = np.dot([1, 0, 0], vec) / np.dot(vec, vec) ** .5
86+
vec_th = np.arccos(vec_costh) * 360 / (2 * np.pi)
87+
bilayer = bilayer.rotate(vec_th, [0, 0, 1])
88+
89+
# Sanity check
90+
assert len(bilayer) == natoms
91+
92+
if return_angle:
93+
return bilayer, theta
94+
else:
95+
return bilayer

sisl/geom/tests/test_geom.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,9 @@ def test_nanotube(self):
3030

3131
def test_diamond(self):
3232
a = diamond()
33+
34+
def test_bilayer(self):
35+
a = bilayer(1.42, twist=(6, 7))
36+
a = bilayer(1.42, twist=(6, 7), layer='bottom')
37+
a = bilayer(1.42, twist=(6, 7), layer='TOP')
38+
a, th = bilayer(1.42, twist=(6, 7), return_angle=True)

0 commit comments

Comments
 (0)