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