LCOV - code coverage report
Current view: top level - src - smeagol_matrix_utils.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:71c3ab0) Lines: 74.7 % 708 529
Test Date: 2026-07-25 06:35:44 Functions: 82.4 % 17 14

            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 Routines to convert sparse matrices between DBCSR (distributed-blocks compressed sparse rows)
      10              : !>        and SIESTA (distributed compressed sparse columns) formats.
      11              : !> \author Sergey Chulkov
      12              : !> \author Christian Ahart
      13              : !> \author Clotilde Cucinotta
      14              : ! **************************************************************************************************
      15              : MODULE smeagol_matrix_utils
      16              :    USE cell_types, ONLY: cell_type, &
      17              :                          real_to_scaled, &
      18              :                          scaled_to_real
      19              :    USE cp_dbcsr_api, ONLY: dbcsr_get_block_p, &
      20              :                            dbcsr_get_info, &
      21              :                            dbcsr_p_type, &
      22              :                            dbcsr_set
      23              :    USE kinds, ONLY: dp, &
      24              :                     dp_size, &
      25              :                     int_8
      26              :    USE message_passing, ONLY: mp_para_env_type, &
      27              :                               mp_request_type, &
      28              :                               mp_waitall
      29              :    USE negf_matrix_utils, ONLY: get_index_by_cell
      30              : #if defined(__SMEAGOL)
      31              :    USE parallel, ONLY: GetNodeOrbs, &
      32              :                        GlobalToLocalOrb, &
      33              :                        LocalToGlobalOrb, &
      34              :                        WhichNodeOrb
      35              : #endif
      36              :    USE particle_types, ONLY: particle_type
      37              :    USE qs_neighbor_list_types, ONLY: get_iterator_info, &
      38              :                                      get_neighbor_list_set_p, &
      39              :                                      neighbor_list_iterate, &
      40              :                                      neighbor_list_iterator_create, &
      41              :                                      neighbor_list_iterator_p_type, &
      42              :                                      neighbor_list_iterator_release, &
      43              :                                      neighbor_list_set_p_type
      44              :    USE qs_subsys_types, ONLY: qs_subsys_get, &
      45              :                               qs_subsys_type
      46              :    USE util, ONLY: sort
      47              : #include "./base/base_uses.f90"
      48              : 
      49              :    IMPLICIT NONE
      50              :    PRIVATE
      51              : 
      52              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'smeagol_matrix_utils'
      53              :    LOGICAL, PARAMETER, PRIVATE          :: debug_this_module = .FALSE.
      54              : 
      55              :    INTEGER, PARAMETER, PRIVATE          :: neighbor_list_iatom_index = 1
      56              :    INTEGER, PARAMETER, PRIVATE          :: neighbor_list_jatom_index = 2
      57              :    INTEGER, PARAMETER, PRIVATE          :: neighbor_list_dbcsr_image_index = 3
      58              :    INTEGER, PARAMETER, PRIVATE          :: neighbor_list_siesta_image_index = 4
      59              :    INTEGER, PARAMETER, PRIVATE          :: neighbor_list_siesta_transp_image_index = 5
      60              :    INTEGER, PARAMETER, PRIVATE          :: neighbor_list_dim1 = neighbor_list_siesta_transp_image_index
      61              : 
      62              :    PUBLIC :: siesta_distrib_csc_struct_type
      63              :    PUBLIC :: siesta_struct_create, siesta_struct_release
      64              :    PUBLIC :: convert_dbcsr_to_distributed_siesta, convert_distributed_siesta_to_dbcsr
      65              : 
      66              :    PRIVATE :: get_negf_cell_ijk, index_in_canonical_enumeration, number_from_canonical_enumeration, pbc_0_1
      67              :    PRIVATE :: get_number_of_mpi_sendrecv_requests, assign_nonzero_elements_to_requests
      68              : 
      69              :    !> number of DBCSR matrix elements to receive from a given rank
      70              :    INTEGER, PARAMETER, PRIVATE          :: nelements_dbcsr_recv = 1
      71              :    !> number of DBCSR matrix elements to send to a given rank
      72              :    INTEGER, PARAMETER, PRIVATE          :: nelements_dbcsr_send = 2
      73              :    INTEGER, PARAMETER, PRIVATE          :: nelements_dbcsr_dim2 = nelements_dbcsr_send
      74              : 
      75              :    ! 128 MiB (to limit memory usage for matrix redistribution)
      76              :    INTEGER(kind=int_8), PARAMETER, PRIVATE :: max_mpi_packet_size_bytes = 134217728
      77              :    INTEGER(kind=int_8), PARAMETER, PRIVATE :: max_mpi_packet_size_dp = max_mpi_packet_size_bytes/INT(dp_size, kind=int_8)
      78              : 
      79              :    ! a portable way to determine the upper bound for tag value is to call
      80              :    ! MPI_COMM_GET_ATTR(comm, MPI_TAG_UB, max_mpi_rank, flag, ierror).
      81              :    ! The MPI specification guarantees a value of 32767
      82              :    INTEGER, PARAMETER, PRIVATE          :: max_mpi_rank = 32767
      83              : 
      84              : ! **************************************************************************************************
      85              : !> \brief Sparsity pattern of replicated SIESTA compressed sparse column (CSC) matrices
      86              : ! **************************************************************************************************
      87              :    TYPE siesta_distrib_csc_struct_type
      88              :       !> gather all non-zero matrix elements on the given MPI process.
      89              :       !> Distribute the elements across MPI processes if gather_root < 0.
      90              :       !> The matrix elements should be located on I/O process in case of bulk transport,
      91              :       !> and should be distributed in case of SMEAGOL calculation.
      92              :       INTEGER                                            :: gather_root = 0
      93              :       !> Based of full (.FALSE.) or upper-triangular (.TRUE.) DBCSR matrix.
      94              :       !> It is used in CPASSERTs to allow access to lower-triangular matrix elements of non-symmetric DBCSR matrices
      95              :       !> In case there is no bugs in this module, these CPASSERTs should newer trigger.
      96              :       !> Therefore the 'symmetric' variable alongside with relevant CPASSERT calls are excessive and can be removed.
      97              :       LOGICAL                                            :: symmetric = .TRUE.
      98              :       !> number of neighbour list nodes for each MPI process (0:num_pe-1).
      99              :       !> If do_merge == .TRUE., nodes for different cell images along k cell vector are merged into one node
     100              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: nnodes_per_proc
     101              : 
     102              :       !> replicated neighbour list (1:neighbor_list_dim1, 1:SUM(nnodes_per_proc)).
     103              :       !> Neighbour list nodes are ordered according to their MPI ranks.
     104              :       !> Thus, the first nnodes_per_proc(0) nodes are stored on MPI rank 0,
     105              :       !> the next nnodes_per_proc(1) nodes reside on MPI rank 1, etc
     106              :       !> Nodes for cell images along transport direction are merged into one node.
     107              :       !> The number of non-zero DBCSR matrix blocks and their DBCSR cell image indices
     108              :       !> are stored into 'n_dbcsr_cell_images_to_merge' and 'dbcsr_cell_image_to_merge' arrays
     109              :       INTEGER, ALLOCATABLE, DIMENSION(:, :)               :: nl_repl
     110              : 
     111              :       !> number of DBCSR images for each local merged neighbour list node (1:nnodes_per_proc(para_env%mepos))
     112              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: n_dbcsr_cell_images_to_merge
     113              :       !> list of DBCSR image indices to merge; (1:SUM(n_dbcsr_cell_images_to_merge))
     114              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: dbcsr_cell_image_to_merge
     115              : 
     116              :       !> number of DBCSR non-zero matrix elements that should be received/sent from each MPI rank
     117              :       !> (0:num_pe-1, 1:nelements_dbcsr_dim2)
     118              :       INTEGER(kind=int_8), ALLOCATABLE, DIMENSION(:, :)   :: nelements_per_proc
     119              : 
     120              :       !> number of non-zero matrix elements local to this MPI rank.
     121              :       INTEGER(kind=int_8)                                :: n_nonzero_elements = 0_int_8
     122              :       INTEGER                                            :: nrows = 0, ncols = 0
     123              :       !> Number of non-zero matrix elements (columns) on each row
     124              :       !> n_nonzero_cols(1:nrows); same as 'numh' in SMEAGOL code.
     125              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: n_nonzero_cols
     126              :       !> offset of the first non-zero matrix elements on each row.
     127              :       !> column_offset(1:nrows); same as 'listhptr' in SMEAGOL code.
     128              :       !> It should be declared as INTEGER(kind=int_8), but SMEAGOL expects it to be INTEGER
     129              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: row_offset
     130              :       !> column index of each non-zero matrix element.
     131              :       !> col_index(1:n_nonzero_elements); same as 'listh' in SMEAGOL code
     132              :       !> col index of the first non-zero matrix elements of irow row is col_index(row_offset(irow)+1)
     133              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: col_index
     134              :       !> index of the non-zero matrix element in a communication buffer for each rank
     135              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: packed_index
     136              :       !> R_atom_row - R_atom_col
     137              :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :)         :: xij
     138              :       !> equivalent atomic orbitals
     139              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: indxuo
     140              :       !> atomic index on which the orbital is centred
     141              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: iaorb
     142              :       !> coordinates of all atoms in the supercell
     143              :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :)         :: xa
     144              :    END TYPE siesta_distrib_csc_struct_type
     145              : 
     146              : CONTAINS
     147              : 
     148              : ! **************************************************************************************************
     149              : !> \brief Map non-zero matrix blocks between sparse matrices in DBCSR and SIESTA formats.
     150              : !> \param siesta_struct      structure that stores metadata (sparsity pattern) of sparse SIESTA matrices
     151              : !> \param matrix_dbcsr_kp    DBCSR matrices for each cell image
     152              : !> \param subsys             QuickStep molecular system
     153              : !> \param cell_to_index      array to convert 3-D cell indices to 1-D DBCSR image indices
     154              : !> \param sab_nl             pair-wise neighbour list
     155              : !> \param para_env           MPI parallel environment
     156              : !> \param max_ij_cell_image  largest index of cell images along i and j cell vectors (e.g. (2,0) in
     157              : !>                           case of 5 cell images (0,0), (1,0), (-1,0), (2,0), and (-2,0))
     158              : !> \param do_merge           merge DBCSR images along transport direction (k cell vector)
     159              : !> \param gather_root        distribute non-zero matrix elements of SIESTA matrices across all
     160              : !>                           parallel processes (-1), or gather them on the given MPI rank (>= 0).
     161              : ! **************************************************************************************************
     162            4 :    SUBROUTINE siesta_struct_create(siesta_struct, matrix_dbcsr_kp, subsys, cell_to_index, &
     163              :                                    sab_nl, para_env, max_ij_cell_image, do_merge, gather_root)
     164              :       TYPE(siesta_distrib_csc_struct_type), &
     165              :          INTENT(inout)                                   :: siesta_struct
     166              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(in)       :: matrix_dbcsr_kp
     167              :       TYPE(qs_subsys_type), POINTER                      :: subsys
     168              :       INTEGER, DIMENSION(:, :, :), POINTER               :: cell_to_index
     169              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
     170              :          POINTER                                         :: sab_nl
     171              :       TYPE(mp_para_env_type), POINTER                    :: para_env
     172              :       INTEGER, DIMENSION(2), INTENT(inout)               :: max_ij_cell_image
     173              :       LOGICAL, INTENT(in)                                :: do_merge
     174              :       INTEGER, INTENT(in)                                :: gather_root
     175              : 
     176              :       CHARACTER(len=*), PARAMETER :: routineN = 'siesta_struct_create'
     177              : 
     178              :       CHARACTER(len=20)                                  :: str_nelem, str_nelem_max
     179              :       INTEGER :: handle, iatom, icol, icol_blk, icol_local, image, image_j, image_k, irow, &
     180              :          irow_local, natoms, ncells_siesta_total, ncols_blk, ncols_total, nrows_local, &
     181              :          nrows_total, offset
     182              :       INTEGER(kind=int_8)                                :: n_nonzero_elements_local
     183              :       INTEGER, DIMENSION(3)                              :: max_ijk_cell_image, ncells_siesta
     184            4 :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_offset, col_blk_size
     185              :       LOGICAL                                            :: do_distribute, is_root_rank
     186              :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :)        :: particle_coords
     187              :       REAL(kind=dp), DIMENSION(3)                        :: real_cell_shift, scaled_cell_shift
     188              :       TYPE(cell_type), POINTER                           :: cell
     189            4 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     190              : 
     191            4 :       CALL timeset(routineN, handle)
     192            4 :       do_distribute = gather_root < 0
     193            4 :       is_root_rank = gather_root == para_env%mepos
     194              : 
     195              :       ! here row_blk_offset / col_blk_offset are global indices of the first row / column of a given non-zero block.
     196              :       ! They are not offsets (index-1) but the actual indices.
     197              :       CALL dbcsr_get_info(matrix=matrix_dbcsr_kp(1)%matrix, &
     198              :                           nfullrows_total=nrows_total, nfullcols_total=ncols_total, &
     199            4 :                           nblkcols_total=ncols_blk, col_blk_size=col_blk_size, col_blk_offset=col_blk_offset)
     200              :       IF (debug_this_module) THEN
     201              :          CPASSERT(nrows_total == ncols_total)
     202              :          CPASSERT(gather_root < para_env%num_pe)
     203              :       END IF
     204              : 
     205            4 :       siesta_struct%gather_root = gather_root
     206            4 :       CALL get_neighbor_list_set_p(neighbor_list_sets=sab_nl, symmetric=siesta_struct%symmetric)
     207              : 
     208              :       ! apply periodic boundary conditions to atomic coordinates
     209            4 :       CALL qs_subsys_get(subsys, cell=cell, particle_set=particle_set, nparticle=natoms)
     210           12 :       ALLOCATE (particle_coords(3, natoms))
     211          140 :       DO iatom = 1, natoms
     212          140 :          CALL pbc_0_1(particle_coords(1:3, iatom), particle_set(iatom)%r(1:3), cell)
     213              :       END DO
     214              : 
     215              :       ! Note: in case we would like to limit the number of cell images along transport direction (k cell vector)
     216              :       !       by enabling 'BulkTransvCellSizeZ' keyword, we need to pass max_ijk_cell_image(1:3) vector
     217              :       !       to the subroutine instead of the reduced vector max_ij_cell_image(1:2)
     218           12 :       max_ijk_cell_image(1:2) = max_ij_cell_image(1:2)
     219              : 
     220              :       ! determine the actual number of cell images along k cell vector. Unless the third element is also passed
     221              :       ! via subroutine arguments, an extra MPI_Allreduce operation is needed each time we call
     222              :       ! replicate_neighbour_list() / get_nnodes_local().
     223            4 :       max_ijk_cell_image(3) = -1
     224              :       ! bulk-transport calculation expects exactly 3 cell images along transport direction
     225            4 :       IF (.NOT. do_merge) max_ijk_cell_image(3) = 1
     226              : 
     227              :       ! replicate pair-wise neighbour list. Identical non-zero matrix blocks from cell image along transport direction
     228              :       ! are grouped together if do_merge == .TRUE.
     229           12 :       ALLOCATE (siesta_struct%nnodes_per_proc(0:para_env%num_pe - 1))
     230              :       CALL replicate_neighbour_list(siesta_struct%nl_repl, &
     231              :                                     siesta_struct%n_dbcsr_cell_images_to_merge, &
     232              :                                     siesta_struct%dbcsr_cell_image_to_merge, &
     233              :                                     siesta_struct%nnodes_per_proc, &
     234            4 :                                     max_ijk_cell_image, sab_nl, para_env, particle_coords, cell, cell_to_index, do_merge)
     235           12 :       max_ij_cell_image(1:2) = max_ijk_cell_image(1:2)
     236              : 
     237              :       ! count number of non-zero matrix elements that need to be send to and received from other parallel processes
     238           16 :       ALLOCATE (siesta_struct%nelements_per_proc(0:para_env%num_pe - 1, nelements_dbcsr_dim2))
     239              :       CALL count_remote_dbcsr_elements(siesta_struct%nelements_per_proc, siesta_struct%nnodes_per_proc, &
     240            4 :                                        siesta_struct%nl_repl, matrix_dbcsr_kp, siesta_struct%symmetric, para_env, gather_root)
     241              : 
     242              :       ! number of SIESTA non-zero matrix elements that are going to be stored on this parallel process
     243           12 :       n_nonzero_elements_local = SUM(siesta_struct%nelements_per_proc(:, nelements_dbcsr_recv))
     244            4 :       siesta_struct%n_nonzero_elements = n_nonzero_elements_local
     245              : 
     246              :       ! as SMEAGOL uses 32-bits integers, the number of non-zero matrix elements is limited by 2^31 per MPI rank.
     247              :       ! Abort CP2K if we are about to exceed this limit.
     248            4 :       IF (n_nonzero_elements_local > INT(HUGE(0), kind=int_8)) THEN
     249            0 :          WRITE (str_nelem, '(I0)') n_nonzero_elements_local
     250            0 :          WRITE (str_nelem_max, '(I0)') HUGE(0)
     251              :          CALL cp_abort(__LOCATION__, &
     252              :                        "The number of non-zero matrix elements per MPI process "//TRIM(str_nelem)// &
     253              :                        " cannot exceed "//TRIM(str_nelem_max)// &
     254            0 :                        ". Please increase the number of MPI processes to satisfy this SMEAGOL limitation.")
     255              :       END IF
     256              : 
     257              :       ! in case there is no nonzero matrix element stored on this process, allocate arrays with one element to avoid SEGFAULT
     258            4 :       IF (n_nonzero_elements_local == 0) n_nonzero_elements_local = 1
     259              : 
     260              :       ! number of SIESTA-matrix rows local to the given parallel process
     261            4 :       IF (do_distribute) THEN
     262              : #if defined(__SMEAGOL)
     263            0 :          CALL GetNodeOrbs(nrows_total, para_env%mepos, para_env%num_pe, nrows_local)
     264              : #else
     265              :          CALL cp_abort(__LOCATION__, &
     266              :                        "CP2K was compiled with no SMEAGOL support.")
     267              : #endif
     268              :       ELSE
     269            4 :          IF (is_root_rank) THEN
     270            2 :             nrows_local = nrows_total
     271              :          ELSE
     272            2 :             nrows_local = 0
     273              :          END IF
     274              :       END IF
     275              : 
     276              :       ! number of cell images along each cell vector. It is 2*m+1, as SIESTA images are ordered as 0, 1, -1, ..., m, -m
     277           16 :       ncells_siesta(1:3) = 2*max_ijk_cell_image(1:3) + 1
     278              :       ! in case of merged cell images along the transport direction, there will be just 1 'merged' cell image along it
     279            4 :       IF (do_merge) ncells_siesta(3) = 1
     280              : 
     281            4 :       ncells_siesta_total = ncells_siesta(1)*ncells_siesta(2)*ncells_siesta(3)
     282              : 
     283              :       ! number of rows local to the given parallel process. Rows are distributed in a block-cyclic manner
     284            4 :       siesta_struct%nrows = nrows_local
     285              : 
     286              :       ! number of columns of the matrix in its dense form. SIESTA uses 1-D (rows) block-cyclic distribution.
     287              :       ! All non-zero matrix elements on a given row are stored on the same parallel process
     288            4 :       siesta_struct%ncols = nrows_total*ncells_siesta_total
     289              : 
     290              :       ! allocate at least one array element to avoid SIGFAULT when passing unallocated arrays to subroutines
     291            4 :       IF (nrows_local == 0) nrows_local = 1
     292              : 
     293           12 :       ALLOCATE (siesta_struct%n_nonzero_cols(nrows_local))
     294            8 :       ALLOCATE (siesta_struct%row_offset(nrows_local))
     295           12 :       ALLOCATE (siesta_struct%col_index(n_nonzero_elements_local))
     296            8 :       ALLOCATE (siesta_struct%packed_index(n_nonzero_elements_local))
     297              : 
     298              :       ! restore the actual number of local rows
     299              :       nrows_local = siesta_struct%nrows
     300              : 
     301              :       ! get number of non-zero matrix element on each local row (n_nonzero_cols),
     302              :       ! offset of the first non-zero matrix element for each local row (row_offset),
     303              :       ! global column indices of all local non-zero matrix elements (col_index), and
     304              :       ! the indices of all local non-zero matrix elements in the communication buffer (packed_index)
     305              :       CALL get_nonzero_element_indices(siesta_struct%n_nonzero_cols, siesta_struct%row_offset, &
     306              :                                        siesta_struct%col_index, siesta_struct%packed_index, &
     307              :                                        siesta_struct%nl_repl, matrix_dbcsr_kp, &
     308            4 :                                        siesta_struct%symmetric, para_env, gather_root)
     309              : 
     310              :       ! indices of equivalent atomic orbitals
     311           12 :       ALLOCATE (siesta_struct%indxuo(siesta_struct%ncols))
     312         1228 :       DO icol = 1, ncols_total
     313         1228 :          siesta_struct%indxuo(icol) = icol
     314              :       END DO
     315          300 :       DO image = 2, ncells_siesta_total
     316       181452 :          siesta_struct%indxuo((image - 1)*ncols_total + 1:image*ncols_total) = siesta_struct%indxuo(1:ncols_total)
     317              :       END DO
     318              : 
     319              :       ! particle index on which the orbital is centred
     320            8 :       ALLOCATE (siesta_struct%iaorb(siesta_struct%ncols))
     321          140 :       DO icol_blk = 1, ncols_blk
     322              :          ! col_blk_offset() is not an offset but the index of the first atomic orbital in the column block
     323         1364 :          siesta_struct%iaorb(col_blk_offset(icol_blk):col_blk_offset(icol_blk) + col_blk_size(icol_blk) - 1) = icol_blk
     324              :       END DO
     325          300 :       DO image = 2, ncells_siesta_total
     326       181452 :         siesta_struct%iaorb((image - 1)*ncols_total + 1:image*ncols_total) = siesta_struct%iaorb(1:ncols_total) + (image - 1)*natoms
     327              :       END DO
     328              : 
     329              :       ! coordinates of all particles in each cell images
     330           12 :       ALLOCATE (siesta_struct%xa(3, natoms*ncells_siesta_total))
     331           16 :       DO image_k = 1, ncells_siesta(3)
     332              :          !icell_siesta(3) = image_k
     333           12 :          scaled_cell_shift(3) = REAL(number_from_canonical_enumeration(image_k), kind=dp) ! SIESTA -> actual cell index
     334           76 :          DO image_j = 1, ncells_siesta(2)
     335           60 :             scaled_cell_shift(2) = REAL(number_from_canonical_enumeration(image_j), kind=dp)
     336          372 :             DO image = 1, ncells_siesta(1)
     337          300 :                scaled_cell_shift(1) = REAL(number_from_canonical_enumeration(image), kind=dp)
     338          300 :                CALL scaled_to_real(real_cell_shift, scaled_cell_shift, cell)
     339          300 :                offset = (((image_k - 1)*ncells_siesta(2) + image_j - 1)*ncells_siesta(1) + image - 1)*natoms
     340        10560 :                DO iatom = 1, natoms
     341        41100 :                   siesta_struct%xa(1:3, offset + iatom) = particle_set(iatom)%r(1:3) + real_cell_shift(1:3)
     342              :                END DO
     343              :             END DO
     344              :          END DO
     345              :       END DO
     346              : 
     347              :       ! inter-atomic distance
     348           12 :       ALLOCATE (siesta_struct%xij(3, n_nonzero_elements_local))
     349          616 :       DO irow_local = 1, nrows_local
     350          612 :          IF (do_distribute) THEN
     351              : #if defined(__SMEAGOL)
     352            0 :             CALL LocalToGlobalOrb(irow_local, para_env%mepos, para_env%num_pe, irow)
     353              : #else
     354              :             CALL cp_abort(__LOCATION__, &
     355              :                           "CP2K was compiled with no SMEAGOL support.")
     356              : #endif
     357              :          ELSE
     358          612 :             irow = irow_local
     359              :             IF (debug_this_module) THEN
     360              :                CPASSERT(is_root_rank)
     361              :             END IF
     362              :          END IF
     363          612 :          offset = siesta_struct%row_offset(irow_local)
     364      2319484 :          DO icol_local = offset + 1, offset + siesta_struct%n_nonzero_cols(irow_local)
     365      2318868 :             icol = siesta_struct%col_index(icol_local)
     366              :             siesta_struct%xij(1:3, icol_local) = siesta_struct%xa(1:3, siesta_struct%iaorb(icol)) - &
     367      9276084 :                                                  siesta_struct%xa(1:3, siesta_struct%iaorb(irow))
     368              :          END DO
     369              :       END DO
     370              : 
     371            4 :       DEALLOCATE (particle_coords)
     372              : 
     373            4 :       CALL timestop(handle)
     374            8 :    END SUBROUTINE siesta_struct_create
     375              : 
     376              : ! **************************************************************************************************
     377              : !> \brief Release a SIESTA matrix structure
     378              : !> \param siesta_struct      structure to release
     379              : ! **************************************************************************************************
     380            4 :    SUBROUTINE siesta_struct_release(siesta_struct)
     381              :       TYPE(siesta_distrib_csc_struct_type), &
     382              :          INTENT(inout)                                   :: siesta_struct
     383              : 
     384              :       CHARACTER(len=*), PARAMETER :: routineN = 'siesta_struct_release'
     385              : 
     386              :       INTEGER                                            :: handle
     387              : 
     388            4 :       CALL timeset(routineN, handle)
     389              : 
     390            4 :       siesta_struct%gather_root = -1
     391              : 
     392            4 :       IF (ALLOCATED(siesta_struct%nnodes_per_proc)) DEALLOCATE (siesta_struct%nnodes_per_proc)
     393            4 :       IF (ALLOCATED(siesta_struct%nl_repl)) DEALLOCATE (siesta_struct%nl_repl)
     394            4 :       IF (ALLOCATED(siesta_struct%n_dbcsr_cell_images_to_merge)) DEALLOCATE (siesta_struct%n_dbcsr_cell_images_to_merge)
     395            4 :       IF (ALLOCATED(siesta_struct%dbcsr_cell_image_to_merge)) DEALLOCATE (siesta_struct%dbcsr_cell_image_to_merge)
     396            4 :       IF (ALLOCATED(siesta_struct%nelements_per_proc)) DEALLOCATE (siesta_struct%nelements_per_proc)
     397              : 
     398            4 :       siesta_struct%n_nonzero_elements = 0
     399            4 :       siesta_struct%nrows = 0
     400            4 :       siesta_struct%ncols = 0
     401              : 
     402            4 :       IF (ALLOCATED(siesta_struct%n_nonzero_cols)) DEALLOCATE (siesta_struct%n_nonzero_cols)
     403            4 :       IF (ALLOCATED(siesta_struct%row_offset)) DEALLOCATE (siesta_struct%row_offset)
     404            4 :       IF (ALLOCATED(siesta_struct%col_index)) DEALLOCATE (siesta_struct%col_index)
     405            4 :       IF (ALLOCATED(siesta_struct%packed_index)) DEALLOCATE (siesta_struct%packed_index)
     406              : 
     407            4 :       IF (ALLOCATED(siesta_struct%xij)) DEALLOCATE (siesta_struct%xij)
     408            4 :       IF (ALLOCATED(siesta_struct%indxuo)) DEALLOCATE (siesta_struct%indxuo)
     409            4 :       IF (ALLOCATED(siesta_struct%iaorb)) DEALLOCATE (siesta_struct%iaorb)
     410            4 :       IF (ALLOCATED(siesta_struct%xa)) DEALLOCATE (siesta_struct%xa)
     411              : 
     412            4 :       CALL timestop(handle)
     413            4 :    END SUBROUTINE siesta_struct_release
     414              : 
     415              : ! **************************************************************************************************
     416              : !> \brief Convert matrix from DBCSR to sparse SIESTA format.
     417              : !> \param matrix_siesta      matrix in SIESTA format [out]
     418              : !> \param matrix_dbcsr_kp    DBCSR matrix [in]
     419              : !> \param siesta_struct      structure to map matrix blocks between formats
     420              : !> \param para_env           MPI parallel environment
     421              : ! **************************************************************************************************
     422           16 :    SUBROUTINE convert_dbcsr_to_distributed_siesta(matrix_siesta, matrix_dbcsr_kp, siesta_struct, para_env)
     423              :       REAL(kind=dp), DIMENSION(:), INTENT(out)           :: matrix_siesta
     424              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(in)       :: matrix_dbcsr_kp
     425              :       TYPE(siesta_distrib_csc_struct_type), INTENT(in)   :: siesta_struct
     426              :       TYPE(mp_para_env_type), INTENT(in), POINTER        :: para_env
     427              : 
     428              :       CHARACTER(len=*), PARAMETER :: routineN = 'convert_dbcsr_to_distributed_siesta'
     429              : 
     430              :       INTEGER :: first_col_minus_one, first_row_minus_one, handle, icol_blk, icol_local, &
     431              :          image_dbcsr, image_ind, image_ind_offset, image_siesta, image_siesta_transp, inode, &
     432              :          inode_proc, iproc, irequest, irow_blk, irow_local, irow_proc, mepos, n_image_ind, &
     433              :          ncols_blk, ncols_local, nnodes_proc, node_offset, nprocs, nrequests_recv, &
     434              :          nrequests_total, nrows_blk, nrows_local
     435              :       INTEGER(kind=int_8)                                :: n_nonzero_elements_dbcsr, &
     436              :                                                             n_nonzero_elements_siesta, &
     437              :                                                             offset_recv_mepos, offset_send_mepos
     438           16 :       INTEGER(kind=int_8), ALLOCATABLE, DIMENSION(:)     :: n_packed_elements_per_proc, &
     439           16 :                                                             nelements_per_request, &
     440           16 :                                                             offset_per_proc, offset_per_request
     441           16 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: next_nonzero_element_offset, peer_rank, &
     442           16 :                                                             request_tag
     443           16 :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_offset, col_blk_size, &
     444           16 :                                                             row_blk_offset, row_blk_size
     445              :       LOGICAL                                            :: do_distribute, found, is_root_rank, &
     446              :                                                             symmetric
     447           16 :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:)           :: recv_buffer, reorder_recv_buffer, &
     448           16 :                                                             send_buffer
     449           16 :       REAL(kind=dp), DIMENSION(:, :), POINTER            :: sm_block, sm_block_merged
     450           16 :       TYPE(mp_request_type), ALLOCATABLE, DIMENSION(:)   :: requests
     451              : 
     452           16 :       CALL timeset(routineN, handle)
     453      9275496 :       matrix_siesta(:) = 0.0_dp
     454              : 
     455           16 :       mepos = para_env%mepos
     456           16 :       nprocs = para_env%num_pe
     457           16 :       do_distribute = siesta_struct%gather_root < 0
     458           16 :       is_root_rank = siesta_struct%gather_root == mepos
     459              : 
     460              :       CALL dbcsr_get_info(matrix=matrix_dbcsr_kp(1)%matrix, &
     461              :                           nblkrows_total=nrows_blk, nblkcols_total=ncols_blk, &
     462              :                           row_blk_size=row_blk_size, col_blk_size=col_blk_size, &
     463           16 :                           row_blk_offset=row_blk_offset, col_blk_offset=col_blk_offset)
     464           16 :       symmetric = siesta_struct%symmetric
     465              : 
     466              :       ! number of locally stored SIESTA non-zero matrix elements
     467           48 :       n_nonzero_elements_siesta = SUM(siesta_struct%nelements_per_proc(:, nelements_dbcsr_recv))
     468              :       ! number of locally stored DBCSR non-zero matrix elements
     469           48 :       n_nonzero_elements_dbcsr = SUM(siesta_struct%nelements_per_proc(:, nelements_dbcsr_send))
     470              : 
     471              :       ! number of concurrent MPI isend / irecv operations
     472              :       nrequests_recv = get_number_of_mpi_sendrecv_requests(mepos, siesta_struct%nelements_per_proc(:, nelements_dbcsr_recv), &
     473           16 :                                                            max_mpi_packet_size_dp)
     474              :       nrequests_total = get_number_of_mpi_sendrecv_requests(mepos, siesta_struct%nelements_per_proc(:, nelements_dbcsr_send), &
     475           16 :                                                             max_mpi_packet_size_dp) + nrequests_recv
     476              : 
     477           16 :       IF (nrequests_total > 0) THEN
     478              :          ! allocate MPI-related arrays. request_tag is not actually needed, as MPI standard guarantees the order of
     479              :          ! peer-to-peer messages with the same tag between same processes
     480           64 :          ALLOCATE (requests(nrequests_total))
     481           48 :          ALLOCATE (peer_rank(nrequests_total), request_tag(nrequests_total))
     482           64 :          ALLOCATE (offset_per_request(nrequests_total), nelements_per_request(nrequests_total))
     483              :          !requests(:) = mp_request_null
     484              : 
     485              :          ! split large messages into a number of smaller messages. It is not really needed
     486              :          ! unless we are going to send > 2^31 matrix elements per MPI request
     487           16 :          IF (nrequests_recv > 0) THEN
     488              :             CALL assign_nonzero_elements_to_requests(offset_per_request(1:nrequests_recv), &
     489              :                                                      nelements_per_request(1:nrequests_recv), &
     490              :                                                      peer_rank(1:nrequests_recv), &
     491              :                                                      request_tag(1:nrequests_recv), &
     492              :                                                      mepos, &
     493              :                                                      siesta_struct%nelements_per_proc(:, nelements_dbcsr_recv), &
     494            8 :                                                      max_mpi_packet_size_dp)
     495              :          END IF
     496           16 :          IF (nrequests_total > nrequests_recv) THEN
     497              :             CALL assign_nonzero_elements_to_requests(offset_per_request(nrequests_recv + 1:nrequests_total), &
     498              :                                                      nelements_per_request(nrequests_recv + 1:nrequests_total), &
     499              :                                                      peer_rank(nrequests_recv + 1:nrequests_total), &
     500              :                                                      request_tag(nrequests_recv + 1:nrequests_total), &
     501              :                                                      mepos, &
     502              :                                                      siesta_struct%nelements_per_proc(:, nelements_dbcsr_send), &
     503            8 :                                                      max_mpi_packet_size_dp)
     504              :          END IF
     505              :       END IF
     506              : 
     507              :       ! point-to-point recv/send can be replaced with alltoallv, if data to distribute per rank is < 2^31 elements
     508              :       ! (should be OK due to SMEAGOL limitation)
     509              :       ! in principle, it is possible to overcome this limit by using derived datatypes
     510              :       ! (which will require additional wrapper functions, indeed).
     511              :       !
     512              :       ! pre-post non-blocking receive operations
     513           16 :       IF (n_nonzero_elements_siesta > 0) THEN
     514           24 :          ALLOCATE (recv_buffer(n_nonzero_elements_siesta))
     515              :       END IF
     516           24 :       DO irequest = 1, nrequests_recv
     517              :          CALL para_env%irecv(recv_buffer(offset_per_request(irequest) + 1: &
     518              :                                          offset_per_request(irequest) + nelements_per_request(irequest)), &
     519           24 :                              peer_rank(irequest), requests(irequest), request_tag(irequest))
     520              :       END DO
     521              : 
     522              :       ! pack local DBCSR non-zero matrix elements ordering by their target parallel process
     523           64 :       ALLOCATE (offset_per_proc(0:nprocs - 1), n_packed_elements_per_proc(0:nprocs - 1))
     524           16 :       offset_per_proc(0) = 0
     525           32 :       DO iproc = 1, nprocs - 1
     526           32 :          offset_per_proc(iproc) = offset_per_proc(iproc - 1) + siesta_struct%nelements_per_proc(iproc - 1, nelements_dbcsr_send)
     527              :       END DO
     528           16 :       n_packed_elements_per_proc(:) = 0
     529              : 
     530              :       ! number of local neighbour-list nodes and offset of the first local neighbour-list node
     531           16 :       nnodes_proc = siesta_struct%nnodes_per_proc(mepos)
     532              :       !node_offset = SUM(siesta_struct%nnodes_per_proc(0:mepos)) - siesta_struct%nnodes_per_proc(mepos)
     533           40 :       node_offset = SUM(siesta_struct%nnodes_per_proc(0:mepos)) - nnodes_proc
     534              : 
     535              :       ! if do_distribute == .FALSE., send all matrix elements to MPI process with rank gather_root
     536              :       ! in case of do_distribute == .TRUE., iproc is determined by calling WhichNodeOrb()
     537           16 :       iproc = siesta_struct%gather_root
     538              : 
     539           16 :       IF (n_nonzero_elements_dbcsr > 0) THEN
     540           48 :          ALLOCATE (send_buffer(n_nonzero_elements_dbcsr))
     541           16 :          send_buffer(:) = 0.0_dp
     542              : 
     543              :          ! iterate over locally-stored DBCSR matrix blocks.
     544              :          ! inode_proc is the target parallel process (where data are going to be sent)
     545           16 :          image_ind_offset = 0
     546        59568 :          DO inode_proc = 1, nnodes_proc
     547        59552 :             n_image_ind = siesta_struct%n_dbcsr_cell_images_to_merge(inode_proc)
     548        59552 :             IF (n_image_ind > 0) THEN
     549        59552 :                inode = node_offset + inode_proc
     550              : 
     551        59552 :                irow_blk = siesta_struct%nl_repl(neighbor_list_iatom_index, inode)
     552        59552 :                icol_blk = siesta_struct%nl_repl(neighbor_list_jatom_index, inode)
     553        59552 :                CPASSERT(irow_blk <= icol_blk .OR. (.NOT. symmetric))
     554        59552 :                image_siesta = siesta_struct%nl_repl(neighbor_list_siesta_image_index, inode)
     555        59552 :                image_siesta_transp = siesta_struct%nl_repl(neighbor_list_siesta_transp_image_index, inode)
     556              : 
     557        59552 :                nrows_local = row_blk_size(irow_blk)
     558        59552 :                ncols_local = col_blk_size(icol_blk)
     559        59552 :                first_row_minus_one = row_blk_offset(irow_blk) - 1
     560        59552 :                first_col_minus_one = col_blk_offset(icol_blk) - 1
     561              : 
     562              :                ! merging cell images along transport direction
     563        59552 :                IF (n_image_ind == 1) THEN
     564              :                   ! the most common case. Nothing to merge, so there is no need to allocate memory for a merged block
     565        59552 :                   image_dbcsr = siesta_struct%dbcsr_cell_image_to_merge(image_ind_offset + 1)
     566              :                   CALL dbcsr_get_block_p(matrix=matrix_dbcsr_kp(image_dbcsr)%matrix, &
     567        59552 :                                          row=irow_blk, col=icol_blk, block=sm_block_merged, found=found)
     568        59552 :                   CPASSERT(found)
     569              :                ELSE ! n_image_ind > 1
     570            0 :                   ALLOCATE (sm_block_merged(nrows_local, ncols_local))
     571              : 
     572            0 :                   DO image_ind = 1, n_image_ind
     573            0 :                      image_dbcsr = siesta_struct%dbcsr_cell_image_to_merge(image_ind + image_ind_offset)
     574              : 
     575              :                      CALL dbcsr_get_block_p(matrix=matrix_dbcsr_kp(image_dbcsr)%matrix, &
     576            0 :                                             row=irow_blk, col=icol_blk, block=sm_block, found=found)
     577            0 :                      CPASSERT(found)
     578              :                      sm_block_merged(1:nrows_local, 1:ncols_local) = sm_block_merged(1:nrows_local, 1:ncols_local) + &
     579            0 :                                                                      sm_block(1:nrows_local, 1:ncols_local)
     580              :                   END DO
     581              :                END IF
     582              : 
     583              :                ! pack matrix elements for the 'normal' SIESTA matrix block
     584        59552 :                IF (image_siesta > 0) THEN
     585       595520 :                   DO irow_local = 1, nrows_local
     586       535968 :                      IF (do_distribute) THEN
     587              : #if defined(__SMEAGOL)
     588            0 :                         CALL WhichNodeOrb(irow_local + first_row_minus_one, nprocs, iproc)
     589              : #else
     590              :                         CALL cp_abort(__LOCATION__, &
     591              :                                       "CP2K was compiled with no SMEAGOL support.")
     592              : #endif
     593              :                      END IF
     594              : 
     595              :                      ! CPASSERT
     596              :                      IF (debug_this_module) THEN
     597              :                         CPASSERT(iproc >= 0 .AND. iproc < nprocs)
     598              :                         IF (n_packed_elements_per_proc(iproc) + ncols_local > &
     599              :                             siesta_struct%nelements_per_proc(iproc, nelements_dbcsr_send)) THEN
     600              :                            CALL cp__a(__SHORT_FILE__, __LINE__)
     601              :                         END IF
     602              :                      END IF
     603              : 
     604              :                      offset_send_mepos = offset_per_proc(iproc) + n_packed_elements_per_proc(iproc)
     605       535968 :                      send_buffer(offset_send_mepos + 1:offset_send_mepos + ncols_local) = sm_block_merged(irow_local, 1:ncols_local)
     606      5359680 : 
     607              :                      n_packed_elements_per_proc(iproc) = n_packed_elements_per_proc(iproc) + ncols_local
     608       595520 :                   END DO
     609              :                END IF
     610              : 
     611              :                ! pack matrix elements of the transposed SIESTA matrix block
     612              :                IF (image_siesta_transp > 0) THEN
     613        59552 :                   DO icol_local = 1, ncols_local
     614       549600 :                      IF (do_distribute) THEN
     615       494640 : #if defined(__SMEAGOL)
     616              :                         CALL WhichNodeOrb(icol_local + first_col_minus_one, nprocs, iproc) ! iproc_orb
     617            0 : #else
     618              :                         CALL cp_abort(__LOCATION__, &
     619              :                                       "CP2K was compiled with no SMEAGOL support.")
     620              : #endif
     621              :                      END IF
     622              : 
     623              :                      ! CPASSERT
     624              :                      IF (debug_this_module) THEN
     625              :                         CPASSERT(iproc >= 0 .AND. iproc < nprocs)
     626              :                         IF (n_packed_elements_per_proc(iproc) + nrows_local > &
     627              :                             siesta_struct%nelements_per_proc(iproc, nelements_dbcsr_send)) THEN
     628              :                            CALL cp__a(__SHORT_FILE__, __LINE__)
     629              :                         END IF
     630              :                      END IF
     631              : 
     632              :                      offset_send_mepos = offset_per_proc(iproc) + n_packed_elements_per_proc(iproc)
     633              :                      send_buffer(offset_send_mepos + 1:offset_send_mepos + nrows_local) = sm_block_merged(1:nrows_local, icol_local)
     634       494640 : 
     635      4946400 :                      n_packed_elements_per_proc(iproc) = n_packed_elements_per_proc(iproc) + nrows_local
     636              :                   END DO
     637       549600 :                END IF
     638              : 
     639              :                IF (n_image_ind > 1) THEN
     640              :                   DEALLOCATE (sm_block_merged)
     641        59552 :                END IF
     642            0 :             END IF
     643              : 
     644              :             image_ind_offset = image_ind_offset + siesta_struct%n_dbcsr_cell_images_to_merge(inode_proc)
     645              :          END DO
     646        59568 : 
     647              :          IF (debug_this_module) THEN
     648              :             DO iproc = 0, nprocs - 1
     649              :                IF (n_packed_elements_per_proc(iproc) /= siesta_struct%nelements_per_proc(iproc, nelements_dbcsr_send)) THEN
     650              :                   CALL cp__a(__SHORT_FILE__, __LINE__)
     651              :                END IF
     652              :             END DO
     653              :          END IF
     654              : 
     655              :          ! send packed data to other parallel processes
     656              :          DO irequest = nrequests_recv + 1, nrequests_total
     657              :             CALL para_env%isend(send_buffer(offset_per_request(irequest) + 1: &
     658           24 :                                             offset_per_request(irequest) + nelements_per_request(irequest)), &
     659              :                                 peer_rank(irequest), requests(irequest), request_tag(irequest))
     660              :          END DO
     661           24 : 
     662              :          ! copy data locally that stay on the same process.
     663              :          IF (mepos > 0) THEN
     664              :             offset_recv_mepos = SUM(siesta_struct%nelements_per_proc(0:mepos - 1, nelements_dbcsr_recv))
     665           16 :          ELSE
     666           16 :             offset_recv_mepos = 0
     667              :          END IF
     668              :          offset_send_mepos = offset_per_proc(mepos)
     669              : 
     670           16 :          IF (debug_this_module) THEN
     671              :             IF (n_packed_elements_per_proc(mepos) /= siesta_struct%nelements_per_proc(mepos, nelements_dbcsr_recv)) THEN
     672              :                CALL cp__a(__SHORT_FILE__, __LINE__)
     673              :             END IF
     674              :          END IF
     675              : 
     676              :          IF (n_packed_elements_per_proc(mepos) > 0) THEN
     677              :             recv_buffer(offset_recv_mepos + 1:offset_recv_mepos + n_packed_elements_per_proc(mepos)) = &
     678           16 :                send_buffer(offset_send_mepos + 1:offset_send_mepos + n_packed_elements_per_proc(mepos))
     679              :          END IF
     680      4591088 :       END IF
     681              : 
     682              :       IF (nrequests_total > 0) THEN
     683              :          ! wait for pending isend/irecv requests
     684           16 :          CALL mp_waitall(requests)
     685              :          DEALLOCATE (nelements_per_request, offset_per_request, peer_rank, requests, request_tag)
     686           16 :       END IF
     687           16 : 
     688              :       ! release send buffers
     689              :       IF (ALLOCATED(send_buffer)) DEALLOCATE (send_buffer)
     690              :       DEALLOCATE (offset_per_proc, n_packed_elements_per_proc)
     691           16 : 
     692           16 :       ! non-zero matrix elements in 'recv_buffer' array are grouped by their source MPI rank,
     693              :       ! local row index, and column index (in this order).
     694              :       ! Reorder the matrix elements ('reorder_recv_buffer') so they are grouped by their local row index,
     695              :       ! source MPI rank, and column index.
     696              :       ! (column indices are in the ascending order within each (row index, source MPI rank) block).
     697              :       ! The array 'packed_index' allows mapping matrix element between these intermediate order and SIESTA order :
     698              :       ! local row index, column index
     699              :       IF (n_nonzero_elements_siesta > 0) THEN
     700              :          ALLOCATE (reorder_recv_buffer(n_nonzero_elements_siesta))
     701           16 :          ALLOCATE (next_nonzero_element_offset(siesta_struct%nrows))
     702           24 :          next_nonzero_element_offset(:) = 0
     703           24 :          offset_recv_mepos = 0
     704            8 : 
     705            8 :          DO inode = 1, SIZE(siesta_struct%nl_repl, 2)
     706              :             irow_blk = siesta_struct%nl_repl(neighbor_list_iatom_index, inode)
     707        59560 :             icol_blk = siesta_struct%nl_repl(neighbor_list_jatom_index, inode)
     708        59552 :             CPASSERT(irow_blk <= icol_blk .OR. (.NOT. symmetric))
     709        59552 :             image_siesta = siesta_struct%nl_repl(neighbor_list_siesta_image_index, inode)
     710        59552 :             image_siesta_transp = siesta_struct%nl_repl(neighbor_list_siesta_transp_image_index, inode)
     711        59552 : 
     712        59552 :             nrows_local = row_blk_size(irow_blk)
     713              :             ncols_local = col_blk_size(icol_blk)
     714        59552 :             first_row_minus_one = row_blk_offset(irow_blk) - 1
     715        59552 :             first_col_minus_one = col_blk_offset(icol_blk) - 1
     716        59552 : 
     717        59552 :             ! normal block
     718              :             IF (image_siesta > 0) THEN
     719              :                DO irow_local = 1, nrows_local
     720        59552 :                   IF (do_distribute) THEN
     721       595520 : #if defined(__SMEAGOL)
     722       535968 :                      CALL GlobalToLocalOrb(irow_local + first_row_minus_one, mepos, nprocs, irow_proc)
     723              : #else
     724            0 :                      CALL cp_abort(__LOCATION__, &
     725              :                                    "CP2K was compiled with no SMEAGOL support.")
     726              : #endif
     727              :                   ELSE
     728              :                      IF (is_root_rank) THEN
     729              :                         irow_proc = irow_local + first_row_minus_one
     730       535968 :                      ELSE
     731       535968 :                         irow_proc = 0
     732              :                      END IF
     733            0 :                   END IF
     734              :                   IF (irow_proc > 0) THEN
     735              :                      offset_send_mepos = siesta_struct%row_offset(irow_proc) + next_nonzero_element_offset(irow_proc)
     736       595520 :                      reorder_recv_buffer(offset_send_mepos + 1:offset_send_mepos + ncols_local) = &
     737       535968 :                         recv_buffer(offset_recv_mepos + 1:offset_recv_mepos + ncols_local)
     738              :                      offset_recv_mepos = offset_recv_mepos + ncols_local
     739      5359680 :                      next_nonzero_element_offset(irow_proc) = next_nonzero_element_offset(irow_proc) + ncols_local
     740       535968 :                   END IF
     741       535968 :                END DO
     742              :             END IF
     743              : 
     744              :             ! transposed block
     745              :             IF (image_siesta_transp > 0) THEN
     746              :                DO icol_local = 1, ncols_local
     747        59560 :                   IF (do_distribute) THEN
     748       549600 : #if defined(__SMEAGOL)
     749       494640 :                      CALL GlobalToLocalOrb(icol_local + first_col_minus_one, mepos, nprocs, irow_proc)
     750              : #else
     751            0 :                      CALL cp_abort(__LOCATION__, &
     752              :                                    "CP2K was compiled with no SMEAGOL support.")
     753              : #endif
     754              :                   ELSE
     755              :                      IF (is_root_rank) THEN
     756              :                         irow_proc = icol_local + first_col_minus_one
     757       494640 :                      ELSE
     758       494640 :                         irow_proc = 0
     759              :                      END IF
     760            0 :                   END IF
     761              :                   IF (irow_proc > 0) THEN
     762              :                      offset_send_mepos = siesta_struct%row_offset(irow_proc) + next_nonzero_element_offset(irow_proc)
     763       549600 :                      reorder_recv_buffer(offset_send_mepos + 1:offset_send_mepos + nrows_local) = &
     764       494640 :                         recv_buffer(offset_recv_mepos + 1:offset_recv_mepos + nrows_local)
     765              :                      offset_recv_mepos = offset_recv_mepos + nrows_local
     766      4946400 :                      next_nonzero_element_offset(irow_proc) = next_nonzero_element_offset(irow_proc) + nrows_local
     767       494640 :                   END IF
     768       494640 :                END DO
     769              :             END IF
     770              :          END DO
     771              : 
     772              :          IF (debug_this_module) THEN
     773              :             DO irow_local = 1, siesta_struct%nrows
     774              :                IF (siesta_struct%n_nonzero_cols(irow_local) /= next_nonzero_element_offset(irow_local)) THEN
     775              :                   CALL cp__a(__SHORT_FILE__, __LINE__)
     776              :                END IF
     777              :             END DO
     778              :          END IF
     779              : 
     780              :          DEALLOCATE (next_nonzero_element_offset)
     781              :          DEALLOCATE (recv_buffer)
     782            8 : 
     783            8 :          ! Map non-zero matrix element between the intermediate order and SIESTA order
     784              :          DO irow_local = 1, siesta_struct%nrows
     785              :             offset_recv_mepos = siesta_struct%row_offset(irow_local)
     786         2456 :             DO icol_local = 1, siesta_struct%n_nonzero_cols(irow_local)
     787         2448 :                matrix_siesta(offset_recv_mepos + icol_local) = &
     788      9277928 :                   reorder_recv_buffer(offset_recv_mepos + siesta_struct%packed_index(offset_recv_mepos + icol_local))
     789              :             END DO
     790      9277920 :          END DO
     791              :          DEALLOCATE (reorder_recv_buffer)
     792              :       END IF
     793            8 : 
     794              :       CALL timestop(handle)
     795              :    END SUBROUTINE convert_dbcsr_to_distributed_siesta
     796           16 : 
     797           48 : ! **************************************************************************************************
     798              : !> \brief Convert matrix from DBCSR to sparse SIESTA format.
     799              : !> \param matrix_dbcsr_kp    DBCSR matrix [out]. The matrix is declared as INTENT(in) as pointers to
     800              : !>                 dbcsr matrices remain intact. However we have intention to update matrix elements
     801              : !> \param matrix_siesta      matrix in SIESTA format [in]
     802              : !> \param siesta_struct      structure to map matrix blocks between formats
     803              : !> \param para_env           MPI parallel environment
     804              : ! **************************************************************************************************
     805              :    SUBROUTINE convert_distributed_siesta_to_dbcsr(matrix_dbcsr_kp, matrix_siesta, siesta_struct, para_env)
     806              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(in)       :: matrix_dbcsr_kp
     807            0 :       REAL(kind=dp), DIMENSION(:), INTENT(in)            :: matrix_siesta
     808              :       TYPE(siesta_distrib_csc_struct_type), INTENT(in)   :: siesta_struct
     809              :       TYPE(mp_para_env_type), INTENT(in), POINTER        :: para_env
     810              : 
     811              :       CHARACTER(len=*), PARAMETER :: routineN = 'convert_distributed_siesta_to_dbcsr'
     812              : 
     813              :       INTEGER :: first_col_minus_one, first_row_minus_one, handle, icol_blk, icol_local, &
     814              :          image_dbcsr, image_siesta, image_siesta_transp, inode, inode_proc, iproc, irequest, &
     815              :          irow_blk, irow_local, irow_proc, mepos, n_image_ind, ncols_blk, ncols_local, nnodes_proc, &
     816              :          node_offset, nprocs, nrequests_recv, nrequests_total, nrows_blk, nrows_local
     817              :       INTEGER(kind=int_8)                                :: n_nonzero_elements_dbcsr, &
     818              :                                                             n_nonzero_elements_siesta, &
     819              :                                                             offset_recv_mepos, offset_send_mepos
     820              :       INTEGER(kind=int_8), ALLOCATABLE, DIMENSION(:)     :: n_packed_elements_per_proc, &
     821              :                                                             nelements_per_request, &
     822            0 :                                                             offset_per_proc, offset_per_request
     823            0 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: next_nonzero_element_offset, peer_rank, &
     824            0 :                                                             request_tag
     825            0 :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_offset, col_blk_size, &
     826            0 :                                                             row_blk_offset, row_blk_size
     827            0 :       LOGICAL                                            :: do_distribute, found, is_root_rank, &
     828            0 :                                                             symmetric
     829              :       REAL(kind=dp), ALLOCATABLE, DIMENSION(:)           :: recv_buffer, reorder_send_buffer, &
     830              :                                                             send_buffer
     831            0 :       REAL(kind=dp), DIMENSION(:, :), POINTER            :: sm_block
     832            0 :       TYPE(mp_request_type), ALLOCATABLE, DIMENSION(:)   :: requests
     833            0 : 
     834            0 :       CALL timeset(routineN, handle)
     835              :       DO image_dbcsr = 1, SIZE(matrix_dbcsr_kp)
     836            0 :          CALL dbcsr_set(matrix_dbcsr_kp(image_dbcsr)%matrix, 0.0_dp)
     837            0 :       END DO
     838            0 : 
     839              :       mepos = para_env%mepos
     840              :       nprocs = para_env%num_pe
     841            0 :       do_distribute = siesta_struct%gather_root < 0
     842            0 :       is_root_rank = siesta_struct%gather_root == mepos
     843            0 : 
     844            0 :       CALL dbcsr_get_info(matrix=matrix_dbcsr_kp(1)%matrix, &
     845              :                           nblkrows_total=nrows_blk, nblkcols_total=ncols_blk, &
     846              :                           row_blk_size=row_blk_size, col_blk_size=col_blk_size, &
     847              :                           row_blk_offset=row_blk_offset, col_blk_offset=col_blk_offset)
     848              :       symmetric = siesta_struct%symmetric
     849            0 : 
     850            0 :       n_nonzero_elements_siesta = SUM(siesta_struct%nelements_per_proc(:, nelements_dbcsr_recv))
     851              :       n_nonzero_elements_dbcsr = SUM(siesta_struct%nelements_per_proc(:, nelements_dbcsr_send))
     852            0 : 
     853            0 :       nrequests_recv = get_number_of_mpi_sendrecv_requests(mepos, siesta_struct%nelements_per_proc(:, nelements_dbcsr_send), &
     854              :                                                            max_mpi_packet_size_dp)
     855              :       nrequests_total = get_number_of_mpi_sendrecv_requests(mepos, siesta_struct%nelements_per_proc(:, nelements_dbcsr_recv), &
     856            0 :                                                             max_mpi_packet_size_dp) + nrequests_recv
     857              :       IF (nrequests_total > 0) THEN
     858            0 :          ALLOCATE (requests(nrequests_total))
     859            0 :          ALLOCATE (peer_rank(nrequests_total), request_tag(nrequests_total))
     860            0 :          ALLOCATE (offset_per_request(nrequests_total), nelements_per_request(nrequests_total))
     861            0 :          !requests(:) = mp_request_null
     862            0 :          IF (nrequests_recv > 0) THEN
     863              :             CALL assign_nonzero_elements_to_requests(offset_per_request(1:nrequests_recv), &
     864            0 :                                                      nelements_per_request(1:nrequests_recv), &
     865              :                                                      peer_rank(1:nrequests_recv), &
     866              :                                                      request_tag(1:nrequests_recv), &
     867              :                                                      mepos, &
     868              :                                                      siesta_struct%nelements_per_proc(:, nelements_dbcsr_send), &
     869              :                                                      max_mpi_packet_size_dp)
     870              :          END IF
     871            0 :          IF (nrequests_total > nrequests_recv) THEN
     872              :             CALL assign_nonzero_elements_to_requests(offset_per_request(nrequests_recv + 1:nrequests_total), &
     873            0 :                                                      nelements_per_request(nrequests_recv + 1:nrequests_total), &
     874              :                                                      peer_rank(nrequests_recv + 1:nrequests_total), &
     875              :                                                      request_tag(nrequests_recv + 1:nrequests_total), &
     876              :                                                      mepos, &
     877              :                                                      siesta_struct%nelements_per_proc(:, nelements_dbcsr_recv), &
     878              :                                                      max_mpi_packet_size_dp)
     879              :          END IF
     880            0 :       END IF
     881              : 
     882              :       IF (n_nonzero_elements_dbcsr > 0) THEN
     883              :          ALLOCATE (recv_buffer(n_nonzero_elements_dbcsr))
     884            0 :       END IF
     885            0 :       DO irequest = 1, nrequests_recv
     886              :          CALL para_env%irecv(recv_buffer(offset_per_request(irequest) + 1: &
     887            0 :                                          offset_per_request(irequest) + nelements_per_request(irequest)), &
     888              :                              peer_rank(irequest), requests(irequest), request_tag(irequest))
     889              :       END DO
     890            0 : 
     891              :       ALLOCATE (offset_per_proc(0:nprocs - 1), n_packed_elements_per_proc(0:nprocs - 1))
     892              :       offset_per_proc(0) = 0
     893            0 :       DO iproc = 1, nprocs - 1
     894            0 :          offset_per_proc(iproc) = offset_per_proc(iproc - 1) + siesta_struct%nelements_per_proc(iproc - 1, nelements_dbcsr_send)
     895            0 :       END DO
     896            0 :       n_packed_elements_per_proc(:) = 0
     897              : 
     898            0 :       IF (mepos > 0) THEN
     899              :          node_offset = SUM(siesta_struct%nnodes_per_proc(0:mepos - 1))
     900            0 :       ELSE
     901            0 :          node_offset = 0
     902              :       END IF
     903              :       nnodes_proc = siesta_struct%nnodes_per_proc(mepos)
     904              : 
     905            0 :       IF (n_nonzero_elements_siesta > 0) THEN
     906              :          ALLOCATE (send_buffer(n_nonzero_elements_siesta))
     907            0 : 
     908            0 :          ALLOCATE (reorder_send_buffer(n_nonzero_elements_siesta))
     909              :          DO irow_local = 1, siesta_struct%nrows
     910            0 :             offset_send_mepos = siesta_struct%row_offset(irow_local)
     911            0 :             DO icol_local = 1, siesta_struct%n_nonzero_cols(irow_local)
     912            0 :                reorder_send_buffer(offset_send_mepos + siesta_struct%packed_index(offset_send_mepos + icol_local)) = &
     913            0 :                   matrix_siesta(offset_send_mepos + icol_local)
     914              :             END DO
     915            0 :          END DO
     916              : 
     917              :          ALLOCATE (next_nonzero_element_offset(siesta_struct%nrows))
     918              :          next_nonzero_element_offset(:) = 0
     919            0 :          offset_send_mepos = 0
     920            0 : 
     921            0 :          DO inode = 1, SIZE(siesta_struct%nl_repl, 2)
     922              :             irow_blk = siesta_struct%nl_repl(neighbor_list_iatom_index, inode)
     923            0 :             icol_blk = siesta_struct%nl_repl(neighbor_list_jatom_index, inode)
     924            0 :             CPASSERT(irow_blk <= icol_blk .OR. (.NOT. symmetric))
     925            0 :             image_siesta = siesta_struct%nl_repl(neighbor_list_siesta_image_index, inode)
     926            0 :             image_siesta_transp = siesta_struct%nl_repl(neighbor_list_siesta_transp_image_index, inode)
     927            0 : 
     928            0 :             nrows_local = row_blk_size(irow_blk)
     929              :             ncols_local = col_blk_size(icol_blk)
     930            0 :             first_row_minus_one = row_blk_offset(irow_blk) - 1
     931            0 :             first_col_minus_one = col_blk_offset(icol_blk) - 1
     932            0 : 
     933            0 :             IF (image_siesta > 0) THEN
     934              :                DO irow_local = 1, nrows_local
     935            0 :                   IF (do_distribute) THEN
     936            0 : #if defined(__SMEAGOL)
     937            0 :                      CALL GlobalToLocalOrb(irow_local + first_row_minus_one, mepos, nprocs, irow_proc)
     938              : #else
     939            0 :                      CALL cp_abort(__LOCATION__, &
     940              :                                    "CP2K was compiled with no SMEAGOL support.")
     941              : #endif
     942              :                   ELSE
     943              :                      IF (is_root_rank) THEN
     944              :                         irow_proc = irow_local + first_row_minus_one
     945            0 :                      ELSE
     946            0 :                         irow_proc = 0
     947              :                      END IF
     948            0 :                   END IF
     949              :                   IF (irow_proc > 0) THEN
     950              :                      offset_recv_mepos = siesta_struct%row_offset(irow_proc) + next_nonzero_element_offset(irow_proc)
     951            0 :                      send_buffer(offset_send_mepos + 1:offset_send_mepos + ncols_local) = &
     952            0 :                         reorder_send_buffer(offset_recv_mepos + 1:offset_recv_mepos + ncols_local)
     953              :                      offset_send_mepos = offset_send_mepos + ncols_local
     954            0 :                      next_nonzero_element_offset(irow_proc) = next_nonzero_element_offset(irow_proc) + ncols_local
     955            0 :                   END IF
     956            0 :                END DO
     957              :             END IF
     958              : 
     959              :             ! transposed block
     960              :             IF (image_siesta_transp > 0) THEN
     961              :                DO icol_local = 1, ncols_local
     962            0 :                   IF (do_distribute) THEN
     963            0 : #if defined(__SMEAGOL)
     964            0 :                      CALL GlobalToLocalOrb(icol_local + first_col_minus_one, mepos, nprocs, irow_proc)
     965              : #else
     966            0 :                      CALL cp_abort(__LOCATION__, &
     967              :                                    "CP2K was compiled with no SMEAGOL support.")
     968              : #endif
     969              :                   ELSE
     970              :                      IF (is_root_rank) THEN
     971              :                         irow_proc = icol_local + first_col_minus_one
     972            0 :                      ELSE
     973            0 :                         irow_proc = 0
     974              :                      END IF
     975            0 :                   END IF
     976              :                   IF (irow_proc > 0) THEN
     977              :                      offset_recv_mepos = siesta_struct%row_offset(irow_proc) + next_nonzero_element_offset(irow_proc)
     978            0 :                      send_buffer(offset_send_mepos + 1:offset_send_mepos + nrows_local) = &
     979            0 :                         reorder_send_buffer(offset_recv_mepos + 1:offset_recv_mepos + nrows_local)
     980              :                      offset_send_mepos = offset_send_mepos + nrows_local
     981            0 :                      next_nonzero_element_offset(irow_proc) = next_nonzero_element_offset(irow_proc) + nrows_local
     982            0 :                   END IF
     983            0 :                END DO
     984              :             END IF
     985              :          END DO
     986              : 
     987              :          IF (debug_this_module) THEN
     988              :             DO irow_local = 1, siesta_struct%nrows
     989              :                IF (siesta_struct%n_nonzero_cols(irow_local) /= next_nonzero_element_offset(irow_local)) THEN
     990              :                   CALL cp__a(__SHORT_FILE__, __LINE__)
     991              :                END IF
     992              :             END DO
     993              :          END IF
     994              : 
     995              :          DEALLOCATE (next_nonzero_element_offset)
     996              :          DEALLOCATE (reorder_send_buffer)
     997            0 : 
     998            0 :          DO irequest = nrequests_recv + 1, nrequests_total
     999              :             CALL para_env%isend(send_buffer(offset_per_request(irequest) + 1: &
    1000            0 :                                             offset_per_request(irequest) + nelements_per_request(irequest)), &
    1001              :                                 peer_rank(irequest), requests(irequest), request_tag(irequest))
    1002              :          END DO
    1003            0 : 
    1004              :          ! copy data locally that stay on the same process.
    1005              :          IF (mepos > 0) THEN
    1006              :             offset_send_mepos = SUM(siesta_struct%nelements_per_proc(0:mepos - 1, nelements_dbcsr_recv))
    1007            0 :          ELSE
    1008            0 :             offset_send_mepos = 0
    1009              :          END IF
    1010              :          offset_recv_mepos = offset_per_proc(mepos)
    1011              : 
    1012            0 :          IF (debug_this_module) THEN
    1013              :             IF (siesta_struct%nelements_per_proc(mepos, nelements_dbcsr_recv) /= &
    1014              :                 siesta_struct%nelements_per_proc(mepos, nelements_dbcsr_send)) THEN
    1015              :                CALL cp__a(__SHORT_FILE__, __LINE__)
    1016              :             END IF
    1017              :          END IF
    1018              : 
    1019              :          IF (siesta_struct%nelements_per_proc(mepos, nelements_dbcsr_send) > 0) THEN
    1020              :             recv_buffer(offset_recv_mepos + 1:offset_recv_mepos + siesta_struct%nelements_per_proc(mepos, nelements_dbcsr_send)) = &
    1021            0 :                send_buffer(offset_send_mepos + 1:offset_send_mepos + siesta_struct%nelements_per_proc(mepos, nelements_dbcsr_send))
    1022              :          END IF
    1023            0 :       END IF
    1024              : 
    1025              :       IF (nrequests_total > 0) THEN
    1026              :          ! wait for pending isend/irecv requests
    1027            0 :          CALL mp_waitall(requests)
    1028              :          DEALLOCATE (nelements_per_request, offset_per_request, peer_rank, requests, request_tag)
    1029            0 :       END IF
    1030            0 : 
    1031              :       IF (ALLOCATED(send_buffer)) DEALLOCATE (send_buffer)
    1032              : 
    1033            0 :       ! if do_distribute == .FALSE., collect matrix elements from MPI process with rank gather_root
    1034              :       iproc = siesta_struct%gather_root
    1035              :       IF (n_nonzero_elements_dbcsr > 0) THEN
    1036            0 :          DO inode_proc = 1, nnodes_proc
    1037            0 :             n_image_ind = siesta_struct%n_dbcsr_cell_images_to_merge(inode_proc)
    1038            0 :             IF (n_image_ind > 0) THEN
    1039            0 :                inode = node_offset + inode_proc
    1040            0 : 
    1041            0 :                irow_blk = siesta_struct%nl_repl(neighbor_list_iatom_index, inode)
    1042              :                icol_blk = siesta_struct%nl_repl(neighbor_list_jatom_index, inode)
    1043            0 :                image_dbcsr = siesta_struct%nl_repl(neighbor_list_dbcsr_image_index, inode)
    1044            0 :                CPASSERT(irow_blk <= icol_blk .OR. (.NOT. symmetric))
    1045            0 :                image_siesta = siesta_struct%nl_repl(neighbor_list_siesta_image_index, inode)
    1046            0 :                image_siesta_transp = siesta_struct%nl_repl(neighbor_list_siesta_transp_image_index, inode)
    1047            0 : 
    1048            0 :                nrows_local = row_blk_size(irow_blk)
    1049              :                ncols_local = col_blk_size(icol_blk)
    1050            0 :                first_row_minus_one = row_blk_offset(irow_blk) - 1
    1051            0 :                first_col_minus_one = col_blk_offset(icol_blk) - 1
    1052            0 : 
    1053            0 :                CALL dbcsr_get_block_p(matrix=matrix_dbcsr_kp(image_dbcsr)%matrix, &
    1054              :                                       row=irow_blk, col=icol_blk, block=sm_block, found=found)
    1055              :                CPASSERT(found)
    1056            0 : 
    1057            0 :                IF (image_siesta > 0) THEN
    1058              :                   DO irow_local = 1, nrows_local
    1059            0 :                      IF (do_distribute) THEN
    1060            0 : #if defined(__SMEAGOL)
    1061            0 :                         CALL WhichNodeOrb(irow_local + first_row_minus_one, nprocs, iproc) ! iproc_orb
    1062              : #else
    1063            0 :                         CALL cp_abort(__LOCATION__, &
    1064              :                                       "CP2K was compiled with no SMEAGOL support.")
    1065              : #endif
    1066              :                      END IF
    1067              :                      ! CPASSERT
    1068              :                      IF (debug_this_module) THEN
    1069              :                         CPASSERT(iproc >= 0 .AND. iproc < nprocs)
    1070              :                         IF (n_packed_elements_per_proc(iproc) + ncols_local > &
    1071              :                             siesta_struct%nelements_per_proc(iproc, nelements_dbcsr_send)) THEN
    1072              :                            CALL cp__a(__SHORT_FILE__, __LINE__)
    1073              :                         END IF
    1074              :                      END IF
    1075              : 
    1076              :                      offset_recv_mepos = offset_per_proc(iproc) + n_packed_elements_per_proc(iproc)
    1077              :                      sm_block(irow_local, 1:ncols_local) = recv_buffer(offset_recv_mepos + 1:offset_recv_mepos + ncols_local)
    1078              : 
    1079            0 :                      n_packed_elements_per_proc(iproc) = n_packed_elements_per_proc(iproc) + ncols_local
    1080            0 :                   END DO
    1081              :                END IF
    1082            0 : 
    1083              :                ! transposed block
    1084              :                IF (image_siesta_transp > 0) THEN
    1085              :                   DO icol_local = 1, ncols_local
    1086              :                      IF (do_distribute) THEN
    1087            0 : #if defined(__SMEAGOL)
    1088            0 :                         CALL WhichNodeOrb(icol_local + first_col_minus_one, nprocs, iproc) ! iproc_orb
    1089            0 : #else
    1090              :                         CALL cp_abort(__LOCATION__, &
    1091            0 :                                       "CP2K was compiled with no SMEAGOL support.")
    1092              : #endif
    1093              :                      END IF
    1094              :                      ! CPASSERT
    1095              :                      IF (debug_this_module) THEN
    1096              :                         CPASSERT(iproc >= 0 .AND. iproc < nprocs)
    1097              :                         IF (n_packed_elements_per_proc(iproc) + nrows_local > &
    1098              :                             siesta_struct%nelements_per_proc(iproc, nelements_dbcsr_send)) THEN
    1099              :                            CALL cp__a(__SHORT_FILE__, __LINE__)
    1100              :                         END IF
    1101              :                      END IF
    1102              : 
    1103              :                      offset_recv_mepos = offset_per_proc(iproc) + n_packed_elements_per_proc(iproc)
    1104              :                      sm_block(1:nrows_local, icol_local) = recv_buffer(offset_recv_mepos + 1:offset_recv_mepos + nrows_local)
    1105              : 
    1106              :                      n_packed_elements_per_proc(iproc) = n_packed_elements_per_proc(iproc) + nrows_local
    1107            0 :                   END DO
    1108            0 :                END IF
    1109              :             END IF
    1110            0 :          END DO
    1111              : 
    1112              :          IF (debug_this_module) THEN
    1113              :             DO iproc = 0, nprocs - 1
    1114              :                IF (n_packed_elements_per_proc(iproc) /= siesta_struct%nelements_per_proc(iproc, nelements_dbcsr_send)) THEN
    1115              :                   CALL cp__a(__SHORT_FILE__, __LINE__)
    1116              :                END IF
    1117              :             END DO
    1118              :          END IF
    1119              : 
    1120              :          DEALLOCATE (recv_buffer)
    1121              :       END IF
    1122              : 
    1123              :       DEALLOCATE (offset_per_proc, n_packed_elements_per_proc)
    1124            0 : 
    1125              :       CALL timestop(handle)
    1126              :    END SUBROUTINE convert_distributed_siesta_to_dbcsr
    1127            0 : 
    1128              :    ! *** PRIVATE SUBROUTINES ***
    1129            0 : 
    1130            0 : ! **************************************************************************************************
    1131              : !> \brief Computes number of neighbour-list nodes on the current parallel process.
    1132              : !> \param nnodes_local              number of nodes [out]
    1133              : !> \param max_ijk_cell_image_local  largest index of cell images along i, j and k cell vectors
    1134              : !>                                  on this parallel process [out]
    1135              : !> \param max_ijk_cell_image        largest index of cell images along i, j and k cell vectors [inout]
    1136              : !> \param sab_nl                    pair-wise neighbour list [in]
    1137              : !> \param para_env                  MPI parallel environment [in]
    1138              : !> \param particle_coords           list of atomic coordinates subject to periodic boundary conditions [in]
    1139              : !> \param cell                      simulation unit cell [in]
    1140              : ! **************************************************************************************************
    1141              :    SUBROUTINE get_nnodes_local(nnodes_local, max_ijk_cell_image_local, max_ijk_cell_image, sab_nl, para_env, particle_coords, cell)
    1142              :       INTEGER, INTENT(out)                               :: nnodes_local
    1143              :       INTEGER, DIMENSION(3), INTENT(out)                 :: max_ijk_cell_image_local
    1144              :       INTEGER, DIMENSION(3), INTENT(inout)               :: max_ijk_cell_image
    1145            4 :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
    1146              :          INTENT(in), POINTER                             :: sab_nl
    1147              :       TYPE(mp_para_env_type), INTENT(in), POINTER        :: para_env
    1148              :       REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
    1149              :          INTENT(in)                                      :: particle_coords
    1150              :       TYPE(cell_type), INTENT(in), POINTER               :: cell
    1151              : 
    1152              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'get_nnodes_local'
    1153              : 
    1154              :       INTEGER                                            :: handle, iatom, icoord, jatom
    1155              :       INTEGER, DIMENSION(3)                              :: cell_ijk, max_ijk_cell_image_tmp
    1156              :       LOGICAL                                            :: update_ncells
    1157              :       REAL(kind=dp), DIMENSION(3)                        :: r_ij
    1158              :       TYPE(neighbor_list_iterator_p_type), &
    1159              :          DIMENSION(:), POINTER                           :: nl_iterator
    1160              : 
    1161              :       CALL timeset(routineN, handle)
    1162              : 
    1163            4 :       update_ncells = .FALSE.
    1164              :       DO icoord = 1, 3 ! x, y, z
    1165            4 :          IF (max_ijk_cell_image(icoord) >= 0) THEN
    1166              :             max_ijk_cell_image_tmp(icoord) = max_ijk_cell_image(icoord)
    1167            4 :          ELSE
    1168           16 :             max_ijk_cell_image_tmp(icoord) = HUGE(max_ijk_cell_image_tmp(icoord))
    1169           16 :             update_ncells = .TRUE.
    1170            4 :          END IF
    1171              :       END DO
    1172            8 : 
    1173            8 :       nnodes_local = 0
    1174              :       max_ijk_cell_image_local(:) = 0
    1175              : 
    1176              :       CALL neighbor_list_iterator_create(nl_iterator, sab_nl)
    1177            4 :       DO WHILE (neighbor_list_iterate(nl_iterator) == 0)
    1178            4 :          CALL get_iterator_info(nl_iterator, iatom=iatom, jatom=jatom, r=r_ij)
    1179              :          CALL get_negf_cell_ijk(cell_ijk, r_ij, r_i=particle_coords(1:3, iatom), r_j=particle_coords(1:3, jatom), cell=cell)
    1180            4 :          cell_ijk(1:3) = ABS(cell_ijk(1:3))
    1181        15164 : 
    1182        15160 :          IF (cell_ijk(1) <= max_ijk_cell_image_tmp(1) .AND. cell_ijk(2) <= max_ijk_cell_image_tmp(2) .AND. &
    1183        15160 :              cell_ijk(3) <= max_ijk_cell_image_tmp(3)) THEN
    1184        60640 :             nnodes_local = nnodes_local + 1
    1185              :             max_ijk_cell_image_local(1:3) = MAX(max_ijk_cell_image_local(1:3), cell_ijk(1:3))
    1186        15160 :          END IF
    1187            4 :       END DO
    1188        14888 :       CALL neighbor_list_iterator_release(nl_iterator)
    1189        59552 : 
    1190              :       IF (update_ncells) THEN
    1191              :          max_ijk_cell_image_tmp(1:3) = max_ijk_cell_image_local(1:3)
    1192            4 :          CALL para_env%max(max_ijk_cell_image_tmp)
    1193              :          DO icoord = 1, 3
    1194            4 :             IF (max_ijk_cell_image(icoord) < 0) THEN
    1195            4 :                max_ijk_cell_image(icoord) = max_ijk_cell_image_tmp(icoord)
    1196            4 :             END IF
    1197           16 :          END DO
    1198           16 :       END IF
    1199            8 : 
    1200              :       CALL timestop(handle)
    1201              :    END SUBROUTINE get_nnodes_local
    1202              : 
    1203              : ! **************************************************************************************************
    1204            4 : !> \brief Construct list of neighbour-list's nodes on the current parallel process.
    1205            4 : !> \param nl_local                  non-merged local neighbour-list's nodes [out]
    1206              : !> \param max_ijk_cell_image_local  largest index of cell images along i, j and k cell vectors
    1207              : !>                                  on this parallel process [in]
    1208              : !> \param max_ijk_cell_image        largest index of cell images along i, j and k cell vectors [in]
    1209              : !> \param sab_nl                    pair-wise neighbour list [in]
    1210              : !> \param particle_coords           list of atomic coordinates subject to periodic boundary conditions [in]
    1211              : !> \param cell                      simulation unit cell [in]
    1212              : !> \param cell_to_index             array to convert 3-D cell indices to 1-D DBCSR image indices
    1213              : !> \param do_merge                  merge cell images along transport direction [in]
    1214              : !> \param node_merged_indices       nodes-related indices. Nodes with identical indices will be merged [out]
    1215              : !> \param k_cells                   list of cell image indices along transport direction. Nodes to
    1216              : !>                be merged have the same 'node_merged_indices' but different 'k_cells' indices [out]
    1217              : ! **************************************************************************************************
    1218              :    SUBROUTINE get_nl_nodes_local(nl_local, max_ijk_cell_image_local, max_ijk_cell_image, sab_nl, particle_coords, &
    1219              :                                  cell, cell_to_index, do_merge, node_merged_indices, k_cells)
    1220              :       INTEGER, DIMENSION(:, :), INTENT(out)              :: nl_local
    1221              :       INTEGER, DIMENSION(3), INTENT(in)                  :: max_ijk_cell_image_local, &
    1222            4 :                                                             max_ijk_cell_image
    1223            4 :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
    1224              :          INTENT(in), POINTER                             :: sab_nl
    1225              :       REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
    1226              :          INTENT(in)                                      :: particle_coords
    1227              :       TYPE(cell_type), INTENT(in), POINTER               :: cell
    1228              :       INTEGER, DIMENSION(:, :, :), INTENT(in), POINTER   :: cell_to_index
    1229              :       LOGICAL, INTENT(in)                                :: do_merge
    1230              :       INTEGER(kind=int_8), DIMENSION(:), INTENT(out)     :: node_merged_indices
    1231              :       INTEGER, DIMENSION(:), INTENT(out)                 :: k_cells
    1232              : 
    1233              :       CHARACTER(len=*), PARAMETER :: routineN = 'get_nl_nodes_local'
    1234              : 
    1235              :       INTEGER                                            :: handle, iatom, icol_blk, image, inode, &
    1236              :                                                             irow_blk, jatom, natoms
    1237              :       INTEGER(kind=8), DIMENSION(2)                      :: ncells_siesta_local
    1238              :       INTEGER, DIMENSION(2)                              :: ncells_siesta
    1239              :       INTEGER, DIMENSION(3)                              :: cell_ijk_abs, cell_ijk_dbcsr, &
    1240              :                                                             cell_ijk_siesta
    1241              :       LOGICAL                                            :: do_symmetric
    1242              :       REAL(kind=dp), DIMENSION(3)                        :: r_ij
    1243              :       TYPE(neighbor_list_iterator_p_type), &
    1244              :          DIMENSION(:), POINTER                           :: nl_iterator
    1245              : 
    1246              :       CALL timeset(routineN, handle)
    1247              :       ! natoms only used to compute a merged 1D index of each DBCSR block
    1248            4 :       natoms = SIZE(particle_coords, 2)
    1249              :       CALL get_neighbor_list_set_p(neighbor_list_sets=sab_nl, symmetric=do_symmetric)
    1250            4 :       ncells_siesta(1:2) = 2*max_ijk_cell_image(1:2) + 1
    1251              : 
    1252            4 :       ncells_siesta_local(1:2) = INT(2*max_ijk_cell_image_local(1:2) + 1, kind=int_8)
    1253            4 : 
    1254           12 :       inode = 0
    1255              :       CALL neighbor_list_iterator_create(nl_iterator, sab_nl)
    1256           12 :       DO WHILE (neighbor_list_iterate(nl_iterator) == 0)
    1257              :          CALL get_iterator_info(nl_iterator, iatom=iatom, jatom=jatom, cell=cell_ijk_dbcsr, r=r_ij)
    1258            4 :          CALL get_negf_cell_ijk(cell_ijk_abs, r_ij, r_i=particle_coords(1:3, iatom), r_j=particle_coords(1:3, jatom), cell=cell)
    1259            4 : 
    1260        15164 :          IF (ABS(cell_ijk_abs(1)) <= max_ijk_cell_image(1) .AND. ABS(cell_ijk_abs(2)) <= max_ijk_cell_image(2) .AND. &
    1261        15160 :              ABS(cell_ijk_abs(3)) <= max_ijk_cell_image(3)) THEN
    1262        15160 : 
    1263              :             inode = inode + 1
    1264        15160 : 
    1265            4 :             image = get_index_by_cell(cell_ijk_dbcsr, cell_to_index)
    1266              :             CPASSERT(image > 0)
    1267        14888 :             nl_local(neighbor_list_dbcsr_image_index, inode) = image
    1268              : 
    1269        14888 :             IF (do_symmetric .AND. iatom > jatom) THEN
    1270        14888 :                irow_blk = jatom
    1271        14888 :                icol_blk = iatom
    1272              :                cell_ijk_abs(1:3) = -cell_ijk_abs(1:3)
    1273        14888 :             ELSE
    1274        28224 :                irow_blk = iatom
    1275        28224 :                icol_blk = jatom
    1276        28224 :             END IF
    1277              : 
    1278              :             nl_local(neighbor_list_iatom_index, inode) = irow_blk
    1279              :             nl_local(neighbor_list_jatom_index, inode) = icol_blk
    1280              : 
    1281              :             cell_ijk_siesta(1:3) = index_in_canonical_enumeration(cell_ijk_abs(1:3)) ! absolute -> SIESTA
    1282        14888 : 
    1283        14888 :             IF (do_merge) THEN
    1284              :                node_merged_indices(inode) = (((cell_ijk_siesta(2) - 1)*ncells_siesta_local(1) + cell_ijk_siesta(1) - 1)* &
    1285        59552 :                                      INT(natoms, kind=int_8) + icol_blk - 1)*INT(natoms, kind=int_8) + INT(irow_blk - 1, kind=int_8)
    1286              :                image = cell_ijk_siesta(1) + ncells_siesta(1)*(cell_ijk_siesta(2) - 1)
    1287        14888 :             ELSE
    1288              :                node_merged_indices(inode) = ((((cell_ijk_siesta(3) - 1)*ncells_siesta_local(2) + &
    1289            0 :                                                cell_ijk_siesta(2) - 1)*ncells_siesta_local(1) + cell_ijk_siesta(1) - 1)* &
    1290            0 :                                      INT(natoms, kind=int_8) + icol_blk - 1)*INT(natoms, kind=int_8) + INT(irow_blk - 1, kind=int_8)
    1291              :                image = cell_ijk_siesta(1) + ncells_siesta(1)*(cell_ijk_siesta(2) - 1 + ncells_siesta(2)*(cell_ijk_siesta(3) - 1))
    1292              :             END IF
    1293              :             k_cells(inode) = cell_ijk_siesta(3)
    1294        14888 :             nl_local(neighbor_list_siesta_image_index, inode) = image
    1295        14888 : 
    1296              :             IF (do_symmetric .AND. irow_blk /= icol_blk) THEN
    1297        14888 :                cell_ijk_abs(1:3) = -cell_ijk_abs(1:3)
    1298        14888 :                cell_ijk_siesta(1:3) = index_in_canonical_enumeration(cell_ijk_abs(1:3)) ! absolute -> SIESTA
    1299              :                IF (do_merge) cell_ijk_siesta(3) = 1
    1300        14888 :                nl_local(neighbor_list_siesta_transp_image_index, inode) = &
    1301        54960 :                   cell_ijk_siesta(1) + ncells_siesta(1)*(cell_ijk_siesta(2) - 1 + ncells_siesta(2)*(cell_ijk_siesta(3) - 1))
    1302        54960 :             ELSE
    1303        13740 :                nl_local(neighbor_list_siesta_transp_image_index, inode) = 0
    1304              :             END IF
    1305        13740 :          END IF
    1306              :       END DO
    1307         1148 :       CALL neighbor_list_iterator_release(nl_iterator)
    1308              : 
    1309              :       IF (debug_this_module) THEN
    1310              :          CPASSERT(SIZE(nl_local, 2) == inode)
    1311            4 :       END IF
    1312              : 
    1313              :       CALL timestop(handle)
    1314              :    END SUBROUTINE get_nl_nodes_local
    1315              : 
    1316              : ! **************************************************************************************************
    1317            4 : !> \brief Replicate (and optionally merge) pair-wise neighbour list.
    1318            4 : !> \param repl_nl                   replicated neighbour list. It needs to be deallocated elsewhere [allocated]
    1319              : !> \param n_dbcsr_cell_images_to_merge  number of merged blocks per neighbour-list node [allocated]
    1320              : !> \param dbcsr_cell_image_to_merge list of DBCSR image indices to merge [allocated]
    1321              : !> \param nnodes_per_proc           number of merged nodes on each parallel processes [out]
    1322              : !> \param max_ijk_cell_image        largest index of cell images along i, j and k cell vectors [inout]
    1323              : !> \param sab_nl                    pair-wise neighbour list [in]
    1324              : !> \param para_env                  MPI parallel environment [in]
    1325              : !> \param particle_coords           list of atomic coordinates subject to periodic boundary conditions [in]
    1326              : !> \param cell                      simulation unit cell [in]
    1327              : !> \param cell_to_index             array to convert 3-D cell indices to 1-D DBCSR image indices [in]
    1328              : !> \param do_merge                  merge cell images along transport direction [in]
    1329              : ! **************************************************************************************************
    1330              :    SUBROUTINE replicate_neighbour_list(repl_nl, n_dbcsr_cell_images_to_merge, dbcsr_cell_image_to_merge, &
    1331              :                                        nnodes_per_proc, max_ijk_cell_image, sab_nl, para_env, particle_coords, &
    1332              :                                        cell, cell_to_index, do_merge)
    1333              :       INTEGER, ALLOCATABLE, DIMENSION(:, :), &
    1334            4 :          INTENT(inout)                                   :: repl_nl
    1335            4 :       INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(inout)  :: n_dbcsr_cell_images_to_merge, &
    1336              :                                                             dbcsr_cell_image_to_merge
    1337              :       INTEGER, DIMENSION(0:), INTENT(out)                :: nnodes_per_proc
    1338              :       INTEGER, DIMENSION(3), INTENT(inout)               :: max_ijk_cell_image
    1339              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
    1340              :          INTENT(in), POINTER                             :: sab_nl
    1341              :       TYPE(mp_para_env_type), INTENT(in), POINTER        :: para_env
    1342              :       REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
    1343              :          INTENT(in)                                      :: particle_coords
    1344              :       TYPE(cell_type), INTENT(in), POINTER               :: cell
    1345              :       INTEGER, DIMENSION(:, :, :), INTENT(in), POINTER   :: cell_to_index
    1346              :       LOGICAL, INTENT(in)                                :: do_merge
    1347              : 
    1348              :       CHARACTER(len=*), PARAMETER :: routineN = 'replicate_neighbour_list'
    1349              : 
    1350              :       INTEGER                                            :: handle, inode, iproc, kcell_closest, &
    1351              :                                                             nnodes_local, nnodes_merged, &
    1352              :                                                             nnodes_repl, offset_inode
    1353              :       INTEGER(kind=int_8), ALLOCATABLE, DIMENSION(:)     :: node_merged_indices
    1354              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: inodes_orig, k_cells
    1355              :       INTEGER, ALLOCATABLE, DIMENSION(:, :)              :: nl_local
    1356              :       INTEGER, DIMENSION(3)                              :: max_ijk_cell_image_local
    1357            4 : 
    1358            4 :       CALL timeset(routineN, handle)
    1359            4 :       CPASSERT(.NOT. ALLOCATED(repl_nl))
    1360              :       CPASSERT(.NOT. ALLOCATED(n_dbcsr_cell_images_to_merge))
    1361              :       CPASSERT(.NOT. ALLOCATED(dbcsr_cell_image_to_merge))
    1362            4 : 
    1363            4 :       CALL get_nnodes_local(nnodes_local, max_ijk_cell_image_local, max_ijk_cell_image, sab_nl, para_env, particle_coords, cell)
    1364            4 : 
    1365            4 :       nnodes_per_proc(:) = 0
    1366              : 
    1367            4 :       IF (nnodes_local > 0) THEN
    1368              :          ALLOCATE (nl_local(neighbor_list_dim1, nnodes_local))
    1369           12 :          ALLOCATE (node_merged_indices(nnodes_local))
    1370              :          ALLOCATE (k_cells(nnodes_local))
    1371            4 :          CALL get_nl_nodes_local(nl_local, max_ijk_cell_image_local, max_ijk_cell_image, sab_nl, particle_coords, cell, &
    1372           12 :                                  cell_to_index, do_merge, node_merged_indices, k_cells)
    1373           12 : 
    1374           12 :          ALLOCATE (inodes_orig(nnodes_local))
    1375              :          CALL sort(node_merged_indices, nnodes_local, inodes_orig)
    1376            4 : 
    1377              :          nnodes_merged = 1
    1378            8 :          DO inode = 2, nnodes_local
    1379            4 :             IF (node_merged_indices(inode) > node_merged_indices(inode - 1)) nnodes_merged = nnodes_merged + 1
    1380              :          END DO
    1381            4 :       ELSE
    1382        14888 :          nnodes_merged = 0
    1383        14888 :       END IF
    1384              : 
    1385              :       nnodes_per_proc(para_env%mepos) = nnodes_merged
    1386              :       CALL para_env%sum(nnodes_per_proc)
    1387              : 
    1388              :       nnodes_repl = SUM(nnodes_per_proc(:))
    1389            4 :       ALLOCATE (repl_nl(neighbor_list_dim1, nnodes_repl))
    1390           20 : 
    1391              :       IF (nnodes_local > 0) THEN
    1392           12 :          IF (para_env%mepos > 0) THEN
    1393           12 :             offset_inode = SUM(nnodes_per_proc(0:para_env%mepos - 1))
    1394              :          ELSE
    1395            4 :             offset_inode = 0
    1396            4 :          END IF
    1397            4 : 
    1398              :          ALLOCATE (n_dbcsr_cell_images_to_merge(nnodes_merged))
    1399              :          ALLOCATE (dbcsr_cell_image_to_merge(nnodes_local))
    1400              :          n_dbcsr_cell_images_to_merge(:) = 0
    1401              : 
    1402           12 :          nnodes_merged = 1 !offset_inode + 1
    1403           12 :          repl_nl(:, offset_inode + 1) = nl_local(:, inodes_orig(1))
    1404            4 :          n_dbcsr_cell_images_to_merge(1) = 1
    1405              :          dbcsr_cell_image_to_merge(1) = nl_local(neighbor_list_dbcsr_image_index, inodes_orig(1))
    1406            4 :          kcell_closest = k_cells(inodes_orig(1))
    1407           24 :          DO inode = 2, nnodes_local
    1408            4 :             IF (node_merged_indices(inode) > node_merged_indices(inode - 1)) THEN
    1409            4 :                nnodes_merged = nnodes_merged + 1
    1410            4 :                repl_nl(:, offset_inode + nnodes_merged) = nl_local(:, inodes_orig(inode))
    1411        14888 :                !n_dbcsr_cell_images_to_merge(nnodes_merged) = 1
    1412        14884 :                kcell_closest = k_cells(inodes_orig(inode))
    1413        14884 :             ELSE
    1414        89304 :                IF (ABS(k_cells(inodes_orig(inode))) < ABS(kcell_closest) .OR. &
    1415              :                    (ABS(k_cells(inodes_orig(inode))) == ABS(kcell_closest) .AND. kcell_closest < 0)) THEN
    1416        14884 :                   repl_nl(:, offset_inode + nnodes_merged) = nl_local(:, inodes_orig(inode))
    1417              :                   kcell_closest = k_cells(inodes_orig(inode))
    1418            0 :                END IF
    1419            0 :             END IF
    1420            0 :             dbcsr_cell_image_to_merge(inode) = nl_local(neighbor_list_dbcsr_image_index, inodes_orig(inode))
    1421            0 :             n_dbcsr_cell_images_to_merge(nnodes_merged) = n_dbcsr_cell_images_to_merge(nnodes_merged) + 1
    1422              :          END DO
    1423              : 
    1424        14884 :          IF (debug_this_module) THEN
    1425        14888 :             CPASSERT(SUM(n_dbcsr_cell_images_to_merge) == nnodes_local)
    1426              :          END IF
    1427              : 
    1428              :          DEALLOCATE (inodes_orig)
    1429              :          DEALLOCATE (node_merged_indices, k_cells)
    1430              :          DEALLOCATE (nl_local)
    1431              :       END IF
    1432            4 : 
    1433            4 :       IF (para_env%num_pe > 1) THEN
    1434            4 :          offset_inode = 0
    1435              :          DO iproc = 0, para_env%num_pe - 1
    1436              :             IF (nnodes_per_proc(iproc) > 0) THEN
    1437            4 :                CALL para_env%bcast(repl_nl(:, offset_inode + 1:offset_inode + nnodes_per_proc(iproc)), iproc)
    1438            4 :                offset_inode = offset_inode + nnodes_per_proc(iproc)
    1439           12 :             END IF
    1440           12 :          END DO
    1441            8 :       END IF
    1442            8 : 
    1443              :       CALL timestop(handle)
    1444              :    END SUBROUTINE replicate_neighbour_list
    1445              : 
    1446              : ! **************************************************************************************************
    1447            4 : !> \brief Count number of DBCSR matrix elements that should be received from (*,nelements_dbcsr_recv)
    1448            8 : !>        and send to (*,nelements_dbcsr_send) each parallel process.
    1449              : !> \param nelements_per_proc number of non-zero matrix elements for each MPI process
    1450              : !> \param nnodes_per_proc    number of non-zero DBCSR matrix blocks (neighbour-list nodes)
    1451              : !> \param nl_repl            replicated neighbour list
    1452              : !> \param matrix_dbcsr_kp    DBCSR matrix
    1453              : !> \param symmetric          whether the DBCSR matrix is a symmetric one
    1454              : !> \param para_env           parallel environment
    1455              : !> \param gather_root        if >=0, gather all non-zero matrix element on the MPI process with
    1456              : !>                           gather_root rank (useful for bulk transport calculation).
    1457              : !>                           If <0, distribute non-zero matrix element across all MPI processes
    1458              : ! **************************************************************************************************
    1459              :    SUBROUTINE count_remote_dbcsr_elements(nelements_per_proc, nnodes_per_proc, nl_repl, matrix_dbcsr_kp, &
    1460              :                                           symmetric, para_env, gather_root)
    1461              :       INTEGER(kind=int_8), DIMENSION(0:, :), INTENT(out) :: nelements_per_proc
    1462              :       INTEGER, DIMENSION(0:), INTENT(in)                 :: nnodes_per_proc
    1463            4 :       INTEGER, DIMENSION(:, :), INTENT(in)               :: nl_repl
    1464              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(in)       :: matrix_dbcsr_kp
    1465              :       LOGICAL, INTENT(in)                                :: symmetric
    1466              :       TYPE(mp_para_env_type), INTENT(in), POINTER        :: para_env
    1467              :       INTEGER, INTENT(in)                                :: gather_root
    1468              : 
    1469              :       CHARACTER(len=*), PARAMETER :: routineN = 'count_remote_dbcsr_elements'
    1470              : 
    1471              :       INTEGER :: first_row_minus_one, handle, icol_blk, image, image_transp, inode, inode_proc, &
    1472              :          iproc, iproc_orb, irow_blk, irow_local, mepos, ncols_blk, ncols_local, nnodes_proc, &
    1473              :          nprocs, nrows_blk, nrows_local, offset_inode
    1474              :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_offset, col_blk_size, &
    1475              :                                                             row_blk_offset, row_blk_size
    1476              :       LOGICAL                                            :: do_distribute
    1477              : 
    1478            4 :       CALL timeset(routineN, handle)
    1479            4 :       nelements_per_proc(:, :) = 0
    1480              :       mepos = para_env%mepos
    1481              :       nprocs = para_env%num_pe
    1482            4 :       do_distribute = gather_root < 0
    1483           28 :       IF (debug_this_module) THEN
    1484            4 :          CPASSERT(SIZE(nnodes_per_proc) == nprocs)
    1485            4 :       END IF
    1486            4 : 
    1487              :       CALL dbcsr_get_info(matrix=matrix_dbcsr_kp(1)%matrix, &
    1488              :                           nblkrows_total=nrows_blk, nblkcols_total=ncols_blk, &
    1489              :                           row_blk_size=row_blk_size, col_blk_size=col_blk_size, &
    1490              :                           row_blk_offset=row_blk_offset, col_blk_offset=col_blk_offset)
    1491              : 
    1492              :       offset_inode = 0
    1493              :       iproc_orb = gather_root ! if do_distribute == .FALSE., send all matrix elements to MPI process with rank gather_root
    1494            4 :       DO iproc = LBOUND(nnodes_per_proc, 1), UBOUND(nnodes_per_proc, 1)
    1495              :          nnodes_proc = nnodes_per_proc(iproc)
    1496            4 :          DO inode_proc = 1, nnodes_proc
    1497            4 :             inode = inode_proc + offset_inode
    1498           16 : 
    1499            8 :             irow_blk = nl_repl(neighbor_list_iatom_index, inode)
    1500        29784 :             icol_blk = nl_repl(neighbor_list_jatom_index, inode)
    1501        29776 :             CPASSERT(irow_blk <= icol_blk .OR. (.NOT. symmetric))
    1502              :             image = nl_repl(neighbor_list_siesta_image_index, inode)
    1503        29776 :             image_transp = nl_repl(neighbor_list_siesta_transp_image_index, inode)
    1504        29776 : 
    1505        29776 :             IF (image > 0) THEN
    1506        29776 :                nrows_local = row_blk_size(irow_blk)
    1507        29776 :                first_row_minus_one = row_blk_offset(irow_blk) - 1
    1508              :                ncols_local = col_blk_size(icol_blk)
    1509        29776 :                DO irow_local = 1, nrows_local
    1510        29776 :                   IF (do_distribute) THEN
    1511        29776 : #if defined(__SMEAGOL)
    1512        29776 :                      CALL WhichNodeOrb(irow_local + first_row_minus_one, nprocs, iproc_orb)
    1513       297760 : #else
    1514       267984 :                      CALL cp_abort(__LOCATION__, &
    1515              :                                    "CP2K was compiled with no SMEAGOL support.")
    1516            0 : #endif
    1517              :                   END IF
    1518              :                   IF (iproc_orb == mepos) THEN
    1519              :                      nelements_per_proc(iproc, nelements_dbcsr_recv) = nelements_per_proc(iproc, nelements_dbcsr_recv) + &
    1520              :                                                                        ncols_local
    1521              :                   END IF
    1522       267984 : 
    1523              :                   IF (iproc == mepos) THEN
    1524       133992 :                      nelements_per_proc(iproc_orb, nelements_dbcsr_send) = nelements_per_proc(iproc_orb, nelements_dbcsr_send) + &
    1525              :                                                                            ncols_local
    1526              :                   END IF
    1527       297760 :                END DO
    1528              :             END IF
    1529       133992 : 
    1530              :             ! transposed block
    1531              :             IF (image_transp > 0) THEN
    1532              :                nrows_local = col_blk_size(icol_blk)
    1533              :                first_row_minus_one = col_blk_offset(icol_blk) - 1
    1534              :                ncols_local = row_blk_size(irow_blk)
    1535        29784 :                DO irow_local = 1, nrows_local
    1536        27480 :                   IF (do_distribute) THEN
    1537        27480 : #if defined(__SMEAGOL)
    1538        27480 :                      CALL WhichNodeOrb(irow_local + first_row_minus_one, nprocs, iproc_orb)
    1539       274800 : #else
    1540       247320 :                      CALL cp_abort(__LOCATION__, &
    1541              :                                    "CP2K was compiled with no SMEAGOL support.")
    1542            0 : #endif
    1543              :                   END IF
    1544              :                   IF (iproc_orb == mepos) THEN
    1545              :                      nelements_per_proc(iproc, nelements_dbcsr_recv) = nelements_per_proc(iproc, nelements_dbcsr_recv) + &
    1546              :                                                                        ncols_local
    1547              :                   END IF
    1548       247320 : 
    1549              :                   IF (iproc == mepos) THEN
    1550       123660 :                      nelements_per_proc(iproc_orb, nelements_dbcsr_send) = nelements_per_proc(iproc_orb, nelements_dbcsr_send) + &
    1551              :                                                                            ncols_local
    1552              :                   END IF
    1553       274800 :                END DO
    1554              :             END IF
    1555       123660 :          END DO
    1556              :          offset_inode = offset_inode + nnodes_proc
    1557              :       END DO
    1558              :       CALL timestop(handle)
    1559              :    END SUBROUTINE count_remote_dbcsr_elements
    1560           12 : 
    1561              : ! **************************************************************************************************
    1562            4 : !> \brief Construct list of non-zero matrix elements' indices in SIESTA format.
    1563            4 : !> \param n_nonzero_cols  number of non-zero matrix elements on each matrix row local to the current
    1564              : !>                        MPI process
    1565              : !> \param row_offset      offset of the first non-zero matrix elements for each locally-stores row
    1566              : !> \param col_index       sorted list of column indices of non-zero matrix element
    1567              : !> \param packed_index    original order of non-sorted column indices
    1568              : !> \param nl_repl         replicated neighbour list
    1569              : !> \param matrix_dbcsr_kp DBCSR matrix
    1570              : !> \param symmetric       whether the DBCSR matrix is a symmetric one
    1571              : !> \param para_env        parallel environment
    1572              : !> \param gather_root     if >=0, gather all non-zero matrix element on the MPI process with
    1573              : !>                        gather_root rank (useful for bulk transport calculation).
    1574              : !>                        If <0, distribute non-zero matrix element across all MPI processes
    1575              : ! **************************************************************************************************
    1576              :    SUBROUTINE get_nonzero_element_indices(n_nonzero_cols, row_offset, col_index, packed_index, &
    1577              :                                           nl_repl, matrix_dbcsr_kp, symmetric, para_env, gather_root)
    1578              :       INTEGER, DIMENSION(:), INTENT(out)                 :: n_nonzero_cols, row_offset, col_index, &
    1579              :                                                             packed_index
    1580            8 :       INTEGER, DIMENSION(:, :), INTENT(in)               :: nl_repl
    1581            4 :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(in)       :: matrix_dbcsr_kp
    1582              :       LOGICAL, INTENT(in)                                :: symmetric
    1583              :       TYPE(mp_para_env_type), INTENT(in), POINTER        :: para_env
    1584              :       INTEGER, INTENT(in)                                :: gather_root
    1585              : 
    1586              :       CHARACTER(len=*), PARAMETER :: routineN = 'get_nonzero_element_indices'
    1587              : 
    1588              :       INTEGER :: first_col_minus_one, first_row_minus_one, handle, icol_blk, icol_local, &
    1589              :          icol_offset, image, image_transp, inode, irow_blk, irow_local, irow_proc, mepos, &
    1590              :          ncols_blk, ncols_local, ncols_total, nnodes, nprocs, nrows_blk, nrows_local, nrows_total
    1591              :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_offset, col_blk_size, &
    1592              :                                                             row_blk_offset, row_blk_size
    1593              :       LOGICAL                                            :: do_distribute, is_root_rank
    1594              : 
    1595            4 :       CALL timeset(routineN, handle)
    1596            4 :       n_nonzero_cols(:) = 0
    1597              :       mepos = para_env%mepos
    1598              :       nprocs = para_env%num_pe
    1599            4 :       do_distribute = gather_root < 0
    1600          618 :       is_root_rank = gather_root == mepos
    1601            4 : 
    1602            4 :       CALL dbcsr_get_info(matrix=matrix_dbcsr_kp(1)%matrix, &
    1603            4 :                           nblkrows_total=nrows_blk, nblkcols_total=ncols_blk, &
    1604            4 :                           nfullrows_total=nrows_total, nfullcols_total=ncols_total, &
    1605              :                           row_blk_size=row_blk_size, col_blk_size=col_blk_size, &
    1606              :                           row_blk_offset=row_blk_offset, col_blk_offset=col_blk_offset)
    1607              : 
    1608              :       nnodes = SIZE(nl_repl, 2)
    1609              :       DO inode = 1, nnodes
    1610            4 :          irow_blk = nl_repl(neighbor_list_iatom_index, inode)
    1611              :          icol_blk = nl_repl(neighbor_list_jatom_index, inode)
    1612            4 :          CPASSERT(irow_blk <= icol_blk .OR. (.NOT. symmetric))
    1613        29780 :          image = nl_repl(neighbor_list_siesta_image_index, inode)
    1614        29776 :          image_transp = nl_repl(neighbor_list_siesta_transp_image_index, inode)
    1615        29776 : 
    1616        29776 :          IF (image > 0) THEN
    1617        29776 :             nrows_local = row_blk_size(irow_blk)
    1618        29776 :             first_row_minus_one = row_blk_offset(irow_blk) - 1
    1619              :             ncols_local = col_blk_size(icol_blk)
    1620        29776 :             DO irow_local = 1, nrows_local
    1621        29776 :                IF (do_distribute) THEN
    1622        29776 : #if defined(__SMEAGOL)
    1623        29776 :                   CALL GlobalToLocalOrb(irow_local + first_row_minus_one, mepos, nprocs, irow_proc)
    1624       297760 : #else
    1625       267984 :                   CALL cp_abort(__LOCATION__, &
    1626              :                                 "CP2K was compiled with no SMEAGOL support.")
    1627            0 : #endif
    1628              :                ELSE
    1629              :                   IF (is_root_rank) THEN
    1630              :                      irow_proc = irow_local + first_row_minus_one
    1631              :                   ELSE
    1632              :                      irow_proc = 0
    1633       267984 :                   END IF
    1634       133992 :                END IF
    1635              :                IF (irow_proc > 0) THEN
    1636       133992 :                   n_nonzero_cols(irow_proc) = n_nonzero_cols(irow_proc) + ncols_local
    1637              :                END IF
    1638              :             END DO
    1639       297760 :          END IF
    1640       133992 : 
    1641              :          ! transposed block
    1642              :          IF (image_transp > 0) THEN
    1643              :             nrows_local = col_blk_size(icol_blk)
    1644              :             first_row_minus_one = col_blk_offset(icol_blk) - 1
    1645              :             ncols_local = row_blk_size(irow_blk)
    1646        29780 :             DO irow_local = 1, nrows_local
    1647        27480 :                IF (do_distribute) THEN
    1648        27480 : #if defined(__SMEAGOL)
    1649        27480 :                   CALL GlobalToLocalOrb(irow_local + first_row_minus_one, mepos, nprocs, irow_proc)
    1650       274800 : #else
    1651       247320 :                   CALL cp_abort(__LOCATION__, &
    1652              :                                 "CP2K was compiled with no SMEAGOL support.")
    1653            0 : #endif
    1654              :                ELSE
    1655              :                   IF (is_root_rank) THEN
    1656              :                      irow_proc = irow_local + first_row_minus_one
    1657              :                   ELSE
    1658              :                      irow_proc = 0
    1659       247320 :                   END IF
    1660       123660 :                END IF
    1661              :                IF (irow_proc > 0) THEN
    1662       123660 :                   n_nonzero_cols(irow_proc) = n_nonzero_cols(irow_proc) + ncols_local
    1663              :                END IF
    1664              :             END DO
    1665       274800 :          END IF
    1666       123660 :       END DO
    1667              : 
    1668              :       row_offset(1) = 0
    1669              :       DO irow_local = 1, SIZE(n_nonzero_cols) - 1
    1670              :          row_offset(irow_local + 1) = row_offset(irow_local) + n_nonzero_cols(irow_local)
    1671              :       END DO
    1672            4 : 
    1673          614 :       n_nonzero_cols(:) = 0
    1674          614 :       col_index(:) = 0
    1675              :       DO inode = 1, nnodes
    1676              :          irow_blk = nl_repl(neighbor_list_iatom_index, inode)
    1677          618 :          icol_blk = nl_repl(neighbor_list_jatom_index, inode)
    1678      2318874 :          CPASSERT(irow_blk <= icol_blk .OR. (.NOT. symmetric))
    1679        29780 :          image = nl_repl(neighbor_list_siesta_image_index, inode)
    1680        29776 :          image_transp = nl_repl(neighbor_list_siesta_transp_image_index, inode)
    1681        29776 : 
    1682        29776 :          IF (image > 0) THEN
    1683        29776 :             nrows_local = row_blk_size(irow_blk)
    1684        29776 :             first_row_minus_one = row_blk_offset(irow_blk) - 1
    1685              :             ncols_local = col_blk_size(icol_blk)
    1686        29776 :             first_col_minus_one = col_blk_offset(icol_blk) + (image - 1)*ncols_total - 1
    1687        29776 :             DO irow_local = 1, nrows_local
    1688        29776 :                IF (do_distribute) THEN
    1689        29776 : #if defined(__SMEAGOL)
    1690        29776 :                   CALL GlobalToLocalOrb(irow_local + first_row_minus_one, mepos, nprocs, irow_proc)
    1691       297760 : #else
    1692       267984 :                   CALL cp_abort(__LOCATION__, &
    1693              :                                 "CP2K was compiled with no SMEAGOL support.")
    1694            0 : #endif
    1695              :                ELSE
    1696              :                   IF (is_root_rank) THEN
    1697              :                      irow_proc = irow_local + first_row_minus_one
    1698              :                   ELSE
    1699              :                      irow_proc = 0
    1700       267984 :                   END IF
    1701       133992 :                END IF
    1702              :                IF (irow_proc > 0) THEN
    1703       133992 :                   icol_offset = row_offset(irow_proc) + n_nonzero_cols(irow_proc)
    1704              :                   DO icol_local = 1, ncols_local
    1705              :                      col_index(icol_offset + icol_local) = first_col_minus_one + icol_local
    1706       297760 :                   END DO
    1707       133992 :                   n_nonzero_cols(irow_proc) = n_nonzero_cols(irow_proc) + ncols_local
    1708      1339920 :                END IF
    1709      1339920 :             END DO
    1710              :          END IF
    1711       133992 : 
    1712              :          ! transposed block
    1713              :          IF (image_transp > 0) THEN
    1714              :             nrows_local = col_blk_size(icol_blk)
    1715              :             first_row_minus_one = col_blk_offset(icol_blk) - 1
    1716              :             ncols_local = row_blk_size(irow_blk)
    1717        29780 :             first_col_minus_one = row_blk_offset(irow_blk) + (image_transp - 1)*nrows_total - 1
    1718        27480 :             DO irow_local = 1, nrows_local
    1719        27480 :                IF (do_distribute) THEN
    1720        27480 : #if defined(__SMEAGOL)
    1721        27480 :                   CALL GlobalToLocalOrb(irow_local + first_row_minus_one, mepos, nprocs, irow_proc)
    1722       274800 : #else
    1723       247320 :                   CALL cp_abort(__LOCATION__, &
    1724              :                                 "CP2K was compiled with no SMEAGOL support.")
    1725            0 : #endif
    1726              :                ELSE
    1727              :                   IF (is_root_rank) THEN
    1728              :                      irow_proc = irow_local + first_row_minus_one
    1729              :                   ELSE
    1730              :                      irow_proc = 0
    1731       247320 :                   END IF
    1732       123660 :                END IF
    1733              :                IF (irow_proc > 0) THEN
    1734       123660 :                   icol_offset = row_offset(irow_proc) + n_nonzero_cols(irow_proc)
    1735              :                   DO icol_local = 1, ncols_local
    1736              :                      col_index(icol_offset + icol_local) = first_col_minus_one + icol_local
    1737       274800 :                   END DO
    1738       123660 :                   n_nonzero_cols(irow_proc) = n_nonzero_cols(irow_proc) + ncols_local
    1739      1236600 :                END IF
    1740      1236600 :             END DO
    1741              :          END IF
    1742       123660 :       END DO
    1743              : 
    1744              :       IF (SIZE(n_nonzero_cols) > 0) THEN
    1745              :          DO irow_local = 1, SIZE(n_nonzero_cols)
    1746              :             CALL sort(col_index(row_offset(irow_local) + 1:row_offset(irow_local) + n_nonzero_cols(irow_local)), &
    1747              :                       n_nonzero_cols(irow_local), &
    1748            4 :                       packed_index(row_offset(irow_local) + 1:row_offset(irow_local) + n_nonzero_cols(irow_local)))
    1749          618 :          END DO
    1750              :       END IF
    1751              : 
    1752          618 :       CALL timestop(handle)
    1753              :    END SUBROUTINE get_nonzero_element_indices
    1754              : 
    1755              : ! **************************************************************************************************
    1756            4 : !> \brief Get absolute i, j, and k indices of cell image for the given DBCSR matrix block.
    1757            4 : !>        Effective cell image recorded in the neighbour-list (where the matrix block is actually
    1758              : !>        stored) depends on atomic coordinates can be significantly different.
    1759              : !> \param cell_ijk      array with 3 indices along the cell's vectors
    1760              : !> \param r_ij          actual interatomic distance (vector R_j - r_i), where R_j is the coordinates
    1761              : !>                      of the j-th atom in the supercell
    1762              : !> \param r_i           coordinates of the i-th atom in the primary unit cell
    1763              : !> \param r_j           coordinates of the j-th atom in the primary unit cell
    1764              : !> \param cell          unit cell
    1765              : ! **************************************************************************************************
    1766              :    SUBROUTINE get_negf_cell_ijk(cell_ijk, r_ij, r_i, r_j, cell)
    1767              :       INTEGER, DIMENSION(3), INTENT(out)                 :: cell_ijk
    1768              :       REAL(kind=dp), DIMENSION(3), INTENT(in)            :: r_ij, r_i, r_j
    1769              :       TYPE(cell_type), INTENT(in), POINTER               :: cell
    1770        30320 : 
    1771              :       REAL(kind=dp), DIMENSION(3)                        :: coords_scaled, r
    1772              : 
    1773              :       r(:) = r_ij(:) + r_i(:) - r_j(:)
    1774              :       CALL real_to_scaled(coords_scaled, r, cell)
    1775              :       cell_ijk(:) = NINT(coords_scaled(:))
    1776              :    END SUBROUTINE get_negf_cell_ijk
    1777       121280 : 
    1778        30320 : ! **************************************************************************************************
    1779       121280 : !> \brief Return the index of an integer number in the sequence 0, 1, -1, ..., n, -n, ...
    1780        30320 : !>        (canonical enumeration of integers, oeis.org/A001057).
    1781              : !> \param  inum     integer number [in]
    1782              : !> \return index of 'inum' in A001057
    1783              : !> \note Cell images in SMEAGOL / SIESTA are ordered according to A001057. Therefore this
    1784              : !>       function converts the absolute index of a cell replica along some (x/y/z) dimension
    1785              : !>       into its corresponding SIESTA's index.
    1786              : ! **************************************************************************************************
    1787              :    ELEMENTAL FUNCTION index_in_canonical_enumeration(inum) RESULT(ind)
    1788              :       INTEGER, INTENT(in)                                :: inum
    1789              :       INTEGER                                            :: ind
    1790              : 
    1791        85884 :       INTEGER                                            :: inum_abs, is_non_positive
    1792              : 
    1793              :       inum_abs = ABS(inum)
    1794              :       !IF (inum <= 0) THEN; is_non_positive = 1; ELSE; is_non_positive = 0; END IF
    1795              :       is_non_positive = MERGE(1, 0, inum <= 0)
    1796              : 
    1797        85884 :       ! inum =  0 -> inum_abs = 0, is_non_positive = 1 -> ind = 1
    1798              :       ! inum =  1 -> inum_abs = 1, is_non_positive = 0 -> ind = 2
    1799        85884 :       ! inum = -1 -> inum_abs = 1, is_non_positive = 1 -> ind = 3
    1800              :       ind = 2*inum_abs + is_non_positive
    1801              :    END FUNCTION index_in_canonical_enumeration
    1802              : 
    1803              : ! **************************************************************************************************
    1804        85884 : !> \brief Return an integer number according to its index in the sequence 0, 1, -1, ..., n, -n, ...
    1805        85884 : !>        (canonical enumeration of integers, oeis.org/A001057)
    1806              : !> \param  ind       index in A001057 starting from 1
    1807              : !> \return integer number according to its position 'ind' in A001057
    1808              : !> \note Cell images in SMEAGOL / SIESTA are ordered according to A001057. Therefore this
    1809              : !>       function converts SIESTA's index of a cell replica along some (x/y/z) dimension
    1810              : !>       into the corresponding absolute index.
    1811              : ! **************************************************************************************************
    1812              :    ELEMENTAL FUNCTION number_from_canonical_enumeration(ind) RESULT(inum)
    1813              :       INTEGER, INTENT(in)                                :: ind
    1814              :       INTEGER                                            :: inum
    1815              : 
    1816          372 :       ! ind < 1 is invalid
    1817              :       ! ind = 1 -> SIGN(0, -1) =  0
    1818              :       ! ind = 2 -> SIGN(1,  0) =  1
    1819              :       ! ind = 3 -> SIGN(1, -1) = -1
    1820              :       inum = SIGN(ind/2, -MOD(ind, 2))
    1821              :    END FUNCTION number_from_canonical_enumeration
    1822              : 
    1823              : ! **************************************************************************************************
    1824          372 : !> \brief   Apply periodic boundary conditions defined by a simulation cell to a position
    1825          372 : !>          vector r. Similar to pbc1 from cell_types.F but returns unscaled coordinates from
    1826              : !>          the scaled range [0, 1) instead of [-0.5, 0.5)
    1827              : !> \param r_pbc   position vector subject to the periodic boundary conditions [out]
    1828              : !> \param r       initial position vector [in]
    1829              : !> \param cell    simulation unit cell [in]
    1830              : ! **************************************************************************************************
    1831              :    PURE SUBROUTINE pbc_0_1(r_pbc, r, cell)
    1832              :       REAL(KIND=dp), DIMENSION(3), INTENT(out)           :: r_pbc
    1833              :       REAL(KIND=dp), DIMENSION(3), INTENT(in)            :: r
    1834              :       TYPE(cell_type), INTENT(in), POINTER               :: cell
    1835          136 : 
    1836              :       REAL(KIND=dp), DIMENSION(3)                        :: s
    1837              : 
    1838              :       IF (cell%orthorhombic) THEN
    1839              :          r_pbc(1) = r(1) - cell%hmat(1, 1)*cell%perd(1)*REAL(FLOOR(cell%h_inv(1, 1)*r(1)), dp)
    1840              :          r_pbc(2) = r(2) - cell%hmat(2, 2)*cell%perd(2)*REAL(FLOOR(cell%h_inv(2, 2)*r(2)), dp)
    1841              :          r_pbc(3) = r(3) - cell%hmat(3, 3)*cell%perd(3)*REAL(FLOOR(cell%h_inv(3, 3)*r(3)), dp)
    1842          136 :       ELSE
    1843          136 :          s(1) = cell%h_inv(1, 1)*r(1) + cell%h_inv(1, 2)*r(2) + cell%h_inv(1, 3)*r(3)
    1844          136 :          s(2) = cell%h_inv(2, 1)*r(1) + cell%h_inv(2, 2)*r(2) + cell%h_inv(2, 3)*r(3)
    1845          136 :          s(3) = cell%h_inv(3, 1)*r(1) + cell%h_inv(3, 2)*r(2) + cell%h_inv(3, 3)*r(3)
    1846              :          s(1) = s(1) - cell%perd(1)*REAL(FLOOR(s(1)), dp)
    1847            0 :          s(2) = s(2) - cell%perd(2)*REAL(FLOOR(s(2)), dp)
    1848            0 :          s(3) = s(3) - cell%perd(3)*REAL(FLOOR(s(3)), dp)
    1849            0 :          r_pbc(1) = cell%hmat(1, 1)*s(1) + cell%hmat(1, 2)*s(2) + cell%hmat(1, 3)*s(3)
    1850            0 :          r_pbc(2) = cell%hmat(2, 1)*s(1) + cell%hmat(2, 2)*s(2) + cell%hmat(2, 3)*s(3)
    1851            0 :          r_pbc(3) = cell%hmat(3, 1)*s(1) + cell%hmat(3, 2)*s(2) + cell%hmat(3, 3)*s(3)
    1852            0 :       END IF
    1853            0 :    END SUBROUTINE pbc_0_1
    1854            0 : 
    1855            0 : ! **************************************************************************************************
    1856              : !> \brief Computes the number of send requests from this MPI process to all the other processes.
    1857          136 : !>        Alternatively computes the number of recv requests per MPI process that the given process
    1858              : !>        expects.
    1859              : !> \param mepos                    MPI rank of the given process
    1860              : !> \param nelements_per_proc       number of element to send / receive
    1861              : !> \param max_nelements_per_packet maximum number of elements per single MPI request
    1862              : !> \return number of MPI requests
    1863              : ! **************************************************************************************************
    1864              :    PURE FUNCTION get_number_of_mpi_sendrecv_requests(mepos, nelements_per_proc, max_nelements_per_packet) RESULT(nrequests)
    1865              :       INTEGER, INTENT(in)                                :: mepos
    1866              :       INTEGER(kind=int_8), DIMENSION(0:), INTENT(in)     :: nelements_per_proc
    1867              :       INTEGER(kind=int_8), INTENT(in)                    :: max_nelements_per_packet
    1868           32 :       INTEGER                                            :: nrequests
    1869              : 
    1870              :       INTEGER                                            :: iproc
    1871              : 
    1872              :       nrequests = 0
    1873              :       DO iproc = LBOUND(nelements_per_proc, 1), UBOUND(nelements_per_proc, 1)
    1874              :          ! there is no need to send data to the same MPI process
    1875              :          IF (iproc /= mepos) THEN
    1876           32 :             nrequests = nrequests + INT(nelements_per_proc(iproc)/max_nelements_per_packet)
    1877          128 :             IF (MOD(nelements_per_proc(iproc), max_nelements_per_packet) > 0) THEN
    1878              :                nrequests = nrequests + 1
    1879           96 :             END IF
    1880           32 :          END IF
    1881           32 :       END DO
    1882           16 :    END FUNCTION get_number_of_mpi_sendrecv_requests
    1883              : 
    1884              : ! **************************************************************************************************
    1885              : !> \brief Map non-zero matrix elements on to MPI requests.
    1886           32 : !> \param element_offset           offset (index-1) of the first element [out]
    1887              : !> \param nelements_per_request    number of element for each request [out]
    1888              : !> \param peer_rank                rank of a peering MPI process
    1889              : !> \param tag                      MPI tag
    1890              : !> \param mepos                    MPI rank of a given MPI process
    1891              : !> \param nelements_per_proc       number of element to send / receive by the current MPI process
    1892              : !> \param max_nelements_per_packet maximum number of elements per single MPI request
    1893              : ! **************************************************************************************************
    1894              :    SUBROUTINE assign_nonzero_elements_to_requests(element_offset, nelements_per_request, peer_rank, tag, &
    1895              :                                                   mepos, nelements_per_proc, max_nelements_per_packet)
    1896              :       INTEGER(kind=int_8), DIMENSION(:), INTENT(out)     :: element_offset, nelements_per_request
    1897              :       INTEGER, DIMENSION(:), INTENT(out)                 :: peer_rank, tag
    1898           16 :       INTEGER, INTENT(in)                                :: mepos
    1899           16 :       INTEGER(kind=int_8), DIMENSION(0:), INTENT(in)     :: nelements_per_proc
    1900              :       INTEGER(kind=int_8), INTENT(in)                    :: max_nelements_per_packet
    1901              : 
    1902              :       INTEGER                                            :: iproc, irequest, nrequests, &
    1903              :                                                             request_offset
    1904              :       INTEGER(kind=int_8)                                :: element_offset_tmp, nelements
    1905              : 
    1906              :       request_offset = 0
    1907              :       element_offset_tmp = 0
    1908              :       DO iproc = LBOUND(nelements_per_proc, 1), UBOUND(nelements_per_proc, 1)
    1909              :          IF (iproc /= mepos) THEN
    1910           16 :             nrequests = INT(nelements_per_proc(iproc)/max_nelements_per_packet)
    1911           16 :             IF (MOD(nelements_per_proc(iproc), max_nelements_per_packet) > 0) nrequests = nrequests + 1
    1912           64 :             CPASSERT(nrequests <= max_mpi_rank + 1)
    1913           32 :             IF (nrequests > 0) THEN
    1914           16 :                nelements = nelements_per_proc(iproc)/nrequests
    1915           16 :                IF (nelements_per_proc(iproc) - nelements*nrequests > 0) nelements = nelements + 1
    1916           16 :                CPASSERT(nelements <= max_nelements_per_packet)
    1917           16 : 
    1918           16 :                DO irequest = 1, nrequests
    1919           16 :                   element_offset(request_offset + irequest) = (irequest - 1)*nelements + element_offset_tmp
    1920           16 :                   IF (irequest < nrequests) THEN
    1921              :                      nelements_per_request(request_offset + irequest) = nelements
    1922           32 :                   ELSE
    1923           16 :                      nelements_per_request(request_offset + irequest) = nelements_per_proc(iproc) - nelements*(nrequests - 1)
    1924           16 :                   END IF
    1925            0 :                   peer_rank(request_offset + irequest) = iproc
    1926              :                   tag(request_offset + irequest) = irequest - 1
    1927           16 :                END DO
    1928              :             END IF
    1929           16 :             request_offset = request_offset + nrequests
    1930           32 :          END IF
    1931              :          element_offset_tmp = element_offset_tmp + nelements_per_proc(iproc)
    1932              :       END DO
    1933           16 : 
    1934              :       IF (debug_this_module) THEN
    1935           48 :          CPASSERT(SIZE(element_offset) == request_offset)
    1936              :          CPASSERT(SIZE(nelements_per_request) == request_offset)
    1937              :          CPASSERT(SIZE(peer_rank) == request_offset)
    1938              :          CPASSERT(SIZE(tag) == request_offset)
    1939              :       END IF
    1940              :    END SUBROUTINE assign_nonzero_elements_to_requests
    1941              : 
    1942              : END MODULE smeagol_matrix_utils
    1943              : /* /opt/cp2k/src/smeagol_matrix_utils.F not long enough */
    1944           16 : /* END: function "__smeagol_matrix_utils_MOD_assign_nonzero_elements_to_requests" */
    1945              : /* ... */
    1946            0 : /* END: function "__smeagol_matrix_utils_MOD___copy_26958B" */
        

Generated by: LCOV version 2.0-1