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
30// ONLY FOR 2D LAYOUT, OTHERWISE NEIGHBOR LIST INTERFACE IMPLEMENTATION WILL
31// CAUSE PROBLEMS (NOT IMPLEMENTED)
32template <class MemorySpace, class AlgorithmTag, class LayoutTag,
33 class BuildTag = Cabana::TeamVectorOpTag>
34class CustomVerletList : public Cabana::VerletList<MemorySpace, AlgorithmTag,
35 LayoutTag, BuildTag> {
36public:
37 CustomVerletList() = default;
38 CustomVerletList(std::size_t const begin, std::size_t const end,
39 std::size_t const max_neigh) {
40 initializeData(end - begin, max_neigh);
41 }
42
45
46 // Note: writing to 'overflow' from multiple threads by 'setOverflow()'
47 // without synchronization can lead to a data race (unspecified behavior).
48 // Since the same value is written from multiple threads concurrently,
49 // this should not affect program behavior.
50 // https://www.openmp.org/spec-html/5.0/openmpsu9.html
51 bool overflow = false;
52
53 // Method to initialize _data without filling neighbors
54 KOKKOS_INLINE_FUNCTION
55 void initializeData(std::size_t const num_particles,
56 std::size_t const max_neigh) {
57 counts = Kokkos::View<int *, MemorySpace>("num_neighbors", num_particles);
59 Kokkos::ViewAllocateWithoutInitializing("neighbors"), num_particles,
60 max_neigh);
61 }
62
63 // Method to realloc _data
64 KOKKOS_INLINE_FUNCTION
65 void reallocData(std::size_t const num_particles,
66 std::size_t const max_neigh) {
67 Kokkos::realloc(counts, num_particles);
68 Kokkos::realloc(Kokkos::WithoutInitializing, neighbors, num_particles,
69 max_neigh);
70 }
71
72 // Method to add a neighbor
73 KOKKOS_INLINE_FUNCTION
74 void addNeighborAtomicLB(int pid, int nid) {
75 auto count = counts(pid);
76 auto count_n = counts(nid);
77
78 if (count > count_n) {
79 std::swap(pid, nid);
80 }
81 count = Kokkos::atomic_fetch_add(&counts(pid), 1);
82 auto overflow = count >= neighbors.extent(1);
83 if (overflow) {
84 setOverflow();
85 } else {
86 neighbors(pid, count) = nid;
87 }
88 }
89
90 // Thread-safe but non-atomic method to add a neighbor
91 KOKKOS_INLINE_FUNCTION
92 void addNeighbor(int pid, int nid) {
93 auto const count = counts(pid);
94
95 auto overflow = count >= neighbors.extent(1);
96 if (overflow) {
97 setOverflow();
98 } else {
99 neighbors(pid, count) = nid;
100 counts(pid) += 1;
101 }
102 }
103
104 // Non-atomic and load-balanced method to add a neighbor
105 KOKKOS_INLINE_FUNCTION
106 void addNeighborLB(int pid, int nid) {
107 auto count = counts(pid);
108 auto count_n = counts(nid);
109
110 if (count > count_n) {
111 std::swap(pid, nid);
112 count = counts(pid);
113 }
114 auto overflow = count >= neighbors.extent(1);
115 if (overflow) {
116 setOverflow();
117 } else {
118 neighbors(pid, count) = nid;
119 counts(pid) += 1;
120 }
121 }
122
123 // Sorting a neighbor
124 KOKKOS_INLINE_FUNCTION
126 Kokkos::parallel_for("custom_verlet_list::sort_neighbors",
127 Kokkos::RangePolicy<Kokkos::DefaultExecutionSpace>(
128 std::size_t{0}, counts.size()),
129 [&](std::size_t const i) {
130 auto const count = counts(i);
131 auto *ptr = &neighbors(i, 0);
132 std::sort(ptr, ptr + count);
133 });
134 Kokkos::fence();
135 }
136
137 // Find max counts
138 KOKKOS_INLINE_FUNCTION
139 auto get_variance_max_counts(auto &ostream) {
140 auto max_counts = 0l;
141 auto ave_counts = 0l;
142 auto ave_sq_counts = 0l;
143 for (int pid = 0; pid < counts.extent(0); ++pid) {
144 auto const count = static_cast<long>(counts(pid));
145 if (max_counts < count)
146 max_counts = count;
147 ave_counts += count;
148 ave_sq_counts += count * count;
149 }
150 if (counts.extent(0) != 0) {
151 ave_counts /= static_cast<long>(counts.extent(0));
152 ave_sq_counts /= static_cast<long>(counts.extent(0));
153 ave_sq_counts -= ave_counts * ave_counts;
154 }
155 ostream << "max:" << max_counts << " ave:" << ave_counts
156 << " var:" << ave_sq_counts << std::endl;
157 return static_cast<int>(max_counts);
158 }
159
160 KOKKOS_INLINE_FUNCTION
162 int max_counts;
163 Kokkos::Max<int> max_reduce(max_counts);
164 Kokkos::parallel_reduce(
165 "custom_verlet_list::reduce_max",
166 Kokkos::RangePolicy<Kokkos::DefaultExecutionSpace>(std::size_t{0},
167 counts.size()),
168 [&](std::size_t const i, int &value) {
169 if (counts(i) > value)
170 value = counts(i);
171 },
172 max_reduce);
173 Kokkos::fence();
174 return max_counts;
175 }
176
177 KOKKOS_INLINE_FUNCTION bool hasOverflow() const { return overflow; }
178
179private:
180 KOKKOS_INLINE_FUNCTION void setOverflow() { overflow = true; }
181};
182
183template <class MemorySpace, class AlgorithmTag, class BuildTag>
184class Cabana::NeighborList<CustomVerletList<MemorySpace, AlgorithmTag,
185 Cabana::VerletLayout2D, BuildTag>> {
186public:
187 //! Kokkos memory space.
188 using memory_space = MemorySpace;
189 //! Neighbor list type.
190 using list_type = CustomVerletList<MemorySpace, AlgorithmTag,
191 Cabana::VerletLayout2D, BuildTag>;
192
193 //! Get the total number of neighbors across all particles.
194 KOKKOS_INLINE_FUNCTION
195 static std::size_t totalNeighbor(list_type const &list) {
196 std::size_t const num_p = list.counts.size();
197 std::size_t total_n = 0;
198 for (std::size_t i = 0; i < num_p; ++i)
199 total_n += list.counts(i);
200 return total_n;
201 }
202
203 //! Get the maximum number of neighbors per particle.
204 KOKKOS_INLINE_FUNCTION
205 static std::size_t maxNeighbor(list_type const &list) {
206 return list.neighbors.extent(1);
207 }
208
209 //! Get the number of neighbors for a given particle index.
210 KOKKOS_INLINE_FUNCTION
211 static std::size_t numNeighbor(list_type const &list,
212 std::size_t const particle_index) {
213 return list.counts(particle_index);
214 }
215
216 //! Get the id for a neighbor for a given particle index and the index of
217 //! the neighbor relative to the particle.
218 KOKKOS_INLINE_FUNCTION
219 static std::size_t getNeighbor(list_type const &list,
220 std::size_t const particle_index,
221 std::size_t const count) {
222 return list.neighbors(particle_index, count);
223 }
224};
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