IPPL API Reference
Independent Parallel Particle Layer C++ API
Loading...
Searching...
No Matches
ParticleSpatialOverlapLayout.hpp
1//
2// Class ParticleSpatialOverlapLayout
3// Particle layout based on spatial decomposition.
4//
5// This is a specialized version of ParticleSpatialLayout, which allows
6// particles to be on multiple processes if they are close to the respective
7// region.
8//
9// After each 'time step' in a calculation, which is defined as a period
10// in which the particle positions may change enough to affect the global
11// layout, the user must call the 'update' routine, which will move
12// particles between processors, etc. After the Nth call to update, a
13// load balancing routine will be called instead. The user may set the
14// frequency of load balancing (N), or may supply a function to
15// determine if load balancing should be done or not.
16//
17#include <Kokkos_MathematicalFunctions.hpp>
18
19#include <numeric>
20#include <vector>
21
22#include "Utility/IpplTimings.h"
23
24#include "../../alpine/ParticleContainer.hpp"
25#include "Communicate/Window.h"
26
27namespace ippl {
28 template <typename T, unsigned Dim, class Mesh, typename... Properties>
30 FieldLayout<Dim>& fl, Mesh& mesh, const T& rcutoff)
31 : Base(fl, mesh)
32 , rcutoff_m(rcutoff)
33 , numLocalParticles_m(0) {
35 }
36
37 template <typename T, unsigned Dim, class Mesh, typename... Properties>
39 FieldLayout<Dim>& fl, Mesh& mesh) {
40 Base::updateLayout(fl, mesh);
41 initializeCells();
42 }
43
44 template <typename T, unsigned Dim, class Mesh, typename... Properties>
46 const auto rank = Comm->rank();
47 const auto hLocalRegions = this->rlayout_m.gethLocalRegions();
48 for (unsigned d = 0; d < Dim; ++d) {
49 PAssert(rcutoff_m <= hLocalRegions(rank)[d].length() / 2 &&
50 "Cutoff is too big with respect to region. "
51 "Particle could be on 3 or more ranks ins one dimension");
52 }
53
54 /* precompute information of cell structure. dividing the region into cells of at least
55 * rcutoff_m length, the length of the overlap. Use std::floor to make sure the boundary
56 * cells are big enough as well.
57 */
58 totalCells_m = 1;
59 numLocalCells_m = 1;
60 for (unsigned d = 0; d < Dim; ++d) {
61 const T length = hLocalRegions(rank)[d].length();
62 const size_type nLocalCells = std::floor(length / rcutoff_m);
63 // two ghost cells, one in each direction
64 numCells_m[d] = nLocalCells + 2 * numGhostCellsPerDim_m;
65 cellWidth_m[d] = length / nLocalCells;
66 totalCells_m *= numCells_m[d];
67 numLocalCells_m *= nLocalCells;
68 }
69 numGhostCells_m = totalCells_m - numLocalCells_m;
70
71 /* calculate cell strides to compute flat index from nd-index
72 * idx = strides[0] * idx0 + strides[1] * idx1 + ...
73 */
74 std::exclusive_scan(numCells_m.begin(), numCells_m.end(), cellStrides_m.begin(), 1,
75 std::multiplies());
76
77 cellParticleCount_m = hash_type("cellParticleCount", totalCells_m);
78 cellStartingIdx_m = hash_type("cellStartingIdx", totalCells_m + 1);
79
80 // Compute cell permutation need to place ghost cells at the end to make sure only local
81 // particles are at indices 0 to numLocalParticles
82 hash_type cellPermutationForward("cell permutation forward", totalCells_m);
83 hash_type cellPermutationBackward("cell permutation backward", totalCells_m);
84
85 /* Step 1. compute prefix sum to determine where the local/ghost cells go */
86 hash_type localPrefixSum("local prefix sum", totalCells_m);
87 hash_type ghostPrefixSum("ghost prefix sum", totalCells_m);
88 const auto& numCells = numCells_m;
89 constexpr auto is = std::make_index_sequence<Dim>();
90 using policy_type = Kokkos::RangePolicy<position_execution_space>;
91
92 Kokkos::parallel_scan(
93 "scan_local", policy_type(0, totalCells_m),
94 KOKKOS_LAMBDA(const size_type i, size_type& update, const bool final) {
95 const size_type val = isLocalCellIndex(is, toCellIndex(i, numCells), numCells);
96 if (final) {
97 localPrefixSum(i) = update;
98 }
99 update += val;
100 });
101 Kokkos::parallel_scan(
102 "scan_ghost", policy_type(0, totalCells_m),
103 KOKKOS_LAMBDA(const size_type i, size_type& update, const bool final) {
104 const size_type val = !isLocalCellIndex(is, toCellIndex(i, numCells), numCells);
105 if (final) {
106 ghostPrefixSum(i) = update;
107 }
108 update += val;
109 });
110
111 /* Step 2. assign the cells at the correct locations of the permutations */
112 const auto numLocalCells = numLocalCells_m;
113 Kokkos::parallel_for(
114 "assign_permutations", policy_type(0, totalCells_m),
115 KOKKOS_LAMBDA(const size_type i) {
116 if (const auto cellIdx = toCellIndex(i, numCells);
117 isLocalCellIndex(is, cellIdx, numCells)) {
118 const size_type localIdx = localPrefixSum(i);
119 cellPermutationForward(i) = localIdx;
120 cellPermutationBackward(localIdx) = i;
121 } else {
122 const size_type ghostIdx = numLocalCells + ghostPrefixSum(i);
123 cellPermutationForward(i) = ghostIdx;
124 cellPermutationBackward(ghostIdx) = i;
125 }
126 });
127
128 cellPermutationForward_m = cellPermutationForward;
129 cellPermutationBackward_m = cellPermutationBackward;
130 }
131
132 template <typename T, unsigned Dim, class Mesh, typename... Properties>
133 template <std::size_t... Idx>
134 KOKKOS_INLINE_FUNCTION constexpr bool
136 const std::index_sequence<Idx...>&, const vector_type& pos, const region_type& globalRegion,
137 Kokkos::Array<bool, Dim> periodic, T overlap) {
138 return ((periodic[Idx]
139 && (pos[Idx] < globalRegion[Idx].min() + overlap
140 || pos[Idx] > globalRegion[Idx].max() - overlap))
141 || ...);
142 }
143
144 namespace detail {
145
146 template <typename ParticleContainer, typename index_type>
147 inline void copyAttributes(ParticleContainer& pc, const index_type& boundaryIndices) {
148 const auto numLoc = pc.getLocalNum();
149 const auto numBoundaryParticles = boundaryIndices.size();
150 detail::runForAllSpaces([&]<typename MemorySpace>() {
151 size_t numAttributesInSpace = 0;
152 pc.template forAllAttributes<MemorySpace>(
153 [&numAttributesInSpace]<typename Attribute>(Attribute&) {
154 ++numAttributesInSpace;
155 });
156 if (numAttributesInSpace == 0) {
157 return;
158 }
159
160 pc.template forAllAttributes<MemorySpace>(
161 [boundaryIndicesMirror = Kokkos::create_mirror_view_and_copy(
162 MemorySpace(), boundaryIndices)]<typename Attribute>(Attribute& att) {
163 att->internalCopy(boundaryIndicesMirror);
164 });
165 });
166 Kokkos::fence();
167 /* make sure other functions (particleExchange and buildCells) know about the ghost
168 * particles. They will not stay pc's particle as their positions are outside the global
169 * domain but are needed as ghost particles.
170 */
171 pc.setLocalNum(numLoc + numBoundaryParticles);
172 }
173 } // namespace detail
174
175 template <typename T, unsigned Dim, class Mesh, typename... Properties>
176 template <class ParticleContainer>
178 ParticleContainer& pc) {
179 static IpplTimings::TimerRef timer = IpplTimings::getTimer("createPeriodicGhostParticles");
180 IpplTimings::startTimer(timer);
181 /* periodic boundary conditions come in pairs. Thus collect whether each dimension is
182 * subject to periodic boundary conditions
183 */
184 Kokkos::Array<bool, Dim> periodic;
185 for (unsigned d = 0; d < Dim; ++d) {
186 periodic[d] = this->getParticleBC()[2 * d] == BC::PERIODIC;
187 }
188 // no need to create periodic ghost particles if no dimension is periodic
189 bool anyPeriodic = false;
190 for (unsigned d = 0; d < Dim; ++d) {
191 anyPeriodic = anyPeriodic || periodic[d];
192 }
193 if (!anyPeriodic) return;
194
195 const auto& globalRegion = this->rlayout_m.getDomain();
196 const auto overlap = rcutoff_m;
197 const auto numLoc = pc.getLocalNum();
198 const auto positions = pc.R.getView();
199
200 constexpr auto is = std::make_index_sequence<Dim>();
201 /* Step 1. Determine all particles which are close to the global domain boundary */
202 size_type numBoundaryParticles = 0;
203 Kokkos::parallel_reduce(
204 "count boundary particles", numLoc,
205 KOKKOS_LAMBDA(const size_t& i, size_type& sum) {
206 if (isCloseToBoundary(is, positions(i), globalRegion, periodic, overlap)) {
207 ++sum;
208 }
209 },
210 Kokkos::Sum<size_type>(numBoundaryParticles));
211 if (numBoundaryParticles == 0) {
212 return;
213 }
214
215 /* Step 2. compute prefix sum to collect boundary particle indices */
216 hash_type boundaryIndices("boundaryIndices", numBoundaryParticles);
217 Kokkos::parallel_scan(
218 "count boundary particles", numLoc,
219 KOKKOS_LAMBDA(const size_t& i, size_type& sum, bool final) {
220 if (isCloseToBoundary(is, positions(i), globalRegion, periodic, overlap)) {
221 if (final) {
222 boundaryIndices(sum) = i;
223 }
224 ++sum;
225 }
226 });
227
228 /* Step 3. copy given particles and all its attributes. A separate function is needed as
229 * lambdas with captures do not work with nvcc and template default argument ot the layout
230 * somehow. NOTE numLoc will change after this call.
231 */
232 detail::copyAttributes(pc, boundaryIndices);
233
234 /* Step 4. set the position of the copied particles to their periodic image */
235 for (unsigned d = 0; d < Dim; ++d) {
236 if (!periodic[d]) {
237 continue;
238 }
239 const auto min = globalRegion[d].min();
240 const auto length = globalRegion[d].length();
241 const auto middle = min + length / 2;
242 Kokkos::parallel_for(
243 "correct positions",
244 // the copied particles are appended at the end of the attributes
245 Kokkos::RangePolicy<position_execution_space>(numLoc,
246 numLoc + numBoundaryParticles),
247 KOKKOS_LAMBDA(const size_t& i) {
248 positions(i)[d] += positions(i)[d] > middle ? -length : length;
249 });
250 }
251
252 IpplTimings::stopTimer(timer);
253 }
254
255 template <typename T, unsigned Dim, class Mesh, typename... Properties>
256 template <class ParticleContainer>
258 ParticleContainer& pc) {
259 /* Apply Boundary Conditions */
260 static IpplTimings::TimerRef ParticleBCTimer = IpplTimings::getTimer("particleBC");
261 IpplTimings::startTimer(ParticleBCTimer);
262 this->applyBC(pc.R, this->rlayout_m.getDomain());
263 createPeriodicGhostParticles(pc);
264 IpplTimings::stopTimer(ParticleBCTimer);
265
266 /* Update Timer for the rest of the function */
267 static IpplTimings::TimerRef ParticleUpdateTimer = IpplTimings::getTimer("updateParticle");
268 IpplTimings::startTimer(ParticleUpdateTimer);
269
270 int nRanks = Comm->size();
271 if (nRanks < 2) {
272 return;
273 }
274
275 /* particle MPI exchange:
276 * 1. figure out which particles need to go where -> locateParticles(...)
277 * 2. fill send buffer and send particles
278 * 3. delete invalidated particles
279 * 4. receive particles
280 */
281
282 // 1. figure out which particles need to go where -> locateParticles(...) ============= //
283
284 static IpplTimings::TimerRef locateTimer = IpplTimings::getTimer("locateParticles");
285 IpplTimings::startTimer(locateTimer);
286
287 /* Rank-local number of particles */
288 size_type localnum = pc.getLocalNum();
289
290 /* particleRanks are the indices correspond to the indices of the local particles,
291 * the values correspond to the ranks to which the particles need to be sent note that the
292 * size is not known yet as particles may belong to multiple ranks
293 * particleRankOffsets are the offsets of each particle as a particle
294 * can be sent to multiple ranks
295 */
296 locate_type particleRanks("particles' MPI ranks");
297 locate_type particleRankOffsets("particles' MPI rank offsets", localnum + 1);
298
299 /* The indices are the indices of the particles,
300 * the boolean values describe whether the particle has left the current rank
301 * 0 --> particle valid (inside current rank)
302 * 1 --> particle invalid (left rank)
303 */
304 bool_type invalidParticles("validity of particles", localnum);
305
306 /* The indices are the MPI ranks,
307 * the values are the number of particles are sent to that rank from myrank
308 */
309 locate_type rankSendCount_dview("rankSendCount Device", nRanks);
310
311 /* The indices have no particular meaning,
312 * the values are the MPI ranks to which we need to send
313 */
314 locate_type destinationRanks_dview("destinationRanks Device", nRanks);
315
316 /* nInvalid is the number of invalid particles
317 * nDestinationRanks is the number of MPI ranks we need to send to
318 */
319 const auto [nInvalid, nDestinationRanks] =
320 locateParticles(pc, particleRanks, particleRankOffsets, invalidParticles,
321 rankSendCount_dview, destinationRanks_dview);
322
323 /* Host space copy of rankSendCount_dview */
324 auto rankSendCount_hview =
325 Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), rankSendCount_dview);
326
327 /* Host Space copy of destinationRanks_dview */
328 auto destinationRanks_hview =
329 Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), destinationRanks_dview);
330 Kokkos::fence();
331
332 IpplTimings::stopTimer(locateTimer);
333
334 // 2. fill send buffer and send particles =============================================== //
335
336 // 2.1 Remote Memory Access window for one-sided communication
337
338 static IpplTimings::TimerRef preprocTimer = IpplTimings::getTimer("sendPreprocess");
339 IpplTimings::startTimer(preprocTimer);
340
341 std::fill(this->nRecvs_m.begin(), this->nRecvs_m.end(), 0);
342
343 this->window_m.fence(0);
344
345 // Prepare RMA window for the ranks we need to send to
346 for (size_t ridx = 0; ridx < nDestinationRanks; ridx++) {
347 int rank = destinationRanks_hview[ridx];
348 if (rank == Comm->rank()) {
349 // we do not need to send to ourselves
350 continue;
351 }
352 const int* src_ptr = &rankSendCount_hview(rank);
353 this->window_m.template put<int>(src_ptr, rank, Comm->rank());
354 }
355 this->window_m.fence(0);
356
357 IpplTimings::stopTimer(preprocTimer);
358
359 // 2.2 Particle Sends
360
361 static IpplTimings::TimerRef sendTimer = IpplTimings::getTimer("particleSend");
362 IpplTimings::startTimer(sendTimer);
363
364 std::vector<MPI_Request> requests(0);
365
366 int tag = Comm->next_tag(mpi::tag::P_SPATIAL_LAYOUT, mpi::tag::P_LAYOUT_CYCLE);
367
368 for (size_t ridx = 0; ridx < nDestinationRanks; ridx++) {
369 int rank = destinationRanks_hview[ridx];
370 if (rank == Comm->rank()) {
371 continue;
372 }
373 hash_type hash("hash", rankSendCount_hview(rank));
374 fillHash(rank, particleRanks, particleRankOffsets, hash);
375 pc.sendToRank(rank, tag, requests, hash);
376 }
377
378 IpplTimings::stopTimer(sendTimer);
379
380 // 3. Internal destruction of invalid particles ======================================= //
381
382 static IpplTimings::TimerRef destroyTimer = IpplTimings::getTimer("particleDestroy");
383 IpplTimings::startTimer(destroyTimer);
384
385 pc.internalDestroy(invalidParticles, nInvalid);
386 Kokkos::fence();
387
388 IpplTimings::stopTimer(destroyTimer);
389
390 // 4. Receive Particles ================================================================ //
391
392 static IpplTimings::TimerRef recvTimer = IpplTimings::getTimer("particleRecv");
393 IpplTimings::startTimer(recvTimer);
394
395 for (int rank = 0; rank < nRanks; ++rank) {
396 if (this->nRecvs_m[rank] > 0) {
397 pc.recvFromRank(rank, tag, this->nRecvs_m[rank]);
398 }
399 }
400 IpplTimings::stopTimer(recvTimer);
401
402 IpplTimings::startTimer(sendTimer);
403
404 if (requests.size() > 0) {
405 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
406 }
407 IpplTimings::stopTimer(sendTimer);
408
409 IpplTimings::stopTimer(ParticleUpdateTimer);
410 }
411
412 template <typename T, unsigned Dim, class Mesh, typename... Properties>
413 template <class ParticleContainer>
415 particleExchange(pc);
416 buildCells(pc);
417 }
418
419 template <typename T, unsigned Dim, class Mesh, typename... Properties>
421 int rank, const locate_type& ranks) {
422 /* the offsets are not required as it is only important how many particles go to each rank
423 * and not which particles
424 */
425 size_t nSends = 0;
426 using policy_type = Kokkos::RangePolicy<position_execution_space>;
427 Kokkos::parallel_reduce(
428 "ParticleSpatialLayout::numberOfSends()", policy_type(0, ranks.extent(0)),
429 KOKKOS_LAMBDA(const size_t i, size_t& num) {
430 num += static_cast<size_t>(rank == ranks(i));
431 },
432 nSends);
433 Kokkos::fence();
434 return nSends;
435 }
436
437 template <typename T, unsigned Dim, class Mesh, typename... Properties>
439 int rank, const locate_type& ranks, const locate_type& offsets, hash_type& hash) {
440 /* Compute the prefix sum and fill the hash
441 */
442 using policy_type = Kokkos::RangePolicy<position_execution_space>;
443 Kokkos::parallel_scan(
444 "ParticleSpatialLayout::fillHash()", policy_type(0, offsets.extent(0) - 1),
445 KOKKOS_LAMBDA(const size_t i, int& idx, const bool final) {
446 /* Check if this particle belongs to our target rank. Ranks of particle i are stored
447 * from offsets(i) to offsets(i + 1)
448 */
449 bool belongs_to_rank = false;
450 const size_t start_rank_idx = offsets(i);
451 const size_t end_rank_idx = offsets(i + 1);
452
453 for (size_t rank_idx = start_rank_idx; rank_idx < end_rank_idx; ++rank_idx) {
454 if (ranks(rank_idx) == rank) {
455 belongs_to_rank = true;
456 break; // Found it, no need to continue
457 }
458 }
459
460 if (final && belongs_to_rank) {
461 hash(idx) = i;
462 }
463
464 if (belongs_to_rank) {
465 idx += 1;
466 }
467 });
468 Kokkos::fence();
469 }
470
471 /* Helper function to fill a view with neighbor ranks
472 */
473 template <typename T, unsigned Dim, class Mesh, typename... Properties>
474 typename ParticleSpatialOverlapLayout<T, Dim, Mesh, Properties...>::locate_type
476 const neighbor_list& neighbors) const {
477 /* collect all neighbors in a set. set instead of vector as with small number of ranks and
478 * periodic boundary conditions the same rank can be in the neighbor list multiple times.
479 */
480 std::set<int> neighborSet;
481 for (const auto& componentNeighbors : neighbors) {
482 for (const auto& nrank : componentNeighbors) {
483 neighborSet.insert(nrank);
484 }
485 }
486
487 // copy neighbors into view
488 locate_type flatNeighbors("Nearest neighbors IDs", neighborSet.size());
489 auto hostMirror = Kokkos::create_mirror_view(flatNeighbors);
490
491 size_type i = 0;
492 for (const auto& neighbor : neighborSet) {
493 hostMirror(i) = neighbor;
494 ++i;
495 }
496
497 Kokkos::deep_copy(flatNeighbors, hostMirror);
498 Kokkos::fence();
499 return flatNeighbors;
500 }
501
502 /* Helper function to get non-neighboring ranks
503 */
504 template <typename T, unsigned Dim, class Mesh, typename... Properties>
505 typename ParticleSpatialOverlapLayout<T, Dim, Mesh, Properties...>::locate_type
507 const locate_type& neighbors_view) const {
508 // Create a view of all non-neighbor ranks. make sure to exclude own its rank
509 const auto numNonNeighborRanks = Comm->size() - neighbors_view.extent(0) - 1;
510 locate_type nonNeighborRanks("Non Neighbor Ranks", numNonNeighborRanks);
511 if (numNonNeighborRanks == 0) {
512 return nonNeighborRanks;
513 }
514
515 // Step 1. Mark all non-neighbor ranks, by removing all neighbors and self
516 const auto total_ranks = Comm->size();
517 bool_type is_remaining("is_remaining", total_ranks);
518 using policy_type = Kokkos::RangePolicy<position_execution_space>;
519 Kokkos::deep_copy(is_remaining, true);
520 Kokkos::fence();
521
522 const auto myRank = Comm->rank();
523 Kokkos::parallel_for(
524 "mark_comm_ranks", policy_type(myRank, myRank + 1),
525 KOKKOS_LAMBDA(const size_t& i) { is_remaining(i) = false; });
526 Kokkos::parallel_for(
527 "mark_comm_ranks", policy_type(0, neighbors_view.extent(0)),
528 KOKKOS_LAMBDA(const size_t& i) { is_remaining(neighbors_view(i)) = false; });
529 Kokkos::fence();
530
531 // Step 2. Fill remaining ranks
532 Kokkos::View<size_type, position_memory_space> counter("counter");
533 Kokkos::deep_copy(counter, 0);
534 Kokkos::fence();
535 Kokkos::parallel_for(
536 "fill_remaining", policy_type(0, total_ranks), KOKKOS_LAMBDA(const size_t& i) {
537 if (is_remaining(i)) {
538 const size_type idx = Kokkos::atomic_fetch_inc(&counter());
539 nonNeighborRanks(idx) = i;
540 }
541 });
542 return nonNeighborRanks;
543 }
544
545 template <typename T, unsigned Dim, class Mesh, typename... Properties>
546 template <typename ParticleContainer>
547 std::pair<detail::size_type, detail::size_type>
549 const ParticleContainer& pc, locate_type& ranks, locate_type& rankOffsets,
550 bool_type& invalid, locate_type& nSends_dview, locate_type& sends_dview) const {
551 const auto positions = pc.R.getView();
552 const auto regions = this->rlayout_m.getdLocalRegions();
553 const auto myRank = Comm->rank();
554 const auto localNum = pc.getLocalNum();
555 const T overlap = rcutoff_m;
556 constexpr auto is = std::make_index_sequence<Dim>();
557 using policy_type = Kokkos::RangePolicy<position_execution_space>;
558
561 locate_type counts("counts", localNum);
562 locate_type outsideIds("Particles outside of neighborhood", localNum);
563
565 size_type outsideCount = 0;
567 size_type invalidCount = 0;
568
570 locate_type neighbors_view = getFlatNeighbors(this->flayout_m.getNeighbors());
571
572 /* red_val: Used to reduce both the number of invalid particles and the number of particles
573 * outside the neighborhood (Kokkos::parallel_scan doesn't allow multiple reduction
574 * values, so we use the helper class increment_type). First element updates InvalidCount,
575 * second one updates outsideCount.
576 */
577 increment_type red_val;
578 red_val.init();
579
586 static IpplTimings::TimerRef neighborSearch = IpplTimings::getTimer("neighborSearch");
587 IpplTimings::startTimer(neighborSearch);
588
589 /* First Pass: count the numbers of neighbor ranks (including self) a particle belongs to */
590 Kokkos::parallel_for(
591 "ParticleSpatialLayout::locateParticles()", policy_type(0, localNum),
592 KOKKOS_LAMBDA(const size_t& i) {
593 const bool inCurr = positionInRegion(is, positions(i), regions(myRank), overlap);
594
595 size_type count = inCurr;
596 invalid(i) = !inCurr;
597 // Count neighboring regions
598 for (size_type j = 0; j < neighbors_view.extent(0); ++j) {
599 const size_type rank = neighbors_view(j);
600 if (positionInRegion(is, positions(i), regions(rank), overlap)) {
601 ++count;
602 }
603 }
604
605 counts(i) = count;
606 });
607 Kokkos::fence();
608
609 /* Second Pass: collect number of particles outside this ranks region and the indices of the
610 * respective particles. Note that in comparison to ParticleSpatialLayout::locateParticles()
611 * particle can be in multiple ranks. For this reason if a particle is in a neighbor but not
612 * here, then we still need to search in all ranks as there is no way to get second
613 * neighbors.
614 */
615 Kokkos::parallel_scan(
616 "count_outside", policy_type(0, localNum),
617 KOKKOS_LAMBDA(const size_type i, increment_type& val, const bool final) {
618 const bool inCurr = !invalid(i);
619 if (final && !inCurr) {
620 outsideIds(val.count[1]) = i;
621 }
622 bool increment[2] = {invalid(i), !inCurr};
623 val += increment;
624 },
625 red_val);
626 Kokkos::fence();
627
628 invalidCount = red_val.count[0];
629 outsideCount = red_val.count[1];
630
631 IpplTimings::stopTimer(neighborSearch);
632
634 static IpplTimings::TimerRef nonNeighboringParticles =
635 IpplTimings::getTimer("nonNeighboringParticles");
636 IpplTimings::startTimer(nonNeighboringParticles);
637
638 using mdrange_type = Kokkos::MDRangePolicy<Kokkos::Rank<2>, position_execution_space>;
639
640 /* Count the number of non-neighbor ranks each particle belongs to */
641 locate_type outsideCounts("counts of outside neighbors", outsideCount);
642 locate_type nonNeighborsView = getNonNeighborRanks(neighbors_view);
643 if (outsideCount > 0 && nonNeighborsView.extent(0) > 0) {
644 Kokkos::deep_copy(outsideCounts, 0);
645 Kokkos::parallel_for(
646 "ParticleSpatialLayout::leftParticles()",
647 mdrange_type({0, 0}, {outsideCount, nonNeighborsView.extent(0)}),
648 KOKKOS_LAMBDA(const size_t i, const size_type j) {
650 const size_type pId = outsideIds(i);
651 const auto rank = nonNeighborsView(j);
652
654 if (positionInRegion(is, positions(pId), regions(rank), overlap)) {
655 Kokkos::atomic_inc(&outsideCounts(i));
656 }
657 });
658 Kokkos::fence();
659 Kokkos::parallel_for(
660 "ParticleSpatialLayout::leftParticles()", policy_type(0, outsideCount),
661 KOKKOS_LAMBDA(const size_t& i) { counts(outsideIds(i)) += outsideCounts(i); });
662 Kokkos::fence();
663 }
664 IpplTimings::stopTimer(nonNeighboringParticles);
665
666 IpplTimings::startTimer(neighborSearch);
667 /* prefix sum for particle rank offsets */
668 Kokkos::deep_copy(Kokkos::subview(rankOffsets, 0), 0);
669 Kokkos::parallel_scan(
670 "ParticleSpatialLayout::locateParticles()", policy_type(0, localNum),
671 KOKKOS_LAMBDA(const size_t i, size_type& localSum, const bool final) {
672 const auto count_i = counts(i);
673 if (final) {
674 rankOffsets(i + 1) = localSum + count_i;
675 }
676 localSum += count_i;
677 });
678 Kokkos::fence();
679
680 /* Get total number of assignments for allocation from the last entry of offsets */
681 auto total_assignments = Kokkos::create_mirror_view(Kokkos::subview(rankOffsets, localNum));
682 Kokkos::deep_copy(total_assignments, Kokkos::subview(rankOffsets, localNum));
683 Kokkos::fence();
684 Kokkos::resize(ranks, total_assignments());
685
686 /* Last Pass: fill the rank data */
687 Kokkos::parallel_for(
688 "ParticleSpatialLayout::locateParticles()", policy_type(0, localNum),
689 KOKKOS_LAMBDA(const size_t& i) {
690 const size_t offset = rankOffsets(i);
691 size_type local_count = 0;
692 if (positionInRegion(is, positions(i), regions(myRank), overlap)) {
693 ranks(offset) = myRank;
694 local_count++;
695 }
696 for (size_t j = 0; j < neighbors_view.extent(0); ++j) {
697 const auto nRank = neighbors_view(j);
698 if (positionInRegion(is, positions(i), regions(nRank), overlap)) {
699 ranks(offset + local_count) = nRank;
700 local_count++;
701 }
702 }
703 });
704 Kokkos::fence();
705
706 IpplTimings::stopTimer(neighborSearch);
707
708 /* Last Pass: add the data of the outside particles to the ranks */
709 IpplTimings::startTimer(nonNeighboringParticles);
710 if (outsideCount > 0) {
711 Kokkos::parallel_for(
712 "ParticleSpatialLayout::leftParticles()", policy_type(0, outsideCount),
713 KOKKOS_LAMBDA(const size_t& i) {
715 const size_type pId = outsideIds(i);
716 const size_type offset = rankOffsets(pId) + counts(pId) - outsideCounts(pId);
717 for (size_t local_count = 0, j = 0; j < nonNeighborsView.extent(0); ++j) {
718 const auto rank = nonNeighborsView(j);
719 if (positionInRegion(is, positions(pId), regions(rank), overlap)) {
720 ranks(offset + local_count) = rank;
721 local_count++;
722 }
723 }
724 });
725 Kokkos::fence();
726 }
727 IpplTimings::stopTimer(nonNeighboringParticles);
728
729 /* compute the number of sends to all ranks */
730 Kokkos::deep_copy(nSends_dview, 0);
731 Kokkos::parallel_for(
732 "Calculate nSends", policy_type(0, ranks.extent(0)),
733 KOKKOS_LAMBDA(const size_t i) {
734 size_type rank = ranks(i);
735 Kokkos::atomic_inc(&nSends_dview(rank));
736 });
737 Kokkos::fence();
738
739 /* compute the ranks to send to and the number of ranks to send to*/
740 Kokkos::View<size_type, position_memory_space> rankSends(
741 "Number of Ranks we need to send to");
742 Kokkos::parallel_for(
743 "Calculate sends", policy_type(0, nSends_dview.extent(0)),
744 KOKKOS_LAMBDA(const size_t rank) {
745 if (nSends_dview(rank) != 0) {
746 size_type index = Kokkos::atomic_fetch_inc(&rankSends());
747 sends_dview(index) = rank;
748 }
749 });
750 Kokkos::fence();
751 size_type temp;
752 Kokkos::deep_copy(temp, rankSends);
753 Kokkos::fence();
754
755 return {invalidCount, temp};
756 }
757
758 template <typename T, unsigned Dim, class Mesh, typename... Properties>
759 template <std::size_t... Idx>
760 KOKKOS_INLINE_FUNCTION constexpr bool
762 const std::index_sequence<Idx...>&, const vector_type& pos, const region_type& region,
763 T overlap) {
764 return ((pos[Idx] > region[Idx].min() - overlap) && ...)
765 && ((pos[Idx] <= region[Idx].max() + overlap) && ...);
766 }
767
768 template <typename T, unsigned Dim, class Mesh, typename... Properties>
769 KOKKOS_INLINE_FUNCTION constexpr
770 typename ParticleSpatialOverlapLayout<T, Dim, Mesh, Properties...>::FlatCellIndex_t
772 const CellIndex_t& cellIndex, const Vector<size_type, Dim>& cellStrides,
773 hash_type cellPermutationForward) {
774 return cellPermutationForward(cellIndex.dot(cellStrides));
775 }
776
777 template <typename T, unsigned Dim, class Mesh, typename... Properties>
778 KOKKOS_INLINE_FUNCTION constexpr
779 typename ParticleSpatialOverlapLayout<T, Dim, Mesh, Properties...>::CellIndex_t
781 FlatCellIndex_t nonPermutedIndex, const Vector<size_type, Dim>& numCells) {
782 CellIndex_t ndIndex;
783 // #pragma unroll
784 for (size_type d = 0; d < Dim; ++d) {
785 ndIndex[d] = nonPermutedIndex % numCells[d];
786 nonPermutedIndex /= numCells[d];
787 }
788 return ndIndex;
789 }
790
791 template <typename T, unsigned Dim, class Mesh, typename... Properties>
792 KOKKOS_INLINE_FUNCTION constexpr
793 typename ParticleSpatialOverlapLayout<T, Dim, Mesh, Properties...>::CellIndex_t
795 const vector_type& pos, const region_type& region, const Vector<T, Dim>& cellWidth) {
796 CellIndex_t cellIndex;
797 for (unsigned d = 0; d < Dim; ++d) {
798 cellIndex[d] = static_cast<size_type>(
799 Kokkos::floor((pos[d] - region[d].min()) / cellWidth[d]) + numGhostCellsPerDim_m);
800 }
801 return cellIndex;
802 }
803
804 template <typename T, unsigned Dim, class Mesh, typename... Properties>
805 template <std::size_t... Idx>
807 const std::index_sequence<Idx...>&, const CellIndex_t& index,
808 const Vector<size_type, Dim>& numCells) {
809 return !((index[Idx] == 0 || index[Idx] == numCells[Idx] - 1) || ...);
810 }
811
812 namespace detail {
813 template <typename ParticleContainer, typename index_type>
814 inline void sortParticles(ParticleContainer& pc, const index_type& newIndex) {
815 detail::runForAllSpaces([&]<typename MemorySpace>() {
816 size_t num_attributes_in_space = 0;
817 pc.template forAllAttributes<MemorySpace>([&]<typename Attribute>(Attribute&) {
818 ++num_attributes_in_space;
819 });
820 if (num_attributes_in_space == 0) {
821 return;
822 }
823
824 pc.template forAllAttributes<MemorySpace>(
825 [newIndexMirror = Kokkos::create_mirror_view_and_copy(
826 MemorySpace(), newIndex)]<typename Attribute>(Attribute& att) {
827 att->applyPermutation(newIndexMirror);
828 });
829 });
830 Kokkos::fence();
831 }
832 } // namespace detail
833
834 template <typename T, unsigned Dim, class Mesh, typename... Properties>
835 template <class ParticleContainer>
837 ParticleContainer& pc) {
838 static IpplTimings::TimerRef cellBuildTimer = IpplTimings::getTimer("cellBuildTimer");
839 IpplTimings::startTimer(cellBuildTimer);
840
841 // get local variables of all necessary data as needed for the Kokkos parallel loops
842 const auto rank = Comm->rank();
843 const size_type numLoc = pc.getLocalNum();
844 const auto positions = pc.R.getView();
845 const auto totalCells = totalCells_m;
846 const auto numLocalCells = numLocalCells_m;
847 const auto localRegion = this->rlayout_m.gethLocalRegions()(rank);
848 const auto& cellWidth = cellWidth_m;
849 const auto cellStrides = cellStrides_m;
850 const auto cellPermutationForward = cellPermutationForward_m;
851
852 using int_type = typename particle_neighbor_list_type::value_type;
853
854 // allocate required (temporary) Kokkos views
855 hash_type cellIndex("cellIndex", numLoc);
856 hash_type cellParticleCount = cellParticleCount_m;
857 hash_type cellStartingIdx = cellStartingIdx_m;
858 hash_type cellCurrentIdx("cellCurrentIdx", totalCells + 1);
859
860 using range_policy = Kokkos::RangePolicy<position_execution_space>;
861
862 /* Step 1. calculate cell index for each particle and keep track of how many particles are
863 * in each cell
864 */
865 Kokkos::deep_copy(cellParticleCount, 0);
866 Kokkos::parallel_for(
867 "CalcCellIndices", range_policy(0, numLoc), KOKKOS_LAMBDA(const size_t& i) {
868 const auto locCellIndex = getCellIndex(positions(i), localRegion, cellWidth);
869 const auto locCellIndexFlat =
870 toFlatCellIndex(locCellIndex, cellStrides, cellPermutationForward);
871 Kokkos::atomic_inc(&cellParticleCount(locCellIndexFlat));
872 cellIndex(i) = locCellIndexFlat;
873 });
874 Kokkos::fence();
875
876 /* Step 2. compute starting indices for each cell from the counts */
877 Kokkos::parallel_scan(
878 "CalcStartingIndices", range_policy(0, totalCells),
879 KOKKOS_LAMBDA(const size_t i, int_type& localSum, bool isFinal) {
880 if (isFinal) {
881 cellStartingIdx(i) = localSum;
882 }
883 localSum += cellParticleCount(i);
884 });
885 /* set last position of cell staring index to numLoc*/
886 Kokkos::deep_copy(
887 Kokkos::subview(cellStartingIdx, Kokkos::make_pair(totalCells, totalCells + 1)),
888 numLoc);
889 Kokkos::fence();
890
891 Kokkos::deep_copy(cellCurrentIdx, cellStartingIdx);
892 Kokkos::fence();
893
894 hash_type newIndex("newIndex", numLoc);
895 hash_type newCellIndex("cellIndex", numLoc);
896
897 /* Step 3. compute new indices for the particles such that they are sorted according to
898 * cellStaringIdx and sort cell indices of the particles in tandem
899 */
900 Kokkos::parallel_for(
901 "Calculate new Indices", range_policy(0, numLoc), KOKKOS_LAMBDA(const size_type& i) {
902 const auto locCellIndex = cellIndex(i);
903 const size_type newIdx = Kokkos::atomic_fetch_inc(&cellCurrentIdx(locCellIndex));
904 newIndex(i) = newIdx;
905 newCellIndex(newIdx) = locCellIndex;
906 });
907 Kokkos::fence();
908
909 /* Step 4. Sort all particles (and all their attributes) according to then new indices
910 * (maybe there is a better solution). A separate function is needed as lambdas with
911 * captures do not work with nvcc and template default argument ot the layout somehow.
912 */
913 detail::sortParticles(pc, newIndex);
914
915 /* Step 5. set local number of particles (excluding ghost particles) is the value of
916 * cellStartingIdx at index numLocalCells*/
917 auto numLocalParticles =
918 Kokkos::create_mirror_view(Kokkos::subview(cellStartingIdx, numLocalCells));
919 Kokkos::deep_copy(numLocalParticles, Kokkos::subview(cellStartingIdx, numLocalCells));
920
921 numLocalParticles_m = numLocalParticles();
922 pc.setLocalNum(numLocalParticles_m);
923
924 /* store the new cell indices */
925 cellIndex_m = newCellIndex;
926
927 IpplTimings::stopTimer(cellBuildTimer);
928 }
929
930 template <typename T, unsigned Dim, class Mesh, typename... Properties>
931 KOKKOS_INLINE_FUNCTION constexpr
932 typename ParticleSpatialOverlapLayout<T, Dim, Mesh,
933 Properties...>::cell_particle_neighbor_list_type
935 const CellIndex_t& cellIndex, const Vector<size_type, Dim>& cellStrides,
936 const hash_type& cellPermutationForward) {
937 /* Generate all 3^Dim combinations of offsets (-1, 0, +1) for each dimension by using
938 * "base-3" representation of neighbor index. in base-3 each digit is 0, 1, 2 subtracting
939 * one leads to the desired offsets -1, 0, +1.
940 */
941 constexpr size_type numNeighbors = detail::countHypercubes(Dim);
942 cell_particle_neighbor_list_type neighborIndices;
943 for (size_type neighborIdx = 0; neighborIdx < numNeighbors; ++neighborIdx) {
944 index_t temp = neighborIdx;
945
946 /* extract the offsets from the base-3 representation of the neighborIdx */
947 auto neighborCellIndex = cellIndex;
948 for (unsigned d = 0; d < Dim; ++d) {
949 neighborCellIndex(d) += (temp % 3) - 1;
950 temp /= 3;
951 }
952
953 neighborIndices[neighborIdx] =
954 toFlatCellIndex(neighborCellIndex, cellStrides, cellPermutationForward);
955 }
956 return neighborIndices;
957 }
958
959 template <typename T, unsigned Dim, class Mesh, typename... Properties>
960 typename ParticleSpatialOverlapLayout<T, Dim, Mesh, Properties...>::ParticleNeighborData
962 return ParticleNeighborData(numLocalParticles_m, cellStrides_m, numCells_m, cellWidth_m,
963 this->rlayout_m.gethLocalRegions()(Comm->rank()),
964 cellStartingIdx_m, cellIndex_m, cellParticleCount_m,
965 cellPermutationForward_m, cellPermutationBackward_m);
966 }
967
968 template <typename T, unsigned Dim, class Mesh, typename... Properties>
969 KOKKOS_FUNCTION
970 typename ParticleSpatialOverlapLayout<T, Dim, Mesh,
971 Properties...>::particle_neighbor_list_type
973 const vector_type& pos, const ParticleNeighborData& particleNeighborData) {
974 /* get the cell index corresponding to pos */
975 const auto locCellIndex =
976 getCellIndex(pos, particleNeighborData.region, particleNeighborData.cellStrides,
977 particleNeighborData.cellWidth);
978 constexpr size_type numNeighbors = detail::countHypercubes(Dim);
979
980 /* cell neighbors */
981 const auto neighbors = getCellNeighbors(locCellIndex, particleNeighborData.cellStrides,
982 particleNeighborData.cellPermutationForward);
983
984 /* Get sizes of the cell neighbors and total particle neighbors */
985 size_type totalParticleInNeighbors = 0;
986 size_type maxParticleInNeighbors = 0;
987
988 Kokkos::Array<typename hash_type::value_type, numNeighbors> neighborSizes;
989 Kokkos::Array<typename hash_type::value_type, numNeighbors> neighborOffsets;
990 // #pragma unroll
991 for (size_type neighborIdx = 0; neighborIdx < numNeighbors; ++neighborIdx) {
992 auto n = particleNeighborData.cellParticleCount(neighbors[neighborIdx]);
993 neighborSizes[neighborIdx] = n;
994 maxParticleInNeighbors = n > maxParticleInNeighbors ? n : maxParticleInNeighbors;
995 neighborOffsets[neighborIdx] = totalParticleInNeighbors;
996 totalParticleInNeighbors += n;
997 }
998
999 /* Collect the neighbor particles from all cell neighbors */
1000 particle_neighbor_list_type neighborList("Neighbor list", totalParticleInNeighbors);
1001
1002 using mdrange_policy = Kokkos::MDRangePolicy<Kokkos::Rank<2>, position_execution_space>;
1003 Kokkos::parallel_for(
1004 "collect neighbors", mdrange_policy({0, 0}, {numNeighbors, maxParticleInNeighbors}),
1005 KOKKOS_LAMBDA(const size_type& i, const size_type& j) {
1006 if (j < neighborSizes[i]) {
1007 neighborList(neighborOffsets[i] + j) =
1008 particleNeighborData.cellStartingIdx(neighbors[i]) + j;
1009 }
1010 });
1011 Kokkos::fence();
1012
1013 return neighborList;
1014 }
1015
1016 template <typename T, unsigned Dim, class Mesh, typename... Properties>
1017 KOKKOS_FUNCTION
1018 typename ParticleSpatialOverlapLayout<T, Dim, Mesh,
1019 Properties...>::particle_neighbor_list_type
1021 index_t particleIndex, const ParticleNeighborData& particleNeighborData) {
1022 // Get the cell of the particle
1023 constexpr size_type numNeighbors = detail::countHypercubes(Dim);
1024
1025 /* get the cell index corresponding to particleIndex */
1026 const auto locCellIndexFlat = particleNeighborData.cellIndex(particleIndex);
1027 const auto locCellIndex =
1028 toCellIndex(particleNeighborData.cellPermutationBackward(locCellIndexFlat),
1029 particleNeighborData.numCells);
1030
1031 /* cell neighbors */
1032 const auto neighbors = getCellNeighbors(locCellIndex, particleNeighborData.cellStrides,
1033 particleNeighborData.cellPermutationForward);
1034
1035 /* Get sizes of the cell neighbors and total particle neighbors */
1036 size_type totalParticleInNeighbors = 0;
1037 size_type maxParticleInNeighbors = 0;
1038
1039 Kokkos::Array<size_type, numNeighbors> neighborSizes;
1040 Kokkos::Array<size_type, numNeighbors + 1> neighborOffsets;
1041 // #pragma unroll
1042 for (size_type neighborIdx = 0; neighborIdx < numNeighbors; ++neighborIdx) {
1043 auto n = particleNeighborData.cellParticleCount(neighbors[neighborIdx]);
1044 maxParticleInNeighbors = n > maxParticleInNeighbors ? n : maxParticleInNeighbors;
1045 neighborOffsets[neighborIdx] = totalParticleInNeighbors;
1046 totalParticleInNeighbors += n;
1047 }
1048 neighborOffsets[numNeighbors] = totalParticleInNeighbors;
1049
1050 /* Collect the neighbor particles from all cell neighbors */
1051 particle_neighbor_list_type neighborList("Neigbor list", totalParticleInNeighbors);
1052
1053 using mdrange_policy = Kokkos::MDRangePolicy<Kokkos::Rank<2>, position_execution_space>;
1054 Kokkos::parallel_for(
1055 "collect neighbors", mdrange_policy({0, 0}, {numNeighbors, maxParticleInNeighbors}),
1056 KOKKOS_LAMBDA(const size_type& i, const size_type& j) {
1057 const auto numParticlesInCell = neighborOffsets[i + 1] - neighborOffsets[i];
1058 if (j < numParticlesInCell) {
1059 neighborList(neighborOffsets[i] + j) =
1060 particleNeighborData.cellStartingIdx(neighbors[i]) + j;
1061 }
1062 });
1063
1064 Kokkos::fence();
1065
1066 return neighborList;
1067 }
1068
1069 template <typename T, unsigned Dim, class Mesh, typename... Properties>
1070 template <typename ExecutionSpace, typename Functor>
1072 static IpplTimings::TimerRef interactionTimer = IpplTimings::getTimer("PPInteractionTimer");
1073 IpplTimings::startTimer(interactionTimer);
1074
1075 /* get local variables necessary for Kokkos parallel regions */
1076 const auto cellStartingIdx = cellStartingIdx_m;
1077 const auto cellParticleCount = cellParticleCount_m;
1078 const auto cellPermutationForward = cellPermutationForward_m;
1079 const auto cellPermutationBackward = cellPermutationBackward_m;
1080 const auto& cellStrides = cellStrides_m;
1081 const auto& numCells = numCells_m;
1082
1083 constexpr auto numCellNeighbors = detail::countHypercubes(Dim);
1084
1085 /* Iterate over all local cells (non-ghost cells) and let all particles of this cell
1086 * interact with all particles from all particles with all cell neighbors
1087 */
1088 using team_policy_t = Kokkos::TeamPolicy<ExecutionSpace>;
1089 using team_t = typename team_policy_t::member_type;
1090 Kokkos::parallel_for(
1091 "ParticleSpatialOverlapLayout::forEachPair()",
1092 team_policy_t(numLocalCells_m, Kokkos::AUTO()), KOKKOS_LAMBDA(const team_t& team) {
1093 const size_type cellIdxFlat = team.league_rank();
1094 if (cellParticleCount(cellIdxFlat) == 0) {
1095 return;
1096 }
1097
1098 const auto cellParticleOffset = cellStartingIdx(cellIdxFlat);
1099 const auto numCellParticles = cellParticleCount(cellIdxFlat);
1100
1101 /* get nd-cell-index and its neighbors */
1102 const auto cellIdx = toCellIndex(cellPermutationBackward(cellIdxFlat), numCells);
1103 const auto cellNeighbors =
1104 getCellNeighbors(cellIdx, cellStrides, cellPermutationForward);
1105
1106 /* iterate over all cell neighbors */
1107 Kokkos::parallel_for(
1108 Kokkos::TeamThreadRange(team, numCellNeighbors), [&](const size_t& n) {
1109 const auto neighborCellIdx = cellNeighbors[n];
1110 const auto neighborCellParticleOffset = cellStartingIdx(neighborCellIdx);
1111 const auto numNeighborCellParticles = cellParticleCount(neighborCellIdx);
1112
1113 /* iterate over all combinations of this cell and neighboring cells
1114 * particles
1115 */
1116 Kokkos::parallel_for(Kokkos::ThreadVectorMDRange<Kokkos::Rank<2>, team_t>(
1117 team, numCellParticles, numNeighborCellParticles),
1118 [&](const size_t& i, const size_t& j) {
1119 const auto particleIdx = cellParticleOffset + i;
1120 const auto neighborIdx =
1121 neighborCellParticleOffset + j;
1122
1123 if (neighborIdx == particleIdx) {
1124 return;
1125 }
1126
1127 f(particleIdx, neighborIdx);
1128 });
1129 });
1130 });
1131 Kokkos::fence();
1132
1133 IpplTimings::stopTimer(interactionTimer);
1134 }
1135
1136} // namespace ippl
Definition FieldLayout.h:166
Definition Mesh.h:15
Definition ParticleSpatialLayout.h:46
typename region_view_type::value_type region_type
Type of a single Region object.
Definition ParticleSpatialLayout.h:94
Definition ParticleSpatialOverlapLayout.h:75
Definition ParticleSpatialOverlapLayout.h:40
ParticleNeighborData getParticleNeighborData() const
Definition ParticleSpatialOverlapLayout.hpp:961
void fillHash(int rank, const locate_type &ranks, const locate_type &offsets, hash_type &hash)
utility function to collect all indices of particles to send to given rank
Definition ParticleSpatialOverlapLayout.hpp:438
KOKKOS_INLINE_FUNCTION static constexpr cell_particle_neighbor_list_type getCellNeighbors(const CellIndex_t &cellIndex, const Vector< size_type, Dim > &cellStrides, const hash_type &cellPermutationForward)
get all indices of cell neighbors of a given nd-cell-index
Definition ParticleSpatialOverlapLayout.hpp:934
KOKKOS_INLINE_FUNCTION static constexpr CellIndex_t toCellIndex(FlatCellIndex_t nonPermutedIndex, const Vector< size_type, Dim > &numCells)
compute the nd-cell-index from a flattened (non-permuted) index
Definition ParticleSpatialOverlapLayout.hpp:780
size_type numberOfSends(int rank, const locate_type &ranks)
utility function to compute how many particles to sent to a given rank
Definition ParticleSpatialOverlapLayout.hpp:420
locate_type getNonNeighborRanks(const locate_type &neighbors_view) const
utility function to get a view of all non-neighboring ranks
Definition ParticleSpatialOverlapLayout.hpp:506
KOKKOS_INLINE_FUNCTION static constexpr bool isLocalCellIndex(const std::index_sequence< Idx... > &, const CellIndex_t &index, const Vector< size_type, Dim > &numCells)
determines whether cell index is local cell index
KOKKOS_INLINE_FUNCTION static constexpr FlatCellIndex_t toFlatCellIndex(const CellIndex_t &cellIndex, const Vector< size_type, Dim > &cellStrides, hash_type cellPermutationForward)
convert a nd-cell-index to flat cell index
Definition ParticleSpatialOverlapLayout.hpp:771
static KOKKOS_FUNCTION particle_neighbor_list_type getParticleNeighbors(index_t particleIndex, const ParticleNeighborData &particleNeighborData)
Function to get particle neighbors depending on index (possible inside Kokkos parallel region) make s...
Definition ParticleSpatialOverlapLayout.hpp:1020
void particleExchange(ParticleContainer &pc)
exchange particles by scanning neighbor ranks first, only scan other ranks if needed....
Definition ParticleSpatialOverlapLayout.hpp:257
typename FieldLayout_t::neighbor_list neighbor_list
Array of N rank lists, where N = number of hypercubes for the dimension Dim.
Definition ParticleSpatialOverlapLayout.h:65
std::pair< detail::size_type, detail::size_type > locateParticles(const ParticleContainer &pc, locate_type &ranks, locate_type &rankOffsets, bool_type &invalid, locate_type &nSends_dview, locate_type &sends_dview) const
This function determines to which rank particles need to be sent after the iteration step....
Definition ParticleSpatialOverlapLayout.hpp:548
KOKKOS_INLINE_FUNCTION static constexpr CellIndex_t getCellIndex(const vector_type &pos, const region_type &region, const Vector< T, Dim > &cellWidth)
get the nd-cell-index of a position
Definition ParticleSpatialOverlapLayout.hpp:794
void createPeriodicGhostParticles(ParticleContainer &pc)
copies particles close to the boundary and offsets them to their closest periodic image
Definition ParticleSpatialOverlapLayout.hpp:177
KOKKOS_INLINE_FUNCTION static constexpr bool positionInRegion(const std::index_sequence< Idx... > &, const vector_type &pos, const region_type &region, T overlap)
determines whether a position is in a region including its overlap
void update(ParticleContainer &pc)
updates particles by exchanging them across ranks according to their positions. then constructs the p...
Definition ParticleSpatialOverlapLayout.hpp:414
void forEachPair(Functor &&f) const
call functor for each combination i, j. make sure to call update first
Definition ParticleSpatialOverlapLayout.hpp:1071
KOKKOS_INLINE_FUNCTION static constexpr bool isCloseToBoundary(const std::index_sequence< Idx... > &, const vector_type &pos, const region_type &region, Kokkos::Array< bool, Dim > periodic, T overlap)
determines whether a position is within overlap to the boundary of a region
void buildCells(ParticleContainer &pc)
builds the cell structure, sorts the particles according to the cells and makes sure only local parti...
Definition ParticleSpatialOverlapLayout.hpp:836
locate_type getFlatNeighbors(const neighbor_list &neighbors) const
utility function to get a flat view of all neighbor processes
Definition ParticleSpatialOverlapLayout.hpp:475
void initializeCells()
initializes all data necessary for the cells
Definition ParticleSpatialOverlapLayout.hpp:45
Definition Archive.h:20
Definition ParticleSpatialLayout.hpp:38