Plasmons.jl
Installing
Plasmons.jl can be used either as a Julia package or as a standalone executable. The latter option is provided for people who do not care about Julia and just want to do physics. If you are one of them just download Plasmons.sif
executable file from the Releases page and you are good to go.
Otherwise, install Plasmons.jl using Pkg:
import Pkg; Pkg.add(url="https://github.com/twesterhout/Plasmons.jl")
Using the executable
After downloading the container (Plasmons.sif
file) from the Releases page one can simply run it. It follows the UNIX philosophy and tries to do one thing and do it well.
The one thing is calculating polarizability $\chi$ (or dielectric function $\varepsilon$). It can be described by the following two functions:
This means that if you provide a Hamiltonian $H$ and frequency $\omega$ (and some information about the environment, namely chemical potential $\mu$ and temperature $T$), Plasmons.sif
will compute $\chi(\omega)$ for your system. If you additionally provide unscreened Coulomb interaction $V$, Plasmons.sif
will compute $varepsilon(\omega)$ as well.
Algorithm
Within real-space RPA (Random Phase Approximation) the calculation of polarizability matrix $\chi(\omega)$ amounts to evaluation of the following equation
Here $E_i$s are energy eigenvalues, $\langle a | i \rangle = \psi_i (a)$ are energy eigenstates, $f_i = f(E_i)$ is the occupation of the $i$'th state given by the Fermi-Dirac distribution, $\eta$ is Landau damping, and $\omega$ is the frequency.
First of all, for a given temperature $T$ and chemical potential $\mu$, we compute occupation numbers $f(E_i)$.
Plasmons.fermidirac
— Functionfermidirac(E; mu, kT) -> f
Return Fermi-Dirac distribution $f$ at energy E
, chemical potential mu
, and temperature kT
. Note that kT
is assumed to be temperature multiplied by the Boltzmann constant, i.e. physical dimension of kT
is the same as E
(e.g. electron-volts).
Next, we compute the matrix $G$. This is done by either Plasmons._g
or Plasmons._g_blocks
functions.
Plasmons._g
— Function_g(ħω, E; mu, kT) -> G
This is an internal function!
Compute matrix G
where $f$ is Fermi-Dirac distribution at chemical potential mu
($\mu$) and temperature kT
($k_B T$). E
is a vector of eigenenergies. ħω
is a complex frequency including Landau damping (i.e. $\hbar\omega + i\eta$). All arguments are assumed to be in energy units.
Sometimes one can further exploit the structure of $G$. For $E \ll \mu$ or $E \gg \mu$ Fermi-Dirac distribution is just a constant and $G$ goes to 0 for all $\omega$. Plasmons._g_blocks
uses this fact to construct a block-sparse version of $G$. The reason why such a block-sparse version is useful will become apparent later.
Plasmons._g_blocks
— Function_g_blocks(ħω, E; mu, kT) -> (Gᵣ, Gᵢ)
This is an internal function!
Calculate matrix $G$ given a vector of eigenenergies E
, chemical potential mu
, and temperature kT
. See _g
for the definition of $G$.
Compared to _g
this function applies to tricks:
G
is split into real and imaginary partsGᵣ
andGᵢ
.- We exploit the "block-sparse" structure of
G
(seePlasmons._ThreeBlockMatrix
).
Let us now turn to the computation of polarizability matrix $\chi$. The naive approach is to write 4 nested loops. However, this is tremendously slow! A slightly better approach which I used for my Bachelor thesis is to rewrite the computation of each $\chi_{a, b}$ as a combination of GEMV
and DOT
operations:
We use the following data structure to hold $A$ as well as the temporary $G A$.
Plasmons._Workspace
— Type_Workspace{<: AbstractArray}
This is an internal data structure!
A workspace which is used by polarizability
function to avoid allocating many temporary arrays. Stores two attributes:
- A vector
A
which is defined by $A_j = \langle j | a \rangle \langle b | j \rangle$. - A vector
temp
which is the product $G A$.
TODO: Finish this.