From 908990d0c722aeea98c38fd3cd4ddb1312f0808d Mon Sep 17 00:00:00 2001 From: lmiq Date: Tue, 25 Aug 2026 19:38:48 -0300 Subject: [PATCH 1/3] add rebuilt option to update! --- docs/src/ParticleSystem/updating.md | 21 +++++++++++++++++++++ src/API/updating.jl | 10 ++++++++++ 2 files changed, 31 insertions(+) diff --git a/docs/src/ParticleSystem/updating.md b/docs/src/ParticleSystem/updating.md index 40dee44d..4e57ea1c 100644 --- a/docs/src/ParticleSystem/updating.md +++ b/docs/src/ParticleSystem/updating.md @@ -13,6 +13,7 @@ update!(system; cutoff = ..., # new cutoff distance unitcell = ..., # new unit cell parallel = ..., # true or false + rebuild = ..., # true or false (default false) ) ``` @@ -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. diff --git a/src/API/updating.jl b/src/API/updating.jl index c15996e6..080f334d 100644 --- a/src/API/updating.jl +++ b/src/API/updating.jl @@ -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 @@ -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.*)" => "" @@ -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!( @@ -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.")) @@ -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 From bc2980988377f514cdf81dd6c2c5d7c8ddfc1760 Mon Sep 17 00:00:00 2001 From: lmiq Date: Tue, 25 Aug 2026 19:42:29 -0300 Subject: [PATCH 2/3] add tests --- test/API/ParticleSystem.jl | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/API/ParticleSystem.jl b/test/API/ParticleSystem.jl index 3215fdc3..d5baef33 100644 --- a/test/API/ParticleSystem.jl +++ b/test/API/ParticleSystem.jl @@ -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 From 90a6dc26c9b2fe53cf226a8ebcfd2e716bf34d9a Mon Sep 17 00:00:00 2001 From: lmiq Date: Tue, 25 Aug 2026 19:45:06 -0300 Subject: [PATCH 3/3] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f47b4541..1ee04cee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 --------------