From a2f57670e2bcf75ed25875c54a0738d94666edd9 Mon Sep 17 00:00:00 2001
From: Joseph Weston <joseph@weston.cloud>
Date: Fri, 26 Apr 2019 11:42:02 +0200
Subject: [PATCH] convert closed system example to jupyter-sphinx

---
 doc/source/tutorial/spectrum.rst | 155 +++++++++++++++++++++++++++----
 1 file changed, 136 insertions(+), 19 deletions(-)

diff --git a/doc/source/tutorial/spectrum.rst b/doc/source/tutorial/spectrum.rst
index acd99d7f..1eeef135 100644
--- a/doc/source/tutorial/spectrum.rst
+++ b/doc/source/tutorial/spectrum.rst
@@ -111,7 +111,31 @@ 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
 
 Although Kwant is (currently) mainly aimed towards transport problems, it
 can also easily be used to compute properties of closed systems -- after
@@ -124,16 +148,49 @@ To compute the eigenenergies and eigenstates, we will make use of the sparse
 linear algebra functionality of `SciPy <https://www.scipy.org>`_, which
 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
@@ -143,14 +200,40 @@ The spectrum can be obtained by diagonalizing the Hamiltonian of the
 system, which in turn can be obtained from the finalized
 system 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
@@ -160,11 +243,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)
+
+.. jupyter-execute::
+    :hide-code:
 
-.. image:: /code/figure/closed_system_eigenvector.*
+    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
@@ -177,11 +283,22 @@ that they can have *non-zero* local current. We can calculate the local
 current due to a state by using `kwant.operator.Current` and plotting
 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
 
-- 
GitLab