LCOV - code coverage report
Current view: top level - src - nnp_neighbor_interface.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:24d69ee) Lines: 88.5 % 200 177
Test Date: 2026-09-03 07:32:15 Functions: 100.0 % 11 11

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

Generated by: LCOV version 2.0-1