def test_real_space_basis(): lat = kwant.lattice.honeycomb(norbs=[1, 1]) sym = kwant.TranslationalSymmetry(lat.vec((1, 0)), lat.vec((0, 1))) bulk = kwant.Builder(sym) bulk[[lat.a(0, 0), lat.b(0, 0)]] = 0 bulk[lat.neighbors()] = 1 # Including real space symmetries symmetries = find_builder_symmetries(bulk) hex_group_2D = hexagonal() hex_group_2D = set( PointGroupElement( np.array(s.R).astype(float), s.conjugate, s.antisymmetry, None) for s in hex_group_2D) assert len(symmetries) == len(hex_group_2D) assert all([ s1 in symmetries and s2 in hex_group_2D for s1, s2 in zip(hex_group_2D, symmetries) ]) # Only onsite discrete symmetries symmetries = find_builder_symmetries(bulk, spatial_symmetries=False) onsites = [ PointGroupElement(np.eye(2), True, False, None), # T PointGroupElement(np.eye(2), True, True, None), # P PointGroupElement(np.eye(2), False, True, None), # C PointGroupElement(np.eye(2), False, False, None) ] # I assert len(symmetries) == len(onsites) assert all([ s1 in symmetries and s2 in onsites for s1, s2 in zip(onsites, symmetries) ])
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 _get_builder_symmetries(builder): """Extract the declared symmetries of a Kwant builder. Parameters ---------- builder : `~kwant.builder.Builder` Returns ------- builder_symmetries : dict Dictionary of the discrete symmetries that the builder has. The symmetries can be particle-hole, time-reversal or chiral, which are returned as qsymm.PointGroupElements, or a conservation law, which is returned as a qsymm.ContinuousGroupGenerators. """ dim = len(np.array(builder.symmetry.periods)) symmetry_names = [ 'time_reversal', 'particle_hole', 'chiral', 'conservation_law' ] builder_symmetries = { name: getattr(builder, name) for name in symmetry_names if getattr(builder, name) is not None } for name, symmetry in builder_symmetries.items(): if name == 'time_reversal': builder_symmetries[name] = PointGroupElement( np.eye(dim), True, False, symmetry) elif name == 'particle_hole': builder_symmetries[name] = PointGroupElement( np.eye(dim), True, True, symmetry) elif name == 'chiral': builder_symmetries[name] = PointGroupElement( np.eye(dim), False, True, symmetry) elif name == 'conservation_law': builder_symmetries[name] = ContinuousGroupGenerator(R=None, U=symmetry) else: raise ValueError("Invalid symmetry name.") return builder_symmetries
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)