LCOV - code coverage report
Current view: top level - src/66_wfs - m_invovl.F90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 90.2 % 307 277
Test Date: 2026-09-20 15:27:41 Functions: 63.6 % 11 7

            Line data    Source code
       1              : !!****m* ABINIT/m_invovl
       2              : !! NAME
       3              : !!  m_invovl
       4              : !!
       5              : !! FUNCTION
       6              : !!  Provides functions to invert the overlap matrix S. Used by Chebyshev in PAW
       7              : !!  See paper by A. Levitt and M. Torrent for details
       8              : !!  S = 1 + projs * s_projs * projs'
       9              : !!  S^-1 = 1 + projs * inv_s_projs * projs', with
      10              : !!  inv_s_projs = - (s_projs^-1 + projs'*projs)^-1
      11              : !!
      12              : !! COPYRIGHT
      13              : !! Copyright (C) 2013-2026 ABINIT group (AL)
      14              : !! This file is distributed under the terms of the
      15              : !! GNU General Public License, see ~abinit/COPYING
      16              : !! or http://www.gnu.org/copyleft/gpl.txt .
      17              : !!
      18              : !! SOURCE
      19              : 
      20              : #if defined HAVE_CONFIG_H
      21              : #include "config.h"
      22              : #endif
      23              : 
      24              : #include "abi_common.h"
      25              : 
      26              : ! nvtx related macro definition
      27              : #include "nvtx_macros.h"
      28              : 
      29              : MODULE m_invovl
      30              : 
      31              :  use defs_basis
      32              :  use m_errors
      33              :  use m_xmpi
      34              :  use m_xomp
      35              :  use m_abicore
      36              :  use m_abi_linalg
      37              : 
      38              :  use defs_abitypes, only : mpi_type
      39              :  use m_time,        only : timab
      40              :  use m_hamiltonian, only : gs_hamiltonian_type
      41              :  use m_bandfft_kpt, only : bandfft_kpt_get_ikpt
      42              :  use m_pawcprj,     only : pawcprj_type, pawcprj_alloc, pawcprj_free, pawcprj_axpby
      43              :  use m_gemm_nonlop_projectors, only : gemm_nonlop_use_gemm
      44              :  use m_nonlop,      only : nonlop
      45              :  use m_prep_kgb,    only : prep_nonlop
      46              : 
      47              : #ifdef HAVE_FC_ISO_C_BINDING
      48              : ! FIXME Don't know what's wrong with GCC when OpenMP GPU Offload is enabled here...
      49              : #ifdef FC_GNU
      50              :  use, intrinsic :: iso_c_binding, only : c_int32_t, c_int64_t, c_float, c_double, c_size_t, c_loc
      51              : #else
      52              :  use, intrinsic :: iso_c_binding, only : c_ptr, c_int32_t, c_int64_t, c_float, c_double, c_size_t, c_loc
      53              : #endif
      54              : #endif
      55              : 
      56              : #if defined(HAVE_GPU_MARKERS)
      57              :  use m_nvtx_data
      58              : #endif
      59              : 
      60              : #ifdef HAVE_GPU
      61              :   use m_gpu_toolbox
      62              : #endif
      63              : 
      64              : #ifdef HAVE_KOKKOS
      65              :  use m_manage_kokkos, only : add_array_kokkos
      66              : #endif
      67              : 
      68              :  implicit none
      69              : 
      70              :  private
      71              : 
      72              : !public procedures.
      73              :  public :: init_invovl
      74              :  public :: make_invovl
      75              :  public :: apply_invovl
      76              :  public :: destroy_invovl
      77              : 
      78              :  ! Those routines are here to assess memory requirements
      79              :  public :: invovl_ompgpu_work_mem
      80              :  public :: invovl_ompgpu_static_mem
      81              : !!***
      82              : 
      83              : !!****t* m_invovl/invovl_kpt_type
      84              : !! NAME
      85              : !! invovl_kpt_type
      86              : !!
      87              : !! FUNCTION
      88              : !! Contains information needed to invert the overlap matrix S
      89              : !!
      90              : !! SOURCE
      91              : 
      92              : #if defined(HAVE_FC_ISO_C_BINDING) && defined(HAVE_GPU_CUDA)
      93              : 
      94              :  type, public :: invovl_kpt_type
      95              : 
      96              :    integer(kind=c_int32_t) :: nprojs
      97              :    !  total number of projectors
      98              :    !    nlmn for a specific atom = count(indlmn(3,:,itypat)>0)
      99              :    !  A value of -1 means that the following arrays are not allocated
     100              : 
     101              :    real(kind=c_double), allocatable :: gram_projs(:,:,:)
     102              :    ! gram_projs(cplx, nprojs, nprojs)
     103              :    ! projs' * projs
     104              : 
     105              :    real(kind=c_double), allocatable :: inv_sij(:,:,:,:)
     106              :    ! inv_sij(cplx, lmnmax, lmnmax, ntypat)
     107              :    ! inverse of ham%sij
     108              : 
     109              :    real(kind=c_double), allocatable :: inv_s_approx(:,:,:,:)
     110              :    ! inv_s_approx(cplx, lmnmax, lmnmax, ntypat)
     111              :    ! preconditionner
     112              : 
     113              :  end type invovl_kpt_type
     114              : 
     115              :  !> companion type to invovl_kpt_type to pass data to gpu/cuda
     116              :  type, bind(c), public :: invovl_kpt_gpu_type
     117              : 
     118              :    integer(kind=c_int32_t) :: nprojs
     119              :    !  total number of projectors
     120              :    !    nlmn for a specific atom = count(indlmn(3,:,itypat)>0)
     121              :    !  A value of -1 means that the following arrays are not allocated
     122              : 
     123              :    type(c_ptr) :: gram_projs
     124              :    ! gram_projs(cplx, nprojs, nprojs)
     125              :    ! projs' * projs
     126              : 
     127              :    integer(kind=c_int32_t) :: gram_projs_dim(3)
     128              : 
     129              :    type(c_ptr) :: inv_sij
     130              :    ! inv_sij(cplx, lmnmax, lmnmax, ntypat)
     131              :    ! inverse of ham%sij
     132              : 
     133              :    integer(kind=c_int32_t) :: inv_sij_dim(4)
     134              : 
     135              :    type(c_ptr) :: inv_s_approx
     136              :    ! inv_s_approx(cplx, lmnmax, lmnmax, ntypat)
     137              :    ! preconditionner
     138              : 
     139              :    integer(kind=c_int32_t) :: inv_s_approx_dim(4)
     140              : 
     141              :  end type invovl_kpt_gpu_type
     142              : 
     143              : #else
     144              : 
     145              : type, public :: invovl_kpt_type
     146              : 
     147              :    integer :: nprojs
     148              :    !  total number of projectors
     149              :    !    nlmn for a specific atom = count(indlmn(3,:,itypat)>0)
     150              :    !  A value of -1 means that the following arrays are not allocated
     151              : 
     152              :    real(dp), allocatable :: gram_projs(:,:,:)
     153              :    ! gram_projs(cplx, nprojs, nprojs)
     154              :    ! projs' * projs
     155              : 
     156              :    real(dp), allocatable :: inv_sij(:,:,:,:)
     157              :    ! inv_sij(cplx, lmnmax, lmnmax, ntypat)
     158              :    ! inverse of ham%sij
     159              : 
     160              :    real(dp), allocatable :: inv_s_approx(:,:,:,:)
     161              :    ! inv_s_approx(cplx, lmnmax, lmnmax, ntypat)
     162              :    ! preconditionner
     163              : 
     164              : end type invovl_kpt_type
     165              : 
     166              : #endif
     167              : 
     168              : !!***
     169              : 
     170              :   integer, parameter :: tim_nonlop = 13
     171              : 
     172              :   integer, parameter :: &
     173              :     & timer_apply_inv_ovl_opernla = 1630, &
     174              :     & timer_apply_inv_ovl_opernlb = 1631, &
     175              :     & timer_apply_inv_ovl_inv_s   = 1632
     176              : 
     177              :  type(invovl_kpt_type), public,save,allocatable, target :: invovl_kpt(:)
     178              : #ifdef HAVE_OPENMP_OFFLOAD
     179              :  real(dp), ABI_CONTIGUOUS pointer :: current_gram_projs(:,:,:)
     180              :  real(dp), ABI_CONTIGUOUS pointer :: current_inv_sij(:,:,:,:)
     181              :  real(dp), ABI_CONTIGUOUS pointer :: current_inv_s_approx(:,:,:,:)
     182              :  real(dp),allocatable, target :: proj_ompgpu(:,:,:)
     183              :  real(dp),allocatable, target :: sm1proj_ompgpu(:,:,:)
     184              :  real(dp),allocatable, target :: PtPsm1proj_ompgpu(:,:,:)
     185              :  !Module variable keeping track of which K-point data is so=tored on GPU
     186              :  integer, save :: current_ikpt_in_gpu=-1
     187              :  integer, save :: gpu_initialized=0
     188              :  integer, save :: mod__cplx=0, mod__nprojs=0
     189              : #endif
     190              : 
     191              : #if defined(HAVE_GPU_CUDA)
     192              : 
     193              :  !> this interface is only useful when gpu is enabled
     194              :  !! these functions are defined in 46_manage_gpu/gpu_apply_invovl_inner.cu
     195              :  !! these functions are entry point for calling cuda implemented functions
     196              :  interface
     197              : 
     198              :    !> allocate GPU workspace for apply_invovl
     199              :    subroutine f_gpu_apply_invovl_inner_alloc(proj_dim, ntypat, realloc) bind(c, name='gpu_apply_invovl_inner_alloc')
     200              :      use, intrinsic :: iso_c_binding
     201              :      integer(kind=c_int32_t),        intent(in) :: proj_dim(3)
     202              :      integer(kind=c_int32_t), value, intent(in) :: ntypat
     203              :      integer(kind=c_int32_t), value, intent(in) :: realloc
     204              :    end subroutine f_gpu_apply_invovl_inner_alloc
     205              : 
     206              :    !> deallocate GPU workspace for apply_invovl
     207              :    subroutine f_gpu_apply_invovl_inner_dealloc() bind(c, name='gpu_apply_invovl_inner_dealloc')
     208              :    end subroutine f_gpu_apply_invovl_inner_dealloc
     209              : 
     210              :    !> allocate GPU workspace for make_invovl (sij and s_approx)
     211              :    subroutine f_gpu_apply_invovl_matrix_alloc(cplx, nprojs, lmnmax, ntypat, realloc) bind(c, name='gpu_apply_invovl_matrix_alloc')
     212              :      use, intrinsic :: iso_c_binding
     213              :      integer(kind=c_int32_t), value, intent(in) :: cplx
     214              :      integer(kind=c_int32_t), value, intent(in) :: nprojs
     215              :      integer(kind=c_int32_t), value, intent(in) :: ntypat
     216              :      integer(kind=c_int32_t), value, intent(in) :: lmnmax
     217              :      integer(kind=c_int32_t), value, intent(in) :: realloc
     218              :    end subroutine f_gpu_apply_invovl_matrix_alloc
     219              : 
     220              :    !> deallocate GPU workspace for make_invovl (sij and s_approx)
     221              :    subroutine f_gpu_apply_invovl_matrix_dealloc() bind(c, name='gpu_apply_invovl_matrix_dealloc')
     222              :    end subroutine f_gpu_apply_invovl_matrix_dealloc
     223              : 
     224              :    !> init data for GPU
     225              :    subroutine f_gpu_init_invovl_data(indlmn_dim, indlmn_ptr) bind(c, name='init_invovl_data')
     226              :      use, intrinsic :: iso_c_binding
     227              :      integer(kind=c_int32_t),        intent(in)    :: indlmn_dim(3)
     228              :      type(c_ptr)            , value                :: indlmn_ptr
     229              :    end subroutine f_gpu_init_invovl_data
     230              : 
     231              :    !> upload inverse overlap matrices
     232              :    subroutine f_upload_inverse_overlap(invovl_gpu, cplx, nprojs, lmnmax, ntypat) bind(c, name='upload_inverse_overlap')
     233              :      use, intrinsic :: iso_c_binding
     234              :      import invovl_kpt_gpu_type
     235              :      type(invovl_kpt_gpu_type), value, intent(in) :: invovl_gpu
     236              :      integer(kind=c_int32_t),   value, intent(in) :: cplx
     237              :      integer(kind=c_int32_t),   value, intent(in) :: nprojs
     238              :      integer(kind=c_int32_t),   value, intent(in) :: lmnmax
     239              :      integer(kind=c_int32_t),   value, intent(in) :: ntypat
     240              :    end subroutine f_upload_inverse_overlap
     241              : 
     242              :    !> solve_inner on GPU
     243              :    subroutine f_solve_inner_gpu(proj_dim, proj_ptr, sm1proj_ptr, ptp_sm1proj_ptr, &
     244              :      & nattyp_dim, nattyp_ptr, ntypat, lmnmax, cplx, block_sliced) bind(c, name='solve_inner_gpu')
     245              : 
     246              :      use, intrinsic :: iso_c_binding
     247              :      integer(kind=c_int32_t),        intent(in) :: proj_dim(3)
     248              :      type(c_ptr)            , value             :: proj_ptr
     249              :      type(c_ptr)            , value             :: sm1proj_ptr
     250              :      type(c_ptr)            , value             :: ptp_sm1proj_ptr
     251              :      integer(kind=c_int32_t), value, intent(in) :: nattyp_dim
     252              :      type(c_ptr)            , value             :: nattyp_ptr
     253              :      integer(kind=c_int32_t), value, intent(in) :: ntypat
     254              :      integer(kind=c_int32_t), value, intent(in) :: lmnmax
     255              :      integer(kind=c_int32_t), value, intent(in) :: cplx
     256              :      integer(kind=c_int32_t), value, intent(in) :: block_sliced
     257              :    end subroutine f_solve_inner_gpu
     258              : 
     259              :  end interface
     260              : 
     261              : #endif
     262              : 
     263              : CONTAINS
     264              : 
     265              : #if defined(HAVE_FC_ISO_C_BINDING) && defined(HAVE_GPU_CUDA)
     266              : !!****f* m_invovl/make_invovl_kpt_gpu
     267              : !! NAME
     268              : !! make_invovl_kpt
     269              : !!
     270              : !! FUNCTION
     271              : !! Create a invovl_pkt_gpu_type from a cpu counter part for cuda interoperability
     272              : !! SOURCE
     273              :   function make_invovl_kpt_gpu(invovl) result(invovl_gpu)
     274              :     type(invovl_kpt_type), intent(inout),target :: invovl
     275              :     type(invovl_kpt_gpu_type)                   :: invovl_gpu
     276              : 
     277              :     invovl_gpu%nprojs = invovl%nprojs
     278              : 
     279              :     invovl_gpu%gram_projs = c_loc(invovl%gram_projs(1,1,1))
     280              :     invovl_gpu%gram_projs_dim = (/ &
     281              :       & size(invovl%gram_projs,1), &
     282              :       & size(invovl%gram_projs,2), &
     283              :       & size(invovl%gram_projs,3)  &
     284              :       & /)
     285              : 
     286              :     invovl_gpu%inv_sij = c_loc(invovl%inv_sij(1,1,1,1))
     287              :     invovl_gpu%inv_sij_dim = (/ &
     288              :       & size(invovl%inv_sij,1), &
     289              :       & size(invovl%inv_sij,2), &
     290              :       & size(invovl%inv_sij,3), &
     291              :       & size(invovl%inv_sij,4)  &
     292              :       & /)
     293              : 
     294              :     invovl_gpu%inv_s_approx = c_loc(invovl%inv_s_approx(1,1,1,1))
     295              :     invovl_gpu%inv_s_approx_dim = (/ &
     296              :       & size(invovl%inv_s_approx,1), &
     297              :       & size(invovl%inv_s_approx,2), &
     298              :       & size(invovl%inv_s_approx,3), &
     299              :       & size(invovl%inv_s_approx,4)  &
     300              :       & /)
     301              : 
     302              :   end function make_invovl_kpt_gpu
     303              : !!***
     304              : #endif
     305              : 
     306              : #ifdef HAVE_OPENMP_OFFLOAD
     307              : 
     308              :  subroutine alloc_ompgpu_buffers(cplx,nprojs,nspinor,ndat)
     309              :   integer,intent(in) :: cplx,nprojs,nspinor,ndat
     310              : 
     311              :   if(gpu_initialized == 0 .or. mod__cplx/=cplx .or. mod__nprojs/=nprojs) then
     312              : 
     313              :     if(gpu_initialized==1) then
     314              :       ABI_FREE(proj_ompgpu)
     315              :       ABI_FREE(sm1proj_ompgpu)
     316              :       ABI_FREE(PtPsm1proj_ompgpu)
     317              :     end if
     318              : 
     319              :     ABI_MALLOC(proj_ompgpu,       (cplx,nprojs,nspinor*ndat))
     320              :     ABI_MALLOC(sm1proj_ompgpu,    (cplx,nprojs,nspinor*ndat))
     321              :     ABI_MALLOC(PtPsm1proj_ompgpu, (cplx,nprojs,nspinor*ndat))
     322              : 
     323              :     mod__cplx=cplx; mod__nprojs=nprojs
     324              : 
     325              :     !FIXME Smater buffer management ?
     326              :     !!$OMP TARGET ENTER DATA MAP(alloc:proj_ompgpu,sm1proj_ompgpu,PtPsm1proj_ompgpu)
     327              :     gpu_initialized=1
     328              :   end if
     329              : 
     330              :  end subroutine alloc_ompgpu_buffers
     331              : 
     332              :  subroutine refresh_invovl_ompgpu_kpt(ikpt)
     333              :   integer,intent(in) :: ikpt
     334              : 
     335              : ! *************************************************************************
     336              : 
     337              :   if(ikpt < 0) then
     338              :     ABI_BUG("Requested GPU upload of a negative K-point index !")
     339              :   end if
     340              : 
     341              :   if(current_ikpt_in_gpu /= -1) then
     342              :     !$OMP TARGET EXIT DATA MAP(delete:current_gram_projs)
     343              :     !$OMP TARGET EXIT DATA MAP(delete:current_inv_sij)
     344              :     !$OMP TARGET EXIT DATA MAP(delete:current_inv_s_approx)
     345              :   end if
     346              : 
     347              :   current_gram_projs   => invovl_kpt(ikpt)%gram_projs
     348              :   current_inv_sij      => invovl_kpt(ikpt)%inv_sij
     349              :   current_inv_s_approx => invovl_kpt(ikpt)%inv_s_approx
     350              : 
     351              :   !$OMP TARGET ENTER DATA MAP(to:current_gram_projs)
     352              :   !$OMP TARGET ENTER DATA MAP(to:current_inv_sij)
     353              :   !$OMP TARGET ENTER DATA MAP(to:current_inv_s_approx)
     354              :   current_ikpt_in_gpu=ikpt
     355              : 
     356              :  end subroutine refresh_invovl_ompgpu_kpt
     357              : #endif
     358              : 
     359              : !!****f* m_invovl/init_invovl
     360              : !! NAME
     361              : !! init_invovl
     362              : !!
     363              : !! FUNCTION
     364              : !! Initalization of the invovl_kpt array
     365              : !!
     366              : !! INPUTS
     367              : !! nkpt= number of k-points
     368              : !!
     369              : !! SOURCE
     370              : 
     371          105 :  subroutine init_invovl(nkpt)
     372              : 
     373              :   integer, intent(in) :: nkpt
     374              :   integer :: ikpt
     375              : 
     376              : ! *************************************************************************
     377              : 
     378         1509 :   ABI_MALLOC(invovl_kpt, (nkpt))
     379              :   ! TODO add cycling if kpt parallelism
     380         1299 :   do ikpt=1,nkpt
     381         1299 :     invovl_kpt(ikpt)%nprojs = -1
     382              :   end do
     383              : 
     384          105 :  end subroutine init_invovl
     385              : !!***
     386              : 
     387              : !!****f* m_invovl/destroy_invovl_ikpt
     388              : !! NAME
     389              : !! destroy_invovl_ikpt
     390              : !!
     391              : !! FUNCTION
     392              : !! Destruction of the ikpt-th member of invovl array
     393              : !!
     394              : !! INPUTS
     395              : !! ikpt= index of k-point
     396              : !!
     397              : !! SOURCE
     398        16527 :  subroutine destroy_invovl_ikpt(ikpt, gpu_option)
     399              : 
     400              :    integer, intent(in) :: ikpt
     401              :    integer, intent(in) :: gpu_option
     402              : 
     403              : ! *************************************************************************
     404              : 
     405              :   if(gpu_option==ABI_GPU_OPENMP) then
     406              : #ifdef HAVE_OPENMP_OFFLOAD
     407              :     if(gpu_initialized==1 .and. current_ikpt_in_gpu == ikpt) then
     408              :       !$OMP TARGET EXIT DATA MAP(delete:current_gram_projs)
     409              :       !$OMP TARGET EXIT DATA MAP(delete:current_inv_sij)
     410              :       !$OMP TARGET EXIT DATA MAP(delete:current_inv_s_approx)
     411              :       nullify(current_gram_projs)
     412              :       nullify(current_inv_sij)
     413              :       nullify(current_inv_s_approx)
     414              :       current_ikpt_in_gpu = -1
     415              :       !FIXME Smater buffer management ?
     416              :       !!$OMP TARGET EXIT DATA MAP(delete:proj_ompgpu,sm1proj_ompgpu,PtPsm1proj_ompgpu)
     417              :       ABI_FREE(proj_ompgpu)
     418              :       ABI_FREE(sm1proj_ompgpu)
     419              :       ABI_FREE(PtPsm1proj_ompgpu)
     420              :       gpu_initialized = 0
     421              :     end if
     422              : #endif
     423              :   end if
     424              : 
     425        16527 :   ABI_FREE(invovl_kpt(ikpt)%gram_projs)
     426        16527 :   ABI_FREE(invovl_kpt(ikpt)%inv_sij)
     427        16527 :   ABI_FREE(invovl_kpt(ikpt)%inv_s_approx)
     428        16527 :   invovl_kpt(ikpt)%nprojs = -1
     429              : 
     430              : #if defined(HAVE_GPU_CUDA) && defined(HAVE_FC_ISO_C_BINDING)
     431              :   if (gpu_option == ABI_GPU_LEGACY .or. gpu_option == ABI_GPU_KOKKOS) then
     432              :     call f_gpu_apply_invovl_inner_dealloc()
     433              :     call f_gpu_apply_invovl_matrix_dealloc()
     434              :   end if
     435              : #endif
     436              : 
     437        16527 :  end subroutine destroy_invovl_ikpt
     438              : !!***
     439              : 
     440              : !!****f* m_invovl/destroy_invovl
     441              : !! NAME
     442              : !! destroy_invovl
     443              : !!
     444              : !! FUNCTION
     445              : !! Destruction of the invovl_kpt array
     446              : !!
     447              : !! INPUTS
     448              : !! nkpt= number of k-points
     449              : !!
     450              : !! SOURCE
     451          105 :  subroutine destroy_invovl(nkpt, gpu_option)
     452              : 
     453              :    integer, intent(in) :: nkpt
     454              :    integer, intent(in) :: gpu_option
     455              :    integer :: ikpt
     456              : 
     457              : ! *************************************************************************
     458              : 
     459              :   ! TODO add cycling if kpt parallelism
     460         1299 :   do ikpt=1,nkpt
     461         1194 :     if(invovl_kpt(ikpt)%nprojs == -1) then
     462              :       ! write(0, *) 'ERROR invovl_kpt is unallocated'
     463              :       cycle
     464              :     end if
     465         1299 :     call destroy_invovl_ikpt(ikpt, gpu_option)
     466              :   end do
     467              : 
     468         1299 :   ABI_FREE(invovl_kpt)
     469              : 
     470          105 :  end subroutine destroy_invovl
     471              : !!***
     472              : 
     473              : !!****f* m_invovl/make_invovl
     474              : !! NAME
     475              : !! make_invovl
     476              : !!
     477              : !! FUNCTION
     478              : !! Builds of the invovl structure
     479              : !!
     480              : !! INPUTS
     481              : !!
     482              : !! SOURCE
     483              : 
     484        16527 : subroutine make_invovl(ham, dimffnl, ffnl, ph3d, mpi_enreg)
     485              : 
     486              :  use m_abi_linalg
     487              : 
     488              :  type(gs_hamiltonian_type),intent(in), target :: ham
     489              :  integer, intent(in) :: dimffnl
     490              :  real(dp),intent(in) :: ffnl(ham%npw_k,dimffnl,ham%lmnmax,ham%ntypat)
     491              :  real(dp),intent(in) :: ph3d(2,ham%npw_k,ham%matblk)
     492              :  type(mpi_type) :: mpi_enreg
     493              : 
     494        33054 :  real(dp) :: atom_projs(2, ham%npw_k, ham%lmnmax)
     495        33054 :  real(dp) :: temp(ham%npw_k)
     496        16527 :  complex(dp), allocatable :: work(:)
     497        16527 :  real(dp), allocatable,target :: projs(:,:,:)
     498        16527 :  real(dp), allocatable :: gram_proj(:,:,:)
     499        16527 :  integer, allocatable :: ipiv(:)
     500              : 
     501              :  integer :: itypat, ilmn, nlmn, jlmn, ia, iaph3d, shift
     502              :  integer :: il, ilm, jlm, ipw, info, ierr, cplx
     503              :  integer :: ikpt_this_proc,cplex_dij
     504              :  logical :: parity
     505              :  real(dp) :: tsec(2)
     506              : #if defined(HAVE_FC_ISO_C_BINDING) && defined(HAVE_GPU_CUDA)
     507              :  character(len=500) :: message
     508              : #endif
     509              :  character :: blas_transpose
     510              : 
     511              :  type(invovl_kpt_type), pointer :: invovl
     512              : #if defined(HAVE_FC_ISO_C_BINDING) && defined(HAVE_GPU_CUDA)
     513              :  type(invovl_kpt_gpu_type) :: invovl_gpu
     514              : #endif
     515        33054 :  integer :: array_nprojs_pp(mpi_enreg%nproc_fft)
     516              :  integer :: iproc, slice_size
     517        16527 :  real(dp), allocatable :: gramwork(:,:,:)
     518              : #ifdef HAVE_OPENMP_OFFLOAD
     519              :  real(dp), ABI_CONTIGUOUS pointer :: invovl_gram_projs(:,:,:)
     520              : #endif
     521              : 
     522              :  integer, parameter :: timer_mkinvovl = 1620, timer_mkinvovl_build_d = 1621, timer_mkinvovl_build_ptp = 1622
     523              : 
     524              : ! *************************************************************************
     525              : 
     526              :  !! S = 1 + PDP', so
     527              :  !! S^-1 = 1 + P inv_s_projs P', with
     528              :  !! inv_s_projs = - (D^-1 + P'*P)^-1
     529              : 
     530              :  ABI_NVTX_START_RANGE(NVTX_MAKE_INVOVL)
     531              : 
     532        16527 :  if(ham%istwf_k == 1) then
     533        16399 :    cplx = 2
     534        16399 :    blas_transpose = 'c'
     535              :  else
     536          128 :    cplx = 1
     537          128 :    blas_transpose = 't'
     538              :  end if
     539              : 
     540        16527 :  ikpt_this_proc=bandfft_kpt_get_ikpt()
     541        16527 :  invovl => invovl_kpt(ikpt_this_proc)
     542              : 
     543        16527 :  if(invovl%nprojs /= -1) then
     544              :    ! We have been here before, cleanup before remaking
     545        15736 :    call destroy_invovl_ikpt(ikpt_this_proc, ham%gpu_option)
     546              :  end if
     547              : 
     548        16527 :  iaph3d = 1
     549              : 
     550        16527 :  call timab(timer_mkinvovl,1,tsec)
     551        16527 :  call timab(timer_mkinvovl_build_d,1,tsec)
     552              : 
     553              :  ! build nprojs
     554        16527 :  invovl%nprojs = 0
     555        33512 :  do itypat=1,ham%ntypat
     556       169762 :    invovl%nprojs = invovl%nprojs + count(ham%indlmn(3,:,itypat)>0)*ham%nattyp(itypat)
     557              :  end do
     558              : 
     559        66108 :  ABI_MALLOC(projs, (2, ham%npw_k, invovl%nprojs))
     560        99162 :  ABI_MALLOC(invovl%inv_sij, (cplx, ham%lmnmax, ham%lmnmax, ham%ntypat))
     561        99162 :  ABI_MALLOC(invovl%inv_s_approx, (cplx, ham%lmnmax, ham%lmnmax, ham%ntypat))
     562              :  ! workspace for inversion
     563        49581 :  ABI_MALLOC(ipiv, (ham%lmnmax))
     564        49581 :  ABI_MALLOC(work, (64*ham%lmnmax))
     565              : 
     566    131483215 :  projs = zero
     567      3443358 :  invovl%inv_sij = zero
     568      3443358 :  invovl%inv_s_approx = zero
     569              : 
     570              :  shift = 0
     571        33512 :  do itypat = 1, ham%ntypat
     572       153235 :    nlmn = count(ham%indlmn(3,:,itypat)>0)
     573        16985 :    if (size(ham%sij(:,itypat))==ham%lmnmax*(ham%lmnmax+1)/2) then
     574              :      cplex_dij = 1
     575          176 :    else if (size(ham%sij(:,itypat))==ham%lmnmax*(ham%lmnmax+1)) then
     576              :      cplex_dij = 2
     577              :    else
     578            0 :      ABI_ERROR('sij size not recognize')
     579              :    end if
     580              :    !! unpack ham%sij into inv_sij
     581       153215 :    do jlmn = 1, nlmn
     582       136230 :      if (cplex_dij==1) then
     583       134822 :        invovl%inv_sij(1, jlmn, jlmn, itypat) = ham%sij(jlmn*(jlmn-1)/2 + jlmn, itypat)
     584              :      else
     585         1408 :        invovl%inv_sij(1, jlmn, jlmn, itypat) = ham%sij(jlmn*(jlmn-1) + 2*jlmn-1, itypat)
     586              :      end if
     587       633170 :      do ilmn = 1, jlmn-1
     588       616185 :        if (cplex_dij==1) then
     589       475027 :          invovl%inv_sij(1, ilmn, jlmn, itypat) = ham%sij(jlmn*(jlmn-1)/2 + ilmn, itypat)
     590       475027 :          invovl%inv_sij(1, jlmn, ilmn, itypat) = ham%sij(jlmn*(jlmn-1)/2 + ilmn, itypat)
     591              :        else
     592         4928 :          invovl%inv_sij(1, ilmn, jlmn, itypat) = ham%sij(jlmn*(jlmn-1) + 2*ilmn-1, itypat)
     593         4928 :          invovl%inv_sij(1, jlmn, ilmn, itypat) = ham%sij(jlmn*(jlmn-1) + 2*ilmn-1, itypat)
     594              : 
     595              :        end if
     596              :      end do
     597              :    end do
     598              : 
     599              :    ! Invert sij
     600        16985 :    if(cplx == 2) then
     601        16729 :      call ZHETRF('U', nlmn, invovl%inv_sij(:,:,:,itypat), ham%lmnmax, ipiv, work, (64*nlmn), info)
     602        16729 :      call ZHETRI('U', nlmn, invovl%inv_sij(:,:,:,itypat), ham%lmnmax, ipiv, work, info)
     603              :    else
     604          256 :      call DSYTRF('U', nlmn, invovl%inv_sij(:,:,:,itypat), ham%lmnmax, ipiv, work, (64*nlmn), info)
     605          256 :      call DSYTRI('U', nlmn, invovl%inv_sij(:,:,:,itypat), ham%lmnmax, ipiv, work, info)
     606              :    end if
     607              :    ! complete the matrix
     608       153215 :    do ilm=1, nlmn
     609       633170 :      do jlm=1, ilm-1
     610       616185 :        invovl%inv_sij(1,ilm,jlm,itypat) =  invovl%inv_sij(1,jlm,ilm,itypat)
     611              :      end do
     612              :    end do
     613              : 
     614              :    !! loop on atoms to build atom_projs and fill projs, s_projs
     615        66891 :    do ia = 1, ham%nattyp(itypat)
     616              : 
     617              :      !! build atom_projs, from opernlb
     618              :      !! P = 4pi/sqrt(ucvol)* conj(diag(ph3d)) * ffnl * diag(parity), with parity = (-i)^l
     619    131566067 :      atom_projs(:,:,:) = zero
     620              : 
     621              :      ! start from 4pi/sqrt(ucvol)*ffnl
     622              :      ! atom_projs(1, :, 1:nlmn) = four_pi/sqrt(ham%ucvol) * ffnl(:, 1, 1:nlmn)
     623              :      ! TODO vectorize (DCOPY with stride)
     624      5013388 :      do ipw=1, ham%npw_k
     625     48745330 :        atom_projs(1,ipw, 1:nlmn) = four_pi/sqrt(ham%ucvol) * ffnl(ipw, 1, 1:nlmn, itypat)
     626              :      end do
     627              : 
     628              :      ! multiply by (-i)^l
     629       304241 :      do ilmn=1,nlmn
     630       270862 :        il=mod(ham%indlmn(1,ilmn, itypat),4);
     631       270862 :        parity=(mod(il,2)==0)
     632       270862 :        if (il>1) then
     633              :          ! multiply by -1
     634     11679440 :          atom_projs(:,:,ilmn) = -atom_projs(:,:,ilmn)
     635              :        end if
     636       304241 :        if(.not. parity) then
     637              :          ! multiply by -i
     638     30080328 :          temp = atom_projs(2,:,ilmn)
     639     30080328 :          atom_projs(2,:,ilmn) = -atom_projs(1,:,ilmn)
     640     30080328 :          atom_projs(1,:,ilmn) =  temp
     641              :        end if
     642              :      end do
     643              : 
     644              :      ! multiply by conj(ph3d)
     645       304241 :      do ilmn=1,nlmn
     646     44002804 :        temp = atom_projs(1, :, ilmn)
     647     44002804 :        atom_projs(1, :, ilmn) = atom_projs(1, :, ilmn) * ph3d(1, :, iaph3d) + atom_projs(2, :, ilmn) * ph3d(2, :, iaph3d)
     648     44036183 :        atom_projs(2, :, ilmn) = atom_projs(2, :, ilmn) * ph3d(1, :, iaph3d) - temp                   * ph3d(2, :, iaph3d)
     649              :      end do
     650              : 
     651              :      ! me_g0 trick
     652        33379 :      if(ham%istwf_k == 2 .and. mpi_enreg%me_g0 == 1) then
     653          126 :        atom_projs(1,1,:) = atom_projs(1,1,:) / sqrt2
     654          126 :        atom_projs(2,1,:) = zero
     655              :      end if
     656        33379 :      if(ham%istwf_k > 1) then
     657       198240 :        atom_projs(:,:,:) = atom_projs(:,:,:) * sqrt2
     658              :      end if
     659              : 
     660              : 
     661              :      !! atom_projs and typat_s_projs are built, copy them to projs and inv_s_projs
     662    131500067 :      projs(:, :, shift+1:shift+nlmn) = atom_projs(:, :, 1:nlmn)
     663        33379 :      shift = shift + nlmn
     664              : 
     665              :      ! build inv_s_approx = (D^-1+PtP)^-1 restricted to a single atom block
     666              :      ! can be optimized (real, build directly from ffnl)
     667        33379 :      if(ia == 1) then
     668              :        ! D^-1
     669      1249311 :        invovl%inv_s_approx(1, :, :, itypat) = invovl%inv_sij(1, :, :, itypat)
     670              :        ! + PtP
     671        84885 :        ABI_MALLOC(gram_proj, (cplx, nlmn, nlmn))
     672              :        call abi_xgemm(blas_transpose,'N', nlmn, nlmn, (3-cplx)*ham%npw_k, cone, atom_projs(:,:,1), (3-cplx)*ham%npw_k, &
     673        16977 : &                     atom_projs(:,:,1), (3-cplx)*ham%npw_k, czero, gram_proj(:,:,1), nlmn,x_cplx=cplx)
     674        16977 :        call xmpi_sum(gram_proj,mpi_enreg%comm_bandspinorfft,ierr)
     675      3423643 :        invovl%inv_s_approx(:,1:nlmn,1:nlmn,itypat) = invovl%inv_s_approx(:,1:nlmn,1:nlmn,itypat) + gram_proj(:,:,:)
     676        16977 :        ABI_FREE(gram_proj)
     677              :        ! ^-1
     678        16977 :        if(cplx == 2) then
     679        16721 :          call ZHETRF('U', nlmn, invovl%inv_s_approx(:,:,:,itypat), ham%lmnmax, ipiv, work, (64*nlmn), info)
     680        16721 :          call ZHETRI('U', nlmn, invovl%inv_s_approx(:,:,:,itypat), ham%lmnmax, ipiv, work, info)
     681              :        else
     682          256 :          call DSYTRF('U', nlmn, invovl%inv_s_approx(:,:,:,itypat), ham%lmnmax, ipiv, work, (64*nlmn), info)
     683          256 :          call DSYTRI('U', nlmn, invovl%inv_s_approx(:,:,:,itypat), ham%lmnmax, ipiv, work, info)
     684              :        end if
     685              :        ! complete lower triangle of matrix
     686       170120 :        do ilm=1, nlmn
     687       632874 :          do jlm=1, ilm-1
     688       479731 :            invovl%inv_s_approx(1, ilm, jlm, itypat) =  invovl%inv_s_approx(1, jlm, ilm, itypat)
     689       615897 :            if(cplx == 2) then
     690       472563 :              invovl%inv_s_approx(2, ilm, jlm, itypat) =  -invovl%inv_s_approx(2, jlm, ilm, itypat)
     691              :            end if
     692              :          end do
     693              :        end do
     694              :      end if
     695              : 
     696        50364 :      iaph3d = iaph3d + 1
     697              :    end do
     698              :  end do
     699        16527 :  ABI_FREE(ipiv)
     700        16527 :  ABI_FREE(work)
     701              : 
     702        16527 :  call timab(timer_mkinvovl_build_d, 2, tsec)
     703        16527 :  call timab(timer_mkinvovl_build_ptp, 1, tsec)
     704              : 
     705              :  ! Compute P'P one column slice at a time (might be too big to fit in one proc)
     706        16527 :  if(mpi_enreg%paral_kgb == 1) then
     707              :    ! Split the work evenly the fft processors
     708        13464 :    array_nprojs_pp(:) = invovl%nprojs / mpi_enreg%nproc_fft
     709              :    ! not enough work, there's MOD(nprojs,mpi_enreg%nproc_fft) tasks left
     710              :    ! assign them to the first ones
     711         6728 :    array_nprojs_pp(1:MOD(invovl%nprojs,mpi_enreg%nproc_fft)) = array_nprojs_pp(1:MOD(invovl%nprojs,mpi_enreg%nproc_fft)) + 1
     712              :  else
     713        19598 :    array_nprojs_pp = invovl%nprojs
     714              :  end if
     715        82635 :  ABI_MALLOC(invovl%gram_projs, (cplx,invovl%nprojs,array_nprojs_pp(mpi_enreg%me_fft+1)))
     716        16527 :  shift = 0
     717        16527 :  if (ham%gpu_option==ABI_GPU_OPENMP .and. mpi_enreg%nproc_fft==1) then
     718              : #ifdef HAVE_OPENMP_OFFLOAD
     719              :    ! compute gram_projs in one GEMM, only one FFT proc expected in GPU mode
     720              :    slice_size = array_nprojs_pp(1)
     721              :    invovl_gram_projs   => invovl%gram_projs
     722              :    !$OMP TARGET ENTER DATA MAP(alloc:invovl_gram_projs)
     723              :    !$OMP TARGET ENTER DATA MAP(to:projs)
     724              : 
     725              :    call abi_xgemm(blas_transpose,'N', invovl%nprojs, slice_size, (3-cplx)*ham%npw_k, cone, &
     726              :    &              projs, (3-cplx)*ham%npw_k, &
     727              :    &              projs, (3-cplx)*ham%npw_k, czero, &
     728              :    &              invovl_gram_projs, invovl%nprojs, &
     729              :    &              x_cplx=cplx, gpu_option=ham%gpu_option)
     730              :    call xmpi_sum(invovl%gram_projs,mpi_enreg%comm_band,ierr,use_omp_map=.true.)
     731              :    !$OMP TARGET EXIT DATA MAP(from:invovl_gram_projs)
     732              :    !$OMP TARGET EXIT DATA MAP(delete:projs)
     733              : #endif
     734              :  else
     735        33062 :    do iproc = 1, mpi_enreg%nproc_fft
     736              :      ! compute local contribution to slice iproc of gram_projs
     737        16535 :      slice_size = array_nprojs_pp(iproc)
     738        82675 :      ABI_MALLOC(gramwork, (cplx,invovl%nprojs,slice_size))
     739              :      call abi_xgemm(blas_transpose,'N', invovl%nprojs, slice_size, (3-cplx)*ham%npw_k, cone,&
     740              :      &              projs(:,:,1), (3-cplx)*ham%npw_k, &
     741              :      &              projs(:, :, shift+1), (3-cplx)*ham%npw_k, czero, &
     742              :      &              gramwork(:,:,1), invovl%nprojs,&
     743        16535 :      &              x_cplx=cplx)
     744        16535 :      shift = shift + slice_size
     745              :      ! reduce on proc i
     746        16535 :      call xmpi_sum_master(gramwork, iproc-1, mpi_enreg%comm_fft, ierr)
     747        16535 :      if(iproc == mpi_enreg%me_fft+1) then
     748     21800344 :        invovl%gram_projs = gramwork
     749              :      end if
     750        33062 :      ABI_FREE(gramwork)
     751              :    end do
     752        16527 :    call xmpi_sum(invovl%gram_projs,mpi_enreg%comm_band,ierr)
     753              :  end if
     754              : 
     755        16527 :  call timab(timer_mkinvovl_build_ptp, 2, tsec)
     756        16527 :  call timab(timer_mkinvovl,2,tsec)
     757              : 
     758        16527 :  ABI_FREE(projs)
     759              : 
     760              : #if defined(HAVE_FC_ISO_C_BINDING) && defined(HAVE_GPU_CUDA)
     761              : 
     762              :  ! upload inverse overlap matrices (sij and s_approx) to GPU memory
     763              :  if (ham%gpu_option==ABI_GPU_LEGACY .or. ham%gpu_option==ABI_GPU_KOKKOS) then
     764              :    ! allocate memory for sij and s_approx on GPU
     765              :    write(message,'(a,a,i12,a,a,i6,a,a,i6,a,a,es12.4,a)') &
     766              :      & 'Allocate GPU memory for inverse overlap computations (sij and s_approx) : ',&
     767              :      & 'nprojs=',invovl%nprojs,ch10,&
     768              :      & 'nlmnax=',ham%lmnmax,ch10,&
     769              :      & 'ntypat=',ham%ntypat,ch10,&
     770              :      & 'gram_projs_gpu_size (GBytes)=',1e-9*cplx*invovl%nprojs*invovl%nprojs*dp,ch10
     771              :    call wrtout(std_out,message,'COLL')
     772              :    call f_gpu_apply_invovl_matrix_alloc(cplx, invovl%nprojs, ham%lmnmax, ham%ntypat, 0)
     773              : 
     774              :    invovl_gpu = make_invovl_kpt_gpu(invovl)
     775              :    call f_upload_inverse_overlap(invovl_gpu, cplx, invovl%nprojs, ham%lmnmax, ham%ntypat)
     776              :    write(message,*) 'Invovl uploaded to GPU memory'
     777              :    call wrtout(std_out,message,'COLL')
     778              :  end if
     779              : 
     780              : #endif
     781              : 
     782              : #ifdef HAVE_OPENMP_OFFLOAD
     783              :  if (ham%gpu_option==ABI_GPU_OPENMP) then
     784              :    call refresh_invovl_ompgpu_kpt(ikpt_this_proc)
     785              :  end if
     786              : #endif
     787              : 
     788              : ! LB-10/06/24: This message is too verbose on some cases (for example many k-points)
     789              : ! write(message,*) 'Invovl built'
     790              : ! call wrtout(std_out,message,'COLL')
     791              : 
     792              :  ABI_NVTX_END_RANGE()
     793              : 
     794        16527 : end subroutine make_invovl
     795              : !!***
     796              : 
     797              : !!****f* m_invovl/apply_invovl
     798              : !! NAME
     799              : !! apply_invovl
     800              : !!
     801              : !! FUNCTION
     802              : !! Applies the inverse of the overlap matrix to cwavef
     803              : !!
     804              : !! INPUTS
     805              : !!
     806              : !! SOURCE
     807              : 
     808       117798 : subroutine apply_invovl(ham, cwavef, sm1cwavef, cwaveprj, npw, ndat, mpi_enreg, nspinor, block_sliced)
     809              : 
     810              : #if defined(HAVE_FC_ISO_C_BINDING) && defined(HAVE_GPU_CUDA)
     811              :   use, intrinsic :: iso_c_binding
     812              : #endif
     813              : 
     814              :   ! args
     815              :   type(gs_hamiltonian_type), intent(in), target :: ham
     816              :   integer, intent(in) :: npw, ndat
     817              :   integer, intent(in) :: nspinor
     818              :   integer, intent(in) :: block_sliced
     819              :   real(dp), intent(inout), target :: cwavef(2, npw*nspinor*ndat) ! TODO should be in, fix nonlop
     820              :   type(mpi_type) :: mpi_enreg
     821              :   real(dp), intent(inout), target :: sm1cwavef(2, npw*nspinor*ndat)
     822              :   type(pawcprj_type), intent(inout) :: cwaveprj(:,:)
     823              : 
     824       117798 :   real(dp),allocatable, target :: proj(:,:,:), sm1proj(:,:,:), PtPsm1proj(:,:,:)
     825              : 
     826              :   ! used to pass proj dimensions to cuda
     827              :   integer(kind=c_int32_t) :: proj_dim(3)
     828              :   integer(kind=c_int32_t) :: nattyp_dim
     829              :   integer(kind=c_int32_t) :: indlmn_dim(3)
     830              : #if defined(HAVE_GPU_CUDA) && defined(HAVE_KOKKOS) && defined(HAVE_YAKL)
     831              :   integer(kind=c_int32_t) :: cwavef_size
     832              : #endif
     833              : 
     834              :   integer :: idat, iatom, nlmn, shift
     835              :   real(dp) :: tsec(2)
     836              : 
     837              :   integer :: choice, cpopt, paw_opt , cplx
     838              :   character :: blas_transpose
     839       117798 :   type(pawcprj_type),allocatable :: cwaveprj_in(:,:)
     840              : 
     841              :   integer :: ikpt_this_proc
     842              :   ! dummies
     843       235596 :   real(dp) :: enlout(ndat), lambda_block(1), gvnlxc(1,1)
     844              :   integer, parameter :: nnlout = 0, idir = 0, signs = 2
     845              : 
     846              :   type(invovl_kpt_type), pointer :: invovl
     847              : 
     848              :   ! *************************************************************************
     849              : 
     850              :   if(ham%gpu_option==ABI_GPU_OPENMP) then
     851              : #ifdef HAVE_OPENMP_OFFLOAD
     852              :     call apply_invovl_ompgpu(ham, cwavef, sm1cwavef, cwaveprj, npw, ndat, mpi_enreg, nspinor, block_sliced)
     853              :     return
     854              : #endif
     855              :   end if
     856              : 
     857              :   ABI_NVTX_START_RANGE(NVTX_INVOVL_PREP)
     858       117798 :   ikpt_this_proc=bandfft_kpt_get_ikpt()
     859       117798 :   invovl => invovl_kpt(ikpt_this_proc)
     860              : 
     861       117798 :   if(ham%istwf_k == 1) then
     862       100987 :     cplx = 2
     863       100987 :     blas_transpose = 'c'
     864              :   else
     865        16811 :     cplx = 1
     866        16811 :     blas_transpose = 't'
     867              :   end if
     868              : 
     869       588990 :   ABI_MALLOC(proj,       (cplx,invovl%nprojs,nspinor*ndat))
     870       471192 :   ABI_MALLOC(sm1proj,    (cplx,invovl%nprojs,nspinor*ndat))
     871       471192 :   ABI_MALLOC(PtPsm1proj, (cplx,invovl%nprojs,nspinor*ndat))
     872     48406358 :   proj = zero
     873     48406358 :   sm1proj = zero
     874     48406358 :   PtPsm1proj = zero
     875              : 
     876       117798 :   proj_dim = (/ size(proj,1), size(proj,2), size(proj,3) /)
     877              : 
     878       117798 :   nattyp_dim = size(ham%nattyp)
     879              : 
     880       117798 :   indlmn_dim = (/ size(ham%indlmn,1), size(ham%indlmn,2), size(ham%indlmn,3) /)
     881              : 
     882              : #if defined(HAVE_GPU_CUDA) && defined(HAVE_FC_ISO_C_BINDING)
     883              : 
     884              :   !! memory allocation of data used in solve_inner_gpu
     885              :   !! note : this is actually done only once
     886              :   if (ham%gpu_option==ABI_GPU_LEGACY .or. ham%gpu_option==ABI_GPU_KOKKOS) then
     887              : 
     888              : #ifdef DEBUG_VERBOSE_GPU
     889              :     if(xmpi_comm_rank(xmpi_world) == 0) then
     890              :       call check_gpu_mem("gpu_apply_invovl_inner_alloc begin")
     891              :     end if
     892              : #endif
     893              : 
     894              :     ! make sure to use sizes from apply_invovl
     895              :     call f_gpu_apply_invovl_inner_alloc(proj_dim, ham%ntypat, 0)
     896              : 
     897              : #ifdef DEBUG_VERBOSE_GPU
     898              :     if(xmpi_comm_rank(xmpi_world) == 0) then
     899              :       call check_gpu_mem("gpu_apply_invovl_inner_alloc end")
     900              :     end if
     901              : #endif
     902              : 
     903              :     ! TODO find a better place to put that initialization
     904              :     call f_gpu_init_invovl_data(indlmn_dim, c_loc(ham%indlmn(1,1,1)))
     905              : 
     906              :   end if
     907              : 
     908              : #endif
     909              : 
     910              : 
     911       117798 :   call timab(timer_apply_inv_ovl_opernla, 1, tsec)
     912              : 
     913              :   ! cwaveprj may be dummy or unused if gemm nonlop is turned on
     914       353394 :   if((.not. gemm_nonlop_use_gemm) .or. size(cwaveprj) > 1) then
     915      2996328 :     ABI_MALLOC(cwaveprj_in, (ham%natom,nspinor*ndat))
     916       117510 :     call pawcprj_alloc(cwaveprj_in,0,ham%dimcprj)
     917              :   else
     918          864 :     ABI_MALLOC(cwaveprj_in, (1,1))
     919          288 :     call pawcprj_alloc(cwaveprj_in,0,(/1/))
     920              :   end if
     921              :   ABI_NVTX_END_RANGE()
     922              : 
     923              :   ! get the cprj
     924              :   ABI_NVTX_START_RANGE(NVTX_INVOVL_NONLOP1)
     925       117798 :   choice = 0 ! only compute cprj, nothing else
     926       117798 :   cpopt = 0 ! compute and save cprj
     927       117798 :   paw_opt = 3 ! S nonlocal operator
     928       117798 :   if (mpi_enreg%paral_kgb==1) then
     929              :     call prep_nonlop(choice,cpopt,cwaveprj_in,enlout,ham,idir,lambda_block,ndat,mpi_enreg,&
     930              :       &                   nnlout,paw_opt,signs,sm1cwavef,tim_nonlop,cwavef,gvnlxc,&
     931              :       &                   already_transposed=.true.,&
     932              :       &                   gpu_option=ham%gpu_option,&
     933        62460 :       &                   vectproj=proj)
     934              :   else
     935              :     call nonlop(choice,cpopt,cwaveprj_in,enlout,ham,idir,lambda_block,mpi_enreg,ndat,&
     936        55338 :       &              nnlout,paw_opt,signs,sm1cwavef,tim_nonlop,cwavef,gvnlxc,vectproj=proj)
     937              :   end if
     938              :   ABI_NVTX_END_RANGE()
     939              : 
     940       117798 :   call timab(timer_apply_inv_ovl_opernla, 2, tsec)
     941       117798 :   call timab(timer_apply_inv_ovl_inv_s, 1, tsec)
     942              : 
     943              :   ! If using GEMM nonlop, proj array is used directly instead of writing in cwaveprj_in content, so skip this copy
     944       117798 :   if(.not. gemm_nonlop_use_gemm) then
     945              :     ! copy cwaveprj_in to proj(:,:)
     946       901430 :     do idat=1, ndat*nspinor
     947       784208 :       shift = 0
     948      2638326 :       do iatom = 1, ham%natom
     949      1736896 :         nlmn = cwaveprj_in(iatom, idat)%nlmn
     950     49114240 :         proj(1:cplx, shift+1:shift+nlmn, idat) = cwaveprj_in(iatom, idat)%cp(1:cplx, 1:nlmn)
     951      2521104 :         shift = shift + nlmn
     952              :       end do
     953              :     end do
     954              :   end if
     955              : 
     956              :   !multiply by S^1
     957              :   ABI_NVTX_START_RANGE(NVTX_INVOVL_INNER)
     958              :   ! TODO : when solve_inner_gpu is ready, update the following to activate GPU computation
     959       117798 :   if (ham%gpu_option == ABI_GPU_LEGACY .or. ham%gpu_option==ABI_GPU_KOKKOS) then
     960              : 
     961              : #if defined(HAVE_FC_ISO_C_BINDING) && defined(HAVE_GPU_CUDA)
     962              : 
     963              :     if (mpi_enreg%nproc_fft /= 1) then
     964              :       ABI_ERROR("[66_wfs/m_invovl.F90:apply_invovl] nproc_fft must be 1, when GPU/CUDA is activated")
     965              :     end if
     966              : 
     967              :     call f_solve_inner_gpu(proj_dim, c_loc(proj(1,1,1)), &
     968              :       & c_loc(sm1proj(1,1,1)), c_loc(PtPsm1proj(1,1,1)), &
     969              :       & nattyp_dim, c_loc(ham%nattyp(1)), ham%ntypat, &
     970              :       & ham%lmnmax, cplx, block_sliced)
     971              : 
     972              : #endif
     973              : 
     974              :   else
     975              : 
     976       117798 :     call solve_inner(invovl, ham, cplx, mpi_enreg, proj, ndat*nspinor, sm1proj, PtPsm1proj, block_sliced)
     977     48406358 :     sm1proj = - sm1proj
     978     48406358 :     PtPsm1proj = - PtPsm1proj
     979              :   end if
     980              : 
     981              :   ABI_NVTX_END_RANGE()
     982              : 
     983              :   ! If using GEMM nonlop, sm1proj array is used directly instead of reading cwaveprj content, so skip this copy
     984       117798 :   if(.not. gemm_nonlop_use_gemm) then
     985              :     ! copy sm1proj to cwaveprj(:,:)
     986       901430 :     do idat=1, ndat*nspinor
     987       784208 :       shift = 0
     988      2638326 :       do iatom = 1, ham%natom
     989      1736896 :         nlmn = cwaveprj(iatom, idat)%nlmn
     990     49114240 :         cwaveprj(iatom, idat)%cp(1:cplx, 1:nlmn) = sm1proj(1:cplx, shift+1:shift+nlmn, idat)
     991      2521104 :         shift = shift + nlmn
     992              :       end do
     993              :     end do
     994              :   end if
     995       117798 :   call timab(timer_apply_inv_ovl_inv_s, 2, tsec)
     996       117798 :   call timab(timer_apply_inv_ovl_opernlb, 1, tsec)
     997              : 
     998              :   ! get the corresponding wf
     999              :   ABI_NVTX_START_RANGE(NVTX_INVOVL_NONLOP2)
    1000       117798 :   cpopt = 2 ! reuse cprj
    1001       117798 :   choice = 7 ! get wf from cprj, without the application of S
    1002              :   paw_opt = 3
    1003       117798 :   if (mpi_enreg%paral_kgb==1) then
    1004              :     call prep_nonlop(choice,cpopt,cwaveprj,enlout,ham,idir,lambda_block,ndat,mpi_enreg,nnlout,&
    1005              :       &                   paw_opt,signs,sm1cwavef,tim_nonlop,cwavef,gvnlxc,already_transposed=.true.,&
    1006        62460 :       &                   gpu_option=ham%gpu_option,vectproj=sm1proj)
    1007              :   else
    1008              :     call nonlop(choice,cpopt,cwaveprj,enlout,ham,idir,lambda_block,mpi_enreg,ndat,nnlout,paw_opt,&
    1009        55338 :       &              signs,sm1cwavef,tim_nonlop,cwavef,gvnlxc,vectproj=sm1proj)
    1010              :   end if
    1011              :   ABI_NVTX_END_RANGE()
    1012              : 
    1013       117798 :   call timab(timer_apply_inv_ovl_opernlb, 2, tsec)
    1014              : 
    1015              :   ABI_NVTX_START_RANGE(NVTX_INVOVL_POST2)
    1016       353394 :   if(size(cwaveprj) > 1) then
    1017              :     ! copy PtPSm1proj to cwaveprj(:,:)
    1018       903446 :     do idat=1, ndat*nspinor
    1019       785936 :       shift = 0
    1020      2643798 :       do iatom = 1, ham%natom
    1021      1740352 :         nlmn = cwaveprj(iatom, idat)%nlmn
    1022     49200640 :         cwaveprj(iatom, idat)%cp(1:cplx, 1:nlmn) = PtPsm1proj(1:cplx, shift+1:shift+nlmn, idat)
    1023      2526288 :         shift = shift + nlmn
    1024              :       end do
    1025              :     end do
    1026              :     !cwaveprj_in is empty if GEMM nonlop is being used, so populate it here
    1027       117510 :     if(gemm_nonlop_use_gemm) then
    1028         2016 :       do idat=1, ndat*nspinor
    1029         1728 :         shift = 0
    1030         5472 :         do iatom = 1, ham%natom
    1031         3456 :           nlmn = cwaveprj_in(iatom, idat)%nlmn
    1032        86400 :           cwaveprj_in(iatom, idat)%cp(1:cplx, 1:nlmn) = proj(1:cplx, shift+1:shift+nlmn, idat)
    1033         5184 :           shift = shift + nlmn
    1034              :         end do
    1035              :       end do
    1036              :     end if
    1037       117510 :     call pawcprj_axpby(one, one, cwaveprj_in, cwaveprj)
    1038              :   end if
    1039       117798 :   call pawcprj_free(cwaveprj_in)
    1040      1858438 :   ABI_FREE(cwaveprj_in)
    1041              : 
    1042       117798 :   if (ham%gpu_option == ABI_GPU_LEGACY .or. ham%gpu_option==ABI_GPU_KOKKOS) then
    1043              : #if defined(HAVE_GPU_CUDA) && defined(HAVE_KOKKOS) && defined(HAVE_YAKL)
    1044              :     cwavef_size = 2*npw*nspinor*ndat
    1045              :     call add_array_kokkos(c_loc(sm1cwavef), c_loc(cwavef), cwavef_size)
    1046              : #endif
    1047              :   else
    1048    389627622 :     sm1cwavef = cwavef + sm1cwavef
    1049              :   end if
    1050              : 
    1051              :   ABI_NVTX_END_RANGE()
    1052              : 
    1053       117798 :   ABI_FREE(proj)
    1054       117798 :   ABI_FREE(sm1proj)
    1055       117798 :   ABI_FREE(PtPsm1proj)
    1056              : 
    1057       117798 : end subroutine apply_invovl
    1058              : !!***
    1059              : 
    1060              : !!****f* m_invovl/solve_inner
    1061              : !! NAME
    1062              : !! solve_inner
    1063              : !!
    1064              : !! FUNCTION
    1065              : !! Helper function: iteratively solves the inner system
    1066              : !!
    1067              : !! INPUTS
    1068              : !!
    1069              : !! SOURCE
    1070       117798 : subroutine solve_inner(invovl, ham, cplx, mpi_enreg, proj, ndat, sm1proj, PtPsm1proj, block_sliced)
    1071              : 
    1072              :  use m_abi_linalg
    1073              : 
    1074              :  integer,intent(in) :: ndat,cplx
    1075              :  type(invovl_kpt_type), intent(in) :: invovl
    1076              :  real(dp), intent(inout) :: proj(cplx, invovl%nprojs,ndat)
    1077              :  real(dp), intent(inout) :: sm1proj(cplx, invovl%nprojs, ndat)
    1078              :  real(dp), intent(inout) :: PtPsm1proj(cplx, invovl%nprojs, ndat)
    1079       117798 :  real(dp), allocatable :: temp_proj(:,:,:)
    1080              :  type(mpi_type), intent(in) :: mpi_enreg
    1081              :  type(gs_hamiltonian_type),intent(in) :: ham
    1082              :  integer, intent(in) :: block_sliced
    1083              : 
    1084       235596 :  integer :: array_nlmntot_pp(mpi_enreg%nproc_fft)
    1085              :  integer :: nlmntot_this_proc, ibeg, iend, ierr, i, nprojs
    1086       235596 :  real(dp) :: resid(cplx, invovl%nprojs,ndat), precondresid(cplx, invovl%nprojs,ndat)
    1087       235596 :  real(dp) :: normprojs(ndat), errs(ndat), maxerr, previous_maxerr
    1088              :  character(len=500) :: message
    1089              : 
    1090              :  real(dp), parameter :: precision = 1e-16 ! maximum relative error. TODO: use tolwfr ?
    1091              :  real(dp) :: convergence_rate
    1092              :  integer :: additional_steps_to_take
    1093              : 
    1094              : ! *************************************************************************
    1095              : 
    1096       117798 :  nprojs = invovl%nprojs
    1097     48406358 :  normprojs = SUM(SUM(proj**2, 1),1)
    1098       117798 :  call xmpi_sum(normprojs, mpi_enreg%comm_fft, ierr)
    1099              : 
    1100              :  ! Compute work distribution : split nprojs evenly between the fft processors
    1101       117798 :  if(mpi_enreg%paral_kgb == 1) then
    1102       125208 :    array_nlmntot_pp(:) = nprojs / mpi_enreg%nproc_fft
    1103              :    ! not enough work, there's MOD(nprojs,mpi_enreg%nproc_fft) tasks left
    1104              :    ! assign them to the first ones
    1105        62460 :    array_nlmntot_pp(1:MOD(nprojs,mpi_enreg%nproc_fft)) = array_nlmntot_pp(1:MOD(nprojs,mpi_enreg%nproc_fft)) + 1
    1106        62604 :    ibeg = SUM(array_nlmntot_pp(1:mpi_enreg%me_fft)) + 1
    1107        62460 :    iend = ibeg + array_nlmntot_pp(1+mpi_enreg%me_fft) - 1
    1108        62460 :    nlmntot_this_proc = iend - ibeg + 1
    1109              :  else
    1110        55338 :    ibeg = 1
    1111        55338 :    iend = nprojs
    1112        55338 :    nlmntot_this_proc = nprojs
    1113              :  end if
    1114              : 
    1115       588990 :  ABI_MALLOC(temp_proj, (cplx, nlmntot_this_proc, ndat))
    1116              : 
    1117              :  ! first guess for sm1proj
    1118       117798 :  call apply_block(ham, cplx, invovl%inv_s_approx, nprojs, ndat, proj, sm1proj, block_sliced)
    1119              : 
    1120              :  ! Iterative refinement
    1121              :  ! TODO use a more efficient iterative algorithm than iterative refinement, use locking
    1122       117798 :  additional_steps_to_take = -1
    1123      1146942 :  do i=1, 30
    1124              :    ! compute resid = proj - (D^-1 + PtP)sm1proj
    1125      1146942 :    call apply_block(ham, cplx, invovl%inv_sij, nprojs, ndat, sm1proj, resid, block_sliced)
    1126    594890922 :    temp_proj = sm1proj(:,ibeg:iend,:)
    1127              : 
    1128              :    ! compute matrix multiplication : PtPsm1proj(:,:,1) = invovl%gram * temp_proj(:,:,1)
    1129              :    call abi_xgemm('N', 'N', nprojs, ndat, nlmntot_this_proc, cone, &
    1130              :      & invovl%gram_projs(:,:,1), nprojs, &
    1131              :      & temp_proj(:,:,1), nlmntot_this_proc, czero, &
    1132              :      & PtPsm1proj(:,:,1), nprojs, &
    1133      1146942 :      & x_cplx=cplx)
    1134      1146942 :    call xmpi_sum(PtPsm1proj, mpi_enreg%comm_fft, ierr)
    1135    594228108 :    resid = proj - resid - Ptpsm1proj
    1136              :    ! exit check
    1137    594228108 :    errs = SUM(SUM(resid**2, 1),1)
    1138      1146942 :    call xmpi_sum(errs, mpi_enreg%comm_fft, ierr)
    1139              : 
    1140     10262028 :    maxerr = sqrt(MAXVAL(errs/normprojs))
    1141      1146942 :    if(maxerr < precision .or. additional_steps_to_take == 1) then
    1142              :      exit
    1143              :      ! We might stall and never get to the specified precision because of machine errors.
    1144              :      ! If we got to 1e-10, extrapolate convergence rate and determine the number of additional
    1145              :      ! steps to take to reach precision
    1146      1080855 :    else if(maxerr < 1e-10 .and. additional_steps_to_take == -1) then
    1147       117798 :      convergence_rate = -LOG(1e-10) / i
    1148       117798 :      additional_steps_to_take = CEILING(-LOG(precision/1e-10)/convergence_rate) + 1
    1149       963057 :    else if(additional_steps_to_take > 0) then
    1150       411936 :      if(previous_maxerr<maxerr)exit
    1151       360225 :      additional_steps_to_take = additional_steps_to_take - 1
    1152              :    end if
    1153      1029144 :    previous_maxerr=maxerr
    1154              : 
    1155              :    ! add preconditionned residual
    1156      1029144 :    call apply_block(ham, cplx, invovl%inv_s_approx, nprojs, ndat, resid, precondresid, block_sliced)
    1157    545939548 :    sm1proj = sm1proj + precondresid
    1158              :  end do
    1159              : 
    1160       117798 :  if(maxerr >= precision .and. maxerr >= 1e-10) then
    1161            0 :    write(message, *) 'In invovl, max error was', maxerr, ' after 30 iterations'
    1162            0 :    ABI_WARNING(message)
    1163              :  else
    1164              :    ! write(message,'(a,i2,a,es13.5)') 'Iterative solver in invovl finished in ', i, ' iterations, error', maxerr
    1165              :    ! call wrtout(std_out,message,'COLL')
    1166              :  end if
    1167              : 
    1168       117798 :  ABI_FREE(temp_proj)
    1169              : 
    1170       117798 : end subroutine solve_inner
    1171              : !!***
    1172              : 
    1173              : !!****f* m_invovl/apply_block
    1174              : !! NAME
    1175              : !! apply_block
    1176              : !!
    1177              : !! FUNCTION
    1178              : !! Helper function: applies a block-diagonal matrix mat(lmnmax, lmnmax, ntypat)
    1179              : !!
    1180              : !! INPUTS
    1181              : !!
    1182              : !! SOURCE
    1183      2293884 : subroutine apply_block(ham, cplx, mat, nprojs, ndat, x, y, block_sliced)
    1184              : 
    1185              :   use m_abi_linalg
    1186              : 
    1187              :   integer,intent(in) :: ndat, nprojs, cplx
    1188              :   real(dp), intent(inout), target :: x(cplx, nprojs, ndat), y(cplx, nprojs, ndat)
    1189              :   type(gs_hamiltonian_type),intent(in) :: ham
    1190              :   real(dp), intent(in) :: mat(cplx, ham%lmnmax, ham%lmnmax, ham%ntypat)
    1191              :   integer, intent(in) :: block_sliced
    1192              : 
    1193              :   integer :: nlmn, shift, itypat, idat
    1194      2293884 :   real(dp),pointer :: work_x(:,:),work_y(:,:)
    1195              : 
    1196              : ! *************************************************************************
    1197              : 
    1198      2293884 :   if (block_sliced == 1) then
    1199              : 
    1200     18230172 :      do idat = 1, ndat
    1201     15936288 :         shift = 1
    1202     40921364 :         do itypat=1, ham%ntypat
    1203    224473468 :            nlmn = count(ham%indlmn(3,:,itypat)>0)
    1204              :            !! apply mat to all atoms at once
    1205              :            ! perform natom multiplications of size nlmn
    1206              :            ! compute y = mat*x
    1207     22691192 :            work_x => x(:, shift:shift+nlmn*ham%nattyp(itypat)-1, idat)
    1208     22691192 :            work_y => y(:, shift:shift+nlmn*ham%nattyp(itypat)-1, idat)
    1209     22691192 :            if(cplx == 2) then
    1210              :               call ZHEMM('L','U', nlmn, ham%nattyp(itypat), cone, &
    1211              :                    &  mat(:, :, :, itypat), ham%lmnmax, &
    1212              :                    &  work_x, nlmn, czero, &
    1213     19831664 :                    &  work_y, nlmn)
    1214              :            else
    1215              :               call DSYMM('L','U', nlmn, ham%nattyp(itypat), one, &
    1216              :                    &  mat(:, :, :, itypat), ham%lmnmax, &
    1217              :                    &  work_x, nlmn, zero, &
    1218      2859528 :                    &  work_y, nlmn)
    1219              :            end if
    1220     38627480 :            shift = shift + nlmn*ham%nattyp(itypat)
    1221              :         end do
    1222              :      end do
    1223              : 
    1224              :   else ! block_sliced = 0
    1225              : 
    1226              :      shift = 1
    1227            0 :      do itypat=1, ham%ntypat
    1228            0 :         nlmn = count(ham%indlmn(3,:,itypat)>0)
    1229              :         !! apply mat to all atoms at once, all idat at once
    1230              :         ! perform natom multiplications of size nlmn
    1231              :         ! be careful here matrix extracted from x and y are not memory contiguous
    1232              :         ! ==> so in the GPU version we will need to adapt leading dimension
    1233            0 :         if(cplx == 2) then
    1234              :            call ZHEMM('L','U', nlmn, ham%nattyp(itypat)*ndat, cone, &
    1235              :                 &  mat(:, :, :, itypat), ham%lmnmax, &
    1236              :                 &  x(:, 1:nlmn*ham%nattyp(itypat), 1:ndat), nlmn, czero, &
    1237            0 :                 &  y(:, 1:shift+nlmn*ham%nattyp(itypat)-1, 1:ndat), nlmn)
    1238              :         else
    1239              :            call DSYMM('L','U', nlmn, ham%nattyp(itypat)*ndat, one, &
    1240              :                 &  mat(:, :, :, itypat), ham%lmnmax, &
    1241              :                 &  x(:, shift:shift+nlmn*ham%nattyp(itypat)-1, 1:ndat), nlmn, zero, &
    1242            0 :                 &  y(:, shift:shift+nlmn*ham%nattyp(itypat)-1, 1:ndat), nlmn)
    1243              :         end if
    1244            0 :         shift = shift + nlmn*ham%nattyp(itypat)
    1245              :      end do
    1246              : 
    1247              :   end if
    1248              : 
    1249      2293884 : end subroutine apply_block
    1250              : !!***
    1251              : 
    1252            0 :  function invovl_ompgpu_work_mem(ham, ndat) result(req_mem)
    1253              : 
    1254              :    type(gs_hamiltonian_type), intent(in) :: ham
    1255              :    integer, intent(in) :: ndat
    1256              :    integer :: nprojs, cplx, itypat
    1257              :    integer(kind=c_size_t) :: req_mem
    1258              : 
    1259            0 :    nprojs = 0
    1260            0 :    do itypat=1,ham%ntypat
    1261            0 :      nprojs = nprojs + count(ham%indlmn(3,:,itypat)>0)*ham%nattyp(itypat)
    1262              :    end do
    1263            0 :    cplx = 2; if(ham%istwf_k == 2) cplx = 1
    1264              : 
    1265            0 :    req_mem = 0
    1266            0 :    req_mem = req_mem + dp * cplx * int(nprojs, c_size_t) * int(ndat, c_size_t)  ! proj
    1267              :    req_mem = req_mem + dp * cplx * int(nprojs, c_size_t) * int(ndat, c_size_t)  ! sm1proj
    1268              :    req_mem = req_mem + dp * cplx * int(nprojs, c_size_t) * int(ndat, c_size_t)  ! PtPsm1proj
    1269            0 :    req_mem = req_mem + dp * cplx * int(nprojs, c_size_t) * int(ndat, c_size_t)  ! resid (solve_inner)
    1270            0 :    req_mem = req_mem + dp * cplx * int(nprojs, c_size_t) * int(ndat, c_size_t)  ! precondresid (solve_inner)
    1271              : 
    1272            0 :  end function invovl_ompgpu_work_mem
    1273              : 
    1274            0 :  function invovl_ompgpu_static_mem(ham) result(req_mem)
    1275              : 
    1276              :    type(gs_hamiltonian_type), intent(in) :: ham
    1277              :    integer :: nprojs, cplx, itypat
    1278              :    integer(kind=c_size_t) :: req_mem
    1279              : 
    1280            0 :    nprojs = 0
    1281            0 :    do itypat=1,ham%ntypat
    1282            0 :      nprojs = nprojs + count(ham%indlmn(3,:,itypat)>0)*ham%nattyp(itypat)
    1283              :    end do
    1284            0 :    cplx = 2; if(ham%istwf_k > 1) cplx = 1
    1285              : 
    1286            0 :    req_mem = 0
    1287            0 :    req_mem = req_mem + dp * cplx * int(nprojs, c_size_t) * int(nprojs, c_size_t)                       ! gram_projs
    1288              :    req_mem = req_mem + dp * cplx * int(ham%lmnmax, c_size_t) * &
    1289            0 :    &         int(ham%lmnmax, c_size_t) * int(ham%ntypat, c_size_t)  ! inv_sij
    1290              :    req_mem = req_mem + dp * cplx * int(ham%lmnmax, c_size_t) * &
    1291            0 :    &         int(ham%lmnmax, c_size_t) * int(ham%ntypat, c_size_t)  ! inv_s_approx
    1292              : 
    1293            0 :  end function invovl_ompgpu_static_mem
    1294              : 
    1295              : #ifdef HAVE_OPENMP_OFFLOAD
    1296              : !*******************************************************************************************************************************!
    1297              : !                                               OpenMP GPU routines                                                             !
    1298              : !*******************************************************************************************************************************!
    1299              : 
    1300              : !!****f* m_invovl/apply_invovl_ompgpu
    1301              : !! NAME
    1302              : !! apply_invovl_ompgpu
    1303              : !!
    1304              : !! FUNCTION
    1305              : !! Applies the inverse of the overlap matrix to cwavef (OpenMP GPU implementation)
    1306              : !!
    1307              : !! INPUTS
    1308              : !!
    1309              : !! SOURCE
    1310              : 
    1311              : subroutine apply_invovl_ompgpu(ham, cwavef, sm1cwavef, cwaveprj, npw, ndat, mpi_enreg, nspinor, block_sliced)
    1312              : 
    1313              : #if defined(HAVE_FC_ISO_C_BINDING) && defined(HAVE_GPU)
    1314              :   use, intrinsic :: iso_c_binding
    1315              : #endif
    1316              : 
    1317              :   ! args
    1318              :   type(gs_hamiltonian_type), intent(in), target :: ham
    1319              :   integer, intent(in) :: npw, ndat
    1320              :   integer, intent(in) :: nspinor
    1321              :   integer, intent(in) :: block_sliced
    1322              :   real(dp), intent(inout), target :: cwavef(2, npw*nspinor*ndat) ! TODO should be in, fix nonlop
    1323              :   type(mpi_type) :: mpi_enreg
    1324              :   real(dp), intent(inout), target :: sm1cwavef(2, npw*nspinor*ndat)
    1325              :   type(pawcprj_type), intent(inout) :: cwaveprj(:,:)
    1326              :   logical :: transfer_omp_args
    1327              : 
    1328              :   real(dp), ABI_CONTIGUOUS pointer :: proj(:,:,:),sm1proj(:,:,:),PtPsm1proj(:,:,:)
    1329              : 
    1330              :   integer :: idat, iatom, icplx, iproj, nprojs, nlmn, shift
    1331              :   real(dp) :: tsec(2)
    1332              : 
    1333              :   integer :: choice, cpopt, paw_opt , cplx, old_me_g0
    1334              :   type(pawcprj_type),allocatable :: cwaveprj_in(:,:)
    1335              : 
    1336              :   integer :: ikpt_this_proc
    1337              :   ! dummies
    1338              :   real(dp) :: enlout(ndat), lambda_block(1), gvnlxc(1,1)
    1339              :   integer, parameter :: nnlout = 0, idir = 0, signs = 2
    1340              : 
    1341              :   type(invovl_kpt_type), pointer :: invovl
    1342              : 
    1343              :   ! *************************************************************************
    1344              : 
    1345              :   ikpt_this_proc=bandfft_kpt_get_ikpt()
    1346              :   invovl => invovl_kpt(ikpt_this_proc)
    1347              :   nprojs=invovl%nprojs
    1348              :   if(ikpt_this_proc /= current_ikpt_in_gpu) call refresh_invovl_ompgpu_kpt(ikpt_this_proc)
    1349              : 
    1350              :   if(ham%istwf_k == 1) then
    1351              :     cplx = 2
    1352              :   else
    1353              :     cplx = 1
    1354              :   end if
    1355              :   call alloc_ompgpu_buffers(cplx,nprojs,nspinor,ndat)
    1356              :   proj => proj_ompgpu
    1357              :   sm1proj => sm1proj_ompgpu
    1358              :   PtPsm1proj => PtPsm1proj_ompgpu
    1359              :   !$OMP TARGET ENTER DATA MAP(alloc:proj,sm1proj,PtPsm1proj)
    1360              : 
    1361              :   transfer_omp_args =  .not. ( xomp_target_is_present(c_loc(sm1cwavef)) &
    1362              :    .and. xomp_target_is_present(c_loc(cwavef)))
    1363              :   !$OMP TARGET ENTER DATA MAP(alloc:gvnlxc)
    1364              :   if(transfer_omp_args) then
    1365              :    !$OMP TARGET ENTER DATA MAP(alloc:sm1cwavef,cwavef)
    1366              :    !$OMP TARGET UPDATE TO(sm1cwavef,cwavef)
    1367              :   end if
    1368              : 
    1369              :   call timab(timer_apply_inv_ovl_opernla, 1, tsec)
    1370              : 
    1371              :   ! get the cprj
    1372              :   ABI_NVTX_START_RANGE(NVTX_INVOVL_NONLOP1)
    1373              :   choice = 0 ! only compute cprj, nothing else
    1374              :   cpopt = 0 ! compute and save cprj
    1375              :   paw_opt = 3 ! S nonlocal operator
    1376              : 
    1377              :   if(ham%istwf_k==2) then
    1378              :     old_me_g0=mpi_enreg%me_g0
    1379              :     if (mpi_enreg%me_fft==0) then
    1380              :       mpi_enreg%me_g0=1
    1381              :     else
    1382              :       mpi_enreg%me_g0=0
    1383              :     end if
    1384              :   end if
    1385              :   call nonlop(choice,cpopt,cwaveprj_in,enlout,ham,idir,lambda_block,mpi_enreg,ndat,nnlout,&
    1386              :                 paw_opt,signs,sm1cwavef,tim_nonlop,cwavef,gvnlxc,vectproj=proj)
    1387              :   ABI_NVTX_END_RANGE()
    1388              : 
    1389              :   call timab(timer_apply_inv_ovl_opernla, 2, tsec)
    1390              :   call timab(timer_apply_inv_ovl_inv_s, 1, tsec)
    1391              : 
    1392              :   !multiply by S^1
    1393              :   ABI_NVTX_START_RANGE(NVTX_INVOVL_INNER)
    1394              :   call solve_inner_ompgpu(invovl, ham, cplx, mpi_enreg, proj, ndat*nspinor, sm1proj, PtPsm1proj, block_sliced)
    1395              :   !$OMP TARGET TEAMS DISTRIBUTE MAP(to:sm1proj,PtPsm1proj)
    1396              :   do idat  =1, ndat*nspinor
    1397              :     !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(iproj,icplx)
    1398              :     do iproj = 1, nprojs
    1399              :       do icplx = 1, cplx
    1400              :         sm1proj(icplx,iproj,idat) = - sm1proj(icplx,iproj,idat)
    1401              :         PtPsm1proj(icplx,iproj,idat) = - PtPsm1proj(icplx,iproj,idat)
    1402              :       end do
    1403              :     end do
    1404              :   end do
    1405              : 
    1406              :   ABI_NVTX_END_RANGE()
    1407              : 
    1408              :   call timab(timer_apply_inv_ovl_inv_s, 2, tsec)
    1409              :   call timab(timer_apply_inv_ovl_opernlb, 1, tsec)
    1410              : 
    1411              :   ! get the corresponding wf
    1412              :   ABI_NVTX_START_RANGE(NVTX_INVOVL_NONLOP2)
    1413              :   cpopt = 2 ! reuse cprj
    1414              :   choice = 7 ! get wf from cprj, without the application of S
    1415              :   paw_opt = 3
    1416              :   call nonlop(choice,cpopt,cwaveprj,enlout,ham,idir,lambda_block,mpi_enreg,ndat,nnlout,paw_opt,&
    1417              :               signs,sm1cwavef,tim_nonlop,cwavef,gvnlxc,vectproj=sm1proj)
    1418              :   ABI_NVTX_END_RANGE()
    1419              : 
    1420              :   call timab(timer_apply_inv_ovl_opernlb, 2, tsec)
    1421              :   if (ham%istwf_k==2) mpi_enreg%me_g0=old_me_g0
    1422              : 
    1423              :   if(size(cwaveprj) > 1) then
    1424              :     ABI_MALLOC(cwaveprj_in, (ham%natom,nspinor*ndat))
    1425              :     call pawcprj_alloc(cwaveprj_in,0,ham%dimcprj)
    1426              :     !$OMP TARGET UPDATE FROM(PtPsm1proj,proj)
    1427              :     ! copy PtPsm1proj to cwaveprj(:,:)
    1428              :     do idat=1, ndat*nspinor
    1429              :       shift = 0
    1430              :       do iatom = 1, ham%natom
    1431              :         nlmn = cwaveprj(iatom, idat)%nlmn
    1432              :         cwaveprj(iatom, idat)%cp(1:cplx, 1:nlmn) = PtPsm1proj(1:cplx, shift+1:shift+nlmn, idat)
    1433              :         shift = shift + nlmn
    1434              :       end do
    1435              :     end do
    1436              :     do idat=1, ndat*nspinor
    1437              :       shift = 0
    1438              :       do iatom = 1, ham%natom
    1439              :         nlmn = cwaveprj_in(iatom, idat)%nlmn
    1440              :         cwaveprj_in(iatom, idat)%cp(1:cplx, 1:nlmn) = proj(1:cplx, shift+1:shift+nlmn, idat)
    1441              :         shift = shift + nlmn
    1442              :       end do
    1443              :     end do
    1444              :     call pawcprj_axpby(one, one, cwaveprj_in, cwaveprj)
    1445              :     call pawcprj_free(cwaveprj_in)
    1446              :     ABI_FREE(cwaveprj_in)
    1447              :   end if
    1448              : 
    1449              :   call abi_xaxpy(2*npw*nspinor*ndat, cone, cwavef, 1, sm1cwavef, 1, gpu_option=ABI_GPU_OPENMP)
    1450              : 
    1451              :   if(transfer_omp_args) then
    1452              :     !$OMP TARGET UPDATE FROM(sm1cwavef,cwavef)
    1453              :     !$OMP TARGET EXIT DATA MAP(delete:sm1cwavef,cwavef)
    1454              :   end if
    1455              : 
    1456              :   !$OMP TARGET EXIT DATA MAP(delete:gvnlxc)
    1457              :   !$OMP TARGET EXIT DATA MAP(delete:proj,sm1proj,PtPsm1proj)
    1458              : 
    1459              : end subroutine apply_invovl_ompgpu
    1460              : !!***
    1461              : 
    1462              : !!****f* m_invovl/solve_inner_ompgpu
    1463              : !! NAME
    1464              : !! solve_inner_ompgpu
    1465              : !!
    1466              : !! FUNCTION
    1467              : !! Helper function: iteratively solves the inner system (OpenMP GPU offload implementation)
    1468              : !!
    1469              : !! INPUTS
    1470              : !!
    1471              : !! SOURCE
    1472              : subroutine solve_inner_ompgpu(invovl, ham, cplx, mpi_enreg, proj, ndat, sm1proj, PtPsm1proj, block_sliced)
    1473              : 
    1474              :  use m_abi_linalg
    1475              : 
    1476              :  integer,intent(in) :: ndat,cplx
    1477              :  type(invovl_kpt_type), intent(in), target :: invovl
    1478              :  real(dp), intent(inout) :: proj(cplx, invovl%nprojs,ndat)
    1479              :  real(dp), intent(inout), target :: sm1proj(cplx, invovl%nprojs, ndat)
    1480              :  real(dp), intent(inout), target :: PtPsm1proj(cplx, invovl%nprojs, ndat)
    1481              :  type(mpi_type), intent(in) :: mpi_enreg
    1482              :  type(gs_hamiltonian_type),intent(in) :: ham
    1483              :  integer, intent(in) :: block_sliced
    1484              : 
    1485              :  integer :: array_nlmntot_pp(mpi_enreg%nproc_fft)
    1486              :  integer :: nlmntot_this_proc, ibeg, iend, ierr, i, nprojs
    1487              :  real(dp) :: resid(cplx, invovl%nprojs,ndat), precondresid(cplx, invovl%nprojs,ndat)
    1488              :  real(dp) :: normprojs(ndat), errs(ndat), maxerr, previous_maxerr
    1489              :  character(len=500) :: message
    1490              : 
    1491              :  real(dp), parameter :: precision = 1e-16 ! maximum relative error. TODO: use tolwfr ?
    1492              :  real(dp) :: convergence_rate,sum_tmp
    1493              :  integer :: additional_steps_to_take,idat,iproj,icplx
    1494              :  integer :: Ptsize(3)
    1495              : 
    1496              : ! *************************************************************************
    1497              : 
    1498              :  Ptsize(1) = cplx
    1499              :  Ptsize(2) = invovl%nprojs
    1500              :  Ptsize(3) = ndat
    1501              :  nprojs = invovl%nprojs
    1502              : 
    1503              :  !$OMP TARGET ENTER DATA MAP(alloc:errs,precondresid,resid,normprojs)
    1504              : 
    1505              :  !FIXME LLVM has trouble with performing team reduction (AOMP 15.0.2)
    1506              : #ifdef FC_LLVM
    1507              :  !$OMP TARGET UPDATE FROM(proj)
    1508              : #else
    1509              :  !$OMP TARGET TEAMS DISTRIBUTE MAP(to:normprojs,proj) PRIVATE(idat,sum_tmp)
    1510              : #endif
    1511              :  do idat = 1,ndat
    1512              :   sum_tmp=0
    1513              : #ifndef FC_LLVM
    1514              :   !$OMP PARALLEL DO COLLAPSE(2) REDUCTION(+:sum_tmp) PRIVATE(iproj,icplx)
    1515              : #endif
    1516              :   do iproj = 1,nprojs
    1517              :     do icplx = 1,cplx
    1518              :       sum_tmp = sum_tmp + proj(icplx,iproj,idat)**2
    1519              :     end do
    1520              :   end do
    1521              :   normprojs(idat)=sum_tmp
    1522              :  end do
    1523              : #ifndef FC_LLVM
    1524              :  !$OMP TARGET UPDATE FROM(normprojs)
    1525              : #endif
    1526              : 
    1527              :  ibeg = 1
    1528              :  iend = nprojs
    1529              :  nlmntot_this_proc = nprojs
    1530              : 
    1531              :  ! first guess for sm1proj
    1532              :  call apply_block_ompgpu(ham, cplx, invovl%inv_s_approx, nprojs, ndat, proj, sm1proj, block_sliced)
    1533              : 
    1534              :  ! Iterative refinement
    1535              :  ! TODO use a more efficient iterative algorithm than iterative refinement, use locking
    1536              :  additional_steps_to_take = -1
    1537              :  do i=1, 30
    1538              : #ifdef FC_NVHPC
    1539              :    ! Silly fix for NVHPC 25.1
    1540              :    if(ndat == -42) write(100,*) ndat
    1541              : #endif
    1542              :    ! compute resid = proj - (D^-1 + PtP)sm1proj
    1543              :    call apply_block_ompgpu(ham, cplx, invovl%inv_sij, nprojs, ndat, sm1proj, resid, block_sliced)
    1544              : 
    1545              :    ! compute matrix multiplication : PtPsm1proj(:,:,1) = invovl%gram * sm1proj(:,:,1)
    1546              :    ABI_NVTX_START_RANGE(NVTX_INVOVL_INNER_GEMM)
    1547              :    call abi_xgemm('N', 'N', nprojs, ndat, nlmntot_this_proc, cone, &
    1548              :    &              current_gram_projs, nprojs,&
    1549              :    &              sm1proj, nlmntot_this_proc, czero, &
    1550              :    &              PtPsm1proj, nprojs, &
    1551              :    &              x_cplx=cplx, gpu_option=ABI_GPU_OPENMP)
    1552              : 
    1553              :    !$OMP TARGET TEAMS DISTRIBUTE &
    1554              :    !$OMP& PRIVATE(idat) MAP(to:proj,resid,PtPsm1proj)
    1555              :    do idat =1, ndat
    1556              :      !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(iproj,icplx)
    1557              :      do iproj =1, nprojs
    1558              :        do icplx = 1,cplx
    1559              :          resid(icplx, iproj, idat) = proj(icplx, iproj, idat) - resid(icplx, iproj, idat) - PtPsm1proj(icplx, iproj, idat)
    1560              :        end do
    1561              :      end do
    1562              :    end do
    1563              : 
    1564              :    ! exit check
    1565              : #ifdef FC_LLVM
    1566              :    !FIXME LLVM has trouble with performing team reduction (v16.0.0 from AMD ROCm 5.6.0)
    1567              :    !$OMP TARGET UPDATE FROM(resid)
    1568              :    errs = SUM(SUM(resid**2, 1),1)
    1569              : #else
    1570              :    !$OMP TARGET TEAMS DISTRIBUTE MAP(to:errs,resid) PRIVATE(idat,sum_tmp)
    1571              :    do idat = 1,ndat
    1572              :      sum_tmp=0
    1573              :      !$OMP PARALLEL DO COLLAPSE(2) REDUCTION(+:sum_tmp) PRIVATE(iproj,icplx)
    1574              :      do iproj = 1,nprojs
    1575              :        do icplx = 1,cplx
    1576              :          sum_tmp = sum_tmp + resid(icplx,iproj,idat)**2
    1577              :        end do
    1578              :      end do
    1579              :      errs(idat)=sum_tmp
    1580              :    end do
    1581              :    !$OMP TARGET UPDATE FROM(errs)
    1582              : #endif
    1583              : 
    1584              :    ABI_NVTX_END_RANGE()
    1585              : 
    1586              :    maxerr = sqrt(MAXVAL(errs/normprojs))
    1587              :    if(maxerr < precision .or. additional_steps_to_take == 1) then
    1588              :      exit
    1589              :      ! We might stall and never get to the specified precision because of machine errors.
    1590              :      ! If we got to 1e-10, extrapolate convergence rate and determine the number of additional
    1591              :      ! steps to take to reach precision
    1592              :    else if(maxerr < 1e-10 .and. additional_steps_to_take == -1) then
    1593              :      convergence_rate = -LOG(1e-10) / i
    1594              :      additional_steps_to_take = CEILING(-LOG(precision/1e-10)/convergence_rate) + 1
    1595              :    else if(additional_steps_to_take > 0) then
    1596              :      if(previous_maxerr<maxerr)exit
    1597              :      additional_steps_to_take = additional_steps_to_take - 1
    1598              :    end if
    1599              :    previous_maxerr=maxerr
    1600              : 
    1601              :    ! add preconditionned residual
    1602              :    call apply_block_ompgpu(ham, cplx, invovl%inv_s_approx, nprojs, ndat, resid, precondresid, block_sliced)
    1603              : 
    1604              :    !$OMP TARGET TEAMS DISTRIBUTE &
    1605              :    !$OMP& PRIVATE(idat) MAP(to:sm1proj,precondresid)
    1606              :    do idat =1, ndat
    1607              :      !$OMP PARALLEL DO PRIVATE(iproj,icplx) COLLAPSE(2)
    1608              :      do iproj =1, nprojs
    1609              :        do icplx = 1,cplx
    1610              :          sm1proj(icplx, iproj, idat) = sm1proj(icplx, iproj, idat) + precondresid(icplx, iproj, idat)
    1611              :        end do
    1612              :      end do
    1613              :    end do
    1614              :  end do
    1615              :  !$OMP TARGET EXIT DATA MAP(delete:errs,resid,precondresid,normprojs)
    1616              : 
    1617              :  if(maxerr >= precision .and. maxerr >= 1e-10) then
    1618              :    write(message, *) 'In invovl, max error was', maxerr, ' after 30 iterations'
    1619              :    ABI_WARNING(message)
    1620              :  else
    1621              :    ! write(message,'(a,i2,a,es13.5)') 'Iterative solver in invovl finished in ', i, ' iterations, error', maxerr
    1622              :    ! call wrtout(std_out,message,'COLL')
    1623              :  end if
    1624              : 
    1625              : end subroutine solve_inner_ompgpu
    1626              : !!***
    1627              : 
    1628              : !!****f* m_invovl/apply_block_ompgpu
    1629              : !! NAME
    1630              : !! apply_block_ompgpu
    1631              : !!
    1632              : !! FUNCTION
    1633              : !! Helper function: applies a block-diagonal matrix mat(lmnmax, lmnmax, ntypat)
    1634              : !!
    1635              : !! INPUTS
    1636              : !!
    1637              : !! SOURCE
    1638              : subroutine apply_block_ompgpu(ham, cplx, mat, nprojs, ndat, x, y, block_sliced)
    1639              : 
    1640              :   use m_abi_linalg
    1641              : 
    1642              :   integer,intent(in) :: ndat, nprojs, cplx
    1643              :   real(dp), intent(inout), target :: x(cplx, nprojs, ndat), y(cplx, nprojs, ndat)
    1644              :   type(gs_hamiltonian_type),intent(in) :: ham
    1645              :   real(dp), intent(in), target :: mat(cplx, ham%lmnmax, ham%lmnmax, ham%ntypat)
    1646              :   integer, intent(in) :: block_sliced
    1647              : 
    1648              :   integer :: nlmn, shift, itypat, idat
    1649              :   real(dp), ABI_CONTIGUOUS pointer :: x_ptr(:, :, :), y_ptr(:, :, :), mat_ptr(:,:,:)
    1650              : 
    1651              : ! *************************************************************************
    1652              : 
    1653              :   if (block_sliced == 1) then
    1654              : 
    1655              :      do idat = 1, ndat
    1656              :         shift = 1
    1657              :         do itypat=1, ham%ntypat
    1658              :            nlmn = count(ham%indlmn(3,:,itypat)>0)
    1659              :            !! apply mat to all atoms at once
    1660              :            ! perform natom multiplications of size nlmn
    1661              :            ! compute y = mat*x
    1662              :            if(cplx == 2) then
    1663              :              !$OMP TARGET DATA USE_DEVICE_ADDR(mat,x,y)
    1664              :              call abi_gpu_zhemm('L','U', nlmn, ham%nattyp(itypat), cone, &
    1665              :                    c_loc(mat(:, :, :, itypat)), ham%lmnmax, &
    1666              :                    c_loc(x(:, shift:shift+nlmn*ham%nattyp(itypat)-1, idat)), nlmn, czero, &
    1667              :                    c_loc(y(:, shift:shift+nlmn*ham%nattyp(itypat)-1, idat)), nlmn)
    1668              :              !$OMP END TARGET DATA
    1669              :            else
    1670              :              !$OMP TARGET DATA USE_DEVICE_ADDR(mat,x,y)
    1671              :              call abi_gpu_xsymm(cplx, 'L','U', nlmn, ham%nattyp(itypat), cone, &
    1672              :                    c_loc(mat(:, :, :, itypat)), ham%lmnmax, &
    1673              :                    c_loc(x(:, shift:shift+nlmn*ham%nattyp(itypat)-1, idat)), nlmn, czero, &
    1674              :                    c_loc(y(:, shift:shift+nlmn*ham%nattyp(itypat)-1, idat)), nlmn)
    1675              :              !$OMP END TARGET DATA
    1676              :            end if
    1677              :            shift = shift + nlmn*ham%nattyp(itypat)
    1678              :         end do
    1679              :      end do
    1680              : 
    1681              :   else ! block_sliced = 0
    1682              : 
    1683              :     shift = 1
    1684              :     do itypat=1, ham%ntypat
    1685              :       nlmn = count(ham%indlmn(3,:,itypat)>0)
    1686              :       x_ptr => x(:, shift:shift+nlmn*ham%nattyp(itypat)-1, :)
    1687              :       y_ptr => y(:, shift:shift+nlmn*ham%nattyp(itypat)-1, :)
    1688              :       mat_ptr => mat(:, :, :, itypat)
    1689              :       !! apply mat to all atoms at once, all idat at once
    1690              :       ! perform natom multiplications of size nlmn
    1691              :       ! be careful here matrix extracted from x and y are not memory contiguous
    1692              :       ! ==> so in the GPU version we will need to adapt leading dimension
    1693              :       !$OMP TARGET DATA USE_DEVICE_ADDR(mat_ptr,x_ptr,y_ptr)
    1694              :       call abi_gpu_xgemm_strided(cplx, 'N','N', &
    1695              :               nlmn, ham%nattyp(itypat), nlmn, cone, &
    1696              :               c_loc(mat_ptr), ham%lmnmax, 0, &
    1697              :               c_loc(x_ptr), nlmn, nprojs, &
    1698              :               czero, &
    1699              :               c_loc(y_ptr), nlmn, nprojs, ndat)
    1700              :       !$OMP END TARGET DATA
    1701              :       shift = shift + nlmn*ham%nattyp(itypat)
    1702              :     end do
    1703              : 
    1704              :   end if
    1705              : 
    1706              : end subroutine apply_block_ompgpu
    1707              : !!***
    1708              : #endif
    1709              : 
    1710            0 : end MODULE m_invovl
    1711              : !!***
        

Generated by: LCOV version 2.3-1