Kwant 1.3.1 Documentation: C. W. Groth, M. Wimmer, A. R. Akhmerov, X. Waintal, Et Al
Kwant 1.3.1 Documentation: C. W. Groth, M. Wimmer, A. R. Akhmerov, X. Waintal, Et Al
1 documentation
Release 1.3.1
1 Preliminaries 1
1.1 About Kwant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 What’s new in Kwant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3 Installation of Kwant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.4 Authors of Kwant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.5 Citing Kwant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.6 Contributing to Kwant and reporting problems . . . . . . . . . . . . . . . . . . . . . . . . 17
1.7 Kwant license . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3 Core modules 85
3.1 kwant – Top level package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
3.2 kwant.builder – High-level construction of systems . . . . . . . . . . . . . . . . . . . . . 86
3.3 kwant.lattice – Bravais lattices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
3.4 kwant.plotter – Plotting of systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
3.5 kwant.solvers – Library of solvers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
3.6 kwant.operator – Operators and Observables . . . . . . . . . . . . . . . . . . . . . . . . 122
3.7 kwant.physics – Physics-related algorithms . . . . . . . . . . . . . . . . . . . . . . . . . 128
Bibliography 161
i
ii
CHAPTER
ONE
PRELIMINARIES
Kwant is a free (open source) Python package for numerical calculations on tight-binding models with
a strong focus on quantum transport. It is designed to be flexible and easy to use. Thanks to the use
of innovative algorithms, Kwant is often faster than other available codes, even those entirely written in
the low level FORTRAN and C/C++ languages.
Tight-binding models can describe a vast variety of systems and phenomena in quantum physics. There-
fore, Kwant can be used to simulate
• metals,
• graphene,
• topological insulators,
• quantum Hall effect,
• superconductivity,
• spintronics,
• molecular electronics,
• any combination of the above, and many other things.
Kwant can calculate
• transport properties (conductance, noise, scattering matrix),
• dispersion relations,
• modes,
• wave functions,
• various Green’s functions,
• out-of-equilibrium local quantities.
Other computations involving tight-binding Hamiltonians can be implemented easily.
This article explains the user-visible changes in Kwant 1.3.0, released on 19 May 2017. See also the full
list of changes up to the most recent bugfix release of the 1.3 series.
1
Kwant 1.3.1 documentation
Builders now have a fill method that fills a builder instance with copies of a template builder. This
can be used to “cut out” shapes from high-symmetry models, or to increase the symmetry period of a
lead.
Thus Kwant gains the new concept of a “model”. Models may be created manually, or with the new
function kwant.continuum.discretize (see next paragraph). There is also support for finalizing models
and e.g. calculating their band structure (see Finalizing builders with multiple translational symmetries).
The new sub-package continuum is a collection of tools for working with continuum models and for
discretizing them into tight-binding models. It aims at providing a handy interface to convert symbolic
Hamiltonians into a builder with N-D translational symmetry that can be use to calculate tight-binding
band structures or construct systems with different/lower symmetry. For example in just a few lines we
can construct a two-band model that exhibits a quantum anomalous spin Hall phase:
def make_model(a):
ham = ("alpha * (k_x * sigma_x - k_y * sigma_y)"
"+ (m + beta * kk) * sigma_z"
"+ (gamma * kk + U) * sigma_0")
subs = {"kk": "k_x**2 + k_y**2"}
return kwant.continuum.discretize(ham, locals=subs, grid_spacing=a)
Often one may wish to calculate quantities that are defined over sites of the system (such as charge
density, spin density along some axis etc), or over hoppings of the system (such as current or spin
current). With the introduction of the operator module it has now become much easier to calculate
such quantities. To obtain the regular density and current everywhere in a system due to a wavefunction
psi, one only needs to do the following:
syst = make_system().finalized()
psi = kwant.wave_function(syst)(0)[0]
Plotting of currents
Quantities defined on system hoppings (e.g. currents calculated using Current) can be directly plotted
as a streamplot over the system using kwant.plotter.current. This is similar to how kwant.plotter.
map can be used to plot quantities defined on sites. The example below shows edge states of a quantum
anomalous Hall phase of the two-band model shown in the above section:
2 Chapter 1. Preliminaries
Kwant 1.3.1 documentation
J = kwant.operator.Current(syst).bind(params=params)
current = sum(J(p) for p in psi)
kwant.plotter.current(syst, current)
30
20
10
10
20
30
40 30 20 10 0 10 20 30 40
Given a lead Hamiltonian that has a conservation law, it is now possible to construct lead modes that
have definite values of the conservation law. This is done by declaring projectors that block diagonalize
the Hamiltonian before the modes are computed. For a Hamiltonian that has one or more of the three
fundamental discrete symmetries (time-reversal symmetry, particle-hole symmetry and chiral symmetry),
it is now possible to declare the symmetries in Kwant. The symmetries are then used to construct
scattering states that are properly related by symmetry. The discrete symmetries may be combined with
conservation laws, such that if different blocks of the Hamiltonian are related by a discrete symmetry,
the lead modes are computed to reflect this.
See the updated tutorial: Superconductors: orbital degrees of freedom, conservation laws and symmetries
In Kwant < 1.3 whenever Hamiltonian values were provided as functions, they all had to take the
same extra parameters (after the site(s)) regardless of whether or not they actually used them at all.
For example, if we had some onsite potential and a magnetic field that we model using the Peierls
substitution, we would have to define our value functions like so:
return V
This was because previously extra arguments were provided to the system by passing them as a sequence
via the args parameter to various Kwant functions (e.g. kwant.smatrix or hamiltonian_submatrix).
In Kwant 1.3 it is now possible for value functions to depend on different parameters, e.g.:
If you make use of this feature then you must in addition pass your arguments via the params parameter.
The value provided to params must be a dict that maps parameter names to values, e.g.:
Passing a dictionary of parameters via params is now the recommended way to provide parameters to
the system.
The kernel polynomial method is now implemented within Kwant to obtain the density of states or, more
generally, the spectral density of a given operator acting on a system or Hamiltonian.
See the tutorial: Calculating spectral density with the kernel polynomial method
See the reference documentation: kwant.kpm – Kernel Polynomial Method
While it remains impossible to finalize a builder with more than a single direction of translational
symmetry, the wraparound module has been added as a temporary work-around until the above limitation
gets lifted.
The function wraparound transforms all (or all but one) translational symmetries of a given builder into
named momentum parameters k_x, k_y, etc. This makes it easy to compute transport through systems
with periodic boundary conditions or across infinite planes.
Plotting the 2-d band structure of graphene is now as straightforward as:
lat = kwant.lattice.honeycomb()
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
wrapped = kwant.wraparound.wraparound(bulk).finalized()
kwant.wraparound.plot_2d_bands(wrapped)
4 Chapter 1. Preliminaries
Kwant 1.3.1 documentation
In Python 3 the internal ordering of dictionaries is not deterministic. This meant that running a Kwant
script twice would produce systems with different ordering of sites, which leads to non-reproducible
calculations. Now, sites in finalized builders are always ordered first by their site family, then by their
tag.
Coincidentally, this means that you can plot a wavefunction in a simple 1D system by just saying:
lattice_1D = chain()
syst = make_system(lattice_1D)
h = syst.hamiltonian_submatrix()
pyplot.plot(np.eigs(h)[1][0])
attach_lead() can now handle leads with greater than nearest-neighbor hoppings
When attaching a lead with greater than nearest-neighbor hoppings, the symmetry period of the finalized
lead is suitably extended and the unit cell size is increased.
Pickling support
The name of the build configuration file, build.conf by default, is now configurable with the
--configfile=PATH option to setup.py. (This makes build configuration usable with the pip tool.) The
build configuration as specified in this file is now more general, allowing to modify any build parameter
for any of the compiled extensions contained in Kwant. See the Installation instructions for details.
Given a site, the method neighbors of Builder returns an iterator over sites that are connected by a
hopping to the provided site. This is in contrast to previous versions of Kwant, where the neighbors were
yielded not of the provided site, but of it’s image in the fundamental domain.
This change is documented here for completeness. We expect that the vast majority of users of Kwant
will not be affected by it.
This article explains the user-visible changes in Kwant 1.2.2, released on 9 December 2015. See also the
full list of changes up to the most recent bugfix release of the 1.2 series.
Kwant 1.2 is identical to Kwant 1.1 except that it has been updated to run on Python 3.4 and above.
Bugfix releases for the 1.1 and 1.2 series will mirror each other, i.e. 1.1.3 and 1.2.3 will fix the same bugs.
Starting with Kwant 1.2, all Kwant development will target Python 3. We plan, however, to maintain
Python 2 support with the 1.1 series for several years.
Even though the interface and functionality of Kwant remain unchanged between versions 1.1 and 1.2,
scripts using Kwant need to be updated to Python 3. This can be done by running the automatic
conversion tool on the command line:
2to3 -w example.py
(The above command will rename the original file to example.py.bak.) The necessary changes are
typically only superficial, as described in What’s New In Python 3.0.
This article explains the user-visible changes in Kwant 1.1.0, released on 21 October 2015. See also the
full list of changes up to the most recent bugfix release of the 1.1 series.
Kwant’s convention is that momenta are positive in the direction of TranslationalSymmetry. While
the momenta returned by modes did respect this convention, the momenta read off the band structure
as given by Bands had the wrong sign. This has been fixed now.
Before actually attaching a lead to a builder, the method attach_lead of Builder prepares a “nice”
interface by adding “missing” sites such that the first unit cell of the lead is completely connected with
the system under construction. These sites and their hoppings are taken over from the lead.
By setting the new option add_cells, attach_lead can now be told to add in addition any number of
complete unit cells of the lead to the system before attaching it. Among other things, this can be useful
for
• controlling the hopping between the lead and the system (Leads are always attached with their
inter-unit-cell hopping to the system, but absorbing one lead unit cell into the system allows to
control this),
• creating a buffer for long range disorder present in the system to die away before the translation-
invariant lead begins.
To support these applications, attach_lead now returns a list of all the sites that have been added to
the system. Creating a buffer for disorder can be thus done as follows:
Note how we set the onsite Hamiltonians of the sites that have been added to the value used in the
system.
SMatrix and GreensFunction have each gained a method conductance_matrix that returns the matrix
𝐺 such that 𝐼 = 𝐺𝑉 where 𝐼 and 𝑉 are, respectively, the vectors of currents and voltages for all the
leads. This matrix is useful for calculating non-local resistances. See Section 2.4 of the book by S. Datta.
If smatrix or greens_function have been called with check_hermicity=True (on by default) and a
restricted number of leads in the out_leads and in_leads parameters, calls to transmission and
conductance_matrix will work whenever it is possible to deduce the result from current conservation.
This allows leaving out one lead (preferably the widest) from out_leads and in_leads, and still to
calculate all transmission probabilities. Doing so has been measured to speed up computations by 20%
in some cases.
6 Chapter 1. Preliminaries
Kwant 1.3.1 documentation
The error messages (exceptions) that appear when the Kwant interface is used incorrectly have been
improved in many cases. For example, if instead of
builder[lattice(0, 1)] = 1
one writes
builder[(0, 1)] = 1
This option which already existed for kwant.plotter.plot is now also available for kwant.plotter.map.
This article explains the new features in Kwant 1.0 compared to Kwant 0.2. Kwant 1.0 was released on
9 September 2013. Please consult the full list of changes in Kwant for all the changes up to the most
recent bugfix release.
Lattices now have a method neighbors, which calculates all the n-th shortest possible hoppings on this
lattice. This replaces the nearest attribute that some lattices used to have.
shape uses an improved flood-fill algorithm, making it work better on narrow ribbons (which were
sometimes buggy before with non-square lattices). Additionally, it was made symmetry-aware: If shape
is used with a lead, the shape does not have to be limited along the lead direction anymore. In fact, if
the shape function does not have the same symmetry as the lead, the result may be unexpected, so it is
highly recommended to use shape functions that have the same symmetry as the lead.
closest now returns an exact, and not approximate closest point. A new method n_closest was added,
which returns the n closest lattice points.
The Builder method possible_hoppings has been rendered obsolete. Where previously one would
have had
syst[lat.neighbors()] = t
This is possible because Builder now accepts functions as keys in addition to Site objects and tuples
of them (hoppings). These functions are expected to yield either sites or hoppings, when given a builder
instance as the sole argument. The use of such keys is to implement sets of sites or hoppings that depend
on what is already present in the builder, such as HoppingKind. In the above example, lat.neighbors()
is a list of HoppingKind objects.
Some renames
• site groups are now called site families. This affects all the names that used to contain “group” or
“groups”.
• lead slices are now referred to as lead cells: This affects all names that used to contain “slice” or
“slices” in the context of leads.
• self_energy has been renamed to selfenergy in all cases, most notably in kwant.physics.
selfenergy.
• wave_func has been renamed to wave_function,
• MonatomicLattice has been renamed to Monatomic,
• PolyatomicLattice has been renamed to Polyatomic.
• solve was split into two functions: smatrix, and greens_function. The former calculates the
scattering matrix, the latter the retarded Green’s function between the sites adjacent to the leads.
It is temporarily not possible to mix self-energy and modes leads within the same system.
• The object that contained the results, BlockResult was also split into SMatrix and
GreensFunction.
A convenience function bands for quick plotting of band structure was implemented.
In order to make naming more consistent, kwant.make_lattice was renamed and can be found now as
kwant.lattice.general. Classes Chain, Square, and Honeycomb from lattice were made functions
chain, square, and honeycomb.
In previous versions if one executed a = kwant.lattice.square(); b = kwant.lattice.square()
then a and b were actually different lattices. This often led to confusions in more convoluted use cases,
so this behavior was changed. Now two site families created with the same parameters are actually
indistinguishable by Kwant. If it is desired to make two site families which have the same geometry,
but mean different things, as for instance in Superconductors: orbital degrees of freedom, conservation
laws and symmetries, then the name argument has to be used when creating a lattice, e.g. a = kwant.
lattice.square(name='a'); b = kwant.lattice.square(name='b').
Parameters to Hamiltonian
Kwant now allows the Hamiltonian matrix elements to be described with functions that depend on an
arbitrary number of parameters in addition to the sites on which they are defined.
Previously, functions defining the Hamiltonian matrix elements had to have the following prototypes:
def onsite(site):
...
If the Hamiltonian elements need to depend on some other external parameters (e.g. magnetic field) then
those had to be provided by some other means than regular function parameters (e.g. global variables).
Now the value functions may accept arbitrary arguments after the Site arguments. These extra arguments
can be specified when smatrix is called by setting the arguments:
8 Chapter 1. Preliminaries
Kwant 1.3.1 documentation
args A tuple of values to be passed as the positional arguments to the Hamiltonian value functions (not
including the Site arguments).
For example, if the hopping and onsite Hamiltonian value functions have the following prototype:
then the values of t, B and pot for which to solve the system can be passed to smatrix like this:
kwant.smatrix(syst, energy,
args=(2., 3., 4.))
With many parameters it can be less error-prone to collect all of them into a single object and pass this
object as the single argument. Such a parameter collection could be a dictionary, or a class instance, for
example:
class SimpleNamespace(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
# With Python >= 3.3 we can have instead:
# from types import SimpleNamespace
The interface that solvers expect from leads attached to a FiniteSystem has been simplified and codified
(see there). Similar to self-energy, calculation of modes is now the lead’s own responsibility.
The new class ModesLead allows to attach leads that have a custom way of calculating their modes (e.g.
ideal leads) directly to a Builder.
Modes or self-energies can now be precomputed before passing the system to a solver, using the method
precalculate. This may save time, when the linear system has to be solved many times with the same
lead parameters.
The function modes now returns two objects: PropagatingModes and StabilizedModes. The first one
contains the wave functions of all the propagating modes in real space, as well as their velocities and
momenta. All these quantities were previously not directly available. The second object contains the
propagating and evanescent modes in the compressed format expected by the sparse solver (previously
this was the sole output of modes). Accordingly, the lead_info attribute of SMatrix contains the real
space information about the modes in the leads (a list of PropagatingModes objects).
The module kwant.digest provides functions that given some input compute a “random” output that
depends on the input in a (cryptographically) intractable way. This functionality is useful for introducing
disorder, e.g.:
def onsite(site):
return 0.3 * kwant.digest.gauss(repr(site)) + 4
The module kwant.rmt supports the creation of random matrix theory Hamiltonians.
The plotting functionality has been extended. By default, symbols and lines in plots are now relative to
the system coordinates, i.e. will scale accordingly if different zoom-levels are used. Different styles for
representing sites and hoppings are now possible. 3D plotting has been made more efficient.
This article explains the user-visible changes in Kwant 0.2. Kwant 0.2 was released on 29 November
2012.
Improved performance
This has been the main focus of this release. Through optimization a level of performance has been
reached that we consider satisfactory: runs of Kwant for mid-sized (100x100 say) systems now typically
spend most time in highly optimized libraries and not anymore in Python-implemented code. For large,
truly performance-critical systems almost all time is now spent in optimized libraries.
An important optimization has been replacing NumPy for most uses within Kwant by tinyarray. tinyarray
provides a subset of NumPy’s functionality in a way that is highly optimized for small arrays such as
the tags of sites in Kwant.
The code for sparse matrix solvers has been reorganized and a new solver has been added next to
kwant.solvers.sparse: kwant.solvers.mumps. The new solver uses the MUMPS software package and is
much (typically several times) faster than the UMFPACK-based old solver. In addition, MUMPS uses
considerably less memory for a given system while at the same time it is able to take advantage of more
than 2 GiB of RAM.
plotter has been rewritten using matplotlib, which allows plot post-processing, basic 3D plotting and
many other features. Due to the possibility to easily modify a matplotlib plot after it has been generated,
function plot has much fewer input parameters, and is less flexible than its previous implementation. Its
10 Chapter 1. Preliminaries
Kwant 1.3.1 documentation
interface is also much more similar to that of matplotlib. For the detailed interface and input description
check plot documentation.
The behavior of plot with low level systems has changed. Arguments of plot which are functions are
given site numbers in place of Site objects when plotting a low level system. This provides an easy way
to make the appearance of lines and symbols depend on computation results.
A new function map was implemented. It allows to show a map of spatial dependence of a function of a
system site (e.g. density of states) without showing the sites themselves.
New usage:
The new function of sparse solvers ldos allows the calculation of the local density of states.
The function solve of sparse solvers now always returns a single instance of BlockResult. The latter has
been generalized to include more information for leads defined as infinite systems.
Ready-to-use Kwant packages are available for many platforms (like GNU/Linux, Mac OS X, Microsoft
Windows). See the installation page of the Kwant website for instructions on how to install Kwant on
your platform. This is the recommended way for new users.
The remainder of this section documents how to build Kwant from source. This information is mostly
of interest to contributors and packagers.
Source distributions of Kwant (and Tinyarray) are available at the downloads section of the Kwant
website as well as PyPI. The sources may be also cloned directly from the official Kwant git repository.
Prerequisites
Kwant can be built and installed following the usual Python conventions by running the following com-
mands in the root directory of the Kwant distribution.
Depending on your system, you might have to run the second command with administrator privileges
(e.g. prefixing it with sudo).
After installation, tests can be run with:
The tutorial examples can be found in the directory tutorial inside the root directory of the Kwant
source distribution.
(Cython will be run automatically when the source tree has been checked out of version control. Kwant
tarballs include the Cython-generated files, and cythonization is disabled when building not from git. If
ever necessary, this default can be overridden by giving the --cython option to setup.py.)
12 Chapter 1. Preliminaries
Kwant 1.3.1 documentation
Build configuration
Kwant contains several extension modules. The compilation and linking of these modules can be config-
ured by editing a build configuration file. By default, this file is build.conf in the root directory of the
Kwant distribution. A different path may be provided using the --configfile=PATH option.
This configuration file consists of sections, one for each extension module that is contained in Kwant,
led by a [section name] header and followed by key = value lines.
The sections bear the names of the extension modules, for example [kwant.operator] or [kwant.
linalg.lapack]. There can be also a [DEFAULT] section that provides default values for all extensions,
also those not explicitly present in the file.
Possible keys are the keyword arguments for distutils.core.Extension (For a complete list, see its
documentation). The corresponding values are whitespace-separated lists of strings.
Example build.conf for compiling Kwant with C assertions and Cython’s line trace feature:
[DEFAULT]
undef_macros = NDEBUG
define_macros = CYTHON_TRACE=1
Kwant must be linked against LAPACK & BLAS, and, optionally, MUMPS. The main application
of build configuration is adopting the build process to the various (deployment) variants of these li-
braries. By default setup.py assumes that LAPACK and BLAS can be found under their usual names.
MUMPS will be not linked against by default, except on Debian-based systems when the package
libmumps-scotch-dev is installed.
The sections [kwant.linalg.lapack] and [kwant.linalg._mumps] may be used to adapt the build
process. (For simplicity and backwards compatibility, [lapack] and [mumps] are aliases for the above.)
The section [lapack] configures the linking against LAPACK _AND_ BLAS, the section [mumps]
against MUMPS. The contents of [lapack] are appended to the configuration for MUMPS itself needs
LAPACK and BLAS as well.
Example build.conf for linking Kwant against a self-compiled MUMPS, SCOTCH and METIS:
[mumps]
libraries = zmumps mumps_common pord metis esmumps scotch scotcherr mpiseq gfortran
[lapack]
libraries = mkl_intel_lp64 mkl_sequential mkl_core mkl_def
library_dirs = /opt/intel/mkl/lib/intel64
extra_link_args = -Wl,-rpath=/opt/intel/mkl/lib/intel64
The detailed syntax of build.conf is explained in the documentation of Python’s configparser module.
To build the documentation, the Sphinx documentation generator is required with numpydoc extension
(version 0.5 or newer). If PDF documentation is to be built, the tools from the libRSVG (Debian/Ubuntu
package librsvg2-bin) are needed to convert SVG drawings into the PDF format.
As a prerequisite for building the documentation, Kwant must have been built successfully using python3
setup.py build as described above (or Kwant must be already installed in Python’s search path).
HTML documentation is built by entering the doc subdirectory of the Kwant package and executing
make html. PDF documentation is generated by executing make latex followed by make all-pdf in
doc/build/latex.
Because of some quirks of how Sphinx works, it might be necessary to execute make clean between
building HTML and PDF documentation. If this is not done, Sphinx may mistakenly use PNG files for
PDF output or other problems may appear.
Kwant should run on all recent Unix-like systems. The following instructions have been verified to work
on Debian 8 (Jessie) or newer, and on Ubuntu 14.04 or newer. For other distributions step 1 will likely
have to be adapted. If Ubuntu-style sudo is not available, the respective command must be run as root.
1. Install the required packages. On Debian-based systems like Ubuntu this can be done by running
the command
sudo apt-get install python3-dev python3-scipy python3-matplotlib python3-pytest g++␣
↪gfortran libopenblas-dev liblapack-dev libmumps-scotch-dev
By default the package will be installed under /usr/local. Run python3 setup.py --help install
for installation options.
Mac OS X: MacPorts
The following instructions are valid for Kwant 1.1 with Python 2.7. They need to be updated for Kwant
1.2. (Help is welcome.)
The required dependencies of Kwant are best installed with one of the packaging systems. Here we only
consider the case of MacPorts in detail. Some remarks for homebrew are given below.
1. Install a recent version of MacPorts, as explained in the installation instructions of MacPorts.
2. Install the required dependencies:
sudo port install gcc47 python27 py27-numpy py27-scipy py27-matplotlib mumps_seq
sudo port select --set python python27
p5. Unpack Kwant, go to the Kwant directory, and edit build.conf to read:
[lapack]
extra_link_args = -Wl,-framework -Wl,Accelerate
[mumps]
include_dirs = /opt/local/include
library_dirs = /opt/local/lib
libraries = zmumps_seq mumps_common_seq pord_seq esmumps scotch scotcherr mpiseq gfortran
14 Chapter 1. Preliminaries
Kwant 1.3.1 documentation
You might note that installing Kwant on Mac OS X is somewhat more involved than installing on Linux.
Part of the reason is that we need to mix Fortran and C code in Kwant: While C code is usually compiled
using Apple compilers, Fortran code must be compiled with the Gnu Fortran compiler (there is no Apple
Fortran compiler). For this reason we force the Gnu compiler suite with the environment variables CC
and LDSHARED as shown above.
Mac OS X: homebrew
The following instructions are valid for Kwant 1.1 with Python 2.7. They need to be updated for Kwant
1.2. (Help is welcome.)
It is also possible to build Kwant using homebrew. The dependencies can be installed as
Note that during the installation you will be told which paths to add when you want to compile/link
against scotch/metis/mumps; you need to add these to the build.conf file. Also, when linking against
MUMPS, one needs also to link against METIS (in addition to the libraries needed for MacPorts).
Microsoft Windows
Our efforts to compile Kwant on Windows using only free software (MinGW) were only moderately
successful. At the end of a very complicated process we obtained packages that worked, albeit unreliably.
As the only recommended way to compile Python extensions on Windows is using Visual C++, it may
well be that there exists no easy solution.
It is possible to compile Kwant on Windows using non-free compilers, however we (the authors of Kwant)
have no experience with this. The existing Windows binary installers of Kwant and Tinyarray were kindly
prepared by Christoph Gohlke.
1.4.1 Funding
We provide Kwant as free software under a BSD license as a service to the physics community. If you
have used Kwant for work that has lead to a scientific publication, please mention the fact that you used
it explicitly in the text body. For example, you may add
the numerical calculations were performed using the Kwant code
to the description of your numerical calculations. In addition, we ask you to cite the main paper that
introduces Kwant:
C. W. Groth, M. Wimmer, A. R. Akhmerov, X. Waintal, Kwant: a software package for
quantum transport, New J. Phys. 16, 063065 (2014).
If you have profited from the quantum transport functionality of Kwant, please also cite the upcoming
paper that describes the relevant algorithms. The reference will also be added here once it is available.
Kwant owes much of its current performance to the use of the MUMPS library for solving systems of
sparse linear equations. If you have done high-performance calculations, we suggest citing
16 Chapter 1. Preliminaries
Kwant 1.3.1 documentation
We see Kwant not just as a package with fixed functionality, but rather as a framework for implementing
different physics-related algorithms using a common set of concepts and, if possible, a shared interface.
We have designed it leaving room for growth, and plan to keep extending it.
External contributions to Kwant are highly welcome. You can help to advance the project not only by
writing code, but also by reporting bugs, and fixing/improving the documentation. Please see the Kwant
website for information on how to get in touch with the Kwant community.
Copyright 2011-2015 C. W. Groth (CEA), M. Wimmer, A. R. Akhmerov, X. Waintal (CEA), and others.
All rights reserved.
(CEA = Commissariat à l’énergie atomique et aux énergies alternatives)
Redistribution and use in source and binary forms, with or without modification, are permitted provided
that the following conditions are met:
• Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
• Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the dis-
tribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS
IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIB-
UTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUB-
STITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTER-
RUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON-
TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBIL-
ITY OF SUCH DAMAGE.
18 Chapter 1. Preliminaries
CHAPTER
TWO
2.1 Introduction
In this tutorial, the most important features of Kwant are explained using simple, but still physically
meaningful examples. Each of the examples is commented extensively. In addition, you will find notes
about more subtle, technical details at the end of each example. At first reading, these notes may be
safely skipped.
A scientific article about Kwant is available as well, see Citing Kwant.
The article introduces Kwant with a somewhat different focus than the tutorial and it is the authors’
intention that both texts complement each other. While the tutorial is more “hands-on”, the article
presents Kwant in a more conceptual way, as well as discussing questions of design and performance.
This introduction to the software Kwant is written for people that already have some experience with
the theory of quantum transport. Several introductions to the field are available, the most widely known
is probably the book “Electronic transport in mesoscopic systems” by Supriyo Datta.
Kwant is a library for Python. Care was taken to fit well with the spirit of the language and to take
advantage of its expressive power. If you do not know Python yet, do not fear: Python is widely regarded
as one of the most accessible programming languages. For an introduction we recommend the official
Python Tutorial. The Beginner’s Guide to Python contains a wealth of links to other tutorials, guides
and books including some for absolute beginners.
2.1.3 Kwant
There are two steps in obtaining a numerical solution to a problem: The first is defining the problem in
a computer-accessible way, the second solving it. The aim of a software package like Kwant is to make
both steps easier.
In Kwant, the definition of the problem amounts to the creation of a tight binding system. The solution
of the problem, i.e. the calculation of the values of physical observables, is achieved by passing the
system to a solver.
The definition of a tight binding system can be seen as nothing else than the creation of a huge sparse
matrix (the Hamiltonian). Equivalently, the sparse Hamiltonian matrix can be seen as an annotated
graph: the nodes of the graph are the sites of the tight binding system, the edges are the hoppings.
Sites are annotated with the corresponding on-site Hamiltonian matrix, hoppings are annotated with
the corresponding hopping integral matrix.
19
Kwant 1.3.1 documentation
One of the central goals of Kwant is to allow easy creation of such annotated graphs that represent tight
binding system. Kwant can be made to know about the general structure of a particular system, the
involved lattices and symmetries. For example, a system with a 1D translational symmetry may be used
as a lead and attached to a another system. If both systems have sites which belong to the same lattices,
the attaching can be done automatically, even if the shapes of the systems are irregular.
Once a tight binding system has been created, solvers provided by Kwant can be used to compute physical
observables. Solvers expect the system to be in a different format than the one used for construction
– the system has to be finalized. In a finalized system the tight binding graph is fixed but the matrix
elements of the Hamiltonian may still change. The finalized format is both more efficient and simpler
– the solvers don’t have to deal with the various details which were facilitating the construction of the
system.
The typical workflow with Kwant is as follows:
1. Create an “empty” tight binding system.
2. Set its matrix elements and hoppings.
3. Attach leads (tight binding systems with translational symmetry).
4. Pass the finalized system to a solver.
Please note that even though this tutorial mostly shows 2-d systems, Kwant is completely general with
respect to the number of dimensions. Kwant does not care in the least whether systems live in one, two,
three, or any other number of dimensions. The only exception is plotting, which out-of-the-box only
works for up to three dimensions. (But custom projections can be specified!)
As first example, we compute the transmission probability through a two-dimensional quantum wire.
The wire is described by the two-dimensional Schrödinger equation
−ℏ2 2
𝐻= (𝜕 + 𝜕𝑦2 ) + 𝑉 (𝑦)
2𝑚 𝑥
with a hard-wall confinement 𝑉 (𝑦) in y-direction.
To be able to implement the quantum wire with Kwant, the continuous Hamiltonian 𝐻 has to be
discretized thus turning it into a tight-binding model. For simplicity, we discretize 𝐻 on the sites of
a square lattice with lattice constant 𝑎. Each site with the integer lattice coordinates (𝑖, 𝑗) has the
real-space coordinates (𝑥, 𝑦) = (𝑎𝑖, 𝑎𝑗).
Introducing the discretized positional states
and an equivalent expression for 𝜕𝑦2 . Subsitituting them in the Hamiltonian gives us
𝐻 = ∑ [ (𝑉 (𝑎𝑖, 𝑎𝑗) + 4𝑡) |𝑖, 𝑗⟩ ⟨𝑖, 𝑗| − 𝑡( |𝑖 + 1, 𝑗⟩ ⟨𝑖, 𝑗| + |𝑖, 𝑗⟩ ⟨𝑖 + 1, 𝑗| + |𝑖, 𝑗 + 1⟩ ⟨𝑖, 𝑗| + |𝑖, 𝑗⟩ ⟨𝑖, 𝑗 + 1| )]
𝑖,𝑗
with
ℏ2
𝑡= .
2𝑚𝑎2
For finite 𝑎, this discretized Hamiltonian approximates the continuous one to any required accuracy. The
approximation is good for all quantum states with a wave length considerably larger than 𝑎.
The remainder of this section demonstrates how to realize the discretized Hamiltonian in Kwant and how
to perform transmission calculations. For simplicity, we choose to work in such units that 𝑡 = 𝑎 = 1.
See also:
The complete source code of this example can be found in tutorial/quantum_wire.py
In order to use Kwant, we need to import it:
import kwant
syst = kwant.Builder()
Observe that we just accessed Builder by the name kwant.Builder. We could have just as well written
kwant.builder.Builder instead. Kwant consists of a number of sub-packages that are all covered in the
reference documentation. For convenience, some of the most widely-used members of the sub-packages
are also accessible directly through the top-level kwant package.
Apart from Builder we also need to specify what kind of sites we want to add to the system. Here we
work with a square lattice. For simplicity, we set the lattice constant to unity:
a = 1
lat = kwant.lattice.square(a)
Since we work with a square lattice, we label the points with two integer coordinates (i, j). Builder
then allows us to add matrix elements corresponding to lattice points: syst[lat(i, j)] = ... sets the
on-site energy for the point (i, j), and syst[lat(i1, j1), lat(i2, j2)] = ... the hopping matrix
element from point (i2, j2) to point (i1, j1).
Note that we need to specify sites for Builder in the form lat(i, j). The lattice object lat does the
translation from integer coordinates to proper site format needed in Builder (more about that in the
technical details below).
We now build a rectangular scattering region that is W lattice points wide and L lattice points long:
t = 1.0
W = 10
L = 30
for i in range(L):
for j in range(W):
# On-site Hamiltonian
syst[lat(i, j)] = 4 * t
# Hopping in y-direction
if j > 0:
syst[lat(i, j), lat(i, j - 1)] = -t
1 https://xkcd.com/353/
# Hopping in x-direction
if i > 0:
syst[lat(i, j), lat(i - 1, j)] = -t
Observe how the above code corresponds directly to the terms of the discretized Hamiltonian: “On-site
Hamiltonian” implements
The hard-wall confinement is realized by not having hoppings (and sites) beyond a certain region of
space.
Next, we define the leads. Leads are also constructed using Builder, but in this case, the system must
have a translational symmetry:
Here, the Builder takes a TranslationalSymmetry as the optional parameter. Note that the (real-
space) vector (-a, 0) defining the translational symmetry must point in a direction away from the
scattering region, into the lead – hence, lead 02 will be the left lead, extending to infinity to the left.
For the lead itself it is enough to add the points of one unit cell as well as the hoppings inside one unit
cell and to the next unit cell of the lead. For a square lattice, and a lead in y-direction the unit cell is
simply a vertical line of points:
for j in range(W):
left_lead[lat(0, j)] = 4 * t
if j > 0:
left_lead[lat(0, j), lat(0, j - 1)] = -t
left_lead[lat(1, j), lat(0, j)] = -t
Note that here it doesn’t matter if you add the hoppings to the next or the previous unit cell – the
translational symmetry takes care of that. The isolated, infinite is attached at the correct position using
syst.attach_lead(left_lead)
This call returns the lead number which will be used to refer to the lead when computing transmissions
(further down in this tutorial). More details about attaching leads can be found in the tutorial Nontrivial
shapes.
We also want to add a lead on the right side. The only difference to the left lead is that the vector of
the translational symmetry must point to the right, the remaining code is the same:
for j in range(W):
right_lead[lat(0, j)] = 4 * t
if j > 0:
syst.attach_lead(right_lead)
Note that here we added points with x-coordinate 0, just as for the left lead. You might object that the
right lead should be placed L (or L+1?) points to the right with respect to the left lead. In fact, you do
not need to worry about that.
Now we have finished building our system! We plot it, to make sure we didn’t make any mistakes:
kwant.plot(syst)
8
6
4
2
0
0 5 10 15 20 25 30
The system is represented in the usual way for tight-binding systems: dots represent the lattice points (i,
j), and for every nonzero hopping element between points there is a line connecting these points. From
the leads, only a few (default 2) unit cells are shown, with fading color.
In order to use our system for a transport calculation, we need to finalize it
syst = syst.finalized()
Having successfully created a system, we now can immediately start to compute its conductance as a
function of energy:
energies = []
data = []
for ie in range(100):
energy = ie * 0.01
We use kwant.smatrix which is a short name for kwant.solvers.default.smatrix of the default solver
module kwant.solvers.default. kwant.smatrix computes the scattering matrix smatrix solving a
sparse linear system. smatrix itself allows to directly compute the total transmission probability from
lead 0 to lead 1 as smatrix.transmission(1, 0). The numbering used to refer to the leads here is the
same as the numbering assigned by the call to attach_lead earlier in the tutorial.
Finally we can use matplotlib to make a plot of the computed data (although writing to file and using
an external viewer such as gnuplot or xmgrace is just as viable)
pyplot.figure()
pyplot.plot(energies, data)
pyplot.xlabel("energy [t]")
pyplot.ylabel("conductance [e^2/h]")
pyplot.show()
3.0
2.5
2.0
conductance [e^2/h]
1.5
1.0
0.5
0.0
0.0 0.2 0.4 0.6 0.8 1.0
energy [t]
We see a conductance quantized in units of 𝑒2 /ℎ, increasing in steps as the energy is increased. The
value of the conductance is determined by the number of occupied subbands that increases with energy.
Technical details
• In the example above, when building the system, only one direction of hopping is given, i.e.
syst[lat(i, j), lat(i, j-1)] = ... and not also syst[lat(i, j-1), lat(i, j)] = .
... The reason is that Builder automatically adds the other direction of the hopping such that
the resulting system is Hermitian.
However, it does not hurt to define the opposite direction of hopping as well:
is valid code. In the latter case, the hopping syst[lat(1, 0), lat(0, 0)] is overwritten by the
last line and also equals to -2.
• Some more details the relation between Builder and the square lattice lat in the example:
Technically, Builder expects sites as indices. Sites themselves have a certain type, and belong to
a site family. A site family is also used to convert something that represents a site (like a tuple)
syst = syst.finalized()
In doing so, we transform the Builder object (with which we built up the system step by step)
into a System that has a fixed structure (which we cannot change any more).
Note that this means that we cannot access the Builder object any more. This is not necesarry
any more, as the computational routines all expect finalized systems. It even has the advantage
that python is now free to release the memory occupied by the Builder which, for large systems,
can be considerable. Roughly speaking, the above code corresponds to
fsyst = syst.finalized()
del syst
syst = fsyst
• Even though the vector passed to the TranslationalSymmetry is specified in real space, it must
be compatible with the lattice symmetries. A single lead can consists of sites belonging to more
than one lattice, but of course the translational symmetry of the lead has to be shared by all of
them.
• Instead of plotting to the screen (which is standard) plot can also write to a file specified by the
argument file. For the plotting to the screen to work the module matplotlib.pyplot has to be
imported. (An informative error message will remind you if you forget.) The reason for this is
pretty technical: matplotlib’s “backend” can only be chosen before matplotlib.pyplot has been
imported. Would Kwant import that module by itself, it would deprive you of the possibility to
choose a non-default backend later.
See also:
The complete source code of this example can be found in tutorial/quantum_wire_revisited.py
Kwant allows for more than one way to build a system. The reason is that Builder is essentially just a
container that can be filled in different ways. Here we present a more compact rewrite of the previous
example (still with the same results).
Also, the previous example was written in the form of a Python script with little structure, and with
everything governed by global variables. This is OK for such a simple example, but for larger projects it
makes sense to partition the code into separate entities. In this example we therefore also aim at more
structure.
We begin the program collecting all imports in the beginning of the file and put the build-up of the
system into a separate function make_system:
import kwant
# For plotting
from matplotlib import pyplot
syst = kwant.Builder()
Previously, the scattering region was build using two for-loops. Instead, we now write:
Here, all lattice points are added at once in the first line. The construct ((i, j) for i in range(L)
for j in range(W)) is a generator that iterates over all points in the rectangle as did the two for-loops
in the previous example. In fact, a Builder can not only be indexed by a single lattice point – it also
allows for lists of points, or, as in this example, a generator (as is also used in list comprehensions in
python).
Having added all lattice points in one line, we now turn to the hoppings. In this case, an iterable like
for the lattice points becomes a bit cumbersome, and we use instead another feature of Kwant:
syst[lat.neighbors()] = -t
In regular lattices, hoppings form large groups such that hoppings within a group can be transformed
into one another by lattice translations. In order to allow to easily manipulate such hoppings, an object
HoppingKind is provided. When given a Builder as an argument, HoppingKind yields all the hoppings
of a certain kind that can be added to this builder without adding new sites. When HoppingKind is
given to Builder as a key, it means that something is done to all the possible hoppings of this kind. A
list of HoppingKind objects corresponding to nearest neighbors in lattices in Kwant is obtained using
lat.neighbors(). syst[lat.neighbors()] = -t then sets all of those hopping matrix elements at
once. In order to set values for all the nth-nearest neighbors at once, one can similarly use syst[lat.
neighbors(n)] = -t. More detailed example of using HoppingKind directly will be provided in Matrix
structure of on-site and hopping elements.
The left lead is constructed in an analogous way:
The previous example duplicated almost identical code for the left and the right lead. The only difference
was the direction of the translational symmetry vector. Here, we only construct the left lead, and use
the method reversed of Builder to obtain a copy of a lead pointing in the opposite direction. Both
leads are attached as before and the finished system returned:
syst.attach_lead(lead)
syst.attach_lead(lead.reversed())
return syst
The remainder of the script has been organized into two functions. One for the plotting of the conduc-
tance.
pyplot.figure()
pyplot.plot(energies, data)
pyplot.xlabel("energy [t]")
pyplot.ylabel("conductance [e^2/h]")
pyplot.show()
def main():
syst = make_system()
Finally, we use the following standard Python construct3 to execute main if the program is used as a
script (i.e. executed as python quantum_wire_revisited.py):
if __name__ == '__main__':
main()
If the example, however, is imported inside Python using import quantum_wire_revisted as qw, main
is not executed automatically. Instead, you can execute it manually using qw.main(). On the other
hand, you also have access to the other functions, make_system and plot_conductance, and can thus
play with the parameters.
The result of the example should be identical to the previous one. Technical details
• We have seen different ways to add lattice points to a Builder. It allows to
– add single points, specified as sites
– add several points at once using a generator (as in this example)
– add several points at once using a list (typically less effective compared to a generator)
For technical reasons it is not possible to add several points using a tuple of sites. Hence it is worth
noting a subtle detail in
Note that (lat(x, y) for x in range(L) for y in range(W)) is not a tuple, but a generator.
Let us elaborate a bit more on this using a simpler example:
>>> a = (0, 1, 2, 3)
>>> b = (i for i in range(4))
Here, a is a tuple, whereas b is a generator. One difference is that one can subscript tuples, but
not generators:
>>> a[0]
0
>>> b[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'generator' object is unsubscriptable
or:
You might wonder, why it is then possible to write for a single hopping:
instead of
In fact, due to the way python handles subscripting, syst[site1, site2] is the same as
syst[(site1, site2)].
(This is the deeper reason why several sites cannot be added as a tuple – it would be impossible
to distinguish whether one would like to add two separate sites, or one hopping.
Each of the following three examples highlights different ways to go beyond the very simple examples of
the previous section.
See also:
The complete source code of this example can be found in tutorial/spin_orbit.py
We begin by extending the simple 2DEG-Hamiltonian by a Rashba spin-orbit coupling and a Zeeman
splitting due to an external magnetic field:
−ℏ2 2
𝐻= (𝜕 + 𝜕𝑦2 ) − 𝑖𝛼(𝜕𝑥 𝜎𝑦 − 𝜕𝑦 𝜎𝑥 ) + 𝐸Z 𝜎𝑧 + 𝑉 (𝑦)
2𝑚 𝑥
Here 𝜎𝑥,𝑦,𝑧 denote the Pauli matrices.
It turns out that this well studied Rashba-Hamiltonian has some peculiar properties in (ballistic)
nanowires: It was first predicted theoretically in Phys. Rev. Lett. 90, 256601 (2003) that such a
system should exhibit non-monotonic conductance steps due to a spin-orbit gap. Only very recently,
this non-monotonic behavior has been supposedly observed in experiment: Nature Physics 6, 336 (2010).
Here we will show that a very simple extension of our previous examples will exactly show this behavior
(Note though that no care was taken to choose realistic parameters).
The tight-binding model corresponding to the Rashba-Hamiltonian naturally exhibits a 2x2-matrix struc-
ture of onsite energies and hoppings. In order to use matrices in our program, we import the Tinyarray
package. (NumPy would work as well, but Tinyarray is much faster for small arrays.)
import tinyarray
For convenience, we define the Pauli-matrices first (with 𝜎0 the unit matrix):
Previously, we used numbers as the values of our matrix elements. However, Builder also accepts
matrices as values, and we can simply write:
Note that the Zeeman energy adds to the onsite term, whereas the Rashba spin-orbit term adds to
the hoppings (due to the derivative operator). Furthermore, the hoppings in x and y-direction have a
different matrix structure. We now cannot use lat.neighbors() to add all the hoppings at once, since
we now have to distinguish x and y-direction. Because of that, we have to explicitly specify the hoppings
in the form expected by HoppingKind:
• A tuple with relative lattice indices. For example, (1, 0) means hopping from (i, j) to (i+1, j),
whereas (1, 1) would mean hopping to (i+1, j+1).
• The target lattice (where to hop to)
• The source lattice (where the hopping originates)
Since we are only dealing with a single lattice here, source and target lattice are identical, but still must
be specified (for an example with hopping between different (sub)lattices, see Beyond square lattices:
graphene).
Again, it is enough to specify one direction of the hopping (i.e. when specifying (1, 0) it is not necessary
to specify (-1, 0)), Builder assures hermiticity.
The leads also allow for a matrix structure,
The remainder of the code is unchanged, and as a result we should obtain the following, clearly non-
monotonic conductance steps:
4
conductance [e^2/h]
0
0.2 0.0 0.2 0.4 0.6
energy [t]
Technical details
• The Tinyarray package, one of the dependencies of Kwant, implements efficient small arrays. It is
used internally in Kwant for storing small vectors and matrices. For performance, it is preferable
to define small arrays that are going to be used with Kwant using Tinyarray. However, NumPy
would work as well:
import numpy
sigma_0 = numpy.array([[1, 0], [0, 1]])
sigma_x = numpy.array([[0, 1], [1, 0]])
sigma_y = numpy.array([[0, -1j], [1j, 0]])
sigma_z = numpy.array([[1, 0], [0, -1]])
Tinyarray arrays behave for most purposes like NumPy arrays except that they are immutable:
they cannot be changed once created. This is important for Kwant: it allows them to be used
directly as dictionary keys.
• It should be emphasized that the relative hopping used for HoppingKind is given in terms of lattice
indices, i.e. relative to the Bravais lattice vectors. For a square lattice, the Bravais lattice vectors
are simply (a,0) and (0,a), and hence the mapping from lattice indices (i,j) to real space and back
is trivial. This becomes more involved in more complicated lattices, where the real-space directions
corresponding to, for example, (1,0) and (0,1) need not be orthogonal any more (see Beyond square
lattices: graphene).
See also:
The complete source code of this example can be found in tutorial/quantum_well.py
Up to now, all examples had position-independent matrix-elements (and thus translational invariance
along the wire, which was the origin of the conductance steps). Now, we consider the case of a position-
dependent potential:
ℏ2 2
𝐻= (𝜕 + 𝜕𝑦2 ) + 𝑉 (𝑥, 𝑦)
2𝑚 𝑥
The position-dependent potential enters in the onsite energies. One possibility would be to again set
the onsite matrix elements of each lattice point individually (as in Transport through a quantum wire).
However, changing the potential then implies the need to build up the system again.
Instead, we use a python function to define the onsite energies. We define the potential profile of a
quantum well as:
def make_system(a=1, t=1.0, W=10, L=30, L_well=10):
# Start with an empty tight-binding system and a single square lattice.
# `a` is the lattice constant (by default set to 1 for simplicity.
lat = kwant.lattice.square(a)
syst = kwant.Builder()
This function takes two arguments: the first of type Site, from which you can get the real-space coordi-
nates using site.pos, and the value of the potential as the second. Note that in potential we can access
variables of the surrounding function: L and L_well are taken from the namespace of make_system.
Kwant now allows us to pass a function as a value to Builder:
def onsite(site, pot=0):
return 4 * t + potential(site, pot)
For each lattice point, the corresponding site is then passed as the first argument to the function onsite.
The values of any additional parameters, which can be used to alter the Hamiltonian matrix elements
at a later stage, are specified later during the call to smatrix. Note that we had to define onsite, as it is
not possible to mix values and functions as in syst[...] = 4 * t + potential.
For the leads, we just use constant values as before. If we passed a function also for the leads (which
is perfectly allowed), this function would need to be compatible with the translational symmetry of the
lead – this should be kept in mind.
Finally, we compute the transmission probability:
# Compute conductance
data = []
for welldepth in welldepths:
smatrix = kwant.smatrix(syst, energy, args=[-welldepth])
data.append(smatrix.transmission(1, 0))
pyplot.figure()
pyplot.plot(welldepths, data)
pyplot.xlabel("well depth [t]")
pyplot.ylabel("conductance [e^2/h]")
pyplot.show()
kwant.smatrix allows us to specify a list, args, that will be passed as additional arguments to the
functions that provide the Hamiltonian matrix elements. In this example we are able to solve the system
for different depths of the potential well by passing the potential value. We obtain the result:
1.0
0.9
0.8
conductance [e^2/h]
0.7
0.6
0.5
0.4
0.0 0.2 0.4 0.6 0.8 1.0
well depth [t]
Starting from no potential (well depth = 0), we observe the typical oscillatory transmission behavior
through resonances in the quantum well.
Warning: If functions are used to set values inside a lead, then they must satisfy the same symmetry
as the lead does. There is (currently) no check and wrong results will be the consequence of a
misbehaving function.
Technical details
• Functions can also be used for hoppings. In this case, they take two Site‘s as arguments and then
an arbitrary number of additional arguments.
• Apart from the real-space position pos, Site has also an attribute tag containing the lattice indices
of the site.
See also:
The complete source code of this example can be found in tutorial/ab_ring.py
Up to now, we only dealt with simple wire geometries. Now we turn to the case of a more complex
geometry, namely transport through a quantum ring that is pierced by a magnetic flux Φ:
For a flux line, it is possible to choose a gauge such that a charged particle acquires a phase 𝑒Φ/ℎ
whenever it crosses the branch cut originating from the flux line (branch cut shown as red dashed line)1 .
There are more symmetric gauges, but this one is most convenient to implement numerically.
Defining such a complex structure adding individual lattice sites is possible, but cumbersome. Fortu-
nately, there is a more convenient solution: First, define a boolean function defining the desired shape,
i.e. a function that returns True whenever a point is inside the shape, and False otherwise:
lat = kwant.lattice.square(a)
syst = kwant.Builder()
Note that this function takes a real-space position as argument (not a Site).
We can now simply add all of the lattice points inside this shape at once, using the function shape
provided by the lattice:
Here, lat.shape takes as a second parameter a (real-space) point that is inside the desired shape. The
hoppings can still be added using lat.neighbors() as before.
Up to now, the system contains constant hoppings and onsite energies, and we still need to include the
phase shift due to the magnetic flux. This is done by overwriting the values of hoppings in x-direction
along the branch cut in the lower arm of the ring. For this we select all hoppings in x-direction that are
of the form (lat(1, j), lat(0, j)) with j<0:
1 The corresponding vector potential is 𝐴 (𝑥, 𝑦) = Φ𝛿(𝑥)Θ(−𝑦) which yields the correct magnetic field 𝐵(𝑥, 𝑦) =
𝑥
Φ𝛿(𝑥)𝛿(𝑦).
def crosses_branchcut(hop):
ix0, iy0 = hop[0].tag
# Modify only those hopings in x-direction that cross the branch cut
def hops_across_cut(syst):
for hop in kwant.builder.HoppingKind((1, 0), lat, lat)(syst):
if crosses_branchcut(hop):
yield hop
syst[hops_across_cut] = hopping_phase
Here, crosses_branchcut is a boolean function that returns True for the desired hoppings. We then use
again a generator (this time with an if-conditional) to set the value of all hoppings across the branch
cut to fluxphase. The rationale behind using a function instead of a constant value for the hopping is
again that we want to vary the flux through the ring, without constantly rebuilding the system – instead
the flux is governed by the parameter phi.
For the leads, we can also use the lat.shape-functionality:
def lead_shape(pos):
(x, y) = pos
return (-W / 2 < y < W / 2)
Here, the shape must be compatible with the translational symmetry of the lead sym_lead. In particular,
this means that it should extend to infinity along the translational symmetry direction (note how there
is no restriction on x in lead_shape)2 .
Attaching the leads is done as before:
syst.attach_lead(lead)
syst.attach_lead(lead.reversed())
In fact, attaching leads seems not so simple any more for the current structure with a scattering region
very much different from the lead shapes. However, the choice of unit cell together with the translational
vector allows to place the lead unambiguously in real space – the unit cell is repeated infinitely many
times in the direction and opposite to the direction of the translational vector. Kwant examines the lead
starting from infinity and traces it back (going opposite to the direction of the translational vector) until
it intersects the scattering region. At this intersection, the lead is attached:
2 Despite the “infinite” shape, the unit cell will still be finite; the TranslationalSymmetry takes care of that.
After the lead has been attached, the system should look like this:
20
15
10
10
15
20
20 10 0 10 20
The computation of the conductance goes in the same fashion as before. Finally you should get the
following result:
1.0
0.8
conductance [e^2/h]
0.6
0.4
0.2
0.0
0.0 0.5 1.0 1.5 2.0 2.5 3.0
flux [flux quantum]
where one can observe the conductance oscillations with the period of one flux quantum. Technical
details
• Leads have to have proper periodicity. Furthermore, the Kwant format requires the hopping from
the leads to the scattering region to be identical to the hoppings between unit cells in the lead.
attach_lead takes care of all these details for you! In fact, it even adds points to the scattering
region, if proper attaching requires this. This becomes more apparent if we attach the leads a bit
further away from the central axis o the ring, as was done in this example:
20
15
10
10
15
20
20 10 0 10 20
• Per default, attach_lead attaches the lead to the “outside” of the structure, by tracing the lead
backwards, coming from infinity.
One can also attach the lead to the inside of the structure, by providing an alternative starting
point from where the lead is traced back:
starts the trace-back in the middle of the ring, resulting in the lead being attached to the inner
circle:
20
15
10
10
15
20
20 15 10 5 0 5 10 15 20
Note that here the lead is treated as if it would pass over the other arm of the ring, without
intersecting it.
See also:
The complete source code of this example can be found in tutorial/band_structure.py
When doing transport simulations, one also often needs to know the band structure of the leads, i.e. the
energies of the propagating plane waves in the leads as a function of momentum. This band structure
contains information about the number of modes, their momenta and velocities.
In this example, we aim to compute the band structure of a simple tight-binding wire.
Computing band structures in Kwant is easy. Just define a lead in the usual way:
# build up one unit cell of the lead, and add the hoppings
# to the next unit cell
for j in range(W):
lead[lat(0, j)] = 4 * t
if j > 0:
lead[lat(0, j), lat(0, j - 1)] = -t
return lead
“Usual way” means defining a translational symmetry vector, as well as one unit cell of the lead, and
the hoppings to neighboring unit cells. This information is enough to make the infinite, translationally
invariant system needed for band structure calculations.
In the previous examples Builder instances like the one created above were attached as leads to the
Builder instance of the scattering region and the latter was finalized. The thus created system contained
implicitly finalized versions of the attached leads. However, now we are working with a single lead and
there is no scattering region. Hence, we have to finalize the Builder of our sole lead explicitly.
That finalized lead is then passed to bands. This function calculates energies of various bands at a range
of momenta and plots the calculated energies. It is really a convenience function, and if one needs to do
something more profound with the dispersion relation these energies may be calculated directly using
Bands. For now we just plot the bandstructure:
def main():
lead = make_lead().finalized()
kwant.plotter.bands(lead, show=False)
pyplot.xlabel("momentum [(lattice constant)^-1]")
pyplot.ylabel("energy [t]")
pyplot.show()
5
energy [t]
0
3 2 1 0 1 2 3
momentum [(lattice constant)^-1]
where we observe the cosine-like dispersion of the square lattice. Close to k=0 this agrees well with the
quadratic dispersion this tight-binding Hamiltonian is approximating.
See also:
The complete source code of this example can be found in tutorial/closed_system.py
Although Kwant is (currently) mainly aimed towards transport problems, it can also easily be used to
compute properties of closed systems – after all, a closed system is nothing more than a scattering region
without leads!
In this example, we compute the wave functions of a closed circular quantum dot and its spectrum as a
function of magnetic field (Fock-Darwin spectrum).
To compute the eigenenergies and eigenstates, we will make use of the sparse linear algebra functionality
of SciPy, which interfaces the ARPACK package:
We set up the system using the shape-function as in Nontrivial shapes, but do not add any leads:
syst = kwant.Builder()
We add the magnetic field using a function and a global variable as we did in the two previous tutorial.
(Here, the gauge is chosen such that 𝐴𝑥 (𝑦) = −𝐵𝑦 and 𝐴𝑦 = 0.)
The spectrum can be obtained by diagonalizing the Hamiltonian of the system, which in turn can be
obtained from the finalized system using hamiltonian_submatrix:
energies = []
for B in Bfields:
# Obtain the Hamiltonian as a dense matrix
ham_mat = syst.hamiltonian_submatrix(args=[B], sparse=True)
energies.append(ev)
pyplot.figure()
pyplot.plot(Bfields, energies)
pyplot.xlabel("magnetic field [arbitrary units]")
pyplot.ylabel("energy [t]")
pyplot.show()
Note that we use sparse linear algebra to efficiently calculate only a few lowest eigenvalues. Finally, we
obtain the result:
0.7
0.6
0.5
energy [t]
0.4
0.3
0.2
0.1
At zero magnetic field several energy levels are degenerate (since our quantum dot is rather symmetric).
These degeneracies are split by the magnetic field, and the eigenenergies flow towards the Landau level
energies at higher magnetic fields1 .
The eigenvectors are obtained very similarly, and can be plotted directly using map:
def plot_wave_function(syst):
# Calculate the wave functions in the system.
ham_mat = syst.hamiltonian_submatrix(sparse=True)
evecs = sla.eigsh(ham_mat, k=20, which='SM')[1]
1 Again, in this tutorial example no care was taken into choosing appropriate material parameters or units. For this
20
10
10
20
20 10 0 10 20
The last two arguments to map are optional. The first prevents a colorbar from appearing. The second,
oversampling=1, makes the image look better for the special case of a square lattice.
As our model breaks time reversal symmetry (because of the applied magnetic field) we can also see
an intereting property of the eigenstates, namely that they can have non-zero local current. We can
calculate the local current due to a state by using kwant.operator.Current and plotting it using
kwant.plotter.current:
def plot_current(syst):
# Calculate the wave functions in the system.
ham_mat = syst.hamiltonian_submatrix(sparse=True)
evecs = sla.eigsh(ham_mat, k=20, which='SM')[1]
30
20
10
10
20
30
30 20 10 0 10 20 30
Technical details
• hamiltonian_submatrix can also return a sparse matrix, if the optional argument sparse=True.
The sparse matrix is in SciPy’s scipy.sparse.coo_matrix format, which can be easily be con-
verted to various other sparse matrix formats (see SciPy’s documentation).
See also:
The complete source code of this example can be found in tutorial/graphene.py
In the following example, we are going to calculate the conductance through a graphene quantum dot
with a p-n junction and two non-collinear leads. In the process, we will touch all of the topics that
we have seen in the previous tutorials, but now for the honeycomb lattice. As you will see, everything
carries over nicely.
We begin by defining the honeycomb lattice of graphene. This is in principle already done in kwant.
lattice.honeycomb, but we do it explicitly here to show how to define a new lattice:
The first argument to the general function is the list of primitive vectors of the lattice; the second one
is the coordinates of basis atoms. The honeycomb lattice has two basis atoms. Each type of basis atom
by itself forms a regular lattice of the same type as well, and those sublattices are referenced as a and b
above.
In the next step we define the shape of the scattering region (circle again) and add all lattice points
using the shape-functionality:
syst = kwant.Builder()
As you can see, this works exactly the same for any kind of lattice. We add the onsite energies using a
function describing the p-n junction; in contrast to the previous tutorial, the potential value is this time
taken from the scope of make_system, since we keep the potential fixed in this example.
As a next step we add the hoppings, making use of HoppingKind. For illustration purposes we define
the hoppings ourselves instead of using graphene.neighbors():
hoppings = (((0, 0), a, b), ((0, 1), a, b), ((-1, 1), a, b))
The nearest-neighbor model for graphene contains only hoppings between different basis atoms. For this
type of hoppings, it is not enough to specify relative lattice indices, but we also need to specify the
proper target and source sublattices. Remember that the format of the hopping specification is (i,j),
target, source. In the previous examples (i.e. Matrix structure of on-site and hopping elements)
target=source=lat, whereas here we have to specify different sublattices. Furthermore, note that the
directions given by the lattice indices (1, 0) and (0, 1) are not orthogonal anymore, since they are given
with respect to the two primitive vectors [(1, 0), (sin_30, cos_30)].
Adding the hoppings however still works the same way:
Modifying the scattering region is also possible as before. Let’s do something crazy, and remove an atom
in sublattice A (which removes also the hoppings from/to this site) as well as add an additional link:
Note again that the conversion from a tuple (i,j) to site is done by the sublattices a and b.
The leads are defined almost as before:
# left lead
sym0 = kwant.TranslationalSymmetry(graphene.vec((-1, 0)))
def lead0_shape(pos):
x, y = pos
return (-0.4 * r < y < 0.4 * r)
lead0 = kwant.Builder(sym0)
lead0[graphene.shape(lead0_shape, (0, 0))] = -pot
lead0[[kwant.builder.HoppingKind(*hopping) for hopping in hoppings]] = -1
def lead1_shape(pos):
v = pos[1] * sin_30 - pos[0] * cos_30
return (-0.4 * r < v < 0.4 * r)
lead1 = kwant.Builder(sym1)
lead1[graphene.shape(lead1_shape, (0, 0))] = pot
lead1[[kwant.builder.HoppingKind(*hopping) for hopping in hoppings]] = -1
Note the method vec used in calculating the parameter for TranslationalSymmetry. The latter expects
a real-space symmetry vector, but for many lattices symmetry vectors are more easily expressed in the
natural coordinate system of the lattice. The vec-method is thus used to map a lattice vector to a
real-space vector.
Observe also that the translational vectors graphene.vec((-1, 0)) and graphene.vec((0, 1)) are
not orthogonal any more as they would have been in a square lattice – they follow the non-orthogonal
primitive vectors defined in the beginning.
Later, we will compute some eigenvalues of the closed scattering region without leads. This is why
we postpone attaching the leads to the system. Instead, we return the scattering region and the leads
separately.
The computation of some eigenvalues of the closed system is done in the following piece of code:
def compute_evs(syst):
# Compute some eigenvalues of the closed system
sparse_mat = syst.hamiltonian_submatrix(sparse=True)
Here we use in contrast to the previous example a sparse matrix and the sparse linear algebra functionality
of SciPy.
The code for computing the band structure and the conductance is identical to the previous examples,
and needs not be further explained here.
Finally, in the main function we make and plot the system:
def main():
pot = 0.1
syst, leads = make_system(pot=pot)
We customize the plotting: we set the site_colors argument of plot to a function which returns 0 for
sublattice a and 1 for sublattice b:
def family_colors(site):
return 0 if site.family == a else 1
The function plot shows these values using a color scale (grayscale by default). The symbol size is
specified in points, and is independent on the overall figure size.
Plotting the closed system gives this result:
10.0
7.5
5.0
2.5
0.0
2.5
5.0
7.5
10.0
10.0 7.5 5.0 2.5 0.0 2.5 5.0 7.5 10.0
compute_evs(syst.finalized())
10
10
15 10 5 0 5 10
1
energy [t]
3
3 2 1 0 1 2 3
momentum [(lattice constant)^-1]
showing all the features of a zigzag lead, including the flat edge state bands (note that the band structure
is not symmetric around zero energy, due to a potential in the leads).
Finally the transmission through the system is computed,
1.0
0.8
conductance [e^2/h]
0.6
0.4
0.2
0.0
0.20 0.15 0.10 0.05 0.00 0.05 0.10 0.15 0.20
energy [t]
showing the typical resonance-like transmission probability through an open quantum dot Technical
details
• In a lattice with more than one basis atom, you can always act either on all sublattices at the same
time, or on a single sublattice only.
For example, you can add lattice points for all sublattices in the current example using:
syst[graphene.shape(...)] = ...
syst[a.shape(...)] = ...
and the same of course with b. Also, you can selectively remove points:
del syst[graphene.shape(...)]
del syst[a.shape(...)]
where the first line removes points in both sublattices, whereas the second line removes only points
in one sublattice.
See also:
The complete source code of this example can be found in tutorial/superconductor.py
This example deals with superconductivity on the level of the Bogoliubov-de Gennes (BdG) equation.
In this framework, the Hamiltonian is given as
𝐻0 − 𝜇 Δ
𝐻=( )
Δ† 𝜇 − 𝒯𝐻0 𝒯−1
where 𝐻0 is the Hamiltonian of the system without superconductivity, 𝜇 the chemical potential, Δ the
superconducting order parameter, and 𝒯 the time-reversal operator. The BdG Hamiltonian introduces
electron and hole degrees of freedom (an artificial doubling - be aware of the fact that electron and hole
excitations are related!), which we will need to include in our model with Kwant.
For this we restrict ourselves to a simple spinless system without magnetic field, so that Δ is just a
number (which we choose real), and 𝒯𝐻0 𝒯−1 = 𝐻0∗ = 𝐻0 . Furthermore, note that the Hamiltonian has
particle-hole symmetry 𝒫, i. e. 𝒫𝐻𝒫−1 = −𝐻.
Care must be taken when transport calculations are done with the BdG equation. Electrons and holes
carry charge with opposite sign, such that it is necessary to separate the electron and hole degrees of
freedom in the scattering matrix. In particular, the conductance of a N-S-junction is given as
𝑒2
𝐺= (𝑁 − 𝑅ee + 𝑅he ) ,
ℎ
where 𝑁 is the number of electron channels in the normal lead, and 𝑅ee the total probability of reflection
from electrons to electrons in the normal lead, and 𝑅eh the total probability of reflection from electrons
to holes in the normal lead. Fortunately, in Kwant it is straightforward to partition the scattering matrix
in these two degrees of freedom.
Let us consider a system that consists of a normal lead on the left, a superconductor on the right, and
a tunnel barrier in between:
We implement the BdG Hamiltonian in Kwant using a 2x2 matrix structure for all Hamiltonian matrix
elements, as we did previously in the spin example. We declare the square lattice and construct the
scattering region with the following:
def make_system(a=1, W=10, L=10, barrier=1.5, barrierpos=(3, 4),
mu=0.4, Delta=0.1, Deltapos=4, t=1.0, phs=True):
# Start with an empty tight-binding system. On each site, there
# are now electron and hole orbitals, so we must specify the
# number of orbitals per site. The orbital structure is the same
# as in the Hamiltonian.
lat = kwant.lattice.square(norbs=2)
syst = kwant.Builder()
# Hoppings
syst[lat.neighbors()] = -t * tau_z
Note the new argument norbs to square. This is the number of orbitals per site in the discretized BdG
Hamiltonian - of course, norbs = 2, since each site has one electron orbital and one hole orbital. It is
necessary to specify norbs here, such that we may later separate the scattering matrix into electrons
and holes. Aside from this, creating the system is syntactically equivalent to spin example. The only
difference is that the Pauli matrices now act in electron-hole space. Note that the tunnel barrier is added
by overwriting previously set on-site matrix elements.
The superconducting order parameter is nonzero only in a part of the scattering region - the part to
the right of the tunnel barrier. Thus, the scattering region is split into a superconducting part (the
right side of it), and a normal part where the pairing is zero (the left side of it). The next step towards
computing conductance is to attach leads. Let’s attach two leads: a normal one to the left end, and a
superconducting one to the right end. Starting with the left lead, we have:
Note the two new new arguments in Builder, conservation_law and particle_hole. For the purpose
of computing conductance, conservation_law is the essential one, as it allows us to separate the electron
and hole degrees of freedom. Note that it is not necessary to specify particle_hole in Builder to
correctly compute the conductance in this example. We will discuss the argument particle_hole later
on. First, let us discuss conservation_law in more detail.
Observe that electrons and holes are uncoupled in the left (normal) lead, since the superconducting
order parameter that couples them is zero. Consequently, we may view the electron and hole degrees of
freedom as being conserved, and may therefore separate them in the Hamiltonian.
In more technical terms, the conservation law implies that the Hamiltonian can be block diagonalized
into uncoupled electron and hole blocks. Since the blocks are uncoupled, we can construct scattering
states in each block independently. Of course, any scattering state from the electron (hole) block is
entirely electron (hole) like. As a result, the scattering matrix separates into blocks that describe the
scattering between different types of carriers, such as electron to electron, hole to electron, et cetera.
As we saw above, conservation laws in Kwant are specified with the conservation_law argument in
Builder. Specifically, conservation_law is a matrix that acts on a single site and it must in addition
have integer eigenvalues. Of course, it must also commute with the onsite Hamiltonian and hoppings to
adjacent sites. Internally, Kwant then uses the eigenvectors of the conservation law to block diagonalize
the Hamiltonian. Here, we’ve specified the conservation law −𝜎𝑧 , such that the eigenvectors with eigen-
values −1 and 1 pick out the electron and hole blocks, respectively. Internally in Kwant, the blocks are
stored in the order of ascending eigenvalues of the conservation law.
In order to move on with the conductance calculation, let’s attach the second lead to the right side of
the scattering region:
return syst
The second (right) lead is superconducting, such that the electron and hole blocks are coupled. Of
course, this means that we can not separate them into uncoupled blocks as we did before, and therefore
no conservation law is specified.
Kwant is now aware of the block structure of the Hamiltonian in the left lead. This means that we
can extract transmission and reflection amplitudes not only into the left lead, but also between different
conservation law blocks in the left lead. Generally if leads 𝑖 and 𝑗 both have a conservation law specified,
smatrix.transmission((i, a), (j, b)) gives us the scattering probability of carriers from block
𝑏 of lead 𝑗, to block 𝑎 of lead 𝑖. In our example, reflection from electrons to electrons in the left
lead is thus smatrix.transmission((0, 0), (0, 0)) (Don’t get confused by the fact that it says
transmission – transmission into the same lead is reflection), and reflection from electrons to holes is
smatrix.transmission((0, 1), (0, 0)):
Note that smatrix.submatrix((0, 0), (0, 0)) returns the block concerning reflection of electrons to
electrons, and from its size we can extract the number of modes 𝑁 .
For the default parameters, we obtain the following conductance:
2.00
1.75
1.50
1.25
conductance [e^2/h]
1.00
0.75
0.50
0.25
0.00
0.00 0.05 0.10 0.15 0.20
energy [t]
We a see a conductance that is proportional to the square of the tunneling probability within the gap,
and proportional to the tunneling probability above the gap. At the gap edge, we observe a resonant
Andreev reflection.
Remember that when we defined Builder for the left lead above, we not only declared an electron-
hole conservation law, but also that the Hamiltonian has the particle-hole symmetry 𝒫 = 𝜎𝑦 which
anticommutes with the Hamiltonian, using the argument particle_hole. In Kwant, whenever one or
more of the fundamental discrete symmetries (time-reversal, particle-hole and chiral) are present in a
lead Hamiltonian, they can be declared in Builder. Kwant then automatically uses them to construct
scattering states that obey the specified symmetries. In this example, we have a discrete symmetry
declared in addition to a conservation law. For any two conservation law blocks that are transformed to
each other by the discrete symmetry, Kwant then automatically computes the scattering states of one
block by applying the symmetry operator to the scattering states of the other.
Now, 𝒫 relates electrons and holes at opposite energies. However, a scattering problem is always solved at
a fixed energy, so generally 𝒫 does not give a relation between the electron and hole blocks. The exception
is of course at zero energy, in which case particle-hole symmetry transforms between the electron and
hole blocks, resulting in a symmetric scattering matrix. We can check the symmetry explicitly with
def check_PHS(syst):
# Scattering matrix
s = kwant.smatrix(syst, energy=0)
# Electron to electron block
s_ee = s.submatrix((0,0), (0,0))
# Hole to hole block
s_hh = s.submatrix((0,1), (0,1))
print('s_ee: \n', np.round(s_ee, 3))
print('s_hh: \n', np.round(s_hh[::-1, ::-1], 3))
print('s_ee - s_hh^*: \n',
np.round(s_ee - s_hh[::-1, ::-1].conj(), 3), '\n')
# Electron to hole block
s_he = s.submatrix((0,1), (0,0))
# Hole to electron block
s_eh = s.submatrix((0,0), (0,1))
print('s_he: \n', np.round(s_he, 3))
print('s_eh: \n', np.round(s_eh[::-1, ::-1], 3))
print('s_he + s_eh^*: \n',
np.round(s_he + s_eh[::-1, ::-1].conj(), 3))
s_ee:
[[ 0.538+0.823j 0.000-0.j ]
[ 0.000+0.j 0.515-0.856j]]
s_hh:
[[ 0.538-0.823j -0.000+0.j ]
[ 0.000-0.j 0.515+0.856j]]
s_ee - s_hh^*:
[[ 0.-0.j 0.-0.j]
[-0.-0.j -0.-0.j]]
s_he:
[[-0.000-0.j 0.054-0.j]
[ 0.179-0.j -0.000-0.j]]
s_eh:
[[ 0.000-0.j -0.054-0.j]
[-0.179+0.j 0.000-0.j]]
s_he + s_eh^*:
[[ 0.-0.j 0.-0.j]
[-0.-0.j -0.-0.j]]
Note that 𝒫 flips the sign of momentum, and for the parameters we consider here, there are two electron
and two hole modes active at zero energy. We thus reorder the matrix elements of the scattering matrix
blocks above, to ensure that the same matrix elements in the electron and hole blocks relate scattering
states and their particle hole partners. Technical details
• If you are only interested in particle (thermal) currents you do not need to separate the electron and
hole degrees of freedom. Still, separating them using a conservation law makes the lead calculation
in the solving phase more efficient.
In the previous tutorials we have mainly concentrated on calculating global properties such as conductance
and band structures. Often, however, insight can be gained from calculating locally-defined quantities,
that is, quantities defined over individual sites or hoppings in your system. In the Closed systems
tutorial we saw how we could visualize the density associated with the eigenstates of a system using
kwant.plotter.map.
In this tutorial we will see how we can calculate more general quantities than simple densities by studying
spin transport in a system with a magnetic texture.
See also:
The complete source code of this example can be found in tutorial/magnetic_texture.py
2.7.1 Introduction
Our starting point will be the following spinful tight-binding model on a square lattice:
𝐻 = − ∑ ∑ |𝑖𝛼⟩⟨𝑗𝛼| + 𝐽 ∑ ∑ m𝑖 ⋅ 𝛼𝛽 |𝑖𝛼⟩⟨𝑖𝛽|,
⟨𝑖𝑗⟩ 𝛼 𝑖 𝛼𝛽
where latin indices run over sites, and greek indices run over spin. We can identify the first term as a
nearest-neighbor hopping between like-spins, and the second as a term that couples spins on the same
site. The second term acts like a magnetic field of strength 𝐽 that varies from site to site and that, on
site 𝑖, points in the direction of the unit vector m𝑖 . 𝛼𝛽 is a vector of Pauli matrices. We shall take the
following form for m𝑖 :
𝑇
𝑥𝑖 𝑦
m𝑖 = ( sin 𝜃𝑖 , 2 𝑖 2 sin 𝜃𝑖 , cos 𝜃𝑖 ) ,
𝑥2𝑖 + 𝑦𝑖2 𝑥𝑖 + 𝑦𝑖
𝜋 𝑟 − 𝑟0
𝜃𝑖 = tanh 𝑖 ,
2 𝛿
where 𝑥𝑖 and 𝑦𝑖 are the 𝑥 and 𝑦 coordinates of site 𝑖, and 𝑟𝑖 = √𝑥2𝑖 + 𝑦𝑖2 .
To define this model in Kwant we start as usual by defining functions that depend on the model param-
eters:
if r == 0:
m_i = [0, 0, 1]
else:
m_i = [
(x / r) * sin(theta),
(y / r) * sin(theta),
cos(theta),
]
return np.array(m_i)
and define our system as a square shape on a square lattice with two orbitals per site, with leads attached
on the left and right:
lat = kwant.lattice.square(norbs=2)
def make_system(L=80):
syst = kwant.Builder()
def square(pos):
return all(-L/2 < p < L/2 for p in pos)
syst.attach_lead(lead)
syst.attach_lead(lead.reversed())
return syst
Below is a plot of a projection of m𝑖 onto the x-y plane inside the scattering region. The z component
is shown by the color scale:
1.00
40
0.75
30
20 0.50
10 0.25
0 0.00
10 0.25
20 0.50
30
0.75
40
40 30 20 10 0 10 20 30 40
We will now be interested in analyzing the form of the scattering states that originate from the left lead:
If we were simulating a spinless system with only a single degree of freedom, then calculating the density
on each site would be as simple as calculating the absolute square of the wavefunction like:
density = np.abs(psi)**2
When there are multiple degrees of freedom per site, however, one has to be more careful. In the present
case with two (spin) degrees of freedom per site one could calculate the per-site density like:
With more than one degree of freedom per site we have more freedom as to what local quantities we can
meaningfully compute. For example, we may wish to calculate the local z-projected spin density. We
could calculate this in the following way:
If we wanted instead to calculate the local y-projected spin density, we would need to use an even more
complicated expression:
The kwant.operator module aims to alleviate somewhat this tedious book-keeping by providing a simple
interface for defining operators that act on wavefunctions. To calculate the above quantities we would
use the Density operator like so:
rho = kwant.operator.Density(syst)
rho_sz = kwant.operator.Density(syst, sigma_z)
rho_sy = kwant.operator.Density(syst, sigma_y)
Density takes a System as its first parameter as well as (optionally) a square matrix that defines the
quantity that you wish to calculate per site. When an instance of a Density is then evaluated with a
wavefunction, the quantity
𝜌𝑖 = †𝑖 M𝑖
is calculated for each site 𝑖, where 𝑖 is a vector consisting of the wavefunction components on that site
and M is the square matrix referred to previously.
Below we can see colorplots of the above-calculated quantities. The array that is returned by evaluating
a Density can be used directly with kwant.plotter.map:
0 z y
40 40 40
30 30 30
20 20 20
10 10 10
0 0 0
10 10 10
20 20 20
30 30 30
40 40 40
40 30 20 10 0 10 20 30 40 40 30 20 10 0 10 20 30 40 40 30 20 10 0 10 20 30 40
Technical Details
Although we refer loosely to “densities” and “operators” above, a Density actually represents a collection
of linear operators. This can be made clear by rewriting the above definition of 𝜌𝑖 in the following way:
∗
𝜌𝑖 = ∑ 𝜓𝛼 ℳ𝑖𝛼𝛽 𝜓𝛽
𝛼𝛽
where greek indices run over the degrees of freedom in the Hilbert space of the scattering region and
latin indices run over sites. We can this identify ℳ𝑖𝛼𝛽 as the components of a rank-3 tensor and can
represent them as a “vector of matrices”:
M 0 … 0 0 …
ℳ=⎡⎛
⎜ 0 0 … ⎞
⎟ , ⎛
⎜0 M …⎞⎟ , …⎤
⎢ ⎥
⎣⎝ ⋮ ⋮ ⋱ ⎠ ⎝⋮ ⋮ ⋱⎠ ⎦
where M is defined as in the main text, and the 0 are zero matrices of the same shape as M.
kwant.operator also has a class Current for calculating local currents, analogously to the local “den-
sities” described above. If one has defined a density via a matrix M and the above equation, then one
can define a local current flowing from site 𝑏 to site 𝑎:
where H𝑎𝑏 is the hopping matrix from site 𝑏 to site 𝑎. For example, to calculate the local current and
spin current:
J_0 = kwant.operator.Current(syst)
J_z = kwant.operator.Current(syst, sigma_z)
J_y = kwant.operator.Current(syst, sigma_y)
Evaluating a Current operator on a wavefunction returns a 1D array of values that can be directly used
with kwant.plotter.current:
J0 Jz Jy
40 40 40
30 30 30
20 20 20
10 10 10
0 0 0
10 10 10
20 20 20
30 30 30
40 40 40
40 30 20 10 0 10 20 30 40 40 30 20 10 0 10 20 30 40 40 30 20 10 0 10 20 30 40
Note: Evaluating a Current operator on a wavefunction returns a 1D array of the same length as the
number of hoppings in the system, ordered in the same way as the edges in the system’s graph.
Technical Details
Similarly to how we saw in the previous section that Density can be thought of as a collection of
operators, Current can be defined in a similar way. Starting from the definition of a “density”:
∗
𝜌𝑎 = ∑ 𝜓𝛼 ℳ𝑎𝛼𝛽 𝜓𝛽 ,
𝛼𝛽
𝜕𝜌𝑎
− ∑ 𝐽𝑎𝑏 = 0
𝜕𝑡 𝑏
where the sum runs over sites 𝑏 neigboring site 𝑎. Plugging in the definition for 𝜌𝑎 , along with the
Schrödinger equation and the assumption that ℳ is time independent, gives:
∗
𝐽𝑎𝑏 = ∑ 𝜓𝛼 (𝑖 ∑ ℋ∗𝑎𝑏𝛾𝛼 ℳ𝑎𝛾𝛽 − ℳ𝑎𝛼𝛾 ℋ𝑎𝑏𝛾𝛽 ) 𝜓𝛽 ,
𝛼𝛽 𝛾
where latin indices run over sites and greek indices run over the Hilbert space degrees of freedom, and
⋱ ⋮ ⋮ ⋮ ⋰
⎛
⎜⋯ ⋱ 0 H𝑎𝑏 ⋯⎞⎟
⎜ ⎟
ℋ𝑎𝑏 =⎜
⎜⋯ 0 ⋱ 0 ⋯⎟⎟ .
⎜
⎜⋯ 0 ⎟
0 ⋱ ⋯⎟
⎝⋰ ⋮ ⋮ ⋮ ⋱⎠
i.e. ℋ𝑎𝑏 is a matrix that is zero everywhere except on elements connecting from site 𝑏 to site 𝑎, where
it is equal to the hopping matrix H𝑎𝑏 between these two sites.
This allows us to identify the rank-4 quantity
The above examples are reasonably simple in the sense that the book-keeping required to manually
calculate the various densities and currents is still manageable. Now we shall look at the case where we
wish to calculate some projected spin currents, but where the spin projection axis varies from place to
place. More specifically, we want to visualize the spin current along the direction of m𝑖 , which changes
continuously over the whole scattering region.
Doing this is as simple as passing a function when instantiating the Current, instead of a constant
matrix:
The function must take a Site as its first parameter, and may optionally take other parameters (i.e. it
must have the same signature as a Hamiltonian onsite function), and must return the square matrix that
defines the operator we wish to calculate.
Note: In the above example we had to pass the extra parameters needed by the following_operator
function via the param keyword argument. In general you must pass all the parameters needed by
the Hamiltonian via params (as you would when calling smatrix or wave_function). In the previous
examples, however, we used the fact that the system hoppings do not depend on any parameters (these
are the only Hamiltonian elements required to calculate currents) to avoid passing the system parameters
for the sake of brevity.
Using this we can see that the spin current is essentially oriented along the direction of 𝑚𝑖 in the present
regime where the onsite term in the Hamiltonian is dominant:
Jmi Jz
40 40
30 30
20 20
10 10
0 0
10 10
20 20
30 30
40 40
40 30 20 10 0 10 20 30 40 40 30 20 10 0 10 20 30 40
Note: Although this example used exclusively Current, you can do the same with Density.
Another useful feature of kwant.operator is the ability to calculate operators over selected parts of a
system. For example, we may wish to calculate the total density of states in a certain part of the system,
or the current flowing through a cut in the system. We can do this selection when creating the operator
by using the keyword parameter where.
To calculate the density of states inside a circle of radius 20 we can simply do:
def circle(site):
return np.linalg.norm(site.pos) < 20
note that we also provide sum=True, which means that evaluating the operator on a wavefunction will
produce a single scalar. This is semantically equivalent to providing sum=False (the default) and running
numpy.sum on the output.
Below we calculate the probability current and z-projected spin current near the interfaces with the left
and right leads.
def left_cut(site_to, site_from):
return site_from.pos[0] <= -39 and site_to.pos[0] > -39
We see that the probability current is conserved across the scattering region, but the z-projected spin
current is not due to the fact that the Hamiltonian does not commute with 𝜎𝑧 everywhere in the scattering
region.
Note: where can also be provided as a sequence of Site or a sequence of hoppings (i.e. pairs of Site),
rather than a function.
In most of the above examples we only used each operator once after creating it. Often one will want to
evaluate an operator with many different wavefunctions, for example with all scattering wavefunctions at
a certain energy, but with the same set of parameters. In such cases it is best to tell the operator to pre-
compute the onsite matrices and any necessary Hamiltonian elements using the given set of parameters,
so that this work is not duplicated every time the operator is evaluated.
This can be achieved with bind:
Warning: Take care that you do not use an operator that was bound to a particular set of parameters
with wavefunctions calculated with a different set of parameters. This will almost certainly give
incorrect results.
# Sum current local from all scattering states on the left at energy=-1
wf_left = wf(0)
J_m_left = sum(J_m_bound(p) for p in wf_left)
J_z_left = sum(J_z_bound(p) for p in wf_left)
30 30
20 20
10 10
0 0
10 10
20 20
30 30
40 40
40 30 20 10 0 10 20 30 40 40 30 20 10 0 10 20 30 40
The plotting functionality of Kwant has been used extensively (through plot and map) in the previous
tutorials. In addition to this basic use, plot offers many options to change the plotting style extensively.
It is the goal of this tutorial to show how these options can be used to achieve various very different
objectives.
See also:
The complete source code of this example can be found in tutorial/plot_graphene.py
We begin by first considering a circular graphene quantum dot (similar to what has been used in parts
of the tutorial Beyond square lattices: graphene.) In contrast to previous examples, we will also use
hoppings beyond next-nearest neighbors:
lat = kwant.lattice.honeycomb()
a, b = lat.sublattices
def circle(pos):
x, y = pos
return x**2 + y**2 < r**2
syst = kwant.Builder()
syst[lat.shape(circle, (0, 0))] = 0
syst[lat.neighbors()] = t
syst.eradicate_dangling()
if tp:
syst[lat.neighbors(2)] = tp
return syst
Note that adding hoppings hoppings to the n-th nearest neighbors can be simply done by passing n
as an argument to neighbors. Also note that we use the method eradicate_dangling to get rid of
single atoms sticking out of the shape. It is necessary to do so before adding the next-nearest-neighbor
hopping1 .
Of course, the system can be plotted simply with default settings:
def plot_system(syst):
kwant.plot(syst)
However, due to the richer structure of the lattice, this results in a rather busy plot:
8 6 4 2 0 2 4 6 8
A much clearer plot can be obtained by using different colors for both sublattices, and by having different
line widths for different hoppings. This can be achieved by passing a function to the arguments of plot,
instead of a constant. For properties of sites, this must be a function taking one site as argument, for
hoppings a function taking the start end end site of hopping as arguments:
def family_color(site):
return 'black' if site.family == a else 'white'
1 A dangling site is defined as having only one hopping connecting it to the rest. With next-nearest-neighbor hopping
also all sites that are dangling with only nearest-neighbor hopping have more than one hopping.
Note that since we are using an unfinalized Builder, a site is really an instance of Site. With these
adjustments we arrive at a plot that carries the same information, but is much easier to interpret:
8 6 4 2 0 2 4 6 8
Apart from plotting the system itself, plot can also be used to plot data living on the system.
As an example, we now compute the eigenstates of the graphene quantum dot and intend to plot the
wave function probability in the quantum dot. For aesthetic reasons (the wave functions look a bit nicer),
we restrict ourselves to nearest-neighbor hopping. Computing the wave functions is done in the usual
way (note that for a large-scale system, one would probably want to use sparse linear algebra):
def plot_data(syst, n):
import scipy.linalg as la
syst = syst.finalized()
ham = syst.hamiltonian_submatrix()
evecs = la.eigh(ham)[1]
wf = abs(evecs[:, n])**2
In most cases, to plot the wave function probability, one wouldn’t use plot, but rather map. Here, we
plot the n-th wave function using it:
This results in a standard pseudocolor plot, showing in this case (n=225) a graphene edge state, i.e. a
wave function mostly localized at the zigzag edges of the quantum dot.
0.035
8
6 0.030
4
0.025
2
0.020
0
0.015
2
4 0.010
6
0.005
6 4 2 0 2 4 6
0.000
However although in general preferable, map has a few deficiencies for this small system: For example,
there are a few distortions at the edge of the dot. (This cannot be avoided in the type of interpolation
used in map). However, we can also use plot to achieve a similar, but smoother result.
For this note that plot can also take an array of floats (or function returning floats) as value for the
site_color argument (the same holds for the hoppings). Via the colormap specified in cmap these are
mapped to color, just as map does! In addition, we can also change the symbol shape depending on the
sublattice. With a triangle pointing up and down on the respective sublattice, the symbols used by plot
fill the space completely:
def family_shape(i):
site = syst.sites[i]
return ('p', 3, 180) if site.family == a else ('p', 3, 0)
def family_color(i):
return 'black' if syst.sites[i].family == a else 'white'
Note that with hop_lw=0 we deactivate plotting the hoppings (that would not serve any purpose here).
Moreover, site_size=0.5 guarantees that the two different types of triangles touch precisely: By default,
plot takes all sizes in units of the nearest-neighbor spacing. site_size=0.5 thus means half the distance
between neighboring sites (and for the triangles this is interpreted as the radius of the inner circle).
Finally, note that since we are dealing with a finalized system now, a site i is represented by an integer.
In order to obtain the original Site, syst.sites[i] can be used.
With this we arrive at
0.035
8
0.030
6
4 0.025
2
0.020
0
2 0.015
4
0.010
6
0.005
8 6 4 2 0 2 4 6 8
0.000
The way how data is presented of course influences what features of the data are best visible in a given
plot. With plot one can easily go beyond pseudocolor-like plots. For example, we can represent the
wave function probability using the symbols itself:
def site_size(i):
return 3 * wf[i] / wf.max()
Here, we choose the symbol size proportional to the wave function probability, while the site color is
transparent to also allow for overlapping symbols to be visible. The hoppings are also plotted in order
to show the underlying lattice.
With this, we arrive at
8 6 4 2 0 2 4 6 8
which shows the edge state nature of the wave function most clearly.
See also:
The complete source code of this example can be found in tutorial/plot_zincblende.py
Zincblende is a very common crystal structure of semiconductors. It is a face-centered cubic crystal with
two inequivalent atoms in the unit cell (i.e. two different types of atoms, unlike diamond which has the
same crystal structure, but two equivalent atoms per unit cell).
It is very easily generated in Kwant with kwant.lattice.general:
Note how we keep references to the two different sublattices for later use.
A three-dimensional structure is created as easily as in two dimensions, by using the shape-functionality:
syst = kwant.Builder()
syst[lat.shape(cuboid_shape, (0, 0, 0))] = None
syst[lat.neighbors()] = None
return syst
We restrict ourselves here to a simple cuboid, and do not bother to add real values for onsite and hopping
energies, but only the placeholder None (in a real calculation, several atomic orbitals would have to be
considered).
plot can plot 3D systems just as easily as its two-dimensional counterparts:
syst = make_cuboid()
kwant.plot(syst)
resulting in
8
6
4
2
0
2
4
12
10
8
0 6
2 4
4 2
6
8 0
10
12 2
14
You might notice that the standard options for plotting are quite different in 3D than in 2D. For example,
by default hoppings are not printed, but sites are instead represented by little “balls” touching each other
(which is achieved by a default site_size=0.5). In fact, this style of plotting 3D shows quite decently
the overall geometry of the system.
When plotting into a window, the 3D plots can also be rotated and scaled arbitrarily, allowing for a good
inspection of the geometry from all sides.
Note: Interactive 3D plots usually do not have the proper aspect ratio, but are a bit squashed.
This is due to bugs in matplotlib’s 3D plotting module that does not properly honor the corresponding
arguments. By resizing the plot window however one can manually adjust the aspect ratio.
Also for 3D it is possible to customize the plot. For example, we can explicitly plot the hoppings as lines,
and color sites differently depending on the sublattice:
syst = make_cuboid(a=1.5, b=1.5, c=1.5)
def family_colors(site):
return 'r' if site.family == a else 'g'
which results in a 3D plot that allows to interactively (when plotted in a window) explore the crystal
structure:
1.2
1.0
0.8
0.6
0.4
0.2
0.0
1.2
1.0
0.8
0.0 0.6
0.2
0.4 0.4
0.6
0.8 0.2
1.0 0.0
1.2
Hence, a few lines of code using Kwant allow to explore all the different crystal lattices out there!
Note:
• The 3D plots are in fact only fake 3D. For example, sites will always be plotted above hoppings
(this is due to the limitations of matplotlib’s 3d module)
• Plotting hoppings in 3D is inherently much slower than plotting sites. Hence, this is not done by
default.
We have already seen in the “Closed systems” tutorial that we can use Kwant simply to build Hamilto-
nians, which we can then directly diagonalize using routines from Scipy.
This already allows us to treat systems with a few thousand sites without too many problems. For larger
systems one is often not so interested in the exact eigenenergies and eigenstates, but more in the density
of states.
The kernel polynomial method (KPM), is an algorithm to obtain a polynomial expansion of the density
of states. It can also be used to calculate the spectral density of arbitrary operators. Kwant has an
implementation of the KPM method that is based on the algorithms presented in Ref.1 .
Roughly speaking, KPM approximates the density of states (or any other spectral density) by expanding
the action of the Hamiltonian (and operator of interest) on a (small) set of random vectors as a sum
of Chebyshev polynomials up to some order, and then averaging. The accuracy of the method can be
tuned by modifying the order of the Chebyshev expansion and the number of random vectors. See notes
on accuracy below for details.
See also:
The complete source code of this example can be found in tutorial/kernel_polynomial_method.py
Performance and accuracy
The KPM method is especially well suited for large systems, and in the case when one is not interested
in individual eigenvalues, but rather in obtaining an approximate spectral density.
The accuracy in the energy resolution is dominated by the number of moments. The lowest accuracy is
at the center of the spectrum, while slightly higher accuracy is obtained at the edges of the spectrum.
If we use the KPM method (with the Jackson kernel, see Ref.1 ) to describe a delta peak at the center
of the spectrum, we will obtain a function similar to a Gaussian of width 𝜎 = 𝜋𝑎/𝑁 , where 𝑁 is the
number of moments, and 𝑎 is the width of the spectrum.
On the other hand, the random vectors will explore the range of the spectrum, and as the system gets
bigger, the number of random vectors that are necessary to sample the whole spectrum reduces. Thus,
a small number of random vectors is in general enough, and increasing this number will not result in a
visible improvement of the approximation.
2.9.1 Introduction
Our aim is to use the kernel polynomial method to obtain the spectral density 𝜌𝐴 (𝐸), as a function of
the energy 𝐸, of some Hilbert space operator 𝐴. We define
𝜌𝐴 (𝐸) = 𝜌(𝐸)𝐴(𝐸),
where 𝐴(𝐸) is the expectation value of 𝐴 for all the eigenstates of the Hamiltonian with energy 𝐸, and
the density of states is
𝐷−1
𝜌(𝐸) = ∑ 𝛿(𝐸 − 𝐸𝑘 ),
𝑘=0
In the following example, we will use the KPM implementation in Kwant to obtain the density of states
of a graphene disk.
1 Rev. Mod. Phys., Vol. 78, No. 1 (2006).
# necessary imports
import kwant
import numpy as np
def circle(pos):
x, y = pos
return x ** 2 + y ** 2 < r ** 2
return syst
After making a system we can then create a SpectralDensity object that represents the density of
states for this system.
fsyst = make_syst().finalized()
spectrum = kwant.kpm.SpectralDensity(fsyst)
The SpectralDensity can then be called like a function to obtain a sequence of energies in the spectrum
of the Hamiltonian, and the corresponding density of states at these energies.
When called with no arguments, an optimal set of energies is chosen (these are not evenly distributed
over the spectrum, see Ref.1 for details), however it is also possible to provide an explicit sequence of
energies at which to evaluate the density of states.
energy_subset = np.linspace(0, 2)
density_subset = spectrum(energy_subset)
densities
2000 density subset
1500
DoS [a.u.]
1000
500
0
3 2 1 0 1 2 3
energy [t]
In addition to being called like functions, SpectralDensity objects also have a method integrate which
can be used to integrate the density of states against some distribution function over the whole spectrum.
If no distribution function is specified, then the uniform distribution is used:
We see that the integral of the density of states is normalized to the total number of available states in
the system. If we wish to calculate, say, the number of states populated in equilibrium, then we should
integrate with respect to a Fermi-Dirac distribution:
SpectralDensity has two methods for increasing the accuracy of the method, each of which offers
different levels of control over what exactly is changed.
The simplest way to obtain a more accurate solution is to use the add_moments method:
spectrum.add_moments(energy_resolution=0.03)
This will update the number of calculated moments and also the default number of sampling points
such that the maximum distance between successive energy points is energy_resolution (see notes on
accuracy).
density
2500 higher energy resolution
2000
1500
DoS [a.u.]
1000
500
0
3 2 1 0 1 2 3
energy [t]
Alternatively, you can directly increase the number of moments with add_moments, or the number of
random vectors with add_vectors.
spectrum.add_moments(100)
spectrum.add_vectors(5)
density
2500 higher number of moments
2000
DoS [a.u.]
1500
1000
500
0
3 2 1 0 1 2 3
energy [t]
Above, we saw how to calculate the density of states by creating a SpectralDensity and passing it a
finalized Kwant system. When instantiating a SpectralDensity we may optionally supply an operator
in addition to the system. In this case it is the spectral density of the given operator that is calculated.
SpectralDensity accepts the operators in a few formats:
• explicit matrices (numpy array of scipy sparse matrices will work)
• operators from kwant.operator
If an explicit matrix is provided then it must have the same shape as the system Hamiltonian.
# identity matrix
matrix_op = scipy.sparse.eye(len(fsyst.sites))
matrix_spectrum = kwant.kpm.SpectralDensity(fsyst, operator=matrix_op)
Using operators from kwant.operator allows us to calculate quantities such as the local density of states
by telling the operator not to sum over all the sites of the system:
SpectralDensity will properly handle this vector output, which allows us to plot the local density of
states at different point in the spectrum:
zero_energy_ldos = local_dos(energy=0)
finite_energy_ldos = local_dos(energy=1)
energy = 0 energy = 1
30 30
20 20
10 10
0 0
10 10
20 20
30 30
30 20 10 0 10 20 30 30 20 10 0 10 20 30
This nicely illustrates the edge states of the graphene dot at zero energy, and the bulk states at higher
energy.
By default SpectralDensity will use random vectors whose components are unit complex numbers with
phases drawn from a uniform distribution. There are several reasons why you may wish to make a
different choice of distribution for your random vectors, for example to enforce certain symmetries or to
only use real-valued vectors.
To change how the random vectors are generated, you need only specify a function that takes the
dimension of the Hilbert space as a single parameter, and which returns a vector in that Hilbert space:
custom_factory = kwant.kpm.SpectralDensity(fsyst,
vector_factory=binary_vectors)
Reproducible calculations
Because KPM internally uses random vectors, running the same calculation twice will not give bit-for-bit
the same result. However, similarly to the funcions in rmt, the random number generator can be directly
manipulated by passing a value to the rng parameter of SpectralDensity. rng can itself be a random
number generator, or it may simply be a seed to pass to the numpy random number generator (that is
used internally by default).
Above, we showed how SpectralDensity can calculate the spectral density of operators, and how we can
define operators by using kwant.operator. If you need even more flexibility, SpectralDensity will also
accept a function as its operator parameter. This function must itself take two parameters, (bra, ket)
and must return either a scalar or a one-dimensional array. In order to be meaningful the function must
be a sesquilinear map, i.e. antilinear in its first argument, and linear in its second argument. Below, we
compare two methods for computing the local density of states, one using kwant.operator.Density,
and the other using a custom function.
References
2.10.1 Introduction
In “Discretization of a Schrödinger Hamiltonian” we have learnt that Kwant works with tight-binding
Hamiltonians. Often, however, one will start with a continuum model and will subsequently need to
discretize it to arrive at a tight-binding model. Although discretizing a Hamiltonian is usually a simple
process, it is tedious and repetitive. The situation is further exacerbated when one introduces additional
on-site degrees of freedom, and tracking all the necessary terms becomes a chore. The continuum sub-
package aims to be a solution to this problem. It is a collection of tools for working with continuum
models and for discretizing them into tight-binding models.
See also:
The complete source code of this tutorial can be found in tutorial/discretize.py
As an example, let us consider the following continuum Schrödinger equation for a semiconducting
heterostructure (using the effective mass approximation):
ℏ2
(𝑘𝑥 𝑘 ) 𝜓(𝑥) = 𝐸 𝜓(𝑥).
2𝑚(𝑥) 𝑥
𝑘𝛼 = −𝑖𝜕𝛼 ,
for 𝛼 = 𝑥, 𝑦 or 𝑧, and discretizing on a regular lattice of points with spacing 𝑎, we obtain the tight-binding
model
1 𝑎 1 𝑎 𝑎
𝐻=− ∑ 𝐴 (𝑥 + ) ( |𝑖⟩ ⟨𝑖 + 1| + ℎ.𝑐.) + 2 ∑ (𝐴 (𝑥 + ) + 𝐴 (𝑥 − )) |𝑖⟩ ⟨𝑖| ,
𝑎2 𝑖 2 𝑎 𝑖 2 2
with 𝐴(𝑥) = ℏ2 .
2𝑚(𝑥)
The function kwant.continuum.discretize takes a symbolic Hamiltonian and turns it into a Builder
instance with appropriate spatial symmetry that serves as a template. (We will see how to use the
template to build systems with a particular shape later).
It is worth noting that discretize treats k_x and x as non-commuting operators, and so their order is
preserved during the discretization process.
The builder produced by discretize may be printed to show the source code of its onsite and hopping
functions (this is a special feature of builders returned by discretize):
# Discrete coordinates: x
# Onsite element:
def onsite(site, A):
(x, ) = 1 * site.tag
_const_0 = (A(1/2 + x))
_const_1 = (A(-1/2 + x))
return (_const_0 + _const_1)
Technical details
• kwant.continuum uses sympy internally to handle symbolic expressions. Strings are converted using
kwant.continuum.sympify, which essentially applies some Kwant-specific rules (such as treating
k_x and x as non-commutative) before calling sympy.sympify
• The builder returned by discretize will have an N-D translational symmetry, where N is the
number of dimensions that were discretized. This is the case, even if there are expressions in the
input (e.g. V(x, y)) which in principle may not have this symmetry. When using the returned
builder directly, or when using it as a template to construct systems with different/lower symmetry,
it is important to ensure that any functional parameters passed to the system respect the symmetry
of the system. Kwant provides no consistency check for this.
• The discretization process consists of taking input 𝐻(𝑘𝑥 , 𝑘𝑦 , 𝑘𝑧 ), multiplying it from the right by
𝜓(𝑥, 𝑦, 𝑧) and iteratively applying a second-order accurate central derivative approximation for
every 𝑘𝛼 = −𝑖𝜕𝛼 :
1 𝑎 𝑎
𝜕𝛼 𝜓(𝛼) = (𝜓 (𝛼 + ) − 𝜓 (𝛼 − )) .
𝑎 2 2
This process is done separately for every summand in Hamiltonian. Once all symbols denoting
operators are applied internal algorithm is calculating gcd for hoppings coming from each summand
in order to find best possible approximation. Please see source code for details.
• Instead of using discretize one can use discretize_symbolic to obtain symbolic output. When
working interactively in Jupyter notebooks it can be useful to use this to see a symbolic rep-
resentation of the discretized Hamiltonian. This works best when combined with sympy Pretty
Printing.
• The symbolic result of discretization obtained with discretize_symbolic can be converted into
a builder using build_discretized. This can be useful if one wants to alter the tight-binding
Hamiltonian before building the system.
Let us now use the output of discretize as a template to build a system and plot some of its energy
eigenstate. For this example the Hamiltonian will be
We now use this system with the fill method of Builder to construct the system we want to investigate:
def stadium(site):
(x, y) = site.pos
x = max(abs(x) - 20, 0)
return x**2 + y**2 < 30**2
syst = kwant.Builder()
syst.fill(template, stadium, (0, 0));
syst = syst.finalized()
After finalizing this system, we can plot one of the system’s energy eigenstates:
0.0014
0.0012
20 0.0010
10
0.0008
0
10 0.0006
20 0.0004
40 20 0 20 40 0.0002
When working with multi-band systems, like the Bernevig-Hughes-Zhang (BHZ) model12 , one can pro-
vide matrix input to discretize using identity and kron. For example, the definition of the BHZ
model can be written succinctly as:
hamiltonian = """
+ C * identity(4) + M * kron(sigma_0, sigma_z)
- B * (k_x**2 + k_y**2) * kron(sigma_0, sigma_z)
- D * (k_x**2 + k_y**2) * kron(sigma_0, sigma_0)
+ A * k_x * kron(sigma_z, sigma_x)
- A * k_y * kron(sigma_0, sigma_y)
"""
def shape(site):
(x, y) = site.pos
return (0 <= y < W and 0 <= x < L)
def lead_shape(site):
(x, y) = site.pos
return (0 <= y < W)
syst = kwant.Builder()
syst.fill(template, shape, (0, 0))
syst.attach_lead(lead)
syst.attach_lead(lead.reversed())
syst = syst.finalized()
kwant.plotter.bands(syst.leads[0], params=params,
momenta=np.linspace(-0.3, 0.3, 201), show=False)
0.04
0.02
energy [eV]
0.00
0.02
0.04
def h_k(k_x):
p = dict(k_x=k_x, **params)
return syst.hamiltonian_submatrix(params=p)
Below we can see the continuum and tight-binding dispersions for two different values of the discretization
grid spacing 𝑎:
a=1 a=0.25
14
continuum continuum
12 tight-binding tight-binding
10
8
energy [a.u.]
energy [a.u.]
6
4
2
0
4 3 2 1 0 1 2 3 4 4 3 2 1 0 1 2 3 4
momentum [a.u.] momentum [a.u.]
We clearly see that the smaller grid spacing is, the better we approximate the original continuous dis-
persion. It is also worth remembering that the Brillouin zone also scales with grid spacing: [− 𝜋𝑎 , 𝜋𝑎 ].
sympify = kwant.continuum.sympify
subs = {'sx': [[0, 1], [1, 0]], 'sz': [[1, 0], [0, -1]]}
e = (
sympify('[[k_x**2, alpha * k_x], [k_x * alpha, -k_x**2]]'),
sympify('k_x**2 * sigma_z + alpha * k_x * sigma_x'),
sympify('k_x**2 * sz + alpha * k_x * sx', locals=subs),
)
True
We can use the locals keyword parameter to substitute expressions and numerical values:
Symbolic expressions obtained in this way can be directly passed to all discretizer functions. Tech-
nical details
Because of the way that sympy handles commutation relations all symbols representing position and
momentum operators are set to be non commutative. This means that the order of momentum and
position operators in the input expression is preserved. Note that it is not possible to define individual
commutation relations within sympy, even expressions such 𝑥𝑘𝑦 𝑥 will not be simplified, even though
mathematically [𝑥, 𝑘𝑦 ] = 0.
References
THREE
CORE MODULES
For convenience, short names are provided for a few widely used objects from the sub-packages. Other-
wise, this package has only very limited functionality of its own.
kwant.KwantDeprecationWarning
exception kwant.KwantDeprecationWarning
Bases: Warning
Class of warnings about a deprecated feature of Kwant.
DeprecationWarning has been made invisible by default in Python 2.7 in order to not confuse non-
developer users with warnings that are not relevant to them. In the case of Kwant, by far most
users are developers, so we feel that a KwantDeprecationWarning that is visible by default is useful.
kwant.UserCodeError
exception kwant.UserCodeError
Bases: Exception
Class for errors that occur in user-provided code.
Usually users will define value functions that Kwant calls in order to evaluate the Hamiltonian. If
one of these function raises an exception then it is caught and this error is raised in its place. This
makes it clear that the error is from the user’s code (and not a bug in Kwant) and also makes it
possible for any libraries that wrap Kwant to detect when a user’s function causes an error.
85
Kwant 1.3.1 documentation
greens_function(sys[, energy, args, ...]) Compute the retarded Green’s function of the sys-
tem between its leads.
ldos(sys[, energy, args, check_hermiticity, ...]) Calculate the local density of states of a system at a
given energy.
smatrix(sys[, energy, args, out_leads, ...]) Compute the scattering matrix of a system.
wave_function(sys[, energy, args, ...]) Return a callable object for the computation of the
wave function inside the scattering region.
3.2.1 Types
kwant.builder.Builder
Notes
Values can be also functions that receive the site or the hopping (passed to the function as two sites)
and possibly additional arguments and are expected to return a valid value. This allows to define
systems quickly, to modify them without reconstructing, and to save memory for many-orbital
models.
In addition to simple keys (single sites and hoppings) more powerful keys are possible as well
that allow to manipulate multiple sites/hoppings in a single operation. Such keys are internally
expanded into a sequence of simple keys by using the method Builder.expand. For example,
syst[general_key] = value is equivalent to
Builder instances automatically ensure that every hopping is Hermitian, so that if builder[a, b]
has been set, there is no need to set builder[b, a].
Builder instances can be made to automatically respect a Symmetry that is passed to them during
creation. The behavior of builders with a symmetry is slightly more sophisticated: all keys are
mapped to the fundamental domain of the symmetry before storing them. This may produce
confusing results when neighbors of a site are queried.
The method attach_lead works only if the sites affected by them have tags which are sequences
of integers. It makes sense only when these sites live on a regular lattice, like the ones provided by
kwant.lattice.
Attaching a lead manually (without the use of attach_lead) amounts to creating a Lead object
and appending it to this list.
Warning: If functions are used to set values in a builder with a symmetry, then they must sat-
isfy the same symmetry. There is (currently) no check and wrong results will be the consequence
of a misbehaving function.
Examples
Define a site.
>>> print(builder[site])
Define a hopping.
Delete a site.
Detach the last lead. (This does not remove the sites that were added to the scattering region by
attach_lead.)
Attributes
leads (list of Lead instances) The leads that are attached to the system.
Methods
Notes
This method is not fool-proof, i.e. if it returns an error, there is no guarantee that the system
stayed unaltered.
The lead numbering starts from zero and increments from there, i.e. the leads are numbered
in the order in which they are attached.
closest(pos)
Return the site that is closest to the given position.
This function takes into account the symmetry of the builder. It is assumed that the symmetry
is a translational symmetry.
This function executes in a time proportional to the number of sites, so it is not efficient
for large builders. It is especially slow for builders with a symmetry, but such systems often
contain only a limited number of sites.
dangling()
Return an iterator over all dangling sites.
degree(site)
Return the number of neighbors of a site.
eradicate_dangling()
Keep deleting dangling sites until none are left.
expand(key)
Expand a general key into an iterator over simple keys.
Parameters key : builder key (see notes)
The key to be expanded
Notes
This method is internally used to expand the keys when getting or deleting items of a builder
(i.e. syst[key] = value or del syst[key]).
fill(template, shape, start, *, max_sites=10000000)
Populate builder using another one as a template.
Starting from one or multiple sites, traverse the graph of the template builder and copy sites
and hoppings to the target builder. The traversal stops at sites that are already present in
the target and on sites that are not inside the provided shape.
This function takes into account translational symmetry. As such, typically the template will
have a higher symmetry than the target.
Newly added sites are connected by hoppings to sites that were already present. This facilitates
construction of a system by a series of calls to ‘fill’.
Parameters template : Builder instance
The builder used as the template. The symmetry of the target builder must
be a subgroup of the symmetry of the template.
shape : callable
A boolean function of site returning whether the site should be added to the
target builder or not. The shape must be compatible with the symmetry of
the target builder.
start : Site instance or iterable thereof or iterable of numbers
The site(s) at which the the flood-fill starts. If start is an iterable of numbers,
the starting site will be template.closest(start).
max_sites : positive number
The maximal number of sites that may be added before RuntimeError is
raised. Used to prevent using up all memory.
Returns added_sites : list of Site objects that were added to the system.
Raises ValueError
If the symmetry of the target isn’t a subgroup of the template symmetry.
RuntimeError
If more than max_sites sites are to be added. The target builder will be left
in an unusable state.
Notes
This function uses a flood-fill algorithm. If the template builder consists of disconnected parts,
the fill will stop at their boundaries.
finalized()
Return a finalized (=usable with solvers) copy of the system.
Returns finalized_system : kwant.system.FiniteSystem
If there is no symmetry.
finalized_system : kwant.system.InfiniteSystem
If a symmetry is present.
Notes
This method does not modify the Builder instance for which it is called.
Upon finalization, it is implicitly assumed that every function assigned as a value to a builder
with a symmetry possesses the same symmetry.
Attached leads are also finalized and will be present in the finalized system to be returned.
Currently, only Builder instances without or with a 1D translational Symmetry can be finalized.
hopping_value_pairs()
Return an iterator over all (hopping, value) pairs.
hoppings()
Return an iterator over all Builder hoppings.
The hoppings that are returned belong to the fundamental domain of the Builder symmetry,
and are not necessarily the ones that were set initially (but always the equivalent ones).
neighbors(site)
Return an iterator over all neighbors of a site.
Technical note: This method respects the symmetry of the builder, i.e. the returned sites are
really connected to the given site (and not to its image in the fundamental domain).
reversed()
Return a shallow copy of the builder with the symmetry reversed.
This method can be used to attach the same infinite system as lead from two opposite sides.
It requires a builder to which an infinite symmetry is associated.
site_value_pairs()
Return an iterator over all (site, value) pairs.
sites()
Return a read-only set over all sites.
The sites that are returned belong to the fundamental domain of the Builder symmetry, and
are not necessarily the ones that were set initially (but always the equivalent ones).
update(other)
Update builder from other.
All sites and hoppings of other, together with their values, are written to self, overwriting
already existing sites and hoppings. The leads of other are appended to the leads of the
system being updated.
This method requires that both builders share the same symmetry.
kwant.builder.Site
class kwant.builder.Site
Bases: tuple
A site, member of a SiteFamily.
Sites are the vertices of the graph which describes the tight binding system in a Builder.
A site is uniquely identified by its family and its tag.
Parameters family : an instance of SiteFamily
The ‘type’ of the site.
tag : a hashable python object
The unique identifier of the site within the site family, typically a vector of
integers.
Raises ValueError
If tag is not a proper tag for family.
Notes
For convenience, family(*tag) can be used instead of Site(family, tag) to create a site.
The parameters of the constructor (see above) are stored as instance variables under the same
names. Given a site site, common things to query are thus site.family, site.tag, and site.
pos.
Methods
Attributes
family
The site family to which the site belongs.
pos
Real space position of the site.
This relies on family having a pos method (see SiteFamily).
tag
The tag of the site.
kwant.builder.HoppingKind
class kwant.builder.HoppingKind
Bases: tuple
A pattern for matching hoppings.
A hopping (a, b) matches precisely when the site family of a equals family_a and that of b
equals family_b and (a.tag - b.tag) is equal to delta. In other words, the matching hoppings
have the form: (family_a(x + delta), family_b(x))
Parameters delta : Sequence of integers
The sequence is interpreted as a vector with integer elements.
family_a : SiteFamily
family_b : SiteFamily or None (default)
The default value means: use the same family as family_a.
Notes
A HoppingKind is a callable object: When called with a Builder as sole argument, an instance of
this class will return an iterator over all possible matching hoppings whose sites are already present
in the system. The hoppings do not have to be already present in the system. For example:
Because a Builder can be indexed with functions or iterables of functions, HoppingKind instances
(or any non-tuple iterables of them, e.g. a list) can be used directly as “wildcards” when setting
or deleting hoppings:
Methods
Attributes
delta
The difference between the tags of the hopping’s sites
family_a
The family of the first site in the hopping
family_b
The family of the second site in the hopping
kwant.builder.SimpleSiteFamily
Methods
normalize_tag(tag)
kwant.builder.BuilderLead
Notes
The hopping from the scattering region to the lead is assumed to be equal to the hopping from a
lead unit cell to the next one in the direction of the symmetry vector (i.e. the lead is ‘leaving’ the
system and starts with a hopping).
Every system has an attribute leads, which stores a list of BuilderLead objects with all the infor-
mation about the leads that are attached.
Attributes
Methods
finalized()
Return a kwant.system.InfiniteSystem corresponding to the compressed lead.
The order of interface sites is kept during finalization.
kwant.builder.SelfEnergyLead
Methods
finalized()
Trivial finalization: the object is returned itself.
selfenergy(energy, args=(), *, params=None)
kwant.builder.ModesLead
Methods
finalized()
Trivial finalization: the object is returned itself.
modes(energy, args=(), *, params=None)
selfenergy(energy, args=(), *, params=None)
kwant.builder.SiteFamily
Methods
normalize_tag(tag)
Return a normalized version of the tag.
Raises TypeError or ValueError if the tag is not acceptable.
kwant.builder.Symmetry
class kwant.builder.Symmetry
Bases: object
Abstract base class for spatial symmetries.
Many physical systems possess a discrete spatial symmetry, which results in special properties of
these systems. This class is the standard way to describe discrete spatial symmetries in Kwant.
An instance of this class can be passed to a Builder instance at its creation. The most important
kind of symmetry is translational symmetry, used to define scattering leads.
Each symmetry has a fundamental domain – a set of sites and hoppings, generating all the possible
sites and hoppings upon action of symmetry group elements. A class derived from Symmetry has
to implement mapping of any site or hopping into the fundamental domain, applying a symmetry
group element to a site or a hopping, and a method which to determine the group element bringing
some site from the fundamental domain to the requested one. Additionally, it has to have a property
num_directions returning the number of independent symmetry group generators (number of
elementary periods for translational symmetry).
A ValueError must be raised by the symmetry class whenever a symmetry is used together with
sites whose site family is not compatible with it. A typical example of this is when the vector
defining a translational symmetry is not a lattice vector.
The type of the domain objects as handled by the methods of this class is not specified. The only
requirement is that it must support the unary minus operation. The reference implementation of
to_fd() is hence self.act(-self.which(a), a, b).
Methods
act(element, a, b=None)
Act with a symmetry group element on a site or hopping.
has_subgroup(other)
Test whether self has the subgroup other...
or, in other words, whether other is a subgroup of self. The reason why this is the abstract
method (and not is_subgroup) is that in general it’s not possible for a subgroup to know its
supergroups.
in_fd(site)
Tell whether site lies within the fundamental domain.
subgroup(*generators)
Return the subgroup generated by a sequence of group elements.
to_fd(a, b=None)
Map a site or hopping to the fundamental domain.
If b is None, return a site equivalent to a within the fundamental domain. Otherwise, return a
hopping equivalent to (a, b) but where the first element belongs to the fundamental domain.
Equivalent to self.act(-self.which(a), a, b).
which(site)
Calculate the domain of the site.
Return the group element whose action on a certain site from the fundamental domain will
result in the given site.
Attributes
num_directions
Number of elementary periods of the symmetry.
kwant.builder.Lead
class kwant.builder.Lead
Bases: object
Abstract base class for leads that can be attached to a Builder.
To attach a lead to a builder, append it to the builder’s leads instance variable. See the documen-
tation of kwant.builder for the concrete classes of leads derived from this one.
Attributes
Methods
finalized()
Return a finalized version of the lead.
Returns finalized_lead
Notes
The finalized lead must be an object that can be used as a lead in a kwant.system.
FiniteSystem. It could be an instance of kwant.system.InfiniteSystem for example.
The order of sites for the finalized lead must be the one specified in interface.
3.3.1 General
kwant.lattice.TranslationalSymmetry
class kwant.lattice.TranslationalSymmetry(*periods)
Bases: kwant.builder.Symmetry
A translational symmetry defined in real space.
Group elements of this symmetry are integer tuples of appropriate length.
Parameters p0, p1, p2, ... : sequences of real numbers
The symmetry periods in real space.
Notes
This symmetry automatically chooses the fundamental domain for each new SiteFamily it en-
counters. If this site family does not correspond to a Bravais lattice, or if it does not have a
commensurate period, an error is produced. A certain flexibility in choice of the fundamental
domain can be achieved by calling manually the add_site_family method and providing it the
other_vectors parameter.
The fundamental domain for hoppings are all hoppings (a, b) with site a in fundamental domain
of sites.
Methods
act(element, a, b=None)
add_site_family(fam, other_vectors=None)
Select a fundamental domain for site family and cache associated data.
Attributes
num_directions
kwant.lattice.general
Notes
kwant.lattice.Monatomic
Attributes
offset (vector) Displacement of the lattice origin from the real space coordinates origin
Methods
closest(pos)
Find the lattice coordinates of the site closest to position pos.
n_closest(pos, n=1)
Find n sites closest to position pos.
Returns sites : numpy array
An array with sites coordinates.
neighbors(n=1, eps=1e-08)
Return n-th nearest neighbor hoppings.
Parameters n : integer
Order of the hoppings to return.
eps : float
Tolerance relative to the length of the shortest lattice vector for when to
consider lengths to be approximately equal.
Returns hoppings : list of kwant.builder.HopplingKind objects
The n-th nearest neighbor hoppings.
Notes
The hoppings are ordered lexicographically according to sublattice from which they originate,
sublattice on which they end, and their lattice coordinates. Out of the two equivalent hoppings
(a hopping and its reverse) only the lexicographically larger one is returned.
normalize_tag(tag)
pos(tag)
Return the real-space position of the site with a given tag.
shape(function, start)
Return a key for all the lattice sites inside a given shape.
The object returned by this method is primarily meant to be used as a key for indexing
Builder instances. See example below.
Parameters function : callable
A function of real space coordinates that returns a truth value: true for
coordinates inside the shape, and false otherwise.
start : 1d array-like
The real-space origin for the flood-fill algorithm.
Returns shape_sites : function
Notes
When the function returned by this method is called, a flood-fill algorithm finds and yields
all the lattice sites inside the specified shape starting from the specified position.
A Symmetry or Builder may be passed as sole argument when calling the function returned
by this method. This will restrict the flood-fill to the fundamental domain of the symmetry
(or the builder’s symmetry). Note that unless the shape function has that symmetry itself,
the result may be unexpected.
Examples
vec(int_vec)
Return the coordinates of a Bravais lattice vector in real space.
Parameters vec : integer vector
Returns output : real vector
wire(center, radius)
Return a key for all the lattice sites inside an infinite cylinder.
This method makes it easy to define cylindrical (2d: rectangular) leads that point in any
direction. The object returned by this method is primarily meant to be used as a key for
indexing Builder instances. See example below.
Parameters center : 1d array-like of floats
A point belonging to the axis of the cylinder.
radius : float
The radius of the cylinder.
Notes
Examples
Attributes
prim_vecs
(sequence of vectors) Primitive vectors
prim_vecs[i]‘ is the i-th primitive basis vector of the lattice displacement of the lattice origin
from the real space coordinates origin.
kwant.lattice.Polyatomic
Methods
neighbors(n=1, eps=1e-08)
Return n-th nearest neighbor hoppings.
Parameters n : integer
Order of the hoppings to return.
eps : float
Tolerance relative to the length of the shortest lattice vector for when to
consider lengths to be approximately equal.
Returns hoppings : list of kwant.builder.HopplingKind objects
The n-th nearest neighbor hoppings.
Notes
The hoppings are ordered lexicographically according to sublattice from which they originate,
sublattice on which they end, and their lattice coordinates. Out of the two equivalent hoppings
(a hopping and its reverse) only the lexicographically larger one is returned.
shape(function, start)
Return a key for all the lattice sites inside a given shape.
The object returned by this method is primarily meant to be used as a key for indexing
Builder instances. See example below.
Parameters function : callable
A function of real space coordinates that returns a truth value: true for
coordinates inside the shape, and false otherwise.
start : 1d array-like
The real-space origin for the flood-fill algorithm.
Returns shape_sites : function
Notes
When the function returned by this method is called, a flood-fill algorithm finds and yields
all the lattice sites inside the specified shape starting from the specified position.
A Symmetry or Builder may be passed as sole argument when calling the function returned
by this method. This will restrict the flood-fill to the fundamental domain of the symmetry
(or the builder’s symmetry). Note that unless the shape function has that symmetry itself,
the result may be unexpected.
Examples
vec(int_vec)
Return the coordinates of a Bravais lattice vector in real space.
Parameters vec : integer vector
Returns output : real vector
wire(center, radius)
Return a key for all the lattice sites inside an infinite cylinder.
This method makes it easy to define cylindrical (2d: rectangular) leads that point in any
direction. The object returned by this method is primarily meant to be used as a key for
indexing Builder instances. See example below.
Parameters center : 1d array-like of floats
A point belonging to the axis of the cylinder.
radius : float
The radius of the cylinder.
Notes
Examples
Attributes
prim_vecs
(sequence of vectors) Primitive vectors
prim_vecs[i]‘ is the i-th primitive basis vector of the lattice displacement of the lattice origin
from the real space coordinates origin.
kwant.lattice.chain
kwant.lattice.square
kwant.lattice.cubic
kwant.lattice.triangular
kwant.lattice.honeycomb
kwant.lattice.kagome
kwant.plotter.plot
show : bool
Whether matplotlib.pyplot.show() is to be called, and the output is to
be shown immediately. Defaults to True.
dpi : float or None
Number of pixels per inch. If not set the matplotlib default is used.
fig_size : tuple or None
Figure size (width, height) in inches. If not set, the default matplotlib value
is used.
ax : matplotlib.axes.Axes instance or None
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
•If None is passed for a plot property, a default value depending on the dimension is chosen.
Typically, the default values result in acceptable plots.
•The meaning of “site” depends on whether the system to be plotted is a builder or a low level
system. For builders, a site is a kwant.builder.Site object. For low level systems, a site is an
integer – the site number.
•color and symbol definitions may be tuples, but not lists or arrays. Arrays of values
(linewidths, colors, sizes) may not be tuples.
•The dimensionality of the plot (2D vs 3D) is inferred from the coordinate array. If there are
more than three coordinates, only the first three are used. If there is just one coordinate, the
second one is padded with zeros.
•The system is scaled to fit the smaller dimension of the figure, given its aspect ratio.
kwant.plotter.map
The color map used for sites and optionally hoppings, if None, matplotlib
default is used.
vmin : float, optional
The lower saturation limit for the colormap; values returned by value which
are smaller than this will saturate
vmax : float, optional
The upper saturation limit for the colormap; valued returned by value which
are larger than this will saturate
a : float, optional
Reference length. If not given, it is determined as a typical nearest neighbor
distance.
method : string, optional
Passed to scipy.interpolate.griddata: “nearest” (default), “linear”, or
“cubic”
oversampling : integer, optional
Number of pixels per reference length. Defaults to 3.
num_lead_cells : integer, optional
number of lead unit cells that should be plotted to indicate the position of
leads. Defaults to 0.
file : string or file object or None
The output file. If None, output will be shown instead.
show : bool
Whether matplotlib.pyplot.show() is to be called, and the output is to
be shown immediately. Defaults to True.
ax : matplotlib.axes.Axes instance or None
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.
pos_transform : function or None
Transformation to be applied to the site position.
background : matplotlib color spec
Areas without sites are filled with this color.
Returns fig : matplotlib figure
A figure with the output if ax is not set, else None.
Notes
•When plotting a system on a square lattice and method is “nearest”, it makes sense to set
oversampling to 1. Then, each site will correspond to exactly one pixel.
kwant.plotter.current
The system graph together with current intensities defines a “discrete” current density field where
the current density is non-zero only on the straight lines that connect sites that are coupled by a
hopping term.
To make this vector field easier to visualize and interpret at different length scales, it is smoothed
by convoluting it with the bell-shaped bump function f(r) = max(1 - (2*r / width)**2, 0)**2.
The bump width is determined by the limit and width parameters.
This routine samples the smoothed field on a regular (square or cubic) grid and displays it using
an enhanced variant of matplotlib’s streamplot.
This is a convenience function that is equivalent to streamplot(*interpolate_current(syst,
current, relwidth), **kwargs). The longer form makes it possible to tweak additional options
of interpolate_current.
Parameters syst : kwant.system.FiniteSystem
The system for which to plot the current.
current : sequence of float
Sequence of values defining currents on each hopping of the system. Ordered
in the same way as syst.graph. This typically will be the result of evaluating
a Current operator.
relwidth : float or None
Relative width of the bumps used to generate the field, as a fraction of the
length of the longest side of the bounding box.
**kwargs : various
Keyword args to be passed verbatim to streamplot.
Returns fig : matplotlib figure
A figure with the output if ax is not set, else None.
kwant.plotter.bands
fig_size : tuple
Figure size (width, height) in inches. If not set, the default matplotlib value
is used.
ax : matplotlib.axes.Axes instance or None
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.
params : dict, optional
Dictionary of parameter names and their values. Mutually exclusive with
‘args’.
Returns fig : matplotlib figure
A figure with the output if ax is not set, else None.
Notes
kwant.plotter.spectrum
Figure size (width, height) in inches. If not set, the default matplotlib value
is used.
ax : matplotlib.axes.Axes instance or None
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.
kwant.plotter.streamplot
show : bool
Whether matplotlib.pyplot.show() is to be called, and the output is to
be shown immediately. Defaults to True.
dpi : float or None
Number of pixels per inch. If not set the matplotlib default is used.
fig_size : tuple or None
Figure size (width, height) in inches. If not set, the default matplotlib value
is used.
ax : matplotlib.axes.Axes instance or None
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.
kwant.plotter.interpolate_current
kwant.plotter.sys_leads_sites
kwant.plotter.sys_leads_sites(sys, num_lead_cells=2)
Return all the sites of the system and of the leads as a list.
Parameters sys : kwant.builder.Builder or kwant.system.System instance
The system, sites of which should be returned.
num_lead_cells : integer
The number of times lead sites from each lead should be returned. This is
useful for showing several unit cells of the lead next to the system.
Returns sites : list of (site, lead_number, copy_number) tuples
A site is a Site instance if the system is not finalized, and an integer other-
wise. For system sites lead_number is None and copy_number is 0, for leads
both are integers.
lead_cells : list of slices
lead_cells[i] gives the position of all the coordinates of lead i within sites.
Notes
Leads are only supported if they are of the same type as the original system, i.e. sites of
BuilderLead leads are returned with an unfinalized system, and sites of system.InfiniteSystem
leads are returned with a finalized system.
kwant.plotter.sys_leads_hoppings
kwant.plotter.sys_leads_hoppings(sys, num_lead_cells=2)
Return all the hoppings of the system and of the leads as an iterator.
Parameters sys : kwant.builder.Builder or kwant.system.System instance
The system, sites of which should be returned.
num_lead_cells : integer
The number of times lead sites from each lead should be returned. This is
useful for showing several unit cells of the lead next to the system.
Returns hoppings : list of (hopping, lead_number, copy_number) tuples
A site is a Site instance if the system is not finalized, and an integer other-
wise. For system sites lead_number is None and copy_number is 0, for leads
both are integers.
lead_cells : list of slices
lead_cells[i] gives the position of all the coordinates of lead i within hoppings.
Notes
Leads are only supported if they are of the same type as the original system, i.e. hoppings of
BuilderLead leads are returned with an unfinalized system, and hoppings of InfiniteSystem
leads are returned with a finalized system.
kwant.plotter.sys_leads_pos
kwant.plotter.sys_leads_pos(sys, site_lead_nr)
Return an array of positions of sites in a system.
Parameters sys : kwant.builder.Builder or kwant.system.System instance
The system, coordinates of sites of which should be returned.
site_lead_nr : list of (site, leadnr, copynr) tuples
Output of sys_leads_sites applied to the system.
Returns coords : numpy.ndarray of floats
Array of coordinates of the sites.
Notes
This function uses site.pos property to get the position of a builder site and sys.pos(sitenr) for
finalized systems. This function requires that all the positions of all the sites have the same
dimensionality.
kwant.plotter.sys_leads_hopping_pos
kwant.plotter.sys_leads_hopping_pos(sys, hop_lead_nr)
Return arrays of coordinates of all hoppings in a system.
Parameters sys : ~kwant.builder.Builder or ~kwant.system.System instance
The system, coordinates of sites of which should be returned.
hoppings : list of (hopping, leadnr, copynr) tuples
Output of sys_leads_hoppings applied to the system.
Returns coords : (end_site, start_site): tuple of NumPy arrays of floats
Array of coordinates of the hoppings. The first half of coordinates in each
array entry are those of the first site in the hopping, the last half are those
of the second site.
Notes
This function uses site.pos property to get the position of a builder site and sys.pos(sitenr)
for finalized systems. This function requires that all the positions of all the sites have the same
dimensionality.
kwant.plotter.mask_interpolate
Notes
•min and max are chosen such that when plotting a system on a square lattice and oversampling
is set to an odd integer, each site will lie exactly at the center of a pixel of the output array.
•When plotting a system on a square lattice and method is “nearest”, it makes sense to set
oversampling to 1. Then, each site will correspond to exactly one pixel in the resulting array.
3.5.1 Overview
Kwant offers several modules for computing the solutions to quantum transport problems, the so-called
solvers. Each of these solvers may use different internal algorithms and/or depend on different exter-
nal libraries. If the libraries needed by one solver are not installed, trying to import it will raise an
ImportError exception. The Installation instructions list all the libraries that are required or can be
used by Kwant and its solvers.
There is one solver, kwant.solvers.default that is always available. For each Kwant installation it
combines the best functionality of the available solvers into a single module. We recommend to use it
unless there are specific reasons to use another. The following functions are provided.
smatrix(sys[, energy, args, out_leads, ...]) Compute the scattering matrix of a system.
greens_function(sys[, energy, args, ...]) Compute the retarded Green’s function of the sys-
tem between its leads.
wave_function(sys[, energy, args, ...]) Return a callable object for the computation of the
wave function inside the scattering region.
ldos(sys[, energy, args, check_hermiticity, ...]) Calculate the local density of states of a system at a
given energy.
kwant.solvers.default.smatrix
Notes
This function can be used to calculate the conductance and other transport properties of a system.
See the documentation for its output type, SMatrix.
The returned object contains the scattering matrix elements from the in_leads to the out_leads as
well as information about the lead modes.
Both in_leads and out_leads must be sorted and may only contain unique entries.
kwant.solvers.default.greens_function
Notes
This function can be used to calculate the conductance and other transport properties of a system.
It is often slower and less stable than the scattering matrix-based calculation executed by smatrix,
and is currently provided mostly for testing purposes and compatibility with RGF code.
It returns an object encapsulating the Green’s function elements between the system sites inter-
facing the leads in in_leads and those interfacing the leads in out_leads. The returned object also
contains a list with self-energies of the leads.
Both in_leads and out_leads must be sorted and may only contain unique entries.
kwant.solvers.default.wave_function
Notes
The returned object can be itself called like a function. Given a lead number, it returns a 2d
NumPy array that contains the wave function within the scattering region due to each incoming
mode of the given lead. Index 0 is the mode number, index 1 is the orbital number. The modes
appear in the same order as incoming modes in kwant.physics.modes.
Examples
kwant.solvers.default.ldos
kwant.solvers.common.SMatrix
Attributes
data (NumPy array) a matrix containing all the requested matrix elements of the
scattering matrix.
lead_info (list of data) a list containing kwant.physics.PropagatingModes for each lead.
out_leads, (sequence of integers) indices of the leads where current is extracted (out) or
in_leads injected (in). Only those are listed for which SMatrix contains the calculated
result.
Methods
block_coords(lead_out, lead_in)
Return slices corresponding to the block from lead_in to lead_out.
conductance_matrix()
Return the conductance matrix.
This is the matrix 𝐶 such that 𝐼 = 𝐶𝑉 where 𝐼 and 𝑉 are, respectively, the vectors of currents
and voltages for each lead.
This matrix is useful for calculating non-local resistances. See Section 2.4 of the book by S.
Datta.
in_block_coords(lead_in)
Return a slice with the columns in the block corresponding to lead_in.
num_propagating(lead)
Return the number of propagating modes in the lead.
out_block_coords(lead_out)
Return a slice with the rows in the block corresponding to lead_out.
submatrix(lead_out, lead_in)
Return the matrix elements from lead_in to lead_out.
transmission(lead_out, lead_in)
Return transmission from lead_in to lead_out.
If lead_in or lead_out are a length-2 sequence, the first number is the number of the lead, and
the second number indexes the eigenvalue of the conserved quantity in that lead (e.g. spin) if
it was specified.
The analog of smatrix, greens_function accordingly returns:
kwant.solvers.common.GreensFunction
Attributes
data (NumPy array) a matrix containing all the requested matrix elements of Green’s
function.
lead_info (list of matrices) a list with self-energies of each lead.
out_leads, (sequence of integers) indices of the leads where current is extracted (out) or
in_leads injected (in). Only those are listed for which SMatrix contains the calculated
result.
Methods
block_coords(lead_out, lead_in)
Return slices corresponding to the block from lead_in to lead_out.
conductance_matrix()
Return the conductance matrix.
This is the matrix 𝐶 such that 𝐼 = 𝐶𝑉 where 𝐼 and 𝑉 are, respectively, the vectors of currents
and voltages for each lead.
This matrix is useful for calculating non-local resistances. See Section 2.4 of the book by S.
Datta.
in_block_coords(lead_in)
Return a slice with the columns in the block corresponding to lead_in.
num_propagating(lead)
Return the number of propagating modes in the lead.
out_block_coords(lead_out)
Return a slice with the rows in the block corresponding to lead_out.
submatrix(lead_out, lead_in)
Return the matrix elements from lead_in to lead_out.
transmission(lead_out, lead_in)
Return transmission from lead_in to lead_out.
If the option current_conserving has been enabled for this object, this method will deduce
missing transmission values whenever possible.
Current conservation is enabled by default for objects returned by smatrix and
greens_function whenever the Hamiltonian has been verified to be Hermitian (option
check_hermiticity, enabled by default).
Being just a thin wrapper around other solvers, the default solver selectively imports their functionality.
To find out the origin of any function in this module, use Python’s help. For example
>>> help(kwant.solvers.default.ldos)
Unlike the default one, other solvers have to be imported manually. They provide, whenever possible,
exactly the same interface as the default. Some allow for specific tuning that can improve performance.
The differences to the default solver are listed in the documentation of each module.
This solver uses SciPy’s scipy.sparse.linalg. The interface is identical to that of the default solver.
scipy.sparse.linalg currently uses internally either the direct sparse solver UMFPACK or if that is
not installed, SuperLU. Often, SciPy’s SuperLU will give quite poor performance and you will be warned
if only SuperLU is found. The module variable uses_umfpack can be checked to determine if UMFPACK
is being used.
This solver uses MUMPS. (Only the sequential, single core version of MUMPS is used.) MUMPS is a
very efficient direct sparse solver that can take advantage of memory beyond 3GiB for the solution of
large problems. Furthermore, it offers a choice of several orderings of the input matrix some of which
can speed up a calculation significantly.
Compared with the default solver, this module adds several options that may be used to fine-tune
performance. Otherwise the interface is identical. These options can be set and queried with the following
functions.
kwant.solvers.mumps.options(self, nrhs=None, ordering=None, sparse_rhs=None)
Modify some options. Return the old options.
Parameters nrhs : number
number of right hand sides that should be solved simultaneously. A value
around 5-10 gives optimal performance on many machines. If memory is an
issue, it can be set to 1, to minimize memory usage (at the cost of slower
performance). Default value is 6.
ordering : string
one of the ordering methods supported by the MUMPS solver (see kwant.
linalg.mumps. The availability of certain orderings depends on the MUMPS
installation.), or ‘kwant_decides’. If ordering=='kwant_decides', the or-
dering that typically gives the best performance is chosen from the available
ones. One can also defer the choice of ordering to MUMPS by specifying
‘auto’, in some cases MUMPS however chooses poorly.
Notes
Thanks to this method returning the old options as a dictionary it is easy to change some options
temporarily:
kwant.solvers.mumps.reset_options(self )
Set the options to default values. Return the old options.
Each solver module (except the default one) contains a class Solver (e.g. kwant.solvers.sparse.
Solver), that actually implements that solver’s functionality. For each module-level function provided
by the solver, there is a correspondent method in the Solver class. The module-level functions are
simply the methods of a hidden Solver instance that is present in each solver module.
The encapsulation in a class allows different solvers to easily share common code. It also makes it
possible to use solvers with different options concurrently. Typically, one does not need this flexibility,
and will not want to bother with the Solver class itself. Instead, one will use the module-level functions
as explained in the previous sections.
3.6.1 Observables
kwant.operator.Density
Notes
In general, if there is a certain “density” (e.g. charge or spin) that is represented by a square matrix
𝑀𝑖 associated with each site 𝑖 then an instance of this class represents the tensor 𝑄𝑖𝛼𝛽 which is
equal to 𝑀𝑖 when 𝛼 and 𝛽 are orbitals on site 𝑖, and zero otherwise.
Methods
>>> A(psi)
to compute the matrix element ⟨𝜑| 𝐴 |𝜓⟩. Note that these quantities may be vectors (e.g. local
charge or current density).
For an operator 𝑄𝑖𝛼𝛽 , bra 𝜑𝛼 and ket 𝜓𝛽 this computes 𝑞𝑖 = ∑𝛼𝛽 𝜑∗𝛼 𝑄𝑖𝛼𝛽 𝜓𝛽 if self.sum is
False, otherwise computes 𝑞 = ∑𝑖𝛼𝛽 𝜑∗𝛼 𝑄𝑖𝛼𝛽 𝜓𝛽 .
Parameters bra, ket : sequence of complex
Must have the same length as the number of orbitals in the system. If only
one is provided, both bra and ket are taken as equal.
args : tuple, optional
The arguments to pass to the system. Used to evaluate the onsite elements
and, possibly, the system Hamiltonian. Mutually exclusive with ‘params’.
params : dict, optional
Dictionary of parameter names and their values. Mutually exclusive with
‘args’.
Returns float if check_hermiticity is True, and ket is None,
otherwise complex. If this operator was created with sum=True,
then a single value is returned, otherwise an array is returned.
Attributes
check_hermiticity
onsite
sum
syst
where
kwant.operator.Current
Where to evaluate the operator. If syst is not a finalized Builder, then this
should be a sequence of pairs of integers. If a function is provided, it should
take a pair of integers or a pair of Site (if syst is a finalized builder) and
return True or False. If not provided, the operator will be calculated over
all hoppings in the system.
check_hermiticity : bool
Check whether the provided onsite is Hermitian. If it is not Hermitian,
then an error will be raised when the operator is evaluated.
sum : bool, default: False
If True, then calling this operator will return a single scalar.
Notes
In general, if there is a certain “density” (e.g. charge or spin) that is represented by a square matrix
𝑀𝑖 associated with each site 𝑖 and 𝐻𝑖𝑗 is the hopping Hamiltonian from site 𝑗 to site i, then an
instance of this class represents the tensor 𝐽𝑖𝑗𝛼𝛽 which is equal to 𝑖 [(𝐻𝑖𝑗 )† 𝑀𝑖 − 𝑀𝑖 𝐻𝑖𝑗 ] when 𝛼
and 𝛽 are orbitals on sites 𝑖 and 𝑗 respectively, and zero otherwise.
The tensor 𝐽𝑖𝑗𝛼𝛽 will also be referred to as 𝑄𝑛𝛼𝛽 , where 𝑛 is the index of hopping (𝑖, 𝑗) in where.
Methods
>>> A(psi)
to compute the matrix element ⟨𝜑| 𝐴 |𝜓⟩. Note that these quantities may be vectors (e.g. local
charge or current density).
For an operator 𝑄𝑖𝛼𝛽 , bra 𝜑𝛼 and ket 𝜓𝛽 this computes 𝑞𝑖 = ∑𝛼𝛽 𝜑∗𝛼 𝑄𝑖𝛼𝛽 𝜓𝛽 if self.sum is
False, otherwise computes 𝑞 = ∑𝑖𝛼𝛽 𝜑∗𝛼 𝑄𝑖𝛼𝛽 𝜓𝛽 .
Parameters bra, ket : sequence of complex
Must have the same length as the number of orbitals in the system. If only
one is provided, both bra and ket are taken as equal.
args : tuple, optional
The arguments to pass to the system. Used to evaluate the onsite elements
and, possibly, the system Hamiltonian. Mutually exclusive with ‘params’.
params : dict, optional
Dictionary of parameter names and their values. Mutually exclusive with
‘args’.
Returns float if check_hermiticity is True, and ket is None,
otherwise complex. If this operator was created with sum=True,
then a single value is returned, otherwise an array is returned.
Attributes
check_hermiticity
onsite
sum
syst
where
kwant.operator.Source
Notes
An example of a “source” is a spin torque. In general, if there is a certain “density” (e.g. charge
or spin) that is represented by a square matrix 𝑀𝑖 associated with each site 𝑖, and 𝐻𝑖 is the onsite
Hamiltonian on site site i, then an instance of this class represents the tensor 𝑄𝑖𝛼𝛽 which is equal
to (𝐻𝑖 )† 𝑀𝑖 − 𝑀𝑖 𝐻𝑖 when 𝛼 and 𝛽 are orbitals on site 𝑖, and zero otherwise.
Methods
>>> A(psi)
to compute the matrix element ⟨𝜑| 𝐴 |𝜓⟩. Note that these quantities may be vectors (e.g. local
charge or current density).
For an operator 𝑄𝑖𝛼𝛽 , bra 𝜑𝛼 and ket 𝜓𝛽 this computes 𝑞𝑖 = ∑𝛼𝛽 𝜑∗𝛼 𝑄𝑖𝛼𝛽 𝜓𝛽 if self.sum is
False, otherwise computes 𝑞 = ∑𝑖𝛼𝛽 𝜑∗𝛼 𝑄𝑖𝛼𝛽 𝜓𝛽 .
Parameters bra, ket : sequence of complex
Must have the same length as the number of orbitals in the system. If only
one is provided, both bra and ket are taken as equal.
Attributes
check_hermiticity
onsite
sum
syst
where
3.7.1 Leads
Bands(sys[, args, params]) Class of callable objects for the computation of en-
ergy bands.
modes(h_cell, h_hop[, tol, stabilization, ...]) Compute the eigendecomposition of a translation op-
erator of a lead.
selfenergy(h_cell, h_hop[, tol]) Compute the self-energy generated by the lead.
two_terminal_shotnoise(smatrix) Compute the shot-noise in a two-terminal setup.
PropagatingModes(wave_functions, velocities, ...) The calculated propagating modes of a lead.
StabilizedModes(vecs, vecslmbdainv, nmodes) Stabilized eigendecomposition of the translation op-
erator.
kwant.physics.Bands
Notes
An instance of this class can be called like a function. Given a momentum (currently this must be a
scalar as all infinite systems are quasi-1-d), it returns a NumPy array containing the eigenenergies
of all modes at this momentum
Examples
Methods
kwant.physics.modes
Notes
The propagating modes are sorted according to the longitudinal component of their k-vector, with
incoming modes having k sorted in descending order, and outgoing modes having k sorted in
ascending order. In simple cases where bands do not cross, this ordering corresponds to “lowest
modes first”. In general, however, it is necessary to examine the band structure – something this
function is not doing by design.
Propagating modes with the same momentum are orthogonalized. All the propagating modes are
normalized by current.
This function uses the most stable and efficient algorithm for calculating the mode decomposition
that the Kwant authors are aware about. Its details are to be published.
kwant.physics.selfenergy
Notes
For simplicity this function internally calculates the modes first. This may cause a small slowdown,
and can be improved if necessary.
kwant.physics.two_terminal_shotnoise
kwant.physics.two_terminal_shotnoise(smatrix)
Compute the shot-noise in a two-terminal setup.
In a two terminal system the shot noise is given by tr((1 - t*t^dagger) * t*t^dagger).
Parameters smatrix : SMatrix instance
A two terminal scattering matrix.
Returns noise : float
Shot noise measured in noise quanta 2 e^3 |V| / pi hbar.
kwant.physics.PropagatingModes
Notes
The sort order of all the three arrays is identical. The first half of the modes have negative velocity,
the second half have positive velocity. The modes with negative velocity are ordered from larger
to lower momenta, the modes with positive velocity vice versa.
The first dimension of wave_functions corresponds to the orbitals of all the sites in a unit cell, the
second one to the number of the mode. Each mode is normalized to carry unit current. If several
modes have the same momentum and velocity, an arbitrary orthonormal basis in the subspace of
these modes is chosen.
If a conservation law is specified to block diagonalize the Hamiltonian to N blocks, then
block_nmodes[i] is the number of left or right moving propagating modes in conservation law block
i. The ordering of blocks is the same as the ordering of the projectors used to block diagonalize
the Hamiltonian.
Attributes
Methods
kwant.physics.StabilizedModes
with h_hop the hopping between unit cells. If h_hop is not invertible, and has the singular value
decomposition u s v^+, then the eigenproblem is written in the basis sqrt(s) v^+ psi_n, sqrt(s)
u^+ psi_(n+1). In this basis we calculate the eigenvectors of the propagating modes, and the
Schur vectors (an orthogonal basis) in the space of evanescent modes.
vecs and vecslmbdainv are the first and the second halves of the wave functions. The first nmodes
are eigenmodes moving in the negative direction (hence they are incoming into the system in Kwant
convention), the second nmodes are eigenmodes moving in the positive direction. The remaining
modes are the Schur vectors of the modes evanescent in the positive direction. Propagating modes
with the same eigenvalue are orthogonalized, and all the propagating modes are normalized to
carry unit current. Finally the sqrt_hop attribute is v sqrt(s).
Attributes
Methods
selfenergy()
Compute the self-energy generated by lead modes.
Returns Sigma : numpy array, real or complex, shape (M,M)
The computed self-energy. Note that even if h_cell and h_hop are both real,
Sigma will typically be complex. (More precisely, if there is a propagating
mode, Sigma will definitely be complex.)
3.7.2 Symmetry
kwant.physics.DiscreteSymmetry
Notes
Whenever one or more discrete symmetry is declared in conjunction with a conservation law, the
symmetry operators and projectors must be declared in canonical form. This means that each
block of the Hamiltonian is transformed either to itself by a discrete symmetry or to a single other
block.
More formally, consider a discrete symmetry S. The symmetry projection that maps from block
i to block j of the Hamiltonian with projectors 𝑃𝑖 and 𝑃𝑗 is 𝑆𝑗𝑖 = 𝑃𝑗+ 𝑆𝑃𝑖 . If 𝑆𝑗𝑖 is nonzero, a
symmetry relation exists between blocks i and j. Canonical form means that for each j, the block
𝑆𝑗𝑖 is nonzero at most for one i, while all other blocks vanish.
If the operators are not in canonical form, they can be made so by further splitting the Hamiltonian
into smaller blocks, i.e. by adding more projectors.
Methods
validate(matrix)
Check if a matrix satisfies the discrete symmetries.
Parameters matrix : sparse or dense matrix
If rectangular, it is padded by zeros to be square.
Returns broken_symmetry : string or None
One of “Conservation law”, “Time reversal”, “Particle-hole”, “Chiral”: the
symmetry broken by the matrix. If the matrix breaks more than one sym-
metry, returns only the first failed check.
FOUR
OTHER MODULES
135
Kwant 1.3.1 documentation
Notes
The representations of symmetry operators are chosen according to Phys. Rev. B 85, 165409.
Matrix indices are grouped first according to orbital number, then sigma-index, then tau-index.
Chiral (sublattice) symmetry C always reads: H = -tau_z H tau_z.
Time reversal symmetry T reads: AI: H = H^*. BDI: H = tau_z H^* tau_z. CI: H = tau_x
H^* tau_x. AII, CII: H = sigma_y H^* sigma_y. DIII: H = tau_y H^* tau_y.
Particle-hole symmetry reads: C, CI: H = -tau_y H^* tau_y. CII: H = -tau_z sigma_y H^*
tau_z sigma_y. D, BDI: H = -H^*. DIII: H = -tau_x H^* tau_x.
This implementation should be sufficiently efficient for large matrices, since it avoids any matrix
multiplication.
kwant.rmt.circular(n, sym=’A’, charge=None, rng=None)
Make a n * n matrix belonging to a symmetric circular ensemble.
Parameters n : int
Size of the matrix. It should be even for the classes C, CI, CII, AII, DIII
(either T^2 = -1 or P^2 = -1).
sym : one of ‘A’, ‘AI’, ‘AII’, ‘AIII’, ‘BDI’, ‘CII’, ‘D’, ‘DIII’, ‘C’, ‘CI’
Altland-Zirnbauer symmetry class of the matrix.
charge : int or None
Topological invariant of the matrix. Should be one of 1, -1 in symmetry
classes D and DIII, should be from 0 to n in classes AIII and BDI, and
should be from 0 to n / 2 in class CII. If charge is None, it is drawn from a
binomial distribution with p = 1 / 2.
rng: int or rng (optional)
Seed or random number generator. If no ‘rng’ is passed, the random number
generator provided by numpy will be used.
Returns s : numpy.ndarray
A numpy array drawn from a corresponding circular ensemble.
Notes
The representations of symmetry operators are chosen according to Phys. Rev. B 85, 165409,
except class D.
Matrix indices are grouped first according to channel number, then sigma-index.
Chiral (sublattice) symmetry C always reads: s = s^+.
Time reversal symmetry T reads: AI, BDI: r = r^T. CI: r = -sigma_y r^T sigma_y. AII,
DIII: r = -r^T. CII: r = sigma_y r^T sigma_y.
Particle-hole symmetry reads: CI: r = -sigma_y r^* sigma_y C, CII: r = sigma_y r^*
sigma_y D, BDI: r = r^*. DIII: -r = r^*.
This function uses QR decomposition to probe symmetric compact groups, as detailed in
arXiv:math-ph/0609050. For a reason as yet unknown, scipy implementation of QR decompo-
sition also works for symplectic matrices.
𝜌𝐴 (𝑒) = 𝜌(𝑒)𝐴(𝑒),
𝐷−1
where 𝜌(𝑒) = ∑𝑘=0 𝛿(𝐸 − 𝐸𝑘 ) is the density of states, and 𝐴(𝑒) is the expectation value of 𝐴 for
all the eigenstates with energy 𝑒.
Parameters hamiltonian : FiniteSystem or matrix Hamiltonian
If a system is passed, it should contain no leads.
params : dict, optional
Additional parameters to pass to the Hamiltonian and operator.
operator : operator, dense matrix, or sparse matrix, optional
Operator for which the spectral density will be evaluated. If it is callable,
the densities at each energy will have the dimension of the result of oper-
ator(bra, ket). If it has a dot method, such as numpy.ndarray and scipy.
sparse.matrices, the densities will be scalars. If no operator is provided,
the density of states is calculated with a faster algorithm.
num_vectors : positive int, default: 10
Number of random vectors for the KPM method.
num_moments : positive int, default: 100
Number of moments, order of the KPM expansion. Mutually exclusive with
‘energy_resolution’.
energy_resolution : positive float, optional
The resolution in energy of the KPM approximation to the spectral density.
Mutually exclusive with ‘num_moments’.
vector_factory : function, optional
The user defined function f(n) generates random vectors of length n that will
be used in the algorithm. If not provided, random phase vectors are used.
The default random vectors are optimal for most cases, see the discussions
in [R1] and [R2].
bounds : pair of floats, optional
Lower and upper bounds for the eigenvalue spectrum of the system. If not
provided, they are computed.
Notes
When passing an operator defined in operator, the result of operator(bra, ket) depends on
the attribute sum of such operator. If sum=True, densities will be scalars, that is, total densities
of the system. If sum=False the densities will be arrays of the length of the system, that is, local
densities.
Examples
In the following example, we will obtain the density of states of a graphene sheet, defined as a
honeycomb lattice with first nearest neighbors coupling.
We start by importing kwant and defining a FiniteSystem,
or
Attributes
ener- (array of floats) Array of sampling points with length 2 * num_moments in the range
gies of the spectrum.
densi- (array of floats) Spectral density of the operator evaluated at the energies.
ties
__call__(energy=None)
Return the spectral density evaluated at energy.
Notes
If energy is not provided, then the densities are obtained by Fast Fourier Transform of the
Chebyshev moments.
add_moments(num_moments=None, *, energy_resolution=None)
Increase the number of Chebyshev moments.
Parameters num_moments: positive int
The number of Chebyshev moments to add. Mutually exclusive with ‘en-
ergy_resolution’.
energy_resolution: positive float, optional
Features wider than this resolution are visible in the spectral density. Mu-
tually exclusive with ‘num_moments’.
add_vectors(num_vectors)
Increase the number of random vectors.
Parameters num_vectors: positive int
The number of random vectors to add.
integrate(distribution_function=None)
Returns the total spectral density.
Returns the integral over the whole spectrum with an optional distribution function.
distribution_function should be able to take arrays as input. Defined using Gauss-
Chebyshev integration.
4.4.1 Discretizer
kwant.continuum.discretize
Warning: This function uses eval (because it calls sympy.sympify), and thus should not be
used on unsanitized input.
kwant.continuum.discretize_symbolic
Warning: This function uses eval (because it calls sympy.sympify), and thus should not be
used on unsanitized input.
kwant.continuum.build_discretized
Warning: This function uses eval (because it calls sympy.sympify), and thus should not be
used on unsanitized input.
kwant.continuum.sympify
kwant.continuum.sympify(expr, locals=None)
Sympify object using special rules for Hamiltonians.
If ‘expr‘ is already a type that SymPy understands, it will do nothing but return that value. Note
that locals will not be used in this situation.
Otherwise, it is sympified by sympy.sympify with a modified namespace such that
•the position operators “x”, “y” or “z” and momentum operators “k_x”, “k_y”, and “k_z” do
not commute,
•all single-letter identifiers and names of Greek letters (e.g. “pi” or “gamma”) are treated as
symbols,
•“kron” corresponds to sympy.physics.quantum.TensorProduct, and “identity” to sympy.
eye,
•“sigma_0”, “sigma_x”, “sigma_y”, “sigma_z” are the Pauli matrices.
In addition, Python list literals are interpreted as SymPy matrices.
Warning: This function uses eval (because it calls sympy.sympify), and thus should not be
used on unsanitized input.
Note: When a value of locals is already a SymPy object, it is used as-is, and
the caller is responsible to set the commutativity of its symbols appropriately.
This possible source of errors is demonstrated in the last example below.
Examples
kwant.continuum.lambdify
kwant.continuum.lambdify(expr, locals=None)
Return a callable object for computing a continuum Hamiltonian.
Warning: This function uses eval (because it calls sympy.sympify), and thus should not be
used on unsanitized input.
4.5.1 kwant.wraparound.wraparound
Notes
Wraparound is stop-gap functionality until Kwant 2.x which will include support for higher-
dimension translational symmetry in the low-level system format. It will be deprecated in the
2.0 release of Kwant.
4.5.2 kwant.wraparound.plot_2d_bands
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.
See also:
kwant.plotter.spectrum
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.
FIVE
The following modules contain functionality that is most often used only internally by Kwant itself or
by advanced users.
5.1.1 kwant.system.System
class kwant.system.System
Bases: object
Abstract general low-level system.
Notes
The sites of the system are indexed by integers ranging from 0 to self.graph.num_nodes - 1.
Optionally, a class derived from System can provide a method pos which is assumed to return the
real-space position of a site given its index.
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.
147
Kwant 1.3.1 documentation
Attributes
Methods
discrete_symmetry(args, *, params=None)
Return the discrete symmetry of the system.
hamiltonian(i, j, *args, params=None)
Return the hamiltonian matrix element for sites i and j.
If i == j, return the on-site Hamiltonian of site i.
if i != j, return the hopping between site i and j.
Hamiltonians may depend (optionally) on positional and keyword arguments
hamiltonian_submatrix(self, args=(), to_sites=None, from_sites=None, sparse=False, re-
turn_norb=False, *, params=None)
Return a submatrix of the system Hamiltonian.
Parameters args : tuple, defaults to empty
Positional arguments to pass to the hamiltonian method. Mutually exclu-
sive with ‘params’.
to_sites : sequence of sites or None (default)
from_sites : sequence of sites or None (default)
sparse : bool
Whether to return a sparse or a dense matrix. Defaults to False.
return_norb : bool
Whether to return arrays of numbers of orbitals. Defaults to False.
params : dict, optional
Dictionary of parameter names and their values. Mutually exclusive with
‘args’.
Returns hamiltonian_part : numpy.ndarray or scipy.sparse.coo_matrix
Submatrix of Hamiltonian of the system.
to_norb : array of integers
Numbers of orbitals on each site in to_sites. Only returned when
return_norb is true.
from_norb : array of integers
Numbers of orbitals on each site in from_sites. Only returned when
return_norb is true.
Notes
The returned submatrix contains all the Hamiltonian matrix elements from from_sites to
to_sites. The default for from_sites and to_sites is None which means to use all sites of
the system in the order in which they appear.
5.1.2 kwant.system.InfiniteSystem
class kwant.system.InfiniteSystem
Bases: kwant.system.System
Abstract infinite low-level system.
An infinite system consists of an infinite series of identical cells. Adjacent cells are connected by
identical inter-cell hoppings.
Notes
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 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 previous cell. They
are included so that hoppings between cells can be 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.
The drawing shows three cells of an infinite system. Each cell consists 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
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.
Attributes
Methods
Notes
The returned submatrix contains all the Hamiltonian matrix elements from from_sites to
to_sites. The default for from_sites and to_sites is None which means to use all sites of
the system in the order in which they appear.
inter_cell_hopping(args=(), sparse=False, *, params=None)
Hopping Hamiltonian between two cells of the infinite system.
modes(energy=0, args=(), *, params=None)
Return mode decomposition of the lead
See documentation of PropagatingModes and StabilizedModes for the return format details.
selfenergy(energy=0, args=(), *, params=None)
Return self-energy of a lead.
The returned matrix has the shape (s, s), where s is sum(len(self.hamiltonian(i, i))
for i in range(self.graph.num_nodes - self.cell_size)).
5.1.3 kwant.system.FiniteSystem
class kwant.system.FiniteSystem
Bases: kwant.system.System
Abstract finite low-level system, possibly with leads.
Notes
Attributes
leads (sequence of leads) Each lead has to provide a method selfenergy that has the same
signature as InfiniteSystem.selfenergy (without the self parameter). It may
also provide modes that has the same signature as InfiniteSystem.modes (without
the self parameter).
lead_interfaces
(sequence of sequences of integers) Each sub-sequence contains the indices of the
system sites to which the lead is connected.
Methods
discrete_symmetry(args, *, params=None)
Return the discrete symmetry of the system.
hamiltonian(i, j, *args, params=None)
Return the hamiltonian matrix element for sites i and j.
If i == j, return the on-site Hamiltonian of site i.
if i != j, return the hopping between site i and j.
Hamiltonians may depend (optionally) on positional and keyword arguments
hamiltonian_submatrix(self, args=(), to_sites=None, from_sites=None, sparse=False, re-
turn_norb=False, *, params=None)
Return a submatrix of the system Hamiltonian.
Parameters args : tuple, defaults to empty
Positional arguments to pass to the hamiltonian method. Mutually exclu-
sive with ‘params’.
to_sites : sequence of sites or None (default)
from_sites : sequence of sites or None (default)
sparse : bool
Whether to return a sparse or a dense matrix. Defaults to False.
return_norb : bool
Whether to return arrays of numbers of orbitals. Defaults to False.
params : dict, optional
Notes
The returned submatrix contains all the Hamiltonian matrix elements from from_sites to
to_sites. The default for from_sites and to_sites is None which means to use all sites of
the system in the order in which they appear.
precalculate(energy=0, args=(), leads=None, what=’modes’, *, params=None)
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
Additional parameters required for calculating the Hamiltionians. Mutually
exclusive with ‘params’.
leads : sequence of integers or None
Numbers of the leads to be precalculated. If None, all are precalculated.
what : ‘modes’, ‘selfenergy’, ‘all’
The quantitity to precompute. ‘all’ will compute both modes and self-
energies. Defaults to ‘modes’.
params : dict, optional
Dictionary of parameter names and their values. Mutually exclusive with
‘args’.
Returns syst : FiniteSystem
A copy of the original system with some leads precalculated.
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.
5.1.4 kwant.system.PrecalculatedLead
Notes
Methods
Graphs, as handled by this module, consist of nodes (numbered by integers, usually ≥ 0). Pairs of nodes
can be connected by edges (numbered by integers ≥ 0). An edge is described by a pair (tail, head) of
node numbers and is always directed.
The basic workflow is to
1. create an object of type Graph,
2. add edges to it using the methods add_edge and add_edges,
3. create a compressed copy of the graph using the method compressed,
4. and use the thus created object for efficient queries.
Example:
Node numbers can be assigned freely, but if they are not consecutive integers starting with zero, storage
space is wasted in the compressed graph. Negative node numbers are special and can be allowed optionally
(see further).
Whenever a method returns multiple edges or nodes (via an iterator), they appear in the order in which
the edges associated with them were added to the graph during construction.
Edge IDs are non-negative integers which identify edges unambiguously. They are assigned automatically
when the graph is compressed. The edge IDs of edges with the same tail will occupy a dense interval of
integers. The IDs of edges sharing the same tail will be assigned from lowest to highest in the order in
which these edges had been added.
The method Graph.compressed takes a parameter which determines whether the graph will be one-way
(the default) or two-way. One-way graphs can be queried for the existence of an edge and provide the
nodes to which a node points (=outgoing neighbors). In addition, two-way graphs can be queried for
the nodes which point to a node (=incoming neighbors).
Another parameter of Graph.compressed, edge_nr_translation, determines whether it will be possible
to use the method edge_id of the compressed graph. This method returns the edge ID of an edge given
the edge number that was returned when an edge was added.
Negative node numbers can be allowed for a Graph (parameter allow_negative_nodes of the constructor).
Edges with negative nodes are considered to be dangling: negative nodes can be neighbors of other nodes,
but cannot be queried directly for neighbors. Consequently, “doubly-dangling” edges which connect two
negative nodes do not make sense and are never allowed. The range of values used for the negative node
numbers does not influence the required storage space in any way.
Compressed graphs have the read-only attributes num_nodes and num_edges.
kwant.graph.Graph
class kwant.graph.Graph
Bases: object
An uncompressed graph. Used to make compressed graphs. (See CGraph.)
Methods
add_edge()
Add the directed edge (tail, head) to the graph.
Parameters tail : integer
head : integer
Returns edge_nr : integer
The sequential number of the edge. This number can be used to query for
the edge ID of an edge in the compressed graph.
Raises ValueError
If a negative node is added when this has not been allowed explicitly or if an
edge is doubly-dangling.
add_edges()
Add multiple edges in one pass.
Parameters edges : iterable of 2-sequences of integers
The parameter edges must be an iterable of elements which describe the
edges to be added. For each edge-element, edge[0] and edge[1] must give,
respectively, the tail and the head. Valid edges are, for example, a list of
Notes
In a one-way compressed graph, an edge with a negative tail is present only minimally: it
is only possible to query the head of such an edge, given the edge ID. This is why one-
way compression of a graph with a negative tail leads to a ValueError being raised, unless
allow_lost_edges is true.
reserve()
Reserve space for edges.
Parameters capacity : integer
Number of edges for which to reserve space.
Notes
It is not necessary to call this method, but using it can speed up the creation of graphs.
write_dot()
Write a representation of the graph in dot format to file.
That resulting file can be visualized with dot(1) or neato(1) form the graphviz package.
Attributes
num_nodes
kwant.graph.CGraph
class kwant.graph.CGraph
Bases: object
A compressed graph which can be efficiently queried for the existence of edges and outgoing neigh-
bors.
Objects of this class do not initialize the members themselves, but expect that they hold usable
values. A good way to create them is by compressing a Graph.
Iterating over a graph yields a sequence of (tail, head) pairs of all edges. The number of an edge in
this sequence equals its edge ID. The built-in function enumerate can thus be used to easily iterate
over all edges along with their edge IDs.
Methods
all_edge_ids()
Return an iterator over all edge IDs of edges with a given tail and head.
Parameters tail : integer
head : integer
Returns edge_id : integer
Raises NodeDoesNotExist
EdgeDoesNotExistError
DisabledFeatureError
If tail is negative and the graph is not two-way compressed.
edge_id()
Return the edge ID of an edge given its sequential number.
Parameters edge_nr : integer
Returns edge_id : integer
Raises DisabledFeatureError
If edge_nr_translation was not enabled during graph compression.
EdgeDoesNotExistError
first_edge_id()
Return the edge ID of the first edge (tail, head).
Parameters tail : integer
head : integer
Returns edge_id : integer
Raises NodeDoesNotExist
EdgeDoesNotExistError
DisabledFeatureError
If tail is negative and the graph is not two-way compressed.
Notes
This method is useful for graphs where each edge occurs only once.
has_dangling_edges()
has_edge()
Does the graph contain the edge (tail, head)?
Notes
This method executes in constant time. It works for all edge IDs, returning both positive and
negative heads.
in_edge_ids()
Return the IDs of incoming edges of a node.
Parameters node : integer
Returns edge_ids : sequence of integers
Raises NodeDoesNotExistError
DisabledFeatureError
If the graph is not two-way compressed.
in_neighbors()
Return the nodes which point to a node.
Parameters node : integer
Returns nodes : sequence of integers
Raises NodeDoesNotExistError
DisabledFeatureError
If the graph is not two-way compressed.
out_edge_ids()
Return the IDs of outgoing edges of node.
Parameters node : integer
Returns edge_ids : sequence of integers
Raises NodeDoesNotExistError
out_neighbors()
Return the nodes a node points to.
Parameters node : integer
Returns nodes : sequence of integers
Raises NodeDoesNotExistError
tail()
Return the tail of an edge, given its edge ID.
Notes
The average performance of this method is O(log num_nodes) for non-negative tails and O(1)
for negative ones.
write_dot()
Write a representation of the graph in dot format to file.
Parameters file : file-like object
Notes
That resulting file can be visualized with dot(1) or neato(1) form the graphviz package.
Attributes
edge_nr_translation
num_edges
num_nodes
num_px_edges
num_xp_edges
twoway
kwant.graph.slice
kwant.graph.slice()
TODO: write me.
kwant.graph.make_undirected
kwant.graph.make_undirected()
undirected_graph(gr) expects a CGraph gr as input, which is interpreted as a directed graph, and
returns a CGraph that is explicitely undirected, i.e. for every edge (i,j) there is also the edge (j,i).
In the process, the function also removes all ‘dangling’ links, i.e. edges to or from negative node
numbers.
If remove_dups == True (default value is True), any duplicates of edges will be removed (this
applies to the case where there are multiple edges (i,j), not to having (i,j) and (j,i)).
The effect of the duplicate edges can be retained if calc_weights == True (default value is False),
in which case a weight array is returned containing the multiplicity of the edges after the graph
has been made undirected.
As a (somewhat drastic but illustrative) example, if make_undirected is applied to a undirected
graph, it will return the same graph again (possibly with the order of edges changed) and a weight
array with 2 everywhere. (Of course, in this case one does not need to call make_undirected ...)
make_undirected() will always return a one-way graph, regardless of whether the input was a two-
way graph or not (NOTE: This restriction could be lifted, if necessary). In addition, the original
edge_ids are lost – the resulting graph will have edge_ids that are not related to the original ones.
(NOTE: there certainly is a relation, but as long as no-one needs it it remains unspecified)
kwant.graph.remove_duplicates
kwant.graph.remove_duplicates()
Remove duplicate edges in the CGraph gr (this applies to the case where there are multiple edges
(i,j), not to having (i,j) and (j,i)). This function modifes the graph in place.
If edge_weights is provided, edge_weights is modified such that the new edge weights are the sum
of the old edge weights if there are duplicate edges.
This function only works on simple graphs (not two-way graphs), and it does not work on graphs
which have a relation between the edge number (given by the order the edges are added) and the
edge_id (given by the order the edges appear in the graph), see the documentation of CGraph.
(Both restrictions could be lifted if necessary.) Furthermore, the function does not support negative
node numbers, i.e. dangling links (the concept of being duplicate is more complicated there.)
kwant.graph.induced_subgraph
kwant.graph.induced_subgraph()
Return a subgraph of the CGraph gr by picking all nodes [0:gr.num_nodes] for which select is
True. select can be either a NumPy array, or a function that takes the node number as input. This
function returns a CGraph as well.
The nodes in the new graph are again numbered sequentially from 0 to num_nodes-1, where
num_nodes is the number of nodes in the subgraph. The numbering is done such that the ordering
of the node numbers in the original and the subgraph are preserved (i.e. if nodes n1 and n2 are
both in the subgraph, and original node number of n1 < original node number of n2, then also
subgraph node number n1 < subgraph node number n2).
If edge_weights is provided, the function also returns the edge weights for the subgraph which are
simply a subset of the original weights.
This function returns a simple graph, regardless of whether the input was a two-way graph or not
(NOTE: This restriction could be lifted, if necessary). Also, the resulting edge_ids are not related
to the original ones in any way (NOTE: There certainly is a relation, but as long no-one needs
it, we do not specify it). Also, negative nodes are discarded (NOTE: this restriction can also be
lifted).
kwant.graph.print_graph
kwant.graph.print_graph()
5.2.3 Other
This package wraps some selected LAPACK functionality not available via NumPy and also contains
a Python-wrapper for MUMPS. It also has several algorithms for finding approximately orthonormal
lattice bases. It is meant for internal use by Kwant itself, but of course nothing prevents you from using
it directly.
The documentation of this package is not included here on purpose in order not to add too many things
to this reference. Please consult the source code directly.
• genindex
161
INDEX
162
Kwant 1.3.1 documentation
F in_block_coords() (kwant.solvers.common.SMatrix
family (kwant.builder.Site attribute), 91 method), 119
family_a (kwant.builder.HoppingKind attribute), in_edge_ids() (kwant.graph.CGraph method), 157
92 in_fd() (kwant.builder.Symmetry method), 96
family_b (kwant.builder.HoppingKind attribute), in_fd() (kwant.lattice.TranslationalSymmetry
92 method), 98
fill() (kwant.builder.Builder method), 89 in_neighbors() (kwant.graph.CGraph method),
finalized() (kwant.builder.Builder method), 90 157
finalized() (kwant.builder.BuilderLead method), 94 index() (kwant.builder.HoppingKind method), 92
finalized() (kwant.builder.Lead method), 97 index() (kwant.builder.Site method), 91
finalized() (kwant.builder.ModesLead method), 94 induced_subgraph() (in module kwant.graph), 159
finalized() (kwant.builder.SelfEnergyLead InfiniteSystem (class in kwant.system), 149
method), 94 integrate() (kwant.kpm.SpectralDensity method),
FiniteSystem (class in kwant.system), 151 139
first_edge_id() (kwant.graph.CGraph method), inter_cell_hopping()
156 (kwant.system.InfiniteSystem method),
150
G interpolate_current() (in module kwant.plotter),
gauss() (in module kwant.digest), 135 112
gaussian() (in module kwant.rmt), 135
general() (in module kwant.lattice), 98
K
Graph (class in kwant.graph), 154 kagome() (in module kwant.lattice), 104
greens_function() (in module kwant (module), 85
kwant.solvers.default), 117 kwant.builder (module), 86
GreensFunction (class in kwant.solvers.common), kwant.continuum (module), 139
120 kwant.digest (module), 135
kwant.graph (module), 153
H kwant.kpm (module), 137
hamiltonian() (kwant.system.FiniteSystem kwant.lattice (module), 97
method), 151 kwant.linalg (module), 160
hamiltonian() (kwant.system.InfiniteSystem kwant.operator (module), 122
method), 149 kwant.physics (module), 128
hamiltonian() (kwant.system.System method), 148 kwant.plotter (module), 104
hamiltonian_submatrix() kwant.rmt (module), 135
(kwant.system.FiniteSystem method), kwant.solvers (module), 118
151 kwant.solvers.default (module), 115
hamiltonian_submatrix() kwant.solvers.mumps (module), 121
(kwant.system.InfiniteSystem method), kwant.solvers.sparse (module), 121
150 kwant.system (module), 147
hamiltonian_submatrix() (kwant.system.System kwant.wraparound (module), 143
method), 148 KwantDeprecationWarning, 85
has_dangling_edges() (kwant.graph.CGraph
method), 156 L
has_edge() (kwant.graph.CGraph method), 156 lambdify() (in module kwant.continuum), 143
has_subgroup() (kwant.builder.Symmetry ldos() (in module kwant.solvers.default), 118
method), 96 Lead (class in kwant.builder), 96
has_subgroup() (kwant.lattice.TranslationalSymmetry
method), 98 M
head() (kwant.graph.CGraph method), 157 make_undirected() (in module kwant.graph), 159
honeycomb() (in module kwant.lattice), 104 map() (in module kwant.plotter), 107
hopping_value_pairs() (kwant.builder.Builder mask_interpolate() (in module kwant.plotter), 115
method), 90 modes() (in module kwant.physics), 129
HoppingKind (class in kwant.builder), 92 modes() (kwant.builder.ModesLead method), 94
hoppings() (kwant.builder.Builder method), 90 modes() (kwant.system.InfiniteSystem method),
150
I modes() (kwant.system.PrecalculatedLead
in_block_coords() (kwant.solvers.common.GreensFunction method), 153
method), 120 ModesLead (class in kwant.builder), 94
Index 163
Kwant 1.3.1 documentation
164 Index
Kwant 1.3.1 documentation
T
tag (kwant.builder.Site attribute), 92
tail() (kwant.graph.CGraph method), 157
test() (in module kwant.digest), 135
to_fd() (kwant.builder.Symmetry method), 96
to_fd() (kwant.lattice.TranslationalSymmetry
method), 98
tocoo() (kwant.operator.Density method), 123
TranslationalSymmetry (class in kwant.lattice), 97
transmission() (kwant.solvers.common.GreensFunction
method), 120
transmission() (kwant.solvers.common.SMatrix
method), 119
triangular() (in module kwant.lattice), 104
two_terminal_shotnoise() (in module
kwant.physics), 131
twoway (kwant.graph.CGraph attribute), 158
U
uniform() (in module kwant.digest), 135
update() (kwant.builder.Builder method), 91
UserCodeError, 85
V
validate() (kwant.physics.DiscreteSymmetry
method), 133
vec() (kwant.lattice.Monatomic method), 100
vec() (kwant.lattice.Polyatomic method), 103
W
wave_function() (in module kwant.solvers.default),
117
where (kwant.operator.Current attribute), 126
where (kwant.operator.Density attribute), 124
where (kwant.operator.Source attribute), 128
which() (kwant.builder.Symmetry method), 96
which() (kwant.lattice.TranslationalSymmetry
method), 98
wire() (kwant.lattice.Monatomic method), 100
wire() (kwant.lattice.Polyatomic method), 103
wraparound() (in module kwant.wraparound), 143
write_dot() (kwant.graph.CGraph method), 158
write_dot() (kwant.graph.Graph method), 155
Index 165