48template<
typename Scalar>
67 template<
typename PosAccessor>
69 starts_.resize(n + 1);
75 for (
int i = 0;
i < n; ++
i) {
84 if (dx * dx +
dy *
dy < ext_sq_)
88 starts_[
i + 1] =
static_cast<int>(flat_.size());
98 template<
typename PosAccessor>
100 if (ref_x_.empty())
return true;
102 for (
int i = 0;
i < n; ++
i) {
115 return { flat_.data() + starts_[
i],
116 flat_.data() + starts_[
i + 1] };
123 return starts_.empty() ? 0 :
static_cast<int>(starts_.size()) - 1;
127 Scalar cutoff_, skin_, ext_sq_;
128 std::vector<int> flat_;
129 std::vector<int> starts_;
130 std::vector<Scalar> ref_x_;
131 std::vector<Scalar> ref_y_;
Cache-coherent 2D cell list for O(1) amortized neighbour queries.
Scalar ext_cutoff() const noexcept
void build(PosAccessor &&get_pos, int n, const CellList2D< Scalar > &cl)
Build the neighbour list using a pre-built CellList2D.
Scalar skin() const noexcept
bool needs_rebuild(PosAccessor &&get_pos, int n) const
Check whether any particle has moved far enough to invalidate the cached list. O(n),...
int n_particles() const noexcept
VerletList2D(Scalar cutoff, Scalar skin)
Scalar cutoff() const noexcept
IntRange neighbors(int i) const noexcept
Cached neighbours of particle i (within cutoff + skin).
constexpr T ipow(T x) noexcept
Compute x^N at compile time via repeated squaring.
Lightweight read-only range over a contiguous int array (C++17-safe span).