m_bvector_20171208.f90

Go to the documentation of this file.
00001 ! m_bvector.f90 -- a simple generator of b-vectors
00002 !
00003 ! Copyright(C) 2017 Terumasa Tadano
00004 !
00005 ! This file is distributed under the terms of the MIT license.
00006 ! Please see http://opensource.org/licenses/mit-license.php for information.
00007 !
00008 module m_bvector
00009   implicit none
00010 contains
00011   !
00012   subroutine generate_bvectors(b1, b2, b3, nkb1, nkb2, nkb3, &
00013        nbmax, maxshell, verbosity, &
00014        nb, bvec_frac, bvec_cart, weight) 
00015     !
00016     ! Automatic generation of b-vectors
00017     !
00018     real(8), intent(in) :: b1(3), b2(3), b3(3)
00019     integer, intent(in) :: nkb1, nkb2, nkb3
00020     integer, intent(in) :: nbmax, maxshell
00021     integer, intent(in) :: verbosity ! expect 0 or 1
00022     integer, intent(out) :: nb
00023     real(8), intent(out) :: bvec_frac(3,nbmax), bvec_cart(3,nbmax)
00024     real(8), intent(out) :: weight(nbmax)
00025     ! local variables
00026     integer :: i, j, k, m, l
00027     integer :: nk, nbvec
00028     integer :: nmulti, nshell
00029     integer :: nbvec_now, nbvec_shell
00030     integer :: nrank, nmin, shift
00031     integer, allocatable :: multiplicity(:)
00032     integer, allocatable :: index_sort(:), index_k(:)
00033     integer, allocatable :: index_k_shell(:), index_k_tmp(:)
00034     logical :: is_new, is_fullrank, satisfy_orthogonal, success
00035     real(8), parameter :: tolerance = 1.0d-8
00036     real(8) :: kval(3), dist_tmp, sigma_tmp(6), bvec(3)
00037     real(8), parameter :: sigma_vec(6) = (/1.d0, 0.d0, 0.d0, 1.d0, 0.d0, 1.d0/)
00038     real(8), allocatable :: xk(:,:), xk_cart(:,:), dist_k(:)
00039     real(8), allocatable :: bvec_merged(:,:), bvec_shell(:,:)
00040     real(8), allocatable :: bvec_tmp(:,:), bvec_now(:,:)
00041     real(8), allocatable :: bbmat(:,:), weight_tmp(:)
00042     real(8), allocatable :: Umat(:,:), Sval(:), VTmat(:,:), Sinv(:,:)
00043     !
00044     if (verbosity > 0) then
00045        write(6,*) ' '
00046        write(6,*) '============================================='
00047        write(6,*) '     Auatomatic generation of b-vectors      '
00048        write(6,*) '============================================='
00049        write(6,*) ' '
00050     endif
00051     !
00052     bvec_frac = 0.0d0
00053     bvec_cart = 0.0d0
00054     weight = 0.0d0
00055     nb = 0
00056     !
00057     ! Generate gamma-centered mesh points in fractional coordinate 
00058     ! in the range of [-0.5:0.5)
00059     !
00060     nk = nkb1 * nkb2 * nkb3 + 26
00061     allocate(xk(3, nk))
00062     m = 0
00063     do i = 1, nkb1
00064        kval(1) = dble(i-1) / dble(nkb1)
00065        if (kval(1) >= 0.5d0) then
00066           kval(1) = kval(1) - 1.0d0
00067        endif
00068        do j = 1, nkb2
00069           kval(2) = dble(j-1) / dble(nkb2)
00070           if (kval(2) >= 0.5d0) then
00071              kval(2) = kval(2) - 1.0d0
00072           endif
00073           do k = 1, nkb3
00074              kval(3) = dble(k-1) / dble(nkb3)
00075              if (kval(3) >= 0.5d0) then
00076                 kval(3) = kval(3) - 1.0d0
00077              endif
00078              m = m + 1
00079              xk(1:3, m) = kval(1:3)
00080           enddo
00081        enddo
00082     enddo
00083     !
00084     ! Also add nearest G-vectors in each direction
00085     !
00086     do i = -1, 1
00087        do j = -1, 1
00088           do k = -1, 1
00089              if (i == 0 .and. j == 0 .and. k == 0) then 
00090                 cycle
00091              endif
00092              m = m + 1
00093              xk(1:3, m) = (/dble(i), dble(j), dble(k)/) 
00094           enddo
00095        enddo
00096     enddo
00097     !
00098     ! Convert mesh points in Cartesian coordinate and
00099     ! calculate distance from gamma point
00100     !
00101     allocate(xk_cart(3, nk), dist_k(nk))
00102     allocate(index_sort(nk))
00103     do i = 1, nk
00104        xk_cart(:,i) = xk(1,i)*b1(:) + xk(2,i)*b2(:) + xk(3,i)*b3(:)
00105        dist_k(i) = sqrt(dot_product(xk_cart(:,i), xk_cart(:,i)))
00106        index_sort(i) = i
00107     enddo
00108     !
00109     ! Sort distance in ascending order (dist_k(1) <= dist_k(2) <= ...)
00110     ! 
00111     call quicksort_double_arg(nk, dist_k, index_sort, 1, nk)
00112     !
00113     ! Construct information about nearest-neighbor shells.
00114     ! multiplicity(i) represents the number k points in the (i-1)th shelll.
00115     !
00116     allocate(multiplicity(maxshell+1))
00117     nmulti = 0
00118     dist_tmp = 0.0d0
00119     multiplicity = 0
00120     m = 1 
00121     do i = 1, nk
00122        if (abs(dist_k(i) - dist_tmp) < tolerance) then
00123           nmulti = nmulti + 1
00124        else
00125           dist_tmp = dist_k(i)
00126           multiplicity(m) = nmulti
00127           ! Reset value for the next shell
00128           nmulti = 1
00129           m = m + 1
00130           if (m > maxshell + 1) then
00131              exit
00132           endif
00133        endif
00134     enddo
00135     !
00136     nshell = 0
00137     do i = 1, maxshell + 1
00138        if (multiplicity(i) > 0) then
00139           nshell = nshell + 1
00140        endif
00141     enddo
00142     !
00143     if (verbosity > 0) then
00144        write(6,*) "  Shell        Distance        Multiplicity"
00145        write(6,*) " -------      ----------       ------------"
00146        m = 1
00147        do i = 1, nshell
00148           dist_tmp = dist_k(m)
00149           ! skip 0th shell (distance = 0.0)
00150           if (dist_tmp > tolerance) then
00151              write(6,'(i5,F20.8,i18)') i - 1, dist_tmp, multiplicity(i) 
00152           endif
00153           m = m + multiplicity(i)
00154        enddo
00155        write(6,*)
00156     endif
00157     deallocate(dist_k)
00158     !
00159     ! index_k: contains k point index of b-vectors 
00160     ! bvec_now: contains Cartesian coordinates of b-vectors
00161     !
00162     allocate(index_k(nk))
00163     allocate(bvec_now(3,nk))
00164     nbvec_now = 0
00165     index_k = 0
00166     bvec_now = 0.0d0
00167     !
00168     ! Main loop over the shell index
00169     !
00170     do i = 2, nshell 
00171        !
00172        nmulti = multiplicity(i)
00173        !
00174        allocate(bvec_shell(3, nmulti)) 
00175        allocate(bvec_tmp(3, nmulti))
00176        allocate(index_k_shell(nmulti))
00177        allocate(index_k_tmp(nmulti))
00178        bvec_shell = 0.0d0
00179        index_k_shell = 0
00180        !
00181        shift = sum(multiplicity(1:i-1))
00182        index_k_tmp(:) = index_sort(shift+1:shift+nmulti)
00183        bvec_tmp(:,1:nmulti) = xk_cart(:,index_k_tmp(1:nmulti))
00184        !
00185        ! Search unique set of b-vectors in this shell and
00186        ! save them in bvec_shell.
00187        !
00188        m = 0
00189        do j = 1, nmulti
00190           !
00191           is_new = .true.
00192           !
00193           bvec(:) = -bvec_tmp(:,j)
00194           do k = 1, m
00195              if (sqrt(dot_product(bvec_shell(:,k)-bvec(:), &
00196                   bvec_shell(:,k)-bvec(:))) < tolerance) then
00197                 is_new = .false.
00198                 exit
00199              endif
00200           enddo
00201           !
00202           if (is_new) then
00203              m = m + 1
00204              bvec_shell(:,m) = bvec_tmp(:,j)
00205              index_k_shell(m) = index_k_tmp(j)
00206           endif
00207        enddo
00208        nbvec_shell = m
00209        nbvec = nbvec_now + nbvec_shell
00210        deallocate(index_k_tmp, bvec_tmp)
00211        !
00212        ! Merge b-vectors for making the b*b matrix
00213        !
00214        allocate(bvec_merged(3, nbvec))
00215        bvec_merged = 0.0d0
00216        bvec_merged(:,1:nbvec_now) = bvec_now(:,1:nbvec_now)
00217        bvec_merged(:,nbvec_now+1:nbvec) = bvec_shell(:,1:nbvec_shell)
00218        !
00219        if (verbosity > 0) then
00220           write(6,*)
00221           write(6,*) " ------------------------------------------------ "
00222           write(6,'(2x,a,i4)') " New shell index : ", i - 1
00223           write(6,*)
00224           write(6,'(2x,a,i4)') " Number of candidate b-vectors : ", nbvec
00225           write(6,*) "  List of b-vectors (Cartesian) :"
00226           do j = 1, nbvec
00227              write(6,'(3F15.8)') bvec_merged(:,j) 
00228           enddo
00229        endif
00230        !
00231        ! Generate b*b matrix
00232        !
00233        allocate(bbmat(6, nbvec))
00234        allocate(weight_tmp(nbvec))
00235        !
00236        m = 0
00237        do j = 1, 3
00238           do k = j, 3
00239              m = m + 1
00240              do l = 1, nbvec
00241                 bbmat(m, l) = bvec_merged(j,l) * bvec_merged(k,l)
00242              enddo
00243           enddo
00244        enddo
00245        !
00246        nmin = min(6, nbvec)
00247        allocate(Sval(nmin), Umat(6, 6), VTmat(nbvec, nbvec))
00248        !
00249        call SVD(6, nbvec, nmin, bbmat, nrank, Sval, Umat, VTmat)
00250        !
00251        is_fullrank = (nrank == nmin)
00252        !
00253        if (is_fullrank) then
00254           !
00255           ! Accept the current shell and update bvec_now and index_k.
00256           !
00257           bvec_now(:,nbvec_now+1:nbvec) = bvec_shell(:,1:nbvec_shell)
00258           index_k(nbvec_now+1:nbvec) = index_k_shell(1:nbvec_shell)
00259           nbvec_now = nbvec
00260           !
00261           allocate(Sinv(6, nbvec))
00262           Sinv = 0.0d0
00263           do j = 1, nmin
00264              Sinv(j,j) = 1.0d0 / Sval(j)
00265           enddo
00266           !
00267           weight_tmp = matmul(transpose(matmul(matmul(Umat, Sinv), VTmat)), sigma_vec)
00268           !
00269           deallocate(Sinv) 
00270           !
00271           if (verbosity > 0) then
00272              write(6,*)
00273              write(6,*) "  Matrix is full-rank. This shell is"
00274              write(6,*) "  linearly independent on the existing shells."
00275              write(*,*) "  Weights of each b-vectors below:"
00276              do j = 1, nbvec
00277                 write(6,'(3x, F15.8)') weight_tmp(j)*0.5d0
00278              enddo
00279           endif
00280           ! 
00281           ! Check if the b-vectors satisfy the orthogonality relation
00282           !
00283           m = 0
00284           do j = 1, 3
00285              do k = j, 3
00286                 m = m + 1
00287                 sigma_tmp(m) = 0.0d0
00288                 do l = 1, nbvec
00289                    sigma_tmp(m) = sigma_tmp(m) &
00290                         + weight_tmp(l)*bvec_merged(j,l)*bvec_merged(k,l)
00291                 enddo
00292              enddo
00293           enddo
00294           !
00295           sigma_tmp = sigma_tmp - sigma_vec
00296           if (sqrt(dot_product(sigma_tmp,sigma_tmp)) < tolerance) then
00297              satisfy_orthogonal = .true.
00298           else
00299              satisfy_orthogonal = .false.
00300           endif
00301        else
00302           if (verbosity > 0) then
00303              write(6,*)
00304              write(6,*) "  Matrix is rank-deficient. This shell is"
00305              write(6,*) "  linearly dependent on the existing shells."
00306              write(6,*) "  Skipping this shell..."
00307           endif
00308        endif
00309        !
00310        deallocate(Sval, Umat, VTmat)
00311        deallocate(bbmat)
00312        deallocate(bvec_shell,bvec_merged)
00313        deallocate(index_k_shell)
00314        !  
00315        success = .false.
00316        if (is_fullrank) then
00317           if (satisfy_orthogonal) then
00318              !
00319              success = .true.
00320              !
00321              if (verbosity > 0) then
00322                 write(6,*)
00323                 write(6,*) "  Weighted orthogonality is satisfied."
00324              endif
00325              !
00326              ! Store data in the return arrays
00327              !
00328              nb = 2 * nbvec
00329              do j = 1, nbvec
00330                 bvec_frac(:, 2*(j-1) + 1) =  xk(:, index_k(j))
00331                 bvec_frac(:, 2*j        ) = -xk(:, index_k(j))
00332                 bvec_cart(:, 2*(j-1) + 1) =  xk_cart(:, index_k(j))
00333                 bvec_cart(:, 2*j        ) = -xk_cart(:, index_k(j))
00334                 weight(2*(j-1) + 1) = weight_tmp(j)*0.5d0
00335                 weight(2*j        ) = weight_tmp(j)*0.5d0
00336              enddo
00337           else
00338              if (verbosity > 0) then
00339                 write(6,*)
00340                 write(6,*) "  Weighted orthogonality is NOT satisfied."
00341              endif
00342           endif
00343        endif
00344        !
00345        deallocate(weight_tmp)
00346        !
00347        if (success) exit
00348     enddo
00349     !
00350     deallocate(xk, xk_cart, index_k, bvec_now)
00351     !
00352     if (success) then
00353        if (verbosity > 0) then
00354           write(6,*)
00355           write(6,*) " b-vectors are created successfully."
00356           write(6,*) ' '
00357           write(6,*) '============================================='
00358           write(6,*) '        Finish automatic generation          '
00359           write(6,*) '============================================='
00360           write(6,*) ' '
00361        endif
00362     else
00363        stop ' Could not find b-vectors'
00364     endif
00365     !
00366     return
00367   end subroutine generate_bvectors
00368   !
00369   !
00370   subroutine SVD(m, n, k, A, nrank, S, U, VT)
00371     !
00372     ! Singular value decomposition of matrix A.
00373     ! 
00374     implicit none
00375     integer, intent(in) :: m, n, k
00376     real(8), intent(in) :: A(m, n)
00377     integer, intent(out) :: nrank
00378     real(8), intent(out) :: S(k), U(m, m), VT(n, n)
00379     ! local variable
00380     integer :: i
00381     integer :: lwork, info
00382     real(8), allocatable :: work(:)
00383     real(8), allocatable :: Acopy(:,:)
00384     !
00385     lwork = max(3*k+max(m,n), 5*k) 
00386     lwork = 2 * lwork
00387     !
00388     allocate(Acopy(m, n), work(lwork))
00389     Acopy = A
00390     !
00391     call dgesvd('A', 'A', m, n, Acopy, m, S, U, m, &
00392          VT, n, work, lwork, info)
00393     !
00394     nrank = 0
00395     do i = 1, k
00396        if (S(1) > 1.0d-12) then
00397           if (S(i) > S(1)*1.0d-8) then
00398              nrank = nrank + 1
00399           endif
00400        endif
00401     enddo
00402     !
00403     deallocate(Acopy, work)
00404     !
00405     return
00406   end subroutine SVD
00407   ! 
00408   recursive subroutine quicksort_double_arg(n, array, ind, first, last)
00409     !
00410     ! Quick sort with index (similar to argsort in python)
00411     !
00412     implicit none
00413     integer, intent(in) :: n, first, last
00414     integer, intent(inout) :: ind(n)
00415     real(8), intent(inout) :: array(n)
00416     integer :: i, j, ind_tmp
00417     real(8) :: tmp, pivot
00418     pivot= array((first+last)/2)
00419     i= first
00420     j= last
00421     do
00422        do while (array(i) < pivot)
00423           i = i + 1
00424        end do
00425        do while (pivot < array(j))
00426           j = j - 1
00427        end do
00428        if (i >= j) exit
00429        ! 
00430        tmp = array(i)
00431        array(i) = array(j)
00432        array(j) = tmp
00433        ind_tmp = ind(i)
00434        ind(i) = ind(j)
00435        ind(j) = ind_tmp
00436        ! 
00437        i= i + 1
00438        j= j - 1
00439     end do
00440     if (first < i - 1) call quicksort_double_arg(n, array, ind, first, i - 1)
00441     if (j + 1 < last)  call quicksort_double_arg(n, array, ind, j + 1, last)
00442   end subroutine quicksort_double_arg
00443   !
00444 end module m_bvector

Generated on 17 Nov 2020 for respack by  doxygen 1.6.1