(git:f2099e5)
Loading...
Searching...
No Matches
gw_ri_rs_non_periodic.F
Go to the documentation of this file.
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
17 USE cell_types, ONLY: cell_type
19 USE cp_dbcsr_api, ONLY: &
25 dbcsr_release, dbcsr_scale, dbcsr_set, dbcsr_type, dbcsr_type_no_symmetry
33 USE cp_fm_diag, ONLY: cp_fm_geeig
34 USE cp_fm_types, ONLY: cp_fm_create,&
46 g_occ_vir,&
56 USE gw_utils_fm, ONLY: fm_contract_aba,&
57 fm_invert,&
59 USE input_constants, ONLY: g0w0,&
60 evgw0,&
62 USE kinds, ONLY: dp,&
63 int_8,&
66 USE machine, ONLY: m_flush,&
74 USE physcon, ONLY: evolt
80#include "./base/base_uses.f90"
81
82 IMPLICIT NONE
83
84 PRIVATE
85
86 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'gw_ri_rs_non_periodic'
87
90
91CONTAINS
92
93! **************************************************************************************************
94!> \brief GW calculation using RI-RS formalism for molecules
95!> \param qs_env ...
96!> \param bs_env Band-structure environment containing GW parameters.
97! **************************************************************************************************
98 SUBROUTINE gw_calc_ri_rs_non_periodic(qs_env, bs_env)
99
100 TYPE(qs_environment_type), POINTER :: qs_env
101 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
102
103 CHARACTER(LEN=*), PARAMETER :: routinen = 'gw_calc_ri_rs_non_periodic'
104
105 INTEGER :: handle
106 TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_sigma_x_gamma, fm_w_time
107
108 CALL timeset(routinen, handle)
109
110 ! ==============================================================================
111 ! 0. Precompute AO and RI radii
112 ! Per-atom cutoff radii from the most diffuse Gaussian primitives of
113 ! the AO ("ORB") and RI auxiliary ("RI_AUX") basis sets:
114 ! α_min,ao = min { ζ_ao | ζ_ao > 10⁻³ }, α_min,ri analogous
115 ! r_ao = sqrt( -ln(ε) / α_min,ao ) (radius_ao_per_atom)
116 ! r_ri = sqrt( -ln(ε) / α_min,ri ) (radius_ri_per_atom)
117 ! ==============================================================================
118 CALL precompute_ri_rs_radii(bs_env)
119
120 ! ==============================================================================
121 ! 1. Grid generation for RI-RS
122 ! Modified Lebedev atomic grids (Duchemin & Blase), one per atom,
123 ! concatenated into a flat global list: r_l = R_A + r_l^(A)
124 ! ==============================================================================
125 CALL setup_ri_rs_grid(bs_env, bs_env%ri_rs%grid_points)
126
127 ! ==============================================================================
128 ! 2a. Atomic basis evaluation on the grid (grid x AO matrix)
129 ! ϕ_μl = ϕ_μ(r_l) (mat_phi_mu_l)
130 ! ==============================================================================
131 CALL atomic_basis_at_grid_point(bs_env, bs_env%ri_rs%grid_points, &
132 bs_env%ri_rs%mat_phi_mu_l)
133
134 ! ==============================================================================
135 ! 2b. Print the memory estimate for the RI-RS calculation
136 ! ==============================================================================
137 CALL print_ri_rs_memory_estimate(bs_env)
138
139 ! ==============================================================================
140 ! 3. RI-RS fitting coefficients Z_lP (grid x RI matrix)
141 ! Per-atom regularized solve, restricted to grid points r_l within a
142 ! cutoff distance of atom P:
143 ! a. D_ll' = [ Σ_μ ϕ_μ(r_l) ϕ_μ(r_l') ]²
144 ! b. D_lP = Σ_μν ϕ_μ(r_l) ϕ_ν(r_l) (μν|P)
145 ! c. Jacobi conditioning with d_l = 1/sqrt(D_ll):
146 ! D'_ll' = d_l D_ll' d_l' + λδ_ll' , D'_lP = d_l D_lP
147 ! d. Solve Σ_l' D'_ll' Z'_l'P = D'_lP
148 ! e. Rescale Z_lP = d_l Z'_lP (mat_Z_lP)
149 ! ==============================================================================
150 CALL compute_z_lp(qs_env, bs_env, bs_env%ri_rs%grid_points, &
151 bs_env%ri_rs%mat_phi_mu_l, bs_env%ri_rs%mat_Z_lP)
152
153 ! flag the RI-RS grid as built so a subsequent RT-BSE run reuses Z_lP
154 ! instead of rebuilding it
155 bs_env%ri_rs%grid_built = .true.
156
157 CALL mp_print_mem_per_rank(bs_env%para_env, bs_env%unit_nr, &
158 label='Memory per MPI process after computing Z_lP:')
159
160 ! ==============================================================================
161 ! 4. Polarizability matrix χ on the imaginary-time grid
162 ! G^occ_µλ(i|τ|) = Σ_n^occ C_µn e^(-|(ϵ_n-ϵ_F)τ|) C_λn
163 ! G^vir_µλ(i|τ|) = Σ_n^vir C_µn e^(-|(ϵ_n-ϵ_F)τ|) C_λn
164 ! G^occ_ll'(i|τ|) = Σ_µν ϕ_µ(r_l) G^occ_µν ϕ_ν(r_l') (G^vir analogous)
165 ! χ_ll'(iτ) = G^occ_ll'(i|τ|) ∘ G^vir_ll'(i|τ|) (element-wise)
166 ! χ_PQ(iτ) = Σ_ll' Z_lP χ_ll'(iτ) Z_l'Q
167 ! ==============================================================================
168 CALL get_mat_chi_gamma_tau(bs_env, bs_env%mat_chi_Gamma_tau, &
169 bs_env%ri_rs%mat_phi_mu_l, bs_env%ri_rs%mat_Z_lP)
170
171 ! ==============================================================================
172 ! 5. Screened Coulomb interaction W (RI basis)
173 ! χ_PQ(iτ) -> χ_PQ(iω) -> ε_PQ(iω) -> W_PQ(iω) -> W_PQ(iτ)
174 ! ==============================================================================
175 CALL compute_w(bs_env, qs_env, bs_env%mat_chi_Gamma_tau, fm_w_time)
176
177 ! ==============================================================================
178 ! 6. Exact-exchange self-energy Σ^x
179 ! D_µν = Σ_n^occ C_µn C_νn (density matrix)
180 ! D_ll' = Σ_µν ϕ_µ(r_l) D_µν ϕ_ν(r_l')
181 ! V^tr_ll' = Σ_PQ Z_lP V^tr_PQ Z_l'Q (truncated Coulomb)
182 ! Σ^x_ll' = D_ll' ∘ V^tr_ll'
183 ! Σ^x_λσ(k=0) = -Σ_ll' ϕ_λ(r_l) Σ^x_ll' ϕ_σ(r_l')
184 ! ==============================================================================
185 CALL compute_sigma_x(bs_env, qs_env, bs_env%ri_rs%mat_phi_mu_l, &
186 bs_env%ri_rs%mat_Z_lP, fm_sigma_x_gamma)
187
188 ! ==============================================================================
189 ! 7. Correlation self-energy Σ^c and quasiparticle energies, iterated
190 ! until eigenvalue self-consistency if the &EVGW0 section is given and
191 ! done in a single pass for G0W0.
192 !
193 ! (a) W_ll'(iτ) = Σ_PQ Z_lP W^MIC_PQ(iτ) Z_l'Q
194 ! Σ^c_ll'(iτ) = -G^occ_ll'(i|τ|) ∘ W_ll'(iτ), τ < 0
195 ! Σ^c_ll'(iτ) = G^vir_ll'(i|τ|) ∘ W_ll'(iτ), τ > 0
196 ! Σ^c_λσ(iτ) = Σ_ll' ϕ_λ(r_l) Σ^c_ll'(iτ) ϕ_σ(r_l')
197 ! (b) Σ^c_λσ(iτ) -> Σ^c_nn(ϵ)
198 ! ϵ_n^GW,(i) = ϵ_n^DFT + Σ^c_nn[G^(i-1),W](ϵ_n^GW,(i)) + Σ^x_nn - v^xc_nn
199 ! ==============================================================================
200 CALL compute_sigma_c_and_qp_energies(bs_env, fm_w_time, fm_sigma_x_gamma)
201
202 CALL de_init_bs_env(qs_env, bs_env)
203
204 CALL timestop(handle)
205
206 END SUBROUTINE gw_calc_ri_rs_non_periodic
207
208! **************************************************************************************************
209!> \brief Correlation self-energy and quasiparticle energies, iterated to eigenvalue
210!> self-consistency in G (evGW0).
211!>
212!> For G0W0 this runs once with G^(0) built from the DFT eigenvalues. For evGW0 the
213!> Green's function is rebuilt from the quasiparticle energies of all states until the
214!> quasiparticle HOMO, LUMO and HOMO-LUMO gap change by less than EPS_ITER between two
215!> cycles, or MAX_ITER cycles are spent. W, Σ^x and everything computed before this
216!> routine stay frozen: they either do not depend on the eigenvalues at all (Σ^x is G
217!> at τ = 0, a pure density matrix) or are held fixed by construction in GW0. The
218!> imaginary-time grid is fixed as well, since W(iτ) lives on it.
219!>
220!> \param bs_env ...
221!> \param fm_W_time ...
222!> \param fm_Sigma_x_Gamma ...
223! **************************************************************************************************
224 SUBROUTINE compute_sigma_c_and_qp_energies(bs_env, fm_W_time, fm_Sigma_x_Gamma)
225
226 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
227 TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_w_time, fm_sigma_x_gamma
228
229 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_Sigma_c_and_QP_energies'
230
231 INTEGER :: handle, i_iter, n_iter
232 LOGICAL :: converged
233 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: eigenval_scf_gamma_dft
234 REAL(kind=dp), DIMENSION(2) :: e_fermi_dft
235 REAL(kind=dp), DIMENSION(3, 2) :: band_prev
236 TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :, :) :: fm_sigma_c_gamma_time
237
238 CALL timeset(routinen, handle)
239
240 converged = .false.
241
242 CALL init_evgw0(bs_env, n_iter, band_prev, eigenval_scf_gamma_dft, e_fermi_dft)
243
244 ! evGW0 self-consistency loop; for G0W0, loop is terminated after one iteration
245 DO i_iter = 1, n_iter
246
247 bs_env%ri_rs%evgw0_i_iter = i_iter
248
249 ! W_ll'(iτ) = Σ_PQ Z_lP W^MIC_PQ(iτ) Z_l'Q
250 ! Σ^c_ll'(iτ) = -G^occ_ll'(i|τ|) ∘ W_ll'(iτ), τ < 0
251 ! Σ^c_ll'(iτ) = G^vir_ll'(i|τ|) ∘ W_ll'(iτ), τ > 0
252 ! Σ^c_λσ(iτ) = Σ_ll' ϕ_λ(r_l) Σ^c_ll'(iτ) ϕ_σ(r_l')
253 CALL compute_sigma_c(bs_env, fm_w_time, bs_env%ri_rs%mat_phi_mu_l, &
254 bs_env%ri_rs%mat_Z_lP, fm_sigma_c_gamma_time)
255
256 ! Σ^c_λσ(iτ) -> Σ^c_nn(ϵ)
257 ! ϵ_n^GW,(i) = ϵ_n^DFT + Σ^c_nn[G^(i-1),W](ϵ_n^GW,(i)) + Σ^x_nn - v^xc_nn
258 CALL compute_qp_energies(bs_env, fm_sigma_x_gamma, fm_sigma_c_gamma_time)
259
260 IF (bs_env%gw_flavour == g0w0) EXIT
261
262 CALL print_evgw0_band_edges(bs_env, band_prev, i_iter, n_iter, converged)
263
264 IF (i_iter == n_iter .OR. converged) EXIT
265
266 ! eigenvalues ϵ_n to be updated in G:
267 ! G^occ_µλ(i|τ|) = Σ_n^occ C_µn e^(-|(ϵ_n-ϵ_F)τ|) C_λn
268 ! G^vir_µλ(i|τ|) = Σ_n^vir C_µn e^(-|(ϵ_n-ϵ_F)τ|) C_λn
269 CALL update_eigenvalues_g(bs_env)
270
271 END DO
272
273 CALL cp_fm_release(fm_w_time)
274
275 CALL reset_and_clean_bs_env(bs_env, eigenval_scf_gamma_dft, e_fermi_dft, fm_sigma_x_gamma)
276
277 CALL delete_unnecessary_files(bs_env)
278
279 CALL timestop(handle)
280
281 END SUBROUTINE compute_sigma_c_and_qp_energies
282
283! **************************************************************************************************
284!> \brief Sets up the evGW0 eigenvalue self-consistency loop: the cycle count, the eigenvalues
285!> the first Green's function is built from, and the DFT reference that the loop
286!> overwrites. A G0W0 run reduces to a single cycle and needs none of it.
287!> \param bs_env ...
288!> \param n_iter ...
289!> \param band_prev ...
290!> \param eigenval_scf_Gamma_dft ...
291!> \param e_fermi_dft ...
292! **************************************************************************************************
293 SUBROUTINE init_evgw0(bs_env, n_iter, band_prev, eigenval_scf_Gamma_dft, e_fermi_dft)
294
295 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
296 INTEGER, INTENT(OUT) :: n_iter
297 REAL(kind=dp), DIMENSION(3, 2), INTENT(OUT) :: band_prev
298 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :), &
299 INTENT(OUT) :: eigenval_scf_gamma_dft
300 REAL(kind=dp), DIMENSION(2), INTENT(OUT) :: e_fermi_dft
301
302 CHARACTER(LEN=*), PARAMETER :: routinen = 'init_evGW0'
303
304 INTEGER :: handle
305
306 CALL timeset(routinen, handle)
307
308 n_iter = 1
309 band_prev(:, :) = 0.0_dp
310 e_fermi_dft(:) = 0.0_dp
311
312 IF (bs_env%gw_flavour == evgw0) THEN
313 n_iter = bs_env%ri_rs%evgw0_iter
314
315 ! eigenvalues currently in G; the first cycle starts is a plain G0W0 step
316 bs_env%eigenval_evGW0(:, :, :) = bs_env%eigenval_scf(:, :, :)
317 ! the loop overwrites these; post-GW printing expects the DFT values back
318 ALLOCATE (eigenval_scf_gamma_dft, source=bs_env%eigenval_scf_Gamma)
319 e_fermi_dft(:) = bs_env%e_fermi(:)
320 END IF
321
322 CALL timestop(handle)
323
324 END SUBROUTINE init_evgw0
325
326! **************************************************************************************************
327!> \brief Feeds the quasiparticle energies of the current evGW0 cycle back into the Green's
328!> function used by the next one, and re-centres the Fermi level between the new
329!> quasiparticle HOMO and LUMO.
330!> \param bs_env ...
331! **************************************************************************************************
332 SUBROUTINE update_eigenvalues_g(bs_env)
333
334 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
335
336 CHARACTER(LEN=*), PARAMETER :: routinen = 'update_eigenvalues_G'
337
338 INTEGER :: handle, i_mo, ispin
339
340 CALL timeset(routinen, handle)
341
342 ! record this cycle's evGW0 result; it is also what the next Green's function is built from
343 bs_env%eigenval_evGW0(:, :, :) = bs_env%eigenval_GW(:, :, :)
344
345 DO ispin = 1, bs_env%n_spin
346 ! Update all physical states; exclude linear-dependency placeholders.
347 DO i_mo = 1, bs_env%n_mo_retained
348 bs_env%eigenval_scf_Gamma(i_mo, ispin) = bs_env%eigenval_GW(i_mo, 1, ispin)
349 END DO
350 bs_env%e_fermi(ispin) = &
351 0.5_dp*(bs_env%eigenval_GW(bs_env%n_occ(ispin), 1, ispin) + &
352 bs_env%eigenval_GW(bs_env%n_occ(ispin) + 1, 1, ispin))
353 END DO
354
355 CALL timestop(handle)
356
357 END SUBROUTINE update_eigenvalues_g
358
359! **************************************************************************************************
360!> \brief Restores the DFT reference that the evGW0 loop overwrote and cleanup
361!> \param bs_env ...
362!> \param eigenval_scf_Gamma_dft ...
363!> \param e_fermi_dft ...
364!> \param fm_Sigma_x_Gamma ..
365! **************************************************************************************************
366 SUBROUTINE reset_and_clean_bs_env(bs_env, eigenval_scf_Gamma_dft, e_fermi_dft, fm_Sigma_x_Gamma)
367
368 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
369 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :), &
370 INTENT(INOUT) :: eigenval_scf_gamma_dft
371 REAL(kind=dp), DIMENSION(2), INTENT(IN) :: e_fermi_dft
372 TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_sigma_x_gamma
373
374 CHARACTER(LEN=*), PARAMETER :: routinen = 'reset_and_clean_bs_env'
375
376 INTEGER :: handle
377
378 CALL timeset(routinen, handle)
379
380 IF (bs_env%gw_flavour == evgw0) THEN
381 ! Update array with final evGW0 result
382 bs_env%eigenval_evGW0(:, :, :) = bs_env%eigenval_GW(:, :, :)
383 ! put the DFT reference back for the post-GW DOS/band-edge printing
384 bs_env%eigenval_scf_Gamma(:, :) = eigenval_scf_gamma_dft(:, :)
385 bs_env%e_fermi(:) = e_fermi_dft(:)
386 DEALLOCATE (eigenval_scf_gamma_dft)
387 CALL cp_fm_release(fm_sigma_x_gamma)
388 END IF
389
390 CALL timestop(handle)
391
392 END SUBROUTINE reset_and_clean_bs_env
393
394! **************************************************************************************************
395!> \brief Print the quasiparticle HOMO, LUMO and HOMO-LUMO gap of the current evGW0 cycle
396!> \param bs_env ...
397!> \param band_prev ...
398!> \param i_iter ...
399!> \param n_iter ...
400!> \param converged ...
401! **************************************************************************************************
402 SUBROUTINE print_evgw0_band_edges(bs_env, band_prev, i_iter, n_iter, converged)
403
404 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
405 REAL(kind=dp), DIMENSION(3, 2), INTENT(INOUT) :: band_prev
406 INTEGER, INTENT(IN) :: i_iter, n_iter
407 LOGICAL, INTENT(OUT) :: converged
408
409 CHARACTER(LEN=*), PARAMETER :: routinen = 'print_evGW0_band_edges'
410
411 INTEGER :: handle, homo, ispin, u
412 REAL(kind=dp) :: max_delta
413 REAL(kind=dp), DIMENSION(3) :: band
414
415 CALL timeset(routinen, handle)
416
417 u = bs_env%unit_nr
418
419 converged = (i_iter > 1)
420 max_delta = 0.0_dp
421
422 IF (u > 0) THEN
423 WRITE (u, '(A)') ' '
424 WRITE (u, '(T2,A)') repeat('-', 79)
425 WRITE (u, '(T2,A,I4,A,I4)') 'evGW0 cycle', i_iter, ' /', n_iter
426 WRITE (u, '(T2,A)') repeat('-', 79)
427 END IF
428
429 DO ispin = 1, bs_env%n_spin
430
431 homo = bs_env%n_occ(ispin)
432 band(1) = bs_env%eigenval_GW(homo, 1, ispin)
433 band(2) = bs_env%eigenval_GW(homo + 1, 1, ispin)
434 band(3) = band(2) - band(1)
435
436 IF (i_iter > 1) THEN
437 max_delta = max(max_delta, maxval(abs(band(:) - band_prev(:, ispin))))
438 END IF
439
440 IF (u > 0) THEN
441 IF (bs_env%n_spin == 2) WRITE (u, '(T2,A,I0)') 'Spin ', ispin
442 WRITE (u, '(T2,A,T61,F20.3)') 'evGW0 HOMO (eV)', band(1)*evolt
443 WRITE (u, '(T2,A,T61,F20.3)') 'evGW0 LUMO (eV)', band(2)*evolt
444 WRITE (u, '(T2,A,T61,F20.3)') 'evGW0 HOMO-LUMO gap (eV)', band(3)*evolt
445 END IF
446
447 band_prev(:, ispin) = band(:)
448
449 END DO
450
451 IF (i_iter > 1) THEN
452 converged = (max_delta < bs_env%ri_rs%evgw0_eps_iter)
453 IF (u > 0) WRITE (u, '(T2,A,T61,F20.6)') 'Max. change to previous cycle (eV)', &
454 max_delta*evolt
455 END IF
456
457 IF (u > 0) WRITE (u, '(T2,A)') repeat('-', 79)
458
459 IF (converged) THEN
460 IF (u > 0) THEN
461 WRITE (u, '(A)') ' '
462 WRITE (u, '(T2,A,I4,A)') &
463 'evGW0 eigenvalue self-consistency reached in', i_iter, ' cycles.'
464 WRITE (u, '(A)') ' '
465 END IF
466 ELSE IF (i_iter == n_iter) THEN
467 CALL cp_warn(__location__, &
468 "The evGW0 eigenvalue self-consistency cycle did not converge "// &
469 "within MAX_ITER cycles. The reported quasiparticle energies are "// &
470 "those of the last cycle.")
471 END IF
472
473 CALL timestop(handle)
474
475 END SUBROUTINE print_evgw0_band_edges
476
477! **************************************************************************************************
478!> \brief Evaluates the AO basis on the RI-RS grid and stores it as the sparse DBCSR matrix
479!> ϕ_μl = ϕ_μ(r_l) (rows = grid points in atom-aligned blocks of at most
480!> max_elements_per_block points, columns = one block per atom's full AO set).
481!> Grid points outside the reach of an atom's most
482!> diffuse Gaussian (or the CUTOFF_RADIUS_RL_AO) are skipped, and only blocks
483!> with at least one element > eps_filter are stored. This locality is the source of
484!> ALL grid-dimension sparsity used downstream. Also caches the atom centers and the
485!> per-chunk centroids needed by the optional CUTOFF_RADIUS_G_W / CUTOFF_RADIUS_RL_W
486!> operator truncations.
487!> \param bs_env ...
488!> \param ri_rs_grid_points ...
489!> \param mat_phi_mu_l ...
490! **************************************************************************************************
491 SUBROUTINE atomic_basis_at_grid_point(bs_env, ri_rs_grid_points, mat_phi_mu_l)
492
493 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
494 REAL(kind=dp), ALLOCATABLE, INTENT(INOUT) :: ri_rs_grid_points(:, :)
495 TYPE(dbcsr_type), INTENT(OUT) :: mat_phi_mu_l
496
497 CHARACTER(LEN=*), PARAMETER :: routinen = 'atomic_basis_at_grid_point'
498
499 INTEGER :: bs_eff, c_size, handle, i, i_blk, ia, iatom, ikind, natom, npcol, nprow, &
500 num_grid_chunks, r_end, r_start, remaining, run, safe_max
501 INTEGER, ALLOCATABLE, DIMENSION(:) :: blk_row_start
502 INTEGER, DIMENSION(:), POINTER :: col_dist, r_blk_sizes, row_dist, sizes_ao
503 REAL(kind=dp) :: r2_threshold
504 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: atom_col_buffer
505 TYPE(cell_type), POINTER :: cell
506 TYPE(dbcsr_distribution_type) :: dbcsr_dist_ks, dist
507 TYPE(gto_basis_set_type), POINTER :: ao_basis
508 TYPE(mp_para_env_type), POINTER :: para_env
509 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
510
511 CALL timeset(routinen, handle)
512
513 natom = bs_env%n_atom
514 cell => bs_env%ri_rs%cell
515 para_env => bs_env%para_env
516 particle_set => bs_env%ri_rs%particle_set
517 sizes_ao => bs_env%sizes_AO
518 cpassert(ASSOCIATED(cell))
519 cpassert(ASSOCIATED(para_env))
520 cpassert(ASSOCIATED(particle_set))
521 cpassert(SIZE(sizes_ao) == natom)
522 cpassert(SIZE(ri_rs_grid_points, 2) == bs_env%ri_rs%n_grid_points)
523
524 ! =========================================================================
525 ! 1. SETUP DBCSR MATRIX TOPOLOGY
526 ! =========================================================================
527
528 ! B. Define Row Block Sizes: atom-aligned blocks (a block never spans two atoms' grid
529 ! runs), each atom's run subdivided into blocks of at most bs_eff points.
530
531 ! Fetch CP2K's default process grid configuration
532 CALL dbcsr_get_info(bs_env%mat_ao_ao%matrix, distribution=dbcsr_dist_ks)
533 CALL dbcsr_distribution_get(dbcsr_dist_ks, nprows=nprow, npcols=npcol)
534
535 ! Overflow-safe upper bound on the block size (see bs_env%dbcsr_msg_elem_limit).
536 safe_max = int(0.5_dp*real(bs_env%dbcsr_msg_elem_limit, dp)* &
537 REAL(max(min(nprow, npcol), 1), dp)/ &
538 REAL(bs_env%ri_rs%n_grid_points, dp))
539 safe_max = max(1, safe_max)
540 ! Block size = CP2K's global max_elements_per_block (GLOBAL/DBCSR input; default 32),
541 ! overflow-capped.
542 bs_eff = max(1, min(max_elements_per_block, safe_max))
543
544 ! Count the atom-aligned blocks, then fill r_blk_sizes and each block's starting grid row.
545 num_grid_chunks = 0
546 DO ia = 1, natom
547 run = bs_env%ri_rs%grid_atom_boundaries(ia + 1) - bs_env%ri_rs%grid_atom_boundaries(ia)
548 IF (run > 0) num_grid_chunks = num_grid_chunks + (run + bs_eff - 1)/bs_eff
549 END DO
550 ALLOCATE (r_blk_sizes(num_grid_chunks), blk_row_start(num_grid_chunks))
551 i_blk = 0
552 r_start = 1
553 DO ia = 1, natom
554 remaining = bs_env%ri_rs%grid_atom_boundaries(ia + 1) - bs_env%ri_rs%grid_atom_boundaries(ia)
555 DO WHILE (remaining > 0)
556 i_blk = i_blk + 1
557 r_blk_sizes(i_blk) = min(bs_eff, remaining)
558 blk_row_start(i_blk) = r_start
559 r_start = r_start + r_blk_sizes(i_blk)
560 remaining = remaining - r_blk_sizes(i_blk)
561 END DO
562 END DO
563
564 IF (bs_env%unit_nr > 0) THEN
565 ! T71 compensates for the two two-byte Greek characters in the label.
566 WRITE (bs_env%unit_nr, '(T2,A,T71,I12)') &
567 'RI-RS grid row-blocks of ϕ_μ(r_l)', num_grid_chunks
568 WRITE (bs_env%unit_nr, '(T2,A,T69,I12)') 'RI-RS grid points per block (max)', bs_eff
569 END IF
570
571 ! Cache atomic positions: AO and RI blocks are one-block-per-atom, so these are the
572 ! block centers used by the optional CUTOFF_RADIUS_G_W atom-pair truncation.
573 IF (ALLOCATED(bs_env%ri_rs%atom_centers)) DEALLOCATE (bs_env%ri_rs%atom_centers)
574 ALLOCATE (bs_env%ri_rs%atom_centers(3, natom))
575 DO iatom = 1, natom
576 bs_env%ri_rs%atom_centers(1:3, iatom) = particle_set(iatom)%r(1:3)
577 END DO
578
579 ! Cache per-chunk centroids for the optional CUTOFF_RADIUS_RL_W / CUTOFF_RADIUS_W0 block
580 ! truncations. Left unallocated otherwise, so PRESENT(centroids) stays .FALSE. at the
581 ! contract_grid_panels* calls.
582 IF (bs_env%ri_rs%cutoff_radius_v_w > 0.0_dp .OR. &
583 bs_env%ri_rs%cutoff_radius_w0 > 0.0_dp) THEN
584 IF (ALLOCATED(bs_env%ri_rs%chunk_centroids)) DEALLOCATE (bs_env%ri_rs%chunk_centroids)
585 ALLOCATE (bs_env%ri_rs%chunk_centroids(3, num_grid_chunks))
586 DO i_blk = 1, num_grid_chunks
587 r_start = blk_row_start(i_blk)
588 r_end = r_start + r_blk_sizes(i_blk) - 1
589 bs_env%ri_rs%chunk_centroids(1, i_blk) = &
590 sum(ri_rs_grid_points(1, r_start:r_end))/real(r_blk_sizes(i_blk), dp)
591 bs_env%ri_rs%chunk_centroids(2, i_blk) = &
592 sum(ri_rs_grid_points(2, r_start:r_end))/real(r_blk_sizes(i_blk), dp)
593 bs_env%ri_rs%chunk_centroids(3, i_blk) = &
594 sum(ri_rs_grid_points(3, r_start:r_end))/real(r_blk_sizes(i_blk), dp)
595 END DO
596 END IF
597
598 ! C. Build Custom Mappings using Round-Robin across the 2D process grid
599
600 ALLOCATE (row_dist(num_grid_chunks))
601 DO i = 1, num_grid_chunks
602 row_dist(i) = mod(i - 1, nprow)
603 END DO
604
605 ALLOCATE (col_dist(natom))
606 DO i = 1, natom
607 col_dist(i) = mod(i - 1, npcol)
608 END DO
609
610 ! E. Create the DBCSR Distribution and Initialize the Matrix
611 CALL dbcsr_distribution_new(dist, template=dbcsr_dist_ks, &
612 row_dist=row_dist, col_dist=col_dist)
613
614 CALL dbcsr_create(mat_phi_mu_l, name="phi_val_sparse", dist=dist, &
615 matrix_type=dbcsr_type_no_symmetry, &
616 row_blk_size=r_blk_sizes, col_blk_size=sizes_ao)
617
618 ! =========================================================================
619 ! 2. STREAM DATA DIRECTLY INTO SPARSE MATRIX
620 ! =========================================================================
621 ! Iterate over the atoms assigned to this specific MPI rank
622 DO iatom = para_env%mepos + 1, natom, para_env%num_pe
623
624 c_size = sizes_ao(iatom)
625
626 ! Allocate a temporary dense buffer just for this specific atom
627 ALLOCATE (atom_col_buffer(bs_env%ri_rs%n_grid_points, c_size))
628 atom_col_buffer = 0.0_dp
629
630 ! Evaluate the basis functions on the grid. Skip grid points outside
631 ! the spatial extent of the most diffuse AO Gaussian on iatom; beyond
632 ! that radius the contribution is guaranteed below eps_filter. A positive
633 ! CUTOFF_RADIUS_RL_AO overrides this with a user-defined hard cutoff.
634 IF (bs_env%ri_rs%cutoff_radius_ri_ao > 0.0_dp) THEN
635 r2_threshold = bs_env%ri_rs%cutoff_radius_ri_ao**2
636 ELSE
637 r2_threshold = bs_env%ri_rs%radius_ao_per_atom(iatom)**2
638 END IF
639 ikind = particle_set(iatom)%atomic_kind%kind_number
640 ao_basis => bs_env%basis_set_AO(ikind)%gto_basis_set
641 CALL evaluate_ao_basis_on_points(atom_col_buffer, ri_rs_grid_points, ao_basis, &
642 particle_set(iatom)%r, cell, cutoff_squared=r2_threshold)
643
644 ! Slice the dense column into the atom-aligned grid row-blocks and insert into DBCSR
645 DO i_blk = 1, num_grid_chunks
646 r_start = blk_row_start(i_blk)
647 r_end = r_start + r_blk_sizes(i_blk) - 1
648
649 ! Apply dynamic sparsity filtering: Only store blocks with physical significance
650 IF (maxval(abs(atom_col_buffer(r_start:r_end, 1:c_size))) > bs_env%eps_filter) THEN
651 CALL dbcsr_put_block(mat_phi_mu_l, row=i_blk, col=iatom, &
652 block=atom_col_buffer(r_start:r_end, 1:c_size))
653 END IF
654 END DO
655
656 DEALLOCATE (atom_col_buffer)
657
658 END DO
659
660 CALL dbcsr_finalize(mat_phi_mu_l)
661
662 IF (bs_env%unit_nr > 0) WRITE (bs_env%unit_nr, '(A)') ' '
663 CALL print_matrix_occupation(mat_phi_mu_l, 'ϕ_μ(r_l)', bs_env)
664
665 ! -------------------------------------------------------------------------
666 ! CLEANUP
667 ! -------------------------------------------------------------------------
668 DEALLOCATE (r_blk_sizes, row_dist, col_dist, blk_row_start)
670
671 CALL timestop(handle)
672
673 END SUBROUTINE atomic_basis_at_grid_point
674
675! **************************************************************************************************
676!> \brief Computes χ_PQ(iτ) from the occupied and virtual Green's functions.
677!> \param bs_env ...
678!> \param mat_chi_Gamma_tau Response matrices χ_PQ(iτ) to be computed
679!> \param mat_phi_mu_l AO values ϕ_μ(r_l) on the RI-RS grid
680!> \param mat_Z_lP RI-RS fitting matrix Z_lP
681! **************************************************************************************************
682 SUBROUTINE get_mat_chi_gamma_tau(bs_env, mat_chi_Gamma_tau, mat_phi_mu_l, mat_Z_lP)
683
684 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
685 TYPE(dbcsr_p_type), DIMENSION(:), POINTER :: mat_chi_gamma_tau
686 TYPE(dbcsr_type), INTENT(INOUT) :: mat_phi_mu_l, mat_z_lp
687
688 CHARACTER(LEN=*), PARAMETER :: routinen = 'get_mat_chi_Gamma_tau'
689
690 INTEGER :: handle, i_t, ispin, n_panels
691 INTEGER, ALLOCATABLE, DIMENSION(:) :: pan_first, pan_last
692 REAL(kind=dp) :: grid_occ, t1, tau
693 TYPE(dbcsr_type) :: matrix_g_occ_ao, matrix_g_vir_ao
694
695 CALL timeset(routinen, handle)
696
697 ! Panel boundaries for the grid-streaming contraction.
698 ! The panels are identical for χ, Σ^x and Σ^c, so the count is reported once here for all three stages.
699
700 CALL resolve_grid_panels(bs_env, mat_phi_mu_l, pan_first, pan_last)
701
702 n_panels = SIZE(pan_first)
703 IF (bs_env%unit_nr > 0) THEN
704 WRITE (bs_env%unit_nr, '(T2,A,T74,I9)') &
705 'Number of batches for χ, Σ matrices', n_panels
706 WRITE (bs_env%unit_nr, '(A)') ' '
707 CALL m_flush(bs_env%unit_nr)
708 END IF
709
710 ! =========================================================================
711 ! IMAGINARY TIME LOOP
712 ! χ_PQ(iτ) = Σ_s g_s · Z^T ( (ϕ G^occ_s ϕ^T) ∘ (ϕ G^vir_s ϕ^T) ) Z
713 ! (g_s = spin degeneracy)
714 ! =========================================================================
715 DO i_t = 1, bs_env%num_time_freq_points
716 t1 = m_walltime()
717 tau = bs_env%time_frequency_grid%imaginary_time(i_t)
718
719 DO ispin = 1, bs_env%n_spin
720
721 ! AO-space Green's functions G^occ_µν, G^vir_µν (dense AO x AO, small)
722 CALL build_g_ao(bs_env, tau, ispin, .true., .false., mat_phi_mu_l, matrix_g_occ_ao)
723 CALL build_g_ao(bs_env, tau, ispin, .false., .true., mat_phi_mu_l, matrix_g_vir_ao)
724
725 ! χ_PQ += g_s · Z^T ( (ϕ G^occ ϕ^T) ∘ (ϕ G^vir ϕ^T) ) Z
726 CALL contract_grid_panels(l_a=mat_phi_mu_l, m_a=matrix_g_occ_ao, &
727 l_b=mat_phi_mu_l, m_b=matrix_g_vir_ao, &
728 l_out=mat_z_lp, mat_out=mat_chi_gamma_tau(i_t)%matrix, &
729 scale=bs_env%spin_degeneracy, eps=bs_env%eps_filter, &
730 para_env=bs_env%para_env, &
731 pan_first=pan_first, pan_last=pan_last, &
732 lb_eq_la=.true., lout_eq_la=.false., &
733 zero_out=(ispin == 1), &
734 keep_sparsity=bs_env%ri_rs%keep_sparsity_rirs, &
735 centroids=bs_env%ri_rs%chunk_centroids, &
736 cutoff=bs_env%ri_rs%cutoff_radius_v_w, &
737 grid_occupation=grid_occ)
738
739 CALL dbcsr_release(matrix_g_occ_ao)
740 CALL dbcsr_release(matrix_g_vir_ao)
741
742 END DO ! ispin
743
744 ! Sparsity reports
745 IF (i_t == 1) THEN
746 CALL print_matrix_occupation(mat_z_lp, 'Z_lP', bs_env)
747 IF (bs_env%unit_nr > 0) THEN
748 WRITE (bs_env%unit_nr, '(T2,A,T73,F7.2,A)') &
749 'Percentage of non-zero matrix elements in G_ll'', χ_ll'', W_ll''', &
750 grid_occ*100.0_dp, ' %'
751 CALL m_flush(bs_env%unit_nr)
752 END IF
753 CALL print_matrix_occupation(mat_chi_gamma_tau(i_t)%matrix, 'χ_PQ', bs_env, &
754 suffix=' for time point 1')
755 IF (bs_env%unit_nr > 0) WRITE (bs_env%unit_nr, '(A)') ' '
756 END IF
757
758 IF (bs_env%unit_nr > 0) THEN
759 WRITE (bs_env%unit_nr, '(T2,A,I13,A,I3,A,F7.1,A)') &
760 'Computed χ(iτ,k=0) for time point', i_t, ' /', bs_env%num_time_freq_points, &
761 ', Execution time', m_walltime() - t1, ' s'
762 END IF
763
764 END DO ! i_t
765
766 IF (bs_env%unit_nr > 0) WRITE (bs_env%unit_nr, '(A)') ' '
767
768 CALL timestop(handle)
769
770 END SUBROUTINE get_mat_chi_gamma_tau
771
772! **************************************************************************************************
773!> \brief Marks the grid blocks whose centroid lies within cutoff of the bounding box of the
774!> panel [blk0, blk1]'s chunk centroids.
775!> \param centroids ...
776!> \param blk0 ...
777!> \param blk1 ...
778!> \param cutoff ...
779!> \param used ...
780! **************************************************************************************************
781 SUBROUTINE mask_grid_blocks_near_panel(centroids, blk0, blk1, cutoff, used)
782
783 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: centroids
784 INTEGER, INTENT(IN) :: blk0, blk1
785 REAL(kind=dp), INTENT(IN) :: cutoff
786 LOGICAL, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: used
787
788 CHARACTER(LEN=*), PARAMETER :: routinen = 'mask_grid_blocks_near_panel'
789
790 INTEGER :: c, handle, k
791 REAL(kind=dp) :: cutoff2, d2, dx
792 REAL(kind=dp), DIMENSION(3) :: hi, lo
793
794 CALL timeset(routinen, handle)
795
796 cutoff2 = cutoff**2
797 lo(:) = minval(centroids(:, blk0:blk1), dim=2)
798 hi(:) = maxval(centroids(:, blk0:blk1), dim=2)
799
800 ALLOCATE (used(SIZE(centroids, 2)))
801 DO c = 1, SIZE(centroids, 2)
802 d2 = 0.0_dp
803 DO k = 1, 3
804 dx = max(0.0_dp, lo(k) - centroids(k, c), centroids(k, c) - hi(k))
805 d2 = d2 + dx*dx
806 END DO
807 used(c) = (d2 <= cutoff2)
808 END DO
809
810 CALL timestop(handle)
811
812 END SUBROUTINE mask_grid_blocks_near_panel
813
814! **************************************************************************************************
815!> \brief Exact allocated-element count of the geo template of panel [blk0, blk1]: the very same
816!> per-block-pair centroid test as build_geo_template_panel, so this is the true DBCSR
817!> data size of A_pan/B_pan/C_pan (DBCSR stores whole blocks).
818!> \param r_blk_sizes ...
819!> \param centroids ...
820!> \param used ...
821!> \param blk0 ...
822!> \param blk1 ...
823!> \param cutoff ...
824!> \param nze_tmpl ...
825! **************************************************************************************************
826 SUBROUTINE panel_template_elems(r_blk_sizes, centroids, used, blk0, blk1, cutoff, nze_tmpl)
827
828 INTEGER, DIMENSION(:), INTENT(IN) :: r_blk_sizes
829 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: centroids
830 LOGICAL, DIMENSION(:), INTENT(IN) :: used
831 INTEGER, INTENT(IN) :: blk0, blk1
832 REAL(kind=dp), INTENT(IN) :: cutoff
833 INTEGER(KIND=int_8), INTENT(OUT) :: nze_tmpl
834
835 CHARACTER(LEN=*), PARAMETER :: routinen = 'panel_template_elems'
836
837 INTEGER :: c, handle, ib, n_used
838 INTEGER, ALLOCATABLE, DIMENSION(:) :: used_idx
839 REAL(kind=dp) :: cutoff2
840
841 CALL timeset(routinen, handle)
842
843 ! Compress the near mask once so the pair loop only visits candidate columns.
844 n_used = count(used)
845 ALLOCATE (used_idx(n_used))
846 n_used = 0
847 DO c = 1, SIZE(used)
848 IF (used(c)) THEN
849 n_used = n_used + 1
850 used_idx(n_used) = c
851 END IF
852 END DO
853
854 cutoff2 = cutoff**2
855 nze_tmpl = 0_int_8
856 !$OMP PARALLEL DO DEFAULT(NONE) SHARED(blk0, blk1, n_used, used_idx, centroids, cutoff2, &
857 !$OMP r_blk_sizes) PRIVATE(ib, c) REDUCTION(+:nze_tmpl)
858 DO ib = blk0, blk1
859 DO c = 1, n_used
860 IF (sum((centroids(:, ib) - centroids(:, used_idx(c)))**2) <= cutoff2) THEN
861 nze_tmpl = nze_tmpl + int(r_blk_sizes(ib), int_8)*int(r_blk_sizes(used_idx(c)), int_8)
862 END IF
863 END DO
864 END DO
865 !$OMP END PARALLEL DO
866
867 CALL timestop(handle)
868
869 END SUBROUTINE panel_template_elems
870
871! **************************************************************************************************
872!> \brief Per-rank peak memory (GB) of one panel step of the neighborhood-restricted
873!> contractions: three grid x grid panels of the template size (A_pan, B_pan, C_pan)
874!> plus the grid x RI intermediates (tmp2 and the accumulation operand) and the
875!> grid x AO intermediate (tmpA), whose column support is the panel's geometric
876!> neighborhood fraction f_near = width/n_grid. Shared by the panel planner and
877!> \param nze_tmpl ...
878!> \param pan_rows ...
879!> \param width ...
880!> \param n_grid_total ...
881!> \param n_RI ...
882!> \param n_ao ...
883!> \param n_procs ...
884!> \param mem_GB ...
885! **************************************************************************************************
886 SUBROUTINE panel_mem_estimate_gb(nze_tmpl, pan_rows, width, n_grid_total, n_RI, n_ao, &
887 n_procs, mem_GB)
888
889 INTEGER(KIND=int_8), INTENT(IN) :: nze_tmpl
890 INTEGER, INTENT(IN) :: pan_rows, width, n_grid_total, n_ri, &
891 n_ao, n_procs
892 REAL(kind=dp), INTENT(OUT) :: mem_gb
893
894 REAL(kind=dp) :: f_near
895
896 f_near = real(width, dp)/real(max(n_grid_total, 1), dp)
897 mem_gb = (3.0_dp*real(nze_tmpl, dp) + &
898 REAL(pan_rows, dp)*f_near*(2.0_dp*REAL(n_RI, dp) + REAL(n_ao, dp)))* &
899 8.0_dp/REAL(MAX(n_procs, 1), dp)*1.0e-9_dp
900
901 END SUBROUTINE panel_mem_estimate_gb
902
903! **************************************************************************************************
904!> \brief Plans the panel boundaries for the streaming contractions. Panels grow by whole grid
905!> row-blocks towards ~panel_size rows. When the neighborhood restriction is active
906!> (centroids+cutoff), each candidate panel is additionally checked against
907!> (a) the 32-bit message bound with the panel's TRUE occupancy
908!> (b) the per-rank memory budget: panel_mem_estimate_GB <= mem_budget_GB.
909!> \param bs_env ...
910!> \param r_blk_sizes ...
911!> \param panel_size ...
912!> \param min_dim ...
913!> \param pan_first ...
914!> \param pan_last ...
915!> \param centroids ...
916!> \param cutoff ...
917!> \param n_RI ...
918!> \param n_ao ...
919!> \param n_procs ...
920!> \param mem_budget_GB ...
921!> \param honor_exact ...
922!> \param unsafe ...
923! **************************************************************************************************
924 SUBROUTINE plan_grid_panels(bs_env, r_blk_sizes, panel_size, min_dim, pan_first, pan_last, &
925 centroids, cutoff, n_RI, n_ao, n_procs, mem_budget_GB, &
926 honor_exact, unsafe)
927
928 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
929 INTEGER, DIMENSION(:), INTENT(IN) :: r_blk_sizes
930 INTEGER, INTENT(IN) :: panel_size, min_dim
931 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: pan_first, pan_last
932 REAL(kind=dp), DIMENSION(:, :), INTENT(IN), &
933 OPTIONAL :: centroids
934 REAL(kind=dp), INTENT(IN), OPTIONAL :: cutoff
935 INTEGER, INTENT(IN), OPTIONAL :: n_ri, n_ao, n_procs
936 REAL(kind=dp), INTENT(IN), OPTIONAL :: mem_budget_gb
937 LOGICAL, INTENT(IN), OPTIONAL :: honor_exact
938 LOGICAL, INTENT(OUT), OPTIONAL :: unsafe
939
940 CHARACTER(LEN=*), PARAMETER :: routinen = 'plan_grid_panels'
941
942 INTEGER :: blk0, blk1, handle, ib, n_grid_blocks, &
943 n_grid_total, n_panels, rows_acc, &
944 TARGET, width
945 INTEGER(KIND=int_8) :: msg, nze_tmpl, side
946 INTEGER, ALLOCATABLE, DIMENSION(:) :: tmp_first, tmp_last
947 LOGICAL :: fits, my_honor_exact, my_unsafe, &
948 use_cutoff
949 LOGICAL, ALLOCATABLE, DIMENSION(:) :: used
950 REAL(kind=dp) :: f_near, mem_gb
951
952 CALL timeset(routinen, handle)
953
954 use_cutoff = PRESENT(centroids) .AND. PRESENT(cutoff)
955 IF (use_cutoff) use_cutoff = cutoff > 0.0_dp
956 IF (use_cutoff) THEN
957 cpassert(PRESENT(n_ri) .AND. PRESENT(n_ao) .AND. PRESENT(n_procs))
958 END IF
959
960 ! honor_exact: use N_PANELS as requested -- do NOT split a panel further even if it trips
961 ! the message-overflow / memory-budget check; instead flag `unsafe` so the caller can warn.
962 my_honor_exact = .false.
963 IF (PRESENT(honor_exact)) my_honor_exact = honor_exact
964 my_unsafe = .false.
965
966 n_grid_blocks = SIZE(r_blk_sizes)
967 n_grid_total = sum(r_blk_sizes)
968 ALLOCATE (tmp_first(n_grid_blocks), tmp_last(n_grid_blocks))
969
970 n_panels = 0
971 blk0 = 1
972 DO WHILE (blk0 <= n_grid_blocks)
973 TARGET = panel_size
974 DO
975 rows_acc = 0
976 blk1 = blk0
977 DO ib = blk0, n_grid_blocks
978 rows_acc = rows_acc + r_blk_sizes(ib)
979 blk1 = ib
980 IF (rows_acc >= TARGET) EXIT
981 END DO
982 IF (.NOT. use_cutoff .OR. blk1 == blk0) EXIT
983 CALL mask_grid_blocks_near_panel(centroids, blk0, blk1, cutoff, used)
984 width = sum(r_blk_sizes, mask=used)
985 CALL panel_template_elems(r_blk_sizes, centroids, used, blk0, blk1, cutoff, nze_tmpl)
986 f_near = real(width, dp)/real(max(n_grid_total, 1), dp)
987 side = int(real(rows_acc, dp)*f_near*real(max(n_ri, n_ao), dp), int_8)
988 msg = max(nze_tmpl, side)/int(max(min_dim, 1), int_8)
989 fits = (msg <= bs_env%dbcsr_msg_elem_limit/4)
990 IF (fits .AND. PRESENT(mem_budget_gb)) THEN
991 IF (mem_budget_gb > 0.0_dp) THEN
992 CALL panel_mem_estimate_gb(nze_tmpl, rows_acc, width, n_grid_total, &
993 n_ri, n_ao, n_procs, mem_gb)
994 fits = (mem_gb <= mem_budget_gb)
995 END IF
996 END IF
997 ! Panel size is bounded only by the message-overflow and memory checks above; there is
998 ! no neighborhood-width (f_near) cap. mp_waitall is dominated by the NUMBER of panel
999 ! multiplies, so fewer/larger panels are cheaper here -- panel count is driven DOWN by
1000 ! the N_PANELS keyword (panel_size), not split up by a width heuristic.
1001 IF (my_honor_exact) THEN
1002 ! Keep exactly the requested grouping; just record if it exceeds a safety limit.
1003 IF (.NOT. fits) my_unsafe = .true.
1004 EXIT
1005 END IF
1006 IF (fits) EXIT
1007 TARGET = max(1, min(TARGET, rows_acc)/2)
1008 END DO
1009 n_panels = n_panels + 1
1010 tmp_first(n_panels) = blk0
1011 tmp_last(n_panels) = blk1
1012 blk0 = blk1 + 1
1013 END DO
1014
1015 ALLOCATE (pan_first(n_panels), pan_last(n_panels))
1016 pan_first(:) = tmp_first(1:n_panels)
1017 pan_last(:) = tmp_last(1:n_panels)
1018 DEALLOCATE (tmp_first, tmp_last)
1019
1020 IF (PRESENT(unsafe)) unsafe = my_unsafe
1021
1022 CALL timestop(handle)
1023
1024 END SUBROUTINE plan_grid_panels
1025
1026! **************************************************************************************************
1027!> \brief Resolves the panel boundaries for the streaming contractions from the bs_env settings:
1028!> \param bs_env ...
1029!> \param mat_phi_mu_l ...
1030!> \param pan_first ...
1031!> \param pan_last ...
1032! **************************************************************************************************
1033 SUBROUTINE resolve_grid_panels(bs_env, mat_phi_mu_l, pan_first, pan_last)
1034
1035 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1036 TYPE(dbcsr_type), INTENT(INOUT) :: mat_phi_mu_l
1037 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: pan_first, pan_last
1038
1039 CHARACTER(LEN=*), PARAMETER :: routinen = 'resolve_grid_panels'
1040
1041 CHARACTER(LEN=max_line_length) :: msg
1042 INTEGER :: handle, min_dim, n_grid_total, &
1043 n_panels_req, npcols, nprows, &
1044 panel_size, safe_max
1045 INTEGER, DIMENSION(:), POINTER :: r_blk_sizes
1046 LOGICAL :: honor_exact, panels_unsafe, use_cutoff
1047 REAL(kind=dp) :: mem_avail_gb, mem_budget_gb
1048 TYPE(dbcsr_distribution_type) :: dist
1049
1050 CALL timeset(routinen, handle)
1051
1052 IF (ALLOCATED(bs_env%ri_rs%pan_first)) THEN
1053 ALLOCATE (pan_first, source=bs_env%ri_rs%pan_first)
1054 ALLOCATE (pan_last, source=bs_env%ri_rs%pan_last)
1055 CALL timestop(handle)
1056 RETURN
1057 END IF
1058
1059 use_cutoff = bs_env%ri_rs%cutoff_radius_v_w > 0.0_dp .AND. &
1060 ALLOCATED(bs_env%ri_rs%chunk_centroids)
1061
1062 ! MIN(nprows, npcols) is the divisor that bounds the worst-rank Cannon message: a
1063 ! P x n_grid panel is replicated into block row strips (P/nprows x n_grid) or column
1064 ! strips (P x n_grid/npcols) during multiply_cannon, so the largest single-rank
1065 ! message is ~ P*n_grid / MIN(nprows,npcols) elements.
1066 CALL dbcsr_get_info(mat_phi_mu_l, nfullrows_total=n_grid_total, row_blk_size=r_blk_sizes, &
1067 distribution=dist)
1068 CALL dbcsr_distribution_get(dist, nprows=nprows, npcols=npcols)
1069 min_dim = max(min(nprows, npcols), 1)
1070
1071 ! Panel height such that NO per-rank DBCSR message can overflow the 32-bit length field
1072 ! (see bs_env%dbcsr_msg_elem_limit): requiring the worst-rank message to stay under
1073 ! 0.5 * HUGE(int_4) gives the safe height P_safe = 0.5 * HUGE(int_4) * min_dim / n_grid.
1074 IF (use_cutoff) THEN
1075 safe_max = n_grid_total
1076 ELSE
1077 safe_max = int(0.5_dp*real(bs_env%dbcsr_msg_elem_limit, dp)*real(min_dim, dp)/ &
1078 REAL(n_grid_total, dp))
1079 safe_max = max(1, min(safe_max, n_grid_total))
1080 END IF
1081
1082 ! A user-set N_PANELS ( > 1 ) is honored EXACTLY: the planner produces that many panels
1083 ! (up to grid-block granularity) and never force-splits them for the message/memory safety
1084 ! limits -- if a limit is tripped it warns instead of silently changing the count.
1085 n_panels_req = bs_env%ri_rs%n_panels
1086 honor_exact = (n_panels_req > 1)
1087 panels_unsafe = .false.
1088 IF (n_panels_req > 1) THEN
1089 ! ceil(n_grid/n_panels_req) rows per panel => exactly n_panels_req panels. With the
1090 ! cutoff active safe_max = n_grid_total (no clamp, honored exactly); without it, safe_max
1091 ! is the int32-overflow ceiling and MUST still bound the panel (the non-cutoff planner
1092 ! loop has no in-loop message-size check).
1093 panel_size = min((n_grid_total + n_panels_req - 1)/n_panels_req, safe_max)
1094 ELSE
1095 ! Default (<= 1): a single whole-grid panel, clamped to the overflow-safe ceiling.
1096 panel_size = safe_max
1097 END IF
1098 panel_size = max(1, panel_size)
1099
1100 IF (use_cutoff) THEN
1101 ! Half of the measured free memory as panel budget.
1102 CALL mp_mem_avail_per_rank_gb(bs_env%para_env, mem_avail_gb)
1103 mem_budget_gb = 0.5_dp*mem_avail_gb
1104 CALL plan_grid_panels(bs_env, r_blk_sizes, panel_size, min_dim, pan_first, pan_last, &
1105 centroids=bs_env%ri_rs%chunk_centroids, &
1106 cutoff=bs_env%ri_rs%cutoff_radius_v_w, &
1107 n_ri=bs_env%n_RI, n_ao=bs_env%n_ao, &
1108 n_procs=bs_env%para_env%num_pe, mem_budget_gb=mem_budget_gb, &
1109 honor_exact=honor_exact, unsafe=panels_unsafe)
1110 ELSE
1111 CALL plan_grid_panels(bs_env, r_blk_sizes, panel_size, min_dim, pan_first, pan_last)
1112 END IF
1113
1114 IF (honor_exact .AND. panels_unsafe) THEN
1115 WRITE (msg, '(A,I0,A)') &
1116 "N_PANELS = ", n_panels_req, " is used as requested, but one or more panels "// &
1117 "exceed the DBCSR 32-bit message length or the memory budget. The run may abort "// &
1118 "or swap; increase N_PANELS if it does."
1119 cpwarn(trim(msg))
1120 END IF
1121
1122 ALLOCATE (bs_env%ri_rs%pan_first, source=pan_first)
1123 ALLOCATE (bs_env%ri_rs%pan_last, source=pan_last)
1124
1125 CALL timestop(handle)
1126
1127 END SUBROUTINE resolve_grid_panels
1128
1129! **************************************************************************************************
1130!> \brief Estimates and prints per-process memory requirements for the RI-RS GW calculation.
1131!> \param bs_env ...
1132! **************************************************************************************************
1133 SUBROUTINE print_ri_rs_memory_estimate(bs_env)
1134
1135!$ USE OMP_LIB, ONLY: omp_get_max_threads
1136
1137 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1138
1139 CHARACTER(LEN=*), PARAMETER :: routinen = 'print_ri_rs_memory_estimate'
1140
1141 CHARACTER(LEN=max_line_length) :: msg
1142 INTEGER :: handle, iatom, ipan, l, &
1143 max_n_ao_used, max_n_local_grid, &
1144 n_ao_used_atom, n_grid_total, &
1145 n_local_grid, n_loc_ri_max, n_procs, &
1146 n_procs_per_atom, n_ri, n_threads, &
1147 natom, pan_rows, pan_width
1148 INTEGER(KIND=int_8) :: nze_tmpl
1149 INTEGER, ALLOCATABLE, DIMENSION(:) :: pan_first, pan_last
1150 INTEGER, DIMENSION(:), POINTER :: r_blk_sizes
1151 LOGICAL :: use_cutoff
1152 LOGICAL, ALLOCATABLE, DIMENSION(:) :: grid_used
1153 REAL(kind=dp) :: cutoff_ri, mem_avail_gb, mem_d_local_gb, &
1154 mem_dlp_gb, mem_pan_gb, mem_panels_gb, &
1155 mem_phi_local_gb, mem_z_lp_gb, &
1156 mem_zlp_peak_gb, pos_p(3)
1157 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
1158
1159 CALL timeset(routinen, handle)
1160
1161 n_grid_total = bs_env%ri_rs%n_grid_points
1162 CALL dbcsr_get_info(bs_env%ri_rs%mat_phi_mu_l, row_blk_size=r_blk_sizes)
1163 cpassert(sum(r_blk_sizes) == n_grid_total)
1164 n_ri = bs_env%n_RI
1165 n_procs = bs_env%para_env%num_pe
1166
1167 ! Z_lP upper bound: dense n_grid × n_RI, distributed evenly across all ranks.
1168 ! The actual sparse Z_lP is smaller due to the per-atom locality cutoff.
1169 mem_z_lp_gb = real(n_grid_total, dp)*real(n_ri, dp)*8.0_dp/ &
1170 REAL(n_procs, dp)*1.0e-9_dp
1171
1172 ! Peak panel memory during Σ^c: two G panels (A_occ, A_vir) + one W panel plus the
1173 ! grid × RI / grid × AO intermediates. With the CUTOFF_RADIUS_RL_W restriction the panel
1174 ! matrices only allocate the geo-template blocks, so use the same nze-aware model as the
1175 ! panel planner (panel_mem_estimate_GB); without the cutoff, dense panel_rows × n_grid.
1176 ! Plus the n_RI × n_RI W_aux matrix. Distributed over n_procs ranks.
1177 use_cutoff = bs_env%ri_rs%cutoff_radius_v_w > 0.0_dp .AND. &
1178 ALLOCATED(bs_env%ri_rs%chunk_centroids)
1179 CALL resolve_grid_panels(bs_env, bs_env%ri_rs%mat_phi_mu_l, pan_first, pan_last)
1180 mem_panels_gb = 0.0_dp
1181 DO ipan = 1, SIZE(pan_first)
1182 pan_rows = sum(r_blk_sizes(pan_first(ipan):pan_last(ipan)))
1183 IF (use_cutoff) THEN
1184 CALL mask_grid_blocks_near_panel(bs_env%ri_rs%chunk_centroids, pan_first(ipan), &
1185 pan_last(ipan), bs_env%ri_rs%cutoff_radius_v_w, &
1186 grid_used)
1187 pan_width = sum(r_blk_sizes, mask=grid_used)
1188 CALL panel_template_elems(r_blk_sizes, bs_env%ri_rs%chunk_centroids, &
1189 grid_used, pan_first(ipan), pan_last(ipan), &
1190 bs_env%ri_rs%cutoff_radius_v_w, nze_tmpl)
1191 CALL panel_mem_estimate_gb(nze_tmpl, pan_rows, pan_width, n_grid_total, &
1192 n_ri, bs_env%n_ao, n_procs, mem_pan_gb)
1193 ELSE
1194 pan_width = n_grid_total
1195 mem_pan_gb = (3.0_dp*real(pan_rows, dp)*real(pan_width, dp) + &
1196 2.0_dp*real(pan_rows, dp)*real(n_ri, dp))* &
1197 8.0_dp/real(n_procs, dp)*1.0e-9_dp
1198 END IF
1199 mem_panels_gb = max(mem_panels_gb, mem_pan_gb)
1200 END DO
1201 mem_panels_gb = mem_panels_gb + &
1202 REAL(n_ri, dp)*REAL(n_ri, dp)*8.0_dp/REAL(n_procs, dp)*1.0e-9_dp
1203
1204 ! Z_lP SOLVE peak (compute_Z_lP). For the atom P with the largest integration
1205 ! sphere, one rank holds simultaneously:
1206 ! D_local : n_local_grid x n_local_grid (dense D'_ll', BLAS path only; O(n_local_grid^2))
1207 ! phi_local: n_local_grid x n_ao_used (AOs reaching into the sphere only)
1208 ! d_lp : n_local_grid x n_loc_ri, replicated once + one private copy per OMP thread
1209 ! n_local_grid = # grid points within cutoff_ri(P) = CUTOFF_RADIUS_RL_RI (if > 0) else
1210 ! r_c(RI metric) + r_RI(P). This is NOT evenly distributed: n_local_grid depends on the
1211 ! local density of atoms/grid, so the rank owning the densest atom peaks well above the
1212 ! average. We report the worst-case (max over atoms) as a per-rank upper bound.
1213 particle_set => bs_env%ri_rs%particle_set
1214 cpassert(ASSOCIATED(particle_set))
1215 natom = bs_env%n_atom
1216
1217 max_n_local_grid = 0
1218 n_loc_ri_max = 0
1219 max_n_ao_used = 0
1220 DO iatom = 1, natom
1221 IF (bs_env%ri_rs%cutoff_radius_ri_rs > 0.0_dp) THEN
1222 cutoff_ri = bs_env%ri_rs%cutoff_radius_ri_rs
1223 ELSE
1224 cutoff_ri = bs_env%ri_metric%cutoff_radius + bs_env%ri_rs%radius_ri_per_atom(iatom)
1225 END IF
1226 pos_p(:) = particle_set(iatom)%r(:)
1227 n_local_grid = 0
1228 DO l = 1, n_grid_total
1229 IF (sum((bs_env%ri_rs%grid_points(1:3, l) - pos_p(1:3))**2) <= cutoff_ri**2) THEN
1230 n_local_grid = n_local_grid + 1
1231 END IF
1232 END DO
1233 max_n_local_grid = max(max_n_local_grid, n_local_grid)
1234 CALL get_n_ao_in_sphere(bs_env, iatom, cutoff_ri, n_ao_used_atom)
1235 max_n_ao_used = max(max_n_ao_used, n_ao_used_atom)
1236 n_loc_ri_max = max(n_loc_ri_max, &
1237 bs_env%i_RI_end_from_atom(iatom) - bs_env%i_RI_start_from_atom(iatom) + 1)
1238 END DO
1239
1240 n_procs_per_atom = min(max(bs_env%ri_rs%n_procs_per_atom_z_lp, 1), n_procs)
1241 n_threads = 1
1242!$ n_threads = omp_get_max_threads()
1243
1244 ! D_local: dense on one rank for the BLAS path; block-cyclic over the subgroup (=> /G) for
1245 ! the ScaLAPACK path (N_PROCS_PER_ATOM_Z_LP = G > 1). phi_local/d_lp stay per-rank either way.
1246 IF (n_procs_per_atom > 1) THEN
1247 mem_d_local_gb = real(max_n_local_grid, dp)**2*8.0_dp/real(n_procs_per_atom, dp)*1.0e-9_dp
1248 ELSE
1249 mem_d_local_gb = real(max_n_local_grid, dp)**2*8.0_dp*1.0e-9_dp
1250 END IF
1251 mem_phi_local_gb = real(max_n_local_grid, dp)*real(max_n_ao_used, dp)*8.0_dp*1.0e-9_dp
1252 mem_dlp_gb = real(max_n_local_grid, dp)*real(n_loc_ri_max, dp)*8.0_dp* &
1253 REAL(1 + n_threads, dp)*1.0e-9_dp
1254 mem_zlp_peak_gb = mem_d_local_gb + mem_phi_local_gb + mem_dlp_gb
1255
1256 ! Available memory per process = node MemLikelyFree / ranks-per-node, min across ranks
1257 ! (0 on non-Linux => warnings suppressed below).
1258 CALL mp_mem_avail_per_rank_gb(bs_env%para_env, mem_avail_gb)
1259
1260 IF (bs_env%unit_nr > 0) THEN
1261 WRITE (bs_env%unit_nr, '(A)') ' '
1262 WRITE (bs_env%unit_nr, '(T2,A)') 'RI-RS memory estimate per MPI process:'
1263 WRITE (bs_env%unit_nr, '(T4,A,F37.2,A)') &
1264 'Available memory per process (system)', mem_avail_gb, ' GB'
1265 WRITE (bs_env%unit_nr, '(T4,A,F18.2,A)') &
1266 'Required for Z_lP (dense upper bound; actual is sparser)', mem_z_lp_gb, ' GB'
1267 WRITE (bs_env%unit_nr, '(T4,A,F25.2,A)') &
1268 'Required for χ, W, Σ panels (peak per panel step)', mem_panels_gb, ' GB'
1269 WRITE (bs_env%unit_nr, '(T4,A,F17.2,A)') &
1270 'Required for Z_lP solve peak (D_local+ϕ, worst-case atom)', mem_zlp_peak_gb, ' GB'
1271 WRITE (bs_env%unit_nr, '(T4,A,T69,I12)') &
1272 'Worst-case local-grid number of grid points:', max_n_local_grid
1273 WRITE (bs_env%unit_nr, '(T4,A,T69,F9.2,A)') &
1274 'Worst-case memory D_local:', mem_d_local_gb, ' GB'
1275
1276 END IF
1277
1278 IF (mem_avail_gb > 0.0_dp .AND. mem_z_lp_gb > mem_avail_gb) THEN
1279 WRITE (msg, '(A,F0.2,A,F0.2,A)') &
1280 "The estimated memory for Z_lP, ", mem_z_lp_gb, " GB per process, exceeds the "// &
1281 "available ", mem_avail_gb, " GB. Z_lP (n_grid x n_RI) is distributed across all "// &
1282 "MPI ranks, so add nodes, use fewer MPI ranks per node, or raise "// &
1283 "N_PROCS_PER_ATOM_Z_LP to distribute each atom block via ScaLAPACK, which reduces "// &
1284 "the per-rank memory roughly by the number of ranks per atom."
1285 cpwarn(trim(msg))
1286 END IF
1287
1288 IF (mem_avail_gb > 0.0_dp .AND. mem_panels_gb > mem_avail_gb) THEN
1289 WRITE (msg, '(A,F0.2,A,F0.2,A)') &
1290 "The estimated peak memory of the chi/W/Sigma panels, ", mem_panels_gb, &
1291 " GB per process, exceeds the available ", mem_avail_gb, " GB. Panel memory "// &
1292 "scales roughly as 3*panel_size*n_grid/n_procs, so add nodes, use fewer MPI ranks "// &
1293 "per node, or raise N_PANELS for more but smaller panels."
1294 cpwarn(trim(msg))
1295 END IF
1296
1297 IF (mem_avail_gb > 0.0_dp .AND. mem_zlp_peak_gb > mem_avail_gb) THEN
1298 WRITE (msg, '(A,F0.2,A,F0.2,A)') &
1299 "The estimated peak memory of the Z_lP solve, ", mem_zlp_peak_gb, &
1300 " GB per process, exceeds the available ", mem_avail_gb, &
1301 " GB. The per-atom matrix D'_ll' dominates and "// &
1302 "scales as n_local_grid^2, and it is not balanced across ranks: the rank owning "// &
1303 "the atom with the largest integration sphere peaks well above the average. "// &
1304 "Either raise N_PROCS_PER_ATOM_Z_LP to distribute D_local block-cyclic via "// &
1305 "ScaLAPACK, which reduces that term roughly by the number of ranks per atom at no "// &
1306 "loss of accuracy, or lower CUTOFF_RADIUS_RL_RI, which shrinks D_local as "// &
1307 "n_local_grid^2 but trades accuracy, or use fewer MPI ranks per node so that each "// &
1308 "rank has more memory for the peak atom."
1309 cpwarn(trim(msg))
1310 END IF
1311
1312 CALL timestop(handle)
1313
1314 END SUBROUTINE print_ri_rs_memory_estimate
1315
1316! **************************************************************************************************
1317!> \brief Creates an empty (panel_chunks x neighborhood_chunks) DBCSR matrix with zero blocks
1318!> pre-allocated only where |centroid(panel_row r) - centroid(column c)| <= cutoff.
1319!> Used with retain_sparsity=.TRUE. in the subsequent dbcsr_multiply so distant blocks
1320!> of the grid-basis panels (ϕ G ϕ^T, Z W Z^T, ...) are never computed at all.
1321!> \param L_pan ...
1322!> \param L_full ...
1323!> \param centroids ...
1324!> \param cutoff ...
1325!> \param blk0 ...
1326!> \param A_template ...
1327!> \param col_map ...
1328! **************************************************************************************************
1329 SUBROUTINE build_geo_template_panel(L_pan, L_full, centroids, cutoff, blk0, A_template, col_map)
1330 TYPE(dbcsr_type), INTENT(IN) :: l_pan, l_full
1331 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: centroids
1332 REAL(kind=dp), INTENT(IN) :: cutoff
1333 INTEGER, INTENT(IN) :: blk0
1334 TYPE(dbcsr_type), INTENT(OUT) :: a_template
1335 INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL :: col_map
1336
1337 CHARACTER(LEN=*), PARAMETER :: routinen = 'build_geo_template_panel'
1338
1339 INTEGER :: c, cg, cs, handle, my_pcol, my_prow, &
1340 n_grid_blks, n_pan_blks, npcols, &
1341 nprows, r, rs
1342 INTEGER, DIMENSION(:), POINTER :: grid_blk_sizes, pan_blk_sizes
1343 REAL(kind=dp) :: cutoff2
1344 REAL(kind=dp), ALLOCATABLE :: zero_blk(:, :)
1345 TYPE(dbcsr_distribution_type) :: dist
1346
1347 CALL timeset(routinen, handle)
1348
1349 cutoff2 = cutoff**2
1350 CALL dbcsr_get_info(l_pan, nblkrows_total=n_pan_blks, row_blk_size=pan_blk_sizes)
1351 CALL dbcsr_get_info(l_full, nblkrows_total=n_grid_blks, row_blk_size=grid_blk_sizes)
1352
1353 ! create_product_matrix assigns row r to process MOD(r-1,nprows) and
1354 ! col c to MOD(c-1,npcols), so we can determine local ownership analytically.
1355 CALL create_product_matrix(l_pan, l_full, 'N', 'T', a_template)
1356 CALL dbcsr_get_info(a_template, distribution=dist)
1357 CALL dbcsr_distribution_get(dist, nprows=nprows, npcols=npcols, &
1358 myprow=my_prow, mypcol=my_pcol)
1359
1360 ALLOCATE (zero_blk(maxval(pan_blk_sizes(1:n_pan_blks)), &
1361 maxval(grid_blk_sizes(1:n_grid_blks))))
1362 zero_blk(:, :) = 0.0_dp
1363
1364 DO r = 1, n_pan_blks
1365 IF (mod(r - 1, nprows) /= my_prow) cycle
1366 rs = pan_blk_sizes(r)
1367 DO c = 1, n_grid_blks
1368 IF (mod(c - 1, npcols) /= my_pcol) cycle
1369 cg = c
1370 IF (PRESENT(col_map)) cg = col_map(c)
1371 IF ((centroids(1, blk0 + r - 1) - centroids(1, cg))**2 + &
1372 (centroids(2, blk0 + r - 1) - centroids(2, cg))**2 + &
1373 (centroids(3, blk0 + r - 1) - centroids(3, cg))**2 <= cutoff2) THEN
1374 cs = grid_blk_sizes(c)
1375 CALL dbcsr_put_block(a_template, r, c, zero_blk(1:rs, 1:cs))
1376 END IF
1377 END DO
1378 END DO
1379 CALL dbcsr_finalize(a_template)
1380
1381 DEALLOCATE (zero_blk)
1382 CALL timestop(handle)
1383
1384 END SUBROUTINE build_geo_template_panel
1385
1386! **************************************************************************************************
1387!> \brief Slices a contiguous range of grid row-blocks [blk0, blk1] out of a (grid x n) DBCSR
1388!> matrix into a new (P x n) panel matrix: iterate the source's local blocks, put the
1389!> in-range ones into the panel with a remapped row-block index, then finalize. Row-block
1390!> index i of the panel corresponds to source row-block blk0+i-1.
1391!> \param mat_full ...
1392!> \param blk0 ...
1393!> \param blk1 ...
1394!> \param mat_panel ...
1395! **************************************************************************************************
1396 SUBROUTINE extract_grid_panel(mat_full, blk0, blk1, mat_panel)
1397
1398 TYPE(dbcsr_type), INTENT(INOUT) :: mat_full
1399 INTEGER, INTENT(IN) :: blk0, blk1
1400 TYPE(dbcsr_type), INTENT(OUT) :: mat_panel
1401
1402 CHARACTER(LEN=*), PARAMETER :: routinen = 'extract_grid_panel'
1403
1404 INTEGER :: handle, ib, jb, npb
1405 INTEGER, DIMENSION(:), POINTER :: col_blk_full, col_dist_full, &
1406 row_blk_full, row_blk_pan, &
1407 row_dist_full, row_dist_pan
1408 REAL(kind=dp), DIMENSION(:, :), POINTER :: blk
1409 TYPE(dbcsr_distribution_type) :: dist_full, dist_pan
1410 TYPE(dbcsr_iterator_type) :: iter
1411
1412 CALL timeset(routinen, handle)
1413
1414 CALL dbcsr_get_info(mat_full, distribution=dist_full, &
1415 row_blk_size=row_blk_full, col_blk_size=col_blk_full)
1416 CALL dbcsr_distribution_get(dist_full, row_dist=row_dist_full, col_dist=col_dist_full)
1417
1418 npb = blk1 - blk0 + 1
1419 ALLOCATE (row_dist_pan(npb), row_blk_pan(npb))
1420 row_dist_pan(:) = row_dist_full(blk0:blk1)
1421 row_blk_pan(:) = row_blk_full(blk0:blk1)
1422
1423 CALL dbcsr_distribution_new(dist_pan, template=dist_full, &
1424 row_dist=row_dist_pan, col_dist=col_dist_full)
1425 CALL dbcsr_create(mat_panel, name="grid_panel", dist=dist_pan, &
1426 matrix_type=dbcsr_type_no_symmetry, &
1427 row_blk_size=row_blk_pan, col_blk_size=col_blk_full)
1428
1429 CALL dbcsr_iterator_start(iter, mat_full)
1430 DO WHILE (dbcsr_iterator_blocks_left(iter))
1431 CALL dbcsr_iterator_next_block(iter, ib, jb, blk)
1432 IF (ib < blk0 .OR. ib > blk1) cycle
1433 CALL dbcsr_put_block(mat_panel, ib - blk0 + 1, jb, blk)
1434 END DO
1435 CALL dbcsr_iterator_stop(iter)
1436 CALL dbcsr_finalize(mat_panel)
1437
1438 CALL dbcsr_distribution_release(dist_pan)
1439 DEALLOCATE (row_dist_pan, row_blk_pan)
1440
1441 CALL timestop(handle)
1442
1443 END SUBROUTINE extract_grid_panel
1444
1445! **************************************************************************************************
1446!> \brief Marks which column blocks of a DBCSR matrix carry at least one non-zero block anywhere
1447!> (global union). Used to restrict the inner index of the panel multiplies to the
1448!> AO/RI atoms that actually touch the panel (exact: dropped rows only meet zeros).
1449!> \param matrix ...
1450!> \param para_env ...
1451!> \param used ...
1452! **************************************************************************************************
1453 SUBROUTINE collect_used_col_blocks(matrix, para_env, used)
1454
1455 TYPE(dbcsr_type), INTENT(INOUT) :: matrix
1456 TYPE(mp_para_env_type), INTENT(IN), POINTER :: para_env
1457 LOGICAL, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: used
1458
1459 CHARACTER(LEN=*), PARAMETER :: routinen = 'collect_used_col_blocks'
1460
1461 INTEGER :: handle, ib, jb, nblkcols
1462 INTEGER, ALLOCATABLE, DIMENSION(:) :: iused
1463 REAL(kind=dp), DIMENSION(:, :), POINTER :: blk
1464 TYPE(dbcsr_iterator_type) :: iter
1465
1466 CALL timeset(routinen, handle)
1467
1468 CALL dbcsr_get_info(matrix, nblkcols_total=nblkcols)
1469 ALLOCATE (iused(nblkcols))
1470 iused(:) = 0
1471
1472 CALL dbcsr_iterator_start(iter, matrix)
1473 DO WHILE (dbcsr_iterator_blocks_left(iter))
1474 CALL dbcsr_iterator_next_block(iter, ib, jb, blk)
1475 iused(jb) = 1
1476 END DO
1477 CALL dbcsr_iterator_stop(iter)
1478
1479 CALL para_env%sum(iused)
1480
1481 ALLOCATE (used(nblkcols))
1482 used(:) = (iused(:) > 0)
1483 DEALLOCATE (iused)
1484
1485 CALL timestop(handle)
1486
1487 END SUBROUTINE collect_used_col_blocks
1488
1489! **************************************************************************************************
1490!> \brief Copies the flagged block rows (compress_rows=.TRUE.) or block columns (.FALSE.) of a
1491!> DBCSR matrix into a compressed matrix. The subset keeps the parent's process assignment
1492!> along the compressed dimension, so every block stays on its owning rank: the extraction
1493!> is purely local (zero communication), like extract_grid_panel.
1494!> \param mat_full ...
1495!> \param used ...
1496!> \param mat_out ...
1497!> \param compress_rows ...
1498!> \param blk_map ...
1499! **************************************************************************************************
1500 SUBROUTINE extract_masked_blocks(mat_full, used, mat_out, compress_rows, blk_map)
1501
1502 TYPE(dbcsr_type), INTENT(INOUT) :: mat_full
1503 LOGICAL, DIMENSION(:), INTENT(IN) :: used
1504 TYPE(dbcsr_type), INTENT(OUT) :: mat_out
1505 LOGICAL, INTENT(IN) :: compress_rows
1506 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT), &
1507 OPTIONAL :: blk_map
1508
1509 CHARACTER(LEN=*), PARAMETER :: routinen = 'extract_masked_blocks'
1510
1511 INTEGER :: handle, ib, jb, n_blk, n_sub, r
1512 INTEGER, ALLOCATABLE, DIMENSION(:) :: inv_map
1513 INTEGER, DIMENSION(:), POINTER :: blk_full, blk_sub, col_blk_full, &
1514 col_dist_full, dist_full_1d, &
1515 dist_sub_1d, row_blk_full, &
1516 row_dist_full
1517 REAL(kind=dp), DIMENSION(:, :), POINTER :: blk
1518 TYPE(dbcsr_distribution_type) :: dist_full, dist_sub
1519 TYPE(dbcsr_iterator_type) :: iter
1520
1521 CALL timeset(routinen, handle)
1522
1523 CALL dbcsr_get_info(mat_full, distribution=dist_full, &
1524 row_blk_size=row_blk_full, col_blk_size=col_blk_full)
1525 CALL dbcsr_distribution_get(dist_full, row_dist=row_dist_full, col_dist=col_dist_full)
1526
1527 IF (compress_rows) THEN
1528 blk_full => row_blk_full
1529 dist_full_1d => row_dist_full
1530 ELSE
1531 blk_full => col_blk_full
1532 dist_full_1d => col_dist_full
1533 END IF
1534 n_blk = SIZE(blk_full)
1535
1536 n_sub = count(used)
1537 cpassert(n_sub > 0)
1538 ALLOCATE (inv_map(n_blk), blk_sub(n_sub), dist_sub_1d(n_sub))
1539 IF (PRESENT(blk_map)) ALLOCATE (blk_map(n_sub))
1540 inv_map(:) = 0
1541 r = 0
1542 DO ib = 1, n_blk
1543 IF (used(ib)) THEN
1544 r = r + 1
1545 inv_map(ib) = r
1546 blk_sub(r) = blk_full(ib)
1547 dist_sub_1d(r) = dist_full_1d(ib)
1548 IF (PRESENT(blk_map)) blk_map(r) = ib
1549 END IF
1550 END DO
1551
1552 IF (compress_rows) THEN
1553 CALL dbcsr_distribution_new(dist_sub, template=dist_full, &
1554 row_dist=dist_sub_1d, col_dist=col_dist_full)
1555 CALL dbcsr_create(mat_out, name="row_subset", dist=dist_sub, &
1556 matrix_type=dbcsr_type_no_symmetry, &
1557 row_blk_size=blk_sub, col_blk_size=col_blk_full)
1558 ELSE
1559 CALL dbcsr_distribution_new(dist_sub, template=dist_full, &
1560 row_dist=row_dist_full, col_dist=dist_sub_1d)
1561 CALL dbcsr_create(mat_out, name="col_subset", dist=dist_sub, &
1562 matrix_type=dbcsr_type_no_symmetry, &
1563 row_blk_size=row_blk_full, col_blk_size=blk_sub)
1564 END IF
1565
1566 CALL dbcsr_iterator_start(iter, mat_full)
1567 DO WHILE (dbcsr_iterator_blocks_left(iter))
1568 CALL dbcsr_iterator_next_block(iter, ib, jb, blk)
1569 IF (compress_rows) THEN
1570 IF (inv_map(ib) > 0) CALL dbcsr_put_block(mat_out, inv_map(ib), jb, blk)
1571 ELSE
1572 IF (inv_map(jb) > 0) CALL dbcsr_put_block(mat_out, ib, inv_map(jb), blk)
1573 END IF
1574 END DO
1575 CALL dbcsr_iterator_stop(iter)
1576 CALL dbcsr_finalize(mat_out)
1577
1578 CALL dbcsr_distribution_release(dist_sub)
1579 DEALLOCATE (inv_map, blk_sub, dist_sub_1d)
1580
1581 CALL timestop(handle)
1582
1583 END SUBROUTINE extract_masked_blocks
1584
1585! **************************************************************************************************
1586!> \brief Pre-seeds a square blocked DBCSR matrix with zero blocks only for block pairs whose
1587!> centers lie within radius, for use with copy_fm_to_dbcsr(keep_sparsity=T) or
1588!> dbcsr_multiply(retain_sparsity=T). Consumers: the CUTOFF_RADIUS_G_W operator truncation
1589!> (atom-blocked, centers = atom_centers) and the RT-BSE CUTOFF_RADIUS_W0 truncation of the
1590!> grid-basis W^0 (grid-blocked, centers = chunk_centroids).
1591!> \param matrix ...
1592!> \param centers block positions, one column per block row/column of matrix
1593!> \param radius truncation radius, same units as centers (bohr)
1594! **************************************************************************************************
1595 SUBROUTINE reserve_blocks_within_radius(matrix, centers, radius)
1596
1597 TYPE(dbcsr_type), INTENT(INOUT) :: matrix
1598 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: centers
1599 REAL(kind=dp), INTENT(IN) :: radius
1600
1601 CHARACTER(LEN=*), PARAMETER :: routinen = 'reserve_blocks_within_radius'
1602
1603 INTEGER :: handle, i, j, my_pcol, my_prow, &
1604 nblkcols, nblkrows
1605 INTEGER, DIMENSION(:), POINTER :: col_blk, col_dist, row_blk, row_dist
1606 REAL(kind=dp) :: radius2
1607 REAL(kind=dp), ALLOCATABLE :: zero_blk(:, :)
1608 TYPE(dbcsr_distribution_type) :: dist
1609
1610 CALL timeset(routinen, handle)
1611
1612 CALL dbcsr_get_info(matrix, nblkrows_total=nblkrows, nblkcols_total=nblkcols, &
1613 row_blk_size=row_blk, col_blk_size=col_blk, distribution=dist)
1614 CALL dbcsr_distribution_get(dist, row_dist=row_dist, col_dist=col_dist, &
1615 myprow=my_prow, mypcol=my_pcol)
1616 cpassert(nblkrows == SIZE(centers, 2))
1617 cpassert(nblkcols == SIZE(centers, 2))
1618
1619 radius2 = radius**2
1620 ALLOCATE (zero_blk(maxval(row_blk(1:nblkrows)), maxval(col_blk(1:nblkcols))))
1621 zero_blk(:, :) = 0.0_dp
1622
1623 DO i = 1, nblkrows
1624 IF (row_dist(i) /= my_prow) cycle
1625 DO j = 1, nblkcols
1626 IF (col_dist(j) /= my_pcol) cycle
1627 IF ((centers(1, i) - centers(1, j))**2 + (centers(2, i) - centers(2, j))**2 + &
1628 (centers(3, i) - centers(3, j))**2 <= radius2) THEN
1629 CALL dbcsr_put_block(matrix, i, j, zero_blk(1:row_blk(i), 1:col_blk(j)))
1630 END IF
1631 END DO
1632 END DO
1633 CALL dbcsr_finalize(matrix)
1634
1635 DEALLOCATE (zero_blk)
1636 CALL timestop(handle)
1637
1638 END SUBROUTINE reserve_blocks_within_radius
1639
1640! **************************************************************************************************
1641!> \brief Creates the (empty) result matrix of op(mat_left) * op(mat_right) with the correct block
1642!> structure and a distribution on the shared process grid, ready to be filled by
1643!> dbcsr_multiply. Row structure comes from op(left), column structure from op(right).
1644!> \param mat_left ...
1645!> \param mat_right ...
1646!> \param transa 'N' or 'T' applied to mat_left
1647!> \param transb 'N' or 'T' applied to mat_right
1648!> \param mat_out ...
1649! **************************************************************************************************
1650 SUBROUTINE create_product_matrix(mat_left, mat_right, transa, transb, mat_out)
1651
1652 TYPE(dbcsr_type), INTENT(IN) :: mat_left, mat_right
1653 CHARACTER(LEN=1), INTENT(IN) :: transa, transb
1654 TYPE(dbcsr_type), INTENT(OUT) :: mat_out
1655
1656 CHARACTER(LEN=*), PARAMETER :: routinen = 'create_product_matrix'
1657
1658 INTEGER :: handle, i, npcols, nprows
1659 INTEGER, DIMENSION(:), POINTER :: col_blk_l, col_blk_r, out_col_blk, &
1660 out_col_dist, out_row_blk, &
1661 out_row_dist, row_blk_l, row_blk_r
1662 TYPE(dbcsr_distribution_type) :: dist_l, dist_out
1663
1664 CALL timeset(routinen, handle)
1665
1666 CALL dbcsr_get_info(mat_left, distribution=dist_l, row_blk_size=row_blk_l, col_blk_size=col_blk_l)
1667 CALL dbcsr_get_info(mat_right, row_blk_size=row_blk_r, col_blk_size=col_blk_r)
1668 CALL dbcsr_distribution_get(dist_l, nprows=nprows, npcols=npcols)
1669
1670 ! block SIZES follow op(left)/op(right); DISTRIBUTIONS are freshly round-robined onto the
1671 ! shared process grid (a transposed operand's row-dist is NOT a valid col-dist on a
1672 ! non-square grid). dbcsr_multiply redistributes internally, so any valid mapping works.
1673 IF (transa == 'N') THEN
1674 out_row_blk => row_blk_l
1675 ELSE
1676 out_row_blk => col_blk_l
1677 END IF
1678 IF (transb == 'N') THEN
1679 out_col_blk => col_blk_r
1680 ELSE
1681 out_col_blk => row_blk_r
1682 END IF
1683
1684 ALLOCATE (out_row_dist(SIZE(out_row_blk)), out_col_dist(SIZE(out_col_blk)))
1685 DO i = 1, SIZE(out_row_blk)
1686 out_row_dist(i) = mod(i - 1, nprows)
1687 END DO
1688 DO i = 1, SIZE(out_col_blk)
1689 out_col_dist(i) = mod(i - 1, npcols)
1690 END DO
1691
1692 CALL dbcsr_distribution_new(dist_out, template=dist_l, &
1693 row_dist=out_row_dist, col_dist=out_col_dist)
1694 CALL dbcsr_create(mat_out, name="panel_product", dist=dist_out, &
1695 matrix_type=dbcsr_type_no_symmetry, &
1696 row_blk_size=out_row_blk, col_blk_size=out_col_blk)
1697 CALL dbcsr_distribution_release(dist_out)
1698 DEALLOCATE (out_row_dist, out_col_dist)
1699
1700 CALL timestop(handle)
1701
1702 END SUBROUTINE create_product_matrix
1703
1704! **************************************************************************************************
1705!> \brief Builds the AO-space Green's function operator G^occ/vir_µν (AO x AO DBCSR)
1706!> \param bs_env ...
1707!> \param tau ...
1708!> \param ispin ...
1709!> \param occ ...
1710!> \param vir ...
1711!> \param template ...
1712!> \param matrix_G_ao ...
1713! **************************************************************************************************
1714 SUBROUTINE build_g_ao(bs_env, tau, ispin, occ, vir, template, matrix_G_ao)
1715
1716 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1717 REAL(kind=dp), INTENT(IN) :: tau
1718 INTEGER, INTENT(IN) :: ispin
1719 LOGICAL, INTENT(IN) :: occ, vir
1720 TYPE(dbcsr_type), INTENT(INOUT) :: template
1721 TYPE(dbcsr_type), INTENT(OUT) :: matrix_g_ao
1722
1723 CHARACTER(LEN=*), PARAMETER :: routinen = 'build_G_ao'
1724
1725 INTEGER :: handle
1726 INTEGER, DIMENSION(:), POINTER :: blk_ao, dist_row_ao
1727 TYPE(cp_fm_type), POINTER :: fm_g
1728 TYPE(dbcsr_distribution_type) :: dist_ao_ao
1729
1730 CALL timeset(routinen, handle)
1731
1732 IF (occ) THEN
1733 fm_g => bs_env%fm_Gocc
1734 ELSE
1735 fm_g => bs_env%fm_Gvir
1736 END IF
1737
1738 CALL g_occ_vir(bs_env, tau, fm_g, ispin, occ=occ, vir=vir)
1739
1740 CALL setup_square_topology(template, dist_ao_ao, blk_ao, dist_row_ao)
1741 CALL dbcsr_create(matrix_g_ao, name="G_ao", dist=dist_ao_ao, &
1742 matrix_type=dbcsr_type_no_symmetry, &
1743 row_blk_size=blk_ao, col_blk_size=blk_ao)
1744
1745 ! Optional CUTOFF_RADIUS_G_W operator truncation: only atom-pair blocks within the radius
1746 ! are reserved and filled.
1747 IF (bs_env%ri_rs%cutoff_radius_g_w > 0.0_dp .AND. &
1748 ALLOCATED(bs_env%ri_rs%atom_centers)) THEN
1749 CALL reserve_blocks_within_radius(matrix_g_ao, bs_env%ri_rs%atom_centers, &
1750 bs_env%ri_rs%cutoff_radius_g_w)
1751 CALL copy_fm_to_dbcsr(fm_g, matrix_g_ao, keep_sparsity=.true.)
1752 ELSE
1753 CALL copy_fm_to_dbcsr(fm_g, matrix_g_ao, keep_sparsity=.false.)
1754 END IF
1755 CALL dbcsr_filter(matrix_g_ao, bs_env%eps_filter)
1756
1757 ! release only the topology; keep matrix_G_ao for the caller
1758 CALL release_square_topology(dist=dist_ao_ao, mapped_dist=dist_row_ao)
1759
1760 CALL timestop(handle)
1761
1762 END SUBROUTINE build_g_ao
1763
1764! **************************************************************************************************
1765!> \brief Panel-streaming evaluation of out += scale * L_out^T (A_grid ∘ B_grid) L_out,
1766!> 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
1767!> grid x grid objects. The grid (row) index is processed in panels of ~panel_size rows; for
1768!> each panel only P x grid slabs are built, Hadamard-multiplied, and contracted into the
1769!> (small) output. Algebraically identical to L_out^T (A_grid ∘ B_grid) L_out summed over
1770!> grid rows, so the result matches the non-streamed path to eps_filter.
1771!>
1772!> Mapping (L in {phi (grid x AO), Z (grid x RI)}, M the AO/RI-space operator):
1773!> chi : L_A=L_B=phi, M_A=G_occ_ao, M_B=G_vir_ao, L_out=Z -> RI x RI
1774!> Sig : L_A=phi (M_A=D/G), L_B=Z (M_B=V/W), L_out=phi -> AO x AO
1775!> \param L_A ...
1776!> \param M_A ...
1777!> \param L_B ...
1778!> \param M_B ...
1779!> \param L_out ...
1780!> \param mat_out ...
1781!> \param scale ...
1782!> \param eps ...
1783!> \param para_env ...
1784!> \param pan_first ...
1785!> \param pan_last ...
1786!> \param lb_eq_la ...
1787!> \param lout_eq_la ...
1788!> \param zero_out ...
1789!> \param keep_sparsity ...
1790!> \param centroids ...
1791!> \param cutoff ...
1792!> \param grid_occupation ...
1793! **************************************************************************************************
1794 SUBROUTINE contract_grid_panels(L_A, M_A, L_B, M_B, L_out, mat_out, scale, eps, para_env, &
1795 pan_first, pan_last, lb_eq_la, lout_eq_la, zero_out, &
1796 keep_sparsity, centroids, cutoff, grid_occupation)
1797
1798 TYPE(dbcsr_type), INTENT(INOUT), TARGET :: l_a
1799 TYPE(dbcsr_type), INTENT(INOUT) :: m_a
1800 TYPE(dbcsr_type), INTENT(INOUT), TARGET :: l_b
1801 TYPE(dbcsr_type), INTENT(INOUT) :: m_b
1802 TYPE(dbcsr_type), INTENT(INOUT), TARGET :: l_out
1803 TYPE(dbcsr_type), INTENT(INOUT) :: mat_out
1804 REAL(kind=dp), INTENT(IN) :: scale, eps
1805 TYPE(mp_para_env_type), INTENT(IN), POINTER :: para_env
1806 INTEGER, DIMENSION(:), INTENT(IN) :: pan_first, pan_last
1807 LOGICAL, INTENT(IN) :: lb_eq_la, lout_eq_la, zero_out
1808 LOGICAL, INTENT(IN), OPTIONAL :: keep_sparsity
1809 REAL(kind=dp), DIMENSION(:, :), INTENT(IN), &
1810 OPTIONAL :: centroids
1811 REAL(kind=dp), INTENT(IN), OPTIONAL :: cutoff
1812 REAL(kind=dp), INTENT(OUT), OPTIONAL :: grid_occupation
1813
1814 CHARACTER(LEN=*), PARAMETER :: routinen = 'contract_grid_panels'
1815
1816 INTEGER :: blk0, blk1, handle, ipan, n_grid_total, &
1817 ncols_pan, nrows_pan
1818 INTEGER, ALLOCATABLE, DIMENSION(:) :: gmap
1819 LOGICAL :: my_keep_sparsity, use_cutoff
1820 LOGICAL, ALLOCATABLE, DIMENSION(:) :: grid_used, useda, usedb
1821 TYPE(dbcsr_type) :: a_pan, b_pan, c_pan, la_pan, la_panc, &
1822 lb_pan, lb_panc, lout_pan, ma_sub, &
1823 mb_sub, tmp2, tmpa, tmpb
1824 TYPE(dbcsr_type), POINTER :: rb_a, rb_b, rb_out
1825 TYPE(dbcsr_type), TARGET :: la_near, lb_near, lout_near
1826
1827 CALL timeset(routinen, handle)
1828
1829 my_keep_sparsity = .false.
1830 IF (PRESENT(keep_sparsity)) my_keep_sparsity = keep_sparsity
1831 use_cutoff = PRESENT(centroids) .AND. PRESENT(cutoff)
1832 IF (use_cutoff) use_cutoff = cutoff > 0.0_dp
1833 IF (PRESENT(grid_occupation)) grid_occupation = 0.0_dp
1834
1835 CALL dbcsr_get_info(l_a, nfullrows_total=n_grid_total)
1836
1837 IF (zero_out) CALL dbcsr_set(mat_out, 0.0_dp)
1838
1839 DO ipan = 1, SIZE(pan_first)
1840 blk0 = pan_first(ipan)
1841 blk1 = pan_last(ipan)
1842
1843 ! phi/Z panel slices (P x n)
1844 CALL extract_grid_panel(l_a, blk0, blk1, la_pan)
1845 IF (.NOT. lb_eq_la) CALL extract_grid_panel(l_b, blk0, blk1, lb_pan)
1846 IF (.NOT. lout_eq_la) CALL extract_grid_panel(l_out, blk0, blk1, lout_pan)
1847
1848 ! Which AO/RI atoms (column blocks) actually touch this panel: the inner index of
1849 ! every multiply below is restricted to them, so only the matching rows of the
1850 ! system-wide operators M_A/M_B ever enter Cannon (exact: dropped rows meet zeros).
1851 CALL collect_used_col_blocks(la_pan, para_env, useda)
1852 IF (.NOT. lb_eq_la) THEN
1853 CALL collect_used_col_blocks(lb_pan, para_env, usedb)
1854 ELSE
1855 IF (ALLOCATED(usedb)) DEALLOCATE (usedb)
1856 ALLOCATE (usedb, source=useda)
1857 END IF
1858 IF (.NOT. (any(useda) .AND. any(usedb))) THEN
1859 ! empty panel slice: its Hadamard contribution is exactly zero on all ranks
1860 CALL dbcsr_release(la_pan)
1861 IF (.NOT. lb_eq_la) CALL dbcsr_release(lb_pan)
1862 IF (.NOT. lout_eq_la) CALL dbcsr_release(lout_pan)
1863 cycle
1864 END IF
1865
1866 ! Grid rows within reach of the panel: with the CUTOFF_RADIUS_RL_W truncation only
1867 ! they can appear as columns of the panel products / inner rows of the L_out multiply,
1868 ! so the system-wide phi/Z right operands are cut down to this neighborhood slice
1869 ! (local extraction, zero communication; exact w.r.t. the geo template).
1870 IF (use_cutoff) THEN
1871 CALL mask_grid_blocks_near_panel(centroids, blk0, blk1, cutoff, grid_used)
1872 CALL extract_masked_blocks(l_a, grid_used, la_near, compress_rows=.true., blk_map=gmap)
1873 IF (.NOT. lb_eq_la) CALL extract_masked_blocks(l_b, grid_used, lb_near, compress_rows=.true.)
1874 IF (.NOT. lout_eq_la) CALL extract_masked_blocks(l_out, grid_used, lout_near, compress_rows=.true.)
1875 rb_a => la_near
1876 ELSE
1877 rb_a => l_a
1878 END IF
1879 IF (lb_eq_la) THEN
1880 rb_b => rb_a
1881 ELSE IF (use_cutoff) THEN
1882 rb_b => lb_near
1883 ELSE
1884 rb_b => l_b
1885 END IF
1886 IF (lout_eq_la) THEN
1887 rb_out => rb_a
1888 ELSE IF (use_cutoff) THEN
1889 rb_out => lout_near
1890 ELSE
1891 rb_out => l_out
1892 END IF
1893
1894 ! A_pan = LA_pan * M_A * L_A^T (P x grid_near).
1895 ! When cutoff is active, A_pan is pre-seeded with only nearby blocks via
1896 ! build_geo_template_panel, and the multiply uses retain_sparsity to skip
1897 ! computing distant blocks entirely (exact: they are zero by locality).
1898 CALL extract_masked_blocks(la_pan, useda, la_panc, compress_rows=.false.)
1899 CALL extract_masked_blocks(m_a, useda, ma_sub, compress_rows=.true.)
1900 CALL create_product_matrix(la_panc, ma_sub, 'N', 'N', tmpa)
1901 CALL dbcsr_multiply('N', 'N', 1.0_dp, la_panc, ma_sub, 0.0_dp, tmpa, filter_eps=eps)
1902 CALL dbcsr_release(ma_sub)
1903 IF (use_cutoff) THEN
1904 CALL build_geo_template_panel(la_pan, la_near, centroids, cutoff, blk0, a_pan, &
1905 col_map=gmap)
1906 ELSE
1907 CALL create_product_matrix(tmpa, rb_a, 'N', 'T', a_pan)
1908 END IF
1909 CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpa, rb_a, 0.0_dp, a_pan, &
1910 filter_eps=eps, retain_sparsity=use_cutoff)
1911 CALL dbcsr_release(tmpa)
1912
1913 ! Grid-basis occupation of A_pan = ϕ G ϕ^T, accumulated over ALL panels into the
1914 ! occupation of the (never formed) full grid x grid object:
1915 ! sum_panels nnz(A_pan) / n_grid^2, with nnz = occ * pan_rows * pan_cols.
1916 ! Panel-independent by construction -- a single-panel sample would instead report the
1917 ! local neighbor count of whichever region happens to land in that panel.
1918 IF (PRESENT(grid_occupation)) THEN
1919 CALL dbcsr_get_info(a_pan, nfullrows_total=nrows_pan, nfullcols_total=ncols_pan)
1920 grid_occupation = grid_occupation + dbcsr_get_occupation(a_pan)* &
1921 REAL(ncols_pan, dp)*REAL(nrows_pan, dp)/ &
1922 (REAL(n_grid_total, dp)*REAL(n_grid_total, dp))
1923 END IF
1924
1925 ! B_pan = LB_pan * M_B * L_B^T (P x grid_near); reuse the L_A slices when L_B == L_A.
1926 ! With keep_sparsity, B_pan is pre-populated with A_pan's block structure so that
1927 ! retain_sparsity forces the final multiply to fill only those blocks (exact for ∘).
1928 IF (lb_eq_la) THEN
1929 CALL extract_masked_blocks(m_b, useda, mb_sub, compress_rows=.true.)
1930 CALL create_product_matrix(la_panc, mb_sub, 'N', 'N', tmpb)
1931 CALL dbcsr_multiply('N', 'N', 1.0_dp, la_panc, mb_sub, 0.0_dp, tmpb, filter_eps=eps)
1932 ELSE
1933 CALL extract_masked_blocks(lb_pan, usedb, lb_panc, compress_rows=.false.)
1934 CALL extract_masked_blocks(m_b, usedb, mb_sub, compress_rows=.true.)
1935 CALL create_product_matrix(lb_panc, mb_sub, 'N', 'N', tmpb)
1936 CALL dbcsr_multiply('N', 'N', 1.0_dp, lb_panc, mb_sub, 0.0_dp, tmpb, filter_eps=eps)
1937 CALL dbcsr_release(lb_panc)
1938 END IF
1939 CALL dbcsr_release(mb_sub)
1940 IF (my_keep_sparsity) THEN
1941 CALL dbcsr_create(b_pan, template=a_pan)
1942 CALL dbcsr_copy(b_pan, a_pan)
1943 CALL dbcsr_set(b_pan, 0.0_dp)
1944 ! The output pattern is already fixed. Omitting redundant on-the-fly filtering also
1945 ! avoids overflowing DBCSR's single-precision screening norms for conditioned Z fits.
1946 CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpb, rb_b, 0.0_dp, b_pan, &
1947 retain_sparsity=.true.)
1948 ELSE
1949 CALL create_product_matrix(tmpb, rb_b, 'N', 'T', b_pan)
1950 CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpb, rb_b, 0.0_dp, b_pan, filter_eps=eps)
1951 END IF
1952 CALL dbcsr_release(tmpb)
1953 CALL dbcsr_release(la_panc)
1954
1955 ! C_pan = scale * (A_pan ∘ B_pan) (P x grid_near)
1956 CALL dbcsr_create(c_pan, template=a_pan)
1957 CALL hadamard_product(a_pan, b_pan, c_pan, scale)
1958 CALL dbcsr_release(a_pan)
1959 CALL dbcsr_release(b_pan)
1960
1961 ! tmp2 = C_pan * L_out (P x n_out; inner index restricted to the neighborhood)
1962 CALL create_product_matrix(c_pan, rb_out, 'N', 'N', tmp2)
1963 CALL dbcsr_multiply('N', 'N', 1.0_dp, c_pan, rb_out, 0.0_dp, tmp2, filter_eps=eps)
1964 CALL dbcsr_release(c_pan)
1965
1966 ! mat_out += L_out_pan^T * tmp2 (accumulate: beta = 1)
1967 IF (lout_eq_la) THEN
1968 CALL dbcsr_multiply('T', 'N', 1.0_dp, la_pan, tmp2, 1.0_dp, mat_out, filter_eps=eps)
1969 ELSE
1970 CALL dbcsr_multiply('T', 'N', 1.0_dp, lout_pan, tmp2, 1.0_dp, mat_out, filter_eps=eps)
1971 CALL dbcsr_release(lout_pan)
1972 END IF
1973 CALL dbcsr_release(tmp2)
1974 IF (.NOT. lb_eq_la) CALL dbcsr_release(lb_pan)
1975 CALL dbcsr_release(la_pan)
1976 IF (use_cutoff) THEN
1977 CALL dbcsr_release(la_near)
1978 IF (.NOT. lb_eq_la) CALL dbcsr_release(lb_near)
1979 IF (.NOT. lout_eq_la) CALL dbcsr_release(lout_near)
1980 END IF
1981
1982 END DO
1983
1984 CALL timestop(handle)
1985
1986 END SUBROUTINE contract_grid_panels
1987
1988! **************************************************************************************************
1989!> \brief Σ^c-specific panel loop: computes both the occupied (neg) and virtual (pos) contributions
1990!> in a single pass over grid panels, forming W_pan = Z_panel × W_aux × Z^T only ONCE per
1991!> panel and reusing it for both the G^occ and G^vir Hadamard contractions.
1992!>
1993!> Computes:
1994!> mat_Sigma_neg = ϕ^T ( (ϕ G^occ ϕ^T) ∘ (Z W^MIC Z^T) ) ϕ
1995!> mat_Sigma_pos = ϕ^T ( (ϕ G^vir ϕ^T) ∘ (Z W^MIC Z^T) ) ϕ
1996!>
1997!> \param mat_phi ...
1998!> \param mat_Z ...
1999!> \param mat_G_occ_ao ...
2000!> \param mat_G_vir_ao ...
2001!> \param mat_W_aux ...
2002!> \param mat_Sigma_neg ...
2003!> \param mat_Sigma_pos ...
2004!> \param eps ...
2005!> \param para_env ...
2006!> \param pan_first ...
2007!> \param pan_last ...
2008!> \param keep_sparsity ...
2009!> \param centroids ...
2010!> \param cutoff ...
2011! **************************************************************************************************
2012 SUBROUTINE contract_grid_panels_sigma_c(mat_phi, mat_Z, mat_G_occ_ao, mat_G_vir_ao, &
2013 mat_W_aux, mat_Sigma_neg, mat_Sigma_pos, eps, para_env, &
2014 pan_first, pan_last, keep_sparsity, centroids, cutoff)
2015
2016 TYPE(dbcsr_type), INTENT(INOUT), TARGET :: mat_phi, mat_z
2017 TYPE(dbcsr_type), INTENT(INOUT) :: mat_g_occ_ao, mat_g_vir_ao, mat_w_aux, &
2018 mat_sigma_neg, mat_sigma_pos
2019 REAL(kind=dp), INTENT(IN) :: eps
2020 TYPE(mp_para_env_type), INTENT(IN), POINTER :: para_env
2021 INTEGER, DIMENSION(:), INTENT(IN) :: pan_first, pan_last
2022 LOGICAL, INTENT(IN), OPTIONAL :: keep_sparsity
2023 REAL(kind=dp), DIMENSION(:, :), INTENT(IN), &
2024 OPTIONAL :: centroids
2025 REAL(kind=dp), INTENT(IN), OPTIONAL :: cutoff
2026
2027 CHARACTER(LEN=*), PARAMETER :: routinen = 'contract_grid_panels_sigma_c'
2028
2029 INTEGER :: blk0, blk1, handle, ipan
2030 INTEGER, ALLOCATABLE, DIMENSION(:) :: gmap
2031 LOGICAL :: my_keep_sparsity, use_cutoff
2032 LOGICAL, ALLOCATABLE, DIMENSION(:) :: grid_used, used_ao, used_ri
2033 TYPE(dbcsr_type) :: a_occ, a_vir, c_pan, g_occ_sub, &
2034 g_vir_sub, phi_pan, phi_panc, tmp2, &
2035 tmpa, tmpb, w_pan, w_sub, z_pan, z_panc
2036 TYPE(dbcsr_type), POINTER :: rb_phi, rb_z
2037 TYPE(dbcsr_type), TARGET :: phi_near, z_near
2038
2039 CALL timeset(routinen, handle)
2040
2041 my_keep_sparsity = .false.
2042 IF (PRESENT(keep_sparsity)) my_keep_sparsity = keep_sparsity
2043 use_cutoff = PRESENT(centroids) .AND. PRESENT(cutoff)
2044 IF (use_cutoff) use_cutoff = cutoff > 0.0_dp
2045
2046 CALL dbcsr_set(mat_sigma_neg, 0.0_dp)
2047 CALL dbcsr_set(mat_sigma_pos, 0.0_dp)
2048
2049 DO ipan = 1, SIZE(pan_first)
2050 blk0 = pan_first(ipan)
2051 blk1 = pan_last(ipan)
2052
2053 CALL extract_grid_panel(mat_phi, blk0, blk1, phi_pan)
2054 CALL extract_grid_panel(mat_z, blk0, blk1, z_pan)
2055
2056 ! AO/RI atoms touching this panel: only the matching rows of G_occ/G_vir/W ever
2057 ! enter the multiplies below (exact: dropped rows meet zero columns of the panel).
2058 CALL collect_used_col_blocks(phi_pan, para_env, used_ao)
2059 CALL collect_used_col_blocks(z_pan, para_env, used_ri)
2060 IF (.NOT. (any(used_ao) .AND. any(used_ri))) THEN
2061 CALL dbcsr_release(phi_pan)
2062 CALL dbcsr_release(z_pan)
2063 cycle
2064 END IF
2065
2066 ! Neighborhood slices of phi/Z (grid rows within cutoff of the panel): they replace
2067 ! the system-wide right operands in every multiply (local extraction, zero comm).
2068 IF (use_cutoff) THEN
2069 CALL mask_grid_blocks_near_panel(centroids, blk0, blk1, cutoff, grid_used)
2070 CALL extract_masked_blocks(mat_phi, grid_used, phi_near, compress_rows=.true., blk_map=gmap)
2071 CALL extract_masked_blocks(mat_z, grid_used, z_near, compress_rows=.true.)
2072 rb_phi => phi_near
2073 rb_z => z_near
2074 ELSE
2075 rb_phi => mat_phi
2076 rb_z => mat_z
2077 END IF
2078
2079 CALL extract_masked_blocks(phi_pan, used_ao, phi_panc, compress_rows=.false.)
2080 CALL extract_masked_blocks(z_pan, used_ri, z_panc, compress_rows=.false.)
2081 CALL extract_masked_blocks(mat_g_occ_ao, used_ao, g_occ_sub, compress_rows=.true.)
2082 CALL extract_masked_blocks(mat_g_vir_ao, used_ao, g_vir_sub, compress_rows=.true.)
2083 CALL extract_masked_blocks(mat_w_aux, used_ri, w_sub, compress_rows=.true.)
2084
2085 ! A_occ = phi_pan × G_occ × phi^T (built first so W_pan can inherit its pattern).
2086 ! With cutoff active, A_occ is pre-seeded with geo-local blocks so that the
2087 ! phi^T multiply uses retain_sparsity and never computes distant blocks.
2088 CALL create_product_matrix(phi_panc, g_occ_sub, 'N', 'N', tmpa)
2089 CALL dbcsr_multiply('N', 'N', 1.0_dp, phi_panc, g_occ_sub, 0.0_dp, tmpa, filter_eps=eps)
2090 IF (use_cutoff) THEN
2091 CALL build_geo_template_panel(phi_pan, phi_near, centroids, cutoff, blk0, a_occ, &
2092 col_map=gmap)
2093 ELSE
2094 CALL create_product_matrix(tmpa, rb_phi, 'N', 'T', a_occ)
2095 END IF
2096 CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpa, rb_phi, 0.0_dp, a_occ, &
2097 filter_eps=eps, retain_sparsity=use_cutoff)
2098 CALL dbcsr_release(tmpa)
2099 CALL dbcsr_release(g_occ_sub)
2100
2101 ! A_vir = phi_pan × G_vir × phi^T (same pre-screen as A_occ)
2102 CALL create_product_matrix(phi_panc, g_vir_sub, 'N', 'N', tmpa)
2103 CALL dbcsr_multiply('N', 'N', 1.0_dp, phi_panc, g_vir_sub, 0.0_dp, tmpa, filter_eps=eps)
2104 IF (use_cutoff) THEN
2105 CALL build_geo_template_panel(phi_pan, phi_near, centroids, cutoff, blk0, a_vir, &
2106 col_map=gmap)
2107 ELSE
2108 CALL create_product_matrix(tmpa, rb_phi, 'N', 'T', a_vir)
2109 END IF
2110 CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpa, rb_phi, 0.0_dp, a_vir, &
2111 filter_eps=eps, retain_sparsity=use_cutoff)
2112 CALL dbcsr_release(tmpa)
2113 CALL dbcsr_release(g_vir_sub)
2114
2115 ! W_pan = Z_pan × W_aux × Z^T (computed once, reused for both Σ^c terms).
2116 ! With keep_sparsity, W_pan is pre-seeded with the union of A_occ and A_vir block
2117 ! patterns so that retain_sparsity forces the multiply to fill only those blocks:
2118 ! exact since W outside G_occ∪G_vir is multiplied by zero in the Hadamard.
2119 CALL create_product_matrix(z_panc, w_sub, 'N', 'N', tmpb)
2120 CALL dbcsr_multiply('N', 'N', 1.0_dp, z_panc, w_sub, 0.0_dp, tmpb, filter_eps=eps)
2121 IF (my_keep_sparsity) THEN
2122 CALL dbcsr_create(w_pan, template=a_occ)
2123 CALL dbcsr_copy(w_pan, a_occ)
2124 CALL dbcsr_add(w_pan, a_vir, 1.0_dp, 1.0_dp)
2125 CALL dbcsr_set(w_pan, 0.0_dp)
2126 ! The retained pattern is exact for the following Hadamard products; screening it
2127 ! again is redundant and may overflow DBCSR's single-precision block-norm product.
2128 CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpb, rb_z, 0.0_dp, w_pan, &
2129 retain_sparsity=.true.)
2130 ELSE
2131 CALL create_product_matrix(tmpb, rb_z, 'N', 'T', w_pan)
2132 CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpb, rb_z, 0.0_dp, w_pan, filter_eps=eps)
2133 END IF
2134 CALL dbcsr_release(tmpb)
2135 CALL dbcsr_release(w_sub)
2136 CALL dbcsr_release(phi_panc)
2137 CALL dbcsr_release(z_panc)
2138
2139 ! Σ^c_neg: ϕ^T ( A_occ ∘ W_pan ) ϕ
2140 CALL dbcsr_create(c_pan, template=a_occ)
2141 CALL hadamard_product(a_occ, w_pan, c_pan, 1.0_dp)
2142 CALL dbcsr_release(a_occ)
2143 CALL create_product_matrix(c_pan, rb_phi, 'N', 'N', tmp2)
2144 CALL dbcsr_multiply('N', 'N', 1.0_dp, c_pan, rb_phi, 0.0_dp, tmp2, filter_eps=eps)
2145 CALL dbcsr_release(c_pan)
2146 CALL dbcsr_multiply('T', 'N', 1.0_dp, phi_pan, tmp2, 1.0_dp, mat_sigma_neg, filter_eps=eps)
2147 CALL dbcsr_release(tmp2)
2148
2149 ! Σ^c_pos: ϕ^T ( A_vir ∘ W_pan ) ϕ — W_pan reused
2150 CALL dbcsr_create(c_pan, template=a_vir)
2151 CALL hadamard_product(a_vir, w_pan, c_pan, 1.0_dp)
2152 CALL dbcsr_release(a_vir)
2153 CALL create_product_matrix(c_pan, rb_phi, 'N', 'N', tmp2)
2154 CALL dbcsr_multiply('N', 'N', 1.0_dp, c_pan, rb_phi, 0.0_dp, tmp2, filter_eps=eps)
2155 CALL dbcsr_release(c_pan)
2156 CALL dbcsr_multiply('T', 'N', 1.0_dp, phi_pan, tmp2, 1.0_dp, mat_sigma_pos, filter_eps=eps)
2157 CALL dbcsr_release(tmp2)
2158
2159 CALL dbcsr_release(w_pan)
2160 CALL dbcsr_release(z_pan)
2161 CALL dbcsr_release(phi_pan)
2162 IF (use_cutoff) THEN
2163 CALL dbcsr_release(phi_near)
2164 CALL dbcsr_release(z_near)
2165 END IF
2166
2167 END DO
2168
2169 CALL timestop(handle)
2170
2171 END SUBROUTINE contract_grid_panels_sigma_c
2172
2173! **************************************************************************************************
2174!> \brief Computes the screened Coulomb interaction on the imaginary-time grid, entirely in the
2175!> RI auxiliary (PQ) basis:
2176!> χ_PQ(iω) = Σ_τ w(ω,τ) cos(ωτ) χ_PQ(iτ) (cosine transform)
2177!> ε(iω) = Id - V^0.5 M^-1 χ(iω) M^-1 V^0.5 (dielectric function)
2178!> W(iω) = V^0.5 ( ε^-1(iω) - Id ) V^0.5 (correlation part only)
2179!> W(iτ) = Σ_ω w̃(τ,ω) cos(ωτ) W(iω) (back transform)
2180!> W(iτ) <- M^-1 W(iτ) M^-1 (fold in the RI metric)
2181!> where V is the bare Coulomb matrix and M the RI metric.
2182!> \param bs_env ...
2183!> \param qs_env ...
2184!> \param mat_chi_Gamma_tau ...
2185!> \param fm_W_time ...
2186! **************************************************************************************************
2187 SUBROUTINE compute_w(bs_env, qs_env, mat_chi_Gamma_tau, fm_W_time)
2188 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2189 TYPE(qs_environment_type), POINTER :: qs_env
2190 TYPE(dbcsr_p_type), DIMENSION(:), POINTER :: mat_chi_gamma_tau
2191 TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_w_time
2192
2193 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_W'
2194
2195 INTEGER :: handle, i_t, j_w
2196 REAL(kind=dp) :: t1
2197 TYPE(cp_fm_type) :: fm_m_inv_v_sqrt, fm_v, fm_v_sqrt
2198
2199 CALL timeset(routinen, handle)
2200
2201 t1 = m_walltime()
2202
2203 CALL create_fm_w_mic_time(bs_env, fm_w_time)
2204
2205 ! 1. Allocate V and M matrices
2206 CALL cp_fm_create(fm_v, bs_env%fm_RI_RI%matrix_struct)
2207 CALL cp_fm_create(fm_v_sqrt, bs_env%fm_RI_RI%matrix_struct)
2208 CALL cp_fm_create(fm_m_inv_v_sqrt, bs_env%fm_RI_RI%matrix_struct)
2209
2210 ! Compute V and M^-1 * V^0.5
2211 CALL compute_v_minvvsqrt(bs_env, qs_env, fm_v, fm_v_sqrt, fm_m_inv_v_sqrt)
2212
2213 ! 2. Loop over frequencies
2214 DO j_w = 1, bs_env%num_time_freq_points
2215 ! Fourier transformation of χ_PQ(iτ) to χ_PQ(iω_j)
2216 CALL compute_fm_chi_gamma_freq(bs_env, bs_env%fm_chi_Gamma_freq, j_w, mat_chi_gamma_tau)
2217
2218 ! ε(iω_j) = Id - V^0.5*M^-1*χ(iω_j)*M^-1*V^0.5
2219 ! W(iω_j) = V^0.5*(ε^-1(iω_j)-Id)*V^0.5
2220 CALL compute_fm_w_freq(bs_env, bs_env%fm_chi_Gamma_freq, fm_v_sqrt, &
2221 fm_m_inv_v_sqrt, bs_env%fm_W_MIC_freq)
2222
2223 ! Fourier transform from W_PQ^MIC(iω_j) to W_PQ^MIC(iτ)
2224 CALL fourier_transform_w_to_t(bs_env, fm_w_time, bs_env%fm_W_MIC_freq, j_w)
2225 END DO
2226
2227 ! M^-1(k=0) W^MIC(iτ) M^-1(k=0) -> fm_W_time
2228 CALL fm_contract_aba(bs_env%fm_Minv_Gamma, fm_w_time)
2229
2230 IF (bs_env%unit_nr > 0) THEN
2231 WRITE (bs_env%unit_nr, '(T2,A,T58,A,F7.1,A)') &
2232 'Computed W(iτ),', ' Execution time', m_walltime() - t1, ' s'
2233 END IF
2234
2235 CALL dbcsr_deallocate_matrix_set(mat_chi_gamma_tau)
2236
2237 ! Cleanup
2238 CALL cp_fm_release(fm_v)
2239 CALL cp_fm_release(fm_v_sqrt)
2240 CALL cp_fm_release(fm_m_inv_v_sqrt)
2241
2242 ! Marek : Fourier transform W^MIC(itau) back to get it at a specific im.frequency point - iomega = 0
2243 IF (bs_env%rtp_method == rtp_method_bse) THEN
2244 t1 = m_walltime()
2245 CALL cp_fm_create(bs_env%fm_W_MIC_freq_zero, bs_env%fm_W_MIC_freq%matrix_struct)
2246 ! Set to zero
2247 CALL cp_fm_set_all(bs_env%fm_W_MIC_freq_zero, 0.0_dp)
2248 ! Sum over all times
2249 DO i_t = 1, bs_env%num_time_freq_points
2250 ! Add the relevant structure with correct weight
2251 CALL cp_fm_scale_and_add(1.0_dp, bs_env%fm_W_MIC_freq_zero, &
2252 bs_env%time_frequency_grid%time_weights_at_zero_frequency(i_t), fm_w_time(i_t))
2253 END DO
2254 ! Done, save to file
2255 CALL fm_write(bs_env%fm_W_MIC_freq_zero, 0, "W_freq_rtp", qs_env)
2256 ! Report calculation
2257 IF (bs_env%unit_nr > 0) THEN
2258 WRITE (bs_env%unit_nr, '(T2,A,T57,A,F7.1,A)') &
2259 'Computed W(0),', ' Execution time', m_walltime() - t1, ' s'
2260 END IF
2261 END IF
2262
2263 IF (bs_env%unit_nr > 0) WRITE (bs_env%unit_nr, '(A)') ' '
2264
2265 CALL timestop(handle)
2266
2267 END SUBROUTINE compute_w
2268
2269! **************************************************************************************************
2270!> \brief Computes V, V^0.5, and M^-1 V^0.5 for the RI-RS dielectric function.
2271!> The Coulomb matrix V is constructed by the RI-RS k-point path. The inverse metric
2272!> M^-1(k=0) is precomputed once in gw_utils and stored in bs_env.
2273!> \param bs_env ...
2274!> \param qs_env ...
2275!> \param fm_V Coulomb matrix V(k=0)
2276!> \param fm_V_sqrt symmetric factor V^0.5
2277!> \param fm_Minv_Vsqrt product M^-1(k=0) V^0.5
2278! **************************************************************************************************
2279 SUBROUTINE compute_v_minvvsqrt(bs_env, qs_env, fm_V, fm_V_sqrt, fm_Minv_Vsqrt)
2280 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2281 TYPE(qs_environment_type), POINTER :: qs_env
2282 TYPE(cp_fm_type), INTENT(INOUT) :: fm_v, fm_v_sqrt, fm_minv_vsqrt
2283
2284 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_V_MinvVsqrt'
2285
2286 INTEGER :: handle
2287 TYPE(atomic_kind_type), DIMENSION(:), POINTER :: atomic_kind_set
2288 TYPE(cell_type), POINTER :: cell
2289 TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: mat_v_kp
2290 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
2291 TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kind_set
2292
2293 CALL timeset(routinen, handle)
2294
2295 IF (bs_env%auto_ri%enabled) THEN
2296 ! -------------------------------------------------------------------
2297 ! 1a. The optimized AB functions span two atoms, so their previously transformed
2298 ! Coulomb matrix cannot be rebuilt by the atom-local k-point integral routine.
2299 ! -------------------------------------------------------------------
2300 CALL cp_fm_to_fm(bs_env%auto_ri%V_pq, fm_v)
2301 ELSE
2302 ! -------------------------------------------------------------------
2303 ! 1b. Build Coulomb Matrix V(k=0) using the kp-routine but only for ikp=1.
2304 ! -------------------------------------------------------------------
2305 CALL get_qs_env(qs_env=qs_env, particle_set=particle_set, cell=cell, &
2306 qs_kind_set=qs_kind_set)
2307 atomic_kind_set => bs_env%ri_rs%atomic_kind_set
2308
2309 ALLOCATE (mat_v_kp(1:1, 1:2))
2310 NULLIFY (mat_v_kp(1, 1)%matrix, mat_v_kp(1, 2)%matrix)
2311 ALLOCATE (mat_v_kp(1, 1)%matrix, mat_v_kp(1, 2)%matrix)
2312 CALL dbcsr_create(mat_v_kp(1, 1)%matrix, template=bs_env%mat_RI_RI%matrix)
2313 CALL dbcsr_reserve_all_blocks(mat_v_kp(1, 1)%matrix)
2314 CALL dbcsr_set(mat_v_kp(1, 1)%matrix, 0.0_dp)
2315 CALL dbcsr_create(mat_v_kp(1, 2)%matrix, template=bs_env%mat_RI_RI%matrix)
2316 CALL dbcsr_reserve_all_blocks(mat_v_kp(1, 2)%matrix)
2317 ! The dummy imaginary part is required only by the k-point routine interface.
2318 CALL dbcsr_set(mat_v_kp(1, 2)%matrix, 0.0_dp)
2319
2320 bs_env%kpoints_chi_eps_W%nkp_grid = bs_env%nkp_grid_chi_eps_W_orig
2321 CALL build_2c_coulomb_matrix_kp(mat_v_kp, bs_env%kpoints_chi_eps_W, "RI_AUX", cell, &
2322 particle_set, qs_kind_set, atomic_kind_set, &
2323 bs_env%size_lattice_sum_V, operator_coulomb, 1, 1)
2324 CALL copy_dbcsr_to_fm(mat_v_kp(1, 1)%matrix, fm_v)
2325
2326 CALL dbcsr_deallocate_matrix(mat_v_kp(1, 1)%matrix)
2327 CALL dbcsr_deallocate_matrix(mat_v_kp(1, 2)%matrix)
2328 DEALLOCATE (mat_v_kp)
2329 END IF
2330
2331 ! -----------------------------------------------------------------------
2332 ! 2. V -> V^0.5.
2333 ! -----------------------------------------------------------------------
2334 CALL fm_sqrt(fm_v, fm_v_sqrt, bs_env%eps_eigval_mat_RI, bs_env%unit_nr)
2335
2336 ! -----------------------------------------------------------------------
2337 ! 3. M^-1(k=0) V^0.5.
2338 ! -----------------------------------------------------------------------
2339 CALL parallel_gemm("N", "T", bs_env%n_RI, bs_env%n_RI, bs_env%n_RI, 1.0_dp, &
2340 bs_env%fm_Minv_Gamma, fm_v_sqrt, 0.0_dp, fm_minv_vsqrt)
2341
2342 CALL timestop(handle)
2343
2344 END SUBROUTINE compute_v_minvvsqrt
2345
2346! **************************************************************************************************
2347!> \brief Computes the screened interaction at one imaginary frequency:
2348!> ε(iω_j) = Id - (M^-1 V^0.5)^T χ(iω_j) (M^-1 V^0.5)
2349!> W(iω_j) = V^0.5^T ( ε^-1(iω_j) - Id ) V^0.5
2350!> ε is inverted via Cholesky; if that fails due to conditioning, via
2351!> eigendecomposition (cp_fm_power) with eigenvalue filtering.
2352!> \param bs_env ...
2353!> \param fm_chi_freq_j ...
2354!> \param fm_V_sqrt ...
2355!> \param fm_Minv_Vsqrt ...
2356!> \param fm_W_freq_j ...
2357! **************************************************************************************************
2358 SUBROUTINE compute_fm_w_freq(bs_env, fm_chi_freq_j, fm_V_sqrt, fm_Minv_Vsqrt, fm_W_freq_j)
2359 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2360 TYPE(cp_fm_type), INTENT(IN) :: fm_chi_freq_j, fm_v_sqrt, fm_minv_vsqrt
2361 TYPE(cp_fm_type), INTENT(INOUT) :: fm_w_freq_j
2362
2363 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_fm_W_freq'
2364
2365 INTEGER :: handle, n_ri
2366 TYPE(cp_fm_type) :: fm_eps_freq_j, fm_work
2367
2368 CALL timeset(routinen, handle)
2369
2370 n_ri = bs_env%n_RI
2371
2372 CALL cp_fm_create(fm_eps_freq_j, fm_chi_freq_j%matrix_struct)
2373 CALL cp_fm_create(fm_work, fm_chi_freq_j%matrix_struct)
2374
2375 ! -----------------------------------------------------------------------
2376 ! 1. ε(iω_j) = Id - (M^-1 * V^0.5)^T * χ(iω_j) * (M^-1 * V^0.5)
2377 ! -----------------------------------------------------------------------
2378 ! work = χ(iω_j) * (M^-1 * V^0.5)
2379 CALL parallel_gemm('N', 'N', n_ri, n_ri, n_ri, 1.0_dp, &
2380 fm_chi_freq_j, fm_minv_vsqrt, 0.0_dp, fm_work)
2381
2382 ! eps_work = (M^-1 * V^0.5)^T * work
2383 CALL parallel_gemm('T', 'N', n_ri, n_ri, n_ri, 1.0_dp, &
2384 fm_minv_vsqrt, fm_work, 0.0_dp, fm_eps_freq_j)
2385
2386 ! ε(iω_j) = Id - eps_work --> -eps_work + Id
2387 CALL fm_add_on_diag(fm_eps_freq_j, 1.0_dp)
2388
2389 ! Force perfect symmetry before Cholesky to avoid info != 0 due to GEMM noise
2390 CALL cp_fm_uplo_to_full(fm_eps_freq_j, fm_work)
2391
2392 ! -----------------------------------------------------------------------
2393 ! 2. W(iω_j) = V^0.5^T * (ε^-1(iω_j) - Id) * V^0.5
2394 ! -----------------------------------------------------------------------
2395
2396 ! a) Invert ε by Cholesky decomposition or, if that fails, by diagonalization.
2397 CALL fm_invert(fm_eps_freq_j, bs_env%eps_eigval_mat_RI, bs_env%unit_nr)
2398
2399 ! b) ε^-1(iω_j) - Id
2400 CALL fm_add_on_diag(fm_eps_freq_j, -1.0_dp)
2401
2402 ! c) W(iω_j) = V^0.5^T * (ε^-1(iω_j) - Id) * V^0.5
2403 CALL fm_contract_aba(fm_v_sqrt, fm_eps_freq_j, fm_w_freq_j)
2404
2405 ! Cleanup
2406 CALL cp_fm_release(fm_work)
2407 CALL cp_fm_release(fm_eps_freq_j)
2408
2409 CALL timestop(handle)
2410
2411 END SUBROUTINE compute_fm_w_freq
2412
2413! **************************************************************************************************
2414!> \brief Adds a real scalar value to the diagonal of a real full matrix
2415!> \param fm ...
2416!> \param alpha ...
2417! **************************************************************************************************
2418 SUBROUTINE fm_add_on_diag(fm, alpha)
2419 TYPE(cp_fm_type), INTENT(INOUT) :: fm
2420 REAL(kind=dp), INTENT(IN) :: alpha
2421
2422 CHARACTER(LEN=*), PARAMETER :: routinen = 'fm_add_on_diag'
2423
2424 INTEGER :: handle, i_global, i_row, j_col, &
2425 j_global, ncol_local, nrow_local
2426 INTEGER, DIMENSION(:), POINTER :: col_indices, row_indices
2427
2428 CALL timeset(routinen, handle)
2429
2430 CALL cp_fm_get_info(matrix=fm, &
2431 nrow_local=nrow_local, &
2432 ncol_local=ncol_local, &
2433 row_indices=row_indices, &
2434 col_indices=col_indices)
2435
2436 DO j_col = 1, ncol_local
2437 j_global = col_indices(j_col)
2438 DO i_row = 1, nrow_local
2439 i_global = row_indices(i_row)
2440 IF (j_global == i_global) THEN
2441 fm%local_data(i_row, j_col) = fm%local_data(i_row, j_col) + alpha
2442 END IF
2443 END DO
2444 END DO
2445
2446 CALL timestop(handle)
2447
2448 END SUBROUTINE fm_add_on_diag
2449
2450! **************************************************************************************************
2451!> \brief Computes the exact-exchange part of the GW self-energy:
2452!> D_μν = Σ_n^occ C_μn C_νn (density matrix = G^occ at τ=0)
2453!> V^tr_PQ = M^-1 (P|Q)_trunc M^-1 (truncated Coulomb, RI basis)
2454!> Σ^x_λσ(k=0) = -Σ_ll' ϕ_λ(r_l) [ (ϕ D ϕ^T)_ll' ∘ (Z V^tr Z^T)_ll' ] ϕ_σ(r_l')
2455!> \param bs_env ...
2456!> \param qs_env ...
2457!> \param mat_phi_mu_l ...
2458!> \param mat_Z_lP ...
2459!> \param fm_Sigma_x_Gamma ...
2460! **************************************************************************************************
2461 SUBROUTINE compute_sigma_x(bs_env, qs_env, mat_phi_mu_l, mat_Z_lP, fm_Sigma_x_Gamma)
2462
2463 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2464 TYPE(qs_environment_type), POINTER :: qs_env
2465 TYPE(dbcsr_type), INTENT(INOUT) :: mat_phi_mu_l, mat_z_lp
2466 TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_sigma_x_gamma
2467
2468 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_Sigma_x'
2469
2470 INTEGER :: handle, ispin
2471 INTEGER, ALLOCATABLE, DIMENSION(:) :: pan_first, pan_last
2472 INTEGER, DIMENSION(:), POINTER :: blk_aux, dist_row_aux
2473 REAL(kind=dp) :: t1
2474 TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :) :: fm_vtr_gamma
2475 TYPE(dbcsr_distribution_type) :: dist_aux_aux
2476 TYPE(dbcsr_type) :: mat_sigma_x_gamma, matrix_d_ao, &
2477 matrix_v_aux
2478
2479 CALL timeset(routinen, handle)
2480
2481 t1 = m_walltime()
2482
2483 ALLOCATE (fm_sigma_x_gamma(bs_env%n_spin))
2484 DO ispin = 1, bs_env%n_spin
2485 CALL cp_fm_create(fm_sigma_x_gamma(ispin), bs_env%fm_s_Gamma%matrix_struct)
2486 END DO
2487
2488 CALL dbcsr_create(mat_sigma_x_gamma, template=bs_env%mat_ao_ao%matrix)
2489
2490 CALL resolve_grid_panels(bs_env, mat_phi_mu_l, pan_first, pan_last)
2491
2492 ! =========================================================================
2493 ! 1. COMPUTE V^tr_PQ (RI x RI)
2494 ! =========================================================================
2495 CALL setup_square_topology(mat_z_lp, dist_aux_aux, blk_aux, dist_row_aux)
2496
2497 IF (bs_env%auto_ri%enabled) THEN
2498 ALLOCATE (fm_vtr_gamma(1, 1))
2499 CALL cp_fm_create(fm_vtr_gamma(1, 1), bs_env%fm_RI_RI%matrix_struct)
2500 CALL cp_fm_to_fm(bs_env%auto_ri%V_pq, fm_vtr_gamma(1, 1))
2501 ELSE
2502 CALL ri_2c_integral_mat(qs_env, fm_vtr_gamma, bs_env%fm_RI_RI%matrix_struct, bs_env%n_RI, &
2503 bs_env%trunc_coulomb)
2504 END IF
2505
2506 ! M^-1(k=0) V^tr(τ) M^-1(k=0) -> fm_Vtr_Gamma
2507 CALL fm_contract_aba(bs_env%fm_Minv_Gamma, fm_vtr_gamma(:, 1))
2508
2509 CALL dbcsr_create(matrix_v_aux, "V_aux", dist_aux_aux, dbcsr_type_no_symmetry, blk_aux, blk_aux)
2510 ! Optional CUTOFF_RADIUS_G_W operator truncation + filter (see build_G_ao)
2511 IF (bs_env%ri_rs%cutoff_radius_g_w > 0.0_dp .AND. ALLOCATED(bs_env%ri_rs%atom_centers)) THEN
2512 CALL reserve_blocks_within_radius(matrix_v_aux, bs_env%ri_rs%atom_centers, &
2513 bs_env%ri_rs%cutoff_radius_g_w)
2514 CALL copy_fm_to_dbcsr(fm_vtr_gamma(1, 1), matrix_v_aux, keep_sparsity=.true.)
2515 ELSE
2516 CALL copy_fm_to_dbcsr(fm_vtr_gamma(1, 1), matrix_v_aux, keep_sparsity=.false.)
2517 END IF
2518 CALL dbcsr_filter(matrix_v_aux, bs_env%eps_filter)
2519
2520 ! =========================================================================
2521 ! 2. SPIN LOOP FOR EXACT EXCHANGE
2522 ! Σ^x_λσ = -Σ_ll' ϕ_λ(r_l) ( D_ll' V^tr_ll' ) ϕ_σ(r_l')
2523 ! = -ϕ^T ( (ϕ D ϕ^T) ∘ (Z V^tr Z^T) ) ϕ
2524 ! =========================================================================
2525 DO ispin = 1, bs_env%n_spin
2526
2527 ! AO-space density matrix D_µν = G^occ at τ = 0
2528 CALL build_g_ao(bs_env, 0.0_dp, ispin, .true., .false., mat_phi_mu_l, matrix_d_ao)
2529
2530 CALL contract_grid_panels(l_a=mat_phi_mu_l, m_a=matrix_d_ao, &
2531 l_b=mat_z_lp, m_b=matrix_v_aux, &
2532 l_out=mat_phi_mu_l, mat_out=mat_sigma_x_gamma, &
2533 scale=1.0_dp, eps=bs_env%eps_filter, &
2534 para_env=bs_env%para_env, &
2535 pan_first=pan_first, pan_last=pan_last, &
2536 lb_eq_la=.false., lout_eq_la=.true., zero_out=.true., &
2537 keep_sparsity=bs_env%ri_rs%keep_sparsity_rirs, &
2538 centroids=bs_env%ri_rs%chunk_centroids, &
2539 cutoff=bs_env%ri_rs%cutoff_radius_v_w)
2540 CALL dbcsr_scale(mat_sigma_x_gamma, -1.0_dp)
2541
2542 CALL dbcsr_release(matrix_d_ao)
2543
2544 ! Data I/O and Export to CP2K Full Matrices
2545 CALL copy_dbcsr_to_fm(mat_sigma_x_gamma, fm_sigma_x_gamma(ispin))
2546
2547 END DO ! ispin
2548
2549 IF (bs_env%unit_nr > 0) THEN
2550 WRITE (bs_env%unit_nr, '(T2,A,T58,A,F7.1,A)') &
2551 'Computed Σ^x(k=0),', ' Execution time', m_walltime() - t1, ' s'
2552 WRITE (bs_env%unit_nr, '(A)') ' '
2553 END IF
2554
2555 ! =========================================================================
2556 ! 3. CLEANUP
2557 ! =========================================================================
2558 CALL dbcsr_release(matrix_v_aux)
2559 CALL dbcsr_release(mat_sigma_x_gamma)
2560 CALL release_square_topology(dist=dist_aux_aux, mapped_dist=dist_row_aux)
2561
2562 CALL cp_fm_release(fm_vtr_gamma)
2563
2564 CALL timestop(handle)
2565
2566 END SUBROUTINE compute_sigma_x
2567
2568! **************************************************************************************************
2569!> \brief Computes the correlation part of the GW self-energy on the imaginary-time grid:
2570!> Σ^c_λσ(iτ<0) = -Σ_ll' ϕ_λ(r_l) [ (ϕ G^occ ϕ^T)_ll' ∘ (Z W^MIC Z^T)_ll' ] ϕ_σ(r_l')
2571!> Σ^c_λσ(iτ>0) = +Σ_ll' ϕ_λ(r_l) [ (ϕ G^vir ϕ^T)_ll' ∘ (Z W^MIC Z^T)_ll' ] ϕ_σ(r_l')
2572!> \param bs_env ...
2573!> \param fm_W_time ...
2574!> \param mat_phi_mu_l ...
2575!> \param mat_Z_lP ...
2576!> \param fm_Sigma_c_Gamma_time ...
2577! **************************************************************************************************
2578 SUBROUTINE compute_sigma_c(bs_env, fm_W_time, mat_phi_mu_l, mat_Z_lP, fm_Sigma_c_Gamma_time)
2579
2580 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2581 TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_w_time
2582 TYPE(dbcsr_type), INTENT(INOUT) :: mat_phi_mu_l, mat_z_lp
2583 TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :, :) :: fm_sigma_c_gamma_time
2584
2585 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_Sigma_c'
2586
2587 INTEGER :: handle, i_t, ispin
2588 INTEGER, ALLOCATABLE, DIMENSION(:) :: pan_first, pan_last
2589 INTEGER, DIMENSION(:), POINTER :: blk_aux, dist_row_aux
2590 REAL(kind=dp) :: t1, tau
2591 TYPE(dbcsr_distribution_type) :: dist_aux_aux
2592 TYPE(dbcsr_p_type), DIMENSION(:, :), POINTER :: mat_sigma_neg_tau, mat_sigma_pos_tau
2593 TYPE(dbcsr_type) :: matrix_g_occ_ao, matrix_g_vir_ao, &
2594 matrix_w_aux
2595
2596 CALL timeset(routinen, handle)
2597
2598 ! =========================================================================
2599 ! 1. SETUP AUXILIARY TOPOLOGY AND PRE-ALLOCATE OUTPUT ARRAYS
2600 ! =========================================================================
2601 CALL setup_square_topology(mat_z_lp, dist_aux_aux, blk_aux, dist_row_aux)
2602
2603 CALL resolve_grid_panels(bs_env, mat_phi_mu_l, pan_first, pan_last)
2604
2605 ! Pre-allocate local DBCSR matrices to act as targets for final output
2606 NULLIFY (mat_sigma_neg_tau, mat_sigma_pos_tau)
2607 ALLOCATE (mat_sigma_neg_tau(bs_env%num_time_freq_points, bs_env%n_spin))
2608 ALLOCATE (mat_sigma_pos_tau(bs_env%num_time_freq_points, bs_env%n_spin))
2609
2610 DO i_t = 1, bs_env%num_time_freq_points
2611 DO ispin = 1, bs_env%n_spin
2612 ALLOCATE (mat_sigma_neg_tau(i_t, ispin)%matrix)
2613 ALLOCATE (mat_sigma_pos_tau(i_t, ispin)%matrix)
2614 CALL dbcsr_create(mat_sigma_neg_tau(i_t, ispin)%matrix, template=bs_env%mat_ao_ao%matrix)
2615 CALL dbcsr_create(mat_sigma_pos_tau(i_t, ispin)%matrix, template=bs_env%mat_ao_ao%matrix)
2616 END DO
2617 END DO
2618
2619 ! =========================================================================
2620 ! 2. IMAGINARY TIME LOOP
2621 ! Σ^c_neg_λσ(iτ) = -ϕ^T ( (ϕ G^occ ϕ^T) ∘ (Z W^MIC Z^T) ) ϕ
2622 ! Σ^c_pos_λσ(iτ) = ϕ^T ( (ϕ G^vir ϕ^T) ∘ (Z W^MIC Z^T) ) ϕ
2623 ! =========================================================================
2624 DO i_t = 1, bs_env%num_time_freq_points
2625 tau = bs_env%time_frequency_grid%imaginary_time(i_t)
2626
2627 CALL dbcsr_create(matrix_w_aux, "W_aux", dist_aux_aux, dbcsr_type_no_symmetry, &
2628 blk_aux, blk_aux)
2629 IF (bs_env%ri_rs%cutoff_radius_g_w > 0.0_dp .AND. ALLOCATED(bs_env%ri_rs%atom_centers)) THEN
2630 CALL reserve_blocks_within_radius(matrix_w_aux, bs_env%ri_rs%atom_centers, &
2631 bs_env%ri_rs%cutoff_radius_g_w)
2632 CALL copy_fm_to_dbcsr(fm_w_time(i_t), matrix_w_aux, keep_sparsity=.true.)
2633 ELSE
2634 CALL copy_fm_to_dbcsr(fm_w_time(i_t), matrix_w_aux, keep_sparsity=.false.)
2635 END IF
2636 CALL dbcsr_filter(matrix_w_aux, bs_env%eps_filter)
2637
2638 DO ispin = 1, bs_env%n_spin
2639 t1 = m_walltime()
2640
2641 ! AO-space Green's functions G^occ_µν, G^vir_µν (dense AO x AO, small)
2642 CALL build_g_ao(bs_env, tau, ispin, .true., .false., mat_phi_mu_l, matrix_g_occ_ao)
2643 CALL build_g_ao(bs_env, tau, ispin, .false., .true., mat_phi_mu_l, matrix_g_vir_ao)
2644
2645 ! Σ^c_neg and Σ^c_pos in a single panel loop: W_pan = Z_panel × W × Z^T built once
2646 CALL contract_grid_panels_sigma_c(mat_phi=mat_phi_mu_l, mat_z=mat_z_lp, &
2647 mat_g_occ_ao=matrix_g_occ_ao, &
2648 mat_g_vir_ao=matrix_g_vir_ao, &
2649 mat_w_aux=matrix_w_aux, &
2650 mat_sigma_neg=mat_sigma_neg_tau(i_t, ispin)%matrix, &
2651 mat_sigma_pos=mat_sigma_pos_tau(i_t, ispin)%matrix, &
2652 eps=bs_env%eps_filter, &
2653 para_env=bs_env%para_env, &
2654 pan_first=pan_first, pan_last=pan_last, &
2655 keep_sparsity=bs_env%ri_rs%keep_sparsity_rirs, &
2656 centroids=bs_env%ri_rs%chunk_centroids, &
2657 cutoff=bs_env%ri_rs%cutoff_radius_v_w)
2658 CALL dbcsr_scale(mat_sigma_neg_tau(i_t, ispin)%matrix, -1.0_dp)
2659
2660 CALL dbcsr_release(matrix_g_occ_ao)
2661 CALL dbcsr_release(matrix_g_vir_ao)
2662
2663 IF (bs_env%unit_nr > 0) THEN
2664 WRITE (bs_env%unit_nr, '(T2,A,I15,A,I3,A,F7.1,A)') &
2665 'Computed Σ^c(iτ) for time point', i_t, ' /', bs_env%num_time_freq_points, &
2666 ', Execution time', m_walltime() - t1, ' s'
2667 END IF
2668
2669 END DO ! ispin
2670
2671 CALL dbcsr_release(matrix_w_aux)
2672
2673 END DO ! i_t
2674
2675 ! -------------------------------------------------------------------------
2676 ! 3. FINALIZE AND CLEANUP
2677 ! -------------------------------------------------------------------------
2678 CALL fill_fm_sigma_c_gamma_time(fm_sigma_c_gamma_time, bs_env, &
2679 mat_sigma_pos_tau, mat_sigma_neg_tau)
2680
2681 ! fm_W_time and the scratch files are released by the caller: in an evGW0 cycle this
2682 ! routine is entered once per iteration and both have to survive until it is done.
2683 CALL dbcsr_deallocate_matrix_set(mat_sigma_neg_tau)
2684 CALL dbcsr_deallocate_matrix_set(mat_sigma_pos_tau)
2685
2686 CALL release_square_topology(dist=dist_aux_aux, mapped_dist=dist_row_aux)
2687
2688 CALL timestop(handle)
2689
2690 END SUBROUTINE compute_sigma_c
2691
2692! **************************************************************************************************
2693!> \brief Builds the DBCSR distribution.
2694!> \param matrix_template ...
2695!> \param square_dist ...
2696!> \param blk_sizes ...
2697!> \param mapped_dist ...
2698! **************************************************************************************************
2699 SUBROUTINE setup_square_topology(matrix_template, square_dist, blk_sizes, mapped_dist)
2700
2701 TYPE(dbcsr_type), INTENT(IN) :: matrix_template
2702 TYPE(dbcsr_distribution_type), INTENT(OUT) :: square_dist
2703 INTEGER, DIMENSION(:), INTENT(OUT), POINTER :: blk_sizes, mapped_dist
2704
2705 CHARACTER(LEN=*), PARAMETER :: routinen = 'setup_square_topology'
2706
2707 INTEGER :: handle, i, nprows
2708 INTEGER, DIMENSION(:), POINTER :: col_blk, col_dist
2709 TYPE(dbcsr_distribution_type) :: dist_template
2710
2711 CALL timeset(routinen, handle)
2712
2713 CALL dbcsr_get_info(matrix_template, distribution=dist_template, col_blk_size=col_blk)
2714 CALL dbcsr_distribution_get(dist_template, col_dist=col_dist, nprows=nprows)
2715
2716 blk_sizes => col_blk
2717 ALLOCATE (mapped_dist(SIZE(blk_sizes)))
2718 DO i = 1, SIZE(blk_sizes)
2719 mapped_dist(i) = mod(i - 1, nprows)
2720 END DO
2721 CALL dbcsr_distribution_new(square_dist, template=dist_template, &
2722 row_dist=mapped_dist, col_dist=col_dist)
2723
2724 CALL timestop(handle)
2725
2726 END SUBROUTINE setup_square_topology
2727
2728! **************************************************************************************************
2729!> \brief Releases a distribution created by setup_square_topology.
2730!> \param dist ...
2731!> \param mapped_dist ...
2732! **************************************************************************************************
2733 SUBROUTINE release_square_topology(dist, mapped_dist)
2734
2735 TYPE(dbcsr_distribution_type), INTENT(INOUT) :: dist
2736 INTEGER, DIMENSION(:), INTENT(INOUT), POINTER :: mapped_dist
2737
2739 IF (ASSOCIATED(mapped_dist)) THEN
2740 DEALLOCATE (mapped_dist)
2741 NULLIFY (mapped_dist)
2742 END IF
2743
2744 END SUBROUTINE release_square_topology
2745
2746! **************************************************************************************************
2747!> \brief Σ^c_λσ(iτ) -> Σ^c_nn(ϵ) and the quasi-particle levels of the non-periodic RI-RS path,
2748!> ϵ_n^GW = ϵ_n^DFT + Σ^c_nn(ϵ_n^GW) + Σ^x_nn - v^xc_nn.
2749!> \param bs_env ...
2750!> \param fm_Sigma_x_Gamma ...
2751!> \param fm_Sigma_c_Gamma_time ...
2752! **************************************************************************************************
2753 SUBROUTINE compute_qp_energies(bs_env, fm_Sigma_x_Gamma, fm_Sigma_c_Gamma_time)
2754
2755 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2756 TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_sigma_x_gamma
2757 TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :, :) :: fm_sigma_c_gamma_time
2758
2759 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_QP_energies'
2760
2761 INTEGER :: handle, ispin, j_t
2762 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: sigma_x_n, v_xc_n
2763 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :) :: sigma_c_n_freq, sigma_c_n_time
2764 TYPE(cp_fm_type) :: fm_ks, fm_mos, fm_s, fm_work
2765
2766 CALL timeset(routinen, handle)
2767
2768 CALL cp_fm_create(fm_ks, bs_env%fm_s_Gamma%matrix_struct)
2769 CALL cp_fm_create(fm_s, bs_env%fm_s_Gamma%matrix_struct)
2770 CALL cp_fm_create(fm_mos, bs_env%fm_s_Gamma%matrix_struct)
2771 CALL cp_fm_create(fm_work, bs_env%fm_s_Gamma%matrix_struct)
2772
2773 ALLOCATE (v_xc_n(bs_env%n_ao), sigma_x_n(bs_env%n_ao))
2774 ALLOCATE (sigma_c_n_time(bs_env%n_ao, bs_env%num_time_freq_points, 2))
2775 ALLOCATE (sigma_c_n_freq(bs_env%n_ao, bs_env%num_time_freq_points, 2))
2776
2777 DO ispin = 1, bs_env%n_spin
2778
2779 ! 1. Roothaan-Hall H^KS_µν C_νn = S_µν C_νn ϵ_n
2780 CALL cp_fm_to_fm(bs_env%fm_ks_Gamma(ispin), fm_ks)
2781 CALL cp_fm_to_fm(bs_env%fm_s_Gamma, fm_s)
2782 CALL cp_fm_geeig(fm_ks, fm_s, fm_mos, bs_env%eigenval_scf(:, 1, ispin), fm_work)
2783
2784 ! 2. v^xc_µν -> v^xc_nn and Σ^x_µν -> Σ^x_nn
2785 CALL to_gamma_and_mo_real(v_xc_n, bs_env%fm_V_xc_Gamma(ispin), fm_mos)
2786 CALL to_gamma_and_mo_real(sigma_x_n, fm_sigma_x_gamma(ispin), fm_mos)
2787
2788 ! 3. Σ^c_µν(+/-i|τ_j|) -> Σ^c_nn(+/-i|τ_j|)
2789 DO j_t = 1, bs_env%num_time_freq_points
2790 CALL to_gamma_and_mo_real(sigma_c_n_time(:, j_t, 1), &
2791 fm_sigma_c_gamma_time(j_t, 1, ispin), fm_mos)
2792 CALL to_gamma_and_mo_real(sigma_c_n_time(:, j_t, 2), &
2793 fm_sigma_c_gamma_time(j_t, 2, ispin), fm_mos)
2794 END DO
2795
2796 ! 4. Σ^c_nn(iτ) -> Σ^c_nn(iω)
2797 CALL time_to_freq(bs_env, sigma_c_n_time, sigma_c_n_freq, ispin)
2798
2799 ! 5. Analytic continuation Σ^c_nn(iω) -> Σ^c_nn(ϵ) and the QP levels
2800 CALL analyt_conti_and_print(bs_env, sigma_c_n_freq, sigma_x_n, v_xc_n, &
2801 bs_env%eigenval_scf(:, 1, ispin), 1, ispin)
2802
2803 END DO ! ispin
2804
2805 CALL get_all_vbm_cbm_bandgaps(bs_env)
2806
2807 IF (bs_env%gw_flavour == g0w0) CALL cp_fm_release(fm_sigma_x_gamma)
2808 CALL cp_fm_release(fm_sigma_c_gamma_time)
2809
2810 CALL cp_fm_release(fm_ks)
2811 CALL cp_fm_release(fm_s)
2812 CALL cp_fm_release(fm_mos)
2813 CALL cp_fm_release(fm_work)
2814
2815 CALL timestop(handle)
2816
2817 END SUBROUTINE compute_qp_energies
2818
2819! **************************************************************************************************
2820!> \brief AO -> MO transform of a Γ-point matrix
2821!> \param array_n ...
2822!> \param fm_Gamma ...
2823!> \param fm_mos ...
2824! **************************************************************************************************
2825 SUBROUTINE to_gamma_and_mo_real(array_n, fm_Gamma, fm_mos)
2826
2827 REAL(kind=dp), DIMENSION(:) :: array_n
2828 TYPE(cp_fm_type) :: fm_gamma, fm_mos
2829
2830 CHARACTER(LEN=*), PARAMETER :: routinen = 'to_Gamma_and_mo_real'
2831
2832 INTEGER :: handle
2833 TYPE(cp_fm_type) :: fm_mo
2834
2835 CALL timeset(routinen, handle)
2836
2837 CALL cp_fm_create(fm_mo, fm_gamma%matrix_struct)
2838
2839 ! A_nn' = Σ_μν C_μn A_μν C_νn'
2840 CALL fm_contract_aba(fm_mos, fm_gamma, fm_mo)
2841
2842 CALL cp_fm_get_diag(fm_mo, array_n)
2843
2844 CALL cp_fm_release(fm_mo)
2845
2846 CALL timestop(handle)
2847
2848 END SUBROUTINE to_gamma_and_mo_real
2849
2850! **************************************************************************************************
2851!> \brief Counts the AO functions whose radial support intersects an RI fitting sphere.
2852!> \param bs_env ...
2853!> \param atom_P Atom at the center of the RI fitting sphere
2854!> \param cutoff_ri Radius of the RI fitting sphere
2855!> \param n_ao_used Number of intersecting AO functions
2856! **************************************************************************************************
2857 SUBROUTINE get_n_ao_in_sphere(bs_env, atom_P, cutoff_ri, n_ao_used)
2858
2859 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2860 INTEGER, INTENT(IN) :: atom_p
2861 REAL(kind=dp), INTENT(IN) :: cutoff_ri
2862 INTEGER, INTENT(OUT) :: n_ao_used
2863
2864 CHARACTER(LEN=*), PARAMETER :: routinen = 'get_n_ao_in_sphere'
2865
2866 INTEGER :: handle, ri_atom
2867
2868 CALL timeset(routinen, handle)
2869
2870 n_ao_used = 0
2871 DO ri_atom = 1, bs_env%n_atom
2872 IF (norm2(bs_env%ri_rs%particle_set(ri_atom)%r(:) - &
2873 bs_env%ri_rs%particle_set(atom_p)%r(:)) > &
2874 bs_env%ri_rs%radius_ao_per_atom(ri_atom) + cutoff_ri) cycle
2875 n_ao_used = n_ao_used + bs_env%i_ao_end_from_atom(ri_atom) - &
2876 bs_env%i_ao_start_from_atom(ri_atom) + 1
2877 END DO
2878
2879 CALL timestop(handle)
2880
2881 END SUBROUTINE get_n_ao_in_sphere
2882
2883! **************************************************************************************************
2884!> \brief Prints the percentage of non-zero elements in a distributed RI-RS matrix.
2885!> \param matrix Distributed matrix whose occupation is reported
2886!> \param label Mathematical matrix label used in the output
2887!> \param bs_env ...
2888!> \param suffix Optional text appended to the matrix label
2889! **************************************************************************************************
2890 SUBROUTINE print_matrix_occupation(matrix, label, bs_env, suffix)
2891
2892 TYPE(dbcsr_type), INTENT(IN) :: matrix
2893 CHARACTER(LEN=*), INTENT(IN) :: label
2894 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2895 CHARACTER(LEN=*), INTENT(IN), OPTIONAL :: suffix
2896
2897 CHARACTER(LEN=*), PARAMETER :: routinen = 'print_matrix_occupation'
2898
2899 CHARACTER(LEN=32) :: output_format
2900 CHARACTER(LEN=max_line_length) :: msg, output_label
2901 INTEGER :: handle, i, unicode_shift
2902 REAL(kind=dp) :: frac_2p31, max_loc, occ
2903
2904 CALL timeset(routinen, handle)
2905
2906 occ = dbcsr_get_occupation(matrix)
2907 max_loc = real(dbcsr_get_data_size(matrix), dp)
2908 CALL bs_env%para_env%max(max_loc)
2909
2910 IF (bs_env%unit_nr > 0) THEN
2911 frac_2p31 = max_loc/real(bs_env%dbcsr_msg_elem_limit, dp)
2912 output_label = 'Percentage of non-zero matrix elements in '//trim(label)
2913 IF (PRESENT(suffix)) output_label = trim(output_label)//trim(suffix)
2914 ! Fortran counts UTF-8 bytes, whereas the terminal displays each Greek letter in one
2915 ! column. Shift the absolute output tab once for every continuation byte.
2916 unicode_shift = 0
2917 DO i = 1, len_trim(output_label)
2918 IF (iand(iachar(output_label(i:i)), 192) == 128) unicode_shift = unicode_shift + 1
2919 END DO
2920 WRITE (output_format, '(A,I0,A)') '(T2,A,T', 72 + unicode_shift, ',F7.2,A)'
2921 WRITE (bs_env%unit_nr, output_format) trim(output_label), occ*100.0_dp, ' %'
2922 IF (frac_2p31 > 0.5_dp) THEN
2923 WRITE (msg, '(3A,F0.2,A)') &
2924 "The largest per-rank message of ", trim(label), " reaches ", frac_2p31, &
2925 " of the 32-bit limit that DBCSR uses for its message length. Beyond it the "// &
2926 "length overflows and multiply_cannon fails. Reduce the per-rank block size, "// &
2927 "for instance with more MPI ranks or a larger N_PANELS."
2928 cpwarn(trim(msg))
2929 END IF
2930 CALL m_flush(bs_env%unit_nr)
2931 END IF
2932
2933 CALL timestop(handle)
2934
2935 END SUBROUTINE print_matrix_occupation
2936
2937END MODULE gw_ri_rs_non_periodic
Define the atomic kind types and their sub types.
Handles all functions related to the CELL.
Definition cell_types.F:15
constants for the different operators of the 2c-integrals
integer, parameter, public operator_coulomb
subroutine, public dbcsr_distribution_release(dist)
...
integer function, public dbcsr_get_data_size(matrix)
...
subroutine, public dbcsr_scale(matrix, alpha_scalar)
...
subroutine, public dbcsr_distribution_new(dist, template, group, pgrid, row_dist, col_dist, reuse_arrays)
...
subroutine, public dbcsr_deallocate_matrix(matrix)
...
logical function, public dbcsr_iterator_blocks_left(iterator)
...
subroutine, public dbcsr_iterator_stop(iterator)
...
subroutine, public dbcsr_copy(matrix_b, matrix_a, name, keep_sparsity, keep_imaginary)
...
subroutine, public dbcsr_multiply(transa, transb, alpha, matrix_a, matrix_b, beta, matrix_c, first_row, last_row, first_column, last_column, first_k, last_k, retain_sparsity, filter_eps, flop)
...
subroutine, public dbcsr_get_info(matrix, nblkrows_total, nblkcols_total, nfullrows_total, nfullcols_total, nblkrows_local, nblkcols_local, nfullrows_local, nfullcols_local, my_prow, my_pcol, local_rows, local_cols, proc_row_dist, proc_col_dist, row_blk_size, col_blk_size, row_blk_offset, col_blk_offset, distribution, name, matrix_type, group)
...
subroutine, public dbcsr_iterator_next_block(iterator, row, column, block, block_number_argument_has_been_removed, row_size, col_size, row_offset, col_offset, transposed)
...
subroutine, public dbcsr_filter(matrix, eps)
...
real(kind=dp) function, public dbcsr_get_occupation(matrix)
...
subroutine, public dbcsr_finalize(matrix)
...
subroutine, public dbcsr_iterator_start(iterator, matrix, shared, dynamic, dynamic_byrows)
...
subroutine, public dbcsr_set(matrix, alpha)
...
subroutine, public dbcsr_release(matrix)
...
subroutine, public dbcsr_put_block(matrix, row, col, block, summation)
...
subroutine, public dbcsr_add(matrix_a, matrix_b, alpha_scalar, beta_scalar)
...
subroutine, public dbcsr_distribution_get(dist, row_dist, col_dist, nrows, ncols, has_threads, group, mynode, numnodes, nprows, npcols, myprow, mypcol, pgrid, subgroups_defined, prow_group, pcol_group)
...
subroutine, public dbcsr_reserve_all_blocks(matrix)
Reserves all blocks.
DBCSR operations in CP2K.
integer, save, public max_elements_per_block
subroutine, public copy_dbcsr_to_fm(matrix, fm, plan)
Copy a DBCSR matrix to a BLACS matrix.
subroutine, public copy_fm_to_dbcsr(fm, matrix, keep_sparsity)
Copy a BLACS matrix to a dbcsr matrix.
Basic linear algebra operations for full matrices.
subroutine, public cp_fm_scale_and_add(alpha, matrix_a, beta, matrix_b)
calc A <- alpha*A + beta*B optimized for alpha == 1.0 (just add beta*B) and beta == 0....
subroutine, public cp_fm_uplo_to_full(matrix, work, uplo)
given a triangular matrix according to uplo, computes the corresponding full matrix
used for collecting some of the diagonalization schemes available for cp_fm_type. cp_fm_power also mo...
Definition cp_fm_diag.F:17
subroutine, public cp_fm_geeig(amatrix, bmatrix, eigenvectors, eigenvalues, work)
General Eigenvalue Problem AX = BXE. Use cuSOLVERMp directly when requested and large enough; otherwi...
represent a full matrix distributed on many processors
Definition cp_fm_types.F:15
subroutine, public cp_fm_get_diag(matrix, diag)
returns the diagonal elements of a fm
subroutine, public cp_fm_get_info(matrix, name, nrow_global, ncol_global, nrow_block, ncol_block, nrow_local, ncol_local, row_indices, col_indices, local_data, context, nrow_locals, ncol_locals, matrix_struct, para_env)
returns all kind of information about the full matrix
subroutine, public cp_fm_set_all(matrix, alpha, beta)
set all elements of a matrix to the same value, and optionally the diagonal to a different one
subroutine, public cp_fm_create(matrix, matrix_struct, name, nrow, ncol, set_zero)
creates a new full matrix with the given structure
Computes the RI-RS fitting matrix Z_lP.
subroutine, public compute_z_lp(qs_env, bs_env, ri_rs_grid_points, mat_phi_mu_l, mat_z_lp)
Computes Z_lP for (1) a tabulated RI basis set or (2) an on-the-fly generated RI basis set.
Main setup file for RI-RS grids {r_l}.
subroutine, public setup_ri_rs_grid(bs_env, grid_points)
Get RI-RS grid points {r_l}, either by on-the-fly optimization or reading pretabulated atomic grids.
GW using RI-RS Approximation for molecules.
subroutine, public gw_calc_ri_rs_non_periodic(qs_env, bs_env)
GW calculation using RI-RS formalism for molecules.
subroutine, public reserve_blocks_within_radius(matrix, centers, radius)
Pre-seeds a square blocked DBCSR matrix with zero blocks only for block pairs whose centers lie withi...
subroutine, public atomic_basis_at_grid_point(bs_env, ri_rs_grid_points, mat_phi_mu_l)
Evaluates the AO basis on the RI-RS grid and stores it as the sparse DBCSR matrix ϕ_μl = ϕ_μ(r_l) (ro...
Common setup operations used by the periodic and non-periodic GW RI-RS implementations.
subroutine, public precompute_ri_rs_radii(bs_env)
Compute per-atom AO and RI basis radii from the most diffuse Gaussian primitive in the AO ("ORB") and...
subroutine, public evaluate_ao_basis_on_points(phi, grid_points, basis, source_position, cell, dphi, cutoff_squared)
Evaluate one explicitly supplied contracted Gaussian basis on Cartesian points.
Routines from paper [Graml2024].
subroutine, public compute_fm_chi_gamma_freq(bs_env, fm_chi_gamma_freq, j_w, mat_chi_gamma_tau)
...
subroutine, public fourier_transform_w_to_t(bs_env, fm_w_mic_time, fm_w_mic_freq_j, j_w)
...
subroutine, public fm_write(fm, matrix_index, matrix_name, qs_env)
...
subroutine, public create_fm_w_mic_time(bs_env, fm_w_mic_time)
...
subroutine, public fill_fm_sigma_c_gamma_time(fm_sigma_c_gamma_time, bs_env, mat_sigma_pos_tau, mat_sigma_neg_tau)
...
subroutine, public g_occ_vir(bs_env, tau, fm_g_gamma, ispin, occ, vir)
...
subroutine, public delete_unnecessary_files(bs_env)
...
Common DBCSR matrix operations used by GW modules.
subroutine, public hadamard_product(matrix_a, matrix_b, matrix_c, factor)
Computes the scaled element-wise product C = factor (A ◦ B) while preserving the block structure of A...
Full-matrix operations not provided by the CP2K FM packages.
Definition gw_utils_fm.F:13
subroutine, public fm_invert(matrix_a, eigenvalue_threshold, unit_nr)
Inverts a symmetric matrix. First, Cholesky decomposition is tried. If it fails, the matrix is diagon...
subroutine, public fm_sqrt(matrix_a, matrix_b, eigenvalue_threshold, unit_nr)
For input A, computes B such that B^T B=A. First, Cholesky decomposition is tried....
subroutine, public time_to_freq(bs_env, sigma_c_n_time, sigma_c_n_freq, ispin)
...
Definition gw_utils.F:3499
subroutine, public analyt_conti_and_print(bs_env, sigma_c_ikp_n_freq, sigma_x_ikp_n, v_xc_ikp_n, eigenval_scf, ikp, ispin)
...
Definition gw_utils.F:3563
subroutine, public de_init_bs_env(qs_env, bs_env)
Releases the memory-heavy GW intermediates that cannot be freed in bs_env_release,...
Definition gw_utils.F:3411
collects all constants needed in input so that they can be used without circular dependencies
integer, parameter, public rtp_method_bse
integer, parameter, public g0w0
integer, parameter, public evgw0
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public max_line_length
Definition kinds.F:59
integer, parameter, public int_8
Definition kinds.F:54
integer, parameter, public dp
Definition kinds.F:34
Routines to compute the Coulomb integral V_(alpha beta)(k) for a k-point k using lattice summation in...
subroutine, public build_2c_coulomb_matrix_kp(matrix_v_kp, kpoints, basis_type, cell, particle_set, qs_kind_set, atomic_kind_set, size_lattice_sum, operator_type, ikp_start, ikp_end)
...
Machine interface based on Fortran 2003 and POSIX.
Definition machine.F:17
subroutine, public m_flush(lunit)
flushes units if the &GLOBAL flag is set accordingly
Definition machine.F:124
real(kind=dp) function, public m_walltime()
returns time from a real-time clock, protected against rolling early/easily
Definition machine.F:141
Interface to the message passing library MPI.
subroutine, public mp_mem_avail_per_rank_gb(comm, mem_avail_gb)
Memory that is currently free on the node, per MPI rank of that node, in GB.
subroutine, public mp_print_mem_per_rank(comm, unit_nr, label)
Prints the memory available to and used by a single MPI rank at the moment of the call....
Framework for 2c-integrals for RI.
Definition mp2_ri_2c.F:14
subroutine, public ri_2c_integral_mat(qs_env, fm_matrix_minv_l_gamma, fm_matrix_l_struct, dimen_ri, ri_metric, put_mat_ks_env, regularization_ri)
...
Definition mp2_ri_2c.F:578
basic linear algebra operations for full matrixes
Define the data structure for the particle information.
Definition of physical constants:
Definition physcon.F:68
real(kind=dp), parameter, public evolt
Definition physcon.F:183
subroutine, public get_all_vbm_cbm_bandgaps(bs_env)
...
subroutine, public get_qs_env(qs_env, atomic_kind_set, qs_kind_set, cell, super_cell, cell_ref, use_ref_cell, kpoints, dft_control, mos, sab_orb, sab_all, qmmm, qmmm_periodic, mimic, sac_ae, sac_ppl, sac_lri, sap_ppnl, sab_vdw, sab_scp, sap_oce, sab_lrc, sab_se, sab_xtbe, sab_tbe, sab_core, sab_xb, sab_xtb_pp, sab_xtb_nonbond, sab_almo, sab_kp, sab_kp_nosym, sab_cneo, particle_set, energy, force, matrix_h, matrix_h_im, matrix_ks, matrix_ks_im, matrix_vxc, run_rtp, rtp, matrix_h_kp, matrix_h_im_kp, matrix_ks_kp, matrix_ks_im_kp, matrix_vxc_kp, kinetic_kp, matrix_s_kp, matrix_w_kp, matrix_s_ri_aux_kp, matrix_s, matrix_s_ri_aux, matrix_w, matrix_p_mp2, matrix_p_mp2_admm, matrix_vhxc, rho, rho_xc, pw_env, ewald_env, ewald_pw, active_space, mpools, input, para_env, blacs_env, scf_control, rel_control, kinetic, qs_charges, vppl, xcint_weights, rho_core, rho_nlcc, rho_nlcc_g, ks_env, ks_qmmm_env, wf_history, scf_env, local_particles, local_molecules, distribution_2d, dbcsr_dist, molecule_kind_set, molecule_set, subsys, cp_subsys, oce, local_rho_set, rho_atom_set, task_list, task_list_soft, rho0_atom_set, rho0_mpole, rhoz_set, rhoz_cneo_set, ecoul_1c, rho0_s_rs, rho0_s_gs, rhoz_cneo_s_rs, rhoz_cneo_s_gs, do_kpoints, has_unit_metric, requires_mo_derivs, mo_derivs, mo_loc_history, nkind, natom, nelectron_total, nelectron_spin, efield, neighbor_list_id, linres_control, xas_env, virial, cp_ddapc_env, cp_ddapc_ewald, outer_scf_history, outer_scf_ihistory, x_data, et_coupling, dftb_potential, results, se_taper, se_store_int_env, se_nddo_mpole, se_nonbond_env, admm_env, lri_env, lri_density, exstate_env, ec_env, harris_env, dispersion_env, gcp_env, vee, rho_external, external_vxc, mask, mp2_env, bs_env, kg_env, wanniercentres, atprop, ls_scf_env, do_transport, transport_env, v_hartree_rspace, s_mstruct_changed, rho_changed, potential_changed, forces_up_to_date, mscfg_env, almo_scf_env, gradient_history, variable_history, embed_pot, spin_embed_pot, polar_env, mos_last_converged, eeq, rhs, do_rixs, tb_tblite)
Get the QUICKSTEP environment.
Define the quickstep kind type and their sub types.
Provides all information about an atomic kind.
Type defining parameters related to the simulation cell.
Definition cell_types.F:60
represent a full matrix
stores all the informations relevant to an mpi environment
Provides all information about a quickstep kind.