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