@@ -48,25 +48,23 @@ since not sorting makes our implementation a lot faster (although less paralleli
4848 In: Computer Graphics Forum 30.1 (2011), pages 99–112.
4949 [doi: 10.1111/J.1467-8659.2010.01832.X](https://doi.org/10.1111/J.1467-8659.2010.01832.X)
5050"""
51- struct GridNeighborhoodSearch{NDIMS, ELTYPE, PB} <: AbstractNeighborhoodSearch
52- hashtable :: Dict{NTuple{NDIMS, Int}, Vector{Int}}
51+ struct GridNeighborhoodSearch{NDIMS, ELTYPE, CL, PB} <: AbstractNeighborhoodSearch
52+ cell_list :: CL
5353 search_radius :: ELTYPE
54- empty_vector :: Vector{Int} # Just an empty vector (used in `eachneighbor`)
54+ periodic_box :: PB
55+ n_cells :: NTuple{NDIMS, Int} # Required to calculate periodic cell index
56+ cell_size :: NTuple{NDIMS, ELTYPE} # Required to calculate cell index
5557 cell_buffer :: Array{NTuple{NDIMS, Int}, 2} # Multithreaded buffer for `update!`
5658 cell_buffer_indices :: Vector{Int} # Store which entries of `cell_buffer` are initialized
57- periodic_box :: PB
58- n_cells :: NTuple{NDIMS, Int}
59- cell_size :: NTuple{NDIMS, ELTYPE}
6059 threaded_nhs_update :: Bool
6160
6261 function GridNeighborhoodSearch {NDIMS} (search_radius, n_particles;
6362 periodic_box_min_corner = nothing ,
6463 periodic_box_max_corner = nothing ,
6564 threaded_nhs_update = true ) where {NDIMS}
6665 ELTYPE = typeof (search_radius)
66+ cell_list = DictionaryCellList {NDIMS} ()
6767
68- hashtable = Dict {NTuple{NDIMS, Int}, Vector{Int}} ()
69- empty_vector = Int[]
7068 cell_buffer = Array {NTuple{NDIMS, Int}, 2} (undef, n_particles, Threads. nthreads ())
7169 cell_buffer_indices = zeros (Int, Threads. nthreads ())
7270
@@ -98,10 +96,10 @@ struct GridNeighborhoodSearch{NDIMS, ELTYPE, PB} <: AbstractNeighborhoodSearch
9896 " must either be both `nothing` or both an array or tuple" ))
9997 end
10098
101- new{NDIMS, ELTYPE,
102- typeof (periodic_box)}(hashtable , search_radius, empty_vector ,
103- cell_buffer, cell_buffer_indices,
104- periodic_box, n_cells, cell_size, threaded_nhs_update)
99+ new{NDIMS, ELTYPE, typeof (cell_list),
100+ typeof (periodic_box)}(cell_list , search_radius, periodic_box, n_cells ,
101+ cell_size, cell_buffer, cell_buffer_indices,
102+ threaded_nhs_update)
105103 end
106104end
107105
@@ -122,20 +120,16 @@ function initialize_grid!(neighborhood_search::GridNeighborhoodSearch{NDIMS},
122120end
123121
124122function initialize_grid! (neighborhood_search:: GridNeighborhoodSearch , coords_fun)
125- (; hashtable ) = neighborhood_search
123+ (; cell_list ) = neighborhood_search
126124
127- empty! (hashtable )
125+ empty! (cell_list )
128126
129127 for particle in 1 : nparticles (neighborhood_search)
130128 # Get cell index of the particle's cell
131129 cell = cell_coords (coords_fun (particle), neighborhood_search)
132130
133- # Add particle to corresponding cell or create cell if it does not exist
134- if haskey (hashtable, cell)
135- append! (hashtable[cell], particle)
136- else
137- hashtable[cell] = [particle]
138- end
131+ # Add particle to corresponding cell
132+ push_cell! (cell_list, cell, particle)
139133 end
140134
141135 return neighborhood_search
@@ -159,22 +153,21 @@ end
159153
160154# Modify the existing hash table by moving particles into their new cells
161155function update_grid! (neighborhood_search:: GridNeighborhoodSearch , coords_fun)
162- (; hashtable , cell_buffer, cell_buffer_indices, threaded_nhs_update) = neighborhood_search
156+ (; cell_list , cell_buffer, cell_buffer_indices, threaded_nhs_update) = neighborhood_search
163157
164- # Reset `cell_buffer` by moving all pointers to the beginning.
158+ # Reset `cell_buffer` by moving all pointers to the beginning
165159 cell_buffer_indices .= 0
166160
167- # Find all cells containing particles that now belong to another cell.
168- # `collect` the keyset to be able to loop over it with `@threaded`.
169- mark_changed_cell! (neighborhood_search, hashtable, coords_fun,
161+ # Find all cells containing particles that now belong to another cell
162+ mark_changed_cell! (neighborhood_search, cell_list, coords_fun,
170163 Val (threaded_nhs_update))
171164
172165 # Iterate over all marked cells and move the particles into their new cells.
173166 for thread in 1 : Threads. nthreads ()
174167 # Only the entries `1:cell_buffer_indices[thread]` are initialized for `thread`.
175168 for i in 1 : cell_buffer_indices[thread]
176169 cell = cell_buffer[i, thread]
177- particles = hashtable [cell]
170+ particles = cell_list [cell]
178171
179172 # Find all particles whose coordinates do not match this cell
180173 moved_particle_indices = (i for i in eachindex (particles)
@@ -187,35 +180,28 @@ function update_grid!(neighborhood_search::GridNeighborhoodSearch, coords_fun)
187180 new_cell_coords = cell_coords (coords_fun (particle), neighborhood_search)
188181
189182 # Add particle to corresponding cell or create cell if it does not exist
190- if haskey (hashtable, new_cell_coords)
191- append! (hashtable[new_cell_coords], particle)
192- else
193- hashtable[new_cell_coords] = [particle]
194- end
183+ push_cell! (cell_list, new_cell_coords, particle)
195184 end
196185
197- # Remove moved particles from this cell or delete the cell if it is now empty
198- if count (_ -> true , moved_particle_indices) == length (particles)
199- delete! (hashtable, cell)
200- else
201- deleteat! (particles, moved_particle_indices)
202- end
186+ # Remove moved particles from this cell
187+ deleteat_cell! (cell_list, cell, moved_particle_indices)
203188 end
204189 end
205190
206191 return neighborhood_search
207192end
208193
209- @inline function mark_changed_cell! (neighborhood_search, hashtable , coords_fun,
194+ @inline function mark_changed_cell! (neighborhood_search, cell_list , coords_fun,
210195 threaded_nhs_update:: Val{true} )
211- @threaded for cell in collect (keys (hashtable))
196+ # `collect` the keyset to be able to loop over it with `@threaded`
197+ @threaded for cell in collect (eachcell (cell_list))
212198 mark_changed_cell! (neighborhood_search, cell, coords_fun)
213199 end
214200end
215201
216- @inline function mark_changed_cell! (neighborhood_search, hashtable , coords_fun,
202+ @inline function mark_changed_cell! (neighborhood_search, cell_list , coords_fun,
217203 threaded_nhs_update:: Val{false} )
218- for cell in collect ( keys (hashtable) )
204+ for cell in eachcell (cell_list )
219205 mark_changed_cell! (neighborhood_search, cell, coords_fun)
220206 end
221207end
225211# Otherwise, `@threaded` does not work here with Julia ARM on macOS.
226212# See https://github.com/JuliaSIMD/Polyester.jl/issues/88.
227213@inline function mark_changed_cell! (neighborhood_search, cell, coords_fun)
228- (; hashtable , cell_buffer, cell_buffer_indices) = neighborhood_search
214+ (; cell_list , cell_buffer, cell_buffer_indices) = neighborhood_search
229215
230- for particle in hashtable [cell]
216+ for particle in cell_list [cell]
231217 if cell_coords (coords_fun (particle), neighborhood_search) != cell
232218 # Mark this cell and continue with the next one.
233219 #
277263end
278264
279265@inline function particles_in_cell (cell_index, neighborhood_search)
280- (; hashtable, empty_vector ) = neighborhood_search
266+ (; cell_list ) = neighborhood_search
281267
282- # Return an empty vector when `cell_index` is not a key of `hashtable` and
283- # reuse the empty vector to avoid allocations
284- return get (hashtable, periodic_cell_index (cell_index, neighborhood_search),
285- empty_vector)
268+ return cell_list[periodic_cell_index (cell_index, neighborhood_search)]
286269end
287270
288271@inline function periodic_cell_index (cell_index, neighborhood_search)
@@ -351,8 +334,3 @@ function copy_neighborhood_search(nhs::GridNeighborhoodSearch, search_radius, x,
351334
352335 return search
353336end
354-
355- # Create a copy of a neighborhood search but with a different search radius
356- function copy_neighborhood_search (nhs:: TrivialNeighborhoodSearch , search_radius, x, y)
357- return nhs
358- end
0 commit comments