From 82db633ed8202904b1cebbdeed711fa77c6436da Mon Sep 17 00:00:00 2001 From: Damien L-G Date: Thu, 4 Jun 2020 18:11:42 -0400 Subject: [PATCH 01/10] Fixup let size() member function in access traits specialization be a KOKKOS_FUNCTION --- core/src/Cabana_Experimental_NeighborList.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/Cabana_Experimental_NeighborList.hpp b/core/src/Cabana_Experimental_NeighborList.hpp index a699f31d1..ab44c3a53 100644 --- a/core/src/Cabana_Experimental_NeighborList.hpp +++ b/core/src/Cabana_Experimental_NeighborList.hpp @@ -77,7 +77,7 @@ struct Access( x( i, 0 ) ), From d126eb011952a14c07a42afe7946b7b1c016c82e Mon Sep 17 00:00:00 2001 From: Damien L-G Date: Thu, 4 Jun 2020 18:14:05 -0400 Subject: [PATCH 02/10] Fixup use MemorySpace type alias in CrsGraph specialization of NeighborList --- core/src/Cabana_Experimental_NeighborList.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/Cabana_Experimental_NeighborList.hpp b/core/src/Cabana_Experimental_NeighborList.hpp index ab44c3a53..5ff7db9fe 100644 --- a/core/src/Cabana_Experimental_NeighborList.hpp +++ b/core/src/Cabana_Experimental_NeighborList.hpp @@ -179,8 +179,8 @@ auto makeNeighborList( Tag, Slice const &coordinate_slice, Impl::makePredicates( coordinate_slice, first, last, radius ), Impl::NeighborDiscriminatorCallback{}, indices, offset ); - return CrsGraph{ - std::move( indices ), std::move( offset ), first, bvh.size()}; + return CrsGraph{std::move( indices ), std::move( offset ), + first, bvh.size()}; } } // namespace Experimental From 5d528c85b119babc164c9707b5cbf263b01cbf71 Mon Sep 17 00:00:00 2001 From: Damien L-G Date: Thu, 4 Jun 2020 18:15:26 -0400 Subject: [PATCH 03/10] Remove redundant bound check in NeighborList::getNeighbor()> --- core/src/Cabana_Experimental_NeighborList.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/Cabana_Experimental_NeighborList.hpp b/core/src/Cabana_Experimental_NeighborList.hpp index 5ff7db9fe..d3d05f036 100644 --- a/core/src/Cabana_Experimental_NeighborList.hpp +++ b/core/src/Cabana_Experimental_NeighborList.hpp @@ -205,7 +205,6 @@ class NeighborList> static KOKKOS_FUNCTION size_type getNeighbor( crs_graph_type const &crs_graph, size_type p, size_type n ) { - assert( (int)p >= 0 && p < crs_graph.total ); assert( n < numNeighbor( crs_graph, p ) ); p -= crs_graph.shift; return crs_graph.col_ind( crs_graph.row_ptr( p ) + n ); From 41f2902587aee6161f8d5d49f9f9a53d8ff52360 Mon Sep 17 00:00:00 2001 From: Damien L-G Date: Thu, 4 Jun 2020 18:19:28 -0400 Subject: [PATCH 04/10] Abstract away the logic to filter collisions to get full/half neighbor in the custom callback --- core/src/Cabana_Experimental_NeighborList.hpp | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/core/src/Cabana_Experimental_NeighborList.hpp b/core/src/Cabana_Experimental_NeighborList.hpp index d3d05f036..1dd197ff8 100644 --- a/core/src/Cabana_Experimental_NeighborList.hpp +++ b/core/src/Cabana_Experimental_NeighborList.hpp @@ -114,36 +114,38 @@ namespace Experimental namespace Impl { -// Custom callback for ArborX::BVH::query() template -struct NeighborDiscriminatorCallback; +struct CollisionFilter; template <> -struct NeighborDiscriminatorCallback +struct CollisionFilter { - using tag = ArborX::Details::InlineCallbackTag; - template - KOKKOS_FUNCTION void operator()( Predicate const &predicate, int i, - OutputFunctor const &out ) const + KOKKOS_FUNCTION bool static keep( int i, int j ) noexcept { - if ( getData( predicate ) != i ) // discard self-collision - { - out( i ); - } + return i != j; // discard self-collision } }; template <> -struct NeighborDiscriminatorCallback +struct CollisionFilter +{ + KOKKOS_FUNCTION static bool keep( int i, int j ) noexcept { return i > j; } +}; + +// Custom callback for ArborX::BVH::query() +template +struct NeighborDiscriminatorCallback { using tag = ArborX::Details::InlineCallbackTag; template - KOKKOS_FUNCTION void operator()( Predicate const &predicate, int i, + KOKKOS_FUNCTION void operator()( Predicate const &predicate, + int primitive_index, OutputFunctor const &out ) const { - if ( getData( predicate ) > i ) + int const predicate_index = getData( predicate ); + if ( CollisionFilter::keep( predicate_index, primitive_index ) ) { - out( i ); + out( primitive_index ); } } }; From a8cb3453e3321f38e4842a02d293eb2a2974380d Mon Sep 17 00:00:00 2001 From: Damien L-G Date: Thu, 4 Jun 2020 18:20:25 -0400 Subject: [PATCH 05/10] Add couple new custom callbacks to populate a 2D neighbor list --- core/src/Cabana_Experimental_NeighborList.hpp | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/core/src/Cabana_Experimental_NeighborList.hpp b/core/src/Cabana_Experimental_NeighborList.hpp index 1dd197ff8..fdea5b89f 100644 --- a/core/src/Cabana_Experimental_NeighborList.hpp +++ b/core/src/Cabana_Experimental_NeighborList.hpp @@ -150,6 +150,45 @@ struct NeighborDiscriminatorCallback } }; +template +struct NeighborDiscriminatorCallback2D_FirstPass +{ + Counts counts; + using tag = ArborX::Details::InlineCallbackTag; + template + KOKKOS_FUNCTION void operator()( Predicate const &predicate, + int primitive_index, + OutputFunctor const & ) const + { + int const predicate_index = getData( predicate ); + if ( CollisionFilter::keep( predicate_index, primitive_index ) ) + { + ++counts( predicate_index ); + } + } +}; + +template +struct NeighborDiscriminatorCallback2D_SecondPass +{ + Counts counts; + Neighbors neighbors; + using tag = ArborX::Details::InlineCallbackTag; + template + KOKKOS_FUNCTION void operator()( Predicate const &predicate, + int primitive_index, + OutputFunctor const & ) const + { + int const predicate_index = getData( predicate ); + if ( CollisionFilter::keep( predicate_index, primitive_index ) ) + { + assert( count( predicate_index ) < (int)neighbors.extent( 1 ) ); + neighbors( predicate_index, counts( predicate_index )++ ) = + primitive_index; + } + } +}; + } // namespace Impl template From 1f261a65eeae420d5e830aa2159e03e5bf4fca58 Mon Sep 17 00:00:00 2001 From: Damien L-G Date: Thu, 4 Jun 2020 18:21:21 -0400 Subject: [PATCH 06/10] Add Experimental::make2DNeighborList() that uses the bounding volume hierarchy from ArborX --- core/src/Cabana_Experimental_NeighborList.hpp | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/core/src/Cabana_Experimental_NeighborList.hpp b/core/src/Cabana_Experimental_NeighborList.hpp index fdea5b89f..c794c3841 100644 --- a/core/src/Cabana_Experimental_NeighborList.hpp +++ b/core/src/Cabana_Experimental_NeighborList.hpp @@ -224,6 +224,57 @@ auto makeNeighborList( Tag, Slice const &coordinate_slice, first, bvh.size()}; } +template +struct Dense +{ + Kokkos::View cnt; + Kokkos::View val; + typename MemorySpace::size_type shift; + typename MemorySpace::size_type total; +}; + +template +auto make2DNeighborList( Tag, Slice const &coordinate_slice, + typename Slice::size_type first, + typename Slice::size_type last, + typename Slice::value_type radius ) +{ + using MemorySpace = typename DeviceType::memory_space; + using ExecutionSpace = typename DeviceType::execution_space; + ExecutionSpace space{}; + + ArborX::BVH bvh( space, coordinate_slice ); + + Kokkos::View indices( "indices", 0 ); + Kokkos::View offset( "offset", 0 ); + + auto const predicates = + Impl::makePredicates( coordinate_slice, first, last, radius ); + + auto const n_queries = ArborX::Traits:: + Access::size( + predicates ); + + Kokkos::View counts( "counts", n_queries ); + bvh.query( + space, predicates, + Impl::NeighborDiscriminatorCallback2D_FirstPass{counts}, + indices, offset ); + + Kokkos::View neighbors( + Kokkos::view_alloc( "neighbors", Kokkos::WithoutInitializing ), + n_queries, ArborX::max( space, counts ) ); + Kokkos::deep_copy( counts, 0 ); // reset counts to zero + bvh.query( + space, predicates, + Impl::NeighborDiscriminatorCallback2D_SecondPass< + decltype( counts ), decltype( neighbors ), Tag>{counts, neighbors}, + indices, offset ); + + return Dense{counts, neighbors, first, bvh.size()}; +} + } // namespace Experimental template From 6974ab4de9bfa2c40657abec6c4bf10cb3a3f123 Mon Sep 17 00:00:00 2001 From: Damien L-G Date: Thu, 4 Jun 2020 18:22:00 -0400 Subject: [PATCH 07/10] Add new specialization of NeighborList for the 2D data structure --- core/src/Cabana_Experimental_NeighborList.hpp | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/core/src/Cabana_Experimental_NeighborList.hpp b/core/src/Cabana_Experimental_NeighborList.hpp index c794c3841..c2e7fd191 100644 --- a/core/src/Cabana_Experimental_NeighborList.hpp +++ b/core/src/Cabana_Experimental_NeighborList.hpp @@ -303,6 +303,32 @@ class NeighborList> } }; +template +class NeighborList> +{ + using size_type = std::size_t; + using specialization_type = Experimental::Dense; + + public: + using memory_space = MemorySpace; + static KOKKOS_FUNCTION size_type numNeighbor( specialization_type const &d, + size_type p ) + { + assert( (int)p >= 0 && p < d.total ); + p -= d.shift; + if ( (int)p < 0 || p >= d.cnt.size() ) + return 0; + return d.cnt( p ); + } + static KOKKOS_FUNCTION size_type getNeighbor( specialization_type const &d, + size_type p, size_type n ) + { + assert( n < numNeighbor( d, p ) ); + p -= d.shift; + return d.val( p, n ); + } +}; + } // namespace Cabana #endif From 150552d05cff2aebc9f1ac2f3b61867ff7be8d07 Mon Sep 17 00:00:00 2001 From: Damien L-G Date: Thu, 4 Jun 2020 18:41:50 -0400 Subject: [PATCH 08/10] Add unit tests for experimental 2D neighbor list with ArborX --- core/unit_test/tstNeighborListArborX.hpp | 109 +++++++++++++++++------ 1 file changed, 82 insertions(+), 27 deletions(-) diff --git a/core/unit_test/tstNeighborListArborX.hpp b/core/unit_test/tstNeighborListArborX.hpp index ef729a0f2..fe58687ca 100644 --- a/core/unit_test/tstNeighborListArborX.hpp +++ b/core/unit_test/tstNeighborListArborX.hpp @@ -53,13 +53,26 @@ void testArborXListHalf() auto aosoa = createParticles( num_particle, box_min, box_max ); auto position = Cabana::slice<0>( aosoa ); - // Create the neighbor list. - using device_type = TEST_MEMSPACE; // sigh... - auto const nlist = Cabana::Experimental::makeNeighborList( - Cabana::HalfNeighborTag{}, position, 0, aosoa.size(), test_radius ); - - // Check the neighbor list. - checkHalfNeighborList( nlist, position, test_radius ); + { + // Create the neighbor list. + using device_type = TEST_MEMSPACE; // sigh... + auto const nlist = Cabana::Experimental::makeNeighborList( + Cabana::HalfNeighborTag{}, position, 0, aosoa.size(), test_radius ); + + // Check the neighbor list. + checkHalfNeighborList( nlist, position, test_radius ); + } + { + // Create the neighbor list. + using device_type = TEST_MEMSPACE; // sigh... + auto const nlist = + Cabana::Experimental::make2DNeighborList( + Cabana::HalfNeighborTag{}, position, 0, aosoa.size(), + test_radius ); + + // Check the neighbor list. + checkHalfNeighborList( nlist, position, test_radius ); + } } //---------------------------------------------------------------------------// @@ -74,14 +87,28 @@ void testArborXListFullPartialRange() auto aosoa = createParticles( num_particle, box_min, box_max ); auto position = Cabana::slice<0>( aosoa ); - // Create the neighbor list. - using device_type = TEST_MEMSPACE; // sigh... - auto const nlist = Cabana::Experimental::makeNeighborList( - Cabana::FullNeighborTag{}, position, 0, num_ignore, test_radius ); - - // Check the neighbor list. - checkFullNeighborListPartialRange( nlist, position, test_radius, - num_ignore ); + { + // Create the neighbor list. + using device_type = TEST_MEMSPACE; // sigh... + auto const nlist = Cabana::Experimental::makeNeighborList( + Cabana::FullNeighborTag{}, position, 0, num_ignore, test_radius ); + + // Check the neighbor list. + checkFullNeighborListPartialRange( nlist, position, test_radius, + num_ignore ); + } + { + // Create the neighbor list. + using device_type = TEST_MEMSPACE; // sigh... + auto const nlist = + Cabana::Experimental::make2DNeighborList( + Cabana::FullNeighborTag{}, position, 0, num_ignore, + test_radius ); + + // Check the neighbor list. + checkFullNeighborListPartialRange( nlist, position, test_radius, + num_ignore ); + } } //---------------------------------------------------------------------------// @@ -95,14 +122,28 @@ void testNeighborArborXParallelFor() auto aosoa = createParticles( num_particle, box_min, box_max ); auto position = Cabana::slice<0>( aosoa ); - // Create the neighbor list. - using device_type = TEST_MEMSPACE; // sigh... - auto const nlist = Cabana::Experimental::makeNeighborList( - Cabana::FullNeighborTag{}, position, 0, aosoa.size(), test_radius ); + { + // Create the neighbor list. + using device_type = TEST_MEMSPACE; // sigh... + auto const nlist = Cabana::Experimental::makeNeighborList( + Cabana::FullNeighborTag{}, position, 0, aosoa.size(), test_radius ); - checkFirstNeighborParallelFor( nlist, position, test_radius ); + checkFirstNeighborParallelFor( nlist, position, test_radius ); - checkSecondNeighborParallelFor( nlist, position, test_radius ); + checkSecondNeighborParallelFor( nlist, position, test_radius ); + } + { + // Create the neighbor list. + using device_type = TEST_MEMSPACE; // sigh... + auto const nlist = + Cabana::Experimental::make2DNeighborList( + Cabana::FullNeighborTag{}, position, 0, aosoa.size(), + test_radius ); + + checkFirstNeighborParallelFor( nlist, position, test_radius ); + + checkSecondNeighborParallelFor( nlist, position, test_radius ); + } } //---------------------------------------------------------------------------// @@ -116,14 +157,28 @@ void testNeighborArborXParallelReduce() auto aosoa = createParticles( num_particle, box_min, box_max ); auto position = Cabana::slice<0>( aosoa ); - // Create the neighbor list. - using device_type = TEST_MEMSPACE; // sigh... - auto const nlist = Cabana::Experimental::makeNeighborList( - Cabana::FullNeighborTag{}, position, 0, aosoa.size(), test_radius ); + { + // Create the neighbor list. + using device_type = TEST_MEMSPACE; // sigh... + auto const nlist = Cabana::Experimental::makeNeighborList( + Cabana::FullNeighborTag{}, position, 0, aosoa.size(), test_radius ); + + checkFirstNeighborParallelReduce( nlist, aosoa, test_radius ); + + checkSecondNeighborParallelReduce( nlist, aosoa, test_radius ); + } + { + // Create the neighbor list. + using device_type = TEST_MEMSPACE; // sigh... + auto const nlist = + Cabana::Experimental::make2DNeighborList( + Cabana::FullNeighborTag{}, position, 0, aosoa.size(), + test_radius ); - checkFirstNeighborParallelReduce( nlist, aosoa, test_radius ); + checkFirstNeighborParallelReduce( nlist, aosoa, test_radius ); - checkSecondNeighborParallelReduce( nlist, aosoa, test_radius ); + checkSecondNeighborParallelReduce( nlist, aosoa, test_radius ); + } } //---------------------------------------------------------------------------// From 2053903573c58eee7a1cbe80bfab02ef8ed2a84e Mon Sep 17 00:00:00 2001 From: Damien L-G Date: Fri, 5 Jun 2020 13:42:09 -0400 Subject: [PATCH 09/10] Fixup typo in assert count -> counts --- core/src/Cabana_Experimental_NeighborList.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/Cabana_Experimental_NeighborList.hpp b/core/src/Cabana_Experimental_NeighborList.hpp index c2e7fd191..f64e38575 100644 --- a/core/src/Cabana_Experimental_NeighborList.hpp +++ b/core/src/Cabana_Experimental_NeighborList.hpp @@ -182,7 +182,7 @@ struct NeighborDiscriminatorCallback2D_SecondPass int const predicate_index = getData( predicate ); if ( CollisionFilter::keep( predicate_index, primitive_index ) ) { - assert( count( predicate_index ) < (int)neighbors.extent( 1 ) ); + assert( counts( predicate_index ) < (int)neighbors.extent( 1 ) ); neighbors( predicate_index, counts( predicate_index )++ ) = primitive_index; } From c79f7ac779369ad8a6a2b9659eba05244329ea0e Mon Sep 17 00:00:00 2001 From: Damien L-G Date: Fri, 5 Jun 2020 13:46:52 -0400 Subject: [PATCH 10/10] Comment that counter increment should be atomic --- core/src/Cabana_Experimental_NeighborList.hpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/src/Cabana_Experimental_NeighborList.hpp b/core/src/Cabana_Experimental_NeighborList.hpp index f64e38575..414e798f1 100644 --- a/core/src/Cabana_Experimental_NeighborList.hpp +++ b/core/src/Cabana_Experimental_NeighborList.hpp @@ -150,6 +150,7 @@ struct NeighborDiscriminatorCallback } }; +// Count in the first pass template struct NeighborDiscriminatorCallback2D_FirstPass { @@ -163,11 +164,12 @@ struct NeighborDiscriminatorCallback2D_FirstPass int const predicate_index = getData( predicate ); if ( CollisionFilter::keep( predicate_index, primitive_index ) ) { - ++counts( predicate_index ); + ++counts( predicate_index ); // WARNING see below** } } }; +// Fill in the second pass template struct NeighborDiscriminatorCallback2D_SecondPass { @@ -184,11 +186,14 @@ struct NeighborDiscriminatorCallback2D_SecondPass { assert( counts( predicate_index ) < (int)neighbors.extent( 1 ) ); neighbors( predicate_index, counts( predicate_index )++ ) = - primitive_index; + primitive_index; // WARNING see below** } } }; +// NOTE** Taking advantage of the knowledge that one predicate is processed by a +// single thread. Count increment should be atomic otherwise. + } // namespace Impl template