(git:691081d)
Loading...
Searching...
No Matches
gw_compute_Z_lP.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 Computes the RI-RS fitting matrix Z_lP.
10!> \par History
11!> 09.2026 created Jan Wilhelm
12!> 09.2026 moved code by Ritaj Tyagi from gw_non_periodic_ri_rs.F
13!> 09.2026 added routine to compute Z_lP for automatic RI optimization
14! **************************************************************************************************
19 USE cell_types, ONLY: cell_type,&
20 pbc
24 USE cp_dbcsr_api, ONLY: &
28 dbcsr_release, dbcsr_set, dbcsr_type, dbcsr_type_no_symmetry
31 USE cp_fm_types, ONLY: cp_fm_type
34 USE cp_output_handling, ONLY: cp_p_file,&
50 USE kinds, ONLY: dp
51 USE libint_2c_3c, ONLY: eri_3center
52 USE machine, ONLY: m_flush,&
56 USE orbital_pointers, ONLY: ncoset
58 USE physcon, ONLY: angstrom
64 USE util, ONLY: sort
65#include "./base/base_uses.f90"
66
67 IMPLICIT NONE
68 PRIVATE
69
70 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'gw_compute_Z_lP'
71
72 PUBLIC :: compute_z_lp
73
74CONTAINS
75
76! **************************************************************************************************
77!> \brief Computes Z_lP in either the standard tabulated or automatically generated RI basis
78!> set.
79!>
80!> For both basis choices, the Z_lP coefficients solve
81!>
82!> Σ_l' D_ll' Z_l'P = d_lP,
83!> D_ll' = [Σ_μ Φ_μ(r_l) Φ_μ(r_l')]²,
84!> d_lP = Σ_μν Φ_μ(r_l) Φ_ν(r_l) (μν|P).
85!>
86!> An automatically optimized function P may contain RI functions on neighboring atoms.
87!> This routine selects the implementation without exposing either implementation outside
88!> this module.
89!> \param qs_env ...
90!> \param bs_env ...
91!> \param ri_rs_grid_points ...
92!> \param mat_phi_mu_l ...
93!> \param mat_Z_lP ...
94! **************************************************************************************************
95 SUBROUTINE compute_z_lp(qs_env, bs_env, ri_rs_grid_points, mat_phi_mu_l, mat_Z_lP)
96 TYPE(qs_environment_type), POINTER :: qs_env
97 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
98 REAL(kind=dp), ALLOCATABLE, INTENT(INOUT) :: ri_rs_grid_points(:, :)
99 TYPE(dbcsr_type), INTENT(INOUT) :: mat_phi_mu_l
100 TYPE(dbcsr_type), INTENT(OUT) :: mat_z_lp
101
102 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_Z_lP'
103
104 INTEGER :: handle
105
106 CALL timeset(routinen, handle)
107
108 IF (bs_env%auto_ri%enabled) THEN
109 CALL compute_z_lp_auto_ri(qs_env, bs_env, ri_rs_grid_points, mat_phi_mu_l, mat_z_lp)
110 ELSE
111 CALL compute_z_lp_standard(qs_env, bs_env, ri_rs_grid_points, mat_phi_mu_l, mat_z_lp)
112 END IF
113
114 CALL timestop(handle)
115
116 END SUBROUTINE compute_z_lp
117
118! **************************************************************************************************
119!> \brief Computes the RI-RS fitting coefficients Z_lP by solving, independently for every RI
120!> atom P, a Jacobi-conditioned, Tikhonov-regularized linear system restricted to the
121!> grid points r_l inside P's integration sphere |r_l - R_P| <= cutoff_ri(P):
122!> D_ll' = [ Σ_μ Φ_μ(r_l) Φ_μ(r_l') ]²,
123!> D_lP = Σ_μν Φ_μ(r_l) Φ_ν(r_l) (μν|P),
124!> d_l = 1 / sqrt(D_ll) (Jacobi conditioning vector)
125!> D'_ll' = d_l D_ll' d_l' + λ δ_ll' (λ = TIKHONOV_SIGMA regularization)
126!> Σ_l' D'_ll' Z'_l'P = d_l D_lP,
127!> Z_lP = d_l Z'_l'P (undo the conditioning)
128!> Work is distributed over atoms in two phases (planned by classify_z_lp_atoms and
129!> lpt_assign_atoms): Phase A solves "small" atoms with single-rank LAPACK
130!> (dpotrf/dpotrs); Phase B solves "big" atoms, whose dense Gram matrix would exceed one
131!> rank's memory, with ScaLAPACK (pdpotrf/pdpotrs) over rank subgroups of size G.
132!> The solved Z columns are scattered into the sparse global mat_Z_lP.
133!> If a Z_lP restart file exists, it is read instead and the solve is skipped entirely.
134!> \param qs_env ...
135!> \param bs_env ...
136!> \param ri_rs_grid_points ...
137!> \param mat_phi_mu_l ...
138!> \param mat_Z_lP ...
139! **************************************************************************************************
140 SUBROUTINE compute_z_lp_standard(qs_env, bs_env, ri_rs_grid_points, mat_phi_mu_l, mat_Z_lP)
141
142 TYPE(qs_environment_type), POINTER :: qs_env
143 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
144 REAL(kind=dp), ALLOCATABLE, INTENT(INOUT) :: ri_rs_grid_points(:, :)
145 TYPE(dbcsr_type), INTENT(INOUT) :: mat_phi_mu_l
146 TYPE(dbcsr_type), INTENT(OUT) :: mat_z_lp
147
148 CHARACTER(LEN=*), PARAMETER :: key = 'PROPERTIES%BANDSTRUCTURE%GW%PRINT%RESTART', &
149 routinen = 'compute_Z_lP_standard'
150
151 INTEGER :: atom_j_mepos, atom_j_stride, atom_p, g, handle, handle_dpotrf, handle_dpotrs, &
152 i_blk, iatom, idx, info, iphase, j, max_ao_size, my_group, n_ao_total, n_ao_used, n_big, &
153 n_done, n_groups, n_loc_ri, n_local_grid, n_my_atoms, n_small, natom, npcol_phi, &
154 num_grid_chunks, phase_hi
155 INTEGER, ALLOCATABLE, DIMENSION(:) :: ao_col_map, big_list, local_grid_idx, &
156 my_atoms_a, my_atoms_b, &
157 n_local_grid_atom, row_offset, &
158 small_list
159 INTEGER, DIMENSION(:), POINTER :: col_dist_ri, r_blk_sizes, ri_blk_sizes, &
160 row_dist_grid
161 LOGICAL :: do_scatter, use_dist
162 REAL(kind=dp) :: balance_a, balance_b, cutoff_ri, &
163 item_start_time, r_c, t1
164 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: cutoff_ri_per_atom, d_vec_local
165 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: d_local, d_lp_local, phi_local
166 TYPE(atomic_kind_type), DIMENSION(:), POINTER :: atomic_kind_set
167 TYPE(cell_type), POINTER :: cell
168 TYPE(cp_blacs_env_type), POINTER :: blacs_env_sub
169 TYPE(cp_fm_struct_type), POINTER :: fm_struct_b, fm_struct_d
170 TYPE(cp_fm_type) :: fm_b, fm_d
171 TYPE(cp_logger_type), POINTER :: logger
172 TYPE(dbcsr_distribution_type) :: dist_phi, dist_z
173 TYPE(gw_3c_ctx_type) :: ctx_3c
174 TYPE(mp_para_env_type), POINTER :: para_env, para_env_sub
175 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
176 TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kind_set
177 TYPE(section_vals_type), POINTER :: input
178
179 CALL timeset(routinen, handle)
180
181 t1 = m_walltime()
182
183 CALL get_qs_env(qs_env, para_env=para_env, particle_set=particle_set, input=input, &
184 qs_kind_set=qs_kind_set, cell=cell, atomic_kind_set=atomic_kind_set)
185
186 NULLIFY (para_env_sub, blacs_env_sub)
187
188 natom = bs_env%n_atom
189 n_ao_total = bs_env%i_ao_end_from_atom(natom)
190
191 ! =========================================================================
192 ! 1. SETUP DBCSR TOPOLOGY & EXACT OFFSETS
193 ! mat_Z_lP inherits the grid row blocking (and row distribution) of
194 ! mat_phi_mu_l; its columns are one block per RI atom.
195 ! =========================================================================
196 CALL dbcsr_get_info(mat_phi_mu_l, row_blk_size=r_blk_sizes, distribution=dist_phi)
197 CALL dbcsr_distribution_get(dist_phi, row_dist=row_dist_grid, npcols=npcol_phi)
198
199 num_grid_chunks = SIZE(r_blk_sizes)
200
201 ALLOCATE (row_offset(num_grid_chunks))
202 row_offset(1) = 0
203 DO i_blk = 2, num_grid_chunks
204 row_offset(i_blk) = row_offset(i_blk - 1) + r_blk_sizes(i_blk - 1)
205 END DO
206
207 ALLOCATE (ri_blk_sizes(natom), col_dist_ri(natom))
208 DO iatom = 1, natom
209 ri_blk_sizes(iatom) = &
210 bs_env%i_RI_end_from_atom(iatom) - bs_env%i_RI_start_from_atom(iatom) + 1
211 col_dist_ri(iatom) = mod(iatom - 1, npcol_phi)
212 END DO
213
214 CALL dbcsr_distribution_new(dist_z, template=dist_phi, &
215 row_dist=row_dist_grid, col_dist=col_dist_ri)
216
217 IF (bs_env%ri_rs%Z_lP_exists) THEN
218 CALL dbcsr_binary_read(filepath=trim(bs_env%prefix)//"Z_lP.matrix", &
219 distribution=dist_z, &
220 matrix_new=mat_z_lp)
221 IF (bs_env%unit_nr > 0) THEN
222 WRITE (bs_env%unit_nr, '(T2,A,T57,A,F7.1,A)') &
223 'Read Z_lP from file ', ' Execution time', m_walltime() - t1, ' s'
224 ! The grid rows use Morton order (spatial_atom_order). A Z_lP.matrix
225 ! written with another grid ordering would be silently read into
226 ! the current row order. Delete stale Z_lP.matrix files and recompute if in doubt.
227 WRITE (bs_env%unit_nr, '(T2,A)') &
228 '*** NOTE: Z_lP restart must match the current (spatial) grid row ordering ***'
229 WRITE (bs_env%unit_nr, '(A)') ' '
230 END IF
231 ELSE
232
233 IF (bs_env%unit_nr > 0) THEN
234 WRITE (bs_env%unit_nr, '(A)') ' '
235 WRITE (bs_env%unit_nr, '(T2,A)') 'Started computing Z_lP'
236 CALL m_flush(bs_env%unit_nr)
237 END IF
238 CALL dbcsr_create(mat_z_lp, name="mat_Z_lP", dist=dist_z, &
239 matrix_type=dbcsr_type_no_symmetry, &
240 row_blk_size=r_blk_sizes, col_blk_size=ri_blk_sizes)
241
242 ! Largest per-atom AO block, needed to size the 3c-integral work buffers.
243 max_ao_size = 0
244 DO j = 1, bs_env%n_atom
245 max_ao_size = max(max_ao_size, &
246 bs_env%i_ao_end_from_atom(j) - &
247 bs_env%i_ao_start_from_atom(j) + 1)
248 END DO
249
250 ! Per-atom RI-RS integration sphere:
251 ! cutoff_ri(P) = r_c + r_RI(P)
252 ! where r_c is the truncated-Coulomb cutoff of the RI metric and r_RI the radius of
253 ! the most diffuse RI auxiliary Gaussian on P. The CUTOFF_RADIUS_RL_RI keyword
254 ! (when > 0) overrides the entire cutoff calculation.
255 ALLOCATE (cutoff_ri_per_atom(natom))
256
257 IF (bs_env%ri_rs%cutoff_radius_ri_rs > 0.0_dp) THEN
258 cutoff_ri_per_atom(:) = bs_env%ri_rs%cutoff_radius_ri_rs
259 ELSE
260 r_c = bs_env%ri_metric%cutoff_radius
261 DO iatom = 1, natom
262 cutoff_ri_per_atom(iatom) = r_c + bs_env%ri_rs%radius_ri_per_atom(iatom)
263 END DO
264 END IF
265
266 CALL print_sphere_cutoff_table(bs_env, cutoff_ri_per_atom)
267
268 ! =========================================================================
269 ! 2. PER-ATOM SOLVER CLASSIFICATION
270 ! Split the atoms into "small" (single-rank LAPACK, Phase A) and "big"
271 ! (distributed ScaLAPACK over subgroups of G ranks, Phase B) by comparing
272 ! each atom's estimated solve peak memory against the measured budget.
273 ! =========================================================================
274 CALL classify_z_lp_atoms(bs_env, ri_rs_grid_points, cutoff_ri_per_atom, ri_blk_sizes, &
275 n_local_grid_atom, small_list, n_small, big_list, n_big, g)
276
277 ! LPT scheduling: sort the atoms of each phase by estimated solve cost
278 ! (n_local_grid^3, Cholesky-dominated) and greedily assign to the least-loaded
279 ! rank (Phase A) / subgroup (Phase B).
280 CALL lpt_assign_atoms(small_list, n_small, n_local_grid_atom, para_env%num_pe, &
281 para_env%mepos, my_atoms_a, balance_a)
282 IF (n_big > 0) THEN
283 n_groups = para_env%num_pe/g
284 my_group = min(para_env%mepos/g, n_groups - 1)
285 CALL lpt_assign_atoms(big_list, n_big, n_local_grid_atom, n_groups, my_group, &
286 my_atoms_b, balance_b)
287 ELSE
288 ALLOCATE (my_atoms_b(0))
289 balance_b = 1.0_dp
290 END IF
291
292 ! Atoms this rank will process across both phases for rank-0 progress
293 n_my_atoms = SIZE(my_atoms_a) + SIZE(my_atoms_b)
294 n_done = 0
295
296 ! Shared context for the three-center integrals (μν|P) of the RHS build
297 CALL gw_3c_ctx_create(ctx_3c, qs_env, bs_env%ri_metric, &
298 basis_j=bs_env%basis_set_AO, basis_k=bs_env%basis_set_AO, &
299 basis_i=bs_env%basis_set_RI)
300
301 ! =========================================================================
302 ! 3. TWO-PHASE LOOP OVER ATOMS
303 ! Phase A processes the "small" atoms with the single-rank BLAS path
304 ! Phase B processes the "big" atoms with the distributed ScaLAPACK path
305 ! over rank subgroups of size G. phi_local for each atom's cutoff sphere
306 ! is built on the fly to avoid replicating a global grid x AO matrix.
307 ! =========================================================================
308 DO iphase = 1, 2
309 IF (iphase == 1) THEN
310 use_dist = .false.
311 atom_j_mepos = 0
312 atom_j_stride = 1
313 phase_hi = SIZE(my_atoms_a)
314 ELSE
315 IF (n_big == 0) cycle
316 use_dist = .true.
317 n_groups = para_env%num_pe/g
318 my_group = min(para_env%mepos/g, n_groups - 1)
319 ALLOCATE (para_env_sub)
320 CALL para_env_sub%from_split(para_env, my_group)
321 CALL cp_blacs_env_create(blacs_env=blacs_env_sub, para_env=para_env_sub)
322 atom_j_mepos = para_env_sub%mepos
323 atom_j_stride = para_env_sub%num_pe
324 ! All ranks of a subgroup share my_group, hence the identical my_atoms_B list
325 ! (the per-atom ScaLAPACK solve is collective over the subgroup).
326 phase_hi = SIZE(my_atoms_b)
327 END IF
328
329 DO idx = 1, phase_hi
330 item_start_time = m_walltime()
331 IF (iphase == 1) THEN
332 atom_p = my_atoms_a(idx)
333 ELSE
334 atom_p = my_atoms_b(idx)
335 END IF
336
337 n_loc_ri = ri_blk_sizes(atom_p)
338 cutoff_ri = cutoff_ri_per_atom(atom_p)
339
340 ! ---------------------------------------------------------------------
341 ! A. Sphere-local AO matrix Φ_μ(r_l): select the grid points with
342 ! |r_l - R_P| <= cutoff_ri(P), evaluate every AO on them, and drop
343 ! points whose largest AO amplitude is below EPS_FILTER.
344 ! ---------------------------------------------------------------------
345 CALL build_phi_on_sphere(bs_env, qs_kind_set, &
346 ri_rs_grid_points, atom_p, cutoff_ri, n_ao_total, &
347 local_grid_idx, n_local_grid, phi_local, &
348 ao_col_map, n_ao_used)
349
350 ! ---------------------------------------------------------------------
351 ! B. Right-hand side D_lP = Σ_μν Φ_μ(r_l) Φ_ν(r_l) (μν|P)
352 ! ---------------------------------------------------------------------
353 ALLOCATE (d_lp_local(n_local_grid, n_loc_ri))
354 d_lp_local = 0.0_dp
355
356 CALL compute_d_lp(bs_env, ctx_3c, phi_local, ao_col_map, d_lp_local, n_local_grid, &
357 n_loc_ri, atom_p, max_ao_size, atom_j_mepos, atom_j_stride)
358
359 ! Reduce per-subgroup-rank partials into the replicated d_lp_local.
360 ! Skipped for BLAS path: each rank has the full sum locally.
361 IF (use_dist) THEN
362 CALL para_env_sub%sum(d_lp_local)
363 END IF
364
365 ! ---------------------------------------------------------------------
366 ! C. Jacobi conditioning vector d_l = 1/sqrt(D_ll) and, on the BLAS path,
367 ! the dense conditioned Gram matrix D'_ll' = d_l D_ll' d_l' + λδ_ll'.
368 ! ---------------------------------------------------------------------
369 ALLOCATE (d_vec_local(n_local_grid))
370
371 IF (.NOT. use_dist) THEN
372 CALL build_gram_jacobi_blas(phi_local, n_local_grid, n_ao_used, &
373 bs_env%ri_rs%tikhonov, d_local, d_vec_local)
374 ELSE
375 ! ScaLAPACK path: only d_vec is needed here (= 1/||phi(r_l)||^2);
376 ! solve_D_lp_distributed builds its block-cyclic slice of D' internally
377 ! with the squared+scaled values, so no dense D_local on this rank.
378 CALL build_jacobi_diag_from_phi(phi_local, n_local_grid, n_ao_used, &
379 d_vec_local)
380 END IF
381
382 ! ---------------------------------------------------------------------
383 ! D. Pre-scale the RHS: D'_lP = d_l * D_lP
384 ! ---------------------------------------------------------------------
385 CALL scale_rows_by_diag(d_lp_local, d_vec_local, n_local_grid, n_loc_ri)
386
387 ! ---------------------------------------------------------------------
388 ! E. Cholesky solve Σ_l' D'_ll' Z'_l'P = D'_lP
389 ! (BLAS dpotrf/dpotrs or ScaLAPACK pdpotrf/pdpotrs)
390 ! ---------------------------------------------------------------------
391 IF (.NOT. use_dist) THEN
392 CALL timeset(routinen//"_dpotrf", handle_dpotrf)
393 CALL dpotrf('L', n_local_grid, d_local, n_local_grid, info)
394 CALL timestop(handle_dpotrf)
395 IF (info /= 0) cpabort("RI-RS Cholesky factorization failed")
396 CALL timeset(routinen//"_dpotrs", handle_dpotrs)
397 CALL dpotrs('L', n_local_grid, n_loc_ri, d_local, n_local_grid, &
398 d_lp_local, n_local_grid, info)
399 CALL timestop(handle_dpotrs)
400 IF (info /= 0) cpabort("RI-RS Cholesky solve failed")
401 DEALLOCATE (d_local)
402 ELSE
403 CALL solve_d_lp_distributed(phi_local, d_vec_local, d_lp_local, &
404 n_local_grid, n_ao_used, n_loc_ri, &
405 bs_env%ri_rs%tikhonov, &
406 para_env_sub, blacs_env_sub, &
407 fm_struct_d, fm_struct_b, fm_d, fm_b, info)
408 IF (info /= 0) cpabort("Distributed RI-RS Cholesky solve failed")
409 END IF
410
411 ! ---------------------------------------------------------------------
412 ! F. Undo the conditioning: Z_lP = d_l * Z'_lP
413 ! ---------------------------------------------------------------------
414 CALL scale_rows_by_diag(d_lp_local, d_vec_local, n_local_grid, n_loc_ri)
415
416 ! ---------------------------------------------------------------------
417 ! G. Scatter the solved Z columns back into the global sparse mat_Z_lP.
418 ! ---------------------------------------------------------------------
419 do_scatter = .true.
420 IF (use_dist) do_scatter = (para_env_sub%mepos == 0)
421 IF (do_scatter) THEN
422 CALL store_z_lp_columns(mat_z_lp, d_lp_local, local_grid_idx, n_local_grid, &
423 n_loc_ri, atom_p, r_blk_sizes, row_offset, &
424 bs_env%eps_filter)
425 END IF
426
427 DEALLOCATE (d_vec_local, d_lp_local)
428 DEALLOCATE (local_grid_idx, phi_local, ao_col_map)
429
430 ! Report each atom completed by the printing rank. No inter-rank communication is
431 ! needed; the final message below is printed only after the global synchronization.
432 n_done = n_done + 1
433 CALL print_z_lp_progress(bs_env, n_done, n_my_atoms, &
434 m_walltime() - item_start_time)
435 END DO ! idx: atoms of this phase owned by this rank / subgroup
436
437 ! Tear down the Phase-B subgroup (all ranks created it collectively).
438 IF (iphase == 2) THEN
439 CALL cp_blacs_env_release(blacs_env_sub)
440 CALL para_env_sub%free()
441 DEALLOCATE (para_env_sub)
442 END IF
443 END DO ! iphase
444
445 DEALLOCATE (cutoff_ri_per_atom)
446 DEALLOCATE (small_list, big_list)
447
448 CALL gw_3c_ctx_release(ctx_3c)
449
450 CALL dbcsr_finalize(mat_z_lp)
451
452 CALL para_env%sync()
453 CALL print_z_lp_progress(bs_env, natom, natom, m_walltime() - t1, &
454 all_mpi_ranks=.true.)
455
456 logger => cp_get_default_logger()
457
458 IF (btest(cp_print_key_should_output(logger%iter_info, input, key), cp_p_file)) THEN
459 CALL dbcsr_binary_write(matrix=mat_z_lp, filepath=trim(bs_env%prefix)//"Z_lP.matrix")
460 END IF
461
462 END IF
463
464 DEALLOCATE (row_offset, ri_blk_sizes, col_dist_ri)
465 CALL dbcsr_distribution_release(dist_z)
466
467 DEALLOCATE (ri_rs_grid_points)
468
469 CALL timestop(handle)
470
471 END SUBROUTINE compute_z_lp_standard
472
473! **************************************************************************************************
474!> \brief Prints the per-kind maximum RI-RS integration-sphere cutoff table.
475!> \param bs_env ...
476!> \param cutoff_ri_per_atom ...
477! **************************************************************************************************
478 SUBROUTINE print_sphere_cutoff_table(bs_env, cutoff_ri_per_atom)
479
480 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
481 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: cutoff_ri_per_atom
482
483 CHARACTER(LEN=*), PARAMETER :: routinen = 'print_sphere_cutoff_table'
484
485 INTEGER :: handle, iatom, ikind, nkind
486 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: cutoff_ri_per_kind
487 TYPE(atomic_kind_type), DIMENSION(:), POINTER :: atomic_kind_set
488 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
489
490 CALL timeset(routinen, handle)
491
492 atomic_kind_set => bs_env%ri_rs%atomic_kind_set
493 particle_set => bs_env%ri_rs%particle_set
494
495 IF (bs_env%unit_nr <= 0) THEN
496 CALL timestop(handle)
497 RETURN
498 END IF
499
500 nkind = SIZE(atomic_kind_set)
501 ALLOCATE (cutoff_ri_per_kind(nkind))
502 cutoff_ri_per_kind(:) = 0.0_dp
503
504 DO iatom = 1, bs_env%n_atom
505 ikind = particle_set(iatom)%atomic_kind%kind_number
506 cutoff_ri_per_kind(ikind) = max(cutoff_ri_per_kind(ikind), cutoff_ri_per_atom(iatom))
507 END DO
508
509 WRITE (bs_env%unit_nr, '(T2,A)') 'Per-kind maximum RI-RS sphere cutoff (Å):'
510 WRITE (bs_env%unit_nr, '(T4,A4,A14)') 'Kind', 'cutoff (Å)'
511 DO ikind = 1, nkind
512 WRITE (bs_env%unit_nr, '(T4,A4,F14.4)') &
513 atomic_kind_set(ikind)%element_symbol, &
514 cutoff_ri_per_kind(ikind)*angstrom
515 END DO
516 WRITE (bs_env%unit_nr, '(A)') ' '
517
518 DEALLOCATE (cutoff_ri_per_kind)
519
520 CALL timestop(handle)
521
522 END SUBROUTINE print_sphere_cutoff_table
523
524! **************************************************************************************************
525!> \brief LPT (longest-processing-time) assignment of the Z_lP atoms to workers (MPI ranks in
526!> Phase A, rank subgroups in Phase B): sort by estimated solve cost n_local_grid^3
527!> (the per-atom Cholesky dominates; the n^2 assembly terms order the atoms the same
528!> way) and greedily give each atom to the least-loaded worker.
529!> \param atom_list ...
530!> \param n_atoms ...
531!> \param n_local_grid_atom ...
532!> \param n_workers ...
533!> \param my_worker ...
534!> \param my_atoms ...
535!> \param max_over_mean ...
536! **************************************************************************************************
537 SUBROUTINE lpt_assign_atoms(atom_list, n_atoms, n_local_grid_atom, n_workers, my_worker, &
538 my_atoms, max_over_mean)
539
540 INTEGER, DIMENSION(:), INTENT(IN) :: atom_list
541 INTEGER, INTENT(IN) :: n_atoms
542 INTEGER, DIMENSION(:), INTENT(IN) :: n_local_grid_atom
543 INTEGER, INTENT(IN) :: n_workers, my_worker
544 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: my_atoms
545 REAL(kind=dp), INTENT(OUT) :: max_over_mean
546
547 CHARACTER(LEN=*), PARAMETER :: routinen = 'lpt_assign_atoms'
548
549 INTEGER :: handle, i, iw, n_mine, w_min
550 INTEGER, ALLOCATABLE, DIMENSION(:) :: mine_tmp, perm
551 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: cost, load
552
553 CALL timeset(routinen, handle)
554
555 max_over_mean = 1.0_dp
556 IF (n_atoms <= 0) THEN
557 ALLOCATE (my_atoms(0))
558 CALL timestop(handle)
559 RETURN
560 END IF
561
562 ALLOCATE (cost(n_atoms), perm(n_atoms), mine_tmp(n_atoms), load(n_workers))
563 DO i = 1, n_atoms
564 cost(i) = real(n_local_grid_atom(atom_list(i)), dp)**3
565 END DO
566 CALL sort(cost, n_atoms, perm) ! ascending; walk backwards for largest-first
567
568 load(:) = 0.0_dp
569 n_mine = 0
570 DO i = n_atoms, 1, -1
571 w_min = 1
572 DO iw = 2, n_workers
573 IF (load(iw) < load(w_min)) w_min = iw
574 END DO
575 load(w_min) = load(w_min) + cost(i)
576 IF (w_min - 1 == my_worker) THEN
577 n_mine = n_mine + 1
578 mine_tmp(n_mine) = atom_list(perm(i))
579 END IF
580 END DO
581
582 ALLOCATE (my_atoms(n_mine))
583 my_atoms(:) = mine_tmp(1:n_mine)
584 IF (sum(load) > 0.0_dp) max_over_mean = maxval(load)*real(n_workers, dp)/sum(load)
585
586 CALL timestop(handle)
587
588 END SUBROUTINE lpt_assign_atoms
589
590! **************************************************************************************************
591!> \brief Computes the dense localized right-hand side for one RI atom P,
592!>
593!> d_lP = Σ_μν Φ_μ(r_l) Φ_ν(r_l) (μν|P).
594!>
595!> RI atom P, OMP-threaded over (atom_j, atom_k) AO-pair blocks: per thread, build the 3c
596!> block, then contract grid-chunked pair densities into a private d_lp partial; partials
597!> are reduced into d_lp at the end.
598!> Pair screening is handled inside build_3c_integral_block_ctx via the `screened` output.
599!> \param bs_env ...
600!> \param ctx ...
601!> \param phi_val ...
602!> \param ao_col_map ...
603!> \param d_lp ...
604!> \param n_grid_total ...
605!> \param n_loc_ri ...
606!> \param atom_P ...
607!> \param max_ao_size ...
608!> \param atom_j_mepos ...
609!> \param atom_j_stride ...
610! **************************************************************************************************
611 SUBROUTINE compute_d_lp(bs_env, ctx, phi_val, ao_col_map, d_lp, n_grid_total, n_loc_ri, atom_P, &
612 max_ao_size, atom_j_mepos, atom_j_stride)
613
614 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
615 TYPE(gw_3c_ctx_type), INTENT(IN) :: ctx
616 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: phi_val
617 INTEGER, DIMENSION(:), INTENT(IN) :: ao_col_map
618 INTEGER, INTENT(IN) :: n_grid_total, n_loc_ri
619 REAL(kind=dp), INTENT(INOUT) :: d_lp(n_grid_total, n_loc_ri)
620 INTEGER, INTENT(IN) :: atom_p, max_ao_size, atom_j_mepos, &
621 atom_j_stride
622
623 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_d_lp'
624 INTEGER, PARAMETER :: grid_chunk = 1024
625
626 INTEGER :: atom_j, atom_k, c, handle, handle_dgemm, &
627 j, jk_idx, jsize, jstart, k, ksize, &
628 kstart, l, l0, n_grid_pair, point, ri
629 INTEGER, ALLOCATABLE :: grid_index(:)
630 LOGICAL :: screened
631 LOGICAL, ALLOCATABLE :: skip_grid_point(:, :)
632 REAL(kind=dp) :: pair_factor
633 REAL(kind=dp), ALLOCATABLE :: grid_result(:, :)
634 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: d_lp_prv, int_2d_prv, rho_chunk
635 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :) :: int_3c_prv
636 TYPE(gw_3c_ws_type) :: ws
637
638 CALL timeset(routinen, handle)
639
640 !$OMP PARALLEL DEFAULT(NONE) &
641 !$OMP SHARED(skip_grid_point, bs_env, ctx, phi_val, ao_col_map, d_lp, n_grid_total, n_loc_ri, atom_P, max_ao_size, &
642 !$OMP atom_j_mepos, atom_j_stride) &
643 !$OMP PRIVATE(grid_index, grid_result, n_grid_pair, point, pair_factor, &
644 !$OMP atom_j, atom_k, c, handle_dgemm, j, jk_idx, jsize, jstart, k, ksize, kstart, &
645 !$OMP l, l0, ri, screened, d_lp_prv, int_2d_prv, rho_chunk, int_3c_prv, ws)
646
647 CALL gw_3c_ws_create(ws, ctx)
648 ALLOCATE (int_3c_prv(max_ao_size, max_ao_size, n_loc_ri))
649 ALLOCATE (int_2d_prv(max_ao_size*max_ao_size, n_loc_ri))
650 ALLOCATE (rho_chunk(grid_chunk, max_ao_size*max_ao_size))
651 ALLOCATE (d_lp_prv(n_grid_total, n_loc_ri))
652 ALLOCATE (grid_index(n_grid_total), grid_result(grid_chunk, n_loc_ri))
653 d_lp_prv(:, :) = 0.0_dp
654
655 !$OMP SINGLE
656 ALLOCATE (skip_grid_point(n_grid_total, bs_env%n_atom))
657 CALL compute_skip_grid_point(bs_env, phi_val, ao_col_map, skip_grid_point)
658 !$OMP END SINGLE
659
660 ! MPI assigns each unordered pair through its first atom; OpenMP divides those atoms.
661 ! Skip only exact-zero pair-grid support, without a new screening threshold.
662 !$OMP DO SCHEDULE(DYNAMIC)
663 DO atom_j = atom_j_mepos + 1, bs_env%n_atom, atom_j_stride
664 DO atom_k = atom_j, bs_env%n_atom
665 jstart = ao_col_map(bs_env%i_ao_start_from_atom(atom_j))
666 kstart = ao_col_map(bs_env%i_ao_start_from_atom(atom_k))
667 IF (jstart == 0 .OR. kstart == 0) cycle
668 jsize = bs_env%i_ao_end_from_atom(atom_j) - bs_env%i_ao_start_from_atom(atom_j) + 1
669 ksize = bs_env%i_ao_end_from_atom(atom_k) - bs_env%i_ao_start_from_atom(atom_k) + 1
670
671 n_grid_pair = 0
672 DO point = 1, n_grid_total
673 IF (skip_grid_point(point, atom_j) .OR. skip_grid_point(point, atom_k)) cycle
674 n_grid_pair = n_grid_pair + 1
675 grid_index(n_grid_pair) = point
676 END DO
677 IF (n_grid_pair == 0) cycle
678 ! (μν|P) = (νμ|P): distinct atom pairs contribute twice.
679 pair_factor = 1.0_dp
680 IF (atom_j /= atom_k) pair_factor = 2.0_dp
681
682 int_3c_prv(1:jsize, 1:ksize, 1:n_loc_ri) = 0.0_dp
683
684 ! Compute B_{μν,P} = (μν|P); ctx-internal triangle-inequality screening on
685 ! kind_radius sets `screened=.TRUE.` for negligible triples.
686 CALL build_3c_integral_block_ctx(int_3c_prv(1:jsize, 1:ksize, 1:n_loc_ri), &
687 ctx, ws, atom_j=atom_j, atom_k=atom_k, atom_i=atom_p, &
688 screened=screened)
689
690 IF (screened) cycle
691
692 ! Flatten 3D B_{μν, P} tensor to 2D B_{(μν), P} matrix for BLAS
693 DO ri = 1, n_loc_ri
694 DO k = 1, ksize
695 DO j = 1, jsize
696 jk_idx = (k - 1)*jsize + j
697 int_2d_prv(jk_idx, ri) = int_3c_prv(j, k, ri)
698 END DO
699 END DO
700 END DO
701
702 ! Pair density ρ(l, μν) = Φ_μ(r_l) Φ_ν(r_l) in grid chunks, contracted on the fly:
703 ! d_{l,P} += ρ(l, μν) B_{(μν),P} (dgemm runs serially inside the parallel region)
704 DO l0 = 1, n_grid_pair, grid_chunk
705 c = min(grid_chunk, n_grid_pair - l0 + 1)
706 DO k = 1, ksize
707 DO j = 1, jsize
708 jk_idx = (k - 1)*jsize + j
709 DO l = 1, c
710 point = grid_index(l0 + l - 1)
711 rho_chunk(l, jk_idx) = phi_val(point, jstart + j - 1)* &
712 phi_val(point, kstart + k - 1)
713 END DO
714 END DO
715 END DO
716 CALL timeset(routinen//"_dgemm", handle_dgemm)
717 CALL dgemm("N", "N", c, n_loc_ri, jsize*ksize, &
718 pair_factor, rho_chunk, grid_chunk, &
719 int_2d_prv, max_ao_size*max_ao_size, &
720 0.0_dp, grid_result, grid_chunk)
721 DO ri = 1, n_loc_ri
722 DO l = 1, c
723 point = grid_index(l0 + l - 1)
724 d_lp_prv(point, ri) = d_lp_prv(point, ri) + grid_result(l, ri)
725 END DO
726 END DO
727 CALL timestop(handle_dgemm)
728 END DO
729 END DO
730 END DO
731 !$OMP END DO
732
733 !$OMP CRITICAL (compute_d_lp_reduce)
734 d_lp(1:n_grid_total, 1:n_loc_ri) = d_lp(1:n_grid_total, 1:n_loc_ri) + &
735 d_lp_prv(1:n_grid_total, 1:n_loc_ri)
736 !$OMP END CRITICAL (compute_d_lp_reduce)
737
738 DEALLOCATE (int_3c_prv, int_2d_prv, rho_chunk, d_lp_prv, grid_index, grid_result)
739 CALL gw_3c_ws_release(ws)
740
741 !$OMP SINGLE
742 DEALLOCATE (skip_grid_point)
743 !$OMP END SINGLE
744
745 !$OMP END PARALLEL
746
747 CALL timestop(handle)
748
749 END SUBROUTINE compute_d_lp
750
751! **************************************************************************************************
752!> \brief Marks grid points where all stored AO values of an atom are exactly zero.
753!> \param bs_env ...
754!> \param phi_val ...
755!> \param ao_col_map ...
756!> \param skip_grid_point ...
757! **************************************************************************************************
758 SUBROUTINE compute_skip_grid_point(bs_env, phi_val, ao_col_map, skip_grid_point)
759
760 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
761 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: phi_val
762 INTEGER, DIMENSION(:), INTENT(IN) :: ao_col_map
763 LOGICAL, DIMENSION(:, :), INTENT(OUT) :: skip_grid_point
764
765 INTEGER :: atom, first_ao, number_of_aos
766
767 skip_grid_point(:, :) = .true.
768 DO atom = 1, bs_env%n_atom
769 first_ao = ao_col_map(bs_env%i_ao_start_from_atom(atom))
770 IF (first_ao == 0) cycle
771 number_of_aos = bs_env%i_ao_end_from_atom(atom) - bs_env%i_ao_start_from_atom(atom) + 1
772 skip_grid_point(:, atom) = all(phi_val(:, first_ao:first_ao + number_of_aos - 1) == 0.0_dp, dim=2)
773 END DO
774
775 END SUBROUTINE compute_skip_grid_point
776
777! **************************************************************************************************
778!> \brief Computes the RI-RS matrix Z_lP for an automatically optimized RI basis.
779!>
780!> The fitting coefficients Z_lP satisfy
781!>
782!> Σ_l' D_ll' Z_l'P = d_lP,
783!> D_ll' = [Σ_μ Φ_μ(r_l) Φ_μ(r_l')]²,
784!> d_lP = Σ_μν Φ_μ(r_l) Φ_ν(r_l) (μν|P).
785!>
786!> For the standard RI-RS fit, P is an atom-centered RI function. Here an optimized
787!> function may contain reference functions on neighboring atoms,
788!>
789!> Φ_p(r) = Σ_A Σ_{P∈A} U_Pp^A Φ_P^A(r),
790!>
791!> so all atomic contributions to d_lp must be accumulated before solving for Z_lp.
792!> If every optimized column uses the complete RI-RS grid, all columns are solved in one
793!> system. Otherwise, each atom-blocked column set is fitted on its own integration sphere;
794!> grid points outside that sphere are excluded from its fitting equations.
795!> \param qs_env ...
796!> \param bs_env ...
797!> \param ri_rs_grid_points ...
798!> \param mat_phi_mu_l ...
799!> \param mat_Z_lP ...
800! **************************************************************************************************
801 SUBROUTINE compute_z_lp_auto_ri(qs_env, bs_env, ri_rs_grid_points, mat_phi_mu_l, mat_Z_lP)
802 TYPE(qs_environment_type), POINTER :: qs_env
803 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
804 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: ri_rs_grid_points
805 TYPE(dbcsr_type), INTENT(INOUT) :: mat_phi_mu_l
806 TYPE(dbcsr_type), INTENT(OUT) :: mat_z_lp
807
808 CHARACTER(LEN=*), PARAMETER :: key = 'PROPERTIES%BANDSTRUCTURE%GW%PRINT%RESTART', &
809 routinen = 'compute_Z_lP_auto_ri'
810
811 INTEGER :: ab_block, atom_a, atom_b, block_size_b, column_first, first_p_ab, fit_atom, &
812 handle, handle_dpotrf, handle_dpotrs, info, max_ao_size, max_nri_ref, mypcol, myprow, &
813 n_ao_total, n_ao_used, n_done, n_my_atoms, n_to_a, natom, ncol, ngrid, nri, nri_ref_a, &
814 nri_ref_b, output_offset, ri_atom
815 INTEGER, ALLOCATABLE, DIMENSION(:) :: ao_col_map, local_grid_idx, row_offset
816 INTEGER, DIMENSION(:), POINTER :: col_dist_ri, ri_blk_sizes, &
817 row_dist_grid, row_size_grid
818 LOGICAL :: ab_block_local, common_grid_available, &
819 have_fitted_columns, &
820 reuse_atomic_integrals
821 LOGICAL, ALLOCATABLE, DIMENSION(:) :: active_atom
822 REAL(kind=dp) :: cutoff_ri, item_start_time, r_c, t1
823 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: d_vec
824 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: d_local, d_lp_all, d_lp_local, &
825 phi_local, u_pp_ab
826 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :) :: u_pp_by_atom
827 REAL(kind=dp), DIMENSION(3) :: center
828 TYPE(cell_type), POINTER :: cell
829 TYPE(cp_logger_type), POINTER :: logger
830 TYPE(dbcsr_distribution_type) :: dist_z
831 TYPE(dbcsr_type) :: mat_rhs
832 TYPE(gw_3c_ctx_type) :: ctx_3c
833 TYPE(mp_para_env_type), POINTER :: para_env, para_env_col
834 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
835 TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kind_set
836 TYPE(section_vals_type), POINTER :: input
837
838 CALL timeset(routinen, handle)
839 t1 = m_walltime()
840 IF (bs_env%unit_nr > 0) THEN
841 WRITE (bs_env%unit_nr, '(A)') ' '
842 WRITE (bs_env%unit_nr, '(T2,A)') 'Started computing Z_lP'
843 CALL m_flush(bs_env%unit_nr)
844 END IF
845 CALL get_qs_env(qs_env, para_env=para_env, particle_set=particle_set, input=input, &
846 qs_kind_set=qs_kind_set, cell=cell)
847 NULLIFY (para_env_col)
848
849 natom = bs_env%n_atom
850 n_ao_total = bs_env%i_ao_end_from_atom(natom)
851 cpassert(bs_env%auto_ri%AB_block_count > 0)
852 cpassert(SIZE(particle_set) == natom)
853
854 CALL prepare_z_lp_auto_ri(bs_env, mat_phi_mu_l, mat_z_lp, dist_z, row_size_grid, &
855 row_dist_grid, col_dist_ri, ri_blk_sizes, row_offset, &
856 myprow, mypcol, max_ao_size)
857
858 CALL gw_3c_ctx_create(ctx_3c, qs_env, bs_env%ri_metric, &
859 basis_j=bs_env%basis_set_AO, basis_k=bs_env%basis_set_AO, &
860 basis_i=bs_env%basis_set_RI)
861
862 ! Check whether one grid can represent d_lp for every optimized atomic column block.
863 CALL common_z_lp_grid_available(bs_env, ri_rs_grid_points, &
864 common_grid_available, have_fitted_columns)
865
866 IF (common_grid_available .AND. have_fitted_columns) THEN
867 ! Evaluate Φ_μ(r_l) once on the common grid.
868 CALL build_phi_on_complete_grid(bs_env, qs_kind_set, &
869 ri_rs_grid_points, n_ao_total, local_grid_idx, ngrid, &
870 phi_local, ao_col_map, n_ao_used)
871 nri = sum(ri_blk_sizes)
872 ALLOCATE (d_lp_all(ngrid, nri), source=0.0_dp)
873 ! d_lp = Σ_A Σ_{P∈A} d_lP U_Pp for every optimized RI function q.
874 CALL compute_d_lp_auto_ri_batch(bs_env, ctx_3c, phi_local, ao_col_map, d_lp_all, ngrid, &
875 max_ao_size, para_env%mepos, para_env%num_pe)
876 CALL para_env%sum(d_lp_all)
877 ALLOCATE (d_vec(ngrid))
878 ! D_ll' = [Σ_μ Φ_μ(r_l) Φ_μ(r_l')]², followed by D Z = d.
879 CALL build_gram_jacobi_blas(phi_local, ngrid, n_ao_used, bs_env%ri_rs%tikhonov, &
880 d_local, d_vec)
881 CALL scale_rows_by_diag(d_lp_all, d_vec, ngrid, nri)
882 CALL timeset(routinen//'_dpotrf', handle_dpotrf)
883 CALL dpotrf('L', ngrid, d_local, ngrid, info)
884 CALL timestop(handle_dpotrf)
885 cpassert(info == 0)
886 CALL timeset(routinen//'_dpotrs', handle_dpotrs)
887 CALL dpotrs('L', ngrid, nri, d_local, ngrid, d_lp_all, ngrid, info)
888 CALL timestop(handle_dpotrs)
889 cpassert(info == 0)
890 CALL scale_rows_by_diag(d_lp_all, d_vec, ngrid, nri)
891
892 DO ab_block = 1, bs_env%auto_ri%AB_block_count
893 atom_a = bs_env%auto_ri%AB_atom_A(ab_block)
894 atom_b = bs_env%auto_ri%AB_atom_B(ab_block)
895 ab_block_local = col_dist_ri(atom_a) == mypcol
896 IF (atom_b /= atom_a) ab_block_local = ab_block_local .OR. col_dist_ri(atom_b) == mypcol
897 IF (.NOT. ab_block_local) cycle
898 ncol = bs_env%auto_ri%AB_size_opt_RI(ab_block)
899 n_to_a = bs_env%auto_ri%AB_size_opt_RI_to_A(ab_block)
900 IF (n_to_a > 0 .AND. col_dist_ri(atom_a) == mypcol) THEN
901 output_offset = sum(ri_blk_sizes(:atom_a - 1)) + &
902 bs_env%auto_ri%AB_first_p_A(ab_block) - 1
903 CALL add_z_lp_columns(mat_z_lp, &
904 d_lp_all(:, output_offset + 1:output_offset + n_to_a), &
905 local_grid_idx, ngrid, atom_a, &
906 bs_env%auto_ri%AB_first_p_A(ab_block), &
907 ri_blk_sizes(atom_a), row_size_grid, row_offset, &
908 row_dist_grid, &
909 myprow, bs_env%eps_filter)
910 END IF
911 IF (n_to_a < ncol) THEN
912 cpassert(atom_b /= atom_a)
913 IF (col_dist_ri(atom_b) == mypcol) THEN
914 block_size_b = ri_blk_sizes(atom_b)
915 output_offset = sum(ri_blk_sizes(:atom_b - 1)) + &
916 bs_env%auto_ri%AB_first_p_B(ab_block) - 1
917 CALL add_z_lp_columns(mat_z_lp, &
918 d_lp_all(:, output_offset + 1: &
919 output_offset + ncol - n_to_a), &
920 local_grid_idx, ngrid, atom_b, &
921 bs_env%auto_ri%AB_first_p_B(ab_block), block_size_b, &
922 row_size_grid, row_offset, row_dist_grid, myprow, &
923 bs_env%eps_filter)
924 END IF
925 END IF
926 END DO
927 DEALLOCATE (d_local, d_vec, d_lp_all, local_grid_idx, phi_local, ao_col_map)
928 ELSE
929 reuse_atomic_integrals = &
930 bs_env%ri_rs%cutoff_radius_ri_ao <= 0.0_dp .OR. &
931 bs_env%ri_rs%cutoff_radius_ri_ao <= &
932 minval(bs_env%ri_rs%radius_ao_per_atom)
933 IF (reuse_atomic_integrals) THEN
934 ! Form d_lp = Σ_A Σ_{P∈A} d_lP U_Pp before fitting each atomic column block.
935 CALL compute_auto_ri_d_lp(qs_env, bs_env, ctx_3c, &
936 ri_rs_grid_points, mat_phi_mu_l, mat_rhs, &
937 max_ao_size)
938 CALL dbcsr_release(mat_z_lp)
939 CALL dbcsr_create(mat_z_lp, name='mat_Z_lP localized AA/AB', dist=dist_z, &
940 matrix_type=dbcsr_type_no_symmetry, row_blk_size=row_size_grid, &
941 col_blk_size=ri_blk_sizes)
942 ! Solve Σ_l' D_ll' Z_l'q = d_lp on each atomic fitting grid.
943 CALL fit_auto_ri_z_lp(qs_env, bs_env, ri_rs_grid_points, mat_rhs, mat_z_lp)
944 CALL dbcsr_release(mat_rhs)
945 ELSE
946 ALLOCATE (para_env_col)
947 CALL para_env_col%from_split(para_env, mypcol)
948 n_my_atoms = 0
949 DO fit_atom = 1, natom
950 IF (col_dist_ri(fit_atom) /= mypcol) cycle
951 IF (ri_blk_sizes(fit_atom) == 0) cycle
952 n_my_atoms = n_my_atoms + 1
953 END DO
954 n_done = 0
955 max_nri_ref = 0
956 DO ri_atom = 1, natom
957 max_nri_ref = max(max_nri_ref, get_ref_ri_size(bs_env, ri_atom))
958 END DO
959 DO fit_atom = 1, natom
960 IF (col_dist_ri(fit_atom) /= mypcol) cycle
961 ncol = ri_blk_sizes(fit_atom)
962 IF (ncol == 0) cycle
963 item_start_time = m_walltime()
964 ALLOCATE (u_pp_by_atom(max_nri_ref, ncol, natom), source=0.0_dp)
965 ALLOCATE (active_atom(natom), source=.false.)
966
967 DO ab_block = 1, bs_env%auto_ri%AB_block_count
968 atom_a = bs_env%auto_ri%AB_atom_A(ab_block)
969 atom_b = bs_env%auto_ri%AB_atom_B(ab_block)
970 n_to_a = bs_env%auto_ri%AB_size_opt_RI_to_A(ab_block)
971 IF (fit_atom == atom_a .AND. n_to_a > 0) THEN
972 column_first = bs_env%auto_ri%AB_first_p_A(ab_block)
973 first_p_ab = 1
974 ncol = n_to_a
975 ELSE IF (fit_atom == atom_b .AND. &
976 n_to_a < bs_env%auto_ri%AB_size_opt_RI(ab_block)) THEN
977 column_first = bs_env%auto_ri%AB_first_p_B(ab_block)
978 first_p_ab = n_to_a + 1
979 ncol = bs_env%auto_ri%AB_size_opt_RI(ab_block) - n_to_a
980 ELSE
981 cycle
982 END IF
983 CALL get_u_pp_ab(bs_env%auto_ri, ab_block, u_pp_ab)
984
985 nri_ref_a = get_ref_ri_size(bs_env, atom_a)
986 u_pp_by_atom(1:nri_ref_a, &
987 column_first:column_first + ncol - 1, atom_a) = &
988 u_pp_ab( &
989 1:nri_ref_a, first_p_ab:first_p_ab + ncol - 1)
990 active_atom(atom_a) = .true.
991 IF (atom_b /= atom_a) THEN
992 nri_ref_b = get_ref_ri_size(bs_env, atom_b)
993 u_pp_by_atom(1:nri_ref_b, &
994 column_first:column_first + ncol - 1, atom_b) = &
995 u_pp_ab( &
996 nri_ref_a + 1:nri_ref_a + nri_ref_b, &
997 first_p_ab:first_p_ab + ncol - 1)
998 active_atom(atom_b) = .true.
999 END IF
1000 DEALLOCATE (u_pp_ab)
1001 END DO
1002
1003 center = particle_set(fit_atom)%r
1004 IF (bs_env%ri_rs%cutoff_radius_ri_rs > 0.0_dp) THEN
1005 cutoff_ri = bs_env%ri_rs%cutoff_radius_ri_rs
1006 ELSE
1007 r_c = bs_env%ri_metric%cutoff_radius
1008 cutoff_ri = 0.0_dp
1009 DO ri_atom = 1, natom
1010 IF (.NOT. active_atom(ri_atom)) cycle
1011 cutoff_ri = max(cutoff_ri, &
1012 r_c + bs_env%ri_rs%radius_ri_per_atom(ri_atom) + &
1013 norm2(center - particle_set(ri_atom)%r))
1014 END DO
1015 END IF
1016 CALL build_phi_on_sphere(bs_env, qs_kind_set, &
1017 ri_rs_grid_points, fit_atom, cutoff_ri, n_ao_total, &
1018 local_grid_idx, ngrid, phi_local, ao_col_map, n_ao_used, &
1019 center=center)
1020 ncol = ri_blk_sizes(fit_atom)
1021 ALLOCATE (d_lp_local(ngrid, ncol), source=0.0_dp)
1022 ! d_lp = Σ_A Σ_{P∈A} d_lP U_Pp on the grid of this atomic column block.
1023 CALL compute_d_lp_auto_ri_atoms(bs_env, ctx_3c, phi_local, ao_col_map, &
1024 d_lp_local, ngrid, u_pp_by_atom, &
1025 active_atom, max_ao_size, &
1026 para_env_col%mepos, para_env_col%num_pe)
1027 CALL para_env_col%sum(d_lp_local)
1028 ALLOCATE (d_vec(ngrid))
1029 ! D_ll' = [Σ_μ Φ_μ(r_l) Φ_μ(r_l')]², followed by D Z = d.
1030 CALL build_gram_jacobi_blas(phi_local, ngrid, n_ao_used, bs_env%ri_rs%tikhonov, &
1031 d_local, d_vec)
1032 CALL scale_rows_by_diag(d_lp_local, d_vec, ngrid, ncol)
1033 CALL timeset(routinen//'_dpotrf', handle_dpotrf)
1034 CALL dpotrf('L', ngrid, d_local, ngrid, info)
1035 CALL timestop(handle_dpotrf)
1036 cpassert(info == 0)
1037 CALL timeset(routinen//'_dpotrs', handle_dpotrs)
1038 CALL dpotrs('L', ngrid, ncol, d_local, ngrid, d_lp_local, ngrid, info)
1039 CALL timestop(handle_dpotrs)
1040 cpassert(info == 0)
1041 CALL scale_rows_by_diag(d_lp_local, d_vec, ngrid, ncol)
1042 CALL add_z_lp_columns(mat_z_lp, d_lp_local, local_grid_idx, ngrid, fit_atom, 1, &
1043 ri_blk_sizes(fit_atom), row_size_grid, row_offset, &
1044 row_dist_grid, &
1045 myprow, bs_env%eps_filter)
1046 DEALLOCATE (d_local, d_vec, d_lp_local, local_grid_idx, phi_local, ao_col_map)
1047 DEALLOCATE (u_pp_by_atom, active_atom)
1048 n_done = n_done + 1
1049 CALL print_z_lp_progress(bs_env, n_done, n_my_atoms, &
1050 m_walltime() - item_start_time)
1051 END DO
1052 CALL para_env_col%free()
1053 DEALLOCATE (para_env_col)
1054 END IF
1055 END IF
1056 CALL gw_3c_ctx_release(ctx_3c)
1057
1058 CALL dbcsr_filter(mat_z_lp, bs_env%eps_filter)
1059 CALL dbcsr_finalize(mat_z_lp)
1060 CALL para_env%sync()
1061 CALL print_z_lp_progress(bs_env, natom, natom, m_walltime() - t1, &
1062 all_mpi_ranks=.true.)
1063 logger => cp_get_default_logger()
1064 IF (btest(cp_print_key_should_output(logger%iter_info, input, key), cp_p_file)) THEN
1065 CALL dbcsr_binary_write(matrix=mat_z_lp, filepath=trim(bs_env%prefix)//'Z_lP.matrix')
1066 END IF
1067
1068 DEALLOCATE (row_offset, ri_blk_sizes, col_dist_ri)
1069 CALL dbcsr_distribution_release(dist_z)
1070 CALL timestop(handle)
1071
1072 END SUBROUTINE compute_z_lp_auto_ri
1073
1074! **************************************************************************************************
1075!> \brief Prints completed Z_lP atoms and execution time from the printing rank.
1076!> \param bs_env GW environment containing the output unit.
1077!> \param n_done Number of atoms completed by the printing rank.
1078!> \param n_total Total number of atoms assigned to the printing rank.
1079!> \param execution_time Wall-clock time used for the reported work.
1080!> \param all_mpi_ranks Whether all MPI ranks have completed the calculation.
1081! **************************************************************************************************
1082 SUBROUTINE print_z_lp_progress(bs_env, n_done, n_total, execution_time, all_mpi_ranks)
1083 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1084 INTEGER, INTENT(IN) :: n_done, n_total
1085 REAL(kind=dp), INTENT(IN) :: execution_time
1086 LOGICAL, INTENT(IN), OPTIONAL :: all_mpi_ranks
1087
1088 CHARACTER(LEN=*), PARAMETER :: routinen = 'print_Z_lP_progress'
1089
1090 INTEGER :: handle
1091 LOGICAL :: completed
1092
1093 CALL timeset(routinen, handle)
1094
1095 IF (bs_env%unit_nr > 0) THEN
1096 completed = .false.
1097 IF (PRESENT(all_mpi_ranks)) completed = all_mpi_ranks
1098 IF (completed) THEN
1099 WRITE (bs_env%unit_nr, '(T2,A,T58,A,F7.1,A,/)') &
1100 'Computed Z_lP (all MPI ranks) for all atoms,', &
1101 'Execution time', execution_time, ' s'
1102 ELSE
1103 WRITE (bs_env%unit_nr, '(T2,A,I11,A,I3,A,F7.1,A)') &
1104 'Computed Z_lP (MPI rank 0) for atom', n_done, ' /', n_total, &
1105 ', Execution time', execution_time, ' s'
1106 END IF
1107 CALL m_flush(bs_env%unit_nr)
1108 END IF
1109
1110 CALL timestop(handle)
1111
1112 END SUBROUTINE print_z_lp_progress
1113
1114! **************************************************************************************************
1115!> \brief Prepares the distributed Z_lP matrix and its atom-blocked column layout. Column block A
1116!> contains all optimized functions assigned to atom A. Every local block is reserved once
1117!> because several AA/AB contraction blocks may contribute to the same block.
1118!> \param bs_env ...
1119!> \param mat_phi_mu_l ...
1120!> \param mat_Z_lP ...
1121!> \param dist_Z ...
1122!> \param row_size_grid ...
1123!> \param row_dist_grid ...
1124!> \param col_dist_ri ...
1125!> \param ri_blk_sizes ...
1126!> \param row_offset ...
1127!> \param myprow ...
1128!> \param mypcol ...
1129!> \param max_ao_size ...
1130! **************************************************************************************************
1131 SUBROUTINE prepare_z_lp_auto_ri(bs_env, mat_phi_mu_l, mat_Z_lP, dist_Z, row_size_grid, &
1132 row_dist_grid, col_dist_ri, ri_blk_sizes, row_offset, &
1133 myprow, mypcol, max_ao_size)
1134 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1135 TYPE(dbcsr_type), INTENT(IN) :: mat_phi_mu_l
1136 TYPE(dbcsr_type), INTENT(OUT) :: mat_z_lp
1137 TYPE(dbcsr_distribution_type), INTENT(OUT) :: dist_z
1138 INTEGER, DIMENSION(:), POINTER :: row_size_grid, row_dist_grid, &
1139 col_dist_ri, ri_blk_sizes
1140 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: row_offset
1141 INTEGER, INTENT(OUT) :: myprow, mypcol, max_ao_size
1142
1143 CHARACTER(LEN=*), PARAMETER :: routinen = 'prepare_Z_lP_auto_ri'
1144
1145 INTEGER :: handle, i_blk, iatom, npcol
1146 TYPE(dbcsr_distribution_type) :: dist_phi
1147
1148 CALL timeset(routinen, handle)
1149
1150 CALL dbcsr_get_info(mat_phi_mu_l, row_blk_size=row_size_grid, distribution=dist_phi)
1151 CALL dbcsr_distribution_get(dist_phi, row_dist=row_dist_grid, npcols=npcol, &
1152 myprow=myprow, mypcol=mypcol)
1153 ALLOCATE (row_offset(SIZE(row_size_grid)))
1154 row_offset(1) = 0
1155 DO i_blk = 2, SIZE(row_size_grid)
1156 row_offset(i_blk) = row_offset(i_blk - 1) + row_size_grid(i_blk - 1)
1157 END DO
1158
1159 ALLOCATE (ri_blk_sizes(bs_env%n_atom), col_dist_ri(bs_env%n_atom))
1160 ri_blk_sizes = bs_env%auto_ri%sizes_opt_RI
1161 DO iatom = 1, bs_env%n_atom
1162 col_dist_ri(iatom) = mod(iatom - 1, npcol)
1163 END DO
1164 CALL dbcsr_distribution_new(dist_z, template=dist_phi, row_dist=row_dist_grid, &
1165 col_dist=col_dist_ri)
1166 CALL dbcsr_create(mat_z_lp, name='mat_Z_lP localized AA/AB', dist=dist_z, &
1167 matrix_type=dbcsr_type_no_symmetry, row_blk_size=row_size_grid, &
1168 col_blk_size=ri_blk_sizes)
1169 CALL dbcsr_reserve_all_blocks(mat_z_lp)
1170 CALL dbcsr_set(mat_z_lp, 0.0_dp)
1171
1172 max_ao_size = 0
1173 DO iatom = 1, bs_env%n_atom
1174 max_ao_size = max(max_ao_size, bs_env%i_ao_end_from_atom(iatom) - &
1175 bs_env%i_ao_start_from_atom(iatom) + 1)
1176 END DO
1177
1178 CALL timestop(handle)
1179
1180 END SUBROUTINE prepare_z_lp_auto_ri
1181
1182! **************************************************************************************************
1183!> \brief Tests whether every fitted atomic block can use the complete RI-RS grid.
1184!>
1185!> For a block centered on A, every grid point must satisfy
1186!>
1187!> |r_l - R_A| <= R_A^fit,
1188!>
1189!> and every atom B whose AOs can contribute must satisfy
1190!>
1191!> |R_B - R_A| <= R_B^AO + R_A^fit.
1192!>
1193!> Pair-midpoint grids do not satisfy this atom-centered criterion in general.
1194!> \param bs_env ...
1195!> \param ri_rs_grid_points ...
1196!> \param common_grid_available ...
1197!> \param have_fitted_columns ...
1198! **************************************************************************************************
1199 SUBROUTINE common_z_lp_grid_available(bs_env, ri_rs_grid_points, &
1200 common_grid_available, have_fitted_columns)
1201 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1202 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: ri_rs_grid_points
1203 LOGICAL, INTENT(OUT) :: common_grid_available, &
1204 have_fitted_columns
1205
1206 CHARACTER(LEN=*), PARAMETER :: routinen = 'common_Z_lP_grid_available'
1207
1208 INTEGER :: ab_block, atom_a, atom_b, fit_atom, &
1209 handle, iatom, igrid, n_to_a, natom
1210 LOGICAL, ALLOCATABLE, DIMENSION(:) :: active_atom
1211 REAL(kind=dp) :: cutoff_ri
1212 REAL(kind=dp), DIMENSION(3) :: center
1213 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
1214
1215 CALL timeset(routinen, handle)
1216
1217 particle_set => bs_env%ri_rs%particle_set
1218 natom = bs_env%n_atom
1219 common_grid_available = .true.
1220 have_fitted_columns = .false.
1221 ALLOCATE (active_atom(natom))
1222 DO fit_atom = 1, natom
1223 IF (bs_env%auto_ri%sizes_opt_RI(fit_atom) == 0) cycle
1224 have_fitted_columns = .true.
1225 active_atom = .false.
1226 DO ab_block = 1, bs_env%auto_ri%AB_block_count
1227 atom_a = bs_env%auto_ri%AB_atom_A(ab_block)
1228 atom_b = bs_env%auto_ri%AB_atom_B(ab_block)
1229 n_to_a = bs_env%auto_ri%AB_size_opt_RI_to_A(ab_block)
1230 IF (.NOT. (fit_atom == atom_a .AND. n_to_a > 0) .AND. &
1231 .NOT. (fit_atom == atom_b .AND. &
1232 n_to_a < bs_env%auto_ri%AB_size_opt_RI(ab_block))) cycle
1233 active_atom(atom_a) = .true.
1234 IF (atom_b /= atom_a) active_atom(atom_b) = .true.
1235 END DO
1236
1237 center = particle_set(fit_atom)%r
1238 IF (bs_env%ri_rs%cutoff_radius_ri_rs > 0.0_dp) THEN
1239 cutoff_ri = bs_env%ri_rs%cutoff_radius_ri_rs
1240 ELSE
1241 cutoff_ri = 0.0_dp
1242 DO iatom = 1, natom
1243 IF (.NOT. active_atom(iatom)) cycle
1244 cutoff_ri = max(cutoff_ri, bs_env%ri_metric%cutoff_radius + &
1245 bs_env%ri_rs%radius_ri_per_atom(iatom) + &
1246 norm2(center - particle_set(iatom)%r))
1247 END DO
1248 END IF
1249
1250 DO igrid = 1, SIZE(ri_rs_grid_points, 2)
1251 IF (norm2(ri_rs_grid_points(1:3, igrid) - center) > cutoff_ri) THEN
1252 common_grid_available = .false.
1253 EXIT
1254 END IF
1255 END DO
1256 IF (.NOT. common_grid_available) EXIT
1257
1258 DO iatom = 1, natom
1259 IF (norm2(particle_set(iatom)%r - center) > &
1260 bs_env%ri_rs%radius_ao_per_atom(iatom) + cutoff_ri) THEN
1261 common_grid_available = .false.
1262 EXIT
1263 END IF
1264 END DO
1265 IF (.NOT. common_grid_available) EXIT
1266 END DO
1267 DEALLOCATE (active_atom)
1268
1269 CALL timestop(handle)
1270
1271 END SUBROUTINE common_z_lp_grid_available
1272
1273! **************************************************************************************************
1274!> \brief Evaluates the AO collocation matrix on the complete RI-RS grid,
1275!>
1276!> Φ_lμ = Φ_μ(r_l), l = 1, ..., N_grid.
1277!>
1278!> "Complete common grid" means that the same full set of grid points is valid for every
1279!> optimized RI column block. The enclosing sphere is only an implementation device passed
1280!> to build_phi_on_sphere; it contains every r_l and every AO center, so its chosen center
1281!> does not select an AA or AB contraction.
1282!> \param bs_env ...
1283!> \param qs_kind_set ...
1284!> \param ri_rs_grid_points ...
1285!> \param n_ao_total ...
1286!> \param local_grid_idx ...
1287!> \param ngrid ...
1288!> \param phi_local ...
1289!> \param ao_col_map ...
1290!> \param n_ao_used ...
1291! **************************************************************************************************
1292 SUBROUTINE build_phi_on_complete_grid(bs_env, qs_kind_set, &
1293 ri_rs_grid_points, n_ao_total, local_grid_idx, ngrid, &
1294 phi_local, ao_col_map, n_ao_used)
1295 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1296 TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kind_set
1297 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: ri_rs_grid_points
1298 INTEGER, INTENT(IN) :: n_ao_total
1299 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: local_grid_idx
1300 INTEGER, INTENT(OUT) :: ngrid
1301 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :), &
1302 INTENT(OUT) :: phi_local
1303 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: ao_col_map
1304 INTEGER, INTENT(OUT) :: n_ao_used
1305
1306 CHARACTER(LEN=*), PARAMETER :: routinen = 'build_phi_on_complete_grid'
1307
1308 INTEGER :: handle, iatom, igrid, reference_atom
1309 REAL(kind=dp) :: cutoff_ri
1310 REAL(kind=dp), DIMENSION(3) :: center
1311 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
1312
1313 CALL timeset(routinen, handle)
1314
1315 particle_set => bs_env%ri_rs%particle_set
1316 reference_atom = 1
1317 center = particle_set(reference_atom)%r
1318 cutoff_ri = 0.0_dp
1319 DO igrid = 1, SIZE(ri_rs_grid_points, 2)
1320 cutoff_ri = max(cutoff_ri, norm2(ri_rs_grid_points(1:3, igrid) - center))
1321 END DO
1322 DO iatom = 1, bs_env%n_atom
1323 cutoff_ri = max(cutoff_ri, norm2(particle_set(iatom)%r - center))
1324 END DO
1325 cutoff_ri = cutoff_ri + 1.0_dp
1326
1327 CALL build_phi_on_sphere(bs_env, qs_kind_set, &
1328 ri_rs_grid_points, reference_atom, cutoff_ri, n_ao_total, &
1329 local_grid_idx, ngrid, phi_local, ao_col_map, n_ao_used, &
1330 center=center)
1331
1332 CALL timestop(handle)
1333
1334 END SUBROUTINE build_phi_on_complete_grid
1335
1336! **************************************************************************************************
1337!> \brief Writes Z_l,p0+p += z_lp into the process-owned grid-row blocks of one atomic column
1338!> block. The routine performs no explicit MPI communication; DBCSR summation combines
1339!> contributions when a block receives columns from more than one contraction group.
1340!> \param mat_Z_lP ...
1341!> \param z_block ...
1342!> \param local_grid_idx ...
1343!> \param n_local_grid ...
1344!> \param atom_index ...
1345!> \param first_column ...
1346!> \param atom_block_size ...
1347!> \param r_blk_sizes ...
1348!> \param row_offset ...
1349!> \param row_dist ...
1350!> \param myprow ...
1351!> \param eps_filter ...
1352! **************************************************************************************************
1353 SUBROUTINE add_z_lp_columns(mat_Z_lP, z_block, local_grid_idx, n_local_grid, atom_index, &
1354 first_column, atom_block_size, r_blk_sizes, row_offset, row_dist, &
1355 myprow, eps_filter)
1356
1357 TYPE(dbcsr_type), INTENT(INOUT) :: mat_z_lp
1358 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: z_block
1359 INTEGER, DIMENSION(:), INTENT(IN) :: local_grid_idx
1360 INTEGER, INTENT(IN) :: n_local_grid, atom_index, first_column, &
1361 atom_block_size
1362 INTEGER, DIMENSION(:), INTENT(IN) :: r_blk_sizes, row_offset, row_dist
1363 INTEGER, INTENT(IN) :: myprow
1364 REAL(kind=dp), INTENT(IN) :: eps_filter
1365
1366 CHARACTER(LEN=*), PARAMETER :: routinen = 'add_Z_lP_columns'
1367
1368 INTEGER :: current_chunk_size, g_pt, handle, i_blk, &
1369 loc_ptr, ncolumn, r_end, r_start
1370 LOGICAL :: row_owned
1371 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: z_blk
1372
1373 CALL timeset(routinen, handle)
1374
1375 ncolumn = SIZE(z_block, 2)
1376 cpassert(first_column > 0)
1377 cpassert(first_column + ncolumn - 1 <= atom_block_size)
1378 ALLOCATE (z_blk(maxval(r_blk_sizes), atom_block_size), source=0.0_dp)
1379 loc_ptr = 1
1380 DO i_blk = 1, SIZE(r_blk_sizes)
1381 r_start = row_offset(i_blk) + 1
1382 r_end = row_offset(i_blk) + r_blk_sizes(i_blk)
1383 current_chunk_size = r_blk_sizes(i_blk)
1384 row_owned = row_dist(i_blk) == myprow
1385 z_blk = 0.0_dp
1386 DO WHILE (loc_ptr <= n_local_grid)
1387 g_pt = local_grid_idx(loc_ptr)
1388 IF (g_pt > r_end) EXIT
1389 IF (row_owned) THEN
1390 z_blk(g_pt - r_start + 1, first_column:first_column + ncolumn - 1) = &
1391 z_block(loc_ptr, 1:ncolumn)
1392 END IF
1393 loc_ptr = loc_ptr + 1
1394 END DO
1395 IF (row_owned .AND. maxval(abs(z_blk(1:current_chunk_size, :))) > eps_filter) THEN
1396 CALL dbcsr_put_block(mat_z_lp, row=i_blk, col=atom_index, &
1397 block=z_blk(1:current_chunk_size, :), summation=.true.)
1398 END IF
1399 END DO
1400 DEALLOCATE (z_blk)
1401
1402 CALL timestop(handle)
1403
1404 END SUBROUTINE add_z_lp_columns
1405
1406! **************************************************************************************************
1407!> \brief Computes m_l^A = OR_{μ∈A}[Φ_μ(r_l) /= 0]. The product
1408!>
1409!> Φ_μ(r_l) Φ_ν(r_l), μ∈A, ν∈B,
1410!>
1411!> is evaluated only where m_l^A AND m_l^B is true. This avoids products and contractions
1412!> whenever either atom has no nonzero AO on r_l; no additional numerical threshold is used.
1413!> \param bs_env ...
1414!> \param phi_val ...
1415!> \param ao_col_map ...
1416!> \param nonzero_ao ...
1417! **************************************************************************************************
1418 SUBROUTINE compute_nonzero_ao_grid_mask(bs_env, phi_val, ao_col_map, nonzero_ao)
1419 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1420 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: phi_val
1421 INTEGER, DIMENSION(:), INTENT(IN) :: ao_col_map
1422 LOGICAL, ALLOCATABLE, DIMENSION(:, :), INTENT(OUT) :: nonzero_ao
1423
1424 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_nonzero_AO_grid_mask'
1425
1426 INTEGER :: col, first, handle, iatom, last, natom, &
1427 ngrid, point
1428
1429 CALL timeset(routinen, handle)
1430
1431 ngrid = SIZE(phi_val, 1)
1432 natom = bs_env%n_atom
1433 ALLOCATE (nonzero_ao(ngrid, natom), source=.false.)
1434 !$OMP PARALLEL DO DEFAULT(NONE) SCHEDULE(STATIC) &
1435 !$OMP SHARED(bs_env, phi_val, ao_col_map, nonzero_ao, ngrid, natom) &
1436 !$OMP PRIVATE(iatom, first, last, col, point)
1437 DO iatom = 1, natom
1438 first = ao_col_map(bs_env%i_ao_start_from_atom(iatom))
1439 IF (first == 0) cycle
1440 last = ao_col_map(bs_env%i_ao_end_from_atom(iatom))
1441 DO col = first, last
1442 DO point = 1, ngrid
1443 nonzero_ao(point, iatom) = &
1444 nonzero_ao(point, iatom) .OR. phi_val(point, col) /= 0.0_dp
1445 END DO
1446 END DO
1447 END DO
1448 !$OMP END PARALLEL DO
1449
1450 CALL timestop(handle)
1451 END SUBROUTINE compute_nonzero_ao_grid_mask
1452
1453! **************************************************************************************************
1454!> \brief Computes d_lp = Σ_{μνP} Φ_μ(r_l)Φ_ν(r_l)(μν|P)U_Pp for all optimized
1455!> columns p that contain reference RI functions P on atom I.
1456!> \param bs_env ...
1457!> \param ctx ...
1458!> \param phi_val ...
1459!> \param ao_col_map ...
1460!> \param d_lp ...
1461!> \param n_grid ...
1462!> \param iatom ...
1463!> \param U_Pp ...
1464!> \param max_ao_size ...
1465! **************************************************************************************************
1466 SUBROUTINE compute_d_lp_auto_ri_atom(bs_env, ctx, phi_val, ao_col_map, d_lp, n_grid, iatom, &
1467 U_Pp, max_ao_size)
1468!$ USE OMP_LIB, ONLY: omp_get_max_threads, omp_get_thread_num
1469 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1470 TYPE(gw_3c_ctx_type), INTENT(IN) :: ctx
1471 REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
1472 INTENT(IN) :: phi_val
1473 INTEGER, DIMENSION(:), INTENT(IN) :: ao_col_map
1474 REAL(kind=dp), DIMENSION(:, :), INTENT(INOUT) :: d_lp
1475 INTEGER, INTENT(IN) :: n_grid, iatom
1476 REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
1477 INTENT(IN) :: u_pp
1478 INTEGER, INTENT(IN) :: max_ao_size
1479
1480 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_d_lp_auto_ri_atom'
1481 INTEGER, PARAMETER :: grid_chunk = 1024
1482
1483 INTEGER :: jatom, katom, c, handle, i_thread, j, jk_idx, jsize, jstart, k, ksize, &
1484 kstart, l, l0, ncol, nri_ref, nthreads, ri, thread_id
1485 LOGICAL :: screened
1486 REAL(kind=dp) :: pair_factor
1487 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: int_2d_prv, rho_chunk
1488 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :) :: d_lp_threads, int_3c_prv
1489 TYPE(gw_3c_ws_type) :: ws
1490
1491 LOGICAL, ALLOCATABLE, DIMENSION(:, :) :: nonzero_ao
1492 INTEGER, ALLOCATABLE, DIMENSION(:) :: grid_index
1493 INTEGER :: n_grid_pair, grid_l, point
1494 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: grid_result
1495
1496 CALL timeset(routinen, handle)
1497 ncol = SIZE(u_pp, 2)
1498 nri_ref = get_ref_ri_size(bs_env, iatom)
1499 nthreads = 1
1500!$ nthreads = omp_get_max_threads()
1501 cpassert(SIZE(d_lp, 1) == n_grid)
1502 cpassert(SIZE(d_lp, 2) == ncol)
1503 cpassert(SIZE(u_pp, 1) == nri_ref)
1504 ALLOCATE (d_lp_threads(n_grid, ncol, nthreads), source=0.0_dp)
1505
1506 CALL compute_nonzero_ao_grid_mask(bs_env, phi_val, ao_col_map, nonzero_ao)
1507
1508 !$OMP PARALLEL DEFAULT(NONE) &
1509 !$OMP SHARED(nonzero_ao, bs_env, ctx, phi_val, ao_col_map, d_lp, n_grid, iatom, &
1510 !$OMP U_Pp, &
1511 !$OMP max_ao_size, ncol, nRI_ref, d_lp_threads, nthreads) &
1512 !$OMP PRIVATE(grid_index, n_grid_pair, grid_l, point, grid_result, &
1513 !$OMP jatom, katom, c, &
1514 !$OMP i_thread, j, jk_idx, jsize, jstart, k, ksize, &
1515 !$OMP kstart, l, l0, ri, screened, pair_factor, int_2d_prv, rho_chunk, &
1516 !$OMP int_3c_prv, ws, thread_id)
1517
1518 thread_id = 1
1519!$ thread_id = omp_get_thread_num() + 1
1520
1521 CALL gw_3c_ws_create(ws, ctx)
1522 ALLOCATE (int_3c_prv(max_ao_size, max_ao_size, ncol))
1523 ALLOCATE (int_2d_prv(max_ao_size*max_ao_size, ncol))
1524 ALLOCATE (rho_chunk(grid_chunk, max_ao_size*max_ao_size))
1525 ALLOCATE (grid_index(n_grid), grid_result(grid_chunk, ncol))
1526
1527 !$OMP DO SCHEDULE(DYNAMIC)
1528 DO jatom = 1, bs_env%n_atom
1529 DO katom = jatom, bs_env%n_atom
1530 jstart = ao_col_map(bs_env%i_ao_start_from_atom(jatom))
1531 kstart = ao_col_map(bs_env%i_ao_start_from_atom(katom))
1532 IF (jstart == 0 .OR. kstart == 0) cycle
1533 jsize = bs_env%i_ao_end_from_atom(jatom) - bs_env%i_ao_start_from_atom(jatom) + 1
1534 ksize = bs_env%i_ao_end_from_atom(katom) - bs_env%i_ao_start_from_atom(katom) + 1
1535 n_grid_pair = 0
1536 DO grid_l = 1, n_grid
1537 IF (.NOT. (nonzero_ao(grid_l, jatom) .AND. nonzero_ao(grid_l, katom))) cycle
1538 n_grid_pair = n_grid_pair + 1
1539 grid_index(n_grid_pair) = grid_l
1540 END DO
1541 IF (n_grid_pair == 0) cycle
1542 int_3c_prv(1:jsize, 1:ksize, 1:ncol) = 0.0_dp
1543 CALL build_3c_integral_block_auto_ri_ctx( &
1544 int_3c_prv(1:jsize, 1:ksize, 1:ncol), ctx, ws, &
1545 atom_j=jatom, atom_k=katom, atom_i=iatom, &
1546 transform=u_pp, transform_row=1, screened=screened)
1547 IF (screened) cycle
1548 DO ri = 1, ncol
1549 DO k = 1, ksize
1550 DO j = 1, jsize
1551 jk_idx = (k - 1)*jsize + j
1552 int_2d_prv(jk_idx, ri) = int_3c_prv(j, k, ri)
1553 END DO
1554 END DO
1555 END DO
1556 pair_factor = 1.0_dp
1557 IF (jatom /= katom) pair_factor = 2.0_dp
1558 DO l0 = 1, n_grid_pair, grid_chunk
1559 c = min(grid_chunk, n_grid_pair - l0 + 1)
1560 DO k = 1, ksize
1561 DO j = 1, jsize
1562 jk_idx = (k - 1)*jsize + j
1563 DO l = 1, c
1564 point = grid_index(l0 + l - 1)
1565 rho_chunk(l, jk_idx) = phi_val(point, jstart + j - 1)* &
1566 phi_val(point, kstart + k - 1)
1567 END DO
1568 END DO
1569 END DO
1570 CALL dgemm('N', 'N', c, ncol, jsize*ksize, pair_factor, rho_chunk, grid_chunk, &
1571 int_2d_prv, max_ao_size*max_ao_size, 0.0_dp, &
1572 grid_result, grid_chunk)
1573 DO ri = 1, ncol
1574 DO l = 1, c
1575 point = grid_index(l0 + l - 1)
1576 d_lp_threads(point, ri, thread_id) = &
1577 d_lp_threads(point, ri, thread_id) + grid_result(l, ri)
1578 END DO
1579 END DO
1580 END DO
1581 END DO
1582 END DO
1583 !$OMP END DO
1584
1585 !$OMP DO COLLAPSE(2) SCHEDULE(STATIC)
1586 DO ri = 1, ncol
1587 DO l = 1, n_grid
1588 DO i_thread = 1, nthreads
1589 d_lp(l, ri) = d_lp(l, ri) + d_lp_threads(l, ri, i_thread)
1590 END DO
1591 END DO
1592 END DO
1593 !$OMP END DO
1594 DEALLOCATE (int_3c_prv, int_2d_prv, rho_chunk)
1595 DEALLOCATE (grid_index, grid_result)
1596 CALL gw_3c_ws_release(ws)
1597 !$OMP END PARALLEL
1598
1599 DEALLOCATE (d_lp_threads)
1600
1601 DEALLOCATE (nonzero_ao)
1602
1603 CALL timestop(handle)
1604
1605 END SUBROUTINE compute_d_lp_auto_ri_atom
1606
1607! **************************************************************************************************
1608!> \brief Computes d_lp = Σ_{μνP} Φ_μ(r_l)Φ_ν(r_l)(μν|P)U_Pp for all AA and AB
1609!> columns p of one RI-RS matrix block. P can belong to either atom of an AB contraction.
1610!> \param bs_env ...
1611!> \param ctx ...
1612!> \param phi_val ...
1613!> \param ao_col_map ...
1614!> \param d_lp ...
1615!> \param n_grid ...
1616!> \param ri_coefficients ...
1617!> \param active_atom ...
1618!> \param max_ao_size ...
1619!> \param atom_j_mepos ...
1620!> \param atom_j_stride ...
1621! **************************************************************************************************
1622 SUBROUTINE compute_d_lp_auto_ri_atoms(bs_env, ctx, phi_val, ao_col_map, d_lp, n_grid, &
1623 ri_coefficients, active_atom, max_ao_size, atom_j_mepos, &
1624 atom_j_stride)
1625!$ USE OMP_LIB, ONLY: omp_get_max_threads, omp_get_thread_num
1626 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1627 TYPE(gw_3c_ctx_type), INTENT(IN) :: ctx
1628 REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
1629 INTENT(IN) :: phi_val
1630 INTEGER, DIMENSION(:), INTENT(IN) :: ao_col_map
1631 REAL(kind=dp), DIMENSION(:, :), INTENT(INOUT) :: d_lp
1632 INTEGER, INTENT(IN) :: n_grid
1633 REAL(kind=dp), DIMENSION(:, :, :), INTENT(IN) :: ri_coefficients
1634 LOGICAL, DIMENSION(:), INTENT(IN) :: active_atom
1635 INTEGER, INTENT(IN) :: max_ao_size, atom_j_mepos, atom_j_stride
1636
1637 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_d_lp_auto_ri_atoms'
1638 INTEGER, PARAMETER :: grid_chunk = 1024
1639
1640 INTEGER :: active_column, iatom, jatom, katom, c, handle, i_thread, j, jk_idx, jsize, &
1641 jstart, k, ksize, kstart, l, l0, &
1642 max_active, nactive, ncol, nri_ref, nthreads, ri, thread_id
1643 INTEGER, ALLOCATABLE, DIMENSION(:) :: atom_ncol
1644 INTEGER, ALLOCATABLE, DIMENSION(:, :) :: atom_column
1645 LOGICAL :: any_integral, screened
1646 REAL(kind=dp) :: pair_factor
1647 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: int_2d_prv, rho_chunk
1648 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :) :: coefficient_compact, d_lp_threads, &
1649 int_3c_prv, int_3c_atom
1650 TYPE(gw_3c_ws_type) :: ws
1651
1652 LOGICAL, ALLOCATABLE, DIMENSION(:, :) :: nonzero_ao
1653 INTEGER, ALLOCATABLE, DIMENSION(:) :: grid_index
1654 INTEGER :: n_grid_pair, grid_l, point
1655 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: grid_result
1656
1657 CALL timeset(routinen, handle)
1658 ncol = SIZE(d_lp, 2)
1659 nthreads = 1
1660!$ nthreads = omp_get_max_threads()
1661 cpassert(SIZE(d_lp, 1) == n_grid)
1662 cpassert(SIZE(ri_coefficients, 2) == ncol)
1663 cpassert(SIZE(ri_coefficients, 3) == SIZE(active_atom))
1664 ALLOCATE (atom_ncol(SIZE(active_atom)), atom_column(ncol, SIZE(active_atom)))
1665 atom_ncol = 0
1666 atom_column = 0
1667 DO iatom = 1, SIZE(active_atom)
1668 IF (.NOT. active_atom(iatom)) cycle
1669 nri_ref = get_ref_ri_size(bs_env, iatom)
1670 DO ri = 1, ncol
1671 IF (.NOT. any(ri_coefficients(1:nri_ref, ri, iatom) /= 0.0_dp)) cycle
1672 atom_ncol(iatom) = atom_ncol(iatom) + 1
1673 atom_column(atom_ncol(iatom), iatom) = ri
1674 END DO
1675 END DO
1676 max_active = maxval(atom_ncol)
1677 cpassert(max_active > 0)
1678 ALLOCATE (coefficient_compact(SIZE(ri_coefficients, 1), max_active, &
1679 SIZE(active_atom)), source=0.0_dp)
1680 DO iatom = 1, SIZE(active_atom)
1681 nri_ref = get_ref_ri_size(bs_env, iatom)
1682 DO active_column = 1, atom_ncol(iatom)
1683 ri = atom_column(active_column, iatom)
1684 coefficient_compact(1:nri_ref, active_column, iatom) = &
1685 ri_coefficients(1:nri_ref, ri, iatom)
1686 END DO
1687 END DO
1688 ALLOCATE (d_lp_threads(n_grid, ncol, nthreads), source=0.0_dp)
1689
1690 CALL compute_nonzero_ao_grid_mask(bs_env, phi_val, ao_col_map, nonzero_ao)
1691
1692 !$OMP PARALLEL DEFAULT(NONE) &
1693 !$OMP SHARED(nonzero_ao, bs_env, ctx, phi_val, ao_col_map, d_lp, n_grid, ri_coefficients, &
1694 !$OMP active_atom, max_ao_size, atom_j_mepos, atom_j_stride, ncol, &
1695 !$OMP d_lp_threads, nthreads, atom_ncol, &
1696 !$OMP atom_column, coefficient_compact, max_active) &
1697 !$OMP PRIVATE(grid_index, n_grid_pair, grid_l, point, grid_result, &
1698 !$OMP active_column, iatom, jatom, katom, c, i_thread, j, jk_idx, jsize, jstart, &
1699 !$OMP k, ksize, kstart, l, l0, &
1700 !$OMP nactive, nRI_ref, ri, any_integral, screened, pair_factor, &
1701 !$OMP int_2d_prv, rho_chunk, &
1702 !$OMP int_3c_prv, int_3c_atom, ws, thread_id)
1703
1704 thread_id = 1
1705!$ thread_id = omp_get_thread_num() + 1
1706
1707 CALL gw_3c_ws_create(ws, ctx)
1708 ALLOCATE (int_3c_prv(max_ao_size, max_ao_size, ncol))
1709 ALLOCATE (int_3c_atom(max_ao_size, max_ao_size, max_active))
1710 ALLOCATE (int_2d_prv(max_ao_size*max_ao_size, ncol))
1711 ALLOCATE (rho_chunk(grid_chunk, max_ao_size*max_ao_size))
1712 ALLOCATE (grid_index(n_grid), grid_result(grid_chunk, ncol))
1713
1714 !$OMP DO SCHEDULE(DYNAMIC)
1715 DO jatom = atom_j_mepos + 1, bs_env%n_atom, atom_j_stride
1716 DO katom = jatom, bs_env%n_atom
1717 jstart = ao_col_map(bs_env%i_ao_start_from_atom(jatom))
1718 kstart = ao_col_map(bs_env%i_ao_start_from_atom(katom))
1719 IF (jstart == 0 .OR. kstart == 0) cycle
1720 jsize = bs_env%i_ao_end_from_atom(jatom) - bs_env%i_ao_start_from_atom(jatom) + 1
1721 ksize = bs_env%i_ao_end_from_atom(katom) - bs_env%i_ao_start_from_atom(katom) + 1
1722 n_grid_pair = 0
1723 DO grid_l = 1, n_grid
1724 IF (.NOT. (nonzero_ao(grid_l, jatom) .AND. nonzero_ao(grid_l, katom))) cycle
1725 n_grid_pair = n_grid_pair + 1
1726 grid_index(n_grid_pair) = grid_l
1727 END DO
1728 IF (n_grid_pair == 0) cycle
1729 int_3c_prv(1:jsize, 1:ksize, 1:ncol) = 0.0_dp
1730 any_integral = .false.
1731 DO iatom = 1, SIZE(active_atom)
1732 IF (.NOT. active_atom(iatom)) cycle
1733 nri_ref = get_ref_ri_size(bs_env, iatom)
1734 nactive = atom_ncol(iatom)
1735 int_3c_atom(1:jsize, 1:ksize, 1:nactive) = 0.0_dp
1736 CALL build_3c_integral_block_auto_ri_ctx( &
1737 int_3c_atom(1:jsize, 1:ksize, 1:nactive), ctx, ws, &
1738 atom_j=jatom, atom_k=katom, atom_i=iatom, &
1739 transform=coefficient_compact(1:nri_ref, 1:nactive, iatom), &
1740 transform_row=1, screened=screened)
1741 IF (.NOT. screened) THEN
1742 any_integral = .true.
1743 DO active_column = 1, nactive
1744 ri = atom_column(active_column, iatom)
1745 int_3c_prv(1:jsize, 1:ksize, ri) = &
1746 int_3c_prv(1:jsize, 1:ksize, ri) + &
1747 int_3c_atom(1:jsize, 1:ksize, active_column)
1748 END DO
1749 END IF
1750 END DO
1751 IF (.NOT. any_integral) cycle
1752
1753 DO ri = 1, ncol
1754 DO k = 1, ksize
1755 DO j = 1, jsize
1756 jk_idx = (k - 1)*jsize + j
1757 int_2d_prv(jk_idx, ri) = int_3c_prv(j, k, ri)
1758 END DO
1759 END DO
1760 END DO
1761
1762 pair_factor = 1.0_dp
1763 IF (jatom /= katom) pair_factor = 2.0_dp
1764 DO l0 = 1, n_grid_pair, grid_chunk
1765 c = min(grid_chunk, n_grid_pair - l0 + 1)
1766 DO k = 1, ksize
1767 DO j = 1, jsize
1768 jk_idx = (k - 1)*jsize + j
1769 DO l = 1, c
1770 point = grid_index(l0 + l - 1)
1771 rho_chunk(l, jk_idx) = phi_val(point, jstart + j - 1)* &
1772 phi_val(point, kstart + k - 1)
1773 END DO
1774 END DO
1775 END DO
1776 CALL dgemm('N', 'N', c, ncol, jsize*ksize, pair_factor, rho_chunk, grid_chunk, &
1777 int_2d_prv, max_ao_size*max_ao_size, 0.0_dp, &
1778 grid_result, grid_chunk)
1779 DO ri = 1, ncol
1780 DO l = 1, c
1781 point = grid_index(l0 + l - 1)
1782 d_lp_threads(point, ri, thread_id) = &
1783 d_lp_threads(point, ri, thread_id) + grid_result(l, ri)
1784 END DO
1785 END DO
1786 END DO
1787 END DO
1788 END DO
1789 !$OMP END DO
1790
1791 !$OMP DO COLLAPSE(2) SCHEDULE(STATIC)
1792 DO ri = 1, ncol
1793 DO l = 1, n_grid
1794 DO i_thread = 1, nthreads
1795 d_lp(l, ri) = d_lp(l, ri) + d_lp_threads(l, ri, i_thread)
1796 END DO
1797 END DO
1798 END DO
1799 !$OMP END DO
1800 DEALLOCATE (int_3c_prv, int_3c_atom, int_2d_prv, rho_chunk)
1801 DEALLOCATE (grid_index, grid_result)
1802 CALL gw_3c_ws_release(ws)
1803 !$OMP END PARALLEL
1804
1805 DEALLOCATE (coefficient_compact, d_lp_threads, atom_column, atom_ncol)
1806
1807 DEALLOCATE (nonzero_ao)
1808
1809 CALL timestop(handle)
1810
1811 END SUBROUTINE compute_d_lp_auto_ri_atoms
1812
1813! **************************************************************************************************
1814!> \brief Computes d_lp = Σ_A Σ_{P∈A} d_lP U_Pp in rank-sized batches on a common grid.
1815!> \param bs_env ...
1816!> \param ctx ...
1817!> \param phi_val ...
1818!> \param ao_col_map ...
1819!> \param d_lp ...
1820!> \param n_grid ...
1821!> \param max_ao_size ...
1822!> \param mepos ...
1823!> \param num_pe ...
1824! **************************************************************************************************
1825 SUBROUTINE compute_d_lp_auto_ri_batch(bs_env, ctx, phi_val, ao_col_map, d_lp, n_grid, &
1826 max_ao_size, mepos, num_pe)
1827 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1828 TYPE(gw_3c_ctx_type), INTENT(IN) :: ctx
1829 REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
1830 INTENT(IN) :: phi_val
1831 INTEGER, DIMENSION(:), INTENT(IN) :: ao_col_map
1832 REAL(kind=dp), DIMENSION(:, :), INTENT(INOUT) :: d_lp
1833 INTEGER, INTENT(IN) :: n_grid, max_ao_size, mepos, num_pe
1834
1835 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_d_lp_auto_ri_batch'
1836
1837 INTEGER :: column, handle, iatom, n_done, n_total, &
1838 ncol_batch
1839 INTEGER, ALLOCATABLE, DIMENSION(:) :: global_map
1840 REAL(kind=dp) :: item_start_time
1841 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: d_lp_batch, u_pp
1842
1843 CALL timeset(routinen, handle)
1844
1845 n_total = 0
1846 DO iatom = mepos + 1, bs_env%n_atom, num_pe
1847 n_total = n_total + 1
1848 END DO
1849 n_done = 0
1850
1851 DO iatom = mepos + 1, bs_env%n_atom, num_pe
1852 item_start_time = m_walltime()
1853 CALL collect_auto_ri_columns_for_atom(bs_env, iatom, u_pp, global_map)
1854 ncol_batch = SIZE(global_map)
1855 IF (ncol_batch == 0) THEN
1856 DEALLOCATE (u_pp, global_map)
1857 n_done = n_done + 1
1858 CALL print_z_lp_progress(bs_env, n_done, n_total, &
1859 m_walltime() - item_start_time)
1860 cycle
1861 END IF
1862
1863 ALLOCATE (d_lp_batch(n_grid, ncol_batch), source=0.0_dp)
1864 CALL compute_d_lp_auto_ri_atom(bs_env, ctx, phi_val, ao_col_map, d_lp_batch, &
1865 n_grid, iatom, u_pp, max_ao_size)
1866 DO column = 1, ncol_batch
1867 d_lp(:, global_map(column)) = d_lp(:, global_map(column)) + d_lp_batch(:, column)
1868 END DO
1869 DEALLOCATE (u_pp, global_map, d_lp_batch)
1870 n_done = n_done + 1
1871 CALL print_z_lp_progress(bs_env, n_done, n_total, &
1872 m_walltime() - item_start_time)
1873 END DO
1874 CALL timestop(handle)
1875
1876 END SUBROUTINE compute_d_lp_auto_ri_batch
1877
1878! **************************************************************************************************
1879!> \brief Collects every optimized column p containing reference functions P on atom A. It returns
1880!>
1881!> U_Pp^A
1882!>
1883!> together with the global optimized-column index of q.
1884!> \param bs_env ...
1885!> \param iatom ...
1886!> \param U_Pp ...
1887!> \param global_map ...
1888! **************************************************************************************************
1889 SUBROUTINE collect_auto_ri_columns_for_atom(bs_env, iatom, U_Pp, global_map)
1890 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1891 INTEGER, INTENT(IN) :: iatom
1892 REAL(kind=dp), ALLOCATABLE, INTENT(OUT) :: u_pp(:, :)
1893 INTEGER, ALLOCATABLE, INTENT(OUT) :: global_map(:)
1894
1895 CHARACTER(LEN=*), PARAMETER :: routinen = 'collect_auto_ri_columns_for_atom'
1896
1897 INTEGER :: ab_block, atom_a, atom_b, column, global_column, handle, local_column, ncol, &
1898 ncol_batch, nri_ref, nri_ref_a, output_offset, row_first
1899 REAL(kind=dp), ALLOCATABLE :: u_pp_ab(:, :)
1900
1901 CALL timeset(routinen, handle)
1902
1903 ncol_batch = 0
1904 DO ab_block = 1, bs_env%auto_ri%AB_block_count
1905 atom_a = bs_env%auto_ri%AB_atom_A(ab_block)
1906 atom_b = bs_env%auto_ri%AB_atom_B(ab_block)
1907 IF (iatom == atom_a .OR. iatom == atom_b) THEN
1908 ncol_batch = ncol_batch + bs_env%auto_ri%AB_size_opt_RI(ab_block)
1909 END IF
1910 END DO
1911
1912 nri_ref = get_ref_ri_size(bs_env, iatom)
1913 ALLOCATE (u_pp(nri_ref, ncol_batch), source=0.0_dp)
1914 ALLOCATE (global_map(ncol_batch))
1915 column = 0
1916 DO ab_block = 1, bs_env%auto_ri%AB_block_count
1917 atom_a = bs_env%auto_ri%AB_atom_A(ab_block)
1918 atom_b = bs_env%auto_ri%AB_atom_B(ab_block)
1919 IF (iatom /= atom_a .AND. iatom /= atom_b) cycle
1920 ncol = bs_env%auto_ri%AB_size_opt_RI(ab_block)
1921 CALL get_u_pp_ab(bs_env%auto_ri, ab_block, u_pp_ab)
1922 row_first = 1
1923 IF (iatom == atom_b .AND. atom_b /= atom_a) THEN
1924 nri_ref_a = get_ref_ri_size(bs_env, atom_a)
1925 row_first = 1 + nri_ref_a
1926 END IF
1927 u_pp(:, column + 1:column + ncol) = &
1928 u_pp_ab( &
1929 row_first:row_first + nri_ref - 1, 1:ncol)
1930
1931 DO local_column = 1, ncol
1932 IF (local_column <= bs_env%auto_ri%AB_size_opt_RI_to_A(ab_block)) THEN
1933 output_offset = sum(bs_env%auto_ri%sizes_opt_RI(:atom_a - 1)) + &
1934 bs_env%auto_ri%AB_first_p_A(ab_block) - 1
1935 global_column = output_offset + local_column
1936 ELSE
1937 cpassert(atom_b /= atom_a)
1938 output_offset = sum(bs_env%auto_ri%sizes_opt_RI(:atom_b - 1)) + &
1939 bs_env%auto_ri%AB_first_p_B(ab_block) - 1
1940 global_column = output_offset + local_column - &
1941 bs_env%auto_ri%AB_size_opt_RI_to_A(ab_block)
1942 END IF
1943 global_map(column + local_column) = global_column
1944 END DO
1945 column = column + ncol
1946 DEALLOCATE (u_pp_ab)
1947 END DO
1948 cpassert(column == ncol_batch)
1949
1950 CALL timestop(handle)
1951
1952 END SUBROUTINE collect_auto_ri_columns_for_atom
1953
1954! **************************************************************************************************
1955!> \brief Unpacks one AB contraction matrix U_Pp; B=A denotes an AA block.
1956!> \param auto_ri ...
1957!> \param AB_block ...
1958!> \param U_Pp ...
1959! **************************************************************************************************
1960 SUBROUTINE get_u_pp_ab(auto_ri, AB_block, U_Pp)
1961 TYPE(auto_ri_type), INTENT(IN) :: auto_ri
1962 INTEGER, INTENT(IN) :: ab_block
1963 REAL(kind=dp), ALLOCATABLE, INTENT(OUT) :: u_pp(:, :)
1964
1965 INTEGER :: first, last, ncolumn, nrow
1966
1967 nrow = auto_ri%AB_size_ref_RI(ab_block)
1968 ncolumn = auto_ri%AB_size_opt_RI(ab_block)
1969 first = auto_ri%U_Pp_AB_offset(ab_block)
1970 last = first + nrow*ncolumn - 1
1971 ALLOCATE (u_pp(nrow, ncolumn))
1972 u_pp(:, :) = reshape(auto_ri%U_Pp_AB(first:last), [nrow, ncolumn])
1973
1974 END SUBROUTINE get_u_pp_ab
1975
1976! **************************************************************************************************
1977!> \brief Computes the fitting radius for every optimized atomic column block. If q assigned to A
1978!> contains reference functions on B, the required radius is
1979!>
1980!> R_A^fit = max_B [r_c + R_B^RI + |R_A - R_B|].
1981!> \param bs_env ...
1982!> \param radius ...
1983! **************************************************************************************************
1984 SUBROUTINE compute_auto_ri_grid_radii(bs_env, radius)
1985 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
1986 REAL(kind=dp), INTENT(OUT) :: radius(:)
1987
1988 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_auto_ri_grid_radii'
1989
1990 INTEGER :: a, ab_block, b, handle, n_to_a, ncol
1991 REAL(kind=dp) :: cutoff, distance
1992 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
1993
1994 CALL timeset(routinen, handle)
1995
1996 particle_set => bs_env%ri_rs%particle_set
1997 radius = bs_env%ri_rs%cutoff_radius_ri_rs
1998 IF (bs_env%ri_rs%cutoff_radius_ri_rs > 0.0_dp) THEN
1999 CALL timestop(handle)
2000 RETURN
2001 END IF
2002 radius = 0.0_dp
2003 cutoff = bs_env%ri_metric%cutoff_radius
2004 DO ab_block = 1, bs_env%auto_ri%AB_block_count
2005 a = bs_env%auto_ri%AB_atom_A(ab_block)
2006 b = bs_env%auto_ri%AB_atom_B(ab_block)
2007 n_to_a = bs_env%auto_ri%AB_size_opt_RI_to_A(ab_block)
2008 ncol = bs_env%auto_ri%AB_size_opt_RI(ab_block)
2009 IF (n_to_a > 0) THEN
2010 radius(a) = max(radius(a), cutoff + bs_env%ri_rs%radius_ri_per_atom(a))
2011 IF (b /= a) THEN
2012 distance = norm2(particle_set(a)%r - particle_set(b)%r)
2013 radius(a) = max(radius(a), cutoff + bs_env%ri_rs%radius_ri_per_atom(b) + distance)
2014 END IF
2015 END IF
2016 IF (n_to_a < ncol) THEN
2017 cpassert(b /= a)
2018 distance = norm2(particle_set(b)%r - particle_set(a)%r)
2019 radius(b) = max(radius(b), cutoff + bs_env%ri_rs%radius_ri_per_atom(b), &
2020 cutoff + bs_env%ri_rs%radius_ri_per_atom(a) + distance)
2021 END IF
2022 END DO
2023
2024 CALL timestop(handle)
2025 END SUBROUTINE compute_auto_ri_grid_radii
2026
2027! **************************************************************************************************
2028!> \brief Computes
2029!>
2030!> d_lp = Σ_A Σ_{P∈A} d_lP U_Pp^A,
2031!> d_lP = Σ_μν Φ_μ(r_l) Φ_ν(r_l) (μν|P),
2032!>
2033!> on the union of the atomic RI-RS grids needed by q. Each atom's three-center integrals are
2034!> evaluated once and transformed with U_Pp^A.
2035!> \param qs_env ...
2036!> \param bs_env ...
2037!> \param ctx ...
2038!> \param grid ...
2039!> \param mat_phi ...
2040!> \param mat_rhs ...
2041!> \param max_ao_size ...
2042! **************************************************************************************************
2043 SUBROUTINE compute_auto_ri_d_lp(qs_env, bs_env, ctx, grid, mat_phi, mat_rhs, max_ao_size)
2044 TYPE(qs_environment_type), POINTER :: qs_env
2045 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2046 TYPE(gw_3c_ctx_type), INTENT(IN) :: ctx
2047 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: grid
2048 TYPE(dbcsr_type), INTENT(IN) :: mat_phi
2049 TYPE(dbcsr_type), INTENT(OUT) :: mat_rhs
2050 INTEGER, INTENT(IN) :: max_ao_size
2051
2052 CHARACTER(LEN=*), PARAMETER :: routinen = 'compute_auto_ri_d_lp'
2053
2054 INTEGER :: ab_block, atom_a, atom_b, first, fit_atom, handle, handle_project, handle_rhs, l, &
2055 last, n_done, n_first_p_abs, n_to_a, n_total, n_union, nao, natom, ncol, ngrid, npcol, &
2056 nprow, ri_atom
2057 INTEGER, ALLOCATABLE, DIMENSION(:) :: ao_map, global_map, local_index, &
2058 row_offset, union_index
2059 INTEGER, DIMENSION(:), POINTER :: ab_row_dist, col_dist, &
2060 first_p_abs_per_atom, retained_size, &
2061 row_dist, row_size
2062 LOGICAL, ALLOCATABLE, DIMENSION(:) :: needed_fit_atom, union_mask
2063 REAL(kind=dp) :: item_start_time, radius
2064 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: fit_radius
2065 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: column_map, phi, rhs, u_pp, union_grid
2066 TYPE(cell_type), POINTER :: cell
2067 TYPE(dbcsr_distribution_type) :: dist_ab, dist_phi, dist_t
2068 TYPE(dbcsr_type) :: ab_d_lp, transform
2069 TYPE(mp_para_env_type), POINTER :: para_env
2070 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
2071 TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kind_set
2072
2073 CALL timeset(routinen, handle)
2074 CALL get_qs_env(qs_env, para_env=para_env, particle_set=particle_set, &
2075 qs_kind_set=qs_kind_set, cell=cell)
2076 natom = bs_env%n_atom
2077 CALL dbcsr_get_info(mat_phi, row_blk_size=row_size, distribution=dist_phi)
2078 CALL dbcsr_distribution_get(dist_phi, row_dist=row_dist, nprows=nprow, npcols=npcol)
2079 ALLOCATE (first_p_abs_per_atom(natom), col_dist(natom), ab_row_dist(natom), &
2080 retained_size(natom), row_offset(SIZE(row_size)))
2081 retained_size(:) = bs_env%auto_ri%sizes_opt_RI
2082 DO ri_atom = 1, natom
2083 first_p_abs_per_atom(ri_atom) = 0
2084 DO ab_block = 1, bs_env%auto_ri%AB_block_count
2085 IF (ri_atom == bs_env%auto_ri%AB_atom_A(ab_block) .OR. &
2086 ri_atom == bs_env%auto_ri%AB_atom_B(ab_block)) THEN
2087 first_p_abs_per_atom(ri_atom) = &
2088 first_p_abs_per_atom(ri_atom) + &
2089 bs_env%auto_ri%AB_size_opt_RI(ab_block)
2090 END IF
2091 END DO
2092 col_dist(ri_atom) = mod(ri_atom - 1, npcol)
2093 ab_row_dist(ri_atom) = mod(ri_atom - 1, nprow)
2094 END DO
2095 row_offset(1) = 0
2096 DO l = 2, SIZE(row_size)
2097 row_offset(l) = row_offset(l - 1) + row_size(l - 1)
2098 END DO
2099 CALL dbcsr_distribution_new(dist_ab, template=dist_phi, &
2100 row_dist=row_dist, col_dist=col_dist)
2101 CALL dbcsr_create(ab_d_lp, name='AUTO_RI AA/AB d_lp', dist=dist_ab, &
2102 matrix_type=dbcsr_type_no_symmetry, row_blk_size=row_size, &
2103 col_blk_size=first_p_abs_per_atom)
2104 CALL dbcsr_distribution_new(dist_t, template=dist_phi, &
2105 row_dist=ab_row_dist, col_dist=col_dist)
2106 CALL dbcsr_create(transform, name='AUTO_RI U_Pp', dist=dist_t, &
2107 matrix_type=dbcsr_type_no_symmetry, row_blk_size=first_p_abs_per_atom, &
2108 col_blk_size=retained_size)
2109 CALL dbcsr_create(mat_rhs, name='AUTO_RI optimized d_lp', dist=dist_ab, &
2110 matrix_type=dbcsr_type_no_symmetry, row_blk_size=row_size, &
2111 col_blk_size=retained_size)
2112 ALLOCATE (fit_radius(natom))
2113 CALL compute_auto_ri_grid_radii(bs_env, fit_radius)
2114 ALLOCATE (needed_fit_atom(natom), union_mask(SIZE(grid, 2)))
2115 n_total = 0
2116 DO ri_atom = para_env%mepos + 1, natom, para_env%num_pe
2117 n_total = n_total + 1
2118 END DO
2119 n_done = 0
2120 CALL timeset(routinen//'_AB_d_lp', handle_rhs)
2121 DO ri_atom = para_env%mepos + 1, natom, para_env%num_pe
2122 item_start_time = m_walltime()
2123 needed_fit_atom = .false.
2124 DO ab_block = 1, bs_env%auto_ri%AB_block_count
2125 atom_a = bs_env%auto_ri%AB_atom_A(ab_block)
2126 atom_b = bs_env%auto_ri%AB_atom_B(ab_block)
2127 IF (ri_atom /= atom_a .AND. ri_atom /= atom_b) cycle
2128 n_to_a = bs_env%auto_ri%AB_size_opt_RI_to_A(ab_block)
2129 IF (n_to_a > 0) needed_fit_atom(atom_a) = .true.
2130 IF (n_to_a < bs_env%auto_ri%AB_size_opt_RI(ab_block)) THEN
2131 needed_fit_atom(atom_b) = .true.
2132 END IF
2133 END DO
2134 IF (.NOT. any(needed_fit_atom)) THEN
2135 n_done = n_done + 1
2136 CALL print_z_lp_progress(bs_env, n_done, n_total, &
2137 m_walltime() - item_start_time)
2138 cycle
2139 END IF
2140 union_mask = .false.
2141 radius = 0.0_dp
2142 DO fit_atom = 1, natom
2143 IF (.NOT. needed_fit_atom(fit_atom)) cycle
2144 radius = max(radius, fit_radius(fit_atom) + &
2145 norm2(particle_set(fit_atom)%r - particle_set(ri_atom)%r))
2146 DO l = 1, SIZE(grid, 2)
2147 IF (norm2(grid(:, l) - particle_set(fit_atom)%r) <= &
2148 fit_radius(fit_atom)) union_mask(l) = .true.
2149 END DO
2150 END DO
2151 n_union = count(union_mask)
2152 ALLOCATE (union_index(n_union), union_grid(3, n_union))
2153 n_union = 0
2154 DO l = 1, SIZE(grid, 2)
2155 IF (.NOT. union_mask(l)) cycle
2156 n_union = n_union + 1
2157 union_index(n_union) = l
2158 union_grid(:, n_union) = grid(:, l)
2159 END DO
2160 CALL build_phi_on_sphere(bs_env, qs_kind_set, union_grid, ri_atom, &
2161 radius + 1.0_dp, bs_env%i_ao_end_from_atom(natom), local_index, &
2162 ngrid, phi, ao_map, nao)
2163 local_index(1:ngrid) = union_index(local_index(1:ngrid))
2164 n_first_p_abs = first_p_abs_per_atom(ri_atom)
2165 CALL collect_auto_ri_columns_for_atom(bs_env, ri_atom, u_pp, global_map)
2166 cpassert(SIZE(global_map) == n_first_p_abs)
2167 first = 1
2168 DO fit_atom = 1, natom
2169 ncol = retained_size(fit_atom)
2170 last = first + ncol - 1
2171 IF (any(global_map >= first .AND. global_map <= last)) THEN
2172 ALLOCATE (column_map(n_first_p_abs, ncol), source=0.0_dp)
2173 DO l = 1, n_first_p_abs
2174 IF (global_map(l) < first .OR. global_map(l) > last) cycle
2175 column_map(l, global_map(l) - first + 1) = 1.0_dp
2176 END DO
2177 CALL dbcsr_put_block(transform, ri_atom, fit_atom, column_map)
2178 DEALLOCATE (column_map)
2179 END IF
2180 first = last + 1
2181 END DO
2182 ALLOCATE (rhs(ngrid, n_first_p_abs), source=0.0_dp)
2183 CALL compute_d_lp_auto_ri_atom(bs_env, ctx, phi, ao_map, rhs, ngrid, &
2184 ri_atom, u_pp, max_ao_size)
2185 CALL store_z_lp_columns(ab_d_lp, rhs, local_index, ngrid, n_first_p_abs, ri_atom, &
2186 row_size, row_offset, 0.0_dp)
2187 DEALLOCATE (rhs, u_pp, global_map, phi, ao_map, local_index, &
2188 union_grid, union_index)
2189 n_done = n_done + 1
2190 CALL print_z_lp_progress(bs_env, n_done, n_total, &
2191 m_walltime() - item_start_time)
2192 END DO
2193 CALL dbcsr_finalize(transform)
2194 CALL dbcsr_finalize(ab_d_lp)
2195 CALL timestop(handle_rhs)
2196 CALL timeset(routinen//'_transform', handle_project)
2197 CALL dbcsr_multiply('N', 'N', 1.0_dp, ab_d_lp, transform, 0.0_dp, &
2198 mat_rhs, filter_eps=0.0_dp)
2199 CALL timestop(handle_project)
2200 CALL dbcsr_release(ab_d_lp)
2201 CALL dbcsr_release(transform)
2202 CALL dbcsr_distribution_release(dist_ab)
2203 CALL dbcsr_distribution_release(dist_t)
2204 DEALLOCATE (first_p_abs_per_atom, retained_size, col_dist, ab_row_dist, row_offset, &
2205 fit_radius, needed_fit_atom, union_mask)
2206 CALL timestop(handle)
2207 END SUBROUTINE compute_auto_ri_d_lp
2208
2209! **************************************************************************************************
2210!> \brief Extracts rhs(i,q) = d_{local_index(i),q} from one optimized atomic DBCSR column block.
2211!> \param mat_rhs ...
2212!> \param atom_index ...
2213!> \param local_index ...
2214!> \param row_offset ...
2215!> \param rhs ...
2216! **************************************************************************************************
2217 SUBROUTINE extract_atom_d_lp(mat_rhs, atom_index, local_index, row_offset, rhs)
2218 TYPE(dbcsr_type), INTENT(INOUT) :: mat_rhs
2219 INTEGER, INTENT(IN) :: atom_index
2220 INTEGER, DIMENSION(:), INTENT(IN) :: local_index, row_offset
2221 REAL(kind=dp), DIMENSION(:, :), INTENT(OUT) :: rhs
2222
2223 CHARACTER(LEN=*), PARAMETER :: routinen = 'extract_atom_d_lp'
2224
2225 INTEGER :: handle, l, next_row, row
2226 LOGICAL :: found
2227 REAL(kind=dp), DIMENSION(:, :), POINTER :: block
2228
2229 CALL timeset(routinen, handle)
2230
2231 rhs = 0.0_dp
2232 row = 0
2233 next_row = 1
2234 NULLIFY (block)
2235 DO l = 1, SIZE(rhs, 1)
2236 DO WHILE (next_row <= SIZE(row_offset))
2237 IF (row_offset(next_row) >= local_index(l)) EXIT
2238 row = next_row
2239 next_row = next_row + 1
2240 CALL dbcsr_get_block_p(mat_rhs, row, atom_index, block, found)
2241 IF (.NOT. found) NULLIFY (block)
2242 END DO
2243 IF (ASSOCIATED(block)) rhs(l, :) = block(local_index(l) - row_offset(row), :)
2244 END DO
2245
2246 CALL timestop(handle)
2247 END SUBROUTINE extract_atom_d_lp
2248
2249! **************************************************************************************************
2250!> \brief Solves Σ_l' D_ll' Z_l'q = d_lp for each optimized atomic column block. Small grids are
2251!> gathered within one process column and solved by a local Cholesky factorization.
2252!> \param qs_env ...
2253!> \param bs_env ...
2254!> \param grid ...
2255!> \param mat_rhs ...
2256!> \param mat_z ...
2257! **************************************************************************************************
2258 SUBROUTINE fit_auto_ri_z_lp(qs_env, bs_env, grid, mat_rhs, mat_z)
2259 TYPE(qs_environment_type), POINTER :: qs_env
2260 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2261 REAL(kind=dp), INTENT(IN) :: grid(:, :)
2262 TYPE(dbcsr_type), INTENT(INOUT) :: mat_rhs, mat_z
2263
2264 CHARACTER(LEN=*), PARAMETER :: routinen = 'fit_auto_ri_z_lp'
2265
2266 INTEGER :: atom_index, base, group_size, handle, &
2267 handle_gather, i, info, mypcol, n_big, &
2268 n_small, nao, natom, ncol, ngrid, &
2269 npcol, slot
2270 INTEGER, ALLOCATABLE :: all_index(:), ao_map(:), big_list(:), &
2271 grid_size(:), local_index(:), &
2272 row_offset(:), small_list(:)
2273 INTEGER, POINTER :: row_size(:)
2274 LOGICAL, ALLOCATABLE :: single_rank(:)
2275 REAL(kind=dp), ALLOCATABLE :: atom_rhs(:, :), buffer(:, :), &
2276 diagonal(:), gram(:, :), &
2277 local_rhs(:, :), phi(:, :), radius(:)
2278 TYPE(dbcsr_distribution_type) :: distribution
2279 TYPE(mp_para_env_type), POINTER :: column_env, para_env
2280 TYPE(qs_kind_type), POINTER :: qs_kinds(:)
2281
2282 CALL timeset(routinen, handle)
2283 CALL get_qs_env(qs_env, para_env=para_env, qs_kind_set=qs_kinds)
2284 CALL dbcsr_get_info(mat_rhs, distribution=distribution, row_blk_size=row_size)
2285 CALL dbcsr_distribution_get(distribution, mypcol=mypcol, npcols=npcol)
2286 ALLOCATE (column_env)
2287 CALL column_env%from_split(para_env, mypcol)
2288 natom = bs_env%n_atom
2289 ALLOCATE (radius(natom), all_index(SIZE(grid, 2)), row_offset(SIZE(row_size)))
2290 CALL compute_auto_ri_grid_radii(bs_env, radius)
2291 CALL classify_z_lp_atoms(bs_env, grid, radius, &
2292 bs_env%auto_ri%sizes_opt_RI, grid_size, &
2293 small_list, n_small, big_list, n_big, group_size)
2294 ALLOCATE (single_rank(natom), source=.false.)
2295 single_rank(small_list(:n_small)) = .true.
2296 DO i = 1, SIZE(all_index)
2297 all_index(i) = i
2298 END DO
2299 row_offset(1) = 0
2300 DO i = 2, SIZE(row_size)
2301 row_offset(i) = row_offset(i - 1) + row_size(i - 1)
2302 END DO
2303 DO base = mypcol + 1, natom, npcol*column_env%num_pe
2304 CALL timeset(routinen//'_gather_rhs', handle_gather)
2305 DO slot = 0, column_env%num_pe - 1
2306 atom_index = base + slot*npcol
2307 IF (atom_index > natom) EXIT
2308 IF (.NOT. single_rank(atom_index)) cycle
2309 ncol = bs_env%auto_ri%sizes_opt_RI(atom_index)
2310 IF (ncol == 0) cycle
2311 ALLOCATE (buffer(SIZE(grid, 2), ncol))
2312 CALL extract_atom_d_lp(mat_rhs, atom_index, all_index, row_offset, buffer)
2313 CALL column_env%sum(buffer, slot)
2314 IF (column_env%mepos == slot) THEN
2315 CALL move_alloc(buffer, atom_rhs)
2316 ELSE
2317 DEALLOCATE (buffer)
2318 END IF
2319 END DO
2320 CALL timestop(handle_gather)
2321 atom_index = base + column_env%mepos*npcol
2322 IF (atom_index > natom) cycle
2323 IF (.NOT. single_rank(atom_index)) cycle
2324 ncol = bs_env%auto_ri%sizes_opt_RI(atom_index)
2325 IF (ncol == 0) cycle
2326 CALL build_phi_on_sphere(bs_env, qs_kinds, grid, atom_index, &
2327 radius(atom_index), bs_env%i_ao_end_from_atom(natom), &
2328 local_index, ngrid, phi, ao_map, nao)
2329 ALLOCATE (local_rhs(ngrid, ncol), diagonal(ngrid))
2330 DO i = 1, ngrid
2331 local_rhs(i, :) = atom_rhs(local_index(i), :)
2332 END DO
2333 DEALLOCATE (atom_rhs)
2334 CALL build_gram_jacobi_blas(phi, ngrid, nao, bs_env%ri_rs%tikhonov, gram, diagonal)
2335 CALL scale_rows_by_diag(local_rhs, diagonal, ngrid, ncol)
2336 CALL dpotrf('L', ngrid, gram, ngrid, info)
2337 cpassert(info == 0)
2338 CALL dpotrs('L', ngrid, ncol, gram, ngrid, local_rhs, ngrid, info)
2339 cpassert(info == 0)
2340 CALL scale_rows_by_diag(local_rhs, diagonal, ngrid, ncol)
2341 CALL store_z_lp_columns(mat_z, local_rhs, local_index, ngrid, ncol, atom_index, &
2342 row_size, row_offset, 0.0_dp)
2343 DEALLOCATE (local_rhs, diagonal, gram, phi, ao_map, local_index)
2344 END DO
2345 IF (n_big > 0) THEN
2346 CALL fit_distributed_auto_ri_z_lp(qs_env, bs_env, grid, mat_rhs, mat_z, radius, &
2347 big_list(:n_big), group_size, row_size, row_offset, &
2348 all_index)
2349 END IF
2350 DEALLOCATE (radius, all_index, row_offset, single_rank, grid_size, small_list, big_list)
2351 CALL column_env%free()
2352 DEALLOCATE (column_env)
2353 CALL dbcsr_finalize(mat_z)
2354 CALL timestop(handle)
2355 END SUBROUTINE fit_auto_ri_z_lp
2356
2357! **************************************************************************************************
2358!> \brief Solves Σ_l' D_ll' Z_l'q = d_lp for large atomic grids with distributed Cholesky
2359!> factorization. Each d_lp block is gathered once and distributed over its assigned rank
2360!> group.
2361!> \param qs_env ...
2362!> \param bs_env ...
2363!> \param grid ...
2364!> \param mat_rhs ...
2365!> \param mat_z ...
2366!> \param radius ...
2367!> \param atom_list ...
2368!> \param group_size ...
2369!> \param row_size ...
2370!> \param row_offset ...
2371!> \param all_index ...
2372! **************************************************************************************************
2373 SUBROUTINE fit_distributed_auto_ri_z_lp(qs_env, bs_env, grid, mat_rhs, mat_z, radius, &
2374 atom_list, group_size, row_size, row_offset, &
2375 all_index)
2376 TYPE(qs_environment_type), POINTER :: qs_env
2377 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2378 REAL(kind=dp), INTENT(IN) :: grid(:, :)
2379 TYPE(dbcsr_type), INTENT(INOUT) :: mat_rhs, mat_z
2380 REAL(kind=dp), INTENT(IN) :: radius(:)
2381 INTEGER, INTENT(IN) :: atom_list(:), group_size, row_size(:), &
2382 row_offset(:), all_index(:)
2383
2384 CHARACTER(LEN=*), PARAMETER :: routinen = 'fit_distributed_auto_ri_z_lp'
2385
2386 INTEGER :: atom_index, base, handle, i, info, &
2387 my_group, nao, ncol, ngrid, ngroups, &
2388 root, slot
2389 INTEGER, ALLOCATABLE :: ao_map(:), local_index(:)
2390 REAL(kind=dp), ALLOCATABLE :: atom_rhs(:, :), buffer(:, :), &
2391 diagonal(:), local_rhs(:, :), phi(:, :)
2392 TYPE(cp_blacs_env_type), POINTER :: blacs_env
2393 TYPE(cp_fm_struct_type), POINTER :: gram_struct, rhs_struct
2394 TYPE(cp_fm_type) :: gram, rhs
2395 TYPE(mp_para_env_type), POINTER :: group_env, para_env
2396 TYPE(qs_kind_type), POINTER :: qs_kinds(:)
2397
2398 CALL timeset(routinen, handle)
2399 CALL get_qs_env(qs_env, para_env=para_env, qs_kind_set=qs_kinds)
2400 ngroups = max(1, para_env%num_pe/group_size)
2401 my_group = min(para_env%mepos/group_size, ngroups - 1)
2402 ALLOCATE (group_env)
2403 CALL group_env%from_split(para_env, my_group)
2404 NULLIFY (blacs_env, gram_struct, rhs_struct)
2405 CALL cp_blacs_env_create(blacs_env=blacs_env, para_env=group_env)
2406 DO base = 1, SIZE(atom_list), ngroups
2407 DO slot = 0, ngroups - 1
2408 IF (base + slot > SIZE(atom_list)) EXIT
2409 atom_index = atom_list(base + slot)
2410 ncol = bs_env%auto_ri%sizes_opt_RI(atom_index)
2411 IF (ncol == 0) cycle
2412 root = slot*group_size
2413 ALLOCATE (buffer(SIZE(grid, 2), ncol))
2414 CALL extract_atom_d_lp(mat_rhs, atom_index, all_index, row_offset, buffer)
2415 CALL para_env%sum(buffer, root)
2416 IF (para_env%mepos == root) THEN
2417 CALL move_alloc(buffer, atom_rhs)
2418 ELSE
2419 DEALLOCATE (buffer)
2420 END IF
2421 END DO
2422 IF (base + my_group > SIZE(atom_list)) cycle
2423 atom_index = atom_list(base + my_group)
2424 ncol = bs_env%auto_ri%sizes_opt_RI(atom_index)
2425 IF (ncol == 0) cycle
2426 IF (group_env%mepos /= 0) ALLOCATE (atom_rhs(SIZE(grid, 2), ncol))
2427 CALL group_env%bcast(atom_rhs, 0)
2428 CALL build_phi_on_sphere(bs_env, qs_kinds, grid, atom_index, &
2429 radius(atom_index), bs_env%i_ao_end_from_atom(bs_env%n_atom), &
2430 local_index, ngrid, phi, ao_map, nao)
2431 ALLOCATE (local_rhs(ngrid, ncol), diagonal(ngrid))
2432 DO i = 1, ngrid
2433 local_rhs(i, :) = atom_rhs(local_index(i), :)
2434 END DO
2435 DEALLOCATE (atom_rhs)
2436 CALL build_jacobi_diag_from_phi(phi, ngrid, nao, diagonal)
2437 CALL scale_rows_by_diag(local_rhs, diagonal, ngrid, ncol)
2438 CALL solve_d_lp_distributed(phi, diagonal, local_rhs, ngrid, nao, ncol, &
2439 bs_env%ri_rs%tikhonov, group_env, blacs_env, &
2440 gram_struct, rhs_struct, gram, rhs, info)
2441 cpassert(info == 0)
2442 CALL scale_rows_by_diag(local_rhs, diagonal, ngrid, ncol)
2443 IF (group_env%mepos == 0) THEN
2444 CALL store_z_lp_columns(mat_z, local_rhs, local_index, ngrid, ncol, atom_index, &
2445 row_size, row_offset, 0.0_dp)
2446 END IF
2447 DEALLOCATE (local_rhs, diagonal, phi, ao_map, local_index)
2448 END DO
2449 CALL cp_blacs_env_release(blacs_env)
2450 CALL group_env%free()
2451 DEALLOCATE (group_env)
2452 CALL timestop(handle)
2453 END SUBROUTINE fit_distributed_auto_ri_z_lp
2454
2455! **************************************************************************************************
2456!> \brief Splits the atoms of the Z_lP solve into a single-rank list ("small", Phase A: LAPACK
2457!> dpotrf/dpotrs on one rank) and a distributed list ("big", Phase B: ScaLAPACK
2458!> pdpotrf/pdpotrs over a rank subgroup of size G), and sizes G.
2459!> AUTO mode (N_PROCS_PER_ATOM_Z_LP <= 0, the default): estimate each atom's single-rank
2460!> peak memory
2461!> peak(P) = 8*n_local_grid(P)^2 (dense Gram matrix D_local)
2462!> + 8*n_local_grid(P)*n_ao_used(P) (phi_local)
2463!> + 8*n_local_grid(P)*n_RI(P)*(1+n_threads) (d_lp + OMP partials)
2464!> and send atoms whose peak exceeds mem_safety * available-memory-per-proc to the
2465!> distributed path; G is auto-sized so the biggest atom's distributed D_local (/G)
2466!> fits alongside the replicated phi_local + d_lp.
2467!> MANUAL mode (> 0): 1 forces the single-rank path for every atom; > 1 keeps the
2468!> memory-based classification but forces that fixed subgroup size G.
2469!> In every mode G is floored by the ScaLAPACK 32-bit index limit (a local block-cyclic
2470!> slice of ~n_local_grid^2/G elements must stay below 2^31 or pdpotrf segfaults).
2471!> \param bs_env ...
2472!> \param ri_rs_grid_points ...
2473!> \param cutoff_ri_per_atom ...
2474!> \param ri_blk_sizes per-atom ...
2475!> \param n_local_grid_atom ...
2476!> \param small_list ...
2477!> \param n_small ...
2478!> \param big_list ...
2479!> \param n_big ...
2480!> \param G ...
2481! **************************************************************************************************
2482 SUBROUTINE classify_z_lp_atoms(bs_env, ri_rs_grid_points, cutoff_ri_per_atom, ri_blk_sizes, &
2483 n_local_grid_atom, small_list, n_small, big_list, n_big, G)
2484
2485!$ USE OMP_LIB, ONLY: omp_get_max_threads
2486
2487 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2488 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: ri_rs_grid_points
2489
2490 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: cutoff_ri_per_atom
2491 INTEGER, DIMENSION(:), INTENT(IN) :: ri_blk_sizes
2492 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: n_local_grid_atom, small_list, big_list
2493 INTEGER, INTENT(OUT) :: n_small, n_big, g
2494 CHARACTER(LEN=*), PARAMETER :: routinen = 'classify_z_lp_atoms'
2495
2496 INTEGER :: handle
2497
2498 ! Conservative fraction of measured available memory usable per rank for the Z_lP
2499 REAL(kind=dp), PARAMETER :: mem_safety = 0.8_dp
2500
2501 ! ScaLAPACK/BLACS index the per-rank local block-cyclic slice (~n_local_grid^2/G
2502 ! elements) with 32-bit integers; keep it safely below 2^31 or pdpotrf segfaults.
2503 REAL(kind=dp), PARAMETER :: scalapack_loc_limit = 2.0e9_dp
2504
2505 INTEGER :: g_atom, g_int32, g_int32_max, l, &
2506 n_ao_used_atom, n_grid_total, &
2507 n_local_grid, natom, nthreads_cls, &
2508 p_loop_atom
2509 LOGICAL :: auto_mode
2510 REAL(kind=dp) :: budget_bytes, cutoff_ri, dlp_bytes, &
2511 mem_avail_gb, ng, nri, peak_bytes, &
2512 phi_bytes
2513 REAL(kind=dp), DIMENSION(3) :: pos_p
2514 TYPE(mp_para_env_type), POINTER :: para_env
2515 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
2516
2517 CALL timeset(routinen, handle)
2518
2519 para_env => bs_env%para_env
2520 particle_set => bs_env%ri_rs%particle_set
2521 natom = bs_env%n_atom
2522 n_grid_total = SIZE(ri_rs_grid_points, 2)
2523
2524 ! Per-atom sphere size: n_local_grid(P) = number of l with |r_l - R_P| <= cutoff_ri(P).
2525 ! It sets both the memory footprint (D_local is n_local_grid^2) and the solve
2526 ! cost (~n_local_grid^3), so it drives classification and the LPT load balancing.
2527 ALLOCATE (n_local_grid_atom(natom), small_list(natom), big_list(natom))
2528 DO p_loop_atom = 1, natom
2529 pos_p(:) = particle_set(p_loop_atom)%r(:)
2530 cutoff_ri = cutoff_ri_per_atom(p_loop_atom)
2531 n_local_grid = 0
2532 DO l = 1, n_grid_total
2533 IF (sum((ri_rs_grid_points(1:3, l) - pos_p(1:3))**2) <= cutoff_ri**2) THEN
2534 n_local_grid = n_local_grid + 1
2535 END IF
2536 END DO
2537 n_local_grid_atom(p_loop_atom) = n_local_grid
2538 END DO
2539
2540 nthreads_cls = 1
2541!$ nthreads_cls = omp_get_max_threads()
2542 ! N_PROCS_PER_ATOM_Z_LP: -1 (default) = AUTO (classify by memory, auto-size G);
2543 ! 1 = force single-rank BLAS for every atom; >1 = classify by memory but use this
2544 ! fixed subgroup size G for the big atoms.
2545 auto_mode = (bs_env%ri_rs%n_procs_per_atom_z_lp <= 0)
2546 CALL mp_mem_avail_per_rank_gb(bs_env%para_env, mem_avail_gb) ! collective over all ranks
2547 budget_bytes = mem_safety*mem_avail_gb*1.0e9_dp
2548
2549 n_small = 0
2550 n_big = 0
2551 g = 1
2552 g_atom = 1 ! max G a big atom needs (memory + ScaLAPACK int32 floor)
2553 g_int32_max = 1 ! max ScaLAPACK-int32 floor over the distributed atoms
2554 IF (bs_env%ri_rs%n_procs_per_atom_z_lp == 1) THEN
2555 ! Force single-rank BLAS for every atom.
2556 DO p_loop_atom = 1, natom
2557 n_small = n_small + 1
2558 small_list(n_small) = p_loop_atom
2559 END DO
2560 ELSE IF (mem_avail_gb <= 0.0_dp) THEN
2561 ! No /proc/meminfo => cannot size by memory.
2562 IF (auto_mode) THEN
2563 IF (bs_env%unit_nr > 0) THEN
2564 cpwarn("RI-RS Z_lP: no meminfo; single-rank solve for all atoms")
2565 END IF
2566 DO p_loop_atom = 1, natom
2567 n_small = n_small + 1
2568 small_list(n_small) = p_loop_atom
2569 END DO
2570 ELSE
2571 ! Fixed G, no meminfo: distribute all atoms; still floor G by the int32 limit.
2572 DO p_loop_atom = 1, natom
2573 ng = real(n_local_grid_atom(p_loop_atom), dp)
2574 g_int32_max = max(g_int32_max, ceiling(ng*ng/scalapack_loc_limit))
2575 n_big = n_big + 1
2576 big_list(n_big) = p_loop_atom
2577 END DO
2578 g = min(bs_env%ri_rs%n_procs_per_atom_z_lp, para_env%num_pe)
2579 IF (g < g_int32_max) THEN
2580 g = min(g_int32_max, para_env%num_pe)
2581 IF (bs_env%unit_nr > 0) THEN
2582 cpwarn("RI-RS Z_lP: raised G to avoid ScaLAPACK overflow")
2583 END IF
2584 END IF
2585 END IF
2586 ELSE
2587 ! Classify by memory: peak (D_local + phi_local + d_lp) vs budget. Small -> BLAS,
2588 ! big -> distributed. Same classification for AUTO and fixed-G modes.
2589 DO p_loop_atom = 1, natom
2590 ng = real(n_local_grid_atom(p_loop_atom), dp)
2591 nri = real(ri_blk_sizes(p_loop_atom), dp)
2592 CALL get_n_ao_in_sphere(bs_env, p_loop_atom, &
2593 cutoff_ri_per_atom(p_loop_atom), n_ao_used_atom)
2594 phi_bytes = 8.0_dp*ng*real(n_ao_used_atom, dp)
2595 dlp_bytes = 8.0_dp*ng*nri*real(1 + nthreads_cls, dp)
2596 peak_bytes = 8.0_dp*ng*ng + phi_bytes + dlp_bytes
2597 IF (peak_bytes <= budget_bytes) THEN
2598 n_small = n_small + 1
2599 small_list(n_small) = p_loop_atom
2600 ELSE
2601 n_big = n_big + 1
2602 big_list(n_big) = p_loop_atom
2603 ! G must satisfy BOTH: (a) memory — distributed D_local (/G) fits next to the
2604 ! replicated phi_local + d_lp; (b) ScaLAPACK — local ~ng^2/G below the int32 limit.
2605 g_int32 = ceiling(ng*ng/scalapack_loc_limit)
2606 g_int32_max = max(g_int32_max, g_int32)
2607 g_atom = max(g_atom, g_int32, &
2608 ceiling(8.0_dp*ng*ng/max(budget_bytes - phi_bytes - dlp_bytes, 1.0_dp)))
2609 END IF
2610 END DO
2611 IF (n_big > 0) THEN
2612 IF (auto_mode) THEN
2613 ! Auto-size G from the most demanding big atom.
2614 IF (g_atom > para_env%num_pe) THEN
2615 CALL cp_abort(__location__, &
2616 "RI-RS Z_lP: an atom is too large to fit even when "// &
2617 "distributed over all ranks. Add nodes, use fewer MPI ranks "// &
2618 "per node, lower CUTOFF_RADIUS_RL_RI, or raise EPS_FILTER "// &
2619 "for more grid screening.")
2620 END IF
2621 g = min(max(g_atom, 2), para_env%num_pe)
2622 ELSE
2623 ! Fixed G from the keyword. Hard-floor by the ScaLAPACK int32 limit (below it
2624 ! pdpotrf segfaults); warn if it is still below the memory recommendation.
2625 g = min(bs_env%ri_rs%n_procs_per_atom_z_lp, para_env%num_pe)
2626 IF (g < g_int32_max) THEN
2627 g = min(g_int32_max, para_env%num_pe)
2628 IF (bs_env%unit_nr > 0) THEN
2629 cpwarn("RI-RS Z_lP: raised G to avoid ScaLAPACK overflow")
2630 END IF
2631 ELSE IF (g < g_atom .AND. bs_env%unit_nr > 0) THEN
2632 cpwarn("RI-RS Z_lP: N_PROCS_PER_ATOM_Z_LP too small for the largest atom")
2633 END IF
2634 END IF
2635 END IF
2636 END IF
2637
2638 CALL timestop(handle)
2639
2640 END SUBROUTINE classify_z_lp_atoms
2641
2642! **************************************************************************************************
2643!> \brief Number of AO basis functions that can be non-zero inside the RI-RS integration sphere
2644!> \param bs_env ...
2645!> \param atom_P ...
2646!> \param cutoff_ri ...
2647!> \param n_ao_used ...
2648! **************************************************************************************************
2649 SUBROUTINE get_n_ao_in_sphere(bs_env, atom_P, cutoff_ri, n_ao_used)
2650
2651 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2652 INTEGER, INTENT(IN) :: atom_p
2653 REAL(kind=dp), INTENT(IN) :: cutoff_ri
2654 INTEGER, INTENT(OUT) :: n_ao_used
2655
2656 CHARACTER(LEN=*), PARAMETER :: routinen = 'get_n_ao_in_sphere'
2657
2658 INTEGER :: handle, ri_atom
2659 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
2660
2661 CALL timeset(routinen, handle)
2662
2663 particle_set => bs_env%ri_rs%particle_set
2664 n_ao_used = 0
2665 DO ri_atom = 1, bs_env%n_atom
2666 IF (norm2(particle_set(ri_atom)%r(:) - particle_set(atom_p)%r(:)) > &
2667 bs_env%ri_rs%radius_ao_per_atom(ri_atom) + cutoff_ri) cycle
2668 n_ao_used = n_ao_used + bs_env%i_ao_end_from_atom(ri_atom) - &
2669 bs_env%i_ao_start_from_atom(ri_atom) + 1
2670 END DO
2671
2672 CALL timestop(handle)
2673
2674 END SUBROUTINE get_n_ao_in_sphere
2675
2676! **************************************************************************************************
2677!> \brief Builds the sphere-local AO matrix phi_local(l, μ) = Φ_μ(r_l) for one RI atom P
2678!> \param bs_env ...
2679!> \param qs_kind_set ...
2680!> \param ri_rs_grid_points ...
2681!> \param atom_P ...
2682!> \param cutoff_ri ...
2683!> \param n_ao_total ...
2684!> \param local_grid_idx ...
2685!> \param n_local_grid ...
2686!> \param phi_local ...
2687!> \param ao_col_map ...
2688!> \param n_ao_used ...
2689!> \param center ...
2690! **************************************************************************************************
2691 SUBROUTINE build_phi_on_sphere(bs_env, qs_kind_set, ri_rs_grid_points, &
2692 atom_P, cutoff_ri, n_ao_total, local_grid_idx, n_local_grid, &
2693 phi_local, ao_col_map, n_ao_used, center)
2694
2695 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2696 TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kind_set
2697 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: ri_rs_grid_points
2698 INTEGER, INTENT(IN) :: atom_p
2699 REAL(kind=dp), INTENT(IN) :: cutoff_ri
2700 INTEGER, INTENT(IN) :: n_ao_total
2701 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: local_grid_idx
2702 INTEGER, INTENT(OUT) :: n_local_grid
2703 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :), &
2704 INTENT(OUT) :: phi_local
2705 INTEGER, ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: ao_col_map
2706 INTEGER, INTENT(OUT) :: n_ao_used
2707 REAL(kind=dp), DIMENSION(3), INTENT(IN), OPTIONAL :: center
2708
2709 CHARACTER(LEN=*), PARAMETER :: routinen = 'build_phi_on_sphere'
2710
2711 INTEGER :: col_end, col_start, handle, j, k, l, &
2712 loc_idx, n_grid_total, n_keep, ri_atom
2713 REAL(kind=dp) :: d_sp, dist, r2_threshold
2714 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: w_pt
2715 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: phi_keep, sphere_grid
2716 REAL(kind=dp), DIMENSION(3) :: pos_p
2717 TYPE(cell_type), POINTER :: cell
2718 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
2719
2720 CALL timeset(routinen, handle)
2721
2722 cell => bs_env%ri_rs%cell
2723 particle_set => bs_env%ri_rs%particle_set
2724 n_grid_total = SIZE(ri_rs_grid_points, 2)
2725 IF (PRESENT(center)) THEN
2726 pos_p(:) = center(:)
2727 ELSE
2728 pos_p(:) = particle_set(atom_p)%r(:)
2729 END IF
2730
2731 n_local_grid = 0
2732 DO l = 1, n_grid_total
2733 dist = sqrt(sum((ri_rs_grid_points(1:3, l) - pos_p(1:3))**2))
2734 IF (dist <= cutoff_ri) n_local_grid = n_local_grid + 1
2735 END DO
2736
2737 ALLOCATE (local_grid_idx(n_local_grid))
2738
2739 n_local_grid = 0
2740 DO l = 1, n_grid_total
2741 dist = sqrt(sum((ri_rs_grid_points(1:3, l) - pos_p(1:3))**2))
2742 IF (dist <= cutoff_ri) THEN
2743 n_local_grid = n_local_grid + 1
2744 local_grid_idx(n_local_grid) = l
2745 END IF
2746 END DO
2747
2748 ALLOCATE (sphere_grid(3, n_local_grid))
2749 DO loc_idx = 1, n_local_grid
2750 sphere_grid(:, loc_idx) = ri_rs_grid_points(:, local_grid_idx(loc_idx))
2751 END DO
2752
2753 ! Only AOs on atoms that reach into the sphere can be non-zero here
2754 ALLOCATE (ao_col_map(n_ao_total))
2755 ao_col_map(:) = 0
2756 n_ao_used = 0
2757 DO ri_atom = 1, bs_env%n_atom
2758 d_sp = norm2(particle_set(ri_atom)%r(:) - pos_p(:))
2759 IF (d_sp > bs_env%ri_rs%radius_ao_per_atom(ri_atom) + cutoff_ri) cycle
2760
2761 DO j = bs_env%i_ao_start_from_atom(ri_atom), bs_env%i_ao_end_from_atom(ri_atom)
2762 n_ao_used = n_ao_used + 1
2763 ao_col_map(j) = n_ao_used
2764 END DO
2765 END DO
2766
2767 ALLOCATE (phi_local(n_local_grid, n_ao_used))
2768 phi_local = 0.0_dp
2769
2770 DO ri_atom = 1, bs_env%n_atom
2771 d_sp = norm2(particle_set(ri_atom)%r(:) - pos_p(:))
2772 IF (d_sp > bs_env%ri_rs%radius_ao_per_atom(ri_atom) + cutoff_ri) cycle
2773
2774 col_start = ao_col_map(bs_env%i_ao_start_from_atom(ri_atom))
2775 col_end = ao_col_map(bs_env%i_ao_end_from_atom(ri_atom))
2776 ! A positive CUTOFF_RADIUS_RI_AO overrides the per-atom Gaussian radius
2777 ! with a user-defined hard cutoff.
2778 IF (bs_env%ri_rs%cutoff_radius_ri_ao > 0.0_dp) THEN
2779 r2_threshold = bs_env%ri_rs%cutoff_radius_ri_ao**2
2780 ELSE
2781 r2_threshold = bs_env%ri_rs%radius_ao_per_atom(ri_atom)**2
2782 END IF
2783
2784 CALL evaluate_ao_on_points(phi_local(:, col_start:col_end), sphere_grid, &
2785 ri_atom, particle_set, qs_kind_set, cell, &
2786 cutoff_squared=r2_threshold)
2787 END DO
2788
2789 DEALLOCATE (sphere_grid)
2790
2791 IF (n_local_grid > 0) THEN
2792 ALLOCATE (w_pt(n_local_grid))
2793 !$OMP PARALLEL DO DEFAULT(NONE) &
2794 !$OMP SHARED(n_local_grid, n_ao_used, phi_local, w_pt) &
2795 !$OMP PRIVATE(l, j) SCHEDULE(STATIC)
2796 DO l = 1, n_local_grid
2797 w_pt(l) = 0.0_dp
2798 DO j = 1, n_ao_used
2799 w_pt(l) = max(w_pt(l), abs(phi_local(l, j)))
2800 END DO
2801 END DO
2802 !$OMP END PARALLEL DO
2803 n_keep = count(w_pt > bs_env%eps_filter)
2804 IF (n_keep < n_local_grid) THEN
2805 ALLOCATE (phi_keep(n_keep, n_ao_used))
2806 k = 0
2807 DO l = 1, n_local_grid
2808 IF (w_pt(l) > bs_env%eps_filter) THEN
2809 k = k + 1
2810 phi_keep(k, :) = phi_local(l, :)
2811 local_grid_idx(k) = local_grid_idx(l)
2812 END IF
2813 END DO
2814 CALL move_alloc(phi_keep, phi_local)
2815 n_local_grid = n_keep
2816 END IF
2817 DEALLOCATE (w_pt)
2818 END IF
2819
2820 CALL timestop(handle)
2821
2822 END SUBROUTINE build_phi_on_sphere
2823
2824! **************************************************************************************************
2825!> \brief Computes a three-center integral block directly in AUTO_RI contraction columns.
2826!>
2827!> For each angular momentum l, the primitive RI Gaussians on the atoms contributing
2828!> to one optimized function are collected before the contraction
2829!>
2830!> (μν|p) = Σ_P (μν|P) U_Pp .
2831!>
2832!> This avoids constructing and retaining the complete reference-basis block (μν|P).
2833!> \param int_3c ...
2834!> \param ctx ...
2835!> \param ws ...
2836!> \param atom_j ...
2837!> \param atom_k ...
2838!> \param atom_i ...
2839!> \param transform ...
2840!> \param transform_row ...
2841!> \param screened ...
2842! **************************************************************************************************
2843 SUBROUTINE build_3c_integral_block_auto_ri_ctx(int_3c, ctx, ws, atom_j, atom_k, atom_i, &
2844 transform, transform_row, screened)
2845 REAL(kind=dp), DIMENSION(:, :, :), INTENT(INOUT) :: int_3c
2846 TYPE(gw_3c_ctx_type), INTENT(IN) :: ctx
2847 TYPE(gw_3c_ws_type), INTENT(INOUT) :: ws
2848 INTEGER, INTENT(IN) :: atom_j, atom_k, atom_i
2849 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: transform
2850 INTEGER, INTENT(IN) :: transform_row
2851 LOGICAL, INTENT(OUT), OPTIONAL :: screened
2852
2853 CHARACTER(LEN=*), PARAMETER :: routinen = 'build_3c_integral_block_auto_ri_ctx'
2854
2855 INTEGER :: handle_contract, handle_eri, ikind, iset, jkind, jset, kkind, kset, l, ncoi, &
2856 ncoj, ncok, ncol, npgf_group, nseti, nsetj, nsetk, primitive_first, sgfi, sgfj, sgfk
2857 INTEGER, DIMENSION(:), POINTER :: lmax_i, lmax_j, lmax_k, lmin_i, lmin_j, &
2858 lmin_k, npgfi, npgfj, npgfk, nsgfi, &
2859 nsgfj, nsgfk
2860 INTEGER, DIMENSION(:, :), POINTER :: first_sgf_i, first_sgf_j, first_sgf_k
2861 REAL(kind=dp) :: dij, dik, djk, group_radius, &
2862 kind_radius_i, kind_radius_j, &
2863 kind_radius_k, sijk_ext
2864 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: rpgf_group, zet_group
2865 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: spi_group
2866 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :) :: sijk, sijk_contr
2867 REAL(kind=dp), DIMENSION(3) :: ri, rij, rik, rj, rjk, rk
2868 REAL(kind=dp), DIMENSION(:), POINTER :: set_radius_i, set_radius_j, set_radius_k
2869 REAL(kind=dp), DIMENSION(:, :), POINTER :: rpgf_i, rpgf_j, rpgf_k, zeti, zetj, zetk
2870
2871 IF (PRESENT(screened)) screened = .false.
2872 ri = pbc(ctx%particle_set(atom_i)%r(1:3), ctx%cell)
2873 rj = pbc(ctx%particle_set(atom_j)%r(1:3), ctx%cell)
2874 rk = pbc(ctx%particle_set(atom_k)%r(1:3), ctx%cell)
2875 rjk = rk - rj
2876 rij = rj - ri
2877 rik = rk - ri
2878 djk = norm2(rjk)
2879 dij = norm2(rij)
2880 dik = norm2(rik)
2881
2882 ikind = ctx%kind_of(atom_i)
2883 jkind = ctx%kind_of(atom_j)
2884 kkind = ctx%kind_of(atom_k)
2885 CALL get_gto_basis_set(ctx%basis_i(ikind)%gto_basis_set, first_sgf=first_sgf_i, &
2886 lmax=lmax_i, lmin=lmin_i, npgf=npgfi, nset=nseti, &
2887 nsgf_set=nsgfi, pgf_radius=rpgf_i, set_radius=set_radius_i, &
2888 zet=zeti, kind_radius=kind_radius_i)
2889 CALL get_gto_basis_set(ctx%basis_j(jkind)%gto_basis_set, first_sgf=first_sgf_j, &
2890 lmax=lmax_j, lmin=lmin_j, npgf=npgfj, nset=nsetj, &
2891 nsgf_set=nsgfj, pgf_radius=rpgf_j, set_radius=set_radius_j, &
2892 zet=zetj, kind_radius=kind_radius_j)
2893 CALL get_gto_basis_set(ctx%basis_k(kkind)%gto_basis_set, first_sgf=first_sgf_k, &
2894 lmax=lmax_k, lmin=lmin_k, npgf=npgfk, nset=nsetk, &
2895 nsgf_set=nsgfk, pgf_radius=rpgf_k, set_radius=set_radius_k, &
2896 zet=zetk, kind_radius=kind_radius_k)
2897
2898 IF (kind_radius_j + kind_radius_i + ctx%dr_ij < dij .OR. &
2899 kind_radius_j + kind_radius_k + ctx%dr_jk < djk .OR. &
2900 kind_radius_k + kind_radius_i + ctx%dr_ik < dik) THEN
2901 IF (PRESENT(screened)) screened = .true.
2902 RETURN
2903 END IF
2904
2905 ncol = SIZE(transform, 2)
2906 cpassert(SIZE(int_3c, 3) == ncol)
2907 DO l = 0, ctx%maxli
2908 npgf_group = 0
2909 group_radius = 0.0_dp
2910 DO iset = 1, nseti
2911 IF (lmin_i(iset) /= l .OR. lmax_i(iset) /= l) cycle
2912 npgf_group = npgf_group + npgfi(iset)
2913 group_radius = max(group_radius, set_radius_i(iset))
2914 END DO
2915 IF (npgf_group == 0) cycle
2916 ncoi = npgf_group*ncoset(l)
2917 ALLOCATE (zet_group(npgf_group), rpgf_group(npgf_group))
2918 ALLOCATE (spi_group(ncoi, ncol), source=0.0_dp)
2919 primitive_first = 1
2920 DO iset = 1, nseti
2921 IF (lmin_i(iset) /= l .OR. lmax_i(iset) /= l) cycle
2922 zet_group(primitive_first:primitive_first + npgfi(iset) - 1) = &
2923 zeti(1:npgfi(iset), iset)
2924 rpgf_group(primitive_first:primitive_first + npgfi(iset) - 1) = &
2925 rpgf_i(1:npgfi(iset), iset)
2926 sgfi = first_sgf_i(1, iset)
2927 spi_group((primitive_first - 1)*ncoset(l) + 1: &
2928 (primitive_first + npgfi(iset) - 1)*ncoset(l), :) = &
2929 matmul(ctx%spi(iset, ikind)%array, &
2930 transform(transform_row + sgfi - 1: &
2931 transform_row + sgfi + nsgfi(iset) - 2, :))
2932 primitive_first = primitive_first + npgfi(iset)
2933 END DO
2934
2935 DO jset = 1, nsetj
2936 IF (set_radius_j(jset) + group_radius + ctx%dr_ij < dij) cycle
2937 DO kset = 1, nsetk
2938 IF (set_radius_j(jset) + set_radius_k(kset) + ctx%dr_jk < djk) cycle
2939 IF (set_radius_k(kset) + group_radius + ctx%dr_ik < dik) cycle
2940 ncoj = npgfj(jset)*ncoset(lmax_j(jset))
2941 ncok = npgfk(kset)*ncoset(lmax_k(kset))
2942 sgfj = first_sgf_j(1, jset)
2943 sgfk = first_sgf_k(1, kset)
2944 IF (ncoj*ncok*ncoi <= 0) cycle
2945 ALLOCATE (sijk(ncoj, ncok, ncoi), source=0.0_dp)
2946 CALL timeset(routinen//'_eri', handle_eri)
2947 CALL eri_3center(sijk, &
2948 lmin_j(jset), lmax_j(jset), npgfj(jset), zetj(:, jset), &
2949 rpgf_j(:, jset), rj, &
2950 lmin_k(kset), lmax_k(kset), npgfk(kset), zetk(:, kset), &
2951 rpgf_k(:, kset), rk, l, l, npgf_group, zet_group, &
2952 rpgf_group, ri, djk, dij, dik, ws%lib, ctx%potential_parameter, &
2953 int_abc_ext=sijk_ext)
2954 CALL timestop(handle_eri)
2955 ALLOCATE (sijk_contr(nsgfj(jset), nsgfk(kset), ncol))
2956 CALL timeset(routinen//'_contract', handle_contract)
2957 CALL abc_contract_xsmm(sijk_contr, sijk, ctx%tspj(jset, jkind)%array, &
2958 ctx%spk(kset, kkind)%array, spi_group, ncoj, ncok, ncoi, &
2959 nsgfj(jset), nsgfk(kset), ncol, ws%cpp_buffer, ws%ccp_buffer)
2960 CALL timestop(handle_contract)
2961 DEALLOCATE (sijk)
2962 int_3c(sgfj:sgfj + nsgfj(jset) - 1, sgfk:sgfk + nsgfk(kset) - 1, :) = &
2963 int_3c(sgfj:sgfj + nsgfj(jset) - 1, sgfk:sgfk + nsgfk(kset) - 1, :) + &
2964 sijk_contr
2965 DEALLOCATE (sijk_contr)
2966 END DO
2967 END DO
2968 DEALLOCATE (zet_group, rpgf_group, spi_group)
2969 END DO
2970
2971 END SUBROUTINE build_3c_integral_block_auto_ri_ctx
2972
2973! **************************************************************************************************
2974!> \brief Returns the reference RI basis size of one atom.
2975!> \param bs_env ...
2976!> \param iatom ...
2977!> \return ...
2978! **************************************************************************************************
2979 INTEGER FUNCTION get_ref_ri_size(bs_env, iatom) RESULT(n)
2980 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
2981 INTEGER, INTENT(IN) :: iatom
2982
2983 INTEGER :: ikind
2984
2985 ikind = bs_env%ri_rs%particle_set(iatom)%atomic_kind%kind_number
2986 n = bs_env%basis_set_RI(ikind)%gto_basis_set%nsgf
2987
2988 END FUNCTION get_ref_ri_size
2989
2990END MODULE gw_compute_z_lp
static GRID_HOST_DEVICE int idx(const orbital a)
Return coset index of given orbital angular momentum.
static void dgemm(const char transa, const char transb, const int m, const int n, const int k, const double alpha, const double *a, const int lda, const double *b, const int ldb, const double beta, double *c, const int ldc)
Convenient wrapper to hide Fortran nature of dgemm_, swapping a and b.
Contraction of integrals over primitive Cartesian Gaussians based on the contraction matrix sphi whic...
subroutine, public abc_contract_xsmm(abcint, sabc, sphi_a, sphi_b, sphi_c, ncoa, ncob, ncoc, nsgfa, nsgfb, nsgfc, cpp_buffer, ccp_buffer, prefac, pstfac)
3-center contraction routine from primitive cartesian Gaussians to spherical Gaussian functions using...
Definition atom.F:9
Define the atomic kind types and their sub types.
subroutine, public get_gto_basis_set(gto_basis_set, name, aliases, norm_type, kind_radius, ncgf, nset, nsgf, cgf_symbol, sgf_symbol, norm_cgf, set_radius, lmax, lmin, lx, ly, lz, m, ncgf_set, npgf, nsgf_set, nshell, cphi, pgf_radius, sphi, scon, zet, first_cgf, first_sgf, l, last_cgf, last_sgf, n, gcc, maxco, maxl, maxpgf, maxsgf_set, maxshell, maxso, nco_sum, npgf_sum, nshell_sum, maxder, short_kind_radius, npgf_seg_sum, ccon)
...
Handles all functions related to the CELL.
Definition cell_types.F:15
methods related to the blacs parallel environment
subroutine, public cp_blacs_env_release(blacs_env)
releases the given blacs_env
subroutine, public cp_blacs_env_create(blacs_env, para_env, blacs_grid_layout, blacs_repeatable, row_major, grid_2d)
allocates and initializes a type that represent a blacs context
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_block_p(matrix, row, col, block, found, row_size, col_size)
...
subroutine, public dbcsr_multiply(transa, transb, alpha, matrix_a, matrix_b, beta, matrix_c, first_row, last_row, first_column, last_column, first_k, last_k, retain_sparsity, filter_eps, flop)
...
subroutine, public dbcsr_get_info(matrix, nblkrows_total, nblkcols_total, nfullrows_total, nfullcols_total, nblkrows_local, nblkcols_local, nfullrows_local, nfullcols_local, my_prow, my_pcol, local_rows, local_cols, proc_row_dist, proc_col_dist, row_blk_size, col_blk_size, row_blk_offset, col_blk_offset, distribution, name, matrix_type, group)
...
subroutine, public dbcsr_filter(matrix, eps)
...
subroutine, public dbcsr_binary_write(matrix, filepath)
...
subroutine, public dbcsr_finalize(matrix)
...
subroutine, public dbcsr_set(matrix, alpha)
...
subroutine, public dbcsr_release(matrix)
...
subroutine, public dbcsr_binary_read(filepath, distribution, matrix_new)
...
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)
...
subroutine, public dbcsr_reserve_all_blocks(matrix)
Reserves all blocks.
represent the structure of a full matrix
represent a full matrix distributed on many processors
Definition cp_fm_types.F:15
various routines to log and control the output. The idea is that decisions about where to log should ...
type(cp_logger_type) function, pointer, public cp_get_default_logger()
returns the default logger
routines to handle the output, The idea is to remove the decision of wheter to output and what to out...
integer, parameter, public cp_p_file
integer function, public cp_print_key_should_output(iteration_info, basis_section, print_key_path, used_print_key, first_time)
returns what should be done with the given property if btest(res,cp_p_store) then the property should...
Input and persistent data for automatic RI basis optimization.
Shared numerical operations for computing the RI-RS matrix Z_lP.
subroutine, public build_gram_jacobi_blas(phi_local, n_local_grid, n_ao_used, tikhonov, d_local, d_vec_local)
Forms the conditioned dense RI-RS matrix.
subroutine, public solve_d_lp_distributed(phi_local, d_vec, d_lp, n_loc, n_ao, n_rhs, tikhonov, para_env_sub, blacs_env_sub, fm_struct_d, fm_struct_b, fm_d, fm_b, info)
Solves D Z = d with ScaLAPACK for one RI atom and replicated inputs.
subroutine, public scale_rows_by_diag(matrix, diagonal, nrow, ncol)
Multiplies every matrix row by the corresponding diagonal entry: A(l, :) <- d_l A(l,...
subroutine, public store_z_lp_columns(mat_z_lp, z_local, local_grid_idx, n_local_grid, n_loc_ri, atom_p, r_blk_sizes, row_offset, eps_filter)
Stores dense Z_lP columns in the distributed block-sparse matrix.
subroutine, public build_jacobi_diag_from_phi(phi_local, n_local_grid, n_ao_used, d_vec_local)
Computes d_l = 1/sqrt(D_ll) = 1/Σ_μ Φ_μ(r_l)² without forming D.
Computes the RI-RS fitting matrix Z_lP.
subroutine, public compute_z_lp(qs_env, bs_env, ri_rs_grid_points, mat_phi_mu_l, mat_z_lp)
Computes Z_lP in either the standard tabulated or automatically generated RI basis set.
Utility method to build 3-center integrals for small cell GW.
subroutine, public build_3c_integral_block_ctx(int_3c, ctx, ws, atom_j, atom_k, atom_i, cell_j, cell_k, cell_i, j_offset, k_offset, i_offset, screened)
Computes the 3c integral block (mu(atom_j) nu(atom_k) | P(atom_i)) for ONE atom triple,...
subroutine, public gw_3c_ws_create(ws, ctx)
Creates a per-thread 3c workspace: libint object + LIBXSMM contraction buffers.
subroutine, public gw_3c_ws_release(ws)
Releases a per-thread 3c workspace.
subroutine, public gw_3c_ctx_release(ctx)
Releases the shared 3c-integral context.
subroutine, public gw_3c_ctx_create(ctx, qs_env, potential_parameter, basis_j, basis_k, basis_i)
Builds the shared 3c-integral context: screening radii, basis maxima, contracted sphi tables,...
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 dp
Definition kinds.F:34
2- and 3-center electron repulsion integral routines based on libint2 Currently available operators: ...
subroutine, public eri_3center(int_abc, la_min, la_max, npgfa, zeta, rpgfa, ra, lb_min, lb_max, npgfb, zetb, rpgfb, rb, lc_min, lc_max, npgfc, zetc, rpgfc, rc, dab, dac, dbc, lib, potential_parameter, int_abc_ext)
Computes the 3-center electron repulsion integrals (ab|c) for a given set of cartesian gaussian orbit...
Machine interface based on Fortran 2003 and POSIX.
Definition machine.F:17
subroutine, public m_flush(lunit)
flushes units if the &GLOBAL flag is set accordingly
Definition machine.F:124
real(kind=dp) function, public m_walltime()
returns time from a real-time clock, protected against rolling early/easily
Definition machine.F:141
Interface to the message passing library MPI.
subroutine, public mp_mem_avail_per_rank_gb(comm, mem_avail_gb)
Memory that is currently free on the node, per MPI rank of that node, in GB.
Provides Cartesian and spherical orbital pointers and indices.
integer, dimension(:), allocatable, public ncoset
Define the data structure for the particle information.
Definition of physical constants:
Definition physcon.F:68
real(kind=dp), parameter, public angstrom
Definition physcon.F:144
subroutine, public get_qs_env(qs_env, atomic_kind_set, qs_kind_set, cell, super_cell, cell_ref, use_ref_cell, kpoints, dft_control, mos, sab_orb, sab_all, qmmm, qmmm_periodic, mimic, sac_ae, sac_ppl, sac_lri, sap_ppnl, sab_vdw, sab_scp, sap_oce, sab_lrc, sab_se, sab_xtbe, sab_tbe, sab_core, sab_xb, sab_xtb_pp, sab_xtb_nonbond, sab_almo, sab_kp, sab_kp_nosym, sab_cneo, particle_set, energy, force, matrix_h, matrix_h_im, matrix_ks, matrix_ks_im, matrix_vxc, run_rtp, rtp, matrix_h_kp, matrix_h_im_kp, matrix_ks_kp, matrix_ks_im_kp, matrix_vxc_kp, kinetic_kp, matrix_s_kp, matrix_w_kp, matrix_s_ri_aux_kp, matrix_s, matrix_s_ri_aux, matrix_w, matrix_p_mp2, matrix_p_mp2_admm, matrix_vhxc, rho, rho_xc, pw_env, ewald_env, ewald_pw, active_space, mpools, input, para_env, blacs_env, scf_control, rel_control, kinetic, qs_charges, vppl, xcint_weights, rho_core, rho_nlcc, rho_nlcc_g, ks_env, ks_qmmm_env, wf_history, scf_env, local_particles, local_molecules, distribution_2d, dbcsr_dist, molecule_kind_set, molecule_set, subsys, cp_subsys, oce, local_rho_set, rho_atom_set, task_list, task_list_soft, rho0_atom_set, rho0_mpole, rhoz_set, rhoz_cneo_set, ecoul_1c, rho0_s_rs, rho0_s_gs, rhoz_cneo_s_rs, rhoz_cneo_s_gs, do_kpoints, has_unit_metric, requires_mo_derivs, mo_derivs, mo_loc_history, nkind, natom, nelectron_total, nelectron_spin, efield, neighbor_list_id, linres_control, xas_env, virial, cp_ddapc_env, cp_ddapc_ewald, outer_scf_history, outer_scf_ihistory, x_data, et_coupling, dftb_potential, results, se_taper, se_store_int_env, se_nddo_mpole, se_nonbond_env, admm_env, lri_env, lri_density, exstate_env, ec_env, harris_env, dispersion_env, gcp_env, vee, rho_external, external_vxc, mask, mp2_env, bs_env, kg_env, wanniercentres, atprop, ls_scf_env, do_transport, transport_env, v_hartree_rspace, s_mstruct_changed, rho_changed, potential_changed, forces_up_to_date, mscfg_env, almo_scf_env, gradient_history, variable_history, embed_pot, spin_embed_pot, polar_env, mos_last_converged, eeq, rhs, do_rixs, tb_tblite)
Get the QUICKSTEP environment.
Define the quickstep kind type and their sub types.
Shared RI-RS grid I/O and contracted-Gaussian evaluation utilities.
subroutine, public evaluate_ao_on_points(phi, grid_points, atom_index, particle_set, qs_kind_set, cell, dphi, cutoff_squared)
Evaluate contracted spherical AOs, and optionally their grid-coordinate derivatives.
All kind of helpful little routines.
Definition util.F:14
Provides all information about an atomic kind.
Type defining parameters related to the simulation cell.
Definition cell_types.F:60
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...
Shared read-only context for repeated 3-center integral block builds: screening parameters,...
Per-thread workspace for 3-center integral block builds: libint object + contraction buffers....
stores all the informations relevant to an mpi environment
Provides all information about a quickstep kind.