LCOV - code coverage report
Current view: top level - src - gw_ri_rs_compute_Z_lP_utils.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:92574dc) Lines: 45.8 % 83 38
Test Date: 2026-09-24 01:27:39 Functions: 60.0 % 5 3

            Line data    Source code
       1              : !--------------------------------------------------------------------------------------------------!
       2              : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3              : !   Copyright 2000-2026 CP2K developers group <https://cp2k.org>                                   !
       4              : !                                                                                                  !
       5              : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6              : !--------------------------------------------------------------------------------------------------!
       7              : 
       8              : ! **************************************************************************************************
       9              : !> \brief Shared numerical operations for computing the RI-RS matrix Z_lP.
      10              : !> \par History
      11              : !>      09.2026 created Jan Wilhelm
      12              : ! **************************************************************************************************
      13              : MODULE gw_ri_rs_compute_Z_lP_utils
      14              :    USE cp_blacs_env,                    ONLY: cp_blacs_env_type
      15              :    USE cp_dbcsr_api,                    ONLY: dbcsr_put_block,&
      16              :                                               dbcsr_type
      17              :    USE cp_fm_cholesky,                  ONLY: cp_fm_cholesky_decompose,&
      18              :                                               cp_fm_cholesky_solve
      19              :    USE cp_fm_struct,                    ONLY: cp_fm_struct_create,&
      20              :                                               cp_fm_struct_release,&
      21              :                                               cp_fm_struct_type
      22              :    USE cp_fm_types,                     ONLY: cp_fm_create,&
      23              :                                               cp_fm_get_info,&
      24              :                                               cp_fm_get_submatrix,&
      25              :                                               cp_fm_release,&
      26              :                                               cp_fm_set_submatrix,&
      27              :                                               cp_fm_type
      28              :    USE kinds,                           ONLY: dp
      29              :    USE message_passing,                 ONLY: mp_para_env_type
      30              : #include "./base/base_uses.f90"
      31              : 
      32              :    IMPLICIT NONE
      33              :    PRIVATE
      34              : 
      35              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'gw_ri_rs_compute_Z_lP_utils'
      36              :    REAL(KIND=dp), PARAMETER, PRIVATE :: jacobi_floor = 1.0E-16_dp
      37              : 
      38              :    PUBLIC :: build_gram_jacobi_blas, &
      39              :              build_jacobi_diag_from_phi, &
      40              :              scale_rows_by_diag, &
      41              :              solve_D_lp_distributed, &
      42              :              store_Z_lP_columns
      43              : 
      44              : CONTAINS
      45              : 
      46              : ! **************************************************************************************************
      47              : !> \brief Forms the conditioned dense RI-RS matrix
      48              : !>
      49              : !>          D_ll'  = [Σ_μ ϕ_μ(r_l) ϕ_μ(r_l')]²,
      50              : !>          d_l    = 1/sqrt(D_ll),
      51              : !>          D'_ll' = d_l D_ll' d_l' + λ δ_ll'.
      52              : !>
      53              : !>        Only the dense single-rank solve needs the complete matrix D'.
      54              : !> \param phi_local ...
      55              : !> \param n_local_grid ...
      56              : !> \param n_ao_used ...
      57              : !> \param tikhonov ...
      58              : !> \param D_local ...
      59              : !> \param d_vec_local ...
      60              : ! **************************************************************************************************
      61           54 :    SUBROUTINE build_gram_jacobi_blas(phi_local, n_local_grid, n_ao_used, tikhonov, D_local, &
      62           54 :                                      d_vec_local)
      63              : 
      64              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: phi_local
      65              :       INTEGER, INTENT(IN)                                :: n_local_grid, n_ao_used
      66              :       REAL(KIND=dp), INTENT(IN)                          :: tikhonov
      67              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :), &
      68              :          INTENT(OUT)                                     :: D_local
      69              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: d_vec_local
      70              : 
      71              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'build_gram_jacobi_blas'
      72              : 
      73              :       INTEGER                                            :: handle, handle_dsyrk, i, j
      74              : 
      75           54 :       CALL timeset(routineN, handle)
      76              : 
      77          216 :       ALLOCATE (D_local(n_local_grid, n_local_grid))
      78           54 :       D_local = 0.0_dp
      79              : 
      80           54 :       CALL timeset(routineN//'_dsyrk', handle_dsyrk)
      81              :       CALL dsyrk("L", "N", n_local_grid, n_ao_used, 1.0_dp, phi_local, &
      82           54 :                  n_local_grid, 0.0_dp, D_local, n_local_grid)
      83           54 :       CALL timestop(handle_dsyrk)
      84              : 
      85              :       !$OMP PARALLEL DO DEFAULT(NONE) &
      86              :       !$OMP SHARED(n_local_grid, D_local, d_vec_local, tikhonov) &
      87              :       !$OMP PRIVATE(i) &
      88           54 :       !$OMP SCHEDULE(STATIC)
      89              :       DO i = 1, n_local_grid
      90              :          D_local(i, i) = D_local(i, i)**2
      91              :          d_vec_local(i) = 1.0_dp/SQRT(MAX(D_local(i, i), jacobi_floor))
      92              :          D_local(i, i) = (D_local(i, i)*d_vec_local(i)**2) + tikhonov
      93              :       END DO
      94              :       !$OMP END PARALLEL DO
      95              : 
      96              :       !$OMP PARALLEL DO DEFAULT(NONE) &
      97              :       !$OMP SHARED(n_local_grid, D_local, d_vec_local) &
      98              :       !$OMP PRIVATE(j, i) &
      99           54 :       !$OMP SCHEDULE(DYNAMIC)
     100              :       DO j = 1, n_local_grid
     101              :          DO i = j + 1, n_local_grid
     102              :             D_local(i, j) = D_local(i, j)**2
     103              :             D_local(i, j) = D_local(i, j)*d_vec_local(i)*d_vec_local(j)
     104              :             D_local(j, i) = D_local(i, j)
     105              :          END DO
     106              :       END DO
     107              :       !$OMP END PARALLEL DO
     108              : 
     109           54 :       CALL timestop(handle)
     110              : 
     111          108 :    END SUBROUTINE build_gram_jacobi_blas
     112              : 
     113              : ! **************************************************************************************************
     114              : !> \brief Computes d_l = 1/sqrt(D_ll) = 1/Σ_μ ϕ_μ(r_l)² without forming D.
     115              : !> \param phi_local ...
     116              : !> \param n_local_grid ...
     117              : !> \param n_ao_used ...
     118              : !> \param d_vec_local ...
     119              : ! **************************************************************************************************
     120            0 :    SUBROUTINE build_jacobi_diag_from_phi(phi_local, n_local_grid, n_ao_used, d_vec_local)
     121              : 
     122              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: phi_local
     123              :       INTEGER, INTENT(IN)                                :: n_local_grid, n_ao_used
     124              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: d_vec_local
     125              : 
     126              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'build_jacobi_diag_from_phi'
     127              : 
     128              :       INTEGER                                            :: handle, i, j
     129              : 
     130            0 :       CALL timeset(routineN, handle)
     131              : 
     132              :       !$OMP PARALLEL DO DEFAULT(NONE) &
     133              :       !$OMP SHARED(n_local_grid, n_ao_used, phi_local, d_vec_local) &
     134              :       !$OMP PRIVATE(i, j) &
     135            0 :       !$OMP SCHEDULE(STATIC)
     136              :       DO i = 1, n_local_grid
     137              :          d_vec_local(i) = 0.0_dp
     138              :          DO j = 1, n_ao_used
     139              :             d_vec_local(i) = d_vec_local(i) + phi_local(i, j)*phi_local(i, j)
     140              :          END DO
     141              :          d_vec_local(i) = 1.0_dp/MAX(d_vec_local(i), jacobi_floor)
     142              :       END DO
     143              :       !$OMP END PARALLEL DO
     144              : 
     145            0 :       CALL timestop(handle)
     146              : 
     147            0 :    END SUBROUTINE build_jacobi_diag_from_phi
     148              : 
     149              : ! **************************************************************************************************
     150              : !> \brief Multiplies every matrix row by the corresponding diagonal entry:
     151              : !>        A(l, :) <- d_l A(l, :).
     152              : !> \param matrix ...
     153              : !> \param diagonal ...
     154              : !> \param nrow ...
     155              : !> \param ncol ...
     156              : ! **************************************************************************************************
     157          108 :    SUBROUTINE scale_rows_by_diag(matrix, diagonal, nrow, ncol)
     158              : 
     159              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT)      :: matrix
     160              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: diagonal
     161              :       INTEGER, INTENT(IN)                                :: nrow, ncol
     162              : 
     163              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'scale_rows_by_diag'
     164              : 
     165              :       INTEGER                                            :: handle, i, j
     166              : 
     167          108 :       CALL timeset(routineN, handle)
     168              : 
     169              :       !$OMP PARALLEL DO DEFAULT(NONE) &
     170              :       !$OMP SHARED(ncol, nrow, matrix, diagonal) &
     171              :       !$OMP PRIVATE(j, i) &
     172          108 :       !$OMP SCHEDULE(STATIC)
     173              :       DO j = 1, ncol
     174              :          DO i = 1, nrow
     175              :             matrix(i, j) = matrix(i, j)*diagonal(i)
     176              :          END DO
     177              :       END DO
     178              :       !$OMP END PARALLEL DO
     179              : 
     180          108 :       CALL timestop(handle)
     181              : 
     182          108 :    END SUBROUTINE scale_rows_by_diag
     183              : 
     184              : ! **************************************************************************************************
     185              : !> \brief Stores dense Z_lP columns in the distributed block-sparse matrix.
     186              : !> \param mat_Z_lP ...
     187              : !> \param Z_local ...
     188              : !> \param local_grid_idx ...
     189              : !> \param n_local_grid ...
     190              : !> \param n_loc_ri ...
     191              : !> \param atom_P ...
     192              : !> \param r_blk_sizes ...
     193              : !> \param row_offset ...
     194              : !> \param eps_filter ...
     195              : ! **************************************************************************************************
     196           50 :    SUBROUTINE store_Z_lP_columns(mat_Z_lP, Z_local, local_grid_idx, n_local_grid, n_loc_ri, &
     197           50 :                                  atom_P, r_blk_sizes, row_offset, eps_filter)
     198              : 
     199              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: mat_Z_lP
     200              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: Z_local
     201              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: local_grid_idx
     202              :       INTEGER, INTENT(IN)                                :: n_local_grid, n_loc_ri, atom_P
     203              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: r_blk_sizes, row_offset
     204              :       REAL(KIND=dp), INTENT(IN)                          :: eps_filter
     205              : 
     206              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'store_Z_lP_columns'
     207              : 
     208              :       INTEGER                                            :: current_chunk_size, g_pt, handle, i_blk, &
     209              :                                                             loc_ptr, r_end, r_start
     210           50 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: Z_blk
     211              : 
     212           50 :       CALL timeset(routineN, handle)
     213              : 
     214          882 :       ALLOCATE (Z_blk(MAXVAL(r_blk_sizes), n_loc_ri))
     215           50 :       loc_ptr = 1
     216              : 
     217          732 :       DO i_blk = 1, SIZE(r_blk_sizes)
     218          682 :          r_start = row_offset(i_blk) + 1
     219          682 :          r_end = row_offset(i_blk) + r_blk_sizes(i_blk)
     220          682 :          current_chunk_size = r_blk_sizes(i_blk)
     221          682 :          Z_blk = 0.0_dp
     222              : 
     223        18845 :          DO WHILE (loc_ptr <= n_local_grid)
     224        18791 :             g_pt = local_grid_idx(loc_ptr)
     225        18791 :             IF (g_pt > r_end) EXIT
     226       255966 :             Z_blk(g_pt - r_start + 1, 1:n_loc_ri) = Z_local(loc_ptr, 1:n_loc_ri)
     227        18791 :             loc_ptr = loc_ptr + 1
     228              :          END DO
     229              : 
     230       252928 :          IF (MAXVAL(ABS(Z_blk(1:current_chunk_size, 1:n_loc_ri))) > eps_filter) THEN
     231              :             CALL dbcsr_put_block(mat_Z_lP, row=i_blk, col=atom_P, &
     232          670 :                                  block=Z_blk(1:current_chunk_size, 1:n_loc_ri))
     233              :          END IF
     234              :       END DO
     235              : 
     236           50 :       DEALLOCATE (Z_blk)
     237              : 
     238           50 :       CALL timestop(handle)
     239              : 
     240           50 :    END SUBROUTINE store_Z_lP_columns
     241              : 
     242              : ! **************************************************************************************************
     243              : !> \brief Solves D Z = d with ScaLAPACK for one RI atom and replicated inputs.
     244              : !>
     245              : !>        Each rank builds its block-cyclic part of
     246              : !>
     247              : !>          D'_ll' = d_l [Σ_μ ϕ_μ(r_l) ϕ_μ(r_l')]² d_l' + λ δ_ll'.
     248              : !>
     249              : !>        The Cholesky solution is gathered into the replicated right-hand side.
     250              : !> \param phi_local ...
     251              : !> \param d_vec ...
     252              : !> \param d_lp ...
     253              : !> \param n_loc ...
     254              : !> \param n_ao ...
     255              : !> \param n_rhs ...
     256              : !> \param tikhonov ...
     257              : !> \param para_env_sub ...
     258              : !> \param blacs_env_sub ...
     259              : !> \param fm_struct_D ...
     260              : !> \param fm_struct_b ...
     261              : !> \param fm_D ...
     262              : !> \param fm_b ...
     263              : !> \param info ...
     264              : ! **************************************************************************************************
     265            0 :    SUBROUTINE solve_D_lp_distributed(phi_local, d_vec, d_lp, n_loc, n_ao, n_rhs, &
     266              :                                      tikhonov, para_env_sub, blacs_env_sub, &
     267              :                                      fm_struct_D, fm_struct_b, fm_D, fm_b, info)
     268              : 
     269              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: phi_local
     270              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: d_vec
     271              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(INOUT)      :: d_lp
     272              :       INTEGER, INTENT(IN)                                :: n_loc, n_ao, n_rhs
     273              :       REAL(KIND=dp), INTENT(IN)                          :: tikhonov
     274              :       TYPE(mp_para_env_type), POINTER                    :: para_env_sub
     275              :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env_sub
     276              :       TYPE(cp_fm_struct_type), POINTER                   :: fm_struct_D, fm_struct_b
     277              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: fm_D, fm_b
     278              :       INTEGER, INTENT(OUT)                               :: info
     279              : 
     280              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'solve_D_lp_distributed'
     281              : 
     282              :       INTEGER                                            :: handle, i_loc, ig, j_loc, jg, &
     283              :                                                             ncol_local, nrow_local
     284            0 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
     285              :       REAL(KIND=dp), CONTIGUOUS, DIMENSION(:, :), &
     286            0 :          POINTER                                         :: local_data
     287              : 
     288            0 :       CALL timeset(routineN, handle)
     289            0 :       info = 0
     290              : 
     291            0 :       NULLIFY (fm_struct_D, fm_struct_b)
     292              :       CALL cp_fm_struct_create(fm_struct_D, para_env=para_env_sub, &
     293              :                                context=blacs_env_sub, &
     294            0 :                                nrow_global=n_loc, ncol_global=n_loc)
     295              :       CALL cp_fm_struct_create(fm_struct_b, para_env=para_env_sub, &
     296              :                                context=blacs_env_sub, &
     297            0 :                                nrow_global=n_loc, ncol_global=n_rhs)
     298            0 :       CALL cp_fm_create(fm_D, fm_struct_D)
     299            0 :       CALL cp_fm_create(fm_b, fm_struct_b)
     300              : 
     301              :       CALL cp_fm_get_info(fm_D, nrow_local=nrow_local, ncol_local=ncol_local, &
     302              :                           row_indices=row_indices, col_indices=col_indices, &
     303            0 :                           local_data=local_data)
     304              : 
     305            0 :       IF (nrow_local > 0 .AND. ncol_local > 0) THEN
     306            0 :          BLOCK
     307              :             INTEGER, PARAMETER :: ntile = 1024
     308              :             INTEGER :: ib, ie, jb, je, mb, kb, ti, tj, handle_dgemm
     309            0 :             REAL(KIND=dp), ALLOCATABLE :: gram_t(:, :), phi_cols_t(:, :), phi_rows_t(:, :)
     310            0 :             ALLOCATE (phi_rows_t(ntile, n_ao), phi_cols_t(n_ao, ntile), gram_t(ntile, ntile))
     311            0 :             DO ib = 1, nrow_local, ntile
     312            0 :                ie = MIN(ib + ntile - 1, nrow_local)
     313            0 :                mb = ie - ib + 1
     314              :                !$OMP PARALLEL DO DEFAULT(NONE) &
     315              :                !$OMP SHARED(mb, n_ao, phi_rows_t, phi_local, row_indices, ib) &
     316            0 :                !$OMP PRIVATE(ti, j_loc) SCHEDULE(STATIC)
     317              :                DO j_loc = 1, n_ao
     318              :                   DO ti = 1, mb
     319              :                      phi_rows_t(ti, j_loc) = phi_local(row_indices(ib + ti - 1), j_loc)
     320              :                   END DO
     321              :                END DO
     322              :                !$OMP END PARALLEL DO
     323            0 :                DO jb = 1, ncol_local, ntile
     324            0 :                   je = MIN(jb + ntile - 1, ncol_local)
     325            0 :                   kb = je - jb + 1
     326              :                   !$OMP PARALLEL DO DEFAULT(NONE) &
     327              :                   !$OMP SHARED(kb, n_ao, phi_cols_t, phi_local, col_indices, jb) &
     328            0 :                   !$OMP PRIVATE(tj, i_loc) SCHEDULE(STATIC)
     329              :                   DO tj = 1, kb
     330              :                      DO i_loc = 1, n_ao
     331              :                         phi_cols_t(i_loc, tj) = phi_local(col_indices(jb + tj - 1), i_loc)
     332              :                      END DO
     333              :                   END DO
     334              :                   !$OMP END PARALLEL DO
     335            0 :                   CALL timeset(routineN//'_dgemm', handle_dgemm)
     336              :                   CALL dgemm('N', 'N', mb, kb, n_ao, &
     337              :                              1.0_dp, phi_rows_t, ntile, phi_cols_t, n_ao, &
     338            0 :                              0.0_dp, gram_t, ntile)
     339            0 :                   CALL timestop(handle_dgemm)
     340              :                   !$OMP PARALLEL DO DEFAULT(NONE) &
     341              :                   !$OMP SHARED(mb, kb, gram_t, d_vec, row_indices, col_indices, ib, jb) &
     342              :                   !$OMP SHARED(local_data, tikhonov) &
     343            0 :                   !$OMP PRIVATE(ti, tj, ig, jg) SCHEDULE(STATIC)
     344              :                   DO tj = 1, kb
     345              :                      jg = col_indices(jb + tj - 1)
     346              :                      DO ti = 1, mb
     347              :                         ig = row_indices(ib + ti - 1)
     348              :                         local_data(ib + ti - 1, jb + tj - 1) = &
     349              :                            gram_t(ti, tj)*gram_t(ti, tj)*d_vec(ig)*d_vec(jg)
     350              :                         IF (ig == jg) THEN
     351              :                            local_data(ib + ti - 1, jb + tj - 1) = &
     352              :                               local_data(ib + ti - 1, jb + tj - 1) + tikhonov
     353              :                         END IF
     354              :                      END DO
     355              :                   END DO
     356              :                   !$OMP END PARALLEL DO
     357              :                END DO
     358              :             END DO
     359            0 :             DEALLOCATE (phi_rows_t, phi_cols_t, gram_t)
     360              :          END BLOCK
     361              :       END IF
     362              : 
     363            0 :       CALL cp_fm_set_submatrix(fm_b, d_lp)
     364              : 
     365            0 :       CALL cp_fm_cholesky_decompose(fm_D, n=n_loc, info_out=info)
     366            0 :       IF (info /= 0) CPABORT("pdpotrf failed in solve_D_lp_distributed")
     367              : 
     368            0 :       CALL cp_fm_cholesky_solve(fm_D, fm_b, n=n_loc, info_out=info)
     369            0 :       IF (info /= 0) CPABORT("pdpotrs failed in solve_D_lp_distributed")
     370              : 
     371            0 :       CALL cp_fm_get_submatrix(fm_b, d_lp)
     372              : 
     373            0 :       CALL cp_fm_release(fm_D)
     374            0 :       CALL cp_fm_release(fm_b)
     375            0 :       CALL cp_fm_struct_release(fm_struct_D)
     376            0 :       CALL cp_fm_struct_release(fm_struct_b)
     377              : 
     378            0 :       CALL timestop(handle)
     379              : 
     380            0 :    END SUBROUTINE solve_D_lp_distributed
     381              : 
     382              : END MODULE gw_ri_rs_compute_Z_lP_utils
        

Generated by: LCOV version 2.0-1