From a5e5aaf78e1dec459d2579b877d4151d0ea111ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Mon, 19 Jan 2026 15:03:09 +0100 Subject: [PATCH 01/17] feat: iterator over neighbours for empty lists --- src/atoms.jl | 6 +++--- src/neighbours.jl | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/atoms.jl b/src/atoms.jl index b875cdc..9e55057 100644 --- a/src/atoms.jl +++ b/src/atoms.jl @@ -81,10 +81,10 @@ Compute the energy of particle `i` by brute force (no neighbour list). `compute_energy_particle(system, i, ::EmptyList)` sums interactions of particle `i` with all other particles using `compute_energy_ij`. """ -function compute_energy_particle(system::Atoms, i, ::EmptyList) +function compute_energy_particle(system::Atoms, i, neighbour_list::EmptyList) energy_i = zero(typeof(system.density)) position_i = get_position(system, i) - for (j, _) in enumerate(system) + for j in get_neighbour_indices(system, neighbour_list, i) energy_i += compute_energy_ij(system, i, j, position_i) end return energy_i @@ -136,4 +136,4 @@ function compute_energy_particle(system::Atoms, i, neighbour_list::LinkedList) end end return energy_i -end \ No newline at end of file +end diff --git a/src/neighbours.jl b/src/neighbours.jl index 2b1aafe..df125eb 100644 --- a/src/neighbours.jl +++ b/src/neighbours.jl @@ -42,6 +42,12 @@ function old_new_cell(::Particles, i, ::EmptyList) return 1, 1 end +"""Iterate over all system particles. +""" +function get_neighbour_indices(system::Particles, neighbour_list::EmptyList, ::Int) + return (j for j in system) +end + """Return the scalar cell index of particle `i` stored in `neighbour_list`. """ function get_cell_index(i::Int, neighbour_list::NeighbourList) From efe19c6edf92a8f507da2891b19b339f9d252080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Mon, 19 Jan 2026 15:09:04 +0100 Subject: [PATCH 02/17] feat: general function to loop over celllists --- src/atoms.jl | 12 ++---------- src/neighbours.jl | 11 +++++++++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/atoms.jl b/src/atoms.jl index 9e55057..32acbb2 100644 --- a/src/atoms.jl +++ b/src/atoms.jl @@ -99,16 +99,8 @@ This restricts pair evaluations to particles in neighbouring cells of `i`. function compute_energy_particle(system::Atoms, i, neighbour_list::CellList) energy_i = zero(typeof(system.density)) position_i = get_position(system, i) - c = get_cell_index(position_i, neighbour_list) - neighbour_cells = neighbour_list.neighbour_cells[c] - - # Scan the neighbourhood of cell mc (including itself) - @inbounds for c2 in neighbour_cells - # Scan atoms in cell c2 - neighbours = neighbour_list.cells[c2] - @inbounds for j in neighbours - energy_i += compute_energy_ij(system, i, j, position_i) - end + for j in get_neighbour_indices(system, neighbour_list, i) + energy_i += compute_energy_ij(system, i, j, position_i) end return energy_i end diff --git a/src/neighbours.jl b/src/neighbours.jl index df125eb..61347c8 100644 --- a/src/neighbours.jl +++ b/src/neighbours.jl @@ -201,6 +201,17 @@ function old_new_cell(system::Particles, i, neighbour_list::CellList) return c, c2 end +"""Iterate over the particles from adjacent cells. +""" +function get_neighbour_indices(system::Particles, neighbour_list::CellList, i::Int) + position_i = get_position(system, i) + c = get_cell_index(position_i, neighbour_list) + neighbour_cells = neighbour_list.neighbour_cells[c] + # Scan the neighbourhood of cell mc (including itself) + # and from there scan atoms in cell c2 + (j for c2 in neighbour_cells for j in neighbour_list.cells[c2]) +end + """Linked-list neighbour list implementation. Uses arrays `head` and `list` to store per-cell linked lists of particle indices. From eef47dc1dde16371ff38bd03fc632e4e45fdc95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Mon, 19 Jan 2026 15:20:26 +0100 Subject: [PATCH 03/17] feat: general function to loop over linkedlists --- src/atoms.jl | 47 ++--------------------------------------------- src/neighbours.jl | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 45 deletions(-) diff --git a/src/atoms.jl b/src/atoms.jl index 32acbb2..cbb9bb8 100644 --- a/src/atoms.jl +++ b/src/atoms.jl @@ -76,27 +76,9 @@ end """ -Compute the energy of particle `i` by brute force (no neighbour list). - -`compute_energy_particle(system, i, ::EmptyList)` sums interactions of particle `i` -with all other particles using `compute_energy_ij`. -""" -function compute_energy_particle(system::Atoms, i, neighbour_list::EmptyList) - energy_i = zero(typeof(system.density)) - position_i = get_position(system, i) - for j in get_neighbour_indices(system, neighbour_list, i) - energy_i += compute_energy_ij(system, i, j, position_i) - end - return energy_i -end - - +Compute the energy of particle `i` using the provided neighbour list. """ -Compute the energy of particle `i` using a `CellList` neighbour list. - -This restricts pair evaluations to particles in neighbouring cells of `i`. -""" -function compute_energy_particle(system::Atoms, i, neighbour_list::CellList) +function compute_energy_particle(system::Atoms, i, neighbour_list::NeighbourList) energy_i = zero(typeof(system.density)) position_i = get_position(system, i) for j in get_neighbour_indices(system, neighbour_list, i) @@ -104,28 +86,3 @@ function compute_energy_particle(system::Atoms, i, neighbour_list::CellList) end return energy_i end - -""" -Compute the energy of particle `i` using a `LinkedList` neighbour list. - -This variant iterates linked list heads for neighbouring cells and accumulates -pair energies computed with `compute_energy_ij`. -""" -function compute_energy_particle(system::Atoms, i, neighbour_list::LinkedList) - energy_i = zero(typeof(system.density)) - # Get cell of particle i - position_i = get_position(system, i) - c = get_cell_index(position_i, neighbour_list) - neighbour_cells = neighbour_list.neighbour_cells[c] - # Scan the neighbourhood of cell mc (including itself) - @inbounds for c2 in neighbour_cells - # Scan atoms in cell c2 - j = neighbour_list.head[c2] - while (j != -1) - energy_ij = compute_energy_ij(system, i, j, position_i) - energy_i += energy_ij - j = neighbour_list.list[j] - end - end - return energy_i -end diff --git a/src/neighbours.jl b/src/neighbours.jl index 61347c8..31c4756 100644 --- a/src/neighbours.jl +++ b/src/neighbours.jl @@ -298,3 +298,22 @@ function old_new_cell(system::Particles, i, neighbour_list::LinkedList) c2 = cell_index(neighbour_list, mc2) return c, c2 end + +"""Iterate over the particles from adjacent cells. +""" +function get_neighbour_indices(system::Particles, neighbour_list::LinkedList, i::Int) + position_i = get_position(system, i) + c = get_cell_index(position_i, neighbour_list) + neighbour_cells = neighbour_list.neighbour_cells[c] + + neighbours = [] + @inbounds for c2 in neighbour_cells + # Scan atoms in cell c2 + j = neighbour_list.head[c2] + while (j != -1) + push!(neighbours, j) + j = neighbour_list.list[j] + end + end + return neighbours +end From bdbe04db645b5ce204442e7de0fbe1b1a9735d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Mon, 19 Jan 2026 15:52:19 +0100 Subject: [PATCH 04/17] fix: empty list not iterate over indices, not positions --- src/neighbours.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neighbours.jl b/src/neighbours.jl index 31c4756..1bffb76 100644 --- a/src/neighbours.jl +++ b/src/neighbours.jl @@ -45,7 +45,7 @@ end """Iterate over all system particles. """ function get_neighbour_indices(system::Particles, neighbour_list::EmptyList, ::Int) - return (j for j in system) + return (j for j in 1:length(system)) end """Return the scalar cell index of particle `i` stored in `neighbour_list`. From 485f0c2aa8b819739cefa3972e5ae9774dcd4660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Mon, 19 Jan 2026 16:02:18 +0100 Subject: [PATCH 05/17] refactor: energy evaluation for molecules --- src/molecules.jl | 74 +++++++----------------------------------------- 1 file changed, 10 insertions(+), 64 deletions(-) diff --git a/src/molecules.jl b/src/molecules.jl index b006f5f..312a935 100644 --- a/src/molecules.jl +++ b/src/molecules.jl @@ -112,15 +112,15 @@ Return arrays of first indices and counts for consecutive blocks in `vec`. function get_first_and_counts(vec::Vector{Int}) firsts = Int[] counts = Int[] - + # Handle empty vector case isempty(vec) && return firsts, counts - + # Initialize with first element current = vec[1] push!(firsts, 1) count = 1 - + # Scan through vector @inbounds for i in 2:length(vec) if vec[i] != current @@ -134,7 +134,7 @@ function get_first_and_counts(vec::Vector{Int}) end # Add last count push!(counts, count) - + return firsts, counts end @@ -198,69 +198,15 @@ function compute_energy_ij(system::Molecules, position_i, position_j, model_ij:: end """ -Compute particle energy by brute force (no neighbour list). - -`compute_energy_particle(system, i, ::EmptyList)` sums interactions of particle `i` -with all particles (including bonded and non-bonded contributions via helper -functions). Used when no neighbour list is available. +Compute particle energy using a the provided neighbour list. """ -function compute_energy_particle(system::Molecules, i, ::EmptyList) - energy = zero(typeof(system.density)) +function compute_energy_particle(system::Molecules, i, neighbour_list::NeighbourList) position_i = system.position[i] bonds_i = system.bonds[i] - @inbounds for j in eachindex(system) - energy += check_compute_energy_ij(system, i, j, position_i, bonds_i) - end - return energy -end - -# With linked list -""" -Compute particle energy using a `LinkedList` neighbour list. -This variant restricts non-bonded pair evaluation to particles in neighbouring -cells defined by the linked list; bonded contributions are added explicitly. -""" -function compute_energy_particle(system::Molecules, i, neighbour_list::LinkedList) - energy_i = zero(typeof(system.density)) - # Get cell of particle i - position_i = system.position[i] - c = get_cell_index(i, neighbour_list) - cells = neighbour_list.neighbour_cells[c] - # Scan the neighbourhood of cell mc (including itself) - bonds_i = system.bonds[i] - energy_i += compute_energy_bonded_i(system, i, position_i, bonds_i) - @inbounds for c2 in cells - # Calculate the scalar cell index of the neighbour cell (with PBC) - j = neighbour_list.head[c2] - while (j != -1) - energy_i += check_nonbonded_compute_energy_ij(system, i, j, position_i, bonds_i) - j = neighbour_list.list[j] - end - end - return energy_i -end - -""" -Compute particle energy using a `CellList` neighbour list. - -This variant restricts non-bonded pair evaluation to particles in neighbouring -cells defined by the cell list; bonded contributions are added explicitly. -""" -function compute_energy_particle(system::Molecules, i, neighbour_list::CellList) - energy_i = zero(typeof(system.density)) - position_i = get_position(system, i) - c = get_cell_index(i, neighbour_list) - neighbour_cells = neighbour_list.neighbour_cells[c] - # Scan the neighbourhood of cell mc (including itself) - bonds_i = system.bonds[i] - energy_i += compute_energy_bonded_i(system, i, position_i, bonds_i) - @inbounds for c2 in neighbour_cells - # Scan atoms in cell c2 - neighbours = neighbour_list.cells[c2] - @inbounds for j in neighbours - energy_i += check_nonbonded_compute_energy_ij(system, i, j, position_i, bonds_i) - end + energy_i = compute_energy_bonded_i(system, i, position_i, bonds_i) + for j in get_neighbour_indices(system, neighbour_list, i) + energy_i += check_nonbonded_compute_energy_ij(system, i, j, position_i, bonds_i) end return energy_i end @@ -290,4 +236,4 @@ function compute_chain_correlation(system::Molecules) end end return sum(correlation_array.^2) -end \ No newline at end of file +end From a627835ec19085b07b2eeff8893dd37f1e5b8f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Mon, 19 Jan 2026 16:09:21 +0100 Subject: [PATCH 06/17] docs: more explicit docstring --- src/molecules.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/molecules.jl b/src/molecules.jl index 312a935..6f0d025 100644 --- a/src/molecules.jl +++ b/src/molecules.jl @@ -199,6 +199,9 @@ end """ Compute particle energy using a the provided neighbour list. + +Non-bonded pair energy evaluations are restricted to particles in the neighbour list; +bonded contributions are added explicitly. """ function compute_energy_particle(system::Molecules, i, neighbour_list::NeighbourList) position_i = system.position[i] From a02f7d8a73aaf43884b7da12217068f3224b240d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Mon, 19 Jan 2026 16:17:21 +0100 Subject: [PATCH 07/17] perf: add inbounds back in --- src/neighbours.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neighbours.jl b/src/neighbours.jl index 1bffb76..d9ba738 100644 --- a/src/neighbours.jl +++ b/src/neighbours.jl @@ -209,7 +209,7 @@ function get_neighbour_indices(system::Particles, neighbour_list::CellList, i::I neighbour_cells = neighbour_list.neighbour_cells[c] # Scan the neighbourhood of cell mc (including itself) # and from there scan atoms in cell c2 - (j for c2 in neighbour_cells for j in neighbour_list.cells[c2]) + (j for c2 in neighbour_cells for j in @inbounds neighbour_list.cells[c2]) end """Linked-list neighbour list implementation. From 3538f17daf8d6d200d3c77aba9669d7285f3c329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Mon, 19 Jan 2026 21:53:25 +0100 Subject: [PATCH 08/17] feat: create a subclass to contain the data for iteration --- src/neighbours.jl | 59 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/src/neighbours.jl b/src/neighbours.jl index d9ba738..398dba0 100644 --- a/src/neighbours.jl +++ b/src/neighbours.jl @@ -299,6 +299,52 @@ function old_new_cell(system::Particles, i, neighbour_list::LinkedList) return c, c2 end +struct LinkedIterator + neighbour_cells::Vector{Int} + head::Vector{Int} + list::Vector{Int} +end + +function Base.iterate(neighbour_list::LinkedIterator, state=-1) + + #@inbounds for c2 in neighbour_cells + # j = neighbour_list.head[c2] + # while (j != -1) + # j = neighbour_list.list[j] + # end + #end + + # First time in + if state == -1 + c, c_state = iterate(neighbour_list.neighbour_cells) + j = neighbour_list.head[c] + state = (c_state, j) + return j, state + end + + c_state, j = state + if j == -1 + next = iterate(neighbour_cells, c_state) + if next == nothing + return nothing + end + c, c_state = next + j = neighbour_list.head[c] + if j == -1 + return nothing + end + state = (c_state, j) + return j, state + end + j = neighbour_list.list[j] + if j == -1 + return nothing + end + state = (c_state, j) + return j, state +end + + """Iterate over the particles from adjacent cells. """ function get_neighbour_indices(system::Particles, neighbour_list::LinkedList, i::Int) @@ -306,14 +352,7 @@ function get_neighbour_indices(system::Particles, neighbour_list::LinkedList, i: c = get_cell_index(position_i, neighbour_list) neighbour_cells = neighbour_list.neighbour_cells[c] - neighbours = [] - @inbounds for c2 in neighbour_cells - # Scan atoms in cell c2 - j = neighbour_list.head[c2] - while (j != -1) - push!(neighbours, j) - j = neighbour_list.list[j] - end - end - return neighbours + iterator = LinkedIterator(neighbour_cells, neighbour_list.head, neighbour_list.list) + return (j for j in iterator) + end From 910498d5addf71c83f1c932ced3a080e0d87e771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Mon, 19 Jan 2026 22:17:01 +0100 Subject: [PATCH 09/17] fix: get correct implementation of iterator --- src/neighbours.jl | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/neighbours.jl b/src/neighbours.jl index 398dba0..a03bec5 100644 --- a/src/neighbours.jl +++ b/src/neighbours.jl @@ -307,38 +307,37 @@ end function Base.iterate(neighbour_list::LinkedIterator, state=-1) - #@inbounds for c2 in neighbour_cells - # j = neighbour_list.head[c2] + #@inbounds for c in neighbour_list.neighbour_cells + # j = neighbour_list.head[c] # while (j != -1) + # do stuff # j = neighbour_list.list[j] # end #end # First time in if state == -1 - c, c_state = iterate(neighbour_list.neighbour_cells) - j = neighbour_list.head[c] - state = (c_state, j) - return j, state - end - - c_state, j = state - if j == -1 - next = iterate(neighbour_cells, c_state) + next = iterate(neighbour_list.neighbour_cells) if next == nothing return nothing end c, c_state = next j = neighbour_list.head[c] + else + c_state, j = state + j = neighbour_list.list[j] if j == -1 - return nothing + next = iterate(neighbour_list.neighbour_cells, c_state) + if next == nothing + return nothing + end + c, c_state = next + j = neighbour_list.head[c] end - state = (c_state, j) - return j, state end - j = neighbour_list.list[j] + if j == -1 - return nothing + return nothing end state = (c_state, j) return j, state From 39c62e10095b47a6f5c24681be4a345103d7e6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Tue, 20 Jan 2026 09:06:33 +0100 Subject: [PATCH 10/17] docs: add some info about the implementation --- src/neighbours.jl | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/neighbours.jl b/src/neighbours.jl index a03bec5..7878f50 100644 --- a/src/neighbours.jl +++ b/src/neighbours.jl @@ -299,22 +299,25 @@ function old_new_cell(system::Particles, i, neighbour_list::LinkedList) return c, c2 end +""" This struct is used to iterate over neighbours of a Linked list +""" struct LinkedIterator neighbour_cells::Vector{Int} head::Vector{Int} list::Vector{Int} end +# To iterate over the neighbours of a linked list, one could write the following loops +#@inbounds for c in neighbour_list.neighbour_cells +# j = neighbour_list.head[c] +# while (j != -1) +# do stuff +# j = neighbour_list.list[j] +# end +#end +# This is however impossible to rewrite as a simple generator +# So we implement the following function, which uses a state to carry over the needed information function Base.iterate(neighbour_list::LinkedIterator, state=-1) - - #@inbounds for c in neighbour_list.neighbour_cells - # j = neighbour_list.head[c] - # while (j != -1) - # do stuff - # j = neighbour_list.list[j] - # end - #end - # First time in if state == -1 next = iterate(neighbour_list.neighbour_cells) From cf5a0b5e962a55320292495125f9c0b5958a096f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Tue, 20 Jan 2026 11:56:50 +0100 Subject: [PATCH 11/17] feat: start moving to pure iterators --- src/atoms.jl | 2 +- src/neighbours.jl | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/atoms.jl b/src/atoms.jl index cbb9bb8..f528a4c 100644 --- a/src/atoms.jl +++ b/src/atoms.jl @@ -81,7 +81,7 @@ Compute the energy of particle `i` using the provided neighbour list. function compute_energy_particle(system::Atoms, i, neighbour_list::NeighbourList) energy_i = zero(typeof(system.density)) position_i = get_position(system, i) - for j in get_neighbour_indices(system, neighbour_list, i) + for j in neighbour_list(system, i) energy_i += compute_energy_ij(system, i, j, position_i) end return energy_i diff --git a/src/neighbours.jl b/src/neighbours.jl index 7878f50..f8683a5 100644 --- a/src/neighbours.jl +++ b/src/neighbours.jl @@ -42,12 +42,21 @@ function old_new_cell(::Particles, i, ::EmptyList) return 1, 1 end -"""Iterate over all system particles. +"""Calling an EmptyList objects return an object which can be iterated upon. + +This iteration will return the indices of the neighbours (which for this list is all the other particles in the system). """ -function get_neighbour_indices(system::Particles, neighbour_list::EmptyList, ::Int) - return (j for j in 1:length(system)) +function (self::EmptyList)(system::Particles, ::Int) + return EmptyListIterator(length(system)) +end + +struct EmptyListIterator + n_particles::Int end +Base.iterate(self::EmptyListIterator, state = 1) = state > self.n_particles ? nothing : (state, state + 1) + + """Return the scalar cell index of particle `i` stored in `neighbour_list`. """ function get_cell_index(i::Int, neighbour_list::NeighbourList) From 3dca5ba2f785d09414355d02cf717d5d9407b272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Tue, 20 Jan 2026 12:16:30 +0100 Subject: [PATCH 12/17] feat: return iterator when calling celllists --- src/neighbours.jl | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/neighbours.jl b/src/neighbours.jl index f8683a5..f2a20c7 100644 --- a/src/neighbours.jl +++ b/src/neighbours.jl @@ -46,16 +46,10 @@ end This iteration will return the indices of the neighbours (which for this list is all the other particles in the system). """ -function (self::EmptyList)(system::Particles, ::Int) - return EmptyListIterator(length(system)) +function (empty_list::EmptyList)(system::Particles, ::Int) + return (j for j in 1:length(system)) end -struct EmptyListIterator - n_particles::Int -end - -Base.iterate(self::EmptyListIterator, state = 1) = state > self.n_particles ? nothing : (state, state + 1) - """Return the scalar cell index of particle `i` stored in `neighbour_list`. """ @@ -210,17 +204,20 @@ function old_new_cell(system::Particles, i, neighbour_list::CellList) return c, c2 end -"""Iterate over the particles from adjacent cells. +"""Calling an EmptyList objects return an object which can be iterated upon. + +This iteration will return the indices of the neighbours (which for this list is all the other particles in the system). """ -function get_neighbour_indices(system::Particles, neighbour_list::CellList, i::Int) +function (cell_list::CellList)(system::Particles, i::Int) position_i = get_position(system, i) - c = get_cell_index(position_i, neighbour_list) - neighbour_cells = neighbour_list.neighbour_cells[c] + c = get_cell_index(position_i, cell_list) + neighbour_cells = cell_list.neighbour_cells[c] # Scan the neighbourhood of cell mc (including itself) # and from there scan atoms in cell c2 - (j for c2 in neighbour_cells for j in @inbounds neighbour_list.cells[c2]) + return (j for c2 in neighbour_cells for j in @inbounds cell_list.cells[c2]) end + """Linked-list neighbour list implementation. Uses arrays `head` and `list` to store per-cell linked lists of particle indices. From a933c2605c83017442db7949c18d47da0ec080ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Tue, 20 Jan 2026 12:20:05 +0100 Subject: [PATCH 13/17] feat: make linked lists callable --- src/neighbours.jl | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/neighbours.jl b/src/neighbours.jl index f2a20c7..b4a31df 100644 --- a/src/neighbours.jl +++ b/src/neighbours.jl @@ -204,9 +204,9 @@ function old_new_cell(system::Particles, i, neighbour_list::CellList) return c, c2 end -"""Calling an EmptyList objects return an object which can be iterated upon. +"""Calling a CellList objects return an object which can be iterated upon. -This iteration will return the indices of the neighbours (which for this list is all the other particles in the system). +This iteration will return the indices of the neighbours of particle i. """ function (cell_list::CellList)(system::Particles, i::Int) position_i = get_position(system, i) @@ -352,15 +352,14 @@ function Base.iterate(neighbour_list::LinkedIterator, state=-1) return j, state end +"""Calling a LinkedList objects return an object which can be iterated upon. -"""Iterate over the particles from adjacent cells. +This iteration will return the indices of the neighbours of particle i. """ -function get_neighbour_indices(system::Particles, neighbour_list::LinkedList, i::Int) +function (linked_list::LinkedList)(system::Particles, i::Int) position_i = get_position(system, i) - c = get_cell_index(position_i, neighbour_list) - neighbour_cells = neighbour_list.neighbour_cells[c] - - iterator = LinkedIterator(neighbour_cells, neighbour_list.head, neighbour_list.list) - return (j for j in iterator) + c = get_cell_index(position_i, linked_list) + neighbour_cells = linked_list.neighbour_cells[c] + return LinkedIterator(neighbour_cells, linked_list.head, linked_list.list) end From 14ef786eaf54966930fe49d0afe7c3e2f453c425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Tue, 20 Jan 2026 12:21:01 +0100 Subject: [PATCH 14/17] feat: propagate changes to molecules --- src/molecules.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/molecules.jl b/src/molecules.jl index 6f0d025..f174424 100644 --- a/src/molecules.jl +++ b/src/molecules.jl @@ -208,7 +208,7 @@ function compute_energy_particle(system::Molecules, i, neighbour_list::Neighbour bonds_i = system.bonds[i] energy_i = compute_energy_bonded_i(system, i, position_i, bonds_i) - for j in get_neighbour_indices(system, neighbour_list, i) + for j in neighbour_list(system, i) energy_i += check_nonbonded_compute_energy_ij(system, i, j, position_i, bonds_i) end return energy_i From 29edf39905fb7421b6544086e6895858cbfe81fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Tue, 20 Jan 2026 13:51:33 +0100 Subject: [PATCH 15/17] perf: add inbounds keywords --- src/neighbours.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/neighbours.jl b/src/neighbours.jl index b4a31df..9d70af4 100644 --- a/src/neighbours.jl +++ b/src/neighbours.jl @@ -214,7 +214,7 @@ function (cell_list::CellList)(system::Particles, i::Int) neighbour_cells = cell_list.neighbour_cells[c] # Scan the neighbourhood of cell mc (including itself) # and from there scan atoms in cell c2 - return (j for c2 in neighbour_cells for j in @inbounds cell_list.cells[c2]) + return (j for c2 in @inbounds neighbour_cells for j in @inbounds cell_list.cells[c2]) end @@ -331,17 +331,17 @@ function Base.iterate(neighbour_list::LinkedIterator, state=-1) return nothing end c, c_state = next - j = neighbour_list.head[c] + @inbounds j = neighbour_list.head[c] else c_state, j = state - j = neighbour_list.list[j] + @inbounds j = neighbour_list.list[j] if j == -1 next = iterate(neighbour_list.neighbour_cells, c_state) if next == nothing return nothing end c, c_state = next - j = neighbour_list.head[c] + @inbounds j = neighbour_list.head[c] end end From a8277fbe41e9b1e5935b859e795b27884e552bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Tue, 20 Jan 2026 16:39:18 +0100 Subject: [PATCH 16/17] fix: properly handle -1 values in heads --- src/neighbours.jl | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/neighbours.jl b/src/neighbours.jl index 9d70af4..a38aba9 100644 --- a/src/neighbours.jl +++ b/src/neighbours.jl @@ -214,7 +214,7 @@ function (cell_list::CellList)(system::Particles, i::Int) neighbour_cells = cell_list.neighbour_cells[c] # Scan the neighbourhood of cell mc (including itself) # and from there scan atoms in cell c2 - return (j for c2 in @inbounds neighbour_cells for j in @inbounds cell_list.cells[c2]) + return (j for c2 in neighbour_cells for j in @inbounds cell_list.cells[c2]) end @@ -326,16 +326,26 @@ end function Base.iterate(neighbour_list::LinkedIterator, state=-1) # First time in if state == -1 - next = iterate(neighbour_list.neighbour_cells) - if next == nothing - return nothing + j = -1 + c_state = nothing + # The while loop is necessary, in case the first head is -1 + while j == -1 + if c_state == nothing + next = iterate(neighbour_list.neighbour_cells) + else + next = iterate(neighbour_list.neighbour_cells, c_state) + end + if next == nothing + return nothing + end + c, c_state = next + @inbounds j = neighbour_list.head[c] end - c, c_state = next - @inbounds j = neighbour_list.head[c] else c_state, j = state @inbounds j = neighbour_list.list[j] - if j == -1 + # The while loop is necessary, in case a head is -1 + while j == -1 next = iterate(neighbour_list.neighbour_cells, c_state) if next == nothing return nothing From be8c12115b28527fe391d881f5f497d3fed3199a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Tue, 20 Jan 2026 16:44:13 +0100 Subject: [PATCH 17/17] perf: remove a control flow --- src/neighbours.jl | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/neighbours.jl b/src/neighbours.jl index a38aba9..0d8e498 100644 --- a/src/neighbours.jl +++ b/src/neighbours.jl @@ -327,14 +327,10 @@ function Base.iterate(neighbour_list::LinkedIterator, state=-1) # First time in if state == -1 j = -1 - c_state = nothing + c_state = 1 # The while loop is necessary, in case the first head is -1 while j == -1 - if c_state == nothing - next = iterate(neighbour_list.neighbour_cells) - else - next = iterate(neighbour_list.neighbour_cells, c_state) - end + next = iterate(neighbour_list.neighbour_cells, c_state) if next == nothing return nothing end