Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ CellListMap.jl Changelog

Version 0.10.5-DEV
--------------
- ![FEATURE][badge-feature] Add `rebuild` keyword argument to `update!` to rebuild the cell lists immediately.

Version 0.10.4
--------------
Expand Down
21 changes: 21 additions & 0 deletions docs/src/ParticleSystem/updating.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ update!(system;
cutoff = ..., # new cutoff distance
unitcell = ..., # new unit cell
parallel = ..., # true or false
rebuild = ..., # true or false (default false)
)
```

Expand Down Expand Up @@ -117,3 +118,23 @@ update!(system; parallel=false) # disable multi-threading

update!(system; parallel=true) # enable multi-threading
```

## Triggering an immediate cell list rebuild

By default, `update!` only records the new properties (coordinates, cutoff, unitcell);
the cell lists themselves are rebuilt lazily, the next time `pairwise!` or `neighborlist!`
is called. This avoids redundant rebuilds when several properties are updated across
multiple `update!` calls before the next pairwise computation.

If the cell lists need to be up to date *immediately* — for instance to inspect them,
or to time the rebuild separately from a pairwise computation — pass `rebuild=true`.
This also picks up any pending changes made directly through the positions array
interface (e.g. `setindex!`), not just the kwargs passed in the same call:

```julia-repl
julia> update!(system; cutoff=0.2, rebuild=true) # cutoff is updated and cell lists are rebuilt now
```

`update!(system)` (no keyword arguments) remains a documented no-op; it does not
change any property, and therefore does not trigger a rebuild on its own — `rebuild=true`
must be passed explicitly.
10 changes: 10 additions & 0 deletions src/API/updating.jl
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ end
cutoff = nothing,
unitcell = nothing,
parallel = nothing,
rebuild = false,
)

Update one or more properties of `sys` in a single call. Only the keyword
Expand All @@ -144,6 +145,11 @@ arguments that are provided (i.e. not `nothing`) are updated.

- `parallel`: whether to use multi-threading (`true` or `false`).

- `rebuild`: if `true`, the cell lists are rebuilt immediately, reflecting any
pending changes (from this call or from prior direct mutations of
`sys.xpositions`/`sys.ypositions`). By default (`false`), the rebuild is
deferred to the next call of `pairwise!` or `neighborlist!`, as usual.

# Example

```jldoctest ;filter = r"( +Parallelization.*)" => ""
Expand All @@ -160,6 +166,8 @@ julia> new_x = rand(SVector{3,Float64}, 100);

julia> update!(sys; xpositions=new_x, cutoff=0.2, unitcell=[2,2,2], parallel=false);

julia> update!(sys; rebuild=true); # force the cell lists to be rebuilt now

```
"""
function update!(
Expand All @@ -170,6 +178,7 @@ function update!(
cutoff = nothing,
unitcell = nothing,
parallel = nothing,
rebuild = false,
)
if !isnothing(positions) && !isnothing(xpositions)
throw(ArgumentError("Either `positions` OR `xpositions` must be provided, not both."))
Expand All @@ -183,6 +192,7 @@ function update!(
!isnothing(cutoff) && _update_cutoff!(sys, cutoff)
!isnothing(unitcell) && _update_unitcell!(sys, unitcell)
!isnothing(parallel) && setfield!(sys, :parallel, parallel)
rebuild && UpdateParticleSystem!(sys)
return sys
end

56 changes: 56 additions & 0 deletions test/API/ParticleSystem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,62 @@ end
@test a == Allocs(0)
end

@testitem "update! rebuild" setup = [AllocTest] begin
using BenchmarkTools
using StaticArrays
using CellListMap
x = rand(SVector{3, Float64}, 1000)

# update!(sys) with no kwargs is a no-op: it does not touch the dirty flag
sys = ParticleSystem(xpositions = x, unitcell = [1, 1, 1], cutoff = 0.1, output = 0.0)
@test sys.xpositions.updated[] == false
update!(sys)
@test sys.xpositions.updated[] == false

# a property update marks the cell lists as outdated, but does not rebuild them
new_x = rand(SVector{3, Float64}, 1000)
update!(sys; xpositions = new_x)
@test sys.xpositions.updated[] == true

# rebuild=true forces the rebuild immediately
update!(sys; rebuild = true)
@test sys.xpositions.updated[] == false

# rebuild=true also picks up pending changes from a previous call, or from
# direct mutation through the positions array interface
sys.xpositions[1] = SVector(0.0, 0.0, 0.0)
@test sys.xpositions.updated[] == true
update!(sys; rebuild = true)
@test sys.xpositions.updated[] == false

# rebuild can be combined with other kwargs in the same call
update!(sys; cutoff = 0.2, rebuild = true)
@test sys.cutoff == 0.2
@test sys.xpositions.updated[] == false

# rebuild=true is non-allocating when nothing is actually pending
a = @ballocated update!($sys; rebuild = true) evals = 1 samples = 1
@test a == Allocs(0)

# two-set systems: rebuild clears both x and y dirty flags
y = rand(SVector{3, Float64}, 1000)
sys2 = ParticleSystem(xpositions = x, ypositions = y, unitcell = [1, 1, 1], cutoff = 0.1, output = 0.0)
update!(sys2; ypositions = rand(SVector{3, Float64}, 1000))
@test sys2.xpositions.updated[] == false
@test sys2.ypositions.updated[] == true
update!(sys2; rebuild = true)
@test sys2.xpositions.updated[] == false
@test sys2.ypositions.updated[] == false

# rebuild=true actually rebuilds the cell lists: pairwise! sees the new positions
sys3 = ParticleSystem(
positions = [SVector(0.0, 0.0, 0.0), SVector(0.05, 0.0, 0.0)],
cutoff = 0.1, unitcell = [1, 1, 1], output = 0.0,
)
update!(sys3; positions = [SVector(0.0, 0.0, 0.0), SVector(0.08, 0.0, 0.0)], rebuild = true)
@test CellListMap.pairwise!((pair, out) -> out += pair.d, sys3) ≈ 0.08
end

@testitem "get_computing_box" begin
using StaticArrays
using CellListMap
Expand Down
Loading