!------------------------------------------------------------------------------
!
! This file is part of the Sternheimer-GW code.
!
! Copyright (C) 2010 - 2016
! Henry Lambert, Martin Schlipf, and Feliciano Giustino
!
! Sternheimer-GW is free software: you can redistribute it and/or modify
! it under the terms of the GNU General Public License as published by
! the Free Software Foundation, either version 3 of the License, or
! (at your option) any later version.
!
! Sternheimer-GW is distributed in the hope that it will be useful,
! but WITHOUT ANY WARRANTY; without even the implied warranty of
! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! GNU General Public License for more details.
!
! You should have received a copy of the GNU General Public License
! along with Sternheimer-GW. If not, see
! http://www.gnu.org/licenses/gpl.html .
!
!------------------------------------------------------------------------------
!> Evaluate the self-energy \f$\Sigma = G W\f$
!!
!! There are two aspects to this product. One is the spacial component and the
!! other is the frequency one. In real space, no convolution is needed and
!! \f$\Sigma\f$ is just the product of \f$G\f$ and \f$W\f$. Hence, we take the
!! Fourier transform to get from reciprocal space where \f$G\f$ and \f$W\f$ are
!! defined to real space. Then we multiply both and Fourier transform \f$\Sigma\f$
!! back to reciprocal space.
!!
!! For the frequency an integration is necessary. There are two different
!! implementations to evaluate this integration to obtain the self-energy
!! \f$\Sigma(\omega)\f$. The essential difference is whether the frequencies
!! \f$\omega\f$ are on the real or the imaginary axis. Common to both cases is
!! that \f$W(\omega)\f$ is given on a few points on the imaginary axis. To
!! get \f$W\f$ for an arbitary frequency in the complex plane, we use the
!! Pade approximation.
!!
!!
The real axis integration
!! If the frequencies of interest are on the real axis, we perform the following
!! integral [1,2]
!! \f{equation}{
!! \Sigma_k(\omega) = \frac{i}{2 \pi N} \sum_{q} \int d\omega'
!! G_{k-q}(\omega + \omega') W_{q}(\omega') e^{-i \delta \omega'}~.
!! \f}
!! Here, \f$\delta\f$ is a small positive number that ensures the correct time
!! order. \f$N\f$ is the number of \f$q\f$ points in the calculation. Note, that
!! we suppressed the spacial coordiantes for clarity. Because \f$W\f$ is evaluated
!! on the imaginary axis and continued to the real axis, it is convenient to
!! shift the integration boundaries
!! \f{equation}{ \label{sig:real}
!! \Sigma_k(\omega) = \frac{i}{2 \pi N} \sum_{q} \int d\omega'
!! G_{k-q}(\omega') W_{q}(\omega - \omega') e^{-i \delta (\omega - \omega')}~.
!! \f}
!! In this way, we can evaluate the Green's function on the integration grid once
!! and only the Coulomb potential is reevaluated for every frequency.
!!
!! The imaginary axis integration
!! The Fourier transform between frequency and time domain are defined as
!! \f{equation}{
!! f(t) = \int \frac{d\omega}{2\pi} f(\omega) e^{i\omega t}
!! \f}
!! and
!! \f{equation}{
!! f(\omega) = \int dt f(t) e^{-i\omega t}~.
!! \f}
!! One can extend this concept to time and frequency on the imaginary axis [3]
!! \f{equation}{
!! f(it) = i \int \frac{d\omega}{2\pi} f(i\omega) e^{i\omega t}
!! \f}
!! and
!! \f{equation}{
!! f(i\omega) = -i \int dt f(it) e^{-i\omega t}~.
!! \f}
!! Notice the occurence of the extra factors \f$\pm i\f$.
!!
!! Taking the Fourier transform of \eqref{sig:real}, we obtain due to the Fourier
!! convolution theorem
!! \f{equation}{
!! \Sigma_k(t) = \frac{i}{N} \sum_q G_{k-q}(t) W_q(\delta - t)
!! \f}
!! the analytic continuation of this equation to the complex plane yields
!! \f{equation}{
!! \Sigma_k(it) = \frac{i}{N} \sum_q G_{k-q}(it) W_q(\delta - it)~.
!! \f}
!! The equivalent of the Fourier convolution theorem for the complex Fourier
!! transform brings us back to frequency space
!! \f{equation}{
!! \Sigma_k(i\omega) = -\frac{1}{N} \sum_q \int \frac{d \omega'}{2\pi}
!! G_{k-q}(i\omega') W_{q}(i\omega - i\omega') e^{-\delta \omega'}.
!! \f}
!! Because the Green's function is evaluated on the imaginary axis (where it
!! is smooth), we can take the limit \f$\delta \rightarrow 0\f$ without
!! numerical problems.
!!
!! References
!! [1]
!! Giustino, Cohen, Louie, Phys. Rev. B **81**, 115105 (2010)
!!
!!
!! [2]
!! Lambert, Giustino, Phys. Rev. B **88**, 075117 (2013)
!!
!!
!! [3]
!! Rieger, *et al.*, Comput. Phys. Comm. **117**, 211 (1999)
!!
MODULE sigma_module
USE kinds, ONLY: dp
IMPLICIT NONE
PRIVATE
PUBLIC sigma_wrapper, sigma_config_type
!> This type contains the information necessary to evaluate the self-energy.
TYPE sigma_config_type
!> The index of the q point at which the Coulomb potential is evaluated.
INTEGER index_q
!> The index of the k - q point at which the Green's function is evaluated.
INTEGER index_kq
!> The inverse symmetry operation to go from q to star(q).
INTEGER inv_op
!> weight of the active point
REAL(dp) weight
END TYPE sigma_config_type
CONTAINS
!> This function provides a wrapper that extracts the necessary information
!! from the global modules to evaluate the self energy.
SUBROUTINE sigma_wrapper(ikpt, freq, vcut, config)
USE cell_base, ONLY: omega
USE constants, ONLY: tpi
USE control_gw, ONLY: multishift, tr2_green, output, tmp_dir_coul
USE disp, ONLY: x_q
USE ener, ONLY: ef
USE freqbins_module, ONLY: freqbins_type
USE gvect, ONLY: ngm
USE gwsigma, ONLY: sigma_c_st, gcutcorr
USE io_files, ONLY: prefix
USE io_global, ONLY: meta_ionode, ionode_id
USE kinds, ONLY: dp
USE klist, ONLY: lgauss
USE mp, ONLY: mp_bcast
USE mp_images, ONLY: my_image_id, inter_image_comm, root_image
USE mp_pools, ONLY: inter_pool_comm, root_pool
USE output_mod, ONLY: filcoul
USE parallel_module, ONLY: parallel_task, mp_root_sum, mp_gatherv
USE sigma_io_module, ONLY: sigma_io_write_c
USE symm_base, ONLY: nsym, s, invs, ftau, nrot
USE timing_module, ONLY: time_sigma_c, time_sigma_setup, &
time_sigma_io, time_sigma_comm
USE truncation_module, ONLY: vcut_type
USE units_gw, ONLY: iuncoul, lrcoul, iunsigma, lrsigma
!> index of the k-point for which the self energy is evaluated
INTEGER, INTENT(IN) :: ikpt
!> type containing the information about the frequencies used for the integration
TYPE(freqbins_type), INTENT(IN) :: freq
!> the truncated Coulomb potential
TYPE(vcut_type), INTENT(IN) :: vcut
!> evaluate the self-energy for these configurations
TYPE(sigma_config_type), INTENT(IN) :: config(:)
!> complex constant of zero
COMPLEX(dp), PARAMETER :: zero = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
!> real constant of 0.5
REAL(dp), PARAMETER :: half = 0.5_dp
!> counter on the configurations
INTEGER icon
!> store the index of the q-point (to avoid rereading the Coulomb matrix)
INTEGER iq
!> complex prefactor
!! for real frequency integration it is \f$ i / 2 \pi N\f$
!! for imaginary frequencies it is \f$-1 / 2 \pi N\f$
COMPLEX(dp) prefactor
!> the prefactor including the q dependent parts
COMPLEX(dp) alpha
!> the symmetry map from reduced to full G mesh
INTEGER, ALLOCATABLE :: gmapsym(:,:)
!> the phase associated with the symmetry
COMPLEX(dp), ALLOCATABLE :: eigv(:,:)
!> the highest and lowest occupied state (in Ry)
REAL(dp) ehomo, elumo
!> the chemical potential of the system
REAL(dp) mu
!> the screened Coulomb interaction
COMPLEX(dp), ALLOCATABLE :: coulomb(:,:,:)
!> the self-energy at the current k-point
COMPLEX(dp), ALLOCATABLE :: sigma(:,:,:)
!> the self-energy at the current k-point collected on the root process
COMPLEX(dp), ALLOCATABLE :: sigma_root(:,:,:)
!> the upper and lower boundary of the frequencies
INTEGER first_sigma, last_sigma
!> the number of frequency tasks done by the various processes
INTEGER, ALLOCATABLE :: num_task(:)
!> number of bytes in a real
INTEGER, PARAMETER :: byte_real = 8
!> name of file in which the Coulomb interaction is store
CHARACTER(:), ALLOCATABLE :: filename
!> error flag from file opening
INTEGER ierr
!> flag indicating whether Coulomb file is already opened
LOGICAL opend
CALL start_clock(time_sigma_c)
CALL start_clock(time_sigma_setup)
!
! set the prefactor depending on whether we integrate along the real or
! the imaginary frequency axis
!
IF (freq%imag_sigma) THEN
prefactor = CMPLX(-1.0_dp / (tpi * REAL(nsym, KIND=dp)), 0.0_dp, KIND=dp)
ELSE
prefactor = CMPLX(0.0_dp, 1.0_dp / (tpi * REAL(nsym, KIND=dp)), KIND=dp)
END IF
!
! determine symmetry
!
ALLOCATE(gmapsym(ngm, nrot))
ALLOCATE(eigv(ngm, nrot))
CALL gmap_sym(nsym, s, ftau, gmapsym, eigv, invs)
DEALLOCATE(eigv)
!
! define the chemical potential
!
IF (.NOT.lgauss) THEN
!
! for semiconductors choose the middle of the gap
CALL get_homo_lumo(ehomo, elumo)
mu = half * (ehomo + elumo)
!
ELSE
!
! for metals set it to the Fermi energy
mu = ef
!
END IF
CALL mp_bcast(mu, ionode_id, inter_pool_comm)
!
! parallelize frequencies over images
!
CALL parallel_task(inter_image_comm, freq%num_sigma(), first_sigma, last_sigma, num_task)
CALL stop_clock(time_sigma_setup)
!
! initialize self energy
!
ALLOCATE(sigma(gcutcorr, gcutcorr, num_task(my_image_id + 1)))
sigma = zero
!
! open the file containing the coulomb interaction
! TODO a wrapper routine for all I/O
!
INQUIRE(UNIT = iuncoul, OPENED = opend)
IF (.NOT. opend) THEN
!
filename = TRIM(tmp_dir_coul) // TRIM(prefix) // "." // TRIM(filcoul) // "1"
OPEN(UNIT = iuncoul, FILE = filename, IOSTAT = ierr, &
ACCESS = 'direct', STATUS = 'old', RECL = byte_real * lrcoul)
CALL errore(__FILE__, "error opening " // filename, ierr)
!
END IF ! open file
!
! initialize index of q (set to 0 so that it is always read on first call)
iq = 0
!
! allocate array for the coulomb matrix
ALLOCATE(coulomb(gcutcorr, gcutcorr, freq%num_freq()))
!
! sum over all q-points
!
DO icon = 1, SIZE(config)
!
! evaluate the coefficients for the analytic continuation of W
!
IF (config(icon)%index_q /= iq) THEN
iq = config(icon)%index_q
CALL davcio(coulomb, lrcoul, iuncoul, iq, -1)
CALL coulpade(coulomb, x_q(:,iq), vcut)
END IF
!
! determine the prefactor
!
alpha = config(icon)%weight * prefactor
!
! evaluate Sigma
!
CALL sigma_correlation(omega, sigma_c_st, multishift, 4, tr2_green, &
mu, alpha, config(icon)%index_kq, freq, first_sigma, &
gmapsym(:gcutcorr, config(icon)%inv_op), &
coulomb, sigma)
!
END DO ! icon
DEALLOCATE(gmapsym)
DEALLOCATE(coulomb)
!
! collect sigma on a single process
!
CALL start_clock(time_sigma_comm)
! first sum sigma across the pool
CALL mp_root_sum(inter_pool_comm, root_pool, sigma)
! the gather the array across the images
CALL mp_gatherv(inter_image_comm, root_image, num_task, sigma, sigma_root)
DEALLOCATE(num_task)
DEALLOCATE(sigma)
CALL stop_clock(time_sigma_comm)
!
! the root process writes sigma to file
!
CALL start_clock(time_sigma_io)
IF (meta_ionode) THEN
!
CALL davcio(sigma_root, lrsigma, iunsigma, ikpt, 1)
CALL sigma_io_write_c(output%unit_sigma, ikpt, sigma_root)
DEALLOCATE(sigma_root)
!
END IF ! ionode
CALL stop_clock(time_sigma_io)
CALL stop_clock(time_sigma_c)
END SUBROUTINE sigma_wrapper
!> Evaluate the product \f$\Sigma = \alpha G W\f$.
!!
!! The product is evaluated in real space. Because the Green's function can
!! be used for several W values, we expect that the Green's function is
!! already transformed to real space by the calling routine.
SUBROUTINE sigma_prod(omega, fft_cust, alpha, gmapsym, green, array)
USE fft_custom, ONLY: fft_cus
USE fft6_module, ONLY: invfft6, fwfft6
USE kinds, ONLY: dp
USE timing_module, ONLY: time_GW_product
!> volume of the unit cell
REAL(dp), INTENT(IN) :: omega
!> type that defines the custom Fourier transform for Sigma
TYPE(fft_cus), INTENT(IN) :: fft_cust
!> The prefactor with which the self-energy is multiplied
COMPLEX(dp), INTENT(IN) :: alpha
!> the symmetry mapping from the reduced to the full G mesh
INTEGER, INTENT(IN) :: gmapsym(:)
!> the Green's function in real space \f$G(r, r')\f$
COMPLEX(dp), INTENT(IN) :: green(:,:)
!> *on input* the screened Coulomb interaction with both indices in reciprocal space
!! *on output* the self-energy with both indices in reciprocal space
COMPLEX(dp), INTENT(INOUT) :: array(:,:)
!> the number of points in real space
INTEGER num_r
!> the number of G vectors defining the reciprocal space
INTEGER num_g
!> counter on G vectors
INTEGER ig
CALL start_clock(time_GW_product)
! determine the helper variables
num_r = fft_cust%dfftt%nnr
num_g = fft_cust%ngmt
!
! sanity check of the input
!
IF (SIZE(green, 1) /= num_r) &
CALL errore(__FILE__, "size of G inconsistent with FFT definition", 1)
IF (SIZE(green, 2) /= num_r) &
CALL errore(__FILE__, "Green's function not a square matrix", 1)
IF (SIZE(array, 1) /= num_r) &
CALL errore(__FILE__, "size of array inconsistent with FFT definition", 1)
IF (SIZE(array, 2) /= num_r) &
CALL errore(__FILE__, "array is not a square matrix", 1)
IF (SIZE(gmapsym) /= num_g) &
CALL errore(__FILE__, "gmapsym is inconsistent with FFT definition", 1)
!!
!! 1. We Fourier transform \f$W(G, G')\f$ to real space.
!!
! array contains W(r, r')
CALL invfft6('Custom', array, fft_cust%dfftt, fft_cust%nlt(gmapsym), omega)
!!
!! 2. We evaluate the product in real space
!! \f$\Sigma(r, r') / \alpha = G(r, r') W(r, r') \f$
!!
! array contains Sigma(r, r') / alpha
array = green * array
!!
!! 3. The resulting is transformed back to reciprocal space \f$\Sigma(G, G')\f$.
!!
!. array contains Sigma(G, G') / alpha
CALL fwfft6('Custom', array, fft_cust%dfftt, fft_cust%nlt, omega)
!! 4. We multiply with the prefactor \f$\alpha\f$.
DO ig = 1, num_g
array(:num_g, ig) = alpha * array(:num_g, ig)
END DO ! ig
CALL stop_clock(time_GW_product)
END SUBROUTINE sigma_prod
!> Evaluate the correlation self-energy for a given frequency range.
!!
!! The algorithm consists of the following steps:
SUBROUTINE sigma_correlation(omega, fft_cust, multishift, lmax, threshold, &
mu, alpha, ikq, freq, first_sigma, &
gmapsym, coulomb, sigma)
USE fft_custom, ONLY: fft_cus
USE fft6_module, ONLY: invfft6
USE freqbins_module, ONLY: freqbins_type
USE green_module, ONLY: green_prepare, green_function, green_nonanalytic
USE kinds, ONLY: dp
USE mp_images, ONLY: inter_image_comm
!> Volume of the unit cell
REAL(dp), INTENT(IN) :: omega
!> Type that defines the custom Fourier transform for Sigma
TYPE(fft_cus), INTENT(IN) :: fft_cust
!> If this flag is set, we use the multishift solver
LOGICAL, INTENT(IN) :: multishift
!> The l value of the BiCGStab(l) algorithm.
INTEGER, INTENT(IN) :: lmax
!> The convergence threshold for the solver.
REAL(dp), INTENT(IN) :: threshold
!> The chemical potential of the system.
REAL(dp), INTENT(IN) :: mu
!> The prefactor with which the self-energy is multiplied
COMPLEX(dp), INTENT(IN) :: alpha
!> The index of the point k - q at which the Green's function is evaluated
INTEGER, INTENT(IN) :: ikq
!> This type defines the frequencies used for the integration
TYPE(freqbins_type), INTENT(IN) :: freq
!> the first frequency done on this process
INTEGER, INTENT(IN) :: first_sigma
!> the symmetry mapping from the reduced to the full G mesh
INTEGER, INTENT(IN) :: gmapsym(:)
!> The screened Coulomb interaction in reciprocal space
COMPLEX(dp), INTENT(IN) :: coulomb(:,:,:)
!> The self-energy \f$\Sigma\f$ at the specified frequency points
COMPLEX(dp), INTENT(OUT) :: sigma(:,:,:)
!> the number of real space points used for the correlation
INTEGER num_r_corr
!> The number of plane-waves in the NSCF calculation.
INTEGER num_g
!> the number of G vectors used for the correlation
INTEGER num_g_corr
!> the number of tasks done by this process
INTEGER num_task
!> counter on the number of tasks
INTEGER itask
!> the number of frequencies for the Green's function
INTEGER num_green
!> counter on the Green's function frequencies
INTEGER igreen
!> counter on the Coulomb frequencies
INTEGER icoul
!> Counter for the frequencies of the self energy.
INTEGER isigma
!> The map from G-vectors at current k to global array.
INTEGER, ALLOCATABLE :: map(:)
!> the occupation of the states
REAL(dp), ALLOCATABLE :: occupation(:)
!> complex value of the chemical potential
COMPLEX(dp) mu_
!> The scaling prefactor in the integration, this is the product of alpha
!! and the weight of the frequency point.
COMPLEX(dp) alpha_weight
!> The current frequency used for the Coulomb interaction
COMPLEX(dp) freq_coul
!> The frequencies used for the Green's function
COMPLEX(dp), ALLOCATABLE :: freq_green(:)
!> The frequencies used for the self energy.
COMPLEX(dp), ALLOCATABLE :: freq_sigma(:)
!> The Green's function in real or reciprocal space
COMPLEX(dp), ALLOCATABLE :: green(:,:,:)
!> work array; contains either W or \f$\Sigma\f$
COMPLEX(dp), ALLOCATABLE :: work(:,:)
!> The eigenvalues \f$\epsilon\f$ for the occupied bands.
COMPLEX(dp), ALLOCATABLE :: eval(:)
!> The eigenvectors \f$u_{\text v}(G)\f$ of the occupied bands.
COMPLEX(dp), ALLOCATABLE :: evec(:,:)
!> complex constant of 0
COMPLEX(dp), PARAMETER :: zero = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
!
! sanity check of the input
!
num_r_corr = fft_cust%dfftt%nnr
num_g_corr = fft_cust%ngmt
num_task = SIZE(sigma, 3)
IF (SIZE(coulomb, 1) /= num_g_corr) &
CALL errore(__FILE__, "screened Coulomb and FFT type inconsistent", 1)
IF (SIZE(coulomb, 2) /= num_g_corr) &
CALL errore(__FILE__, "screened Coulomb must be a square matrix", 1)
IF (SIZE(sigma, 1) /= num_g_corr) &
CALL errore(__FILE__, "self energy and FFT type inconsistent", 1)
IF (SIZE(sigma, 2) /= num_g_corr) &
CALL errore(__FILE__, "self energy must be a square matrix", 1)
IF (first_sigma + num_task - 1 > freq%num_sigma()) &
CALL errore(__FILE__, "frequency index is out of bounds of the frequency array", 1)
IF (SIZE(gmapsym) /= num_g_corr) &
CALL errore(__FILE__, "gmapsym and FFT type are inconsistent", 1)
!!
!! 1. shift the frequency grid so that the origin is at the Fermi energy
!!
mu_ = CMPLX(mu, 0.0_dp, KIND=dp)
num_green = 2 * freq%num_coul()
ALLOCATE(freq_green(num_green))
ALLOCATE(freq_sigma(freq%num_sigma()))
freq_green = freq%green(mu_)
freq_sigma = mu_ + freq%sigma
!!
!! 2. prepare the QE module so that we can evaluate the Green's function
!!
! this will allocate map
CALL green_prepare(ikq, num_g_corr, map, num_g, occupation, eval, evec)
!!
!! 3. evaluate the Green's function of the system
!!
! allocate an array that can contain the Fourier transformed quanity
ALLOCATE(green(num_r_corr, num_r_corr, num_green))
! after this call, we obtained G(G, G', w)
CALL green_function(inter_image_comm, multishift, lmax, threshold, map, &
num_g, freq_green, green)
!!
!! 4. we add the nonanalytic part if on the real axis
!!
IF (.NOT. freq%imag_sigma) THEN
CALL green_nonanalytic(map, freq_green, occupation, eval, evec, green)
END IF
DEALLOCATE(occupation, eval, evec)
!!
!! 5. Fourier transform Green's function to real space
!!
! the result is G(r, r', w)
DO igreen = 1, num_green
CALL invfft6('Custom', green(:,:,igreen), fft_cust%dfftt, fft_cust%nlt, omega)
END DO ! igreen
! create work array
ALLOCATE(work(num_r_corr, num_r_corr))
!!
!! 6. distribute the frequencies of \f$\Sigma\f$ across the image
!!
DO itask = 1, num_task
!
isigma = first_sigma + itask - 1
!
DO igreen = 1, num_green
!!
!! 7. construct W for the frequency \f$\omega^{\Sigma} - \omega^{\text{green}}\f$.
!!
work = zero
freq_coul = freq_sigma(isigma) - freq_green(igreen)
! work will contain W(G, G', wS - wG)
CALL construct_w(coulomb, work(1:num_g_corr, 1:num_g_corr), ABS(freq_coul))
!!
!! 8. convolute G and W
!!
icoul = MOD(igreen - 1, freq%num_coul()) + 1
alpha_weight = alpha * freq%weight(icoul)
! work will contain Sigma(G, G', wS)
CALL sigma_prod(omega, fft_cust, alpha_weight, gmapsym, green(:,:,igreen), work)
!
!!
!! 9. add the result to \f$\Sigma\f$
!!
sigma(:,:,itask) = sigma(:,:,itask) + work(1:num_g_corr, 1:num_g_corr)
!
END DO ! igreen
!
END DO ! isigma
END SUBROUTINE sigma_correlation
END MODULE sigma_module