@@ -22,71 +22,102 @@ initialization and update.
2222 [`PeriodicBox`](@ref).
2323- `update_strategy`: Strategy to parallelize `update!` of the internally used
2424 `GridNeighborhoodSearch`. See [`GridNeighborhoodSearch`](@ref)
25- for available options.
25+ for available options. This is only used for the default value
26+ of `update_neighborhood_search` below.
27+ - `update_neighborhood_search = GridNeighborhoodSearch{NDIMS}(; periodic_box, update_strategy)`:
28+ The neighborhood search used to compute the neighbor lists.
29+ By default, a [`GridNeighborhoodSearch`](@ref) is used.
30+ - `backend = DynamicVectorOfVectors{Int32}`: Type of the data structure to store
31+ the neighbor lists. Can be
32+ - `Vector{Vector{Int32}}`: Scattered memory, but very memory-efficient.
33+ - `DynamicVectorOfVectors{Int32}`: Contiguous memory, optimizing cache-hits
34+ and GPU-compatible.
35+ - `max_neighbors`: Maximum number of neighbors per particle. This will be used to
36+ allocate the `DynamicVectorOfVectors`. It is not used with
37+ other backends. The default is 64 in 2D and 324 in 3D.
2638"""
27- struct PrecomputedNeighborhoodSearch{NDIMS, NHS, NL , PB} <: AbstractNeighborhoodSearch
28- neighborhood_search :: NHS
39+ struct PrecomputedNeighborhoodSearch{NDIMS, NL, ELTYPE , PB, NHS } < :
40+ AbstractNeighborhoodSearch
2941 neighbor_lists :: NL
42+ search_radius :: ELTYPE
3043 periodic_box :: PB
44+ neighborhood_search :: NHS
3145
32- function PrecomputedNeighborhoodSearch {NDIMS} (; search_radius = 0.0 , n_points = 0 ,
33- periodic_box = nothing ,
34- update_strategy = nothing ) where {NDIMS}
35- nhs = GridNeighborhoodSearch {NDIMS} (; search_radius, n_points,
36- periodic_box, update_strategy)
37-
38- neighbor_lists = Vector {Vector{Int}} ()
39-
40- new{NDIMS, typeof (nhs),
41- typeof (neighbor_lists),
42- typeof (periodic_box)}(nhs, neighbor_lists, periodic_box)
46+ function PrecomputedNeighborhoodSearch {NDIMS} (neighbor_lists, search_radius,
47+ periodic_box,
48+ update_neighborhood_search) where {NDIMS}
49+ return new{NDIMS, typeof (neighbor_lists),
50+ typeof (search_radius),
51+ typeof (periodic_box),
52+ typeof (update_neighborhood_search)}(neighbor_lists, search_radius,
53+ periodic_box,
54+ update_neighborhood_search)
4355 end
4456end
4557
58+ function PrecomputedNeighborhoodSearch {NDIMS} (; search_radius = 0.0 , n_points = 0 ,
59+ periodic_box = nothing ,
60+ update_strategy = nothing ,
61+ update_neighborhood_search = GridNeighborhoodSearch {NDIMS} (;
62+ search_radius,
63+ n_points,
64+ periodic_box,
65+ update_strategy),
66+ backend = DynamicVectorOfVectors{Int32},
67+ max_neighbors = 4 * NDIMS^ 4 ) where {NDIMS}
68+ neighbor_lists = construct_backend (nothing , backend, n_points, max_neighbors)
69+
70+ PrecomputedNeighborhoodSearch {NDIMS} (neighbor_lists, search_radius,
71+ periodic_box, update_neighborhood_search)
72+ end
73+
4674@inline Base. ndims (:: PrecomputedNeighborhoodSearch{NDIMS} ) where {NDIMS} = NDIMS
4775
4876@inline requires_update (:: PrecomputedNeighborhoodSearch ) = (true , true )
4977
50- @inline function search_radius (search:: PrecomputedNeighborhoodSearch )
51- return search_radius (search. neighborhood_search)
52- end
53-
5478function initialize! (search:: PrecomputedNeighborhoodSearch ,
5579 x:: AbstractMatrix , y:: AbstractMatrix ;
5680 parallelization_backend = default_backend (x),
5781 eachindex_y = axes (y, 2 ))
5882 (; neighborhood_search, neighbor_lists) = search
5983
84+ if eachindex_y != axes (y, 2 )
85+ error (" this neighborhood search does not support inactive points" )
86+ end
87+
6088 # Initialize grid NHS
61- initialize! (neighborhood_search, x, y; eachindex_y, parallelization_backend)
89+ initialize! (neighborhood_search, x, y; parallelization_backend)
6290
6391 initialize_neighbor_lists! (neighbor_lists, neighborhood_search, x, y,
64- parallelization_backend, eachindex_y)
92+ parallelization_backend)
93+
94+ return search
6595end
6696
67- # WARNING! Experimental feature:
68- # By default, determine the parallelization backend from the type of `x`.
69- # Optionally, pass a `KernelAbstractions.Backend` to run the KernelAbstractions.jl code
70- # on this backend. This can be useful to run GPU kernels on the CPU by passing
71- # `parallelization_backend = KernelAbstractions.CPU()`, even though `x isa Array`.
7297function update! (search:: PrecomputedNeighborhoodSearch ,
7398 x:: AbstractMatrix , y:: AbstractMatrix ;
7499 points_moving = (true , true ), parallelization_backend = default_backend (x),
75100 eachindex_y = axes (y, 2 ))
76101 (; neighborhood_search, neighbor_lists) = search
77102
78- # Update grid NHS
79- update! (neighborhood_search, x, y; eachindex_y, points_moving, parallelization_backend)
103+ if eachindex_y != axes (y, 2 )
104+ error (" this neighborhood search does not support inactive points" )
105+ end
106+
107+ # Update the internal neighborhood search
108+ update! (neighborhood_search, x, y; points_moving, parallelization_backend)
80109
81110 # Skip update if both point sets are static
82111 if any (points_moving)
83112 initialize_neighbor_lists! (neighbor_lists, neighborhood_search, x, y,
84- parallelization_backend, eachindex_y )
113+ parallelization_backend)
85114 end
115+
116+ return search
86117end
87118
88119function initialize_neighbor_lists! (neighbor_lists, neighborhood_search, x, y,
89- parallelization_backend, eachindex_y )
120+ parallelization_backend)
90121 # Initialize neighbor lists
91122 empty! (neighbor_lists)
92123 resize! (neighbor_lists, size (x, 2 ))
@@ -95,12 +126,28 @@ function initialize_neighbor_lists!(neighbor_lists, neighborhood_search, x, y,
95126 end
96127
97128 # Fill neighbor lists
98- foreach_point_neighbor (x, y, neighborhood_search; parallelization_backend,
99- points = eachindex_y ) do point, neighbor, _, _
129+ foreach_point_neighbor (x, y, neighborhood_search;
130+ parallelization_backend ) do point, neighbor, _, _
100131 push! (neighbor_lists[point], neighbor)
101132 end
102133end
103134
135+ function initialize_neighbor_lists! (neighbor_lists:: DynamicVectorOfVectors ,
136+ neighborhood_search, x, y, parallelization_backend)
137+ resize! (neighbor_lists, size (x, 2 ))
138+
139+ # `Base.empty!.(neighbor_lists)`, but for all backends
140+ @threaded parallelization_backend for i in eachindex (neighbor_lists)
141+ emptyat! (neighbor_lists, i)
142+ end
143+
144+ # Fill neighbor lists
145+ foreach_point_neighbor (x, y, neighborhood_search;
146+ parallelization_backend) do point, neighbor, _, _
147+ pushat! (neighbor_lists, point, neighbor)
148+ end
149+ end
150+
104151@inline function foreach_neighbor (f, neighbor_system_coords,
105152 neighborhood_search:: PrecomputedNeighborhoodSearch ,
106153 point, point_coords, search_radius)
132179
133180function copy_neighborhood_search (nhs:: PrecomputedNeighborhoodSearch ,
134181 search_radius, n_points; eachpoint = 1 : n_points)
135- update_strategy_ = nhs. neighborhood_search. update_strategy
182+ update_neighborhood_search = copy_neighborhood_search (nhs. neighborhood_search,
183+ search_radius, n_points;
184+ eachpoint)
185+ max_neighbors = max_inner_length (nhs. neighbor_lists, 4 * ndims (nhs)^ 4 )
136186 return PrecomputedNeighborhoodSearch {ndims(nhs)} (; search_radius, n_points,
137187 periodic_box = nhs. periodic_box,
138- update_strategy = update_strategy_)
188+ update_neighborhood_search,
189+ backend = typeof (nhs. neighbor_lists),
190+ max_neighbors)
191+ end
192+
193+ @inline function freeze_neighborhood_search (search:: PrecomputedNeighborhoodSearch )
194+ # Indicate that the neighborhood search is static and will not be updated anymore.
195+ # For the `PrecomputedNeighborhoodSearch`, strip the inner neighborhood search,
196+ # which is used only for initialization and updating.
197+ return PrecomputedNeighborhoodSearch {ndims(search)} (search. neighbor_lists,
198+ search. search_radius,
199+ search. periodic_box,
200+ nothing )
201+ end
202+
203+ # TODO move to `vector_of_vectors.jl`
204+ function max_inner_length (cells:: DynamicVectorOfVectors , fallback)
205+ return size (cells. backend, 1 )
206+ end
207+
208+ # Fallback when backend is a `Vector{Vector{T}}`. Only used for copying the cell list.
209+ function max_inner_length (:: Any , fallback)
210+ return fallback
139211end
0 commit comments