11import 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
0 commit comments