(git:6344619)
Loading...
Searching...
No Matches
ri_rs_fitting.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 Shared numerical operations for computing the RI-RS matrix Z_lP.
10!> \par History
11!> 09.2026 created Jan Wilhelm
12! **************************************************************************************************
15 USE cp_dbcsr_api, ONLY: dbcsr_put_block,&
22 USE cp_fm_types, ONLY: cp_fm_create,&
28 USE kinds, ONLY: dp
30#include "./base/base_uses.f90"
31
32 IMPLICIT NONE
33 PRIVATE
34
35 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'ri_rs_fitting'
36 REAL(KIND=dp), PARAMETER, PRIVATE :: jacobi_floor = 1.0e-16_dp
37
38 PUBLIC :: evaluate_ri_rs_fit, &
41
42CONTAINS
43
44! **************************************************************************************************
45!> \brief Computes the regularized RI-RS fit from an AO collocation matrix and a replicated RHS.
46!>
47!> D_ll' = [Σ_μ ϕ_μ(r_l) ϕ_μ(r_l')]²,
48!> d_l = 1/sqrt(max(D_ll, jacobi_floor)),
49!> D'_ll' = d_l D_ll' d_l' + λ δ_ll',
50!> Σ_l' D'_ll' Z'_l'P = d_l d_lP,
51!> Z_lP = d_l Z'_lP.
52!>
53!> The RI-RS least-squares construction and balancing are described by Duchemin and
54!> Blase, arXiv:1902.00066, Sec. II.3. Here jacobi_floor bounds D_ll from below in
55!> both the LAPACK and ScaLAPACK paths. The regularization λ is added after Jacobi
56!> scaling; in the unscaled system its diagonal contribution is λ/d_l².
57!> \param phi_local AO values on the fitting grid, replicated within the solve subgroup
58!> \param d_lp replicated RHS d_lP on entry and fitted coefficients Z_lP on return
59!> \param tikhonov regularization parameter λ in the Jacobi-scaled space
60!> \param para_env_sub optional subgroup communicator for the distributed solve
61!> \param blacs_env_sub optional BLACS context, supplied together with para_env_sub
62! **************************************************************************************************
63 SUBROUTINE solve_ri_rs_fit(phi_local, d_lp, tikhonov, para_env_sub, blacs_env_sub)
64 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: phi_local
65 REAL(kind=dp), DIMENSION(:, :), INTENT(INOUT) :: d_lp
66 REAL(kind=dp), INTENT(IN) :: tikhonov
67 TYPE(mp_para_env_type), OPTIONAL, POINTER :: para_env_sub
68 TYPE(cp_blacs_env_type), OPTIONAL, POINTER :: blacs_env_sub
69
70 CHARACTER(LEN=*), PARAMETER :: routinen = 'solve_ri_rs_fit'
71
72 INTEGER :: handle, handle2, i, info, n_grid, n_rhs
73 REAL(kind=dp), ALLOCATABLE :: gram(:, :), jacobi_scale(:)
74
75 CALL timeset(routinen, handle)
76
77 n_grid = SIZE(phi_local, 1)
78 n_rhs = SIZE(d_lp, 2)
79 cpassert(SIZE(d_lp, 1) == n_grid)
80 cpassert(PRESENT(para_env_sub) .EQV. PRESENT(blacs_env_sub))
81 IF (PRESENT(para_env_sub)) THEN
82 ! Only the row norms are replicated; the distributed solve builds its own Gram tiles.
83 ALLOCATE (jacobi_scale(n_grid))
84 !$OMP PARALLEL DO DEFAULT(NONE) &
85 !$OMP SHARED(n_grid, phi_local, jacobi_scale) &
86 !$OMP PRIVATE(i) SCHEDULE(STATIC)
87 DO i = 1, n_grid
88 jacobi_scale(i) = 1.0_dp/max(dot_product(phi_local(i, :), phi_local(i, :)), sqrt(jacobi_floor))
89 END DO
90 !$OMP END PARALLEL DO
91 ELSE
92 CALL factor_ri_rs_fit(phi_local, tikhonov, gram, jacobi_scale, info)
93 IF (info /= 0) cpabort('RI-RS Cholesky factorization failed')
94 END IF
95
96 ! Pre-scale the right-hand side: d_lP <- d_l d_lP.
97 CALL scale_rows_by_diag(d_lp, jacobi_scale)
98 IF (PRESENT(para_env_sub)) THEN
99 CALL solve_d_lp_distributed(phi_local, jacobi_scale, d_lp, tikhonov, &
100 para_env_sub, blacs_env_sub)
101 ELSE
102 ! Solve Σ_l' D'_ll' Z'_l'P = d_l d_lP, overwriting d_lp with Z'_lP.
103 CALL timeset(routinen//'_dpotrs', handle2)
104 CALL dpotrs('L', n_grid, n_rhs, gram, n_grid, d_lp, n_grid, info)
105 CALL timestop(handle2)
106 IF (info /= 0) cpabort('RI-RS Cholesky solve failed')
107 END IF
108 ! Undo the conditioning on the solution: Z_lP = d_l Z'_lP.
109 CALL scale_rows_by_diag(d_lp, jacobi_scale)
110
111 CALL timestop(handle)
112
113 END SUBROUTINE solve_ri_rs_fit
114
115! **************************************************************************************************
116!> \brief Build and factor the Jacobi-scaled squared AO Gram matrix, K = d D d + λ I.
117!> The lower triangle is used throughout; the factor is retained for subsequent solves.
118!> See solve_ri_rs_fit for the RI-RS balancing convention and reference.
119!> \param phi AO values indexed (grid point, AO)
120!> \param tikhonov regularization in the Jacobi-scaled space
121!> \param factor lower Cholesky factor of K on successful return
122!> \param jacobi_scale pair-feature scales d_l
123!> \param info LAPACK factorization status; the caller chooses how to handle failure
124!> \param overlap optional lower triangle of the scaled AO overlap V V^T, V_l = sqrt(d_l) phi_l
125! **************************************************************************************************
126 SUBROUTINE factor_ri_rs_fit(phi, tikhonov, factor, jacobi_scale, info, overlap)
127 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: phi
128 REAL(kind=dp), INTENT(IN) :: tikhonov
129 REAL(kind=dp), ALLOCATABLE, INTENT(OUT) :: factor(:, :), jacobi_scale(:)
130 INTEGER, INTENT(OUT) :: info
131 REAL(kind=dp), ALLOCATABLE, INTENT(OUT), OPTIONAL :: overlap(:, :)
132
133 CHARACTER(LEN=*), PARAMETER :: routinen = 'factor_ri_rs_fit'
134
135 INTEGER :: handle, handle2, i, j, n_grid
136
137 CALL timeset(routinen, handle)
138 n_grid = SIZE(phi, 1)
139 ALLOCATE (factor(n_grid, n_grid), jacobi_scale(n_grid))
140 ! First form S_ll' = Σ_μ phi_lμ phi_l'μ; D_ll' = S_ll'^2.
141 CALL timeset(routinen//'_dsyrk', handle2)
142 CALL dsyrk('L', 'N', n_grid, SIZE(phi, 2), 1.0_dp, phi, n_grid, 0.0_dp, factor, n_grid)
143 CALL timestop(handle2)
144 DO i = 1, n_grid
145 jacobi_scale(i) = 1.0_dp/max(factor(i, i), sqrt(jacobi_floor))
146 END DO
147 IF (PRESENT(overlap)) THEN
148 ALLOCATE (overlap(n_grid, n_grid))
149 DO j = 1, n_grid
150 DO i = j, n_grid
151 overlap(i, j) = factor(i, j)*sqrt(jacobi_scale(i))*sqrt(jacobi_scale(j))
152 END DO
153 END DO
154 END IF
155 ! Add λ after Jacobi scaling; LAPACK needs only the lower triangle.
156 !$OMP PARALLEL DO DEFAULT(NONE) &
157 !$OMP SHARED(n_grid, factor, jacobi_scale, tikhonov) &
158 !$OMP PRIVATE(j, i) SCHEDULE(DYNAMIC)
159 DO j = 1, n_grid
160 DO i = j, n_grid
161 factor(i, j) = factor(i, j)**2*jacobi_scale(i)*jacobi_scale(j)
162 END DO
163 factor(j, j) = factor(j, j) + tikhonov
164 END DO
165 !$OMP END PARALLEL DO
166 CALL timeset(routinen//'_dpotrf', handle2)
167 CALL dpotrf('L', n_grid, factor, n_grid, info)
168 CALL timestop(handle2)
169 CALL timestop(handle)
170 END SUBROUTINE factor_ri_rs_fit
171
172! **************************************************************************************************
173!> \brief Evaluate the normalized cluster residual and its exact derivative in AO space.
174!>
175!> V_l = sqrt(d_l) phi_l, S = V V^T, K = S∘S + λI, and K Z' = b, with
176!> b_lP = V_l^T B_P V_l. B_P is the symmetric matrix defined by the upper AO triangle.
177!> The objective is ||B - A Z'||²/||B||², A_:l = svec(V_l V_l^T), without a penalty term.
178!> For λ>0 the derivative includes the response of the regularized coefficients.
179!> Each RI-column panel reuses V B_P for the RHS and gradient before releasing the panel.
180!> The balancing convention and RI-RS reference are given in solve_ri_rs_fit.
181!> \param phi unscaled AO values indexed (grid point, AO)
182!> \param int_3c three-center integrals indexed (AO, AO, RI); only the upper AO triangle is used
183!> \param tikhonov regularization in the Jacobi-scaled space
184!> \param normalized_error normalized squared residual
185!> \param fit_successful false if the target is empty or the regularized fit cannot be solved
186!> \param ao_gradient optional derivative with respect to the unscaled AO values
187!> \param max_abs_error optional largest absolute unweighted integral error
188!> \param workspace_budget_bytes optional total budget for evaluator-owned gradient workspaces;
189!> the caller deducts its own arrays first. Reserve all fixed workspaces before sizing
190!> the product cache. An absent or nonpositive budget streams one RI column.
191! **************************************************************************************************
192 SUBROUTINE evaluate_ri_rs_fit(phi, int_3c, tikhonov, normalized_error, fit_successful, &
193 ao_gradient, max_abs_error, workspace_budget_bytes)
194 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: phi
195 REAL(kind=dp), DIMENSION(:, :, :), INTENT(IN) :: int_3c
196 REAL(kind=dp), INTENT(IN) :: tikhonov
197 REAL(kind=dp), INTENT(OUT) :: normalized_error
198 LOGICAL, INTENT(OUT) :: fit_successful
199 REAL(kind=dp), DIMENSION(:, :), INTENT(OUT), &
200 OPTIONAL :: ao_gradient
201 REAL(kind=dp), INTENT(OUT), OPTIONAL :: max_abs_error
202 REAL(kind=dp), INTENT(IN), OPTIONAL :: workspace_budget_bytes
203
204 CHARACTER(LEN=*), PARAMETER :: routinen = 'evaluate_ri_rs_fit'
205 INTEGER, PARAMETER :: residual_block_size = 64
206
207 INTEGER :: first, handle, handle2, i, icache, imu, &
208 info, inu, ip, j, last, n_ao, &
209 n_cached, n_grid, n_panel, n_ri, &
210 panel_first, panel_last
211 REAL(kind=dp) :: coefficient_norm2, error_scale, fixed_bytes, integral_norm2, na, ng, &
212 projection, residual_norm2, roundoff_estimate, row_norm2, value, weight
213 REAL(kind=dp), ALLOCATABLE :: coefficient_work(:, :), factor(:, :), jacobi_scale(:), &
214 overlap(:, :), products(:, :, :), residual(:, :), v(:, :), z_prime(:, :)
215
216 CALL timeset(routinen, handle)
217 n_grid = SIZE(phi, 1)
218 n_ao = SIZE(phi, 2)
219 n_ri = SIZE(int_3c, 3)
220 cpassert(SIZE(int_3c, 1) == n_ao .AND. SIZE(int_3c, 2) == n_ao)
221 cpassert(tikhonov >= 0.0_dp)
222 IF (PRESENT(ao_gradient)) THEN
223 cpassert(all(shape(ao_gradient) == shape(phi)))
224 ao_gradient(:, :) = 0.0_dp
225 END IF
226 normalized_error = huge(normalized_error)
227 IF (PRESENT(max_abs_error)) max_abs_error = huge(max_abs_error)
228 fit_successful = .false.
229 IF (n_grid < 1 .OR. n_ri < 1) THEN
230 CALL timestop(handle)
231 RETURN
232 END IF
233 integral_norm2 = 0.0_dp
234 DO ip = 1, n_ri
235 DO inu = 1, n_ao
236 integral_norm2 = integral_norm2 + int_3c(inu, inu, ip)**2 + &
237 2.0_dp*dot_product(int_3c(1:inu - 1, inu, ip), int_3c(1:inu - 1, inu, ip))
238 END DO
239 END DO
240 IF (integral_norm2 <= tiny(1.0_dp)) THEN
241 CALL timestop(handle)
242 RETURN
243 END IF
244
245 n_panel = n_ri
246 n_cached = 1
247 IF (PRESENT(ao_gradient)) THEN
248 n_panel = 1
249 IF (PRESENT(workspace_budget_bytes)) THEN
250 IF (workspace_budget_bytes > 0.0_dp) THEN
251 ng = real(n_grid, dp)
252 na = real(n_ao, dp)
253 ! Reserve factor, overlap, V, two coefficient arrays, Jacobi scales and the
254 ! largest residual panel before assigning all remaining budget to V B_P.
255 fixed_bytes = 8.0_dp*(2.0_dp*ng**2 + ng*na + 2.0_dp*ng*real(n_ri, dp) + ng + &
256 na*min(na, real(residual_block_size, dp)))
257 n_panel = int(min(real(n_ri, dp), max(0.0_dp, &
258 (workspace_budget_bytes - fixed_bytes)/(8.0_dp*ng*na))))
259 IF (n_panel < 1) THEN
260 cpabort("RI-RS minimum fit workspace exceeds memory budget")
261 END IF
262 END IF
263 END IF
264 n_cached = n_panel
265 CALL factor_ri_rs_fit(phi, tikhonov, factor, jacobi_scale, info, overlap)
266 ELSE
267 CALL factor_ri_rs_fit(phi, tikhonov, factor, jacobi_scale, info)
268 END IF
269 IF (info /= 0) THEN
270 CALL timestop(handle)
271 RETURN
272 END IF
273 ALLOCATE (v(n_grid, n_ao), products(n_grid, n_ao, n_cached), &
274 coefficient_work(n_grid, n_ri), z_prime(n_grid, n_ri))
275 DO imu = 1, n_ao
276 v(:, imu) = sqrt(jacobi_scale)*phi(:, imu)
277 END DO
278 ! K is independent of P. Solve a panel of RHSs and consume its cached products immediately.
279 ! Keep Z' and the coefficient response for the final grid-space symmetric product.
280 value = 0.0_dp
281 coefficient_norm2 = 0.0_dp
282 CALL timeset(routinen//'_ri_panels', handle2)
283 DO panel_first = 1, n_ri, n_panel
284 panel_last = min(panel_first + n_panel - 1, n_ri)
285 DO ip = panel_first, panel_last
286 icache = 1
287 IF (PRESENT(ao_gradient)) icache = ip - panel_first + 1
288 CALL dsymm('R', 'U', n_grid, n_ao, 1.0_dp, int_3c(:, :, ip), n_ao, v, n_grid, &
289 0.0_dp, products(:, :, icache), n_grid)
290 DO i = 1, n_grid
291 coefficient_work(i, ip) = dot_product(v(i, :), products(i, :, icache))
292 END DO
293 END DO
294 z_prime(:, panel_first:panel_last) = coefficient_work(:, panel_first:panel_last)
295 CALL dpotrs('L', n_grid, panel_last - panel_first + 1, factor, n_grid, &
296 z_prime(:, panel_first:panel_last), n_grid, info)
297 IF (info /= 0) THEN
298 CALL timestop(handle2)
299 CALL timestop(handle)
300 RETURN
301 END IF
302 DO ip = panel_first, panel_last
303 value = value + dot_product(z_prime(:, ip), coefficient_work(:, ip))
304 coefficient_norm2 = coefficient_norm2 + dot_product(z_prime(:, ip), z_prime(:, ip))
305 END DO
306
307 IF (PRESENT(ao_gradient)) THEN
308 ! Reuse the RHS for Y = K^-1 Z' and then C = Z' + λY; λ=0 needs no response solve.
309 coefficient_work(:, panel_first:panel_last) = z_prime(:, panel_first:panel_last)
310 IF (tikhonov > 0.0_dp) THEN
311 CALL dpotrs('L', n_grid, panel_last - panel_first + 1, factor, n_grid, &
312 coefficient_work(:, panel_first:panel_last), n_grid, info)
313 IF (info /= 0) THEN
314 CALL timestop(handle2)
315 CALL timestop(handle)
316 RETURN
317 END IF
318 coefficient_work(:, panel_first:panel_last) = z_prime(:, panel_first:panel_last) + &
319 tikhonov*coefficient_work(:, panel_first:panel_last)
320 END IF
321 ! Accumulate -Σ_P C_lP (V B_P)_l while the panel products are still available.
322 DO ip = panel_first, panel_last
323 icache = ip - panel_first + 1
324 DO imu = 1, n_ao
325 ao_gradient(:, imu) = ao_gradient(:, imu) - coefficient_work(:, ip)*products(:, imu, icache)
326 END DO
327 END DO
328 IF (tikhonov > 0.0_dp) THEN
329 ! Retain T = C - Z'/2 = Z'/2 + λY for M = Z'T^T + TZ'^T below.
330 coefficient_work(:, panel_first:panel_last) = coefficient_work(:, panel_first:panel_last) - &
331 0.5_dp*z_prime(:, panel_first:panel_last)
332 END IF
333 END IF
334 END DO
335 CALL timestop(handle2)
336 ! K Z' = b implies ||B-AZ'||² = ||B||² - tr(Z'^T b) - λ ||Z'||².
337 weight = tikhonov*coefficient_norm2
338 residual_norm2 = integral_norm2 - value - weight
339 error_scale = integral_norm2 + abs(value) + weight
340 ! Large coefficients can also amplify the factorization's backward error. The scaled
341 ! Gram has diagonal <= 1, hence ||K||_2 <= n_grid + λ; use a conservative roundoff estimate.
342 roundoff_estimate = 8.0_dp*epsilon(1.0_dp)*real(n_grid, dp)* &
343 (real(n_grid, dp) + tikhonov)*coefficient_norm2
344 ! Near an exact fit, use bounded AO-column panels to avoid cancellation. This path also
345 ! supplies the final maximum-error diagnostic, without storing a pair-by-RI residual.
346 IF (PRESENT(max_abs_error) .OR. &
347 residual_norm2 <= max(sqrt(epsilon(1.0_dp))*error_scale, roundoff_estimate)) THEN
348 CALL timeset(routinen//'_residual', handle2)
349 ALLOCATE (residual(n_ao, min(residual_block_size, n_ao)))
350 residual_norm2 = 0.0_dp
351 IF (PRESENT(max_abs_error)) max_abs_error = 0.0_dp
352 DO ip = 1, n_ri
353 DO first = 1, n_ao, residual_block_size
354 last = min(first + residual_block_size - 1, n_ao)
355 DO inu = first, last
356 products(:, inu - first + 1, 1) = z_prime(:, ip)*v(:, inu)
357 END DO
358 CALL dgemm('T', 'N', n_ao, last - first + 1, n_grid, -1.0_dp, v, n_grid, products(:, :, 1), n_grid, &
359 0.0_dp, residual, n_ao)
360 DO inu = first, last
361 DO imu = 1, inu
362 value = int_3c(imu, inu, ip) + residual(imu, inu - first + 1)
363 weight = real(2 - merge(1, 0, imu == inu), dp)
364 residual_norm2 = residual_norm2 + weight*value**2
365 IF (PRESENT(max_abs_error)) max_abs_error = max(max_abs_error, abs(value))
366 END DO
367 END DO
368 END DO
369 END DO
370 CALL timestop(handle2)
371 END IF
372 DEALLOCATE (products)
373 normalized_error = residual_norm2/integral_norm2
374
375 IF (PRESENT(ao_gradient)) THEN
376 CALL timeset(routinen//'_gradient', handle2)
377 ! All panel solves are complete. Reuse the factor for M = Z'Z'^T + λ(Z'Y^T + YZ'^T).
378 IF (tikhonov > 0.0_dp) THEN
379 CALL dsyr2k('L', 'N', n_grid, n_ri, 1.0_dp, z_prime, n_grid, coefficient_work, n_grid, &
380 0.0_dp, factor, n_grid)
381 ELSE
382 CALL dsyrk('L', 'N', n_grid, n_ri, 1.0_dp, z_prime, n_grid, 0.0_dp, factor, n_grid)
383 END IF
384 DO j = 1, n_grid
385 DO i = j, n_grid
386 factor(i, j) = factor(i, j)*overlap(i, j)
387 END DO
388 END DO
389 ! Add (M∘S)V to the accumulated negative term, then normalize the AO gradient.
390 CALL dsymm('L', 'L', n_grid, n_ao, 1.0_dp, factor, n_grid, v, n_grid, &
391 1.0_dp, ao_gradient, n_grid)
392 ao_gradient(:, :) = (4.0_dp/integral_norm2)*ao_gradient
393 ! V_l = sqrt(d_l) phi_l. Differentiate the normalization only above the floor;
394 ! on the clamped branch d_l is constant, including at the branch boundary.
395 DO i = 1, n_grid
396 row_norm2 = dot_product(phi(i, :), phi(i, :))
397 IF (row_norm2 > sqrt(jacobi_floor)) THEN
398 projection = dot_product(phi(i, :), ao_gradient(i, :))/row_norm2
399 ao_gradient(i, :) = ao_gradient(i, :) - projection*phi(i, :)
400 END IF
401 ao_gradient(i, :) = sqrt(jacobi_scale(i))*ao_gradient(i, :)
402 END DO
403 CALL timestop(handle2)
404 END IF
405 fit_successful = .true.
406 CALL timestop(handle)
407 END SUBROUTINE evaluate_ri_rs_fit
408
409! **************************************************************************************************
410!> \brief Multiplies every matrix row by the corresponding diagonal entry:
411!> A(l, :) <- d_l A(l, :).
412!> \param matrix ...
413!> \param diagonal ...
414! **************************************************************************************************
415 SUBROUTINE scale_rows_by_diag(matrix, diagonal)
416
417 REAL(kind=dp), DIMENSION(:, :), INTENT(INOUT) :: matrix
418 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: diagonal
419
420 CHARACTER(LEN=*), PARAMETER :: routinen = 'scale_rows_by_diag'
421
422 INTEGER :: handle, i, j
423
424 CALL timeset(routinen, handle)
425 cpassert(SIZE(diagonal) == SIZE(matrix, 1))
426
427 !$OMP PARALLEL DO DEFAULT(NONE) &
428 !$OMP SHARED(matrix, diagonal) &
429 !$OMP PRIVATE(j, i) &
430 !$OMP SCHEDULE(STATIC)
431 DO j = 1, SIZE(matrix, 2)
432 DO i = 1, SIZE(matrix, 1)
433 matrix(i, j) = matrix(i, j)*diagonal(i)
434 END DO
435 END DO
436 !$OMP END PARALLEL DO
437
438 CALL timestop(handle)
439
440 END SUBROUTINE scale_rows_by_diag
441
442! **************************************************************************************************
443!> \brief Stores dense Z_lP columns in the distributed block-sparse matrix.
444!> \param mat_Z_lP ...
445!> \param Z_local ...
446!> \param local_grid_idx ...
447!> \param n_local_grid ...
448!> \param n_loc_ri ...
449!> \param atom_P ...
450!> \param r_blk_sizes ...
451!> \param row_offset ...
452!> \param eps_filter ...
453! **************************************************************************************************
454 SUBROUTINE store_z_lp_columns(mat_Z_lP, Z_local, local_grid_idx, n_local_grid, n_loc_ri, &
455 atom_P, r_blk_sizes, row_offset, eps_filter)
456
457 TYPE(dbcsr_type), INTENT(INOUT) :: mat_z_lp
458 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: z_local
459 INTEGER, DIMENSION(:), INTENT(IN) :: local_grid_idx
460 INTEGER, INTENT(IN) :: n_local_grid, n_loc_ri, atom_p
461 INTEGER, DIMENSION(:), INTENT(IN) :: r_blk_sizes, row_offset
462 REAL(kind=dp), INTENT(IN) :: eps_filter
463
464 CHARACTER(LEN=*), PARAMETER :: routinen = 'store_Z_lP_columns'
465
466 INTEGER :: current_chunk_size, g_pt, handle, i_blk, &
467 loc_ptr, r_end, r_start
468 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: z_blk
469
470 CALL timeset(routinen, handle)
471
472 ALLOCATE (z_blk(maxval(r_blk_sizes), n_loc_ri))
473 ! local_grid_idx is ascending, so one walking pointer maps sphere rows to global grid blocks.
474 loc_ptr = 1
475
476 DO i_blk = 1, SIZE(r_blk_sizes)
477 r_start = row_offset(i_blk) + 1
478 r_end = row_offset(i_blk) + r_blk_sizes(i_blk)
479 current_chunk_size = r_blk_sizes(i_blk)
480 z_blk = 0.0_dp
481
482 DO WHILE (loc_ptr <= n_local_grid)
483 g_pt = local_grid_idx(loc_ptr)
484 IF (g_pt > r_end) EXIT
485 z_blk(g_pt - r_start + 1, 1:n_loc_ri) = z_local(loc_ptr, 1:n_loc_ri)
486 loc_ptr = loc_ptr + 1
487 END DO
488
489 ! DBCSR routes each retained block to its global owner when the caller finalizes mat_Z_lP.
490 IF (maxval(abs(z_blk(1:current_chunk_size, 1:n_loc_ri))) > eps_filter) THEN
491 CALL dbcsr_put_block(mat_z_lp, row=i_blk, col=atom_p, &
492 block=z_blk(1:current_chunk_size, 1:n_loc_ri))
493 END IF
494 END DO
495
496 DEALLOCATE (z_blk)
497
498 CALL timestop(handle)
499
500 END SUBROUTINE store_z_lp_columns
501
502! **************************************************************************************************
503!> \brief Solves the Jacobi-scaled, regularized RI-RS system with ScaLAPACK.
504!>
505!> Each rank builds its block-cyclic part of
506!>
507!> D'_ll' = d_l [Σ_μ ϕ_μ(r_l) ϕ_μ(r_l')]² d_l' + λ δ_ll'.
508!>
509!> The Cholesky solution Z'_lP is gathered into the replicated right-hand side.
510!> The caller then restores Z_lP = d_l Z'_lP.
511!> \param phi_local replicated AO values on the fitting grid
512!> \param d_vec Jacobi scaling vector d_l, with the same floor as in the dense solve
513!> \param d_lp replicated scaled RHS d_l d_lP on entry, intermediate solution Z'_lP on return
514!> \param tikhonov ...
515!> \param para_env_sub ...
516!> \param blacs_env_sub ...
517! **************************************************************************************************
518 SUBROUTINE solve_d_lp_distributed(phi_local, d_vec, d_lp, tikhonov, para_env_sub, blacs_env_sub)
519
520 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: phi_local
521 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: d_vec
522 REAL(kind=dp), DIMENSION(:, :), INTENT(INOUT) :: d_lp
523 REAL(kind=dp), INTENT(IN) :: tikhonov
524 TYPE(mp_para_env_type), POINTER :: para_env_sub
525 TYPE(cp_blacs_env_type), POINTER :: blacs_env_sub
526
527 CHARACTER(LEN=*), PARAMETER :: routinen = 'solve_D_lp_distributed'
528
529 INTEGER :: handle, i_loc, ig, info, j_loc, jg, &
530 n_ao, n_loc, n_rhs, ncol_local, &
531 nrow_local
532 INTEGER, DIMENSION(:), POINTER :: col_indices, row_indices
533 REAL(kind=dp), CONTIGUOUS, DIMENSION(:, :), &
534 POINTER :: local_data
535 TYPE(cp_fm_struct_type), POINTER :: fm_struct_b, fm_struct_d
536 TYPE(cp_fm_type) :: fm_b, fm_d
537
538 CALL timeset(routinen, handle)
539 n_loc = SIZE(phi_local, 1)
540 n_ao = SIZE(phi_local, 2)
541 n_rhs = SIZE(d_lp, 2)
542
543 NULLIFY (fm_struct_d, fm_struct_b)
544 CALL cp_fm_struct_create(fm_struct_d, para_env=para_env_sub, &
545 context=blacs_env_sub, &
546 nrow_global=n_loc, ncol_global=n_loc)
547 CALL cp_fm_struct_create(fm_struct_b, para_env=para_env_sub, &
548 context=blacs_env_sub, &
549 nrow_global=n_loc, ncol_global=n_rhs)
550 CALL cp_fm_create(fm_d, fm_struct_d)
551 CALL cp_fm_create(fm_b, fm_struct_b)
552
553 CALL cp_fm_get_info(fm_d, nrow_local=nrow_local, ncol_local=ncol_local, &
554 row_indices=row_indices, col_indices=col_indices, &
555 local_data=local_data)
556
557 ! Build only the rank-owned Gram tiles, avoiding a replicated n_grid × n_grid matrix.
558 IF (nrow_local > 0 .AND. ncol_local > 0) THEN
559 block
560 INTEGER, PARAMETER :: ntile = 1024
561 INTEGER :: ib, ie, jb, je, mb, kb, ti, tj, handle_dgemm
562 REAL(kind=dp), ALLOCATABLE :: gram_t(:, :), phi_cols_t(:, :), phi_rows_t(:, :)
563 ALLOCATE (phi_rows_t(ntile, n_ao), phi_cols_t(n_ao, ntile), gram_t(ntile, ntile))
564 ! Pack AO values for the grid rows/columns selected by the block-cyclic FM layout.
565 DO ib = 1, nrow_local, ntile
566 ie = min(ib + ntile - 1, nrow_local)
567 mb = ie - ib + 1
568 !$OMP PARALLEL DO DEFAULT(NONE) &
569 !$OMP SHARED(mb, n_ao, phi_rows_t, phi_local, row_indices, ib) &
570 !$OMP PRIVATE(ti, j_loc) SCHEDULE(STATIC)
571 DO j_loc = 1, n_ao
572 DO ti = 1, mb
573 phi_rows_t(ti, j_loc) = phi_local(row_indices(ib + ti - 1), j_loc)
574 END DO
575 END DO
576 !$OMP END PARALLEL DO
577 DO jb = 1, ncol_local, ntile
578 je = min(jb + ntile - 1, ncol_local)
579 kb = je - jb + 1
580 !$OMP PARALLEL DO DEFAULT(NONE) &
581 !$OMP SHARED(kb, n_ao, phi_cols_t, phi_local, col_indices, jb) &
582 !$OMP PRIVATE(tj, i_loc) SCHEDULE(STATIC)
583 DO tj = 1, kb
584 DO i_loc = 1, n_ao
585 phi_cols_t(i_loc, tj) = phi_local(col_indices(jb + tj - 1), i_loc)
586 END DO
587 END DO
588 !$OMP END PARALLEL DO
589 CALL timeset(routinen//'_dgemm', handle_dgemm)
590 CALL dgemm('N', 'N', mb, kb, n_ao, &
591 1.0_dp, phi_rows_t, ntile, phi_cols_t, n_ao, &
592 0.0_dp, gram_t, ntile)
593 CALL timestop(handle_dgemm)
594 ! Square the AO Gram entries, apply d_l d_l', and add λ on the global diagonal.
595 !$OMP PARALLEL DO DEFAULT(NONE) &
596 !$OMP SHARED(mb, kb, gram_t, d_vec, row_indices, col_indices, ib, jb) &
597 !$OMP SHARED(local_data, tikhonov) &
598 !$OMP PRIVATE(ti, tj, ig, jg) SCHEDULE(STATIC)
599 DO tj = 1, kb
600 jg = col_indices(jb + tj - 1)
601 DO ti = 1, mb
602 ig = row_indices(ib + ti - 1)
603 local_data(ib + ti - 1, jb + tj - 1) = &
604 gram_t(ti, tj)*gram_t(ti, tj)*d_vec(ig)*d_vec(jg)
605 IF (ig == jg) THEN
606 local_data(ib + ti - 1, jb + tj - 1) = &
607 local_data(ib + ti - 1, jb + tj - 1) + tikhonov
608 END IF
609 END DO
610 END DO
611 !$OMP END PARALLEL DO
612 END DO
613 END DO
614 DEALLOCATE (phi_rows_t, phi_cols_t, gram_t)
615 END block
616 END IF
617
618 ! Distribute the pre-scaled RHS and solve for Z'_lP within the BLACS subgroup.
619 CALL cp_fm_set_submatrix(fm_b, d_lp)
620
621 CALL cp_fm_cholesky_decompose(fm_d, n=n_loc, info_out=info)
622 IF (info /= 0) cpabort("pdpotrf failed in solve_D_lp_distributed")
623
624 CALL cp_fm_cholesky_solve(fm_d, fm_b, n=n_loc, info_out=info)
625 IF (info /= 0) cpabort("pdpotrs failed in solve_D_lp_distributed")
626
627 ! Replicate Z'_lP within the subgroup; the caller applies the final Jacobi rescaling.
628 CALL cp_fm_get_submatrix(fm_b, d_lp)
629
630 CALL cp_fm_release(fm_d)
631 CALL cp_fm_release(fm_b)
632 CALL cp_fm_struct_release(fm_struct_d)
633 CALL cp_fm_struct_release(fm_struct_b)
634
635 CALL timestop(handle)
636
637 END SUBROUTINE solve_d_lp_distributed
638
639END MODULE ri_rs_fitting
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.
methods related to the blacs parallel environment
subroutine, public dbcsr_put_block(matrix, row, col, block, summation)
...
various cholesky decomposition related routines
subroutine, public cp_fm_cholesky_solve(matrix, matrixb, n, info_out)
solves A*X = B for X, given the Cholesky decomposition U of the symmetric positive def....
subroutine, public cp_fm_cholesky_decompose(matrix, n, info_out)
used to replace a symmetric positive def. matrix M with its cholesky decomposition U: M = U^T * U,...
represent the structure of a full matrix
subroutine, public cp_fm_struct_create(fmstruct, para_env, context, nrow_global, ncol_global, nrow_block, ncol_block, descriptor, first_p_pos, local_leading_dimension, template_fmstruct, square_blocks, force_block)
allocates and initializes a full matrix structure
subroutine, public cp_fm_struct_release(fmstruct)
releases a full matrix structure
represent a full matrix distributed on many processors
Definition cp_fm_types.F:15
subroutine, public cp_fm_get_info(matrix, name, nrow_global, ncol_global, nrow_block, ncol_block, nrow_local, ncol_local, row_indices, col_indices, local_data, context, nrow_locals, ncol_locals, matrix_struct, para_env)
returns all kind of information about the full matrix
subroutine, public cp_fm_set_submatrix(fm, new_values, start_row, start_col, n_rows, n_cols, alpha, beta, transpose)
sets a submatrix of a full matrix fm(start_row:start_row+n_rows,start_col:start_col+n_cols) = alpha*o...
subroutine, public cp_fm_create(matrix, matrix_struct, name, nrow, ncol, set_zero)
creates a new full matrix with the given structure
subroutine, public cp_fm_get_submatrix(fm, target_m, start_row, start_col, n_rows, n_cols, transpose)
gets a submatrix of a full matrix op(target_m)(1:n_rows,1:n_cols) =fm(start_row:start_row+n_rows,...
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
Interface to the message passing library MPI.
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.
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 solve_ri_rs_fit(phi_local, d_lp, tikhonov, para_env_sub, blacs_env_sub)
Computes the regularized RI-RS fit from an AO collocation matrix and a replicated RHS.
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
stores all the informations relevant to an mpi environment