(git:a660c7f)
Loading...
Searching...
No Matches
skala_gpw_features.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 Build SKALA TorchScript feature dictionaries from CP2K GPW real-space grids.
10! **************************************************************************************************
12 USE cell_types, ONLY: cell_type,&
13 pbc
15 USE kinds, ONLY: dp,&
16 int_8
20 USE pw_types, ONLY: pw_r3d_rs_type
21 USE torch_api, ONLY: &
28#include "./base/base_uses.f90"
29
30 IMPLICIT NONE
31
32 PRIVATE
33
34 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'skala_gpw_features'
35 REAL(KIND=dp), PARAMETER, PRIVATE :: layout_tol = 1.0e-12_dp
36 INTEGER, PARAMETER, PRIVATE :: ndynamic_per_point = 10, nrks_dynamic_per_point = 5, &
37 nstatic_per_point = 5, ngrad_per_point = 10
38 INTEGER, PARAMETER, PUBLIC :: skala_gpw_atom_partition_hard = 1, &
40 REAL(kind=dp), PARAMETER, PRIVATE :: smooth_partition_eps = 1.0e-12_dp
41
45
46 TYPE skala_gpw_layout_cache_type
47 INTEGER :: chunk_atom_begin = 1, chunk_atom_end = 0, &
48 chunk_feature_begin = 1, &
49 chunk_feature_count = 0, chunk_natom = 0, &
50 natom = 0, nflat = 0, nflat_local = 0, &
51 npoint = 0, nproc = 0, &
52 atom_partition = skala_gpw_atom_partition_hard
53 INTEGER, DIMENSION(2, 3) :: bo = 0, bounds = 0
54 INTEGER, DIMENSION(3) :: npts = 0
55 INTEGER, ALLOCATABLE, DIMENSION(:) :: dynamic_counts, dynamic_displs, &
56 chunk_feature_counts, chunk_feature_displs, &
57 chunk_grad_counts, chunk_grad_displs, &
58 feature_counts, feature_displs, &
59 feature_source_points, global_to_feature, &
60 local_feature_counts, local_feature_offsets, &
61 local_feature_points, local_feature_rows, &
62 route_grad_return_recv_counts, &
63 route_grad_return_recv_displs, &
64 route_grad_return_send_counts, &
65 route_grad_return_send_displs, &
66 route_local_dest, chunk_return_positions, &
67 route_point_recv_counts, &
68 route_point_recv_displs, &
69 route_point_send_counts, &
70 route_point_send_displs, &
71 route_send_local_rows
72 INTEGER, ALLOCATABLE, DIMENSION(:, :, :) :: feature_index
73 INTEGER(KIND=int_8), ALLOCATABLE, DIMENSION(:) :: atomic_grid_sizes, chunk_atomic_grid_sizes, &
74 chunk_feature_indices
75 INTEGER(KIND=int_8), ALLOCATABLE, DIMENSION(:) :: local_feature_indices
76 INTEGER(KIND=int_8), ALLOCATABLE, DIMENSION(:, :) :: atomic_grid_size_bound_shape, &
77 chunk_atomic_grid_size_bound_shape
78 TYPE(torch_dict_type) :: chunk_inputs
79 TYPE(torch_dict_type) :: chunk_static_inputs
80 TYPE(torch_dict_type) :: inputs
81 TYPE(torch_dict_type) :: static_inputs
82 TYPE(torch_tensor_type) :: atomic_grid_size_bound_shape_t
83 TYPE(torch_tensor_type) :: atomic_grid_sizes_t
84 TYPE(torch_tensor_type) :: atomic_grid_weights_t
85 TYPE(torch_tensor_type) :: chunk_atomic_grid_size_bound_shape_t
86 TYPE(torch_tensor_type) :: chunk_atomic_grid_sizes_t
87 TYPE(torch_tensor_type) :: chunk_atomic_grid_weights_t
88 TYPE(torch_tensor_type) :: chunk_coarse_0_atomic_coords_t
89 TYPE(torch_tensor_type) :: chunk_density_t
90 TYPE(torch_tensor_type) :: chunk_density_input_t
91 TYPE(torch_tensor_type) :: chunk_feature_indices_t
92 TYPE(torch_tensor_type) :: chunk_grad_t
93 TYPE(torch_tensor_type) :: chunk_grad_input_t
94 TYPE(torch_tensor_type) :: chunk_grid_coords_t
95 TYPE(torch_tensor_type) :: chunk_grid_weights_t
96 TYPE(torch_tensor_type) :: chunk_kin_t
97 TYPE(torch_tensor_type) :: chunk_kin_input_t
98 TYPE(torch_tensor_type) :: coarse_0_atomic_coords_t
99 TYPE(torch_tensor_type) :: density_t
100 TYPE(torch_tensor_type) :: grid_coords_t
101 TYPE(torch_tensor_type) :: grid_weights_t
102 TYPE(torch_tensor_type) :: grad_t
103 TYPE(torch_tensor_type) :: kin_t
104 TYPE(torch_tensor_type) :: local_feature_indices_t
105 REAL(kind=dp) :: dvol = 0.0_dp, weight_sum = 0.0_dp, &
106 weight_sumsq = 0.0_dp
107 REAL(kind=dp), DIMENSION(3, 3) :: cell_hmat = 0.0_dp, dh = 0.0_dp
108 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: atomic_grid_weights, chunk_atomic_grid_weights, &
109 chunk_grid_weights, grid_weights
110 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: atom_coords, chunk_coarse_0_atomic_coords, &
111 chunk_grid_coords, coarse_0_atomic_coords, &
112 grid_coords
113 LOGICAL :: active = .false., has_weights = .false., &
114 chunk_dynamic_input_views_active = .false., &
115 chunk_dynamic_tensors_active = .false., &
116 chunk_inputs_active = .false., &
117 chunk_inputs_use_collapsed_rks = .false., &
118 chunk_static_tensors_active = .false., &
119 dynamic_tensors_active = .false., &
120 inputs_active = .false., &
121 static_tensors_active = .false.
122 END TYPE skala_gpw_layout_cache_type
123
125 INTEGER :: chunk_feature_count = 0, nflat = 0, &
126 nflat_local = 0, &
127 atom_partition = skala_gpw_atom_partition_hard
128 TYPE(torch_dict_type) :: inputs
129 TYPE(torch_tensor_type) :: atomic_grid_size_bound_shape_t
130 TYPE(torch_tensor_type) :: atomic_grid_sizes_t
131 TYPE(torch_tensor_type) :: atomic_grid_weights_t
132 TYPE(torch_tensor_type) :: coarse_0_atomic_coords_t
133 TYPE(torch_tensor_type) :: density_input_t
134 TYPE(torch_tensor_type) :: density_t
135 TYPE(torch_tensor_type) :: grad_t
136 TYPE(torch_tensor_type) :: grad_input_t
137 TYPE(torch_tensor_type) :: grid_coords_t
138 TYPE(torch_tensor_type) :: grid_weights_t
139 TYPE(torch_tensor_type) :: kin_input_t
140 TYPE(torch_tensor_type) :: kin_t
141 TYPE(torch_tensor_type) :: local_feature_indices_t
142 INTEGER, ALLOCATABLE, DIMENSION(:) :: chunk_grad_counts, chunk_grad_displs, &
143 local_feature_counts, local_feature_offsets, &
144 local_feature_rows, &
145 chunk_return_positions, &
146 route_grad_return_recv_counts, &
147 route_grad_return_recv_displs, &
148 route_grad_return_send_counts, &
149 route_grad_return_send_displs, &
150 route_point_recv_counts, &
151 route_point_recv_displs, &
152 route_point_send_counts, &
153 route_point_send_displs, &
154 route_send_local_rows
155 INTEGER, ALLOCATABLE, DIMENSION(:, :, :) :: feature_index
156 INTEGER(KIND=int_8), ALLOCATABLE, DIMENSION(:) :: atomic_grid_sizes
157 INTEGER(KIND=int_8), ALLOCATABLE, DIMENSION(:, :) :: atomic_grid_size_bound_shape
158 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: atomic_grid_weights, grid_weights
159 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: chunk_density, chunk_kin, &
160 coarse_0_atomic_coords, density, &
161 grid_coords, kin
162 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :, :) :: chunk_grad, grad
163 REAL(kind=dp) :: electron_count = 0.0_dp, &
164 grid_weight_sum = 0.0_dp, &
165 spin_moment = 0.0_dp
166 LOGICAL :: active = .false., owns_coordinate_tensor = .false., &
167 owns_grid_coordinate_tensor = .false., &
168 owns_weight_tensors = .false., &
169 owns_dynamic_tensors = .true., &
170 owns_inputs = .true., &
171 owns_static_tensors = .true., &
172 uses_atom_chunk_routing = .false., &
173 uses_atom_chunks = .false., &
174 uses_collapsed_rks_dynamic = .false.
176
177 TYPE(skala_gpw_layout_cache_type), SAVE :: cached_layout
178
179CONTAINS
180
181! **************************************************************************************************
182!> \brief Build a flat SKALA molecular feature dictionary from a local GPW grid.
183!> \param features ...
184!> \param rho_set ...
185!> \param rho_r ...
186!> \param particle_set ...
187!> \param cell ...
188!> \param requires_grad ...
189!> \param weights ...
190!> \param requires_coordinate_grad ...
191!> \param requires_stress_grad ...
192!> \param use_atom_chunks ...
193!> \param route_atom_chunks ...
194!> \param atom_partition ...
195! **************************************************************************************************
196 SUBROUTINE skala_gpw_feature_build(features, rho_set, rho_r, particle_set, cell, &
197 requires_grad, weights, requires_coordinate_grad, &
198 requires_stress_grad, use_atom_chunks, route_atom_chunks, &
199 atom_partition)
200 TYPE(skala_gpw_feature_type), INTENT(INOUT) :: features
201 TYPE(xc_rho_set_type), INTENT(IN) :: rho_set
202 TYPE(pw_r3d_rs_type), DIMENSION(:), INTENT(IN) :: rho_r
203 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
204 TYPE(cell_type), POINTER :: cell
205 LOGICAL, INTENT(IN), OPTIONAL :: requires_grad
206 TYPE(pw_r3d_rs_type), OPTIONAL, POINTER :: weights
207 LOGICAL, INTENT(IN), OPTIONAL :: requires_coordinate_grad, &
208 requires_stress_grad, use_atom_chunks, &
209 route_atom_chunks
210 INTEGER, INTENT(IN), OPTIONAL :: atom_partition
211
212 INTEGER :: handle, i, ipt, ispin, j, k, local_row, my_atom_partition, &
213 ndynamic_local_per_point, nflat, nflat_local, nspins, phase_handle, real_base, row
214 INTEGER, DIMENSION(2, 3) :: bo
215 LOGICAL :: collapse_spin_dynamics, my_requires_coordinate_grad, my_requires_grad, &
216 my_requires_stress_grad, my_route_atom_chunks, my_use_atom_chunks, &
217 use_atom_chunk_protocol, use_atom_chunk_routing
218 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: global_dynamic, local_dynamic
219 REAL(kind=dp), DIMENSION(:, :, :), POINTER :: rho, rhoa, rhob, tau_a, tau_b, tau_total
220 TYPE(cp_3d_r_cp_type), DIMENSION(3) :: drho, drhoa, drhob
221 TYPE(pw_grid_type), POINTER :: pw_grid
222
223 CALL timeset("skala_gpw_feature_build", handle)
224
225 my_requires_grad = .false.
226 IF (PRESENT(requires_grad)) my_requires_grad = requires_grad
227 my_requires_coordinate_grad = .false.
228 IF (PRESENT(requires_coordinate_grad)) THEN
229 my_requires_coordinate_grad = requires_coordinate_grad
230 END IF
231 my_requires_stress_grad = .false.
232 IF (PRESENT(requires_stress_grad)) my_requires_stress_grad = requires_stress_grad
233 my_use_atom_chunks = .false.
234 IF (PRESENT(use_atom_chunks)) my_use_atom_chunks = use_atom_chunks
235 my_route_atom_chunks = .false.
236 IF (PRESENT(route_atom_chunks)) my_route_atom_chunks = route_atom_chunks
237 my_atom_partition = skala_gpw_atom_partition_hard
238 IF (PRESENT(atom_partition)) my_atom_partition = atom_partition
239 IF (my_atom_partition /= skala_gpw_atom_partition_hard .AND. &
240 my_atom_partition /= skala_gpw_atom_partition_smooth) THEN
241 CALL cp_abort(__location__, "Unknown native SKALA atom-partition mode.")
242 END IF
243 cpassert(ASSOCIATED(cell))
244 cpassert(ASSOCIATED(particle_set))
245 cpassert(SIZE(rho_r) == 1 .OR. SIZE(rho_r) == 2)
246 cpassert(ASSOCIATED(rho_r(1)%pw_grid))
247 pw_grid => rho_r(1)%pw_grid
248
249 nspins = SIZE(rho_r)
250 bo = pw_grid%bounds_local
251 nflat_local = pw_grid%ngpts_local
252
253 CALL timeset("skala_gpw_pre_release", phase_handle)
254 CALL skala_gpw_feature_release(features)
255 CALL timestop(phase_handle)
256
257 CALL timeset("skala_gpw_layout_cache", phase_handle)
258 CALL ensure_layout_cache(pw_grid, particle_set, cell, weights, my_atom_partition)
259 CALL timestop(phase_handle)
260 nflat = cached_layout%nflat
261 use_atom_chunk_protocol = my_use_atom_chunks .AND. &
262 .NOT. (my_requires_coordinate_grad .OR. my_requires_stress_grad)
263 use_atom_chunk_routing = use_atom_chunk_protocol .AND. my_route_atom_chunks
264 collapse_spin_dynamics = nspins == 1 .AND. use_atom_chunk_routing
265 ndynamic_local_per_point = ndynamic_per_point
266 IF (collapse_spin_dynamics) ndynamic_local_per_point = nrks_dynamic_per_point
267 ALLOCATE (local_dynamic(ndynamic_local_per_point*nflat_local))
268 local_dynamic = 0.0_dp
269
270 CALL timeset("skala_gpw_pack_local", phase_handle)
271 IF (nspins == 1) THEN
272 CALL xc_rho_set_get(rho_set, rho=rho, drho=drho, tau=tau_total)
273 ELSE
274 CALL xc_rho_set_get(rho_set, rhoa=rhoa, rhob=rhob, drhoa=drhoa, drhob=drhob, &
275 tau_a=tau_a, tau_b=tau_b)
276 END IF
277
278 local_row = 0
279 DO k = bo(1, 3), bo(2, 3)
280 DO j = bo(1, 2), bo(2, 2)
281 DO i = bo(1, 1), bo(2, 1)
282 local_row = local_row + 1
283 real_base = ndynamic_local_per_point*(local_row - 1)
284
285 IF (nspins == 1) THEN
286 IF (collapse_spin_dynamics) THEN
287 local_dynamic(real_base + 1) = 0.5_dp*rho(i, j, k)
288 local_dynamic(real_base + 2) = 0.5_dp*drho(1)%array(i, j, k)
289 local_dynamic(real_base + 3) = 0.5_dp*drho(2)%array(i, j, k)
290 local_dynamic(real_base + 4) = 0.5_dp*drho(3)%array(i, j, k)
291 local_dynamic(real_base + 5) = 0.5_dp*tau_total(i, j, k)
292 ELSE
293 local_dynamic(real_base + 1) = 0.5_dp*rho(i, j, k)
294 local_dynamic(real_base + 2) = 0.5_dp*rho(i, j, k)
295 DO ispin = 1, 2
296 local_dynamic(real_base + 2 + 3*(ispin - 1) + 1) = &
297 0.5_dp*drho(1)%array(i, j, k)
298 local_dynamic(real_base + 2 + 3*(ispin - 1) + 2) = &
299 0.5_dp*drho(2)%array(i, j, k)
300 local_dynamic(real_base + 2 + 3*(ispin - 1) + 3) = &
301 0.5_dp*drho(3)%array(i, j, k)
302 local_dynamic(real_base + 8 + ispin) = 0.5_dp*tau_total(i, j, k)
303 END DO
304 END IF
305 ELSE
306 local_dynamic(real_base + 1) = rhoa(i, j, k)
307 local_dynamic(real_base + 2) = rhob(i, j, k)
308 local_dynamic(real_base + 3) = drhoa(1)%array(i, j, k)
309 local_dynamic(real_base + 4) = drhoa(2)%array(i, j, k)
310 local_dynamic(real_base + 5) = drhoa(3)%array(i, j, k)
311 local_dynamic(real_base + 6) = drhob(1)%array(i, j, k)
312 local_dynamic(real_base + 7) = drhob(2)%array(i, j, k)
313 local_dynamic(real_base + 8) = drhob(3)%array(i, j, k)
314 local_dynamic(real_base + 9) = tau_a(i, j, k)
315 local_dynamic(real_base + 10) = tau_b(i, j, k)
316 END IF
317 END DO
318 END DO
319 END DO
320 CALL timestop(phase_handle)
321
322 CALL timeset("skala_gpw_copy_layout", phase_handle)
323 CALL copy_cached_layout(features, my_requires_coordinate_grad .OR. my_requires_stress_grad, &
324 my_requires_stress_grad .OR. &
325 (my_atom_partition == skala_gpw_atom_partition_smooth .AND. &
326 (my_requires_coordinate_grad .OR. my_requires_stress_grad)))
327 CALL timestop(phase_handle)
328
329 IF (use_atom_chunk_routing) THEN
330 CALL timeset("skala_gpw_route_dyn", phase_handle)
331 CALL route_atom_chunk_dynamics(features, local_dynamic, pw_grid%para%group, &
332 collapse_spin_dynamics)
333 features%uses_atom_chunk_routing = .true.
334 features%uses_atom_chunks = .true.
335 CALL timestop(phase_handle)
336 ELSE
337 ALLOCATE (global_dynamic(ndynamic_per_point*cached_layout%npoint))
338 CALL timeset("skala_gpw_allgatherv", phase_handle)
339 CALL pw_grid%para%group%allgatherv(local_dynamic, global_dynamic, &
340 cached_layout%dynamic_counts, &
341 cached_layout%dynamic_displs)
342 CALL timestop(phase_handle)
343
344 CALL timeset("skala_gpw_reorder_dyn", phase_handle)
345 ALLOCATE (features%density(nflat, 2), features%grad(nflat, 3, 2), &
346 features%kin(nflat, 2))
347 features%density = 0.0_dp
348 features%grad = 0.0_dp
349 features%kin = 0.0_dp
350
351 DO row = 1, nflat
352 ipt = cached_layout%feature_source_points(row)
353 real_base = ndynamic_per_point*(ipt - 1)
354 features%density(row, :) = global_dynamic(real_base + 1:real_base + 2)
355 features%grad(row, 1, 1) = global_dynamic(real_base + 3)
356 features%grad(row, 2, 1) = global_dynamic(real_base + 4)
357 features%grad(row, 3, 1) = global_dynamic(real_base + 5)
358 features%grad(row, 1, 2) = global_dynamic(real_base + 6)
359 features%grad(row, 2, 2) = global_dynamic(real_base + 7)
360 features%grad(row, 3, 2) = global_dynamic(real_base + 8)
361 features%kin(row, :) = global_dynamic(real_base + 9:real_base + 10)
362 END DO
363 CALL timestop(phase_handle)
364 END IF
365
366 CALL timeset("skala_gpw_feature_sums", phase_handle)
367 IF (features%uses_atom_chunks) THEN
368 features%electron_count = 0.0_dp
369 features%spin_moment = 0.0_dp
370 IF (features%chunk_feature_count > 0) THEN
371 IF (features%uses_collapsed_rks_dynamic) THEN
372 features%electron_count = sum(2.0_dp*features%chunk_density(:, 1)* &
373 cached_layout%chunk_grid_weights)
374 ELSE
375 features%electron_count = sum((features%chunk_density(:, 1) + &
376 features%chunk_density(:, 2))* &
377 cached_layout%chunk_grid_weights)
378 features%spin_moment = sum((features%chunk_density(:, 1) - &
379 features%chunk_density(:, 2))* &
380 cached_layout%chunk_grid_weights)
381 END IF
382 END IF
383 CALL pw_grid%para%group%sum(features%electron_count)
384 CALL pw_grid%para%group%sum(features%spin_moment)
385 ELSE
386 features%electron_count = sum((features%density(:, 1) + features%density(:, 2))* &
387 features%grid_weights)
388 features%spin_moment = sum((features%density(:, 1) - features%density(:, 2))* &
389 features%grid_weights)
390 END IF
391 features%grid_weight_sum = sum(features%grid_weights)
392 CALL timestop(phase_handle)
393
394 CALL timeset("skala_gpw_tensor_update", phase_handle)
395 IF (use_atom_chunk_protocol .AND. .NOT. features%uses_atom_chunks) THEN
396 IF (features%chunk_feature_count > 0) CALL extract_atom_chunk_dynamics(features)
397 features%uses_atom_chunks = .true.
398 END IF
399 IF (.NOT. features%uses_atom_chunks .OR. features%chunk_feature_count > 0) THEN
400 CALL add_feature_tensors(features, my_requires_grad, my_requires_coordinate_grad, &
401 my_requires_stress_grad, &
402 features%uses_atom_chunks, &
403 requires_weight_grad= &
404 (my_atom_partition == skala_gpw_atom_partition_smooth .AND. &
405 (my_requires_coordinate_grad .OR. my_requires_stress_grad)))
406 ELSE
407 ! This rank participates in atom-chunk communication but owns no model input rows.
408 features%owns_coordinate_tensor = .false.
409 features%owns_grid_coordinate_tensor = .false.
410 features%owns_weight_tensors = .false.
411 features%owns_dynamic_tensors = .false.
412 features%owns_inputs = .false.
413 features%owns_static_tensors = .false.
414 END IF
415 CALL timestop(phase_handle)
416 features%active = .true.
417
418 IF (ALLOCATED(global_dynamic)) DEALLOCATE (global_dynamic)
419 DEALLOCATE (local_dynamic)
420 CALL timestop(handle)
421
422 END SUBROUTINE skala_gpw_feature_build
423
424! **************************************************************************************************
425!> \brief Ensure that static grid-to-atom layout data is cached for the current grid/geometry.
426!> \param pw_grid ...
427!> \param particle_set ...
428!> \param cell ...
429!> \param weights ...
430!> \param atom_partition ...
431! **************************************************************************************************
432 SUBROUTINE ensure_layout_cache(pw_grid, particle_set, cell, weights, atom_partition)
433 TYPE(pw_grid_type), POINTER :: pw_grid
434 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
435 TYPE(cell_type), POINTER :: cell
436 TYPE(pw_r3d_rs_type), OPTIONAL, POINTER :: weights
437 INTEGER, INTENT(IN), OPTIONAL :: atom_partition
438
439 INTEGER :: my_atom_partition, phase_handle
440 LOGICAL :: cache_matches
441
442 my_atom_partition = skala_gpw_atom_partition_hard
443 IF (PRESENT(atom_partition)) my_atom_partition = atom_partition
444 IF (PRESENT(weights)) THEN
445 CALL timeset("skala_gpw_layout_match", phase_handle)
446 cache_matches = layout_cache_matches(pw_grid, particle_set, cell, weights, &
447 my_atom_partition)
448 CALL timestop(phase_handle)
449 IF (cache_matches) RETURN
450 CALL timeset("skala_gpw_layout_rebuild", phase_handle)
451 CALL rebuild_layout_cache(pw_grid, particle_set, cell, weights, my_atom_partition)
452 CALL timestop(phase_handle)
453 ELSE
454 CALL timeset("skala_gpw_layout_match", phase_handle)
455 cache_matches = layout_cache_matches(pw_grid, particle_set, cell, &
456 atom_partition=my_atom_partition)
457 CALL timestop(phase_handle)
458 IF (cache_matches) RETURN
459 CALL timeset("skala_gpw_layout_rebuild", phase_handle)
460 CALL rebuild_layout_cache(pw_grid, particle_set, cell, &
461 atom_partition=my_atom_partition)
462 CALL timestop(phase_handle)
463 END IF
464
465 END SUBROUTINE ensure_layout_cache
466
467! **************************************************************************************************
468!> \brief Check whether the current static layout cache can be reused.
469!> \param pw_grid ...
470!> \param particle_set ...
471!> \param cell ...
472!> \param weights ...
473!> \param atom_partition ...
474!> \return ...
475! **************************************************************************************************
476 FUNCTION layout_cache_matches(pw_grid, particle_set, cell, weights, atom_partition) RESULT(matches)
477 TYPE(pw_grid_type), POINTER :: pw_grid
478 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
479 TYPE(cell_type), POINTER :: cell
480 TYPE(pw_r3d_rs_type), OPTIONAL, POINTER :: weights
481 INTEGER, INTENT(IN), OPTIONAL :: atom_partition
482 LOGICAL :: matches
483
484 INTEGER :: iatom, my_atom_partition
485 LOGICAL :: weights_match
486
487 my_atom_partition = skala_gpw_atom_partition_hard
488 IF (PRESENT(atom_partition)) my_atom_partition = atom_partition
489 matches = .false.
490 IF (.NOT. cached_layout%active) RETURN
491 IF (cached_layout%atom_partition /= my_atom_partition) RETURN
492 IF (cached_layout%natom /= SIZE(particle_set)) RETURN
493 IF (cached_layout%nflat_local /= pw_grid%ngpts_local) RETURN
494 IF (cached_layout%nproc /= pw_grid%para%group%num_pe) RETURN
495 IF (any(cached_layout%bo /= pw_grid%bounds_local)) RETURN
496 IF (any(cached_layout%bounds /= pw_grid%bounds)) RETURN
497 IF (any(cached_layout%npts /= pw_grid%npts)) RETURN
498 IF (abs(cached_layout%dvol - pw_grid%dvol) > layout_tol) RETURN
499 IF (any(abs(cached_layout%dh - pw_grid%dh) > layout_tol)) RETURN
500 IF (any(abs(cached_layout%cell_hmat - cell%hmat) > layout_tol)) RETURN
501 IF (.NOT. ALLOCATED(cached_layout%atom_coords)) RETURN
502
503 DO iatom = 1, SIZE(particle_set)
504 IF (any(abs(cached_layout%atom_coords(:, iatom) - particle_set(iatom)%r) > layout_tol)) RETURN
505 END DO
506
507 IF (PRESENT(weights)) THEN
508 weights_match = layout_weights_match(pw_grid, weights)
509 ELSE
510 weights_match = layout_weights_match(pw_grid)
511 END IF
512 IF (.NOT. weights_match) RETURN
513
514 matches = .true.
515
516 END FUNCTION layout_cache_matches
517
518! **************************************************************************************************
519!> \brief Check whether current optional integration weights match the cached static tensors.
520!> \param pw_grid ...
521!> \param weights ...
522!> \return ...
523! **************************************************************************************************
524 FUNCTION layout_weights_match(pw_grid, weights) RESULT(matches)
525 TYPE(pw_grid_type), POINTER :: pw_grid
526 TYPE(pw_r3d_rs_type), OPTIONAL, POINTER :: weights
527 LOGICAL :: matches
528
529 LOGICAL :: has_weights
530 REAL(kind=dp) :: weight_sum, weight_sumsq
531
532 matches = .false.
533 mark_used(pw_grid)
534 IF (PRESENT(weights)) THEN
535 CALL weights_signature(weights, has_weights, weight_sum, weight_sumsq)
536 ELSE
537 CALL weights_signature(has_weights=has_weights, weight_sum=weight_sum, &
538 weight_sumsq=weight_sumsq)
539 END IF
540
541 IF (cached_layout%has_weights .NEQV. has_weights) RETURN
542 IF (abs(cached_layout%weight_sum - weight_sum) > layout_tol) RETURN
543 IF (abs(cached_layout%weight_sumsq - weight_sumsq) > layout_tol) RETURN
544
545 matches = .true.
546
547 END FUNCTION layout_weights_match
548
549! **************************************************************************************************
550!> \brief Build the static SKALA layout cache.
551!> \param pw_grid ...
552!> \param particle_set ...
553!> \param cell ...
554!> \param weights ...
555!> \param atom_partition ...
556! **************************************************************************************************
557 SUBROUTINE rebuild_layout_cache(pw_grid, particle_set, cell, weights, atom_partition)
558 TYPE(pw_grid_type), POINTER :: pw_grid
559 TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
560 TYPE(cell_type), POINTER :: cell
561 TYPE(pw_r3d_rs_type), OPTIONAL, POINTER :: weights
562 INTEGER, INTENT(IN), OPTIONAL :: atom_partition
563
564 INTEGER :: feature_local, i, iatom, ipt, j, k, local_row, max_grid_size, max_local_features, &
565 my_atom_partition, natom, nfeature_local, nflat, nflat_local, npoint, nproc, owner, pe, &
566 pe_index, phase_handle, row, source_global, source_local, static_base
567 INTEGER, ALLOCATABLE, DIMENSION(:) :: atom_offset, atom_position, chunk_atom_begin, &
568 chunk_atom_end, cursor, feature_counts, feature_displs, global_owner, &
569 global_source_points, local_feature_counts_tmp, local_owner, local_source_global, &
570 local_source_points, point_counts, point_displs, static_counts, static_displs
571 INTEGER, DIMENSION(2, 3) :: bo
572 LOGICAL :: has_weights
573 REAL(kind=dp) :: base_weight, included_sum, &
574 partition_weight, weight_sum, &
575 weight_sumsq
576 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: distances, global_static, local_static, &
577 partition_weights
578 REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: atom_coords_pbc, atom_image_coords
579 REAL(kind=dp), DIMENSION(3) :: grid_point, owner_coord
580
581 CALL release_layout_cache(cached_layout)
582
583 my_atom_partition = skala_gpw_atom_partition_hard
584 IF (PRESENT(atom_partition)) my_atom_partition = atom_partition
585 natom = SIZE(particle_set)
586 bo = pw_grid%bounds_local
587 nflat_local = pw_grid%ngpts_local
588 nproc = pw_grid%para%group%num_pe
589 pe_index = pw_grid%para%group%mepos + 1
590
591 IF (PRESENT(weights)) THEN
592 CALL weights_signature(weights, has_weights, weight_sum, weight_sumsq)
593 ELSE
594 CALL weights_signature(has_weights=has_weights, weight_sum=weight_sum, &
595 weight_sumsq=weight_sumsq)
596 END IF
597
598 max_local_features = nflat_local
599 IF (my_atom_partition == skala_gpw_atom_partition_smooth) THEN
600 max_local_features = nflat_local*natom
601 END IF
602 ALLOCATE (local_owner(max_local_features), &
603 local_source_points(max_local_features), &
604 local_static(nstatic_per_point*max_local_features), &
605 local_feature_counts_tmp(nflat_local), feature_counts(nproc), &
606 feature_displs(nproc), point_counts(nproc), point_displs(nproc), &
607 static_counts(nproc), static_displs(nproc), atom_coords_pbc(3, natom), &
608 atom_image_coords(3, natom), distances(natom), partition_weights(natom))
609 ALLOCATE (cached_layout%feature_index(bo(1, 1):bo(2, 1), &
610 bo(1, 2):bo(2, 2), &
611 bo(1, 3):bo(2, 3)))
612 cached_layout%feature_index = 0
613 local_static = 0.0_dp
614 local_feature_counts_tmp = 0
615 DO iatom = 1, natom
616 atom_coords_pbc(:, iatom) = pbc(particle_set(iatom)%r, cell, positive_range=.true.)
617 END DO
618
619 CALL timeset("skala_gpw_layout_local", phase_handle)
620 local_row = 0
621 nfeature_local = 0
622 DO k = bo(1, 3), bo(2, 3)
623 DO j = bo(1, 2), bo(2, 2)
624 DO i = bo(1, 1), bo(2, 1)
625 local_row = local_row + 1
626 grid_point = grid_coordinate(pw_grid, [i, j, k])
627 base_weight = pw_grid%dvol
628 IF (PRESENT(weights)) THEN
629 IF (ASSOCIATED(weights)) base_weight = base_weight*weights%array(i, j, k)
630 END IF
631 cached_layout%feature_index(i, j, k) = local_row
632
633 IF (my_atom_partition == skala_gpw_atom_partition_hard) THEN
634 owner = nearest_atom(grid_point, atom_coords_pbc, cell)
635 owner_coord = atom_coords_pbc(:, owner)
636 nfeature_local = nfeature_local + 1
637 local_feature_counts_tmp(local_row) = 1
638 local_owner(nfeature_local) = owner
639 local_source_points(nfeature_local) = local_row
640 static_base = nstatic_per_point*(nfeature_local - 1)
641 local_static(static_base + 1:static_base + 3) = grid_point
642 local_static(static_base + 4) = base_weight
643 local_static(static_base + 5) = base_weight
644 ELSE
645 CALL smooth_atom_partition(grid_point, atom_coords_pbc, cell, &
646 partition_weights, atom_image_coords, distances)
647 included_sum = sum(partition_weights, mask=partition_weights > smooth_partition_eps)
648 IF (included_sum <= 0.0_dp) THEN
649 owner = nearest_atom(grid_point, atom_coords_pbc, cell)
650 partition_weights = 0.0_dp
651 partition_weights(owner) = 1.0_dp
652 included_sum = 1.0_dp
653 END IF
654 DO iatom = 1, natom
655 IF (partition_weights(iatom) <= smooth_partition_eps) cycle
656 partition_weight = partition_weights(iatom)/included_sum
657 nfeature_local = nfeature_local + 1
658 local_feature_counts_tmp(local_row) = &
659 local_feature_counts_tmp(local_row) + 1
660 local_owner(nfeature_local) = iatom
661 local_source_points(nfeature_local) = local_row
662 static_base = nstatic_per_point*(nfeature_local - 1)
663 local_static(static_base + 1:static_base + 3) = grid_point
664 local_static(static_base + 4) = base_weight*partition_weight
665 local_static(static_base + 5) = base_weight
666 END DO
667 END IF
668 END DO
669 END DO
670 END DO
671 CALL timestop(phase_handle)
672
673 ! SKALA groups all grid points by atom. This ordering is static while the
674 ! grid, cell, atom positions, and optional integration weights are unchanged.
675 CALL timeset("skala_gpw_layout_gather", phase_handle)
676 CALL pw_grid%para%group%allgather(nflat_local, point_counts)
677 CALL counts_to_displs(point_counts, point_displs)
678 npoint = sum(point_counts)
679 CALL pw_grid%para%group%allgather(nfeature_local, feature_counts)
680 CALL counts_to_displs(feature_counts, feature_displs)
681 DO pe = 1, nproc
682 static_counts(pe) = nstatic_per_point*feature_counts(pe)
683 static_displs(pe) = nstatic_per_point*feature_displs(pe)
684 END DO
685 nflat = sum(feature_counts)
686 ALLOCATE (global_owner(nflat), global_source_points(nflat), &
687 global_static(nstatic_per_point*nflat), local_source_global(nfeature_local))
688 DO feature_local = 1, nfeature_local
689 local_source_global(feature_local) = point_displs(pe_index) + local_source_points(feature_local)
690 END DO
691 CALL pw_grid%para%group%allgatherv(local_owner(1:nfeature_local), global_owner, feature_counts, &
692 feature_displs)
693 CALL pw_grid%para%group%allgatherv(local_source_global, global_source_points, feature_counts, &
694 feature_displs)
695 CALL pw_grid%para%group%allgatherv(local_static(1:nstatic_per_point*nfeature_local), &
696 global_static, static_counts, &
697 static_displs)
698 CALL timestop(phase_handle)
699
700 ALLOCATE (cached_layout%chunk_feature_counts(nproc), &
701 cached_layout%chunk_feature_displs(nproc), &
702 cached_layout%chunk_grad_counts(nproc), cached_layout%chunk_grad_displs(nproc), &
703 cached_layout%feature_counts(nproc), cached_layout%feature_displs(nproc), &
704 cached_layout%dynamic_counts(nproc), cached_layout%dynamic_displs(nproc), &
705 cached_layout%route_grad_return_recv_counts(nproc), &
706 cached_layout%route_grad_return_recv_displs(nproc), &
707 cached_layout%route_grad_return_send_counts(nproc), &
708 cached_layout%route_grad_return_send_displs(nproc), &
709 cached_layout%route_point_recv_counts(nproc), &
710 cached_layout%route_point_recv_displs(nproc), &
711 cached_layout%route_point_send_counts(nproc), &
712 cached_layout%route_point_send_displs(nproc), &
713 cached_layout%feature_source_points(nflat), &
714 cached_layout%global_to_feature(npoint), cached_layout%atomic_grid_sizes(natom), &
715 cached_layout%local_feature_counts(nflat_local), &
716 cached_layout%local_feature_offsets(nflat_local + 1), &
717 cached_layout%local_feature_rows(nfeature_local), &
718 cached_layout%local_feature_points(nfeature_local), &
719 cached_layout%local_feature_indices(nfeature_local), atom_offset(natom + 1), &
720 atom_position(natom), chunk_atom_begin(nproc), chunk_atom_end(nproc), &
721 cursor(nflat_local))
722 cached_layout%feature_counts(:) = feature_counts
723 cached_layout%feature_displs(:) = feature_displs
724 cached_layout%dynamic_counts(:) = ndynamic_per_point*point_counts
725 cached_layout%dynamic_displs(:) = ndynamic_per_point*point_displs
726 cached_layout%atomic_grid_sizes = 0_int_8
727 cached_layout%global_to_feature = 0
728 cached_layout%local_feature_counts(:) = local_feature_counts_tmp
729 cached_layout%local_feature_offsets(1) = 1
730 DO local_row = 1, nflat_local
731 cached_layout%local_feature_offsets(local_row + 1) = &
732 cached_layout%local_feature_offsets(local_row) + &
733 cached_layout%local_feature_counts(local_row)
734 END DO
735 cursor(:) = cached_layout%local_feature_offsets(1:nflat_local)
736
737 CALL timeset("skala_gpw_layout_atom_sort", phase_handle)
738 DO ipt = 1, nflat
739 cached_layout%atomic_grid_sizes(global_owner(ipt)) = &
740 cached_layout%atomic_grid_sizes(global_owner(ipt)) + 1_int_8
741 END DO
742 atom_offset(1) = 1
743 DO iatom = 1, natom
744 atom_offset(iatom + 1) = atom_offset(iatom) + int(cached_layout%atomic_grid_sizes(iatom))
745 END DO
746 DO iatom = 1, natom
747 atom_position(iatom) = atom_offset(iatom)
748 END DO
749 max_grid_size = maxval(int(cached_layout%atomic_grid_sizes))
750 CALL build_atom_chunks(cached_layout%atomic_grid_sizes, atom_offset, nproc, &
751 chunk_atom_begin, chunk_atom_end, &
752 cached_layout%chunk_feature_counts, &
753 cached_layout%chunk_feature_displs)
754 cached_layout%chunk_grad_counts(:) = ngrad_per_point*cached_layout%chunk_feature_counts
755 cached_layout%chunk_grad_displs(:) = ngrad_per_point*cached_layout%chunk_feature_displs
756 cached_layout%chunk_atom_begin = chunk_atom_begin(pe_index)
757 cached_layout%chunk_atom_end = chunk_atom_end(pe_index)
758 cached_layout%chunk_feature_begin = cached_layout%chunk_feature_displs(pe_index) + 1
759 cached_layout%chunk_feature_count = cached_layout%chunk_feature_counts(pe_index)
760 cached_layout%chunk_natom = cached_layout%chunk_atom_end - &
761 cached_layout%chunk_atom_begin + 1
762
763 ALLOCATE (cached_layout%grid_coords(3, nflat), cached_layout%grid_weights(nflat), &
764 cached_layout%atomic_grid_weights(nflat), &
765 cached_layout%coarse_0_atomic_coords(3, natom), &
766 cached_layout%atomic_grid_size_bound_shape(0, max_grid_size), &
767 cached_layout%atom_coords(3, natom))
768 cached_layout%grid_coords = 0.0_dp
769 cached_layout%grid_weights = 0.0_dp
770 cached_layout%atomic_grid_weights = 0.0_dp
771 cached_layout%atomic_grid_size_bound_shape = 0_int_8
772
773 DO iatom = 1, natom
774 cached_layout%atom_coords(:, iatom) = particle_set(iatom)%r
775 cached_layout%coarse_0_atomic_coords(:, iatom) = atom_coords_pbc(:, iatom)
776 END DO
777
778 DO ipt = 1, nflat
779 owner = global_owner(ipt)
780 row = atom_position(owner)
781 atom_position(owner) = atom_position(owner) + 1
782 source_global = global_source_points(ipt)
783 cached_layout%feature_source_points(row) = source_global
784 IF (cached_layout%global_to_feature(source_global) == 0) THEN
785 cached_layout%global_to_feature(source_global) = row
786 END IF
787 static_base = nstatic_per_point*(ipt - 1)
788 cached_layout%grid_coords(:, row) = global_static(static_base + 1:static_base + 3)
789 cached_layout%grid_weights(row) = global_static(static_base + 4)
790 cached_layout%atomic_grid_weights(row) = global_static(static_base + 5)
791 source_local = source_global - point_displs(pe_index)
792 IF (source_local >= 1 .AND. source_local <= nflat_local) THEN
793 feature_local = cursor(source_local)
794 cursor(source_local) = cursor(source_local) + 1
795 cached_layout%local_feature_rows(feature_local) = row
796 cached_layout%local_feature_points(feature_local) = source_local
797 END IF
798 END DO
799
800 cpassert(all(cached_layout%global_to_feature > 0))
801 cpassert(all(cached_layout%local_feature_rows > 0))
802 cpassert(all(cached_layout%local_feature_points > 0))
803 DO k = bo(1, 3), bo(2, 3)
804 DO j = bo(1, 2), bo(2, 2)
805 DO i = bo(1, 1), bo(2, 1)
806 local_row = cached_layout%feature_index(i, j, k)
807 cached_layout%feature_index(i, j, k) = &
808 cached_layout%local_feature_rows(cached_layout%local_feature_offsets(local_row))
809 END DO
810 END DO
811 END DO
812 DO feature_local = 1, nfeature_local
813 cached_layout%local_feature_indices(feature_local) = &
814 int(cached_layout%local_feature_rows(feature_local) - 1, kind=int_8)
815 END DO
816 CALL timestop(phase_handle)
817 CALL timeset("skala_gpw_layout_chunk_routes", phase_handle)
818 CALL build_atom_chunk_routes(cached_layout, cached_layout%local_feature_rows, &
819 pw_grid%para%group)
820 CALL build_atom_chunk_layout(cached_layout)
821 CALL timestop(phase_handle)
822
823 cached_layout%natom = natom
824 cached_layout%nflat = nflat
825 cached_layout%nflat_local = nflat_local
826 cached_layout%npoint = npoint
827 cached_layout%nproc = nproc
828 cached_layout%atom_partition = my_atom_partition
829 cached_layout%bo = bo
830 cached_layout%bounds = pw_grid%bounds
831 cached_layout%npts = pw_grid%npts
832 cached_layout%dvol = pw_grid%dvol
833 cached_layout%dh = pw_grid%dh
834 cached_layout%cell_hmat = cell%hmat
835 cached_layout%weight_sum = weight_sum
836 cached_layout%weight_sumsq = weight_sumsq
837 cached_layout%has_weights = has_weights
838 CALL timeset("skala_gpw_layout_tensors", phase_handle)
839 CALL build_static_layout_tensors(cached_layout)
840 CALL timestop(phase_handle)
841 cached_layout%active = .true.
842
843 DEALLOCATE (atom_coords_pbc, atom_image_coords, atom_offset, atom_position, &
844 chunk_atom_begin, chunk_atom_end, cursor, feature_counts, feature_displs, &
845 global_owner, global_source_points, global_static, local_feature_counts_tmp, &
846 distances, local_owner, local_source_global, local_source_points, &
847 local_static, partition_weights, point_counts, point_displs, static_counts, &
848 static_displs)
849
850 END SUBROUTINE rebuild_layout_cache
851
852! **************************************************************************************************
853!> \brief Build cached Torch tensors for static SKALA inputs.
854!> \param cache ...
855! **************************************************************************************************
856 SUBROUTINE build_static_layout_tensors(cache)
857 TYPE(skala_gpw_layout_cache_type), INTENT(INOUT) :: cache
858
859 cpassert(.NOT. cache%static_tensors_active)
860
861 CALL torch_tensor_from_array(cache%grid_coords_t, cache%grid_coords)
862 CALL torch_tensor_to_device_leaf(cache%grid_coords_t, .false.)
863 CALL torch_tensor_from_array(cache%grid_weights_t, cache%grid_weights)
864 CALL torch_tensor_to_device_leaf(cache%grid_weights_t, .false.)
865 CALL torch_tensor_from_array(cache%atomic_grid_weights_t, cache%atomic_grid_weights)
866 CALL torch_tensor_to_device_leaf(cache%atomic_grid_weights_t, .false.)
867 CALL torch_tensor_from_array(cache%atomic_grid_sizes_t, cache%atomic_grid_sizes)
868 CALL torch_tensor_to_device_leaf(cache%atomic_grid_sizes_t, .false.)
869 CALL torch_tensor_from_array(cache%coarse_0_atomic_coords_t, cache%coarse_0_atomic_coords)
870 CALL torch_tensor_to_device_leaf(cache%coarse_0_atomic_coords_t, .false.)
871 CALL torch_tensor_from_array(cache%atomic_grid_size_bound_shape_t, &
872 cache%atomic_grid_size_bound_shape)
873 CALL torch_tensor_to_device_leaf(cache%atomic_grid_size_bound_shape_t, .false.)
874 CALL torch_tensor_from_array(cache%local_feature_indices_t, cache%local_feature_indices)
875 CALL torch_tensor_to_device_leaf(cache%local_feature_indices_t, .false.)
876
877 CALL torch_dict_create(cache%static_inputs)
878 CALL torch_dict_insert(cache%static_inputs, "grid_coords", cache%grid_coords_t)
879 CALL torch_dict_insert(cache%static_inputs, "grid_weights", cache%grid_weights_t)
880 CALL torch_dict_insert(cache%static_inputs, "atomic_grid_weights", &
881 cache%atomic_grid_weights_t)
882 CALL torch_dict_insert(cache%static_inputs, "atomic_grid_sizes", &
883 cache%atomic_grid_sizes_t)
884 CALL torch_dict_insert(cache%static_inputs, "atomic_grid_size_bound_shape", &
885 cache%atomic_grid_size_bound_shape_t)
886 cache%static_tensors_active = .true.
887
888 IF (cache%chunk_feature_count > 0) THEN
889 cpassert(.NOT. cache%chunk_static_tensors_active)
890 CALL torch_tensor_from_array(cache%chunk_grid_coords_t, cache%chunk_grid_coords)
891 CALL torch_tensor_to_device_leaf(cache%chunk_grid_coords_t, .false.)
892 CALL torch_tensor_from_array(cache%chunk_grid_weights_t, cache%chunk_grid_weights)
893 CALL torch_tensor_to_device_leaf(cache%chunk_grid_weights_t, .false.)
894 CALL torch_tensor_from_array(cache%chunk_atomic_grid_weights_t, &
895 cache%chunk_atomic_grid_weights)
896 CALL torch_tensor_to_device_leaf(cache%chunk_atomic_grid_weights_t, .false.)
897 CALL torch_tensor_from_array(cache%chunk_atomic_grid_sizes_t, &
898 cache%chunk_atomic_grid_sizes)
899 CALL torch_tensor_to_device_leaf(cache%chunk_atomic_grid_sizes_t, .false.)
900 CALL torch_tensor_from_array(cache%chunk_coarse_0_atomic_coords_t, &
901 cache%chunk_coarse_0_atomic_coords)
902 CALL torch_tensor_to_device_leaf(cache%chunk_coarse_0_atomic_coords_t, .false.)
903 CALL torch_tensor_from_array(cache%chunk_atomic_grid_size_bound_shape_t, &
904 cache%chunk_atomic_grid_size_bound_shape)
905 CALL torch_tensor_to_device_leaf(cache%chunk_atomic_grid_size_bound_shape_t, .false.)
906 CALL torch_tensor_from_array(cache%chunk_feature_indices_t, cache%chunk_feature_indices)
907 CALL torch_tensor_to_device_leaf(cache%chunk_feature_indices_t, .false.)
908
909 CALL torch_dict_create(cache%chunk_static_inputs)
910 CALL torch_dict_insert(cache%chunk_static_inputs, "grid_coords", &
911 cache%chunk_grid_coords_t)
912 CALL torch_dict_insert(cache%chunk_static_inputs, "grid_weights", &
913 cache%chunk_grid_weights_t)
914 CALL torch_dict_insert(cache%chunk_static_inputs, "atomic_grid_weights", &
915 cache%chunk_atomic_grid_weights_t)
916 CALL torch_dict_insert(cache%chunk_static_inputs, "atomic_grid_sizes", &
917 cache%chunk_atomic_grid_sizes_t)
918 CALL torch_dict_insert(cache%chunk_static_inputs, "atomic_grid_size_bound_shape", &
919 cache%chunk_atomic_grid_size_bound_shape_t)
920 cache%chunk_static_tensors_active = .true.
921 END IF
922
923 END SUBROUTINE build_static_layout_tensors
924
925! **************************************************************************************************
926!> \brief Copy static cached layout arrays into a feature bundle.
927!> \param features ...
928!> \param needs_coordinate_array ...
929!> \param needs_grid_coordinate_array ...
930! **************************************************************************************************
931 SUBROUTINE copy_cached_layout(features, needs_coordinate_array, needs_grid_coordinate_array)
932 TYPE(skala_gpw_feature_type), INTENT(INOUT) :: features
933 LOGICAL, INTENT(IN) :: needs_coordinate_array, &
934 needs_grid_coordinate_array
935
936 cpassert(cached_layout%active)
937
938 ALLOCATE (features%feature_index(lbound(cached_layout%feature_index, 1): &
939 ubound(cached_layout%feature_index, 1), &
940 lbound(cached_layout%feature_index, 2): &
941 ubound(cached_layout%feature_index, 2), &
942 lbound(cached_layout%feature_index, 3): &
943 ubound(cached_layout%feature_index, 3)))
944 ALLOCATE (features%grid_weights(cached_layout%nflat))
945 ALLOCATE (features%local_feature_counts(cached_layout%nflat_local), &
946 features%local_feature_offsets(cached_layout%nflat_local + 1), &
947 features%local_feature_rows(SIZE(cached_layout%local_feature_rows)))
948
949 features%feature_index(:, :, :) = cached_layout%feature_index
950 features%grid_weights(:) = cached_layout%grid_weights
951 features%local_feature_counts(:) = cached_layout%local_feature_counts
952 features%local_feature_offsets(:) = cached_layout%local_feature_offsets
953 features%local_feature_rows(:) = cached_layout%local_feature_rows
954 features%nflat = cached_layout%nflat
955 features%nflat_local = cached_layout%nflat_local
956 features%chunk_feature_count = cached_layout%chunk_feature_count
957 features%atom_partition = cached_layout%atom_partition
958 ALLOCATE (features%atomic_grid_sizes(cached_layout%natom))
959 features%atomic_grid_sizes(:) = cached_layout%atomic_grid_sizes
960 IF (needs_grid_coordinate_array) THEN
961 ALLOCATE (features%grid_coords(3, cached_layout%nflat))
962 ALLOCATE (features%atomic_grid_weights(cached_layout%nflat))
963 features%grid_coords(:, :) = cached_layout%grid_coords
964 features%atomic_grid_weights(:) = cached_layout%atomic_grid_weights
965 END IF
966 ALLOCATE (features%chunk_grad_counts(cached_layout%nproc), &
967 features%chunk_grad_displs(cached_layout%nproc), &
968 features%route_grad_return_recv_counts(cached_layout%nproc), &
969 features%route_grad_return_recv_displs(cached_layout%nproc), &
970 features%route_grad_return_send_counts(cached_layout%nproc), &
971 features%route_grad_return_send_displs(cached_layout%nproc), &
972 features%route_point_recv_counts(cached_layout%nproc), &
973 features%route_point_recv_displs(cached_layout%nproc), &
974 features%route_point_send_counts(cached_layout%nproc), &
975 features%route_point_send_displs(cached_layout%nproc), &
976 features%route_send_local_rows(SIZE(cached_layout%route_send_local_rows)))
977 features%chunk_grad_counts(:) = cached_layout%chunk_grad_counts
978 features%chunk_grad_displs(:) = cached_layout%chunk_grad_displs
979 features%route_grad_return_recv_counts(:) = cached_layout%route_grad_return_recv_counts
980 features%route_grad_return_recv_displs(:) = cached_layout%route_grad_return_recv_displs
981 features%route_grad_return_send_counts(:) = cached_layout%route_grad_return_send_counts
982 features%route_grad_return_send_displs(:) = cached_layout%route_grad_return_send_displs
983 features%route_point_recv_counts(:) = cached_layout%route_point_recv_counts
984 features%route_point_recv_displs(:) = cached_layout%route_point_recv_displs
985 features%route_point_send_counts(:) = cached_layout%route_point_send_counts
986 features%route_point_send_displs(:) = cached_layout%route_point_send_displs
987 features%route_send_local_rows(:) = cached_layout%route_send_local_rows
988 IF (needs_coordinate_array) THEN
989 ALLOCATE (features%coarse_0_atomic_coords(3, cached_layout%natom))
990 features%coarse_0_atomic_coords(:, :) = cached_layout%coarse_0_atomic_coords
991 END IF
992
993 END SUBROUTINE copy_cached_layout
994
995! **************************************************************************************************
996!> \brief Split the atom-ordered feature rows into contiguous atom chunks.
997!> \param atomic_grid_sizes ...
998!> \param atom_offset ...
999!> \param nproc ...
1000!> \param chunk_atom_begin ...
1001!> \param chunk_atom_end ...
1002!> \param chunk_feature_counts ...
1003!> \param chunk_feature_displs ...
1004! **************************************************************************************************
1005 SUBROUTINE build_atom_chunks(atomic_grid_sizes, atom_offset, nproc, chunk_atom_begin, &
1006 chunk_atom_end, chunk_feature_counts, chunk_feature_displs)
1007 INTEGER(KIND=int_8), DIMENSION(:), INTENT(IN) :: atomic_grid_sizes
1008 INTEGER, DIMENSION(:), INTENT(IN) :: atom_offset
1009 INTEGER, INTENT(IN) :: nproc
1010 INTEGER, DIMENSION(:), INTENT(OUT) :: chunk_atom_begin, chunk_atom_end, &
1011 chunk_feature_counts, &
1012 chunk_feature_displs
1013
1014 INTEGER :: best_limit, count, displ, end_atom, lower_limit, max_end_atom, midpoint, natom, &
1015 next_atom, next_count, pe, ranks_left, target_chunks, total_count, upper_limit
1016
1017 natom = SIZE(atomic_grid_sizes)
1018 chunk_atom_begin = natom + 1
1019 chunk_atom_end = natom
1020 chunk_feature_counts = 0
1021 chunk_feature_displs = 0
1022 IF (natom == 0) RETURN
1023
1024 target_chunks = min(nproc, natom)
1025 total_count = atom_offset(natom + 1) - 1
1026 lower_limit = maxval(int(atomic_grid_sizes))
1027 lower_limit = max(lower_limit, (total_count + target_chunks - 1)/target_chunks)
1028 upper_limit = total_count
1029 best_limit = upper_limit
1030 DO WHILE (lower_limit <= upper_limit)
1031 midpoint = (lower_limit + upper_limit)/2
1032 IF (atom_chunks_fit_limit(atomic_grid_sizes, midpoint, target_chunks)) THEN
1033 best_limit = midpoint
1034 upper_limit = midpoint - 1
1035 ELSE
1036 lower_limit = midpoint + 1
1037 END IF
1038 END DO
1039
1040 displ = 0
1041 next_atom = 1
1042 DO pe = 1, nproc
1043 chunk_feature_displs(pe) = displ
1044 IF (pe > target_chunks .OR. next_atom > natom) cycle
1045
1046 ranks_left = target_chunks - pe + 1
1047 chunk_atom_begin(pe) = next_atom
1048 max_end_atom = natom - ranks_left + 1
1049 end_atom = next_atom
1050 count = int(atomic_grid_sizes(end_atom))
1051 DO WHILE (end_atom < max_end_atom)
1052 next_count = count + int(atomic_grid_sizes(end_atom + 1))
1053 IF (next_count > best_limit) EXIT
1054 end_atom = end_atom + 1
1055 count = next_count
1056 END DO
1057
1058 chunk_atom_end(pe) = end_atom
1059 chunk_feature_counts(pe) = atom_offset(end_atom + 1) - atom_offset(next_atom)
1060 displ = displ + chunk_feature_counts(pe)
1061 next_atom = end_atom + 1
1062 END DO
1063
1064 cpassert(displ == atom_offset(natom + 1) - 1)
1065
1066 END SUBROUTINE build_atom_chunks
1067
1068! **************************************************************************************************
1069!> \brief Check if contiguous atom chunks can stay below a feature-count limit.
1070!> \param atomic_grid_sizes ...
1071!> \param limit ...
1072!> \param nchunks ...
1073!> \return ...
1074! **************************************************************************************************
1075 FUNCTION atom_chunks_fit_limit(atomic_grid_sizes, limit, nchunks) RESULT(fits)
1076 INTEGER(KIND=int_8), DIMENSION(:), INTENT(IN) :: atomic_grid_sizes
1077 INTEGER, INTENT(IN) :: limit, nchunks
1078 LOGICAL :: fits
1079
1080 INTEGER :: atom_count, chunk_count, iatom, &
1081 used_chunks
1082
1083 fits = .false.
1084 IF (SIZE(atomic_grid_sizes) == 0) THEN
1085 fits = .true.
1086 RETURN
1087 END IF
1088
1089 used_chunks = 1
1090 chunk_count = 0
1091 DO iatom = 1, SIZE(atomic_grid_sizes)
1092 atom_count = int(atomic_grid_sizes(iatom))
1093 IF (atom_count > limit) RETURN
1094 IF (chunk_count + atom_count > limit) THEN
1095 used_chunks = used_chunks + 1
1096 chunk_count = atom_count
1097 ELSE
1098 chunk_count = chunk_count + atom_count
1099 END IF
1100 END DO
1101 fits = used_chunks <= nchunks
1102
1103 END FUNCTION atom_chunks_fit_limit
1104
1105! **************************************************************************************************
1106!> \brief Return the MPI rank owning an atom-ordered feature row.
1107!> \param row ...
1108!> \param counts ...
1109!> \param displs ...
1110!> \return ...
1111! **************************************************************************************************
1112 FUNCTION feature_row_chunk_owner(row, counts, displs) RESULT(owner)
1113 INTEGER, INTENT(IN) :: row
1114 INTEGER, DIMENSION(:), INTENT(IN) :: counts, displs
1115 INTEGER :: owner
1116
1117 INTEGER :: pe
1118
1119 owner = 0
1120 DO pe = 1, SIZE(counts)
1121 IF (row > displs(pe) .AND. row <= displs(pe) + counts(pe)) THEN
1122 owner = pe
1123 RETURN
1124 END IF
1125 END DO
1126
1127 END FUNCTION feature_row_chunk_owner
1128
1129! **************************************************************************************************
1130!> \brief Build zero-based displacement arrays from per-rank counts.
1131!> \param counts ...
1132!> \param displs ...
1133! **************************************************************************************************
1134 SUBROUTINE counts_to_displs(counts, displs)
1135 INTEGER, DIMENSION(:), INTENT(IN) :: counts
1136 INTEGER, DIMENSION(:), INTENT(OUT) :: displs
1137
1138 INTEGER :: pe
1139
1140 displs(1) = 0
1141 DO pe = 2, SIZE(counts)
1142 displs(pe) = displs(pe - 1) + counts(pe - 1)
1143 END DO
1144
1145 END SUBROUTINE counts_to_displs
1146
1147! **************************************************************************************************
1148!> \brief Precompute all-to-all routing between local grid rows and atom chunks.
1149!> \param cache ...
1150!> \param local_to_global ...
1151!> \param group ...
1152! **************************************************************************************************
1153 SUBROUTINE build_atom_chunk_routes(cache, local_to_global, group)
1154 TYPE(skala_gpw_layout_cache_type), INTENT(INOUT) :: cache
1155 INTEGER, DIMENSION(:), INTENT(IN) :: local_to_global
1156
1157 CLASS(mp_comm_type), INTENT(IN) :: group
1158
1159 INTEGER :: chunk_row, dest, local_feature, point_pos, row
1160 INTEGER, ALLOCATABLE, DIMENSION(:) :: cursor, recv_meta, send_meta
1161
1162 ALLOCATE (cache%route_local_dest(SIZE(local_to_global)), &
1163 cache%route_send_local_rows(SIZE(local_to_global)), &
1164 cache%chunk_return_positions(cache%chunk_feature_count), &
1165 cursor(SIZE(cache%route_point_send_counts)))
1166 cache%route_point_send_counts = 0
1167 cache%route_send_local_rows = 0
1168 cache%chunk_return_positions = 0
1169 DO local_feature = 1, SIZE(local_to_global)
1170 dest = feature_row_chunk_owner(local_to_global(local_feature), &
1171 cache%chunk_feature_counts, &
1172 cache%chunk_feature_displs)
1173 cpassert(dest > 0)
1174 cache%route_local_dest(local_feature) = dest
1175 cache%route_point_send_counts(dest) = cache%route_point_send_counts(dest) + 1
1176 END DO
1177 CALL counts_to_displs(cache%route_point_send_counts, cache%route_point_send_displs)
1178 cursor(:) = cache%route_point_send_displs + 1
1179 DO local_feature = 1, SIZE(local_to_global)
1180 dest = cache%route_local_dest(local_feature)
1181 point_pos = cursor(dest)
1182 cursor(dest) = cursor(dest) + 1
1183 cache%route_send_local_rows(point_pos) = cache%local_feature_points(local_feature)
1184 END DO
1185 CALL group%alltoall(cache%route_point_send_counts, cache%route_point_recv_counts, 1)
1186 CALL counts_to_displs(cache%route_point_recv_counts, cache%route_point_recv_displs)
1187
1188 ALLOCATE (send_meta(SIZE(local_to_global)), recv_meta(cache%chunk_feature_count))
1189 cursor(:) = cache%route_point_send_displs + 1
1190 DO local_feature = 1, SIZE(local_to_global)
1191 dest = cache%route_local_dest(local_feature)
1192 point_pos = cursor(dest)
1193 cursor(dest) = cursor(dest) + 1
1194 send_meta(point_pos) = local_to_global(local_feature)
1195 END DO
1196 CALL group%alltoall(send_meta, cache%route_point_send_counts, &
1197 cache%route_point_send_displs, recv_meta, &
1198 cache%route_point_recv_counts, &
1199 cache%route_point_recv_displs)
1200 DO point_pos = 1, cache%chunk_feature_count
1201 row = recv_meta(point_pos)
1202 chunk_row = row - cache%chunk_feature_begin + 1
1203 cpassert(chunk_row >= 1 .AND. chunk_row <= cache%chunk_feature_count)
1204 cache%chunk_return_positions(chunk_row) = point_pos
1205 END DO
1206
1207 cache%route_grad_return_send_counts(:) = ngrad_per_point*cache%route_point_recv_counts
1208 cache%route_grad_return_send_displs(:) = ngrad_per_point*cache%route_point_recv_displs
1209 cache%route_grad_return_recv_counts(:) = ngrad_per_point*cache%route_point_send_counts
1210 cache%route_grad_return_recv_displs(:) = ngrad_per_point*cache%route_point_send_displs
1211
1212 cpassert(sum(cache%route_point_send_counts) == SIZE(local_to_global))
1213 cpassert(sum(cache%route_point_recv_counts) == cache%chunk_feature_count)
1214 cpassert(all(cache%route_send_local_rows > 0))
1215 cpassert(all(cache%chunk_return_positions > 0))
1216
1217 DEALLOCATE (cursor, recv_meta, send_meta)
1218
1219 END SUBROUTINE build_atom_chunk_routes
1220
1221! **************************************************************************************************
1222!> \brief Materialize the current rank's atom chunk static layout.
1223!> \param cache ...
1224! **************************************************************************************************
1225 SUBROUTINE build_atom_chunk_layout(cache)
1226 TYPE(skala_gpw_layout_cache_type), INTENT(INOUT) :: cache
1227
1228 INTEGER :: irow, max_grid_size, row_begin, row_end
1229
1230 IF (cache%chunk_feature_count <= 0 .OR. cache%chunk_natom <= 0) RETURN
1231
1232 row_begin = cache%chunk_feature_begin
1233 row_end = row_begin + cache%chunk_feature_count - 1
1234 ALLOCATE (cache%chunk_grid_coords(3, cache%chunk_feature_count), &
1235 cache%chunk_grid_weights(cache%chunk_feature_count), &
1236 cache%chunk_atomic_grid_weights(cache%chunk_feature_count), &
1237 cache%chunk_atomic_grid_sizes(cache%chunk_natom), &
1238 cache%chunk_coarse_0_atomic_coords(3, cache%chunk_natom), &
1239 cache%chunk_feature_indices(cache%chunk_feature_count))
1240 cache%chunk_grid_coords(:, :) = cache%grid_coords(:, row_begin:row_end)
1241 cache%chunk_grid_weights(:) = cache%grid_weights(row_begin:row_end)
1242 cache%chunk_atomic_grid_weights(:) = cache%atomic_grid_weights(row_begin:row_end)
1243 cache%chunk_atomic_grid_sizes(:) = &
1244 cache%atomic_grid_sizes(cache%chunk_atom_begin:cache%chunk_atom_end)
1245 cache%chunk_coarse_0_atomic_coords(:, :) = &
1246 cache%coarse_0_atomic_coords(:, cache%chunk_atom_begin:cache%chunk_atom_end)
1247
1248 max_grid_size = maxval(int(cache%chunk_atomic_grid_sizes))
1249 ALLOCATE (cache%chunk_atomic_grid_size_bound_shape(0, max_grid_size))
1250 cache%chunk_atomic_grid_size_bound_shape = 0_int_8
1251 DO irow = 1, cache%chunk_feature_count
1252 cache%chunk_feature_indices(irow) = int(irow - 1, kind=int_8)
1253 END DO
1254
1255 END SUBROUTINE build_atom_chunk_layout
1256
1257! **************************************************************************************************
1258!> \brief Send local dynamic feature rows to their atom-chunk owner ranks.
1259!> \param features ...
1260!> \param local_dynamic ...
1261!> \param group ...
1262!> \param collapse_spin_dynamics ...
1263! **************************************************************************************************
1264 SUBROUTINE route_atom_chunk_dynamics(features, local_dynamic, group, collapse_spin_dynamics)
1265 TYPE(skala_gpw_feature_type), INTENT(INOUT) :: features
1266 REAL(kind=dp), DIMENSION(:), INTENT(IN) :: local_dynamic
1267
1268 CLASS(mp_comm_type), INTENT(IN) :: group
1269 LOGICAL, INTENT(IN) :: collapse_spin_dynamics
1270
1271 INTEGER :: chunk_row, dest, dyn_base, local_feature, local_row, &
1272 ndynamic_route_per_point, nrecv, nsend, &
1273 point_pos, src_base
1274 INTEGER, ALLOCATABLE, DIMENSION(:) :: cursor, recv_counts, recv_displs, &
1275 send_counts, send_displs
1276 REAL(kind=dp), ALLOCATABLE, DIMENSION(:) :: recv_dynamic, send_dynamic
1277
1278 nsend = SIZE(cached_layout%route_local_dest)
1279 nrecv = sum(cached_layout%route_point_recv_counts)
1280 cpassert(nsend == SIZE(cached_layout%local_feature_rows))
1281 cpassert(nrecv == cached_layout%chunk_feature_count)
1282 ndynamic_route_per_point = ndynamic_per_point
1283 IF (collapse_spin_dynamics) ndynamic_route_per_point = nrks_dynamic_per_point
1284
1285 ALLOCATE (send_dynamic(max(1, ndynamic_route_per_point*nsend)), &
1286 recv_dynamic(max(1, ndynamic_route_per_point*nrecv)), &
1287 cursor(cached_layout%nproc), send_counts(cached_layout%nproc), &
1288 send_displs(cached_layout%nproc), recv_counts(cached_layout%nproc), &
1289 recv_displs(cached_layout%nproc))
1290 send_counts(:) = ndynamic_route_per_point*cached_layout%route_point_send_counts
1291 send_displs(:) = ndynamic_route_per_point*cached_layout%route_point_send_displs
1292 recv_counts(:) = ndynamic_route_per_point*cached_layout%route_point_recv_counts
1293 recv_displs(:) = ndynamic_route_per_point*cached_layout%route_point_recv_displs
1294 cursor(:) = cached_layout%route_point_send_displs + 1
1295 DO local_feature = 1, nsend
1296 dest = cached_layout%route_local_dest(local_feature)
1297 point_pos = cursor(dest)
1298 cursor(dest) = cursor(dest) + 1
1299 dyn_base = ndynamic_route_per_point*(point_pos - 1)
1300 local_row = cached_layout%local_feature_points(local_feature)
1301 src_base = ndynamic_route_per_point*(local_row - 1)
1302 send_dynamic(dyn_base + 1:dyn_base + ndynamic_route_per_point) = &
1303 local_dynamic(src_base + 1:src_base + ndynamic_route_per_point)
1304 END DO
1305
1306 CALL group%alltoall(send_dynamic, send_counts, send_displs, recv_dynamic, recv_counts, &
1307 recv_displs)
1308
1309 features%uses_collapsed_rks_dynamic = collapse_spin_dynamics
1310 IF (cached_layout%chunk_feature_count > 0) THEN
1311 IF (collapse_spin_dynamics) THEN
1312 ALLOCATE (features%chunk_density(cached_layout%chunk_feature_count, 1), &
1313 features%chunk_grad(cached_layout%chunk_feature_count, 3, 1), &
1314 features%chunk_kin(cached_layout%chunk_feature_count, 1), &
1315 features%chunk_return_positions(cached_layout%chunk_feature_count))
1316 ELSE
1317 ALLOCATE (features%chunk_density(cached_layout%chunk_feature_count, 2), &
1318 features%chunk_grad(cached_layout%chunk_feature_count, 3, 2), &
1319 features%chunk_kin(cached_layout%chunk_feature_count, 2), &
1320 features%chunk_return_positions(cached_layout%chunk_feature_count))
1321 END IF
1322 features%chunk_return_positions(:) = cached_layout%chunk_return_positions
1323
1324 DO chunk_row = 1, cached_layout%chunk_feature_count
1325 point_pos = cached_layout%chunk_return_positions(chunk_row)
1326 cpassert(point_pos >= 1 .AND. point_pos <= cached_layout%chunk_feature_count)
1327 dyn_base = ndynamic_route_per_point*(point_pos - 1)
1328 IF (collapse_spin_dynamics) THEN
1329 features%chunk_density(chunk_row, 1) = recv_dynamic(dyn_base + 1)
1330 features%chunk_grad(chunk_row, 1, 1) = recv_dynamic(dyn_base + 2)
1331 features%chunk_grad(chunk_row, 2, 1) = recv_dynamic(dyn_base + 3)
1332 features%chunk_grad(chunk_row, 3, 1) = recv_dynamic(dyn_base + 4)
1333 features%chunk_kin(chunk_row, 1) = recv_dynamic(dyn_base + 5)
1334 ELSE
1335 features%chunk_density(chunk_row, :) = recv_dynamic(dyn_base + 1:dyn_base + 2)
1336 features%chunk_grad(chunk_row, 1, 1) = recv_dynamic(dyn_base + 3)
1337 features%chunk_grad(chunk_row, 2, 1) = recv_dynamic(dyn_base + 4)
1338 features%chunk_grad(chunk_row, 3, 1) = recv_dynamic(dyn_base + 5)
1339 features%chunk_grad(chunk_row, 1, 2) = recv_dynamic(dyn_base + 6)
1340 features%chunk_grad(chunk_row, 2, 2) = recv_dynamic(dyn_base + 7)
1341 features%chunk_grad(chunk_row, 3, 2) = recv_dynamic(dyn_base + 8)
1342 features%chunk_kin(chunk_row, :) = recv_dynamic(dyn_base + 9:dyn_base + 10)
1343 END IF
1344 END DO
1345 cpassert(all(features%chunk_return_positions > 0))
1346 END IF
1347
1348 DEALLOCATE (cursor, recv_counts, recv_displs, recv_dynamic, send_counts, send_displs, &
1349 send_dynamic)
1350
1351 END SUBROUTINE route_atom_chunk_dynamics
1352
1353! **************************************************************************************************
1354!> \brief Extract the current rank's atom chunk from the global dynamic feature arrays.
1355!> \param features ...
1356! **************************************************************************************************
1357 SUBROUTINE extract_atom_chunk_dynamics(features)
1358 TYPE(skala_gpw_feature_type), INTENT(INOUT) :: features
1359
1360 INTEGER :: row_begin, row_end
1361
1362 cpassert(cached_layout%chunk_feature_count > 0)
1363 row_begin = cached_layout%chunk_feature_begin
1364 row_end = row_begin + cached_layout%chunk_feature_count - 1
1365 ALLOCATE (features%chunk_density(cached_layout%chunk_feature_count, 2), &
1366 features%chunk_grad(cached_layout%chunk_feature_count, 3, 2), &
1367 features%chunk_kin(cached_layout%chunk_feature_count, 2))
1368 features%chunk_density(:, :) = features%density(row_begin:row_end, :)
1369 features%chunk_grad(:, :, :) = features%grad(row_begin:row_end, :, :)
1370 features%chunk_kin(:, :) = features%kin(row_begin:row_end, :)
1371
1372 END SUBROUTINE extract_atom_chunk_dynamics
1373
1374! **************************************************************************************************
1375!> \brief Compute a local signature for optional integration weights.
1376!> \param weights ...
1377!> \param has_weights ...
1378!> \param weight_sum ...
1379!> \param weight_sumsq ...
1380! **************************************************************************************************
1381 SUBROUTINE weights_signature(weights, has_weights, weight_sum, weight_sumsq)
1382 TYPE(pw_r3d_rs_type), OPTIONAL, POINTER :: weights
1383 LOGICAL, INTENT(OUT) :: has_weights
1384 REAL(kind=dp), INTENT(OUT) :: weight_sum, weight_sumsq
1385
1386 has_weights = .false.
1387 weight_sum = 0.0_dp
1388 weight_sumsq = 0.0_dp
1389 IF (PRESENT(weights)) THEN
1390 IF (ASSOCIATED(weights)) THEN
1391 has_weights = .true.
1392 weight_sum = sum(weights%array)
1393 weight_sumsq = sum(weights%array*weights%array)
1394 END IF
1395 END IF
1396
1397 END SUBROUTINE weights_signature
1398
1399! **************************************************************************************************
1400!> \brief Release cached layout arrays.
1401!> \param cache ...
1402! **************************************************************************************************
1403 SUBROUTINE release_layout_cache(cache)
1404 TYPE(skala_gpw_layout_cache_type), INTENT(INOUT) :: cache
1405
1406 IF (cache%inputs_active) THEN
1407 CALL torch_dict_release(cache%inputs)
1408 cache%inputs_active = .false.
1409 END IF
1410
1411 IF (cache%chunk_inputs_active) THEN
1412 CALL torch_dict_release(cache%chunk_inputs)
1413 cache%chunk_inputs_active = .false.
1414 END IF
1415
1416 IF (cache%dynamic_tensors_active) THEN
1417 CALL torch_tensor_release(cache%density_t)
1418 CALL torch_tensor_release(cache%grad_t)
1419 CALL torch_tensor_release(cache%kin_t)
1420 cache%dynamic_tensors_active = .false.
1421 END IF
1422
1423 IF (cache%chunk_dynamic_tensors_active) THEN
1424 IF (cache%chunk_dynamic_input_views_active) THEN
1425 CALL torch_tensor_release(cache%chunk_density_input_t)
1426 CALL torch_tensor_release(cache%chunk_grad_input_t)
1427 CALL torch_tensor_release(cache%chunk_kin_input_t)
1428 cache%chunk_dynamic_input_views_active = .false.
1429 END IF
1430 CALL torch_tensor_release(cache%chunk_density_t)
1431 CALL torch_tensor_release(cache%chunk_grad_t)
1432 CALL torch_tensor_release(cache%chunk_kin_t)
1433 cache%chunk_dynamic_tensors_active = .false.
1434 END IF
1435
1436 IF (cache%static_tensors_active) THEN
1437 CALL torch_tensor_release(cache%grid_coords_t)
1438 CALL torch_tensor_release(cache%grid_weights_t)
1439 CALL torch_tensor_release(cache%atomic_grid_weights_t)
1440 CALL torch_tensor_release(cache%atomic_grid_sizes_t)
1441 CALL torch_tensor_release(cache%coarse_0_atomic_coords_t)
1442 CALL torch_tensor_release(cache%atomic_grid_size_bound_shape_t)
1443 CALL torch_tensor_release(cache%local_feature_indices_t)
1444 CALL torch_dict_release(cache%static_inputs)
1445 cache%static_tensors_active = .false.
1446 END IF
1447
1448 IF (cache%chunk_static_tensors_active) THEN
1449 CALL torch_tensor_release(cache%chunk_grid_coords_t)
1450 CALL torch_tensor_release(cache%chunk_grid_weights_t)
1451 CALL torch_tensor_release(cache%chunk_atomic_grid_weights_t)
1452 CALL torch_tensor_release(cache%chunk_atomic_grid_sizes_t)
1453 CALL torch_tensor_release(cache%chunk_coarse_0_atomic_coords_t)
1454 CALL torch_tensor_release(cache%chunk_atomic_grid_size_bound_shape_t)
1455 CALL torch_tensor_release(cache%chunk_feature_indices_t)
1456 CALL torch_dict_release(cache%chunk_static_inputs)
1457 cache%chunk_static_tensors_active = .false.
1458 END IF
1459
1460 IF (ALLOCATED(cache%chunk_feature_counts)) DEALLOCATE (cache%chunk_feature_counts)
1461 IF (ALLOCATED(cache%chunk_feature_displs)) DEALLOCATE (cache%chunk_feature_displs)
1462 IF (ALLOCATED(cache%chunk_grad_counts)) DEALLOCATE (cache%chunk_grad_counts)
1463 IF (ALLOCATED(cache%chunk_grad_displs)) DEALLOCATE (cache%chunk_grad_displs)
1464 IF (ALLOCATED(cache%route_grad_return_recv_counts)) THEN
1465 DEALLOCATE (cache%route_grad_return_recv_counts)
1466 END IF
1467 IF (ALLOCATED(cache%route_grad_return_recv_displs)) THEN
1468 DEALLOCATE (cache%route_grad_return_recv_displs)
1469 END IF
1470 IF (ALLOCATED(cache%route_grad_return_send_counts)) THEN
1471 DEALLOCATE (cache%route_grad_return_send_counts)
1472 END IF
1473 IF (ALLOCATED(cache%route_grad_return_send_displs)) THEN
1474 DEALLOCATE (cache%route_grad_return_send_displs)
1475 END IF
1476 IF (ALLOCATED(cache%route_local_dest)) DEALLOCATE (cache%route_local_dest)
1477 IF (ALLOCATED(cache%chunk_return_positions)) DEALLOCATE (cache%chunk_return_positions)
1478 IF (ALLOCATED(cache%route_point_recv_counts)) DEALLOCATE (cache%route_point_recv_counts)
1479 IF (ALLOCATED(cache%route_point_recv_displs)) DEALLOCATE (cache%route_point_recv_displs)
1480 IF (ALLOCATED(cache%route_point_send_counts)) DEALLOCATE (cache%route_point_send_counts)
1481 IF (ALLOCATED(cache%route_point_send_displs)) DEALLOCATE (cache%route_point_send_displs)
1482 IF (ALLOCATED(cache%route_send_local_rows)) DEALLOCATE (cache%route_send_local_rows)
1483 IF (ALLOCATED(cache%dynamic_counts)) DEALLOCATE (cache%dynamic_counts)
1484 IF (ALLOCATED(cache%dynamic_displs)) DEALLOCATE (cache%dynamic_displs)
1485 IF (ALLOCATED(cache%feature_counts)) DEALLOCATE (cache%feature_counts)
1486 IF (ALLOCATED(cache%feature_displs)) DEALLOCATE (cache%feature_displs)
1487 IF (ALLOCATED(cache%feature_source_points)) DEALLOCATE (cache%feature_source_points)
1488 IF (ALLOCATED(cache%global_to_feature)) DEALLOCATE (cache%global_to_feature)
1489 IF (ALLOCATED(cache%feature_index)) DEALLOCATE (cache%feature_index)
1490 IF (ALLOCATED(cache%atomic_grid_sizes)) DEALLOCATE (cache%atomic_grid_sizes)
1491 IF (ALLOCATED(cache%chunk_atomic_grid_sizes)) DEALLOCATE (cache%chunk_atomic_grid_sizes)
1492 IF (ALLOCATED(cache%chunk_feature_indices)) DEALLOCATE (cache%chunk_feature_indices)
1493 IF (ALLOCATED(cache%local_feature_counts)) DEALLOCATE (cache%local_feature_counts)
1494 IF (ALLOCATED(cache%local_feature_indices)) DEALLOCATE (cache%local_feature_indices)
1495 IF (ALLOCATED(cache%local_feature_offsets)) DEALLOCATE (cache%local_feature_offsets)
1496 IF (ALLOCATED(cache%local_feature_points)) DEALLOCATE (cache%local_feature_points)
1497 IF (ALLOCATED(cache%local_feature_rows)) DEALLOCATE (cache%local_feature_rows)
1498 IF (ALLOCATED(cache%atomic_grid_size_bound_shape)) THEN
1499 DEALLOCATE (cache%atomic_grid_size_bound_shape)
1500 END IF
1501 IF (ALLOCATED(cache%chunk_atomic_grid_size_bound_shape)) THEN
1502 DEALLOCATE (cache%chunk_atomic_grid_size_bound_shape)
1503 END IF
1504 IF (ALLOCATED(cache%atomic_grid_weights)) DEALLOCATE (cache%atomic_grid_weights)
1505 IF (ALLOCATED(cache%chunk_atomic_grid_weights)) DEALLOCATE (cache%chunk_atomic_grid_weights)
1506 IF (ALLOCATED(cache%chunk_grid_weights)) DEALLOCATE (cache%chunk_grid_weights)
1507 IF (ALLOCATED(cache%grid_weights)) DEALLOCATE (cache%grid_weights)
1508 IF (ALLOCATED(cache%atom_coords)) DEALLOCATE (cache%atom_coords)
1509 IF (ALLOCATED(cache%chunk_coarse_0_atomic_coords)) THEN
1510 DEALLOCATE (cache%chunk_coarse_0_atomic_coords)
1511 END IF
1512 IF (ALLOCATED(cache%coarse_0_atomic_coords)) DEALLOCATE (cache%coarse_0_atomic_coords)
1513 IF (ALLOCATED(cache%chunk_grid_coords)) DEALLOCATE (cache%chunk_grid_coords)
1514 IF (ALLOCATED(cache%grid_coords)) DEALLOCATE (cache%grid_coords)
1515
1516 cache%chunk_atom_begin = 1
1517 cache%chunk_atom_end = 0
1518 cache%chunk_feature_begin = 1
1519 cache%chunk_feature_count = 0
1520 cache%chunk_natom = 0
1521 cache%natom = 0
1522 cache%nflat = 0
1523 cache%nflat_local = 0
1524 cache%npoint = 0
1525 cache%nproc = 0
1526 cache%atom_partition = skala_gpw_atom_partition_hard
1527 cache%bo = 0
1528 cache%bounds = 0
1529 cache%npts = 0
1530 cache%dvol = 0.0_dp
1531 cache%weight_sum = 0.0_dp
1532 cache%weight_sumsq = 0.0_dp
1533 cache%cell_hmat = 0.0_dp
1534 cache%dh = 0.0_dp
1535 cache%active = .false.
1536 cache%has_weights = .false.
1537 cache%chunk_dynamic_tensors_active = .false.
1538 cache%chunk_dynamic_input_views_active = .false.
1539 cache%chunk_inputs_active = .false.
1540 cache%chunk_inputs_use_collapsed_rks = .false.
1541 cache%chunk_static_tensors_active = .false.
1542 cache%dynamic_tensors_active = .false.
1543 cache%inputs_active = .false.
1544 cache%static_tensors_active = .false.
1545
1546 END SUBROUTINE release_layout_cache
1547
1548! **************************************************************************************************
1549!> \brief Release Torch objects and backing arrays owned by a feature bundle.
1550!> \param features ...
1551! **************************************************************************************************
1552 SUBROUTINE skala_gpw_feature_release(features)
1553 TYPE(skala_gpw_feature_type), INTENT(INOUT) :: features
1554
1555 IF (features%active) THEN
1556 IF (features%owns_dynamic_tensors) THEN
1557 IF (features%uses_collapsed_rks_dynamic) THEN
1558 CALL torch_tensor_release(features%density_input_t)
1559 CALL torch_tensor_release(features%grad_input_t)
1560 CALL torch_tensor_release(features%kin_input_t)
1561 END IF
1562 CALL torch_tensor_release(features%density_t)
1563 CALL torch_tensor_release(features%grad_t)
1564 CALL torch_tensor_release(features%kin_t)
1565 END IF
1566 IF (features%owns_static_tensors) THEN
1567 CALL torch_tensor_release(features%grid_coords_t)
1568 CALL torch_tensor_release(features%grid_weights_t)
1569 CALL torch_tensor_release(features%atomic_grid_weights_t)
1570 CALL torch_tensor_release(features%atomic_grid_sizes_t)
1571 CALL torch_tensor_release(features%atomic_grid_size_bound_shape_t)
1572 END IF
1573 IF (features%owns_grid_coordinate_tensor) THEN
1574 CALL torch_tensor_release(features%grid_coords_t)
1575 END IF
1576 IF (features%owns_weight_tensors) THEN
1577 CALL torch_tensor_release(features%grid_weights_t)
1578 CALL torch_tensor_release(features%atomic_grid_weights_t)
1579 END IF
1580 IF (features%owns_static_tensors .OR. features%owns_coordinate_tensor) THEN
1581 CALL torch_tensor_release(features%coarse_0_atomic_coords_t)
1582 END IF
1583 IF (features%owns_inputs) CALL torch_dict_release(features%inputs)
1584 features%active = .false.
1585 features%owns_coordinate_tensor = .false.
1586 features%owns_grid_coordinate_tensor = .false.
1587 features%owns_weight_tensors = .false.
1588 features%owns_dynamic_tensors = .true.
1589 features%owns_inputs = .true.
1590 features%owns_static_tensors = .true.
1591 features%uses_atom_chunk_routing = .false.
1592 features%uses_atom_chunks = .false.
1593 features%uses_collapsed_rks_dynamic = .false.
1594 END IF
1595
1596 IF (ALLOCATED(features%chunk_density)) DEALLOCATE (features%chunk_density)
1597 IF (ALLOCATED(features%chunk_grad)) DEALLOCATE (features%chunk_grad)
1598 IF (ALLOCATED(features%chunk_kin)) DEALLOCATE (features%chunk_kin)
1599 IF (ALLOCATED(features%density)) DEALLOCATE (features%density)
1600 IF (ALLOCATED(features%grad)) DEALLOCATE (features%grad)
1601 IF (ALLOCATED(features%kin)) DEALLOCATE (features%kin)
1602 IF (ALLOCATED(features%chunk_grad_counts)) DEALLOCATE (features%chunk_grad_counts)
1603 IF (ALLOCATED(features%chunk_grad_displs)) DEALLOCATE (features%chunk_grad_displs)
1604 IF (ALLOCATED(features%chunk_return_positions)) DEALLOCATE (features%chunk_return_positions)
1605 IF (ALLOCATED(features%route_grad_return_recv_counts)) THEN
1606 DEALLOCATE (features%route_grad_return_recv_counts)
1607 END IF
1608 IF (ALLOCATED(features%route_grad_return_recv_displs)) THEN
1609 DEALLOCATE (features%route_grad_return_recv_displs)
1610 END IF
1611 IF (ALLOCATED(features%route_grad_return_send_counts)) THEN
1612 DEALLOCATE (features%route_grad_return_send_counts)
1613 END IF
1614 IF (ALLOCATED(features%route_grad_return_send_displs)) THEN
1615 DEALLOCATE (features%route_grad_return_send_displs)
1616 END IF
1617 IF (ALLOCATED(features%route_point_recv_counts)) THEN
1618 DEALLOCATE (features%route_point_recv_counts)
1619 END IF
1620 IF (ALLOCATED(features%route_point_recv_displs)) THEN
1621 DEALLOCATE (features%route_point_recv_displs)
1622 END IF
1623 IF (ALLOCATED(features%route_point_send_counts)) THEN
1624 DEALLOCATE (features%route_point_send_counts)
1625 END IF
1626 IF (ALLOCATED(features%route_point_send_displs)) THEN
1627 DEALLOCATE (features%route_point_send_displs)
1628 END IF
1629 IF (ALLOCATED(features%route_send_local_rows)) DEALLOCATE (features%route_send_local_rows)
1630 IF (ALLOCATED(features%feature_index)) DEALLOCATE (features%feature_index)
1631 IF (ALLOCATED(features%local_feature_counts)) DEALLOCATE (features%local_feature_counts)
1632 IF (ALLOCATED(features%local_feature_offsets)) DEALLOCATE (features%local_feature_offsets)
1633 IF (ALLOCATED(features%local_feature_rows)) DEALLOCATE (features%local_feature_rows)
1634 IF (ALLOCATED(features%grid_coords)) DEALLOCATE (features%grid_coords)
1635 IF (ALLOCATED(features%grid_weights)) DEALLOCATE (features%grid_weights)
1636 IF (ALLOCATED(features%atomic_grid_weights)) DEALLOCATE (features%atomic_grid_weights)
1637 IF (ALLOCATED(features%atomic_grid_sizes)) DEALLOCATE (features%atomic_grid_sizes)
1638 IF (ALLOCATED(features%coarse_0_atomic_coords)) DEALLOCATE (features%coarse_0_atomic_coords)
1639 IF (ALLOCATED(features%atomic_grid_size_bound_shape)) THEN
1640 DEALLOCATE (features%atomic_grid_size_bound_shape)
1641 END IF
1642 features%chunk_feature_count = 0
1643 features%nflat = 0
1644 features%nflat_local = 0
1645 features%atom_partition = skala_gpw_atom_partition_hard
1646 features%uses_atom_chunk_routing = .false.
1647 features%uses_collapsed_rks_dynamic = .false.
1648
1649 END SUBROUTINE skala_gpw_feature_release
1650
1651! **************************************************************************************************
1652!> \brief Return how many atom-contiguous subchunks the cached rank chunk needs.
1653!> \param max_rows ...
1654!> \return ...
1655! **************************************************************************************************
1656 FUNCTION skala_gpw_atom_subchunk_count(max_rows) RESULT(nsubchunks)
1657 INTEGER, INTENT(IN) :: max_rows
1658 INTEGER :: nsubchunks
1659
1660 INTEGER :: atom_rows, iatom, rows
1661
1662 nsubchunks = 0
1663 IF (.NOT. cached_layout%active) RETURN
1664 IF (cached_layout%chunk_natom <= 0) RETURN
1665 IF (max_rows <= 0) THEN
1666 nsubchunks = 1
1667 RETURN
1668 END IF
1669
1670 rows = 0
1671 DO iatom = 1, cached_layout%chunk_natom
1672 atom_rows = int(cached_layout%chunk_atomic_grid_sizes(iatom))
1673 IF (rows > 0 .AND. rows + atom_rows > max_rows) THEN
1674 nsubchunks = nsubchunks + 1
1675 rows = 0
1676 END IF
1677 rows = rows + atom_rows
1678 END DO
1679 IF (rows > 0) nsubchunks = nsubchunks + 1
1680 nsubchunks = max(1, nsubchunks)
1681
1683
1684! **************************************************************************************************
1685!> \brief Build an atom-contiguous subchunk feature bundle from a rank-local atom chunk.
1686!> \param parent ...
1687!> \param features ...
1688!> \param subchunk_index ...
1689!> \param max_rows ...
1690!> \param requires_grad ...
1691! **************************************************************************************************
1692 SUBROUTINE skala_gpw_feature_build_atom_subchunk(parent, features, subchunk_index, &
1693 max_rows, requires_grad)
1694 TYPE(skala_gpw_feature_type), INTENT(IN) :: parent
1695 TYPE(skala_gpw_feature_type), INTENT(INOUT) :: features
1696 INTEGER, INTENT(IN) :: subchunk_index, max_rows
1697 LOGICAL, INTENT(IN) :: requires_grad
1698
1699 INTEGER :: atom_begin, atom_count, atom_end, &
1700 max_grid_size, row_begin, row_count, &
1701 row_end
1702
1703 CALL skala_gpw_feature_release(features)
1704 cpassert(parent%uses_atom_chunks)
1705 CALL atom_subchunk_bounds(subchunk_index, max_rows, atom_begin, atom_end, &
1706 row_begin, row_end)
1707 atom_count = atom_end - atom_begin + 1
1708 row_count = row_end - row_begin + 1
1709 cpassert(atom_count > 0)
1710 cpassert(row_count > 0)
1711 mark_used(requires_grad)
1712 max_grid_size = maxval(int(cached_layout%chunk_atomic_grid_sizes(atom_begin:atom_end)))
1713
1714 ALLOCATE (features%atomic_grid_size_bound_shape(0, max_grid_size))
1715 features%atomic_grid_size_bound_shape = 0_int_8
1716
1717 features%chunk_feature_count = row_count
1718 features%nflat = parent%nflat
1719 features%nflat_local = parent%nflat_local
1720 features%grid_weight_sum = sum(cached_layout%chunk_grid_weights(row_begin:row_end))
1721 features%uses_atom_chunks = .true.
1722 features%uses_atom_chunk_routing = parent%uses_atom_chunk_routing
1723 CALL add_subchunk_feature_tensors(parent, features, atom_begin, atom_count, row_begin, &
1724 row_count)
1725 features%active = .true.
1726
1728
1729! **************************************************************************************************
1730!> \brief Return atom and row bounds for an atom-contiguous rank-local subchunk.
1731!> \param subchunk_index ...
1732!> \param max_rows ...
1733!> \param atom_begin ...
1734!> \param atom_end ...
1735!> \param row_begin ...
1736!> \param row_end ...
1737! **************************************************************************************************
1738 SUBROUTINE atom_subchunk_bounds(subchunk_index, max_rows, atom_begin, atom_end, &
1739 row_begin, row_end)
1740 INTEGER, INTENT(IN) :: subchunk_index, max_rows
1741 INTEGER, INTENT(OUT) :: atom_begin, atom_end, row_begin, row_end
1742
1743 INTEGER :: atom_rows, current_subchunk, iatom, &
1744 row_cursor, rows
1745
1746 cpassert(subchunk_index > 0)
1747 cpassert(max_rows > 0)
1748 cpassert(cached_layout%chunk_natom > 0)
1749
1750 atom_begin = 1
1751 atom_end = 0
1752 row_begin = 1
1753 row_end = 0
1754 current_subchunk = 1
1755 row_cursor = 1
1756 rows = 0
1757 DO iatom = 1, cached_layout%chunk_natom
1758 atom_rows = int(cached_layout%chunk_atomic_grid_sizes(iatom))
1759 IF (rows > 0 .AND. rows + atom_rows > max_rows) THEN
1760 IF (current_subchunk == subchunk_index) THEN
1761 atom_end = iatom - 1
1762 row_end = row_cursor - 1
1763 RETURN
1764 END IF
1765 current_subchunk = current_subchunk + 1
1766 atom_begin = iatom
1767 row_begin = row_cursor
1768 rows = 0
1769 END IF
1770 rows = rows + atom_rows
1771 row_cursor = row_cursor + atom_rows
1772 END DO
1773
1774 IF (current_subchunk == subchunk_index) THEN
1775 atom_end = cached_layout%chunk_natom
1776 row_end = row_cursor - 1
1777 RETURN
1778 END IF
1779
1780 cpabort("Requested native SKALA atom subchunk does not exist.")
1781
1782 END SUBROUTINE atom_subchunk_bounds
1783
1784! **************************************************************************************************
1785!> \brief Insert a subchunk into a Torch dictionary using static views of the cached chunk tensors.
1786!> \param parent ...
1787!> \param features ...
1788!> \param atom_begin ...
1789!> \param atom_count ...
1790!> \param row_begin ...
1791!> \param row_count ...
1792! **************************************************************************************************
1793 SUBROUTINE add_subchunk_feature_tensors(parent, features, atom_begin, atom_count, row_begin, &
1794 row_count)
1795 TYPE(skala_gpw_feature_type), INTENT(IN) :: parent
1796 TYPE(skala_gpw_feature_type), INTENT(INOUT) :: features
1797 INTEGER, INTENT(IN) :: atom_begin, atom_count, row_begin, &
1798 row_count
1799
1800 cpassert(cached_layout%chunk_static_tensors_active)
1801 cpassert(parent%active)
1802 cpassert(ALLOCATED(features%atomic_grid_size_bound_shape))
1803
1804 features%owns_coordinate_tensor = .false.
1805 features%owns_dynamic_tensors = .true.
1806 features%owns_inputs = .true.
1807 features%owns_static_tensors = .true.
1808 features%uses_collapsed_rks_dynamic = parent%uses_collapsed_rks_dynamic
1809
1810 CALL torch_tensor_narrow(cached_layout%chunk_grid_coords_t, 0, row_begin - 1, &
1811 row_count, features%grid_coords_t)
1812 CALL torch_tensor_narrow(cached_layout%chunk_grid_weights_t, 0, row_begin - 1, &
1813 row_count, features%grid_weights_t)
1814 CALL torch_tensor_narrow(cached_layout%chunk_atomic_grid_weights_t, 0, row_begin - 1, &
1815 row_count, features%atomic_grid_weights_t)
1816 CALL torch_tensor_narrow(cached_layout%chunk_atomic_grid_sizes_t, 0, atom_begin - 1, &
1817 atom_count, features%atomic_grid_sizes_t)
1818 CALL torch_tensor_narrow(cached_layout%chunk_coarse_0_atomic_coords_t, 0, &
1819 atom_begin - 1, atom_count, features%coarse_0_atomic_coords_t)
1820 CALL torch_tensor_from_array(features%atomic_grid_size_bound_shape_t, &
1821 features%atomic_grid_size_bound_shape)
1822 CALL torch_tensor_to_device_leaf(features%atomic_grid_size_bound_shape_t, .false.)
1823 CALL torch_tensor_narrow(parent%density_t, 1, row_begin - 1, row_count, &
1824 features%density_t)
1825 CALL torch_tensor_narrow(parent%grad_t, 2, row_begin - 1, row_count, features%grad_t)
1826 CALL torch_tensor_narrow(parent%kin_t, 1, row_begin - 1, row_count, features%kin_t)
1827 IF (features%uses_collapsed_rks_dynamic) THEN
1828 CALL torch_tensor_expand_dim(features%density_t, 0, 2, features%density_input_t)
1829 CALL torch_tensor_expand_dim(features%grad_t, 0, 2, features%grad_input_t)
1830 CALL torch_tensor_expand_dim(features%kin_t, 0, 2, features%kin_input_t)
1831 END IF
1832
1833 CALL torch_dict_create(features%inputs)
1834 CALL torch_dict_insert(features%inputs, "grid_coords", features%grid_coords_t)
1835 CALL torch_dict_insert(features%inputs, "grid_weights", features%grid_weights_t)
1836 CALL torch_dict_insert(features%inputs, "atomic_grid_weights", &
1837 features%atomic_grid_weights_t)
1838 CALL torch_dict_insert(features%inputs, "atomic_grid_sizes", &
1839 features%atomic_grid_sizes_t)
1840 CALL torch_dict_insert(features%inputs, "atomic_grid_size_bound_shape", &
1841 features%atomic_grid_size_bound_shape_t)
1842 IF (features%uses_collapsed_rks_dynamic) THEN
1843 CALL torch_dict_insert(features%inputs, "density", features%density_input_t)
1844 CALL torch_dict_insert(features%inputs, "grad", features%grad_input_t)
1845 CALL torch_dict_insert(features%inputs, "kin", features%kin_input_t)
1846 ELSE
1847 CALL torch_dict_insert(features%inputs, "density", features%density_t)
1848 CALL torch_dict_insert(features%inputs, "grad", features%grad_t)
1849 CALL torch_dict_insert(features%inputs, "kin", features%kin_t)
1850 END IF
1851 CALL torch_dict_insert(features%inputs, "coarse_0_atomic_coords", &
1852 features%coarse_0_atomic_coords_t)
1853
1854 END SUBROUTINE add_subchunk_feature_tensors
1855
1856! **************************************************************************************************
1857!> \brief Insert owned subchunk arrays into a Torch dictionary.
1858!> \param features ...
1859!> \param requires_grad ...
1860! **************************************************************************************************
1861 SUBROUTINE add_owned_feature_tensors(features, requires_grad)
1862 TYPE(skala_gpw_feature_type), INTENT(INOUT) :: features
1863 LOGICAL, INTENT(IN) :: requires_grad
1864
1865 cpassert(ALLOCATED(features%chunk_density))
1866 cpassert(ALLOCATED(features%chunk_grad))
1867 cpassert(ALLOCATED(features%chunk_kin))
1868 cpassert(ALLOCATED(features%grid_coords))
1869 cpassert(ALLOCATED(features%grid_weights))
1870 cpassert(ALLOCATED(features%atomic_grid_weights))
1871 cpassert(ALLOCATED(features%atomic_grid_sizes))
1872 cpassert(ALLOCATED(features%atomic_grid_size_bound_shape))
1873 cpassert(ALLOCATED(features%coarse_0_atomic_coords))
1874
1875 features%owns_coordinate_tensor = .false.
1876 features%owns_dynamic_tensors = .true.
1877 features%owns_inputs = .true.
1878 features%owns_static_tensors = .true.
1879
1880 CALL torch_tensor_from_array(features%grid_coords_t, features%grid_coords)
1881 CALL torch_tensor_to_device_leaf(features%grid_coords_t, .false.)
1882 CALL torch_tensor_from_array(features%grid_weights_t, features%grid_weights)
1883 CALL torch_tensor_to_device_leaf(features%grid_weights_t, .false.)
1884 CALL torch_tensor_from_array(features%atomic_grid_weights_t, features%atomic_grid_weights)
1885 CALL torch_tensor_to_device_leaf(features%atomic_grid_weights_t, .false.)
1886 CALL torch_tensor_from_array(features%atomic_grid_sizes_t, features%atomic_grid_sizes)
1887 CALL torch_tensor_to_device_leaf(features%atomic_grid_sizes_t, .false.)
1888 CALL torch_tensor_from_array(features%coarse_0_atomic_coords_t, &
1889 features%coarse_0_atomic_coords)
1890 CALL torch_tensor_to_device_leaf(features%coarse_0_atomic_coords_t, .false.)
1891 CALL torch_tensor_from_array(features%atomic_grid_size_bound_shape_t, &
1892 features%atomic_grid_size_bound_shape)
1893 CALL torch_tensor_to_device_leaf(features%atomic_grid_size_bound_shape_t, .false.)
1894 CALL torch_tensor_from_array(features%density_t, features%chunk_density)
1895 CALL torch_tensor_to_device_leaf(features%density_t, requires_grad)
1896 CALL torch_tensor_from_array(features%grad_t, features%chunk_grad)
1897 CALL torch_tensor_to_device_leaf(features%grad_t, requires_grad)
1898 CALL torch_tensor_from_array(features%kin_t, features%chunk_kin)
1899 CALL torch_tensor_to_device_leaf(features%kin_t, requires_grad)
1900
1901 CALL torch_dict_create(features%inputs)
1902 CALL torch_dict_insert(features%inputs, "grid_coords", features%grid_coords_t)
1903 CALL torch_dict_insert(features%inputs, "grid_weights", features%grid_weights_t)
1904 CALL torch_dict_insert(features%inputs, "atomic_grid_weights", &
1905 features%atomic_grid_weights_t)
1906 CALL torch_dict_insert(features%inputs, "atomic_grid_sizes", &
1907 features%atomic_grid_sizes_t)
1908 CALL torch_dict_insert(features%inputs, "atomic_grid_size_bound_shape", &
1909 features%atomic_grid_size_bound_shape_t)
1910 CALL torch_dict_insert(features%inputs, "density", features%density_t)
1911 CALL torch_dict_insert(features%inputs, "grad", features%grad_t)
1912 CALL torch_dict_insert(features%inputs, "kin", features%kin_t)
1913 CALL torch_dict_insert(features%inputs, "coarse_0_atomic_coords", &
1914 features%coarse_0_atomic_coords_t)
1915
1916 END SUBROUTINE add_owned_feature_tensors
1917
1918! **************************************************************************************************
1919!> \brief Insert all SKALA feature tensors into the Torch dictionary.
1920!> \param features ...
1921!> \param requires_grad ...
1922!> \param requires_coordinate_grad ...
1923!> \param requires_stress_grad ...
1924!> \param use_atom_chunks ...
1925!> \param requires_weight_grad ...
1926! **************************************************************************************************
1927 SUBROUTINE add_feature_tensors(features, requires_grad, requires_coordinate_grad, &
1928 requires_stress_grad, use_atom_chunks, requires_weight_grad)
1929 TYPE(skala_gpw_feature_type), INTENT(INOUT) :: features
1930 LOGICAL, INTENT(IN) :: requires_grad, requires_coordinate_grad, &
1931 requires_stress_grad, use_atom_chunks
1932 LOGICAL, INTENT(IN), OPTIONAL :: requires_weight_grad
1933
1934 LOGICAL :: my_requires_weight_grad
1935
1936 my_requires_weight_grad = .false.
1937 IF (PRESENT(requires_weight_grad)) my_requires_weight_grad = requires_weight_grad
1938
1939 cpassert(cached_layout%static_tensors_active)
1940 features%owns_static_tensors = .false.
1941 features%owns_coordinate_tensor = .false.
1942 features%owns_grid_coordinate_tensor = .false.
1943 features%owns_weight_tensors = .false.
1944 features%owns_dynamic_tensors = .false.
1945 features%owns_inputs = .true.
1946 IF (use_atom_chunks) THEN
1947 cpassert(.NOT. requires_coordinate_grad)
1948 cpassert(.NOT. requires_stress_grad)
1949 cpassert(.NOT. my_requires_weight_grad)
1950 cpassert(cached_layout%chunk_static_tensors_active)
1951 features%grid_coords_t = cached_layout%chunk_grid_coords_t
1952 features%grid_weights_t = cached_layout%chunk_grid_weights_t
1953 features%atomic_grid_weights_t = cached_layout%chunk_atomic_grid_weights_t
1954 features%atomic_grid_sizes_t = cached_layout%chunk_atomic_grid_sizes_t
1955 features%atomic_grid_size_bound_shape_t = &
1956 cached_layout%chunk_atomic_grid_size_bound_shape_t
1957 features%local_feature_indices_t = cached_layout%chunk_feature_indices_t
1958
1959 IF (cached_layout%chunk_inputs_active .AND. &
1960 (cached_layout%chunk_inputs_use_collapsed_rks .NEQV. &
1961 features%uses_collapsed_rks_dynamic)) THEN
1962 CALL torch_dict_release(cached_layout%chunk_inputs)
1963 cached_layout%chunk_inputs_active = .false.
1964 END IF
1965 IF (.NOT. features%uses_collapsed_rks_dynamic .AND. &
1966 cached_layout%chunk_dynamic_input_views_active) THEN
1967 CALL torch_tensor_release(cached_layout%chunk_density_input_t)
1968 CALL torch_tensor_release(cached_layout%chunk_grad_input_t)
1969 CALL torch_tensor_release(cached_layout%chunk_kin_input_t)
1970 cached_layout%chunk_dynamic_input_views_active = .false.
1971 END IF
1972
1973 CALL torch_tensor_reset_from_array(cached_layout%chunk_density_t, &
1974 features%chunk_density, requires_grad=requires_grad)
1975 features%density_t = cached_layout%chunk_density_t
1976 CALL torch_tensor_reset_from_array(cached_layout%chunk_grad_t, features%chunk_grad, &
1977 requires_grad=requires_grad)
1978 features%grad_t = cached_layout%chunk_grad_t
1979 CALL torch_tensor_reset_from_array(cached_layout%chunk_kin_t, features%chunk_kin, &
1980 requires_grad=requires_grad)
1981 features%kin_t = cached_layout%chunk_kin_t
1982 cached_layout%chunk_dynamic_tensors_active = .true.
1983
1984 IF (features%uses_collapsed_rks_dynamic .AND. &
1985 .NOT. cached_layout%chunk_dynamic_input_views_active) THEN
1986 CALL torch_tensor_expand_dim(cached_layout%chunk_density_t, 0, 2, &
1987 cached_layout%chunk_density_input_t)
1988 CALL torch_tensor_expand_dim(cached_layout%chunk_grad_t, 0, 2, &
1989 cached_layout%chunk_grad_input_t)
1990 CALL torch_tensor_expand_dim(cached_layout%chunk_kin_t, 0, 2, &
1991 cached_layout%chunk_kin_input_t)
1992 cached_layout%chunk_dynamic_input_views_active = .true.
1993 END IF
1994 IF (features%uses_collapsed_rks_dynamic) THEN
1995 features%density_input_t = cached_layout%chunk_density_input_t
1996 features%grad_input_t = cached_layout%chunk_grad_input_t
1997 features%kin_input_t = cached_layout%chunk_kin_input_t
1998 END IF
1999
2000 IF (.NOT. cached_layout%chunk_inputs_active) THEN
2001 CALL torch_dict_clone(cached_layout%chunk_static_inputs, cached_layout%chunk_inputs)
2002 IF (features%uses_collapsed_rks_dynamic) THEN
2003 CALL torch_dict_insert(cached_layout%chunk_inputs, "density", &
2004 features%density_input_t)
2005 CALL torch_dict_insert(cached_layout%chunk_inputs, "grad", &
2006 features%grad_input_t)
2007 CALL torch_dict_insert(cached_layout%chunk_inputs, "kin", &
2008 features%kin_input_t)
2009 ELSE
2010 CALL torch_dict_insert(cached_layout%chunk_inputs, "density", &
2011 cached_layout%chunk_density_t)
2012 CALL torch_dict_insert(cached_layout%chunk_inputs, "grad", &
2013 cached_layout%chunk_grad_t)
2014 CALL torch_dict_insert(cached_layout%chunk_inputs, "kin", &
2015 cached_layout%chunk_kin_t)
2016 END IF
2017 CALL torch_dict_insert(cached_layout%chunk_inputs, "coarse_0_atomic_coords", &
2018 cached_layout%chunk_coarse_0_atomic_coords_t)
2019 cached_layout%chunk_inputs_use_collapsed_rks = features%uses_collapsed_rks_dynamic
2020 cached_layout%chunk_inputs_active = .true.
2021 END IF
2022 features%inputs = cached_layout%chunk_inputs
2023 features%owns_inputs = .false.
2024 features%coarse_0_atomic_coords_t = cached_layout%chunk_coarse_0_atomic_coords_t
2025 ELSE
2026 IF (.NOT. requires_stress_grad .AND. .NOT. my_requires_weight_grad) THEN
2027 features%grid_coords_t = cached_layout%grid_coords_t
2028 features%grid_weights_t = cached_layout%grid_weights_t
2029 features%atomic_grid_weights_t = cached_layout%atomic_grid_weights_t
2030 END IF
2031 features%atomic_grid_sizes_t = cached_layout%atomic_grid_sizes_t
2032 features%atomic_grid_size_bound_shape_t = cached_layout%atomic_grid_size_bound_shape_t
2033 features%local_feature_indices_t = cached_layout%local_feature_indices_t
2034
2035 CALL torch_tensor_reset_from_array(cached_layout%density_t, features%density, &
2036 requires_grad=requires_grad)
2037 features%density_t = cached_layout%density_t
2038 CALL torch_tensor_reset_from_array(cached_layout%grad_t, features%grad, &
2039 requires_grad=requires_grad)
2040 features%grad_t = cached_layout%grad_t
2041 CALL torch_tensor_reset_from_array(cached_layout%kin_t, features%kin, &
2042 requires_grad=requires_grad)
2043 features%kin_t = cached_layout%kin_t
2044 cached_layout%dynamic_tensors_active = .true.
2045
2046 IF (requires_coordinate_grad .OR. requires_stress_grad .OR. my_requires_weight_grad) THEN
2047 IF (requires_stress_grad .OR. my_requires_weight_grad) THEN
2048 CALL torch_dict_create(features%inputs)
2049 IF (requires_stress_grad) THEN
2050 CALL torch_tensor_from_array(features%grid_coords_t, features%grid_coords)
2051 CALL torch_tensor_to_device_leaf(features%grid_coords_t, .true.)
2052 CALL torch_dict_insert(features%inputs, "grid_coords", features%grid_coords_t)
2053 features%owns_grid_coordinate_tensor = .true.
2054 ELSE
2055 features%grid_coords_t = cached_layout%grid_coords_t
2056 CALL torch_dict_insert(features%inputs, "grid_coords", features%grid_coords_t)
2057 END IF
2058 CALL torch_tensor_from_array(features%grid_weights_t, features%grid_weights)
2059 CALL torch_tensor_to_device_leaf(features%grid_weights_t, .true.)
2060 CALL torch_tensor_from_array(features%atomic_grid_weights_t, &
2061 features%atomic_grid_weights)
2062 CALL torch_tensor_to_device_leaf(features%atomic_grid_weights_t, .true.)
2063 CALL torch_dict_insert(features%inputs, "grid_weights", features%grid_weights_t)
2064 CALL torch_dict_insert(features%inputs, "atomic_grid_weights", &
2065 features%atomic_grid_weights_t)
2066 CALL torch_dict_insert(features%inputs, "atomic_grid_sizes", &
2067 features%atomic_grid_sizes_t)
2068 CALL torch_dict_insert(features%inputs, "atomic_grid_size_bound_shape", &
2069 features%atomic_grid_size_bound_shape_t)
2070 features%owns_weight_tensors = .true.
2071 ELSE
2072 CALL torch_dict_clone(cached_layout%static_inputs, features%inputs)
2073 END IF
2074 CALL torch_dict_insert(features%inputs, "density", features%density_t)
2075 CALL torch_dict_insert(features%inputs, "grad", features%grad_t)
2076 CALL torch_dict_insert(features%inputs, "kin", features%kin_t)
2077 ELSE
2078 IF (.NOT. cached_layout%inputs_active) THEN
2079 CALL torch_dict_clone(cached_layout%static_inputs, cached_layout%inputs)
2080 CALL torch_dict_insert(cached_layout%inputs, "density", cached_layout%density_t)
2081 CALL torch_dict_insert(cached_layout%inputs, "grad", cached_layout%grad_t)
2082 CALL torch_dict_insert(cached_layout%inputs, "kin", cached_layout%kin_t)
2083 CALL torch_dict_insert(cached_layout%inputs, "coarse_0_atomic_coords", &
2084 cached_layout%coarse_0_atomic_coords_t)
2085 cached_layout%inputs_active = .true.
2086 END IF
2087 features%inputs = cached_layout%inputs
2088 features%owns_inputs = .false.
2089 features%coarse_0_atomic_coords_t = cached_layout%coarse_0_atomic_coords_t
2090 END IF
2091 END IF
2092
2093 IF (requires_coordinate_grad .OR. requires_stress_grad) THEN
2094 cpassert(.NOT. use_atom_chunks)
2095 CALL torch_tensor_from_array(features%coarse_0_atomic_coords_t, &
2096 features%coarse_0_atomic_coords)
2097 CALL torch_tensor_to_device_leaf(features%coarse_0_atomic_coords_t, .true.)
2098 CALL torch_dict_insert(features%inputs, "coarse_0_atomic_coords", &
2099 features%coarse_0_atomic_coords_t)
2100 features%owns_coordinate_tensor = .true.
2101 END IF
2102
2103 END SUBROUTINE add_feature_tensors
2104
2105! **************************************************************************************************
2106!> \brief Return the Cartesian coordinate of a regular GPW grid point.
2107!> \param pw_grid ...
2108!> \param index ...
2109!> \return ...
2110! **************************************************************************************************
2111 FUNCTION grid_coordinate(pw_grid, index) RESULT(coord)
2112 TYPE(pw_grid_type), POINTER :: pw_grid
2113 INTEGER, DIMENSION(3), INTENT(IN) :: index
2114 REAL(kind=dp), DIMENSION(3) :: coord
2115
2116 INTEGER, DIMENSION(3) :: relative_index
2117
2118 relative_index = index - pw_grid%bounds(1, :)
2119 coord = real(relative_index(1), kind=dp)*pw_grid%dh(:, 1) + &
2120 REAL(relative_index(2), kind=dp)*pw_grid%dh(:, 2) + &
2121 REAL(relative_index(3), kind=dp)*pw_grid%dh(:, 3)
2122
2123 END FUNCTION grid_coordinate
2124
2125! **************************************************************************************************
2126!> \brief Build Becke-like smooth atom weights for one native-grid point.
2127!> \param grid_point ...
2128!> \param atom_coords ...
2129!> \param cell ...
2130!> \param weights ...
2131!> \param atom_image_coords ...
2132!> \param distances ...
2133! **************************************************************************************************
2134 SUBROUTINE smooth_atom_partition(grid_point, atom_coords, cell, weights, atom_image_coords, &
2135 distances)
2136 REAL(kind=dp), DIMENSION(3), INTENT(IN) :: grid_point
2137 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: atom_coords
2138 TYPE(cell_type), POINTER :: cell
2139 REAL(kind=dp), DIMENSION(:), INTENT(OUT) :: weights
2140 REAL(kind=dp), DIMENSION(:, :), INTENT(OUT) :: atom_image_coords
2141 REAL(kind=dp), DIMENSION(:), INTENT(OUT) :: distances
2142
2143 INTEGER :: iatom, jatom, natom
2144 REAL(kind=dp) :: mu, rab, rsum, switch, total
2145 REAL(kind=dp), DIMENSION(3) :: rij
2146 REAL(kind=dp), DIMENSION(3, SIZE(atom_coords, 2)) :: partition_atom_coords
2147
2148 natom = SIZE(atom_coords, 2)
2149 cpassert(SIZE(weights) == natom)
2150 cpassert(SIZE(atom_image_coords, 1) == 3)
2151 cpassert(SIZE(atom_image_coords, 2) == natom)
2152 cpassert(SIZE(distances) == natom)
2153
2154 DO iatom = 1, natom
2155 atom_image_coords(:, iatom) = &
2156 nearest_image_coordinate(atom_coords(:, iatom), grid_point, cell)
2157 partition_atom_coords(:, iatom) = &
2158 nearest_atom_image_coordinate(atom_coords(:, iatom), grid_point, cell)
2159 rij = grid_point - partition_atom_coords(:, iatom)
2160 distances(iatom) = sqrt(sum(rij**2))
2161 END DO
2162
2163 weights = 1.0_dp
2164 DO iatom = 1, natom - 1
2165 DO jatom = iatom + 1, natom
2166 rij = partition_atom_coords(:, iatom) - partition_atom_coords(:, jatom)
2167 rab = sqrt(sum(rij**2))
2168 IF (rab <= layout_tol) cycle
2169 mu = (distances(iatom) - distances(jatom))/rab
2170 mu = max(-1.0_dp, min(1.0_dp, mu))
2171 switch = 0.5_dp*(1.0_dp - becke_shape(mu))
2172 weights(iatom) = weights(iatom)*switch
2173 weights(jatom) = weights(jatom)*(1.0_dp - switch)
2174 END DO
2175 END DO
2176
2177 total = sum(weights)
2178 IF (total > 0.0_dp) THEN
2179 weights = weights/total
2180 ELSE
2181 rsum = huge(1.0_dp)
2182 jatom = 1
2183 DO iatom = 1, natom
2184 IF (distances(iatom) < rsum) THEN
2185 rsum = distances(iatom)
2186 jatom = iatom
2187 END IF
2188 END DO
2189 weights = 0.0_dp
2190 weights(jatom) = 1.0_dp
2191 END IF
2192
2193 END SUBROUTINE smooth_atom_partition
2194
2195! **************************************************************************************************
2196!> \brief Build smooth atom weights and their atom/cell deformation derivatives.
2197!> \param grid_point ...
2198!> \param atom_coords ...
2199!> \param cell ...
2200!> \param weights ...
2201!> \param included ...
2202!> \param dweights_datom ...
2203!> \param dweights_dstrain ...
2204! **************************************************************************************************
2205 SUBROUTINE skala_gpw_smooth_partition_derivatives(grid_point, atom_coords, cell, &
2206 weights, included, dweights_datom, &
2207 dweights_dstrain)
2208 REAL(kind=dp), DIMENSION(3), INTENT(IN) :: grid_point
2209 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: atom_coords
2210 TYPE(cell_type), POINTER :: cell
2211 REAL(kind=dp), DIMENSION(:), INTENT(OUT) :: weights
2212 LOGICAL, DIMENSION(:), INTENT(OUT) :: included
2213 REAL(kind=dp), DIMENSION(:, :, :), INTENT(OUT) :: dweights_datom, dweights_dstrain
2214
2215 INTEGER :: iatom, idir, jatom, jdir, natom
2216 REAL(kind=dp) :: dist_diff, ds_dmu, included_sum, mu, &
2217 mu_raw, one_minus_switch, rab, rsum, &
2218 switch, total
2219 REAL(kind=dp), DIMENSION(3) :: dmu_atom_i, dmu_atom_j, ds_atom_i, &
2220 ds_atom_j, pair, unit_pair
2221 REAL(kind=dp), DIMENSION(3, 3) :: dmu_strain, ds_strain, mean_strain
2222 REAL(kind=dp), DIMENSION(3, SIZE(atom_coords, 2), & SIZE(atom_coords, 2)) :: log_weight_atom
2223 REAL(kind=dp), DIMENSION(3, SIZE(atom_coords, 2)) :: mean_atom, partition_atom_coords, rvecs, &
2224 unit_rvecs
2225 REAL(kind=dp), &
2226 DIMENSION(3, 3, SIZE(atom_coords, 2)) :: log_weight_strain
2227 REAL(kind=dp), DIMENSION(SIZE(atom_coords, 2)) :: distances, normalized_weights, &
2228 raw_weights
2229
2230 natom = SIZE(atom_coords, 2)
2231 cpassert(SIZE(weights) == natom)
2232 cpassert(SIZE(included) == natom)
2233 cpassert(SIZE(dweights_datom, 1) == 3)
2234 cpassert(SIZE(dweights_datom, 2) == natom)
2235 cpassert(SIZE(dweights_datom, 3) == natom)
2236 cpassert(SIZE(dweights_dstrain, 1) == 3)
2237 cpassert(SIZE(dweights_dstrain, 2) == 3)
2238 cpassert(SIZE(dweights_dstrain, 3) == natom)
2239
2240 weights = 0.0_dp
2241 included = .false.
2242 dweights_datom = 0.0_dp
2243 dweights_dstrain = 0.0_dp
2244 raw_weights = 1.0_dp
2245 log_weight_atom = 0.0_dp
2246 log_weight_strain = 0.0_dp
2247
2248 DO iatom = 1, natom
2249 partition_atom_coords(:, iatom) = &
2250 nearest_atom_image_coordinate(atom_coords(:, iatom), grid_point, cell)
2251 rvecs(:, iatom) = grid_point - partition_atom_coords(:, iatom)
2252 distances(iatom) = sqrt(sum(rvecs(:, iatom)**2))
2253 IF (distances(iatom) > layout_tol) THEN
2254 unit_rvecs(:, iatom) = rvecs(:, iatom)/distances(iatom)
2255 ELSE
2256 unit_rvecs(:, iatom) = 0.0_dp
2257 END IF
2258 END DO
2259
2260 DO iatom = 1, natom - 1
2261 DO jatom = iatom + 1, natom
2262 pair = partition_atom_coords(:, iatom) - partition_atom_coords(:, jatom)
2263 rab = sqrt(sum(pair**2))
2264 IF (rab <= layout_tol) cycle
2265 unit_pair = pair/rab
2266 dist_diff = distances(iatom) - distances(jatom)
2267 mu_raw = dist_diff/rab
2268 mu = max(-1.0_dp, min(1.0_dp, mu_raw))
2269 switch = 0.5_dp*(1.0_dp - becke_shape(mu))
2270 one_minus_switch = 1.0_dp - switch
2271
2272 IF (abs(mu_raw) < 1.0_dp) THEN
2273 ds_dmu = -0.5_dp*becke_shape_derivative(mu)
2274 ELSE
2275 ds_dmu = 0.0_dp
2276 END IF
2277 IF (abs(ds_dmu) > 0.0_dp .AND. switch > tiny(1.0_dp) .AND. &
2278 one_minus_switch > tiny(1.0_dp)) THEN
2279 dmu_atom_i = (-unit_rvecs(:, iatom)*rab - dist_diff*unit_pair)/rab**2
2280 dmu_atom_j = (unit_rvecs(:, jatom)*rab + dist_diff*unit_pair)/rab**2
2281 ds_atom_i = ds_dmu*dmu_atom_i
2282 ds_atom_j = ds_dmu*dmu_atom_j
2283 log_weight_atom(:, iatom, iatom) = &
2284 log_weight_atom(:, iatom, iatom) + ds_atom_i/switch
2285 log_weight_atom(:, iatom, jatom) = &
2286 log_weight_atom(:, iatom, jatom) - ds_atom_i/one_minus_switch
2287 log_weight_atom(:, jatom, iatom) = &
2288 log_weight_atom(:, jatom, iatom) + ds_atom_j/switch
2289 log_weight_atom(:, jatom, jatom) = &
2290 log_weight_atom(:, jatom, jatom) - ds_atom_j/one_minus_switch
2291
2292 DO idir = 1, 3
2293 DO jdir = 1, 3
2294 dmu_strain(idir, jdir) = &
2295 ((unit_rvecs(idir, iatom)*rvecs(jdir, iatom) - &
2296 unit_rvecs(idir, jatom)*rvecs(jdir, jatom))*rab - &
2297 dist_diff*unit_pair(idir)*pair(jdir))/rab**2
2298 END DO
2299 END DO
2300 ds_strain = ds_dmu*dmu_strain
2301 log_weight_strain(:, :, iatom) = &
2302 log_weight_strain(:, :, iatom) + ds_strain/switch
2303 log_weight_strain(:, :, jatom) = &
2304 log_weight_strain(:, :, jatom) - ds_strain/one_minus_switch
2305 END IF
2306
2307 raw_weights(iatom) = raw_weights(iatom)*switch
2308 raw_weights(jatom) = raw_weights(jatom)*one_minus_switch
2309 END DO
2310 END DO
2311
2312 total = sum(raw_weights)
2313 IF (total > 0.0_dp) THEN
2314 normalized_weights = raw_weights/total
2315 included = normalized_weights > smooth_partition_eps
2316 ELSE
2317 rsum = huge(1.0_dp)
2318 jatom = 1
2319 DO iatom = 1, natom
2320 IF (distances(iatom) < rsum) THEN
2321 rsum = distances(iatom)
2322 jatom = iatom
2323 END IF
2324 END DO
2325 included(jatom) = .true.
2326 weights(jatom) = 1.0_dp
2327 RETURN
2328 END IF
2329
2330 included_sum = sum(raw_weights, mask=included)
2331 IF (included_sum <= 0.0_dp) THEN
2332 rsum = huge(1.0_dp)
2333 jatom = 1
2334 DO iatom = 1, natom
2335 IF (distances(iatom) < rsum) THEN
2336 rsum = distances(iatom)
2337 jatom = iatom
2338 END IF
2339 END DO
2340 included = .false.
2341 included(jatom) = .true.
2342 weights = 0.0_dp
2343 weights(jatom) = 1.0_dp
2344 RETURN
2345 END IF
2346
2347 DO iatom = 1, natom
2348 IF (included(iatom)) weights(iatom) = raw_weights(iatom)/included_sum
2349 END DO
2350
2351 mean_atom = 0.0_dp
2352 mean_strain = 0.0_dp
2353 DO iatom = 1, natom
2354 IF (.NOT. included(iatom)) cycle
2355 mean_strain = mean_strain + weights(iatom)*log_weight_strain(:, :, iatom)
2356 DO jatom = 1, natom
2357 mean_atom(:, jatom) = mean_atom(:, jatom) + &
2358 weights(iatom)*log_weight_atom(:, jatom, iatom)
2359 END DO
2360 END DO
2361
2362 DO iatom = 1, natom
2363 IF (.NOT. included(iatom)) cycle
2364 dweights_dstrain(:, :, iatom) = &
2365 weights(iatom)*(log_weight_strain(:, :, iatom) - mean_strain)
2366 DO jatom = 1, natom
2367 dweights_datom(:, jatom, iatom) = &
2368 weights(iatom)*(log_weight_atom(:, jatom, iatom) - mean_atom(:, jatom))
2369 END DO
2370 END DO
2371
2373
2374! **************************************************************************************************
2375!> \brief Becke fuzzy-cell shape function.
2376!> \param mu ...
2377!> \return ...
2378! **************************************************************************************************
2379 PURE FUNCTION becke_shape(mu) RESULT(val)
2380 REAL(kind=dp), INTENT(IN) :: mu
2381 REAL(kind=dp) :: val
2382
2383 INTEGER :: iter
2384
2385 val = mu
2386 DO iter = 1, 3
2387 val = 0.5_dp*val*(3.0_dp - val*val)
2388 END DO
2389
2390 END FUNCTION becke_shape
2391
2392! **************************************************************************************************
2393!> \brief Derivative of the Becke fuzzy-cell shape function.
2394!> \param mu ...
2395!> \return ...
2396! **************************************************************************************************
2397 PURE FUNCTION becke_shape_derivative(mu) RESULT(val)
2398 REAL(kind=dp), INTENT(IN) :: mu
2399 REAL(kind=dp) :: val
2400
2401 INTEGER :: iter
2402 REAL(kind=dp) :: x
2403
2404 x = mu
2405 val = 1.0_dp
2406 DO iter = 1, 3
2407 val = val*1.5_dp*(1.0_dp - x*x)
2408 x = 0.5_dp*x*(3.0_dp - x*x)
2409 END DO
2410
2411 END FUNCTION becke_shape_derivative
2412
2413! **************************************************************************************************
2414!> \brief Return the atom image nearest to a regular-grid point.
2415!> \param atom_coord ...
2416!> \param grid_point ...
2417!> \param cell ...
2418!> \return ...
2419! **************************************************************************************************
2420 FUNCTION nearest_atom_image_coordinate(atom_coord, grid_point, cell) RESULT(coord)
2421 REAL(kind=dp), DIMENSION(3), INTENT(IN) :: atom_coord, grid_point
2422 TYPE(cell_type), POINTER :: cell
2423 REAL(kind=dp), DIMENSION(3) :: coord
2424
2425 REAL(kind=dp) :: dx, dy, dz
2426
2427 IF (cell%orthorhombic) THEN
2428 dx = atom_coord(1) - grid_point(1)
2429 dy = atom_coord(2) - grid_point(2)
2430 dz = atom_coord(3) - grid_point(3)
2431 dx = dx - cell%hmat(1, 1)*cell%perd(1)*anint(cell%h_inv(1, 1)*dx)
2432 dy = dy - cell%hmat(2, 2)*cell%perd(2)*anint(cell%h_inv(2, 2)*dy)
2433 dz = dz - cell%hmat(3, 3)*cell%perd(3)*anint(cell%h_inv(3, 3)*dz)
2434 coord = grid_point + [dx, dy, dz]
2435 ELSE
2436 coord = grid_point + pbc(grid_point, atom_coord, cell)
2437 END IF
2438
2439 END FUNCTION nearest_atom_image_coordinate
2440
2441! **************************************************************************************************
2442!> \brief Return the grid-point image nearest to the owning atom coordinate.
2443!> \param owner_coord ...
2444!> \param grid_point ...
2445!> \param cell ...
2446!> \return ...
2447! **************************************************************************************************
2448 FUNCTION nearest_image_coordinate(owner_coord, grid_point, cell) RESULT(coord)
2449 REAL(kind=dp), DIMENSION(3), INTENT(IN) :: owner_coord, grid_point
2450 TYPE(cell_type), POINTER :: cell
2451 REAL(kind=dp), DIMENSION(3) :: coord
2452
2453 REAL(kind=dp) :: dx, dy, dz
2454
2455 IF (cell%orthorhombic) THEN
2456 dx = grid_point(1) - owner_coord(1)
2457 dy = grid_point(2) - owner_coord(2)
2458 dz = grid_point(3) - owner_coord(3)
2459 dx = dx - cell%hmat(1, 1)*cell%perd(1)*anint(cell%h_inv(1, 1)*dx)
2460 dy = dy - cell%hmat(2, 2)*cell%perd(2)*anint(cell%h_inv(2, 2)*dy)
2461 dz = dz - cell%hmat(3, 3)*cell%perd(3)*anint(cell%h_inv(3, 3)*dz)
2462 coord = owner_coord + [dx, dy, dz]
2463 ELSE
2464 coord = owner_coord + pbc(owner_coord, grid_point, cell)
2465 END IF
2466
2467 END FUNCTION nearest_image_coordinate
2468
2469! **************************************************************************************************
2470!> \brief Assign a grid point to the nearest periodic atom.
2471!> \param grid_point ...
2472!> \param atom_coords ...
2473!> \param cell ...
2474!> \return ...
2475! **************************************************************************************************
2476 FUNCTION nearest_atom(grid_point, atom_coords, cell) RESULT(owner)
2477 REAL(kind=dp), DIMENSION(3), INTENT(IN) :: grid_point
2478 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: atom_coords
2479 TYPE(cell_type), POINTER :: cell
2480 INTEGER :: owner
2481
2482 INTEGER :: iatom
2483 REAL(kind=dp) :: best_r2, dx, dy, dz, r2
2484 REAL(kind=dp), DIMENSION(3) :: rij
2485
2486 owner = 1
2487 best_r2 = huge(1.0_dp)
2488 IF (cell%orthorhombic) THEN
2489 DO iatom = 1, SIZE(atom_coords, 2)
2490 dx = grid_point(1) - atom_coords(1, iatom)
2491 dy = grid_point(2) - atom_coords(2, iatom)
2492 dz = grid_point(3) - atom_coords(3, iatom)
2493 dx = dx - cell%hmat(1, 1)*cell%perd(1)*anint(cell%h_inv(1, 1)*dx)
2494 dy = dy - cell%hmat(2, 2)*cell%perd(2)*anint(cell%h_inv(2, 2)*dy)
2495 dz = dz - cell%hmat(3, 3)*cell%perd(3)*anint(cell%h_inv(3, 3)*dz)
2496 r2 = dx*dx + dy*dy + dz*dz
2497 IF (r2 < best_r2) THEN
2498 best_r2 = r2
2499 owner = iatom
2500 END IF
2501 END DO
2502 ELSE
2503 DO iatom = 1, SIZE(atom_coords, 2)
2504 rij = pbc(grid_point, atom_coords(:, iatom), cell)
2505 r2 = sum(rij**2)
2506 IF (r2 < best_r2) THEN
2507 best_r2 = r2
2508 owner = iatom
2509 END IF
2510 END DO
2511 END IF
2512
2513 END FUNCTION nearest_atom
2514
2515END MODULE skala_gpw_features
2516
Handles all functions related to the CELL.
Definition cell_types.F:15
various utilities that regard array of different kinds: output, allocation,... maybe it is not a good...
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public int_8
Definition kinds.F:54
integer, parameter, public dp
Definition kinds.F:34
Interface to the message passing library MPI.
Define the data structure for the particle information.
Build SKALA TorchScript feature dictionaries from CP2K GPW real-space grids.
subroutine, public skala_gpw_feature_build_atom_subchunk(parent, features, subchunk_index, max_rows, requires_grad)
Build an atom-contiguous subchunk feature bundle from a rank-local atom chunk.
integer, parameter, public skala_gpw_atom_partition_hard
subroutine, public skala_gpw_feature_release(features)
Release Torch objects and backing arrays owned by a feature bundle.
integer function, public skala_gpw_atom_subchunk_count(max_rows)
Return how many atom-contiguous subchunks the cached rank chunk needs.
subroutine, public skala_gpw_feature_build(features, rho_set, rho_r, particle_set, cell, requires_grad, weights, requires_coordinate_grad, requires_stress_grad, use_atom_chunks, route_atom_chunks, atom_partition)
Build a flat SKALA molecular feature dictionary from a local GPW grid.
type(skala_gpw_layout_cache_type), save cached_layout
subroutine, public skala_gpw_smooth_partition_derivatives(grid_point, atom_coords, cell, weights, included, dweights_datom, dweights_dstrain)
Build smooth atom weights and their atom/cell deformation derivatives.
integer, parameter, public skala_gpw_atom_partition_smooth
subroutine, public torch_dict_release(dict)
Releases a Torch dictionary and all its ressources.
Definition torch_api.F:1799
subroutine, public torch_tensor_narrow(tensor, dim, start_index, length, result)
Creates a view of a contiguous tensor slice.
Definition torch_api.F:1427
subroutine, public torch_tensor_to_device_leaf(tensor, requires_grad)
Moves a tensor to the active Torch device and makes it an autograd leaf.
Definition torch_api.F:1523
subroutine, public torch_dict_create(dict)
Creates an empty Torch dictionary.
Definition torch_api.F:1682
subroutine, public torch_tensor_expand_dim(tensor, dim, extent, result)
Creates an expanded tensor view along one singleton dimension.
Definition torch_api.F:1390
subroutine, public torch_dict_insert(dict, key, tensor)
Inserts a Torch tensor into a Torch dictionary.
Definition torch_api.F:1733
subroutine, public torch_dict_clone(source, target)
Clones a Torch dictionary.
Definition torch_api.F:1705
subroutine, public torch_tensor_release(tensor)
Releases a Torch tensor and all its ressources.
Definition torch_api.F:1658
contains the structure
subroutine, public xc_rho_set_get(rho_set, can_return_null, rho, drho, norm_drho, rhoa, rhob, norm_drhoa, norm_drhob, rho_1_3, rhoa_1_3, rhob_1_3, laplace_rho, laplace_rhoa, laplace_rhob, drhoa, drhob, rho_cutoff, drho_cutoff, tau_cutoff, tau, tau_a, tau_b, local_bounds)
returns the various attributes of rho_set
Type defining parameters related to the simulation cell.
Definition cell_types.F:60
represent a pointer to a contiguous 3d array
represent a density, with all the representation and data needed to perform a functional evaluation