This is a modern Fortran library sister to WannInt. This library is meant to provide a backend for automation of sampling and integration workflows of functions defined on the Brillouin zone (BZ) of a crystal.
Many quantities in solid state physics (Berry curvature, optical responses, transport properties...) are expressed as a function, or as the integral of a function, defined in reciprocal space. The most general form of such function is
where
To abstract the idea of integer and continuous indices, MAC is used.
The derived type
type, public :: task_specifier
is defined.
A task is created by calling
call tsk%construct(name, [&
int_ind, &
cont_data_start, &
cont_data_end, &
cont_data_steps, &
exponent, ]&
calculator)
where
-
character(len=*), intent(in) :: name
is the name of the task. -
integer, optional, intent(in) :: int_ind(:)
is the dimension specifier of the integer indices$\alpha$ in$C^{\alpha}(\textbf{k}; \beta)$ . That is, the size of the array is the number of integer indices, and the value of each element is the number of values that index can take. Ifint_ind
is not present,$C^{\alpha}(\textbf{k}; \beta)$ does not depend on$\alpha$ . -
integer, optional, intent(in) :: cont_data_steps(:)
is the dimension specifier of the continuous indices$\beta$ in$C^{\alpha}(\textbf{k}; \beta)$ . That is, the size of the array is the number of continuous variables, and the value of each element is the number of steps that the variable has been discretized into. Ifcont_data_steps
is not present,$C^{\alpha}(\textbf{k}; \beta)$ does not depend on$\beta$ . -
real(dp), optional, intent(in) :: cont_data_start(:)
is a real array where each array element specifies the starting sampling point for each of the continuous variables. -
real(dp), optional, intent(in) :: cont_data_end(:)
is a real array where each array element specifies the ending sampling point for each of the continuous variables. -
real(dp), optional, intent(in) :: exponent
is a real number$e$ , which if not present, will sample the variable$\beta_i$ according to$\beta_i(j)=$ cont_data_start(i) + (cont_data_end(i) - cont_data_start(i))
$\times (j-1)/$ (cont_data_steps(i) - 1)
, and if present as$\beta_i(j) \rightarrow e^{\beta_i(j)}$ . Ifcont_data_steps(i) = 1
, then$\beta_i(j)=$ cont_data_start(i)
. -
procedure(cs), pass(self) :: calculator
is a function with interfacecs
which corresponds the implementation of$C^{\alpha}(\textbf{k}; \beta)$ . See interface.
Is a MAC container specifier initialized to the dimension specifier given by int_ind
.
Is a MAC container specifier initialized to the dimension specifier given by cont_data_steps
.
Is called as,
name = tsk%name()
where character(len=120) :: name
.
Is called as,
initialized = tsk%initialized()
where logical :: initialized
is .true.
if the task has been initialized and .false.
otherwise.
This utility serves to retrieve the value of
beta = tsk%cdt(var, step)
where real(dp) :: beta
has the value var=
step=
The interface
use MAC, only: container_specifier, container
use WannInt, only: crystal
abstract interface
function cs(self, crys, k, other)
import dp, task_specifier, crystal
class(task_specifier), intent(in) :: self
class(crystal), intent(in) :: crys
real(dp), intent(in) :: k(3)
class(*), optional, intent(in) :: other
complex(dp) :: cs(self%idims%size(), self%cdims%size())
end function cs
end interface
describes the shape of any calculator. The input values for these types of functions are always an object of class task_specifier
, a WannInt crystal class object crys
, a real(dp), dimension(3)
variable k
representing a vector in the BZ of the crystal and an optional unlimited polymorphic object other
. The output value is a complex(dp)
rank-2 array which holds, in each dimension, the memory layout index corresponding to a particular permutation of the integer and continuous variables, respectively.
- If you need to pass further external information to the calculator (a value for a Dirac delta function smearing for integration tasks, an exception, or an error case...), it is best to extend either
task_specifier
orcrystal
, depending on what information needs to be passed. - It is a clever idea to traverse the whole output array with the aid of MAC's indexing when defining calculators.
do r = 1, tsk%cdims%size()
r_arr = tsk%cdims%ind(r)
cv1 = tsk%cdt(var = 1, step = r_arr(1)) !external variable 1,
cv2 = tsk%cdt(var = 2, step = r_arr(2)) !external variable 2,
![...]
do i = 1, tsk%idims%size()
i_arr = tsk%idims%ind(i)
iv1 = i_arr(1) !integer variable 1,
iv2 = i_arr(2) !integer variable 2,
cs(i, r) = ... !your implementation in terms of iv1, cv1...
![...]
enddo
enddo
- Most of the times (implementing optical responses, transport properties...), the number of integer and continuous values is known. In these cases it is a clever idea to loop though the variables in a transparent way and then indexing.
cvshape = tsk%cdims%shape()
ivshape = tsk%idims%shape()
do w1 = 1, cvshape(1) !external variable 1,
cv1 = tsk%cdt(var = 1, step = w1)
do w2 = 1, cvshape(2) !external variable 2,
cv2 = tsk%cdt(var = 2, step = w2)
![...]
r_arr = [w1, w2, ...]
do i1 = 1, ivshape(1) !integer variable 1,
do i2 = 1, ivshape(2) !integer variable 2,
![...]
i_arr = [i1, i2, ...]
!your implementation in terms of i1, cv1...
cs(tsk%idims%ind(i_arr), tsk%rdims%ind(r_arr)) = ...
enddo
enddo
![...]
enddo
enddo
An initialized task is sampled by calling (1st way),
call tsk%sample(crys, kpart, store_at [, parallelization, other])
where
-
class(crystal), intent(in) :: crys
is a WannInt crystal. -
integer, intent(in) :: kpart(3)
is a integer array where each component specifies the number of points to partition each dimension of the BZ into. Each componentkpart(i)
discretizes the reciprocal lattice vector$\mathbf{b}_i$ . -
complex(dp), allocatable, intent(out) :: store_at(:, :, :)/store_at(:, :)
is a rank-3 or rank-2 array. If rank-3:- The first dimension specifies the memory layout representation of
$\textbf{k}$ in the order that will be laid out by a MAC container specifier withdimension_specifier = kpart
. - The second dimension is the memory layout representation of a particular permutation of integer indices
$\alpha$ in the order laid out bytsk%idims
. - The third dimension is the memory layout representation of a particular permutation of continuous variables
$\beta$ in the order laid out bytsk%cdims
.
- The first dimension specifies the memory layout representation of
- If rank-2:
- The first dimension is the memory layout representation of a particular permutation of integer indices
$\alpha$ in the order laid out bytsk%idims
. - The second dimension is the memory layout representation of a particular permutation of continuous variables
$\beta$ in the order laid out bytsk%cdims
. -
store_at
holds an unnormalized sum over all theproduct(kpart)
points given as input.
- The first dimension is the memory layout representation of a particular permutation of integer indices
-
character(len=*), optional, intent(in) :: parallelization
is an specification of the parallelization method to employ to distribute the sampling. Options areMPI+OMP
,MPI
,OMP
,none
. Default isOMP
. -
class(*), optional, intent(in) :: other
is an unlimited polymorphic object to be passed to the calculator during sampling. See interface.
Or by calling (2nd way), An initialized task is sampled by calling (1st way),
call tsk%sample(crys, klist, store_at [, parallelization, other])
where all present parameters are the same as described in the previous calling way except for
-
real(dp), intent(in) :: klist(3, nk)
, which is a list ofnk
points where eachklist(:, j)
represents a BZ point$\textbf{k}^j=(k^j_1, k^j_2, k^j_3)$ , where each$k^j_i$ is given in coordinates relative to the reciprocal lattice vectors$\mathbf{b}_i$ (crystal coordinates).
The library defines the utilities kpath
, kslice
. These utilities are of use to create a list of klist(3, nk)
which traverse a path and a slice of the BZ, respectively.
The utility is used as
path = kpath(vecs, nkpts)
where,
-
real(dp), intent(in) :: vecs(nv, 3)
are the coordinates ofnk
points where eachvecs(:, j)
represents a point$\textbf{k}^j=(k^j_1, k^j_2, k^j_3)$ , where each$k^j_i$ is given in coordinates relative to the reciprocal lattice vectors$\mathbf{b}_i$ (crystal coordinates). -
integer, intent(in) :: nkpts(nv - 1)
each elementnkpts(i)
is the number of points in a straight line to consider between pointsvecs(i, :)
,vecs(i + 1, :)
. -
real(dp), allocatable :: path(:, :)
is the return value. On output, the shape is[3, nk]
, wherenk = sum(nkpts) - (nvec - 2)
. Each elementpath(:, j)
represents a point$\textbf{k}^j=(k^j_1, k^j_2, k^j_3)$ , along the ordered path, where each$k^j_i$ is given in coordinates relative to the reciprocal lattice vectors$\mathbf{b}_i$ (crystal coordinates).
The utility is used as
slice = kslice(corner, vec_a, vec_b, part)
where,
-
real(dp), intent(in) :: corner(3)
are the coordinates of a point$\textbf{k}^j=(k^j_1, k^j_2, k^j_3)$ , where each$k^j_i$ is given in coordinates relative to the reciprocal lattice vectors$\mathbf{b}_i$ (crystal coordinates). This parameter represents the bottom-left point of the slice. -
real(dp), intent(in) :: vec_a(3), vec_b(3)
are the coordinates of vectors$\textbf{k}^1$ and$\textbf{k}^2$ , where each$k^j_i$ is given in coordinates relative to the reciprocal lattice vectors$\mathbf{b}_i$ (crystal coordinates), representing two vectors defining the slice. -
integer, intent(in) :: part(2)
is a integer array where each component specifies the number of points to partition each dimension of the BZ into. Each componentpart(i)
discretizes the reciprocal lattice vector$\mathbf{b}_i$ . -
real(dp), allocatable :: slice(:, :)
is the return value. On output, the shape is[3, nk]
, wherenk = product(part)
. Each elementslice(:, j)
represents a point$\textbf{k}^j=(k^j_1, k^j_2, k^j_3)$ , where each$k^j_i$ is given in coordinates relative to the reciprocal lattice vectors$\mathbf{b}_i$ (crystal coordinates). The ordering if$\textbf{k}$ points is given by the order that will be laid out by a MAC container specifier withdimension_specifier = part
.
The utility is used as
k_cart = crys_to_cart(k_crys, crys)
where,
-
real(dp), intent(in) :: k_crys(3)
are the coordinates of a point$\textbf{k}^j=(k^j_1, k^j_2, k^j_3)$ , where each$k^j_i$ is given in coordinates relative to the reciprocal lattice vectors$\mathbf{b}_i$ (crystal coordinates). -
class(crystal), intent(in) :: crys
is a WannInt crystal. -
real(dp), intent(in) :: k_cart(3)
is the return value. On output, the components are the coordinates of a point$\textbf{k}^j=(k^j_x, k^j_y, k^j_z)$ , where each$k^j_i$ is the component, in$\text{A}^{-1}$ , relative to the cartesian frame used to define the real lattice basis of the crystalcrys
.
The utility is used as
k_crys = cart_to_crys(k_cart, crys)
where,
-
real(dp), intent(in) :: k_cart(3)
are the coordinates of a point$\textbf{k}^j=(k^j_x, k^j_y, k^j_z)$ , where each$k^j_i$ is the component, in$\text{A}^{-1}$ , relative to the cartesian frame used to define the real lattice basis of the crystalcrys
. -
class(crystal), intent(in) :: crys
is a WannInt crystal. -
real(dp), intent(in) :: k_crys(3)
is the return value. On output, the components are the coordinates of a point$\textbf{k}^j=(k^j_1, k^j_2, k^j_3)$ , where each$k^j_i$ is given in coordinates relative to the reciprocal lattice vectors$\mathbf{b}_i$ (crystal coordinates).
The utility is used as
path_length = kpath_length(vecs, nkpts, crys)
where,
-
real(dp), intent(in) :: vecs(nv, 3)
are the coordinates ofnk
points where eachvecs(:, j)
represents a point$\textbf{k}^j=(k^j_1, k^j_2, k^j_3)$ , where each$k^j_i$ is given in coordinates relative to the reciprocal lattice vectors$\mathbf{b}_i$ (crystal coordinates). -
integer, intent(in) :: nkpts(nv - 1)
each elementnkpts(i)
is the number of points in a straight line to consider between pointsvecs(i, :)
,vecs(i + 1, :)
. -
class(crystal), intent(in) :: crys
is a WannInt crystal. -
real(dp), allocatable :: path_length(:)
is the return value. If the arraypath
has been constructed by using thekpath
utility, theik
th element of thepath_length
array contains the distance traversed along the kpath, in$\text{A}^{-1}$ , up until arriving to the pointpath(:, ik)
.
An automated build is available for Fortran Package Manager users. This is the recommended way to build and use WannInt in your projects. You can add WannInt to your project dependencies by including
[dependencies]
SsTC_driver = { git="https://github.com/irukoa/SsTC_driver.git" }
in the fpm.toml
file.
MAC's objects
type, public :: container_specifier
type, extends(container_specifier), public :: container
and WannInt's objects and utilities
type, public :: crystal
public :: diagonalize
public :: dirac_delta
public :: deg_list
public :: schur
public :: SVD
public :: expsh
public :: logu
are made public by SsTC_driver.
The mayor limitation lies in memory management when sampling with parallelization. The array store_at
needs to be dynamically allocated. Its size is given by nk
product(int_ind)
product(cont_data_steps)
. Before making any calculation it is suggested to check the value of its size and choose a parallelization scheme accordingly. It is recommended to use none
or OMP
parallelization in PCs and MPI
or MPI+OMP
in multinode clusters. If the size of store_at
is too big, segmentation faults may occur. We recommend using only store_at
in its rank-3 version in sampling calls if keeping the index of
We recommend the compilers in the Intel oneAPI toolkit ifort/mpiifort
and ifx/mpiifx
or the GNU compilers gfortran/mpifort
. If possible, we recommend using the --heap-arrays
flag for Intel compilers and the -fmax-stack-var-size=n
flag for GNU compilers.