IPPL API Reference
Independent Parallel Particle Layer C++ API
Loading...
Searching...
No Matches
ParticleBase.hpp
1//
2// Class ParticleBase
3// Base class for all user-defined particle classes.
4//
5// ParticleBase is a container and manager for a set of particles.
6// The user must define a class derived from ParticleBase which describes
7// what specific data attributes the particle has (e.g., mass or charge).
8// Each attribute is an instance of a ParticleAttribute<T> class; ParticleBase
9// keeps a list of pointers to these attributes, and performs particle creation
10// and destruction.
11//
12// ParticleBase is templated on the ParticleLayout mechanism for the particles.
13// This template parameter should be a class derived from ParticleLayout.
14// ParticleLayout-derived classes maintain the info on which particles are
15// located on which processor, and performs the specific communication
16// required between processors for the particles. The ParticleLayout is
17// templated on the type and dimension of the atom position attribute, and
18// ParticleBase uses the same types for these items as the given
19// ParticleLayout.
20//
21// ParticleBase and all derived classes have the following common
22// characteristics:
23// - The spatial positions of the N particles are stored in the
24// particle_position_type variable R
25// - The global index of the N particles are stored in the
26// particle_index_type variable ID
27// - A pointer to an allocated layout class. When you construct a
28// ParticleBase, you must provide a layout instance, and ParticleBase
29// will delete this instance when it (the ParticleBase) is deleted.
30//
31// To use this class, the user defines a derived class with the same
32// structure as in this example:
33//
34// class UserParticles :
35// public ParticleBase< ParticleSpatialLayout<double,3> > {
36// public:
37// // attributes for this class
38// ParticleAttribute<double> rad; // radius
39// particle_position_type vel; // velocity, same storage type as R
40//
41// // constructor: add attributes to base class
42// UserParticles(ParticleSpatialLayout<double,2>* L) : ParticleBase(L) {
43// addAttribute(rad);
44// addAttribute(vel);
45// }
46// };
47//
48// This example defines a user class with 3D position and two extra
49// attributes: a radius rad (double), and a velocity vel (a 3D Vector).
50//
51
52namespace ippl {
53
54 template <class PLayout, typename... IP>
56 : layout_m(nullptr)
57 , localNum_m(0)
58 , totalNum_m(0)
59 , nextID_m(Comm->rank())
60 , numNodes_m(Comm->size()) {
61 if constexpr (EnableIDs) {
63 }
65 ID.set_name("ID");
66 R.set_name("position");
67 }
68
69 template <class PLayout, typename... IP>
71 : ParticleBase() {
72 initialize(layout);
73 }
74
75 template <class PLayout, typename... IP>
76 template <typename MemorySpace>
78 attributes_m.template get<MemorySpace>().push_back(&pa);
79 pa.setParticleCount(localNum_m);
80 }
81
82 template <class PLayout, typename... IP>
84 // PAssert(layout_m == nullptr);
85
86 // save the layout, and perform setup tasks
87 layout_m = &layout;
88 }
89
90 template <class PLayout, typename... IP>
91 void ParticleBase<PLayout, IP...>::create(size_type nLocal, bool non_destructive) {
92 PAssert(layout_m != nullptr);
93
94 if (nLocal > 0) {
95 forAllAttributes([&]<typename Attribute>(Attribute& attribute) {
96 attribute->create(nLocal, non_destructive);
97 });
98
99 if constexpr (EnableIDs) {
100 // Set the unique ID value for these new particles. The new entries
101 // live at indices [localNum_m, localNum_m + nLocal); the k-th new
102 // particle on this rank gets ID nextID + numNodes * k.
103 using policy_type =
104 Kokkos::RangePolicy<size_type, typename particle_index_type::execution_space>;
105 auto pIDs = ID.getView();
106 auto nextID = this->nextID_m;
107 auto numNodes = this->numNodes_m;
108 auto offset = localNum_m;
109 Kokkos::parallel_for(
110 "ParticleBase<...>::create(size_t,bool)",
111 policy_type(localNum_m, localNum_m + nLocal),
112 KOKKOS_LAMBDA(const std::int64_t i) {
113 pIDs(i) = nextID + numNodes * (i - offset);
114 });
115 nextID_m += numNodes_m * nLocal;
116 }
117
118 // remember that we're creating these new particles
119 localNum_m += nLocal;
120 }
121
122 Comm->allreduce(localNum_m, totalNum_m, 1, std::plus<size_type>());
123 }
124
125 template <class PLayout, typename... IP>
126 void ParticleBase<PLayout, IP...>::alloc(size_type nLocal) {
127 PAssert(layout_m != nullptr);
128 forAllAttributes([&]<typename Attribute>(Attribute& attribute) {
129 attribute->alloc(nLocal);
130 });
131 // Intentionally does NOT touch localNum_m, totalNum_m, nextID_m.
132 }
133
134 template <class PLayout, typename... IP>
136 PAssert(layout_m != nullptr);
137
138 size_type n = (id > -1) ? 1 : 0;
139
140 // temporary change
141 index_type tmpNextID = nextID_m;
142 nextID_m = id;
143 numNodes_m = 0;
144
145 create(n);
146
147 nextID_m = tmpNextID;
148 numNodes_m = Comm->size();
149 }
150
151 template <class PLayout, typename... IP>
153 PAssert(layout_m != nullptr);
154
155 // Compute the number of particles local to each processor
156 size_type nLocal = nTotal / numNodes_m;
157
158 const size_t rank = Comm->rank();
159
160 size_type rest = nTotal - nLocal * rank;
161 if (rank < rest) {
162 ++nLocal;
163 }
164
165 create(nLocal);
166 }
167
168 template <class PLayout, typename... IP>
169 template <typename... Properties>
170 void ParticleBase<PLayout, IP...>::destroy(const Kokkos::View<bool*, Properties...>& invalid,
171 const size_type destroyNum) {
172 this->internalDestroy(invalid, destroyNum);
173
174 Comm->allreduce(localNum_m, totalNum_m, 1, std::plus<size_type>());
175 }
176
177 template <class PLayout, typename... IP>
178 template <typename... Properties>
180 const Kokkos::View<bool*, Properties...>& invalid, const size_type destroyNum) {
181 PAssert(destroyNum <= localNum_m);
182
183 // If there aren't any particles to delete, do nothing
184 if (destroyNum == 0) {
185 return;
186 }
187
188 // If we're deleting all the particles, there's no point in doing
189 // anything because the valid region will be empty; we only need to
190 // update the particle count
191 if (destroyNum == localNum_m) {
192 localNum_m = 0;
193 return;
194 }
195
196 using view_type = Kokkos::View<bool*, Properties...>;
197 using memory_space = typename view_type::memory_space;
198 using execution_space = typename view_type::execution_space;
199 using policy_type = Kokkos::RangePolicy<execution_space>;
200 auto& locDeleteIndex = deleteIndex_m.get<memory_space>();
201 auto& locKeepIndex = keepIndex_m.get<memory_space>();
202
203 // Resize buffers, if necessary
204 detail::runForAllSpaces([&]<typename MemorySpace>() {
205 if (attributes_m.template get<memory_space>().size() > 0) {
206 int overalloc = Comm->getDefaultOverallocation();
207 auto& del = deleteIndex_m.get<memory_space>();
208 auto& keep = keepIndex_m.get<memory_space>();
209 if (del.size() < destroyNum) {
210 Kokkos::realloc(del, destroyNum * overalloc);
211 Kokkos::realloc(keep, destroyNum * overalloc);
212 }
213 }
214 });
215
216 // Reset index buffer
217 Kokkos::deep_copy(locDeleteIndex, -1);
218
219 // Find the indices of the invalid particles in the valid region
220 Kokkos::parallel_scan(
221 "Scan in ParticleBase::destroy()", policy_type(0, localNum_m - destroyNum),
222 KOKKOS_LAMBDA(const size_t i, int& idx, const bool final) {
223 if (final && invalid(i)) {
224 locDeleteIndex(idx) = i;
225 }
226 if (invalid(i)) {
227 idx += 1;
228 }
229 });
230 Kokkos::fence();
231
232 // Determine the total number of invalid particles in the valid region
233 size_type maxDeleteIndex = 0;
234 Kokkos::parallel_reduce(
235 "Reduce in ParticleBase::destroy()", policy_type(0, destroyNum),
236 KOKKOS_LAMBDA(const size_t i, size_t& maxIdx) {
237 if (locDeleteIndex(i) >= 0 && i > maxIdx) {
238 maxIdx = i;
239 }
240 },
241 Kokkos::Max<size_type>(maxDeleteIndex));
242
243 // Find the indices of the valid particles in the invalid region
244 Kokkos::parallel_scan(
245 "Second scan in ParticleBase::destroy()",
246 Kokkos::RangePolicy<size_type, execution_space>(localNum_m - destroyNum, localNum_m),
247 KOKKOS_LAMBDA(const size_t i, int& idx, const bool final) {
248 if (final && !invalid(i)) {
249 locKeepIndex(idx) = i;
250 }
251 if (!invalid(i)) {
252 idx += 1;
253 }
254 });
255
256 Kokkos::fence();
257
258 localNum_m -= destroyNum;
259
260 // We need to delete particles in all memory spaces. If there are any attributes not stored
261 // in the memory space we've already been using, we need to copy the index views to the
262 // other spaces.
263 auto filter = [&]<typename MemorySpace>() {
264 return attributes_m.template get<MemorySpace>().size() > 0;
265 };
266 deleteIndex_m.copyToOtherSpaces<memory_space>(filter);
267 keepIndex_m.copyToOtherSpaces<memory_space>(filter);
268
269 // Partition the attributes into valid and invalid regions
270 // NOTE: The vector elements are pointers, but we want to extract
271 // the memory space from the class type, so we explicitly
272 // make the lambda argument a pointer to the template parameter
273 forAllAttributes([&]<typename Attribute>(Attribute*& attribute) {
274 using att_memory_space = typename Attribute::memory_space;
275 auto& del = deleteIndex_m.get<att_memory_space>();
276 auto& keep = keepIndex_m.get<att_memory_space>();
277 attribute->destroy(del, keep, maxDeleteIndex + 1);
278 });
279 }
280
281 template <class PLayout, typename... IP>
282 template <typename HashType>
284 std::vector<MPI_Request>& requests,
285 const HashType& hash) {
286 size_type nSends = hash.size();
287 requests.resize(requests.size() + 1);
288
289 auto hashes = hash_container_type(hash, [&]<typename MemorySpace>() {
290 return attributes_m.template get<MemorySpace>().size() > 0;
291 });
292 pack(hashes);
293 detail::runForAllSpaces([&]<typename MemorySpace>() {
294 size_type bufSize = packedSize<MemorySpace>(nSends);
295 if (bufSize == 0) {
296 return;
297 }
298
299 auto buf = Comm->getBuffer<MemorySpace>(bufSize);
300
301 Comm->isend(rank, tag++, *this, *buf, requests.back(), nSends);
302 buf->resetWritePos();
303 });
304 }
305
306 template <class PLayout, typename... IP>
307 void ParticleBase<PLayout, IP...>::recvFromRank(int rank, int tag, size_type nRecvs) {
308 detail::runForAllSpaces([&]<typename MemorySpace>() {
309 size_type bufSize = packedSize<MemorySpace>(nRecvs);
310 if (bufSize == 0) {
311 return;
312 }
313
314 auto buf = Comm->getBuffer<MemorySpace>(bufSize);
315
316 Comm->recv(rank, tag++, *this, *buf, bufSize, nRecvs);
317 buf->resetReadPos();
318 });
319 unpack(nRecvs);
320 }
321
322 template <class PLayout, typename... IP>
323 template <typename Archive>
324 void ParticleBase<PLayout, IP...>::serialize(Archive& ar, size_type nsends) {
325 using memory_space = typename Archive::buffer_type::memory_space;
326 forAllAttributes<memory_space>([&]<typename Attribute>(Attribute& att) {
327 att->serialize(ar, nsends);
328 });
329 }
330
331 template <class PLayout, typename... IP>
332 template <typename Archive>
333 void ParticleBase<PLayout, IP...>::deserialize(Archive& ar, size_type nrecvs) {
334 using memory_space = typename Archive::buffer_type::memory_space;
335 forAllAttributes<memory_space>([&]<typename Attribute>(Attribute& att) {
336 att->deserialize(ar, nrecvs);
337 });
338 }
339
340 template <class PLayout, typename... IP>
341 template <typename MemorySpace>
342 detail::size_type ParticleBase<PLayout, IP...>::packedSize(const size_type count) const {
343 size_type total = 0;
344 forAllAttributes<MemorySpace>([&]<typename Attribute>(const Attribute& att) {
345 total += att->packedSize(count);
346 });
347 return total;
348 }
349
350 template <class PLayout, typename... IP>
351 void ParticleBase<PLayout, IP...>::pack(const hash_container_type& hash) {
352 detail::runForAllSpaces([&]<typename MemorySpace>() {
353 auto& att = attributes_m.template get<MemorySpace>();
354 for (unsigned j = 0; j < att.size(); j++) {
355 att[j]->pack(hash.template get<MemorySpace>());
356 }
357 });
358 }
359
360 template <class PLayout, typename... IP>
362 detail::runForAllSpaces([&]<typename MemorySpace>() {
363 auto& att = attributes_m.template get<MemorySpace>();
364 for (unsigned j = 0; j < att.size(); j++) {
365 att[j]->unpack(nrecvs);
366 }
367 });
368 localNum_m += nrecvs;
369 }
370} // namespace ippl
Definition ParticleBase.h:87
void addAttribute(detail::ParticleAttribBase< MemorySpace > &pa)
Definition ParticleBase.hpp:77
void globalCreate(size_type nTotal)
Definition ParticleBase.hpp:152
void initialize(Layout_t &layout)
Definition ParticleBase.hpp:83
void create(size_type nLocal, bool non_destructive=false)
Definition ParticleBase.hpp:91
void destroy(const Kokkos::View< bool *, Properties... > &invalid, const size_type destroyNum)
Definition ParticleBase.hpp:170
void alloc(size_type nLocal)
Definition ParticleBase.hpp:126
void createWithID(index_type id)
Definition ParticleBase.hpp:135
ParticleBase()
Definition ParticleBase.hpp:55
particle_position_type R
view of particle positions
Definition ParticleBase.h:115
particle_index_type ID
view of particle IDs
Definition ParticleBase.h:118
Definition ParticleAttribBase.h:30
Definition Archive.h:20