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 GW using RI-RS Approximation for molecules
10 : !> \par History
11 : !> 04.2026 created [Ritaj Tyagi]
12 : ! **************************************************************************************************
13 :
14 : MODULE gw_non_periodic_ri_rs
15 : USE atomic_kind_types, ONLY: atomic_kind_type,&
16 : get_atomic_kind_set
17 : USE cell_types, ONLY: cell_type
18 : USE constants_operator, ONLY: operator_coulomb
19 : USE cp_dbcsr_api, ONLY: &
20 : dbcsr_add, dbcsr_copy, dbcsr_create, dbcsr_deallocate_matrix, dbcsr_distribution_get, &
21 : dbcsr_distribution_new, dbcsr_distribution_release, dbcsr_distribution_type, dbcsr_filter, &
22 : dbcsr_finalize, dbcsr_get_block_p, dbcsr_get_data_size, dbcsr_get_info, &
23 : dbcsr_get_occupation, dbcsr_iterator_blocks_left, dbcsr_iterator_next_block, &
24 : dbcsr_iterator_start, dbcsr_iterator_stop, dbcsr_iterator_type, dbcsr_multiply, &
25 : dbcsr_p_type, dbcsr_put_block, dbcsr_release, dbcsr_scale, dbcsr_set, dbcsr_type, &
26 : dbcsr_type_no_symmetry
27 : USE cp_dbcsr_contrib, ONLY: dbcsr_reserve_all_blocks
28 : USE cp_dbcsr_operations, ONLY: copy_dbcsr_to_fm,&
29 : copy_fm_to_dbcsr,&
30 : dbcsr_deallocate_matrix_set,&
31 : max_elements_per_block
32 : USE cp_fm_basic_linalg, ONLY: cp_fm_scale_and_add,&
33 : cp_fm_uplo_to_full
34 : USE cp_fm_diag, ONLY: cp_fm_geeig
35 : USE cp_fm_types, ONLY: cp_fm_create,&
36 : cp_fm_get_diag,&
37 : cp_fm_get_info,&
38 : cp_fm_release,&
39 : cp_fm_set_all,&
40 : cp_fm_to_fm,&
41 : cp_fm_type
42 : USE gw_compute_Z_lP, ONLY: compute_Z_lP
43 : USE gw_fm_utils, ONLY: fm_contract_ABA,&
44 : fm_invert,&
45 : fm_sqrt
46 : USE gw_large_cell_gamma, ONLY: Fourier_transform_w_to_t,&
47 : G_occ_vir,&
48 : compute_fm_chi_Gamma_freq,&
49 : create_fm_W_MIC_time,&
50 : delete_unnecessary_files,&
51 : fill_fm_Sigma_c_Gamma_time,&
52 : fm_write
53 : USE gw_optimize_ri_rs_grid, ONLY: optimize_ri_rs_grid
54 : USE gw_utils, ONLY: analyt_conti_and_print,&
55 : de_init_bs_env,&
56 : time_to_freq
57 : USE input_constants, ONLY: G0W0,&
58 : evGW0,&
59 : rtp_method_bse
60 : USE kinds, ONLY: default_path_length,&
61 : default_string_length,&
62 : dp,&
63 : int_8,&
64 : max_line_length
65 : USE kpoint_coulomb_2c, ONLY: build_2c_coulomb_matrix_kp
66 : USE machine, ONLY: m_flush,&
67 : m_walltime
68 : USE message_passing, ONLY: mp_mem_avail_per_rank_GB,&
69 : mp_para_env_type,&
70 : mp_print_mem_per_rank
71 : USE mp2_ri_2c, ONLY: RI_2c_integral_mat
72 : USE parallel_gemm_api, ONLY: parallel_gemm
73 : USE particle_types, ONLY: particle_type
74 : USE physcon, ONLY: angstrom,&
75 : evolt
76 : USE post_scf_bandstructure_types, ONLY: post_scf_bandstructure_type,&
77 : rirs_grid_type
78 : USE post_scf_bandstructure_utils, ONLY: get_all_VBM_CBM_bandgaps
79 : USE qs_environment_types, ONLY: get_qs_env,&
80 : qs_environment_type
81 : USE qs_kind_types, ONLY: get_qs_kind,&
82 : qs_kind_type
83 : USE rirs_grid_utils, ONLY: evaluate_ao_on_points,&
84 : get_rirs_grid_filepath,&
85 : initialize_rirs_grid,&
86 : read_rirs_grid_file
87 : USE util, ONLY: sort
88 : #include "./base/base_uses.f90"
89 :
90 : IMPLICIT NONE
91 :
92 : PRIVATE
93 :
94 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'gw_non_periodic_ri_rs'
95 :
96 : PUBLIC :: gw_calc_non_periodic_ri_rs, ri_rs_grid_assembler, &
97 : get_basis_offsets, precompute_ri_rs_radii, &
98 : atomic_basis_at_grid_point, &
99 : reserve_blocks_within_radius
100 :
101 : CONTAINS
102 :
103 : ! **************************************************************************************************
104 : !> \brief GW calculation using RI-RS formalism for molecules
105 : !> \param qs_env ...
106 : !> \param bs_env ...
107 : ! **************************************************************************************************
108 38 : SUBROUTINE gw_calc_non_periodic_ri_rs(qs_env, bs_env)
109 :
110 : TYPE(qs_environment_type), POINTER :: qs_env
111 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
112 :
113 : CHARACTER(LEN=*), PARAMETER :: routineN = 'gw_calc_non_periodic_ri_rs'
114 :
115 : INTEGER :: handle
116 38 : TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_Sigma_x_Gamma, fm_W_time
117 :
118 38 : CALL timeset(routineN, handle)
119 :
120 : ! ==============================================================================
121 : ! 0. Precompute AO and RI radii
122 : ! Per-atom cutoff radii from the most diffuse Gaussian primitives of
123 : ! the AO ("ORB") and RI auxiliary ("RI_AUX") basis sets:
124 : ! α_min,ao = min { ζ_ao | ζ_ao > 10⁻³ }, α_min,ri analogous
125 : ! r_ao = sqrt( -ln(ε) / α_min,ao ) (radius_ao_per_atom)
126 : ! r_ri = sqrt( -ln(ε) / α_min,ri ) (radius_ri_per_atom)
127 : ! ==============================================================================
128 38 : CALL precompute_ri_rs_radii(qs_env, bs_env)
129 :
130 : ! ==============================================================================
131 : ! 1. Grid generation for RI-RS
132 : ! Modified Lebedev atomic grids (Duchemin & Blase), one per atom,
133 : ! concatenated into a flat global list: r_l = R_A + r_l^(A)
134 : ! ==============================================================================
135 38 : CALL ri_rs_grid_assembler(qs_env, bs_env, bs_env%ri_rs%grid_points)
136 :
137 : ! ==============================================================================
138 : ! 2a. Atomic basis evaluation on the grid (grid x AO matrix)
139 : ! Φ_μl = Φ_μ(r_l) (mat_phi_mu_l)
140 : ! ==============================================================================
141 : CALL atomic_basis_at_grid_point(qs_env, bs_env, bs_env%ri_rs%grid_points, &
142 38 : bs_env%ri_rs%mat_phi_mu_l)
143 :
144 : ! ==============================================================================
145 : ! 2b. Print the memory estimate for the RI-RS calculation
146 : ! ==============================================================================
147 38 : CALL print_ri_rs_memory_estimate(qs_env, bs_env)
148 :
149 : ! ==============================================================================
150 : ! 3. RI-RS fitting coefficients Z_lP (grid x RI matrix)
151 : ! Per-atom regularized solve, restricted to grid points r_l within a
152 : ! cutoff distance of atom P:
153 : ! a. D_ll' = [ Σ_μ Φ_μ(r_l) Φ_μ(r_l') ]²
154 : ! b. D_lP = Σ_μν Φ_μ(r_l) Φ_ν(r_l) (μν|P)
155 : ! c. Jacobi conditioning with d_l = 1/sqrt(D_ll):
156 : ! D'_ll' = d_l D_ll' d_l' + λδ_ll' , D'_lP = d_l D_lP
157 : ! d. Solve Σ_l' D'_ll' Z'_l'P = D'_lP
158 : ! e. Rescale Z_lP = d_l Z'_lP (mat_Z_lP)
159 : ! ==============================================================================
160 : CALL compute_Z_lP(qs_env, bs_env, bs_env%ri_rs%grid_points, &
161 38 : bs_env%ri_rs%mat_phi_mu_l, bs_env%ri_rs%mat_Z_lP)
162 :
163 : ! flag the RI-RS grid as built so a subsequent RT-BSE run reuses Z_lP
164 : ! instead of rebuilding it
165 38 : bs_env%ri_rs%grid_built = .TRUE.
166 :
167 : CALL mp_print_mem_per_rank(bs_env%para_env, bs_env%unit_nr, &
168 38 : label='Memory per MPI process after computing Z_lP:')
169 :
170 : ! ==============================================================================
171 : ! 4. Polarizability matrix χ on the imaginary-time grid
172 : ! G^occ_µλ(i|τ|) = Σ_n^occ C_µn e^(-|(ϵ_n-ϵ_F)τ|) C_λn
173 : ! G^vir_µλ(i|τ|) = Σ_n^vir C_µn e^(-|(ϵ_n-ϵ_F)τ|) C_λn
174 : ! G^occ_ll'(i|τ|) = Σ_µν Φ_µ(r_l) G^occ_µν Φ_ν(r_l') (G^vir analogous)
175 : ! χ_ll'(iτ) = G^occ_ll'(i|τ|) ∘ G^vir_ll'(i|τ|) (element-wise)
176 : ! χ_PQ(iτ) = Σ_ll' Z_lP χ_ll'(iτ) Z_l'Q
177 : ! ==============================================================================
178 : CALL get_mat_chi_Gamma_tau(bs_env, bs_env%mat_chi_Gamma_tau, &
179 38 : bs_env%ri_rs%mat_phi_mu_l, bs_env%ri_rs%mat_Z_lP)
180 :
181 : ! ==============================================================================
182 : ! 5. Screened Coulomb interaction W (RI basis)
183 : ! χ_PQ(iτ) -> χ_PQ(iω) -> ε_PQ(iω) -> W_PQ(iω) -> W_PQ(iτ)
184 : ! ==============================================================================
185 38 : CALL compute_W(bs_env, qs_env, bs_env%mat_chi_Gamma_tau, fm_W_time)
186 :
187 : ! ==============================================================================
188 : ! 6. Exact-exchange self-energy Σ^x
189 : ! D_µν = Σ_n^occ C_µn C_νn (density matrix)
190 : ! D_ll' = Σ_µν Φ_µ(r_l) D_µν Φ_ν(r_l')
191 : ! V^tr_ll' = Σ_PQ Z_lP V^tr_PQ Z_l'Q (truncated Coulomb)
192 : ! Σ^x_ll' = D_ll' ∘ V^tr_ll'
193 : ! Σ^x_λσ(k=0) = -Σ_ll' Φ_λ(r_l) Σ^x_ll' Φ_σ(r_l')
194 : ! ==============================================================================
195 : CALL compute_Sigma_x(bs_env, qs_env, bs_env%ri_rs%mat_phi_mu_l, &
196 38 : bs_env%ri_rs%mat_Z_lP, fm_Sigma_x_Gamma)
197 :
198 : ! ==============================================================================
199 : ! 7. Correlation self-energy Σ^c and quasiparticle energies, iterated
200 : ! until eigenvalue self-consistency if the &EVGW0 section is given and
201 : ! done in a single pass for G0W0.
202 : !
203 : ! (a) W_ll'(iτ) = Σ_PQ Z_lP W^MIC_PQ(iτ) Z_l'Q
204 : ! Σ^c_ll'(iτ) = -G^occ_ll'(i|τ|) ∘ W_ll'(iτ), τ < 0
205 : ! Σ^c_ll'(iτ) = G^vir_ll'(i|τ|) ∘ W_ll'(iτ), τ > 0
206 : ! Σ^c_λσ(iτ) = Σ_ll' Φ_λ(r_l) Σ^c_ll'(iτ) Φ_σ(r_l')
207 : ! (b) Σ^c_λσ(iτ) -> Σ^c_nn(ϵ)
208 : ! ϵ_n^GW,(i) = ϵ_n^DFT + Σ^c_nn[G^(i-1),W](ϵ_n^GW,(i)) + Σ^x_nn - v^xc_nn
209 : ! ==============================================================================
210 38 : CALL compute_Sigma_c_and_QP_energies(bs_env, fm_W_time, fm_Sigma_x_Gamma)
211 :
212 38 : CALL de_init_bs_env(qs_env, bs_env)
213 :
214 38 : CALL timestop(handle)
215 :
216 38 : END SUBROUTINE gw_calc_non_periodic_ri_rs
217 :
218 : ! **************************************************************************************************
219 : !> \brief Correlation self-energy and quasiparticle energies, iterated to eigenvalue
220 : !> self-consistency in G (evGW0).
221 : !>
222 : !> For G0W0 this runs once with G^(0) built from the DFT eigenvalues. For evGW0 the
223 : !> Green's function is rebuilt from the quasiparticle energies of all states until the
224 : !> quasiparticle HOMO, LUMO and HOMO-LUMO gap change by less than EPS_ITER between two
225 : !> cycles, or MAX_ITER cycles are spent. W, Σ^x and everything computed before this
226 : !> routine stay frozen: they either do not depend on the eigenvalues at all (Σ^x is G
227 : !> at τ = 0, a pure density matrix) or are held fixed by construction in GW0. The
228 : !> imaginary-time grid is fixed as well, since W(iτ) lives on it.
229 : !>
230 : !> \param bs_env ...
231 : !> \param fm_W_time ...
232 : !> \param fm_Sigma_x_Gamma ...
233 : ! **************************************************************************************************
234 38 : SUBROUTINE compute_Sigma_c_and_QP_energies(bs_env, fm_W_time, fm_Sigma_x_Gamma)
235 :
236 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
237 : TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_W_time, fm_Sigma_x_Gamma
238 :
239 : CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_Sigma_c_and_QP_energies'
240 :
241 : INTEGER :: handle, i_iter, n_iter
242 : LOGICAL :: converged
243 38 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: eigenval_scf_Gamma_dft
244 : REAL(KIND=dp), DIMENSION(2) :: e_fermi_dft
245 : REAL(KIND=dp), DIMENSION(3, 2) :: band_prev
246 38 : TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :, :) :: fm_Sigma_c_Gamma_time
247 :
248 38 : CALL timeset(routineN, handle)
249 :
250 : converged = .FALSE.
251 :
252 38 : CALL init_evGW0(bs_env, n_iter, band_prev, eigenval_scf_Gamma_dft, e_fermi_dft)
253 :
254 : ! evGW0 self-consistency loop; for G0W0, loop is terminated after one iteration
255 52 : DO i_iter = 1, n_iter
256 :
257 52 : bs_env%ri_rs%evgw0_i_iter = i_iter
258 :
259 : ! W_ll'(iτ) = Σ_PQ Z_lP W^MIC_PQ(iτ) Z_l'Q
260 : ! Σ^c_ll'(iτ) = -G^occ_ll'(i|τ|) ∘ W_ll'(iτ), τ < 0
261 : ! Σ^c_ll'(iτ) = G^vir_ll'(i|τ|) ∘ W_ll'(iτ), τ > 0
262 : ! Σ^c_λσ(iτ) = Σ_ll' Φ_λ(r_l) Σ^c_ll'(iτ) Φ_σ(r_l')
263 : CALL compute_Sigma_c(bs_env, fm_W_time, bs_env%ri_rs%mat_phi_mu_l, &
264 52 : bs_env%ri_rs%mat_Z_lP, fm_Sigma_c_Gamma_time)
265 :
266 : ! Σ^c_λσ(iτ) -> Σ^c_nn(ϵ)
267 : ! ϵ_n^GW,(i) = ϵ_n^DFT + Σ^c_nn[G^(i-1),W](ϵ_n^GW,(i)) + Σ^x_nn - v^xc_nn
268 52 : CALL compute_QP_energies(bs_env, fm_Sigma_x_Gamma, fm_Sigma_c_Gamma_time)
269 :
270 52 : IF (bs_env%gw_flavour == G0W0) EXIT
271 :
272 18 : CALL print_evGW0_band_edges(bs_env, band_prev, i_iter, n_iter, converged)
273 :
274 18 : IF (i_iter == n_iter .OR. converged) EXIT
275 :
276 : ! eigenvalues ϵ_n to be updated in G:
277 : ! G^occ_µλ(i|τ|) = Σ_n^occ C_µn e^(-|(ϵ_n-ϵ_F)τ|) C_λn
278 : ! G^vir_µλ(i|τ|) = Σ_n^vir C_µn e^(-|(ϵ_n-ϵ_F)τ|) C_λn
279 70 : CALL update_eigenvalues_G(bs_env)
280 :
281 : END DO
282 :
283 38 : CALL cp_fm_release(fm_W_time)
284 :
285 38 : CALL reset_and_clean_bs_env(bs_env, eigenval_scf_Gamma_dft, e_fermi_dft, fm_Sigma_x_Gamma)
286 :
287 38 : CALL delete_unnecessary_files(bs_env)
288 :
289 38 : CALL timestop(handle)
290 :
291 38 : END SUBROUTINE compute_Sigma_c_and_QP_energies
292 :
293 : ! **************************************************************************************************
294 : !> \brief Sets up the evGW0 eigenvalue self-consistency loop: the cycle count, the eigenvalues
295 : !> the first Green's function is built from, and the DFT reference that the loop
296 : !> overwrites. A G0W0 run reduces to a single cycle and needs none of it.
297 : !> \param bs_env ...
298 : !> \param n_iter ...
299 : !> \param band_prev ...
300 : !> \param eigenval_scf_Gamma_dft ...
301 : !> \param e_fermi_dft ...
302 : ! **************************************************************************************************
303 38 : SUBROUTINE init_evGW0(bs_env, n_iter, band_prev, eigenval_scf_Gamma_dft, e_fermi_dft)
304 :
305 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
306 : INTEGER, INTENT(OUT) :: n_iter
307 : REAL(KIND=dp), DIMENSION(3, 2), INTENT(OUT) :: band_prev
308 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :), &
309 : INTENT(OUT) :: eigenval_scf_Gamma_dft
310 : REAL(KIND=dp), DIMENSION(2), INTENT(OUT) :: e_fermi_dft
311 :
312 : CHARACTER(LEN=*), PARAMETER :: routineN = 'init_evGW0'
313 :
314 : INTEGER :: handle
315 :
316 38 : CALL timeset(routineN, handle)
317 :
318 38 : n_iter = 1
319 38 : band_prev(:, :) = 0.0_dp
320 38 : e_fermi_dft(:) = 0.0_dp
321 :
322 38 : IF (bs_env%gw_flavour == evGW0) THEN
323 4 : n_iter = bs_env%ri_rs%evgw0_iter
324 :
325 : ! eigenvalues currently in G; the first cycle starts is a plain G0W0 step
326 70 : bs_env%eigenval_evGW0(:, :, :) = bs_env%eigenval_scf(:, :, :)
327 : ! the loop overwrites these; post-GW printing expects the DFT values back
328 76 : ALLOCATE (eigenval_scf_Gamma_dft, SOURCE=bs_env%eigenval_scf_Gamma)
329 12 : e_fermi_dft(:) = bs_env%e_fermi(:)
330 : END IF
331 :
332 38 : CALL timestop(handle)
333 :
334 38 : END SUBROUTINE init_evGW0
335 :
336 : ! **************************************************************************************************
337 : !> \brief Feeds the quasiparticle energies of the current evGW0 cycle back into the Green's
338 : !> function used by the next one, and re-centres the Fermi level between the new
339 : !> quasiparticle HOMO and LUMO.
340 : !> \param bs_env ...
341 : ! **************************************************************************************************
342 14 : SUBROUTINE update_eigenvalues_G(bs_env)
343 :
344 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
345 :
346 : CHARACTER(LEN=*), PARAMETER :: routineN = 'update_eigenvalues_G'
347 :
348 : INTEGER :: handle, i_mo, ispin
349 :
350 14 : CALL timeset(routineN, handle)
351 :
352 : ! record this cycle's evGW0 result; it is also what the next Green's function is built from
353 230 : bs_env%eigenval_evGW0(:, :, :) = bs_env%eigenval_GW(:, :, :)
354 :
355 34 : DO ispin = 1, bs_env%n_spin
356 : ! Update all physical states; exclude linear-dependency placeholders.
357 196 : DO i_mo = 1, bs_env%n_mo_retained
358 196 : bs_env%eigenval_scf_Gamma(i_mo, ispin) = bs_env%eigenval_GW(i_mo, 1, ispin)
359 : END DO
360 : bs_env%e_fermi(ispin) = &
361 : 0.5_dp*(bs_env%eigenval_GW(bs_env%n_occ(ispin), 1, ispin) + &
362 34 : bs_env%eigenval_GW(bs_env%n_occ(ispin) + 1, 1, ispin))
363 : END DO
364 :
365 14 : CALL timestop(handle)
366 :
367 14 : END SUBROUTINE update_eigenvalues_G
368 :
369 : ! **************************************************************************************************
370 : !> \brief Restores the DFT reference that the evGW0 loop overwrote and cleanup
371 : !> \param bs_env ...
372 : !> \param eigenval_scf_Gamma_dft ...
373 : !> \param e_fermi_dft ...
374 : !> \param fm_Sigma_x_Gamma ..
375 : ! **************************************************************************************************
376 38 : SUBROUTINE reset_and_clean_bs_env(bs_env, eigenval_scf_Gamma_dft, e_fermi_dft, fm_Sigma_x_Gamma)
377 :
378 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
379 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :), &
380 : INTENT(INOUT) :: eigenval_scf_Gamma_dft
381 : REAL(KIND=dp), DIMENSION(2), INTENT(IN) :: e_fermi_dft
382 : TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_Sigma_x_Gamma
383 :
384 : CHARACTER(LEN=*), PARAMETER :: routineN = 'reset_and_clean_bs_env'
385 :
386 : INTEGER :: handle
387 :
388 38 : CALL timeset(routineN, handle)
389 :
390 38 : IF (bs_env%gw_flavour == evGW0) THEN
391 : ! Update array with final evGW0 result
392 70 : bs_env%eigenval_evGW0(:, :, :) = bs_env%eigenval_GW(:, :, :)
393 : ! put the DFT reference back for the post-GW DOS/band-edge printing
394 64 : bs_env%eigenval_scf_Gamma(:, :) = eigenval_scf_Gamma_dft(:, :)
395 12 : bs_env%e_fermi(:) = e_fermi_dft(:)
396 4 : DEALLOCATE (eigenval_scf_Gamma_dft)
397 4 : CALL cp_fm_release(fm_Sigma_x_Gamma)
398 : END IF
399 :
400 38 : CALL timestop(handle)
401 :
402 38 : END SUBROUTINE reset_and_clean_bs_env
403 :
404 : ! **************************************************************************************************
405 : !> \brief Print the quasiparticle HOMO, LUMO and HOMO-LUMO gap of the current evGW0 cycle
406 : !> \param bs_env ...
407 : !> \param band_prev ...
408 : !> \param i_iter ...
409 : !> \param n_iter ...
410 : !> \param converged ...
411 : ! **************************************************************************************************
412 18 : SUBROUTINE print_evGW0_band_edges(bs_env, band_prev, i_iter, n_iter, converged)
413 :
414 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
415 : REAL(KIND=dp), DIMENSION(3, 2), INTENT(INOUT) :: band_prev
416 : INTEGER, INTENT(IN) :: i_iter, n_iter
417 : LOGICAL, INTENT(OUT) :: converged
418 :
419 : CHARACTER(LEN=*), PARAMETER :: routineN = 'print_evGW0_band_edges'
420 :
421 : INTEGER :: handle, homo, ispin, u
422 : REAL(KIND=dp) :: max_delta
423 : REAL(KIND=dp), DIMENSION(3) :: band
424 :
425 18 : CALL timeset(routineN, handle)
426 :
427 18 : u = bs_env%unit_nr
428 :
429 18 : converged = (i_iter > 1)
430 18 : max_delta = 0.0_dp
431 :
432 18 : IF (u > 0) THEN
433 9 : WRITE (u, '(A)') ' '
434 9 : WRITE (u, '(T2,A)') REPEAT('-', 79)
435 9 : WRITE (u, '(T2,A,I4,A,I4)') 'evGW0 cycle', i_iter, ' /', n_iter
436 9 : WRITE (u, '(T2,A)') REPEAT('-', 79)
437 : END IF
438 :
439 44 : DO ispin = 1, bs_env%n_spin
440 :
441 26 : homo = bs_env%n_occ(ispin)
442 26 : band(1) = bs_env%eigenval_GW(homo, 1, ispin)
443 26 : band(2) = bs_env%eigenval_GW(homo + 1, 1, ispin)
444 26 : band(3) = band(2) - band(1)
445 :
446 26 : IF (i_iter > 1) THEN
447 80 : max_delta = MAX(max_delta, MAXVAL(ABS(band(:) - band_prev(:, ispin))))
448 : END IF
449 :
450 26 : IF (u > 0) THEN
451 13 : IF (bs_env%n_spin == 2) WRITE (u, '(T2,A,I0)') 'Spin ', ispin
452 13 : WRITE (u, '(T2,A,T61,F20.3)') 'evGW0 HOMO (eV)', band(1)*evolt
453 13 : WRITE (u, '(T2,A,T61,F20.3)') 'evGW0 LUMO (eV)', band(2)*evolt
454 13 : WRITE (u, '(T2,A,T61,F20.3)') 'evGW0 HOMO-LUMO gap (eV)', band(3)*evolt
455 : END IF
456 :
457 122 : band_prev(:, ispin) = band(:)
458 :
459 : END DO
460 :
461 18 : IF (i_iter > 1) THEN
462 14 : converged = (max_delta < bs_env%ri_rs%evgw0_eps_iter)
463 21 : IF (u > 0) WRITE (u, '(T2,A,T61,F20.6)') 'Max. change to previous cycle (eV)', &
464 14 : max_delta*evolt
465 : END IF
466 :
467 11 : IF (u > 0) WRITE (u, '(T2,A)') REPEAT('-', 79)
468 :
469 18 : IF (converged) THEN
470 4 : IF (u > 0) THEN
471 2 : WRITE (u, '(A)') ' '
472 : WRITE (u, '(T2,A,I4,A)') &
473 2 : 'evGW0 eigenvalue self-consistency reached in', i_iter, ' cycles.'
474 2 : WRITE (u, '(A)') ' '
475 : END IF
476 14 : ELSE IF (i_iter == n_iter) THEN
477 : CALL cp_warn(__LOCATION__, &
478 : "The evGW0 eigenvalue self-consistency cycle did not converge "// &
479 : "within MAX_ITER cycles. The reported quasiparticle energies are "// &
480 0 : "those of the last cycle.")
481 : END IF
482 :
483 18 : CALL timestop(handle)
484 :
485 18 : END SUBROUTINE print_evGW0_band_edges
486 :
487 : ! **************************************************************************************************
488 : !> \brief Compute per-atom AO and RI basis radii from the most diffuse Gaussian
489 : !> primitive in the AO ("ORB") and RI auxiliary ("RI_AUX") basis sets.
490 : !> Stores results in bs_env%ri_rs%radius_ao_per_atom(:) and
491 : !> bs_env%ri_rs%radius_ri_per_atom(:) and prints a per-atom table.
492 : !> Radius: r_kind = sqrt(-log(eps) / alpha_min_kind)
493 : !> with eps = eps_filter.
494 : !> \param qs_env ...
495 : !> \param bs_env ...
496 : ! **************************************************************************************************
497 46 : SUBROUTINE precompute_ri_rs_radii(qs_env, bs_env)
498 :
499 : TYPE(qs_environment_type), POINTER :: qs_env
500 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
501 :
502 : CHARACTER(LEN=*), PARAMETER :: routineN = 'precompute_ri_rs_radii'
503 :
504 : INTEGER :: handle, i, iatom, ikind, j, natom, nkind
505 46 : INTEGER, ALLOCATABLE, DIMENSION(:) :: kind_of
506 : REAL(KIND=dp) :: eps
507 46 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: alpha_min_ao_kind, alpha_min_ri_kind
508 46 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: zet_ao, zet_ri
509 46 : TYPE(atomic_kind_type), DIMENSION(:), POINTER :: atomic_kind_set
510 46 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
511 :
512 46 : CALL timeset(routineN, handle)
513 :
514 : CALL get_qs_env(qs_env, nkind=nkind, atomic_kind_set=atomic_kind_set, &
515 46 : particle_set=particle_set)
516 46 : natom = bs_env%n_atom
517 :
518 46 : eps = bs_env%eps_filter
519 :
520 184 : ALLOCATE (alpha_min_ao_kind(nkind), alpha_min_ri_kind(nkind))
521 112 : alpha_min_ao_kind = HUGE(1.0_dp)
522 112 : alpha_min_ri_kind = HUGE(1.0_dp)
523 :
524 112 : DO ikind = 1, nkind
525 66 : zet_ao => bs_env%basis_set_AO(ikind)%gto_basis_set%zet
526 66 : zet_ri => bs_env%basis_set_RI(ikind)%gto_basis_set%zet
527 :
528 220 : DO i = 1, SIZE(zet_ao, 1)
529 512 : DO j = 1, SIZE(zet_ao, 2)
530 446 : IF (zet_ao(i, j) > 1.0E-3_dp) THEN
531 292 : alpha_min_ao_kind(ikind) = MIN(alpha_min_ao_kind(ikind), zet_ao(i, j))
532 : END IF
533 : END DO
534 : END DO
535 178 : DO i = 1, SIZE(zet_ri, 1)
536 580 : DO j = 1, SIZE(zet_ri, 2)
537 514 : IF (zet_ri(i, j) > 1.0E-3_dp) THEN
538 448 : alpha_min_ri_kind(ikind) = MIN(alpha_min_ri_kind(ikind), zet_ri(i, j))
539 : END IF
540 : END DO
541 : END DO
542 : END DO
543 :
544 46 : CALL get_atomic_kind_set(atomic_kind_set=atomic_kind_set, kind_of=kind_of)
545 :
546 138 : ALLOCATE (bs_env%ri_rs%radius_ao_per_atom(natom))
547 92 : ALLOCATE (bs_env%ri_rs%radius_ri_per_atom(natom))
548 158 : DO iatom = 1, natom
549 112 : ikind = kind_of(iatom)
550 112 : bs_env%ri_rs%radius_ao_per_atom(iatom) = SQRT(-LOG(eps)/alpha_min_ao_kind(ikind))
551 158 : bs_env%ri_rs%radius_ri_per_atom(iatom) = SQRT(-LOG(eps)/alpha_min_ri_kind(ikind))
552 : END DO
553 :
554 46 : IF (bs_env%unit_nr > 0) THEN
555 23 : WRITE (bs_env%unit_nr, '(T2,A)') 'RI-RS basis radii (Å):'
556 23 : WRITE (bs_env%unit_nr, '(T4,A6,2X,A4,2A14)') 'Kind', 'Elem', 'r_AO (Å)', 'r_RI (Å)'
557 56 : DO ikind = 1, nkind
558 : WRITE (bs_env%unit_nr, '(T4,I6,2X,A4,2F14.4)') &
559 33 : ikind, &
560 33 : atomic_kind_set(ikind)%element_symbol, &
561 33 : SQRT(-LOG(eps)/alpha_min_ao_kind(ikind))*angstrom, &
562 89 : SQRT(-LOG(eps)/alpha_min_ri_kind(ikind))*angstrom
563 : END DO
564 23 : WRITE (bs_env%unit_nr, '(A)') ' '
565 : END IF
566 :
567 46 : DEALLOCATE (alpha_min_ao_kind, alpha_min_ri_kind, kind_of)
568 :
569 46 : CALL timestop(handle)
570 :
571 46 : END SUBROUTINE precompute_ri_rs_radii
572 :
573 : ! **************************************************************************************************
574 : !> \brief Spreads the low 21 bits of a into every third bit (bits 0,3,6,...,60): the 1-D helper
575 : !> for a 3-D Morton (Z-order) code. Standard 64-bit magic-mask implementation.
576 : !> \param a value in [0, 2^21)
577 : !> \param x a with two zero bits inserted between consecutive input bits
578 : ! **************************************************************************************************
579 336 : SUBROUTINE morton_split3(a, x)
580 : INTEGER(KIND=int_8), INTENT(IN) :: a
581 : INTEGER(KIND=int_8), INTENT(OUT) :: x
582 :
583 336 : x = IAND(a, INT(z'1FFFFF', int_8))
584 336 : x = IAND(IOR(x, ISHFT(x, 32)), INT(z'1F00000000FFFF', int_8))
585 336 : x = IAND(IOR(x, ISHFT(x, 16)), INT(z'1F0000FF0000FF', int_8))
586 336 : x = IAND(IOR(x, ISHFT(x, 8)), INT(z'100F00F00F00F00F', int_8))
587 336 : x = IAND(IOR(x, ISHFT(x, 4)), INT(z'10C30C30C30C30C3', int_8))
588 336 : x = IAND(IOR(x, ISHFT(x, 2)), INT(z'1249249249249249', int_8))
589 336 : END SUBROUTINE morton_split3
590 :
591 : ! **************************************************************************************************
592 : !> \brief Returns a permutation of atom indices in Morton (Z-order) space-filling order of their
593 : !> Cartesian centers, so consecutive atoms are spatial neighbors. The RI-RS grid rows are
594 : !> laid down in this order, so a contiguous grid panel maps to a compact spatial region and
595 : !> the CUTOFF_RADIUS_RL_W neighborhood of every panel shrinks. The grid row index
596 : !> is a summed contraction index, so ANY permutation is result-preserving; this one is
597 : !> chosen purely to improve locality. Coordinates are normalized to the atom bounding box
598 : !> and quantized to 21 bits per axis (sub-picometre for any real cell).
599 : !> \param particle_set ...
600 : !> \param order order(i) = atom index placed at layout position i
601 : ! **************************************************************************************************
602 46 : SUBROUTINE spatial_atom_order(particle_set, order)
603 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
604 : INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: order
605 :
606 : CHARACTER(LEN=*), PARAMETER :: routineN = 'spatial_atom_order'
607 : INTEGER, PARAMETER :: nbits = 21
608 :
609 : INTEGER :: handle, ia, k, natom
610 : INTEGER(KIND=int_8) :: cmax, ic(3), m1, m2, m3
611 46 : INTEGER(KIND=int_8), ALLOCATABLE :: mcode(:)
612 : REAL(KIND=dp) :: hi(3), lo(3), span(3)
613 :
614 46 : CALL timeset(routineN, handle)
615 :
616 46 : natom = SIZE(particle_set)
617 230 : ALLOCATE (order(natom), mcode(natom))
618 : cmax = ISHFT(1_int_8, nbits) - 1_int_8
619 :
620 184 : lo(:) = HUGE(1.0_dp)
621 184 : hi(:) = -HUGE(1.0_dp)
622 158 : DO ia = 1, natom
623 494 : DO k = 1, 3
624 336 : lo(k) = MIN(lo(k), particle_set(ia)%r(k))
625 448 : hi(k) = MAX(hi(k), particle_set(ia)%r(k))
626 : END DO
627 : END DO
628 184 : span(:) = hi(:) - lo(:)
629 184 : DO k = 1, 3
630 184 : IF (span(k) <= 0.0_dp) span(k) = 1.0_dp
631 : END DO
632 :
633 158 : DO ia = 1, natom
634 448 : DO k = 1, 3
635 336 : ic(k) = INT(((particle_set(ia)%r(k) - lo(k))/span(k))*REAL(cmax, dp), int_8)
636 448 : ic(k) = MIN(cmax, MAX(0_int_8, ic(k)))
637 : END DO
638 112 : CALL morton_split3(ic(1), m1)
639 112 : CALL morton_split3(ic(2), m2)
640 112 : CALL morton_split3(ic(3), m3)
641 158 : mcode(ia) = IOR(IOR(m1, ISHFT(m2, 1)), ISHFT(m3, 2))
642 : END DO
643 :
644 : ! sort(mcode, natom, order): order(i) = original atom index with the i-th smallest code
645 46 : CALL sort(mcode, natom, order)
646 :
647 46 : DEALLOCATE (mcode)
648 :
649 46 : CALL timestop(handle)
650 :
651 46 : END SUBROUTINE spatial_atom_order
652 :
653 : ! **************************************************************************************************
654 : !> \brief Compute grid points for RI-RS, two options:
655 : !> 1) Read tabulated atomic grids by Duchemin & Blase:
656 : !> JCP 150, 174120 (2019), JCTC 17, 2383 (2021)
657 : !> 2) Optimize grid for specific molecule / unit cell, start from tabulated grid
658 : !> \param qs_env ...
659 : !> \param bs_env ...
660 : !> \param ri_rs_grid_points ...
661 : ! **************************************************************************************************
662 46 : SUBROUTINE ri_rs_grid_assembler(qs_env, bs_env, ri_rs_grid_points)
663 :
664 : TYPE(qs_environment_type), POINTER :: qs_env
665 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
666 : REAL(KIND=dp), ALLOCATABLE, INTENT(OUT) :: ri_rs_grid_points(:, :)
667 :
668 : CHARACTER(LEN=*), PARAMETER :: routineN = 'ri_rs_grid_assembler'
669 :
670 : INTEGER :: handle
671 46 : TYPE(atomic_kind_type), DIMENSION(:), POINTER :: atomic_kind_set
672 46 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
673 :
674 46 : CALL timeset(routineN, handle)
675 :
676 : !! Get the information about the atoms in the system
677 46 : CALL get_qs_env(qs_env, atomic_kind_set=atomic_kind_set, particle_set=particle_set)
678 :
679 : !! 1. Read the tabulated atomic grids into cache
680 46 : CALL build_grid_cache(bs_env, atomic_kind_set)
681 :
682 : !! 2. Optimize RIRS grid points
683 46 : IF (bs_env%ri_rs%grid_opt%enabled) THEN
684 8 : CALL optimize_ri_rs_grid(qs_env, bs_env)
685 : END IF
686 :
687 : !! 3. Assemble and store the global molecular grid
688 46 : CALL assemble_RI_RS_grid_points(bs_env, particle_set, ri_rs_grid_points)
689 :
690 : !! 4. Release the atom-relative grid cache
691 46 : CALL release_RI_RS_grid_cache(bs_env)
692 :
693 46 : CALL timestop(handle)
694 :
695 46 : END SUBROUTINE ri_rs_grid_assembler
696 :
697 : ! **************************************************************************************************
698 : !> \brief Assemble the global RI-RS grid in spatial atom order from atom-relative cached grids.
699 : !> For point l on atom A, r_l = R_A + Δr_l^(A).
700 : !> \param bs_env GW environment containing the cached grids and receiving the atom boundaries.
701 : !> \param particle_set Atomic positions R_A.
702 : !> \param ri_rs_grid_points Global Cartesian grid coordinates in spatial atom order.
703 : ! **************************************************************************************************
704 46 : SUBROUTINE assemble_RI_RS_grid_points(bs_env, particle_set, ri_rs_grid_points)
705 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
706 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
707 : REAL(KIND=dp), ALLOCATABLE, INTENT(OUT) :: ri_rs_grid_points(:, :)
708 :
709 : INTEGER :: atom, atom_grid_end, atom_grid_start, &
710 : layout_position, natom, total_grid_npts
711 46 : INTEGER, ALLOCATABLE :: atom_grid_offsets(:), atom_order(:)
712 : REAL(KIND=dp) :: atom_center(3)
713 :
714 46 : natom = bs_env%n_atom
715 46 : CALL spatial_atom_order(particle_set, atom_order)
716 :
717 : ! grid_atom_boundaries(k) starts the k-th atom's run in spatial layout order.
718 : ! atom_grid_offsets(A) locates atom A in the resulting global coordinate array.
719 230 : ALLOCATE (bs_env%ri_rs%grid_atom_boundaries(natom + 1), atom_grid_offsets(natom))
720 46 : total_grid_npts = 0
721 158 : DO layout_position = 1, natom
722 112 : atom = atom_order(layout_position)
723 112 : atom_grid_offsets(atom) = total_grid_npts + 1
724 112 : bs_env%ri_rs%grid_atom_boundaries(layout_position) = total_grid_npts + 1
725 158 : total_grid_npts = total_grid_npts + bs_env%ri_rs%grid_cache(atom)%npts
726 : END DO
727 46 : bs_env%ri_rs%grid_atom_boundaries(natom + 1) = total_grid_npts + 1
728 :
729 46 : IF (bs_env%unit_nr > 0) THEN
730 : WRITE (bs_env%unit_nr, FMT="(T2,A,T69,I12)") &
731 23 : 'Total grid points used for RI-RS:', total_grid_npts
732 23 : WRITE (bs_env%unit_nr, "(A)") ' '
733 : END IF
734 :
735 138 : ALLOCATE (ri_rs_grid_points(3, total_grid_npts))
736 : !$OMP PARALLEL DO DEFAULT(NONE) &
737 : !$OMP SHARED(ri_rs_grid_points, atom_grid_offsets, particle_set, bs_env, natom) &
738 : !$OMP PRIVATE(atom, atom_center, atom_grid_start, atom_grid_end) &
739 46 : !$OMP SCHEDULE(DYNAMIC, 1)
740 : DO atom = 1, natom
741 : atom_center(:) = particle_set(atom)%r(:)
742 : atom_grid_start = atom_grid_offsets(atom)
743 : atom_grid_end = atom_grid_start + bs_env%ri_rs%grid_cache(atom)%npts - 1
744 :
745 : ! Translate the cached atom-relative points Δr_l^(A) to r_l = R_A + Δr_l^(A).
746 : ri_rs_grid_points(1, atom_grid_start:atom_grid_end) = &
747 : bs_env%ri_rs%grid_cache(atom)%raw_points(1, :) + atom_center(1)
748 : ri_rs_grid_points(2, atom_grid_start:atom_grid_end) = &
749 : bs_env%ri_rs%grid_cache(atom)%raw_points(2, :) + atom_center(2)
750 : ri_rs_grid_points(3, atom_grid_start:atom_grid_end) = &
751 : bs_env%ri_rs%grid_cache(atom)%raw_points(3, :) + atom_center(3)
752 : END DO
753 : !$OMP END PARALLEL DO
754 46 : END SUBROUTINE assemble_RI_RS_grid_points
755 :
756 : ! **************************************************************************************************
757 : !> \brief Release the atom-relative RI-RS grid cache after assembling the global molecular grid.
758 : !> \param bs_env GW environment containing the grid cache.
759 : ! **************************************************************************************************
760 46 : SUBROUTINE release_RI_RS_grid_cache(bs_env)
761 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
762 :
763 : INTEGER :: atom
764 :
765 158 : DO atom = 1, SIZE(bs_env%ri_rs%grid_cache)
766 158 : DEALLOCATE (bs_env%ri_rs%grid_cache(atom)%raw_points)
767 : END DO
768 158 : DEALLOCATE (bs_env%ri_rs%grid_cache)
769 46 : END SUBROUTINE release_RI_RS_grid_cache
770 :
771 : ! **************************************************************************************************
772 : !> \brief Reads grids from .ion files and stores them in memory based on grid_select
773 : !> \param bs_env ...
774 : !> \param atomic_kind_set ...
775 : ! **************************************************************************************************
776 46 : SUBROUTINE build_grid_cache(bs_env, atomic_kind_set)
777 :
778 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
779 : TYPE(atomic_kind_type), DIMENSION(:), POINTER :: atomic_kind_set
780 :
781 : CHARACTER(LEN=*), PARAMETER :: routineN = 'build_grid_cache'
782 :
783 : CHARACTER(LEN=default_path_length) :: full_path, warning_msg
784 : CHARACTER(LEN=default_string_length) :: atom_sym
785 : INTEGER :: handle, iatom, ikind, &
786 : n_voronoi_candidates, natom, nkind, &
787 : requested_npts
788 46 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
789 46 : TYPE(rirs_grid_type), ALLOCATABLE :: kind_grid(:)
790 :
791 46 : CALL timeset(routineN, handle)
792 :
793 46 : nkind = SIZE(atomic_kind_set)
794 46 : particle_set => bs_env%ri_rs%particle_set
795 46 : CPASSERT(ASSOCIATED(particle_set))
796 46 : natom = bs_env%n_atom
797 204 : ALLOCATE (kind_grid(nkind))
798 :
799 112 : DO ikind = 1, nkind
800 66 : atom_sym = TRIM(atomic_kind_set(ikind)%element_symbol)
801 : CALL get_rirs_grid_filepath(atom_sym, bs_env%ri_rs%grid_select, &
802 66 : bs_env%ri_rs%grid_file_suffix, full_path)
803 66 : CALL read_rirs_grid_file(TRIM(full_path), kind_grid(ikind)%raw_points)
804 112 : kind_grid(ikind)%npts = SIZE(kind_grid(ikind)%raw_points, 2)
805 : END DO
806 :
807 : ! Make every atom own an independent copy. The optimizer can therefore adapt symmetry-
808 : ! equivalent atoms to their individual local environments, and GW consumes those copies
809 : ! directly without writing intermediate .ion files.
810 46 : IF (ALLOCATED(bs_env%ri_rs%grid_cache)) DEALLOCATE (bs_env%ri_rs%grid_cache)
811 250 : ALLOCATE (bs_env%ri_rs%grid_cache(natom))
812 158 : DO iatom = 1, natom
813 112 : ikind = particle_set(iatom)%atomic_kind%kind_number
814 112 : bs_env%ri_rs%grid_cache(iatom) = kind_grid(ikind)
815 158 : IF (bs_env%ri_rs%grid_opt%enabled) THEN
816 24 : atom_sym = TRIM(atomic_kind_set(ikind)%element_symbol)
817 24 : requested_npts = bs_env%ri_rs%grid_opt%grid_npoints(ikind)
818 : CALL initialize_rirs_grid(bs_env%ri_rs%grid_cache(iatom)%raw_points, &
819 : requested_npts, iatom, particle_set, &
820 24 : n_voronoi_candidates)
821 24 : IF (n_voronoi_candidates < requested_npts) THEN
822 : WRITE (warning_msg, '(A,I0,A,A,A,I0,A,I0,A)') &
823 0 : "Atom ", iatom, " (", TRIM(atom_sym), "): Grid selection requests ", requested_npts, &
824 0 : " points, but only ", n_voronoi_candidates, &
825 0 : " are inside its Voronoi volume. Selecting from the full RI-RS source grid."
826 0 : CALL cp_warn(__LOCATION__, TRIM(warning_msg))
827 : END IF
828 24 : bs_env%ri_rs%grid_cache(iatom)%npts = requested_npts
829 : END IF
830 : END DO
831 112 : DEALLOCATE (kind_grid)
832 :
833 46 : CALL timestop(handle)
834 :
835 46 : END SUBROUTINE build_grid_cache
836 :
837 : ! **************************************************************************************************
838 : !> \brief Evaluates the AO basis on the RI-RS grid and stores it as the sparse DBCSR matrix
839 : !> Φ_μl = Φ_μ(r_l) (rows = grid points in atom-aligned blocks of at most
840 : !> max_elements_per_block points, columns = one block per atom's full AO set).
841 : !> Grid points outside the reach of an atom's most
842 : !> diffuse Gaussian (or the CUTOFF_RADIUS_RL_AO) are skipped, and only blocks
843 : !> with at least one element > eps_filter are stored. This locality is the source of
844 : !> ALL grid-dimension sparsity used downstream. Also caches the atom centers and the
845 : !> per-chunk centroids needed by the optional CUTOFF_RADIUS_G_W / CUTOFF_RADIUS_RL_W
846 : !> operator truncations.
847 : !> \param qs_env ...
848 : !> \param bs_env ...
849 : !> \param ri_rs_grid_points ...
850 : !> \param mat_phi_mu_l ...
851 : ! **************************************************************************************************
852 46 : SUBROUTINE atomic_basis_at_grid_point(qs_env, bs_env, ri_rs_grid_points, mat_phi_mu_l)
853 :
854 : TYPE(qs_environment_type), POINTER :: qs_env
855 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
856 : REAL(KIND=dp), ALLOCATABLE, INTENT(INOUT) :: ri_rs_grid_points(:, :)
857 : TYPE(dbcsr_type), INTENT(OUT) :: mat_phi_mu_l
858 :
859 : CHARACTER(LEN=*), PARAMETER :: routineN = 'atomic_basis_at_grid_point'
860 :
861 : INTEGER :: bs_eff, c_size, dimen_ORB, handle, i, i_blk, ia, iatom, natom, npcol, nprow, &
862 : num_grid_chunks, r_end, r_start, remaining, run, safe_max, total_grid_npts
863 46 : INTEGER, ALLOCATABLE, DIMENSION(:) :: blk_row_start, first_sgf
864 46 : INTEGER, DIMENSION(:), POINTER :: c_blk_sizes, col_dist, r_blk_sizes, &
865 46 : row_dist
866 : REAL(KIND=dp) :: r2_threshold
867 46 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :) :: atom_col_buffer
868 46 : TYPE(atomic_kind_type), DIMENSION(:), POINTER :: atomic_kind_set
869 : TYPE(cell_type), POINTER :: cell
870 : TYPE(dbcsr_distribution_type) :: dist
871 : TYPE(dbcsr_distribution_type), POINTER :: dbcsr_dist_ks
872 : TYPE(mp_para_env_type), POINTER :: para_env
873 46 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
874 46 : TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kind_set
875 :
876 46 : CALL timeset(routineN, handle)
877 :
878 : ! Extract environment variables
879 : CALL get_qs_env(qs_env, cell=cell, atomic_kind_set=atomic_kind_set, &
880 : qs_kind_set=qs_kind_set, particle_set=particle_set, &
881 46 : para_env=para_env)
882 :
883 46 : natom = bs_env%n_atom
884 46 : total_grid_npts = SIZE(ri_rs_grid_points, 2)
885 :
886 : ! Map the starting indices of spherical gaussian functions (SGF) for each atom
887 138 : ALLOCATE (first_sgf(natom + 1))
888 46 : CALL get_basis_offsets(particle_set, qs_kind_set, first_sgf, dimen_ORB)
889 :
890 : ! =========================================================================
891 : ! 1. SETUP DBCSR MATRIX TOPOLOGY
892 : ! =========================================================================
893 :
894 : ! A. Define Column Block Sizes (1 Block = 1 Atom's full basis set)
895 138 : ALLOCATE (c_blk_sizes(natom))
896 158 : DO iatom = 1, natom
897 158 : c_blk_sizes(iatom) = first_sgf(iatom + 1) - first_sgf(iatom)
898 : END DO
899 :
900 : ! B. Define Row Block Sizes: atom-aligned blocks (a block never spans two atoms' grid
901 : ! runs), each atom's run subdivided into blocks of at most bs_eff points.
902 :
903 : ! Fetch CP2K's default process grid configuration
904 46 : CALL get_qs_env(qs_env, dbcsr_dist=dbcsr_dist_ks)
905 46 : CALL dbcsr_distribution_get(dbcsr_dist_ks, nprows=nprow, npcols=npcol)
906 :
907 : ! Overflow-safe upper bound on the block size (see bs_env%dbcsr_msg_elem_limit).
908 : safe_max = INT(0.5_dp*REAL(bs_env%dbcsr_msg_elem_limit, dp)* &
909 : REAL(MAX(MIN(nprow, npcol), 1), dp)/ &
910 46 : REAL(total_grid_npts, dp))
911 46 : safe_max = MAX(1, safe_max)
912 : ! Block size = CP2K's global max_elements_per_block (GLOBAL/DBCSR input; default 32),
913 : ! overflow-capped.
914 46 : bs_eff = MAX(1, MIN(max_elements_per_block, safe_max))
915 :
916 : ! Count the atom-aligned blocks, then fill r_blk_sizes and each block's starting grid row.
917 46 : num_grid_chunks = 0
918 158 : DO ia = 1, natom
919 112 : run = bs_env%ri_rs%grid_atom_boundaries(ia + 1) - bs_env%ri_rs%grid_atom_boundaries(ia)
920 158 : IF (run > 0) num_grid_chunks = num_grid_chunks + (run + bs_eff - 1)/bs_eff
921 : END DO
922 184 : ALLOCATE (r_blk_sizes(num_grid_chunks), blk_row_start(num_grid_chunks))
923 46 : i_blk = 0
924 46 : r_start = 1
925 158 : DO ia = 1, natom
926 112 : remaining = bs_env%ri_rs%grid_atom_boundaries(ia + 1) - bs_env%ri_rs%grid_atom_boundaries(ia)
927 760 : DO WHILE (remaining > 0)
928 602 : i_blk = i_blk + 1
929 602 : r_blk_sizes(i_blk) = MIN(bs_eff, remaining)
930 602 : blk_row_start(i_blk) = r_start
931 602 : r_start = r_start + r_blk_sizes(i_blk)
932 714 : remaining = remaining - r_blk_sizes(i_blk)
933 : END DO
934 : END DO
935 :
936 46 : IF (bs_env%unit_nr > 0) THEN
937 : ! T71 compensates for the two two-byte Greek characters in the label.
938 : WRITE (bs_env%unit_nr, '(T2,A,T71,I12)') &
939 23 : 'RI-RS grid row-blocks of Φ_μ(r_l)', num_grid_chunks
940 23 : WRITE (bs_env%unit_nr, '(T2,A,T69,I12)') 'RI-RS grid points per block (max)', bs_eff
941 : END IF
942 :
943 : ! Cache atomic positions: AO and RI blocks are one-block-per-atom, so these are the
944 : ! block centers used by the optional CUTOFF_RADIUS_G_W atom-pair truncation.
945 46 : IF (ALLOCATED(bs_env%ri_rs%atom_centers)) DEALLOCATE (bs_env%ri_rs%atom_centers)
946 138 : ALLOCATE (bs_env%ri_rs%atom_centers(3, natom))
947 158 : DO iatom = 1, natom
948 942 : bs_env%ri_rs%atom_centers(1:3, iatom) = particle_set(iatom)%r(1:3)
949 : END DO
950 :
951 : ! Cache per-chunk centroids for the optional CUTOFF_RADIUS_RL_W / CUTOFF_RADIUS_W0 block
952 : ! truncations. Left unallocated otherwise, so PRESENT(centroids) stays .FALSE. at the
953 : ! contract_grid_panels* calls.
954 46 : IF (bs_env%ri_rs%cutoff_radius_v_w > 0.0_dp .OR. &
955 : bs_env%ri_rs%cutoff_radius_w0 > 0.0_dp) THEN
956 8 : IF (ALLOCATED(bs_env%ri_rs%chunk_centroids)) DEALLOCATE (bs_env%ri_rs%chunk_centroids)
957 24 : ALLOCATE (bs_env%ri_rs%chunk_centroids(3, num_grid_chunks))
958 110 : DO i_blk = 1, num_grid_chunks
959 102 : r_start = blk_row_start(i_blk)
960 102 : r_end = r_start + r_blk_sizes(i_blk) - 1
961 : bs_env%ri_rs%chunk_centroids(1, i_blk) = &
962 2950 : SUM(ri_rs_grid_points(1, r_start:r_end))/REAL(r_blk_sizes(i_blk), dp)
963 : bs_env%ri_rs%chunk_centroids(2, i_blk) = &
964 2950 : SUM(ri_rs_grid_points(2, r_start:r_end))/REAL(r_blk_sizes(i_blk), dp)
965 : bs_env%ri_rs%chunk_centroids(3, i_blk) = &
966 2996 : SUM(ri_rs_grid_points(3, r_start:r_end))/REAL(r_blk_sizes(i_blk), dp)
967 : END DO
968 : END IF
969 :
970 : ! C. Build Custom Mappings using Round-Robin across the 2D process grid
971 :
972 92 : ALLOCATE (row_dist(num_grid_chunks))
973 648 : DO i = 1, num_grid_chunks
974 648 : row_dist(i) = MOD(i - 1, nprow)
975 : END DO
976 :
977 92 : ALLOCATE (col_dist(natom))
978 158 : DO i = 1, natom
979 158 : col_dist(i) = MOD(i - 1, npcol)
980 : END DO
981 :
982 : ! E. Create the DBCSR Distribution and Initialize the Matrix
983 : CALL dbcsr_distribution_new(dist, template=dbcsr_dist_ks, &
984 46 : row_dist=row_dist, col_dist=col_dist)
985 :
986 : CALL dbcsr_create(mat_phi_mu_l, name="phi_val_sparse", dist=dist, &
987 : matrix_type=dbcsr_type_no_symmetry, &
988 46 : row_blk_size=r_blk_sizes, col_blk_size=c_blk_sizes)
989 :
990 : ! =========================================================================
991 : ! 2. STREAM DATA DIRECTLY INTO SPARSE MATRIX
992 : ! =========================================================================
993 : ! Iterate over the atoms assigned to this specific MPI rank
994 46 : DO iatom = para_env%mepos + 1, natom, para_env%num_pe
995 :
996 56 : c_size = c_blk_sizes(iatom)
997 :
998 : ! Allocate a temporary dense buffer just for this specific atom
999 224 : ALLOCATE (atom_col_buffer(total_grid_npts, c_size))
1000 56 : atom_col_buffer = 0.0_dp
1001 :
1002 : ! Evaluate the basis functions on the grid. Skip grid points outside
1003 : ! the spatial extent of the most diffuse AO Gaussian on iatom; beyond
1004 : ! that radius the contribution is guaranteed below eps_filter. A positive
1005 : ! CUTOFF_RADIUS_RL_AO overrides this with a user-defined hard cutoff.
1006 56 : IF (bs_env%ri_rs%cutoff_radius_ri_ao > 0.0_dp) THEN
1007 3 : r2_threshold = bs_env%ri_rs%cutoff_radius_ri_ao**2
1008 : ELSE
1009 53 : r2_threshold = bs_env%ri_rs%radius_ao_per_atom(iatom)**2
1010 : END IF
1011 : CALL fill_phi_for_atom(atom_col_buffer, ri_rs_grid_points, total_grid_npts, &
1012 56 : iatom, particle_set, qs_kind_set, cell, r2_threshold)
1013 :
1014 : ! Slice the dense column into the atom-aligned grid row-blocks and insert into DBCSR
1015 801 : DO i_blk = 1, num_grid_chunks
1016 745 : r_start = blk_row_start(i_blk)
1017 745 : r_end = r_start + r_blk_sizes(i_blk) - 1
1018 :
1019 : ! Apply dynamic sparsity filtering: Only store blocks with physical significance
1020 82176 : IF (MAXVAL(ABS(atom_col_buffer(r_start:r_end, 1:c_size))) > bs_env%eps_filter) THEN
1021 : CALL dbcsr_put_block(mat_phi_mu_l, row=i_blk, col=iatom, &
1022 733 : block=atom_col_buffer(r_start:r_end, 1:c_size))
1023 : END IF
1024 : END DO
1025 :
1026 56 : DEALLOCATE (atom_col_buffer)
1027 :
1028 : END DO
1029 :
1030 46 : CALL dbcsr_finalize(mat_phi_mu_l)
1031 :
1032 46 : IF (bs_env%unit_nr > 0) WRITE (bs_env%unit_nr, '(A)') ' '
1033 46 : CALL print_matrix_occupation(mat_phi_mu_l, 'Φ_μ(r_l)', bs_env)
1034 :
1035 : ! -------------------------------------------------------------------------
1036 : ! CLEANUP
1037 : ! -------------------------------------------------------------------------
1038 46 : DEALLOCATE (first_sgf, r_blk_sizes, c_blk_sizes, row_dist, col_dist, blk_row_start)
1039 46 : CALL dbcsr_distribution_release(dist)
1040 :
1041 46 : CALL timestop(handle)
1042 :
1043 230 : END SUBROUTINE atomic_basis_at_grid_point
1044 :
1045 : ! **************************************************************************************************
1046 : !> \brief Computes the AO basis offsets: first_sgf(iatom) is the global index of the first
1047 : !> spherical Gaussian function (SGF) of iatom, first_sgf(natom+1) = total_sgf + 1,
1048 : !> and total_sgf is the total number of AO basis functions.
1049 : !> \param particle_set ...
1050 : !> \param qs_kind_set ...
1051 : !> \param first_sgf ...
1052 : !> \param total_sgf ...
1053 : ! **************************************************************************************************
1054 46 : SUBROUTINE get_basis_offsets(particle_set, qs_kind_set, first_sgf, total_sgf)
1055 :
1056 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
1057 : TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kind_set
1058 : INTEGER, INTENT(OUT) :: first_sgf(:), total_sgf
1059 :
1060 : CHARACTER(LEN=*), PARAMETER :: routineN = 'get_basis_offsets'
1061 :
1062 : INTEGER :: handle, iatom, ikind, nsgf
1063 :
1064 46 : CALL timeset(routineN, handle)
1065 :
1066 46 : total_sgf = 0
1067 158 : DO iatom = 1, SIZE(particle_set)
1068 112 : first_sgf(iatom) = total_sgf + 1
1069 112 : ikind = particle_set(iatom)%atomic_kind%kind_number
1070 112 : CALL get_qs_kind(qs_kind_set(ikind), nsgf=nsgf, basis_type="ORB")
1071 158 : total_sgf = total_sgf + nsgf
1072 : END DO
1073 46 : first_sgf(SIZE(particle_set) + 1) = total_sgf + 1
1074 :
1075 46 : CALL timestop(handle)
1076 :
1077 46 : END SUBROUTINE get_basis_offsets
1078 :
1079 : ! **************************************************************************************************
1080 : !> \brief Computes χ_PQ(iτ) from the occupied and virtual Green's functions.
1081 : !> \param bs_env GW calculation environment and imaginary-time grid
1082 : !> \param mat_chi_Gamma_tau Response matrices χ_PQ(iτ) to be computed
1083 : !> \param mat_phi_mu_l AO values Φ_μ(r_l) on the RI-RS grid
1084 : !> \param mat_Z_lP RI-RS fitting matrix Z_lP
1085 : ! **************************************************************************************************
1086 38 : SUBROUTINE get_mat_chi_Gamma_tau(bs_env, mat_chi_Gamma_tau, mat_phi_mu_l, mat_Z_lP)
1087 :
1088 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1089 : TYPE(dbcsr_p_type), DIMENSION(:), POINTER :: mat_chi_Gamma_tau
1090 : TYPE(dbcsr_type), INTENT(INOUT) :: mat_phi_mu_l, mat_Z_lP
1091 :
1092 : CHARACTER(LEN=*), PARAMETER :: routineN = 'get_mat_chi_Gamma_tau'
1093 :
1094 : INTEGER :: handle, i_t, ispin, n_panels
1095 38 : INTEGER, ALLOCATABLE, DIMENSION(:) :: pan_first, pan_last
1096 : REAL(KIND=dp) :: grid_occ, t1, tau
1097 : TYPE(dbcsr_type) :: matrix_G_occ_ao, matrix_G_vir_ao
1098 :
1099 38 : CALL timeset(routineN, handle)
1100 :
1101 : ! Panel boundaries for the grid-streaming contraction.
1102 : ! The panels are identical for χ, Σ^x and Σ^c, so the count is reported once here for all three stages.
1103 :
1104 38 : CALL resolve_grid_panels(bs_env, mat_phi_mu_l, pan_first, pan_last)
1105 :
1106 38 : n_panels = SIZE(pan_first)
1107 38 : IF (bs_env%unit_nr > 0) THEN
1108 : WRITE (bs_env%unit_nr, '(T2,A,T74,I9)') &
1109 19 : 'Number of batches for χ, Σ matrices', n_panels
1110 19 : WRITE (bs_env%unit_nr, '(A)') ' '
1111 19 : CALL m_flush(bs_env%unit_nr)
1112 : END IF
1113 :
1114 : ! =========================================================================
1115 : ! IMAGINARY TIME LOOP
1116 : ! χ_PQ(iτ) = Σ_s g_s · Z^T ( (φ G^occ_s φ^T) ∘ (φ G^vir_s φ^T) ) Z
1117 : ! (g_s = spin degeneracy)
1118 : ! =========================================================================
1119 568 : DO i_t = 1, bs_env%num_time_freq_points
1120 530 : t1 = m_walltime()
1121 530 : tau = bs_env%time_frequency_grid%imaginary_time(i_t)
1122 :
1123 1140 : DO ispin = 1, bs_env%n_spin
1124 :
1125 : ! AO-space Green's functions G^occ_µν, G^vir_µν (dense AO x AO, small)
1126 610 : CALL build_G_ao(bs_env, tau, ispin, .TRUE., .FALSE., mat_phi_mu_l, matrix_G_occ_ao)
1127 610 : CALL build_G_ao(bs_env, tau, ispin, .FALSE., .TRUE., mat_phi_mu_l, matrix_G_vir_ao)
1128 :
1129 : ! χ_PQ += g_s · Z^T ( (φ G^occ φ^T) ∘ (φ G^vir φ^T) ) Z
1130 : CALL contract_grid_panels(L_A=mat_phi_mu_l, M_A=matrix_G_occ_ao, &
1131 : L_B=mat_phi_mu_l, M_B=matrix_G_vir_ao, &
1132 : L_out=mat_Z_lP, mat_out=mat_chi_Gamma_tau(i_t)%matrix, &
1133 : scale=bs_env%spin_degeneracy, eps=bs_env%eps_filter, &
1134 : para_env=bs_env%para_env, &
1135 : pan_first=pan_first, pan_last=pan_last, &
1136 : lb_eq_la=.TRUE., lout_eq_la=.FALSE., &
1137 : zero_out=(ispin == 1), &
1138 : keep_sparsity=bs_env%ri_rs%keep_sparsity_rirs, &
1139 : centroids=bs_env%ri_rs%chunk_centroids, &
1140 : cutoff=bs_env%ri_rs%cutoff_radius_v_w, &
1141 610 : grid_occupation=grid_occ)
1142 :
1143 610 : CALL dbcsr_release(matrix_G_occ_ao)
1144 1140 : CALL dbcsr_release(matrix_G_vir_ao)
1145 :
1146 : END DO ! ispin
1147 :
1148 : ! Sparsity reports
1149 530 : IF (i_t == 1) THEN
1150 38 : CALL print_matrix_occupation(mat_Z_lP, 'Z_lP', bs_env)
1151 38 : IF (bs_env%unit_nr > 0) THEN
1152 : WRITE (bs_env%unit_nr, '(T2,A,T73,F7.2,A)') &
1153 19 : 'Percentage of non-zero matrix elements in G_ll'', χ_ll'', W_ll''', &
1154 38 : grid_occ*100.0_dp, ' %'
1155 19 : CALL m_flush(bs_env%unit_nr)
1156 : END IF
1157 : CALL print_matrix_occupation(mat_chi_Gamma_tau(i_t)%matrix, 'χ_PQ', bs_env, &
1158 38 : suffix=' for time point 1')
1159 38 : IF (bs_env%unit_nr > 0) WRITE (bs_env%unit_nr, '(A)') ' '
1160 : END IF
1161 :
1162 568 : IF (bs_env%unit_nr > 0) THEN
1163 : WRITE (bs_env%unit_nr, '(T2,A,I13,A,I3,A,F7.1,A)') &
1164 265 : 'Computed χ(iτ,k=0) for time point', i_t, ' /', bs_env%num_time_freq_points, &
1165 530 : ', Execution time', m_walltime() - t1, ' s'
1166 : END IF
1167 :
1168 : END DO ! i_t
1169 :
1170 38 : IF (bs_env%unit_nr > 0) WRITE (bs_env%unit_nr, '(A)') ' '
1171 :
1172 38 : CALL timestop(handle)
1173 :
1174 76 : END SUBROUTINE get_mat_chi_Gamma_tau
1175 :
1176 : ! **************************************************************************************************
1177 : !> \brief Marks the grid blocks whose centroid lies within cutoff of the bounding box of the
1178 : !> panel [blk0, blk1]'s chunk centroids.
1179 : !> \param centroids ...
1180 : !> \param blk0 ...
1181 : !> \param blk1 ...
1182 : !> \param cutoff ...
1183 : !> \param used ...
1184 : ! **************************************************************************************************
1185 92 : SUBROUTINE mask_grid_blocks_near_panel(centroids, blk0, blk1, cutoff, used)
1186 :
1187 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: centroids
1188 : INTEGER, INTENT(IN) :: blk0, blk1
1189 : REAL(KIND=dp), INTENT(IN) :: cutoff
1190 : LOGICAL, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: used
1191 :
1192 : CHARACTER(LEN=*), PARAMETER :: routineN = 'mask_grid_blocks_near_panel'
1193 :
1194 : INTEGER :: c, handle, k
1195 : REAL(KIND=dp) :: cutoff2, d2, dx
1196 : REAL(KIND=dp), DIMENSION(3) :: hi, lo
1197 :
1198 92 : CALL timeset(routineN, handle)
1199 :
1200 92 : cutoff2 = cutoff**2
1201 92 : lo(:) = MINVAL(centroids(:, blk0:blk1), DIM=2)
1202 92 : hi(:) = MAXVAL(centroids(:, blk0:blk1), DIM=2)
1203 :
1204 276 : ALLOCATE (used(SIZE(centroids, 2)))
1205 2024 : DO c = 1, SIZE(centroids, 2)
1206 : d2 = 0.0_dp
1207 7728 : DO k = 1, 3
1208 5796 : dx = MAX(0.0_dp, lo(k) - centroids(k, c), centroids(k, c) - hi(k))
1209 7728 : d2 = d2 + dx*dx
1210 : END DO
1211 2024 : used(c) = (d2 <= cutoff2)
1212 : END DO
1213 :
1214 92 : CALL timestop(handle)
1215 :
1216 92 : END SUBROUTINE mask_grid_blocks_near_panel
1217 :
1218 : ! **************************************************************************************************
1219 : !> \brief Exact allocated-element count of the geo template of panel [blk0, blk1]: the very same
1220 : !> per-block-pair centroid test as build_geo_template_panel, so this is the true DBCSR
1221 : !> data size of A_pan/B_pan/C_pan (DBCSR stores whole blocks).
1222 : !> \param r_blk_sizes ...
1223 : !> \param centroids ...
1224 : !> \param used ...
1225 : !> \param blk0 ...
1226 : !> \param blk1 ...
1227 : !> \param cutoff ...
1228 : !> \param nze_tmpl ...
1229 : ! **************************************************************************************************
1230 8 : SUBROUTINE panel_template_elems(r_blk_sizes, centroids, used, blk0, blk1, cutoff, nze_tmpl)
1231 :
1232 : INTEGER, DIMENSION(:), INTENT(IN) :: r_blk_sizes
1233 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: centroids
1234 : LOGICAL, DIMENSION(:), INTENT(IN) :: used
1235 : INTEGER, INTENT(IN) :: blk0, blk1
1236 : REAL(KIND=dp), INTENT(IN) :: cutoff
1237 : INTEGER(KIND=int_8), INTENT(OUT) :: nze_tmpl
1238 :
1239 : CHARACTER(LEN=*), PARAMETER :: routineN = 'panel_template_elems'
1240 :
1241 : INTEGER :: c, handle, ib, n_used
1242 8 : INTEGER, ALLOCATABLE, DIMENSION(:) :: used_idx
1243 : REAL(KIND=dp) :: cutoff2
1244 :
1245 8 : CALL timeset(routineN, handle)
1246 :
1247 : ! Compress the near mask once so the pair loop only visits candidate columns.
1248 176 : n_used = COUNT(used)
1249 24 : ALLOCATE (used_idx(n_used))
1250 8 : n_used = 0
1251 176 : DO c = 1, SIZE(used)
1252 176 : IF (used(c)) THEN
1253 168 : n_used = n_used + 1
1254 168 : used_idx(n_used) = c
1255 : END IF
1256 : END DO
1257 :
1258 8 : cutoff2 = cutoff**2
1259 8 : nze_tmpl = 0_int_8
1260 : !$OMP PARALLEL DO DEFAULT(NONE) SHARED(blk0, blk1, n_used, used_idx, centroids, cutoff2, &
1261 8 : !$OMP r_blk_sizes) PRIVATE(ib, c) REDUCTION(+:nze_tmpl)
1262 : DO ib = blk0, blk1
1263 : DO c = 1, n_used
1264 : IF (SUM((centroids(:, ib) - centroids(:, used_idx(c)))**2) <= cutoff2) THEN
1265 : nze_tmpl = nze_tmpl + INT(r_blk_sizes(ib), int_8)*INT(r_blk_sizes(used_idx(c)), int_8)
1266 : END IF
1267 : END DO
1268 : END DO
1269 : !$OMP END PARALLEL DO
1270 :
1271 8 : CALL timestop(handle)
1272 :
1273 16 : END SUBROUTINE panel_template_elems
1274 :
1275 : ! **************************************************************************************************
1276 : !> \brief Per-rank peak memory (GB) of one panel step of the neighborhood-restricted
1277 : !> contractions: three grid x grid panels of the template size (A_pan, B_pan, C_pan)
1278 : !> plus the grid x RI intermediates (tmp2 and the accumulation operand) and the
1279 : !> grid x AO intermediate (tmpA), whose column support is the panel's geometric
1280 : !> neighborhood fraction f_near = width/n_grid. Shared by the panel planner and
1281 : !> \param nze_tmpl ...
1282 : !> \param pan_rows ...
1283 : !> \param width ...
1284 : !> \param n_grid_total ...
1285 : !> \param n_RI ...
1286 : !> \param n_ao ...
1287 : !> \param n_procs ...
1288 : !> \param mem_GB ...
1289 : ! **************************************************************************************************
1290 8 : SUBROUTINE panel_mem_estimate_GB(nze_tmpl, pan_rows, width, n_grid_total, n_RI, n_ao, &
1291 : n_procs, mem_GB)
1292 :
1293 : INTEGER(KIND=int_8), INTENT(IN) :: nze_tmpl
1294 : INTEGER, INTENT(IN) :: pan_rows, width, n_grid_total, n_RI, &
1295 : n_ao, n_procs
1296 : REAL(KIND=dp), INTENT(OUT) :: mem_GB
1297 :
1298 : REAL(KIND=dp) :: f_near
1299 :
1300 8 : f_near = REAL(width, dp)/REAL(MAX(n_grid_total, 1), dp)
1301 : mem_GB = (3.0_dp*REAL(nze_tmpl, dp) + &
1302 : REAL(pan_rows, dp)*f_near*(2.0_dp*REAL(n_RI, dp) + REAL(n_ao, dp)))* &
1303 8 : 8.0_dp/REAL(MAX(n_procs, 1), dp)*1.0E-9_dp
1304 :
1305 8 : END SUBROUTINE panel_mem_estimate_GB
1306 :
1307 : ! **************************************************************************************************
1308 : !> \brief Plans the panel boundaries for the streaming contractions. Panels grow by whole grid
1309 : !> row-blocks towards ~panel_size rows. When the neighborhood restriction is active
1310 : !> (centroids+cutoff), each candidate panel is additionally checked against
1311 : !> (a) the 32-bit message bound with the panel's TRUE occupancy
1312 : !> (b) the per-rank memory budget: panel_mem_estimate_GB <= mem_budget_GB.
1313 : !> \param bs_env ...
1314 : !> \param r_blk_sizes ...
1315 : !> \param panel_size ...
1316 : !> \param min_dim ...
1317 : !> \param pan_first ...
1318 : !> \param pan_last ...
1319 : !> \param centroids ...
1320 : !> \param cutoff ...
1321 : !> \param n_RI ...
1322 : !> \param n_ao ...
1323 : !> \param n_procs ...
1324 : !> \param mem_budget_GB ...
1325 : !> \param honor_exact ...
1326 : !> \param unsafe ...
1327 : ! **************************************************************************************************
1328 38 : SUBROUTINE plan_grid_panels(bs_env, r_blk_sizes, panel_size, min_dim, pan_first, pan_last, &
1329 38 : centroids, cutoff, n_RI, n_ao, n_procs, mem_budget_GB, &
1330 : honor_exact, unsafe)
1331 :
1332 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1333 : INTEGER, DIMENSION(:), INTENT(IN) :: r_blk_sizes
1334 : INTEGER, INTENT(IN) :: panel_size, min_dim
1335 : INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: pan_first, pan_last
1336 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN), &
1337 : OPTIONAL :: centroids
1338 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: cutoff
1339 : INTEGER, INTENT(IN), OPTIONAL :: n_RI, n_ao, n_procs
1340 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: mem_budget_GB
1341 : LOGICAL, INTENT(IN), OPTIONAL :: honor_exact
1342 : LOGICAL, INTENT(OUT), OPTIONAL :: unsafe
1343 :
1344 : CHARACTER(LEN=*), PARAMETER :: routineN = 'plan_grid_panels'
1345 :
1346 : INTEGER :: blk0, blk1, handle, ib, n_grid_blocks, &
1347 : n_grid_total, n_panels, rows_acc, &
1348 : TARGET, width
1349 : INTEGER(KIND=int_8) :: msg, nze_tmpl, side
1350 38 : INTEGER, ALLOCATABLE, DIMENSION(:) :: tmp_first, tmp_last
1351 : LOGICAL :: fits, my_honor_exact, my_unsafe, &
1352 : use_cutoff
1353 38 : LOGICAL, ALLOCATABLE, DIMENSION(:) :: used
1354 : REAL(KIND=dp) :: f_near, mem_GB
1355 :
1356 38 : CALL timeset(routineN, handle)
1357 :
1358 38 : use_cutoff = PRESENT(centroids) .AND. PRESENT(cutoff)
1359 38 : IF (use_cutoff) use_cutoff = cutoff > 0.0_dp
1360 2 : IF (use_cutoff) THEN
1361 2 : CPASSERT(PRESENT(n_RI) .AND. PRESENT(n_ao) .AND. PRESENT(n_procs))
1362 : END IF
1363 :
1364 : ! honor_exact: use N_PANELS as requested -- do NOT split a panel further even if it trips
1365 : ! the message-overflow / memory-budget check; instead flag `unsafe` so the caller can warn.
1366 38 : my_honor_exact = .FALSE.
1367 38 : IF (PRESENT(honor_exact)) my_honor_exact = honor_exact
1368 38 : my_unsafe = .FALSE.
1369 :
1370 38 : n_grid_blocks = SIZE(r_blk_sizes)
1371 556 : n_grid_total = SUM(r_blk_sizes)
1372 152 : ALLOCATE (tmp_first(n_grid_blocks), tmp_last(n_grid_blocks))
1373 :
1374 38 : n_panels = 0
1375 38 : blk0 = 1
1376 78 : DO WHILE (blk0 <= n_grid_blocks)
1377 40 : TARGET = panel_size
1378 0 : DO
1379 40 : rows_acc = 0
1380 40 : blk1 = blk0
1381 520 : DO ib = blk0, n_grid_blocks
1382 518 : rows_acc = rows_acc + r_blk_sizes(ib)
1383 518 : blk1 = ib
1384 520 : IF (rows_acc >= TARGET) EXIT
1385 : END DO
1386 40 : IF (.NOT. use_cutoff .OR. blk1 == blk0) EXIT
1387 4 : CALL mask_grid_blocks_near_panel(centroids, blk0, blk1, cutoff, used)
1388 88 : width = SUM(r_blk_sizes, MASK=used)
1389 4 : CALL panel_template_elems(r_blk_sizes, centroids, used, blk0, blk1, cutoff, nze_tmpl)
1390 4 : f_near = REAL(width, dp)/REAL(MAX(n_grid_total, 1), dp)
1391 4 : side = INT(REAL(rows_acc, dp)*f_near*REAL(MAX(n_RI, n_ao), dp), int_8)
1392 4 : msg = MAX(nze_tmpl, side)/INT(MAX(min_dim, 1), int_8)
1393 4 : fits = (msg <= bs_env%dbcsr_msg_elem_limit/4)
1394 4 : IF (fits .AND. PRESENT(mem_budget_GB)) THEN
1395 4 : IF (mem_budget_GB > 0.0_dp) THEN
1396 : CALL panel_mem_estimate_GB(nze_tmpl, rows_acc, width, n_grid_total, &
1397 4 : n_RI, n_ao, n_procs, mem_GB)
1398 4 : fits = (mem_GB <= mem_budget_GB)
1399 : END IF
1400 : END IF
1401 : ! Panel size is bounded only by the message-overflow and memory checks above; there is
1402 : ! no neighborhood-width (f_near) cap. mp_waitall is dominated by the NUMBER of panel
1403 : ! multiplies, so fewer/larger panels are cheaper here -- panel count is driven DOWN by
1404 : ! the N_PANELS keyword (panel_size), not split up by a width heuristic.
1405 4 : IF (my_honor_exact) THEN
1406 : ! Keep exactly the requested grouping; just record if it exceeds a safety limit.
1407 4 : IF (.NOT. fits) my_unsafe = .TRUE.
1408 : EXIT
1409 : END IF
1410 0 : IF (fits) EXIT
1411 36 : TARGET = MAX(1, MIN(TARGET, rows_acc)/2)
1412 : END DO
1413 40 : n_panels = n_panels + 1
1414 40 : tmp_first(n_panels) = blk0
1415 40 : tmp_last(n_panels) = blk1
1416 40 : blk0 = blk1 + 1
1417 : END DO
1418 :
1419 152 : ALLOCATE (pan_first(n_panels), pan_last(n_panels))
1420 78 : pan_first(:) = tmp_first(1:n_panels)
1421 78 : pan_last(:) = tmp_last(1:n_panels)
1422 38 : DEALLOCATE (tmp_first, tmp_last)
1423 :
1424 38 : IF (PRESENT(unsafe)) unsafe = my_unsafe
1425 :
1426 38 : CALL timestop(handle)
1427 :
1428 76 : END SUBROUTINE plan_grid_panels
1429 :
1430 : ! **************************************************************************************************
1431 : !> \brief Resolves the panel boundaries for the streaming contractions from the bs_env settings:
1432 : !> \param bs_env ...
1433 : !> \param mat_phi_mu_l ...
1434 : !> \param pan_first ...
1435 : !> \param pan_last ...
1436 : ! **************************************************************************************************
1437 166 : SUBROUTINE resolve_grid_panels(bs_env, mat_phi_mu_l, pan_first, pan_last)
1438 :
1439 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1440 : TYPE(dbcsr_type), INTENT(INOUT) :: mat_phi_mu_l
1441 : INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: pan_first, pan_last
1442 :
1443 : CHARACTER(LEN=*), PARAMETER :: routineN = 'resolve_grid_panels'
1444 :
1445 : CHARACTER(LEN=max_line_length) :: msg
1446 : INTEGER :: handle, min_dim, n_grid_total, &
1447 : n_panels_req, npcols, nprows, &
1448 : panel_size, safe_max
1449 166 : INTEGER, DIMENSION(:), POINTER :: r_blk_sizes
1450 : LOGICAL :: honor_exact, panels_unsafe, use_cutoff
1451 : REAL(KIND=dp) :: mem_avail_GB, mem_budget_GB
1452 : TYPE(dbcsr_distribution_type) :: dist
1453 :
1454 166 : CALL timeset(routineN, handle)
1455 :
1456 166 : IF (ALLOCATED(bs_env%ri_rs%pan_first)) THEN
1457 518 : ALLOCATE (pan_first, SOURCE=bs_env%ri_rs%pan_first)
1458 518 : ALLOCATE (pan_last, SOURCE=bs_env%ri_rs%pan_last)
1459 128 : CALL timestop(handle)
1460 : RETURN
1461 : END IF
1462 :
1463 : use_cutoff = bs_env%ri_rs%cutoff_radius_v_w > 0.0_dp .AND. &
1464 38 : ALLOCATED(bs_env%ri_rs%chunk_centroids)
1465 :
1466 : ! MIN(nprows, npcols) is the divisor that bounds the worst-rank Cannon message: a
1467 : ! P x n_grid panel is replicated into block row strips (P/nprows x n_grid) or column
1468 : ! strips (P x n_grid/npcols) during multiply_cannon, so the largest single-rank
1469 : ! message is ~ P*n_grid / MIN(nprows,npcols) elements.
1470 : CALL dbcsr_get_info(mat_phi_mu_l, nfullrows_total=n_grid_total, row_blk_size=r_blk_sizes, &
1471 38 : distribution=dist)
1472 38 : CALL dbcsr_distribution_get(dist, nprows=nprows, npcols=npcols)
1473 38 : min_dim = MAX(MIN(nprows, npcols), 1)
1474 :
1475 : ! Panel height such that NO per-rank DBCSR message can overflow the 32-bit length field
1476 : ! (see bs_env%dbcsr_msg_elem_limit): requiring the worst-rank message to stay under
1477 : ! 0.5 * HUGE(int_4) gives the safe height P_safe = 0.5 * HUGE(int_4) * min_dim / n_grid.
1478 38 : IF (use_cutoff) THEN
1479 2 : safe_max = n_grid_total
1480 : ELSE
1481 : safe_max = INT(0.5_dp*REAL(bs_env%dbcsr_msg_elem_limit, dp)*REAL(min_dim, dp)/ &
1482 36 : REAL(n_grid_total, dp))
1483 36 : safe_max = MAX(1, MIN(safe_max, n_grid_total))
1484 : END IF
1485 :
1486 : ! A user-set N_PANELS ( > 1 ) is honored EXACTLY: the planner produces that many panels
1487 : ! (up to grid-block granularity) and never force-splits them for the message/memory safety
1488 : ! limits -- if a limit is tripped it warns instead of silently changing the count.
1489 38 : n_panels_req = bs_env%ri_rs%n_panels
1490 38 : honor_exact = (n_panels_req > 1)
1491 38 : panels_unsafe = .FALSE.
1492 38 : IF (n_panels_req > 1) THEN
1493 : ! ceil(n_grid/n_panels_req) rows per panel => exactly n_panels_req panels. With the
1494 : ! cutoff active safe_max = n_grid_total (no clamp, honored exactly); without it, safe_max
1495 : ! is the int32-overflow ceiling and MUST still bound the panel (the non-cutoff planner
1496 : ! loop has no in-loop message-size check).
1497 2 : panel_size = MIN((n_grid_total + n_panels_req - 1)/n_panels_req, safe_max)
1498 : ELSE
1499 : ! Default (<= 1): a single whole-grid panel, clamped to the overflow-safe ceiling.
1500 36 : panel_size = safe_max
1501 : END IF
1502 38 : panel_size = MAX(1, panel_size)
1503 :
1504 38 : IF (use_cutoff) THEN
1505 : ! Half of the measured free memory as panel budget.
1506 2 : CALL mp_mem_avail_per_rank_GB(bs_env%para_env, mem_avail_GB)
1507 2 : mem_budget_GB = 0.5_dp*mem_avail_GB
1508 : CALL plan_grid_panels(bs_env, r_blk_sizes, panel_size, min_dim, pan_first, pan_last, &
1509 : centroids=bs_env%ri_rs%chunk_centroids, &
1510 : cutoff=bs_env%ri_rs%cutoff_radius_v_w, &
1511 : n_RI=bs_env%n_RI, n_ao=bs_env%n_ao, &
1512 : n_procs=bs_env%para_env%num_pe, mem_budget_GB=mem_budget_GB, &
1513 2 : honor_exact=honor_exact, unsafe=panels_unsafe)
1514 : ELSE
1515 36 : CALL plan_grid_panels(bs_env, r_blk_sizes, panel_size, min_dim, pan_first, pan_last)
1516 : END IF
1517 :
1518 38 : IF (honor_exact .AND. panels_unsafe) THEN
1519 : WRITE (msg, '(A,I0,A)') &
1520 0 : "N_PANELS = ", n_panels_req, " is used as requested, but one or more panels "// &
1521 : "exceed the DBCSR 32-bit message length or the memory budget. The run may abort "// &
1522 0 : "or swap; increase N_PANELS if it does."
1523 0 : CPWARN(TRIM(msg))
1524 : END IF
1525 :
1526 154 : ALLOCATE (bs_env%ri_rs%pan_first, SOURCE=pan_first)
1527 154 : ALLOCATE (bs_env%ri_rs%pan_last, SOURCE=pan_last)
1528 :
1529 38 : CALL timestop(handle)
1530 :
1531 204 : END SUBROUTINE resolve_grid_panels
1532 :
1533 : ! **************************************************************************************************
1534 : !> \brief Estimates and prints per-process memory requirements for the RI-RS GW calculation.
1535 : !> \param qs_env ...
1536 : !> \param bs_env ...
1537 : ! **************************************************************************************************
1538 38 : SUBROUTINE print_ri_rs_memory_estimate(qs_env, bs_env)
1539 :
1540 : !$ USE OMP_LIB, ONLY: omp_get_max_threads
1541 :
1542 : TYPE(qs_environment_type), POINTER :: qs_env
1543 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1544 :
1545 : CHARACTER(LEN=*), PARAMETER :: routineN = 'print_ri_rs_memory_estimate'
1546 :
1547 : CHARACTER(LEN=max_line_length) :: msg
1548 : INTEGER :: handle, iatom, ipan, l, &
1549 : max_n_ao_used, max_n_local_grid, &
1550 : n_ao_used_atom, n_grid_total, &
1551 : n_local_grid, n_loc_ri_max, n_procs, &
1552 : n_procs_per_atom, n_RI, n_threads, &
1553 : natom, pan_rows, pan_width
1554 : INTEGER(KIND=int_8) :: nze_tmpl
1555 38 : INTEGER, ALLOCATABLE, DIMENSION(:) :: pan_first, pan_last
1556 38 : INTEGER, DIMENSION(:), POINTER :: r_blk_sizes
1557 : LOGICAL :: use_cutoff
1558 38 : LOGICAL, ALLOCATABLE, DIMENSION(:) :: grid_used
1559 : REAL(KIND=dp) :: cutoff_ri, mem_avail_GB, mem_D_local_GB, &
1560 : mem_dlp_GB, mem_pan_GB, mem_panels_GB, &
1561 : mem_phi_local_GB, mem_Z_lP_GB, &
1562 : mem_Zlp_peak_GB, pos_P(3)
1563 38 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
1564 :
1565 38 : CALL timeset(routineN, handle)
1566 :
1567 : CALL dbcsr_get_info(bs_env%ri_rs%mat_phi_mu_l, nfullrows_total=n_grid_total, &
1568 38 : row_blk_size=r_blk_sizes)
1569 38 : n_RI = bs_env%n_RI
1570 38 : n_procs = bs_env%para_env%num_pe
1571 :
1572 : ! Z_lP upper bound: dense n_grid × n_RI, distributed evenly across all ranks.
1573 : ! The actual sparse Z_lP is smaller due to the per-atom locality cutoff.
1574 : mem_Z_lP_GB = REAL(n_grid_total, dp)*REAL(n_RI, dp)*8.0_dp/ &
1575 38 : REAL(n_procs, dp)*1.0E-9_dp
1576 :
1577 : ! Peak panel memory during Σ^c: two G panels (A_occ, A_vir) + one W panel plus the
1578 : ! grid × RI / grid × AO intermediates. With the CUTOFF_RADIUS_RL_W restriction the panel
1579 : ! matrices only allocate the geo-template blocks, so use the same nze-aware model as the
1580 : ! panel planner (panel_mem_estimate_GB); without the cutoff, dense panel_rows × n_grid.
1581 : ! Plus the n_RI × n_RI W_aux matrix. Distributed over n_procs ranks.
1582 : use_cutoff = bs_env%ri_rs%cutoff_radius_v_w > 0.0_dp .AND. &
1583 38 : ALLOCATED(bs_env%ri_rs%chunk_centroids)
1584 38 : CALL resolve_grid_panels(bs_env, bs_env%ri_rs%mat_phi_mu_l, pan_first, pan_last)
1585 38 : mem_panels_GB = 0.0_dp
1586 78 : DO ipan = 1, SIZE(pan_first)
1587 558 : pan_rows = SUM(r_blk_sizes(pan_first(ipan):pan_last(ipan)))
1588 40 : IF (use_cutoff) THEN
1589 : CALL mask_grid_blocks_near_panel(bs_env%ri_rs%chunk_centroids, pan_first(ipan), &
1590 : pan_last(ipan), bs_env%ri_rs%cutoff_radius_v_w, &
1591 4 : grid_used)
1592 88 : pan_width = SUM(r_blk_sizes, MASK=grid_used)
1593 : CALL panel_template_elems(r_blk_sizes, bs_env%ri_rs%chunk_centroids, &
1594 : grid_used, pan_first(ipan), pan_last(ipan), &
1595 4 : bs_env%ri_rs%cutoff_radius_v_w, nze_tmpl)
1596 : CALL panel_mem_estimate_GB(nze_tmpl, pan_rows, pan_width, n_grid_total, &
1597 4 : n_RI, bs_env%n_ao, n_procs, mem_pan_GB)
1598 : ELSE
1599 36 : pan_width = n_grid_total
1600 : mem_pan_GB = (3.0_dp*REAL(pan_rows, dp)*REAL(pan_width, dp) + &
1601 : 2.0_dp*REAL(pan_rows, dp)*REAL(n_RI, dp))* &
1602 36 : 8.0_dp/REAL(n_procs, dp)*1.0E-9_dp
1603 : END IF
1604 78 : mem_panels_GB = MAX(mem_panels_GB, mem_pan_GB)
1605 : END DO
1606 : mem_panels_GB = mem_panels_GB + &
1607 38 : REAL(n_RI, dp)*REAL(n_RI, dp)*8.0_dp/REAL(n_procs, dp)*1.0E-9_dp
1608 :
1609 : ! Z_lP SOLVE peak (compute_Z_lP). For the atom P with the largest integration
1610 : ! sphere, one rank holds simultaneously:
1611 : ! D_local : n_local_grid x n_local_grid (dense Gram, BLAS path only; O(n_local_grid^2))
1612 : ! phi_local: n_local_grid x n_ao_used (AOs reaching into the sphere only)
1613 : ! d_lp : n_local_grid x n_loc_ri, replicated once + one private copy per OMP thread
1614 : ! n_local_grid = # grid points within cutoff_ri(P) = CUTOFF_RADIUS_RL_RI (if > 0) else
1615 : ! r_c(RI metric) + r_RI(P). This is NOT evenly distributed: n_local_grid depends on the
1616 : ! local density of atoms/grid, so the rank owning the densest atom peaks well above the
1617 : ! average. We report the worst-case (max over atoms) as a per-rank upper bound.
1618 38 : CALL get_qs_env(qs_env, particle_set=particle_set)
1619 38 : natom = bs_env%n_atom
1620 :
1621 38 : max_n_local_grid = 0
1622 38 : n_loc_ri_max = 0
1623 38 : max_n_ao_used = 0
1624 134 : DO iatom = 1, natom
1625 96 : IF (bs_env%ri_rs%cutoff_radius_ri_rs > 0.0_dp) THEN
1626 6 : cutoff_ri = bs_env%ri_rs%cutoff_radius_ri_rs
1627 : ELSE
1628 90 : cutoff_ri = bs_env%ri_metric%cutoff_radius + bs_env%ri_rs%radius_ri_per_atom(iatom)
1629 : END IF
1630 384 : pos_P(:) = particle_set(iatom)%r(:)
1631 : n_local_grid = 0
1632 37108 : DO l = 1, n_grid_total
1633 148144 : IF (SUM((bs_env%ri_rs%grid_points(1:3, l) - pos_P(1:3))**2) <= cutoff_ri**2) THEN
1634 35626 : n_local_grid = n_local_grid + 1
1635 : END IF
1636 : END DO
1637 96 : max_n_local_grid = MAX(max_n_local_grid, n_local_grid)
1638 96 : CALL get_n_ao_in_sphere(bs_env, particle_set, iatom, cutoff_ri, n_ao_used_atom)
1639 96 : max_n_ao_used = MAX(max_n_ao_used, n_ao_used_atom)
1640 : n_loc_ri_max = MAX(n_loc_ri_max, &
1641 134 : bs_env%i_RI_end_from_atom(iatom) - bs_env%i_RI_start_from_atom(iatom) + 1)
1642 : END DO
1643 :
1644 38 : n_procs_per_atom = MIN(MAX(bs_env%ri_rs%n_procs_per_atom_z_lp, 1), n_procs)
1645 38 : n_threads = 1
1646 38 : !$ n_threads = omp_get_max_threads()
1647 :
1648 : ! D_local: dense on one rank for the BLAS path; block-cyclic over the subgroup (=> /G) for
1649 : ! the ScaLAPACK path (N_PROCS_PER_ATOM_Z_LP = G > 1). phi_local/d_lp stay per-rank either way.
1650 38 : IF (n_procs_per_atom > 1) THEN
1651 0 : mem_D_local_GB = REAL(max_n_local_grid, dp)**2*8.0_dp/REAL(n_procs_per_atom, dp)*1.0E-9_dp
1652 : ELSE
1653 38 : mem_D_local_GB = REAL(max_n_local_grid, dp)**2*8.0_dp*1.0E-9_dp
1654 : END IF
1655 38 : mem_phi_local_GB = REAL(max_n_local_grid, dp)*REAL(max_n_ao_used, dp)*8.0_dp*1.0E-9_dp
1656 : mem_dlp_GB = REAL(max_n_local_grid, dp)*REAL(n_loc_ri_max, dp)*8.0_dp* &
1657 38 : REAL(1 + n_threads, dp)*1.0E-9_dp
1658 38 : mem_Zlp_peak_GB = mem_D_local_GB + mem_phi_local_GB + mem_dlp_GB
1659 :
1660 : ! Available memory per process = node MemLikelyFree / ranks-per-node, min across ranks
1661 : ! (0 on non-Linux => warnings suppressed below).
1662 38 : CALL mp_mem_avail_per_rank_GB(bs_env%para_env, mem_avail_GB)
1663 :
1664 38 : IF (bs_env%unit_nr > 0) THEN
1665 19 : WRITE (bs_env%unit_nr, '(A)') ' '
1666 19 : WRITE (bs_env%unit_nr, '(T2,A)') 'RI-RS memory estimate per MPI process:'
1667 : WRITE (bs_env%unit_nr, '(T4,A,F37.2,A)') &
1668 19 : 'Available memory per process (system)', mem_avail_GB, ' GB'
1669 : WRITE (bs_env%unit_nr, '(T4,A,F18.2,A)') &
1670 19 : 'Required for Z_lP (dense upper bound; actual is sparser)', mem_Z_lP_GB, ' GB'
1671 : WRITE (bs_env%unit_nr, '(T4,A,F25.2,A)') &
1672 19 : 'Required for χ, W, Σ panels (peak per panel step)', mem_panels_GB, ' GB'
1673 : WRITE (bs_env%unit_nr, '(T4,A,F17.2,A)') &
1674 19 : 'Required for Z_lP solve peak (D_local+φ, worst-case atom)', mem_Zlp_peak_GB, ' GB'
1675 : WRITE (bs_env%unit_nr, '(T4,A,T69,I12)') &
1676 19 : 'Worst-case local-grid number of grid points:', max_n_local_grid
1677 : WRITE (bs_env%unit_nr, '(T4,A,T69,F9.2,A)') &
1678 19 : 'Worst-case memory D_local:', mem_D_local_GB, ' GB'
1679 :
1680 : END IF
1681 :
1682 38 : IF (mem_avail_GB > 0.0_dp .AND. mem_Z_lP_GB > mem_avail_GB) THEN
1683 : WRITE (msg, '(A,F0.2,A,F0.2,A)') &
1684 0 : "The estimated memory for Z_lP, ", mem_Z_lP_GB, " GB per process, exceeds the "// &
1685 0 : "available ", mem_avail_GB, " GB. Z_lP (n_grid x n_RI) is distributed across all "// &
1686 : "MPI ranks, so add nodes, use fewer MPI ranks per node, or raise "// &
1687 : "N_PROCS_PER_ATOM_Z_LP to distribute each atom block via ScaLAPACK, which reduces "// &
1688 0 : "the per-rank memory roughly by the number of ranks per atom."
1689 0 : CPWARN(TRIM(msg))
1690 : END IF
1691 :
1692 38 : IF (mem_avail_GB > 0.0_dp .AND. mem_panels_GB > mem_avail_GB) THEN
1693 : WRITE (msg, '(A,F0.2,A,F0.2,A)') &
1694 0 : "The estimated peak memory of the chi/W/Sigma panels, ", mem_panels_GB, &
1695 0 : " GB per process, exceeds the available ", mem_avail_GB, " GB. Panel memory "// &
1696 : "scales roughly as 3*panel_size*n_grid/n_procs, so add nodes, use fewer MPI ranks "// &
1697 0 : "per node, or raise N_PANELS for more but smaller panels."
1698 0 : CPWARN(TRIM(msg))
1699 : END IF
1700 :
1701 38 : IF (mem_avail_GB > 0.0_dp .AND. mem_Zlp_peak_GB > mem_avail_GB) THEN
1702 : WRITE (msg, '(A,F0.2,A,F0.2,A)') &
1703 0 : "The estimated peak memory of the Z_lP solve, ", mem_Zlp_peak_GB, &
1704 0 : " GB per process, exceeds the available ", mem_avail_GB, &
1705 : " GB. The per-atom Gram matrix D_local(n_local_grid, n_local_grid) dominates and "// &
1706 : "scales as n_local_grid^2, and it is not balanced across ranks: the rank owning "// &
1707 : "the atom with the largest integration sphere peaks well above the average. "// &
1708 : "Either raise N_PROCS_PER_ATOM_Z_LP to distribute D_local block-cyclic via "// &
1709 : "ScaLAPACK, which reduces that term roughly by the number of ranks per atom at no "// &
1710 : "loss of accuracy, or lower CUTOFF_RADIUS_RL_RI, which shrinks D_local as "// &
1711 : "n_local_grid^2 but trades accuracy, or use fewer MPI ranks per node so that each "// &
1712 0 : "rank has more memory for the peak atom."
1713 0 : CPWARN(TRIM(msg))
1714 : END IF
1715 :
1716 38 : CALL timestop(handle)
1717 :
1718 76 : END SUBROUTINE print_ri_rs_memory_estimate
1719 :
1720 : ! **************************************************************************************************
1721 : !> \brief Creates an empty (panel_chunks x neighborhood_chunks) DBCSR matrix with zero blocks
1722 : !> pre-allocated only where |centroid(panel_row r) - centroid(column c)| <= cutoff.
1723 : !> Used with retain_sparsity=.TRUE. in the subsequent dbcsr_multiply so distant blocks
1724 : !> of the grid-basis panels (φ G φ^T, Z W Z^T, ...) are never computed at all.
1725 : !> \param L_pan ...
1726 : !> \param L_full ...
1727 : !> \param centroids ...
1728 : !> \param cutoff ...
1729 : !> \param blk0 ...
1730 : !> \param A_template ...
1731 : !> \param col_map ...
1732 : ! **************************************************************************************************
1733 124 : SUBROUTINE build_geo_template_panel(L_pan, L_full, centroids, cutoff, blk0, A_template, col_map)
1734 : TYPE(dbcsr_type), INTENT(IN) :: L_pan, L_full
1735 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: centroids
1736 : REAL(KIND=dp), INTENT(IN) :: cutoff
1737 : INTEGER, INTENT(IN) :: blk0
1738 : TYPE(dbcsr_type), INTENT(OUT) :: A_template
1739 : INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL :: col_map
1740 :
1741 : CHARACTER(LEN=*), PARAMETER :: routineN = 'build_geo_template_panel'
1742 :
1743 : INTEGER :: c, cg, cs, handle, my_pcol, my_prow, &
1744 : n_grid_blks, n_pan_blks, npcols, &
1745 : nprows, r, rs
1746 124 : INTEGER, DIMENSION(:), POINTER :: grid_blk_sizes, pan_blk_sizes
1747 : REAL(KIND=dp) :: cutoff2
1748 124 : REAL(KIND=dp), ALLOCATABLE :: zero_blk(:, :)
1749 : TYPE(dbcsr_distribution_type) :: dist
1750 :
1751 124 : CALL timeset(routineN, handle)
1752 :
1753 124 : cutoff2 = cutoff**2
1754 124 : CALL dbcsr_get_info(L_pan, nblkrows_total=n_pan_blks, row_blk_size=pan_blk_sizes)
1755 124 : CALL dbcsr_get_info(L_full, nblkrows_total=n_grid_blks, row_blk_size=grid_blk_sizes)
1756 :
1757 : ! create_product_matrix assigns row r to process MOD(r-1,nprows) and
1758 : ! col c to MOD(c-1,npcols), so we can determine local ownership analytically.
1759 124 : CALL create_product_matrix(L_pan, L_full, 'N', 'T', A_template)
1760 124 : CALL dbcsr_get_info(A_template, distribution=dist)
1761 : CALL dbcsr_distribution_get(dist, nprows=nprows, npcols=npcols, &
1762 124 : myprow=my_prow, mypcol=my_pcol)
1763 :
1764 : ALLOCATE (zero_blk(MAXVAL(pan_blk_sizes(1:n_pan_blks)), &
1765 4402 : MAXVAL(grid_blk_sizes(1:n_grid_blks))))
1766 124 : zero_blk(:, :) = 0.0_dp
1767 :
1768 1426 : DO r = 1, n_pan_blks
1769 1302 : IF (MOD(r - 1, nprows) /= my_prow) CYCLE
1770 651 : rs = pan_blk_sizes(r)
1771 14446 : DO c = 1, n_grid_blks
1772 13671 : IF (MOD(c - 1, npcols) /= my_pcol) CYCLE
1773 13671 : cg = c
1774 13671 : IF (PRESENT(col_map)) cg = col_map(c)
1775 13671 : IF ((centroids(1, blk0 + r - 1) - centroids(1, cg))**2 + &
1776 : (centroids(2, blk0 + r - 1) - centroids(2, cg))**2 + &
1777 1302 : (centroids(3, blk0 + r - 1) - centroids(3, cg))**2 <= cutoff2) THEN
1778 12741 : cs = grid_blk_sizes(c)
1779 12741 : CALL dbcsr_put_block(A_template, r, c, zero_blk(1:rs, 1:cs))
1780 : END IF
1781 : END DO
1782 : END DO
1783 124 : CALL dbcsr_finalize(A_template)
1784 :
1785 124 : DEALLOCATE (zero_blk)
1786 124 : CALL timestop(handle)
1787 :
1788 124 : END SUBROUTINE build_geo_template_panel
1789 :
1790 : ! **************************************************************************************************
1791 : !> \brief Slices a contiguous range of grid row-blocks [blk0, blk1] out of a (grid x n) DBCSR
1792 : !> matrix into a new (P x n) panel matrix: iterate the source's local blocks, put the
1793 : !> in-range ones into the panel with a remapped row-block index, then finalize. Row-block
1794 : !> index i of the panel corresponds to source row-block blk0+i-1.
1795 : !> \param mat_full ...
1796 : !> \param blk0 ...
1797 : !> \param blk1 ...
1798 : !> \param mat_panel ...
1799 : ! **************************************************************************************************
1800 3012 : SUBROUTINE extract_grid_panel(mat_full, blk0, blk1, mat_panel)
1801 :
1802 : TYPE(dbcsr_type), INTENT(INOUT) :: mat_full
1803 : INTEGER, INTENT(IN) :: blk0, blk1
1804 : TYPE(dbcsr_type), INTENT(OUT) :: mat_panel
1805 :
1806 : CHARACTER(LEN=*), PARAMETER :: routineN = 'extract_grid_panel'
1807 :
1808 : INTEGER :: handle, ib, jb, npb
1809 3012 : INTEGER, DIMENSION(:), POINTER :: col_blk_full, col_dist_full, &
1810 3012 : row_blk_full, row_blk_pan, &
1811 3012 : row_dist_full, row_dist_pan
1812 3012 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: blk
1813 : TYPE(dbcsr_distribution_type) :: dist_full, dist_pan
1814 : TYPE(dbcsr_iterator_type) :: iter
1815 :
1816 3012 : CALL timeset(routineN, handle)
1817 :
1818 : CALL dbcsr_get_info(mat_full, distribution=dist_full, &
1819 3012 : row_blk_size=row_blk_full, col_blk_size=col_blk_full)
1820 3012 : CALL dbcsr_distribution_get(dist_full, row_dist=row_dist_full, col_dist=col_dist_full)
1821 :
1822 3012 : npb = blk1 - blk0 + 1
1823 12048 : ALLOCATE (row_dist_pan(npb), row_blk_pan(npb))
1824 91344 : row_dist_pan(:) = row_dist_full(blk0:blk1)
1825 91344 : row_blk_pan(:) = row_blk_full(blk0:blk1)
1826 :
1827 : CALL dbcsr_distribution_new(dist_pan, template=dist_full, &
1828 3012 : row_dist=row_dist_pan, col_dist=col_dist_full)
1829 : CALL dbcsr_create(mat_panel, name="grid_panel", dist=dist_pan, &
1830 : matrix_type=dbcsr_type_no_symmetry, &
1831 3012 : row_blk_size=row_blk_pan, col_blk_size=col_blk_full)
1832 :
1833 3012 : CALL dbcsr_iterator_start(iter, mat_full)
1834 55416 : DO WHILE (dbcsr_iterator_blocks_left(iter))
1835 52404 : CALL dbcsr_iterator_next_block(iter, ib, jb, blk)
1836 52404 : IF (ib < blk0 .OR. ib > blk1) CYCLE
1837 55416 : CALL dbcsr_put_block(mat_panel, ib - blk0 + 1, jb, blk)
1838 : END DO
1839 3012 : CALL dbcsr_iterator_stop(iter)
1840 3012 : CALL dbcsr_finalize(mat_panel)
1841 :
1842 3012 : CALL dbcsr_distribution_release(dist_pan)
1843 3012 : DEALLOCATE (row_dist_pan, row_blk_pan)
1844 :
1845 3012 : CALL timestop(handle)
1846 :
1847 9036 : END SUBROUTINE extract_grid_panel
1848 :
1849 : ! **************************************************************************************************
1850 : !> \brief Marks which column blocks of a DBCSR matrix carry at least one non-zero block anywhere
1851 : !> (global union). Used to restrict the inner index of the panel multiplies to the
1852 : !> AO/RI atoms that actually touch the panel (exact: dropped rows only meet zeros).
1853 : !> \param matrix ...
1854 : !> \param para_env ...
1855 : !> \param used ...
1856 : ! **************************************************************************************************
1857 2382 : SUBROUTINE collect_used_col_blocks(matrix, para_env, used)
1858 :
1859 : TYPE(dbcsr_type), INTENT(INOUT) :: matrix
1860 : TYPE(mp_para_env_type), INTENT(IN), POINTER :: para_env
1861 : LOGICAL, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: used
1862 :
1863 : CHARACTER(LEN=*), PARAMETER :: routineN = 'collect_used_col_blocks'
1864 :
1865 : INTEGER :: handle, ib, jb, nblkcols
1866 : INTEGER, ALLOCATABLE, DIMENSION(:) :: iused
1867 2382 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: blk
1868 : TYPE(dbcsr_iterator_type) :: iter
1869 :
1870 2382 : CALL timeset(routineN, handle)
1871 :
1872 2382 : CALL dbcsr_get_info(matrix, nblkcols_total=nblkcols)
1873 7146 : ALLOCATE (iused(nblkcols))
1874 2382 : iused(:) = 0
1875 :
1876 2382 : CALL dbcsr_iterator_start(iter, matrix)
1877 43039 : DO WHILE (dbcsr_iterator_blocks_left(iter))
1878 40657 : CALL dbcsr_iterator_next_block(iter, ib, jb, blk)
1879 40657 : iused(jb) = 1
1880 : END DO
1881 2382 : CALL dbcsr_iterator_stop(iter)
1882 :
1883 2382 : CALL para_env%sum(iused)
1884 :
1885 4764 : ALLOCATE (used(nblkcols))
1886 8040 : used(:) = (iused(:) > 0)
1887 2382 : DEALLOCATE (iused)
1888 :
1889 2382 : CALL timestop(handle)
1890 :
1891 4764 : END SUBROUTINE collect_used_col_blocks
1892 :
1893 : ! **************************************************************************************************
1894 : !> \brief Copies the flagged block rows (compress_rows=.TRUE.) or block columns (.FALSE.) of a
1895 : !> DBCSR matrix into a compressed matrix. The subset keeps the parent's process assignment
1896 : !> along the compressed dimension, so every block stays on its owning rank: the extraction
1897 : !> is purely local (zero communication), like extract_grid_panel.
1898 : !> \param mat_full ...
1899 : !> \param used ...
1900 : !> \param mat_out ...
1901 : !> \param compress_rows ...
1902 : !> \param blk_map ...
1903 : ! **************************************************************************************************
1904 6392 : SUBROUTINE extract_masked_blocks(mat_full, used, mat_out, compress_rows, blk_map)
1905 :
1906 : TYPE(dbcsr_type), INTENT(INOUT) :: mat_full
1907 : LOGICAL, DIMENSION(:), INTENT(IN) :: used
1908 : TYPE(dbcsr_type), INTENT(OUT) :: mat_out
1909 : LOGICAL, INTENT(IN) :: compress_rows
1910 : INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT), &
1911 : OPTIONAL :: blk_map
1912 :
1913 : CHARACTER(LEN=*), PARAMETER :: routineN = 'extract_masked_blocks'
1914 :
1915 : INTEGER :: handle, ib, jb, n_blk, n_sub, r
1916 6392 : INTEGER, ALLOCATABLE, DIMENSION(:) :: inv_map
1917 6392 : INTEGER, DIMENSION(:), POINTER :: blk_full, blk_sub, col_blk_full, &
1918 6392 : col_dist_full, dist_full_1d, &
1919 6392 : dist_sub_1d, row_blk_full, &
1920 6392 : row_dist_full
1921 6392 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: blk
1922 : TYPE(dbcsr_distribution_type) :: dist_full, dist_sub
1923 : TYPE(dbcsr_iterator_type) :: iter
1924 :
1925 6392 : CALL timeset(routineN, handle)
1926 :
1927 : CALL dbcsr_get_info(mat_full, distribution=dist_full, &
1928 6392 : row_blk_size=row_blk_full, col_blk_size=col_blk_full)
1929 6392 : CALL dbcsr_distribution_get(dist_full, row_dist=row_dist_full, col_dist=col_dist_full)
1930 :
1931 6392 : IF (compress_rows) THEN
1932 4010 : blk_full => row_blk_full
1933 4010 : dist_full_1d => row_dist_full
1934 : ELSE
1935 2382 : blk_full => col_blk_full
1936 2382 : dist_full_1d => col_dist_full
1937 : END IF
1938 6392 : n_blk = SIZE(blk_full)
1939 :
1940 24696 : n_sub = COUNT(used)
1941 6392 : CPASSERT(n_sub > 0)
1942 38352 : ALLOCATE (inv_map(n_blk), blk_sub(n_sub), dist_sub_1d(n_sub))
1943 6476 : IF (PRESENT(blk_map)) ALLOCATE (blk_map(n_sub))
1944 6392 : inv_map(:) = 0
1945 6392 : r = 0
1946 24696 : DO ib = 1, n_blk
1947 24696 : IF (used(ib)) THEN
1948 18304 : r = r + 1
1949 18304 : inv_map(ib) = r
1950 18304 : blk_sub(r) = blk_full(ib)
1951 18304 : dist_sub_1d(r) = dist_full_1d(ib)
1952 18304 : IF (PRESENT(blk_map)) blk_map(r) = ib
1953 : END IF
1954 : END DO
1955 :
1956 6392 : IF (compress_rows) THEN
1957 : CALL dbcsr_distribution_new(dist_sub, template=dist_full, &
1958 4010 : row_dist=dist_sub_1d, col_dist=col_dist_full)
1959 : CALL dbcsr_create(mat_out, name="row_subset", dist=dist_sub, &
1960 : matrix_type=dbcsr_type_no_symmetry, &
1961 4010 : row_blk_size=blk_sub, col_blk_size=col_blk_full)
1962 : ELSE
1963 : CALL dbcsr_distribution_new(dist_sub, template=dist_full, &
1964 2382 : row_dist=row_dist_full, col_dist=dist_sub_1d)
1965 : CALL dbcsr_create(mat_out, name="col_subset", dist=dist_sub, &
1966 : matrix_type=dbcsr_type_no_symmetry, &
1967 2382 : row_blk_size=row_blk_full, col_blk_size=blk_sub)
1968 : END IF
1969 :
1970 6392 : CALL dbcsr_iterator_start(iter, mat_full)
1971 62602 : DO WHILE (dbcsr_iterator_blocks_left(iter))
1972 56210 : CALL dbcsr_iterator_next_block(iter, ib, jb, blk)
1973 62602 : IF (compress_rows) THEN
1974 15553 : IF (inv_map(ib) > 0) CALL dbcsr_put_block(mat_out, inv_map(ib), jb, blk)
1975 : ELSE
1976 40657 : IF (inv_map(jb) > 0) CALL dbcsr_put_block(mat_out, ib, inv_map(jb), blk)
1977 : END IF
1978 : END DO
1979 6392 : CALL dbcsr_iterator_stop(iter)
1980 6392 : CALL dbcsr_finalize(mat_out)
1981 :
1982 6392 : CALL dbcsr_distribution_release(dist_sub)
1983 6392 : DEALLOCATE (inv_map, blk_sub, dist_sub_1d)
1984 :
1985 6392 : CALL timestop(handle)
1986 :
1987 12784 : END SUBROUTINE extract_masked_blocks
1988 :
1989 : ! **************************************************************************************************
1990 : !> \brief Pre-seeds a square blocked DBCSR matrix with zero blocks only for block pairs whose
1991 : !> centers lie within radius, for use with copy_fm_to_dbcsr(keep_sparsity=T) or
1992 : !> dbcsr_multiply(retain_sparsity=T). Consumers: the CUTOFF_RADIUS_G_W operator truncation
1993 : !> (atom-blocked, centers = atom_centers) and the RT-BSE CUTOFF_RADIUS_W0 truncation of the
1994 : !> grid-basis W^0 (grid-blocked, centers = chunk_centroids).
1995 : !> \param matrix ...
1996 : !> \param centers block positions, one column per block row/column of matrix
1997 : !> \param radius truncation radius, same units as centers (bohr)
1998 : ! **************************************************************************************************
1999 6 : SUBROUTINE reserve_blocks_within_radius(matrix, centers, radius)
2000 :
2001 : TYPE(dbcsr_type), INTENT(INOUT) :: matrix
2002 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: centers
2003 : REAL(KIND=dp), INTENT(IN) :: radius
2004 :
2005 : CHARACTER(LEN=*), PARAMETER :: routineN = 'reserve_blocks_within_radius'
2006 :
2007 : INTEGER :: handle, i, j, my_pcol, my_prow, &
2008 : nblkcols, nblkrows
2009 6 : INTEGER, DIMENSION(:), POINTER :: col_blk, col_dist, row_blk, row_dist
2010 : REAL(KIND=dp) :: radius2
2011 6 : REAL(KIND=dp), ALLOCATABLE :: zero_blk(:, :)
2012 : TYPE(dbcsr_distribution_type) :: dist
2013 :
2014 6 : CALL timeset(routineN, handle)
2015 :
2016 : CALL dbcsr_get_info(matrix, nblkrows_total=nblkrows, nblkcols_total=nblkcols, &
2017 6 : row_blk_size=row_blk, col_blk_size=col_blk, distribution=dist)
2018 : CALL dbcsr_distribution_get(dist, row_dist=row_dist, col_dist=col_dist, &
2019 6 : myprow=my_prow, mypcol=my_pcol)
2020 6 : CPASSERT(nblkrows == SIZE(centers, 2))
2021 6 : CPASSERT(nblkcols == SIZE(centers, 2))
2022 :
2023 6 : radius2 = radius**2
2024 144 : ALLOCATE (zero_blk(MAXVAL(row_blk(1:nblkrows)), MAXVAL(col_blk(1:nblkcols))))
2025 6 : zero_blk(:, :) = 0.0_dp
2026 :
2027 66 : DO i = 1, nblkrows
2028 60 : IF (row_dist(i) /= my_prow) CYCLE
2029 336 : DO j = 1, nblkcols
2030 300 : IF (col_dist(j) /= my_pcol) CYCLE
2031 300 : IF ((centers(1, i) - centers(1, j))**2 + (centers(2, i) - centers(2, j))**2 + &
2032 60 : (centers(3, i) - centers(3, j))**2 <= radius2) THEN
2033 168 : CALL dbcsr_put_block(matrix, i, j, zero_blk(1:row_blk(i), 1:col_blk(j)))
2034 : END IF
2035 : END DO
2036 : END DO
2037 6 : CALL dbcsr_finalize(matrix)
2038 :
2039 6 : DEALLOCATE (zero_blk)
2040 6 : CALL timestop(handle)
2041 :
2042 6 : END SUBROUTINE reserve_blocks_within_radius
2043 :
2044 : ! **************************************************************************************************
2045 : !> \brief Creates the (empty) result matrix of op(mat_left) * op(mat_right) with the correct block
2046 : !> structure and a distribution on the shared process grid, ready to be filled by
2047 : !> dbcsr_multiply. Row structure comes from op(left), column structure from op(right).
2048 : !> \param mat_left ...
2049 : !> \param mat_right ...
2050 : !> \param transa 'N' or 'T' applied to mat_left
2051 : !> \param transb 'N' or 'T' applied to mat_right
2052 : !> \param mat_out ...
2053 : ! **************************************************************************************************
2054 8514 : SUBROUTINE create_product_matrix(mat_left, mat_right, transa, transb, mat_out)
2055 :
2056 : TYPE(dbcsr_type), INTENT(IN) :: mat_left, mat_right
2057 : CHARACTER(LEN=1), INTENT(IN) :: transa, transb
2058 : TYPE(dbcsr_type), INTENT(OUT) :: mat_out
2059 :
2060 : CHARACTER(LEN=*), PARAMETER :: routineN = 'create_product_matrix'
2061 :
2062 : INTEGER :: handle, i, npcols, nprows
2063 8514 : INTEGER, DIMENSION(:), POINTER :: col_blk_l, col_blk_r, out_col_blk, &
2064 8514 : out_col_dist, out_row_blk, &
2065 8514 : out_row_dist, row_blk_l, row_blk_r
2066 : TYPE(dbcsr_distribution_type) :: dist_l, dist_out
2067 :
2068 8514 : CALL timeset(routineN, handle)
2069 :
2070 8514 : CALL dbcsr_get_info(mat_left, distribution=dist_l, row_blk_size=row_blk_l, col_blk_size=col_blk_l)
2071 8514 : CALL dbcsr_get_info(mat_right, row_blk_size=row_blk_r, col_blk_size=col_blk_r)
2072 8514 : CALL dbcsr_distribution_get(dist_l, nprows=nprows, npcols=npcols)
2073 :
2074 : ! block SIZES follow op(left)/op(right); DISTRIBUTIONS are freshly round-robined onto the
2075 : ! shared process grid (a transposed operand's row-dist is NOT a valid col-dist on a
2076 : ! non-square grid). dbcsr_multiply redistributes internally, so any valid mapping works.
2077 8514 : IF (transa == 'N') THEN
2078 8514 : out_row_blk => row_blk_l
2079 : ELSE
2080 0 : out_row_blk => col_blk_l
2081 : END IF
2082 8514 : IF (transb == 'N') THEN
2083 6178 : out_col_blk => col_blk_r
2084 : ELSE
2085 2336 : out_col_blk => row_blk_r
2086 : END IF
2087 :
2088 42570 : ALLOCATE (out_row_dist(SIZE(out_row_blk)), out_col_dist(SIZE(out_col_blk)))
2089 131364 : DO i = 1, SIZE(out_row_blk)
2090 131364 : out_row_dist(i) = MOD(i - 1, nprows)
2091 : END DO
2092 58318 : DO i = 1, SIZE(out_col_blk)
2093 58318 : out_col_dist(i) = MOD(i - 1, npcols)
2094 : END DO
2095 :
2096 : CALL dbcsr_distribution_new(dist_out, template=dist_l, &
2097 8514 : row_dist=out_row_dist, col_dist=out_col_dist)
2098 : CALL dbcsr_create(mat_out, name="panel_product", dist=dist_out, &
2099 : matrix_type=dbcsr_type_no_symmetry, &
2100 8514 : row_blk_size=out_row_blk, col_blk_size=out_col_blk)
2101 8514 : CALL dbcsr_distribution_release(dist_out)
2102 8514 : DEALLOCATE (out_row_dist, out_col_dist)
2103 :
2104 8514 : CALL timestop(handle)
2105 :
2106 25542 : END SUBROUTINE create_product_matrix
2107 :
2108 : ! **************************************************************************************************
2109 : !> \brief Builds the AO-space Green's function operator G^occ/vir_µν (AO x AO DBCSR)
2110 : !> \param bs_env ...
2111 : !> \param tau ...
2112 : !> \param ispin ...
2113 : !> \param occ ...
2114 : !> \param vir ...
2115 : !> \param template ...
2116 : !> \param matrix_G_ao ...
2117 : ! **************************************************************************************************
2118 5768 : SUBROUTINE build_G_ao(bs_env, tau, ispin, occ, vir, template, matrix_G_ao)
2119 :
2120 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2121 : REAL(KIND=dp), INTENT(IN) :: tau
2122 : INTEGER, INTENT(IN) :: ispin
2123 : LOGICAL, INTENT(IN) :: occ, vir
2124 : TYPE(dbcsr_type), INTENT(INOUT) :: template
2125 : TYPE(dbcsr_type), INTENT(OUT) :: matrix_G_ao
2126 :
2127 : CHARACTER(LEN=*), PARAMETER :: routineN = 'build_G_ao'
2128 :
2129 : INTEGER :: handle
2130 2884 : INTEGER, DIMENSION(:), POINTER :: blk_ao, dist_row_ao
2131 : TYPE(cp_fm_type), POINTER :: fm_G
2132 : TYPE(dbcsr_distribution_type) :: dist_ao_ao
2133 :
2134 2884 : CALL timeset(routineN, handle)
2135 :
2136 2884 : IF (occ) THEN
2137 1464 : fm_G => bs_env%fm_Gocc
2138 : ELSE
2139 1420 : fm_G => bs_env%fm_Gvir
2140 : END IF
2141 :
2142 2884 : CALL G_occ_vir(bs_env, tau, fm_G, ispin, occ=occ, vir=vir)
2143 :
2144 2884 : CALL setup_square_topology(template, dist_ao_ao, blk_ao, dist_row_ao)
2145 : CALL dbcsr_create(matrix_G_ao, name="G_ao", dist=dist_ao_ao, &
2146 : matrix_type=dbcsr_type_no_symmetry, &
2147 2884 : row_blk_size=blk_ao, col_blk_size=blk_ao)
2148 :
2149 : ! Optional CUTOFF_RADIUS_G_W operator truncation: only atom-pair blocks within the radius
2150 : ! are reserved and filled.
2151 2884 : IF (bs_env%ri_rs%cutoff_radius_g_w > 0.0_dp .AND. &
2152 : ALLOCATED(bs_env%ri_rs%atom_centers)) THEN
2153 : CALL reserve_blocks_within_radius(matrix_G_ao, bs_env%ri_rs%atom_centers, &
2154 0 : bs_env%ri_rs%cutoff_radius_g_w)
2155 0 : CALL copy_fm_to_dbcsr(fm_G, matrix_G_ao, keep_sparsity=.TRUE.)
2156 : ELSE
2157 2884 : CALL copy_fm_to_dbcsr(fm_G, matrix_G_ao, keep_sparsity=.FALSE.)
2158 : END IF
2159 2884 : CALL dbcsr_filter(matrix_G_ao, bs_env%eps_filter)
2160 :
2161 : ! release only the topology; keep matrix_G_ao for the caller
2162 2884 : CALL release_square_topology(dist=dist_ao_ao, mapped_dist=dist_row_ao)
2163 :
2164 2884 : CALL timestop(handle)
2165 :
2166 2884 : END SUBROUTINE build_G_ao
2167 :
2168 : ! **************************************************************************************************
2169 : !> \brief Panel-streaming evaluation of out += scale * L_out^T (A_grid ∘ B_grid) L_out,
2170 : !> where A_grid = L_A M_A L_A^T and B_grid = L_B M_B L_B^T, WITHOUT ever forming the full
2171 : !> grid x grid objects. The grid (row) index is processed in panels of ~panel_size rows; for
2172 : !> each panel only P x grid slabs are built, Hadamard-multiplied, and contracted into the
2173 : !> (small) output. Algebraically identical to L_out^T (A_grid ∘ B_grid) L_out summed over
2174 : !> grid rows, so the result matches the non-streamed path to eps_filter.
2175 : !>
2176 : !> Mapping (L in {phi (grid x AO), Z (grid x RI)}, M the AO/RI-space operator):
2177 : !> chi : L_A=L_B=phi, M_A=G_occ_ao, M_B=G_vir_ao, L_out=Z -> RI x RI
2178 : !> Sig : L_A=phi (M_A=D/G), L_B=Z (M_B=V/W), L_out=phi -> AO x AO
2179 : !> \param L_A ...
2180 : !> \param M_A ...
2181 : !> \param L_B ...
2182 : !> \param M_B ...
2183 : !> \param L_out ...
2184 : !> \param mat_out ...
2185 : !> \param scale ...
2186 : !> \param eps ...
2187 : !> \param para_env ...
2188 : !> \param pan_first ...
2189 : !> \param pan_last ...
2190 : !> \param lb_eq_la ...
2191 : !> \param lout_eq_la ...
2192 : !> \param zero_out ...
2193 : !> \param keep_sparsity ...
2194 : !> \param centroids ...
2195 : !> \param cutoff ...
2196 : !> \param grid_occupation ...
2197 : ! **************************************************************************************************
2198 654 : SUBROUTINE contract_grid_panels(L_A, M_A, L_B, M_B, L_out, mat_out, scale, eps, para_env, &
2199 654 : pan_first, pan_last, lb_eq_la, lout_eq_la, zero_out, &
2200 654 : keep_sparsity, centroids, cutoff, grid_occupation)
2201 :
2202 : TYPE(dbcsr_type), INTENT(INOUT), TARGET :: L_A
2203 : TYPE(dbcsr_type), INTENT(INOUT) :: M_A
2204 : TYPE(dbcsr_type), INTENT(INOUT), TARGET :: L_B
2205 : TYPE(dbcsr_type), INTENT(INOUT) :: M_B
2206 : TYPE(dbcsr_type), INTENT(INOUT), TARGET :: L_out
2207 : TYPE(dbcsr_type), INTENT(INOUT) :: mat_out
2208 : REAL(KIND=dp), INTENT(IN) :: scale, eps
2209 : TYPE(mp_para_env_type), INTENT(IN), POINTER :: para_env
2210 : INTEGER, DIMENSION(:), INTENT(IN) :: pan_first, pan_last
2211 : LOGICAL, INTENT(IN) :: lb_eq_la, lout_eq_la, zero_out
2212 : LOGICAL, INTENT(IN), OPTIONAL :: keep_sparsity
2213 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN), &
2214 : OPTIONAL :: centroids
2215 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: cutoff
2216 : REAL(KIND=dp), INTENT(OUT), OPTIONAL :: grid_occupation
2217 :
2218 : CHARACTER(LEN=*), PARAMETER :: routineN = 'contract_grid_panels'
2219 :
2220 : INTEGER :: blk0, blk1, handle, ipan, n_grid_total, &
2221 : ncols_pan, nrows_pan
2222 654 : INTEGER, ALLOCATABLE, DIMENSION(:) :: gmap
2223 : LOGICAL :: my_keep_sparsity, use_cutoff
2224 654 : LOGICAL, ALLOCATABLE, DIMENSION(:) :: grid_used, usedA, usedB
2225 : TYPE(dbcsr_type) :: A_pan, B_pan, C_pan, LA_pan, LA_panC, &
2226 : LB_pan, LB_panC, Lout_pan, MA_sub, &
2227 : MB_sub, tmp2, tmpA, tmpB
2228 : TYPE(dbcsr_type), POINTER :: RB_A, RB_B, RB_out
2229 : TYPE(dbcsr_type), TARGET :: LA_near, LB_near, Lout_near
2230 :
2231 654 : CALL timeset(routineN, handle)
2232 :
2233 654 : my_keep_sparsity = .FALSE.
2234 654 : IF (PRESENT(keep_sparsity)) my_keep_sparsity = keep_sparsity
2235 654 : use_cutoff = PRESENT(centroids) .AND. PRESENT(cutoff)
2236 654 : IF (use_cutoff) use_cutoff = cutoff > 0.0_dp
2237 654 : IF (PRESENT(grid_occupation)) grid_occupation = 0.0_dp
2238 :
2239 654 : CALL dbcsr_get_info(L_A, nfullrows_total=n_grid_total)
2240 :
2241 654 : IF (zero_out) CALL dbcsr_set(mat_out, 0.0_dp)
2242 :
2243 1330 : DO ipan = 1, SIZE(pan_first)
2244 676 : blk0 = pan_first(ipan)
2245 676 : blk1 = pan_last(ipan)
2246 :
2247 : ! phi/Z panel slices (P x n)
2248 676 : CALL extract_grid_panel(L_A, blk0, blk1, LA_pan)
2249 676 : IF (.NOT. lb_eq_la) CALL extract_grid_panel(L_B, blk0, blk1, LB_pan)
2250 676 : IF (.NOT. lout_eq_la) CALL extract_grid_panel(L_out, blk0, blk1, Lout_pan)
2251 :
2252 : ! Which AO/RI atoms (column blocks) actually touch this panel: the inner index of
2253 : ! every multiply below is restricted to them, so only the matching rows of the
2254 : ! system-wide operators M_A/M_B ever enter Cannon (exact: dropped rows meet zeros).
2255 676 : CALL collect_used_col_blocks(LA_pan, para_env, usedA)
2256 676 : IF (.NOT. lb_eq_la) THEN
2257 46 : CALL collect_used_col_blocks(LB_pan, para_env, usedB)
2258 : ELSE
2259 630 : IF (ALLOCATED(usedB)) DEALLOCATE (usedB)
2260 3380 : ALLOCATE (usedB, SOURCE=usedA)
2261 : END IF
2262 676 : IF (.NOT. (ANY(usedA) .AND. ANY(usedB))) THEN
2263 : ! empty panel slice: its Hadamard contribution is exactly zero on all ranks
2264 0 : CALL dbcsr_release(LA_pan)
2265 0 : IF (.NOT. lb_eq_la) CALL dbcsr_release(LB_pan)
2266 0 : IF (.NOT. lout_eq_la) CALL dbcsr_release(Lout_pan)
2267 : CYCLE
2268 : END IF
2269 :
2270 : ! Grid rows within reach of the panel: with the CUTOFF_RADIUS_RL_W truncation only
2271 : ! they can appear as columns of the panel products / inner rows of the L_out multiply,
2272 : ! so the system-wide phi/Z right operands are cut down to this neighborhood slice
2273 : ! (local extraction, zero communication; exact w.r.t. the geo template).
2274 676 : IF (use_cutoff) THEN
2275 44 : CALL mask_grid_blocks_near_panel(centroids, blk0, blk1, cutoff, grid_used)
2276 44 : CALL extract_masked_blocks(L_A, grid_used, LA_near, compress_rows=.TRUE., blk_map=gmap)
2277 44 : IF (.NOT. lb_eq_la) CALL extract_masked_blocks(L_B, grid_used, LB_near, compress_rows=.TRUE.)
2278 44 : IF (.NOT. lout_eq_la) CALL extract_masked_blocks(L_out, grid_used, Lout_near, compress_rows=.TRUE.)
2279 : RB_A => LA_near
2280 : ELSE
2281 : RB_A => L_A
2282 : END IF
2283 676 : IF (lb_eq_la) THEN
2284 : RB_B => RB_A
2285 46 : ELSE IF (use_cutoff) THEN
2286 : RB_B => LB_near
2287 : ELSE
2288 42 : RB_B => L_B
2289 : END IF
2290 676 : IF (lout_eq_la) THEN
2291 : RB_out => RB_A
2292 630 : ELSE IF (use_cutoff) THEN
2293 : RB_out => Lout_near
2294 : ELSE
2295 590 : RB_out => L_out
2296 : END IF
2297 :
2298 : ! A_pan = LA_pan * M_A * L_A^T (P x grid_near).
2299 : ! When cutoff is active, A_pan is pre-seeded with only nearby blocks via
2300 : ! build_geo_template_panel, and the multiply uses retain_sparsity to skip
2301 : ! computing distant blocks entirely (exact: they are zero by locality).
2302 676 : CALL extract_masked_blocks(LA_pan, usedA, LA_panC, compress_rows=.FALSE.)
2303 676 : CALL extract_masked_blocks(M_A, usedA, MA_sub, compress_rows=.TRUE.)
2304 676 : CALL create_product_matrix(LA_panC, MA_sub, 'N', 'N', tmpA)
2305 676 : CALL dbcsr_multiply('N', 'N', 1.0_dp, LA_panC, MA_sub, 0.0_dp, tmpA, filter_eps=eps)
2306 676 : CALL dbcsr_release(MA_sub)
2307 676 : IF (use_cutoff) THEN
2308 : CALL build_geo_template_panel(LA_pan, LA_near, centroids, cutoff, blk0, A_pan, &
2309 44 : col_map=gmap)
2310 : ELSE
2311 632 : CALL create_product_matrix(tmpA, RB_A, 'N', 'T', A_pan)
2312 : END IF
2313 : CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpA, RB_A, 0.0_dp, A_pan, &
2314 676 : filter_eps=eps, retain_sparsity=use_cutoff)
2315 676 : CALL dbcsr_release(tmpA)
2316 :
2317 : ! Grid-basis occupation of A_pan = φ G φ^T, accumulated over ALL panels into the
2318 : ! occupation of the (never formed) full grid x grid object:
2319 : ! sum_panels nnz(A_pan) / n_grid^2, with nnz = occ * pan_rows * pan_cols.
2320 : ! Panel-independent by construction -- a single-panel sample would instead report the
2321 : ! local neighbor count of whichever region happens to land in that panel.
2322 676 : IF (PRESENT(grid_occupation)) THEN
2323 630 : CALL dbcsr_get_info(A_pan, nfullrows_total=nrows_pan, nfullcols_total=ncols_pan)
2324 : grid_occupation = grid_occupation + dbcsr_get_occupation(A_pan)* &
2325 : REAL(ncols_pan, dp)*REAL(nrows_pan, dp)/ &
2326 630 : (REAL(n_grid_total, dp)*REAL(n_grid_total, dp))
2327 : END IF
2328 :
2329 : ! B_pan = LB_pan * M_B * L_B^T (P x grid_near); reuse the L_A slices when L_B == L_A.
2330 : ! With keep_sparsity, B_pan is pre-populated with A_pan's block structure so that
2331 : ! retain_sparsity forces the final multiply to fill only those blocks (exact for ∘).
2332 676 : IF (lb_eq_la) THEN
2333 630 : CALL extract_masked_blocks(M_B, usedA, MB_sub, compress_rows=.TRUE.)
2334 630 : CALL create_product_matrix(LA_panC, MB_sub, 'N', 'N', tmpB)
2335 630 : CALL dbcsr_multiply('N', 'N', 1.0_dp, LA_panC, MB_sub, 0.0_dp, tmpB, filter_eps=eps)
2336 : ELSE
2337 46 : CALL extract_masked_blocks(LB_pan, usedB, LB_panC, compress_rows=.FALSE.)
2338 46 : CALL extract_masked_blocks(M_B, usedB, MB_sub, compress_rows=.TRUE.)
2339 46 : CALL create_product_matrix(LB_panC, MB_sub, 'N', 'N', tmpB)
2340 46 : CALL dbcsr_multiply('N', 'N', 1.0_dp, LB_panC, MB_sub, 0.0_dp, tmpB, filter_eps=eps)
2341 46 : CALL dbcsr_release(LB_panC)
2342 : END IF
2343 676 : CALL dbcsr_release(MB_sub)
2344 676 : IF (my_keep_sparsity) THEN
2345 676 : CALL dbcsr_create(B_pan, template=A_pan)
2346 676 : CALL dbcsr_copy(B_pan, A_pan)
2347 676 : CALL dbcsr_set(B_pan, 0.0_dp)
2348 : ! The output pattern is already fixed. Omitting redundant on-the-fly filtering also
2349 : ! avoids overflowing DBCSR's single-precision screening norms for conditioned Z fits.
2350 : CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpB, RB_B, 0.0_dp, B_pan, &
2351 676 : retain_sparsity=.TRUE.)
2352 : ELSE
2353 0 : CALL create_product_matrix(tmpB, RB_B, 'N', 'T', B_pan)
2354 0 : CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpB, RB_B, 0.0_dp, B_pan, filter_eps=eps)
2355 : END IF
2356 676 : CALL dbcsr_release(tmpB)
2357 676 : CALL dbcsr_release(LA_panC)
2358 :
2359 : ! C_pan = scale * (A_pan ∘ B_pan) (P x grid_near)
2360 676 : CALL dbcsr_create(C_pan, template=A_pan)
2361 676 : CALL hadamard_product(A_pan, B_pan, C_pan, scale)
2362 676 : CALL dbcsr_release(A_pan)
2363 676 : CALL dbcsr_release(B_pan)
2364 :
2365 : ! tmp2 = C_pan * L_out (P x n_out; inner index restricted to the neighborhood)
2366 676 : CALL create_product_matrix(C_pan, RB_out, 'N', 'N', tmp2)
2367 676 : CALL dbcsr_multiply('N', 'N', 1.0_dp, C_pan, RB_out, 0.0_dp, tmp2, filter_eps=eps)
2368 676 : CALL dbcsr_release(C_pan)
2369 :
2370 : ! mat_out += L_out_pan^T * tmp2 (accumulate: beta = 1)
2371 676 : IF (lout_eq_la) THEN
2372 46 : CALL dbcsr_multiply('T', 'N', 1.0_dp, LA_pan, tmp2, 1.0_dp, mat_out, filter_eps=eps)
2373 : ELSE
2374 630 : CALL dbcsr_multiply('T', 'N', 1.0_dp, Lout_pan, tmp2, 1.0_dp, mat_out, filter_eps=eps)
2375 630 : CALL dbcsr_release(Lout_pan)
2376 : END IF
2377 676 : CALL dbcsr_release(tmp2)
2378 676 : IF (.NOT. lb_eq_la) CALL dbcsr_release(LB_pan)
2379 676 : CALL dbcsr_release(LA_pan)
2380 3358 : IF (use_cutoff) THEN
2381 44 : CALL dbcsr_release(LA_near)
2382 44 : IF (.NOT. lb_eq_la) CALL dbcsr_release(LB_near)
2383 44 : IF (.NOT. lout_eq_la) CALL dbcsr_release(Lout_near)
2384 : END IF
2385 :
2386 : END DO
2387 :
2388 654 : CALL timestop(handle)
2389 :
2390 1308 : END SUBROUTINE contract_grid_panels
2391 :
2392 : ! **************************************************************************************************
2393 : !> \brief Σ^c-specific panel loop: computes both the occupied (neg) and virtual (pos) contributions
2394 : !> in a single pass over grid panels, forming W_pan = Z_panel × W_aux × Z^T only ONCE per
2395 : !> panel and reusing it for both the G^occ and G^vir Hadamard contractions.
2396 : !>
2397 : !> Computes:
2398 : !> mat_Sigma_neg = φ^T ( (φ G^occ φ^T) ∘ (Z W^MIC Z^T) ) φ
2399 : !> mat_Sigma_pos = φ^T ( (φ G^vir φ^T) ∘ (Z W^MIC Z^T) ) φ
2400 : !>
2401 : !> \param mat_phi ...
2402 : !> \param mat_Z ...
2403 : !> \param mat_G_occ_ao ...
2404 : !> \param mat_G_vir_ao ...
2405 : !> \param mat_W_aux ...
2406 : !> \param mat_Sigma_neg ...
2407 : !> \param mat_Sigma_pos ...
2408 : !> \param eps ...
2409 : !> \param para_env ...
2410 : !> \param pan_first ...
2411 : !> \param pan_last ...
2412 : !> \param keep_sparsity ...
2413 : !> \param centroids ...
2414 : !> \param cutoff ...
2415 : ! **************************************************************************************************
2416 810 : SUBROUTINE contract_grid_panels_sigma_c(mat_phi, mat_Z, mat_G_occ_ao, mat_G_vir_ao, &
2417 : mat_W_aux, mat_Sigma_neg, mat_Sigma_pos, eps, para_env, &
2418 810 : pan_first, pan_last, keep_sparsity, centroids, cutoff)
2419 :
2420 : TYPE(dbcsr_type), INTENT(INOUT), TARGET :: mat_phi, mat_Z
2421 : TYPE(dbcsr_type), INTENT(INOUT) :: mat_G_occ_ao, mat_G_vir_ao, mat_W_aux, &
2422 : mat_Sigma_neg, mat_Sigma_pos
2423 : REAL(KIND=dp), INTENT(IN) :: eps
2424 : TYPE(mp_para_env_type), INTENT(IN), POINTER :: para_env
2425 : INTEGER, DIMENSION(:), INTENT(IN) :: pan_first, pan_last
2426 : LOGICAL, INTENT(IN), OPTIONAL :: keep_sparsity
2427 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN), &
2428 : OPTIONAL :: centroids
2429 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: cutoff
2430 :
2431 : CHARACTER(LEN=*), PARAMETER :: routineN = 'contract_grid_panels_sigma_c'
2432 :
2433 : INTEGER :: blk0, blk1, handle, ipan
2434 810 : INTEGER, ALLOCATABLE, DIMENSION(:) :: gmap
2435 : LOGICAL :: my_keep_sparsity, use_cutoff
2436 810 : LOGICAL, ALLOCATABLE, DIMENSION(:) :: grid_used, used_ao, used_ri
2437 : TYPE(dbcsr_type) :: A_occ, A_vir, C_pan, G_occ_sub, &
2438 : G_vir_sub, phi_pan, phi_panC, tmp2, &
2439 : tmpA, tmpB, W_pan, W_sub, Z_pan, Z_panC
2440 : TYPE(dbcsr_type), POINTER :: RB_phi, RB_Z
2441 : TYPE(dbcsr_type), TARGET :: phi_near, Z_near
2442 :
2443 810 : CALL timeset(routineN, handle)
2444 :
2445 810 : my_keep_sparsity = .FALSE.
2446 810 : IF (PRESENT(keep_sparsity)) my_keep_sparsity = keep_sparsity
2447 810 : use_cutoff = PRESENT(centroids) .AND. PRESENT(cutoff)
2448 810 : IF (use_cutoff) use_cutoff = cutoff > 0.0_dp
2449 :
2450 810 : CALL dbcsr_set(mat_Sigma_neg, 0.0_dp)
2451 810 : CALL dbcsr_set(mat_Sigma_pos, 0.0_dp)
2452 :
2453 1640 : DO ipan = 1, SIZE(pan_first)
2454 830 : blk0 = pan_first(ipan)
2455 830 : blk1 = pan_last(ipan)
2456 :
2457 830 : CALL extract_grid_panel(mat_phi, blk0, blk1, phi_pan)
2458 830 : CALL extract_grid_panel(mat_Z, blk0, blk1, Z_pan)
2459 :
2460 : ! AO/RI atoms touching this panel: only the matching rows of G_occ/G_vir/W ever
2461 : ! enter the multiplies below (exact: dropped rows meet zero columns of the panel).
2462 830 : CALL collect_used_col_blocks(phi_pan, para_env, used_ao)
2463 830 : CALL collect_used_col_blocks(Z_pan, para_env, used_ri)
2464 830 : IF (.NOT. (ANY(used_ao) .AND. ANY(used_ri))) THEN
2465 0 : CALL dbcsr_release(phi_pan)
2466 0 : CALL dbcsr_release(Z_pan)
2467 0 : CYCLE
2468 : END IF
2469 :
2470 : ! Neighborhood slices of phi/Z (grid rows within cutoff of the panel): they replace
2471 : ! the system-wide right operands in every multiply (local extraction, zero comm).
2472 830 : IF (use_cutoff) THEN
2473 40 : CALL mask_grid_blocks_near_panel(centroids, blk0, blk1, cutoff, grid_used)
2474 40 : CALL extract_masked_blocks(mat_phi, grid_used, phi_near, compress_rows=.TRUE., blk_map=gmap)
2475 40 : CALL extract_masked_blocks(mat_Z, grid_used, Z_near, compress_rows=.TRUE.)
2476 40 : RB_phi => phi_near
2477 40 : RB_Z => Z_near
2478 : ELSE
2479 : RB_phi => mat_phi
2480 : RB_Z => mat_Z
2481 : END IF
2482 :
2483 830 : CALL extract_masked_blocks(phi_pan, used_ao, phi_panC, compress_rows=.FALSE.)
2484 830 : CALL extract_masked_blocks(Z_pan, used_ri, Z_panC, compress_rows=.FALSE.)
2485 830 : CALL extract_masked_blocks(mat_G_occ_ao, used_ao, G_occ_sub, compress_rows=.TRUE.)
2486 830 : CALL extract_masked_blocks(mat_G_vir_ao, used_ao, G_vir_sub, compress_rows=.TRUE.)
2487 830 : CALL extract_masked_blocks(mat_W_aux, used_ri, W_sub, compress_rows=.TRUE.)
2488 :
2489 : ! A_occ = phi_pan × G_occ × phi^T (built first so W_pan can inherit its pattern).
2490 : ! With cutoff active, A_occ is pre-seeded with geo-local blocks so that the
2491 : ! phi^T multiply uses retain_sparsity and never computes distant blocks.
2492 830 : CALL create_product_matrix(phi_panC, G_occ_sub, 'N', 'N', tmpA)
2493 830 : CALL dbcsr_multiply('N', 'N', 1.0_dp, phi_panC, G_occ_sub, 0.0_dp, tmpA, filter_eps=eps)
2494 830 : IF (use_cutoff) THEN
2495 : CALL build_geo_template_panel(phi_pan, phi_near, centroids, cutoff, blk0, A_occ, &
2496 40 : col_map=gmap)
2497 : ELSE
2498 790 : CALL create_product_matrix(tmpA, RB_phi, 'N', 'T', A_occ)
2499 : END IF
2500 : CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpA, RB_phi, 0.0_dp, A_occ, &
2501 830 : filter_eps=eps, retain_sparsity=use_cutoff)
2502 830 : CALL dbcsr_release(tmpA)
2503 830 : CALL dbcsr_release(G_occ_sub)
2504 :
2505 : ! A_vir = phi_pan × G_vir × phi^T (same pre-screen as A_occ)
2506 830 : CALL create_product_matrix(phi_panC, G_vir_sub, 'N', 'N', tmpA)
2507 830 : CALL dbcsr_multiply('N', 'N', 1.0_dp, phi_panC, G_vir_sub, 0.0_dp, tmpA, filter_eps=eps)
2508 830 : IF (use_cutoff) THEN
2509 : CALL build_geo_template_panel(phi_pan, phi_near, centroids, cutoff, blk0, A_vir, &
2510 40 : col_map=gmap)
2511 : ELSE
2512 790 : CALL create_product_matrix(tmpA, RB_phi, 'N', 'T', A_vir)
2513 : END IF
2514 : CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpA, RB_phi, 0.0_dp, A_vir, &
2515 830 : filter_eps=eps, retain_sparsity=use_cutoff)
2516 830 : CALL dbcsr_release(tmpA)
2517 830 : CALL dbcsr_release(G_vir_sub)
2518 :
2519 : ! W_pan = Z_pan × W_aux × Z^T (computed once, reused for both Σ^c terms).
2520 : ! With keep_sparsity, W_pan is pre-seeded with the union of A_occ and A_vir block
2521 : ! patterns so that retain_sparsity forces the multiply to fill only those blocks:
2522 : ! exact since W outside G_occ∪G_vir is multiplied by zero in the Hadamard.
2523 830 : CALL create_product_matrix(Z_panC, W_sub, 'N', 'N', tmpB)
2524 830 : CALL dbcsr_multiply('N', 'N', 1.0_dp, Z_panC, W_sub, 0.0_dp, tmpB, filter_eps=eps)
2525 830 : IF (my_keep_sparsity) THEN
2526 830 : CALL dbcsr_create(W_pan, template=A_occ)
2527 830 : CALL dbcsr_copy(W_pan, A_occ)
2528 830 : CALL dbcsr_add(W_pan, A_vir, 1.0_dp, 1.0_dp)
2529 830 : CALL dbcsr_set(W_pan, 0.0_dp)
2530 : ! The retained pattern is exact for the following Hadamard products; screening it
2531 : ! again is redundant and may overflow DBCSR's single-precision block-norm product.
2532 : CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpB, RB_Z, 0.0_dp, W_pan, &
2533 830 : retain_sparsity=.TRUE.)
2534 : ELSE
2535 0 : CALL create_product_matrix(tmpB, RB_Z, 'N', 'T', W_pan)
2536 0 : CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpB, RB_Z, 0.0_dp, W_pan, filter_eps=eps)
2537 : END IF
2538 830 : CALL dbcsr_release(tmpB)
2539 830 : CALL dbcsr_release(W_sub)
2540 830 : CALL dbcsr_release(phi_panC)
2541 830 : CALL dbcsr_release(Z_panC)
2542 :
2543 : ! Σ^c_neg: φ^T ( A_occ ∘ W_pan ) φ
2544 830 : CALL dbcsr_create(C_pan, template=A_occ)
2545 830 : CALL hadamard_product(A_occ, W_pan, C_pan, 1.0_dp)
2546 830 : CALL dbcsr_release(A_occ)
2547 830 : CALL create_product_matrix(C_pan, RB_phi, 'N', 'N', tmp2)
2548 830 : CALL dbcsr_multiply('N', 'N', 1.0_dp, C_pan, RB_phi, 0.0_dp, tmp2, filter_eps=eps)
2549 830 : CALL dbcsr_release(C_pan)
2550 830 : CALL dbcsr_multiply('T', 'N', 1.0_dp, phi_pan, tmp2, 1.0_dp, mat_Sigma_neg, filter_eps=eps)
2551 830 : CALL dbcsr_release(tmp2)
2552 :
2553 : ! Σ^c_pos: φ^T ( A_vir ∘ W_pan ) φ — W_pan reused
2554 830 : CALL dbcsr_create(C_pan, template=A_vir)
2555 830 : CALL hadamard_product(A_vir, W_pan, C_pan, 1.0_dp)
2556 830 : CALL dbcsr_release(A_vir)
2557 830 : CALL create_product_matrix(C_pan, RB_phi, 'N', 'N', tmp2)
2558 830 : CALL dbcsr_multiply('N', 'N', 1.0_dp, C_pan, RB_phi, 0.0_dp, tmp2, filter_eps=eps)
2559 830 : CALL dbcsr_release(C_pan)
2560 830 : CALL dbcsr_multiply('T', 'N', 1.0_dp, phi_pan, tmp2, 1.0_dp, mat_Sigma_pos, filter_eps=eps)
2561 830 : CALL dbcsr_release(tmp2)
2562 :
2563 830 : CALL dbcsr_release(W_pan)
2564 830 : CALL dbcsr_release(Z_pan)
2565 830 : CALL dbcsr_release(phi_pan)
2566 6620 : IF (use_cutoff) THEN
2567 40 : CALL dbcsr_release(phi_near)
2568 40 : CALL dbcsr_release(Z_near)
2569 : END IF
2570 :
2571 : END DO
2572 :
2573 810 : CALL timestop(handle)
2574 :
2575 1620 : END SUBROUTINE contract_grid_panels_sigma_c
2576 :
2577 : ! **************************************************************************************************
2578 : !> \brief Computes C = A ◦ B (Element-wise Hadamard product) for sparse DBCSR matrices.
2579 : !> \param matrix_A ...
2580 : !> \param matrix_B ...
2581 : !> \param matrix_C ...
2582 : !> \param fac (Scaling factor applied to the product)
2583 : ! **************************************************************************************************
2584 4672 : SUBROUTINE hadamard_product(matrix_A, matrix_B, matrix_C, fac)
2585 :
2586 : TYPE(dbcsr_type), INTENT(INOUT) :: matrix_A, matrix_B, matrix_C
2587 : REAL(KIND=dp), INTENT(IN) :: fac
2588 :
2589 : CHARACTER(LEN=*), PARAMETER :: routineN = 'hadamard_product'
2590 :
2591 : INTEGER :: col, handle, row
2592 : LOGICAL :: found
2593 2336 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: blk_B, blk_C
2594 : TYPE(dbcsr_iterator_type) :: iter
2595 :
2596 2336 : CALL timeset(routineN, handle)
2597 :
2598 2336 : CALL dbcsr_copy(matrix_C, matrix_A)
2599 :
2600 2336 : CALL dbcsr_iterator_start(iter, matrix_C)
2601 298394 : DO WHILE (dbcsr_iterator_blocks_left(iter))
2602 296058 : CALL dbcsr_iterator_next_block(iter, row, col, blk_C)
2603 :
2604 296058 : CALL dbcsr_get_block_p(matrix_B, row, col, blk_B, found)
2605 :
2606 298394 : IF (found) THEN
2607 527574254 : blk_C(:, :) = fac*blk_C(:, :)*blk_B(:, :)
2608 : ELSE
2609 : ! If B is sparse here, the product is zero
2610 0 : blk_C(:, :) = 0.0_dp
2611 : END IF
2612 : END DO
2613 2336 : CALL dbcsr_iterator_stop(iter)
2614 :
2615 2336 : CALL timestop(handle)
2616 :
2617 2336 : END SUBROUTINE hadamard_product
2618 :
2619 : ! **************************************************************************************************
2620 : !> \brief Computes the screened Coulomb interaction on the imaginary-time grid, entirely in the
2621 : !> RI auxiliary (PQ) basis:
2622 : !> χ_PQ(iω) = Σ_τ w(ω,τ) cos(ωτ) χ_PQ(iτ) (cosine transform)
2623 : !> ε(iω) = Id - V^0.5 M^-1 χ(iω) M^-1 V^0.5 (dielectric function)
2624 : !> W(iω) = V^0.5 ( ε^-1(iω) - Id ) V^0.5 (correlation part only)
2625 : !> W(iτ) = Σ_ω w̃(τ,ω) cos(ωτ) W(iω) (back transform)
2626 : !> W(iτ) <- M^-1 W(iτ) M^-1 (fold in the RI metric)
2627 : !> where V is the bare Coulomb matrix and M the RI metric.
2628 : !> \param bs_env ...
2629 : !> \param qs_env ...
2630 : !> \param mat_chi_Gamma_tau ...
2631 : !> \param fm_W_time ...
2632 : ! **************************************************************************************************
2633 152 : SUBROUTINE compute_W(bs_env, qs_env, mat_chi_Gamma_tau, fm_W_time)
2634 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2635 : TYPE(qs_environment_type), POINTER :: qs_env
2636 : TYPE(dbcsr_p_type), DIMENSION(:), POINTER :: mat_chi_Gamma_tau
2637 : TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_W_time
2638 :
2639 : CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_W'
2640 :
2641 : INTEGER :: handle, i_t, j_w
2642 : REAL(KIND=dp) :: t1
2643 : TYPE(cp_fm_type) :: fm_M_inv_V_sqrt, fm_V, fm_V_sqrt
2644 :
2645 38 : CALL timeset(routineN, handle)
2646 :
2647 38 : t1 = m_walltime()
2648 :
2649 38 : CALL create_fm_W_MIC_time(bs_env, fm_W_time)
2650 :
2651 : ! 1. Allocate V and M matrices
2652 38 : CALL cp_fm_create(fm_V, bs_env%fm_RI_RI%matrix_struct)
2653 38 : CALL cp_fm_create(fm_V_sqrt, bs_env%fm_RI_RI%matrix_struct)
2654 38 : CALL cp_fm_create(fm_M_inv_V_sqrt, bs_env%fm_RI_RI%matrix_struct)
2655 :
2656 : ! Compute V and M^-1 * V^0.5
2657 38 : CALL compute_V_MinvVsqrt(bs_env, qs_env, fm_V, fm_V_sqrt, fm_M_inv_V_sqrt)
2658 :
2659 : ! 2. Loop over frequencies
2660 568 : DO j_w = 1, bs_env%num_time_freq_points
2661 : ! Fourier transformation of χ_PQ(iτ) to χ_PQ(iω_j)
2662 530 : CALL compute_fm_chi_Gamma_freq(bs_env, bs_env%fm_chi_Gamma_freq, j_w, mat_chi_Gamma_tau)
2663 :
2664 : ! ε(iω_j) = Id - V^0.5*M^-1*χ(iω_j)*M^-1*V^0.5
2665 : ! W(iω_j) = V^0.5*(ε^-1(iω_j)-Id)*V^0.5
2666 : CALL compute_fm_W_freq(bs_env, bs_env%fm_chi_Gamma_freq, fm_V_sqrt, &
2667 530 : fm_M_inv_V_sqrt, bs_env%fm_W_MIC_freq)
2668 :
2669 : ! Fourier transform from W_PQ^MIC(iω_j) to W_PQ^MIC(iτ)
2670 568 : CALL Fourier_transform_w_to_t(bs_env, fm_W_time, bs_env%fm_W_MIC_freq, j_w)
2671 : END DO
2672 :
2673 : ! M^-1(k=0) W^MIC(iτ) M^-1(k=0) -> fm_W_time
2674 38 : CALL fm_contract_ABA(bs_env%fm_Minv_Gamma, fm_W_time)
2675 :
2676 38 : IF (bs_env%unit_nr > 0) THEN
2677 : WRITE (bs_env%unit_nr, '(T2,A,T58,A,F7.1,A)') &
2678 19 : 'Computed W(iτ),', ' Execution time', m_walltime() - t1, ' s'
2679 : END IF
2680 :
2681 38 : CALL dbcsr_deallocate_matrix_set(mat_chi_Gamma_tau)
2682 :
2683 : ! Cleanup
2684 38 : CALL cp_fm_release(fm_V)
2685 38 : CALL cp_fm_release(fm_V_sqrt)
2686 38 : CALL cp_fm_release(fm_M_inv_V_sqrt)
2687 :
2688 : ! Marek : Fourier transform W^MIC(itau) back to get it at a specific im.frequency point - iomega = 0
2689 38 : IF (bs_env%rtp_method == rtp_method_bse) THEN
2690 14 : t1 = m_walltime()
2691 14 : CALL cp_fm_create(bs_env%fm_W_MIC_freq_zero, bs_env%fm_W_MIC_freq%matrix_struct)
2692 : ! Set to zero
2693 14 : CALL cp_fm_set_all(bs_env%fm_W_MIC_freq_zero, 0.0_dp)
2694 : ! Sum over all times
2695 294 : DO i_t = 1, bs_env%num_time_freq_points
2696 : ! Add the relevant structure with correct weight
2697 : CALL cp_fm_scale_and_add(1.0_dp, bs_env%fm_W_MIC_freq_zero, &
2698 294 : bs_env%time_frequency_grid%time_weights_at_zero_frequency(i_t), fm_W_time(i_t))
2699 : END DO
2700 : ! Done, save to file
2701 14 : CALL fm_write(bs_env%fm_W_MIC_freq_zero, 0, "W_freq_rtp", qs_env)
2702 : ! Report calculation
2703 14 : IF (bs_env%unit_nr > 0) THEN
2704 : WRITE (bs_env%unit_nr, '(T2,A,T57,A,F7.1,A)') &
2705 7 : 'Computed W(0),', ' Execution time', m_walltime() - t1, ' s'
2706 : END IF
2707 : END IF
2708 :
2709 38 : IF (bs_env%unit_nr > 0) WRITE (bs_env%unit_nr, '(A)') ' '
2710 :
2711 38 : CALL timestop(handle)
2712 :
2713 38 : END SUBROUTINE compute_W
2714 :
2715 : ! **************************************************************************************************
2716 : !> \brief Computes V, V^0.5, and M^-1 V^0.5 for the RI-RS dielectric function.
2717 : !> The Coulomb matrix V is constructed by the RI-RS k-point path. The inverse metric
2718 : !> M^-1(k=0) is precomputed once in gw_utils and stored in bs_env.
2719 : !> \param bs_env GW environment containing M^-1(k=0) and the RI basis
2720 : !> \param qs_env electronic-structure environment used to evaluate V(k=0)
2721 : !> \param fm_V Coulomb matrix V(k=0)
2722 : !> \param fm_V_sqrt symmetric factor V^0.5
2723 : !> \param fm_Minv_Vsqrt product M^-1(k=0) V^0.5
2724 : ! **************************************************************************************************
2725 38 : SUBROUTINE compute_V_MinvVsqrt(bs_env, qs_env, fm_V, fm_V_sqrt, fm_Minv_Vsqrt)
2726 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2727 : TYPE(qs_environment_type), POINTER :: qs_env
2728 : TYPE(cp_fm_type), INTENT(INOUT) :: fm_V, fm_V_sqrt, fm_Minv_Vsqrt
2729 :
2730 : CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_V_MinvVsqrt'
2731 :
2732 : INTEGER :: handle
2733 38 : TYPE(atomic_kind_type), DIMENSION(:), POINTER :: atomic_kind_set
2734 : TYPE(cell_type), POINTER :: cell
2735 38 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: mat_V_kp
2736 38 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
2737 38 : TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kind_set
2738 :
2739 38 : CALL timeset(routineN, handle)
2740 :
2741 38 : IF (bs_env%auto_ri%enabled) THEN
2742 : ! -------------------------------------------------------------------
2743 : ! 1a. The optimized AB functions span two atoms, so their previously transformed
2744 : ! Coulomb matrix cannot be rebuilt by the atom-local k-point integral routine.
2745 : ! -------------------------------------------------------------------
2746 2 : CALL cp_fm_to_fm(bs_env%auto_ri%V_pq, fm_V)
2747 : ELSE
2748 : ! -------------------------------------------------------------------
2749 : ! 1b. Build Coulomb Matrix V(k=0) using the kp-routine but only for ikp=1.
2750 : ! -------------------------------------------------------------------
2751 : CALL get_qs_env(qs_env=qs_env, particle_set=particle_set, cell=cell, &
2752 36 : qs_kind_set=qs_kind_set, atomic_kind_set=atomic_kind_set)
2753 :
2754 180 : ALLOCATE (mat_V_kp(1:1, 1:2))
2755 36 : NULLIFY (mat_V_kp(1, 1)%matrix, mat_V_kp(1, 2)%matrix)
2756 36 : ALLOCATE (mat_V_kp(1, 1)%matrix, mat_V_kp(1, 2)%matrix)
2757 36 : CALL dbcsr_create(mat_V_kp(1, 1)%matrix, template=bs_env%mat_RI_RI%matrix)
2758 36 : CALL dbcsr_reserve_all_blocks(mat_V_kp(1, 1)%matrix)
2759 36 : CALL dbcsr_set(mat_V_kp(1, 1)%matrix, 0.0_dp)
2760 36 : CALL dbcsr_create(mat_V_kp(1, 2)%matrix, template=bs_env%mat_RI_RI%matrix)
2761 36 : CALL dbcsr_reserve_all_blocks(mat_V_kp(1, 2)%matrix)
2762 : ! The dummy imaginary part is required only by the k-point routine interface.
2763 36 : CALL dbcsr_set(mat_V_kp(1, 2)%matrix, 0.0_dp)
2764 :
2765 144 : bs_env%kpoints_chi_eps_W%nkp_grid = bs_env%nkp_grid_chi_eps_W_orig
2766 : CALL build_2c_coulomb_matrix_kp(mat_V_kp, bs_env%kpoints_chi_eps_W, "RI_AUX", cell, &
2767 : particle_set, qs_kind_set, atomic_kind_set, &
2768 36 : bs_env%size_lattice_sum_V, operator_coulomb, 1, 1)
2769 36 : CALL copy_dbcsr_to_fm(mat_V_kp(1, 1)%matrix, fm_V)
2770 :
2771 36 : CALL dbcsr_deallocate_matrix(mat_V_kp(1, 1)%matrix)
2772 36 : CALL dbcsr_deallocate_matrix(mat_V_kp(1, 2)%matrix)
2773 36 : DEALLOCATE (mat_V_kp)
2774 : END IF
2775 :
2776 : ! -----------------------------------------------------------------------
2777 : ! 2. V -> V^0.5.
2778 : ! -----------------------------------------------------------------------
2779 38 : CALL fm_sqrt(fm_V, fm_V_sqrt, bs_env%eps_eigval_mat_RI, bs_env%unit_nr)
2780 :
2781 : ! -----------------------------------------------------------------------
2782 : ! 3. M^-1(k=0) V^0.5.
2783 : ! -----------------------------------------------------------------------
2784 : CALL parallel_gemm("N", "T", bs_env%n_RI, bs_env%n_RI, bs_env%n_RI, 1.0_dp, &
2785 38 : bs_env%fm_Minv_Gamma, fm_V_sqrt, 0.0_dp, fm_Minv_Vsqrt)
2786 :
2787 38 : CALL timestop(handle)
2788 :
2789 38 : END SUBROUTINE compute_V_MinvVsqrt
2790 :
2791 : ! **************************************************************************************************
2792 : !> \brief Computes the screened interaction at one imaginary frequency:
2793 : !> ε(iω_j) = Id - (M^-1 V^0.5)^T χ(iω_j) (M^-1 V^0.5)
2794 : !> W(iω_j) = V^0.5^T ( ε^-1(iω_j) - Id ) V^0.5
2795 : !> ε is inverted via Cholesky; if that fails due to conditioning, via
2796 : !> eigendecomposition (cp_fm_power) with eigenvalue filtering.
2797 : !> \param bs_env ...
2798 : !> \param fm_chi_freq_j ...
2799 : !> \param fm_V_sqrt ...
2800 : !> \param fm_Minv_Vsqrt ...
2801 : !> \param fm_W_freq_j ...
2802 : ! **************************************************************************************************
2803 1590 : SUBROUTINE compute_fm_W_freq(bs_env, fm_chi_freq_j, fm_V_sqrt, fm_Minv_Vsqrt, fm_W_freq_j)
2804 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2805 : TYPE(cp_fm_type), INTENT(IN) :: fm_chi_freq_j, fm_V_sqrt, fm_Minv_Vsqrt
2806 : TYPE(cp_fm_type), INTENT(INOUT) :: fm_W_freq_j
2807 :
2808 : CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_fm_W_freq'
2809 :
2810 : INTEGER :: handle, n_RI
2811 : TYPE(cp_fm_type) :: fm_eps_freq_j, fm_work
2812 :
2813 530 : CALL timeset(routineN, handle)
2814 :
2815 530 : n_RI = bs_env%n_RI
2816 :
2817 530 : CALL cp_fm_create(fm_eps_freq_j, fm_chi_freq_j%matrix_struct)
2818 530 : CALL cp_fm_create(fm_work, fm_chi_freq_j%matrix_struct)
2819 :
2820 : ! -----------------------------------------------------------------------
2821 : ! 1. ε(iω_j) = Id - (M^-1 * V^0.5)^T * χ(iω_j) * (M^-1 * V^0.5)
2822 : ! -----------------------------------------------------------------------
2823 : ! work = χ(iω_j) * (M^-1 * V^0.5)
2824 : CALL parallel_gemm('N', 'N', n_RI, n_RI, n_RI, 1.0_dp, &
2825 530 : fm_chi_freq_j, fm_Minv_Vsqrt, 0.0_dp, fm_work)
2826 :
2827 : ! eps_work = (M^-1 * V^0.5)^T * work
2828 : CALL parallel_gemm('T', 'N', n_RI, n_RI, n_RI, 1.0_dp, &
2829 530 : fm_Minv_Vsqrt, fm_work, 0.0_dp, fm_eps_freq_j)
2830 :
2831 : ! ε(iω_j) = Id - eps_work --> -eps_work + Id
2832 530 : CALL fm_add_on_diag(fm_eps_freq_j, 1.0_dp)
2833 :
2834 : ! Force perfect symmetry before Cholesky to avoid info != 0 due to GEMM noise
2835 530 : CALL cp_fm_uplo_to_full(fm_eps_freq_j, fm_work)
2836 :
2837 : ! -----------------------------------------------------------------------
2838 : ! 2. W(iω_j) = V^0.5^T * (ε^-1(iω_j) - Id) * V^0.5
2839 : ! -----------------------------------------------------------------------
2840 :
2841 : ! a) Invert ε by Cholesky decomposition or, if that fails, by diagonalization.
2842 530 : CALL fm_invert(fm_eps_freq_j, bs_env%eps_eigval_mat_RI, bs_env%unit_nr)
2843 :
2844 : ! b) ε^-1(iω_j) - Id
2845 530 : CALL fm_add_on_diag(fm_eps_freq_j, -1.0_dp)
2846 :
2847 : ! c) W(iω_j) = V^0.5^T * (ε^-1(iω_j) - Id) * V^0.5
2848 530 : CALL fm_contract_ABA(fm_V_sqrt, fm_eps_freq_j, fm_W_freq_j)
2849 :
2850 : ! Cleanup
2851 530 : CALL cp_fm_release(fm_work)
2852 530 : CALL cp_fm_release(fm_eps_freq_j)
2853 :
2854 530 : CALL timestop(handle)
2855 :
2856 530 : END SUBROUTINE compute_fm_W_freq
2857 :
2858 : ! **************************************************************************************************
2859 : !> \brief Adds a real scalar value to the diagonal of a real full matrix
2860 : !> \param fm ...
2861 : !> \param alpha ...
2862 : ! **************************************************************************************************
2863 2120 : SUBROUTINE fm_add_on_diag(fm, alpha)
2864 : TYPE(cp_fm_type), INTENT(INOUT) :: fm
2865 : REAL(KIND=dp), INTENT(IN) :: alpha
2866 :
2867 : CHARACTER(LEN=*), PARAMETER :: routineN = 'fm_add_on_diag'
2868 :
2869 : INTEGER :: handle, i_global, i_row, j_col, &
2870 : j_global, ncol_local, nrow_local
2871 1060 : INTEGER, DIMENSION(:), POINTER :: col_indices, row_indices
2872 :
2873 1060 : CALL timeset(routineN, handle)
2874 :
2875 : CALL cp_fm_get_info(matrix=fm, &
2876 : nrow_local=nrow_local, &
2877 : ncol_local=ncol_local, &
2878 : row_indices=row_indices, &
2879 1060 : col_indices=col_indices)
2880 :
2881 37280 : DO j_col = 1, ncol_local
2882 36220 : j_global = col_indices(j_col)
2883 739390 : DO i_row = 1, nrow_local
2884 702110 : i_global = row_indices(i_row)
2885 738330 : IF (j_global == i_global) THEN
2886 18110 : fm%local_data(i_row, j_col) = fm%local_data(i_row, j_col) + alpha
2887 : END IF
2888 : END DO
2889 : END DO
2890 :
2891 1060 : CALL timestop(handle)
2892 :
2893 1060 : END SUBROUTINE fm_add_on_diag
2894 :
2895 : ! **************************************************************************************************
2896 : !> \brief Computes the exact-exchange part of the GW self-energy:
2897 : !> D_μν = Σ_n^occ C_μn C_νn (density matrix = G^occ at τ=0)
2898 : !> V^tr_PQ = M^-1 (P|Q)_trunc M^-1 (truncated Coulomb, RI basis)
2899 : !> Σ^x_λσ(k=0) = -Σ_ll' Φ_λ(r_l) [ (φ D φ^T)_ll' ∘ (Z V^tr Z^T)_ll' ] Φ_σ(r_l')
2900 : !> \param bs_env ...
2901 : !> \param qs_env ...
2902 : !> \param mat_phi_mu_l ...
2903 : !> \param mat_Z_lP ...
2904 : !> \param fm_Sigma_x_Gamma ...
2905 : ! **************************************************************************************************
2906 38 : SUBROUTINE compute_Sigma_x(bs_env, qs_env, mat_phi_mu_l, mat_Z_lP, fm_Sigma_x_Gamma)
2907 :
2908 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2909 : TYPE(qs_environment_type), POINTER :: qs_env
2910 : TYPE(dbcsr_type), INTENT(INOUT) :: mat_phi_mu_l, mat_Z_lP
2911 : TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_Sigma_x_Gamma
2912 :
2913 : CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_Sigma_x'
2914 :
2915 : INTEGER :: handle, ispin
2916 38 : INTEGER, ALLOCATABLE, DIMENSION(:) :: pan_first, pan_last
2917 38 : INTEGER, DIMENSION(:), POINTER :: blk_aux, dist_row_aux
2918 : REAL(KIND=dp) :: t1
2919 38 : TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :) :: fm_Vtr_Gamma
2920 : TYPE(dbcsr_distribution_type) :: dist_aux_aux
2921 : TYPE(dbcsr_type) :: mat_Sigma_x_Gamma, matrix_D_ao, &
2922 : matrix_V_aux
2923 :
2924 38 : CALL timeset(routineN, handle)
2925 :
2926 38 : t1 = m_walltime()
2927 :
2928 158 : ALLOCATE (fm_Sigma_x_Gamma(bs_env%n_spin))
2929 82 : DO ispin = 1, bs_env%n_spin
2930 82 : CALL cp_fm_create(fm_Sigma_x_Gamma(ispin), bs_env%fm_s_Gamma%matrix_struct)
2931 : END DO
2932 :
2933 38 : CALL dbcsr_create(mat_Sigma_x_Gamma, template=bs_env%mat_ao_ao%matrix)
2934 :
2935 38 : CALL resolve_grid_panels(bs_env, mat_phi_mu_l, pan_first, pan_last)
2936 :
2937 : ! =========================================================================
2938 : ! 1. COMPUTE V^tr_PQ (RI x RI)
2939 : ! =========================================================================
2940 38 : CALL setup_square_topology(mat_Z_lP, dist_aux_aux, blk_aux, dist_row_aux)
2941 :
2942 38 : IF (bs_env%auto_ri%enabled) THEN
2943 6 : ALLOCATE (fm_Vtr_Gamma(1, 1))
2944 2 : CALL cp_fm_create(fm_Vtr_Gamma(1, 1), bs_env%fm_RI_RI%matrix_struct)
2945 2 : CALL cp_fm_to_fm(bs_env%auto_ri%V_pq, fm_Vtr_Gamma(1, 1))
2946 : ELSE
2947 : CALL RI_2c_integral_mat(qs_env, fm_Vtr_Gamma, bs_env%fm_RI_RI, bs_env%n_RI, &
2948 36 : bs_env%trunc_coulomb, do_kpoints=.FALSE.)
2949 : END IF
2950 :
2951 : ! M^-1(k=0) V^tr(τ) M^-1(k=0) -> fm_Vtr_Gamma
2952 38 : CALL fm_contract_ABA(bs_env%fm_Minv_Gamma, fm_Vtr_Gamma(:, 1))
2953 :
2954 38 : CALL dbcsr_create(matrix_V_aux, "V_aux", dist_aux_aux, dbcsr_type_no_symmetry, blk_aux, blk_aux)
2955 : ! Optional CUTOFF_RADIUS_G_W operator truncation + filter (see build_G_ao)
2956 38 : IF (bs_env%ri_rs%cutoff_radius_g_w > 0.0_dp .AND. ALLOCATED(bs_env%ri_rs%atom_centers)) THEN
2957 : CALL reserve_blocks_within_radius(matrix_V_aux, bs_env%ri_rs%atom_centers, &
2958 0 : bs_env%ri_rs%cutoff_radius_g_w)
2959 0 : CALL copy_fm_to_dbcsr(fm_Vtr_Gamma(1, 1), matrix_V_aux, keep_sparsity=.TRUE.)
2960 : ELSE
2961 38 : CALL copy_fm_to_dbcsr(fm_Vtr_Gamma(1, 1), matrix_V_aux, keep_sparsity=.FALSE.)
2962 : END IF
2963 38 : CALL dbcsr_filter(matrix_V_aux, bs_env%eps_filter)
2964 :
2965 : ! =========================================================================
2966 : ! 2. SPIN LOOP FOR EXACT EXCHANGE
2967 : ! Σ^x_λσ = -Σ_ll' Φ_λ(r_l) ( D_ll' V^tr_ll' ) Φ_σ(r_l')
2968 : ! = -φ^T ( (φ D φ^T) ∘ (Z V^tr Z^T) ) φ
2969 : ! =========================================================================
2970 82 : DO ispin = 1, bs_env%n_spin
2971 :
2972 : ! AO-space density matrix D_µν = G^occ at τ = 0
2973 44 : CALL build_G_ao(bs_env, 0.0_dp, ispin, .TRUE., .FALSE., mat_phi_mu_l, matrix_D_ao)
2974 :
2975 : CALL contract_grid_panels(L_A=mat_phi_mu_l, M_A=matrix_D_ao, &
2976 : L_B=mat_Z_lP, M_B=matrix_V_aux, &
2977 : L_out=mat_phi_mu_l, mat_out=mat_Sigma_x_Gamma, &
2978 : scale=1.0_dp, eps=bs_env%eps_filter, &
2979 : para_env=bs_env%para_env, &
2980 : pan_first=pan_first, pan_last=pan_last, &
2981 : lb_eq_la=.FALSE., lout_eq_la=.TRUE., zero_out=.TRUE., &
2982 : keep_sparsity=bs_env%ri_rs%keep_sparsity_rirs, &
2983 : centroids=bs_env%ri_rs%chunk_centroids, &
2984 44 : cutoff=bs_env%ri_rs%cutoff_radius_v_w)
2985 44 : CALL dbcsr_scale(mat_Sigma_x_Gamma, -1.0_dp)
2986 :
2987 44 : CALL dbcsr_release(matrix_D_ao)
2988 :
2989 : ! Data I/O and Export to CP2K Full Matrices
2990 82 : CALL copy_dbcsr_to_fm(mat_Sigma_x_Gamma, fm_Sigma_x_Gamma(ispin))
2991 :
2992 : END DO ! ispin
2993 :
2994 38 : IF (bs_env%unit_nr > 0) THEN
2995 : WRITE (bs_env%unit_nr, '(T2,A,T58,A,F7.1,A)') &
2996 19 : 'Computed Σ^x(k=0),', ' Execution time', m_walltime() - t1, ' s'
2997 19 : WRITE (bs_env%unit_nr, '(A)') ' '
2998 : END IF
2999 :
3000 : ! =========================================================================
3001 : ! 3. CLEANUP
3002 : ! =========================================================================
3003 38 : CALL dbcsr_release(matrix_V_aux)
3004 38 : CALL dbcsr_release(mat_Sigma_x_Gamma)
3005 38 : CALL release_square_topology(dist=dist_aux_aux, mapped_dist=dist_row_aux)
3006 :
3007 38 : CALL cp_fm_release(fm_Vtr_Gamma)
3008 :
3009 38 : CALL timestop(handle)
3010 :
3011 114 : END SUBROUTINE compute_Sigma_x
3012 :
3013 : ! **************************************************************************************************
3014 : !> \brief Computes the correlation part of the GW self-energy on the imaginary-time grid:
3015 : !> Σ^c_λσ(iτ<0) = -Σ_ll' Φ_λ(r_l) [ (φ G^occ φ^T)_ll' ∘ (Z W^MIC Z^T)_ll' ] Φ_σ(r_l')
3016 : !> Σ^c_λσ(iτ>0) = +Σ_ll' Φ_λ(r_l) [ (φ G^vir φ^T)_ll' ∘ (Z W^MIC Z^T)_ll' ] Φ_σ(r_l')
3017 : !> \param bs_env ...
3018 : !> \param fm_W_time ...
3019 : !> \param mat_phi_mu_l ...
3020 : !> \param mat_Z_lP ...
3021 : !> \param fm_Sigma_c_Gamma_time ...
3022 : ! **************************************************************************************************
3023 52 : SUBROUTINE compute_Sigma_c(bs_env, fm_W_time, mat_phi_mu_l, mat_Z_lP, fm_Sigma_c_Gamma_time)
3024 :
3025 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
3026 : TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_W_time
3027 : TYPE(dbcsr_type), INTENT(INOUT) :: mat_phi_mu_l, mat_Z_lP
3028 : TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :, :) :: fm_Sigma_c_Gamma_time
3029 :
3030 : CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_Sigma_c'
3031 :
3032 : INTEGER :: handle, i_t, ispin
3033 52 : INTEGER, ALLOCATABLE, DIMENSION(:) :: pan_first, pan_last
3034 52 : INTEGER, DIMENSION(:), POINTER :: blk_aux, dist_row_aux
3035 : REAL(KIND=dp) :: t1, tau
3036 : TYPE(dbcsr_distribution_type) :: dist_aux_aux
3037 52 : TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: mat_Sigma_neg_tau, mat_Sigma_pos_tau
3038 : TYPE(dbcsr_type) :: matrix_G_occ_ao, matrix_G_vir_ao, &
3039 : matrix_W_aux
3040 :
3041 52 : CALL timeset(routineN, handle)
3042 :
3043 : ! =========================================================================
3044 : ! 1. SETUP AUXILIARY TOPOLOGY AND PRE-ALLOCATE OUTPUT ARRAYS
3045 : ! =========================================================================
3046 52 : CALL setup_square_topology(mat_Z_lP, dist_aux_aux, blk_aux, dist_row_aux)
3047 :
3048 52 : CALL resolve_grid_panels(bs_env, mat_phi_mu_l, pan_first, pan_last)
3049 :
3050 : ! Pre-allocate local DBCSR matrices to act as targets for final output
3051 52 : NULLIFY (mat_Sigma_neg_tau, mat_Sigma_pos_tau)
3052 1082 : ALLOCATE (mat_Sigma_neg_tau(bs_env%num_time_freq_points, bs_env%n_spin))
3053 1082 : ALLOCATE (mat_Sigma_pos_tau(bs_env%num_time_freq_points, bs_env%n_spin))
3054 :
3055 722 : DO i_t = 1, bs_env%num_time_freq_points
3056 1532 : DO ispin = 1, bs_env%n_spin
3057 810 : ALLOCATE (mat_Sigma_neg_tau(i_t, ispin)%matrix)
3058 810 : ALLOCATE (mat_Sigma_pos_tau(i_t, ispin)%matrix)
3059 810 : CALL dbcsr_create(mat_Sigma_neg_tau(i_t, ispin)%matrix, template=bs_env%mat_ao_ao%matrix)
3060 1480 : CALL dbcsr_create(mat_Sigma_pos_tau(i_t, ispin)%matrix, template=bs_env%mat_ao_ao%matrix)
3061 : END DO
3062 : END DO
3063 :
3064 : ! =========================================================================
3065 : ! 2. IMAGINARY TIME LOOP
3066 : ! Σ^c_neg_λσ(iτ) = -φ^T ( (φ G^occ φ^T) ∘ (Z W^MIC Z^T) ) φ
3067 : ! Σ^c_pos_λσ(iτ) = φ^T ( (φ G^vir φ^T) ∘ (Z W^MIC Z^T) ) φ
3068 : ! =========================================================================
3069 722 : DO i_t = 1, bs_env%num_time_freq_points
3070 670 : tau = bs_env%time_frequency_grid%imaginary_time(i_t)
3071 :
3072 : CALL dbcsr_create(matrix_W_aux, "W_aux", dist_aux_aux, dbcsr_type_no_symmetry, &
3073 670 : blk_aux, blk_aux)
3074 670 : IF (bs_env%ri_rs%cutoff_radius_g_w > 0.0_dp .AND. ALLOCATED(bs_env%ri_rs%atom_centers)) THEN
3075 : CALL reserve_blocks_within_radius(matrix_W_aux, bs_env%ri_rs%atom_centers, &
3076 0 : bs_env%ri_rs%cutoff_radius_g_w)
3077 0 : CALL copy_fm_to_dbcsr(fm_W_time(i_t), matrix_W_aux, keep_sparsity=.TRUE.)
3078 : ELSE
3079 670 : CALL copy_fm_to_dbcsr(fm_W_time(i_t), matrix_W_aux, keep_sparsity=.FALSE.)
3080 : END IF
3081 670 : CALL dbcsr_filter(matrix_W_aux, bs_env%eps_filter)
3082 :
3083 1480 : DO ispin = 1, bs_env%n_spin
3084 810 : t1 = m_walltime()
3085 :
3086 : ! AO-space Green's functions G^occ_µν, G^vir_µν (dense AO x AO, small)
3087 810 : CALL build_G_ao(bs_env, tau, ispin, .TRUE., .FALSE., mat_phi_mu_l, matrix_G_occ_ao)
3088 810 : CALL build_G_ao(bs_env, tau, ispin, .FALSE., .TRUE., mat_phi_mu_l, matrix_G_vir_ao)
3089 :
3090 : ! Σ^c_neg and Σ^c_pos in a single panel loop: W_pan = Z_panel × W × Z^T built once
3091 : CALL contract_grid_panels_sigma_c(mat_phi=mat_phi_mu_l, mat_Z=mat_Z_lP, &
3092 : mat_G_occ_ao=matrix_G_occ_ao, &
3093 : mat_G_vir_ao=matrix_G_vir_ao, &
3094 : mat_W_aux=matrix_W_aux, &
3095 : mat_Sigma_neg=mat_Sigma_neg_tau(i_t, ispin)%matrix, &
3096 : mat_Sigma_pos=mat_Sigma_pos_tau(i_t, ispin)%matrix, &
3097 : eps=bs_env%eps_filter, &
3098 : para_env=bs_env%para_env, &
3099 : pan_first=pan_first, pan_last=pan_last, &
3100 : keep_sparsity=bs_env%ri_rs%keep_sparsity_rirs, &
3101 : centroids=bs_env%ri_rs%chunk_centroids, &
3102 810 : cutoff=bs_env%ri_rs%cutoff_radius_v_w)
3103 810 : CALL dbcsr_scale(mat_Sigma_neg_tau(i_t, ispin)%matrix, -1.0_dp)
3104 :
3105 810 : CALL dbcsr_release(matrix_G_occ_ao)
3106 810 : CALL dbcsr_release(matrix_G_vir_ao)
3107 :
3108 1480 : IF (bs_env%unit_nr > 0) THEN
3109 : WRITE (bs_env%unit_nr, '(T2,A,I15,A,I3,A,F7.1,A)') &
3110 405 : 'Computed Σ^c(iτ) for time point', i_t, ' /', bs_env%num_time_freq_points, &
3111 810 : ', Execution time', m_walltime() - t1, ' s'
3112 : END IF
3113 :
3114 : END DO ! ispin
3115 :
3116 722 : CALL dbcsr_release(matrix_W_aux)
3117 :
3118 : END DO ! i_t
3119 :
3120 : ! -------------------------------------------------------------------------
3121 : ! 3. FINALIZE AND CLEANUP
3122 : ! -------------------------------------------------------------------------
3123 : CALL fill_fm_Sigma_c_Gamma_time(fm_Sigma_c_Gamma_time, bs_env, &
3124 52 : mat_Sigma_pos_tau, mat_Sigma_neg_tau)
3125 :
3126 : ! fm_W_time and the scratch files are released by the caller: in an evGW0 cycle this
3127 : ! routine is entered once per iteration and both have to survive until it is done.
3128 52 : CALL dbcsr_deallocate_matrix_set(mat_Sigma_neg_tau)
3129 52 : CALL dbcsr_deallocate_matrix_set(mat_Sigma_pos_tau)
3130 :
3131 52 : CALL release_square_topology(dist=dist_aux_aux, mapped_dist=dist_row_aux)
3132 :
3133 52 : CALL timestop(handle)
3134 :
3135 104 : END SUBROUTINE compute_Sigma_c
3136 :
3137 : ! **************************************************************************************************
3138 : !> \brief Builds the DBCSR distribution.
3139 : !> \param matrix_template ...
3140 : !> \param square_dist ...
3141 : !> \param blk_sizes ...
3142 : !> \param mapped_dist ...
3143 : ! **************************************************************************************************
3144 2974 : SUBROUTINE setup_square_topology(matrix_template, square_dist, blk_sizes, mapped_dist)
3145 :
3146 : TYPE(dbcsr_type), INTENT(IN) :: matrix_template
3147 : TYPE(dbcsr_distribution_type), INTENT(OUT) :: square_dist
3148 : INTEGER, DIMENSION(:), INTENT(OUT), POINTER :: blk_sizes, mapped_dist
3149 :
3150 : CHARACTER(LEN=*), PARAMETER :: routineN = 'setup_square_topology'
3151 :
3152 : INTEGER :: handle, i, nprows
3153 2974 : INTEGER, DIMENSION(:), POINTER :: col_blk, col_dist
3154 : TYPE(dbcsr_distribution_type) :: dist_template
3155 :
3156 2974 : CALL timeset(routineN, handle)
3157 :
3158 2974 : CALL dbcsr_get_info(matrix_template, distribution=dist_template, col_blk_size=col_blk)
3159 2974 : CALL dbcsr_distribution_get(dist_template, col_dist=col_dist, nprows=nprows)
3160 :
3161 2974 : blk_sizes => col_blk
3162 8922 : ALLOCATE (mapped_dist(SIZE(blk_sizes)))
3163 9990 : DO i = 1, SIZE(blk_sizes)
3164 9990 : mapped_dist(i) = MOD(i - 1, nprows)
3165 : END DO
3166 : CALL dbcsr_distribution_new(square_dist, template=dist_template, &
3167 2974 : row_dist=mapped_dist, col_dist=col_dist)
3168 :
3169 2974 : CALL timestop(handle)
3170 :
3171 5948 : END SUBROUTINE setup_square_topology
3172 :
3173 : ! **************************************************************************************************
3174 : !> \brief Releases a distribution created by setup_square_topology.
3175 : !> \param dist ...
3176 : !> \param mapped_dist ...
3177 : ! **************************************************************************************************
3178 2974 : SUBROUTINE release_square_topology(dist, mapped_dist)
3179 :
3180 : TYPE(dbcsr_distribution_type), INTENT(INOUT) :: dist
3181 : INTEGER, DIMENSION(:), INTENT(INOUT), POINTER :: mapped_dist
3182 :
3183 2974 : CALL dbcsr_distribution_release(dist)
3184 2974 : IF (ASSOCIATED(mapped_dist)) THEN
3185 2974 : DEALLOCATE (mapped_dist)
3186 : NULLIFY (mapped_dist)
3187 : END IF
3188 :
3189 2974 : END SUBROUTINE release_square_topology
3190 :
3191 : ! **************************************************************************************************
3192 : !> \brief Σ^c_λσ(iτ) -> Σ^c_nn(ϵ) and the quasi-particle levels of the non-periodic RI-RS path,
3193 : !> ϵ_n^GW = ϵ_n^DFT + Σ^c_nn(ϵ_n^GW) + Σ^x_nn - v^xc_nn.
3194 : !> \param bs_env ...
3195 : !> \param fm_Sigma_x_Gamma ...
3196 : !> \param fm_Sigma_c_Gamma_time ...
3197 : ! **************************************************************************************************
3198 52 : SUBROUTINE compute_QP_energies(bs_env, fm_Sigma_x_Gamma, fm_Sigma_c_Gamma_time)
3199 :
3200 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
3201 : TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_Sigma_x_Gamma
3202 : TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :, :) :: fm_Sigma_c_Gamma_time
3203 :
3204 : CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_QP_energies'
3205 :
3206 : INTEGER :: handle, ispin, j_t
3207 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: Sigma_x_n, V_xc_n
3208 : REAL(KIND=dp), ALLOCATABLE, DIMENSION(:, :, :) :: Sigma_c_n_freq, Sigma_c_n_time
3209 : TYPE(cp_fm_type) :: fm_ks, fm_mos, fm_s, fm_work
3210 :
3211 52 : CALL timeset(routineN, handle)
3212 :
3213 52 : CALL cp_fm_create(fm_ks, bs_env%fm_s_Gamma%matrix_struct)
3214 52 : CALL cp_fm_create(fm_s, bs_env%fm_s_Gamma%matrix_struct)
3215 52 : CALL cp_fm_create(fm_mos, bs_env%fm_s_Gamma%matrix_struct)
3216 52 : CALL cp_fm_create(fm_work, bs_env%fm_s_Gamma%matrix_struct)
3217 :
3218 208 : ALLOCATE (V_xc_n(bs_env%n_ao), Sigma_x_n(bs_env%n_ao))
3219 260 : ALLOCATE (Sigma_c_n_time(bs_env%n_ao, bs_env%num_time_freq_points, 2))
3220 156 : ALLOCATE (Sigma_c_n_freq(bs_env%n_ao, bs_env%num_time_freq_points, 2))
3221 :
3222 116 : DO ispin = 1, bs_env%n_spin
3223 :
3224 : ! 1. Roothaan-Hall H^KS_µν C_νn = S_µν C_νn ϵ_n
3225 64 : CALL cp_fm_to_fm(bs_env%fm_ks_Gamma(ispin), fm_ks)
3226 64 : CALL cp_fm_to_fm(bs_env%fm_s_Gamma, fm_s)
3227 64 : CALL cp_fm_geeig(fm_ks, fm_s, fm_mos, bs_env%eigenval_scf(:, 1, ispin), fm_work)
3228 :
3229 : ! 2. v^xc_µν -> v^xc_nn and Σ^x_µν -> Σ^x_nn
3230 64 : CALL to_Gamma_and_mo_real(V_xc_n, bs_env%fm_V_xc_Gamma(ispin), fm_mos)
3231 64 : CALL to_Gamma_and_mo_real(Sigma_x_n, fm_Sigma_x_Gamma(ispin), fm_mos)
3232 :
3233 : ! 3. Σ^c_µν(+/-i|τ_j|) -> Σ^c_nn(+/-i|τ_j|)
3234 874 : DO j_t = 1, bs_env%num_time_freq_points
3235 : CALL to_Gamma_and_mo_real(Sigma_c_n_time(:, j_t, 1), &
3236 810 : fm_Sigma_c_Gamma_time(j_t, 1, ispin), fm_mos)
3237 : CALL to_Gamma_and_mo_real(Sigma_c_n_time(:, j_t, 2), &
3238 874 : fm_Sigma_c_Gamma_time(j_t, 2, ispin), fm_mos)
3239 : END DO
3240 :
3241 : ! 4. Σ^c_nn(iτ) -> Σ^c_nn(iω)
3242 64 : CALL time_to_freq(bs_env, Sigma_c_n_time, Sigma_c_n_freq, ispin)
3243 :
3244 : ! 5. Analytic continuation Σ^c_nn(iω) -> Σ^c_nn(ϵ) and the QP levels
3245 : CALL analyt_conti_and_print(bs_env, Sigma_c_n_freq, Sigma_x_n, V_xc_n, &
3246 116 : bs_env%eigenval_scf(:, 1, ispin), 1, ispin)
3247 :
3248 : END DO ! ispin
3249 :
3250 52 : CALL get_all_VBM_CBM_bandgaps(bs_env)
3251 :
3252 52 : IF (bs_env%gw_flavour == G0W0) CALL cp_fm_release(fm_Sigma_x_Gamma)
3253 52 : CALL cp_fm_release(fm_Sigma_c_Gamma_time)
3254 :
3255 52 : CALL cp_fm_release(fm_ks)
3256 52 : CALL cp_fm_release(fm_s)
3257 52 : CALL cp_fm_release(fm_mos)
3258 52 : CALL cp_fm_release(fm_work)
3259 :
3260 52 : CALL timestop(handle)
3261 :
3262 104 : END SUBROUTINE compute_QP_energies
3263 :
3264 : ! **************************************************************************************************
3265 : !> \brief AO -> MO transform of a Γ-point matrix
3266 : !> \param array_n ...
3267 : !> \param fm_Gamma ...
3268 : !> \param fm_mos ...
3269 : ! **************************************************************************************************
3270 1748 : SUBROUTINE to_Gamma_and_mo_real(array_n, fm_Gamma, fm_mos)
3271 :
3272 : REAL(KIND=dp), DIMENSION(:) :: array_n
3273 : TYPE(cp_fm_type) :: fm_Gamma, fm_mos
3274 :
3275 : CHARACTER(LEN=*), PARAMETER :: routineN = 'to_Gamma_and_mo_real'
3276 :
3277 : INTEGER :: handle
3278 : TYPE(cp_fm_type) :: fm_mo
3279 :
3280 1748 : CALL timeset(routineN, handle)
3281 :
3282 1748 : CALL cp_fm_create(fm_mo, fm_Gamma%matrix_struct)
3283 :
3284 : ! A_nn' = Σ_μν C_μn A_μν C_νn'
3285 1748 : CALL fm_contract_ABA(fm_mos, fm_Gamma, fm_mo)
3286 :
3287 1748 : CALL cp_fm_get_diag(fm_mo, array_n)
3288 :
3289 1748 : CALL cp_fm_release(fm_mo)
3290 :
3291 1748 : CALL timestop(handle)
3292 :
3293 1748 : END SUBROUTINE to_Gamma_and_mo_real
3294 :
3295 : ! **************************************************************************************************
3296 : !> \brief Evaluates the atom-local AO functions Φ_μ(r_l) on the RI-RS grid points.
3297 : !> \param phi_val Values Φ_μ(r_l) for the requested atom
3298 : !> \param ri_rs_grid Cartesian RI-RS grid points r_l
3299 : !> \param npts Number of RI-RS grid points
3300 : !> \param iatom Atom whose AO functions are evaluated
3301 : !> \param particle_set Atomic positions and kind assignments
3302 : !> \param qs_kind_set Atomic-kind basis information
3303 : !> \param cell Simulation cell and boundary conditions
3304 : !> \param r2_threshold Squared radial cutoff for neglecting AO values
3305 : ! **************************************************************************************************
3306 56 : SUBROUTINE fill_phi_for_atom(phi_val, ri_rs_grid, npts, iatom, &
3307 : particle_set, qs_kind_set, cell, r2_threshold)
3308 :
3309 : REAL(KIND=dp), INTENT(INOUT) :: phi_val(:, :)
3310 : INTEGER, INTENT(IN) :: npts
3311 : REAL(KIND=dp), INTENT(IN) :: ri_rs_grid(3, npts)
3312 : INTEGER, INTENT(IN) :: iatom
3313 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
3314 : TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kind_set
3315 : TYPE(cell_type), POINTER :: cell
3316 : REAL(KIND=dp), INTENT(IN) :: r2_threshold
3317 :
3318 : CHARACTER(LEN=*), PARAMETER :: routineN = 'fill_phi_for_atom'
3319 :
3320 : INTEGER :: handle
3321 :
3322 56 : CALL timeset(routineN, handle)
3323 :
3324 : CALL evaluate_ao_on_points(phi_val, ri_rs_grid, iatom, particle_set, qs_kind_set, cell, &
3325 56 : cutoff_squared=r2_threshold)
3326 :
3327 56 : CALL timestop(handle)
3328 :
3329 56 : END SUBROUTINE fill_phi_for_atom
3330 :
3331 : ! **************************************************************************************************
3332 : !> \brief Counts the AO functions whose radial support intersects an RI fitting sphere.
3333 : !> \param bs_env GW calculation environment and atom-to-AO offsets
3334 : !> \param particle_set Atomic positions
3335 : !> \param atom_P Atom at the center of the RI fitting sphere
3336 : !> \param cutoff_ri Radius of the RI fitting sphere
3337 : !> \param n_ao_used Number of intersecting AO functions
3338 : ! **************************************************************************************************
3339 96 : SUBROUTINE get_n_ao_in_sphere(bs_env, particle_set, atom_P, cutoff_ri, n_ao_used)
3340 :
3341 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
3342 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
3343 : INTEGER, INTENT(IN) :: atom_P
3344 : REAL(KIND=dp), INTENT(IN) :: cutoff_ri
3345 : INTEGER, INTENT(OUT) :: n_ao_used
3346 :
3347 : CHARACTER(LEN=*), PARAMETER :: routineN = 'get_n_ao_in_sphere'
3348 :
3349 : INTEGER :: handle, ri_atom
3350 :
3351 96 : CALL timeset(routineN, handle)
3352 :
3353 96 : n_ao_used = 0
3354 348 : DO ri_atom = 1, bs_env%n_atom
3355 1008 : IF (NORM2(particle_set(ri_atom)%r(:) - particle_set(atom_P)%r(:)) > &
3356 : bs_env%ri_rs%radius_ao_per_atom(ri_atom) + cutoff_ri) CYCLE
3357 : n_ao_used = n_ao_used + bs_env%i_ao_end_from_atom(ri_atom) - &
3358 348 : bs_env%i_ao_start_from_atom(ri_atom) + 1
3359 : END DO
3360 :
3361 96 : CALL timestop(handle)
3362 :
3363 96 : END SUBROUTINE get_n_ao_in_sphere
3364 :
3365 : ! **************************************************************************************************
3366 : !> \brief Prints the percentage of non-zero elements in a distributed RI-RS matrix.
3367 : !> \param matrix Distributed matrix whose occupation is reported
3368 : !> \param label Mathematical matrix label used in the output
3369 : !> \param bs_env GW calculation environment and output unit
3370 : !> \param suffix Optional text appended to the matrix label
3371 : ! **************************************************************************************************
3372 122 : SUBROUTINE print_matrix_occupation(matrix, label, bs_env, suffix)
3373 :
3374 : TYPE(dbcsr_type), INTENT(IN) :: matrix
3375 : CHARACTER(LEN=*), INTENT(IN) :: label
3376 : TYPE(post_scf_bandstructure_type), POINTER :: bs_env
3377 : CHARACTER(LEN=*), INTENT(IN), OPTIONAL :: suffix
3378 :
3379 : CHARACTER(LEN=*), PARAMETER :: routineN = 'print_matrix_occupation'
3380 :
3381 : CHARACTER(LEN=32) :: output_format
3382 : CHARACTER(LEN=max_line_length) :: msg, output_label
3383 : INTEGER :: handle, i, unicode_shift
3384 : REAL(KIND=dp) :: frac_2p31, max_loc, occ
3385 :
3386 122 : CALL timeset(routineN, handle)
3387 :
3388 122 : occ = dbcsr_get_occupation(matrix)
3389 122 : max_loc = REAL(dbcsr_get_data_size(matrix), dp)
3390 122 : CALL bs_env%para_env%max(max_loc)
3391 :
3392 122 : IF (bs_env%unit_nr > 0) THEN
3393 61 : frac_2p31 = max_loc/REAL(bs_env%dbcsr_msg_elem_limit, dp)
3394 61 : output_label = 'Percentage of non-zero matrix elements in '//TRIM(label)
3395 61 : IF (PRESENT(suffix)) output_label = TRIM(output_label)//TRIM(suffix)
3396 : ! Fortran counts UTF-8 bytes, whereas the terminal displays each Greek letter in one
3397 : ! column. Shift the absolute output tab once for every continuation byte.
3398 61 : unicode_shift = 0
3399 3347 : DO i = 1, LEN_TRIM(output_label)
3400 3347 : IF (IAND(IACHAR(output_label(i:i)), 192) == 128) unicode_shift = unicode_shift + 1
3401 : END DO
3402 61 : WRITE (output_format, '(A,I0,A)') '(T2,A,T', 72 + unicode_shift, ',F7.2,A)'
3403 61 : WRITE (bs_env%unit_nr, output_format) TRIM(output_label), occ*100.0_dp, ' %'
3404 61 : IF (frac_2p31 > 0.5_dp) THEN
3405 : WRITE (msg, '(3A,F0.2,A)') &
3406 0 : "The largest per-rank message of ", TRIM(label), " reaches ", frac_2p31, &
3407 : " of the 32-bit limit that DBCSR uses for its message length. Beyond it the "// &
3408 : "length overflows and multiply_cannon fails. Reduce the per-rank block size, "// &
3409 0 : "for instance with more MPI ranks or a larger N_PANELS."
3410 0 : CPWARN(TRIM(msg))
3411 : END IF
3412 61 : CALL m_flush(bs_env%unit_nr)
3413 : END IF
3414 :
3415 122 : CALL timestop(handle)
3416 :
3417 122 : END SUBROUTINE print_matrix_occupation
3418 :
3419 : END MODULE gw_non_periodic_ri_rs
|