Line data Source code
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 Per-nnp persistent neighbour-interface state for the NNP hot path.
10 : !> Separates neighbour bookkeeping from the ACSF and network loops:
11 : !> species-pair routing is precomputed once and per-element work
12 : !> buffers are reused. State lives on the parent nnp_type, so each
13 : !> &NNP force_eval keeps its own copy and the lifetime tracks
14 : !> nnp_env_release.
15 : !> \author Dhruv Sharma (ds2173@cam.ac.uk)
16 : !> \author Christoph Schran (christoph.schran@rub.de)
17 : !> \date 2026-05-21
18 : ! **************************************************************************************************
19 : MODULE nnp_neighbor_interface
20 :
21 : USE kinds, ONLY: dp
22 : USE nnp_environment_types, ONLY: nnp_dGdr_grp_type,&
23 : nnp_neigh_grp_type,&
24 : nnp_neighbor_interface_state_release,&
25 : nnp_neighbor_workspace_type,&
26 : nnp_type
27 : #include "./base/base_uses.f90"
28 :
29 : IMPLICIT NONE
30 :
31 : PRIVATE
32 :
33 : PUBLIC :: nnp_neighbor_interface_prepare, &
34 : nnp_neighbor_interface_reset_neighbor, &
35 : nnp_grp_grow_dGdr, &
36 : nnp_neigh_grp_grow, &
37 : nnp_workspace_grow_caches
38 :
39 : CONTAINS
40 :
41 : ! **************************************************************************************************
42 : !> \brief Ensure pair-routing metadata and reusable workspaces are ready for the current NNP model.
43 : !> \param nnp NNP environment whose neighbor_interface_state will be (re)built if needed.
44 : !> \author Dhruv Sharma (ds2173@cam.ac.uk)
45 : ! **************************************************************************************************
46 55492 : SUBROUTINE nnp_neighbor_interface_prepare(nnp)
47 :
48 : TYPE(nnp_type), INTENT(INOUT) :: nnp
49 :
50 : CHARACTER(len=*), PARAMETER :: routineN = 'nnp_neighbor_interface_prepare'
51 :
52 : INTEGER :: handle
53 : LOGICAL :: rebuild
54 :
55 55492 : CALL timeset(routineN, handle)
56 :
57 55492 : rebuild = .NOT. nnp%neighbor_interface_state%initialized
58 55492 : IF (.NOT. rebuild) CALL nnp_neighbor_interface_needs_rebuild(nnp, rebuild)
59 :
60 55492 : IF (rebuild) THEN
61 17 : CALL nnp_neighbor_interface_state_release(nnp%neighbor_interface_state)
62 17 : CALL nnp_neighbor_interface_build_pair_maps(nnp)
63 : END IF
64 :
65 55492 : CALL timestop(handle)
66 :
67 55492 : END SUBROUTINE nnp_neighbor_interface_prepare
68 :
69 : ! **************************************************************************************************
70 : !> \brief Reset per-group neighbour counters for one central element before refilling
71 : !> the reusable buffers. Zeroes n_rad/n_ang1/n_ang2; the (ind, dist) slabs
72 : !> are kept allocated and overwritten in place by the next push.
73 : !> \param nnp NNP environment whose neighbor_interface_state will have its counters cleared.
74 : !> \param ind central-element index (1..nnp%n_ele) whose workspace is reset.
75 : !> \author Dhruv Sharma (ds2173@cam.ac.uk)
76 : ! **************************************************************************************************
77 252785 : SUBROUTINE nnp_neighbor_interface_reset_neighbor(nnp, ind)
78 :
79 : TYPE(nnp_type), INTENT(INOUT) :: nnp
80 : INTEGER, INTENT(IN) :: ind
81 :
82 868699 : nnp%neighbor_interface_state%workspace(ind)%neighbor%n_rad(:) = 0
83 769054 : nnp%neighbor_interface_state%workspace(ind)%neighbor%n_ang1(:) = 0
84 769054 : nnp%neighbor_interface_state%workspace(ind)%neighbor%n_ang2(:) = 0
85 :
86 252785 : END SUBROUTINE nnp_neighbor_interface_reset_neighbor
87 :
88 : ! **************************************************************************************************
89 : !> \brief Check whether the persistent state matches the current NNP model. Assumes
90 : !> the per-group routing (ele_ind, cutoff) is fixed after nnp_init_acsf_groups,
91 : !> so only element and per-element group counts are compared.
92 : !> \param nnp NNP environment whose persistent neighbour-interface state will be inspected
93 : !> \param rebuild (out) .TRUE. if element count or per-element group sizes have changed
94 : ! **************************************************************************************************
95 55475 : SUBROUTINE nnp_neighbor_interface_needs_rebuild(nnp, rebuild)
96 :
97 : TYPE(nnp_type), INTENT(IN) :: nnp
98 : LOGICAL, INTENT(OUT) :: rebuild
99 :
100 : INTEGER :: i
101 :
102 55475 : rebuild = .FALSE.
103 55475 : IF (nnp%neighbor_interface_state%n_ele /= nnp%n_ele) THEN
104 0 : rebuild = .TRUE.
105 0 : RETURN
106 : END IF
107 55475 : IF (.NOT. ALLOCATED(nnp%neighbor_interface_state%n_rad)) THEN
108 0 : rebuild = .TRUE.
109 0 : RETURN
110 : END IF
111 :
112 221596 : DO i = 1, nnp%n_ele
113 : IF (nnp%neighbor_interface_state%n_rad(i) /= nnp%n_rad(i) .OR. &
114 : nnp%neighbor_interface_state%n_ang(i) /= nnp%n_ang(i) .OR. &
115 166121 : nnp%neighbor_interface_state%n_radgrp(i) /= nnp%rad(i)%n_symfgrp .OR. &
116 55475 : nnp%neighbor_interface_state%n_anggrp(i) /= nnp%ang(i)%n_symfgrp) THEN
117 0 : rebuild = .TRUE.
118 0 : RETURN
119 : END IF
120 : END DO
121 :
122 : END SUBROUTINE nnp_neighbor_interface_needs_rebuild
123 :
124 : ! **************************************************************************************************
125 : !> \brief Build species-pair routing tables and initialize reusable workspaces.
126 : !> \param nnp NNP environment; pair_map and workspace arrays are (re-)allocated on its neighbor_interface_state
127 : ! **************************************************************************************************
128 17 : SUBROUTINE nnp_neighbor_interface_build_pair_maps(nnp)
129 :
130 : TYPE(nnp_type), INTENT(INOUT) :: nnp
131 :
132 : INTEGER :: i
133 :
134 : ASSOCIATE (state => nnp%neighbor_interface_state)
135 17 : state%n_ele = nnp%n_ele
136 51 : ALLOCATE (state%n_rad(nnp%n_ele))
137 34 : ALLOCATE (state%n_ang(nnp%n_ele))
138 34 : ALLOCATE (state%n_radgrp(nnp%n_ele))
139 34 : ALLOCATE (state%n_anggrp(nnp%n_ele))
140 176 : ALLOCATE (state%pair_map(nnp%n_ele, nnp%n_ele))
141 137 : ALLOCATE (state%workspace(nnp%n_ele))
142 :
143 52 : DO i = 1, nnp%n_ele
144 35 : state%n_rad(i) = nnp%n_rad(i)
145 35 : state%n_ang(i) = nnp%n_ang(i)
146 35 : state%n_radgrp(i) = nnp%rad(i)%n_symfgrp
147 52 : state%n_anggrp(i) = nnp%ang(i)%n_symfgrp
148 : END DO
149 :
150 52 : DO i = 1, nnp%n_ele
151 35 : CALL nnp_neighbor_interface_build_pair_map_for_element(nnp, i)
152 52 : CALL nnp_neighbor_interface_init_workspace_metadata(nnp, i)
153 : END DO
154 :
155 17 : state%initialized = .TRUE.
156 : END ASSOCIATE
157 :
158 17 : END SUBROUTINE nnp_neighbor_interface_build_pair_maps
159 :
160 : ! **************************************************************************************************
161 : !> \brief Build all species-pair routes for one central element.
162 : !> \param nnp NNP environment providing the radial/angular SF groups
163 : !> \param ind central-element index whose pair-map row is built
164 : ! **************************************************************************************************
165 35 : SUBROUTINE nnp_neighbor_interface_build_pair_map_for_element(nnp, ind)
166 :
167 : TYPE(nnp_type), INTENT(INOUT) :: nnp
168 : INTEGER, INTENT(IN) :: ind
169 :
170 : INTEGER :: idx, neighbor_ind, s
171 :
172 108 : DO neighbor_ind = 1, nnp%n_ele
173 35 : ASSOCIATE (pair_map => nnp%neighbor_interface_state%pair_map(ind, neighbor_ind))
174 73 : pair_map%n_rad = 0
175 73 : pair_map%n_ang1 = 0
176 73 : pair_map%n_ang2 = 0
177 73 : pair_map%max_relevant_cutoff = 0.0_dp
178 :
179 222 : DO s = 1, nnp%rad(ind)%n_symfgrp
180 222 : IF (nnp%rad(ind)%symfgrp(s)%ele_ind(1) == neighbor_ind) pair_map%n_rad = pair_map%n_rad + 1
181 : END DO
182 251 : DO s = 1, nnp%ang(ind)%n_symfgrp
183 178 : IF (nnp%ang(ind)%symfgrp(s)%ele_ind(1) == neighbor_ind) pair_map%n_ang1 = pair_map%n_ang1 + 1
184 251 : IF (nnp%ang(ind)%symfgrp(s)%ele_ind(2) == neighbor_ind) pair_map%n_ang2 = pair_map%n_ang2 + 1
185 : END DO
186 :
187 219 : ALLOCATE (pair_map%rad_groups(MAX(1, pair_map%n_rad)))
188 219 : ALLOCATE (pair_map%ang1_groups(MAX(1, pair_map%n_ang1)))
189 219 : ALLOCATE (pair_map%ang2_groups(MAX(1, pair_map%n_ang2)))
190 :
191 73 : idx = 0
192 222 : DO s = 1, nnp%rad(ind)%n_symfgrp
193 222 : IF (nnp%rad(ind)%symfgrp(s)%ele_ind(1) == neighbor_ind) THEN
194 71 : idx = idx + 1
195 71 : pair_map%rad_groups(idx) = s
196 71 : pair_map%max_relevant_cutoff = MAX(pair_map%max_relevant_cutoff, nnp%rad(ind)%symfgrp(s)%cutoff)
197 : END IF
198 : END DO
199 :
200 73 : idx = 0
201 251 : DO s = 1, nnp%ang(ind)%n_symfgrp
202 251 : IF (nnp%ang(ind)%symfgrp(s)%ele_ind(1) == neighbor_ind) THEN
203 86 : idx = idx + 1
204 86 : pair_map%ang1_groups(idx) = s
205 86 : pair_map%max_relevant_cutoff = MAX(pair_map%max_relevant_cutoff, nnp%ang(ind)%symfgrp(s)%cutoff)
206 : END IF
207 : END DO
208 :
209 73 : idx = 0
210 324 : DO s = 1, nnp%ang(ind)%n_symfgrp
211 251 : IF (nnp%ang(ind)%symfgrp(s)%ele_ind(2) == neighbor_ind) THEN
212 86 : idx = idx + 1
213 86 : pair_map%ang2_groups(idx) = s
214 86 : pair_map%max_relevant_cutoff = MAX(pair_map%max_relevant_cutoff, nnp%ang(ind)%symfgrp(s)%cutoff)
215 : END IF
216 : END DO
217 : END ASSOCIATE
218 : END DO
219 :
220 35 : END SUBROUTINE nnp_neighbor_interface_build_pair_map_for_element
221 :
222 : ! **************************************************************************************************
223 : !> \brief Cache max scratch sizes for one central element.
224 : !> \param nnp NNP environment providing per-element SF group definitions
225 : !> \param ind central-element index whose scratch workspace metadata is cached
226 : ! **************************************************************************************************
227 35 : SUBROUTINE nnp_neighbor_interface_init_workspace_metadata(nnp, ind)
228 :
229 : TYPE(nnp_type), INTENT(INOUT) :: nnp
230 : INTEGER, INTENT(IN) :: ind
231 :
232 : INTEGER :: s
233 :
234 : ASSOCIATE (workspace => nnp%neighbor_interface_state%workspace(ind))
235 35 : workspace%max_rad_symf = 0
236 35 : workspace%max_ang_symf = 0
237 35 : workspace%n_input_nodes = nnp%n_rad(ind) + nnp%n_ang(ind)
238 :
239 106 : DO s = 1, nnp%rad(ind)%n_symfgrp
240 106 : workspace%max_rad_symf = MAX(workspace%max_rad_symf, nnp%rad(ind)%symfgrp(s)%n_symf)
241 : END DO
242 121 : DO s = 1, nnp%ang(ind)%n_symfgrp
243 121 : workspace%max_ang_symf = MAX(workspace%max_ang_symf, nnp%ang(ind)%symfgrp(s)%n_symf)
244 : END DO
245 :
246 : ! Per-SF scratch reused inside nnp_calc_rad / nnp_calc_ang, sized by max_*_symf.
247 35 : IF (ALLOCATED(workspace%radial_sym)) DEALLOCATE (workspace%radial_sym)
248 35 : IF (ALLOCATED(workspace%radial_force)) DEALLOCATE (workspace%radial_force)
249 35 : IF (ALLOCATED(workspace%angular_sym)) DEALLOCATE (workspace%angular_sym)
250 35 : IF (ALLOCATED(workspace%angular_force)) DEALLOCATE (workspace%angular_force)
251 105 : ALLOCATE (workspace%radial_sym(MAX(1, workspace%max_rad_symf)))
252 105 : ALLOCATE (workspace%radial_force(3, MAX(1, workspace%max_rad_symf)))
253 105 : ALLOCATE (workspace%angular_sym(MAX(1, workspace%max_ang_symf)))
254 105 : ALLOCATE (workspace%angular_force(3, 3, MAX(1, workspace%max_ang_symf)))
255 :
256 : ! 1D angular cutoff caches; start small and grow lazily to the peak
257 : ! per-element angular neighbour count (nnp_workspace_grow_caches).
258 35 : IF (ALLOCATED(workspace%fc_cache1)) DEALLOCATE (workspace%fc_cache1)
259 35 : IF (ALLOCATED(workspace%dfc_cache1)) DEALLOCATE (workspace%dfc_cache1)
260 35 : IF (ALLOCATED(workspace%fc_cache2)) DEALLOCATE (workspace%fc_cache2)
261 35 : IF (ALLOCATED(workspace%dfc_cache2)) DEALLOCATE (workspace%dfc_cache2)
262 35 : workspace%cache_cap = 8
263 35 : ALLOCATE (workspace%fc_cache1(workspace%cache_cap))
264 35 : ALLOCATE (workspace%dfc_cache1(workspace%cache_cap))
265 35 : ALLOCATE (workspace%fc_cache2(workspace%cache_cap))
266 35 : ALLOCATE (workspace%dfc_cache2(workspace%cache_cap))
267 :
268 : ! self_dGdr sized once here; independent of the candidate pool.
269 35 : IF (ALLOCATED(workspace%self_dGdr)) DEALLOCATE (workspace%self_dGdr)
270 105 : ALLOCATE (workspace%self_dGdr(3, MAX(1, workspace%n_input_nodes)))
271 :
272 : ! Per-group neighbour counters and dense (ind, dist) containers, sized to
273 : ! n_symfgrp here; the per-group slabs grow lazily via nnp_neigh_grp_grow.
274 35 : CALL nnp_release_neighbor_local(workspace)
275 105 : ALLOCATE (workspace%neighbor%n_rad(MAX(1, nnp%rad(ind)%n_symfgrp)))
276 105 : ALLOCATE (workspace%neighbor%n_ang1(MAX(1, nnp%ang(ind)%n_symfgrp)))
277 105 : ALLOCATE (workspace%neighbor%n_ang2(MAX(1, nnp%ang(ind)%n_symfgrp)))
278 176 : ALLOCATE (workspace%neighbor%rad(MAX(1, nnp%rad(ind)%n_symfgrp)))
279 191 : ALLOCATE (workspace%neighbor%ang1(MAX(1, nnp%ang(ind)%n_symfgrp)))
280 191 : ALLOCATE (workspace%neighbor%ang2(MAX(1, nnp%ang(ind)%n_symfgrp)))
281 106 : workspace%neighbor%n_rad(:) = 0
282 121 : workspace%neighbor%n_ang1(:) = 0
283 121 : workspace%neighbor%n_ang2(:) = 0
284 140 : workspace%neighbor%pbc_copies = 0
285 :
286 : ! Per-group dG/dr buffers: container arrays sized to n_symfgrp and each
287 : ! entry's n_symf recorded. The %data slab stays unallocated until
288 : ! nnp_grp_grow_dGdr sees a real neighbour count.
289 35 : CALL nnp_release_dGdr_grp_array(workspace%dGdr_rad)
290 35 : CALL nnp_release_dGdr_grp_array(workspace%dGdr_ang_jj)
291 35 : CALL nnp_release_dGdr_grp_array(workspace%dGdr_ang_kk)
292 176 : ALLOCATE (workspace%dGdr_rad(MAX(1, nnp%rad(ind)%n_symfgrp)))
293 191 : ALLOCATE (workspace%dGdr_ang_jj(MAX(1, nnp%ang(ind)%n_symfgrp)))
294 191 : ALLOCATE (workspace%dGdr_ang_kk(MAX(1, nnp%ang(ind)%n_symfgrp)))
295 106 : DO s = 1, nnp%rad(ind)%n_symfgrp
296 71 : workspace%dGdr_rad(s)%n_symf = nnp%rad(ind)%symfgrp(s)%n_symf
297 106 : workspace%dGdr_rad(s)%cap = 0
298 : END DO
299 156 : DO s = 1, nnp%ang(ind)%n_symfgrp
300 86 : workspace%dGdr_ang_jj(s)%n_symf = nnp%ang(ind)%symfgrp(s)%n_symf
301 86 : workspace%dGdr_ang_jj(s)%cap = 0
302 86 : workspace%dGdr_ang_kk(s)%n_symf = nnp%ang(ind)%symfgrp(s)%n_symf
303 121 : workspace%dGdr_ang_kk(s)%cap = 0
304 : END DO
305 : END ASSOCIATE
306 :
307 35 : END SUBROUTINE nnp_neighbor_interface_init_workspace_metadata
308 :
309 : ! **************************************************************************************************
310 : !> \brief Release the workspace's neighbor%(rad,ang1,ang2) per-group slabs before
311 : !> re-allocation, keeping the persistent caches that survive a rebuild.
312 : !> \param workspace per-element scratch workspace whose neighbor%(rad,ang1,ang2) slabs will be released
313 : ! **************************************************************************************************
314 35 : SUBROUTINE nnp_release_neighbor_local(workspace)
315 :
316 : TYPE(nnp_neighbor_workspace_type), INTENT(INOUT) :: workspace
317 :
318 : INTEGER :: s
319 :
320 35 : IF (ALLOCATED(workspace%neighbor%rad)) THEN
321 0 : DO s = 1, SIZE(workspace%neighbor%rad)
322 0 : IF (ALLOCATED(workspace%neighbor%rad(s)%ind)) DEALLOCATE (workspace%neighbor%rad(s)%ind)
323 0 : IF (ALLOCATED(workspace%neighbor%rad(s)%dist)) DEALLOCATE (workspace%neighbor%rad(s)%dist)
324 : END DO
325 0 : DEALLOCATE (workspace%neighbor%rad)
326 : END IF
327 35 : IF (ALLOCATED(workspace%neighbor%ang1)) THEN
328 0 : DO s = 1, SIZE(workspace%neighbor%ang1)
329 0 : IF (ALLOCATED(workspace%neighbor%ang1(s)%ind)) DEALLOCATE (workspace%neighbor%ang1(s)%ind)
330 0 : IF (ALLOCATED(workspace%neighbor%ang1(s)%dist)) DEALLOCATE (workspace%neighbor%ang1(s)%dist)
331 : END DO
332 0 : DEALLOCATE (workspace%neighbor%ang1)
333 : END IF
334 35 : IF (ALLOCATED(workspace%neighbor%ang2)) THEN
335 0 : DO s = 1, SIZE(workspace%neighbor%ang2)
336 0 : IF (ALLOCATED(workspace%neighbor%ang2(s)%ind)) DEALLOCATE (workspace%neighbor%ang2(s)%ind)
337 0 : IF (ALLOCATED(workspace%neighbor%ang2(s)%dist)) DEALLOCATE (workspace%neighbor%ang2(s)%dist)
338 : END DO
339 0 : DEALLOCATE (workspace%neighbor%ang2)
340 : END IF
341 35 : IF (ALLOCATED(workspace%neighbor%n_rad)) DEALLOCATE (workspace%neighbor%n_rad)
342 35 : IF (ALLOCATED(workspace%neighbor%n_ang1)) DEALLOCATE (workspace%neighbor%n_ang1)
343 35 : IF (ALLOCATED(workspace%neighbor%n_ang2)) DEALLOCATE (workspace%neighbor%n_ang2)
344 140 : workspace%neighbor%pbc_copies = -1
345 :
346 35 : END SUBROUTINE nnp_release_neighbor_local
347 :
348 : ! **************************************************************************************************
349 : !> \brief Release every per-group dG/dr buffer in a container array, then deallocate the container.
350 : !> \param grps per-group dG/dr container array to release (each entry's %data is freed first)
351 : ! **************************************************************************************************
352 105 : SUBROUTINE nnp_release_dGdr_grp_array(grps)
353 :
354 : TYPE(nnp_dGdr_grp_type), ALLOCATABLE, &
355 : INTENT(INOUT) :: grps(:)
356 :
357 : INTEGER :: s
358 :
359 105 : IF (.NOT. ALLOCATED(grps)) RETURN
360 0 : DO s = 1, SIZE(grps)
361 0 : IF (ALLOCATED(grps(s)%data)) DEALLOCATE (grps(s)%data)
362 0 : grps(s)%cap = 0
363 0 : grps(s)%n_symf = 0
364 : END DO
365 0 : DEALLOCATE (grps)
366 :
367 : END SUBROUTINE nnp_release_dGdr_grp_array
368 :
369 : ! **************************************************************************************************
370 : !> \brief Ensure a per-group dG/dr buffer holds n_needed neighbours, growing by
371 : !> 1.5x (with an additive floor) so reallocation amortizes to O(1) over a
372 : !> trajectory. grp%data is allocated on return (cap >= 1), so callers can
373 : !> ASSOCIATE-bind it even for a zero-trip loop.
374 : !> \param grp per-element dGdr group whose %data slab is reallocated if undersized
375 : !> \param n_needed minimum required capacity (third dim of grp%data)
376 : !> \author Dhruv Sharma (ds2173@cam.ac.uk)
377 : ! **************************************************************************************************
378 157020 : SUBROUTINE nnp_grp_grow_dGdr(grp, n_needed)
379 :
380 : TYPE(nnp_dGdr_grp_type), INTENT(INOUT) :: grp
381 : INTEGER, INTENT(IN) :: n_needed
382 :
383 : INTEGER :: new_cap
384 :
385 157020 : IF (ALLOCATED(grp%data) .AND. grp%cap >= n_needed) RETURN
386 313 : IF (ALLOCATED(grp%data)) DEALLOCATE (grp%data)
387 313 : new_cap = MAX(MAX(1, n_needed), INT(grp%cap*1.5_dp) + 8)
388 1252 : ALLOCATE (grp%data(3, MAX(1, grp%n_symf), new_cap))
389 313 : grp%cap = new_cap
390 :
391 : END SUBROUTINE nnp_grp_grow_dGdr
392 :
393 : ! **************************************************************************************************
394 : !> \brief Ensure a per-group (ind, dist) neighbour buffer holds n_needed entries.
395 : !> Called from inside the linked-cell push loop, so existing entries are
396 : !> preserved on grow via MOVE_ALLOC. 1.5x growth amortizes reallocation to
397 : !> O(1) over a trajectory.
398 : !> \param grp per-species-pair neighbour group whose (ind, dist) buffers grow
399 : !> \param n_needed minimum required entry count
400 : !> \author Dhruv Sharma (ds2173@cam.ac.uk)
401 : ! **************************************************************************************************
402 645 : SUBROUTINE nnp_neigh_grp_grow(grp, n_needed)
403 :
404 : TYPE(nnp_neigh_grp_type), INTENT(INOUT) :: grp
405 : INTEGER, INTENT(IN) :: n_needed
406 :
407 : INTEGER :: n_old, new_cap
408 645 : INTEGER, ALLOCATABLE :: new_ind(:)
409 645 : REAL(KIND=dp), ALLOCATABLE :: new_dist(:, :)
410 :
411 645 : IF (ALLOCATED(grp%dist) .AND. grp%cap >= n_needed) RETURN
412 :
413 645 : new_cap = MAX(MAX(8, n_needed), INT(grp%cap*1.5_dp) + 8)
414 :
415 645 : IF (ALLOCATED(grp%dist)) THEN
416 476 : n_old = grp%cap
417 1428 : ALLOCATE (new_dist(4, new_cap))
418 1428 : ALLOCATE (new_ind(new_cap))
419 476 : IF (n_old > 0) THEN
420 64876 : new_dist(:, 1:n_old) = grp%dist(:, 1:n_old)
421 13356 : new_ind(1:n_old) = grp%ind(1:n_old)
422 : END IF
423 476 : CALL MOVE_ALLOC(new_dist, grp%dist)
424 476 : CALL MOVE_ALLOC(new_ind, grp%ind)
425 : ELSE
426 507 : ALLOCATE (grp%dist(4, new_cap))
427 507 : ALLOCATE (grp%ind(new_cap))
428 : END IF
429 645 : grp%cap = new_cap
430 :
431 : END SUBROUTINE nnp_neigh_grp_grow
432 :
433 : ! **************************************************************************************************
434 : !> \brief Ensure the four per-element angular cutoff caches hold at least n_needed
435 : !> entries. These 1D scratch arrays are reused across angular groups within
436 : !> one nnp_calc_acsf call, so the peak is MAX_s(n_ang1) and MAX_s(n_ang2).
437 : !> 1.5x lazy growth.
438 : !> \param workspace per-element workspace whose fc_cache1/dfc_cache1/fc_cache2/dfc_cache2 grow
439 : !> \param n_needed minimum required cache length
440 : !> \author Dhruv Sharma (ds2173@cam.ac.uk)
441 : ! **************************************************************************************************
442 204699 : SUBROUTINE nnp_workspace_grow_caches(workspace, n_needed)
443 :
444 : TYPE(nnp_neighbor_workspace_type), INTENT(INOUT) :: workspace
445 : INTEGER, INTENT(IN) :: n_needed
446 :
447 : INTEGER :: new_cap
448 :
449 204699 : IF (workspace%cache_cap >= n_needed) RETURN
450 :
451 42 : new_cap = MAX(MAX(8, n_needed), INT(workspace%cache_cap*1.5_dp) + 8)
452 : ! Contents not preserved: nnp_fill_fc_dfc_cache overwrites fully before any read.
453 42 : IF (ALLOCATED(workspace%fc_cache1)) DEALLOCATE (workspace%fc_cache1)
454 42 : IF (ALLOCATED(workspace%dfc_cache1)) DEALLOCATE (workspace%dfc_cache1)
455 42 : IF (ALLOCATED(workspace%fc_cache2)) DEALLOCATE (workspace%fc_cache2)
456 42 : IF (ALLOCATED(workspace%dfc_cache2)) DEALLOCATE (workspace%dfc_cache2)
457 126 : ALLOCATE (workspace%fc_cache1(new_cap))
458 84 : ALLOCATE (workspace%dfc_cache1(new_cap))
459 84 : ALLOCATE (workspace%fc_cache2(new_cap))
460 84 : ALLOCATE (workspace%dfc_cache2(new_cap))
461 42 : workspace%cache_cap = new_cap
462 :
463 : END SUBROUTINE nnp_workspace_grow_caches
464 :
465 : END MODULE nnp_neighbor_interface
|