Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • kwant/kwant
  • jbweston/kwant
  • anton-akhmerov/kwant
  • cwg/kwant
  • Mathieu/kwant
  • slavoutich/kwant
  • pacome/kwant
  • behrmann/kwant
  • michaelwimmer/kwant
  • albeercik/kwant
  • eunjongkim/kwant
  • basnijholt/kwant
  • r-j-skolasinski/kwant
  • sahmed95/kwant
  • pablopiskunow/kwant
  • mare/kwant
  • dvarjas/kwant
  • Paul/kwant
  • bbuijtendorp/kwant
  • tkloss/kwant
  • torosdahl/kwant
  • kel85uk/kwant
  • kpoyhonen/kwant
  • Fromeworld/kwant
  • quaeritis/kwant
  • marwahaha/kwant
  • fernandodfufrpe/kwant
  • oly/kwant
  • jiamingh/kwant
  • mehdi2369/kwant
  • ValFadeev/kwant
  • Kostas/kwant
  • chelseabaptiste03/kwant
33 results
Show changes
Commits on Source (24)
Showing
with 1645 additions and 374 deletions
...@@ -166,6 +166,14 @@ check for dependencies installed: ...@@ -166,6 +166,14 @@ check for dependencies installed:
.test: &test .test: &test
stage: test
script:
- py.test -r w --flakes kwant --junitxml=tests.xml --durations=10
artifacts:
reports:
junit: tests.xml
.coverage: &coverage
stage: test stage: test
script: script:
- py.test -r w --cov=kwant --cov-report term --cov-report html --flakes kwant --junitxml=tests.xml --durations=10 - py.test -r w --cov=kwant --cov-report term --cov-report html --flakes kwant --junitxml=tests.xml --durations=10
...@@ -215,6 +223,14 @@ test:bleeding-edge: ...@@ -215,6 +223,14 @@ test:bleeding-edge:
- schedules - schedules
allow_failure: true allow_failure: true
coverage:latest:
<<: *coverage
<<: *latest_env
dependencies:
- build:latest
only:
- schedules
## Documentation building ## Documentation building
build documentation: build documentation:
...@@ -223,7 +239,9 @@ build documentation: ...@@ -223,7 +239,9 @@ build documentation:
- build:latest - build:latest
stage: test stage: test
script: script:
- make -C doc realclean; make -C doc html SPHINXOPTS='-A website_deploy=True -n -W' SOURCE_LINK_TEMPLATE="$CI_PROJECT_URL"/blob/\$\$r/\$\$f - pip install git+https://github.com/jupyter-widgets/jupyter-sphinx
- python -m ipykernel install --user --name kwant-latest
- make -C doc realclean; make -C doc html SPHINXOPTS='-A website_deploy=True -n -W -D jupyter_execute_default_kernel=kwant-latest' SOURCE_LINK_TEMPLATE="$CI_PROJECT_URL"/blob/\$\$r/\$\$f
artifacts: artifacts:
paths: paths:
- doc/build/html/ - doc/build/html/
...@@ -235,7 +253,9 @@ build PDF documentation: ...@@ -235,7 +253,9 @@ build PDF documentation:
- build:latest - build:latest
stage: test stage: test
script: script:
- make -C doc latex SPHINXOPTS='-n -W' - pip install git+https://github.com/jupyter-widgets/jupyter-sphinx
- python -m ipykernel install --user --name kwant-latest
- make -C doc latex SPHINXOPTS='-n -W -D jupyter_execute_default_kernel=kwant-latest'
- cd doc/build/latex - cd doc/build/latex
- make all-pdf - make all-pdf
artifacts: artifacts:
...@@ -258,7 +278,9 @@ check for broken links in doc: ...@@ -258,7 +278,9 @@ check for broken links in doc:
upload coverage: upload coverage:
stage: deploy stage: deploy
only: only:
- branches@kwant/kwant - schedules
dependencies:
- coverage:latest
environment: environment:
name: coverage/$CI_BUILD_REF_NAME name: coverage/$CI_BUILD_REF_NAME
url: https://kwant-project.org/coverage/$CI_BUILD_REF_SLUG url: https://kwant-project.org/coverage/$CI_BUILD_REF_SLUG
......
# Release %vX.Y.Z **UPDATE THIS**
This is a checklist based on the [release document](RELEASE.rst); consult it for more details.
## Preflight checks
- [ ] All the issues from this milestone are resolved
- [ ] Ensure that tests pass (either on [stable](https://gitlab.kwant-project.org/kwant/kwant/tree/stable) or on [master](https://gitlab.kwant-project.org/kwant/kwant/tree/master) branch)
- [ ] Documentation looks correct https://test.kwant-project.org/doc/<branch name>
- [ ] [Whatnew](doc/source/pre/whatsnew) is up to date
- [ ] `AUTHORS.rst` and `.mailmap` are up to date (run `git shortlog -s | sed -e "s/^ *[0-9\t ]*//"| xargs -i sh -c 'grep -q "{}" AUTHORS.rst || echo "{}"'`)
## Make a release, but do not publish it yet
- [ ] Tag the release
- [ ] Build the source tarball and inspect it
- [ ] Build the documentation
## Test packaging
These steps may be done in parallel
### Debian
- [ ] Follow the steps for building the Debian packages from [RELEASE.rst](RELEASE.rst)
### Conda
- [ ] Publish the release candidate source tarball "somewhere on the internet" (SOTI)
- [ ] open a PR to the [conda-forge/kwant-feedstock](https://github.com/conda-forge/kwant-feedstock/) repo on Github. Make sure to mark the PR as WIP so that it doesn't get merged in accidentally
- [ ] set the upstream tarball URL (in meta.yaml) to SOTI
- [ ] update the tarball hash in meta.yaml
- [ ] See if the package builds
## Publish the release
- [ ] push the tag
- [ ] upload the source tarball to PyPI
- [ ] upload the source tarball to the Kwant website
- [ ] publish the debian packages
- [ ] publish the ubuntu packages
- [ ] create a new version of the Kwant conda-forge feedstock, and open a pull request to upstream
- [ ] upload the documentation to the Kwant website
- [ ] update the Kwant website to say that Conda is the preferred way to install Kwant on Windows
## Announce the release
- [ ] Write a short post summarizing the highlights on the Kwant website
- [ ] post to the Kwant mailing list
## Working towards next release
- [ ] add a whatsnew file for the next release
- [ ] tag this release with an `a0` suffix
- [ ] push this tag to the official Kwant repository
- [ ] create a milestone for the next release
...@@ -3,6 +3,10 @@ Making a Kwant release ...@@ -3,6 +3,10 @@ Making a Kwant release
This document guides a contributor through creating a release of Kwant. This document guides a contributor through creating a release of Kwant.
Create a release issue
######################
Use the correct `issue template <gitlab.kwant-project.org/kwant/kwant/issues/new?issuable_template=release>`_, adjust it if necessary.
Preflight checks Preflight checks
################ ################
...@@ -114,21 +118,23 @@ Building the documentation requires 'sphinx' and a Latex installation. ...@@ -114,21 +118,23 @@ Building the documentation requires 'sphinx' and a Latex installation.
First build the HTML and PDF documentation:: First build the HTML and PDF documentation::
./setup.py build ./setup.py build
cd doc make -C doc realclean
make realclean make -C doc html latex
make html latex SPHINXOPTS='-A website_deploy=True -n -W' make -C doc/build/latex all-pdf
cd doc/build/latex
make all-pdf
Then create a zipped version of the HTML documentation and name the PDF Then create a zipped version of the HTML documentation and name the PDF
consistently, storing them, for example, in the "dist" directory along with the consistently, storing them, for example, in the "dist" directory along with the
source tarballs:: source tarballs::
ln -s `pwd`/doc/build/html /tmp/kwant-doc-<version> version=$(git describe | sed 's/^v//') # Assumes that we are on a tag.
(cd /tmp/; zip -r kwant-doc-<version>.zip kwant-doc-<version>) ln -s `pwd`/doc/build/html /tmp/kwant-doc-$version
mv /tmp/kwant-doc-<version>.zip dist (cd /tmp/; zip -r kwant-doc-$version.zip kwant-doc-$version)
mv /tmp/kwant-doc-$version.zip dist
mv doc/build/latex/kwant.pdf dist/kwant-doc-$version.pdf
Finally, rebuild the documentation for the website (including the web analysis javascript code)::
mv doc/build/latex/kwant.pdf dist/kwant-doc-<version>.pdf make -C doc html SPHINXOPTS='-A website_deploy=True -n -W'
Clone the repository of the Kwant Debian package Clone the repository of the Kwant Debian package
...@@ -514,23 +520,20 @@ Ask Christoph Groth if you need to be granted access. ...@@ -514,23 +520,20 @@ Ask Christoph Groth if you need to be granted access.
Upload the zipped HTML and PDF documentation:: Upload the zipped HTML and PDF documentation::
scp dist/kwant-doc-<version>.zip kwant-project.org:webapps/downloads/doc scp dist/kwant-doc-<version>.{zip,pdf} kwant-project.org:webapps/downloads/doc
scp dist/kwant-doc-<version>.pdf kwant-project.org:webapps/downloads/doc
Point the symbolic links ``latest.zip`` and ``latest.pdf`` to these new files::
ssh kwant-project.org "cd webapps/downloads/doc; ln -s kwant-doc-<version>.zip latest.zip"
ssh kwant-project.org "cd webapps/downloads/doc; ln -s kwant-doc-<version>.pdf latest.pdf"
Then upload the HTML documentation for the main website:: Upload the HTML documentation for the website::
rsync -rlv --delete doc/build/html/* kwant-project.org:webapps/kwant/doc/<short-version> rsync -rlv --delete doc/build/html/* kwant-project.org:webapps/kwant/doc/<short-version>
where in the above ``<short-version>`` is just the major and minor version numbers. where in the above ``<short-version>`` is just the major and minor version numbers.
Finally point the symbolic link ``<major-version>`` to ``<short-version>``:: Finally, create symbolic links for the website::
ssh kwant-project.org "cd webapps/kwant/doc; ln -s <major> <short-version>" ssh kwant-project.org
for e in zip pdf; do ln -sf kwant-doc-<version>.$e webapps/downloads/doc/latest.$e; done
ln -nsf <short-version> webapps/kwant/doc/<major>
exit
Announce the release Announce the release
......
...@@ -15,8 +15,15 @@ import sys ...@@ -15,8 +15,15 @@ import sys
import os import os
import string import string
from distutils.util import get_platform from distutils.util import get_platform
sys.path.insert(0, "../../build/lib.{0}-{1}.{2}".format(
get_platform(), *sys.version_info[:2])) package_path = os.path.abspath(
"../../build/lib.{0}-{1}.{2}"
.format(get_platform(), *sys.version_info[:2]))
# Insert into sys.path so that we can import kwant here
sys.path.insert(0, package_path)
# Insert into PYTHONPATH so that jupyter-sphinx will pick it up
os.environ['PYTHONPATH'] = ':'.join((package_path, os.environ.get('PYTHONPATH','')))
import kwant import kwant
import kwant.qsymm import kwant.qsymm
...@@ -31,7 +38,7 @@ sys.path.insert(0, os.path.abspath('../sphinxext')) ...@@ -31,7 +38,7 @@ sys.path.insert(0, os.path.abspath('../sphinxext'))
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.autosummary', extensions = ['sphinx.ext.autodoc', 'sphinx.ext.autosummary',
'sphinx.ext.todo', 'sphinx.ext.mathjax', 'numpydoc', 'sphinx.ext.todo', 'sphinx.ext.mathjax', 'numpydoc',
'kwantdoc', 'sphinx.ext.linkcode'] 'kwantdoc', 'sphinx.ext.linkcode', 'jupyter_sphinx.execute']
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['../templates'] templates_path = ['../templates']
...@@ -239,7 +246,6 @@ autosummary_generate = True ...@@ -239,7 +246,6 @@ autosummary_generate = True
autoclass_content = "both" autoclass_content = "both"
autodoc_default_flags = ['show-inheritance'] autodoc_default_flags = ['show-inheritance']
# -- Teach Sphinx to document bound methods like functions --------------------- # -- Teach Sphinx to document bound methods like functions ---------------------
import types import types
from sphinx.ext import autodoc from sphinx.ext import autodoc
......
What's new in Kwant 1.5
=======================
This article explains the user-visible changes in Kwant 1.5.0.
...@@ -2,6 +2,7 @@ What's new in Kwant ...@@ -2,6 +2,7 @@ What's new in Kwant
=================== ===================
.. toctree:: .. toctree::
1.5
1.4 1.4
1.3 1.3
1.2 1.2
......
...@@ -62,13 +62,35 @@ Transport through a quantum wire ...@@ -62,13 +62,35 @@ Transport through a quantum wire
.. seealso:: .. seealso::
The complete source code of this example can be found in The complete source code of this example can be found in
:download:`quantum_wire.py </code/download/quantum_wire.py>` :jupyter-download:script:`quantum_wire`
In order to use Kwant, we need to import it: In order to use Kwant, we need to import it:
.. literalinclude:: /code/include/quantum_wire.py .. jupyter-kernel::
:start-after: #HIDDEN_BEGIN_dwhx :id: quantum_wire
:end-before: #HIDDEN_END_dwhx
.. jupyter-execute::
:hide-code:
# Tutorial 2.2.2. Transport through a quantum wire
# ================================================
#
# Physics background
# ------------------
# Conductance of a quantum wire; subbands
#
# Kwant features highlighted
# --------------------------
# - Builder for setting up transport systems easily
# - Making scattering region and leads
# - Using the simple sparse solver for computing Landauer conductance
# For plotting
from matplotlib import pyplot
.. jupyter-execute::
import kwant
Enabling Kwant is as easy as this [#]_ ! Enabling Kwant is as easy as this [#]_ !
...@@ -76,9 +98,11 @@ The first step is now the definition of the system with scattering region and ...@@ -76,9 +98,11 @@ The first step is now the definition of the system with scattering region and
leads. For this we make use of the `~kwant.builder.Builder` type that allows to leads. For this we make use of the `~kwant.builder.Builder` type that allows to
define a system in a convenient way. We need to create an instance of it: define a system in a convenient way. We need to create an instance of it:
.. literalinclude:: /code/include/quantum_wire.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_goiq
:end-before: #HIDDEN_END_goiq # First define the tight-binding system
syst = kwant.Builder()
Observe that we just accessed `~kwant.builder.Builder` by the name Observe that we just accessed `~kwant.builder.Builder` by the name
``kwant.Builder``. We could have just as well written ``kwant.Builder``. We could have just as well written
...@@ -92,9 +116,10 @@ Apart from `~kwant.builder.Builder` we also need to specify ...@@ -92,9 +116,10 @@ Apart from `~kwant.builder.Builder` we also need to specify
what kind of sites we want to add to the system. Here we work with 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 square lattice. For simplicity, we set the lattice constant to unity:
.. literalinclude:: /code/include/quantum_wire.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_suwo
:end-before: #HIDDEN_END_suwo a = 1
lat = kwant.lattice.square(a)
Since we work with a square lattice, we label the points with two Since we work with a square lattice, we label the points with two
integer coordinates `(i, j)`. `~kwant.builder.Builder` then integer coordinates `(i, j)`. `~kwant.builder.Builder` then
...@@ -111,9 +136,25 @@ needed in Builder (more about that in the technical details below). ...@@ -111,9 +136,25 @@ needed in Builder (more about that in the technical details below).
We now build a rectangular scattering region that is `W` We now build a rectangular scattering region that is `W`
lattice points wide and `L` lattice points long: lattice points wide and `L` lattice points long:
.. literalinclude:: /code/include/quantum_wire.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_zfvr
:end-before: #HIDDEN_END_zfvr t = 1.0
W, L = 10, 30
# Define the scattering region
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
# 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 Observe how the above code corresponds directly to the terms of the discretized
Hamiltonian: Hamiltonian:
...@@ -140,9 +181,14 @@ Next, we define the leads. Leads are also constructed using ...@@ -140,9 +181,14 @@ Next, we define the leads. Leads are also constructed using
`~kwant.builder.Builder`, but in this case, the `~kwant.builder.Builder`, but in this case, the
system must have a translational symmetry: system must have a translational symmetry:
.. literalinclude:: /code/include/quantum_wire.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_xcmc
:end-before: #HIDDEN_END_xcmc # Then, define and attach the leads:
# First the lead to the left
# (Note: TranslationalSymmetry takes a real-space vector)
sym_left_lead = kwant.TranslationalSymmetry((-a, 0))
left_lead = kwant.Builder(sym_left_lead)
Here, the `~kwant.builder.Builder` takes a Here, the `~kwant.builder.Builder` takes a
`~kwant.lattice.TranslationalSymmetry` as the optional parameter. Note that the `~kwant.lattice.TranslationalSymmetry` as the optional parameter. Note that the
...@@ -155,17 +201,22 @@ as the hoppings inside one unit cell and to the next unit cell of the lead. ...@@ -155,17 +201,22 @@ 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 For a square lattice, and a lead in y-direction the unit cell is
simply a vertical line of points: simply a vertical line of points:
.. literalinclude:: /code/include/quantum_wire.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_ndez
:end-before: #HIDDEN_END_ndez 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 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 previous unit cell -- the translational symmetry takes care of that. The
isolated, infinite is attached at the correct position using isolated, infinite is attached at the correct position using
.. literalinclude:: /code/include/quantum_wire.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_fskr :hide-output:
:end-before: #HIDDEN_END_fskr
syst.attach_lead(left_lead)
This call returns the lead number which will be used to refer to the lead when 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 computing transmissions (further down in this tutorial). More details about
...@@ -175,9 +226,21 @@ We also want to add a lead on the right side. The only difference to ...@@ -175,9 +226,21 @@ 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 the left lead is that the vector of the translational
symmetry must point to the right, the remaining code is the same: symmetry must point to the right, the remaining code is the same:
.. literalinclude:: /code/include/quantum_wire.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_xhqc :hide-output:
:end-before: #HIDDEN_END_xhqc
# Then the lead to the right
sym_right_lead = kwant.TranslationalSymmetry((a, 0))
right_lead = kwant.Builder(sym_right_lead)
for j in range(W):
right_lead[lat(0, j)] = 4 * t
if j > 0:
right_lead[lat(0, j), lat(0, j - 1)] = -t
right_lead[lat(1, j), lat(0, j)] = -t
syst.attach_lead(right_lead)
Note that here we added points with x-coordinate 0, just as for the left 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` You might object that the right lead should be placed `L`
...@@ -187,13 +250,9 @@ you do not need to worry about that. ...@@ -187,13 +250,9 @@ you do not need to worry about that.
Now we have finished building our system! We plot it, to make sure we didn't Now we have finished building our system! We plot it, to make sure we didn't
make any mistakes: make any mistakes:
.. literalinclude:: /code/include/quantum_wire.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_wsgh
:end-before: #HIDDEN_END_wsgh
This should bring up this picture: kwant.plot(syst);
.. image:: /code/figure/quantum_wire_syst.*
The system is represented in the usual way for tight-binding systems: The system is represented in the usual way for tight-binding systems:
dots represent the lattice points `(i, j)`, and for every dots represent the lattice points `(i, j)`, and for every
...@@ -203,16 +262,29 @@ fading color. ...@@ -203,16 +262,29 @@ fading color.
In order to use our system for a transport calculation, we need to finalize it In order to use our system for a transport calculation, we need to finalize it
.. literalinclude:: /code/include/quantum_wire.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_dngj
:end-before: #HIDDEN_END_dngj # Finalize the system
syst = syst.finalized()
Having successfully created a system, we now can immediately start to compute Having successfully created a system, we now can immediately start to compute
its conductance as a function of energy: its conductance as a function of energy:
.. literalinclude:: /code/include/quantum_wire.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_buzn
:end-before: #HIDDEN_END_buzn # Now that we have the system, we can compute conductance
energies = []
data = []
for ie in range(100):
energy = ie * 0.01
# compute the scattering matrix at a given energy
smatrix = kwant.smatrix(syst, energy)
# compute the transmission probability from lead 0 to
# lead 1
energies.append(energy)
data.append(smatrix.transmission(1, 0))
We use ``kwant.smatrix`` which is a short name for We use ``kwant.smatrix`` which is a short name for
`kwant.solvers.default.smatrix` of the default solver module `kwant.solvers.default.smatrix` of the default solver module
...@@ -227,13 +299,15 @@ Finally we can use ``matplotlib`` to make a plot of the computed data ...@@ -227,13 +299,15 @@ Finally we can use ``matplotlib`` to make a plot of the computed data
(although writing to file and using an external viewer such as (although writing to file and using an external viewer such as
gnuplot or xmgrace is just as viable) gnuplot or xmgrace is just as viable)
.. literalinclude:: /code/include/quantum_wire.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_lliv
:end-before: #HIDDEN_END_lliv
This should yield the result
.. image:: /code/figure/quantum_wire_result.* # Use matplotlib to write output
# We should see conductance steps
pyplot.figure()
pyplot.plot(energies, data)
pyplot.xlabel("energy [t]")
pyplot.ylabel("conductance [e^2/h]")
pyplot.show()
We see a conductance quantized in units of :math:`e^2/h`, We see a conductance quantized in units of :math:`e^2/h`,
increasing in steps as the energy is increased. The increasing in steps as the energy is increased. The
...@@ -333,7 +407,12 @@ Building the same system with less code ...@@ -333,7 +407,12 @@ Building the same system with less code
.. seealso:: .. seealso::
The complete source code of this example can be found in The complete source code of this example can be found in
:download:`quantum_wire_revisited.py </code/download/quantum_wire_revisited.py>` :jupyter-download:script:`quantum_wire_revisited`
.. jupyter-kernel::
:id: quantum_wire_revisited
Kwant allows for more than one way to build a system. The reason is that Kwant allows for more than one way to build a system. The reason is that
`~kwant.builder.Builder` is essentially just a container that can be filled in `~kwant.builder.Builder` is essentially just a container that can be filled in
...@@ -347,19 +426,51 @@ the code into separate entities. In this example we therefore also aim at more ...@@ -347,19 +426,51 @@ the code into separate entities. In this example we therefore also aim at more
structure. structure.
We begin the program collecting all imports in the beginning of the 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 file and defining the a square lattice and empty scattering region.
`make_system`:
.. jupyter-execute::
:hide-code:
# Tutorial 2.2.3. Building the same system with less code
# =======================================================
#
# Physics background
# ------------------
# Conductance of a quantum wire; subbands
#
# Kwant features highlighted
# --------------------------
# - Using iterables and builder.HoppingKind for making systems
# - introducing `reversed()` for the leads
#
# Note: Does the same as tutorial1a.py, but using other features of Kwant.
.. jupyter-execute::
import kwant
# For plotting
from matplotlib import pyplot
a = 1
t = 1.0
W, L = 10, 30
# 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()
.. literalinclude:: /code/include/quantum_wire_revisited.py
:start-after: #HIDDEN_BEGIN_xkzy
:end-before: #HIDDEN_END_xkzy
Previously, the scattering region was build using two ``for``-loops. Previously, the scattering region was build using two ``for``-loops.
Instead, we now write: Instead, we now write:
.. literalinclude:: /code/include/quantum_wire_revisited.py
:start-after: #HIDDEN_BEGIN_vvjt .. jupyter-execute::
:end-before: #HIDDEN_END_vvjt
syst[(lat(x, y) for x in range(L) for y in range(W))] = 4 * t
Here, all lattice points are added at once in the first line. The 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 construct ``((i, j) for i in range(L) for j in range(W))`` is a
...@@ -375,9 +486,9 @@ hoppings. In this case, an iterable like for the lattice ...@@ -375,9 +486,9 @@ hoppings. In this case, an iterable like for the lattice
points becomes a bit cumbersome, and we use instead another points becomes a bit cumbersome, and we use instead another
feature of Kwant: feature of Kwant:
.. literalinclude:: /code/include/quantum_wire_revisited.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_nooi
:end-before: #HIDDEN_END_nooi syst[lat.neighbors()] = -t
In regular lattices, hoppings form large groups such that hoppings within a 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 group can be transformed into one another by lattice translations. In order to
...@@ -397,9 +508,11 @@ values for all the nth-nearest neighbors at once, one can similarly use ...@@ -397,9 +508,11 @@ values for all the nth-nearest neighbors at once, one can similarly use
The left lead is constructed in an analogous way: The left lead is constructed in an analogous way:
.. literalinclude:: /code/include/quantum_wire_revisited.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_iepx
:end-before: #HIDDEN_END_iepx lead = kwant.Builder(kwant.TranslationalSymmetry((-a, 0)))
lead[(lat(0, j) for j in range(W))] = 4 * t
lead[lat.neighbors()] = -t
The previous example duplicated almost identical code for the left and The previous example duplicated almost identical code for the left and
the right lead. The only difference was the direction of the translational the right lead. The only difference was the direction of the translational
...@@ -408,30 +521,59 @@ symmetry vector. Here, we only construct the left lead, and use the method ...@@ -408,30 +521,59 @@ symmetry vector. Here, we only construct the left lead, and use the method
of a lead pointing in the opposite direction. Both leads are attached as of a lead pointing in the opposite direction. Both leads are attached as
before and the finished system returned: before and the finished system returned:
.. literalinclude:: /code/include/quantum_wire_revisited.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_yxot :hide-output:
:end-before: #HIDDEN_END_yxot
syst.attach_lead(lead)
syst.attach_lead(lead.reversed())
The remainder of the script has been organized into two functions. One for the The remainder of the script has been organized into two functions. One for the
plotting of the conductance. plotting of the conductance.
.. literalinclude:: /code/include/quantum_wire_revisited.py
:start-after: #HIDDEN_BEGIN_ayuk .. jupyter-execute::
:end-before: #HIDDEN_END_ayuk
def plot_conductance(syst, energies):
# Compute conductance
data = []
for energy in energies:
smatrix = kwant.smatrix(syst, energy)
data.append(smatrix.transmission(1, 0))
pyplot.figure()
pyplot.plot(energies, data)
pyplot.xlabel("energy [t]")
pyplot.ylabel("conductance [e^2/h]")
pyplot.show()
And one ``main`` function. And one ``main`` function.
.. literalinclude:: /code/include/quantum_wire_revisited.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_cjel
:end-before: #HIDDEN_END_cjel def main():
# Check that the system looks as intended.
kwant.plot(syst)
# Finalize the system.
fsyst = syst.finalized()
# We should see conductance steps.
plot_conductance(fsyst, energies=[0.01 * i for i in range(100)])
Finally, we use the following standard Python construct [#]_ to execute Finally, we use the following standard Python construct [#]_ to execute
``main`` if the program is used as a script (i.e. executed as ``main`` if the program is used as a script (i.e. executed as
``python quantum_wire_revisited.py``): ``python quantum_wire_revisited.py``):
.. literalinclude:: /code/include/quantum_wire_revisited.py
:start-after: #HIDDEN_BEGIN_ypbj .. jupyter-execute::
:end-before: #HIDDEN_END_ypbj
# Call the main function if the script gets executed (as opposed to imported).
# See <http://docs.python.org/library/__main__.html>.
if __name__ == '__main__':
main()
If the example, however, is imported inside Python using ``import If the example, however, is imported inside Python using ``import
quantum_wire_revisted as qw``, ``main`` is not executed automatically. quantum_wire_revisted as qw``, ``main`` is not executed automatically.
...@@ -453,11 +595,9 @@ The result of the example should be identical to the previous one. ...@@ -453,11 +595,9 @@ The result of the example should be identical to the previous one.
For technical reasons it is not possible to add several points For technical reasons it is not possible to add several points
using a tuple of sites. Hence it is worth noting using a tuple of sites. Hence it is worth noting
a subtle detail in a subtle detail in:
.. literalinclude:: /code/include/quantum_wire_revisited.py >>> syst[(lat(x, y) for x in range(L) for y in range(W))] = 4 * t
:start-after: #HIDDEN_BEGIN_vvjt
:end-before: #HIDDEN_END_vvjt
Note that ``(lat(x, y) for x in range(L) for y in range(W))`` is not Note that ``(lat(x, y) for x in range(L) for y in range(W))`` is not
a tuple, but a generator. a tuple, but a generator.
......
...@@ -5,7 +5,7 @@ Beyond square lattices: graphene ...@@ -5,7 +5,7 @@ Beyond square lattices: graphene
.. seealso:: .. seealso::
The complete source code of this example can be found in The complete source code of this example can be found in
:download:`graphene.py </code/download/graphene.py>` :jupyter-download:script:`graphene`
In the following example, we are going to calculate the In the following example, we are going to calculate the
conductance through a graphene quantum dot with a p-n junction conductance through a graphene quantum dot with a p-n junction
...@@ -18,9 +18,40 @@ We begin by defining the honeycomb lattice of graphene. This is ...@@ -18,9 +18,40 @@ We begin by defining the honeycomb lattice of graphene. This is
in principle already done in `kwant.lattice.honeycomb`, but we do it in principle already done in `kwant.lattice.honeycomb`, but we do it
explicitly here to show how to define a new lattice: explicitly here to show how to define a new lattice:
.. literalinclude:: /code/include/graphene.py .. jupyter-kernel::
:start-after: #HIDDEN_BEGIN_hnla :id: graphene
:end-before: #HIDDEN_END_hnla
.. jupyter-execute::
:hide-code:
# Tutorial 2.5. Beyond square lattices: graphene
# ==============================================
#
# Physics background
# ------------------
# Transport through a graphene quantum dot with a pn-junction
#
# Kwant features highlighted
# --------------------------
# - Application of all the aspects of tutorials 1-3 to a more complicated
# lattice, namely graphene
from math import pi, sqrt, tanh
from matplotlib import pyplot
import kwant
# For computing eigenvalues
import scipy.sparse.linalg as sla
sin_30, cos_30 = (1 / 2, sqrt(3) / 2)
.. jupyter-execute::
graphene = kwant.lattice.general([(1, 0), (sin_30, cos_30)],
[(0, 0), (0, 1 / sqrt(3))])
a, b = graphene.sublattices
The first argument to the `~kwant.lattice.general` function is the list of The first argument to the `~kwant.lattice.general` function is the list of
primitive vectors of the lattice; the second one is the coordinates of basis primitive vectors of the lattice; the second one is the coordinates of basis
...@@ -31,9 +62,30 @@ itself forms a regular lattice of the same type as well, and those ...@@ -31,9 +62,30 @@ itself forms a regular lattice of the same type as well, and those
In the next step we define the shape of the scattering region (circle again) In the next step we define the shape of the scattering region (circle again)
and add all lattice points using the ``shape``-functionality: and add all lattice points using the ``shape``-functionality:
.. literalinclude:: /code/include/graphene.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_shzy :hide-code:
:end-before: #HIDDEN_END_shzy
r = 10
w = 2.0
pot = 0.1
.. jupyter-execute::
#### Define the scattering region. ####
# circular scattering region
def circle(pos):
x, y = pos
return x ** 2 + y ** 2 < r ** 2
syst = kwant.Builder()
# w: width and pot: potential maximum of the p-n junction
def potential(site):
(x, y) = site.pos
d = y * cos_30 + x * sin_30
return pot * tanh(d / w)
syst[graphene.shape(circle, (0, 0))] = potential
As you can see, this works exactly the same for any kind of lattice. 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; We add the onsite energies using a function describing the p-n junction;
...@@ -45,9 +97,11 @@ As a next step we add the hoppings, making use of ...@@ -45,9 +97,11 @@ As a next step we add the hoppings, making use of
`~kwant.builder.HoppingKind`. For illustration purposes we define `~kwant.builder.HoppingKind`. For illustration purposes we define
the hoppings ourselves instead of using ``graphene.neighbors()``: the hoppings ourselves instead of using ``graphene.neighbors()``:
.. literalinclude:: /code/include/graphene.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_hsmc
:end-before: #HIDDEN_END_hsmc # specify the hoppings of the graphene lattice in the
# format expected by builder.HoppingKind
hoppings = (((0, 0), a, b), ((0, 1), a, b), ((-1, 1), a, b))
The nearest-neighbor model for graphene contains only The nearest-neighbor model for graphene contains only
hoppings between different basis atoms. For this type of hoppings between different basis atoms. For this type of
...@@ -63,27 +117,50 @@ respect to the two primitive vectors ``[(1, 0), (sin_30, cos_30)]``. ...@@ -63,27 +117,50 @@ respect to the two primitive vectors ``[(1, 0), (sin_30, cos_30)]``.
Adding the hoppings however still works the same way: Adding the hoppings however still works the same way:
.. literalinclude:: /code/include/graphene.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_bfwb
:end-before: #HIDDEN_END_bfwb syst[[kwant.builder.HoppingKind(*hopping) for hopping in hoppings]] = -1
Modifying the scattering region is also possible as before. Let's Modifying the scattering region is also possible as before. Let's
do something crazy, and remove an atom in sublattice A do something crazy, and remove an atom in sublattice A
(which removes also the hoppings from/to this site) as well (which removes also the hoppings from/to this site) as well
as add an additional link: as add an additional link:
.. literalinclude:: /code/include/graphene.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_efut
:end-before: #HIDDEN_END_efut # Modify the scattering region
del syst[a(0, 0)]
syst[a(-2, 1), b(2, 2)] = -1
Note again that the conversion from a tuple `(i,j)` to site Note again that the conversion from a tuple `(i,j)` to site
is done by the sublattices `a` and `b`. is done by the sublattices `a` and `b`.
The leads are defined almost as before: The leads are defined almost as before:
.. literalinclude:: /code/include/graphene.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_aakh
:end-before: #HIDDEN_END_aakh #### Define the leads. ####
# 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
# The second lead, going to the top right
sym1 = kwant.TranslationalSymmetry(graphene.vec((0, 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 `~kwant.lattice.Polyatomic.vec` used in calculating the Note the method `~kwant.lattice.Polyatomic.vec` used in calculating the
parameter for `~kwant.lattice.TranslationalSymmetry`. The latter expects a parameter for `~kwant.lattice.TranslationalSymmetry`. The latter expects a
...@@ -98,28 +175,63 @@ in a square lattice -- they follow the non-orthogonal primitive vectors defined ...@@ -98,28 +175,63 @@ in a square lattice -- they follow the non-orthogonal primitive vectors defined
in the beginning. in the beginning.
Later, we will compute some eigenvalues of the closed scattering region without 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, leads. This is why we postpone attaching the leads to the system.
we return the scattering region and the leads separately.
.. literalinclude:: /code/include/graphene.py
:start-after: #HIDDEN_BEGIN_kmmw
:end-before: #HIDDEN_END_kmmw
The computation of some eigenvalues of the closed system is done The computation of some eigenvalues of the closed system is done
in the following piece of code: in the following piece of code:
.. literalinclude:: /code/include/graphene.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_zydk
:end-before: #HIDDEN_END_zydk def compute_evs(syst):
# Compute some eigenvalues of the closed system
sparse_mat = syst.hamiltonian_submatrix(sparse=True)
evs = sla.eigs(sparse_mat, 2)[0]
print(evs.real)
The code for computing the band structure and the conductance is identical The code for computing the band structure and the conductance is identical
to the previous examples, and needs not be further explained here. to the previous examples, and needs not be further explained here.
Finally, in the `main` function we make and plot the system: Finally, we plot the system:
.. jupyter-execute::
:hide-code:
def plot_conductance(syst, energies):
# Compute transmission as a function of energy
data = []
for energy in energies:
smatrix = kwant.smatrix(syst, energy)
data.append(smatrix.transmission(0, 1))
pyplot.figure()
pyplot.plot(energies, data)
pyplot.xlabel("energy [t]")
pyplot.ylabel("conductance [e^2/h]")
pyplot.show()
.. literalinclude:: /code/include/graphene.py def plot_bandstructure(flead, momenta):
:start-after: #HIDDEN_BEGIN_itkk bands = kwant.physics.Bands(flead)
:end-before: #HIDDEN_END_itkk energies = [bands(k) for k in momenta]
pyplot.figure()
pyplot.plot(momenta, energies)
pyplot.xlabel("momentum [(lattice constant)^-1]")
pyplot.ylabel("energy [t]")
pyplot.show()
.. jupyter-execute::
# To highlight the two sublattices of graphene, we plot one with
# a filled, and the other one with an open circle:
def family_colors(site):
return 0 if site.family == a else 1
# Plot the closed system without leads.
kwant.plot(syst, site_color=family_colors, site_lw=0.1, colorbar=False);
We customize the plotting: we set the `site_colors` argument of We customize the plotting: we set the `site_colors` argument of
`~kwant.plotter.plot` to a function which returns 0 for `~kwant.plotter.plot` to a function which returns 0 for
...@@ -132,27 +244,43 @@ The function `~kwant.plotter.plot` shows these values using a color scale ...@@ -132,27 +244,43 @@ The function `~kwant.plotter.plot` shows these values using a color scale
(grayscale by default). The symbol `size` is specified in points, and is (grayscale by default). The symbol `size` is specified in points, and is
independent on the overall figure size. independent on the overall figure size.
Plotting the closed system gives this result:
.. image:: /code/figure/graphene_syst1.*
Computing the eigenvalues of largest magnitude, Computing the eigenvalues of largest magnitude,
.. literalinclude:: /code/include/graphene.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_jmbi
:end-before: #HIDDEN_END_jmbi compute_evs(syst.finalized())
should yield two eigenvalues equal to ``[ 3.07869311, yields two eigenvalues equal to ``[ 3.07869311,
-3.06233144]``. -3.06233144]``.
The remaining code of `main` attaches the leads to the system and plots it The remaining code attaches the leads to the system and plots it
again: again:
.. image:: /code/figure/graphene_syst2.* .. jupyter-execute::
# Attach the leads to the system.
for lead in [lead0, lead1]:
syst.attach_lead(lead)
# Then, plot the system with leads.
kwant.plot(syst, site_color=family_colors, site_lw=0.1,
lead_site_lw=0, colorbar=False);
We then finalize the system:
It computes the band structure of one of lead 0: .. jupyter-execute::
.. image:: /code/figure/graphene_bs.* syst = syst.finalized()
and compute the band structure of one of lead 0:
.. jupyter-execute::
# Compute the band structure of lead 0.
momenta = [-pi + 0.02 * pi * i for i in range(101)]
plot_bandstructure(syst.leads[0], momenta)
showing all the features of a zigzag lead, including the flat showing all the features of a zigzag lead, including the flat
edge state bands (note that the band structure is not symmetric around edge state bands (note that the band structure is not symmetric around
...@@ -160,7 +288,11 @@ zero energy, due to a potential in the leads). ...@@ -160,7 +288,11 @@ zero energy, due to a potential in the leads).
Finally the transmission through the system is computed, Finally the transmission through the system is computed,
.. image:: /code/figure/graphene_result.* .. jupyter-execute::
# Plot conductance.
energies = [-2 * pot + 4. / 50. * pot * i for i in range(51)]
plot_conductance(syst, energies)
showing the typical resonance-like transmission probability through showing the typical resonance-like transmission probability through
an open quantum dot an open quantum dot
......
...@@ -13,8 +13,43 @@ texture. ...@@ -13,8 +13,43 @@ texture.
.. seealso:: .. seealso::
The complete source code of this example can be found in The complete source code of this example can be found in
:download:`magnetic_texture.py </code/download/magnetic_texture.py>` :jupyter-download:script:`magnetic_texture`
.. jupyter-kernel::
:id: magnetic_texture
.. jupyter-execute::
:hide-code:
# Tutorial 2.7. Spin textures
# ===========================
#
# Physics background
# ------------------
# - Spin textures
# - Skyrmions
#
# Kwant features highlighted
# --------------------------
# - operators
# - plotting vector fields
from math import sin, cos, tanh, pi
import itertools
import numpy as np
import tinyarray as ta
import matplotlib.pyplot as plt
import kwant
sigma_0 = ta.array([[1, 0], [0, 1]])
sigma_x = ta.array([[0, 1], [1, 0]])
sigma_y = ta.array([[0, -1j], [1j, 0]])
sigma_z = ta.array([[1, 0], [0, -1]])
# vector of Pauli matrices σ_αiβ where greek
# letters denote spinor indices
sigma = np.rollaxis(np.array([sigma_x, sigma_y, sigma_z]), 1)
Introduction Introduction
------------ ------------
...@@ -47,28 +82,119 @@ of site :math:`i`, and :math:`r_i = \sqrt{x_i^2 + y_i^2}`. ...@@ -47,28 +82,119 @@ of site :math:`i`, and :math:`r_i = \sqrt{x_i^2 + y_i^2}`.
To define this model in Kwant we start as usual by defining functions that To define this model in Kwant we start as usual by defining functions that
depend on the model parameters: depend on the model parameters:
.. literalinclude:: /code/include/magnetic_texture.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_model
:end-before: #HIDDEN_END_model def field_direction(pos, r0, delta):
x, y = pos
r = np.linalg.norm(pos)
r_tilde = (r - r0) / delta
theta = (tanh(r_tilde) - 1) * (pi / 2)
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)
def scattering_onsite(site, r0, delta, J):
m_i = field_direction(site.pos, r0, delta)
return J * np.dot(m_i, sigma)
def lead_onsite(site, J):
return J * sigma_z
and define our system as a square shape on a square lattice with two orbitals 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: per site, with leads attached on the left and right:
.. literalinclude:: /code/include/magnetic_texture.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_syst
:end-before: #HIDDEN_END_syst 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[lat.shape(square, (0, 0))] = scattering_onsite
syst[lat.neighbors()] = -sigma_0
lead = kwant.Builder(kwant.TranslationalSymmetry((-1, 0)),
conservation_law=-sigma_z)
lead[lat.shape(square, (0, 0))] = lead_onsite
lead[lat.neighbors()] = -sigma_0
syst.attach_lead(lead)
syst.attach_lead(lead.reversed())
return syst
Below is a plot of a projection of :math:`\mathbf{m}_i` onto the x-y plane Below is a plot of a projection of :math:`\mathbf{m}_i` onto the x-y plane
inside the scattering region. The z component is shown by the color scale: inside the scattering region. The z component is shown by the color scale:
.. image:: /code/figure/mag_field_direction.* .. jupyter-execute::
:hide-code:
def plot_vector_field(syst, params):
xmin, ymin = min(s.tag for s in syst.sites)
xmax, ymax = max(s.tag for s in syst.sites)
x, y = np.meshgrid(np.arange(xmin, xmax+1), np.arange(ymin, ymax+1))
m_i = [field_direction(p, **params) for p in zip(x.flat, y.flat)]
m_i = np.reshape(m_i, x.shape + (3,))
m_i = np.rollaxis(m_i, 2, 0)
fig, ax = plt.subplots(1, 1)
im = ax.quiver(x, y, *m_i, pivot='mid', scale=75)
fig.colorbar(im)
plt.show()
def plot_densities(syst, densities):
fig, axes = plt.subplots(1, len(densities), figsize=(13, 10))
for ax, (title, rho) in zip(axes, densities):
kwant.plotter.map(syst, rho, ax=ax, a=4)
ax.set_title(title)
plt.show()
def plot_currents(syst, currents):
fig, axes = plt.subplots(1, len(currents), figsize=(13, 10))
if not hasattr(axes, '__len__'):
axes = (axes,)
for ax, (title, current) in zip(axes, currents):
kwant.plotter.current(syst, current, ax=ax, colorbar=False,
fig_size=(13, 10))
ax.set_title(title)
plt.show()
.. jupyter-execute::
:hide-code:
syst = make_system().finalized()
.. jupyter-execute::
:hide-code:
plot_vector_field(syst, dict(r0=20, delta=10))
We will now be interested in analyzing the form of the scattering states We will now be interested in analyzing the form of the scattering states
that originate from the left lead: that originate from the left lead:
.. literalinclude:: /code/include/magnetic_texture.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_wavefunction
:end-before: #HIDDEN_END_wavefunction params = dict(r0=20, delta=10, J=1)
wf = kwant.wave_function(syst, energy=-1, params=params)
psi = wf(0)[0]
Local densities Local densities
--------------- ---------------
...@@ -82,34 +208,45 @@ When there are multiple degrees of freedom per site, however, one has to be ...@@ -82,34 +208,45 @@ 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 more careful. In the present case with two (spin) degrees of freedom per site
one could calculate the per-site density like: one could calculate the per-site density like:
.. literalinclude:: /code/include/magnetic_texture.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_ldos
:end-before: #HIDDEN_END_ldos # even (odd) indices correspond to spin up (down)
up, down = psi[::2], psi[1::2]
density = np.abs(up)**2 + np.abs(down)**2
With more than one degree of freedom per site we have more freedom as to what 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 local quantities we can meaningfully compute. For example, we may wish to
calculate the local z-projected spin density. We could calculate calculate the local z-projected spin density. We could calculate
this in the following way: this in the following way:
.. literalinclude:: /code/include/magnetic_texture.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_lsdz
:end-before: #HIDDEN_END_lsdz # spin down components have a minus sign
spin_z = np.abs(up)**2 - np.abs(down)**2
If we wanted instead to calculate the local y-projected spin density, we would If we wanted instead to calculate the local y-projected spin density, we would
need to use an even more complicated expression: need to use an even more complicated expression:
.. literalinclude:: /code/include/magnetic_texture.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_lsdy
:end-before: #HIDDEN_END_lsdy # spin down components have a minus sign
spin_y = 1j * (down.conjugate() * up - up.conjugate() * down)
The `kwant.operator` module aims to alleviate somewhat this tedious The `kwant.operator` module aims to alleviate somewhat this tedious
book-keeping by providing a simple interface for defining operators that act on book-keeping by providing a simple interface for defining operators that act on
wavefunctions. To calculate the above quantities we would use the wavefunctions. To calculate the above quantities we would use the
`~kwant.operator.Density` operator like so: `~kwant.operator.Density` operator like so:
.. literalinclude:: /code/include/magnetic_texture.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_lden
:end-before: #HIDDEN_END_lden rho = kwant.operator.Density(syst)
rho_sz = kwant.operator.Density(syst, sigma_z)
rho_sy = kwant.operator.Density(syst, sigma_y)
# calculate the expectation values of the operators with 'psi'
density = rho(psi)
spin_z = rho_sz(psi)
spin_y = rho_sy(psi)
`~kwant.operator.Density` takes a `~kwant.system.System` as its first parameter `~kwant.operator.Density` takes a `~kwant.system.System` as its first parameter
as well as (optionally) a square matrix that defines the quantity that you wish as well as (optionally) a square matrix that defines the quantity that you wish
...@@ -126,8 +263,14 @@ Below we can see colorplots of the above-calculated quantities. The array that ...@@ -126,8 +263,14 @@ Below we can see colorplots of the above-calculated quantities. The array that
is returned by evaluating a `~kwant.operator.Density` can be used directly with is returned by evaluating a `~kwant.operator.Density` can be used directly with
`kwant.plotter.density`: `kwant.plotter.density`:
.. image:: /code/figure/spin_densities.* .. jupyter-execute::
:hide-code:
plot_densities(syst, [
('$σ_0$', density),
('$σ_z$', spin_z),
('$σ_y$', spin_y),
])
.. specialnote:: Technical Details .. specialnote:: Technical Details
...@@ -180,14 +323,27 @@ where :math:`\mathbf{H}_{ab}` is the hopping matrix from site :math:`b` to site ...@@ -180,14 +323,27 @@ where :math:`\mathbf{H}_{ab}` is the hopping matrix from site :math:`b` to site
:math:`a`. For example, to calculate the local current and :math:`a`. For example, to calculate the local current and
spin current: spin current:
.. literalinclude:: /code/include/magnetic_texture.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_current
:end-before: #HIDDEN_END_current J_0 = kwant.operator.Current(syst)
J_z = kwant.operator.Current(syst, sigma_z)
J_y = kwant.operator.Current(syst, sigma_y)
# calculate the expectation values of the operators with 'psi'
current = J_0(psi)
spin_z_current = J_z(psi)
spin_y_current = J_y(psi)
Evaluating a `~kwant.operator.Current` operator on a wavefunction returns a Evaluating a `~kwant.operator.Current` operator on a wavefunction returns a
1D array of values that can be directly used with `kwant.plotter.current`: 1D array of values that can be directly used with `kwant.plotter.current`:
.. image:: /code/figure/spin_currents.* .. jupyter-execute::
plot_currents(syst, [
('$J_{σ_0}$', current),
('$J_{σ_z}$', spin_z_current),
('$J_{σ_y}$', spin_y_current),
])
.. note:: .. note::
...@@ -262,9 +418,16 @@ scattering region. ...@@ -262,9 +418,16 @@ scattering region.
Doing this is as simple as passing a *function* when instantiating Doing this is as simple as passing a *function* when instantiating
the `~kwant.operator.Current`, instead of a constant matrix: the `~kwant.operator.Current`, instead of a constant matrix:
.. literalinclude:: /code/include/magnetic_texture.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_following
:end-before: #HIDDEN_END_following def following_m_i(site, r0, delta):
m_i = field_direction(site.pos, r0, delta)
return np.dot(m_i, sigma)
J_m = kwant.operator.Current(syst, following_m_i)
# evaluate the operator
m_current = J_m(psi, params=dict(r0=25, delta=10))
The function must take a `~kwant.builder.Site` as its first parameter, The function must take a `~kwant.builder.Site` as its first parameter,
and may optionally take other parameters (i.e. it must have the same and may optionally take other parameters (i.e. it must have the same
...@@ -274,7 +437,7 @@ matrix that defines the operator we wish to calculate. ...@@ -274,7 +437,7 @@ matrix that defines the operator we wish to calculate.
.. note:: .. note::
In the above example we had to pass the extra parameters needed by the In the above example we had to pass the extra parameters needed by the
``following_operator`` function via the ``param`` keyword argument. In ``following_operator`` function via the ``params`` keyword argument. In
general you must pass all the parameters needed by the Hamiltonian via general you must pass all the parameters needed by the Hamiltonian via
``params`` (as you would when calling `~kwant.solvers.default.smatrix` or ``params`` (as you would when calling `~kwant.solvers.default.smatrix` or
`~kwant.solvers.default.wave_function`). In the previous examples, `~kwant.solvers.default.wave_function`). In the previous examples,
...@@ -286,7 +449,13 @@ Using this we can see that the spin current is essentially oriented along ...@@ -286,7 +449,13 @@ Using this we can see that the spin current is essentially oriented along
the direction of :math:`m_i` in the present regime where the onsite term the direction of :math:`m_i` in the present regime where the onsite term
in the Hamiltonian is dominant: in the Hamiltonian is dominant:
.. image:: /code/figure/spin_current_comparison.* .. jupyter-execute::
:hide-code:
plot_currents(syst, [
(r'$J_{\mathbf{m}_i}$', m_current),
('$J_{σ_z}$', spin_z_current),
])
.. note:: Although this example used exclusively `~kwant.operator.Current`, .. note:: Although this example used exclusively `~kwant.operator.Current`,
you can do the same with `~kwant.operator.Density`. you can do the same with `~kwant.operator.Density`.
...@@ -308,11 +477,16 @@ Density of states in a circle ...@@ -308,11 +477,16 @@ Density of states in a circle
To calculate the density of states inside a circle of radius To calculate the density of states inside a circle of radius
20 we can simply do: 20 we can simply do:
.. literalinclude:: /code/include/magnetic_texture.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_density_cut
:end-before: #HIDDEN_END_density_cut def circle(site):
return np.linalg.norm(site.pos) < 20
.. literalinclude:: /code/figure/circle_dos.txt rho_circle = kwant.operator.Density(syst, where=circle, sum=True)
all_states = np.vstack((wf(0), wf(1)))
dos_in_circle = sum(rho_circle(p) for p in all_states) / (2 * pi)
print('density of states in circle:', dos_in_circle)
note that we also provide ``sum=True``, which means that evaluating the note that we also provide ``sum=True``, which means that evaluating the
operator on a wavefunction will produce a single scalar. This is semantically operator on a wavefunction will produce a single scalar. This is semantically
...@@ -325,11 +499,22 @@ Current flowing through a cut ...@@ -325,11 +499,22 @@ Current flowing through a cut
Below we calculate the probability current and z-projected spin current near Below we calculate the probability current and z-projected spin current near
the interfaces with the left and right leads. the interfaces with the left and right leads.
.. literalinclude:: /code/include/magnetic_texture.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_current_cut
:end-before: #HIDDEN_END_current_cut def left_cut(site_to, site_from):
return site_from.pos[0] <= -39 and site_to.pos[0] > -39
def right_cut(site_to, site_from):
return site_from.pos[0] < 39 and site_to.pos[0] >= 39
.. literalinclude:: /code/figure/current_cut.txt J_left = kwant.operator.Current(syst, where=left_cut, sum=True)
J_right = kwant.operator.Current(syst, where=right_cut, sum=True)
Jz_left = kwant.operator.Current(syst, sigma_z, where=left_cut, sum=True)
Jz_right = kwant.operator.Current(syst, sigma_z, where=right_cut, sum=True)
print('J_left:', J_left(psi), ' J_right:', J_right(psi))
print('Jz_left:', Jz_left(psi), ' Jz_right:', Jz_right(psi))
We see that the probability current is conserved across the scattering region, 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 but the z-projected spin current is not due to the fact that the Hamiltonian
...@@ -360,8 +545,23 @@ This can be achieved with `~kwant.operator.Current.bind`: ...@@ -360,8 +545,23 @@ This can be achieved with `~kwant.operator.Current.bind`:
*different* set of parameters. This will almost certainly give *different* set of parameters. This will almost certainly give
incorrect results. incorrect results.
.. literalinclude:: /code/include/magnetic_texture.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_bind
:end-before: #HIDDEN_END_bind J_m = kwant.operator.Current(syst, following_m_i)
J_z = kwant.operator.Current(syst, sigma_z)
J_m_bound = J_m.bind(params=dict(r0=25, delta=10, J=1))
J_z_bound = J_z.bind(params=dict(r0=25, delta=10, J=1))
# 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)
.. jupyter-execute::
:hide-code:
.. image:: /code/figure/bound_current.* plot_currents(syst, [
(r'$J_{\mathbf{m}_i}$ (from left)', J_m_left),
(r'$J_{σ_z}$ (from left)', J_z_left),
])
...@@ -12,15 +12,52 @@ these options can be used to achieve various very different objectives. ...@@ -12,15 +12,52 @@ these options can be used to achieve various very different objectives.
.. seealso:: .. seealso::
The complete source code of this example can be found in The complete source code of this example can be found in
:download:`plot_graphene.py </code/download/plot_graphene.py>` :jupyter-download:script:`plot_graphene`
.. jupyter-kernel::
:id: plot_graphene
.. jupyter-execute::
:hide-code:
# Tutorial 2.8.1. 2D example: graphene quantum dot
# ================================================
#
# Physics background
# ------------------
# - graphene edge states
#
# Kwant features highlighted
# --------------------------
# - demonstrate different ways of plotting
from matplotlib import pyplot
import kwant
We begin by first considering a circular graphene quantum dot (similar to what 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 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: to previous examples, we will also use hoppings beyond next-nearest neighbors:
.. literalinclude:: /code/include/plot_graphene.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_makesyst
:end-before: #HIDDEN_END_makesyst lat = kwant.lattice.honeycomb()
a, b = lat.sublattices
def make_system(r=8, t=-1, tp=-0.1):
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 Note that adding hoppings hoppings to the `n`-th nearest neighbors can be
simply done by passing `n` as an argument to simply done by passing `n` as an argument to
...@@ -29,16 +66,14 @@ simply done by passing `n` as an argument to ...@@ -29,16 +66,14 @@ simply done by passing `n` as an argument to
out of the shape. It is necessary to do so *before* adding the out of the shape. It is necessary to do so *before* adding the
next-nearest-neighbor hopping [#]_. next-nearest-neighbor hopping [#]_.
Of course, the system can be plotted simply with default settings: Of course, the system can be plotted simply with default settings,
however, due to the richer structure of the lattice, this results in a rather
.. literalinclude:: /code/include/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: busy plot:
.. image:: /code/figure/plot_graphene_syst1.* .. jupyter-execute::
syst = make_system()
kwant.plot(syst);
A much clearer plot can be obtained by using different colors for both A much clearer plot can be obtained by using different colors for both
sublattices, and by having different line widths for different hoppings. This sublattices, and by having different line widths for different hoppings. This
...@@ -47,15 +82,19 @@ can be achieved by passing a function to the arguments of ...@@ -47,15 +82,19 @@ can be achieved by passing a function to the arguments of
must be a function taking one site as argument, for hoppings a function taking must be a function taking one site as argument, for hoppings a function taking
the start end end site of hopping as arguments: the start end end site of hopping as arguments:
.. literalinclude:: /code/include/plot_graphene.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_plotsyst2
:end-before: #HIDDEN_END_plotsyst2 def family_color(site):
return 'black' if site.family == a else 'white'
def hopping_lw(site1, site2):
return 0.04 if site1.family == site2.family else 0.1
kwant.plot(syst, site_lw=0.1, site_color=family_color, hop_lw=hopping_lw);
Note that since we are using an unfinalized Builder, a ``site`` is really an 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 instance of `~kwant.builder.Site`. With these adjustments we arrive at a plot
that carries the same information, but is much easier to interpret: that carries the same information, but is much easier to interpret.
.. image:: /code/figure/plot_graphene_syst2.*
Apart from plotting the *system* itself, `~kwant.plotter.plot` can also be used Apart from plotting the *system* itself, `~kwant.plotter.plot` can also be used
to plot *data* living on the system. to plot *data* living on the system.
...@@ -67,23 +106,27 @@ nearest-neighbor hopping. Computing the wave functions is done in the usual ...@@ -67,23 +106,27 @@ 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 way (note that for a large-scale system, one would probably want to use sparse
linear algebra): linear algebra):
.. literalinclude:: /code/include/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 .. jupyter-execute::
`~kwant.plotter.plot`, but rather `~kwant.plotter.map`. Here, we plot the
`n`-th wave function using it: import scipy.linalg as la
syst = make_system(tp=0).finalized()
ham = syst.hamiltonian_submatrix()
evecs = la.eigh(ham)[1]
.. literalinclude:: /code/include/plot_graphene.py wf = abs(evecs[:, 225])**2
:start-after: #HIDDEN_BEGIN_plotdata2
:end-before: #HIDDEN_END_plotdata2
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.
This results in a standard pseudocolor plot, showing in this case (``n=225``) a 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 graphene edge state, i.e. a wave function mostly localized at the zigzag edges
of the quantum dot. of the quantum dot.
.. image:: /code/figure/plot_graphene_data1.* .. jupyter-execute::
kwant.plotter.map(syst, wf, oversampling=10, cmap='gist_heat_r');
However although in general preferable, `~kwant.plotter.map` has a few However although in general preferable, `~kwant.plotter.map` has a few
deficiencies for this small system: For example, there are a few distortions at deficiencies for this small system: For example, there are a few distortions at
...@@ -99,9 +142,17 @@ the symbol shape depending on the sublattice. With a triangle pointing up and ...@@ -99,9 +142,17 @@ 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 down on the respective sublattice, the symbols used by plot fill the space
completely: completely:
.. literalinclude:: /code/include/plot_graphene.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_plotdata3
:end-before: #HIDDEN_END_plotdata3 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'
kwant.plot(syst, site_color=wf, site_symbol=family_shape,
site_size=0.5, hop_lw=0, cmap='gist_heat_r');
Note that with ``hop_lw=0`` we deactivate plotting the hoppings (that would not 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 serve any purpose here). Moreover, ``site_size=0.5`` guarantees that the two
...@@ -114,31 +165,23 @@ Finally, note that since we are dealing with a finalized system now, a site `i` ...@@ -114,31 +165,23 @@ 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 is represented by an integer. In order to obtain the original
`~kwant.builder.Site`, ``syst.sites[i]`` can be used. `~kwant.builder.Site`, ``syst.sites[i]`` can be used.
With this we arrive at
.. image:: /code/figure/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 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 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 beyond pseudocolor-like plots. For example, we can represent the wave function
probability using the symbols itself: probability using the symbols itself:
.. literalinclude:: /code/include/plot_graphene.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_plotdata4
:end-before: #HIDDEN_END_plotdata4 def site_size(i):
return 3 * wf[i] / wf.max()
kwant.plot(syst, site_size=site_size, site_color=(0, 0, 1, 0.3),
hop_lw=0.1);
Here, we choose the symbol size proportional to the wave function probability, 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 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. visible. The hoppings are also plotted in order to show the underlying lattice.
With this, we arrive at
.. image:: /code/figure/plot_graphene_data3.*
which shows the edge state nature of the wave function most clearly.
.. rubric:: Footnotes .. rubric:: Footnotes
.. [#] A dangling site is defined as having only one hopping connecting it to .. [#] A dangling site is defined as having only one hopping connecting it to
...@@ -150,7 +193,28 @@ which shows the edge state nature of the wave function most clearly. ...@@ -150,7 +193,28 @@ which shows the edge state nature of the wave function most clearly.
.. seealso:: .. seealso::
The complete source code of this example can be found in The complete source code of this example can be found in
:download:`plot_zincblende.py </code/download/plot_zincblende.py>` :jupyter-download:script:`plot_zincblende`
.. jupyter-kernel::
:id: plot_zincblende
.. jupyter-execute::
:hide-code:
# Tutorial 2.8.2. 3D example: zincblende structure
# ================================================
#
# Physical background
# -------------------
# - 3D Bravais lattices
#
# Kwant features highlighted
# --------------------------
# - demonstrate different ways of plotting in 3D
from matplotlib import pyplot
import kwant
Zincblende is a very common crystal structure of semiconductors. It is a Zincblende is a very common crystal structure of semiconductors. It is a
face-centered cubic crystal with two inequivalent atoms in the unit cell face-centered cubic crystal with two inequivalent atoms in the unit cell
...@@ -159,18 +223,29 @@ structure, but two equivalent atoms per unit cell). ...@@ -159,18 +223,29 @@ structure, but two equivalent atoms per unit cell).
It is very easily generated in Kwant with `kwant.lattice.general`: It is very easily generated in Kwant with `kwant.lattice.general`:
.. literalinclude:: /code/include/plot_zincblende.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_zincblende1
:end-before: #HIDDEN_END_zincblende1 lat = kwant.lattice.general([(0, 0.5, 0.5), (0.5, 0, 0.5), (0.5, 0.5, 0)],
[(0, 0, 0), (0.25, 0.25, 0.25)])
a, b = lat.sublattices
Note how we keep references to the two different sublattices for later use. 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 A three-dimensional structure is created as easily as in two dimensions, by
using the `~kwant.lattice.PolyatomicLattice.shape`-functionality: using the `~kwant.lattice.PolyatomicLattice.shape`-functionality:
.. literalinclude:: /code/include/plot_zincblende.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_zincblende2
:end-before: #HIDDEN_END_zincblende2 def make_cuboid(a=15, b=10, c=5):
def cuboid_shape(pos):
x, y, z = pos
return 0 <= x < a and 0 <= y < b and 0 <= z < c
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 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 values for onsite and hopping energies, but only the placeholder ``None`` (in a
...@@ -179,13 +254,11 @@ real calculation, several atomic orbitals would have to be considered). ...@@ -179,13 +254,11 @@ real calculation, several atomic orbitals would have to be considered).
`~kwant.plotter.plot` can plot 3D systems just as easily as its two-dimensional `~kwant.plotter.plot` can plot 3D systems just as easily as its two-dimensional
counterparts: counterparts:
.. literalinclude:: /code/include/plot_zincblende.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_plot1
:end-before: #HIDDEN_END_plot1
resulting in syst = make_cuboid()
.. image:: /code/figure/plot_zincblende_syst1.* kwant.plot(syst);
You might notice that the standard options for plotting are quite different in 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 3D than in 2D. For example, by default hoppings are not printed, but sites are
...@@ -207,14 +280,18 @@ Also for 3D it is possible to customize the plot. For example, we ...@@ -207,14 +280,18 @@ Also for 3D it is possible to customize the plot. For example, we
can explicitly plot the hoppings as lines, and color sites differently can explicitly plot the hoppings as lines, and color sites differently
depending on the sublattice: depending on the sublattice:
.. literalinclude:: /code/include/plot_zincblende.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_plot2
:end-before: #HIDDEN_END_plot2
which results in a 3D plot that allows to interactively (when plotted syst = make_cuboid(a=1.5, b=1.5, c=1.5)
in a window) explore the crystal structure:
.. image:: /code/figure/plot_zincblende_syst2.* def family_colors(site):
return 'r' if site.family == a else 'g'
kwant.plot(syst, site_size=0.18, site_lw=0.01, hop_lw=0.05,
site_color=family_colors);
which results in a 3D plot that allows to interactively (when plotted
in a window) explore the crystal structure.
Hence, a few lines of code using Kwant allow to explore all the different Hence, a few lines of code using Kwant allow to explore all the different
crystal lattices out there! crystal lattices out there!
......
...@@ -6,7 +6,29 @@ Band structure calculations ...@@ -6,7 +6,29 @@ Band structure calculations
.. seealso:: .. seealso::
The complete source code of this example can be found in The complete source code of this example can be found in
:download:`band_structure.py </code/download/band_structure.py>` :jupyter-download:script:`band_structure`
.. jupyter-kernel::
:id: band_structure
.. jupyter-execute::
:hide-code:
# Tutorial 2.4.1. Band structure calculations
# ===========================================
#
# Physics background
# ------------------
# band structure of a simple quantum wire in tight-binding approximation
#
# Kwant features highlighted
# --------------------------
# - Computing the band structure of a finalized lead.
import kwant
# For plotting
from matplotlib import pyplot
When doing transport simulations, one also often needs to know the band 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 structure of the leads, i.e. the energies of the propagating plane waves in the
...@@ -19,9 +41,26 @@ tight-binding wire. ...@@ -19,9 +41,26 @@ tight-binding wire.
Computing band structures in Kwant is easy. Just define a lead in the Computing band structures in Kwant is easy. Just define a lead in the
usual way: usual way:
.. literalinclude:: /code/include/band_structure.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_zxip
:end-before: #HIDDEN_END_zxip def make_lead(a=1, t=1.0, W=10):
# Start with an empty lead with a single square lattice
lat = kwant.lattice.square(a)
sym_lead = kwant.TranslationalSymmetry((-a, 0))
lead = kwant.Builder(sym_lead)
# 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
lead[lat(1, j), lat(0, j)] = -t
return lead
"Usual way" means defining a translational symmetry vector, as well "Usual way" means defining a translational symmetry vector, as well
as one unit cell of the lead, and the hoppings to neighboring as one unit cell of the lead, and the hoppings to neighboring
...@@ -42,13 +81,24 @@ do something more profound with the dispersion relation these energies may be ...@@ -42,13 +81,24 @@ do something more profound with the dispersion relation these energies may be
calculated directly using `~kwant.physics.Bands`. For now we just plot the calculated directly using `~kwant.physics.Bands`. For now we just plot the
bandstructure: bandstructure:
.. literalinclude:: /code/include/band_structure.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_pejz
:end-before: #HIDDEN_END_pejz def main():
lead = make_lead().finalized()
kwant.plotter.bands(lead, show=False)
pyplot.xlabel("momentum [(lattice constant)^-1]")
pyplot.ylabel("energy [t]")
pyplot.show()
This gives the result: This gives the result:
.. image:: /code/figure/band_structure_result.* .. jupyter-execute::
:hide-code:
# Call the main function if the script gets executed (as opposed to imported).
# See <http://docs.python.org/library/__main__.html>.
if __name__ == '__main__':
main()
where we observe the cosine-like dispersion of the square lattice. Close 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 to ``k=0`` this agrees well with the quadratic dispersion this tight-binding
...@@ -61,7 +111,31 @@ Closed systems ...@@ -61,7 +111,31 @@ Closed systems
.. seealso:: .. seealso::
The complete source code of this example can be found in The complete source code of this example can be found in
:download:`closed_system.py </code/download/closed_system.py>` :jupyter-download:script:`closed_system`
.. jupyter-kernel::
:id: closed_system
.. jupyter-execute::
:hide-code:
# Tutorial 2.4.2. Closed systems
# ==============================
#
# Physics background
# ------------------
# Fock-darwin spectrum of a quantum dot (energy spectrum in
# as a function of a magnetic field)
#
# Kwant features highlighted
# --------------------------
# - Use of `hamiltonian_submatrix` in order to obtain a Hamiltonian
# matrix.
from cmath import exp
import numpy as np
from matplotlib import pyplot
import kwant
Although Kwant is (currently) mainly aimed towards transport problems, it Although Kwant is (currently) mainly aimed towards transport problems, it
can also easily be used to compute properties of closed systems -- after can also easily be used to compute properties of closed systems -- after
...@@ -74,16 +148,49 @@ To compute the eigenenergies and eigenstates, we will make use of the sparse ...@@ -74,16 +148,49 @@ To compute the eigenenergies and eigenstates, we will make use of the sparse
linear algebra functionality of `SciPy <https://www.scipy.org>`_, which linear algebra functionality of `SciPy <https://www.scipy.org>`_, which
interfaces the ARPACK package: interfaces the ARPACK package:
.. literalinclude:: /code/include/closed_system.py
:start-after: #HIDDEN_BEGIN_tibv .. jupyter-execute::
:end-before: #HIDDEN_END_tibv
# For eigenvalue computation
import scipy.sparse.linalg as sla
We set up the system using the `shape`-function as in We set up the system using the `shape`-function as in
:ref:`tutorial-abring`, but do not add any leads: :ref:`tutorial-abring`, but do not add any leads:
.. literalinclude:: /code/include/closed_system.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_qlyd :hide-code:
:end-before: #HIDDEN_END_qlyd
a = 1
t = 1.0
r = 10
.. jupyter-execute::
def make_system(a=1, t=1.0, r=10):
lat = kwant.lattice.square(a, norbs=1)
syst = kwant.Builder()
# Define the quantum dot
def circle(pos):
(x, y) = pos
rsq = x ** 2 + y ** 2
return rsq < r ** 2
def hopx(site1, site2, B):
# The magnetic field is controlled by the parameter B
y = site1.pos[1]
return -t * exp(-1j * B * y)
syst[lat.shape(circle, (0, 0))] = 4 * t
# hoppings in x-direction
syst[kwant.builder.HoppingKind((1, 0), lat, lat)] = hopx
# hoppings in y-directions
syst[kwant.builder.HoppingKind((0, 1), lat, lat)] = -t
# It's a closed system for a change, so no leads
return syst
We add the magnetic field using a function and a global variable as we 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 did in the two previous tutorial. (Here, the gauge is chosen such that
...@@ -93,14 +200,40 @@ The spectrum can be obtained by diagonalizing the Hamiltonian of the ...@@ -93,14 +200,40 @@ The spectrum can be obtained by diagonalizing the Hamiltonian of the
system, which in turn can be obtained from the finalized system, which in turn can be obtained from the finalized
system using `~kwant.system.System.hamiltonian_submatrix`: system using `~kwant.system.System.hamiltonian_submatrix`:
.. literalinclude:: /code/include/closed_system.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_yvri
:end-before: #HIDDEN_END_yvri def plot_spectrum(syst, Bfields):
energies = []
for B in Bfields:
# Obtain the Hamiltonian as a sparse matrix
ham_mat = syst.hamiltonian_submatrix(params=dict(B=B), sparse=True)
# we only calculate the 15 lowest eigenvalues
ev = sla.eigsh(ham_mat.tocsc(), k=15, sigma=0,
return_eigenvectors=False)
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 Note that we use sparse linear algebra to efficiently calculate only a
few lowest eigenvalues. Finally, we obtain the result: few lowest eigenvalues. Finally, we obtain the result:
.. image:: /code/figure/closed_system_result.* .. jupyter-execute::
:hide-code:
syst = make_system()
syst = syst.finalized()
# We should observe energy levels that flow towards Landau
# level energies with increasing magnetic field.
plot_spectrum(syst, [iB * 0.002 for iB in range(100)])
At zero magnetic field several energy levels are degenerate (since our At zero magnetic field several energy levels are degenerate (since our
quantum dot is rather symmetric). These degeneracies are split quantum dot is rather symmetric). These degeneracies are split
...@@ -110,11 +243,34 @@ Landau level energies at higher magnetic fields [#]_. ...@@ -110,11 +243,34 @@ Landau level energies at higher magnetic fields [#]_.
The eigenvectors are obtained very similarly, and can be plotted directly The eigenvectors are obtained very similarly, and can be plotted directly
using `~kwant.plotter.map`: using `~kwant.plotter.map`:
.. literalinclude:: /code/include/closed_system.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_wave :hide-code:
:end-before: #HIDDEN_END_wave
def sorted_eigs(ev):
evals, evecs = ev
evals, evecs = map(np.array, zip(*sorted(zip(evals, evecs.transpose()))))
return evals, evecs.transpose()
.. jupyter-execute::
def plot_wave_function(syst, B=0.001):
# Calculate the wave functions in the system.
ham_mat = syst.hamiltonian_submatrix(sparse=True, params=dict(B=B))
evals, evecs = sorted_eigs(sla.eigsh(ham_mat.tocsc(), k=20, sigma=0))
# Plot the probability density of the 10th eigenmode.
kwant.plotter.map(syst, np.abs(evecs[:, 9])**2,
colorbar=False, oversampling=1)
.. image:: /code/figure/closed_system_eigenvector.* .. jupyter-execute::
:hide-code:
syst = make_system(r=30)
# Plot an eigenmode of a circular dot. Here we create a larger system for
# better spatial resolution.
syst = make_system(r=30).finalized()
plot_wave_function(syst);
The last two arguments to `~kwant.plotter.map` are optional. The first prevents The last two arguments to `~kwant.plotter.map` are optional. The first prevents
a colorbar from appearing. The second, ``oversampling=1``, makes the image look a colorbar from appearing. The second, ``oversampling=1``, makes the image look
...@@ -127,11 +283,22 @@ that they can have *non-zero* local current. We can calculate the local ...@@ -127,11 +283,22 @@ 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 current due to a state by using `kwant.operator.Current` and plotting
it using `kwant.plotter.current`: it using `kwant.plotter.current`:
.. literalinclude:: /code/include/closed_system.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_current
:end-before: #HIDDEN_END_current def plot_current(syst, B=0.001):
# Calculate the wave functions in the system.
ham_mat = syst.hamiltonian_submatrix(sparse=True, params=dict(B=B))
evals, evecs = sorted_eigs(sla.eigsh(ham_mat.tocsc(), k=20, sigma=0))
# Calculate and plot the local current of the 10th eigenmode.
J = kwant.operator.Current(syst)
current = J(evecs[:, 9], params=dict(B=B))
kwant.plotter.current(syst, current, colorbar=False)
.. jupyter-execute::
:hide-code:
.. image:: /code/figure/closed_system_current.* plot_current(syst);
.. specialnote:: Technical details .. specialnote:: Technical details
......
...@@ -11,7 +11,10 @@ Matrix structure of on-site and hopping elements ...@@ -11,7 +11,10 @@ Matrix structure of on-site and hopping elements
.. seealso:: .. seealso::
The complete source code of this example can be found in The complete source code of this example can be found in
:download:`spin_orbit.py </code/download/spin_orbit.py>` :jupyter-download:script:`spin_orbit`
.. jupyter-kernel::
:id: spin_orbit
We begin by extending the simple 2DEG-Hamiltonian by a Rashba spin-orbit We begin by extending the simple 2DEG-Hamiltonian by a Rashba spin-orbit
coupling and a Zeeman splitting due to an external magnetic field: coupling and a Zeeman splitting due to an external magnetic field:
...@@ -42,24 +45,75 @@ use matrices in our program, we import the Tinyarray package. (`NumPy ...@@ -42,24 +45,75 @@ use matrices in our program, we import the Tinyarray package. (`NumPy
<http://www.numpy.org/>`_ would work as well, but Tinyarray is much faster <http://www.numpy.org/>`_ would work as well, but Tinyarray is much faster
for small arrays.) for small arrays.)
.. literalinclude:: /code/include/spin_orbit.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_xumz :hide-code:
:end-before: #HIDDEN_END_xumz
# Tutorial 2.3.1. Matrix structure of on-site and hopping elements
# ================================================================
#
# Physics background
# ------------------
# Gaps in quantum wires with spin-orbit coupling and Zeeman splititng,
# as theoretically predicted in
# http://prl.aps.org/abstract/PRL/v90/i25/e256601
# and (supposedly) experimentally oberved in
# http://www.nature.com/nphys/journal/v6/n5/abs/nphys1626.html
#
# Kwant features highlighted
# --------------------------
# - Numpy matrices as values in Builder
import kwant
# For plotting
from matplotlib import pyplot
.. jupyter-execute::
# For matrix support
import tinyarray
For convenience, we define the Pauli-matrices first (with :math:`\sigma_0` the For convenience, we define the Pauli-matrices first (with :math:`\sigma_0` the
unit matrix): unit matrix):
.. literalinclude:: /code/include/spin_orbit.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_hwbt
:end-before: #HIDDEN_END_hwbt # define Pauli-matrices for convenience
sigma_0 = tinyarray.array([[1, 0], [0, 1]])
sigma_x = tinyarray.array([[0, 1], [1, 0]])
sigma_y = tinyarray.array([[0, -1j], [1j, 0]])
sigma_z = tinyarray.array([[1, 0], [0, -1]])
Previously, we used numbers as the values of our matrix elements. Previously, we used numbers as the values of our matrix elements.
However, `~kwant.builder.Builder` also accepts matrices as values, and However, `~kwant.builder.Builder` also accepts matrices as values, and
we can simply write: we can simply write:
.. literalinclude:: /code/include/spin_orbit.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_uxrm :hide-code:
:end-before: #HIDDEN_END_uxrm
# 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()
syst = kwant.Builder()
t = 1.0
alpha = 0.5
e_z = 0.08
W, L = 10, 30
.. jupyter-execute::
#### Define the scattering region. ####
syst[(lat(x, y) for x in range(L) for y in range(W))] = \
4 * t * sigma_0 + e_z * sigma_z
# hoppings in x-direction
syst[kwant.builder.HoppingKind((1, 0), lat, lat)] = \
-t * sigma_0 + 1j * alpha * sigma_y / 2
# hoppings in y-directions
syst[kwant.builder.HoppingKind((0, 1), lat, lat)] = \
-t * sigma_0 - 1j * alpha * sigma_x / 2
Note that the Zeeman energy adds to the onsite term, whereas the Rashba 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). spin-orbit term adds to the hoppings (due to the derivative operator).
...@@ -85,14 +139,49 @@ when specifying `(1, 0)` it is not necessary to specify `(-1, 0)`), ...@@ -85,14 +139,49 @@ when specifying `(1, 0)` it is not necessary to specify `(-1, 0)`),
The leads also allow for a matrix structure, The leads also allow for a matrix structure,
.. literalinclude:: /code/include/spin_orbit.py
:start-after: #HIDDEN_BEGIN_yliu .. jupyter-execute::
:end-before: #HIDDEN_END_yliu :hide-code:
#### Define the left lead. ####
lead = kwant.Builder(kwant.TranslationalSymmetry((-1, 0)))
.. jupyter-execute::
lead[(lat(0, j) for j in range(W))] = 4 * t * sigma_0 + e_z * sigma_z
# hoppings in x-direction
lead[kwant.builder.HoppingKind((1, 0), lat, lat)] = \
-t * sigma_0 + 1j * alpha * sigma_y / 2
# hoppings in y-directions
lead[kwant.builder.HoppingKind((0, 1), lat, lat)] = \
-t * sigma_0 - 1j * alpha * sigma_x / 2
.. jupyter-execute::
:hide-code:
#### Attach the leads and finalize the system. ####
syst.attach_lead(lead)
syst.attach_lead(lead.reversed())
syst = syst.finalized()
The remainder of the code is unchanged, and as a result we should obtain The remainder of the code is unchanged, and as a result we should obtain
the following, clearly non-monotonic conductance steps: the following, clearly non-monotonic conductance steps:
.. image:: /code/figure/spin_orbit_result.* .. jupyter-execute::
:hide-code:
# Compute conductance
energies=[0.01 * i - 0.3 for i in range(100)]
data = []
for energy in energies:
smatrix = kwant.smatrix(syst, energy)
data.append(smatrix.transmission(1, 0))
pyplot.figure()
pyplot.plot(energies, data)
pyplot.xlabel("energy [t]")
pyplot.ylabel("conductance [e^2/h]")
pyplot.show()
.. specialnote:: Technical details .. specialnote:: Technical details
...@@ -129,7 +218,29 @@ Spatially dependent values through functions ...@@ -129,7 +218,29 @@ Spatially dependent values through functions
.. seealso:: .. seealso::
The complete source code of this example can be found in The complete source code of this example can be found in
:download:`quantum_well.py </code/download/quantum_well.py>` :jupyter-download:script:`quantum_well`
.. jupyter-kernel::
:id: quantum_well
.. jupyter-execute::
:hide-code:
# Tutorial 2.3.2. Spatially dependent values through functions
# ============================================================
#
# Physics background
# ------------------
# transmission through a quantum well
#
# Kwant features highlighted
# --------------------------
# - Functions as values in Builder
import kwant
# For plotting
from matplotlib import pyplot
Up to now, all examples had position-independent matrix-elements Up to now, all examples had position-independent matrix-elements
(and thus translational invariance along the wire, which (and thus translational invariance along the wire, which
...@@ -148,22 +259,57 @@ changing the potential then implies the need to build up the system again. ...@@ -148,22 +259,57 @@ 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 Instead, we use a python *function* to define the onsite energies. We
define the potential profile of a quantum well as: define the potential profile of a quantum well as:
.. literalinclude:: /code/include/quantum_well.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_ehso :hide-code:
:end-before: #HIDDEN_END_ehso
a = 1
t = 1.0
W, L, L_well = 10, 30, 10
.. jupyter-execute::
# 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()
#### Define the scattering region. ####
# Potential profile
def potential(site, pot):
(x, y) = site.pos
if (L - L_well) / 2 < x < (L + L_well) / 2:
return pot
else:
return 0
This function takes two arguments: the first of type `~kwant.builder.Site`, This function takes two arguments: the first of type `~kwant.builder.Site`,
from which you can get the real-space coordinates using ``site.pos``, and the from which you can get the real-space coordinates using ``site.pos``, and the
value of the potential as the second. Note that in `potential` we can access 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 variables `L` and `L_well` that are defined globally.
namespace of `make_system`.
Kwant now allows us to pass a function as a value to Kwant now allows us to pass a function as a value to
`~kwant.builder.Builder`: `~kwant.builder.Builder`:
.. literalinclude:: /code/include/quantum_well.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_coid
:end-before: #HIDDEN_END_coid def onsite(site, pot):
return 4 * t + potential(site, pot)
syst[(lat(x, y) for x in range(L) for y in range(W))] = onsite
syst[lat.neighbors()] = -t
.. jupyter-execute::
:hide-code:
#### Define and attach the leads. ####
lead = kwant.Builder(kwant.TranslationalSymmetry((-a, 0)))
lead[(lat(0, j) for j in range(W))] = 4 * t
lead[lat.neighbors()] = -t
syst.attach_lead(lead)
syst.attach_lead(lead.reversed())
syst = syst.finalized()
For each lattice point, the corresponding site is then passed as the For each lattice point, the corresponding site is then passed as the
first argument to the function `onsite`. The values of any additional first argument to the function `onsite`. The values of any additional
...@@ -180,9 +326,21 @@ of the lead -- this should be kept in mind. ...@@ -180,9 +326,21 @@ of the lead -- this should be kept in mind.
Finally, we compute the transmission probability: Finally, we compute the transmission probability:
.. literalinclude:: /code/include/quantum_well.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_sqvr
:end-before: #HIDDEN_END_sqvr def plot_conductance(syst, energy, welldepths):
# Compute conductance
data = []
for welldepth in welldepths:
smatrix = kwant.smatrix(syst, energy, params=dict(pot=-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 dictionary, `params`, that contains ``kwant.smatrix`` allows us to specify a dictionary, `params`, that contains
the additional arguments required by the Hamiltonian matrix elements. the additional arguments required by the Hamiltonian matrix elements.
...@@ -191,7 +349,11 @@ of the potential well by passing the potential value (remember above ...@@ -191,7 +349,11 @@ of the potential well by passing the potential value (remember above
we defined our `onsite` function that takes a parameter named `pot`). we defined our `onsite` function that takes a parameter named `pot`).
We obtain the result: We obtain the result:
.. image:: /code/figure/quantum_well_result.* .. jupyter-execute::
:hide-code:
plot_conductance(syst, energy=0.2,
welldepths=[0.01 * i for i in range(100)])
Starting from no potential (well depth = 0), we observe the typical Starting from no potential (well depth = 0), we observe the typical
oscillatory transmission behavior through resonances in the quantum well. oscillatory transmission behavior through resonances in the quantum well.
...@@ -218,7 +380,35 @@ Nontrivial shapes ...@@ -218,7 +380,35 @@ Nontrivial shapes
.. seealso:: .. seealso::
The complete source code of this example can be found in The complete source code of this example can be found in
:download:`ab_ring.py </code/download/ab_ring.py>` :jupyter-download:script:`ab_ring`
.. jupyter-kernel::
:id: ab_ring
.. jupyter-execute::
:hide-code:
# Tutorial 2.3.3. Nontrivial shapes
# =================================
#
# Physics background
# ------------------
# Flux-dependent transmission through a quantum ring
#
# Kwant features highlighted
# --------------------------
# - More complex shapes with lattices
# - Allows for discussion of subtleties of `attach_lead` (not in the
# example, but in the tutorial main text)
# - Modifcations of hoppings/sites after they have been added
from cmath import exp
from math import pi
import kwant
# For plotting
from matplotlib import pyplot
Up to now, we only dealt with simple wire geometries. Now we turn to the case 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 of a more complex geometry, namely transport through a quantum ring
...@@ -238,9 +428,29 @@ First, define a boolean function defining the desired shape, i.e. a function ...@@ -238,9 +428,29 @@ First, define a boolean function defining the desired shape, i.e. a function
that returns ``True`` whenever a point is inside the shape, and that returns ``True`` whenever a point is inside the shape, and
``False`` otherwise: ``False`` otherwise:
.. literalinclude:: /code/include/ab_ring.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_eusz :hide-code:
:end-before: #HIDDEN_END_eusz
a = 1
t = 1.0
W = 10
r1, r2 = 10, 20
.. jupyter-execute::
# 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()
#### Define the scattering region. ####
# Now, we aim for a more complex shape, namely a ring (or annulus)
def ring(pos):
(x, y) = pos
rsq = x ** 2 + y ** 2
return (r1 ** 2 < rsq < r2 ** 2)
Note that this function takes a real-space position as argument (not a Note that this function takes a real-space position as argument (not a
`~kwant.builder.Site`). `~kwant.builder.Site`).
...@@ -249,9 +459,11 @@ We can now simply add all of the lattice points inside this shape at ...@@ -249,9 +459,11 @@ We can now simply add all of the lattice points inside this shape at
once, using the function `~kwant.lattice.Square.shape` once, using the function `~kwant.lattice.Square.shape`
provided by the lattice: provided by the lattice:
.. literalinclude:: /code/include/ab_ring.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_lcak
:end-before: #HIDDEN_END_lcak # and add the corresponding lattice points using the `shape`-function
syst[lat.shape(ring, (0, r1 + 1))] = 4 * t
syst[lat.neighbors()] = -t
Here, ``lat.shape`` takes as a second parameter a (real-space) point that is 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 inside the desired shape. The hoppings can still be added using
...@@ -264,9 +476,30 @@ along the branch cut in the lower arm of the ring. For this we select ...@@ -264,9 +476,30 @@ 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))` all hoppings in x-direction that are of the form `(lat(1, j), lat(0, j))`
with ``j<0``: with ``j<0``:
.. literalinclude:: /code/include/ab_ring.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_lvkt
:end-before: #HIDDEN_END_lvkt # In order to introduce a flux through the ring, we introduce a phase on
# the hoppings on the line cut through one of the arms. Since we want to
# change the flux without modifying the Builder instance repeatedly, we
# define the modified hoppings as a function that takes the flux as its
# parameter phi.
def hopping_phase(site1, site2, phi):
return -t * exp(1j * phi)
def crosses_branchcut(hop):
ix0, iy0 = hop[0].tag
# builder.HoppingKind with the argument (1, 0) below
# returns hoppings ordered as ((i+1, j), (i, j))
return iy0 < 0 and ix0 == 1 # ix1 == 0 then implied
# 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 Here, `crosses_branchcut` is a boolean function that returns ``True`` for
the desired hoppings. We then use again a generator (this time with the desired hoppings. We then use again a generator (this time with
...@@ -279,9 +512,19 @@ by the parameter `phi`. ...@@ -279,9 +512,19 @@ by the parameter `phi`.
For the leads, we can also use the ``lat.shape``-functionality: For the leads, we can also use the ``lat.shape``-functionality:
.. literalinclude:: /code/include/ab_ring.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_qwgr
:end-before: #HIDDEN_END_qwgr #### Define the leads. ####
# left lead
sym_lead = kwant.TranslationalSymmetry((-a, 0))
lead = kwant.Builder(sym_lead)
def lead_shape(pos):
(x, y) = pos
return (-W / 2 < y < W / 2)
lead[lat.shape(lead_shape, (0, 0))] = 4 * t
lead[lat.neighbors()] = -t
Here, the shape must be compatible with the translational symmetry Here, the shape must be compatible with the translational symmetry
of the lead ``sym_lead``. In particular, this means that it should extend to of the lead ``sym_lead``. In particular, this means that it should extend to
...@@ -290,9 +533,12 @@ no restriction on ``x`` in ``lead_shape``) [#]_. ...@@ -290,9 +533,12 @@ no restriction on ``x`` in ``lead_shape``) [#]_.
Attaching the leads is done as before: Attaching the leads is done as before:
.. literalinclude:: /code/include/ab_ring.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_skbz :hide-output:
:end-before: #HIDDEN_END_skbz
#### Attach the leads ####
syst.attach_lead(lead)
syst.attach_lead(lead.reversed())
In fact, attaching leads seems not so simple any more for the current In fact, attaching leads seems not so simple any more for the current
structure with a scattering region very much different from the lead structure with a scattering region very much different from the lead
...@@ -309,12 +555,36 @@ the lead is attached: ...@@ -309,12 +555,36 @@ the lead is attached:
After the lead has been attached, the system should look like this: After the lead has been attached, the system should look like this:
.. image:: /code/figure/ab_ring_syst.* .. jupyter-execute::
:hide-code:
kwant.plot(syst);
The computation of the conductance goes in the same fashion as before. The computation of the conductance goes in the same fashion as before.
Finally you should get the following result: Finally you should get the following result:
.. image:: /code/figure/ab_ring_result.*
.. jupyter-execute::
:hide-code:
def plot_conductance(syst, energy, fluxes):
# compute conductance
normalized_fluxes = [flux / (2 * pi) for flux in fluxes]
data = []
for flux in fluxes:
smatrix = kwant.smatrix(syst, energy, params=dict(phi=flux))
data.append(smatrix.transmission(1, 0))
pyplot.figure()
pyplot.plot(normalized_fluxes, data)
pyplot.xlabel("flux [flux quantum]")
pyplot.ylabel("conductance [e^2/h]")
pyplot.show()
# We should see a conductance that is periodic with the flux quantum
plot_conductance(syst.finalized(), energy=0.15,
fluxes=[0.01 * i * 3 * 2 * pi for i in range(100)])
where one can observe the conductance oscillations with the where one can observe the conductance oscillations with the
period of one flux quantum. period of one flux quantum.
...@@ -330,7 +600,41 @@ period of one flux quantum. ...@@ -330,7 +600,41 @@ period of one flux quantum.
becomes more apparent if we attach the leads a bit further away 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: from the central axis o the ring, as was done in this example:
.. image:: /code/figure/ab_ring_note1.* .. jupyter-kernel::
:id: ab_ring_note1
.. jupyter-execute::
:hide-code:
import kwant
from matplotlib import pyplot
a = 1
t = 1.0
W = 10
r1, r2 = 10, 20
lat = kwant.lattice.square()
syst = kwant.Builder()
def ring(pos):
(x, y) = pos
rsq = x**2 + y**2
return ( r1**2 < rsq < r2**2)
syst[lat.shape(ring, (0, 11))] = 4 * t
syst[lat.neighbors()] = -t
sym_lead0 = kwant.TranslationalSymmetry((-a, 0))
lead0 = kwant.Builder(sym_lead0)
def lead_shape(pos):
(x, y) = pos
return (-1 < x < 1) and ( 0.5 * W < y < 1.5 * W )
lead0[lat.shape(lead_shape, (0, W))] = 4 * t
lead0[lat.neighbors()] = -t
lead1 = lead0.reversed()
syst.attach_lead(lead0)
syst.attach_lead(lead1)
kwant.plot(syst);
- Per default, `~kwant.builder.Builder.attach_lead` attaches - Per default, `~kwant.builder.Builder.attach_lead` attaches
the lead to the "outside" of the structure, by tracing the the lead to the "outside" of the structure, by tracing the
...@@ -345,7 +649,40 @@ period of one flux quantum. ...@@ -345,7 +649,40 @@ period of one flux quantum.
starts the trace-back in the middle of the ring, resulting starts the trace-back in the middle of the ring, resulting
in the lead being attached to the inner circle: in the lead being attached to the inner circle:
.. image:: /code/figure/ab_ring_note2.* .. jupyter-kernel::
:id: ab_ring_note2
.. jupyter-execute::
:hide-code:
import kwant
from matplotlib import pyplot
a = 1
t = 1.0
W = 10
r1, r2 = 10, 20
lat = kwant.lattice.square(a)
syst = kwant.Builder()
def ring(pos):
(x, y) = pos
rsq = x**2 + y**2
return ( r1**2 < rsq < r2**2)
syst[lat.shape(ring, (0, 11))] = 4 * t
syst[lat.neighbors()] = -t
sym_lead0 = kwant.TranslationalSymmetry((-a, 0))
lead0 = kwant.Builder(sym_lead0)
def lead_shape(pos):
(x, y) = pos
return (-1 < x < 1) and ( -W/2 < y < W/2 )
lead0[lat.shape(lead_shape, (0, 0))] = 4 * t
lead0[lat.neighbors()] = -t
lead1 = lead0.reversed()
syst.attach_lead(lead0)
syst.attach_lead(lead1, lat(0, 0))
kwant.plot(syst);
Note that here the lead is treated as if it would pass over Note that here the lead is treated as if it would pass over
the other arm of the ring, without intersecting it. the other arm of the ring, without intersecting it.
......
...@@ -3,7 +3,37 @@ Superconductors: orbital degrees of freedom, conservation laws and symmetries ...@@ -3,7 +3,37 @@ Superconductors: orbital degrees of freedom, conservation laws and symmetries
.. seealso:: .. seealso::
The complete source code of this example can be found in The complete source code of this example can be found in
:download:`superconductor.py </code/download/superconductor.py>` :jupyter-download:script:`superconductor`
.. jupyter-kernel::
:id: superconductor
.. jupyter-execute::
:hide-code:
# Tutorial 2.6. "Superconductors": orbitals, conservation laws and symmetries
# ===========================================================================
#
# Physics background
# ------------------
# - conductance of a NS-junction (Andreev reflection, superconducting gap)
#
# Kwant features highlighted
# --------------------------
# - Implementing electron and hole ("orbital") degrees of freedom
# using conservation laws.
# - Use of discrete symmetries to relate scattering states.
import kwant
from matplotlib import pyplot
import tinyarray
import numpy as np
tau_x = tinyarray.array([[0, 1], [1, 0]])
tau_y = tinyarray.array([[0, -1j], [1j, 0]])
tau_z = tinyarray.array([[1, 0], [0, -1]])
This example deals with superconductivity on the level of the This example deals with superconductivity on the level of the
Bogoliubov-de Gennes (BdG) equation. In this framework, the Hamiltonian Bogoliubov-de Gennes (BdG) equation. In this framework, the Hamiltonian
...@@ -58,9 +88,40 @@ for all Hamiltonian matrix elements, as we did ...@@ -58,9 +88,40 @@ for all Hamiltonian matrix elements, as we did
previously in the :ref:`spin example <tutorial_spinorbit>`. We declare previously in the :ref:`spin example <tutorial_spinorbit>`. We declare
the square lattice and construct the scattering region with the following: the square lattice and construct the scattering region with the following:
.. literalinclude:: /code/include/superconductor.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_nbvn :hide-code:
:end-before: #HIDDEN_END_nbvn
a = 1
W, L = 10, 10
barrier = 1.5
barrierpos = (3, 4)
mu = 0.4
Delta = 0.1
Deltapos=4
t = 1.0
.. jupyter-execute::
# 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()
#### Define the scattering region. ####
# The superconducting order parameter couples electron and hole orbitals
# on each site, and hence enters as an onsite potential.
# The pairing is only included beyond the point 'Deltapos' in the scattering region.
syst[(lat(x, y) for x in range(Deltapos) for y in range(W))] = (4 * t - mu) * tau_z
syst[(lat(x, y) for x in range(Deltapos, L) for y in range(W))] = (4 * t - mu) * tau_z + Delta * tau_x
# The tunnel barrier
syst[(lat(x, y) for x in range(barrierpos[0], barrierpos[1])
for y in range(W))] = (4 * t + barrier - mu) * tau_z
# Hoppings
syst[lat.neighbors()] = -t * tau_z
Note the new argument ``norbs`` to `~kwant.lattice.square`. This is Note the new argument ``norbs`` to `~kwant.lattice.square`. This is
the number of orbitals per site in the discretized BdG Hamiltonian - of course, the number of orbitals per site in the discretized BdG Hamiltonian - of course,
...@@ -80,9 +141,16 @@ of it). The next step towards computing conductance is to attach leads. ...@@ -80,9 +141,16 @@ 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 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: one to the right end. Starting with the left lead, we have:
.. literalinclude:: /code/include/superconductor.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_ttth
:end-before: #HIDDEN_END_ttth #### Define the leads. ####
# Left lead - normal, so the order parameter is zero.
sym_left = kwant.TranslationalSymmetry((-a, 0))
# Specify the conservation law used to treat electrons and holes separately.
# We only do this in the left lead, where the pairing is zero.
lead0 = kwant.Builder(sym_left, conservation_law=-tau_z, particle_hole=tau_y)
lead0[(lat(0, j) for j in range(W))] = (4 * t - mu) * tau_z
lead0[lat.neighbors()] = -t * tau_z
Note the two new new arguments in `~kwant.builder.Builder`, ``conservation_law`` Note the two new new arguments in `~kwant.builder.Builder`, ``conservation_law``
and ``particle_hole``. For the purpose of computing conductance, ``conservation_law`` and ``particle_hole``. For the purpose of computing conductance, ``conservation_law``
...@@ -120,9 +188,19 @@ of ascending eigenvalues of the conservation law. ...@@ -120,9 +188,19 @@ of ascending eigenvalues of the conservation law.
In order to move on with the conductance calculation, let's attach the second In order to move on with the conductance calculation, let's attach the second
lead to the right side of the scattering region: lead to the right side of the scattering region:
.. literalinclude:: /code/include/superconductor.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_zuuw
:end-before: #HIDDEN_END_zuuw # Right lead - superconducting, so the order parameter is included.
sym_right = kwant.TranslationalSymmetry((a, 0))
lead1 = kwant.Builder(sym_right)
lead1[(lat(0, j) for j in range(W))] = (4 * t - mu) * tau_z + Delta * tau_x
lead1[lat.neighbors()] = -t * tau_z
#### Attach the leads and finalize the system. ####
syst.attach_lead(lead0)
syst.attach_lead(lead1)
syst = syst.finalized()
The second (right) lead is superconducting, such that the electron and hole 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 blocks are coupled. Of course, this means that we can not separate them into
...@@ -140,9 +218,23 @@ confused by the fact that it says ``transmission`` -- transmission ...@@ -140,9 +218,23 @@ confused by the fact that it says ``transmission`` -- transmission
into the same lead is reflection), and reflection from electrons to holes into the same lead is reflection), and reflection from electrons to holes
is ``smatrix.transmission((0, 1), (0, 0))``: is ``smatrix.transmission((0, 1), (0, 0))``:
.. literalinclude:: /code/include/superconductor.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_jbjt
:end-before: #HIDDEN_END_jbjt def plot_conductance(syst, energies):
# Compute conductance
data = []
for energy in energies:
smatrix = kwant.smatrix(syst, energy)
# Conductance is N - R_ee + R_he
data.append(smatrix.submatrix((0, 0), (0, 0)).shape[0] -
smatrix.transmission((0, 0), (0, 0)) +
smatrix.transmission((0, 1), (0, 0)))
pyplot.figure()
pyplot.plot(energies, data)
pyplot.xlabel("energy [t]")
pyplot.ylabel("conductance [e^2/h]")
pyplot.show()
Note that ``smatrix.submatrix((0, 0), (0, 0))`` returns the block concerning 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 reflection of electrons to electrons, and from its size we can extract the number of modes
...@@ -150,7 +242,10 @@ reflection of electrons to electrons, and from its size we can extract the numbe ...@@ -150,7 +242,10 @@ reflection of electrons to electrons, and from its size we can extract the numbe
For the default parameters, we obtain the following conductance: For the default parameters, we obtain the following conductance:
.. image:: /code/figure/superconductor_transport_result.* .. jupyter-execute::
:hide-code:
plot_conductance(syst, energies=[0.002 * i for i in range(-10, 100)])
We a see a conductance that is proportional to the square of the tunneling We a see a conductance that is proportional to the square of the tunneling
probability within the gap, and proportional to the tunneling probability probability within the gap, and proportional to the tunneling probability
...@@ -176,13 +271,34 @@ the electron and hole blocks. The exception is of course at zero energy, in whic ...@@ -176,13 +271,34 @@ the electron and hole blocks. The exception is of course at zero energy, in whic
case particle-hole symmetry transforms between the electron and hole blocks, resulting case particle-hole symmetry transforms between the electron and hole blocks, resulting
in a symmetric scattering matrix. We can check the symmetry explicitly with in a symmetric scattering matrix. We can check the symmetry explicitly with
.. literalinclude:: /code/include/superconductor.py .. jupyter-execute::
:start-after: #HIDDEN_BEGIN_pqmp
:end-before: #HIDDEN_END_pqmp 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))
which yields the output which yields the output
.. literalinclude:: /code/figure/check_PHS_out.txt .. jupyter-execute::
:hide-code:
check_PHS(syst)
Note that :math:`\mathcal{P}` flips the sign of momentum, and for the parameters Note that :math:`\mathcal{P}` 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 consider here, there are two electron and two hole modes active at zero energy.
......
...@@ -29,22 +29,29 @@ except ImportError: ...@@ -29,22 +29,29 @@ except ImportError:
raise raise
from ._common import KwantDeprecationWarning, UserCodeError from ._common import KwantDeprecationWarning, UserCodeError
__all__.extend(['KwantDeprecationWarning', 'UserCodeError']) __all__.extend(['KwantDeprecationWarning', 'UserCodeError'])
for module in ['system', 'builder', 'lattice', 'solvers', 'digest', 'rmt', # Pre-import most submodules. (We make exceptions for submodules that have
'operator', 'kpm', 'wraparound']: # special dependencies or where that would take too long.)
exec('from . import {0}'.format(module)) from . import system
__all__.append(module) from . import builder
from . import lattice
from . import solvers
from . import digest
from . import rmt
from . import operator
from . import kpm
from . import wraparound
__all__.extend(['system', 'builder', 'lattice', 'solvers', 'digest', 'rmt',
'operator', 'kpm', 'wraparound'])
# Make selected functionality available directly in the root namespace. # Make selected functionality available directly in the root namespace.
available = [('builder', ['Builder', 'HoppingKind']), from .builder import Builder, HoppingKind
('lattice', ['TranslationalSymmetry']), __all__.extend(['Builder', 'HoppingKind'])
('solvers.default', from .lattice import TranslationalSymmetry
['smatrix', 'greens_function', 'ldos', 'wave_function'])] __all__.extend(['TranslationalSymmetry'])
for module, names in available: from .solvers.default import smatrix, greens_function, ldos, wave_function
exec('from .{0} import {1}'.format(module, ', '.join(names))) __all__.extend(['smatrix', 'greens_function', 'ldos', 'wave_function'])
__all__.extend(names)
# Importing plotter might not work, but this does not have to be a problem -- # Importing plotter might not work, but this does not have to be a problem --
# only no plotting will be available. # only no plotting will be available.
......
...@@ -15,6 +15,5 @@ except ImportError as error: ...@@ -15,6 +15,5 @@ except ImportError as error:
"dependencies is not installed.") "dependencies is not installed.")
raise ImportError(msg) from error raise ImportError(msg) from error
__all__ = ['discretize', 'discretize_symbolic', 'build_discretized', __all__ = ['discretize', 'discretize_symbolic', 'build_discretized',
'sympify', 'lambdify', 'momentum_operators', 'position_operators'] 'sympify', 'lambdify', 'momentum_operators', 'position_operators']
...@@ -9,8 +9,7 @@ ...@@ -9,8 +9,7 @@
"""Functionality for graphs""" """Functionality for graphs"""
# Merge the public interface of all submodules. # Merge the public interface of all submodules.
__all__ = [] from .core import *
for module in ['core', 'defs']: from .defs import *
exec('from . import {0}'.format(module))
exec('from .{0} import *'.format(module)) __all__ = [core.__all__ + defs.__all__]
exec('__all__.extend({0}.__all__)'.format(module))
...@@ -10,7 +10,10 @@ __all__ = ['lapack'] ...@@ -10,7 +10,10 @@ __all__ = ['lapack']
from . import lapack from . import lapack
# Merge the public interface of the other submodules. # Merge the public interface of the other submodules.
for module in ['decomp_lu', 'decomp_ev', 'decomp_schur']: from .decomp_lu import *
exec('from . import {0}'.format(module)) from .decomp_schur import *
exec('from .{0} import *'.format(module)) from .decomp_ev import *
exec('__all__.extend({0}.__all__)'.format(module))
__all__.extend([decomp_lu.__all__,
decomp_ev.__all__,
decomp_schur.__all__])
...@@ -8,8 +8,14 @@ ...@@ -8,8 +8,14 @@
"""Physics-related algorithms""" """Physics-related algorithms"""
# Merge the public interface of all submodules. # Merge the public interface of all submodules.
__all__ = [] from .leads import *
for module in ['leads', 'dispersion', 'noise', 'symmetry', 'gauge']: from .dispersion import *
exec('from . import {0}'.format(module)) from .noise import *
exec('from .{0} import *'.format(module)) from .symmetry import *
exec('__all__.extend({0}.__all__)'.format(module)) from .gauge import *
__all__ = [leads.__all__
+ dispersion.__all__
+ noise.__all__
+ symmetry.__all__
+ gauge.__all__]
[pytest] [pytest]
testpaths = kwant testpaths = kwant
flakes-ignore = flakes-ignore =
__init__.py UnusedImport __init__.py UnusedImport ImportStarUsed ImportStarUsage
kwant/_plotter.py UnusedImport kwant/_plotter.py UnusedImport
graph/tests/test_scotch.py UndefinedName graph/tests/test_scotch.py UndefinedName
graph/tests/test_dissection.py UndefinedName