def test_init(): lat1d = pb.Lattice(1) assert len(lat1d.vectors) == 1 assert pytest.fuzzy_equal(lat1d.vectors[0], [1, 0, 0]) lat2d = pb.Lattice([1, 0], [0, 1]) assert len(lat2d.vectors) == 2 assert pytest.fuzzy_equal(lat2d.vectors[0], [1, 0, 0]) assert pytest.fuzzy_equal(lat2d.vectors[1], [0, 1, 0]) lat3d = pb.Lattice([1, 0, 0], [0, 1, 0], [0, 0, 1]) assert len(lat3d.vectors) == 3 assert pytest.fuzzy_equal(lat3d.vectors[0], [1, 0, 0]) assert pytest.fuzzy_equal(lat3d.vectors[1], [0, 1, 0]) assert pytest.fuzzy_equal(lat3d.vectors[2], [0, 0, 1])
def monolayer_4atom(onsite=(0, 0)): """Nearest-neighbor with 4 atoms per unit cell: square lattice instead of oblique Parameters ---------- onsite : Tuple[float, float] Onsite energy for sublattices A and B. """ from .constants import a_cc, a, t lat = pb.Lattice(a1=[a, 0], a2=[0, 3*a_cc]) lat.add_sublattices(('A', [ 0, -a_cc/2], onsite[0]), ('B', [ 0, a_cc/2], onsite[1])) lat.add_aliases(('A2', 'A', [a / 2, a_cc]), ('B2', 'B', [a / 2, 2 * a_cc])) lat.add_hoppings( # inside the unit sell ([0, 0], 'A', 'B', t), ([0, 0], 'B', 'A2', t), ([0, 0], 'A2', 'B2', t), # between neighbouring unit cells ([-1, -1], 'A', 'B2', t), ([ 0, -1], 'A', 'B2', t), ([-1, 0], 'B', 'A2', t), ) lat.min_neighbors = 2 return lat
def bilayer_graphene(): """Bilayer lattice in the AB-stacked form (Bernal-stacked) This is the simplest model with just a single intralayer and a single interlayer hopping. """ a = 0.24595 # [nm] unit cell length a_cc = 0.142 # [nm] carbon-carbon distance c0 = 0.335 # [nm] interlayer spacing lat = pb.Lattice(a1=[a / 2, a / 2 * sqrt(3)], a2=[a / 2, -a / 2 * sqrt(3)]) lat.add_sublattices(('A1', [0, -a_cc / 2, 0]), ('B1', [0, a_cc / 2, 0]), ('A2', [0, a_cc / 2, -c0]), ('B2', [0, 3 * a_cc / 2, -c0])) lat.register_hopping_energies({ 'gamma0': -2.8, # [eV] intralayer 'gamma1': -0.4, # [eV] interlayer }) lat.add_hoppings( # layer 1 ([0, 0], 'A1', 'B1', 'gamma0'), ([0, 1], 'A1', 'B1', 'gamma0'), ([-1, 0], 'A1', 'B1', 'gamma0'), # layer 2 ([0, 0], 'A2', 'B2', 'gamma0'), ([0, 1], 'A2', 'B2', 'gamma0'), ([-1, 0], 'A2', 'B2', 'gamma0'), # interlayer ([0, 0], 'B1', 'A2', 'gamma1')) return lat
def benzene(): """Return the lattice specification for benzene """ N = 1 # number of rings a = 0.2462 # [nm] site-site distance al = (N + 1) * a * sqrt(3) # [nm] unit cell length t = -1 # [eV] nearest neighbour hopping #t2 = 0.2 # create a lattice with 2 primitive vectors lat = pb.Lattice(a1=[al, 0], a2=[0, 3 * a]) lat.add_sublattices( # name and position ('C1', [-0.5 * a * sqrt(3), 0.5 * a], 0), ('C2', [-0.5 * a * sqrt(3), -0.5 * a], 0), ('C3', [0, a], 0), ('C4', [0, -a], 0), ('C5', [0.5 * a * sqrt(3), 0.5 * a], 0), ('C6', [0.5 * a * sqrt(3), -0.5 * a], 0)) lat.add_hoppings( # inside the main cell ([0, 0], 'C1', 'C2', t), ([0, 0], 'C1', 'C3', t), ([0, 0], 'C3', 'C5', t), ([0, 0], 'C5', 'C6', t), ([0, 0], 'C4', 'C6', t), ([0, 0], 'C2', 'C4', t) # between neighboring cells # ([1, -1], 'A', 'B', t), ) return lat
def graphene_initial(onsite=(0, 0)): theta = np.pi / 3 a1 = np.array([1 + np.cos(theta), np.sin(theta)]) a2 = np.array([0, 2 * np.sin(theta)]) lat = pb.Lattice(a1=a1, a2=a2) lat.add_sublattices( # name, position, and onsite potential ('A', [0, 0], onsite[0]), ('B', [1, 0], onsite[1])) lat.add_hoppings( # inside the main cell, between which atoms, and the value ([0, 0], 'A', 'B', -1), # between neighboring cells, between which atoms, and the value ([-1, 0], 'A', 'B', -1 / EnergyScale), ([-1, 1], 'A', 'B', -1 / EnergyScale)) node0 = [[+0, +0], 'A'] node1 = [[+0, +0], 'B'] node2 = [[-1, +0], 'B'] node3 = [[-1, +1], 'B'] struc_disorder_one = kite.StructuralDisorder(lat, position=[[32, 32]]) struc_disorder_one.add_structural_disorder( (*node0, *node1, timp), (*node0, *node2, timp), (*node0, *node3, timp), (*node0, limp)) return lat, [struc_disorder_one]
def haldane(): """Return the lattice specification for Haldane model""" a = 0.24595 # [nm] unit cell length a_cc = 0.142 # [nm] carbon-carbon distance t = -1 t2 = t / 10 m = 0 # create a lattice with 2 primitive vectors lat = pb.Lattice(a1=[a, 0], a2=[a / 2, a / 2 * sqrt(3)]) lat.add_sublattices( # name and position ('A', [0, -a_cc / 2], -m), ('B', [0, a_cc / 2], m)) lat.add_hoppings( # inside the main cell ([0, 0], 'A', 'B', t), # between neighboring cells ([1, -1], 'A', 'B', t), ([0, -1], 'A', 'B', t), ([1, 0], 'A', 'A', t2 * 1j), ([0, -1], 'A', 'A', t2 * 1j), ([-1, 1], 'A', 'A', t2 * 1j), ([1, 0], 'B', 'B', t2 * -1j), ([0, -1], 'B', 'B', t2 * -1j), ([-1, 1], 'B', 'B', t2 * -1j)) return lat
def graphene(onsite=(0, 0)): """Make a honeycomb lattice with nearest neighbor hopping Parameters ---------- onsite : tuple or list Onsite energy at different sublattices. """ theta = np.pi / 3 t = 2.8 # eV a1 = np.array([1, 0, 0] ) a2 = np.array([0, 1, 0]) a3 = np.array([0, 0, 1]) lat = pb.Lattice( a1=a1, a2=a2, a3=a3 ) lat.add_sublattices( # name, position, and onsite potential ('A', [0, 0, 0], onsite[0]) ) lat.add_hoppings( ([1, 0, 0], 'A', 'A', - t), ([0, 1, 0], 'A', 'A', - t), ([0, 0, 1], 'A', 'A', - t) ) return lat
def test_brillouin_zone(): from math import pi, sqrt lat = pb.Lattice(a1=1) assert pytest.fuzzy_equal(lat.brillouin_zone(), [-pi, pi]) lat = pb.Lattice(a1=[0, 1], a2=[0.5, 0.5]) assert pytest.fuzzy_equal( lat.brillouin_zone(), [[0, -2 * pi], [2 * pi, 0], [0, 2 * pi], [-2 * pi, 0]]) # Identical lattices represented using acute and obtuse angles between primitive vectors acute = pb.Lattice(a1=[1, 0], a2=[1 / 2, 1 / 2 * sqrt(3)]) obtuse = pb.Lattice(a1=[1 / 2, 1 / 2 * sqrt(3)], a2=[1 / 2, -1 / 2 * sqrt(3)]) assert pytest.fuzzy_equal(acute.brillouin_zone(), obtuse.brillouin_zone())
def monolayer_alt(onsite=(0, 0)): """Nearest-neighbor lattice with alternative lattice vectors This lattice is mainly here to demonstrate specifying hoppings in matrix form. Parameters ---------- onsite : Tuple[float, float] Onsite energy for sublattices A and B. """ from math import sqrt from .constants import a_cc, a, t lat = pb.Lattice( a1=[a / 2, a / 2 * sqrt(3)], a2=[-a / 2, a / 2 * sqrt(3)], ) lat.add_sublattices(('A', [0, 0], onsite[0]), ('B', [0, a_cc], onsite[1])) # matrix hopping specification r0 = [0, 0] r1 = [0, -1] r2 = [-1, 0] tr0 = [[0, t], [t, 0]] tr1 = [[0, t], [0, 0]] tr2 = [[0, t], [0, 0]] lat.add_hopping_matrices([r0, tr0], [r1, tr1], [r2, tr2]) lat.min_neighbors = 2 return lat
def honeycomb_lattice(onsite=(0, 0)): """Make a honeycomb lattice with nearest neighbor hopping Parameters ---------- onsite : tuple or list Onsite energy at different sublattices. """ "" # define lattice vectors theta = np.pi / 3 a1 = np.array([1 + np.cos(theta), np.sin(theta)]) a2 = np.array([0, 2 * np.sin(theta)]) # create a lattice with 2 primitive vectors lat = pb.Lattice(a1=a1, a2=a2) # Add sublattices lat.add_sublattices( # name, position, and onsite potential ('A', [0, 0], onsite[0]), ('B', [1, 0], onsite[1])) # Add hoppings lat.add_hoppings( # inside the main cell, between which atoms, and the value ([+0, +0], 'A', 'B', -1), # between neighboring cells, between which atoms, and the value ([-1, +0], 'A', 'B', -1), ([-1, +1], 'A', 'B', -1)) return lat
def graphene_initial(onsite=(0,0)): theta = np.pi / 3 a1 = np.array([2 * np.sin(theta), 0]) a2 = np.array([np.sin(theta), 1 + np.cos(theta)]) # create a lattice with 2 primitive vectors lat = pb.Lattice( a1=a1, a2=a2 ) # Add sublattices lat.add_sublattices( # name, position, and onsite potential ('A', [0, 0], onsite[0]), ('B', [0, 1], onsite[1]) ) # Add hoppings lat.add_hoppings( # inside the main cell, between which atoms, and the value ([0, 0], 'A', 'B', - 1), # between neighboring cells, between which atoms, and the value ([0, -1], 'A', 'B', - 1), ([1, -1], 'A', 'B', - 1) ) return lat
def graphene(onsite=(0, 0), nearest_neighbors=1): """Make a honeycomb lattice with nearest neighbor hopping Parameters ---------- onsite : tuple or list Onsite energy at different sublattices. """ theta = np.pi / 3 a1 = np.array([1 + np.cos(theta), np.sin(theta)]) a2 = np.array([0, 2 * np.sin(theta)]) lat = pb.Lattice(a1=a1, a2=a2) lat.add_sublattices( # name, position, and onsite potential ('A', [0, 0], onsite[0]), ('B', [1, 0], onsite[1])) lat.add_hoppings(([0, 0], 'A', 'B', -t), ([-1, 0], 'A', 'B', -t), ([-1, 1], 'A', 'B', -t)) if nearest_neighbors >= 2: lat.add_hoppings( ([0, -1], 'A', 'A', t_nn), ([0, -1], 'B', 'B', t_nn), ([1, -1], 'A', 'A', t_nn), ([1, -1], 'B', 'B', t_nn), ([1, 0], 'A', 'A', t_nn), ([1, 0], 'B', 'B', t_nn), ) return lat
def bilayer(gamma3=False, gamma4=False, onsite=(0, 0, 0, 0)): """Bilayer lattice in the AB-stacked form (Bernal-stacked) * :math:`\gamma_0` is the single-layer hopping within the top layer (A1/B1) and bottom layer (A2/B2) * :math:`\gamma_1` is the inter-layer hopping between B1 and A2 (where atom B1 lies directly over A2) * Hoppings :math:`\gamma_3` and :math:`\gamma_4` are optional (see parameters) Parameters ---------- gamma3, gamma4 : bool Enable :math:`\gamma_3` and/or :math:`\gamma_4` hoppings. By default, only :math:`\gamma_0` and :math:`\gamma_1` are active. onsite : Tuple[float, float, float, float] Onsite energy for A1, B1, A2, B2 """ from math import sqrt from .constants import a_cc, a, t lat = pb.Lattice(a1=[a / 2, a / 2 * sqrt(3)], a2=[-a / 2, a / 2 * sqrt(3)]) c0 = 0.335 # [nm] interlayer spacing lat.add_sublattices(('A1', [0, -a_cc / 2, 0], onsite[0]), ('B1', [0, a_cc / 2, 0], onsite[1]), ('A2', [0, a_cc / 2, -c0], onsite[2]), ('B2', [0, 3 * a_cc / 2, -c0], onsite[3])) lat.register_hopping_energies({ 'gamma0': t, 'gamma1': -0.4, 'gamma3': -0.3, 'gamma4': -0.04 }) lat.add_hoppings( # layer 1 ([0, 0], 'A1', 'B1', 'gamma0'), ([0, -1], 'A1', 'B1', 'gamma0'), ([-1, 0], 'A1', 'B1', 'gamma0'), # layer 2 ([0, 0], 'A2', 'B2', 'gamma0'), ([0, -1], 'A2', 'B2', 'gamma0'), ([-1, 0], 'A2', 'B2', 'gamma0'), # interlayer ([0, 0], 'B1', 'A2', 'gamma1')) if gamma3: lat.add_hoppings(([0, 1], 'B2', 'A1', 'gamma3'), ([1, 0], 'B2', 'A1', 'gamma3'), ([1, 1], 'B2', 'A1', 'gamma3')) if gamma4: lat.add_hoppings(([0, 0], 'A2', 'A1', 'gamma4'), ([0, 1], 'A2', 'A1', 'gamma4'), ([1, 0], 'A2', 'A1', 'gamma4')) lat.min_neighbors = 2 return lat
def square_lattice(d=1, t=1): lat = pb.Lattice(a1=[d, 0], a2=[0, d]) lat.add_sublattices(('A', [0, 0])) lat.add_hoppings( ([0, 1], 'A', 'A', -t), ([1, 0], 'A', 'A', -t), ) return lat
def square_lattice(onsite=0): a1 = np.array([1, 0]) a2 = np.array([0, 1]) lat = pb.Lattice(a1=a1, a2=a2) lat.add_sublattices(('A', [0, 0], onsite)) lat.add_hoppings(([1, 0], 'A', 'A', -1), ([0, 1], 'A', 'A', -1)) return lat
def trestle(a=0.2, t1=0.8 + 0.6j, t2=2): """A more complicated 1D lattice with 2 sublattices""" lat = pb.Lattice(1.3 * a) lat.add_sublattices(('A', [0, 0], 0), ('B', [a / 2, a], 0)) lat.add_hoppings((0, 'A', 'B', t1), (1, 'A', 'B', t1), (1, 'A', 'A', t2), (1, 'B', 'B', t2)) lat.min_neighbors = 2 return lat
def mock_lattice(): a_cc, a, t = 1, 1.73, 1 lat = pb.Lattice([a, 0], [0.5 * a, 0.866 * a]) lat.add_sublattices(['a', (0, -a_cc / 2)], ['b', (0, a_cc / 2)]) lat.add_hoppings([(0, 0), 'a', 'b', t], [(1, -1), 'a', 'b', t], [(0, -1), 'a', 'b', t]) lat.min_neighbors = 2 return lat
def phosphorene_4band(): """Monolayer phosphorene lattice using the four-band model""" a = 0.222 ax = 0.438 ay = 0.332 theta = 96.79 * (pi / 180) phi = 103.69 * (pi / 180) lat = pb.Lattice(a1=[ax, 0], a2=[0, ay]) h = a * sin(phi - pi / 2) s = 0.5 * ax - a * cos(theta / 2) lat.add_sublattices(('A', [-ax / 4 - s / 2, -ay / 4, h], 0), ('B', [-ax / 4 + s / 2, -ay / 4, 0], 0), ('C', [ax / 4 - s / 2, ay / 4, 0], 0), ('D', [ax / 4 + s / 2, ay / 4, h], 0)) lat.register_hopping_energies({ 't1': -1.22, 't2': 3.665, 't3': -0.205, 't4': -0.105, 't5': -0.055 }) lat.add_hoppings( # t1 ([-1, 0], 'A', 'D', 't1'), ([-1, -1], 'A', 'D', 't1'), ([0, 0], 'B', 'C', 't1'), ([0, -1], 'B', 'C', 't1'), # t2 ([0, 0], 'A', 'B', 't2'), ([0, 0], 'C', 'D', 't2'), # t3 ([0, 0], 'A', 'D', 't3'), ([0, -1], 'A', 'D', 't3'), ([1, 1], 'C', 'B', 't3'), ([1, 0], 'C', 'B', 't3'), # t4 ([0, 0], 'A', 'C', 't4'), ([0, -1], 'A', 'C', 't4'), ([-1, 0], 'A', 'C', 't4'), ([-1, -1], 'A', 'C', 't4'), ([0, 0], 'B', 'D', 't4'), ([0, -1], 'B', 'D', 't4'), ([-1, 0], 'B', 'D', 't4'), ([-1, -1], 'B', 'D', 't4'), # t5 ([-1, 0], 'A', 'B', 't5'), ([0, 1], 'A', 'B', 't5'), ([0, -1], 'A', 'B', 't5'), ([-1, 0], 'C', 'D', 't5'), ([0, 1], 'C', 'D', 't5'), ([0, -1], 'C', 'D', 't5'), ) return lat
def monolayer(nearest_neighbors=1, onsite=(0, 0), **kwargs): """Monolayer graphene lattice up to `nearest_neighbors` hoppings Parameters ---------- nearest_neighbors : int Number of nearest neighbors to consider. onsite : Tuple[float, float] Onsite energy for sublattices A and B. **kwargs Specify the hopping parameters `t`, `t_nn` and `t_nnn`. If not given, the default values from :mod:`.graphene.constants` will be used. """ from math import sqrt from .constants import a_cc, a, t, t_nn lat = pb.Lattice(a1=[a, 0], a2=[a / 2, a / 2 * sqrt(3)]) # The next-nearest hoppings shift the Dirac point away from zero energy. # This will push it back to zero for consistency with the first-nearest model. onsite_offset = 0 if nearest_neighbors < 2 else 3 * kwargs.get( 't_nn', t_nn) lat.add_sublattices(('A', [0, -a_cc / 2], onsite[0] + onsite_offset), ('B', [0, a_cc / 2], onsite[1] + onsite_offset)) lat.register_hopping_energies({ 't': kwargs.get('t', t), 't_nn': kwargs.get('t_nn', t_nn), 't_nnn': kwargs.get('t_nnn', 0.05), }) lat.add_hoppings(([0, 0], 'A', 'B', 't'), ([1, -1], 'A', 'B', 't'), ([0, -1], 'A', 'B', 't')) if nearest_neighbors >= 2: lat.add_hoppings( ([0, -1], 'A', 'A', 't_nn'), ([0, -1], 'B', 'B', 't_nn'), ([1, -1], 'A', 'A', 't_nn'), ([1, -1], 'B', 'B', 't_nn'), ([1, 0], 'A', 'A', 't_nn'), ([1, 0], 'B', 'B', 't_nn'), ) if nearest_neighbors >= 3: lat.add_hoppings( [(1, -2), 'A', 'B', 't_nnn'], [(1, 0), 'A', 'B', 't_nnn'], [(-1, 0), 'A', 'B', 't_nnn'], ) if nearest_neighbors >= 4: raise RuntimeError("No more") lat.min_neighbors = 2 return lat
def anthracene(): """Return the lattice specification for anthracene (3 rings) """ N=3 # number of rings a = 0.2462 # [nm] site-site distance al = 1.5*N*a*sqrt(3) # [nm] unit cell length t = -1 # [eV] nearest neighbour hopping #t2 = 0.2 # create a lattice with 2 primitive vectors lat = pb.Lattice( a1=[al, 0], a2=[al/2, al/2 * sqrt(3)] ) lat.add_sublattices( # name and position ('C1', [-0.5*a*sqrt(3),0.5*a],0), ('C2', [-0.5*a*sqrt(3),-0.5*a],0), ('C3', [0, a], 0), ('C4', [0, -a], 0), ('C5', [0.5 * a * sqrt(3), 0.5 * a], 0), ('C6', [0.5 * a * sqrt(3), -0.5 * a], 0), ('C7', [a * sqrt(3), a], 0), ('C8', [a * sqrt(3), -a], 0), ('C9', [1.5 * a * sqrt(3), 0.5 * a], 0), ('C10',[1.5 * a * sqrt(3), -0.5 * a], 0), ('C11', [2*a * sqrt(3), a], 0), ('C12', [2*a * sqrt(3), -a], 0), ('C13', [2.5 * a * sqrt(3), 0.5 * a], 0), ('C14', [2.5 * a * sqrt(3), -0.5 * a], 0) ) lat.add_hoppings( # inside the main cell ([0, 0], 'C1', 'C2', t), ([0, 0], 'C1', 'C3', t), ([0, 0], 'C3', 'C5', t), ([0, 0], 'C2', 'C4', t), ([0, 0], 'C4', 'C6', t), ([0, 0], 'C5', 'C6', t), ([0, 0], 'C5', 'C7', t), ([0, 0], 'C7', 'C9', t), ([0, 0], 'C6', 'C8', t), ([0, 0], 'C8', 'C10', t), ([0, 0], 'C9', 'C10', t), ([0, 0], 'C9', 'C11', t), ([0, 0], 'C11', 'C13', t), ([0, 0], 'C10', 'C12', t), ([0, 0], 'C12', 'C14', t), ([0, 0], 'C13', 'C14', t) # between neighboring cells # ([1, -1], 'A', 'B', t), ) return lat
def monolayer_graphene(a, t): lat = pb.Lattice(a1=[3 * a / 2, sqrt(3) * a / 2], a2=[3 * a / 2, -sqrt(3) * a / 2]) lat.add_sublattices(('a', [0, 0]), ('b', [a / 2, sqrt(3) * a / 2])) lat.add_hoppings(([0, 0], 'a', 'b', t), ([-1, 1], 'a', 'b', t), ([-1, 0], 'a', 'b', t)) return lat
def lattice(): lat = pb.Lattice([1]) lat.add_sublattices(("A", [0], [[1, 3j], [0, 2]])) lat.register_hopping_energies({ "t22": [[0, 1], [2, 3]], "t11": 1, # incompatible hopping - it's never used so it shouldn't raise any errors }) lat.add_hoppings(([1], "A", "A", "t22")) return lat
def lattice(): lat = pb.Lattice([1, 0], [0, 1]) lat.add_sublattices(("A", [0, 0], [0, 0, 0, 0])) lat.register_hopping_energies({ "t44": [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]] }) lat.add_hoppings(([1, 0], "A", "A", "t44"), ([0, 1], "A", "A", "t44")) return lat
def checkerboard_lattice(delta, t): lat = pb.Lattice(a1=[1, 0], a2=[0, 1]) lat.add_sublattices(('A', [0, 0], -delta), ('B', [1 / 2, 1 / 2], delta)) lat.add_hoppings( ([0, 0], 'A', 'B', t), ([0, -1], 'A', 'B', t), ([-1, 0], 'A', 'B', t), ([-1, -1], 'A', 'B', t), ) return lat
def test_builder(): """Builder pattern methods""" lattice = pb.Lattice([1, 0], [0, 1]) copy = lattice.with_offset([0, 0.5]) assert pytest.fuzzy_equal(copy.offset, [0, 0.5, 0]) assert pytest.fuzzy_equal(lattice.offset, [0, 0, 0]) copy = lattice.with_min_neighbors(5) assert copy.min_neighbors == 5 assert lattice.min_neighbors == 1
def hbn_monolayer(): """Create a lattice of monolayer hBN """ a = math.sqrt(3) * a_bn lat = pb.Lattice(a1=[a / 2, a / 2 * math.sqrt(3)], a2=[-a / 2, a / 2 * math.sqrt(3)]) lat.add_sublattices(('Br', [0, -a_bn, -c0], vb), ('N', [0, 0, -c0], vn)) lat.min_neighbors = 2 # no need for hoppings lattice is used only to generate coordinates return lat
def multi_orbital_lattice(): lat = pb.Lattice([1, 0], [0, 1]) tau_z = np.array([[1, 0], [0, -1]]) tau_x = np.array([[0, 1], [1, 0]]) lat.add_sublattices(("A", [0, 0], tau_z + 2 * tau_x), ("B", [0, 0.1], 0.5), ("C", [0, 0.2], [1, 2, 3])) lat.add_hoppings(([0, -1], "A", "A", 3 * tau_z), ([1, 0], "A", "A", 3 * tau_z), ([0, 0], "B", "C", [[2, 3, 4]])) return lat
def three_band_lattice(): """MoS2 lattice using the three-band model""" # TODO: this is a proof of concept for `Lattice.add_hopping_matrices()` # TODO: still needs to be checked for accuracy # lattice constant a = 0.319 # [nm] # onsite energies eps0 = 1.046 eps2 = 2.104 # hoppings t0 = -0.184 t1 = 0.401 t2 = 0.507 t11 = 0.218 t12 = 0.338 t22 = 0.057 # convenient constant rt3 = math.sqrt(3) lat = pb.Lattice(a1=[a, 0], a2=[0.5 * a, 0.5 * rt3 * a]) lat.add_sublattices(('s1', [0, 0], eps0), ('s2', [0, 0], eps2), ('s3', [0, 0], eps2)) r1 = [1, 0] r2 = [1, -1] r3 = [0, -1] t_mat1 = [[t0, t1, t2], [-t1, t11, t12], [t2, -t12, t22]] t_mat2 = [[t0, 0.5 * t1 - 0.5 * rt3 * t2, -0.5 * rt3 * t1 - 0.5 * t2], [ -0.5 * t1 - 0.5 * rt3 * t2, 0.25 * t11 + 0.75 * t22, 0.25 * rt3 * (t22 - t11) - t12 ], [ 0.5 * rt3 * t1 - 0.5 * t2, 0.25 * rt3 * (t22 - t11) + t12, 0.75 * t11 + 0.25 * t22 ]] t_mat3 = [[t0, -0.5 * t1 + 0.5 * rt3 * t2, -0.5 * rt3 * t1 - 0.5 * t2], [ 0.5 * t1 + 0.5 * rt3 * t2, 0.25 * t11 + 0.75 * t22, 0.25 * rt3 * (t11 - t22) + t12 ], [ 0.5 * rt3 * t1 - 0.5 * t2, -0.25 * rt3 * (t11 + t22) - t12, 0.75 * t11 + 0.25 * t22 ]] lat.add_hopping_matrices([r1, t_mat1], [r2, t_mat2], [r3, t_mat3]) lat.min_neighbors = 2 return lat
def test_add_hopping(capsys): lat = pb.Lattice([1, 0], [0, 1]) lat.add_sublattices(("A", [0.0, 0.5]), ("B", [0.5, 0.0])) lat.add_hoppings(([0, 0], "A", "B", 1), ([1, -1], "A", "B", 1), ([0, -1], "A", "B", 2)) assert lat.nhop == 2 assert lat.hoppings["__anonymous__0"].family_id == 0 assert lat.hoppings["__anonymous__0"].energy == 1 assert lat.hoppings["__anonymous__1"].family_id == 1 assert lat.hoppings["__anonymous__1"].energy == 2 lat.add_hoppings(([0, 1], "A", "B", 1)) assert lat.nhop == 2 lat.add_hoppings(([1, 0], "A", "B", 3)) assert lat.nhop == 3 with pytest.raises(RuntimeError) as excinfo: lat.add_one_hopping([0, 0], "A", "B", 1) assert "hopping already exists" in str(excinfo.value) with pytest.raises(RuntimeError) as excinfo: lat.add_one_hopping([0, 0], "A", "A", 1) assert "Don't define onsite energy here" in str(excinfo.value) with pytest.raises(IndexError) as excinfo: lat.add_one_hopping([0, 0], "C", "A", 1) assert "There is no sublattice named 'C'" in str(excinfo.value) lat.register_hopping_energies({"t_nn": 0.1, "t_nnn": 0.01}) assert lat.nhop == 5 assert lat.hoppings["t_nn"].energy == 0.1 assert lat.hoppings["t_nnn"].energy == 0.01 lat.add_one_hopping([0, 1], "A", "A", "t_nn") with pytest.raises(RuntimeError) as excinfo: lat.register_hopping_energies({"": 0.0}) assert "Hopping name can't be blank" in str(excinfo.value) with pytest.raises(RuntimeError) as excinfo: lat.register_hopping_energies({"t_nn": 0.2}) assert "Hopping 't_nn' already exists" in str(excinfo.value) with pytest.raises(IndexError) as excinfo: lat.add_one_hopping((0, 1), "A", "A", "tt") assert "There is no hopping named 'tt'" in str(excinfo.value) with pytest.warns(LoudDeprecationWarning): assert lat("t_nn") == "t_nn" capsys.readouterr()
def monolayer_4band(num_hoppings=4): """Monolayer phosphorene lattice using the four-band model Parameters ---------- num_hoppings : int Number of hopping terms to consider: from t2 to t5. """ a = 0.222 # nm ax = 0.438 # nm ay = 0.332 # nm theta = 96.79 * (pi / 180) phi = 103.69 * (pi / 180) lat = pb.Lattice(a1=[ax, 0], a2=[0, ay]) h = a * sin(phi - pi / 2) s = 0.5 * ax - a * cos(theta / 2) lat.add_sublattices( ('A', [-s / 2, -ay / 2, h], 0), ('B', [s / 2, -ay / 2, 0], 0), ('C', [-s / 2 + ax / 2, 0, 0], 0), ('D', [s / 2 + ax / 2, 0, h], 0)) lat.register_hopping_energies({ 't1': -1.22, 't2': 3.665, 't3': -0.205, 't4': -0.105, 't5': -0.055 }) if num_hoppings < 2: raise RuntimeError("t1 and t2 must be included") elif num_hoppings > 5: raise RuntimeError("t5 is the last one") if num_hoppings >= 2: lat.add_hoppings(([-1, 0], 'A', 'D', 't1'), ([-1, -1], 'A', 'D', 't1'), ([0, 0], 'B', 'C', 't1'), ([0, -1], 'B', 'C', 't1')) lat.add_hoppings(([0, 0], 'A', 'B', 't2'), ([0, 0], 'C', 'D', 't2')) if num_hoppings >= 3: lat.add_hoppings(([0, 0], 'A', 'D', 't3'), ([0, -1], 'A', 'D', 't3'), ([1, 1], 'C', 'B', 't3'), ([1, 0], 'C', 'B', 't3')) if num_hoppings >= 4: lat.add_hoppings(([0, 0], 'A', 'C', 't4'), ([0, -1], 'A', 'C', 't4'), ([-1, 0], 'A', 'C', 't4'), ([-1, -1], 'A', 'C', 't4'), ([0, 0], 'B', 'D', 't4'), ([0, -1], 'B', 'D', 't4'), ([-1, 0], 'B', 'D', 't4'), ([-1, -1], 'B', 'D', 't4')) if num_hoppings >= 5: lat.add_hoppings(([-1, 0], 'A', 'B', 't5'), ([-1, 0], 'C', 'D', 't5')) lat.min_neighbors = 2 return lat