LCOV - code coverage report
Current view: top level - src - task_list_methods.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:b310db4) Lines: 85.5 % 1093 934
Test Date: 2026-10-23 01:10:43 Functions: 97.2 % 36 35

            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 generate the tasks lists used by collocate and integrate routines
      10              : !> \par History
      11              : !>      01.2008 [Joost VandeVondele] refactered out of qs_collocate / qs_integrate
      12              : !> \author Joost VandeVondele
      13              : ! **************************************************************************************************
      14              : MODULE task_list_methods
      15              :    USE offload_api, ONLY: offload_create_buffer, offload_buffer_type
      16              :    USE grid_api, ONLY: grid_create_basis_set, grid_create_task_list
      17              :    USE ao_util, ONLY: exp_radius_very_extended
      18              :    USE basis_set_types, ONLY: get_gto_basis_set, &
      19              :                               gto_basis_set_p_type, &
      20              :                               gto_basis_set_type
      21              :    USE cell_types, ONLY: cell_type, &
      22              :                          pbc
      23              :    USE cp_control_types, ONLY: dft_control_type
      24              :    USE cube_utils, ONLY: compute_cube_center, &
      25              :                          cube_info_type, &
      26              :                          return_cube, &
      27              :                          return_cube_nonortho
      28              :    USE cp_dbcsr_api, ONLY: dbcsr_convert_sizes_to_offsets, &
      29              :                            dbcsr_finalize, &
      30              :                            dbcsr_get_block_p, &
      31              :                            dbcsr_get_info, &
      32              :                            dbcsr_p_type, &
      33              :                            dbcsr_put_block, &
      34              :                            dbcsr_type, &
      35              :                            dbcsr_work_create
      36              :    USE gaussian_gridlevels, ONLY: gaussian_gridlevel, &
      37              :                                   gridlevel_info_type
      38              :    USE kinds, ONLY: default_string_length, &
      39              :                     dp, &
      40              :                     int_8
      41              :    USE kpoint_types, ONLY: get_kpoint_info, &
      42              :                            kpoint_type
      43              :    USE memory_utilities, ONLY: reallocate
      44              :    USE message_passing, ONLY: &
      45              :       mp_comm_type
      46              :    USE particle_types, ONLY: particle_type
      47              :    USE particle_methods, ONLY: get_particle_set
      48              :    USE pw_env_types, ONLY: pw_env_get, &
      49              :                            pw_env_type
      50              :    USE qs_kind_types, ONLY: get_qs_kind, &
      51              :                             qs_kind_type
      52              :    USE qs_ks_types, ONLY: get_ks_env, &
      53              :                           qs_ks_env_type
      54              :    USE qs_neighbor_list_types, ONLY: neighbor_list_set_p_type
      55              :    USE realspace_grid_types, ONLY: realspace_grid_desc_p_type, &
      56              :                                    realspace_grid_desc_type, &
      57              :                                    rs_grid_create, &
      58              :                                    rs_grid_locate_rank, &
      59              :                                    rs_grid_release, &
      60              :                                    rs_grid_reorder_ranks, realspace_grid_type
      61              :    USE task_list_types, ONLY: deserialize_task, &
      62              :                               reallocate_tasks, &
      63              :                               serialize_task, &
      64              :                               task_list_type, &
      65              :                               atom_pair_type, &
      66              :                               task_size_in_int8, &
      67              :                               task_type
      68              :    USE util, ONLY: sort
      69              : 
      70              : !$ USE OMP_LIB, ONLY: omp_destroy_lock, omp_get_num_threads, omp_init_lock, &
      71              : !$                    omp_lock_kind, omp_set_lock, omp_unset_lock
      72              : #include "./base/base_uses.f90"
      73              : 
      74              :    #:include './common/array_sort.fypp'
      75              : 
      76              :    IMPLICIT NONE
      77              : 
      78              :    LOGICAL, PRIVATE, PARAMETER :: debug_this_module = .FALSE.
      79              : 
      80              :    PRIVATE
      81              : 
      82              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'task_list_methods'
      83              : 
      84              :    PUBLIC :: generate_qs_task_list, &
      85              :              task_list_inner_loop
      86              :    PUBLIC :: distribute_tasks, &
      87              :              rs_distribute_matrix, &
      88              :              rs_scatter_matrices, &
      89              :              rs_gather_matrices, &
      90              :              rs_copy_to_buffer, &
      91              :              rs_copy_to_matrices
      92              : 
      93              : CONTAINS
      94              : 
      95              : ! **************************************************************************************************
      96              : !> \brief ...
      97              : !> \param ks_env ...
      98              : !> \param task_list ...
      99              : !> \param basis_type ...
     100              : !> \param reorder_rs_grid_ranks Flag that indicates if this routine should
     101              : !>        or should not overwrite the rs descriptor (see comment below)
     102              : !> \param skip_load_balance_distributed ...
     103              : !> \param pw_env_external ...
     104              : !> \param sab_orb_external ...
     105              : !> \param ext_kpoints ...
     106              : !> \par History
     107              : !>      01.2008 factored out of calculate_rho_elec [Joost VandeVondele]
     108              : !>      04.2010 divides tasks into grid levels and atom pairs for integrate/collocate [Iain Bethune]
     109              : !>              (c) The Numerical Algorithms Group (NAG) Ltd, 2010 on behalf of the HECToR project
     110              : !>      06.2015 adjusted to be used with multiple images (k-points) [JGH]
     111              : !> \note  If this routine is called several times with different task lists,
     112              : !>        the default behaviour is to re-optimize the grid ranks and overwrite
     113              : !>        the rs descriptor and grids. reorder_rs_grid_ranks = .FALSE. prevents the code
     114              : !>        of performing a new optimization by leaving the rank order in
     115              : !>        its current state.
     116              : ! **************************************************************************************************
     117        17655 :    SUBROUTINE generate_qs_task_list(ks_env, task_list, basis_type, &
     118              :                                     reorder_rs_grid_ranks, skip_load_balance_distributed, &
     119              :                                     pw_env_external, sab_orb_external, ext_kpoints)
     120              : 
     121              :       TYPE(qs_ks_env_type), POINTER                      :: ks_env
     122              :       TYPE(task_list_type), POINTER                      :: task_list
     123              :       CHARACTER(LEN=*), INTENT(IN)                       :: basis_type
     124              :       LOGICAL, INTENT(IN)                                :: reorder_rs_grid_ranks, &
     125              :                                                             skip_load_balance_distributed
     126              :       TYPE(pw_env_type), OPTIONAL, POINTER               :: pw_env_external
     127              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
     128              :          OPTIONAL, POINTER                               :: sab_orb_external
     129              :       TYPE(kpoint_type), OPTIONAL, POINTER               :: ext_kpoints
     130              : 
     131              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'generate_qs_task_list'
     132              :       INTEGER, PARAMETER                                 :: max_tasks = 2000
     133              : 
     134              :       INTEGER :: cindex, curr_tasks, handle, i, iatom, iatom_old, igrid_level, igrid_level_old, &
     135              :                  ikind, ilevel, img, img_old, ipair, ipgf, iset, itask, jatom, jatom_old, jkind, jpgf, &
     136              :                  jset, maxpgf, maxset, natoms, nimages, nkind, nseta, nsetb, slot
     137        17655 :       INTEGER, ALLOCATABLE, DIMENSION(:, :, :)           :: blocks
     138              :       INTEGER, DIMENSION(3)                              :: cellind
     139        17655 :       INTEGER, DIMENSION(:), POINTER                     :: la_max, la_min, lb_max, lb_min, npgfa, &
     140        17655 :                                                             npgfb, nsgf
     141        17655 :       INTEGER, DIMENSION(:, :, :), POINTER               :: cell_to_index
     142              :       LOGICAL                                            :: dokp
     143              :       REAL(KIND=dp)                                      :: kind_radius_a, kind_radius_b
     144              :       REAL(KIND=dp), DIMENSION(3)                        :: ra, rab
     145        17655 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: set_radius_a, set_radius_b
     146        17655 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: rpgfa, rpgfb, zeta, zetb
     147              :       TYPE(cell_type), POINTER                           :: cell
     148        17655 :       TYPE(cube_info_type), DIMENSION(:), POINTER        :: cube_info
     149              :       TYPE(dft_control_type), POINTER                    :: dft_control
     150              :       TYPE(gridlevel_info_type), POINTER                 :: gridlevel_info
     151        17655 :       TYPE(gto_basis_set_p_type), DIMENSION(:), POINTER  :: basis_set_list
     152              :       TYPE(gto_basis_set_type), POINTER                  :: basis_set_a, basis_set_b, orb_basis_set
     153              :       TYPE(kpoint_type), POINTER                         :: kpoints
     154              :       TYPE(neighbor_list_set_p_type), DIMENSION(:), &
     155        17655 :          POINTER                                         :: sab_orb
     156        17655 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     157              :       TYPE(pw_env_type), POINTER                         :: pw_env
     158        17655 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     159              :       TYPE(qs_kind_type), POINTER                        :: qs_kind
     160              :       TYPE(realspace_grid_desc_p_type), DIMENSION(:), &
     161        17655 :          POINTER                                         :: rs_descs
     162        17655 :       TYPE(realspace_grid_type), DIMENSION(:), POINTER :: rs_grids
     163        17655 :       TYPE(task_type), DIMENSION(:), POINTER             :: tasks
     164              : 
     165        17655 :       CALL timeset(routineN, handle)
     166              : 
     167              :       CALL get_ks_env(ks_env, &
     168              :                       qs_kind_set=qs_kind_set, &
     169              :                       cell=cell, &
     170              :                       particle_set=particle_set, &
     171        17655 :                       dft_control=dft_control)
     172              : 
     173        17655 :       CALL get_ks_env(ks_env, sab_orb=sab_orb)
     174        17655 :       IF (PRESENT(sab_orb_external)) sab_orb => sab_orb_external
     175              : 
     176        17655 :       CALL get_ks_env(ks_env, pw_env=pw_env)
     177        17655 :       IF (PRESENT(pw_env_external)) pw_env => pw_env_external
     178        17655 :       CALL pw_env_get(pw_env, rs_descs=rs_descs, rs_grids=rs_grids)
     179              : 
     180              :       ! *** assign from pw_env
     181        17655 :       gridlevel_info => pw_env%gridlevel_info
     182        17655 :       cube_info => pw_env%cube_info
     183              : 
     184              :       ! find maximum numbers
     185        17655 :       nkind = SIZE(qs_kind_set)
     186        17655 :       natoms = SIZE(particle_set)
     187        17655 :       maxset = 0
     188        17655 :       maxpgf = 0
     189        48580 :       DO ikind = 1, nkind
     190        30925 :          qs_kind => qs_kind_set(ikind)
     191              :          CALL get_qs_kind(qs_kind=qs_kind, &
     192        30925 :                           basis_set=orb_basis_set, basis_type=basis_type)
     193              : 
     194        30925 :          IF (.NOT. ASSOCIATED(orb_basis_set)) CYCLE
     195        30925 :          CALL get_gto_basis_set(gto_basis_set=orb_basis_set, npgf=npgfa, nset=nseta)
     196              : 
     197        30925 :          maxset = MAX(nseta, maxset)
     198       112223 :          maxpgf = MAX(MAXVAL(npgfa), maxpgf)
     199              :       END DO
     200              : 
     201              :       ! kpoint related
     202        17655 :       nimages = dft_control%nimages
     203        17655 :       dokp = .FALSE.
     204        17655 :       NULLIFY (cell_to_index)
     205        17655 :       NULLIFY (kpoints)
     206        17655 :       IF (PRESENT(ext_kpoints)) THEN
     207          864 :          IF (ASSOCIATED(ext_kpoints)) THEN
     208           22 :             dokp = .TRUE.
     209           22 :             CALL get_kpoint_info(kpoint=ext_kpoints, cell_to_index=cell_to_index)
     210           22 :             nimages = SIZE(ext_kpoints%index_to_cell, 2)
     211              :          END IF
     212              :       END IF
     213        17655 :       IF (.NOT. dokp .AND. nimages > 1) THEN
     214         1174 :          dokp = .TRUE.
     215         1174 :          CALL get_ks_env(ks_env=ks_env, kpoints=kpoints)
     216         1174 :          CALL get_kpoint_info(kpoint=kpoints, cell_to_index=cell_to_index)
     217              :       END IF
     218              : 
     219              :       ! free the atom_pair lists if allocated
     220        17655 :       IF (ASSOCIATED(task_list%atom_pair_send)) DEALLOCATE (task_list%atom_pair_send)
     221        17655 :       IF (ASSOCIATED(task_list%atom_pair_recv)) DEALLOCATE (task_list%atom_pair_recv)
     222              : 
     223              :       ! construct a list of all tasks
     224        17655 :       IF (.NOT. ASSOCIATED(task_list%tasks)) THEN
     225        10459 :          CALL reallocate_tasks(task_list%tasks, max_tasks)
     226              :       END IF
     227        17655 :       task_list%ntasks = 0
     228        17655 :       curr_tasks = SIZE(task_list%tasks)
     229              : 
     230        83890 :       ALLOCATE (basis_set_list(nkind))
     231        48580 :       DO ikind = 1, nkind
     232        30925 :          qs_kind => qs_kind_set(ikind)
     233              :          CALL get_qs_kind(qs_kind=qs_kind, basis_set=basis_set_a, &
     234        30925 :                           basis_type=basis_type)
     235        48580 :          IF (ASSOCIATED(basis_set_a)) THEN
     236        30925 :             basis_set_list(ikind)%gto_basis_set => basis_set_a
     237              :          ELSE
     238            0 :             NULLIFY (basis_set_list(ikind)%gto_basis_set)
     239              :          END IF
     240              :       END DO
     241              : !!$OMP PARALLEL DEFAULT(NONE) &
     242              : !!$OMP SHARED (sab_orb, dokp, basis_set_list, task_list, rs_descs, dft_control, cube_info, gridlevel_info,  &
     243              : !!$OMP         curr_tasks, maxpgf, maxset, natoms, nimages, particle_set, cell_to_index, cell) &
     244              : !!$OMP PRIVATE (ikind, jkind, iatom, jatom, rab, cellind, basis_set_a, basis_set_b, ra, &
     245              : !!$OMP          la_max, la_min, npgfa, nseta, rpgfa, set_radius_a, kind_radius_a, zeta, &
     246              : !!$OMP          lb_max, lb_min, npgfb, nsetb, rpgfb, set_radius_b, kind_radius_b, zetb, &
     247              : !!$OMP          cindex, slot)
     248              :       ! Loop over neighbor list
     249              : !!$OMP DO SCHEDULE(GUIDED)
     250      1935814 :       DO slot = 1, sab_orb(1)%nl_size
     251      1918159 :          ikind = sab_orb(1)%nlist_task(slot)%ikind
     252      1918159 :          jkind = sab_orb(1)%nlist_task(slot)%jkind
     253      1918159 :          iatom = sab_orb(1)%nlist_task(slot)%iatom
     254      1918159 :          jatom = sab_orb(1)%nlist_task(slot)%jatom
     255      7672636 :          rab(1:3) = sab_orb(1)%nlist_task(slot)%r(1:3)
     256      7672636 :          cellind(1:3) = sab_orb(1)%nlist_task(slot)%cell(1:3)
     257              : 
     258      1918159 :          basis_set_a => basis_set_list(ikind)%gto_basis_set
     259      1918159 :          IF (.NOT. ASSOCIATED(basis_set_a)) CYCLE
     260      1918159 :          basis_set_b => basis_set_list(jkind)%gto_basis_set
     261      1918159 :          IF (.NOT. ASSOCIATED(basis_set_b)) CYCLE
     262      1918159 :          ra(:) = pbc(particle_set(iatom)%r, cell)
     263              :          ! basis ikind
     264      1918159 :          la_max => basis_set_a%lmax
     265      1918159 :          la_min => basis_set_a%lmin
     266      1918159 :          npgfa => basis_set_a%npgf
     267      1918159 :          nseta = basis_set_a%nset
     268      1918159 :          rpgfa => basis_set_a%pgf_radius
     269      1918159 :          set_radius_a => basis_set_a%set_radius
     270      1918159 :          kind_radius_a = basis_set_a%kind_radius
     271      1918159 :          zeta => basis_set_a%zet
     272              :          ! basis jkind
     273      1918159 :          lb_max => basis_set_b%lmax
     274      1918159 :          lb_min => basis_set_b%lmin
     275      1918159 :          npgfb => basis_set_b%npgf
     276      1918159 :          nsetb = basis_set_b%nset
     277      1918159 :          rpgfb => basis_set_b%pgf_radius
     278      1918159 :          set_radius_b => basis_set_b%set_radius
     279      1918159 :          kind_radius_b = basis_set_b%kind_radius
     280      1918159 :          zetb => basis_set_b%zet
     281              : 
     282      1918159 :          IF (dokp) THEN
     283       574869 :             cindex = cell_to_index(cellind(1), cellind(2), cellind(3))
     284              :          ELSE
     285      1343290 :             cindex = 1
     286              :          END IF
     287              : 
     288              :          CALL task_list_inner_loop(task_list%tasks, task_list%ntasks, curr_tasks, &
     289              :                                    rs_descs, dft_control, cube_info, gridlevel_info, cindex, &
     290              :                                    iatom, jatom, rpgfa, rpgfb, zeta, zetb, kind_radius_b, &
     291              :                                    set_radius_a, set_radius_b, ra, rab, &
     292      1935814 :                                    la_max, la_min, lb_max, lb_min, npgfa, npgfb, nseta, nsetb)
     293              : 
     294              :       END DO
     295              : !!$OMP END PARALLEL
     296              : 
     297              :       ! redistribute the task list so that all tasks map on the local rs grids
     298              :       CALL distribute_tasks( &
     299              :          rs_descs=rs_descs, ntasks=task_list%ntasks, natoms=natoms, &
     300              :          tasks=task_list%tasks, atom_pair_send=task_list%atom_pair_send, &
     301              :          atom_pair_recv=task_list%atom_pair_recv, symmetric=.TRUE., &
     302              :          reorder_rs_grid_ranks=reorder_rs_grid_ranks, &
     303        17655 :          skip_load_balance_distributed=skip_load_balance_distributed)
     304              : 
     305              :       ! compute offsets for rs_scatter_matrix / rs_copy_matrix
     306        52965 :       ALLOCATE (nsgf(natoms))
     307        17655 :       CALL get_particle_set(particle_set, qs_kind_set, basis=basis_set_list, nsgf=nsgf)
     308        17655 :       IF (ASSOCIATED(task_list%atom_pair_send)) THEN
     309              :          ! only needed when there is a distributed grid
     310              :          CALL rs_calc_offsets(pairs=task_list%atom_pair_send, &
     311              :                               nsgf=nsgf, &
     312              :                               group_size=rs_descs(1)%rs_desc%group_size, &
     313              :                               pair_offsets=task_list%pair_offsets_send, &
     314              :                               rank_offsets=task_list%rank_offsets_send, &
     315              :                               rank_sizes=task_list%rank_sizes_send, &
     316           50 :                               buffer_size=task_list%buffer_size_send)
     317              :       END IF
     318              :       CALL rs_calc_offsets(pairs=task_list%atom_pair_recv, &
     319              :                            nsgf=nsgf, &
     320              :                            group_size=rs_descs(1)%rs_desc%group_size, &
     321              :                            pair_offsets=task_list%pair_offsets_recv, &
     322              :                            rank_offsets=task_list%rank_offsets_recv, &
     323              :                            rank_sizes=task_list%rank_sizes_recv, &
     324        17655 :                            buffer_size=task_list%buffer_size_recv)
     325        17655 :       IF (ASSOCIATED(task_list%atom_pair_send)) THEN
     326              :          CALL rs_group_atom_pairs(task_list%atom_pair_send, &
     327           50 :                                   task_list%unpack_block_starts, task_list%unpack_pair_indices)
     328              :       ELSE
     329              :          CALL rs_group_atom_pairs(task_list%atom_pair_recv, &
     330        17605 :                                   task_list%unpack_block_starts, task_list%unpack_pair_indices)
     331              :       END IF
     332        17655 :       DEALLOCATE (basis_set_list, nsgf)
     333              : 
     334              :       ! If the rank order has changed, reallocate any of the distributed rs_grids
     335        17655 :       IF (reorder_rs_grid_ranks) THEN
     336        75096 :          DO i = 1, gridlevel_info%ngrid_levels
     337        75096 :             IF (rs_descs(i)%rs_desc%distributed) THEN
     338           56 :                CALL rs_grid_release(rs_grids(i))
     339           56 :                CALL rs_grid_create(rs_grids(i), rs_descs(i)%rs_desc)
     340              :             END IF
     341              :          END DO
     342              :       END IF
     343              : 
     344              :       CALL create_grid_task_list(task_list=task_list, &
     345              :                                  qs_kind_set=qs_kind_set, &
     346              :                                  particle_set=particle_set, &
     347              :                                  cell=cell, &
     348              :                                  basis_type=basis_type, &
     349        17655 :                                  rs_grids=rs_grids)
     350              : 
     351              :       ! Now we have the final list of tasks, setup the task_list with the
     352              :       ! data needed for the loops in integrate_v/calculate_rho
     353              : 
     354        17655 :       IF (ASSOCIATED(task_list%taskstart)) THEN
     355         7196 :          DEALLOCATE (task_list%taskstart)
     356              :       END IF
     357        17655 :       IF (ASSOCIATED(task_list%taskstop)) THEN
     358         7196 :          DEALLOCATE (task_list%taskstop)
     359              :       END IF
     360        17655 :       IF (ASSOCIATED(task_list%npairs)) THEN
     361         7196 :          DEALLOCATE (task_list%npairs)
     362              :       END IF
     363              : 
     364              :       ! First, count the number of unique atom pairs per grid level
     365              : 
     366        52965 :       ALLOCATE (task_list%npairs(SIZE(rs_descs)))
     367              : 
     368        17655 :       iatom_old = -1; jatom_old = -1; igrid_level_old = -1; img_old = -1
     369        17655 :       ipair = 0
     370        87546 :       task_list%npairs = 0
     371              : 
     372     10687311 :       DO i = 1, task_list%ntasks
     373     10669656 :          igrid_level = task_list%tasks(i)%grid_level
     374     10669656 :          img = task_list%tasks(i)%image
     375     10669656 :          iatom = task_list%tasks(i)%iatom
     376     10669656 :          jatom = task_list%tasks(i)%jatom
     377     10669656 :          iset = task_list%tasks(i)%iset
     378     10669656 :          jset = task_list%tasks(i)%jset
     379     10669656 :          ipgf = task_list%tasks(i)%ipgf
     380     10669656 :          jpgf = task_list%tasks(i)%jpgf
     381     10687311 :          IF (igrid_level /= igrid_level_old) THEN
     382        48170 :             IF (igrid_level_old /= -1) THEN
     383        30854 :                task_list%npairs(igrid_level_old) = ipair
     384              :             END IF
     385              :             ipair = 1
     386              :             igrid_level_old = igrid_level
     387              :             iatom_old = iatom
     388              :             jatom_old = jatom
     389              :             img_old = img
     390     10621486 :          ELSE IF (iatom /= iatom_old .OR. jatom /= jatom_old .OR. img /= img_old) THEN
     391      1266191 :             ipair = ipair + 1
     392      1266191 :             iatom_old = iatom
     393      1266191 :             jatom_old = jatom
     394      1266191 :             img_old = img
     395              :          END IF
     396              :       END DO
     397              :       ! Take care of the last iteration
     398        17655 :       IF (task_list%ntasks /= 0) THEN
     399        17316 :          task_list%npairs(igrid_level) = ipair
     400              :       END IF
     401              : 
     402              :       ! Second, for each atom pair, find the indices in the task list
     403              :       ! of the first and last task
     404              : 
     405              :       ! Array sized for worst case
     406       140172 :       ALLOCATE (task_list%taskstart(MAXVAL(task_list%npairs), SIZE(rs_descs)))
     407       140172 :       ALLOCATE (task_list%taskstop(MAXVAL(task_list%npairs), SIZE(rs_descs)))
     408              : 
     409        17655 :       iatom_old = -1; jatom_old = -1; igrid_level_old = -1; img_old = -1
     410        17655 :       ipair = 0
     411      2903361 :       task_list%taskstart = 0
     412      2903361 :       task_list%taskstop = 0
     413              : 
     414     10687311 :       DO i = 1, task_list%ntasks
     415     10669656 :          igrid_level = task_list%tasks(i)%grid_level
     416     10669656 :          img = task_list%tasks(i)%image
     417     10669656 :          iatom = task_list%tasks(i)%iatom
     418     10669656 :          jatom = task_list%tasks(i)%jatom
     419     10669656 :          iset = task_list%tasks(i)%iset
     420     10669656 :          jset = task_list%tasks(i)%jset
     421     10669656 :          ipgf = task_list%tasks(i)%ipgf
     422     10669656 :          jpgf = task_list%tasks(i)%jpgf
     423     10687311 :          IF (igrid_level /= igrid_level_old) THEN
     424        48170 :             IF (igrid_level_old /= -1) THEN
     425        30854 :                task_list%taskstop(ipair, igrid_level_old) = i - 1
     426              :             END IF
     427        48170 :             ipair = 1
     428        48170 :             task_list%taskstart(ipair, igrid_level) = i
     429        48170 :             igrid_level_old = igrid_level
     430        48170 :             iatom_old = iatom
     431        48170 :             jatom_old = jatom
     432        48170 :             img_old = img
     433     10621486 :          ELSE IF (iatom /= iatom_old .OR. jatom /= jatom_old .OR. img /= img_old) THEN
     434      1266191 :             ipair = ipair + 1
     435      1266191 :             task_list%taskstart(ipair, igrid_level) = i
     436      1266191 :             task_list%taskstop(ipair - 1, igrid_level) = i - 1
     437      1266191 :             iatom_old = iatom
     438      1266191 :             jatom_old = jatom
     439      1266191 :             img_old = img
     440              :          END IF
     441              :       END DO
     442              :       ! Take care of the last iteration
     443        17655 :       IF (task_list%ntasks /= 0) THEN
     444        17316 :          task_list%taskstop(ipair, igrid_level) = task_list%ntasks
     445              :       END IF
     446              : 
     447              :       ! Debug task destribution
     448              :       IF (debug_this_module) THEN
     449              :          tasks => task_list%tasks
     450              :          WRITE (6, *)
     451              :          WRITE (6, *) "Total number of tasks              ", task_list%ntasks
     452              :          DO igrid_level = 1, gridlevel_info%ngrid_levels
     453              :             WRITE (6, *) "Total number of pairs(grid_level)  ", &
     454              :                igrid_level, task_list%npairs(igrid_level)
     455              :          END DO
     456              :          WRITE (6, *)
     457              : 
     458              :          DO igrid_level = 1, gridlevel_info%ngrid_levels
     459              : 
     460              :             ALLOCATE (blocks(natoms, natoms, nimages))
     461              :             blocks = -1
     462              :             DO ipair = 1, task_list%npairs(igrid_level)
     463              :                itask = task_list%taskstart(ipair, igrid_level)
     464              :                ilevel = task_list%tasks(itask)%grid_level
     465              :                img = task_list%tasks(itask)%image
     466              :                iatom = task_list%tasks(itask)%iatom
     467              :                jatom = task_list%tasks(itask)%jatom
     468              :                iset = task_list%tasks(itask)%iset
     469              :                jset = task_list%tasks(itask)%jset
     470              :                ipgf = task_list%tasks(itask)%ipgf
     471              :                jpgf = task_list%tasks(itask)%jpgf
     472              :                IF (blocks(iatom, jatom, img) == -1 .AND. blocks(jatom, iatom, img) == -1) THEN
     473              :                   blocks(iatom, jatom, img) = 1
     474              :                   blocks(jatom, iatom, img) = 1
     475              :                ELSE
     476              :                   WRITE (6, *) "TASK LIST CONFLICT IN PAIR       ", ipair
     477              :                   WRITE (6, *) "Reuse of iatom, jatom, image     ", iatom, jatom, img
     478              :                END IF
     479              : 
     480              :                iatom_old = iatom
     481              :                jatom_old = jatom
     482              :                img_old = img
     483              :                DO itask = task_list%taskstart(ipair, igrid_level), task_list%taskstop(ipair, igrid_level)
     484              :                   ilevel = task_list%tasks(itask)%grid_level
     485              :                   img = task_list%tasks(itask)%image
     486              :                   iatom = task_list%tasks(itask)%iatom
     487              :                   jatom = task_list%tasks(itask)%jatom
     488              :                   iset = task_list%tasks(itask)%iset
     489              :                   jset = task_list%tasks(itask)%jset
     490              :                   ipgf = task_list%tasks(itask)%ipgf
     491              :                   jpgf = task_list%tasks(itask)%jpgf
     492              :                   IF (iatom /= iatom_old .OR. jatom /= jatom_old .OR. img /= img_old) THEN
     493              :                      WRITE (6, *) "TASK LIST CONFLICT IN TASK       ", itask
     494              :                      WRITE (6, *) "Inconsistent iatom, jatom, image ", iatom, jatom, img
     495              :                      WRITE (6, *) "Should be    iatom, jatom, image ", iatom_old, jatom_old, img_old
     496              :                   END IF
     497              : 
     498              :                END DO
     499              :             END DO
     500              :             DEALLOCATE (blocks)
     501              : 
     502              :          END DO
     503              : 
     504              :       END IF
     505              : 
     506        17655 :       CALL timestop(handle)
     507              : 
     508        17655 :    END SUBROUTINE generate_qs_task_list
     509              : 
     510              : ! **************************************************************************************************
     511              : !> \brief Sends the task list data to the grid API.
     512              : !> \author Ole Schuett
     513              : ! **************************************************************************************************
     514        17655 :    SUBROUTINE create_grid_task_list(task_list, qs_kind_set, particle_set, cell, basis_type, rs_grids)
     515              :       TYPE(task_list_type), POINTER                      :: task_list
     516              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     517              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     518              :       TYPE(cell_type), POINTER                           :: cell
     519              :       CHARACTER(LEN=*)                                   :: basis_type
     520              :       TYPE(realspace_grid_type), DIMENSION(:), POINTER :: rs_grids
     521              : 
     522              :       TYPE(gto_basis_set_type), POINTER                  :: orb_basis_set
     523              :       INTEGER                                            :: nset, natoms, nkinds, ntasks, &
     524              :                                                             ikind, iatom, itask, nsgf
     525        17655 :       INTEGER, DIMENSION(:), ALLOCATABLE                 :: atom_kinds, level_list, iatom_list, jatom_list, &
     526        17655 :                                                             iset_list, jset_list, ipgf_list, jpgf_list, &
     527        17655 :                                                             border_mask_list, block_num_list
     528        17655 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE           :: radius_list
     529        17655 :       REAL(KIND=dp), DIMENSION(:, :), ALLOCATABLE        :: rab_list, atom_positions
     530        17655 :       TYPE(task_type), DIMENSION(:), POINTER             :: tasks
     531        17655 :       INTEGER, DIMENSION(:, :), POINTER                  :: first_sgf
     532        17655 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: sphi, zet
     533        17655 :       INTEGER, DIMENSION(:), POINTER                     :: lmax, lmin, npgf, nsgf_set
     534              : 
     535        17655 :       nkinds = SIZE(qs_kind_set)
     536        17655 :       natoms = SIZE(particle_set)
     537        17655 :       ntasks = task_list%ntasks
     538        17655 :       tasks => task_list%tasks
     539              : 
     540        17655 :       IF (.NOT. ASSOCIATED(task_list%grid_basis_sets)) THEN
     541              :          ! Basis sets do not change during simulation - only need to create them once.
     542        49540 :          ALLOCATE (task_list%grid_basis_sets(nkinds))
     543        28622 :          DO ikind = 1, nkinds
     544        18163 :             CALL get_qs_kind(qs_kind_set(ikind), basis_type=basis_type, basis_set=orb_basis_set)
     545              :             CALL get_gto_basis_set(gto_basis_set=orb_basis_set, &
     546              :                                    nset=nset, &
     547              :                                    nsgf=nsgf, &
     548              :                                    nsgf_set=nsgf_set, &
     549              :                                    npgf=npgf, &
     550              :                                    first_sgf=first_sgf, &
     551              :                                    lmax=lmax, &
     552              :                                    lmin=lmin, &
     553              :                                    sphi=sphi, &
     554        18163 :                                    zet=zet)
     555              :             CALL grid_create_basis_set(nset=nset, &
     556              :                                        nsgf=nsgf, &
     557              :                                        maxco=SIZE(sphi, 1), &
     558              :                                        maxpgf=SIZE(zet, 1), &
     559              :                                        lmin=lmin, &
     560              :                                        lmax=lmax, &
     561              :                                        npgf=npgf, &
     562              :                                        nsgf_set=nsgf_set, &
     563              :                                        first_sgf=first_sgf, &
     564              :                                        sphi=sphi, &
     565              :                                        zet=zet, &
     566        28622 :                                        basis_set=task_list%grid_basis_sets(ikind))
     567              :          END DO
     568              :       END IF
     569              : 
     570              :       ! Pack task list infos
     571        88275 :       ALLOCATE (atom_kinds(natoms), atom_positions(3, natoms))
     572        81060 :       DO iatom = 1, natoms
     573        63405 :          atom_kinds(iatom) = particle_set(iatom)%atomic_kind%kind_number
     574        81060 :          atom_positions(:, iatom) = pbc(particle_set(iatom)%r, cell)
     575              :       END DO
     576              : 
     577        87258 :       ALLOCATE (level_list(ntasks), iatom_list(ntasks), jatom_list(ntasks))
     578        86919 :       ALLOCATE (iset_list(ntasks), jset_list(ntasks), ipgf_list(ntasks), jpgf_list(ntasks))
     579        52287 :       ALLOCATE (border_mask_list(ntasks), block_num_list(ntasks))
     580        87597 :       ALLOCATE (radius_list(ntasks), rab_list(3, ntasks))
     581              : 
     582     10687311 :       DO itask = 1, ntasks
     583     10669656 :          level_list(itask) = tasks(itask)%grid_level
     584     10669656 :          iatom_list(itask) = tasks(itask)%iatom
     585     10669656 :          jatom_list(itask) = tasks(itask)%jatom
     586     10669656 :          iset_list(itask) = tasks(itask)%iset
     587     10669656 :          jset_list(itask) = tasks(itask)%jset
     588     10669656 :          ipgf_list(itask) = tasks(itask)%ipgf
     589     10669656 :          jpgf_list(itask) = tasks(itask)%jpgf
     590     10669656 :          IF (tasks(itask)%dist_type == 2) THEN
     591            0 :             border_mask_list(itask) = IAND(63, NOT(tasks(itask)%subpatch_pattern))  ! invert last 6 bits
     592              :          ELSE
     593     10669656 :             border_mask_list(itask) = 0 ! no masking
     594              :          END IF
     595     10669656 :          block_num_list(itask) = tasks(itask)%pair_index  ! change of nomenclature pair_index -> block_num
     596     10669656 :          radius_list(itask) = tasks(itask)%radius
     597     42696279 :          rab_list(:, itask) = tasks(itask)%rab(:)
     598              :       END DO
     599              : 
     600              :       CALL grid_create_task_list(ntasks=ntasks, &
     601              :                                  natoms=natoms, &
     602              :                                  nkinds=nkinds, &
     603              :                                  nblocks=SIZE(task_list%pair_offsets_recv), &
     604              :                                  block_offsets=task_list%pair_offsets_recv, &
     605              :                                  atom_positions=atom_positions, &
     606              :                                  atom_kinds=atom_kinds, &
     607              :                                  basis_sets=task_list%grid_basis_sets, &
     608              :                                  level_list=level_list, &
     609              :                                  iatom_list=iatom_list, &
     610              :                                  jatom_list=jatom_list, &
     611              :                                  iset_list=iset_list, &
     612              :                                  jset_list=jset_list, &
     613              :                                  ipgf_list=ipgf_list, &
     614              :                                  jpgf_list=jpgf_list, &
     615              :                                  border_mask_list=border_mask_list, &
     616              :                                  block_num_list=block_num_list, &
     617              :                                  radius_list=radius_list, &
     618              :                                  rab_list=rab_list, &
     619              :                                  rs_grids=rs_grids, &
     620        17655 :                                  task_list=task_list%grid_task_list)
     621              : 
     622        17655 :       CALL offload_create_buffer(task_list%buffer_size_recv, task_list%pab_buffer)
     623        17655 :       CALL offload_create_buffer(task_list%buffer_size_recv, task_list%hab_buffer)
     624              : 
     625        35310 :    END SUBROUTINE create_grid_task_list
     626              : 
     627              : ! **************************************************************************************************
     628              : !> \brief ...
     629              : !> \param tasks ...
     630              : !> \param ntasks ...
     631              : !> \param curr_tasks ...
     632              : !> \param rs_descs ...
     633              : !> \param dft_control ...
     634              : !> \param cube_info ...
     635              : !> \param gridlevel_info ...
     636              : !> \param cindex ...
     637              : !> \param iatom ...
     638              : !> \param jatom ...
     639              : !> \param rpgfa ...
     640              : !> \param rpgfb ...
     641              : !> \param zeta ...
     642              : !> \param zetb ...
     643              : !> \param kind_radius_b ...
     644              : !> \param set_radius_a ...
     645              : !> \param set_radius_b ...
     646              : !> \param ra ...
     647              : !> \param rab ...
     648              : !> \param la_max ...
     649              : !> \param la_min ...
     650              : !> \param lb_max ...
     651              : !> \param lb_min ...
     652              : !> \param npgfa ...
     653              : !> \param npgfb ...
     654              : !> \param nseta ...
     655              : !> \param nsetb ...
     656              : !> \par History
     657              : !>      Joost VandeVondele: 10.2008 refactored
     658              : ! **************************************************************************************************
     659      1929220 :    SUBROUTINE task_list_inner_loop(tasks, ntasks, curr_tasks, rs_descs, dft_control, &
     660              :                                    cube_info, gridlevel_info, cindex, &
     661              :                                    iatom, jatom, rpgfa, rpgfb, zeta, zetb, kind_radius_b, set_radius_a, set_radius_b, ra, rab, &
     662              :                                    la_max, la_min, lb_max, lb_min, npgfa, npgfb, nseta, nsetb)
     663              : 
     664              :       TYPE(task_type), DIMENSION(:), POINTER             :: tasks
     665              :       INTEGER                                            :: ntasks, curr_tasks
     666              :       TYPE(realspace_grid_desc_p_type), DIMENSION(:), &
     667              :          POINTER                                         :: rs_descs
     668              :       TYPE(dft_control_type), POINTER                    :: dft_control
     669              :       TYPE(cube_info_type), DIMENSION(:), POINTER        :: cube_info
     670              :       TYPE(gridlevel_info_type), POINTER                 :: gridlevel_info
     671              :       INTEGER                                            :: cindex, iatom, jatom
     672              :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: rpgfa, rpgfb, zeta, zetb
     673              :       REAL(KIND=dp)                                      :: kind_radius_b
     674              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: set_radius_a, set_radius_b
     675              :       REAL(KIND=dp), DIMENSION(3)                        :: ra, rab
     676              :       INTEGER, DIMENSION(:), POINTER                     :: la_max, la_min, lb_max, lb_min, npgfa, &
     677              :                                                             npgfb
     678              :       INTEGER                                            :: nseta, nsetb
     679              : 
     680              :       INTEGER                                            :: cube_center(3), igrid_level, ipgf, iset, &
     681              :                                                             jpgf, jset, lb_cube(3), ub_cube(3)
     682              :       REAL(KIND=dp)                                      :: dab, rab2, radius, zetp
     683              : 
     684      1929220 :       rab2 = rab(1)*rab(1) + rab(2)*rab(2) + rab(3)*rab(3)
     685      1929220 :       dab = SQRT(rab2)
     686              : 
     687      5704238 :       loop_iset: DO iset = 1, nseta
     688              : 
     689      3775018 :          IF (set_radius_a(iset) + kind_radius_b < dab) CYCLE loop_iset
     690              : 
     691     11377116 :          loop_jset: DO jset = 1, nsetb
     692              : 
     693      6695318 :             IF (set_radius_a(iset) + set_radius_b(jset) < dab) CYCLE loop_jset
     694              : 
     695     19345993 :             loop_ipgf: DO ipgf = 1, npgfa(iset)
     696              : 
     697     11271771 :                IF (rpgfa(ipgf, iset) + set_radius_b(jset) < dab) CYCLE loop_ipgf
     698              : 
     699     33771488 :                loop_jpgf: DO jpgf = 1, npgfb(jset)
     700              : 
     701     20414466 :                   IF (rpgfa(ipgf, iset) + rpgfb(jpgf, jset) < dab) CYCLE loop_jpgf
     702              : 
     703     10823718 :                   zetp = zeta(ipgf, iset) + zetb(jpgf, jset)
     704     10823718 :                   igrid_level = gaussian_gridlevel(gridlevel_info, zetp)
     705              : 
     706              :                   CALL compute_pgf_properties(cube_center, lb_cube, ub_cube, radius, &
     707              :                                               rs_descs(igrid_level)%rs_desc, cube_info(igrid_level), &
     708              :                                               la_max(iset), zeta(ipgf, iset), la_min(iset), &
     709              :                                               lb_max(jset), zetb(jpgf, jset), lb_min(jset), &
     710     10823718 :                                               ra, rab, rab2, dft_control%qs_control%eps_rho_rspace)
     711              : 
     712              :                   CALL pgf_to_tasks(tasks, ntasks, curr_tasks, &
     713              :                                     rab, cindex, iatom, jatom, iset, jset, ipgf, jpgf, &
     714              :                                     la_max(iset), lb_max(jset), rs_descs(igrid_level)%rs_desc, &
     715              :                                     igrid_level, gridlevel_info%ngrid_levels, cube_center, &
     716     31686237 :                                     lb_cube, ub_cube, radius)
     717              : 
     718              :                END DO loop_jpgf
     719              : 
     720              :             END DO loop_ipgf
     721              : 
     722              :          END DO loop_jset
     723              : 
     724              :       END DO loop_iset
     725              : 
     726      1929220 :    END SUBROUTINE task_list_inner_loop
     727              : 
     728              : ! **************************************************************************************************
     729              : !> \brief combines the calculation of several basic properties of a given pgf:
     730              : !>  its center, the bounding cube, the radius, the cost,
     731              : !>  tries to predict the time needed for processing this task
     732              : !>      in this way an improved load balance might be obtained
     733              : !> \param cube_center ...
     734              : !> \param lb_cube ...
     735              : !> \param ub_cube ...
     736              : !> \param radius ...
     737              : !> \param rs_desc ...
     738              : !> \param cube_info ...
     739              : !> \param la_max ...
     740              : !> \param zeta ...
     741              : !> \param la_min ...
     742              : !> \param lb_max ...
     743              : !> \param zetb ...
     744              : !> \param lb_min ...
     745              : !> \param ra ...
     746              : !> \param rab ...
     747              : !> \param rab2 ...
     748              : !> \param eps ...
     749              : !> \par History
     750              : !>      10.2008 refactored [Joost VandeVondele]
     751              : !> \note
     752              : !>      -) this requires the radius to be computed in the same way as
     753              : !>      collocate_pgf_product, we should factor that part into a subroutine
     754              : !>      -) we're assuming that integrate_pgf and collocate_pgf are the same cost for load balancing
     755              : !>         this is more or less true for map_consistent
     756              : !>      -) in principle, the computed radius could be recycled in integrate_pgf/collocate_pgf if it is certainly
     757              : !>         the same, this could lead to a small speedup
     758              : !>      -) the cost function is a fit through the median cost of mapping a pgf with a given l and a given radius (in grid points)
     759              : !>         fitting the measured data on an opteron/g95 using the expression
     760              : !>         a*(l+b)(r+c)**3+d which is based on the innerloop of the collocating routines
     761              : ! **************************************************************************************************
     762     10823718 :    SUBROUTINE compute_pgf_properties(cube_center, lb_cube, ub_cube, radius, &
     763              :                                      rs_desc, cube_info, la_max, zeta, la_min, lb_max, zetb, lb_min, ra, rab, rab2, eps)
     764              : 
     765              :       INTEGER, DIMENSION(3), INTENT(OUT)                 :: cube_center, lb_cube, ub_cube
     766              :       REAL(KIND=dp), INTENT(OUT)                         :: radius
     767              :       TYPE(realspace_grid_desc_type), POINTER            :: rs_desc
     768              :       TYPE(cube_info_type), INTENT(IN)                   :: cube_info
     769              :       INTEGER, INTENT(IN)                                :: la_max
     770              :       REAL(KIND=dp), INTENT(IN)                          :: zeta
     771              :       INTEGER, INTENT(IN)                                :: la_min, lb_max
     772              :       REAL(KIND=dp), INTENT(IN)                          :: zetb
     773              :       INTEGER, INTENT(IN)                                :: lb_min
     774              :       REAL(KIND=dp), INTENT(IN)                          :: ra(3), rab(3), rab2, eps
     775              : 
     776              :       INTEGER                                            :: extent(3)
     777     10823718 :       INTEGER, DIMENSION(:), POINTER                     :: sphere_bounds
     778              :       REAL(KIND=dp)                                      :: cutoff, f, prefactor, rb(3), zetp
     779              :       REAL(KIND=dp), DIMENSION(3)                        :: rp
     780              : 
     781              : ! the radius for this task
     782              : 
     783     10823718 :       zetp = zeta + zetb
     784     43294872 :       rp(:) = ra(:) + zetb/zetp*rab(:)
     785     43294872 :       rb(:) = ra(:) + rab(:)
     786     10823718 :       cutoff = 1.0_dp
     787     10823718 :       f = zetb/zetp
     788     10823718 :       prefactor = EXP(-zeta*f*rab2)
     789              :       radius = exp_radius_very_extended(la_min, la_max, lb_min, lb_max, ra=ra, rb=rb, rp=rp, &
     790     10823718 :                                         zetp=zetp, eps=eps, prefactor=prefactor, cutoff=cutoff)
     791              : 
     792     10823718 :       CALL compute_cube_center(cube_center, rs_desc, zeta, zetb, ra, rab)
     793              :       ! compute cube_center, the center of the gaussian product to map (folded to within the unit cell)
     794     43294872 :       cube_center(:) = MODULO(cube_center(:), rs_desc%npts(:))
     795     43294872 :       cube_center(:) = cube_center(:) + rs_desc%lb(:)
     796              : 
     797     10823718 :       IF (rs_desc%orthorhombic) THEN
     798     10092425 :          CALL return_cube(cube_info, radius, lb_cube, ub_cube, sphere_bounds)
     799              :       ELSE
     800       731293 :          CALL return_cube_nonortho(cube_info, radius, lb_cube, ub_cube, rp)
     801              :          !? unclear if extent is computed correctly.
     802      2925172 :          extent(:) = ub_cube(:) - lb_cube(:)
     803      2925172 :          lb_cube(:) = -extent(:)/2 - 1
     804      2925172 :          ub_cube(:) = extent(:)/2
     805              :       END IF
     806              : 
     807     10823718 :    END SUBROUTINE compute_pgf_properties
     808              : ! **************************************************************************************************
     809              : !> \brief predicts the cost of a task in kcycles for a given task
     810              : !>        the model is based on a fit of actual data, and might need updating
     811              : !>        as collocate_pgf_product changes (or CPUs/compilers change)
     812              : !>        maybe some dynamic approach, improving the cost model on the fly could
     813              : !>        work as well
     814              : !>        the cost model does not yet take into account the fraction of space
     815              : !>        that is mapped locally for a given cube and rs_grid (generalised tasks)
     816              : !> \param lb_cube ...
     817              : !> \param ub_cube ...
     818              : !> \param fraction ...
     819              : !> \param lmax ...
     820              : !> \param is_ortho ...
     821              : !> \return ...
     822              : ! **************************************************************************************************
     823     10823718 :    INTEGER FUNCTION cost_model(lb_cube, ub_cube, fraction, lmax, is_ortho)
     824              :       INTEGER, DIMENSION(3), INTENT(IN)                  :: lb_cube, ub_cube
     825              :       REAL(KIND=dp), INTENT(IN)                          :: fraction
     826              :       INTEGER                                            :: lmax
     827              :       LOGICAL                                            :: is_ortho
     828              : 
     829              :       INTEGER                                            :: cmax
     830              :       REAL(KIND=dp)                                      :: v1, v2, v3, v4, v5
     831              : 
     832     43294872 :       cmax = MAXVAL(((ub_cube - lb_cube) + 1)/2)
     833              : 
     834     10823718 :       IF (is_ortho) THEN
     835              :          v1 = 1.504760E+00_dp
     836              :          v2 = 3.126770E+00_dp
     837              :          v3 = 5.074106E+00_dp
     838              :          v4 = 1.091568E+00_dp
     839              :          v5 = 1.070187E+00_dp
     840              :       ELSE
     841       731293 :          v1 = 7.831105E+00_dp
     842       731293 :          v2 = 2.675174E+00_dp
     843       731293 :          v3 = 7.546553E+00_dp
     844       731293 :          v4 = 6.122446E-01_dp
     845       731293 :          v5 = 3.886382E+00_dp
     846              :       END IF
     847     10823718 :       cost_model = CEILING(((lmax + v1)*(cmax + v2)**3*v3*fraction + v4 + v5*lmax**7)/1000.0_dp)
     848              : 
     849     10823718 :    END FUNCTION cost_model
     850              : ! **************************************************************************************************
     851              : !> \brief pgf_to_tasks converts a given pgf to one or more tasks, in particular
     852              : !>        this determines by which CPUs a given pgf gets collocated
     853              : !>        the format of the task array is as follows
     854              : !>        tasks(1,i) := destination
     855              : !>        tasks(2,i) := source
     856              : !>        tasks(3,i) := compressed type (iatom, jatom, ....)
     857              : !>        tasks(4,i) := type (0: replicated, 1: distributed local, 2: distributed generalised)
     858              : !>        tasks(5,i) := cost
     859              : !>        tasks(6,i) := alternate destination code (0 if none available)
     860              : !>
     861              : !> \param tasks ...
     862              : !> \param ntasks ...
     863              : !> \param curr_tasks ...
     864              : !> \param rab ...
     865              : !> \param cindex ...
     866              : !> \param iatom ...
     867              : !> \param jatom ...
     868              : !> \param iset ...
     869              : !> \param jset ...
     870              : !> \param ipgf ...
     871              : !> \param jpgf ...
     872              : !> \param la_max ...
     873              : !> \param lb_max ...
     874              : !> \param rs_desc ...
     875              : !> \param igrid_level ...
     876              : !> \param n_levels ...
     877              : !> \param cube_center ...
     878              : !> \param lb_cube ...
     879              : !> \param ub_cube ...
     880              : !> \par History
     881              : !>      10.2008 Refactored based on earlier routines by MattW [Joost VandeVondele]
     882              : ! **************************************************************************************************
     883     10823718 :    SUBROUTINE pgf_to_tasks(tasks, ntasks, curr_tasks, &
     884              :                            rab, cindex, iatom, jatom, iset, jset, ipgf, jpgf, &
     885              :                            la_max, lb_max, rs_desc, igrid_level, n_levels, &
     886              :                            cube_center, lb_cube, ub_cube, radius)
     887              : 
     888              :       TYPE(task_type), DIMENSION(:), POINTER             :: tasks
     889              :       INTEGER, INTENT(INOUT)                             :: ntasks, curr_tasks
     890              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: rab
     891              :       INTEGER, INTENT(IN)                                :: cindex, iatom, jatom, iset, jset, ipgf, &
     892              :                                                             jpgf, la_max, lb_max
     893              :       TYPE(realspace_grid_desc_type), POINTER            :: rs_desc
     894              :       INTEGER, INTENT(IN)                                :: igrid_level, n_levels
     895              :       INTEGER, DIMENSION(3), INTENT(IN)                  :: cube_center, lb_cube, ub_cube
     896              :       REAL(KIND=dp), INTENT(IN)                          :: radius
     897              : 
     898              :       INTEGER, PARAMETER                                 :: add_tasks = 1000
     899              :       REAL(kind=dp), PARAMETER                           :: mult_tasks = 2.0_dp
     900              : 
     901              :       INTEGER                                            :: added_tasks, cost, j, lmax
     902              :       LOGICAL                                            :: is_ortho
     903              :       REAL(KIND=dp)                                      :: tfraction
     904              : 
     905              : !$OMP SINGLE
     906     10823718 :       ntasks = ntasks + 1
     907     10823718 :       IF (ntasks > curr_tasks) THEN
     908         1346 :          curr_tasks = INT((curr_tasks + add_tasks)*mult_tasks)
     909         1346 :          CALL reallocate_tasks(tasks, curr_tasks)
     910              :       END IF
     911              : !$OMP END SINGLE
     912              : 
     913     10823718 :       IF (rs_desc%distributed) THEN
     914              : 
     915              :          ! finds the node(s) that need to process this task
     916              :          ! on exit tasks(:)%dist_type is 1 for distributed tasks and 2 for generalised tasks
     917              :          CALL rs_find_node(rs_desc, igrid_level, n_levels, cube_center, &
     918         1476 :                            ntasks=ntasks, tasks=tasks, lb_cube=lb_cube, ub_cube=ub_cube, added_tasks=added_tasks)
     919              : 
     920              :       ELSE
     921     10822242 :          tasks(ntasks)%destination = encode_rank(rs_desc%my_pos, igrid_level, n_levels)
     922     10822242 :          tasks(ntasks)%dist_type = 0
     923     10822242 :          tasks(ntasks)%subpatch_pattern = 0
     924     10822242 :          added_tasks = 1
     925              :       END IF
     926              : 
     927     10823718 :       lmax = la_max + lb_max
     928     10823718 :       is_ortho = (tasks(ntasks)%dist_type == 0 .OR. tasks(ntasks)%dist_type == 1) .AND. rs_desc%orthorhombic
     929              :       ! we assume the load is shared equally between processes dealing with a generalised Gaussian.
     930              :       ! this could be refined in the future
     931     10823718 :       tfraction = 1.0_dp/added_tasks
     932              : 
     933     10823718 :       cost = cost_model(lb_cube, ub_cube, tfraction, lmax, is_ortho)
     934              : 
     935     21647436 :       DO j = 1, added_tasks
     936     10823718 :          tasks(ntasks - added_tasks + j)%source = encode_rank(rs_desc%my_pos, igrid_level, n_levels)
     937     10823718 :          tasks(ntasks - added_tasks + j)%cost = cost
     938     10823718 :          tasks(ntasks - added_tasks + j)%grid_level = igrid_level
     939     10823718 :          tasks(ntasks - added_tasks + j)%image = cindex
     940     10823718 :          tasks(ntasks - added_tasks + j)%iatom = iatom
     941     10823718 :          tasks(ntasks - added_tasks + j)%jatom = jatom
     942     10823718 :          tasks(ntasks - added_tasks + j)%iset = iset
     943     10823718 :          tasks(ntasks - added_tasks + j)%jset = jset
     944     10823718 :          tasks(ntasks - added_tasks + j)%ipgf = ipgf
     945     10823718 :          tasks(ntasks - added_tasks + j)%jpgf = jpgf
     946     43294872 :          tasks(ntasks - added_tasks + j)%rab = rab
     947     21647436 :          tasks(ntasks - added_tasks + j)%radius = radius
     948              :       END DO
     949              : 
     950     10823718 :    END SUBROUTINE pgf_to_tasks
     951              : 
     952              : ! **************************************************************************************************
     953              : !> \brief performs load balancing of the tasks on the distributed grids
     954              : !> \param tasks ...
     955              : !> \param ntasks ...
     956              : !> \param rs_descs ...
     957              : !> \param grid_level ...
     958              : !> \param natoms ...
     959              : !> \par History
     960              : !>      created 2008-10-03 [Joost VandeVondele]
     961              : ! **************************************************************************************************
     962           56 :    SUBROUTINE load_balance_distributed(tasks, ntasks, rs_descs, grid_level, natoms)
     963              : 
     964              :       TYPE(task_type), DIMENSION(:), POINTER             :: tasks
     965              :       INTEGER                                            :: ntasks
     966              :       TYPE(realspace_grid_desc_p_type), DIMENSION(:), &
     967              :          POINTER                                         :: rs_descs
     968              :       INTEGER                                            :: grid_level, natoms
     969              : 
     970              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'load_balance_distributed'
     971              : 
     972              :       INTEGER                                            :: handle
     973           56 :       INTEGER, DIMENSION(:, :, :), POINTER               :: list
     974              : 
     975           56 :       CALL timeset(routineN, handle)
     976              : 
     977           56 :       NULLIFY (list)
     978              :       ! here we create for each cpu (0:ncpu-1) a list of possible destinations.
     979              :       ! if a destination would not be in this list, it is a bug
     980           56 :       CALL create_destination_list(list, rs_descs, grid_level)
     981              : 
     982              :       ! now, walk over the tasks, filling in the loads of each destination
     983           56 :       CALL compute_load_list(list, rs_descs, grid_level, tasks, ntasks, natoms, create_list=.TRUE.)
     984              : 
     985              :       ! optimize loads & fluxes
     986           56 :       CALL optimize_load_list(list, rs_descs(1)%rs_desc%group, rs_descs(1)%rs_desc%my_pos)
     987              : 
     988              :       ! now, walk over the tasks, using the list to set the destinations
     989           56 :       CALL compute_load_list(list, rs_descs, grid_level, tasks, ntasks, natoms, create_list=.FALSE.)
     990              : 
     991           56 :       DEALLOCATE (list)
     992              : 
     993           56 :       CALL timestop(handle)
     994              : 
     995           56 :    END SUBROUTINE load_balance_distributed
     996              : 
     997              : ! **************************************************************************************************
     998              : !> \brief this serial routine adjusts the fluxes in the global list
     999              : !>
    1000              : !> \param list_global ...
    1001              : !> \par History
    1002              : !>      created 2008-10-06 [Joost VandeVondele]
    1003              : ! **************************************************************************************************
    1004           28 :    SUBROUTINE balance_global_list(list_global)
    1005              :       INTEGER, DIMENSION(:, :, 0:)                       :: list_global
    1006              : 
    1007              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'balance_global_list'
    1008              :       INTEGER, PARAMETER                                 :: Max_Iter = 100
    1009              :       REAL(KIND=dp), PARAMETER                           :: Tolerance_factor = 0.005_dp
    1010              : 
    1011              :       INTEGER                                            :: dest, handle, icpu, idest, iflux, &
    1012              :                                                             ilocal, k, maxdest, Ncpu, Nflux
    1013           28 :       INTEGER, ALLOCATABLE, DIMENSION(:, :)              :: flux_connections
    1014              :       LOGICAL                                            :: solution_optimal
    1015              :       REAL(KIND=dp)                                      :: average, load_shift, max_load_shift, &
    1016              :                                                             tolerance
    1017           28 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: load, optimized_flux, optimized_load
    1018           28 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :)        :: flux_limits
    1019              : 
    1020           28 :       CALL timeset(routineN, handle)
    1021              : 
    1022           28 :       Ncpu = SIZE(list_global, 3)
    1023           28 :       maxdest = SIZE(list_global, 2)
    1024           84 :       ALLOCATE (load(0:Ncpu - 1))
    1025           28 :       load = 0.0_dp
    1026           56 :       ALLOCATE (optimized_load(0:Ncpu - 1))
    1027              : 
    1028              :       ! figure out the number of fluxes
    1029              :       ! we assume that the global_list is symmetric
    1030           84 :       Nflux = 0
    1031           84 :       DO icpu = 0, ncpu - 1
    1032          196 :          DO idest = 1, maxdest
    1033          112 :             dest = list_global(1, idest, icpu)
    1034          168 :             IF (dest < ncpu .AND. dest > icpu) Nflux = Nflux + 1
    1035              :          END DO
    1036              :       END DO
    1037           84 :       ALLOCATE (optimized_flux(Nflux))
    1038           84 :       ALLOCATE (flux_limits(2, Nflux))
    1039           84 :       ALLOCATE (flux_connections(2, Nflux))
    1040              : 
    1041              :       ! reorder data
    1042           28 :       flux_limits = 0
    1043           28 :       Nflux = 0
    1044           84 :       DO icpu = 0, ncpu - 1
    1045          168 :          load(icpu) = SUM(list_global(2, :, icpu))
    1046          196 :          DO idest = 1, maxdest
    1047          112 :             dest = list_global(1, idest, icpu)
    1048          168 :             IF (dest < ncpu) THEN
    1049          112 :                IF (dest /= icpu) THEN
    1050           56 :                   IF (dest > icpu) THEN
    1051           28 :                      Nflux = Nflux + 1
    1052           28 :                      flux_limits(2, Nflux) = list_global(2, idest, icpu)
    1053           28 :                      flux_connections(1, Nflux) = icpu
    1054           28 :                      flux_connections(2, Nflux) = dest
    1055              :                   ELSE
    1056           28 :                      DO iflux = 1, Nflux
    1057           28 :                         IF (flux_connections(1, iflux) == dest .AND. flux_connections(2, iflux) == icpu) THEN
    1058           28 :                            flux_limits(1, iflux) = -list_global(2, idest, icpu)
    1059           28 :                            EXIT
    1060              :                         END IF
    1061              :                      END DO
    1062              :                   END IF
    1063              :                END IF
    1064              :             END IF
    1065              :          END DO
    1066              :       END DO
    1067              : 
    1068           28 :       solution_optimal = .FALSE.
    1069           28 :       optimized_flux = 0.0_dp
    1070              : 
    1071              :       ! an iterative solver, if iterated till convergence the maximum load is minimal
    1072              :       ! we terminate before things are fully converged, since this does show up in the timings
    1073              :       ! once the largest shift becomes less than a small fraction of the average load, we're done
    1074              :       ! we're perfectly happy if the load balance is within 1 percent or so
    1075              :       ! the maximum load normally converges even faster
    1076           84 :       average = SUM(load)/SIZE(load)
    1077           28 :       tolerance = Tolerance_factor*average
    1078              : 
    1079           84 :       optimized_load(:) = load
    1080          333 :       DO k = 1, Max_iter
    1081              :          max_load_shift = 0.0_dp
    1082          660 :          DO iflux = 1, Nflux
    1083          330 :             load_shift = (optimized_load(flux_connections(1, iflux)) - optimized_load(flux_connections(2, iflux)))/2
    1084          330 :             load_shift = MAX(flux_limits(1, iflux) - optimized_flux(iflux), load_shift)
    1085          330 :             load_shift = MIN(flux_limits(2, iflux) - optimized_flux(iflux), load_shift)
    1086          330 :             max_load_shift = MAX(ABS(load_shift), max_load_shift)
    1087          330 :             optimized_load(flux_connections(1, iflux)) = optimized_load(flux_connections(1, iflux)) - load_shift
    1088          330 :             optimized_load(flux_connections(2, iflux)) = optimized_load(flux_connections(2, iflux)) + load_shift
    1089          660 :             optimized_flux(iflux) = optimized_flux(iflux) + load_shift
    1090              :          END DO
    1091          333 :          IF (max_load_shift < tolerance) THEN
    1092           84 :             solution_optimal = .TRUE.
    1093              :             EXIT
    1094              :          END IF
    1095              :       END DO
    1096              : 
    1097              :       ! now adjust the load list to reflect the optimized fluxes
    1098              :       ! reorder data
    1099              :       Nflux = 0
    1100           84 :       DO icpu = 0, ncpu - 1
    1101          168 :          DO idest = 1, maxdest
    1102          168 :             IF (list_global(1, idest, icpu) == icpu) ilocal = idest
    1103              :          END DO
    1104          196 :          DO idest = 1, maxdest
    1105          112 :             dest = list_global(1, idest, icpu)
    1106          168 :             IF (dest < ncpu) THEN
    1107          112 :                IF (dest /= icpu) THEN
    1108           56 :                   IF (dest > icpu) THEN
    1109           28 :                      Nflux = Nflux + 1
    1110           28 :                      IF (optimized_flux(Nflux) > 0) THEN
    1111              :                         list_global(2, ilocal, icpu) = list_global(2, ilocal, icpu) + &
    1112            0 :                                                        list_global(2, idest, icpu) - NINT(optimized_flux(Nflux))
    1113            0 :                         list_global(2, idest, icpu) = NINT(optimized_flux(Nflux))
    1114              :                      ELSE
    1115              :                         list_global(2, ilocal, icpu) = list_global(2, ilocal, icpu) + &
    1116           28 :                                                        list_global(2, idest, icpu)
    1117           28 :                         list_global(2, idest, icpu) = 0
    1118              :                      END IF
    1119              :                   ELSE
    1120           28 :                      DO iflux = 1, Nflux
    1121           28 :                         IF (flux_connections(1, iflux) == dest .AND. flux_connections(2, iflux) == icpu) THEN
    1122           28 :                            IF (optimized_flux(iflux) > 0) THEN
    1123              :                               list_global(2, ilocal, icpu) = list_global(2, ilocal, icpu) + &
    1124            0 :                                                              list_global(2, idest, icpu)
    1125            0 :                               list_global(2, idest, icpu) = 0
    1126              :                            ELSE
    1127              :                               list_global(2, ilocal, icpu) = list_global(2, ilocal, icpu) + &
    1128           28 :                                                              list_global(2, idest, icpu) + NINT(optimized_flux(iflux))
    1129           28 :                               list_global(2, idest, icpu) = -NINT(optimized_flux(iflux))
    1130              :                            END IF
    1131              :                            EXIT
    1132              :                         END IF
    1133              :                      END DO
    1134              :                   END IF
    1135              :                END IF
    1136              :             END IF
    1137              :          END DO
    1138              :       END DO
    1139              : 
    1140           28 :       CALL timestop(handle)
    1141              : 
    1142           56 :    END SUBROUTINE balance_global_list
    1143              : 
    1144              : ! **************************************************************************************************
    1145              : !> \brief this routine gets back optimized loads for all destinations
    1146              : !>
    1147              : !> \param list ...
    1148              : !> \param group ...
    1149              : !> \param my_pos ...
    1150              : !> \par History
    1151              : !>      created 2008-10-06 [Joost VandeVondele]
    1152              : !>      Modified 2016-01   [EPCC] Reduce memory requirements on P processes
    1153              : !>                                from O(P^2) to O(P)
    1154              : ! **************************************************************************************************
    1155           56 :    SUBROUTINE optimize_load_list(list, group, my_pos)
    1156              :       INTEGER, DIMENSION(:, :, 0:)                       :: list
    1157              :       TYPE(mp_comm_type), INTENT(IN) :: group
    1158              :       INTEGER, INTENT(IN)                                :: my_pos
    1159              : 
    1160              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'optimize_load_list'
    1161              :       INTEGER, PARAMETER                                 :: rank_of_root = 0
    1162              : 
    1163              :       INTEGER                                            :: handle, icpu, idest, maxdest, ncpu
    1164              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: load_all
    1165           56 :       INTEGER, ALLOCATABLE, DIMENSION(:, :)              :: load_partial
    1166              :       INTEGER, ALLOCATABLE, DIMENSION(:, :, :)           :: list_global
    1167              : 
    1168           56 :       CALL timeset(routineN, handle)
    1169              : 
    1170           56 :       ncpu = SIZE(list, 3)
    1171           56 :       maxdest = SIZE(list, 2)
    1172              : 
    1173              :       !find total workload ...
    1174          168 :       ALLOCATE (load_all(maxdest*ncpu))
    1175          112 :       load_all(:) = RESHAPE(list(2, :, :), [maxdest*ncpu])
    1176           56 :       CALL group%sum(load_all(:), rank_of_root)
    1177              : 
    1178              :       ! ... and optimise the work per process
    1179          224 :       ALLOCATE (list_global(2, maxdest, ncpu))
    1180           56 :       IF (rank_of_root == my_pos) THEN
    1181          196 :          list_global(1, :, :) = list(1, :, :)
    1182          252 :          list_global(2, :, :) = RESHAPE(load_all, [maxdest, ncpu])
    1183           28 :          CALL balance_global_list(list_global)
    1184              :       END IF
    1185           56 :       CALL group%bcast(list_global, rank_of_root)
    1186              : 
    1187              :       !figure out how much can be sent to other processes
    1188          224 :       ALLOCATE (load_partial(maxdest, ncpu))
    1189              :       ! send 'load_all', which is a copy of 'list' (but without leading dimension/stride)
    1190          168 :       CALL group%sum_partial(RESHAPE(load_all, [maxdest, ncpu]), load_partial(:, :))
    1191              : 
    1192          168 :       DO icpu = 1, ncpu
    1193          392 :          DO idest = 1, maxdest
    1194              : 
    1195              :             !need to deduct 1 because `list' was passed in to this routine as being indexed from zero
    1196          336 :             IF (load_partial(idest, icpu) > list_global(2, idest, icpu)) THEN
    1197           38 :                IF (load_partial(idest, icpu) - list(2, idest, icpu - 1) < list_global(2, idest, icpu)) THEN
    1198              :                   list(2, idest, icpu - 1) = list_global(2, idest, icpu) &
    1199           10 :                                              - (load_partial(idest, icpu) - list(2, idest, icpu - 1))
    1200              :                ELSE
    1201           28 :                   list(2, idest, icpu - 1) = 0
    1202              :                END IF
    1203              :             END IF
    1204              : 
    1205              :          END DO
    1206              :       END DO
    1207              : 
    1208              :       !clean up before leaving
    1209           56 :       DEALLOCATE (load_all)
    1210           56 :       DEALLOCATE (list_global)
    1211           56 :       DEALLOCATE (load_partial)
    1212              : 
    1213           56 :       CALL timestop(handle)
    1214           56 :    END SUBROUTINE optimize_load_list
    1215              : 
    1216              : ! **************************************************************************************************
    1217              : !> \brief fill the load list with values derived from the tasks array
    1218              : !>        from the alternate locations, we select the alternate location that
    1219              : !>        can be used without increasing the number of matrix blocks needed to
    1220              : !>        distribute.
    1221              : !>        Replicated tasks are not yet considered
    1222              : !>
    1223              : !> \param list ...
    1224              : !> \param rs_descs ...
    1225              : !> \param grid_level ...
    1226              : !> \param tasks ...
    1227              : !> \param ntasks ...
    1228              : !> \param natoms ...
    1229              : !> \param create_list ...
    1230              : !> \par History
    1231              : !>      created 2008-10-06 [Joost VandeVondele]
    1232              : ! **************************************************************************************************
    1233          112 :    SUBROUTINE compute_load_list(list, rs_descs, grid_level, tasks, ntasks, natoms, create_list)
    1234              :       INTEGER, DIMENSION(:, :, 0:)                       :: list
    1235              :       TYPE(realspace_grid_desc_p_type), DIMENSION(:), &
    1236              :          POINTER                                         :: rs_descs
    1237              :       INTEGER                                            :: grid_level
    1238              :       TYPE(task_type), DIMENSION(:), POINTER             :: tasks
    1239              :       INTEGER                                            :: ntasks, natoms
    1240              :       LOGICAL                                            :: create_list
    1241              : 
    1242              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_load_list'
    1243              : 
    1244              :       INTEGER :: cost, dest, handle, i, iatom, ilevel, img, img_old, iopt, ipgf, iset, itask, &
    1245              :                  itask_start, itask_stop, jatom, jpgf, jset, li, maxdest, ncpu, ndest_pair, nopt, nshort, &
    1246              :                  rank
    1247              :       INTEGER(KIND=int_8)                                :: bit_pattern, ipair, ipair_old, natom8
    1248              :       INTEGER(KIND=int_8), ALLOCATABLE, DIMENSION(:)     :: loads
    1249          112 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: all_dests, index
    1250              :       INTEGER, DIMENSION(6)                              :: options
    1251              : 
    1252          112 :       CALL timeset(routineN, handle)
    1253              : 
    1254          336 :       ALLOCATE (loads(0:rs_descs(grid_level)%rs_desc%group_size - 1))
    1255          112 :       CALL get_current_loads(loads, rs_descs, grid_level, ntasks, tasks, use_reordered_ranks=.FALSE.)
    1256              : 
    1257          112 :       maxdest = SIZE(list, 2)
    1258          112 :       ncpu = SIZE(list, 3)
    1259          112 :       natom8 = natoms
    1260              : 
    1261              :       ! first find the tasks that deal with the same atom pair
    1262          112 :       itask_stop = 0
    1263          112 :       ipair_old = HUGE(ipair_old)
    1264          112 :       img_old = -1
    1265          112 :       ALLOCATE (all_dests(0))
    1266          112 :       ALLOCATE (INDEX(0))
    1267              : 
    1268              :       DO
    1269              : 
    1270              :          ! first find the range of tasks that deal with the same atom pair
    1271          306 :          itask_start = itask_stop + 1
    1272          306 :          itask_stop = itask_start
    1273          306 :          IF (itask_stop > ntasks) EXIT
    1274          194 :          ilevel = tasks(itask_stop)%grid_level
    1275          194 :          img_old = tasks(itask_stop)%image
    1276          194 :          iatom = tasks(itask_stop)%iatom
    1277          194 :          jatom = tasks(itask_stop)%jatom
    1278          194 :          iset = tasks(itask_stop)%iset
    1279          194 :          jset = tasks(itask_stop)%jset
    1280          194 :          ipgf = tasks(itask_stop)%ipgf
    1281          194 :          jpgf = tasks(itask_stop)%jpgf
    1282              : 
    1283          194 :          ipair_old = (iatom - 1)*natom8 + (jatom - 1)
    1284              :          DO
    1285         7842 :             IF (itask_stop + 1 > ntasks) EXIT
    1286         7750 :             ilevel = tasks(itask_stop + 1)%grid_level
    1287         7750 :             img = tasks(itask_stop + 1)%image
    1288         7750 :             iatom = tasks(itask_stop + 1)%iatom
    1289         7750 :             jatom = tasks(itask_stop + 1)%jatom
    1290         7750 :             iset = tasks(itask_stop + 1)%iset
    1291         7750 :             jset = tasks(itask_stop + 1)%jset
    1292         7750 :             ipgf = tasks(itask_stop + 1)%ipgf
    1293         7750 :             jpgf = tasks(itask_stop + 1)%jpgf
    1294              : 
    1295         7750 :             ipair = (iatom - 1)*natom8 + (jatom - 1)
    1296         7842 :             IF (ipair == ipair_old .AND. img == img_old) THEN
    1297              :                itask_stop = itask_stop + 1
    1298              :             ELSE
    1299              :                EXIT
    1300              :             END IF
    1301              :          END DO
    1302          194 :          ipair = ipair_old
    1303          194 :          nshort = itask_stop - itask_start + 1
    1304              : 
    1305              :          ! find the unique list of destinations on this grid level only
    1306          194 :          DEALLOCATE (all_dests)
    1307          582 :          ALLOCATE (all_dests(nshort))
    1308          194 :          DEALLOCATE (index)
    1309          388 :          ALLOCATE (INDEX(nshort))
    1310         8036 :          DO i = 1, nshort
    1311         7842 :             ilevel = tasks(itask_start + i - 1)%grid_level
    1312         7842 :             img = tasks(itask_start + i - 1)%image
    1313         7842 :             iatom = tasks(itask_start + i - 1)%iatom
    1314         7842 :             jatom = tasks(itask_start + i - 1)%jatom
    1315         7842 :             iset = tasks(itask_start + i - 1)%iset
    1316         7842 :             jset = tasks(itask_start + i - 1)%jset
    1317         7842 :             ipgf = tasks(itask_start + i - 1)%ipgf
    1318         7842 :             jpgf = tasks(itask_start + i - 1)%jpgf
    1319              : 
    1320         8036 :             IF (ilevel == grid_level) THEN
    1321         2952 :                all_dests(i) = decode_rank(tasks(itask_start + i - 1)%destination, SIZE(rs_descs))
    1322              :             ELSE
    1323         4890 :                all_dests(i) = HUGE(all_dests(i))
    1324              :             END IF
    1325              :          END DO
    1326          194 :          CALL sort(all_dests, nshort, index)
    1327          194 :          ndest_pair = 1
    1328         7842 :          DO i = 2, nshort
    1329         7842 :             IF ((all_dests(ndest_pair) /= all_dests(i)) .AND. (all_dests(i) /= HUGE(all_dests(i)))) THEN
    1330           10 :                ndest_pair = ndest_pair + 1
    1331           10 :                all_dests(ndest_pair) = all_dests(i)
    1332              :             END IF
    1333              :          END DO
    1334              : 
    1335         8148 :          DO itask = itask_start, itask_stop
    1336              : 
    1337         7842 :             dest = decode_rank(tasks(itask)%destination, SIZE(rs_descs)) ! notice that dest can be changed
    1338         7842 :             ilevel = tasks(itask)%grid_level
    1339         7842 :             img = tasks(itask)%image
    1340         7842 :             iatom = tasks(itask)%iatom
    1341         7842 :             jatom = tasks(itask)%jatom
    1342         7842 :             iset = tasks(itask)%iset
    1343         7842 :             jset = tasks(itask)%jset
    1344         7842 :             ipgf = tasks(itask)%ipgf
    1345         7842 :             jpgf = tasks(itask)%jpgf
    1346              : 
    1347              :             ! Only proceed with tasks which are on this grid level
    1348         7842 :             IF (ilevel /= grid_level) CYCLE
    1349         2952 :             ipair = (iatom - 1)*natom8 + (jatom - 1)
    1350         2952 :             cost = INT(tasks(itask)%cost)
    1351              : 
    1352          194 :             SELECT CASE (tasks(itask)%dist_type)
    1353              :             CASE (1)
    1354         2952 :                bit_pattern = tasks(itask)%subpatch_pattern
    1355         2952 :                nopt = 0
    1356         2952 :                IF (BTEST(bit_pattern, 0)) THEN
    1357            0 :                   rank = rs_grid_locate_rank(rs_descs(ilevel)%rs_desc, dest, [-1, 0, 0])
    1358            0 :                   IF (ANY(all_dests(1:ndest_pair) == rank)) THEN
    1359            0 :                      nopt = nopt + 1
    1360            0 :                      options(nopt) = rank
    1361              :                   END IF
    1362              :                END IF
    1363         2952 :                IF (BTEST(bit_pattern, 1)) THEN
    1364            0 :                   rank = rs_grid_locate_rank(rs_descs(ilevel)%rs_desc, dest, [+1, 0, 0])
    1365            0 :                   IF (ANY(all_dests(1:ndest_pair) == rank)) THEN
    1366            0 :                      nopt = nopt + 1
    1367            0 :                      options(nopt) = rank
    1368              :                   END IF
    1369              :                END IF
    1370         2952 :                IF (BTEST(bit_pattern, 2)) THEN
    1371           48 :                   rank = rs_grid_locate_rank(rs_descs(ilevel)%rs_desc, dest, [0, -1, 0])
    1372           96 :                   IF (ANY(all_dests(1:ndest_pair) == rank)) THEN
    1373            0 :                      nopt = nopt + 1
    1374            0 :                      options(nopt) = rank
    1375              :                   END IF
    1376              :                END IF
    1377         2952 :                IF (BTEST(bit_pattern, 3)) THEN
    1378            0 :                   rank = rs_grid_locate_rank(rs_descs(ilevel)%rs_desc, dest, [0, +1, 0])
    1379            0 :                   IF (ANY(all_dests(1:ndest_pair) == rank)) THEN
    1380            0 :                      nopt = nopt + 1
    1381            0 :                      options(nopt) = rank
    1382              :                   END IF
    1383              :                END IF
    1384         2952 :                IF (BTEST(bit_pattern, 4)) THEN
    1385         1342 :                   rank = rs_grid_locate_rank(rs_descs(ilevel)%rs_desc, dest, [0, 0, -1])
    1386         2584 :                   IF (ANY(all_dests(1:ndest_pair) == rank)) THEN
    1387          100 :                      nopt = nopt + 1
    1388          100 :                      options(nopt) = rank
    1389              :                   END IF
    1390              :                END IF
    1391         2952 :                IF (BTEST(bit_pattern, 5)) THEN
    1392          500 :                   rank = rs_grid_locate_rank(rs_descs(ilevel)%rs_desc, dest, [0, 0, +1])
    1393         1000 :                   IF (ANY(all_dests(1:ndest_pair) == rank)) THEN
    1394           80 :                      nopt = nopt + 1
    1395           80 :                      options(nopt) = rank
    1396              :                   END IF
    1397              :                END IF
    1398         2952 :                IF (nopt > 0) THEN
    1399              :                   ! set it to the rank with the lowest load
    1400          180 :                   rank = options(1)
    1401          180 :                   DO iopt = 2, nopt
    1402          180 :                      IF (loads(rank) > loads(options(iopt))) rank = options(iopt)
    1403              :                   END DO
    1404              :                ELSE
    1405         2772 :                   rank = dest
    1406              :                END IF
    1407         2952 :                li = list_index(list, rank, dest)
    1408         2952 :                IF (create_list) THEN
    1409         1476 :                   list(2, li, dest) = list(2, li, dest) + cost
    1410              :                ELSE
    1411         1476 :                   IF (list(1, li, dest) == dest) THEN
    1412         1386 :                      tasks(itask)%destination = encode_rank(dest, ilevel, SIZE(rs_descs))
    1413              :                   ELSE
    1414           90 :                      IF (list(2, li, dest) >= cost) THEN
    1415           10 :                         list(2, li, dest) = list(2, li, dest) - cost
    1416           10 :                         tasks(itask)%destination = encode_rank(list(1, li, dest), ilevel, SIZE(rs_descs))
    1417              :                      ELSE
    1418           80 :                         tasks(itask)%destination = encode_rank(dest, ilevel, SIZE(rs_descs))
    1419              :                      END IF
    1420              :                   END IF
    1421              :                END IF
    1422              :             CASE (2) ! generalised
    1423            0 :                li = list_index(list, dest, dest)
    1424            0 :                IF (create_list) THEN
    1425            0 :                   list(2, li, dest) = list(2, li, dest) + cost
    1426              :                ELSE
    1427            0 :                   IF (list(1, li, dest) == dest) THEN
    1428            0 :                      tasks(itask)%destination = encode_rank(dest, ilevel, SIZE(rs_descs))
    1429              :                   ELSE
    1430            0 :                      IF (list(2, li, dest) >= cost) THEN
    1431            0 :                         list(2, li, dest) = list(2, li, dest) - cost
    1432            0 :                         tasks(itask)%destination = encode_rank(list(1, li, dest), ilevel, SIZE(rs_descs))
    1433              :                      ELSE
    1434            0 :                         tasks(itask)%destination = encode_rank(dest, ilevel, SIZE(rs_descs))
    1435              :                      END IF
    1436              :                   END IF
    1437              :                END IF
    1438              :             CASE DEFAULT
    1439         7842 :                CPABORT("Unknown task list distribution type")
    1440              :             END SELECT
    1441              : 
    1442              :          END DO
    1443              : 
    1444              :       END DO
    1445              : 
    1446          112 :       CALL timestop(handle)
    1447              : 
    1448          224 :    END SUBROUTINE compute_load_list
    1449              : ! **************************************************************************************************
    1450              : !> \brief small helper function to return the proper index in the list array
    1451              : !>
    1452              : !> \param list ...
    1453              : !> \param rank ...
    1454              : !> \param dest ...
    1455              : !> \return ...
    1456              : !> \par History
    1457              : !>      created 2008-10-06 [Joost VandeVondele]
    1458              : ! **************************************************************************************************
    1459         2952 :    INTEGER FUNCTION list_index(list, rank, dest)
    1460              :       INTEGER, DIMENSION(:, :, 0:), INTENT(IN)           :: list
    1461              :       INTEGER, INTENT(IN)                                :: rank, dest
    1462              : 
    1463         2952 :       list_index = 1
    1464         1424 :       DO
    1465         4376 :          IF (list(1, list_index, dest) == rank) EXIT
    1466         1424 :          list_index = list_index + 1
    1467              :       END DO
    1468         2952 :    END FUNCTION list_index
    1469              : ! **************************************************************************************************
    1470              : !> \brief create a list with possible destinations (i.e. the central cpu and neighbors) for each cpu
    1471              : !>        note that we allocate it with an additional field to store the load of this destination
    1472              : !>
    1473              : !> \param list ...
    1474              : !> \param rs_descs ...
    1475              : !> \param grid_level ...
    1476              : !> \par History
    1477              : !>      created 2008-10-06 [Joost VandeVondele]
    1478              : ! **************************************************************************************************
    1479           56 :    SUBROUTINE create_destination_list(list, rs_descs, grid_level)
    1480              :       INTEGER, DIMENSION(:, :, :), POINTER               :: list
    1481              :       TYPE(realspace_grid_desc_p_type), DIMENSION(:), &
    1482              :          POINTER                                         :: rs_descs
    1483              :       INTEGER, INTENT(IN)                                :: grid_level
    1484              : 
    1485              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'create_destination_list'
    1486              : 
    1487              :       INTEGER                                            :: handle, i, icpu, j, maxcount, ncpu, &
    1488              :                                                             ultimate_max
    1489           56 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: index, sublist
    1490              : 
    1491           56 :       CALL timeset(routineN, handle)
    1492              : 
    1493           56 :       CPASSERT(.NOT. ASSOCIATED(list))
    1494           56 :       ncpu = rs_descs(grid_level)%rs_desc%group_size
    1495           56 :       ultimate_max = 7
    1496              : 
    1497          168 :       ALLOCATE (list(2, ultimate_max, 0:ncpu - 1))
    1498              : 
    1499           56 :       ALLOCATE (INDEX(ultimate_max))
    1500           56 :       ALLOCATE (sublist(ultimate_max))
    1501          448 :       sublist = HUGE(sublist)
    1502              : 
    1503           56 :       maxcount = 1
    1504          168 :       DO icpu = 0, ncpu - 1
    1505          112 :          sublist(1) = icpu
    1506          112 :          sublist(2) = rs_grid_locate_rank(rs_descs(grid_level)%rs_desc, icpu, [-1, 0, 0])
    1507          112 :          sublist(3) = rs_grid_locate_rank(rs_descs(grid_level)%rs_desc, icpu, [+1, 0, 0])
    1508          112 :          sublist(4) = rs_grid_locate_rank(rs_descs(grid_level)%rs_desc, icpu, [0, -1, 0])
    1509          112 :          sublist(5) = rs_grid_locate_rank(rs_descs(grid_level)%rs_desc, icpu, [0, +1, 0])
    1510          112 :          sublist(6) = rs_grid_locate_rank(rs_descs(grid_level)%rs_desc, icpu, [0, 0, -1])
    1511          112 :          sublist(7) = rs_grid_locate_rank(rs_descs(grid_level)%rs_desc, icpu, [0, 0, +1])
    1512              :          ! only retain unique values of the destination
    1513          112 :          CALL sort(sublist, ultimate_max, index)
    1514          112 :          j = 1
    1515          784 :          DO i = 2, 7
    1516          784 :             IF (sublist(i) /= sublist(j)) THEN
    1517          112 :                j = j + 1
    1518          112 :                sublist(j) = sublist(i)
    1519              :             END IF
    1520              :          END DO
    1521          112 :          maxcount = MAX(maxcount, j)
    1522          672 :          sublist(j + 1:ultimate_max) = HUGE(sublist)
    1523          896 :          list(1, :, icpu) = sublist
    1524          952 :          list(2, :, icpu) = 0
    1525              :       END DO
    1526              : 
    1527           56 :       CALL reallocate(list, 1, 2, 1, maxcount, 0, ncpu - 1)
    1528              : 
    1529           56 :       CALL timestop(handle)
    1530              : 
    1531          112 :    END SUBROUTINE create_destination_list
    1532              : 
    1533              : ! **************************************************************************************************
    1534              : !> \brief given a task list, compute the load of each process everywhere
    1535              : !>        giving this function the ability to loop over a (sub)set of rs_grids,
    1536              : !>        and do all the communication in one shot, would speed it up
    1537              : !> \param loads ...
    1538              : !> \param rs_descs ...
    1539              : !> \param grid_level ...
    1540              : !> \param ntasks ...
    1541              : !> \param tasks ...
    1542              : !> \param use_reordered_ranks ...
    1543              : !> \par History
    1544              : !>      none
    1545              : !> \author MattW 21/11/2007
    1546              : ! **************************************************************************************************
    1547          500 :    SUBROUTINE get_current_loads(loads, rs_descs, grid_level, ntasks, tasks, use_reordered_ranks)
    1548              :       INTEGER(KIND=int_8), DIMENSION(:)                  :: loads
    1549              :       TYPE(realspace_grid_desc_p_type), DIMENSION(:), &
    1550              :          POINTER                                         :: rs_descs
    1551              :       INTEGER                                            :: grid_level, ntasks
    1552              :       TYPE(task_type), DIMENSION(:), POINTER             :: tasks
    1553              :       LOGICAL, INTENT(IN)                                :: use_reordered_ranks
    1554              : 
    1555              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'get_current_loads'
    1556              : 
    1557              :       INTEGER                                            :: handle, i, iatom, ilevel, img, ipgf, &
    1558              :                                                             iset, jatom, jpgf, jset
    1559              :       INTEGER(KIND=int_8)                                :: total_cost_local
    1560              :       INTEGER(KIND=int_8), ALLOCATABLE, DIMENSION(:)     :: recv_buf_i, send_buf_i
    1561              :       TYPE(realspace_grid_desc_type), POINTER            :: desc
    1562              : 
    1563          500 :       CALL timeset(routineN, handle)
    1564              : 
    1565          500 :       desc => rs_descs(grid_level)%rs_desc
    1566              : 
    1567              :       ! allocate local arrays
    1568         1500 :       ALLOCATE (send_buf_i(desc%group_size))
    1569         1000 :       ALLOCATE (recv_buf_i(desc%group_size))
    1570              : 
    1571              :       ! communication step 1 : compute the total local cost of the tasks
    1572              :       !                        each proc needs to know the amount of work he will receive
    1573              : 
    1574              :       ! send buffer now contains for each target the cost of the tasks it will receive
    1575          500 :       send_buf_i = 0
    1576        39092 :       DO i = 1, ntasks
    1577        38592 :          ilevel = tasks(i)%grid_level
    1578        38592 :          img = tasks(i)%image
    1579        38592 :          iatom = tasks(i)%iatom
    1580        38592 :          jatom = tasks(i)%jatom
    1581        38592 :          iset = tasks(i)%iset
    1582        38592 :          jset = tasks(i)%jset
    1583        38592 :          ipgf = tasks(i)%ipgf
    1584        38592 :          jpgf = tasks(i)%jpgf
    1585        38592 :          IF (ilevel /= grid_level) CYCLE
    1586        11144 :          IF (use_reordered_ranks) THEN
    1587              :             send_buf_i(rs_descs(ilevel)%rs_desc%virtual2real(decode_rank(tasks(i)%destination, SIZE(rs_descs))) + 1) = &
    1588              :                send_buf_i(rs_descs(ilevel)%rs_desc%virtual2real(decode_rank(tasks(i)%destination, SIZE(rs_descs))) + 1) &
    1589         3846 :                + tasks(i)%cost
    1590              :          ELSE
    1591              :             send_buf_i(decode_rank(tasks(i)%destination, SIZE(rs_descs)) + 1) = &
    1592              :                send_buf_i(decode_rank(tasks(i)%destination, SIZE(rs_descs)) + 1) &
    1593         6798 :                + tasks(i)%cost
    1594              :          END IF
    1595              :       END DO
    1596          500 :       CALL desc%group%alltoall(send_buf_i, recv_buf_i, 1)
    1597              : 
    1598              :       ! communication step 2 : compute the global cost of the tasks
    1599         1500 :       total_cost_local = SUM(recv_buf_i)
    1600              : 
    1601              :       ! after this step, the recv buffer contains the local cost for each CPU
    1602         1500 :       CALL desc%group%allgather(total_cost_local, loads)
    1603              : 
    1604          500 :       CALL timestop(handle)
    1605              : 
    1606         1000 :    END SUBROUTINE get_current_loads
    1607              : ! **************************************************************************************************
    1608              : !> \brief performs load balancing shifting tasks on the replicated grids
    1609              : !>        this modifies the destination of some of the tasks on replicated
    1610              : !>        grids, and in this way balances the load
    1611              : !> \param rs_descs ...
    1612              : !> \param ntasks ...
    1613              : !> \param tasks ...
    1614              : !> \par History
    1615              : !>      none
    1616              : !> \author MattW 21/11/2007
    1617              : ! **************************************************************************************************
    1618           50 :    SUBROUTINE load_balance_replicated(rs_descs, ntasks, tasks)
    1619              : 
    1620              :       TYPE(realspace_grid_desc_p_type), DIMENSION(:), &
    1621              :          POINTER                                         :: rs_descs
    1622              :       INTEGER                                            :: ntasks
    1623              :       TYPE(task_type), DIMENSION(:), POINTER             :: tasks
    1624              : 
    1625              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'load_balance_replicated'
    1626              : 
    1627              :       INTEGER                                            :: handle, i, iatom, ilevel, img, ipgf, &
    1628              :                                                             iset, j, jatom, jpgf, jset, &
    1629              :                                                             no_overloaded, no_underloaded, &
    1630              :                                                             proc_receiving
    1631              :       INTEGER(KIND=int_8)                                :: average_cost, cost_task_rep, count, &
    1632              :                                                             offset, total_cost_global
    1633           50 :       INTEGER(KIND=int_8), ALLOCATABLE, DIMENSION(:)     :: load_imbalance, loads, recv_buf_i
    1634           50 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: index
    1635              :       TYPE(realspace_grid_desc_type), POINTER            :: desc
    1636              : 
    1637           50 :       CALL timeset(routineN, handle)
    1638              : 
    1639           50 :       desc => rs_descs(1)%rs_desc
    1640              : 
    1641              :       ! allocate local arrays
    1642          150 :       ALLOCATE (recv_buf_i(desc%group_size))
    1643          100 :       ALLOCATE (loads(desc%group_size))
    1644              : 
    1645           50 :       recv_buf_i = 0
    1646          244 :       DO i = 1, SIZE(rs_descs)
    1647          194 :          CALL get_current_loads(loads, rs_descs, i, ntasks, tasks, use_reordered_ranks=.TRUE.)
    1648          632 :          recv_buf_i(:) = recv_buf_i + loads
    1649              :       END DO
    1650              : 
    1651          150 :       total_cost_global = SUM(recv_buf_i)
    1652           50 :       average_cost = total_cost_global/desc%group_size
    1653              : 
    1654              :       !
    1655              :       ! compute how to redistribute the replicated tasks so that the average cost is reached
    1656              :       !
    1657              : 
    1658              :       ! load imbalance measures the load of a given CPU relative
    1659              :       ! to the optimal load distribution (load=average)
    1660          150 :       ALLOCATE (load_imbalance(desc%group_size))
    1661          150 :       ALLOCATE (INDEX(desc%group_size))
    1662              : 
    1663          150 :       load_imbalance(:) = recv_buf_i - average_cost
    1664           50 :       no_overloaded = 0
    1665           50 :       no_underloaded = 0
    1666              : 
    1667          150 :       DO i = 1, desc%group_size
    1668          100 :          IF (load_imbalance(i) > 0) no_overloaded = no_overloaded + 1
    1669          150 :          IF (load_imbalance(i) < 0) no_underloaded = no_underloaded + 1
    1670              :       END DO
    1671              : 
    1672              :       ! sort the recv_buffer on number of tasks, gives us index which provides a
    1673              :       ! mapping between processor ranks and how overloaded the processor
    1674           50 :       CALL sort(recv_buf_i, SIZE(recv_buf_i), index)
    1675              : 
    1676              :       ! find out the number of replicated tasks each proc has
    1677              :       ! but only those tasks which have not yet been assigned
    1678           50 :       cost_task_rep = 0
    1679         3896 :       DO i = 1, ntasks
    1680              :          IF (tasks(i)%dist_type == 0 &
    1681         3896 :              .AND. decode_rank(tasks(i)%destination, SIZE(rs_descs)) == decode_rank(tasks(i)%source, SIZE(rs_descs))) THEN
    1682         2370 :             cost_task_rep = cost_task_rep + tasks(i)%cost
    1683              :          END IF
    1684              :       END DO
    1685              : 
    1686              :       ! now, correct the load imbalance for the overloaded CPUs
    1687              :       ! they will send away not more than the total load of replicated tasks
    1688           50 :       CALL desc%group%allgather(cost_task_rep, recv_buf_i)
    1689              : 
    1690          150 :       DO i = 1, desc%group_size
    1691              :          ! At the moment we can only offload replicated tasks
    1692          150 :          IF (load_imbalance(i) > 0) load_imbalance(i) = MIN(load_imbalance(i), recv_buf_i(i))
    1693              :       END DO
    1694              : 
    1695              :       ! simplest algorithm I can think of of is that the processor with the most
    1696              :       ! excess tasks fills up the process needing most, then moves on to next most.
    1697              :       ! At the moment if we've got less replicated tasks than we're overloaded then
    1698              :       ! task balancing will be incomplete
    1699              : 
    1700              :       ! only need to do anything if I've excess tasks
    1701           50 :       IF (load_imbalance(desc%my_pos + 1) > 0) THEN
    1702              : 
    1703           23 :          count = 0 ! weighted amount of tasks offloaded
    1704           23 :          offset = 0 ! no of underloaded processes already filled by other more overloaded procs
    1705              : 
    1706              :          ! calculate offset
    1707           23 :          DO i = desc%group_size, desc%group_size - no_overloaded + 1, -1
    1708           23 :             IF (INDEX(i) == desc%my_pos + 1) THEN
    1709              :                EXIT
    1710              :             ELSE
    1711            0 :                offset = offset + load_imbalance(INDEX(i))
    1712              :             END IF
    1713              :          END DO
    1714              : 
    1715              :          ! find my starting processor to send to
    1716           23 :          proc_receiving = HUGE(proc_receiving)
    1717           23 :          DO i = 1, no_underloaded
    1718           23 :             offset = offset + load_imbalance(INDEX(i))
    1719           23 :             IF (offset <= 0) THEN
    1720              :                proc_receiving = i
    1721              :                EXIT
    1722              :             END IF
    1723              :          END DO
    1724              : 
    1725              :          ! offset now contains minus the number of tasks proc_receiving requires
    1726              :          ! we fill this up by adjusting the destination of tasks on the replicated grid,
    1727              :          ! then move to next most underloaded proc
    1728         1408 :          DO j = 1, ntasks
    1729              :             IF (tasks(j)%dist_type == 0 &
    1730         1408 :                 .AND. decode_rank(tasks(j)%destination, SIZE(rs_descs)) == decode_rank(tasks(j)%source, SIZE(rs_descs))) THEN
    1731              :                ! just avoid sending to non existing procs due to integer truncation
    1732              :                ! in the computation of the average
    1733          819 :                IF (proc_receiving > no_underloaded) EXIT
    1734              :                ! set new destination
    1735          819 :                ilevel = tasks(j)%grid_level
    1736          819 :                img = tasks(j)%image
    1737          819 :                iatom = tasks(j)%iatom
    1738          819 :                jatom = tasks(j)%jatom
    1739          819 :                iset = tasks(j)%iset
    1740          819 :                jset = tasks(j)%jset
    1741          819 :                ipgf = tasks(j)%ipgf
    1742          819 :                jpgf = tasks(j)%jpgf
    1743          819 :                tasks(j)%destination = encode_rank(INDEX(proc_receiving) - 1, ilevel, SIZE(rs_descs))
    1744          819 :                offset = offset + tasks(j)%cost
    1745          819 :                count = count + tasks(j)%cost
    1746          819 :                IF (count >= load_imbalance(desc%my_pos + 1)) EXIT
    1747          796 :                IF (offset > 0) THEN
    1748            0 :                   proc_receiving = proc_receiving + 1
    1749              :                   ! just avoid sending to non existing procs due to integer truncation
    1750              :                   ! in the computation of the average
    1751            0 :                   IF (proc_receiving > no_underloaded) EXIT
    1752            0 :                   offset = load_imbalance(INDEX(proc_receiving))
    1753              :                END IF
    1754              :             END IF
    1755              :          END DO
    1756              :       END IF
    1757              : 
    1758           50 :       DEALLOCATE (index)
    1759           50 :       DEALLOCATE (load_imbalance)
    1760              : 
    1761           50 :       CALL timestop(handle)
    1762              : 
    1763          100 :    END SUBROUTINE load_balance_replicated
    1764              : 
    1765              : ! **************************************************************************************************
    1766              : !> \brief given an input task list, redistribute so that all tasks can be processed locally,
    1767              : !>        i.e. dest equals rank
    1768              : !> \param rs_descs ...
    1769              : !> \param ntasks ...
    1770              : !> \param tasks ...
    1771              : !> \param ntasks_recv ...
    1772              : !> \param tasks_recv ...
    1773              : !> \par History
    1774              : !>      none
    1775              : !> \author MattW 21/11/2007
    1776              : ! **************************************************************************************************
    1777           50 :    SUBROUTINE create_local_tasks(rs_descs, ntasks, tasks, ntasks_recv, tasks_recv)
    1778              : 
    1779              :       TYPE(realspace_grid_desc_p_type), DIMENSION(:), &
    1780              :          POINTER                                         :: rs_descs
    1781              :       INTEGER                                            :: ntasks
    1782              :       TYPE(task_type), DIMENSION(:), POINTER             :: tasks
    1783              :       INTEGER                                            :: ntasks_recv
    1784              :       TYPE(task_type), DIMENSION(:), POINTER             :: tasks_recv
    1785              : 
    1786              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'create_local_tasks'
    1787              : 
    1788              :       INTEGER                                            :: handle, i, j, k, l, rank
    1789              :       INTEGER(KIND=int_8), ALLOCATABLE, DIMENSION(:)     :: recv_buf, send_buf
    1790              :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: recv_disps, recv_sizes, send_disps, &
    1791              :                                                             send_sizes
    1792              :       TYPE(realspace_grid_desc_type), POINTER            :: desc
    1793              : 
    1794           50 :       CALL timeset(routineN, handle)
    1795              : 
    1796           50 :       desc => rs_descs(1)%rs_desc
    1797              : 
    1798              :       ! allocate local arrays
    1799          150 :       ALLOCATE (send_sizes(desc%group_size))
    1800          100 :       ALLOCATE (recv_sizes(desc%group_size))
    1801          100 :       ALLOCATE (send_disps(desc%group_size))
    1802          100 :       ALLOCATE (recv_disps(desc%group_size))
    1803          150 :       ALLOCATE (send_buf(desc%group_size))
    1804          100 :       ALLOCATE (recv_buf(desc%group_size))
    1805              : 
    1806              :       ! fill send buffer, now counting how many tasks will be send (stored in an int8 array for convenience only).
    1807           50 :       send_buf = 0
    1808         3896 :       DO i = 1, ntasks
    1809              :          rank = rs_descs(decode_level(tasks(i)%destination, SIZE(rs_descs))) &
    1810         3846 :                 %rs_desc%virtual2real(decode_rank(tasks(i)%destination, SIZE(rs_descs)))
    1811         3896 :          send_buf(rank + 1) = send_buf(rank + 1) + 1
    1812              :       END DO
    1813              : 
    1814           50 :       CALL desc%group%alltoall(send_buf, recv_buf, 1)
    1815              : 
    1816              :       ! pack the tasks, and send them around
    1817              : 
    1818           50 :       send_sizes = 0
    1819           50 :       send_disps = 0
    1820           50 :       recv_sizes = 0
    1821           50 :       recv_disps = 0
    1822              : 
    1823           50 :       send_sizes(1) = INT(send_buf(1)*task_size_in_int8)
    1824           50 :       recv_sizes(1) = INT(recv_buf(1)*task_size_in_int8)
    1825          100 :       DO i = 2, desc%group_size
    1826           50 :          send_sizes(i) = INT(send_buf(i)*task_size_in_int8)
    1827           50 :          recv_sizes(i) = INT(recv_buf(i)*task_size_in_int8)
    1828           50 :          send_disps(i) = send_disps(i - 1) + send_sizes(i - 1)
    1829          100 :          recv_disps(i) = recv_disps(i - 1) + recv_sizes(i - 1)
    1830              :       END DO
    1831              : 
    1832              :       ! deallocate old send/recv buffers
    1833           50 :       DEALLOCATE (send_buf)
    1834           50 :       DEALLOCATE (recv_buf)
    1835              : 
    1836              :       ! allocate them with new sizes
    1837          243 :       ALLOCATE (send_buf(SUM(send_sizes)))
    1838          248 :       ALLOCATE (recv_buf(SUM(recv_sizes)))
    1839              : 
    1840              :       ! do packing
    1841           50 :       send_buf = 0
    1842           50 :       send_sizes = 0
    1843         3896 :       DO j = 1, ntasks
    1844              :          i = rs_descs(decode_level(tasks(j)%destination, SIZE(rs_descs))) &
    1845         3846 :              %rs_desc%virtual2real(decode_rank(tasks(j)%destination, SIZE(rs_descs))) + 1
    1846         3846 :          l = send_disps(i) + send_sizes(i)
    1847         3846 :          CALL serialize_task(tasks(j), send_buf(l + 1:l + task_size_in_int8))
    1848         3896 :          send_sizes(i) = send_sizes(i) + task_size_in_int8
    1849              :       END DO
    1850              : 
    1851              :       ! do communication
    1852           50 :       CALL desc%group%alltoall(send_buf, send_sizes, send_disps, recv_buf, recv_sizes, recv_disps)
    1853              : 
    1854           50 :       DEALLOCATE (send_buf)
    1855              : 
    1856          150 :       ntasks_recv = SUM(recv_sizes)/task_size_in_int8
    1857         4144 :       ALLOCATE (tasks_recv(ntasks_recv))
    1858              : 
    1859              :       ! do unpacking
    1860           50 :       l = 0
    1861          150 :       DO i = 1, desc%group_size
    1862         3996 :          DO j = 0, recv_sizes(i)/task_size_in_int8 - 1
    1863         3846 :             l = l + 1
    1864         3846 :             k = recv_disps(i) + j*task_size_in_int8
    1865         3946 :             CALL deserialize_task(tasks_recv(l), recv_buf(k + 1:k + task_size_in_int8))
    1866              :          END DO
    1867              :       END DO
    1868              : 
    1869           50 :       DEALLOCATE (recv_buf)
    1870           50 :       DEALLOCATE (send_sizes)
    1871           50 :       DEALLOCATE (recv_sizes)
    1872           50 :       DEALLOCATE (send_disps)
    1873           50 :       DEALLOCATE (recv_disps)
    1874              : 
    1875           50 :       CALL timestop(handle)
    1876              : 
    1877           50 :    END SUBROUTINE create_local_tasks
    1878              : 
    1879              : ! **************************************************************************************************
    1880              : !> \brief Assembles tasks to be performed on local grid
    1881              : !> \param rs_descs the grids
    1882              : !> \param ntasks Number of tasks for local processing
    1883              : !> \param natoms ...
    1884              : !> \param nimages ...
    1885              : !> \param tasks the task set generated on this processor
    1886              : !> \param rval ...
    1887              : !> \param atom_pair_send ...
    1888              : !> \param atom_pair_recv ...
    1889              : !> \param symmetric ...
    1890              : !> \param reorder_rs_grid_ranks ...
    1891              : !> \param skip_load_balance_distributed ...
    1892              : !> \par History
    1893              : !>      none
    1894              : !> \author MattW 21/11/2007
    1895              : ! **************************************************************************************************
    1896        19941 :    SUBROUTINE distribute_tasks(rs_descs, ntasks, natoms, &
    1897              :                                tasks, atom_pair_send, atom_pair_recv, &
    1898              :                                symmetric, reorder_rs_grid_ranks, skip_load_balance_distributed)
    1899              : 
    1900              :       TYPE(realspace_grid_desc_p_type), DIMENSION(:), &
    1901              :          POINTER                                         :: rs_descs
    1902              :       INTEGER                                            :: ntasks, natoms
    1903              :       TYPE(task_type), DIMENSION(:), POINTER             :: tasks
    1904              :       TYPE(atom_pair_type), DIMENSION(:), POINTER        :: atom_pair_send, atom_pair_recv
    1905              :       LOGICAL, INTENT(IN)                                :: symmetric, reorder_rs_grid_ranks, &
    1906              :                                                             skip_load_balance_distributed
    1907              : 
    1908              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'distribute_tasks'
    1909              : 
    1910              :       INTEGER                                            :: handle, igrid_level, irank, ntasks_recv
    1911              :       INTEGER(KIND=int_8)                                :: load_gap, max_load, replicated_load
    1912        19941 :       INTEGER(KIND=int_8), ALLOCATABLE, DIMENSION(:)     :: total_loads, total_loads_tmp, trial_loads
    1913        19941 :       INTEGER(KIND=int_8), DIMENSION(:, :), POINTER      :: loads
    1914        19941 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: indices, real2virtual, total_index
    1915              :       LOGICAL                                            :: distributed_grids, fixed_first_grid
    1916              :       TYPE(realspace_grid_desc_type), POINTER            :: desc
    1917        19941 :       TYPE(task_type), DIMENSION(:), POINTER             :: tasks_recv
    1918              : 
    1919        19941 :       CALL timeset(routineN, handle)
    1920              : 
    1921        19941 :       CPASSERT(ASSOCIATED(tasks))
    1922              : 
    1923              :       ! *** figure out if we have distributed grids
    1924        19941 :       distributed_grids = .FALSE.
    1925        98940 :       DO igrid_level = 1, SIZE(rs_descs)
    1926        98940 :          IF (rs_descs(igrid_level)%rs_desc%distributed) THEN
    1927           56 :             distributed_grids = .TRUE.
    1928              :          END IF
    1929              :       END DO
    1930        19941 :       desc => rs_descs(1)%rs_desc
    1931              : 
    1932        19941 :       IF (distributed_grids) THEN
    1933              : 
    1934          200 :          ALLOCATE (loads(0:desc%group_size - 1, SIZE(rs_descs)))
    1935          150 :          ALLOCATE (total_loads(0:desc%group_size - 1))
    1936              : 
    1937           50 :          total_loads = 0
    1938              : 
    1939              :          ! First round of balancing on the distributed grids
    1940              :          ! we just balance each of the distributed grids independently
    1941          244 :          DO igrid_level = 1, SIZE(rs_descs)
    1942          244 :             IF (rs_descs(igrid_level)%rs_desc%distributed) THEN
    1943              : 
    1944           56 :                IF (.NOT. skip_load_balance_distributed) THEN
    1945           56 :                   CALL load_balance_distributed(tasks, ntasks, rs_descs, igrid_level, natoms)
    1946              :                END IF
    1947              : 
    1948              :                CALL get_current_loads(loads(:, igrid_level), rs_descs, igrid_level, ntasks, &
    1949           56 :                                       tasks, use_reordered_ranks=.FALSE.)
    1950              : 
    1951          168 :                total_loads(:) = total_loads + loads(:, igrid_level)
    1952              : 
    1953              :             END IF
    1954              :          END DO
    1955              : 
    1956              :          ! calculate the total load of replicated tasks, so we can decide if it is worth
    1957              :          ! reordering the distributed grid levels
    1958              : 
    1959           50 :          replicated_load = 0
    1960          244 :          DO igrid_level = 1, SIZE(rs_descs)
    1961          244 :             IF (.NOT. rs_descs(igrid_level)%rs_desc%distributed) THEN
    1962              :                CALL get_current_loads(loads(:, igrid_level), rs_descs, igrid_level, ntasks, &
    1963          138 :                                       tasks, use_reordered_ranks=.FALSE.)
    1964          414 :                replicated_load = replicated_load + SUM(loads(:, igrid_level))
    1965              :             END IF
    1966              :          END DO
    1967              : 
    1968              :          ! Now we adjust the rank ordering based on the current loads
    1969              :          ! we leave the first distributed level and all the replicated levels in the default order
    1970           50 :          IF (reorder_rs_grid_ranks) THEN
    1971           50 :             fixed_first_grid = .FALSE.
    1972          244 :             DO igrid_level = 1, SIZE(rs_descs)
    1973          244 :                IF (rs_descs(igrid_level)%rs_desc%distributed) THEN
    1974           56 :                   IF (fixed_first_grid .EQV. .FALSE.) THEN
    1975          150 :                      total_loads(:) = loads(:, igrid_level)
    1976              :                      fixed_first_grid = .TRUE.
    1977              :                   ELSE
    1978           18 :                      ALLOCATE (trial_loads(0:desc%group_size - 1))
    1979              : 
    1980           18 :                      trial_loads(:) = total_loads + loads(:, igrid_level)
    1981           18 :                      max_load = MAXVAL(trial_loads)
    1982              :                      load_gap = 0
    1983           18 :                      DO irank = 0, desc%group_size - 1
    1984           18 :                         load_gap = load_gap + max_load - trial_loads(irank)
    1985              :                      END DO
    1986              : 
    1987              :                      ! If there is not enough replicated load to load balance well enough
    1988              :                      ! then we will reorder this grid level
    1989            6 :                      IF (load_gap > replicated_load*1.05_dp) THEN
    1990              : 
    1991           18 :                         ALLOCATE (indices(0:desc%group_size - 1))
    1992           12 :                         ALLOCATE (total_index(0:desc%group_size - 1))
    1993           12 :                         ALLOCATE (total_loads_tmp(0:desc%group_size - 1))
    1994           12 :                         ALLOCATE (real2virtual(0:desc%group_size - 1))
    1995              : 
    1996           18 :                         total_loads_tmp(:) = total_loads
    1997            6 :                         CALL sort(total_loads_tmp, desc%group_size, total_index)
    1998            6 :                         CALL sort(loads(:, igrid_level), desc%group_size, indices)
    1999              : 
    2000              :                         ! Reorder so that the rank with smallest load on this grid level is paired with
    2001              :                         ! the highest load in total
    2002           18 :                         DO irank = 0, desc%group_size - 1
    2003              :                            total_loads(total_index(irank) - 1) = total_loads(total_index(irank) - 1) + &
    2004           12 :                                                                  loads(desc%group_size - irank - 1, igrid_level)
    2005           18 :                            real2virtual(total_index(irank) - 1) = indices(desc%group_size - irank - 1) - 1
    2006              :                         END DO
    2007              : 
    2008            6 :                         CALL rs_grid_reorder_ranks(rs_descs(igrid_level)%rs_desc, real2virtual)
    2009              : 
    2010            6 :                         DEALLOCATE (indices)
    2011            6 :                         DEALLOCATE (total_index)
    2012            6 :                         DEALLOCATE (total_loads_tmp)
    2013            6 :                         DEALLOCATE (real2virtual)
    2014              :                      ELSE
    2015            0 :                         total_loads(:) = trial_loads
    2016              :                      END IF
    2017              : 
    2018            6 :                      DEALLOCATE (trial_loads)
    2019              : 
    2020              :                   END IF
    2021              :                END IF
    2022              :             END DO
    2023              :          END IF
    2024              : 
    2025              :          ! Now we use the replicated tasks to balance out the rest of the load
    2026           50 :          CALL load_balance_replicated(rs_descs, ntasks, tasks)
    2027              : 
    2028              :          ! given a list of tasks, this will do the needed reshuffle so that all tasks will be local
    2029           50 :          CALL create_local_tasks(rs_descs, ntasks, tasks, ntasks_recv, tasks_recv)
    2030              : 
    2031              :          !
    2032              :          ! tasks list are complete, we can compute the list of atomic blocks (atom pairs)
    2033              :          ! we will be sending. These lists are needed for redistribute_matrix.
    2034              :          !
    2035           50 :          CALL get_atom_pair(atom_pair_send, tasks, ntasks=ntasks, send=.TRUE., symmetric=symmetric, rs_descs=rs_descs)
    2036              : 
    2037           50 :          CALL get_atom_pair(atom_pair_recv, tasks_recv, ntasks=ntasks_recv, send=.FALSE., symmetric=symmetric, rs_descs=rs_descs)
    2038              : 
    2039              :          ! cleanup, at this point we  don't need the original tasks anymore
    2040           50 :          DEALLOCATE (tasks)
    2041           50 :          DEALLOCATE (loads)
    2042           50 :          DEALLOCATE (total_loads)
    2043              : 
    2044              :       ELSE
    2045        19891 :          tasks_recv => tasks
    2046        19891 :          ntasks_recv = ntasks
    2047        19891 :          CALL get_atom_pair(atom_pair_recv, tasks_recv, ntasks=ntasks_recv, send=.FALSE., symmetric=symmetric, rs_descs=rs_descs)
    2048              :          ! not distributed, hence atom_pair_send not needed
    2049              :       END IF
    2050              : 
    2051              :       ! here we sort the task list we will process locally.
    2052        59433 :       ALLOCATE (indices(ntasks_recv))
    2053        19941 :       CALL tasks_sort(tasks_recv, ntasks_recv, indices)
    2054        19941 :       DEALLOCATE (indices)
    2055              : 
    2056              :       !
    2057              :       ! final lists are ready
    2058              :       !
    2059              : 
    2060        19941 :       tasks => tasks_recv
    2061        19941 :       ntasks = ntasks_recv
    2062              : 
    2063        19941 :       CALL timestop(handle)
    2064              : 
    2065        39882 :    END SUBROUTINE distribute_tasks
    2066              : 
    2067              : ! **************************************************************************************************
    2068              : !> \brief ...
    2069              : !> \param atom_pair ...
    2070              : !> \param my_tasks ...
    2071              : !> \param send ...
    2072              : !> \param symmetric ...
    2073              : !> \param natoms ...
    2074              : !> \param nimages ...
    2075              : !> \param rs_descs ...
    2076              : ! **************************************************************************************************
    2077        19991 :    SUBROUTINE get_atom_pair(atom_pair, tasks, ntasks, send, symmetric, rs_descs)
    2078              : 
    2079              :       TYPE(atom_pair_type), DIMENSION(:), POINTER        :: atom_pair
    2080              :       TYPE(task_type), DIMENSION(:), INTENT(INOUT)       :: tasks
    2081              :       INTEGER, INTENT(IN)                                :: ntasks
    2082              :       LOGICAL, INTENT(IN)                                :: send, symmetric
    2083              :       TYPE(realspace_grid_desc_p_type), DIMENSION(:), INTENT(IN) :: rs_descs
    2084              : 
    2085              :       INTEGER                                            :: i, ilevel, iatom, jatom, npairs, virt_rank
    2086        19991 :       INTEGER, DIMENSION(:), ALLOCATABLE                 :: indices
    2087        19991 :       TYPE(atom_pair_type), DIMENSION(:), ALLOCATABLE    :: atom_pair_tmp
    2088              : 
    2089        19991 :       CPASSERT(.NOT. ASSOCIATED(atom_pair))
    2090        19991 :       IF (ntasks == 0) THEN
    2091          397 :          ALLOCATE (atom_pair(0))
    2092              :          RETURN
    2093              :       END IF
    2094              : 
    2095              :       ! calculate list of atom pairs
    2096              :       ! fill pair list taking into account symmetry
    2097     10886346 :       ALLOCATE (atom_pair_tmp(ntasks))
    2098     10847158 :       DO i = 1, ntasks
    2099     10827564 :          atom_pair_tmp(i)%image = tasks(i)%image
    2100     10827564 :          iatom = tasks(i)%iatom
    2101     10827564 :          jatom = tasks(i)%jatom
    2102     10827564 :          IF (symmetric .AND. iatom > jatom) THEN
    2103              :             ! iatom / jatom swapped
    2104      4144106 :             atom_pair_tmp(i)%row = jatom
    2105      4144106 :             atom_pair_tmp(i)%col = iatom
    2106              :          ELSE
    2107      6683458 :             atom_pair_tmp(i)%row = iatom
    2108      6683458 :             atom_pair_tmp(i)%col = jatom
    2109              :          END IF
    2110              : 
    2111     10847158 :          IF (send) THEN
    2112              :             ! If sending, we need to use the 'real rank' as the pair has to be sent to the process which
    2113              :             ! actually has the correct part of the rs_grid to do the mapping
    2114         3846 :             ilevel = tasks(i)%grid_level
    2115         3846 :             virt_rank = decode_rank(tasks(i)%destination, SIZE(rs_descs))
    2116         3846 :             atom_pair_tmp(i)%rank = rs_descs(ilevel)%rs_desc%virtual2real(virt_rank)
    2117              :          ELSE
    2118              :             ! If we are receiving, then no conversion is needed as the rank is that of the process with the
    2119              :             ! required matrix block, and the ordering of the rs grid is irrelevant
    2120     10823718 :             atom_pair_tmp(i)%rank = decode_rank(tasks(i)%source, SIZE(rs_descs))
    2121              :          END IF
    2122              :       END DO
    2123              : 
    2124              :       ! find unique atom pairs that I'm sending/receiving
    2125        58782 :       ALLOCATE (indices(ntasks))
    2126        19594 :       CALL atom_pair_sort(atom_pair_tmp, ntasks, indices)
    2127        19594 :       npairs = 1
    2128        19594 :       tasks(indices(1))%pair_index = 1
    2129     10827564 :       DO i = 2, ntasks
    2130     10807970 :          IF (atom_pair_less_than(atom_pair_tmp(i - 1), atom_pair_tmp(i))) THEN
    2131       698354 :             npairs = npairs + 1
    2132       698354 :             atom_pair_tmp(npairs) = atom_pair_tmp(i)
    2133              :          END IF
    2134     10827564 :          tasks(indices(i))%pair_index = npairs
    2135              :       END DO
    2136        19594 :       DEALLOCATE (indices)
    2137              : 
    2138              :       ! Copy unique pairs to final location.
    2139       776730 :       ALLOCATE (atom_pair(npairs))
    2140       737542 :       atom_pair(:) = atom_pair_tmp(:npairs)
    2141        19594 :       DEALLOCATE (atom_pair_tmp)
    2142              : 
    2143              :    END SUBROUTINE get_atom_pair
    2144              : 
    2145              : ! **************************************************************************************************
    2146              : !> \brief redistributes the matrix so that it can be used in realspace operations
    2147              : !>        i.e. according to the task lists for collocate and integrate.
    2148              : !>        This routine can become a bottleneck in large calculations.
    2149              : !> \param rs_descs ...
    2150              : !> \param pmats ...
    2151              : !> \param atom_pair_send ...
    2152              : !> \param atom_pair_recv ...
    2153              : !> \param natoms ...
    2154              : !> \param nimages ...
    2155              : !> \param scatter ...
    2156              : !> \param hmats ...
    2157              : ! **************************************************************************************************
    2158            0 :    SUBROUTINE rs_distribute_matrix(rs_descs, pmats, atom_pair_send, atom_pair_recv, &
    2159              :                                    nimages, scatter, hmats)
    2160              : 
    2161              :       TYPE(realspace_grid_desc_p_type), DIMENSION(:), &
    2162              :          POINTER                                         :: rs_descs
    2163              :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: pmats
    2164              :       TYPE(atom_pair_type), DIMENSION(:), POINTER        :: atom_pair_send, atom_pair_recv
    2165              :       INTEGER                                            :: nimages
    2166              :       LOGICAL                                            :: scatter
    2167              :       TYPE(dbcsr_p_type), DIMENSION(:), OPTIONAL, &
    2168              :          POINTER                                         :: hmats
    2169              : 
    2170              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'rs_distribute_matrix'
    2171              : 
    2172              :       INTEGER                                            :: acol, arow, handle, i, img, j, k, l, me, &
    2173              :                                                             nblkcols_total, nblkrows_total, ncol, &
    2174              :                                                             nrow, nthread, nthread_left
    2175            0 :       INTEGER, ALLOCATABLE, DIMENSION(:) :: first_col, first_row, last_col, last_row, recv_disps, &
    2176            0 :                                             recv_pair_count, recv_pair_disps, recv_sizes, send_disps, send_pair_count, &
    2177            0 :                                             send_pair_disps, send_sizes
    2178            0 :       INTEGER, DIMENSION(:), POINTER                     :: col_blk_size, row_blk_size
    2179              :       LOGICAL                                            :: found
    2180            0 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:), TARGET   :: recv_buf_r, send_buf_r
    2181            0 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: h_block, p_block
    2182              :       TYPE(dbcsr_type), POINTER                          :: hmat, pmat
    2183              :       TYPE(realspace_grid_desc_type), POINTER            :: desc
    2184            0 :       REAL(kind=dp), DIMENSION(:), POINTER                            :: vector
    2185              : 
    2186            0 : !$    INTEGER(kind=omp_lock_kind), ALLOCATABLE, DIMENSION(:) :: locks
    2187              : 
    2188            0 :       CALL timeset(routineN, handle)
    2189              : 
    2190            0 :       IF (.NOT. scatter) THEN
    2191            0 :          CPASSERT(PRESENT(hmats))
    2192              :       END IF
    2193              : 
    2194            0 :       desc => rs_descs(1)%rs_desc
    2195            0 :       me = desc%my_pos + 1
    2196              : 
    2197              :       ! allocate local arrays
    2198            0 :       ALLOCATE (send_sizes(desc%group_size))
    2199            0 :       ALLOCATE (recv_sizes(desc%group_size))
    2200            0 :       ALLOCATE (send_disps(desc%group_size))
    2201            0 :       ALLOCATE (recv_disps(desc%group_size))
    2202            0 :       ALLOCATE (send_pair_count(desc%group_size))
    2203            0 :       ALLOCATE (recv_pair_count(desc%group_size))
    2204            0 :       ALLOCATE (send_pair_disps(desc%group_size))
    2205            0 :       ALLOCATE (recv_pair_disps(desc%group_size))
    2206              : 
    2207            0 :       pmat => pmats(1)%matrix
    2208              :       CALL dbcsr_get_info(pmat, &
    2209              :                           row_blk_size=row_blk_size, &
    2210              :                           col_blk_size=col_blk_size, &
    2211              :                           nblkrows_total=nblkrows_total, &
    2212            0 :                           nblkcols_total=nblkcols_total)
    2213            0 :       ALLOCATE (first_row(nblkrows_total), last_row(nblkrows_total), &
    2214            0 :                 first_col(nblkcols_total), last_col(nblkcols_total))
    2215            0 :       CALL dbcsr_convert_sizes_to_offsets(row_blk_size, first_row, last_row)
    2216            0 :       CALL dbcsr_convert_sizes_to_offsets(col_blk_size, first_col, last_col)
    2217              : 
    2218              :       ! set up send buffer sizes
    2219            0 :       send_sizes = 0
    2220            0 :       send_pair_count = 0
    2221            0 :       DO i = 1, SIZE(atom_pair_send)
    2222            0 :          k = atom_pair_send(i)%rank + 1 ! proc we're sending this block to
    2223            0 :          arow = atom_pair_send(i)%row
    2224            0 :          acol = atom_pair_send(i)%col
    2225            0 :          nrow = last_row(arow) - first_row(arow) + 1
    2226            0 :          ncol = last_col(acol) - first_col(acol) + 1
    2227            0 :          send_sizes(k) = send_sizes(k) + nrow*ncol
    2228            0 :          send_pair_count(k) = send_pair_count(k) + 1
    2229              :       END DO
    2230              : 
    2231            0 :       send_disps = 0
    2232            0 :       send_pair_disps = 0
    2233            0 :       DO i = 2, desc%group_size
    2234            0 :          send_disps(i) = send_disps(i - 1) + send_sizes(i - 1)
    2235            0 :          send_pair_disps(i) = send_pair_disps(i - 1) + send_pair_count(i - 1)
    2236              :       END DO
    2237              : 
    2238            0 :       ALLOCATE (send_buf_r(SUM(send_sizes)))
    2239              : 
    2240              :       ! set up recv buffer
    2241              : 
    2242            0 :       recv_sizes = 0
    2243            0 :       recv_pair_count = 0
    2244            0 :       DO i = 1, SIZE(atom_pair_recv)
    2245            0 :          k = atom_pair_recv(i)%rank + 1 ! proc we're receiving this data from
    2246            0 :          arow = atom_pair_recv(i)%row
    2247            0 :          acol = atom_pair_recv(i)%col
    2248            0 :          nrow = last_row(arow) - first_row(arow) + 1
    2249            0 :          ncol = last_col(acol) - first_col(acol) + 1
    2250            0 :          recv_sizes(k) = recv_sizes(k) + nrow*ncol
    2251            0 :          recv_pair_count(k) = recv_pair_count(k) + 1
    2252              :       END DO
    2253              : 
    2254            0 :       recv_disps = 0
    2255            0 :       recv_pair_disps = 0
    2256            0 :       DO i = 2, desc%group_size
    2257            0 :          recv_disps(i) = recv_disps(i - 1) + recv_sizes(i - 1)
    2258            0 :          recv_pair_disps(i) = recv_pair_disps(i - 1) + recv_pair_count(i - 1)
    2259              :       END DO
    2260            0 :       ALLOCATE (recv_buf_r(SUM(recv_sizes)))
    2261              : 
    2262              : !$OMP PARALLEL DEFAULT(NONE) &
    2263              : !$OMP          SHARED(desc,send_pair_count,send_pair_disps,nimages),&
    2264              : !$OMP          SHARED(last_row,first_row,last_col,first_col),&
    2265              : !$OMP          SHARED(pmats,send_buf_r,send_disps,send_sizes),&
    2266              : !$OMP          SHARED(atom_pair_send,me,hmats,nblkrows_total),&
    2267              : !$OMP          SHARED(atom_pair_recv,recv_buf_r,scatter,recv_pair_disps), &
    2268              : !$OMP          SHARED(recv_sizes,recv_disps,recv_pair_count,locks), &
    2269              : !$OMP          PRIVATE(i,img,arow,acol,nrow,ncol,p_block,found,j,k,l),&
    2270            0 : !$OMP          PRIVATE(nthread,h_block,nthread_left,hmat,pmat,vector)
    2271              : 
    2272              :       nthread = 1
    2273              : !$    nthread = omp_get_num_threads()
    2274              :       nthread_left = 1
    2275              : !$    nthread_left = MAX(1, nthread - 1)
    2276              : 
    2277              :       ! do packing
    2278              : !$OMP DO schedule(guided)
    2279              :       DO l = 1, desc%group_size
    2280              :          IF (l == me) CYCLE
    2281              :          send_sizes(l) = 0
    2282              :          DO i = 1, send_pair_count(l)
    2283              :             arow = atom_pair_send(send_pair_disps(l) + i)%row
    2284              :             acol = atom_pair_send(send_pair_disps(l) + i)%col
    2285              :             img = atom_pair_send(send_pair_disps(l) + i)%image
    2286              :             nrow = last_row(arow) - first_row(arow) + 1
    2287              :             ncol = last_col(acol) - first_col(acol) + 1
    2288              :             pmat => pmats(img)%matrix
    2289              :             CALL dbcsr_get_block_p(matrix=pmat, row=arow, col=acol, block=p_block, found=found)
    2290              :             CPASSERT(found)
    2291              : 
    2292              :             DO k = 1, ncol
    2293              :                DO j = 1, nrow
    2294              :                   send_buf_r(send_disps(l) + send_sizes(l) + j + (k - 1)*nrow) = p_block(j, k)
    2295              :                END DO
    2296              :             END DO
    2297              :             send_sizes(l) = send_sizes(l) + nrow*ncol
    2298              :          END DO
    2299              :       END DO
    2300              : !$OMP END DO
    2301              : 
    2302              :       IF (.NOT. scatter) THEN
    2303              :          ! We need locks to protect concurrent summation into H
    2304              : !$OMP SINGLE
    2305              : !$       ALLOCATE (locks(nthread*10))
    2306              : !$OMP END SINGLE
    2307              : 
    2308              : !$OMP DO
    2309              : !$       do i = 1, nthread*10
    2310              : !$          call omp_init_lock(locks(i))
    2311              : !$       end do
    2312              : !$OMP END DO
    2313              :       END IF
    2314              : 
    2315              : !$OMP MASTER
    2316              :       ! do communication
    2317              :       CALL desc%group%alltoall(send_buf_r, send_sizes, send_disps, &
    2318              :                                recv_buf_r, recv_sizes, recv_disps)
    2319              : !$OMP END MASTER
    2320              : 
    2321              :       ! If this is a scatter, then no need to copy local blocks,
    2322              :       ! If not, we sum them directly into H (bypassing the alltoall)
    2323              :       IF (.NOT. scatter) THEN
    2324              : 
    2325              :          ! Distribute work over remaining threads assuming one is still in the alltoall
    2326              : !$OMP DO schedule(dynamic,MAX(1,send_pair_count(me)/nthread_left))
    2327              :          DO i = 1, send_pair_count(me)
    2328              :             arow = atom_pair_send(send_pair_disps(me) + i)%row
    2329              :             acol = atom_pair_send(send_pair_disps(me) + i)%col
    2330              :             img = atom_pair_send(send_pair_disps(me) + i)%image
    2331              :             nrow = last_row(arow) - first_row(arow) + 1
    2332              :             ncol = last_col(acol) - first_col(acol) + 1
    2333              :             hmat => hmats(img)%matrix
    2334              :             pmat => pmats(img)%matrix
    2335              :             CALL dbcsr_get_block_p(matrix=hmat, row=arow, col=acol, BLOCK=h_block, found=found)
    2336              :             CPASSERT(found)
    2337              :             CALL dbcsr_get_block_p(matrix=pmat, row=arow, col=acol, BLOCK=p_block, found=found)
    2338              :             CPASSERT(found)
    2339              : 
    2340              : !$          call omp_set_lock(locks((arow - 1)*nthread*10/nblkrows_total + 1))
    2341              :             DO k = 1, ncol
    2342              :                DO j = 1, nrow
    2343              :                   h_block(j, k) = h_block(j, k) + p_block(j, k)
    2344              :                END DO
    2345              :             END DO
    2346              : !$          call omp_unset_lock(locks((arow - 1)*nthread*10/nblkrows_total + 1))
    2347              :          END DO
    2348              : !$OMP END DO
    2349              :       ELSE
    2350              :          ! We will insert new blocks into P, so create mutable work matrices
    2351              :          DO img = 1, nimages
    2352              :             pmat => pmats(img)%matrix
    2353              :             CALL dbcsr_work_create(pmat, work_mutable=.TRUE., &
    2354              :                                    nblks_guess=SIZE(atom_pair_recv)/nthread, sizedata_guess=SIZE(recv_buf_r)/nthread, &
    2355              :                                    n=nthread)
    2356              :          END DO
    2357              :       END IF
    2358              : 
    2359              : ! wait for comm and setup to finish
    2360              : !$OMP BARRIER
    2361              : 
    2362              :       !do unpacking
    2363              : !$OMP DO schedule(guided)
    2364              :       DO l = 1, desc%group_size
    2365              :          IF (l == me) CYCLE
    2366              :          recv_sizes(l) = 0
    2367              :          DO i = 1, recv_pair_count(l)
    2368              :             arow = atom_pair_recv(recv_pair_disps(l) + i)%row
    2369              :             acol = atom_pair_recv(recv_pair_disps(l) + i)%col
    2370              :             img = atom_pair_recv(recv_pair_disps(l) + i)%image
    2371              :             nrow = last_row(arow) - first_row(arow) + 1
    2372              :             ncol = last_col(acol) - first_col(acol) + 1
    2373              :             pmat => pmats(img)%matrix
    2374              :             NULLIFY (p_block)
    2375              :             CALL dbcsr_get_block_p(matrix=pmat, row=arow, col=acol, BLOCK=p_block, found=found)
    2376              : 
    2377              :             IF (PRESENT(hmats)) THEN
    2378              :                hmat => hmats(img)%matrix
    2379              :                CALL dbcsr_get_block_p(matrix=hmat, row=arow, col=acol, BLOCK=h_block, found=found)
    2380              :                CPASSERT(found)
    2381              :             END IF
    2382              : 
    2383              :             IF (scatter .AND. .NOT. ASSOCIATED(p_block)) THEN
    2384              :                vector => recv_buf_r(recv_disps(l) + recv_sizes(l) + 1:recv_disps(l) + recv_sizes(l) + nrow*ncol)
    2385              :                CALL dbcsr_put_block(pmat, arow, acol, block=RESHAPE(vector, [nrow, ncol]))
    2386              :             END IF
    2387              :             IF (.NOT. scatter) THEN
    2388              : !$             call omp_set_lock(locks((arow - 1)*nthread*10/nblkrows_total + 1))
    2389              :                DO k = 1, ncol
    2390              :                   DO j = 1, nrow
    2391              :                      h_block(j, k) = h_block(j, k) + recv_buf_r(recv_disps(l) + recv_sizes(l) + j + (k - 1)*nrow)
    2392              :                   END DO
    2393              :                END DO
    2394              : !$             call omp_unset_lock(locks((arow - 1)*nthread*10/nblkrows_total + 1))
    2395              :             END IF
    2396              :             recv_sizes(l) = recv_sizes(l) + nrow*ncol
    2397              :          END DO
    2398              :       END DO
    2399              : !$OMP END DO
    2400              : 
    2401              : !$    IF (.not. scatter) THEN
    2402              : !$OMP DO
    2403              : !$       do i = 1, nthread*10
    2404              : !$          call omp_destroy_lock(locks(i))
    2405              : !$       end do
    2406              : !$OMP END DO
    2407              : !$    END IF
    2408              : 
    2409              : !$OMP SINGLE
    2410              : !$    IF (.not. scatter) THEN
    2411              : !$       DEALLOCATE (locks)
    2412              : !$    END IF
    2413              : !$OMP END SINGLE NOWAIT
    2414              : 
    2415              :       IF (scatter) THEN
    2416              :          ! Blocks were added to P
    2417              :          DO img = 1, nimages
    2418              :             pmat => pmats(img)%matrix
    2419              :             CALL dbcsr_finalize(pmat)
    2420              :          END DO
    2421              :       END IF
    2422              : !$OMP END PARALLEL
    2423              : 
    2424            0 :       DEALLOCATE (send_buf_r)
    2425            0 :       DEALLOCATE (recv_buf_r)
    2426              : 
    2427            0 :       DEALLOCATE (send_sizes)
    2428            0 :       DEALLOCATE (recv_sizes)
    2429            0 :       DEALLOCATE (send_disps)
    2430            0 :       DEALLOCATE (recv_disps)
    2431            0 :       DEALLOCATE (send_pair_count)
    2432            0 :       DEALLOCATE (recv_pair_count)
    2433            0 :       DEALLOCATE (send_pair_disps)
    2434            0 :       DEALLOCATE (recv_pair_disps)
    2435              : 
    2436            0 :       DEALLOCATE (first_row, last_row, first_col, last_col)
    2437              : 
    2438            0 :       CALL timestop(handle)
    2439              : 
    2440            0 :    END SUBROUTINE rs_distribute_matrix
    2441              : 
    2442              : ! **************************************************************************************************
    2443              : !> \brief Calculates offsets and sizes for rs_scatter_matrix and rs_copy_matrix.
    2444              : !> \author Ole Schuett
    2445              : ! **************************************************************************************************
    2446        17705 :    SUBROUTINE rs_calc_offsets(pairs, nsgf, group_size, &
    2447              :                               pair_offsets, rank_offsets, rank_sizes, buffer_size)
    2448              :       TYPE(atom_pair_type), DIMENSION(:), INTENT(IN)     :: pairs
    2449              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: nsgf
    2450              :       INTEGER, INTENT(IN)                                :: group_size
    2451              :       INTEGER, DIMENSION(:), POINTER                     :: pair_offsets, rank_offsets, rank_sizes
    2452              :       INTEGER, INTENT(INOUT)                             :: buffer_size
    2453              : 
    2454              :       INTEGER                                            :: acol, arow, i, block_size, total_size, k, prev_k
    2455              : 
    2456        17705 :       IF (ASSOCIATED(pair_offsets)) DEALLOCATE (pair_offsets)
    2457        17705 :       IF (ASSOCIATED(rank_offsets)) DEALLOCATE (rank_offsets)
    2458        17705 :       IF (ASSOCIATED(rank_sizes)) DEALLOCATE (rank_sizes)
    2459              : 
    2460              :       ! calculate buffer_size and pair_offsets
    2461        52769 :       ALLOCATE (pair_offsets(SIZE(pairs)))
    2462        17705 :       total_size = 0
    2463       724631 :       DO i = 1, SIZE(pairs)
    2464       706926 :          pair_offsets(i) = total_size
    2465       706926 :          arow = pairs(i)%row
    2466       706926 :          acol = pairs(i)%col
    2467       706926 :          block_size = nsgf(arow)*nsgf(acol)
    2468       724631 :          total_size = total_size + block_size
    2469              :       END DO
    2470        17705 :       buffer_size = total_size
    2471              : 
    2472              :       ! calculate rank_offsets and rank_sizes
    2473        53115 :       ALLOCATE (rank_offsets(group_size))
    2474        35410 :       ALLOCATE (rank_sizes(group_size))
    2475        51889 :       rank_offsets = 0
    2476        51889 :       rank_sizes = 0
    2477        17705 :       IF (SIZE(pairs) > 0) THEN
    2478        17359 :          prev_k = pairs(1)%rank + 1
    2479       724285 :          DO i = 1, SIZE(pairs)
    2480       706926 :             k = pairs(i)%rank + 1
    2481       706926 :             CPASSERT(k >= prev_k) ! expecting the pairs to be ordered by rank
    2482       724285 :             IF (k > prev_k) THEN
    2483           65 :                rank_offsets(k) = pair_offsets(i)
    2484           65 :                rank_sizes(prev_k) = rank_offsets(k) - rank_offsets(prev_k)
    2485           65 :                prev_k = k
    2486              :             END IF
    2487              :          END DO
    2488        17359 :          rank_sizes(k) = buffer_size - rank_offsets(k) ! complete last rank
    2489              :       END IF
    2490              : 
    2491        17705 :    END SUBROUTINE rs_calc_offsets
    2492              : 
    2493              : ! **************************************************************************************************
    2494              : !> \brief Group contributions to each matrix block without changing the communication order.
    2495              : !>        Rebuilt with the task list; contains indices only, independent of matrices and threads.
    2496              : !> \param pairs atom pairs in communication order
    2497              : !> \param block_starts first position of each block in pair_indices, plus an end sentinel
    2498              : !> \param pair_indices original pair indices grouped by destination block
    2499              : ! **************************************************************************************************
    2500        17655 :    SUBROUTINE rs_group_atom_pairs(pairs, block_starts, pair_indices)
    2501              :       TYPE(atom_pair_type), DIMENSION(:), INTENT(IN)     :: pairs
    2502              :       INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT)   :: block_starts, pair_indices
    2503              : 
    2504              :       INTEGER                                            :: i, nblocks, npairs
    2505        17655 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: starts
    2506        17655 :       TYPE(atom_pair_type), ALLOCATABLE, DIMENSION(:)    :: sorted_pairs
    2507              : 
    2508        17655 :       npairs = SIZE(pairs)
    2509       829656 :       ALLOCATE (sorted_pairs(npairs), pair_indices(npairs), starts(npairs + 1))
    2510       724414 :       sorted_pairs(:) = pairs(:)
    2511              :       ! Ignore source rank for grouping. The stable sort preserves contribution order within each block.
    2512       724414 :       sorted_pairs(:)%rank = 0
    2513        17655 :       CALL atom_pair_sort(sorted_pairs, npairs, pair_indices)
    2514              : 
    2515        17655 :       nblocks = 0
    2516       724414 :       DO i = 1, npairs
    2517       706759 :          IF (i > 1) THEN
    2518       689448 :             IF (.NOT. atom_pair_less_than(sorted_pairs(i - 1), sorted_pairs(i))) CYCLE
    2519              :          END IF
    2520       706686 :          nblocks = nblocks + 1
    2521       724414 :          starts(nblocks) = i
    2522              :       END DO
    2523        17655 :       starts(nblocks + 1) = npairs + 1
    2524        70620 :       ALLOCATE (block_starts(nblocks + 1))
    2525       741996 :       block_starts(:) = starts(:nblocks + 1)
    2526              : 
    2527        17655 :    END SUBROUTINE rs_group_atom_pairs
    2528              : 
    2529              : ! **************************************************************************************************
    2530              : !> \brief Scatters dbcsr matrix blocks and receives them into a buffer as needed before collocation.
    2531              : !> \author Ole Schuett
    2532              : ! **************************************************************************************************
    2533          298 :    SUBROUTINE rs_scatter_matrices(src_matrices, dest_buffer, task_list, group)
    2534              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(IN)       :: src_matrices
    2535              :       TYPE(offload_buffer_type), INTENT(INOUT)           :: dest_buffer
    2536              :       TYPE(task_list_type), INTENT(IN)                   :: task_list
    2537              :       TYPE(mp_comm_type), INTENT(IN)                     :: group
    2538              : 
    2539              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'rs_scatter_matrices'
    2540              : 
    2541              :       INTEGER                                            :: handle
    2542              :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE           :: buffer_send
    2543              : 
    2544          298 :       CALL timeset(routineN, handle)
    2545          873 :       ALLOCATE (buffer_send(task_list%buffer_size_send))
    2546              : 
    2547              :       ! pack dbcsr blocks into send buffer
    2548          298 :       CPASSERT(ASSOCIATED(task_list%atom_pair_send))
    2549              :       CALL rs_pack_buffer(src_matrices=src_matrices, &
    2550              :                           dest_buffer=buffer_send, &
    2551              :                           atom_pair=task_list%atom_pair_send, &
    2552          298 :                           pair_offsets=task_list%pair_offsets_send)
    2553              : 
    2554              :       ! mpi all-to-all communication, receiving directly into blocks_recv%buffer.
    2555              :       CALL group%alltoall(buffer_send, task_list%rank_sizes_send, task_list%rank_offsets_send, &
    2556              :                           dest_buffer%host_buffer, &
    2557         2682 :                           task_list%rank_sizes_recv, task_list%rank_offsets_recv)
    2558              : 
    2559          298 :       DEALLOCATE (buffer_send)
    2560          298 :       CALL timestop(handle)
    2561              : 
    2562          298 :    END SUBROUTINE rs_scatter_matrices
    2563              : 
    2564              : ! **************************************************************************************************
    2565              : !> \brief Gather the dbcsr matrix blocks and receives them into a buffer as needed after integration.
    2566              : !> \author Ole Schuett
    2567              : ! **************************************************************************************************
    2568          248 :    SUBROUTINE rs_gather_matrices(src_buffer, dest_matrices, task_list, group)
    2569              :       TYPE(offload_buffer_type), INTENT(IN)              :: src_buffer
    2570              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(INOUT)    :: dest_matrices
    2571              :       TYPE(task_list_type), INTENT(IN)                   :: task_list
    2572              :       TYPE(mp_comm_type), INTENT(IN)                     :: group
    2573              : 
    2574              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'rs_gather_matrices'
    2575              : 
    2576              :       INTEGER                                            :: handle
    2577              :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE           :: buffer_send
    2578              : 
    2579          248 :       CALL timeset(routineN, handle)
    2580              : 
    2581              :       ! Caution: The meaning of send and recv are reversed in this routine.
    2582          730 :       ALLOCATE (buffer_send(task_list%buffer_size_send)) ! e.g. this is actually used for receiving
    2583              : 
    2584              :       ! mpi all-to-all communication
    2585              :       CALL group%alltoall(src_buffer%host_buffer, task_list%rank_sizes_recv, task_list%rank_offsets_recv, &
    2586         2232 :                           buffer_send, task_list%rank_sizes_send, task_list%rank_offsets_send)
    2587              : 
    2588              :       ! unpack dbcsr blocks from send buffer
    2589          248 :       CPASSERT(ASSOCIATED(task_list%atom_pair_send))
    2590              :       CALL rs_unpack_buffer(src_buffer=buffer_send, &
    2591              :                             dest_matrices=dest_matrices, &
    2592              :                             atom_pair=task_list%atom_pair_send, &
    2593              :                             pair_offsets=task_list%pair_offsets_send, &
    2594              :                             block_starts=task_list%unpack_block_starts, &
    2595          248 :                             pair_indices=task_list%unpack_pair_indices)
    2596              : 
    2597          248 :       DEALLOCATE (buffer_send)
    2598          248 :       CALL timestop(handle)
    2599              : 
    2600          248 :    END SUBROUTINE rs_gather_matrices
    2601              : 
    2602              : ! **************************************************************************************************
    2603              : !> \brief Copies the DBCSR blocks into buffer, replaces rs_scatter_matrix for non-distributed grids.
    2604              : !> \author Ole Schuett
    2605              : ! **************************************************************************************************
    2606       289785 :    SUBROUTINE rs_copy_to_buffer(src_matrices, dest_buffer, task_list)
    2607              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(IN)       :: src_matrices
    2608              :       TYPE(offload_buffer_type), INTENT(INOUT)           :: dest_buffer
    2609              :       TYPE(task_list_type), INTENT(IN)                   :: task_list
    2610              : 
    2611              :       CALL rs_pack_buffer(src_matrices=src_matrices, &
    2612              :                           dest_buffer=dest_buffer%host_buffer, &
    2613              :                           atom_pair=task_list%atom_pair_recv, &
    2614       289785 :                           pair_offsets=task_list%pair_offsets_recv)
    2615              : 
    2616       289785 :    END SUBROUTINE rs_copy_to_buffer
    2617              : 
    2618              : ! **************************************************************************************************
    2619              : !> \brief Copies from buffer into DBCSR matrics, replaces rs_gather_matrix for non-distributed grids.
    2620              : !> \author Ole Schuett
    2621              : ! **************************************************************************************************
    2622       242594 :    SUBROUTINE rs_copy_to_matrices(src_buffer, dest_matrices, task_list)
    2623              :       TYPE(offload_buffer_type), INTENT(IN)              :: src_buffer
    2624              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(INOUT)    :: dest_matrices
    2625              :       TYPE(task_list_type), INTENT(IN)                   :: task_list
    2626              : 
    2627              :       CALL rs_unpack_buffer(src_buffer=src_buffer%host_buffer, &
    2628              :                             dest_matrices=dest_matrices, &
    2629              :                             atom_pair=task_list%atom_pair_recv, &
    2630              :                             pair_offsets=task_list%pair_offsets_recv, &
    2631              :                             block_starts=task_list%unpack_block_starts, &
    2632       242594 :                             pair_indices=task_list%unpack_pair_indices)
    2633              : 
    2634       242594 :    END SUBROUTINE rs_copy_to_matrices
    2635              : 
    2636              : ! **************************************************************************************************
    2637              : !> \brief Helper routine for rs_scatter_matrix and rs_copy_to_buffer.
    2638              : !> \author Ole Schuett
    2639              : ! **************************************************************************************************
    2640       290083 :    SUBROUTINE rs_pack_buffer(src_matrices, dest_buffer, atom_pair, pair_offsets)
    2641              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(IN)       :: src_matrices
    2642              :       REAL(KIND=dp), DIMENSION(:), INTENT(INOUT)         :: dest_buffer
    2643              :       TYPE(atom_pair_type), DIMENSION(:), INTENT(IN)     :: atom_pair
    2644              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: pair_offsets
    2645              : 
    2646              :       INTEGER                                            :: acol, arow, img, i, offset, block_size
    2647              :       LOGICAL                                            :: found
    2648       290083 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: block
    2649              : 
    2650              : !$OMP PARALLEL DEFAULT(NONE), &
    2651              : !$OMP          SHARED(src_matrices,atom_pair,pair_offsets,dest_buffer), &
    2652       290083 : !$OMP          PRIVATE(acol,arow,img,i,offset,block_size,found,block)
    2653              : !$OMP DO schedule(guided)
    2654              :       DO i = 1, SIZE(atom_pair)
    2655              :          arow = atom_pair(i)%row
    2656              :          acol = atom_pair(i)%col
    2657              :          img = atom_pair(i)%image
    2658              :          CALL dbcsr_get_block_p(matrix=src_matrices(img)%matrix, row=arow, col=acol, &
    2659              :                                 block=block, found=found)
    2660              :          CPASSERT(found)
    2661              :          block_size = SIZE(block)
    2662              :          offset = pair_offsets(i)
    2663              :          dest_buffer(offset + 1:offset + block_size) = RESHAPE(block, shape=[block_size])
    2664              :       END DO
    2665              : !$OMP END DO
    2666              : !$OMP END PARALLEL
    2667              : 
    2668       290083 :    END SUBROUTINE rs_pack_buffer
    2669              : 
    2670              : ! **************************************************************************************************
    2671              : !> \brief Helper routine for rs_gather_matrix and rs_copy_to_matrices.
    2672              : !> \author Ole Schuett
    2673              : ! **************************************************************************************************
    2674       242842 :    SUBROUTINE rs_unpack_buffer(src_buffer, dest_matrices, atom_pair, pair_offsets, block_starts, pair_indices)
    2675              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: src_buffer
    2676              :       TYPE(dbcsr_p_type), DIMENSION(:), INTENT(INOUT)    :: dest_matrices
    2677              :       TYPE(atom_pair_type), DIMENSION(:), INTENT(IN)     :: atom_pair
    2678              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: pair_offsets
    2679              :       INTEGER, DIMENSION(:), INTENT(IN)                  :: block_starts, pair_indices
    2680              : 
    2681              :       INTEGER                                            :: acol, arow, i, iblock, img, ipair, &
    2682              :                                                             j, ncols, nrows, offset, pair
    2683              :       LOGICAL                                            :: found
    2684       242842 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: block
    2685              : 
    2686              :       ! One thread owns each destination block and accumulates all ranks' contributions.
    2687              : !$OMP PARALLEL DEFAULT(NONE), &
    2688              : !$OMP          SHARED(src_buffer,atom_pair,pair_offsets,dest_matrices,block_starts,pair_indices), &
    2689       242842 : !$OMP          PRIVATE(acol,arow,i,iblock,img,ipair,j,ncols,nrows,offset,pair,found,block)
    2690              : !$OMP DO schedule(guided)
    2691              :       DO iblock = 1, SIZE(block_starts) - 1
    2692              :          pair = pair_indices(block_starts(iblock))
    2693              :          arow = atom_pair(pair)%row
    2694              :          acol = atom_pair(pair)%col
    2695              :          img = atom_pair(pair)%image
    2696              :          CALL dbcsr_get_block_p(matrix=dest_matrices(img)%matrix, row=arow, col=acol, &
    2697              :                                 block=block, found=found)
    2698              :          CPASSERT(found)
    2699              :          nrows = SIZE(block, 1)
    2700              :          ncols = SIZE(block, 2)
    2701              :          DO ipair = block_starts(iblock), block_starts(iblock + 1) - 1
    2702              :             offset = pair_offsets(pair_indices(ipair))
    2703              :             DO j = 1, ncols
    2704              :                DO i = 1, nrows
    2705              :                   block(i, j) = block(i, j) + src_buffer(offset + i + (j - 1)*nrows)
    2706              :                END DO
    2707              :             END DO
    2708              :          END DO
    2709              :       END DO
    2710              : !$OMP END DO
    2711              : !$OMP END PARALLEL
    2712              : 
    2713       242842 :    END SUBROUTINE rs_unpack_buffer
    2714              : 
    2715              : ! **************************************************************************************************
    2716              : !> \brief determines the rank of the processor who's real rs grid contains point
    2717              : !> \param rs_desc ...
    2718              : !> \param igrid_level ...
    2719              : !> \param n_levels ...
    2720              : !> \param cube_center ...
    2721              : !> \param ntasks ...
    2722              : !> \param tasks ...
    2723              : !> \param lb_cube ...
    2724              : !> \param ub_cube ...
    2725              : !> \param added_tasks ...
    2726              : !> \par History
    2727              : !>      11.2007 created [MattW]
    2728              : !>      10.2008 rewritten [Joost VandeVondele]
    2729              : !> \author MattW
    2730              : ! **************************************************************************************************
    2731         1476 :    SUBROUTINE rs_find_node(rs_desc, igrid_level, n_levels, cube_center, ntasks, tasks, &
    2732              :                            lb_cube, ub_cube, added_tasks)
    2733              : 
    2734              :       TYPE(realspace_grid_desc_type), POINTER            :: rs_desc
    2735              :       INTEGER, INTENT(IN)                                :: igrid_level, n_levels
    2736              :       INTEGER, DIMENSION(3), INTENT(IN)                  :: cube_center
    2737              :       INTEGER, INTENT(INOUT)                             :: ntasks
    2738              :       TYPE(task_type), DIMENSION(:), POINTER             :: tasks
    2739              :       INTEGER, DIMENSION(3), INTENT(IN)                  :: lb_cube, ub_cube
    2740              :       INTEGER, INTENT(OUT)                               :: added_tasks
    2741              : 
    2742              :       INTEGER, PARAMETER                                 :: add_tasks = 1000
    2743              :       REAL(kind=dp), PARAMETER                           :: mult_tasks = 2.0_dp
    2744              : 
    2745              :       INTEGER :: bit_index, coord(3), curr_tasks, dest, i, icoord(3), idest, itask, ix, iy, iz, &
    2746              :                  lb_coord(3), lb_domain(3), lbc(3), ub_coord(3), ub_domain(3), ubc(3)
    2747              :       INTEGER                                            :: bit_pattern
    2748              :       LOGICAL                                            :: dir_periodic(3)
    2749              : 
    2750         1476 :       coord(1) = rs_desc%x2coord(cube_center(1))
    2751         1476 :       coord(2) = rs_desc%y2coord(cube_center(2))
    2752         1476 :       coord(3) = rs_desc%z2coord(cube_center(3))
    2753         1476 :       dest = rs_desc%coord2rank(coord(1), coord(2), coord(3))
    2754              : 
    2755              :       ! the real cube coordinates
    2756         5904 :       lbc = lb_cube + cube_center
    2757         5904 :       ubc = ub_cube + cube_center
    2758              : 
    2759        10332 :       IF (ALL((rs_desc%lb_global(:, dest) - rs_desc%border) <= lbc) .AND. &
    2760              :           ALL((rs_desc%ub_global(:, dest) + rs_desc%border) >= ubc)) THEN
    2761              :          !standard distributed collocation/integration
    2762         1476 :          tasks(ntasks)%destination = encode_rank(dest, igrid_level, n_levels)
    2763         1476 :          tasks(ntasks)%dist_type = 1
    2764         1476 :          tasks(ntasks)%subpatch_pattern = 0
    2765         1476 :          added_tasks = 1
    2766              : 
    2767              :          ! here we figure out if there is an alternate location for this task
    2768              :          ! i.e. even though the cube_center is not in the real local domain,
    2769              :          ! it might fully fit in the halo of the neighbor
    2770              :          ! if its radius is smaller than the maximum radius
    2771              :          ! the list of possible neighbors is stored here as a bit pattern
    2772              :          ! to reconstruct what the rank of the neigbor is,
    2773              :          ! we can use (note this requires the correct rs_grid)
    2774              :          !  IF (BTEST(bit_pattern,0)) rank=rs_grid_locate_rank(rs_desc,tasks(ntasks)%destination,[-1,0,0])
    2775              :          !  IF (BTEST(bit_pattern,1)) rank=rs_grid_locate_rank(rs_desc,tasks(ntasks)%destination,[+1,0,0])
    2776              :          !  IF (BTEST(bit_pattern,2)) rank=rs_grid_locate_rank(rs_desc,tasks(ntasks)%destination,[0,-1,0])
    2777              :          !  IF (BTEST(bit_pattern,3)) rank=rs_grid_locate_rank(rs_desc,tasks(ntasks)%destination,[0,+1,0])
    2778              :          !  IF (BTEST(bit_pattern,4)) rank=rs_grid_locate_rank(rs_desc,tasks(ntasks)%destination,[0,0,-1])
    2779              :          !  IF (BTEST(bit_pattern,5)) rank=rs_grid_locate_rank(rs_desc,tasks(ntasks)%destination,[0,0,+1])
    2780         1476 :          bit_index = 0
    2781         1476 :          bit_pattern = 0
    2782         5904 :          DO i = 1, 3
    2783         5904 :             IF (rs_desc%perd(i) == 1) THEN
    2784         2952 :                bit_pattern = IBCLR(bit_pattern, bit_index)
    2785         2952 :                bit_index = bit_index + 1
    2786         2952 :                bit_pattern = IBCLR(bit_pattern, bit_index)
    2787         2952 :                bit_index = bit_index + 1
    2788              :             ELSE
    2789              :                ! fits the left neighbor ?
    2790         1476 :                IF (ubc(i) <= rs_desc%lb_global(i, dest) - 1 + rs_desc%border) THEN
    2791          695 :                   bit_pattern = IBSET(bit_pattern, bit_index)
    2792          695 :                   bit_index = bit_index + 1
    2793              :                ELSE
    2794          781 :                   bit_pattern = IBCLR(bit_pattern, bit_index)
    2795          781 :                   bit_index = bit_index + 1
    2796              :                END IF
    2797              :                ! fits the right neighbor ?
    2798         1476 :                IF (lbc(i) >= rs_desc%ub_global(i, dest) + 1 - rs_desc%border) THEN
    2799          250 :                   bit_pattern = IBSET(bit_pattern, bit_index)
    2800          250 :                   bit_index = bit_index + 1
    2801              :                ELSE
    2802         1226 :                   bit_pattern = IBCLR(bit_pattern, bit_index)
    2803         1226 :                   bit_index = bit_index + 1
    2804              :                END IF
    2805              :             END IF
    2806              :          END DO
    2807         1476 :          tasks(ntasks)%subpatch_pattern = bit_pattern
    2808              : 
    2809              :       ELSE
    2810              :          ! generalised collocation/integration needed
    2811              :          ! first we figure out how many neighbors we have to add to include the lbc/ubc
    2812              :          ! in the available domains (inclusive of halo points)
    2813              :          ! first we 'ignore' periodic boundary conditions
    2814              :          ! i.e. ub_coord-lb_coord+1 might be larger than group_dim
    2815            0 :          lb_coord = coord
    2816            0 :          ub_coord = coord
    2817            0 :          lb_domain = rs_desc%lb_global(:, dest) - rs_desc%border
    2818            0 :          ub_domain = rs_desc%ub_global(:, dest) + rs_desc%border
    2819            0 :          DO i = 1, 3
    2820              :             ! only if the grid is not periodic in this direction we need to take care of adding neighbors
    2821            0 :             IF (rs_desc%perd(i) == 0) THEN
    2822              :                ! if the domain lower bound is greater than the lbc we need to add the size of the neighbor domain
    2823            0 :                DO
    2824            0 :                   IF (lb_domain(i) > lbc(i)) THEN
    2825            0 :                      lb_coord(i) = lb_coord(i) - 1
    2826            0 :                      icoord = MODULO(lb_coord, rs_desc%group_dim)
    2827            0 :                      idest = rs_desc%coord2rank(icoord(1), icoord(2), icoord(3))
    2828            0 :                      lb_domain(i) = lb_domain(i) - (rs_desc%ub_global(i, idest) - rs_desc%lb_global(i, idest) + 1)
    2829              :                   ELSE
    2830              :                      EXIT
    2831              :                   END IF
    2832              :                END DO
    2833              :                ! same for the upper bound
    2834            0 :                DO
    2835            0 :                   IF (ub_domain(i) < ubc(i)) THEN
    2836            0 :                      ub_coord(i) = ub_coord(i) + 1
    2837            0 :                      icoord = MODULO(ub_coord, rs_desc%group_dim)
    2838            0 :                      idest = rs_desc%coord2rank(icoord(1), icoord(2), icoord(3))
    2839            0 :                      ub_domain(i) = ub_domain(i) + (rs_desc%ub_global(i, idest) - rs_desc%lb_global(i, idest) + 1)
    2840              :                   ELSE
    2841              :                      EXIT
    2842              :                   END IF
    2843              :                END DO
    2844              :             END IF
    2845              :          END DO
    2846              : 
    2847              :          ! some care is needed for the periodic boundaries
    2848            0 :          DO i = 1, 3
    2849            0 :             IF (ub_domain(i) - lb_domain(i) + 1 >= rs_desc%npts(i)) THEN
    2850            0 :                dir_periodic(i) = .TRUE.
    2851            0 :                lb_coord(i) = 0
    2852            0 :                ub_coord(i) = rs_desc%group_dim(i) - 1
    2853              :             ELSE
    2854            0 :                dir_periodic(i) = .FALSE.
    2855              :             END IF
    2856              :          END DO
    2857              : 
    2858            0 :          added_tasks = PRODUCT(ub_coord - lb_coord + 1)
    2859            0 :          itask = ntasks
    2860            0 :          ntasks = ntasks + added_tasks - 1
    2861            0 :          IF (ntasks > SIZE(tasks)) THEN
    2862            0 :             curr_tasks = INT((SIZE(tasks) + add_tasks)*mult_tasks)
    2863            0 :             CALL reallocate_tasks(tasks, curr_tasks)
    2864              :          END IF
    2865            0 :          DO iz = lb_coord(3), ub_coord(3)
    2866            0 :          DO iy = lb_coord(2), ub_coord(2)
    2867            0 :          DO ix = lb_coord(1), ub_coord(1)
    2868            0 :             icoord = MODULO([ix, iy, iz], rs_desc%group_dim)
    2869            0 :             idest = rs_desc%coord2rank(icoord(1), icoord(2), icoord(3))
    2870            0 :             tasks(itask)%destination = encode_rank(idest, igrid_level, n_levels)
    2871            0 :             tasks(itask)%dist_type = 2
    2872            0 :             tasks(itask)%subpatch_pattern = 0
    2873              :             ! encode the domain size for this task
    2874              :             ! if the bit is set, we need to add the border in that direction
    2875            0 :             IF (ix == lb_coord(1) .AND. .NOT. dir_periodic(1)) THEN
    2876            0 :                tasks(itask)%subpatch_pattern = IBSET(tasks(itask)%subpatch_pattern, 0)
    2877              :             END IF
    2878            0 :             IF (ix == ub_coord(1) .AND. .NOT. dir_periodic(1)) THEN
    2879            0 :                tasks(itask)%subpatch_pattern = IBSET(tasks(itask)%subpatch_pattern, 1)
    2880              :             END IF
    2881            0 :             IF (iy == lb_coord(2) .AND. .NOT. dir_periodic(2)) THEN
    2882            0 :                tasks(itask)%subpatch_pattern = IBSET(tasks(itask)%subpatch_pattern, 2)
    2883              :             END IF
    2884            0 :             IF (iy == ub_coord(2) .AND. .NOT. dir_periodic(2)) THEN
    2885            0 :                tasks(itask)%subpatch_pattern = IBSET(tasks(itask)%subpatch_pattern, 3)
    2886              :             END IF
    2887            0 :             IF (iz == lb_coord(3) .AND. .NOT. dir_periodic(3)) THEN
    2888            0 :                tasks(itask)%subpatch_pattern = IBSET(tasks(itask)%subpatch_pattern, 4)
    2889              :             END IF
    2890            0 :             IF (iz == ub_coord(3) .AND. .NOT. dir_periodic(3)) THEN
    2891            0 :                tasks(itask)%subpatch_pattern = IBSET(tasks(itask)%subpatch_pattern, 5)
    2892              :             END IF
    2893            0 :             itask = itask + 1
    2894              :          END DO
    2895              :          END DO
    2896              :          END DO
    2897              :       END IF
    2898              : 
    2899         1476 :    END SUBROUTINE rs_find_node
    2900              : 
    2901              : ! **************************************************************************************************
    2902              : !> \brief utility functions for encoding the grid level with a rank, allowing
    2903              : !>        different grid levels to maintain a different rank ordering without
    2904              : !>        losing information.  These encoded_ints are stored in the tasks(1:2,:) array
    2905              : !> \param rank ...
    2906              : !> \param grid_level ...
    2907              : !> \param n_levels ...
    2908              : !> \return ...
    2909              : !> \par History
    2910              : !>      4.2009 created [Iain Bethune]
    2911              : !>        (c) The Numerical Algorithms Group (NAG) Ltd, 2009 on behalf of the HECToR project
    2912              : ! **************************************************************************************************
    2913     21649731 :    FUNCTION encode_rank(rank, grid_level, n_levels) RESULT(encoded_int)
    2914              : 
    2915              :       INTEGER, INTENT(IN)                                :: rank, grid_level, n_levels
    2916              :       INTEGER                                            :: encoded_int
    2917              : 
    2918              : ! ordered so can still sort by rank
    2919              : 
    2920     21649731 :       encoded_int = rank*n_levels + grid_level - 1
    2921              : 
    2922     21649731 :    END FUNCTION encode_rank
    2923              : 
    2924              : ! **************************************************************************************************
    2925              : !> \brief ...
    2926              : !> \param encoded_int ...
    2927              : !> \param n_levels ...
    2928              : !> \return ...
    2929              : ! **************************************************************************************************
    2930     10873716 :    FUNCTION decode_rank(encoded_int, n_levels) RESULT(rank)
    2931              : 
    2932              :       INTEGER, INTENT(IN)                                :: encoded_int
    2933              :       INTEGER, INTENT(IN)                                :: n_levels
    2934              :       INTEGER                                            :: rank
    2935              : 
    2936     10873716 :       rank = INT(encoded_int/n_levels)
    2937              : 
    2938     10873716 :    END FUNCTION decode_rank
    2939              : 
    2940              : ! **************************************************************************************************
    2941              : !> \brief ...
    2942              : !> \param encoded_int ...
    2943              : !> \param n_levels ...
    2944              : !> \return ...
    2945              : ! **************************************************************************************************
    2946         7692 :    FUNCTION decode_level(encoded_int, n_levels) RESULT(grid_level)
    2947              : 
    2948              :       INTEGER, INTENT(IN)                                :: encoded_int
    2949              :       INTEGER, INTENT(IN)                                :: n_levels
    2950              :       INTEGER                                            :: grid_level
    2951              : 
    2952         7692 :       grid_level = INT(MODULO(encoded_int, n_levels)) + 1
    2953              : 
    2954         7692 :    END FUNCTION decode_level
    2955              : 
    2956              : ! **************************************************************************************************
    2957              : !> \brief Sort pgf index pair (ipgf,iset,iatom),(jpgf,jset,jatom) for which all atom pairs are
    2958              : !>        grouped, and for each atom pair all set pairs are grouped and for each set pair,
    2959              : !>        all pgfs are grouped.
    2960              : !>        This yields the right order of the tasks for collocation after the sort
    2961              : !>        in distribute_tasks. E.g. for a atom pair, all sets and pgfs are computed in one go.
    2962              : !>        The exception is the gridlevel. Tasks are first ordered wrt to grid_level. This implies
    2963              : !>        that a given density matrix block will be decontracted several times,
    2964              : !>        but cache effects on the grid make up for this.
    2965              : !> \param a ...
    2966              : !> \param b ...
    2967              : !> \return ...
    2968              : !> \author Ole Schuett
    2969              : ! **************************************************************************************************
    2970    104134820 :    PURE FUNCTION tasks_less_than(a, b) RESULT(res)
    2971              :       TYPE(task_type), INTENT(IN)                        :: a, b
    2972              :       LOGICAL                                            :: res
    2973              : 
    2974    104134820 :       IF (a%grid_level /= b%grid_level) THEN
    2975     25212610 :          res = a%grid_level < b%grid_level
    2976              : 
    2977     78922210 :       ELSE IF (a%image /= b%image) THEN
    2978     17755254 :          res = a%image < b%image
    2979              : 
    2980     61166956 :       ELSE IF (a%iatom /= b%iatom) THEN
    2981      8440495 :          res = a%iatom < b%iatom
    2982              : 
    2983     52726461 :       ELSE IF (a%jatom /= b%jatom) THEN
    2984      9274828 :          res = a%jatom < b%jatom
    2985              : 
    2986     43451633 :       ELSE IF (a%iset /= b%iset) THEN
    2987      6509869 :          res = a%iset < b%iset
    2988              : 
    2989     36941764 :       ELSE IF (a%jset /= b%jset) THEN
    2990      6854303 :          res = a%jset < b%jset
    2991              : 
    2992     30087461 :       ELSE IF (a%ipgf /= b%ipgf) THEN
    2993      8495656 :          res = a%ipgf < b%ipgf
    2994              : 
    2995              :       ELSE
    2996     21591805 :          res = a%jpgf < b%jpgf
    2997              : 
    2998              :       END IF
    2999    104134820 :    END FUNCTION tasks_less_than
    3000              : 
    3001    248287722 :    #:call array_sort(prefix='tasks', type='TYPE(task_type)')
    3002              :    #:endcall
    3003              : 
    3004              : ! **************************************************************************************************
    3005              : !> \brief Order atom pairs to find duplicates.
    3006              : !> \param a ...
    3007              : !> \param b ...
    3008              : !> \return ...
    3009              : !> \author Ole Schuett
    3010              : ! **************************************************************************************************
    3011     54922940 :    PURE FUNCTION atom_pair_less_than(a, b) RESULT(res)
    3012              :       TYPE(atom_pair_type), INTENT(IN)                   :: a, b
    3013              :       LOGICAL                                            :: res
    3014              : 
    3015     54922940 :       IF (a%rank /= b%rank) THEN
    3016         4524 :          res = a%rank < b%rank
    3017              : 
    3018     54918416 :       ELSE IF (a%row /= b%row) THEN
    3019      7675520 :          res = a%row < b%row
    3020              : 
    3021     47242896 :       ELSE IF (a%col /= b%col) THEN
    3022      7617174 :          res = a%col < b%col
    3023              : 
    3024              :       ELSE
    3025     39625722 :          res = a%image < b%image
    3026              : 
    3027              :       END IF
    3028     54922940 :    END FUNCTION atom_pair_less_than
    3029              : 
    3030    117832197 :    #:call array_sort(prefix='atom_pair', type='TYPE(atom_pair_type)')
    3031              :    #:endcall
    3032              : 
    3033              : END MODULE task_list_methods
        

Generated by: LCOV version 2.0-1