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 act(self, element, a, b=None): m_part = self._get_site_family_data(a.family)[0] try: delta = ta.dot(m_part, element) except ValueError: msg = 'Expecting a {0}-tuple group element, but got `{1}` instead.' raise ValueError(msg.format(self.num_directions, element)) if self.is_reversed: delta = -delta if b is None: return builder.Site(a.family, a.tag + delta, True) elif b.family == a.family: return (builder.Site(a.family, a.tag + delta, True), builder.Site(b.family, b.tag + delta, True)) else: m_part = self._get_site_family_data(b.family)[0] try: delta2 = ta.dot(m_part, element) except ValueError: msg = ('Expecting a {0}-tuple group element, ' 'but got `{1}` instead.') raise ValueError(msg.format(self.num_directions, element)) if self.is_reversed: delta2 = -delta2 return (builder.Site(a.family, a.tag + delta, True), builder.Site(b.family, b.tag + delta2, True))
def act(self, element, a, b=None): element = ta.array(element) if element.dtype is not int: raise ValueError("group element must be a tuple of integers") m_part = self._get_site_family_data(a.family)[0] try: delta = ta.dot(m_part, element) except ValueError: msg = 'Expecting a {0}-tuple group element, but got `{1}` instead.' raise ValueError(msg.format(self.num_directions, element)) if self.is_reversed: delta = -delta if b is None: return builder.Site(a.family, a.tag + delta, True) elif b.family == a.family: return (builder.Site(a.family, a.tag + delta, True), builder.Site(b.family, b.tag + delta, True)) else: m_part = self._get_site_family_data(b.family)[0] try: delta2 = ta.dot(m_part, element) except ValueError: msg = ('Expecting a {0}-tuple group element, ' 'but got `{1}` instead.') raise ValueError(msg.format(self.num_directions, element)) if self.is_reversed: delta2 = -delta2 return (builder.Site(a.family, a.tag + delta, True), builder.Site(b.family, b.tag + delta2, True))
def test_dot(): # Check acceptance of non-tinyarray arguments. assert ta.dot([1, 2], (3, 4)) == 11 for dtype in dtypes: shape_pairs = [(1, 1), (2, 2), (3, 3), (0, 0), (0, (0, 1)), ((0, 1), 1), (0, (0, 2)), ((0, 2), 2), (1, (1, 2)), ((2, 1), 1), (2, (2, 1)), ((1, 2), 2), (2, (2, 3)), ((3, 2), 2), ((1, 1), (1, 1)), ((2, 2), (2, 2)), ((3, 3), (3, 3)), ((2, 3), (3, 2)), ((2, 1), (1, 2)), ((2, 3, 4), (4, 3)), ((2, 3, 4), 4), ((3, 4), (2, 4, 3)), (4, (2, 4, 3))] # We have to use almost_equal here because the result of numpy's dot # does not always agree to the last bit with a naive implementation. # (This is probably due to their usage of SSE or parallelization.) # # On my machine in summer 2012 with Python 2.7 and 3.2 the program # # import numpy as np # a = np.array([13.2, 14.3, 15.4, 16.5]) # b = np.array([-5.0, -3.9, -2.8, -1.7]) # r = np.dot(a, b) # rr = sum(x * y for x, y in zip(a, b)) # print(r - rr) # # outputs 2.84217094304e-14. for sa, sb in shape_pairs: a = make(sa, dtype) b = make(sb, dtype) - 5 assert_almost_equal(ta.dot(ta.array(a), ta.array(b)), np.dot(a, b), 13) shape_pairs = [((), 2), (2, ()), (1, 2), (1, (2, 2)), ((1, 1), 2), ((2, 2), (3, 2)), ((2, 3, 2), (4, 3)), ((2, 3, 4), 3), ((3, 3), (2, 4, 3)), (3, (2, 4, 3))] for sa, sb in shape_pairs: a = make(sa, dtype) b = make(sb, dtype) - 5 raises(ValueError, ta.dot, ta.array(a.tolist()), ta.array(b.tolist())) raises(ValueError, ta.dot, ta.array(a), ta.array(b))
def test_dot(): # Check acceptance of non-tinyarray arguments. assert_equal(ta.dot([1, 2], (3, 4)), 11) for dtype in dtypes: shape_pairs = [(1, 1), (2, 2), (3, 3), (0, 0), (0, (0, 1)), ((0, 1), 1), (0, (0, 2)), ((0, 2), 2), (1, (1, 2)), ((2, 1), 1), (2, (2, 1)), ((1, 2), 2), (2, (2, 3)), ((3, 2), 2), ((1, 1), (1, 1)), ((2, 2), (2, 2)), ((3, 3), (3, 3)), ((2, 3), (3, 2)), ((2, 1), (1, 2)), ((2, 3, 4), (4, 3)), ((2, 3, 4), 4), ((3, 4), (2, 4, 3)), (4, (2, 4, 3))] # We have to use almost_equal here because the result of numpy's dot # does not always agree to the last bit with a naive implementation. # (This is probably due to their usage of SSE or parallelization.) # # On my machine in summer 2012 with Python 2.7 and 3.2 the program # # import numpy as np # a = np.array([13.2, 14.3, 15.4, 16.5]) # b = np.array([-5.0, -3.9, -2.8, -1.7]) # r = np.dot(a, b) # rr = sum(x * y for x, y in zip(a, b)) # print(r - rr) # # outputs 2.84217094304e-14. for sa, sb in shape_pairs: a = make(sa, dtype) b = make(sb, dtype) - 5 assert_almost_equal(ta.dot(ta.array(a), ta.array(b)), np.dot(a, b), 13) shape_pairs = [((), 2), (2, ()), (1, 2), (1, (2, 2)), ((1, 1), 2), ((2, 2), (3, 2)), ((2, 3, 2), (4, 3)), ((2, 3, 4), 3), ((3, 3), (2, 4, 3)), (3, (2, 4, 3))] for sa, sb in shape_pairs: a = make(sa, dtype) b = make(sb, dtype) - 5 assert_raises(ValueError, ta.dot, ta.array(a.tolist()), ta.array(b.tolist())) assert_raises(ValueError, ta.dot, ta.array(a), ta.array(b))
def __init__(self, prim_vecs, basis, name='', norbs=None): prim_vecs = ta.array(prim_vecs, float) if prim_vecs.ndim != 2: raise ValueError('`prim_vecs` must be a 2d array-like object.') dim = prim_vecs.shape[1] if name is None: name = '' if isinstance(name, str): name = [name + str(i) for i in range(len(basis))] if prim_vecs.shape[0] > dim: raise ValueError('Number of primitive vectors exceeds ' 'the space dimensionality.') basis = ta.array(basis, float) if basis.ndim != 2: raise ValueError('`basis` must be a 2d array-like object.') if basis.shape[1] != dim: raise ValueError('Basis dimensionality does not match ' 'the space dimensionality.') try: norbs = list(norbs) if len(norbs) != len(basis): raise ValueError('Length of `norbs` is not the same as ' 'the number of basis vectors') except TypeError: norbs = [norbs] * len(basis) self.sublattices = [Monatomic(prim_vecs, offset, sname, norb) for offset, sname, norb in zip(basis, name, norbs)] # Sequence of primitive vectors of the lattice. self._prim_vecs = prim_vecs # Precalculation of auxiliary arrays for real space calculations. self.reduced_vecs, self.transf = lll.lll(prim_vecs) self.voronoi = ta.dot(lll.voronoi(self.reduced_vecs), self.transf)
def __init__(self, prim_vecs, basis, name='', norbs=None): prim_vecs = ta.array(prim_vecs, float) _check_prim_vecs(prim_vecs) dim = prim_vecs.shape[1] if name is None: name = '' if isinstance(name, str): name = [name + str(i) for i in range(len(basis))] basis = ta.array(basis, float) if basis.ndim != 2: raise ValueError('`basis` must be a 2d array-like object.') if basis.shape[1] != dim: raise ValueError('Basis dimensionality does not match ' 'the space dimensionality.') try: norbs = list(norbs) if len(norbs) != len(basis): raise ValueError('Length of `norbs` is not the same as ' 'the number of basis vectors') except TypeError: norbs = [norbs] * len(basis) self.sublattices = [ Monatomic(prim_vecs, offset, sname, norb) for offset, sname, norb in zip(basis, name, norbs) ] # Sequence of primitive vectors of the lattice. self._prim_vecs = prim_vecs # Precalculation of auxiliary arrays for real space calculations. self.reduced_vecs, self.transf = lll.lll(prim_vecs) self.voronoi = ta.dot(lll.voronoi(self.reduced_vecs), self.transf)
def which(self, site): det_x_inv_m_part, det_m = self._get_site_family_data(site.family)[-2:] if isinstance(site, system.Site): result = ta.dot(det_x_inv_m_part, site.tag) // det_m elif isinstance(site, system.SiteArray): result = np.dot(det_x_inv_m_part, site.tags.transpose()) // det_m else: raise TypeError("'site' must be a Site or a SiteArray") return -result if self.is_reversed else result
def subgroup(self, *generators): """Return the subgroup generated by a sequence of group elements. Parameters ---------- *generators: sequence of int Each generator must have length ``self.num_directions``. """ generators = ta.array(generators) if generators.dtype != int: raise ValueError('Generators must be sequences of integers.') return TranslationalSymmetry(*ta.dot(generators, self.periods))
def vec(self, int_vec): """ Return the coordinates of a Bravais lattice vector in real space. Parameters ---------- vec : integer vector Returns ------- output : real vector """ return ta.dot(int_vec, self._prim_vecs)
def act(self, element, a, b=None): is_site = isinstance(a, system.Site) # Tinyarray for small arrays (single site) else numpy array_mod = ta if is_site else np element = array_mod.array(element) if not np.issubdtype(element.dtype, np.integer): raise ValueError("group element must be a tuple of integers") if (len(element.shape) == 2 and is_site): raise ValueError("must provide a single group element when " "acting on single sites.") if (len(element.shape) == 1 and not is_site): # We can act on a whole SiteArray with a single group element # using numpy broadcasting. element = element.reshape(1, -1) m_part = self._get_site_family_data(a.family)[0] try: delta = array_mod.dot(m_part, element) except ValueError: msg = 'Expecting a {0}-tuple group element, but got `{1}` instead.' raise ValueError(msg.format(self.num_directions, element)) if self.is_reversed: delta = -delta if b is None: if is_site: return system.Site(a.family, a.tag + delta, True) else: return system.SiteArray(a.family, a.tags + delta.transpose()) elif b.family == a.family: if is_site: return (system.Site(a.family, a.tag + delta, True), system.Site(b.family, b.tag + delta, True)) else: return (system.SiteArray(a.family, a.tags + delta.transpose()), system.SiteArray(b.family, b.tags + delta.transpose())) else: m_part = self._get_site_family_data(b.family)[0] try: delta2 = ta.dot(m_part, element) except ValueError: msg = ('Expecting a {0}-tuple group element, ' 'but got `{1}` instead.') raise ValueError(msg.format(self.num_directions, element)) if self.is_reversed: delta2 = -delta2 if is_site: return (system.Site(a.family, a.tag + delta, True), system.Site(b.family, b.tag + delta2, True)) else: return (system.SiteArray(a.family, a.tags + delta.transpose()), system.SiteArray(b.family, b.tags + delta2.transpose()))
def has_subgroup(self, other): if isinstance(other, system.NoSymmetry): return True elif not isinstance(other, TranslationalSymmetry): raise ValueError("Unknown symmetry type.") if other.periods.shape[1] != self.periods.shape[1]: return False # Mismatch of spatial dimensionalities. inv = np.linalg.pinv(self.periods) factors = np.dot(other.periods, inv) # Absolute tolerance is correct in the following since we want an error # relative to the closest integer. return (np.allclose(factors, np.round(factors), rtol=0, atol=1e-8) and np.allclose(ta.dot(factors, self.periods), other.periods))
def f(*args, **kwargs): a, b, *args = args assert not (args and kwargs) sym_b = sym.act(elem, b) k = [kwargs[k] for k in momenta] if kwargs else args[mnp:] phase = cmath.exp(1j * ta.dot(elem, k)) if not callable(val): v = val elif kwargs: if not takes_kwargs: kwargs = {p: kwargs[p] for p in extra_params} v = val(a, sym_b, **kwargs) else: v = val(a, sym_b, *args[:mnp]) return phase * v
def __init__(self, prim_vecs, offset=None, name='', norbs=None): prim_vecs = ta.array(prim_vecs, float) if prim_vecs.ndim != 2: raise ValueError('``prim_vecs`` must be a 2d array-like object.') dim = prim_vecs.shape[1] if name is None: name = '' if prim_vecs.shape[0] > dim: raise ValueError('Number of primitive vectors exceeds ' 'the space dimensionality.') if offset is None: offset = ta.zeros(dim) else: offset = ta.array(offset, float) if offset.shape != (dim, ): raise ValueError('Dimensionality of offset does not match ' 'that of the space.') msg = '{0}({1}, {2}, {3}, {4})' cl = self.__module__ + '.' + self.__class__.__name__ canonical_repr = msg.format(cl, short_array_repr(prim_vecs), short_array_repr(offset), repr(name), repr(norbs)) super().__init__(canonical_repr, name, norbs) self.sublattices = [self] self._prim_vecs = prim_vecs self.inv_pv = ta.array(np.linalg.pinv(prim_vecs)) self.offset = offset # Precalculation of auxiliary arrays for real space calculations. self.reduced_vecs, self.transf = lll.lll(prim_vecs) self.voronoi = ta.dot(lll.voronoi(self.reduced_vecs), self.transf) self.dim = dim self.lattice_dim = len(prim_vecs) if name != '': msg = "<Monatomic lattice {0}{1}>" orbs = ' with {0} orbitals'.format( self.norbs) if self.norbs else '' self.cached_str = msg.format(name, orbs) else: msg = "<unnamed Monatomic lattice, vectors {0}, origin [{1}]{2}>" orbs = ', with {0} orbitals'.format(norbs) if norbs else '' self.cached_str = msg.format(short_array_str(self._prim_vecs), short_array_str(self.offset), orbs)
def __init__(self, prim_vecs, offset=None, name='', norbs=None): prim_vecs = ta.array(prim_vecs, float) if prim_vecs.ndim != 2: raise ValueError('``prim_vecs`` must be a 2d array-like object.') dim = prim_vecs.shape[1] if name is None: name = '' if prim_vecs.shape[0] > dim: raise ValueError('Number of primitive vectors exceeds ' 'the space dimensionality.') if offset is None: offset = ta.zeros(dim) else: offset = ta.array(offset, float) if offset.shape != (dim,): raise ValueError('Dimensionality of offset does not match ' 'that of the space.') msg = '{0}({1}, {2}, {3}, {4})' cl = self.__module__ + '.' + self.__class__.__name__ canonical_repr = msg.format(cl, short_array_repr(prim_vecs), short_array_repr(offset), repr(name), repr(norbs)) super().__init__(canonical_repr, name, norbs) self.sublattices = [self] self._prim_vecs = prim_vecs self.inv_pv = ta.array(np.linalg.pinv(prim_vecs)) self.offset = offset # Precalculation of auxiliary arrays for real space calculations. self.reduced_vecs, self.transf = lll.lll(prim_vecs) self.voronoi = ta.dot(lll.voronoi(self.reduced_vecs), self.transf) self.dim = dim self.lattice_dim = len(prim_vecs) if name != '': msg = "<Monatomic lattice {0}{1}>" orbs = ' with {0} orbitals'.format(self.norbs) if self.norbs else '' self.cached_str = msg.format(name, orbs) else: msg = "<unnamed Monatomic lattice, vectors {0}, origin [{1}]{2}>" orbs = ', with {0} orbitals'.format(norbs) if norbs else '' self.cached_str = msg.format(short_array_str(self._prim_vecs), short_array_str(self.offset), orbs)
def _mul(R1, R2): # Cached multiplication of spatial parts. if is_sympy_matrix(R1) and is_sympy_matrix(R2): # If spatial parts are sympy matrices, use cached multiplication. R = R1 * R2 elif not (is_sympy_matrix(R1) or is_sympy_matrix(R2)): # If arrays, use dot R = ta.dot(R1, R2) elif ((is_sympy_matrix(R1) or is_sympy_matrix(R2)) and (isinstance(R1, ta.ndarray_int) or isinstance(R2, ta.ndarray_int))): # Multiplying sympy and integer tinyarray is ok, should result in sympy R = sympy.ImmutableMatrix(R1) * sympy.ImmutableMatrix(R2) else: raise ValueError("Mixing of sympy and floating point in the spatial part R is not allowed. " "To avoid this error, make sure that all PointGroupElements are initialized " "with either floating point arrays or sympy matrices as rotations. " "Integer arrays are allowed in both cases.") R = _make_int(R) return R
def disk(site): pos = site.pos return ta.dot(pos, pos) < 13
def halfplane(site): return ta.dot(site.pos - (-1, 1), (-0.9, 0.63)) > 0
def vec_norm(vec): return np.sqrt(ta.dot(vec, vec))
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 f(a, *args): phase = cmath.exp(1j * ta.dot(elem, args[mnp:])) v = val(a, sym.act(elem, a), *args[:mnp]) if callable(val) else val pv = phase * v return pv + herm_conj(pv)
def f(a, b, *args): phase = cmath.exp(1j * ta.dot(elem, args[mnp:])) v = val(a, sym.act(elem, b), *args[:mnp]) if callable(val) else val return phase * v
def make_lattice(a, theta): x = ta.dot(R(theta), (a, 0)) y = ta.dot(R(theta), (0, a)) return kwant.lattice.general([x, y], norbs=1)
def pos(self, tag): """Return the real-space position of the site with a given tag.""" return ta.dot(tag, self._prim_vecs) + self.offset
def f(*args): a, b, *args = args phase = cmath.exp(1j * ta.dot(elem, args[mnp:])) v = val(a, sym.act(elem, b), *args[:mnp]) if callable(val) else val return phase * v
def which(self, site): det_x_inv_m_part, det_m = self._get_site_family_data(site.family)[-2:] result = ta.dot(det_x_inv_m_part, site.tag) // det_m return -result if self.is_reversed else result
def f(*args): a, *args = args phase = cmath.exp(1j * ta.dot(elem, args[mnp:])) v = val(a, sym.act(elem, a), *args[:mnp]) if callable(val) else val pv = phase * v return pv + herm_conj(pv)
def wire_shape(pos): rel_pos = pos - center projection = rel_pos - direction * ta.dot(direction, rel_pos) return sum(projection * projection) <= r_squared
def shape(site): d = site.pos - center dd = ta.dot(d, d) return dd <= rr
def make_lattice(a, salt='0'): theta_x = kwant.digest.uniform('x', salt=salt) * np.pi / 6 theta_y = kwant.digest.uniform('y', salt=salt) * np.pi / 6 x = ta.dot(R(theta_x), (a, 0)) y = ta.dot(R(theta_y), (0, a)) return kwant.lattice.general([x, y], norbs=1)
def plot_2d_bands(syst, k_x=31, k_y=31, params=None, mask_brillouin_zone=False, extend_bbox=0, file=None, show=True, dpi=None, fig_size=None, ax=None): """Plot 2D band structure of a wrapped around system. This function is primarily useful for systems that have translational symmetry vectors that are non-orthogonal (e.g. graphene). This function will properly plot the band structure in an orthonormal basis in k-space, as opposed to in the basis of reciprocal lattice vectors (which would produce a "skewed" Brillouin zone). If your system has orthogonal lattice vectors, you are probably better off using `kwant.plotter.spectrum`. Parameters ---------- syst : `kwant.system.FiniteSystem` A 2D system that was finalized from a Builder produced by `kwant.wraparound.wraparound`. Note that this *must* be a finite system; so `kwant.wraparound.wraparound` should have been called with ``keep=None``. k_x, k_y : int or sequence of float, default: 31 Either a number of sampling points, or a sequence of points at which the band structure is to be evaluated, in units of inverse length. params : dict, optional Dictionary of parameter names and their values, not including the momentum parameters. mask_brillouin_zone : bool, default: False If True, then the band structure will only be plotted over the first Brillouin zone. By default the band structure is plotted over a rectangular bounding box that contains the Brillouin zone. extend_bbox : float, default: 0 Amount by which to extend the region over which the band structure is plotted, expressed as a proportion of the Brillouin zone bounding box length. i.e. ``extend_bbox=0.1`` will extend the region by 10% (in all directions). file : string or file object, optional The output file. If None, output will be shown instead. show : bool, default: False Whether ``matplotlib.pyplot.show()`` is to be called, and the output is to be shown immediately. Defaults to `True`. dpi : float, optional Number of pixels per inch. If not set the ``matplotlib`` default is used. fig_size : tuple, optional Figure size `(width, height)` in inches. If not set, the default ``matplotlib`` value is used. ax : ``matplotlib.axes.Axes`` instance, optional If `ax` is not `None`, no new figure is created, but the plot is done within the existing Axes `ax`. in this case, `file`, `show`, `dpi` and `fig_size` are ignored. Returns ------- fig : matplotlib figure A figure with the output if `ax` is not set, else None. Notes ----- This function produces plots where the units of momentum are inverse length. This is contrary to `kwant.plotter.bands`, where the units of momentum are inverse lattice constant. If the lattice vectors for the symmetry of ``syst`` are not orthogonal, then part of the plotted band structure will be outside the first Brillouin zone (inside the bounding box of the brillouin zone). Setting ``mask_brillouin_zone=True`` will cause the plot to be truncated outside of the first Brillouin zone. See Also -------- kwant.plotter.spectrum """ if not hasattr(syst, '_wrapped_symmetry'): raise TypeError("Expecting a system that was produced by " "'kwant.wraparound.wraparound'.") if not isinstance(syst, system.FiniteSystem): msg = ("All symmetry directions must be wrapped around: specify " "'keep=None' when calling 'kwant.wraparound.wraparound'.") raise TypeError(msg) params = params or {} lat_ndim, space_ndim = syst._wrapped_symmetry.periods.shape if lat_ndim != 2: raise ValueError("Expected a system with a 2D translational symmetry.") if space_ndim != lat_ndim: raise ValueError("Lattice dimension must equal realspace dimension.") # columns of B are lattice vectors B = np.array(syst._wrapped_symmetry.periods).T # columns of A are reciprocal lattice vectors A = np.linalg.pinv(B).T ## calculate the bounding box for the 1st Brillouin zone # Get lattice points that neighbor the origin, in basis of lattice vectors reduced_vecs, transf = lll.lll(A.T) neighbors = ta.dot(lll.voronoi(reduced_vecs), transf) # Add the origin to these points. klat_points = np.concatenate(([[0] * lat_ndim], neighbors)) # Transform to cartesian coordinates and rescale. # Will be used in 'outside_bz' function, later on. klat_points = 2 * np.pi * np.dot(klat_points, A.T) # Calculate the Voronoi cell vertices vor = scipy.spatial.Voronoi(klat_points) around_origin = vor.point_region[0] bz_vertices = vor.vertices[vor.regions[around_origin]] # extract bounding box k_max = np.max(np.abs(bz_vertices), axis=0) ## build grid along each axis, if needed ks = [] for k, km in zip((k_x, k_y), k_max): k = np.array(k) if not k.shape: if extend_bbox: km += km * extend_bbox k = np.linspace(-km, km, k) ks.append(k) # TODO: It is very inefficient to call 'momentum_to_lattice' once for # each point (for trivial Hamiltonians 60% of the time is spent # doing this). We should instead transform the whole grid in one call. def momentum_to_lattice(k): k, residuals = scipy.linalg.lstsq(A, k)[:2] if np.any(abs(residuals) > 1e-7): raise RuntimeError("Requested momentum doesn't correspond" " to any lattice momentum.") return k def ham(k_x, k_y=None, **params): # transform into the basis of reciprocal lattice vectors k = momentum_to_lattice([k_x] if k_y is None else [k_x, k_y]) p = dict(zip(syst._momentum_names, k), **params) return syst.hamiltonian_submatrix(params=p, sparse=False) def outside_bz(k_x, k_y, **_): dm = scipy.spatial.distance_matrix(klat_points, [[k_x, k_y]]) return np.argmin(dm) != 0 # is origin no closest 'klat_point' to 'k'? fig = plotter.spectrum(ham, x=('k_x', ks[0]), y=('k_y', ks[1]) if lat_ndim == 2 else None, params=params, mask=(outside_bz if mask_brillouin_zone else None), file=file, show=show, dpi=dpi, fig_size=fig_size, ax=ax) return fig
lat = kwant.lattice.triangular() sys = kwant.Builder() sys[lat.shape(circle, (0, 0))] = 0 sys[lat.neighbors()] = 1 lead_dirs = [lat.vec((-3, 1)), lat.vec((0, -1)), lat.vec((2, -1))] for d in lead_dirs: lead = kwant.Builder(kwant.TranslationalSymmetry(d)) lead[lat.wire((0, 0), 2.1)] = 0 lead[lat.neighbors()] = 1 sys.attach_lead(lead) fig = kwant.plot(sys, show=False) ax = pyplot.gca() pyplot.text(-2, 5.5, 'scattering region', size=15) pyplot.text(-10, -1, 'lead 0', color='red', size=15) pyplot.text(-3, -7.7, 'lead 1', color='red', size=15) pyplot.text(5.5, 0, 'lead 2', color='red', size=15) for dir, offset in zip(lead_dirs, [10.5, 8, 8.6]): dir = dir / math.sqrt(ta.dot(dir, dir)) for i in [0, 0.4, 0.8]: ax.add_artist(Circle(dir * (offset + i), 0.06, fc='k')) pyplot.axis('off') pyplot.xlim((-11, 9)) pyplot.ylim((-8, 6)) fig.tight_layout() fig.set_size_inches(*(5, 3.75)) fig.savefig("tbsys.pdf")