LCOV - code coverage report
Current view: top level - src/71_wannier - m_scdm_math.F90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 62.1 % 116 72
Test Date: 2026-09-19 17:42:43 Functions: 54.5 % 11 6

            Line data    Source code
       1              : !!****m*ABINIT/m_scdm_math
       2              : !! NAME
       3              : !!  m_scdm_math
       4              : !!
       5              : !! FUNCTION
       6              : !!  Math functions used by SCDM
       7              : !! (select columns of density matrix method) for getting wannier functions.
       8              : !!
       9              : !! COPYRIGHT
      10              : !!  Copyright (C) 2005-2026 ABINIT group (hexu)
      11              : !!  This file is distributed under the terms of the
      12              : !!  GNU General Public License, see ~abinit/COPYING
      13              : !!  or http://www.gnu.org/copyleft/gpl.txt .
      14              : !!
      15              : !! SOURCE
      16              : 
      17              : #if defined HAVE_CONFIG_H
      18              : #include "config.h"
      19              : #endif
      20              : 
      21              : 
      22              : 
      23              : #include "abi_common.h"
      24              : 
      25              : module m_scdm_math
      26              :   use defs_basis
      27              :   use m_abicore
      28              :   use m_errors
      29              :   implicit none
      30              :   real(dp), parameter, public:: tpi = 2*PI
      31              :   complex(dp), parameter, public:: tpi_im = cmplx(0.0_dp, tpi, kind = dp)
      32              : 
      33              : 
      34              : ! wrapper for LAPACK routine ZGEEV
      35              :   type, public:: eigensolver
      36              :      integer:: ndim = -1, lwork = 0
      37              :      complex(dp), allocatable  :: work(:)
      38              :      real(dp), allocatable:: rwork(:)
      39              :    contains
      40              :      procedure:: run => eigensolver_run
      41              :      procedure:: finalize => eigensolver_finalize
      42              :   end type eigensolver
      43              : 
      44              : 
      45              :   public:: complex_QRCP_piv_only
      46              :   public:: real_svd
      47              :   public:: complex_svd
      48              :   public:: gaussian
      49              :   public:: fermi
      50              :   public:: insertion_sort_double
      51              :   public:: build_Rgrid
      52              :   private
      53              : 
      54              : contains
      55              : 
      56              :   !
      57            2 :   subroutine complex_QRCP_Piv_only(A, Piv)
      58              :     complex(dp), intent(in):: A(:, :)
      59              :     integer, intent(inout):: Piv(:)
      60            4 :     real(dp):: rwork(size(A, 2)*2)
      61            4 :     complex(dp):: tau(min(size(A, 1), size(A, 2)))
      62              :     integer:: m, n
      63            2 :     complex(dp), allocatable:: work(:)
      64              :     complex(dp):: tmp_work(2)
      65              :     integer:: lwork
      66              :     integer:: info
      67              :     EXTERNAL ZGEQP3
      68              : 
      69            2 :     m = size(A, 1)
      70            2 :     n = size(A, 2)
      71           62 :     rwork(:)=0.0_dp
      72            2 :     call ZGEQP3(m, n, A, m, piv, tau, tmp_work, -1, rwork, info)
      73            2 :     if(info /= 0) then
      74            0 :        ABI_ERROR("Error in doing QRCP")
      75              :     endif
      76           32 :     Piv(:) = 0
      77           62 :     rwork(:) = 0.0_DP
      78            2 :     lwork = INT(AINT(REAL(tmp_work(1))))
      79              : 
      80            6 :     ABI_MALLOC(work, (lwork))
      81         1026 :     work(:) = (0.0_DP, 0.0_DP)
      82              : 
      83            2 :     CALL ZGEQP3(m, n, A, m, piv, tau, work, lwork, rwork, info)
      84            2 :     if(info /= 0) then
      85            0 :        ABI_ERROR("Error in doing QRCP")
      86              :     endif
      87            2 :     ABI_SFREE(work)
      88            2 :   end subroutine complex_QRCP_Piv_only
      89              : 
      90              : 
      91              : 
      92              :  ! wrapper for LAPACK routine DGESVD
      93              :  ! A = U*S*VT
      94              :  ! Note: A is overwritten by U
      95              :  ! S is the singular values
      96              :  ! VT is the transpose of V
      97            0 :   subroutine real_svd(A,  U, S, VT)
      98              :     real(dp), intent(in):: A(:, :)
      99              :     real(dp), intent(inout):: U(:, :), S(:), VT(:,:)
     100              :     integer:: LWMAX
     101              :     real(dp):: tmp(5)
     102            0 :     real(dp), allocatable::  WORK(:)
     103              :     integer  :: M, N
     104              :     integer::         LDA, LDU, LDVT
     105              :     integer::         INFO, LWORK
     106              :     EXTERNAL         DGESVD
     107            0 :     M = size(A, 1)
     108            0 :     N = size(A, 2)
     109            0 :     LDA = M
     110            0 :     LDU = M
     111            0 :     LDVT = N
     112            0 :     LWORK = -1
     113            0 :     LWMAX = max(size(A, 1), size(A, 2))*10
     114              :     CALL DGESVD( 'All', 'All', M, N, A, LDA, S, U, LDU, VT, LDVT, &
     115            0 :          & tmp, LWORK, INFO )
     116            0 :     LWORK = MIN( LWMAX, INT( tmp( 1 ) ) )
     117              : 
     118            0 :     ABI_MALLOC(work, (lwork))
     119              :     CALL DGESVD( 'All', 'All', M, N, A, LDA, S, U, LDU, VT, LDVT, &
     120            0 :          WORK, LWORK, INFO )
     121            0 :     IF( INFO .GT. 0 ) THEN
     122            0 :        ABI_ERROR('The algorithm computing SVD failed to converge.')
     123            0 :        STOP
     124              :     END IF
     125            0 :     ABI_SFREE(work)
     126            0 :   end subroutine real_svd
     127              : 
     128              : ! wrapper for LAPACK routine ZGESVD
     129              : ! A = U*S*VT
     130              : ! Note: A is overwritten by U
     131              : ! S is the singular values
     132              : ! VT is the transpose of V
     133              : ! mode: A, or S, or N or O, see
     134              : ! https://netlib.org/lapack/explore-html/d3/da8/group__complex16_g_esing_gad6f0c85f3cca2968e1ef901d2b6014ee.html
     135          192 :   subroutine complex_svd(A,  U, S, VT, mode)
     136              :     complex(dp), intent(in):: A(:, :)
     137              :     complex(dp), intent(inout):: U(:, :),  VT(:,:)
     138              :     real(dp), intent(inout):: S(:)
     139              :     character, intent(in):: mode  ! A, or S, or N/O
     140              :     integer:: LWMAX
     141              :     complex(dp):: tmp(2)
     142          192 :     complex(dp), allocatable::  WORK(:)
     143          192 :     real(dp), allocatable:: rwork(:)
     144              :     integer  :: M, N
     145              :     integer::         LDA, LDU, LDVT
     146              :     integer::         INFO, LWORK
     147              :     EXTERNAL         ZGESVD
     148              : 
     149          192 :     M = size(A, 1)
     150          192 :     N = size(A, 2)
     151          192 :     LWMAX = max(size(A, 1), size(A, 2))*10
     152          192 :     LDA = M
     153          192 :     LDU = M
     154          192 :     LDVT = N
     155          192 :     LWORK = -1
     156          576 :     ABI_MALLOC(rwork, (min(M, N)*6))
     157              :     CALL ZGESVD( mode, mode, M, N, A, LDA, S, U, LDU, VT, LDVT, &
     158          192 :          & tmp, LWORK, rwork, INFO )
     159          192 :     LWORK = MIN( LWMAX, INT( tmp( 1 ) ) )
     160          576 :     ABI_MALLOC(work, (lwork))
     161              :     CALL ZGESVD( mode, mode, M, N, A, LDA, S, U, LDU, VT, LDVT, &
     162          192 :          WORK, LWORK, rwork, INFO )
     163          192 :     IF( INFO .GT. 0 ) THEN
     164            0 :        ABI_ERROR('The algorithm computing SVD failed to converge.')
     165            0 :        STOP
     166              :     END IF
     167          192 :     ABI_SFREE(work)
     168          192 :     ABI_SFREE(rwork)
     169          192 :   end subroutine complex_svd
     170              : 
     171              : 
     172            0 :   function gaussian(x, mu, sigma) result(y)
     173              :     real(dp), intent(in):: x, mu, sigma
     174              :     real(dp):: y
     175            0 :     y = exp(-1.0 * (x-mu)**2/sigma**2)
     176            0 :   end function gaussian
     177              : 
     178              :   !===============================================================
     179              :   ! Complementary error function.
     180              :   !===============================================================
     181              :   pure function erfcd(x) result(y)
     182              :     real(dp), intent(in):: x
     183              :     real(dp):: y
     184              :     real(dp):: t, z
     185              :     z = abs(x)
     186              :     t = 1.0 / ( 1.0+0.5*z )
     187              : 
     188              :     y = t*exp( -z*z - 1.26551223+t *   &
     189              :          ( 1.00002368+t * ( 0.37409196+t * &
     190              :          ( 0.09678418+t * (-0.18628806+t * &
     191              :          ( 0.27886807+t * (-1.13520398+t * &
     192              :          ( 1.48851587+t * (-0.82215223+t * 0.17087277 )))))))))
     193              :     if ( x .lt. 0.0 ) y = 2.0-y
     194              : 
     195              :   end function erfcd
     196              : 
     197         1935 :   function fermi(x, mu, sigma) result(y)
     198              :     real(dp), intent(in):: x, mu, sigma
     199              :     real(dp):: y
     200         1935 :     y = 0.5*erfc((x-mu) / sigma)
     201         1935 :   end function fermi
     202              : 
     203              : ! wrapper for LAPACK routine ZHEEV
     204              : ! evecs are used as the input matrix which will be overwritten
     205              : ! evals are the eigenvalues
     206          344 :   subroutine eigensolver_run(self, evals, evecs)
     207              :     class(eigensolver), intent(inout):: self
     208              :     real(dp), intent(inout):: evals(:)
     209              :     complex(dp), intent(inout):: evecs(:,:)
     210              :     integer ::  info
     211              :     external ZHEEV
     212          344 :     if (self%ndim == -1) then
     213          344 :        self%ndim = size(evecs, 1)
     214          344 :        self%lwork = -1
     215          344 :        ABI_MALLOC(self%work, (1))
     216         1032 :        ABI_MALLOC(self%rwork, (3*size(evecs, 1)-2))
     217          344 :        call ZHEEV('V', 'U', self%ndim, evecs, self%ndim, evals, self%work, self%lwork, self%rwork, info)
     218          344 :        self%lwork = INT(self%work(1))
     219          344 :        ABI_SFREE(self%work)
     220         1032 :        ABI_MALLOC(self%work, (self%lwork))
     221            0 :     else if (self%ndim /= size(evecs, 1)) then
     222            0 :        ABI_ERROR("Eigensovler: The size of the evecs is not the same as previous one.")
     223              :     end if
     224          344 :     call ZHEEV('V', 'U', self%ndim, evecs, self%ndim, evals, self%work, self%lwork, self%rwork, info)
     225          344 :   end subroutine eigensolver_run
     226              : 
     227              : ! free the memory used by eigensolver
     228          344 :   subroutine eigensolver_finalize(self)
     229              :     class(eigensolver), intent(inout):: self
     230          344 :     self%ndim = -1
     231          344 :     self%lwork = -1
     232          344 :     ABI_SFREE(self%work)
     233          344 :     ABI_SFREE(self%rwork)
     234          344 :   end subroutine eigensolver_finalize
     235              : 
     236              :   !----------------------------------------------------------------------
     237              :   !> @brief insertion_sort_int: sort a array using insertion sort algorithm
     238              :   !>  it is a memory safe method but is generally slow.
     239              :   !> @param[inout]  a: the array to be sorted. and will output inplace
     240              :   !> @param[inout] order (optional) the sorted index, it can be used to sort
     241              :   !>  other arrays so that the order in consistent.
     242              :   !----------------------------------------------------------------------
     243            0 :   subroutine insertion_sort_double(a, order)
     244              :     real(dp), intent(inout):: a(:)
     245              :     integer, optional, intent(inout):: order(size(a))
     246              :     integer:: n, i, j
     247              :     real(dp):: v
     248            0 :     n = size(a)
     249            0 :     if (present(order)) then
     250            0 :        do i = 1, n
     251            0 :           order(i)=i
     252              :        end do
     253              :     end if
     254            0 :     do i = 2, n
     255            0 :        v = a(i)
     256            0 :        j = i-1
     257            0 :        do while(j >= 1 )
     258            0 :           if (a(j)<=v) exit
     259            0 :           a(j+1)=a(j)
     260            0 :           if(present(order)) order(j+1)=order(j)
     261            0 :           j = j-1
     262              :        end do
     263            0 :        a(j+1)=v
     264            0 :        if(present(order)) order(j+1)=i
     265              :     end do
     266              : 
     267            0 :   end subroutine insertion_sort_double
     268              : 
     269              : 
     270              :   ! -4/2-> -1, 4/2->2. Then (-1, 0, 1, 2)
     271          126 :   pure function div2(x) result(y)
     272              :     integer, intent(in):: x
     273              :     integer:: y
     274          126 :     if(mod(x, 2)==0 .and. x < 0) then
     275           63 :        y = x/2+1
     276              :     else
     277           63 :        y = x/2
     278              :     endif
     279              :   end function div2
     280              : 
     281              : ! build the Rlist for the given kmesh
     282              : ! Rlist is a 3*n array, where n is the number of R vectors
     283            3 :   subroutine build_Rgrid(kmesh, Rlist)
     284              :     integer, intent(in):: kmesh(3)
     285              :     integer, allocatable, intent(inout):: Rlist(:,:)
     286              :     integer:: n, i1, i2, i3, i
     287              : 
     288            3 :     n = kmesh(1) * kmesh(2) *kmesh(3)
     289            9 :     ABI_MALLOC(Rlist, (3, n))
     290            3 :     i = 0
     291              :     ! Note that C/Fortran integer division is "truncate towards 0" division,
     292              :     ! whereas Python one is "floor" division.
     293              :     ! For C/Fortran, the behavior for even and odd numbers is
     294              :     !  not consistent and need special treatment in div2.
     295           21 :     do i3 = div2(-kmesh(3)), div2(kmesh(3))
     296           87 :        do i2 = div2(-kmesh(2)), div2(kmesh(2))
     297          348 :           do i1 = div2(-kmesh(1)), div2(kmesh(1))
     298          192 :              i = i+1
     299          816 :              Rlist(:, i) = [i1, i2, i3]
     300              :           end do
     301              :        end do
     302              :     end do
     303            3 :   end subroutine build_Rgrid
     304              : 
     305              : 
     306              : 
     307            0 : end module m_scdm_math
     308              : 
     309              : !!***
        

Generated by: LCOV version 2.3-1