def test_site_state(): @pb.site_state_modifier def mod(state): return np.ones_like(state) assert np.all(mod(zero)) assert np.all(mod.apply(zero, one, one, one, one)) capture = {} @pb.site_state_modifier def check_args(state, x, y, z, sub_id, sites): capture[sub_id] = [v.copy() for v in (state, x, y, z)] capture[sub_id].append(sites.argsort_nearest([0, graphene.a_cc / 2])) return state def assert_state(name, **kwargs): state, x, y, z, nearest = capture[name] assert state.shape == kwargs["shape"] assert pytest.fuzzy_equal(state, kwargs["state"]) assert x.shape == kwargs["shape"] assert pytest.fuzzy_equal(x, kwargs["x"]) assert pytest.fuzzy_equal(y, kwargs["y"]) assert pytest.fuzzy_equal(z, kwargs["z"]) assert np.all(nearest == kwargs["nearest"]) model = build_model(check_args, pb.primitive(1, 2)) assert model.hamiltonian.dtype == np.float32 assert_state("A", shape=(2, ), state=[True] * 2, nearest=[1, 0], x=[-graphene.a / 2, 0], y=[-2 * graphene.a_cc, -graphene.a_cc / 2], z=[0, 0]) assert_state("B", shape=(2, ), state=[True] * 2, nearest=[1, 0], x=[-graphene.a / 2, 0], y=[-graphene.a_cc, graphene.a_cc / 2], z=[0, 0]) @pb.site_state_modifier def remove_site(state): state[0] = False return state model = build_model(remove_site, pb.primitive(2, 2)) assert model.system.num_sites == 6 @pb.site_state_modifier(min_neighbors=1) def remove_dangling(state, sub_id): state[sub_id == "A"] = False return state with pytest.raises(RuntimeError) as excinfo: build_model(remove_dangling, pb.primitive(3, 3)) assert "0 sites" in str(excinfo.value)
def test_multiorbital_hamiltonian(): """For multi-orbital lattices the Hamiltonian size is larger than the number of sites""" 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 model = pb.Model(lattice(), pb.primitive(3)) h = model.hamiltonian.toarray() assert model.system.num_sites == 3 assert h.shape[0] == 6 assert pytest.fuzzy_equal(h, h.T.conjugate()) assert pytest.fuzzy_equal(h[:2, :2], h[-2:, -2:]) assert pytest.fuzzy_equal(h[:2, :2], [[1, 3j], [-3j, 2]]) assert pytest.fuzzy_equal(h[:2, 2:4], [[0, 1], [2, 3]]) @pb.onsite_energy_modifier def onsite(energy, x, sub_id): return 3 * energy + sub_id.eye * 0 * x @pb.hopping_energy_modifier def hopping(energy): return 2 * energy model = pb.Model(lattice(), pb.primitive(3), onsite, hopping) h = model.hamiltonian.toarray() assert model.system.num_sites == 3 assert h.shape[0] == 6 assert pytest.fuzzy_equal(h, h.T.conjugate()) assert pytest.fuzzy_equal(h[:2, :2], h[-2:, -2:]) assert pytest.fuzzy_equal(h[:2, :2], [[3, 9j], [-9j, 6]]) assert pytest.fuzzy_equal(h[:2, 2:4], [[0, 2], [4, 6]]) assert pytest.fuzzy_equal(h[2:4, 4:6], [[0, 2], [4, 6]]) def lattice_with_zero_diagonal(): lat = pb.Lattice([1]) lat.add_sublattices(("A", [0], [[0, 3j], [0, 0]])) return lat model = pb.Model(lattice_with_zero_diagonal(), pb.primitive(3)) h = model.hamiltonian.toarray() assert model.system.num_sites == 3 assert h.shape[0] == 6 assert pytest.fuzzy_equal(h, h.T.conjugate()) assert pytest.fuzzy_equal(h[:2, :2], h[-2:, -2:]) assert pytest.fuzzy_equal(h[:2, :2], [[0, 3j], [-3j, 0]])
def test_primitive(): """The primitive shape can be positioned using the lattice origin""" model = pb.Model(graphene.monolayer(), pb.primitive(2, 2)) assert model.system.num_sites == 8 assert np.isclose(model.system.x.min(), -1.5 * graphene.a, rtol=1e-3) assert np.isclose(model.system.y.min(), -2 * graphene.a_cc, rtol=1e-3) model = pb.Model( graphene.monolayer().with_offset( [0.5 * graphene.a, 0.5 * graphene.a_cc]), pb.primitive(2, 2)) assert model.system.num_sites == 8 assert np.isclose(model.system.x.min(), -graphene.a, rtol=1e-3) assert np.isclose(model.system.y.min(), -1.5 * graphene.a_cc, rtol=1e-3)
def test_hopping_generator(): """Generated next-nearest hoppings should produce the same result as the builtin lattice""" from scipy.spatial import cKDTree @pb.hopping_generator("tnn_test", energy=graphene.t_nn) def next_nearest(x, y, z): pos = np.stack([x, y, z], axis=1) dmin = graphene.a * 0.95 dmax = graphene.a * 1.05 kdtree = cKDTree(pos) coo = kdtree.sparse_distance_matrix(kdtree, dmax).tocoo() idx = coo.data > dmin return coo.row[idx], coo.col[idx] @pb.onsite_energy_modifier def onsite_offset(energy): return energy + 3 * graphene.t_nn model = pb.Model(graphene.monolayer(), next_nearest, onsite_offset, graphene.hexagon_ac(1)) expected = pb.Model(graphene.monolayer(2), graphene.hexagon_ac(1)) assert pytest.fuzzy_equal(model.hamiltonian, expected.hamiltonian) @pb.hopping_generator("t_new", energy=1.0) def bad_generator(): """Different array lengths""" return [0, 1, 2], [0, 1] model = pb.Model(graphene.monolayer(), pb.primitive(3, 3), bad_generator) with pytest.raises(RuntimeError) as excinfo: model.eval() assert "the number of `from` and `to` indices must be equal" in str( excinfo.value)
def test_hopping_buffer(): """The energy passed to hopping modifiers is buffered, but users should not be aware of it""" 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 capture = {} @pb.hopping_energy_modifier def check_buffer(energy, hop_id): capture.setdefault(hop_id, []) capture[hop_id] += [energy.copy()] energy[0] = 99 return energy model = pb.Model(lattice(), pb.primitive(3000, 2), check_buffer) assert model.system.num_sites == 6000 assert model.hamiltonian.shape[0] == 24000 energies = capture["t44"] assert len(energies) >= 2 assert energies[0].shape == (6250, 4, 4) for energy in energies: assert np.argwhere(energy == 99).size == 0
def test_api(): model = pb.Model(graphene.monolayer(), pb.primitive(2, 2)) system = model.system idx = system.num_sites // 2 assert idx == system.find_nearest(system.xyz[idx]) assert idx == system.find_nearest(system.xyz[idx], 'B') assert system.find_nearest([0, 0], 'A') != system.find_nearest([0, 0], 'B') with pytest.raises(IndexError) as excinfo: system.find_nearest([0, 0], 'invalid_sublattice') assert "There is no Site named" in str(excinfo.value) assert np.allclose(system.expanded_positions.x, system.positions.x) s = pb.Model(group6_tmd.monolayer_3band("MoS2"), pb.primitive(2, 2)).system assert s.expanded_positions.x.size == s.positions.x.size * 3
def test_sites(): model = pb.Model(graphene.monolayer(), pb.primitive(2, 2)) system = model.system sites = pb.system.Sites(system.positions, system.sublattices, system.lattice) idx = system.num_sites // 2 assert idx == sites.find_nearest(system.xyz[idx]) assert idx == sites.find_nearest(system.xyz[idx], system.sublattices[idx]) assert sites.find_nearest([0, 0], 'A') != sites.find_nearest([0, 0], 'B')
def test_sites(): model = pb.Model(graphene.monolayer(), pb.primitive(2, 2)) system = model.system sites = pb.system.Sites(system.positions, system.sublattices) idx = system.num_sites // 2 assert idx == sites.find_nearest(system.xyz[idx]) assert idx == sites.find_nearest(system.xyz[idx], system.sublattices[idx]) assert sites.find_nearest([0, 0], 'A') != sites.find_nearest([0, 0], 'B')
def test_site_and_hopping_interaction(): """Add a new row of sites and connect them just like the rest of the lattice""" d = 1.0 v = 1.5 t = 1.0 def square_lattice(): lat = pb.Lattice(a1=[d, 0], a2=[0, d]) lat.add_sublattices(("A", [0, 0], v)) lat.add_hoppings(([0, 1], "A", "A", t), ([1, 0], "A", "A", t)) return lat @pb.site_generator(name="B", energy=v) def edge_sites(system): edge_atoms = system.count_neighbors() < 4 x, y, z = (v[edge_atoms] for v in system.positions) top_edge_only = np.isclose(y, y.max()) x, y, z = (v[top_edge_only] for v in (x, y, z)) y += d return x, y, z @pb.hopping_generator(name="t_edge", energy=t) def edge_hoppings(system, y): new_sites = system.sub == "B" edge_atoms = np.logical_and(system.sub != "B", system.count_neighbors() < 4) top_edge_only = np.logical_and(edge_atoms, np.isclose(y, y[edge_atoms].max())) return new_sites, top_edge_only @pb.hopping_generator(name="t_edge2", energy=t) def edge_hoppings2(system): edge_idx = np.flatnonzero(system.sub == "B") to_idx = edge_idx[1:] from_idx = edge_idx[:-1] return to_idx, from_idx model = pb.Model(square_lattice(), pb.primitive(6, 4), edge_sites, edge_hoppings, edge_hoppings2) expected = pb.Model(square_lattice(), pb.primitive(6, 5)) assert pytest.fuzzy_equal(model.hamiltonian, expected.hamiltonian)
def test_api(): model = pb.Model(graphene.monolayer(), pb.primitive(2, 2)) system = model.system idx = system.num_sites // 2 assert idx == system.find_nearest(system.xyz[idx]) assert idx == system.find_nearest(system.xyz[idx], 'B') assert system.find_nearest([0, 0], 'A') != system.find_nearest([0, 0], 'B') with pytest.raises(IndexError) as excinfo: system.find_nearest([0, 0], 'invalid_sublattice') assert "There is no sublattice" in str(excinfo.value)
def test_site_position(): @pb.site_position_modifier def mod(x, y, z): return x + 1, y + 1, z + 1 assert (one, ) * 3 == mod(zero, zero, zero) assert (one, ) * 3 == mod.apply(zero, zero, zero, one) capture = {} @pb.site_position_modifier def check_args(x, y, z, sub_id, sites): capture[sub_id] = [v.copy() for v in (x, y, z)] capture[sub_id].append(sites.argsort_nearest([0, graphene.a_cc / 2])) return x, y, z def assert_positions(name, **kwargs): x, y, z, nearest = capture[name] assert x.shape == kwargs["shape"] assert y.shape == kwargs["shape"] assert z.shape == kwargs["shape"] assert pytest.fuzzy_equal(x, kwargs["x"]) assert pytest.fuzzy_equal(y, kwargs["y"]) assert pytest.fuzzy_equal(z, kwargs["z"]) assert np.all(nearest == kwargs["nearest"]) model = build_model(check_args, pb.primitive(1, 2)) assert model.hamiltonian.dtype == np.float32 assert_positions("A", shape=(2, ), nearest=[1, 0], x=[-graphene.a / 2, 0], y=[-2 * graphene.a_cc, -graphene.a_cc / 2], z=[0, 0]) assert_positions("B", shape=(2, ), nearest=[1, 0], x=[-graphene.a / 2, 0], y=[-graphene.a_cc, graphene.a_cc / 2], z=[0, 0]) @pb.site_position_modifier def shift(x, y, z): return x + 1, y + 1, z + 1 model = build_model(check_args, shift) assert pytest.fuzzy_equal(model.system.x, [1, 1]) assert pytest.fuzzy_equal(model.system.y, [1 - graphene.a_cc / 2, 1 + graphene.a_cc / 2]) assert pytest.fuzzy_equal(model.system.z, [1, 1])
def test_multiorbital_hamiltonian(): """For multi-orbital lattices the Hamiltonian size is larger than the number of sites""" def lattice(): lat = pb.Lattice([1]) lat.add_sublattices(("A", [0], [[1, 3j], [0, 2]])) lat.register_hopping_energies({"t22": [[0, 1], [2, 3]]}) lat.add_hoppings(([1], "A", "A", "t22")) return lat model = pb.Model(lattice(), pb.primitive(3)) h = model.hamiltonian.todense() assert model.system.num_sites == 3 assert h.shape[0] == 6 assert pytest.fuzzy_equal(h, h.H) assert pytest.fuzzy_equal(h[:2, :2], h[-2:, -2:]) assert pytest.fuzzy_equal(h[:2, :2], [[1, 3j], [-3j, 2]]) assert pytest.fuzzy_equal(h[:2, 2:4], [[0, 1], [2, 3]]) @pb.onsite_energy_modifier def onsite(energy): return 3 * energy @pb.hopping_energy_modifier def hopping(energy): return 2 * energy model = pb.Model(lattice(), pb.primitive(3), onsite, hopping) h = model.hamiltonian.todense() assert model.system.num_sites == 3 assert h.shape[0] == 6 assert pytest.fuzzy_equal(h, h.H) assert pytest.fuzzy_equal(h[:2, :2], h[-2:, -2:]) assert pytest.fuzzy_equal(h[:2, :2], [[3, 9j], [-9j, 6]]) assert pytest.fuzzy_equal(h[:2, 2:4], [[0, 2], [4, 6]]) assert pytest.fuzzy_equal(h[2:4, 4:6], [[0, 2], [4, 6]])
def test_wrapper_return(): """Make sure the boost python wrapper return type conversion is working""" @pb.hopping_energy_modifier def mul(energy): """Returning a non-contiguous view will force the wrapper to create a copy""" energy = np.concatenate([energy, energy]) energy *= 3 return energy[::2] lattice = pb.Lattice([1, 0]) lattice.add_sublattices(("A", [0, 0]), ("B", [0, 0])) lattice.add_one_hopping([0], "A", "B", 1.0) model = pb.Model(lattice, mul, pb.primitive(2)) assert pytest.fuzzy_equal(model.hamiltonian.data, [3, 3, 3, 3])
def test_api(): model = pb.Model(graphene.monolayer(), pb.primitive(2, 2)) system = model.system idx = system.num_sites // 2 assert idx == system.find_nearest(system.xyz[idx]) assert idx == system.find_nearest(system.xyz[idx], system.sublattices[idx]) assert system.find_nearest([0, 0], 'A') != system.find_nearest([0, 0], 'B') invalid_sublattice = 99 with pytest.raises(KeyError) as excinfo: system.find_nearest([0, 0], invalid_sublattice) assert "There is no sublattice" in str(excinfo.value) with pytest.raises(KeyError) as excinfo: system.find_nearest([0, 0], 'invalid_sublattice') assert "There is no sublattice" in str(excinfo.value)
def mymodel(dim=20, theta=0, B=0.0): # Pauli matrices S1 = array([[0, 1], [1, 0]]) S2 = array([[0, -1j], [1j, 0]]) S3 = array([[1, 0], [0, -1]]) Tx = -S3 # Hopping in the x direction Ty = -S3 # Hopping in the y direction Tz = -S3 - 1j * S1 # Hopping in the z direction U = 5 * S3 # Onsite term # Building the lattice model lat = pb.Lattice(a1=[1, 0, 0], a2=[0, 1, 0], a3=[0, 0, 1]) lat.add_sublattices(('A', [0, 0, 0], U)) lat.add_hoppings(([1, 0, 0], 'A', 'A', Tx), ([0, 1, 0], 'A', 'A', Ty), ([0, 0, 1], 'A', 'A', Tz)) # generating the model object by combinig the lattice and the Peierls field model = pb.Model(lat, pb.primitive(a1=dim, a2=dim, a3=dim), constant_magnetic_field(B=B, theta=theta)) return model
import pytest import pybinding as pb from pybinding.repository import graphene models = { 'graphene-monolayer': [graphene.monolayer(), graphene.hexagon_ac(1)], 'graphene-monolayer-alt': [graphene.monolayer_alt(), pb.rectangle(1.6, 1.4)], 'graphene-monolayer-4atom': [graphene.monolayer_4atom()], 'graphene-monolayer-nn': [graphene.monolayer(2), pb.regular_polygon(6, 0.9)], 'graphene-monolayer-periodic-1d': [graphene.monolayer(), pb.primitive(5, 5), pb.translational_symmetry(a1=True, a2=False)], 'graphene-monolayer-periodic-1d-alt': [graphene.monolayer_4atom(), pb.rectangle(1), pb.translational_symmetry(a1=False, a2=0.6)], 'graphene-monolayer-periodic-2d': [graphene.monolayer(), pb.primitive(a1=5, a2=5), pb.translational_symmetry(a1=1, a2=1)], 'graphene-monolayer-4atom-periodic-2d': [graphene.monolayer_4atom(), pb.rectangle(1), pb.translational_symmetry(a1=0.6, a2=0.6)], 'graphene-bilayer': [graphene.bilayer(), graphene.hexagon_ac(0.6)], } @pytest.fixture(scope='module', ids=list(models.keys()), params=models.values()) def model(request): return pb.Model(*request.param) def test_api(): model = pb.Model(graphene.monolayer(), pb.primitive(2, 2)) system = model.system
import pytest import numpy as np import pybinding as pb from pybinding.repository import graphene, group6_tmd models = { 'graphene-monolayer': [graphene.monolayer(), graphene.hexagon_ac(1)], 'graphene-monolayer-alt': [graphene.monolayer_alt(), pb.rectangle(1.6, 1.4)], 'graphene-monolayer-4atom': [graphene.monolayer_4atom()], 'graphene-monolayer-nn': [graphene.monolayer(2), pb.regular_polygon(6, 0.9)], 'graphene-monolayer-periodic-1d': [graphene.monolayer(), pb.primitive(5, 5), pb.translational_symmetry(a1=True, a2=False)], 'graphene-monolayer-periodic-1d-alt': [graphene.monolayer_4atom(), pb.rectangle(1), pb.translational_symmetry(a1=False, a2=0.6)], 'graphene-monolayer-periodic-2d': [graphene.monolayer(), pb.primitive(a1=5, a2=5), pb.translational_symmetry(a1=1, a2=1)], 'graphene-monolayer-4atom-periodic-2d': [graphene.monolayer_4atom(), pb.rectangle(1), pb.translational_symmetry(a1=0.6, a2=0.6)], 'graphene-bilayer': [graphene.bilayer(), graphene.hexagon_ac(0.6)], } @pytest.fixture(scope='module', ids=list(models.keys()), params=models.values()) def model(request): return pb.Model(*request.param) def test_pickle_round_trip(model): import pickle unpickled = pickle.loads(pickle.dumps(model.system))
import numpy as np import pybinding as pb from pybinding.repository import graphene, group6_tmd models = { 'graphene-monolayer': [graphene.monolayer(), graphene.hexagon_ac(1)], 'graphene-monolayer-alt': [graphene.monolayer_alt(), pb.rectangle(1.6, 1.4)], 'graphene-monolayer-4atom': [graphene.monolayer_4atom()], 'graphene-monolayer-nn': [graphene.monolayer(2), pb.regular_polygon(6, 0.9)], 'graphene-monolayer-periodic-1d': [ graphene.monolayer(), pb.primitive(5, 5), pb.translational_symmetry(a1=True, a2=False) ], 'graphene-monolayer-periodic-1d-alt': [ graphene.monolayer_4atom(), pb.rectangle(1), pb.translational_symmetry(a1=False, a2=0.6) ], 'graphene-monolayer-periodic-2d': [ graphene.monolayer(), pb.primitive(a1=5, a2=5), pb.translational_symmetry(a1=1, a2=1) ], 'graphene-monolayer-4atom-periodic-2d': [ graphene.monolayer_4atom(), pb.rectangle(1),
import pybinding as pb import numpy as np import matplotlib.pyplot as plt d = 1 pb.pltutils.use_style() lattice = pb.Lattice(a1=[d,0], a2=[d/2, d*np.math.sqrt(3)/2]) lattice.add_sublattices(("A", [0,0])) lattice.add_hoppings(([0,1], "A", "A", 1), ([1,0], "A", "A", 1)) model = pb.Model(lattice, pb.primitive(a1=5, a2=5)) model.plot() plt.show()
def test_multiorbital_hoppings(): """For multi-orbital lattices, hopping modifiers get `energy` as 3D array""" 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.25, 0.0], tau_z + 2 * tau_x), ("B", [0.0, 0.5], 0.5), ("C", [0.25, 0.0], [1, 2, 3])) lat.register_hopping_energies({ "t11": 1, "t1|2": 2, "t1|3": 3, "t22": 3 * tau_z, "t23": [[0, 1, 2], [3, 4, 5]], "t13": [[11, 12, 13]], }) lat.add_hoppings(([1, 0], "B", "B", "t11"), ([0, 1], "B", "B", "t11"), ([1, 1], "B", "B", "t1|2"), ([1, -1], "B", "B", "t1|3"), ([0, 1], "A", "A", "t22"), ([0, 0], "A", "C", "t23"), ([0, 0], "B", "C", "t13")) return lat capture = {} @pb.hopping_energy_modifier def hopping(energy, hop_id, x1, y1, z1, x2, y2, z2): capture[hop_id] = [v.copy() for v in (energy, x1, y1, z1, x2, y2, z2)] return energy def assert_positions(name, **expected): _, x1, y1, z1, x2, y2, z2 = capture[name] assert pytest.fuzzy_equal(x1.squeeze(), expected["x1"]) assert pytest.fuzzy_equal(y1.squeeze(), expected["y1"]) assert pytest.fuzzy_equal(z1.squeeze(), expected["z1"]) assert pytest.fuzzy_equal(x2.squeeze(), expected["x2"]) assert pytest.fuzzy_equal(y2.squeeze(), expected["y2"]) assert pytest.fuzzy_equal(z1.squeeze(), expected["z2"]) def assert_hoppings(name, **expected): energy, *_ = capture[name] assert energy.shape == expected["shape"] for i in range(energy.shape[0]): assert pytest.fuzzy_equal(energy[i], expected["energy"]) model = pb.Model(multi_orbital_lattice(), pb.primitive(2, 2), hopping) assert model.system.num_sites == 12 assert model.hamiltonian.shape[0] == 24 assert_positions("t11", x1=[-1, -1, 0, -1], y1=[-0.5, -0.5, -0.5, 0.5], z1=[0, 0, 0, 0], x2=[0, -1, 0, 0], y2=[-0.5, 0.5, 0.5, 0.5], z2=[0, 0, 0, 0]) assert_positions("t22", x1=[-1.25, -0.25], y1=[-1, -1], z1=[0, 0], x2=[-1.25, -0.25], y2=[0, 0], z2=[0, 0]) assert_positions("t23", x1=[-1.25, -0.25, -1.25, -0.25], y1=[-1, -1, 0, 0], z1=[0, 0, 0, 0], x2=[-0.75, 0.25, -0.75, 0.25], y2=[-1, -1, 0, 0], z2=[0, 0, 0, 0]) assert_positions("t13", x1=[-1, 0, -1, 0], y1=[-0.5, -0.5, 0.5, 0.5], z1=[0, 0, 0, 0], x2=[-0.75, 0.25, -0.75, 0.25], y2=[-1, -1, 0, 0], z2=[0, 0, 0, 0]) assert_hoppings("t11", shape=(4, ), energy=[1]) assert_hoppings("t22", shape=(2, 2, 2), energy=[[3, 0], [0, -3]]) assert_hoppings("t23", shape=(4, 2, 3), energy=[[0, 1, 2], [3, 4, 5]]) assert_hoppings("t13", shape=(4, 1, 3), energy=[[11, 12, 13]]) @pb.hopping_energy_modifier def hopping_mod(energy, hop_id, x1, y1, z1, x2, y2, z2): if hop_id == "t11": energy *= x1 * 0 elif hop_id == "t1": energy *= 3 elif hop_id == "t22": energy += [[0, 1], [1, 0]] elif hop_id == "t23": energy += [[1, 0, 0]] elif hop_id == "t13": energy *= 2 capture[hop_id] = [v.copy() for v in (energy, x1, y1, z1, x2, y2, z2)] return energy model = pb.Model(multi_orbital_lattice(), pb.primitive(2, 2), hopping_mod) assert model.system.num_sites == 12 assert model.hamiltonian.shape[0] == 24 assert_hoppings("t11", shape=(4, ), energy=[0]) assert_hoppings("t1|2", shape=(1, ), energy=[6]) assert_hoppings("t1|3", shape=(1, ), energy=[9]) assert_hoppings("t22", shape=(2, 2, 2), energy=[[3, 1], [1, -3]]) assert_hoppings("t23", shape=(4, 2, 3), energy=[[1, 1, 2], [4, 4, 5]]) assert_hoppings("t13", shape=(4, 1, 3), energy=[[22, 24, 26]])
def test_multiorbital_hoppings(): """For multi-orbital lattices, hopping modifiers get `energy` as 3D array""" 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.25, 0.0], tau_z + 2 * tau_x), ("B", [0.0, 0.5], 0.5), ("C", [0.25, 0.0], [1, 2, 3])) lat.register_hopping_energies({ "t11": 1, "t22": 3 * tau_z, "t23": [[0, 1, 2], [3, 4, 5]], "t13": [[11, 12, 13]], }) lat.add_hoppings(([1, 0], "B", "B", "t11"), ([0, 1], "B", "B", "t11"), ([0, 1], "A", "A", "t22"), ([0, 0], "A", "C", "t23"), ([0, 0], "B", "C", "t13")) return lat capture = {} @pb.hopping_energy_modifier def hopping(energy, hop_id, x1, y1, z1, x2, y2, z2): capture[hop_id] = [v.copy() for v in (energy, x1, y1, z1, x2, y2, z2)] return energy def assert_hoppings(name, **expected): energy, x1, y1, z1, x2, y2, z2 = capture[name] assert energy.shape == expected["shape"] expected_energy = np.array(expected["energy"]) for index in np.ndindex(*expected_energy.shape): expected_slice = np.full(energy.shape[-1], expected_energy[index]) assert pytest.fuzzy_equal(energy[index], expected_slice) assert pytest.fuzzy_equal(x1, expected["x1"]) assert pytest.fuzzy_equal(y1, expected["y1"]) assert pytest.fuzzy_equal(z1, expected["z1"]) assert pytest.fuzzy_equal(x2, expected["x2"]) assert pytest.fuzzy_equal(y2, expected["y2"]) assert pytest.fuzzy_equal(z1, expected["z2"]) model = pb.Model(multi_orbital_lattice(), pb.primitive(2, 2), hopping) assert model.system.num_sites == 12 assert model.hamiltonian.shape[0] == 24 assert_hoppings("t11", shape=(4,), energy=[1, 1, 1, 1], x1=[-1, -1, 0, -1], y1=[-0.5, -0.5, -0.5, 0.5], z1=[0, 0, 0, 0], x2=[0, -1, 0, 0], y2=[-0.5, 0.5, 0.5, 0.5], z2=[0, 0, 0, 0]) assert_hoppings("t22", shape=(2, 2, 2), energy=[[3, 0], [0, -3]], x1=[-1.25, -0.25], y1=[-1, -1], z1=[0, 0], x2=[-1.25, -0.25], y2=[0, 0], z2=[0, 0]) assert_hoppings("t23", shape=(2, 3, 4), energy=[[0, 1, 2], [3, 4, 5]], x1=[-1.25, -0.25, -1.25, -0.25], y1=[-1, -1, 0, 0], z1=[0, 0, 0, 0], x2=[-0.75, 0.25, -0.75, 0.25], y2=[-1, -1, 0, 0], z2=[0, 0, 0, 0]) assert_hoppings("t13", shape=(1, 3, 4), energy=[[11, 12, 13]], x1=[-1, 0, -1, 0], y1=[-0.5, -0.5, 0.5, 0.5], z1=[0, 0, 0, 0], x2=[-0.75, 0.25, -0.75, 0.25], y2=[-1, -1, 0, 0], z2=[0, 0, 0, 0])