LCOV - code coverage report
Current view: top level - src - preconditioner_makes.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:6d276e9) Lines: 92.5 % 522 483
Test Date: 2026-09-10 07:29:18 Functions: 100.0 % 14 14

            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 computes preconditioners, and implements methods to apply them
      10              : !>      currently used in qs_ot
      11              : !> \par History
      12              : !>      - [UB] 2009-05-13 Adding stable approximate inverse (full and sparse)
      13              : !> \author Joost VandeVondele (09.2002)
      14              : ! **************************************************************************************************
      15              : MODULE preconditioner_makes
      16              :    USE arnoldi_api,                     ONLY: arnoldi_env_type,&
      17              :                                               arnoldi_ev,&
      18              :                                               deallocate_arnoldi_env,&
      19              :                                               get_selected_ritz_val,&
      20              :                                               get_selected_ritz_vector,&
      21              :                                               set_arnoldi_initial_vector,&
      22              :                                               setup_arnoldi_env
      23              :    USE cp_cfm_basic_linalg,             ONLY: cp_cfm_column_scale,&
      24              :                                               cp_cfm_gemm,&
      25              :                                               cp_cfm_scale_and_add,&
      26              :                                               cp_cfm_triangular_multiply,&
      27              :                                               cp_cfm_uplo_to_full
      28              :    USE cp_cfm_cholesky,                 ONLY: cp_cfm_cholesky_decompose,&
      29              :                                               cp_cfm_cholesky_invert
      30              :    USE cp_cfm_diag,                     ONLY: cp_cfm_geeig
      31              :    USE cp_cfm_types,                    ONLY: cp_cfm_create,&
      32              :                                               cp_cfm_get_info,&
      33              :                                               cp_cfm_release,&
      34              :                                               cp_cfm_set_element,&
      35              :                                               cp_cfm_to_cfm,&
      36              :                                               cp_cfm_type
      37              :    USE cp_dbcsr_api,                    ONLY: &
      38              :         dbcsr_add, dbcsr_copy, dbcsr_create, dbcsr_get_info, dbcsr_multiply, dbcsr_p_type, &
      39              :         dbcsr_release, dbcsr_type, dbcsr_type_symmetric
      40              :    USE cp_dbcsr_contrib,                ONLY: dbcsr_add_on_diag
      41              :    USE cp_dbcsr_operations,             ONLY: copy_dbcsr_to_fm,&
      42              :                                               cp_dbcsr_m_by_n_from_template,&
      43              :                                               cp_dbcsr_sm_fm_multiply,&
      44              :                                               cp_fm_to_dbcsr_row_template
      45              :    USE cp_fm_basic_linalg,              ONLY: cp_fm_column_scale,&
      46              :                                               cp_fm_triangular_invert,&
      47              :                                               cp_fm_triangular_multiply,&
      48              :                                               cp_fm_uplo_to_full
      49              :    USE cp_fm_cholesky,                  ONLY: cp_fm_cholesky_decompose,&
      50              :                                               cp_fm_cholesky_reduce,&
      51              :                                               cp_fm_cholesky_restore
      52              :    USE cp_fm_diag,                      ONLY: choose_eigv_solver
      53              :    USE cp_fm_struct,                    ONLY: cp_fm_struct_create,&
      54              :                                               cp_fm_struct_release,&
      55              :                                               cp_fm_struct_type
      56              :    USE cp_fm_types,                     ONLY: cp_fm_create,&
      57              :                                               cp_fm_get_diag,&
      58              :                                               cp_fm_get_info,&
      59              :                                               cp_fm_release,&
      60              :                                               cp_fm_to_fm,&
      61              :                                               cp_fm_type
      62              :    USE input_constants,                 ONLY: &
      63              :         cholesky_inverse, cholesky_reduce, ot_precond_fermi_low_rank, ot_precond_full_all, &
      64              :         ot_precond_full_kinetic, ot_precond_full_single, ot_precond_full_single_inverse, &
      65              :         ot_precond_s_inverse, ot_precond_solver_default, ot_precond_solver_inv_chol
      66              :    USE kinds,                           ONLY: dp
      67              :    USE mathconstants,                   ONLY: z_one,&
      68              :                                               z_zero
      69              :    USE parallel_gemm_api,               ONLY: parallel_gemm
      70              :    USE preconditioner_types,            ONLY: preconditioner_type
      71              : #include "./base/base_uses.f90"
      72              : 
      73              :    IMPLICIT NONE
      74              : 
      75              :    PRIVATE
      76              : 
      77              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'preconditioner_makes'
      78              : 
      79              :    PUBLIC :: make_complex_full_all, make_complex_full_kinetic, make_complex_full_s_inverse, &
      80              :              make_complex_full_single, make_complex_full_single_inverse, &
      81              :              make_preconditioner_matrix
      82              : 
      83              : CONTAINS
      84              : 
      85              : ! **************************************************************************************************
      86              : !> \brief Build the state-selective FULL_ALL operator for a complex k-point channel.
      87              : !>        The occupied/reference subspace is retained in its current gauge, while the
      88              : !>        orthogonal complement carries the spectrum of H(k).
      89              : !> \param preconditioner_env preconditioner storage
      90              : !> \param matrix_c0 complex reference orbitals, C^H S C = I
      91              : !> \param matrix_h complex Hermitian k-point Hamiltonian
      92              : !> \param matrix_s complex Hermitian k-point overlap
      93              : !> \param c0_evals reference-orbital energies in the current OT gauge
      94              : !> \param energy_gap denominator floor
      95              : ! **************************************************************************************************
      96          152 :    SUBROUTINE make_complex_full_all(preconditioner_env, matrix_c0, matrix_h, matrix_s, &
      97          152 :                                     c0_evals, energy_gap)
      98              : 
      99              :       TYPE(preconditioner_type)                          :: preconditioner_env
     100              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix_c0, matrix_h, matrix_s
     101              :       REAL(KIND=dp), DIMENSION(:), INTENT(IN)            :: c0_evals
     102              :       REAL(KIND=dp), INTENT(IN)                          :: energy_gap
     103              : 
     104              :       CHARACTER(len=*), PARAMETER :: routineN = 'make_complex_full_all'
     105              :       REAL(KIND=dp), PARAMETER                           :: fudge_factor = 0.25_dp, &
     106              :                                                             lambda_base = 10.0_dp
     107              : 
     108          152 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:)        :: shifted_evals
     109              :       COMPLEX(KIND=dp), CONTIGUOUS, DIMENSION(:, :), &
     110          152 :          POINTER                                         :: local_data
     111              :       INTEGER                                            :: handle, j, k, n, ncol_local, nrow_local
     112          152 :       INTEGER, DIMENSION(:), POINTER                     :: col_indices
     113              :       REAL(KIND=dp)                                      :: error_estimate, lambda
     114          152 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: residual_norm_sq
     115              :       TYPE(cp_cfm_type) :: matrix_chc, matrix_diag_work, matrix_hc0, matrix_s_copy, matrix_sc0, &
     116              :          matrix_sc_chc, matrix_shifted_sc, matrix_tmp
     117              : 
     118          152 :       CALL timeset(routineN, handle)
     119              : 
     120          152 :       CALL cp_cfm_get_info(matrix_c0, nrow_global=n, ncol_global=k)
     121          152 :       CPASSERT(n > 0)
     122          152 :       CPASSERT(k > 0 .AND. k <= n)
     123          152 :       CPASSERT(SIZE(c0_evals) >= k)
     124          152 :       CPASSERT(energy_gap > 0.0_dp)
     125              : 
     126          152 :       IF (ASSOCIATED(preconditioner_env%complex_fm)) THEN
     127            0 :          CALL cp_cfm_release(preconditioner_env%complex_fm)
     128            0 :          DEALLOCATE (preconditioner_env%complex_fm)
     129              :       END IF
     130          152 :       IF (ASSOCIATED(preconditioner_env%occ_evals)) DEALLOCATE (preconditioner_env%occ_evals)
     131          152 :       IF (ASSOCIATED(preconditioner_env%full_evals)) DEALLOCATE (preconditioner_env%full_evals)
     132              : 
     133          152 :       ALLOCATE (preconditioner_env%complex_fm)
     134              :       CALL cp_cfm_create(preconditioner_env%complex_fm, matrix_h%matrix_struct, &
     135          152 :                          name='complex FULL_ALL eigenvectors')
     136          760 :       ALLOCATE (preconditioner_env%full_evals(n), preconditioner_env%occ_evals(k))
     137              : 
     138          152 :       CALL cp_cfm_create(matrix_hc0, matrix_c0%matrix_struct, name='complex FULL_ALL HC')
     139          152 :       CALL cp_cfm_create(matrix_sc0, matrix_c0%matrix_struct, name='complex FULL_ALL SC')
     140              :       CALL cp_cfm_create(matrix_chc, matrix_c0%matrix_struct, nrow=k, ncol=k, &
     141          152 :                          name='complex FULL_ALL CHC')
     142              :       CALL cp_cfm_create(matrix_sc_chc, matrix_c0%matrix_struct, &
     143          152 :                          name='complex FULL_ALL SC CHC')
     144              :       CALL cp_cfm_create(matrix_shifted_sc, matrix_c0%matrix_struct, &
     145          152 :                          name='complex FULL_ALL shifted SC')
     146          152 :       CALL cp_cfm_create(matrix_tmp, matrix_h%matrix_struct, name='complex FULL_ALL projected H')
     147          152 :       CALL cp_cfm_create(matrix_s_copy, matrix_s%matrix_struct, name='complex FULL_ALL S copy')
     148              :       CALL cp_cfm_create(matrix_diag_work, matrix_h%matrix_struct, &
     149          152 :                          name='complex FULL_ALL diagonalization work')
     150              : 
     151          152 :       CALL cp_cfm_gemm('N', 'N', n, k, n, z_one, matrix_h, matrix_c0, z_zero, matrix_hc0)
     152          152 :       CALL cp_cfm_gemm('N', 'N', n, k, n, z_one, matrix_s, matrix_c0, z_zero, matrix_sc0)
     153          152 :       CALL cp_cfm_gemm('C', 'N', k, k, n, z_one, matrix_c0, matrix_hc0, z_zero, matrix_chc)
     154              : 
     155              :       ! Estimate the S^-1 norm of the Ritz residual, R = H C - S C epsilon.
     156              :       ! As in the real FULL_ALL implementation, use it to prevent an inaccurate
     157              :       ! reference subspace from producing an overly aggressive preconditioner.
     158          608 :       ALLOCATE (shifted_evals(k), residual_norm_sq(k))
     159         1610 :       shifted_evals(:) = CMPLX(c0_evals(1:k), 0.0_dp, KIND=dp)
     160          152 :       CALL cp_cfm_to_cfm(matrix_sc0, matrix_sc_chc)
     161          152 :       CALL cp_cfm_column_scale(matrix_sc_chc, shifted_evals)
     162          152 :       CALL cp_cfm_to_cfm(matrix_hc0, matrix_shifted_sc)
     163          152 :       CALL cp_cfm_scale_and_add(z_one, matrix_shifted_sc, -z_one, matrix_sc_chc)
     164          152 :       CALL cp_cfm_to_cfm(matrix_s, matrix_s_copy)
     165          152 :       CALL cp_cfm_cholesky_decompose(matrix_s_copy)
     166              :       CALL cp_cfm_triangular_multiply(matrix_s_copy, matrix_shifted_sc, side='L', &
     167          152 :                                       transa_tr='C', invert_tr=.TRUE., uplo_tr='U')
     168              : 
     169          152 :       residual_norm_sq(:) = 0.0_dp
     170              :       CALL cp_cfm_get_info(matrix_shifted_sc, nrow_local=nrow_local, ncol_local=ncol_local, &
     171          152 :                            col_indices=col_indices, local_data=local_data)
     172         1610 :       DO j = 1, ncol_local
     173              :          residual_norm_sq(col_indices(j)) = residual_norm_sq(col_indices(j)) + &
     174        62054 :                                             SUM(ABS(local_data(1:nrow_local, j))**2)
     175              :       END DO
     176          152 :       CALL preconditioner_env%para_env%sum(residual_norm_sq)
     177         1610 :       error_estimate = SQRT(MAXVAL(residual_norm_sq))
     178          152 :       preconditioner_env%energy_gap = MAX(energy_gap, error_estimate*fudge_factor)
     179          152 :       lambda = lambda_base + error_estimate
     180              : 
     181              :       ! Q^H H Q with Q = I - C C^H S. This removes occupied/complement cross terms
     182              :       ! without rotating the reference columns.
     183          152 :       CALL cp_cfm_to_cfm(matrix_h, matrix_tmp)
     184          152 :       CALL cp_cfm_gemm('N', 'C', n, n, k, -z_one, matrix_hc0, matrix_sc0, z_one, matrix_tmp)
     185          152 :       CALL cp_cfm_gemm('N', 'C', n, n, k, -z_one, matrix_sc0, matrix_hc0, z_one, matrix_tmp)
     186          152 :       CALL cp_cfm_gemm('N', 'N', n, k, k, z_one, matrix_sc0, matrix_chc, z_zero, matrix_sc_chc)
     187          152 :       CALL cp_cfm_gemm('N', 'C', n, n, k, z_one, matrix_sc_chc, matrix_sc0, z_one, matrix_tmp)
     188              : 
     189              :       ! Shift the retained reference subspace below the complementary spectrum, diagonalize,
     190              :       ! and then restore the original reference columns and their energy labels exactly.
     191         1610 :       shifted_evals(:) = CMPLX(c0_evals(1:k) - lambda, 0.0_dp, KIND=dp)
     192          152 :       CALL cp_cfm_to_cfm(matrix_sc0, matrix_shifted_sc)
     193          152 :       CALL cp_cfm_column_scale(matrix_shifted_sc, shifted_evals)
     194          152 :       CALL cp_cfm_gemm('N', 'C', n, n, k, z_one, matrix_shifted_sc, matrix_sc0, z_one, matrix_tmp)
     195              : 
     196          152 :       CALL cp_cfm_to_cfm(matrix_s, matrix_s_copy)
     197              :       CALL cp_cfm_geeig(matrix_tmp, matrix_s_copy, preconditioner_env%complex_fm, &
     198          152 :                         preconditioner_env%full_evals, matrix_diag_work)
     199              : 
     200         1610 :       preconditioner_env%occ_evals(:) = c0_evals(1:k)
     201         1610 :       preconditioner_env%full_evals(1:k) = c0_evals(1:k)
     202          152 :       CALL cp_cfm_to_cfm(matrix_c0, preconditioner_env%complex_fm, k)
     203          152 :       preconditioner_env%in_use = ot_precond_full_all
     204          152 :       preconditioner_env%solver = ot_precond_solver_default
     205              : 
     206          152 :       DEALLOCATE (residual_norm_sq, shifted_evals)
     207          152 :       CALL cp_cfm_release(matrix_diag_work)
     208          152 :       CALL cp_cfm_release(matrix_s_copy)
     209          152 :       CALL cp_cfm_release(matrix_tmp)
     210          152 :       CALL cp_cfm_release(matrix_shifted_sc)
     211          152 :       CALL cp_cfm_release(matrix_sc_chc)
     212          152 :       CALL cp_cfm_release(matrix_chc)
     213          152 :       CALL cp_cfm_release(matrix_sc0)
     214          152 :       CALL cp_cfm_release(matrix_hc0)
     215              : 
     216          152 :       CALL timestop(handle)
     217              : 
     218          456 :    END SUBROUTINE make_complex_full_all
     219              : 
     220              : ! **************************************************************************************************
     221              : !> \brief Build the complex spectral FULL_SINGLE preconditioner.
     222              : !> \param preconditioner_env preconditioner storage
     223              : !> \param matrix_h complex Hermitian k-point Hamiltonian
     224              : !> \param matrix_s complex Hermitian k-point overlap
     225              : !> \param energy_homo occupied spectral edge
     226              : !> \param energy_gap denominator floor
     227              : ! **************************************************************************************************
     228           28 :    SUBROUTINE make_complex_full_single(preconditioner_env, matrix_h, matrix_s, &
     229              :                                        energy_homo, energy_gap)
     230              : 
     231              :       TYPE(preconditioner_type)                          :: preconditioner_env
     232              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix_h, matrix_s
     233              :       REAL(KIND=dp), INTENT(IN)                          :: energy_homo, energy_gap
     234              : 
     235              :       CHARACTER(len=*), PARAMETER :: routineN = 'make_complex_full_single'
     236              : 
     237           28 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:)        :: scaling
     238              :       INTEGER                                            :: handle, i, n
     239           28 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: eigenvalues
     240              :       TYPE(cp_cfm_type)                                  :: matrix_diag_a, matrix_diag_b, &
     241              :                                                             matrix_eigenvectors, &
     242              :                                                             matrix_scaled_eigenvectors, matrix_work
     243              : 
     244           28 :       CALL timeset(routineN, handle)
     245              : 
     246           28 :       CALL cp_cfm_get_info(matrix_h, nrow_global=n)
     247           28 :       CPASSERT(n > 0)
     248           28 :       CPASSERT(energy_gap > 0.0_dp)
     249              : 
     250           28 :       IF (ASSOCIATED(preconditioner_env%complex_fm)) THEN
     251            0 :          CALL cp_cfm_release(preconditioner_env%complex_fm)
     252            0 :          DEALLOCATE (preconditioner_env%complex_fm)
     253              :       END IF
     254           28 :       IF (ASSOCIATED(preconditioner_env%occ_evals)) DEALLOCATE (preconditioner_env%occ_evals)
     255           28 :       IF (ASSOCIATED(preconditioner_env%full_evals)) DEALLOCATE (preconditioner_env%full_evals)
     256              : 
     257           28 :       ALLOCATE (preconditioner_env%complex_fm)
     258              :       CALL cp_cfm_create(preconditioner_env%complex_fm, matrix_h%matrix_struct, &
     259           28 :                          name='complex FULL_SINGLE preconditioner')
     260              :       CALL cp_cfm_create(matrix_diag_a, matrix_h%matrix_struct, &
     261           28 :                          name='complex FULL_SINGLE diagonalization A')
     262              :       CALL cp_cfm_create(matrix_diag_b, matrix_s%matrix_struct, &
     263           28 :                          name='complex FULL_SINGLE diagonalization B')
     264              :       CALL cp_cfm_create(matrix_eigenvectors, matrix_h%matrix_struct, &
     265           28 :                          name='complex FULL_SINGLE eigenvectors')
     266              :       CALL cp_cfm_create(matrix_scaled_eigenvectors, matrix_h%matrix_struct, &
     267           28 :                          name='complex FULL_SINGLE scaled eigenvectors')
     268              :       CALL cp_cfm_create(matrix_work, matrix_h%matrix_struct, &
     269           28 :                          name='complex FULL_SINGLE diagonalization work')
     270              : 
     271          140 :       ALLOCATE (eigenvalues(n), scaling(n))
     272           28 :       CALL cp_cfm_to_cfm(matrix_h, matrix_diag_a)
     273           28 :       CALL cp_cfm_to_cfm(matrix_s, matrix_diag_b)
     274              :       CALL cp_cfm_geeig(matrix_diag_a, matrix_diag_b, matrix_eigenvectors, &
     275           28 :                         eigenvalues, matrix_work)
     276          372 :       DO i = 1, n
     277              :          scaling(i) = CMPLX(1.0_dp/MAX(eigenvalues(i) - energy_homo, energy_gap), &
     278          372 :                             0.0_dp, KIND=dp)
     279              :       END DO
     280           28 :       CALL cp_cfm_to_cfm(matrix_eigenvectors, matrix_scaled_eigenvectors)
     281           28 :       CALL cp_cfm_column_scale(matrix_scaled_eigenvectors, scaling)
     282              :       CALL cp_cfm_gemm('N', 'C', n, n, n, z_one, matrix_scaled_eigenvectors, &
     283           28 :                        matrix_eigenvectors, z_zero, preconditioner_env%complex_fm)
     284              : 
     285           28 :       preconditioner_env%energy_gap = energy_gap
     286           28 :       preconditioner_env%in_use = ot_precond_full_single
     287           28 :       preconditioner_env%solver = ot_precond_solver_default
     288              : 
     289           28 :       DEALLOCATE (scaling, eigenvalues)
     290           28 :       CALL cp_cfm_release(matrix_work)
     291           28 :       CALL cp_cfm_release(matrix_scaled_eigenvectors)
     292           28 :       CALL cp_cfm_release(matrix_eigenvectors)
     293           28 :       CALL cp_cfm_release(matrix_diag_b)
     294           28 :       CALL cp_cfm_release(matrix_diag_a)
     295              : 
     296           28 :       CALL timestop(handle)
     297              : 
     298           56 :    END SUBROUTINE make_complex_full_single
     299              : 
     300              : ! **************************************************************************************************
     301              : !> \brief ...
     302              : !> \param preconditioner_env ...
     303              : !> \param matrix_h ...
     304              : !> \param matrix_s ...
     305              : !> \param matrix_t ...
     306              : !> \param mo_coeff ...
     307              : !> \param energy_homo ...
     308              : !> \param eigenvalues_ot ...
     309              : !> \param energy_gap ...
     310              : !> \param my_solver_type ...
     311              : ! **************************************************************************************************
     312         9704 :    SUBROUTINE make_preconditioner_matrix(preconditioner_env, matrix_h, matrix_s, matrix_t, mo_coeff, &
     313         9704 :                                          energy_homo, eigenvalues_ot, energy_gap, &
     314              :                                          my_solver_type)
     315              :       TYPE(preconditioner_type)                          :: preconditioner_env
     316              :       TYPE(dbcsr_type), POINTER                          :: matrix_h
     317              :       TYPE(dbcsr_type), OPTIONAL, POINTER                :: matrix_s, matrix_t
     318              :       TYPE(cp_fm_type), INTENT(IN)                       :: mo_coeff
     319              :       REAL(KIND=dp)                                      :: energy_homo
     320              :       REAL(KIND=dp), DIMENSION(:)                        :: eigenvalues_ot
     321              :       REAL(KIND=dp)                                      :: energy_gap
     322              :       INTEGER                                            :: my_solver_type
     323              : 
     324              :       INTEGER                                            :: precon_type
     325              : 
     326         9704 :       precon_type = preconditioner_env%in_use
     327           38 :       SELECT CASE (precon_type)
     328              :       CASE (ot_precond_full_single)
     329           38 :          IF (my_solver_type /= ot_precond_solver_default) THEN
     330            0 :             CPABORT("Only PRECOND_SOLVER DEFAULT for the moment")
     331              :          END IF
     332           38 :          IF (PRESENT(matrix_s)) THEN
     333              :             CALL make_full_single(preconditioner_env, preconditioner_env%fm, &
     334           32 :                                   matrix_h, matrix_s, energy_homo, energy_gap)
     335              :          ELSE
     336              :             CALL make_full_single_ortho(preconditioner_env, preconditioner_env%fm, &
     337            6 :                                         matrix_h, energy_homo, energy_gap)
     338              :          END IF
     339              : 
     340              :       CASE (ot_precond_s_inverse)
     341           78 :          IF (my_solver_type == ot_precond_solver_default) my_solver_type = ot_precond_solver_inv_chol
     342           78 :          IF (.NOT. PRESENT(matrix_s)) THEN
     343            0 :             CPABORT("Type for S=1 not implemented")
     344              :          END IF
     345           78 :          CALL make_full_s_inverse(preconditioner_env, matrix_s)
     346              : 
     347              :       CASE (ot_precond_full_kinetic)
     348         1321 :          IF (my_solver_type == ot_precond_solver_default) my_solver_type = ot_precond_solver_inv_chol
     349         1321 :          IF (.NOT. (PRESENT(matrix_s) .AND. PRESENT(matrix_t))) THEN
     350            0 :             CPABORT("Type for S=1 not implemented")
     351              :          END IF
     352         1321 :          CALL make_full_kinetic(preconditioner_env, matrix_t, matrix_s, energy_gap)
     353              :       CASE (ot_precond_full_single_inverse)
     354         4501 :          IF (my_solver_type == ot_precond_solver_default) my_solver_type = ot_precond_solver_inv_chol
     355              :          CALL make_full_single_inverse(preconditioner_env, mo_coeff, matrix_h, energy_gap, &
     356         4501 :                                        matrix_s=matrix_s)
     357              :       CASE (ot_precond_full_all)
     358         3762 :          IF (my_solver_type /= ot_precond_solver_default) THEN
     359            0 :             CPABORT("Only PRECOND_SOLVER DEFAULT for the moment")
     360              :          END IF
     361         3762 :          IF (PRESENT(matrix_s)) THEN
     362              :             CALL make_full_all(preconditioner_env, mo_coeff, matrix_h, matrix_s, &
     363         3682 :                                eigenvalues_ot, energy_gap)
     364              :          ELSE
     365              :             CALL make_full_all_ortho(preconditioner_env, mo_coeff, matrix_h, &
     366           80 :                                      eigenvalues_ot, energy_gap)
     367              :          END IF
     368              : 
     369              :       CASE (ot_precond_fermi_low_rank)
     370            4 :          IF (my_solver_type /= ot_precond_solver_default) THEN
     371            0 :             CPABORT("Only PRECOND_SOLVER DEFAULT for the moment")
     372              :          END IF
     373            4 :          IF (PRESENT(matrix_s)) THEN
     374              :             CALL make_full_all(preconditioner_env, mo_coeff, matrix_h, matrix_s, &
     375            4 :                                eigenvalues_ot, energy_gap, common_reference=energy_homo)
     376              :          ELSE
     377              :             CALL make_full_all_ortho(preconditioner_env, mo_coeff, matrix_h, &
     378            0 :                                      eigenvalues_ot, energy_gap, common_reference=energy_homo)
     379              :          END IF
     380              : 
     381              :       CASE DEFAULT
     382         9704 :          CPABORT("Type not implemented")
     383              :       END SELECT
     384              : 
     385         9704 :    END SUBROUTINE make_preconditioner_matrix
     386              : 
     387              : ! **************************************************************************************************
     388              : !> \brief Simply takes the overlap matrix as preconditioner
     389              : !> \param preconditioner_env ...
     390              : !> \param matrix_s ...
     391              : ! **************************************************************************************************
     392           78 :    SUBROUTINE make_full_s_inverse(preconditioner_env, matrix_s)
     393              :       TYPE(preconditioner_type)                          :: preconditioner_env
     394              :       TYPE(dbcsr_type), POINTER                          :: matrix_s
     395              : 
     396              :       CHARACTER(len=*), PARAMETER :: routineN = 'make_full_s_inverse'
     397              : 
     398              :       INTEGER                                            :: handle
     399              : 
     400           78 :       CALL timeset(routineN, handle)
     401              : 
     402           78 :       CPASSERT(ASSOCIATED(matrix_s))
     403              : 
     404           78 :       IF (.NOT. ASSOCIATED(preconditioner_env%sparse_matrix)) THEN
     405           78 :          ALLOCATE (preconditioner_env%sparse_matrix)
     406              :       END IF
     407           78 :       CALL dbcsr_copy(preconditioner_env%sparse_matrix, matrix_s, name="full_kinetic")
     408              : 
     409           78 :       CALL timestop(handle)
     410              : 
     411           78 :    END SUBROUTINE make_full_s_inverse
     412              : 
     413              : ! **************************************************************************************************
     414              : !> \brief kinetic matrix+shift*overlap as preconditioner. Cheap but could
     415              : !>        be better
     416              : !> \param preconditioner_env ...
     417              : !> \param matrix_t ...
     418              : !> \param matrix_s ...
     419              : !> \param energy_gap ...
     420              : ! **************************************************************************************************
     421         1321 :    SUBROUTINE make_full_kinetic(preconditioner_env, matrix_t, matrix_s, &
     422              :                                 energy_gap)
     423              :       TYPE(preconditioner_type)                          :: preconditioner_env
     424              :       TYPE(dbcsr_type), POINTER                          :: matrix_t, matrix_s
     425              :       REAL(KIND=dp)                                      :: energy_gap
     426              : 
     427              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'make_full_kinetic'
     428              : 
     429              :       INTEGER                                            :: handle
     430              :       REAL(KIND=dp)                                      :: shift
     431              : 
     432         1321 :       CALL timeset(routineN, handle)
     433              : 
     434         1321 :       CPASSERT(ASSOCIATED(matrix_t))
     435         1321 :       CPASSERT(ASSOCIATED(matrix_s))
     436              : 
     437         1321 :       IF (.NOT. ASSOCIATED(preconditioner_env%sparse_matrix)) THEN
     438         1319 :          ALLOCATE (preconditioner_env%sparse_matrix)
     439              :       END IF
     440         1321 :       CALL dbcsr_copy(preconditioner_env%sparse_matrix, matrix_t, name="full_kinetic")
     441              : 
     442         1321 :       shift = MAX(0.0_dp, energy_gap)
     443              : 
     444              :       CALL dbcsr_add(preconditioner_env%sparse_matrix, matrix_s, &
     445         1321 :                      alpha_scalar=1.0_dp, beta_scalar=shift)
     446              : 
     447         1321 :       CALL timestop(handle)
     448              : 
     449         1321 :    END SUBROUTINE make_full_kinetic
     450              : 
     451              : ! **************************************************************************************************
     452              : !> \brief full_single_preconditioner
     453              : !> \param preconditioner_env ...
     454              : !> \param fm ...
     455              : !> \param matrix_h ...
     456              : !> \param matrix_s ...
     457              : !> \param energy_homo ...
     458              : !> \param energy_gap ...
     459              : ! **************************************************************************************************
     460           32 :    SUBROUTINE make_full_single(preconditioner_env, fm, matrix_h, matrix_s, &
     461              :                                energy_homo, energy_gap)
     462              :       TYPE(preconditioner_type)                          :: preconditioner_env
     463              :       TYPE(cp_fm_type), POINTER                          :: fm
     464              :       TYPE(dbcsr_type), POINTER                          :: matrix_h, matrix_s
     465              :       REAL(KIND=dp)                                      :: energy_homo, energy_gap
     466              : 
     467              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'make_full_single'
     468              : 
     469              :       INTEGER                                            :: handle, i, n
     470           32 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: evals
     471              :       TYPE(cp_fm_struct_type), POINTER                   :: fm_struct_tmp
     472              :       TYPE(cp_fm_type)                                   :: fm_h, fm_s
     473              : 
     474           32 :       CALL timeset(routineN, handle)
     475              : 
     476           32 :       NULLIFY (fm_struct_tmp, evals)
     477              : 
     478           32 :       IF (ASSOCIATED(fm)) THEN
     479            0 :          CALL cp_fm_release(fm)
     480            0 :          DEALLOCATE (fm)
     481              :          NULLIFY (fm)
     482              :       END IF
     483           32 :       CALL dbcsr_get_info(matrix_h, nfullrows_total=n)
     484           96 :       ALLOCATE (evals(n))
     485              : 
     486              :       CALL cp_fm_struct_create(fm_struct_tmp, nrow_global=n, ncol_global=n, &
     487              :                                context=preconditioner_env%ctxt, &
     488           32 :                                para_env=preconditioner_env%para_env)
     489           32 :       ALLOCATE (fm)
     490           32 :       CALL cp_fm_create(fm, fm_struct_tmp, name="preconditioner")
     491           32 :       CALL cp_fm_create(fm_h, fm_struct_tmp, name="fm_h")
     492           32 :       CALL cp_fm_create(fm_s, fm_struct_tmp, name="fm_s")
     493           32 :       CALL cp_fm_struct_release(fm_struct_tmp)
     494              : 
     495           32 :       CALL copy_dbcsr_to_fm(matrix_h, fm_h)
     496           32 :       CALL copy_dbcsr_to_fm(matrix_s, fm_s)
     497           32 :       CALL cp_fm_cholesky_decompose(fm_s)
     498              : 
     499           32 :       SELECT CASE (preconditioner_env%cholesky_use)
     500              :       CASE (cholesky_inverse)
     501              : ! if cho inverse
     502            0 :          CALL cp_fm_triangular_invert(fm_s)
     503            0 :          CALL cp_fm_uplo_to_full(fm_h, fm)
     504              : 
     505              :          CALL cp_fm_triangular_multiply(fm_s, fm_h, side="R", transpose_tr=.FALSE., &
     506            0 :                                         invert_tr=.FALSE., uplo_tr="U", n_rows=n, n_cols=n, alpha=1.0_dp)
     507              :          CALL cp_fm_triangular_multiply(fm_s, fm_h, side="L", transpose_tr=.TRUE., &
     508            0 :                                         invert_tr=.FALSE., uplo_tr="U", n_rows=n, n_cols=n, alpha=1.0_dp)
     509              :       CASE (cholesky_reduce)
     510           32 :          CALL cp_fm_cholesky_reduce(fm_h, fm_s)
     511              :       CASE DEFAULT
     512           32 :          CPABORT("cholesky type not implemented")
     513              :       END SELECT
     514              : 
     515           32 :       CALL choose_eigv_solver(fm_h, fm, evals)
     516              : 
     517           32 :       SELECT CASE (preconditioner_env%cholesky_use)
     518              :       CASE (cholesky_inverse)
     519              :          CALL cp_fm_triangular_multiply(fm_s, fm, side="L", transpose_tr=.FALSE., &
     520            0 :                                         invert_tr=.FALSE., uplo_tr="U", n_rows=n, n_cols=n, alpha=1.0_dp)
     521            0 :          DO i = 1, n
     522            0 :             evals(i) = 1.0_dp/MAX(evals(i) - energy_homo, energy_gap)
     523              :          END DO
     524            0 :          CALL cp_fm_to_fm(fm, fm_h)
     525              :       CASE (cholesky_reduce)
     526           32 :          CALL cp_fm_cholesky_restore(fm, n, fm_s, fm_h, "SOLVE")
     527          568 :          DO i = 1, n
     528          568 :             evals(i) = 1.0_dp/MAX(evals(i) - energy_homo, energy_gap)
     529              :          END DO
     530           64 :          CALL cp_fm_to_fm(fm_h, fm)
     531              :       END SELECT
     532              : 
     533           32 :       CALL cp_fm_column_scale(fm, evals)
     534           32 :       CALL parallel_gemm('N', 'T', n, n, n, 1.0_dp, fm, fm_h, 0.0_dp, fm_s)
     535           32 :       CALL cp_fm_to_fm(fm_s, fm)
     536              : 
     537           32 :       DEALLOCATE (evals)
     538           32 :       CALL cp_fm_release(fm_h)
     539           32 :       CALL cp_fm_release(fm_s)
     540              : 
     541           32 :       CALL timestop(handle)
     542              : 
     543           96 :    END SUBROUTINE make_full_single
     544              : 
     545              : ! **************************************************************************************************
     546              : !> \brief full single in the orthonormal basis
     547              : !> \param preconditioner_env ...
     548              : !> \param fm ...
     549              : !> \param matrix_h ...
     550              : !> \param energy_homo ...
     551              : !> \param energy_gap ...
     552              : ! **************************************************************************************************
     553            6 :    SUBROUTINE make_full_single_ortho(preconditioner_env, fm, matrix_h, &
     554              :                                      energy_homo, energy_gap)
     555              :       TYPE(preconditioner_type)                          :: preconditioner_env
     556              :       TYPE(cp_fm_type), POINTER                          :: fm
     557              :       TYPE(dbcsr_type), POINTER                          :: matrix_h
     558              :       REAL(KIND=dp)                                      :: energy_homo, energy_gap
     559              : 
     560              :       CHARACTER(len=*), PARAMETER :: routineN = 'make_full_single_ortho'
     561              : 
     562              :       INTEGER                                            :: handle, i, n
     563            6 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: evals
     564              :       TYPE(cp_fm_struct_type), POINTER                   :: fm_struct_tmp
     565              :       TYPE(cp_fm_type)                                   :: fm_h, fm_s
     566              : 
     567            6 :       CALL timeset(routineN, handle)
     568            6 :       NULLIFY (fm_struct_tmp, evals)
     569              : 
     570            6 :       IF (ASSOCIATED(fm)) THEN
     571            0 :          CALL cp_fm_release(fm)
     572            0 :          DEALLOCATE (fm)
     573              :          NULLIFY (fm)
     574              :       END IF
     575            6 :       CALL dbcsr_get_info(matrix_h, nfullrows_total=n)
     576           18 :       ALLOCATE (evals(n))
     577              : 
     578              :       CALL cp_fm_struct_create(fm_struct_tmp, nrow_global=n, ncol_global=n, &
     579              :                                context=preconditioner_env%ctxt, &
     580            6 :                                para_env=preconditioner_env%para_env)
     581            6 :       ALLOCATE (fm)
     582            6 :       CALL cp_fm_create(fm, fm_struct_tmp, name="preconditioner")
     583            6 :       CALL cp_fm_create(fm_h, fm_struct_tmp, name="fm_h")
     584            6 :       CALL cp_fm_create(fm_s, fm_struct_tmp, name="fm_s")
     585            6 :       CALL cp_fm_struct_release(fm_struct_tmp)
     586              : 
     587            6 :       CALL copy_dbcsr_to_fm(matrix_h, fm_h)
     588              : 
     589            6 :       CALL choose_eigv_solver(fm_h, fm, evals)
     590          282 :       DO i = 1, n
     591          282 :          evals(i) = 1.0_dp/MAX(evals(i) - energy_homo, energy_gap)
     592              :       END DO
     593            6 :       CALL cp_fm_to_fm(fm, fm_h)
     594            6 :       CALL cp_fm_column_scale(fm, evals)
     595            6 :       CALL parallel_gemm('N', 'T', n, n, n, 1.0_dp, fm, fm_h, 0.0_dp, fm_s)
     596            6 :       CALL cp_fm_to_fm(fm_s, fm)
     597              : 
     598            6 :       DEALLOCATE (evals)
     599            6 :       CALL cp_fm_release(fm_h)
     600            6 :       CALL cp_fm_release(fm_s)
     601              : 
     602            6 :       CALL timestop(handle)
     603              : 
     604           18 :    END SUBROUTINE make_full_single_ortho
     605              : 
     606              : ! **************************************************************************************************
     607              : !> \brief generates a state by state preconditioner based on the full hamiltonian matrix
     608              : !> \param preconditioner_env ...
     609              : !> \param matrix_c0 ...
     610              : !> \param matrix_h ...
     611              : !> \param matrix_s ...
     612              : !> \param c0_evals ...
     613              : !> \param energy_gap should be a slight underestimate of the physical energy gap for almost all systems
     614              : !>      the c0 are already ritz states of (h,s)
     615              : !> \param common_reference optional scalar occupied-space level used by FERMI_LOW_RANK
     616              : !> \par History
     617              : !>      10.2006 made more stable [Joost VandeVondele]
     618              : !> \note
     619              : !>      includes error estimate on the hamiltonian matrix to result in a stable preconditioner
     620              : !>      a preconditioner for each eigenstate i is generated by keeping the factorized form
     621              : !>      U diag( something i ) U^T. It is important to only precondition in the subspace orthogonal to c0.
     622              : !>      not only is it the only part that matters, it also simplifies the computation of
     623              : !>      the lagrangian multipliers in the OT minimization  (i.e. if the c0 here is different
     624              : !>      from the c0 used in the OT setup, there will be a bug).
     625              : ! **************************************************************************************************
     626         3686 :    SUBROUTINE make_full_all(preconditioner_env, matrix_c0, matrix_h, matrix_s, c0_evals, energy_gap, &
     627              :                             common_reference)
     628              :       TYPE(preconditioner_type)                          :: preconditioner_env
     629              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix_c0
     630              :       TYPE(dbcsr_type), POINTER                          :: matrix_h, matrix_s
     631              :       REAL(KIND=dp), DIMENSION(:)                        :: c0_evals
     632              :       REAL(KIND=dp)                                      :: energy_gap
     633              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: common_reference
     634              : 
     635              :       CHARACTER(len=*), PARAMETER                        :: routineN = 'make_full_all'
     636              :       REAL(KIND=dp), PARAMETER                           :: fudge_factor = 0.25_dp, &
     637              :                                                             lambda_base = 10.0_dp
     638              : 
     639              :       INTEGER                                            :: handle, k, n
     640              :       REAL(KIND=dp)                                      :: error_estimate, lambda
     641         3686 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: diag, norms, shifted_evals
     642              :       TYPE(cp_fm_struct_type), POINTER                   :: fm_struct_tmp
     643              :       TYPE(cp_fm_type)                                   :: matrix_hc0, matrix_left, matrix_s1, &
     644              :                                                             matrix_s2, matrix_sc0, matrix_shc0, &
     645              :                                                             matrix_tmp, ortho
     646              :       TYPE(cp_fm_type), POINTER                          :: matrix_pre
     647              : 
     648         3686 :       CALL timeset(routineN, handle)
     649              : 
     650         3686 :       IF (ASSOCIATED(preconditioner_env%fm)) THEN
     651            0 :          CALL cp_fm_release(preconditioner_env%fm)
     652            0 :          DEALLOCATE (preconditioner_env%fm)
     653              :          NULLIFY (preconditioner_env%fm)
     654              :       END IF
     655         3686 :       CALL cp_fm_get_info(matrix_c0, nrow_global=n, ncol_global=k)
     656              :       CALL cp_fm_struct_create(fm_struct_tmp, nrow_global=n, ncol_global=n, &
     657              :                                context=preconditioner_env%ctxt, &
     658         3686 :                                para_env=preconditioner_env%para_env)
     659         3686 :       ALLOCATE (preconditioner_env%fm)
     660         3686 :       CALL cp_fm_create(preconditioner_env%fm, fm_struct_tmp, name="preconditioner_env%fm")
     661         3686 :       CALL cp_fm_create(ortho, fm_struct_tmp, name="ortho")
     662         3686 :       CALL cp_fm_create(matrix_tmp, fm_struct_tmp, name="matrix_tmp")
     663         3686 :       CALL cp_fm_struct_release(fm_struct_tmp)
     664        11058 :       ALLOCATE (preconditioner_env%full_evals(n))
     665        10948 :       ALLOCATE (preconditioner_env%occ_evals(k))
     666              : 
     667              :       ! 0) cholesky decompose the overlap matrix, if this fails the basis is singular,
     668              :       !    more than EPS_DEFAULT
     669         3686 :       CALL copy_dbcsr_to_fm(matrix_s, ortho)
     670         3686 :       CALL cp_fm_cholesky_decompose(ortho)
     671              : ! if cho inverse
     672         3686 :       IF (preconditioner_env%cholesky_use == cholesky_inverse) THEN
     673            0 :          CALL cp_fm_triangular_invert(ortho)
     674              :       END IF
     675              :       ! 1) Construct a new H matrix, which has the current C0 as eigenvectors,
     676              :       !    possibly shifted by an amount lambda,
     677              :       !    and the same spectrum as the original H matrix in the space orthogonal to the C0
     678              :       !    with P=C0 C0 ^ T
     679              :       !    (1 - PS)^T H (1-PS) + (PS)^T (H - lambda S ) (PS)
     680              :       !    we exploit that the C0 are already the ritz states of H
     681         3686 :       CALL cp_fm_create(matrix_sc0, matrix_c0%matrix_struct, name="sc0")
     682         3686 :       CALL cp_dbcsr_sm_fm_multiply(matrix_s, matrix_c0, matrix_sc0, k)
     683         3686 :       CALL cp_fm_create(matrix_hc0, matrix_c0%matrix_struct, name="hc0")
     684         3686 :       CALL cp_dbcsr_sm_fm_multiply(matrix_h, matrix_c0, matrix_hc0, k)
     685              : 
     686              :       ! An aside, try to estimate the error on the ritz values, we'll need it later on
     687         3686 :       CALL cp_fm_create(matrix_shc0, matrix_c0%matrix_struct, name="shc0")
     688              : 
     689         3686 :       SELECT CASE (preconditioner_env%cholesky_use)
     690              :       CASE (cholesky_inverse)
     691              : ! if cho inverse
     692            0 :          CALL cp_fm_to_fm(matrix_hc0, matrix_shc0)
     693              :          CALL cp_fm_triangular_multiply(ortho, matrix_shc0, side="L", transpose_tr=.TRUE., &
     694            0 :                                         invert_tr=.FALSE., uplo_tr="U", n_rows=n, n_cols=k, alpha=1.0_dp)
     695              :       CASE (cholesky_reduce)
     696         3686 :          CALL cp_fm_cholesky_restore(matrix_hc0, k, ortho, matrix_shc0, "SOLVE", transa="T")
     697              :       CASE DEFAULT
     698         3686 :          CPABORT("cholesky type not implemented")
     699              :       END SELECT
     700              :       CALL cp_fm_struct_create(fm_struct_tmp, nrow_global=k, ncol_global=k, &
     701              :                                context=preconditioner_env%ctxt, &
     702         3686 :                                para_env=preconditioner_env%para_env)
     703         3686 :       CALL cp_fm_create(matrix_s1, fm_struct_tmp, name="matrix_s1")
     704         3686 :       CALL cp_fm_struct_release(fm_struct_tmp)
     705              :       ! since we only use diagonal elements this is a bit of a waste
     706         3686 :       CALL parallel_gemm('T', 'N', k, k, n, 1.0_dp, matrix_shc0, matrix_shc0, 0.0_dp, matrix_s1)
     707         7262 :       ALLOCATE (diag(k))
     708         3686 :       CALL cp_fm_get_diag(matrix_s1, diag)
     709         3686 :       IF (PRESENT(common_reference)) THEN
     710              :          ! ||(1-CC^T S)S^-1 H C||_F is invariant under C -> C U.
     711           68 :          error_estimate = SQRT(MAX(0.0_dp, SUM(diag) - SUM(c0_evals**2)))
     712              :       ELSE
     713        21142 :          error_estimate = MAXVAL(SQRT(ABS(diag - c0_evals**2)))
     714              :       END IF
     715         3686 :       DEALLOCATE (diag)
     716         3686 :       CALL cp_fm_release(matrix_s1)
     717         3686 :       CALL cp_fm_release(matrix_shc0)
     718              :       ! we'll only use the energy gap, if our estimate of the error on the eigenvalues
     719              :       ! is small enough. A large error combined with a small energy gap would otherwise lead to
     720              :       ! an aggressive but bad preconditioner. Only when the error is small (MD), we can precondition
     721              :       ! aggressively
     722         3686 :       preconditioner_env%energy_gap = MAX(energy_gap, error_estimate*fudge_factor)
     723         3686 :       CALL copy_dbcsr_to_fm(matrix_h, matrix_tmp)
     724         3686 :       matrix_pre => preconditioner_env%fm
     725         3686 :       CALL cp_fm_uplo_to_full(matrix_tmp, matrix_pre)
     726              :       ! tmp = H ( 1 - PS )
     727         3686 :       CALL parallel_gemm('N', 'T', n, n, k, -1.0_dp, matrix_hc0, matrix_sc0, 1.0_dp, matrix_tmp)
     728              : 
     729              :       CALL cp_fm_struct_create(fm_struct_tmp, nrow_global=k, ncol_global=n, &
     730              :                                context=preconditioner_env%ctxt, &
     731         3686 :                                para_env=preconditioner_env%para_env)
     732         3686 :       CALL cp_fm_create(matrix_left, fm_struct_tmp, name="matrix_left")
     733         3686 :       CALL cp_fm_struct_release(fm_struct_tmp)
     734         3686 :       CALL parallel_gemm('T', 'N', k, n, n, 1.0_dp, matrix_c0, matrix_tmp, 0.0_dp, matrix_left)
     735              :       ! tmp = (1 - PS)^T H (1-PS)
     736         3686 :       CALL parallel_gemm('N', 'N', n, n, k, -1.0_dp, matrix_sc0, matrix_left, 1.0_dp, matrix_tmp)
     737         3686 :       CALL cp_fm_release(matrix_left)
     738              : 
     739         7262 :       ALLOCATE (shifted_evals(k))
     740         3686 :       lambda = lambda_base + error_estimate
     741         3686 :       IF (PRESENT(common_reference)) THEN
     742           36 :          shifted_evals = common_reference - lambda
     743              :       ELSE
     744        21032 :          shifted_evals = c0_evals - lambda
     745              :       END IF
     746         3686 :       CALL cp_fm_to_fm(matrix_sc0, matrix_hc0)
     747         3686 :       CALL cp_fm_column_scale(matrix_hc0, shifted_evals)
     748         3686 :       CALL parallel_gemm('N', 'T', n, n, k, 1.0_dp, matrix_hc0, matrix_sc0, 1.0_dp, matrix_tmp)
     749              : 
     750              :       ! 2) diagonalize this operator
     751         3686 :       SELECT CASE (preconditioner_env%cholesky_use)
     752              :       CASE (cholesky_inverse)
     753              :          CALL cp_fm_triangular_multiply(ortho, matrix_tmp, side="R", transpose_tr=.FALSE., &
     754            0 :                                         invert_tr=.FALSE., uplo_tr="U", n_rows=n, n_cols=n, alpha=1.0_dp)
     755              :          CALL cp_fm_triangular_multiply(ortho, matrix_tmp, side="L", transpose_tr=.TRUE., &
     756            0 :                                         invert_tr=.FALSE., uplo_tr="U", n_rows=n, n_cols=n, alpha=1.0_dp)
     757              :       CASE (cholesky_reduce)
     758         3686 :          CALL cp_fm_cholesky_reduce(matrix_tmp, ortho)
     759              :       END SELECT
     760         3686 :       CALL choose_eigv_solver(matrix_tmp, matrix_pre, preconditioner_env%full_evals)
     761         3686 :       SELECT CASE (preconditioner_env%cholesky_use)
     762              :       CASE (cholesky_inverse)
     763              :          CALL cp_fm_triangular_multiply(ortho, matrix_pre, side="L", transpose_tr=.FALSE., &
     764            0 :                                         invert_tr=.FALSE., uplo_tr="U", n_rows=n, n_cols=n, alpha=1.0_dp)
     765            0 :          CALL cp_fm_to_fm(matrix_pre, matrix_tmp)
     766              :       CASE (cholesky_reduce)
     767         3686 :          CALL cp_fm_cholesky_restore(matrix_pre, n, ortho, matrix_tmp, "SOLVE")
     768         7372 :          CALL cp_fm_to_fm(matrix_tmp, matrix_pre)
     769              :       END SELECT
     770              : 
     771              :       ! test that the subspace remained conserved
     772              :       IF (.FALSE.) THEN
     773              :          CALL cp_fm_struct_create(fm_struct_tmp, nrow_global=k, ncol_global=k, &
     774              :                                   context=preconditioner_env%ctxt, &
     775              :                                   para_env=preconditioner_env%para_env)
     776              :          CALL cp_fm_create(matrix_s1, fm_struct_tmp, name="matrix_s1")
     777              :          CALL cp_fm_create(matrix_s2, fm_struct_tmp, name="matrix_s2")
     778              :          CALL cp_fm_struct_release(fm_struct_tmp)
     779              :          ALLOCATE (norms(k))
     780              :          CALL parallel_gemm('T', 'N', k, k, n, 1.0_dp, matrix_sc0, matrix_tmp, 0.0_dp, matrix_s1)
     781              :          CALL choose_eigv_solver(matrix_s1, matrix_s2, norms)
     782              :          WRITE (*, *) "matrix norm deviation (should be close to zero): ", MAXVAL(ABS(ABS(norms) - 1.0_dp))
     783              :          DEALLOCATE (norms)
     784              :          CALL cp_fm_release(matrix_s1)
     785              :          CALL cp_fm_release(matrix_s2)
     786              :       END IF
     787              : 
     788              :       ! 3) replace the lowest k evals and evecs with what they should be
     789         3686 :       IF (PRESENT(common_reference)) THEN
     790           36 :          preconditioner_env%occ_evals = common_reference
     791           36 :          preconditioner_env%full_evals(1:k) = common_reference
     792              :       ELSE
     793        21032 :          preconditioner_env%occ_evals = c0_evals
     794              :          ! This choice makes FULL_ALL constant when applied to sc0 (see apply_full_all).
     795        21032 :          preconditioner_env%full_evals(1:k) = c0_evals
     796              :       END IF
     797         3686 :       CALL cp_fm_to_fm(matrix_c0, matrix_pre, k, 1, 1)
     798              : 
     799         3686 :       CALL cp_fm_release(matrix_sc0)
     800         3686 :       CALL cp_fm_release(matrix_hc0)
     801         3686 :       CALL cp_fm_release(ortho)
     802         3686 :       CALL cp_fm_release(matrix_tmp)
     803         3686 :       DEALLOCATE (shifted_evals)
     804         3686 :       CALL timestop(handle)
     805              : 
     806        29488 :    END SUBROUTINE make_full_all
     807              : 
     808              : ! **************************************************************************************************
     809              : !> \brief full all in the orthonormal basis
     810              : !> \param preconditioner_env ...
     811              : !> \param matrix_c0 ...
     812              : !> \param matrix_h ...
     813              : !> \param c0_evals ...
     814              : !> \param energy_gap ...
     815              : !> \param common_reference optional scalar occupied-space level used by FERMI_LOW_RANK
     816              : ! **************************************************************************************************
     817           80 :    SUBROUTINE make_full_all_ortho(preconditioner_env, matrix_c0, matrix_h, c0_evals, energy_gap, &
     818              :                                   common_reference)
     819              : 
     820              :       TYPE(preconditioner_type)                          :: preconditioner_env
     821              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix_c0
     822              :       TYPE(dbcsr_type), POINTER                          :: matrix_h
     823              :       REAL(KIND=dp), DIMENSION(:)                        :: c0_evals
     824              :       REAL(KIND=dp)                                      :: energy_gap
     825              :       REAL(KIND=dp), INTENT(IN), OPTIONAL                :: common_reference
     826              : 
     827              :       CHARACTER(len=*), PARAMETER :: routineN = 'make_full_all_ortho'
     828              :       REAL(KIND=dp), PARAMETER                           :: fudge_factor = 0.25_dp, &
     829              :                                                             lambda_base = 10.0_dp
     830              : 
     831              :       INTEGER                                            :: handle, k, n
     832              :       REAL(KIND=dp)                                      :: error_estimate, lambda
     833           80 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: diag, norms, shifted_evals
     834              :       TYPE(cp_fm_struct_type), POINTER                   :: fm_struct_tmp
     835              :       TYPE(cp_fm_type)                                   :: matrix_hc0, matrix_left, matrix_s1, &
     836              :                                                             matrix_s2, matrix_sc0, matrix_tmp
     837              :       TYPE(cp_fm_type), POINTER                          :: matrix_pre
     838              : 
     839           80 :       CALL timeset(routineN, handle)
     840              : 
     841           80 :       IF (ASSOCIATED(preconditioner_env%fm)) THEN
     842            0 :          CALL cp_fm_release(preconditioner_env%fm)
     843            0 :          DEALLOCATE (preconditioner_env%fm)
     844              :          NULLIFY (preconditioner_env%fm)
     845              :       END IF
     846           80 :       CALL cp_fm_get_info(matrix_c0, nrow_global=n, ncol_global=k)
     847              :       CALL cp_fm_struct_create(fm_struct_tmp, nrow_global=n, ncol_global=n, &
     848              :                                context=preconditioner_env%ctxt, &
     849           80 :                                para_env=preconditioner_env%para_env)
     850           80 :       ALLOCATE (preconditioner_env%fm)
     851           80 :       CALL cp_fm_create(preconditioner_env%fm, fm_struct_tmp, name="preconditioner_env%fm")
     852           80 :       CALL cp_fm_create(matrix_tmp, fm_struct_tmp, name="matrix_tmp")
     853           80 :       CALL cp_fm_struct_release(fm_struct_tmp)
     854          240 :       ALLOCATE (preconditioner_env%full_evals(n))
     855          240 :       ALLOCATE (preconditioner_env%occ_evals(k))
     856              : 
     857              :       ! 1) Construct a new H matrix, which has the current C0 as eigenvectors,
     858              :       !    possibly shifted by an amount lambda,
     859              :       !    and the same spectrum as the original H matrix in the space orthogonal to the C0
     860              :       !    with P=C0 C0 ^ T
     861              :       !    (1 - PS)^T H (1-PS) + (PS)^T (H - lambda S ) (PS)
     862              :       !    we exploit that the C0 are already the ritz states of H
     863           80 :       CALL cp_fm_create(matrix_sc0, matrix_c0%matrix_struct, name="sc0")
     864           80 :       CALL cp_fm_to_fm(matrix_c0, matrix_sc0)
     865           80 :       CALL cp_fm_create(matrix_hc0, matrix_c0%matrix_struct, name="hc0")
     866           80 :       CALL cp_dbcsr_sm_fm_multiply(matrix_h, matrix_c0, matrix_hc0, k)
     867              : 
     868              :       ! An aside, try to estimate the error on the ritz values, we'll need it later on
     869              :       CALL cp_fm_struct_create(fm_struct_tmp, nrow_global=k, ncol_global=k, &
     870              :                                context=preconditioner_env%ctxt, &
     871           80 :                                para_env=preconditioner_env%para_env)
     872           80 :       CALL cp_fm_create(matrix_s1, fm_struct_tmp, name="matrix_s1")
     873           80 :       CALL cp_fm_struct_release(fm_struct_tmp)
     874              :       ! since we only use diagonal elements this is a bit of a waste
     875           80 :       CALL parallel_gemm('T', 'N', k, k, n, 1.0_dp, matrix_hc0, matrix_hc0, 0.0_dp, matrix_s1)
     876          160 :       ALLOCATE (diag(k))
     877           80 :       CALL cp_fm_get_diag(matrix_s1, diag)
     878           80 :       IF (PRESENT(common_reference)) THEN
     879              :          ! ||(1-CC^T)H C||_F is invariant under C -> C U.
     880            0 :          error_estimate = SQRT(MAX(0.0_dp, SUM(diag) - SUM(c0_evals**2)))
     881              :       ELSE
     882          826 :          error_estimate = MAXVAL(SQRT(ABS(diag - c0_evals**2)))
     883              :       END IF
     884           80 :       DEALLOCATE (diag)
     885           80 :       CALL cp_fm_release(matrix_s1)
     886              :       ! we'll only use the energy gap, if our estimate of the error on the eigenvalues
     887              :       ! is small enough. A large error combined with a small energy gap would otherwise lead to
     888              :       ! an aggressive but bad preconditioner. Only when the error is small (MD), we can precondition
     889              :       ! aggressively
     890           80 :       preconditioner_env%energy_gap = MAX(energy_gap, error_estimate*fudge_factor)
     891              : 
     892           80 :       matrix_pre => preconditioner_env%fm
     893           80 :       CALL copy_dbcsr_to_fm(matrix_h, matrix_tmp)
     894           80 :       CALL cp_fm_uplo_to_full(matrix_tmp, matrix_pre)
     895              :       ! tmp = H ( 1 - PS )
     896           80 :       CALL parallel_gemm('N', 'T', n, n, k, -1.0_dp, matrix_hc0, matrix_sc0, 1.0_dp, matrix_tmp)
     897              : 
     898              :       CALL cp_fm_struct_create(fm_struct_tmp, nrow_global=k, ncol_global=n, &
     899              :                                context=preconditioner_env%ctxt, &
     900           80 :                                para_env=preconditioner_env%para_env)
     901           80 :       CALL cp_fm_create(matrix_left, fm_struct_tmp, name="matrix_left")
     902           80 :       CALL cp_fm_struct_release(fm_struct_tmp)
     903           80 :       CALL parallel_gemm('T', 'N', k, n, n, 1.0_dp, matrix_c0, matrix_tmp, 0.0_dp, matrix_left)
     904              :       ! tmp = (1 - PS)^T H (1-PS)
     905           80 :       CALL parallel_gemm('N', 'N', n, n, k, -1.0_dp, matrix_sc0, matrix_left, 1.0_dp, matrix_tmp)
     906           80 :       CALL cp_fm_release(matrix_left)
     907              : 
     908          160 :       ALLOCATE (shifted_evals(k))
     909           80 :       lambda = lambda_base + error_estimate
     910           80 :       IF (PRESENT(common_reference)) THEN
     911            0 :          shifted_evals = common_reference - lambda
     912              :       ELSE
     913          826 :          shifted_evals = c0_evals - lambda
     914              :       END IF
     915           80 :       CALL cp_fm_to_fm(matrix_sc0, matrix_hc0)
     916           80 :       CALL cp_fm_column_scale(matrix_hc0, shifted_evals)
     917           80 :       CALL parallel_gemm('N', 'T', n, n, k, 1.0_dp, matrix_hc0, matrix_sc0, 1.0_dp, matrix_tmp)
     918              : 
     919              :       ! 2) diagonalize this operator
     920           80 :       CALL choose_eigv_solver(matrix_tmp, matrix_pre, preconditioner_env%full_evals)
     921              : 
     922              :       ! test that the subspace remained conserved
     923              :       IF (.FALSE.) THEN
     924              :          CALL cp_fm_to_fm(matrix_pre, matrix_tmp)
     925              :          CALL cp_fm_struct_create(fm_struct_tmp, nrow_global=k, ncol_global=k, &
     926              :                                   context=preconditioner_env%ctxt, &
     927              :                                   para_env=preconditioner_env%para_env)
     928              :          CALL cp_fm_create(matrix_s1, fm_struct_tmp, name="matrix_s1")
     929              :          CALL cp_fm_create(matrix_s2, fm_struct_tmp, name="matrix_s2")
     930              :          CALL cp_fm_struct_release(fm_struct_tmp)
     931              :          ALLOCATE (norms(k))
     932              :          CALL parallel_gemm('T', 'N', k, k, n, 1.0_dp, matrix_sc0, matrix_tmp, 0.0_dp, matrix_s1)
     933              :          CALL choose_eigv_solver(matrix_s1, matrix_s2, norms)
     934              : 
     935              :          WRITE (*, *) "matrix norm deviation (should be close to zero): ", MAXVAL(ABS(ABS(norms) - 1.0_dp))
     936              :          DEALLOCATE (norms)
     937              :          CALL cp_fm_release(matrix_s1)
     938              :          CALL cp_fm_release(matrix_s2)
     939              :       END IF
     940              : 
     941              :       ! 3) replace the lowest k evals and evecs with what they should be
     942           80 :       IF (PRESENT(common_reference)) THEN
     943            0 :          preconditioner_env%occ_evals = common_reference
     944            0 :          preconditioner_env%full_evals(1:k) = common_reference
     945              :       ELSE
     946          826 :          preconditioner_env%occ_evals = c0_evals
     947              :          ! This choice makes FULL_ALL constant when applied to sc0 (see apply_full_all).
     948          826 :          preconditioner_env%full_evals(1:k) = c0_evals
     949              :       END IF
     950           80 :       CALL cp_fm_to_fm(matrix_c0, matrix_pre, k, 1, 1)
     951              : 
     952           80 :       CALL cp_fm_release(matrix_sc0)
     953           80 :       CALL cp_fm_release(matrix_hc0)
     954           80 :       CALL cp_fm_release(matrix_tmp)
     955           80 :       DEALLOCATE (shifted_evals)
     956              : 
     957           80 :       CALL timestop(handle)
     958              : 
     959          560 :    END SUBROUTINE make_full_all_ortho
     960              : 
     961              : ! **************************************************************************************************
     962              : !> \brief generates a preconditioner matrix H-lambda S+(SC)(2.0*CT*H*C+delta)(SC)^T
     963              : !>        for later inversion.
     964              : !>        H is the Kohn Sham matrix
     965              : !>        lambda*S shifts the spectrum of the generalized form up by lambda
     966              : !>        the last term only shifts the occupied space (reversing them in energy order)
     967              : !>        This form is implicitly multiplied from both sides by S^0.5
     968              : !>        This ensures we precondition the correct quantity
     969              : !>        Before this reads S^-0.5 H S^-0.5 + lambda + (S^0.5 C)shifts(S^0.5 C)T
     970              : !>        which might be a bit more obvious
     971              : !>        Replaced the old full_single_inverse at revision 14616
     972              : !> \param preconditioner_env the preconditioner env
     973              : !> \param matrix_c0 the MO coefficient matrix (fm)
     974              : !> \param matrix_h Kohn-Sham matrix (dbcsr)
     975              : !> \param energy_gap an additional shift in lambda=-E_homo+energy_gap
     976              : !> \param matrix_s the overlap matrix if not orthonormal (dbcsr, optional)
     977              : ! **************************************************************************************************
     978         4501 :    SUBROUTINE make_full_single_inverse(preconditioner_env, matrix_c0, matrix_h, energy_gap, matrix_s)
     979              :       TYPE(preconditioner_type)                          :: preconditioner_env
     980              :       TYPE(cp_fm_type), INTENT(IN)                       :: matrix_c0
     981              :       TYPE(dbcsr_type), POINTER                          :: matrix_h
     982              :       REAL(KIND=dp)                                      :: energy_gap
     983              :       TYPE(dbcsr_type), OPTIONAL, POINTER                :: matrix_s
     984              : 
     985              :       CHARACTER(len=*), PARAMETER :: routineN = 'make_full_single_inverse'
     986              : 
     987              :       INTEGER                                            :: handle, k, n
     988              :       REAL(KIND=dp)                                      :: max_ev, min_ev, pre_shift
     989              :       TYPE(arnoldi_env_type)                             :: arnoldi_env
     990         4501 :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrices
     991              :       TYPE(dbcsr_type), TARGET                           :: dbcsr_cThc, dbcsr_hc, dbcsr_sc, mo_dbcsr
     992              : 
     993         4501 :       CALL timeset(routineN, handle)
     994              : 
     995              :       ! Allocate all working matrices needed
     996         4501 :       CALL cp_fm_get_info(matrix_c0, nrow_global=n, ncol_global=k)
     997              :       ! copy the fm MO's to a sparse matrix, can be solved better if the sparse version is already present
     998              :       ! but for the time beeing this will do
     999         4501 :       CALL cp_fm_to_dbcsr_row_template(mo_dbcsr, matrix_c0, matrix_h)
    1000         4501 :       CALL dbcsr_create(dbcsr_sc, template=mo_dbcsr)
    1001         4501 :       CALL dbcsr_create(dbcsr_hc, template=mo_dbcsr)
    1002         4501 :       CALL cp_dbcsr_m_by_n_from_template(dbcsr_cThc, matrix_h, k, k, sym=dbcsr_type_symmetric)
    1003              : 
    1004              :       ! Check whether the output matrix was already created, if not do it now
    1005         4501 :       IF (.NOT. ASSOCIATED(preconditioner_env%sparse_matrix)) THEN
    1006         4501 :          ALLOCATE (preconditioner_env%sparse_matrix)
    1007              :       END IF
    1008              : 
    1009              :       ! Put the first term of the preconditioner (H) into the output matrix
    1010         4501 :       CALL dbcsr_copy(preconditioner_env%sparse_matrix, matrix_h)
    1011              : 
    1012              :       ! Precompute some matrices
    1013              :       ! S*C, if orthonormal this will be simply C so a copy will do
    1014         4501 :       IF (PRESENT(matrix_s)) THEN
    1015         4113 :          CALL dbcsr_multiply("N", "N", 1.0_dp, matrix_s, mo_dbcsr, 0.0_dp, dbcsr_sc)
    1016              :       ELSE
    1017          388 :          CALL dbcsr_copy(dbcsr_sc, mo_dbcsr)
    1018              :       END IF
    1019              : 
    1020              : !----------------------------compute the occupied subspace and shift it ------------------------------------
    1021              :       ! cT*H*C which will be used to shift the occupied states to 0
    1022         4501 :       CALL dbcsr_multiply("N", "N", 1.0_dp, matrix_h, mo_dbcsr, 0.0_dp, dbcsr_hc)
    1023         4501 :       CALL dbcsr_multiply("T", "N", 1.0_dp, mo_dbcsr, dbcsr_hc, 0.0_dp, dbcsr_cThc)
    1024              : 
    1025              :       ! Compute the Energy of the HOMO. We will use this as a reference energy
    1026         9002 :       ALLOCATE (matrices(1))
    1027         4501 :       matrices(1)%matrix => dbcsr_cThc
    1028              :       CALL setup_arnoldi_env(arnoldi_env, matrices, max_iter=20, threshold=1.0E-3_dp, selection_crit=2, &
    1029         4501 :                              nval_request=1, nrestarts=8, generalized_ev=.FALSE., iram=.FALSE.)
    1030         4501 :       IF (ASSOCIATED(preconditioner_env%max_ev_vector)) THEN
    1031         2444 :          CALL set_arnoldi_initial_vector(arnoldi_env, preconditioner_env%max_ev_vector)
    1032              :       END IF
    1033         4501 :       CALL arnoldi_ev(matrices, arnoldi_env)
    1034         4501 :       max_ev = REAL(get_selected_ritz_val(arnoldi_env, 1), dp)
    1035              : 
    1036              :       ! save the ev as guess for the next time
    1037         4501 :       IF (.NOT. ASSOCIATED(preconditioner_env%max_ev_vector)) ALLOCATE (preconditioner_env%max_ev_vector)
    1038         4501 :       CALL get_selected_ritz_vector(arnoldi_env, 1, matrices(1)%matrix, preconditioner_env%max_ev_vector)
    1039         4501 :       CALL deallocate_arnoldi_env(arnoldi_env)
    1040         4501 :       DEALLOCATE (matrices)
    1041              : 
    1042              :       ! Lets shift the occupied states a bit further up, -1.0 because we gonna subtract it from H
    1043         4501 :       CALL dbcsr_add_on_diag(dbcsr_cThc, -0.5_dp)
    1044              :       ! Get the AO representation of the shift (see above why S is needed), W-matrix like object
    1045         4501 :       CALL dbcsr_multiply("N", "N", 2.0_dp, dbcsr_sc, dbcsr_cThc, 0.0_dp, dbcsr_hc)
    1046         4501 :       CALL dbcsr_multiply("N", "T", -1.0_dp, dbcsr_hc, dbcsr_sc, 1.0_dp, preconditioner_env%sparse_matrix)
    1047              : 
    1048              : !-------------------------------------compute eigenvalues of H ----------------------------------------------
    1049              :       ! Setup the arnoldi procedure to compute the lowest ev. if S is present this has to be the generalized ev
    1050         4501 :       IF (PRESENT(matrix_s)) THEN
    1051        12339 :          ALLOCATE (matrices(2))
    1052         4113 :          matrices(1)%matrix => preconditioner_env%sparse_matrix
    1053         4113 :          matrices(2)%matrix => matrix_s
    1054              :          CALL setup_arnoldi_env(arnoldi_env, matrices, max_iter=20, threshold=2.0E-2_dp, selection_crit=3, &
    1055         4113 :                                 nval_request=1, nrestarts=21, generalized_ev=.TRUE., iram=.FALSE.)
    1056              :       ELSE
    1057          776 :          ALLOCATE (matrices(1))
    1058          388 :          matrices(1)%matrix => preconditioner_env%sparse_matrix
    1059              :          CALL setup_arnoldi_env(arnoldi_env, matrices, max_iter=20, threshold=2.0E-2_dp, selection_crit=3, &
    1060          388 :                                 nval_request=1, nrestarts=8, generalized_ev=.FALSE., iram=.FALSE.)
    1061              :       END IF
    1062         4501 :       IF (ASSOCIATED(preconditioner_env%min_ev_vector)) THEN
    1063         2444 :          CALL set_arnoldi_initial_vector(arnoldi_env, preconditioner_env%min_ev_vector)
    1064              :       END IF
    1065              : 
    1066              :       ! compute the LUMO energy
    1067         4501 :       CALL arnoldi_ev(matrices, arnoldi_env)
    1068         4501 :       min_eV = REAL(get_selected_ritz_val(arnoldi_env, 1), dp)
    1069              : 
    1070              :       ! save the lumo vector for restarting in the next step
    1071         4501 :       IF (.NOT. ASSOCIATED(preconditioner_env%min_ev_vector)) ALLOCATE (preconditioner_env%min_ev_vector)
    1072         4501 :       CALL get_selected_ritz_vector(arnoldi_env, 1, matrices(1)%matrix, preconditioner_env%min_ev_vector)
    1073         4501 :       CALL deallocate_arnoldi_env(arnoldi_env)
    1074         4501 :       DEALLOCATE (matrices)
    1075              : 
    1076              : !-------------------------------------compute eigenvalues of H ----------------------------------------------
    1077              :       ! Shift the Lumo to the 1.5*the computed energy_gap or the external energy gap value
    1078              :       ! The factor 1.5 is determined by trying. If the LUMO is positive, enough, just leave it alone
    1079         4501 :       pre_shift = MAX(1.5_dp*(min_ev - max_ev), energy_gap)
    1080         4501 :       IF (min_ev < pre_shift) THEN
    1081         4483 :          pre_shift = pre_shift - min_ev
    1082              :       ELSE
    1083           18 :          pre_shift = 0.0_dp
    1084              :       END IF
    1085         4501 :       IF (PRESENT(matrix_s)) THEN
    1086         4113 :          CALL dbcsr_add(preconditioner_env%sparse_matrix, matrix_s, 1.0_dp, pre_shift)
    1087              :       ELSE
    1088          388 :          CALL dbcsr_add_on_diag(preconditioner_env%sparse_matrix, pre_shift)
    1089              :       END IF
    1090              : 
    1091         4501 :       CALL dbcsr_release(mo_dbcsr)
    1092         4501 :       CALL dbcsr_release(dbcsr_hc)
    1093         4501 :       CALL dbcsr_release(dbcsr_sc)
    1094         4501 :       CALL dbcsr_release(dbcsr_cThc)
    1095              : 
    1096         4501 :       CALL timestop(handle)
    1097              : 
    1098         4501 :    END SUBROUTINE make_full_single_inverse
    1099              : 
    1100              : ! **************************************************************************************************
    1101              : !> \brief Build a gauge-covariant FULL_SINGLE_INVERSE operator for a complex k-point channel.
    1102              : !> \param preconditioner_env preconditioner storage
    1103              : !> \param matrix_c0 complex reference orbitals, C^H S C = I
    1104              : !> \param matrix_h complex Hermitian k-point Hamiltonian
    1105              : !> \param matrix_s complex Hermitian k-point overlap
    1106              : !> \param energy_gap lower spectral bound of the positive operator
    1107              : ! **************************************************************************************************
    1108           64 :    SUBROUTINE make_complex_full_single_inverse(preconditioner_env, matrix_c0, matrix_h, matrix_s, &
    1109              :                                                energy_gap)
    1110              : 
    1111              :       TYPE(preconditioner_type)                          :: preconditioner_env
    1112              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix_c0, matrix_h, matrix_s
    1113              :       REAL(KIND=dp), INTENT(IN)                          :: energy_gap
    1114              : 
    1115              :       CHARACTER(len=*), PARAMETER :: routineN = 'make_complex_full_single_inverse'
    1116              : 
    1117              :       INTEGER                                            :: handle, i, k, n
    1118              :       REAL(KIND=dp)                                      :: max_ev, min_ev, pre_shift, target_edge
    1119              :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: occupied_evals, operator_evals
    1120              :       TYPE(cp_cfm_type) :: matrix_chc, matrix_diag_a, matrix_diag_b, matrix_diag_evec, &
    1121              :          matrix_diag_work, matrix_hc0, matrix_occ_diag_a, matrix_occ_diag_b, matrix_occ_diag_evec, &
    1122              :          matrix_occ_diag_work, matrix_operator, matrix_sc0, matrix_sc_chc
    1123              : 
    1124           64 :       CALL timeset(routineN, handle)
    1125              : 
    1126           64 :       CALL cp_cfm_get_info(matrix_c0, nrow_global=n, ncol_global=k)
    1127           64 :       CPASSERT(n > 0)
    1128           64 :       CPASSERT(k > 0 .AND. k <= n)
    1129           64 :       CPASSERT(energy_gap > 0.0_dp)
    1130              : 
    1131           64 :       CALL cp_cfm_create(matrix_hc0, matrix_c0%matrix_struct, name='complex FULL_SINGLE HC')
    1132           64 :       CALL cp_cfm_create(matrix_sc0, matrix_c0%matrix_struct, name='complex FULL_SINGLE SC')
    1133              :       CALL cp_cfm_create(matrix_chc, matrix_c0%matrix_struct, nrow=k, ncol=k, &
    1134           64 :                          name='complex FULL_SINGLE CHC')
    1135              :       CALL cp_cfm_create(matrix_sc_chc, matrix_c0%matrix_struct, &
    1136           64 :                          name='complex FULL_SINGLE SC CHC')
    1137              :       CALL cp_cfm_create(matrix_operator, matrix_h%matrix_struct, &
    1138           64 :                          name='complex FULL_SINGLE operator')
    1139              :       CALL cp_cfm_create(matrix_diag_a, matrix_h%matrix_struct, &
    1140           64 :                          name='complex FULL_SINGLE diagonalization A')
    1141              :       CALL cp_cfm_create(matrix_diag_b, matrix_s%matrix_struct, &
    1142           64 :                          name='complex FULL_SINGLE diagonalization B')
    1143              :       CALL cp_cfm_create(matrix_diag_evec, matrix_h%matrix_struct, nrow=n, ncol=1, &
    1144           64 :                          name='complex FULL_SINGLE eigenvectors')
    1145              :       CALL cp_cfm_create(matrix_diag_work, matrix_h%matrix_struct, &
    1146           64 :                          name='complex FULL_SINGLE diagonalization work')
    1147              :       CALL cp_cfm_create(matrix_occ_diag_a, matrix_c0%matrix_struct, nrow=k, ncol=k, &
    1148           64 :                          name='complex FULL_SINGLE occupied diagonalization A')
    1149              :       CALL cp_cfm_create(matrix_occ_diag_b, matrix_c0%matrix_struct, nrow=k, ncol=k, &
    1150           64 :                          name='complex FULL_SINGLE occupied diagonalization B', set_zero=.TRUE.)
    1151              :       CALL cp_cfm_create(matrix_occ_diag_evec, matrix_c0%matrix_struct, nrow=k, ncol=k, &
    1152           64 :                          name='complex FULL_SINGLE occupied eigenvectors')
    1153              :       CALL cp_cfm_create(matrix_occ_diag_work, matrix_c0%matrix_struct, nrow=k, ncol=k, &
    1154           64 :                          name='complex FULL_SINGLE occupied diagonalization work')
    1155              : 
    1156           64 :       CALL cp_cfm_gemm('N', 'N', n, k, n, z_one, matrix_h, matrix_c0, z_zero, matrix_hc0)
    1157           64 :       CALL cp_cfm_gemm('N', 'N', n, k, n, z_one, matrix_s, matrix_c0, z_zero, matrix_sc0)
    1158           64 :       CALL cp_cfm_gemm('C', 'N', k, k, n, z_one, matrix_c0, matrix_hc0, z_zero, matrix_chc)
    1159              : 
    1160              :       ! A = H - 2*S*C*(C^H*H*C)*C^H*S + S*C*C^H*S is invariant under C -> C*U.
    1161           64 :       CALL cp_cfm_to_cfm(matrix_h, matrix_operator)
    1162              :       CALL cp_cfm_gemm('N', 'N', n, k, k, z_one, matrix_sc0, matrix_chc, &
    1163           64 :                        z_zero, matrix_sc_chc)
    1164              :       CALL cp_cfm_gemm('N', 'C', n, n, k, -2.0_dp*z_one, matrix_sc_chc, matrix_sc0, &
    1165           64 :                        z_one, matrix_operator)
    1166              :       CALL cp_cfm_gemm('N', 'C', n, n, k, z_one, matrix_sc0, matrix_sc0, &
    1167           64 :                        z_one, matrix_operator)
    1168              : 
    1169          192 :       ALLOCATE (occupied_evals(k), operator_evals(1))
    1170           64 :       CALL cp_cfm_to_cfm(matrix_chc, matrix_occ_diag_a)
    1171          484 :       DO i = 1, k
    1172          484 :          CALL cp_cfm_set_element(matrix_occ_diag_b, i, i, z_one)
    1173              :       END DO
    1174              :       CALL cp_cfm_geeig(matrix_occ_diag_a, matrix_occ_diag_b, matrix_occ_diag_evec, &
    1175           64 :                         occupied_evals, matrix_occ_diag_work)
    1176          484 :       max_ev = MAXVAL(occupied_evals)
    1177           64 :       CALL cp_cfm_to_cfm(matrix_operator, matrix_diag_a)
    1178           64 :       CALL cp_cfm_to_cfm(matrix_s, matrix_diag_b)
    1179              :       CALL cp_cfm_geeig(matrix_diag_a, matrix_diag_b, matrix_diag_evec, &
    1180           64 :                         operator_evals, matrix_diag_work, lowest_subset=.TRUE.)
    1181           64 :       min_ev = operator_evals(1)
    1182           64 :       target_edge = MAX(1.5_dp*(min_ev - max_ev), energy_gap)
    1183           64 :       pre_shift = MAX(0.0_dp, target_edge - min_ev)
    1184           64 :       IF (pre_shift > 0.0_dp) THEN
    1185              :          CALL cp_cfm_scale_and_add(z_one, matrix_operator, &
    1186           64 :                                    CMPLX(pre_shift, 0.0_dp, KIND=dp), matrix_s)
    1187              :       END IF
    1188              : 
    1189              :       CALL store_complex_inverse(preconditioner_env, matrix_operator, &
    1190           64 :                                  ot_precond_full_single_inverse, energy_gap)
    1191              : 
    1192           64 :       DEALLOCATE (operator_evals, occupied_evals)
    1193           64 :       CALL cp_cfm_release(matrix_occ_diag_work)
    1194           64 :       CALL cp_cfm_release(matrix_occ_diag_evec)
    1195           64 :       CALL cp_cfm_release(matrix_occ_diag_b)
    1196           64 :       CALL cp_cfm_release(matrix_occ_diag_a)
    1197           64 :       CALL cp_cfm_release(matrix_diag_work)
    1198           64 :       CALL cp_cfm_release(matrix_diag_evec)
    1199           64 :       CALL cp_cfm_release(matrix_diag_b)
    1200           64 :       CALL cp_cfm_release(matrix_diag_a)
    1201           64 :       CALL cp_cfm_release(matrix_operator)
    1202           64 :       CALL cp_cfm_release(matrix_sc_chc)
    1203           64 :       CALL cp_cfm_release(matrix_chc)
    1204           64 :       CALL cp_cfm_release(matrix_sc0)
    1205           64 :       CALL cp_cfm_release(matrix_hc0)
    1206              : 
    1207           64 :       CALL timestop(handle)
    1208              : 
    1209          256 :    END SUBROUTINE make_complex_full_single_inverse
    1210              : 
    1211              : ! **************************************************************************************************
    1212              : !> \brief Build the inverse complex overlap preconditioner.
    1213              : !> \param preconditioner_env preconditioner storage
    1214              : !> \param matrix_s complex Hermitian k-point overlap
    1215              : ! **************************************************************************************************
    1216           92 :    SUBROUTINE make_complex_full_s_inverse(preconditioner_env, matrix_s)
    1217              : 
    1218              :       TYPE(preconditioner_type)                          :: preconditioner_env
    1219              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix_s
    1220              : 
    1221           92 :       CALL store_complex_inverse(preconditioner_env, matrix_s, ot_precond_s_inverse, 0.0_dp)
    1222              : 
    1223           92 :    END SUBROUTINE make_complex_full_s_inverse
    1224              : 
    1225              : ! **************************************************************************************************
    1226              : !> \brief Build the inverse complex kinetic-plus-overlap preconditioner.
    1227              : !> \param preconditioner_env preconditioner storage
    1228              : !> \param matrix_t complex Hermitian k-point kinetic operator
    1229              : !> \param matrix_s complex Hermitian k-point overlap
    1230              : !> \param energy_gap non-negative overlap shift
    1231              : ! **************************************************************************************************
    1232          124 :    SUBROUTINE make_complex_full_kinetic(preconditioner_env, matrix_t, matrix_s, energy_gap)
    1233              : 
    1234              :       TYPE(preconditioner_type)                          :: preconditioner_env
    1235              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix_t, matrix_s
    1236              :       REAL(KIND=dp), INTENT(IN)                          :: energy_gap
    1237              : 
    1238              :       REAL(KIND=dp)                                      :: shift
    1239              :       TYPE(cp_cfm_type)                                  :: matrix_operator
    1240              : 
    1241          124 :       shift = MAX(0.0_dp, energy_gap)
    1242              :       CALL cp_cfm_create(matrix_operator, matrix_t%matrix_struct, &
    1243          124 :                          name='complex FULL_KINETIC operator')
    1244          124 :       CALL cp_cfm_to_cfm(matrix_t, matrix_operator)
    1245          124 :       CALL cp_cfm_scale_and_add(z_one, matrix_operator, CMPLX(shift, 0.0_dp, KIND=dp), matrix_s)
    1246              :       CALL store_complex_inverse(preconditioner_env, matrix_operator, &
    1247          124 :                                  ot_precond_full_kinetic, energy_gap)
    1248          124 :       CALL cp_cfm_release(matrix_operator)
    1249              : 
    1250          124 :    END SUBROUTINE make_complex_full_kinetic
    1251              : 
    1252              : ! **************************************************************************************************
    1253              : !> \brief Store an explicitly inverted positive complex Hermitian operator.
    1254              : !> \param preconditioner_env preconditioner storage
    1255              : !> \param matrix_operator positive complex Hermitian operator
    1256              : !> \param preconditioner_kind selected OT preconditioner
    1257              : !> \param energy_gap configured spectral shift
    1258              : ! **************************************************************************************************
    1259          840 :    SUBROUTINE store_complex_inverse(preconditioner_env, matrix_operator, &
    1260              :                                     preconditioner_kind, energy_gap)
    1261              : 
    1262              :       TYPE(preconditioner_type)                          :: preconditioner_env
    1263              :       TYPE(cp_cfm_type), INTENT(IN)                      :: matrix_operator
    1264              :       INTEGER, INTENT(IN)                                :: preconditioner_kind
    1265              :       REAL(KIND=dp), INTENT(IN)                          :: energy_gap
    1266              : 
    1267              :       INTEGER                                            :: info
    1268              : 
    1269          280 :       IF (ASSOCIATED(preconditioner_env%complex_fm)) THEN
    1270            0 :          CALL cp_cfm_release(preconditioner_env%complex_fm)
    1271            0 :          DEALLOCATE (preconditioner_env%complex_fm)
    1272              :       END IF
    1273          280 :       ALLOCATE (preconditioner_env%complex_fm)
    1274              :       CALL cp_cfm_create(preconditioner_env%complex_fm, matrix_operator%matrix_struct, &
    1275          280 :                          name='complex inverse preconditioner')
    1276          280 :       CALL cp_cfm_to_cfm(matrix_operator, preconditioner_env%complex_fm)
    1277          280 :       CALL cp_cfm_cholesky_decompose(preconditioner_env%complex_fm, info_out=info)
    1278          280 :       CPASSERT(info == 0)
    1279          280 :       CALL cp_cfm_cholesky_invert(preconditioner_env%complex_fm, info_out=info)
    1280          280 :       CPASSERT(info == 0)
    1281          280 :       CALL cp_cfm_uplo_to_full(preconditioner_env%complex_fm)
    1282              : 
    1283          280 :       preconditioner_env%energy_gap = energy_gap
    1284          280 :       preconditioner_env%in_use = preconditioner_kind
    1285          280 :       preconditioner_env%solver = ot_precond_solver_default
    1286              : 
    1287          280 :    END SUBROUTINE store_complex_inverse
    1288              : END MODULE preconditioner_makes
        

Generated by: LCOV version 2.0-1