Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/PointNeighbors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ using LinearAlgebra: dot
using Polyester: Polyester
@reexport using StaticArrays: SVector

include("vector_of_vectors.jl")
include("util.jl")
include("vector_of_vectors.jl")
include("neighborhood_search.jl")
include("nhs_trivial.jl")
include("cell_lists/cell_lists.jl")
Expand Down
23 changes: 18 additions & 5 deletions src/nhs_precomputed.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
periodic_box = nothing, update_strategy = nothing,
update_neighborhood_search = GridNeighborhoodSearch{NDIMS}(),
backend = DynamicVectorOfVectors{Int32},
max_neighbors = max_neighbors(NDIMS))
max_neighbors = max_neighbors(NDIMS),
sort_neighbor_lists = true)

Neighborhood search with precomputed neighbor lists. A list of all neighbors is computed
for each point during initialization and update.
Expand Down Expand Up @@ -44,23 +45,29 @@ to strip the internal neighborhood search, which is not needed anymore.
- `max_neighbors`: Maximum number of neighbors per particle. This will be used to
allocate the `DynamicVectorOfVectors`. It is not used with
other backends. The default is 64 in 2D and 324 in 3D.
- `sort_neighbor_lists = true`: Whether to sort the neighbor lists after construction.
This can improve cache hits on CPUs and improve coalesced
memory access on GPUs.
Comment thread
efaulhaber marked this conversation as resolved.
"""
struct PrecomputedNeighborhoodSearch{NDIMS, NL, ELTYPE, PB, NHS} <:
AbstractNeighborhoodSearch
neighbor_lists :: NL
search_radius :: ELTYPE
periodic_box :: PB
neighborhood_search :: NHS
sort_neighbor_lists :: Bool

function PrecomputedNeighborhoodSearch{NDIMS}(neighbor_lists, search_radius,
periodic_box,
update_neighborhood_search) where {NDIMS}
update_neighborhood_search,
sort_neighbor_lists) where {NDIMS}
return new{NDIMS, typeof(neighbor_lists),
typeof(search_radius),
typeof(periodic_box),
typeof(update_neighborhood_search)}(neighbor_lists, search_radius,
periodic_box,
update_neighborhood_search)
update_neighborhood_search,
sort_neighbor_lists)
end
end

Expand All @@ -73,11 +80,13 @@ function PrecomputedNeighborhoodSearch{NDIMS}(; search_radius = 0.0, n_points =
periodic_box,
update_strategy),
backend = DynamicVectorOfVectors{Int32},
max_neighbors = max_neighbors(NDIMS)) where {NDIMS}
max_neighbors = max_neighbors(NDIMS),
sort_neighbor_lists = true) where {NDIMS}
neighbor_lists = construct_backend(backend, n_points, max_neighbors)

PrecomputedNeighborhoodSearch{NDIMS}(neighbor_lists, search_radius,
periodic_box, update_neighborhood_search)
periodic_box, update_neighborhood_search,
sort_neighbor_lists)
end

# Default values for maximum neighbor count
Expand Down Expand Up @@ -168,6 +177,10 @@ function initialize_neighbor_lists!(neighbor_lists::DynamicVectorOfVectors,
parallelization_backend) do point, neighbor, _, _
pushat!(neighbor_lists, point, neighbor)
end

if neighborhood_search.sort_neighbor_lists
sorteach!(neighbor_lists)
end
end

@inline function foreach_neighbor(f, neighbor_system_coords,
Expand Down
2 changes: 1 addition & 1 deletion src/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ For GPU arrays, the respective `KernelAbstractions.Backend` is returned.
"""
@inline default_backend(::AbstractArray) = PolyesterBackend()
@inline default_backend(x::AbstractGPUArray) = KernelAbstractions.get_backend(x)
@inline default_backend(x::DynamicVectorOfVectors) = default_backend(x.backend)
@inline default_backend(x::PermutedDimsArray) = default_backend(x.parent)

"""
@threaded backend for ... end
Expand Down
27 changes: 27 additions & 0 deletions src/vector_of_vectors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ end

@inline Base.size(vov::DynamicVectorOfVectors) = (vov.length_[],)

@inline default_backend(x::DynamicVectorOfVectors) = default_backend(x.backend)

@inline function Base.getindex(vov::DynamicVectorOfVectors, i)
(; backend, lengths) = vov

Expand Down Expand Up @@ -162,6 +164,31 @@ end
return vov
end

# Sort each inner vector
@inline function sorteach!(vov::DynamicVectorOfVectors)
# TODO remove this check when Metal supports sorting
if nameof(typeof(default_backend(vov.backend))) == :MetalBackend
@warn "sorting neighbor lists is not supported on Metal. Skipping sort."
Comment thread
efaulhaber marked this conversation as resolved.
return vov
end
Comment thread
efaulhaber marked this conversation as resolved.

@threaded default_backend(vov.backend) for i in axes(vov.backend, 2)
for j in (vov.lengths[i] + 1):size(vov.backend, 1)
@inbounds vov.backend[j, i] = typemax(eltype(vov.backend))
end
end

sort!(vov.backend, dims = 1)

Comment thread
efaulhaber marked this conversation as resolved.
return vov
end

@inline function sorteach!(vov::Vector{<:Vector{T}}) where {T}
sort!.(vov)

return vov
end

function max_inner_length(cells::DynamicVectorOfVectors, fallback)
return size(cells.backend, 1)
end
Expand Down
113 changes: 70 additions & 43 deletions test/vector_of_vectors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
vov = PointNeighbors.DynamicVectorOfVectors{ELTYPE}(max_outer_length = 20,
max_inner_length = 4)

# Test internal size
@test size(vov.backend) == (4, 20)

function verify(vov, vov_ref)
@test length(vov) == length(vov_ref)
@test eachindex(vov) == eachindex(vov_ref)
Expand All @@ -24,62 +21,92 @@
end
end

# Initial check
verify(vov, vov_ref)
@testset "Initial state" begin
# Test internal size
@test size(vov.backend) == (4, 20)

# Initial check
verify(vov, vov_ref)
end

@testset "First push!" begin
push!(vov_ref, type.([1, 3, 2]))
Comment thread
efaulhaber marked this conversation as resolved.
push!(vov, type.([1, 3, 2]))

verify(vov, vov_ref)
end

@testset "push! multiple items" begin
push!(vov_ref, type.([4]), type.([5, 6, 7, 8]))
push!(vov, type.([4]), type.([5, 6, 7, 8]))

verify(vov, vov_ref)
end

@testset "push! to an inner vector" begin
push!(vov_ref[1], type(12))
PointNeighbors.pushat!(vov, 1, type(12))

# First `push!`
push!(vov_ref, type.([1, 2, 3]))
push!(vov, type.([1, 2, 3]))
verify(vov, vov_ref)
end

verify(vov, vov_ref)
@testset "push! overflow" begin
error_ = ErrorException("cell list is full. Use a larger `max_points_per_cell`.")
@test_throws error_ PointNeighbors.pushat!(vov, 1, type(13))

# `push!` multiple items
push!(vov_ref, type.([4]), type.([5, 6, 7, 8]))
push!(vov, type.([4]), type.([5, 6, 7, 8]))
verify(vov, vov_ref)
end

verify(vov, vov_ref)
@testset "deleteat!" begin
# Delete entry of inner vector. Note that this changes the order of the elements.
deleteat!(vov_ref[3], 2)
PointNeighbors.deleteatat!(vov, 3, 2)

# `push!` to an inner vector
push!(vov_ref[1], type(12))
PointNeighbors.pushat!(vov, 1, type(12))
@test vov_ref[3] == type.([5, 7, 8])
@test vov[3] == type.([5, 8, 7])

# `push!` overflow
error_ = ErrorException("cell list is full. Use a larger `max_points_per_cell`.")
@test_throws error_ PointNeighbors.pushat!(vov, 1, type(13))
# Delete second to last entry
deleteat!(vov_ref[3], 2)
PointNeighbors.deleteatat!(vov, 3, 2)

verify(vov, vov_ref)
@test vov_ref[3] == type.([5, 8])
@test vov[3] == type.([5, 7])

# Delete entry of inner vector. Note that this changes the order of the elements.
deleteat!(vov_ref[3], 2)
PointNeighbors.deleteatat!(vov, 3, 2)
# Delete last entry
deleteat!(vov_ref[3], 2)
PointNeighbors.deleteatat!(vov, 3, 2)

@test vov_ref[3] == type.([5, 7, 8])
@test vov[3] == type.([5, 8, 7])
# Now they are identical again
verify(vov, vov_ref)

# Delete second to last entry
deleteat!(vov_ref[3], 2)
PointNeighbors.deleteatat!(vov, 3, 2)
# Delete the remaining entry of this vector
deleteat!(vov_ref[3], 1)
PointNeighbors.deleteatat!(vov, 3, 1)

@test vov_ref[3] == type.([5, 8])
@test vov[3] == type.([5, 7])
verify(vov, vov_ref)
end

# Delete last entry
deleteat!(vov_ref[3], 2)
PointNeighbors.deleteatat!(vov, 3, 2)
# Skip for Tuples
if ELTYPE <: Number
@testset "sorteach!" begin
# Make sure that the first inner vector is unsorted.
# If this fails, make sure the tests above don't yield a sorted vector.
@test vov[1] != sort(vov[1])

# Now they are identical again
verify(vov, vov_ref)
PointNeighbors.sorteach!(vov)
PointNeighbors.sorteach!(vov_ref)

# Delete the remaining entry of this vector
deleteat!(vov_ref[3], 1)
PointNeighbors.deleteatat!(vov, 3, 1)
@test vov[1] == sort(vov[1])

verify(vov, vov_ref)
verify(vov, vov_ref)
end
end

# `empty!`
empty!(vov_ref)
empty!(vov)
@testset "empty!" begin
empty!(vov_ref)
empty!(vov)

verify(vov, vov_ref)
verify(vov, vov_ref)
end
end
end
Loading