LCOV - code coverage report
Current view: top level - src/fm - cp_fm_diag_utils.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:24d69ee) Lines: 72.9 % 240 175
Test Date: 2026-09-03 07:32:15 Functions: 75.0 % 12 9

            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 Auxiliary tools to redistribute cp_fm_type and cp_cfm_type matrices before and after
      10              : !>        diagonalization. Heuristics are used to determine the optimal number of CPUs for
      11              : !>        diagonalization and the input matrices are redistributed if necessary
      12              : !> \par History
      13              : !>      - [01.2018] moved redistribution related code from cp_fm_syevd here
      14              : !>      - [08.2026] added complex matrix (cp_cfm_type) counterparts
      15              : !> \author Nico Holmberg [01.2018]
      16              : ! **************************************************************************************************
      17              : MODULE cp_fm_diag_utils
      18              :    USE cp_blacs_env,                    ONLY: cp_blacs_env_create,&
      19              :                                               cp_blacs_env_release,&
      20              :                                               cp_blacs_env_type
      21              :    USE cp_cfm_types,                    ONLY: cp_cfm_create,&
      22              :                                               cp_cfm_get_info,&
      23              :                                               cp_cfm_release,&
      24              :                                               cp_cfm_type
      25              :    USE cp_fm_struct,                    ONLY: cp_fm_struct_create,&
      26              :                                               cp_fm_struct_get,&
      27              :                                               cp_fm_struct_release,&
      28              :                                               cp_fm_struct_type
      29              :    USE cp_fm_types,                     ONLY: cp_fm_create,&
      30              :                                               cp_fm_get_info,&
      31              :                                               cp_fm_release,&
      32              :                                               cp_fm_type
      33              :    USE cp_log_handling,                 ONLY: cp_get_default_logger,&
      34              :                                               cp_logger_get_default_io_unit,&
      35              :                                               cp_logger_type
      36              :    USE kinds,                           ONLY: dp
      37              :    USE mathlib,                         ONLY: gcd
      38              :    USE message_passing,                 ONLY: mp_para_env_type
      39              : #include "../base/base_uses.f90"
      40              : 
      41              :    IMPLICIT NONE
      42              : 
      43              :    PRIVATE
      44              : 
      45              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_fm_diag_utils'
      46              : 
      47              :    ! Information on redistribution
      48              :    TYPE, PUBLIC :: cp_fm_redistribute_info
      49              :       INTEGER :: matrix_order = -1
      50              :       INTEGER :: num_pe_old = -1 ! number of processes before a potential redistribute
      51              :       INTEGER :: num_pe_new = -1 ! number of processes after a potential redistribute
      52              :       INTEGER :: num_pe_opt = -1 ! optimal number of processes for the given matrix
      53              :       INTEGER :: num_pe_max_nz_col = -1 ! the maximal number of processes s.t. no column has zero width, may be < 0 if ignored
      54              :       LOGICAL :: redistribute = .FALSE. ! whether or not the matrix was actually redistributed
      55              :    CONTAINS
      56              :       PROCEDURE, PASS(self) :: write => cp_fm_redistribute_info_write
      57              :    END TYPE cp_fm_redistribute_info
      58              : 
      59              :    ! Container for redistribution settings and temporary work structs
      60              :    TYPE cp_fm_redistribute_type
      61              :       ! Settings
      62              :       INTEGER                                  :: a = -1, x = -1
      63              :       LOGICAL                                  :: should_print = .FALSE.
      64              :       LOGICAL                                  :: elpa_force_redistribute = .FALSE.
      65              :       ! Temporaries
      66              :       INTEGER, DIMENSION(:), POINTER           :: group_distribution => NULL(), &
      67              :                                                   group_partition => NULL()
      68              :       TYPE(cp_blacs_env_type), POINTER         :: blacs_env_new => NULL()
      69              :       TYPE(mp_para_env_type), POINTER          :: para_env_new => NULL()
      70              :    END TYPE cp_fm_redistribute_type
      71              : 
      72              :    ! Permanent instance of the redistribute type
      73              :    TYPE(cp_fm_redistribute_type), PRIVATE, &
      74              :       SAVE                                     :: work_redistribute
      75              : 
      76              :    ! Public subroutines
      77              : 
      78              :    PUBLIC :: cp_fm_redistribute_start, &
      79              :              cp_fm_redistribute_end, &
      80              :              cp_fm_redistribute_init, &
      81              :              cp_cfm_redistribute_start, &
      82              :              cp_cfm_redistribute_end
      83              : 
      84              : CONTAINS
      85              : 
      86              : ! **************************************************************************************************
      87              : !> \brief Write the redistribute info nicely formatted to the given I/O unit
      88              : !> \param self reference to the cp_fm_redistribute_info instance
      89              : !> \param io_unit I/O unit to use for writing
      90              : ! **************************************************************************************************
      91            0 :    SUBROUTINE cp_fm_redistribute_info_write(self, io_unit)
      92              :       CLASS(cp_fm_redistribute_info), INTENT(IN) :: self
      93              :       INTEGER, INTENT(IN) :: io_unit
      94              : 
      95            0 :       WRITE (UNIT=io_unit, FMT="(A)") ""
      96              :       WRITE (UNIT=io_unit, FMT="(T2,A,T71,I10)") &
      97            0 :          "CP_FM_DIAG| Number of processes over which the matrix is distributed ", self%num_pe_old, &
      98            0 :          "CP_FM_DIAG| Matrix order ", self%matrix_order
      99              :       WRITE (UNIT=io_unit, FMT="(T2,A,T71,I10)") &
     100            0 :          "CP_FM_DIAG| Optimal number of CPUs ", self%num_pe_opt
     101            0 :       IF (self%num_pe_max_nz_col < 0) THEN
     102              :          WRITE (UNIT=io_unit, FMT="(T2,A,T71,A10)") &
     103            0 :             "CP_FM_DIAG| Maximum number of CPUs (with non-zero columns) ", "<N/A>"
     104              :       ELSE
     105              :          WRITE (UNIT=io_unit, FMT="(T2,A,T71,I10)") &
     106            0 :             "CP_FM_DIAG| Maximum number of CPUs (with non-zero columns): ", self%num_pe_max_nz_col
     107              :       END IF
     108            0 :       IF (self%redistribute) THEN
     109              :          WRITE (UNIT=io_unit, FMT="(T2,A,T71,I10)") &
     110            0 :             "CP_FM_DIAG| Number of processes for the redistribution ", self%num_pe_new
     111              :       ELSE
     112              :          WRITE (UNIT=io_unit, FMT="(T2,A)") &
     113            0 :             "CP_FM_DIAG| The matrix will NOT be redistributed"
     114              :       END IF
     115            0 :       WRITE (UNIT=io_unit, FMT="(A)") ""
     116              : 
     117            0 :    END SUBROUTINE cp_fm_redistribute_info_write
     118              : 
     119              : ! **************************************************************************************************
     120              : !> \brief  Releases the temporary storage needed when redistributing arrays
     121              : !> \param  has_redistributed flag that determines if the processors holds a part of the
     122              : !>                           redistributed array
     123              : !> \author Nico Holmberg [01.2018]
     124              : ! **************************************************************************************************
     125       275518 :    SUBROUTINE cp_fm_redistribute_work_finalize(has_redistributed)
     126              :       LOGICAL, INTENT(IN)                                :: has_redistributed
     127              : 
     128       275518 :       IF (ASSOCIATED(work_redistribute%group_distribution)) THEN
     129       275518 :          IF (has_redistributed) THEN
     130       139645 :             CALL cp_blacs_env_release(work_redistribute%blacs_env_new)
     131              :          END IF
     132       275518 :          CALL work_redistribute%para_env_new%free()
     133       275518 :          DEALLOCATE (work_redistribute%para_env_new)
     134       275518 :          DEALLOCATE (work_redistribute%group_distribution)
     135       275518 :          DEALLOCATE (work_redistribute%group_partition)
     136              :       END IF
     137              :       ! Return work to its initial state
     138       275518 :       work_redistribute = cp_fm_redistribute_type()
     139              : 
     140       275518 :    END SUBROUTINE cp_fm_redistribute_work_finalize
     141              : 
     142              : ! **************************************************************************************************
     143              : !> \brief  Initializes the parameters that determine how to calculate the optimal number of CPUs
     144              : !>         for diagonalizing a matrix. The parameters are read from the GLOBAL input section.
     145              : !> \param a                integer parameter used to define the rule for determining the optimal
     146              : !>                         number of CPUs for diagonalization
     147              : !> \param x                integer parameter used to define the rule for determining the optimal
     148              : !>                         number of CPUs for diagonalization
     149              : !> \param should_print     flag that determines if information about the redistribution process
     150              : !>                         should be printed
     151              : !> \param elpa_force_redistribute  flag that if redistribution should always be performed when
     152              : !>                                 the ELPA diagonalization library is in use
     153              : !> \author Nico Holmberg [01.2018]
     154              : ! **************************************************************************************************
     155        11451 :    SUBROUTINE cp_fm_redistribute_init(a, x, should_print, elpa_force_redistribute)
     156              :       INTEGER, INTENT(IN)                                :: a, x
     157              :       LOGICAL, INTENT(IN)                                :: should_print, elpa_force_redistribute
     158              : 
     159              :       work_redistribute%a = a
     160              :       work_redistribute%x = x
     161              :       work_redistribute%should_print = should_print
     162              :       work_redistribute%elpa_force_redistribute = elpa_force_redistribute
     163              :       ! Init work
     164        11451 :       work_redistribute = cp_fm_redistribute_type()
     165              : 
     166        11451 :    END SUBROUTINE cp_fm_redistribute_init
     167              : 
     168              : ! **************************************************************************************************
     169              : !> \brief  Calculates the optimal number of CPUs for diagonalizing a matrix.
     170              : !> \param  size  the size of the diagonalized matrix
     171              : !> \return the optimal number of CPUs
     172              : !> \author Nico Holmberg [01.2018]
     173              : ! **************************************************************************************************
     174       279668 :    PURE FUNCTION cp_fm_diag_get_optimal_ncpu(size) RESULT(ncpu)
     175              :       INTEGER, INTENT(IN)                                :: size
     176              :       INTEGER                                            :: ncpu
     177              : 
     178              :       ncpu = ((size + work_redistribute%a*work_redistribute%x - 1)/ &
     179       279668 :               (work_redistribute%a*work_redistribute%x))*work_redistribute%a
     180              : 
     181       279668 :    END FUNCTION cp_fm_diag_get_optimal_ncpu
     182              : 
     183              : #if defined(__parallel)
     184              : ! **************************************************************************************************
     185              : !> \brief  Determines the largest number of CPUs a matrix can be distributed on without any of the
     186              : !>         processors getting a zero-width column (currently only needed for ELPA).
     187              : !> \param  matrix the matrix that will be diagonalized
     188              : !> \return the maximum number of CPUs for ELPA
     189              : !> \author Nico Holmberg [01.2018]
     190              : ! **************************************************************************************************
     191        14422 :    FUNCTION cp_fm_max_ncpu_non_zero_column(matrix) RESULT(ncpu)
     192              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix
     193              :       INTEGER                                            :: ncpu
     194              : 
     195              :       INTEGER                                            :: gcd_max, ipe, jpe, ncol_block, &
     196              :                                                             ncol_global, npcol, nrow_block, &
     197              :                                                             nrow_global, num_pe_old, nzero
     198        14422 :       INTEGER, DIMENSION(:), POINTER                     :: ncol_locals
     199              :       INTEGER, EXTERNAL                                  :: numroc
     200              : 
     201        14422 :       NULLIFY (ncol_locals)
     202              :       ! First check if there are any zero width columns in current layout
     203              :       CALL cp_fm_get_info(matrix, ncol_locals=ncol_locals, &
     204              :                           nrow_global=nrow_global, ncol_global=ncol_global, &
     205        14422 :                           nrow_block=nrow_block, ncol_block=ncol_block)
     206        28850 :       nzero = COUNT(ncol_locals == 0)
     207        14422 :       num_pe_old = matrix%matrix_struct%para_env%num_pe
     208        14422 :       ncpu = num_pe_old - nzero
     209              : 
     210              :       ! Avoid layouts with odd number of CPUs (blacs grid layout will be square)
     211        14422 :       IF (ncpu > 2) THEN
     212            0 :          ncpu = ncpu - MODULO(ncpu, 2)
     213              :       END IF
     214              : 
     215              :       ! if there are no zero-width columns and the number of processors was even, leave it at that
     216        14422 :       IF (ncpu == num_pe_old) THEN
     217              :          RETURN
     218              :       END IF
     219              : 
     220              :       ! Iteratively search for the maximum number of CPUs for ELPA
     221              :       ! On each step, we test whether the blacs grid created with ncpu processes
     222              :       ! contains any columns with zero width
     223            0 :       DO WHILE (ncpu > 1)
     224              :          ! Determine layout of new blacs grid with ncpu CPUs
     225              :          ! (snippet copied from cp_blacs_env.F:cp_blacs_env_create)
     226            0 :          gcd_max = -1
     227            0 :          DO ipe = 1, CEILING(SQRT(REAL(ncpu, dp)))
     228            0 :             jpe = ncpu/ipe
     229            0 :             IF (ipe*jpe /= ncpu) THEN
     230              :                CYCLE
     231              :             END IF
     232            0 :             IF (gcd(ipe, jpe) >= gcd_max) THEN
     233            0 :                npcol = jpe
     234            0 :                gcd_max = gcd(ipe, jpe)
     235              :             END IF
     236              :          END DO
     237              : 
     238              :          ! Count the number of processors without any columns
     239              :          ! (snippet copied from cp_fm_struct.F:cp_fm_struct_create)
     240            0 :          nzero = 0
     241            0 :          DO ipe = 0, npcol - 1
     242            0 :             IF (numroc(ncol_global, ncol_block, ipe, 0, npcol) == 0) THEN
     243            0 :                nzero = nzero + 1
     244              :             END IF
     245              :          END DO
     246              : 
     247            0 :          IF (nzero == 0) THEN
     248              :             EXIT
     249              :          END IF
     250              : 
     251            0 :          ncpu = ncpu - nzero
     252              : 
     253            0 :          IF (ncpu > 2) THEN
     254            0 :             ncpu = ncpu - MODULO(ncpu, 2)
     255              :          END IF
     256              :       END DO
     257              : 
     258        14422 :    END FUNCTION cp_fm_max_ncpu_non_zero_column
     259              : #endif
     260              : 
     261              : ! **************************************************************************************************
     262              : !> \brief   Determines the optimal number of CPUs for matrix diagonalization and redistributes
     263              : !>          the input matrices if necessary
     264              : !> \param matrix           the input cp_fm_type matrix to be diagonalized
     265              : !> \param eigenvectors     the cp_fm_type matrix that will hold the eigenvectors of the input matrix
     266              : !> \param matrix_new       the redistributed input matrix which will subsequently be diagonalized,
     267              : !>                         or a pointer to the original matrix if no redistribution is required
     268              : !> \param eigenvectors_new the redistributed eigenvectors matrix, or a pointer to the original
     269              : !>                         matrix if no redistribution is required
     270              : !> \param caller_is_elpa   flag that determines if ELPA is used for diagonalization
     271              : !> \param redist_info      get info about the redistribution
     272              : !> \par History
     273              : !>      - [01.2018] created by moving redistribution related code from cp_fm_syevd here
     274              : !> \author Nico Holmberg [01.2018]
     275              : ! **************************************************************************************************
     276        14422 :    SUBROUTINE cp_fm_redistribute_start(matrix, eigenvectors, matrix_new, eigenvectors_new, &
     277              :                                        caller_is_elpa, redist_info)
     278              : 
     279              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix, eigenvectors
     280              :       TYPE(cp_fm_type), INTENT(OUT)            :: matrix_new, eigenvectors_new
     281              :       LOGICAL, OPTIONAL, INTENT(IN)            :: caller_is_elpa
     282              : 
     283              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_redistribute_start'
     284              : 
     285              :       INTEGER                                  :: handle
     286              :       LOGICAL                                  :: is_elpa
     287              :       TYPE(cp_fm_redistribute_info), OPTIONAL, INTENT(OUT) :: redist_info
     288              : 
     289              : #if defined(__parallel)
     290              :       REAL(KIND=dp)                            :: fake_local_data(1, 1)
     291              :       INTEGER                                  :: fake_descriptor(9), mepos_old, &
     292              :                                                   io_unit, ngroups, ncol_block, blksize, nrow_block
     293              :       TYPE(cp_fm_struct_type), POINTER         :: fm_struct_new
     294              :       TYPE(mp_para_env_type), POINTER          :: para_env
     295              :       TYPE(cp_logger_type), POINTER            :: logger
     296              :       TYPE(cp_fm_redistribute_info)          :: rdinfo
     297              : #endif
     298              : 
     299       279596 :       CALL timeset(routineN, handle)
     300       279596 :       is_elpa = .FALSE.
     301       279596 :       IF (PRESENT(caller_is_elpa)) THEN
     302              : #if defined(__ELPA)
     303        14422 :          is_elpa = caller_is_elpa
     304              : #else
     305              :          CPABORT("CP2K compiled without the ELPA library.")
     306              : #endif
     307              :       END IF
     308              : 
     309              : #if defined(__parallel)
     310              : 
     311       279596 :       logger => cp_get_default_logger()
     312       279596 :       io_unit = cp_logger_get_default_io_unit(logger)
     313              : 
     314              :       ! first figure out the optimal number of cpus
     315              :       ! this is pure heuristics, the defaults are based on rosa timings
     316              :       ! that demonstrate that timings go up sharply if too many tasks are used
     317              :       ! we take a multiple of 4, and approximately n/60
     318       279596 :       para_env => matrix%matrix_struct%para_env
     319       279596 :       mepos_old = para_env%mepos
     320       279596 :       ncol_block = -1 ! normally we also want to adjust the block size according to the optimal # of CPUs
     321       279596 :       nrow_block = -1
     322       279596 :       blksize = -1
     323              : 
     324       279596 :       rdinfo%matrix_order = matrix%matrix_struct%nrow_global
     325       279596 :       rdinfo%num_pe_old = para_env%num_pe
     326       279596 :       rdinfo%num_pe_opt = cp_fm_diag_get_optimal_ncpu(rdinfo%matrix_order)
     327       279596 :       rdinfo%num_pe_new = rdinfo%num_pe_opt
     328              :       rdinfo%num_pe_max_nz_col = -1
     329              :       rdinfo%redistribute = .FALSE.
     330              : 
     331       279596 :       IF (is_elpa) THEN
     332              :          ! with ELPA we don't have to redistribute if not necessary (scales, unlike ScaLAPACK)
     333        14422 :          rdinfo%num_pe_new = rdinfo%num_pe_old
     334              : 
     335              :          ! BUT: Diagonalization with ELPA fails when a processor column has zero width
     336              :          ! Determine the maximum number of CPUs the matrix can be distributed without zero-width columns
     337              :          ! for the current block size.
     338        14422 :          rdinfo%num_pe_max_nz_col = cp_fm_max_ncpu_non_zero_column(matrix)
     339              : 
     340              :          ! if the user wants to redistribute to the ScaLAPACK optimal number of CPUs anyway, let him if it's safe.
     341        14422 :          IF (work_redistribute%elpa_force_redistribute .AND. rdinfo%num_pe_opt < rdinfo%num_pe_max_nz_col) THEN
     342              :             ! Use heuristics to determine the need for redistribution (when num_pe_opt is smaller than the safe maximum)
     343              :             ! in this case we can also take the block size used for ScaLAPACK
     344            0 :             rdinfo%num_pe_new = rdinfo%num_pe_opt
     345        14422 :          ELSE IF (rdinfo%num_pe_old > rdinfo%num_pe_max_nz_col) THEN
     346              :             ! Otherwise, only redistribute if we have to
     347            0 :             rdinfo%num_pe_new = rdinfo%num_pe_max_nz_col
     348              :             ! do NOT let cp_fm_struct_create automatically adjust the block size because the
     349              :             ! calculated number of processors such that no block has 0 columns wouldn't match (see #578):
     350              :             ! if the automatically chosen block size is larger than the present one we would still end
     351              :             ! up with empty processors
     352              :          END IF
     353              : 
     354        14422 :          CALL cp_fm_get_info(matrix, ncol_block=ncol_block, nrow_block=nrow_block)
     355              : 
     356              :          ! On GPUs, ELPA requires the block size to be a power of 2
     357        14422 :          blksize = 1
     358        88704 :          DO WHILE (2*blksize <= MIN(nrow_block, ncol_block))
     359        14422 :             blksize = blksize*2
     360              :          END DO
     361        14422 :          nrow_block = blksize
     362        14422 :          ncol_block = blksize
     363              :       END IF
     364              : 
     365              :       ! finally, only redistribute if we're going to use less CPUs than before or changed the block size
     366              :       rdinfo%redistribute = (rdinfo%num_pe_old > rdinfo%num_pe_new) .OR. (blksize >= 0 .AND. &
     367       279596 :                                    ((blksize /= matrix%matrix_struct%ncol_block) .OR. (blksize /= matrix%matrix_struct%nrow_block)))
     368              : 
     369       279596 :       IF (work_redistribute%should_print .AND. io_unit > 0) THEN
     370            0 :          IF (is_elpa) THEN
     371            0 :             IF (work_redistribute%elpa_force_redistribute) THEN
     372              :                WRITE (UNIT=io_unit, FMT="(T2,A,T78,A3)") &
     373            0 :                   "CP_FM_DIAG| Force redistribute (ELPA):", "YES"
     374              :             ELSE
     375              :                WRITE (UNIT=io_unit, FMT="(T2,A,T79,A2)") &
     376            0 :                   "CP_FM_DIAG| Force redistribute (ELPA):", "NO"
     377              :             END IF
     378              :          END IF
     379            0 :          CALL rdinfo%write(io_unit)
     380              :       END IF
     381       279596 :       CALL para_env%sync()
     382              : 
     383              :       ! if the optimal is smaller than num_pe, we will redistribute the input matrix
     384       279596 :       IF (rdinfo%redistribute) THEN
     385              :          ! split comm, the first num_pe_new tasks will do the work
     386       826338 :          ALLOCATE (work_redistribute%group_distribution(0:rdinfo%num_pe_old - 1))
     387       275446 :          ALLOCATE (work_redistribute%group_partition(0:1))
     388       826338 :          work_redistribute%group_partition = [rdinfo%num_pe_new, rdinfo%num_pe_old - rdinfo%num_pe_new]
     389       275446 :          ALLOCATE (work_redistribute%para_env_new)
     390              :          CALL work_redistribute%para_env_new%from_split( &
     391              :             comm=para_env, ngroups=ngroups, group_distribution=work_redistribute%group_distribution, &
     392       275446 :             n_subgroups=2, group_partition=work_redistribute%group_partition)
     393              : 
     394       275446 :          IF (work_redistribute%group_distribution(mepos_old) == 0) THEN
     395              : 
     396              :             ! create blacs, should inherit the preferences for the layout and so on, from the higher level
     397       139609 :             NULLIFY (work_redistribute%blacs_env_new)
     398       139609 :             CALL cp_blacs_env_create(blacs_env=work_redistribute%blacs_env_new, para_env=work_redistribute%para_env_new)
     399              : 
     400              :             ! create new matrix
     401       139609 :             NULLIFY (fm_struct_new)
     402       139609 :             IF (nrow_block == -1 .OR. ncol_block == -1) THEN
     403              :                CALL cp_fm_struct_create(fmstruct=fm_struct_new, &
     404              :                                         para_env=work_redistribute%para_env_new, &
     405              :                                         context=work_redistribute%blacs_env_new, &
     406              :                                         nrow_global=rdinfo%matrix_order, ncol_global=rdinfo%matrix_order, &
     407       134473 :                                         ncol_block=ncol_block, nrow_block=nrow_block)
     408              :             ELSE
     409              :                CALL cp_fm_struct_create(fmstruct=fm_struct_new, &
     410              :                                         para_env=work_redistribute%para_env_new, &
     411              :                                         context=work_redistribute%blacs_env_new, &
     412              :                                         nrow_global=rdinfo%matrix_order, ncol_global=rdinfo%matrix_order, &
     413         5136 :                                         ncol_block=ncol_block, nrow_block=nrow_block, force_block=.TRUE.)
     414              :             END IF
     415       139609 :             CALL cp_fm_create(matrix_new, matrix_struct=fm_struct_new, name="yevd_new_mat")
     416       139609 :             CALL cp_fm_create(eigenvectors_new, matrix_struct=fm_struct_new, name="yevd_new_vec")
     417       139609 :             CALL cp_fm_struct_release(fm_struct_new)
     418              : 
     419              :             ! redistribute old
     420              :             CALL pdgemr2d(rdinfo%matrix_order, rdinfo%matrix_order, matrix%local_data(1, 1), 1, 1, &
     421              :                           matrix%matrix_struct%descriptor, &
     422              :                           matrix_new%local_data(1, 1), 1, 1, matrix_new%matrix_struct%descriptor, &
     423       139609 :                           matrix%matrix_struct%context)
     424              :          ELSE
     425              :             ! these tasks must help redistribute (they own part of the data),
     426              :             ! but need fake 'new' data, and their descriptor must indicate this with -1
     427              :             ! see also scalapack comments on pdgemr2d
     428      1358370 :             fake_descriptor = -1
     429              :             CALL pdgemr2d(rdinfo%matrix_order, rdinfo%matrix_order, matrix%local_data(1, 1), 1, 1, &
     430              :                           matrix%matrix_struct%descriptor, &
     431              :                           fake_local_data(1, 1), 1, 1, fake_descriptor, &
     432       135837 :                           matrix%matrix_struct%context)
     433              :          END IF
     434              :       ELSE
     435              :          ! No need to redistribute, just return pointers to the original arrays
     436         4150 :          matrix_new = matrix
     437         4150 :          eigenvectors_new = eigenvectors
     438              :       END IF
     439              : 
     440       279596 :       IF (PRESENT(redist_info)) THEN
     441        14422 :          redist_info = rdinfo
     442              :       END IF
     443              : #else
     444              : 
     445              :       MARK_USED(matrix)
     446              :       MARK_USED(eigenvectors)
     447              :       MARK_USED(matrix_new)
     448              :       MARK_USED(eigenvectors_new)
     449              :       MARK_USED(redist_info)
     450              :       CPABORT("Routine called in non-parallel case.")
     451              : #endif
     452              : 
     453       279596 :       CALL timestop(handle)
     454              : 
     455       279596 :    END SUBROUTINE cp_fm_redistribute_start
     456              : 
     457              : ! **************************************************************************************************
     458              : !> \brief Redistributes eigenvectors and eigenvalues  back to the original communicator group
     459              : !> \param matrix           the input cp_fm_type matrix to be diagonalized
     460              : !> \param eigenvectors     the cp_fm_type matrix that will hold the eigenvectors of the input matrix
     461              : !> \param eig              global array holding the eigenvalues of the input matrixmatrix
     462              : !> \param matrix_new       the redistributed input matrix which will subsequently be diagonalized,
     463              : !>                         or a pointer to the original matrix if no redistribution is required
     464              : !> \param eigenvectors_new the redistributed eigenvectors matrix, or a pointer to the original
     465              : !>                         matrix if no redistribution is required
     466              : !> \par History
     467              : !>      - [01.2018] created by moving redistribution related code from cp_fm_syevd here
     468              : !> \author Nico Holmberg [01.2018]
     469              : ! **************************************************************************************************
     470       279596 :    SUBROUTINE cp_fm_redistribute_end(matrix, eigenvectors, eig, matrix_new, eigenvectors_new)
     471              : 
     472              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix, eigenvectors
     473              :       REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: eig
     474              :       TYPE(cp_fm_type), INTENT(INOUT)          :: matrix_new, eigenvectors_new
     475              : 
     476              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_redistribute_end'
     477              : 
     478              :       INTEGER                                  :: handle
     479              : #if defined(__parallel)
     480              :       REAL(KIND=dp)                            :: fake_local_data(1, 1)
     481              :       INTEGER                                  :: fake_descriptor(9), mepos_old, n
     482              :       TYPE(mp_para_env_type), POINTER          :: para_env
     483              : #endif
     484              : 
     485       279596 :       CALL timeset(routineN, handle)
     486              : 
     487              : #if defined(__parallel)
     488              : 
     489              :       ! Check if matrix was redistributed
     490       279596 :       IF (ASSOCIATED(work_redistribute%group_distribution)) THEN
     491       275446 :          n = matrix%matrix_struct%nrow_global
     492       275446 :          para_env => matrix%matrix_struct%para_env
     493       275446 :          mepos_old = para_env%mepos
     494              : 
     495       275446 :          IF (work_redistribute%group_distribution(mepos_old) == 0) THEN
     496              :             ! redistribute results on CPUs that hold the redistributed matrix
     497              :             CALL pdgemr2d(n, n, eigenvectors_new%local_data(1, 1), 1, 1, eigenvectors_new%matrix_struct%descriptor, &
     498              :                           eigenvectors%local_data(1, 1), 1, 1, eigenvectors%matrix_struct%descriptor, &
     499       139609 :                           eigenvectors%matrix_struct%context)
     500       139609 :             CALL cp_fm_release(matrix_new)
     501       139609 :             CALL cp_fm_release(eigenvectors_new)
     502              :          ELSE
     503              :             ! these tasks must help redistribute (they own part of the data),
     504              :             ! but need fake 'new' data, and their descriptor must indicate this with -1
     505              :             ! see also scalapack comments on pdgemr2d
     506      1358370 :             fake_descriptor = -1
     507              :             CALL pdgemr2d(n, n, fake_local_data(1, 1), 1, 1, fake_descriptor, &
     508              :                           eigenvectors%local_data(1, 1), 1, 1, eigenvectors%matrix_struct%descriptor, &
     509       135837 :                           eigenvectors%matrix_struct%context)
     510              :          END IF
     511              :          ! free work
     512       275446 :          CALL cp_fm_redistribute_work_finalize(work_redistribute%group_distribution(mepos_old) == 0)
     513              : 
     514              :          ! finally, also the eigenvalues need to end up on the non-group member tasks
     515      7662682 :          CALL para_env%bcast(eig, 0)
     516              :       END IF
     517              : 
     518              : #else
     519              : 
     520              :       MARK_USED(matrix)
     521              :       MARK_USED(eigenvectors)
     522              :       MARK_USED(eig)
     523              :       MARK_USED(matrix_new)
     524              :       MARK_USED(eigenvectors_new)
     525              :       CPABORT("Routine called in non-parallel case.")
     526              : #endif
     527              : 
     528       279596 :       CALL timestop(handle)
     529              : 
     530       279596 :    END SUBROUTINE cp_fm_redistribute_end
     531              : 
     532              : #if defined(__parallel)
     533              : ! **************************************************************************************************
     534              : !> \brief  Determines the largest number of CPUs a matrix can be distributed on without any of the
     535              : !>         processors getting a zero-width column (currently only needed for ELPA).
     536              : !> \param  matrix the matrix that will be diagonalized
     537              : !> \return the maximum number of CPUs for ELPA
     538              : ! **************************************************************************************************
     539           72 :    FUNCTION cp_cfm_max_ncpu_non_zero_column(matrix) RESULT(ncpu)
     540              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix
     541              :       INTEGER                                            :: ncpu
     542              : 
     543              :       INTEGER                                            :: gcd_max, ipe, jpe, ncol_block, &
     544              :                                                             ncol_global, npcol, nrow_block, &
     545              :                                                             nrow_global, num_pe_old, nzero
     546           72 :       INTEGER, DIMENSION(:), POINTER                     :: ncol_locals
     547              :       INTEGER, EXTERNAL                                  :: numroc
     548              : 
     549           72 :       NULLIFY (ncol_locals)
     550              :       ! First check if there are any zero width columns in current layout
     551              :       CALL cp_fm_struct_get(matrix%matrix_struct, ncol_locals=ncol_locals, &
     552              :                             nrow_global=nrow_global, ncol_global=ncol_global, &
     553           72 :                             nrow_block=nrow_block, ncol_block=ncol_block)
     554          144 :       nzero = COUNT(ncol_locals == 0)
     555           72 :       num_pe_old = matrix%matrix_struct%para_env%num_pe
     556           72 :       ncpu = num_pe_old - nzero
     557              : 
     558              :       ! Avoid layouts with odd number of CPUs (blacs grid layout will be square)
     559           72 :       IF (ncpu > 2) THEN
     560            0 :          ncpu = ncpu - MODULO(ncpu, 2)
     561              :       END IF
     562              : 
     563              :       ! if there are no zero-width columns and the number of processors was even, leave it at that
     564           72 :       IF (ncpu == num_pe_old) THEN
     565              :          RETURN
     566              :       END IF
     567              : 
     568              :       ! Iteratively search for the maximum number of CPUs for ELPA
     569              :       ! On each step, we test whether the blacs grid created with ncpu processes
     570              :       ! contains any columns with zero width
     571            0 :       DO WHILE (ncpu > 1)
     572              :          ! Determine layout of new blacs grid with ncpu CPUs
     573              :          ! (snippet copied from cp_blacs_env.F:cp_blacs_env_create)
     574            0 :          gcd_max = -1
     575            0 :          DO ipe = 1, CEILING(SQRT(REAL(ncpu, dp)))
     576            0 :             jpe = ncpu/ipe
     577            0 :             IF (ipe*jpe /= ncpu) THEN
     578              :                CYCLE
     579              :             END IF
     580            0 :             IF (gcd(ipe, jpe) >= gcd_max) THEN
     581            0 :                npcol = jpe
     582            0 :                gcd_max = gcd(ipe, jpe)
     583              :             END IF
     584              :          END DO
     585              : 
     586              :          ! Count the number of processors without any columns
     587              :          ! (snippet copied from cp_fm_struct.F:cp_fm_struct_create)
     588            0 :          nzero = 0
     589            0 :          DO ipe = 0, npcol - 1
     590            0 :             IF (numroc(ncol_global, ncol_block, ipe, 0, npcol) == 0) THEN
     591            0 :                nzero = nzero + 1
     592              :             END IF
     593              :          END DO
     594              : 
     595            0 :          IF (nzero == 0) THEN
     596              :             EXIT
     597              :          END IF
     598              : 
     599            0 :          ncpu = ncpu - nzero
     600              : 
     601            0 :          IF (ncpu > 2) THEN
     602            0 :             ncpu = ncpu - MODULO(ncpu, 2)
     603              :          END IF
     604              :       END DO
     605              : 
     606           72 :    END FUNCTION cp_cfm_max_ncpu_non_zero_column
     607              : #endif
     608              : 
     609              : ! **************************************************************************************************
     610              : !> \brief   Determines the optimal number of CPUs for matrix diagonalization and redistributes
     611              : !>          the input matrices if necessary
     612              : !> \param matrix           the input cp_cfm_type matrix to be diagonalized
     613              : !> \param eigenvectors     the cp_cfm_type matrix that will hold the eigenvectors of the input matrix
     614              : !> \param matrix_new       the redistributed input matrix which will subsequently be diagonalized,
     615              : !>                         or a pointer to the original matrix if no redistribution is required
     616              : !> \param eigenvectors_new the redistributed eigenvectors matrix, or a pointer to the original
     617              : !>                         matrix if no redistribution is required
     618              : !> \param caller_is_elpa   flag that determines if ELPA is used for diagonalization
     619              : !> \param redist_info      get info about the redistribution
     620              : !> \par History
     621              : !>      - [08.2026] created by mirroring cp_fm_redistribute_start for complex matrices
     622              : ! **************************************************************************************************
     623           72 :    SUBROUTINE cp_cfm_redistribute_start(matrix, eigenvectors, matrix_new, eigenvectors_new, &
     624              :                                         caller_is_elpa, redist_info)
     625              : 
     626              :       TYPE(cp_cfm_type), INTENT(IN)             :: matrix, eigenvectors
     627              :       TYPE(cp_cfm_type), INTENT(OUT)            :: matrix_new, eigenvectors_new
     628              :       LOGICAL, OPTIONAL, INTENT(IN)             :: caller_is_elpa
     629              : 
     630              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_redistribute_start'
     631              : 
     632              :       INTEGER                                  :: handle
     633              :       LOGICAL                                  :: is_elpa
     634              :       TYPE(cp_fm_redistribute_info), OPTIONAL, INTENT(OUT) :: redist_info
     635              : 
     636              : #if defined(__parallel)
     637              :       COMPLEX(KIND=dp)                         :: fake_local_data(1, 1)
     638              :       INTEGER                                  :: fake_descriptor(9), mepos_old, &
     639              :                                                   io_unit, ngroups, ncol_block, blksize, nrow_block
     640              :       TYPE(cp_fm_struct_type), POINTER         :: fm_struct_new
     641              :       TYPE(mp_para_env_type), POINTER          :: para_env
     642              :       TYPE(cp_logger_type), POINTER            :: logger
     643              :       TYPE(cp_fm_redistribute_info)            :: rdinfo
     644              : #endif
     645              : 
     646           72 :       CALL timeset(routineN, handle)
     647           72 :       is_elpa = .FALSE.
     648           72 :       IF (PRESENT(caller_is_elpa)) THEN
     649              : #if defined(__ELPA)
     650           72 :          is_elpa = caller_is_elpa
     651              : #else
     652              :          CPABORT("CP2K compiled without the ELPA library.")
     653              : #endif
     654              :       END IF
     655              : 
     656              : #if defined(__parallel)
     657              : 
     658           72 :       logger => cp_get_default_logger()
     659           72 :       io_unit = cp_logger_get_default_io_unit(logger)
     660              : 
     661              :       ! first figure out the optimal number of cpus
     662              :       ! this is pure heuristics, the defaults are based on rosa timings
     663              :       ! that demonstrate that timings go up sharply if too many tasks are used
     664              :       ! we take a multiple of 4, and approximately n/60
     665           72 :       para_env => matrix%matrix_struct%para_env
     666           72 :       mepos_old = para_env%mepos
     667           72 :       ncol_block = -1 ! normally we also want to adjust the block size according to the optimal # of CPUs
     668           72 :       nrow_block = -1
     669           72 :       blksize = -1
     670              : 
     671           72 :       rdinfo%matrix_order = matrix%matrix_struct%nrow_global
     672           72 :       rdinfo%num_pe_old = para_env%num_pe
     673           72 :       rdinfo%num_pe_opt = cp_fm_diag_get_optimal_ncpu(rdinfo%matrix_order)
     674           72 :       rdinfo%num_pe_new = rdinfo%num_pe_opt
     675              :       rdinfo%num_pe_max_nz_col = -1
     676              :       rdinfo%redistribute = .FALSE.
     677              : 
     678           72 :       IF (is_elpa) THEN
     679              :          ! with ELPA we don't have to redistribute if not necessary (scales, unlike ScaLAPACK)
     680           72 :          rdinfo%num_pe_new = rdinfo%num_pe_old
     681              : 
     682              :          ! BUT: Diagonalization with ELPA fails when a processor column has zero width
     683              :          ! Determine the maximum number of CPUs the matrix can be distributed without zero-width columns
     684              :          ! for the current block size.
     685           72 :          rdinfo%num_pe_max_nz_col = cp_cfm_max_ncpu_non_zero_column(matrix)
     686              : 
     687              :          ! if the user wants to redistribute to the ScaLAPACK optimal number of CPUs anyway, let him if it's safe.
     688           72 :          IF (work_redistribute%elpa_force_redistribute .AND. rdinfo%num_pe_opt < rdinfo%num_pe_max_nz_col) THEN
     689              :             ! Use heuristics to determine the need for redistribution (when num_pe_opt is smaller than the safe maximum)
     690              :             ! in this case we can also take the block size used for ScaLAPACK
     691            0 :             rdinfo%num_pe_new = rdinfo%num_pe_opt
     692           72 :          ELSE IF (rdinfo%num_pe_old > rdinfo%num_pe_max_nz_col) THEN
     693              :             ! Otherwise, only redistribute if we have to
     694            0 :             rdinfo%num_pe_new = rdinfo%num_pe_max_nz_col
     695              :             ! do NOT let cp_fm_struct_create automatically adjust the block size because the
     696              :             ! calculated number of processors such that no block has 0 columns wouldn't match (see #578):
     697              :             ! if the automatically chosen block size is larger than the present one we would still end
     698              :             ! up with empty processors
     699              :          END IF
     700              : 
     701           72 :          CALL cp_cfm_get_info(matrix, ncol_block=ncol_block, nrow_block=nrow_block)
     702              : 
     703              :          ! On GPUs, ELPA requires the block size to be a power of 2
     704           72 :          blksize = 1
     705          360 :          DO WHILE (2*blksize <= MIN(nrow_block, ncol_block))
     706           72 :             blksize = blksize*2
     707              :          END DO
     708           72 :          nrow_block = blksize
     709           72 :          ncol_block = blksize
     710              :       END IF
     711              : 
     712              :       ! finally, only redistribute if we're going to use less CPUs than before or changed the block size
     713              :       rdinfo%redistribute = (rdinfo%num_pe_old > rdinfo%num_pe_new) .OR. (blksize >= 0 .AND. &
     714           72 :                                    ((blksize /= matrix%matrix_struct%ncol_block) .OR. (blksize /= matrix%matrix_struct%nrow_block)))
     715              : 
     716           72 :       IF (work_redistribute%should_print .AND. io_unit > 0) THEN
     717            0 :          IF (is_elpa) THEN
     718            0 :             IF (work_redistribute%elpa_force_redistribute) THEN
     719              :                WRITE (UNIT=io_unit, FMT="(T2,A,T78,A3)") &
     720            0 :                   "CP_FM_DIAG| Force redistribute (ELPA):", "YES"
     721              :             ELSE
     722              :                WRITE (UNIT=io_unit, FMT="(T2,A,T79,A2)") &
     723            0 :                   "CP_FM_DIAG| Force redistribute (ELPA):", "NO"
     724              :             END IF
     725              :          END IF
     726            0 :          CALL rdinfo%write(io_unit)
     727              :       END IF
     728           72 :       CALL para_env%sync()
     729              : 
     730              :       ! if the optimal is smaller than num_pe, we will redistribute the input matrix
     731           72 :       IF (rdinfo%redistribute) THEN
     732              :          ! split comm, the first num_pe_new tasks will do the work
     733          216 :          ALLOCATE (work_redistribute%group_distribution(0:rdinfo%num_pe_old - 1))
     734           72 :          ALLOCATE (work_redistribute%group_partition(0:1))
     735          216 :          work_redistribute%group_partition = [rdinfo%num_pe_new, rdinfo%num_pe_old - rdinfo%num_pe_new]
     736           72 :          ALLOCATE (work_redistribute%para_env_new)
     737              :          CALL work_redistribute%para_env_new%from_split( &
     738              :             comm=para_env, ngroups=ngroups, group_distribution=work_redistribute%group_distribution, &
     739           72 :             n_subgroups=2, group_partition=work_redistribute%group_partition)
     740              : 
     741           72 :          IF (work_redistribute%group_distribution(mepos_old) == 0) THEN
     742              : 
     743              :             ! create blacs, should inherit the preferences for the layout and so on, from the higher level
     744           36 :             NULLIFY (work_redistribute%blacs_env_new)
     745           36 :             CALL cp_blacs_env_create(blacs_env=work_redistribute%blacs_env_new, para_env=work_redistribute%para_env_new)
     746              : 
     747              :             ! create new matrix
     748           36 :             NULLIFY (fm_struct_new)
     749           36 :             IF (nrow_block == -1 .OR. ncol_block == -1) THEN
     750              :                CALL cp_fm_struct_create(fmstruct=fm_struct_new, &
     751              :                                         para_env=work_redistribute%para_env_new, &
     752              :                                         context=work_redistribute%blacs_env_new, &
     753              :                                         nrow_global=rdinfo%matrix_order, ncol_global=rdinfo%matrix_order, &
     754            0 :                                         ncol_block=ncol_block, nrow_block=nrow_block)
     755              :             ELSE
     756              :                CALL cp_fm_struct_create(fmstruct=fm_struct_new, &
     757              :                                         para_env=work_redistribute%para_env_new, &
     758              :                                         context=work_redistribute%blacs_env_new, &
     759              :                                         nrow_global=rdinfo%matrix_order, ncol_global=rdinfo%matrix_order, &
     760           36 :                                         ncol_block=ncol_block, nrow_block=nrow_block, force_block=.TRUE.)
     761              :             END IF
     762           36 :             CALL cp_cfm_create(matrix_new, matrix_struct=fm_struct_new, name="zheevd_new_mat")
     763           36 :             CALL cp_cfm_create(eigenvectors_new, matrix_struct=fm_struct_new, name="zheevd_new_vec")
     764           36 :             CALL cp_fm_struct_release(fm_struct_new)
     765              : 
     766              :             ! redistribute old
     767              :             CALL pzgemr2d(rdinfo%matrix_order, rdinfo%matrix_order, matrix%local_data(1, 1), 1, 1, &
     768              :                           matrix%matrix_struct%descriptor, &
     769              :                           matrix_new%local_data(1, 1), 1, 1, matrix_new%matrix_struct%descriptor, &
     770           36 :                           matrix%matrix_struct%context)
     771              :          ELSE
     772              :             ! these tasks must help redistribute (they own part of the data),
     773              :             ! but need fake 'new' data, and their descriptor must indicate this with -1
     774              :             ! see also scalapack comments on pzgemr2d
     775          360 :             fake_descriptor = -1
     776              :             CALL pzgemr2d(rdinfo%matrix_order, rdinfo%matrix_order, matrix%local_data(1, 1), 1, 1, &
     777              :                           matrix%matrix_struct%descriptor, &
     778              :                           fake_local_data(1, 1), 1, 1, fake_descriptor, &
     779           36 :                           matrix%matrix_struct%context)
     780              :          END IF
     781              :       ELSE
     782              :          ! No need to redistribute, just return pointers to the original arrays
     783            0 :          matrix_new = matrix
     784            0 :          eigenvectors_new = eigenvectors
     785              :       END IF
     786              : 
     787           72 :       IF (PRESENT(redist_info)) THEN
     788           72 :          redist_info = rdinfo
     789              :       END IF
     790              : #else
     791              : 
     792              :       MARK_USED(matrix)
     793              :       MARK_USED(eigenvectors)
     794              :       MARK_USED(matrix_new)
     795              :       MARK_USED(eigenvectors_new)
     796              :       MARK_USED(redist_info)
     797              :       CPABORT("Routine called in non-parallel case.")
     798              : #endif
     799              : 
     800           72 :       CALL timestop(handle)
     801              : 
     802           72 :    END SUBROUTINE cp_cfm_redistribute_start
     803              : 
     804              : ! **************************************************************************************************
     805              : !> \brief Redistributes eigenvectors and eigenvalues  back to the original communicator group
     806              : !> \param matrix           the input cp_cfm_type matrix to be diagonalized
     807              : !> \param eigenvectors     the cp_cfm_type matrix that will hold the eigenvectors of the input matrix
     808              : !> \param eig              global array holding the eigenvalues of the input matrixmatrix
     809              : !> \param matrix_new       the redistributed input matrix which will subsequently be diagonalized,
     810              : !>                         or a pointer to the original matrix if no redistribution is required
     811              : !> \param eigenvectors_new the redistributed eigenvectors matrix, or a pointer to the original
     812              : !>                         matrix if no redistribution is required
     813              : !> \par History
     814              : !>      - [08.2026] created by mirroring cp_fm_redistribute_end for complex matrices
     815              : ! **************************************************************************************************
     816           72 :    SUBROUTINE cp_cfm_redistribute_end(matrix, eigenvectors, eig, matrix_new, eigenvectors_new)
     817              : 
     818              :       TYPE(cp_cfm_type), INTENT(IN)             :: matrix, eigenvectors
     819              :       REAL(KIND=dp), DIMENSION(:), INTENT(INOUT) :: eig
     820              :       TYPE(cp_cfm_type), INTENT(INOUT)          :: matrix_new, eigenvectors_new
     821              : 
     822              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_redistribute_end'
     823              : 
     824              :       INTEGER                                  :: handle
     825              : #if defined(__parallel)
     826              :       COMPLEX(KIND=dp)                         :: fake_local_data(1, 1)
     827              :       INTEGER                                  :: fake_descriptor(9), mepos_old, n
     828              :       TYPE(mp_para_env_type), POINTER          :: para_env
     829              : #endif
     830              : 
     831           72 :       CALL timeset(routineN, handle)
     832              : 
     833              : #if defined(__parallel)
     834              : 
     835              :       ! Check if matrix was redistributed
     836           72 :       IF (ASSOCIATED(work_redistribute%group_distribution)) THEN
     837           72 :          n = matrix%matrix_struct%nrow_global
     838           72 :          para_env => matrix%matrix_struct%para_env
     839           72 :          mepos_old = para_env%mepos
     840              : 
     841           72 :          IF (work_redistribute%group_distribution(mepos_old) == 0) THEN
     842              :             ! redistribute results on CPUs that hold the redistributed matrix
     843              :             CALL pzgemr2d(n, n, eigenvectors_new%local_data(1, 1), 1, 1, eigenvectors_new%matrix_struct%descriptor, &
     844              :                           eigenvectors%local_data(1, 1), 1, 1, eigenvectors%matrix_struct%descriptor, &
     845           36 :                           eigenvectors%matrix_struct%context)
     846           36 :             CALL cp_cfm_release(matrix_new)
     847           36 :             CALL cp_cfm_release(eigenvectors_new)
     848              :          ELSE
     849              :             ! these tasks must help redistribute (they own part of the data),
     850              :             ! but need fake 'new' data, and their descriptor must indicate this with -1
     851              :             ! see also scalapack comments on pzgemr2d
     852          360 :             fake_descriptor = -1
     853              :             CALL pzgemr2d(n, n, fake_local_data(1, 1), 1, 1, fake_descriptor, &
     854              :                           eigenvectors%local_data(1, 1), 1, 1, eigenvectors%matrix_struct%descriptor, &
     855           36 :                           eigenvectors%matrix_struct%context)
     856              :          END IF
     857              :          ! free work
     858           72 :          CALL cp_fm_redistribute_work_finalize(work_redistribute%group_distribution(mepos_old) == 0)
     859              : 
     860              :          ! finally, also the eigenvalues need to end up on the non-group member tasks
     861         5688 :          CALL para_env%bcast(eig, 0)
     862              :       END IF
     863              : 
     864              : #else
     865              : 
     866              :       MARK_USED(matrix)
     867              :       MARK_USED(eigenvectors)
     868              :       MARK_USED(eig)
     869              :       MARK_USED(matrix_new)
     870              :       MARK_USED(eigenvectors_new)
     871              :       CPABORT("Routine called in non-parallel case.")
     872              : #endif
     873              : 
     874           72 :       CALL timestop(handle)
     875              : 
     876           72 :    END SUBROUTINE cp_cfm_redistribute_end
     877              : 
     878            0 : END MODULE cp_fm_diag_utils
        

Generated by: LCOV version 2.0-1