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 Auxiliary routines for GW + Bethe-Salpeter for computing electronic excitations
10 : !> \par History
11 : !> 11.2023 created [Maximilian Graml]
12 : ! **************************************************************************************************
13 : MODULE bse_util
14 : USE atomic_kind_types, ONLY: atomic_kind_type
15 : USE cell_types, ONLY: cell_type
16 : USE cp_blacs_env, ONLY: cp_blacs_env_type
17 : USE cp_control_types, ONLY: dft_control_type
18 : USE cp_dbcsr_api, ONLY: dbcsr_create,&
19 : dbcsr_init_p,&
20 : dbcsr_p_type,&
21 : dbcsr_set,&
22 : dbcsr_type_symmetric
23 : USE cp_dbcsr_cp2k_link, ONLY: cp_dbcsr_alloc_block_from_nbl
24 : USE cp_dbcsr_operations, ONLY: cp_dbcsr_sm_fm_multiply,&
25 : dbcsr_allocate_matrix_set,&
26 : dbcsr_deallocate_matrix_set
27 : USE cp_fm_basic_linalg, ONLY: cp_fm_trace,&
28 : cp_fm_uplo_to_full
29 : USE cp_fm_cholesky, ONLY: cp_fm_cholesky_decompose,&
30 : cp_fm_cholesky_invert
31 : USE cp_fm_struct, ONLY: cp_fm_struct_create,&
32 : cp_fm_struct_release,&
33 : cp_fm_struct_type
34 : USE cp_fm_types, ONLY: cp_fm_create,&
35 : cp_fm_get_info,&
36 : cp_fm_release,&
37 : cp_fm_set_all,&
38 : cp_fm_to_fm_submat,&
39 : cp_fm_to_fm_submat_general,&
40 : cp_fm_type
41 : USE cp_log_handling, ONLY: cp_get_default_logger,&
42 : cp_logger_type
43 : USE cp_output_handling, ONLY: cp_print_key_finished_output,&
44 : cp_print_key_unit_nr
45 : USE cp_realspace_grid_cube, ONLY: cp_pw_to_cube
46 : USE input_constants, ONLY: bse_iterdiag,&
47 : bse_screening_alpha,&
48 : bse_screening_rpa,&
49 : bse_screening_tdhf,&
50 : use_mom_ref_coac
51 : USE input_section_types, ONLY: section_vals_type
52 : USE kinds, ONLY: default_path_length,&
53 : dp,&
54 : int_8
55 : USE message_passing, ONLY: mp_para_env_type,&
56 : mp_request_type
57 : USE mo_window, ONLY: mo_window_type
58 : USE moments_utils, ONLY: get_reference_point
59 : USE mp2_types, ONLY: integ_mat_buffer_type,&
60 : mp2_type
61 : USE parallel_gemm_api, ONLY: parallel_gemm
62 : USE particle_list_types, ONLY: particle_list_type
63 : USE particle_types, ONLY: particle_type
64 : USE physcon, ONLY: evolt
65 : USE pw_env_types, ONLY: pw_env_get,&
66 : pw_env_type
67 : USE pw_poisson_types, ONLY: pw_poisson_type
68 : USE pw_pool_types, ONLY: pw_pool_p_type,&
69 : pw_pool_type
70 : USE pw_types, ONLY: pw_c1d_gs_type,&
71 : pw_r3d_rs_type
72 : USE qs_collocate_density, ONLY: calculate_wavefunction
73 : USE qs_environment_types, ONLY: get_qs_env,&
74 : qs_environment_type
75 : USE qs_kind_types, ONLY: qs_kind_type
76 : USE qs_mo_types, ONLY: get_mo_set,&
77 : mo_set_type
78 : USE qs_moments, ONLY: build_local_moment_matrix
79 : USE qs_neighbor_list_types, ONLY: neighbor_list_set_p_type
80 : USE qs_subsys_types, ONLY: qs_subsys_get,&
81 : qs_subsys_type
82 : USE rpa_communication, ONLY: communicate_buffer
83 : USE util, ONLY: sort,&
84 : sort_unique
85 : #include "./base/base_uses.f90"
86 :
87 : IMPLICIT NONE
88 :
89 : PRIVATE
90 :
91 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'bse_util'
92 :
93 : PUBLIC :: mult_B_with_W, fm_general_add_bse, truncate_fm, &
94 : deallocate_matrices_bse, comp_eigvec_coeff_BSE, sort_excitations, &
95 : estimate_BSE_resources, filter_eigvec_contrib, truncate_BSE_matrices, &
96 : adapt_BSE_input_params, get_multipoles_mo, &
97 : reshuffle_eigvec, print_bse_nto_cubes, trace_exciton_descr, &
98 : get_bse_spin_block_layout, assemble_joint_ov_slab, &
99 : ia_of_occ_virt, occ_of_ia, virt_of_ia
100 :
101 : CONTAINS
102 :
103 : ! **************************************************************************************************
104 : !> \brief Compound transition index ia = (i-1)*virt + a of the pair (i, a): a is the fast index
105 : !> \param i_occ occupied level
106 : !> \param a_virt virtual level
107 : !> \param virt number of virtual levels
108 : !> \return ia
109 : ! **************************************************************************************************
110 12240 : PURE FUNCTION ia_of_occ_virt(i_occ, a_virt, virt) RESULT(ia)
111 :
112 : INTEGER, INTENT(IN) :: i_occ, a_virt, virt
113 : INTEGER :: ia
114 :
115 12240 : ia = (i_occ - 1)*virt + a_virt
116 :
117 12240 : END FUNCTION ia_of_occ_virt
118 :
119 : ! **************************************************************************************************
120 : !> \brief Occupied level of the compound transition index ia = (i-1)*virt + a
121 : !> \param ia compound transition index
122 : !> \param virt number of virtual levels
123 : !> \return i
124 : ! **************************************************************************************************
125 0 : PURE FUNCTION occ_of_ia(ia, virt) RESULT(i_occ)
126 :
127 : INTEGER, INTENT(IN) :: ia, virt
128 : INTEGER :: i_occ
129 :
130 0 : i_occ = (ia - 1)/virt + 1
131 :
132 0 : END FUNCTION occ_of_ia
133 :
134 : ! **************************************************************************************************
135 : !> \brief Virtual level of the compound transition index ia = (i-1)*virt + a
136 : !> \param ia compound transition index
137 : !> \param virt number of virtual levels
138 : !> \return a
139 : ! **************************************************************************************************
140 0 : PURE FUNCTION virt_of_ia(ia, virt) RESULT(a_virt)
141 :
142 : INTEGER, INTENT(IN) :: ia, virt
143 : INTEGER :: a_virt
144 :
145 0 : a_virt = MOD(ia - 1, virt) + 1
146 :
147 0 : END FUNCTION virt_of_ia
148 :
149 : ! **************************************************************************************************
150 : !> \brief Multiplies B-matrix (RI-3c-Integrals) with W (screening) to obtain \bar{B}
151 : !> \param fm_mat_S_ij_bse ...
152 : !> \param fm_mat_S_ia_bse ...
153 : !> \param fm_mat_S_bar_ia_bse ...
154 : !> \param fm_mat_S_bar_ij_bse ...
155 : !> \param fm_mat_Q_static_bse_gemm ...
156 : !> \param dimen_RI ...
157 : !> \param homo ...
158 : !> \param virtual ...
159 : ! **************************************************************************************************
160 336 : SUBROUTINE mult_B_with_W(fm_mat_S_ij_bse, fm_mat_S_ia_bse, fm_mat_S_bar_ia_bse, &
161 : fm_mat_S_bar_ij_bse, fm_mat_Q_static_bse_gemm, &
162 : dimen_RI, homo, virtual)
163 :
164 : TYPE(cp_fm_type), INTENT(IN) :: fm_mat_S_ij_bse, fm_mat_S_ia_bse
165 : TYPE(cp_fm_type), INTENT(OUT) :: fm_mat_S_bar_ia_bse, fm_mat_S_bar_ij_bse
166 : TYPE(cp_fm_type), INTENT(IN) :: fm_mat_Q_static_bse_gemm
167 : INTEGER, INTENT(IN) :: dimen_RI, homo, virtual
168 :
169 : CHARACTER(LEN=*), PARAMETER :: routineN = 'mult_B_with_W'
170 :
171 : INTEGER :: handle, i_global, iiB, info_chol, &
172 : j_global, jjB, ncol_local, nrow_local
173 56 : INTEGER, DIMENSION(:), POINTER :: col_indices, row_indices
174 : TYPE(cp_fm_type) :: fm_work
175 :
176 56 : CALL timeset(routineN, handle)
177 :
178 56 : CALL cp_fm_create(fm_mat_S_bar_ia_bse, fm_mat_S_ia_bse%matrix_struct)
179 56 : CALL cp_fm_set_all(fm_mat_S_bar_ia_bse, 0.0_dp)
180 :
181 56 : CALL cp_fm_create(fm_mat_S_bar_ij_bse, fm_mat_S_ij_bse%matrix_struct)
182 56 : CALL cp_fm_set_all(fm_mat_S_bar_ij_bse, 0.0_dp)
183 :
184 56 : CALL cp_fm_create(fm_work, fm_mat_Q_static_bse_gemm%matrix_struct)
185 56 : CALL cp_fm_set_all(fm_work, 0.0_dp)
186 :
187 : ! get info of fm_mat_Q_static_bse and compute ((1+Q(0))^-1-1)
188 : CALL cp_fm_get_info(matrix=fm_mat_Q_static_bse_gemm, &
189 : nrow_local=nrow_local, &
190 : ncol_local=ncol_local, &
191 : row_indices=row_indices, &
192 56 : col_indices=col_indices)
193 :
194 4278 : DO jjB = 1, ncol_local
195 4222 : j_global = col_indices(jjB)
196 238165 : DO iiB = 1, nrow_local
197 233887 : i_global = row_indices(iiB)
198 238109 : IF (j_global == i_global .AND. i_global <= dimen_RI) THEN
199 2415 : fm_mat_Q_static_bse_gemm%local_data(iiB, jjB) = fm_mat_Q_static_bse_gemm%local_data(iiB, jjB) + 1.0_dp
200 : END IF
201 : END DO
202 : END DO
203 :
204 : ! calculate Trace(Log(Matrix)) as Log(DET(Matrix)) via cholesky decomposition
205 56 : CALL cp_fm_cholesky_decompose(matrix=fm_mat_Q_static_bse_gemm, n=dimen_RI, info_out=info_chol)
206 :
207 56 : IF (info_chol /= 0) THEN
208 0 : CALL cp_abort(__LOCATION__, 'Cholesky decomposition failed for static polarization in BSE')
209 : END IF
210 :
211 : ! calculate [1+Q(i0)]^-1
212 56 : CALL cp_fm_cholesky_invert(fm_mat_Q_static_bse_gemm)
213 :
214 : ! symmetrize the result
215 56 : CALL cp_fm_uplo_to_full(fm_mat_Q_static_bse_gemm, fm_work)
216 :
217 : CALL parallel_gemm(transa="N", transb="N", m=dimen_RI, n=homo**2, k=dimen_RI, alpha=1.0_dp, &
218 : matrix_a=fm_mat_Q_static_bse_gemm, matrix_b=fm_mat_S_ij_bse, beta=0.0_dp, &
219 56 : matrix_c=fm_mat_S_bar_ij_bse)
220 :
221 : ! fm_mat_S_bar_ia_bse has a different blacs_env as fm_mat_S_ij_bse since we take
222 : ! fm_mat_S_ia_bse from RPA. Therefore, we also need a different fm_mat_Q_static_bse_gemm
223 : CALL parallel_gemm(transa="N", transb="N", m=dimen_RI, n=homo*virtual, k=dimen_RI, alpha=1.0_dp, &
224 : matrix_a=fm_mat_Q_static_bse_gemm, matrix_b=fm_mat_S_ia_bse, beta=0.0_dp, &
225 56 : matrix_c=fm_mat_S_bar_ia_bse)
226 :
227 56 : CALL cp_fm_release(fm_work)
228 :
229 56 : CALL timestop(handle)
230 :
231 56 : END SUBROUTINE mult_B_with_W
232 :
233 : ! **************************************************************************************************
234 : !> \brief Adds and reorders full matrices with a combined index structure, e.g. adding W_ij,ab
235 : !> to A_ia, jb which needs MPI communication.
236 : !> \param fm_out ...
237 : !> \param fm_in ...
238 : !> \param beta ...
239 : !> \param nrow_secidx_in ...
240 : !> \param ncol_secidx_in ...
241 : !> \param nrow_secidx_out ...
242 : !> \param ncol_secidx_out ...
243 : !> \param unit_nr ...
244 : !> \param reordering ...
245 : !> \param mp2_env ...
246 : !> \param row_offset ...
247 : !> \param col_offset ...
248 : ! **************************************************************************************************
249 536 : SUBROUTINE fm_general_add_bse(fm_out, fm_in, beta, nrow_secidx_in, ncol_secidx_in, &
250 : nrow_secidx_out, ncol_secidx_out, unit_nr, reordering, mp2_env, &
251 : row_offset, col_offset)
252 :
253 : TYPE(cp_fm_type), INTENT(INOUT) :: fm_out
254 : TYPE(cp_fm_type), INTENT(IN) :: fm_in
255 : REAL(kind=dp) :: beta
256 : INTEGER, INTENT(IN) :: nrow_secidx_in, ncol_secidx_in, &
257 : nrow_secidx_out, ncol_secidx_out
258 : INTEGER :: unit_nr
259 : INTEGER, DIMENSION(4) :: reordering
260 : TYPE(mp2_type), INTENT(IN) :: mp2_env
261 : INTEGER, INTENT(IN), OPTIONAL :: row_offset, col_offset
262 :
263 : CHARACTER(LEN=*), PARAMETER :: routineN = 'fm_general_add_bse'
264 :
265 : INTEGER :: col_idx_loc, dummy, handle, handle2, i_entry_rec, idx_col_out, idx_row_out, ii, &
266 : iproc, jj, my_col_offset, my_row_offset, ncol_block_in, ncol_block_out, ncol_local_in, &
267 : ncol_local_out, nprocs, nrow_block_in, nrow_block_out, nrow_local_in, nrow_local_out, &
268 : proc_send, row_idx_loc, send_pcol, send_prow
269 536 : INTEGER, ALLOCATABLE, DIMENSION(:) :: entry_counter, num_entries_rec, &
270 : num_entries_send
271 : INTEGER, DIMENSION(4) :: indices_in
272 536 : INTEGER, DIMENSION(:), POINTER :: col_indices_in, col_indices_out, &
273 536 : row_indices_in, row_indices_out
274 : TYPE(integ_mat_buffer_type), ALLOCATABLE, &
275 536 : DIMENSION(:) :: buffer_rec, buffer_send
276 : TYPE(mp_para_env_type), POINTER :: para_env_out
277 536 : TYPE(mp_request_type), DIMENSION(:, :), POINTER :: req_array
278 :
279 : ! Offsets place the reshuffled block into a sub-block of fm_out (open-shell joint matrix);
280 : ! both default 0, recovering the closed-shell single-block placement bit-identically.
281 :
282 536 : my_row_offset = 0
283 536 : my_col_offset = 0
284 72 : IF (PRESENT(row_offset)) my_row_offset = row_offset
285 536 : IF (PRESENT(col_offset)) my_col_offset = col_offset
286 :
287 536 : CALL timeset(routineN, handle)
288 536 : CALL timeset(routineN//"_1_setup", handle2)
289 :
290 536 : para_env_out => fm_out%matrix_struct%para_env
291 : ! A_iajb
292 : ! We start by moving data from local parts of W_ijab to the full matrix A_iajb using buffers
293 : CALL cp_fm_get_info(matrix=fm_out, &
294 : nrow_local=nrow_local_out, &
295 : ncol_local=ncol_local_out, &
296 : row_indices=row_indices_out, &
297 : col_indices=col_indices_out, &
298 : nrow_block=nrow_block_out, &
299 536 : ncol_block=ncol_block_out)
300 :
301 1608 : ALLOCATE (num_entries_rec(0:para_env_out%num_pe - 1))
302 1608 : ALLOCATE (num_entries_send(0:para_env_out%num_pe - 1))
303 :
304 536 : num_entries_rec(:) = 0
305 536 : num_entries_send(:) = 0
306 :
307 536 : dummy = 0
308 :
309 : CALL cp_fm_get_info(matrix=fm_in, &
310 : nrow_local=nrow_local_in, &
311 : ncol_local=ncol_local_in, &
312 : row_indices=row_indices_in, &
313 : col_indices=col_indices_in, &
314 : nrow_block=nrow_block_in, &
315 536 : ncol_block=ncol_block_in)
316 :
317 536 : IF (unit_nr > 0 .AND. mp2_env%bse%bse_debug_print) THEN
318 89 : WRITE (unit_nr, '(T2,A10,T13,A14,A10,T71,I10)') 'BSE|DEBUG|', 'Row number of ', fm_out%name, &
319 178 : fm_out%matrix_struct%nrow_global
320 89 : WRITE (unit_nr, '(T2,A10,T13,A17,A10,T71,I10)') 'BSE|DEBUG|', 'Column number of ', fm_out%name, &
321 178 : fm_out%matrix_struct%ncol_global
322 :
323 89 : WRITE (unit_nr, '(T2,A10,T13,A18,A10,T71,I10)') 'BSE|DEBUG|', 'Row block size of ', fm_out%name, nrow_block_out
324 89 : WRITE (unit_nr, '(T2,A10,T13,A21,A10,T71,I10)') 'BSE|DEBUG|', 'Column block size of ', fm_out%name, ncol_block_out
325 :
326 89 : WRITE (unit_nr, '(T2,A10,T13,A14,A10,T71,I10)') 'BSE|DEBUG|', 'Row number of ', fm_in%name, &
327 178 : fm_in%matrix_struct%nrow_global
328 89 : WRITE (unit_nr, '(T2,A10,T13,A17,A10,T71,I10)') 'BSE|DEBUG|', 'Column number of ', fm_in%name, &
329 178 : fm_in%matrix_struct%ncol_global
330 :
331 89 : WRITE (unit_nr, '(T2,A10,T13,A18,A10,T71,I10)') 'BSE|DEBUG|', 'Row block size of ', fm_in%name, nrow_block_in
332 89 : WRITE (unit_nr, '(T2,A10,T13,A21,A10,T71,I10)') 'BSE|DEBUG|', 'Column block size of ', fm_in%name, ncol_block_in
333 : END IF
334 :
335 : ! Use scalapack wrapper to find process index in fm_out
336 : ! To that end, we obtain the global index in fm_out from the level indices
337 536 : indices_in(:) = 0
338 12754 : DO row_idx_loc = 1, nrow_local_in
339 12218 : indices_in(1) = (row_indices_in(row_idx_loc) - 1)/nrow_secidx_in + 1
340 12218 : indices_in(2) = MOD(row_indices_in(row_idx_loc) - 1, nrow_secidx_in) + 1
341 1221584 : DO col_idx_loc = 1, ncol_local_in
342 1208830 : indices_in(3) = (col_indices_in(col_idx_loc) - 1)/ncol_secidx_in + 1
343 1208830 : indices_in(4) = MOD(col_indices_in(col_idx_loc) - 1, ncol_secidx_in) + 1
344 :
345 1208830 : idx_row_out = my_row_offset + indices_in(reordering(2)) + (indices_in(reordering(1)) - 1)*nrow_secidx_out
346 1208830 : idx_col_out = my_col_offset + indices_in(reordering(4)) + (indices_in(reordering(3)) - 1)*ncol_secidx_out
347 :
348 1208830 : send_prow = fm_out%matrix_struct%g2p_row(idx_row_out)
349 1208830 : send_pcol = fm_out%matrix_struct%g2p_col(idx_col_out)
350 :
351 1208830 : proc_send = fm_out%matrix_struct%context%blacs2mpi(send_prow, send_pcol)
352 :
353 1221048 : num_entries_send(proc_send) = num_entries_send(proc_send) + 1
354 :
355 : END DO
356 : END DO
357 :
358 536 : CALL timestop(handle2)
359 :
360 536 : CALL timeset(routineN//"_2_comm_entry_nums", handle2)
361 536 : IF (unit_nr > 0 .AND. mp2_env%bse%bse_debug_print) THEN
362 89 : WRITE (unit_nr, '(T2,A10,T13,A27)') 'BSE|DEBUG|', 'Communicating entry numbers'
363 : END IF
364 :
365 536 : CALL para_env_out%alltoall(num_entries_send, num_entries_rec, 1)
366 :
367 536 : CALL timestop(handle2)
368 :
369 536 : CALL timeset(routineN//"_3_alloc_buffer", handle2)
370 536 : IF (unit_nr > 0 .AND. mp2_env%bse%bse_debug_print) THEN
371 89 : WRITE (unit_nr, '(T2,A10,T13,A18)') 'BSE|DEBUG|', 'Allocating buffers'
372 : END IF
373 :
374 : ! Buffers for entries and their indices
375 2680 : ALLOCATE (buffer_rec(0:para_env_out%num_pe - 1))
376 2680 : ALLOCATE (buffer_send(0:para_env_out%num_pe - 1))
377 :
378 : ! allocate data message and corresponding indices
379 1608 : DO iproc = 0, para_env_out%num_pe - 1
380 :
381 3026 : ALLOCATE (buffer_rec(iproc)%msg(num_entries_rec(iproc)))
382 1210438 : buffer_rec(iproc)%msg = 0.0_dp
383 :
384 : END DO
385 :
386 1608 : DO iproc = 0, para_env_out%num_pe - 1
387 :
388 3026 : ALLOCATE (buffer_send(iproc)%msg(num_entries_send(iproc)))
389 1210438 : buffer_send(iproc)%msg = 0.0_dp
390 :
391 : END DO
392 :
393 1608 : DO iproc = 0, para_env_out%num_pe - 1
394 :
395 3026 : ALLOCATE (buffer_rec(iproc)%indx(num_entries_rec(iproc), 2))
396 2421412 : buffer_rec(iproc)%indx = 0
397 :
398 : END DO
399 :
400 1608 : DO iproc = 0, para_env_out%num_pe - 1
401 :
402 3026 : ALLOCATE (buffer_send(iproc)%indx(num_entries_send(iproc), 2))
403 2421412 : buffer_send(iproc)%indx = 0
404 :
405 : END DO
406 :
407 536 : CALL timestop(handle2)
408 :
409 536 : CALL timeset(routineN//"_4_buf_from_fmin_"//fm_out%name, handle2)
410 536 : IF (unit_nr > 0 .AND. mp2_env%bse%bse_debug_print) THEN
411 89 : WRITE (unit_nr, '(T2,A10,T13,A18,A10,A13)') 'BSE|DEBUG|', 'Writing data from ', fm_in%name, ' into buffers'
412 : END IF
413 :
414 1608 : ALLOCATE (entry_counter(0:para_env_out%num_pe - 1))
415 536 : entry_counter(:) = 0
416 :
417 : ! Now we can write the actual data and indices to the send-buffer
418 12754 : DO row_idx_loc = 1, nrow_local_in
419 12218 : indices_in(1) = (row_indices_in(row_idx_loc) - 1)/nrow_secidx_in + 1
420 12218 : indices_in(2) = MOD(row_indices_in(row_idx_loc) - 1, nrow_secidx_in) + 1
421 1221584 : DO col_idx_loc = 1, ncol_local_in
422 1208830 : indices_in(3) = (col_indices_in(col_idx_loc) - 1)/ncol_secidx_in + 1
423 1208830 : indices_in(4) = MOD(col_indices_in(col_idx_loc) - 1, ncol_secidx_in) + 1
424 :
425 1208830 : idx_row_out = my_row_offset + indices_in(reordering(2)) + (indices_in(reordering(1)) - 1)*nrow_secidx_out
426 1208830 : idx_col_out = my_col_offset + indices_in(reordering(4)) + (indices_in(reordering(3)) - 1)*ncol_secidx_out
427 :
428 1208830 : send_prow = fm_out%matrix_struct%g2p_row(idx_row_out)
429 1208830 : send_pcol = fm_out%matrix_struct%g2p_col(idx_col_out)
430 :
431 1208830 : proc_send = fm_out%matrix_struct%context%blacs2mpi(send_prow, send_pcol)
432 1208830 : entry_counter(proc_send) = entry_counter(proc_send) + 1
433 :
434 : buffer_send(proc_send)%msg(entry_counter(proc_send)) = &
435 1208830 : fm_in%local_data(row_idx_loc, col_idx_loc)
436 :
437 1208830 : buffer_send(proc_send)%indx(entry_counter(proc_send), 1) = idx_row_out
438 1221048 : buffer_send(proc_send)%indx(entry_counter(proc_send), 2) = idx_col_out
439 :
440 : END DO
441 : END DO
442 :
443 8040 : ALLOCATE (req_array(1:para_env_out%num_pe, 4))
444 :
445 536 : CALL timestop(handle2)
446 :
447 536 : CALL timeset(routineN//"_5_comm_buffer", handle2)
448 536 : IF (unit_nr > 0 .AND. mp2_env%bse%bse_debug_print) THEN
449 89 : WRITE (unit_nr, '(T2,A10,T13,A21)') 'BSE|DEBUG|', 'Communicating buffers'
450 : END IF
451 :
452 : ! communicate the buffer
453 : CALL communicate_buffer(para_env_out, num_entries_rec, num_entries_send, buffer_rec, &
454 536 : buffer_send, req_array)
455 :
456 536 : CALL timestop(handle2)
457 :
458 536 : CALL timeset(routineN//"_6_buffer_to_fmout"//fm_out%name, handle2)
459 536 : IF (unit_nr > 0 .AND. mp2_env%bse%bse_debug_print) THEN
460 89 : WRITE (unit_nr, '(T2,A10,T13,A24,A10)') 'BSE|DEBUG|', 'Writing from buffers to ', fm_out%name
461 : END IF
462 :
463 : ! fill fm_out with the entries from buffer_rec, i.e. buffer_rec are parts of fm_in
464 536 : nprocs = para_env_out%num_pe
465 :
466 : !$OMP PARALLEL DO DEFAULT(NONE) &
467 : !$OMP SHARED(fm_out, nprocs, num_entries_rec, buffer_rec, beta) &
468 536 : !$OMP PRIVATE(iproc, i_entry_rec, ii, jj)
469 : DO iproc = 0, nprocs - 1
470 : DO i_entry_rec = 1, num_entries_rec(iproc)
471 : ii = fm_out%matrix_struct%g2l_row(buffer_rec(iproc)%indx(i_entry_rec, 1))
472 : jj = fm_out%matrix_struct%g2l_col(buffer_rec(iproc)%indx(i_entry_rec, 2))
473 :
474 : fm_out%local_data(ii, jj) = fm_out%local_data(ii, jj) + beta*buffer_rec(iproc)%msg(i_entry_rec)
475 : END DO
476 : END DO
477 : !$OMP END PARALLEL DO
478 :
479 536 : CALL timestop(handle2)
480 :
481 536 : CALL timeset(routineN//"_7_cleanup", handle2)
482 536 : IF (unit_nr > 0 .AND. mp2_env%bse%bse_debug_print) THEN
483 89 : WRITE (unit_nr, '(T2,A10,T13,A41)') 'BSE|DEBUG|', 'Starting cleanup of communication buffers'
484 : END IF
485 :
486 : !Clean up all the arrays from the communication process
487 1608 : DO iproc = 0, para_env_out%num_pe - 1
488 1072 : DEALLOCATE (buffer_rec(iproc)%msg)
489 1072 : DEALLOCATE (buffer_rec(iproc)%indx)
490 1072 : DEALLOCATE (buffer_send(iproc)%msg)
491 1608 : DEALLOCATE (buffer_send(iproc)%indx)
492 : END DO
493 2680 : DEALLOCATE (buffer_rec, buffer_send)
494 536 : DEALLOCATE (req_array)
495 536 : DEALLOCATE (entry_counter)
496 536 : DEALLOCATE (num_entries_rec, num_entries_send)
497 :
498 536 : CALL timestop(handle2)
499 536 : CALL timestop(handle)
500 :
501 4288 : END SUBROUTINE fm_general_add_bse
502 :
503 : ! **************************************************************************************************
504 : !> \brief Routine for truncating a full matrix as given by the energy cutoffs in the input file.
505 : !> Logic: Matrices have some dimension dimen_RI x nrow_in*ncol_in for the incoming (untruncated) matrix
506 : !> and dimen_RI x nrow_out*ncol_out for the truncated matrix. The truncation is done by resorting the indices
507 : !> via parallel communication.
508 : !> \param fm_out ...
509 : !> \param fm_in ...
510 : !> \param ncol_in ...
511 : !> \param nrow_out ...
512 : !> \param ncol_out ...
513 : !> \param unit_nr ...
514 : !> \param mp2_env ...
515 : !> \param nrow_offset ...
516 : !> \param ncol_offset ...
517 : ! **************************************************************************************************
518 132 : SUBROUTINE truncate_fm(fm_out, fm_in, ncol_in, &
519 : nrow_out, ncol_out, unit_nr, mp2_env, &
520 : nrow_offset, ncol_offset)
521 :
522 : TYPE(cp_fm_type), INTENT(INOUT) :: fm_out
523 : TYPE(cp_fm_type), INTENT(IN) :: fm_in
524 : INTEGER :: ncol_in, nrow_out, ncol_out, unit_nr
525 : TYPE(mp2_type), INTENT(INOUT) :: mp2_env
526 : INTEGER, INTENT(IN), OPTIONAL :: nrow_offset, ncol_offset
527 :
528 : CHARACTER(LEN=*), PARAMETER :: routineN = 'truncate_fm'
529 :
530 : INTEGER :: col_idx_loc, dummy, handle, handle2, i_entry_rec, idx_col_first, idx_col_in, &
531 : idx_col_out, idx_col_sec, idx_row_in, ii, iproc, jj, ncol_block_in, ncol_block_out, &
532 : ncol_local_in, ncol_local_out, nprocs, nrow_block_in, nrow_block_out, nrow_local_in, &
533 : nrow_local_out, proc_send, row_idx_loc, send_pcol, send_prow
534 132 : INTEGER, ALLOCATABLE, DIMENSION(:) :: entry_counter, num_entries_rec, &
535 : num_entries_send
536 132 : INTEGER, DIMENSION(:), POINTER :: col_indices_in, col_indices_out, &
537 132 : row_indices_in, row_indices_out
538 : LOGICAL :: correct_ncol, correct_nrow
539 : TYPE(integ_mat_buffer_type), ALLOCATABLE, &
540 132 : DIMENSION(:) :: buffer_rec, buffer_send
541 : TYPE(mp_para_env_type), POINTER :: para_env_out
542 132 : TYPE(mp_request_type), DIMENSION(:, :), POINTER :: req_array
543 :
544 132 : CALL timeset(routineN, handle)
545 132 : CALL timeset(routineN//"_1_setup", handle2)
546 :
547 132 : correct_nrow = .FALSE.
548 132 : correct_ncol = .FALSE.
549 : !In case of truncation in the occupied space, we need to correct the interval of indices
550 132 : IF (PRESENT(nrow_offset)) THEN
551 88 : correct_nrow = .TRUE.
552 : END IF
553 132 : IF (PRESENT(ncol_offset)) THEN
554 44 : correct_ncol = .TRUE.
555 : END IF
556 :
557 132 : para_env_out => fm_out%matrix_struct%para_env
558 :
559 : CALL cp_fm_get_info(matrix=fm_out, &
560 : nrow_local=nrow_local_out, &
561 : ncol_local=ncol_local_out, &
562 : row_indices=row_indices_out, &
563 : col_indices=col_indices_out, &
564 : nrow_block=nrow_block_out, &
565 132 : ncol_block=ncol_block_out)
566 :
567 396 : ALLOCATE (num_entries_rec(0:para_env_out%num_pe - 1))
568 396 : ALLOCATE (num_entries_send(0:para_env_out%num_pe - 1))
569 :
570 132 : num_entries_rec(:) = 0
571 132 : num_entries_send(:) = 0
572 :
573 132 : dummy = 0
574 :
575 : CALL cp_fm_get_info(matrix=fm_in, &
576 : nrow_local=nrow_local_in, &
577 : ncol_local=ncol_local_in, &
578 : row_indices=row_indices_in, &
579 : col_indices=col_indices_in, &
580 : nrow_block=nrow_block_in, &
581 132 : ncol_block=ncol_block_in)
582 :
583 132 : IF (unit_nr > 0 .AND. mp2_env%bse%bse_debug_print) THEN
584 9 : WRITE (unit_nr, '(T2,A10,T13,A14,A10,T71,I10)') 'BSE|DEBUG|', 'Row number of ', fm_out%name, &
585 18 : fm_out%matrix_struct%nrow_global
586 9 : WRITE (unit_nr, '(T2,A10,T13,A17,A10,T71,I10)') 'BSE|DEBUG|', 'Column number of ', fm_out%name, &
587 18 : fm_out%matrix_struct%ncol_global
588 :
589 9 : WRITE (unit_nr, '(T2,A10,T13,A18,A10,T71,I10)') 'BSE|DEBUG|', 'Row block size of ', fm_out%name, nrow_block_out
590 9 : WRITE (unit_nr, '(T2,A10,T13,A21,A10,T71,I10)') 'BSE|DEBUG|', 'Column block size of ', fm_out%name, ncol_block_out
591 :
592 9 : WRITE (unit_nr, '(T2,A10,T13,A14,A10,T71,I10)') 'BSE|DEBUG|', 'Row number of ', fm_in%name, &
593 18 : fm_in%matrix_struct%nrow_global
594 9 : WRITE (unit_nr, '(T2,A10,T13,A17,A10,T71,I10)') 'BSE|DEBUG|', 'Column number of ', fm_in%name, &
595 18 : fm_in%matrix_struct%ncol_global
596 :
597 9 : WRITE (unit_nr, '(T2,A10,T13,A18,A10,T71,I10)') 'BSE|DEBUG|', 'Row block size of ', fm_in%name, nrow_block_in
598 9 : WRITE (unit_nr, '(T2,A10,T13,A21,A10,T71,I10)') 'BSE|DEBUG|', 'Column block size of ', fm_in%name, ncol_block_in
599 : END IF
600 :
601 : ! We find global indices in S with nrow_in and ncol_in for truncation
602 9240 : DO col_idx_loc = 1, ncol_local_in
603 9108 : idx_col_in = col_indices_in(col_idx_loc)
604 :
605 9108 : idx_col_first = (idx_col_in - 1)/ncol_in + 1
606 9108 : idx_col_sec = MOD(idx_col_in - 1, ncol_in) + 1
607 :
608 : ! If occupied orbitals are included, these have to be handled differently
609 : ! due to their reversed indexing
610 9108 : IF (correct_nrow) THEN
611 3284 : idx_col_first = idx_col_first - nrow_offset + 1
612 3284 : IF (idx_col_first <= 0) CYCLE
613 : ELSE
614 5824 : IF (idx_col_first > nrow_out) EXIT
615 : END IF
616 9108 : IF (correct_ncol) THEN
617 554 : idx_col_sec = idx_col_sec - ncol_offset + 1
618 554 : IF (idx_col_sec <= 0) CYCLE
619 : ELSE
620 8554 : IF (idx_col_sec > ncol_out) CYCLE
621 : END IF
622 :
623 8106 : idx_col_out = idx_col_sec + (idx_col_first - 1)*ncol_out
624 :
625 325508 : DO row_idx_loc = 1, nrow_local_in
626 317270 : idx_row_in = row_indices_in(row_idx_loc)
627 :
628 317270 : send_prow = fm_out%matrix_struct%g2p_row(idx_row_in)
629 317270 : send_pcol = fm_out%matrix_struct%g2p_col(idx_col_out)
630 :
631 317270 : proc_send = fm_out%matrix_struct%context%blacs2mpi(send_prow, send_pcol)
632 :
633 326378 : num_entries_send(proc_send) = num_entries_send(proc_send) + 1
634 :
635 : END DO
636 : END DO
637 :
638 132 : CALL timestop(handle2)
639 :
640 132 : CALL timeset(routineN//"_2_comm_entry_nums", handle2)
641 132 : IF (unit_nr > 0 .AND. mp2_env%bse%bse_debug_print) THEN
642 9 : WRITE (unit_nr, '(T2,A10,T13,A27)') 'BSE|DEBUG|', 'Communicating entry numbers'
643 : END IF
644 :
645 132 : CALL para_env_out%alltoall(num_entries_send, num_entries_rec, 1)
646 :
647 132 : CALL timestop(handle2)
648 :
649 132 : CALL timeset(routineN//"_3_alloc_buffer", handle2)
650 132 : IF (unit_nr > 0 .AND. mp2_env%bse%bse_debug_print) THEN
651 9 : WRITE (unit_nr, '(T2,A10,T13,A18)') 'BSE|DEBUG|', 'Allocating buffers'
652 : END IF
653 :
654 : ! Buffers for entries and their indices
655 660 : ALLOCATE (buffer_rec(0:para_env_out%num_pe - 1))
656 660 : ALLOCATE (buffer_send(0:para_env_out%num_pe - 1))
657 :
658 : ! allocate data message and corresponding indices
659 396 : DO iproc = 0, para_env_out%num_pe - 1
660 :
661 704 : ALLOCATE (buffer_rec(iproc)%msg(num_entries_rec(iproc)))
662 317666 : buffer_rec(iproc)%msg = 0.0_dp
663 :
664 : END DO
665 :
666 396 : DO iproc = 0, para_env_out%num_pe - 1
667 :
668 704 : ALLOCATE (buffer_send(iproc)%msg(num_entries_send(iproc)))
669 317666 : buffer_send(iproc)%msg = 0.0_dp
670 :
671 : END DO
672 :
673 396 : DO iproc = 0, para_env_out%num_pe - 1
674 :
675 704 : ALLOCATE (buffer_rec(iproc)%indx(num_entries_rec(iproc), 2))
676 635464 : buffer_rec(iproc)%indx = 0
677 :
678 : END DO
679 :
680 396 : DO iproc = 0, para_env_out%num_pe - 1
681 :
682 704 : ALLOCATE (buffer_send(iproc)%indx(num_entries_send(iproc), 2))
683 635464 : buffer_send(iproc)%indx = 0
684 :
685 : END DO
686 :
687 132 : CALL timestop(handle2)
688 :
689 132 : CALL timeset(routineN//"_4_buf_from_fmin_"//fm_out%name, handle2)
690 132 : IF (unit_nr > 0 .AND. mp2_env%bse%bse_debug_print) THEN
691 9 : WRITE (unit_nr, '(T2,A10,T13,A18,A10,A13)') 'BSE|DEBUG|', 'Writing data from ', fm_in%name, ' into buffers'
692 : END IF
693 :
694 396 : ALLOCATE (entry_counter(0:para_env_out%num_pe - 1))
695 132 : entry_counter(:) = 0
696 :
697 : ! Now we can write the actual data and indices to the send-buffer
698 9240 : DO col_idx_loc = 1, ncol_local_in
699 9108 : idx_col_in = col_indices_in(col_idx_loc)
700 :
701 9108 : idx_col_first = (idx_col_in - 1)/ncol_in + 1
702 9108 : idx_col_sec = MOD(idx_col_in - 1, ncol_in) + 1
703 :
704 : ! If occupied orbitals are included, these have to be handled differently
705 : ! due to their reversed indexing
706 9108 : IF (correct_nrow) THEN
707 3284 : idx_col_first = idx_col_first - nrow_offset + 1
708 3284 : IF (idx_col_first <= 0) CYCLE
709 : ELSE
710 5824 : IF (idx_col_first > nrow_out) EXIT
711 : END IF
712 9108 : IF (correct_ncol) THEN
713 554 : idx_col_sec = idx_col_sec - ncol_offset + 1
714 554 : IF (idx_col_sec <= 0) CYCLE
715 : ELSE
716 8554 : IF (idx_col_sec > ncol_out) CYCLE
717 : END IF
718 :
719 8106 : idx_col_out = idx_col_sec + (idx_col_first - 1)*ncol_out
720 :
721 325508 : DO row_idx_loc = 1, nrow_local_in
722 317270 : idx_row_in = row_indices_in(row_idx_loc)
723 :
724 317270 : send_prow = fm_out%matrix_struct%g2p_row(idx_row_in)
725 :
726 317270 : send_pcol = fm_out%matrix_struct%g2p_col(idx_col_out)
727 :
728 317270 : proc_send = fm_out%matrix_struct%context%blacs2mpi(send_prow, send_pcol)
729 317270 : entry_counter(proc_send) = entry_counter(proc_send) + 1
730 :
731 : buffer_send(proc_send)%msg(entry_counter(proc_send)) = &
732 317270 : fm_in%local_data(row_idx_loc, col_idx_loc)
733 : !No need to create row_out, since it is identical to incoming
734 : !We dont change the RI index for any fm_mat_XX_BSE
735 317270 : buffer_send(proc_send)%indx(entry_counter(proc_send), 1) = idx_row_in
736 326378 : buffer_send(proc_send)%indx(entry_counter(proc_send), 2) = idx_col_out
737 :
738 : END DO
739 : END DO
740 :
741 1980 : ALLOCATE (req_array(1:para_env_out%num_pe, 4))
742 :
743 132 : CALL timestop(handle2)
744 :
745 132 : CALL timeset(routineN//"_5_comm_buffer", handle2)
746 132 : IF (unit_nr > 0 .AND. mp2_env%bse%bse_debug_print) THEN
747 9 : WRITE (unit_nr, '(T2,A10,T13,A21)') 'BSE|DEBUG|', 'Communicating buffers'
748 : END IF
749 :
750 : ! communicate the buffer
751 : CALL communicate_buffer(para_env_out, num_entries_rec, num_entries_send, buffer_rec, &
752 132 : buffer_send, req_array)
753 :
754 132 : CALL timestop(handle2)
755 :
756 132 : CALL timeset(routineN//"_6_buffer_to_fmout"//fm_out%name, handle2)
757 132 : IF (unit_nr > 0 .AND. mp2_env%bse%bse_debug_print) THEN
758 9 : WRITE (unit_nr, '(T2,A10,T13,A24,A10)') 'BSE|DEBUG|', 'Writing from buffers to ', fm_out%name
759 : END IF
760 :
761 : ! fill fm_out with the entries from buffer_rec, i.e. buffer_rec are parts of fm_in
762 132 : nprocs = para_env_out%num_pe
763 :
764 : !$OMP PARALLEL DO DEFAULT(NONE) &
765 : !$OMP SHARED(fm_out, nprocs, num_entries_rec, buffer_rec) &
766 132 : !$OMP PRIVATE(iproc, i_entry_rec, ii, jj)
767 : DO iproc = 0, nprocs - 1
768 : DO i_entry_rec = 1, num_entries_rec(iproc)
769 : ii = fm_out%matrix_struct%g2l_row(buffer_rec(iproc)%indx(i_entry_rec, 1))
770 : jj = fm_out%matrix_struct%g2l_col(buffer_rec(iproc)%indx(i_entry_rec, 2))
771 :
772 : fm_out%local_data(ii, jj) = fm_out%local_data(ii, jj) + buffer_rec(iproc)%msg(i_entry_rec)
773 : END DO
774 : END DO
775 : !$OMP END PARALLEL DO
776 :
777 132 : CALL timestop(handle2)
778 :
779 132 : CALL timeset(routineN//"_7_cleanup", handle2)
780 132 : IF (unit_nr > 0 .AND. mp2_env%bse%bse_debug_print) THEN
781 9 : WRITE (unit_nr, '(T2,A10,T13,A41)') 'BSE|DEBUG|', 'Starting cleanup of communication buffers'
782 : END IF
783 :
784 : !Clean up all the arrays from the communication process
785 396 : DO iproc = 0, para_env_out%num_pe - 1
786 264 : DEALLOCATE (buffer_rec(iproc)%msg)
787 264 : DEALLOCATE (buffer_rec(iproc)%indx)
788 264 : DEALLOCATE (buffer_send(iproc)%msg)
789 396 : DEALLOCATE (buffer_send(iproc)%indx)
790 : END DO
791 660 : DEALLOCATE (buffer_rec, buffer_send)
792 132 : DEALLOCATE (req_array)
793 132 : DEALLOCATE (entry_counter)
794 132 : DEALLOCATE (num_entries_rec, num_entries_send)
795 :
796 132 : CALL timestop(handle2)
797 132 : CALL timestop(handle)
798 :
799 1056 : END SUBROUTINE truncate_fm
800 :
801 : ! **************************************************************************************************
802 : !> \brief ...
803 : !> \param fm_mat_S_bar_ia_bse ...
804 : !> \param fm_mat_S_bar_ij_bse ...
805 : !> \param fm_mat_S_trunc ...
806 : !> \param fm_mat_S_ij_trunc ...
807 : !> \param fm_mat_S_ab_trunc ...
808 : !> \param fm_mat_Q_static_bse_gemm ...
809 : !> \param mp2_env ...
810 : ! **************************************************************************************************
811 40 : SUBROUTINE deallocate_matrices_bse(fm_mat_S_bar_ia_bse, fm_mat_S_bar_ij_bse, &
812 : fm_mat_S_trunc, fm_mat_S_ij_trunc, fm_mat_S_ab_trunc, &
813 : fm_mat_Q_static_bse_gemm, mp2_env)
814 :
815 : TYPE(cp_fm_type), INTENT(INOUT) :: fm_mat_S_bar_ia_bse, fm_mat_S_bar_ij_bse, fm_mat_S_trunc, &
816 : fm_mat_S_ij_trunc, fm_mat_S_ab_trunc, fm_mat_Q_static_bse_gemm
817 : TYPE(mp2_type) :: mp2_env
818 :
819 : CHARACTER(LEN=*), PARAMETER :: routineN = 'deallocate_matrices_bse'
820 :
821 : INTEGER :: handle
822 :
823 40 : CALL timeset(routineN, handle)
824 :
825 40 : CALL cp_fm_release(fm_mat_S_bar_ia_bse)
826 40 : CALL cp_fm_release(fm_mat_S_bar_ij_bse)
827 40 : CALL cp_fm_release(fm_mat_S_trunc)
828 40 : CALL cp_fm_release(fm_mat_S_ij_trunc)
829 40 : CALL cp_fm_release(fm_mat_S_ab_trunc)
830 40 : CALL cp_fm_release(fm_mat_Q_static_bse_gemm)
831 40 : IF (mp2_env%bse%do_nto_analysis) THEN
832 4 : DEALLOCATE (mp2_env%bse%bse_nto_state_list_final)
833 : END IF
834 :
835 40 : CALL timestop(handle)
836 :
837 40 : END SUBROUTINE deallocate_matrices_bse
838 :
839 : ! **************************************************************************************************
840 : !> \brief Routine for computing the coefficients of the eigenvectors of the BSE matrix from a
841 : !> multiplication with the eigenvalues
842 : !> \param fm_work ...
843 : !> \param eig_vals ...
844 : !> \param beta ...
845 : !> \param gamma ...
846 : !> \param do_transpose ...
847 : ! **************************************************************************************************
848 88 : SUBROUTINE comp_eigvec_coeff_BSE(fm_work, eig_vals, beta, gamma, do_transpose)
849 :
850 : TYPE(cp_fm_type), INTENT(INOUT) :: fm_work
851 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:), &
852 : INTENT(IN) :: eig_vals
853 : REAL(KIND=dp), INTENT(IN) :: beta
854 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: gamma
855 : LOGICAL, INTENT(IN), OPTIONAL :: do_transpose
856 :
857 : CHARACTER(LEN=*), PARAMETER :: routineN = 'comp_eigvec_coeff_BSE'
858 :
859 : INTEGER :: handle, i_row_global, ii, j_col_global, &
860 : jj, ncol_local, nrow_local
861 44 : INTEGER, DIMENSION(:), POINTER :: col_indices, row_indices
862 : LOGICAL :: my_do_transpose
863 : REAL(KIND=dp) :: coeff, my_gamma
864 :
865 44 : CALL timeset(routineN, handle)
866 :
867 44 : IF (PRESENT(gamma)) THEN
868 44 : my_gamma = gamma
869 : ELSE
870 : my_gamma = 2.0_dp
871 : END IF
872 :
873 44 : IF (PRESENT(do_transpose)) THEN
874 44 : my_do_transpose = do_transpose
875 : ELSE
876 : my_do_transpose = .FALSE.
877 : END IF
878 :
879 : CALL cp_fm_get_info(matrix=fm_work, &
880 : nrow_local=nrow_local, &
881 : ncol_local=ncol_local, &
882 : row_indices=row_indices, &
883 44 : col_indices=col_indices)
884 :
885 44 : IF (my_do_transpose) THEN
886 3576 : DO jj = 1, ncol_local
887 3532 : j_col_global = col_indices(jj)
888 1531936 : DO ii = 1, nrow_local
889 1528360 : coeff = (eig_vals(j_col_global)**beta)/my_gamma
890 1531892 : fm_work%local_data(ii, jj) = fm_work%local_data(ii, jj)*coeff
891 : END DO
892 : END DO
893 : ELSE
894 0 : DO jj = 1, ncol_local
895 0 : DO ii = 1, nrow_local
896 0 : i_row_global = row_indices(ii)
897 0 : coeff = (eig_vals(i_row_global)**beta)/my_gamma
898 0 : fm_work%local_data(ii, jj) = fm_work%local_data(ii, jj)*coeff
899 : END DO
900 : END DO
901 : END IF
902 :
903 44 : CALL timestop(handle)
904 :
905 44 : END SUBROUTINE comp_eigvec_coeff_BSE
906 :
907 : ! **************************************************************************************************
908 : !> \brief Sorts excitation entries by ascending primary index, reordering the secondary index,
909 : !> the eigenvector coefficients and - open shell - the spin index alongside
910 : !> \param idx_prim Primary index of each entry; sorted in place and used as the sort key
911 : !> \param idx_sec Secondary index of each entry, reordered to follow idx_prim
912 : !> \param eigvec_entries Eigenvector coefficients of each entry, reordered to follow idx_prim
913 : !> \param idx_spin Optional spin index of each entry (open shell), reordered to follow idx_prim
914 : ! **************************************************************************************************
915 1414 : SUBROUTINE sort_excitations(idx_prim, idx_sec, eigvec_entries, idx_spin)
916 :
917 : INTEGER, ALLOCATABLE, DIMENSION(:) :: idx_prim, idx_sec
918 : REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: eigvec_entries
919 : INTEGER, ALLOCATABLE, DIMENSION(:), OPTIONAL :: idx_spin
920 :
921 : CHARACTER(LEN=*), PARAMETER :: routineN = 'sort_excitations'
922 :
923 : INTEGER :: handle, ii, kk, num_entries, num_mults
924 1414 : INTEGER, ALLOCATABLE, DIMENSION(:) :: idx_prim_work, idx_sec_work, &
925 1414 : idx_spin_work, tmp_index
926 : LOGICAL :: unique_entries
927 1414 : REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: eigvec_entries_work
928 :
929 1414 : CALL timeset(routineN, handle)
930 :
931 1414 : num_entries = SIZE(idx_prim)
932 :
933 3726 : ALLOCATE (tmp_index(num_entries))
934 :
935 1414 : CALL sort(idx_prim, num_entries, tmp_index)
936 :
937 2312 : ALLOCATE (idx_sec_work(num_entries))
938 3726 : ALLOCATE (eigvec_entries_work(num_entries))
939 1528 : IF (PRESENT(idx_spin)) ALLOCATE (idx_spin_work(num_entries))
940 :
941 3970 : DO ii = 1, num_entries
942 2556 : idx_sec_work(ii) = idx_sec(tmp_index(ii))
943 2556 : eigvec_entries_work(ii) = eigvec_entries(tmp_index(ii))
944 3970 : IF (PRESENT(idx_spin)) idx_spin_work(ii) = idx_spin(tmp_index(ii))
945 : END DO
946 :
947 1414 : DEALLOCATE (tmp_index)
948 1414 : DEALLOCATE (idx_sec)
949 1414 : DEALLOCATE (eigvec_entries)
950 :
951 1414 : CALL MOVE_ALLOC(idx_sec_work, idx_sec)
952 1414 : CALL MOVE_ALLOC(eigvec_entries_work, eigvec_entries)
953 1414 : IF (PRESENT(idx_spin)) THEN
954 168 : DEALLOCATE (idx_spin)
955 168 : CALL MOVE_ALLOC(idx_spin_work, idx_spin)
956 : END IF
957 :
958 : !Now check for multiple entries in first idx to check necessity of sorting in second idx
959 1414 : CALL sort_unique(idx_prim, unique_entries)
960 1414 : IF (.NOT. unique_entries) THEN
961 768 : ALLOCATE (idx_prim_work(num_entries))
962 2048 : idx_prim_work(:) = idx_prim(:)
963 : ! Find duplicate entries in idx_prim
964 2048 : DO ii = 1, num_entries
965 1664 : IF (idx_prim_work(ii) == 0) CYCLE
966 6032 : num_mults = COUNT(idx_prim_work == idx_prim_work(ii))
967 990 : IF (num_mults > 1) THEN
968 : !Set all duplicate entries to 0
969 1714 : idx_prim_work(ii:ii + num_mults - 1) = 0
970 : !Start sorting in secondary index
971 1560 : ALLOCATE (idx_sec_work(num_mults))
972 1560 : ALLOCATE (eigvec_entries_work(num_mults))
973 1714 : idx_sec_work(:) = idx_sec(ii:ii + num_mults - 1)
974 1714 : eigvec_entries_work(:) = eigvec_entries(ii:ii + num_mults - 1)
975 520 : IF (PRESENT(idx_spin)) THEN
976 188 : ALLOCATE (idx_spin_work(num_mults))
977 332 : idx_spin_work(:) = idx_spin(ii:ii + num_mults - 1)
978 : END IF
979 1040 : ALLOCATE (tmp_index(num_mults))
980 520 : CALL sort(idx_sec_work, num_mults, tmp_index)
981 :
982 : !Now write newly sorted indices to original arrays
983 1714 : DO kk = ii, ii + num_mults - 1
984 1194 : idx_sec(kk) = idx_sec_work(kk - ii + 1)
985 1194 : eigvec_entries(kk) = eigvec_entries_work(tmp_index(kk - ii + 1))
986 1714 : IF (PRESENT(idx_spin)) idx_spin(kk) = idx_spin_work(tmp_index(kk - ii + 1))
987 : END DO
988 : !Deallocate work arrays
989 520 : DEALLOCATE (tmp_index)
990 520 : DEALLOCATE (idx_sec_work)
991 520 : DEALLOCATE (eigvec_entries_work)
992 520 : IF (PRESENT(idx_spin)) DEALLOCATE (idx_spin_work)
993 : END IF
994 2048 : idx_prim_work(ii) = idx_prim(ii)
995 : END DO
996 384 : DEALLOCATE (idx_prim_work)
997 : END IF
998 :
999 1414 : CALL timestop(handle)
1000 :
1001 4242 : END SUBROUTINE sort_excitations
1002 :
1003 : ! **************************************************************************************************
1004 : !> \brief Roughly estimates the needed runtime and memory during the BSE run
1005 : !> \param n_ov_joint ...
1006 : !> \param unit_nr ...
1007 : !> \param bse_abba ...
1008 : !> \param para_env ...
1009 : !> \param diag_runtime_est ...
1010 : ! **************************************************************************************************
1011 42 : SUBROUTINE estimate_BSE_resources(n_ov_joint, unit_nr, bse_abba, &
1012 : para_env, diag_runtime_est)
1013 :
1014 : INTEGER, INTENT(IN) :: n_ov_joint, unit_nr
1015 : LOGICAL :: bse_abba
1016 : TYPE(mp_para_env_type), POINTER :: para_env
1017 : REAL(KIND=dp) :: diag_runtime_est
1018 :
1019 : CHARACTER(LEN=*), PARAMETER :: routineN = 'estimate_BSE_resources'
1020 :
1021 : INTEGER :: handle, num_BSE_matrices
1022 : INTEGER(KIND=int_8) :: full_dim
1023 : REAL(KIND=dp) :: mem_est, mem_est_per_rank
1024 :
1025 42 : CALL timeset(routineN, handle)
1026 :
1027 : ! Number of matrices with size of A in TDA is 2 (A itself and W_ijab)
1028 42 : num_BSE_matrices = 2
1029 : ! With the full diagonalization of ABBA, we need several auxiliary matrices in the process
1030 : ! The maximum number is 2 + 2 + 6 (additional B and C matrix as well as 6 matrices to create C)
1031 42 : IF (bse_abba) THEN
1032 22 : num_BSE_matrices = 10
1033 : END IF
1034 :
1035 42 : full_dim = INT(n_ov_joint, KIND=int_8)**2*INT(num_BSE_matrices, KIND=int_8)
1036 42 : mem_est = REAL(8*full_dim, KIND=dp)/REAL(1024**3, KIND=dp)
1037 42 : mem_est_per_rank = REAL(mem_est/para_env%num_pe, KIND=dp)
1038 :
1039 42 : IF (unit_nr > 0) THEN
1040 : ! WRITE (unit_nr, '(T2,A4,T7,A40,T68,F13.3)') 'BSE|', 'Total peak memory estimate from BSE [GB]', &
1041 : ! mem_est
1042 21 : WRITE (unit_nr, '(T2,A4,T7,A40,T68,ES13.3)') 'BSE|', 'Total peak memory estimate from BSE [GB]', &
1043 42 : mem_est
1044 21 : WRITE (unit_nr, '(T2,A4,T7,A47,T68,F13.3)') 'BSE|', 'Peak memory estimate per MPI rank from BSE [GB]', &
1045 42 : mem_est_per_rank
1046 21 : WRITE (unit_nr, '(T2,A4)') 'BSE|'
1047 : END IF
1048 : ! Rough estimation of diagonalization runtimes. Baseline was a full BSE Naphthalene
1049 : ! run with 11000x11000 entries in A/B/C, which took 10s on 32 ranks
1050 : diag_runtime_est = REAL(INT(n_ov_joint, KIND=int_8)/11000_int_8, KIND=dp)**3* &
1051 42 : 10*32/REAL(para_env%num_pe, KIND=dp)
1052 :
1053 42 : CALL timestop(handle)
1054 :
1055 42 : END SUBROUTINE estimate_BSE_resources
1056 :
1057 : ! **************************************************************************************************
1058 : !> \brief Filters eigenvector entries above a given threshold to describe excitations in the
1059 : !> singleparticle basis
1060 : !> \param fm_eigvec ...
1061 : !> \param idx_homo ...
1062 : !> \param idx_virt ...
1063 : !> \param eigvec_entries ...
1064 : !> \param i_exc ...
1065 : !> \param virtual ...
1066 : !> \param num_entries ...
1067 : !> \param mp2_env ...
1068 : !> \param offsets ...
1069 : !> \param virtual_per_spin ...
1070 : !> \param idx_spin ...
1071 : ! **************************************************************************************************
1072 1414 : SUBROUTINE filter_eigvec_contrib(fm_eigvec, idx_homo, idx_virt, eigvec_entries, &
1073 : i_exc, virtual, num_entries, mp2_env, &
1074 1414 : offsets, virtual_per_spin, idx_spin)
1075 :
1076 : TYPE(cp_fm_type), INTENT(IN) :: fm_eigvec
1077 : INTEGER, ALLOCATABLE, DIMENSION(:) :: idx_homo, idx_virt
1078 : REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: eigvec_entries
1079 : INTEGER :: i_exc, virtual, num_entries
1080 : TYPE(mp2_type), INTENT(INOUT) :: mp2_env
1081 : INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL :: offsets, virtual_per_spin
1082 : INTEGER, ALLOCATABLE, DIMENSION(:), OPTIONAL :: idx_spin
1083 :
1084 : CHARACTER(LEN=*), PARAMETER :: routineN = 'filter_eigvec_contrib'
1085 :
1086 : INTEGER :: eigvec_idx, handle, ii, iproc, isp, jj, &
1087 : kk, ksp, ncol_local, nrow_local, &
1088 : num_entries_local, r_local, v_local
1089 : INTEGER, ALLOCATABLE, DIMENSION(:) :: num_entries_to_comm
1090 1414 : INTEGER, DIMENSION(:), POINTER :: col_indices, row_indices
1091 : REAL(KIND=dp) :: eigvec_entry
1092 : TYPE(integ_mat_buffer_type), ALLOCATABLE, &
1093 1414 : DIMENSION(:) :: buffer_entries
1094 : TYPE(mp_para_env_type), POINTER :: para_env
1095 :
1096 1414 : CALL timeset(routineN, handle)
1097 :
1098 1414 : para_env => fm_eigvec%matrix_struct%para_env
1099 :
1100 : CALL cp_fm_get_info(matrix=fm_eigvec, &
1101 : nrow_local=nrow_local, &
1102 : ncol_local=ncol_local, &
1103 : row_indices=row_indices, &
1104 1414 : col_indices=col_indices)
1105 :
1106 4242 : ALLOCATE (num_entries_to_comm(0:para_env%num_pe - 1))
1107 1414 : num_entries_to_comm(:) = 0
1108 :
1109 83720 : DO jj = 1, ncol_local
1110 : !First check if i is localized on this proc
1111 82306 : IF (col_indices(jj) /= i_exc) THEN
1112 : CYCLE
1113 : END IF
1114 54976 : DO ii = 1, nrow_local
1115 52172 : eigvec_idx = row_indices(ii)
1116 52172 : eigvec_entry = fm_eigvec%local_data(ii, jj)
1117 134478 : IF (ABS(eigvec_entry) > mp2_env%bse%eps_x) THEN
1118 1278 : num_entries_to_comm(para_env%mepos) = num_entries_to_comm(para_env%mepos) + 1
1119 : END IF
1120 : END DO
1121 : END DO
1122 :
1123 : !Gather number of entries of other processes
1124 1414 : CALL para_env%sum(num_entries_to_comm)
1125 :
1126 1414 : num_entries_local = num_entries_to_comm(para_env%mepos)
1127 :
1128 7070 : ALLOCATE (buffer_entries(0:para_env%num_pe - 1))
1129 :
1130 4242 : DO iproc = 0, para_env%num_pe - 1
1131 7106 : ALLOCATE (buffer_entries(iproc)%msg(num_entries_to_comm(iproc)))
1132 7106 : ALLOCATE (buffer_entries(iproc)%indx(num_entries_to_comm(iproc), 3))
1133 5384 : buffer_entries(iproc)%msg = 0.0_dp
1134 20394 : buffer_entries(iproc)%indx = 0
1135 : END DO
1136 :
1137 : kk = 1
1138 83720 : DO jj = 1, ncol_local
1139 : !First check if i is localized on this proc
1140 82306 : IF (col_indices(jj) /= i_exc) THEN
1141 : CYCLE
1142 : END IF
1143 54976 : DO ii = 1, nrow_local
1144 52172 : eigvec_idx = row_indices(ii)
1145 52172 : eigvec_entry = fm_eigvec%local_data(ii, jj)
1146 134478 : IF (ABS(eigvec_entry) > mp2_env%bse%eps_x) THEN
1147 : ! Decode spin block from the joint row index (blocks are contiguous; sigma is the
1148 : ! largest offset strictly below eigvec_idx). offsets absent -> closed shell, sigma=1.
1149 1278 : isp = 1
1150 1278 : r_local = eigvec_idx
1151 1278 : v_local = virtual
1152 1278 : IF (PRESENT(offsets)) THEN
1153 213 : DO ksp = SIZE(offsets), 1, -1
1154 213 : IF (eigvec_idx > offsets(ksp)) THEN
1155 : isp = ksp
1156 : EXIT
1157 : END IF
1158 : END DO
1159 146 : r_local = eigvec_idx - offsets(isp)
1160 146 : v_local = virtual_per_spin(isp)
1161 : END IF
1162 1278 : buffer_entries(para_env%mepos)%indx(kk, 1) = (r_local - 1)/v_local + 1
1163 1278 : buffer_entries(para_env%mepos)%indx(kk, 2) = MOD(r_local - 1, v_local) + 1
1164 1278 : buffer_entries(para_env%mepos)%indx(kk, 3) = isp
1165 1278 : buffer_entries(para_env%mepos)%msg(kk) = eigvec_entry
1166 1278 : kk = kk + 1
1167 : END IF
1168 : END DO
1169 : END DO
1170 :
1171 4242 : DO iproc = 0, para_env%num_pe - 1
1172 2828 : CALL para_env%sum(buffer_entries(iproc)%msg)
1173 4242 : CALL para_env%sum(buffer_entries(iproc)%indx)
1174 : END DO
1175 :
1176 : !Now sum up gathered information
1177 4242 : num_entries = SUM(num_entries_to_comm)
1178 3726 : ALLOCATE (idx_homo(num_entries))
1179 2312 : ALLOCATE (idx_virt(num_entries))
1180 3726 : ALLOCATE (eigvec_entries(num_entries))
1181 1528 : IF (PRESENT(idx_spin)) ALLOCATE (idx_spin(num_entries))
1182 :
1183 1414 : kk = 1
1184 4242 : DO iproc = 0, para_env%num_pe - 1
1185 4242 : IF (num_entries_to_comm(iproc) /= 0) THEN
1186 4006 : DO ii = 1, num_entries_to_comm(iproc)
1187 2556 : idx_homo(kk) = buffer_entries(iproc)%indx(ii, 1)
1188 2556 : idx_virt(kk) = buffer_entries(iproc)%indx(ii, 2)
1189 2556 : IF (PRESENT(idx_spin)) idx_spin(kk) = buffer_entries(iproc)%indx(ii, 3)
1190 2556 : eigvec_entries(kk) = buffer_entries(iproc)%msg(ii)
1191 4006 : kk = kk + 1
1192 : END DO
1193 : END IF
1194 : END DO
1195 :
1196 : !Deallocate all the used arrays
1197 4242 : DO iproc = 0, para_env%num_pe - 1
1198 2828 : DEALLOCATE (buffer_entries(iproc)%msg)
1199 4242 : DEALLOCATE (buffer_entries(iproc)%indx)
1200 : END DO
1201 5656 : DEALLOCATE (buffer_entries)
1202 1414 : DEALLOCATE (num_entries_to_comm)
1203 1414 : NULLIFY (row_indices)
1204 1414 : NULLIFY (col_indices)
1205 :
1206 : !Now sort the results according to the involved singleparticle orbitals
1207 : ! (homo first, then virtual). idx_spin is payload, permuted alongside the entries.
1208 1414 : IF (PRESENT(idx_spin)) THEN
1209 168 : CALL sort_excitations(idx_homo, idx_virt, eigvec_entries, idx_spin)
1210 : ELSE
1211 1246 : CALL sort_excitations(idx_homo, idx_virt, eigvec_entries)
1212 : END IF
1213 :
1214 1414 : CALL timestop(handle)
1215 :
1216 1414 : END SUBROUTINE filter_eigvec_contrib
1217 :
1218 : ! **************************************************************************************************
1219 : !> \brief Spin-block layout for the open-shell (joint) BSE matrix: per-spin OV-pair counts and the
1220 : !> block offsets into the joint matrix of dimension n_ov_joint = sum_sigma homo*virtual.
1221 : !> \param homo_red per-spin (reduced) number of occupied levels
1222 : !> \param virt_red per-spin (reduced) number of virtual levels
1223 : !> \param n_ov per-spin OV-pair count (OUT)
1224 : !> \param offsets per-spin block offset into the joint matrix (OUT)
1225 : !> \param n_ov_joint total joint dimension (OUT)
1226 : ! **************************************************************************************************
1227 126 : SUBROUTINE get_bse_spin_block_layout(homo_red, virt_red, n_ov, offsets, n_ov_joint)
1228 : INTEGER, DIMENSION(:), INTENT(IN) :: homo_red, virt_red
1229 : INTEGER, DIMENSION(:), INTENT(OUT) :: n_ov, offsets
1230 : INTEGER, INTENT(OUT) :: n_ov_joint
1231 :
1232 : INTEGER :: isp
1233 :
1234 126 : n_ov_joint = 0
1235 288 : DO isp = 1, SIZE(homo_red)
1236 162 : offsets(isp) = n_ov_joint
1237 162 : n_ov(isp) = homo_red(isp)*virt_red(isp)
1238 288 : n_ov_joint = n_ov_joint + n_ov(isp)
1239 : END DO
1240 :
1241 126 : END SUBROUTINE get_bse_spin_block_layout
1242 :
1243 : ! **************************************************************************************************
1244 : !> \brief Determines indices within the given energy cutoffs and truncates Eigenvalues and matrices
1245 : !> \param fm_mat_S_ia_bse ...
1246 : !> \param fm_mat_S_ij_bse ...
1247 : !> \param fm_mat_S_ab_bse ...
1248 : !> \param fm_mat_S_trunc ...
1249 : !> \param fm_mat_S_ij_trunc ...
1250 : !> \param fm_mat_S_ab_trunc ...
1251 : !> \param Eigenval_scf ...
1252 : !> \param Eigenval ...
1253 : !> \param Eigenval_reduced ...
1254 : !> \param homo ...
1255 : !> \param virtual ...
1256 : !> \param dimen_RI ...
1257 : !> \param unit_nr ...
1258 : !> \param bse_lev_virt ...
1259 : !> \param homo_red ...
1260 : !> \param virt_red ...
1261 : !> \param mp2_env ...
1262 : !> \param window Absolute MO window to retain.
1263 : !> \param print_window Whether to print the cutoff summary.
1264 : ! **************************************************************************************************
1265 112 : SUBROUTINE truncate_BSE_matrices(fm_mat_S_ia_bse, fm_mat_S_ij_bse, fm_mat_S_ab_bse, &
1266 : fm_mat_S_trunc, fm_mat_S_ij_trunc, fm_mat_S_ab_trunc, &
1267 56 : Eigenval_scf, Eigenval, Eigenval_reduced, &
1268 : homo, virtual, dimen_RI, unit_nr, &
1269 : bse_lev_virt, &
1270 : homo_red, virt_red, &
1271 : mp2_env, &
1272 : window, print_window)
1273 :
1274 : TYPE(cp_fm_type), INTENT(IN) :: fm_mat_S_ia_bse, fm_mat_S_ij_bse, &
1275 : fm_mat_S_ab_bse
1276 : TYPE(cp_fm_type), INTENT(INOUT) :: fm_mat_S_trunc, fm_mat_S_ij_trunc, &
1277 : fm_mat_S_ab_trunc
1278 : REAL(KIND=dp), DIMENSION(:) :: Eigenval_scf, Eigenval
1279 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: Eigenval_reduced
1280 : INTEGER, INTENT(IN) :: homo, virtual, dimen_RI, unit_nr, &
1281 : bse_lev_virt
1282 : INTEGER, INTENT(OUT) :: homo_red, virt_red
1283 : TYPE(mp2_type), INTENT(INOUT) :: mp2_env
1284 : TYPE(mo_window_type), INTENT(IN) :: window
1285 : LOGICAL, INTENT(IN) :: print_window
1286 :
1287 : CHARACTER(LEN=*), PARAMETER :: routineN = 'truncate_BSE_matrices'
1288 :
1289 : INTEGER :: handle, homo_incl, virt_incl
1290 : TYPE(cp_blacs_env_type), POINTER :: context
1291 : TYPE(cp_fm_struct_type), POINTER :: fm_struct_ab, fm_struct_ia, fm_struct_ij
1292 : TYPE(mp_para_env_type), POINTER :: para_env
1293 :
1294 56 : CALL timeset(routineN, handle)
1295 :
1296 56 : homo_incl = window%first_mo
1297 56 : virt_incl = window%last_mo - homo
1298 56 : homo_red = homo - homo_incl + 1
1299 56 : virt_red = virt_incl
1300 :
1301 56 : IF (print_window .AND. unit_nr > 0) THEN
1302 20 : IF (mp2_env%bse%bse_cutoff_occ > 0) THEN
1303 17 : WRITE (unit_nr, '(T2,A4,T7,A29,T71,F10.3)') 'BSE|', 'Cutoff occupied orbitals [eV]', &
1304 34 : mp2_env%bse%bse_cutoff_occ*evolt
1305 : ELSE
1306 3 : WRITE (unit_nr, '(T2,A4,T7,A37)') 'BSE|', 'No cutoff given for occupied orbitals'
1307 : END IF
1308 20 : IF (mp2_env%bse%bse_cutoff_empty > 0) THEN
1309 18 : WRITE (unit_nr, '(T2,A4,T7,A26,T71,F10.3)') 'BSE|', 'Cutoff empty orbitals [eV]', &
1310 36 : mp2_env%bse%bse_cutoff_empty*evolt
1311 : ELSE
1312 2 : WRITE (unit_nr, '(T2,A4,T7,A34)') 'BSE|', 'No cutoff given for empty orbitals'
1313 : END IF
1314 20 : WRITE (unit_nr, '(T2,A4,T7,A20,T71,I10)') 'BSE|', 'First occupied index', homo_incl
1315 20 : WRITE (unit_nr, '(T2,A4,T7,A32,T71,I10)') 'BSE|', 'Last empty index (not MO index!)', virt_incl
1316 20 : WRITE (unit_nr, '(T2,A4,T7,A35,T71,F10.3)') 'BSE|', 'Energy of first occupied index [eV]', &
1317 40 : Eigenval(homo_incl)*evolt
1318 20 : WRITE (unit_nr, '(T2,A4,T7,A31,T71,F10.3)') 'BSE|', 'Energy of last empty index [eV]', &
1319 40 : Eigenval(homo + virt_incl)*evolt
1320 20 : WRITE (unit_nr, '(T2,A4,T7,A54,T71,F10.3)') 'BSE|', &
1321 20 : 'Energy difference of first occupied index to HOMO [eV]', &
1322 40 : -(Eigenval(homo_incl) - Eigenval(homo))*evolt
1323 20 : WRITE (unit_nr, '(T2,A4,T7,A50,T71,F10.3)') 'BSE|', &
1324 20 : 'Energy difference of last empty index to LUMO [eV]', &
1325 40 : (Eigenval(homo + virt_incl) - Eigenval(homo + 1))*evolt
1326 20 : WRITE (unit_nr, '(T2,A4,T7,A35,T71,I10)') 'BSE|', 'Number of GW-corrected occupied MOs', &
1327 40 : mp2_env%ri_g0w0%corr_mos_occ
1328 20 : WRITE (unit_nr, '(T2,A4,T7,A32,T71,I10)') 'BSE|', 'Number of GW-corrected empty MOs', &
1329 40 : bse_lev_virt
1330 20 : WRITE (unit_nr, '(T2,A4)') 'BSE|'
1331 : END IF
1332 56 : IF (unit_nr > 0) THEN
1333 28 : IF (homo - homo_incl + 1 > mp2_env%ri_g0w0%corr_mos_occ) THEN
1334 0 : CPABORT("Number of GW-corrected occupied MOs too small for chosen BSE cutoff")
1335 : END IF
1336 28 : IF (virt_incl > bse_lev_virt) THEN
1337 0 : CPABORT("Number of GW-corrected virtual MOs too small for chosen BSE cutoff")
1338 : END IF
1339 : END IF
1340 : !Truncate full fm_S matrices
1341 : !Allocate new truncated matrices of proper size
1342 56 : para_env => fm_mat_S_ia_bse%matrix_struct%para_env
1343 56 : context => fm_mat_S_ia_bse%matrix_struct%context
1344 :
1345 56 : CALL cp_fm_struct_create(fm_struct_ia, para_env, context, dimen_RI, homo_red*virt_red)
1346 56 : CALL cp_fm_struct_create(fm_struct_ij, para_env, context, dimen_RI, homo_red*homo_red)
1347 56 : CALL cp_fm_struct_create(fm_struct_ab, para_env, context, dimen_RI, virt_red*virt_red)
1348 :
1349 56 : CALL cp_fm_create(fm_mat_S_trunc, fm_struct_ia, name="fm_S_trunc", set_zero=.TRUE.)
1350 56 : CALL cp_fm_create(fm_mat_S_ij_trunc, fm_struct_ij, name="fm_S_ij_trunc", set_zero=.TRUE.)
1351 56 : CALL cp_fm_create(fm_mat_S_ab_trunc, fm_struct_ab, name="fm_S_ab_trunc", set_zero=.TRUE.)
1352 :
1353 : !Copy parts of original matrices to truncated ones
1354 56 : IF (mp2_env%bse%bse_cutoff_occ > 0 .OR. mp2_env%bse%bse_cutoff_empty > 0) THEN
1355 : !Truncate eigenvals
1356 132 : ALLOCATE (Eigenval_reduced(homo_red + virt_red))
1357 : ! Include USE_KS_ENERGIES input
1358 44 : IF (mp2_env%bse%use_ks_energies) THEN
1359 48 : Eigenval_reduced(:) = Eigenval_scf(homo_incl:homo + virt_incl)
1360 : ELSE
1361 646 : Eigenval_reduced(:) = Eigenval(homo_incl:homo + virt_incl)
1362 : END IF
1363 :
1364 : CALL truncate_fm(fm_mat_S_trunc, fm_mat_S_ia_bse, virtual, &
1365 : homo_red, virt_red, unit_nr, mp2_env, &
1366 44 : nrow_offset=homo_incl)
1367 : CALL truncate_fm(fm_mat_S_ij_trunc, fm_mat_S_ij_bse, homo, &
1368 : homo_red, homo_red, unit_nr, mp2_env, &
1369 44 : homo_incl, homo_incl)
1370 : CALL truncate_fm(fm_mat_S_ab_trunc, fm_mat_S_ab_bse, bse_lev_virt, &
1371 44 : virt_red, virt_red, unit_nr, mp2_env)
1372 :
1373 : ELSE
1374 12 : IF (unit_nr > 0) THEN
1375 6 : WRITE (unit_nr, '(T2,A4,T7,A37)') 'BSE|', 'No truncation of BSE matrices applied'
1376 6 : WRITE (unit_nr, '(T2,A4)') 'BSE|'
1377 : END IF
1378 36 : ALLOCATE (Eigenval_reduced(homo_red + virt_red))
1379 : ! Include USE_KS_ENERGIES input
1380 12 : IF (mp2_env%bse%use_ks_energies) THEN
1381 0 : Eigenval_reduced(:) = Eigenval_scf(:)
1382 : ELSE
1383 600 : Eigenval_reduced(:) = Eigenval(:)
1384 : END IF
1385 : CALL cp_fm_to_fm_submat_general(fm_mat_S_ia_bse, fm_mat_S_trunc, dimen_RI, homo_red*virt_red, &
1386 12 : 1, 1, 1, 1, context)
1387 : CALL cp_fm_to_fm_submat_general(fm_mat_S_ij_bse, fm_mat_S_ij_trunc, dimen_RI, homo_red*homo_red, &
1388 12 : 1, 1, 1, 1, context)
1389 : CALL cp_fm_to_fm_submat_general(fm_mat_S_ab_bse, fm_mat_S_ab_trunc, dimen_RI, virt_red*virt_red, &
1390 12 : 1, 1, 1, 1, context)
1391 : END IF
1392 :
1393 56 : CALL cp_fm_struct_release(fm_struct_ia)
1394 56 : CALL cp_fm_struct_release(fm_struct_ij)
1395 56 : CALL cp_fm_struct_release(fm_struct_ab)
1396 :
1397 56 : NULLIFY (para_env)
1398 56 : NULLIFY (context)
1399 :
1400 56 : CALL timestop(handle)
1401 :
1402 56 : END SUBROUTINE truncate_BSE_matrices
1403 :
1404 : ! **************************************************************************************************
1405 : !> \brief ...
1406 : !> \param fm_eigvec ...
1407 : !> \param fm_eigvec_reshuffled ...
1408 : !> \param homo ...
1409 : !> \param virtual ...
1410 : !> \param n_exc ...
1411 : !> \param do_transpose ...
1412 : !> \param unit_nr ...
1413 : !> \param mp2_env ...
1414 : ! **************************************************************************************************
1415 900 : SUBROUTINE reshuffle_eigvec(fm_eigvec, fm_eigvec_reshuffled, homo, virtual, n_exc, do_transpose, &
1416 : unit_nr, mp2_env)
1417 :
1418 : TYPE(cp_fm_type), INTENT(IN) :: fm_eigvec
1419 : TYPE(cp_fm_type), INTENT(INOUT) :: fm_eigvec_reshuffled
1420 : INTEGER, INTENT(IN) :: homo, virtual, n_exc
1421 : LOGICAL, INTENT(IN) :: do_transpose
1422 : INTEGER, INTENT(IN) :: unit_nr
1423 : TYPE(mp2_type), INTENT(INOUT) :: mp2_env
1424 :
1425 : CHARACTER(LEN=*), PARAMETER :: routineN = 'reshuffle_eigvec'
1426 :
1427 : INTEGER :: handle, my_m_col, my_n_row
1428 : INTEGER, DIMENSION(4) :: reordering
1429 : TYPE(cp_fm_struct_type), POINTER :: fm_struct_eigvec_col, &
1430 : fm_struct_eigvec_reshuffled
1431 : TYPE(cp_fm_type) :: fm_eigvec_col
1432 :
1433 300 : CALL timeset(routineN, handle)
1434 :
1435 : ! Define reordering:
1436 : ! (ia,11) to (a1,i1) for transposition
1437 : ! (ia,11) to (i1,a1) for default
1438 300 : IF (do_transpose) THEN
1439 50 : reordering = [2, 3, 1, 4]
1440 50 : my_n_row = virtual
1441 50 : my_m_col = homo
1442 : ELSE
1443 250 : reordering = [1, 3, 2, 4]
1444 250 : my_n_row = homo
1445 250 : my_m_col = virtual
1446 : END IF
1447 :
1448 : CALL cp_fm_struct_create(fm_struct_eigvec_col, &
1449 : fm_eigvec%matrix_struct%para_env, fm_eigvec%matrix_struct%context, &
1450 300 : homo*virtual, 1)
1451 : CALL cp_fm_struct_create(fm_struct_eigvec_reshuffled, &
1452 : fm_eigvec%matrix_struct%para_env, fm_eigvec%matrix_struct%context, &
1453 300 : my_n_row, my_m_col)
1454 :
1455 : ! Resort indices
1456 300 : CALL cp_fm_create(fm_eigvec_col, fm_struct_eigvec_col, name="BSE_column_vector")
1457 300 : CALL cp_fm_set_all(fm_eigvec_col, 0.0_dp)
1458 300 : CALL cp_fm_create(fm_eigvec_reshuffled, fm_struct_eigvec_reshuffled, name="BSE_reshuffled_eigenvector")
1459 300 : CALL cp_fm_set_all(fm_eigvec_reshuffled, 0.0_dp)
1460 : ! Fill matrix
1461 : CALL cp_fm_to_fm_submat(fm_eigvec, fm_eigvec_col, &
1462 : homo*virtual, 1, &
1463 : 1, n_exc, &
1464 300 : 1, 1)
1465 : ! Reshuffle
1466 : CALL fm_general_add_bse(fm_eigvec_reshuffled, fm_eigvec_col, 1.0_dp, &
1467 : virtual, 1, &
1468 : 1, 1, &
1469 300 : unit_nr, reordering, mp2_env)
1470 :
1471 300 : CALL cp_fm_release(fm_eigvec_col)
1472 300 : CALL cp_fm_struct_release(fm_struct_eigvec_col)
1473 300 : CALL cp_fm_struct_release(fm_struct_eigvec_reshuffled)
1474 :
1475 300 : CALL timestop(handle)
1476 :
1477 300 : END SUBROUTINE reshuffle_eigvec
1478 :
1479 : ! **************************************************************************************************
1480 : !> \brief Borrowed from the tddfpt module with slight adaptions
1481 : !> \param qs_env ...
1482 : !> \param mos ...
1483 : !> \param istate ...
1484 : !> \param info_approximation ...
1485 : !> \param stride ...
1486 : !> \param append_cube ...
1487 : !> \param print_section ...
1488 : ! **************************************************************************************************
1489 0 : SUBROUTINE print_bse_nto_cubes(qs_env, mos, istate, info_approximation, &
1490 : stride, append_cube, print_section)
1491 :
1492 : TYPE(qs_environment_type), POINTER :: qs_env
1493 : TYPE(mo_set_type), DIMENSION(:), INTENT(IN) :: mos
1494 : INTEGER, INTENT(IN) :: istate
1495 : CHARACTER(LEN=10) :: info_approximation
1496 : INTEGER, DIMENSION(:), POINTER :: stride
1497 : LOGICAL, INTENT(IN) :: append_cube
1498 : TYPE(section_vals_type), POINTER :: print_section
1499 :
1500 : CHARACTER(LEN=*), PARAMETER :: routineN = 'print_bse_nto_cubes'
1501 :
1502 : CHARACTER(LEN=default_path_length) :: filename, info_approx_trunc, &
1503 : my_pos_cube, title
1504 : INTEGER :: handle, i, iset, nmo, unit_nr_cube
1505 : LOGICAL :: mpi_io
1506 0 : TYPE(atomic_kind_type), DIMENSION(:), POINTER :: atomic_kind_set
1507 : TYPE(cell_type), POINTER :: cell
1508 : TYPE(cp_fm_type), POINTER :: mo_coeff
1509 : TYPE(cp_logger_type), POINTER :: logger
1510 : TYPE(dft_control_type), POINTER :: dft_control
1511 : TYPE(particle_list_type), POINTER :: particles
1512 0 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
1513 : TYPE(pw_c1d_gs_type) :: wf_g
1514 : TYPE(pw_env_type), POINTER :: pw_env
1515 0 : TYPE(pw_pool_p_type), DIMENSION(:), POINTER :: pw_pools
1516 : TYPE(pw_pool_type), POINTER :: auxbas_pw_pool
1517 : TYPE(pw_r3d_rs_type) :: wf_r
1518 0 : TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kind_set
1519 : TYPE(qs_subsys_type), POINTER :: subsys
1520 :
1521 0 : logger => cp_get_default_logger()
1522 0 : CALL timeset(routineN, handle)
1523 :
1524 0 : CALL get_qs_env(qs_env=qs_env, dft_control=dft_control, pw_env=pw_env)
1525 0 : CALL pw_env_get(pw_env, auxbas_pw_pool=auxbas_pw_pool, pw_pools=pw_pools)
1526 0 : CALL auxbas_pw_pool%create_pw(wf_r)
1527 0 : CALL auxbas_pw_pool%create_pw(wf_g)
1528 :
1529 0 : CALL get_qs_env(qs_env, subsys=subsys)
1530 0 : CALL qs_subsys_get(subsys, particles=particles)
1531 :
1532 0 : my_pos_cube = "REWIND"
1533 0 : IF (append_cube) THEN
1534 0 : my_pos_cube = "APPEND"
1535 : END IF
1536 :
1537 : CALL get_qs_env(qs_env=qs_env, &
1538 : atomic_kind_set=atomic_kind_set, &
1539 : qs_kind_set=qs_kind_set, &
1540 : cell=cell, &
1541 0 : particle_set=particle_set)
1542 :
1543 0 : DO iset = 1, 2
1544 0 : CALL get_mo_set(mo_set=mos(iset), mo_coeff=mo_coeff, nmo=nmo)
1545 0 : DO i = 1, nmo
1546 : CALL calculate_wavefunction(mo_coeff, i, wf_r, wf_g, atomic_kind_set, qs_kind_set, &
1547 0 : cell, dft_control, particle_set, pw_env)
1548 0 : IF (iset == 1) THEN
1549 0 : WRITE (filename, '(A6,I3.3,A5,I2.2,a11)') "_NEXC_", istate, "_NTO_", i, "_Hole_State"
1550 0 : ELSE IF (iset == 2) THEN
1551 0 : WRITE (filename, '(A6,I3.3,A5,I2.2,a15)') "_NEXC_", istate, "_NTO_", i, "_Particle_State"
1552 : END IF
1553 0 : info_approx_trunc = TRIM(ADJUSTL(info_approximation))
1554 0 : info_approx_trunc = info_approx_trunc(2:LEN_TRIM(info_approx_trunc) - 1)
1555 0 : filename = TRIM(info_approx_trunc)//TRIM(filename)
1556 0 : mpi_io = .TRUE.
1557 : unit_nr_cube = cp_print_key_unit_nr(logger, print_section, '', extension=".cube", &
1558 : middle_name=TRIM(filename), file_position=my_pos_cube, &
1559 0 : log_filename=.FALSE., ignore_should_output=.TRUE., mpi_io=mpi_io)
1560 0 : IF (iset == 1) THEN
1561 0 : WRITE (title, *) "Natural Transition Orbital Hole State", i
1562 0 : ELSE IF (iset == 2) THEN
1563 0 : WRITE (title, *) "Natural Transition Orbital Particle State", i
1564 : END IF
1565 0 : CALL cp_pw_to_cube(wf_r, unit_nr_cube, title, particles=particles, stride=stride, mpi_io=mpi_io)
1566 : CALL cp_print_key_finished_output(unit_nr_cube, logger, print_section, '', &
1567 0 : ignore_should_output=.TRUE., mpi_io=mpi_io)
1568 : END DO
1569 : END DO
1570 :
1571 0 : CALL auxbas_pw_pool%give_back_pw(wf_g)
1572 0 : CALL auxbas_pw_pool%give_back_pw(wf_r)
1573 :
1574 0 : CALL timestop(handle)
1575 0 : END SUBROUTINE print_bse_nto_cubes
1576 :
1577 : ! **************************************************************************************************
1578 : !> \brief Checks BSE input section and adapts them if necessary
1579 : !> \param homo ...
1580 : !> \param virtual ...
1581 : !> \param unit_nr ...
1582 : !> \param mp2_env ...
1583 : !> \param qs_env ...
1584 : ! **************************************************************************************************
1585 48 : SUBROUTINE adapt_BSE_input_params(homo, virtual, unit_nr, mp2_env, qs_env)
1586 :
1587 : INTEGER, INTENT(IN) :: homo, virtual, unit_nr
1588 : TYPE(mp2_type) :: mp2_env
1589 : TYPE(qs_environment_type), POINTER :: qs_env
1590 :
1591 : CHARACTER(LEN=*), PARAMETER :: routineN = 'adapt_BSE_input_params'
1592 :
1593 : INTEGER :: handle, i, j, n, n_exc_max, &
1594 : ndim_periodic_cell, &
1595 : ndim_periodic_poisson, &
1596 : num_state_list_exceptions
1597 : TYPE(cell_type), POINTER :: cell_ref
1598 : TYPE(pw_env_type), POINTER :: pw_env
1599 : TYPE(pw_poisson_type), POINTER :: poisson_env
1600 :
1601 48 : CALL timeset(routineN, handle)
1602 : ! Get environment infos for later usage
1603 48 : NULLIFY (pw_env, cell_ref, poisson_env)
1604 48 : CALL get_qs_env(qs_env, pw_env=pw_env, cell_ref=cell_ref)
1605 48 : CALL pw_env_get(pw_env, poisson_env=poisson_env)
1606 192 : ndim_periodic_poisson = COUNT(poisson_env%parameters%periodic == 1)
1607 192 : ndim_periodic_cell = SUM(cell_ref%perd(1:3)) ! Borrowed from cell_methods.F/write_cell_low
1608 :
1609 : ! The iterative solver computes the NUM_EXC_EN lowest excitations only
1610 48 : n_exc_max = homo*virtual
1611 48 : IF (mp2_env%bse%bse_diag_method == bse_iterdiag) THEN
1612 6 : n_exc_max = MIN(n_exc_max, mp2_env%bse%num_exc_en)
1613 : END IF
1614 :
1615 : ! Handle negative NUM_PRINT_EXC
1616 48 : IF (mp2_env%bse%num_print_exc < 0 .OR. &
1617 : mp2_env%bse%num_print_exc > n_exc_max) THEN
1618 12 : mp2_env%bse%num_print_exc = n_exc_max
1619 12 : IF (unit_nr > 0) THEN
1620 : CALL cp_hint(__LOCATION__, &
1621 : "Keyword NUM_PRINT_EXC is either negative or too large. "// &
1622 6 : "Printing all computed excitations.")
1623 : END IF
1624 : END IF
1625 :
1626 : ! Default to NUM_PRINT_EXC if too large or negative,
1627 : ! but only if NTOs are called - would be confusing for the user otherwise
1628 : ! Prepare and adapt user inputs for NTO analysis
1629 : ! Logic: Explicit state list overrides NUM_PRINT_EXC_NTOS
1630 : ! If only NUM_PRINT_EXC_NTOS is given, we write the array 1,...,NUM_PRINT_EXC_NTOS to
1631 : ! bse_nto_state_list
1632 48 : IF (mp2_env%bse%do_nto_analysis) THEN
1633 4 : IF (mp2_env%bse%explicit_nto_list) THEN
1634 0 : IF (mp2_env%bse%num_print_exc_ntos > 0) THEN
1635 0 : IF (unit_nr > 0) THEN
1636 : CALL cp_hint(__LOCATION__, &
1637 : "Keywords NUM_PRINT_EXC_NTOS and STATE_LIST are both given in input. "// &
1638 0 : "Overriding NUM_PRINT_EXC_NTOS.")
1639 : END IF
1640 : END IF
1641 : ! Check if all states are within the range
1642 : ! Count them and initialize new array afterwards
1643 0 : num_state_list_exceptions = 0
1644 0 : DO i = 1, SIZE(mp2_env%bse%bse_nto_state_list)
1645 0 : IF (mp2_env%bse%bse_nto_state_list(i) < 1 .OR. &
1646 0 : mp2_env%bse%bse_nto_state_list(i) > mp2_env%bse%num_print_exc) THEN
1647 0 : num_state_list_exceptions = num_state_list_exceptions + 1
1648 : END IF
1649 : END DO
1650 0 : IF (num_state_list_exceptions > 0) THEN
1651 0 : IF (unit_nr > 0) THEN
1652 : CALL cp_hint(__LOCATION__, &
1653 : "STATE_LIST contains indices outside the range of included excitation levels. "// &
1654 0 : "Ignoring these states.")
1655 : END IF
1656 : END IF
1657 0 : n = SIZE(mp2_env%bse%bse_nto_state_list) - num_state_list_exceptions
1658 0 : ALLOCATE (mp2_env%bse%bse_nto_state_list_final(n))
1659 0 : mp2_env%bse%bse_nto_state_list_final(:) = 0
1660 : i = 1
1661 0 : DO j = 1, SIZE(mp2_env%bse%bse_nto_state_list)
1662 0 : IF (mp2_env%bse%bse_nto_state_list(j) >= 1 .AND. &
1663 0 : mp2_env%bse%bse_nto_state_list(j) <= mp2_env%bse%num_print_exc) THEN
1664 0 : mp2_env%bse%bse_nto_state_list_final(i) = mp2_env%bse%bse_nto_state_list(j)
1665 0 : i = i + 1
1666 : END IF
1667 : END DO
1668 :
1669 0 : mp2_env%bse%num_print_exc_ntos = SIZE(mp2_env%bse%bse_nto_state_list_final)
1670 : ELSE
1671 4 : IF (mp2_env%bse%num_print_exc_ntos > mp2_env%bse%num_print_exc .OR. &
1672 : mp2_env%bse%num_print_exc_ntos < 0) THEN
1673 4 : mp2_env%bse%num_print_exc_ntos = mp2_env%bse%num_print_exc
1674 : END IF
1675 12 : ALLOCATE (mp2_env%bse%bse_nto_state_list_final(mp2_env%bse%num_print_exc_ntos))
1676 104 : DO i = 1, mp2_env%bse%num_print_exc_ntos
1677 104 : mp2_env%bse%bse_nto_state_list_final(i) = i
1678 : END DO
1679 : END IF
1680 : END IF
1681 :
1682 : ! Takes care of triplet states, when oscillator strengths are 0
1683 48 : IF (mp2_env%bse%bse_spin_config /= 0 .AND. &
1684 : mp2_env%bse%eps_nto_osc_str > 0) THEN
1685 0 : IF (unit_nr > 0) THEN
1686 : CALL cp_warn(__LOCATION__, &
1687 : "Cannot apply EPS_OSC_STR for Triplet excitations. "// &
1688 0 : "Resetting EPS_OSC_STR to default.")
1689 : END IF
1690 0 : mp2_env%bse%eps_nto_osc_str = -1.0_dp
1691 : END IF
1692 :
1693 : ! Take care of number for computed exciton descriptors
1694 48 : IF (mp2_env%bse%num_print_exc_descr < 0 .OR. &
1695 : mp2_env%bse%num_print_exc_descr > mp2_env%bse%num_print_exc) THEN
1696 4 : IF (unit_nr > 0) THEN
1697 : CALL cp_hint(__LOCATION__, &
1698 : "Keyword NUM_PRINT_EXC_DESCR is either negative or too large. "// &
1699 2 : "Printing exciton descriptors up to NUM_PRINT_EXC.")
1700 : END IF
1701 4 : mp2_env%bse%num_print_exc_descr = mp2_env%bse%num_print_exc
1702 : END IF
1703 :
1704 : ! Handle screening factor options
1705 48 : IF (mp2_env%BSE%screening_factor > 0.0_dp) THEN
1706 2 : IF (mp2_env%BSE%screening_method /= bse_screening_alpha) THEN
1707 0 : IF (unit_nr > 0) THEN
1708 : CALL cp_warn(__LOCATION__, &
1709 : "Screening factor is only supported for &SCREENING_IN_W ALPHA. "// &
1710 0 : "Resetting SCREENING_IN_W to ALPHA.")
1711 : END IF
1712 0 : mp2_env%BSE%screening_method = bse_screening_alpha
1713 : END IF
1714 2 : IF (mp2_env%BSE%screening_factor > 1.0_dp) THEN
1715 0 : IF (unit_nr > 0) THEN
1716 : CALL cp_warn(__LOCATION__, &
1717 0 : "Screening factor is larger than 1.0. ")
1718 : END IF
1719 : END IF
1720 : END IF
1721 :
1722 48 : IF (mp2_env%BSE%screening_factor < 0.0_dp .AND. &
1723 : mp2_env%BSE%screening_method == bse_screening_alpha) THEN
1724 0 : IF (unit_nr > 0) THEN
1725 : CALL cp_warn(__LOCATION__, &
1726 0 : "Screening factor is negative. Defaulting to 0.25")
1727 : END IF
1728 0 : mp2_env%BSE%screening_factor = 0.25_dp
1729 : END IF
1730 :
1731 48 : IF (mp2_env%BSE%screening_factor == 0.0_dp) THEN
1732 : ! Use RPA internally in this case
1733 0 : mp2_env%BSE%screening_method = bse_screening_rpa
1734 : END IF
1735 48 : IF (mp2_env%BSE%screening_factor == 1.0_dp) THEN
1736 : ! Use TDHF internally in this case
1737 0 : mp2_env%BSE%screening_method = bse_screening_tdhf
1738 : END IF
1739 :
1740 : ! Add warning for usage of KS energies
1741 48 : IF (mp2_env%bse%use_ks_energies) THEN
1742 2 : IF (unit_nr > 0) THEN
1743 : CALL cp_warn(__LOCATION__, &
1744 : "Using KS energies for BSE calculations. Therefore, no quantities "// &
1745 1 : "of the preceeding GW calculation enter the BSE.")
1746 : END IF
1747 : END IF
1748 :
1749 : ! Add warning if periodic calculation is invoked
1750 48 : IF (ndim_periodic_poisson /= 0) THEN
1751 0 : IF (unit_nr > 0) THEN
1752 : CALL cp_warn(__LOCATION__, &
1753 : "Poisson solver should be invoked by PERIODIC NONE. "// &
1754 : "The applied length gauge might give misleading results for "// &
1755 0 : "oscillator strengths.")
1756 : END IF
1757 : END IF
1758 48 : IF (ndim_periodic_cell /= 0) THEN
1759 0 : IF (unit_nr > 0) THEN
1760 : CALL cp_warn(__LOCATION__, &
1761 : "CELL in SUBSYS should be invoked with PERIODIC NONE. "// &
1762 : "The applied length gauge might give misleading results for "// &
1763 0 : "oscillator strengths.")
1764 : END IF
1765 : END IF
1766 :
1767 48 : CALL timestop(handle)
1768 48 : END SUBROUTINE adapt_BSE_input_params
1769 :
1770 : ! **************************************************************************************************
1771 :
1772 : ! **************************************************************************************************
1773 : !> \brief ...
1774 : !> \param fm_multipole_ai_trunc ...
1775 : !> \param fm_multipole_ij_trunc ...
1776 : !> \param fm_multipole_ab_trunc ...
1777 : !> \param qs_env ...
1778 : !> \param mo_coeff ...
1779 : !> \param rpoint ...
1780 : !> \param n_moments ...
1781 : !> \param homo_red ...
1782 : !> \param virtual_red ...
1783 : !> \param context_BSE ...
1784 : !> \param ispin spin channel whose mo_set supplies homo/nao (default 1); open-shell beta needs 2
1785 : ! **************************************************************************************************
1786 60 : SUBROUTINE get_multipoles_mo(fm_multipole_ai_trunc, fm_multipole_ij_trunc, fm_multipole_ab_trunc, &
1787 60 : qs_env, mo_coeff, rpoint, n_moments, &
1788 : homo_red, virtual_red, context_BSE, ispin)
1789 :
1790 : TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:), &
1791 : INTENT(INOUT) :: fm_multipole_ai_trunc, &
1792 : fm_multipole_ij_trunc, &
1793 : fm_multipole_ab_trunc
1794 : TYPE(qs_environment_type), POINTER :: qs_env
1795 : TYPE(cp_fm_type), DIMENSION(:), INTENT(IN) :: mo_coeff
1796 : REAL(dp), ALLOCATABLE, DIMENSION(:), INTENT(INOUT) :: rpoint
1797 : INTEGER, INTENT(IN) :: n_moments, homo_red, virtual_red
1798 : TYPE(cp_blacs_env_type), POINTER :: context_BSE
1799 : INTEGER, INTENT(IN), OPTIONAL :: ispin
1800 :
1801 : CHARACTER(LEN=*), PARAMETER :: routineN = 'get_multipoles_mo'
1802 :
1803 : INTEGER :: handle, idir, my_ispin, n_multipole, &
1804 : n_occ, n_virt, nao, nmo_mp2
1805 60 : REAL(KIND=dp), DIMENSION(:), POINTER :: ref_point
1806 : TYPE(cp_fm_struct_type), POINTER :: fm_struct_mp_ab_trunc, fm_struct_mp_ai_trunc, &
1807 : fm_struct_mp_ij_trunc, fm_struct_multipoles_ao, fm_struct_nao_nmo, fm_struct_nmo_nmo
1808 : TYPE(cp_fm_type) :: fm_work
1809 60 : TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_multipole_per_dir
1810 60 : TYPE(dbcsr_p_type), DIMENSION(:), POINTER :: matrix_multipole, matrix_s
1811 60 : TYPE(mo_set_type), DIMENSION(:), POINTER :: mos
1812 : TYPE(mp_para_env_type), POINTER :: para_env_BSE
1813 : TYPE(neighbor_list_set_p_type), DIMENSION(:), &
1814 60 : POINTER :: sab_orb
1815 :
1816 60 : CALL timeset(routineN, handle)
1817 :
1818 60 : my_ispin = 1
1819 60 : IF (PRESENT(ispin)) my_ispin = ispin
1820 :
1821 : !First, we calculate the AO dipoles
1822 60 : NULLIFY (sab_orb, matrix_s)
1823 : CALL get_qs_env(qs_env, &
1824 : mos=mos, &
1825 : matrix_s=matrix_s, &
1826 60 : sab_orb=sab_orb)
1827 :
1828 : ! Use the same blacs environment as for the MO coefficients to ensure correct multiplication dbcsr x fm later on
1829 60 : fm_struct_multipoles_ao => mos(my_ispin)%mo_coeff%matrix_struct
1830 : ! BSE has different contexts and blacsenvs
1831 60 : para_env_BSE => context_BSE%para_env
1832 : ! Get size of multipole tensor
1833 60 : n_multipole = (6 + 11*n_moments + 6*n_moments**2 + n_moments**3)/6 - 1
1834 60 : NULLIFY (matrix_multipole)
1835 60 : CALL dbcsr_allocate_matrix_set(matrix_multipole, n_multipole)
1836 396 : ALLOCATE (fm_multipole_per_dir(n_multipole))
1837 276 : DO idir = 1, n_multipole
1838 216 : CALL dbcsr_init_p(matrix_multipole(idir)%matrix)
1839 : CALL dbcsr_create(matrix_multipole(idir)%matrix, name="ao_multipole", &
1840 216 : template=matrix_s(1)%matrix, matrix_type=dbcsr_type_symmetric)
1841 216 : CALL cp_dbcsr_alloc_block_from_nbl(matrix_multipole(idir)%matrix, sab_orb)
1842 276 : CALL dbcsr_set(matrix_multipole(idir)%matrix, 0._dp)
1843 : END DO
1844 :
1845 60 : CALL get_reference_point(rpoint, qs_env=qs_env, reference=use_mom_ref_coac, ref_point=ref_point)
1846 :
1847 60 : CALL build_local_moment_matrix(qs_env, matrix_multipole, n_moments, ref_point=rpoint)
1848 :
1849 60 : NULLIFY (sab_orb)
1850 :
1851 : ! Now we transform them to MO
1852 : ! n_occ is the number of occupied MOs, nao the number of all AOs
1853 : ! Writing homo to n_occ instead if nmo,
1854 : ! takes care of ADDED_MOS, which would overwrite nmo of qs_env-mos, if invoked
1855 60 : CALL get_mo_set(mo_set=mos(my_ispin), homo=n_occ, nao=nao)
1856 : ! Takes into account removed nullspace values from SVD
1857 60 : nmo_mp2 = mo_coeff(1)%matrix_struct%ncol_global
1858 60 : n_virt = nmo_mp2 - n_occ
1859 :
1860 : ! At the end, we need four different layouts of matrices in this multiplication, e.g. for a dipole:
1861 : ! D_pq = full multipole matrix for occupied and unoccupied
1862 : ! Final result:D_pq= C_{mu p} <\mu|\vec{r}|\nu> C_{\nu q} EQ.I
1863 : ! \_______/ \___________/ \______/
1864 : ! fm_coeff matrix_multipole fm_coeff
1865 : ! (EQ.Ia) (EQ.Ib) (EQ.Ia)
1866 : ! Intermediate work matrices:
1867 : ! fm_work = <\mu|\vec{r}|\nu> C_{\nu q} EQ.II
1868 :
1869 : ! Struct for the full multipole matrix
1870 : CALL cp_fm_struct_create(fm_struct_nao_nmo, &
1871 : fm_struct_multipoles_ao%para_env, fm_struct_multipoles_ao%context, &
1872 60 : nao, nmo_mp2)
1873 : CALL cp_fm_struct_create(fm_struct_nmo_nmo, &
1874 : fm_struct_multipoles_ao%para_env, fm_struct_multipoles_ao%context, &
1875 60 : nmo_mp2, nmo_mp2)
1876 :
1877 : ! At the very end, we copy the multipoles corresponding to truncated BSE indices in i and a
1878 : CALL cp_fm_struct_create(fm_struct_mp_ai_trunc, para_env_BSE, &
1879 60 : context_BSE, virtual_red, homo_red)
1880 : CALL cp_fm_struct_create(fm_struct_mp_ij_trunc, para_env_BSE, &
1881 60 : context_BSE, homo_red, homo_red)
1882 : CALL cp_fm_struct_create(fm_struct_mp_ab_trunc, para_env_BSE, &
1883 60 : context_BSE, virtual_red, virtual_red)
1884 276 : DO idir = 1, n_multipole
1885 : CALL cp_fm_create(fm_multipole_ai_trunc(idir), matrix_struct=fm_struct_mp_ai_trunc, &
1886 216 : name="dipoles_mo_ai_trunc")
1887 216 : CALL cp_fm_set_all(fm_multipole_ai_trunc(idir), 0.0_dp)
1888 : CALL cp_fm_create(fm_multipole_ij_trunc(idir), matrix_struct=fm_struct_mp_ij_trunc, &
1889 216 : name="dipoles_mo_ij_trunc")
1890 216 : CALL cp_fm_set_all(fm_multipole_ij_trunc(idir), 0.0_dp)
1891 : CALL cp_fm_create(fm_multipole_ab_trunc(idir), matrix_struct=fm_struct_mp_ab_trunc, &
1892 216 : name="dipoles_mo_ab_trunc")
1893 276 : CALL cp_fm_set_all(fm_multipole_ab_trunc(idir), 0.0_dp)
1894 : END DO
1895 :
1896 : ! Need another temporary matrix to store intermediate result from right multiplication
1897 : ! D = C_{mu a} <\mu|\vec{r}|\nu> C_{\nu i}
1898 60 : CALL cp_fm_create(fm_work, matrix_struct=fm_struct_nao_nmo, name="multipole_work")
1899 60 : CALL cp_fm_set_all(fm_work, 0.0_dp)
1900 :
1901 276 : DO idir = 1, n_multipole
1902 : ! Create the full multipole matrix per direction
1903 216 : CALL cp_fm_create(fm_multipole_per_dir(idir), matrix_struct=fm_struct_nmo_nmo, name="multipoles_mo")
1904 216 : CALL cp_fm_set_all(fm_multipole_per_dir(idir), 0.0_dp)
1905 : ! Fill final (MO) multipole matrix
1906 : CALL cp_dbcsr_sm_fm_multiply(matrix_multipole(idir)%matrix, mo_coeff(1), &
1907 216 : fm_work, ncol=nmo_mp2)
1908 : ! Now obtain the multipoles by the final multiplication;
1909 : ! We do that inside the loop to obtain multipoles per axis for print
1910 216 : CALL parallel_gemm('T', 'N', nmo_mp2, nmo_mp2, nao, 1.0_dp, mo_coeff(1), fm_work, 0.0_dp, fm_multipole_per_dir(idir))
1911 :
1912 : ! Truncate full matrix to the BSE indices
1913 : ! D_ai
1914 : CALL cp_fm_to_fm_submat_general(fm_multipole_per_dir(idir), &
1915 : fm_multipole_ai_trunc(idir), &
1916 : virtual_red, &
1917 : homo_red, &
1918 : n_occ + 1, &
1919 : n_occ - homo_red + 1, &
1920 : 1, &
1921 : 1, &
1922 216 : fm_multipole_per_dir(idir)%matrix_struct%context)
1923 : ! D_ij
1924 : CALL cp_fm_to_fm_submat_general(fm_multipole_per_dir(idir), &
1925 : fm_multipole_ij_trunc(idir), &
1926 : homo_red, &
1927 : homo_red, &
1928 : n_occ - homo_red + 1, &
1929 : n_occ - homo_red + 1, &
1930 : 1, &
1931 : 1, &
1932 216 : fm_multipole_per_dir(idir)%matrix_struct%context)
1933 : ! D_ab
1934 : CALL cp_fm_to_fm_submat_general(fm_multipole_per_dir(idir), &
1935 : fm_multipole_ab_trunc(idir), &
1936 : virtual_red, &
1937 : virtual_red, &
1938 : n_occ + 1, &
1939 : n_occ + 1, &
1940 : 1, &
1941 : 1, &
1942 276 : fm_multipole_per_dir(idir)%matrix_struct%context)
1943 : END DO
1944 :
1945 : !Release matrices and structs
1946 60 : NULLIFY (fm_struct_multipoles_ao)
1947 60 : CALL cp_fm_struct_release(fm_struct_mp_ai_trunc)
1948 60 : CALL cp_fm_struct_release(fm_struct_mp_ij_trunc)
1949 60 : CALL cp_fm_struct_release(fm_struct_mp_ab_trunc)
1950 60 : CALL cp_fm_struct_release(fm_struct_nao_nmo)
1951 60 : CALL cp_fm_struct_release(fm_struct_nmo_nmo)
1952 276 : DO idir = 1, n_multipole
1953 276 : CALL cp_fm_release(fm_multipole_per_dir(idir))
1954 : END DO
1955 60 : DEALLOCATE (fm_multipole_per_dir)
1956 60 : CALL cp_fm_release(fm_work)
1957 60 : CALL dbcsr_deallocate_matrix_set(matrix_multipole)
1958 :
1959 60 : CALL timestop(handle)
1960 :
1961 180 : END SUBROUTINE get_multipoles_mo
1962 :
1963 : ! **************************************************************************************************
1964 : !> \brief Computes trace of form Tr{A^T B C} for exciton descriptors
1965 : !> \param fm_A Full Matrix, typically X or Y, in format homo x virtual
1966 : !> \param fm_B ...
1967 : !> \param fm_C ...
1968 : !> \param alpha ...
1969 : ! **************************************************************************************************
1970 11520 : SUBROUTINE trace_exciton_descr(fm_A, fm_B, fm_C, alpha)
1971 :
1972 : TYPE(cp_fm_type), INTENT(IN) :: fm_A, fm_B, fm_C
1973 : REAL(KIND=dp), INTENT(OUT) :: alpha
1974 :
1975 : CHARACTER(LEN=*), PARAMETER :: routineN = 'trace_exciton_descr'
1976 :
1977 : INTEGER :: handle, ncol_A, ncol_B, ncol_C, nrow_A, &
1978 : nrow_B, nrow_C
1979 : TYPE(cp_fm_type) :: fm_work_ia
1980 :
1981 1920 : CALL timeset(routineN, handle)
1982 :
1983 1920 : CALL cp_fm_create(fm_work_ia, fm_A%matrix_struct)
1984 1920 : CALL cp_fm_get_info(fm_A, nrow_global=nrow_A, ncol_global=ncol_A)
1985 1920 : CALL cp_fm_get_info(fm_B, nrow_global=nrow_B, ncol_global=ncol_B)
1986 1920 : CALL cp_fm_get_info(fm_C, nrow_global=nrow_C, ncol_global=ncol_C)
1987 :
1988 : ! Check matrix sizes
1989 1920 : CPASSERT(nrow_A == nrow_B .AND. ncol_A == ncol_C .AND. ncol_B == nrow_C)
1990 :
1991 1920 : CALL cp_fm_set_all(fm_work_ia, 0.0_dp)
1992 :
1993 : CALL parallel_gemm("N", "N", nrow_A, ncol_A, nrow_C, 1.0_dp, &
1994 1920 : fm_B, fm_C, 0.0_dp, fm_work_ia)
1995 :
1996 1920 : CALL cp_fm_trace(fm_A, fm_work_ia, alpha)
1997 :
1998 1920 : CALL cp_fm_release(fm_work_ia)
1999 :
2000 1920 : CALL timestop(handle)
2001 :
2002 1920 : END SUBROUTINE trace_exciton_descr
2003 :
2004 : ! **************************************************************************************************
2005 : !> \brief Column-concatenate per-spin ia-slabs into the joint dimen_RI x n_ov_joint slab.
2006 : !> Sigma-block of spin isp occupies columns offsets(isp)+1 .. offsets(isp)+n_ov(isp).
2007 : !> fm_S_joint must be pre-created and zeroed by the caller.
2008 : !> \param fm_S_ia per-spin ia-slabs, shape (dimen_RI, n_ov(isp)) per spin
2009 : !> \param offsets per-spin column offsets into fm_S_joint (0-based)
2010 : !> \param n_ov per-spin OV-pair counts
2011 : !> \param dimen_RI RI auxiliary basis dimension (row count)
2012 : !> \param fm_S_joint pre-created output slab (dimen_RI x n_ov_joint)
2013 : ! **************************************************************************************************
2014 12 : SUBROUTINE assemble_joint_ov_slab(fm_S_ia, offsets, n_ov, dimen_RI, fm_S_joint)
2015 : TYPE(cp_fm_type), DIMENSION(:), INTENT(IN) :: fm_S_ia
2016 : INTEGER, DIMENSION(:), INTENT(IN) :: offsets, n_ov
2017 : INTEGER, INTENT(IN) :: dimen_RI
2018 : TYPE(cp_fm_type), INTENT(INOUT) :: fm_S_joint
2019 :
2020 : CHARACTER(LEN=*), PARAMETER :: routineN = 'assemble_joint_ov_slab'
2021 :
2022 : INTEGER :: handle, isp
2023 :
2024 12 : CALL timeset(routineN, handle)
2025 36 : DO isp = 1, SIZE(fm_S_ia)
2026 36 : CALL cp_fm_to_fm_submat(fm_S_ia(isp), fm_S_joint, dimen_RI, n_ov(isp), 1, 1, 1, offsets(isp) + 1)
2027 : END DO
2028 12 : CALL timestop(handle)
2029 :
2030 12 : END SUBROUTINE assemble_joint_ov_slab
2031 :
2032 : END MODULE bse_util
|