! ! Copyright (C) 2004 PWSCF group ! This file is distributed under the terms of the ! GNU General Public License. See the file `License' ! in the root directory of the present distribution, ! or http://www.gnu.org/copyleft/gpl.txt . ! ! !--------------------------------------------------------------- subroutine vxcgc(ndm,mesh,nspin,r,r2,rho,rhoc,vgc,egc,iflag) !--------------------------------------------------------------- ! ! ! This routine computes the exchange and correlation potential and ! energy to be added to the local density, to have the first ! gradient correction. ! In input the density is rho(r) (multiplied by 4*pi*r2). ! ! The units of the potential are Ryd. ! use kinds, only : DP use constants, only : fpi use funct, only : gcxc, gcx_spin, gcc_spin implicit none integer, intent(in) :: ndm,mesh,nspin,iflag real(DP), intent(in) :: r(mesh), r2(mesh), rho(ndm,2), rhoc(ndm) real(DP), intent(out):: vgc(ndm,2), egc(ndm) integer :: i, is, ierr real(DP) :: sx,sc,v1x,v2x,v1c,v2c real(DP) :: v1xup, v1xdw, v2xup, v2xdw, v1cup, v1cdw real(DP) :: segno, arho real(DP) :: rh, zeta, grh2, grho2(2) real(DP),parameter :: eps=1.e-12_dp real(DP), allocatable :: grho(:,:), h(:,:), dh(:), rhoaux(:,:) ! ! First compute the charge and the charge gradient, assumed ! to have spherical symmetry. The gradient is the derivative of ! the charge with respect to the modulus of r. ! allocate(rhoaux(mesh,2),stat=ierr) allocate(grho(mesh,2),stat=ierr) allocate(h(mesh,2),stat=ierr) allocate(dh(mesh),stat=ierr) egc=0.0_dp vgc=0.0_dp do is=1,nspin do i=1, mesh rhoaux(i,is)=(rho(i,is)+rhoc(i)/nspin)/fpi/r2(i) enddo call radial_gradient(rhoaux(1,is),grho(1,is),r,mesh,iflag) enddo if (nspin.eq.1) then ! ! GGA case ! do i=1,mesh arho=abs(rhoaux(i,1)) segno=sign(1.0_dp,rhoaux(i,1)) if (arho.gt.eps.and.abs(grho(i,1)).gt.eps) then call gcxc(arho,grho(i,1)**2,sx,sc,v1x,v2x,v1c,v2c) egc(i)=(sx+sc)*segno vgc(i,1)= v1x+v1c h(i,1) =(v2x+v2c)*grho(i,1)*r2(i) ! if (i.lt.4) write(6,'(f20.12,e20.12,2f20.12)') & ! rho(i,1), grho(i,1)**2, & ! vgc(i,1),h(i,1) else vgc(i,1)=0.0_dp egc(i)=0.0_dp h(i,1)=0.0_dp endif end do else ! ! this is the \sigma-GGA case ! do i=1,mesh ! ! NB: the special or wrong cases where one or two charges ! or gradients are zero or negative must ! be detected within the gcxc_spin routine ! ! spin-polarised case ! do is = 1, nspin grho2(is)=grho(i,is)**2 enddo call gcx_spin (rhoaux(i, 1), rhoaux(i, 2), grho2(1), grho2(2), & sx, v1xup, v1xdw, v2xup, v2xdw) rh = rhoaux(i, 1) + rhoaux(i, 2) if (rh.gt.eps) then zeta = (rhoaux (i, 1) - rhoaux (i, 2) ) / rh grh2 = (grho (i, 1) + grho (i, 2) ) **2 call gcc_spin (rh, zeta, grh2, sc, v1cup, v1cdw, v2c) else sc = 0.0_dp v1cup = 0.0_dp v1cdw = 0.0_dp v2c = 0.0_dp endif egc(i)=sx+sc vgc(i,1)= v1xup+v1cup vgc(i,2)= v1xdw+v1cdw h(i,1) =((v2xup+v2c)*grho(i,1)+v2c*grho(i,2))*r2(i) h(i,2) =((v2xdw+v2c)*grho(i,2)+v2c*grho(i,1))*r2(i) ! if (i.lt.4) write(6,'(f20.12,e20.12,2f20.12)') & ! rho(i,1)*2.0_dp, grho(i,1)**2*4.0_dp, & ! vgc(i,1), h(i,2) enddo endif ! ! We need the gradient of h to calculate the last part of the exchange ! and correlation potential. ! do is=1,nspin call radial_gradient(h(1,is),dh,r,mesh,iflag) ! ! Finally we compute the total exchange and correlation energy and ! potential. We put the original values on the charge and multiply ! by two to have as output Ry units. do i=1, mesh vgc(i,is)=vgc(i,is)-dh(i)/r2(i) vgc(i,is)=2.0_dp*vgc(i,is) if (is.eq.1) egc(i)=2.0_dp*egc(i) ! if (is.eq.1.and.i.lt.4) write(6,'(3f20.12)') & ! vgc(i,1) enddo enddo deallocate(dh) deallocate(h) deallocate(grho) deallocate(rhoaux) return end subroutine vxcgc subroutine radial_gradient(f,gf,r,mesh,iflag) ! ! This subroutine calculates the derivative with respect to r of a ! radial function defined on the mesh r. If iflag=0 it uses all mesh ! points. If iflag=1 it uses only a coarse grained mesh close to the ! origin, to avoid large errors in the derivative when the function ! is too smooth. ! use kinds, only : DP use radial_grids, only : series implicit none integer, intent(in) :: mesh, iflag real(DP), intent(in) :: f(mesh), r(mesh) real(DP), intent(out) :: gf(mesh) integer :: i,j,k,imin,npoint real(DP) :: delta, b(5), faux(6), raux(6) ! ! This formula is used in the all-electron case. ! if (iflag==0) then do i=2, mesh-1 gf(i)=( (r(i+1)-r(i))**2*(f(i-1)-f(i)) & -(r(i-1)-r(i))**2*(f(i+1)-f(i)) ) & /((r(i+1)-r(i))*(r(i-1)-r(i))*(r(i+1)-r(i-1))) enddo gf(mesh)=0.0_dp ! ! The gradient in the first point is a linear interpolation of the ! gradient at point 2 and 3. ! gf(1) = gf(2) + (gf(3)-gf(2)) * (r(1)-r(2)) / (r(3)-r(2)) return endif ! ! If the input function is slowly changing (as the pseudocharge), ! the previous formula is affected by numerical errors close to the ! origin where the r points are too close one to the other. Therefore ! we calculate the gradient on a coarser mesh. This gradient is often ! more accurate but still does not remove all instabilities observed ! with the GGA. ! At larger r the distances between points become larger than delta ! and this formula coincides with the previous one. ! (ADC 08/2007) ! delta=0.00001_dp imin=1 points: do i=2, mesh do j=i+1,mesh if (r(j)>r(i)+delta) then do k=i-1,1,-1 if (r(k)r(imin+1)+(k-1)*delta) then faux(k)=gf(i) raux(k)=r(i) j=i+1 cycle points_fit endif enddo enddo points_fit call fit_pol(raux,faux,npoint,3,b) do i=1,imin gf(i)=b(1)+r(i)*(b(2)+r(i)*(b(3)+r(i)*b(4))) enddo return end subroutine radial_gradient subroutine fit_pol(xdata,ydata,n,degree,b) ! ! This routine finds the coefficients of the least-square polynomial which ! interpolates the n input data points. ! use kinds, ONLY : DP implicit none integer, intent(in) :: n, degree real(DP), intent(in) :: xdata(n), ydata(n) real(DP), intent(out) :: b(degree+1) integer :: ipiv(degree+1), info, i, j, k real(DP) :: bmat(degree+1,degree+1), amat(degree+1,n) amat(1,:)=1.0_DP do i=2,degree+1 do j=1,n amat(i,j)=amat(i-1,j)*xdata(j) enddo enddo do i=1,degree+1 b(i)=0.0_DP do k=1,n b(i)=b(i)+ydata(k)*xdata(k)**(i-1) enddo enddo do i=1,degree+1 do j=1,degree+1 bmat(i,j)=0.0_DP do k=1,n bmat(i,j)=bmat(i,j)+amat(i,k)*amat(j,k) enddo enddo enddo ! ! This lapack routine solves the linear system that gives the ! coefficients of the interpolating polynomial. ! call DGESV(degree+1, 1, bmat, degree+1, ipiv, b, degree+1, info) if (info.ne.0) call errore('pol_fit','problems with the linear system', & abs(info)) return end subroutine fit_pol