Skip to content

Commit 5d52dfd

Browse files
authored
Implement copy_neighborhood_search to allow empty "template" neighborhood search (trixi-framework#32)
* Rename `threaded_nhs_update` to `threaded_update` * Add `threaded_update` option to `PrecomputedNeighborhoodSearch` * Properly implement `copy_neighborhood_search` * Create empty NHS templates by default * Fix tests * Add tests for copied templates * Add doctest * Fix rebase * Break doctests to verify that they are running * Revert "Break doctests to verify that they are running" This reverts commit 7e4275d. * Fix typo * Implement suggestions
1 parent 021f57f commit 5d52dfd

File tree

9 files changed

+146
-63
lines changed

9 files changed

+146
-63
lines changed

src/PointNeighbors.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ include("neighborhood_search.jl")
1111
include("nhs_trivial.jl")
1212
include("cell_lists/cell_lists.jl")
1313
include("nhs_grid.jl")
14-
include("nhs_neighbor_lists.jl")
14+
include("nhs_precomputed.jl")
1515

16-
export foreach_point_neighbor, foreach_neighbor, PeriodicBox
16+
export foreach_point_neighbor, foreach_neighbor
1717
export TrivialNeighborhoodSearch, GridNeighborhoodSearch, PrecomputedNeighborhoodSearch
1818
export initialize!, update!, initialize_grid!, update_grid!
19+
export PeriodicBox, copy_neighborhood_search
1920

2021
end # module PointNeighbors

src/neighborhood_search.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,37 @@ See also [`initialize!`](@ref).
4141
return search
4242
end
4343

44+
"""
45+
copy_neighborhood_search(search::AbstractNeighborhoodSearch, search_radius, n_points;
46+
eachpoint = 1:n_points)
47+
48+
Create a new **uninitialized** neighborhood search of the same type and with the same
49+
configuration options as `search`, but with a different search radius and number of points.
50+
51+
The [`TrivialNeighborhoodSearch`](@ref) also requires an iterator `eachpoint`, which most
52+
of the time will be `1:n_points`. If the `TrivialNeighborhoodSearch` is never going to be
53+
used, the keyword argument `eachpoint` can be ignored.
54+
55+
This is useful when a simulation code requires multiple neighborhood searches of the same
56+
kind. One can then just pass an empty neighborhood search as a template and use
57+
this function inside the simulation code to generate similar neighborhood searches with
58+
different search radii and different numbers of points.
59+
```jldoctest; filter = r"GridNeighborhoodSearch{2,.*"
60+
# Template
61+
nhs = GridNeighborhoodSearch{2}()
62+
63+
# Inside the simulation code, generate similar neighborhood searches
64+
nhs1 = copy_neighborhood_search(nhs, 1.0, 100)
65+
66+
# output
67+
GridNeighborhoodSearch{2, Float64, ...}(...)
68+
```
69+
"""
70+
@inline function copy_neighborhood_search(search::AbstractNeighborhoodSearch,
71+
search_radius, n_points; eachpoint = 1:n_points)
72+
return nothing
73+
end
74+
4475
"""
4576
PeriodicBox(; min_corner, max_corner)
4677

src/nhs_grid.jl

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@doc raw"""
2-
GridNeighborhoodSearch{NDIMS}(search_radius, n_points;
3-
periodic_box = nothing, threaded_nhs_update = true)
2+
GridNeighborhoodSearch{NDIMS}(; search_radius = 0.0, n_points = 0,
3+
periodic_box = nothing, threaded_update = true)
44
55
Simple grid-based neighborhood search with uniform search radius.
66
The domain is divided into a regular grid.
@@ -23,17 +23,19 @@ As opposed to (Ihmsen et al. 2011), we do not sort the points in any way,
2323
since not sorting makes our implementation a lot faster (although less parallelizable).
2424
2525
# Arguments
26-
- `NDIMS`: Number of dimensions.
27-
- `search_radius`: The uniform search radius.
28-
- `n_points`: Total number of points.
26+
- `NDIMS`: Number of dimensions.
2927
3028
# Keywords
29+
- `search_radius = 0.0`: The fixed search radius. The default of `0.0` is useful together
30+
with [`copy_neighborhood_search`](@ref).
31+
- `n_points = 0`: Total number of points. The default of `0` is useful together
32+
with [`copy_neighborhood_search`](@ref).
3133
- `periodic_box = nothing`: In order to use a (rectangular) periodic domain, pass a
3234
[`PeriodicBox`](@ref).
33-
- `threaded_nhs_update = true`: Can be used to deactivate thread parallelization in the
34-
neighborhood search update. This can be one of the largest
35-
sources of variations between simulations with different
36-
thread numbers due to neighbor ordering changes.
35+
- `threaded_update = true`: Can be used to deactivate thread parallelization in the
36+
neighborhood search update. This can be one of the largest
37+
sources of variations between simulations with different
38+
thread numbers due to neighbor ordering changes.
3739
3840
## References
3941
- M. Chalela, E. Sillero, L. Pereyra, M.A. Garcia, J.B. Cabral, M. Lares, M. Merchán.
@@ -53,11 +55,11 @@ struct GridNeighborhoodSearch{NDIMS, ELTYPE, CL, PB} <: AbstractNeighborhoodSear
5355
cell_size :: NTuple{NDIMS, ELTYPE} # Required to calculate cell index
5456
cell_buffer :: Array{NTuple{NDIMS, Int}, 2} # Multithreaded buffer for `update!`
5557
cell_buffer_indices :: Vector{Int} # Store which entries of `cell_buffer` are initialized
56-
threaded_nhs_update :: Bool
58+
threaded_update :: Bool
5759

58-
function GridNeighborhoodSearch{NDIMS}(search_radius, n_points;
60+
function GridNeighborhoodSearch{NDIMS}(; search_radius = 0.0, n_points = 0,
5961
periodic_box = nothing,
60-
threaded_nhs_update = true) where {NDIMS}
62+
threaded_update = true) where {NDIMS}
6163
ELTYPE = typeof(search_radius)
6264
cell_list = DictionaryCellList{NDIMS}()
6365

@@ -87,7 +89,7 @@ struct GridNeighborhoodSearch{NDIMS, ELTYPE, CL, PB} <: AbstractNeighborhoodSear
8789
new{NDIMS, ELTYPE, typeof(cell_list),
8890
typeof(periodic_box)}(cell_list, search_radius, periodic_box, n_cells,
8991
cell_size, cell_buffer, cell_buffer_indices,
90-
threaded_nhs_update)
92+
threaded_update)
9193
end
9294
end
9395

@@ -141,14 +143,14 @@ end
141143

142144
# Modify the existing hash table by moving points into their new cells
143145
function update_grid!(neighborhood_search::GridNeighborhoodSearch, coords_fun)
144-
(; cell_list, cell_buffer, cell_buffer_indices, threaded_nhs_update) = neighborhood_search
146+
(; cell_list, cell_buffer, cell_buffer_indices, threaded_update) = neighborhood_search
145147

146148
# Reset `cell_buffer` by moving all pointers to the beginning
147149
cell_buffer_indices .= 0
148150

149151
# Find all cells containing points that now belong to another cell
150152
mark_changed_cell!(neighborhood_search, cell_list, coords_fun,
151-
Val(threaded_nhs_update))
153+
Val(threaded_update))
152154

153155
# Iterate over all marked cells and move the points into their new cells.
154156
for thread in 1:Threads.nthreads()
@@ -180,15 +182,15 @@ function update_grid!(neighborhood_search::GridNeighborhoodSearch, coords_fun)
180182
end
181183

182184
@inline function mark_changed_cell!(neighborhood_search, cell_list, coords_fun,
183-
threaded_nhs_update::Val{true})
185+
threaded_update::Val{true})
184186
# `collect` the keyset to be able to loop over it with `@threaded`
185187
@threaded for cell in collect(eachcell(cell_list))
186188
mark_changed_cell!(neighborhood_search, cell, coords_fun)
187189
end
188190
end
189191

190192
@inline function mark_changed_cell!(neighborhood_search, cell_list, coords_fun,
191-
threaded_nhs_update::Val{false})
193+
threaded_update::Val{false})
192194
for cell in eachcell(cell_list)
193195
mark_changed_cell!(neighborhood_search, cell, coords_fun)
194196
end
@@ -307,13 +309,9 @@ end
307309
return floor(Int, i)
308310
end
309311

310-
# Create a copy of a neighborhood search but with a different search radius
311-
function copy_neighborhood_search(nhs::GridNeighborhoodSearch, search_radius, x, y)
312-
search = GridNeighborhoodSearch{ndims(nhs)}(search_radius, npoints(nhs),
313-
periodic_box = nhs.periodic_box)
314-
315-
# Initialize neighborhood search
316-
initialize!(search, x, y)
317-
318-
return search
312+
function copy_neighborhood_search(nhs::GridNeighborhoodSearch, search_radius, n_points;
313+
eachpoint = 1:n_points)
314+
return GridNeighborhoodSearch{ndims(nhs)}(; search_radius, n_points,
315+
periodic_box = nhs.periodic_box,
316+
threaded_update = nhs.threaded_update)
319317
end

src/nhs_neighbor_lists.jl renamed to src/nhs_precomputed.jl

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@doc raw"""
2-
PrecomputedNeighborhoodSearch{NDIMS}(search_radius, n_points;
3-
periodic_box = nothing)
2+
PrecomputedNeighborhoodSearch{NDIMS}(; search_radius = 0.0, n_points = 0,
3+
periodic_box = nothing, threaded_update = true)
44
55
Neighborhood search with precomputed neighbor lists. A list of all neighbors is computed
66
for each point during initialization and update.
@@ -11,23 +11,31 @@ A [`GridNeighborhoodSearch`](@ref) is used internally to compute the neighbor li
1111
initialization and update.
1212
1313
# Arguments
14-
- `NDIMS`: Number of dimensions.
15-
- `search_radius`: The uniform search radius.
16-
- `n_points`: Total number of points.
14+
- `NDIMS`: Number of dimensions.
1715
1816
# Keywords
17+
- `search_radius = 0.0`: The fixed search radius. The default of `0.0` is useful together
18+
with [`copy_neighborhood_search`](@ref).
19+
- `n_points = 0`: Total number of points. The default of `0` is useful together
20+
with [`copy_neighborhood_search`](@ref).
1921
- `periodic_box = nothing`: In order to use a (rectangular) periodic domain, pass a
2022
[`PeriodicBox`](@ref).
23+
- `threaded_update = true`: Can be used to deactivate thread parallelization in the
24+
neighborhood search update. This can be one of the largest
25+
sources of variations between simulations with different
26+
thread numbers due to neighbor ordering changes.
2127
"""
2228
struct PrecomputedNeighborhoodSearch{NDIMS, NHS, NL, PB}
2329
neighborhood_search :: NHS
2430
neighbor_lists :: NL
2531
periodic_box :: PB
2632

27-
function PrecomputedNeighborhoodSearch{NDIMS}(search_radius, n_points;
28-
periodic_box = nothing) where {NDIMS}
29-
nhs = GridNeighborhoodSearch{NDIMS}(search_radius, n_points,
30-
periodic_box = periodic_box)
33+
function PrecomputedNeighborhoodSearch{NDIMS}(; search_radius = 0.0, n_points = 0,
34+
periodic_box = nothing,
35+
threaded_update = true) where {NDIMS}
36+
nhs = GridNeighborhoodSearch{NDIMS}(; search_radius, n_points,
37+
periodic_box = periodic_box,
38+
threaded_update = threaded_update)
3139

3240
neighbor_lists = Vector{Vector{Int}}()
3341

@@ -103,3 +111,11 @@ end
103111
@inline f(point, neighbor, pos_diff, distance)
104112
end
105113
end
114+
115+
function copy_neighborhood_search(nhs::PrecomputedNeighborhoodSearch,
116+
search_radius, n_points; eachpoint = 1:n_points)
117+
threaded_update = nhs.neighborhood_search.threaded_update
118+
return PrecomputedNeighborhoodSearch{ndims(nhs)}(; search_radius, n_points,
119+
periodic_box = nhs.periodic_box,
120+
threaded_update)
121+
end

src/nhs_trivial.jl

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
@doc raw"""
2-
TrivialNeighborhoodSearch{NDIMS}(search_radius, eachpoint, periodic_box = nothing)
2+
TrivialNeighborhoodSearch{NDIMS}(; search_radius = 0.0, eachpoint = 1:0,
3+
periodic_box = nothing)
34
45
Trivial neighborhood search that simply loops over all points.
56
67
# Arguments
7-
- `NDIMS`: Number of dimensions.
8-
- `search_radius`: The uniform search radius.
9-
- `eachparticle`: Iterator for all point indices. Usually just `1:n_points`.
8+
- `NDIMS`: Number of dimensions.
109
1110
# Keywords
11+
- `search_radius = 0.0`: The fixed search radius. The default of `0.0` is useful together
12+
with [`copy_neighborhood_search`](@ref).
13+
- `eachpoint = 1:0`: Iterator for all point indices. Usually just `1:n_points`.
14+
The default of `1:0` is useful together with
15+
[`copy_neighborhood_search`](@ref).
1216
- `periodic_box = nothing`: In order to use a (rectangular) periodic domain, pass a
1317
[`PeriodicBox`](@ref).
1418
"""
@@ -17,7 +21,7 @@ struct TrivialNeighborhoodSearch{NDIMS, ELTYPE, EP, PB} <: AbstractNeighborhoodS
1721
eachpoint :: EP
1822
periodic_box :: PB
1923

20-
function TrivialNeighborhoodSearch{NDIMS}(search_radius, eachpoint;
24+
function TrivialNeighborhoodSearch{NDIMS}(; search_radius = 0.0, eachpoint = 1:0,
2125
periodic_box = nothing) where {NDIMS}
2226
new{NDIMS, typeof(search_radius),
2327
typeof(eachpoint), typeof(periodic_box)}(search_radius, eachpoint, periodic_box)
@@ -39,3 +43,9 @@ end
3943
function copy_neighborhood_search(nhs::TrivialNeighborhoodSearch, search_radius, x, y)
4044
return nhs
4145
end
46+
47+
function copy_neighborhood_search(nhs::TrivialNeighborhoodSearch,
48+
search_radius, n_points; eachpoint = 1:n_points)
49+
return TrivialNeighborhoodSearch{ndims(nhs)}(; search_radius, eachpoint,
50+
periodic_box = nhs.periodic_box)
51+
end

test/neighborhood_search.jl

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,34 @@
3939
search_radius = 0.1
4040

4141
neighborhood_searches = [
42-
TrivialNeighborhoodSearch{NDIMS}(search_radius, 1:n_points,
42+
TrivialNeighborhoodSearch{NDIMS}(; search_radius, eachpoint = 1:n_points,
4343
periodic_box = periodic_boxes[i]),
44-
GridNeighborhoodSearch{NDIMS}(search_radius, n_points,
44+
GridNeighborhoodSearch{NDIMS}(; search_radius, n_points,
4545
periodic_box = periodic_boxes[i]),
46-
PrecomputedNeighborhoodSearch{NDIMS}(search_radius, n_points,
46+
PrecomputedNeighborhoodSearch{NDIMS}(; search_radius, n_points,
4747
periodic_box = periodic_boxes[i]),
4848
]
49-
neighborhood_searches_names = [
49+
50+
names = [
5051
"`TrivialNeighborhoodSearch`",
5152
"`GridNeighborhoodSearch`",
5253
"`PrecomputedNeighborhoodSearch`",
5354
]
5455

56+
# Also test copied templates
57+
template_nhs = [
58+
TrivialNeighborhoodSearch{NDIMS}(periodic_box = periodic_boxes[i]),
59+
GridNeighborhoodSearch{NDIMS}(periodic_box = periodic_boxes[i]),
60+
PrecomputedNeighborhoodSearch{NDIMS}(periodic_box = periodic_boxes[i]),
61+
]
62+
copied_nhs = copy_neighborhood_search.(template_nhs, search_radius, n_points)
63+
append!(neighborhood_searches, copied_nhs)
64+
65+
names_copied = [name * " copied" for name in names]
66+
append!(names, names_copied)
67+
5568
# Run this for every neighborhood search
56-
@testset "$(neighborhood_searches_names[j])" for j in eachindex(neighborhood_searches_names)
69+
@testset "$(names[j])" for j in eachindex(names)
5770
nhs = neighborhood_searches[j]
5871

5972
initialize!(nhs, coords, coords)
@@ -103,7 +116,8 @@
103116

104117
# Compute expected neighbor lists by brute-force looping over all points
105118
# as potential neighbors (`TrivialNeighborhoodSearch`).
106-
trivial_nhs = TrivialNeighborhoodSearch{NDIMS}(search_radius, axes(coords, 2))
119+
trivial_nhs = TrivialNeighborhoodSearch{NDIMS}(; search_radius,
120+
eachpoint = axes(coords, 2))
107121

108122
neighbors_expected = [Int[] for _ in axes(coords, 2)]
109123

@@ -114,16 +128,27 @@
114128
end
115129

116130
neighborhood_searches = [
117-
GridNeighborhoodSearch{NDIMS}(search_radius, n_points),
118-
PrecomputedNeighborhoodSearch{NDIMS}(search_radius, n_points),
131+
GridNeighborhoodSearch{NDIMS}(; search_radius, n_points),
132+
PrecomputedNeighborhoodSearch{NDIMS}(; search_radius, n_points),
119133
]
120134

121-
neighborhood_searches_names = [
135+
names = [
122136
"`GridNeighborhoodSearch`",
123137
"`PrecomputedNeighborhoodSearch`",
124138
]
125139

126-
@testset "$(neighborhood_searches_names[i])" for i in eachindex(neighborhood_searches_names)
140+
# Also test copied templates
141+
template_nhs = [
142+
GridNeighborhoodSearch{NDIMS}(),
143+
PrecomputedNeighborhoodSearch{NDIMS}(),
144+
]
145+
copied_nhs = copy_neighborhood_search.(template_nhs, search_radius, n_points)
146+
append!(neighborhood_searches, copied_nhs)
147+
148+
names_copied = [name * " copied" for name in names]
149+
append!(names, names_copied)
150+
151+
@testset "$(names[i])" for i in eachindex(names)
127152
nhs = neighborhood_searches[i]
128153

129154
# Initialize with `seed = 1`

0 commit comments

Comments
 (0)