LCOV - code coverage report
Current view: top level - src/fm - cp_cfm_diag.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:92574dc) Lines: 85.4 % 288 246
Test Date: 2026-09-24 01:27:39 Functions: 83.3 % 12 10

            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 diagonalization schemes available for cp_cfm_type
      10              : !> \note
      11              : !>      first version : only one routine right now
      12              : !> \author Joost VandeVondele (2003-09)
      13              : ! **************************************************************************************************
      14              : MODULE cp_cfm_diag
      15              :    USE cp_blacs_env, ONLY: cp_blacs_env_type
      16              :    USE cp_cfm_cholesky, ONLY: cp_cfm_cholesky_decompose
      17              :    USE cp_cfm_basic_linalg, ONLY: cp_cfm_gemm, &
      18              :                                   cp_cfm_column_scale, &
      19              :                                   cp_cfm_scale, &
      20              :                                   cp_cfm_triangular_invert, &
      21              :                                   cp_cfm_triangular_multiply
      22              :    USE cp_cfm_types, ONLY: cp_cfm_create, &
      23              :                            cp_cfm_get_info, &
      24              :                            cp_cfm_release, &
      25              :                            cp_cfm_set_element, &
      26              :                            cp_cfm_to_cfm, &
      27              :                            cp_cfm_type
      28              :    USE cp_fm_diag, ONLY: diag_check_requested, &
      29              :                          diag_check_warning_threshold, &
      30              :                          diag_lib_explicit, &
      31              :                          diag_type, &
      32              :                          direct_generalized_diagonalization, &
      33              :                          cusolver_n_min, &
      34              :                          elpa_neigvec_min, &
      35              :                          FM_DIAG_TYPE_CUSOLVER, &
      36              :                          FM_DIAG_TYPE_ELPA, &
      37              :                          FM_DIAG_TYPE_SCALAPACK, &
      38              :                          set_removed_eigval_to
      39              :    USE cp_cfm_elpa, ONLY: cp_cfm_diag_elpa, &
      40              :                           is_elpa_c_broken
      41              :    USE cp_fm_cusolver_api, ONLY: cp_cfm_general_cusolver
      42              : #if defined(__DLAF)
      43              :    USE cp_cfm_dlaf_api, ONLY: cp_cfm_diag_gen_dlaf, &
      44              :                               cp_cfm_diag_dlaf
      45              :    USE cp_dlaf_utils_api, ONLY: cp_dlaf_initialize, cp_dlaf_create_grid
      46              :    USE cp_fm_diag, ONLY: dlaf_neigvec_min, FM_DIAG_TYPE_DLAF
      47              : #endif
      48              :    USE cp_log_handling, ONLY: cp_to_string
      49              :    USE kinds, ONLY: default_string_length, &
      50              :                     dp
      51              :    USE machine, ONLY: default_output_unit
      52              :    USE mathconstants, ONLY: z_one, &
      53              :                             z_zero
      54              : #if defined (__HAS_IEEE_EXCEPTIONS)
      55              :    USE ieee_exceptions, ONLY: ieee_get_halting_mode, &
      56              :                               ieee_set_halting_mode, &
      57              :                               IEEE_ALL
      58              : #endif
      59              : #include "../base/base_uses.f90"
      60              : 
      61              :    IMPLICIT NONE
      62              :    PRIVATE
      63              : 
      64              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_cfm_diag'
      65              : 
      66              :    PUBLIC :: cp_cfm_heevd, cp_cfm_geeig, cp_cfm_geeig_canon, &
      67              :              cp_cfm_geeig_local, cp_cfm_geeig_canon_local
      68              : 
      69              :    ! Caller-owned LAPACK scratch: no module SAVE state and no cross-worker sharing.
      70              :    TYPE, PUBLIC :: cp_cfm_diag_workspace_type
      71              :       INTEGER :: n = 0
      72              :       LOGICAL :: generalized = .FALSE.
      73              :       COMPLEX(KIND=dp), ALLOCATABLE :: work(:)
      74              :       REAL(KIND=dp), ALLOCATABLE :: evals(:), rwork(:)
      75              :       INTEGER, ALLOCATABLE :: iwork(:)
      76              :    END TYPE cp_cfm_diag_workspace_type
      77              :    PUBLIC :: cp_cfm_local_workspace_prepare
      78              : 
      79              : CONTAINS
      80              : 
      81              : ! **************************************************************************************************
      82              : !> \brief Perform a diagonalisation of a complex matrix
      83              : !> \param matrix ...
      84              : !> \param eigenvectors ...
      85              : !> \param eigenvalues ...
      86              : !> \par History
      87              : !>      12.2024 Added DLA-Future support [Rocco Meli]
      88              : !>      08.2026 Added ELPA support
      89              : !> \author Joost VandeVondele
      90              : ! **************************************************************************************************
      91        64797 :    SUBROUTINE cp_cfm_heevd(matrix, eigenvectors, eigenvalues)
      92              : 
      93              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix, eigenvectors
      94              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: eigenvalues
      95              : 
      96              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_cfm_heevd'
      97              : 
      98              :       INTEGER                                            :: handle
      99              : 
     100        64797 :       CALL timeset(routineN, handle)
     101              : 
     102              : #if defined(__DLAF)
     103              :       IF (diag_type == FM_DIAG_TYPE_DLAF .AND. matrix%matrix_struct%nrow_global >= dlaf_neigvec_min) THEN
     104              :          ! Initialize DLA-Future on-demand; if already initialized, does nothing
     105              :          CALL cp_dlaf_initialize()
     106              : 
     107              :          ! Create DLAF grid from BLACS context; if already present, does nothing
     108              :          CALL cp_dlaf_create_grid(matrix%matrix_struct%context%get_handle())
     109              : 
     110              :          CALL cp_cfm_diag_dlaf(matrix, eigenvectors, eigenvalues)
     111              :       ELSE
     112              : #endif
     113              :          ! We don't trust ELPA with very small matrices and use it for complex matrices
     114              :          ! only when the diagonalization library was requested explicitly.
     115              :          ! A runtime correctness check may have disabled ELPA for mis-compiled BLOCK2 kernels.
     116              :          IF (diag_type == FM_DIAG_TYPE_ELPA .AND. diag_lib_explicit .AND. &
     117        64797 :              .NOT. is_elpa_c_broken() .AND. &
     118              :              matrix%matrix_struct%nrow_global >= elpa_neigvec_min) THEN
     119           72 :             CALL cp_cfm_diag_elpa(matrix, eigenvectors, eigenvalues)
     120              :          ELSE
     121        64725 :             CALL cp_cfm_heevd_base(matrix, eigenvectors, eigenvalues)
     122              :          END IF
     123              : #if defined(__DLAF)
     124              :       END IF
     125              : #endif
     126              : 
     127        64797 :       CALL timestop(handle)
     128              : 
     129        64797 :    END SUBROUTINE cp_cfm_heevd
     130              : 
     131              : ! **************************************************************************************************
     132              : !> \brief Perform a diagonalisation of a complex matrix
     133              : !> \param matrix ...
     134              : !> \param eigenvectors ...
     135              : !> \param eigenvalues ...
     136              : !> \par History
     137              : !>      - (De)Allocation checks updated (15.02.2011,MK)
     138              : !> \author Joost VandeVondele
     139              : ! **************************************************************************************************
     140        64725 :    SUBROUTINE cp_cfm_heevd_base(matrix, eigenvectors, eigenvalues)
     141              : 
     142              :       TYPE(cp_cfm_type), INTENT(IN)            :: matrix, eigenvectors
     143              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: eigenvalues
     144              : 
     145              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_heevd_base'
     146              : 
     147        64725 :       COMPLEX(KIND=dp), DIMENSION(:), POINTER  :: work
     148              :       COMPLEX(KIND=dp), DIMENSION(:, :), &
     149        64725 :          POINTER                               :: m
     150              :       INTEGER                                  :: handle, info, liwork, &
     151              :                                                   lrwork, lwork, n
     152        64725 :       INTEGER, DIMENSION(:), POINTER           :: iwork
     153        64725 :       REAL(KIND=dp), DIMENSION(:), POINTER     :: rwork
     154              : #if defined(__parallel)
     155              :       INTEGER, DIMENSION(9)                    :: descm, descv
     156              :       COMPLEX(KIND=dp), DIMENSION(:, :), &
     157        64725 :          POINTER                               :: v
     158              : #endif
     159              : #if defined (__HAS_IEEE_EXCEPTIONS)
     160              :       LOGICAL, DIMENSION(5)                    :: halt
     161              : #endif
     162              : 
     163        64725 :       CALL timeset(routineN, handle)
     164              : 
     165        64725 :       n = matrix%matrix_struct%nrow_global
     166        64725 :       m => matrix%local_data
     167        64725 :       ALLOCATE (iwork(1), rwork(1), work(1))
     168              :       ! work space query
     169        64725 :       lwork = -1
     170        64725 :       lrwork = -1
     171        64725 :       liwork = -1
     172              : 
     173              : #if defined(__parallel)
     174        64725 :       v => eigenvectors%local_data
     175       647250 :       descm(:) = matrix%matrix_struct%descriptor(:)
     176       647250 :       descv(:) = eigenvectors%matrix_struct%descriptor(:)
     177              :       CALL pzheevd('V', 'U', n, m(1, 1), 1, 1, descm, eigenvalues(1), v(1, 1), 1, 1, descv, &
     178        64725 :                    work(1), lwork, rwork(1), lrwork, iwork(1), liwork, info)
     179              :       ! The work space query for lwork does not return always sufficiently large values.
     180              :       ! Let's add some margin to avoid crashes.
     181        64725 :       lwork = CEILING(REAL(work(1), KIND=dp)) + 1000
     182              :       ! needed to correct for a bug in scalapack, unclear how much the right number is
     183        64725 :       lrwork = CEILING(rwork(1)) + 1000000
     184        64725 :       liwork = iwork(1)
     185              : #else
     186              :       CALL zheevd('V', 'U', n, m(1, 1), SIZE(m, 1), eigenvalues(1), &
     187              :                   work(1), lwork, rwork(1), lrwork, iwork(1), liwork, info)
     188              :       lwork = CEILING(REAL(work(1), KIND=dp))
     189              :       lrwork = CEILING(rwork(1))
     190              :       liwork = iwork(1)
     191              : #endif
     192              : 
     193        64725 :       DEALLOCATE (iwork, rwork, work)
     194       453075 :       ALLOCATE (iwork(liwork), rwork(lrwork), work(lwork))
     195              : 
     196              : ! (Sca-)LAPACK takes advantage of IEEE754 exceptions for speedup.
     197              : ! Therefore, we disable floating point traps temporarily.
     198              : #if defined (__HAS_IEEE_EXCEPTIONS)
     199              :       CALL ieee_get_halting_mode(IEEE_ALL, halt)
     200              :       CALL ieee_set_halting_mode(IEEE_ALL, .FALSE.)
     201              : #endif
     202              : #if defined(__parallel)
     203              :       CALL pzheevd('V', 'U', n, m(1, 1), 1, 1, descm, eigenvalues(1), v(1, 1), 1, 1, descv, &
     204        64725 :                    work(1), lwork, rwork(1), lrwork, iwork(1), liwork, info)
     205              : #else
     206              :       CALL zheevd('V', 'U', n, m(1, 1), SIZE(m, 1), eigenvalues(1), &
     207              :                   work(1), lwork, rwork(1), lrwork, iwork(1), liwork, info)
     208              :       eigenvectors%local_data = matrix%local_data
     209              : #endif
     210              : #if defined (__HAS_IEEE_EXCEPTIONS)
     211              :       CALL ieee_set_halting_mode(IEEE_ALL, halt)
     212              : #endif
     213              : 
     214        64725 :       DEALLOCATE (iwork, rwork, work)
     215        64725 :       IF (info /= 0) CPABORT("Diagonalisation of a complex matrix failed")
     216              : 
     217        64725 :       CALL timestop(handle)
     218              : 
     219        64725 :    END SUBROUTINE cp_cfm_heevd_base
     220              : 
     221              : ! **************************************************************************************************
     222              : !> \brief   Check C^H*S*C = I for a generalized complex eigenvalue problem.
     223              : !> \param overlap original overlap matrix S; used as work matrix and overwritten
     224              : !> \param eigenvectors eigenvectors C to be checked
     225              : !> \param scratch work matrix
     226              : !> \param nvec ...
     227              : ! **************************************************************************************************
     228           16 :    SUBROUTINE check_generalized_diag(overlap, eigenvectors, scratch, nvec)
     229              : 
     230              :       TYPE(cp_cfm_type), INTENT(IN)                      :: eigenvectors
     231              :       TYPE(cp_cfm_type), INTENT(INOUT)                   :: overlap, scratch
     232              :       INTEGER, INTENT(IN)                                :: nvec
     233              : 
     234              :       CHARACTER(LEN=*), PARAMETER                        :: routineN = 'check_generalized_diag'
     235              : 
     236              :       CHARACTER(LEN=default_string_length)               :: diag_type_name
     237              :       COMPLEX(KIND=dp)                                   :: gold, test
     238              :       INTEGER                                            :: handle, i, j, ncol, nrow, output_unit
     239              :       REAL(KIND=dp)                                      :: eps, eps_abort, eps_warning
     240              : #if defined(__parallel)
     241              :       TYPE(cp_blacs_env_type), POINTER                   :: context
     242              :       INTEGER                                            :: il, jl, ipcol, iprow, &
     243              :                                                             mypcol, myprow, npcol, nprow
     244              :       INTEGER, DIMENSION(9)                              :: desca
     245              : #endif
     246              : 
     247           16 :       CALL timeset(routineN, handle)
     248              : 
     249           16 :       IF (.NOT. diag_check_requested()) THEN
     250            0 :          CALL timestop(handle)
     251            0 :          RETURN
     252              :       END IF
     253              : 
     254           16 :       output_unit = default_output_unit
     255           16 :       eps_warning = diag_check_warning_threshold()
     256           16 :       eps_abort = 10.0_dp*eps_warning
     257              : 
     258           16 :       nrow = eigenvectors%matrix_struct%nrow_global
     259           16 :       ncol = MIN(eigenvectors%matrix_struct%ncol_global, nvec)
     260              : 
     261           16 :       CALL cp_cfm_gemm("N", "N", nrow, ncol, nrow, z_one, overlap, eigenvectors, z_zero, scratch)
     262           16 :       CALL cp_cfm_gemm("C", "N", ncol, ncol, nrow, z_one, eigenvectors, scratch, z_zero, overlap)
     263              : 
     264           16 :       gold = z_zero
     265           16 :       test = z_zero
     266           16 :       eps = 0.0_dp
     267              : 
     268              : #if defined(__parallel)
     269           16 :       context => overlap%matrix_struct%context
     270           16 :       myprow = context%mepos(1)
     271           16 :       mypcol = context%mepos(2)
     272           16 :       nprow = context%num_pe(1)
     273           16 :       npcol = context%num_pe(2)
     274          160 :       desca(:) = overlap%matrix_struct%descriptor(:)
     275          160 :       outer: DO j = 1, ncol
     276         1456 :          DO i = 1, ncol
     277         1296 :             CALL infog2l(i, j, desca, nprow, npcol, myprow, mypcol, il, jl, iprow, ipcol)
     278         1440 :             IF ((iprow == myprow) .AND. (ipcol == mypcol)) THEN
     279          648 :                gold = MERGE(z_zero, z_one, i /= j)
     280          648 :                test = overlap%local_data(il, jl)
     281          648 :                eps = ABS(test - gold)
     282          648 :                IF (eps > eps_warning) EXIT outer
     283              :             END IF
     284              :          END DO
     285              :       END DO outer
     286              : #else
     287              :       outer: DO j = 1, ncol
     288              :          DO i = 1, ncol
     289              :             gold = MERGE(z_zero, z_one, i /= j)
     290              :             test = overlap%local_data(i, j)
     291              :             eps = ABS(test - gold)
     292              :             IF (eps > eps_warning) EXIT outer
     293              :          END DO
     294              :       END DO outer
     295              : #endif
     296              : 
     297           16 :       IF (eps > eps_warning) THEN
     298            0 :          IF (diag_type == FM_DIAG_TYPE_SCALAPACK) THEN
     299            0 :             diag_type_name = "HEGVX"
     300            0 :          ELSE IF (diag_type == FM_DIAG_TYPE_CUSOLVER) THEN
     301            0 :             diag_type_name = "CUSOLVER"
     302            0 :          ELSE IF (diag_type == FM_DIAG_TYPE_ELPA .AND. diag_lib_explicit) THEN
     303            0 :             diag_type_name = "ELPA"
     304              : #if defined(__DLAF)
     305              :          ELSE IF (diag_type == FM_DIAG_TYPE_DLAF) THEN
     306              :             diag_type_name = "DLAF"
     307              : #endif
     308              :          ELSE
     309            0 :             diag_type_name = "generalized eigensolver"
     310              :          END IF
     311              :          WRITE (UNIT=output_unit, FMT="(/,T2,A,/,T2,A,I0,A,I0,A,ES10.3,/,T2,A,F0.0,A,ES10.3)") &
     312            0 :             "The generalized eigenvectors returned by "//TRIM(diag_type_name)//" are not S-orthonormal", &
     313            0 :             "Absolute deviation of matrix element (", i, ", ", j, ") is ", eps, &
     314            0 :             "The deviation from the expected value ", REAL(gold, KIND=dp), " is", eps
     315            0 :          IF (eps > eps_abort) THEN
     316              :             CALL cp_abort(__LOCATION__, &
     317            0 :                           "ERROR in "//routineN//": Check of generalized matrix diagonalization failed")
     318              :          ELSE
     319            0 :             CPWARN("Check of generalized matrix diagonalization failed in routine "//routineN)
     320              :          END IF
     321              :       END IF
     322              : 
     323           16 :       CALL timestop(handle)
     324              : 
     325              :    END SUBROUTINE check_generalized_diag
     326              : 
     327              : ! **************************************************************************************************
     328              : !> \brief General Eigenvalue Problem  AX = BXE
     329              : !>        Single option version: Cholesky decomposition of B
     330              : !> \param amatrix ...
     331              : !> \param bmatrix ...
     332              : !> \param eigenvectors ...
     333              : !> \param eigenvalues ...
     334              : !> \param work ...
     335              : !> \param lowest_subset compute only the requested lowest eigenpairs with ScaLAPACK when available
     336              : !> \par History
     337              : !>      12.2024 Added DLA-Future support [Rocco Meli]
     338              : ! **************************************************************************************************
     339        36870 :    SUBROUTINE cp_cfm_geeig(amatrix, bmatrix, eigenvectors, eigenvalues, work, lowest_subset)
     340              : 
     341              :       TYPE(cp_cfm_type), INTENT(IN)                      :: amatrix, bmatrix, eigenvectors
     342              :       REAL(KIND=dp), DIMENSION(:)                        :: eigenvalues
     343              :       TYPE(cp_cfm_type), INTENT(IN)                      :: work
     344              :       LOGICAL, INTENT(IN), OPTIONAL                      :: lowest_subset
     345              : 
     346              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_cfm_geeig'
     347              : 
     348              :       INTEGER                                            :: handle, nao, nmo
     349              :       LOGICAL                                            :: check_eigenvectors, use_lowest_subset
     350              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: evals
     351              :       TYPE(cp_cfm_type)                                  :: overlap_check, scratch_check
     352              : 
     353        36870 :       CALL timeset(routineN, handle)
     354              : 
     355        36870 :       CALL cp_cfm_get_info(amatrix, nrow_global=nao)
     356       110610 :       ALLOCATE (evals(nao))
     357        36870 :       nmo = SIZE(eigenvalues)
     358        36870 :       check_eigenvectors = diag_check_requested()
     359        36870 :       use_lowest_subset = .FALSE.
     360        36870 :       IF (PRESENT(lowest_subset)) use_lowest_subset = lowest_subset .AND. nmo < nao
     361              : #if !defined(__parallel)
     362              :       use_lowest_subset = .FALSE.
     363              : #endif
     364              : 
     365              :       IF (use_lowest_subset) THEN
     366              : #if defined(__parallel)
     367          176 :          IF (check_eigenvectors) THEN
     368            0 :             CALL cp_cfm_create(overlap_check, bmatrix%matrix_struct)
     369            0 :             CALL cp_cfm_create(scratch_check, bmatrix%matrix_struct)
     370            0 :             CALL cp_cfm_to_cfm(bmatrix, overlap_check)
     371              :          END IF
     372          176 :          CALL cp_cfm_geeig_scalapack(amatrix, bmatrix, work, evals(1:nmo))
     373          176 :          IF (check_eigenvectors) THEN
     374            0 :             CALL check_generalized_diag(overlap_check, work, scratch_check, nmo)
     375            0 :             CALL cp_cfm_release(scratch_check)
     376            0 :             CALL cp_cfm_release(overlap_check)
     377              :          END IF
     378              : #endif
     379        36694 :       ELSE IF (diag_type == FM_DIAG_TYPE_CUSOLVER .AND. direct_generalized_diagonalization .AND. &
     380              :                nao >= cusolver_n_min) THEN
     381              :          ! Use cuSolverMP generalized eigenvalue solver without a CP2K-side
     382              :          ! Cholesky reduction.
     383            0 :          IF (check_eigenvectors) THEN
     384            0 :             CALL cp_cfm_create(overlap_check, bmatrix%matrix_struct)
     385            0 :             CALL cp_cfm_create(scratch_check, bmatrix%matrix_struct)
     386            0 :             CALL cp_cfm_to_cfm(bmatrix, overlap_check)
     387              :          END IF
     388            0 :          CALL cp_cfm_general_cusolver(amatrix, bmatrix, work, evals)
     389            0 :          IF (check_eigenvectors) THEN
     390            0 :             CALL check_generalized_diag(overlap_check, work, scratch_check, nmo)
     391            0 :             CALL cp_cfm_release(scratch_check)
     392            0 :             CALL cp_cfm_release(overlap_check)
     393              :          END IF
     394              : #if defined(__DLAF)
     395              :       ELSE IF (diag_type == FM_DIAG_TYPE_DLAF .AND. direct_generalized_diagonalization .AND. &
     396              :                nao >= dlaf_neigvec_min) THEN
     397              :          ! Initialize DLA-Future on-demand; if already initialized, does nothing
     398              :          CALL cp_dlaf_initialize()
     399              : 
     400              :          ! Create DLAF grid from BLACS context; if already present, does nothing
     401              :          CALL cp_dlaf_create_grid(amatrix%matrix_struct%context%get_handle())
     402              :          CALL cp_dlaf_create_grid(bmatrix%matrix_struct%context%get_handle())
     403              :          CALL cp_dlaf_create_grid(eigenvectors%matrix_struct%context%get_handle())
     404              : 
     405              :          ! Use DLA-Future generalized eigenvalue solver for large matrices
     406              :          IF (check_eigenvectors) THEN
     407              :             CALL cp_cfm_create(overlap_check, bmatrix%matrix_struct)
     408              :             CALL cp_cfm_create(scratch_check, bmatrix%matrix_struct)
     409              :             CALL cp_cfm_to_cfm(bmatrix, overlap_check)
     410              :          END IF
     411              :          CALL cp_cfm_diag_gen_dlaf(amatrix, bmatrix, work, evals)
     412              :          IF (check_eigenvectors) THEN
     413              :             CALL check_generalized_diag(overlap_check, work, scratch_check, nmo)
     414              :             CALL cp_cfm_release(scratch_check)
     415              :             CALL cp_cfm_release(overlap_check)
     416              :          END IF
     417              : #endif
     418              : #if defined(__parallel)
     419        36694 :       ELSE IF (diag_type == FM_DIAG_TYPE_SCALAPACK .AND. direct_generalized_diagonalization) THEN
     420              :          ! Use ScaLAPACK generalized eigenvalue solver without a CP2K-side
     421              :          ! Cholesky reduction.
     422           16 :          IF (check_eigenvectors) THEN
     423           16 :             CALL cp_cfm_create(overlap_check, bmatrix%matrix_struct)
     424           16 :             CALL cp_cfm_create(scratch_check, bmatrix%matrix_struct)
     425           16 :             CALL cp_cfm_to_cfm(bmatrix, overlap_check)
     426              :          END IF
     427           16 :          CALL cp_cfm_geeig_scalapack(amatrix, bmatrix, work, evals)
     428           16 :          IF (check_eigenvectors) THEN
     429           16 :             CALL check_generalized_diag(overlap_check, work, scratch_check, nmo)
     430           16 :             CALL cp_cfm_release(scratch_check)
     431           16 :             CALL cp_cfm_release(overlap_check)
     432              :          END IF
     433              : #endif
     434              :       ELSE
     435              :          ! Cholesky decompose S=U(T)U
     436        36678 :          CALL cp_cfm_cholesky_decompose(bmatrix)
     437              :          ! Invert to get U^(-1)
     438        36678 :          CALL cp_cfm_triangular_invert(bmatrix)
     439              :          ! Reduce to get U^(-T) * H * U^(-1)
     440        36678 :          CALL cp_cfm_triangular_multiply(bmatrix, amatrix, side="R")
     441        36678 :          CALL cp_cfm_triangular_multiply(bmatrix, amatrix, transa_tr="C")
     442              :          ! Diagonalize
     443        36678 :          CALL cp_cfm_heevd(matrix=amatrix, eigenvectors=work, eigenvalues=evals)
     444              :          ! Restore vectors C = U^(-1) * C*
     445        36678 :          CALL cp_cfm_triangular_multiply(bmatrix, work)
     446              :       END IF
     447              : 
     448        36870 :       CALL cp_cfm_to_cfm(work, eigenvectors, nmo)
     449       796076 :       eigenvalues(1:nmo) = evals(1:nmo)
     450              : 
     451        36870 :       DEALLOCATE (evals)
     452              : 
     453        36870 :       CALL timestop(handle)
     454              : 
     455        36870 :    END SUBROUTINE cp_cfm_geeig
     456              : 
     457              : ! **************************************************************************************************
     458              : !> \brief General Eigenvalue Problem AX = BXE using ScaLAPACK PZHEGVX.
     459              : !> \param amatrix ...
     460              : !> \param bmatrix ...
     461              : !> \param eigenvectors ...
     462              : !> \param eigenvalues ...
     463              : ! **************************************************************************************************
     464          192 :    SUBROUTINE cp_cfm_geeig_scalapack(amatrix, bmatrix, eigenvectors, eigenvalues)
     465              : 
     466              :       TYPE(cp_cfm_type), INTENT(IN)                      :: amatrix, bmatrix, eigenvectors
     467              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: eigenvalues
     468              : 
     469              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_cfm_geeig_scalapack'
     470              : 
     471              : #if defined(__parallel)
     472              :       REAL(KIND=dp), PARAMETER                           :: orfac = -1.0_dp, &
     473              :                                                             vl = 0.0_dp, &
     474              :                                                             vu = 0.0_dp
     475              : 
     476          192 :       COMPLEX(KIND=dp), DIMENSION(:), ALLOCATABLE        :: work
     477          192 :       COMPLEX(KIND=dp), DIMENSION(:, :), POINTER         :: a, b, z
     478              :       INTEGER                                            :: handle, info, liwork, lwork, lrwork, &
     479              :                                                             m, n, nb, neig, npcol, nprow, nz
     480              :       INTEGER, DIMENSION(9)                              :: desca, descb, descz
     481          192 :       INTEGER, DIMENSION(:), ALLOCATABLE                 :: iclustr, ifail, iwork
     482              :       REAL(KIND=dp)                                      :: abstol
     483          192 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE           :: gap, rwork, w
     484              : 
     485              :       INTEGER                                            :: mq0, nn, np0, npe
     486              :       INTEGER, EXTERNAL                                  :: iceil, numroc
     487              :       REAL(KIND=dp), EXTERNAL                            :: dlamch
     488              : #if defined (__HAS_IEEE_EXCEPTIONS)
     489              :       LOGICAL, DIMENSION(5)                              :: halt
     490              : #endif
     491              : #else
     492              :       INTEGER                                            :: handle
     493              : #endif
     494              : 
     495          192 :       CALL timeset(routineN, handle)
     496              : 
     497              : #if defined(__parallel)
     498          192 :       n = amatrix%matrix_struct%nrow_global
     499          192 :       neig = MIN(SIZE(eigenvalues), n)
     500              : 
     501          192 :       IF (neig == 0) THEN
     502            0 :          CALL timestop(handle)
     503            0 :          RETURN
     504              :       END IF
     505              : 
     506          192 :       IF (amatrix%matrix_struct%nrow_block /= amatrix%matrix_struct%ncol_block) THEN
     507            0 :          CPABORT("ERROR in "//routineN//": Invalid blocksize (no square blocks) found")
     508              :       END IF
     509              : 
     510          192 :       a => amatrix%local_data
     511          192 :       b => bmatrix%local_data
     512          192 :       z => eigenvectors%local_data
     513         1920 :       desca(:) = amatrix%matrix_struct%descriptor(:)
     514         1920 :       descb(:) = bmatrix%matrix_struct%descriptor(:)
     515         1920 :       descz(:) = eigenvectors%matrix_struct%descriptor(:)
     516              : 
     517          192 :       nprow = amatrix%matrix_struct%context%num_pe(1)
     518          192 :       npcol = amatrix%matrix_struct%context%num_pe(2)
     519          192 :       npe = nprow*npcol
     520          192 :       nb = amatrix%matrix_struct%nrow_block
     521          192 :       nn = MAX(n, nb, 2)
     522          192 :       np0 = numroc(nn, nb, 0, 0, nprow)
     523          192 :       mq0 = MAX(numroc(nn, nb, 0, 0, npcol), nb)
     524              : 
     525          192 :       lwork = n + (np0 + mq0 + nb)*nb
     526          192 :       lrwork = 4*n + MAX(5*nn, np0*mq0) + iceil(neig, npe)*nn + MAX(0, neig - 1)*n
     527          192 :       liwork = 6*MAX(n, npe + 1, 4)
     528              : 
     529          576 :       ALLOCATE (gap(npe))
     530          192 :       gap = 0.0_dp
     531          576 :       ALLOCATE (iclustr(2*npe))
     532          192 :       iclustr = 0
     533          576 :       ALLOCATE (ifail(n))
     534          192 :       ifail = 0
     535          576 :       ALLOCATE (iwork(liwork))
     536          576 :       ALLOCATE (rwork(lrwork))
     537          576 :       ALLOCATE (w(n))
     538          576 :       ALLOCATE (work(lwork))
     539              : 
     540          192 :       abstol = 2.0_dp*dlamch("S")
     541              : 
     542              : #if defined (__HAS_IEEE_EXCEPTIONS)
     543              :       CALL ieee_get_halting_mode(IEEE_ALL, halt)
     544              :       CALL ieee_set_halting_mode(IEEE_ALL, .FALSE.)
     545              : #endif
     546              :       CALL pzhegvx(1, "V", "I", "U", n, a(1, 1), 1, 1, desca, b(1, 1), 1, 1, descb, &
     547              :                    vl, vu, 1, neig, abstol, m, nz, w(1), orfac, z(1, 1), 1, 1, descz, &
     548              :                    work(1), lwork, rwork(1), lrwork, iwork(1), liwork, ifail(1), &
     549          192 :                    iclustr(1), gap(1), info)
     550              : #if defined (__HAS_IEEE_EXCEPTIONS)
     551              :       CALL ieee_set_halting_mode(IEEE_ALL, halt)
     552              : #endif
     553              : 
     554          192 :       IF (info /= 0 .OR. m < neig .OR. nz < neig) THEN
     555            0 :          CPABORT("ERROR in PZHEGVX (ScaLAPACK), info="//TRIM(cp_to_string(info)))
     556              :       END IF
     557              : 
     558         1209 :       eigenvalues(:) = 0.0_dp
     559         1209 :       eigenvalues(1:neig) = w(1:neig)
     560              : 
     561          192 :       DEALLOCATE (gap, iclustr, ifail, iwork, rwork, w, work)
     562              : #else
     563              :       MARK_USED(amatrix)
     564              :       MARK_USED(bmatrix)
     565              :       MARK_USED(eigenvectors)
     566              :       MARK_USED(eigenvalues)
     567              :       CPABORT("ERROR in "//routineN//": PZHEGVX requested without ScaLAPACK support")
     568              : #endif
     569              : 
     570          192 :       CALL timestop(handle)
     571              : 
     572          192 :    END SUBROUTINE cp_cfm_geeig_scalapack
     573              : 
     574              : ! **************************************************************************************************
     575              : !> \brief General Eigenvalue Problem  AX = BXE
     576              : !>        Use canonical orthogonalization
     577              : !> \param amatrix ...
     578              : !> \param bmatrix ...
     579              : !> \param eigenvectors ...
     580              : !> \param eigenvalues ...
     581              : !> \param work ...
     582              : !> \param epseig ...
     583              : !> \param nmo_retained ...
     584              : ! **************************************************************************************************
     585         2596 :    SUBROUTINE cp_cfm_geeig_canon(amatrix, bmatrix, eigenvectors, eigenvalues, work, epseig, &
     586              :                                  nmo_retained)
     587              : 
     588              :       TYPE(cp_cfm_type), INTENT(INOUT)                   :: amatrix, bmatrix, eigenvectors
     589              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: eigenvalues
     590              :       TYPE(cp_cfm_type), INTENT(INOUT)                   :: work
     591              :       REAL(KIND=dp), INTENT(IN)                          :: epseig
     592              :       INTEGER, INTENT(OUT), OPTIONAL                     :: nmo_retained
     593              : 
     594              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_geeig_canon'
     595              : 
     596              :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:)        :: cevals
     597              :       INTEGER                                            :: handle, i, icol, irow, nao, nc, ncol, &
     598              :                                                             nmo, nx
     599              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: evals
     600              : 
     601         2596 :       CALL timeset(routineN, handle)
     602              : 
     603              :       ! Test sizes
     604         2596 :       CALL cp_cfm_get_info(amatrix, nrow_global=nao)
     605         2596 :       nmo = SIZE(eigenvalues)
     606        12980 :       ALLOCATE (evals(nao), cevals(nao))
     607              : 
     608              :       ! Diagonalize -S matrix, this way the NULL space is at the end of the spectrum
     609         2596 :       CALL cp_cfm_scale(-z_one, bmatrix)
     610         2596 :       CALL cp_cfm_heevd(bmatrix, work, evals)
     611        81048 :       evals(:) = -evals(:)
     612         2596 :       nc = nao
     613        80760 :       DO i = 1, nao
     614        80760 :          IF (evals(i) < epseig) THEN
     615           16 :             nc = i - 1
     616           16 :             EXIT
     617              :          END IF
     618              :       END DO
     619         2596 :       CPASSERT(nc /= 0)
     620              : 
     621         2596 :       IF (nc /= nao) THEN
     622           16 :          IF (nc < nmo) THEN
     623              :             ! Copy NULL space definition to last vectors of eigenvectors (if needed)
     624            0 :             ncol = nmo - nc
     625            0 :             CALL cp_cfm_to_cfm(work, eigenvectors, ncol, nc + 1, nc + 1)
     626              :          END IF
     627              :          ! Set NULL space in eigenvector matrix of S to zero
     628          304 :          DO icol = nc + 1, nao
     629        50992 :             DO irow = 1, nao
     630        50976 :                CALL cp_cfm_set_element(work, irow, icol, z_zero)
     631              :             END DO
     632              :          END DO
     633              :          ! Set small eigenvalues to a dummy save value
     634          304 :          evals(nc + 1:nao) = 1.0_dp
     635              :       END IF
     636              :       ! Calculate U*s**(-1/2)
     637        81048 :       cevals(:) = CMPLX(1.0_dp/SQRT(evals(:)), 0.0_dp, KIND=dp)
     638         2596 :       CALL cp_cfm_column_scale(work, cevals)
     639              :       ! Reduce to get U^(-C) * H * U^(-1)
     640         2596 :       CALL cp_cfm_gemm("C", "N", nao, nao, nao, z_one, work, amatrix, z_zero, bmatrix)
     641         2596 :       CALL cp_cfm_gemm("N", "N", nao, nao, nao, z_one, bmatrix, work, z_zero, amatrix)
     642         2596 :       IF (nc /= nao) THEN
     643              :          ! set diagonal values to save large value
     644          304 :          DO icol = nc + 1, nao
     645              :             CALL cp_cfm_set_element(amatrix, icol, icol, &
     646          304 :                                     CMPLX(set_removed_eigval_to, 0.0_dp, KIND=dp))
     647              :          END DO
     648              :       END IF
     649              :       ! Diagonalize
     650         2596 :       CALL cp_cfm_heevd(amatrix, bmatrix, evals)
     651        31910 :       eigenvalues(1:nmo) = evals(1:nmo)
     652         2596 :       nx = MIN(nc, nmo)
     653              :       ! Restore vectors C = U^(-1) * C*
     654         2596 :       CALL cp_cfm_gemm("N", "N", nao, nx, nc, z_one, work, bmatrix, z_zero, eigenvectors)
     655              : 
     656              :       ! Number of basis modes that survived the linear-dependency filter. The remaining
     657              :       ! nao - nc entries of eigenvalues(:) are the placeholders set above.
     658         2596 :       IF (PRESENT(nmo_retained)) nmo_retained = nc
     659              : 
     660         2596 :       DEALLOCATE (evals)
     661              : 
     662         2596 :       CALL timestop(handle)
     663              : 
     664         5192 :    END SUBROUTINE cp_cfm_geeig_canon
     665              : 
     666              : ! **************************************************************************************************
     667              : !> \brief Solve a generalized complex eigenproblem using the local LAPACK backend.
     668              : !>        This routine is restricted to a one-rank BLACS grid.  It deliberately
     669              : !>        avoids ScaLAPACK so independent k-points can be evaluated concurrently
     670              : !>        without making overlapping MPI calls from OpenMP worker threads.
     671              : !> \param amatrix Hamiltonian, overwritten
     672              : !> \param bmatrix overlap matrix, overwritten
     673              : !> \param eigenvectors eigenvectors
     674              : !> \param eigenvalues eigenvalues
     675              : !> \param ws reusable private LAPACK scratch
     676              : ! **************************************************************************************************
     677        42325 :    SUBROUTINE cp_cfm_geeig_local(amatrix, bmatrix, eigenvectors, eigenvalues, ws)
     678              : 
     679              :       TYPE(cp_cfm_type), INTENT(IN)                      :: amatrix, bmatrix, eigenvectors
     680              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: eigenvalues
     681              :       TYPE(cp_cfm_diag_workspace_type), INTENT(INOUT)  :: ws
     682              : 
     683              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'cp_cfm_geeig_local'
     684              : 
     685        42325 :       COMPLEX(KIND=dp), DIMENSION(:, :), POINTER         :: a, b, z
     686              :       INTEGER                                            :: handle, info, n, nmo
     687              : #if defined (__HAS_IEEE_EXCEPTIONS)
     688              :       LOGICAL, DIMENSION(5)                              :: halt
     689              : #endif
     690              : 
     691        42325 :       CALL timeset(routineN, handle)
     692              : 
     693       126975 :       CPASSERT(PRODUCT(amatrix%matrix_struct%context%num_pe) == 1)
     694        42325 :       n = amatrix%matrix_struct%nrow_global
     695        42325 :       nmo = MIN(n, SIZE(eigenvalues))
     696        42325 :       a => amatrix%local_data
     697        42325 :       b => bmatrix%local_data
     698        42325 :       z => eigenvectors%local_data
     699              : 
     700        42325 :       CALL cp_cfm_local_workspace_prepare(ws, n, a, b)
     701              : 
     702              : #if defined (__HAS_IEEE_EXCEPTIONS)
     703              :       CALL ieee_get_halting_mode(IEEE_ALL, halt)
     704              :       CALL ieee_set_halting_mode(IEEE_ALL, .FALSE.)
     705              : #endif
     706              :       CALL zhegvd(1, 'V', 'U', n, a(1, 1), SIZE(a, 1), b(1, 1), SIZE(b, 1), ws%evals(1), &
     707        42325 :                   ws%work(1), SIZE(ws%work), ws%rwork(1), SIZE(ws%rwork), ws%iwork(1), SIZE(ws%iwork), info)
     708              : #if defined (__HAS_IEEE_EXCEPTIONS)
     709              :       CALL ieee_set_halting_mode(IEEE_ALL, halt)
     710              : #endif
     711        42325 :       IF (info /= 0) THEN
     712            0 :          CALL cp_abort(__LOCATION__, "Local ZHEGVD failed, info="//TRIM(cp_to_string(info)))
     713              :       END IF
     714              : 
     715       875424 :       eigenvalues(1:nmo) = ws%evals(1:nmo)
     716     74654112 :       z(1:n, 1:nmo) = a(1:n, 1:nmo)
     717              : 
     718        42325 :       CALL timestop(handle)
     719              : 
     720        42325 :    END SUBROUTINE cp_cfm_geeig_local
     721              : 
     722              : ! **************************************************************************************************
     723              : !> \brief Canonical generalized complex diagonalization on a one-rank BLACS grid.
     724              : !> \param amatrix Hamiltonian, overwritten
     725              : !> \param bmatrix overlap matrix, overwritten and used as work storage
     726              : !> \param eigenvectors eigenvectors
     727              : !> \param eigenvalues eigenvalues
     728              : !> \param work work matrix
     729              : !> \param epseig overlap eigenvalue threshold
     730              : !> \param ws reusable private LAPACK scratch
     731              : ! **************************************************************************************************
     732         1400 :    SUBROUTINE cp_cfm_geeig_canon_local(amatrix, bmatrix, eigenvectors, eigenvalues, work, epseig, ws)
     733              : 
     734              :       TYPE(cp_cfm_type), INTENT(IN)                      :: amatrix, bmatrix, eigenvectors
     735              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: eigenvalues
     736              :       TYPE(cp_cfm_type), INTENT(IN)                      :: work
     737              :       REAL(KIND=dp), INTENT(IN)                          :: epseig
     738              :       TYPE(cp_cfm_diag_workspace_type), INTENT(INOUT)    :: ws
     739              : 
     740              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_cfm_geeig_canon_local'
     741              : 
     742         1400 :       COMPLEX(KIND=dp), DIMENSION(:, :), POINTER         :: a, b, u, z
     743              :       INTEGER                                            :: handle, i, info, n, nc, nmo, nx
     744              : 
     745         1400 :       CALL timeset(routineN, handle)
     746              : 
     747         4200 :       CPASSERT(PRODUCT(amatrix%matrix_struct%context%num_pe) == 1)
     748         1400 :       n = amatrix%matrix_struct%nrow_global
     749         1400 :       nmo = MIN(n, SIZE(eigenvalues))
     750         1400 :       a => amatrix%local_data
     751         1400 :       b => bmatrix%local_data
     752         1400 :       u => work%local_data
     753         1400 :       z => eigenvectors%local_data
     754         1400 :       CALL cp_cfm_local_workspace_prepare(ws, n, b)
     755              : 
     756      1024034 :       b(1:n, 1:n) = -b(1:n, 1:n)
     757         1400 :       CALL cp_cfm_heevd_local(b, u, info, ws)
     758         1400 :       IF (info /= 0) THEN
     759            0 :          CALL cp_abort(__LOCATION__, "Local overlap ZHEEVD failed, info="//TRIM(cp_to_string(info)))
     760              :       END IF
     761        29494 :       ws%evals(:) = -ws%evals(:)
     762         1400 :       nc = n
     763        29356 :       DO i = 1, n
     764        29356 :          IF (ws%evals(i) < epseig) THEN
     765           28 :             nc = i - 1
     766           28 :             EXIT
     767              :          END IF
     768              :       END DO
     769         1400 :       CPASSERT(nc /= 0)
     770              : 
     771         1400 :       IF (nc < n) THEN
     772           28 :          IF (nc < nmo) z(1:n, nc + 1:nmo) = u(1:n, nc + 1:nmo)
     773        14518 :          u(1:n, nc + 1:n) = z_zero
     774          166 :          ws%evals(nc + 1:n) = 1.0_dp
     775              :       END IF
     776        29494 :       DO i = 1, n
     777      1024034 :          u(1:n, i) = u(1:n, i)/SQRT(ws%evals(i))
     778              :       END DO
     779              : 
     780              :       CALL zgemm('C', 'N', n, n, n, z_one, u(1, 1), SIZE(u, 1), a(1, 1), SIZE(a, 1), &
     781         1400 :                  z_zero, b(1, 1), SIZE(b, 1))
     782              :       CALL zgemm('N', 'N', n, n, n, z_one, b(1, 1), SIZE(b, 1), u(1, 1), SIZE(u, 1), &
     783         1400 :                  z_zero, a(1, 1), SIZE(a, 1))
     784         1400 :       IF (nc < n) THEN
     785          166 :          DO i = nc + 1, n
     786          166 :             a(i, i) = CMPLX(10000.0_dp, 0.0_dp, KIND=dp)
     787              :          END DO
     788              :       END IF
     789              : 
     790         1400 :       CALL cp_cfm_heevd_local(a, b, info, ws)
     791         1400 :       IF (info /= 0) THEN
     792            0 :          CALL cp_abort(__LOCATION__, "Local Hamiltonian ZHEEVD failed, info="//TRIM(cp_to_string(info)))
     793              :       END IF
     794        11135 :       eigenvalues(1:nmo) = ws%evals(1:nmo)
     795         1400 :       nx = MIN(nc, nmo)
     796              :       CALL zgemm('N', 'N', n, nx, nc, z_one, u(1, 1), SIZE(u, 1), b(1, 1), SIZE(b, 1), &
     797         1400 :                  z_zero, z(1, 1), SIZE(z, 1))
     798              : 
     799         1400 :       CALL timestop(handle)
     800              : 
     801         1400 :    END SUBROUTINE cp_cfm_geeig_canon_local
     802              : 
     803              : ! **************************************************************************************************
     804              : !> \brief Local LAPACK ZHEEVD helper. The eigenvectors are copied to vectors.
     805              : !> \param matrix ...
     806              : !> \param vectors ...
     807              : !> \param info ...
     808              : !> \param ws prepared private LAPACK scratch
     809              : ! **************************************************************************************************
     810         2800 :    SUBROUTINE cp_cfm_heevd_local(matrix, vectors, info, ws)
     811              : 
     812              :       COMPLEX(KIND=dp), DIMENSION(:, :), INTENT(INOUT)  :: matrix, vectors
     813              :       INTEGER, INTENT(OUT)                               :: info
     814              : 
     815              :       TYPE(cp_cfm_diag_workspace_type), INTENT(INOUT) :: ws
     816              :       INTEGER :: n
     817              : #if defined (__HAS_IEEE_EXCEPTIONS)
     818              :       LOGICAL, DIMENSION(5)                              :: halt
     819              : #endif
     820              : 
     821         2800 :       n = ws%n
     822              : #if defined (__HAS_IEEE_EXCEPTIONS)
     823              :       CALL ieee_get_halting_mode(IEEE_ALL, halt)
     824              :       CALL ieee_set_halting_mode(IEEE_ALL, .FALSE.)
     825              : #endif
     826              :       CALL zheevd('V', 'U', n, matrix(1, 1), SIZE(matrix, 1), ws%evals(1), &
     827         2800 :                   ws%work(1), SIZE(ws%work), ws%rwork(1), SIZE(ws%rwork), ws%iwork(1), SIZE(ws%iwork), info)
     828              : #if defined (__HAS_IEEE_EXCEPTIONS)
     829              :       CALL ieee_set_halting_mode(IEEE_ALL, halt)
     830              : #endif
     831      2048068 :       vectors(1:n, 1:n) = matrix(1:n, 1:n)
     832              : 
     833         2800 :    END SUBROUTINE cp_cfm_heevd_local
     834              : 
     835              : ! **************************************************************************************************
     836              : !> \brief Query LAPACK once per matrix order/solver and retain private scratch across k points.
     837              : !> \param ws caller-owned workspace
     838              : !> \param n matrix order
     839              : !> \param a query matrix; workspace queries do not read its values
     840              : !> \param b optional overlap selects ZHEGVD; otherwise query ZHEEVD
     841              : ! **************************************************************************************************
     842        67151 :    SUBROUTINE cp_cfm_local_workspace_prepare(ws, n, a, b)
     843              :       TYPE(cp_cfm_diag_workspace_type), INTENT(INOUT)    :: ws
     844              :       INTEGER, INTENT(IN)                                :: n
     845              :       COMPLEX(KIND=dp), INTENT(INOUT)                    :: a(:, :)
     846              :       COMPLEX(KIND=dp), INTENT(INOUT), OPTIONAL          :: b(:, :)
     847              : 
     848              :       COMPLEX(KIND=dp)                                   :: work(1)
     849              :       INTEGER                                            :: info, iwork(1)
     850              :       REAL(KIND=dp)                                      :: rwork(1)
     851              : 
     852        67151 :       IF (ws%n == n .AND. (ws%generalized .EQV. PRESENT(b))) RETURN
     853        23426 :       IF (ALLOCATED(ws%work)) DEALLOCATE (ws%work, ws%rwork, ws%iwork, ws%evals)
     854        70278 :       ALLOCATE (ws%evals(n))
     855        23426 :       IF (PRESENT(b)) THEN
     856              :          CALL zhegvd(1, 'V', 'U', n, a(1, 1), SIZE(a, 1), b(1, 1), SIZE(b, 1), ws%evals(1), &
     857        22830 :                      work(1), -1, rwork(1), -1, iwork(1), -1, info)
     858              :       ELSE
     859          596 :          CALL zheevd('V', 'U', n, a(1, 1), SIZE(a, 1), ws%evals(1), work(1), -1, rwork(1), -1, iwork(1), -1, info)
     860              :       END IF
     861        23426 :       IF (info /= 0) THEN
     862              :          CALL cp_abort(__LOCATION__, &
     863            0 :                        "Local LAPACK workspace query failed, info="//TRIM(cp_to_string(info)))
     864              :       END IF
     865            0 :       ALLOCATE (ws%work(MAX(1, CEILING(REAL(work(1), dp)))), &
     866       163982 :                 ws%rwork(MAX(1, CEILING(rwork(1)))), ws%iwork(MAX(1, iwork(1))))
     867        23426 :       ws%n = n
     868        23426 :       ws%generalized = PRESENT(b)
     869              :    END SUBROUTINE cp_cfm_local_workspace_prepare
     870              : 
     871            0 : END MODULE cp_cfm_diag
        

Generated by: LCOV version 2.0-1