Skip to content

Commit 021f57f

Browse files
authored
Revise periodic box API (trixi-framework#30)
* Revise periodic box API * Allow `Vector`s in constructor of `PeriodicBox` * Fix tests * Fix docs for `TrivialNeighborhoodSearch`
1 parent fbee028 commit 021f57f

File tree

7 files changed

+61
-97
lines changed

7 files changed

+61
-97
lines changed

src/PointNeighbors.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ include("cell_lists/cell_lists.jl")
1313
include("nhs_grid.jl")
1414
include("nhs_neighbor_lists.jl")
1515

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

src/neighborhood_search.jl

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

44+
"""
45+
PeriodicBox(; min_corner, max_corner)
46+
47+
Define a rectangular periodic domain.
48+
49+
# Keywords
50+
- `min_corner`: Coordinates of the domain corner in negative coordinate directions.
51+
- `max_corner`: Coordinates of the domain corner in positive coordinate directions.
52+
"""
4453
struct PeriodicBox{NDIMS, ELTYPE}
4554
min_corner :: SVector{NDIMS, ELTYPE}
4655
max_corner :: SVector{NDIMS, ELTYPE}
4756
size :: SVector{NDIMS, ELTYPE}
4857

49-
function PeriodicBox(min_corner, max_corner)
50-
new{length(min_corner), eltype(min_corner)}(min_corner, max_corner,
51-
max_corner - min_corner)
58+
function PeriodicBox(; min_corner, max_corner)
59+
min_corner_ = SVector(Tuple(min_corner))
60+
max_corner_ = SVector(Tuple(max_corner))
61+
62+
new{length(min_corner), eltype(min_corner)}(min_corner_, max_corner_,
63+
max_corner_ - min_corner_)
5264
end
5365
end
5466

src/nhs_grid.jl

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@doc raw"""
2-
GridNeighborhoodSearch{NDIMS}(search_radius, n_points; periodic_box_min_corner=nothing,
3-
periodic_box_max_corner=nothing, threaded_nhs_update=true)
2+
GridNeighborhoodSearch{NDIMS}(search_radius, n_points;
3+
periodic_box = nothing, threaded_nhs_update = true)
44
55
Simple grid-based neighborhood search with uniform search radius.
66
The domain is divided into a regular grid.
@@ -28,15 +28,12 @@ since not sorting makes our implementation a lot faster (although less paralleli
2828
- `n_points`: Total number of points.
2929
3030
# Keywords
31-
- `periodic_box_min_corner`: In order to use a (rectangular) periodic domain, pass the
32-
coordinates of the domain corner in negative coordinate
33-
directions.
34-
- `periodic_box_max_corner`: In order to use a (rectangular) periodic domain, pass the
35-
coordinates of the domain corner in positive coordinate
36-
directions.
37-
- `threaded_nhs_update=true`: Can be used to deactivate thread parallelization in the neighborhood search update.
38-
This can be one of the largest sources of variations between simulations
39-
with different thread numbers due to neighbor ordering changes.
31+
- `periodic_box = nothing`: In order to use a (rectangular) periodic domain, pass a
32+
[`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.
4037
4138
## References
4239
- M. Chalela, E. Sillero, L. Pereyra, M.A. Garcia, J.B. Cabral, M. Lares, M. Merchán.
@@ -59,25 +56,19 @@ struct GridNeighborhoodSearch{NDIMS, ELTYPE, CL, PB} <: AbstractNeighborhoodSear
5956
threaded_nhs_update :: Bool
6057

6158
function GridNeighborhoodSearch{NDIMS}(search_radius, n_points;
62-
periodic_box_min_corner = nothing,
63-
periodic_box_max_corner = nothing,
59+
periodic_box = nothing,
6460
threaded_nhs_update = true) where {NDIMS}
6561
ELTYPE = typeof(search_radius)
6662
cell_list = DictionaryCellList{NDIMS}()
6763

6864
cell_buffer = Array{NTuple{NDIMS, Int}, 2}(undef, n_points, Threads.nthreads())
6965
cell_buffer_indices = zeros(Int, Threads.nthreads())
7066

71-
if search_radius < eps() ||
72-
(periodic_box_min_corner === nothing && periodic_box_max_corner === nothing)
73-
67+
if search_radius < eps() || isnothing(periodic_box)
7468
# No periodicity
75-
periodic_box = nothing
7669
n_cells = ntuple(_ -> -1, Val(NDIMS))
7770
cell_size = ntuple(_ -> search_radius, Val(NDIMS))
78-
elseif periodic_box_min_corner !== nothing && periodic_box_max_corner !== nothing
79-
periodic_box = PeriodicBox(periodic_box_min_corner, periodic_box_max_corner)
80-
71+
else
8172
# Round up search radius so that the grid fits exactly into the domain without
8273
# splitting any cells. This might impact performance slightly, since larger
8374
# cells mean that more potential neighbors are considered than necessary.
@@ -91,9 +82,6 @@ struct GridNeighborhoodSearch{NDIMS, ELTYPE, CL, PB} <: AbstractNeighborhoodSear
9182
"in each dimension when used with periodicity. " *
9283
"Please use no NHS for very small problems."))
9384
end
94-
else
95-
throw(ArgumentError("`periodic_box_min_corner` and `periodic_box_max_corner` " *
96-
"must either be both `nothing` or both an array or tuple"))
9785
end
9886

9987
new{NDIMS, ELTYPE, typeof(cell_list),
@@ -103,7 +91,7 @@ struct GridNeighborhoodSearch{NDIMS, ELTYPE, CL, PB} <: AbstractNeighborhoodSear
10391
end
10492
end
10593

106-
@inline Base.ndims(neighborhood_search::GridNeighborhoodSearch{NDIMS}) where {NDIMS} = NDIMS
94+
@inline Base.ndims(::GridNeighborhoodSearch{NDIMS}) where {NDIMS} = NDIMS
10795

10896
@inline function npoints(neighborhood_search::GridNeighborhoodSearch)
10997
return size(neighborhood_search.cell_buffer, 1)
@@ -321,13 +309,8 @@ end
321309

322310
# Create a copy of a neighborhood search but with a different search radius
323311
function copy_neighborhood_search(nhs::GridNeighborhoodSearch, search_radius, x, y)
324-
if nhs.periodic_box === nothing
325-
search = GridNeighborhoodSearch{ndims(nhs)}(search_radius, npoints(nhs))
326-
else
327-
search = GridNeighborhoodSearch{ndims(nhs)}(search_radius, npoints(nhs),
328-
periodic_box_min_corner = nhs.periodic_box.min_corner,
329-
periodic_box_max_corner = nhs.periodic_box.max_corner)
330-
end
312+
search = GridNeighborhoodSearch{ndims(nhs)}(search_radius, npoints(nhs),
313+
periodic_box = nhs.periodic_box)
331314

332315
# Initialize neighborhood search
333316
initialize!(search, x, y)

src/nhs_neighbor_lists.jl

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
@doc raw"""
22
PrecomputedNeighborhoodSearch{NDIMS}(search_radius, n_points;
3-
periodic_box_min_corner = nothing,
4-
periodic_box_max_corner = nothing)
3+
periodic_box = nothing)
54
65
Neighborhood search with precomputed neighbor lists. A list of all neighbors is computed
76
for each point during initialization and update.
@@ -17,32 +16,24 @@ initialization and update.
1716
- `n_points`: Total number of points.
1817
1918
# Keywords
20-
- `periodic_box_min_corner`: In order to use a (rectangular) periodic domain, pass the
21-
coordinates of the domain corner in negative coordinate
22-
directions.
23-
- `periodic_box_max_corner`: In order to use a (rectangular) periodic domain, pass the
24-
coordinates of the domain corner in positive coordinate
25-
directions.
19+
- `periodic_box = nothing`: In order to use a (rectangular) periodic domain, pass a
20+
[`PeriodicBox`](@ref).
2621
"""
2722
struct PrecomputedNeighborhoodSearch{NDIMS, NHS, NL, PB}
2823
neighborhood_search :: NHS
2924
neighbor_lists :: NL
3025
periodic_box :: PB
3126

3227
function PrecomputedNeighborhoodSearch{NDIMS}(search_radius, n_points;
33-
periodic_box_min_corner = nothing,
34-
periodic_box_max_corner = nothing) where {
35-
NDIMS
36-
}
28+
periodic_box = nothing) where {NDIMS}
3729
nhs = GridNeighborhoodSearch{NDIMS}(search_radius, n_points,
38-
periodic_box_min_corner = periodic_box_min_corner,
39-
periodic_box_max_corner = periodic_box_max_corner)
30+
periodic_box = periodic_box)
4031

4132
neighbor_lists = Vector{Vector{Int}}()
4233

4334
new{NDIMS, typeof(nhs),
4435
typeof(neighbor_lists),
45-
typeof(nhs.periodic_box)}(nhs, neighbor_lists, nhs.periodic_box)
36+
typeof(periodic_box)}(nhs, neighbor_lists, periodic_box)
4637
end
4738
end
4839

src/nhs_trivial.jl

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,30 @@
11
@doc raw"""
2-
TrivialNeighborhoodSearch{NDIMS}(search_radius, eachpoint)
2+
TrivialNeighborhoodSearch{NDIMS}(search_radius, eachpoint, periodic_box = nothing)
33
44
Trivial neighborhood search that simply loops over all points.
55
66
# Arguments
77
- `NDIMS`: Number of dimensions.
88
- `search_radius`: The uniform search radius.
9-
- `eachpoint`: `UnitRange` of all point indices. Usually just `1:n_points`.
9+
- `eachparticle`: Iterator for all point indices. Usually just `1:n_points`.
1010
1111
# Keywords
12-
- `periodic_box_min_corner`: In order to use a (rectangular) periodic domain, pass the
13-
coordinates of the domain corner in negative coordinate
14-
directions.
15-
- `periodic_box_max_corner`: In order to use a (rectangular) periodic domain, pass the
16-
coordinates of the domain corner in positive coordinate
17-
directions.
12+
- `periodic_box = nothing`: In order to use a (rectangular) periodic domain, pass a
13+
[`PeriodicBox`](@ref).
1814
"""
1915
struct TrivialNeighborhoodSearch{NDIMS, ELTYPE, EP, PB} <: AbstractNeighborhoodSearch
2016
search_radius :: ELTYPE
2117
eachpoint :: EP
2218
periodic_box :: PB
2319

2420
function TrivialNeighborhoodSearch{NDIMS}(search_radius, eachpoint;
25-
periodic_box_min_corner = nothing,
26-
periodic_box_max_corner = nothing) where {
27-
NDIMS
28-
}
29-
if search_radius < eps() ||
30-
(periodic_box_min_corner === nothing && periodic_box_max_corner === nothing)
31-
32-
# No periodicity
33-
periodic_box = nothing
34-
elseif periodic_box_min_corner !== nothing && periodic_box_max_corner !== nothing
35-
periodic_box = PeriodicBox(periodic_box_min_corner, periodic_box_max_corner)
36-
else
37-
throw(ArgumentError("`periodic_box_min_corner` and `periodic_box_max_corner` " *
38-
"must either be both `nothing` or both an array or tuple"))
39-
end
40-
21+
periodic_box = nothing) where {NDIMS}
4122
new{NDIMS, typeof(search_radius),
4223
typeof(eachpoint), typeof(periodic_box)}(search_radius, eachpoint, periodic_box)
4324
end
4425
end
4526

46-
@inline function Base.ndims(neighborhood_search::TrivialNeighborhoodSearch{NDIMS}) where {
47-
NDIMS
48-
}
49-
return NDIMS
50-
end
27+
@inline Base.ndims(::TrivialNeighborhoodSearch{NDIMS}) where {NDIMS} = NDIMS
5128

5229
@inline initialize!(search::TrivialNeighborhoodSearch, x, y) = search
5330

test/neighborhood_search.jl

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@
2424
]
2525

2626
periodic_boxes = [
27-
([-0.1, -0.2], [0.2, 0.4]),
27+
PeriodicBox(min_corner = [-0.1, -0.2], max_corner = [0.2, 0.4]),
2828
# The `GridNeighborhoodSearch` is forced to round up the cell sizes in this test
2929
# to avoid split cells.
30-
([-0.1, -0.2], [0.205, 0.43]),
31-
([-0.1, -0.2, 0.05], [0.2, 0.4, 0.35]),
30+
PeriodicBox(min_corner = [-0.1, -0.2], max_corner = [0.205, 0.43]),
31+
PeriodicBox(min_corner = [-0.1, -0.2, 0.05], max_corner = [0.2, 0.4, 0.35]),
3232
]
3333

3434
@testset verbose=true "$(names[i])" for i in eachindex(names)
@@ -40,14 +40,11 @@
4040

4141
neighborhood_searches = [
4242
TrivialNeighborhoodSearch{NDIMS}(search_radius, 1:n_points,
43-
periodic_box_min_corner = periodic_boxes[i][1],
44-
periodic_box_max_corner = periodic_boxes[i][2]),
43+
periodic_box = periodic_boxes[i]),
4544
GridNeighborhoodSearch{NDIMS}(search_radius, n_points,
46-
periodic_box_min_corner = periodic_boxes[i][1],
47-
periodic_box_max_corner = periodic_boxes[i][2]),
45+
periodic_box = periodic_boxes[i]),
4846
PrecomputedNeighborhoodSearch{NDIMS}(search_radius, n_points,
49-
periodic_box_min_corner = periodic_boxes[i][1],
50-
periodic_box_max_corner = periodic_boxes[i][2]),
47+
periodic_box = periodic_boxes[i]),
5148
]
5249
neighborhood_searches_names = [
5350
"`TrivialNeighborhoodSearch`",

test/nhs_grid.jl

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -148,19 +148,18 @@
148148
]
149149

150150
periodic_boxes = [
151-
([-0.1, -0.2], [0.2, 0.4]),
151+
PeriodicBox(min_corner = [-0.1, -0.2], max_corner = [0.2, 0.4]),
152152
# The `GridNeighborhoodSearch` is forced to round up the cell sizes in this test
153153
# to avoid split cells.
154-
([-0.1, -0.2], [0.205, 0.43]),
155-
([-0.1, -0.2, 0.05], [0.2, 0.4, 0.35]),
154+
PeriodicBox(min_corner = [-0.1, -0.2], max_corner = [0.205, 0.43]),
155+
PeriodicBox(min_corner = [-0.1, -0.2, 0.05], max_corner = [0.2, 0.4, 0.35]),
156156
]
157157

158158
@testset verbose=true "$(names[i])" for i in eachindex(names)
159159
coords = coordinates[i]
160160

161161
nhs = GridNeighborhoodSearch{size(coords, 1)}(0.1, size(coords, 2),
162-
periodic_box_min_corner = periodic_boxes[i][1],
163-
periodic_box_max_corner = periodic_boxes[i][2])
162+
periodic_box = periodic_boxes[i])
164163

165164
initialize_grid!(nhs, coords)
166165

@@ -189,13 +188,18 @@
189188

190189
# 5 x 1 cells
191190
nhs = GridNeighborhoodSearch{2}(1.0, size(coords, 2),
192-
periodic_box_min_corner = [-1.5, 0.0],
193-
periodic_box_max_corner = [2.5, 3.0])
191+
periodic_box = PeriodicBox(min_corner = [
192+
-1.5,
193+
0.0,
194+
],
195+
max_corner = [
196+
2.5,
197+
3.0,
198+
]))
194199

195200
initialize_grid!(nhs, coords)
196201

197-
neighbors = [sort(unique(collect(PointNeighbors.eachneighbor(coords[:,
198-
i],
202+
neighbors = [sort(unique(collect(PointNeighbors.eachneighbor(coords[:, i],
199203
nhs))))
200204
for i in 1:2]
201205

0 commit comments

Comments
 (0)