Sophie

Sophie

distrib > Fedora > 18 > i386 > by-pkgid > 9fa01ead33f6cb147c452ccad90c9326 > files > 212

wannier90-2.0.0-1.fc18.i686.rpm

!-*- mode: F90; mode: font-lock; column-number-mode: true -*-!
!                                                            !
! Copyright (C) 2012 Lampros Andrinopoulos, Nicholas Hine,   !
!                    and Arash A Mostofi                     !
!                                                            !
! In publications arising from use of this code, please cite !
!                                                            !
!     L Andrinopoulos, NDM Hine and AA Mostofi,              !
!                  J. Chem. Phys. 135, 154105 (2011)         !
!                                                            !
! The methodology coded here is based on the original work   !
! of PL Silvestrelli, Phys Rev Lett 100, 053002 (2008)       !
!                                                            !
! 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 .                      !
!                                                            !
!------------------------------------------------------------!
! Code to calculate vdW energies from MLWFs                  !
!------------------------------------------------------------!
program vdw

  implicit none

  integer, parameter :: dp=kind(1.0d0)
  integer, parameter :: k=1500         ! for integration scheme
  real(dp) :: fnl,fnl2,fnl4,c6nl,evdw,rnl,f,c6_eff,shift,&
       rc_coeff, degeneracy,tol_dist,tol_occ
  integer :: n,l,num_frag,iwann,iwann2,ifrag,ifrag2
  real(dp), allocatable :: r(:,:,:),rc(:,:),s(:,:),rvdw(:,:),fw(:,:),&
       num_proxim(:,:),distance(:,:,:)
!!  real(dp), allocatable :: s_amalg(:,:),r_amalg(:,:)
  real(dp), parameter :: pi=3.141592653589793238462643383279d0
  real(dp), parameter :: ang2a0=0.52917720859d0 ! NIST CODATA 2006 value
!!  real(dp), parameter :: ang2a0=0.5291772108d0 ! old W90 value
  character(len=30) :: filename, units, dummy
  integer, allocatable :: num_wann(:),num_wann_new(:),num_orb(:)
  integer :: num_wann_old,num_wann_tot
  integer :: max_num_wann
  logical, allocatable :: p(:,:)
  logical :: amalgamate, disentangle
  real(kind=DP) :: rnp
  integer :: iostat

  call get_command_argument(1,filename)
  
  open(unit=1,file=filename,action='read',iostat=iostat)
  if (iostat /= 0) then
     write(0,'(a)') "Error while reading file '" // trim(filename) // "'."
     write(0,'(a)') "Pass the correct file name on the command line!"
     stop 1
  end if
  open(unit=2,file=adjustl(trim(filename))//".out",action='write')
  write(2,'(a/)') ' Running w90_vdw. Written by L Andrinopoulos et al. (c) 2012.'
  write(2,'(a/)') ' L Andrinopoulos, NDM Hine and AA Mostofi, J Chem Phys 135, 154105 (2011).'
  write(2,'(a,a,a/)') ' Reading input file ', adjustl(trim(filename)), '...'

  
  ! read input parameters
  read(1,*) dummy, disentangle ! whether MLWFs are disentangled
  write(2,'(a,l2)') ' disentangle: ', disentangle
  read(1,*) dummy, amalgamate  ! whether MLWFs need to be amalgamated
  write(2,'(a,l2)') ' amalgamate : ', amalgamate
  read(1,*) dummy, degeneracy  ! number of electrons per MLWF
  write(2,'(a,f5.3)') ' degeneracy : ', degeneracy
  read(1,'(9x,i3)') num_frag   ! number of distinct molecular fragments
  write(2,'(a,i3)') ' num_frag   : ', num_frag
  allocate(num_wann(num_frag),num_wann_new(num_frag))
  allocate(p(num_frag,3),num_orb(num_frag))
  read(1,'(12x)')
  read(1,*) num_wann(1:num_frag) ! number of MLWFs per fragment
  write(2,'(a)',advance='no') ' num_wann   : '
  do n=1,num_frag
     write(2,'(i4,1x)',advance='no') num_wann(n)
  enddo
  read(1,*) dummy, tol_occ ! tolerance for splitting p-orbitals
  write(2,'(/a,f6.3)') ' tol_occ    : ', tol_occ
  if (.not.disentangle) write(2,'(a)') ' [disentanglement not used; ignoring tol_occ]'
  read(1,'(12x)')
  do ifrag=1,num_frag
     read(1,'(l1,1x,l1,1x,l1)') p(ifrag,1:3) ! directions in which to split p-orbitals
  enddo
  do ifrag=1,num_frag
     write(2,'(a,i2,1x,l2,1x,l2,1x,l2)') ' pxyz       : ',ifrag,(p(ifrag,n),n=1,3)
  enddo
  if (.not.disentangle) write(2,'(a)') ' [disentanglement not used; ignoring pxyz]'
  ! tolerance for amalgamating cocentric MLWFs
  ! not used unless amalgamate = T
  read(1,*) dummy, tol_dist
  write(2,'(a,f6.3)') ' tol_dist   : ', tol_dist
  if (.not.amalgamate) write(2,'(a)') ' [amalgamate not used; ignoring tol_dist]'


  max_num_wann = maxval(num_wann(:))
  allocate (r(num_frag,3,2*max_num_wann), &
       rc(num_frag,2*max_num_wann), &
       s(num_frag,2*max_num_wann), &
       rvdw(num_frag,2*max_num_wann), &
       fw(num_frag,2*max_num_wann), &
       distance(num_frag,max_num_wann,max_num_wann), &
       num_proxim(num_frag,max_num_wann))
  
  read(1,'(19x)') 
  read(1,*) units ! units in which centres and spreads are given
  if (adjustl(trim(units))=='ang') then
     write(2,'(a,a)') ' units      :  ',adjustl(trim(units))
  else
     write(2,'(a)') ' units      :   bohr (assumed)'
  endif
  do ifrag=1,num_frag
     do iwann=1,num_wann(ifrag)
        read(1,*) r(ifrag,1:3,iwann),s(ifrag,iwann),fw(ifrag,iwann)
     enddo
  enddo
  close (1)
  ! end read input parameters

  ! set electronic occupancy of each MLWF
  fw(:,:)=degeneracy*fw(:,:)
  ! convert spread^2 to spread
  s(:,:)=sqrt(s(:,:))

  ! aam: convert centres and spreads to bohr if necessary 
  !      (code works in atomic units)
  if (adjustl(trim(units))=='ang') then
     r(:,:,:)=r(:,:,:)/ang2a0
     s(:,:)=s(:,:)/ang2a0
  endif
  
  write(2,'(a)') ' centres, sqrt(quadratic spreads) [in Ang], and electronic occupancies:'
  do ifrag=1,num_frag
     do iwann=1,num_wann(ifrag)
        write(2,'(i3,1x,i3,1x,3f13.8,f12.8,f12.8)') &
             ifrag,iwann,r(ifrag,1:3,iwann)*ang2a0,s(ifrag,iwann)*ang2a0,fw(ifrag,iwann)
     enddo
  enddo

  write(2,'(/a)') ' done reading input file'

  if(amalgamate) then
     
     write(2,'(/a)',advance='no') ' amalgamating MLWF centres...'

     num_proxim(:,:)=0
     do ifrag=1,num_frag
        
        do
           num_wann_old = num_wann(ifrag)
           
           iwannloop: do iwann=1,num_wann_old
              do iwann2=iwann+1,num_wann_old
                 
                 distance(ifrag,iwann,iwann2) = sqrt( &
                      (r(ifrag,1,iwann)-r(ifrag,1,iwann2))**(2.0d0) + &
                      (r(ifrag,2,iwann)-r(ifrag,2,iwann2))**(2.0d0) + &
                      (r(ifrag,3,iwann)-r(ifrag,3,iwann2))**(2.0d0) )
                                  
                 if(distance(ifrag,iwann,iwann2)<=tol_dist) then
                    num_proxim(ifrag,iwann) = num_proxim(ifrag,iwann) + 1
                    num_wann(ifrag) = num_wann(ifrag) - 1
                    rnp = real(num_proxim(ifrag,iwann),DP)
                    r(ifrag,1:3,iwann) = r(ifrag,1:3,iwann) * rnp/(rnp+1.0_DP) + r(ifrag,1:3,iwann2)/(rnp+1.0_DP)
                    r(ifrag,:,iwann2:num_wann(ifrag)) = r(ifrag,:,iwann2+1:num_wann(ifrag)+1)
                    s(ifrag,iwann) = s(ifrag,iwann) * rnp/(rnp+1.0_DP) + s(ifrag,iwann2)/(rnp+1.0_DP)
                    s(ifrag,iwann2:num_wann(ifrag)) = s(ifrag,iwann2+1:num_wann(ifrag)+1)
                    fw(ifrag,iwann) = fw(ifrag,iwann) + fw(ifrag,iwann2)
                    fw(ifrag,iwann2:num_wann(ifrag)) = fw(ifrag,iwann2+1:num_wann(ifrag)+1)
                    exit iwannloop
                 endif
              enddo
           enddo iwannloop
           if (num_wann(ifrag)==num_wann_old) exit
        enddo
        
     enddo

     write(2,'(a)') ' ... done'
     
  endif

  num_orb(:)=0
  do ifrag=1,num_frag
     do l=1,3
        if (p(ifrag,l)) then
           do iwann=1,num_wann(ifrag)
              if(fw(ifrag,iwann)<=2.0d0*tol_occ) then
                 num_orb(ifrag)=num_orb(ifrag)+1 
                 s(ifrag,iwann)=7.0d0*sqrt(2.0d0)/16.0d0*s(ifrag,iwann)
                 shift=sqrt(15.0d0)/7.0d0*s(ifrag,iwann)
                 r(ifrag,l,iwann)=r(ifrag,l,iwann)+shift
                 s(ifrag,num_wann(ifrag)+num_orb(ifrag))=s(ifrag,iwann)
                 r(ifrag,1:3,num_wann(ifrag)+num_orb(ifrag))=r(ifrag,1:3,iwann)
                 r(ifrag,l,num_wann(ifrag)+num_orb(ifrag))=r(ifrag,l,iwann)-2.0d0*shift
                 fw(ifrag,iwann)=0.5d0*fw(ifrag,iwann)
                 fw(ifrag,num_wann(ifrag)+num_orb(ifrag))=fw(ifrag,iwann)
              endif
           enddo
        endif
     enddo
     num_wann(ifrag)=num_wann(ifrag)+num_orb(ifrag)
  enddo

  num_wann_tot = 0
  do ifrag=1,num_frag
     num_wann_tot = num_wann_tot + num_wann(ifrag)
  enddo
  
  !Cutoff radius
  rc_coeff = 1.0_dp
  rc(:,:)=rc_coeff*s(:,:)*sqrt(3.0d0)*(0.769d0+0.5d0*log(s(:,:)))
  
!VdW radii obtained by 0.01 electron density contour; 
!if (radcut=='vdw') then
!rvdw(:,:)=rc_coeff*s(:,:)*(1.475d0-0.866d0*log(s(:,:)))
!else
  rvdw(:,:)=rc(:,:)
!endif

  evdw=0.0_dp
  c6_eff = 0.0_dp

  write(2,'(/a)') ' calculating E_vdW and C6_effective ...'

  write(2,'(/a)') ' centres, sqrt(quadratic spreads) [in Ang], and electronic occupancies'
  write(2,'(a)')  ' after p-splitting (if any) and amalgamation of co-centric MLWFs (if any):'

  do ifrag=1,num_frag
     do iwann=1,num_wann(ifrag)
        ! write centres, spreads and occupancies used in calculating E_vdW
        write(2,'(i3,1x,i3,1x,3f13.8,f12.8,f12.8)') &
             ifrag,iwann,r(ifrag,1:3,iwann)*ang2a0,s(ifrag,iwann)*ang2a0,fw(ifrag,iwann)

        do ifrag2=ifrag+1,num_frag
           do iwann2=1,num_wann(ifrag2) !No double counting, no self-interacting terms
              
              ! |rn-rl|=rnl
              rnl = sqrt( (r(ifrag,1,iwann)-r(ifrag2,1,iwann2))**(2.0d0) + &
                   (r(ifrag,2,iwann)-r(ifrag2,2,iwann2))**(2.0d0) + &
                   (r(ifrag,3,iwann)-r(ifrag2,3,iwann2))**(2.0d0) )

              !Integration scheme (Romberg) 
              call c(fnl,k,s(ifrag,iwann),s(ifrag2,iwann2),fw(ifrag,iwann),fw(ifrag2,iwann2),rc_coeff)
              call c(fnl2,2*k,s(ifrag,iwann),s(ifrag2,iwann2),fw(ifrag,iwann),fw(ifrag2,iwann2),rc_coeff)
              call c(fnl4,4*k,s(ifrag,iwann),s(ifrag2,iwann2),fw(ifrag,iwann),fw(ifrag2,iwann2),rc_coeff)
              f=1.0d0/45.0d0*(64.0d0*fnl4-20.0d0*fnl2+fnl)
            
              !C6 Coefficients and VdW Energy calculation
              c6nl=(s(ifrag,iwann)**(1.5d0))*(s(ifrag2,iwann2)**(3.0d0))*0.5d0*(3.0d0**(-1.25d0))*f
              c6_eff = c6_eff + c6nl
              evdw = evdw - 1.0d0/(1.0d0+exp(-20.0d0*(rnl/(rvdw(ifrag,iwann)+rvdw(ifrag2,iwann2))-1))) &
                   * c6nl/(rnl**(6.0d0))

           enddo
        enddo
     enddo
  enddo

!  close(3)
  write(2,'(/a)') repeat('=',50)
  write(2,'(a,f16.8,a)') ' vdW energy = ',evdw,   ' Ha'
  write(2,'(a,f16.8,a)') ' C6_eff     = ',c6_eff, ' Ha*Bohr^6'
  write(2,'(a)') repeat('=',50)

  write(2,'(/a)') ' Have a nice day.'

close(2)

contains

  !Integration subroutine
  subroutine c(s,n,sn,sl,fn,fl,rcoeff)

    implicit none
    integer, intent(in) :: n
    real(dp) :: x,y,hx,hy,xc,yc,beta,integral,f
    integer :: i,j
    real(dp),allocatable :: a(:),b(:)
    real(dp), intent(in) :: sn,sl,fn,fl,rcoeff
    real(dp), intent(out) :: s
    
    
    beta=(sn/sl)**(1.5d0)
    
    !Integration boundaries
    xc=3.0d0*rcoeff*(0.769d0+0.5d0*log(sn))
    yc=3.0d0*rcoeff*(0.769d0+0.5d0*log(sl))
    
    allocate (a(n),b(n))
    
    !Sum coefficients
    a(:)=1.0d0
    b(:)=1.0d0
    a(1)=13.0d0/12d0
    b(1)=a(1)
    a(n-1)=a(1)
    b(n-1)=a(1)
    a(n)=5.0d0/12d0
    b(n)=a(n)
    
    hx=xc/n
    hy=yc/n
    
    integral=0
    
    do i=1,n
       x=i*hx
       do j=1,n
          y=j*hy
          f=(x*x*y*y*exp(-x)*exp(-y))/(exp(-x)/(beta*sqrt(fl))+exp(-y)/sqrt(fn))
          integral=integral+a(i)*b(j)*hx*hy*f
       end do
    end do
    
    s=integral
    deallocate (a,b)
  end subroutine c
  
end program vdw