LCOV - code coverage report
Current view: top level - src/fm - cp_cfm_types.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:9ccafda) Lines: 90.2 % 458 413
Test Date: 2026-10-15 00:55:44 Functions: 76.2 % 21 16

            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 Represents a complex full matrix distributed on many processors.
      10              : !> \author Joost VandeVondele, based on Fawzi's cp_fm_* routines
      11              : ! **************************************************************************************************
      12              : MODULE cp_cfm_types
      13              :    USE cp_blacs_env,                    ONLY: cp_blacs_env_type
      14              :    USE cp_fm_struct,                    ONLY: cp_fm_struct_create,&
      15              :                                               cp_fm_struct_equivalent,&
      16              :                                               cp_fm_struct_get,&
      17              :                                               cp_fm_struct_release,&
      18              :                                               cp_fm_struct_retain,&
      19              :                                               cp_fm_struct_type
      20              :    USE cp_fm_types,                     ONLY: cp_fm_type
      21              :    USE kinds,                           ONLY: dp
      22              :    USE mathconstants,                   ONLY: z_one,&
      23              :                                               z_zero
      24              :    USE message_passing,                 ONLY: cp2k_is_parallel,&
      25              :                                               mp_any_source,&
      26              :                                               mp_para_env_type,&
      27              :                                               mp_proc_null,&
      28              :                                               mp_request_null,&
      29              :                                               mp_request_type,&
      30              :                                               mp_waitall
      31              : #include "../base/base_uses.f90"
      32              : 
      33              :    IMPLICIT NONE
      34              :    PRIVATE
      35              : 
      36              :    LOGICAL, PRIVATE, PARAMETER :: debug_this_module = .TRUE.
      37              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_cfm_types'
      38              :    INTEGER, PARAMETER, PRIVATE :: src_tag = 3, dest_tag = 5, send_tag = 7, recv_tag = 11
      39              : 
      40              :    PUBLIC :: cp_cfm_type, cp_cfm_p_type, copy_cfm_info_type
      41              :    PUBLIC :: cp_cfm_cleanup_copy_general, &
      42              :              cp_cfm_copy_general, &
      43              :              cp_cfm_create, &
      44              :              cp_cfm_finish_copy_general, &
      45              :              cp_cfm_get_element, &
      46              :              cp_cfm_get_info, &
      47              :              cp_cfm_get_submatrix, &
      48              :              cp_cfm_release, &
      49              :              cp_cfm_set_all, &
      50              :              cp_cfm_set_element, &
      51              :              cp_cfm_set_submatrix, &
      52              :              cp_cfm_start_copy_general, &
      53              :              cp_cfm_to_cfm, &
      54              :              cp_cfm_to_fm, &
      55              :              cp_fm_to_cfm
      56              : 
      57              :    INTERFACE cp_cfm_to_cfm
      58              :       MODULE PROCEDURE cp_cfm_to_cfm_matrix, & ! a full matrix
      59              :          cp_cfm_to_cfm_columns ! just a number of columns
      60              :    END INTERFACE
      61              : 
      62              : ! **************************************************************************************************
      63              : !> \brief Represent a complex full matrix.
      64              : !> \param name           the name of the matrix, used for printing
      65              : !> \param matrix_struct structure of this matrix
      66              : !> \param local_data    array with the data of the matrix (its content depends on
      67              : !>                      the matrix type used: in parallel run it will be in
      68              : !>                      ScaLAPACK format, in sequential run it will simply contain the matrix)
      69              : ! **************************************************************************************************
      70              :    TYPE cp_cfm_type
      71              :       CHARACTER(len=60) :: name = ""
      72              :       TYPE(cp_fm_struct_type), POINTER :: matrix_struct => NULL()
      73              :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER, CONTIGUOUS :: local_data => NULL()
      74              :    END TYPE cp_cfm_type
      75              : 
      76              : ! **************************************************************************************************
      77              : !> \brief Just to build arrays of pointers to matrices.
      78              : !> \param matrix the pointer to the matrix
      79              : ! **************************************************************************************************
      80              :    TYPE cp_cfm_p_type
      81              :       TYPE(cp_cfm_type), POINTER :: matrix => NULL()
      82              :    END TYPE cp_cfm_p_type
      83              : 
      84              : ! **************************************************************************************************
      85              : !> \brief Stores the state of a copy between cp_cfm_start_copy_general
      86              : !>        and cp_cfm_finish_copy_general.
      87              : !> \par History
      88              : !>      Jan 2017  derived type 'copy_info_type' has been created [Mark T]
      89              : !>      Jan 2018  the type 'copy_info_type' has been adapted for complex matrices [Sergey Chulkov]
      90              : ! **************************************************************************************************
      91              :    TYPE copy_cfm_info_type
      92              :       !> number of MPI processes that send data
      93              :       INTEGER                                     :: send_size = -1
      94              :       !> number of locally stored rows (1) and columns (2) of the destination matrix
      95              :       INTEGER, DIMENSION(2)                       :: nlocal_recv = -1
      96              :       !> number of rows (1) and columns (2) of the ScaLAPACK block of the source matrix
      97              :       INTEGER, DIMENSION(2)                       :: nblock_src = -1
      98              :       !> BLACS process grid shape of the source matrix: (1) nproc_row, (2) nproc_col
      99              :       INTEGER, DIMENSION(2)                       :: src_num_pe = -1
     100              :       !> displacements into recv_buf
     101              :       INTEGER, ALLOCATABLE, DIMENSION(:)          :: recv_disp
     102              :       !> MPI requests for non-blocking receive and send operations
     103              :       TYPE(mp_request_type), ALLOCATABLE, DIMENSION(:)          :: recv_request, send_request
     104              :       !> global column and row indices of locally stored elements of the destination matrix
     105              :       INTEGER, DIMENSION(:), POINTER              :: recv_col_indices => NULL(), recv_row_indices => NULL()
     106              :       !> rank of MPI process with BLACS coordinates (prow, pcol)
     107              :       INTEGER, ALLOCATABLE, DIMENSION(:, :)       :: src_blacs2mpi
     108              :       !> receiving and sending buffers for non-blocking MPI communication
     109              :       COMPLEX(kind=dp), ALLOCATABLE, DIMENSION(:) :: recv_buf, send_buf
     110              :    END TYPE copy_cfm_info_type
     111              : 
     112              : CONTAINS
     113              : 
     114              : ! **************************************************************************************************
     115              : !> \brief Creates a new full matrix with the given structure.
     116              : !> \param matrix        matrix to be created
     117              : !> \param matrix_struct structure of the matrix
     118              : !> \param name          name of the matrix
     119              : !> \param nrow ...
     120              : !> \param ncol ...
     121              : !> \param set_zero ...
     122              : !> \note
     123              : !>      preferred allocation routine
     124              : ! **************************************************************************************************
     125       875601 :    SUBROUTINE cp_cfm_create(matrix, matrix_struct, name, nrow, ncol, set_zero)
     126              :       TYPE(cp_cfm_type), INTENT(OUT)                     :: matrix
     127              :       TYPE(cp_fm_struct_type), INTENT(IN), TARGET        :: matrix_struct
     128              :       CHARACTER(len=*), INTENT(in), OPTIONAL             :: name
     129              :       INTEGER, INTENT(IN), OPTIONAL                      :: nrow, ncol
     130              :       LOGICAL, INTENT(IN), OPTIONAL                      :: set_zero
     131              : 
     132              :       INTEGER                                            :: ncol_global, ncol_local, nrow_global, &
     133              :                                                             nrow_local
     134              :       TYPE(cp_blacs_env_type), POINTER                   :: context
     135              :       TYPE(cp_fm_struct_type), POINTER                   :: cfm_struct
     136              : 
     137       878602 :       IF (PRESENT(nrow) .OR. PRESENT(ncol)) THEN
     138         3001 :          CALL cp_fm_struct_get(matrix_struct, nrow_global=nrow_global, ncol_global=ncol_global)
     139         3001 :          IF (PRESENT(nrow)) nrow_global = nrow
     140         3001 :          IF (PRESENT(ncol)) ncol_global = ncol
     141              :          CALL cp_fm_struct_create(cfm_struct, template_fmstruct=matrix_struct, &
     142         3001 :                                   nrow_global=nrow_global, ncol_global=ncol_global)
     143              : 
     144         3001 :          context => cfm_struct%context
     145         3001 :          matrix%matrix_struct => cfm_struct
     146         3001 :          CALL cp_fm_struct_retain(matrix%matrix_struct)
     147              : 
     148         3001 :          nrow_local = cfm_struct%local_leading_dimension
     149         3001 :          ncol_local = MAX(1, cfm_struct%ncol_locals(context%mepos(2)))
     150              : 
     151         3001 :          CALL cp_fm_struct_release(cfm_struct)
     152              :       ELSE
     153       872600 :          context => matrix_struct%context
     154       872600 :          matrix%matrix_struct => matrix_struct
     155       872600 :          CALL cp_fm_struct_retain(matrix%matrix_struct)
     156              : 
     157       872600 :          nrow_local = matrix_struct%local_leading_dimension
     158       872600 :          ncol_local = MAX(1, matrix_struct%ncol_locals(context%mepos(2)))
     159              :       END IF
     160              : 
     161       875601 :       NULLIFY (matrix%local_data)
     162      3502404 :       ALLOCATE (matrix%local_data(nrow_local, ncol_local))
     163              : 
     164       875601 :       IF (PRESENT(set_zero)) THEN
     165        19379 :          IF (set_zero) THEN
     166     23848545 :             matrix%local_data(1:nrow_local, 1:ncol_local) = z_zero
     167              :          END IF
     168              :       END IF
     169              : 
     170       875601 :       IF (PRESENT(name)) THEN
     171       286484 :          matrix%name = name
     172              :       ELSE
     173       589117 :          matrix%name = 'full complex matrix'
     174              :       END IF
     175              : 
     176       875601 :    END SUBROUTINE cp_cfm_create
     177              : 
     178              : ! **************************************************************************************************
     179              : !> \brief Releases a full matrix.
     180              : !> \param matrix the matrix to release
     181              : ! **************************************************************************************************
     182       951185 :    SUBROUTINE cp_cfm_release(matrix)
     183              :       TYPE(cp_cfm_type), INTENT(INOUT)                   :: matrix
     184              : 
     185       951185 :       IF (ASSOCIATED(matrix%local_data)) THEN
     186       875601 :          DEALLOCATE (matrix%local_data)
     187              :       END IF
     188       951185 :       matrix%name = ""
     189       951185 :       CALL cp_fm_struct_release(matrix%matrix_struct)
     190       951185 :    END SUBROUTINE cp_cfm_release
     191              : 
     192              : ! **************************************************************************************************
     193              : !> \brief Set all elements of the full matrix to alpha. Besides, set all
     194              : !>        diagonal matrix elements to beta (if given explicitly).
     195              : !> \param matrix  matrix to initialise
     196              : !> \param alpha   value of off-diagonal matrix elements
     197              : !> \param beta    value of diagonal matrix elements (equal to alpha if absent)
     198              : !> \date    12.06.2001
     199              : !> \author  Matthias Krack
     200              : !> \version 1.0
     201              : ! **************************************************************************************************
     202       120810 :    SUBROUTINE cp_cfm_set_all(matrix, alpha, beta)
     203              :       TYPE(cp_cfm_type), INTENT(IN)                   :: matrix
     204              :       COMPLEX(kind=dp), INTENT(in)                       :: alpha
     205              :       COMPLEX(kind=dp), INTENT(in), OPTIONAL             :: beta
     206              : 
     207              :       INTEGER                                            :: irow_local, nrow_local
     208              : #if defined(__parallel)
     209              :       INTEGER                                            :: icol_local, ncol_local
     210       120810 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
     211              : #endif
     212              : 
     213       362430 :       CALL zcopy(SIZE(matrix%local_data), alpha, 0, matrix%local_data(1, 1), 1)
     214              : 
     215       120810 :       IF (PRESENT(beta)) THEN
     216              : #if defined(__parallel)
     217              :          CALL cp_cfm_get_info(matrix, nrow_local=nrow_local, ncol_local=ncol_local, &
     218        57092 :                               row_indices=row_indices, col_indices=col_indices)
     219              : 
     220        57092 :          icol_local = 1
     221        57092 :          irow_local = 1
     222              : 
     223       235840 :          DO WHILE (irow_local <= nrow_local .AND. icol_local <= ncol_local)
     224       235840 :             IF (row_indices(irow_local) < col_indices(icol_local)) THEN
     225            0 :                irow_local = irow_local + 1
     226       178748 :             ELSE IF (row_indices(irow_local) > col_indices(icol_local)) THEN
     227        72751 :                icol_local = icol_local + 1
     228              :             ELSE
     229       105997 :                matrix%local_data(irow_local, icol_local) = beta
     230       105997 :                irow_local = irow_local + 1
     231       105997 :                icol_local = icol_local + 1
     232              :             END IF
     233              :          END DO
     234              : #else
     235              :          nrow_local = MIN(matrix%matrix_struct%nrow_global, matrix%matrix_struct%ncol_global)
     236              : 
     237              :          DO irow_local = 1, nrow_local
     238              :             matrix%local_data(irow_local, irow_local) = beta
     239              :          END DO
     240              : #endif
     241              :       END IF
     242              : 
     243       120810 :    END SUBROUTINE cp_cfm_set_all
     244              : 
     245              : ! **************************************************************************************************
     246              : !> \brief Get the matrix element by its global index.
     247              : !> \param matrix      full matrix
     248              : !> \param irow_global global row index
     249              : !> \param icol_global global column index
     250              : !> \param alpha       matrix element
     251              : !> \par History
     252              : !>      , TCH, created
     253              : !>      always return the answer
     254              : ! **************************************************************************************************
     255      1609112 :    SUBROUTINE cp_cfm_get_element(matrix, irow_global, icol_global, alpha)
     256              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix
     257              :       INTEGER, INTENT(in)                                :: irow_global, icol_global
     258              :       COMPLEX(kind=dp), INTENT(out)                      :: alpha
     259              : 
     260              : #if defined(__parallel)
     261              :       INTEGER                                            :: icol_local, ipcol, iprow, irow_local, &
     262              :                                                             mypcol, myprow, npcol, nprow
     263              :       INTEGER, DIMENSION(9)                              :: desca
     264              :       TYPE(cp_blacs_env_type), POINTER                   :: context
     265              : #endif
     266              : 
     267              : #if defined(__parallel)
     268      1609112 :       context => matrix%matrix_struct%context
     269      1609112 :       myprow = context%mepos(1)
     270      1609112 :       mypcol = context%mepos(2)
     271      1609112 :       nprow = context%num_pe(1)
     272      1609112 :       npcol = context%num_pe(2)
     273              : 
     274     16091120 :       desca(:) = matrix%matrix_struct%descriptor(:)
     275              : 
     276              :       CALL infog2l(irow_global, icol_global, desca, nprow, npcol, myprow, mypcol, &
     277      1609112 :                    irow_local, icol_local, iprow, ipcol)
     278              : 
     279      1609112 :       IF ((iprow == myprow) .AND. (ipcol == mypcol)) THEN
     280       804556 :          alpha = matrix%local_data(irow_local, icol_local)
     281       804556 :          CALL context%ZGEBS2D('A', ' ', 1, 1, alpha, 1)
     282              :       ELSE
     283       804556 :          CALL context%ZGEBR2D('A', ' ', 1, 1, alpha, 1, iprow, ipcol)
     284              :       END IF
     285              : 
     286              : #else
     287              :       alpha = matrix%local_data(irow_global, icol_global)
     288              : #endif
     289              : 
     290      1609112 :    END SUBROUTINE cp_cfm_get_element
     291              : 
     292              : ! **************************************************************************************************
     293              : !> \brief Set the matrix element (irow_global,icol_global) of the full matrix to alpha.
     294              : !> \param matrix      full matrix
     295              : !> \param irow_global global row index
     296              : !> \param icol_global global column index
     297              : !> \param alpha       value of the matrix element
     298              : !> \date    12.06.2001
     299              : !> \author  Matthias Krack
     300              : !> \version 1.0
     301              : ! **************************************************************************************************
     302      1177756 :    SUBROUTINE cp_cfm_set_element(matrix, irow_global, icol_global, alpha)
     303              :       TYPE(cp_cfm_type), INTENT(IN)                   :: matrix
     304              :       INTEGER, INTENT(in)                                :: irow_global, icol_global
     305              :       COMPLEX(kind=dp), INTENT(in)                       :: alpha
     306              : 
     307              : #if defined(__parallel)
     308              :       INTEGER                                            :: icol_local, ipcol, iprow, irow_local, &
     309              :                                                             mypcol, myprow, npcol, nprow
     310              :       INTEGER, DIMENSION(9)                              :: desca
     311              :       TYPE(cp_blacs_env_type), POINTER                   :: context
     312              : #endif
     313              : 
     314              : #if defined(__parallel)
     315      1177756 :       context => matrix%matrix_struct%context
     316      1177756 :       myprow = context%mepos(1)
     317      1177756 :       mypcol = context%mepos(2)
     318      1177756 :       nprow = context%num_pe(1)
     319      1177756 :       npcol = context%num_pe(2)
     320              : 
     321     11777560 :       desca(:) = matrix%matrix_struct%descriptor(:)
     322              : 
     323              :       CALL infog2l(irow_global, icol_global, desca, nprow, npcol, myprow, mypcol, &
     324      1177756 :                    irow_local, icol_local, iprow, ipcol)
     325              : 
     326      1177756 :       IF ((iprow == myprow) .AND. (ipcol == mypcol)) THEN
     327       588878 :          matrix%local_data(irow_local, icol_local) = alpha
     328              :       END IF
     329              : 
     330              : #else
     331              :       matrix%local_data(irow_global, icol_global) = alpha
     332              : #endif
     333              : 
     334      1177756 :    END SUBROUTINE cp_cfm_set_element
     335              : 
     336              : ! **************************************************************************************************
     337              : !> \brief Extract a sub-matrix from the full matrix:
     338              : !>        op(target_m)(1:n_rows,1:n_cols) = fm(start_row:start_row+n_rows,start_col:start_col+n_cols).
     339              : !>        Sub-matrix 'target_m' is replicated on each CPU. Using this call is expensive.
     340              : !> \param fm          full matrix you want to get the elements from
     341              : !> \param target_m    2-D array to store the extracted sub-matrix
     342              : !> \param start_row   global row index of the matrix element target_m(1,1) (defaults to 1)
     343              : !> \param start_col   global column index of the matrix element target_m(1,1) (defaults to 1)
     344              : !> \param n_rows      number of rows to extract (defaults to size(op(target_m),1))
     345              : !> \param n_cols      number of columns to extract (defaults to size(op(target_m),2))
     346              : !> \param transpose   indicates that the extracted sub-matrix target_m should be transposed:
     347              : !>                    op(target_m) = target_m^T if .TRUE.,
     348              : !>                    op(target_m) = target_m   if .FALSE. (defaults to false)
     349              : !> \par History
     350              : !>   * 04.2016 created borrowing from Fawzi's cp_fm_get_submatrix [Lianheng Tong]
     351              : !>   * 01.2018 drop innermost conditional branching [Sergey Chulkov]
     352              : !> \author Lianheng Tong
     353              : !> \note
     354              : !>      Optimized for full column updates. The matrix target_m is replicated and valid on all CPUs.
     355              : ! **************************************************************************************************
     356        25890 :    SUBROUTINE cp_cfm_get_submatrix(fm, target_m, start_row, start_col, n_rows, n_cols, transpose)
     357              :       TYPE(cp_cfm_type), INTENT(IN)                      :: fm
     358              :       COMPLEX(kind=dp), DIMENSION(:, :), INTENT(out)     :: target_m
     359              :       INTEGER, INTENT(in), OPTIONAL                      :: start_row, start_col, n_rows, n_cols
     360              :       LOGICAL, INTENT(in), OPTIONAL                      :: transpose
     361              : 
     362              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_get_submatrix'
     363              : 
     364        25890 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: local_data
     365              :       INTEGER :: end_col_global, end_col_local, end_row_global, end_row_local, handle, i, j, &
     366              :          ncol_global, ncol_local, nrow_global, nrow_local, start_col_global, start_col_local, &
     367              :          start_row_global, start_row_local, this_col
     368        25890 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
     369              :       LOGICAL                                            :: do_zero, tr_a
     370              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     371              : 
     372        25890 :       CALL timeset(routineN, handle)
     373              : 
     374        77670 :       IF (SIZE(target_m) /= 0) THEN
     375              : #if defined(__parallel)
     376        25890 :          do_zero = .TRUE.
     377              : #else
     378              :          do_zero = .FALSE.
     379              : #endif
     380              : 
     381        25890 :          tr_a = .FALSE.
     382        25890 :          IF (PRESENT(transpose)) tr_a = transpose
     383              : 
     384              :          ! find out the first and last global row/column indices
     385        25890 :          start_row_global = 1
     386        25890 :          start_col_global = 1
     387        25890 :          IF (PRESENT(start_row)) start_row_global = start_row
     388        25890 :          IF (PRESENT(start_col)) start_col_global = start_col
     389              : 
     390        25890 :          IF (tr_a) THEN
     391           80 :             end_row_global = SIZE(target_m, 2)
     392           80 :             end_col_global = SIZE(target_m, 1)
     393              :          ELSE
     394        25810 :             end_row_global = SIZE(target_m, 1)
     395        25810 :             end_col_global = SIZE(target_m, 2)
     396              :          END IF
     397        25890 :          IF (PRESENT(n_rows)) end_row_global = n_rows
     398        25890 :          IF (PRESENT(n_cols)) end_col_global = n_cols
     399              : 
     400        25890 :          end_row_global = end_row_global + start_row_global - 1
     401        25890 :          end_col_global = end_col_global + start_col_global - 1
     402              : 
     403              :          CALL cp_cfm_get_info(matrix=fm, &
     404              :                               nrow_global=nrow_global, ncol_global=ncol_global, &
     405              :                               nrow_local=nrow_local, ncol_local=ncol_local, &
     406        25890 :                               row_indices=row_indices, col_indices=col_indices)
     407        25890 :          IF (end_row_global > nrow_global) THEN
     408              :             end_row_global = nrow_global
     409              :             do_zero = .TRUE.
     410              :          END IF
     411        25890 :          IF (end_col_global > ncol_global) THEN
     412              :             end_col_global = ncol_global
     413              :             do_zero = .TRUE.
     414              :          END IF
     415              : 
     416              :          ! find out row/column indices of locally stored matrix elements that needs to be copied.
     417              :          ! Arrays row_indices and col_indices are assumed to be sorted in ascending order
     418        35720 :          DO start_row_local = 1, nrow_local
     419        35720 :             IF (row_indices(start_row_local) >= start_row_global) EXIT
     420              :          END DO
     421              : 
     422       409428 :          DO end_row_local = start_row_local, nrow_local
     423       409428 :             IF (row_indices(end_row_local) > end_row_global) EXIT
     424              :          END DO
     425        25890 :          end_row_local = end_row_local - 1
     426              : 
     427        61004 :          DO start_col_local = 1, ncol_local
     428        61004 :             IF (col_indices(start_col_local) >= start_col_global) EXIT
     429              :          END DO
     430              : 
     431       166904 :          DO end_col_local = start_col_local, ncol_local
     432       166904 :             IF (col_indices(end_col_local) > end_col_global) EXIT
     433              :          END DO
     434        25890 :          end_col_local = end_col_local - 1
     435              : 
     436        25890 :          para_env => fm%matrix_struct%para_env
     437        25890 :          local_data => fm%local_data
     438              : 
     439              :          ! wipe the content of the target matrix if:
     440              :          !  * the source matrix is distributed across a number of processes, or
     441              :          !  * not all elements of the target matrix will be assigned, e.g.
     442              :          !        when the target matrix is larger then the source matrix
     443              :          IF (do_zero) THEN
     444        77670 :             CALL zcopy(SIZE(target_m), z_zero, 0, target_m(1, 1), 1)
     445              :          END IF
     446              : 
     447        25890 :          IF (tr_a) THEN
     448          288 :             DO j = start_col_local, end_col_local
     449          208 :                this_col = col_indices(j) - start_col_global + 1
     450         1000 :                DO i = start_row_local, end_row_local
     451          920 :                   target_m(this_col, row_indices(i) - start_row_global + 1) = local_data(i, j)
     452              :                END DO
     453              :             END DO
     454              :          ELSE
     455       166616 :             DO j = start_col_local, end_col_local
     456       140806 :                this_col = col_indices(j) - start_col_global + 1
     457      2191663 :                DO i = start_row_local, end_row_local
     458      2165853 :                   target_m(row_indices(i) - start_row_global + 1, this_col) = local_data(i, j)
     459              :                END DO
     460              :             END DO
     461              :          END IF
     462              : 
     463      7996474 :          CALL para_env%sum(target_m)
     464              :       END IF
     465              : 
     466        25890 :       CALL timestop(handle)
     467        25890 :    END SUBROUTINE cp_cfm_get_submatrix
     468              : 
     469              : ! **************************************************************************************************
     470              : !> \brief Set a sub-matrix of the full matrix:
     471              : !>       matrix(start_row:start_row+n_rows,start_col:start_col+n_cols)
     472              : !>       = alpha*op(new_values)(1:n_rows,1:n_cols) +
     473              : !>         beta*matrix(start_row:start_row+n_rows,start_col:start_col+n_cols)
     474              : !> \param matrix      full to update
     475              : !> \param new_values  replicated 2-D array that holds new elements of the updated sub-matrix
     476              : !> \param start_row   global row index of the matrix element new_values(1,1) (defaults to 1)
     477              : !> \param start_col   global column index of the matrix element new_values(1,1) (defaults to 1)
     478              : !> \param n_rows      number of rows to update (defaults to size(op(new_values),1))
     479              : !> \param n_cols      number of columns to update (defaults to size(op(new_values),2))
     480              : !> \param alpha       scale factor for the new values (defaults to (1.0,0.0))
     481              : !> \param beta        scale factor for the old values (defaults to (0.0,0.0))
     482              : !> \param transpose   indicates that the matrix new_values should be transposed:
     483              : !>                    op(new_values) = new_values^T if .TRUE.,
     484              : !>                    op(new_values) = new_values   if .FALSE. (defaults to false)
     485              : !> \par History
     486              : !>   * 04.2016 created borrowing from Fawzi's cp_fm_set_submatrix [Lianheng Tong]
     487              : !>   * 01.2018 drop innermost conditional branching [Sergey Chulkov]
     488              : !> \author Lianheng Tong
     489              : !> \note
     490              : !>      Optimized for alpha=(1.0,0.0), beta=(0.0,0.0)
     491              : !>      All matrix elements 'new_values' need to be valid on all CPUs
     492              : ! **************************************************************************************************
     493        11336 :    SUBROUTINE cp_cfm_set_submatrix(matrix, new_values, start_row, &
     494              :                                    start_col, n_rows, n_cols, alpha, beta, transpose)
     495              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix
     496              :       COMPLEX(kind=dp), DIMENSION(:, :), INTENT(in)      :: new_values
     497              :       INTEGER, INTENT(in), OPTIONAL                      :: start_row, start_col, n_rows, n_cols
     498              :       COMPLEX(kind=dp), INTENT(in), OPTIONAL             :: alpha, beta
     499              :       LOGICAL, INTENT(in), OPTIONAL                      :: transpose
     500              : 
     501              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_set_submatrix'
     502              : 
     503              :       COMPLEX(kind=dp)                                   :: al, be
     504        11336 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: local_data
     505              :       INTEGER :: end_col_global, end_col_local, end_row_global, end_row_local, handle, i, j, &
     506              :          ncol_global, ncol_local, nrow_global, nrow_local, start_col_global, start_col_local, &
     507              :          start_row_global, start_row_local, this_col
     508        11336 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
     509              :       LOGICAL                                            :: tr_a
     510              : 
     511        11336 :       CALL timeset(routineN, handle)
     512              : 
     513        11336 :       al = z_one
     514        11336 :       be = z_zero
     515        11336 :       IF (PRESENT(alpha)) al = alpha
     516        11336 :       IF (PRESENT(beta)) be = beta
     517              : 
     518              :       ! find out the first and last global row/column indices
     519        11336 :       start_row_global = 1
     520        11336 :       start_col_global = 1
     521        11336 :       IF (PRESENT(start_row)) start_row_global = start_row
     522        11336 :       IF (PRESENT(start_col)) start_col_global = start_col
     523              : 
     524        11336 :       tr_a = .FALSE.
     525        11336 :       IF (PRESENT(transpose)) tr_a = transpose
     526              : 
     527            0 :       IF (tr_a) THEN
     528            0 :          end_row_global = SIZE(new_values, 2)
     529            0 :          end_col_global = SIZE(new_values, 1)
     530              :       ELSE
     531        11336 :          end_row_global = SIZE(new_values, 1)
     532        11336 :          end_col_global = SIZE(new_values, 2)
     533              :       END IF
     534        11336 :       IF (PRESENT(n_rows)) end_row_global = n_rows
     535        11336 :       IF (PRESENT(n_cols)) end_col_global = n_cols
     536              : 
     537        11336 :       end_row_global = end_row_global + start_row_global - 1
     538        11336 :       end_col_global = end_col_global + start_col_global - 1
     539              : 
     540              :       CALL cp_cfm_get_info(matrix=matrix, &
     541              :                            nrow_global=nrow_global, ncol_global=ncol_global, &
     542              :                            nrow_local=nrow_local, ncol_local=ncol_local, &
     543        11336 :                            row_indices=row_indices, col_indices=col_indices)
     544        11336 :       IF (end_row_global > nrow_global) end_row_global = nrow_global
     545        11336 :       IF (end_col_global > ncol_global) end_col_global = ncol_global
     546              : 
     547              :       ! find out row/column indices of locally stored matrix elements that needs to be set.
     548              :       ! Arrays row_indices and col_indices are assumed to be sorted in ascending order
     549        26152 :       DO start_row_local = 1, nrow_local
     550        26152 :          IF (row_indices(start_row_local) >= start_row_global) EXIT
     551              :       END DO
     552              : 
     553       226029 :       DO end_row_local = start_row_local, nrow_local
     554       226029 :          IF (row_indices(end_row_local) > end_row_global) EXIT
     555              :       END DO
     556        11336 :       end_row_local = end_row_local - 1
     557              : 
     558        56166 :       DO start_col_local = 1, ncol_local
     559        56166 :          IF (col_indices(start_col_local) >= start_col_global) EXIT
     560              :       END DO
     561              : 
     562        88732 :       DO end_col_local = start_col_local, ncol_local
     563        88732 :          IF (col_indices(end_col_local) > end_col_global) EXIT
     564              :       END DO
     565        11336 :       end_col_local = end_col_local - 1
     566              : 
     567        11336 :       local_data => matrix%local_data
     568              : 
     569        11336 :       IF (al == z_one .AND. be == z_zero) THEN
     570        11336 :          IF (tr_a) THEN
     571            0 :             DO j = start_col_local, end_col_local
     572            0 :                this_col = col_indices(j) - start_col_global + 1
     573            0 :                DO i = start_row_local, end_row_local
     574            0 :                   local_data(i, j) = new_values(this_col, row_indices(i) - start_row_global + 1)
     575              :                END DO
     576              :             END DO
     577              :          ELSE
     578        88732 :             DO j = start_col_local, end_col_local
     579        77396 :                this_col = col_indices(j) - start_col_global + 1
     580      1305346 :                DO i = start_row_local, end_row_local
     581      1294010 :                   local_data(i, j) = new_values(row_indices(i) - start_row_global + 1, this_col)
     582              :                END DO
     583              :             END DO
     584              :          END IF
     585              :       ELSE
     586            0 :          IF (tr_a) THEN
     587            0 :             DO j = start_col_local, end_col_local
     588            0 :                this_col = col_indices(j) - start_col_global + 1
     589            0 :                DO i = start_row_local, end_row_local
     590              :                   local_data(i, j) = al*new_values(this_col, row_indices(i) - start_row_global + 1) + &
     591            0 :                                      be*local_data(i, j)
     592              :                END DO
     593              :             END DO
     594              :          ELSE
     595            0 :             DO j = start_col_local, end_col_local
     596            0 :                this_col = col_indices(j) - start_col_global + 1
     597            0 :                DO i = start_row_local, end_row_local
     598              :                   local_data(i, j) = al*new_values(row_indices(i) - start_row_global + 1, this_col) + &
     599            0 :                                      be*local_data(i, j)
     600              :                END DO
     601              :             END DO
     602              :          END IF
     603              :       END IF
     604              : 
     605        11336 :       CALL timestop(handle)
     606        11336 :    END SUBROUTINE cp_cfm_set_submatrix
     607              : 
     608              : ! **************************************************************************************************
     609              : !> \brief Returns information about a full matrix.
     610              : !> \param matrix        matrix
     611              : !> \param name          name of the matrix
     612              : !> \param nrow_global   total number of rows
     613              : !> \param ncol_global   total number of columns
     614              : !> \param nrow_block    number of rows per ScaLAPACK block
     615              : !> \param ncol_block    number of columns per ScaLAPACK block
     616              : !> \param nrow_local    number of locally stored rows
     617              : !> \param ncol_local    number of locally stored columns
     618              : !> \param row_indices   global indices of locally stored rows
     619              : !> \param col_indices   global indices of locally stored columns
     620              : !> \param local_data    locally stored matrix elements
     621              : !> \param context       BLACS context
     622              : !> \param matrix_struct matrix structure
     623              : !> \param para_env      parallel environment
     624              : !> \date    12.06.2001
     625              : !> \author  Matthias Krack
     626              : !> \version 1.0
     627              : ! **************************************************************************************************
     628      1803287 :    SUBROUTINE cp_cfm_get_info(matrix, name, nrow_global, ncol_global, &
     629              :                               nrow_block, ncol_block, nrow_local, ncol_local, &
     630              :                               row_indices, col_indices, local_data, context, &
     631              :                               matrix_struct, para_env)
     632              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix
     633              :       CHARACTER(len=*), INTENT(OUT), OPTIONAL            :: name
     634              :       INTEGER, INTENT(OUT), OPTIONAL                     :: nrow_global, ncol_global, nrow_block, &
     635              :                                                             ncol_block, nrow_local, ncol_local
     636              :       INTEGER, DIMENSION(:), OPTIONAL, POINTER           :: row_indices, col_indices
     637              :       COMPLEX(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
     638              :          OPTIONAL, POINTER                               :: local_data
     639              :       TYPE(cp_blacs_env_type), OPTIONAL, POINTER         :: context
     640              :       TYPE(cp_fm_struct_type), OPTIONAL, POINTER         :: matrix_struct
     641              :       TYPE(mp_para_env_type), OPTIONAL, POINTER          :: para_env
     642              : 
     643            0 :       IF (PRESENT(name)) name = matrix%name
     644      1803287 :       IF (PRESENT(matrix_struct)) matrix_struct => matrix%matrix_struct
     645      1803287 :       IF (PRESENT(local_data)) local_data => matrix%local_data ! not hiding things anymore :-(
     646              : 
     647              :       CALL cp_fm_struct_get(matrix%matrix_struct, nrow_local=nrow_local, &
     648              :                             ncol_local=ncol_local, nrow_global=nrow_global, &
     649              :                             ncol_global=ncol_global, nrow_block=nrow_block, &
     650              :                             ncol_block=ncol_block, context=context, &
     651      1803287 :                             row_indices=row_indices, col_indices=col_indices, para_env=para_env)
     652              : 
     653      1803287 :    END SUBROUTINE cp_cfm_get_info
     654              : 
     655              : ! **************************************************************************************************
     656              : !> \brief Copy content of a full matrix into another full matrix of the same size.
     657              : !> \param source      source matrix
     658              : !> \param destination destination matrix
     659              : !> \author Joost VandeVondele
     660              : ! **************************************************************************************************
     661       862053 :    SUBROUTINE cp_cfm_to_cfm_matrix(source, destination)
     662              :       TYPE(cp_cfm_type), INTENT(IN)                      :: source, destination
     663              : 
     664              :       INTEGER                                            :: npcol, nprow
     665              : 
     666       862053 :       nprow = source%matrix_struct%context%num_pe(1)
     667       862053 :       npcol = source%matrix_struct%context%num_pe(2)
     668              : 
     669       862053 :       IF (.NOT. cp2k_is_parallel .OR. &
     670              :           cp_fm_struct_equivalent(source%matrix_struct, &
     671              :                                   destination%matrix_struct)) THEN
     672       862053 :          IF (SIZE(source%local_data, 1) /= SIZE(destination%local_data, 1) .OR. &
     673              :              SIZE(source%local_data, 2) /= SIZE(destination%local_data, 2)) THEN
     674            0 :             CPABORT("internal local_data has different sizes")
     675              :          END IF
     676      2586159 :          CALL zcopy(SIZE(source%local_data), source%local_data(1, 1), 1, destination%local_data(1, 1), 1)
     677              :       ELSE
     678            0 :          IF (source%matrix_struct%nrow_global /= destination%matrix_struct%nrow_global) THEN
     679            0 :             CPABORT("cannot copy between full matrixes of differen sizes")
     680              :          END IF
     681            0 :          IF (source%matrix_struct%ncol_global /= destination%matrix_struct%ncol_global) THEN
     682            0 :             CPABORT("cannot copy between full matrixes of differen sizes")
     683              :          END IF
     684              : #if defined(__parallel)
     685              :          CALL pzcopy(source%matrix_struct%nrow_global* &
     686              :                      source%matrix_struct%ncol_global, &
     687              :                      source%local_data(1, 1), 1, 1, source%matrix_struct%descriptor, 1, &
     688            0 :                      destination%local_data(1, 1), 1, 1, destination%matrix_struct%descriptor, 1)
     689              : #else
     690              :          CPABORT("cp_cfm_to_cfm_matrix: pzcopy needs __parallel build flag")
     691              : #endif
     692              :       END IF
     693       862053 :    END SUBROUTINE cp_cfm_to_cfm_matrix
     694              : 
     695              : ! **************************************************************************************************
     696              : !> \brief Copy a number of sequential columns of a full matrix into another full matrix.
     697              : !> \param msource      source matrix
     698              : !> \param mtarget      destination matrix
     699              : !> \param ncol         number of columns to copy
     700              : !> \param source_start global index of the first column to copy within the source matrix
     701              : !> \param target_start global index of the first column to copy within the destination matrix
     702              : ! **************************************************************************************************
     703        38329 :    SUBROUTINE cp_cfm_to_cfm_columns(msource, mtarget, ncol, source_start, &
     704              :                                     target_start)
     705              : 
     706              :       TYPE(cp_cfm_type), INTENT(IN)                   :: msource, mtarget
     707              :       INTEGER, INTENT(IN)                                :: ncol
     708              :       INTEGER, INTENT(IN), OPTIONAL                      :: source_start, target_start
     709              : 
     710              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_to_cfm_columns'
     711              : 
     712        38329 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: a, b
     713              :       INTEGER                                            :: handle, n, ss, ts
     714              : #if defined(__parallel)
     715              :       INTEGER                                            :: i
     716              :       INTEGER, DIMENSION(9)                              :: desca, descb
     717              : #endif
     718              : 
     719        38329 :       CALL timeset(routineN, handle)
     720              : 
     721        38329 :       ss = 1
     722        38329 :       ts = 1
     723              : 
     724        38329 :       IF (PRESENT(source_start)) ss = source_start
     725        38329 :       IF (PRESENT(target_start)) ts = target_start
     726              : 
     727        38329 :       n = msource%matrix_struct%nrow_global
     728              : 
     729        38329 :       a => msource%local_data
     730        38329 :       b => mtarget%local_data
     731              : 
     732              : #if defined(__parallel)
     733       383290 :       desca(:) = msource%matrix_struct%descriptor(:)
     734       383290 :       descb(:) = mtarget%matrix_struct%descriptor(:)
     735       766134 :       DO i = 0, ncol - 1
     736       766134 :          CALL pzcopy(n, a(1, 1), 1, ss + i, desca, 1, b(1, 1), 1, ts + i, descb, 1)
     737              :       END DO
     738              : #else
     739              :       CALL zcopy(ncol*n, a(1, ss), 1, b(1, ts), 1)
     740              : #endif
     741              : 
     742        38329 :       CALL timestop(handle)
     743              : 
     744        38329 :    END SUBROUTINE cp_cfm_to_cfm_columns
     745              : 
     746              : ! **************************************************************************************************
     747              : !> \brief Copy just a triangular matrix.
     748              : !> \param msource source matrix
     749              : !> \param mtarget target matrix
     750              : !> \param uplo    'U' for upper triangular, 'L' for lower triangular
     751              : ! **************************************************************************************************
     752            0 :    SUBROUTINE cp_cfm_to_cfm_triangular(msource, mtarget, uplo)
     753              :       TYPE(cp_cfm_type), INTENT(IN)                   :: msource, mtarget
     754              :       CHARACTER(len=*), INTENT(IN)                       :: uplo
     755              : 
     756              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_to_cfm_triangular'
     757              : 
     758            0 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: aa, bb
     759              :       INTEGER                                            :: handle, ncol, nrow
     760              : #if defined(__parallel)
     761              :       INTEGER, DIMENSION(9)                              :: desca, descb
     762              : #endif
     763              : 
     764            0 :       CALL timeset(routineN, handle)
     765              : 
     766            0 :       nrow = msource%matrix_struct%nrow_global
     767            0 :       ncol = msource%matrix_struct%ncol_global
     768              : 
     769            0 :       aa => msource%local_data
     770            0 :       bb => mtarget%local_data
     771              : 
     772              : #if defined(__parallel)
     773            0 :       desca(:) = msource%matrix_struct%descriptor(:)
     774            0 :       descb(:) = mtarget%matrix_struct%descriptor(:)
     775            0 :       CALL pzlacpy(uplo, nrow, ncol, aa(1, 1), 1, 1, desca, bb(1, 1), 1, 1, descb)
     776              : #else
     777              :       CALL zlacpy(uplo, nrow, ncol, aa(1, 1), nrow, bb(1, 1), nrow)
     778              : #endif
     779              : 
     780            0 :       CALL timestop(handle)
     781            0 :    END SUBROUTINE cp_cfm_to_cfm_triangular
     782              : 
     783              : ! **************************************************************************************************
     784              : !> \brief Copy real and imaginary parts of a complex full matrix into
     785              : !>        separate real-value full matrices.
     786              : !> \param msource    complex matrix
     787              : !> \param mtargetr   (optional) real part of the source matrix
     788              : !> \param mtargeti   (optional) imaginary part of the source matrix
     789              : !> \note
     790              : !>        Matrix structures are assumed to be equivalent.
     791              : ! **************************************************************************************************
     792       413670 :    SUBROUTINE cp_cfm_to_fm(msource, mtargetr, mtargeti)
     793              : 
     794              :       TYPE(cp_cfm_type), INTENT(IN)                      :: msource
     795              :       TYPE(cp_fm_type), INTENT(IN), OPTIONAL             :: mtargetr, mtargeti
     796              : 
     797              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_cfm_to_fm'
     798              : 
     799       413670 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: zmat
     800              :       INTEGER                                            :: handle
     801       413670 :       REAL(kind=dp), DIMENSION(:, :), POINTER            :: imat, rmat
     802              : 
     803       413670 :       CALL timeset(routineN, handle)
     804              : 
     805       413670 :       zmat => msource%local_data
     806       413670 :       IF (PRESENT(mtargetr)) THEN
     807       385896 :          rmat => mtargetr%local_data
     808              :          IF ((.NOT. cp_fm_struct_equivalent(mtargetr%matrix_struct, msource%matrix_struct)) .OR. &
     809       385896 :              (SIZE(rmat, 1) /= SIZE(zmat, 1)) .OR. &
     810              :              (SIZE(rmat, 2) /= SIZE(zmat, 2))) THEN
     811            0 :             CPABORT("size of local_data of mtargetr differ to msource")
     812              :          END IF
     813              :          ! copy local data
     814    526410404 :          rmat = REAL(zmat, kind=dp)
     815              :       ELSE
     816       413670 :          NULLIFY (rmat)
     817              :       END IF
     818       413670 :       IF (PRESENT(mtargeti)) THEN
     819       333666 :          imat => mtargeti%local_data
     820              :          IF ((.NOT. cp_fm_struct_equivalent(mtargeti%matrix_struct, msource%matrix_struct)) .OR. &
     821       333666 :              (SIZE(imat, 1) /= SIZE(zmat, 1)) .OR. &
     822              :              (SIZE(imat, 2) /= SIZE(zmat, 2))) THEN
     823            0 :             CPABORT("size of local_data of mtargeti differ to msource")
     824              :          END IF
     825              :          ! copy local data
     826    525555584 :          imat = REAL(AIMAG(zmat), kind=dp)
     827              :       ELSE
     828       413670 :          NULLIFY (imat)
     829              :       END IF
     830              : 
     831       413670 :       CALL timestop(handle)
     832              : 
     833       413670 :    END SUBROUTINE cp_cfm_to_fm
     834              : 
     835              : ! **************************************************************************************************
     836              : !> \brief Construct a complex full matrix by taking its real and imaginary parts from
     837              : !>        two separate real-value full matrices.
     838              : !> \param msourcer   (optional) real part of the complex matrix (defaults to 0.0)
     839              : !> \param msourcei   (optional) imaginary part of the complex matrix (defaults to 0.0)
     840              : !> \param mtarget    resulting complex matrix
     841              : !> \note
     842              : !>        Matrix structures are assumed to be equivalent.
     843              : ! **************************************************************************************************
     844       341855 :    SUBROUTINE cp_fm_to_cfm(msourcer, msourcei, mtarget)
     845              :       TYPE(cp_fm_type), INTENT(IN), OPTIONAL             :: msourcer, msourcei
     846              :       TYPE(cp_cfm_type), INTENT(IN)                      :: mtarget
     847              : 
     848              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_fm_to_cfm'
     849              : 
     850       341855 :       COMPLEX(kind=dp), DIMENSION(:, :), POINTER         :: zmat
     851              :       INTEGER                                            :: handle, mode
     852       341855 :       REAL(kind=dp), DIMENSION(:, :), POINTER            :: imat, rmat
     853              : 
     854       341855 :       CALL timeset(routineN, handle)
     855              : 
     856       341855 :       mode = 0
     857       341855 :       zmat => mtarget%local_data
     858       341855 :       IF (PRESENT(msourcer)) THEN
     859       330683 :          rmat => msourcer%local_data
     860              :          IF ((.NOT. cp_fm_struct_equivalent(msourcer%matrix_struct, mtarget%matrix_struct)) .OR. &
     861       330683 :              (SIZE(rmat, 1) /= SIZE(zmat, 1)) .OR. &
     862              :              (SIZE(rmat, 2) /= SIZE(zmat, 2))) THEN
     863            0 :             CPABORT("size of local_data of msourcer differ to mtarget")
     864              :          END IF
     865              :          mode = mode + 1
     866              :       ELSE
     867              :          NULLIFY (rmat)
     868              :       END IF
     869       341855 :       IF (PRESENT(msourcei)) THEN
     870       246911 :          imat => msourcei%local_data
     871              :          IF ((.NOT. cp_fm_struct_equivalent(msourcei%matrix_struct, mtarget%matrix_struct)) .OR. &
     872       246911 :              (SIZE(imat, 1) /= SIZE(zmat, 1)) .OR. &
     873              :              (SIZE(imat, 2) /= SIZE(zmat, 2))) THEN
     874            0 :             CPABORT("size of local_data of msourcei differ to mtarget")
     875              :          END IF
     876       246911 :          mode = mode + 2
     877              :       ELSE
     878              :          NULLIFY (imat)
     879              :       END IF
     880              :       ! copy local data
     881              :       SELECT CASE (mode)
     882              :       CASE (0)
     883            0 :          zmat(:, :) = z_zero
     884              :       CASE (1)
     885     32864290 :          zmat(:, :) = CMPLX(rmat(:, :), 0.0_dp, kind=dp)
     886              :       CASE (2)
     887      1096362 :          zmat(:, :) = CMPLX(0.0_dp, imat(:, :), kind=dp)
     888              :       CASE (3)
     889    351073988 :          zmat(:, :) = CMPLX(rmat(:, :), imat(:, :), kind=dp)
     890              :       END SELECT
     891              : 
     892       341855 :       CALL timestop(handle)
     893              : 
     894       341855 :    END SUBROUTINE cp_fm_to_cfm
     895              : 
     896              : ! **************************************************************************************************
     897              : !> \brief General copy of a cfm matrix to another cfm matrix.
     898              : !>        Uses non-blocking MPI rather than ScaLAPACK.
     899              : !> \param source input cfm matrix
     900              : !> \param destination output cfm matrix
     901              : !> \param para_env parallel environment corresponding to the BLACS env that covers all parts
     902              : !>                  of the input and output matrices
     903              : ! **************************************************************************************************
     904        12060 :    SUBROUTINE cp_cfm_copy_general(source, destination, para_env)
     905              :       TYPE(cp_cfm_type), INTENT(IN)                      :: source, destination
     906              :       TYPE(mp_para_env_type), INTENT(IN), POINTER        :: para_env
     907              : 
     908              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_cfm_copy_general'
     909              : 
     910              :       INTEGER                                            :: handle
     911       108540 :       TYPE(copy_cfm_info_type)                           :: info
     912              : 
     913        12060 :       CALL timeset(routineN, handle)
     914              : 
     915        12060 :       CALL cp_cfm_start_copy_general(source, destination, para_env, info)
     916        12060 :       IF (ASSOCIATED(destination%matrix_struct)) THEN
     917        12060 :          CALL cp_cfm_finish_copy_general(destination, info)
     918              :       END IF
     919        12060 :       IF (ASSOCIATED(source%matrix_struct)) THEN
     920        12038 :          CALL cp_cfm_cleanup_copy_general(info)
     921              :       END IF
     922              : 
     923        12060 :       CALL timestop(handle)
     924        12060 :    END SUBROUTINE cp_cfm_copy_general
     925              : 
     926              : ! **************************************************************************************************
     927              : !> \brief Initiate the copy operation: get distribution data, post MPI isend and irecvs.
     928              : !> \param source       input complex-valued fm matrix
     929              : !> \param destination  output complex-valued fm matrix
     930              : !> \param para_env     parallel environment corresponding to the BLACS env that covers all parts
     931              : !>                     of the input and output matrices
     932              : !> \param info         all of the data that will be needed to complete the copy operation
     933              : !> \note a slightly modified version of the subroutine cp_fm_start_copy_general() that uses
     934              : !>       allocatable arrays instead of pointers wherever possible.
     935              : ! **************************************************************************************************
     936       244520 :    SUBROUTINE cp_cfm_start_copy_general(source, destination, para_env, info)
     937              :       TYPE(cp_cfm_type), INTENT(IN)                      :: source, destination
     938              :       TYPE(mp_para_env_type), INTENT(IN), POINTER        :: para_env
     939              :       TYPE(copy_cfm_info_type), INTENT(out)              :: info
     940              : 
     941              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_start_copy_general'
     942              : 
     943              :       INTEGER :: dest_p_i, dest_q_j, global_rank, global_size, handle, i, j, k, mpi_rank, &
     944              :          ncol_block_dest, ncol_block_src, ncol_local_recv, ncol_local_send, ncols, &
     945              :          nrow_block_dest, nrow_block_src, nrow_local_recv, nrow_local_send, nrows, p, q, &
     946              :          recv_rank, recv_size, send_rank, send_size
     947        24452 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: all_ranks, dest2global, dest_p, dest_q, &
     948        48904 :                                                             recv_count, send_count, send_disp, &
     949        24452 :                                                             source2global, src_p, src_q
     950        24452 :       INTEGER, ALLOCATABLE, DIMENSION(:, :)              :: dest_blacs2mpi
     951              :       INTEGER, DIMENSION(2)                              :: dest_block, dest_block_tmp, dest_num_pe, &
     952              :                                                             src_block, src_block_tmp, src_num_pe
     953        48904 :       INTEGER, DIMENSION(:), POINTER                     :: recv_col_indices, recv_row_indices, &
     954        48904 :                                                             send_col_indices, send_row_indices
     955              :       TYPE(cp_fm_struct_type), POINTER                   :: recv_dist, send_dist
     956       342328 :       TYPE(mp_request_type), DIMENSION(6)                :: recv_req, send_req
     957              : 
     958        24452 :       CALL timeset(routineN, handle)
     959              : 
     960              :       IF (.NOT. cp2k_is_parallel) THEN
     961              :          ! Just copy all of the matrix data into a 'send buffer', to be unpacked later
     962              :          nrow_local_send = SIZE(source%local_data, 1)
     963              :          ncol_local_send = SIZE(source%local_data, 2)
     964              :          ALLOCATE (info%send_buf(nrow_local_send*ncol_local_send))
     965              :          k = 0
     966              :          DO j = 1, ncol_local_send
     967              :             DO i = 1, nrow_local_send
     968              :                k = k + 1
     969              :                info%send_buf(k) = source%local_data(i, j)
     970              :             END DO
     971              :          END DO
     972              :       ELSE
     973        24452 :          NULLIFY (recv_dist, send_dist)
     974        24452 :          NULLIFY (recv_col_indices, recv_row_indices, send_col_indices, send_row_indices)
     975              : 
     976              :          ! The 'global' communicator contains both the source and destination decompositions
     977        24452 :          global_size = para_env%num_pe
     978        24452 :          global_rank = para_env%mepos
     979              : 
     980              :          ! The source/send decomposition and destination/recv decompositions may only exist on
     981              :          ! on a subset of the processes involved in the communication
     982              :          ! Check if the source and/or destination arguments are .not. ASSOCIATED():
     983              :          ! if so, skip the send / recv parts (since these processes do not participate in the sending/receiving distribution)
     984        24452 :          IF (ASSOCIATED(destination%matrix_struct)) THEN
     985        24452 :             recv_dist => destination%matrix_struct
     986        24452 :             recv_rank = recv_dist%para_env%mepos
     987              :          ELSE
     988            0 :             recv_rank = mp_proc_null
     989              :          END IF
     990              : 
     991        24452 :          IF (ASSOCIATED(source%matrix_struct)) THEN
     992        18234 :             send_dist => source%matrix_struct
     993        18234 :             send_rank = send_dist%para_env%mepos
     994              :          ELSE
     995         6218 :             send_rank = mp_proc_null
     996              :          END IF
     997              : 
     998              :          ! Map the rank in the source/dest communicator to the global rank
     999        73356 :          ALLOCATE (all_ranks(0:global_size - 1))
    1000              : 
    1001        24452 :          CALL para_env%allgather(send_rank, all_ranks)
    1002        24452 :          IF (ASSOCIATED(destination%matrix_struct)) THEN
    1003       122260 :             ALLOCATE (source2global(0:COUNT(all_ranks /= mp_proc_null) - 1))
    1004        73356 :             DO i = 0, global_size - 1
    1005        73356 :                IF (all_ranks(i) /= mp_proc_null) THEN
    1006        36468 :                   source2global(all_ranks(i)) = i
    1007              :                END IF
    1008              :             END DO
    1009              :          END IF
    1010              : 
    1011        24452 :          CALL para_env%allgather(recv_rank, all_ranks)
    1012        24452 :          IF (ASSOCIATED(source%matrix_struct)) THEN
    1013        91170 :             ALLOCATE (dest2global(0:COUNT(all_ranks /= mp_proc_null) - 1))
    1014        54702 :             DO i = 0, global_size - 1
    1015        54702 :                IF (all_ranks(i) /= mp_proc_null) THEN
    1016        36468 :                   dest2global(all_ranks(i)) = i
    1017              :                END IF
    1018              :             END DO
    1019              :          END IF
    1020        24452 :          DEALLOCATE (all_ranks)
    1021              : 
    1022              :          ! Some data from the two decompositions will be needed by all processes in the global group :
    1023              :          ! process grid shape, block size, and the BLACS-to-MPI mapping
    1024              : 
    1025              :          ! The global root process will receive the data (from the root process in each decomposition)
    1026       171164 :          send_req(:) = mp_request_null
    1027        24452 :          IF (global_rank == 0) THEN
    1028        85582 :             recv_req(:) = mp_request_null
    1029        12226 :             CALL para_env%irecv(src_block, mp_any_source, recv_req(1), tag=src_tag)
    1030        12226 :             CALL para_env%irecv(dest_block, mp_any_source, recv_req(2), tag=dest_tag)
    1031        12226 :             CALL para_env%irecv(src_num_pe, mp_any_source, recv_req(3), tag=src_tag)
    1032        12226 :             CALL para_env%irecv(dest_num_pe, mp_any_source, recv_req(4), tag=dest_tag)
    1033              :          END IF
    1034              : 
    1035        24452 :          IF (ASSOCIATED(source%matrix_struct)) THEN
    1036        18234 :             IF ((send_rank == 0)) THEN
    1037              :                ! need to use separate buffers here in case this is actually global rank 0
    1038        36678 :                src_block_tmp = [send_dist%nrow_block, send_dist%ncol_block]
    1039        12226 :                CALL para_env%isend(src_block_tmp, 0, send_req(1), tag=src_tag)
    1040        12226 :                CALL para_env%isend(send_dist%context%num_pe, 0, send_req(2), tag=src_tag)
    1041              :             END IF
    1042              :          END IF
    1043              : 
    1044        24452 :          IF (ASSOCIATED(destination%matrix_struct)) THEN
    1045        24452 :             IF ((recv_rank == 0)) THEN
    1046        36678 :                dest_block_tmp = [recv_dist%nrow_block, recv_dist%ncol_block]
    1047        12226 :                CALL para_env%isend(dest_block_tmp, 0, send_req(3), tag=dest_tag)
    1048        12226 :                CALL para_env%isend(recv_dist%context%num_pe, 0, send_req(4), tag=dest_tag)
    1049              :             END IF
    1050              :          END IF
    1051              : 
    1052        24452 :          IF (global_rank == 0) THEN
    1053        12226 :             CALL mp_waitall(recv_req(1:4))
    1054              :             ! Now we know the process decomposition, we can allocate the arrays to hold the blacs2mpi mapping
    1055            0 :             ALLOCATE (info%src_blacs2mpi(0:src_num_pe(1) - 1, 0:src_num_pe(2) - 1), &
    1056        85582 :                       dest_blacs2mpi(0:dest_num_pe(1) - 1, 0:dest_num_pe(2) - 1))
    1057        12226 :             CALL para_env%irecv(info%src_blacs2mpi, mp_any_source, recv_req(5), tag=src_tag)
    1058        12226 :             CALL para_env%irecv(dest_blacs2mpi, mp_any_source, recv_req(6), tag=dest_tag)
    1059              :          END IF
    1060              : 
    1061        24452 :          IF (ASSOCIATED(source%matrix_struct)) THEN
    1062        18234 :             IF ((send_rank == 0)) THEN
    1063        12226 :                CALL para_env%isend(send_dist%context%blacs2mpi(:, :), 0, send_req(5), tag=src_tag)
    1064              :             END IF
    1065              :          END IF
    1066              : 
    1067        24452 :          IF (ASSOCIATED(destination%matrix_struct)) THEN
    1068        24452 :             IF ((recv_rank == 0)) THEN
    1069        12226 :                CALL para_env%isend(recv_dist%context%blacs2mpi(:, :), 0, send_req(6), tag=dest_tag)
    1070              :             END IF
    1071              :          END IF
    1072              : 
    1073        24452 :          IF (global_rank == 0) THEN
    1074        12226 :             CALL mp_waitall(recv_req(5:6))
    1075              :          END IF
    1076              : 
    1077              :          ! Finally, broadcast the data to all processes in the global communicator
    1078        24452 :          CALL para_env%bcast(src_block, 0)
    1079        24452 :          CALL para_env%bcast(dest_block, 0)
    1080        24452 :          CALL para_env%bcast(src_num_pe, 0)
    1081        24452 :          CALL para_env%bcast(dest_num_pe, 0)
    1082        73356 :          info%src_num_pe(1:2) = src_num_pe(1:2)
    1083        73356 :          info%nblock_src(1:2) = src_block(1:2)
    1084        24452 :          IF (global_rank /= 0) THEN
    1085            0 :             ALLOCATE (info%src_blacs2mpi(0:src_num_pe(1) - 1, 0:src_num_pe(2) - 1), &
    1086        85582 :                       dest_blacs2mpi(0:dest_num_pe(1) - 1, 0:dest_num_pe(2) - 1))
    1087              :          END IF
    1088        24452 :          CALL para_env%bcast(info%src_blacs2mpi, 0)
    1089        24452 :          CALL para_env%bcast(dest_blacs2mpi, 0)
    1090              : 
    1091        24452 :          recv_size = dest_num_pe(1)*dest_num_pe(2)
    1092        24452 :          send_size = src_num_pe(1)*src_num_pe(2)
    1093        24452 :          info%send_size = send_size
    1094        24452 :          CALL mp_waitall(send_req(:))
    1095              : 
    1096              :          ! Setup is now complete, we can start the actual communication here.
    1097              :          ! The order implemented here is:
    1098              :          !  DEST_1
    1099              :          !      compute recv sizes
    1100              :          !      call irecv
    1101              :          !  SRC_1
    1102              :          !      compute send sizes
    1103              :          !      pack send buffers
    1104              :          !      call isend
    1105              :          !  DEST_2
    1106              :          !      wait for the recvs and unpack buffers (this part eventually will go into another
    1107              :          !         routine to allow comms to run concurrently)
    1108              :          !  SRC_2
    1109              :          !      wait for the sends
    1110              : 
    1111              :          ! DEST_1
    1112        24452 :          IF (ASSOCIATED(destination%matrix_struct)) THEN
    1113              :             CALL cp_fm_struct_get(recv_dist, row_indices=recv_row_indices, &
    1114        24452 :                                   col_indices=recv_col_indices)
    1115        24452 :             info%recv_col_indices => recv_col_indices
    1116        24452 :             info%recv_row_indices => recv_row_indices
    1117        24452 :             nrow_block_src = src_block(1)
    1118        24452 :             ncol_block_src = src_block(2)
    1119       158728 :             ALLOCATE (recv_count(0:send_size - 1), info%recv_disp(0:send_size - 1), info%recv_request(0:send_size - 1))
    1120              : 
    1121              :             ! Determine the recv counts, allocate the receive buffers, call mpi_irecv for all the non-zero sized receives
    1122        24452 :             nrow_local_recv = recv_dist%nrow_locals(recv_dist%context%mepos(1))
    1123        24452 :             ncol_local_recv = recv_dist%ncol_locals(recv_dist%context%mepos(2))
    1124        24452 :             info%nlocal_recv(1) = nrow_local_recv
    1125        24452 :             info%nlocal_recv(2) = ncol_local_recv
    1126              :             ! Initialise src_p, src_q arrays (sized using number of rows/cols in the receiving distribution)
    1127       122260 :             ALLOCATE (src_p(nrow_local_recv), src_q(ncol_local_recv))
    1128       332338 :             DO i = 1, nrow_local_recv
    1129              :                ! For each local row we will receive, we look up its global row (in recv_row_indices),
    1130              :                ! then work out which row block it comes from, and which process row that row block comes from.
    1131       332338 :                src_p(i) = MOD(((recv_row_indices(i) - 1)/nrow_block_src), src_num_pe(1))
    1132              :             END DO
    1133       459594 :             DO j = 1, ncol_local_recv
    1134              :                ! Similarly for the columns
    1135       459594 :                src_q(j) = MOD(((recv_col_indices(j) - 1)/ncol_block_src), src_num_pe(2))
    1136              :             END DO
    1137              :             ! src_p/q now contains the process row/column ID that will send data to that row/column
    1138              : 
    1139        48904 :             DO q = 0, src_num_pe(2) - 1
    1140       459594 :                ncols = COUNT(src_q == q)
    1141        85372 :                DO p = 0, src_num_pe(1) - 1
    1142       587988 :                   nrows = COUNT(src_p == p)
    1143              :                   ! Use the send_dist here as we are looking up the processes where the data comes from
    1144        60920 :                   recv_count(info%src_blacs2mpi(p, q)) = nrows*ncols
    1145              :                END DO
    1146              :             END DO
    1147        24452 :             DEALLOCATE (src_p, src_q)
    1148              : 
    1149              :             ! Use one long buffer (and displacements into that buffer)
    1150              :             !     this prevents the need for a rectangular array where not all elements will be populated
    1151       109824 :             ALLOCATE (info%recv_buf(SUM(recv_count(:))))
    1152        24452 :             info%recv_disp(0) = 0
    1153        36468 :             DO i = 1, send_size - 1
    1154        36468 :                info%recv_disp(i) = info%recv_disp(i - 1) + recv_count(i - 1)
    1155              :             END DO
    1156              : 
    1157              :             ! Issue receive calls on ranks which expect data
    1158        60920 :             DO k = 0, send_size - 1
    1159        60920 :                IF (recv_count(k) > 0) THEN
    1160              :                   CALL para_env%irecv(info%recv_buf(info%recv_disp(k) + 1:info%recv_disp(k) + recv_count(k)), &
    1161        24452 :                                       source2global(k), info%recv_request(k))
    1162              :                ELSE
    1163        12016 :                   info%recv_request(k) = mp_request_null
    1164              :                END IF
    1165              :             END DO
    1166        24452 :             DEALLOCATE (source2global)
    1167              :          END IF ! ASSOCIATED(destination)
    1168              : 
    1169              :          ! SRC_1
    1170        24452 :          IF (ASSOCIATED(source%matrix_struct)) THEN
    1171              :             CALL cp_fm_struct_get(send_dist, row_indices=send_row_indices, &
    1172        18234 :                                   col_indices=send_col_indices)
    1173        18234 :             nrow_block_dest = dest_block(1)
    1174        18234 :             ncol_block_dest = dest_block(2)
    1175       127638 :             ALLOCATE (send_count(0:recv_size - 1), send_disp(0:recv_size - 1), info%send_request(0:recv_size - 1))
    1176              : 
    1177              :             ! Determine the send counts, allocate the send buffers
    1178        18234 :             nrow_local_send = send_dist%nrow_locals(send_dist%context%mepos(1))
    1179        18234 :             ncol_local_send = send_dist%ncol_locals(send_dist%context%mepos(2))
    1180              : 
    1181              :             ! Initialise dest_p, dest_q arrays (sized nrow_local, ncol_local)
    1182              :             !   i.e. number of rows,cols in the sending distribution
    1183        91170 :             ALLOCATE (dest_p(nrow_local_send), dest_q(ncol_local_send))
    1184              : 
    1185       326120 :             DO i = 1, nrow_local_send
    1186              :                ! Use the send_dist%row_indices() here (we are looping over the local rows we will send)
    1187       326120 :                dest_p(i) = MOD(((send_row_indices(i) - 1)/nrow_block_dest), dest_num_pe(1))
    1188              :             END DO
    1189       389186 :             DO j = 1, ncol_local_send
    1190       389186 :                dest_q(j) = MOD(((send_col_indices(j) - 1)/ncol_block_dest), dest_num_pe(2))
    1191              :             END DO
    1192              :             ! dest_p/q now contain the process row/column ID that will receive data from that row/column
    1193              : 
    1194        36468 :             DO q = 0, dest_num_pe(2) - 1
    1195       389186 :                ncols = COUNT(dest_q == q)
    1196        72936 :                DO p = 0, dest_num_pe(1) - 1
    1197       652240 :                   nrows = COUNT(dest_p == p)
    1198        54702 :                   send_count(dest_blacs2mpi(p, q)) = nrows*ncols
    1199              :                END DO
    1200              :             END DO
    1201        18234 :             DEALLOCATE (dest_p, dest_q)
    1202              : 
    1203              :             ! Allocate the send buffer using send_count -- and calculate the offset into the buffer for each process
    1204        91170 :             ALLOCATE (info%send_buf(SUM(send_count(:))))
    1205        18234 :             send_disp(0) = 0
    1206        36468 :             DO k = 1, recv_size - 1
    1207        36468 :                send_disp(k) = send_disp(k - 1) + send_count(k - 1)
    1208              :             END DO
    1209              : 
    1210              :             ! Loop over the smat, pack the send buffers
    1211        18234 :             send_count(:) = 0
    1212       389186 :             DO j = 1, ncol_local_send
    1213              :                ! Use send_col_indices and row_indices here, as we are looking up the global row/column number of local rows.
    1214       370952 :                dest_q_j = MOD(((send_col_indices(j) - 1)/ncol_block_dest), dest_num_pe(2))
    1215     12219064 :                DO i = 1, nrow_local_send
    1216     11829878 :                   dest_p_i = MOD(((send_row_indices(i) - 1)/nrow_block_dest), dest_num_pe(1))
    1217     11829878 :                   mpi_rank = dest_blacs2mpi(dest_p_i, dest_q_j)
    1218     11829878 :                   send_count(mpi_rank) = send_count(mpi_rank) + 1
    1219     12200830 :                   info%send_buf(send_disp(mpi_rank) + send_count(mpi_rank)) = source%local_data(i, j)
    1220              :                END DO
    1221              :             END DO
    1222              : 
    1223              :             ! For each non-zero send_count, call mpi_isend
    1224        54702 :             DO k = 0, recv_size - 1
    1225        54702 :                IF (send_count(k) > 0) THEN
    1226              :                   CALL para_env%isend(info%send_buf(send_disp(k) + 1:send_disp(k) + send_count(k)), &
    1227        24452 :                                       dest2global(k), info%send_request(k))
    1228              :                ELSE
    1229        12016 :                   info%send_request(k) = mp_request_null
    1230              :                END IF
    1231              :             END DO
    1232        18234 :             DEALLOCATE (send_count, send_disp, dest2global)
    1233              :          END IF ! ASSOCIATED(source)
    1234        24452 :          DEALLOCATE (dest_blacs2mpi)
    1235              : 
    1236              :       END IF !IF (.NOT. cp2k_is_parallel)
    1237              : 
    1238        24452 :       CALL timestop(handle)
    1239              : 
    1240        97808 :    END SUBROUTINE cp_cfm_start_copy_general
    1241              : 
    1242              : ! **************************************************************************************************
    1243              : !> \brief Complete the copy operation: wait for comms, unpack, clean up MPI state.
    1244              : !> \param destination  output cfm matrix
    1245              : !> \param info         all of the data that will be needed to complete the copy operation
    1246              : !> \note a slightly modified version of the subroutine cp_fm_finish_copy_general() that uses
    1247              : !>       allocatable arrays instead of pointers wherever possible.
    1248              : ! **************************************************************************************************
    1249        24452 :    SUBROUTINE cp_cfm_finish_copy_general(destination, info)
    1250              :       TYPE(cp_cfm_type), INTENT(IN)                      :: destination
    1251              :       TYPE(copy_cfm_info_type), INTENT(inout)            :: info
    1252              : 
    1253              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_finish_copy_general'
    1254              : 
    1255              :       INTEGER                                            :: handle, i, j, k, mpi_rank, ni, nj, &
    1256              :                                                             src_q_j
    1257        24452 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: recv_count, src_p_i
    1258        24452 :       INTEGER, DIMENSION(:), POINTER                     :: recv_col_indices, recv_row_indices
    1259              : 
    1260        24452 :       CALL timeset(routineN, handle)
    1261              : 
    1262              :       IF (.NOT. cp2k_is_parallel) THEN
    1263              :          ! Now unpack the data from the 'send buffer'
    1264              :          k = 0
    1265              :          DO j = 1, SIZE(destination%local_data, 2)
    1266              :             DO i = 1, SIZE(destination%local_data, 1)
    1267              :                k = k + 1
    1268              :                destination%local_data(i, j) = info%send_buf(k)
    1269              :             END DO
    1270              :          END DO
    1271              :          DEALLOCATE (info%send_buf)
    1272              :       ELSE
    1273              :          ! Set up local variables ...
    1274        24452 :          recv_col_indices => info%recv_col_indices
    1275        24452 :          recv_row_indices => info%recv_row_indices
    1276              : 
    1277              :          ! ... use the local variables to do the work
    1278              :          ! DEST_2
    1279        24452 :          CALL mp_waitall(info%recv_request(:))
    1280              : 
    1281        24452 :          nj = info%nlocal_recv(2)
    1282        24452 :          ni = info%nlocal_recv(1)
    1283       122260 :          ALLOCATE (recv_count(0:info%send_size - 1), src_p_i(ni))
    1284              :          ! Loop over the rmat, filling it in with data from the recv buffers
    1285              :          ! (here the block sizes, num_pes refer to the distribution of the source matrix)
    1286        24452 :          recv_count(:) = 0
    1287       332338 :          DO i = 1, ni
    1288       332338 :             src_p_i(i) = MOD(((recv_row_indices(i) - 1)/info%nblock_src(1)), info%src_num_pe(1))
    1289              :          END DO
    1290              : 
    1291       459594 :          DO j = 1, nj
    1292       435142 :             src_q_j = MOD(((recv_col_indices(j) - 1)/info%nblock_src(2)), info%src_num_pe(2))
    1293     12289472 :             DO i = 1, ni
    1294     11829878 :                mpi_rank = info%src_blacs2mpi(src_p_i(i), src_q_j)
    1295     11829878 :                recv_count(mpi_rank) = recv_count(mpi_rank) + 1
    1296     12265020 :                destination%local_data(i, j) = info%recv_buf(info%recv_disp(mpi_rank) + recv_count(mpi_rank))
    1297              :             END DO
    1298              :          END DO
    1299              : 
    1300        24452 :          DEALLOCATE (recv_count, src_p_i)
    1301              :          ! Invalidate the stored state
    1302        24452 :          NULLIFY (info%recv_col_indices, info%recv_row_indices)
    1303        24452 :          DEALLOCATE (info%recv_disp, info%recv_request, info%recv_buf, info%src_blacs2mpi)
    1304              :       END IF
    1305              : 
    1306        24452 :       CALL timestop(handle)
    1307              : 
    1308        24452 :    END SUBROUTINE cp_cfm_finish_copy_general
    1309              : 
    1310              : ! **************************************************************************************************
    1311              : !> \brief Complete the copy operation: wait for comms clean up MPI state.
    1312              : !> \param info    all of the data that will be needed to complete the copy operation
    1313              : !> \note a slightly modified version of the subroutine cp_fm_cleanup_copy_general() that uses
    1314              : !>       allocatable arrays instead of pointers wherever possible.
    1315              : ! **************************************************************************************************
    1316        18234 :    SUBROUTINE cp_cfm_cleanup_copy_general(info)
    1317              :       TYPE(copy_cfm_info_type), INTENT(inout)            :: info
    1318              : 
    1319              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_cleanup_copy_general'
    1320              : 
    1321              :       INTEGER                                            :: handle
    1322              : 
    1323        18234 :       CALL timeset(routineN, handle)
    1324              : 
    1325              :       IF (cp2k_is_parallel) THEN
    1326              :          ! SRC_2
    1327              :          ! If this process is also in the destination decomposition, this deallocate
    1328              :          ! Was already done in cp_fm_finish_copy_general
    1329        18234 :          IF (ALLOCATED(info%src_blacs2mpi)) DEALLOCATE (info%src_blacs2mpi)
    1330        18234 :          CALL mp_waitall(info%send_request(:))
    1331        18234 :          DEALLOCATE (info%send_request, info%send_buf)
    1332              :       END IF
    1333              : 
    1334        18234 :       CALL timestop(handle)
    1335        18234 :    END SUBROUTINE cp_cfm_cleanup_copy_general
    1336            0 : END MODULE cp_cfm_types
        

Generated by: LCOV version 2.0-1