LCOV - code coverage report
Current view: top level - src/fm - cp_fm_diag.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:24d69ee) Lines: 82.1 % 531 436
Test Date: 2026-09-03 07:32:15 Functions: 94.1 % 17 16

            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 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              :    USE cp_blacs_types, ONLY: cp_blacs_type
      19              :    USE cp_blacs_env, ONLY: cp_blacs_env_type
      20              :    USE cp_fm_basic_linalg, ONLY: cp_fm_column_scale, &
      21              :                                  cp_fm_gemm, &
      22              :                                  cp_fm_scale, &
      23              :                                  cp_fm_syrk, &
      24              :                                  cp_fm_triangular_invert, &
      25              :                                  cp_fm_triangular_multiply, &
      26              :                                  cp_fm_uplo_to_full
      27              :    USE cp_fm_cholesky, ONLY: cp_fm_cholesky_decompose
      28              :    USE cp_fm_diag_utils, ONLY: cp_fm_redistribute_end, &
      29              :                                cp_fm_redistribute_start
      30              :    USE cp_fm_elpa, ONLY: cp_fm_diag_elpa, &
      31              :                          finalize_elpa_library, &
      32              :                          initialize_elpa_library, &
      33              :                          set_elpa_kernel
      34              :    USE cp_cfm_elpa, ONLY: set_elpa_c_kernel
      35              :    USE cp_fm_cusolver_api, ONLY: cp_fm_diag_cusolver, &
      36              :                                  cp_fm_general_cusolver
      37              : #if defined(__DLAF)
      38              :    USE cp_fm_dlaf_api, ONLY: cp_fm_diag_dlaf, cp_fm_diag_gen_dlaf
      39              :    USE cp_dlaf_utils_api, ONLY: cp_dlaf_initialize, cp_dlaf_finalize
      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              :                           cp_fm_create, &
      46              :                           cp_fm_get_info, &
      47              :                           cp_fm_release, &
      48              :                           cp_fm_set_all, &
      49              :                           cp_fm_to_fm, &
      50              :                           cp_fm_to_fm_submat, &
      51              :                           cp_fm_type
      52              :    USE cp_fm_struct, ONLY: cp_fm_struct_equivalent, &
      53              :                            cp_fm_struct_create, &
      54              :                            cp_fm_struct_release, &
      55              :                            cp_fm_struct_type
      56              :    USE cp_log_handling, ONLY: cp_logger_get_default_unit_nr, &
      57              :                               cp_get_default_logger, &
      58              :                               cp_logger_get_default_io_unit, &
      59              :                               cp_logger_type, &
      60              :                               cp_to_string
      61              :    USE cp_log_handling, ONLY: cp_get_default_logger, &
      62              :                               cp_logger_get_default_unit_nr, &
      63              :                               cp_logger_get_unit_nr, &
      64              :                               cp_logger_type
      65              :    USE kinds, ONLY: default_string_length, &
      66              :                     dp
      67              :    USE machine, ONLY: default_output_unit, &
      68              :                       m_memory
      69              :    USE parallel_gemm_api, ONLY: parallel_gemm
      70              : #if defined (__parallel)
      71              :    USE message_passing, ONLY: mp_comm_type
      72              : #endif
      73              : #if defined (__HAS_IEEE_EXCEPTIONS)
      74              :    USE ieee_exceptions, ONLY: ieee_get_halting_mode, &
      75              :                               ieee_set_halting_mode, &
      76              :                               IEEE_ALL
      77              : #endif
      78              : #include "../base/base_uses.f90"
      79              : 
      80              :    IMPLICIT NONE
      81              :    PRIVATE
      82              : 
      83              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_fm_diag'
      84              : 
      85              :    REAL(KIND=dp), PARAMETER, PUBLIC :: eps_check_diag_default = 5.0E-14_dp
      86              : 
      87              :    ! Placeholder eigenvalue for linearly dependent modes removed by canonical orthogonalization.
      88              :    ! Pushes unphysical states above the real spectrum so downstream routines can ignore them.
      89              :    REAL(KIND=dp), PARAMETER, PUBLIC :: set_removed_eigval_to = 10000.0_dp
      90              : 
      91              :    ! The following saved variables are diagonalization global
      92              :    ! Stores the default library for diagonalization
      93              :    INTEGER, SAVE, PUBLIC    :: diag_type = 0
      94              :    ! Whether the diagonalization library was requested explicitly (e.g. via PREFERRED_DIAG_LIBRARY);
      95              :    ! complex matrices use ELPA only in that case.
      96              :    LOGICAL, SAVE, PUBLIC    :: diag_lib_explicit = .FALSE.
      97              :    ! Minimum number of eigenvectors for the use of the ELPA eigensolver.
      98              :    ! The ScaLAPACK eigensolver is used as fallback for all smaller cases.
      99              :    INTEGER, SAVE, PUBLIC    :: elpa_neigvec_min = 0
     100              :    ! Minimum matrix size for the use of the cuSOLVERMp eigensolver.
     101              :    ! Smaller matrices use the ScaLAPACK fallback to avoid GPU launch overheads.
     102              :    INTEGER, PARAMETER, PUBLIC :: cusolver_n_min = 64
     103              :    ! Minimum number of eigenvectors for the use of the DLAF eigensolver.
     104              :    ! The ScaLAPACK eigensolver is used as fallback for all smaller cases.
     105              :    INTEGER, SAVE, PUBLIC    :: dlaf_neigvec_min = 0
     106              :    LOGICAL, SAVE, PUBLIC :: direct_generalized_diagonalization = .FALSE.
     107              :    ! Threshold value for the orthonormality check of the eigenvectors obtained
     108              :    ! after a diagonalization. A negative value disables the check.
     109              :    REAL(KIND=dp), SAVE :: eps_check_diag = -1.0_dp
     110              : 
     111              :    ! Constants for the diag_type above
     112              :    INTEGER, PARAMETER, PUBLIC  :: FM_DIAG_TYPE_SCALAPACK = 101, &
     113              :                                   FM_DIAG_TYPE_ELPA = 102, &
     114              :                                   FM_DIAG_TYPE_CUSOLVER = 103, &
     115              :                                   FM_DIAG_TYPE_DLAF = 104
     116              : #if defined(__CUSOLVERMP)
     117              :    INTEGER, PARAMETER, PUBLIC :: FM_DIAG_TYPE_DEFAULT = FM_DIAG_TYPE_CUSOLVER
     118              : #elif defined(__ELPA)
     119              :    INTEGER, PARAMETER, PUBLIC :: FM_DIAG_TYPE_DEFAULT = FM_DIAG_TYPE_ELPA
     120              : #else
     121              :    INTEGER, PARAMETER, PUBLIC :: FM_DIAG_TYPE_DEFAULT = FM_DIAG_TYPE_SCALAPACK
     122              : #endif
     123              : 
     124              :    ! Public subroutines
     125              :    PUBLIC :: choose_eigv_solver, &
     126              :              cp_fm_block_jacobi, &
     127              :              cp_fm_power, &
     128              :              cp_fm_syevd, &
     129              :              cp_fm_syevx, &
     130              :              cp_fm_svd, &
     131              :              cp_fm_geeig, &
     132              :              cp_fm_geeig_canon, &
     133              :              diag_check_requested, &
     134              :              diag_check_warning_threshold, &
     135              :              diag_init, &
     136              :              diag_finalize
     137              : 
     138              : CONTAINS
     139              : 
     140              : ! **************************************************************************************************
     141              : !> \brief Setup the diagonalization library to be used
     142              : !> \param diag_lib diag_library flag from GLOBAL section in input
     143              : !> \param fallback_applied .TRUE. if support for the requested library was not compiled-in and fallback
     144              : !>                         to ScaLAPACK was applied, .FALSE. otherwise.
     145              : !> \param elpa_kernel integer that determines which ELPA kernel to use for diagonalization
     146              : !> \param elpa_c_kernel ...
     147              : !> \param elpa_neigvec_min_input ...
     148              : !> \param elpa_qr logical that determines if ELPA should try to use QR to accelerate the
     149              : !>                diagonalization procedure of suitably sized matrices
     150              : !> \param elpa_print logical that determines if information about the ELPA diagonalization should
     151              : !>                   be printed
     152              : !> \param elpa_one_stage logical that enables the one-stage solver
     153              : !> \param dlaf_neigvec_min_input ...
     154              : !> \param eps_check_diag_input ...
     155              : !> \param direct_generalized_diagonalization_input ...
     156              : !> \param diag_lib_explicit_input ...
     157              : !> \par History
     158              : !>      - Add support for DLA-Future (05.09.2023, RMeli)
     159              : !> \author  MI 11.2013
     160              : ! **************************************************************************************************
     161        11451 :    SUBROUTINE diag_init(diag_lib, fallback_applied, elpa_kernel, elpa_c_kernel, elpa_neigvec_min_input, &
     162              :                         elpa_qr, elpa_print, elpa_one_stage, dlaf_neigvec_min_input, eps_check_diag_input, &
     163              :                         direct_generalized_diagonalization_input, diag_lib_explicit_input)
     164              :       CHARACTER(LEN=*), INTENT(IN)                       :: diag_lib
     165              :       LOGICAL, INTENT(OUT)                               :: fallback_applied
     166              :       INTEGER, INTENT(IN)                                :: elpa_kernel
     167              :       INTEGER, INTENT(IN), OPTIONAL                      :: elpa_c_kernel
     168              :       INTEGER, INTENT(IN)                                :: elpa_neigvec_min_input
     169              :       LOGICAL, INTENT(IN)                                :: elpa_qr, elpa_print, elpa_one_stage
     170              :       INTEGER, INTENT(IN)                                :: dlaf_neigvec_min_input
     171              :       REAL(KIND=dp), INTENT(IN)                          :: eps_check_diag_input
     172              :       LOGICAL, INTENT(IN), OPTIONAL :: direct_generalized_diagonalization_input, &
     173              :          diag_lib_explicit_input
     174              : 
     175              :       LOGICAL, SAVE                                      :: initialized = .FALSE.
     176              : 
     177        11451 :       fallback_applied = .FALSE.
     178              : 
     179        11451 :       IF (diag_lib == "ScaLAPACK") THEN
     180          204 :          diag_type = FM_DIAG_TYPE_SCALAPACK
     181        11247 :       ELSE IF (diag_lib == "ELPA") THEN
     182              : #if defined (__ELPA)
     183              :          ! ELPA is requested and available
     184        11247 :          diag_type = FM_DIAG_TYPE_ELPA
     185              : #else
     186              :          ! ELPA library requested but not linked, switch back to SL
     187              :          diag_type = FM_DIAG_TYPE_SCALAPACK
     188              :          fallback_applied = .TRUE.
     189              : #endif
     190            0 :       ELSE IF (diag_lib == "cuSOLVER") THEN
     191            0 :          diag_type = FM_DIAG_TYPE_CUSOLVER
     192            0 :       ELSE IF (diag_lib == "DLAF") THEN
     193              : #if defined (__DLAF)
     194              :          diag_type = FM_DIAG_TYPE_DLAF
     195              : #else
     196            0 :          CPABORT("ERROR in diag_init: CP2K was not compiled with DLA-Future support")
     197              : #endif
     198              :       ELSE
     199            0 :          CPABORT("ERROR in diag_init: Initialization of unknown diagonalization library requested")
     200              :       END IF
     201              : 
     202              :       ! Complex matrices use ELPA only when the diagonalization library was
     203              :       ! requested explicitly (e.g. via PREFERRED_DIAG_LIBRARY ELPA).
     204        11451 :       diag_lib_explicit = .TRUE.
     205        11451 :       IF (PRESENT(diag_lib_explicit_input)) diag_lib_explicit = diag_lib_explicit_input
     206              : 
     207              :       ! Initialization of requested diagonalization library
     208        11451 :       IF (.NOT. initialized .AND. diag_type == FM_DIAG_TYPE_ELPA) THEN
     209        10646 :          CALL initialize_elpa_library(one_stage=elpa_one_stage, qr=elpa_qr, should_print=elpa_print)
     210        10646 :          CALL set_elpa_kernel(elpa_kernel)
     211        10646 :          IF (PRESENT(elpa_c_kernel)) CALL set_elpa_c_kernel(elpa_c_kernel)
     212        10646 :          initialized = .TRUE.
     213              :       END IF
     214              : #if defined(__DLAF)
     215              :       IF (.NOT. initialized .AND. diag_type == FM_DIAG_TYPE_DLAF) THEN
     216              :          CALL cp_dlaf_initialize()
     217              :          initialized = .TRUE.
     218              :       END IF
     219              :       dlaf_neigvec_min = dlaf_neigvec_min_input
     220              : #else
     221              :       MARK_USED(dlaf_neigvec_min_input)
     222              : #endif
     223              : 
     224        11451 :       elpa_neigvec_min = elpa_neigvec_min_input
     225        11451 :       eps_check_diag = eps_check_diag_input
     226        11451 :       IF (PRESENT(direct_generalized_diagonalization_input)) THEN
     227        11451 :          direct_generalized_diagonalization = direct_generalized_diagonalization_input
     228              :       ELSE
     229            0 :          direct_generalized_diagonalization = .FALSE.
     230              :       END IF
     231              : 
     232        11451 :    END SUBROUTINE diag_init
     233              : 
     234              : ! **************************************************************************************************
     235              : !> \brief Finalize the diagonalization library
     236              : ! **************************************************************************************************
     237        11241 :    SUBROUTINE diag_finalize()
     238              : #if defined (__ELPA)
     239        11241 :       IF (diag_type == FM_DIAG_TYPE_ELPA) &
     240        11037 :          CALL finalize_elpa_library()
     241              : #endif
     242              : #if defined (__DLAF)
     243              :       IF (diag_type == FM_DIAG_TYPE_DLAF) &
     244              :          CALL cp_dlaf_finalize()
     245              : #endif
     246        11241 :    END SUBROUTINE diag_finalize
     247              : 
     248              : ! **************************************************************************************************
     249              : !> \brief   Choose the Eigensolver depending on which library is available
     250              : !>          ELPA seems to be unstable for small systems
     251              : !> \param matrix ...
     252              : !> \param eigenvectors ...
     253              : !> \param eigenvalues ...
     254              : !> \param info ...
     255              : !> \par     info If present returns error code and prevents program stops.
     256              : !>               Works currently only for cp_fm_syevd with ScaLAPACK.
     257              : !>               Other solvers will end the program regardless of PRESENT(info).
     258              : !> \par History
     259              : !>      - Do not use ELPA for small matrices and use instead ScaLAPACK as fallback (10.05.2021, MK)
     260              : ! **************************************************************************************************
     261       276116 :    SUBROUTINE choose_eigv_solver(matrix, eigenvectors, eigenvalues, info)
     262              : 
     263              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix, eigenvectors
     264              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: eigenvalues
     265              :       INTEGER, INTENT(OUT), OPTIONAL                     :: info
     266              : 
     267              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'choose_eigv_solver'
     268              : 
     269              :       ! Sample peak memory
     270       276116 :       CALL m_memory()
     271              : 
     272       276116 :       IF (PRESENT(info)) info = 0 ! Default for solvers that do not return an info.
     273              : 
     274       276116 :       IF (diag_type == FM_DIAG_TYPE_SCALAPACK) THEN
     275         5054 :          CALL cp_fm_syevd(matrix, eigenvectors, eigenvalues, info)
     276              : 
     277       271062 :       ELSE IF (diag_type == FM_DIAG_TYPE_ELPA) THEN
     278       271062 :          IF (matrix%matrix_struct%nrow_global < elpa_neigvec_min) THEN
     279              :             ! We don't trust ELPA with very small matrices.
     280       256640 :             CALL cp_fm_syevd(matrix, eigenvectors, eigenvalues, info)
     281              :          ELSE
     282        14422 :             CALL cp_fm_diag_elpa(matrix, eigenvectors, eigenvalues)
     283              :          END IF
     284              : 
     285            0 :       ELSE IF (diag_type == FM_DIAG_TYPE_CUSOLVER) THEN
     286            0 :          IF (matrix%matrix_struct%nrow_global < cusolver_n_min) THEN
     287              :             ! We don't trust cuSolver with very small matrices.
     288            0 :             CALL cp_fm_syevd(matrix, eigenvectors, eigenvalues, info)
     289              :          ELSE
     290            0 :             CALL cp_fm_diag_cusolver(matrix, eigenvectors, eigenvalues)
     291              :          END IF
     292              : 
     293              : #if defined(__DLAF)
     294              :       ELSE IF (diag_type == FM_DIAG_TYPE_DLAF) THEN
     295              :          IF (matrix%matrix_struct%nrow_global < dlaf_neigvec_min) THEN
     296              :             ! Use ScaLAPACK for small matrices
     297              :             CALL cp_fm_syevd(matrix, eigenvectors, eigenvalues, info)
     298              :          ELSE
     299              :             CALL cp_fm_diag_dlaf(matrix, eigenvectors, eigenvalues)
     300              :          END IF
     301              : #endif
     302              : 
     303              :       ELSE
     304            0 :          CPABORT("ERROR in "//routineN//": Invalid diagonalization type requested")
     305              :       END IF
     306              : 
     307       276116 :       CALL check_diag(matrix, eigenvectors, nvec=SIZE(eigenvalues))
     308              : 
     309       276116 :    END SUBROUTINE choose_eigv_solver
     310              : 
     311              : ! **************************************************************************************************
     312              : !> \brief Return whether diagonalization checks should be performed.
     313              : !> \return ...
     314              : ! **************************************************************************************************
     315       657001 :    FUNCTION diag_check_requested() RESULT(check_requested)
     316              :       LOGICAL                                            :: check_requested
     317              : 
     318              : #if defined(__CHECK_DIAG)
     319              :       check_requested = .TRUE.
     320              : #else
     321       657001 :       check_requested = eps_check_diag >= 0.0_dp
     322              : #endif
     323              : 
     324       657001 :    END FUNCTION diag_check_requested
     325              : 
     326              : ! **************************************************************************************************
     327              : !> \brief Return the warning threshold for diagonalization checks.
     328              : !> \return ...
     329              : ! **************************************************************************************************
     330       541348 :    FUNCTION diag_check_warning_threshold() RESULT(eps_warning)
     331              :       REAL(KIND=dp)                                      :: eps_warning
     332              : 
     333       541348 :       eps_warning = eps_check_diag_default
     334       541348 :       IF (eps_check_diag >= 0.0_dp) THEN
     335          354 :          eps_warning = eps_check_diag
     336              :       END IF
     337              : 
     338       541348 :    END FUNCTION diag_check_warning_threshold
     339              : 
     340              : ! **************************************************************************************************
     341              : !> \brief   Check result of diagonalization, i.e. the orthonormality of the eigenvectors
     342              : !> \param matrix Work matrix
     343              : !> \param eigenvectors Eigenvectors to be checked
     344              : !> \param nvec ...
     345              : ! **************************************************************************************************
     346       541330 :    SUBROUTINE check_diag(matrix, eigenvectors, nvec)
     347              : 
     348              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix, eigenvectors
     349              :       INTEGER, INTENT(IN)                                :: nvec
     350              : 
     351              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'check_diag'
     352              : 
     353              :       CHARACTER(LEN=default_string_length)               :: diag_type_name
     354              :       REAL(KIND=dp)                                      :: eps, eps_abort, eps_warning, gold, test
     355              :       INTEGER                                            :: handle, i, j, ncol, nrow, output_unit
     356              :       LOGICAL                                            :: check_eigenvectors
     357              : #if defined(__parallel)
     358              :       TYPE(cp_blacs_env_type), POINTER                   :: context
     359              :       INTEGER                                            :: il, jl, ipcol, iprow, &
     360              :                                                             mypcol, myprow, npcol, nprow
     361              :       INTEGER, DIMENSION(9)                              :: desca
     362              : #endif
     363              : 
     364       541330 :       CALL timeset(routineN, handle)
     365              : 
     366       541330 :       output_unit = default_output_unit
     367       541330 :       check_eigenvectors = diag_check_requested()
     368       541330 :       eps_warning = diag_check_warning_threshold()
     369       541330 :       eps_abort = 10.0_dp*eps_warning
     370              : 
     371       541330 :       gold = 0.0_dp
     372       541330 :       test = 0.0_dp
     373       541330 :       eps = 0.0_dp
     374              : 
     375       541330 :       IF (check_eigenvectors) THEN
     376              : #if defined(__parallel)
     377          336 :          nrow = eigenvectors%matrix_struct%nrow_global
     378          336 :          ncol = MIN(eigenvectors%matrix_struct%ncol_global, nvec)
     379          336 :          CALL cp_fm_gemm("T", "N", ncol, ncol, nrow, 1.0_dp, eigenvectors, eigenvectors, 0.0_dp, matrix)
     380          336 :          context => matrix%matrix_struct%context
     381          336 :          myprow = context%mepos(1)
     382          336 :          mypcol = context%mepos(2)
     383          336 :          nprow = context%num_pe(1)
     384          336 :          npcol = context%num_pe(2)
     385         3360 :          desca(:) = matrix%matrix_struct%descriptor(:)
     386         6044 :          outer: DO j = 1, ncol
     387       250176 :             DO i = 1, ncol
     388       244132 :                CALL infog2l(i, j, desca, nprow, npcol, myprow, mypcol, il, jl, iprow, ipcol)
     389       249840 :                IF ((iprow == myprow) .AND. (ipcol == mypcol)) THEN
     390       122066 :                   gold = MERGE(0.0_dp, 1.0_dp, i /= j)
     391       122066 :                   test = matrix%local_data(il, jl)
     392       122066 :                   eps = ABS(test - gold)
     393       122066 :                   IF (eps > eps_warning) EXIT outer
     394              :                END IF
     395              :             END DO
     396              :          END DO outer
     397              : #else
     398              :          nrow = SIZE(eigenvectors%local_data, 1)
     399              :          ncol = MIN(SIZE(eigenvectors%local_data, 2), nvec)
     400              :          CALL dgemm("T", "N", ncol, ncol, nrow, 1.0_dp, &
     401              :                     eigenvectors%local_data(1, 1), nrow, &
     402              :                     eigenvectors%local_data(1, 1), nrow, &
     403              :                     0.0_dp, matrix%local_data(1, 1), nrow)
     404              :          outer: DO j = 1, ncol
     405              :             DO i = 1, ncol
     406              :                gold = MERGE(0.0_dp, 1.0_dp, i /= j)
     407              :                test = matrix%local_data(i, j)
     408              :                eps = ABS(test - gold)
     409              :                IF (eps > eps_warning) EXIT outer
     410              :             END DO
     411              :          END DO outer
     412              : #endif
     413          336 :          IF (eps > eps_warning) THEN
     414            0 :             IF (diag_type == FM_DIAG_TYPE_SCALAPACK) THEN
     415            0 :                diag_type_name = "SYEVD"
     416            0 :             ELSE IF (diag_type == FM_DIAG_TYPE_ELPA) THEN
     417            0 :                diag_type_name = "ELPA"
     418            0 :             ELSE IF (diag_type == FM_DIAG_TYPE_CUSOLVER) THEN
     419            0 :                diag_type_name = "CUSOLVER"
     420            0 :             ELSE IF (diag_type == FM_DIAG_TYPE_DLAF) THEN
     421            0 :                diag_type_name = "DLAF"
     422              :             ELSE
     423            0 :                CPABORT("Unknown diag_type")
     424              :             END IF
     425              :             WRITE (UNIT=output_unit, FMT="(/,T2,A,/,T2,A,I0,A,I0,A,F0.15,/,T2,A,F0.0,A,ES10.3)") &
     426            0 :                "The eigenvectors returned by "//TRIM(diag_type_name)//" are not orthonormal", &
     427            0 :                "Matrix element (", i, ", ", j, ") = ", test, &
     428            0 :                "The deviation from the expected value ", gold, " is", eps
     429            0 :             IF (eps > eps_abort) THEN
     430            0 :                CPABORT("ERROR in "//routineN//": Check of matrix diagonalization failed")
     431              :             ELSE
     432            0 :                CPWARN("Check of matrix diagonalization failed in routine "//routineN)
     433              :             END IF
     434              :          END IF
     435              :       END IF
     436              : 
     437       541330 :       CALL timestop(handle)
     438              : 
     439       541330 :    END SUBROUTINE check_diag
     440              : 
     441              : ! **************************************************************************************************
     442              : !> \brief   Check C^T*S*C = I for a generalized eigenvalue problem.
     443              : !> \param overlap original overlap matrix S; used as work matrix and overwritten
     444              : !> \param eigenvectors eigenvectors C to be checked
     445              : !> \param scratch work matrix
     446              : !> \param nvec ...
     447              : ! **************************************************************************************************
     448            2 :    SUBROUTINE check_generalized_diag(overlap, eigenvectors, scratch, nvec)
     449              : 
     450              :       TYPE(cp_fm_type), INTENT(IN)                       :: eigenvectors
     451              :       TYPE(cp_fm_type), INTENT(INOUT)                    :: overlap, scratch
     452              :       INTEGER, INTENT(IN)                                :: nvec
     453              : 
     454              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'check_generalized_diag'
     455              : 
     456              :       CHARACTER(LEN=default_string_length)               :: diag_type_name
     457              :       REAL(KIND=dp)                                      :: eps, eps_abort, eps_warning, gold, test
     458              :       INTEGER                                            :: handle, i, j, ncol, nrow, output_unit
     459              : #if defined(__parallel)
     460              :       TYPE(cp_blacs_env_type), POINTER                   :: context
     461              :       INTEGER                                            :: il, jl, ipcol, iprow, &
     462              :                                                             mypcol, myprow, npcol, nprow
     463              :       INTEGER, DIMENSION(9)                              :: desca
     464              : #endif
     465              : 
     466            2 :       CALL timeset(routineN, handle)
     467              : 
     468            2 :       IF (.NOT. diag_check_requested()) THEN
     469            0 :          CALL timestop(handle)
     470            0 :          RETURN
     471              :       END IF
     472              : 
     473            2 :       output_unit = default_output_unit
     474            2 :       eps_warning = diag_check_warning_threshold()
     475            2 :       eps_abort = 10.0_dp*eps_warning
     476              : 
     477            2 :       nrow = eigenvectors%matrix_struct%nrow_global
     478            2 :       ncol = MIN(eigenvectors%matrix_struct%ncol_global, nvec)
     479              : 
     480            2 :       CALL parallel_gemm("N", "N", nrow, ncol, nrow, 1.0_dp, overlap, eigenvectors, 0.0_dp, scratch)
     481            2 :       CALL parallel_gemm("T", "N", ncol, ncol, nrow, 1.0_dp, eigenvectors, scratch, 0.0_dp, overlap)
     482              : 
     483            2 :       gold = 0.0_dp
     484            2 :       test = 0.0_dp
     485            2 :       eps = 0.0_dp
     486              : 
     487              : #if defined(__parallel)
     488            2 :       context => overlap%matrix_struct%context
     489            2 :       myprow = context%mepos(1)
     490            2 :       mypcol = context%mepos(2)
     491            2 :       nprow = context%num_pe(1)
     492            2 :       npcol = context%num_pe(2)
     493           20 :       desca(:) = overlap%matrix_struct%descriptor(:)
     494           19 :       outer: DO j = 1, ncol
     495          293 :          DO i = 1, ncol
     496          274 :             CALL infog2l(i, j, desca, nprow, npcol, myprow, mypcol, il, jl, iprow, ipcol)
     497          291 :             IF ((iprow == myprow) .AND. (ipcol == mypcol)) THEN
     498           18 :                gold = MERGE(0.0_dp, 1.0_dp, i /= j)
     499           18 :                test = overlap%local_data(il, jl)
     500           18 :                eps = ABS(test - gold)
     501           18 :                IF (eps > eps_warning) EXIT outer
     502              :             END IF
     503              :          END DO
     504              :       END DO outer
     505              : #else
     506              :       outer: DO j = 1, ncol
     507              :          DO i = 1, ncol
     508              :             gold = MERGE(0.0_dp, 1.0_dp, i /= j)
     509              :             test = overlap%local_data(i, j)
     510              :             eps = ABS(test - gold)
     511              :             IF (eps > eps_warning) EXIT outer
     512              :          END DO
     513              :       END DO outer
     514              : #endif
     515              : 
     516            2 :       IF (eps > eps_warning) THEN
     517            1 :          IF (diag_type == FM_DIAG_TYPE_SCALAPACK) THEN
     518            1 :             diag_type_name = "SYGVX"
     519            0 :          ELSE IF (diag_type == FM_DIAG_TYPE_ELPA) THEN
     520            0 :             diag_type_name = "ELPA"
     521            0 :          ELSE IF (diag_type == FM_DIAG_TYPE_CUSOLVER) THEN
     522            0 :             diag_type_name = "CUSOLVER"
     523            0 :          ELSE IF (diag_type == FM_DIAG_TYPE_DLAF) THEN
     524            0 :             diag_type_name = "DLAF"
     525              :          ELSE
     526            0 :             CPABORT("Unknown diag_type")
     527              :          END IF
     528              :          WRITE (UNIT=output_unit, FMT="(/,T2,A,/,T2,A,I0,A,I0,A,F0.15,/,T2,A,F0.0,A,ES10.3)") &
     529            1 :             "The generalized eigenvectors returned by "//TRIM(diag_type_name)//" are not S-orthonormal", &
     530            1 :             "Matrix element (", i, ", ", j, ") = ", test, &
     531            2 :             "The deviation from the expected value ", gold, " is", eps
     532            1 :          IF (eps > eps_abort) THEN
     533            0 :             CPABORT("ERROR in "//routineN//": Check of generalized matrix diagonalization failed")
     534              :          ELSE
     535            1 :             CPWARN("Check of generalized matrix diagonalization failed in routine "//routineN)
     536              :          END IF
     537              :       END IF
     538              : 
     539            2 :       CALL timestop(handle)
     540              : 
     541              :    END SUBROUTINE check_generalized_diag
     542              : 
     543              : ! **************************************************************************************************
     544              : !> \brief Issues an error messages and exits (optionally only warns).
     545              : !> \param mesg message to be issued
     546              : !> \param info error code (optional)
     547              : !> \param warn only warn (optional)
     548              : ! **************************************************************************************************
     549            0 :    SUBROUTINE cp_fm_error(mesg, info, warn)
     550              :       CHARACTER(LEN=*), INTENT(IN)                       :: mesg
     551              :       INTEGER, INTENT(IN), OPTIONAL                      :: info
     552              :       LOGICAL, INTENT(IN), OPTIONAL                      :: warn
     553              : 
     554              :       CHARACTER(LEN=2*default_string_length)             :: message
     555              :       LOGICAL                                            :: warning
     556              : 
     557            0 :       IF (PRESENT(info)) THEN
     558            0 :          WRITE (message, "(A,A,I0,A)") mesg, " (INFO = ", info, ")"
     559              :       ELSE
     560            0 :          WRITE (message, "(A)") mesg
     561              :       END IF
     562              : 
     563            0 :       IF (PRESENT(warn)) THEN
     564            0 :          warning = warn
     565              :       ELSE ! abort
     566              :          warning = .FALSE.
     567              :       END IF
     568              : 
     569            0 :       IF (warning) THEN
     570            0 :          CPWARN(TRIM(message))
     571              :       ELSE
     572            0 :          CPABORT(TRIM(message))
     573              :       END IF
     574            0 :    END SUBROUTINE cp_fm_error
     575              : 
     576              : ! **************************************************************************************************
     577              : !> \brief   Computes all eigenvalues and vectors of a real symmetric matrix
     578              : !>          significantly faster than syevx, scales also much better.
     579              : !>          Needs workspace to allocate all the eigenvectors
     580              : !> \param matrix ...
     581              : !> \param eigenvectors ...
     582              : !> \param eigenvalues ...
     583              : !> \param info ...
     584              : !> \par     matrix is supposed to be in upper triangular form, and overwritten by this routine
     585              : !> \par     info If present returns error code and prevents program stops.
     586              : !>               Works currently only for scalapack.
     587              : !>               Other solvers will end the program regardless of PRESENT(info).
     588              : ! **************************************************************************************************
     589       265174 :    SUBROUTINE cp_fm_syevd(matrix, eigenvectors, eigenvalues, info)
     590              : 
     591              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix, eigenvectors
     592              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: eigenvalues
     593              :       INTEGER, INTENT(OUT), OPTIONAL           :: info
     594              : 
     595              :       CHARACTER(LEN=*), PARAMETER              :: routineN = 'cp_fm_syevd'
     596              : 
     597              :       INTEGER                                  :: handle, myinfo, n, nmo
     598              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eig
     599              : #if defined(__parallel)
     600              :       TYPE(cp_fm_type)                         :: eigenvectors_new, matrix_new
     601              : #else
     602              :       INTEGER                                  :: liwork, lwork
     603              :       INTEGER, DIMENSION(:), POINTER           :: iwork
     604              :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: m
     605              :       REAL(KIND=dp), DIMENSION(:), POINTER     :: work
     606              :       INTEGER, TARGET                          :: v(1)
     607              :       REAL(KIND=dp), TARGET                    :: w(1)
     608              : #endif
     609              : 
     610       265174 :       CALL timeset(routineN, handle)
     611              : 
     612       265174 :       myinfo = 0
     613              : 
     614       265174 :       n = matrix%matrix_struct%nrow_global
     615       795522 :       ALLOCATE (eig(n))
     616              : 
     617              : #if defined(__parallel)
     618              :       ! Determine if the input matrix needs to be redistributed before diagonalization.
     619              :       ! Heuristics are used to determine the optimal number of CPUs for diagonalization.
     620              :       ! The redistributed matrix is stored in matrix_new, which is just a pointer
     621              :       ! to the original matrix if no redistribution is required
     622       265174 :       CALL cp_fm_redistribute_start(matrix, eigenvectors, matrix_new, eigenvectors_new)
     623              : 
     624              :       ! Call scalapack on CPUs that hold the new matrix
     625       265174 :       IF (ASSOCIATED(matrix_new%matrix_struct)) THEN
     626       134473 :          IF (PRESENT(info)) THEN
     627         2691 :             CALL cp_fm_syevd_base(matrix_new, eigenvectors_new, eig, myinfo)
     628              :          ELSE
     629       131782 :             CALL cp_fm_syevd_base(matrix_new, eigenvectors_new, eig)
     630              :          END IF
     631              :       END IF
     632              :       ! Redistribute results and clean up
     633       265174 :       CALL cp_fm_redistribute_end(matrix, eigenvectors, eig, matrix_new, eigenvectors_new)
     634              : #else
     635              :       ! Retrieve the optimal work array sizes first
     636              :       lwork = -1
     637              :       liwork = -1
     638              :       m => matrix%local_data
     639              :       iwork => v
     640              :       work => w
     641              : 
     642              :       CALL dsyevd('V', 'U', n, m(1, 1), SIZE(m, 1), eig(1), work(1), lwork, iwork(1), liwork, myinfo)
     643              : 
     644              :       IF (myinfo /= 0) THEN
     645              :          CALL cp_fm_error("ERROR in DSYEVD: Work space query failed", myinfo, PRESENT(info))
     646              :       END IF
     647              : 
     648              :       ! Reallocate work arrays and perform diagonalisation
     649              :       lwork = NINT(work(1))
     650              :       ALLOCATE (work(lwork))
     651              : 
     652              :       liwork = iwork(1)
     653              :       ALLOCATE (iwork(liwork))
     654              : 
     655              :       CALL dsyevd('V', 'U', n, m(1, 1), SIZE(m, 1), eig(1), work(1), lwork, iwork(1), liwork, myinfo)
     656              : 
     657              :       IF (myinfo /= 0) THEN
     658              :          CALL cp_fm_error("ERROR in DSYEVD: Matrix diagonalization failed", myinfo, PRESENT(info))
     659              :       END IF
     660              : 
     661              :       CALL cp_fm_to_fm(matrix, eigenvectors)
     662              : 
     663              :       DEALLOCATE (iwork)
     664              :       DEALLOCATE (work)
     665              : #endif
     666              : 
     667       265174 :       IF (PRESENT(info)) info = myinfo
     668              : 
     669       265174 :       nmo = SIZE(eigenvalues, 1)
     670       265174 :       IF (nmo > n) THEN
     671            0 :          eigenvalues(1:n) = eig(1:n)
     672              :       ELSE
     673      2142183 :          eigenvalues(1:nmo) = eig(1:nmo)
     674              :       END IF
     675              : 
     676       265174 :       DEALLOCATE (eig)
     677              : 
     678       265174 :       CALL check_diag(matrix, eigenvectors, n)
     679              : 
     680       265174 :       CALL timestop(handle)
     681              : 
     682       530348 :    END SUBROUTINE cp_fm_syevd
     683              : 
     684              : ! **************************************************************************************************
     685              : !> \brief ...
     686              : !> \param matrix ...
     687              : !> \param eigenvectors ...
     688              : !> \param eigenvalues ...
     689              : !> \param info ...
     690              : ! **************************************************************************************************
     691       134473 :    SUBROUTINE cp_fm_syevd_base(matrix, eigenvectors, eigenvalues, info)
     692              : 
     693              :       TYPE(cp_fm_type), INTENT(IN)             :: matrix, eigenvectors
     694              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: eigenvalues
     695              :       INTEGER, INTENT(OUT), OPTIONAL           :: info
     696              : 
     697              :       CHARACTER(LEN=*), PARAMETER              :: routineN = 'cp_fm_syevd_base'
     698              : 
     699              :       INTEGER                                  :: handle, myinfo
     700              : #if defined(__parallel)
     701              :       TYPE(cp_blacs_env_type), POINTER         :: context
     702              :       INTEGER                                  :: liwork, lwork, n
     703              :       INTEGER, DIMENSION(9)                    :: descm, descv
     704       134473 :       INTEGER, DIMENSION(:), POINTER           :: iwork
     705       134473 :       REAL(KIND=dp), DIMENSION(:), POINTER     :: work
     706       134473 :       REAL(KIND=dp), DIMENSION(:, :), POINTER  :: m, v
     707              :       REAL(KIND=dp), TARGET                    :: w(1)
     708              : #if defined (__HAS_IEEE_EXCEPTIONS)
     709              :       LOGICAL, DIMENSION(5)                    :: halt
     710              : #endif
     711              : #endif
     712              : 
     713       134473 :       CALL timeset(routineN, handle)
     714              : 
     715       134473 :       myinfo = 0
     716              : 
     717              : #if defined(__parallel)
     718              : 
     719       134473 :       n = matrix%matrix_struct%nrow_global
     720       134473 :       m => matrix%local_data
     721       134473 :       context => matrix%matrix_struct%context
     722      1344730 :       descm(:) = matrix%matrix_struct%descriptor(:)
     723              : 
     724       134473 :       v => eigenvectors%local_data
     725      1344730 :       descv(:) = eigenvectors%matrix_struct%descriptor(:)
     726              : 
     727       134473 :       liwork = 7*n + 8*context%num_pe(2) + 2
     728       403419 :       ALLOCATE (iwork(liwork))
     729              : 
     730              :       ! Work space query
     731       134473 :       lwork = -1
     732       134473 :       work => w
     733              : 
     734              :       CALL pdsyevd('V', 'U', n, m(1, 1), 1, 1, descm, eigenvalues(1), v(1, 1), 1, 1, descv, &
     735       134473 :                    work(1), lwork, iwork(1), liwork, myinfo)
     736              : 
     737       134473 :       IF (matrix%matrix_struct%para_env%is_source() .AND. (myinfo /= 0)) THEN
     738            0 :          CALL cp_fm_error("ERROR in PDSYEVD: Work space query failed", myinfo, PRESENT(info))
     739              :       END IF
     740              : 
     741       134473 :       lwork = NINT(work(1)) ! can be insufficient due to bug in reference ScaLAPACK
     742              : #if !defined(__SCALAPACK_NO_WA)
     743              :       ! Query workspace for QDORMTR as called by reference ScaLAPACK (PDSYEVD).
     744              :       CALL pdormtr('L', 'U', 'N', n, n, m(1, 1), 1, 1, descm, m(1, 1), &
     745       134473 :                    v(1, 1), 1, 1, descv, work(1), -1, myinfo)
     746              : 
     747       134473 :       IF (matrix%matrix_struct%para_env%is_source() .AND. (myinfo /= 0)) THEN
     748            0 :          CALL cp_fm_error("ERROR in PDORMTR: Work space query failed", myinfo, PRESENT(info))
     749              :       END IF
     750              : 
     751       134473 :       IF (lwork < (work(1) + 2*n)) THEN
     752        52044 :          lwork = NINT(work(1)) + 2*n ! still wrong by 2*N
     753              :       END IF
     754              : #endif
     755       403419 :       ALLOCATE (work(lwork))
     756              : 
     757              :       ! Initial/documented amount of liwork is exceeded (slightly worrisome too).
     758       134473 :       IF (liwork < iwork(1)) THEN
     759            0 :          liwork = iwork(1)
     760            0 :          DEALLOCATE (iwork)
     761            0 :          ALLOCATE (iwork(liwork))
     762              :       END IF
     763              : 
     764              :       ! ScaLAPACK takes advantage of IEEE754 exceptions for speedup.
     765              :       ! Therefore, we disable floating point traps temporarily.
     766              : #if defined (__HAS_IEEE_EXCEPTIONS)
     767              :       CALL ieee_get_halting_mode(IEEE_ALL, halt)
     768              :       CALL ieee_set_halting_mode(IEEE_ALL, .FALSE.)
     769              : #endif
     770              : 
     771              :       CALL pdsyevd('V', 'U', n, m(1, 1), 1, 1, descm, eigenvalues(1), v(1, 1), 1, 1, descv, &
     772       134473 :                    work(1), lwork, iwork(1), liwork, myinfo)
     773              : 
     774              : #if defined (__HAS_IEEE_EXCEPTIONS)
     775              :       CALL ieee_set_halting_mode(IEEE_ALL, halt)
     776              : #endif
     777       134473 :       IF (matrix%matrix_struct%para_env%is_source() .AND. (myinfo /= 0)) THEN
     778            0 :          CALL cp_fm_error("ERROR in PDSYEVD: Matrix diagonalization failed", myinfo, PRESENT(info))
     779              :       END IF
     780              : 
     781       134473 :       IF (PRESENT(info)) info = myinfo
     782              : 
     783       134473 :       DEALLOCATE (work)
     784       134473 :       DEALLOCATE (iwork)
     785              : #else
     786              :       MARK_USED(matrix)
     787              :       MARK_USED(eigenvectors)
     788              :       MARK_USED(eigenvalues)
     789              :       myinfo = -1
     790              :       IF (PRESENT(info)) info = myinfo
     791              :       CALL cp_fm_error("ERROR in "//TRIM(routineN)// &
     792              :                        ": Matrix diagonalization using PDSYEVD requested without ScaLAPACK support")
     793              : #endif
     794              : 
     795       134473 :       CALL timestop(handle)
     796              : 
     797       134473 :    END SUBROUTINE cp_fm_syevd_base
     798              : 
     799              : ! **************************************************************************************************
     800              : !> \brief   compute eigenvalues and optionally eigenvectors of a real symmetric matrix using scalapack.
     801              : !>          If eigenvectors are required this routine will replicate a full matrix on each CPU...
     802              : !>          if more than a handful of vectors are needed, use cp_fm_syevd instead
     803              : !> \param matrix ...
     804              : !> \param eigenvectors ...
     805              : !> \param eigenvalues ...
     806              : !> \param neig ...
     807              : !> \param work_syevx ...
     808              : !> \par     matrix is supposed to be in upper triangular form, and overwritten by this routine
     809              : !>          neig   is the number of vectors needed (default all)
     810              : !>          work_syevx evec calculation only, is the fraction of the working buffer allowed (1.0 use full buffer)
     811              : !>                     reducing this saves time, but might cause the routine to fail
     812              : ! **************************************************************************************************
     813           40 :    SUBROUTINE cp_fm_syevx(matrix, eigenvectors, eigenvalues, neig, work_syevx)
     814              : 
     815              :       ! Diagonalise the symmetric n by n matrix using the LAPACK library.
     816              : 
     817              :       TYPE(cp_fm_type), INTENT(IN)                 :: matrix
     818              :       TYPE(cp_fm_type), OPTIONAL, INTENT(IN)       :: eigenvectors
     819              :       REAL(KIND=dp), OPTIONAL, INTENT(IN)          :: work_syevx
     820              :       INTEGER, INTENT(IN), OPTIONAL                :: neig
     821              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)     :: eigenvalues
     822              : 
     823              :       CHARACTER(LEN=*), PARAMETER                  :: routineN = "cp_fm_syevx"
     824              : 
     825              : #if defined(__parallel)
     826              :       REAL(KIND=dp), PARAMETER                     :: orfac = -1.0_dp
     827              : #endif
     828              :       REAL(KIND=dp), PARAMETER                     :: vl = 0.0_dp, &
     829              :                                                       vu = 0.0_dp
     830              : 
     831              :       TYPE(cp_blacs_env_type), POINTER             :: context
     832              :       TYPE(cp_logger_type), POINTER                :: logger
     833              :       CHARACTER(LEN=1)                             :: job_type
     834              :       REAL(KIND=dp)                                :: abstol, work_syevx_local
     835              :       INTEGER                                      :: handle, info, liwork, lwork, &
     836              :                                                       m, n, nb, npcol, nprow, &
     837              :                                                       output_unit, neig_local
     838              :       LOGICAL                                      :: ionode, needs_evecs
     839           40 :       INTEGER, DIMENSION(:), ALLOCATABLE           :: ifail, iwork
     840           40 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE     :: w, work
     841           40 :       REAL(KIND=dp), DIMENSION(:, :), POINTER      :: a, z
     842              : 
     843              :       REAL(KIND=dp), EXTERNAL                      :: dlamch
     844              : 
     845              : #if defined(__parallel)
     846              :       INTEGER                                      :: nn, np0, npe, nq0, nz
     847              :       INTEGER, DIMENSION(9)                        :: desca, descz
     848           40 :       INTEGER, DIMENSION(:), ALLOCATABLE           :: iclustr
     849           40 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE     :: gap
     850              :       INTEGER, EXTERNAL                            :: iceil, numroc
     851              : #else
     852              :       INTEGER                                      :: nla, nlz
     853              :       INTEGER, EXTERNAL                            :: ilaenv
     854              : #endif
     855              : #if defined (__HAS_IEEE_EXCEPTIONS)
     856              :       LOGICAL, DIMENSION(5)                        :: halt
     857              : #endif
     858              : 
     859              :       ! by default all
     860           40 :       n = matrix%matrix_struct%nrow_global
     861           40 :       neig_local = n
     862           40 :       IF (PRESENT(neig)) neig_local = neig
     863           40 :       IF (neig_local == 0) RETURN
     864              : 
     865           40 :       CALL timeset(routineN, handle)
     866              : 
     867           40 :       needs_evecs = PRESENT(eigenvectors)
     868              : 
     869           40 :       logger => cp_get_default_logger()
     870           40 :       ionode = logger%para_env%is_source()
     871           40 :       n = matrix%matrix_struct%nrow_global
     872              : 
     873              :       ! by default allocate all needed space
     874           40 :       work_syevx_local = 1.0_dp
     875           40 :       IF (PRESENT(work_syevx)) work_syevx_local = work_syevx
     876              : 
     877              :       ! set scalapack job type
     878           40 :       IF (needs_evecs) THEN
     879           40 :          job_type = "V"
     880              :       ELSE
     881            0 :          job_type = "N"
     882              :       END IF
     883              : 
     884              :       ! target the most accurate calculation of the eigenvalues
     885           40 :       abstol = 2.0_dp*dlamch("S")
     886              : 
     887           40 :       context => matrix%matrix_struct%context
     888           40 :       nprow = context%num_pe(1)
     889           40 :       npcol = context%num_pe(2)
     890              : 
     891          120 :       ALLOCATE (w(n))
     892          560 :       eigenvalues(:) = 0.0_dp
     893              : #if defined(__parallel)
     894              : 
     895           40 :       IF (matrix%matrix_struct%nrow_block /= matrix%matrix_struct%ncol_block) THEN
     896            0 :          CPABORT("ERROR in "//routineN//": Invalid blocksize (no square blocks) found")
     897              :       END IF
     898              : 
     899           40 :       a => matrix%local_data
     900          400 :       desca(:) = matrix%matrix_struct%descriptor(:)
     901              : 
     902           40 :       IF (needs_evecs) THEN
     903           40 :          z => eigenvectors%local_data
     904          400 :          descz(:) = eigenvectors%matrix_struct%descriptor(:)
     905              :       ELSE
     906              :          ! z will not be referenced
     907            0 :          z => matrix%local_data
     908            0 :          descz = desca
     909              :       END IF
     910              : 
     911              :       ! Get the optimal work storage size
     912              : 
     913           40 :       npe = nprow*npcol
     914           40 :       nb = matrix%matrix_struct%nrow_block
     915           40 :       nn = MAX(n, nb, 2)
     916           40 :       np0 = numroc(nn, nb, 0, 0, nprow)
     917           40 :       nq0 = MAX(numroc(nn, nb, 0, 0, npcol), nb)
     918              : 
     919           40 :       IF (needs_evecs) THEN
     920              :          lwork = 5*n + MAX(5*nn, np0*nq0) + iceil(neig_local, npe)*nn + 2*nb*nb + &
     921           40 :                  INT(work_syevx_local*REAL((neig_local - 1)*n, dp)) !!!! allocates a full matrix on every CPU !!!!!
     922              :       ELSE
     923            0 :          lwork = 5*n + MAX(5*nn, nb*(np0 + 1))
     924              :       END IF
     925           40 :       liwork = 6*MAX(N, npe + 1, 4)
     926              : 
     927          120 :       ALLOCATE (gap(npe))
     928           40 :       gap = 0.0_dp
     929          120 :       ALLOCATE (iclustr(2*npe))
     930           40 :       iclustr = 0
     931          120 :       ALLOCATE (ifail(n))
     932           40 :       ifail = 0
     933          120 :       ALLOCATE (iwork(liwork))
     934          120 :       ALLOCATE (work(lwork))
     935              : 
     936              :       ! ScaLAPACK takes advantage of IEEE754 exceptions for speedup.
     937              :       ! Therefore, we disable floating point traps temporarily.
     938              : #if defined (__HAS_IEEE_EXCEPTIONS)
     939              :       CALL ieee_get_halting_mode(IEEE_ALL, halt)
     940              :       CALL ieee_set_halting_mode(IEEE_ALL, .FALSE.)
     941              : #endif
     942              :       CALL pdsyevx(job_type, "I", "U", n, a(1, 1), 1, 1, desca, vl, vu, 1, neig_local, abstol, m, nz, w(1), orfac, &
     943           40 :                    z(1, 1), 1, 1, descz, work(1), lwork, iwork(1), liwork, ifail(1), iclustr(1), gap, info)
     944              : #if defined (__HAS_IEEE_EXCEPTIONS)
     945              :       CALL ieee_set_halting_mode(IEEE_ALL, halt)
     946              : #endif
     947              : 
     948              :       ! Error handling
     949           40 :       IF (info /= 0) THEN
     950            0 :          IF (ionode) THEN
     951            0 :             output_unit = cp_logger_get_unit_nr(logger, local=.FALSE.)
     952              :             WRITE (unit=output_unit, FMT="(/,(T3,A,T12,1X,I10))") &
     953            0 :                "info    = ", info, &
     954            0 :                "lwork   = ", lwork, &
     955            0 :                "liwork  = ", liwork, &
     956            0 :                "nz      = ", nz
     957            0 :             IF (info > 0) THEN
     958              :                WRITE (unit=output_unit, FMT="(/,T3,A,(T12,6(1X,I10)))") &
     959            0 :                   "ifail   = ", ifail
     960              :                WRITE (unit=output_unit, FMT="(/,T3,A,(T12,6(1X,I10)))") &
     961            0 :                   "iclustr = ", iclustr
     962              :                WRITE (unit=output_unit, FMT="(/,T3,A,(T12,6(1X,E10.3)))") &
     963            0 :                   "gap     = ", gap
     964              :             END IF
     965              :          END IF
     966            0 :          CPABORT("ERROR in PDSYEVX (ScaLAPACK)")
     967              :       END IF
     968              : 
     969              :       ! Release work storage
     970           40 :       DEALLOCATE (gap)
     971           40 :       DEALLOCATE (iclustr)
     972              : 
     973              : #else
     974              : 
     975              :       a => matrix%local_data
     976              :       IF (needs_evecs) THEN
     977              :          z => eigenvectors%local_data
     978              :       ELSE
     979              :          ! z will not be referenced
     980              :          z => matrix%local_data
     981              :       END IF
     982              : 
     983              :       ! Get the optimal work storage size
     984              : 
     985              :       nb = MAX(ilaenv(1, "DSYTRD", "U", n, -1, -1, -1), &
     986              :                ilaenv(1, "DORMTR", "U", n, -1, -1, -1))
     987              : 
     988              :       lwork = MAX((nb + 3)*n, 8*n) + n ! sun bug fix
     989              :       liwork = 5*n
     990              : 
     991              :       ALLOCATE (ifail(n))
     992              :       ifail = 0
     993              :       ALLOCATE (iwork(liwork))
     994              :       ALLOCATE (work(lwork))
     995              :       info = 0
     996              :       nla = SIZE(a, 1)
     997              :       nlz = SIZE(z, 1)
     998              : 
     999              :       ! LAPACK takes advantage of IEEE754 exceptions for speedup.
    1000              :       ! Therefore, we disable floating point traps temporarily.
    1001              : #if defined (__HAS_IEEE_EXCEPTIONS)
    1002              :       CALL ieee_get_halting_mode(IEEE_ALL, halt)
    1003              :       CALL ieee_set_halting_mode(IEEE_ALL, .FALSE.)
    1004              : #endif
    1005              :       CALL dsyevx(job_type, "I", "U", n, a(1, 1), nla, vl, vu, 1, neig_local, &
    1006              :                   abstol, m, w, z(1, 1), nlz, work(1), lwork, iwork(1), ifail(1), info)
    1007              : #if defined (__HAS_IEEE_EXCEPTIONS)
    1008              :       CALL ieee_set_halting_mode(IEEE_ALL, halt)
    1009              : #endif
    1010              : 
    1011              :       ! Error handling
    1012              :       IF (info /= 0) THEN
    1013              :          output_unit = cp_logger_get_unit_nr(logger, local=.FALSE.)
    1014              :          WRITE (unit=output_unit, FMT="(/,(T3,A,T12,1X,I10))") &
    1015              :             "info    = ", info
    1016              :          IF (info > 0) THEN
    1017              :             WRITE (unit=output_unit, FMT="(/,T3,A,(T12,6(1X,I10)))") &
    1018              :                "ifail   = ", ifail
    1019              :          END IF
    1020              :          CPABORT("Error in DSYEVX (ScaLAPACK)")
    1021              :       END IF
    1022              : 
    1023              : #endif
    1024              :       ! Release work storage
    1025           40 :       DEALLOCATE (ifail)
    1026           40 :       DEALLOCATE (iwork)
    1027           40 :       DEALLOCATE (work)
    1028          400 :       eigenvalues(1:neig_local) = w(1:neig_local)
    1029           40 :       DEALLOCATE (w)
    1030              : 
    1031           40 :       IF (needs_evecs) CALL check_diag(matrix, eigenvectors, neig_local)
    1032              : 
    1033           40 :       CALL timestop(handle)
    1034              : 
    1035          120 :    END SUBROUTINE cp_fm_syevx
    1036              : 
    1037              : ! **************************************************************************************************
    1038              : !> \brief decomposes a quadratic matrix into its singular value decomposition
    1039              : !> \param matrix_a ...
    1040              : !> \param matrix_eigvl ...
    1041              : !> \param matrix_eigvr_t ...
    1042              : !> \param eigval ...
    1043              : !> \param info ...
    1044              : !> \author Maximilian Graml
    1045              : ! **************************************************************************************************
    1046          100 :    SUBROUTINE cp_fm_svd(matrix_a, matrix_eigvl, matrix_eigvr_t, eigval, info)
    1047              : 
    1048              :       TYPE(cp_fm_type), INTENT(IN)              :: matrix_a
    1049              :       TYPE(cp_fm_type), INTENT(INOUT)           :: matrix_eigvl, matrix_eigvr_t
    1050              :       REAL(KIND=dp), DIMENSION(:), POINTER, &
    1051              :          INTENT(INOUT)                          :: eigval
    1052              :       INTEGER, INTENT(OUT), OPTIONAL            :: info
    1053              : 
    1054              :       CHARACTER(LEN=*), PARAMETER :: routineN = 'cp_fm_svd'
    1055              : 
    1056              :       INTEGER                                   :: handle, n, m, myinfo, lwork
    1057          100 :       REAL(KIND=dp), DIMENSION(:, :), POINTER   :: a
    1058              :       TYPE(cp_fm_type)                          :: matrix_lu
    1059              :       REAL(KIND=dp), DIMENSION(:), POINTER      :: work
    1060              :       REAL(KIND=dp), TARGET                     :: w(1)
    1061              : #if defined(__parallel)
    1062              :       INTEGER, DIMENSION(9)                     :: desca, descu, descvt
    1063              : #endif
    1064              : 
    1065          100 :       CALL timeset(routineN, handle)
    1066              : 
    1067              :       CALL cp_fm_create(matrix=matrix_lu, &
    1068              :                         matrix_struct=matrix_a%matrix_struct, &
    1069          100 :                         name="A_lu"//TRIM(ADJUSTL(cp_to_string(1)))//"MATRIX")
    1070          100 :       CALL cp_fm_to_fm(matrix_a, matrix_lu)
    1071          100 :       a => matrix_lu%local_data
    1072          100 :       m = matrix_lu%matrix_struct%nrow_global
    1073          100 :       n = matrix_lu%matrix_struct%ncol_global
    1074              :       ! Assert that incoming matrix is quadratic
    1075          100 :       CPASSERT(m == n)
    1076              : 
    1077              :       ! Prepare for workspace queries
    1078          100 :       myinfo = 0
    1079          100 :       lwork = -1
    1080          100 :       work => w
    1081              : #if defined(__parallel)
    1082              :       ! To do: That might need a redistribution check as in cp_fm_syevd
    1083         1000 :       desca(:) = matrix_lu%matrix_struct%descriptor(:)
    1084         1000 :       descu(:) = matrix_eigvl%matrix_struct%descriptor(:)
    1085         1000 :       descvt(:) = matrix_eigvr_t%matrix_struct%descriptor(:)
    1086              : 
    1087              :       ! Workspace query
    1088              :       CALL pdgesvd('V', 'V', m, m, matrix_lu%local_data, 1, 1, desca, eigval, matrix_eigvl%local_data, &
    1089          100 :                    1, 1, descu, matrix_eigvr_t%local_data, 1, 1, descvt, work, lwork, myinfo)
    1090              : 
    1091          100 :       IF (matrix_lu%matrix_struct%para_env%is_source() .AND. (myinfo /= 0)) THEN
    1092            0 :          CALL cp_fm_error("ERROR in PDGESVD: Work space query failed", myinfo, PRESENT(info))
    1093              :       END IF
    1094              : 
    1095          100 :       lwork = NINT(work(1))
    1096          300 :       ALLOCATE (work(lwork))
    1097              : 
    1098              :       CALL pdgesvd('V', 'V', m, m, matrix_lu%local_data, 1, 1, desca, eigval, matrix_eigvl%local_data, &
    1099          100 :                    1, 1, descu, matrix_eigvr_t%local_data, 1, 1, descvt, work, lwork, myinfo)
    1100              : 
    1101          100 :       IF (matrix_lu%matrix_struct%para_env%is_source() .AND. (myinfo /= 0)) THEN
    1102            0 :          CALL cp_fm_error("ERROR in PDGESVD: Matrix diagonalization failed", myinfo, PRESENT(info))
    1103              :       END IF
    1104              : #else
    1105              :       ! Workspace query
    1106              :       CALL dgesvd('S', 'S', m, m, matrix_lu%local_data, m, eigval, matrix_eigvl%local_data, &
    1107              :                   m, matrix_eigvr_t%local_data, m, work, lwork, myinfo)
    1108              : 
    1109              :       IF (myinfo /= 0) THEN
    1110              :          CALL cp_fm_error("ERROR in DGESVD: Work space query failed", myinfo, PRESENT(info))
    1111              :       END IF
    1112              : 
    1113              :       lwork = NINT(work(1))
    1114              :       ALLOCATE (work(lwork))
    1115              : 
    1116              :       CALL dgesvd('S', 'S', m, m, matrix_lu%local_data, m, eigval, matrix_eigvl%local_data, &
    1117              :                   m, matrix_eigvr_t%local_data, m, work, lwork, myinfo)
    1118              : 
    1119              :       IF (myinfo /= 0) THEN
    1120              :          CALL cp_fm_error("ERROR in DGESVD: Matrix diagonalization failed", myinfo, PRESENT(info))
    1121              :       END IF
    1122              : 
    1123              : #endif
    1124              :       ! Release intermediary matrices
    1125          100 :       DEALLOCATE (work)
    1126          100 :       CALL cp_fm_release(matrix_lu)
    1127              : 
    1128          100 :       IF (PRESENT(info)) info = myinfo
    1129              : 
    1130          100 :       CALL timestop(handle)
    1131          100 :    END SUBROUTINE cp_fm_svd
    1132              : 
    1133              : ! **************************************************************************************************
    1134              : !> \brief ...
    1135              : !> \param matrix ...
    1136              : !> \param work ...
    1137              : !> \param exponent ...
    1138              : !> \param threshold ...
    1139              : !> \param n_dependent ...
    1140              : !> \param verbose ...
    1141              : !> \param eigvals ...
    1142              : ! **************************************************************************************************
    1143         3572 :    SUBROUTINE cp_fm_power(matrix, work, exponent, threshold, n_dependent, verbose, eigvals)
    1144              : 
    1145              :       ! Raise the real symmetric n by n matrix to the power given by
    1146              :       ! the exponent. All eigenvectors with a corresponding eigenvalue lower
    1147              :       ! than threshold are quenched. result in matrix
    1148              : 
    1149              :       ! - Creation (29.03.1999, Matthias Krack)
    1150              :       ! - Parallelised using BLACS and ScaLAPACK (06.06.2001,MK)
    1151              : 
    1152              :       TYPE(cp_fm_type), INTENT(IN)               :: matrix, work
    1153              :       REAL(KIND=dp), INTENT(IN)                  :: exponent, threshold
    1154              :       INTEGER, INTENT(OUT)                       :: n_dependent
    1155              :       LOGICAL, INTENT(IN), OPTIONAL              :: verbose
    1156              :       REAL(KIND=dp), DIMENSION(2), INTENT(OUT), &
    1157              :          OPTIONAL                                :: eigvals
    1158              : 
    1159              :       CHARACTER(LEN=*), PARAMETER                :: routineN = 'cp_fm_power'
    1160              : 
    1161              :       INTEGER                                    :: handle, icol_global, &
    1162              :                                                     mypcol, myprow, &
    1163              :                                                     ncol_global, nrow_global
    1164              :       LOGICAL                                    :: my_verbose
    1165              :       REAL(KIND=dp)                              :: condition_number, f, p
    1166              :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE   :: eigenvalues
    1167         3572 :       REAL(KIND=dp), DIMENSION(:, :), POINTER    :: eigenvectors
    1168              :       TYPE(cp_blacs_env_type), POINTER           :: context
    1169              : 
    1170              : #if defined(__parallel)
    1171              :       INTEGER           :: icol_local, ipcol, iprow, irow_global, irow_local
    1172              : #endif
    1173              : 
    1174         3572 :       CALL timeset(routineN, handle)
    1175              : 
    1176         3572 :       my_verbose = .FALSE.
    1177         3572 :       IF (PRESENT(verbose)) my_verbose = verbose
    1178              : 
    1179         3572 :       context => matrix%matrix_struct%context
    1180         3572 :       myprow = context%mepos(1)
    1181         3572 :       mypcol = context%mepos(2)
    1182         3572 :       n_dependent = 0
    1183         3572 :       p = 0.5_dp*exponent
    1184              : 
    1185         3572 :       nrow_global = matrix%matrix_struct%nrow_global
    1186         3572 :       ncol_global = matrix%matrix_struct%ncol_global
    1187              : 
    1188        10716 :       ALLOCATE (eigenvalues(ncol_global))
    1189         3572 :       eigenvalues(:) = 0.0_dp
    1190              : 
    1191              :       ! Compute the eigenvectors and eigenvalues
    1192              : 
    1193         3572 :       CALL choose_eigv_solver(matrix, work, eigenvalues)
    1194              : 
    1195         3572 :       IF (PRESENT(eigvals)) THEN
    1196          772 :          eigvals(1) = eigenvalues(1)
    1197          772 :          eigvals(2) = eigenvalues(ncol_global)
    1198              :       END IF
    1199              : 
    1200              : #if defined(__parallel)
    1201         3572 :       eigenvectors => work%local_data
    1202              : 
    1203              :       ! Build matrix**exponent with eigenvector quenching
    1204              : 
    1205        97603 :       DO icol_global = 1, ncol_global
    1206              : 
    1207        97603 :          IF (eigenvalues(icol_global) < threshold) THEN
    1208              : 
    1209           50 :             n_dependent = n_dependent + 1
    1210              : 
    1211           50 :             ipcol = work%matrix_struct%g2p_col(icol_global)
    1212              : 
    1213           50 :             IF (mypcol == ipcol) THEN
    1214           50 :                icol_local = work%matrix_struct%g2l_col(icol_global)
    1215         5850 :                DO irow_global = 1, nrow_global
    1216         5800 :                   iprow = work%matrix_struct%g2p_row(irow_global)
    1217         5850 :                   IF (myprow == iprow) THEN
    1218         2900 :                      irow_local = work%matrix_struct%g2l_row(irow_global)
    1219         2900 :                      eigenvectors(irow_local, icol_local) = 0.0_dp
    1220              :                   END IF
    1221              :                END DO
    1222              :             END IF
    1223              : 
    1224              :          ELSE
    1225              : 
    1226        93981 :             f = eigenvalues(icol_global)**p
    1227              : 
    1228        93981 :             ipcol = work%matrix_struct%g2p_col(icol_global)
    1229              : 
    1230        93981 :             IF (mypcol == ipcol) THEN
    1231        93119 :                icol_local = work%matrix_struct%g2l_col(icol_global)
    1232      5790054 :                DO irow_global = 1, nrow_global
    1233      5696935 :                   iprow = work%matrix_struct%g2p_row(irow_global)
    1234      5790054 :                   IF (myprow == iprow) THEN
    1235      3264064 :                      irow_local = work%matrix_struct%g2l_row(irow_global)
    1236              :                      eigenvectors(irow_local, icol_local) = &
    1237      3264064 :                         f*eigenvectors(irow_local, icol_local)
    1238              :                   END IF
    1239              :                END DO
    1240              :             END IF
    1241              : 
    1242              :          END IF
    1243              : 
    1244              :       END DO
    1245              : 
    1246              : #else
    1247              : 
    1248              :       eigenvectors => work%local_data
    1249              : 
    1250              :       ! Build matrix**exponent with eigenvector quenching
    1251              : 
    1252              :       DO icol_global = 1, ncol_global
    1253              : 
    1254              :          IF (eigenvalues(icol_global) < threshold) THEN
    1255              : 
    1256              :             n_dependent = n_dependent + 1
    1257              :             eigenvectors(1:nrow_global, icol_global) = 0.0_dp
    1258              : 
    1259              :          ELSE
    1260              : 
    1261              :             f = eigenvalues(icol_global)**p
    1262              :             eigenvectors(1:nrow_global, icol_global) = &
    1263              :                f*eigenvectors(1:nrow_global, icol_global)
    1264              : 
    1265              :          END IF
    1266              : 
    1267              :       END DO
    1268              : 
    1269              : #endif
    1270         3572 :       CALL cp_fm_syrk("U", "N", ncol_global, 1.0_dp, work, 1, 1, 0.0_dp, matrix)
    1271         3572 :       CALL cp_fm_uplo_to_full(matrix, work)
    1272              : 
    1273              :       ! Print some warnings/notes
    1274         3572 :       IF (matrix%matrix_struct%para_env%is_source() .AND. my_verbose) THEN
    1275            0 :          condition_number = ABS(eigenvalues(ncol_global)/eigenvalues(1))
    1276              :          WRITE (UNIT=cp_logger_get_default_unit_nr(), FMT="(/,(T2,A,ES15.6))") &
    1277            0 :             "CP_FM_POWER: smallest eigenvalue:", eigenvalues(1), &
    1278            0 :             "CP_FM_POWER: largest eigenvalue: ", eigenvalues(ncol_global), &
    1279            0 :             "CP_FM_POWER: condition number:   ", condition_number
    1280            0 :          IF (eigenvalues(1) <= 0.0_dp) THEN
    1281              :             WRITE (UNIT=cp_logger_get_default_unit_nr(), FMT="(/,T2,A)") &
    1282            0 :                "WARNING: matrix has a negative eigenvalue, tighten EPS_DEFAULT"
    1283              :          END IF
    1284            0 :          IF (condition_number > 1.0E12_dp) THEN
    1285              :             WRITE (UNIT=cp_logger_get_default_unit_nr(), FMT="(/,T2,A)") &
    1286            0 :                "WARNING: high condition number => possibly ill-conditioned matrix"
    1287              :          END IF
    1288              :       END IF
    1289              : 
    1290         3572 :       DEALLOCATE (eigenvalues)
    1291              : 
    1292         3572 :       CALL timestop(handle)
    1293              : 
    1294         3572 :    END SUBROUTINE cp_fm_power
    1295              : 
    1296              : ! **************************************************************************************************
    1297              : !> \brief ...
    1298              : !> \param matrix ...
    1299              : !> \param eigenvectors ...
    1300              : !> \param eigval ...
    1301              : !> \param thresh ...
    1302              : !> \param start_sec_block ...
    1303              : ! **************************************************************************************************
    1304           18 :    SUBROUTINE cp_fm_block_jacobi(matrix, eigenvectors, eigval, thresh, start_sec_block)
    1305              : 
    1306              :       ! Calculates block diagonalization of a full symmetric matrix
    1307              :       ! It has its origin in cp_fm_syevx. This routine rotates only elements
    1308              :       ! which are larger than a threshold values "thresh".
    1309              :       ! start_sec_block is the start of the second block.
    1310              :       ! IT DOES ONLY ONE SWEEP!
    1311              : 
    1312              :       ! - Creation (07.10.2002, Martin Fengler)
    1313              :       ! - Cosmetics (05.04.06, MK)
    1314              : 
    1315              :       TYPE(cp_fm_type), INTENT(IN)              :: eigenvectors, matrix
    1316              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)   :: eigval
    1317              :       INTEGER, INTENT(IN)                       :: start_sec_block
    1318              :       REAL(KIND=dp), INTENT(IN)                 :: thresh
    1319              : 
    1320              :       CHARACTER(len=*), PARAMETER               :: routineN = 'cp_fm_block_jacobi'
    1321              : 
    1322              :       INTEGER :: handle
    1323              :       REAL(KIND=dp), DIMENSION(:, :), POINTER   :: a, ev
    1324              : 
    1325              :       REAL(KIND=dp) :: tan_theta, tau, c, s
    1326              :       INTEGER  :: q, p, N
    1327           18 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE  :: c_ip
    1328              : 
    1329              : #if defined(__parallel)
    1330              :       TYPE(cp_blacs_env_type), POINTER :: context
    1331              : 
    1332              :       INTEGER :: nprow, npcol, block_dim_row, block_dim_col, info, &
    1333              :                  ev_row_block_size, iam, mynumrows, mype, npe, q_loc
    1334           18 :       REAL(KIND=dp), DIMENSION(:, :), ALLOCATABLE  :: a_loc, ev_loc
    1335              :       INTEGER, DIMENSION(9)                        :: desca, descz, &
    1336              :                                                       desc_a_block, &
    1337              :                                                       desc_ev_loc
    1338              :       TYPE(mp_comm_type):: allgrp
    1339              :       TYPE(cp_blacs_type) :: ictxt_loc
    1340              :       INTEGER, EXTERNAL :: numroc
    1341              : #endif
    1342              : 
    1343              :       ! -------------------------------------------------------------------------
    1344              : 
    1345           18 :       CALL timeset(routineN, handle)
    1346              : 
    1347              : #if defined(__parallel)
    1348           18 :       context => matrix%matrix_struct%context
    1349           18 :       allgrp = matrix%matrix_struct%para_env
    1350              : 
    1351           18 :       nprow = context%num_pe(1)
    1352           18 :       npcol = context%num_pe(2)
    1353              : 
    1354           18 :       N = matrix%matrix_struct%nrow_global
    1355              : 
    1356           18 :       A => matrix%local_data
    1357          180 :       desca(:) = matrix%matrix_struct%descriptor(:)
    1358           18 :       EV => eigenvectors%local_data
    1359          180 :       descz(:) = eigenvectors%matrix_struct%descriptor(:)
    1360              : 
    1361              :       ! Copy the block to be rotated to the master process firstly and broadcast to all processes
    1362              :       ! start_sec_block defines where the second block starts!
    1363              :       ! Block will be processed together with the OO block
    1364              : 
    1365           18 :       block_dim_row = start_sec_block - 1
    1366           18 :       block_dim_col = N - block_dim_row
    1367           72 :       ALLOCATE (A_loc(block_dim_row, block_dim_col))
    1368              : 
    1369           18 :       mype = matrix%matrix_struct%para_env%mepos
    1370           18 :       npe = matrix%matrix_struct%para_env%num_pe
    1371              :       ! Get a new context
    1372           18 :       CALL ictxt_loc%gridinit(matrix%matrix_struct%para_env, 'R', nprow*npcol, 1)
    1373              : 
    1374              :       CALL descinit(desc_a_block, block_dim_row, block_dim_col, block_dim_row, &
    1375           18 :                     block_dim_col, 0, 0, ictxt_loc%get_handle(), block_dim_row, info)
    1376              : 
    1377              :       CALL pdgemr2d(block_dim_row, block_dim_col, A, 1, start_sec_block, desca, &
    1378           18 :                     A_loc, 1, 1, desc_a_block, context%get_handle())
    1379              :       ! Only the master (root) process received data yet
    1380           18 :       CALL allgrp%bcast(A_loc, 0)
    1381              : 
    1382              :       ! Since each process owns now the upper block, the eigenvectors can be re-sorted in such a way that
    1383              :       ! each process has a NN*1 grid, i.e. the process owns a bunch of rows which can be modified locally
    1384              : 
    1385              :       ! Initialize distribution of the eigenvectors
    1386           18 :       iam = mype
    1387           18 :       ev_row_block_size = n/(nprow*npcol)
    1388           18 :       mynumrows = NUMROC(N, ev_row_block_size, iam, 0, nprow*npcol)
    1389              : 
    1390          108 :       ALLOCATE (EV_loc(mynumrows, N), c_ip(mynumrows))
    1391              : 
    1392              :       CALL descinit(desc_ev_loc, N, N, ev_row_block_size, N, 0, 0, ictxt_loc%get_handle(), &
    1393           18 :                     mynumrows, info)
    1394              : 
    1395           18 :       CALL pdgemr2d(N, N, EV, 1, 1, descz, EV_loc, 1, 1, desc_ev_loc, context%get_handle())
    1396              : 
    1397              :       ! Start block diagonalization of matrix
    1398              : 
    1399           18 :       q_loc = 0
    1400              : 
    1401         1170 :       DO q = start_sec_block, N
    1402         1152 :          q_loc = q_loc + 1
    1403       148626 :          DO p = 1, (start_sec_block - 1)
    1404              : 
    1405       148608 :             IF (ABS(A_loc(p, q_loc)) > thresh) THEN
    1406              : 
    1407       117566 :                tau = (eigval(q) - eigval(p))/(2.0_dp*A_loc(p, q_loc))
    1408              : 
    1409       117566 :                tan_theta = SIGN(1.0_dp, tau)/(ABS(tau) + SQRT(1.0_dp + tau*tau))
    1410              : 
    1411              :                ! Cos(theta)
    1412       117566 :                c = 1.0_dp/SQRT(1.0_dp + tan_theta*tan_theta)
    1413       117566 :                s = tan_theta*c
    1414              : 
    1415              :                ! Calculate eigenvectors: Q*J
    1416              :                ! c_ip = c*EV_loc(:,p) - s*EV_loc(:,q)
    1417              :                ! c_iq = s*EV_loc(:,p) + c*EV_loc(:,q)
    1418              :                ! EV(:,p) = c_ip
    1419              :                ! EV(:,q) = c_iq
    1420       117566 :                CALL dcopy(mynumrows, EV_loc(1, p), 1, c_ip(1), 1)
    1421       117566 :                CALL dscal(mynumrows, c, EV_loc(1, p), 1)
    1422       117566 :                CALL daxpy(mynumrows, -s, EV_loc(1, q), 1, EV_loc(1, p), 1)
    1423       117566 :                CALL dscal(mynumrows, c, EV_loc(1, q), 1)
    1424       117566 :                CALL daxpy(mynumrows, s, c_ip(1), 1, EV_loc(1, q), 1)
    1425              : 
    1426              :             END IF
    1427              : 
    1428              :          END DO
    1429              :       END DO
    1430              : 
    1431              :       ! Copy eigenvectors back to the original distribution
    1432           18 :       CALL pdgemr2d(N, N, EV_loc, 1, 1, desc_ev_loc, EV, 1, 1, descz, context%get_handle())
    1433              : 
    1434              :       ! Release work storage
    1435           18 :       DEALLOCATE (A_loc, EV_loc, c_ip)
    1436              : 
    1437           18 :       CALL ictxt_loc%gridexit()
    1438              : 
    1439              : #else
    1440              : 
    1441              :       N = matrix%matrix_struct%nrow_global
    1442              : 
    1443              :       ALLOCATE (c_ip(N)) ! Local eigenvalue vector
    1444              : 
    1445              :       A => matrix%local_data ! Contains the Matrix to be worked on
    1446              :       EV => eigenvectors%local_data ! Contains the eigenvectors up to blocksize, rest is garbage
    1447              : 
    1448              :       ! Start matrix diagonalization
    1449              : 
    1450              :       tan_theta = 0.0_dp
    1451              :       tau = 0.0_dp
    1452              : 
    1453              :       DO q = start_sec_block, N
    1454              :          DO p = 1, (start_sec_block - 1)
    1455              : 
    1456              :             IF (ABS(A(p, q)) > thresh) THEN
    1457              : 
    1458              :                tau = (eigval(q) - eigval(p))/(2.0_dp*A(p, q))
    1459              : 
    1460              :                tan_theta = SIGN(1.0_dp, tau)/(ABS(tau) + SQRT(1.0_dp + tau*tau))
    1461              : 
    1462              :                ! Cos(theta)
    1463              :                c = 1.0_dp/SQRT(1.0_dp + tan_theta*tan_theta)
    1464              :                s = tan_theta*c
    1465              : 
    1466              :                ! Calculate eigenvectors: Q*J
    1467              :                ! c_ip = c*EV(:,p) - s*EV(:,q)
    1468              :                ! c_iq = s*EV(:,p) + c*EV(:,q)
    1469              :                ! EV(:,p) = c_ip
    1470              :                ! EV(:,q) = c_iq
    1471              :                CALL dcopy(N, EV(1, p), 1, c_ip(1), 1)
    1472              :                CALL dscal(N, c, EV(1, p), 1)
    1473              :                CALL daxpy(N, -s, EV(1, q), 1, EV(1, p), 1)
    1474              :                CALL dscal(N, c, EV(1, q), 1)
    1475              :                CALL daxpy(N, s, c_ip(1), 1, EV(1, q), 1)
    1476              : 
    1477              :             END IF
    1478              : 
    1479              :          END DO
    1480              :       END DO
    1481              : 
    1482              :       ! Release work storage
    1483              : 
    1484              :       DEALLOCATE (c_ip)
    1485              : 
    1486              : #endif
    1487              : 
    1488           18 :       CALL timestop(handle)
    1489              : 
    1490           90 :    END SUBROUTINE cp_fm_block_jacobi
    1491              : 
    1492              : ! **************************************************************************************************
    1493              : !> \brief General Eigenvalue Problem AX = BXE.
    1494              : !>        Use cuSOLVERMp directly when requested and large enough; otherwise
    1495              : !>        reduce the problem through a Cholesky decomposition of B.
    1496              : !> \param amatrix ...
    1497              : !> \param bmatrix ...
    1498              : !> \param eigenvectors ...
    1499              : !> \param eigenvalues ...
    1500              : !> \param work ...
    1501              : ! **************************************************************************************************
    1502         1276 :    SUBROUTINE cp_fm_geeig(amatrix, bmatrix, eigenvectors, eigenvalues, work)
    1503              : 
    1504              :       TYPE(cp_fm_type), INTENT(IN)                       :: amatrix, bmatrix, eigenvectors
    1505              :       REAL(KIND=dp), DIMENSION(:)                        :: eigenvalues
    1506              :       TYPE(cp_fm_type), INTENT(IN)                       :: work
    1507              : 
    1508              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_fm_geeig'
    1509              : 
    1510              :       INTEGER                                            :: handle, nao, nmo
    1511              :       LOGICAL                                            :: check_eigenvectors
    1512              :       TYPE(cp_fm_type)                                   :: overlap_check, scratch_check
    1513              : 
    1514         1276 :       CALL timeset(routineN, handle)
    1515              : 
    1516         1276 :       CALL cp_fm_get_info(amatrix, nrow_global=nao)
    1517         1276 :       nmo = SIZE(eigenvalues)
    1518         1276 :       check_eigenvectors = diag_check_requested()
    1519              : 
    1520         1276 :       IF (diag_type == FM_DIAG_TYPE_CUSOLVER .AND. direct_generalized_diagonalization .AND. &
    1521              :           nao >= cusolver_n_min) THEN
    1522              :          ! Use cuSolverMP generalized eigenvalue solver without a CP2K-side
    1523              :          ! Cholesky reduction.
    1524              :          ! Use work as intermediate buffer since eigenvectors may be smaller (nao x nmo)
    1525            0 :          IF (check_eigenvectors) THEN
    1526            0 :             CALL cp_fm_create(overlap_check, bmatrix%matrix_struct)
    1527            0 :             CALL cp_fm_create(scratch_check, bmatrix%matrix_struct)
    1528            0 :             CALL cp_fm_to_fm(bmatrix, overlap_check)
    1529              :          END IF
    1530            0 :          CALL cp_fm_general_cusolver(amatrix, bmatrix, work, eigenvalues)
    1531            0 :          IF (check_eigenvectors) THEN
    1532            0 :             CALL check_generalized_diag(overlap_check, work, scratch_check, nmo)
    1533            0 :             CALL cp_fm_release(scratch_check)
    1534            0 :             CALL cp_fm_release(overlap_check)
    1535              :          END IF
    1536            0 :          CALL cp_fm_to_fm(work, eigenvectors, nmo)
    1537              : #if defined(__parallel)
    1538         1276 :       ELSE IF (diag_type == FM_DIAG_TYPE_SCALAPACK .AND. direct_generalized_diagonalization) THEN
    1539              :          ! Use ScaLAPACK generalized eigenvalue solver without a CP2K-side
    1540              :          ! Cholesky reduction.
    1541            2 :          IF (check_eigenvectors) THEN
    1542            2 :             CALL cp_fm_create(overlap_check, bmatrix%matrix_struct)
    1543            2 :             CALL cp_fm_create(scratch_check, bmatrix%matrix_struct)
    1544            2 :             CALL cp_fm_to_fm(bmatrix, overlap_check)
    1545              :          END IF
    1546            2 :          CALL cp_fm_geeig_scalapack(amatrix, bmatrix, work, eigenvalues)
    1547            2 :          IF (check_eigenvectors) THEN
    1548            2 :             CALL check_generalized_diag(overlap_check, work, scratch_check, nmo)
    1549            2 :             CALL cp_fm_release(scratch_check)
    1550            2 :             CALL cp_fm_release(overlap_check)
    1551              :          END IF
    1552            2 :          CALL cp_fm_to_fm(work, eigenvectors, nmo)
    1553              : #endif
    1554              : #if defined(__DLAF)
    1555              :       ELSE IF (diag_type == FM_DIAG_TYPE_DLAF .AND. direct_generalized_diagonalization .AND. &
    1556              :                nao >= dlaf_neigvec_min) THEN
    1557              :          ! Use DLA-Future generalized eigenvalue solver for large matrices
    1558              :          IF (check_eigenvectors) THEN
    1559              :             CALL cp_fm_create(overlap_check, bmatrix%matrix_struct)
    1560              :             CALL cp_fm_create(scratch_check, bmatrix%matrix_struct)
    1561              :             CALL cp_fm_to_fm(bmatrix, overlap_check)
    1562              :          END IF
    1563              :          CALL cp_fm_diag_gen_dlaf(amatrix, bmatrix, work, eigenvalues)
    1564              :          IF (check_eigenvectors) THEN
    1565              :             CALL check_generalized_diag(overlap_check, work, scratch_check, nmo)
    1566              :             CALL cp_fm_release(scratch_check)
    1567              :             CALL cp_fm_release(overlap_check)
    1568              :          END IF
    1569              :          CALL cp_fm_to_fm(work, eigenvectors, nmo)
    1570              : #endif
    1571              :       ELSE
    1572              :          ! Cholesky decompose S=U(T)U
    1573         1274 :          CALL cp_fm_cholesky_decompose(bmatrix)
    1574              :          ! Invert to get U^(-1)
    1575         1274 :          CALL cp_fm_triangular_invert(bmatrix)
    1576              :          ! Reduce to get U^(-T) * H * U^(-1)
    1577         1274 :          CALL cp_fm_triangular_multiply(bmatrix, amatrix, side="R")
    1578         1274 :          CALL cp_fm_triangular_multiply(bmatrix, amatrix, transpose_tr=.TRUE.)
    1579              :          ! Diagonalize
    1580              :          CALL choose_eigv_solver(matrix=amatrix, eigenvectors=work, &
    1581         1274 :                                  eigenvalues=eigenvalues)
    1582              :          ! Restore vectors C = U^(-1) * C*
    1583         1274 :          CALL cp_fm_triangular_multiply(bmatrix, work)
    1584         1274 :          CALL cp_fm_to_fm(work, eigenvectors, nmo)
    1585              :       END IF
    1586              : 
    1587         1276 :       CALL timestop(handle)
    1588              : 
    1589         1276 :    END SUBROUTINE cp_fm_geeig
    1590              : 
    1591              : ! **************************************************************************************************
    1592              : !> \brief General Eigenvalue Problem AX = BXE using ScaLAPACK PDSYGVX.
    1593              : !> \param amatrix ...
    1594              : !> \param bmatrix ...
    1595              : !> \param eigenvectors ...
    1596              : !> \param eigenvalues ...
    1597              : ! **************************************************************************************************
    1598            2 :    SUBROUTINE cp_fm_geeig_scalapack(amatrix, bmatrix, eigenvectors, eigenvalues)
    1599              : 
    1600              :       TYPE(cp_fm_type), INTENT(IN)                       :: amatrix, bmatrix, eigenvectors
    1601              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: eigenvalues
    1602              : 
    1603              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_fm_geeig_scalapack'
    1604              : 
    1605              : #if defined(__parallel)
    1606              :       REAL(KIND=dp), PARAMETER                           :: orfac = -1.0_dp, &
    1607              :                                                             vl = 0.0_dp, &
    1608              :                                                             vu = 0.0_dp
    1609              : 
    1610              :       INTEGER                                            :: handle, info, liwork, lwork, m, n, nb, &
    1611              :                                                             neig, npcol, nprow, nz
    1612              :       INTEGER, DIMENSION(9)                              :: desca, descb, descz
    1613            2 :       INTEGER, DIMENSION(:), ALLOCATABLE                 :: iclustr, ifail, iwork
    1614              :       REAL(KIND=dp)                                      :: abstol
    1615            2 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE           :: gap, w, work
    1616            2 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: a, b, z
    1617              : 
    1618              :       INTEGER                                            :: mq0, nn, np0, npe
    1619              :       INTEGER, EXTERNAL                                  :: iceil, numroc
    1620              :       REAL(KIND=dp), EXTERNAL                            :: dlamch
    1621              : #if defined (__HAS_IEEE_EXCEPTIONS)
    1622              :       LOGICAL, DIMENSION(5)                              :: halt
    1623              : #endif
    1624              : #else
    1625              :       INTEGER                                            :: handle
    1626              : #endif
    1627              : 
    1628            2 :       CALL timeset(routineN, handle)
    1629              : 
    1630              : #if defined(__parallel)
    1631            2 :       n = amatrix%matrix_struct%nrow_global
    1632            2 :       neig = MIN(SIZE(eigenvalues), n)
    1633              : 
    1634            2 :       IF (neig == 0) THEN
    1635            0 :          CALL timestop(handle)
    1636            0 :          RETURN
    1637              :       END IF
    1638              : 
    1639            2 :       IF (amatrix%matrix_struct%nrow_block /= amatrix%matrix_struct%ncol_block) THEN
    1640            0 :          CPABORT("ERROR in "//routineN//": Invalid blocksize (no square blocks) found")
    1641              :       END IF
    1642              : 
    1643            2 :       a => amatrix%local_data
    1644            2 :       b => bmatrix%local_data
    1645            2 :       z => eigenvectors%local_data
    1646           20 :       desca(:) = amatrix%matrix_struct%descriptor(:)
    1647           20 :       descb(:) = bmatrix%matrix_struct%descriptor(:)
    1648           20 :       descz(:) = eigenvectors%matrix_struct%descriptor(:)
    1649              : 
    1650            2 :       nprow = amatrix%matrix_struct%context%num_pe(1)
    1651            2 :       npcol = amatrix%matrix_struct%context%num_pe(2)
    1652            2 :       npe = nprow*npcol
    1653            2 :       nb = amatrix%matrix_struct%nrow_block
    1654            2 :       nn = MAX(n, nb, 2)
    1655            2 :       np0 = numroc(nn, nb, 0, 0, nprow)
    1656            2 :       mq0 = MAX(numroc(nn, nb, 0, 0, npcol), nb)
    1657              : 
    1658              :       lwork = 5*n + MAX(5*nn, np0*mq0 + 2*nb*nb) + iceil(neig, npe)*nn + &
    1659            2 :               MAX(0, neig - 1)*n
    1660            2 :       liwork = 6*MAX(n, npe + 1, 4)
    1661              : 
    1662            6 :       ALLOCATE (gap(npe))
    1663            2 :       gap = 0.0_dp
    1664            6 :       ALLOCATE (iclustr(2*npe))
    1665            2 :       iclustr = 0
    1666            6 :       ALLOCATE (ifail(n))
    1667            2 :       ifail = 0
    1668            6 :       ALLOCATE (iwork(liwork))
    1669            6 :       ALLOCATE (w(n))
    1670            6 :       ALLOCATE (work(lwork))
    1671              : 
    1672            2 :       abstol = 2.0_dp*dlamch("S")
    1673              : 
    1674              : #if defined (__HAS_IEEE_EXCEPTIONS)
    1675              :       CALL ieee_get_halting_mode(IEEE_ALL, halt)
    1676              :       CALL ieee_set_halting_mode(IEEE_ALL, .FALSE.)
    1677              : #endif
    1678              :       CALL pdsygvx(1, "V", "I", "U", n, a(1, 1), 1, 1, desca, b(1, 1), 1, 1, descb, &
    1679              :                    vl, vu, 1, neig, abstol, m, nz, w(1), orfac, z(1, 1), 1, 1, descz, &
    1680            2 :                    work(1), lwork, iwork(1), liwork, ifail(1), iclustr(1), gap(1), info)
    1681              : #if defined (__HAS_IEEE_EXCEPTIONS)
    1682              :       CALL ieee_set_halting_mode(IEEE_ALL, halt)
    1683              : #endif
    1684              : 
    1685            2 :       IF (info /= 0 .OR. m < neig .OR. nz < neig) THEN
    1686            0 :          CPABORT("ERROR in PDSYGVX (ScaLAPACK), info="//TRIM(cp_to_string(info)))
    1687              :       END IF
    1688              : 
    1689           34 :       eigenvalues(:) = 0.0_dp
    1690           34 :       eigenvalues(1:neig) = w(1:neig)
    1691              : 
    1692            2 :       DEALLOCATE (gap, iclustr, ifail, iwork, w, work)
    1693              : #else
    1694              :       MARK_USED(amatrix)
    1695              :       MARK_USED(bmatrix)
    1696              :       MARK_USED(eigenvectors)
    1697              :       MARK_USED(eigenvalues)
    1698              :       CPABORT("ERROR in "//routineN//": PDSYGVX requested without ScaLAPACK support")
    1699              : #endif
    1700              : 
    1701            2 :       CALL timestop(handle)
    1702              : 
    1703            2 :    END SUBROUTINE cp_fm_geeig_scalapack
    1704              : 
    1705              : ! **************************************************************************************************
    1706              : !> \brief General Eigenvalue Problem  AX = BXE
    1707              : !>        Use canonical diagonalization : U*s**(-1/2)
    1708              : !> \param amatrix ...
    1709              : !> \param bmatrix ...
    1710              : !> \param eigenvectors ...
    1711              : !> \param eigenvalues ...
    1712              : !> \param work ...
    1713              : !> \param epseig ...
    1714              : !> \param nmo_retained ...
    1715              : ! **************************************************************************************************
    1716          160 :    SUBROUTINE cp_fm_geeig_canon(amatrix, bmatrix, eigenvectors, eigenvalues, work, epseig, &
    1717              :                                 nmo_retained)
    1718              : 
    1719              :       TYPE(cp_fm_type), INTENT(IN)                       :: amatrix, bmatrix, eigenvectors
    1720              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: eigenvalues
    1721              :       TYPE(cp_fm_type), INTENT(IN)                       :: work
    1722              :       REAL(KIND=dp), INTENT(IN)                          :: epseig
    1723              :       INTEGER, INTENT(OUT), OPTIONAL                     :: nmo_retained
    1724              : 
    1725              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_fm_geeig_canon'
    1726              : 
    1727              :       INTEGER                                            :: handle, i, icol, irow, nao, nc, ncol, &
    1728              :                                                             nmo, nx
    1729              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: evals
    1730              : 
    1731          160 :       CALL timeset(routineN, handle)
    1732              : 
    1733              :       ! Test sizees
    1734          160 :       CALL cp_fm_get_info(amatrix, nrow_global=nao)
    1735          160 :       nmo = SIZE(eigenvalues)
    1736          480 :       ALLOCATE (evals(nao))
    1737              : 
    1738              :       ! Diagonalize -S matrix, this way the NULL space is at the end of the spectrum
    1739          160 :       CALL cp_fm_scale(-1.0_dp, bmatrix)
    1740          160 :       CALL choose_eigv_solver(matrix=bmatrix, eigenvectors=work, eigenvalues=evals)
    1741         5816 :       evals(:) = -evals(:)
    1742          160 :       nc = nao
    1743         5524 :       DO i = 1, nao
    1744         5524 :          IF (evals(i) < epseig) THEN
    1745           40 :             nc = i - 1
    1746           40 :             EXIT
    1747              :          END IF
    1748              :       END DO
    1749          160 :       CPASSERT(nc /= 0)
    1750              : 
    1751          160 :       IF (nc /= nao) THEN
    1752           40 :          IF (nc < nmo) THEN
    1753              :             ! Copy NULL space definition to last vectors of eigenvectors (if needed)
    1754            0 :             ncol = nmo - nc
    1755            0 :             CALL cp_fm_to_fm(work, eigenvectors, ncol, nc + 1, nc + 1)
    1756              :          END IF
    1757              :          ! Set NULL space in eigenvector matrix of S to zero
    1758          332 :          DO icol = nc + 1, nao
    1759        36172 :             DO irow = 1, nao
    1760        36132 :                CALL cp_fm_set_element(work, irow, icol, 0.0_dp)
    1761              :             END DO
    1762              :          END DO
    1763              :          ! Set small eigenvalues to a dummy save value
    1764          332 :          evals(nc + 1:nao) = 1.0_dp
    1765              :       END IF
    1766              :       ! Calculate U*s**(-1/2)
    1767         5816 :       evals(:) = 1.0_dp/SQRT(evals(:))
    1768          160 :       CALL cp_fm_column_scale(work, evals)
    1769              :       ! Reduce to get U^(-T) * H * U^(-1)
    1770          160 :       CALL cp_fm_gemm("T", "N", nao, nao, nao, 1.0_dp, work, amatrix, 0.0_dp, bmatrix)
    1771          160 :       CALL cp_fm_gemm("N", "N", nao, nao, nao, 1.0_dp, bmatrix, work, 0.0_dp, amatrix)
    1772          160 :       IF (nc /= nao) THEN
    1773              :          ! set diagonal values to save large value
    1774          332 :          DO icol = nc + 1, nao
    1775          332 :             CALL cp_fm_set_element(amatrix, icol, icol, set_removed_eigval_to)
    1776              :          END DO
    1777              :       END IF
    1778              :       ! Diagonalize
    1779          160 :       CALL choose_eigv_solver(matrix=amatrix, eigenvectors=bmatrix, eigenvalues=eigenvalues)
    1780          160 :       nx = MIN(nc, nmo)
    1781              :       ! Restore vectors C = U^(-1) * C*
    1782          160 :       CALL cp_fm_gemm("N", "N", nao, nx, nc, 1.0_dp, work, bmatrix, 0.0_dp, eigenvectors)
    1783              : 
    1784              :       ! Number of basis modes that survived the linear-dependency filter. The remaining
    1785              :       ! nao - nc entries of eigenvalues(:) are the placeholders set above.
    1786          160 :       IF (PRESENT(nmo_retained)) nmo_retained = nc
    1787              : 
    1788          160 :       DEALLOCATE (evals)
    1789              : 
    1790          160 :       CALL timestop(handle)
    1791              : 
    1792          160 :    END SUBROUTINE cp_fm_geeig_canon
    1793              : 
    1794              : END MODULE cp_fm_diag
        

Generated by: LCOV version 2.0-1