What's new in Kwant 1.5
=======================

This article explains the user-visible changes in Kwant 1.5.0.


Kwant supports vectorized value functions
-----------------------------------------
It is now possible to define value functions that act on whole
arrays of sites at a time.
::

    def onsite(sites):
        r = sites.positions()
        return np.exp(-np.linalg.norm(r, axis=1)**2)

    def hopping(to_sites, from_sites):
        dr = to_sites.positions() - from_sites.positions()
        return 1 / np.linalg.norm(dr, axis=1)**2


    lat = kwant.lattice.square(norbs=1)
    syst = kwant.Builder(vectorize=True)
    syst[(lat(i, j) for i in range(4) for j in range(10)] = onsite
    syst[lat.neighbors()] = hopping
    syst = syst.finalized()

    syst.hamiltonian_submatrix()

Notice that when creating the Builder we provide the ``vectorize=True`` flag,
and that the value functions now take `~kwant.system.SiteArray` objects
(which have a ``positions`` method, rather than a ``pos`` property as for
`~kwant.system.Site`). We can now operate on the array of site positions
using ``numpy``. Using vectorized value functions in this way can produce an
order of magnitude speedup when evaluating system Hamiltonians (though this
speedup may be masked by other parts of your computation e.g. calculating
the scattering matrix).

Similarly, the ``onsite`` passed to an operator may now be vectorized in the same
way, as long as the system passed to the operator is also vectorized:
::

    def x_onsite(sites):
        x, _ = sites.positions().transpose()
        return x

    x_position = kwant.operator.Density(syst, x_onsite)

    psi = kwant.wave_function(syst, energy=0.1)(0)[0]
    x_position(psi)


Deprecation of leaving 'norbs' unset for site families
------------------------------------------------------
When constructing site families (e.g. lattices) it is now deprecated to
leave the 'norbs' parameter unset. This will now raise a
KwantDeprecationWarning and will be disallowed in a future version of
Kwant. For example, when constructing a square lattice with 1 orbital
per site, use::

    kwant.lattice.square(norbs=1)

rather than::

    kwant.lattice.square()


Automatic addition of Peierls phase terms to Builders
-----------------------------------------------------
Kwant 1.4 introduced `kwant.physics.magnetic_gauge` for computing Peierls
phases for arbitrary geometries and for systems with leads. Using this
functionality requires that the system value functions are equipped to
take the required Peierls phase parameters, which is not possible when
you are not in direct control of the value functions (e.g. discretized
systems). In Kwant 1.5 we have added the missing piece of functionality,
`kwant.builder.add_peierls_phase`, which properly adds the Peierls phase
parameters to a Builder's value functions::

    syst = make_system()
    lead = make_lead()
    syst.attach_lead(lead)
    syst.attach_lead(lead.reversed())

    fsyst, phase = kwant.builder.add_peierls_phase(syst)

    def B_syst(pos):
        return np.exp(-np.sum(pos * pos))

    kwant.smatrix(fsyst, parameters=dict(t=-1, **phase(B_syst, 0, 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.

Discretization onto a Landau level basis
----------------------------------------
The Hamiltonian for a system infinite in at least two dimensions and with
a constant applied magnetic field may be expressed in a basis of Landau levels.
The momenta in the plane perpendicular to the magnetic field direction are
written in terms of the Landau level ladder operators:

.. math::
    k_x = \sqrt{\frac{B}{2}} (a + a^\dagger) \quad\quad
    k_y = i \sqrt{\frac{B}{2}} (a - a^\dagger)

The Hamiltonian is then expressed in terms of these ladder operators, which
allows for a straight-forward discretization in the basis of Landau levels,
provided that the basis is truncated after $N$ levels.
`kwant.continuum.discretize_landau` makes this procedure simple::

    syst = kwant.continuum.discretize_landau("k_x**2 + k_y**2", N)
    syst.finalized().hamiltonian_submatrix(params=dict(B=0.5))

`~kwant.continuum.discretize_landau` produces a regular Kwant Builder that
can be inspected or finalized as usual. 3D Hamiltonians for systems that
extend into the direction perpendicular to the magnetic field are also
possible::

    template = kwant.continuum.discretize_landau("k_x**2 + k_y**2 + k_z**2 + V(z)", N)

This will produce a Builder with a single translational symmetry, which can be
directly finalized, or can be used as a template for e.g. a heterostructure stack
in the direction of the magnetic field::

    def stack(site):
        z, = site.pos
        return 0 <= z < 10

    template = kwant.continuum.discretize_landau("k_x**2 + k_y**2 + k_z**2 + V(z)", N)
    syst = kwant.Builder()
    syst.fill(template, stack, (0,))


Minimum required versions for some dependencies have increased
--------------------------------------------------------------
Kwant now requires at least the following versions:

+ Python 3.6
+ numpy 0.13.3
+ scipy 0.19.1

The kwant extensions (plotting, continuum and qsymm) now require at
least the following versions:

+ matplotlib 2.1.1
+ sympy 1.1.1
+ qsymm 1.2.6

These versions (or newer) are available in the latest stable releases
of Ubuntu and Debian GNU/Linux, with the exception of qsymm, which is
available on PyPI or Conda forge.