LCOV - code coverage report
Current view: top level - src/fm - cp_fm_elpa.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:21ef868) Lines: 81.2 % 160 130
Test Date: 2026-08-14 07:04:57 Functions: 100.0 % 6 6

            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 Wrapper for ELPA
      10              : !> \author Ole Schuett
      11              : ! **************************************************************************************************
      12              : MODULE cp_fm_elpa
      13              :    USE cp_log_handling, ONLY: cp_to_string
      14              :    USE machine, ONLY: m_cpuid_static, &
      15              :                       MACHINE_CPU_GENERIC, &
      16              :                       MACHINE_X86_SSE4, &
      17              :                       MACHINE_X86_AVX, &
      18              :                       MACHINE_X86_AVX2, &
      19              :                       MACHINE_X86_AVX512
      20              :    USE cp_blacs_env, ONLY: cp_blacs_env_type
      21              :    USE cp_fm_basic_linalg, ONLY: cp_fm_uplo_to_full
      22              :    USE cp_fm_diag_utils, ONLY: cp_fm_redistribute_start, &
      23              :                                cp_fm_redistribute_end, &
      24              :                                cp_fm_redistribute_info
      25              :    USE cp_fm_struct, ONLY: cp_fm_struct_get
      26              :    USE cp_fm_types, ONLY: cp_fm_type, &
      27              :                           cp_fm_to_fm, &
      28              :                           cp_fm_release, &
      29              :                           cp_fm_create, &
      30              :                           cp_fm_write_info
      31              :    USE cp_log_handling, ONLY: cp_get_default_logger, &
      32              :                               cp_logger_get_default_io_unit, &
      33              :                               cp_logger_type
      34              :    USE kinds, ONLY: default_string_length, dp
      35              :    USE message_passing, ONLY: mp_comm_type
      36              :    USE OMP_LIB, ONLY: omp_get_max_threads
      37              : #if defined(__HAS_IEEE_EXCEPTIONS)
      38              :    USE ieee_exceptions, ONLY: ieee_get_halting_mode, &
      39              :                               ieee_set_halting_mode, &
      40              :                               IEEE_ALL
      41              : #endif
      42              : #include "../base/base_uses.f90"
      43              : 
      44              : #if defined(__ELPA)
      45              :    USE elpa_constants, ONLY: ELPA_SOLVER_1STAGE, ELPA_SOLVER_2STAGE, ELPA_OK, &
      46              :                              ELPA_2STAGE_REAL_INVALID, &
      47              :                              ELPA_2STAGE_REAL_DEFAULT, &
      48              :                              ELPA_2STAGE_REAL_GENERIC, &
      49              :                              ELPA_2STAGE_REAL_GENERIC_SIMPLE, &
      50              :                              ELPA_2STAGE_REAL_BGP, &
      51              :                              ELPA_2STAGE_REAL_BGQ, &
      52              :                              ELPA_2STAGE_REAL_SSE_ASSEMBLY, &
      53              :                              ELPA_2STAGE_REAL_SSE_BLOCK2, &
      54              :                              ELPA_2STAGE_REAL_SSE_BLOCK4, &
      55              :                              ELPA_2STAGE_REAL_SSE_BLOCK6, &
      56              :                              ELPA_2STAGE_REAL_AVX_BLOCK2, &
      57              :                              ELPA_2STAGE_REAL_AVX_BLOCK4, &
      58              :                              ELPA_2STAGE_REAL_AVX_BLOCK6, &
      59              :                              ELPA_2STAGE_REAL_AVX2_BLOCK2, &
      60              :                              ELPA_2STAGE_REAL_AVX2_BLOCK4, &
      61              :                              ELPA_2STAGE_REAL_AVX2_BLOCK6, &
      62              :                              ELPA_2STAGE_REAL_AVX512_BLOCK2, &
      63              :                              ELPA_2STAGE_REAL_AVX512_BLOCK4, &
      64              :                              ELPA_2STAGE_REAL_AVX512_BLOCK6, &
      65              :                              ELPA_2STAGE_REAL_NVIDIA_GPU, &
      66              :                              ELPA_2STAGE_REAL_AMD_GPU, &
      67              :                              ELPA_2STAGE_REAL_INTEL_GPU_SYCL
      68              : 
      69              :    USE elpa, ONLY: elpa_t, elpa_init, elpa_uninit, &
      70              :                    elpa_allocate, elpa_deallocate
      71              : #endif
      72              : 
      73              :    IMPLICIT NONE
      74              : 
      75              :    PRIVATE
      76              : 
      77              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cp_fm_elpa'
      78              : 
      79              : #if defined(__ELPA)
      80              :    INTEGER, DIMENSION(21), PARAMETER :: elpa_kernel_ids = [ &
      81              :                                         ELPA_2STAGE_REAL_INVALID, & ! auto
      82              :                                         ELPA_2STAGE_REAL_GENERIC, &
      83              :                                         ELPA_2STAGE_REAL_GENERIC_SIMPLE, &
      84              :                                         ELPA_2STAGE_REAL_BGP, &
      85              :                                         ELPA_2STAGE_REAL_BGQ, &
      86              :                                         ELPA_2STAGE_REAL_SSE_ASSEMBLY, &
      87              :                                         ELPA_2STAGE_REAL_SSE_BLOCK2, &
      88              :                                         ELPA_2STAGE_REAL_SSE_BLOCK4, &
      89              :                                         ELPA_2STAGE_REAL_SSE_BLOCK6, &
      90              :                                         ELPA_2STAGE_REAL_AVX_BLOCK2, &
      91              :                                         ELPA_2STAGE_REAL_AVX_BLOCK4, &
      92              :                                         ELPA_2STAGE_REAL_AVX_BLOCK6, &
      93              :                                         ELPA_2STAGE_REAL_AVX2_BLOCK2, &
      94              :                                         ELPA_2STAGE_REAL_AVX2_BLOCK4, &
      95              :                                         ELPA_2STAGE_REAL_AVX2_BLOCK6, &
      96              :                                         ELPA_2STAGE_REAL_AVX512_BLOCK2, &
      97              :                                         ELPA_2STAGE_REAL_AVX512_BLOCK4, &
      98              :                                         ELPA_2STAGE_REAL_AVX512_BLOCK6, &
      99              :                                         ELPA_2STAGE_REAL_NVIDIA_GPU, &
     100              :                                         ELPA_2STAGE_REAL_AMD_GPU, &
     101              :                                         ELPA_2STAGE_REAL_INTEL_GPU_SYCL]
     102              : 
     103              :    CHARACTER(len=14), DIMENSION(SIZE(elpa_kernel_ids)), PARAMETER :: &
     104              :       elpa_kernel_names = [CHARACTER(len=14) :: &
     105              :                            "AUTO", &
     106              :                            "GENERIC", &
     107              :                            "GENERIC_SIMPLE", &
     108              :                            "BGP", &
     109              :                            "BGQ", &
     110              :                            "SSE", &
     111              :                            "SSE_BLOCK2", &
     112              :                            "SSE_BLOCK4", &
     113              :                            "SSE_BLOCK6", &
     114              :                            "AVX_BLOCK2", &
     115              :                            "AVX_BLOCK4", &
     116              :                            "AVX_BLOCK6", &
     117              :                            "AVX2_BLOCK2", &
     118              :                            "AVX2_BLOCK4", &
     119              :                            "AVX2_BLOCK6", &
     120              :                            "AVX512_BLOCK2", &
     121              :                            "AVX512_BLOCK4", &
     122              :                            "AVX512_BLOCK6", &
     123              :                            "NVIDIA_GPU", &
     124              :                            "AMD_GPU", &
     125              :                            "INTEL_GPU"]
     126              : 
     127              :    CHARACTER(len=44), DIMENSION(SIZE(elpa_kernel_ids)), PARAMETER :: &
     128              :       elpa_kernel_descriptions = [CHARACTER(len=44) :: &
     129              :                                   "Automatically selected kernel", &
     130              :                                   "Generic kernel", &
     131              :                                   "Simplified generic kernel", &
     132              :                                   "Kernel optimized for IBM BGP", &
     133              :                                   "Kernel optimized for IBM BGQ", &
     134              :                                   "Kernel optimized for x86_64/SSE", &
     135              :                                   "Kernel optimized for x86_64/SSE (block=2)", &
     136              :                                   "Kernel optimized for x86_64/SSE (block=4)", &
     137              :                                   "Kernel optimized for x86_64/SSE (block=6)", &
     138              :                                   "Kernel optimized for Intel AVX (block=2)", &
     139              :                                   "Kernel optimized for Intel AVX (block=4)", &
     140              :                                   "Kernel optimized for Intel AVX (block=6)", &
     141              :                                   "Kernel optimized for Intel AVX2 (block=2)", &
     142              :                                   "Kernel optimized for Intel AVX2 (block=4)", &
     143              :                                   "Kernel optimized for Intel AVX2 (block=6)", &
     144              :                                   "Kernel optimized for Intel AVX-512 (block=2)", &
     145              :                                   "Kernel optimized for Intel AVX-512 (block=4)", &
     146              :                                   "Kernel optimized for Intel AVX-512 (block=6)", &
     147              :                                   "Kernel targeting Nvidia GPUs", &
     148              :                                   "Kernel targeting AMD GPUs", &
     149              :                                   "Kernel targeting Intel GPUs"]
     150              : #else
     151              :    INTEGER, DIMENSION(1), PARAMETER :: elpa_kernel_ids = [-1]
     152              :    CHARACTER(len=14), DIMENSION(1), PARAMETER :: elpa_kernel_names = ["AUTO"]
     153              :    CHARACTER(len=44), DIMENSION(1), PARAMETER :: elpa_kernel_descriptions = ["Automatically selected kernel"]
     154              : #endif
     155              : 
     156              : #if defined(__ELPA)
     157              :    INTEGER, SAVE :: elpa_kernel = elpa_kernel_ids(1) ! auto
     158              : #endif
     159              : 
     160              :    ! elpa_qr_unsafe: disable block size limitations
     161              :    LOGICAL, SAVE :: elpa_qr_unsafe = .TRUE., &
     162              :                     elpa_print = .FALSE., &
     163              :                     elpa_qr = .FALSE.
     164              : 
     165              : #if defined(__OFFLOAD_OPENCL)
     166              :    LOGICAL, SAVE :: elpa_one_stage = .TRUE.
     167              : #else
     168              :    LOGICAL, SAVE :: elpa_one_stage = .FALSE.
     169              : #endif
     170              : 
     171              :    PUBLIC :: cp_fm_diag_elpa, &
     172              :              set_elpa_kernel, &
     173              :              elpa_one_stage, &
     174              :              elpa_print, &
     175              :              elpa_qr, &
     176              :              elpa_kernel_ids, &
     177              :              elpa_kernel_names, &
     178              :              elpa_kernel_descriptions, &
     179              :              initialize_elpa_library, &
     180              :              finalize_elpa_library
     181              : 
     182              : CONTAINS
     183              : 
     184              : #if defined(__ELPA)
     185              : ! **************************************************************************************************
     186              : !> \brief Return a printable name for an ELPA real kernel.
     187              : !> \param kernel ELPA real kernel id
     188              : !> \return ...
     189              : ! **************************************************************************************************
     190           30 :    FUNCTION get_elpa_kernel_name(kernel) RESULT(kernel_name)
     191              :       INTEGER, INTENT(IN)                                :: kernel
     192              :       CHARACTER(len=default_string_length)               :: kernel_name
     193              : 
     194              :       INTEGER                                            :: i
     195              : 
     196           30 :       kernel_name = "id: "//TRIM(ADJUSTL(cp_to_string(kernel)))
     197          180 :       DO i = 1, SIZE(elpa_kernel_ids)
     198          180 :          IF (elpa_kernel_ids(i) == kernel) THEN
     199           30 :             kernel_name = elpa_kernel_names(i)
     200           30 :             EXIT
     201              :          END IF
     202              :       END DO
     203           30 :    END FUNCTION get_elpa_kernel_name
     204              : #endif
     205              : 
     206              : ! **************************************************************************************************
     207              : !> \brief Initialize the ELPA library
     208              : !> \param one_stage ...
     209              : !> \param qr ...
     210              : !> \param should_print flag that determines if additional information
     211              : !>        is printed when the diagonalization routine is called.
     212              : ! **************************************************************************************************
     213        10426 :    SUBROUTINE initialize_elpa_library(one_stage, qr, should_print)
     214              :       LOGICAL, INTENT(IN), OPTIONAL                      :: one_stage, qr, should_print
     215              : 
     216              : #if defined(__ELPA)
     217        10426 :       IF (elpa_init(20180525) /= ELPA_OK) THEN
     218            0 :          CPABORT("The linked ELPA library does not support the required API version")
     219              :       END IF
     220        10426 :       IF (PRESENT(one_stage)) elpa_one_stage = one_stage
     221        10426 :       IF (PRESENT(should_print)) elpa_print = should_print
     222        10426 :       IF (PRESENT(qr)) elpa_qr = qr
     223              : #else
     224              :       MARK_USED(one_stage)
     225              :       MARK_USED(qr)
     226              :       MARK_USED(should_print)
     227              :       CPABORT("Initialization of ELPA library requested but not enabled during build")
     228              : #endif
     229        10426 :    END SUBROUTINE initialize_elpa_library
     230              : 
     231              : ! **************************************************************************************************
     232              : !> \brief Finalize the ELPA library
     233              : ! **************************************************************************************************
     234        10817 :    SUBROUTINE finalize_elpa_library()
     235              : #if defined(__ELPA)
     236        10817 :       CALL elpa_uninit()
     237              : #else
     238              :       CPABORT("Finalization of ELPA library requested but not enabled during build")
     239              : #endif
     240        10817 :    END SUBROUTINE finalize_elpa_library
     241              : 
     242              : ! **************************************************************************************************
     243              : !> \brief Sets the active ELPA kernel.
     244              : !> \param requested_kernel one of the elpa_kernel_ids
     245              : ! **************************************************************************************************
     246        10426 :    SUBROUTINE set_elpa_kernel(requested_kernel)
     247              :       INTEGER, INTENT(IN)                                :: requested_kernel
     248              : 
     249              : #if defined(__ELPA)
     250              :       INTEGER                                            :: cpuid
     251              : 
     252        10426 :       elpa_kernel = requested_kernel
     253              : 
     254              :       ! Resolve AUTO kernel.
     255        10426 :       IF (elpa_kernel == ELPA_2STAGE_REAL_INVALID) THEN
     256        10422 :          cpuid = m_cpuid_static()
     257            0 :          SELECT CASE (cpuid)
     258              :          CASE (MACHINE_CPU_GENERIC)
     259            0 :             elpa_kernel = ELPA_2STAGE_REAL_GENERIC
     260              :          CASE (MACHINE_X86_SSE4)
     261            0 :             elpa_kernel = ELPA_2STAGE_REAL_SSE_BLOCK4
     262              :          CASE (MACHINE_X86_AVX)
     263            0 :             elpa_kernel = ELPA_2STAGE_REAL_AVX_BLOCK4
     264              :          CASE (MACHINE_X86_AVX2)
     265        10422 :             elpa_kernel = ELPA_2STAGE_REAL_AVX2_BLOCK4
     266              :          CASE (MACHINE_X86_AVX512)
     267        10422 :             elpa_kernel = ELPA_2STAGE_REAL_AVX512_BLOCK4
     268              :          END SELECT
     269              : 
     270              :          ! Prefer GPU kernel if available.
     271              : #if !defined(__NO_OFFLOAD_ELPA)
     272              : #if defined(__OFFLOAD_CUDA)
     273              :          elpa_kernel = ELPA_2STAGE_REAL_NVIDIA_GPU
     274              : #endif
     275              : #if defined(__OFFLOAD_HIP)
     276              :          elpa_kernel = ELPA_2STAGE_REAL_AMD_GPU
     277              : #endif
     278              : #if defined(__OFFLOAD_OPENCL)
     279              :          elpa_kernel = ELPA_2STAGE_REAL_INTEL_GPU_SYCL
     280              : #endif
     281              : #endif
     282              :          ! If we could not find a suitable kernel then use ELPA_2STAGE_REAL_DEFAULT.
     283        10422 :          IF (elpa_kernel == ELPA_2STAGE_REAL_INVALID) THEN
     284            0 :             elpa_kernel = ELPA_2STAGE_REAL_DEFAULT
     285              :          END IF
     286              :       END IF
     287              : #else
     288              :       MARK_USED(requested_kernel)
     289              : #endif
     290        10426 :    END SUBROUTINE set_elpa_kernel
     291              : 
     292              : ! **************************************************************************************************
     293              : !> \brief Driver routine to diagonalize a FM matrix with the ELPA library.
     294              : !> \param matrix the matrix that is diagonalized
     295              : !> \param eigenvectors eigenvectors of the input matrix
     296              : !> \param eigenvalues eigenvalues of the input matrix
     297              : ! **************************************************************************************************
     298        14420 :    SUBROUTINE cp_fm_diag_elpa(matrix, eigenvectors, eigenvalues)
     299              :       TYPE(cp_fm_type), INTENT(IN)          :: matrix, eigenvectors
     300              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT) :: eigenvalues
     301              : 
     302              : #if defined(__ELPA)
     303              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_diag_elpa'
     304              : 
     305              :       INTEGER                                  :: handle
     306              :       TYPE(cp_fm_type)                         :: eigenvectors_new, matrix_new
     307              :       TYPE(cp_fm_redistribute_info)            :: rdinfo
     308              : 
     309        14420 :       CALL timeset(routineN, handle)
     310              : 
     311              :       ! Determine if the input matrix needs to be redistributed before diagonalization.
     312              :       ! Heuristics are used to determine the optimal number of CPUs for diagonalization.
     313              :       ! The redistributed matrix is stored in matrix_new, which is just a pointer
     314              :       ! to the original matrix if no redistribution is required.
     315              :       ! With ELPA, we have to make sure that all processor columns have nonzero width
     316              :       CALL cp_fm_redistribute_start(matrix, eigenvectors, matrix_new, eigenvectors_new, &
     317        14420 :                                     caller_is_elpa=.TRUE., redist_info=rdinfo)
     318              : 
     319              :       ! Call ELPA on CPUs that hold the new matrix
     320        14420 :       IF (ASSOCIATED(matrix_new%matrix_struct)) THEN
     321         9285 :          CALL cp_fm_diag_elpa_base(matrix_new, eigenvectors_new, eigenvalues, rdinfo)
     322              :       END IF
     323              : 
     324              :       ! Redistribute results and clean up
     325        14420 :       CALL cp_fm_redistribute_end(matrix, eigenvectors, eigenvalues, matrix_new, eigenvectors_new)
     326              : 
     327        14420 :       CALL timestop(handle)
     328              : #else
     329              :       eigenvalues = 0
     330              :       MARK_USED(matrix)
     331              :       MARK_USED(eigenvectors)
     332              : 
     333              :       CPABORT("CP2K compiled without the ELPA library.")
     334              : #endif
     335        14420 :    END SUBROUTINE cp_fm_diag_elpa
     336              : 
     337              : #if defined(__ELPA)
     338              : ! **************************************************************************************************
     339              : !> \brief Actual routine that calls ELPA to diagonalize a FM matrix.
     340              : !> \param matrix the matrix that is diagonalized
     341              : !> \param eigenvectors eigenvectors of the input matrix
     342              : !> \param eigenvalues eigenvalues of the input matrix
     343              : !> \param rdinfo ...
     344              : ! **************************************************************************************************
     345         9285 :    SUBROUTINE cp_fm_diag_elpa_base(matrix, eigenvectors, eigenvalues, rdinfo)
     346              : 
     347              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix, eigenvectors
     348              :       REAL(KIND=dp), DIMENSION(:), INTENT(OUT)           :: eigenvalues
     349              :       TYPE(cp_fm_redistribute_info), INTENT(IN)          :: rdinfo
     350              : 
     351              :       CHARACTER(len=*), PARAMETER :: routineN = 'cp_fm_diag_elpa_base'
     352              : 
     353              :       INTEGER                                            :: handle
     354              : 
     355              :       CLASS(elpa_t), POINTER                   :: elpa_obj
     356              :       CHARACTER(len=default_string_length)     :: kernel_name, message
     357              :       TYPE(mp_comm_type) :: group
     358              :       INTEGER                                  :: fallback_kernel, &
     359              :                                                   mypcol, myprow, n, &
     360              :                                                   n_rows, n_cols, &
     361              :                                                   nblk, neig, io_unit, &
     362              :                                                   success
     363              :       LOGICAL                                  :: use_qr, check_eigenvalues
     364         9285 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: eval, eval_noqr
     365              :       TYPE(cp_blacs_env_type), POINTER         :: context
     366              :       TYPE(cp_fm_type)                         :: matrix_noqr, eigenvectors_noqr
     367              :       TYPE(cp_logger_type), POINTER            :: logger
     368              :       REAL(KIND=dp), PARAMETER                 :: th = 1.0E-14_dp
     369         9285 :       INTEGER, DIMENSION(:), POINTER           :: ncol_locals
     370              : #if defined(__HAS_IEEE_EXCEPTIONS)
     371              :       LOGICAL, DIMENSION(5)                    :: halt
     372              : #endif
     373              : 
     374         9285 :       CALL timeset(routineN, handle)
     375         9285 :       NULLIFY (logger)
     376         9285 :       NULLIFY (ncol_locals)
     377              : 
     378         9285 :       check_eigenvalues = .FALSE.
     379              : 
     380         9285 :       logger => cp_get_default_logger()
     381         9285 :       io_unit = cp_logger_get_default_io_unit(logger)
     382              : 
     383         9285 :       n = matrix%matrix_struct%nrow_global
     384         9285 :       context => matrix%matrix_struct%context
     385         9285 :       group = matrix%matrix_struct%para_env
     386              : 
     387         9285 :       myprow = context%mepos(1)
     388         9285 :       mypcol = context%mepos(2)
     389              : 
     390              :       ! elpa needs the full matrix
     391         9285 :       CALL cp_fm_uplo_to_full(matrix, eigenvectors)
     392              : 
     393              :       CALL cp_fm_struct_get(matrix%matrix_struct, &
     394              :                             local_leading_dimension=n_rows, &
     395              :                             ncol_local=n_cols, &
     396              :                             nrow_block=nblk, &
     397         9285 :                             ncol_locals=ncol_locals)
     398              : 
     399              :       ! ELPA will fail in 'solve_tridi', with no useful error message, fail earlier
     400        18576 :       IF (io_unit > 0 .AND. ANY(ncol_locals == 0)) THEN
     401            0 :          CALL rdinfo%write(io_unit)
     402            0 :          CALL cp_fm_write_info(matrix, io_unit)
     403            0 :          CPABORT("ELPA [pre-fail]: Problem contains processor column with zero width.")
     404              :       END IF
     405              : 
     406         9285 :       neig = SIZE(eigenvalues, 1)
     407              :       ! Decide if matrix is suitable for ELPA to use QR
     408              :       ! The definition of what is considered a suitable matrix depends on the ELPA version
     409              :       ! The relevant ELPA files to check are
     410              :       !     - Proper matrix order:  src/elpa2/elpa2_template.F90
     411              :       !     - Proper block size:    test/Fortran/test.F90
     412              :       ! Note that the names of these files might change in different ELPA versions
     413              :       ! Matrix order must be even
     414         9285 :       use_qr = elpa_qr .AND. (MODULO(n, 2) == 0)
     415              :       ! Matrix order and block size must be greater than or equal to 64
     416         9285 :       IF (.NOT. elpa_qr_unsafe) THEN
     417            0 :          use_qr = use_qr .AND. (n >= 64) .AND. (nblk >= 64)
     418              :       END IF
     419              : 
     420              :       ! Check if eigenvalues computed with elpa_qr_unsafe should be verified
     421         9285 :       IF (use_qr .AND. elpa_qr_unsafe .AND. elpa_print) THEN
     422           40 :          check_eigenvalues = .TRUE.
     423              :       END IF
     424              : 
     425         9285 :       CALL matrix%matrix_struct%para_env%bcast(check_eigenvalues)
     426              : 
     427         9285 :       IF (check_eigenvalues) THEN
     428              :          ! Allocate and initialize needed temporaries to compute eigenvalues without ELPA QR
     429          120 :          ALLOCATE (eval_noqr(n))
     430           40 :          CALL cp_fm_create(matrix=matrix_noqr, matrix_struct=matrix%matrix_struct)
     431           40 :          CALL cp_fm_to_fm(matrix, matrix_noqr)
     432           40 :          CALL cp_fm_create(matrix=eigenvectors_noqr, matrix_struct=eigenvectors%matrix_struct)
     433           80 :          CALL cp_fm_uplo_to_full(matrix_noqr, eigenvectors_noqr)
     434              :       END IF
     435              : 
     436         9285 :       IF (io_unit > 0 .AND. elpa_print) THEN
     437              :          WRITE (UNIT=io_unit, FMT="(/,T2,A)") &
     438           30 :             "ELPA| Matrix diagonalization information"
     439              : 
     440           30 :          kernel_name = get_elpa_kernel_name(elpa_kernel)
     441              : 
     442              :          WRITE (UNIT=io_unit, FMT="(T2,A,T71,I10)") &
     443           30 :             "ELPA| Matrix order (NA) ", n, &
     444           30 :             "ELPA| Matrix block size (NBLK) ", nblk, &
     445           30 :             "ELPA| Number of eigenvectors (NEV) ", neig, &
     446           30 :             "ELPA| Local rows (LOCAL_NROWS) ", n_rows, &
     447           60 :             "ELPA| Local columns (LOCAL_NCOLS) ", n_cols
     448              :          WRITE (UNIT=io_unit, FMT="(T2,A,T61,A20)") &
     449           30 :             "ELPA| Kernel ", ADJUSTR(TRIM(kernel_name))
     450           30 :          IF (elpa_qr) THEN
     451              :             WRITE (UNIT=io_unit, FMT="(T2,A,T78,A3)") &
     452           20 :                "ELPA| QR step requested ", "YES"
     453              :          ELSE
     454              :             WRITE (UNIT=io_unit, FMT="(T2,A,T79,A2)") &
     455           10 :                "ELPA| QR step requested ", "NO"
     456              :          END IF
     457              : 
     458           30 :          IF (elpa_qr) THEN
     459           20 :             IF (use_qr) THEN
     460              :                WRITE (UNIT=io_unit, FMT="(T2,A,T78,A3)") &
     461           20 :                   "ELPA| Matrix is suitable for QR ", "YES"
     462              :             ELSE
     463              :                WRITE (UNIT=io_unit, FMT="(T2,A,T79,A2)") &
     464            0 :                   "ELPA| Matrix is suitable for QR ", "NO"
     465              :             END IF
     466           20 :             IF (.NOT. use_qr) THEN
     467            0 :                IF (MODULO(n, 2) /= 0) THEN
     468              :                   WRITE (UNIT=io_unit, FMT="(T2,A)") &
     469            0 :                      "ELPA| Matrix order is NOT even"
     470              :                END IF
     471            0 :                IF ((nblk < 64) .AND. (.NOT. elpa_qr_unsafe)) THEN
     472              :                   WRITE (UNIT=io_unit, FMT="(T2,A)") &
     473            0 :                      "ELPA| Matrix block size is NOT 64 or greater"
     474              :                END IF
     475              :             ELSE
     476           20 :                IF ((nblk < 64) .AND. elpa_qr_unsafe) THEN
     477              :                   WRITE (UNIT=io_unit, FMT="(T2,A)") &
     478           10 :                      "ELPA| Matrix block size check was bypassed"
     479              :                END IF
     480              :             END IF
     481              :          END IF
     482              :       END IF
     483              : 
     484              :       ! the full eigenvalues vector is needed
     485        27855 :       ALLOCATE (eval(n))
     486              : 
     487         9285 :       elpa_obj => elpa_allocate()
     488              : 
     489         9285 :       CALL elpa_obj%set("na", n, success)
     490         9285 :       CPASSERT(success == ELPA_OK)
     491              : 
     492         9285 :       CALL elpa_obj%set("nev", neig, success)
     493         9285 :       CPASSERT(success == ELPA_OK)
     494              : 
     495         9285 :       CALL elpa_obj%set("local_nrows", n_rows, success)
     496         9285 :       CPASSERT(success == ELPA_OK)
     497              : 
     498         9285 :       CALL elpa_obj%set("local_ncols", n_cols, success)
     499         9285 :       CPASSERT(success == ELPA_OK)
     500              : 
     501         9285 :       CALL elpa_obj%set("nblk", nblk, success)
     502         9285 :       CPASSERT(success == ELPA_OK)
     503              : 
     504         9285 :       CALL elpa_obj%set("mpi_comm_parent", group%get_handle(), success)
     505         9285 :       CPASSERT(success == ELPA_OK)
     506              : 
     507         9285 :       CALL elpa_obj%set("process_row", myprow, success)
     508         9285 :       CPASSERT(success == ELPA_OK)
     509              : 
     510         9285 :       CALL elpa_obj%set("process_col", mypcol, success)
     511         9285 :       CPASSERT(success == ELPA_OK)
     512              : 
     513         9285 :       success = elpa_obj%setup()
     514         9285 :       CPASSERT(success == ELPA_OK)
     515              : 
     516              :       CALL elpa_obj%set("solver", &
     517              :                         MERGE(ELPA_SOLVER_1STAGE, ELPA_SOLVER_2STAGE, elpa_one_stage), &
     518        18550 :                         success)
     519         9285 :       IF (success /= ELPA_OK) THEN
     520            0 :          CPABORT("Setting solver for ELPA failed")
     521              :       END IF
     522              : 
     523              :       ! enabling the GPU must happen before setting the kernel
     524            0 :       SELECT CASE (elpa_kernel)
     525              :       CASE (ELPA_2STAGE_REAL_NVIDIA_GPU)
     526            0 :          CALL elpa_obj%set("nvidia-gpu", 1, success)
     527            0 :          CPASSERT(success == ELPA_OK)
     528              :       CASE (ELPA_2STAGE_REAL_AMD_GPU)
     529            0 :          CALL elpa_obj%set("amd-gpu", 1, success)
     530            0 :          CPASSERT(success == ELPA_OK)
     531              :       CASE (ELPA_2STAGE_REAL_INTEL_GPU_SYCL)
     532            0 :          CALL elpa_obj%set("intel-gpu", 1, success)
     533         9285 :          CPASSERT(success == ELPA_OK)
     534              :       END SELECT
     535              : 
     536         9285 :       IF (.NOT. elpa_one_stage) THEN
     537              :          ! Keep ELPA's configured default in case the requested kernel is unavailable.
     538         9265 :          CALL elpa_obj%get("real_kernel", fallback_kernel, success)
     539         9265 :          CPASSERT(success == ELPA_OK)
     540              : 
     541         9265 :          CALL elpa_obj%set("real_kernel", elpa_kernel, success)
     542         9265 :          IF (success /= ELPA_OK) THEN
     543              :             message = "Requested ELPA real kernel "//TRIM(get_elpa_kernel_name(elpa_kernel))// &
     544            0 :                       " is unavailable; falling back to "//TRIM(get_elpa_kernel_name(fallback_kernel))
     545            0 :             CPWARN(TRIM(message))
     546            0 :             CALL elpa_obj%set("real_kernel", fallback_kernel, success)
     547            0 :             CPASSERT(success == ELPA_OK)
     548              :             ! Avoid retrying the unavailable kernel for every subsequent diagonalization.
     549            0 :             elpa_kernel = fallback_kernel
     550              :          END IF
     551              : 
     552         9265 :          IF (use_qr) THEN
     553           40 :             CALL elpa_obj%set("qr", 1, success)
     554           40 :             CPASSERT(success == ELPA_OK)
     555              :          END IF
     556              :       END IF
     557              : 
     558              :       ! Set number of threads only when ELPA was built with OpenMP support.
     559         9285 :       IF (elpa_obj%can_set("omp_threads", omp_get_max_threads()) == ELPA_OK) THEN
     560         9285 :          CALL elpa_obj%set("omp_threads", omp_get_max_threads(), success)
     561         9285 :          CPASSERT(success == ELPA_OK)
     562              :       END IF
     563              : 
     564              :       ! ELPA solver: calculate the Eigenvalues/vectors
     565              : #if defined(__HAS_IEEE_EXCEPTIONS)
     566              :       CALL ieee_get_halting_mode(IEEE_ALL, halt)
     567              :       CALL ieee_set_halting_mode(IEEE_ALL, .FALSE.)
     568              : #endif
     569         9285 :       CALL elpa_obj%eigenvectors(matrix%local_data, eval, eigenvectors%local_data, success)
     570              : #if defined(__HAS_IEEE_EXCEPTIONS)
     571              :       CALL ieee_set_halting_mode(IEEE_ALL, halt)
     572              : #endif
     573              : 
     574         9285 :       IF (success /= ELPA_OK) THEN
     575            0 :          CPABORT("ELPA failed to diagonalize a matrix")
     576              :       END IF
     577              : 
     578         9285 :       IF (check_eigenvalues) THEN
     579              :          ! run again without QR
     580           40 :          CALL elpa_obj%set("qr", 0, success)
     581           40 :          CPASSERT(success == ELPA_OK)
     582              : 
     583           40 :          CALL elpa_obj%eigenvectors(matrix_noqr%local_data, eval_noqr, eigenvectors_noqr%local_data, success)
     584           40 :          IF (success /= ELPA_OK) THEN
     585            0 :             CPABORT("ELPA failed to diagonalize a matrix even without QR decomposition")
     586              :          END IF
     587              : 
     588         3400 :          IF (ANY(ABS(eval(1:neig) - eval_noqr(1:neig)) > th)) THEN
     589            0 :             CPABORT("ELPA failed to calculate Eigenvalues with ELPA's QR decomposition")
     590              :          END IF
     591              : 
     592           40 :          DEALLOCATE (eval_noqr)
     593           40 :          CALL cp_fm_release(matrix_noqr)
     594           40 :          CALL cp_fm_release(eigenvectors_noqr)
     595              :       END IF
     596              : 
     597         9285 :       CALL elpa_deallocate(elpa_obj, success)
     598         9285 :       CPASSERT(success == ELPA_OK)
     599              : 
     600       770780 :       eigenvalues(1:neig) = eval(1:neig)
     601         9285 :       DEALLOCATE (eval)
     602              : 
     603         9285 :       CALL timestop(handle)
     604              : 
     605        37140 :    END SUBROUTINE cp_fm_diag_elpa_base
     606              : #endif
     607              : 
     608              : END MODULE cp_fm_elpa
        

Generated by: LCOV version 2.0-1