(git:e90df42)
Loading...
Searching...
No Matches
bse_bs_env_link.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 Bethe-Salpeter equation on the RI-AO GW, the GW of PROPERTIES%BANDSTRUCTURE%GW with explicit
10!> three-center RI integrals over atomic orbitals (no RI_RS): reads PROPERTIES%BSE, builds the
11!> window-sized RI slabs and the effective inverse dielectric matrix from bs_env, and hands
12!> them to the solver core through prepare_bse_env_from_bandstructure_gw.
13!> Non-periodic systems only. Reads no real-time propagation input.
14!> \par History
15!> 09.2026 created [Maximilian Graml]
16!> \author Maximilian Graml
17! **************************************************************************************************
20 USE bse_main, ONLY: bse_solve,&
24 USE bse_types, ONLY: bse_env_release,&
26 USE bse_util, ONLY: adapt_bse_screening,&
29 USE cp_dbcsr_api, ONLY: &
36 USE cp_fm_diag, ONLY: cp_fm_power
40 USE cp_fm_types, ONLY: cp_fm_create,&
49 USE dbt_api, ONLY: &
50 dbt_batched_contract_finalize, dbt_batched_contract_init, dbt_clear, dbt_contract, &
51 dbt_create, dbt_destroy, dbt_get_block, dbt_get_stored_coordinates, &
52 dbt_iterator_blocks_left, dbt_iterator_next_block, dbt_iterator_start, dbt_iterator_stop, &
53 dbt_iterator_type, dbt_pgrid_create, dbt_pgrid_destroy, dbt_pgrid_type, dbt_type
59 USE kinds, ONLY: dp,&
60 int_8
61 USE libint_2c_3c, ONLY: libint_potential_type
62 USE mo_window, ONLY: mo_window_type
71#include "./base/base_uses.f90"
72
73 IMPLICIT NONE
74
75 PRIVATE
76
77 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'bse_bs_env_link'
78
79 ! block size of the MO index p in the tensors: the AO and RI indices come in atomic blocks, the MO
80 ! index has none. A block is the unit the tensor grid distributes and filters, so the size bounds
81 ! the largest block of the MO product, (RI atom) x (q sector) x mo_block_size
82 INTEGER, PARAMETER, PRIVATE :: mo_block_size = 64
83
84 PUBLIC :: bse_from_bs_env
85
86CONTAINS
87
88! **************************************************************************************************
89!> \brief Entry from the properties driver after the bandstructure block. Hands the solver core what
90!> its kernels are built from, for p, q in the active window:
91!> B^P_pq = sum_QR (pq|Q) [M^-1]_QR [V^1/2]_RP,
92!> ε^-1_eff,PQ = δ_PQ + sum_RS [V^-1/2]_PR W^c_RS(iω=0) [V^-1/2]_SQ,
93!> and the quasiparticle energies ε_p of the GW, so that, with the core's
94!> \bar{B}^P_ij = sum_Q ε^-1_eff,PQ B^Q_ij,
95!> A_ia,jb = δ_ij δ_ab (ε_a-ε_i) + α sum_P B^P_ia B^P_jb - w sum_P \bar{B}^P_ij B^P_ab
96!> B_ia,jb = α sum_P B^P_ia B^P_jb - w sum_P \bar{B}^P_ib B^P_ja
97!> V_PQ: bare Coulomb and M_PQ: RI metric in the RI basis, W^c = W - V: the correlation part
98!> of the GW's static screened interaction. Order: checks, section, window, screening,
99!> per-spin slabs, seam, solve, release
100!> \param qs_env source of bs_env; the slab build reads its blacs context and matrix distribution
101!> \param bse_section PROPERTIES%BSE
102! **************************************************************************************************
103 SUBROUTINE bse_from_bs_env(qs_env, bse_section)
104 TYPE(qs_environment_type), POINTER :: qs_env
105 TYPE(section_vals_type), POINTER :: bse_section
106
107 CHARACTER(LEN=*), PARAMETER :: routinen = 'bse_from_bs_env'
108
109 INTEGER :: handle, ispin, n_mo_real, n_ri, n_spin, &
110 n_window, unit_nr
111 INTEGER, ALLOCATABLE, DIMENSION(:) :: homo_full, homo_red, virt_red
112 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: eigenval_dft, eigenval_qp
113 TYPE(bse_env_type) :: bse_env
114 TYPE(cp_fm_type) :: fm_eps_inv, fm_m_inv_v_sqrt
115 TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: fm_s_ab, fm_s_ia, fm_s_ij
116 TYPE(cp_logger_type), POINTER :: logger
117 TYPE(mo_window_type) :: window
118 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
119
120 CALL timeset(routinen, handle)
121
122 logger => cp_get_default_logger()
123 unit_nr = cp_logger_get_default_io_unit(logger)
124 NULLIFY (bs_env)
125 CALL get_qs_env(qs_env, bs_env=bs_env)
126 ! the driver calls this after the bandstructure block, which creates bs_env
127 IF (.NOT. ASSOCIATED(bs_env)) cpabort("PROPERTIES%BSE found no GW environment (bs_env)")
128 ! check_cp2k_input refused each of these at input parsing with a message that says what to change;
129 ! Gamma is the one k-point that carries the MOs and the energies read below
130 IF (.NOT. bs_env%do_gw .OR. bs_env%do_periodic .OR. bs_env%do_gw_ri_rs .OR. bs_env%nkp_bs_and_DOS /= 1) THEN
131 cpabort("PROPERTIES%BSE needs a non-periodic, Gamma-only GW without RI_RS")
132 END IF
133 ! read_bse_input told the GW at its init to keep its static W and the 3c neighbour list
134 IF (.NOT. (bs_env%keep_W_freq_zero .AND. bs_env%keep_3c_nl) .OR. &
135 .NOT. ASSOCIATED(bs_env%fm_W_MIC_freq_zero%matrix_struct)) THEN
136 cpabort("PROPERTIES%BSE: the GW kept no static W(0) or no 3c neighbour list")
137 END IF
138 ! every RI batch of the slabs goes to one tensor group; unset groups would skip them all silently
139 IF (bs_env%num_tensor_groups < 1 .OR. bs_env%tensor_group_color < 0) THEN
140 cpabort("PROPERTIES%BSE: the tensor groups of the GW are not set")
141 END IF
142
143 n_spin = bs_env%n_spin
144 n_ri = bs_env%n_RI
145 ! the canonical orthogonalisation parks the linearly dependent directions above n_mo_retained
146 n_mo_real = min(bs_env%n_mo_retained, bs_env%n_ao)
147
148 CALL read_bse_section(bse_section, bse_env)
149 CALL adapt_bse_screening(bse_env, unit_nr)
150 CALL bse_resolve_memory_per_proc(bse_env%memory_per_proc_gb, bs_env%para_env, unit_nr)
151
152 CALL link_window(bs_env, bse_env, n_mo_real, window, homo_red, virt_red, unit_nr)
153 n_window = window%last_mo - window%first_mo + 1
154
155 CALL link_screening(qs_env, bs_env, unit_nr, fm_m_inv_v_sqrt, fm_eps_inv, &
156 bse_env%screening_method == bse_screening_w0)
157 ! the GW kept W(0) for the screening and nl_3c for the slabs; each goes once read
158 CALL cp_fm_release(bs_env%fm_W_MIC_freq_zero)
159
160 ALLOCATE (fm_s_ia(n_spin), fm_s_ij(n_spin), fm_s_ab(n_spin))
161 ALLOCATE (eigenval_qp(n_window, n_spin), eigenval_dft(n_window, n_spin), homo_full(n_spin))
162 DO ispin = 1, n_spin
163 CALL link_slabs(qs_env, bs_env, bse_env, ispin, window, homo_red(ispin), virt_red(ispin), fm_m_inv_v_sqrt, &
164 fm_s_ij(ispin), fm_s_ia(ispin), fm_s_ab(ispin), unit_nr)
165 eigenval_qp(:, ispin) = bs_env%eigenval_GW(window%first_mo:window%last_mo, 1, ispin)
166 eigenval_dft(:, ispin) = bs_env%eigenval_scf_Gamma(window%first_mo:window%last_mo, ispin)
167 homo_full(ispin) = bs_env%n_occ(ispin)
168 END DO
169 CALL cp_fm_release(fm_m_inv_v_sqrt)
170 CALL neighbor_list_3c_destroy(bs_env%nl_3c)
171
172 CALL prepare_bse_env_from_bandstructure_gw(bse_env, fm_s_ia, fm_s_ij, fm_s_ab, fm_eps_inv, &
173 eigenval_qp, eigenval_dft, homo_full, homo_red, virt_red, &
174 n_ri, qs_env, unit_nr)
175 CALL cp_fm_release(fm_eps_inv)
176 CALL bse_solve(bse_env, bs_env%fm_mo_coeff_Gamma(1:n_spin), unit_nr)
177 CALL bse_env_release(bse_env)
178
179 CALL timestop(handle)
180
181 END SUBROUTINE bse_from_bs_env
182
183! **************************************************************************************************
184!> \brief The active window on the DFT axis over the real (retained) MOs, one absolute range for
185!> every spin, and the levels inside it per spin: homo_red = n_occ - first_mo + 1 occupied,
186!> virt_red = last_mo - n_occ virtual, with n_occ the occupied MOs of the spin
187!> \param bs_env DFT eigenvalues and occupations at Gamma
188!> \param bse_env its cutoffs, read from the section already
189!> \param n_mo_real MOs that are states, not linear-dependency placeholders
190!> \param window first and last MO of the window
191!> \param homo_red occupied levels in the window per spin, allocated here
192!> \param virt_red virtual levels in the window per spin, allocated here
193!> \param unit_nr output unit, positive on the writing rank only
194! **************************************************************************************************
195 SUBROUTINE link_window(bs_env, bse_env, n_mo_real, window, homo_red, virt_red, unit_nr)
196 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
197 TYPE(bse_env_type), INTENT(IN) :: bse_env
198 INTEGER, INTENT(IN) :: n_mo_real
199 TYPE(mo_window_type), INTENT(OUT) :: window
200 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: homo_red, virt_red
201 INTEGER, INTENT(IN) :: unit_nr
202
203 CHARACTER(LEN=*), PARAMETER :: routinen = 'link_window'
204
205 INTEGER :: handle, ispin, n_spin
206 INTEGER, ALLOCATABLE, DIMENSION(:) :: homo, n_mo
207
208 CALL timeset(routinen, handle)
209
210 n_spin = bs_env%n_spin
211 ALLOCATE (n_mo(n_spin), homo(n_spin), homo_red(n_spin), virt_red(n_spin))
212 n_mo(:) = n_mo_real
213 homo(:) = bs_env%n_occ(1:n_spin)
214 CALL bse_window_from_dft(bs_env%eigenval_scf_Gamma(1:n_mo_real, 1:n_spin), n_mo, homo, &
215 bse_env%bse_cutoff_occ, bse_env%bse_cutoff_empty, window, warn=.true.)
216 DO ispin = 1, n_spin
217 homo_red(ispin) = homo(ispin) - window%first_mo + 1
218 virt_red(ispin) = window%last_mo - homo(ispin)
219 IF (homo_red(ispin) < 1) cpabort("PROPERTIES%BSE: ENERGY_CUTOFF_OCC leaves no occupied level")
220 IF (virt_red(ispin) < 1) cpabort("PROPERTIES%BSE: ENERGY_CUTOFF_EMPTY leaves no empty level")
221 END DO
222 IF (unit_nr > 0) WRITE (unit_nr, '(T2,A4,T7,A,T61,2I10)') 'BSE|', &
223 'Active window on the DFT axis (first, last MO)', window%first_mo, window%last_mo
224 DEALLOCATE (n_mo, homo)
225
226 CALL timestop(handle)
227
228 END SUBROUTINE link_window
229
230! **************************************************************************************************
231!> \brief The RI transform and the effective inverse dielectric matrix from the GW's static W,
232!> with V_PQ the bare Coulomb and M_PQ the RI metric in the RI basis:
233!> [M^-1 V^1/2]_QP = sum_R [M^-1]_QR [V^1/2]_RP, so that the slabs
234!> B^P_pq = sum_Q (pq|Q) [M^-1 V^1/2]_QP give
235!> sum_P B^P_pq B^P_rs = sum_QR (pq|Q) [M^-1 V M^-1]_QR (R|rs);
236!> with the stored W~_PQ = sum_RS [M^-1]_PR W^c_RS(iω=0) [M^-1]_SQ,
237!> ε^-1_eff,PQ = δ_PQ + sum_RS [V^-1/2]_PR W^c_RS(iω=0) [V^-1/2]_SQ, so that
238!> sum_PQ B^P_pq ε^-1_eff,PQ B^Q_rs = sum_RS (pq|R) [M^-1 (V + W^c(iω=0)) M^-1]_RS (S|rs),
239!> the full static W of the GW. The GW builds the same factor M^-1 V^1/2 per frequency from a
240!> Cholesky factor of V (compute_MinvVsqrt_Vsqrt); the symmetric roots here give V^-1/2 as
241!> well, which the stored W~ needs, with the eigenvalue threshold of the GW (eps_eigval_mat_RI).
242!> The inverse root and the effective dielectric matrix are needed for W0 screening only.
243!> \param qs_env for the 2c RI integrals
244!> \param bs_env RI metric and its inverse, the stored static W~, the RI x RI matrix structure
245!> \param unit_nr output unit, positive on the writing rank only
246!> \param fm_M_inv_V_sqrt [M^-1 V^1/2]_QP, RI x RI, created here
247!> \param fm_eps_inv ε^-1_eff,PQ, RI x RI, created here for W0 screening only
248!> \param need_screening whether to build the effective inverse dielectric matrix
249! **************************************************************************************************
250 SUBROUTINE link_screening(qs_env, bs_env, unit_nr, fm_M_inv_V_sqrt, fm_eps_inv, need_screening)
251 TYPE(qs_environment_type), POINTER :: qs_env
252 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
253 INTEGER, INTENT(IN) :: unit_nr
254 TYPE(cp_fm_type), INTENT(OUT) :: fm_m_inv_v_sqrt, fm_eps_inv
255 LOGICAL, INTENT(IN) :: need_screening
256
257 CHARACTER(LEN=*), PARAMETER :: routinen = 'link_screening'
258
259 INTEGER :: handle, n_dependent, n_ri
260 TYPE(cp_fm_type) :: fm_v_inv_sqrt, fm_v_sqrt, fm_wc, fm_work
261 TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:, :) :: fm_m, fm_v
262 TYPE(libint_potential_type) :: coulomb_op
263
264 CALL timeset(routinen, handle)
265
266 n_ri = bs_env%n_RI
267 ! bare Coulomb V_PQ (coulomb_op keeps its default, the full 1/r) and the RI metric M_PQ
268 CALL ri_2c_integral_mat(qs_env, fm_v, bs_env%fm_RI_RI%matrix_struct, n_ri, coulomb_op)
269 IF (need_screening) THEN
270 CALL ri_2c_integral_mat(qs_env, fm_m, bs_env%fm_RI_RI%matrix_struct, n_ri, bs_env%ri_metric)
271 END IF
272
273 ! [V^1/2]_PQ and [V^-1/2]_PQ by symmetric roots; eigenvalues of V under the GW's threshold are dropped
274 CALL cp_fm_create(fm_v_sqrt, bs_env%fm_RI_RI%matrix_struct)
275 CALL cp_fm_create(fm_work, bs_env%fm_RI_RI%matrix_struct)
276 CALL cp_fm_to_fm(fm_v(1, 1), fm_v_sqrt)
277 CALL cp_fm_power(fm_v_sqrt, fm_work, 0.5_dp, bs_env%eps_eigval_mat_RI, n_dependent)
278 IF (unit_nr > 0 .AND. n_dependent > 0) WRITE (unit_nr, '(T2,A4,T7,A,T71,I10)') 'BSE|', &
279 'RI Coulomb directions dropped by the eigenvalue threshold', n_dependent
280
281 ! [M^-1 V^1/2]_QP = sum_R [M^-1]_QR [V^1/2]_RP
282 CALL cp_fm_create(fm_m_inv_v_sqrt, bs_env%fm_RI_RI%matrix_struct)
283 CALL parallel_gemm("N", "N", n_ri, n_ri, n_ri, 1.0_dp, bs_env%fm_Minv_Gamma, fm_v_sqrt, &
284 0.0_dp, fm_m_inv_v_sqrt)
285
286 IF (need_screening) THEN
287 CALL cp_fm_create(fm_v_inv_sqrt, bs_env%fm_RI_RI%matrix_struct)
288 CALL cp_fm_to_fm(fm_v(1, 1), fm_v_inv_sqrt)
289 CALL cp_fm_power(fm_v_inv_sqrt, fm_work, -0.5_dp, bs_env%eps_eigval_mat_RI, n_dependent)
290 ! W^c_PQ(iω=0) = sum_RS M_PR W~_RS M_SQ, in place on a copy of the stored W~
291 CALL cp_fm_create(fm_wc, bs_env%fm_RI_RI%matrix_struct)
292 CALL cp_fm_to_fm(bs_env%fm_W_MIC_freq_zero, fm_wc)
293 CALL fm_contract_aba(fm_m(1, 1), fm_wc)
294 ! ε^-1_eff,PQ = δ_PQ + sum_RS [V^-1/2]_PR W^c_RS(iω=0) [V^-1/2]_SQ, the sum in the work matrix
295 CALL fm_contract_aba(fm_v_inv_sqrt, fm_wc, fm_work)
296 CALL cp_fm_create(fm_eps_inv, bs_env%fm_RI_RI%matrix_struct)
297 CALL cp_fm_set_all(fm_eps_inv, 0.0_dp, 1.0_dp)
298 CALL cp_fm_scale_and_add(1.0_dp, fm_eps_inv, 1.0_dp, fm_work)
299 END IF
300
301 CALL cp_fm_release(fm_v)
302 CALL cp_fm_release(fm_m)
303 CALL cp_fm_release(fm_v_sqrt)
304 CALL cp_fm_release(fm_v_inv_sqrt)
305 CALL cp_fm_release(fm_wc)
306 CALL cp_fm_release(fm_work)
307
308 CALL timestop(handle)
309
310 END SUBROUTINE link_screening
311
312! **************************************************************************************************
313!> \brief The window columns of the MO coefficients, C_μp for p in the window, as a 2-index
314!> tensor (AO atom | MO block) on the tensor group's grid, the MO blocking the caller's.
315!> The coefficients are a full matrix on all ranks, the tensor lives on the ranks of one
316!> tensor group, and no copy between the two exists. The routine takes the route of the GW's
317!> own matrices (cp_fm_to_dbt: full matrix, DBCSR matrix on all ranks, DBCSR matrix on
318!> the group, tensor) and creates the two DBCSR matrices of that route in the
319!> (AO atom | MO block) blocking, for which the GW holds no template. A small object,
320!> n_ao x n_window: the steps are there for the change of communicator, not for memory
321!> \param bs_env MO coefficients at Gamma, AO block sizes, the tensor group
322!> \param dbcsr_dist distribution of the SCF matrices, the template of the global matrix
323!> \param ispin spin channel
324!> \param window first and last MO of the active window
325!> \param sizes_mo MO block sizes of the tensor, summing to the window
326!> \param t_C the tensor, created here
327! **************************************************************************************************
328 SUBROUTINE link_mo_tensor(bs_env, dbcsr_dist, ispin, window, sizes_mo, t_C)
329 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
330 TYPE(dbcsr_distribution_type), POINTER :: dbcsr_dist
331 INTEGER, INTENT(IN) :: ispin
332 TYPE(mo_window_type), INTENT(IN) :: window
333 INTEGER, DIMENSION(:), INTENT(IN) :: sizes_mo
334 TYPE(dbt_type), INTENT(OUT) :: t_c
335
336 CHARACTER(LEN=*), PARAMETER :: routinen = 'link_mo_tensor'
337
338 INTEGER :: handle, n_window
339 INTEGER, ALLOCATABLE, DIMENSION(:) :: dist_1, dist_2
340 INTEGER, DIMENSION(2) :: pdims_2d
341 TYPE(cp_fm_struct_type), POINTER :: struct_c_window
342 TYPE(cp_fm_type) :: fm_c_window
343 TYPE(dbcsr_distribution_type) :: dist_global, dist_group, dist_tmpl
344 TYPE(dbcsr_type) :: mat_global, mat_group
345 TYPE(dbt_pgrid_type) :: pgrid_2d
346
347 CALL timeset(routinen, handle)
348
349 n_window = window%last_mo - window%first_mo + 1
350 ! the MO blocks cover the window exactly
351 IF (sum(sizes_mo) /= n_window) cpabort("MO block sizes do not sum to the active window")
352
353 pdims_2d = 0
354 CALL dbt_pgrid_create(bs_env%para_env_tensor, pdims_2d, pgrid_2d)
355 CALL create_2c_tensor(t_c, dist_1, dist_2, pgrid_2d, bs_env%sizes_AO, sizes_mo, name="(AO | MO)")
356 DEALLOCATE (dist_1, dist_2)
357 CALL dbt_pgrid_destroy(pgrid_2d)
358
359 ! the window columns as a full matrix, then the route of the GW's own matrices (cp_fm_to_dbt):
360 ! global DBCSR, group DBCSR, tensor; the coefficients are never replicated
361 NULLIFY (struct_c_window)
362 CALL cp_fm_struct_create(struct_c_window, template_fmstruct=bs_env%fm_mo_coeff_Gamma(ispin)%matrix_struct, &
363 ncol_global=n_window)
364 CALL cp_fm_create(fm_c_window, struct_c_window)
365 CALL cp_fm_struct_release(struct_c_window)
366 CALL cp_fm_to_fm_submat(bs_env%fm_mo_coeff_Gamma(ispin), fm_c_window, bs_env%n_ao, n_window, 1, window%first_mo, 1, 1)
367 CALL link_matrix_round_robin(dbcsr_dist, bs_env%sizes_AO, sizes_mo, "C window", dist_global, mat_global)
368 ! the group matrix on the grid of the GW's AO matrices of the tensor group
369 CALL dbcsr_get_info(bs_env%mat_ao_ao_tensor%matrix, distribution=dist_tmpl)
370 CALL link_matrix_round_robin(dist_tmpl, bs_env%sizes_AO, sizes_mo, "C window, group", dist_group, mat_group)
371 CALL cp_fm_to_dbt(fm_c_window, mat_global, mat_group, t_c, bs_env%para_env, &
372 bs_env%para_env_tensor%num_pe, bs_env%eps_filter)
373
374 CALL cp_fm_release(fm_c_window)
375 CALL dbcsr_release(mat_global)
376 CALL dbcsr_release(mat_group)
377 CALL dbcsr_distribution_release(dist_global)
378 CALL dbcsr_distribution_release(dist_group)
379
380 CALL timestop(handle)
381
382 END SUBROUTINE link_mo_tensor
383
384! **************************************************************************************************
385!> \brief A DBCSR matrix with the given blocking on the communicator and process grid of a template
386!> distribution, the blocks round robin over the grid rows and columns
387!> \param dist_tmpl the template
388!> \param row_sizes row block sizes
389!> \param col_sizes column block sizes
390!> \param matrix_name name of the matrix
391!> \param matrix_dist the distribution of the matrix, created here, released after the matrix
392!> \param new_matrix the matrix, created here
393! **************************************************************************************************
394 SUBROUTINE link_matrix_round_robin(dist_tmpl, row_sizes, col_sizes, matrix_name, matrix_dist, new_matrix)
395 TYPE(dbcsr_distribution_type), INTENT(IN) :: dist_tmpl
396 INTEGER, DIMENSION(:), INTENT(IN) :: row_sizes, col_sizes
397 CHARACTER(LEN=*), INTENT(IN) :: matrix_name
398 TYPE(dbcsr_distribution_type), INTENT(OUT) :: matrix_dist
399 TYPE(dbcsr_type), INTENT(OUT) :: new_matrix
400
401 INTEGER :: i_block, npcols, nprows
402 INTEGER, DIMENSION(:), POINTER :: col_blk, col_dist, row_blk, row_dist
403
404 NULLIFY (col_blk, col_dist, row_blk, row_dist)
405 CALL dbcsr_distribution_get(dist_tmpl, nprows=nprows, npcols=npcols)
406 ALLOCATE (row_dist(SIZE(row_sizes)), col_dist(SIZE(col_sizes)), row_blk(SIZE(row_sizes)), col_blk(SIZE(col_sizes)))
407 row_dist(:) = [(mod(i_block - 1, nprows), i_block=1, SIZE(row_sizes))]
408 col_dist(:) = [(mod(i_block - 1, npcols), i_block=1, SIZE(col_sizes))]
409 row_blk(:) = row_sizes(:)
410 col_blk(:) = col_sizes(:)
411 CALL dbcsr_distribution_new(matrix_dist, template=dist_tmpl, row_dist=row_dist, col_dist=col_dist)
412 CALL dbcsr_create(new_matrix, name=matrix_name, dist=matrix_dist, matrix_type=dbcsr_type_no_symmetry, &
413 row_blk_size=row_blk, col_blk_size=col_blk)
414 DEALLOCATE (row_dist, col_dist, row_blk, col_blk)
415
416 END SUBROUTINE link_matrix_round_robin
417
418! **************************************************************************************************
419!> \brief Block sizes of the window index p: blocks of at most mo_block_size levels inside the
420!> occupied and inside the virtual sector, so that no block straddles the two
421!> \param n_occ occupied levels in the window
422!> \param n_virt virtual levels in the window
423!> \param block_sizes levels per block, occupied blocks first, allocated here
424!> \param n_block_occ occupied blocks
425! **************************************************************************************************
426 SUBROUTINE link_sector_blocks(n_occ, n_virt, block_sizes, n_block_occ)
427 INTEGER, INTENT(IN) :: n_occ, n_virt
428 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: block_sizes
429 INTEGER, INTENT(OUT) :: n_block_occ
430
431 INTEGER :: n_block_virt
432
433 n_block_occ = (n_occ - 1)/mo_block_size + 1
434 n_block_virt = (n_virt - 1)/mo_block_size + 1
435 ALLOCATE (block_sizes(n_block_occ + n_block_virt))
436 block_sizes(:) = mo_block_size
437 block_sizes(n_block_occ) = n_occ - (n_block_occ - 1)*mo_block_size
438 block_sizes(n_block_occ + n_block_virt) = n_virt - (n_block_virt - 1)*mo_block_size
439
440 END SUBROUTINE link_sector_blocks
441
442! **************************************************************************************************
443!> \brief Contiguous RI atom ranges of at most max_RI_per_batch RI functions; an atom larger than
444!> that forms its own batch
445!> \param sizes_RI RI functions per atom
446!> \param max_RI_per_batch RI functions per batch the memory left allows
447!> \param batch_first first atom per batch, allocated here
448!> \param batch_last last atom per batch, allocated here
449! **************************************************************************************************
450 SUBROUTINE link_ri_batches(sizes_RI, max_RI_per_batch, batch_first, batch_last)
451 INTEGER, DIMENSION(:), INTENT(IN) :: sizes_ri
452 INTEGER, INTENT(IN) :: max_ri_per_batch
453 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: batch_first, batch_last
454
455 INTEGER :: i_atom, n_atom, n_batch, n_ri_in_batch
456 INTEGER, ALLOCATABLE, DIMENSION(:) :: first_atom, last_atom
457
458 n_atom = SIZE(sizes_ri)
459 ALLOCATE (first_atom(n_atom), last_atom(n_atom))
460 n_batch = 0
461 n_ri_in_batch = 0
462 DO i_atom = 1, n_atom
463 IF (n_ri_in_batch > 0 .AND. n_ri_in_batch + sizes_ri(i_atom) > max_ri_per_batch) THEN
464 last_atom(n_batch) = i_atom - 1
465 n_ri_in_batch = 0
466 END IF
467 IF (n_ri_in_batch == 0) THEN
468 n_batch = n_batch + 1
469 first_atom(n_batch) = i_atom
470 END IF
471 n_ri_in_batch = n_ri_in_batch + sizes_ri(i_atom)
472 END DO
473 last_atom(n_batch) = n_atom
474 ALLOCATE (batch_first(n_batch), batch_last(n_batch))
475 batch_first(:) = first_atom(1:n_batch)
476 batch_last(:) = last_atom(1:n_batch)
477 DEALLOCATE (first_atom, last_atom)
478
479 END SUBROUTINE link_ri_batches
480
481! **************************************************************************************************
482!> \brief What MEMORY_PER_PROC leaves to the slab build per rank and the largest RI batch it allows:
483!> per RI function of a batch, on the ranks of its tensor group the 3c integrals (P|μν), the
484!> half-transform (P μ|q) and the product (P|qp), and on all ranks the block matrices of the
485!> batch, their global copies and the batch's full matrix; one atom per batch with a warning
486!> when one RI atom alone exceeds what is left
487!> \param bs_env sizes and communicators
488!> \param bse_env the resolved MEMORY_PER_PROC
489!> \param n_window levels in the active window
490!> \param unit_nr output unit, positive on the writing rank only
491!> \param budget_bytes what is left, in bytes; the RI transform chunks its work matrix by it as well
492!> \param max_RI_per_batch RI functions per batch within what is left, at least one
493! **************************************************************************************************
494 SUBROUTINE link_batch_budget(bs_env, bse_env, n_window, unit_nr, budget_bytes, max_RI_per_batch)
495 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
496 TYPE(bse_env_type), INTENT(IN) :: bse_env
497 INTEGER, INTENT(IN) :: n_window, unit_nr
498 INTEGER(KIND=int_8), INTENT(OUT) :: budget_bytes
499 INTEGER, INTENT(OUT) :: max_ri_per_batch
500
501 CHARACTER(LEN=16) :: left_str, need_str
502 INTEGER(KIND=int_8) :: bytes_per_ri, largest_batch_bytes, &
503 one_atom_bytes
504 REAL(kind=dp) :: mem_left_gb
505
506 mem_left_gb = bse_memory_left_gb(bse_env%memory_per_proc_gb, bs_env%para_env)
507 budget_bytes = int(mem_left_gb*1.0e9_dp, kind=int_8)
508 ! per RI function of a batch, all distributed: on the ranks of its tensor group the 3c
509 ! integrals (P|μν), the half-transform (P μ|q) and the product (P|qp); on all ranks the block
510 ! matrices of the batch, their global copies (two inside the group-to-global hop) and the
511 ! batch's full matrix, four (P|qp); the contractions' own scratch is not counted
512 bytes_per_ri = 8_int_8*((int(bs_env%n_ao, int_8)**2 + int(bs_env%n_ao, int_8)*int(n_window, int_8) + &
513 int(n_window, int_8)**2)/int(bs_env%para_env_tensor%num_pe, int_8) + &
514 4_int_8*int(n_window, int_8)**2/int(bs_env%para_env%num_pe, int_8))
515 max_ri_per_batch = int(max(1_int_8, min(int(sum(bs_env%sizes_RI), int_8), budget_bytes/bytes_per_ri)))
516 one_atom_bytes = int(maxval(bs_env%sizes_RI), int_8)*bytes_per_ri
517 largest_batch_bytes = max(int(max_ri_per_batch, int_8)*bytes_per_ri, one_atom_bytes)
518 IF (unit_nr > 0) THEN
519 WRITE (unit_nr, '(T2,A4,T7,A,T67,F14.3)') 'BSE|', 'Memory per MPI rank for the BSE, MEMORY_PER_PROC (GB)', &
520 bse_env%memory_per_proc_gb
521 WRITE (unit_nr, '(T2,A4,T7,A,T67,F14.3)') 'BSE|', 'Left for the 3c integrals after the memory held (GB)', &
522 mem_left_gb
523 WRITE (unit_nr, '(T2,A4,T7,A,T67,F14.3)') 'BSE|', 'Largest RI batch per MPI rank (GB)', &
524 REAL(largest_batch_bytes, dp)*1.0e-9_dp
525 END IF
526 ! nothing smaller than one atom per batch exists: run with it and say so
527 IF (one_atom_bytes > budget_bytes) THEN
528 WRITE (need_str, '(F16.3)') real(one_atom_bytes, dp)*1.0e-9_dp
529 WRITE (left_str, '(F16.3)') mem_left_gb
530 CALL cp_warn(__location__, &
531 "PROPERTIES%BSE: the 3c integral batch of one RI atom needs "//trim(adjustl(need_str))// &
532 " GB per MPI rank, "//trim(adjustl(left_str))//" GB are left of MEMORY_PER_PROC; one atom "// &
533 "per batch. Raise MEMORY_PER_PROC or use more MPI ranks.")
534 END IF
535
536 END SUBROUTINE link_batch_budget
537
538! **************************************************************************************************
539!> \brief The three window-sized slabs of one spin, B^P_pq = sum_Q (pq|Q) [M^-1 V^1/2]_QP with
540!> (Q|pq) = sum_μν C_μp (Q|μν) C_νq, batched over RI atoms: the 3c integrals of the batch
541!> are rebuilt and transformed over ν, then over μ, by two tensor contractions on the batch's
542!> tensor group; the product goes to all ranks and its rows are copied into the slabs; the
543!> RI transform by M^-1 V^1/2 follows in column chunks. Column conventions of the solver core:
544!> ia = (i-1) n_virt + a, ij = (i-1) n_occ + j, ab = (a-1) n_virt + b
545!> \param qs_env blacs context, distribution of the SCF matrices, environment of the 3c integrals
546!> \param bs_env RI and AO sizes, the 3c neighbour list and tensor template, the tensor groups
547!> \param bse_env its memory budget and memory check
548!> \param ispin spin channel
549!> \param window first and last MO of the active window
550!> \param n_occ occupied levels in the window
551!> \param n_virt virtual levels in the window
552!> \param fm_M_inv_V_sqrt the RI transform [M^-1 V^1/2]_QP, RI x RI
553!> \param fm_S_ij B^P_ij, RI x n_occ*n_occ, created here
554!> \param fm_S_ia B^P_ia, RI x n_occ*n_virt, created here
555!> \param fm_S_ab B^P_ab, RI x n_virt*n_virt, created here
556!> \param unit_nr output unit, positive on the writing rank only
557! **************************************************************************************************
558 SUBROUTINE link_slabs(qs_env, bs_env, bse_env, ispin, window, n_occ, n_virt, fm_M_inv_V_sqrt, fm_S_ij, fm_S_ia, &
559 fm_S_ab, unit_nr)
560 TYPE(qs_environment_type), POINTER :: qs_env
561 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
562 TYPE(bse_env_type), INTENT(IN) :: bse_env
563 INTEGER, INTENT(IN) :: ispin
564 TYPE(mo_window_type), INTENT(IN) :: window
565 INTEGER, INTENT(IN) :: n_occ, n_virt
566 TYPE(cp_fm_type), INTENT(IN) :: fm_m_inv_v_sqrt
567 TYPE(cp_fm_type), INTENT(OUT) :: fm_s_ij, fm_s_ia, fm_s_ab
568 INTEGER, INTENT(IN) :: unit_nr
569
570 CHARACTER(LEN=*), PARAMETER :: routinen = 'link_slabs'
571
572 INTEGER :: atom_first, atom_last, handle, i_batch, &
573 max_ri_per_batch, n_batch, &
574 n_block_occ, n_ri, n_window
575 INTEGER(KIND=int_8) :: budget_bytes
576 INTEGER, ALLOCATABLE, DIMENSION(:) :: batch_first, batch_last, dist_1, dist_2, &
577 dist_3, sizes_p, sizes_q
578 INTEGER, DIMENSION(3) :: pdims_3d
579 TYPE(cp_blacs_env_type), POINTER :: blacs_env
580 TYPE(cp_fm_struct_type), POINTER :: struct_ab, struct_ia, struct_ij
581 TYPE(dbcsr_distribution_type), POINTER :: dbcsr_dist
582 TYPE(dbt_pgrid_type) :: pgrid_3d
583 TYPE(dbt_type) :: t_3c, t_3c_half, t_3c_mo, t_c_p, t_c_q
584
585 CALL timeset(routinen, handle)
586
587 NULLIFY (blacs_env, dbcsr_dist, struct_ab, struct_ia, struct_ij)
588 CALL get_qs_env(qs_env, blacs_env=blacs_env, dbcsr_dist=dbcsr_dist)
589 n_ri = bs_env%n_RI
590 n_window = n_occ + n_virt
591 CALL cp_fm_struct_create(struct_ij, para_env=bs_env%para_env, context=blacs_env, &
592 nrow_global=n_ri, ncol_global=n_occ*n_occ)
593 CALL cp_fm_struct_create(struct_ia, para_env=bs_env%para_env, context=blacs_env, &
594 nrow_global=n_ri, ncol_global=n_occ*n_virt)
595 CALL cp_fm_struct_create(struct_ab, para_env=bs_env%para_env, context=blacs_env, &
596 nrow_global=n_ri, ncol_global=n_virt*n_virt)
597 ! the slabs collect (Q|pq) batch by batch; the RI transform at the end makes them B^P_pq in place
598 CALL cp_fm_create(fm_s_ij, struct_ij, name="fm_S_ij", set_zero=.true.)
599 CALL cp_fm_create(fm_s_ia, struct_ia, name="fm_S_ia", set_zero=.true.)
600 CALL cp_fm_create(fm_s_ab, struct_ab, name="fm_S_ab", set_zero=.true.)
601 CALL cp_fm_struct_release(struct_ij)
602 CALL cp_fm_struct_release(struct_ia)
603 CALL cp_fm_struct_release(struct_ab)
604
605 ! the window index q in two blocks, occupied and virtual, so that every (P, q, p) block of the
606 ! product covers the whole q range of one pair type; p in blocks inside each sector
607 ALLOCATE (sizes_q(2))
608 sizes_q(:) = [n_occ, n_virt]
609 CALL link_sector_blocks(n_occ, n_virt, sizes_p, n_block_occ)
610 CALL link_mo_tensor(bs_env, dbcsr_dist, ispin, window, sizes_q, t_c_q)
611 CALL link_mo_tensor(bs_env, dbcsr_dist, ispin, window, sizes_p, t_c_p)
612
613 CALL link_batch_budget(bs_env, bse_env, n_window, unit_nr, budget_bytes, max_ri_per_batch)
614 CALL link_ri_batches(bs_env%sizes_RI, max_ri_per_batch, batch_first, batch_last)
615 n_batch = SIZE(batch_first)
616 IF (unit_nr > 0) WRITE (unit_nr, '(T2,A4,T7,A,T71,I10)') 'BSE|', 'RI atom batches of the 3c integral transformation', n_batch
617
618 ! (RI AO | MO) of the half-transform and (RI | MO MO) of the product on the tensor group's grid
619 pdims_3d = 0
620 CALL dbt_pgrid_create(bs_env%para_env_tensor, pdims_3d, pgrid_3d)
621 CALL create_3c_tensor(t_3c_half, dist_1, dist_2, dist_3, pgrid_3d, bs_env%sizes_RI, bs_env%sizes_AO, &
622 sizes_q, map1=[1, 2], map2=[3], name="(RI AO | MO)")
623 DEALLOCATE (dist_1, dist_2, dist_3)
624 CALL dbt_pgrid_destroy(pgrid_3d)
625 pdims_3d = 0
626 CALL dbt_pgrid_create(bs_env%para_env_tensor, pdims_3d, pgrid_3d, &
627 tensor_dims=[bs_env%n_atom, SIZE(sizes_q), SIZE(sizes_p)])
628 CALL create_3c_tensor(t_3c_mo, dist_1, dist_2, dist_3, pgrid_3d, bs_env%sizes_RI, sizes_q, sizes_p, &
629 map1=[1], map2=[2, 3], name="(RI | MO MO)")
630 DEALLOCATE (dist_1, dist_2, dist_3)
631 CALL dbt_pgrid_destroy(pgrid_3d)
632 CALL dbt_create(bs_env%t_RI_AO__AO, t_3c)
633
634 ! C_q contracts once per batch in a layout-compatible contraction: the batched context keeps its replicated
635 ! copy across the batches; on the small operand only, so that no grid changes under the neighbour list
636 ! of the 3c integrals. No context on C_p: no layout of (Pμ|q) fits both contractions, so the second remaps it
637 CALL dbt_batched_contract_init(t_c_q)
638
639 DO i_batch = 1, n_batch
640 atom_first = batch_first(i_batch)
641 atom_last = batch_last(i_batch)
642 ! one tensor group per batch, round robin; every rank joins the hop into the slabs
643 IF (modulo(i_batch - 1, bs_env%num_tensor_groups) == bs_env%tensor_group_color) THEN
644 CALL compute_3c_integrals(qs_env, bs_env, t_3c, atoms_ri=[atom_first, atom_last])
645 ! (Pμ|q) = sum_ν (P|μν) C_νq
646 CALL dbt_contract(alpha=1.0_dp, tensor_1=t_3c, tensor_2=t_c_q, beta=0.0_dp, tensor_3=t_3c_half, &
647 contract_1=[3], notcontract_1=[1, 2], map_1=[1, 2], &
648 contract_2=[1], notcontract_2=[2], map_2=[3], &
649 filter_eps=bs_env%eps_filter)
650 CALL dbt_clear(t_3c)
651 ! (P|qp) = sum_μ (Pμ|q) C_μp, distributed over the group
652 CALL dbt_contract(alpha=1.0_dp, tensor_1=t_3c_half, tensor_2=t_c_p, beta=0.0_dp, tensor_3=t_3c_mo, &
653 contract_1=[2], notcontract_1=[1, 3], map_1=[1, 2], &
654 contract_2=[1], notcontract_2=[2], map_2=[3], &
655 filter_eps=bs_env%eps_filter)
656 CALL dbt_clear(t_3c_half)
657 END IF
658 CALL link_slab_rows(bs_env, blacs_env, dbcsr_dist, t_3c_mo, n_occ, n_virt, sizes_p, n_block_occ, atom_first, atom_last, &
659 fm_s_ij, fm_s_ia, fm_s_ab)
660 CALL dbt_clear(t_3c_mo)
661 END DO
662
663 CALL dbt_batched_contract_finalize(t_c_q)
664 CALL dbt_destroy(t_3c)
665 CALL dbt_destroy(t_3c_half)
666 CALL dbt_destroy(t_3c_mo)
667 CALL dbt_destroy(t_c_q)
668 CALL dbt_destroy(t_c_p)
669 DEALLOCATE (sizes_q, sizes_p, batch_first, batch_last)
670
671 ! B^P_pq = sum_Q [M^-1 V^1/2]_QP (Q|pq), in column chunks, in place
672 CALL apply_ri_transform(fm_m_inv_v_sqrt, fm_s_ij, n_ri, n_occ*n_occ, budget_bytes)
673 CALL apply_ri_transform(fm_m_inv_v_sqrt, fm_s_ia, n_ri, n_occ*n_virt, budget_bytes)
674 CALL apply_ri_transform(fm_m_inv_v_sqrt, fm_s_ab, n_ri, n_virt*n_virt, budget_bytes)
675
676 CALL timestop(handle)
677
678 END SUBROUTINE link_slabs
679
680! **************************************************************************************************
681!> \brief The batch product (P|qp) into the rows of its RI functions in the three slabs: the local
682!> (P, q, p) blocks into one block matrix per pair type on the tensor group's grid, the
683!> group-to-global hop of the GW, the block-cyclic copy, the row copy into the slab; every
684!> rank takes part, a rank outside the batch's group with no blocks. The pair (q, p) lands
685!> in column (p-1) n_q + q of its slab, n_q = n_occ for ij and n_virt for ia and ab:
686!> ij = (i-1) n_occ + j at (q, p) = (j, i), ia = (i-1) n_virt + a at (a, i),
687!> ab = (a-1) n_virt + b at (b, a)
688!> \param bs_env RI offsets and sizes per atom, the communicator of all ranks
689!> \param blacs_env context of the slabs
690!> \param dbcsr_dist a distribution over all ranks, the template of the global block matrices
691!> \param t_3c_mo (P|qp) of the RI atoms atom_first..atom_last, q in two blocks (occupied, virtual),
692!> p in sector blocks
693!> \param n_occ occupied levels in the window
694!> \param n_virt virtual levels in the window
695!> \param sizes_p the p blocks, occupied blocks first
696!> \param n_block_occ occupied blocks of p
697!> \param atom_first first RI atom of the batch
698!> \param atom_last last RI atom of the batch
699!> \param fm_S_ij slab of the ij pairs; the rows of the batch are written
700!> \param fm_S_ia slab of the ia pairs; the rows of the batch are written
701!> \param fm_S_ab slab of the ab pairs; the rows of the batch are written
702! **************************************************************************************************
703 SUBROUTINE link_slab_rows(bs_env, blacs_env, dbcsr_dist, t_3c_mo, n_occ, n_virt, sizes_p, n_block_occ, atom_first, atom_last, &
704 fm_S_ij, fm_S_ia, fm_S_ab)
705 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
706 TYPE(cp_blacs_env_type), POINTER :: blacs_env
707 TYPE(dbcsr_distribution_type), POINTER :: dbcsr_dist
708 TYPE(dbt_type), INTENT(INOUT) :: t_3c_mo
709 INTEGER, INTENT(IN) :: n_occ, n_virt
710 INTEGER, DIMENSION(:), INTENT(IN) :: sizes_p
711 INTEGER, INTENT(IN) :: n_block_occ, atom_first, atom_last
712 TYPE(cp_fm_type), INTENT(IN) :: fm_s_ij, fm_s_ia, fm_s_ab
713
714 CHARACTER(LEN=*), PARAMETER :: routinen = 'link_slab_rows'
715 INTEGER, PARAMETER :: pair_ab = 3, pair_ia = 2, pair_ij = 1
716
717 INTEGER :: col_block, group_handle, handle, i_pair, my_rank, n_atom_batch, n_block_p, &
718 n_ri_batch, npcol, nprow, owner_rank, p_block, q_block, rank, ri_first, row_block
719 INTEGER, ALLOCATABLE, DIMENSION(:, :) :: cart_coords
720 INTEGER, DIMENSION(3) :: blk_index, blk_size, n_col_blocks, &
721 n_cols_pair, p_block_offset, &
722 q_block_of_pair
723 INTEGER, DIMENSION(:), POINTER :: col_dist, col_sizes, row_dist, row_sizes
724 INTEGER, DIMENSION(:, :), POINTER :: pgrid
725 LOGICAL :: found
726 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :) :: block_pqp
727 TYPE(cp_fm_struct_type), POINTER :: struct_batch
728 TYPE(cp_fm_type) :: fm_batch
729 TYPE(dbcsr_distribution_type), DIMENSION(3) :: dist_global, dist_group
730 TYPE(dbcsr_type), DIMENSION(3) :: mat_global, mat_group
731 TYPE(dbt_iterator_type) :: iter
732
733 CALL timeset(routinen, handle)
734
735 NULLIFY (col_dist, col_sizes, pgrid, row_dist, row_sizes, struct_batch)
736 ri_first = bs_env%i_RI_start_from_atom(atom_first)
737 n_ri_batch = bs_env%i_RI_end_from_atom(atom_last) - ri_first + 1
738 n_atom_batch = atom_last - atom_first + 1
739 n_block_p = SIZE(sizes_p)
740 n_cols_pair = [n_occ*n_occ, n_occ*n_virt, n_virt*n_virt]
741 n_col_blocks = [n_block_occ, n_block_occ, n_block_p - n_block_occ]
742 q_block_of_pair = [1, 2, 2]
743 ! column block col_block of a pair type is the p block p_block = p_block_offset + col_block
744 p_block_offset = [0, 0, n_block_occ]
745
746 ! the group grid of the tensor as the process grid of the local matrices, its ranks placed by
747 ! their coordinates, so that every block the tensor holds is local to its matrix
748 associate(cart => t_3c_mo%pgrid%mp_comm_2d)
749 my_rank = cart%mepos
750 group_handle = cart%get_handle()
751 nprow = cart%num_pe_cart(1)
752 npcol = cart%num_pe_cart(2)
753 ALLOCATE (cart_coords(2, 0:cart%num_pe - 1), pgrid(0:nprow - 1, 0:npcol - 1))
754 DO rank = 0, cart%num_pe - 1
755 CALL cart%coords(rank, cart_coords(:, rank))
756 pgrid(cart_coords(1, rank), cart_coords(2, rank)) = rank
757 END DO
758 END associate
759 ALLOCATE (row_dist(n_atom_batch), row_sizes(n_atom_batch))
760 DO row_block = 1, n_atom_batch
761 CALL dbt_get_stored_coordinates(t_3c_mo, [atom_first + row_block - 1, 1, 1], owner_rank)
762 row_dist(row_block) = cart_coords(1, owner_rank)
763 row_sizes(row_block) = bs_env%sizes_RI(atom_first + row_block - 1)
764 END DO
765 DO i_pair = 1, 3
766 ALLOCATE (col_dist(n_col_blocks(i_pair)), col_sizes(n_col_blocks(i_pair)))
767 DO col_block = 1, n_col_blocks(i_pair)
768 p_block = p_block_offset(i_pair) + col_block
769 CALL dbt_get_stored_coordinates(t_3c_mo, [atom_first, q_block_of_pair(i_pair), p_block], owner_rank)
770 col_dist(col_block) = cart_coords(2, owner_rank)
771 ! the q range of the pair type times the p block
772 col_sizes(col_block) = merge(n_occ, n_virt, i_pair == pair_ij)*sizes_p(p_block)
773 END DO
774 CALL dbcsr_distribution_new(dist_group(i_pair), group=group_handle, pgrid=pgrid, &
775 row_dist=row_dist, col_dist=col_dist)
776 CALL dbcsr_create(mat_group(i_pair), name="(P|qp) batch, group", dist=dist_group(i_pair), &
777 matrix_type=dbcsr_type_no_symmetry, row_blk_size=row_sizes, col_blk_size=col_sizes)
778 ! the threaded put_block below cannot create the work matrix itself
779 CALL dbcsr_work_create(mat_group(i_pair), work_mutable=.true.)
780 ! the global matrix of the batch, round robin over the process grid of the slabs
781 CALL link_matrix_round_robin(dbcsr_dist, row_sizes, col_sizes, "(P|qp) batch", dist_global(i_pair), mat_global(i_pair))
782 DEALLOCATE (col_dist, col_sizes)
783 END DO
784 DEALLOCATE (pgrid, cart_coords, row_dist, row_sizes)
785
786 ! the local (P, q, p) blocks as (P, qp) blocks, q fastest, the column order of the slabs; the
787 ! iterator needs the full thread team
788!$OMP PARALLEL DEFAULT(NONE) SHARED(t_3c_mo, mat_group, n_block_occ, atom_first, p_block_offset, my_rank) &
789!$OMP PRIVATE(iter, blk_index, blk_size, block_Pqp, found, q_block, p_block, i_pair, row_block, col_block, owner_rank)
790 CALL dbt_iterator_start(iter, t_3c_mo)
791 DO WHILE (dbt_iterator_blocks_left(iter))
792 CALL dbt_iterator_next_block(iter, blk_index, blk_size=blk_size)
793 q_block = blk_index(2)
794 p_block = blk_index(3)
795 ! pair types of the (q block, p block) blocks: (occ, occ) ij, (virt, occ) ia, (virt, virt) ab;
796 ! (occ, virt) is the ai block, which no slab holds
797 IF (q_block == 1 .AND. p_block > n_block_occ) cycle
798 IF (q_block == 1) THEN
799 i_pair = pair_ij
800 ELSE IF (p_block <= n_block_occ) THEN
801 i_pair = pair_ia
802 ELSE
803 i_pair = pair_ab
804 END IF
805 ALLOCATE (block_pqp(blk_size(1), blk_size(2), blk_size(3)))
806 CALL dbt_get_block(t_3c_mo, blk_index, blk_size, block_pqp, found)
807 IF (found) THEN
808 row_block = blk_index(1) - atom_first + 1
809 col_block = p_block - p_block_offset(i_pair)
810 CALL dbcsr_get_stored_coordinates(mat_group(i_pair), row_block, col_block, owner_rank)
811 ! the group matrix was laid out by the tensor's coordinates: the rank holding the block owns it
812 IF (owner_rank /= my_rank) cpabort("(P|qp) block is not local to its group matrix")
813 CALL dbcsr_put_block(mat_group(i_pair), row_block, col_block, &
814 reshape(block_pqp, [blk_size(1), blk_size(2)*blk_size(3)]))
815 END IF
816 DEALLOCATE (block_pqp)
817 END DO
818 CALL dbt_iterator_stop(iter)
819!$OMP END PARALLEL
820
821 DO i_pair = 1, 3
822 CALL dbcsr_finalize(mat_group(i_pair))
823 CALL cp_dbcsr_from_subgroups(mat_group(i_pair), mat_global(i_pair), bs_env%para_env)
824 CALL cp_fm_struct_create(struct_batch, para_env=bs_env%para_env, context=blacs_env, &
825 nrow_global=n_ri_batch, ncol_global=n_cols_pair(i_pair))
826 CALL cp_fm_create(fm_batch, struct_batch)
827 CALL cp_fm_struct_release(struct_batch)
828 CALL copy_dbcsr_to_fm(mat_global(i_pair), fm_batch)
829 ! known bottleneck: cp_fm_to_fm_submat copies column by column, one pdcopy per pair column; the
830 ! memory it takes on single ranks can stay until the end of the run and set their peak. A block
831 ! redistribution of fm_batch into the rows of the slab would replace it
832 SELECT CASE (i_pair)
833 CASE (pair_ij)
834 CALL cp_fm_to_fm_submat(fm_batch, fm_s_ij, n_ri_batch, n_cols_pair(i_pair), 1, 1, ri_first, 1)
835 CASE (pair_ia)
836 CALL cp_fm_to_fm_submat(fm_batch, fm_s_ia, n_ri_batch, n_cols_pair(i_pair), 1, 1, ri_first, 1)
837 CASE (pair_ab)
838 CALL cp_fm_to_fm_submat(fm_batch, fm_s_ab, n_ri_batch, n_cols_pair(i_pair), 1, 1, ri_first, 1)
839 END SELECT
840 CALL cp_fm_release(fm_batch)
841 CALL dbcsr_release(mat_group(i_pair))
842 CALL dbcsr_release(mat_global(i_pair))
843 CALL dbcsr_distribution_release(dist_group(i_pair))
844 CALL dbcsr_distribution_release(dist_global(i_pair))
845 END DO
846
847 CALL timestop(handle)
848
849 END SUBROUTINE link_slab_rows
850
851! **************************************************************************************************
852!> \brief B^P_c = sum_Q [M^-1 V^1/2]_QP (Q|c) for every column c of a slab, in place: the columns pass in
853!> chunks through one RI x chunk work matrix of at most budget_bytes per rank,
854!> max_cols_chunk = budget_bytes N_all/(8 n_RI) with N_all the ranks of the slab
855!> \param fm_M_inv_V_sqrt the RI transform [M^-1 V^1/2]_QP, RI x RI
856!> \param fm_slab (Q|c) on entry, B^P_c on exit
857!> \param n_RI RI functions, the rows of the slab
858!> \param n_cols columns of the slab
859!> \param budget_bytes per-rank budget of the work matrix, which is distributed over the ranks
860! **************************************************************************************************
861 SUBROUTINE apply_ri_transform(fm_M_inv_V_sqrt, fm_slab, n_RI, n_cols, budget_bytes)
862 TYPE(cp_fm_type), INTENT(IN) :: fm_m_inv_v_sqrt, fm_slab
863 INTEGER, INTENT(IN) :: n_ri, n_cols
864 INTEGER(KIND=int_8), INTENT(IN) :: budget_bytes
865
866 CHARACTER(LEN=*), PARAMETER :: routinen = 'apply_ri_transform'
867
868 INTEGER :: col_first, handle, max_cols_chunk, &
869 n_cols_chunk
870 TYPE(cp_fm_struct_type), POINTER :: struct_work
871 TYPE(cp_fm_type) :: fm_work
872
873 CALL timeset(routinen, handle)
874
875 max_cols_chunk = int(max(1_int_8, min(int(n_cols, int_8), &
876 budget_bytes*int(fm_slab%matrix_struct%para_env%num_pe, int_8) &
877 /(8_int_8*int(n_ri, int_8)))))
878 NULLIFY (struct_work)
879 CALL cp_fm_struct_create(struct_work, para_env=fm_slab%matrix_struct%para_env, &
880 context=fm_slab%matrix_struct%context, nrow_global=n_ri, ncol_global=max_cols_chunk)
881 CALL cp_fm_create(fm_work, struct_work)
882 CALL cp_fm_struct_release(struct_work)
883 DO col_first = 1, n_cols, max_cols_chunk
884 n_cols_chunk = min(max_cols_chunk, n_cols - col_first + 1)
885 ! work_P,c = sum_Q [M^-1 V^1/2]_QP slab_Q,c for the columns c of this chunk, then back into the
886 ! slab; the copy back is column by column (one pdcopy each), see link_slab_rows
887 CALL parallel_gemm("T", "N", n_ri, n_cols_chunk, n_ri, 1.0_dp, fm_m_inv_v_sqrt, fm_slab, 0.0_dp, fm_work, &
888 b_first_col=col_first, c_first_col=1)
889 CALL cp_fm_to_fm_submat(fm_work, fm_slab, nrow=n_ri, ncol=n_cols_chunk, s_firstrow=1, s_firstcol=1, &
890 t_firstrow=1, t_firstcol=col_first)
891 END DO
892 CALL cp_fm_release(fm_work)
893
894 CALL timestop(handle)
895
896 END SUBROUTINE apply_ri_transform
897
898END MODULE bse_bs_env_link
static GRID_HOST_DEVICE int modulo(int a, int m)
Equivalent of Fortran's MODULO, which always return a positive number. https://gcc....
Reads the &BSE input section into the BSE environment; called by read_mp2_section with the section un...
Definition bse_input.F:15
subroutine, public read_bse_section(bse_section, bse_env)
Reads every keyword of the &BSE section into the settings of the BSE environment.
Definition bse_input.F:38
Main routines for GW + Bethe-Salpeter for computing electronic excitations: a GW path prepares the BS...
Definition bse_main.F:16
subroutine, public prepare_bse_env_from_bandstructure_gw(bse_env, fm_s_ia, fm_s_ij, fm_s_ab, fm_eps_inv, eigenval_qp, eigenval_dft, homo_full, homo_red, virt_red, dimen_ri, qs_env, unit_nr)
Entry for the BSE of PROPERTIESBANDSTRUCTUREGW (PROPERTIESBSE), which hands over window-sized slabs a...
Definition bse_main.F:192
subroutine, public bse_solve(bse_env, mo_coeff, unit_nr)
Solves the BSE on a prepared environment: input normalisation, the screened slabs from [1+Q(0)]^-1,...
Definition bse_main.F:404
Matrix-free application of the BSE matrices A and B to trial vectors from RI slabs that are sliced al...
Definition bse_matvec.F:14
real(kind=dp) function, public bse_memory_left_gb(memory_per_proc_gb, para_env)
What MEMORY_PER_PROC leaves to a step: the resolved memory per MPI rank minus the memory the rank hol...
Definition bse_matvec.F:126
subroutine, public bse_resolve_memory_per_proc(memory_per_proc_gb, para_env, unit_nr)
MEMORY_PER_PROC of the BSE as a number: the keyword's value, or for -1 the memory the rank holds plus...
Definition bse_matvec.F:94
The BSE environment: the settings of the &BSE section and the state a GW path prepares for the solver...
Definition bse_types.F:14
subroutine, public bse_env_release(bse_env)
Releases the state a GW path prepared and nullifies the QS pointers; the settings stay,...
Definition bse_types.F:136
Auxiliary routines for GW + Bethe-Salpeter for computing electronic excitations.
Definition bse_util.F:13
subroutine, public adapt_bse_screening(bse_env, unit_nr)
Resolve SCREENING_IN_W and its factor before constructing the dielectric matrix or slabs.
Definition bse_util.F:1121
subroutine, public bse_window_from_dft(eigenval_scf, n_mo, homo, cutoff_occ, cutoff_empty, window, warn)
The active MO window on the DFT axis: one absolute MO range for every spin; when the spin windows dif...
Definition bse_util.F:576
methods related to the blacs parallel environment
subroutine, public dbcsr_distribution_release(dist)
...
subroutine, public dbcsr_distribution_new(dist, template, group, pgrid, row_dist, col_dist, reuse_arrays)
...
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_get_stored_coordinates(matrix, row, column, processor)
...
subroutine, public dbcsr_work_create(matrix, nblks_guess, sizedata_guess, n, work_mutable)
...
subroutine, public dbcsr_finalize(matrix)
...
subroutine, public dbcsr_release(matrix)
...
subroutine, public dbcsr_put_block(matrix, row, col, block, summation)
...
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)
...
DBCSR operations in CP2K.
subroutine, public copy_dbcsr_to_fm(matrix, fm, plan, check_plan)
Copy a DBCSR matrix to a BLACS matrix.
Redistribution of DBCSR blocks and distributed matrix slices.
subroutine, public cp_dbcsr_from_subgroups(mat_local, mat_global, para_env, row_mapping, copy_only)
Sum subgroup blocks into the global layout, or copy mapped blocks.
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....
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_power(matrix, work, exponent, threshold, n_dependent, verbose, eigvals)
...
represent the structure of a full matrix
subroutine, public cp_fm_struct_create(fmstruct, para_env, context, nrow_global, ncol_global, nrow_block, ncol_block, descriptor, first_p_pos, local_leading_dimension, template_fmstruct, square_blocks, force_block)
allocates and initializes a full matrix structure
subroutine, public cp_fm_struct_release(fmstruct)
releases a full matrix structure
represent a full matrix distributed on many processors
Definition cp_fm_types.F:15
subroutine, public cp_fm_to_fm_submat(msource, mtarget, nrow, ncol, s_firstrow, s_firstcol, t_firstrow, t_firstcol)
copy just a part ot the 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
various routines to log and control the output. The idea is that decisions about where to log should ...
integer function, public cp_logger_get_default_io_unit(logger)
returns the unit nr for the ionode (-1 on all other processors) skips as well checks if the procs cal...
type(cp_logger_type) function, pointer, public cp_get_default_logger()
returns the default logger
This is the start of a dbt_api, all publically needed functions are exported here....
Definition dbt_api.F:17
Redistribute matrices between full-matrix and tensor process layouts.
subroutine, public cp_fm_to_dbt(fm_global, mat_global, mat_local, tensor, para_env, num_pe_sub, eps_filter, block_row_ranges)
...
Routines from paper [Graml2024].
subroutine, public compute_3c_integrals(qs_env, bs_env, t_3c, atoms_ao_1, atoms_ao_2, atoms_ri)
...
Full-matrix operations not provided by the CP2K FM packages.
Definition gw_utils_fm.F:13
collects all constants needed in input so that they can be used without circular dependencies
integer, parameter, public bse_screening_w0
objects that represent the structure of input sections and the data contained in an input section
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public int_8
Definition kinds.F:54
integer, parameter, public dp
Definition kinds.F:34
2- and 3-center electron repulsion integral routines based on libint2 Currently available operators: ...
Common selection and union operations for contiguous molecular-orbital windows.
Definition mo_window.F:11
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:576
basic linear algebra operations for full matrixes
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.
Utility methods to build 3-center integral tensors of various types.
subroutine, public create_2c_tensor(t2c, dist_1, dist_2, pgrid, sizes_1, sizes_2, order, name)
...
subroutine, public create_3c_tensor(t3c, dist_1, dist_2, dist_3, pgrid, sizes_1, sizes_2, sizes_3, map1, map2, name)
...
Utility methods to build 3-center integral tensors of various types.
Definition qs_tensors.F:11
subroutine, public neighbor_list_3c_destroy(ijk_list)
Destroy 3c neighborlist.
Definition qs_tensors.F:381
Settings of the &BSE section (read_bse_section, re-read before every BSE run, normalised in place by ...
Definition bse_types.F:44
represent a blacs multidimensional parallel environment (for the mpi corrispective see cp_paratypes/m...
keeps the information about the structure of a full matrix
represent a full matrix
type of a logger, at the moment it contains just a print level starting at which level it should be l...