@@ -1,341 +1,367 @@
 # 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, rng=0)
 #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, rng=0)
 
 #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, rng=0)
     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, rng=0)
 #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, rng=0)
 #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, rng=0)
 #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,
                                           rng=0)
     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, rng=0)
 #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(),
                                                rng=0)
 #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, rng=0)
     rho_alt_spectrum = kwant.kpm.SpectralDensity(fsyst, operator=rho_alt, rng=0)
 #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,
                                      rng=0)
     # 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,
                                      rng=0)
 
     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,
                                          rng=0)
 
     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()