/**************************************************************************** * Copyright (c) 2017-2023 by the ArborX authors * * All rights reserved. * * * * This file is part of the ArborX library. ArborX is * * distributed under a BSD 3-clause license. For the licensing terms see * * the LICENSE file in the top-level directory. * * * * SPDX-License-Identifier: BSD-3-Clause * ****************************************************************************/ #ifndef ARBORX_NEIGHBOR_LIST_HPP #define ARBORX_NEIGHBOR_LIST_HPP #include #include #include // reallocWithoutInitializing #include #include #include #include namespace ArborX::Experimental { struct NeighborListPredicateGetter { float _radius; template KOKKOS_FUNCTION auto operator()(PairValueIndex const &pair) const { static_assert(GeometryTraits::is_point_v); constexpr int dim = GeometryTraits::dimension_v; using Coordinate = typename GeometryTraits::coordinate_type_t; auto const &hyper_point = reinterpret_cast< ExperimentalHyperGeometry::Point const &>(pair.value); return intersects(ExperimentalHyperGeometry::Sphere{ hyper_point, _radius}); } }; template void findHalfNeighborList(ExecutionSpace const &space, Primitives const &primitives, float radius, Offsets &offsets, Indices &indices) { Kokkos::Profiling::pushRegion("ArborX::Experimental::HalfNeighborList"); namespace KokkosExt = ArborX::Details::KokkosExt; using Details::HalfTraversal; using Points = Details::AccessValues; using MemorySpace = typename Points::memory_space; static_assert( KokkosExt::is_accessible_from::value, "Primitives must be accessible from the execution space"); using Point = typename Points::value_type; static_assert(GeometryTraits::is_point_v); Points points{primitives}; // NOLINT int const n = points.size(); using Value = PairValueIndex; BoundingVolumeHierarchy bvh( space, Experimental::attach_indices(points)); Kokkos::Profiling::pushRegion( "ArborX::Experimental::HalfNeighborList::Count"); KokkosExt::reallocWithoutInitializing(space, offsets, n + 1); Kokkos::deep_copy(space, offsets, 0); HalfTraversal( space, bvh, KOKKOS_LAMBDA(Value const &, Value const &value) { Kokkos::atomic_increment(&offsets(value.index)); }, NeighborListPredicateGetter{radius}); KokkosExt::exclusive_scan(space, offsets, offsets, 0); KokkosExt::reallocWithoutInitializing(space, indices, KokkosExt::lastElement(space, offsets)); Kokkos::Profiling::popRegion(); Kokkos::Profiling::pushRegion("ArborX::Experimental::HalfNeighborList::Fill"); auto counts = KokkosExt::clone(space, Kokkos::subview(offsets, std::make_pair(0, n)), "ArborX::Experimental::HalfNeighborList::counts"); HalfTraversal( space, bvh, KOKKOS_LAMBDA(Value const &value1, Value const &value2) { indices(Kokkos::atomic_fetch_inc(&counts(value2.index))) = value1.index; }, NeighborListPredicateGetter{radius}); Kokkos::Profiling::popRegion(); Kokkos::Profiling::popRegion(); } template void findFullNeighborList(ExecutionSpace const &space, Primitives const &primitives, float radius, Offsets &offsets, Indices &indices) { Kokkos::Profiling::pushRegion("ArborX::Experimental::FullNeighborList"); namespace KokkosExt = ArborX::Details::KokkosExt; using Details::HalfTraversal; using Points = Details::AccessValues; using MemorySpace = typename Points::memory_space; static_assert( KokkosExt::is_accessible_from::value, "Primitives must be accessible from the execution space"); using Point = typename Points::value_type; static_assert(GeometryTraits::is_point_v); Points points{primitives}; // NOLINT int const n = points.size(); using Value = PairValueIndex; BoundingVolumeHierarchy bvh( space, Experimental::attach_indices(points)); Kokkos::Profiling::pushRegion( "ArborX::Experimental::FullNeighborList::Count"); KokkosExt::reallocWithoutInitializing(space, offsets, n + 1); Kokkos::deep_copy(space, offsets, 0); HalfTraversal( space, bvh, KOKKOS_LAMBDA(Value const &value1, Value const &value2) { Kokkos::atomic_increment(&offsets(value1.index)); Kokkos::atomic_increment(&offsets(value2.index)); }, NeighborListPredicateGetter{radius}); KokkosExt::exclusive_scan(space, offsets, offsets, 0); KokkosExt::reallocWithoutInitializing(space, indices, KokkosExt::lastElement(space, offsets)); Kokkos::Profiling::popRegion(); Kokkos::Profiling::pushRegion("ArborX::Experimental::FullNeighborList::Fill"); auto counts = KokkosExt::clone(space, Kokkos::subview(offsets, std::make_pair(0, n)), "ArborX::Experimental::FullNeighborList::counts"); HalfTraversal( space, bvh, KOKKOS_LAMBDA(Value const &value1, Value const &value2) { indices(Kokkos::atomic_fetch_inc(&counts(value2.index))) = value1.index; }, NeighborListPredicateGetter{radius}); Kokkos::Profiling::popRegion(); Kokkos::Profiling::pushRegion("ArborX::Experimental::FullNeighborList::Copy"); auto counts_copy = KokkosExt::clone(space, counts, counts.label() + "_copy"); Kokkos::parallel_for( "ArborX::Experimental::FullNeighborList::Copy", Kokkos::TeamPolicy(space, n, Kokkos::AUTO, 1), KOKKOS_LAMBDA( typename Kokkos::TeamPolicy::member_type const &member) { auto const i = member.league_rank(); auto const first = offsets(i); auto const last = counts_copy(i); Kokkos::parallel_for( Kokkos::TeamVectorRange(member, last - first), [&](int j) { int const k = indices(first + j); indices(Kokkos::atomic_fetch_inc(&counts(k))) = i; }); }); Kokkos::Profiling::popRegion(); Kokkos::Profiling::popRegion(); } } // namespace ArborX::Experimental #endif