!------------------------------------------------------------------------------ ! ! 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 . ! !------------------------------------------------------------------------------ !> Provides the routines to truncate a quantity to improve the k-point convergence. MODULE truncation_module USE kinds, ONLY: dp USE coulomb_vcut_module IMPLICIT NONE ! ! definition of different truncation methods ! !> length of the truncation method INTEGER, PARAMETER :: trunc_length = 80 !> no truncation - use bare Coulomb potential INTEGER, PARAMETER :: NO_TRUNCATION = 0 CHARACTER(LEN=trunc_length), PARAMETER :: NO_TRUNCATION_1 = 'none' CHARACTER(LEN=trunc_length), PARAMETER :: NO_TRUNCATION_2 = 'off' CHARACTER(LEN=trunc_length), PARAMETER :: NO_TRUNCATION_3 = 'false' CHARACTER(LEN=trunc_length), PARAMETER :: NO_TRUNCATION_4 = 'no' CHARACTER(LEN=trunc_length), PARAMETER :: NO_TRUNCATION_5 = 'no truncation' !> spherical truncation - truncate potential at certain distance INTEGER, PARAMETER :: SPHERICAL_TRUNCATION = 1 CHARACTER(LEN=trunc_length), PARAMETER :: SPHERICAL_TRUNCATION_1 = 'on' CHARACTER(LEN=trunc_length), PARAMETER :: SPHERICAL_TRUNCATION_2 = 'true' CHARACTER(LEN=trunc_length), PARAMETER :: SPHERICAL_TRUNCATION_3 = 'yes' CHARACTER(LEN=trunc_length), PARAMETER :: SPHERICAL_TRUNCATION_4 = 'spherical' CHARACTER(LEN=trunc_length), PARAMETER :: SPHERICAL_TRUNCATION_5 = 'spherical truncation' !> film geometry truncation (expects film in x-y plane) - !! truncate potential at certain height INTEGER, PARAMETER :: FILM_TRUNCATION = 2 CHARACTER(LEN=trunc_length), PARAMETER :: FILM_TRUNCATION_1 = 'film' CHARACTER(LEN=trunc_length), PARAMETER :: FILM_TRUNCATION_2 = 'film truncation' CHARACTER(LEN=trunc_length), PARAMETER :: FILM_TRUNCATION_3 = '2d' CHARACTER(LEN=trunc_length), PARAMETER :: FILM_TRUNCATION_4 = '2d truncation' !> spherical truncation using the QE coulomb_vcut module INTEGER, PARAMETER :: VCUT_SPHERICAL_TRUNCATION = 3 CHARACTER(LEN=trunc_length), PARAMETER :: VCUT_SPHERICAL_TRUNCATION_1 = 'vcut spherical' CHARACTER(LEN=trunc_length), PARAMETER :: VCUT_SPHERICAL_TRUNCATION_2 = 'vcut spherical truncation' CHARACTER(LEN=trunc_length), PARAMETER :: VCUT_SPHERICAL_TRUNCATION_3 = 'spherical vcut' CHARACTER(LEN=trunc_length), PARAMETER :: VCUT_SPHERICAL_TRUNCATION_4 = 'spherical truncation vcut' !> Wigner-Seitz truncation using the QE coulomb_vcut module INTEGER, PARAMETER :: VCUT_WIGNER_SEITZ_TRUNCATION = 4 CHARACTER(LEN=trunc_length), PARAMETER :: VCUT_WIGNER_SEITZ_TRUNCATION_1 = 'wigner-seitz' CHARACTER(LEN=trunc_length), PARAMETER :: VCUT_WIGNER_SEITZ_TRUNCATION_2 = 'wigner-seitz truncation' CHARACTER(LEN=trunc_length), PARAMETER :: VCUT_WIGNER_SEITZ_TRUNCATION_3 = 'ws' CHARACTER(LEN=trunc_length), PARAMETER :: VCUT_WIGNER_SEITZ_TRUNCATION_4 = 'ws truncation' PRIVATE truncate_bare, truncate_spherical, truncate_film CONTAINS !> Evaluate how the quantity associated with a reciprocal vector is truncated. !! !! Calculate a factor to scale a quantity defined in reciprocal space. There are !! different methods implemented to truncate the quantity, which are selected by !! the first parameter. !! !!

No truncation

!! The bare Coulomb potential is used and only the divergent terms are removed. !! To activate this truncation scheme, set truncation to 'none', 'off', 'false', !! 'no', or 'no truncation' in the input file. !! !!

Spherical truncation

!! We truncate in real space at a certain radius R. In reciprocal space this leads !! to the prefactor \f$[1 - \cos(k R)]\f$. This prefactor expands to \f$(k R)^2 / 2\f$ !! for small k cancelling the divergence of the Coulomb potential there. To activate !! this truncation scheme, set truncation to 'on', 'true', 'yes', 'spherical', or !! 'spherical truncation' in the input file. !! !! There is an alternative implementation of the spherical truncation inside of !! QuantumEspresso. To activate it set truncation to 'vcut spherical', 'vcut !! spherical truncation', 'spherical vcut', or 'spherical truncation vcut' in the !! input file. The difference between these implementations is only in the choice !! of the radius of the sphere. Note that this will significantly increase the !! setup time, because the vcut type is initialized. !! !!

Film truncation

!! We truncate at a certain height Z, which eliminates the divergence in reciprocal !! space. For details refer to Ismail-Beigi, Phys. Rev. B 73, 233103 (2006). To !! activate this truncation scheme, set truncation to 'film', '2d', 'film truncation', !! or '2d truncation' in the input file. !! !!

Wigner-Seitz truncation

!! We truncate the potential to the actual super cell of the calculation as defined !! by the q-point mesh. For this we separate the potential into a short- and a !! long-range part. The short-range part is evaluated analytically and the long-range !! one via a Fourier transform. Because this evaluation is computationally costly, !! it is done once at the beginning of the calculation and tabulated. Expect an !! significantly increases setup time, though. To select this truncation set !! truncation to 'wigner-seitz', 'wigner-seitz truncation', 'ws', or 'ws truncation' !! in the input file. !! FUNCTION truncate(method, vcut, kpt) RESULT (factor) USE cell_base, ONLY: at, alat, omega USE constants, ONLY: fpi USE disp, ONLY: nq1, nq2, nq3 !> Truncation method used; must be one of the integer constants !! defined in this module. INTEGER, INTENT(IN) :: method !> The truncated potential evaluated with the QE coulomb_vcut module TYPE(vcut_type), INTENT(IN) :: vcut !> Reciprocal lattice vector for which the quantity is truncated. REAL(dp), INTENT(IN) :: kpt(3) !> Coulomb potential in reciprocal space scaled according to the specified !! truncation scheme. REAL(dp) factor REAL(dp) length_cut SELECT CASE (method) CASE (NO_TRUNCATION) factor = truncate_bare(kpt) CASE (SPHERICAL_TRUNCATION) ! cutoff radius length_cut = (3 * omega * nq1 * nq2 * nq3 / fpi)**(1.0 / 3.0) factor = truncate_spherical(kpt, length_cut) CASE (FILM_TRUNCATION) ! cutoff height length_cut = 0.5 * SQRT(SUM(at(:,3)**2)) * alat * nq3 factor = truncate_film(kpt, length_cut) CASE (VCUT_SPHERICAL_TRUNCATION) factor = vcut_spheric_get(vcut, kpt) CASE (VCUT_WIGNER_SEITZ_TRUNCATION) factor = vcut_get(vcut,kpt) CASE DEFAULT CALL errore(__FILE__, "unknown truncation method", 1) factor = 0.0_dp END SELECT ! method END FUNCTION truncate !> Implements the bare Coulomb potential. !! \param[in] kpt The point in reciprocal space. !! \see truncate for the details. REAL(dp) FUNCTION truncate_bare(kpt) RESULT (factor) USE constants, ONLY : eps8, fpi, e2 REAL(dp), INTENT(IN) :: kpt(3) ! |k| and |k|^2 REAL(dp) length_k, length_k2 length_k2 = SUM(kpt**2) length_k = SQRT(length_k2) ! for large k vector IF (length_k > eps8) THEN ! bare Coulomb potential 4 pi e^2 / k^2 factor = fpi * e2 / length_k2 ! small k are removed ELSE factor = 0 END IF END FUNCTION truncate_bare !> Implements the spherical truncation. !! \param[in] kpt The point in reciprocal space. !! \param[in] rcut The distance at which the potential is cut in real space. !! \see truncate REAL(dp) FUNCTION truncate_spherical(kpt, rcut) RESULT (factor) USE constants, ONLY: eps8, tpi, fpi, e2 REAL(dp), INTENT(IN) :: kpt(3) REAL(dp), INTENT(IN) :: rcut ! |k| and |k|^2 REAL(dp) length_k, length_k2 length_k2 = SUM(kpt**2) length_k = SQRT(length_k2) ! for large k vector IF (length_k > eps8) THEN ! Coulomb potential 4 pi e^2 / k^2 is scaled by (1 - cos(k r)) factor = fpi * e2 / length_k2 * (1 - COS(rcut * length_k)) ! limit of small values ELSE ! (1 - cos(k r)) ~ (k r)^2 / 2 ! with prefactor 4 pi e^2 / k^2, this yields 2 pi e^2 r^2 factor = tpi * e2 * rcut**2 END IF END FUNCTION truncate_spherical !> Implements the film truncation. !! \param[in] kpt The point in reciprocal space. !! \param[in] zcut The height at which the potential is cut in real space. !! \see truncate REAL(dp) FUNCTION truncate_film(kpt, zcut) RESULT (factor) USE constants, ONLY: eps6, tpi, fpi, e2 REAL(dp), INTENT(IN) :: kpt(3) REAL(dp), INTENT(IN) :: zcut ! |k| and |k|^2 REAL(dp) length_k, length_k2 ! length of vector in z direction and in xy plane REAL(dp) length_kz, length_kxy length_k2 = SUM(kpt**2) length_k = SQRT(length_k2) length_kxy = SQRT(SUM(kpt(1:2)**2)) length_kz = kpt(3) ! general case - large vector IF (length_k > eps6) THEN ! Coulomb potential 4 pi e^2 / k^2 is scaled by (1 - exp(-kxy z) * cos(kz z)) factor = fpi * e2 / length_k2 * (1 - EXP(-length_kxy * zcut) * COS(length_kz * zcut)) ! special case - small vector ELSE ! 1 - cos(kz z) ~ 1 - (kz z)^2 / 2 = (kz z)^2 / 2 ! with prefactor 4 pi e^2 / k^2, this yields 2 pi e^2 z^2 factor = tpi * e2 * zcut**2 END IF END FUNCTION truncate_film !> This subroutine is a wrapper to vcut_init of coulomb_vcut module !! with added restarting capabilities. !! !! If a file with the vcut_type is found, it is opened and it's consistency !! with the input parameters is assessed. If the type matches, we do not need !! to reevaluate all the information about the truncation and just load it !! from file. If the file doesn't match the provided information or the file !! doesn't exist, we evaluate truncated potential from scratch. SUBROUTINE vcut_reinit(vcut, super_cell, cutoff, tmp_dir) USE constants, ONLY: eps12 USE iotk_module, ONLY: iotk_free_unit, iotk_open_write, iotk_open_read, & iotk_write_dat, iotk_scan_dat, iotk_close_write, & iotk_close_read !> the truncated Coulomb potential TYPE(vcut_type), INTENT(OUT) :: vcut !> the lattice vectors of the super cell spanned by the q Grid REAL(dp), INTENT(IN) :: super_cell(3,3) !> the energy cutoff used to generate the mesh REAL(dp), INTENT(IN) :: cutoff !> the directory to which the file is written CHARACTER(*), INTENT(IN) :: tmp_dir !> the name of the file in which vcut is stored CHARACTER(*), PARAMETER :: filename = 'vcut.xml' !> the path to the file CHARACTER(:), ALLOCATABLE :: filepath !> the root tag used in the file CHARACTER(*), PARAMETER :: tag_root = 'TRUNC_COULOMB' !> the tag for the unit cell CHARACTER(*), PARAMETER :: tag_cell = 'UNIT_CELL' !> the tag for the reciprocal unit cell CHARACTER(*), PARAMETER :: tag_inv_cell = 'REC_UNIT_CELL' !> the tag for the volume of the unit cell CHARACTER(*), PARAMETER :: tag_volume = 'VOLUME' !> the tag for the volume of the reciprocal unit cell CHARACTER(*), PARAMETER :: tag_inv_volume = 'REC_VOLUME' !> the tag for the shape of the array CHARACTER(*), PARAMETER :: tag_shape = 'SHAPE_ARRAY' !> the tag for the truncated Coulomb potential CHARACTER(*), PARAMETER :: tag_trunc_coul = 'POTENTIAL' !> the tag for the energy cutoff CHARACTER(*), PARAMETER :: tag_cutoff = 'ENERGY_CUTOFF' !> the tag for the flag indicating if the cell is orthorhombic CHARACTER(*), PARAMETER :: tag_ortho = 'ORTHORHOMBIC' !> does the file exist LOGICAL lexist !> unit used to access the file INTEGER iunit !> helper to read the shape of the array INTEGER nn(3) ! check if the file exists filepath = TRIM(tmp_dir) // filename INQUIRE(FILE = filepath, EXIST = lexist) ! find a free unit CALL iotk_free_unit(iunit) ! ! read the data from the file ! DO WHILE (lexist) ! open the file CALL iotk_open_read(iunit, filepath, binary = .TRUE.) ! read the energy cutoff and the unit cell CALL iotk_scan_dat(iunit, tag_cutoff, vcut%cutoff) CALL iotk_scan_dat(iunit, tag_cell, vcut%a) ! check if this is compatible with the input IF (ABS(vcut%cutoff - cutoff) > eps12 .OR. & ANY(ABS(vcut%a - super_cell) > eps12) ) THEN ! if there is a non-zero difference close the file and abort reading CALL iotk_close_read(iunit) EXIT END IF ! read the rest of the cell information CALL iotk_scan_dat(iunit, tag_inv_cell, vcut%b) CALL iotk_scan_dat(iunit, tag_volume, vcut%a_omega) CALL iotk_scan_dat(iunit, tag_inv_volume, vcut%b_omega) CALL iotk_scan_dat(iunit, tag_ortho, vcut%orthorombic) ! read the shape of the array CALL iotk_scan_dat(iunit, tag_shape, nn) ! the shape values should be odd IF (ANY(MOD(nn, 2) /= 1)) THEN ! if any even exists we close the file and abort reading CALL iotk_close_read(iunit) EXIT END IF ! allocate array for the truncated Coulomb potential nn = nn / 2 ALLOCATE(vcut%corrected(-nn(1):nn(1), -nn(2):nn(2), -nn(3):nn(3))) CALL iotk_scan_dat(iunit, tag_trunc_coul, vcut%corrected) ! after the file is read we are done CALL iotk_close_read(iunit) RETURN END DO ! read file ! ! we should not reach this statement unless reading the file failed, ! so we generate a new vcut type and write it to disk ! CALL vcut_init(vcut, super_cell, cutoff) ! open the file CALL iotk_open_write(iunit, filepath, binary = .TRUE., root = tag_root) ! ! write the vcut type to disk ! CALL iotk_write_dat(iunit, tag_cutoff, vcut%cutoff) CALL iotk_write_dat(iunit, tag_cell, vcut%a) CALL iotk_write_dat(iunit, tag_inv_cell, vcut%b) CALL iotk_write_dat(iunit, tag_volume, vcut%a_omega) CALL iotk_write_dat(iunit, tag_inv_volume, vcut%b_omega) CALL iotk_write_dat(iunit, tag_ortho, vcut%orthorombic) CALL iotk_write_dat(iunit, tag_shape, SHAPE(vcut%corrected)) CALL iotk_write_dat(iunit, tag_trunc_coul, vcut%corrected) ! close the file CALL iotk_close_write(iunit) END SUBROUTINE vcut_reinit END MODULE truncation_module