LCOV - code coverage report
Current view: top level - src - cp_dbcsr_redistribution.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:123285f) Lines: 98.3 % 286 281
Test Date: 2026-11-04 00:58:17 Functions: 100.0 % 4 4

            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 Redistribution of DBCSR blocks and distributed matrix slices.
      10              : ! **************************************************************************************************
      11              : MODULE cp_dbcsr_redistribution
      12              :    USE cp_dbcsr_api,                    ONLY: &
      13              :         dbcsr_clear, dbcsr_filter, dbcsr_finalize, dbcsr_get_block_p, dbcsr_get_info, &
      14              :         dbcsr_get_readonly_block_p, dbcsr_get_stored_coordinates, dbcsr_iterator_blocks_left, &
      15              :         dbcsr_iterator_next_block, dbcsr_iterator_readonly_start, dbcsr_iterator_start, &
      16              :         dbcsr_iterator_stop, dbcsr_iterator_type, dbcsr_p_type, dbcsr_reserve_blocks, dbcsr_set, &
      17              :         dbcsr_type, dbcsr_type_no_symmetry
      18              :    USE cp_dbcsr_contrib,                ONLY: dbcsr_reserve_all_blocks
      19              :    USE cp_dbcsr_operations,             ONLY: cp_dbcsr_m_by_n_from_template
      20              :    USE kinds,                           ONLY: dp,&
      21              :                                               int_8
      22              :    USE matrix_communication,            ONLY: allocate_matrix_buffers,&
      23              :                                               exchange_matrix_buffers,&
      24              :                                               matrix_buffer_type
      25              :    USE message_passing,                 ONLY: mp_comm_type,&
      26              :                                               mp_para_env_type
      27              :    USE util,                            ONLY: sort
      28              : #include "./base/base_uses.f90"
      29              : 
      30              :    IMPLICIT NONE
      31              :    PRIVATE
      32              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_dbcsr_redistribution'
      33              :    PUBLIC :: cp_dbcsr_to_subgroups, cp_dbcsr_from_subgroups, cp_array_to_dbcsr_slices, cp_array_to_dbcsr_blocks
      34              : 
      35              : CONTAINS
      36              : 
      37              : ! **************************************************************************************************
      38              : !> \brief ...
      39              : !> \param mat_global ...
      40              : !> \param mat_local ...
      41              : !> \param para_env ...
      42              : !> \param num_pe_sub ...
      43              : !> \param block_row_ranges ...
      44              : ! **************************************************************************************************
      45        12744 :    SUBROUTINE cp_dbcsr_to_subgroups(mat_global, mat_local, para_env, num_pe_sub, block_row_ranges)
      46              :       TYPE(dbcsr_type)                                   :: mat_global, mat_local
      47              :       TYPE(mp_para_env_type), INTENT(IN)                 :: para_env
      48              :       INTEGER                                            :: num_pe_sub
      49              :       INTEGER, DIMENSION(:, :), OPTIONAL                 :: block_row_ranges
      50              : 
      51              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_dbcsr_to_subgroups'
      52              : 
      53              :       INTEGER :: block_offset, block_size, col, col_size, handle, handle2, i_block, igroup, imep, &
      54              :          imep_sub, msg_offset, ngroup, offset, row, row_size
      55              :       INTEGER, ALLOCATABLE                               :: counts_rec(:, :), counts_send(:, :)
      56        12744 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: blk_counter, entry_counter
      57              :       LOGICAL                                            :: found
      58        12744 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: data_block
      59              :       TYPE(dbcsr_iterator_type)                          :: iter
      60        12744 :       TYPE(matrix_buffer_type)                           :: buffer_rec, buffer_send
      61              : 
      62        12744 :       CALL timeset(routineN, handle)
      63              : 
      64        12744 :       CALL timeset(routineN//"_count", handle2)
      65              : 
      66        12744 :       NULLIFY (data_block)
      67              : 
      68        50976 :       ALLOCATE (counts_send(2, 0:para_env%num_pe - 1), counts_rec(2, 0:para_env%num_pe - 1))
      69        12744 :       counts_send = 0
      70              : 
      71        12744 :       ngroup = para_env%num_pe/num_pe_sub
      72              : 
      73        12744 :       CALL dbcsr_iterator_start(iter, mat_global)
      74        41327 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
      75              : 
      76              :          CALL dbcsr_iterator_next_block(iter, row, col, data_block, &
      77        28583 :                                         row_size=row_size, col_size=col_size)
      78              : 
      79        28583 :          CALL dbcsr_get_stored_coordinates(mat_local, row, col, imep_sub)
      80              : 
      81        80413 :          DO igroup = 0, ngroup - 1
      82              : 
      83        39086 :             IF (PRESENT(block_row_ranges)) THEN
      84        18032 :                IF (row < block_row_ranges(1, igroup + 1) .OR. row > block_row_ranges(2, igroup + 1)) CYCLE
      85              :             END IF
      86        39086 :             imep = imep_sub + igroup*num_pe_sub
      87              : 
      88        39086 :             counts_send(1, imep) = counts_send(1, imep) + row_size*col_size
      89        67669 :             counts_send(2, imep) = counts_send(2, imep) + 1
      90              : 
      91              :          END DO
      92              : 
      93              :       END DO
      94              : 
      95        12744 :       CALL dbcsr_iterator_stop(iter)
      96              : 
      97        12744 :       CALL timestop(handle2)
      98              : 
      99        12744 :       CALL timeset(routineN//"_exchange_counts", handle2)
     100              : 
     101        12744 :       IF (para_env%num_pe > 1) THEN
     102        12744 :          CALL para_env%alltoall(counts_send, counts_rec, 2)
     103              :       ELSE
     104            0 :          counts_rec(:, :) = counts_send
     105              :       END IF
     106              : 
     107        12744 :       CALL timestop(handle2)
     108              : 
     109        12744 :       CALL timeset(routineN//"_pack", handle2)
     110              : 
     111              :       CALL allocate_matrix_buffers(counts_send(1, :), counts_rec(1, :), 3, buffer_send, buffer_rec, &
     112        12744 :                                    counts_send(2, :), counts_rec(2, :))
     113              : 
     114        38232 :       ALLOCATE (entry_counter(0:para_env%num_pe - 1))
     115        38232 :       entry_counter(:) = buffer_send%msg_offset(:para_env%num_pe - 1)
     116              : 
     117        25488 :       ALLOCATE (blk_counter(0:para_env%num_pe - 1))
     118        38232 :       blk_counter(:) = buffer_send%indx_offset(:para_env%num_pe - 1)
     119              : 
     120        12744 :       CALL dbcsr_iterator_start(iter, mat_global)
     121        41327 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
     122              : 
     123              :          CALL dbcsr_iterator_next_block(iter, row, col, data_block, &
     124        28583 :                                         row_size=row_size, col_size=col_size)
     125              : 
     126        28583 :          CALL dbcsr_get_stored_coordinates(mat_local, row, col, imep_sub)
     127              : 
     128        80413 :          DO igroup = 0, ngroup - 1
     129              : 
     130        39086 :             IF (PRESENT(block_row_ranges)) THEN
     131        18032 :                IF (row < block_row_ranges(1, igroup + 1) .OR. row > block_row_ranges(2, igroup + 1)) CYCLE
     132              :             END IF
     133              : 
     134        39086 :             imep = imep_sub + igroup*num_pe_sub
     135              : 
     136        39086 :             msg_offset = entry_counter(imep)
     137              : 
     138        39086 :             block_size = row_size*col_size
     139              : 
     140              :             buffer_send%msg(msg_offset + 1:msg_offset + block_size) = &
     141      2698120 :                RESHAPE(data_block(1:row_size, 1:col_size), [block_size])
     142              : 
     143        39086 :             entry_counter(imep) = entry_counter(imep) + block_size
     144              : 
     145        39086 :             blk_counter(imep) = blk_counter(imep) + 1
     146              : 
     147        39086 :             block_offset = blk_counter(imep)
     148              : 
     149              :             ! The payload offset in each record is relative to its peer's segment.
     150       184927 :             buffer_send%indx(:, block_offset) = [row, col, msg_offset - buffer_send%msg_offset(imep)]
     151              : 
     152              :          END DO
     153              : 
     154              :       END DO
     155              : 
     156        12744 :       CALL dbcsr_iterator_stop(iter)
     157              : 
     158        12744 :       CALL timestop(handle2)
     159              : 
     160        12744 :       CALL timeset(routineN//"_exchange_data", handle2)
     161              : 
     162        12744 :       CALL exchange_matrix_buffers(para_env, buffer_rec, buffer_send)
     163              : 
     164        12744 :       CALL timestop(handle2)
     165              : 
     166        12744 :       CALL timeset(routineN//"_reserve", handle2)
     167              : 
     168        12744 :       CALL dbcsr_set(mat_local, 0.0_dp)
     169        12744 :       CALL dbcsr_filter(mat_local, 1.0_dp)
     170        12744 :       CALL dbcsr_reserve_blocks(mat_local, rows=buffer_rec%indx(1, :), cols=buffer_rec%indx(2, :))
     171        12744 :       CALL dbcsr_finalize(mat_local)
     172        12744 :       CALL dbcsr_set(mat_local, 0.0_dp)
     173              : 
     174        12744 :       CALL timestop(handle2)
     175              : 
     176        12744 :       CALL timeset(routineN//"_unpack", handle2)
     177              : 
     178        38232 :       DO imep = 0, para_env%num_pe - 1
     179        77318 :          DO i_block = buffer_rec%indx_offset(imep) + 1, buffer_rec%indx_offset(imep + 1)
     180        39086 :             row = buffer_rec%indx(1, i_block)
     181        39086 :             col = buffer_rec%indx(2, i_block)
     182        39086 :             offset = buffer_rec%msg_offset(imep) + buffer_rec%indx(3, i_block)
     183              :             CALL dbcsr_get_block_p(mat_local, row, col, data_block, found=found, &
     184        39086 :                                    row_size=row_size, col_size=col_size)
     185        39086 :             CPASSERT(found)
     186              :             data_block(:, :) = RESHAPE(buffer_rec%msg(offset + 1:offset + row_size*col_size), &
     187       181832 :                                        [row_size, col_size])
     188              :          END DO
     189              :       END DO
     190              : 
     191        12744 :       CALL timestop(handle2)
     192              : 
     193        12744 :       CALL timestop(handle)
     194              : 
     195       114696 :    END SUBROUTINE cp_dbcsr_to_subgroups
     196              : 
     197              : ! **************************************************************************************************
     198              : !> \brief Sum subgroup blocks into the global layout, or copy mapped blocks.
     199              : !> \param mat_local ...
     200              : !> \param mat_global ...
     201              : !> \param para_env communicator covering all source and destination ranks
     202              : !> \param row_mapping optional source-to-destination block-row permutation
     203              : !> \param copy_only copy without summation, filtering, or consuming the source (default: false)
     204              : ! **************************************************************************************************
     205         7384 :    SUBROUTINE cp_dbcsr_from_subgroups(mat_local, mat_global, para_env, row_mapping, copy_only)
     206              : 
     207              :       TYPE(dbcsr_type)                                   :: mat_local, mat_global
     208              :       TYPE(mp_para_env_type), INTENT(IN)                 :: para_env
     209              :       INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL        :: row_mapping
     210              :       LOGICAL, INTENT(IN), OPTIONAL                      :: copy_only
     211              : 
     212              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_dbcsr_from_subgroups'
     213              : 
     214              :       INTEGER                                            :: block_size, col, col_size, handle, &
     215              :                                                             handle2, i_block, imep, mapped_row, &
     216              :                                                             offset, row, row_size
     217              :       INTEGER, ALLOCATABLE                               :: counts_rec(:, :), counts_send(:, :)
     218         7384 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: block_counter, entry_counter
     219              :       LOGICAL                                            :: found, my_copy_only
     220         7384 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: data_block
     221              :       TYPE(dbcsr_iterator_type)                          :: iter
     222         7384 :       TYPE(matrix_buffer_type)                           :: buffer_rec, buffer_send
     223              : 
     224         7384 :       CALL timeset(routineN, handle)
     225         7384 :       my_copy_only = .FALSE.
     226         7384 :       IF (PRESENT(copy_only)) my_copy_only = copy_only
     227              : 
     228         7384 :       CALL timeset(routineN//"_prepare", handle2)
     229              : 
     230         7384 :       IF (.NOT. my_copy_only) THEN
     231         7382 :          CALL dbcsr_clear(mat_global)
     232         7382 :          CALL dbcsr_reserve_all_blocks(mat_global)
     233              :       END IF
     234              : 
     235        29536 :       ALLOCATE (counts_send(2, 0:para_env%num_pe - 1), counts_rec(2, 0:para_env%num_pe - 1))
     236         7384 :       counts_send = 0
     237              : 
     238         7384 :       CALL dbcsr_iterator_readonly_start(iter, mat_local)
     239        22379 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
     240              : 
     241              :          CALL dbcsr_iterator_next_block(iter, row, col, data_block, &
     242        14995 :                                         row_size=row_size, col_size=col_size)
     243              : 
     244        14995 :          mapped_row = row
     245        14995 :          IF (PRESENT(row_mapping)) mapped_row = row_mapping(row)
     246        14995 :          CALL dbcsr_get_stored_coordinates(mat_global, mapped_row, col, imep)
     247              : 
     248        14995 :          counts_send(1, imep) = counts_send(1, imep) + row_size*col_size
     249        14995 :          counts_send(2, imep) = counts_send(2, imep) + 1
     250              : 
     251              :       END DO
     252              : 
     253         7384 :       CALL dbcsr_iterator_stop(iter)
     254              : 
     255         7384 :       CALL timestop(handle2)
     256              : 
     257         7384 :       CALL timeset(routineN//"_exchange_counts", handle2)
     258              : 
     259         7384 :       IF (para_env%num_pe > 1) THEN
     260         7384 :          CALL para_env%alltoall(counts_send, counts_rec, 2)
     261              :       ELSE
     262            0 :          counts_rec(:, :) = counts_send
     263              :       END IF
     264              : 
     265         7384 :       CALL timestop(handle2)
     266              : 
     267         7384 :       CALL timeset(routineN//"_pack", handle2)
     268              : 
     269              :       CALL allocate_matrix_buffers(counts_send(1, :), counts_rec(1, :), 3, buffer_send, buffer_rec, &
     270         7384 :                                    counts_send(2, :), counts_rec(2, :))
     271              : 
     272        22152 :       ALLOCATE (block_counter(0:para_env%num_pe - 1))
     273        22152 :       block_counter(:) = buffer_send%indx_offset(:para_env%num_pe - 1)
     274              : 
     275        14768 :       ALLOCATE (entry_counter(0:para_env%num_pe - 1))
     276        22152 :       entry_counter(:) = buffer_send%msg_offset(:para_env%num_pe - 1)
     277              : 
     278              :       ! fill buffer_send
     279         7384 :       CALL dbcsr_iterator_readonly_start(iter, mat_local)
     280        22379 :       DO WHILE (dbcsr_iterator_blocks_left(iter))
     281              : 
     282              :          CALL dbcsr_iterator_next_block(iter, row, col, data_block, &
     283        14995 :                                         row_size=row_size, col_size=col_size)
     284              : 
     285        14995 :          mapped_row = row
     286        14995 :          IF (PRESENT(row_mapping)) mapped_row = row_mapping(row)
     287        14995 :          CALL dbcsr_get_stored_coordinates(mat_global, mapped_row, col, imep)
     288              : 
     289        14995 :          block_size = row_size*col_size
     290              : 
     291        14995 :          offset = entry_counter(imep)
     292              : 
     293              :          buffer_send%msg(offset + 1:offset + block_size) = &
     294      1832702 :             RESHAPE(data_block(1:row_size, 1:col_size), [block_size])
     295              : 
     296        14995 :          i_block = block_counter(imep) + 1
     297              : 
     298              :          ! The receiver adds its own segment start to this peer-relative offset.
     299        59980 :          buffer_send%indx(:, i_block) = [mapped_row, col, offset - buffer_send%msg_offset(imep)]
     300              : 
     301        14995 :          entry_counter(imep) = entry_counter(imep) + block_size
     302              : 
     303        14995 :          block_counter(imep) = block_counter(imep) + 1
     304              : 
     305              :       END DO
     306              : 
     307         7384 :       CALL dbcsr_iterator_stop(iter)
     308              : 
     309         7384 :       CALL timestop(handle2)
     310              : 
     311         7384 :       CALL timeset(routineN//"_exchange_data", handle2)
     312              : 
     313         7384 :       CALL exchange_matrix_buffers(para_env, buffer_rec, buffer_send)
     314              : 
     315         7384 :       CALL timestop(handle2)
     316              : 
     317         7384 :       CALL timeset(routineN//"_unpack", handle2)
     318              : 
     319              :       ! Keep the rank and receive-buffer order of the block-wise accumulation.
     320        22152 :       DO imep = 0, para_env%num_pe - 1
     321        37147 :          DO i_block = buffer_rec%indx_offset(imep) + 1, buffer_rec%indx_offset(imep + 1)
     322        14995 :             row = buffer_rec%indx(1, i_block)
     323        14995 :             col = buffer_rec%indx(2, i_block)
     324        14995 :             offset = buffer_rec%msg_offset(imep) + buffer_rec%indx(3, i_block)
     325              :             CALL dbcsr_get_block_p(mat_global, row, col, data_block, found=found, &
     326        14995 :                                    row_size=row_size, col_size=col_size)
     327        14995 :             CPASSERT(found)
     328        44758 :             IF (my_copy_only) THEN
     329              :                data_block(:, :) = RESHAPE(buffer_rec%msg(offset + 1:offset + row_size*col_size), &
     330           24 :                                           [row_size, col_size])
     331              :             ELSE
     332              :                data_block(:, :) = data_block + &
     333              :                                   RESHAPE(buffer_rec%msg(offset + 1:offset + row_size*col_size), &
     334      1966126 :                                           [row_size, col_size])
     335              :             END IF
     336              :          END DO
     337              :       END DO
     338              : 
     339         7384 :       IF (.NOT. my_copy_only) THEN
     340              :          ! Remove numerically zero blocks from the summed matrix.
     341         7382 :          CALL dbcsr_filter(mat_global, 1.0E-30_dp)
     342              :       END IF
     343              : 
     344              :       IF (.NOT. my_copy_only) THEN
     345         7382 :          CALL dbcsr_set(mat_local, 0.0_dp)
     346         7382 :          CALL dbcsr_filter(mat_local, 1.0_dp)
     347              :       END IF
     348              : 
     349         7384 :       CALL timestop(handle2)
     350              : 
     351         7384 :       CALL timestop(handle)
     352              : 
     353        66456 :    END SUBROUTINE cp_dbcsr_from_subgroups
     354              : 
     355              : ! **************************************************************************************************
     356              : !> \brief Consume a distributed flattened array and create a DBCSR matrix per slice.
     357              : !> \param array local flattened rows; all ranks have the same number of slices; deallocated on return
     358              : !> \param nrows number of rows in each output matrix
     359              : !> \param ncols number of columns in each output matrix
     360              : !> \param para_env_sub communicator for the output matrices
     361              : !> \param first_row first flattened row held locally
     362              : !> \param last_row last flattened row held locally
     363              : !> \param template DBCSR template defining output distribution and block sizes
     364              : !> \param matrices preallocated array of output matrices
     365              : !> \param matrix_ptrs preallocated array of output pointers; pointees are allocated here
     366              : !> \note Supply exactly one output representation. Flattened rows use (i-1)*ncols+j.
     367              : ! **************************************************************************************************
     368          360 :    SUBROUTINE cp_array_to_dbcsr_slices(array, nrows, ncols, para_env_sub, &
     369          360 :                                        first_row, last_row, template, matrices, matrix_ptrs)
     370              :       REAL(KIND=dp), ALLOCATABLE, INTENT(INOUT), TARGET  :: array(:, :)
     371              :       INTEGER, INTENT(IN)                                :: nrows, ncols
     372              :       TYPE(mp_para_env_type), INTENT(IN), POINTER        :: para_env_sub
     373              :       INTEGER, INTENT(IN)                                :: first_row, last_row
     374              :       TYPE(dbcsr_type), INTENT(INOUT)                    :: template
     375              :       TYPE(dbcsr_type), INTENT(INOUT), OPTIONAL, TARGET  :: matrices(:)
     376              :       TYPE(dbcsr_p_type), INTENT(INOUT), OPTIONAL        :: matrix_ptrs(:)
     377              : 
     378              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_array_to_dbcsr_slices'
     379              : 
     380              :       INTEGER                                            :: handle, i, islice, j, nblock, nslice
     381          360 :       INTEGER, ALLOCATABLE                               :: blocks(:, :)
     382          360 :       INTEGER, POINTER                                   :: local_cols(:), local_rows(:)
     383          360 :       REAL(KIND=dp), POINTER                             :: values(:, :, :)
     384          360 :       TYPE(dbcsr_p_type), ALLOCATABLE                    :: outputs(:)
     385              : 
     386          360 :       CALL timeset(routineN, handle)
     387          360 :       CPASSERT(PRESENT(matrices) .NEQV. PRESENT(matrix_ptrs))
     388          360 :       nslice = SIZE(array, 2)
     389        17003 :       ALLOCATE (outputs(nslice))
     390        16283 :       DO islice = 1, nslice
     391        15923 :          IF (PRESENT(matrices)) THEN
     392           83 :             outputs(islice)%matrix => matrices(islice)
     393              :          ELSE
     394        15840 :             ALLOCATE (matrix_ptrs(islice)%matrix)
     395        15840 :             outputs(islice)%matrix => matrix_ptrs(islice)%matrix
     396              :          END IF
     397              :          CALL cp_dbcsr_m_by_n_from_template(outputs(islice)%matrix, template=template, &
     398        15923 :                                             m=nrows, n=ncols, sym=dbcsr_type_no_symmetry)
     399        15923 :          CALL dbcsr_reserve_all_blocks(outputs(islice)%matrix)
     400        16283 :          CALL dbcsr_finalize(outputs(islice)%matrix)
     401              :       END DO
     402          360 :       IF (nslice > 0) THEN
     403          360 :          CALL dbcsr_get_info(outputs(1)%matrix, local_rows=local_rows, local_cols=local_cols)
     404         1072 :          ALLOCATE (blocks(3, SIZE(local_rows)*SIZE(local_cols)*nslice))
     405          360 :          nblock = 0
     406        16283 :          DO islice = 1, nslice
     407        32206 :             DO j = 1, SIZE(local_cols)
     408        47051 :                DO i = 1, SIZE(local_rows)
     409        15205 :                   nblock = nblock + 1
     410        76743 :                   blocks(:, nblock) = [local_rows(i), local_cols(j), islice]
     411              :                END DO
     412              :             END DO
     413              :          END DO
     414              :          ! A rank-remapped view preserves the row-major flattened ia convention without copying.
     415          360 :          values(1:SIZE(array, 1), 1:1, 1:nslice) => array
     416              :          CALL cp_array_to_dbcsr_blocks(values, first_row, last_row, para_env_sub, outputs, blocks, &
     417          360 :                                        row_major=.TRUE.)
     418              :       END IF
     419          360 :       DEALLOCATE (array)
     420          360 :       CALL timestop(handle)
     421          720 :    END SUBROUTINE cp_array_to_dbcsr_slices
     422              : 
     423              : ! **************************************************************************************************
     424              : !> \brief Fill existing DBCSR blocks directly from distributed flattened array slices.
     425              : !>        Requests split contiguous block fragments at source-rank boundaries. Sources combine
     426              : !>        components before replying, so each target element transfers only one real value.
     427              : !>        Slice tiles bound request/reply storage; existing sparsity is preserved.
     428              : !> \param array local flattened entries, components, slices; borrowed, never deallocated
     429              : !> \param first_entry first flattened entry held locally
     430              : !> \param last_entry last flattened entry held locally; may precede first_entry on empty ranks
     431              : !> \param comm communicator covering sources and output matrices, in any source-range order
     432              : !> \param matrices existing output slices with identical dimensions and block sizes
     433              : !> \param blocks requested (block row, block column, slice) columns; absent local blocks are skipped
     434              : !> \param factors optional integer component multiplicities per requested block; default one component
     435              : !> \param row_major flatten with column index fastest; default Fortran column-major ordering
     436              : !> \note Source ranges must partition the whole flattened matrix. Requested blocks must be unique.
     437              : !>       Unrequested output entries are zeroed. The 64 MiB workspace target includes the borrowed
     438              : !>       array and descriptors; an irreducible single-slice exchange may exceed this target.
     439              : ! **************************************************************************************************
     440          360 :    SUBROUTINE cp_array_to_dbcsr_blocks(array, first_entry, last_entry, comm, matrices, blocks, factors, row_major)
     441              :       REAL(KIND=dp), INTENT(IN)                          :: array(:, :, :)
     442              :       INTEGER, INTENT(IN)                                :: first_entry, last_entry, blocks(:, :)
     443              : 
     444              :       CLASS(mp_comm_type), INTENT(IN)                    :: comm
     445              :       TYPE(dbcsr_p_type), INTENT(INOUT)                  :: matrices(:)
     446              :       INTEGER, INTENT(IN), OPTIONAL                      :: factors(:, :)
     447              :       LOGICAL, INTENT(IN), OPTIONAL                      :: row_major
     448              : 
     449              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_array_to_dbcsr_blocks'
     450              : 
     451              :       INTEGER(KIND=int_8), PARAMETER                     :: max_bytes = 64_int_8*1024**2
     452              : 
     453              :       INTEGER :: a, b, col, component, element, first, handle, i, image, last, length, lo, &
     454              :                  lower, middle, nc, nfast, nprocs, pass, peer, pos, rank, record, row, upper, width
     455              :       INTEGER(KIND=int_8)                                :: fixed_bytes, nel
     456          360 :       INTEGER, ALLOCATABLE                               :: counter(:), counts_rec(:, :), &
     457          360 :                                                             counts_send(:, :), ends(:), order(:), &
     458          360 :                                                             ranges(:, :), targets(:, :)
     459          360 :       INTEGER, POINTER                                   :: fast_offset(:), fast_size(:), &
     460          360 :                                                             slow_offset(:), slow_size(:)
     461              :       LOGICAL                                            :: found, row_order
     462          360 :       REAL(KIND=dp), POINTER                             :: block(:, :)
     463          360 :       TYPE(matrix_buffer_type)                           :: requests_rec, requests_send
     464              : 
     465          360 :       CALL timeset(routineN, handle)
     466          360 :       IF (SIZE(matrices) == 0) THEN
     467            0 :          CALL timestop(handle)
     468              :          RETURN
     469              :       END IF
     470          360 :       row_order = .FALSE.
     471          360 :       IF (PRESENT(row_major)) row_order = row_major
     472              :       CALL dbcsr_get_info(matrices(1)%matrix, row_blk_size=fast_size, col_blk_size=slow_size, &
     473          360 :                           row_blk_offset=fast_offset, col_blk_offset=slow_offset)
     474          360 :       IF (row_order) THEN
     475              :          CALL dbcsr_get_info(matrices(1)%matrix, row_blk_size=slow_size, col_blk_size=fast_size, &
     476          360 :                              row_blk_offset=slow_offset, col_blk_offset=fast_offset)
     477              :       END IF
     478          720 :       nfast = SUM(fast_size)
     479          720 :       nel = INT(nfast, int_8)*SUM(slow_size)
     480          360 :       nc = SIZE(array, 2)
     481          360 :       CPASSERT(nel > 0 .AND. nel < HUGE(element))
     482          360 :       CPASSERT(nc >= 1)
     483          360 :       CPASSERT(SIZE(array, 1) == MAX(0, last_entry - first_entry + 1))
     484          360 :       CPASSERT(SIZE(array, 3) == SIZE(matrices) .AND. SIZE(blocks, 1) == 3)
     485          360 :       IF (PRESENT(factors)) THEN
     486            0 :          CPASSERT(SIZE(factors, 1) == nc .AND. SIZE(factors, 2) == SIZE(blocks, 2))
     487              :       ELSE
     488          360 :          CPASSERT(nc == 1)
     489              :       END IF
     490          360 :       nprocs = comm%num_pe
     491              :       ALLOCATE (ends(nprocs), order(nprocs), ranges(2, nprocs), counts_send(2, 0:nprocs - 1), &
     492         3960 :                 counts_rec(2, 0:nprocs - 1), counter(0:nprocs - 1))
     493         1080 :       CALL comm%allgather([first_entry, last_entry], ranges)
     494          736 :       ends(:) = ranges(2, :)
     495          736 :       WHERE (ranges(2, :) < ranges(1, :)) ends = HUGE(element)
     496          360 :       CALL sort(ends, nprocs, order)
     497              :       ! Validate coverage, independently of communicator rank ordering and empty ranges.
     498          360 :       element = 0
     499          736 :       DO rank = 1, nprocs
     500          376 :          IF (ends(rank) == HUGE(element)) EXIT
     501          376 :          CPASSERT(ranges(1, order(rank)) == element + 1)
     502          736 :          element = ends(rank)
     503              :       END DO
     504          360 :       CPASSERT(INT(element, int_8) == nel)
     505         2160 :       fixed_bytes = 8_int_8*SIZE(array, KIND=int_8) + 4_int_8*SIZE(blocks, KIND=int_8) + 64_int_8*nprocs
     506          360 :       IF (PRESENT(factors)) fixed_bytes = fixed_bytes + 4_int_8*SIZE(factors, KIND=int_8)
     507              :       width = INT(MIN(INT(SIZE(matrices), int_8), &
     508          360 :                       MAX(1_int_8, (max_bytes - fixed_bytes)/((80_int_8 + 8*nc)*nel))))
     509          360 :       CALL comm%min(width)
     510        16283 :       DO image = 1, SIZE(matrices)
     511        16283 :          CALL dbcsr_set(matrices(image)%matrix, 0.0_dp)
     512              :       END DO
     513          360 :       DO first = 1, SIZE(matrices), width
     514          360 :          last = MIN(SIZE(matrices), first + width - 1)
     515          360 :          counts_send(:, :) = 0
     516         1080 :          DO pass = 1, 2
     517          720 :             IF (pass == 2) THEN
     518          360 :                CALL comm%alltoall(counts_send, counts_rec, 2)
     519              :                CALL allocate_matrix_buffers(counts_send(1, :), counts_rec(1, :), 3 + nc, requests_send, requests_rec, &
     520          360 :                                             send_blocks=counts_send(2, :), recv_blocks=counts_rec(2, :))
     521         1072 :                ALLOCATE (targets(3, SIZE(requests_send%indx, 2)))
     522          736 :                counter(:) = requests_send%indx_offset(:nprocs - 1)
     523              :             END IF
     524        31490 :             DO i = 1, SIZE(blocks, 2)
     525        30410 :                image = blocks(3, i)
     526        30410 :                IF (image < first .OR. image > last) CYCLE
     527        30410 :                CALL dbcsr_get_readonly_block_p(matrices(image)%matrix, blocks(1, i), blocks(2, i), block, found=found)
     528        30410 :                IF (.NOT. found) CYCLE
     529        30410 :                a = blocks(1, i)
     530        30410 :                b = blocks(2, i)
     531        30410 :                IF (row_order) THEN
     532        30410 :                   a = blocks(2, i)
     533        30410 :                   b = blocks(1, i)
     534              :                END IF
     535       174636 :                DO col = 1, slow_size(b)
     536              :                   row = 1
     537       257042 :                   DO WHILE (row <= fast_size(a))
     538       113536 :                      element = (slow_offset(b) + col - 2)*nfast + fast_offset(a) + row - 1
     539              :                      ! Find the first nonempty source range whose upper bound covers this entry.
     540       113536 :                      lower = 1
     541       113536 :                      upper = nprocs
     542       120600 :                      DO WHILE (lower < upper)
     543         7064 :                         middle = (lower + upper)/2
     544       120600 :                         IF (ends(middle) < element) THEN
     545         3532 :                            lower = middle + 1
     546              :                         ELSE
     547              :                            upper = middle
     548              :                         END IF
     549              :                      END DO
     550       113536 :                      peer = order(lower) - 1
     551       113536 :                      length = MIN(fast_size(a) - row + 1, ends(lower) - element + 1)
     552       113536 :                      IF (pass == 1) THEN
     553       170304 :                         counts_send(:, peer) = counts_send(:, peer) + [length, 1]
     554              :                      ELSE
     555        56768 :                         counter(peer) = counter(peer) + 1
     556        56768 :                         record = counter(peer)
     557       227072 :                         requests_send%indx(1:3, record) = [element, length, image]
     558       113536 :                         requests_send%indx(4:, record) = 1
     559        56768 :                         IF (PRESENT(factors)) requests_send%indx(4:, record) = factors(:, i)
     560       227072 :                         targets(:, record) = [i, row, col]
     561              :                      END IF
     562       113536 :                      row = row + length
     563              :                   END DO
     564              :                END DO
     565              :             END DO
     566              :          END DO
     567          360 :          CALL exchange_matrix_buffers(comm, requests_rec, requests_send, do_msg=.FALSE.)
     568          736 :          DO peer = 0, nprocs - 1
     569          376 :             pos = requests_rec%msg_offset(peer)
     570        57504 :             DO record = requests_rec%indx_offset(peer) + 1, requests_rec%indx_offset(peer + 1)
     571        56768 :                lo = requests_rec%indx(1, record) - first_entry + 1
     572        56768 :                length = requests_rec%indx(2, record)
     573        56768 :                image = requests_rec%indx(3, record)
     574        56768 :                CPASSERT(lo >= 1 .AND. lo + length - 1 <= SIZE(array, 1))
     575              :                requests_rec%msg(pos + 1:pos + length) = &
     576       948940 :                   REAL(requests_rec%indx(4, record), dp)*array(lo:lo + length - 1, 1, image)
     577        56768 :                DO component = 2, nc
     578              :                   requests_rec%msg(pos + 1:pos + length) = requests_rec%msg(pos + 1:pos + length) + &
     579        56768 :                                       REAL(requests_rec%indx(3 + component, record), dp)*array(lo:lo + length - 1, component, image)
     580              :                END DO
     581        57144 :                pos = pos + length
     582              :             END DO
     583              :          END DO
     584          360 :          CALL exchange_matrix_buffers(comm, requests_send, requests_rec, do_indx=.FALSE.)
     585          736 :          DO peer = 0, nprocs - 1
     586          376 :             pos = requests_send%msg_offset(peer)
     587        57504 :             DO record = requests_send%indx_offset(peer) + 1, requests_send%indx_offset(peer + 1)
     588        56768 :                i = targets(1, record)
     589        56768 :                row = targets(2, record)
     590        56768 :                col = targets(3, record)
     591        56768 :                length = requests_send%indx(2, record)
     592        56768 :                CALL dbcsr_get_block_p(matrices(blocks(3, i))%matrix, blocks(1, i), blocks(2, i), block, found=found)
     593        56768 :                CPASSERT(found)
     594        56768 :                IF (row_order) THEN
     595       948940 :                   block(col, row:row + length - 1) = requests_send%msg(pos + 1:pos + length)
     596              :                ELSE
     597            0 :                   block(row:row + length - 1, col) = requests_send%msg(pos + 1:pos + length)
     598              :                END IF
     599       113912 :                pos = pos + length
     600              :             END DO
     601              :          END DO
     602          360 :          DEALLOCATE (targets)
     603              :       END DO
     604          360 :       CALL timestop(handle)
     605          720 :    END SUBROUTINE cp_array_to_dbcsr_blocks
     606              : 
     607              : END MODULE cp_dbcsr_redistribution
        

Generated by: LCOV version 2.0-1