LCOV - code coverage report
Current view: top level - src/fm - cp_fm_types.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:71c3ab0) Lines: 87.8 % 800 702
Test Date: 2026-07-25 06:35:44 Functions: 80.4 % 46 37

            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 represent a full matrix distributed on many processors
      10              : !> \par History
      11              : !>      3) separated structure object, removed globenv, renamed to full matrix
      12              : !>         many changes (fawzi 08.2002)
      13              : !> \author Matthias Krack (22.05.2001)
      14              : ! **************************************************************************************************
      15              : MODULE cp_fm_types
      16              :    USE cp_blacs_env,                    ONLY: cp_blacs_env_type
      17              :    USE cp_blacs_types,                  ONLY: cp_blacs_type
      18              :    USE cp_fm_struct,                    ONLY: cp_fm_struct_create,&
      19              :                                               cp_fm_struct_equivalent,&
      20              :                                               cp_fm_struct_get,&
      21              :                                               cp_fm_struct_release,&
      22              :                                               cp_fm_struct_retain,&
      23              :                                               cp_fm_struct_type,&
      24              :                                               cp_fm_struct_write_info
      25              :    USE kinds,                           ONLY: dp
      26              :    USE message_passing,                 ONLY: cp2k_is_parallel,&
      27              :                                               mp_any_source,&
      28              :                                               mp_para_env_type,&
      29              :                                               mp_proc_null,&
      30              :                                               mp_request_null,&
      31              :                                               mp_request_type,&
      32              :                                               mp_waitall
      33              :    USE parallel_rng_types,              ONLY: UNIFORM,&
      34              :                                               rng_stream_type
      35              : #include "../base/base_uses.f90"
      36              : 
      37              :    IMPLICIT NONE
      38              : 
      39              :    PRIVATE
      40              : 
      41              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_fm_types'
      42              :    LOGICAL, PARAMETER          :: debug_this_module = .TRUE.
      43              :    INTEGER, PARAMETER :: src_tag = 3, dest_tag = 5, send_tag = 7, recv_tag = 11
      44              : 
      45              :    INTEGER, PRIVATE :: cp_fm_mm_type = 1
      46              : 
      47              :    PUBLIC :: cp_fm_type, &
      48              :              cp_fm_p_type, copy_info_type
      49              : 
      50              :    PUBLIC :: cp_fm_add_to_element, &
      51              :              cp_fm_create, &
      52              :              cp_fm_release, &
      53              :              cp_fm_get_info, &
      54              :              cp_fm_set_element, &
      55              :              cp_fm_get_element, &
      56              :              cp_fm_get_diag, & ! get diagonal
      57              :              cp_fm_set_all, & ! set all elements and diagonal
      58              :              cp_fm_set_all_submatrix, & ! set a submatrix to a given value
      59              :              cp_fm_set_submatrix, & ! set a submatrix to given values
      60              :              cp_fm_get_submatrix, & ! get a submatrix of given values
      61              :              cp_fm_init_random, &
      62              :              cp_fm_maxabsval, & ! find the maximum absolute value
      63              :              cp_fm_maxabsrownorm, & ! find the maximum of the sum of the abs of the elements of a row
      64              :              cp_fm_to_fm, & ! copy (parts of) a fm to a fm
      65              :              cp_fm_vectorsnorm, & ! compute the norm of the column-vectors
      66              :              cp_fm_vectorssum, & ! compute the sum of all elements of the column-vectors
      67              :              cp_fm_to_fm_submat, & ! copy (parts of) a fm to a fm
      68              :              cp_fm_to_fm_triangular, &
      69              :              cp_fm_copy_general, &
      70              :              cp_fm_start_copy_general, &
      71              :              cp_fm_finish_copy_general, &
      72              :              cp_fm_cleanup_copy_general, &
      73              :              cp_fm_write_unformatted, & ! writes a full matrix to an open unit
      74              :              cp_fm_write_formatted, & ! writes a full matrix to an open unit
      75              :              cp_fm_read_unformatted, & ! reads a full matrix from an open unit
      76              :              cp_fm_setup, & ! allows to set flags for fms
      77              :              cp_fm_get_mm_type, &
      78              :              cp_fm_write_info, &
      79              :              cp_fm_to_fm_submat_general ! copy matrix across different contexts
      80              : 
      81              :    PUBLIC :: cp_fm_pilaenv
      82              : 
      83              :    INTERFACE cp_fm_to_fm
      84              :       MODULE PROCEDURE cp_fm_to_fm_matrix, & ! a full matrix
      85              :          cp_fm_to_fm_columns ! just a number of columns
      86              :    END INTERFACE
      87              : 
      88              :    INTERFACE cp_fm_release
      89              :       MODULE PROCEDURE cp_fm_release_aa0, &
      90              :          cp_fm_release_aa1, &
      91              :          cp_fm_release_aa2, &
      92              :          cp_fm_release_aa3, &
      93              :          cp_fm_release_ap1, &
      94              :          cp_fm_release_ap2, &
      95              :          cp_fm_release_pa1, &
      96              :          cp_fm_release_pa2, &
      97              :          cp_fm_release_pa3, &
      98              :          cp_fm_release_pp1, &
      99              :          cp_fm_release_pp2
     100              :    END INTERFACE
     101              : 
     102              : ! **************************************************************************************************
     103              : !> \brief represent a full matrix
     104              : !> \param name the name of the matrix, used for printing
     105              : !> \param matrix_struct structure of this matrix
     106              : !> \param local_data array with the data of the matrix (its contents
     107              : !>        depend on the matrix type used: in parallel runs it will be
     108              : !>        in scalapack format, in sequential, it will simply contain
     109              : !>        the matrix)
     110              : !> \par History
     111              : !>      08.2002 created [fawzi]
     112              : !> \author fawzi
     113              : ! **************************************************************************************************
     114              :    TYPE cp_fm_type
     115              : !    PRIVATE
     116              :       CHARACTER(LEN=60) :: name = ""
     117              :       TYPE(cp_fm_struct_type), POINTER :: matrix_struct => NULL()
     118              :       REAL(KIND=dp), DIMENSION(:, :), POINTER, CONTIGUOUS :: local_data => NULL()
     119              :    END TYPE cp_fm_type
     120              : 
     121              : ! **************************************************************************************************
     122              : !> \brief just to build arrays of pointers to matrices
     123              : !> \param matrix the pointer to the matrix
     124              : !> \par History
     125              : !>      08.2002 created [fawzi]
     126              : !> \author fawzi
     127              : ! **************************************************************************************************
     128              :    TYPE cp_fm_p_type
     129              :       TYPE(cp_fm_type), POINTER :: matrix => NULL()
     130              :    END TYPE cp_fm_p_type
     131              : 
     132              : ! **************************************************************************************************
     133              : !> \brief Stores the state of a copy between cp_fm_start_copy_general
     134              : !>        and cp_fm_finish_copy_general
     135              : !> \par History
     136              : !>      Jan 2017  [Mark T]
     137              : ! **************************************************************************************************
     138              :    TYPE copy_info_type
     139              :       INTEGER :: send_size = -1
     140              :       INTEGER, DIMENSION(2) :: nlocal_recv = -1, nblock_src = -1, src_num_pe = -1 ! 1->row  2->col
     141              :       TYPE(mp_request_type), DIMENSION(:), ALLOCATABLE :: send_request, recv_request
     142              :       INTEGER, DIMENSION(:), ALLOCATABLE   :: recv_disp
     143              :       INTEGER, DIMENSION(:), POINTER       :: recv_col_indices => NULL(), recv_row_indices => NULL()
     144              :       INTEGER, DIMENSION(:, :), ALLOCATABLE :: src_blacs2mpi
     145              :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: recv_buf, send_buf
     146              :    END TYPE copy_info_type
     147              : 
     148              : CONTAINS
     149              : 
     150              : ! **************************************************************************************************
     151              : !> \brief creates a new full matrix with the given structure
     152              : !> \param matrix the matrix to be created
     153              : !> \param matrix_struct the structure of matrix
     154              : !> \param name ...
     155              : !> \param nrow ...
     156              : !> \param ncol ...
     157              : !> \param set_zero ...
     158              : !> \par History
     159              : !>      08.2002 created [fawzi]
     160              : !> \author Fawzi Mohamed
     161              : !> \note
     162              : !>      preferred allocation routine
     163              : ! **************************************************************************************************
     164      1867020 :    SUBROUTINE cp_fm_create(matrix, matrix_struct, name, nrow, ncol, set_zero)
     165              :       TYPE(cp_fm_type), INTENT(OUT)                      :: matrix
     166              :       TYPE(cp_fm_struct_type), INTENT(IN), TARGET        :: matrix_struct
     167              :       CHARACTER(LEN=*), INTENT(in), OPTIONAL             :: name
     168              :       INTEGER, INTENT(IN), OPTIONAL                      :: nrow, ncol
     169              :       LOGICAL, INTENT(in), OPTIONAL                      :: set_zero
     170              : 
     171              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'cp_fm_create'
     172              : 
     173              :       INTEGER                                            :: handle, ncol_global, ncol_local, &
     174              :                                                             nrow_global, nrow_local
     175              :       TYPE(cp_blacs_env_type), POINTER                   :: context
     176              :       TYPE(cp_fm_struct_type), POINTER                   :: fm_struct
     177              : 
     178      1867020 :       CALL timeset(routineN, handle)
     179              : 
     180      1867020 :       IF (PRESENT(nrow) .OR. PRESENT(ncol)) THEN
     181         1512 :          CALL cp_fm_struct_get(matrix_struct, nrow_global=nrow_global, ncol_global=ncol_global)
     182         1512 :          IF (PRESENT(nrow)) nrow_global = nrow
     183         1512 :          IF (PRESENT(ncol)) ncol_global = ncol
     184              :          CALL cp_fm_struct_create(fm_struct, template_fmstruct=matrix_struct, &
     185         1512 :                                   nrow_global=nrow_global, ncol_global=ncol_global)
     186              : 
     187         1512 :          context => fm_struct%context
     188         1512 :          matrix%matrix_struct => fm_struct
     189         1512 :          CALL cp_fm_struct_retain(matrix%matrix_struct)
     190              : 
     191         1512 :          nrow_local = fm_struct%local_leading_dimension
     192         1512 :          ncol_local = MAX(1, fm_struct%ncol_locals(context%mepos(2)))
     193              : 
     194         1512 :          CALL cp_fm_struct_release(fm_struct)
     195              :       ELSE
     196              : 
     197      1865508 :          context => matrix_struct%context
     198      1865508 :          matrix%matrix_struct => matrix_struct
     199      1865508 :          CALL cp_fm_struct_retain(matrix%matrix_struct)
     200              : 
     201              :          ! OK, we allocate here at least a 1 x 1 matrix
     202              :          ! this must (and is) compatible with the descinit call
     203              :          ! in cp_fm_struct
     204      1865508 :          nrow_local = matrix_struct%local_leading_dimension
     205      1865508 :          ncol_local = MAX(1, matrix_struct%ncol_locals(context%mepos(2)))
     206              :       END IF
     207              : 
     208      1867020 :       NULLIFY (matrix%local_data)
     209              : 
     210      7468080 :       ALLOCATE (matrix%local_data(nrow_local, ncol_local))
     211              : 
     212      1867020 :       IF (PRESENT(set_zero)) THEN
     213       229691 :          IF (set_zero) THEN
     214     69097314 :             matrix%local_data(1:nrow_local, 1:ncol_local) = 0.0_dp
     215              :          END IF
     216              :       END IF
     217              : 
     218      1867020 :       IF (PRESENT(name)) THEN
     219       862072 :          matrix%name = name
     220              :       ELSE
     221      1004948 :          matrix%name = 'full matrix'
     222              :       END IF
     223              : 
     224      1867020 :       CALL timestop(handle)
     225              : 
     226      1867020 :    END SUBROUTINE cp_fm_create
     227              : 
     228              : ! **************************************************************************************************
     229              : !> \brief releases a full matrix
     230              : !> \param matrix the matrix to release
     231              : !> \par History
     232              : !>      08.2002 created [fawzi]
     233              : !> \author Fawzi Mohamed
     234              : ! **************************************************************************************************
     235      1887321 :    SUBROUTINE cp_fm_release_aa0(matrix)
     236              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: matrix
     237              : 
     238      1887321 :       IF (ASSOCIATED(matrix%local_data)) THEN
     239      1866296 :          DEALLOCATE (matrix%local_data)
     240              :          NULLIFY (matrix%local_data)
     241              :       END IF
     242      1887321 :       matrix%name = ""
     243      1887321 :       CALL cp_fm_struct_release(matrix%matrix_struct)
     244              : 
     245      1887321 :    END SUBROUTINE cp_fm_release_aa0
     246              : 
     247              : ! **************************************************************************************************
     248              : !> \brief ...
     249              : !> \param matrices ...
     250              : ! **************************************************************************************************
     251        81706 :    SUBROUTINE cp_fm_release_aa1(matrices)
     252              :       TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:)        :: matrices
     253              : 
     254              :       INTEGER                                            :: i
     255              : 
     256        81706 :       IF (ALLOCATED(matrices)) THEN
     257       217601 :          DO i = 1, SIZE(matrices)
     258       217601 :             CALL cp_fm_release(matrices(i))
     259              :          END DO
     260        80260 :          DEALLOCATE (matrices)
     261              :       END IF
     262        81706 :    END SUBROUTINE cp_fm_release_aa1
     263              : 
     264              : ! **************************************************************************************************
     265              : !> \brief ...
     266              : !> \param matrices ...
     267              : ! **************************************************************************************************
     268         9142 :    SUBROUTINE cp_fm_release_aa2(matrices)
     269              :       TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :)     :: matrices
     270              : 
     271              :       INTEGER                                            :: i, j
     272              : 
     273         9142 :       IF (ALLOCATED(matrices)) THEN
     274        18012 :          DO i = 1, SIZE(matrices, 1)
     275        49968 :             DO j = 1, SIZE(matrices, 2)
     276        43820 :                CALL cp_fm_release(matrices(i, j))
     277              :             END DO
     278              :          END DO
     279         6148 :          DEALLOCATE (matrices)
     280              :       END IF
     281         9142 :    END SUBROUTINE cp_fm_release_aa2
     282              : 
     283              : ! **************************************************************************************************
     284              : !> \brief ...
     285              : !> \param matrices ...
     286              : ! **************************************************************************************************
     287           50 :    SUBROUTINE cp_fm_release_aa3(matrices)
     288              :       TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :, :)  :: matrices
     289              : 
     290              :       INTEGER                                            :: i, j, k
     291              : 
     292           50 :       IF (ALLOCATED(matrices)) THEN
     293          572 :          DO i = 1, SIZE(matrices, 1)
     294         1644 :             DO j = 1, SIZE(matrices, 2)
     295         2786 :                DO k = 1, SIZE(matrices, 3)
     296         2264 :                   CALL cp_fm_release(matrices(i, j, k))
     297              :                END DO
     298              :             END DO
     299              :          END DO
     300           50 :          DEALLOCATE (matrices)
     301              :       END IF
     302           50 :    END SUBROUTINE cp_fm_release_aa3
     303              : 
     304              : ! **************************************************************************************************
     305              : !> \brief ...
     306              : !> \param matrices ...
     307              : ! **************************************************************************************************
     308       191898 :    SUBROUTINE cp_fm_release_pa1(matrices)
     309              :       TYPE(cp_fm_type), DIMENSION(:), POINTER            :: matrices
     310              : 
     311              :       INTEGER                                            :: i
     312              : 
     313       191898 :       IF (ASSOCIATED(matrices)) THEN
     314       148737 :          DO i = 1, SIZE(matrices)
     315       148737 :             CALL cp_fm_release(matrices(i))
     316              :          END DO
     317        61518 :          DEALLOCATE (matrices)
     318              :          NULLIFY (matrices)
     319              :       END IF
     320       191898 :    END SUBROUTINE cp_fm_release_pa1
     321              : 
     322              : ! **************************************************************************************************
     323              : !> \brief ...
     324              : !> \param matrices ...
     325              : ! **************************************************************************************************
     326        72662 :    SUBROUTINE cp_fm_release_pa2(matrices)
     327              :       TYPE(cp_fm_type), DIMENSION(:, :), POINTER         :: matrices
     328              : 
     329              :       INTEGER                                            :: i, j
     330              : 
     331        72662 :       IF (ASSOCIATED(matrices)) THEN
     332        82526 :          DO i = 1, SIZE(matrices, 1)
     333       153494 :             DO j = 1, SIZE(matrices, 2)
     334       130402 :                CALL cp_fm_release(matrices(i, j))
     335              :             END DO
     336              :          END DO
     337        23092 :          DEALLOCATE (matrices)
     338              :          NULLIFY (matrices)
     339              :       END IF
     340        72662 :    END SUBROUTINE cp_fm_release_pa2
     341              : 
     342              : ! **************************************************************************************************
     343              : !> \brief ...
     344              : !> \param matrices ...
     345              : ! **************************************************************************************************
     346            0 :    SUBROUTINE cp_fm_release_pa3(matrices)
     347              :       TYPE(cp_fm_type), DIMENSION(:, :, :), POINTER      :: matrices
     348              : 
     349              :       INTEGER                                            :: i, j, k
     350              : 
     351            0 :       IF (ASSOCIATED(matrices)) THEN
     352            0 :          DO i = 1, SIZE(matrices, 1)
     353            0 :             DO j = 1, SIZE(matrices, 2)
     354            0 :                DO k = 1, SIZE(matrices, 3)
     355            0 :                   CALL cp_fm_release(matrices(i, j, k))
     356              :                END DO
     357              :             END DO
     358              :          END DO
     359            0 :          DEALLOCATE (matrices)
     360              :          NULLIFY (matrices)
     361              :       END IF
     362            0 :    END SUBROUTINE cp_fm_release_pa3
     363              : 
     364              : ! **************************************************************************************************
     365              : !> \brief ...
     366              : !> \param matrices ...
     367              : ! **************************************************************************************************
     368            0 :    SUBROUTINE cp_fm_release_ap1(matrices)
     369              :       TYPE(cp_fm_p_type), ALLOCATABLE, DIMENSION(:)      :: matrices
     370              : 
     371              :       INTEGER                                            :: i
     372              : 
     373            0 :       IF (ALLOCATED(matrices)) THEN
     374            0 :          DO i = 1, SIZE(matrices)
     375            0 :             CALL cp_fm_release(matrices(i)%matrix)
     376            0 :             DEALLOCATE (matrices(i)%matrix)
     377              :          END DO
     378            0 :          DEALLOCATE (matrices)
     379              :       END IF
     380            0 :    END SUBROUTINE cp_fm_release_ap1
     381              : 
     382              : ! **************************************************************************************************
     383              : !> \brief ...
     384              : !> \param matrices ...
     385              : ! **************************************************************************************************
     386            0 :    SUBROUTINE cp_fm_release_ap2(matrices)
     387              :       TYPE(cp_fm_p_type), ALLOCATABLE, DIMENSION(:, :)   :: matrices
     388              : 
     389              :       INTEGER                                            :: i, j
     390              : 
     391            0 :       IF (ALLOCATED(matrices)) THEN
     392            0 :          DO i = 1, SIZE(matrices, 1)
     393            0 :             DO j = 1, SIZE(matrices, 2)
     394            0 :                CALL cp_fm_release(matrices(i, j)%matrix)
     395            0 :                DEALLOCATE (matrices(i, j)%matrix)
     396              :             END DO
     397              :          END DO
     398            0 :          DEALLOCATE (matrices)
     399              :       END IF
     400            0 :    END SUBROUTINE cp_fm_release_ap2
     401              : 
     402              : ! **************************************************************************************************
     403              : !> \brief ...
     404              : !> \param matrices ...
     405              : ! **************************************************************************************************
     406            0 :    SUBROUTINE cp_fm_release_pp1(matrices)
     407              :       TYPE(cp_fm_p_type), DIMENSION(:), POINTER          :: matrices
     408              : 
     409              :       INTEGER                                            :: i
     410              : 
     411            0 :       IF (ASSOCIATED(matrices)) THEN
     412            0 :          DO i = 1, SIZE(matrices)
     413            0 :             CALL cp_fm_release(matrices(i)%matrix)
     414            0 :             DEALLOCATE (matrices(i)%matrix)
     415              :          END DO
     416            0 :          DEALLOCATE (matrices)
     417              :          NULLIFY (matrices)
     418              :       END IF
     419            0 :    END SUBROUTINE cp_fm_release_pp1
     420              : 
     421              : ! **************************************************************************************************
     422              : !> \brief ...
     423              : !> \param matrices ...
     424              : ! **************************************************************************************************
     425            0 :    SUBROUTINE cp_fm_release_pp2(matrices)
     426              :       TYPE(cp_fm_p_type), DIMENSION(:, :), POINTER       :: matrices
     427              : 
     428              :       INTEGER                                            :: i, j
     429              : 
     430            0 :       IF (ASSOCIATED(matrices)) THEN
     431            0 :          DO i = 1, SIZE(matrices, 1)
     432            0 :             DO j = 1, SIZE(matrices, 2)
     433            0 :                CALL cp_fm_release(matrices(i, j)%matrix)
     434            0 :                DEALLOCATE (matrices(i, j)%matrix)
     435              :             END DO
     436              :          END DO
     437            0 :          DEALLOCATE (matrices)
     438              :          NULLIFY (matrices)
     439              :       END IF
     440            0 :    END SUBROUTINE cp_fm_release_pp2
     441              : 
     442              : ! **************************************************************************************************
     443              : !> \brief fills a matrix with random numbers
     444              : !> \param matrix : to be initialized
     445              : !> \param ncol : numbers of cols to fill
     446              : !> \param start_col : starting at coll number
     447              : !> \author Joost VandeVondele
     448              : !> \note
     449              : !>      the value of a_ij is independent of the number of cpus
     450              : ! **************************************************************************************************
     451         2926 :    SUBROUTINE cp_fm_init_random(matrix, ncol, start_col)
     452              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix
     453              :       INTEGER, INTENT(IN), OPTIONAL                      :: ncol, start_col
     454              : 
     455              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'cp_fm_init_random'
     456              : 
     457              :       INTEGER :: handle, icol_global, icol_local, irow_local, my_ncol, my_start_col, ncol_global, &
     458              :          ncol_local, nrow_global, nrow_local
     459         5852 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
     460              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: buff
     461              :       REAL(KIND=dp), CONTIGUOUS, DIMENSION(:, :), &
     462         2926 :          POINTER                                         :: local_data
     463              :       REAL(KIND=dp), DIMENSION(3, 2), SAVE :: &
     464              :          seed = RESHAPE([1.0_dp, 2.0_dp, 3.0_dp, 4.0_dp, 5.0_dp, 6.0_dp], [3, 2])
     465              :       TYPE(rng_stream_type)                              :: rng
     466              : 
     467         2926 :       CALL timeset(routineN, handle)
     468              : 
     469              :       ! guarantee same seed over all tasks
     470         2926 :       CALL matrix%matrix_struct%para_env%bcast(seed, 0)
     471              : 
     472              :       rng = rng_stream_type("cp_fm_init_random_stream", distribution_type=UNIFORM, &
     473         2926 :                             extended_precision=.TRUE., seed=seed)
     474              : 
     475              :       CALL cp_fm_get_info(matrix, nrow_global=nrow_global, ncol_global=ncol_global, &
     476              :                           nrow_local=nrow_local, ncol_local=ncol_local, &
     477              :                           local_data=local_data, &
     478         2926 :                           row_indices=row_indices, col_indices=col_indices)
     479              : 
     480         2926 :       my_start_col = 1
     481         2926 :       IF (PRESENT(start_col)) my_start_col = start_col
     482         2926 :       my_ncol = matrix%matrix_struct%ncol_global
     483         2926 :       IF (PRESENT(ncol)) my_ncol = ncol
     484              : 
     485         2926 :       IF (ncol_global < (my_start_col + my_ncol - 1)) THEN
     486            0 :          CPABORT("ncol_global>=(my_start_col+my_ncol-1)")
     487              :       END IF
     488              : 
     489         8778 :       ALLOCATE (buff(nrow_global))
     490              : 
     491              :       ! each global row has its own substream, in order to reach the stream for the local col,
     492              :       ! we just reset to the next substream
     493              :       ! following this, we fill the full buff with random numbers, and pick those we need
     494         2926 :       icol_global = 0
     495        22879 :       DO icol_local = 1, ncol_local
     496        19953 :          CPASSERT(col_indices(icol_local) > icol_global)
     497              :          DO
     498        19953 :             CALL rng%reset_to_next_substream()
     499        19953 :             icol_global = icol_global + 1
     500        19953 :             IF (icol_global == col_indices(icol_local)) EXIT
     501              :          END DO
     502        19953 :          CALL rng%fill(buff)
     503       722599 :          DO irow_local = 1, nrow_local
     504       719673 :             local_data(irow_local, icol_local) = buff(row_indices(irow_local))
     505              :          END DO
     506              :       END DO
     507              : 
     508         2926 :       DEALLOCATE (buff)
     509              : 
     510              :       ! store seed before deletion (unclear if this is the proper seed)
     511              : 
     512              :       ! Note that, the initial state (rng%ig) instead of the current state (rng%cg) is stored in the
     513              :       ! seed variable. As a consequence, each invocation of cp_fm_init_random uses exactly the same
     514              :       ! stream of random numbers. While this seems odd and contrary to the original design,
     515              :       ! it was probably introduced to improve reproducibility.
     516              :       ! See also https://github.com/cp2k/cp2k/pull/506
     517         2926 :       CALL rng%get(ig=seed)
     518              : 
     519         2926 :       CALL timestop(handle)
     520              : 
     521        81928 :    END SUBROUTINE cp_fm_init_random
     522              : 
     523              : ! **************************************************************************************************
     524              : !> \brief set all elements of a matrix to the same value,
     525              : !>      and optionally the diagonal to a different one
     526              : !> \param matrix input matrix
     527              : !> \param alpha scalar used to set all elements of the matrix
     528              : !> \param beta scalar used to set diagonal of the matrix
     529              : !> \note
     530              : !>      can be used to zero a matrix
     531              : !>      can be used to create a unit matrix (I-matrix) alpha=0.0_dp beta=1.0_dp
     532              : ! **************************************************************************************************
     533       414079 :    SUBROUTINE cp_fm_set_all(matrix, alpha, beta)
     534              : 
     535              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix
     536              :       REAL(KIND=dp), INTENT(IN)                          :: alpha
     537              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: beta
     538              : 
     539              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'cp_fm_set_all'
     540              : 
     541              :       INTEGER                                            :: handle, i, n
     542              : 
     543       414079 :       CALL timeset(routineN, handle)
     544              : 
     545    231898538 :       matrix%local_data(:, :) = alpha
     546              : 
     547       414079 :       IF (PRESENT(beta)) THEN
     548        66720 :          n = MIN(matrix%matrix_struct%nrow_global, matrix%matrix_struct%ncol_global)
     549       515216 :          DO i = 1, n
     550       515216 :             CALL cp_fm_set_element(matrix, i, i, beta)
     551              :          END DO
     552              :       END IF
     553              : 
     554       414079 :       CALL timestop(handle)
     555              : 
     556       414079 :    END SUBROUTINE cp_fm_set_all
     557              : 
     558              : ! **************************************************************************************************
     559              : !> \brief returns the diagonal elements of a fm
     560              : !> \param matrix ...
     561              : !> \param diag ...
     562              : ! **************************************************************************************************
     563        16116 :    SUBROUTINE cp_fm_get_diag(matrix, diag)
     564              : 
     565              :       ! arguments
     566              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix
     567              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: diag
     568              : 
     569              :       ! locals
     570              :       INTEGER :: i, nrow_global
     571              : 
     572              : #if defined(__parallel)
     573              :       INTEGER, DIMENSION(9) :: desca
     574              :       TYPE(cp_blacs_env_type), POINTER :: context
     575              :       INTEGER :: icol_local, ipcol, iprow, irow_local, mypcol, myprow, npcol, &
     576              :                  nprow
     577        16116 :       REAL(KIND=dp), DIMENSION(:, :), POINTER :: a
     578              : #endif
     579              : 
     580        16116 :       CALL cp_fm_get_info(matrix, nrow_global=nrow_global)
     581              : 
     582              : #if defined(__parallel)
     583       182016 :       diag = 0.0_dp
     584        16116 :       context => matrix%matrix_struct%context
     585        16116 :       myprow = context%mepos(1)
     586        16116 :       mypcol = context%mepos(2)
     587        16116 :       nprow = context%num_pe(1)
     588        16116 :       npcol = context%num_pe(2)
     589              : 
     590        16116 :       a => matrix%local_data
     591       161160 :       desca(:) = matrix%matrix_struct%descriptor(:)
     592              : 
     593       182016 :       DO i = 1, nrow_global
     594              :          CALL infog2l(i, i, desca, nprow, npcol, myprow, mypcol, &
     595       165900 :                       irow_local, icol_local, iprow, ipcol)
     596       182016 :          IF ((iprow == myprow) .AND. (ipcol == mypcol)) THEN
     597        83034 :             diag(i) = a(irow_local, icol_local)
     598              :          END IF
     599              :       END DO
     600              : #else
     601              :       DO i = 1, nrow_global
     602              :          diag(i) = matrix%local_data(i, i)
     603              :       END DO
     604              : #endif
     605       347916 :       CALL matrix%matrix_struct%para_env%sum(diag)
     606              : 
     607        16116 :    END SUBROUTINE cp_fm_get_diag
     608              : 
     609              : ! **************************************************************************************************
     610              : !> \brief returns an element of a fm
     611              : !>      this value is valid on every cpu
     612              : !>      using this call is expensive
     613              : !> \param matrix the matrix to read
     614              : !> \param irow_global the row
     615              : !> \param icol_global the col
     616              : !> \param alpha the value of matrix(irow_global, icol_global)
     617              : !> \param local true if the element is on this cpu, false otherwise
     618              : !> \note
     619              : !>      - modified semantics. now this function always returns the value
     620              : !>        previously the value was zero on cpus that didn't own the relevant
     621              : !>        part of the matrix (Joost VandeVondele, May 2003)
     622              : !>      - usage of the function should be avoided, as it is likely to rather slow
     623              : !>        using row_indices/col_indices/local_data + some smart scheme normally
     624              : !>        yields a real parallel code
     625              : ! **************************************************************************************************
     626      1193594 :    SUBROUTINE cp_fm_get_element(matrix, irow_global, icol_global, alpha, local)
     627              : 
     628              :       ! arguments
     629              :       TYPE(cp_fm_type), INTENT(IN)          :: matrix
     630              :       REAL(KIND=dp), INTENT(OUT)                     :: alpha
     631              :       INTEGER, INTENT(IN)                       :: icol_global, &
     632              :                                                    irow_global
     633              :       LOGICAL, INTENT(OUT), OPTIONAL            :: local
     634              : 
     635              :       ! locals
     636              : #if defined(__parallel)
     637              :       INTEGER, DIMENSION(9) :: desca
     638              :       TYPE(cp_blacs_env_type), POINTER :: context
     639              :       INTEGER :: icol_local, ipcol, iprow, irow_local, mypcol, myprow, npcol, &
     640              :                  nprow
     641      1193594 :       REAL(KIND=dp), DIMENSION(:, :), POINTER :: a
     642              : #endif
     643              : 
     644              : #if defined(__parallel)
     645      1193594 :       context => matrix%matrix_struct%context
     646      1193594 :       myprow = context%mepos(1)
     647      1193594 :       mypcol = context%mepos(2)
     648      1193594 :       nprow = context%num_pe(1)
     649      1193594 :       npcol = context%num_pe(2)
     650              : 
     651      1193594 :       a => matrix%local_data
     652     11935940 :       desca(:) = matrix%matrix_struct%descriptor(:)
     653              : 
     654              :       CALL infog2l(irow_global, icol_global, desca, nprow, npcol, myprow, mypcol, &
     655      1193594 :                    irow_local, icol_local, iprow, ipcol)
     656              : 
     657      1193594 :       IF ((iprow == myprow) .AND. (ipcol == mypcol)) THEN
     658       596847 :          alpha = a(irow_local, icol_local)
     659       596847 :          CALL context%dgebs2d('All', ' ', 1, 1, alpha, 1)
     660       596847 :          IF (PRESENT(local)) local = .TRUE.
     661              :       ELSE
     662       596747 :          CALL context%dgebr2d('All', ' ', 1, 1, alpha, 1, iprow, ipcol)
     663       596747 :          IF (PRESENT(local)) local = .FALSE.
     664              :       END IF
     665              : 
     666              : #else
     667              :       IF (PRESENT(local)) local = .TRUE.
     668              :       alpha = matrix%local_data(irow_global, icol_global)
     669              : #endif
     670              : 
     671      1193594 :    END SUBROUTINE cp_fm_get_element
     672              : 
     673              : ! **************************************************************************************************
     674              : !> \brief sets an element of a matrix
     675              : !> \param matrix ...
     676              : !> \param irow_global ...
     677              : !> \param icol_global ...
     678              : !> \param alpha ...
     679              : !> \note
     680              : !>      we expect all cpus to have the same arguments in the call to this function
     681              : !>      (otherwise one should use local_data tricks)
     682              : ! **************************************************************************************************
     683       707330 :    SUBROUTINE cp_fm_set_element(matrix, irow_global, icol_global, alpha)
     684              :       TYPE(cp_fm_type), INTENT(IN)          :: matrix
     685              :       INTEGER, INTENT(IN)                      :: irow_global, icol_global
     686              :       REAL(KIND=dp), INTENT(IN)                :: alpha
     687              : 
     688              :       INTEGER                                  :: mypcol, myprow, npcol, nprow
     689              :       TYPE(cp_blacs_env_type), POINTER         :: context
     690              : #if defined(__parallel)
     691              :       INTEGER                                  :: icol_local, ipcol, iprow, &
     692              :                                                   irow_local
     693              :       INTEGER, DIMENSION(9)                    :: desca
     694       707330 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
     695              : #endif
     696              : 
     697       707330 :       context => matrix%matrix_struct%context
     698       707330 :       myprow = context%mepos(1)
     699       707330 :       mypcol = context%mepos(2)
     700       707330 :       nprow = context%num_pe(1)
     701       707330 :       npcol = context%num_pe(2)
     702              : 
     703              : #if defined(__parallel)
     704              : 
     705       707330 :       a => matrix%local_data
     706              : 
     707      7073300 :       desca(:) = matrix%matrix_struct%descriptor(:)
     708              : 
     709              :       CALL infog2l(irow_global, icol_global, desca, nprow, npcol, myprow, mypcol, &
     710       707330 :                    irow_local, icol_local, iprow, ipcol)
     711              : 
     712       707330 :       IF ((iprow == myprow) .AND. (ipcol == mypcol)) THEN
     713       355570 :          a(irow_local, icol_local) = alpha
     714              :       END IF
     715              : 
     716              : #else
     717              : 
     718              :       matrix%local_data(irow_global, icol_global) = alpha
     719              : 
     720              : #endif
     721       707330 :    END SUBROUTINE cp_fm_set_element
     722              : 
     723              : ! **************************************************************************************************
     724              : !> \brief sets a submatrix of a full matrix
     725              : !>       fm(start_row:start_row+n_rows,start_col:start_col+n_cols)
     726              : !>       = alpha*op(new_values)(1:n_rows,1:n_cols)+ beta
     727              : !>       * fm(start_row:start_row+n_rows,start_col:start_col+n_cols)
     728              : !> \param fm the full to change
     729              : !> \param new_values a replicated full matrix with the new values
     730              : !> \param start_row the starting row of b_matrix (defaults to 1)
     731              : !> \param start_col the starting col of b_matrix (defaults to 1)
     732              : !> \param n_rows the number of row to change in b (defaults to
     733              : !>        size(op(new_values),1))
     734              : !> \param n_cols the number of columns to change in b (defaults to
     735              : !>        size(op(new_values),2))
     736              : !> \param alpha rescaling factor for the new values (defaults to 1.0)
     737              : !> \param beta rescaling factor for the old values (defaults to 0.0)
     738              : !> \param transpose if new_values should be transposed: if true
     739              : !>        op(new_values)=new_values^T, else op(new_values)=new_values
     740              : !>        (defaults to false)
     741              : !> \par History
     742              : !>      07.2002 created borrowing from Joost's blacs_replicated_copy [fawzi]
     743              : !> \author Fawzi Mohamed
     744              : !> \note
     745              : !>      optimized for full column updates and alpha=1.0, beta=0.0
     746              : !>      the new_values need to be valid on all cpus
     747              : ! **************************************************************************************************
     748        70643 :    SUBROUTINE cp_fm_set_submatrix(fm, new_values, start_row, &
     749              :                                   start_col, n_rows, n_cols, alpha, beta, transpose)
     750              :       TYPE(cp_fm_type), INTENT(IN)                       :: fm
     751              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(in)         :: new_values
     752              :       INTEGER, INTENT(in), OPTIONAL                      :: start_row, start_col, n_rows, n_cols
     753              :       REAL(KIND=dp), INTENT(in), OPTIONAL                :: alpha, beta
     754              :       LOGICAL, INTENT(in), OPTIONAL                      :: transpose
     755              : 
     756              :       INTEGER                                            :: i, i0, j, j0, ncol, ncol_block, &
     757              :                                                             ncol_global, ncol_local, nrow, &
     758              :                                                             nrow_block, nrow_global, nrow_local, &
     759              :                                                             this_col, this_row
     760        70643 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
     761              :       LOGICAL                                            :: tr_a
     762              :       REAL(KIND=dp)                                      :: al, be
     763        70643 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: full_block
     764              : 
     765        70643 :       al = 1.0_dp; be = 0.0_dp; i0 = 1; j0 = 1; tr_a = .FALSE.
     766              : 
     767        19356 :       IF (PRESENT(alpha)) al = alpha
     768        70643 :       IF (PRESENT(beta)) be = beta
     769        70643 :       IF (PRESENT(start_row)) i0 = start_row
     770        70643 :       IF (PRESENT(start_col)) j0 = start_col
     771        70643 :       IF (PRESENT(transpose)) tr_a = transpose
     772        16153 :       IF (tr_a) THEN
     773        16061 :          nrow = SIZE(new_values, 2)
     774        16061 :          ncol = SIZE(new_values, 1)
     775              :       ELSE
     776        54582 :          nrow = SIZE(new_values, 1)
     777        54582 :          ncol = SIZE(new_values, 2)
     778              :       END IF
     779        70643 :       IF (PRESENT(n_rows)) nrow = n_rows
     780        70643 :       IF (PRESENT(n_cols)) ncol = n_cols
     781              : 
     782        70643 :       full_block => fm%local_data
     783              : 
     784              :       CALL cp_fm_get_info(matrix=fm, &
     785              :                           nrow_global=nrow_global, ncol_global=ncol_global, &
     786              :                           nrow_block=nrow_block, ncol_block=ncol_block, &
     787              :                           nrow_local=nrow_local, ncol_local=ncol_local, &
     788        70643 :                           row_indices=row_indices, col_indices=col_indices)
     789              : 
     790        70643 :       IF (al == 1.0 .AND. be == 0.0) THEN
     791      1290994 :          DO j = 1, ncol_local
     792      1229937 :             this_col = col_indices(j) - j0 + 1
     793      1290994 :             IF (this_col >= 1 .AND. this_col <= ncol) THEN
     794       296459 :                IF (tr_a) THEN
     795         6475 :                   IF (i0 == 1 .AND. nrow_global == nrow) THEN
     796       158370 :                      DO i = 1, nrow_local
     797       158370 :                         full_block(i, j) = new_values(this_col, row_indices(i))
     798              :                      END DO
     799              :                   ELSE
     800          594 :                      DO i = 1, nrow_local
     801          510 :                         this_row = row_indices(i) - i0 + 1
     802          594 :                         IF (this_row >= 1 .AND. this_row <= nrow) THEN
     803          255 :                            full_block(i, j) = new_values(this_col, this_row)
     804              :                         END IF
     805              :                      END DO
     806              :                   END IF
     807              :                ELSE
     808       289984 :                   IF (i0 == 1 .AND. nrow_global == nrow) THEN
     809      6955169 :                      DO i = 1, nrow_local
     810      6955169 :                         full_block(i, j) = new_values(row_indices(i), this_col)
     811              :                      END DO
     812              :                   ELSE
     813       538491 :                      DO i = 1, nrow_local
     814       528241 :                         this_row = row_indices(i) - i0 + 1
     815       538491 :                         IF (this_row >= 1 .AND. this_row <= nrow) THEN
     816        58493 :                            full_block(i, j) = new_values(this_row, this_col)
     817              :                         END IF
     818              :                      END DO
     819              :                   END IF
     820              :                END IF
     821              :             END IF
     822              :          END DO
     823              :       ELSE
     824       838496 :          DO j = 1, ncol_local
     825       828910 :             this_col = col_indices(j) - j0 + 1
     826       838496 :             IF (this_col >= 1 .AND. this_col <= ncol) THEN
     827       828910 :                IF (tr_a) THEN
     828     88287891 :                   DO i = 1, nrow_local
     829     87458981 :                      this_row = row_indices(i) - i0 + 1
     830     88287891 :                      IF (this_row >= 1 .AND. this_row <= nrow) THEN
     831              :                         full_block(i, j) = al*new_values(this_col, this_row) + &
     832       414455 :                                            be*full_block(i, j)
     833              :                      END IF
     834              :                   END DO
     835              :                ELSE
     836            0 :                   DO i = 1, nrow_local
     837            0 :                      this_row = row_indices(i) - i0 + 1
     838            0 :                      IF (this_row >= 1 .AND. this_row <= nrow) THEN
     839              :                         full_block(i, j) = al*new_values(this_row, this_col) + &
     840            0 :                                            be*full_block(i, j)
     841              :                      END IF
     842              :                   END DO
     843              :                END IF
     844              :             END IF
     845              :          END DO
     846              :       END IF
     847              : 
     848        70643 :    END SUBROUTINE cp_fm_set_submatrix
     849              : 
     850              : ! **************************************************************************************************
     851              : !> \brief sets a submatrix of a full matrix to a given value
     852              : !>       fm(start_row:start_row+n_rows,start_col:start_col+n_cols) = value
     853              : !> \param fm the full to change
     854              : !> \param new_value  ...
     855              : !> \param start_row the starting row of matrix
     856              : !> \param start_col the starting col of matrix
     857              : !> \param n_rows the number of rows to change
     858              : !> \param n_cols the number of columns to change
     859              : !> \par History
     860              : !>      07.2002 created borrowing from Joost's blacs_replicated_copy [fawzi]
     861              : !>      12.2025 created from cp_fm_set_submatrix
     862              : !> \author JGH
     863              : ! **************************************************************************************************
     864      1102292 :    SUBROUTINE cp_fm_set_all_submatrix(fm, new_value, start_row, start_col, n_rows, n_cols)
     865              :       TYPE(cp_fm_type), INTENT(IN)                       :: fm
     866              :       REAL(KIND=dp), INTENT(in)                          :: new_value
     867              :       INTEGER, INTENT(in)                                :: start_row, start_col, n_rows, n_cols
     868              : 
     869              :       INTEGER                                            :: i, i0, j, j0, ncol_global, ncol_local, &
     870              :                                                             nrow_global, nrow_local, this_col, &
     871              :                                                             this_row
     872      1102292 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
     873      1102292 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: full_block
     874              : 
     875      1102292 :       full_block => fm%local_data
     876              : 
     877              :       CALL cp_fm_get_info(matrix=fm, &
     878              :                           nrow_global=nrow_global, ncol_global=ncol_global, &
     879              :                           nrow_local=nrow_local, ncol_local=ncol_local, &
     880      1102292 :                           row_indices=row_indices, col_indices=col_indices)
     881              : 
     882      1102292 :       i0 = start_row
     883      1102292 :       j0 = start_col
     884     20761024 :       DO j = 1, ncol_local
     885     19658732 :          this_col = col_indices(j) - j0 + 1
     886     20761024 :          IF (this_col >= 1 .AND. this_col <= n_cols) THEN
     887    639770447 :             DO i = 1, nrow_local
     888    620171677 :                this_row = row_indices(i) - i0 + 1
     889    639770447 :                IF (this_row >= 1 .AND. this_row <= n_rows) THEN
     890    612983463 :                   full_block(i, j) = new_value
     891              :                END IF
     892              :             END DO
     893              :          END IF
     894              :       END DO
     895              : 
     896      1102292 :    END SUBROUTINE cp_fm_set_all_submatrix
     897              : 
     898              : ! **************************************************************************************************
     899              : !> \brief gets a submatrix of a full matrix
     900              : !>       op(target_m)(1:n_rows,1:n_cols)
     901              : !>       =fm(start_row:start_row+n_rows,start_col:start_col+n_cols)
     902              : !>      target_m is replicated on all cpus
     903              : !>      using this call is expensive
     904              : !> \param fm the full you want to get the info from
     905              : !> \param target_m a replicated full matrix that will contain the result
     906              : !> \param start_row the starting row of b_matrix (defaults to 1)
     907              : !> \param start_col the starting col of b_matrix (defaults to 1)
     908              : !> \param n_rows the number of row to change in b (defaults to
     909              : !>        size(op(new_values),1))
     910              : !> \param n_cols the number of columns to change in b (defaults to
     911              : !>        size(op(new_values),2))
     912              : !> \param transpose if target_m should be transposed: if true
     913              : !>        op(target_m)=target_m^T, else op(target_m)=target_m
     914              : !>        (defaults to false)
     915              : !> \par History
     916              : !>      07.2002 created borrowing from Joost's blacs_replicated_copy [fawzi]
     917              : !> \author Fawzi Mohamed
     918              : !> \note
     919              : !>      optimized for full column updates. Zeros out a little too much
     920              : !>      of target_m
     921              : !>      the target_m is replicated and valid on all cpus
     922              : ! **************************************************************************************************
     923       100816 :    SUBROUTINE cp_fm_get_submatrix(fm, target_m, start_row, &
     924              :                                   start_col, n_rows, n_cols, transpose)
     925              :       TYPE(cp_fm_type), INTENT(IN)                       :: fm
     926              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(out)        :: target_m
     927              :       INTEGER, INTENT(in), OPTIONAL                      :: start_row, start_col, n_rows, n_cols
     928              :       LOGICAL, INTENT(in), OPTIONAL                      :: transpose
     929              : 
     930              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_get_submatrix'
     931              : 
     932              :       INTEGER                                            :: handle, i, i0, j, j0, ncol, ncol_global, &
     933              :                                                             ncol_local, nrow, nrow_global, &
     934              :                                                             nrow_local, this_col, this_row
     935       100816 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
     936              :       LOGICAL                                            :: tr_a
     937       100816 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: full_block
     938              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     939              : 
     940       100816 :       CALL timeset(routineN, handle)
     941              : 
     942       100816 :       i0 = 1; j0 = 1; tr_a = .FALSE.
     943              : 
     944       100816 :       IF (PRESENT(start_row)) i0 = start_row
     945       100816 :       IF (PRESENT(start_col)) j0 = start_col
     946       100816 :       IF (PRESENT(transpose)) tr_a = transpose
     947         6152 :       IF (tr_a) THEN
     948         2206 :          nrow = SIZE(target_m, 2)
     949         2206 :          ncol = SIZE(target_m, 1)
     950              :       ELSE
     951        98610 :          nrow = SIZE(target_m, 1)
     952        98610 :          ncol = SIZE(target_m, 2)
     953              :       END IF
     954       100816 :       IF (PRESENT(n_rows)) nrow = n_rows
     955       100816 :       IF (PRESENT(n_cols)) ncol = n_cols
     956              : 
     957       100816 :       para_env => fm%matrix_struct%para_env
     958              : 
     959       100816 :       full_block => fm%local_data
     960              : #if defined(__parallel)
     961              :       ! zero-out whole target_m
     962       100816 :       IF (SIZE(target_m, 1)*SIZE(target_m, 2) /= 0) THEN
     963       119008 :          CALL dcopy(SIZE(target_m, 1)*SIZE(target_m, 2), [0.0_dp], 0, target_m, 1)
     964              :       END IF
     965              : #endif
     966              : 
     967              :       CALL cp_fm_get_info(matrix=fm, &
     968              :                           nrow_global=nrow_global, ncol_global=ncol_global, &
     969              :                           nrow_local=nrow_local, ncol_local=ncol_local, &
     970       100816 :                           row_indices=row_indices, col_indices=col_indices)
     971              : 
     972       616874 :       DO j = 1, ncol_local
     973       516058 :          this_col = col_indices(j) - j0 + 1
     974       616874 :          IF (this_col >= 1 .AND. this_col <= ncol) THEN
     975       377070 :             IF (tr_a) THEN
     976         2206 :                IF (i0 == 1 .AND. nrow_global == nrow) THEN
     977        80516 :                   DO i = 1, nrow_local
     978        80516 :                      target_m(this_col, row_indices(i)) = full_block(i, j)
     979              :                   END DO
     980              :                ELSE
     981            0 :                   DO i = 1, nrow_local
     982            0 :                      this_row = row_indices(i) - i0 + 1
     983            0 :                      IF (this_row >= 1 .AND. this_row <= nrow) THEN
     984            0 :                         target_m(this_col, this_row) = full_block(i, j)
     985              :                      END IF
     986              :                   END DO
     987              :                END IF
     988              :             ELSE
     989       374864 :                IF (i0 == 1 .AND. nrow_global == nrow) THEN
     990      7989133 :                   DO i = 1, nrow_local
     991      7989133 :                      target_m(row_indices(i), this_col) = full_block(i, j)
     992              :                   END DO
     993              :                ELSE
     994      1735300 :                   DO i = 1, nrow_local
     995      1694848 :                      this_row = row_indices(i) - i0 + 1
     996      1735300 :                      IF (this_row >= 1 .AND. this_row <= nrow) THEN
     997        85570 :                         target_m(this_row, this_col) = full_block(i, j)
     998              :                      END IF
     999              :                   END DO
    1000              :                END IF
    1001              :             END IF
    1002              :          END IF
    1003              :       END DO
    1004              : 
    1005     23827844 :       CALL para_env%sum(target_m)
    1006              : 
    1007       100816 :       CALL timestop(handle)
    1008              : 
    1009       100816 :    END SUBROUTINE cp_fm_get_submatrix
    1010              : 
    1011              : ! **************************************************************************************************
    1012              : !> \brief returns all kind of information about the full matrix
    1013              : !> \param matrix ...
    1014              : !> \param name ...
    1015              : !> \param nrow_global ...
    1016              : !> \param ncol_global ...
    1017              : !> \param nrow_block ...
    1018              : !> \param ncol_block ...
    1019              : !> \param nrow_local ...
    1020              : !> \param ncol_local ...
    1021              : !> \param row_indices ...
    1022              : !> \param col_indices ...
    1023              : !> \param local_data ...
    1024              : !> \param context ...
    1025              : !> \param nrow_locals ...
    1026              : !> \param ncol_locals ...
    1027              : !> \param matrix_struct ...
    1028              : !> \param para_env ...
    1029              : !> \note
    1030              : !>       see also cp_fm_struct for explanation
    1031              : !>       - nrow_local, ncol_local, row_indices, col_indices, local_data are hooks for efficient
    1032              : !>         access to the local blacs block
    1033              : ! **************************************************************************************************
    1034      6380303 :    SUBROUTINE cp_fm_get_info(matrix, name, nrow_global, ncol_global, &
    1035              :                              nrow_block, ncol_block, nrow_local, ncol_local, &
    1036              :                              row_indices, col_indices, local_data, context, &
    1037              :                              nrow_locals, ncol_locals, matrix_struct, para_env)
    1038              : 
    1039              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix
    1040              :       CHARACTER(LEN=*), INTENT(OUT), OPTIONAL            :: name
    1041              :       INTEGER, INTENT(OUT), OPTIONAL                     :: nrow_global, ncol_global, nrow_block, &
    1042              :                                                             ncol_block, nrow_local, ncol_local
    1043              :       INTEGER, DIMENSION(:), OPTIONAL, POINTER           :: row_indices, col_indices
    1044              :       REAL(KIND=dp), CONTIGUOUS, DIMENSION(:, :), &
    1045              :          OPTIONAL, POINTER                               :: local_data
    1046              :       TYPE(cp_blacs_env_type), OPTIONAL, POINTER         :: context
    1047              :       INTEGER, DIMENSION(:), OPTIONAL, POINTER           :: nrow_locals, ncol_locals
    1048              :       TYPE(cp_fm_struct_type), OPTIONAL, POINTER         :: matrix_struct
    1049              :       TYPE(mp_para_env_type), OPTIONAL, POINTER          :: para_env
    1050              : 
    1051            8 :       IF (PRESENT(name)) name = matrix%name
    1052      6380303 :       IF (PRESENT(matrix_struct)) matrix_struct => matrix%matrix_struct
    1053      6380303 :       IF (PRESENT(local_data)) local_data => matrix%local_data ! not hiding things anymore :-(
    1054              : 
    1055              :       CALL cp_fm_struct_get(matrix%matrix_struct, nrow_local=nrow_local, &
    1056              :                             ncol_local=ncol_local, nrow_global=nrow_global, &
    1057              :                             ncol_global=ncol_global, nrow_block=nrow_block, &
    1058              :                             ncol_block=ncol_block, row_indices=row_indices, &
    1059              :                             col_indices=col_indices, nrow_locals=nrow_locals, &
    1060      6380303 :                             ncol_locals=ncol_locals, context=context, para_env=para_env)
    1061              : 
    1062      6380303 :    END SUBROUTINE cp_fm_get_info
    1063              : 
    1064              : ! **************************************************************************************************
    1065              : !> \brief Write nicely formatted info about the FM to the given I/O unit (including the underlying FM struct)
    1066              : !> \param matrix a cp_fm_type instance
    1067              : !> \param io_unit the I/O unit to use for writing
    1068              : ! **************************************************************************************************
    1069            3 :    SUBROUTINE cp_fm_write_info(matrix, io_unit)
    1070              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix
    1071              :       INTEGER, INTENT(IN)                                :: io_unit
    1072              : 
    1073            3 :       WRITE (io_unit, '(/,A,A12)') "CP_FM | Name:                           ", matrix%name
    1074            3 :       CALL cp_fm_struct_write_info(matrix%matrix_struct, io_unit)
    1075            3 :    END SUBROUTINE cp_fm_write_info
    1076              : 
    1077              : ! **************************************************************************************************
    1078              : !> \brief find the maximum absolute value of the matrix element
    1079              : !>      maxval(abs(matrix))
    1080              : !> \param matrix ...
    1081              : !> \param a_max ...
    1082              : !> \param ir_max ...
    1083              : !> \param ic_max ...
    1084              : ! **************************************************************************************************
    1085       127407 :    SUBROUTINE cp_fm_maxabsval(matrix, a_max, ir_max, ic_max)
    1086              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix
    1087              :       REAL(KIND=dp), INTENT(OUT)                         :: a_max
    1088              :       INTEGER, INTENT(OUT), OPTIONAL                     :: ir_max, ic_max
    1089              : 
    1090              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'cp_fm_maxabsval'
    1091              : 
    1092              :       INTEGER                                            :: handle, i, ic_max_local, ir_max_local, &
    1093              :                                                             j, mepos, ncol_local, nrow_local, &
    1094              :                                                             num_pe
    1095       127407 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: ic_max_vec, ir_max_vec
    1096       127407 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
    1097              :       REAL(dp)                                           :: my_max
    1098       127407 :       REAL(dp), ALLOCATABLE, DIMENSION(:)                :: a_max_vec
    1099       127407 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: my_block
    1100              : 
    1101       127407 :       CALL timeset(routineN, handle)
    1102              : 
    1103       127407 :       my_block => matrix%local_data
    1104              : 
    1105              :       CALL cp_fm_get_info(matrix, nrow_local=nrow_local, ncol_local=ncol_local, &
    1106       127407 :                           row_indices=row_indices, col_indices=col_indices)
    1107              : 
    1108     77958727 :       a_max = MAXVAL(ABS(my_block(1:nrow_local, 1:ncol_local)))
    1109              : 
    1110       127407 :       IF (PRESENT(ir_max)) THEN
    1111            0 :          num_pe = matrix%matrix_struct%para_env%num_pe
    1112            0 :          mepos = matrix%matrix_struct%para_env%mepos
    1113            0 :          ALLOCATE (ir_max_vec(0:num_pe - 1))
    1114            0 :          ir_max_vec(0:num_pe - 1) = 0
    1115            0 :          ALLOCATE (ic_max_vec(0:num_pe - 1))
    1116            0 :          ic_max_vec(0:num_pe - 1) = 0
    1117            0 :          ALLOCATE (a_max_vec(0:num_pe - 1))
    1118            0 :          a_max_vec(0:num_pe - 1) = 0.0_dp
    1119            0 :          my_max = 0.0_dp
    1120              : 
    1121            0 :          IF ((ncol_local > 0) .AND. (nrow_local > 0)) THEN
    1122            0 :             DO i = 1, ncol_local
    1123            0 :                DO j = 1, nrow_local
    1124            0 :                   IF (ABS(my_block(j, i)) > my_max) THEN
    1125            0 :                      my_max = my_block(j, i)
    1126            0 :                      ir_max_local = j
    1127            0 :                      ic_max_local = i
    1128              :                   END IF
    1129              :                END DO
    1130              :             END DO
    1131              : 
    1132            0 :             a_max_vec(mepos) = my_max
    1133            0 :             ir_max_vec(mepos) = row_indices(ir_max_local)
    1134            0 :             ic_max_vec(mepos) = col_indices(ic_max_local)
    1135              : 
    1136              :          END IF
    1137              : 
    1138            0 :          CALL matrix%matrix_struct%para_env%sum(a_max_vec)
    1139            0 :          CALL matrix%matrix_struct%para_env%sum(ir_max_vec)
    1140            0 :          CALL matrix%matrix_struct%para_env%sum(ic_max_vec)
    1141              : 
    1142            0 :          my_max = 0.0_dp
    1143            0 :          DO i = 0, num_pe - 1
    1144            0 :             IF (a_max_vec(i) > my_max) THEN
    1145            0 :                ir_max = ir_max_vec(i)
    1146            0 :                ic_max = ic_max_vec(i)
    1147              :             END IF
    1148              :          END DO
    1149              : 
    1150            0 :          DEALLOCATE (ir_max_vec, ic_max_vec, a_max_vec)
    1151            0 :          CPASSERT(ic_max > 0)
    1152            0 :          CPASSERT(ir_max > 0)
    1153              : 
    1154              :       END IF
    1155              : 
    1156       127407 :       CALL matrix%matrix_struct%para_env%max(a_max)
    1157              : 
    1158       127407 :       CALL timestop(handle)
    1159              : 
    1160       254814 :    END SUBROUTINE cp_fm_maxabsval
    1161              : 
    1162              : ! **************************************************************************************************
    1163              : !> \brief find the maximum over the rows of the sum of the absolute values of the elements of a given row
    1164              : !>      = || A ||_infinity
    1165              : !> \param matrix ...
    1166              : !> \param a_max ...
    1167              : !> \note
    1168              : !>      for a real symmetric matrix it holds that || A ||_2 = |lambda_max| < || A ||_infinity
    1169              : !>      Hence this can be used to estimate an upper bound for the eigenvalues of a matrix
    1170              : !>      http://mathworld.wolfram.com/MatrixNorm.html
    1171              : !>      (but the bound is not so tight in the general case)
    1172              : ! **************************************************************************************************
    1173         4986 :    SUBROUTINE cp_fm_maxabsrownorm(matrix, a_max)
    1174              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix
    1175              :       REAL(KIND=dp), INTENT(OUT)                         :: a_max
    1176              : 
    1177              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_maxabsrownorm'
    1178              : 
    1179              :       INTEGER                                            :: handle, i, j, ncol_local, nrow_global, &
    1180              :                                                             nrow_local
    1181         4986 :       INTEGER, DIMENSION(:), POINTER                     :: row_indices
    1182              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: values
    1183         4986 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: my_block
    1184              : 
    1185         4986 :       CALL timeset(routineN, handle)
    1186              : 
    1187         4986 :       my_block => matrix%local_data
    1188              : 
    1189              :       CALL cp_fm_get_info(matrix, row_indices=row_indices, nrow_global=nrow_global, &
    1190         4986 :                           nrow_local=nrow_local, ncol_local=ncol_local)
    1191              : 
    1192              :       ! the efficiency could be improved by making use of the row-col distribution of scalapack
    1193        14958 :       ALLOCATE (values(nrow_global))
    1194         4986 :       values = 0.0_dp
    1195        65834 :       DO j = 1, ncol_local
    1196       535250 :          DO i = 1, nrow_local
    1197       530264 :             values(row_indices(i)) = values(row_indices(i)) + ABS(my_block(i, j))
    1198              :          END DO
    1199              :       END DO
    1200         4986 :       CALL matrix%matrix_struct%para_env%sum(values)
    1201        65834 :       a_max = MAXVAL(values)
    1202         4986 :       DEALLOCATE (values)
    1203              : 
    1204         4986 :       CALL timestop(handle)
    1205         4986 :    END SUBROUTINE cp_fm_maxabsrownorm
    1206              : 
    1207              : ! **************************************************************************************************
    1208              : !> \brief find the inorm of each column norm_{j}= sqrt( \sum_{i} A_{ij}*A_{ij} )
    1209              : !> \param matrix ...
    1210              : !> \param norm_array ...
    1211              : ! **************************************************************************************************
    1212         1316 :    SUBROUTINE cp_fm_vectorsnorm(matrix, norm_array)
    1213              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix
    1214              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: norm_array
    1215              : 
    1216              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'cp_fm_vectorsnorm'
    1217              : 
    1218              :       INTEGER                                            :: handle, i, j, ncol_global, ncol_local, &
    1219              :                                                             nrow_local
    1220         1316 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices
    1221         1316 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: my_block
    1222              : 
    1223         1316 :       CALL timeset(routineN, handle)
    1224              : 
    1225         1316 :       my_block => matrix%local_data
    1226              : 
    1227              :       CALL cp_fm_get_info(matrix, col_indices=col_indices, ncol_global=ncol_global, &
    1228         1316 :                           nrow_local=nrow_local, ncol_local=ncol_local)
    1229              : 
    1230              :       ! the efficiency could be improved by making use of the row-col distribution of scalapack
    1231        31004 :       norm_array = 0.0_dp
    1232        31004 :       DO j = 1, ncol_local
    1233      1325922 :          DO i = 1, nrow_local
    1234      1324606 :             norm_array(col_indices(j)) = norm_array(col_indices(j)) + my_block(i, j)*my_block(i, j)
    1235              :          END DO
    1236              :       END DO
    1237        60692 :       CALL matrix%matrix_struct%para_env%sum(norm_array)
    1238        31004 :       norm_array = SQRT(norm_array)
    1239              : 
    1240         1316 :       CALL timestop(handle)
    1241         1316 :    END SUBROUTINE cp_fm_vectorsnorm
    1242              : 
    1243              : ! **************************************************************************************************
    1244              : !> \brief summing up all the elements along the matrix's i-th index
    1245              : !>        \f$ \mathrm{sum}_{j} = \sum_{i} A_{ij} \f$
    1246              : !>        or
    1247              : !>        \f$ \mathrm{sum}_{i} = \sum_{j} A_{ij} \f$
    1248              : !> \param matrix     an input matrix A
    1249              : !> \param sum_array  sums of elements in each column/row
    1250              : !> \param dir ...
    1251              : !> \note  forked from cp_fm_vectorsnorm() to be used with
    1252              : !>        the maximum overlap method
    1253              : !>        added row variation
    1254              : ! **************************************************************************************************
    1255        11782 :    SUBROUTINE cp_fm_vectorssum(matrix, sum_array, dir)
    1256              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix
    1257              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: sum_array
    1258              :       CHARACTER(LEN=1), INTENT(IN), OPTIONAL             :: dir
    1259              : 
    1260              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'cp_fm_vectorssum'
    1261              : 
    1262              :       INTEGER                                            :: handle, i, j, ncol_local, nrow_local
    1263        11782 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices, row_indices
    1264              :       LOGICAL                                            :: docol
    1265        11782 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: my_block
    1266              : 
    1267        11782 :       CALL timeset(routineN, handle)
    1268              : 
    1269        11782 :       IF (PRESENT(dir)) THEN
    1270        11742 :          IF (dir == 'c' .OR. dir == 'C') THEN
    1271              :             docol = .TRUE.
    1272              :          ELSE IF (dir == 'r' .OR. dir == 'R') THEN
    1273              :             docol = .FALSE.
    1274              :          ELSE
    1275            0 :             CPABORT('Wrong argument DIR')
    1276              :          END IF
    1277              :       ELSE
    1278              :          docol = .TRUE.
    1279              :       END IF
    1280              : 
    1281        11782 :       my_block => matrix%local_data
    1282              : 
    1283              :       CALL cp_fm_get_info(matrix, col_indices=col_indices, row_indices=row_indices, &
    1284        11782 :                           nrow_local=nrow_local, ncol_local=ncol_local)
    1285              : 
    1286              :       ! the efficiency could be improved by making use of the row-col distribution of scalapack
    1287       286428 :       sum_array(:) = 0.0_dp
    1288        11782 :       IF (docol) THEN
    1289          448 :       DO j = 1, ncol_local
    1290         3628 :          DO i = 1, nrow_local
    1291         3588 :             sum_array(col_indices(j)) = sum_array(col_indices(j)) + my_block(i, j)
    1292              :          END DO
    1293              :       END DO
    1294              :       ELSE
    1295       102950 :       DO j = 1, ncol_local
    1296      6528294 :          DO i = 1, nrow_local
    1297      6516552 :             sum_array(row_indices(i)) = sum_array(row_indices(i)) + my_block(i, j)
    1298              :          END DO
    1299              :       END DO
    1300              :       END IF
    1301       561074 :       CALL matrix%matrix_struct%para_env%sum(sum_array)
    1302              : 
    1303        11782 :       CALL timestop(handle)
    1304        11782 :    END SUBROUTINE cp_fm_vectorssum
    1305              : 
    1306              : ! **************************************************************************************************
    1307              : !> \brief copy one identically sized matrix in the other
    1308              : !> \param source ...
    1309              : !> \param destination ...
    1310              : !> \note
    1311              : !>      see also cp_fm_to_fm_columns
    1312              : ! **************************************************************************************************
    1313       584285 :    SUBROUTINE cp_fm_to_fm_matrix(source, destination)
    1314              : 
    1315              :       TYPE(cp_fm_type), INTENT(IN)                       :: source, destination
    1316              : 
    1317              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_to_fm_matrix'
    1318              : 
    1319              :       INTEGER                                            :: handle, npcol, nprow
    1320              : 
    1321       584285 :       CALL timeset(routineN, handle)
    1322              : 
    1323       584285 :       nprow = source%matrix_struct%context%num_pe(1)
    1324       584285 :       npcol = source%matrix_struct%context%num_pe(2)
    1325              : 
    1326       584285 :       IF ((.NOT. cp2k_is_parallel) .OR. &
    1327              :           cp_fm_struct_equivalent(source%matrix_struct, &
    1328              :                                   destination%matrix_struct)) THEN
    1329       584285 :          IF (SIZE(source%local_data, 1) /= SIZE(destination%local_data, 1) .OR. &
    1330              :              SIZE(source%local_data, 2) /= SIZE(destination%local_data, 2)) THEN
    1331              :             CALL cp_abort(__LOCATION__, &
    1332              :                           "Cannot copy full matrix <"//TRIM(source%name)// &
    1333              :                           "> to full matrix <"//TRIM(destination%name)// &
    1334            0 :                           ">. The local_data blocks have different sizes.")
    1335              :          END IF
    1336              :          CALL dcopy(SIZE(source%local_data, 1)*SIZE(source%local_data, 2), &
    1337       584285 :                     source%local_data, 1, destination%local_data, 1)
    1338              :       ELSE
    1339            0 :          CPABORT("Data structures of source and target full matrix are not equivalent")
    1340              :       END IF
    1341              : 
    1342       584285 :       CALL timestop(handle)
    1343              : 
    1344       584285 :    END SUBROUTINE cp_fm_to_fm_matrix
    1345              : 
    1346              : ! **************************************************************************************************
    1347              : !> \brief copy just a subset of columns of a fm to a fm
    1348              : !> \param msource ...
    1349              : !> \param mtarget ...
    1350              : !> \param ncol ...
    1351              : !> \param source_start ...
    1352              : !> \param target_start ...
    1353              : ! **************************************************************************************************
    1354       173124 :    SUBROUTINE cp_fm_to_fm_columns(msource, mtarget, ncol, source_start, &
    1355              :                                   target_start)
    1356              : 
    1357              :       TYPE(cp_fm_type), INTENT(IN)          :: msource, mtarget
    1358              :       INTEGER, INTENT(IN)                      :: ncol
    1359              :       INTEGER, INTENT(IN), OPTIONAL            :: source_start, target_start
    1360              : 
    1361              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_to_fm_columns'
    1362              : 
    1363              :       INTEGER                                  :: handle, n, ss, ts
    1364              :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a, b
    1365              : #if defined(__parallel)
    1366              :       INTEGER                                  :: i
    1367              :       INTEGER, DIMENSION(9)                    :: desca, descb
    1368              : #endif
    1369              : 
    1370       173124 :       CALL timeset(routineN, handle)
    1371              : 
    1372       173124 :       ss = 1
    1373       173124 :       ts = 1
    1374              : 
    1375       173124 :       IF (PRESENT(source_start)) ss = source_start
    1376       173124 :       IF (PRESENT(target_start)) ts = target_start
    1377              : 
    1378       173124 :       n = msource%matrix_struct%nrow_global
    1379              : 
    1380       173124 :       a => msource%local_data
    1381       173124 :       b => mtarget%local_data
    1382              : 
    1383              : #if defined(__parallel)
    1384      1731240 :       desca(:) = msource%matrix_struct%descriptor(:)
    1385      1731240 :       descb(:) = mtarget%matrix_struct%descriptor(:)
    1386       744908 :       DO i = 0, ncol - 1
    1387       744908 :          CALL pdcopy(n, a, 1, ss + i, desca, 1, b, 1, ts + i, descb, 1)
    1388              :       END DO
    1389              : #else
    1390              :       IF (ss <= SIZE(a, 2) .AND. ts <= SIZE(b, 2)) THEN
    1391              :          CALL dcopy(ncol*n, a(:, ss), 1, b(:, ts), 1)
    1392              :       END IF
    1393              : #endif
    1394              : 
    1395       173124 :       CALL timestop(handle)
    1396              : 
    1397       173124 :    END SUBROUTINE cp_fm_to_fm_columns
    1398              : 
    1399              : ! **************************************************************************************************
    1400              : !> \brief copy just a triangular matrix
    1401              : !> \param msource ...
    1402              : !> \param mtarget ...
    1403              : !> \param uplo ...
    1404              : ! **************************************************************************************************
    1405           58 :    SUBROUTINE cp_fm_to_fm_triangular(msource, mtarget, uplo)
    1406              : 
    1407              :       TYPE(cp_fm_type), INTENT(IN)             :: msource, mtarget
    1408              :       CHARACTER(LEN=1), OPTIONAL, INTENT(IN)   :: uplo
    1409              : 
    1410              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_to_fm_triangular'
    1411              : 
    1412              :       CHARACTER(LEN=1)                         :: myuplo
    1413              :       INTEGER                                  :: handle, ncol, nrow
    1414           58 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a, b
    1415              : #if defined(__parallel)
    1416              :       INTEGER, DIMENSION(9)                    :: desca, descb
    1417              : #endif
    1418              : 
    1419           58 :       CALL timeset(routineN, handle)
    1420              : 
    1421           58 :       myuplo = 'U'
    1422           58 :       IF (PRESENT(uplo)) myuplo = uplo
    1423              : 
    1424           58 :       nrow = msource%matrix_struct%nrow_global
    1425           58 :       ncol = msource%matrix_struct%ncol_global
    1426              : 
    1427           58 :       a => msource%local_data
    1428           58 :       b => mtarget%local_data
    1429              : 
    1430              : #if defined(__parallel)
    1431          580 :       desca(:) = msource%matrix_struct%descriptor(:)
    1432          580 :       descb(:) = mtarget%matrix_struct%descriptor(:)
    1433           58 :       CALL pdlacpy(myuplo, nrow, ncol, a(1, 1), 1, 1, desca, b(1, 1), 1, 1, descb)
    1434              : #else
    1435              :       CALL dlacpy(myuplo, nrow, ncol, a(1, 1), nrow, b(1, 1), nrow)
    1436              : #endif
    1437              : 
    1438           58 :       CALL timestop(handle)
    1439              : 
    1440           58 :    END SUBROUTINE cp_fm_to_fm_triangular
    1441              : 
    1442              : ! **************************************************************************************************
    1443              : !> \brief copy just a part ot the matrix
    1444              : !> \param msource ...
    1445              : !> \param mtarget ...
    1446              : !> \param nrow ...
    1447              : !> \param ncol ...
    1448              : !> \param s_firstrow ...
    1449              : !> \param s_firstcol ...
    1450              : !> \param t_firstrow ...
    1451              : !> \param t_firstcol ...
    1452              : ! **************************************************************************************************
    1453              : 
    1454        16396 :    SUBROUTINE cp_fm_to_fm_submat(msource, mtarget, nrow, ncol, s_firstrow, s_firstcol, t_firstrow, t_firstcol)
    1455              : 
    1456              :       TYPE(cp_fm_type), INTENT(IN)             :: msource, mtarget
    1457              :       INTEGER, INTENT(IN)                      :: nrow, ncol, s_firstrow, &
    1458              :                                                   s_firstcol, t_firstrow, &
    1459              :                                                   t_firstcol
    1460              : 
    1461              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_to_fm_submat'
    1462              : 
    1463              :       INTEGER                                  :: handle, i, na, nb, ss, ts
    1464              :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a, b
    1465              : #if defined(__parallel)
    1466              :       INTEGER, DIMENSION(9)                    :: desca, descb
    1467              : #endif
    1468              : 
    1469        16396 :       CALL timeset(routineN, handle)
    1470              : 
    1471        16396 :       a => msource%local_data
    1472        16396 :       b => mtarget%local_data
    1473              : 
    1474        16396 :       na = msource%matrix_struct%nrow_global
    1475        16396 :       nb = mtarget%matrix_struct%nrow_global
    1476              : !    nrow must be <= na and nb
    1477        16396 :       IF (nrow > na) THEN
    1478            0 :          CPABORT("cannot copy because nrow > number of rows of source matrix")
    1479              :       END IF
    1480        16396 :       IF (nrow > nb) THEN
    1481            0 :          CPABORT("cannot copy because nrow > number of rows of target matrix")
    1482              :       END IF
    1483        16396 :       na = msource%matrix_struct%ncol_global
    1484        16396 :       nb = mtarget%matrix_struct%ncol_global
    1485              : !    ncol must be <= na_col and nb_col
    1486        16396 :       IF (ncol > na) THEN
    1487            0 :          CPABORT("cannot copy because nrow > number of rows of source matrix")
    1488              :       END IF
    1489        16396 :       IF (ncol > nb) THEN
    1490            0 :          CPABORT("cannot copy because nrow > number of rows of target matrix")
    1491              :       END IF
    1492              : 
    1493              : #if defined(__parallel)
    1494       163960 :       desca(:) = msource%matrix_struct%descriptor(:)
    1495       163960 :       descb(:) = mtarget%matrix_struct%descriptor(:)
    1496       184512 :       DO i = 0, ncol - 1
    1497       168116 :          ss = s_firstcol + i
    1498       168116 :          ts = t_firstcol + i
    1499       184512 :          CALL pdcopy(nrow, a, s_firstrow, ss, desca, 1, b, t_firstrow, ts, descb, 1)
    1500              :       END DO
    1501              : #else
    1502              :       DO i = 0, ncol - 1
    1503              :          ss = s_firstcol + i
    1504              :          ts = t_firstcol + i
    1505              :          CALL dcopy(nrow, a(s_firstrow:, ss), 1, b(t_firstrow:, ts), 1)
    1506              :       END DO
    1507              : #endif
    1508              : 
    1509        16396 :       CALL timestop(handle)
    1510        16396 :    END SUBROUTINE cp_fm_to_fm_submat
    1511              : 
    1512              : ! **************************************************************************************************
    1513              : !> \brief General copy of a fm matrix to another fm matrix.
    1514              : !>        Uses non-blocking MPI rather than ScaLAPACK.
    1515              : !>
    1516              : !> \param source          input fm matrix
    1517              : !> \param destination     output fm matrix
    1518              : !> \param para_env        parallel environment corresponding to the BLACS env that covers all parts
    1519              : !>                        of the input and output matrices
    1520              : !> \par History
    1521              : !>      31-Jan-2017 : Re-implemented using non-blocking MPI [IainB, MarkT]
    1522              : ! **************************************************************************************************
    1523        19008 :    SUBROUTINE cp_fm_copy_general(source, destination, para_env)
    1524              :       TYPE(cp_fm_type), INTENT(IN)                       :: source, destination
    1525              :       TYPE(mp_para_env_type), INTENT(IN)                 :: para_env
    1526              : 
    1527              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_copy_general'
    1528              : 
    1529              :       INTEGER                                            :: handle
    1530       171072 :       TYPE(copy_info_type)                               :: info
    1531              : 
    1532        19008 :       CALL timeset(routineN, handle)
    1533              : 
    1534        19008 :       CALL cp_fm_start_copy_general(source, destination, para_env, info)
    1535        19008 :       IF (ASSOCIATED(destination%matrix_struct)) THEN
    1536        18994 :          CALL cp_fm_finish_copy_general(destination, info)
    1537              :       END IF
    1538        19008 :       IF (ASSOCIATED(source%matrix_struct)) THEN
    1539        18803 :          CALL cp_fm_cleanup_copy_general(info)
    1540              :       END IF
    1541              : 
    1542        19008 :       CALL timestop(handle)
    1543        19008 :    END SUBROUTINE cp_fm_copy_general
    1544              : 
    1545              : ! **************************************************************************************************
    1546              : !> \brief Initiates the copy operation: get distribution data, post MPI isend and irecvs
    1547              : !> \param source input fm matrix
    1548              : !> \param destination output fm matrix
    1549              : !> \param para_env parallel environment corresponding to the BLACS env that covers all parts
    1550              : !>                        of the input and output matrices
    1551              : !> \param info all of the data that will be needed to complete the copy operation
    1552              : ! **************************************************************************************************
    1553      7476320 :    SUBROUTINE cp_fm_start_copy_general(source, destination, para_env, info)
    1554              :       TYPE(cp_fm_type), INTENT(IN)                       :: source, destination
    1555              :       TYPE(mp_para_env_type), INTENT(IN)                 :: para_env
    1556              :       TYPE(copy_info_type), INTENT(OUT)                  :: info
    1557              : 
    1558              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_start_copy_general'
    1559              : 
    1560              :       INTEGER :: dest_p_i, dest_q_j, global_rank, global_size, handle, i, j, k, mpi_rank, &
    1561              :          ncol_block_dest, ncol_block_src, ncol_local_recv, ncol_local_send, ncols, &
    1562              :          nrow_block_dest, nrow_block_src, nrow_local_recv, nrow_local_send, nrows, p, q, &
    1563              :          recv_rank, recv_size, send_rank, send_size
    1564       747632 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: all_ranks, dest2global, dest_p, dest_q, &
    1565      1495264 :                                                             recv_count, send_count, send_disp, &
    1566       747632 :                                                             source2global, src_p, src_q
    1567       747632 :       INTEGER, ALLOCATABLE, DIMENSION(:, :)              :: dest_blacs2mpi
    1568              :       INTEGER, DIMENSION(2)                              :: dest_block, dest_block_tmp, dest_num_pe, &
    1569              :                                                             src_block, src_block_tmp, src_num_pe
    1570      1495264 :       INTEGER, DIMENSION(:), POINTER                     :: recv_col_indices, recv_row_indices, &
    1571      1495264 :                                                             send_col_indices, send_row_indices
    1572              :       TYPE(cp_fm_struct_type), POINTER                   :: recv_dist, send_dist
    1573     10466848 :       TYPE(mp_request_type), DIMENSION(6)                :: recv_req, send_req
    1574              : 
    1575       747632 :       CALL timeset(routineN, handle)
    1576              : 
    1577              :       IF (.NOT. cp2k_is_parallel) THEN
    1578              :          ! Just copy all of the matrix data into a 'send buffer', to be unpacked later
    1579              :          nrow_local_send = SIZE(source%local_data, 1)
    1580              :          ncol_local_send = SIZE(source%local_data, 2)
    1581              :          ALLOCATE (info%send_buf(nrow_local_send*ncol_local_send))
    1582              :          k = 0
    1583              :          DO j = 1, ncol_local_send
    1584              :             DO i = 1, nrow_local_send
    1585              :                k = k + 1
    1586              :                info%send_buf(k) = source%local_data(i, j)
    1587              :             END DO
    1588              :          END DO
    1589              :       ELSE
    1590       747632 :          NULLIFY (recv_dist, send_dist)
    1591       747632 :          NULLIFY (recv_col_indices, recv_row_indices, send_col_indices, send_row_indices)
    1592              : 
    1593              :          ! The 'global' communicator contains both the source and destination decompositions
    1594       747632 :          global_size = para_env%num_pe
    1595       747632 :          global_rank = para_env%mepos
    1596              : 
    1597              :          ! The source/send decomposition and destination/recv decompositions may only exist on
    1598              :          ! on a subset of the processes involved in the communication
    1599              :          ! Check if the source and/or destination arguments are .not. ASSOCIATED():
    1600              :          ! if so, skip the send / recv parts (since these processes do not participate in the sending/receiving distribution)
    1601       747632 :          IF (ASSOCIATED(destination%matrix_struct)) THEN
    1602       566770 :             recv_dist => destination%matrix_struct
    1603       566770 :             recv_rank = recv_dist%para_env%mepos
    1604              :          ELSE
    1605       180862 :             recv_rank = mp_proc_null
    1606              :          END IF
    1607              : 
    1608       747632 :          IF (ASSOCIATED(source%matrix_struct)) THEN
    1609       656671 :             send_dist => source%matrix_struct
    1610       656671 :             send_rank = send_dist%para_env%mepos
    1611              :          ELSE
    1612        90961 :             send_rank = mp_proc_null
    1613              :          END IF
    1614              : 
    1615              :          ! Map the rank in the source/dest communicator to the global rank
    1616      2242896 :          ALLOCATE (all_ranks(0:global_size - 1))
    1617              : 
    1618       747632 :          CALL para_env%allgather(send_rank, all_ranks)
    1619       747632 :          IF (ASSOCIATED(recv_dist)) THEN
    1620      2833850 :             ALLOCATE (source2global(0:COUNT(all_ranks /= mp_proc_null) - 1))
    1621      1700310 :             DO i = 0, global_size - 1
    1622      1700310 :                IF (all_ranks(i) /= mp_proc_null) THEN
    1623       951618 :                   source2global(all_ranks(i)) = i
    1624              :                END IF
    1625              :             END DO
    1626              :          END IF
    1627              : 
    1628       747632 :          CALL para_env%allgather(recv_rank, all_ranks)
    1629       747632 :          IF (ASSOCIATED(send_dist)) THEN
    1630      3283355 :             ALLOCATE (dest2global(0:COUNT(all_ranks /= mp_proc_null) - 1))
    1631      1970013 :             DO i = 0, global_size - 1
    1632      1970013 :                IF (all_ranks(i) /= mp_proc_null) THEN
    1633       951618 :                   dest2global(all_ranks(i)) = i
    1634              :                END IF
    1635              :             END DO
    1636              :          END IF
    1637       747632 :          DEALLOCATE (all_ranks)
    1638              : 
    1639              :          ! Some data from the two decompositions will be needed by all processes in the global group :
    1640              :          ! process grid shape, block size, and the BLACS-to-MPI mapping
    1641              : 
    1642              :          ! The global root process will receive the data (from the root process in each decomposition)
    1643      5233424 :          send_req(:) = mp_request_null
    1644       747632 :          IF (global_rank == 0) THEN
    1645      2616712 :             recv_req(:) = mp_request_null
    1646       373816 :             CALL para_env%irecv(src_block, mp_any_source, recv_req(1), tag=src_tag)
    1647       373816 :             CALL para_env%irecv(dest_block, mp_any_source, recv_req(2), tag=dest_tag)
    1648       373816 :             CALL para_env%irecv(src_num_pe, mp_any_source, recv_req(3), tag=src_tag)
    1649       373816 :             CALL para_env%irecv(dest_num_pe, mp_any_source, recv_req(4), tag=dest_tag)
    1650              :          END IF
    1651              : 
    1652       747632 :          IF (ASSOCIATED(send_dist)) THEN
    1653       656671 :             IF ((send_rank == 0)) THEN
    1654              :                ! need to use separate buffers here in case this is actually global rank 0
    1655      1121448 :                src_block_tmp = [send_dist%nrow_block, send_dist%ncol_block]
    1656       373816 :                CALL para_env%isend(src_block_tmp, 0, send_req(1), tag=src_tag)
    1657       373816 :                CALL para_env%isend(send_dist%context%num_pe, 0, send_req(2), tag=src_tag)
    1658              :             END IF
    1659              :          END IF
    1660              : 
    1661       747632 :          IF (ASSOCIATED(recv_dist)) THEN
    1662       566770 :             IF ((recv_rank == 0)) THEN
    1663      1121448 :                dest_block_tmp = [recv_dist%nrow_block, recv_dist%ncol_block]
    1664       373816 :                CALL para_env%isend(dest_block_tmp, 0, send_req(3), tag=dest_tag)
    1665       373816 :                CALL para_env%isend(recv_dist%context%num_pe, 0, send_req(4), tag=dest_tag)
    1666              :             END IF
    1667              :          END IF
    1668              : 
    1669       747632 :          IF (global_rank == 0) THEN
    1670       373816 :             CALL mp_waitall(recv_req(1:4))
    1671              :             ! Now we know the process decomposition, we can allocate the arrays to hold the blacs2mpi mapping
    1672            0 :             ALLOCATE (info%src_blacs2mpi(0:src_num_pe(1) - 1, 0:src_num_pe(2) - 1), &
    1673              :                       dest_blacs2mpi(0:dest_num_pe(1) - 1, 0:dest_num_pe(2) - 1) &
    1674      2616712 :                       )
    1675       373816 :             CALL para_env%irecv(info%src_blacs2mpi, mp_any_source, recv_req(5), tag=src_tag)
    1676       373816 :             CALL para_env%irecv(dest_blacs2mpi, mp_any_source, recv_req(6), tag=dest_tag)
    1677              :          END IF
    1678              : 
    1679       747632 :          IF (ASSOCIATED(send_dist)) THEN
    1680       656671 :             IF ((send_rank == 0)) THEN
    1681       373816 :                CALL para_env%isend(send_dist%context%blacs2mpi(:, :), 0, send_req(5), tag=src_tag)
    1682              :             END IF
    1683              :          END IF
    1684              : 
    1685       747632 :          IF (ASSOCIATED(recv_dist)) THEN
    1686       566770 :             IF ((recv_rank == 0)) THEN
    1687       373816 :                CALL para_env%isend(recv_dist%context%blacs2mpi(:, :), 0, send_req(6), tag=dest_tag)
    1688              :             END IF
    1689              :          END IF
    1690              : 
    1691       747632 :          IF (global_rank == 0) THEN
    1692       373816 :             CALL mp_waitall(recv_req(5:6))
    1693              :          END IF
    1694              : 
    1695              :          ! Finally, broadcast the data to all processes in the global communicator
    1696       747632 :          CALL para_env%bcast(src_block, 0)
    1697       747632 :          CALL para_env%bcast(dest_block, 0)
    1698       747632 :          CALL para_env%bcast(src_num_pe, 0)
    1699       747632 :          CALL para_env%bcast(dest_num_pe, 0)
    1700      2242896 :          info%src_num_pe(1:2) = src_num_pe(1:2)
    1701      2242896 :          info%nblock_src(1:2) = src_block(1:2)
    1702       747632 :          IF (global_rank /= 0) THEN
    1703            0 :             ALLOCATE (info%src_blacs2mpi(0:src_num_pe(1) - 1, 0:src_num_pe(2) - 1), &
    1704            0 :                       dest_blacs2mpi(0:dest_num_pe(1) - 1, 0:dest_num_pe(2) - 1) &
    1705      2616712 :                       )
    1706              :          END IF
    1707       747632 :          CALL para_env%bcast(info%src_blacs2mpi, 0)
    1708       747632 :          CALL para_env%bcast(dest_blacs2mpi, 0)
    1709              : 
    1710       747632 :          recv_size = dest_num_pe(1)*dest_num_pe(2)
    1711       747632 :          send_size = src_num_pe(1)*src_num_pe(2)
    1712       747632 :          info%send_size = send_size
    1713       747632 :          CALL mp_waitall(send_req(:))
    1714              : 
    1715              :          ! Setup is now complete, we can start the actual communication here.
    1716              :          ! The order implemented here is:
    1717              :          !  DEST_1
    1718              :          !      compute recv sizes
    1719              :          !      call irecv
    1720              :          !  SRC_1
    1721              :          !      compute send sizes
    1722              :          !      pack send buffers
    1723              :          !      call isend
    1724              :          !  DEST_2
    1725              :          !      wait for the recvs and unpack buffers (this part eventually will go into another
    1726              :          !         routine to allow comms to run concurrently)
    1727              :          !  SRC_2
    1728              :          !      wait for the sends
    1729              : 
    1730              :          ! DEST_1
    1731       747632 :          IF (ASSOCIATED(recv_dist)) THEN
    1732              :             CALL cp_fm_struct_get(recv_dist, row_indices=recv_row_indices, &
    1733              :                                   col_indices=recv_col_indices &
    1734       566770 :                                   )
    1735       566770 :             info%recv_col_indices => recv_col_indices
    1736       566770 :             info%recv_row_indices => recv_row_indices
    1737       566770 :             nrow_block_src = src_block(1)
    1738       566770 :             ncol_block_src = src_block(2)
    1739      3785468 :             ALLOCATE (recv_count(0:send_size - 1), info%recv_disp(0:send_size - 1), info%recv_request(0:send_size - 1))
    1740              : 
    1741              :             ! Determine the recv counts, allocate the receive buffers, call mpi_irecv for all the non-zero sized receives
    1742       566770 :             nrow_local_recv = recv_dist%nrow_locals(recv_dist%context%mepos(1))
    1743       566770 :             ncol_local_recv = recv_dist%ncol_locals(recv_dist%context%mepos(2))
    1744       566770 :             info%nlocal_recv(1) = nrow_local_recv
    1745       566770 :             info%nlocal_recv(2) = ncol_local_recv
    1746              :             ! Initialise src_p, src_q arrays (sized using number of rows/cols in the receiving distribution)
    1747      2833850 :             ALLOCATE (src_p(nrow_local_recv), src_q(ncol_local_recv))
    1748     10243499 :             DO i = 1, nrow_local_recv
    1749              :                ! For each local row we will receive, we look up its global row (in recv_row_indices),
    1750              :                ! then work out which row block it comes from, and which process row that row block comes from.
    1751     10243499 :                src_p(i) = MOD(((recv_row_indices(i) - 1)/nrow_block_src), src_num_pe(1))
    1752              :             END DO
    1753     15081274 :             DO j = 1, ncol_local_recv
    1754              :                ! Similarly for the columns
    1755     15081274 :                src_q(j) = MOD(((recv_col_indices(j) - 1)/ncol_block_src), src_num_pe(2))
    1756              :             END DO
    1757              :             ! src_p/q now contains the process row/column ID that will send data to that row/column
    1758              : 
    1759      1133540 :             DO q = 0, src_num_pe(2) - 1
    1760     15081274 :                ncols = COUNT(src_q == q)
    1761      2085158 :                DO p = 0, src_num_pe(1) - 1
    1762     18055250 :                   nrows = COUNT(src_p == p)
    1763              :                   ! Use the send_dist here as we are looking up the processes where the data comes from
    1764      1518388 :                   recv_count(info%src_blacs2mpi(p, q)) = nrows*ncols
    1765              :                END DO
    1766              :             END DO
    1767       566770 :             DEALLOCATE (src_p, src_q)
    1768              : 
    1769              :             ! Use one long buffer (and displacements into that buffer)
    1770              :             !     this prevents the need for a rectangular array where not all elements will be populated
    1771      2651928 :             ALLOCATE (info%recv_buf(SUM(recv_count(:))))
    1772       566770 :             info%recv_disp(0) = 0
    1773       951618 :             DO i = 1, send_size - 1
    1774       951618 :                info%recv_disp(i) = info%recv_disp(i - 1) + recv_count(i - 1)
    1775              :             END DO
    1776              : 
    1777              :             ! Issue receive calls on ranks which expect data
    1778      1518388 :             DO k = 0, send_size - 1
    1779      1518388 :                IF (recv_count(k) > 0) THEN
    1780              :                   CALL para_env%irecv(info%recv_buf(info%recv_disp(k) + 1:info%recv_disp(k) + recv_count(k)), &
    1781       748198 :                                       source2global(k), info%recv_request(k))
    1782              :                END IF
    1783              :             END DO
    1784       566770 :             DEALLOCATE (source2global)
    1785              :          END IF ! ASSOCIATED(recv_dist)
    1786              : 
    1787              :          ! SRC_1
    1788       747632 :          IF (ASSOCIATED(send_dist)) THEN
    1789              :             CALL cp_fm_struct_get(send_dist, row_indices=send_row_indices, &
    1790              :                                   col_indices=send_col_indices &
    1791       656671 :                                   )
    1792       656671 :             nrow_block_dest = dest_block(1)
    1793       656671 :             ncol_block_dest = dest_block(2)
    1794      4234973 :             ALLOCATE (send_count(0:recv_size - 1), send_disp(0:recv_size - 1), info%send_request(0:recv_size - 1))
    1795              : 
    1796              :             ! Determine the send counts, allocate the send buffers
    1797       656671 :             nrow_local_send = send_dist%nrow_locals(send_dist%context%mepos(1))
    1798       656671 :             ncol_local_send = send_dist%ncol_locals(send_dist%context%mepos(2))
    1799              : 
    1800              :             ! Initialise dest_p, dest_q arrays (sized nrow_local, ncol_local)
    1801              :             !   i.e. number of rows,cols in the sending distribution
    1802      3283355 :             ALLOCATE (dest_p(nrow_local_send), dest_q(ncol_local_send))
    1803              : 
    1804     10333400 :             DO i = 1, nrow_local_send
    1805              :                ! Use the send_dist%row_indices() here (we are looping over the local rows we will send)
    1806     10333400 :                dest_p(i) = MOD(((send_row_indices(i) - 1)/nrow_block_dest), dest_num_pe(1))
    1807              :             END DO
    1808     17426607 :             DO j = 1, ncol_local_send
    1809     17426607 :                dest_q(j) = MOD(((send_col_indices(j) - 1)/ncol_block_dest), dest_num_pe(2))
    1810              :             END DO
    1811              :             ! dest_p/q now contain the process row/column ID that will receive data from that row/column
    1812              : 
    1813      1313342 :             DO q = 0, dest_num_pe(2) - 1
    1814     17426607 :                ncols = COUNT(dest_q == q)
    1815      2264960 :                DO p = 0, dest_num_pe(1) - 1
    1816     15800320 :                   nrows = COUNT(dest_p == p)
    1817      1608289 :                   send_count(dest_blacs2mpi(p, q)) = nrows*ncols
    1818              :                END DO
    1819              :             END DO
    1820       656671 :             DEALLOCATE (dest_p, dest_q)
    1821              : 
    1822              :             ! Allocate the send buffer using send_count -- and calculate the offset into the buffer for each process
    1823      2921631 :             ALLOCATE (info%send_buf(SUM(send_count(:))))
    1824       656671 :             send_disp(0) = 0
    1825       951618 :             DO k = 1, recv_size - 1
    1826       951618 :                send_disp(k) = send_disp(k - 1) + send_count(k - 1)
    1827              :             END DO
    1828              : 
    1829              :             ! Loop over the smat, pack the send buffers
    1830       656671 :             send_count(:) = 0
    1831     17426607 :             DO j = 1, ncol_local_send
    1832              :                ! Use send_col_indices and row_indices here, as we are looking up the global row/column number of local rows.
    1833     16769936 :                dest_q_j = MOD(((send_col_indices(j) - 1)/ncol_block_dest), dest_num_pe(2))
    1834    529383379 :                DO i = 1, nrow_local_send
    1835    511956772 :                   dest_p_i = MOD(((send_row_indices(i) - 1)/nrow_block_dest), dest_num_pe(1))
    1836    511956772 :                   mpi_rank = dest_blacs2mpi(dest_p_i, dest_q_j)
    1837    511956772 :                   send_count(mpi_rank) = send_count(mpi_rank) + 1
    1838    528726708 :                   info%send_buf(send_disp(mpi_rank) + send_count(mpi_rank)) = source%local_data(i, j)
    1839              :                END DO
    1840              :             END DO
    1841              : 
    1842              :             ! For each non-zero send_count, call mpi_isend
    1843      1608289 :             DO k = 0, recv_size - 1
    1844      1608289 :                IF (send_count(k) > 0) THEN
    1845              :                   CALL para_env%isend(info%send_buf(send_disp(k) + 1:send_disp(k) + send_count(k)), &
    1846       748198 :                                       dest2global(k), info%send_request(k))
    1847              :                END IF
    1848              :             END DO
    1849       656671 :             DEALLOCATE (send_count, send_disp, dest2global)
    1850              :          END IF ! ASSOCIATED(send_dist)
    1851       747632 :          DEALLOCATE (dest_blacs2mpi)
    1852              : 
    1853              :       END IF !IF (.NOT. cp2k_is_parallel)
    1854              : 
    1855       747632 :       CALL timestop(handle)
    1856              : 
    1857      2990528 :    END SUBROUTINE cp_fm_start_copy_general
    1858              : 
    1859              : ! **************************************************************************************************
    1860              : !> \brief Completes the copy operation: wait for comms, unpack, clean up MPI state
    1861              : !> \param destination output fm matrix
    1862              : !> \param info all of the data that will be needed to complete the copy operation
    1863              : ! **************************************************************************************************
    1864       566770 :    SUBROUTINE cp_fm_finish_copy_general(destination, info)
    1865              :       TYPE(cp_fm_type), INTENT(IN)                       :: destination
    1866              :       TYPE(copy_info_type), INTENT(INOUT)                :: info
    1867              : 
    1868              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_finish_copy_general'
    1869              : 
    1870              :       INTEGER                                            :: handle, i, j, k, mpi_rank, send_size, &
    1871              :                                                             src_p_i, src_q_j
    1872       566770 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: recv_count
    1873              :       INTEGER, DIMENSION(2)                              :: nblock_src, nlocal_recv, src_num_pe
    1874       566770 :       INTEGER, DIMENSION(:), POINTER                     :: recv_col_indices, recv_row_indices
    1875              : 
    1876       566770 :       CALL timeset(routineN, handle)
    1877              : 
    1878              :       IF (.NOT. cp2k_is_parallel) THEN
    1879              :          ! Now unpack the data from the 'send buffer'
    1880              :          k = 0
    1881              :          DO j = 1, SIZE(destination%local_data, 2)
    1882              :             DO i = 1, SIZE(destination%local_data, 1)
    1883              :                k = k + 1
    1884              :                destination%local_data(i, j) = info%send_buf(k)
    1885              :             END DO
    1886              :          END DO
    1887              :          DEALLOCATE (info%send_buf)
    1888              :       ELSE
    1889              :          ! Set up local variables ...
    1890       566770 :          send_size = info%send_size
    1891      1700310 :          nlocal_recv(1:2) = info%nlocal_recv(:)
    1892      1700310 :          nblock_src(1:2) = info%nblock_src(:)
    1893      1700310 :          src_num_pe(1:2) = info%src_num_pe(:)
    1894       566770 :          recv_col_indices => info%recv_col_indices
    1895       566770 :          recv_row_indices => info%recv_row_indices
    1896              : 
    1897              :          ! ... use the local variables to do the work
    1898              :          ! DEST_2
    1899       566770 :          CALL mp_waitall(info%recv_request(:))
    1900      1700310 :          ALLOCATE (recv_count(0:send_size - 1))
    1901              :          ! Loop over the rmat, filling it in with data from the recv buffers
    1902              :          ! (here the block sizes, num_pes refer to the distribution of the source matrix)
    1903       566770 :          recv_count(:) = 0
    1904     15081274 :          DO j = 1, nlocal_recv(2)
    1905     14514504 :             src_q_j = MOD(((recv_col_indices(j) - 1)/nblock_src(2)), src_num_pe(2))
    1906    527038046 :             DO i = 1, nlocal_recv(1)
    1907    511956772 :                src_p_i = MOD(((recv_row_indices(i) - 1)/nblock_src(1)), src_num_pe(1))
    1908    511956772 :                mpi_rank = info%src_blacs2mpi(src_p_i, src_q_j)
    1909    511956772 :                recv_count(mpi_rank) = recv_count(mpi_rank) + 1
    1910    526471276 :                destination%local_data(i, j) = info%recv_buf(info%recv_disp(mpi_rank) + recv_count(mpi_rank))
    1911              :             END DO
    1912              :          END DO
    1913       566770 :          DEALLOCATE (recv_count, info%recv_disp, info%recv_request, info%recv_buf, info%src_blacs2mpi)
    1914              :          ! Invalidate the stored state
    1915              :          NULLIFY (info%recv_col_indices, &
    1916       566770 :                   info%recv_row_indices)
    1917              : 
    1918              :       END IF
    1919              : 
    1920       566770 :       CALL timestop(handle)
    1921              : 
    1922       566770 :    END SUBROUTINE cp_fm_finish_copy_general
    1923              : 
    1924              : ! **************************************************************************************************
    1925              : !> \brief Completes the copy operation: wait for comms clean up MPI state
    1926              : !> \param info all of the data that will be needed to complete the copy operation
    1927              : ! **************************************************************************************************
    1928       655975 :    SUBROUTINE cp_fm_cleanup_copy_general(info)
    1929              :       TYPE(copy_info_type), INTENT(INOUT)                :: info
    1930              : 
    1931              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_cleanup_copy_general'
    1932              : 
    1933              :       INTEGER                                            :: handle
    1934              : 
    1935       655975 :       CALL timeset(routineN, handle)
    1936              : 
    1937              :       IF (.NOT. cp2k_is_parallel) THEN
    1938              :          ! Don't do anything - no MPI state for the serial case
    1939              :       ELSE
    1940              :          ! SRC_2
    1941              :          ! If this process is also in the destination decomposition, this deallocate
    1942              :          ! Was already done in cp_fm_finish_copy_general
    1943       655975 :          IF (ALLOCATED(info%src_blacs2mpi)) THEN
    1944       180166 :             DEALLOCATE (info%src_blacs2mpi)
    1945              :          END IF
    1946       655975 :          CALL mp_waitall(info%send_request)
    1947       655975 :          DEALLOCATE (info%send_request, info%send_buf)
    1948              : 
    1949              :       END IF
    1950              : 
    1951       655975 :       CALL timestop(handle)
    1952              : 
    1953       655975 :    END SUBROUTINE cp_fm_cleanup_copy_general
    1954              : 
    1955              : ! **************************************************************************************************
    1956              : !> \brief General copy of a submatrix of fm matrix to  a submatrix of another fm matrix.
    1957              : !>        The two matrices can have different contexts.
    1958              : !>
    1959              : !>        Summary of distribution routines for dense matrices
    1960              : !>        The following will copy A(iA:iA+M-1,jA:jA+N-1) to B(iB:iB+M-1,jB:jB+N-1):
    1961              : !>
    1962              : !>        call pdgemr2d(M,N,Aloc,iA,jA,descA,Bloc,iB,jB,descB,context)
    1963              : !>
    1964              : !>        A process that is not a part of the context of A should set descA(2)
    1965              : !>        to -1, and similarly for B.
    1966              : !>
    1967              : !> \param source          input fm matrix
    1968              : !> \param destination     output fm matrix
    1969              : !> \param nrows           number of rows of sub matrix to be copied
    1970              : !> \param ncols           number of cols of sub matrix to be copied
    1971              : !> \param s_firstrow      starting global row index of sub matrix in source
    1972              : !> \param s_firstcol      starting global col index of sub matrix in source
    1973              : !> \param d_firstrow      starting global row index of sub matrix in destination
    1974              : !> \param d_firstcol      starting global col index of sub matrix in destination
    1975              : !> \param global_context  process grid that covers all parts of either A or B.
    1976              : ! **************************************************************************************************
    1977        11579 :    SUBROUTINE cp_fm_to_fm_submat_general(source, &
    1978              :                                          destination, &
    1979              :                                          nrows, &
    1980              :                                          ncols, &
    1981              :                                          s_firstrow, &
    1982              :                                          s_firstcol, &
    1983              :                                          d_firstrow, &
    1984              :                                          d_firstcol, &
    1985              :                                          global_context)
    1986              : 
    1987              :       TYPE(cp_fm_type), INTENT(IN)                       :: source, destination
    1988              :       INTEGER, INTENT(IN)                                :: nrows, ncols, s_firstrow, s_firstcol, &
    1989              :                                                             d_firstrow, d_firstcol
    1990              : 
    1991              :       CLASS(cp_blacs_type), INTENT(IN)        :: global_context
    1992              : 
    1993              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_to_fm_submat_general'
    1994              : 
    1995              :       LOGICAL                                  :: debug
    1996              :       INTEGER                                  :: handle
    1997              : #if defined(__parallel)
    1998              :       INTEGER, DIMENSION(9)                    :: desca, descb
    1999              :       REAL(KIND=dp), DIMENSION(1, 1), TARGET   :: dummy
    2000        11579 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: smat, dmat
    2001              : #endif
    2002              : 
    2003        11579 :       CALL timeset(routineN, handle)
    2004              : 
    2005        11579 :       debug = debug_this_module
    2006              : 
    2007              :       IF (.NOT. cp2k_is_parallel) THEN
    2008              :          CALL cp_fm_to_fm_submat(source, &
    2009              :                                  destination, &
    2010              :                                  nrows, &
    2011              :                                  ncols, &
    2012              :                                  s_firstrow, &
    2013              :                                  s_firstcol, &
    2014              :                                  d_firstrow, &
    2015              :                                  d_firstcol)
    2016              :       ELSE
    2017              : #ifdef __parallel
    2018              :          NULLIFY (smat, dmat)
    2019              :          ! check whether source is available on this process
    2020        11579 :          IF (ASSOCIATED(source%matrix_struct)) THEN
    2021       115790 :             desca = source%matrix_struct%descriptor
    2022        11579 :             IF (nrows > source%matrix_struct%nrow_global) THEN
    2023            0 :                CPABORT("nrows is greater than nrow_global of source")
    2024              :             END IF
    2025        11579 :             IF (ncols > source%matrix_struct%ncol_global) THEN
    2026            0 :                CPABORT("ncols is greater than ncol_global of source")
    2027              :             END IF
    2028        11579 :             smat => source%local_data
    2029              :          ELSE
    2030            0 :             desca = -1
    2031            0 :             smat => dummy
    2032              :          END IF
    2033              :          ! check destination is available on this process
    2034        11579 :          IF (ASSOCIATED(destination%matrix_struct)) THEN
    2035       115790 :             descb = destination%matrix_struct%descriptor
    2036        11579 :             IF (nrows > destination%matrix_struct%nrow_global) THEN
    2037            0 :                CPABORT("nrows is greater than nrow_global of destination")
    2038              :             END IF
    2039        11579 :             IF (ncols > destination%matrix_struct%ncol_global) THEN
    2040            0 :                CPABORT("ncols is greater than ncol_global of destination")
    2041              :             END IF
    2042        11579 :             dmat => destination%local_data
    2043              :          ELSE
    2044            0 :             descb = -1
    2045            0 :             dmat => dummy
    2046              :          END IF
    2047              :          ! do copy
    2048              : 
    2049              :          CALL pdgemr2d(nrows, &
    2050              :                        ncols, &
    2051              :                        smat, &
    2052              :                        s_firstrow, &
    2053              :                        s_firstcol, &
    2054              :                        desca, &
    2055              :                        dmat, &
    2056              :                        d_firstrow, &
    2057              :                        d_firstcol, &
    2058              :                        descb, &
    2059        11579 :                        global_context%get_handle())
    2060              : #else
    2061              :          MARK_USED(global_context)
    2062              :          CPABORT("this subroutine only supports SCALAPACK")
    2063              : #endif
    2064              :       END IF
    2065              : 
    2066        11579 :       CALL timestop(handle)
    2067              : 
    2068        11579 :    END SUBROUTINE cp_fm_to_fm_submat_general
    2069              : 
    2070              : ! **************************************************************************************************
    2071              : !> \brief ...
    2072              : !> \param matrix ...
    2073              : !> \param irow_global ...
    2074              : !> \param icol_global ...
    2075              : !> \param alpha ...
    2076              : ! **************************************************************************************************
    2077          240 :    SUBROUTINE cp_fm_add_to_element(matrix, irow_global, icol_global, alpha)
    2078              : 
    2079              :       ! Add alpha to the matrix element specified by the global indices
    2080              :       ! irow_global and icol_global
    2081              : 
    2082              :       ! - Creation (05.05.06,MK)
    2083              : 
    2084              :       TYPE(cp_fm_type), INTENT(IN)          :: matrix
    2085              :       INTEGER, INTENT(IN)                      :: irow_global, icol_global
    2086              :       REAL(KIND=dp), INTENT(IN)                :: alpha
    2087              : 
    2088              :       INTEGER                                  :: mypcol, myprow, npcol, nprow
    2089          240 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: a
    2090              :       TYPE(cp_blacs_env_type), POINTER         :: context
    2091              : #if defined(__parallel)
    2092              :       INTEGER                                  :: icol_local, ipcol, iprow, &
    2093              :                                                   irow_local
    2094              :       INTEGER, DIMENSION(9)                    :: desca
    2095              : #endif
    2096              : 
    2097          240 :       context => matrix%matrix_struct%context
    2098              : 
    2099          240 :       myprow = context%mepos(1)
    2100          240 :       mypcol = context%mepos(2)
    2101              : 
    2102          240 :       nprow = context%num_pe(1)
    2103          240 :       npcol = context%num_pe(2)
    2104              : 
    2105          240 :       a => matrix%local_data
    2106              : 
    2107              : #if defined(__parallel)
    2108              : 
    2109         2400 :       desca(:) = matrix%matrix_struct%descriptor(:)
    2110              : 
    2111              :       CALL infog2l(irow_global, icol_global, desca, nprow, npcol, myprow, mypcol, &
    2112          240 :                    irow_local, icol_local, iprow, ipcol)
    2113              : 
    2114          240 :       IF ((iprow == myprow) .AND. (ipcol == mypcol)) THEN
    2115          120 :          a(irow_local, icol_local) = a(irow_local, icol_local) + alpha
    2116              :       END IF
    2117              : 
    2118              : #else
    2119              : 
    2120              :       a(irow_global, icol_global) = a(irow_global, icol_global) + alpha
    2121              : 
    2122              : #endif
    2123              : 
    2124          240 :    END SUBROUTINE cp_fm_add_to_element
    2125              : 
    2126              : ! **************************************************************************************************
    2127              : !> \brief ...
    2128              : !> \param fm ...
    2129              : !> \param unit ...
    2130              : ! **************************************************************************************************
    2131        82496 :    SUBROUTINE cp_fm_write_unformatted(fm, unit)
    2132              :       TYPE(cp_fm_type), INTENT(IN)             :: fm
    2133              :       INTEGER, INTENT(IN)                      :: unit
    2134              : 
    2135              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_write_unformatted'
    2136              : 
    2137              :       INTEGER                                  :: handle, j, max_block, &
    2138              :                                                   ncol_global, nrow_global
    2139              :       TYPE(mp_para_env_type), POINTER          :: para_env
    2140              : #if defined(__parallel)
    2141              :       INTEGER                                  :: i, i_block, icol_local, &
    2142              :                                                   in, info, ipcol, &
    2143              :                                                   iprow, irow_local, &
    2144              :                                                   mepos, &
    2145              :                                                   num_pe, rb, tag
    2146              :       INTEGER, DIMENSION(9)                    :: desc
    2147        82496 :       REAL(KIND=dp), DIMENSION(:), POINTER     :: vecbuf
    2148              :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: newdat
    2149              :       TYPE(cp_blacs_type) :: ictxt_loc
    2150              :       INTEGER, EXTERNAL                        :: numroc
    2151              : #endif
    2152              : 
    2153        82496 :       CALL timeset(routineN, handle)
    2154              :       CALL cp_fm_get_info(fm, nrow_global=nrow_global, ncol_global=ncol_global, ncol_block=max_block, &
    2155        82496 :                           para_env=para_env)
    2156              : 
    2157              : #if defined(__parallel)
    2158        82496 :       num_pe = para_env%num_pe
    2159        82496 :       mepos = para_env%mepos
    2160        82496 :       rb = nrow_global
    2161        82496 :       tag = 0
    2162              :       ! get a new context
    2163        82496 :       CALL ictxt_loc%gridinit(para_env, 'R', 1, num_pe)
    2164        82496 :       CALL descinit(desc, nrow_global, ncol_global, rb, max_block, 0, 0, ictxt_loc%get_handle(), nrow_global, info)
    2165        82496 :       CPASSERT(info == 0)
    2166              :       ASSOCIATE (nprow => ictxt_loc%num_pe(1), npcol => ictxt_loc%num_pe(2), &
    2167              :                  myprow => ictxt_loc%mepos(1), mypcol => ictxt_loc%mepos(2))
    2168       164992 :          in = numroc(ncol_global, max_block, mypcol, 0, npcol)
    2169              : 
    2170       329984 :          ALLOCATE (newdat(nrow_global, MAX(1, in)))
    2171              : 
    2172              :          ! do the actual scalapack to cols reordering
    2173              :          CALL pdgemr2d(nrow_global, ncol_global, fm%local_data, 1, 1, &
    2174              :                        fm%matrix_struct%descriptor, &
    2175        82496 :                        newdat, 1, 1, desc, ictxt_loc%get_handle())
    2176              : 
    2177       247488 :          ALLOCATE (vecbuf(nrow_global*max_block))
    2178     30451326 :          vecbuf = HUGE(1.0_dp) ! init for valgrind
    2179              : 
    2180       359851 :          DO i = 1, ncol_global, MAX(max_block, 1)
    2181       194859 :             i_block = MIN(max_block, ncol_global - i + 1)
    2182              :             CALL infog2l(1, i, desc, nprow, npcol, myprow, mypcol, &
    2183       194859 :                          irow_local, icol_local, iprow, ipcol)
    2184       194859 :             IF (ipcol == mypcol) THEN
    2185       716301 :                DO j = 1, i_block
    2186     77838719 :                   vecbuf((j - 1)*nrow_global + 1:nrow_global*j) = newdat(:, icol_local + j - 1)
    2187              :                END DO
    2188              :             END IF
    2189              : 
    2190       194859 :             IF (ipcol == 0) THEN
    2191              :                ! do nothing
    2192              :             ELSE
    2193        66134 :                IF (ipcol == mypcol) THEN
    2194     17111747 :                   CALL para_env%send(vecbuf(:), 0, tag)
    2195              :                END IF
    2196        66134 :                IF (mypcol == 0) THEN
    2197     34190427 :                   CALL para_env%recv(vecbuf(:), ipcol, tag)
    2198              :                END IF
    2199              :             END IF
    2200              : 
    2201       277355 :             IF (unit > 0) THEN
    2202       713589 :                DO j = 1, i_block
    2203     39247782 :                   WRITE (unit) vecbuf((j - 1)*nrow_global + 1:nrow_global*j)
    2204              :                END DO
    2205              :             END IF
    2206              : 
    2207              :          END DO
    2208              :       END ASSOCIATE
    2209        82496 :       DEALLOCATE (vecbuf)
    2210              : 
    2211        82496 :       CALL ictxt_loc%gridexit()
    2212              : 
    2213        82496 :       DEALLOCATE (newdat)
    2214              : 
    2215              : #else
    2216              : 
    2217              :       IF (unit > 0) THEN
    2218              :          DO j = 1, ncol_global
    2219              :             WRITE (unit) fm%local_data(:, j)
    2220              :          END DO
    2221              :       END IF
    2222              : 
    2223              : #endif
    2224        82496 :       CALL timestop(handle)
    2225              : 
    2226       412480 :    END SUBROUTINE cp_fm_write_unformatted
    2227              : 
    2228              : ! **************************************************************************************************
    2229              : !> \brief Write out a full matrix in plain text.
    2230              : !> \param fm the matrix to be outputted
    2231              : !> \param unit the unit number for I/O
    2232              : !> \param header optional header
    2233              : !> \param value_format ...
    2234              : ! **************************************************************************************************
    2235         1378 :    SUBROUTINE cp_fm_write_formatted(fm, unit, header, value_format)
    2236              :       TYPE(cp_fm_type), INTENT(IN)             :: fm
    2237              :       INTEGER, INTENT(IN)                      :: unit
    2238              :       CHARACTER(LEN=*), INTENT(IN), OPTIONAL   :: header, value_format
    2239              : 
    2240              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_write_formatted'
    2241              : 
    2242              :       CHARACTER(LEN=21)                        :: my_value_format
    2243              :       INTEGER                                  :: handle, i, j, max_block, &
    2244              :                                                   ncol_global, nrow_global
    2245              :       TYPE(mp_para_env_type), POINTER          :: para_env
    2246              : #if defined(__parallel)
    2247              :       INTEGER                                  :: i_block, icol_local, &
    2248              :                                                   in, info, ipcol, &
    2249              :                                                   iprow, irow_local, &
    2250              :                                                   mepos, num_pe, rb, tag, k, &
    2251              :                                                   icol, irow
    2252              :       INTEGER, DIMENSION(9)                    :: desc
    2253         1378 :       REAL(KIND=dp), DIMENSION(:), POINTER     :: vecbuf
    2254         1378 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: newdat
    2255              :       TYPE(cp_blacs_type) :: ictxt_loc
    2256              :       INTEGER, EXTERNAL                        :: numroc
    2257              : #endif
    2258              : 
    2259         1378 :       CALL timeset(routineN, handle)
    2260              :       CALL cp_fm_get_info(fm, nrow_global=nrow_global, ncol_global=ncol_global, ncol_block=max_block, &
    2261         1378 :                           para_env=para_env)
    2262              : 
    2263         1378 :       IF (PRESENT(value_format)) THEN
    2264            0 :          CPASSERT(LEN_TRIM(ADJUSTL(value_format)) < 11)
    2265            0 :          my_value_format = "(I10, I10, "//TRIM(ADJUSTL(value_format))//")"
    2266              :       ELSE
    2267         1378 :          my_value_format = "(I10, I10, ES24.12)"
    2268              :       END IF
    2269              : 
    2270         1378 :       IF (unit > 0) THEN
    2271           11 :          IF (PRESENT(header)) WRITE (unit, *) header
    2272           11 :          WRITE (unit, "(A2, A8, A10, A24)") "#", "Row", "Column", ADJUSTL("Value")
    2273              :       END IF
    2274              : 
    2275              : #if defined(__parallel)
    2276         1378 :       num_pe = para_env%num_pe
    2277         1378 :       mepos = para_env%mepos
    2278         1378 :       rb = nrow_global
    2279         1378 :       tag = 0
    2280              :       ! get a new context
    2281         1378 :       CALL ictxt_loc%gridinit(para_env, 'R', 1, num_pe)
    2282         1378 :       CALL descinit(desc, nrow_global, ncol_global, rb, max_block, 0, 0, ictxt_loc%get_handle(), nrow_global, info)
    2283         1378 :       CPASSERT(info == 0)
    2284              :       ASSOCIATE (nprow => ictxt_loc%num_pe(1), npcol => ictxt_loc%num_pe(2), &
    2285              :                  myprow => ictxt_loc%mepos(1), mypcol => ictxt_loc%mepos(2))
    2286         2756 :          in = numroc(ncol_global, max_block, mypcol, 0, npcol)
    2287              : 
    2288         5512 :          ALLOCATE (newdat(nrow_global, MAX(1, in)))
    2289              : 
    2290              :          ! do the actual scalapack to cols reordering
    2291              :          CALL pdgemr2d(nrow_global, ncol_global, fm%local_data, 1, 1, &
    2292              :                        fm%matrix_struct%descriptor, &
    2293         1378 :                        newdat, 1, 1, desc, ictxt_loc%get_handle())
    2294              : 
    2295         4134 :          ALLOCATE (vecbuf(nrow_global*max_block))
    2296        14136 :          vecbuf = HUGE(1.0_dp) ! init for valgrind
    2297         1378 :          irow = 1
    2298         1378 :          icol = 1
    2299              : 
    2300         5510 :          DO i = 1, ncol_global, MAX(max_block, 1)
    2301         2754 :             i_block = MIN(max_block, ncol_global - i + 1)
    2302              :             CALL infog2l(1, i, desc, nprow, npcol, myprow, mypcol, &
    2303         2754 :                          irow_local, icol_local, iprow, ipcol)
    2304         2754 :             IF (ipcol == mypcol) THEN
    2305         2922 :                DO j = 1, i_block
    2306        20802 :                   vecbuf((j - 1)*nrow_global + 1:nrow_global*j) = newdat(:, icol_local + j - 1)
    2307              :                END DO
    2308              :             END IF
    2309              : 
    2310         2754 :             IF (ipcol == 0) THEN
    2311              :                ! do nothing
    2312              :             ELSE
    2313         1370 :                IF (ipcol == mypcol) THEN
    2314         5019 :                   CALL para_env%send(vecbuf(:), 0, tag)
    2315              :                END IF
    2316         1370 :                IF (mypcol == 0) THEN
    2317         9353 :                   CALL para_env%recv(vecbuf(:), ipcol, tag)
    2318              :                END IF
    2319              :             END IF
    2320              : 
    2321         4132 :             IF (unit > 0) THEN
    2322          210 :                DO j = 1, i_block
    2323         6438 :                   DO k = (j - 1)*nrow_global + 1, nrow_global*j
    2324         6228 :                      WRITE (UNIT=unit, FMT=my_value_format) irow, icol, vecbuf(k)
    2325         6228 :                      irow = irow + 1
    2326         6417 :                      IF (irow > nrow_global) THEN
    2327          189 :                         irow = 1
    2328          189 :                         icol = icol + 1
    2329              :                      END IF
    2330              :                   END DO
    2331              :                END DO
    2332              :             END IF
    2333              : 
    2334              :          END DO
    2335              :       END ASSOCIATE
    2336         1378 :       DEALLOCATE (vecbuf)
    2337              : 
    2338         1378 :       CALL ictxt_loc%gridexit()
    2339              : 
    2340         1378 :       DEALLOCATE (newdat)
    2341              : 
    2342              : #else
    2343              : 
    2344              :       IF (unit > 0) THEN
    2345              :          DO j = 1, ncol_global
    2346              :             DO i = 1, nrow_global
    2347              :                WRITE (UNIT=unit, FMT=my_value_format) i, j, fm%local_data(i, j)
    2348              :             END DO
    2349              :          END DO
    2350              :       END IF
    2351              : 
    2352              : #endif
    2353         1378 :       CALL timestop(handle)
    2354              : 
    2355         6890 :    END SUBROUTINE cp_fm_write_formatted
    2356              : 
    2357              : ! **************************************************************************************************
    2358              : !> \brief ...
    2359              : !> \param fm ...
    2360              : !> \param unit ...
    2361              : ! **************************************************************************************************
    2362         1974 :    SUBROUTINE cp_fm_read_unformatted(fm, unit)
    2363              :       TYPE(cp_fm_type), INTENT(INOUT)          :: fm
    2364              :       INTEGER, INTENT(IN)                      :: unit
    2365              : 
    2366              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_read_unformatted'
    2367              : 
    2368              :       INTEGER                                  :: handle, j, max_block, &
    2369              :                                                   ncol_global, nrow_global
    2370              :       TYPE(mp_para_env_type), POINTER          :: para_env
    2371              : #if defined(__parallel)
    2372              :       INTEGER                                  :: k, n_cols
    2373              :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: vecbuf
    2374              : #endif
    2375              : 
    2376         1974 :       CALL timeset(routineN, handle)
    2377              : 
    2378              :       CALL cp_fm_get_info(fm, nrow_global=nrow_global, ncol_global=ncol_global, ncol_block=max_block, &
    2379         1974 :                           para_env=para_env)
    2380              : 
    2381              : #if defined(__parallel)
    2382              : 
    2383              :       ! the parallel case could be made more efficient (see cp_fm_write_unformatted)
    2384              : 
    2385         7896 :       ALLOCATE (vecbuf(nrow_global, max_block))
    2386              : 
    2387         6152 :       DO j = 1, ncol_global, max_block
    2388              : 
    2389         4178 :          n_cols = MIN(max_block, ncol_global - j + 1)
    2390         4178 :          IF (para_env%mepos == 0) THEN
    2391        16918 :             DO k = 1, n_cols
    2392       368077 :                READ (unit) vecbuf(:, k)
    2393              :             END DO
    2394              :          END IF
    2395      1482498 :          CALL para_env%bcast(vecbuf, 0)
    2396         6152 :          CALL cp_fm_set_submatrix(fm, vecbuf, start_row=1, start_col=j, n_cols=n_cols)
    2397              : 
    2398              :       END DO
    2399              : 
    2400         1974 :       DEALLOCATE (vecbuf)
    2401              : 
    2402              : #else
    2403              : 
    2404              :       DO j = 1, ncol_global
    2405              :          READ (unit) fm%local_data(:, j)
    2406              :       END DO
    2407              : 
    2408              : #endif
    2409              : 
    2410         1974 :       CALL timestop(handle)
    2411              : 
    2412         1974 :    END SUBROUTINE cp_fm_read_unformatted
    2413              : 
    2414              : ! **************************************************************************************************
    2415              : !> \brief ...
    2416              : !> \param mm_type ...
    2417              : ! **************************************************************************************************
    2418        11087 :    SUBROUTINE cp_fm_setup(mm_type)
    2419              :       INTEGER, INTENT(IN)                                :: mm_type
    2420              : 
    2421        11087 :       cp_fm_mm_type = mm_type
    2422        11087 :    END SUBROUTINE cp_fm_setup
    2423              : 
    2424              : ! **************************************************************************************************
    2425              : !> \brief ...
    2426              : !> \return ...
    2427              : ! **************************************************************************************************
    2428      2284050 :    FUNCTION cp_fm_get_mm_type() RESULT(res)
    2429              :       INTEGER                                            :: res
    2430              : 
    2431      2284050 :       res = cp_fm_mm_type
    2432      2284050 :    END FUNCTION cp_fm_get_mm_type
    2433              : 
    2434              : ! **************************************************************************************************
    2435              : !> \brief ...
    2436              : !> \param ictxt ...
    2437              : !> \param prec ...
    2438              : !> \return ...
    2439              : ! **************************************************************************************************
    2440           10 :    FUNCTION cp_fm_pilaenv(ictxt, prec) RESULT(res)
    2441              :       INTEGER                                            :: ictxt
    2442              :       CHARACTER(LEN=1)                                   :: prec
    2443              :       INTEGER                                            :: res
    2444              : #if defined(__parallel)
    2445              :       INTEGER                                            :: pilaenv
    2446           10 :       res = pilaenv(ictxt, prec)
    2447              : #else
    2448              :       MARK_USED(ictxt)
    2449              :       MARK_USED(prec)
    2450              :       res = -1
    2451              : #endif
    2452              : 
    2453           10 :    END FUNCTION cp_fm_pilaenv
    2454              : 
    2455            0 : END MODULE cp_fm_types
        

Generated by: LCOV version 2.0-1