LCOV - code coverage report
Current view: top level - src - preconditioner_makes.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:92574dc) Lines: 93.4 % 625 584
Test Date: 2026-09-24 01:27:39 Functions: 100.0 % 16 16

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

Generated by: LCOV version 2.0-1