Skip to content

Commit dcf4bfe

Browse files
committed
enh: finalized few missing details in bilayer method
Signed-off-by: Nick Papior <[email protected]>
1 parent f5695c8 commit dcf4bfe

File tree

3 files changed

+76
-63
lines changed

3 files changed

+76
-63
lines changed

sisl/geom/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
honeycomb
2323
nanotube
2424
diamond
25+
bilayer
2526
2627
2728
Basic

sisl/geom/bilayer.py

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,52 @@
11
import numpy as np
22

3-
from sisl import Atom, geom, Cuboid
3+
from sisl import geom, Atom, Cuboid
44

55
__all__ = ['bilayer']
66

77

8-
def bilayer(bond=1.42, bottom_atom=None, top_atom=None, stacking='AB', twist=(0, 0), separation=3.35, ret_angle=False, layer='both'):
9-
""" Commensurate unit cell of a hexagonal bilayer structure, possibly with a twist angle.
8+
def bilayer(bond=1.42, bottom_atom=None, top_atom=None, stacking='AB',
9+
twist=(0, 0), separation=3.35, ret_angle=False, layer='both'):
10+
r""" Commensurate unit cell of a hexagonal bilayer structure, possibly with a twist angle.
1011
11-
This routine follows the prescription of twisted bilayer graphene found in
12-
Laissardiere et al., Nano Lett. 10, 804-808 (2010).
12+
This routine follows the prescription of twisted bilayer graphene found in [1]_.
1313
14+
Notes
15+
-----
16+
This routine may change in the future to force some of the arguments.
1417
1518
Parameters
1619
----------
17-
bond : 1.42
18-
bond length (in Angstrom) between atoms in the honeycomb lattice
19-
bottom_atom : Atom(6)
20-
atom (or atoms) in the bottom layer
21-
top_atom : Atom(6)
22-
atom (or atoms) in the top layer
20+
bond : float, optional
21+
bond length between atoms in the honeycomb lattice
22+
bottom_atom : Atom, optional
23+
atom (or atoms) in the bottom layer. Defaults to ``Atom(6)``
24+
top_atom : Atom, optional
25+
atom (or atoms) in the top layer, defaults to `bottom_atom`
2326
stacking : {'AB', 'AA'}
2427
stacking sequence of the bilayer
25-
twist : (0, 0)
28+
twist : tuple of int, optional
2629
integer coordinates (m, n) defining a commensurate twist angle
27-
separation : float
30+
separation : float, optional
2831
distance between the two layers (in Angstrom)
29-
ret_angle : bool
32+
ret_angle : bool, optional
3033
return the twist angle (in degrees) in addition to the geometry instance
3134
layer : {'both', 'bottom', 'top'}
32-
control which layer(s) to include in geometry
35+
control which layer(s) to return
36+
37+
References
38+
----------
39+
.. [1] G. Trambly de Laissardiere, D. Mayou, L. Magaud, "Localization of Dirac Electrons in Rotated Graphene Bilayers", Nano Letts. 10, 804-808 (2010)
3340
"""
3441
if bottom_atom is None:
3542
bottom_atom = Atom(Z=6, R=bond * 1.01)
3643
if top_atom is None:
37-
top_atom = Atom(Z=6, R=bond * 1.01)
44+
top_atom = bottom_atom
3845

3946
# Construct two layers
40-
bottom = geom.honeycomb(bond=bond, atom=bottom_atom)
41-
top = geom.honeycomb(bond=bond, atom=top_atom)
42-
honeycomb = bottom.copy()
47+
bottom = geom.honeycomb(bond, bottom_atom)
48+
top = geom.honeycomb(bond, top_atom)
49+
ref_cell = bottom.cell.copy()
4350

4451
if stacking.lower() == 'aa':
4552
top = top.move([0, 0, separation])
@@ -54,7 +61,7 @@ def bilayer(bond=1.42, bottom_atom=None, top_atom=None, stacking='AB', twist=(0,
5461
m, n = n, m
5562

5663
if not (isinstance(n, int) and isinstance(m, int)):
57-
raise RuntimeError('twist coordinates need to be integers!')
64+
raise ValueError("bilayer: twist coordinates need to be integers!")
5865

5966
if m == n:
6067
# No twisting
@@ -63,22 +70,22 @@ def bilayer(bond=1.42, bottom_atom=None, top_atom=None, stacking='AB', twist=(0,
6370
natoms = 2
6471
else:
6572
# Twisting
66-
cos_theta = (n ** 2 + 4 * n * m + m ** 2) / (2 * (n ** 2 + n * m + m ** 2) )
67-
theta = np.arccos(cos_theta) * 360 / (2 * np.pi)
73+
cos_theta = (n ** 2 + 4 * n * m + m ** 2) / (2 * (n ** 2 + n * m + m ** 2))
74+
theta = np.arccos(cos_theta) * 180 / np.pi
6875
rep = 4 * (n + m)
6976
natoms = 2 * (n ** 2 + n * m + m ** 2)
7077

7178
if rep > 1:
7279
# Set origo through an A atom near the middle of the geometry
7380
bottom = bottom.tile(rep, axis=0).tile(rep, axis=1)
7481
top = top.tile(rep, axis=0).tile(rep, axis=1)
75-
tvec = rep * (honeycomb.cell[0] + honeycomb.cell[1]) / 2
82+
tvec = rep * (ref_cell[0] + ref_cell[1]) / 2
7683
bottom = bottom.move(-tvec)
7784
top = top.move(-tvec)
7885

7986
# Set new lattice vectors
80-
bottom.cell[0] = n * honeycomb.cell[0] + m * honeycomb.cell[1]
81-
bottom.cell[1] = -m * honeycomb.cell[0] + (n + m) * honeycomb.cell[1]
87+
bottom.cell[0] = n * ref_cell[0] + m * ref_cell[1]
88+
bottom.cell[1] = -m * ref_cell[0] + (n + m) * ref_cell[1]
8289

8390
# Rotate top layer around A atom in bottom layer
8491
top = top.rotate(theta, [0, 0, 1])
@@ -96,16 +103,19 @@ def bilayer(bond=1.42, bottom_atom=None, top_atom=None, stacking='AB', twist=(0,
96103

97104
if rep > 1:
98105
# Remove atoms outside cell
99-
center = 1e-3 * np.array([1, 1, 1])
100-
cell_box = Cuboid(1.0 * bilayer.cell[:], center=center)
106+
cell_box = Cuboid(bilayer.cell)
107+
cell_box.set_origo([-0.0001] * 3)
101108
inside_idx = cell_box.within_index(bilayer.xyz)
102109
bilayer = bilayer.sub(inside_idx)
103110

104111
# Rotate whole cell
105112
vec = bilayer.cell[0] + bilayer.cell[1]
106-
vec_costh = np.dot([1, 0, 0], vec) / np.dot(vec, vec) ** .5
107-
vec_th = np.arccos(vec_costh) * 360 / (2 * np.pi)
113+
vec_costh = vec[0] / vec.dot(vec) ** 0.5
114+
vec_th = np.arccos(vec_costh) * 180 / np.pi
108115
bilayer = bilayer.rotate(vec_th, [0, 0, 1])
116+
else:
117+
# Shift back
118+
bilayer = bilayer.move(tvec)
109119

110120
# Sanity check
111121
assert len(bilayer) == natoms

sisl/geom/tests/test_geom.py

Lines changed: 36 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,39 @@
77
import numpy as np
88

99

10-
class Test:
11-
12-
def test_basis(self):
13-
a = sc(2.52, Atom['Fe'])
14-
a = bcc(2.52, Atom['Fe'])
15-
a = bcc(2.52, Atom['Fe'], orthogonal=True)
16-
a = fcc(2.52, Atom['Au'])
17-
a = fcc(2.52, Atom['Au'], orthogonal=True)
18-
a = hcp(2.52, Atom['Au'])
19-
a = hcp(2.52, Atom['Au'], orthogonal=True)
20-
21-
def test_flat(self):
22-
a = graphene()
23-
a = graphene(atom='C')
24-
a = graphene(orthogonal=True)
25-
26-
def test_nanotube(self):
27-
a = nanotube(1.42)
28-
a = nanotube(1.42, chirality=(3, 5))
29-
a = nanotube(1.42, chirality=(6, -3))
30-
31-
def test_diamond(self):
32-
a = diamond()
33-
34-
def test_bilayer(self):
35-
a = bilayer(1.42)
36-
a = bilayer(1.42, stacking='AA')
37-
for m in range(7):
38-
a = bilayer(1.42, twist=(m, m + 1))
39-
a = bilayer(1.42, twist=(6, 7), layer='bottom')
40-
a = bilayer(1.42, twist=(6, 7), layer='TOP')
41-
a = bilayer(1.42, bottom_atom=(Atom['B'], Atom['N']), twist=(6, 7))
42-
a = bilayer(1.42, top_atom=(Atom(5), Atom(7)), twist=(6, 7))
43-
a, th = bilayer(1.42, twist=(6, 7), ret_angle=True)
10+
def test_basis():
11+
a = sc(2.52, Atom['Fe'])
12+
a = bcc(2.52, Atom['Fe'])
13+
a = bcc(2.52, Atom['Fe'], orthogonal=True)
14+
a = fcc(2.52, Atom['Au'])
15+
a = fcc(2.52, Atom['Au'], orthogonal=True)
16+
a = hcp(2.52, Atom['Au'])
17+
a = hcp(2.52, Atom['Au'], orthogonal=True)
18+
19+
20+
def test_flat():
21+
a = graphene()
22+
a = graphene(atom='C')
23+
a = graphene(orthogonal=True)
24+
25+
26+
def test_nanotube():
27+
a = nanotube(1.42)
28+
a = nanotube(1.42, chirality=(3, 5))
29+
a = nanotube(1.42, chirality=(6, -3))
30+
31+
32+
def test_diamond():
33+
a = diamond()
34+
35+
36+
def test_bilayer():
37+
a = bilayer(1.42)
38+
a = bilayer(1.42, stacking='AA')
39+
for m in range(7):
40+
a = bilayer(1.42, twist=(m, m + 1))
41+
a = bilayer(1.42, twist=(6, 7), layer='bottom')
42+
a = bilayer(1.42, twist=(6, 7), layer='TOP')
43+
a = bilayer(1.42, bottom_atom=(Atom['B'], Atom['N']), twist=(6, 7))
44+
a = bilayer(1.42, top_atom=(Atom(5), Atom(7)), twist=(6, 7))
45+
a, th = bilayer(1.42, twist=(6, 7), ret_angle=True)

0 commit comments

Comments
 (0)