Line data Source code
1 : !--------------------------------------------------------------------------------------------------!
2 : ! CP2K: A general program to perform molecular dynamics simulations !
3 : ! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4 : ! !
5 : ! SPDX-License-Identifier: GPL-2.0-or-later !
6 : !--------------------------------------------------------------------------------------------------!
7 :
8 : ! **************************************************************************************************
9 : !> \brief DBCSR collocation, layouts and panel contractions for real-space resolution of the identity.
10 : ! **************************************************************************************************
11 : MODULE ri_rs_matrices
12 : USE basis_set_types, ONLY: gto_basis_set_p_type,&
13 : gto_basis_set_type
14 : USE cp_dbcsr_api, ONLY: &
15 : dbcsr_add, dbcsr_copy, dbcsr_create, dbcsr_distribution_get, dbcsr_distribution_new, &
16 : dbcsr_distribution_release, dbcsr_distribution_type, dbcsr_finalize, dbcsr_get_data_size, &
17 : dbcsr_get_info, dbcsr_get_occupation, dbcsr_iterator_blocks_left, &
18 : dbcsr_iterator_next_block, dbcsr_iterator_start, dbcsr_iterator_stop, dbcsr_iterator_type, &
19 : dbcsr_multiply, dbcsr_p_type, dbcsr_put_block, dbcsr_release, dbcsr_set, dbcsr_type, &
20 : dbcsr_type_no_symmetry
21 : USE cp_dbcsr_operations, ONLY: copy_fm_to_dbcsr,&
22 : max_elements_per_block
23 : USE cp_fm_types, ONLY: cp_fm_create,&
24 : cp_fm_release,&
25 : cp_fm_to_fm,&
26 : cp_fm_type
27 : USE gw_auto_ri_types, ONLY: auto_ri_type
28 : USE gw_utils_dbcsr, ONLY: hadamard_product_inplace
29 : USE gw_utils_fm, ONLY: fm_contract_ABA
30 : USE kinds, ONLY: dp,&
31 : int_8,&
32 : max_line_length
33 : USE libint_2c_3c, ONLY: libint_potential_type
34 : USE machine, ONLY: m_flush
35 : USE message_passing, ONLY: mp_mem_avail_per_rank_GB,&
36 : mp_para_env_type
37 : USE mp2_ri_2c, ONLY: RI_2c_integral_mat
38 : USE qs_environment_types, ONLY: qs_environment_type
39 : USE ri_rs_types, ONLY: ri_rs_env
40 : USE ri_rs_utils, ONLY: evaluate_ao_basis_on_points
41 : #include "./base/base_uses.f90"
42 :
43 : IMPLICIT NONE
44 : PRIVATE
45 : PUBLIC :: atomic_basis_at_grid_point, setup_square_topology, &
46 : release_dbcsr_topology_and_matrices, reserve_blocks_within_radius, &
47 : print_matrix_occupation, &
48 : contract_grid_panels, mask_grid_blocks_near_panel, &
49 : panel_template_elems, panel_mem_estimate_GB, resolve_grid_panels, &
50 : build_ri_rs_bare_aux_interaction
51 :
52 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'ri_rs_matrices'
53 :
54 : CONTAINS
55 :
56 : ! **************************************************************************************************
57 : !> \brief Builds the grid-by-AO collocation matrix with bounded dense evaluation panels.
58 : !> The molecular path uses atom-aligned row blocks and minimum-image AO values.
59 : !> The Gamma path uses uniform row blocks and sums neighbouring periodic images.
60 : !> \param ri_rs grid, borrowed geometry references and output collocation matrix
61 : !> \param basis_set_AO AO basis sets, one per atomic kind
62 : !> \param matrix_template template for the process grid and AO column block sizes
63 : !> \param para_env parallel environment
64 : !> \param eps_filter block screening threshold
65 : !> \param msg_elem_limit maximum number of elements per DBCSR message
66 : !> \param unit_nr output unit
67 : !> \param periodic_images select neighbouring-image summation for Gamma
68 : ! **************************************************************************************************
69 48 : SUBROUTINE atomic_basis_at_grid_point(ri_rs, basis_set_AO, matrix_template, &
70 : para_env, eps_filter, msg_elem_limit, unit_nr, periodic_images)
71 : TYPE(ri_rs_env), INTENT(INOUT) :: ri_rs
72 : TYPE(gto_basis_set_p_type), INTENT(IN) :: basis_set_AO(:)
73 : TYPE(dbcsr_type), INTENT(IN) :: matrix_template
74 : TYPE(mp_para_env_type), POINTER :: para_env
75 : REAL(KIND=dp), INTENT(IN) :: eps_filter
76 : INTEGER(KIND=int_8), INTENT(IN) :: msg_elem_limit
77 : INTEGER, INTENT(IN) :: unit_nr
78 : LOGICAL, INTENT(IN) :: periodic_images
79 :
80 : CHARACTER(LEN=*), PARAMETER :: routineN = 'atomic_basis_at_grid_point'
81 : INTEGER, PARAMETER :: grid_panel_size = 4096
82 :
83 : INTEGER :: blocks_per_panel, bs_eff, c_size, first_block, handle, i, i_blk, ia, iatom, &
84 : ikind, last_block, n_grid, natom, npcol, nprow, num_grid_chunks, panel_size, panel_start, &
85 : r_end, r_start, remaining, row_first, row_last, run, safe_max
86 48 : INTEGER, ALLOCATABLE :: blk_row_start(:)
87 48 : INTEGER, POINTER :: c_blk_sizes(:), col_dist(:), &
88 48 : r_blk_sizes(:), row_dist(:)
89 : REAL(KIND=dp) :: cutoff_squared
90 48 : REAL(KIND=dp), ALLOCATABLE :: atom_col_buffer(:, :)
91 : TYPE(dbcsr_distribution_type) :: dist, template_dist
92 : TYPE(gto_basis_set_type), POINTER :: ao_basis
93 :
94 48 : CALL timeset(routineN, handle)
95 48 : CPASSERT(ASSOCIATED(ri_rs%cell))
96 48 : CPASSERT(ASSOCIATED(para_env))
97 48 : CPASSERT(ASSOCIATED(ri_rs%particle_set))
98 48 : CALL dbcsr_get_info(matrix_template, distribution=template_dist, col_blk_size=c_blk_sizes)
99 48 : natom = SIZE(c_blk_sizes)
100 48 : n_grid = SIZE(ri_rs%grid_points, 2)
101 48 : CPASSERT(SIZE(ri_rs%particle_set) == natom)
102 48 : CPASSERT(n_grid == ri_rs%n_grid_points)
103 48 : CALL dbcsr_distribution_get(template_dist, nprows=nprow, npcols=npcol)
104 :
105 48 : bs_eff = max_elements_per_block
106 48 : IF (.NOT. periodic_images) THEN
107 48 : safe_max = INT(msg_elem_limit*MAX(MIN(nprow, npcol), 1)/n_grid/2)
108 48 : bs_eff = MAX(1, MIN(bs_eff, MAX(1, safe_max)))
109 : END IF
110 48 : IF (periodic_images) THEN
111 0 : num_grid_chunks = n_grid/bs_eff
112 0 : IF (MOD(n_grid, bs_eff) /= 0) num_grid_chunks = num_grid_chunks + 1
113 : ELSE
114 48 : num_grid_chunks = 0
115 166 : DO ia = 1, natom
116 118 : run = ri_rs%grid_atom_boundaries(ia + 1) - ri_rs%grid_atom_boundaries(ia)
117 166 : IF (run > 0) num_grid_chunks = num_grid_chunks + (run + bs_eff - 1)/bs_eff
118 : END DO
119 : END IF
120 192 : ALLOCATE (r_blk_sizes(num_grid_chunks), blk_row_start(num_grid_chunks))
121 48 : i_blk = 0
122 48 : r_start = 1
123 214 : DO ia = 1, MERGE(1, natom, periodic_images)
124 118 : IF (periodic_images) THEN
125 : remaining = n_grid
126 : ELSE
127 118 : remaining = ri_rs%grid_atom_boundaries(ia + 1) - ri_rs%grid_atom_boundaries(ia)
128 : END IF
129 780 : DO WHILE (remaining > 0)
130 614 : i_blk = i_blk + 1
131 614 : r_blk_sizes(i_blk) = MIN(bs_eff, remaining)
132 614 : blk_row_start(i_blk) = r_start
133 614 : r_start = r_start + r_blk_sizes(i_blk)
134 614 : remaining = remaining - r_blk_sizes(i_blk)
135 : END DO
136 : END DO
137 :
138 48 : IF (.NOT. periodic_images) THEN
139 48 : IF (unit_nr > 0) THEN
140 24 : WRITE (unit_nr, '(T2,A,T71,I12)') 'RI-RS grid row-blocks of ϕ_μ(r_l)', num_grid_chunks
141 24 : WRITE (unit_nr, '(T2,A,T69,I12)') 'RI-RS grid points per block (max)', bs_eff
142 : END IF
143 48 : IF (ri_rs%cutoff_radius_v_w > 0.0_dp .OR. ri_rs%cutoff_radius_w0 > 0.0_dp) THEN
144 8 : IF (ALLOCATED(ri_rs%chunk_centroids)) DEALLOCATE (ri_rs%chunk_centroids)
145 24 : ALLOCATE (ri_rs%chunk_centroids(3, num_grid_chunks))
146 110 : DO i_blk = 1, num_grid_chunks
147 102 : r_start = blk_row_start(i_blk)
148 102 : r_end = r_start + r_blk_sizes(i_blk) - 1
149 456 : DO i = 1, 3
150 : ri_rs%chunk_centroids(i, i_blk) = &
151 8952 : SUM(ri_rs%grid_points(i, r_start:r_end))/REAL(r_blk_sizes(i_blk), dp)
152 : END DO
153 : END DO
154 : END IF
155 : END IF
156 :
157 192 : ALLOCATE (row_dist(num_grid_chunks), col_dist(natom))
158 662 : DO i = 1, num_grid_chunks
159 662 : row_dist(i) = MOD(i - 1, nprow)
160 : END DO
161 166 : DO i = 1, natom
162 166 : col_dist(i) = MOD(i - 1, npcol)
163 : END DO
164 48 : CALL dbcsr_distribution_new(dist, template=template_dist, row_dist=row_dist, col_dist=col_dist)
165 : CALL dbcsr_create(ri_rs%mat_phi_mu_l, name='phi_val_sparse', dist=dist, &
166 48 : matrix_type=dbcsr_type_no_symmetry, row_blk_size=r_blk_sizes, col_blk_size=c_blk_sizes)
167 :
168 662 : blocks_per_panel = MAX(1, grid_panel_size/MAXVAL(r_blk_sizes))
169 48 : DO iatom = para_env%mepos + 1, natom, para_env%num_pe
170 59 : c_size = c_blk_sizes(iatom)
171 999 : ALLOCATE (atom_col_buffer(MIN(n_grid, blocks_per_panel*MAXVAL(r_blk_sizes)), c_size))
172 59 : cutoff_squared = ri_rs%radius_ao_per_atom(iatom)**2
173 59 : IF (.NOT. periodic_images .AND. ri_rs%cutoff_radius_ri_ao > 0.0_dp) THEN
174 3 : cutoff_squared = ri_rs%cutoff_radius_ri_ao**2
175 : END IF
176 59 : ikind = ri_rs%particle_set(iatom)%atomic_kind%kind_number
177 59 : ao_basis => basis_set_AO(ikind)%gto_basis_set
178 118 : DO first_block = 1, num_grid_chunks, blocks_per_panel
179 59 : last_block = MIN(first_block + blocks_per_panel - 1, num_grid_chunks)
180 59 : panel_start = blk_row_start(first_block)
181 59 : panel_size = blk_row_start(last_block) + r_blk_sizes(last_block) - panel_start
182 79323 : atom_col_buffer(:panel_size, :) = 0.0_dp
183 : CALL evaluate_ao_basis_on_points(atom_col_buffer(:panel_size, :), &
184 : ri_rs%grid_points(:, panel_start:panel_start + panel_size - 1), &
185 : ao_basis, ri_rs%particle_set(iatom)%r, ri_rs%cell, &
186 59 : cutoff_squared=cutoff_squared, periodic_images=periodic_images)
187 881 : DO i_blk = first_block, last_block
188 763 : row_first = blk_row_start(i_blk) - panel_start + 1
189 763 : row_last = row_first + r_blk_sizes(i_blk) - 1
190 82708 : IF (MAXVAL(ABS(atom_col_buffer(row_first:row_last, :))) > eps_filter) THEN
191 : CALL dbcsr_put_block(ri_rs%mat_phi_mu_l, row=i_blk, col=iatom, &
192 751 : block=atom_col_buffer(row_first:row_last, :))
193 : END IF
194 : END DO
195 : END DO
196 59 : DEALLOCATE (atom_col_buffer)
197 : END DO
198 48 : CALL dbcsr_finalize(ri_rs%mat_phi_mu_l)
199 48 : DEALLOCATE (r_blk_sizes, row_dist, col_dist)
200 48 : CALL dbcsr_distribution_release(dist)
201 48 : CALL timestop(handle)
202 240 : END SUBROUTINE atomic_basis_at_grid_point
203 :
204 : ! **************************************************************************************************
205 : !> \brief Creates a square topology from a selected row or column layout.
206 : !> \param matrix_template source matrix
207 : !> \param dim_type ROW or COL
208 : !> \param square_dist output distribution
209 : !> \param blk_sizes borrowed block sizes
210 : !> \param mapped_dist owned mapping in the other process-grid direction
211 : ! **************************************************************************************************
212 36060 : SUBROUTINE setup_square_topology(matrix_template, dim_type, square_dist, blk_sizes, mapped_dist)
213 :
214 : TYPE(dbcsr_type), INTENT(IN) :: matrix_template
215 : CHARACTER(LEN=*), INTENT(IN) :: dim_type
216 : TYPE(dbcsr_distribution_type), INTENT(OUT) :: square_dist
217 : INTEGER, DIMENSION(:), INTENT(OUT), POINTER :: blk_sizes, mapped_dist
218 :
219 : CHARACTER(LEN=*), PARAMETER :: routineN = 'setup_square_topology'
220 :
221 : INTEGER :: handle, i, np, npcols, nprows
222 12020 : INTEGER, DIMENSION(:), POINTER :: col_blk, col_dist, row_blk, row_dist
223 : TYPE(dbcsr_distribution_type) :: dist_template
224 :
225 12020 : CALL timeset(routineN, handle)
226 :
227 : CALL dbcsr_get_info(matrix_template, distribution=dist_template, &
228 12020 : row_blk_size=row_blk, col_blk_size=col_blk)
229 : CALL dbcsr_distribution_get(dist_template, row_dist=row_dist, col_dist=col_dist, &
230 12020 : nprows=nprows, npcols=npcols)
231 :
232 12020 : IF (TRIM(dim_type) == 'ROW') THEN
233 : ! Creates ROW x ROW (e.g., Grid x Grid from mat_phi_mu_l)
234 600 : blk_sizes => row_blk
235 600 : np = npcols
236 1800 : ALLOCATE (mapped_dist(SIZE(blk_sizes)))
237 6960 : DO i = 1, SIZE(blk_sizes)
238 6960 : mapped_dist(i) = MOD(i - 1, np)
239 : END DO
240 : CALL dbcsr_distribution_new(square_dist, template=dist_template, &
241 600 : row_dist=row_dist, col_dist=mapped_dist)
242 :
243 11420 : ELSE IF (TRIM(dim_type) == 'COL') THEN
244 : ! Creates COL x COL (e.g., Aux x Aux from mat_Z_lP)
245 11420 : blk_sizes => col_blk
246 11420 : np = nprows
247 34260 : ALLOCATE (mapped_dist(SIZE(blk_sizes)))
248 35414 : DO i = 1, SIZE(blk_sizes)
249 35414 : mapped_dist(i) = MOD(i - 1, np)
250 : END DO
251 : CALL dbcsr_distribution_new(square_dist, template=dist_template, &
252 11420 : row_dist=mapped_dist, col_dist=col_dist)
253 : END IF
254 :
255 12020 : CALL timestop(handle)
256 :
257 12020 : END SUBROUTINE setup_square_topology
258 :
259 : ! **************************************************************************************************
260 : !> \brief Releases RI-RS topology mappings and temporary DBCSR matrices.
261 : !> \param dist distribution to release
262 : !> \param mapped_dist owned mapping to deallocate
263 : !> \param m1 optional matrix
264 : !> \param m2 optional matrix
265 : !> \param m3 optional matrix
266 : !> \param m4 optional matrix
267 : ! **************************************************************************************************
268 16620 : SUBROUTINE release_dbcsr_topology_and_matrices(dist, mapped_dist, m1, m2, m3, m4)
269 :
270 : TYPE(dbcsr_distribution_type), INTENT(INOUT), &
271 : OPTIONAL :: dist
272 : INTEGER, DIMENSION(:), INTENT(INOUT), OPTIONAL, &
273 : POINTER :: mapped_dist
274 : TYPE(dbcsr_type), INTENT(INOUT), OPTIONAL :: m1, m2, m3, m4
275 :
276 : CHARACTER(LEN=*), PARAMETER :: routineN = 'release_dbcsr_topology_and_matrices'
277 :
278 : INTEGER :: handle
279 :
280 16620 : CALL timeset(routineN, handle)
281 :
282 16620 : IF (PRESENT(dist)) CALL dbcsr_distribution_release(dist)
283 16620 : IF (PRESENT(mapped_dist)) THEN
284 12020 : IF (ASSOCIATED(mapped_dist)) THEN
285 12020 : DEALLOCATE (mapped_dist)
286 : NULLIFY (mapped_dist)
287 : END IF
288 : END IF
289 16620 : IF (PRESENT(m1)) CALL dbcsr_release(m1)
290 16620 : IF (PRESENT(m2)) CALL dbcsr_release(m2)
291 16620 : IF (PRESENT(m3)) CALL dbcsr_release(m3)
292 16620 : IF (PRESENT(m4)) CALL dbcsr_release(m4)
293 :
294 16620 : CALL timestop(handle)
295 :
296 16620 : END SUBROUTINE release_dbcsr_topology_and_matrices
297 :
298 : ! **************************************************************************************************
299 : !> \brief Reserves grid blocks with block-centre distances within a fixed radius.
300 : !> \param matrix target matrix
301 : !> \param centers block-centre coordinates
302 : !> \param radius truncation radius
303 : ! **************************************************************************************************
304 6 : SUBROUTINE reserve_blocks_within_radius(matrix, centers, radius)
305 :
306 : TYPE(dbcsr_type), INTENT(INOUT) :: matrix
307 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: centers
308 : REAL(KIND=dp), INTENT(IN) :: radius
309 :
310 : CHARACTER(LEN=*), PARAMETER :: routineN = 'reserve_blocks_within_radius'
311 :
312 : INTEGER :: handle, i, j, my_pcol, my_prow, &
313 : nblkcols, nblkrows
314 6 : INTEGER, DIMENSION(:), POINTER :: col_blk, col_dist, row_blk, row_dist
315 : REAL(KIND=dp) :: radius2
316 6 : REAL(KIND=dp), ALLOCATABLE :: zero_blk(:, :)
317 : TYPE(dbcsr_distribution_type) :: dist
318 :
319 6 : CALL timeset(routineN, handle)
320 :
321 : CALL dbcsr_get_info(matrix, nblkrows_total=nblkrows, nblkcols_total=nblkcols, &
322 6 : row_blk_size=row_blk, col_blk_size=col_blk, distribution=dist)
323 : CALL dbcsr_distribution_get(dist, row_dist=row_dist, col_dist=col_dist, &
324 6 : myprow=my_prow, mypcol=my_pcol)
325 6 : CPASSERT(nblkrows == SIZE(centers, 2))
326 6 : CPASSERT(nblkcols == SIZE(centers, 2))
327 :
328 6 : radius2 = radius**2
329 144 : ALLOCATE (zero_blk(MAXVAL(row_blk(1:nblkrows)), MAXVAL(col_blk(1:nblkcols))))
330 6 : zero_blk(:, :) = 0.0_dp
331 :
332 66 : DO i = 1, nblkrows
333 60 : IF (row_dist(i) /= my_prow) CYCLE
334 336 : DO j = 1, nblkcols
335 300 : IF (col_dist(j) /= my_pcol) CYCLE
336 1260 : IF (DOT_PRODUCT(centers(:, i) - centers(:, j), centers(:, i) - centers(:, j)) <= radius2) THEN
337 168 : CALL dbcsr_put_block(matrix, i, j, zero_blk(1:row_blk(i), 1:col_blk(j)))
338 : END IF
339 : END DO
340 : END DO
341 6 : CALL dbcsr_finalize(matrix)
342 :
343 6 : DEALLOCATE (zero_blk)
344 6 : CALL timestop(handle)
345 :
346 6 : END SUBROUTINE reserve_blocks_within_radius
347 :
348 : ! **************************************************************************************************
349 : !> \brief Prints the percentage of non-zero elements in a distributed RI-RS matrix.
350 : !> \param matrix Distributed matrix whose occupation is reported
351 : !> \param label Mathematical matrix label used in the output
352 : !> \param para_env communicator shared by all matrix ranks
353 : !> \param unit_nr output unit; nonpositive values suppress printing
354 : !> \param msg_elem_limit maximum number of elements per DBCSR message
355 : !> \param suffix Optional text appended to the matrix label
356 : ! **************************************************************************************************
357 260 : SUBROUTINE print_matrix_occupation(matrix, label, para_env, unit_nr, msg_elem_limit, suffix)
358 :
359 : TYPE(dbcsr_type), INTENT(IN) :: matrix
360 : CHARACTER(LEN=*), INTENT(IN) :: label
361 : TYPE(mp_para_env_type), INTENT(IN) :: para_env
362 : INTEGER, INTENT(IN) :: unit_nr
363 : INTEGER(KIND=int_8), INTENT(IN) :: msg_elem_limit
364 : CHARACTER(LEN=*), INTENT(IN), OPTIONAL :: suffix
365 :
366 : CHARACTER(LEN=*), PARAMETER :: routineN = 'print_matrix_occupation'
367 :
368 : CHARACTER(LEN=32) :: output_format
369 : CHARACTER(LEN=max_line_length) :: msg, output_label
370 : INTEGER :: handle, i, unicode_shift
371 : REAL(KIND=dp) :: frac_2p31, max_loc, occ
372 :
373 260 : CALL timeset(routineN, handle)
374 :
375 260 : occ = dbcsr_get_occupation(matrix)
376 260 : max_loc = REAL(dbcsr_get_data_size(matrix), dp)
377 260 : CALL para_env%max(max_loc)
378 :
379 260 : IF (unit_nr > 0) THEN
380 130 : frac_2p31 = max_loc/REAL(msg_elem_limit, dp)
381 130 : output_label = 'Percentage of non-zero matrix elements in '//TRIM(label)
382 130 : IF (PRESENT(suffix)) output_label = TRIM(output_label)//TRIM(suffix)
383 : ! Fortran counts UTF-8 bytes, whereas the terminal displays each Greek letter in one
384 : ! column. Shift the absolute output tab once for every continuation byte.
385 130 : unicode_shift = 0
386 8171 : DO i = 1, LEN_TRIM(output_label)
387 8171 : IF (IAND(IACHAR(output_label(i:i)), 192) == 128) unicode_shift = unicode_shift + 1
388 : END DO
389 130 : WRITE (output_format, '(A,I0,A)') '(T2,A,T', 72 + unicode_shift, ',F7.2,A)'
390 130 : WRITE (unit_nr, output_format) TRIM(output_label), occ*100.0_dp, ' %'
391 130 : IF (frac_2p31 > 0.5_dp) THEN
392 : WRITE (msg, '(3A,F0.2,A)') &
393 0 : "The largest per-rank message of ", TRIM(label), " reaches ", frac_2p31, &
394 : " of the 32-bit limit that DBCSR uses for its message length. Beyond it the "// &
395 : "length overflows and multiply_cannon fails. Reduce the per-rank block size, "// &
396 0 : "for instance with more MPI ranks."
397 0 : CPWARN(TRIM(msg))
398 : END IF
399 130 : CALL m_flush(unit_nr)
400 : END IF
401 :
402 260 : CALL timestop(handle)
403 :
404 260 : END SUBROUTINE print_matrix_occupation
405 :
406 : ! **************************************************************************************************
407 : !> \brief Marks the grid blocks whose centroid lies within cutoff of the bounding box of the
408 : !> panel [blk0, blk1]'s chunk centroids.
409 : !> \param centroids ...
410 : !> \param blk0 ...
411 : !> \param blk1 ...
412 : !> \param cutoff ...
413 : !> \param used ...
414 : ! **************************************************************************************************
415 46 : SUBROUTINE mask_grid_blocks_near_panel(centroids, blk0, blk1, cutoff, used)
416 :
417 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: centroids
418 : INTEGER, INTENT(IN) :: blk0, blk1
419 : REAL(KIND=dp), INTENT(IN) :: cutoff
420 : LOGICAL, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: used
421 :
422 : CHARACTER(LEN=*), PARAMETER :: routineN = 'mask_grid_blocks_near_panel'
423 :
424 : INTEGER :: c, handle, k
425 : REAL(KIND=dp) :: cutoff2, d2, dx
426 : REAL(KIND=dp), DIMENSION(3) :: hi, lo
427 :
428 46 : CALL timeset(routineN, handle)
429 :
430 46 : cutoff2 = cutoff**2
431 46 : lo(:) = MINVAL(centroids(:, blk0:blk1), DIM=2)
432 46 : hi(:) = MAXVAL(centroids(:, blk0:blk1), DIM=2)
433 :
434 138 : ALLOCATE (used(SIZE(centroids, 2)))
435 1012 : DO c = 1, SIZE(centroids, 2)
436 : d2 = 0.0_dp
437 3864 : DO k = 1, 3
438 2898 : dx = MAX(0.0_dp, lo(k) - centroids(k, c), centroids(k, c) - hi(k))
439 3864 : d2 = d2 + dx*dx
440 : END DO
441 1012 : used(c) = (d2 <= cutoff2)
442 : END DO
443 :
444 46 : CALL timestop(handle)
445 :
446 46 : END SUBROUTINE mask_grid_blocks_near_panel
447 :
448 : ! **************************************************************************************************
449 : !> \brief Exact allocated-element count of the geo template of panel [blk0, blk1]: the very same
450 : !> per-block-pair centroid test as build_geo_template_panel, so this is the true DBCSR
451 : !> data size of A_pan/B_pan (DBCSR stores whole blocks).
452 : !> \param r_blk_sizes ...
453 : !> \param centroids ...
454 : !> \param used ...
455 : !> \param blk0 ...
456 : !> \param blk1 ...
457 : !> \param cutoff ...
458 : !> \param nze_tmpl ...
459 : ! **************************************************************************************************
460 4 : SUBROUTINE panel_template_elems(r_blk_sizes, centroids, used, blk0, blk1, cutoff, nze_tmpl)
461 :
462 : INTEGER, DIMENSION(:), INTENT(IN) :: r_blk_sizes
463 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: centroids
464 : LOGICAL, DIMENSION(:), INTENT(IN) :: used
465 : INTEGER, INTENT(IN) :: blk0, blk1
466 : REAL(KIND=dp), INTENT(IN) :: cutoff
467 : INTEGER(KIND=int_8), INTENT(OUT) :: nze_tmpl
468 :
469 : CHARACTER(LEN=*), PARAMETER :: routineN = 'panel_template_elems'
470 :
471 : INTEGER :: c, handle, ib, n_used
472 4 : INTEGER, ALLOCATABLE, DIMENSION(:) :: used_idx
473 : REAL(KIND=dp) :: cutoff2
474 :
475 4 : CALL timeset(routineN, handle)
476 :
477 : ! Compress the near mask once so the pair loop only visits candidate columns.
478 88 : n_used = COUNT(used)
479 12 : ALLOCATE (used_idx(n_used))
480 4 : n_used = 0
481 88 : DO c = 1, SIZE(used)
482 88 : IF (used(c)) THEN
483 84 : n_used = n_used + 1
484 84 : used_idx(n_used) = c
485 : END IF
486 : END DO
487 :
488 4 : cutoff2 = cutoff**2
489 4 : nze_tmpl = 0_int_8
490 : !$OMP PARALLEL DO DEFAULT(NONE) SHARED(blk0, blk1, n_used, used_idx, centroids, cutoff2, &
491 4 : !$OMP r_blk_sizes) PRIVATE(ib, c) REDUCTION(+:nze_tmpl)
492 : DO ib = blk0, blk1
493 : DO c = 1, n_used
494 : IF (SUM((centroids(:, ib) - centroids(:, used_idx(c)))**2) <= cutoff2) THEN
495 : nze_tmpl = nze_tmpl + INT(r_blk_sizes(ib), int_8)*INT(r_blk_sizes(used_idx(c)), int_8)
496 : END IF
497 : END DO
498 : END DO
499 : !$OMP END PARALLEL DO
500 :
501 4 : CALL timestop(handle)
502 :
503 8 : END SUBROUTINE panel_template_elems
504 :
505 : ! **************************************************************************************************
506 : !> \brief Per-rank peak memory (GB) of one panel step of the neighborhood-restricted
507 : !> contractions: up to three grid x grid panels (two A operands and the shared B operand)
508 : !> plus the grid x RI intermediates (tmp2 and the accumulation operand) and the
509 : !> grid x AO intermediate (tmpA), whose column support is the panel's geometric
510 : !> neighborhood fraction f_near = width/n_grid. Used by planning and memory reporting.
511 : !> \param nze_tmpl ...
512 : !> \param pan_rows ...
513 : !> \param width ...
514 : !> \param n_grid_total ...
515 : !> \param n_RI ...
516 : !> \param n_ao ...
517 : !> \param n_procs ...
518 : !> \param mem_GB ...
519 : ! **************************************************************************************************
520 42 : SUBROUTINE panel_mem_estimate_GB(nze_tmpl, pan_rows, width, n_grid_total, n_RI, n_ao, &
521 : n_procs, mem_GB)
522 :
523 : INTEGER(KIND=int_8), INTENT(IN) :: nze_tmpl
524 : INTEGER, INTENT(IN) :: pan_rows, width, n_grid_total, n_RI, &
525 : n_ao, n_procs
526 : REAL(KIND=dp), INTENT(OUT) :: mem_GB
527 :
528 : REAL(KIND=dp) :: f_near
529 :
530 42 : f_near = REAL(width, dp)/REAL(MAX(n_grid_total, 1), dp)
531 : mem_GB = (3.0_dp*REAL(nze_tmpl, dp) + &
532 : REAL(pan_rows, dp)*f_near*(2.0_dp*REAL(n_RI, dp) + REAL(n_ao, dp)))* &
533 42 : 8.0_dp/REAL(MAX(n_procs, 1), dp)*1.0E-9_dp
534 :
535 42 : END SUBROUTINE panel_mem_estimate_GB
536 :
537 : ! **************************************************************************************************
538 : !> \brief Plans whole-grid-block panels using a message-size target without a cutoff.
539 : !> A positive cutoff additionally checks estimated message and memory costs.
540 : !> A single indivisible cutoff block exceeding an estimated limit is reported
541 : !> and accepted, since the panel cannot be split further.
542 : !> \param msg_elem_limit maximum number of elements per DBCSR message
543 : !> \param r_blk_sizes grid row-block sizes
544 : !> \param min_dim smaller dimension of the DBCSR process grid
545 : !> \param pan_first first grid block of each panel
546 : !> \param pan_last last grid block of each panel
547 : !> \param n_RI auxiliary basis dimension
548 : !> \param n_ao AO basis dimension
549 : !> \param n_procs number of MPI processes
550 : !> \param mem_budget_GB per-rank panel memory budget; nonpositive disables the memory check
551 : !> \param centroids optional grid-block centres
552 : !> \param cutoff optional geometric truncation radius
553 : ! **************************************************************************************************
554 40 : SUBROUTINE plan_grid_panels(msg_elem_limit, r_blk_sizes, min_dim, pan_first, pan_last, &
555 40 : n_RI, n_ao, n_procs, mem_budget_GB, centroids, cutoff)
556 :
557 : INTEGER(KIND=int_8), INTENT(IN) :: msg_elem_limit
558 : INTEGER, DIMENSION(:), INTENT(IN) :: r_blk_sizes
559 : INTEGER, INTENT(IN) :: min_dim
560 : INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: pan_first, pan_last
561 : INTEGER, INTENT(IN) :: n_RI, n_ao, n_procs
562 : REAL(KIND=dp), INTENT(IN) :: mem_budget_GB
563 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN), &
564 : OPTIONAL :: centroids
565 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: cutoff
566 :
567 : CHARACTER(LEN=*), PARAMETER :: routineN = 'plan_grid_panels'
568 :
569 : INTEGER :: blk0, blk1, handle, ib, n_grid_blocks, &
570 : n_grid_total, n_panels, panel_size, &
571 : rows_acc, TARGET, width
572 : INTEGER(KIND=int_8) :: msg, msg_limit, nze_tmpl, side
573 40 : INTEGER, ALLOCATABLE :: tmp_first(:), tmp_last(:)
574 : LOGICAL :: fits, use_cutoff, warned_memory, &
575 : warned_message
576 40 : LOGICAL, ALLOCATABLE :: used(:)
577 : REAL(KIND=dp) :: f_near, mem_GB
578 :
579 40 : CALL timeset(routineN, handle)
580 :
581 40 : use_cutoff = PRESENT(centroids) .AND. PRESENT(cutoff)
582 40 : IF (use_cutoff) use_cutoff = cutoff > 0.0_dp
583 40 : msg_limit = msg_elem_limit/4
584 40 : warned_memory = .FALSE.
585 40 : warned_message = .FALSE.
586 40 : n_grid_blocks = SIZE(r_blk_sizes)
587 570 : n_grid_total = SUM(r_blk_sizes)
588 40 : panel_size = n_grid_total
589 40 : IF (.NOT. use_cutoff) THEN
590 : ! Preserve the original message-derived target and whole-block growth.
591 38 : panel_size = INT(0.5_dp*REAL(msg_elem_limit, dp)*REAL(min_dim, dp)/REAL(n_grid_total, dp))
592 38 : panel_size = MAX(1, MIN(panel_size, n_grid_total))
593 : END IF
594 160 : ALLOCATE (tmp_first(n_grid_blocks), tmp_last(n_grid_blocks))
595 :
596 40 : n_panels = 0
597 40 : blk0 = 1
598 80 : DO WHILE (blk0 <= n_grid_blocks)
599 : TARGET = panel_size
600 2 : DO
601 40 : rows_acc = 0
602 530 : DO ib = blk0, n_grid_blocks
603 530 : rows_acc = rows_acc + r_blk_sizes(ib)
604 530 : blk1 = ib
605 530 : IF (rows_acc >= TARGET) EXIT
606 : END DO
607 40 : IF (.NOT. use_cutoff) EXIT
608 2 : CALL mask_grid_blocks_near_panel(centroids, blk0, blk1, cutoff, used)
609 44 : width = SUM(r_blk_sizes, MASK=used)
610 2 : CALL panel_template_elems(r_blk_sizes, centroids, used, blk0, blk1, cutoff, nze_tmpl)
611 2 : f_near = REAL(width, dp)/REAL(MAX(n_grid_total, 1), dp)
612 2 : side = INT(REAL(rows_acc, dp)*f_near*REAL(MAX(n_RI, n_ao), dp), int_8)
613 2 : msg = MAX(nze_tmpl, side)/INT(MAX(min_dim, 1), int_8)
614 : CALL panel_mem_estimate_GB(nze_tmpl, rows_acc, width, n_grid_total, &
615 2 : n_RI, n_ao, n_procs, mem_GB)
616 2 : fits = msg <= msg_limit
617 2 : IF (mem_budget_GB > 0.0_dp) fits = fits .AND. mem_GB <= mem_budget_GB
618 0 : IF (fits) EXIT
619 0 : IF (blk1 == blk0) THEN
620 0 : IF (msg > msg_limit .AND. .NOT. warned_message) THEN
621 0 : CPWARN("A single RI-RS grid block exceeds the estimated panel message limit.")
622 0 : warned_message = .TRUE.
623 : END IF
624 0 : IF (mem_budget_GB > 0.0_dp .AND. mem_GB > mem_budget_GB .AND. .NOT. warned_memory) THEN
625 0 : CPWARN("A single RI-RS grid block exceeds the estimated panel memory budget.")
626 0 : warned_memory = .TRUE.
627 : END IF
628 : EXIT
629 : END IF
630 : ! Preserve whole blocks and prefer large panels to reduce the number of multiplies.
631 38 : TARGET = MAX(1, MIN(TARGET, rows_acc)/2)
632 : END DO
633 40 : n_panels = n_panels + 1
634 40 : tmp_first(n_panels) = blk0
635 40 : tmp_last(n_panels) = blk1
636 40 : blk0 = blk1 + 1
637 : END DO
638 :
639 160 : ALLOCATE (pan_first, SOURCE=tmp_first(:n_panels))
640 120 : ALLOCATE (pan_last, SOURCE=tmp_last(:n_panels))
641 40 : CALL timestop(handle)
642 :
643 80 : END SUBROUTINE plan_grid_panels
644 :
645 : ! **************************************************************************************************
646 : !> \brief Creates an empty (panel_chunks x neighborhood_chunks) DBCSR matrix with zero blocks
647 : !> pre-allocated only where |centroid(panel_row r) - centroid(column c)| <= cutoff.
648 : !> Used with retain_sparsity=.TRUE. in the subsequent dbcsr_multiply so distant blocks
649 : !> of the grid-basis panels (ϕ G ϕ^T, Z W Z^T, ...) are never computed at all.
650 : !> \param L_pan ...
651 : !> \param L_full ...
652 : !> \param centroids ...
653 : !> \param cutoff ...
654 : !> \param blk0 ...
655 : !> \param A_template ...
656 : !> \param col_map ...
657 : ! **************************************************************************************************
658 62 : SUBROUTINE build_geo_template_panel(L_pan, L_full, centroids, cutoff, blk0, A_template, col_map)
659 : TYPE(dbcsr_type), INTENT(IN) :: L_pan, L_full
660 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN) :: centroids
661 : REAL(KIND=dp), INTENT(IN) :: cutoff
662 : INTEGER, INTENT(IN) :: blk0
663 : TYPE(dbcsr_type), INTENT(OUT) :: A_template
664 : INTEGER, DIMENSION(:), INTENT(IN) :: col_map
665 :
666 : CHARACTER(LEN=*), PARAMETER :: routineN = 'build_geo_template_panel'
667 :
668 : INTEGER :: c, cg, cs, handle, my_pcol, my_prow, &
669 : n_grid_blks, n_pan_blks, npcols, &
670 : nprows, r, rs
671 62 : INTEGER, DIMENSION(:), POINTER :: grid_blk_sizes, pan_blk_sizes
672 : REAL(KIND=dp) :: cutoff2
673 62 : REAL(KIND=dp), ALLOCATABLE :: zero_blk(:, :)
674 : TYPE(dbcsr_distribution_type) :: dist
675 :
676 62 : CALL timeset(routineN, handle)
677 :
678 62 : cutoff2 = cutoff**2
679 62 : CALL dbcsr_get_info(L_pan, nblkrows_total=n_pan_blks, row_blk_size=pan_blk_sizes)
680 62 : CALL dbcsr_get_info(L_full, nblkrows_total=n_grid_blks, row_blk_size=grid_blk_sizes)
681 :
682 : ! create_product_matrix assigns row r to process MOD(r-1,nprows) and
683 : ! col c to MOD(c-1,npcols), so we can determine local ownership analytically.
684 62 : CALL create_product_matrix(L_pan, L_full, 'T', A_template)
685 62 : CALL dbcsr_get_info(A_template, distribution=dist)
686 : CALL dbcsr_distribution_get(dist, nprows=nprows, npcols=npcols, &
687 62 : myprow=my_prow, mypcol=my_pcol)
688 :
689 : ALLOCATE (zero_blk(MAXVAL(pan_blk_sizes(1:n_pan_blks)), &
690 2852 : MAXVAL(grid_blk_sizes(1:n_grid_blks))))
691 62 : zero_blk(:, :) = 0.0_dp
692 :
693 1364 : DO r = 1, n_pan_blks
694 1302 : IF (MOD(r - 1, nprows) /= my_prow) CYCLE
695 651 : rs = pan_blk_sizes(r)
696 14384 : DO c = 1, n_grid_blks
697 13671 : IF (MOD(c - 1, npcols) /= my_pcol) CYCLE
698 13671 : cg = col_map(c)
699 13671 : IF ((centroids(1, blk0 + r - 1) - centroids(1, cg))**2 + &
700 : (centroids(2, blk0 + r - 1) - centroids(2, cg))**2 + &
701 1302 : (centroids(3, blk0 + r - 1) - centroids(3, cg))**2 <= cutoff2) THEN
702 12741 : cs = grid_blk_sizes(c)
703 12741 : CALL dbcsr_put_block(A_template, r, c, zero_blk(1:rs, 1:cs))
704 : END IF
705 : END DO
706 : END DO
707 62 : CALL dbcsr_finalize(A_template)
708 :
709 62 : DEALLOCATE (zero_blk)
710 62 : CALL timestop(handle)
711 :
712 62 : END SUBROUTINE build_geo_template_panel
713 :
714 : ! **************************************************************************************************
715 : !> \brief Slices a contiguous range of grid row-blocks [blk0, blk1] out of a (grid x n) DBCSR
716 : !> matrix into a new (P x n) panel matrix: iterate the source's local blocks, put the
717 : !> in-range ones into the panel with a remapped row-block index, then finalize. Row-block
718 : !> index i of the panel corresponds to source row-block blk0+i-1.
719 : !> \param mat_full ...
720 : !> \param blk0 ...
721 : !> \param blk1 ...
722 : !> \param mat_panel ...
723 : ! **************************************************************************************************
724 3012 : SUBROUTINE extract_grid_panel(mat_full, blk0, blk1, mat_panel)
725 :
726 : TYPE(dbcsr_type), INTENT(INOUT) :: mat_full
727 : INTEGER, INTENT(IN) :: blk0, blk1
728 : TYPE(dbcsr_type), INTENT(OUT) :: mat_panel
729 :
730 : CHARACTER(LEN=*), PARAMETER :: routineN = 'extract_grid_panel'
731 :
732 : INTEGER :: handle, ib, jb, npb
733 3012 : INTEGER, DIMENSION(:), POINTER :: col_blk_full, col_dist_full, &
734 3012 : row_blk_full, row_blk_pan, &
735 3012 : row_dist_full, row_dist_pan
736 3012 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: blk
737 : TYPE(dbcsr_distribution_type) :: dist_full, dist_pan
738 : TYPE(dbcsr_iterator_type) :: iter
739 :
740 3012 : CALL timeset(routineN, handle)
741 :
742 : CALL dbcsr_get_info(mat_full, distribution=dist_full, &
743 3012 : row_blk_size=row_blk_full, col_blk_size=col_blk_full)
744 3012 : CALL dbcsr_distribution_get(dist_full, row_dist=row_dist_full, col_dist=col_dist_full)
745 :
746 3012 : npb = blk1 - blk0 + 1
747 12048 : ALLOCATE (row_dist_pan(npb), row_blk_pan(npb))
748 92352 : row_dist_pan(:) = row_dist_full(blk0:blk1)
749 92352 : row_blk_pan(:) = row_blk_full(blk0:blk1)
750 :
751 : CALL dbcsr_distribution_new(dist_pan, template=dist_full, &
752 3012 : row_dist=row_dist_pan, col_dist=col_dist_full)
753 : CALL dbcsr_create(mat_panel, name="grid_panel", dist=dist_pan, &
754 : matrix_type=dbcsr_type_no_symmetry, &
755 3012 : row_blk_size=row_blk_pan, col_blk_size=col_blk_full)
756 :
757 3012 : CALL dbcsr_iterator_start(iter, mat_full)
758 54093 : DO WHILE (dbcsr_iterator_blocks_left(iter))
759 51081 : CALL dbcsr_iterator_next_block(iter, ib, jb, blk)
760 51081 : IF (ib < blk0 .OR. ib > blk1) CYCLE
761 54093 : CALL dbcsr_put_block(mat_panel, ib - blk0 + 1, jb, blk)
762 : END DO
763 3012 : CALL dbcsr_iterator_stop(iter)
764 3012 : CALL dbcsr_finalize(mat_panel)
765 :
766 3012 : CALL dbcsr_distribution_release(dist_pan)
767 3012 : DEALLOCATE (row_dist_pan, row_blk_pan)
768 :
769 3012 : CALL timestop(handle)
770 :
771 9036 : END SUBROUTINE extract_grid_panel
772 :
773 : ! **************************************************************************************************
774 : !> \brief Marks which column blocks of a DBCSR matrix carry at least one non-zero block anywhere
775 : !> (global union). Used to restrict the inner index of the panel multiplies to the
776 : !> AO/RI atoms that actually touch the panel (exact: dropped rows only meet zeros).
777 : !> \param matrix ...
778 : !> \param para_env ...
779 : !> \param used ...
780 : ! **************************************************************************************************
781 2382 : SUBROUTINE collect_used_col_blocks(matrix, para_env, used)
782 :
783 : TYPE(dbcsr_type), INTENT(INOUT) :: matrix
784 : TYPE(mp_para_env_type), INTENT(IN), POINTER :: para_env
785 : LOGICAL, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: used
786 :
787 : CHARACTER(LEN=*), PARAMETER :: routineN = 'collect_used_col_blocks'
788 :
789 : INTEGER :: handle, ib, jb, nblkcols
790 : INTEGER, ALLOCATABLE, DIMENSION(:) :: iused
791 2382 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: blk
792 : TYPE(dbcsr_iterator_type) :: iter
793 :
794 2382 : CALL timeset(routineN, handle)
795 :
796 2382 : CALL dbcsr_get_info(matrix, nblkcols_total=nblkcols)
797 7146 : ALLOCATE (iused(nblkcols))
798 2382 : iused(:) = 0
799 :
800 2382 : CALL dbcsr_iterator_start(iter, matrix)
801 43648 : DO WHILE (dbcsr_iterator_blocks_left(iter))
802 41266 : CALL dbcsr_iterator_next_block(iter, ib, jb, blk)
803 41266 : iused(jb) = 1
804 : END DO
805 2382 : CALL dbcsr_iterator_stop(iter)
806 :
807 2382 : CALL para_env%sum(iused)
808 :
809 4764 : ALLOCATE (used(nblkcols))
810 8040 : used(:) = (iused(:) > 0)
811 2382 : DEALLOCATE (iused)
812 :
813 2382 : CALL timestop(handle)
814 :
815 4764 : END SUBROUTINE collect_used_col_blocks
816 :
817 : ! **************************************************************************************************
818 : !> \brief Copies the flagged block rows (compress_rows=.TRUE.) or block columns (.FALSE.) of a
819 : !> DBCSR matrix into a compressed matrix. The subset keeps the parent's process assignment
820 : !> along the compressed dimension, so every block stays on its owning rank: the extraction
821 : !> is purely local (zero communication), like extract_grid_panel.
822 : !> \param mat_full ...
823 : !> \param used ...
824 : !> \param mat_out ...
825 : !> \param compress_rows ...
826 : !> \param blk_map ...
827 : ! **************************************************************************************************
828 6308 : SUBROUTINE extract_masked_blocks(mat_full, used, mat_out, compress_rows, blk_map)
829 :
830 : TYPE(dbcsr_type), INTENT(INOUT) :: mat_full
831 : LOGICAL, DIMENSION(:), INTENT(IN) :: used
832 : TYPE(dbcsr_type), INTENT(OUT) :: mat_out
833 : LOGICAL, INTENT(IN) :: compress_rows
834 : INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT), &
835 : OPTIONAL :: blk_map
836 :
837 : CHARACTER(LEN=*), PARAMETER :: routineN = 'extract_masked_blocks'
838 :
839 : INTEGER :: handle, ib, jb, n_blk, n_sub, r
840 6308 : INTEGER, ALLOCATABLE, DIMENSION(:) :: inv_map
841 6308 : INTEGER, DIMENSION(:), POINTER :: blk_full, blk_sub, col_blk_full, &
842 6308 : col_dist_full, dist_full_1d, &
843 6308 : dist_sub_1d, row_blk_full, &
844 6308 : row_dist_full
845 6308 : REAL(KIND=dp), DIMENSION(:, :), POINTER :: blk
846 : TYPE(dbcsr_distribution_type) :: dist_full, dist_sub
847 : TYPE(dbcsr_iterator_type) :: iter
848 :
849 6308 : CALL timeset(routineN, handle)
850 :
851 : CALL dbcsr_get_info(mat_full, distribution=dist_full, &
852 6308 : row_blk_size=row_blk_full, col_blk_size=col_blk_full)
853 6308 : CALL dbcsr_distribution_get(dist_full, row_dist=row_dist_full, col_dist=col_dist_full)
854 :
855 6308 : IF (compress_rows) THEN
856 3926 : blk_full => row_blk_full
857 3926 : dist_full_1d => row_dist_full
858 : ELSE
859 2382 : blk_full => col_blk_full
860 2382 : dist_full_1d => col_dist_full
861 : END IF
862 6308 : n_blk = SIZE(blk_full)
863 :
864 22848 : n_sub = COUNT(used)
865 6308 : CPASSERT(n_sub > 0)
866 37848 : ALLOCATE (inv_map(n_blk), blk_sub(n_sub), dist_sub_1d(n_sub))
867 6350 : IF (PRESENT(blk_map)) ALLOCATE (blk_map(n_sub))
868 6308 : inv_map(:) = 0
869 6308 : r = 0
870 22848 : DO ib = 1, n_blk
871 22848 : IF (used(ib)) THEN
872 16540 : r = r + 1
873 16540 : inv_map(ib) = r
874 16540 : blk_sub(r) = blk_full(ib)
875 16540 : dist_sub_1d(r) = dist_full_1d(ib)
876 16540 : IF (PRESENT(blk_map)) blk_map(r) = ib
877 : END IF
878 : END DO
879 :
880 6308 : IF (compress_rows) THEN
881 : CALL dbcsr_distribution_new(dist_sub, template=dist_full, &
882 3926 : row_dist=dist_sub_1d, col_dist=col_dist_full)
883 : CALL dbcsr_create(mat_out, name="row_subset", dist=dist_sub, &
884 : matrix_type=dbcsr_type_no_symmetry, &
885 3926 : row_blk_size=blk_sub, col_blk_size=col_blk_full)
886 : ELSE
887 : CALL dbcsr_distribution_new(dist_sub, template=dist_full, &
888 2382 : row_dist=row_dist_full, col_dist=dist_sub_1d)
889 : CALL dbcsr_create(mat_out, name="col_subset", dist=dist_sub, &
890 : matrix_type=dbcsr_type_no_symmetry, &
891 2382 : row_blk_size=row_blk_full, col_blk_size=blk_sub)
892 : END IF
893 :
894 6308 : CALL dbcsr_iterator_start(iter, mat_full)
895 61048 : DO WHILE (dbcsr_iterator_blocks_left(iter))
896 54740 : CALL dbcsr_iterator_next_block(iter, ib, jb, blk)
897 61048 : IF (compress_rows) THEN
898 13474 : IF (inv_map(ib) > 0) CALL dbcsr_put_block(mat_out, inv_map(ib), jb, blk)
899 : ELSE
900 41266 : IF (inv_map(jb) > 0) CALL dbcsr_put_block(mat_out, ib, inv_map(jb), blk)
901 : END IF
902 : END DO
903 6308 : CALL dbcsr_iterator_stop(iter)
904 6308 : CALL dbcsr_finalize(mat_out)
905 :
906 6308 : CALL dbcsr_distribution_release(dist_sub)
907 6308 : DEALLOCATE (inv_map, blk_sub, dist_sub_1d)
908 :
909 6308 : CALL timestop(handle)
910 :
911 12616 : END SUBROUTINE extract_masked_blocks
912 :
913 : ! **************************************************************************************************
914 : !> \brief Creates the (empty) result matrix of op(mat_left) * op(mat_right) with the correct block
915 : !> structure and a distribution on the shared process grid, ready to be filled by
916 : !> dbcsr_multiply. Row structure comes from op(left), column structure from op(right).
917 : !> \param mat_left ...
918 : !> \param mat_right ...
919 : !> \param transb 'N' or 'T' applied to mat_right
920 : !> \param mat_out ...
921 : ! **************************************************************************************************
922 8514 : SUBROUTINE create_product_matrix(mat_left, mat_right, transb, mat_out)
923 :
924 : TYPE(dbcsr_type), INTENT(IN) :: mat_left, mat_right
925 : CHARACTER(LEN=1), INTENT(IN) :: transb
926 : TYPE(dbcsr_type), INTENT(OUT) :: mat_out
927 :
928 : CHARACTER(LEN=*), PARAMETER :: routineN = 'create_product_matrix'
929 :
930 : INTEGER :: handle, i, npcols, nprows
931 8514 : INTEGER, DIMENSION(:), POINTER :: col_blk_r, out_col_blk, out_col_dist, &
932 8514 : out_row_blk, out_row_dist, row_blk_l, &
933 8514 : row_blk_r
934 : TYPE(dbcsr_distribution_type) :: dist_l, dist_out
935 :
936 8514 : CALL timeset(routineN, handle)
937 :
938 8514 : CALL dbcsr_get_info(mat_left, distribution=dist_l, row_blk_size=row_blk_l)
939 8514 : CALL dbcsr_get_info(mat_right, row_blk_size=row_blk_r, col_blk_size=col_blk_r)
940 8514 : CALL dbcsr_distribution_get(dist_l, nprows=nprows, npcols=npcols)
941 :
942 : ! block SIZES follow op(left)/op(right); DISTRIBUTIONS are freshly round-robined onto the
943 : ! shared process grid (a transposed operand's row-dist is NOT a valid col-dist on a
944 : ! non-square grid). dbcsr_multiply redistributes internally, so any valid mapping works.
945 8514 : out_row_blk => row_blk_l
946 8514 : IF (transb == 'N') THEN
947 6178 : out_col_blk => col_blk_r
948 : ELSE
949 2336 : out_col_blk => row_blk_r
950 : END IF
951 :
952 42570 : ALLOCATE (out_row_dist(SIZE(out_row_blk)), out_col_dist(SIZE(out_col_blk)))
953 132732 : DO i = 1, SIZE(out_row_blk)
954 132732 : out_row_dist(i) = MOD(i - 1, nprows)
955 : END DO
956 57388 : DO i = 1, SIZE(out_col_blk)
957 57388 : out_col_dist(i) = MOD(i - 1, npcols)
958 : END DO
959 :
960 : CALL dbcsr_distribution_new(dist_out, template=dist_l, &
961 8514 : row_dist=out_row_dist, col_dist=out_col_dist)
962 : CALL dbcsr_create(mat_out, name="panel_product", dist=dist_out, &
963 : matrix_type=dbcsr_type_no_symmetry, &
964 8514 : row_blk_size=out_row_blk, col_blk_size=out_col_blk)
965 8514 : CALL dbcsr_distribution_release(dist_out)
966 8514 : DEALLOCATE (out_row_dist, out_col_dist)
967 :
968 8514 : CALL timestop(handle)
969 :
970 25542 : END SUBROUTINE create_product_matrix
971 :
972 : ! **************************************************************************************************
973 : !> \brief Projects two operators onto grid panels, takes their Hadamard product, and
974 : !> back-projects it: out += scale * L_out^T (A_grid ∘ B_grid) L_out,
975 : !> where A_grid = L_A M_A L_A^T and B_grid = L_B M_B L_B^T.
976 : !> Each L has shape (grid, basis), and its M has shape (basis, basis).
977 : !> The three L matrices must share the ordered grid rows, row blocking and process grid.
978 : !> Only panel x grid slabs are formed; contributions are accumulated over all panels.
979 : !> The sequence of EPS_FILTER screening and floating-point reductions may differ from
980 : !> a full-grid evaluation.
981 : !>
982 : !> The physical contractions and their signs/spin factors are specified in gw_ri_rs_kernels:
983 : !> χ: L_A=L_B=ϕ, M_A=G_occ, M_B=G_vir, L_out=Z -> RI x RI
984 : !> Σx: L_A=ϕ, M_A=D, L_B=Z, M_B=M^-1 V^tr M^-1, L_out=ϕ -> AO x AO
985 : !> Σc: L_A=ϕ, M_A=[G_occ,G_vir], L_B=Z, M_B=W, L_out=ϕ -> two AO x AO outputs.
986 : !> The second Σc output reuses the same W grid panel; its projection and back-projection
987 : !> otherwise follow the same operations as the first output.
988 : !> \param L_A grid-by-basis collocation or fitting matrix for the first operand
989 : !> \param M_A one or two square operators, with row/column blocking matching the columns of L_A
990 : !> \param L_B grid-by-basis collocation or fitting matrix for the second operand
991 : !> \param M_B second square operator, with row/column blocking matching the columns of L_B
992 : !> \param L_out grid-by-output-basis matrix for back-projection
993 : !> \param mat_out borrowed pointers to pre-created outputs, one per M_A, with the column blocking of L_out
994 : !> \param scale prefactor of the Hadamard product, including the caller's sign/spin convention
995 : !> \param eps DBCSR multiplication screening threshold
996 : !> \param para_env communicator containing all matrix ranks
997 : !> \param pan_first first global grid row-block of each ordered, nonoverlapping panel
998 : !> \param pan_last last global grid row-block of each panel; panels together cover the grid
999 : !> \param lb_eq_la L_B equals L_A in values and layout, allowing their grid/AO slices to be shared
1000 : !> \param lout_eq_la L_out equals L_A in values and layout, allowing back-projection slices to be shared
1001 : !> \param zero_out clear the output matrices before accumulating, otherwise add to their existing values
1002 : !> \param centroids optional global grid-block centres, paired with a positive cutoff for truncation
1003 : !> \param cutoff retain grid-block pairs within this centroid distance; nonpositive disables truncation
1004 : !> \param grid_occupation allocated-element fraction of the first grid operand before the Hadamard product
1005 : ! **************************************************************************************************
1006 1506 : SUBROUTINE contract_grid_panels(L_A, M_A, L_B, M_B, L_out, mat_out, scale, eps, para_env, &
1007 1506 : pan_first, pan_last, lb_eq_la, lout_eq_la, zero_out, &
1008 1506 : centroids, cutoff, grid_occupation)
1009 :
1010 : TYPE(dbcsr_type), INTENT(INOUT), TARGET :: L_A
1011 : TYPE(dbcsr_type), DIMENSION(:), INTENT(INOUT) :: M_A
1012 : TYPE(dbcsr_type), INTENT(INOUT), TARGET :: L_B
1013 : TYPE(dbcsr_type), INTENT(INOUT) :: M_B
1014 : TYPE(dbcsr_type), INTENT(INOUT), TARGET :: L_out
1015 : TYPE(dbcsr_p_type), DIMENSION(:), INTENT(IN) :: mat_out
1016 : REAL(KIND=dp), INTENT(IN) :: scale, eps
1017 : TYPE(mp_para_env_type), INTENT(IN), POINTER :: para_env
1018 : INTEGER, DIMENSION(:), INTENT(IN) :: pan_first, pan_last
1019 : LOGICAL, INTENT(IN) :: lb_eq_la, lout_eq_la, zero_out
1020 : REAL(KIND=dp), DIMENSION(:, :), INTENT(IN), &
1021 : OPTIONAL :: centroids
1022 : REAL(KIND=dp), INTENT(IN), OPTIONAL :: cutoff
1023 : REAL(KIND=dp), INTENT(OUT), OPTIONAL :: grid_occupation
1024 :
1025 : CHARACTER(LEN=*), PARAMETER :: routineN = 'contract_grid_panels'
1026 :
1027 : INTEGER :: blk0, blk1, handle, i_A, ipan, &
1028 : n_grid_total, ncols_pan, nrows_pan
1029 1506 : INTEGER, ALLOCATABLE, DIMENSION(:) :: gmap
1030 : LOGICAL :: use_cutoff
1031 1506 : LOGICAL, ALLOCATABLE, DIMENSION(:) :: grid_used, usedA, usedB
1032 4518 : TYPE(dbcsr_type) :: A_pan(2), B_pan, LB_pan, LB_panC, &
1033 : MA_sub, MB_sub, tmp2, tmpA, tmpB
1034 : TYPE(dbcsr_type), POINTER :: panel_out, RB_A, RB_B, RB_out
1035 : TYPE(dbcsr_type), TARGET :: LA_near, LA_pan, LA_panC, LB_near, &
1036 : Lout_near, Lout_pan
1037 :
1038 1506 : CALL timeset(routineN, handle)
1039 :
1040 1506 : CPASSERT(SIZE(M_A) >= 1 .AND. SIZE(M_A) <= 2)
1041 1506 : CPASSERT(SIZE(mat_out) == SIZE(M_A))
1042 :
1043 1506 : use_cutoff = PRESENT(centroids) .AND. PRESENT(cutoff)
1044 1506 : IF (use_cutoff) use_cutoff = cutoff > 0.0_dp
1045 1506 : IF (PRESENT(grid_occupation)) grid_occupation = 0.0_dp
1046 :
1047 1506 : CALL dbcsr_get_info(L_A, nfullrows_total=n_grid_total)
1048 :
1049 1506 : IF (zero_out) THEN
1050 3682 : DO i_A = 1, SIZE(mat_out)
1051 3682 : CALL dbcsr_set(mat_out(i_A)%matrix, 0.0_dp)
1052 : END DO
1053 : END IF
1054 :
1055 3012 : DO ipan = 1, SIZE(pan_first)
1056 1506 : blk0 = pan_first(ipan)
1057 1506 : blk1 = pan_last(ipan)
1058 :
1059 : ! Slice only the current grid rows; compact away AO/RI columns that are exactly absent.
1060 1506 : CALL extract_grid_panel(L_A, blk0, blk1, LA_pan)
1061 1506 : CALL collect_used_col_blocks(LA_pan, para_env, usedA)
1062 1506 : IF (.NOT. lb_eq_la) THEN
1063 876 : CALL extract_grid_panel(L_B, blk0, blk1, LB_pan)
1064 876 : CALL collect_used_col_blocks(LB_pan, para_env, usedB)
1065 : ELSE
1066 630 : IF (ALLOCATED(usedB)) DEALLOCATE (usedB)
1067 3380 : ALLOCATE (usedB, SOURCE=usedA)
1068 : END IF
1069 1506 : IF (.NOT. ANY(usedA) .OR. .NOT. ANY(usedB)) THEN
1070 0 : CALL dbcsr_release(LA_pan)
1071 0 : IF (.NOT. lb_eq_la) CALL dbcsr_release(LB_pan)
1072 : CYCLE
1073 : END IF
1074 1506 : IF (lout_eq_la) THEN
1075 : panel_out => LA_pan
1076 : ELSE
1077 630 : CALL extract_grid_panel(L_out, blk0, blk1, Lout_pan)
1078 630 : panel_out => Lout_pan
1079 : END IF
1080 1506 : CALL extract_masked_blocks(LA_pan, usedA, LA_panC, compress_rows=.FALSE.)
1081 :
1082 : ! Grid rows within reach of the panel: with the CUTOFF_RADIUS_RL_W truncation only
1083 : ! they can appear as columns of the panel products / inner rows of the L_out multiply,
1084 : ! so the system-wide phi/Z right operands are cut down to this neighborhood slice
1085 : ! (local extraction, zero communication; exact w.r.t. the geo template).
1086 1506 : IF (use_cutoff) THEN
1087 42 : CALL mask_grid_blocks_near_panel(centroids, blk0, blk1, cutoff, grid_used)
1088 42 : CALL extract_masked_blocks(L_A, grid_used, LA_near, compress_rows=.TRUE., blk_map=gmap)
1089 42 : IF (.NOT. lb_eq_la) CALL extract_masked_blocks(L_B, grid_used, LB_near, compress_rows=.TRUE.)
1090 42 : IF (.NOT. lout_eq_la) CALL extract_masked_blocks(L_out, grid_used, Lout_near, compress_rows=.TRUE.)
1091 : RB_A => LA_near
1092 : ELSE
1093 : RB_A => L_A
1094 : END IF
1095 1506 : IF (lb_eq_la) THEN
1096 : RB_B => RB_A
1097 876 : ELSE IF (use_cutoff) THEN
1098 : RB_B => LB_near
1099 : ELSE
1100 854 : RB_B => L_B
1101 : END IF
1102 1506 : IF (lout_eq_la) THEN
1103 : RB_out => RB_A
1104 630 : ELSE IF (use_cutoff) THEN
1105 : RB_out => Lout_near
1106 : ELSE
1107 610 : RB_out => L_out
1108 : END IF
1109 :
1110 3842 : DO i_A = 1, SIZE(M_A)
1111 : ! A_pan(i_A) = LA_pan * M_A(i_A) * L_A^T (P x grid_near).
1112 : ! When cutoff is active, A_pan(i_A) is pre-seeded with only nearby blocks via
1113 : ! build_geo_template_panel, and the multiply uses retain_sparsity to skip
1114 : ! computing distant blocks entirely within the chosen grid cutoff.
1115 2336 : CALL extract_masked_blocks(M_A(i_A), usedA, MA_sub, compress_rows=.TRUE.)
1116 2336 : CALL create_product_matrix(LA_panC, MA_sub, 'N', tmpA)
1117 2336 : CALL dbcsr_multiply('N', 'N', 1.0_dp, LA_panC, MA_sub, 0.0_dp, tmpA, filter_eps=eps)
1118 2336 : CALL dbcsr_release(MA_sub)
1119 2336 : IF (use_cutoff) THEN
1120 : CALL build_geo_template_panel(LA_pan, LA_near, centroids, cutoff, blk0, A_pan(i_A), &
1121 62 : col_map=gmap)
1122 : ELSE
1123 2274 : CALL create_product_matrix(tmpA, RB_A, 'T', A_pan(i_A))
1124 : END IF
1125 : CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpA, RB_A, 0.0_dp, A_pan(i_A), &
1126 2336 : filter_eps=eps, retain_sparsity=use_cutoff)
1127 2336 : CALL dbcsr_release(tmpA)
1128 :
1129 : ! Grid-basis occupation of A_pan(i_A) = ϕ G ϕ^T, accumulated over ALL panels into the
1130 : ! occupation of the (never formed) full grid x grid object:
1131 : ! sum_panels nnz(A_pan(i_A)) / n_grid^2, with nnz = occ * pan_rows * pan_cols.
1132 : ! Panel-independent by construction -- a single-panel sample would instead report the
1133 : ! local neighbor count of whichever region happens to land in that panel.
1134 6178 : IF (PRESENT(grid_occupation) .AND. i_A == 1) THEN
1135 46 : CALL dbcsr_get_info(A_pan(i_A), nfullrows_total=nrows_pan, nfullcols_total=ncols_pan)
1136 : grid_occupation = grid_occupation + dbcsr_get_occupation(A_pan(i_A))* &
1137 : REAL(ncols_pan, dp)*REAL(nrows_pan, dp)/ &
1138 46 : (REAL(n_grid_total, dp)*REAL(n_grid_total, dp))
1139 : END IF
1140 : END DO
1141 :
1142 : ! B_pan = LB_pan * M_B * L_B^T (P x grid_near); reuse the L_A slices when L_B == L_A.
1143 : ! B_pan retains the union of the A panels' block structures so that
1144 : ! retain_sparsity forces the final multiply to fill only those blocks (exact for ∘).
1145 1506 : IF (lb_eq_la) THEN
1146 630 : CALL extract_masked_blocks(M_B, usedA, MB_sub, compress_rows=.TRUE.)
1147 630 : CALL create_product_matrix(LA_panC, MB_sub, 'N', tmpB)
1148 630 : CALL dbcsr_multiply('N', 'N', 1.0_dp, LA_panC, MB_sub, 0.0_dp, tmpB, filter_eps=eps)
1149 : ELSE
1150 876 : CALL extract_masked_blocks(LB_pan, usedB, LB_panC, compress_rows=.FALSE.)
1151 876 : CALL extract_masked_blocks(M_B, usedB, MB_sub, compress_rows=.TRUE.)
1152 876 : CALL create_product_matrix(LB_panC, MB_sub, 'N', tmpB)
1153 876 : CALL dbcsr_multiply('N', 'N', 1.0_dp, LB_panC, MB_sub, 0.0_dp, tmpB, filter_eps=eps)
1154 876 : CALL dbcsr_release(LB_panC)
1155 : END IF
1156 1506 : CALL dbcsr_release(MB_sub)
1157 1506 : CALL dbcsr_create(B_pan, template=A_pan(1))
1158 1506 : CALL dbcsr_copy(B_pan, A_pan(1))
1159 1506 : IF (SIZE(M_A) == 2) CALL dbcsr_add(B_pan, A_pan(2), 1.0_dp, 1.0_dp)
1160 1506 : CALL dbcsr_set(B_pan, 0.0_dp)
1161 : ! The output pattern is already fixed. Omitting redundant on-the-fly filtering also
1162 : ! avoids overflowing DBCSR's single-precision screening norms for conditioned Z fits.
1163 : CALL dbcsr_multiply('N', 'T', 1.0_dp, tmpB, RB_B, 0.0_dp, B_pan, &
1164 1506 : retain_sparsity=.TRUE.)
1165 1506 : CALL dbcsr_release(tmpB)
1166 1506 : CALL dbcsr_release(LA_panC)
1167 :
1168 3842 : DO i_A = 1, SIZE(M_A)
1169 : ! Reuse A_pan storage for its Hadamard product with the shared B panel.
1170 2336 : CALL hadamard_product_inplace(A_pan(i_A), B_pan, scale)
1171 2336 : CALL create_product_matrix(A_pan(i_A), RB_out, 'N', tmp2)
1172 2336 : CALL dbcsr_multiply('N', 'N', 1.0_dp, A_pan(i_A), RB_out, 0.0_dp, tmp2, filter_eps=eps)
1173 2336 : CALL dbcsr_release(A_pan(i_A))
1174 :
1175 : ! mat_out += L_out_pan^T * tmp2.
1176 2336 : CALL dbcsr_multiply('T', 'N', 1.0_dp, panel_out, tmp2, 1.0_dp, mat_out(i_A)%matrix, filter_eps=eps)
1177 6178 : CALL dbcsr_release(tmp2)
1178 : END DO
1179 1506 : CALL dbcsr_release(B_pan)
1180 1506 : IF (.NOT. lout_eq_la) CALL dbcsr_release(Lout_pan)
1181 1506 : IF (.NOT. lb_eq_la) CALL dbcsr_release(LB_pan)
1182 1506 : CALL dbcsr_release(LA_pan)
1183 4518 : IF (use_cutoff) THEN
1184 42 : CALL dbcsr_release(LA_near)
1185 42 : IF (.NOT. lb_eq_la) CALL dbcsr_release(LB_near)
1186 42 : IF (.NOT. lout_eq_la) CALL dbcsr_release(Lout_near)
1187 : END IF
1188 :
1189 : END DO
1190 :
1191 1506 : CALL timestop(handle)
1192 :
1193 4518 : END SUBROUTINE contract_grid_panels
1194 :
1195 : ! **************************************************************************************************
1196 : !> \brief Build the unfiltered auxiliary interaction M^-1 V^tr M^-1 in the Z-column layout.
1197 : !> AUTO_RI uses its already transformed Coulomb matrix in the optimized auxiliary basis.
1198 : !> Filtering and cache lifetime are owned by the caller.
1199 : !> \param qs_env electronic-structure environment for ordinary RI integrals
1200 : !> \param metric_inverse inverse auxiliary metric
1201 : !> \param trunc_coulomb Coulomb operator for ordinary RI integrals
1202 : !> \param auto_ri optimized auxiliary basis and its Coulomb matrix
1203 : !> \param mat_Z_lP template defining the auxiliary blocking and process grid
1204 : !> \param matrix_V_aux output bare interaction in the auxiliary basis
1205 : ! **************************************************************************************************
1206 40 : SUBROUTINE build_ri_rs_bare_aux_interaction(qs_env, metric_inverse, trunc_coulomb, auto_ri, &
1207 : mat_Z_lP, matrix_V_aux)
1208 : TYPE(qs_environment_type), POINTER :: qs_env
1209 : TYPE(cp_fm_type), INTENT(IN) :: metric_inverse
1210 : TYPE(libint_potential_type), INTENT(IN) :: trunc_coulomb
1211 : TYPE(auto_ri_type), INTENT(IN) :: auto_ri
1212 : TYPE(dbcsr_type), INTENT(IN) :: mat_Z_lP
1213 : TYPE(dbcsr_type), INTENT(OUT) :: matrix_V_aux
1214 :
1215 : CHARACTER(LEN=*), PARAMETER :: routineN = 'build_ri_rs_bare_aux_interaction'
1216 :
1217 : INTEGER :: handle
1218 40 : INTEGER, POINTER :: blk_aux(:), dist_row_aux(:)
1219 40 : TYPE(cp_fm_type), ALLOCATABLE :: fm_Vtr_Gamma(:, :)
1220 : TYPE(dbcsr_distribution_type) :: dist_aux_aux
1221 :
1222 40 : CALL timeset(routineN, handle)
1223 40 : IF (auto_ri%enabled) THEN
1224 4 : CPASSERT(auto_ri%ready)
1225 12 : ALLOCATE (fm_Vtr_Gamma(1, 1))
1226 4 : CALL cp_fm_create(fm_Vtr_Gamma(1, 1), metric_inverse%matrix_struct)
1227 4 : CALL cp_fm_to_fm(auto_ri%V_pq, fm_Vtr_Gamma(1, 1))
1228 : ELSE
1229 : CALL RI_2c_integral_mat(qs_env, fm_Vtr_Gamma, metric_inverse%matrix_struct, &
1230 36 : metric_inverse%matrix_struct%nrow_global, trunc_coulomb)
1231 : END IF
1232 40 : CALL fm_contract_ABA(metric_inverse, fm_Vtr_Gamma(:, 1))
1233 40 : CALL setup_square_topology(mat_Z_lP, 'COL', dist_aux_aux, blk_aux, dist_row_aux)
1234 40 : CALL dbcsr_create(matrix_V_aux, "V_aux", dist_aux_aux, dbcsr_type_no_symmetry, blk_aux, blk_aux)
1235 40 : CALL copy_fm_to_dbcsr(fm_Vtr_Gamma(1, 1), matrix_V_aux, keep_sparsity=.FALSE.)
1236 40 : CALL cp_fm_release(fm_Vtr_Gamma)
1237 40 : CALL release_dbcsr_topology_and_matrices(dist=dist_aux_aux, mapped_dist=dist_row_aux)
1238 40 : CALL timestop(handle)
1239 80 : END SUBROUTINE build_ri_rs_bare_aux_interaction
1240 :
1241 : ! **************************************************************************************************
1242 : !> \brief Resolve and cache grid-panel boundaries for RI-RS streaming contractions.
1243 : !> \param ri_rs collocation and fitting matrices, grid geometry and owned panel cache
1244 : !> \param para_env parallel environment
1245 : !> \param msg_elem_limit maximum number of elements per DBCSR message
1246 : !> \param pan_first ...
1247 : !> \param pan_last ...
1248 : !> \param cutoff grid-centroid distance cutoff; zero disables geometric truncation
1249 : ! **************************************************************************************************
1250 174 : SUBROUTINE resolve_grid_panels(ri_rs, para_env, msg_elem_limit, pan_first, pan_last, cutoff)
1251 :
1252 : TYPE(ri_rs_env), INTENT(INOUT) :: ri_rs
1253 : TYPE(mp_para_env_type), POINTER :: para_env
1254 : INTEGER(KIND=int_8), INTENT(IN) :: msg_elem_limit
1255 : INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: pan_first, pan_last
1256 : REAL(KIND=dp), INTENT(IN) :: cutoff
1257 :
1258 : CHARACTER(LEN=*), PARAMETER :: routineN = 'resolve_grid_panels'
1259 :
1260 : INTEGER :: handle, min_dim, n_ao, n_grid_total, &
1261 : n_RI, npcols, nprows
1262 174 : INTEGER, DIMENSION(:), POINTER :: r_blk_sizes
1263 : LOGICAL :: use_cutoff
1264 : REAL(KIND=dp) :: effective_cutoff, mem_avail_GB, &
1265 : mem_budget_GB
1266 : TYPE(dbcsr_distribution_type) :: dist
1267 :
1268 174 : CALL timeset(routineN, handle)
1269 :
1270 : use_cutoff = cutoff > 0.0_dp .AND. &
1271 174 : ALLOCATED(ri_rs%chunk_centroids)
1272 166 : effective_cutoff = 0.0_dp
1273 8 : IF (use_cutoff) effective_cutoff = cutoff
1274 174 : IF (ALLOCATED(ri_rs%pan_first)) THEN
1275 : ! This is an exact cache key, not a tolerance-based geometric comparison.
1276 134 : IF (ri_rs%panel_plan_cutoff == effective_cutoff) THEN
1277 536 : ALLOCATE (pan_first, SOURCE=ri_rs%pan_first)
1278 536 : ALLOCATE (pan_last, SOURCE=ri_rs%pan_last)
1279 134 : CALL timestop(handle)
1280 : RETURN
1281 : END IF
1282 0 : DEALLOCATE (ri_rs%pan_first, ri_rs%pan_last)
1283 : END IF
1284 :
1285 : ! MIN(nprows, npcols) is the divisor that bounds the worst-rank Cannon message: a
1286 : ! P x n_grid panel is replicated into block row strips (P/nprows x n_grid) or column
1287 : ! strips (P x n_grid/npcols) during multiply_cannon, so the largest single-rank
1288 : ! message is ~ P*n_grid / MIN(nprows,npcols) elements.
1289 : CALL dbcsr_get_info(ri_rs%mat_phi_mu_l, nfullrows_total=n_grid_total, nfullcols_total=n_ao, &
1290 40 : row_blk_size=r_blk_sizes, distribution=dist)
1291 40 : CALL dbcsr_distribution_get(dist, nprows=nprows, npcols=npcols)
1292 40 : min_dim = MAX(MIN(nprows, npcols), 1)
1293 :
1294 40 : CALL dbcsr_get_info(ri_rs%mat_Z_lP, nfullcols_total=n_RI)
1295 : ! Only the cutoff path uses the measured free memory to limit panel sizes.
1296 40 : mem_budget_GB = 0.0_dp
1297 40 : IF (use_cutoff) THEN
1298 2 : CALL mp_mem_avail_per_rank_GB(para_env, mem_avail_GB)
1299 2 : mem_budget_GB = 0.5_dp*mem_avail_GB
1300 : END IF
1301 : CALL plan_grid_panels(msg_elem_limit, r_blk_sizes, min_dim, pan_first, pan_last, &
1302 : n_RI, n_ao, para_env%num_pe, mem_budget_GB, &
1303 40 : centroids=ri_rs%chunk_centroids, cutoff=effective_cutoff)
1304 :
1305 160 : ALLOCATE (ri_rs%pan_first, SOURCE=pan_first)
1306 160 : ALLOCATE (ri_rs%pan_last, SOURCE=pan_last)
1307 40 : ri_rs%panel_plan_cutoff = effective_cutoff
1308 :
1309 40 : CALL timestop(handle)
1310 :
1311 214 : END SUBROUTINE resolve_grid_panels
1312 :
1313 : END MODULE ri_rs_matrices
|