# k-space paths

## Default paths

To generate a k-path for, say, the space group of diamond (space group 227; a cubic face-centered Bravais lattice), we can call irrfbz_path, which will return a minimal path in the irreducible Brillouin zone:

using Brillouin
sgnum = 227
Rs = [[1,0,0], [0,1,0], [0,0,1]] # conventional direct basis
kp = irrfbz_path(sgnum, Rs)
KPath{3} (6 points, 2 paths, 8 points in paths):
points: :U => [0.625, 0.25, 0.625]
:W => [0.5, 0.25, 0.75]
:K => [0.375, 0.375, 0.75]
:Γ => [0.0, 0.0, 0.0]
:L => [0.5, 0.5, 0.5]
:X => [0.5, 0.0, 0.5]
paths: [:Γ, :X, :U]
[:K, :Γ, :L, :W, :X]
basis: [-6.283185, 6.283185, 6.283185]
[6.283185, -6.283185, 6.283185]
[6.283185, 6.283185, -6.283185]

The path data is sourced from the HPKOT paper (or, equivalently, the SeeK-path Python package).

The coordinates of the path are given with respect to the primitive reciprocal basis (here, [[-2π,2π,2π], [2π,-2π,2π], [2π,2π,-2π]]). To convert to a Cartesian basis, we can use cartesianize or cartesianize! (in-place):

cartesianize(kp)
KPath{3} (6 points, 2 paths, 8 points in paths):
points: :U => [1.570796, 6.283185, 1.570796]
:W => [3.141593, 6.283185, 0.0]
:K => [4.712389, 4.712389, 0.0]
:Γ => [0.0, 0.0, 0.0]
:L => [3.141593, 3.141593, 3.141593]
:X => [0.0, 6.283185, 0.0]
paths: [:Γ, :X, :U]
[:K, :Γ, :L, :W, :X]
basis: [-6.283185, 6.283185, 6.283185]
[6.283185, -6.283185, 6.283185]
[6.283185, 6.283185, -6.283185]

We can visualize the k-path using PlotlyJS.jl (conversion to a Cartesian basis for plotting is automatic):

using PlotlyJS
Pᵏ = plot(kp)

Usually, it'll be more helpful to understand the path's geometry in the context of the associated Brillouin zone. To visualize this, we can plot the combination of a Cell (created via wignerseitz) and a KPath:

pGs = basis(kp)      # primitive reciprocal basis associated with k-path
c = wignerseitz(pGs) # associated Brillouin zone
Pᶜ⁺ᵏ = plot(c, kp)

## Interpolation

Interpolation of a KPath structure can be achieved using either interpolate(::KPath, ::Integer) or splice(::KPath, ::Integer), returning a KPathInterpolant. As an example, interpolate(kp, N) returns an interpolation with a target of N interpolation points, distributed as equidistantly as possible (with the distance metric evaluated in Cartesian space):

kpi = interpolate(kp, 100)
99-element KPathInterpolant{3}:
[0.0, 0.0, 0.0]
[0.022727272727272728, 0.0, 0.022727272727272728]
[0.045454545454545456, 0.0, 0.045454545454545456]
[0.06818181818181818, 0.0, 0.06818181818181818]
[0.09090909090909091, 0.0, 0.09090909090909091]
[0.11363636363636363, 0.0, 0.11363636363636363]
[0.13636363636363635, 0.0, 0.13636363636363635]
[0.1590909090909091, 0.0, 0.1590909090909091]
[0.18181818181818182, 0.0, 0.18181818181818182]
[0.20454545454545453, 0.0, 0.20454545454545453]
⋮
[0.5, 0.18181818181818182, 0.6818181818181817]
[0.5, 0.1590909090909091, 0.6590909090909091]
[0.5, 0.13636363636363635, 0.6363636363636364]
[0.5, 0.11363636363636365, 0.6136363636363636]
[0.5, 0.09090909090909093, 0.5909090909090908]
[0.5, 0.06818181818181818, 0.5681818181818181]
[0.5, 0.045454545454545456, 0.5454545454545454]
[0.5, 0.022727272727272735, 0.5227272727272727]
[0.5, 0.0, 0.5]

The returned KPathInterpolant implements the AbstractVector interface, with iterants returning SVector{D, Float64} elements. To get a conventional "flat" vector, we can simply call collect(kpi).

Internally, KPathInterpolant includes additional structure and information: namely, the high-symmetry points and associated labels along the path as well as a partitioning into connected vs. disconnected path segments.

## Band structure

The additional structure of KPathInterpolation enables convenient and clear visualizations of band structure diagrams in combination with PlotlyJS.

To illustrate this, suppose we are considering a tight-binding problem for an s-orbital situated at the 1a Wyckoff position. Such a problem has a single band with dispersion  (assuming a cubic side length $a = 1$):

$$$\epsilon(\mathbf{k}) = 4\gamma\Bigl( \cos \tfrac{1}{2}k_x \cos \tfrac{1}{2}k_y + \cos \tfrac{1}{2}k_y \cos \tfrac{1}{2}k_z + \cos \tfrac{1}{2}k_z \cos \tfrac{1}{2}k_x \Bigr)$$$

with $k_{x,y,z}$ denoting coordinates in a Cartesian basis (which are related to the coordinates $k_{1,2,3}$ in a primitive reciprocal basis by $k_x = 2π(-k_1+k_2+k_3)$, $k_x = 2π(k_1-k_2+k_3)$, and $k_z = 2π(k_1+k_2-k_3)$).

We can calculate the energy band along our k-path using the interpolation object kpi. To do so, we define a function that implements $\epsilon(\mathbf{k})$ and broadcast it over the elements of kpi:

function ϵ(k; γ::Real=1.0)
kx = 2π*(-k+k+k)
ky = 2π*(+k-k+k)
kz = 2π*(+k+k-k)
return 4γ * (cos(kx/2)*cos(ky/2) + cos(ky/2)*cos(kz/2) + cos(kz/2)*cos(kx/2))
end
band = ϵ.(kpi)
99-element Vector{Float64}:
12.0
11.918571535047462
11.67594378891598
11.277055962836148
10.73002826264945
10.045996594834067
9.238885871562282
8.325126539644781
7.3233201040150915
6.253860454731438
⋮
-4.0
-3.9999999999999996
-4.0
-3.9999999999999996
-4.0
-4.0
-4.0
-4.0
-4.0

Finally, we can visualize the associated band using a Brillouin-overloaded PlotlyJS plot-call:

P = plot(kpi, [band])

If we have multiple bands, say $\epsilon_1(\mathbf{k}) = \epsilon(\mathbf{k})$ and $\epsilon_2(\mathbf{k}) = 20 - \tfrac{1}{2}\epsilon(\mathbf{k})$, we can easily plot that by collecting the bands in a single vector (or concatenating into a matrix):

band1 = ϵ.(kpi)
band2 = 20 .- (1/2).*band1
P¹² = plot(kpi, [band1, band2])
PlotlyJS.plotMethod
plot(kpi::KPathInterpolant, bands, [layout]; kwargs...)

Plot a dispersion diagram for provided bands and k-path interpolant kpi.

bands must be an iterable of iterables of <:Reals (e.g., a Vector{Vector{Float64}}), with the first iteration running over distinct energy bands, and the second running over distinct k-points in kpi. Note that the length of each iterant of bands must equal length(kpi).

Alternatively, bands can be an AbstractMatrix{<:Real}, with columns interpreted as distinct energy bands and rows as distinct k-points.

A layout can be supplied to overwrite default layout choices (set by Brillouin.DEFAULT_PLOTLY_LAYOUT_DISPERSION). Alternatively, some simple settings can be set directly via keyword arguments (see below).

Keyword arguments kwargs

• ylims: y-axis limits (default: quasi-tight around bands's range)

• ylabel: y-axis label (default: "Energy")

• title: plot title (default: nothing); can be a String or an attr dictionary of PlotlyJS properties

• band_highlights: dictionary of non-default styling for specified band ranges (default: nothing, indicating all-default styling).

Example: To color bands 2 and 3 black, set band_highlights = Dict(2:3 => attr(color=:black, width=3). Unlisted band ranges are shown in default band styling.

• annotations: dictionary of hover-text annotations for labeled high-symmetry points in kpi (default: nothing, indicating no annotations). Suitable for labeling of irreps.

Example: Assume bands 1 and 2 touch at X, but not at Γ. To label this, we set: annotations = Dict(:X => [1:2 => "touching!], :Γ => [1 => "isolated", 2 => "isolated"]). If a band-range is provided, a single annotation is placed at the mean of the energies at these band-ranges. Alternatively, if the first element of each pair is a non-Integer Real number, it is interpreted as referring to the frequency of the annotation.

source