LCOV - code coverage report
Current view: top level - src/fm - cp_fm_diag.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:ccc2433) Lines: 315 376 83.8 %
Date: 2024-04-25 07:09:54 Functions: 11 11 100.0 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2024 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : ! **************************************************************************************************
       9             : !> \brief used for collecting some of the diagonalization schemes available for
      10             : !>      cp_fm_type. cp_fm_power also moved here as it is very related
      11             : !> \note
      12             : !>      first version : most routines imported
      13             : !> \par History
      14             : !>      - unused Jacobi routines removed, cosmetics (05.04.06,MK)
      15             : !> \author Joost VandeVondele (2003-08)
      16             : ! **************************************************************************************************
      17             : MODULE cp_fm_diag
      18             : 
      19             :    USE cp_blacs_types, ONLY: cp_blacs_type
      20             :    USE cp_blacs_env, ONLY: cp_blacs_env_type
      21             :    USE cp_fm_basic_linalg, ONLY: cp_fm_column_scale, &
      22             :                                  cp_fm_gemm, &
      23             :                                  cp_fm_scale, &
      24             :                                  cp_fm_syrk, &
      25             :                                  cp_fm_triangular_invert, &
      26             :                                  cp_fm_triangular_multiply, &
      27             :                                  cp_fm_upper_to_full
      28             :    USE cp_fm_cholesky, ONLY: cp_fm_cholesky_decompose
      29             :    USE cp_fm_diag_utils, ONLY: cp_fm_redistribute_end, &
      30             :                                cp_fm_redistribute_start
      31             :    USE cp_fm_elpa, ONLY: cp_fm_diag_elpa, &
      32             :                          finalize_elpa_library, &
      33             :                          initialize_elpa_library, &
      34             :                          set_elpa_kernel, &
      35             :                          set_elpa_print, &
      36             :                          set_elpa_qr
      37             :    USE cp_fm_cusolver_api, ONLY: cp_fm_diag_cusolver
      38             : #if defined(__DLAF)
      39             :    USE cp_fm_dlaf_api, ONLY: cp_fm_diag_dlaf
      40             : #endif
      41             :    USE cp_fm_types, ONLY: cp_fm_get_info, &
      42             :                           cp_fm_set_element, &
      43             :                           cp_fm_to_fm, &
      44             :                           cp_fm_type
      45             :    USE cp_log_handling, ONLY: cp_get_default_logger, &
      46             :                               cp_logger_get_default_unit_nr, &
      47             :                               cp_logger_get_unit_nr, &
      48             :                               cp_logger_type
      49             :    USE kinds, ONLY: default_string_length, &
      50             :                     dp
      51             :    USE machine, ONLY: default_output_unit, &
      52             :                       m_memory
      53             : #if defined (__SCALAPACK)
      54             :    USE message_passing, ONLY: mp_comm_type
      55             : #endif
      56             : #if defined (__HAS_IEEE_EXCEPTIONS)
      57             :    USE ieee_exceptions, ONLY: ieee_get_halting_mode, &
      58             :                               ieee_set_halting_mode, &
      59             :                               IEEE_ALL
      60             : #endif
      61             : #include "../base/base_uses.f90"
      62             : 
      63             :    IMPLICIT NONE
      64             : 
      65             :    PRIVATE
      66             : 
      67             :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_fm_diag'
      68             : 
      69             :    REAL(KIND=dp), PARAMETER, PUBLIC :: eps_check_diag_default = 5.0E-14_dp
      70             : 
      71             :    ! The following saved variables are diagonalization global
      72             :    ! Stores the default library for diagonalization
      73             :    INTEGER, SAVE       :: diag_type = 0
      74             :    ! Minimum number of eigenvectors for the use of the ELPA eigensolver.
      75             :    ! The ScaLAPACK eigensolver is used as fallback for all smaller cases.
      76             :    INTEGER, SAVE       :: elpa_neigvec_min = 0
      77             : #if defined(__DLAF)
      78             :    ! Minimum number of eigenvectors for the use of the DLAF eigensolver.
      79             :    ! The ScaLAPACK eigensolver is used as fallback for all smaller cases.
      80             :    INTEGER, SAVE       :: dlaf_neigvec_min = 0
      81             : #endif
      82             :    ! Threshold value for the orthonormality check of the eigenvectors obtained
      83             :    ! after a diagonalization. A negative value disables the check.
      84             :    REAL(KIND=dp), SAVE :: eps_check_diag = -1.0_dp
      85             : 
      86             :    ! Constants for the diag_type above
      87             :    INTEGER, PARAMETER, PUBLIC  :: FM_DIAG_TYPE_SCALAPACK = 101, &
      88             :                                   FM_DIAG_TYPE_ELPA = 102, &
      89             :                                   FM_DIAG_TYPE_CUSOLVER = 103, &
      90             :                                   FM_DIAG_TYPE_DLAF = 104
      91             : #if defined(__CUSOLVERMP)
      92             :    INTEGER, PARAMETER, PUBLIC :: FM_DIAG_TYPE_DEFAULT = FM_DIAG_TYPE_CUSOLVER
      93             : #elif defined(__ELPA)
      94             :    INTEGER, PARAMETER, PUBLIC :: FM_DIAG_TYPE_DEFAULT = FM_DIAG_TYPE_ELPA
      95             : #elif defined(__DLAF)
      96             :    INTEGER, PARAMETER, PUBLIC :: FM_DIAG_TYPE_DEFAULT = FM_DIAG_TYPE_DLAF
      97             : #else
      98             :    INTEGER, PARAMETER, PUBLIC :: FM_DIAG_TYPE_DEFAULT = FM_DIAG_TYPE_SCALAPACK
      99             : #endif
     100             : 
     101             :    ! Public subroutines
     102             :    PUBLIC :: choose_eigv_solver, &
     103             :              cp_fm_block_jacobi, &
     104             :              cp_fm_power, &
     105             :              cp_fm_syevd, &
     106             :              cp_fm_syevx, &
     107             :              cp_fm_geeig, &
     108             :              cp_fm_geeig_canon, &
     109             :              diag_init, &
     110             :              diag_finalize
     111             : 
     112             : CONTAINS
     113             : 
     114             : ! **************************************************************************************************
     115             : !> \brief Setup the diagonalization library to be used
     116             : !> \param diag_lib diag_library flag from GLOBAL section in input
     117             : !> \param fallback_applied .TRUE. if support for the requested library was not compiled-in and fallback
     118             : !>                         to ScaLAPACK was applied, .FALSE. otherwise.
     119             : !> \param elpa_kernel integer that determines which ELPA kernel to use for diagonalization
     120             : !> \param elpa_neigvec_min_input ...
     121             : !> \param elpa_qr logical that determines if ELPA should try to use QR to accelerate the
     122             : !>                diagonalization procedure of suitably sized matrices
     123             : !> \param elpa_print logical that determines if information about the ELPA diagonalization should
     124             : !>                   be printed
     125             : !> \param elpa_qr_unsafe logical that enables potentially unsafe ELPA options
     126             : !> \param dlaf_neigvec_min_input ...
     127             : !> \param eps_check_diag_input ...
     128             : !> \par History
     129             : !>      - Add support for DLA-Future (05.09.2023, RMeli)
     130             : !> \author  MI 11.2013
     131             : ! **************************************************************************************************
     132        8989 :    SUBROUTINE diag_init(diag_lib, fallback_applied, elpa_kernel, elpa_neigvec_min_input, elpa_qr, &
     133             :                         elpa_print, elpa_qr_unsafe, dlaf_neigvec_min_input, eps_check_diag_input)
     134             :       CHARACTER(LEN=*), INTENT(IN)                       :: diag_lib
     135             :       LOGICAL, INTENT(OUT)                               :: fallback_applied
     136             :       INTEGER, INTENT(IN)                                :: elpa_kernel, elpa_neigvec_min_input
     137             :       LOGICAL, INTENT(IN)                                :: elpa_qr, elpa_print, elpa_qr_unsafe
     138             :       INTEGER, INTENT(IN)                                :: dlaf_neigvec_min_input
     139             :       REAL(KIND=dp), INTENT(IN)                          :: eps_check_diag_input
     140             : 
     141        8989 :       fallback_applied = .FALSE.
     142             : 
     143        8989 :       IF (diag_lib == "ScaLAPACK") THEN
     144         188 :          diag_type = FM_DIAG_TYPE_SCALAPACK
     145        8801 :       ELSE IF (diag_lib == "ELPA") THEN
     146             : #if defined (__ELPA)
     147             :          ! ELPA is requested and available
     148        8801 :          diag_type = FM_DIAG_TYPE_ELPA
     149             : #else
     150             :          ! ELPA library requested but not linked, switch back to SL
     151             :          diag_type = FM_DIAG_TYPE_SCALAPACK
     152             :          fallback_applied = .TRUE.
     153             : #endif
     154           0 :       ELSE IF (diag_lib == "cuSOLVER") THEN
     155           0 :          diag_type = FM_DIAG_TYPE_CUSOLVER
     156           0 :       ELSE IF (diag_lib == "DLAF") THEN
     157             : #if defined (__DLAF)
     158             :          diag_type = FM_DIAG_TYPE_DLAF
     159             : #else
     160           0 :          CPABORT("ERROR in diag_init: CP2K was not compiled with DLA-Future support")
     161             : #endif
     162             :       ELSE
     163           0 :          CPABORT("ERROR in diag_init: Initialization of unknown diagonalization library requested")
     164             :       END IF
     165             : 
     166             :       ! Initialization of requested diagonalization library:
     167        8989 :       IF (diag_type == FM_DIAG_TYPE_ELPA) THEN
     168        8801 :          CALL initialize_elpa_library()
     169        8801 :          CALL set_elpa_kernel(elpa_kernel)
     170        8801 :          CALL set_elpa_qr(elpa_qr, elpa_qr_unsafe)
     171        8801 :          CALL set_elpa_print(elpa_print)
     172             :       END IF
     173             : 
     174        8989 :       elpa_neigvec_min = elpa_neigvec_min_input
     175             : #if defined(__DLAF)
     176             :       dlaf_neigvec_min = dlaf_neigvec_min_input
     177             : #else
     178             :       MARK_USED(dlaf_neigvec_min_input)
     179             : #endif
     180        8989 :       eps_check_diag = eps_check_diag_input
     181             : 
     182        8989 :    END SUBROUTINE diag_init
     183             : 
     184             : ! **************************************************************************************************
     185             : !> \brief Finalize the diagonalization library
     186             : ! **************************************************************************************************
     187        8779 :    SUBROUTINE diag_finalize()
     188             : #if defined (__ELPA)
     189        8779 :       IF (diag_type == FM_DIAG_TYPE_ELPA) &
     190        8591 :          CALL finalize_elpa_library()
     191             : #endif
     192        8779 :    END SUBROUTINE diag_finalize
     193             : 
     194             : ! **************************************************************************************************
     195             : !> \brief   Choose the Eigensolver depending on which library is available
     196             : !>          ELPA seems to be unstable for small systems
     197             : !> \param matrix ...
     198             : !> \param eigenvectors ...
     199             : !> \param eigenvalues ...
     200             : !> \param info ...
     201             : !> \par     info If present returns error code and prevents program stops.
     202             : !>               Works currently only for cp_fm_syevd with ScaLAPACK.
     203             : !>               Other solvers will end the program regardless of PRESENT(info).
     204             : !> \par History
     205             : !>      - Do not use ELPA for small matrices and use instead ScaLAPACK as fallback (10.05.2021, MK)
     206             : ! **************************************************************************************************
     207      162069 :    SUBROUTINE choose_eigv_solver(matrix, eigenvectors, eigenvalues, info)
     208             : 
     209             :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix, eigenvectors
     210             :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: eigenvalues
     211             :       INTEGER, INTENT(OUT), OPTIONAL                     :: info
     212             : 
     213             :       CHARACTER(LEN=*), PARAMETER :: routineN = 'choose_eigv_solver'
     214             : 
     215             :       ! Sample peak memory
     216      162069 :       CALL m_memory()
     217             : 
     218      162069 :       IF (PRESENT(info)) info = 0 ! Default for solvers that do not return an info.
     219             : 
     220      162069 :       IF (diag_type == FM_DIAG_TYPE_SCALAPACK) THEN
     221        4114 :          CALL cp_fm_syevd(matrix, eigenvectors, eigenvalues, info)
     222             : 
     223      157955 :       ELSE IF (diag_type == FM_DIAG_TYPE_ELPA) THEN
     224      157955 :          IF (matrix%matrix_struct%nrow_global < elpa_neigvec_min) THEN
     225             :             ! We don't trust ELPA with very small matrices.
     226       98637 :             CALL cp_fm_syevd(matrix, eigenvectors, eigenvalues, info)
     227             :          ELSE
     228       59318 :             CALL cp_fm_diag_elpa(matrix, eigenvectors, eigenvalues)
     229             :          END IF
     230             : 
     231           0 :       ELSE IF (diag_type == FM_DIAG_TYPE_CUSOLVER) THEN
     232           0 :          IF (matrix%matrix_struct%nrow_global < 64) THEN
     233             :             ! We don't trust cuSolver with very small matrices.
     234           0 :             CALL cp_fm_syevd(matrix, eigenvectors, eigenvalues, info)
     235             :          ELSE
     236           0 :             CALL cp_fm_diag_cusolver(matrix, eigenvectors, eigenvalues)
     237             :          END IF
     238             : 
     239             : #if defined(__DLAF)
     240             :       ELSE IF (diag_type == FM_DIAG_TYPE_DLAF) THEN
     241             :          IF (matrix%matrix_struct%nrow_global < dlaf_neigvec_min) THEN
     242             :             ! Use ScaLAPACK for small matrices
     243             :             CALL cp_fm_syevd(matrix, eigenvectors, eigenvalues, info)
     244             :          ELSE
     245             :             CALL cp_fm_diag_dlaf(matrix, eigenvectors, eigenvalues)
     246             :          END IF
     247             : #endif
     248             : 
     249             :       ELSE
     250           0 :          CPABORT("ERROR in "//routineN//": Invalid diagonalization type requested")
     251             :       END IF
     252             : 
     253      162069 :       CALL check_diag(matrix, eigenvectors, nvec=SIZE(eigenvalues))
     254             : 
     255      162069 :    END SUBROUTINE choose_eigv_solver
     256             : 
     257             : ! **************************************************************************************************
     258             : !> \brief   Check result of diagonalization, i.e. the orthonormality of the eigenvectors
     259             : !> \param matrix Work matrix
     260             : !> \param eigenvectors Eigenvectors to be checked
     261             : !> \param nvec ...
     262             : ! **************************************************************************************************
     263      267418 :    SUBROUTINE check_diag(matrix, eigenvectors, nvec)
     264             : 
     265             :       TYPE(cp_fm_type), INTENT(IN)                    :: matrix, eigenvectors
     266             :       INTEGER, INTENT(IN)                                :: nvec
     267             : 
     268             :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'check_diag'
     269             : 
     270             :       CHARACTER(LEN=default_string_length)               :: diag_type_name
     271             :       REAL(KIND=dp)                                      :: eps_abort, eps_warning
     272             :       INTEGER                                            :: handle, i, j, ncol, nrow, output_unit
     273             :       LOGICAL                                            :: check_eigenvectors
     274             : #if defined(__SCALAPACK)
     275             :       TYPE(cp_blacs_env_type), POINTER                   :: context
     276             :       INTEGER                                            :: il, jl, ipcol, iprow, &
     277             :                                                             mypcol, myprow, npcol, nprow
     278             :       INTEGER, DIMENSION(9)                              :: desca
     279             : #endif
     280             : 
     281      267418 :       CALL timeset(routineN, handle)
     282             : 
     283      267418 :       IF (diag_type == FM_DIAG_TYPE_SCALAPACK) THEN
     284        8228 :          diag_type_name = "SYEVD"
     285      259190 :       ELSE IF (diag_type == FM_DIAG_TYPE_ELPA) THEN
     286      259190 :          diag_type_name = "ELPA"
     287           0 :       ELSE IF (diag_type == FM_DIAG_TYPE_CUSOLVER) THEN
     288           0 :          diag_type_name = "CUSOLVER"
     289           0 :       ELSE IF (diag_type == FM_DIAG_TYPE_DLAF) THEN
     290           0 :          diag_type_name = "DLAF"
     291             :       ELSE
     292           0 :          CPABORT("Unknown diag_type")
     293             :       END IF
     294             : 
     295      267418 :       output_unit = default_output_unit
     296             : 
     297      267418 :       eps_warning = eps_check_diag_default
     298             : #if defined(__CHECK_DIAG)
     299             :       check_eigenvectors = .TRUE.
     300             :       IF (eps_check_diag >= 0.0_dp) THEN
     301             :          eps_warning = eps_check_diag
     302             :       END IF
     303             : #else
     304      267418 :       IF (eps_check_diag >= 0.0_dp) THEN
     305         242 :          check_eigenvectors = .TRUE.
     306         242 :          eps_warning = eps_check_diag
     307             :       ELSE
     308             :          check_eigenvectors = .FALSE.
     309             :       END IF
     310             : #endif
     311      267418 :       eps_abort = 10.0_dp*eps_warning
     312             : 
     313      267418 :       IF (check_eigenvectors) THEN
     314             : #if defined(__SCALAPACK)
     315         242 :          nrow = eigenvectors%matrix_struct%nrow_global
     316         242 :          ncol = MIN(eigenvectors%matrix_struct%ncol_global, nvec)
     317         242 :          CALL cp_fm_gemm("T", "N", ncol, ncol, nrow, 1.0_dp, eigenvectors, eigenvectors, 0.0_dp, matrix)
     318         242 :          context => matrix%matrix_struct%context
     319         242 :          myprow = context%mepos(1)
     320         242 :          mypcol = context%mepos(2)
     321         242 :          nprow = context%num_pe(1)
     322         242 :          npcol = context%num_pe(2)
     323        2420 :          desca(:) = matrix%matrix_struct%descriptor(:)
     324        2792 :          DO i = 1, ncol
     325       41810 :             DO j = 1, ncol
     326       39018 :                CALL infog2l(i, j, desca, nprow, npcol, myprow, mypcol, il, jl, iprow, ipcol)
     327       41568 :                IF ((iprow == myprow) .AND. (ipcol == mypcol)) THEN
     328       19509 :                   IF (i == j) THEN
     329        1275 :                      IF (ABS(matrix%local_data(il, jl) - 1.0_dp) > eps_warning) THEN
     330             :                         WRITE (UNIT=output_unit, FMT="(/,T2,A,/,T2,A,I0,A,I0,A,F0.15,/,T2,A,ES10.3)") &
     331           0 :                            "The eigenvectors returned by "//TRIM(diag_type_name)//" are not orthonormal", &
     332           0 :                            "Matrix element (", i, ", ", j, ") = ", matrix%local_data(il, jl), &
     333           0 :                            "The deviation from the expected value 1 is", ABS(matrix%local_data(il, jl) - 1.0_dp)
     334           0 :                         IF (ABS(matrix%local_data(il, jl) - 1.0_dp) > eps_abort) THEN
     335           0 :                            CPABORT("ERROR in "//routineN//": Check of matrix diagonalization failed")
     336             :                         ELSE
     337           0 :                            CPWARN("Check of matrix diagonalization failed in routine "//routineN)
     338             :                         END IF
     339             :                      END IF
     340             :                   ELSE
     341       18234 :                      IF (ABS(matrix%local_data(il, jl)) > eps_warning) THEN
     342             :                         WRITE (UNIT=output_unit, FMT="(/,T2,A,/,T2,A,I0,A,I0,A,F0.15,/,T2,A,ES10.3)") &
     343           0 :                            "The eigenvectors returned by "//TRIM(diag_type_name)//" are not orthonormal", &
     344           0 :                            "Matrix element (", i, ", ", j, ") = ", matrix%local_data(il, jl), &
     345           0 :                            "The deviation from the expected value 0 is", ABS(matrix%local_data(il, jl))
     346           0 :                         IF (ABS(matrix%local_data(il, jl)) > eps_abort) THEN
     347           0 :                            CPABORT("ERROR in "//routineN//": Check of matrix diagonalization failed")
     348             :                         ELSE
     349           0 :                            CPWARN("Check of matrix diagonalization failed in routine "//routineN)
     350             :                         END IF
     351             :                      END IF
     352             :                   END IF
     353             :                END IF
     354             :             END DO
     355             :          END DO
     356             : #else
     357             :          nrow = SIZE(eigenvectors%local_data, 1)
     358             :          ncol = MIN(SIZE(eigenvectors%local_data, 2), nvec)
     359             :          CALL dgemm("T", "N", ncol, ncol, nrow, 1.0_dp, &
     360             :                     eigenvectors%local_data(1, 1), nrow, &
     361             :                     eigenvectors%local_data(1, 1), nrow, &
     362             :                     0.0_dp, matrix%local_data(1, 1), nrow)
     363             :          DO i = 1, ncol
     364             :             DO j = 1, ncol
     365             :                IF (i == j) THEN
     366             :                   IF (ABS(matrix%local_data(i, j) - 1.0_dp) > eps_warning) THEN
     367             :                      WRITE (UNIT=output_unit, FMT="(/,T2,A,/,T2,A,I0,A,I0,A,F0.15,/,T2,A,ES10.3)") &
     368             :                         "The eigenvectors returned by "//TRIM(diag_type_name)//" are not orthonormal", &
     369             :                         "Matrix element (", i, ", ", j, ") = ", matrix%local_data(i, j), &
     370             :                         "The deviation from the expected value 1 is", ABS(matrix%local_data(i, j) - 1.0_dp)
     371             :                      IF (ABS(matrix%local_data(i, j) - 1.0_dp) > eps_abort) THEN
     372             :                         CPABORT("ERROR in "//routineN//": Check of matrix diagonalization failed")
     373             :                      ELSE
     374             :                         CPWARN("Check of matrix diagonalization failed in routine "//routineN)
     375             :                      END IF
     376             :                   END IF
     377             :                ELSE
     378             :                   IF (ABS(matrix%local_data(i, j)) > eps_warning) THEN
     379             :                      WRITE (UNIT=output_unit, FMT="(/,T2,A,/,T2,A,I0,A,I0,A,F0.15,/,T2,A,ES10.3)") &
     380             :                         "The eigenvectors returned by "//TRIM(diag_type_name)//" are not orthonormal", &
     381             :                         "Matrix element (", i, ", ", j, ") = ", matrix%local_data(i, j), &
     382             :                         "The deviation from the expected value 0 is", ABS(matrix%local_data(i, j))
     383             :                      IF (ABS(matrix%local_data(i, j)) > eps_abort) THEN
     384             :                         CPABORT("ERROR in "//routineN//": Check of matrix diagonalization failed")
     385             :                      ELSE
     386             :                         CPWARN("Check of matrix diagonalization failed in routine "//routineN)
     387             :                      END IF
     388             :                   END IF
     389             :                END IF
     390             :             END DO
     391             :          END DO
     392             : #endif
     393             :       END IF
     394             : 
     395      267418 :       CALL timestop(handle)
     396             : 
     397      267418 :    END SUBROUTINE check_diag
     398             : 
     399             : ! **************************************************************************************************
     400             : !> \brief   Computes all eigenvalues and vectors of a real symmetric matrix
     401             : !>          significantly faster than syevx, scales also much better.
     402             : !>          Needs workspace to allocate all the eigenvectors
     403             : !> \param matrix ...
     404             : !> \param eigenvectors ...
     405             : !> \param eigenvalues ...
     406             : !> \param info ...
     407             : !> \par     matrix is supposed to be in upper triangular form, and overwritten by this routine
     408             : !> \par     info If present returns error code and prevents program stops.
     409             : !>               Works currently only for scalapack.
     410             : !>               Other solvers will end the program regardless of PRESENT(info).
     411             : ! **************************************************************************************************
     412      105309 :    SUBROUTINE cp_fm_syevd(matrix, eigenvectors, eigenvalues, info)
     413             : 
     414             :       TYPE(cp_fm_type), INTENT(IN)  :: matrix, eigenvectors
     415             :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: eigenvalues
     416             :       INTEGER, INTENT(OUT), OPTIONAL           :: info
     417             : 
     418             :       CHARACTER(LEN=*), PARAMETER              :: routineN = 'cp_fm_syevd'
     419             : 
     420             :       INTEGER                                  :: handle, myinfo, n, nmo
     421             :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eig
     422             : #if defined(__SCALAPACK)
     423             :       TYPE(cp_fm_type)                         :: eigenvectors_new, matrix_new
     424             : #else
     425             :       CHARACTER(LEN=2*default_string_length)   :: message
     426             :       INTEGER                                  :: liwork, lwork, nl
     427             :       INTEGER, DIMENSION(:), POINTER           :: iwork
     428             :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: m
     429             :       REAL(KIND=dp), DIMENSION(:), POINTER     :: work
     430             : #endif
     431             : 
     432      105309 :       CALL timeset(routineN, handle)
     433             : 
     434      105309 :       myinfo = 0
     435             : 
     436      105309 :       n = matrix%matrix_struct%nrow_global
     437      315927 :       ALLOCATE (eig(n))
     438             : 
     439             : #if defined(__SCALAPACK)
     440             :       ! Determine if the input matrix needs to be redistributed before diagonalization.
     441             :       ! Heuristics are used to determine the optimal number of CPUs for diagonalization.
     442             :       ! The redistributed matrix is stored in matrix_new, which is just a pointer
     443             :       ! to the original matrix if no redistribution is required
     444      105309 :       CALL cp_fm_redistribute_start(matrix, eigenvectors, matrix_new, eigenvectors_new)
     445             : 
     446             :       ! Call scalapack on CPUs that hold the new matrix
     447      105309 :       IF (ASSOCIATED(matrix_new%matrix_struct)) THEN
     448       53312 :          IF (PRESENT(info)) THEN
     449        1107 :             CALL cp_fm_syevd_base(matrix_new, eigenvectors_new, eig, myinfo)
     450             :          ELSE
     451       52205 :             CALL cp_fm_syevd_base(matrix_new, eigenvectors_new, eig)
     452             :          END IF
     453             :       END IF
     454             :       ! Redistribute results and clean up
     455      105309 :       CALL cp_fm_redistribute_end(matrix, eigenvectors, eig, matrix_new, eigenvectors_new)
     456             : #else
     457             :       ! Retrieve the optimal work array sizes first
     458             :       lwork = -1
     459             :       liwork = -1
     460             :       m => matrix%local_data
     461             :       eig(:) = 0.0_dp
     462             : 
     463             :       ALLOCATE (work(1))
     464             :       work(:) = 0.0_dp
     465             :       ALLOCATE (iwork(1))
     466             :       iwork(:) = 0
     467             :       nl = SIZE(m, 1)
     468             : 
     469             :       CALL dsyevd('V', 'U', n, m(1, 1), nl, eig(1), work(1), lwork, iwork(1), liwork, myinfo)
     470             : 
     471             :       IF (myinfo /= 0) THEN
     472             :          WRITE (message, "(A,I0,A)") "ERROR in DSYEVD: Work space query failed (INFO = ", myinfo, ")"
     473             :          IF (PRESENT(info)) THEN
     474             :             CPWARN(TRIM(message))
     475             :          ELSE
     476             :             CPABORT(TRIM(message))
     477             :          END IF
     478             :       END IF
     479             : 
     480             :       ! Reallocate work arrays and perform diagonalisation
     481             :       lwork = INT(work(1))
     482             :       DEALLOCATE (work)
     483             :       ALLOCATE (work(lwork))
     484             :       work(:) = 0.0_dp
     485             : 
     486             :       liwork = iwork(1)
     487             :       DEALLOCATE (iwork)
     488             :       ALLOCATE (iwork(liwork))
     489             :       iwork(:) = 0
     490             : 
     491             :       CALL dsyevd('V', 'U', n, m(1, 1), nl, eig(1), work(1), lwork, iwork(1), liwork, myinfo)
     492             : 
     493             :       IF (myinfo /= 0) THEN
     494             :          WRITE (message, "(A,I0,A)") "ERROR in DSYEVD: Matrix diagonalization failed (INFO = ", myinfo, ")"
     495             :          IF (PRESENT(info)) THEN
     496             :             CPWARN(TRIM(message))
     497             :          ELSE
     498             :             CPABORT(TRIM(message))
     499             :          END IF
     500             :       END IF
     501             : 
     502             :       CALL cp_fm_to_fm(matrix, eigenvectors)
     503             : 
     504             :       DEALLOCATE (iwork)
     505             :       DEALLOCATE (work)
     506             : #endif
     507             : 
     508      105309 :       IF (PRESENT(info)) info = myinfo
     509             : 
     510      105309 :       nmo = SIZE(eigenvalues, 1)
     511      105309 :       IF (nmo > n) THEN
     512        4744 :          eigenvalues(1:n) = eig(1:n)
     513             :       ELSE
     514      728088 :          eigenvalues(1:nmo) = eig(1:nmo)
     515             :       END IF
     516             : 
     517      105309 :       DEALLOCATE (eig)
     518             : 
     519      105309 :       CALL check_diag(matrix, eigenvectors, n)
     520             : 
     521      105309 :       CALL timestop(handle)
     522             : 
     523      105309 :    END SUBROUTINE cp_fm_syevd
     524             : 
     525             : ! **************************************************************************************************
     526             : !> \brief ...
     527             : !> \param matrix ...
     528             : !> \param eigenvectors ...
     529             : !> \param eigenvalues ...
     530             : !> \param info ...
     531             : ! **************************************************************************************************
     532       53312 :    SUBROUTINE cp_fm_syevd_base(matrix, eigenvectors, eigenvalues, info)
     533             : 
     534             :       TYPE(cp_fm_type), INTENT(IN)          :: matrix, eigenvectors
     535             :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: eigenvalues
     536             :       INTEGER, INTENT(OUT), OPTIONAL           :: info
     537             : 
     538             :       CHARACTER(LEN=*), PARAMETER              :: routineN = 'cp_fm_syevd_base'
     539             : 
     540             :       CHARACTER(LEN=2*default_string_length)   :: message
     541             :       INTEGER                                  :: handle, myinfo
     542             : #if defined(__SCALAPACK)
     543             :       TYPE(cp_blacs_env_type), POINTER         :: context
     544             :       INTEGER                                  :: liwork, lwork, &
     545             :                                                   mypcol, myprow, n
     546             :       INTEGER, DIMENSION(9)                    :: descm, descv
     547       53312 :       INTEGER, DIMENSION(:), POINTER           :: iwork
     548       53312 :       REAL(KIND=dp), DIMENSION(:), POINTER     :: work
     549       53312 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: m, v
     550             : #if defined (__HAS_IEEE_EXCEPTIONS)
     551             :       LOGICAL, DIMENSION(5)                    :: halt
     552             : #endif
     553             : #endif
     554             : 
     555       53312 :       CALL timeset(routineN, handle)
     556             : 
     557       53312 :       myinfo = 0
     558             : 
     559             : #if defined(__SCALAPACK)
     560             : 
     561       53312 :       n = matrix%matrix_struct%nrow_global
     562       53312 :       m => matrix%local_data
     563       53312 :       context => matrix%matrix_struct%context
     564       53312 :       myprow = context%mepos(1)
     565       53312 :       mypcol = context%mepos(2)
     566      533120 :       descm(:) = matrix%matrix_struct%descriptor(:)
     567             : 
     568       53312 :       v => eigenvectors%local_data
     569      533120 :       descv(:) = eigenvectors%matrix_struct%descriptor(:)
     570             : 
     571       53312 :       liwork = 7*n + 8*context%num_pe(2) + 2
     572      159936 :       ALLOCATE (iwork(liwork))
     573             : 
     574             :       ! Work space query
     575       53312 :       lwork = -1
     576       53312 :       ALLOCATE (work(1))
     577             : 
     578             :       CALL pdsyevd('V', 'U', n, m(1, 1), 1, 1, descm, eigenvalues(1), v(1, 1), 1, 1, descv, &
     579       53312 :                    work(1), lwork, iwork(1), liwork, myinfo)
     580             : 
     581       53312 :       IF (matrix%matrix_struct%para_env%is_source() .AND. (myinfo /= 0)) THEN
     582           0 :          WRITE (message, "(A,I0,A)") "ERROR in PDSYEVD: Work space query failed (INFO = ", myinfo, ")"
     583           0 :          IF (PRESENT(info)) THEN
     584           0 :             CPWARN(TRIM(message))
     585             :          ELSE
     586           0 :             CPABORT(TRIM(message))
     587             :          END IF
     588             :       END IF
     589             : 
     590             :       ! look here for a PDORMTR warning :-)
     591             :       ! this routine seems to need more workspace than reported
     592             :       ! (? lapack bug ...)
     593             :       ! arbitrary additional memory  ... we give 100000 more words
     594             :       ! (it seems to depend on the block size used)
     595             : 
     596       53312 :       lwork = NINT(work(1) + 100000)
     597             :       ! lwork = NINT(work(1))
     598       53312 :       DEALLOCATE (work)
     599      159936 :       ALLOCATE (work(lwork))
     600             : 
     601             :       ! Scalapack takes advantage of IEEE754 exceptions for speedup.
     602             :       ! Therefore, we disable floating point traps temporarily.
     603             : #if defined (__HAS_IEEE_EXCEPTIONS)
     604             :       CALL ieee_get_halting_mode(IEEE_ALL, halt)
     605             :       CALL ieee_set_halting_mode(IEEE_ALL, .FALSE.)
     606             : #endif
     607             : 
     608             :       CALL pdsyevd('V', 'U', n, m(1, 1), 1, 1, descm, eigenvalues(1), v(1, 1), 1, 1, descv, &
     609       53312 :                    work(1), lwork, iwork(1), liwork, myinfo)
     610             : 
     611             : #if defined (__HAS_IEEE_EXCEPTIONS)
     612             :       CALL ieee_set_halting_mode(IEEE_ALL, halt)
     613             : #endif
     614       53312 :       IF (matrix%matrix_struct%para_env%is_source() .AND. (myinfo /= 0)) THEN
     615           0 :          WRITE (message, "(A,I0,A)") "ERROR in PDSYEVD: Matrix diagonalization failed (INFO = ", myinfo, ")"
     616           0 :          IF (PRESENT(info)) THEN
     617           0 :             CPWARN(TRIM(message))
     618             :          ELSE
     619           0 :             CPABORT(TRIM(message))
     620             :          END IF
     621             :       END IF
     622             : 
     623       53312 :       IF (PRESENT(info)) info = myinfo
     624             : 
     625       53312 :       DEALLOCATE (work)
     626       53312 :       DEALLOCATE (iwork)
     627             : #else
     628             :       MARK_USED(matrix)
     629             :       MARK_USED(eigenvectors)
     630             :       MARK_USED(eigenvalues)
     631             :       myinfo = -1
     632             :       IF (PRESENT(info)) info = myinfo
     633             :       message = "ERROR in "//TRIM(routineN)// &
     634             :                 ": Matrix diagonalization using PDSYEVD requested without ScaLAPACK support"
     635             :       CPABORT(TRIM(message))
     636             : #endif
     637             : 
     638       53312 :       CALL timestop(handle)
     639             : 
     640       53312 :    END SUBROUTINE cp_fm_syevd_base
     641             : 
     642             : ! **************************************************************************************************
     643             : !> \brief   compute eigenvalues and optionally eigenvectors of a real symmetric matrix using scalapack.
     644             : !>          If eigenvectors are required this routine will replicate a full matrix on each CPU...
     645             : !>          if more than a handful of vectors are needed, use cp_fm_syevd instead
     646             : !> \param matrix ...
     647             : !> \param eigenvectors ...
     648             : !> \param eigenvalues ...
     649             : !> \param neig ...
     650             : !> \param work_syevx ...
     651             : !> \par     matrix is supposed to be in upper triangular form, and overwritten by this routine
     652             : !>          neig   is the number of vectors needed (default all)
     653             : !>          work_syevx evec calculation only, is the fraction of the working buffer allowed (1.0 use full buffer)
     654             : !>                     reducing this saves time, but might cause the routine to fail
     655             : ! **************************************************************************************************
     656          40 :    SUBROUTINE cp_fm_syevx(matrix, eigenvectors, eigenvalues, neig, work_syevx)
     657             : 
     658             :       ! Diagonalise the symmetric n by n matrix using the LAPACK library.
     659             : 
     660             :       TYPE(cp_fm_type), INTENT(IN)              :: matrix
     661             :       TYPE(cp_fm_type), OPTIONAL, INTENT(IN)    :: eigenvectors
     662             :       REAL(KIND=dp), OPTIONAL, INTENT(IN)          :: work_syevx
     663             :       INTEGER, INTENT(IN), OPTIONAL                :: neig
     664             :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)     :: eigenvalues
     665             : 
     666             :       CHARACTER(LEN=*), PARAMETER                  :: routineN = "cp_fm_syevx"
     667             : 
     668             : #if defined(__SCALAPACK)
     669             :       REAL(KIND=dp), PARAMETER                     :: orfac = -1.0_dp
     670             : #endif
     671             :       REAL(KIND=dp), PARAMETER                     :: vl = 0.0_dp, &
     672             :                                                       vu = 0.0_dp
     673             : 
     674             :       TYPE(cp_blacs_env_type), POINTER             :: context
     675             :       TYPE(cp_logger_type), POINTER                :: logger
     676             :       CHARACTER(LEN=1)                             :: job_type
     677             :       REAL(KIND=dp)                                :: abstol, work_syevx_local
     678             :       INTEGER                                      :: handle, info, &
     679             :                                                       liwork, lwork, m, mypcol, &
     680             :                                                       myprow, n, nb, npcol, &
     681             :                                                       nprow, output_unit, &
     682             :                                                       neig_local
     683             :       LOGICAL                                      :: ionode, needs_evecs
     684          40 :       INTEGER, DIMENSION(:), ALLOCATABLE           :: ifail, iwork
     685          40 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE     :: w, work
     686          40 :       REAL(KIND=dp), DIMENSION(:, :), POINTER      :: a, z
     687             : 
     688             :       REAL(KIND=dp), EXTERNAL                      :: dlamch
     689             : 
     690             : #if defined(__SCALAPACK)
     691             :       INTEGER                                      :: nn, np0, npe, nq0, nz
     692             :       INTEGER, DIMENSION(9)                        :: desca, descz
     693          40 :       INTEGER, DIMENSION(:), ALLOCATABLE           :: iclustr
     694          40 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE     :: gap
     695             :       INTEGER, EXTERNAL                            :: iceil, numroc
     696             : #if defined (__HAS_IEEE_EXCEPTIONS)
     697             :       LOGICAL, DIMENSION(5)                        :: halt
     698             : #endif
     699             : #else
     700             :       INTEGER                                      :: nla, nlz
     701             :       INTEGER, EXTERNAL                            :: ilaenv
     702             : #endif
     703             : 
     704             :       ! by default all
     705          40 :       n = matrix%matrix_struct%nrow_global
     706          40 :       neig_local = n
     707          40 :       IF (PRESENT(neig)) neig_local = neig
     708          40 :       IF (neig_local == 0) RETURN
     709             : 
     710          40 :       CALL timeset(routineN, handle)
     711             : 
     712          40 :       needs_evecs = PRESENT(eigenvectors)
     713             : 
     714          40 :       logger => cp_get_default_logger()
     715          40 :       ionode = logger%para_env%is_source()
     716          40 :       n = matrix%matrix_struct%nrow_global
     717             : 
     718             :       ! by default allocate all needed space
     719          40 :       work_syevx_local = 1.0_dp
     720          40 :       IF (PRESENT(work_syevx)) work_syevx_local = work_syevx
     721             : 
     722             :       ! set scalapack job type
     723          40 :       IF (needs_evecs) THEN
     724          40 :          job_type = "V"
     725             :       ELSE
     726           0 :          job_type = "N"
     727             :       END IF
     728             : 
     729             :       ! target the most accurate calculation of the eigenvalues
     730          40 :       abstol = 2.0_dp*dlamch("S")
     731             : 
     732          40 :       context => matrix%matrix_struct%context
     733          40 :       myprow = context%mepos(1)
     734          40 :       mypcol = context%mepos(2)
     735          40 :       nprow = context%num_pe(1)
     736          40 :       npcol = context%num_pe(2)
     737             : 
     738         120 :       ALLOCATE (w(n))
     739         560 :       eigenvalues(:) = 0.0_dp
     740             : #if defined(__SCALAPACK)
     741             : 
     742          40 :       IF (matrix%matrix_struct%nrow_block /= matrix%matrix_struct%ncol_block) THEN
     743           0 :          CPABORT("ERROR in "//routineN//": Invalid blocksize (no square blocks) found")
     744             :       END IF
     745             : 
     746          40 :       a => matrix%local_data
     747         400 :       desca(:) = matrix%matrix_struct%descriptor(:)
     748             : 
     749          40 :       IF (needs_evecs) THEN
     750          40 :          z => eigenvectors%local_data
     751         400 :          descz(:) = eigenvectors%matrix_struct%descriptor(:)
     752             :       ELSE
     753             :          ! z will not be referenced
     754           0 :          z => matrix%local_data
     755           0 :          descz = desca
     756             :       END IF
     757             : 
     758             :       ! Get the optimal work storage size
     759             : 
     760          40 :       npe = nprow*npcol
     761          40 :       nb = matrix%matrix_struct%nrow_block
     762          40 :       nn = MAX(n, nb, 2)
     763          40 :       np0 = numroc(nn, nb, 0, 0, nprow)
     764          40 :       nq0 = MAX(numroc(nn, nb, 0, 0, npcol), nb)
     765             : 
     766          40 :       IF (needs_evecs) THEN
     767             :          lwork = 5*n + MAX(5*nn, np0*nq0) + iceil(neig_local, npe)*nn + 2*nb*nb + &
     768          40 :                  INT(work_syevx_local*REAL((neig_local - 1)*n, dp)) !!!! allocates a full matrix on every CPU !!!!!
     769             :       ELSE
     770           0 :          lwork = 5*n + MAX(5*nn, nb*(np0 + 1))
     771             :       END IF
     772          40 :       liwork = 6*MAX(N, npe + 1, 4)
     773             : 
     774         120 :       ALLOCATE (gap(npe))
     775         120 :       gap = 0.0_dp
     776         120 :       ALLOCATE (iclustr(2*npe))
     777         200 :       iclustr = 0
     778         120 :       ALLOCATE (ifail(n))
     779         560 :       ifail = 0
     780         120 :       ALLOCATE (iwork(liwork))
     781         120 :       ALLOCATE (work(lwork))
     782             : 
     783             :       ! Scalapack takes advantage of IEEE754 exceptions for speedup.
     784             :       ! Therefore, we disable floating point traps temporarily.
     785             : #if defined (__HAS_IEEE_EXCEPTIONS)
     786             :       CALL ieee_get_halting_mode(IEEE_ALL, halt)
     787             :       CALL ieee_set_halting_mode(IEEE_ALL, .FALSE.)
     788             : #endif
     789             : 
     790             :       CALL pdsyevx(job_type, "I", "U", n, a(1, 1), 1, 1, desca, vl, vu, 1, neig_local, abstol, m, nz, w(1), orfac, &
     791          40 :                    z(1, 1), 1, 1, descz, work(1), lwork, iwork(1), liwork, ifail(1), iclustr(1), gap, info)
     792             : 
     793             : #if defined (__HAS_IEEE_EXCEPTIONS)
     794             :       CALL ieee_set_halting_mode(IEEE_ALL, halt)
     795             : #endif
     796             : 
     797             :       ! Error handling
     798             : 
     799          40 :       IF (info /= 0) THEN
     800           0 :          IF (ionode) THEN
     801           0 :             output_unit = cp_logger_get_unit_nr(logger, local=.FALSE.)
     802             :             WRITE (unit=output_unit, FMT="(/,(T3,A,T12,1X,I10))") &
     803           0 :                "info    = ", info, &
     804           0 :                "lwork   = ", lwork, &
     805           0 :                "liwork  = ", liwork, &
     806           0 :                "nz      = ", nz
     807           0 :             IF (info > 0) THEN
     808             :                WRITE (unit=output_unit, FMT="(/,T3,A,(T12,6(1X,I10)))") &
     809           0 :                   "ifail   = ", ifail
     810             :                WRITE (unit=output_unit, FMT="(/,T3,A,(T12,6(1X,I10)))") &
     811           0 :                   "iclustr = ", iclustr
     812             :                WRITE (unit=output_unit, FMT="(/,T3,A,(T12,6(1X,E10.3)))") &
     813           0 :                   "gap     = ", gap
     814             :             END IF
     815             :          END IF
     816           0 :          CPABORT("ERROR in PDSYEVX (ScaLAPACK)")
     817             :       END IF
     818             : 
     819             :       ! Release work storage
     820             : 
     821          40 :       DEALLOCATE (gap)
     822          40 :       DEALLOCATE (iclustr)
     823          40 :       DEALLOCATE (ifail)
     824          40 :       DEALLOCATE (iwork)
     825          40 :       DEALLOCATE (work)
     826             : 
     827             : #else
     828             : 
     829             :       a => matrix%local_data
     830             :       IF (needs_evecs) THEN
     831             :          z => eigenvectors%local_data
     832             :       ELSE
     833             :          ! z will not be referenced
     834             :          z => matrix%local_data
     835             :       END IF
     836             : 
     837             :       ! Get the optimal work storage size
     838             : 
     839             :       nb = MAX(ilaenv(1, "DSYTRD", "U", n, -1, -1, -1), &
     840             :                ilaenv(1, "DORMTR", "U", n, -1, -1, -1))
     841             : 
     842             :       lwork = MAX((nb + 3)*n, 8*n) + n ! sun bug fix
     843             :       liwork = 5*n
     844             : 
     845             :       ALLOCATE (ifail(n))
     846             :       ifail = 0
     847             :       ALLOCATE (iwork(liwork))
     848             :       ALLOCATE (work(lwork))
     849             :       info = 0
     850             :       nla = SIZE(a, 1)
     851             :       nlz = SIZE(z, 1)
     852             : 
     853             :       CALL dsyevx(job_type, "I", "U", n, a(1, 1), nla, vl, vu, 1, neig_local, &
     854             :                   abstol, m, w, z(1, 1), nlz, work(1), lwork, iwork(1), ifail(1), info)
     855             : 
     856             :       ! Error handling
     857             : 
     858             :       IF (info /= 0) THEN
     859             :          output_unit = cp_logger_get_unit_nr(logger, local=.FALSE.)
     860             :          WRITE (unit=output_unit, FMT="(/,(T3,A,T12,1X,I10))") &
     861             :             "info    = ", info
     862             :          IF (info > 0) THEN
     863             :             WRITE (unit=output_unit, FMT="(/,T3,A,(T12,6(1X,I10)))") &
     864             :                "ifail   = ", ifail
     865             :          END IF
     866             :          CPABORT("Error in DSYEVX (ScaLAPACK)")
     867             :       END IF
     868             : 
     869             :       ! Release work storage
     870             : 
     871             :       DEALLOCATE (ifail)
     872             :       DEALLOCATE (iwork)
     873             :       DEALLOCATE (work)
     874             : 
     875             : #endif
     876         400 :       eigenvalues(1:neig_local) = w(1:neig_local)
     877          40 :       DEALLOCATE (w)
     878             : 
     879          40 :       IF (needs_evecs) CALL check_diag(matrix, eigenvectors, neig_local)
     880             : 
     881          40 :       CALL timestop(handle)
     882             : 
     883         120 :    END SUBROUTINE cp_fm_syevx
     884             : 
     885             : ! **************************************************************************************************
     886             : !> \brief ...
     887             : !> \param matrix ...
     888             : !> \param work ...
     889             : !> \param exponent ...
     890             : !> \param threshold ...
     891             : !> \param n_dependent ...
     892             : !> \param verbose ...
     893             : !> \param eigvals ...
     894             : ! **************************************************************************************************
     895        1100 :    SUBROUTINE cp_fm_power(matrix, work, exponent, threshold, n_dependent, verbose, eigvals)
     896             : 
     897             :       ! Raise the real symmetric n by n matrix to the power given by
     898             :       ! the exponent. All eigenvectors with a corresponding eigenvalue lower
     899             :       ! than threshold are quenched. result in matrix
     900             : 
     901             :       ! - Creation (29.03.1999, Matthias Krack)
     902             :       ! - Parallelised using BLACS and ScaLAPACK (06.06.2001,MK)
     903             : 
     904             :       TYPE(cp_fm_type), INTENT(IN)            :: matrix, work
     905             :       REAL(KIND=dp), INTENT(IN)                  :: exponent, threshold
     906             :       INTEGER, INTENT(OUT)                       :: n_dependent
     907             :       LOGICAL, INTENT(IN), OPTIONAL              :: verbose
     908             :       REAL(KIND=dp), DIMENSION(2), INTENT(OUT), &
     909             :          OPTIONAL                                :: eigvals
     910             : 
     911             :       CHARACTER(LEN=*), PARAMETER                :: routineN = 'cp_fm_power'
     912             : 
     913             :       INTEGER                                    :: handle, icol_global, &
     914             :                                                     mypcol, myprow, &
     915             :                                                     ncol_global, npcol, nprow, &
     916             :                                                     nrow_global
     917             :       LOGICAL                                    :: my_verbose
     918             :       REAL(KIND=dp)                              :: condition_number, f, p
     919             :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE   :: eigenvalues
     920        1100 :       REAL(KIND=dp), DIMENSION(:, :), POINTER    :: eigenvectors
     921             :       TYPE(cp_blacs_env_type), POINTER           :: context
     922             : 
     923             : #if defined(__SCALAPACK)
     924             :       INTEGER           :: icol_local, ipcol, iprow, irow_global, &
     925             :                            irow_local, ncol_block, nrow_block
     926             :       INTEGER, EXTERNAL :: indxg2l, indxg2p
     927             : #endif
     928             : 
     929        1100 :       CALL timeset(routineN, handle)
     930             : 
     931        1100 :       my_verbose = .FALSE.
     932        1100 :       IF (PRESENT(verbose)) my_verbose = verbose
     933             : 
     934        1100 :       context => matrix%matrix_struct%context
     935        1100 :       myprow = context%mepos(1)
     936        1100 :       mypcol = context%mepos(2)
     937        1100 :       nprow = context%num_pe(1)
     938        1100 :       npcol = context%num_pe(2)
     939        1100 :       n_dependent = 0
     940        1100 :       p = 0.5_dp*exponent
     941             : 
     942        1100 :       nrow_global = matrix%matrix_struct%nrow_global
     943        1100 :       ncol_global = matrix%matrix_struct%ncol_global
     944             : 
     945        3300 :       ALLOCATE (eigenvalues(ncol_global))
     946             : 
     947       23931 :       eigenvalues(:) = 0.0_dp
     948             : 
     949             :       ! Compute the eigenvectors and eigenvalues
     950             : 
     951        1100 :       CALL choose_eigv_solver(matrix, work, eigenvalues)
     952             : 
     953        1100 :       IF (PRESENT(eigvals)) THEN
     954         330 :          eigvals(1) = eigenvalues(1)
     955         330 :          eigvals(2) = eigenvalues(ncol_global)
     956             :       END IF
     957             : 
     958             : #if defined(__SCALAPACK)
     959        1100 :       nrow_block = work%matrix_struct%nrow_block
     960        1100 :       ncol_block = work%matrix_struct%ncol_block
     961             : 
     962        1100 :       eigenvectors => work%local_data
     963             : 
     964             :       ! Build matrix**exponent with eigenvector quenching
     965             : 
     966       23931 :       DO icol_global = 1, ncol_global
     967             : 
     968       23931 :          IF (eigenvalues(icol_global) < threshold) THEN
     969             : 
     970          82 :             n_dependent = n_dependent + 1
     971             : 
     972             :             ipcol = indxg2p(icol_global, ncol_block, mypcol, &
     973          82 :                             work%matrix_struct%first_p_pos(2), npcol)
     974             : 
     975          82 :             IF (mypcol == ipcol) THEN
     976             :                icol_local = indxg2l(icol_global, ncol_block, mypcol, &
     977          82 :                                     work%matrix_struct%first_p_pos(2), npcol)
     978        4714 :                DO irow_global = 1, nrow_global
     979             :                   iprow = indxg2p(irow_global, nrow_block, myprow, &
     980        4632 :                                   work%matrix_struct%first_p_pos(1), nprow)
     981        4714 :                   IF (myprow == iprow) THEN
     982             :                      irow_local = indxg2l(irow_global, nrow_block, myprow, &
     983        2316 :                                           work%matrix_struct%first_p_pos(1), nprow)
     984        2316 :                      eigenvectors(irow_local, icol_local) = 0.0_dp
     985             :                   END IF
     986             :                END DO
     987             :             END IF
     988             : 
     989             :          ELSE
     990             : 
     991       22749 :             f = eigenvalues(icol_global)**p
     992             : 
     993             :             ipcol = indxg2p(icol_global, ncol_block, mypcol, &
     994       22749 :                             work%matrix_struct%first_p_pos(2), npcol)
     995             : 
     996       22749 :             IF (mypcol == ipcol) THEN
     997             :                icol_local = indxg2l(icol_global, ncol_block, mypcol, &
     998       22749 :                                     work%matrix_struct%first_p_pos(2), npcol)
     999     1557072 :                DO irow_global = 1, nrow_global
    1000             :                   iprow = indxg2p(irow_global, nrow_block, myprow, &
    1001     1534323 :                                   work%matrix_struct%first_p_pos(1), nprow)
    1002     1557072 :                   IF (myprow == iprow) THEN
    1003             :                      irow_local = indxg2l(irow_global, nrow_block, myprow, &
    1004      811236 :                                           work%matrix_struct%first_p_pos(1), nprow)
    1005             :                      eigenvectors(irow_local, icol_local) = &
    1006      811236 :                         f*eigenvectors(irow_local, icol_local)
    1007             :                   END IF
    1008             :                END DO
    1009             :             END IF
    1010             : 
    1011             :          END IF
    1012             : 
    1013             :       END DO
    1014             : 
    1015             : #else
    1016             : 
    1017             :       eigenvectors => work%local_data
    1018             : 
    1019             :       ! Build matrix**exponent with eigenvector quenching
    1020             : 
    1021             :       DO icol_global = 1, ncol_global
    1022             : 
    1023             :          IF (eigenvalues(icol_global) < threshold) THEN
    1024             : 
    1025             :             n_dependent = n_dependent + 1
    1026             :             eigenvectors(1:nrow_global, icol_global) = 0.0_dp
    1027             : 
    1028             :          ELSE
    1029             : 
    1030             :             f = eigenvalues(icol_global)**p
    1031             :             eigenvectors(1:nrow_global, icol_global) = &
    1032             :                f*eigenvectors(1:nrow_global, icol_global)
    1033             : 
    1034             :          END IF
    1035             : 
    1036             :       END DO
    1037             : 
    1038             : #endif
    1039        1100 :       CALL cp_fm_syrk("U", "N", ncol_global, 1.0_dp, work, 1, 1, 0.0_dp, matrix)
    1040        1100 :       CALL cp_fm_upper_to_full(matrix, work)
    1041             : 
    1042             :       ! Print some warnings/notes
    1043        1100 :       IF (matrix%matrix_struct%para_env%is_source() .AND. my_verbose) THEN
    1044           0 :          condition_number = ABS(eigenvalues(ncol_global)/eigenvalues(1))
    1045             :          WRITE (UNIT=cp_logger_get_default_unit_nr(), FMT="(/,(T2,A,ES15.6))") &
    1046           0 :             "CP_FM_POWER: smallest eigenvalue:", eigenvalues(1), &
    1047           0 :             "CP_FM_POWER: largest eigenvalue: ", eigenvalues(ncol_global), &
    1048           0 :             "CP_FM_POWER: condition number:   ", condition_number
    1049           0 :          IF (eigenvalues(1) <= 0.0_dp) THEN
    1050             :             WRITE (UNIT=cp_logger_get_default_unit_nr(), FMT="(/,T2,A)") &
    1051           0 :                "WARNING: matrix has a negative eigenvalue, tighten EPS_DEFAULT"
    1052             :          END IF
    1053           0 :          IF (condition_number > 1.0E12_dp) THEN
    1054             :             WRITE (UNIT=cp_logger_get_default_unit_nr(), FMT="(/,T2,A)") &
    1055           0 :                "WARNING: high condition number => possibly ill-conditioned matrix"
    1056             :          END IF
    1057             :       END IF
    1058             : 
    1059        1100 :       DEALLOCATE (eigenvalues)
    1060             : 
    1061        1100 :       CALL timestop(handle)
    1062             : 
    1063        1100 :    END SUBROUTINE cp_fm_power
    1064             : 
    1065             : ! **************************************************************************************************
    1066             : !> \brief ...
    1067             : !> \param matrix ...
    1068             : !> \param eigenvectors ...
    1069             : !> \param eigval ...
    1070             : !> \param thresh ...
    1071             : !> \param start_sec_block ...
    1072             : ! **************************************************************************************************
    1073          18 :    SUBROUTINE cp_fm_block_jacobi(matrix, eigenvectors, eigval, thresh, &
    1074             :                                  start_sec_block)
    1075             : 
    1076             :       ! Calculates block diagonalization of a full symmetric matrix
    1077             :       ! It has its origin in cp_fm_syevx. This routine rotates only elements
    1078             :       ! which are larger than a threshold values "thresh".
    1079             :       ! start_sec_block is the start of the second block.
    1080             :       ! IT DOES ONLY ONE SWEEP!
    1081             : 
    1082             :       ! - Creation (07.10.2002, Martin Fengler)
    1083             :       ! - Cosmetics (05.04.06, MK)
    1084             : 
    1085             :       TYPE(cp_fm_type), INTENT(IN)           :: eigenvectors, matrix
    1086             :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)   :: eigval
    1087             :       INTEGER, INTENT(IN)                       :: start_sec_block
    1088             :       REAL(KIND=dp), INTENT(IN)                 :: thresh
    1089             : 
    1090             :       CHARACTER(len=*), PARAMETER               :: routineN = 'cp_fm_block_jacobi'
    1091             : 
    1092             :       INTEGER :: handle
    1093             :       REAL(KIND=dp), DIMENSION(:, :), POINTER    :: a, ev
    1094             : 
    1095             :       REAL(KIND=dp) :: tan_theta, tau, c, s
    1096             :       INTEGER  :: q, p, N
    1097          18 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: c_ip
    1098             : 
    1099             : #if defined(__SCALAPACK)
    1100             :       TYPE(cp_blacs_env_type), POINTER :: context
    1101             : 
    1102             :       INTEGER :: myprow, mypcol, nprow, npcol, block_dim_row, block_dim_col, &
    1103             :                  info, ev_row_block_size, iam, mynumrows, mype, npe, &
    1104             :                  q_loc
    1105          18 :       REAL(KIND=dp), DIMENSION(:, :), ALLOCATABLE :: a_loc, ev_loc
    1106             :       INTEGER, DIMENSION(9)                        :: desca, descz, desc_a_block, &
    1107             :                                                       desc_ev_loc
    1108             :       TYPE(mp_comm_type):: allgrp
    1109             :       TYPE(cp_blacs_type) :: ictxt_loc
    1110             :       INTEGER, EXTERNAL :: numroc, indxl2g, indxg2l
    1111             : #endif
    1112             : 
    1113             :       ! -------------------------------------------------------------------------
    1114             : 
    1115          18 :       CALL timeset(routineN, handle)
    1116             : 
    1117             : #if defined(__SCALAPACK)
    1118          18 :       context => matrix%matrix_struct%context
    1119          18 :       allgrp = matrix%matrix_struct%para_env
    1120             : 
    1121          18 :       myprow = context%mepos(1)
    1122          18 :       mypcol = context%mepos(2)
    1123          18 :       nprow = context%num_pe(1)
    1124          18 :       npcol = context%num_pe(2)
    1125             : 
    1126          18 :       N = matrix%matrix_struct%nrow_global
    1127             : 
    1128          18 :       A => matrix%local_data
    1129         180 :       desca(:) = matrix%matrix_struct%descriptor(:)
    1130          18 :       EV => eigenvectors%local_data
    1131         180 :       descz(:) = eigenvectors%matrix_struct%descriptor(:)
    1132             : 
    1133             :       ! Copy the block to be rotated to the master process firstly and broadcast to all processes
    1134             :       ! start_sec_block defines where the second block starts!
    1135             :       ! Block will be processed together with the OO block
    1136             : 
    1137          18 :       block_dim_row = start_sec_block - 1
    1138          18 :       block_dim_col = N - block_dim_row
    1139          72 :       ALLOCATE (A_loc(block_dim_row, block_dim_col))
    1140             : 
    1141          18 :       mype = matrix%matrix_struct%para_env%mepos
    1142          18 :       npe = matrix%matrix_struct%para_env%num_pe
    1143             :       ! Get a new context
    1144          18 :       CALL ictxt_loc%gridinit(matrix%matrix_struct%para_env, 'Row-major', NPROW*NPCOL, 1)
    1145             : 
    1146             :       CALL descinit(desc_a_block, block_dim_row, block_dim_col, block_dim_row, &
    1147          18 :                     block_dim_col, 0, 0, ictxt_loc%get_handle(), block_dim_row, info)
    1148             : 
    1149             :       CALL pdgemr2d(block_dim_row, block_dim_col, A, 1, start_sec_block, desca, &
    1150          18 :                     A_loc, 1, 1, desc_a_block, context%get_handle())
    1151             :       ! Only the master (root) process received data yet
    1152          18 :       CALL allgrp%bcast(A_loc, 0)
    1153             : 
    1154             :       ! Since each process owns now the upper block, the eigenvectors can be re-sorted in such a way that
    1155             :       ! each process has a NN*1 grid, i.e. the process owns a bunch of rows which can be modified locally
    1156             : 
    1157             :       ! Initialize distribution of the eigenvectors
    1158          18 :       iam = mype
    1159          18 :       ev_row_block_size = n/(nprow*npcol)
    1160          18 :       mynumrows = NUMROC(N, ev_row_block_size, iam, 0, NPROW*NPCOL)
    1161             : 
    1162         108 :       ALLOCATE (EV_loc(mynumrows, N), c_ip(mynumrows))
    1163             : 
    1164             :       CALL descinit(desc_ev_loc, N, N, ev_row_block_size, N, 0, 0, ictxt_loc%get_handle(), &
    1165          18 :                     mynumrows, info)
    1166             : 
    1167          18 :       CALL pdgemr2d(N, N, EV, 1, 1, descz, EV_loc, 1, 1, desc_ev_loc, context%get_handle())
    1168             : 
    1169             :       ! Start block diagonalization of matrix
    1170             : 
    1171          18 :       q_loc = 0
    1172             : 
    1173        1170 :       DO q = start_sec_block, N
    1174        1152 :          q_loc = q_loc + 1
    1175      148626 :          DO p = 1, (start_sec_block - 1)
    1176             : 
    1177      148608 :             IF (ABS(A_loc(p, q_loc)) > thresh) THEN
    1178             : 
    1179      117566 :                tau = (eigval(q) - eigval(p))/(2.0_dp*A_loc(p, q_loc))
    1180             : 
    1181      117566 :                tan_theta = SIGN(1.0_dp, tau)/(ABS(tau) + SQRT(1.0_dp + tau*tau))
    1182             : 
    1183             :                ! Cos(theta)
    1184      117566 :                c = 1.0_dp/SQRT(1.0_dp + tan_theta*tan_theta)
    1185      117566 :                s = tan_theta*c
    1186             : 
    1187             :                ! Calculate eigenvectors: Q*J
    1188             :                ! c_ip = c*EV_loc(:,p) - s*EV_loc(:,q)
    1189             :                ! c_iq = s*EV_loc(:,p) + c*EV_loc(:,q)
    1190             :                ! EV(:,p) = c_ip
    1191             :                ! EV(:,q) = c_iq
    1192      117566 :                CALL dcopy(mynumrows, EV_loc(1, p), 1, c_ip(1), 1)
    1193      117566 :                CALL dscal(mynumrows, c, EV_loc(1, p), 1)
    1194      117566 :                CALL daxpy(mynumrows, -s, EV_loc(1, q), 1, EV_loc(1, p), 1)
    1195      117566 :                CALL dscal(mynumrows, c, EV_loc(1, q), 1)
    1196      117566 :                CALL daxpy(mynumrows, s, c_ip(1), 1, EV_loc(1, q), 1)
    1197             : 
    1198             :             END IF
    1199             : 
    1200             :          END DO
    1201             :       END DO
    1202             : 
    1203             :       ! Copy eigenvectors back to the original distribution
    1204          18 :       CALL pdgemr2d(N, N, EV_loc, 1, 1, desc_ev_loc, EV, 1, 1, descz, context%get_handle())
    1205             : 
    1206             :       ! Release work storage
    1207          18 :       DEALLOCATE (A_loc, EV_loc, c_ip)
    1208             : 
    1209          18 :       CALL ictxt_loc%gridexit()
    1210             : 
    1211             : #else
    1212             : 
    1213             :       N = matrix%matrix_struct%nrow_global
    1214             : 
    1215             :       ALLOCATE (c_ip(N)) ! Local eigenvalue vector
    1216             : 
    1217             :       A => matrix%local_data ! Contains the Matrix to be worked on
    1218             :       EV => eigenvectors%local_data ! Contains the eigenvectors up to blocksize, rest is garbage
    1219             : 
    1220             :       ! Start matrix diagonalization
    1221             : 
    1222             :       tan_theta = 0.0_dp
    1223             :       tau = 0.0_dp
    1224             : 
    1225             :       DO q = start_sec_block, N
    1226             :          DO p = 1, (start_sec_block - 1)
    1227             : 
    1228             :             IF (ABS(A(p, q)) > thresh) THEN
    1229             : 
    1230             :                tau = (eigval(q) - eigval(p))/(2.0_dp*A(p, q))
    1231             : 
    1232             :                tan_theta = SIGN(1.0_dp, tau)/(ABS(tau) + SQRT(1.0_dp + tau*tau))
    1233             : 
    1234             :                ! Cos(theta)
    1235             :                c = 1.0_dp/SQRT(1.0_dp + tan_theta*tan_theta)
    1236             :                s = tan_theta*c
    1237             : 
    1238             :                ! Calculate eigenvectors: Q*J
    1239             :                ! c_ip = c*EV(:,p) - s*EV(:,q)
    1240             :                ! c_iq = s*EV(:,p) + c*EV(:,q)
    1241             :                ! EV(:,p) = c_ip
    1242             :                ! EV(:,q) = c_iq
    1243             :                CALL dcopy(N, EV(1, p), 1, c_ip(1), 1)
    1244             :                CALL dscal(N, c, EV(1, p), 1)
    1245             :                CALL daxpy(N, -s, EV(1, q), 1, EV(1, p), 1)
    1246             :                CALL dscal(N, c, EV(1, q), 1)
    1247             :                CALL daxpy(N, s, c_ip(1), 1, EV(1, q), 1)
    1248             : 
    1249             :             END IF
    1250             : 
    1251             :          END DO
    1252             :       END DO
    1253             : 
    1254             :       ! Release work storage
    1255             : 
    1256             :       DEALLOCATE (c_ip)
    1257             : 
    1258             : #endif
    1259             : 
    1260          18 :       CALL timestop(handle)
    1261             : 
    1262          90 :    END SUBROUTINE cp_fm_block_jacobi
    1263             : 
    1264             : ! **************************************************************************************************
    1265             : !> \brief General Eigenvalue Problem  AX = BXE
    1266             : !>        Single option version: Cholesky decomposition of B
    1267             : !> \param amatrix ...
    1268             : !> \param bmatrix ...
    1269             : !> \param eigenvectors ...
    1270             : !> \param eigenvalues ...
    1271             : !> \param work ...
    1272             : ! **************************************************************************************************
    1273         262 :    SUBROUTINE cp_fm_geeig(amatrix, bmatrix, eigenvectors, eigenvalues, work)
    1274             : 
    1275             :       TYPE(cp_fm_type), INTENT(IN)                       :: amatrix, bmatrix, eigenvectors
    1276             :       REAL(KIND=dp), DIMENSION(:)                        :: eigenvalues
    1277             :       TYPE(cp_fm_type), INTENT(IN)                       :: work
    1278             : 
    1279             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_fm_geeig'
    1280             : 
    1281             :       INTEGER                                            :: handle, nao, nmo
    1282             : 
    1283         262 :       CALL timeset(routineN, handle)
    1284             : 
    1285         262 :       CALL cp_fm_get_info(amatrix, nrow_global=nao)
    1286         262 :       nmo = SIZE(eigenvalues)
    1287             :       ! Cholesky decompose S=U(T)U
    1288         262 :       CALL cp_fm_cholesky_decompose(bmatrix)
    1289             :       ! Invert to get U^(-1)
    1290         262 :       CALL cp_fm_triangular_invert(bmatrix)
    1291             :       ! Reduce to get U^(-T) * H * U^(-1)
    1292         262 :       CALL cp_fm_triangular_multiply(bmatrix, amatrix, side="R")
    1293         262 :       CALL cp_fm_triangular_multiply(bmatrix, amatrix, transpose_tr=.TRUE.)
    1294             :       ! Diagonalize
    1295             :       CALL choose_eigv_solver(matrix=amatrix, eigenvectors=work, &
    1296         262 :                               eigenvalues=eigenvalues)
    1297             :       ! Restore vectors C = U^(-1) * C*
    1298         262 :       CALL cp_fm_triangular_multiply(bmatrix, work)
    1299         262 :       CALL cp_fm_to_fm(work, eigenvectors, nmo)
    1300             : 
    1301         262 :       CALL timestop(handle)
    1302             : 
    1303         262 :    END SUBROUTINE cp_fm_geeig
    1304             : 
    1305             : ! **************************************************************************************************
    1306             : !> \brief General Eigenvalue Problem  AX = BXE
    1307             : !>        Use canonical diagonalization : U*s**(-1/2)
    1308             : !> \param amatrix ...
    1309             : !> \param bmatrix ...
    1310             : !> \param eigenvectors ...
    1311             : !> \param eigenvalues ...
    1312             : !> \param work ...
    1313             : !> \param epseig ...
    1314             : ! **************************************************************************************************
    1315          64 :    SUBROUTINE cp_fm_geeig_canon(amatrix, bmatrix, eigenvectors, eigenvalues, work, epseig)
    1316             : 
    1317             :       TYPE(cp_fm_type), INTENT(IN)                       :: amatrix, bmatrix, eigenvectors
    1318             :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: eigenvalues
    1319             :       TYPE(cp_fm_type), INTENT(IN)                       :: work
    1320             :       REAL(KIND=dp), INTENT(IN)                          :: epseig
    1321             : 
    1322             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_fm_geeig_canon'
    1323             : 
    1324             :       INTEGER                                            :: handle, i, icol, irow, nao, nc, ncol, &
    1325             :                                                             nmo, nx
    1326             :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: seigval
    1327             : 
    1328          64 :       CALL timeset(routineN, handle)
    1329             : 
    1330             :       ! Test sizees
    1331          64 :       CALL cp_fm_get_info(amatrix, nrow_global=nao)
    1332          64 :       nmo = SIZE(eigenvalues)
    1333         192 :       ALLOCATE (seigval(nao))
    1334             : 
    1335             :       ! Diagonalize -S matrix, this way the NULL space is at the end of the spectrum
    1336          64 :       CALL cp_fm_scale(-1.0_dp, bmatrix)
    1337          64 :       CALL choose_eigv_solver(matrix=bmatrix, eigenvectors=work, eigenvalues=seigval)
    1338        4972 :       seigval(:) = -seigval(:)
    1339          64 :       nc = nao
    1340        4680 :       DO i = 1, nao
    1341        4680 :          IF (seigval(i) < epseig) THEN
    1342          40 :             nc = i - 1
    1343          40 :             EXIT
    1344             :          END IF
    1345             :       END DO
    1346          64 :       CPASSERT(nc /= 0)
    1347             : 
    1348          64 :       IF (nc /= nao) THEN
    1349          40 :          IF (nc < nmo) THEN
    1350             :             ! Copy NULL space definition to last vectors of eigenvectors (if needed)
    1351           0 :             ncol = nmo - nc
    1352           0 :             CALL cp_fm_to_fm(work, eigenvectors, ncol, nc + 1, nc + 1)
    1353             :          END IF
    1354             :          ! Set NULL space in eigenvector matrix of S to zero
    1355         332 :          DO icol = nc + 1, nao
    1356       36172 :             DO irow = 1, nao
    1357       36132 :                CALL cp_fm_set_element(work, irow, icol, 0.0_dp)
    1358             :             END DO
    1359             :          END DO
    1360             :          ! Set small eigenvalues to a dummy save value
    1361         332 :          seigval(nc + 1:nao) = 1.0_dp
    1362             :       END IF
    1363             :       ! Calculate U*s**(-1/2)
    1364        4972 :       seigval(:) = 1.0_dp/SQRT(seigval(:))
    1365          64 :       CALL cp_fm_column_scale(work, seigval)
    1366             :       ! Reduce to get U^(-T) * H * U^(-1)
    1367          64 :       CALL cp_fm_gemm("T", "N", nao, nao, nao, 1.0_dp, work, amatrix, 0.0_dp, bmatrix)
    1368          64 :       CALL cp_fm_gemm("N", "N", nao, nao, nao, 1.0_dp, bmatrix, work, 0.0_dp, amatrix)
    1369          64 :       IF (nc /= nao) THEN
    1370             :          ! set diagonal values to save large value
    1371         332 :          DO icol = nc + 1, nao
    1372         332 :             CALL cp_fm_set_element(amatrix, icol, icol, 10000.0_dp)
    1373             :          END DO
    1374             :       END IF
    1375             :       ! Diagonalize
    1376          64 :       CALL choose_eigv_solver(matrix=amatrix, eigenvectors=bmatrix, eigenvalues=eigenvalues)
    1377          64 :       nx = MIN(nc, nmo)
    1378             :       ! Restore vectors C = U^(-1) * C*
    1379          64 :       CALL cp_fm_gemm("N", "N", nao, nx, nc, 1.0_dp, work, bmatrix, 0.0_dp, eigenvectors)
    1380             : 
    1381          64 :       DEALLOCATE (seigval)
    1382             : 
    1383          64 :       CALL timestop(handle)
    1384             : 
    1385          64 :    END SUBROUTINE cp_fm_geeig_canon
    1386             : 
    1387             : END MODULE cp_fm_diag

Generated by: LCOV version 1.15