Currents

This chapter covers the calculation of currents in the system.

Basics

An AbstractCurrents object is a collection of currents of some nature (e.g. charge, spin, etc.) from one site to another. In most cases it is a lazy object that doesn't store the currents themselves, but rather the parameters to evaluate them on the go.

A good example of this is DensityCurrents, which calculates the charge currents between sites.

using LatticeModels, Plots
l = SquareLattice(10, 10)
H = tightbinding_hamiltonian(l)
P = densitymatrix(H, statistics=FermiDirac, mu=0)
H1 = tightbinding_hamiltonian(l, field=PointFlux(0.1, (5.5, 5.5)))
C = DensityCurrents(H1, P)
plot(C)
Example block output

Here we find the density matrix P of the system, add a magnetic field to the Hamiltonian to induce currents (H1), and calculate the charge currents between sites (C). The resulting plot shows the currents between sites.

Currents can be indexed with sites to get the currents between them:

julia> site1 = l[!, x=1, y=1]; site2 = l[!, x=1, y=2]; site3 = l[!, x=1, y=3];
julia> C[site1, site2]-0.007078986147809262
julia> C[site1, site3] # Current between site1 and site3 is zero0.0

You can evaluate all currents at once by converting the currents object to Currents. This can be useful when you want to perform non-trivial operations on the currents:

julia> C1 = Currents(C)Currents{SparseArrays.SparseMatrixCSC{Float64, Int64}} on 100-site SquareLattice in 2D space
julia> C1[site1, site2] == C[site1, site2]true

Also note that the Currents object is writable:

site4 = l[!, x=2, y=2]
C1[site1, site4] = 0.05
C1[l[!, x=10, y=10], l[!, x=9, y=9]] = 0.05
plot(C1)
Example block output

Common Operations

One function you may find useful is currentsfromto, which calculates currents between two sites or other domains. As an example, let's find how the currents through a circular sample depends on time:

using LatticeModels, Plots
l = SquareLattice{2}(Circle(10), !Circle(5))
h(t) = tightbinding_hamiltonian(l, field=PointFlux(0.1 * t, (0, 0)))
P = densitymatrix(h(0), statistics=FermiDirac, mu=0)
τ = 10
ev = Evolution(t -> h(min(t, τ)), P)
dom1 = l[x = 0 .. Inf, y = 0]
dom2 = l[x = 0 .. Inf, y = 1]
currs = TimeSequence(ev, 0:0.1:2τ) do (Pt, Ht, t)
    Currents(DensityCurrents(Ht, Pt))    # Convert to Currents, since P is reused
end
currs_from_to = map(curr -> currentsfromto(curr, dom1, dom2), currs)
plot(currs_from_to, title="Currents through the circle")
Example block output

Here we perform a time evolution of the system with a magnetic field that is slowly increasing from 0 to 0.1 * τ, after which it remains constant. See the Evolution chapter for more details about time evolution.

The value we evaluated is the currents between two domains dom1 and dom2 at each time step. Here are the domains:

C = currs[τ]
plot(C)
plot!(dom1, label="Domain 1")
plot!(dom2, label="Domain 2")
Example block output

Another useful function is currentsfrom, which calculates currents from a given domain (site or part of the lattice) to the rest of the lattice and returns a LatticeValue object.

Let us continue with the previous example. This will calculate the currents from site1 in the bottom-left corner to the rest of the lattice:

site = l[!, x = -7, y = 2]
heatmap(currentsfrom(C, site), c=:balance, clims=(-0.06, 0.06))
plot!(site, label="", title="Currents from site $(site.coords)")
Example block output

Also note that currents are iterable objects, yielding a tuple of source => target pair and the current value. The order of the sites is such that the current is directed from the source to the target and is always positive. This can be useful to analyze, for example, the direction of the currents:

cs = zeros(4)
ns = zeros(Int, 4)
for ((src, tgt), curr) in C
    @assert curr >= 0
    curr < 1e-6 && continue
    x, y = normalize(tgt.coords - src.coords)
    angle = atan(y, x)
    idx = mod(round(Int, angle / (π / 2)), 4) + 1
    cs[idx] += curr
    ns[idx] += 1
end
bar(["0", "π/2", "π", "3π/2"], cs ./ ns, label="Average current by direction")
Example block output

Visualization

The currents can be visualized using the plot function. The values of the currents will be shown as arrows between the sites. The color of the arrows represents the magnitude of the currents. Also note that the opacity of the arrows is also proportional to the magnitude of the currents, which can be disabled by setting arrowtransparency=false. You can customize the appearance of the arrows using the arrowheadsize and arrowheadwidth keywords.

Here we will continue with the C currents obtained in the previous example:

p = plot(layout = @layout[a b; _ c{0.5w} _], size=(1000, 1000))
plot!(p[1], C, title="Default")
plot!(p[2], C, arrowtransparency=false, title="Opaque arrows")
plot!(p[3], C, arrowheadsize=0.3, arrowheadwidth=1, arrowtransparency=false,
    title="Custom arrowhead")
Example block output

You can plot an AbstractCurrents and a LatticeValue object in one axes. However, due to Plots.jl limitations, both series must share the same colorbar. You can bypass this by plotting them on separate axes, or by plotting the currents with a single color. In this case you need to set line_z=nothing to disable the colorbar for the arrows. Here is an example of plotting the currents and the lattice together:

plot(localdensity(P), st=:shape)
plot!(C, c=:pink2, arrowheadsize=0.3, arrowheadwidth=0.6, line_z=nothing)
Example block output
Tip

To make the plot more readable and even almost artistic, keep in mind that the arrows should be of similar hue, but different brightness compared to the background. This can be achieved by setting the arrows color to one that fits the background color palette.