Skip to content
Snippets Groups Projects
Commit 592979dd authored by Joseph Weston's avatar Joseph Weston
Browse files

shift tutorials to make room for another

parent cfa38b5c
No related branches found
No related tags found
No related merge requests found
......@@ -11,3 +11,4 @@ Tutorial: learning Kwant through examples
tutorial6
tutorial7
tutorial8
tutorial9
# Tutorial 2.7.1. 2D example: graphene quantum dot
# Tutorial 2.8.1. 2D example: graphene quantum dot
# ================================================
#
# Physics background
......
# Tutorial 2.7.2. 3D example: zincblende structure
# Tutorial 2.8.2. 3D example: zincblende structure
# ================================================
#
# Physical background
......
Plotting Kwant systems and data in various styles
-------------------------------------------------
The plotting functionality of Kwant has been used extensively (through
`~kwant.plotter.plot` and `~kwant.plotter.map`) in the previous tutorials. In
addition to this basic use, `~kwant.plotter.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.
2D example: graphene quantum dot
................................
.. seealso::
The complete source code of this example can be found in
:download:`tutorial/plot_graphene.py <../../../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 :ref:`tutorial-graphene`.) In contrast
to previous examples, we will also use hoppings beyond next-nearest neighbors:
.. literalinclude:: plot_graphene.py
:start-after: #HIDDEN_BEGIN_makesyst
:end-before: #HIDDEN_END_makesyst
Note that adding hoppings hoppings to the `n`-th nearest neighbors can be
simply done by passing `n` as an argument to
`~kwant.lattice.Polyatomic.neighbors`. Also note that we use the method
`~kwant.builder.Builder.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 hopping [#]_.
Of course, the system can be plotted simply with default settings:
.. literalinclude:: plot_graphene.py
:start-after: #HIDDEN_BEGIN_plotsyst1
:end-before: #HIDDEN_END_plotsyst1
However, due to the richer structure of the lattice, this results in a rather
busy plot:
.. image:: ../images/plot_graphene_syst1.*
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
`~kwant.plotter.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:
.. literalinclude:: plot_graphene.py
:start-after: #HIDDEN_BEGIN_plotsyst2
:end-before: #HIDDEN_END_plotsyst2
Note that since we are using an unfinalized Builder, a ``site`` is really an
instance of `~kwant.builder.Site`. With these adjustments we arrive at a plot
that carries the same information, but is much easier to interpret:
.. image:: ../images/plot_graphene_syst2.*
Apart from plotting the *system* itself, `~kwant.plotter.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):
.. literalinclude:: plot_graphene.py
:start-after: #HIDDEN_BEGIN_plotdata1
:end-before: #HIDDEN_END_plotdata1
In most cases, to plot the wave function probability, one wouldn't use
`~kwant.plotter.plot`, but rather `~kwant.plotter.map`. Here, we plot the
`n`-th wave function using it:
.. literalinclude:: plot_graphene.py
:start-after: #HIDDEN_BEGIN_plotdata2
:end-before: #HIDDEN_END_plotdata2
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.
.. image:: ../images/plot_graphene_data1.*
However although in general preferable, `~kwant.plotter.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 `~kwant.plotter.map`). However, we can also use `~kwant.plotter.plot` to
achieve a similar, but smoother result.
For this note that `~kwant.plotter.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 `~kwant.plotter.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:
.. literalinclude:: plot_graphene.py
:start-after: #HIDDEN_BEGIN_plotdata3
:end-before: #HIDDEN_END_plotdata3
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, `~kwant.plotter.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
`~kwant.builder.Site`, ``syst.sites[i]`` can be used.
With this we arrive at
.. image:: ../images/plot_graphene_data2.*
with the same information as `~kwant.plotter.map`, but with a cleaner look.
The way how data is presented of course influences what features of the data
are best visible in a given plot. With `~kwant.plotter.plot` one can easily go
beyond pseudocolor-like plots. For example, we can represent the wave function
probability using the symbols itself:
.. literalinclude:: plot_graphene.py
:start-after: #HIDDEN_BEGIN_plotdata4
:end-before: #HIDDEN_END_plotdata4
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
.. image:: ../images/plot_graphene_data3.*
which shows the edge state nature of the wave function most clearly.
.. rubric:: Footnotes
.. [#] 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.
3D example: zincblende structure
................................
.. seealso::
The complete source code of this example can be found in
:download:`tutorial/plot_zincblende.py <../../../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`:
.. literalinclude:: plot_zincblende.py
:start-after: #HIDDEN_BEGIN_zincblende1
:end-before: #HIDDEN_END_zincblende1
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 `~kwant.lattice.PolyatomicLattice.shape`-functionality:
.. literalinclude:: plot_zincblende.py
:start-after: #HIDDEN_BEGIN_zincblende2
:end-before: #HIDDEN_END_zincblende2
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).
`~kwant.plotter.plot` can plot 3D systems just as easily as its two-dimensional
counterparts:
.. literalinclude:: plot_zincblende.py
:start-after: #HIDDEN_BEGIN_plot1
:end-before: #HIDDEN_END_plot1
resulting in
.. image:: ../images/plot_zincblende_syst1.*
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:
.. literalinclude:: plot_zincblende.py
:start-after: #HIDDEN_BEGIN_plot2
:end-before: #HIDDEN_END_plot2
which results in a 3D plot that allows to interactively (when plotted
in a window) explore the crystal structure:
.. image:: ../images/plot_zincblende_syst2.*
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.
Interpolated density and current: QPC with disorder
...................................................
.. seealso::
The complete source code of this example can be found in
:download:`tutorial/plot_qpc.py <../../../tutorial/plot_qpc.py>`
In the above examples we saw some useful methods for plotting systems where
single-site resolution is required. Sometimes, however, having single-site
precision is a hinderance, rather than a help, and looking at *averaged*
quantities is more useful. This is particularly important in systems with a
large number of sites, and systems that are discretizations of continuum
models.
Here we will show how to plot interpolated quantities using `kwant.plotter.map`
and `kwant.plotter.current` using the example of a quantum point contact (QPC)
with a perpendicular magnetic field and disorder:
.. literalinclude:: plot_qpc.py
:start-after: #HIDDEN_BEGIN_syst
:end-before: #HIDDEN_END_syst
.. image:: ../images/plot_qpc_syst.*
Now we will compute the density of particles and current due to states
originating in the left lead with energy 0.15.
.. literalinclude:: plot_qpc.py
:start-after: #HIDDEN_BEGIN_wf
:end-before: #HIDDEN_END_wf
We can then plot the density using `~kwant.plotter.map`:
.. literalinclude:: plot_qpc.py
:start-after: #HIDDEN_BEGIN_density
:end-before: #HIDDEN_END_density
.. image:: ../images/plot_qpc_density.*
We pass ``method='linear'`` to ``map``, which produces a smoother plot than the
default style. We see that density is concentrated on the edges of the sample,
as we expect due to Hall effect induced by the perpendicular magnetic field.
Plotting the current in the system will enable us to make even more sense
of what is going on:
.. literalinclude:: plot_qpc.py
:start-after: #HIDDEN_BEGIN_current
:end-before: #HIDDEN_END_current
.. image:: ../images/plot_qpc_current.*
We can now clearly see that current enters the system from the lower-left edge
of the system (this matches our intuition, as we calculated the current for
scattering states originating in the left-hand lead), and is backscattered by
the restriction of the QPC in the centre.
This diff is collapsed.
This diff is collapsed.
Discretizing continuous Hamiltonians
------------------------------------
Introduction
............
In ":ref:`tutorial_discretization_schrodinger`" 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 `~kwant.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.
.. seealso::
The complete source code of this tutorial can be found in
:download:`tutorial/continuum_discretizer.py <../../../tutorial/continuum_discretizer.py>`
.. _tutorial_discretizer_introduction:
Discretizing by hand
....................
As an example, let us consider the following continuum Schrödinger equation
for a semiconducting heterostructure (using the effective mass approximation):
.. math::
\left( k_x \frac{\hbar^2}{2 m(x)} k_x \right) \psi(x) = E \, \psi(x).
Replacing the momenta by their corresponding differential operators
.. math::
k_\alpha = -i \partial_\alpha,
for :math:`\alpha = x, y` or :math:`z`, and discretizing on a regular lattice of
points with spacing :math:`a`, we obtain the tight-binding model
.. math::
H = - \frac{1}{a^2} \sum_i A\left(x+\frac{a}{2}\right)
\big(\ket{i}\bra{i+1} + h.c.\big)
+ \frac{1}{a^2} \sum_i
\left( A\left(x+\frac{a}{2}\right) + A\left(x-\frac{a}{2}\right)\right)
\ket{i} \bra{i},
with :math:`A(x) = \frac{\hbar^2}{2 m(x)}`.
Using `~kwant.continuum.discretize` to obtain a template
........................................................
The function `kwant.continuum.discretize` takes a symbolic Hamiltonian and
turns it into a `~kwant.builder.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).
.. literalinclude:: continuum_discretizer.py
:start-after: #HIDDEN_BEGIN_symbolic_discretization
:end-before: #HIDDEN_END_symbolic_discretization
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.
Setting the ``verbose`` parameter to ``True`` prints extra information about the
onsite and hopping functions assigned to the ``Builder`` produced
by ``discretize``:
.. literalinclude:: ../images/discretizer_intro_verbose.txt
.. specialnote:: 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
:math:`H(k_x, k_y, k_z)`, multiplying it from the right by
:math:`\psi(x, y, z)` and iteratively applying a second-order accurate
central derivative approximation for every
:math:`k_\alpha=-i\partial_\alpha`:
.. math::
\partial_\alpha \psi(\alpha) =
\frac{1}{a} \left( \psi\left(\alpha + \frac{a}{2}\right)
-\psi\left(\alpha - \frac{a}{2}\right)\right).
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
`~kwant.continuum.discretize_symbolic` to obtain symbolic output.
When working interactively in `Jupyter notebooks <https://jupyter.org/>`_
it can be useful to use this to see a symbolic representation of
the discretized Hamiltonian. This works best when combined with ``sympy``
`Pretty Printing <http://docs.sympy.org/latest/tutorial/printing.html#setting-up-pretty-printing>`_.
- The symbolic result of discretization obtained with
``discretize_symbolic`` can be converted into a
builder using `~kwant.continuum.build_discretized`.
This can be useful if one wants to alter the tight-binding Hamiltonian
before building the system.
Building a Kwant system from the template
.........................................
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
.. math::
H = k_x^2 + k_y^2 + V(x, y),
where :math:`V(x, y)` is some arbitrary potential.
First, use ``discretize`` to obtain a
builder that we will use as a template:
.. literalinclude:: continuum_discretizer.py
:start-after: #HIDDEN_BEGIN_template
:end-before: #HIDDEN_END_template
We now use this system with the `~kwant.builder.Builder.fill`
method of `~kwant.builder.Builder` to construct the system we
want to investigate:
.. literalinclude:: continuum_discretizer.py
:start-after: #HIDDEN_BEGIN_fill
:end-before: #HIDDEN_END_fill
After finalizing this system, we can plot one of the system's
energy eigenstates:
.. literalinclude:: continuum_discretizer.py
:start-after: #HIDDEN_BEGIN_plot_eigenstate
:end-before: #HIDDEN_END_plot_eigenstate
.. image:: ../images/discretizer_gs.*
Note in the above that we provided the function ``V`` to
``syst.hamiltonian_submatrix`` using ``params=dict(V=potential)``, rather than
via ``args``.
In addition, the function passed as ``V`` expects two input parameters ``x``
and ``y``, the same as in the initial continuum Hamiltonian.
Models with more structure: Bernevig-Hughes-Zhang
.................................................
When working with multi-band systems, like the Bernevig-Hughes-Zhang (BHZ)
model [1]_ [2]_, one can provide matrix input to `~kwant.continuum.discretize`
using ``identity`` and ``kron``. For example, the definition of the BHZ model can be
written succinctly as:
.. literalinclude:: continuum_discretizer.py
:start-after: #HIDDEN_BEGIN_define_qsh
:end-before: #HIDDEN_END_define_qsh
We can then make a ribbon out of this template system:
.. literalinclude:: continuum_discretizer.py
:start-after: #HIDDEN_BEGIN_define_qsh_build
:end-before: #HIDDEN_END_define_qsh_build
and plot its dispersion using `kwant.plotter.bands`:
.. literalinclude:: continuum_discretizer.py
:start-after: #HIDDEN_BEGIN_plot_qsh_band
:end-before: #HIDDEN_END_plot_qsh_band
.. image:: ../images/discretizer_qsh_band.*
In the above we see the edge states of the quantum spin Hall effect, which
we can visualize using `kwant.plotter.map`:
.. literalinclude:: continuum_discretizer.py
:start-after: #HIDDEN_BEGIN_plot_qsh_wf
:end-before: #HIDDEN_END_plot_qsh_wf
.. image:: ../images/discretizer_qsh_wf.*
Limitations of discretization
.............................
It is important to remember that the discretization of a continuum
model is an *approximation* that is only valid in the low-energy
limit. For example, the quadratic continuum Hamiltonian
.. math::
H_\textrm{continuous}(k_x) = \frac{\hbar^2}{2m}k_x^2
and its discretized approximation
.. math::
H_\textrm{tight-binding}(k_x) = 2t \big(1 - \cos(k_x a)\big),
where :math:`t=\frac{\hbar^2}{2ma^2}`, are only valid in the limit
:math:`E \lt t`. The grid spacing :math:`a` must be chosen according
to how high in energy you need your tight-binding model to be valid.
It is possible to set :math:`a` through the ``grid_spacing`` parameter
to `~kwant.continuum.discretize`, as we will illustrate in the following
example. Let us start from the continuum Hamiltonian
.. math::
H(k) = k_x^2 \mathbb{1}_{2\times2} + α k_x \sigma_y.
We start by defining this model as a string and setting the value of the
:math:`α` parameter:
.. literalinclude:: continuum_discretizer.py
:start-after: #HIDDEN_BEGIN_ls_def
:end-before: #HIDDEN_END_ls_def
Now we can use `kwant.continuum.lambdify` to obtain a function that computes
:math:`H(k)`:
.. literalinclude:: continuum_discretizer.py
:start-after: #HIDDEN_BEGIN_ls_hk_cont
:end-before: #HIDDEN_END_ls_hk_cont
We can also construct a discretized approximation using
`kwant.continuum.discretize`, in a similar manner to previous examples:
.. literalinclude:: continuum_discretizer.py
:start-after: #HIDDEN_BEGIN_ls_hk_tb
:end-before: #HIDDEN_END_ls_hk_tb
Below we can see the continuum and tight-binding dispersions for two
different values of the discretization grid spacing :math:`a`:
.. image:: ../images/discretizer_lattice_spacing.*
We clearly see that the smaller grid spacing is, the better we approximate
the original continuous dispersion. It is also worth remembering that the
Brillouin zone also scales with grid spacing: :math:`[-\frac{\pi}{a},
\frac{\pi}{a}]`.
Advanced topics
...............
The input to `kwant.continuum.discretize` and `kwant.continuum.lambdify` can be
not only a ``string``, as we saw above, but also a ``sympy`` expression or
a ``sympy`` matrix.
This functionality will probably be mostly useful to people who
are already experienced with ``sympy``.
It is possible to use ``identity`` (for identity matrix), ``kron`` (for Kronecker product), as well as Pauli matrices ``sigma_0``,
``sigma_x``, ``sigma_y``, ``sigma_z`` in the input to
`~kwant.continuum.lambdify` and `~kwant.continuum.discretize`, in order to simplify
expressions involving matrices. Matrices can also be provided explicitly using
square ``[]`` brackets. For example, all following expressions are equivalent:
.. literalinclude:: continuum_discretizer.py
:start-after: #HIDDEN_BEGIN_subs_1
:end-before: #HIDDEN_END_subs_1
.. literalinclude:: ../images/discretizer_subs_1.txt
We can use the ``substitutions`` keyword parameter to substitute expressions
and numerical values:
.. literalinclude:: continuum_discretizer.py
:start-after: #HIDDEN_BEGIN_subs_2
:end-before: #HIDDEN_END_subs_2
.. literalinclude:: ../images/discretizer_subs_2.txt
Symbolic expressions obtained in this way can be directly passed to all
``discretizer`` functions.
.. specialnote:: Technical 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 :math:`x k_y x`
will not be simplified, even though mathematically :math:`[x, k_y] = 0`.
.. rubric:: References
.. [1] `Science, 314, 1757 (2006)
<https://arxiv.org/abs/cond-mat/0611399>`_.
.. [2] `Phys. Rev. B 82, 045122 (2010)
<https://arxiv.org/abs/1005.1682>`_.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment