LCOV - code coverage report
Current view: top level - src/66_nonlocal - m_opernla_ylm_mv.F90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 67 0
Test Date: 2026-09-19 15:24:51 Functions: 0.0 % 1 0

            Line data    Source code
       1              : !!****m* ABINIT/m_opernla_ylm_mv
       2              : !! NAME
       3              : !!  m_opernla_ylm_mv
       4              : !!
       5              : !! FUNCTION
       6              : !!
       7              : !! COPYRIGHT
       8              : !!  Copyright (C) 2021-2026 ABINIT group (LB,MT)
       9              : !!  This file is distributed under the terms of the
      10              : !!  GNU General Public License, see ~abinit/COPYING
      11              : !!  or http://www.gnu.org/copyleft/gpl.txt .
      12              : !!
      13              : !! SOURCE
      14              : 
      15              : #if defined HAVE_CONFIG_H
      16              : #include "config.h"
      17              : #endif
      18              : 
      19              : #include "abi_common.h"
      20              : 
      21              : module m_opernla_ylm_mv
      22              : 
      23              :  use defs_basis
      24              :  use m_abicore
      25              :  use m_errors
      26              :  use m_xmpi
      27              : #if defined HAVE_OPENMP
      28              :  use OMP_LIB
      29              : #endif
      30              : 
      31              :  use defs_abitypes, only : MPI_type
      32              :  use m_time,        only : timab
      33              : 
      34              :  implicit none
      35              : 
      36              :  private
      37              : !!***
      38              : 
      39              :  public :: opernla_ylm_mv
      40              :  integer,public,save :: opernla_mv_counter = -1
      41              :  integer,public,save :: opernla_mv_dgemv_counter = -1
      42              : !!***
      43              : !!***
      44              : 
      45              : contains
      46              : !!***
      47              : 
      48              : !!****f* ABINIT/opernla_ylm_mv
      49              : !! NAME
      50              : !! opernla_ylm_mv
      51              : !!
      52              : !! FUNCTION
      53              : !! "matrix-vector" alternative implementation of "opernla_ylm".
      54              : !!
      55              : !! For a given wave-function |c>, get all projected scalars
      56              : !! <p_lmn|c> where |p_lmn> are non-local projectors
      57              : !!   With:
      58              : !!   <p_lmn|c>=4pi/sqrt(vol) (i)^l Sum_g[c(g).f_nl(g).Y_lm(g).exp(2pi.i.g.R)]
      59              : !!
      60              : !! Here this is done in 3 steps:
      61              : !! (1) compute for every g   : scal(g)   = c(g).exp(2pi.i.g.R)
      62              : !! (2) compute for every lmn : scal(lmn) = Sum_g[scal(g).f_nl(g).Y_lm(g)]
      63              : !! (3) compute for every lmn : <p_lmn|c> = 4pi/sqrt(vol).(i)^l.scal(lmn)
      64              : !!
      65              : !! Step (2) is a real-matrix/complex-vector multiplication, here two options are possible:
      66              : !! - case nloalg(1)=2 : compute the real and imaginary parts separately using two calls of DGMEV
      67              : !! - case nloalg(1)=3 : in order to read the matrix only once, we compute both real and imaginary parts at the same time "by hand"
      68              : !!
      69              : !! Depending on the achitecture and the available blas library, one option could be more interesting than an other...
      70              : !!
      71              : !! INPUTS
      72              : !!  choice=chooses possible output:
      73              : !!         if choice>=0: compute projected scalars
      74              : !!         if choice<0: same as choice>0 but use already computed projected scalars
      75              : !!  cplex=1 if <p_lmn|c> scalars are real (equivalent to istwfk>1)
      76              : !!        2 if <p_lmn|c> scalars are complex
      77              : !!  dimffnl=second dimension of ffnl
      78              : !!  ffnl(npw,dimffnl,nlmn)= nonlocal quantities containing nonlocal form factors
      79              : !!  ia3=gives the number of the first atom in the subset presently treated
      80              : !!  indlmn(6,nlmn)= array giving l,m,n,lm,ln,s for i=lmn
      81              : !!  istwf_k=option parameter that describes the storage of wfs
      82              : !!  matblk=dimension of the array ph3d
      83              : !!  mpi_enreg=information about MPI parallelization
      84              : !!  nincat=number of atoms in the subset here treated
      85              : !!  nlmn=number of (l,m,n) numbers for current type of atom
      86              : !!  nloalg(3)=governs the choice of the algorithm for non-local operator.
      87              : !!  npw=number of plane waves in reciprocal space
      88              : !!  nspinor=number of spinorial components of the wavefunctions (on current proc)
      89              : !!  ph3d(2,npw,matblk)=three-dimensional phase factors
      90              : !!  ucvol=unit cell volume (bohr^3)
      91              : !!  vect(2,npw*my_nspinor)=starting vector in reciprocal space
      92              : !!
      93              : !! OUTPUT
      94              : !!  gx(cplex,nlmn,nincat,nspinor)= projected scalars
      95              : !!
      96              : !! SIDE EFFECTS
      97              : !!
      98              : !! NOTES
      99              : !! 1-Not available yet for openMP
     100              : !! 2-Operate for one type of atom, and within this given type of atom,
     101              : !!   for a subset of at most nincat atoms.
     102              : !! 3-projector derivatives (abs(choice)>1) are not implemented yet
     103              : !!
     104              : !! SOURCE
     105              : 
     106            0 : subroutine opernla_ylm_mv(choice,cplex,dimffnl,ffnl,gx,&
     107            0 : &       ia3,indlmn,istwf_k,matblk,mpi_enreg,nincat,nlmn,&
     108            0 : &       nloalg,npw,nspinor,ph3d,ucvol,vect)
     109              : 
     110              : !Arguments ------------------------------------
     111              : !scalars
     112              :  integer,intent(in) :: choice,cplex,dimffnl,ia3,istwf_k,matblk
     113              :  integer,intent(in) :: nincat,nlmn,npw,nspinor
     114              :  real(dp),intent(in) :: ucvol
     115              :  type(MPI_type),intent(in) :: mpi_enreg
     116              : !arrays
     117              :  integer,intent(in) :: indlmn(6,nlmn),nloalg(3)
     118              :  real(dp),intent(in),target :: ffnl(npw,dimffnl,nlmn)
     119              :  real(dp),intent(in) :: ph3d(2,npw,matblk)
     120              :  real(dp),intent(in) :: vect(:,:)
     121              :  real(dp),intent(out) :: gx(cplex,nlmn,nincat,nspinor)
     122              : 
     123              : !Local variables-------------------------------
     124              : !scalars
     125              :  logical :: use_dgemv
     126              :  integer :: ia,iaph3d,ierr,il,ilmn,ipw,ipw0,ipwshft,ispinor,jpw
     127              :  real(dp) :: wt
     128              : !arrays
     129              :  real(dp) :: buffer_r,buffer_i,tsec(2)
     130            0 :  real(dp),pointer :: ffnl_loc(:,:)
     131            0 :  real(dp),allocatable :: scali(:),scalr(:)
     132            0 :  real(dp),allocatable :: scalr_lmn(:),scali_lmn(:)
     133              :  complex(dp) :: ctmp,cil(4)
     134              : ! *************************************************************************
     135              : 
     136            0 :  if (choice==-1) return
     137              : 
     138              : !Some checks
     139            0 :  if (abs(choice)>1) then
     140            0 :    ABI_ERROR('Only abs(choice)<=1 is available for now.')
     141              :  end if
     142            0 :  if (nloalg(1)<2.or.nloalg(1)>10) then
     143            0 :    ABI_ERROR('nloalg(1) should be between 2 and 10.')
     144              :  end if
     145              : ! nthreads=1
     146              : !#if defined HAVE_OPENMP
     147              : ! nthreads=OMP_GET_NUM_THREADS()
     148              : !#endif
     149              : ! if (nthreads>1) then
     150              : !   ABI_ERROR('Only nthreads=1 is available for now.')
     151              : ! end if
     152              : 
     153            0 :  use_dgemv = nloalg(1)==2.or.nloalg(1)==5.or.nloalg(1)==7
     154              :  if (choice>=0.or.abs(choice)>1) then
     155            0 :    if (use_dgemv) then
     156            0 :      if(opernla_mv_dgemv_counter>=0) opernla_mv_dgemv_counter = opernla_mv_dgemv_counter + 1
     157              :    else
     158            0 :      if(opernla_mv_counter>=0) opernla_mv_counter = opernla_mv_counter + 1
     159              :    end if
     160              :  end if
     161              : 
     162              : !Useful variables
     163            0 :  wt=four_pi/sqrt(ucvol);if (cplex==1) wt=2.d0*wt
     164            0 :  ipw0=1;if (istwf_k==2.and.mpi_enreg%me_g0_fft==1) ipw0=2
     165              : 
     166              : !Allocate work space
     167            0 :  ABI_MALLOC(scalr,(npw))
     168            0 :  ABI_MALLOC(scali,(npw))
     169            0 :  ABI_MALLOC(scalr_lmn,(nlmn))
     170            0 :  ABI_MALLOC(scali_lmn,(nlmn))
     171              : 
     172            0 :  ffnl_loc => ffnl(:,1,:)
     173              : 
     174              : ! i^l
     175            0 :  cil(1) = ( 1.0_DP, 0.0_DP) * wt
     176            0 :  cil(2) = ( 0.0_DP, 1.0_DP) * wt
     177            0 :  cil(3) = (-1.0_DP, 0.0_DP) * wt
     178            0 :  cil(4) = ( 0.0_DP,-1.0_DP) * wt
     179              : 
     180              :  ! FIXME OpenMP parallelism is still flawed here so forcing NUM_THREADS=1 here
     181              :  ! Bug reproducible with v9[71] and bounds checks enabled
     182              : !$OMP PARALLEL PRIVATE(il,ilmn,ipw,jpw), &
     183              : !$OMP PRIVATE(ispinor,ipwshft,ia,iaph3d) &
     184              : !$OMP NUM_THREADS(1)
     185              : 
     186              : !Loop on spinorial components
     187            0 :  do ispinor =1,nspinor
     188            0 :    ipwshft=(ispinor-1)*npw
     189              : 
     190              : !  Loop on atoms (blocking)
     191            0 :    do ia=1,nincat
     192            0 :      iaph3d=ia;if (nloalg(2)>0) iaph3d=ia+ia3-1
     193              : !    Step (1) : Compute scal(g) = c(g).exp(2pi.i.g.R)
     194              : !$OMP DO
     195            0 :      do ipw=ipw0,npw
     196            0 :        jpw=ipw+ipwshft
     197            0 :        scalr(ipw)=(vect(1,jpw)*ph3d(1,ipw,iaph3d)-vect(2,jpw)*ph3d(2,ipw,iaph3d))
     198            0 :        scali(ipw)=(vect(2,jpw)*ph3d(1,ipw,iaph3d)+vect(1,jpw)*ph3d(2,ipw,iaph3d))
     199              :      end do
     200              : !$OMP END DO
     201              : 
     202              : !$OMP SINGLE
     203            0 :      if (ipw0==2) then
     204            0 :        scalr(1)=half*vect(1,1+ipwshft)*ph3d(1,1,iaph3d)
     205            0 :        scali(1)=half*vect(1,1+ipwshft)*ph3d(2,1,iaph3d)
     206              :      end if
     207              : !$OMP END SINGLE
     208              : 
     209              : !    --------------------------------------------------------------------
     210              : !    ALL CHOICES:
     211              : !    Accumulate Gx
     212              : !    --------------------------------------------------------------------
     213              : 
     214            0 :      if (choice>=0) then
     215              : 
     216              : !      Step (2) : Compute scal(lmn) = Sum_g[scal(g).f_nl(g).Y_lm(g)]
     217            0 :        if (use_dgemv) then
     218            0 :          call DGEMV('T',npw,nlmn,1.0_DP,ffnl_loc,npw,scalr,1,0.0_DP,scalr_lmn,1)
     219            0 :          call DGEMV('T',npw,nlmn,1.0_DP,ffnl_loc,npw,scali,1,0.0_DP,scali_lmn,1)
     220              :        else
     221            0 :          do ilmn=1,nlmn
     222              : !           do ipw=1,npw
     223              : !             scalr_lmn(ilmn) = scalr_lmn(ilmn) + scalr(ipw) * ffnl_loc(ipw,ilmn)
     224              : !             scali_lmn(ilmn) = scali_lmn(ilmn) + scali(ipw) * ffnl_loc(ipw,ilmn)
     225              : !           end do
     226              : !$OMP SINGLE
     227              :            buffer_r = 0.0_DP
     228              :            buffer_i = 0.0_DP
     229              : !$OMP END SINGLE
     230              : !$OMP DO REDUCTION(+:buffer_r,buffer_i)
     231            0 :            do ipw=1,npw
     232            0 :              buffer_r = buffer_r + scalr(ipw) * ffnl_loc(ipw,ilmn)
     233            0 :              buffer_i = buffer_i + scali(ipw) * ffnl_loc(ipw,ilmn)
     234              :            end do
     235              : !$OMP END DO
     236              : !$OMP SINGLE
     237            0 :            scalr_lmn(ilmn) = buffer_r
     238            0 :            scali_lmn(ilmn) = buffer_i
     239              : !$OMP END SINGLE
     240              :          end do
     241              :        end if
     242              : !      Step (3) : Compute gx(lmn) = 4pi/sqrt(vol) (i)^l scal(lmn)
     243              : !$OMP SINGLE
     244            0 :        if (cplex==2) then
     245            0 :          do ilmn=1,nlmn
     246            0 :            il=mod(indlmn(1,ilmn),4)+1
     247            0 :            ctmp = cil(il) * cmplx(scalr_lmn(ilmn),scali_lmn(ilmn),kind=DP)
     248            0 :            gx(1,ilmn,ia,ispinor) = real(ctmp)
     249            0 :            gx(2,ilmn,ia,ispinor) = aimag(ctmp)
     250              :          end do
     251              :        else
     252            0 :          do ilmn=1,nlmn
     253            0 :            il=mod(indlmn(1,ilmn),4)+1
     254            0 :            ctmp = cil(il) * cmplx(scalr_lmn(ilmn),scali_lmn(ilmn),kind=DP)
     255            0 :            gx(1,ilmn,ia,ispinor) = real(ctmp)
     256              :          end do
     257              :        end if
     258              : !$OMP END SINGLE
     259              : 
     260              :      end if
     261              : 
     262              :    end do ! End loop on atoms
     263              : 
     264              :  end do !  End loop on spinorial components
     265              : !$OMP END PARALLEL
     266              : 
     267              : !Deallocate temporary space
     268            0 :  ABI_FREE(scalr)
     269            0 :  ABI_FREE(scali)
     270            0 :  ABI_FREE(scalr_lmn)
     271            0 :  ABI_FREE(scali_lmn)
     272              : 
     273              : !Has to reduce arrays in case of FFT parallelization
     274            0 :  if (mpi_enreg%nproc_fft>1) then
     275            0 :    call timab(48,1,tsec)
     276            0 :    if (choice>=0) then
     277            0 :      call xmpi_sum(gx,mpi_enreg%comm_fft,ierr)
     278              :    end if
     279            0 :    call timab(48,2,tsec)
     280              :  end if
     281              : 
     282            0 : end subroutine opernla_ylm_mv
     283              : !!***
     284              : 
     285              : end module m_opernla_ylm_mv
     286              : !!***
        

Generated by: LCOV version 2.3-1