numerics 0.1.0
Loading...
Searching...
No Matches
verlet_list.hpp
Go to the documentation of this file.
1/// @file verlet_list.hpp
2/// @brief Verlet neighbour list with skin-radius temporal caching
3#pragma once
4
5#include "cell_list.hpp"
6#include <cmath>
7#include <utility>
8#include <vector>
9
10namespace num {
11
12template<typename Scalar>
14 public:
15 VerletList2D(Scalar cutoff, Scalar skin)
16 : cutoff_(cutoff),
17 skin_(skin),
18 ext_sq_((cutoff + skin) * (cutoff + skin)) {}
19
20 /// @brief Build the neighbour list using a pre-built CellList2D.
21 template<typename PosAccessor>
22 void build(PosAccessor&& get_pos, int n, const CellList2D<Scalar>& cl) {
23 starts_.resize(n + 1);
24 flat_.clear();
25 ref_x_.resize(n);
26 ref_y_.resize(n);
27
28 starts_[0] = 0;
29 for (int i = 0; i < n; ++i) {
30 auto [xi, yi] = get_pos(i);
31 ref_x_[i] = xi;
32 ref_y_[i] = yi;
33
34 cl.query(xi, yi, [&](int j) {
35 if (j == i)
36 return;
37 auto [xj, yj] = get_pos(j);
38 const Scalar dx = xi - xj, dy = yi - yj;
39 if (dx * dx + dy * dy < ext_sq_)
40 flat_.push_back(j);
41 });
42
43 starts_[i + 1] = static_cast<int>(flat_.size());
44 }
45 }
46
47 /// @brief Return true if a particle moved more than half the skin.
48 template<typename PosAccessor>
49 bool needs_rebuild(PosAccessor&& get_pos, int n) const {
50 if (ref_x_.empty())
51 return true;
52 const Scalar half_skin_sq = (skin_ * Scalar(0.5)) * (skin_ * Scalar(0.5));
53 for (int i = 0; i < n; ++i) {
54 auto [xi, yi] = get_pos(i);
55 const Scalar dx = xi - ref_x_[i];
56 const Scalar dy = yi - ref_y_[i];
57 if (dx * dx + dy * dy > half_skin_sq)
58 return true;
59 }
60 return false;
61 }
62
63 /// @brief Cached neighbors of particle i.
64 IntRange neighbors(int i) const noexcept {
65 return {flat_.data() + starts_[i], flat_.data() + starts_[i + 1]};
66 }
67
68 Scalar cutoff() const noexcept { return cutoff_; }
69 Scalar skin() const noexcept { return skin_; }
70 Scalar ext_cutoff() const noexcept { return cutoff_ + skin_; }
71 int n_particles() const noexcept {
72 return starts_.empty() ? 0 : static_cast<int>(starts_.size()) - 1;
73 }
74
75 private:
76 Scalar cutoff_, skin_, ext_sq_;
77 std::vector<int> flat_;
78 std::vector<int> starts_;
79 std::vector<Scalar> ref_x_;
80 std::vector<Scalar> ref_y_;
81};
82
83} // namespace num
Cache-coherent 2D cell list for O(1) amortized neighbour queries.
void query(Scalar px, Scalar py, F &&f) const
Call f(j) for candidate particles near (px, py).
Definition cell_list.hpp:61
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
Return true if a particle moved more than half the skin.
int n_particles() const noexcept
VerletList2D(Scalar cutoff, Scalar skin)
Scalar cutoff() const noexcept
IntRange neighbors(int i) const noexcept
Cached neighbors of particle i.