def test_closest(): rng = ensure_rng(10) for sym_dim in range(1, 4): for space_dim in range(sym_dim, 4): lat = kwant.lattice.general(ta.identity(space_dim)) # Choose random periods. while True: periods = rng.randint(-10, 11, (sym_dim, space_dim)) if np.linalg.det(np.dot(periods, periods.T)) > 0.1: # Periods are reasonably linearly independent. break syst = builder.Builder(kwant.TranslationalSymmetry(*periods)) for tag in rng.randint(-30, 31, (4, space_dim)): # Add site and connect it to the others. old_sites = list(syst.sites()) new_site = lat(*tag) syst[new_site] = None syst[((new_site, os) for os in old_sites)] = None # Test consistency with fill(). for point in 200 * rng.random_sample((10, space_dim)) - 100: closest = syst.closest(point) dist = closest.pos - point dist = ta.dot(dist, dist) syst2 = builder.Builder() syst2.fill(syst, inside_disc(point, 2 * dist), closest) assert syst2.closest(point) == closest for site in syst2.sites(): dd = site.pos - point dd = ta.dot(dd, dd) assert dd >= 0.999999 * dist
def test_update(): lat = builder.SimpleSiteFamily() syst = builder.Builder() syst[[lat(0, ), lat(1, )]] = 1 syst[lat(0, ), lat(1, )] = 1 other_syst = builder.Builder() other_syst[[lat(1, ), lat(2, )]] = 2 other_syst[lat(1, ), lat(2, )] = 1 lead0 = builder.Builder(VerySimpleSymmetry(-1)) lead0[lat(0, )] = 1 lead0[(lat(0, ), lat(1, ))] = 1 lead0 = builder.BuilderLead(lead0, [lat(0, )]) syst.leads.append(lead0) lead1 = builder.Builder(VerySimpleSymmetry(1)) lead1[lat(2, )] = 1 lead1[(lat(2, ), lat(1, ))] = 1 lead1 = builder.BuilderLead(lead1, [lat(2, )]) other_syst.leads.append(lead1) syst.update(other_syst) assert syst.leads == [lead0, lead1] expected = sorted([((0, ), 1), ((1, ), 2), ((2, ), 2)]) assert sorted(((s.tag, v) for s, v in syst.site_value_pairs())) == expected expected = sorted([((0, ), (1, ), 1), ((1, ), (2, ), 1)]) assert (sorted(((a.tag, b.tag, v) for (a, b), v in syst.hopping_value_pairs())) == expected)
def test_attach_lead(): fam = builder.SimpleSiteFamily(norbs=1) fam_noncommensurate = builder.SimpleSiteFamily(name='other') syst = builder.Builder() syst[fam(1)] = 0 lead = builder.Builder(VerySimpleSymmetry(-2)) raises(ValueError, syst.attach_lead, lead) lead[fam(0)] = 1 raises(ValueError, syst.attach_lead, lead) lead[fam(1)] = 1 syst.attach_lead(lead) raises(ValueError, syst.attach_lead, lead, fam(5)) syst = builder.Builder() # The tag of the site that is added in the following line is an empty tuple. # This simulates a site family that is not commensurate with the symmetry of # the lead. Such sites may be present in the system, as long as there are # other sites that will interrupt the lead. syst[fam_noncommensurate()] = 2 syst[fam(1)] = 0 syst[fam(0)] = 1 lead[fam(0), fam(1)] = lead[fam(0), fam(2)] = 1 syst.attach_lead(lead) assert len(list(syst.sites())) == 4 assert set(syst.leads[0].interface) == set([fam(-1), fam(0)]) syst[fam(-10)] = syst[fam(-11)] = 0 syst.attach_lead(lead) assert set(syst.leads[1].interface) == set([fam(-10), fam(-11)]) assert len(list(syst.sites())) == 6 syst.attach_lead(lead, fam(-5)) assert set(syst.leads[0].interface) == set([fam(-1), fam(0)]) # add some further-than-nearest-neighbor hoppings hop_range = 3 lead = builder.Builder( VerySimpleSymmetry(1), conservation_law=np.eye(1), time_reversal=np.eye(1), particle_hole=np.eye(1), chiral=np.eye(1)) lead[fam(0)] = 1 for i in range(1, hop_range + 1): lead[fam(0), fam(i)] = 1 syst.attach_lead(lead) expanded_lead = syst.leads[-1].builder assert expanded_lead.symmetry.period == hop_range assert len(list(expanded_lead.sites())) == hop_range assert expanded_lead.conservation_law is lead.conservation_law assert expanded_lead.time_reversal is lead.time_reversal assert expanded_lead.particle_hole is lead.particle_hole assert expanded_lead.chiral is lead.chiral # check that we can actually finalize the system syst.finalized()
def test_discrete_symmetries(): lat = builder.SimpleSiteFamily(name='ccc', norbs=2) lat2 = builder.SimpleSiteFamily(name='bla', norbs=1) lat3 = builder.SimpleSiteFamily(name='dd', norbs=4) cons_law = {lat: np.diag([1, 2]), lat2: 0} syst = builder.Builder( conservation_law=cons_law, time_reversal=( lambda site, p: np.exp(1j * p) * np.identity(site.family.norbs))) syst[lat(1)] = np.identity(2) syst[lat2(1)] = 1 params = dict(p=0) sym = syst.finalized().discrete_symmetry(params=params) for proj, should_be in zip(sym.projectors, np.identity(3)): assert np.allclose(proj.toarray(), should_be.reshape((3, 1))) assert np.allclose(sym.time_reversal.toarray(), np.identity(3)) syst.conservation_law = lambda site, p: cons_law[site.family] sym = syst.finalized().discrete_symmetry(params=params) for proj, should_be in zip(sym.projectors, np.identity(3)): assert np.allclose(proj.toarray(), should_be.reshape((-1, 1))) syst = builder.Builder(conservation_law=np.diag([-1, 1])) syst[lat(1)] = np.identity(2) sym = syst.finalized().discrete_symmetry() for proj, should_be in zip(sym.projectors, np.identity(2)): assert np.allclose(proj.toarray(), should_be.reshape((-1, 1))) syst = builder.Builder(conservation_law=1) syst[lat2(1)] = 0 sym = syst.finalized().discrete_symmetry() [proj] = sym.projectors assert np.allclose(proj.toarray(), [[1]]) syst = kwant.Builder(conservation_law=np.diag([-1, 1, -1, 1])) syst[lat3(0)] = np.eye(4) sym = syst.finalized().discrete_symmetry() p1 = np.zeros((4, 2)) p1[0, 0] = p1[2, 1] = 1 assert np.allclose(sym.projectors[0].toarray(), p1) p2 = np.zeros((4, 2)) p2[1, 0] = p2[3, 1] = 1 assert np.allclose(sym.projectors[1].toarray(), p2) # test parameter passing to conservation_law syst = builder.Builder(conservation_law=lambda site, b: b) syst[lat2(1)] = 0 sym = syst.finalized().discrete_symmetry(params=dict(a=None, b=1)) [proj] = sym.projectors assert np.allclose(proj.toarray(), [[1]])
def test_neighbors_not_in_single_domain(): sr = builder.Builder() lead = builder.Builder(VerySimpleSymmetry(-1)) fam = builder.SimpleSiteFamily() sr[(fam(x, y) for x in range(3) for y in range(3) if x >= y)] = 0 sr[builder.HoppingKind((1, 0), fam)] = 1 sr[builder.HoppingKind((0, 1), fam)] = 1 lead[(fam(0, y) for y in range(3))] = 0 lead[((fam(0, y), fam(1, y)) for y in range(3))] = 1 lead[((fam(0, y), fam(0, y + 1)) for y in range(2))] = 1 sr.leads.append(builder.BuilderLead(lead, [fam(i, i) for i in range(3)])) raises(ValueError, sr.finalized)
def test_wire(): rng = ensure_rng(5) vecs = rng.randn(3, 3) vecs[0] = [1, 0, 0] center = rng.randn(3) lat = lattice.general(vecs, rng.randn(4, 3), norbs=1) syst = builder.Builder(lattice.TranslationalSymmetry((2, 0, 0))) def wire_shape(pos): pos = np.array(pos) return np.linalg.norm(pos[1:] - center[1:])**2 <= 8.6**2 syst[lat.shape(wire_shape, center)] = 0 sites2 = set(syst.sites()) syst = builder.Builder(lattice.TranslationalSymmetry((2, 0, 0))) syst[lat.wire(center, 8.6)] = 1 sites1 = set(syst.sites()) assert sites1 == sites2
def test_site_families(): syst = builder.Builder() fam = builder.SimpleSiteFamily() ofam = builder.SimpleSiteFamily() yafam = builder.SimpleSiteFamily('another_name') syst[fam(0)] = 7 assert syst[fam(0)] == 7 assert len(set([fam, ofam, fam('a'), ofam('a'), yafam])) == 3 syst[fam(1)] = 123 assert syst[fam(1)] == 123 assert syst[ofam(1)] == 123 raises(KeyError, syst.__getitem__, yafam(1)) # test site families compare equal/not-equal assert fam == ofam assert fam != yafam assert fam != None assert fam != 'a' # test site families sorting fam1 = builder.SimpleSiteFamily(norbs=1) fam2 = builder.SimpleSiteFamily(norbs=2) assert fam1 < fam2 # string '1' is lexicographically less than '2'
def make_system(): # 1 # / \ # 3-0---2-4-5 6-7 8 syst = builder.Builder() fam = builder.SimpleSiteFamily() syst[(fam(i) for i in range(9))] = None syst[[(fam(0), fam(1)), (fam(1), fam(2)), (fam(2), fam(0))]] = None syst[[(fam(0), fam(3)), (fam(2), fam(4)), (fam(4), fam(5))]] = None syst[fam(6), fam(7)] = None return syst
def check_construction_and_indexing(sites, sites_fd, hoppings, hoppings_fd, unknown_hoppings, sym=None): fam = builder.SimpleSiteFamily() syst = builder.Builder(sym) t, V = 1.0j, 0.0 syst[sites] = V for site in sites: syst[site] = V syst[hoppings] = t for hopping in hoppings: syst[hopping] = t for hopping in unknown_hoppings: raises(KeyError, syst.__setitem__, hopping, t) assert (fam(5), fam(123)) not in syst assert (sites[0], fam(5, 123)) not in syst assert (fam(7, 8), sites[0]) not in syst for site in sites: assert site in syst assert syst[site] == V for hop in hoppings: rev_hop = hop[1], hop[0] assert hop in syst assert rev_hop in syst assert syst[hop] == t assert syst[rev_hop] == t.conjugate() assert syst.degree(sites[0]) == 2 assert (sorted(s for s in syst.neighbors(sites[0])) == sorted( [sites[1], sites[-1]])) del syst[hoppings] assert list(syst.hoppings()) == [] syst[hoppings] = t del syst[sites[0]] assert sorted(tuple(s) for s in syst.sites()) == sorted(sites_fd[1:]) assert (sorted( (a, b) for a, b in syst.hoppings()) == sorted(hoppings_fd[1:-1])) assert (sorted((tuple(site.tag), value) for site, value in syst.site_value_pairs()) == sorted( (tuple(site.tag), syst[site]) for site in syst.sites())) assert (sorted((tuple(a.tag), tuple(b.tag), value) for (a, b), value in syst.hopping_value_pairs()) == sorted( (tuple(a.tag), tuple(b.tag), syst[a, b]) for a, b in syst.hoppings()))
def test_value_equality_and_identity(): m = ta.array([[1, 2], [3j, 4j]]) syst = builder.Builder() fam = builder.SimpleSiteFamily() syst[fam(0)] = m syst[fam(1)] = m assert syst[fam(1)] is m syst[fam(0), fam(1)] = m assert syst[fam(1), fam(0)] == m.transpose().conjugate() assert syst[fam(0), fam(1)] is m syst[fam(1), fam(0)] = m assert syst[fam(0), fam(1)] == m.transpose().conjugate() assert syst[fam(1), fam(0)] is m
def test_builder_with_symmetry(): g = kwant.lattice.general(ta.identity(3)) sym = kwant.TranslationalSymmetry((0, 0, 3), (0, 2, 0)) syst = builder.Builder(sym) t, V = 1.0j, 0.0 hoppings = [(g(5, 0, 0), g(0, 5, 0)), (g(0, 5, 0), g(0, 0, 5)), (g(0, 0, 5), g(5, 0, 0)), (g(0, 3, 0), g(0, 0, 5)), (g(0, 7, -6), g(5, 6, -6))] hoppings_fd = [(g(5, 0, 0), g(0, 5, 0)), (g(0, 1, 0), g(0, -4, 5)), (g(0, 0, 2), g(5, 0, -3)), (g(0, 1, 0), g(0, -2, 5)), (g(0, 1, 0), g(5, 0, 0))] syst[(a for a, b in hoppings)] = V syst[hoppings] = t # TODO: Once Tinyarray supports "<" the conversion to tuple can be removed. assert (sorted(tuple(site.tag) for site in syst.sites()) == sorted(set(tuple(hop[0].tag) for hop in hoppings_fd))) for sites in hoppings_fd: for site in sites: assert site in syst assert syst[site] == V # TODO: Once Tinyarray supports "<" the conversion to tuple can be removed. assert (sorted((tuple(a.tag), tuple(b.tag)) for a, b in syst.hoppings()) == sorted((tuple(a.tag), tuple(b.tag)) for a, b in hoppings_fd)) for hop in hoppings_fd: rhop = hop[1], hop[0] assert hop in syst assert rhop in syst assert syst[hop] == t assert syst[rhop] == t.conjugate() del syst[g(0, 6, -4), g(0, 11, -9)] assert (g(0, 1, 0), g(0, -4, 5)) not in syst del syst[g(0, 3, -3)] assert (list((a.tag, b.tag) for a, b in syst.hoppings()) == [((0, 0, 2), (5, 0, -3))])
def test_hermitian_conjugation(): def f(i, j, arg): i, j = i.tag, j.tag if j[0] == i[0] + 1: return arg * ta.array([[1, 2j], [3 + 1j, 4j]]) else: raise ValueError syst = builder.Builder() fam = builder.SimpleSiteFamily() syst[fam(0)] = syst[fam(1)] = ta.identity(2) syst[fam(0), fam(1)] = f assert syst[fam(0), fam(1)] is f assert isinstance(syst[fam(1), fam(0)], builder.HermConjOfFunc) assert (syst[fam(1), fam(0)](fam(1), fam(0), 2) == syst[fam(0), fam(1)](fam(0), fam(1), 2).conjugate().transpose()) syst[fam(0), fam(1)] = syst[fam(1), fam(0)] assert isinstance(syst[fam(0), fam(1)], builder.HermConjOfFunc) assert syst[fam(1), fam(0)] is f
def test_HoppingKind(): g = kwant.lattice.general(ta.identity(3), name='some_lattice') h = kwant.lattice.general(ta.identity(3), name='another_lattice') sym = kwant.TranslationalSymmetry((0, 2, 0)) syst = builder.Builder(sym) syst[((h if max(x, y, z) % 2 else g)(x, y, z) for x in range(4) for y in range(2) for z in range(4))] = None for delta, ga, gb, n in [((1, 0, 0), g, h, 4), ((1, 0, 0), h, g, 7), ((0, 1, 0), g, h, 1), ((0, 4, 0), h, h, 21), ((0, 0, 1), g, h, 4)]: ph = list(builder.HoppingKind(delta, ga, gb)(syst)) assert len(ph) == n ph = set(ph) assert len(ph) == n ph2 = list(( sym.to_fd(b, a) for a, b in builder.HoppingKind(ta.negative(delta), gb, ga)(syst))) assert len(ph2) == n ph2 = set(ph2) assert ph2 == ph for a, b in ph: assert a.family == ga assert b.family == gb assert sym.to_fd(a) == a assert a.tag - b.tag == delta # test hashability and equality hk = builder.HoppingKind((1, 0, 0), g) hk2 = builder.HoppingKind((1, 0, 0), g) hk3 = builder.HoppingKind((1, 0, 0), g, h) assert hk == hk2 assert hash(hk) == hash(hk2) assert hk != hk3 assert hash(hk) != hash(hk3) assert len({hk: 0, hk2:1, hk3: 2}) == 2
def test_finalization(): """Test the finalization of finite and infinite systems. In order to exactly verify the finalization, low-level features of the build module are used directly. This is not the way one would use a finalized system in normal code. """ def set_sites(dest): while len(dest) < n_sites: site = rng.randrange(size), rng.randrange(size) if site not in dest: dest[site] = random_onsite_hamiltonian(rng) def set_hops(dest, sites): while len(dest) < n_hops: a, b = rng.sample(list(sites), 2) if (a, b) not in dest and (b, a) not in dest: dest[a, b] = random_hopping_integral(rng) rng = Random(123) size = 20 n_sites = 120 n_hops = 500 # Make scattering region blueprint. sr_sites = {} set_sites(sr_sites) sr_hops = {} set_hops(sr_hops, sr_sites) # Make lead blueprint. possible_neighbors = rng.sample(list(sr_sites), n_sites // 2) lead_sites = {} for pn in possible_neighbors: lead_sites[pn] = random_hopping_integral(rng) set_sites(lead_sites) lead_hops = {} # Hoppings within a single lead unit cell set_hops(lead_hops, lead_sites) lead_sites_list = list(lead_sites) neighbors = set() for i in range(n_hops): while True: a = rng.choice(lead_sites_list) b = rng.choice(possible_neighbors) neighbors.add(b) b = b[0] - size, b[1] if rng.randrange(2): a, b = b, a if (a, b) not in lead_hops and (b, a) not in lead_hops: break lead_hops[a, b] = random_hopping_integral(rng) neighbors = sorted(neighbors) # Build scattering region from blueprint and test it. syst = builder.Builder() fam = kwant.lattice.general(ta.identity(2)) for site, value in sr_sites.items(): syst[fam(*site)] = value for hop, value in sr_hops.items(): syst[fam(*hop[0]), fam(*hop[1])] = value fsyst = syst.finalized() check_id_by_site(fsyst) check_onsite(fsyst, sr_sites) check_hoppings(fsyst, sr_hops) # check that sites are sorted assert fsyst.sites == tuple(sorted(fam(*site) for site in sr_sites)) # Build lead from blueprint and test it. lead = builder.Builder(kwant.TranslationalSymmetry((size, 0))) for site, value in lead_sites.items(): shift = rng.randrange(-5, 6) * size site = site[0] + shift, site[1] lead[fam(*site)] = value with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") lead.finalized() # Trigger the warning. assert len(w) == 1 assert issubclass(w[0].category, RuntimeWarning) assert "disconnected" in str(w[0].message) for (a, b), value in lead_hops.items(): shift = rng.randrange(-5, 6) * size a = a[0] + shift, a[1] b = b[0] + shift, b[1] lead[fam(*a), fam(*b)] = value flead = lead.finalized() all_sites = list(lead_sites) all_sites.extend((x - size, y) for (x, y) in neighbors) check_id_by_site(fsyst) check_onsite(flead, all_sites, check_values=False) check_onsite(flead, lead_sites, subset=True) check_hoppings(flead, lead_hops) # Attach lead to system with empty interface. syst.leads.append(builder.BuilderLead(lead, ())) raises(ValueError, syst.finalized) # Attach lead with improper interface. syst.leads[-1] = builder.BuilderLead( lead, 2 * tuple(builder.Site(fam, n) for n in neighbors)) raises(ValueError, syst.finalized) # Attach lead properly. syst.leads[-1] = builder.BuilderLead(lead, (builder.Site(fam, n) for n in neighbors)) fsyst = syst.finalized() assert len(fsyst.lead_interfaces) == 1 assert ([fsyst.sites[i].tag for i in fsyst.lead_interfaces[0]] == neighbors) # test that we cannot finalize a system with a badly sorted interface order raises(ValueError, builder.InfiniteSystem, lead, [builder.Site(fam, n) for n in reversed(neighbors)]) # site ordering independent of whether interface was specified flead_order = builder.InfiniteSystem( lead, [builder.Site(fam, n) for n in neighbors]) assert flead.sites == flead_order.sites syst.leads[-1] = builder.BuilderLead(lead, (builder.Site(fam, n) for n in neighbors)) fsyst = syst.finalized() assert len(fsyst.lead_interfaces) == 1 assert ([fsyst.sites[i].tag for i in fsyst.lead_interfaces[0]] == neighbors) # Add a hopping to the lead which couples two next-nearest cells and check # whether this leads to an error. a = rng.choice(lead_sites_list) b = rng.choice(possible_neighbors) b = b[0] + 2 * size, b[1] lead[fam(*a), fam(*b)] = random_hopping_integral(rng) raises(ValueError, lead.finalized)
def model_to_builder(model, norbs, lat_vecs, atom_coords, *, coeffs=None): """Make a `~kwant.builder.Builder` out of qsymm.Models or qsymm.BlochModels. Parameters ---------- model : qsymm.Model, qsymm.BlochModel, or an iterable thereof The Hamiltonian (or terms of the Hamiltonian) to convert to a Builder. norbs : OrderedDict or sequence of pairs Maps sites to the number of orbitals per site in a unit cell. lat_vecs : list of arrays Lattice vectors of the underlying tight binding lattice. atom_coords : list of arrays Positions of the sites (or atoms) within a unit cell. The ordering of the atoms is the same as in norbs. coeffs : list of sympy.Symbol, default None. Constant prefactors for the individual terms in model, if model is a list of multiple objects. If model is a single Model or BlochModel object, this argument is ignored. By default assigns the coefficient c_n to element model[n]. Returns ------- syst : `~kwant.builder.Builder` The unfinalized Kwant system representing the qsymm Model(s). Notes ----- Onsite terms that are not provided in the input model are set to zero by default. The input model(s) representing the tight binding Hamiltonian in Bloch form should follow the convention where the difference in the real space atomic positions appear in the Bloch factors. """ def make_int(R): # If close to an integer array convert to integer tinyarray, else # return None R_int = ta.array(np.round(R), int) if qsymm.linalg.allclose(R, R_int): return R_int else: return None def term_onsite(onsites_dict, hopping_dict, hop_mat, atoms, sublattices, coords_dict): """Find the Kwant onsites and hoppings in a qsymm.BlochModel term that has no lattice translation in the Bloch factor. """ for atom1, atom2 in it.product(atoms, atoms): # Subblock within the same sublattice is onsite hop = hop_mat[ranges[atom1], ranges[atom2]] if sublattices[atom1] == sublattices[atom2]: onsites_dict[atom1] += Model({coeff: hop}, momenta=momenta) # Blocks between sublattices are hoppings between sublattices # at the same position. # Only include nonzero hoppings elif not allclose(hop, 0): if not allclose(np.array(coords_dict[atom1]), np.array(coords_dict[atom2])): raise ValueError( "Position of sites not compatible with qsymm model.") lat_basis = np.array(zer) hop = Model({coeff: hop}, momenta=momenta) hop_dir = builder.HoppingKind(-lat_basis, sublattices[atom1], sublattices[atom2]) hopping_dict[hop_dir] += hop return onsites_dict, hopping_dict def term_hopping(hopping_dict, hop_mat, atoms, sublattices, coords_dict): """Find Kwant hoppings in a qsymm.BlochModel term that has a lattice translation in the Bloch factor. """ # Iterate over combinations of atoms, set hoppings between each for atom1, atom2 in it.product(atoms, atoms): # Take the block from atom1 to atom2 hop = hop_mat[ranges[atom1], ranges[atom2]] # Only include nonzero hoppings if allclose(hop, 0): continue # Adjust hopping vector to Bloch form basis r_lattice = ( r_vec + np.array(coords_dict[atom1]) - np.array(coords_dict[atom2]) ) # Bring vector to basis of lattice vectors lat_basis = np.linalg.solve(np.vstack(lat_vecs).T, r_lattice) lat_basis = make_int(lat_basis) # Should only have hoppings that are integer multiples of # lattice vectors if lat_basis is not None: hop_dir = builder.HoppingKind(-lat_basis, sublattices[atom1], sublattices[atom2]) # Set the hopping as the matrix times the hopping amplitude hopping_dict[hop_dir] += Model({coeff: hop}, momenta=momenta) else: raise RuntimeError('A nonzero hopping not matching a ' 'lattice vector was found.') return hopping_dict # Disambiguate single model instances from iterables thereof. Because # Model is itself iterable (subclasses dict) this is a bit cumbersome. if isinstance(model, Model): # BlochModel can't yet handle getting a Blochmodel as input if not isinstance(model, BlochModel): model = BlochModel(model) else: model = BlochModel(hamiltonian_from_family( model, coeffs=coeffs, nsimplify=False, tosympy=False)) # 'momentum' and 'zer' are used in the closures defined above, so don't # move these declarations down. momenta = model.momenta if len(momenta) != len(lat_vecs): raise ValueError("Dimension of the lattice and number of " "momenta do not match.") zer = [0] * len(momenta) # Subblocks of the Hamiltonian for different atoms. N = 0 if not any([isinstance(norbs, OrderedDict), isinstance(norbs, list), isinstance(norbs, tuple)]): raise ValueError('norbs must be OrderedDict, tuple, or list.') else: norbs = OrderedDict(norbs) ranges = dict() for a, n in norbs.items(): ranges[a] = slice(N, N + n) N += n # Extract atoms and number of orbitals per atom, # store the position of each atom atoms, orbs = zip(*norbs.items()) coords_dict = dict(zip(atoms, atom_coords)) # Make the kwant lattice lat = lattice.general(lat_vecs, atom_coords, norbs=orbs) # Store sublattices by name sublattices = dict(zip(atoms, lat.sublattices)) # Keep track of the hoppings and onsites by storing those # which have already been set. hopping_dict = defaultdict(dict) onsites_dict = defaultdict(dict) # Iterate over all terms in the model. for key, hop_mat in model.items(): # Determine whether this term is an onsite or a hopping, extract # overall symbolic coefficient if any, extract the exponential # part describing the hopping if present. r_vec, coeff = key # Onsite term; modifies onsites_dict and hopping_dict in-place if allclose(r_vec, 0): term_onsite( onsites_dict, hopping_dict, hop_mat, atoms, sublattices, coords_dict) # Hopping term; modifies hopping_dict in-place else: term_hopping(hopping_dict, hop_mat, atoms, sublattices, coords_dict) # If some onsite terms are not set, we set them to zero. for atom in atoms: if atom not in onsites_dict: onsites_dict[atom] = Model( {sympy.numbers.One(): np.zeros((norbs[atom], norbs[atom]))}, momenta=momenta) # Make the Kwant system, and set all onsites and hoppings. sym = lattice.TranslationalSymmetry(*lat_vecs) syst = builder.Builder(sym) # Iterate over all onsites and set them for atom, onsite in onsites_dict.items(): syst[sublattices[atom](*zer)] = onsite.lambdify(onsite=True) # Finally, iterate over all the hoppings and set them for direction, hopping in hopping_dict.items(): syst[direction] = hopping.lambdify(hopping=True) return syst
def test_ModesLead_and_SelfEnergyLead(): lat = builder.SimpleSiteFamily() hoppings = [ builder.HoppingKind((1, 0), lat), builder.HoppingKind((0, 1), lat) ] rng = Random(123) L = 5 t = 1 energies = [0.9, 1.7] syst = builder.Builder() for x in range(L): for y in range(L): syst[lat(x, y)] = 4 * t + rng.random() - 0.5 syst[hoppings] = -t # Attach a lead from the left. lead = builder.Builder(VerySimpleSymmetry(-1)) for y in range(L): lead[lat(0, y)] = 4 * t lead[hoppings] = -t syst.attach_lead(lead) # Make the right lead and attach it. lead = builder.Builder(VerySimpleSymmetry(1)) for y in range(L): lead[lat(0, y)] = 4 * t lead[hoppings] = -t syst.attach_lead(lead) fsyst = syst.finalized() ts = [kwant.smatrix(fsyst, e).transmission(1, 0) for e in energies] # Replace lead with it's finalized copy. lead = fsyst.leads[1] interface = [lat(L - 1, lead.sites[i].tag[1]) for i in range(L)] # Re-attach right lead as ModesLead. syst.leads[1] = builder.ModesLead(lead.modes, interface) fsyst = syst.finalized() ts2 = [kwant.smatrix(fsyst, e).transmission(1, 0) for e in energies] assert_almost_equal(ts2, ts) # Re-attach right lead as ModesLead with old-style modes API # that does not take a 'params' keyword parameter. syst.leads[1] = builder.ModesLead( lambda energy, args: lead.modes(energy, args), interface) fsyst = syst.finalized() ts2 = [kwant.smatrix(fsyst, e).transmission(1, 0) for e in energies] assert_almost_equal(ts2, ts) # Re-attach right lead as SelfEnergyLead. syst.leads[1] = builder.SelfEnergyLead(lead.selfenergy, interface) fsyst = syst.finalized() ts2 = [ kwant.greens_function(fsyst, e).transmission(1, 0) for e in energies ] assert_almost_equal(ts2, ts) # Re-attach right lead as SelfEnergyLead with old-style selfenergy API # that does not take a 'params' keyword parameter. syst.leads[1] = builder.SelfEnergyLead( lambda energy, args: lead.selfenergy(energy, args), interface) fsyst = syst.finalized() ts2 = [ kwant.greens_function(fsyst, e).transmission(1, 0) for e in energies ] assert_almost_equal(ts2, ts) # Append a virtual (=zero self energy) lead. This should have no effect. # Also verifies that the selfenergy callback function can return exotic # arraylikes. syst.leads.append( builder.SelfEnergyLead(lambda *args: list(ta.zeros((L, L))), interface)) fsyst = syst.finalized() ts2 = [ kwant.greens_function(fsyst, e).transmission(1, 0) for e in energies ] assert_almost_equal(ts2, ts)
def test_fill(): g = kwant.lattice.square() sym_x = kwant.TranslationalSymmetry((-1, 0)) sym_xy = kwant.TranslationalSymmetry((-1, 0), (0, 1)) template_1d = builder.Builder(sym_x) template_1d[g(0, 0)] = None template_1d[g.neighbors()] = None def line_200(site): return -100 <= site.pos[0] < 100 ## Test that copying a builder by "fill" preserves everything. for sym, func in [ (kwant.TranslationalSymmetry(*np.diag([3, 4, 5])), lambda pos: True), (builder.NoSymmetry(), lambda pos: ta.dot(pos, pos) < 17) ]: cubic = kwant.lattice.general(ta.identity(3)) # Make a weird system. orig = kwant.Builder(sym) sites = cubic.shape(func, (0, 0, 0)) for i, site in enumerate(orig.expand(sites)): if i % 7 == 0: continue orig[site] = i for i, hopp in enumerate(orig.expand(cubic.neighbors(1))): if i % 11 == 0: continue orig[hopp] = i * 1.2345 for i, hopp in enumerate(orig.expand(cubic.neighbors(2))): if i % 13 == 0: continue orig[hopp] = i * 1j # Clone the original using fill. clone = kwant.Builder(sym) clone.fill(orig, lambda s: True, (0, 0, 0)) # Verify that both are identical. assert set(clone.site_value_pairs()) == set(orig.site_value_pairs()) assert (set(clone.hopping_value_pairs()) == set( orig.hopping_value_pairs())) ## Test for warning when "start" is out. target = builder.Builder() for start in [(-101, 0), (101, 0)]: with warns(RuntimeWarning): target.fill(template_1d, line_200, start) ## Test filling of infinite builder. for n in [1, 2, 4]: sym_n = kwant.TranslationalSymmetry((n, 0)) for start in [g(0, 0), g(20, 0)]: target = builder.Builder(sym_n) sites = target.fill(template_1d, lambda s: True, start, max_sites=10) assert len(sites) == n assert len(list(target.hoppings())) == n assert set(sym_n.to_fd(s) for s in sites) == set(target.sites()) ## test max_sites target = builder.Builder() for max_sites in (-1, 0): with raises(ValueError): target.fill(template_1d, lambda site: True, g(0, 0), max_sites=max_sites) assert len(list(target.sites())) == 0 target = builder.Builder() with raises(RuntimeError): target.fill(template_1d, line_200, g(0, 0), max_sites=10) ## test filling target = builder.Builder() added_sites = target.fill(template_1d, line_200, g(0, 0)) assert len(added_sites) == 200 # raise warning if target already contains all starting sites with warns(RuntimeWarning): target.fill(template_1d, line_200, g(0, 0)) ## test multiplying unit cell size in 1D n_cells = 10 sym_nx = kwant.TranslationalSymmetry(*(sym_x.periods * n_cells)) target = builder.Builder(sym_nx) target.fill(template_1d, lambda site: True, g(0, 0)) should_be_syst = builder.Builder(sym_nx) should_be_syst[(g(i, 0) for i in range(n_cells))] = None should_be_syst[g.neighbors()] = None assert sorted(target.sites()) == sorted(should_be_syst.sites()) assert sorted(target.hoppings()) == sorted(should_be_syst.hoppings()) ## test multiplying unit cell size in 2D template_2d = builder.Builder(sym_xy) template_2d[g(0, 0)] = None template_2d[g.neighbors()] = None template_2d[builder.HoppingKind((2, 2), g)] = None nm_cells = (3, 5) sym_nmxy = kwant.TranslationalSymmetry(*(sym_xy.periods * nm_cells)) target = builder.Builder(sym_nmxy) target.fill(template_2d, lambda site: True, g(0, 0)) should_be_syst = builder.Builder(sym_nmxy) should_be_syst[(g(i, j) for i in range(10) for j in range(10))] = None should_be_syst[g.neighbors()] = None should_be_syst[builder.HoppingKind((2, 2), g)] = None assert sorted(target.sites()) == sorted(should_be_syst.sites()) assert sorted(target.hoppings()) == sorted(should_be_syst.hoppings()) ## test filling 0D builder with 2D builder def square_shape(site): x, y = site.tag return 0 <= x < 10 and 0 <= y < 10 target = builder.Builder() target.fill(template_2d, square_shape, g(0, 0)) should_be_syst = builder.Builder() should_be_syst[(g(i, j) for i in range(10) for j in range(10))] = None should_be_syst[g.neighbors()] = None should_be_syst[builder.HoppingKind((2, 2), g)] = None assert sorted(target.sites()) == sorted(should_be_syst.sites()) assert sorted(target.hoppings()) == sorted(should_be_syst.hoppings()) ## test that 'fill' respects the symmetry of the target builder lat = kwant.lattice.chain(a=1) template = builder.Builder(kwant.TranslationalSymmetry((-1, ))) template[lat(0)] = 2 template[lat.neighbors()] = -1 target = builder.Builder(kwant.TranslationalSymmetry((-2, ))) target[lat(0)] = None to_target_fd = target.symmetry.to_fd # Refuses to fill the target because target already contains the starting # site. with warns(RuntimeWarning): target.fill(template, lambda x: True, lat(0)) # should only add a single site (and hopping) new_sites = target.fill(template, lambda x: True, lat(1)) assert target[lat(0)] is None # should not be overwritten by template assert target[lat(-1)] == template[lat(0)] assert len(new_sites) == 1 assert to_target_fd(new_sites[0]) == to_target_fd(lat(-1))
def test_hamiltonian_evaluation(): def f_onsite(site): return site.tag[0] def f_hopping(a, b): a, b = a.tag, b.tag return complex(a[0] + b[0], a[1] - b[1]) tags = [(0, 0), (1, 1), (2, 2), (3, 3)] edges = [(0, 1), (0, 2), (0, 3), (1, 2)] syst = builder.Builder() fam = builder.SimpleSiteFamily() sites = [fam(*tag) for tag in tags] syst[(fam(*tag) for tag in tags)] = f_onsite syst[((fam(*tags[i]), fam(*tags[j])) for (i, j) in edges)] = f_hopping fsyst = syst.finalized() assert fsyst.graph.num_nodes == len(tags) assert fsyst.graph.num_edges == 2 * len(edges) for i in range(len(tags)): site = fsyst.sites[i] assert site in sites assert fsyst.hamiltonian(i, i) == syst[site](site) for t, h in fsyst.graph: tsite = fsyst.sites[t] hsite = fsyst.sites[h] assert fsyst.hamiltonian(t, h) == syst[tsite, hsite](tsite, hsite) # test when user-function raises errors def onsite_raises(site): raise ValueError() def hopping_raises(a, b): raise ValueError('error message') def test_raising(fsyst, hop): a, b = hop # exceptions are converted to kwant.UserCodeError and we add our message with raises(kwant.UserCodeError) as ctx: fsyst.hamiltonian(a, a) msg = 'Error occurred in user-supplied value function "onsite_raises"' assert msg in ctx.exconly() for hop in [(a, b), (b, a)]: with raises(kwant.UserCodeError) as ctx: fsyst.hamiltonian(*hop) msg = ('Error occurred in user-supplied ' 'value function "hopping_raises"') assert msg in ctx.exconly() # test with finite system new_hop = (fam(-1, 0), fam(0, 0)) syst[new_hop[0]] = onsite_raises syst[new_hop] = hopping_raises fsyst = syst.finalized() hop = tuple(map(fsyst.sites.index, new_hop)) test_raising(fsyst, hop) # test with infinite system inf_syst = kwant.Builder(VerySimpleSymmetry(2)) for k, v in it.chain(syst.site_value_pairs(), syst.hopping_value_pairs()): inf_syst[k] = v inf_fsyst = inf_syst.finalized() hop = tuple(map(inf_fsyst.sites.index, new_hop)) test_raising(inf_fsyst, hop)
def test_bad_keys(): def setitem(key): syst[key] = None fam = builder.SimpleSiteFamily() syst = builder.Builder() failures = [ # Invalid single keys ([syst.__contains__, syst.__getitem__, setitem, syst.__delitem__], [(TypeError, [123, (0, 1), (fam(0), 123), (123, (fam(0)))]), (IndexError, [(fam(0), ), (fam(0), fam(1), fam(2))]), (ValueError, [(fam(0), fam(0)), (fam(2), fam(2))])]), # Hoppings that contain sites that do not belong to the system ([syst.__getitem__, setitem, syst.__delitem__], [(KeyError, [(fam(0), fam(3)), (fam(2), fam(1)), (fam(2), fam(3))])]), # Sequences containing a bad key. ([setitem, syst.__delitem__], [(TypeError, [[fam(0), fam(1), 123], [fam(0), (fam(1), )], [fam(0), (fam(1), fam(2))], [(fam(0), fam(1)), (0, 1)], [(fam(0), fam(1)), (fam(0), 123)], [(fam(0), fam(1)), (123, fam(0))], [(fam(0), fam(1)), fam(2)]]), (IndexError, [[(fam(0), fam(1)), (fam(2), )]]), (ValueError, [[(fam(0), fam(1)), (fam(2), fam(2))], [(fam(0), fam(0)), (fam(1), fam(0))]]), (KeyError, [[(fam(0), fam(1)), (fam(0), fam(3))], [(fam(0), fam(1)), (fam(2), fam(1))], [(fam(1), fam(2)), (fam(0), fam(1))]])]), # Sites that do not belong to the system, also as part of a # sequence ([syst.__delitem__], [(KeyError, [fam(123), [fam(0), fam(123)], [fam(123), fam(1)]])]), # Various things that are not sites present in the system. ([syst.degree, lambda site: list(syst.neighbors(site))], [(TypeError, [ 123, [0, 1, 2], (0, 1), (fam(0), fam(1)), [fam(0), fam(1)], [fam(1), fam(2)], [fam(3), fam(0)] ]), (KeyError, [fam(123)])]) ] for funcs, errors in failures: for error, keys in errors: for key in keys: for func in funcs: syst[[fam(0), fam(1)]] = None syst[fam(0), fam(1)] = None try: raises(error, func, key) except AssertionError: print(func, error, key) raise