diff --git a/.gitignore b/.gitignore index 72b8ea2fd3736c407ac8b321b17bc6d4937ae0cb..ba94c807d204266b925eb7c25a92bc57bfe42971 100644 --- a/.gitignore +++ b/.gitignore @@ -8,13 +8,7 @@ /dist /doc/build /doc/source/reference/generated/ -/doc/source/code/include/*.py -/doc/source/code/figure/*.png -/doc/source/code/figure/*.pdf -/doc/source/code/figure/.*_flag -/doc/source/code/figure/[a-zA-Z]*.py -/doc/source/code/figure/*.txt -/doc/source/code/download/ +/doc/source/figure/*.pdf /build.conf /kwant.egg-info/ /MANIFEST.in diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ffffe78d75dda5e855b7acdbf8088e76770ff1c9..a3ef10b22292cf12ec26d224aae75e1409dc8c23 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -239,7 +239,10 @@ build documentation: - build:latest stage: test 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 + - apt-get update && apt-get install -y librsvg2-bin # for converting svgs to pdfs + - pip install git+https://github.com/jupyter-widgets/jupyter-sphinx sphinxcontrib-svg2pdfconverter + - python -m ipykernel install --user --name kwant-latest + - make -C doc clean; 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: paths: - doc/build/html/ @@ -251,7 +254,10 @@ build PDF documentation: - build:latest stage: test script: - - make -C doc latex SPHINXOPTS='-n -W' + - apt-get update && apt-get install -y librsvg2-bin # for converting svgs to pdfs + - pip install git+https://github.com/jupyter-widgets/jupyter-sphinx sphinxcontrib-svg2pdfconverter + - 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 - make all-pdf artifacts: diff --git a/INSTALL.rst b/INSTALL.rst index 336dd2db0ddbe2fa067559f79884ba910f00fbbc..db5d098791853708de117e989bbb9c80a3fdfa58 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -144,9 +144,11 @@ Building the documentation To build the documentation, the `Sphinx documentation generator <http://www.sphinx-doc.org/en/stable/>`_ is required with ``numpydoc`` extension -(version 0.5 or newer). If PDF documentation is to be built, the tools -from the `libRSVG <https://wiki.gnome.org/action/show/Projects/LibRsvg>`_ (Debian/Ubuntu package -``librsvg2-bin``) are needed to convert SVG drawings into the PDF format. +(version 0.5 or newer), as well as ``jupyter-sphinx`` (version 0.2 or newer). +If PDF documentation is to be built, the tools +from the `libRSVG <https://wiki.gnome.org/action/show/Projects/LibRsvg>`_ +(Debian/Ubuntu package ``librsvg2-bin``) are needed to convert SVG drawings +into the PDF format. As a prerequisite for building the documentation, Kwant must have been built successfully using ``python3 setup.py build`` as described above (or Kwant must @@ -160,25 +162,6 @@ Because of some quirks of how Sphinx works, it might be necessary to execute done, Sphinx may mistakenly use PNG files for PDF output or other problems may appear. -When ``make html`` is run, modified tutorial example scripts are executed to -update any figures that might have changed. The machinery behind this works as -follows. The canonical source for a tutorial script, say ``graphene.py`` is -the file ``doc/source/images/graphene.py.diff``. This diff file contains the -information to recreate two versions of ``graphene.py``: a version that is -presented in the documentation (``doc/source/tutorial/graphene.py``), and a -version that is used to generate the figures for the documentation -(``doc/source/images/graphene.py``). Both versions are related but differ -e.g. in the details of the plotting. When ``make html`` is run, both versions -are extracted form the diff file. - -The diff file may be modified directly. Another possible way of working is to -directly modify either the tutorial script or the figure generation script. -Then ``make html`` will use the command line tool `wiggle -<https://github.com/neilbrown/wiggle>`_ to propagate the modifications accordingly. -This will often just work, but may sometimes result in conflicts, in which case -a message will be printed. The conflicts then have to be resolved much like -with a version control system. - **************************** Hints for specific platforms **************************** diff --git a/doc/Makefile b/doc/Makefile index 1bdce83437737749dadea3aa5f4f9823b365e68a..a0b1e46b7fa11f3ec117e2c99d89c44c573b067b 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,183 +1,40 @@ -# Makefile for Sphinx documentation - -# Copyright 2011-2017 Kwant authors. +# Minimal makefile for Sphinx documentation # -# This file is part of Kwant. It is subject to the license terms in the file -# LICENSE.rst found in the top-level directory of this distribution and at -# http://kwant-project.org/license. A list of Kwant authors can be found in -# the file AUTHORS.rst at the top-level directory of this distribution and at -# http://kwant-project.org/authors. # You can set these variables from the command line. SPHINXOPTS = -SPHINXBUILD = python3 -c 'import sys, sphinx; sys.exit(sphinx.main(sys.argv))' -PAPER = +SPHINXBUILD = sphinx-build +SOURCEDIR = source BUILDDIR = build -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile # We convert all SVG files to PDF for LaTeX output. For HTML output, we don't # create PNGs but rather use the SVG files directly. FIGURESOURCES = $(shell find source -name "*.svg") GENERATEDPDF = $(patsubst %.svg,%.pdf,$(FIGURESOURCES)) -# Figure generation from patched tutorial scripts -# -# As make does not support the generation of multiple targets by a single -# invocation of a (non-implicit) rule, we use a trick: We pretend to be -# generating a single (empty) flag file per invocation. The figure files are -# generated as well, but only as side-effects. Each flag file is used to -# remember the time at which the corresponding figure-generating script was run. -# This works perfectly unless the actual output files are deleted without -# deleting the corresponding flag file. -FIGSCRIPTS = $(patsubst %.diff,%,$(notdir $(wildcard source/code/figure/*.py.diff))) -FIGURES = $(patsubst %.py,source/code/figure/.%_flag,$(FIGSCRIPTS)) -SCRIPTS = $(sort $(FIGSCRIPTS) $(notdir $(wildcard source/code/include/*.py))) -INCLUDES = $(patsubst %,source/code/include/%,$(SCRIPTS)) -DOWNLOADS = $(patsubst %,source/code/download/%,$(SCRIPTS)) - -.PHONY: help clean realclean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest - -help: - @echo "Please use \`make <target>' where <target> is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - @echo - @echo "Append SPHINXOPTS='-A website_deploy=True' to include web analytics code." - -clean: - -rm -rf $(BUILDDIR)/* $(GENERATEDPDF) - -rm -rf source/reference/generated - -realclean: clean - -rm -f $(FIGURES) - -rm -f $(patsubst %,source/code/include/%,$(FIGSCRIPTS)) - -rm -f $(DOWNLOADS) - -rm -f $(patsubst %,source/code/figure/%,$(FIGSCRIPTS)) - -rm -f $(patsubst %.py,source/code/figure/%_*.png,$(FIGSCRIPTS)) - -rm -f $(patsubst %.py,source/code/figure/%_*.pdf,$(FIGSCRIPTS)) - -html: $(FIGURES) $(INCLUDES) $(DOWNLOADS) - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: $(FIGURES) $(INCLUDES) $(DOWNLOADS) - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -pickle: $(FIGURES) $(INCLUDES) $(DOWNLOADS) - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: $(FIGURES) $(INCLUDES) $(DOWNLOADS) - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: $(FIGURES) $(INCLUDES) $(DOWNLOADS) - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: $(FIGURES) $(INCLUDES) $(DOWNLOADS) - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/kwant.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/kwant.qhc" - -latex: $(GENERATEDPDF) $(FIGURES) $(INCLUDES) $(DOWNLOADS) - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ - "run these through (pdf)latex." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - %.pdf: %.svg inkscape --export-pdf=$@ $< -#### Tutorial and figure script generation machinery #### -# See source/code/README for an explanation. - -# Make tutorial scripts by extracting the (complete!) context of the "patches". -# We make sure not to use 'wiggle' here. -.SECONDARY: -source/code/include/%.py: source/code/figure/%.py.diff - @sed -n '/^[- ]/ s/^.//p' <$< >$@ - @touch -r $< $@ +# Emtpy target required so that the default target is not triggered +%.svg: -source/code/download/%.py: source/code/include/%.py - @mkdir -p source/code/download - @grep -v '^#HIDDEN' <$< >$@ -# Make the figure generation scripts by patching tutorial scripts. If the -# tutorial scripts haven't been modified, don't patch but directly extract the -# figure generation scripts. This means that 'wiggle' is only needed when the -# tutorial scripts have been modified. -.SECONDARY: -source/code/figure/%.py: source/code/include/%.py - @if [ $< -nt $@.diff ]; then \ - cp $< $@; \ - rm -f $@.porig; \ - if ! wiggle --replace $@ $@.diff; then \ - command -v wiggle >/dev/null 2>&1 && \ - echo "Resolve conflicts by editing the files named below"; \ - touch -d@0 $@; \ - exit 1; \ - fi \ - else \ - sed -n '/^[+ ]/ s/^.//p' <$@.diff >$@; \ - touch -r $@.diff $@; \ - fi - -# Make the figure generation scripts also depend on the diffs. -define makedep -source/code/figure/$(1): source/code/figure/$(1).diff -endef -$(foreach name,$(FIGSCRIPTS),$(eval $(call makedep,$(name)))) - -# Run an figure generation script. When successful, and if the script is newer -# than the corresponding diff, recreate the latter. Note that the -# corresponding tutorial script cannot be newer, since if it is, the figure -# generation script is generated from it by patching. -source/code/figure/.%_flag: source/code/figure/%.py - cd $(dir $<) && python3 $(notdir $<) - @if [ ! -f $<.diff -o $< -nt $<.diff ]; then \ - wiggle --diff --lines source/code/include/$(notdir $<) $< >$<.diff; \ - touch -r $< $<.diff; \ - fi - @rm -f $<.porig - @touch $@ +clean: + rm -f $(GENERATEDPDF) + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +latex: Makefile $(GENERATEDPDF) + cd .. ; python setup.py build ; cd - + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + cd .. ; python setup.py build ; cd - + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/source/code/README b/doc/source/code/README deleted file mode 100644 index 896e658aca4cc6ec6d2d757dab099beca7bea2d1..0000000000000000000000000000000000000000 --- a/doc/source/code/README +++ /dev/null @@ -1,42 +0,0 @@ -This directory contains the code examples from the documentation. -Most scripts are present in three related but different versions that -correspond to three different usages. - -* Subdirectory 'figure': scripts used for figure generation. Figures - are not displayed but saved to disk. - -* Subdirectory 'include': scripts that display figures on screen. - They contain commented marks for including snippets in the - documentation. - -* Subdirectory 'download': complete scripts to be offered for download - by readers. Like 'include' but with the include marks removed. - -Most scripts are extracted from corresponding '*.py.diff' files inside -'figure/'. These are patches from the 'include' version to the -'figure' version. The patches include complete context and as such -can be used to recreate both files. It's these patches that are kept -under version control. - -running 'make html' or 'make latex' inside '/doc' will automatically -update all these scripts according to the following scheme: - - ---->------------->------ - / \ - / download/x.py \ -figure/x.py.diff ^ \ - ^ \ | \ - | -> include/x.py ---(patch)---> figure/x.py - | | | - | | | - \ v / - ----<----------(diff)--------------<-------- - -Thus, it is possible to update figure/x.py.diff, include/x.py or -figure/x.py and any changes will be propagated automatically when -'make' is run. (Only download/x.py is a dead end.) The user will be -informed about any conflicts. The makefile will only update files -that are older than their sources and is careful to propagate time -stamps in order to avoid infinite loops. - -Editing only figure/x.py.diff is a sure way to avoid any conflicts. diff --git a/doc/source/code/figure/_defs.py b/doc/source/code/figure/_defs.py deleted file mode 100644 index 985e2e9a3cc8f0057e53ea9726ff252975ad7343..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/_defs.py +++ /dev/null @@ -1,34 +0,0 @@ -################################################################ -# Make matplotlib work without X11 -################################################################ -import matplotlib -matplotlib.use('Agg') - -################################################################ -# Prepend Kwant's build directory to sys.path -################################################################ -import sys -from distutils.util import get_platform -sys.path.insert(0, "../../../../build/lib.{0}-{1}.{2}".format( - get_platform(), *sys.version_info[:2])) - -################################################################ -# Define constants for plotting -################################################################ -pt_to_in = 1. / 72. - -# Default width of figures in pts -figwidth_pt = 600 -figwidth_in = figwidth_pt * pt_to_in - -# Width for smaller figures -figwidth_small_pt = 400 -figwidth_small_in = figwidth_small_pt * pt_to_in - -# Sizes for matplotlib figures -mpl_width_in = figwidth_pt * pt_to_in -mpl_label_size = None # font sizes in points -mpl_tick_size = None - -# dpi for conversion from inches -dpi = 90 diff --git a/doc/source/code/figure/ab_ring.py.diff b/doc/source/code/figure/ab_ring.py.diff deleted file mode 100644 index c4fb178b462033ee9f0cdc906c4cfb5f349e403b..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/ab_ring.py.diff +++ /dev/null @@ -1,202 +0,0 @@ -@@ -1,127 +1,196 @@ - # 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 - -+import _defs - from cmath import exp - from math import pi - - import kwant - - # For plotting - from matplotlib import pyplot - - - #HIDDEN_BEGIN_eusz - def make_system(a=1, t=1.0, W=10, r1=10, r2=20): - # 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) - #HIDDEN_END_eusz - - # and add the corresponding lattice points using the `shape`-function - #HIDDEN_BEGIN_lcak - syst[lat.shape(ring, (0, r1 + 1))] = 4 * t - syst[lat.neighbors()] = -t - #HIDDEN_END_lcak - - # 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. - #HIDDEN_BEGIN_lvkt - 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 - #HIDDEN_END_lvkt - - #### Define the leads. #### - # left lead - #HIDDEN_BEGIN_qwgr - 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 - #HIDDEN_END_qwgr - - #### Attach the leads and return the system. #### - #HIDDEN_BEGIN_skbz - syst.attach_lead(lead) - syst.attach_lead(lead.reversed()) - #HIDDEN_END_skbz - - return syst - - -+def make_system_note1(a=1, t=1.0, W=10, r1=10, r2=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 ( 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) -+ return syst -+ -+ -+def make_system_note2(a=1, t=1.0, W=10, r1=10, r2=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)) -+ return syst -+ -+ - 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() -+ fig = pyplot.figure() - pyplot.plot(normalized_fluxes, data) -- pyplot.xlabel("flux [flux quantum]") -- pyplot.ylabel("conductance [e^2/h]") -- pyplot.show() -+ pyplot.xlabel("flux [flux quantum]", -+ fontsize=_defs.mpl_label_size) -+ pyplot.ylabel("conductance [e^2/h]", -+ fontsize=_defs.mpl_label_size) -+ pyplot.setp(fig.get_axes()[0].get_xticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ pyplot.setp(fig.get_axes()[0].get_yticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ fig.set_size_inches(_defs.mpl_width_in, _defs.mpl_width_in * 3. / 4.) -+ fig.subplots_adjust(left=0.15, right=0.95, top=0.95, bottom=0.15) -+ fig.savefig("ab_ring_result.pdf") -+ fig.savefig("ab_ring_result.png", dpi=_defs.dpi) - - - def main(): - syst = make_system() - - # Check that the system looks as intended. -- kwant.plot(syst) -+ size = (_defs.figwidth_in, _defs.figwidth_in) -+ for extension in ('pdf', 'png'): -+ kwant.plot(syst, file="ab_ring_syst." + extension, -+ fig_size=size, dpi=_defs.dpi) -+ - - # Finalize the system. - syst = syst.finalized() - - # We should see a conductance that is periodic with the flux quantum - plot_conductance(syst, energy=0.15, fluxes=[0.01 * i * 3 * 2 * pi - for i in range(100)]) - - -+ # Finally, some plots needed for the notes -+ syst = make_system_note1() -+ for extension in ('pdf', 'png'): -+ kwant.plot(syst, file="ab_ring_note1." + extension, -+ fig_size=size, dpi=_defs.dpi) -+ syst = make_system_note2() -+ for extension in ('pdf', 'png'): -+ kwant.plot(syst, file="ab_ring_note2." + extension, -+ fig_size=size, dpi=_defs.dpi) -+ -+ - # 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() diff --git a/doc/source/code/figure/band_structure.py.diff b/doc/source/code/figure/band_structure.py.diff deleted file mode 100644 index 8a2652072aea317f4a6e5090497c37b43afdef15..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/band_structure.py.diff +++ /dev/null @@ -1,67 +0,0 @@ -@@ -1,52 +1,62 @@ - # 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 _defs - import kwant - - # For plotting. - from matplotlib import pyplot - - #HIDDEN_BEGIN_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 - #HIDDEN_END_zxip - - - #HIDDEN_BEGIN_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() -+ fig = kwant.plotter.bands(lead, show=False) -+ pyplot.xlabel("momentum [(lattice constant)^-1]", -+ fontsize=_defs.mpl_label_size) -+ pyplot.ylabel("energy [t]", fontsize=_defs.mpl_label_size) -+ pyplot.setp(fig.get_axes()[0].get_xticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ pyplot.setp(fig.get_axes()[0].get_yticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ fig.set_size_inches(_defs.mpl_width_in, _defs.mpl_width_in * 3. / 4.) -+ fig.subplots_adjust(left=0.15, right=0.95, top=0.95, bottom=0.15) -+ for extension in ('pdf', 'png'): -+ fig.savefig("band_structure_result." + extension, dpi=_defs.dpi) -+ - #HIDDEN_END_pejz - - - # 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() diff --git a/doc/source/code/figure/closed_system.py.diff b/doc/source/code/figure/closed_system.py.diff deleted file mode 100644 index 748e9eca48693b42d4f54faf64e988e034096101..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/closed_system.py.diff +++ /dev/null @@ -1,168 +0,0 @@ -@@ -1,144 +1,161 @@ - # 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. - -+import _defs - from cmath import exp - import numpy as np - import kwant - - # For eigenvalue computation - #HIDDEN_BEGIN_tibv - import scipy.sparse.linalg as sla - #HIDDEN_END_tibv - - # For plotting - from matplotlib import pyplot - - - def make_system(a=1, t=1.0, r=10): - # Start with an empty tight-binding system and a single square lattice. - # `a` is the lattice constant (by default set to 1 for simplicity). - - #HIDDEN_BEGIN_qlyd - 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 - #HIDDEN_END_qlyd - - - #HIDDEN_BEGIN_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() -+ fig = pyplot.figure() - pyplot.plot(Bfields, energies) -- pyplot.xlabel("magnetic field [arbitrary units]") -- pyplot.ylabel("energy [t]") -- pyplot.show() -+ pyplot.xlabel("magnetic field [arbitrary units]", -+ fontsize=_defs.mpl_label_size) -+ pyplot.ylabel("energy [t]", fontsize=_defs.mpl_label_size) -+ pyplot.setp(fig.get_axes()[0].get_xticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ pyplot.setp(fig.get_axes()[0].get_yticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ fig.set_size_inches(_defs.mpl_width_in, _defs.mpl_width_in * 3. / 4.) -+ fig.subplots_adjust(left=0.15, right=0.95, top=0.95, bottom=0.15) -+ for extension in ('pdf', 'png'): -+ fig.savefig("closed_system_result." + extension, dpi=_defs.dpi) - #HIDDEN_END_yvri - - def sorted_eigs(ev): - evals, evecs = ev - evals, evecs = map(np.array, zip(*sorted(zip(evals, evecs.transpose())))) - return evals, evecs.transpose() - - #HIDDEN_BEGIN_wave - def plot_wave_function(syst, B=0.001): -+ size = (_defs.figwidth_in, _defs.figwidth_in) -+ - # 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) -+ for extension in ('pdf', 'png'): -+ kwant.plotter.map( -+ syst, np.abs(evecs[:, 9])**2, colorbar=False, oversampling=1, -+ file="closed_system_eigenvector." + extension, -+ fig_size=size, dpi=_defs.dpi) - #HIDDEN_END_wave - - - #HIDDEN_BEGIN_current - def plot_current(syst, B=0.001): -+ size = (_defs.figwidth_in, _defs.figwidth_in) -+ - # 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) -+ for extension in ('pdf', 'png'): -+ kwant.plotter.current( -+ syst, current, colorbar=False, -+ file="closed_system_current." + extension, -+ fig_size=size, dpi=_defs.dpi) - #HIDDEN_END_current - - - def main(): - syst = make_system() - -- # Check that the system looks as intended. -- kwant.plot(syst) -- - # Finalize the system. - syst = syst.finalized() - - # The following try-clause can be removed once SciPy 0.9 becomes uncommon. - try: - # 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)]) - - # 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) - plot_current(syst) - except ValueError as e: - if e.message == "Input matrix is not real-valued.": - print("The calculation of eigenvalues failed because of a bug in SciPy 0.9.") - print("Please upgrade to a newer version of SciPy.") - else: - raise - - - # 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() diff --git a/doc/source/code/figure/discretize.py.diff b/doc/source/code/figure/discretize.py.diff deleted file mode 100644 index aebaf3e03c10d291d2b25789c1145b90d6210a15..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/discretize.py.diff +++ /dev/null @@ -1,248 +0,0 @@ -@@ -1,225 +1,239 @@ - # Tutorial 2.9. Processing continuum Hamiltonians with discretize - # =============================================================== - # - # Physics background - # ------------------ - # - tight-binding approximation of continuous Hamiltonians - # - # Kwant features highlighted - # -------------------------- - # - kwant.continuum.discretize - -+import _defs - - import kwant - #HIDDEN_BEGIN_import - import kwant.continuum - #HIDDEN_END_import - import scipy.sparse.linalg - import scipy.linalg - import numpy as np - - # For plotting - import matplotlib as mpl - from matplotlib import pyplot as plt - - -+def save_figure(file_name): -+ if not file_name: -+ return -+ for extension in ('pdf', 'png'): -+ plt.savefig('.'.join((file_name,extension)), -+ dpi=_defs.dpi, bbox_inches='tight') -+ -+ - def stadium_system(L=20, W=20): - #HIDDEN_BEGIN_template - hamiltonian = "k_x**2 + k_y**2 + V(x, y)" - template = kwant.continuum.discretize(hamiltonian) -- print(template) -+ with open('discretizer_verbose.txt', 'w') as f: -+ print(template, file=f) - #HIDDEN_END_template - - #HIDDEN_BEGIN_fill - def stadium(site): - (x, y) = site.pos - x = max(abs(x) - 20, 0) - return x**2 + y**2 < 30**2 - - syst = kwant.Builder() - syst.fill(template, stadium, (0, 0)); - syst = syst.finalized() - #HIDDEN_END_fill - return syst - - - #HIDDEN_BEGIN_plot_eigenstate - def plot_eigenstate(syst, n=2, Vx=.0003, Vy=.0005): - - def potential(x, y): - return Vx * x + Vy * y - - ham = syst.hamiltonian_submatrix(params=dict(V=potential), sparse=True) - evecs = scipy.sparse.linalg.eigsh(ham, k=10, which='SM')[1] - kwant.plotter.map(syst, abs(evecs[:, n])**2, show=False) - #HIDDEN_END_plot_eigenstate -- plt.show() -+ save_figure('discretizer_gs') - - - def qsh_system(a=20, L=2000, W=1000): - #HIDDEN_BEGIN_define_qsh - hamiltonian = """ - + C * identity(4) + M * kron(sigma_0, sigma_z) - - B * (k_x**2 + k_y**2) * kron(sigma_0, sigma_z) - - D * (k_x**2 + k_y**2) * kron(sigma_0, sigma_0) - + A * k_x * kron(sigma_z, sigma_x) - - A * k_y * kron(sigma_0, sigma_y) - """ - - template = kwant.continuum.discretize(hamiltonian, grid=a) - #HIDDEN_END_define_qsh - - #HIDDEN_BEGIN_define_qsh_build - def shape(site): - (x, y) = site.pos - return (0 <= y < W and 0 <= x < L) - - def lead_shape(site): - (x, y) = site.pos - return (0 <= y < W) - - syst = kwant.Builder() - syst.fill(template, shape, (0, 0)) - - lead = kwant.Builder(kwant.TranslationalSymmetry([-a, 0])) - lead.fill(template, lead_shape, (0, 0)) - - syst.attach_lead(lead) - syst.attach_lead(lead.reversed()) - - syst = syst.finalized() - #HIDDEN_END_define_qsh_build - return syst - - - def analyze_qsh(): - params = dict(A=3.65, B=-68.6, D=-51.1, M=-0.01, C=0) - syst = qsh_system() - - #HIDDEN_BEGIN_plot_qsh_band - kwant.plotter.bands(syst.leads[0], params=params, - momenta=np.linspace(-0.3, 0.3, 201), show=False) - #HIDDEN_END_plot_qsh_band - - plt.grid() - plt.xlim(-.3, 0.3) - plt.ylim(-0.05, 0.05) - plt.xlabel('momentum [1/A]') - plt.ylabel('energy [eV]') -- plt.show() -+ save_figure('discretizer_qsh_band') - #HIDDEN_BEGIN_plot_qsh_wf -+ - # get scattering wave functions at E=0 - wf = kwant.wave_function(syst, energy=0, params=params) - - # prepare density operators - sigma_z = np.array([[1, 0], [0, -1]]) - prob_density = kwant.operator.Density(syst, np.eye(4)) - spin_density = kwant.operator.Density(syst, np.kron(sigma_z, np.eye(2))) - - # calculate expectation values and plot them - wf_sqr = sum(prob_density(psi) for psi in wf(0)) # states from left lead - rho_sz = sum(spin_density(psi) for psi in wf(0)) # states from left lead - - fig, (ax1, ax2) = plt.subplots(1, 2, sharey=True, figsize=(16, 4)) - kwant.plotter.map(syst, wf_sqr, ax=ax1) - kwant.plotter.map(syst, rho_sz, ax=ax2) - #HIDDEN_END_plot_qsh_wf - ax = ax1 - im = [obj for obj in ax.get_children() - if isinstance(obj, mpl.image.AxesImage)][0] - fig.colorbar(im, ax=ax) - - ax = ax2 - im = [obj for obj in ax.get_children() - if isinstance(obj, mpl.image.AxesImage)][0] - fig.colorbar(im, ax=ax) - - ax1.set_title('Probability density') - ax2.set_title('Spin density') -- plt.show() -+ save_figure('discretizer_qsh_wf') - - - def lattice_spacing(): - #HIDDEN_BEGIN_ls_def - hamiltonian = "k_x**2 * identity(2) + alpha * k_x * sigma_y" - params = dict(alpha=.5) - #HIDDEN_END_ls_def - - def plot(ax, a=1): - #HIDDEN_BEGIN_ls_hk_cont - h_k = kwant.continuum.lambdify(hamiltonian, locals=params) - k_cont = np.linspace(-4, 4, 201) - e_cont = [scipy.linalg.eigvalsh(h_k(k_x=ki)) for ki in k_cont] - #HIDDEN_END_ls_hk_cont - - #HIDDEN_BEGIN_ls_hk_tb - template = kwant.continuum.discretize(hamiltonian, grid=a) - syst = kwant.wraparound.wraparound(template).finalized() - - def h_k(k_x): - p = dict(k_x=k_x, **params) - return syst.hamiltonian_submatrix(params=p) - - k_tb = np.linspace(-np.pi/a, np.pi/a, 201) - e_tb = [scipy.linalg.eigvalsh(h_k(k_x=a*ki)) for ki in k_tb] - #HIDDEN_END_ls_hk_tb - - ax.plot(k_cont, e_cont, 'r-') - ax.plot(k_tb, e_tb, 'k-') - - ax.plot([], [], 'r-', label='continuum') - ax.plot([], [], 'k-', label='tight-binding') - - ax.set_xlim(-4, 4) - ax.set_ylim(-1, 14) - ax.set_title('a={}'.format(a)) - - ax.set_xlabel('momentum [a.u.]') - ax.set_ylabel('energy [a.u.]') - ax.grid() - ax.legend() - - _, (ax1, ax2) = plt.subplots(1, 2, sharey=True, figsize=(12, 4)) - - plot(ax1, a=1) - plot(ax2, a=.25) -- plt.show() -+ save_figure('discretizer_lattice_spacing') - - - def substitutions(): - #HIDDEN_BEGIN_subs_1 - sympify = kwant.continuum.sympify - subs = {'sx': [[0, 1], [1, 0]], 'sz': [[1, 0], [0, -1]]} - - e = ( - sympify('[[k_x**2, alpha * k_x], [k_x * alpha, -k_x**2]]'), - sympify('k_x**2 * sigma_z + alpha * k_x * sigma_x'), - sympify('k_x**2 * sz + alpha * k_x * sx', locals=subs), - ) - -- print(e[0] == e[1] == e[2]) -+ with open('discretizer_subs_1.txt', 'w') as f: -+ print(e[0] == e[1] == e[2], file=f) - #HIDDEN_END_subs_1 - - #HIDDEN_BEGIN_subs_2 - subs = {'A': 'A(x) + B', 'V': 'V(x) + V_0', 'C': 5} -- print(sympify('k_x * A * k_x + V + C', locals=subs)) -+ with open('discretizer_subs_2.txt', 'w') as f: -+ print(sympify('k_x * A * k_x + V + C', locals=subs), file=f) - #HIDDEN_END_subs_2 - - - def main(): - #HIDDEN_BEGIN_symbolic_discretization - template = kwant.continuum.discretize('k_x * A(x) * k_x') -- print(template) -+ with open('discretizer_intro_verbose.txt', 'w') as f: -+ print(template, file=f) - #HIDDEN_END_symbolic_discretization - - syst = stadium_system() - plot_eigenstate(syst) - - analyze_qsh() - lattice_spacing() - substitutions() - - # 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() diff --git a/doc/source/code/figure/faq.py.diff b/doc/source/code/figure/faq.py.diff deleted file mode 100644 index 7c8889dceec701572fde707e7c5aed3d5cf9f427..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/faq.py.diff +++ /dev/null @@ -1,483 +0,0 @@ -@@ -1,450 +1,479 @@ - # Frequently asked questions - # - # This script is a disorganized collection of code snippets. As a whole, it is - # not meant as an example of good programming practice. - -+import _defs - import kwant - import numpy as np - import tinyarray - import matplotlib - from matplotlib import pyplot as plt - matplotlib.rcParams['figure.figsize'] = (3.5, 3.5) - - -+def save_figure(file_name): -+ for extension in ('pdf', 'png'): -+ plt.savefig('.'.join((file_name, extension)), -+ dpi=_defs.dpi, bbox_inches='tight') -+ -+ - #### What is a Site? - - #HIDDEN_BEGIN_site - a = 1 - lat = kwant.lattice.square(a) - syst = kwant.Builder() - - syst[lat(1, 0)] = 4 - syst[lat(1, 1)] = 4 - - kwant.plot(syst) -+save_figure("faq_site") - #HIDDEN_END_site - - - #### What is a hopping? - - a = 1 - lat = kwant.lattice.square(a) - syst = kwant.Builder() - - syst[lat(1, 0)] = 4 - syst[lat(1, 1)] = 4 - #HIDDEN_BEGIN_hopping - syst[(lat(1, 0), lat(1, 1))] = 1j - #HIDDEN_END_hopping - - kwant.plot(syst) -+save_figure("faq_hopping") - - - #### What is a lattice? - - #HIDDEN_BEGIN_lattice_monatomic - # Two monatomic lattices - primitive_vectors = [(1, 0), (0, 1)] - lat_a = kwant.lattice.Monatomic(primitive_vectors, offset=(0, 0)) - lat_b = kwant.lattice.Monatomic(primitive_vectors, offset=(0.5, 0.5)) - # lat1 is equivalent to kwant.lattice.square() - - syst = kwant.Builder() - - syst[lat_a(0, 0)] = 4 - syst[lat_b(0, 0)] = 4 - - kwant.plot(syst) -+save_figure("faq_lattice") - #HIDDEN_END_lattice_monatomic - - #HIDDEN_BEGIN_lattice_polyatomic - # One polyatomic lattice containing two sublattices - lat = kwant.lattice.Polyatomic([(1, 0), (0, 1)], [(0, 0), (0.5, 0.5)]) - sub_a, sub_b = lat.sublattices - #HIDDEN_END_lattice_polyatomic - - #### How to make a hole in a system? - - #HIDDEN_BEGIN_hole - # Define the lattice and the (empty) system - a = 2 - lat = kwant.lattice.cubic(a) - syst = kwant.Builder() - - L = 10 - W = 10 - H = 2 - - # Add sites to the system in a cuboid - - syst[(lat(i, j, k) for i in range(L) for j in range(W) for k in range(H))] = 4 - kwant.plot(syst) -+save_figure("faq_hole1") - - # Delete sites to create a hole - - def in_hole(site): - x, y, z = site.pos / a - (L/2, W/2, H/2) # position relative to centre - return abs(x) < L / 4 and abs(y) < W / 4 - - for site in filter(in_hole, list(syst.sites())): - del syst[site] - - kwant.plot(syst) -+save_figure("faq_hole2") - #HIDDEN_END_hole - - - #### How can we get access to the sites of our system? - - builder = kwant.Builder() - lat = kwant.lattice.square() - builder[(lat(i, j) for i in range(3) for j in range(3))] = 4 - #HIDDEN_BEGIN_sites1 - # Before finalizing the system - - sites = list(builder.sites()) # sites() doe *not* return a list - #HIDDEN_END_sites1 - #HIDDEN_BEGIN_sites2 - # After finalizing the system - syst = builder.finalized() - sites = syst.sites # syst.sites is an actual list - #HIDDEN_END_sites2 - #HIDDEN_BEGIN_sites3 - i = syst.id_by_site[lat(0, 2)] # we want the id of the site lat(0, 2) - #HIDDEN_END_sites3 - - - #### How to plot a polyatomic lattice with different colors? - - #HIDDEN_BEGIN_colors1 - lat = kwant.lattice.kagome() - syst = kwant.Builder() - - a, b, c = lat.sublattices # The kagome lattice has 3 sublattices - #HIDDEN_END_colors1 - - #HIDDEN_BEGIN_colors2 - # Plot sites from different families in different colors - def family_color(site): - if site.family == a: - return 'red' - if site.family == b: - return 'green' - else: - return 'blue' - - def plot_system(syst): - kwant.plot(syst, site_lw=0.1, site_color=family_color) - - ## Add sites and hoppings. - for i in range(4): - for j in range (4): - syst[a(i, j)] = 4 - syst[b(i, j)] = 4 - syst[c(i, j)] = 4 - - syst[lat.neighbors()] = -1 - - ## Plot the system. - plot_system(syst) -+save_figure("faq_colors") - #HIDDEN_END_colors2 - -- - #### How to create all hoppings in a given direction using Hoppingkind? - - # Monatomic lattice - - #HIDDEN_BEGIN_direction1 - # Create hopping between neighbors with HoppingKind - a = 1 - syst = kwant.Builder() - lat = kwant.lattice.square(a) - syst[ (lat(i, j) for i in range(5) for j in range(5)) ] = 4 - - syst[kwant.builder.HoppingKind((1, 0), lat)] = -1 - kwant.plot(syst) -+save_figure("faq_direction1") - #HIDDEN_END_direction1 - - # Polyatomic lattice - - lat = kwant.lattice.kagome() - syst = kwant.Builder() - - a, b, c = lat.sublattices # The kagome lattice has 3 sublattices - - def family_color(site): - if site.family == a: - return 'red' - if site.family == b: - return 'green' - else: - return 'blue' - - def plot_system(syst): - kwant.plot(syst, site_size=0.15, site_lw=0.05, site_color=family_color) - - - for i in range(4): - for j in range (4): - syst[a(i, j)] = 4 - syst[b(i, j)] = 4 - syst[c(i, j)] = 4 - - - #HIDDEN_BEGIN_direction2 - # equivalent to syst[kwant.builder.HoppingKind((0, 1), b)] = -1 - syst[kwant.builder.HoppingKind((0, 1), b, b)] = -1 - #HIDDEN_END_direction2 - plot_system(syst) -+save_figure("faq_direction2") - # Delete the hoppings previously created - del syst[kwant.builder.HoppingKind((0, 1), b, b)] - #HIDDEN_BEGIN_direction3 - syst[kwant.builder.HoppingKind((0, 0), a, b)] = -1 - syst[kwant.builder.HoppingKind((0, 0), a, c)] = -1 - syst[kwant.builder.HoppingKind((0, 0), c, b)] = -1 - #HIDDEN_END_direction3 - plot_system(syst) -+save_figure("faq_direction3") - - - #### How to create the hoppings between adjacent sites? - - # Monatomic lattice - - #HIDDEN_BEGIN_adjacent1 - # Create hoppings with lat.neighbors() - syst = kwant.Builder() - lat = kwant.lattice.square() - syst[(lat(i, j) for i in range(3) for j in range(3))] = 4 - - syst[lat.neighbors()] = -1 # Equivalent to lat.neighbors(1) - kwant.plot(syst) -+save_figure("faq_adjacent1") - - del syst[lat.neighbors()] # Delete all nearest-neighbor hoppings - syst[lat.neighbors(2)] = -1 - - kwant.plot(syst) -+save_figure("faq_adjacent2") - #HIDDEN_END_adjacent1 - - # Polyatomic lattice - - #HIDDEN_BEGIN_FAQ6 - - # Hoppings using .neighbors() - #HIDDEN_BEGIN_adjacent2 - # Create the system - lat = kwant.lattice.kagome() - syst = kwant.Builder() - a, b, c = lat.sublattices # The kagome lattice has 3 sublattices - - for i in range(4): - for j in range (4): - syst[a(i, j)] = 4 # red - syst[b(i, j)] = 4 # green - syst[c(i, j)] = 4 # blue - - syst[lat.neighbors()] = -1 - #HIDDEN_END_adjacent2 - plot_system(syst) -+save_figure("faq_adjacent3") - del syst[lat.neighbors()] # Delete the hoppings previously created - #HIDDEN_BEGIN_adjacent3 - syst[a.neighbors()] = -1 - #HIDDEN_END_adjacent3 - plot_system(syst) -+save_figure("faq_adjacent4") - del syst[a.neighbors()] # Delete the hoppings previously created - - - syst[lat.neighbors(2)] = -1 - plot_system(syst) - del syst[lat.neighbors(2)] - - - #### How to create a lead with a lattice different from the scattering region? - - # Plot sites from different families in different colors - - def plot_system(syst): - - def family_color(site): - if site.family == subA: - return 'blue' - if site.family == subB: - return 'yellow' - else: - return 'green' - - kwant.plot(syst, site_lw=0.1, site_color=family_color) - - - #HIDDEN_BEGIN_different_lattice1 - # Define the scattering Region - L = 5 - W = 5 - - lat = kwant.lattice.honeycomb() - subA, subB = lat.sublattices - - syst = kwant.Builder() - syst[(subA(i, j) for i in range(L) for j in range(W))] = 4 - syst[(subB(i, j) for i in range(L) for j in range(W))] = 4 - syst[lat.neighbors()] = -1 - #HIDDEN_END_different_lattice1 - plot_system(syst) -+save_figure("faq_different_lattice1") - - #HIDDEN_BEGIN_different_lattice2 - # Create a lead - lat_lead = kwant.lattice.square() - sym_lead1 = kwant.TranslationalSymmetry((0, 1)) - - lead1 = kwant.Builder(sym_lead1) - lead1[(lat_lead(i, 0) for i in range(2, 7))] = 4 - lead1[lat_lead.neighbors()] = -1 - #HIDDEN_END_different_lattice2 - plot_system(lead1) -+save_figure("faq_different_lattice2") - - #HIDDEN_BEGIN_different_lattice3 - syst[(lat_lead(i, 5) for i in range(2, 7))] = 4 - syst[lat_lead.neighbors()] = -1 - - # Manually attach sites from graphene to square lattice - syst[((lat_lead(i+2, 5), subB(i, 4)) for i in range(5))] = -1 - #HIDDEN_END_different_lattice3 - plot_system(syst) -+save_figure("faq_different_lattice3") - - #HIDDEN_BEGIN_different_lattice4 - syst.attach_lead(lead1) - #HIDDEN_END_different_lattice4 - plot_system(syst) -+save_figure("faq_different_lattice4") - - - #### How to cut a finite system out of a system with translationnal symmetries? - - #HIDDEN_BEGIN_fill1 - # Create 3d model. - cubic = kwant.lattice.cubic() - sym_3d = kwant.TranslationalSymmetry([1, 0, 0], [0, 1, 0], [0, 0, 1]) - model = kwant.Builder(sym_3d) - model[cubic(0, 0, 0)] = 4 - model[cubic.neighbors()] = -1 - #HIDDEN_END_fill1 - - #HIDDEN_BEGIN_fill2 - # Build scattering region (white). - def cuboid_shape(site): - x, y, z = abs(site.pos) - return x < 4 and y < 10 and z < 3 - - cuboid = kwant.Builder() - cuboid.fill(model, cuboid_shape, (0, 0, 0)); - #HIDDEN_END_fill2 - kwant.plot(cuboid); -+save_figure("faq_fill2") - - #HIDDEN_BEGIN_fill3 - # Build electrode (black). - def electrode_shape(site): - x, y, z = site.pos - (0, 5, 2) - return y**2 + z**2 < 2.3**2 - - electrode = kwant.Builder(kwant.TranslationalSymmetry([1, 0, 0])) - electrode.fill(model, electrode_shape, (0, 5, 2)) # lead - - # Scattering region - cuboid.fill(electrode, lambda s: abs(s.pos[0]) < 7, (0, 5, 4)) - - cuboid.attach_lead(electrode) - #HIDDEN_END_fill3 - kwant.plot(cuboid); -+save_figure("faq_fill3") - - - #### How does Kwant order the propagating modes of a lead? - - #HIDDEN_BEGIN_pm - lat = kwant.lattice.square() - - lead = kwant.Builder(kwant.TranslationalSymmetry((-1, 0))) - lead[(lat(0, i) for i in range(3))] = 4 - lead[lat.neighbors()] = -1 - - flead = lead.finalized() - - E = 2.5 - prop_modes, _ = flead.modes(energy=E) - #HIDDEN_END_pm - - def plot_and_label_modes(lead, E): - # Plot the different modes - pmodes, _ = lead.modes(energy=E) - kwant.plotter.bands(lead, show=False) - for i, k in enumerate(pmodes.momenta): - plt.plot(k, E, 'ko') - plt.annotate(str(i), xy=(k, E), xytext=(-5, 8), - textcoords='offset points', - bbox=dict(boxstyle='round,pad=0.1',fc='white', alpha=0.7)) - plt.plot([-3, 3], [E, E], 'r--') - plt.ylim(E-1, E+1) - plt.xlim(-2, 2) - plt.xlabel("momentum") - plt.ylabel("energy") - plt.show() - - plot_and_label_modes(flead, E) -+save_figure('faq_pm1') - - # More involved example - - s0 = np.eye(2) - sz = np.array([[1, 0], [0, -1]]) - - lead2 = kwant.Builder(kwant.TranslationalSymmetry((-1, 0))) - - lead2[(lat(0, i) for i in range(2))] = np.diag([1.8, -1]) - lead2[lat.neighbors()] = -1 * sz - - flead2 = lead2.finalized() - - plot_and_label_modes(flead2, 1) -+save_figure('faq_pm2') - - - #### How does Kwant order components of an individual wavefunction? - def circle(R): - return lambda r: np.linalg.norm(r) < R - - - def make_system(lat): - norbs = lat.norbs - syst = kwant.Builder() - syst[lat.shape(circle(3), (0, 0))] = 4 * np.eye(norbs) - syst[lat.neighbors()] = -1 * np.eye(norbs) - - lead = kwant.Builder(kwant.TranslationalSymmetry((-1, 0))) - lead[(lat(0, i) for i in range(-1, 2))] = 4 * np.eye(norbs) - lead[lat.neighbors()] = -1 * np.eye(norbs) - - syst.attach_lead(lead) - syst.attach_lead(lead.reversed()) - - return syst.finalized() - - - #HIDDEN_BEGIN_ord1 - lat = kwant.lattice.square(norbs=1) - syst = make_system(lat) - scattering_states = kwant.wave_function(syst, energy=1) - wf = scattering_states(0)[0] # scattering state from lead 0 incoming in mode 0 - - idx = syst.id_by_site[lat(0, 0)] # look up index of site - --print('wavefunction on lat(0, 0): ', wf[idx]) -+with open('faq_ord1.txt', 'w') as f: -+ print('wavefunction on lat(0, 0): ', wf[idx], file=f) - #HIDDEN_END_ord1 - - #HIDDEN_BEGIN_ord2 - lat = kwant.lattice.square(norbs=2) - syst = make_system(lat) - scattering_states = kwant.wave_function(syst, energy=1) - wf = scattering_states(0)[0] # scattering state from lead 0 incoming in mode 0 - - idx = syst.id_by_site[lat(0, 0)] # look up index of site - - # Group consecutive degrees of freedom from 'wf' together; these correspond - # to degrees of freedom on the same site. - wf = wf.reshape(-1, 2) - --print('wavefunction on lat(0, 0): ', wf[idx]) -+with open('faq_ord2.txt', 'w') as f: -+ print('wavefunction on lat(0, 0): ', wf[idx], file=f) - #HIDDEN_END_ord2 diff --git a/doc/source/code/figure/graphene.py.diff b/doc/source/code/figure/graphene.py.diff deleted file mode 100644 index 1352ed270df555a9cd1aa5c6ef51632590cd7337..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/graphene.py.diff +++ /dev/null @@ -1,217 +0,0 @@ -@@ -1,179 +1,203 @@ - # 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 - -+import _defs - from math import pi, sqrt, tanh - - import kwant - - # For computing eigenvalues - import scipy.sparse.linalg as sla - - # For plotting - from matplotlib import pyplot - - - # Define the graphene lattice - sin_30, cos_30 = (1 / 2, sqrt(3) / 2) - #HIDDEN_BEGIN_hnla - graphene = kwant.lattice.general([(1, 0), (sin_30, cos_30)], - [(0, 0), (0, 1 / sqrt(3))]) - a, b = graphene.sublattices - #HIDDEN_END_hnla - - - #HIDDEN_BEGIN_shzy - def make_system(r=10, w=2.0, pot=0.1): - - #### 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 - #HIDDEN_END_shzy - - # specify the hoppings of the graphene lattice in the - # format expected by builder.HoppingKind - #HIDDEN_BEGIN_hsmc - hoppings = (((0, 0), a, b), ((0, 1), a, b), ((-1, 1), a, b)) - #HIDDEN_END_hsmc - #HIDDEN_BEGIN_bfwb - syst[[kwant.builder.HoppingKind(*hopping) for hopping in hoppings]] = -1 - #HIDDEN_END_bfwb - - # Modify the scattering region - #HIDDEN_BEGIN_efut - del syst[a(0, 0)] - syst[a(-2, 1), b(2, 2)] = -1 - #HIDDEN_END_efut - - #### Define the leads. #### - #HIDDEN_BEGIN_aakh - # 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 - #HIDDEN_END_aakh - - #HIDDEN_BEGIN_kmmw - return syst, [lead0, lead1] - #HIDDEN_END_kmmw - - - #HIDDEN_BEGIN_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) - #HIDDEN_END_zydk - - - 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() -+ fig = pyplot.figure() - pyplot.plot(energies, data) -- pyplot.xlabel("energy [t]") -- pyplot.ylabel("conductance [e^2/h]") -- pyplot.show() -+ pyplot.xlabel("energy [t]", -+ fontsize=_defs.mpl_label_size) -+ pyplot.ylabel("conductance [e^2/h]", -+ fontsize=_defs.mpl_label_size) -+ pyplot.setp(fig.get_axes()[0].get_xticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ pyplot.setp(fig.get_axes()[0].get_yticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ fig.set_size_inches(_defs.mpl_width_in, _defs.mpl_width_in * 3. / 4.) -+ fig.subplots_adjust(left=0.15, right=0.95, top=0.95, bottom=0.15) -+ for extension in ('pdf', 'png'): -+ fig.savefig("graphene_result." + extension, dpi=_defs.dpi) - - - def plot_bandstructure(flead, momenta): - bands = kwant.physics.Bands(flead) - energies = [bands(k) for k in momenta] - -- pyplot.figure() -+ fig = pyplot.figure() - pyplot.plot(momenta, energies) -- pyplot.xlabel("momentum [(lattice constant)^-1]") -- pyplot.ylabel("energy [t]") -- pyplot.show() -+ pyplot.xlabel("momentum [(lattice constant)^-1]", -+ fontsize=_defs.mpl_label_size) -+ pyplot.ylabel("energy [t]", -+ fontsize=_defs.mpl_label_size) -+ pyplot.setp(fig.get_axes()[0].get_xticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ pyplot.setp(fig.get_axes()[0].get_yticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ fig.set_size_inches(_defs.mpl_width_in, _defs.mpl_width_in * 3. / 4.) -+ fig.subplots_adjust(left=0.15, right=0.95, top=0.95, bottom=0.15) -+ for extension in ('pdf', 'png'): -+ fig.savefig("graphene_bs." + extension, dpi=_defs.dpi) - - - #HIDDEN The part of the following code block which begins with family_colors - #HIDDEN is included verbatim in the tutorial text because nested code examples - #HIDDEN are not supported. Remember to update the tutorial text when you - #HIDDEN modify this block. - #HIDDEN_BEGIN_itkk - def main(): - pot = 0.1 - syst, leads = make_system(pot=pot) - - # 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) -+ size = (_defs.figwidth_in, _defs.figwidth_in) -+ for extension in ('pdf', 'png'): -+ kwant.plot(syst, site_color=family_colors, site_lw=0.1, colorbar=False, -+ file="graphene_syst1." + extension, -+ fig_size=size, dpi=_defs.dpi) - #HIDDEN_END_itkk - - # Compute some eigenvalues. - #HIDDEN_BEGIN_jmbi - compute_evs(syst.finalized()) - #HIDDEN_END_jmbi - - # Attach the leads to the system. - for lead in leads: - 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) -+ size = (_defs.figwidth_in, 0.9 * _defs.figwidth_in) -+ for extension in ('pdf', 'png'): -+ kwant.plot(syst, site_color=family_colors, colorbar=False, site_lw=0.1, -+ file="graphene_syst2." + extension, -+ fig_size=size, dpi=_defs.dpi, lead_site_lw=0) - - # Finalize the system. - syst = syst.finalized() - - # Compute the band structure of lead 0. - momenta = [-pi + 0.02 * pi * i for i in range(101)] - plot_bandstructure(syst.leads[0], momenta) - - # Plot conductance. - energies = [-2 * pot + 4. / 50. * pot * i for i in range(51)] - plot_conductance(syst, energies) - - - # 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() diff --git a/doc/source/code/figure/kernel_polynomial_method.py.diff b/doc/source/code/figure/kernel_polynomial_method.py.diff deleted file mode 100644 index 3ac532512ea1d3add0d5604c897aa0e9ceb49399..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/kernel_polynomial_method.py.diff +++ /dev/null @@ -1,374 +0,0 @@ -@@ -1,219 +1,242 @@ - # Tutorial 2.8. Calculating spectral density with the Kernel Polynomial Method - # ============================================================================ - # - # Physics background - # ------------------ - # - Chebyshev polynomials, random trace approximation, spectral densities. - # - # Kwant features highlighted - # -------------------------- - # - kpm module,kwant operators. - - import scipy - -+import _defs -+from contextlib import redirect_stdout -+ - # For plotting - from matplotlib import pyplot as plt - - #HIDDEN_BEGIN_sys1 - # necessary imports - import kwant - import numpy as np - - - # define the system - def make_syst(r=30, t=-1, a=1): - syst = kwant.Builder() - lat = kwant.lattice.honeycomb(a, norbs=1) - - def circle(pos): - x, y = pos - return x ** 2 + y ** 2 < r ** 2 - - syst[lat.shape(circle, (0, 0))] = 0. - syst[lat.neighbors()] = t - syst.eradicate_dangling() - - return syst - #HIDDEN_END_sys1 - - #HIDDEN_BEGIN_sys2 - # define a Haldane system - def make_syst_topo(r=30, a=1, t=1, t2=0.5): - syst = kwant.Builder() - lat = kwant.lattice.honeycomb(a, norbs=1, name=['a', 'b']) - - def circle(pos): - x, y = pos - return x ** 2 + y ** 2 < r ** 2 - - syst[lat.shape(circle, (0, 0))] = 0. - syst[lat.neighbors()] = t - # add second neighbours hoppings - syst[lat.a.neighbors()] = 1j * t2 - syst[lat.b.neighbors()] = -1j * t2 - syst.eradicate_dangling() - - return lat, syst.finalized() - #HIDDEN_END_sys2 - - - #HIDDEN_BEGIN_sys3 - # define the system - def make_syst_staggered(r=30, t=-1, a=1, m=0.1): - syst = kwant.Builder() - lat = kwant.lattice.honeycomb(a, norbs=1) - - def circle(pos): - x, y = pos - return x ** 2 + y ** 2 < r ** 2 - - syst[lat.a.shape(circle, (0, 0))] = m - syst[lat.b.shape(circle, (0, 0))] = -m - syst[lat.neighbors()] = t - syst.eradicate_dangling() - - return syst - #HIDDEN_END_sys3 - - # Plot several density of states curves on the same axes. --def plot_dos(labels_to_data): -+def plot_dos(labels_to_data, file_name=None, ylabel="DoS [a.u.]"): - plt.figure(figsize=(5,4)) - for label, (x, y) in labels_to_data: - plt.plot(x, y.real, label=label, linewidth=2) - plt.legend(loc=2, framealpha=0.5) - plt.xlabel("energy [t]") - plt.ylabel(ylabel) -- plt.show() -+ save_figure(file_name) - plt.clf() - - - # Plot fill density of states plus curves on the same axes. --def plot_dos_and_curves(dos labels_to_data): -+def plot_dos_and_curves(dos, labels_to_data, file_name=None, ylabel="DoS [a.u.]"): - plt.figure(figsize=(5,4)) - plt.fill_between(dos[0], dos[1], label="DoS [a.u.]", - alpha=0.5, color='gray') - for label, (x, y) in labels_to_data: - plt.plot(x, y, label=label, linewidth=2) - plt.legend(loc=2, framealpha=0.5) - plt.xlabel("energy [t]") - plt.ylabel(ylabel) -- plt.show() -+ save_figure(file_name) - plt.clf() - - def site_size_conversion(densities): - return 3 * np.abs(densities) / max(densities) - - - # Plot several local density of states maps in different subplots - def plot_ldos(syst, densities, file_name=None): - fig, axes = plt.subplots(1, len(densities), figsize=(7*len(densities), 7)) - for ax, (title, rho) in zip(axes, densities): - kwant.plotter.density(syst, rho.real, ax=ax) - ax.set_title(title) - ax.set(adjustable='box', aspect='equal') -- plt.show() -+ save_figure(file_name) - plt.clf() - -+def save_figure(file_name): -+ if not file_name: -+ return -+ for extension in ('pdf', 'png'): -+ plt.savefig('.'.join((file_name,extension)), -+ dpi=_defs.dpi, bbox_inches='tight') -+ -+ - def simple_dos_example(): - #HIDDEN_BEGIN_kpm1 - fsyst = make_syst().finalized() - - spectrum = kwant.kpm.SpectralDensity(fsyst) - #HIDDEN_END_kpm1 - - #HIDDEN_BEGIN_kpm2 - energies, densities = spectrum() - #HIDDEN_END_kpm2 - - #HIDDEN_BEGIN_kpm3 - energy_subset = np.linspace(0, 2) - density_subset = spectrum(energy_subset) - #HIDDEN_END_kpm3 - - plot_dos([ - ('densities', (energies, densities)), - ('density subset', (energy_subset, density_subset)), -- ]) -+ ], -+ file_name='kpm_dos' -+ ) - - - def dos_integrating_example(fsyst): - spectrum = kwant.kpm.SpectralDensity(fsyst) - - #HIDDEN_BEGIN_int1 -- print('identity resolution:', spectrum.integrate()) -+ with open('kpm_normalization.txt', 'w') as f: -+ with redirect_stdout(f): -+ print('identity resolution:', spectrum.integrate()) - #HIDDEN_END_int1 - - #HIDDEN_BEGIN_int2 - # Fermi energy 0.1 and temperature 0.2 - fermi = lambda E: 1 / (np.exp((E - 0.1) / 0.2) + 1) - -- print('number of filled states:', spectrum.integrate(fermi)) -+ with open('kpm_total_states.txt', 'w') as f: -+ with redirect_stdout(f): -+ print('number of filled states:', spectrum.integrate(fermi)) - #HIDDEN_END_int2 - - - def increasing_accuracy_example(fsyst): - spectrum = kwant.kpm.SpectralDensity(fsyst) - original_dos = spectrum() # get unaltered DoS - - #HIDDEN_BEGIN_acc1 - spectrum.add_moments(energy_resolution=0.03) - #HIDDEN_END_acc1 - - increased_resolution_dos = spectrum() - - plot_dos([ - ('density', original_dos), - ('higher energy resolution', increased_resolution_dos), -- ]) -+ ], -+ file_name='kpm_dos_acc' -+ ) - - #HIDDEN_BEGIN_acc2 - spectrum.add_moments(100) - spectrum.add_vectors(5) - #HIDDEN_END_acc2 - - increased_moments_dos = spectrum() - - plot_dos([ - ('density', original_dos), - ('higher number of moments', increased_moments_dos), -- ]) -+ ], -+ file_name='kpm_dos_r' -+ ) - - - def operator_example(fsyst): - #HIDDEN_BEGIN_op1 - # identity matrix - matrix_op = scipy.sparse.eye(len(fsyst.sites)) - matrix_spectrum = kwant.kpm.SpectralDensity(fsyst, operator=matrix_op) - #HIDDEN_END_op1 - - #HIDDEN_BEGIN_op2 - # 'sum=True' means we sum over all the sites - kwant_op = kwant.operator.Density(fsyst, sum=True) - operator_spectrum = kwant.kpm.SpectralDensity(fsyst, operator=kwant_op) - #HIDDEN_END_op2 - - plot_dos([ - ('identity matrix', matrix_spectrum()), - ('kwant.operator.Density', operator_spectrum()), - ]) - - - def ldos_example(fsyst): - #HIDDEN_BEGIN_op3 - # 'sum=False' is the default, but we include it explicitly here for clarity. - kwant_op = kwant.operator.Density(fsyst, sum=False) - local_dos = kwant.kpm.SpectralDensity(fsyst, operator=kwant_op) - #HIDDEN_END_op3 - - #HIDDEN_BEGIN_op4 - zero_energy_ldos = local_dos(energy=0) - finite_energy_ldos = local_dos(energy=1) - plot_ldos(fsyst, [ - ('energy = 0', zero_energy_ldos), - ('energy = 1', finite_energy_ldos) -- ]) -+ ], -+ file_name='kpm_ldos' -+ ) - #HIDDEN_END_op4 - - - def ldos_sites_example(): - fsyst = make_syst_staggered().finalized() - #HIDDEN_BEGIN_op5 - # find 'A' and 'B' sites in the unit cell at the center of the disk - center_tag = np.array([0, 0]) - where = lambda s: s.tag == center_tag - # make local vectors - vector_factory = kwant.kpm.LocalVectors(fsyst, where) - #HIDDEN_END_op5 - - #HIDDEN_BEGIN_op6 - # 'num_vectors' can be unspecified when using 'LocalVectors' - local_dos = kwant.kpm.SpectralDensity(fsyst, num_vectors=None, - vector_factory=vector_factory, - mean=False) - energies, densities = local_dos() - plot_dos([ - ('A sublattice', (energies, densities[:, 0])), - ('B sublattice', (energies, densities[:, 1])), -- ]) -+ ], -+ file_name='kpm_ldos_sites' -+ ) - #HIDDEN_END_op6 - - - def vector_factory_example(fsyst): - spectrum = kwant.kpm.SpectralDensity(fsyst) - #HIDDEN_BEGIN_fact1 - # construct a generator of vectors with n random elements -1 or +1. - n = fsyst.hamiltonian_submatrix(sparse=True).shape[0] - def binary_vectors(): - while True: - yield np.rint(np.random.random_sample(n)) * 2 - 1 - - custom_factory = kwant.kpm.SpectralDensity(fsyst, - vector_factory=binary_vectors()) - #HIDDEN_END_fact1 - plot_dos([ - ('default vector factory', spectrum()), - ('binary vector factory', custom_factory()), - ]) - - - def bilinear_map_operator_example(fsyst): - #HIDDEN_BEGIN_blm - rho = kwant.operator.Density(fsyst, sum=True) - - # sesquilinear map that does the same thing as `rho` - def rho_alt(bra, ket): - return np.vdot(bra, ket) - - rho_spectrum = kwant.kpm.SpectralDensity(fsyst, operator=rho) - rho_alt_spectrum = kwant.kpm.SpectralDensity(fsyst, operator=rho_alt) - #HIDDEN_END_blm - - plot_dos([ - ('kwant.operator.Density', rho_spectrum()), - ('bilinear operator', rho_alt_spectrum()), - ]) - - def conductivity_example(): - #HIDDEN_BEGIN_cond - # construct the Haldane model - lat, fsyst = make_syst_topo() - # find 'A' and 'B' sites in the unit cell at the center of the disk - where = lambda s: np.linalg.norm(s.pos) < 1 - - # component 'xx' - s_factory = kwant.kpm.LocalVectors(fsyst, where) - cond_xx = kwant.kpm.conductivity(fsyst, alpha='x', beta='x', mean=True, - num_vectors=None, vector_factory=s_factory) - # component 'xy' - s_factory = kwant.kpm.LocalVectors(fsyst, where) - cond_xy = kwant.kpm.conductivity(fsyst, alpha='x', beta='y', mean=True, - num_vectors=None, vector_factory=s_factory) - - energies = cond_xx.energies - cond_array_xx = np.array([cond_xx(e, temperature=0.01) for e in energies]) - cond_array_xy = np.array([cond_xy(e, temperature=0.01) for e in energies]) - - # area of the unit cell per site - area_per_site = np.abs(np.cross(*lat.prim_vecs)) / len(lat.sublattices) - cond_array_xx /= area_per_site - cond_array_xy /= area_per_site - #HIDDEN_END_cond - # ldos - s_factory = kwant.kpm.LocalVectors(fsyst, where) - spectrum = kwant.kpm.SpectralDensity(fsyst, num_vectors=None, - vector_factory=s_factory) - - plot_dos_and_curves( - (spectrum.energies, spectrum.densities * 8), - [ - (r'Longitudinal conductivity $\sigma_{xx} / 4$', - (energies, cond_array_xx / 4)), - (r'Hall conductivity $\sigma_{xy}$', - (energies, cond_array_xy))], - ylabel=r'$\sigma [e^2/h]$', - file_name='kpm_cond' - ) - - - def main(): - simple_dos_example() - - fsyst = make_syst().finalized() - - dos_integrating_example(fsyst) - increasing_accuracy_example(fsyst) - operator_example(fsyst) - ldos_example(fsyst) - ldos_sites_example() - vector_factory_example(fsyst) - bilinear_map_operator_example(fsyst) - conductivity_example() - - - # 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() diff --git a/doc/source/code/figure/magnetic_texture.py.diff b/doc/source/code/figure/magnetic_texture.py.diff deleted file mode 100644 index a29a458e43ea5314b09e93426bf907872228f8f9..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/magnetic_texture.py.diff +++ /dev/null @@ -1,286 +0,0 @@ -@@ -1,245 +1,268 @@ - # Tutorial 2.7. Spin textures - # =========================== - # - # Physics background - # ------------------ - # - Spin textures - # - Skyrmions - # - # Kwant features highlighted - # -------------------------- - # - operators - # - plotting vector fields - -+import _defs -+from contextlib import redirect_stdout -+ - 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) - - #HIDDEN_BEGIN_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 - #HIDDEN_END_model - - - #HIDDEN_BEGIN_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 - #HIDDEN_END_syst - - --def plot_vector_field(syst, params): -+def save_plot(fname): -+ for extension in ('.pdf', '.png'): -+ plt.savefig(fname + extension, -+ dpi=_defs.dpi, bbox_inches='tight') -+ -+ -+def plot_vector_field(syst, params, fname=None): - 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) -+ fig, ax = plt.subplots(1, 1, figsize=(9, 7)) - im = ax.quiver(x, y, *m_i, pivot='mid', scale=75) - fig.colorbar(im) -- plt.show() -+ if fname: -+ save_plot(fname) -+ else: -+ plt.show() - - --def plot_densities(syst, densities): -- fig, axes = plt.subplots(1, len(densities)) -+def plot_densities(syst, densities, fname=None): -+ fig, axes = plt.subplots(1, len(densities), figsize=(7*len(densities), 7)) - for ax, (title, rho) in zip(axes, densities): - kwant.plotter.map(syst, rho, ax=ax, a=4) - ax.set_title(title) -- plt.show() -+ if fname: -+ save_plot(fname) -+ else: -+ plt.show() - - --def plot_currents(syst, currents): -- fig, axes = plt.subplots(1, len(currents)) -+ -+def plot_currents(syst, currents, fname=None): -+ fig, axes = plt.subplots(1, len(currents), figsize=(7*len(currents), 7)) - if not hasattr(axes, '__len__'): - axes = (axes,) - for ax, (title, current) in zip(axes, currents): - kwant.plotter.current(syst, current, ax=ax, colorbar=False) - ax.set_title(title) -- plt.show() -+ if fname: -+ save_plot(fname) -+ else: -+ plt.show() - - - def main(): - syst = make_system().finalized() - - #HIDDEN_BEGIN_wavefunction - params = dict(r0=20, delta=10, J=1) - wf = kwant.wave_function(syst, energy=-1, params=params) - psi = wf(0)[0] - #HIDDEN_END_wavefunction - -- plot_vector_field(syst, dict(r0=20, delta=10)) -+ plot_vector_field(syst, dict(r0=20, delta=10), fname='mag_field_direction') - - #HIDDEN_BEGIN_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 - #HIDDEN_END_ldos - - #HIDDEN_BEGIN_lsdz - # spin down components have a minus sign - spin_z = np.abs(up)**2 - np.abs(down)**2 - #HIDDEN_END_lsdz - - #HIDDEN_BEGIN_lsdy - # spin down components have a minus sign - spin_y = 1j * (down.conjugate() * up - up.conjugate() * down) - #HIDDEN_END_lsdy - - #HIDDEN_BEGIN_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) - #HIDDEN_END_lden - - plot_densities(syst, [ - ('$σ_0$', density), - ('$σ_z$', spin_z), - ('$σ_y$', spin_y), -- ]) -+ ], fname='spin_densities') - - #HIDDEN_BEGIN_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) - #HIDDEN_END_current - - plot_currents(syst, [ - ('$J_{σ_0}$', current), - ('$J_{σ_z}$', spin_z_current), - ('$J_{σ_y}$', spin_y_current), -- ]) -+ ], fname='spin_currents') - - #HIDDEN_BEGIN_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)) - #HIDDEN_END_following - - plot_currents(syst, [ - (r'$J_{\mathbf{m}_i}$', m_current), - ('$J_{σ_z}$', spin_z_current), -- ]) -+ ], fname='spin_current_comparison') - - - #HIDDEN_BEGIN_density_cut - def circle(site): - return np.linalg.norm(site.pos) < 20 - - 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) -+ with open('circle_dos.txt', 'w') as f: -+ with redirect_stdout(f): -+ print('density of states in circle:', dos_in_circle) - #HIDDEN_END_density_cut - - #HIDDEN_BEGIN_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 - - 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)) -+ with open('current_cut.txt', 'w') as f: -+ with redirect_stdout(f): -+ print('J_left:', J_left(psi), ' J_right:', J_right(psi)) -+ print('Jz_left:', Jz_left(psi), ' Jz_right:', Jz_right(psi)) - #HIDDEN_END_current_cut - - #HIDDEN_BEGIN_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) - #HIDDEN_END_bind - - plot_currents(syst, [ - (r'$J_{\mathbf{m}_i}$ (from left)', J_m_left), - (r'$J_{σ_z}$ (from left)', J_z_left), -- ]) -+ ], fname='bound_current') - - - if __name__ == '__main__': - main() diff --git a/doc/source/code/figure/plot_graphene.py.diff b/doc/source/code/figure/plot_graphene.py.diff deleted file mode 100644 index 82c7555424f6a0206c68cbce0235da3e6fa7ef73..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/plot_graphene.py.diff +++ /dev/null @@ -1,141 +0,0 @@ -@@ -1,112 +1,130 @@ - # Tutorial 2.8.1. 2D example: graphene quantum dot - # ================================================ - # - # Physics background - # ------------------ - # - graphene edge states - # - # Kwant features highlighted - # -------------------------- - # - demonstrate different ways of plotting - -+import _defs - import kwant - from matplotlib import pyplot - - #HIDDEN_BEGIN_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.shape(circle, (0,0))] = 0 - syst[lat.neighbors()] = t - syst.eradicate_dangling() - if tp: - syst[lat.neighbors(2)] = tp - - return syst - #HIDDEN_END_makesyst - - - #HIDDEN_BEGIN_plotsyst1 - def plot_system(syst): -- kwant.plot(syst) - #HIDDEN_END_plotsyst1 -- # the standard plot is ok, but not very intelligible. One can do -- # better by playing wioth colors and linewidths -+ # standard plot - not very intelligible for this particular situation -+ size = (_defs.figwidth_in, _defs.figwidth_in) -+ for extension in ('pdf', 'png'): -+ kwant.plot(syst, file="plot_graphene_syst1." + extension, -+ fig_size=size, dpi=_defs.dpi) - - # use color and linewidths to get a better plot - #HIDDEN_BEGIN_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) -+ size = (_defs.figwidth_in, _defs.figwidth_in) -+ for extension in ('pdf', 'png'): -+ kwant.plot(syst, site_lw=0.1, site_color=family_color, -+ hop_lw=hopping_lw, file="plot_graphene_syst2." + extension, -+ fig_size=size, dpi=_defs.dpi) - #HIDDEN_END_plotsyst2 - - - #HIDDEN_BEGIN_plotdata1 - def plot_data(syst, n): - import scipy.linalg as la - - syst = syst.finalized() - ham = syst.hamiltonian_submatrix() - evecs = la.eigh(ham)[1] - - wf = abs(evecs[:, n])**2 - #HIDDEN_END_plotdata1 - - # the usual - works great in general, looks just a bit crufty for - # small systems - - #HIDDEN_BEGIN_plotdata2 -- kwant.plotter.map(syst, wf, oversampling=10, cmap='gist_heat_r') -+ size = (_defs.figwidth_in, _defs.figwidth_in) -+ for extension in ('pdf', 'png'): -+ kwant.plotter.map(syst, wf, oversampling=10, cmap='gist_heat_r', -+ file="plot_graphene_data1." + extension, -+ fig_size=size, dpi=_defs.dpi) - #HIDDEN_END_plotdata2 - - # use two different sort of triangles to cleanly fill the space - #HIDDEN_BEGIN_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') -+ size = (_defs.figwidth_in, _defs.figwidth_in) -+ for extension in ('pdf', 'png'): -+ kwant.plot(syst, site_color=wf, site_symbol=family_shape, -+ site_size=0.5, hop_lw=0, cmap='gist_heat_r', -+ file="plot_graphene_data2." + extension, -+ fig_size=size, dpi=_defs.dpi) - #HIDDEN_END_plotdata3 - - # plot by changing the symbols itself - #HIDDEN_BEGIN_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) -+ size = (_defs.figwidth_in, _defs.figwidth_in) -+ for extension in ('pdf', 'png'): -+ kwant.plot(syst, site_size=site_size, site_color=(0,0,1,0.3), -+ hop_lw=0.1, file="plot_graphene_data3." + extension, -+ fig_size=size, dpi=_defs.dpi) - #HIDDEN_END_plotdata4 - - - def main(): - # plot the graphene system in different styles - syst = make_system() - - plot_system(syst) - - # compute a wavefunction (number 225) and plot it in different - # styles - syst = make_system(tp=0) - - plot_data(syst, 225) - - - # 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() diff --git a/doc/source/code/figure/plot_qahe.py.diff b/doc/source/code/figure/plot_qahe.py.diff deleted file mode 100644 index 1788f677df857d1b75b8706aa0907fad4b9a467d..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/plot_qahe.py.diff +++ /dev/null @@ -1,78 +0,0 @@ -@@ -1,72 +1,76 @@ - # Comprehensive example: quantum anomalous Hall effect - # ==================================================== - # - # Physics background - # ------------------ - # + Quantum anomalous Hall effect - # - # Features highlighted - # -------------------- - # + Use of `kwant.continuum` to discretize a continuum Hamiltonian - # + Use of `kwant.operator` to compute local current - # + Use of `kwant.plotter.current` to plot local current - -+import _defs - import math - import matplotlib.pyplot - import kwant - import kwant.continuum - - - # 2 band model exhibiting quantum anomalous Hall effect - #HIDDEN_BEGIN_model - def make_model(a): - ham = ("alpha * (k_x * sigma_x - k_y * sigma_y)" - "+ (m + beta * kk) * sigma_z" - "+ (gamma * kk + U) * sigma_0") - subs = {"kk": "k_x**2 + k_y**2"} - return kwant.continuum.discretize(ham, locals=subs, grid=a) - #HIDDEN_END_model - - - def make_system(model, L): - def lead_shape(site): - x, y = site.pos / L - return abs(y) < 0.5 - - # QPC shape: a rectangle with 2 gaussians - # etched out of the top and bottom edge. - def central_shape(site): - x, y = site.pos / L - return abs(x) < 3/5 and abs(y) < 0.5 - 0.4 * math.exp(-40 * x**2) - - lead = kwant.Builder(kwant.TranslationalSymmetry( - model.lattice.vec((-1, 0)))) - lead.fill(model, lead_shape, (0, 0)) - - syst = kwant.Builder() - syst.fill(model, central_shape, (0, 0)) - syst.attach_lead(lead) - syst.attach_lead(lead.reversed()) - - return syst.finalized() - - - def main(): - # Set up our model and system, and define the model parameters. - params = dict(alpha=0.365, beta=0.686, gamma=0.512, m=-0.01, U=0) - model = make_model(1) - syst = make_system(model, 70) - kwant.plot(syst) - - # Calculate the scattering states at energy 'm' coming from the left - # lead, and the associated particle current. - psi = kwant.wave_function(syst, energy=params['m'], params=params)(0) - #HIDDEN_BEGIN_current - J = kwant.operator.Current(syst).bind(params=params) - current = sum(J(p) for p in psi) -- kwant.plotter.current(syst, current) -+ for extension in ('pdf', 'png'): -+ kwant.plotter.current(syst, current, -+ file="plot_qahe_current." + extension, -+ dpi=_defs.dpi) - #HIDDEN_END_current - - - if __name__ == '__main__': - main() diff --git a/doc/source/code/figure/plot_zincblende.py.diff b/doc/source/code/figure/plot_zincblende.py.diff deleted file mode 100644 index ad5007871331dd28bf8653ab5be63e102d58205b..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/plot_zincblende.py.diff +++ /dev/null @@ -1,71 +0,0 @@ -@@ -1,59 +1,67 @@ - # Tutorial 2.8.2. 3D example: zincblende structure - # ================================================ - # - # Physical background - # ------------------- - # - 3D Bravais lattices - # - # Kwant features highlighted - # -------------------------- - # - demonstrate different ways of plotting in 3D - -+import _defs - import kwant - from matplotlib import pyplot - - #HIDDEN_BEGIN_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 - #HIDDEN_END_zincblende1 - - #HIDDEN_BEGIN_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 - #HIDDEN_END_zincblende2 - - - def main(): - # the standard plotting style for 3D is mainly useful for - # checking shapes: - #HIDDEN_BEGIN_plot1 - syst = make_cuboid() - -- kwant.plot(syst) -+ size = (_defs.figwidth_in, _defs.figwidth_in) -+ for extension in ('pdf', 'png'): -+ kwant.plot(syst, file="plot_zincblende_syst1." + extension, -+ fig_size=size, dpi=_defs.dpi) - #HIDDEN_END_plot1 - - # visualize the crystal structure better for a very small system - #HIDDEN_BEGIN_plot2 - syst = make_cuboid(a=1.5, b=1.5, c=1.5) - - 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) -+ size = (_defs.figwidth_in, _defs.figwidth_in) -+ for extension in ('pdf', 'png'): -+ kwant.plot(syst, site_size=0.18, site_lw=0.01, hop_lw=0.05, -+ site_color=family_colors, -+ file="plot_zincblende_syst2." + extension, -+ fig_size=size, dpi=_defs.dpi) - #HIDDEN_END_plot2 - - - # 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() diff --git a/doc/source/code/figure/quantum_well.py.diff b/doc/source/code/figure/quantum_well.py.diff deleted file mode 100644 index 8b1be4bb17cd8513816464e1782381842961ab88..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/quantum_well.py.diff +++ /dev/null @@ -1,103 +0,0 @@ -@@ -1,88 +1,95 @@ - # 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 _defs - import kwant - - # For plotting - from matplotlib import pyplot - - - #HIDDEN_BEGIN_ehso - def make_system(a=1, t=1.0, W=10, L=30, L_well=10): - # Start with an empty tight-binding system and a single square lattice. - # `a` is the lattice constant (by default set to 1 for simplicity. - lat = kwant.lattice.square(a) - - syst = kwant.Builder() - - #### 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 - #HIDDEN_END_ehso - - #HIDDEN_BEGIN_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 - #HIDDEN_END_coid - - #### 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()) - - return syst - - - def plot_conductance(syst, energy, welldepths): - #HIDDEN_BEGIN_sqvr - - # Compute conductance - data = [] - for welldepth in welldepths: - smatrix = kwant.smatrix(syst, energy, params=dict(pot=-welldepth)) - data.append(smatrix.transmission(1, 0)) - -- pyplot.figure() -+ fig = pyplot.figure() - pyplot.plot(welldepths, data) -- pyplot.xlabel("well depth [t]") -- pyplot.ylabel("conductance [e^2/h]") -- pyplot.show() -+ pyplot.xlabel("well depth [t]", -+ fontsize=_defs.mpl_label_size) -+ pyplot.ylabel("conductance [e^2/h]", -+ fontsize=_defs.mpl_label_size) -+ pyplot.setp(fig.get_axes()[0].get_xticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ pyplot.setp(fig.get_axes()[0].get_yticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ fig.set_size_inches(_defs.mpl_width_in, _defs.mpl_width_in * 3. / 4.) -+ fig.subplots_adjust(left=0.15, right=0.95, top=0.95, bottom=0.15) -+ for extension in ('pdf', 'png'): -+ fig.savefig("quantum_well_result." + extension, dpi=_defs.dpi) - #HIDDEN_END_sqvr - - - def main(): - syst = make_system() - -- # Check that the system looks as intended. -- kwant.plot(syst) -- - # Finalize the system. - syst = syst.finalized() - - # We should see conductance steps. - plot_conductance(syst, energy=0.2, - welldepths=[0.01 * i for i in range(100)]) - - - # 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() diff --git a/doc/source/code/figure/quantum_wire.py.diff b/doc/source/code/figure/quantum_wire.py.diff deleted file mode 100644 index 396ab255359afa8c53f7790a1c53b7b9fe2de178..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/quantum_wire.py.diff +++ /dev/null @@ -1,136 +0,0 @@ -@@ -1,121 +1,130 @@ - # 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 - -+import _defs - from matplotlib import pyplot - #HIDDEN_BEGIN_dwhx - import kwant - #HIDDEN_END_dwhx - - # First, define the tight-binding system - - #HIDDEN_BEGIN_goiq - syst = kwant.Builder() - #HIDDEN_END_goiq - - # Here, we are only working with square lattices - #HIDDEN_BEGIN_suwo - a = 1 - lat = kwant.lattice.square(a) - #HIDDEN_END_suwo - - #HIDDEN_BEGIN_zfvr - t = 1.0 - W = 10 - L = 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 - #HIDDEN_END_zfvr - - # Then, define and attach the leads: - - # First the lead to the left - # (Note: TranslationalSymmetry takes a real-space vector) - #HIDDEN_BEGIN_xcmc - sym_left_lead = kwant.TranslationalSymmetry((-a, 0)) - left_lead = kwant.Builder(sym_left_lead) - #HIDDEN_END_xcmc - - #HIDDEN_BEGIN_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 - #HIDDEN_END_ndez - - #HIDDEN_BEGIN_fskr - syst.attach_lead(left_lead) - #HIDDEN_END_fskr - - # Then the lead to the right - #HIDDEN_BEGIN_xhqc - 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) - #HIDDEN_END_xhqc - - # Plot it, to make sure it's OK - #HIDDEN_BEGIN_wsgh --kwant.plot(syst) -+size = (_defs.figwidth_in, 0.3 * _defs.figwidth_in) -+for extension in ('pdf', 'png'): -+ kwant.plot(syst, file="quantum_wire_syst." + extension, -+ fig_size=size, dpi=_defs.dpi) - #HIDDEN_END_wsgh - - # Finalize the system - #HIDDEN_BEGIN_dngj - syst = syst.finalized() - #HIDDEN_END_dngj - - # Now that we have the system, we can compute conductance - #HIDDEN_BEGIN_buzn - 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)) - #HIDDEN_END_buzn - - # Use matplotlib to write output - # We should see conductance steps - #HIDDEN_BEGIN_lliv --pyplot.figure() -+fig = pyplot.figure() - pyplot.plot(energies, data) --pyplot.xlabel("energy [t]") --pyplot.ylabel("conductance [e^2/h]") --pyplot.show() -+pyplot.xlabel("energy [t]", fontsize=_defs.mpl_label_size) -+pyplot.ylabel("conductance [e^2/h]", fontsize=_defs.mpl_label_size) -+pyplot.setp(fig.get_axes()[0].get_xticklabels(), fontsize=_defs.mpl_tick_size) -+pyplot.setp(fig.get_axes()[0].get_yticklabels(), fontsize=_defs.mpl_tick_size) -+fig.set_size_inches(_defs.mpl_width_in, _defs.mpl_width_in * 3. / 4.) -+fig.subplots_adjust(left=0.15, right=0.95, top=0.95, bottom=0.15) -+for extension in ('pdf', 'png'): -+ fig.savefig("quantum_wire_result." + extension, dpi=_defs.dpi) - #HIDDEN_END_lliv diff --git a/doc/source/code/figure/spin_orbit.py.diff b/doc/source/code/figure/spin_orbit.py.diff deleted file mode 100644 index 794c1b5b755e22ea1a175c40a9078bd3e5b3e12b..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/spin_orbit.py.diff +++ /dev/null @@ -1,118 +0,0 @@ -@@ -1,104 +1,110 @@ - # 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 _defs - import kwant - - # For plotting - from matplotlib import pyplot - - # For matrix support - #HIDDEN_BEGIN_xumz - import tinyarray - #HIDDEN_END_xumz - - # define Pauli-matrices for convenience - #HIDDEN_BEGIN_hwbt - 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]]) - #HIDDEN_END_hwbt - - - def make_system(t=1.0, alpha=0.5, e_z=0.08, W=10, L=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() - - syst = kwant.Builder() - - #### Define the scattering region. #### - #HIDDEN_BEGIN_uxrm - 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 - #HIDDEN_END_uxrm - - #### Define the left lead. #### - lead = kwant.Builder(kwant.TranslationalSymmetry((-1, 0))) - - #HIDDEN_BEGIN_yliu - 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 - #HIDDEN_END_yliu - - #### Attach the leads and return the finalized system. #### - syst.attach_lead(lead) - syst.attach_lead(lead.reversed()) - - return syst - - - 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() -+ fig = pyplot.figure() - pyplot.plot(energies, data) -- pyplot.xlabel("energy [t]") -- pyplot.ylabel("conductance [e^2/h]") -- pyplot.show() -+ pyplot.xlabel("energy [t]", fontsize=_defs.mpl_label_size) -+ pyplot.ylabel("conductance [e^2/h]", -+ fontsize=_defs.mpl_label_size) -+ pyplot.setp(fig.get_axes()[0].get_xticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ pyplot.setp(fig.get_axes()[0].get_yticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ fig.set_size_inches(_defs.mpl_width_in, _defs.mpl_width_in * 3. / 4.) -+ fig.subplots_adjust(left=0.15, right=0.95, top=0.95, bottom=0.15) -+ for extension in ('pdf', 'png'): -+ fig.savefig("spin_orbit_result." + extension, dpi=_defs.dpi) - - - def main(): - syst = make_system() - -- # Check that the system looks as intended. -- kwant.plot(syst) -- - # Finalize the system. - syst = syst.finalized() - - # We should see non-monotonic conductance steps. - plot_conductance(syst, energies=[0.01 * i - 0.3 for i in range(100)]) - - - # 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() diff --git a/doc/source/code/figure/superconductor.py.diff b/doc/source/code/figure/superconductor.py.diff deleted file mode 100644 index 75d9b061f233f5fb1a45c874977e3156cf7f1296..0000000000000000000000000000000000000000 --- a/doc/source/code/figure/superconductor.py.diff +++ /dev/null @@ -1,148 +0,0 @@ -@@ -1,132 +1,141 @@ - # 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 _defs - import kwant - - import tinyarray - import numpy as np - - # For plotting - from matplotlib import pyplot -+from contextlib import redirect_stdout - - tau_x = tinyarray.array([[0, 1], [1, 0]]) - tau_y = tinyarray.array([[0, -1j], [1j, 0]]) - tau_z = tinyarray.array([[1, 0], [0, -1]]) - - #HIDDEN_BEGIN_nbvn - def make_system(a=1, W=10, L=10, barrier=1.5, barrierpos=(3, 4), - mu=0.4, Delta=0.1, Deltapos=4, t=1.0): - # 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 - #HIDDEN_END_nbvn - #HIDDEN_BEGIN_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 - #HIDDEN_END_ttth - #HIDDEN_BEGIN_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 return the system. #### - syst.attach_lead(lead0) - syst.attach_lead(lead1) - - return syst - #HIDDEN_END_zuuw - - #HIDDEN_BEGIN_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))) - #HIDDEN_END_jbjt -- pyplot.figure() -+ fig = pyplot.figure() - pyplot.plot(energies, data) - pyplot.xlabel("energy [t]") - pyplot.ylabel("conductance [e^2/h]") -- pyplot.show() -+ pyplot.setp(fig.get_axes()[0].get_xticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ pyplot.setp(fig.get_axes()[0].get_yticklabels(), -+ fontsize=_defs.mpl_tick_size) -+ fig.set_size_inches(_defs.mpl_width_in, _defs.mpl_width_in * 3. / 4.) -+ fig.subplots_adjust(left=0.15, right=0.95, top=0.95, bottom=0.15) -+ for extension in ('pdf', 'png'): -+ fig.savefig("superconductor_transport_result." + extension, -+ dpi=_defs.dpi) - - #HIDDEN_BEGIN_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)) - #HIDDEN_END_pqmp - - def main(): - syst = make_system(W=10) - -- # Check that the system looks as intended. -- kwant.plot(syst) -- - # Finalize the system. - syst = syst.finalized() - - # Check particle-hole symmetry of the scattering matrix -- check_PHS(syst) -+ with open('check_PHS_out.txt', 'w') as f: -+ with redirect_stdout(f): -+ check_PHS(syst) - - # Compute and plot the conductance - plot_conductance(syst, energies=[0.002 * i for i in range(-10, 100)]) - - - # 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() diff --git a/doc/source/code/include/quantum_wire_revisited.py b/doc/source/code/include/quantum_wire_revisited.py deleted file mode 100644 index 60add47870d7c6690e30cd4a9e46adc69d8d4d6f..0000000000000000000000000000000000000000 --- a/doc/source/code/include/quantum_wire_revisited.py +++ /dev/null @@ -1,91 +0,0 @@ -# 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. - -#HIDDEN_BEGIN_xkzy -import kwant - -# For plotting -from matplotlib import pyplot - - -def make_system(a=1, t=1.0, W=10, L=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() -#HIDDEN_END_xkzy - - #### Define the scattering region. #### -#HIDDEN_BEGIN_vvjt - syst[(lat(x, y) for x in range(L) for y in range(W))] = 4 * t -#HIDDEN_END_vvjt -#HIDDEN_BEGIN_nooi - syst[lat.neighbors()] = -t -#HIDDEN_END_nooi - - #### Define and attach the leads. #### - # Construct the left lead. -#HIDDEN_BEGIN_iepx - lead = kwant.Builder(kwant.TranslationalSymmetry((-a, 0))) - lead[(lat(0, j) for j in range(W))] = 4 * t - lead[lat.neighbors()] = -t -#HIDDEN_END_iepx - - # Attach the left lead and its reversed copy. -#HIDDEN_BEGIN_yxot - syst.attach_lead(lead) - syst.attach_lead(lead.reversed()) - - return syst -#HIDDEN_END_yxot - - -#HIDDEN_BEGIN_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() -#HIDDEN_END_ayuk - - -#HIDDEN_BEGIN_cjel -def main(): - syst = make_system() - - # Check that the system looks as intended. - kwant.plot(syst) - - # Finalize the system. - syst = syst.finalized() - - # We should see conductance steps. - plot_conductance(syst, energies=[0.01 * i for i in range(100)]) -#HIDDEN_END_cjel - - -# Call the main function if the script gets executed (as opposed to imported). -# See <http://docs.python.org/library/__main__.html>. -#HIDDEN_BEGIN_ypbj -if __name__ == '__main__': - main() -#HIDDEN_END_ypbj diff --git a/doc/source/conf.py b/doc/source/conf.py index d3d7fceb5022796e9fe464cde6def683778bed46..080a71ff0039aaf8528ddf692c45e1569050697a 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -15,8 +15,15 @@ import sys import os import string 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.qsymm @@ -31,7 +38,8 @@ sys.path.insert(0, os.path.abspath('../sphinxext')) extensions = ['sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.todo', 'sphinx.ext.mathjax', 'numpydoc', - 'kwantdoc', 'sphinx.ext.linkcode'] + 'kwantdoc', 'sphinx.ext.linkcode', 'jupyter_sphinx.execute', + 'sphinxcontrib.rsvgconverter'] # Add any paths that contain templates here, relative to this directory. templates_path = ['../templates'] @@ -239,7 +247,6 @@ autosummary_generate = True autoclass_content = "both" autodoc_default_flags = ['show-inheritance'] - # -- Teach Sphinx to document bound methods like functions --------------------- import types from sphinx.ext import autodoc diff --git a/doc/source/code/figure/ab_ring_sketch.svg b/doc/source/figure/ab_ring_sketch.svg similarity index 100% rename from doc/source/code/figure/ab_ring_sketch.svg rename to doc/source/figure/ab_ring_sketch.svg diff --git a/doc/source/code/figure/ab_ring_sketch2.svg b/doc/source/figure/ab_ring_sketch2.svg similarity index 100% rename from doc/source/code/figure/ab_ring_sketch2.svg rename to doc/source/figure/ab_ring_sketch2.svg diff --git a/doc/source/code/figure/superconductor_transport_sketch.svg b/doc/source/figure/superconductor_transport_sketch.svg similarity index 100% rename from doc/source/code/figure/superconductor_transport_sketch.svg rename to doc/source/figure/superconductor_transport_sketch.svg diff --git a/doc/source/pre/whatsnew/1.3.rst b/doc/source/pre/whatsnew/1.3.rst index f7dbd8fc0ef4373499d976fc3b908f04548e629e..8336e99d9c66dc1fb7628414fe28cf648c18319c 100644 --- a/doc/source/pre/whatsnew/1.3.rst +++ b/doc/source/pre/whatsnew/1.3.rst @@ -28,11 +28,43 @@ tight-binding band structures or construct systems with different/lower symmetry. For example in just a few lines we can construct a two-band model that exhibits a quantum anomalous spin Hall phase: -.. literalinclude:: ../../code/include/plot_qahe.py - :start-after: HIDDEN_BEGIN_model - :end-before: HIDDEN_END_model +.. jupyter-kernel:: + :id: plot_qahe + +.. jupyter-execute:: + :hide-code: + + # Comprehensive example: quantum anomalous Hall effect + # ==================================================== + # + # Physics background + # ------------------ + # + Quantum anomalous Hall effect + # + # Features highlighted + # -------------------- + # + Use of `kwant.continuum` to discretize a continuum Hamiltonian + # + Use of `kwant.operator` to compute local current + # + Use of `kwant.plotter.current` to plot local current + + import math + import matplotlib.pyplot + import kwant + import kwant.continuum + +.. jupyter-execute:: ../../tutorial/boilerplate.py + :hide-code: + +.. jupyter-execute:: -From: :download:`QAHE example script <../../code/download/plot_qahe.py>` + def make_model(a): + ham = ("alpha * (k_x * sigma_x - k_y * sigma_y)" + "+ (m + beta * kk) * sigma_z" + "+ (gamma * kk + U) * sigma_0") + subs = {"kk": "k_x**2 + k_y**2"} + return kwant.continuum.discretize(ham, locals=subs, grid=a) + +From: :jupyter-download:script:`plot_qahe` See the tutorial: :doc:`../../tutorial/discretize` @@ -71,13 +103,47 @@ The example below shows edge states of a quantum anomalous Hall phase of the two-band model shown in the `above section <#tools-for-continuum-hamiltonians>`_: -.. literalinclude:: ../../code/include/plot_qahe.py - :start-after: HIDDEN_BEGIN_current - :end-before: HIDDEN_END_current +.. jupyter-execute:: + :hide-code: + + def make_system(model, L): + def lead_shape(site): + x, y = site.pos / L + return abs(y) < 0.5 + + # QPC shape: a rectangle with 2 gaussians + # etched out of the top and bottom edge. + def central_shape(site): + x, y = site.pos / L + return abs(x) < 3/5 and abs(y) < 0.5 - 0.4 * math.exp(-40 * x**2) + + lead = kwant.Builder(kwant.TranslationalSymmetry( + model.lattice.vec((-1, 0)))) + lead.fill(model, lead_shape, (0, 0)) + + syst = kwant.Builder() + syst.fill(model, central_shape, (0, 0)) + syst.attach_lead(lead) + syst.attach_lead(lead.reversed()) + + return syst.finalized() + + # Set up our model and system, and define the model parameters. + params = dict(alpha=0.365, beta=0.686, gamma=0.512, m=-0.01, U=0) + model = make_model(1) + syst = make_system(model, 70) + + # Calculate the scattering states at energy 'm' coming from the left + # lead, and the associated particle current. + psi = kwant.wave_function(syst, energy=params['m'], params=params)(0) + +.. jupyter-execute:: -.. image:: ../../code/figure/plot_qahe_current.* + J = kwant.operator.Current(syst).bind(params=params) + current = sum(J(p) for p in psi) + kwant.plotter.current(syst, current); -From: :download:`QAHE example script <../../code/download/plot_qahe.py>` +From: :jupyter-download:script:`plot_qahe` Scattering states with discrete symmetries and conservation laws ---------------------------------------------------------------- diff --git a/doc/source/pre/whatsnew/1.5.rst b/doc/source/pre/whatsnew/1.5.rst index b985489ec6a45dd48c04bcab293d8454f76abab9..9a9c085c8d924c0cda45d84dc75a5b1a0b893016 100644 --- a/doc/source/pre/whatsnew/1.5.rst +++ b/doc/source/pre/whatsnew/1.5.rst @@ -2,3 +2,13 @@ What's new in Kwant 1.5 ======================= This article explains the user-visible changes in Kwant 1.5.0. + +Improved tutorial building +-------------------------- +Improving or adding to Kwant's tutorial is now much simpler. Now +the text and code for each tutorial is kept in the same file, making +it easy to see where changes need to be made, and images generated by +the code are inserted directly into the document thanks to the magic of +`jupyter-sphinx <https://github.com/jupyter-widgets/jupyter-sphinx/>`_. +It has never been easier to get started contributing to Kwant by +helping us improve our documentation. diff --git a/doc/source/tutorial/boilerplate.py b/doc/source/tutorial/boilerplate.py new file mode 100644 index 0000000000000000000000000000000000000000..db2fba37a6f8546b66d4ca8f06a8eda668438ca7 --- /dev/null +++ b/doc/source/tutorial/boilerplate.py @@ -0,0 +1,6 @@ +import matplotlib +import matplotlib.pyplot +from IPython.display import set_matplotlib_formats + +matplotlib.rcParams['figure.figsize'] = matplotlib.pyplot.figaspect(1) * 2 +set_matplotlib_formats('svg') diff --git a/doc/source/tutorial/discretize.rst b/doc/source/tutorial/discretize.rst index b737b00ca074cbfd88a7e863be67ebc03063d902..31c4b3bb3ca217888c3c0e1b53e444a270906705 100644 --- a/doc/source/tutorial/discretize.rst +++ b/doc/source/tutorial/discretize.rst @@ -18,8 +18,32 @@ continuum models and for discretizing them into tight-binding models. .. seealso:: The complete source code of this tutorial can be found in - :download:`discretize.py </code/download/discretize.py>` + :jupyter-download:script:`discretize` +.. jupyter-kernel:: + :id: discretize + +.. jupyter-execute:: + :hide-code: + + # Tutorial 2.9. Processing continuum Hamiltonians with discretize + # =============================================================== + # + # Physics background + # ------------------ + # - tight-binding approximation of continuous Hamiltonians + # + # Kwant features highlighted + # -------------------------- + # - kwant.continuum.discretize + + import matplotlib as mpl + from matplotlib import pyplot + + import kwant + +.. jupyter-execute:: boilerplate.py + :hide-code: .. _tutorial_discretizer_introduction: @@ -55,9 +79,16 @@ Using `~kwant.continuum.discretize` to obtain a template ........................................................ First we must explicitly import the `kwant.continuum` package: -.. literalinclude:: /code/include/discretize.py - :start-after: #HIDDEN_BEGIN_import - :end-before: #HIDDEN_END_import +.. jupyter-execute:: + + import kwant.continuum + +.. jupyter-execute:: + :hide-code: + + import scipy.sparse.linalg + import scipy.linalg + import numpy as np The function `kwant.continuum.discretize` takes a symbolic Hamiltonian and turns it into a `~kwant.builder.Builder` instance with appropriate spatial @@ -65,17 +96,16 @@ symmetry that serves as a template. (We will see how to use the template to build systems with a particular shape later). -.. literalinclude:: /code/include/discretize.py - :start-after: #HIDDEN_BEGIN_symbolic_discretization - :end-before: #HIDDEN_END_symbolic_discretization +.. jupyter-execute:: + + template = kwant.continuum.discretize('k_x * A(x) * k_x') + print(template) It is worth noting that ``discretize`` treats ``k_x`` and ``x`` as non-commuting operators, and so their order is preserved during the discretization process. -The builder produced by ``discretize`` may be printed to show the source code of its onsite and hopping functions (this is a special feature of builders returned by ``discretize``): - -.. literalinclude:: /code/figure/discretizer_intro_verbose.txt +Printing the Builder produced by ``discretize`` shows the source code of its onsite and hopping functions (this is a special feature of builders returned by ``discretize``). .. specialnote:: Technical details @@ -139,26 +169,45 @@ where :math:`V(x, y)` is some arbitrary potential. First, use ``discretize`` to obtain a builder that we will use as a template: -.. literalinclude:: /code/include/discretize.py - :start-after: #HIDDEN_BEGIN_template - :end-before: #HIDDEN_END_template +.. jupyter-execute:: + + hamiltonian = "k_x**2 + k_y**2 + V(x, y)" + template = kwant.continuum.discretize(hamiltonian) + print(template) We now use this system with the `~kwant.builder.Builder.fill` method of `~kwant.builder.Builder` to construct the system we want to investigate: -.. literalinclude:: /code/include/discretize.py - :start-after: #HIDDEN_BEGIN_fill - :end-before: #HIDDEN_END_fill +.. jupyter-execute:: + + def stadium(site): + (x, y) = site.pos + x = max(abs(x) - 20, 0) + return x**2 + y**2 < 30**2 + + syst = kwant.Builder() + syst.fill(template, stadium, (0, 0)); + syst = syst.finalized() After finalizing this system, we can plot one of the system's energy eigenstates: -.. literalinclude:: /code/include/discretize.py - :start-after: #HIDDEN_BEGIN_plot_eigenstate - :end-before: #HIDDEN_END_plot_eigenstate +.. jupyter-execute:: + + def plot_eigenstate(syst, n=2, Vx=.0003, Vy=.0005): + + def potential(x, y): + return Vx * x + Vy * y -.. image:: /code/figure/discretizer_gs.* + ham = syst.hamiltonian_submatrix(params=dict(V=potential), sparse=True) + evecs = scipy.sparse.linalg.eigsh(ham, k=10, which='SM')[1] + kwant.plotter.map(syst, abs(evecs[:, n])**2, show=False) + +.. jupyter-execute:: + :hide-code: + + plot_eigenstate(syst) Note in the above that we pass the spatially varying potential *function* to our system via a parameter called ``V``, because the symbol :math:`V` @@ -176,32 +225,95 @@ model [1]_ [2]_, one can provide matrix input to `~kwant.continuum.discretize` using ``identity`` and ``kron``. For example, the definition of the BHZ model can be written succinctly as: -.. literalinclude:: /code/include/discretize.py - :start-after: #HIDDEN_BEGIN_define_qsh - :end-before: #HIDDEN_END_define_qsh +.. jupyter-execute:: + + hamiltonian = """ + + C * identity(4) + M * kron(sigma_0, sigma_z) + - B * (k_x**2 + k_y**2) * kron(sigma_0, sigma_z) + - D * (k_x**2 + k_y**2) * kron(sigma_0, sigma_0) + + A * k_x * kron(sigma_z, sigma_x) + - A * k_y * kron(sigma_0, sigma_y) + """ + + a = 20 + + template = kwant.continuum.discretize(hamiltonian, grid=a) We can then make a ribbon out of this template system: -.. literalinclude:: /code/include/discretize.py - :start-after: #HIDDEN_BEGIN_define_qsh_build - :end-before: #HIDDEN_END_define_qsh_build +.. jupyter-execute:: + + L, W = 2000, 1000 + + def shape(site): + (x, y) = site.pos + return (0 <= y < W and 0 <= x < L) + + def lead_shape(site): + (x, y) = site.pos + return (0 <= y < W) + + syst = kwant.Builder() + syst.fill(template, shape, (0, 0)) + + lead = kwant.Builder(kwant.TranslationalSymmetry([-a, 0])) + lead.fill(template, lead_shape, (0, 0)) + + syst.attach_lead(lead) + syst.attach_lead(lead.reversed()) + + syst = syst.finalized() and plot its dispersion using `kwant.plotter.bands`: -.. literalinclude:: /code/include/discretize.py - :start-after: #HIDDEN_BEGIN_plot_qsh_band - :end-before: #HIDDEN_END_plot_qsh_band +.. jupyter-execute:: -.. image:: /code/figure/discretizer_qsh_band.* + params = dict(A=3.65, B=-68.6, D=-51.1, M=-0.01, C=0) + + kwant.plotter.bands(syst.leads[0], params=params, + momenta=np.linspace(-0.3, 0.3, 201), show=False) + + pyplot.grid() + pyplot.xlim(-.3, 0.3) + pyplot.ylim(-0.05, 0.05) + pyplot.xlabel('momentum [1/A]') + pyplot.ylabel('energy [eV]') + pyplot.show() In the above we see the edge states of the quantum spin Hall effect, which we can visualize using `kwant.plotter.map`: -.. literalinclude:: /code/include/discretize.py - :start-after: #HIDDEN_BEGIN_plot_qsh_wf - :end-before: #HIDDEN_END_plot_qsh_wf +.. jupyter-execute:: + + # get scattering wave functions at E=0 + wf = kwant.wave_function(syst, energy=0, params=params) -.. image:: /code/figure/discretizer_qsh_wf.* + # prepare density operators + sigma_z = np.array([[1, 0], [0, -1]]) + prob_density = kwant.operator.Density(syst, np.eye(4)) + spin_density = kwant.operator.Density(syst, np.kron(sigma_z, np.eye(2))) + + # calculate expectation values and plot them + wf_sqr = sum(prob_density(psi) for psi in wf(0)) # states from left lead + rho_sz = sum(spin_density(psi) for psi in wf(0)) # states from left lead + + fig, (ax1, ax2) = pyplot.subplots(1, 2, sharey=True, figsize=(16, 4)) + kwant.plotter.map(syst, wf_sqr, ax=ax1) + kwant.plotter.map(syst, rho_sz, ax=ax2) + + ax = ax1 + im = [obj for obj in ax.get_children() + if isinstance(obj, mpl.image.AxesImage)][0] + fig.colorbar(im, ax=ax) + + ax = ax2 + im = [obj for obj in ax.get_children() + if isinstance(obj, mpl.image.AxesImage)][0] + fig.colorbar(im, ax=ax) + + ax1.set_title('Probability density') + ax2.set_title('Spin density') + pyplot.show() Limitations of discretization @@ -238,29 +350,62 @@ example. Let us start from the continuum Hamiltonian We start by defining this model as a string and setting the value of the :math:`α` parameter: -.. literalinclude:: /code/include/discretize.py - :start-after: #HIDDEN_BEGIN_ls_def - :end-before: #HIDDEN_END_ls_def +.. jupyter-execute:: + + hamiltonian = "k_x**2 * identity(2) + alpha * k_x * sigma_y" + params = dict(alpha=.5) Now we can use `kwant.continuum.lambdify` to obtain a function that computes :math:`H(k)`: -.. literalinclude:: /code/include/discretize.py - :start-after: #HIDDEN_BEGIN_ls_hk_cont - :end-before: #HIDDEN_END_ls_hk_cont +.. jupyter-execute:: + + h_k = kwant.continuum.lambdify(hamiltonian, locals=params) + k_cont = np.linspace(-4, 4, 201) + e_cont = [scipy.linalg.eigvalsh(h_k(k_x=ki)) for ki in k_cont] We can also construct a discretized approximation using `kwant.continuum.discretize`, in a similar manner to previous examples: -.. literalinclude:: /code/include/discretize.py - :start-after: #HIDDEN_BEGIN_ls_hk_tb - :end-before: #HIDDEN_END_ls_hk_tb +.. jupyter-execute:: + + def plot(ax, a=1): + template = kwant.continuum.discretize(hamiltonian, grid=a) + syst = kwant.wraparound.wraparound(template).finalized() + + def h_k(k_x): + p = dict(k_x=k_x, **params) + return syst.hamiltonian_submatrix(params=p) + + k_tb = np.linspace(-np.pi/a, np.pi/a, 201) + e_tb = [scipy.linalg.eigvalsh(h_k(k_x=a*ki)) for ki in k_tb] + + ax.plot(k_cont, e_cont, 'r-') + ax.plot(k_tb, e_tb, 'k-') + + ax.plot([], [], 'r-', label='continuum') + ax.plot([], [], 'k-', label='tight-binding') + + ax.set_xlim(-4, 4) + ax.set_ylim(-1, 14) + ax.set_title('a={}'.format(a)) + + ax.set_xlabel('momentum [a.u.]') + ax.set_ylabel('energy [a.u.]') + ax.grid() + ax.legend() Below we can see the continuum and tight-binding dispersions for two different values of the discretization grid spacing :math:`a`: -.. image:: /code/figure/discretizer_lattice_spacing.* +.. jupyter-execute:: + :hide-code: + + _, (ax1, ax2) = pyplot.subplots(1, 2, sharey=True, figsize=(12, 4)) + plot(ax1, a=1) + plot(ax2, a=.25) + pyplot.show() We clearly see that the smaller grid spacing is, the better we approximate the original continuous dispersion. It is also worth remembering that the @@ -284,20 +429,26 @@ It is possible to use ``identity`` (for identity matrix), ``kron`` (for Kronecke expressions involving matrices. Matrices can also be provided explicitly using square ``[]`` brackets. For example, all following expressions are equivalent: -.. literalinclude:: /code/include/discretize.py - :start-after: #HIDDEN_BEGIN_subs_1 - :end-before: #HIDDEN_END_subs_1 +.. jupyter-execute:: + + sympify = kwant.continuum.sympify + subs = {'sx': [[0, 1], [1, 0]], 'sz': [[1, 0], [0, -1]]} + + e = ( + sympify('[[k_x**2, alpha * k_x], [k_x * alpha, -k_x**2]]'), + sympify('k_x**2 * sigma_z + alpha * k_x * sigma_x'), + sympify('k_x**2 * sz + alpha * k_x * sx', locals=subs), + ) -.. literalinclude:: /code/figure/discretizer_subs_1.txt + print(e[0] == e[1] == e[2]) We can use the ``locals`` keyword parameter to substitute expressions and numerical values: -.. literalinclude:: /code/include/discretize.py - :start-after: #HIDDEN_BEGIN_subs_2 - :end-before: #HIDDEN_END_subs_2 +.. jupyter-execute:: -.. literalinclude:: /code/figure/discretizer_subs_2.txt + subs = {'A': 'A(x) + B', 'V': 'V(x) + V_0', 'C': 5} + print(sympify('k_x * A * k_x + V + C', locals=subs)) Symbolic expressions obtained in this way can be directly passed to all ``discretizer`` functions. diff --git a/doc/source/tutorial/faq.rst b/doc/source/tutorial/faq.rst index c2ba0700dbc8d3ef4aaf3c2665fbf4ed77a944eb..ec52448d7afaf0d3ee4a2440088829a8dfc1b921 100644 --- a/doc/source/tutorial/faq.rst +++ b/doc/source/tutorial/faq.rst @@ -5,6 +5,17 @@ questions that are discussed there. The `Kwant paper <https://downloads.kwant-project.org/doc/kwant-paper.pdf>`_ also digs deeper into Kwant's structure. +.. jupyter-execute:: + :hide-code: + + import kwant + import numpy as np + import tinyarray + import matplotlib + from matplotlib import pyplot as plt + +.. jupyter-execute:: boilerplate.py + :hide-code: What is a system, and what is a builder? ======================================== @@ -48,11 +59,16 @@ combination of family and tag uniquely defines a site. For example let us create an empty tight binding system and add two sites: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_site - :end-before: #HIDDEN_END_site +.. jupyter-execute:: + + a = 1 + lat = kwant.lattice.square(a) + syst = kwant.Builder() -.. image:: /code/figure/faq_site.* + syst[lat(1, 0)] = 4 + syst[lat(1, 1)] = 4 + + kwant.plot(syst); In the above snippet we added 2 sites: ``lat(1, 0)`` and ``lat(0, 1)``. Both of these sites belong to the same family, ``lat``, but have different tags: @@ -79,11 +95,11 @@ tuples, for example lists, are not treated as hoppings. Starting from the example code from `What is a site?`_, we can add a hopping to our system in the following way: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_hopping - :end-before: #HIDDEN_END_hopping +.. jupyter-execute:: + + syst[(lat(1, 0), lat(1, 1))] = 1j -.. image:: /code/figure/faq_hopping.* + kwant.plot(syst); Visually, a hopping is represented as a line that joins two sites. @@ -139,18 +155,29 @@ Let's create two monatomic lattices (``lat_a`` and ``lat_b``). ``(1, 0)`` and ``(0, 1)`` will be the primitive vectors and ``(0, 0)`` and ``(0.5, 0.5)`` the origins of the two lattices: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_lattice_monatomic - :end-before: #HIDDEN_END_lattice_monatomic +.. jupyter-execute:: + + # Two monatomic lattices + primitive_vectors = [(1, 0), (0, 1)] + lat_a = kwant.lattice.Monatomic(primitive_vectors, offset=(0, 0)) + lat_b = kwant.lattice.Monatomic(primitive_vectors, offset=(0.5, 0.5)) + # lat1 is equivalent to kwant.lattice.square() -.. image:: /code/figure/faq_lattice.* + syst = kwant.Builder() + + syst[lat_a(0, 0)] = 4 + syst[lat_b(0, 0)] = 4 + + kwant.plot(syst); We can also create a ``Polyatomic`` lattice with the same primitive vectors and two sites in the basis: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_lattice_polyatomic - :end-before: #HIDDEN_END_lattice_polyatomic +.. jupyter-execute:: + + # One polyatomic lattice containing two sublattices + lat = kwant.lattice.Polyatomic([(1, 0), (0, 1)], [(0, 0), (0.5, 0.5)]) + sub_a, sub_b = lat.sublattices The two sublattices ``sub_a`` and ``sub_b`` are nothing else than ``Monatomic`` instances, and are equivalent to ``lat_a`` and ``lat_b`` that we created @@ -169,17 +196,40 @@ When plotting, how to color the different sublattices differently? ================================================================== In the following example we shall use a kagome lattice, which has three sublattices. -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_colors1 - :end-before: #HIDDEN_END_colors1 +.. jupyter-execute:: + + lat = kwant.lattice.kagome() + syst = kwant.Builder() + + a, b, c = lat.sublattices # The kagome lattice has 3 sublattices As we can see below, we create a new plotting function that assigns a color for each family, and a different size for the hoppings depending on the family of the two sites. Finally we add sites and hoppings to our system and plot it with the new function. -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_colors2 - :end-before: #HIDDEN_END_colors2 +.. jupyter-execute:: -.. image:: /code/figure/faq_colors.* + # Plot sites from different families in different colors + def family_color(site): + if site.family == a: + return 'red' + if site.family == b: + return 'green' + else: + return 'blue' + + def plot_system(syst): + kwant.plot(syst, site_lw=0.1, site_color=family_color) + + ## Add sites and hoppings. + for i in range(4): + for j in range (4): + syst[a(i, j)] = 4 + syst[b(i, j)] = 4 + syst[c(i, j)] = 4 + + syst[lat.neighbors()] = -1 + + ## Plot the system. + plot_system(syst) How to create many similar hoppings in one go? @@ -197,31 +247,64 @@ integers). The following example shows how this can be used: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_direction1 - :end-before: #HIDDEN_END_direction1 +.. jupyter-execute:: + + # Create hopping between neighbors with HoppingKind + a = 1 + syst = kwant.Builder() + lat = kwant.lattice.square(a) + syst[ (lat(i, j) for i in range(5) for j in range(5)) ] = 4 -.. image:: /code/figure/faq_direction1.* + syst[kwant.builder.HoppingKind((1, 0), lat)] = -1 + kwant.plot(syst); Note that ``HoppingKind`` only works with site families so you cannot use them directly with ``Polyatomic`` lattices; you have to explicitly specify the sublattices when creating a ``HoppingKind``: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_direction2 - :end-before: #HIDDEN_END_direction2 +.. jupyter-execute:: + :hide-code: + + lat = kwant.lattice.kagome() + syst = kwant.Builder() + + a, b, c = lat.sublattices # The kagome lattice has 3 sublattices + + for i in range(4): + for j in range (4): + syst[a(i, j)] = 4 + syst[b(i, j)] = 4 + syst[c(i, j)] = 4 + +.. jupyter-execute:: + + # equivalent to syst[kwant.builder.HoppingKind((0, 1), b)] = -1 + syst[kwant.builder.HoppingKind((0, 1), b, b)] = -1 Here, we want the hoppings between the sites from sublattice b with a direction of (0,1) in the lattice coordinates. -.. image:: /code/figure/faq_direction2.* +.. jupyter-execute:: + :hide-code: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_direction3 - :end-before: #HIDDEN_END_direction3 + plot_system(syst) + +.. jupyter-execute:: + :hide-code: + + # Delete the hoppings previously created + del syst[kwant.builder.HoppingKind((0, 1), b, b)] + +.. jupyter-execute:: + + syst[kwant.builder.HoppingKind((0, 0), a, b)] = -1 + syst[kwant.builder.HoppingKind((0, 0), a, c)] = -1 + syst[kwant.builder.HoppingKind((0, 0), c, b)] = -1 Here, we create hoppings between the sites of the same lattice coordinates but from different families. -.. image:: /code/figure/faq_direction3.* +.. jupyter-execute:: + + plot_system(syst) How to set the hoppings between adjacent sites? @@ -230,32 +313,53 @@ How to set the hoppings between adjacent sites? that returns a list of ``HoppingKind`` instances that connect sites with their (n-nearest) neighors: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_adjacent1 - :end-before: #HIDDEN_END_adjacent1 +.. jupyter-execute:: + + # Create hoppings with lat.neighbors() + syst = kwant.Builder() + lat = kwant.lattice.square() + syst[(lat(i, j) for i in range(3) for j in range(3))] = 4 -.. image:: /code/figure/faq_adjacent1.* -.. image:: /code/figure/faq_adjacent2.* + syst[lat.neighbors()] = -1 # Equivalent to lat.neighbors(1) + kwant.plot(syst); -As we can see in the figure above, ``lat.neighbors()`` (on the left) returns the hoppings between the first nearest neighbors and ``lat.neighbors(2)`` (on the right) returns the hoppings between the second nearest neighbors. +.. jupyter-execute:: + + del syst[lat.neighbors()] # Delete all nearest-neighbor hoppings + syst[lat.neighbors(2)] = -1 + + kwant.plot(syst); + +As we can see in the figures above, ``lat.neighbors()`` returns the hoppings between the first nearest neighbors and ``lat.neighbors(2)`` returns the hoppings between the second nearest neighbors. When using a ``Polyatomic`` lattice ``neighbors()`` knows about the different sublattices: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_adjacent2 - :end-before: #HIDDEN_END_adjacent2 +.. jupyter-execute:: + + # Create the system + lat = kwant.lattice.kagome() + syst = kwant.Builder() + a, b, c = lat.sublattices # The kagome lattice has 3 sublattices + + for i in range(4): + for j in range (4): + syst[a(i, j)] = 4 # red + syst[b(i, j)] = 4 # green + syst[c(i, j)] = 4 # blue -.. image:: /code/figure/faq_adjacent3.* + syst[lat.neighbors()] = -1 + + plot_system(syst) However, if we use the ``neighbors()`` method of a single sublattice, we will only get the neighbors *on that sublattice*: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_adjacent3 - :end-before: #HIDDEN_END_adjacent3 +.. jupyter-execute:: -.. image:: /code/figure/faq_adjacent4.* + del syst[lat.neighbors()] # Delete the hoppings previously created + syst[a.neighbors()] = -1 + plot_system(syst) Note in the above that there are *only* hoppings between the red sites. This is an artifact of the visualisation: the blue and green sites just happen to lie @@ -268,12 +372,35 @@ To make a hole in the system, use ``del syst[site]``, just like with any other mapping. In the following example we remove all sites inside some "hole" region: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_hole - :end-before: #HIDDEN_END_hole +.. jupyter-execute:: + + # Define the lattice and the (empty) system + a = 2 + lat = kwant.lattice.cubic(a) + syst = kwant.Builder() + + L = 10 + W = 10 + H = 2 + + # Add sites to the system in a cuboid + + syst[(lat(i, j, k) for i in range(L) for j in range(W) for k in range(H))] = 4 + kwant.plot(syst); + +.. jupyter-execute:: + + # Delete sites to create a hole + + def in_hole(site): + x, y, z = site.pos / a - (L/2, W/2, H/2) # position relative to centre + return abs(x) < L / 4 and abs(y) < W / 4 + + for site in filter(in_hole, list(syst.sites())): + del syst[site] + + kwant.plot(syst); -.. image:: /code/figure/faq_hole1.* -.. image:: /code/figure/faq_hole2.* ``del syst[site]`` also works after hoppings have been added to the system. If a site is deleted, then all the hoppings to/from that site are also deleted. @@ -287,9 +414,18 @@ what is a builder?`_ if you do not know the difference). We can access the sites of a ``Builder`` by using its `~kwant.builder.Builder.sites` method: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_sites1 - :end-before: #HIDDEN_END_sites1 +.. jupyter-execute:: + :hide-code: + + builder = kwant.Builder() + lat = kwant.lattice.square() + builder[(lat(i, j) for i in range(3) for j in range(3))] = 4 + +.. jupyter-execute:: + + # Before finalizing the system + + sites = list(builder.sites()) # sites() doe *not* return a list The ``sites()`` method returns an *iterator* over the system sites, and in the above example we create a list from the contents of this iterator, which @@ -300,9 +436,11 @@ well be returned in a different order. After finalization, when we are dealing with a ``System``, the sites themselves are stored in a list, which can be accessed via the ``sites`` attribute: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_sites2 - :end-before: #HIDDEN_END_sites2 +.. jupyter-execute:: + + # After finalizing the system + syst = builder.finalized() + sites = syst.sites # syst.sites is an actual list The order of sites in a ``System`` is fixed, and also defines the ordering of the system Hamiltonian, system wavefunctions etc. (see `How does Kwant order components of an individual wavefunction?`_ for details). @@ -310,9 +448,9 @@ the system Hamiltonian, system wavefunctions etc. (see `How does Kwant order com ``System`` also contains the inverse mapping, ``id_by_site`` which gives us the index of a given site within the system: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_sites3 - :end-before: #HIDDEN_END_sites3 +.. jupyter-execute:: + + i = syst.id_by_site[lat(0, 2)] # we want the id of the site lat(0, 2) How to use different lattices for the scattering region and a lead? @@ -322,19 +460,35 @@ which we want to connect to leads that contain sites from a square lattice. First we construct the central system: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_different_lattice1 - :end-before: #HIDDEN_END_different_lattice1 +.. jupyter-execute:: -.. image:: /code/figure/faq_different_lattice1.* + # Define the scattering Region + L = 5 + W = 5 + + lat = kwant.lattice.honeycomb() + subA, subB = lat.sublattices + + syst = kwant.Builder() + syst[(subA(i, j) for i in range(L) for j in range(W))] = 4 + syst[(subB(i, j) for i in range(L) for j in range(W))] = 4 + syst[lat.neighbors()] = -1 + + kwant.plot(syst); and the lead: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_different_lattice2 - :end-before: #HIDDEN_END_different_lattice2 +.. jupyter-execute:: + + # Create a lead + lat_lead = kwant.lattice.square() + sym_lead1 = kwant.TranslationalSymmetry((0, 1)) + + lead1 = kwant.Builder(sym_lead1) + lead1[(lat_lead(i, 0) for i in range(2, 7))] = 4 + lead1[lat_lead.neighbors()] = -1 -.. image:: /code/figure/faq_different_lattice2.* + kwant.plot(syst); We cannot simply use `~kwant.builder.Builder.attach_lead` to attach this lead to the system with the honeycomb lattice because Kwant does not know how sites from @@ -343,19 +497,22 @@ these two lattices should be connected. We must first add a layer of sites from the square lattice to the system and manually add the hoppings from these sites to the sites from the honeycomb lattice: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_different_lattice3 - :end-before: #HIDDEN_END_different_lattice3 +.. jupyter-execute:: -.. image:: /code/figure/faq_different_lattice3.* + syst[(lat_lead(i, 5) for i in range(2, 7))] = 4 + syst[lat_lead.neighbors()] = -1 + + # Manually attach sites from graphene to square lattice + syst[((lat_lead(i+2, 5), subB(i, 4)) for i in range(5))] = -1 + + kwant.plot(syst); ``attach_lead()`` will now be able to attach the lead: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_different_lattice4 - :end-before: #HIDDEN_END_different_lattice4 +.. jupyter-execute:: -.. image:: /code/figure/faq_different_lattice4.* + syst.attach_lead(lead1) + kwant.plot(syst); How to cut a finite system out of a system with translational symmetries? @@ -369,30 +526,50 @@ will be repeated in the desired shape to create the final system. For example, say we want to create a simple model on a cubic lattice: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_fill1 - :end-before: #HIDDEN_END_fill1 +.. jupyter-execute:: + + # Create 3d model. + cubic = kwant.lattice.cubic() + sym_3d = kwant.TranslationalSymmetry([1, 0, 0], [0, 1, 0], [0, 0, 1]) + model = kwant.Builder(sym_3d) + model[cubic(0, 0, 0)] = 4 + model[cubic.neighbors()] = -1 We have now created our "template" ``Builder`` which has 3 translational symmetries. Next we will fill a system with no translational symmetries with sites and hoppings from the template inside a cuboid: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_fill2 - :end-before: #HIDDEN_END_fill2 +.. jupyter-execute:: -.. image:: /code/figure/faq_fill2.* + # Build scattering region (white). + def cuboid_shape(site): + x, y, z = abs(site.pos) + return x < 4 and y < 10 and z < 3 + + cuboid = kwant.Builder() + cuboid.fill(model, cuboid_shape, (0, 0, 0)); + kwant.plot(cuboid); We can then use the original template to create a lead, which has 1 translational symmetry. We can then use this lead as a template to fill another section of the system with a cylinder of sites and hoppings: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_fill3 - :end-before: #HIDDEN_END_fill3 +.. jupyter-execute:: + + # Build electrode (black). + def electrode_shape(site): + x, y, z = site.pos - (0, 5, 2) + return y**2 + z**2 < 2.3**2 -.. image:: /code/figure/faq_fill3.* + electrode = kwant.Builder(kwant.TranslationalSymmetry([1, 0, 0])) + electrode.fill(model, electrode_shape, (0, 5, 2)) # lead + # Scattering region + cuboid.fill(electrode, lambda s: abs(s.pos[0]) < 7, (0, 5, 4)) + + cuboid.attach_lead(electrode) + + kwant.plot(cuboid); How does Kwant order the propagating modes of a lead? ===================================================== @@ -402,16 +579,44 @@ achieved with the `~kwant.system.InfiniteSystem.modes` method, which returns a pair of objects, the first of which contains the propagating modes of the system in a `~kwant.physics.PropagatingModes` object: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_pm - :end-before: #HIDDEN_END_pm +.. jupyter-execute:: + + lat = kwant.lattice.square() + + lead = kwant.Builder(kwant.TranslationalSymmetry((-1, 0))) + lead[(lat(0, i) for i in range(3))] = 4 + lead[lat.neighbors()] = -1 + + flead = lead.finalized() + + E = 2.5 + prop_modes, _ = flead.modes(energy=E) ``PropagatingModes`` contains the wavefunctions, velocities and momenta of the modes at the requested energy (2.5 in this example). In order to understand the order in which these quantities are returned it is often useful to look at the a section of the band structure for the system in question: -.. image:: /code/figure/faq_pm1.* +.. jupyter-execute:: + :hide-code: + + def plot_and_label_modes(lead, E): + # Plot the different modes + pmodes, _ = lead.modes(energy=E) + kwant.plotter.bands(lead, show=False) + for i, k in enumerate(pmodes.momenta): + plt.plot(k, E, 'ko') + plt.annotate(str(i), xy=(k, E), xytext=(-5, 8), + textcoords='offset points', + bbox=dict(boxstyle='round,pad=0.1',fc='white', alpha=0.7)) + plt.plot([-3, 3], [E, E], 'r--') + plt.ylim(E-1, E+1) + plt.xlim(-2, 2) + plt.xlabel("momentum") + plt.ylabel("energy") + plt.show() + + plot_and_label_modes(flead, E) On the above band structure we have labelled the 4 modes in the order that they appear in the output of ``modes()`` at energy 2.5. Note that @@ -425,7 +630,20 @@ the modes are sorted in the following way: For more complicated systems and band structures this can lead to some unintuitive orderings: -.. image:: /code/figure/faq_pm2.* +.. jupyter-execute:: + :hide-code: + + s0 = np.eye(2) + sz = np.array([[1, 0], [0, -1]]) + + lead2 = kwant.Builder(kwant.TranslationalSymmetry((-1, 0))) + + lead2[(lat(0, i) for i in range(2))] = np.diag([1.8, -1]) + lead2[lat.neighbors()] = -1 * sz + + flead2 = lead2.finalized() + + plot_and_label_modes(flead2, 1) How does Kwant order scattering states? @@ -452,10 +670,38 @@ When all the site families present in a system have only 1 degree of freedom per site (i.e. the all the onsites are scalars) then the index into a wavefunction defined over the system is exactly the site index: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_ord1 - :end-before: #HIDDEN_END_ord1 -.. literalinclude:: /code/figure/faq_ord1.txt +.. jupyter-execute:: + :hide-code: + + def circle(R): + return lambda r: np.linalg.norm(r) < R + + + def make_system(lat): + norbs = lat.norbs + syst = kwant.Builder() + syst[lat.shape(circle(3), (0, 0))] = 4 * np.eye(norbs) + syst[lat.neighbors()] = -1 * np.eye(norbs) + + lead = kwant.Builder(kwant.TranslationalSymmetry((-1, 0))) + lead[(lat(0, i) for i in range(-1, 2))] = 4 * np.eye(norbs) + lead[lat.neighbors()] = -1 * np.eye(norbs) + + syst.attach_lead(lead) + syst.attach_lead(lead.reversed()) + + return syst.finalized() + +.. jupyter-execute:: + + lat = kwant.lattice.square(norbs=1) + syst = make_system(lat) + scattering_states = kwant.wave_function(syst, energy=1) + wf = scattering_states(0)[0] # scattering state from lead 0 incoming in mode 0 + + idx = syst.id_by_site[lat(0, 0)] # look up index of site + + print('wavefunction on lat(0, 0): ', wf[idx]) We see that the wavefunction on a single site is a single complex number, as expected. @@ -467,10 +713,20 @@ to one another. In the case where all site families in the system have the wavefunction into a matrix, where the row number indexes the site, and the column number indexes the degree of freedom on that site: -.. literalinclude:: /code/include/faq.py - :start-after: #HIDDEN_BEGIN_ord2 - :end-before: #HIDDEN_END_ord2 -.. literalinclude:: /code/figure/faq_ord2.txt +.. jupyter-execute:: + + lat = kwant.lattice.square(norbs=2) + syst = make_system(lat) + scattering_states = kwant.wave_function(syst, energy=1) + wf = scattering_states(0)[0] # scattering state from lead 0 incoming in mode 0 + + idx = syst.id_by_site[lat(0, 0)] # look up index of site + + # Group consecutive degrees of freedom from 'wf' together; these correspond + # to degrees of freedom on the same site. + wf = wf.reshape(-1, 2) + + print('wavefunction on lat(0, 0): ', wf[idx]) We see that the wavefunction on a single site is a *vector* of 2 complex numbers, as we expect. diff --git a/doc/source/tutorial/first_steps.rst b/doc/source/tutorial/first_steps.rst index 1ddce048c4da222856413471aeb452390ed3e7c7..f509aaa0c262be17a1eaa53b8551812e06e88f22 100644 --- a/doc/source/tutorial/first_steps.rst +++ b/doc/source/tutorial/first_steps.rst @@ -13,7 +13,7 @@ Schrödinger equation .. math:: H = \frac{-\hbar^2}{2m}(\partial_x^2 + \partial_y^2) + V(y) -with a hard-wall confinement :math:`V(y)` in y-direction. +with a hard-wall confinement :math:`V(y)` in the y-direction. To be able to implement the quantum wire with Kwant, the continuous Hamiltonian :math:`H` has to be discretized thus turning it into a tight-binding @@ -62,23 +62,50 @@ Transport through a quantum wire .. seealso:: 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: -.. literalinclude:: /code/include/quantum_wire.py - :start-after: #HIDDEN_BEGIN_dwhx - :end-before: #HIDDEN_END_dwhx +.. jupyter-kernel:: + :id: quantum_wire + +.. 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:: boilerplate.py + :hide-code: + +.. jupyter-execute:: + + import kwant Enabling Kwant is as easy as this [#]_ ! -The first step is now the definition of the system with scattering region and +The first step is now to define the system with scattering region and 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: -.. literalinclude:: /code/include/quantum_wire.py - :start-after: #HIDDEN_BEGIN_goiq - :end-before: #HIDDEN_END_goiq +.. jupyter-execute:: + + # First define the tight-binding system + + syst = kwant.Builder() Observe that we just accessed `~kwant.builder.Builder` by the name ``kwant.Builder``. We could have just as well written @@ -92,9 +119,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 a square lattice. For simplicity, we set the lattice constant to unity: -.. literalinclude:: /code/include/quantum_wire.py - :start-after: #HIDDEN_BEGIN_suwo - :end-before: #HIDDEN_END_suwo +.. jupyter-execute:: + + a = 1 + lat = kwant.lattice.square(a) Since we work with a square lattice, we label the points with two integer coordinates `(i, j)`. `~kwant.builder.Builder` then @@ -111,9 +139,25 @@ needed in Builder (more about that in the technical details below). We now build a rectangular scattering region that is `W` lattice points wide and `L` lattice points long: -.. literalinclude:: /code/include/quantum_wire.py - :start-after: #HIDDEN_BEGIN_zfvr - :end-before: #HIDDEN_END_zfvr +.. jupyter-execute:: + + 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 Hamiltonian: @@ -140,9 +184,14 @@ Next, we define the leads. Leads are also constructed using `~kwant.builder.Builder`, but in this case, the system must have a translational symmetry: -.. literalinclude:: /code/include/quantum_wire.py - :start-after: #HIDDEN_BEGIN_xcmc - :end-before: #HIDDEN_END_xcmc +.. jupyter-execute:: + + # 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 `~kwant.lattice.TranslationalSymmetry` as the optional parameter. Note that the @@ -155,17 +204,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 simply a vertical line of points: -.. literalinclude:: /code/include/quantum_wire.py - :start-after: #HIDDEN_BEGIN_ndez - :end-before: #HIDDEN_END_ndez +.. jupyter-execute:: + + for j in range(W): + left_lead[lat(0, j)] = 4 * t + if j > 0: + left_lead[lat(0, j), lat(0, j - 1)] = -t + left_lead[lat(1, j), lat(0, j)] = -t Note that here it doesn't matter if you add the hoppings to the next or the previous unit cell -- the translational symmetry takes care of that. The isolated, infinite is attached at the correct position using -.. literalinclude:: /code/include/quantum_wire.py - :start-after: #HIDDEN_BEGIN_fskr - :end-before: #HIDDEN_END_fskr +.. jupyter-execute:: + :hide-output: + + syst.attach_lead(left_lead) This call returns the lead number which will be used to refer to the lead when computing transmissions (further down in this tutorial). More details about @@ -175,9 +229,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 symmetry must point to the right, the remaining code is the same: -.. literalinclude:: /code/include/quantum_wire.py - :start-after: #HIDDEN_BEGIN_xhqc - :end-before: #HIDDEN_END_xhqc +.. jupyter-execute:: + :hide-output: + + # 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. You might object that the right lead should be placed `L` @@ -187,13 +253,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 make any mistakes: -.. literalinclude:: /code/include/quantum_wire.py - :start-after: #HIDDEN_BEGIN_wsgh - :end-before: #HIDDEN_END_wsgh +.. jupyter-execute:: -This should bring up this picture: - -.. image:: /code/figure/quantum_wire_syst.* + kwant.plot(syst); The system is represented in the usual way for tight-binding systems: dots represent the lattice points `(i, j)`, and for every @@ -203,16 +265,29 @@ fading color. In order to use our system for a transport calculation, we need to finalize it -.. literalinclude:: /code/include/quantum_wire.py - :start-after: #HIDDEN_BEGIN_dngj - :end-before: #HIDDEN_END_dngj +.. jupyter-execute:: + + # Finalize the system + syst = syst.finalized() Having successfully created a system, we now can immediately start to compute its conductance as a function of energy: -.. literalinclude:: /code/include/quantum_wire.py - :start-after: #HIDDEN_BEGIN_buzn - :end-before: #HIDDEN_END_buzn +.. jupyter-execute:: + + # 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 `kwant.solvers.default.smatrix` of the default solver module @@ -227,13 +302,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 gnuplot or xmgrace is just as viable) -.. literalinclude:: /code/include/quantum_wire.py - :start-after: #HIDDEN_BEGIN_lliv - :end-before: #HIDDEN_END_lliv +.. jupyter-execute:: -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`, increasing in steps as the energy is increased. The @@ -333,7 +410,12 @@ Building the same system with less code .. seealso:: 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.builder.Builder` is essentially just a container that can be filled in @@ -347,19 +429,56 @@ the code into separate entities. In this example we therefore also aim at more structure. We begin the program collecting all imports in the beginning of the -file and put the build-up of the system into a separate function -`make_system`: +file and defining the a square lattice and empty scattering region. + +.. 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 + +.. jupyter-execute:: boilerplate.py + :hide-code: + +.. jupyter-execute:: + + 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. Instead, we now write: -.. literalinclude:: /code/include/quantum_wire_revisited.py - :start-after: #HIDDEN_BEGIN_vvjt - :end-before: #HIDDEN_END_vvjt + +.. jupyter-execute:: + + 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 construct ``((i, j) for i in range(L) for j in range(W))`` is a @@ -375,9 +494,9 @@ hoppings. In this case, an iterable like for the lattice points becomes a bit cumbersome, and we use instead another feature of Kwant: -.. literalinclude:: /code/include/quantum_wire_revisited.py - :start-after: #HIDDEN_BEGIN_nooi - :end-before: #HIDDEN_END_nooi +.. jupyter-execute:: + + syst[lat.neighbors()] = -t In regular lattices, hoppings form large groups such that hoppings within a group can be transformed into one another by lattice translations. In order to @@ -397,49 +516,48 @@ values for all the nth-nearest neighbors at once, one can similarly use The left lead is constructed in an analogous way: -.. literalinclude:: /code/include/quantum_wire_revisited.py - :start-after: #HIDDEN_BEGIN_iepx - :end-before: #HIDDEN_END_iepx +.. jupyter-execute:: + + 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 right lead. The only difference was the direction of the translational symmetry vector. Here, we only construct the left lead, and use the method `~kwant.builder.Builder.reversed` of `~kwant.builder.Builder` to obtain a copy of a lead pointing in the opposite direction. Both leads are attached as -before and the finished system returned: +before: -.. literalinclude:: /code/include/quantum_wire_revisited.py - :start-after: #HIDDEN_BEGIN_yxot - :end-before: #HIDDEN_END_yxot +.. jupyter-execute:: + :hide-output: -The remainder of the script has been organized into two functions. One for the -plotting of the conductance. + syst.attach_lead(lead) + syst.attach_lead(lead.reversed()) -.. literalinclude:: /code/include/quantum_wire_revisited.py - :start-after: #HIDDEN_BEGIN_ayuk - :end-before: #HIDDEN_END_ayuk +The remainder of the script proceeds identically. We first finalize the system: -And one ``main`` function. +.. jupyter-execute:: -.. literalinclude:: /code/include/quantum_wire_revisited.py - :start-after: #HIDDEN_BEGIN_cjel - :end-before: #HIDDEN_END_cjel + syst = syst.finalized() -Finally, we use the following standard Python construct [#]_ to execute -``main`` if the program is used as a script (i.e. executed as -``python quantum_wire_revisited.py``): +and then calculate the transmission and plot: -.. literalinclude:: /code/include/quantum_wire_revisited.py - :start-after: #HIDDEN_BEGIN_ypbj - :end-before: #HIDDEN_END_ypbj +.. jupyter-execute:: -If the example, however, is imported inside Python using ``import -quantum_wire_revisted as qw``, ``main`` is not executed automatically. -Instead, you can execute it manually using ``qw.main()``. On the other -hand, you also have access to the other functions, ``make_system`` and -``plot_conductance``, and can thus play with the parameters. + energies = [] + data = [] + for ie in range(100): + energy = ie * 0.01 + smatrix = kwant.smatrix(syst, energy) + energies.append(energy) + data.append(smatrix.transmission(1, 0)) -The result of the example should be identical to the previous one. + pyplot.figure() + pyplot.plot(energies, data) + pyplot.xlabel("energy [t]") + pyplot.ylabel("conductance [e^2/h]") + pyplot.show() .. specialnote:: Technical details @@ -453,11 +571,9 @@ The result of the example should be identical to the previous one. For technical reasons it is not possible to add several points using a tuple of sites. Hence it is worth noting - a subtle detail in + a subtle detail in: - .. literalinclude:: /code/include/quantum_wire_revisited.py - :start-after: #HIDDEN_BEGIN_vvjt - :end-before: #HIDDEN_END_vvjt + >>> syst[(lat(x, y) for x in range(L) for y in range(W))] = 4 * t Note that ``(lat(x, y) for x in range(L) for y in range(W))`` is not a tuple, but a generator. @@ -512,6 +628,132 @@ The result of the example should be identical to the previous one. it would be impossible to distinguish whether one would like to add two separate sites, or one hopping. + +Tips for organizing your simulation scripts +........................................... + +.. seealso:: + The complete source code of this example can be found in + :jupyter-download:script:`quantum_wire_organized` + + +.. jupyter-kernel:: + :id: quantum_wire_organized + +.. jupyter-execute:: + :hide-code: + + # Tutorial 2.2.4. Organizing a simulation script + # ============================================== + # + # Physics background + # ------------------ + # Conductance of a quantum wire; subbands + # + # Note: Does the same as quantum_write_revisited.py, but features + # better code organization + + +The above two examples illustrate some of the core features of Kwant, however +the code was presented in a style which is good for exposition, but which is +bad for making your code understandable and reusable. In this example we will +lay out some best practices for writing your own simulation scripts. + +In the above examples we constructed a single Kwant system, using global variables +for parameters such as the lattice constant and the length and width of the system. +Instead, it is preferable to create a *function* that you can call, and which will +return a Kwant ``Builder``: + +.. jupyter-execute:: + + from matplotlib import pyplot + import kwant + +.. jupyter-execute:: boilerplate.py + :hide-code: + +.. jupyter-execute:: + + def make_system(L, W, a=1, t=1.0): + lat = kwant.lattice.square(a) + + syst = kwant.Builder() + syst[(lat(i, j) for i in range(L) for j in range(W))] = 4 * t + syst[lat.neighbors()] = -t + + 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()) + + return syst + + +By encapsulating system creation within ``make_system`` we *document* our code +by telling readers that *this* is how we create a system, and that creating a system +depends on *these* parameters (the length and width of the system, in this case, as well +as the lattice constant and the value for the hopping parameter). By defining a function +we also ensure that we can consistently create different systems (e.g. of different sizes) +of the same type (rectangular slab). + +We similarly encapsulate the part of the script that does computation and plotting into +a function ``plot_conductance``: + +.. jupyter-execute:: + + 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 the ``main`` function that glues together the components that we previously defined: + +.. jupyter-execute:: + + def main(): + syst = make_system(W=10, L=30) + + # 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 +``main`` if the program is used as a script (i.e. executed as +``python quantum_wire_organized.py``): + + +.. jupyter-execute:: + + # 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 +quantum_wire_organized as qw``, ``main`` is not executed automatically. +Instead, you can execute it manually using ``qw.main()``. On the other +hand, you also have access to the other functions, ``make_system`` and +``plot_conductance``, and can thus play with the parameters. + +The result of this example should be identical to the previous one. + + .. rubric:: Footnotes .. [#] https://docs.python.org/3/library/__main__.html diff --git a/doc/source/tutorial/graphene.rst b/doc/source/tutorial/graphene.rst index 63bcf5f29859ea09f11b84410c64871bf49fe9b5..6eaa93d6ed142f430bd078399b2850cdea61cf32 100644 --- a/doc/source/tutorial/graphene.rst +++ b/doc/source/tutorial/graphene.rst @@ -5,7 +5,7 @@ Beyond square lattices: graphene .. seealso:: 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 conductance through a graphene quantum dot with a p-n junction @@ -18,9 +18,43 @@ We begin by defining the honeycomb lattice of graphene. This is in principle already done in `kwant.lattice.honeycomb`, but we do it explicitly here to show how to define a new lattice: -.. literalinclude:: /code/include/graphene.py - :start-after: #HIDDEN_BEGIN_hnla - :end-before: #HIDDEN_END_hnla +.. jupyter-kernel:: + :id: graphene + +.. 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:: boilerplate.py + :hide-code: + +.. 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 primitive vectors of the lattice; the second one is the coordinates of basis @@ -31,9 +65,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) and add all lattice points using the ``shape``-functionality: -.. literalinclude:: /code/include/graphene.py - :start-after: #HIDDEN_BEGIN_shzy - :end-before: #HIDDEN_END_shzy +.. jupyter-execute:: + :hide-code: + + 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. We add the onsite energies using a function describing the p-n junction; @@ -45,9 +100,11 @@ As a next step we add the hoppings, making use of `~kwant.builder.HoppingKind`. For illustration purposes we define the hoppings ourselves instead of using ``graphene.neighbors()``: -.. literalinclude:: /code/include/graphene.py - :start-after: #HIDDEN_BEGIN_hsmc - :end-before: #HIDDEN_END_hsmc +.. jupyter-execute:: + + # 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 hoppings between different basis atoms. For this type of @@ -63,27 +120,50 @@ respect to the two primitive vectors ``[(1, 0), (sin_30, cos_30)]``. Adding the hoppings however still works the same way: -.. literalinclude:: /code/include/graphene.py - :start-after: #HIDDEN_BEGIN_bfwb - :end-before: #HIDDEN_END_bfwb +.. jupyter-execute:: + + syst[[kwant.builder.HoppingKind(*hopping) for hopping in hoppings]] = -1 Modifying the scattering region is also possible as before. Let's do something crazy, and remove an atom in sublattice A (which removes also the hoppings from/to this site) as well as add an additional link: -.. literalinclude:: /code/include/graphene.py - :start-after: #HIDDEN_BEGIN_efut - :end-before: #HIDDEN_END_efut +.. jupyter-execute:: + + # 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 is done by the sublattices `a` and `b`. The leads are defined almost as before: -.. literalinclude:: /code/include/graphene.py - :start-after: #HIDDEN_BEGIN_aakh - :end-before: #HIDDEN_END_aakh +.. jupyter-execute:: + + #### 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 parameter for `~kwant.lattice.TranslationalSymmetry`. The latter expects a @@ -98,28 +178,63 @@ in a square lattice -- they follow the non-orthogonal primitive vectors defined in the beginning. Later, we will compute some eigenvalues of the closed scattering region without -leads. This is why we postpone attaching the leads to the system. Instead, -we return the scattering region and the leads separately. +leads. This is why we postpone attaching the leads to the system. -.. 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 in the following piece of code: -.. literalinclude:: /code/include/graphene.py - :start-after: #HIDDEN_BEGIN_zydk - :end-before: #HIDDEN_END_zydk +.. jupyter-execute:: + + 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 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() + + + def plot_bandstructure(flead, momenta): + bands = kwant.physics.Bands(flead) + 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 -.. literalinclude:: /code/include/graphene.py - :start-after: #HIDDEN_BEGIN_itkk - :end-before: #HIDDEN_END_itkk + # 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 `~kwant.plotter.plot` to a function which returns 0 for @@ -132,27 +247,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 independent on the overall figure size. -Plotting the closed system gives this result: - -.. image:: /code/figure/graphene_syst1.* Computing the eigenvalues of largest magnitude, -.. literalinclude:: /code/include/graphene.py - :start-after: #HIDDEN_BEGIN_jmbi - :end-before: #HIDDEN_END_jmbi +.. jupyter-execute:: -should yield two eigenvalues equal to ``[ 3.07869311, + compute_evs(syst.finalized()) + +yields two eigenvalues equal to ``[ 3.07869311, -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: -.. 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); -It computes the band structure of one of lead 0: +We then finalize the system: -.. image:: /code/figure/graphene_bs.* +.. jupyter-execute:: + + 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 edge state bands (note that the band structure is not symmetric around @@ -160,7 +291,11 @@ zero energy, due to a potential in the leads). 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 an open quantum dot diff --git a/doc/source/tutorial/kpm.rst b/doc/source/tutorial/kpm.rst index 38c84891cbd84ea044e8600ee56c3e3d0a1cd638..7e1232581335aa1a77969899f59344e34c93eba7 100644 --- a/doc/source/tutorial/kpm.rst +++ b/doc/source/tutorial/kpm.rst @@ -17,8 +17,30 @@ KPM method `kwant.kpm`, that is based on the algorithms presented in Ref. [1]_. .. seealso:: The complete source code of this example can be found in - :download:`kernel_polynomial_method.py </code/download/kernel_polynomial_method.py>` + :jupyter-download:script:`kernel_polynomial_method` +.. jupyter-kernel:: + :id: kernel_polynomial_method + +.. jupyter-execute:: + :hide-code: + + # Tutorial 2.8. Calculating spectral density with the Kernel Polynomial Method + # ============================================================================ + # + # Physics background + # ------------------ + # - Chebyshev polynomials, random trace approximation, spectral densities. + # + # Kwant features highlighted + # -------------------------- + # - kpm module,kwant operators. + + import scipy + from matplotlib import pyplot + +.. jupyter-execute:: boilerplate.py + :hide-code: Introduction ************ @@ -103,35 +125,104 @@ to obtain the (global) density of states of a graphene disk. We start by importing kwant and defining our system. -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_sys1 - :end-before: #HIDDEN_END_sys1 +.. jupyter-execute:: + + # necessary imports + import kwant + import numpy as np + + + # define the system + def make_syst(r=30, t=-1, a=1): + syst = kwant.Builder() + lat = kwant.lattice.honeycomb(a, norbs=1) + + def circle(pos): + x, y = pos + return x ** 2 + y ** 2 < r ** 2 + + syst[lat.shape(circle, (0, 0))] = 0. + syst[lat.neighbors()] = t + syst.eradicate_dangling() + + return syst + +.. jupyter-execute:: + :hide-code: + + ## common plotting routines ## + + # Plot several density of states curves on the same axes. + def plot_dos(labels_to_data): + pyplot.figure() + for label, (x, y) in labels_to_data: + pyplot.plot(x, y.real, label=label, linewidth=2) + pyplot.legend(loc=2, framealpha=0.5) + pyplot.xlabel("energy [t]") + pyplot.ylabel("DoS [a.u.]") + pyplot.show() + + + # Plot fill density of states plus curves on the same axes. + def plot_dos_and_curves(dos, labels_to_data): + pyplot.figure() + pyplot.fill_between(dos[0], dos[1], label="DoS [a.u.]", + alpha=0.5, color='gray') + for label, (x, y) in labels_to_data: + pyplot.plot(x, y, label=label, linewidth=2) + pyplot.legend(loc=2, framealpha=0.5) + pyplot.xlabel("energy [t]") + pyplot.ylabel("$σ [e^2/h]$") + pyplot.show() + + + def site_size_conversion(densities): + return 3 * np.abs(densities) / max(densities) + + + # Plot several local density of states maps in different subplots + def plot_ldos(syst, densities): + fig, axes = pyplot.subplots(1, len(densities), figsize=(7*len(densities), 7)) + for ax, (title, rho) in zip(axes, densities): + kwant.plotter.density(syst, rho.real, ax=ax) + ax.set_title(title) + ax.set(adjustable='box', aspect='equal') + pyplot.show() After making a system we can then create a `~kwant.kpm.SpectralDensity` object that represents the density of states for this system. -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_kpm1 - :end-before: #HIDDEN_END_kpm1 +.. jupyter-execute:: + + fsyst = make_syst().finalized() + + spectrum = kwant.kpm.SpectralDensity(fsyst) The `~kwant.kpm.SpectralDensity` can then be called like a function to obtain a sequence of energies in the spectrum of the Hamiltonian, and the corresponding density of states at these energies. -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_kpm2 - :end-before: #HIDDEN_END_kpm2 +.. jupyter-execute:: + + energies, densities = spectrum() When called with no arguments, an optimal set of energies is chosen (these are not evenly distributed over the spectrum, see Ref. [1]_ for details), however it is also possible to provide an explicit sequence of energies at which to evaluate the density of states. -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_kpm3 - :end-before: #HIDDEN_END_kpm3 +.. jupyter-execute:: + + energy_subset = np.linspace(0, 2) + density_subset = spectrum(energy_subset) + +.. jupyter-execute:: + :hide-code: -.. image:: /code/figure/kpm_dos.* + plot_dos([ + ('densities', (energies, densities)), + ('density subset', (energy_subset, density_subset)), + ]) In addition to being called like functions, `~kwant.kpm.SpectralDensity` objects also have a method `~kwant.kpm.SpectralDensity.integrate` which can be @@ -139,22 +230,21 @@ used to integrate the density of states against some distribution function over the whole spectrum. If no distribution function is specified, then the uniform distribution is used: -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_int1 - :end-before: #HIDDEN_END_int1 +.. jupyter-execute:: -.. literalinclude:: /code/figure/kpm_normalization.txt + print('identity resolution:', spectrum.integrate()) We see that the integral of the density of states is normalized to the total number of available states in the system. If we wish to calculate, say, the number of states populated in equilibrium, then we should integrate with respect to a Fermi-Dirac distribution: -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_int2 - :end-before: #HIDDEN_END_int2 +.. jupyter-execute:: -.. literalinclude:: /code/figure/kpm_total_states.txt + # Fermi energy 0.1 and temperature 0.2 + fermi = lambda E: 1 / (np.exp((E - 0.1) / 0.2) + 1) + + print('number of filled states:', spectrum.integrate(fermi)) .. specialnote:: Stability and performance: spectral bounds @@ -194,23 +284,51 @@ In the following example, we compute the local density of states at the center of the graphene disk, and we add a staggering potential between the two sublattices. -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_sys3 - :end-before: #HIDDEN_END_sys3 +.. jupyter-execute:: + + def make_syst_staggered(r=30, t=-1, a=1, m=0.1): + syst = kwant.Builder() + lat = kwant.lattice.honeycomb(a, norbs=1) + + def circle(pos): + x, y = pos + return x ** 2 + y ** 2 < r ** 2 + + syst[lat.a.shape(circle, (0, 0))] = m + syst[lat.b.shape(circle, (0, 0))] = -m + syst[lat.neighbors()] = t + syst.eradicate_dangling() + + return syst Next, we choose one site of each sublattice "A" and "B", -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_op5 - :end-before: #HIDDEN_END_op5 +.. jupyter-execute:: + + fsyst_staggered = make_syst_staggered().finalized() + # find 'A' and 'B' sites in the unit cell at the center of the disk + center_tag = np.array([0, 0]) + where = lambda s: s.tag == center_tag + # make local vectors + vector_factory = kwant.kpm.LocalVectors(fsyst_staggered, where) and plot their respective local density of states. -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_op6 - :end-before: #HIDDEN_END_op6 +.. jupyter-execute:: + + # 'num_vectors' can be unspecified when using 'LocalVectors' + local_dos = kwant.kpm.SpectralDensity(fsyst_staggered, num_vectors=None, + vector_factory=vector_factory, + mean=False) + energies, densities = local_dos() -.. image:: /code/figure/kpm_ldos_sites.* +.. jupyter-execute:: + :hide-code: + + plot_dos([ + ('A sublattice', (energies, densities[:, 0])), + ('B sublattice', (energies, densities[:, 1])), + ]) Note that there is no noise comming from the random vectors. @@ -225,9 +343,15 @@ exactly is changed. The simplest way to obtain a more accurate solution is to use the ``add_moments`` method: -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_acc1 - :end-before: #HIDDEN_END_acc1 +.. jupyter-execute:: + :hide-code: + + spectrum = kwant.kpm.SpectralDensity(fsyst) + original_dos = spectrum() + +.. jupyter-execute:: + + spectrum.add_moments(energy_resolution=0.03) This will update the number of calculated moments and also the default number of sampling points such that the maximum distance between successive @@ -237,12 +361,19 @@ Alternatively, you can directly increase the number of moments with ``add_moments``, or the number of random vectors with ``add_vectors``. The added vectors will be generated with the ``vector_factory``. -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_acc2 - :end-before: #HIDDEN_END_acc2 +.. jupyter-execute:: + + spectrum.add_moments(100) + spectrum.add_vectors(5) -.. image:: /code/figure/kpm_dos_r.* +.. jupyter-execute:: + :hide-code: + increased_moments_dos = spectrum() + plot_dos([ + ('density', original_dos), + ('higher number of moments', increased_moments_dos), + ]) .. _operator_spectral_density: @@ -264,17 +395,19 @@ the spectral density of the given operator that is calculated. If an explicit matrix is provided then it must have the same shape as the system Hamiltonian. -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_op1 - :end-before: #HIDDEN_END_op1 +.. jupyter-execute:: + # identity matrix + matrix_op = scipy.sparse.eye(len(fsyst.sites)) + matrix_spectrum = kwant.kpm.SpectralDensity(fsyst, operator=matrix_op) Or, to do the same calculation using `kwant.operator.Density`: -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_op2 - :end-before: #HIDDEN_END_op2 +.. jupyter-execute:: + # 'sum=True' means we sum over all the sites + kwant_op = kwant.operator.Density(fsyst, sum=True) + operator_spectrum = kwant.kpm.SpectralDensity(fsyst, operator=kwant_op) Spectral density with random vectors ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -283,9 +416,11 @@ Using operators from `kwant.operator` allows us to calculate quantities such as the *local* density of states by telling the operator not to sum over all the sites of the system: -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_op3 - :end-before: #HIDDEN_END_op3 +.. jupyter-execute:: + + # 'sum=False' is the default, but we include it explicitly here for clarity. + kwant_op = kwant.operator.Density(fsyst, sum=False) + local_dos = kwant.kpm.SpectralDensity(fsyst, operator=kwant_op) `~kwant.kpm.SpectralDensity` will properly handle this vector output, and will average the local density obtained with random vectors. @@ -294,12 +429,18 @@ The accuracy of this approximation depends on the number of random vectors used. This allows us to plot an approximate local density of states at different points in the spectrum: -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_op4 - :end-before: #HIDDEN_END_op4 +.. jupyter-execute:: -.. image:: /code/figure/kpm_ldos.* + zero_energy_ldos = local_dos(energy=0) + finite_energy_ldos = local_dos(energy=1) +.. jupyter-execute:: + :hide-code: + + plot_ldos(fsyst, [ + ('energy = 0', zero_energy_ldos), + ('energy = 1', finite_energy_ldos) + ]) Calculating Kubo conductivity ***************************** @@ -325,17 +466,53 @@ with nearest neigbours hoppings. To turn it into a topological insulator we add imaginary second nearest neigbours hoppings, where the sign changes for each sublattice. -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_sys2 - :end-before: #HIDDEN_END_sys2 +.. jupyter-execute:: + + def make_syst_topo(r=30, a=1, t=1, t2=0.5): + syst = kwant.Builder() + lat = kwant.lattice.honeycomb(a, norbs=1, name=['a', 'b']) + + def circle(pos): + x, y = pos + return x ** 2 + y ** 2 < r ** 2 + + syst[lat.shape(circle, (0, 0))] = 0. + syst[lat.neighbors()] = t + # add second neighbours hoppings + syst[lat.a.neighbors()] = 1j * t2 + syst[lat.b.neighbors()] = -1j * t2 + syst.eradicate_dangling() + + return lat, syst.finalized() To calculate the bulk conductivity, we will select sites in the unit cell in the middle of the sample, and create a vector factory that outputs local vectors -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_cond - :end-before: #HIDDEN_END_cond +.. jupyter-execute:: + + # construct the Haldane model + lat, fsyst_topo = make_syst_topo() + # find 'A' and 'B' sites in the unit cell at the center of the disk + where = lambda s: np.linalg.norm(s.pos) < 1 + + # component 'xx' + s_factory = kwant.kpm.LocalVectors(fsyst_topo, where) + cond_xx = kwant.kpm.conductivity(fsyst_topo, alpha='x', beta='x', mean=True, + num_vectors=None, vector_factory=s_factory) + # component 'xy' + s_factory = kwant.kpm.LocalVectors(fsyst_topo, where) + cond_xy = kwant.kpm.conductivity(fsyst_topo, alpha='x', beta='y', mean=True, + num_vectors=None, vector_factory=s_factory) + + energies = cond_xx.energies + cond_array_xx = np.array([cond_xx(e, temperature=0.01) for e in energies]) + cond_array_xy = np.array([cond_xy(e, temperature=0.01) for e in energies]) + + # area of the unit cell per site + area_per_site = np.abs(np.cross(*lat.prim_vecs)) / len(lat.sublattices) + cond_array_xx /= area_per_site + cond_array_xy /= area_per_site Note that the Kubo conductivity must be normalized with the area covered by the vectors. In this case, each local vector represents a site, and @@ -345,8 +522,22 @@ value of the conductivity over large parts of the system. In this case, the area that normalizes the result, is the area covered by the random vectors. -.. image:: /code/figure/kpm_cond.* +.. jupyter-execute:: + :hide-code: + + s_factory = kwant.kpm.LocalVectors(fsyst_topo, where) + spectrum = kwant.kpm.SpectralDensity(fsyst_topo, num_vectors=None, + vector_factory=s_factory) + + plot_dos_and_curves( + (spectrum.energies, spectrum.densities * 8), + [ + (r'Longitudinal conductivity $\sigma_{xx} / 4$', + (energies, cond_array_xx / 4)), + (r'Hall conductivity $\sigma_{xy}$', + (energies, cond_array_xy))], + ) .. _advanced_topics: @@ -370,9 +561,16 @@ To change how the random vectors are generated, you need only specify a function that takes the dimension of the Hilbert space as a single parameter, and which returns a vector in that Hilbert space: -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_fact1 - :end-before: #HIDDEN_END_fact1 +.. jupyter-execute:: + + # construct a generator of vectors with n random elements -1 or +1. + n = fsyst.hamiltonian_submatrix(sparse=True).shape[0] + def binary_vectors(): + while True: + yield np.rint(np.random.random_sample(n)) * 2 - 1 + + custom_factory = kwant.kpm.SpectralDensity(fsyst, + vector_factory=binary_vectors()) Aditionally, a `~kwant.kpm.LocalVectors` generator is also available, that returns local vectors that correspond to the sites passed. Note that @@ -407,9 +605,16 @@ first argument, and linear in its second argument. Below, we compare two methods for computing the local density of states, one using `kwant.operator.Density`, and the other using a custom function. -.. literalinclude:: /code/include/kernel_polynomial_method.py - :start-after: #HIDDEN_BEGIN_blm - :end-before: #HIDDEN_END_blm +.. jupyter-execute:: + + rho = kwant.operator.Density(fsyst, sum=True) + + # sesquilinear map that does the same thing as `rho` + def rho_alt(bra, ket): + return np.vdot(bra, ket) + + rho_spectrum = kwant.kpm.SpectralDensity(fsyst, operator=rho) + rho_alt_spectrum = kwant.kpm.SpectralDensity(fsyst, operator=rho_alt) __ operator_spectral_density_ diff --git a/doc/source/tutorial/operators.rst b/doc/source/tutorial/operators.rst index ae397e3f55ce58656b7910724c5d78dadd8542d9..1646ae86e2cd5a47747843f6dd952144b0a92175 100644 --- a/doc/source/tutorial/operators.rst +++ b/doc/source/tutorial/operators.rst @@ -13,8 +13,46 @@ texture. .. seealso:: 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) + +.. jupyter-execute:: boilerplate.py + :hide-code: Introduction ------------ @@ -47,28 +85,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 depend on the model parameters: -.. literalinclude:: /code/include/magnetic_texture.py - :start-after: #HIDDEN_BEGIN_model - :end-before: #HIDDEN_END_model +.. jupyter-execute:: + + 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 per site, with leads attached on the left and right: -.. literalinclude:: /code/include/magnetic_texture.py - :start-after: #HIDDEN_BEGIN_syst - :end-before: #HIDDEN_END_syst +.. jupyter-execute:: + + 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 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 that originate from the left lead: -.. literalinclude:: /code/include/magnetic_texture.py - :start-after: #HIDDEN_BEGIN_wavefunction - :end-before: #HIDDEN_END_wavefunction +.. jupyter-execute:: + + params = dict(r0=20, delta=10, J=1) + wf = kwant.wave_function(syst, energy=-1, params=params) + psi = wf(0)[0] Local densities --------------- @@ -82,34 +211,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 one could calculate the per-site density like: -.. literalinclude:: /code/include/magnetic_texture.py - :start-after: #HIDDEN_BEGIN_ldos - :end-before: #HIDDEN_END_ldos +.. jupyter-execute:: + + # 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 local quantities we can meaningfully compute. For example, we may wish to calculate the local z-projected spin density. We could calculate this in the following way: -.. literalinclude:: /code/include/magnetic_texture.py - :start-after: #HIDDEN_BEGIN_lsdz - :end-before: #HIDDEN_END_lsdz +.. jupyter-execute:: + + # 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 need to use an even more complicated expression: -.. literalinclude:: /code/include/magnetic_texture.py - :start-after: #HIDDEN_BEGIN_lsdy - :end-before: #HIDDEN_END_lsdy +.. jupyter-execute:: + + # 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 book-keeping by providing a simple interface for defining operators that act on wavefunctions. To calculate the above quantities we would use the `~kwant.operator.Density` operator like so: -.. literalinclude:: /code/include/magnetic_texture.py - :start-after: #HIDDEN_BEGIN_lden - :end-before: #HIDDEN_END_lden +.. jupyter-execute:: + + 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 as well as (optionally) a square matrix that defines the quantity that you wish @@ -126,8 +266,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 `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 @@ -180,14 +326,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 spin current: -.. literalinclude:: /code/include/magnetic_texture.py - :start-after: #HIDDEN_BEGIN_current - :end-before: #HIDDEN_END_current +.. jupyter-execute:: + + 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 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:: @@ -262,9 +421,16 @@ scattering region. Doing this is as simple as passing a *function* when instantiating the `~kwant.operator.Current`, instead of a constant matrix: -.. literalinclude:: /code/include/magnetic_texture.py - :start-after: #HIDDEN_BEGIN_following - :end-before: #HIDDEN_END_following +.. jupyter-execute:: + + 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, and may optionally take other parameters (i.e. it must have the same @@ -274,7 +440,7 @@ matrix that defines the operator we wish to calculate. .. note:: In the above example we had to pass the extra parameters needed by the - ``following_operator`` function via the ``param`` keyword argument. In + ``following_operator`` function via the ``params`` keyword argument. In general you must pass all the parameters needed by the Hamiltonian via ``params`` (as you would when calling `~kwant.solvers.default.smatrix` or `~kwant.solvers.default.wave_function`). In the previous examples, @@ -286,7 +452,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 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`, you can do the same with `~kwant.operator.Density`. @@ -308,11 +480,16 @@ Density of states in a circle To calculate the density of states inside a circle of radius 20 we can simply do: -.. literalinclude:: /code/include/magnetic_texture.py - :start-after: #HIDDEN_BEGIN_density_cut - :end-before: #HIDDEN_END_density_cut +.. jupyter-execute:: + + 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 operator on a wavefunction will produce a single scalar. This is semantically @@ -325,11 +502,22 @@ Current flowing through a cut Below we calculate the probability current and z-projected spin current near the interfaces with the left and right leads. -.. literalinclude:: /code/include/magnetic_texture.py - :start-after: #HIDDEN_BEGIN_current_cut - :end-before: #HIDDEN_END_current_cut +.. jupyter-execute:: + + 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, but the z-projected spin current is not due to the fact that the Hamiltonian @@ -360,8 +548,23 @@ This can be achieved with `~kwant.operator.Current.bind`: *different* set of parameters. This will almost certainly give incorrect results. -.. literalinclude:: /code/include/magnetic_texture.py - :start-after: #HIDDEN_BEGIN_bind - :end-before: #HIDDEN_END_bind +.. jupyter-execute:: + + 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), + ]) diff --git a/doc/source/tutorial/plotting.rst b/doc/source/tutorial/plotting.rst index f8bb6586a1c2b7d6cd30cf084d983b5eef8b3ce5..ea04094a7381839540b5c5dfe82ea35258ff4767 100644 --- a/doc/source/tutorial/plotting.rst +++ b/doc/source/tutorial/plotting.rst @@ -12,15 +12,55 @@ these options can be used to achieve various very different objectives. .. seealso:: 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 + +.. jupyter-execute:: boilerplate.py + :hide-code: We begin by first considering a circular graphene quantum dot (similar to what has been used in parts of the tutorial :ref:`tutorial-graphene`.) In contrast to previous examples, we will also use hoppings beyond next-nearest neighbors: -.. literalinclude:: /code/include/plot_graphene.py - :start-after: #HIDDEN_BEGIN_makesyst - :end-before: #HIDDEN_END_makesyst +.. jupyter-execute:: + + 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 simply done by passing `n` as an argument to @@ -29,16 +69,14 @@ simply done by passing `n` as an argument to out of the shape. It is necessary to do so *before* adding the next-nearest-neighbor hopping [#]_. -Of course, the system can be plotted simply with default settings: - -.. literalinclude:: /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 +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 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 sublattices, and by having different line widths for different hoppings. This @@ -47,15 +85,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 the start end end site of hopping as arguments: -.. literalinclude:: /code/include/plot_graphene.py - :start-after: #HIDDEN_BEGIN_plotsyst2 - :end-before: #HIDDEN_END_plotsyst2 +.. jupyter-execute:: + + 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 instance of `~kwant.builder.Site`. With these adjustments we arrive at a plot -that carries the same information, but is much easier to interpret: - -.. image:: /code/figure/plot_graphene_syst2.* +that carries the same information, but is much easier to interpret. Apart from plotting the *system* itself, `~kwant.plotter.plot` can also be used to plot *data* living on the system. @@ -67,23 +109,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 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 -`~kwant.plotter.plot`, but rather `~kwant.plotter.map`. Here, we plot the -`n`-th wave function using it: +.. jupyter-execute:: + + 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 - :start-after: #HIDDEN_BEGIN_plotdata2 - :end-before: #HIDDEN_END_plotdata2 + wf = abs(evecs[:, 225])**2 +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 graphene edge state, i.e. a wave function mostly localized at the zigzag edges 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 deficiencies for this small system: For example, there are a few distortions at @@ -99,9 +145,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 completely: -.. literalinclude:: /code/include/plot_graphene.py - :start-after: #HIDDEN_BEGIN_plotdata3 - :end-before: #HIDDEN_END_plotdata3 +.. jupyter-execute:: + + 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 serve any purpose here). Moreover, ``site_size=0.5`` guarantees that the two @@ -114,31 +168,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 `~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 are best visible in a given plot. With `~kwant.plotter.plot` one can easily go beyond pseudocolor-like plots. For example, we can represent the wave function probability using the symbols itself: -.. literalinclude:: /code/include/plot_graphene.py - :start-after: #HIDDEN_BEGIN_plotdata4 - :end-before: #HIDDEN_END_plotdata4 +.. jupyter-execute:: + + 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, while the site color is transparent to also allow for overlapping symbols to be visible. The hoppings are also plotted in order to show the underlying lattice. -With this, we arrive at - -.. image:: /code/figure/plot_graphene_data3.* - -which shows the edge state nature of the wave function most clearly. - .. rubric:: Footnotes .. [#] A dangling site is defined as having only one hopping connecting it to @@ -150,7 +196,31 @@ which shows the edge state nature of the wave function most clearly. .. seealso:: 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 + +.. jupyter-execute:: boilerplate.py + :hide-code: Zincblende is a very common crystal structure of semiconductors. It is a face-centered cubic crystal with two inequivalent atoms in the unit cell @@ -159,18 +229,29 @@ structure, but two equivalent atoms per unit cell). It is very easily generated in Kwant with `kwant.lattice.general`: -.. literalinclude:: /code/include/plot_zincblende.py - :start-after: #HIDDEN_BEGIN_zincblende1 - :end-before: #HIDDEN_END_zincblende1 +.. jupyter-execute:: + + 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. A three-dimensional structure is created as easily as in two dimensions, by using the `~kwant.lattice.PolyatomicLattice.shape`-functionality: -.. literalinclude:: /code/include/plot_zincblende.py - :start-after: #HIDDEN_BEGIN_zincblende2 - :end-before: #HIDDEN_END_zincblende2 +.. jupyter-execute:: + + 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 values for onsite and hopping energies, but only the placeholder ``None`` (in a @@ -179,13 +260,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 counterparts: -.. literalinclude:: /code/include/plot_zincblende.py - :start-after: #HIDDEN_BEGIN_plot1 - :end-before: #HIDDEN_END_plot1 +.. jupyter-execute:: -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 3D than in 2D. For example, by default hoppings are not printed, but sites are @@ -207,14 +286,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 depending on the sublattice: -.. literalinclude:: /code/include/plot_zincblende.py - :start-after: #HIDDEN_BEGIN_plot2 - :end-before: #HIDDEN_END_plot2 +.. jupyter-execute:: -which results in a 3D plot that allows to interactively (when plotted -in a window) explore the crystal structure: + syst = make_cuboid(a=1.5, b=1.5, c=1.5) -.. 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 crystal lattices out there! diff --git a/doc/source/tutorial/spectrum.rst b/doc/source/tutorial/spectrum.rst index 734f5a1d0e161d2434789bc9ce3f3a880e122419..b2cbc51d3bef2b378891baca292857360ca6d95d 100644 --- a/doc/source/tutorial/spectrum.rst +++ b/doc/source/tutorial/spectrum.rst @@ -6,7 +6,32 @@ Band structure calculations .. seealso:: 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 + +.. jupyter-execute:: boilerplate.py + :hide-code: 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 @@ -19,9 +44,26 @@ tight-binding wire. Computing band structures in Kwant is easy. Just define a lead in the usual way: -.. literalinclude:: /code/include/band_structure.py - :start-after: #HIDDEN_BEGIN_zxip - :end-before: #HIDDEN_END_zxip +.. jupyter-execute:: + + 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 as one unit cell of the lead, and the hoppings to neighboring @@ -42,13 +84,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 bandstructure: -.. literalinclude:: /code/include/band_structure.py - :start-after: #HIDDEN_BEGIN_pejz - :end-before: #HIDDEN_END_pejz +.. jupyter-execute:: + + 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: -.. 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 to ``k=0`` this agrees well with the quadratic dispersion this tight-binding @@ -61,7 +114,34 @@ Closed systems .. seealso:: 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 + +.. jupyter-execute:: boilerplate.py + :hide-code: Although Kwant is (currently) mainly aimed towards transport problems, it can also easily be used to compute properties of closed systems -- after @@ -74,16 +154,49 @@ To compute the eigenenergies and eigenstates, we will make use of the sparse linear algebra functionality of `SciPy <https://www.scipy.org>`_, which interfaces the ARPACK package: -.. literalinclude:: /code/include/closed_system.py - :start-after: #HIDDEN_BEGIN_tibv - :end-before: #HIDDEN_END_tibv + +.. jupyter-execute:: + + # For eigenvalue computation + import scipy.sparse.linalg as sla We set up the system using the `shape`-function as in :ref:`tutorial-abring`, but do not add any leads: -.. literalinclude:: /code/include/closed_system.py - :start-after: #HIDDEN_BEGIN_qlyd - :end-before: #HIDDEN_END_qlyd +.. jupyter-execute:: + :hide-code: + + 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 did in the two previous tutorial. (Here, the gauge is chosen such that @@ -93,14 +206,40 @@ The spectrum can be obtained by diagonalizing the Hamiltonian of the system, which in turn can be obtained from the finalized system using `~kwant.system.System.hamiltonian_submatrix`: -.. literalinclude:: /code/include/closed_system.py - :start-after: #HIDDEN_BEGIN_yvri - :end-before: #HIDDEN_END_yvri +.. jupyter-execute:: + + 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 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 quantum dot is rather symmetric). These degeneracies are split @@ -110,11 +249,34 @@ Landau level energies at higher magnetic fields [#]_. The eigenvectors are obtained very similarly, and can be plotted directly using `~kwant.plotter.map`: -.. literalinclude:: /code/include/closed_system.py - :start-after: #HIDDEN_BEGIN_wave - :end-before: #HIDDEN_END_wave +.. jupyter-execute:: + :hide-code: + + 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 a colorbar from appearing. The second, ``oversampling=1``, makes the image look @@ -127,11 +289,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 it using `kwant.plotter.current`: -.. literalinclude:: /code/include/closed_system.py - :start-after: #HIDDEN_BEGIN_current - :end-before: #HIDDEN_END_current +.. jupyter-execute:: + + 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 diff --git a/doc/source/tutorial/spin_potential_shape.rst b/doc/source/tutorial/spin_potential_shape.rst index 02ac1fbad566ad8daa570f26677e86c3d83fd01d..770ba4a55f06f0c4a8bf0b89669cb66bb2fad407 100644 --- a/doc/source/tutorial/spin_potential_shape.rst +++ b/doc/source/tutorial/spin_potential_shape.rst @@ -11,7 +11,10 @@ Matrix structure of on-site and hopping elements .. seealso:: 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 coupling and a Zeeman splitting due to an external magnetic field: @@ -42,24 +45,78 @@ use matrices in our program, we import the Tinyarray package. (`NumPy <http://www.numpy.org/>`_ would work as well, but Tinyarray is much faster for small arrays.) -.. literalinclude:: /code/include/spin_orbit.py - :start-after: #HIDDEN_BEGIN_xumz - :end-before: #HIDDEN_END_xumz +.. jupyter-execute:: + :hide-code: + + # 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:: boilerplate.py + :hide-code: + +.. jupyter-execute:: + + # For matrix support + import tinyarray For convenience, we define the Pauli-matrices first (with :math:`\sigma_0` the unit matrix): -.. literalinclude:: /code/include/spin_orbit.py - :start-after: #HIDDEN_BEGIN_hwbt - :end-before: #HIDDEN_END_hwbt +.. jupyter-execute:: + + # 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]]) + +and we also define some other parameters useful for constructing our system: + +.. jupyter-execute:: + + t = 1.0 + alpha = 0.5 + e_z = 0.08 + W, L = 10, 30 Previously, we used numbers as the values of our matrix elements. However, `~kwant.builder.Builder` also accepts matrices as values, and we can simply write: -.. literalinclude:: /code/include/spin_orbit.py - :start-after: #HIDDEN_BEGIN_uxrm - :end-before: #HIDDEN_END_uxrm +.. jupyter-execute:: + :hide-code: + + lat = kwant.lattice.square() + syst = kwant.Builder() + +.. 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 spin-orbit term adds to the hoppings (due to the derivative operator). @@ -85,14 +142,49 @@ when specifying `(1, 0)` it is not necessary to specify `(-1, 0)`), The leads also allow for a matrix structure, -.. literalinclude:: /code/include/spin_orbit.py - :start-after: #HIDDEN_BEGIN_yliu - :end-before: #HIDDEN_END_yliu + +.. jupyter-execute:: + :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 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 @@ -129,7 +221,32 @@ Spatially dependent values through functions .. seealso:: 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 + +.. jupyter-execute:: boilerplate.py + :hide-code: Up to now, all examples had position-independent matrix-elements (and thus translational invariance along the wire, which @@ -148,22 +265,50 @@ changing the potential then implies the need to build up the system again. Instead, we use a python *function* to define the onsite energies. We define the potential profile of a quantum well as: -.. literalinclude:: /code/include/quantum_well.py - :start-after: #HIDDEN_BEGIN_ehso - :end-before: #HIDDEN_END_ehso +.. jupyter-execute:: + + W, L, L_well = 10, 30, 10 + + 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`, 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 -variables of the surrounding function: `L` and `L_well` are taken from the -namespace of `make_system`. +variables `L` and `L_well` that are defined globally. Kwant now allows us to pass a function as a value to `~kwant.builder.Builder`: -.. literalinclude:: /code/include/quantum_well.py - :start-after: #HIDDEN_BEGIN_coid - :end-before: #HIDDEN_END_coid +.. jupyter-execute:: + + a = 1 + t = 1.0 + + def onsite(site, pot): + return 4 * t + potential(site, pot) + + lat = kwant.lattice.square(a) + syst = kwant.Builder() + + 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 first argument to the function `onsite`. The values of any additional @@ -180,9 +325,21 @@ of the lead -- this should be kept in mind. Finally, we compute the transmission probability: -.. literalinclude:: /code/include/quantum_well.py - :start-after: #HIDDEN_BEGIN_sqvr - :end-before: #HIDDEN_END_sqvr +.. jupyter-execute:: + + 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 the additional arguments required by the Hamiltonian matrix elements. @@ -191,7 +348,11 @@ of the potential well by passing the potential value (remember above we defined our `onsite` function that takes a parameter named `pot`). 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 oscillatory transmission behavior through resonances in the quantum well. @@ -218,13 +379,44 @@ Nontrivial shapes .. seealso:: 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 + +.. jupyter-execute:: boilerplate.py + :hide-code: Up to now, we only dealt with simple wire geometries. Now we turn to the case of a more complex geometry, namely transport through a quantum ring that is pierced by a magnetic flux :math:`\Phi`: -.. image:: /code/figure/ab_ring_sketch.* +.. image:: /figure/ab_ring_sketch.* For a flux line, it is possible to choose a gauge such that a charged particle acquires a phase :math:`e\Phi/h` whenever it @@ -238,9 +430,14 @@ First, define a boolean function defining the desired shape, i.e. a function that returns ``True`` whenever a point is inside the shape, and ``False`` otherwise: -.. literalinclude:: /code/include/ab_ring.py - :start-after: #HIDDEN_BEGIN_eusz - :end-before: #HIDDEN_END_eusz +.. jupyter-execute:: + + r1, r2 = 10, 20 + + 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 `~kwant.builder.Site`). @@ -249,9 +446,16 @@ We can now simply add all of the lattice points inside this shape at once, using the function `~kwant.lattice.Square.shape` provided by the lattice: -.. literalinclude:: /code/include/ab_ring.py - :start-after: #HIDDEN_BEGIN_lcak - :end-before: #HIDDEN_END_lcak +.. jupyter-execute:: + + a = 1 + t = 1.0 + + lat = kwant.lattice.square(a) + syst = kwant.Builder() + + 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 inside the desired shape. The hoppings can still be added using @@ -264,9 +468,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))` with ``j<0``: -.. literalinclude:: /code/include/ab_ring.py - :start-after: #HIDDEN_BEGIN_lvkt - :end-before: #HIDDEN_END_lvkt +.. jupyter-execute:: + + # 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 the desired hoppings. We then use again a generator (this time with @@ -279,9 +504,21 @@ by the parameter `phi`. For the leads, we can also use the ``lat.shape``-functionality: -.. literalinclude:: /code/include/ab_ring.py - :start-after: #HIDDEN_BEGIN_qwgr - :end-before: #HIDDEN_END_qwgr +.. jupyter-execute:: + + #### Define the leads. #### + W = 10 + + 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 of the lead ``sym_lead``. In particular, this means that it should extend to @@ -290,9 +527,12 @@ no restriction on ``x`` in ``lead_shape``) [#]_. Attaching the leads is done as before: -.. literalinclude:: /code/include/ab_ring.py - :start-after: #HIDDEN_BEGIN_skbz - :end-before: #HIDDEN_END_skbz +.. jupyter-execute:: + :hide-output: + + #### 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 structure with a scattering region very much different from the lead @@ -305,16 +545,40 @@ back (going opposite to the direction of the translational vector) until it intersects the scattering region. At this intersection, the lead is attached: -.. image:: /code/figure/ab_ring_sketch2.* +.. image:: /figure/ab_ring_sketch2.* 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. 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 period of one flux quantum. @@ -330,7 +594,47 @@ period of one flux quantum. 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: - .. image:: /code/figure/ab_ring_note1.* + .. jupyter-kernel:: + :id: ab_ring_note1 + + .. jupyter-execute:: + :hide-code: + + import kwant + from matplotlib import pyplot + + .. jupyter-execute:: boilerplate.py + :hide-code: + + .. jupyter-execute:: + :hide-code: + + 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 the lead to the "outside" of the structure, by tracing the @@ -345,7 +649,46 @@ period of one flux quantum. starts the trace-back in the middle of the ring, resulting 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 + + .. jupyter-execute:: boilerplate.py + :hide-code: + + .. jupyter-execute:: + :hide-code: + + 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 the other arm of the ring, without intersecting it. diff --git a/doc/source/tutorial/superconductors.rst b/doc/source/tutorial/superconductors.rst index 6113cb78a7be06bf57b7bf7f39b65095505b1f29..78f004d34820c3c4ef6219a855681ab542f0aa7a 100644 --- a/doc/source/tutorial/superconductors.rst +++ b/doc/source/tutorial/superconductors.rst @@ -3,7 +3,40 @@ Superconductors: orbital degrees of freedom, conservation laws and symmetries .. seealso:: 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]]) + +.. jupyter-execute:: boilerplate.py + :hide-code: This example deals with superconductivity on the level of the Bogoliubov-de Gennes (BdG) equation. In this framework, the Hamiltonian @@ -51,16 +84,48 @@ of freedom. Let us consider a system that consists of a normal lead on the left, a superconductor on the right, and a tunnel barrier in between: -.. image:: /code/figure/superconductor_transport_sketch.* +.. image:: /figure/superconductor_transport_sketch.* We implement the BdG Hamiltonian in Kwant using a 2x2 matrix structure for all Hamiltonian matrix elements, as we did -previously in the :ref:`spin example <tutorial_spinorbit>`. We declare -the square lattice and construct the scattering region with the following: +previously in the :ref:`spin example <tutorial_spinorbit>`. +We start by declaring some parameters that will be used in the following code: + +.. jupyter-execute:: + + a = 1 + W, L = 10, 10 + barrier = 1.5 + barrierpos = (3, 4) + mu = 0.4 + Delta = 0.1 + Deltapos = 4 + t = 1.0 + +and we declare the square lattice and construct the scattering region with the following: + +.. jupyter-execute:: -.. literalinclude:: /code/include/superconductor.py - :start-after: #HIDDEN_BEGIN_nbvn - :end-before: #HIDDEN_END_nbvn + # 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 the number of orbitals per site in the discretized BdG Hamiltonian - of course, @@ -80,9 +145,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 one to the right end. Starting with the left lead, we have: -.. literalinclude:: /code/include/superconductor.py - :start-after: #HIDDEN_BEGIN_ttth - :end-before: #HIDDEN_END_ttth +.. jupyter-execute:: + + #### 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`` and ``particle_hole``. For the purpose of computing conductance, ``conservation_law`` @@ -120,9 +192,19 @@ of ascending eigenvalues of the conservation law. In order to move on with the conductance calculation, let's attach the second lead to the right side of the scattering region: -.. literalinclude:: /code/include/superconductor.py - :start-after: #HIDDEN_BEGIN_zuuw - :end-before: #HIDDEN_END_zuuw +.. jupyter-execute:: + + # 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 blocks are coupled. Of course, this means that we can not separate them into @@ -140,9 +222,23 @@ confused by the fact that it says ``transmission`` -- transmission into the same lead is reflection), and reflection from electrons to holes is ``smatrix.transmission((0, 1), (0, 0))``: -.. literalinclude:: /code/include/superconductor.py - :start-after: #HIDDEN_BEGIN_jbjt - :end-before: #HIDDEN_END_jbjt +.. jupyter-execute:: + + 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 reflection of electrons to electrons, and from its size we can extract the number of modes @@ -150,7 +246,10 @@ reflection of electrons to electrons, and from its size we can extract the numbe 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 probability within the gap, and proportional to the tunneling probability @@ -176,13 +275,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 in a symmetric scattering matrix. We can check the symmetry explicitly with -.. literalinclude:: /code/include/superconductor.py - :start-after: #HIDDEN_BEGIN_pqmp - :end-before: #HIDDEN_END_pqmp +.. jupyter-execute:: + + 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 -.. 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 we consider here, there are two electron and two hole modes active at zero energy.