def test_inverse_transform(): # Define family on square lattice s = spin_matrices(1 / 2) # Time reversal TR = PointGroupElement( np.eye(2), True, False, spin_rotation(2 * np.pi * np.array([0, 1 / 2, 0]), s)) # Mirror symmetry Mx = PointGroupElement( np.array([[-1, 0], [0, 1]]), False, False, spin_rotation(2 * np.pi * np.array([1 / 2, 0, 0]), s)) # Fourfold C4 = PointGroupElement( np.array([[0, 1], [-1, 0]]), False, False, spin_rotation(2 * np.pi * np.array([0, 0, 1 / 4]), s)) symmetries = [TR, Mx, C4] # One site per unit cell norbs = OrderedDict([('A', 2)]) # Hopping to a neighbouring atom one primitive lattice vector away hopping_vectors = [('A', 'A', [1, 0])] # Make family family = bloch_family(hopping_vectors, symmetries, norbs) fam = hamiltonian_from_family(family, tosympy=False) # Atomic coordinates within the unit cell atom_coords = [(0, 0)] lat_vecs = [(1, 0), (0, 1)] syst = model_to_builder(fam, norbs, lat_vecs, atom_coords) # Convert it back ham2 = builder_to_model(syst).tomodel(nsimplify=True) # Check that it's the same as the original assert fam == ham2 # Check that the Hamiltonians are identical at random points in the Brillouin zone sysw = kwant.wraparound.wraparound(syst).finalized() H1 = sysw.hamiltonian_submatrix H2 = ham2.lambdify() H3 = fam.lambdify() coeffs = 0.5 + np.random.rand(3) for _ in range(20): kx, ky = 3 * np.pi * (np.random.rand(2) - 0.5) params = dict(c0=coeffs[0], c1=coeffs[1], c2=coeffs[2], k_x=kx, k_y=ky) assert allclose(H1(params=params), H2(**params)) assert allclose(H1(params=params), H3(**params))
def test_consistency_kwant(): """Make a random 1D Model, convert it to a builder, and compare the Bloch representation of the Model with that which Kwant uses in wraparound and in Bands. Then, convert the builder back to a Model and compare with the original Model. For comparison, we also make the system using Kwant only. """ orbs = 4 T = np.random.rand(2 * orbs, 2 * orbs) + 1j * np.random.rand(2 * orbs, 2 * orbs) H = np.random.rand(2 * orbs, 2 * orbs) + 1j * np.random.rand(2 * orbs, 2 * orbs) H += H.T.conj() # Make the 1D Model manually using only qsymm features. c0, c1 = sympy.symbols('c0 c1', real=True) kx = _commutative_momenta[0] Ham = Model({c0 * e**(-I * kx): T}, momenta=[0]) Ham += Ham.T().conj() Ham += Model({c1: H}, momenta=[0]) # Two superimposed atoms, same number of orbitals on each norbs = OrderedDict([('A', orbs), ('B', orbs)]) atom_coords = [(0.3, ), (0.3, )] lat_vecs = [(1, )] # Lattice vector # Make a Kwant builder out of the qsymm Model model_syst = model_to_builder(Ham, norbs, lat_vecs, atom_coords) fmodel_syst = model_syst.finalized() # Make the same system manually using only Kwant features. lat = kwant.lattice.general(np.array([[1.]]), [(0., )], norbs=2 * orbs) kwant_syst = kwant.Builder(kwant.TranslationalSymmetry(*lat.prim_vecs)) def onsite(site, c1): return c1 * H def hopping(site1, site2, c0): return c0 * T sublat = lat.sublattices[0] kwant_syst[sublat(0, )] = onsite hopp = kwant.builder.HoppingKind((1, ), sublat) kwant_syst[hopp] = hopping fkwant_syst = kwant_syst.finalized() # Make sure we are consistent with bands calculations in kwant # The Bloch Hamiltonian used in Kwant for the bands computation # is h(k) = exp(-i*k)*hop + onsite + exp(i*k)*hop.T.conj. # We also check that all is consistent with wraparound coeffs = (0.7, 1.2) params = dict(c0=coeffs[0], c1=coeffs[1]) kwant_hop = fkwant_syst.inter_cell_hopping(params=params) kwant_onsite = fkwant_syst.cell_hamiltonian(params=params) model_kwant_hop = fmodel_syst.inter_cell_hopping(params=params) model_kwant_onsite = fmodel_syst.cell_hamiltonian(params=params) assert allclose(model_kwant_hop, coeffs[0] * T) assert allclose(model_kwant_hop, kwant_hop) assert allclose(model_kwant_onsite, kwant_onsite) h_model_kwant = ( lambda k: np.exp(-1j * k) * model_kwant_hop + model_kwant_onsite + np. exp(1j * k) * model_kwant_hop.T.conj()) # As in kwant.Bands h_model = Ham.lambdify() wsyst = kwant.wraparound.wraparound(model_syst).finalized() for _ in range(20): k = (np.random.rand() - 0.5) * 2 * np.pi assert allclose(h_model_kwant(k), h_model(coeffs[0], coeffs[1], k)) params['k_x'] = k h_wrap = wsyst.hamiltonian_submatrix(params=params) assert allclose(h_model(coeffs[0], coeffs[1], k), h_wrap) # Get the model back from the builder # From the Kwant builder based on original Model Ham1 = builder_to_model(model_syst, momenta=Ham.momenta).tomodel(nsimplify=True) # From the pure Kwant builder Ham2 = builder_to_model(kwant_syst, momenta=Ham.momenta).tomodel(nsimplify=True) assert Ham == Ham1 assert Ham == Ham2
def test_graphene_to_kwant(): norbs = OrderedDict([('A', 1), ('B', 1) ]) # A and B atom per unit cell, one orbital each hopping_vectors = [('A', 'B', [1, 0]) ] # Hopping between neighbouring A and B atoms # Atomic coordinates within the unit cell atom_coords = [(0, 0), (1, 0)] # We set the interatom distance to 1, so the lattice vectors have length sqrt(3) lat_vecs = [(3 / 2, np.sqrt(3) / 2), (3 / 2, -np.sqrt(3) / 2)] # Time reversal TR = PointGroupElement(sympy.eye(2), True, False, np.eye(2)) # Chiral symmetry C = PointGroupElement(sympy.eye(2), False, True, np.array([[1, 0], [0, -1]])) # Atom A rotates into A, B into B. sphi = 2 * sympy.pi / 3 RC3 = sympy.Matrix([[sympy.cos(sphi), -sympy.sin(sphi)], [sympy.sin(sphi), sympy.cos(sphi)]]) C3 = PointGroupElement(RC3, False, False, np.eye(2)) # Generate graphene Hamiltonian in Kwant from qsymm symmetries = [C, TR, C3] # Generate using a family family = bloch_family(hopping_vectors, symmetries, norbs) syst_from_family = model_to_builder(family, norbs, lat_vecs, atom_coords, coeffs=None) # Generate using a single Model object g = sympy.Symbol('g', real=True) ham = hamiltonian_from_family(family, coeffs=[g]) ham = Model(hamiltonian=ham, momenta=family[0].momenta) syst_from_model = model_to_builder(ham, norbs, lat_vecs, atom_coords) # Make the graphene Hamiltonian using kwant only atoms, orbs = zip(*[(atom, norb) for atom, norb in norbs.items()]) # Make the kwant lattice lat = kwant.lattice.general(lat_vecs, atom_coords, norbs=orbs) # Store sublattices by name sublattices = { atom: sublat for atom, sublat in zip(atoms, lat.sublattices) } sym = kwant.TranslationalSymmetry(*lat_vecs) bulk = kwant.Builder(sym) bulk[[sublattices['A'](0, 0), sublattices['B'](0, 0)]] = 0 def hop(site1, site2, c0): return c0 bulk[lat.neighbors()] = hop fsyst_family = kwant.wraparound.wraparound(syst_from_family).finalized() fsyst_model = kwant.wraparound.wraparound(syst_from_model).finalized() fsyst_kwant = kwant.wraparound.wraparound(bulk).finalized() # Check that the energies are identical at random points in the Brillouin zone coeff = 0.5 + np.random.rand() for _ in range(20): kx, ky = 3 * np.pi * (np.random.rand(2) - 0.5) params = dict(c0=coeff, k_x=kx, k_y=ky) hamiltonian1 = fsyst_kwant.hamiltonian_submatrix(params=params, sparse=False) hamiltonian2 = fsyst_family.hamiltonian_submatrix(params=params, sparse=False) assert allclose(hamiltonian1, hamiltonian2) params = dict(g=coeff, k_x=kx, k_y=ky) hamiltonian3 = fsyst_model.hamiltonian_submatrix(params=params, sparse=False) assert allclose(hamiltonian2, hamiltonian3) # Include random onsites as well one = sympy.numbers.One() onsites = [ Model({one: np.array([[1, 0], [0, 0]])}, momenta=family[0].momenta), Model({one: np.array([[0, 0], [0, 1]])}, momenta=family[0].momenta) ] family = family + onsites syst_from_family = model_to_builder(family, norbs, lat_vecs, atom_coords, coeffs=None) gs = list(sympy.symbols('g0:%d' % 3, real=True)) ham = hamiltonian_from_family(family, coeffs=gs) ham = Model(hamiltonian=ham, momenta=family[0].momenta) syst_from_model = model_to_builder(ham, norbs, lat_vecs, atom_coords) def onsite_A(site, c1): return c1 def onsite_B(site, c2): return c2 bulk[[sublattices['A'](0, 0)]] = onsite_A bulk[[sublattices['B'](0, 0)]] = onsite_B fsyst_family = kwant.wraparound.wraparound(syst_from_family).finalized() fsyst_model = kwant.wraparound.wraparound(syst_from_model).finalized() fsyst_kwant = kwant.wraparound.wraparound(bulk).finalized() # Check equivalence of the Hamiltonian at random points in the BZ coeffs = 0.5 + np.random.rand(3) for _ in range(20): kx, ky = 3 * np.pi * (np.random.rand(2) - 0.5) params = dict(c0=coeffs[0], c1=coeffs[1], c2=coeffs[2], k_x=kx, k_y=ky) hamiltonian1 = fsyst_kwant.hamiltonian_submatrix(params=params, sparse=False) hamiltonian2 = fsyst_family.hamiltonian_submatrix(params=params, sparse=False) assert allclose(hamiltonian1, hamiltonian2) params = dict(g0=coeffs[0], g1=coeffs[1], g2=coeffs[2], k_x=kx, k_y=ky) hamiltonian3 = fsyst_model.hamiltonian_submatrix(params=params, sparse=False) assert allclose(hamiltonian2, hamiltonian3)