(git:6344619)
Loading...
Searching...
No Matches
ri_rs_grid_optimization.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 Optimize automatically initialized atom-centred RI-RS grids.
10!> \par History
11!> 09.2026 created [Jan Wilhelm]
12! **************************************************************************************************
15 USE cell_types, ONLY: cell_type
16 USE cp_lbfgs, ONLY: setulb
24 USE kinds, ONLY: dp
25 USE machine, ONLY: m_flush,&
37#include "./base/base_uses.f90"
38
39 IMPLICIT NONE
40 PRIVATE
41
42 PUBLIC :: optimize_ri_rs_grid
43
44 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'ri_rs_grid_optimization'
45
46 TYPE, PRIVATE :: local_cluster_3c_integrals_type
47 INTEGER, ALLOCATABLE :: atom_indices(:)
48 REAL(KIND=dp), ALLOCATABLE :: int_3c(:, :, :)
49 END TYPE local_cluster_3c_integrals_type
50
51CONTAINS
52
53! **************************************************************************************************
54!> \brief Initialize Lebedev grids and subsequently optimize their grid-point coordinates.
55!> \param bs_env ...
56! **************************************************************************************************
57 SUBROUTINE optimize_ri_rs_grid(bs_env)
58 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
59
60 CHARACTER(len=*), PARAMETER :: routinen = 'optimize_ri_rs_grid'
61
62 INTEGER :: handle
63 TYPE(local_cluster_3c_integrals_type), ALLOCATABLE :: cluster_3c_int(:)
64
65 CALL timeset(routinen, handle)
66
67 ! Validate all state and user input before the initialization consumes them.
68 CALL validate_ri_rs_grid_optimization_input(bs_env)
69
70 ! setup Lebedev initial grid inside Voronoi volume, select points with Cholesky decomp.
71 CALL initialize_ri_rs_grid(bs_env)
72
73 ! Validate the initialized RI-RS state and report the start of the optimization.
74 CALL prepare_ri_rs_grid_optimization(bs_env)
75
76 ! C_A = {B: |R_A - R_B| < R_cut}; store (μν|P) for μ, ν, and P centered in C_A.
77 CALL build_cluster_3c_int(bs_env, cluster_3c_int)
78
79 ! E_A = Σ_{μνP∈C_A} [(μν|P) - Σ_{l∈G_A} ϕ_μ(r_l) ϕ_ν(r_l) Z_lP^(A)]².
80 ! Minimize E_loc(norm) = N_atom^(-1) Σ_A [E_A / Σ_{μνP∈C_A} |(μν|P)|²].
81 CALL optimize_grid_coordinates(bs_env, cluster_3c_int)
82
83 CALL timestop(handle)
84
85 END SUBROUTINE optimize_ri_rs_grid
86
87! **************************************************************************************************
88!> \brief Validate state and user input needed to initialize and optimize RI-RS grids.
89!> \param bs_env ...
90! **************************************************************************************************
91 SUBROUTINE validate_ri_rs_grid_optimization_input(bs_env)
92 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
93
94 INTEGER :: iatom, ikind
95
96 cpassert(ASSOCIATED(bs_env%ri_rs%cell))
97 cpassert(ASSOCIATED(bs_env%ri_rs%particle_set))
98 cpassert(ASSOCIATED(bs_env%para_env))
99 cpassert(ALLOCATED(bs_env%basis_set_AO))
100 cpassert(ALLOCATED(bs_env%basis_set_RI))
101 cpassert(ALLOCATED(bs_env%sizes_AO))
102 cpassert(SIZE(bs_env%ri_rs%particle_set) == bs_env%n_atom)
103 cpassert(SIZE(bs_env%sizes_AO) == bs_env%n_atom)
104 cpassert(SIZE(bs_env%basis_set_AO) == SIZE(bs_env%basis_set_RI))
105
106 IF (bs_env%ri_rs%grid_opt%rs_ao_ratio <= 0.0_dp) THEN
107 cpabort("GRID_OPTIMIZATION%RS_AO_RATIO must be positive")
108 END IF
109 IF (bs_env%ri_rs%grid_opt%cutoff_atomic_cluster <= 0.0_dp) THEN
110 cpabort("GRID_OPTIMIZATION%CUTOFF_ATOMIC_CLUSTER must be positive")
111 END IF
112 IF (bs_env%ri_rs%grid_opt%max_iter < 1) THEN
113 cpabort("GRID_OPTIMIZATION%MAX_ITER must be positive")
114 END IF
115 IF (bs_env%ri_rs%tikhonov < 0.0_dp) THEN
116 cpabort("RI_RS%TIKHONOV must not be negative")
117 END IF
118
119 DO iatom = 1, bs_env%n_atom
120 ikind = bs_env%ri_rs%particle_set(iatom)%atomic_kind%kind_number
121 cpassert(ikind >= 1 .AND. ikind <= SIZE(bs_env%basis_set_AO))
122 cpassert(ASSOCIATED(bs_env%basis_set_AO(ikind)%gto_basis_set))
123 cpassert(ASSOCIATED(bs_env%basis_set_RI(ikind)%gto_basis_set))
124 IF (bs_env%sizes_AO(iatom) < 1 .OR. &
125 bs_env%basis_set_RI(ikind)%gto_basis_set%nsgf < 1) THEN
126 cpabort("Every atom in GRID_OPTIMIZATION needs ORB and RI_AUX functions")
127 END IF
128 END DO
129 END SUBROUTINE validate_ri_rs_grid_optimization_input
130
131! **************************************************************************************************
132!> \brief Validate initialized RI-RS state and announce the grid optimization.
133!> \param bs_env ...
134! **************************************************************************************************
135 SUBROUTINE prepare_ri_rs_grid_optimization(bs_env)
136 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
137
138 INTEGER :: iatom, unit_nr
139
140 cpassert(ALLOCATED(bs_env%ri_rs%atomic_grids))
141 cpassert(SIZE(bs_env%ri_rs%atomic_grids) == bs_env%n_atom)
142
143 DO iatom = 1, bs_env%n_atom
144 cpassert(ALLOCATED(bs_env%ri_rs%atomic_grids(iatom)%raw_points))
145 cpassert(SIZE(bs_env%ri_rs%atomic_grids(iatom)%raw_points, 1) == 3)
146 cpassert(SIZE(bs_env%ri_rs%atomic_grids(iatom)%raw_points, 2) > 0)
147 END DO
148
149 unit_nr = bs_env%unit_nr
150 IF (unit_nr > 0) THEN
151 WRITE (unit_nr, '(T2,A)') 'Started RI-RS grid optimization'
152 CALL m_flush(unit_nr)
153 END IF
154 END SUBROUTINE prepare_ri_rs_grid_optimization
155
156! **************************************************************************************************
157!> \brief Evaluate the normalized local objective E_loc and its gradient for one L-BFGS trial vector.
158!> \param grid_coordinates Flattened atom-relative grid coordinates.
159!> \param atom_coordinate_offsets Starting coordinate offset for each atom.
160!> \param bs_env ...
161!> \param cluster_3c_int Rank-local cluster three-centre integrals.
162!> \param fit_memory_budget Per-rank bytes available for cluster workspaces; zero means unknown.
163!> \param normalized_error Mean normalized squared three-centre-integral error.
164!> \param coordinate_gradient Optional derivative; omitting it requests final error diagnostics only.
165!> \param max_abs_error Largest absolute integral error in diagnostic mode; zero in gradient mode.
166!> \param fit_successful Whether every local-cluster evaluation succeeded.
167! **************************************************************************************************
168 SUBROUTINE grid_objective(grid_coordinates, atom_coordinate_offsets, bs_env, cluster_3c_int, &
169 fit_memory_budget, normalized_error, coordinate_gradient, &
170 max_abs_error, fit_successful)
171 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: grid_coordinates
172 INTEGER, DIMENSION(:), INTENT(IN) :: atom_coordinate_offsets
173 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
174 TYPE(local_cluster_3c_integrals_type), &
175 DIMENSION(:), INTENT(IN) :: cluster_3c_int
176 REAL(kind=dp), INTENT(IN) :: fit_memory_budget
177 REAL(kind=dp), INTENT(OUT) :: normalized_error
178 REAL(kind=dp), DIMENSION(:), INTENT(OUT), OPTIONAL :: coordinate_gradient
179 REAL(kind=dp), INTENT(OUT) :: max_abs_error
180 LOGICAL, INTENT(OUT) :: fit_successful
181
182 INTEGER :: evaluated_cluster_count, icluster, &
183 successful_ranks
184 LOGICAL :: cluster_fit_successful
185 REAL(kind=dp) :: cluster_max_abs_error, &
186 cluster_normalized_error
187 REAL(kind=dp), ALLOCATABLE :: cluster_coordinate_gradient(:, :)
188 TYPE(mp_para_env_type), POINTER :: para_env
189
190 para_env => bs_env%para_env
191 IF (PRESENT(coordinate_gradient)) THEN
192 cpassert(SIZE(coordinate_gradient) == SIZE(grid_coordinates))
193 coordinate_gradient(:) = 0.0_dp
194 END IF
195 CALL unpack_lbfgs_grid_coordinates(grid_coordinates, atom_coordinate_offsets, &
196 bs_env%ri_rs%atomic_grids)
197 normalized_error = 0.0_dp
198 evaluated_cluster_count = 0
199 max_abs_error = 0.0_dp
200 fit_successful = .true.
201 DO icluster = 1, SIZE(cluster_3c_int)
202 IF (PRESENT(coordinate_gradient)) THEN
203 CALL evaluate_local_cluster(cluster_3c_int(icluster), bs_env, fit_memory_budget, cluster_normalized_error, &
204 cluster_max_abs_error, cluster_fit_successful, cluster_coordinate_gradient)
205 ELSE
206 CALL evaluate_local_cluster(cluster_3c_int(icluster), bs_env, fit_memory_budget, cluster_normalized_error, &
207 cluster_max_abs_error, cluster_fit_successful)
208 END IF
209 IF (.NOT. cluster_fit_successful) THEN
210 fit_successful = .false.
211 EXIT
212 END IF
213 normalized_error = normalized_error + cluster_normalized_error
214 IF (PRESENT(coordinate_gradient)) THEN
215 CALL accumulate_atom_gradient(cluster_3c_int(icluster)%atom_indices, &
216 bs_env%ri_rs%atomic_grids, &
217 atom_coordinate_offsets, cluster_coordinate_gradient, &
218 coordinate_gradient)
219 DEALLOCATE (cluster_coordinate_gradient)
220 END IF
221 evaluated_cluster_count = evaluated_cluster_count + 1
222 max_abs_error = &
223 max(max_abs_error, cluster_max_abs_error)
224 END DO
225 successful_ranks = merge(1, 0, fit_successful)
226 CALL para_env%sum(normalized_error)
227 IF (PRESENT(coordinate_gradient)) CALL para_env%sum(coordinate_gradient)
228 CALL para_env%sum(evaluated_cluster_count)
229 CALL para_env%sum(successful_ranks)
230 CALL para_env%max(max_abs_error)
231 fit_successful = evaluated_cluster_count == bs_env%n_atom .AND. &
232 successful_ranks == para_env%num_pe
233 IF (.NOT. fit_successful) THEN
234 normalized_error = huge(normalized_error)
235 IF (PRESENT(coordinate_gradient)) coordinate_gradient(:) = 0.0_dp
236 RETURN
237 END IF
238 ! E_loc(norm) = (1/N_atom) Σ_A [E_A / Σ_{μνP ∈ C_A} |(μν|P)|²].
239 normalized_error = normalized_error/real(bs_env%n_atom, dp)
240 IF (PRESENT(coordinate_gradient)) coordinate_gradient(:) = coordinate_gradient/real(bs_env%n_atom, dp)
241 END SUBROUTINE grid_objective
242
243! **************************************************************************************************
244!> \brief Drive L-BFGS, repeatedly evaluating E_loc and its gradient, and return the best trial.
245!> \param bs_env ...
246!> \param cluster_3c_int Rank-local cluster three-centre integrals.
247! **************************************************************************************************
248 SUBROUTINE optimize_grid_coordinates(bs_env, cluster_3c_int)
249 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
250 TYPE(local_cluster_3c_integrals_type), &
251 DIMENSION(:), INTENT(IN) :: cluster_3c_int
252
253 INTEGER, PARAMETER :: lbfgs_history = 7
254 REAL(kind=dp), PARAMETER :: lbfgs_factr = 0.0_dp, &
255 lbfgs_pgtol = 1.0e-9_dp, &
256 mem_safety = 0.8_dp
257
258 CHARACTER(LEN=60) :: line_search_state, optimizer_task
259 INTEGER :: evaluation_count, handle, iatom, &
260 molecular_outside, npoints, unit_nr
261 INTEGER, ALLOCATABLE :: atom_coordinate_offsets(:), &
262 bound_types(:), integer_workspace(:)
263 INTEGER, DIMENSION(44) :: integer_state
264 LOGICAL :: evaluation_successful, fit_successful, &
265 have_best
266 LOGICAL, ALLOCATABLE :: inside(:)
267 LOGICAL, DIMENSION(4) :: logical_state
268 REAL(kind=dp) :: best_normalized_error, &
269 fit_memory_budget, max_abs_error, &
270 mem_avail_gb, normalized_error, &
271 start_time
272 REAL(kind=dp), ALLOCATABLE :: best_grid_coordinates(:), coordinate_gradient(:), &
273 grid_coordinates(:), lower_bounds(:), points(:, :), upper_bounds(:), workspace(:)
274 REAL(kind=dp), DIMENSION(29) :: real_state
275
276 CALL timeset("rirs_grid_LBFGS", handle)
277 start_time = m_walltime()
278
279 CALL pack_lbfgs_grid_coordinates(bs_env%ri_rs%atomic_grids, grid_coordinates, &
280 atom_coordinate_offsets)
281 cpassert(SIZE(grid_coordinates) > 0)
282
283 ALLOCATE (best_grid_coordinates(SIZE(grid_coordinates)), &
284 coordinate_gradient(SIZE(grid_coordinates)), &
285 lower_bounds(SIZE(grid_coordinates)), &
286 upper_bounds(SIZE(grid_coordinates)), &
287 bound_types(SIZE(grid_coordinates)), &
288 integer_workspace(3*SIZE(grid_coordinates)), &
289 workspace(2*lbfgs_history*SIZE(grid_coordinates) + &
290 5*SIZE(grid_coordinates) + 11*lbfgs_history**2 + 8*lbfgs_history))
291 ! L-BFGS-B ignores lower_bounds and upper_bounds when bound_types is zero.
292 lower_bounds = 0.0_dp
293 upper_bounds = 0.0_dp
294 bound_types = 0
295 optimizer_task = 'START'
296 line_search_state = ''
297 normalized_error = huge(normalized_error)
298 coordinate_gradient = 0.0_dp
299 workspace = 0.0_dp
300 integer_workspace = 0
301 logical_state = .false.
302 integer_state = 0
303 real_state = 0.0_dp
304 evaluation_count = 0
305 have_best = .false.
306 best_normalized_error = huge(best_normalized_error)
307 best_grid_coordinates(:) = grid_coordinates
308
309 ! This query is collective. Take one snapshot after resident integrals and L-BFGS
310 ! workspaces exist, before ranks enter their unequal numbers of local cluster evaluations.
311 ! Use the same safety fraction as the production Z_lP fitting classification.
312 CALL mp_mem_avail_per_rank_gb(bs_env%para_env, mem_avail_gb)
313 ! This budgets all incremental cluster workspaces, including the caller's AO arrays.
314 ! The product cache receives only the remainder after those workspaces are reserved.
315 fit_memory_budget = mem_safety*mem_avail_gb*1.0e9_dp
316 IF (mem_avail_gb <= 0.0_dp .AND. bs_env%unit_nr > 0) THEN
317 cpwarn("RI-RS grid optimization: no meminfo; streaming one RI column")
318 END IF
319
320 DO
321 CALL setulb(SIZE(grid_coordinates), lbfgs_history, grid_coordinates, &
322 lower_bounds, upper_bounds, bound_types, &
323 normalized_error, coordinate_gradient, lbfgs_factr, lbfgs_pgtol, &
324 workspace, integer_workspace, optimizer_task, -1, line_search_state, &
325 logical_state, integer_state, real_state, -1.0_dp)
326 IF (optimizer_task(1:2) == 'FG') THEN
327 IF (evaluation_count >= bs_env%ri_rs%grid_opt%max_iter) EXIT
328 CALL grid_objective(grid_coordinates, atom_coordinate_offsets, bs_env, cluster_3c_int, &
329 fit_memory_budget, normalized_error, coordinate_gradient, &
330 max_abs_error, evaluation_successful)
331 evaluation_count = evaluation_count + 1
332 IF (.NOT. evaluation_successful) EXIT
333 IF (evaluation_count == 1 .AND. bs_env%unit_nr > 0) THEN
334 WRITE (bs_env%unit_nr, '(T2,A,T61,ES20.12)') &
335 'RI-RS grid initial normalized error', normalized_error
336 CALL m_flush(bs_env%unit_nr)
337 END IF
338 IF (.NOT. have_best .OR. normalized_error < best_normalized_error) THEN
339 have_best = .true.
340 best_normalized_error = normalized_error
341 best_grid_coordinates(:) = grid_coordinates
342 END IF
343 ELSE IF (optimizer_task(1:5) == 'NEW_X') THEN
344 cycle
345 ELSE
346 EXIT
347 END IF
348 END DO
349 IF (.NOT. have_best) cpabort("RI-RS grid optimization produced no regularized fit")
350 grid_coordinates(:) = best_grid_coordinates
351 ! grid_objective restores the best coordinates to atomic_grids and evaluates only the
352 ! final errors. No best-fit coefficients are retained across L-BFGS trials.
353 CALL grid_objective(grid_coordinates, atom_coordinate_offsets, bs_env, cluster_3c_int, &
354 fit_memory_budget, normalized_error, max_abs_error=max_abs_error, fit_successful=fit_successful)
355
356 IF (.NOT. fit_successful) cpabort("RI-RS grid optimization produced no regularized fit")
357
358 DO iatom = 1, SIZE(bs_env%ri_rs%atomic_grids)
359 bs_env%ri_rs%atomic_grids(iatom)%npts = &
360 SIZE(bs_env%ri_rs%atomic_grids(iatom)%raw_points, 2)
361 END DO
362 bs_env%ri_rs%Z_lP_exists = .false.
363
364 unit_nr = bs_env%unit_nr
365 IF (unit_nr > 0) THEN
366 npoints = 0
367 molecular_outside = 0
368 DO iatom = 1, SIZE(bs_env%ri_rs%atomic_grids)
369 ALLOCATE (points(3, bs_env%ri_rs%atomic_grids(iatom)%npts), &
370 inside(bs_env%ri_rs%atomic_grids(iatom)%npts))
371 points(:, :) = bs_env%ri_rs%atomic_grids(iatom)%raw_points
372 CALL filter_grid_to_voronoi(points, iatom, bs_env%ri_rs%particle_set, mask=inside)
373 molecular_outside = molecular_outside + count(.NOT. inside)
374 npoints = npoints + SIZE(inside)
375 DEALLOCATE (points, inside)
376 END DO
377 WRITE (unit_nr, '(T2,A,T69,F10.1,A)') &
378 'RI-RS grid optimization completed, execution time:', m_walltime() - start_time, ' s'
379 WRITE (unit_nr, '(T2,A,T72,ES9.1)') 'Normalized 3C error:', normalized_error
380 WRITE (unit_nr, '(T2,A,T72,ES9.1)') 'Maximum absolute 3C error:', max_abs_error
381 WRITE (unit_nr, '(T2,A,T60,I9,A,I9)') &
382 'Optimized points outside molecular Voronoi cells:', molecular_outside, ' / ', npoints
383 FLUSH (unit_nr)
384 END IF
385 CALL timestop(handle)
386 END SUBROUTINE optimize_grid_coordinates
387
388! **************************************************************************************************
389!> \brief Build each C_A and store the upper AO triangle of (μν|P); this routine owns the integral context.
390!> \param bs_env ...
391!> \param cluster_3c_int Rank-local cluster three-centre integrals.
392! **************************************************************************************************
393 SUBROUTINE build_cluster_3c_int(bs_env, cluster_3c_int)
394 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
395 TYPE(local_cluster_3c_integrals_type), &
396 ALLOCATABLE, INTENT(OUT) :: cluster_3c_int(:)
397
398 INTEGER :: handle, i_cluster_atom_j, i_cluster_atom_k, i_cluster_atom_p, iatom, iatom_j, &
399 iatom_k, iatom_p, icenter_atom, icluster_atom, ikind, ilocal_cluster, ip, &
400 n_local_clusters, nao_cluster, nri_cluster, nri_nonzero
401 INTEGER, ALLOCATABLE :: ao_offset(:), ri_offset(:), &
402 sizes_ref_ri(:)
403 LOGICAL :: screened
404 REAL(kind=dp) :: cluster_radius
405 REAL(kind=dp), ALLOCATABLE :: int_3c_nonzero(:, :, :)
406 TYPE(cell_type), POINTER :: cell
407 TYPE(gw_3c_ctx_type) :: integral_context
408 TYPE(gw_3c_ws_type) :: workspace
409 TYPE(mp_para_env_type), POINTER :: para_env
410 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
411
412 CALL timeset("rirs_grid_build_clusters", handle)
413
414 cell => bs_env%ri_rs%cell
415 para_env => bs_env%para_env
416 particle_set => bs_env%ri_rs%particle_set
417 cluster_radius = bs_env%ri_rs%grid_opt%cutoff_atomic_cluster
418 ALLOCATE (sizes_ref_ri(bs_env%n_atom))
419 DO iatom = 1, bs_env%n_atom
420 ikind = particle_set(iatom)%atomic_kind%kind_number
421 sizes_ref_ri(iatom) = bs_env%basis_set_RI(ikind)%gto_basis_set%nsgf
422 END DO
423 n_local_clusters = count([(mod(icenter_atom - 1, para_env%num_pe) == para_env%mepos, &
424 icenter_atom=1, bs_env%n_atom)])
425 ALLOCATE (cluster_3c_int(n_local_clusters), &
426 ao_offset(bs_env%n_atom), ri_offset(bs_env%n_atom))
427 CALL gw_3c_ctx_create(integral_context, bs_env, bs_env%ri_metric, &
428 basis_j=bs_env%basis_set_AO, basis_k=bs_env%basis_set_AO, &
429 basis_i=bs_env%basis_set_RI)
430 CALL gw_3c_ws_create(workspace, integral_context)
431 ilocal_cluster = 0
432 DO icenter_atom = 1, bs_env%n_atom
433 IF (mod(icenter_atom - 1, para_env%num_pe) /= para_env%mepos) cycle
434 ilocal_cluster = ilocal_cluster + 1
435 CALL get_rirs_cluster_atoms(particle_set, cell, icenter_atom, cluster_radius, &
436 cluster_3c_int(ilocal_cluster)%atom_indices)
437 ao_offset = 0
438 ri_offset = 0
439 nao_cluster = 0
440 nri_cluster = 0
441 DO icluster_atom = 1, SIZE(cluster_3c_int(ilocal_cluster)%atom_indices)
442 iatom = cluster_3c_int(ilocal_cluster)%atom_indices(icluster_atom)
443 ao_offset(iatom) = nao_cluster
444 ri_offset(iatom) = nri_cluster
445 nao_cluster = nao_cluster + bs_env%sizes_AO(iatom)
446 nri_cluster = nri_cluster + sizes_ref_ri(iatom)
447 END DO
448 ALLOCATE (cluster_3c_int(ilocal_cluster)%Int_3c(nao_cluster, nao_cluster, nri_cluster), &
449 source=0.0_dp)
450 DO i_cluster_atom_p = 1, SIZE(cluster_3c_int(ilocal_cluster)%atom_indices)
451 iatom_p = cluster_3c_int(ilocal_cluster)%atom_indices(i_cluster_atom_p)
452 DO i_cluster_atom_k = 1, SIZE(cluster_3c_int(ilocal_cluster)%atom_indices)
453 iatom_k = cluster_3c_int(ilocal_cluster)%atom_indices(i_cluster_atom_k)
454 ! The evaluator reads only the upper AO triangle (DSYMM with UPLO='U').
455 ! Global atom ordering also orders the cluster AO blocks; lower blocks stay zero.
456 DO i_cluster_atom_j = 1, i_cluster_atom_k
457 iatom_j = cluster_3c_int(ilocal_cluster)%atom_indices(i_cluster_atom_j)
458 CALL build_3c_integral_block_ctx(cluster_3c_int(ilocal_cluster)%Int_3c, &
459 integral_context, workspace, &
460 atom_j=iatom_j, atom_k=iatom_k, atom_i=iatom_p, &
461 j_offset=ao_offset(iatom_j), &
462 k_offset=ao_offset(iatom_k), &
463 i_offset=ri_offset(iatom_p), screened=screened)
464 END DO
465 END DO
466 END DO
467 ! Zero auxiliary columns contribute neither to the objective nor its derivatives.
468 nri_nonzero = count([(any(cluster_3c_int(ilocal_cluster)%Int_3c(:, :, ip) /= 0.0_dp), &
469 ip=1, nri_cluster)])
470 IF (nri_nonzero < nri_cluster) THEN
471 ALLOCATE (int_3c_nonzero(nao_cluster, nao_cluster, nri_nonzero))
472 nri_nonzero = 0
473 DO ip = 1, nri_cluster
474 IF (.NOT. any(cluster_3c_int(ilocal_cluster)%Int_3c(:, :, ip) /= 0.0_dp)) cycle
475 nri_nonzero = nri_nonzero + 1
476 int_3c_nonzero(:, :, nri_nonzero) = cluster_3c_int(ilocal_cluster)%Int_3c(:, :, ip)
477 END DO
478 CALL move_alloc(int_3c_nonzero, cluster_3c_int(ilocal_cluster)%Int_3c)
479 END IF
480 END DO
481 CALL gw_3c_ws_release(workspace)
482 CALL gw_3c_ctx_release(integral_context)
483 CALL timestop(handle)
484 END SUBROUTINE build_cluster_3c_int
485
486! **************************************************************************************************
487!> \brief Evaluate ϕ_μ(r_l) on G_A and compute the normalized local error E_A and its gradient.
488!> \param cluster_3c_int Local atom indices and exact three-centre integrals.
489!> \param bs_env ...
490!> \param fit_memory_budget Per-rank incremental workspace budget before allocating this cluster.
491!> \param normalized_error Normalized squared three-centre-integral error.
492!> \param max_abs_error Largest absolute integral error in diagnostic mode; zero in gradient mode.
493!> \param fit_successful Whether the regularized fitting equations were solved.
494!> \param coordinate_gradient Optional derivative with respect to the cluster's physical grid coordinates.
495! **************************************************************************************************
496 SUBROUTINE evaluate_local_cluster(cluster_3c_int, bs_env, fit_memory_budget, normalized_error, max_abs_error, &
497 fit_successful, coordinate_gradient)
498 TYPE(local_cluster_3c_integrals_type), INTENT(IN) :: cluster_3c_int
499 TYPE(post_scf_bandstructure_type), POINTER :: bs_env
500 REAL(kind=dp), INTENT(IN) :: fit_memory_budget
501 REAL(kind=dp), INTENT(OUT) :: normalized_error, max_abs_error
502 LOGICAL, INTENT(OUT) :: fit_successful
503 REAL(kind=dp), ALLOCATABLE, INTENT(OUT), OPTIONAL :: coordinate_gradient(:, :)
504
505 INTEGER :: ao_offset, grid_point_offset, iatom, &
506 icartesian, icluster_atom, igrid, &
507 ikind, n_grid_points, nao, nao_atom
508 REAL(kind=dp) :: evaluation_budget
509 REAL(kind=dp), ALLOCATABLE :: ao_gradient(:, :), &
510 dphi_alpha_l_mu(:, :, :), &
511 grid_points(:, :), phi_l_mu(:, :)
512 TYPE(cell_type), POINTER :: cell
513 TYPE(gto_basis_set_type), POINTER :: ao_basis
514 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
515
516 cell => bs_env%ri_rs%cell
517 particle_set => bs_env%ri_rs%particle_set
518 nao = SIZE(cluster_3c_int%Int_3c, 1)
519 n_grid_points = 0
520 DO icluster_atom = 1, SIZE(cluster_3c_int%atom_indices)
521 iatom = cluster_3c_int%atom_indices(icluster_atom)
522 n_grid_points = n_grid_points + SIZE(bs_env%ri_rs%atomic_grids(iatom)%raw_points, 2)
523 END DO
524 evaluation_budget = 0.0_dp
525 IF (PRESENT(coordinate_gradient) .AND. fit_memory_budget > 0.0_dp) THEN
526 ! phi, dphi and its adjoint use 5*g*a doubles; points and coordinate gradients use 6*g.
527 evaluation_budget = fit_memory_budget - &
528 8.0_dp*real(n_grid_points, dp)*(5.0_dp*real(nao, dp) + 6.0_dp)
529 IF (evaluation_budget <= 0.0_dp) THEN
530 cpabort("RI-RS cluster AO workspace exceeds memory budget")
531 END IF
532 END IF
533 ALLOCATE (grid_points(3, n_grid_points), phi_l_mu(n_grid_points, nao))
534 IF (PRESENT(coordinate_gradient)) THEN
535 ALLOCATE (dphi_alpha_l_mu(3, n_grid_points, nao), ao_gradient(n_grid_points, nao), &
536 coordinate_gradient(3, n_grid_points))
537 dphi_alpha_l_mu = 0.0_dp
538 coordinate_gradient(:, :) = 0.0_dp
539 END IF
540 phi_l_mu = 0.0_dp
541 grid_point_offset = 0
542 DO icluster_atom = 1, SIZE(cluster_3c_int%atom_indices)
543 iatom = cluster_3c_int%atom_indices(icluster_atom)
544 grid_points(:, grid_point_offset + 1:grid_point_offset + &
545 SIZE(bs_env%ri_rs%atomic_grids(iatom)%raw_points, 2)) = &
546 spread(particle_set(iatom)%r, 2, &
547 SIZE(bs_env%ri_rs%atomic_grids(iatom)%raw_points, 2)) + &
548 bs_env%ri_rs%atomic_grids(iatom)%raw_points
549 grid_point_offset = grid_point_offset + SIZE(bs_env%ri_rs%atomic_grids(iatom)%raw_points, 2)
550 END DO
551 ao_offset = 0
552 DO icluster_atom = 1, SIZE(cluster_3c_int%atom_indices)
553 iatom = cluster_3c_int%atom_indices(icluster_atom)
554 ikind = particle_set(iatom)%atomic_kind%kind_number
555 ao_basis => bs_env%basis_set_AO(ikind)%gto_basis_set
556 nao_atom = ao_basis%nsgf
557 IF (PRESENT(coordinate_gradient)) THEN
558 CALL evaluate_ao_basis_on_points(phi_l_mu(:, ao_offset + 1:ao_offset + nao_atom), &
559 grid_points, ao_basis, particle_set(iatom)%r, cell, &
560 dphi=dphi_alpha_l_mu(:, :, ao_offset + 1:ao_offset + nao_atom))
561 ELSE
562 CALL evaluate_ao_basis_on_points(phi_l_mu(:, ao_offset + 1:ao_offset + nao_atom), &
563 grid_points, ao_basis, particle_set(iatom)%r, cell)
564 END IF
565 ao_offset = ao_offset + nao_atom
566 END DO
567 max_abs_error = 0.0_dp
568 IF (PRESENT(coordinate_gradient)) THEN
569 CALL evaluate_ri_rs_fit(phi_l_mu, cluster_3c_int%Int_3c, bs_env%ri_rs%tikhonov, &
570 normalized_error, fit_successful, ao_gradient=ao_gradient, &
571 workspace_budget_bytes=evaluation_budget)
572 IF (.NOT. fit_successful) RETURN
573 DO igrid = 1, n_grid_points
574 DO icartesian = 1, 3
575 coordinate_gradient(icartesian, igrid) = &
576 dot_product(ao_gradient(igrid, :), dphi_alpha_l_mu(icartesian, igrid, :))
577 END DO
578 END DO
579 ELSE
580 CALL evaluate_ri_rs_fit(phi_l_mu, cluster_3c_int%Int_3c, bs_env%ri_rs%tikhonov, &
581 normalized_error, fit_successful, max_abs_error=max_abs_error)
582 END IF
583 END SUBROUTINE evaluate_local_cluster
584
585! **************************************************************************************************
586!> \brief Sum gradient contributions from overlapping C_A into each atom-centred grid coordinate.
587!> \param cluster_atoms Atom indices in the local cluster.
588!> \param grids Atom-centred RI-RS grids.
589!> \param atom_coordinate_offsets Starting coordinate offset for each atom.
590!> \param cluster_coordinate_gradient Gradient for the cluster's physical grid points.
591!> \param coordinate_gradient Global flattened atom-relative gradient to update.
592! **************************************************************************************************
593 SUBROUTINE accumulate_atom_gradient(cluster_atoms, grids, atom_coordinate_offsets, &
594 cluster_coordinate_gradient, coordinate_gradient)
595 INTEGER, DIMENSION(:), INTENT(IN) :: cluster_atoms
596 TYPE(rirs_grid_type), DIMENSION(:), INTENT(IN) :: grids
597 INTEGER, DIMENSION(:), INTENT(IN) :: atom_coordinate_offsets
598 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: cluster_coordinate_gradient
599 REAL(kind=dp), DIMENSION(:), INTENT(INOUT) :: coordinate_gradient
600
601 INTEGER :: grid_point_offset, iatom, icartesian, &
602 icluster_atom, igrid_point
603
604 grid_point_offset = 0
605 DO icluster_atom = 1, SIZE(cluster_atoms)
606 iatom = cluster_atoms(icluster_atom)
607 DO igrid_point = 1, SIZE(grids(iatom)%raw_points, 2)
608 DO icartesian = 1, 3
609 coordinate_gradient(atom_coordinate_offsets(iatom) + 3*(igrid_point - 1) + icartesian) = &
610 coordinate_gradient(atom_coordinate_offsets(iatom) + 3*(igrid_point - 1) + icartesian) + &
611 cluster_coordinate_gradient(icartesian, grid_point_offset + igrid_point)
612 END DO
613 END DO
614 grid_point_offset = grid_point_offset + SIZE(grids(iatom)%raw_points, 2)
615 END DO
616 END SUBROUTINE accumulate_atom_gradient
617
618! **************************************************************************************************
619!> \brief Pack all atom-centred grids into the single Cartesian vector required by L-BFGS.
620!> \param grids Atom-centred RI-RS grids.
621!> \param grid_coordinates Flattened atom-relative grid coordinates.
622!> \param atom_coordinate_offsets Starting coordinate offset for each atom.
623!>
624!> SIZE(grid_coordinates) = 3 Σ_A N_grid,A. Coordinates are ordered Cartesian component first,
625!> then grid point, then atom, matching the column-major layout of raw_points(3,N_grid,A).
626! **************************************************************************************************
627 SUBROUTINE pack_lbfgs_grid_coordinates(grids, grid_coordinates, atom_coordinate_offsets)
628 TYPE(rirs_grid_type), DIMENSION(:), INTENT(IN) :: grids
629 REAL(kind=dp), ALLOCATABLE, INTENT(OUT) :: grid_coordinates(:)
630 INTEGER, ALLOCATABLE, INTENT(OUT) :: atom_coordinate_offsets(:)
631
632 INTEGER :: atom_coordinate_count, iatom, &
633 n_grid_coordinates
634
635 ALLOCATE (atom_coordinate_offsets(SIZE(grids)))
636 n_grid_coordinates = 0
637 DO iatom = 1, SIZE(grids)
638 atom_coordinate_offsets(iatom) = n_grid_coordinates
639 n_grid_coordinates = n_grid_coordinates + SIZE(grids(iatom)%raw_points)
640 END DO
641 ALLOCATE (grid_coordinates(n_grid_coordinates))
642
643 DO iatom = 1, SIZE(grids)
644 atom_coordinate_count = SIZE(grids(iatom)%raw_points)
645 grid_coordinates(atom_coordinate_offsets(iatom) + 1: &
646 atom_coordinate_offsets(iatom) + atom_coordinate_count) = &
647 reshape(grids(iatom)%raw_points, [atom_coordinate_count])
648 END DO
649 END SUBROUTINE pack_lbfgs_grid_coordinates
650
651! **************************************************************************************************
652!> \brief Restore the optimized Cartesian vector to the persistent atom-centred RI-RS grids.
653!> \param grid_coordinates Flattened atom-relative grid coordinates.
654!> \param atom_coordinate_offsets Starting coordinate offset for each atom.
655!> \param grids Atom-centred RI-RS grids to update.
656! **************************************************************************************************
657 SUBROUTINE unpack_lbfgs_grid_coordinates(grid_coordinates, atom_coordinate_offsets, grids)
658 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: grid_coordinates
659 INTEGER, DIMENSION(:), INTENT(IN) :: atom_coordinate_offsets
660 TYPE(rirs_grid_type), DIMENSION(:), INTENT(INOUT) :: grids
661
662 INTEGER :: atom_coordinate_count, iatom
663
664 cpassert(SIZE(atom_coordinate_offsets) == SIZE(grids))
665 DO iatom = 1, SIZE(grids)
666 atom_coordinate_count = SIZE(grids(iatom)%raw_points)
667 grids(iatom)%raw_points(:, :) = reshape( &
668 grid_coordinates(atom_coordinate_offsets(iatom) + 1: &
669 atom_coordinate_offsets(iatom) + &
670 atom_coordinate_count), &
671 shape(grids(iatom)%raw_points))
672 END DO
673 END SUBROUTINE unpack_lbfgs_grid_coordinates
674
Handles all functions related to the CELL.
Definition cell_types.F:15
LBFGS-B routine (version 3.0, April 25, 2011).
Definition cp_lbfgs.F:19
subroutine, public setulb(n, m, x, lower_bound, upper_bound, nbd, f, g, factr, pgtol, wa, iwa, task, iprint, csave, lsave, isave, dsave, trust_radius, spgr, iwunit)
This subroutine partitions the working arrays wa and iwa, and then uses the limited memory BFGS metho...
Definition cp_lbfgs.F:188
Utility method to build 3-center integrals for small cell GW.
subroutine, public gw_3c_ws_create(ws, ctx)
Creates a per-thread 3c workspace: libint object + LIBXSMM contraction buffers.
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_ctx_release(ctx)
Releases the shared 3c-integral context.
subroutine, public gw_3c_ws_release(ws)
Releases a per-thread 3c workspace.
subroutine, public gw_3c_ctx_create(ctx, bs_env, potential_parameter, basis_j, basis_k, basis_i)
Build the shared 3c-integral context from the band-structure environment and explicitly supplied pote...
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
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:127
real(kind=dp) function, public m_walltime()
returns time from a real-time clock, protected against rolling early/easily
Definition machine.F:144
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.
Define the data structure for the particle information.
Shared numerical operations for computing the RI-RS matrix Z_lP.
subroutine, public evaluate_ri_rs_fit(phi, int_3c, tikhonov, normalized_error, fit_successful, ao_gradient, max_abs_error, workspace_budget_bytes)
Evaluate the normalized cluster residual and its exact derivative in AO space.
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.
Optimize automatically initialized atom-centred RI-RS grids.
subroutine, public optimize_ri_rs_grid(bs_env)
Initialize Lebedev grids and subsequently optimize their grid-point coordinates.
Shared real-space resolution-of-identity grid and matrix data.
Definition ri_rs_types.F:11
Geometry and AO evaluation for real-space resolution of the identity.
Definition ri_rs_utils.F:11
subroutine, public evaluate_ao_basis_on_points(phi, grid_points, basis, source_position, cell, dphi, cutoff_squared, periodic_images)
Evaluate one explicitly supplied contracted Gaussian basis on Cartesian points.
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 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.
Type defining parameters related to the simulation cell.
Definition cell_types.F:60
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