(git:f2099e5)
Loading...
Searching...
No Matches
gw_ri_rs_grid_initialization.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 Initialize atom-owned RI-RS grids by Cholesky selection from Lebedev grids.
10! **************************************************************************************************
17 USE kinds, ONLY: default_string_length,&
18 dp
27 USE util, ONLY: locate
28#include "./base/base_uses.f90"
29
30 IMPLICIT NONE
31 PRIVATE
32 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'gw_ri_rs_grid_initialization'
33
34 PUBLIC :: initialize_ri_rs_grid
35
36CONTAINS
37
38! **************************************************************************************************
39!> \brief Construct the initial RI-RS grid of every atom.
40!>
41!> For every atom B, the Lebedev grid points are
42!>
43!> (1) r_l = R_B + ρ_s Ω_a,
44!>
45!> where ρ_s is a radial quadrature point and Ω_a is a Lebedev direction. Lebedev grids are
46!> tabulated according to the angular degree L up to which they integrate exactly. To integrate a
47!> three-centre integral (μν|P), the smallest available Lebedev grid satisfying
48!>
49!> (2) L >= 2 l_AO,max + l_RI,max + ΔL
50!>
51!> is used. Here, l_AO,max is the maximum angular momentum of the atomic AO basis functions ϕ_μ,
52!> l_RI,max is the maximum angular momentum of the auxiliary basis functions φ_P, and the internal
53!> angular buffer is ΔL. Enough radial points ρ_s are used that the Lebedev grid of every atom B
54!> contains at least
55!>
56!> (3) N_initial^B >= α_initial N_AO^B,
57!>
58!> points, where N_AO^B is the number of atomic orbitals on atom B and
59!> α_initial = max(30, 2 RS_AO_RATIO). The factor two provides candidates that can be discarded by
60!> the molecular Voronoi filter. The requested initial RI-RS grid of atom A contains
61!>
62!> (4) N_R^A = ceil(RS_AO_RATIO * N_AO^A)
63!>
64!> points. Around every atom A, a cluster is defined as
65!>
66!> (5) C_A = {B : |R_B - R_A| <= CUTOFF_ATOMIC_CLUSTER}.
67!>
68!> The Cholesky selection uses all Lebedev grid points of every atom B in C_A. The molecular
69!> Voronoi cell of atom A is
70!>
71!> (6) V_A = {r_l : |r_l - R_A| <= |r_l - R_B| for every atom B}.
72!>
73!> For cluster C_A, Cholesky selection is performed on
74!>
75!> (7) D_ll' = [Σ_(μ in C_A) ϕ_μ(r_l) ϕ_μ(r_l')]^2.
76!>
77!> The first grid point is the point with the largest diagonal element,
78!>
79!> (8) d_l^(0) = D_ll, q_1 = arg max_l d_l^(0).
80!>
81!> For every selected point q_k, the Cholesky column and diagonal are updated according to
82!>
83!> (9) L_lk = [D_lq_k - Σ_(j<k) L_lj L_q_kj]/sqrt(d_q_k^(k-1)),
84!>
85!> (10) d_l^(k) = max(0, d_l^(k-1) - L_lk^2),
86!>
87!> and the next point q_(k+1) is the point with the largest d_l^(k). Points outside V_A take part
88!> in Eqs. (7)-(10), but only selected points inside V_A are placed into the RI-RS grid of atom A.
89!> If D_ll' reaches its numerical rank before N_R^A points have been retained, let S_A contain the
90!> retained points. Every unused point r_l in V_A is assigned the distance
91!>
92!> (11) ρ_l = min_(q in S_A) |r_l - q|,
93!>
94!> and the point
95!>
96!> (12) q_new = arg max_(r_l in V_A and r_l not in S_A) ρ_l
97!>
98!> is appended to S_A. Equations (11)-(12) are repeated until Eq. (4) is satisfied. This maximin
99!> distance is only a geometric selection criterion, not the three-centre-integral fitting error.
100!> Each cluster C_A is processed independently on one MPI rank; only the completed atom grids are
101!> communicated.
102!> \param bs_env Band-structure environment containing GW parameters.
103! **************************************************************************************************
104 SUBROUTINE initialize_ri_rs_grid(bs_env)
105 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
106
107 CHARACTER(LEN=*), PARAMETER :: routinen = 'initialize_ri_rs_grid'
108 INTEGER, PARAMETER :: l_additional = 2, radial_quadrature = do_gapw_log
109 REAL(kind=dp), PARAMETER :: minimum_candidate_ratio = 30.0_dp
110
111 INTEGER :: handle, ikind
112 REAL(kind=dp) :: candidate_ratio
113 TYPE(rirs_grid_type), ALLOCATABLE :: radial_lebedev_grids(:)
114
115 CALL timeset(routinen, handle)
116
117 candidate_ratio = max(minimum_candidate_ratio, 2.0_dp*bs_env%ri_rs%grid_opt%rs_ao_ratio)
118
119 ! Build the Lebedev grids of Eqs. (1)-(3) for all elements in the calculation
120 ALLOCATE (radial_lebedev_grids(SIZE(bs_env%basis_set_AO)))
121 DO ikind = 1, SIZE(bs_env%basis_set_AO)
122 CALL build_lebedev_grid(bs_env%basis_set_AO(ikind)%gto_basis_set, &
123 bs_env%basis_set_RI(ikind)%gto_basis_set, &
124 candidate_ratio, l_additional, radial_quadrature, &
125 radial_lebedev_grids(ikind))
126 END DO
127
128 ! Apply Eqs. (5)-(12) for every atom and keep points inside Voronoi volume, Eq. (6)
129 CALL cholesky_selection_and_voronoi_filtering(bs_env, radial_lebedev_grids)
130
131 CALL broadcast_ri_rs_grids(bs_env)
132
133 CALL timestop(handle)
134
135 END SUBROUTINE initialize_ri_rs_grid
136
137! **************************************************************************************************
138!> \brief Build one fixed-orientation Lebedev grid according to Eqs. (1)-(3).
139!> \param ao ...
140!> \param ri ...
141!> \param ratio α_initial in Eq. (3).
142!> \param l_additional ΔL in Eq. (2).
143!> \param radial_quadrature ...
144!> \param grid ...
145! **************************************************************************************************
146 SUBROUTINE build_lebedev_grid(ao, ri, ratio, l_additional, radial_quadrature, grid)
147 TYPE(gto_basis_set_type), POINTER :: ao, ri
148 REAL(kind=dp), INTENT(IN) :: ratio
149 INTEGER, INTENT(IN) :: l_additional, radial_quadrature
150 TYPE(rirs_grid_type), INTENT(OUT) :: grid
151
152 INTEGER :: degree, ir, nang, nrad, offset, rule
153 TYPE(grid_atom_type), POINTER :: radial_grid
154
155 cpassert(ASSOCIATED(ao) .AND. ASSOCIATED(ri))
156 degree = 2*maxval(ao%lmax) + maxval(ri%lmax) + l_additional
157 rule = get_number_of_lebedev_grid(l=degree)
158 nang = lebedev_grid(rule)%n
159 nrad = max(2, ceiling(ratio*real(ao%nsgf, dp)/real(nang, dp)))
160
161 NULLIFY (radial_grid)
162 CALL allocate_grid_atom(radial_grid)
163 CALL create_grid_atom(radial_grid, nrad, nang, 0, rule, radial_quadrature)
164 grid%npts = nrad*nang
165 ALLOCATE (grid%raw_points(3, grid%npts))
166 DO ir = 1, nrad
167 offset = (ir - 1)*nang
168 grid%raw_points(:, offset + 1:offset + nang) = &
169 radial_grid%rad(ir)*lebedev_grid(rule)%r
170 END DO
171 CALL deallocate_grid_atom(radial_grid)
172 END SUBROUTINE build_lebedev_grid
173
174! **************************************************************************************************
175!> \brief Apply Eqs. (5)-(12) independently for every atom and keep points inside Eq. (6).
176!> \param bs_env ...
177!> \param radial_lebedev_grids ...
178! **************************************************************************************************
179 SUBROUTINE cholesky_selection_and_voronoi_filtering(bs_env, radial_lebedev_grids)
180 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
181 TYPE(rirs_grid_type), INTENT(IN) :: radial_lebedev_grids(:)
182
183 INTEGER :: atom_mpi_rank, iatom, ikind, npoints
184
185 cpassert(.NOT. ALLOCATED(bs_env%ri_rs%atomic_grids))
186 ALLOCATE (bs_env%ri_rs%atomic_grids(bs_env%n_atom))
187 DO iatom = 1, bs_env%n_atom
188 ikind = bs_env%ri_rs%particle_set(iatom)%atomic_kind%kind_number
189 npoints = ceiling(bs_env%ri_rs%grid_opt%rs_ao_ratio* &
190 bs_env%basis_set_AO(ikind)%gto_basis_set%nsgf)
191 bs_env%ri_rs%atomic_grids(iatom)%npts = npoints
192 ALLOCATE (bs_env%ri_rs%atomic_grids(iatom)%raw_points(3, npoints))
193
194 atom_mpi_rank = mod(iatom - 1, bs_env%para_env%num_pe)
195 IF (atom_mpi_rank == bs_env%para_env%mepos) THEN
196 CALL select_cholesky_grid_iatom(bs_env, radial_lebedev_grids, iatom)
197 END IF
198 END DO
199 END SUBROUTINE cholesky_selection_and_voronoi_filtering
200
201! **************************************************************************************************
202!> \brief Select one atom grid with Eqs. (5)-(12) and the molecular Voronoi cell in Eq. (6).
203!> \param bs_env ...
204!> \param radial_lebedev_grids ...
205!> \param iatom ...
206! **************************************************************************************************
207 SUBROUTINE select_cholesky_grid_iatom(bs_env, radial_lebedev_grids, iatom)
208 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
209 TYPE(rirs_grid_type), INTENT(IN) :: radial_lebedev_grids(:)
210 INTEGER, INTENT(IN) :: iatom
211
212 CHARACTER(LEN=3*default_string_length) :: failure
213 INTEGER :: igrid_point, npoints, nretained
214 INTEGER, ALLOCATABLE :: ao_point_indices(:), atom_n_ao(:), atom_point_offsets(:), &
215 atom_value_offsets(:), cluster_atoms(:), selected_indices(:)
216 LOGICAL, ALLOCATABLE :: inside_voronoi(:)
217 REAL(kind=dp), ALLOCATABLE :: ao_values(:), cluster_points(:, :)
218
219 CALL get_rirs_cluster_atoms(bs_env%ri_rs%particle_set, bs_env%ri_rs%cell, iatom, &
220 bs_env%ri_rs%grid_opt%cutoff_atomic_cluster, cluster_atoms)
221 CALL collect_cluster_lebedev_points(bs_env, radial_lebedev_grids, iatom, &
222 cluster_atoms, cluster_points)
223 npoints = SIZE(cluster_points, 2)
224 ALLOCATE (inside_voronoi(npoints))
225 CALL filter_grid_to_voronoi(cluster_points, iatom, bs_env%ri_rs%particle_set, &
226 mask=inside_voronoi)
227 IF (count(inside_voronoi) < bs_env%ri_rs%atomic_grids(iatom)%npts) THEN
228 WRITE (failure, '(A,I0,A,I0,A,I0,A)') 'Atom ', iatom, ': only ', count(inside_voronoi), &
229 ' Voronoi grid points for ', bs_env%ri_rs%atomic_grids(iatom)%npts, &
230 ' points; reduce RS_AO_RATIO.'
231 cpabort(trim(failure))
232 END IF
233
234 CALL evaluate_cluster_ao_values(bs_env, iatom, cluster_atoms, cluster_points, &
235 ao_point_indices, atom_point_offsets, atom_value_offsets, &
236 atom_n_ao, ao_values)
237 ALLOCATE (selected_indices(bs_env%ri_rs%atomic_grids(iatom)%npts))
238 CALL select_cholesky_grid_points(ao_point_indices, atom_point_offsets, atom_value_offsets, &
239 atom_n_ao, ao_values, npoints, &
240 inside_voronoi, selected_indices)
241 nretained = count(selected_indices > 0)
242 IF (nretained == 0) THEN
243 WRITE (failure, '(A,I0,A)') 'Atom ', iatom, &
244 ': Cholesky selection found no numerically independent point.'
245 cpabort(trim(failure))
246 ELSE IF (nretained < SIZE(selected_indices)) THEN
247 CALL complete_grid_by_maximin_distance(cluster_points, inside_voronoi, selected_indices)
248 END IF
249 DO igrid_point = 1, SIZE(selected_indices)
250 bs_env%ri_rs%atomic_grids(iatom)%raw_points(:, igrid_point) = &
251 cluster_points(:, selected_indices(igrid_point))
252 END DO
253 END SUBROUTINE select_cholesky_grid_iatom
254
255! **************************************************************************************************
256!> \brief Complete a rank-saturated atom grid with the geometric maximin rule in Eqs. (11)-(12).
257!> \param cluster_points Coordinates r_l of all existing cluster-grid points.
258!> \param inside_voronoi True for points inside the molecular Voronoi cell V_A.
259!> \param selected_indices Cholesky indices on entry and the completed indices on exit.
260! **************************************************************************************************
261 SUBROUTINE complete_grid_by_maximin_distance(cluster_points, inside_voronoi, selected_indices)
262 REAL(kind=dp), INTENT(IN) :: cluster_points(:, :)
263 LOGICAL, INTENT(IN) :: inside_voronoi(:)
264 INTEGER, INTENT(INOUT) :: selected_indices(:)
265
266 INTEGER :: igrid_point, inew_point, iselected, &
267 nretained
268 LOGICAL, ALLOCATABLE :: unselected_inside(:)
269 REAL(kind=dp) :: distance_sq, largest_distance_sq
270 REAL(kind=dp), ALLOCATABLE :: nearest_distance_sq(:)
271
272 cpassert(SIZE(cluster_points, 2) == SIZE(inside_voronoi))
273 cpassert(count(inside_voronoi) >= SIZE(selected_indices))
274
275 nretained = count(selected_indices > 0)
276 cpassert(nretained > 0)
277 ALLOCATE (unselected_inside(SIZE(inside_voronoi)), &
278 nearest_distance_sq(SIZE(inside_voronoi)))
279 unselected_inside(:) = inside_voronoi
280 nearest_distance_sq(:) = huge(1.0_dp)
281
282 ! Initialize ρ_l^2 in Eq. (11) from all retained Cholesky points.
283 DO iselected = 1, nretained
284 unselected_inside(selected_indices(iselected)) = .false.
285 DO igrid_point = 1, SIZE(inside_voronoi)
286 IF (.NOT. unselected_inside(igrid_point)) cycle
287 distance_sq = sum((cluster_points(:, igrid_point) - &
288 cluster_points(:, selected_indices(iselected)))**2)
289 nearest_distance_sq(igrid_point) = &
290 min(nearest_distance_sq(igrid_point), distance_sq)
291 END DO
292 END DO
293
294 DO WHILE (nretained < SIZE(selected_indices))
295 inew_point = 0
296 largest_distance_sq = -1.0_dp
297 DO igrid_point = 1, SIZE(inside_voronoi)
298 IF (.NOT. unselected_inside(igrid_point)) cycle
299 ! Strict comparison makes the lowest cluster-point index win an exact tie.
300 IF (nearest_distance_sq(igrid_point) > largest_distance_sq) THEN
301 largest_distance_sq = nearest_distance_sq(igrid_point)
302 inew_point = igrid_point
303 END IF
304 END DO
305 cpassert(inew_point > 0)
306
307 nretained = nretained + 1
308 selected_indices(nretained) = inew_point
309 unselected_inside(inew_point) = .false.
310 DO igrid_point = 1, SIZE(inside_voronoi)
311 IF (.NOT. unselected_inside(igrid_point)) cycle
312 distance_sq = sum((cluster_points(:, igrid_point) - &
313 cluster_points(:, inew_point))**2)
314 nearest_distance_sq(igrid_point) = &
315 min(nearest_distance_sq(igrid_point), distance_sq)
316 END DO
317 END DO
318 END SUBROUTINE complete_grid_by_maximin_distance
319
320! **************************************************************************************************
321!> \brief Collect all cluster Lebedev points used in Eqs. (5) and (7).
322!> \param bs_env ...
323!> \param radial_lebedev_grids ...
324!> \param iatom ...
325!> \param cluster_atoms ...
326!> \param cluster_points ...
327! **************************************************************************************************
328 SUBROUTINE collect_cluster_lebedev_points(bs_env, radial_lebedev_grids, iatom, &
329 cluster_atoms, cluster_points)
330 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
331 TYPE(rirs_grid_type), INTENT(IN) :: radial_lebedev_grids(:)
332 INTEGER, INTENT(IN) :: iatom, cluster_atoms(:)
333 REAL(kind=dp), ALLOCATABLE, INTENT(OUT) :: cluster_points(:, :)
334
335 INTEGER :: cluster_iatom, i_cluster_atom, ikind, n, &
336 npoints, offset
337
338 npoints = 0
339 DO i_cluster_atom = 1, SIZE(cluster_atoms)
340 ikind = bs_env%ri_rs%particle_set(cluster_atoms(i_cluster_atom))%atomic_kind%kind_number
341 npoints = npoints + radial_lebedev_grids(ikind)%npts
342 END DO
343 ALLOCATE (cluster_points(3, npoints))
344
345 offset = 0
346 DO i_cluster_atom = 1, SIZE(cluster_atoms)
347 cluster_iatom = cluster_atoms(i_cluster_atom)
348 ikind = bs_env%ri_rs%particle_set(cluster_iatom)%atomic_kind%kind_number
349 n = radial_lebedev_grids(ikind)%npts
350 cluster_points(:, offset + 1:offset + n) = radial_lebedev_grids(ikind)%raw_points + &
351 spread(bs_env%ri_rs%particle_set(cluster_iatom)%r - &
352 bs_env%ri_rs%particle_set(iatom)%r, 2, n)
353 offset = offset + n
354 END DO
355 END SUBROUTINE collect_cluster_lebedev_points
356
357! **************************************************************************************************
358!> \brief Evaluate ϕ_μ(r_l) needed for D_ll' in Eq. (7), using AO locality. Atom b uses the
359!> point rows atom_point_offsets(b):atom_point_offsets(b+1)-1. Its column-major block
360!> ϕ_μ(r_l) has shape (number of point rows, atom_n_ao(b)) and starts at
361!> atom_value_offsets(b) in ao_values.
362!> \param bs_env ...
363!> \param iatom ...
364!> \param cluster_atoms ...
365!> \param cluster_points ...
366!> \param ao_point_indices Cluster-point index for every stored point row.
367!> \param atom_point_offsets First stored point row for each atom, followed by the final bound.
368!> \param atom_value_offsets First AO value for each atom, followed by the final bound.
369!> \param atom_n_ao Number of AO functions for each atom.
370!> \param ao_values Compact atom-local values ϕ_μ(r_l).
371! **************************************************************************************************
372 SUBROUTINE evaluate_cluster_ao_values(bs_env, iatom, cluster_atoms, cluster_points, &
373 ao_point_indices, atom_point_offsets, atom_value_offsets, &
374 atom_n_ao, ao_values)
375 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
376 INTEGER, INTENT(IN) :: iatom, cluster_atoms(:)
377 REAL(kind=dp), INTENT(IN) :: cluster_points(:, :)
378 INTEGER, ALLOCATABLE, INTENT(OUT) :: ao_point_indices(:), &
379 atom_point_offsets(:), &
380 atom_value_offsets(:), atom_n_ao(:)
381 REAL(kind=dp), ALLOCATABLE, INTENT(OUT) :: ao_values(:)
382
383 INTEGER :: cluster_iatom, first_point, first_value, i_cluster_atom, igrid_point, ikind, &
384 last_point, last_value, nactive, npoints
385 INTEGER, ALLOCATABLE :: point_indices(:)
386 LOGICAL, ALLOCATABLE :: active(:)
387 REAL(kind=dp), ALLOCATABLE :: absolute_points(:, :), &
388 atom_points(:, :), atom_values(:, :)
389 TYPE(gto_basis_set_type), POINTER :: ao
390
391 npoints = SIZE(cluster_points, 2)
392 ALLOCATE (absolute_points(3, npoints))
393 absolute_points(:, :) = cluster_points + &
394 spread(bs_env%ri_rs%particle_set(iatom)%r, 2, npoints)
395 ALLOCATE (point_indices(npoints))
396 point_indices(:) = [(igrid_point, igrid_point=1, npoints)]
397 ALLOCATE (active(npoints), atom_n_ao(SIZE(cluster_atoms)), &
398 atom_point_offsets(SIZE(cluster_atoms) + 1), &
399 atom_value_offsets(SIZE(cluster_atoms) + 1))
400
401 atom_point_offsets(1) = 1
402 atom_value_offsets(1) = 1
403 DO i_cluster_atom = 1, SIZE(cluster_atoms)
404 cluster_iatom = cluster_atoms(i_cluster_atom)
405 ikind = bs_env%ri_rs%particle_set(cluster_iatom)%atomic_kind%kind_number
406 ao => bs_env%basis_set_AO(ikind)%gto_basis_set
407 cpassert(ao%kind_radius > 0.0_dp)
408 active(:) = sum((absolute_points - &
409 spread(bs_env%ri_rs%particle_set(cluster_iatom)%r, 2, npoints))**2, dim=1) &
410 <= ao%kind_radius**2
411 nactive = count(active)
412 atom_n_ao(i_cluster_atom) = ao%nsgf
413 atom_point_offsets(i_cluster_atom + 1) = atom_point_offsets(i_cluster_atom) + nactive
414 atom_value_offsets(i_cluster_atom + 1) = atom_value_offsets(i_cluster_atom) + &
415 nactive*atom_n_ao(i_cluster_atom)
416 END DO
417
418 ALLOCATE (ao_point_indices(atom_point_offsets(SIZE(cluster_atoms) + 1) - 1), &
419 ao_values(atom_value_offsets(SIZE(cluster_atoms) + 1) - 1))
420 DO i_cluster_atom = 1, SIZE(cluster_atoms)
421 cluster_iatom = cluster_atoms(i_cluster_atom)
422 ikind = bs_env%ri_rs%particle_set(cluster_iatom)%atomic_kind%kind_number
423 ao => bs_env%basis_set_AO(ikind)%gto_basis_set
424 active(:) = sum((absolute_points - &
425 spread(bs_env%ri_rs%particle_set(cluster_iatom)%r, 2, npoints))**2, dim=1) &
426 <= ao%kind_radius**2
427 first_point = atom_point_offsets(i_cluster_atom)
428 last_point = atom_point_offsets(i_cluster_atom + 1) - 1
429 first_value = atom_value_offsets(i_cluster_atom)
430 last_value = atom_value_offsets(i_cluster_atom + 1) - 1
431 nactive = last_point - first_point + 1
432 ao_point_indices(first_point:last_point) = pack(point_indices, active)
433 ALLOCATE (atom_points(3, nactive), atom_values(nactive, ao%nsgf))
434 atom_points(:, :) = reshape(pack(absolute_points, spread(active, 1, 3)), [3, nactive])
435 atom_values(:, :) = 0.0_dp
436 CALL evaluate_ao_basis_on_points(atom_values, atom_points, ao, &
437 bs_env%ri_rs%particle_set(cluster_iatom)%r, bs_env%ri_rs%cell)
438 ao_values(first_value:last_value) = reshape(atom_values, [nactive*ao%nsgf])
439 DEALLOCATE (atom_points, atom_values)
440 END DO
441 END SUBROUTINE evaluate_cluster_ao_values
442
443! **************************************************************************************************
444!> \brief Apply the Cholesky selection of Eqs. (7)-(10) and retain selected points in V_A.
445!> \param ao_point_indices Cluster-point index for every stored point row.
446!> \param atom_point_offsets First stored point row for each atom, followed by the final bound.
447!> \param atom_value_offsets First AO value for each atom, followed by the final bound.
448!> \param atom_n_ao Number of AO functions for each atom.
449!> \param ao_values Compact atom-local values ϕ_μ(r_l).
450!> \param npoints Number of points in the cluster Lebedev grids.
451!> \param inside_voronoi True for points inside the molecular Voronoi cell V_A.
452!> \param selected_indices Selected indices in V_A; zero denotes rank exhaustion.
453! **************************************************************************************************
454 SUBROUTINE select_cholesky_grid_points(ao_point_indices, atom_point_offsets, atom_value_offsets, &
455 atom_n_ao, ao_values, npoints, inside_voronoi, &
456 selected_indices)
457 INTEGER, INTENT(IN) :: ao_point_indices(:), &
458 atom_point_offsets(:), &
459 atom_value_offsets(:), atom_n_ao(:)
460 REAL(kind=dp), CONTIGUOUS, INTENT(IN), TARGET :: ao_values(:)
461 INTEGER, INTENT(IN) :: npoints
462 LOGICAL, INTENT(IN) :: inside_voronoi(npoints)
463 INTEGER, INTENT(OUT) :: selected_indices(:)
464
465 INTEGER :: capacity, first_point, first_value, iblock, igrid_point, irow, last_point, &
466 last_value, n_ao, new_capacity, nretained, nrows, nselected, selected_point
467 REAL(kind=dp) :: selected_diagonal
468 REAL(kind=dp), ALLOCATABLE :: diagonal(:), factor(:, :), grown(:, :), initial_diagonal(:), &
469 selected_ao_values(:), selected_factor_values(:), selection_matrix_column(:), values(:)
470 REAL(kind=dp), POINTER :: atom_ao_values(:, :)
471
472 ALLOCATE (diagonal(npoints), initial_diagonal(npoints), &
473 selection_matrix_column(npoints), values(npoints))
474
475 ! d_l^(0) = D_ll = [Σ_μ ϕ_μ(r_l)^2]^2.
476 diagonal(:) = 0.0_dp
477 DO iblock = 1, SIZE(atom_n_ao)
478 first_point = atom_point_offsets(iblock)
479 last_point = atom_point_offsets(iblock + 1) - 1
480 first_value = atom_value_offsets(iblock)
481 last_value = atom_value_offsets(iblock + 1) - 1
482 nrows = last_point - first_point + 1
483 atom_ao_values(1:nrows, 1:atom_n_ao(iblock)) => ao_values(first_value:last_value)
484 DO irow = 1, nrows
485 igrid_point = ao_point_indices(first_point + irow - 1)
486 diagonal(igrid_point) = diagonal(igrid_point) + sum(atom_ao_values(irow, :)**2)
487 END DO
488 END DO
489 diagonal(:) = diagonal**2
490 initial_diagonal(:) = diagonal
491
492 capacity = min(32, npoints)
493 ALLOCATE (factor(npoints, capacity), selected_ao_values(maxval(atom_n_ao)), &
494 selected_factor_values(npoints))
495 selected_indices(:) = 0
496 nselected = 0
497 nretained = 0
498 DO WHILE (nretained < SIZE(selected_indices) .AND. nselected < npoints)
499 selected_point = maxloc(diagonal, dim=1)
500 selected_diagonal = diagonal(selected_point)
501 IF (selected_diagonal <= 0.0_dp) EXIT
502
503 IF (nselected == capacity) THEN
504 new_capacity = min(npoints, capacity + max(32, capacity/2))
505 ALLOCATE (grown(npoints, new_capacity))
506 grown(:, :capacity) = factor(:, :capacity)
507 CALL move_alloc(grown, factor)
508 capacity = new_capacity
509 END IF
510
511 ! Form D_lq from atom-local AO products without storing the dense D_ll' matrix.
512 selection_matrix_column(:) = 0.0_dp
513 DO iblock = 1, SIZE(atom_n_ao)
514 first_point = atom_point_offsets(iblock)
515 last_point = atom_point_offsets(iblock + 1) - 1
516 nrows = last_point - first_point + 1
517 IF (nrows == 0) cycle
518 irow = locate(ao_point_indices(first_point:last_point), selected_point)
519 IF (irow == 0) cycle
520 first_value = atom_value_offsets(iblock)
521 last_value = atom_value_offsets(iblock + 1) - 1
522 n_ao = atom_n_ao(iblock)
523 atom_ao_values(1:nrows, 1:n_ao) => ao_values(first_value:last_value)
524 selected_ao_values(:n_ao) = atom_ao_values(irow, :)
525 CALL dgemv('N', nrows, n_ao, 1.0_dp, atom_ao_values, nrows, &
526 selected_ao_values, 1, 0.0_dp, values, 1)
527 DO irow = 1, nrows
528 igrid_point = ao_point_indices(first_point + irow - 1)
529 selection_matrix_column(igrid_point) = &
530 selection_matrix_column(igrid_point) + values(irow)
531 END DO
532 END DO
533 selection_matrix_column(:) = selection_matrix_column**2
534
535 ! L_lk = [D_lq_k - sum_(j<k) L_lj L_q_kj]/sqrt(d_q_k^(k-1)).
536 IF (nselected > 0) THEN
537 selected_factor_values(:nselected) = factor(selected_point, :nselected)
538 CALL dgemv('N', npoints, nselected, -1.0_dp, factor, npoints, &
539 selected_factor_values, 1, 1.0_dp, selection_matrix_column, 1)
540 END IF
541 nselected = nselected + 1
542 factor(:, nselected) = selection_matrix_column/sqrt(selected_diagonal)
543 diagonal(:) = max(0.0_dp, diagonal - factor(:, nselected)**2)
544 WHERE (diagonal <= 64.0_dp*epsilon(1.0_dp)*initial_diagonal) diagonal = 0.0_dp
545 diagonal(selected_point) = 0.0_dp
546
547 IF (inside_voronoi(selected_point)) THEN
548 nretained = nretained + 1
549 selected_indices(nretained) = selected_point
550 END IF
551 END DO
552 END SUBROUTINE select_cholesky_grid_points
553
554! **************************************************************************************************
555!> \brief Communicate the independently constructed atom grids after Eqs. (5)-(12).
556!> \param bs_env ...
557! **************************************************************************************************
558 SUBROUTINE broadcast_ri_rs_grids(bs_env)
559 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
560
561 INTEGER :: atom_mpi_rank, iatom
562
563 DO iatom = 1, bs_env%n_atom
564 atom_mpi_rank = mod(iatom - 1, bs_env%para_env%num_pe)
565 CALL bs_env%para_env%bcast(bs_env%ri_rs%atomic_grids(iatom)%raw_points, atom_mpi_rank)
566 END DO
567 END SUBROUTINE broadcast_ri_rs_grids
568
Initialize atom-owned RI-RS grids by Cholesky selection from Lebedev grids.
subroutine, public initialize_ri_rs_grid(bs_env)
Construct the initial RI-RS grid of every atom.
Common setup operations used by the periodic and non-periodic GW RI-RS implementations.
subroutine, public get_rirs_cluster_atoms(particle_set, cell, icenter_atom, radius, atom_indices)
Form C_A from nuclei within the specified radius, in global atom order.
subroutine, public filter_grid_to_voronoi(points, icenter_atom, particle_set, mask, atom_indices)
Retain source-grid points inside the Voronoi volume of one atom.
subroutine, public evaluate_ao_basis_on_points(phi, grid_points, basis, source_position, cell, dphi, cutoff_squared)
Evaluate one explicitly supplied contracted Gaussian basis on Cartesian points.
collects all constants needed in input so that they can be used without circular dependencies
integer, parameter, public do_gapw_log
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
integer, parameter, public default_string_length
Definition kinds.F:57
Generation of the spherical Lebedev grids. All Lebedev grids were generated with a precision of at le...
Definition lebedev.F:57
type(oh_grid), dimension(nlg), target, public lebedev_grid
Definition lebedev.F:85
integer function, public get_number_of_lebedev_grid(l, n)
Get the number of the Lebedev grid, which has the requested angular momentum quantnum number l or siz...
Definition lebedev.F:114
subroutine, public deallocate_grid_atom(grid_atom)
Deallocate a Gaussian-type orbital (GTO) basis set data set.
subroutine, public allocate_grid_atom(grid_atom)
Initialize components of the grid_atom_type structure.
subroutine, public create_grid_atom(grid_atom, nr, na, llmax, ll, quadrature)
...
All kind of helpful little routines.
Definition util.F:14
pure integer function, public locate(array, x)
Purpose: Given an array array(1:n), and given a value x, a value x_index is returned which is the ind...
Definition util.F:61