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