system.py 16.7 KB
Newer Older
1
# Copyright 2011-2013 Kwant authors.
2
#
Christoph Groth's avatar
Christoph Groth committed
3
4
# This file is part of Kwant.  It is subject to the license terms in the file
# LICENSE.rst found in the top-level directory of this distribution and at
5
# http://kwant-project.org/license.  A list of Kwant authors can be found in
Christoph Groth's avatar
Christoph Groth committed
6
# the file AUTHORS.rst at the top-level directory of this distribution and at
7
8
# http://kwant-project.org/authors.

9
"""Low-level interface of systems"""
10
11
12

__all__ = ['System', 'FiniteSystem', 'InfiniteSystem']

Anton Akhmerov's avatar
Anton Akhmerov committed
13
import abc
14
import warnings
Anton Akhmerov's avatar
Anton Akhmerov committed
15
from copy import copy
16
from . import _system
17
from ._common  import deprecate_args
18

Anton Akhmerov's avatar
Anton Akhmerov committed
19

20
class System(metaclass=abc.ABCMeta):
21
22
    """Abstract general low-level system.

23
24
    Attributes
    ----------
25
26
    graph : kwant.graph.CGraph
        The system graph.
27
28
29
30
31
32
    site_ranges : None or sorted sequence of triples of integers
        If provided, encodes ranges of sites that have the same number of
        orbitals. Each triple consists of ``(first_site, norbs, orb_offset)``:
        the first site in the range, the number of orbitals on each site in the
        range, and the offset of the first orbital of the first site in the
        range.  In addition, the final triple should have the form
33
        ``(graph.num_nodes, 0, tot_norbs)`` where ``tot_norbs`` is the
34
        total number of orbitals in the system.
35
36
37
    parameters : frozenset of strings
        The names of the parameters on which the system depends. This attribute
        is provisional and may be changed in a future version of Kwant
38
39
40
41
42
43

    Notes
    -----
    The sites of the system are indexed by integers ranging from 0 to
    ``self.graph.num_nodes - 1``.

44
    Optionally, a class derived from ``System`` can provide a method ``pos`` which
45
    is assumed to return the real-space position of a site given its index.
46
47
48
49
50
51
52

    Due to the ordering semantics of sequences, and the fact that a given
    ``first_site`` can only appear *at most once* in ``site_ranges``,
    ``site_ranges`` is ordered according to ``first_site``.

    Consecutive elements in ``site_ranges`` are not required to have different
    numbers of orbitals.
53
54
    """
    @abc.abstractmethod
55
    def hamiltonian(self, i, j, *args, params=None):
56
        """Return the hamiltonian matrix element for sites ``i`` and ``j``.
57

58
        If ``i == j``, return the on-site Hamiltonian of site ``i``.
59

60
        if ``i != j``, return the hopping between site ``i`` and ``j``.
61
62

        Hamiltonians may depend (optionally) on positional and
63
64
65
66
        keyword arguments.

        Providing positional arguments via 'args' is deprecated,
        instead, provide named parameters as a dictionary via 'params'.
67
68
69
        """
        pass

70
    @deprecate_args
71
    def discrete_symmetry(self, args, *, params=None):
72
73
74
75
76
        """Return the discrete symmetry of the system.

        Providing positional arguments via 'args' is deprecated,
        instead, provide named parameters as a dictionary via 'params'.
        """
77
78
79
80
81
        # Avoid the circular import.
        from .physics import DiscreteSymmetry
        return DiscreteSymmetry()


82
83
84
85
86
87
88
89
90
91
92
93
94
95
    def __str__(self):
        items = [
            # (format, extractor, skip if info not present)
            ('{} sites', self.graph.num_nodes, False),
            ('{} hoppings', self.graph.num_edges, False),
            ('parameters: {}', tuple(self.parameters), True),
        ]
        # Skip some information when it's not present (parameters)
        details = [fmt.format(info) for fmt, info, skip in items
                   if (info or not skip)]
        details = ', and '.join((', '.join(details[:-1]), details[-1]))
        return '<{} with {}>'.format(self.__class__.__name__, details)


96
# Add a C-implemented function as an unbound method to class System.
97
System.hamiltonian_submatrix = _system.hamiltonian_submatrix
98
99


Joseph Weston's avatar
Joseph Weston committed
100
class FiniteSystem(System, metaclass=abc.ABCMeta):
101
102
    """Abstract finite low-level system, possibly with leads.

103
104
    Attributes
    ----------
Christoph Groth's avatar
Christoph Groth committed
105
    leads : sequence of leads
106
107
        Each lead has to provide a method ``selfenergy`` that has
        the same signature as `InfiniteSystem.selfenergy` (without the
108
109
110
111
112
        ``self`` parameter), and must have property ``parameters``:
        a collection of strings that name the system parameters (
        though this requirement is provisional and may be removed in
        a future version of Kwant).
        It may also provide ``modes`` that has the
113
114
        same signature as `InfiniteSystem.modes` (without the ``self``
        parameter).
115
    lead_interfaces : sequence of sequences of integers
116
117
        Each sub-sequence contains the indices of the system sites
        to which the lead is connected.
118
119
120
121
    lead_paddings : sequence of sequences of integers
        Each sub-sequence contains the indices of the system sites
        that belong to the lead, and therefore have the same onsite as the lead
        sites, and are connected by the same hoppings as the lead sites.
122
123
124
125
    parameters : frozenset of strings
        The names of the parameters on which the system depends. This does
        not include the parameters for any leads. This attribute
        is provisional and may be changed in a future version of Kwant
126
127
128

    Notes
    -----
129
130
    The length of ``leads`` must be equal to the length of ``lead_interfaces``
    and ``lead_paddings``.
131

132
    For lead ``n``, the method leads[n].selfenergy must return a square matrix
133
    whose size is ``sum(len(self.hamiltonian(site, site)) for site in
134
    self.lead_interfaces[n])``. The output of ``leads[n].modes`` has to be a
135
    tuple of `~kwant.physics.PropagatingModes`, `~kwant.physics.StabilizedModes`.
136
137

    Often, the elements of `leads` will be instances of `InfiniteSystem`.  If
138
139
    this is the case for lead ``n``, the sites ``lead_interfaces[n]`` match
    the first ``len(lead_interfaces[n])`` sites of the InfiniteSystem.
140

141
142
    """

143
    @deprecate_args
144
    def precalculate(self, energy=0, args=(), leads=None,
145
                     what='modes', *, params=None):
Anton Akhmerov's avatar
Anton Akhmerov committed
146
147
148
149
150
151
152
153
154
155
156
157
158
        """
        Precalculate modes or self-energies in the leads.

        Construct a copy of the system, with the lead modes precalculated,
        which may significantly speed up calculations where only the system
        is changing.

        Parameters
        ----------
        energy : float
            Energy at which the modes or self-energies have to be
            evaluated.
        args : sequence
159
            Additional parameters required for calculating the Hamiltionians.
160
            Deprecated in favor of 'params' (and mutually exclusive with it).
161
        leads : sequence of integers or None
162
            Numbers of the leads to be precalculated. If ``None``, all are
Anton Akhmerov's avatar
Anton Akhmerov committed
163
            precalculated.
164
165
166
        what : 'modes', 'selfenergy', 'all'
            The quantitity to precompute. 'all' will compute both
            modes and self-energies. Defaults to 'modes'.
167
168
169
        params : dict, optional
            Dictionary of parameter names and their values. Mutually exclusive
            with 'args'.
Anton Akhmerov's avatar
Anton Akhmerov committed
170
171
172

        Returns
        -------
173
        syst : FiniteSystem
Anton Akhmerov's avatar
Anton Akhmerov committed
174
            A copy of the original system with some leads precalculated.
175
176
177
178
179
180

        Notes
        -----
        If the leads are precalculated at certain `energy` or `args` values,
        they might give wrong results if used to solve the system with
        different parameter values. Use this function with caution.
Anton Akhmerov's avatar
Anton Akhmerov committed
181
        """
182
183
184
185
186

        if what not in ('modes', 'selfenergy', 'all'):
            raise ValueError("Invalid value of argument 'what': "
                             "{0}".format(what))

Anton Akhmerov's avatar
Anton Akhmerov committed
187
188
        result = copy(self)
        if leads is None:
Joseph Weston's avatar
Joseph Weston committed
189
            leads = list(range(len(self.leads)))
Anton Akhmerov's avatar
Anton Akhmerov committed
190
191
192
193
194
195
        new_leads = []
        for nr, lead in enumerate(self.leads):
            if nr not in leads:
                new_leads.append(lead)
                continue
            modes, selfenergy = None, None
196
            if what in ('modes', 'all'):
197
                modes = lead.modes(energy, args, params=params)
198
199
            if what in ('selfenergy', 'all'):
                if modes:
200
                    selfenergy = modes[1].selfenergy()
201
                else:
202
                    selfenergy = lead.selfenergy(energy, args, params=params)
203
            new_leads.append(PrecalculatedLead(modes, selfenergy))
Anton Akhmerov's avatar
Anton Akhmerov committed
204
205
        result.leads = new_leads
        return result
206

207
    @deprecate_args
208
209
210
211
212
213
    def validate_symmetries(self, args=(), *, params=None):
        """Check that the Hamiltonian satisfies discrete symmetries.

        Applies `~kwant.physics.DiscreteSymmetry.validate` to the
        Hamiltonian, see its documentation for details on the return
        format.
214
215
216

        Providing positional arguments via 'args' is deprecated,
        instead, provide named parameters as a dictionary via 'params'.
217
218
219
220
221
        """
        symmetries = self.discrete_symmetry(args=args, params=params)
        ham = self.hamiltonian_submatrix(args, sparse=True, params=params)
        return symmetries.validate(ham)

222

Joseph Weston's avatar
Joseph Weston committed
223
class InfiniteSystem(System, metaclass=abc.ABCMeta):
224
    """Abstract infinite low-level system.
225

226
227
    An infinite system consists of an infinite series of identical cells.
    Adjacent cells are connected by identical inter-cell hoppings.
228

229
230
    Attributes
    ----------
231
232
    cell_size : integer
        The number of sites in a single cell of the system.
233
234
235

    Notes
    -----
236
237
    The system graph of an infinite systems contains a single cell, as well as
    the part of the previous cell which is connected to it.  The first
238
239
    `cell_size` sites form one complete single cell.  The remaining ``N`` sites
    of the graph (``N`` equals ``graph.num_nodes - cell_size``) belong to the
240
    previous cell.  They are included so that hoppings between cells can be
241
242
243
244
    represented.  The N sites of the previous cell correspond to the first
    ``N`` sites of the fully included cell.  When an ``InfiniteSystem`` is used
    as a lead, ``N`` acts also as the number of interface sites to which it
    must be connected.
245

246
    The drawing shows three cells of an infinite system.  Each cell consists
247
248
249
250
251
252
253
254
255
256
257
    of three sites.  Numbers denote sites which are included into the system
    graph.  Stars denote sites which are not included.  Hoppings are included
    in the graph if and only if they occur between two sites which are part of
    the graph::

            * 2 *
        ... | | | ...
            * 0 3
            |/|/|
            *-1-4

258
        <-- order of cells
259
260
261
262

    The numbering of sites in the drawing is one of the two valid ones for that
    infinite system.  The other scheme has the numbers of site 0 and 1
    exchanged, as well as of site 3 and 4.
263

264
    """
265
    @deprecate_args
266
    def cell_hamiltonian(self, args=(), sparse=False, *, params=None):
267
268
269
270
271
        """Hamiltonian of a single cell of the infinite system.

        Providing positional arguments via 'args' is deprecated,
        instead, provide named parameters as a dictionary via 'params'.
        """
Joseph Weston's avatar
Joseph Weston committed
272
        cell_sites = range(self.cell_size)
273
        return self.hamiltonian_submatrix(args, cell_sites, cell_sites,
274
                                          sparse=sparse, params=params)
275

276
    @deprecate_args
277
    def inter_cell_hopping(self, args=(), sparse=False, *, params=None):
278
279
280
281
282
        """Hopping Hamiltonian between two cells of the infinite system.

        Providing positional arguments via 'args' is deprecated,
        instead, provide named parameters as a dictionary via 'params'.
        """
Joseph Weston's avatar
Joseph Weston committed
283
284
        cell_sites = range(self.cell_size)
        interface_sites = range(self.cell_size, self.graph.num_nodes)
285
        return self.hamiltonian_submatrix(args, cell_sites, interface_sites,
286
                                          sparse=sparse, params=params)
287

288
    @deprecate_args
289
    def modes(self, energy=0, args=(), *, params=None):
290
        """Return mode decomposition of the lead
291

292
293
        See documentation of `~kwant.physics.PropagatingModes` and
        `~kwant.physics.StabilizedModes` for the return format details.
294
295
296
297
298
299

        The wave functions of the returned modes are defined over the
        *unit cell* of the system, which corresponds to the degrees of
        freedom on the first ``cell_sites`` sites of the system
        (recall that infinite systems store first the sites in the unit
        cell, then connected sites in the neighboring unit cell).
300
301
302

        Providing positional arguments via 'args' is deprecated,
        instead, provide named parameters as a dictionary via 'params'.
303
        """
304
        from . import physics   # Putting this here avoids a circular import.
305
306
307
        ham = self.cell_hamiltonian(args, params=params)
        hop = self.inter_cell_hopping(args, params=params)
        symmetries = self.discrete_symmetry(args, params=params)
308
309
        # Check whether each symmetry is broken.
        # If a symmetry is broken, it is ignored in the computation.
310
        broken = set(symmetries.validate(ham) + symmetries.validate(hop))
Tómas's avatar
Tómas committed
311
312
313
314
        attribute_names = {'Conservation law': 'projectors',
                          'Time reversal': 'time_reversal',
                          'Particle-hole': 'particle-hole',
                          'Chiral': 'chiral'}
315
        for name in broken:
316
317
            warnings.warn('Hamiltonian breaks ' + name +
                          ', ignoring the symmetry in the computation.')
Tómas's avatar
Tómas committed
318
319
320
            assert name in attribute_names, 'Inconsistent naming of symmetries'
            setattr(symmetries, attribute_names[name], None)

321
322
323
324
325
        shape = ham.shape
        assert len(shape) == 2
        assert shape[0] == shape[1]
        # Subtract energy from the diagonal.
        ham.flat[::ham.shape[0] + 1] -= energy
326
327
328
329
330

        # Particle-hole and chiral symmetries only apply at zero energy.
        if energy:
            symmetries.particle_hole = symmetries.chiral = None
        return physics.modes(ham, hop, discrete_symmetry=symmetries)
331

332
    @deprecate_args
333
    def selfenergy(self, energy=0, args=(), *, params=None):
334
335
        """Return self-energy of a lead.

336
        The returned matrix has the shape (s, s), where s is
337
338
        ``sum(len(self.hamiltonian(i, i)) for i in range(self.graph.num_nodes -
        self.cell_size))``.
339
340
341

        Providing positional arguments via 'args' is deprecated,
        instead, provide named parameters as a dictionary via 'params'.
342
        """
343
        from . import physics   # Putting this here avoids a circular import.
344
        ham = self.cell_hamiltonian(args, params=params)
345
346
347
348
349
        shape = ham.shape
        assert len(shape) == 2
        assert shape[0] == shape[1]
        # Subtract energy from the diagonal.
        ham.flat[::ham.shape[0] + 1] -= energy
350
351
        return physics.selfenergy(ham,
                                  self.inter_cell_hopping(args, params=params))
Anton Akhmerov's avatar
Anton Akhmerov committed
352

353
    @deprecate_args
354
355
356
357
358
359
    def validate_symmetries(self, args=(), *, params=None):
        """Check that the Hamiltonian satisfies discrete symmetries.

        Returns `~kwant.physics.DiscreteSymmetry.validate` applied
        to the onsite matrix and the hopping. See its documentation for
        details on the return format.
360
361
362

        Providing positional arguments via 'args' is deprecated,
        instead, provide named parameters as a dictionary via 'params'.
363
364
365
366
        """
        symmetries = self.discrete_symmetry(args=args, params=params)
        ham = self.cell_hamiltonian(args=args, sparse=True, params=params)
        hop = self.inter_cell_hopping(args=args, sparse=True, params=params)
367
368
        broken = set(symmetries.validate(ham) + symmetries.validate(hop))
        return list(broken)
369

Anton Akhmerov's avatar
Anton Akhmerov committed
370

371
class PrecalculatedLead:
372
    def __init__(self, modes=None, selfenergy=None):
Anton Akhmerov's avatar
Anton Akhmerov committed
373
374
375
376
        """A general lead defined by its self energy.

        Parameters
        ----------
377
        modes : (kwant.physics.PropagatingModes, kwant.physics.StabilizedModes)
Anton Akhmerov's avatar
Anton Akhmerov committed
378
379
380
381
382
383
            Modes of the lead.
        selfenergy : numpy array
            Lead self-energy.

        Notes
        -----
384
        At least one of ``modes`` and ``selfenergy`` must be provided.
Anton Akhmerov's avatar
Anton Akhmerov committed
385
386
387
388
389
        """
        if modes is None and selfenergy is None:
            raise ValueError("No precalculated values provided.")
        self._modes = modes
        self._selfenergy = selfenergy
390
391
392
        # Modes/Self-energy have already been evaluated, so there
        # is no parametric dependence anymore
        self.parameters = frozenset()
Anton Akhmerov's avatar
Anton Akhmerov committed
393

394
    @deprecate_args
395
    def modes(self, energy=0, args=(), *, params=None):
Anton Akhmerov's avatar
Anton Akhmerov committed
396
397
398
        if self._modes is not None:
            return self._modes
        else:
399
400
401
            raise ValueError("No precalculated modes were provided. "
                             "Consider using precalculate() with "
                             "what='modes' or what='all'")
Anton Akhmerov's avatar
Anton Akhmerov committed
402

403
    @deprecate_args
404
    def selfenergy(self, energy=0, args=(), *, params=None):
Anton Akhmerov's avatar
Anton Akhmerov committed
405
406
407
        if self._selfenergy is not None:
            return self._selfenergy
        else:
408
409
410
            raise ValueError("No precalculated selfenergy was provided. "
                             "Consider using precalculate() with "
                             "what='selfenergy' or what='all'")