LCOV - code coverage report
Current view: top level - src - nnp_neighbor_interface.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:21ef868) Lines: 88.2 % 195 172
Test Date: 2026-08-14 07:04:57 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              : #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
        

Generated by: LCOV version 2.0-1