ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
custom_verlet_list.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2025-2026 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include <config/config.hpp>
23
24#include <Cabana_VerletList.hpp>
25
26#include <algorithm>
27#include <cassert>
28#include <cstddef>
29
30template <class MemorySpace, class ListAlgorithm, class Layout, class BuildTag>
32namespace Cabana {
33template <class MemorySpace, class AlgorithmTag, class BuildTag>
34class NeighborList<
35 CustomVerletList<MemorySpace, AlgorithmTag, VerletLayout2D, BuildTag>>;
36}
37
38// ONLY FOR 2D LAYOUT, OTHERWISE NEIGHBOR LIST INTERFACE IMPLEMENTATION WILL
39// CAUSE PROBLEMS (NOT IMPLEMENTED)
40template <class MemorySpace, class AlgorithmTag, class LayoutTag,
41 class BuildTag = Cabana::TeamVectorOpTag>
42class CustomVerletList : public Cabana::VerletList<MemorySpace, AlgorithmTag,
43 LayoutTag, BuildTag> {
44public:
45 CustomVerletList() = default;
46 CustomVerletList(std::size_t const begin, std::size_t const end,
47 std::size_t const max_neigh) {
48 initializeData(end - begin, max_neigh);
49 }
50
51 Kokkos::View<int *, MemorySpace> counts;
52 Kokkos::View<int **, Kokkos::LayoutRight, MemorySpace> neighbors;
53
54 // Note: writing to 'overflow' from multiple threads by 'setOverflow()'
55 // without synchronization can lead to a data race (unspecified behavior).
56 // Since the same value is written from multiple threads concurrently,
57 // this should not affect program behavior.
58 // https://www.openmp.org/spec-html/5.0/openmpsu9.html
59 bool overflow = false;
60
61 // Method to initialize _data without filling neighbors
62 KOKKOS_INLINE_FUNCTION
63 void initializeData(std::size_t const num_particles,
64 std::size_t const max_neigh) {
65 counts = Kokkos::View<int *, MemorySpace>("num_neighbors", num_particles);
66 neighbors = Kokkos::View<int **, Kokkos::LayoutRight, MemorySpace>(
67 Kokkos::ViewAllocateWithoutInitializing("neighbors"), num_particles,
68 max_neigh);
69 }
70
71 // Method to realloc _data
72 KOKKOS_INLINE_FUNCTION
73 void reallocData(std::size_t const num_particles,
74 std::size_t const max_neigh) {
75 Kokkos::realloc(counts, num_particles);
76 Kokkos::realloc(Kokkos::WithoutInitializing, neighbors, num_particles,
77 max_neigh);
78 }
79
80 // Method to add a neighbor
81 KOKKOS_INLINE_FUNCTION
82 void addNeighborAtomicLB(int pid, int nid) {
83 auto count = counts(pid);
84 auto count_n = counts(nid);
85
86 if (count > count_n) {
87 std::swap(pid, nid);
88 }
89 count = Kokkos::atomic_fetch_add(&counts(pid), 1);
90 auto overflow = count >= neighbors.extent(1);
91 if (overflow) {
92 setOverflow();
93 } else {
94 neighbors(pid, count) = nid;
95 }
96 }
97
98 // Thread-safe but non-atomic method to add a neighbor
99 KOKKOS_INLINE_FUNCTION
100 void addNeighbor(int pid, int nid) {
101 auto const count = counts(pid);
102
103 auto overflow = count >= neighbors.extent(1);
104 if (overflow) {
105 setOverflow();
106 } else {
107 neighbors(pid, count) = nid;
108 counts(pid) += 1;
109 }
110 }
111
112 // Non-atomic and load-balanced method to add a neighbor
113 KOKKOS_INLINE_FUNCTION
114 void addNeighborLB(int pid, int nid) {
115 auto count = counts(pid);
116 auto count_n = counts(nid);
117
118 if (count > count_n) {
119 std::swap(pid, nid);
120 count = counts(pid);
121 }
122 auto overflow = count >= neighbors.extent(1);
123 if (overflow) {
124 setOverflow();
125 } else {
126 neighbors(pid, count) = nid;
127 counts(pid) += 1;
128 }
129 }
130
131 // Sorting a neighbor
132 KOKKOS_INLINE_FUNCTION
134 Kokkos::parallel_for("custom_verlet_list::sort_neighbors",
135 Kokkos::RangePolicy<Kokkos::DefaultExecutionSpace>(
136 std::size_t{0}, counts.size()),
137 [&](std::size_t const i) {
138 auto const count = counts(i);
139 auto *ptr = &neighbors(i, 0);
140 std::sort(ptr, ptr + count);
141 });
142 Kokkos::fence();
143 }
144
145 // Find max counts
146 KOKKOS_INLINE_FUNCTION
147 auto get_variance_max_counts(auto &ostream) {
148 auto max_counts = 0l;
149 auto ave_counts = 0l;
150 auto ave_sq_counts = 0l;
151 for (int pid = 0; pid < counts.extent(0); ++pid) {
152 auto const count = static_cast<long>(counts(pid));
153 if (max_counts < count)
154 max_counts = count;
155 ave_counts += count;
156 ave_sq_counts += count * count;
157 }
158 if (counts.extent(0) != 0) {
159 ave_counts /= static_cast<long>(counts.extent(0));
160 ave_sq_counts /= static_cast<long>(counts.extent(0));
161 ave_sq_counts -= ave_counts * ave_counts;
162 }
163 ostream << "max:" << max_counts << " ave:" << ave_counts
164 << " var:" << ave_sq_counts << std::endl;
165 return static_cast<int>(max_counts);
166 }
167
168 KOKKOS_INLINE_FUNCTION
170 int max_counts;
171 Kokkos::Max<int> max_reduce(max_counts);
172 Kokkos::parallel_reduce(
173 "custom_verlet_list::reduce_max",
174 Kokkos::RangePolicy<Kokkos::DefaultExecutionSpace>(std::size_t{0},
175 counts.size()),
176 [&](std::size_t const i, int &value) {
177 if (counts(i) > value)
178 value = counts(i);
179 },
180 max_reduce);
181 Kokkos::fence();
182 return max_counts;
183 }
184
185 KOKKOS_INLINE_FUNCTION bool hasOverflow() const { return overflow; }
186
187private:
188 KOKKOS_INLINE_FUNCTION void setOverflow() { overflow = true; }
189};
190
191template <class MemorySpace, class AlgorithmTag, class BuildTag>
192class Cabana::NeighborList<CustomVerletList<MemorySpace, AlgorithmTag,
193 Cabana::VerletLayout2D, BuildTag>> {
194public:
195 //! Kokkos memory space.
196 using memory_space = MemorySpace;
197 //! Neighbor list type.
198 using list_type = CustomVerletList<MemorySpace, AlgorithmTag,
199 Cabana::VerletLayout2D, BuildTag>;
200
201 //! Get the total number of neighbors across all particles.
202 KOKKOS_INLINE_FUNCTION
203 static std::size_t totalNeighbor(list_type const &list) {
204 std::size_t const num_p = list.counts.size();
205 std::size_t total_n = 0;
206 for (std::size_t i = 0; i < num_p; ++i)
207 total_n += list.counts(i);
208 return total_n;
209 }
210
211 //! Get the maximum number of neighbors per particle.
212 KOKKOS_INLINE_FUNCTION
213 static std::size_t maxNeighbor(list_type const &list) {
214 return list.neighbors.extent(1);
215 }
216
217 //! Get the number of neighbors for a given particle index.
218 KOKKOS_INLINE_FUNCTION
219 static std::size_t numNeighbor(list_type const &list,
220 std::size_t const particle_index) {
221 return list.counts(particle_index);
222 }
223
224 //! Get the id for a neighbor for a given particle index and the index of
225 //! the neighbor relative to the particle.
226 KOKKOS_INLINE_FUNCTION
227 static std::size_t getNeighbor(list_type const &list,
228 std::size_t const particle_index,
229 std::size_t const count) {
230 return list.neighbors(particle_index, count);
231 }
232};
static KOKKOS_INLINE_FUNCTION std::size_t getNeighbor(list_type const &list, std::size_t const particle_index, std::size_t const count)
Get the id for a neighbor for a given particle index and the index of the neighbor relative to the pa...
static KOKKOS_INLINE_FUNCTION std::size_t totalNeighbor(list_type const &list)
Get the total number of neighbors across all particles.
static KOKKOS_INLINE_FUNCTION std::size_t maxNeighbor(list_type const &list)
Get the maximum number of neighbors per particle.
static KOKKOS_INLINE_FUNCTION std::size_t numNeighbor(list_type const &list, std::size_t const particle_index)
Get the number of neighbors for a given particle index.
KOKKOS_INLINE_FUNCTION void addNeighborLB(int pid, int nid)
KOKKOS_INLINE_FUNCTION void sortNeighbors()
KOKKOS_INLINE_FUNCTION auto get_max_counts()
CustomVerletList(std::size_t const begin, std::size_t const end, std::size_t const max_neigh)
KOKKOS_INLINE_FUNCTION void addNeighborAtomicLB(int pid, int nid)
KOKKOS_INLINE_FUNCTION void initializeData(std::size_t const num_particles, std::size_t const max_neigh)
KOKKOS_INLINE_FUNCTION void reallocData(std::size_t const num_particles, std::size_t const max_neigh)
KOKKOS_INLINE_FUNCTION void addNeighbor(int pid, int nid)
CustomVerletList()=default
Kokkos::View< int **, Kokkos::LayoutRight, MemorySpace > neighbors
KOKKOS_INLINE_FUNCTION auto get_variance_max_counts(auto &ostream)
Kokkos::View< int *, MemorySpace > counts
KOKKOS_INLINE_FUNCTION bool hasOverflow() const