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