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<
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 inline void initializeData(std::size_t const num_particles,
63 std::size_t const max_neigh) {
64 counts = Kokkos::View<int *, MemorySpace>("num_neighbors", num_particles);
65 neighbors = Kokkos::View<int **, Kokkos::LayoutRight, MemorySpace>(
66 Kokkos::view_alloc(MemorySpace{}, Kokkos::WithoutInitializing,
67 "neighbors"),
69 }
70
71 // Method to realloc _data
72 inline void reallocData(std::size_t const num_particles,
73 std::size_t const max_neigh) {
74 Kokkos::realloc(counts, num_particles);
75 Kokkos::realloc(Kokkos::WithoutInitializing, neighbors, num_particles,
76 max_neigh);
77 }
78
79 // Method to add a neighbor
81 void addNeighborAtomicLB(int pid, int nid) {
82 auto count = counts(pid);
83 auto count_n = counts(nid);
84
85 if (count > count_n) {
86 std::swap(pid, nid);
87 }
88 count = Kokkos::atomic_fetch_add(&counts(pid), 1);
89 auto overflow = count >= neighbors.extent(1);
90 if (overflow) {
91 setOverflow();
92 } else {
93 neighbors(pid, count) = nid;
94 }
95 }
96
97 // Thread-safe but non-atomic method to add a neighbor
99 void addNeighbor(int pid, int nid) {
100 auto const count = counts(pid);
101
102 auto overflow = count >= neighbors.extent(1);
103 if (overflow) {
104 setOverflow();
105 } else {
106 neighbors(pid, count) = nid;
107 counts(pid) += 1;
108 }
109 }
110
111 // Non-atomic and load-balanced method to add a neighbor
113 void addNeighborLB(int pid, int nid) {
114 auto count = counts(pid);
115 auto count_n = counts(nid);
116
117 if (count > count_n) {
118 std::swap(pid, nid);
119 count = counts(pid);
120 }
121 auto overflow = count >= neighbors.extent(1);
122 if (overflow) {
123 setOverflow();
124 } else {
125 neighbors(pid, count) = nid;
126 counts(pid) += 1;
127 }
128 }
129
130 // Sorting a neighbor
133 Kokkos::parallel_for("custom_verlet_list::sort_neighbors",
134 Kokkos::RangePolicy<Kokkos::DefaultHostExecutionSpace>(
135 std::size_t{0}, counts.size()),
136 [&](std::size_t const i) {
137 auto const count = counts(i);
138 auto *ptr = &neighbors(i, 0);
139 std::sort(ptr, ptr + count);
140 });
141 Kokkos::fence();
142 }
143
144 // Find max counts
146 auto get_variance_max_counts(auto &ostream) {
147 auto max_counts = 0l;
148 auto ave_counts = 0l;
149 auto ave_sq_counts = 0l;
150 for (int pid = 0; pid < counts.extent(0); ++pid) {
151 auto const count = static_cast<long>(counts(pid));
152 if (max_counts < count)
153 max_counts = count;
154 ave_counts += count;
155 ave_sq_counts += count * count;
156 }
157 if (counts.extent(0) != 0) {
158 ave_counts /= static_cast<long>(counts.extent(0));
159 ave_sq_counts /= static_cast<long>(counts.extent(0));
161 }
162 ostream << "max:" << max_counts << " ave:" << ave_counts
163 << " var:" << ave_sq_counts << std::endl;
164 return static_cast<int>(max_counts);
165 }
166
169 int max_counts;
170 Kokkos::Max<int> max_reduce(max_counts);
171 Kokkos::parallel_reduce(
172 "custom_verlet_list::reduce_max",
173 Kokkos::RangePolicy<Kokkos::DefaultHostExecutionSpace>(std::size_t{0},
174 counts.size()),
175 [&](std::size_t const i, int &value) {
176 if (counts(i) > value)
177 value = counts(i);
178 },
179 max_reduce);
180 Kokkos::fence();
181 return max_counts;
182 }
183
185
186private:
187 KOKKOS_INLINE_FUNCTION void setOverflow() { overflow = true; }
188};
189
190template <class MemorySpace, class AlgorithmTag, class BuildTag>
191class Cabana::NeighborList<CustomVerletList<MemorySpace, AlgorithmTag,
193public:
194 //! Kokkos memory space.
196 //! Neighbor list type.
198 Cabana::VerletLayout2D, BuildTag>;
199
200 //! Get the total number of neighbors across all particles.
202 static std::size_t totalNeighbor(list_type const &list) {
203 std::size_t const num_p = list.counts.size();
204 std::size_t total_n = 0;
205 for (std::size_t i = 0; i < num_p; ++i)
206 total_n += list.counts(i);
207 return total_n;
208 }
209
210 //! Get the maximum number of neighbors per particle.
212 static std::size_t maxNeighbor(list_type const &list) {
213 return list.neighbors.extent(1);
214 }
215
216 //! Get the number of neighbors for a given particle index.
218 static std::size_t numNeighbor(list_type const &list,
219 std::size_t const particle_index) {
220 return list.counts(particle_index);
221 }
222
223 //! Get the id for a neighbor for a given particle index and the index of
224 //! the neighbor relative to the particle.
226 static std::size_t getNeighbor(list_type const &list,
227 std::size_t const particle_index,
228 std::size_t const count) {
229 return list.neighbors(particle_index, count);
230 }
231};
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)
void initializeData(std::size_t const num_particles, std::size_t const max_neigh)
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)
void reallocData(std::size_t const num_particles, std::size_t const max_neigh)
KOKKOS_INLINE_FUNCTION void addNeighborAtomicLB(int pid, int nid)
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
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.