17#include <Kokkos_MathematicalFunctions.hpp>
22#include "Utility/IpplTimings.h"
24#include "../../alpine/ParticleContainer.hpp"
25#include "Communicate/Window.h"
28 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
33 , numLocalParticles_m(0) {
37 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
40 Base::updateLayout(fl, mesh);
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");
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);
64 numCells_m[d] = nLocalCells + 2 * numGhostCellsPerDim_m;
65 cellWidth_m[d] = length / nLocalCells;
66 totalCells_m *= numCells_m[d];
67 numLocalCells_m *= nLocalCells;
69 numGhostCells_m = totalCells_m - numLocalCells_m;
74 std::exclusive_scan(numCells_m.begin(), numCells_m.end(), cellStrides_m.begin(), 1,
77 cellParticleCount_m = hash_type(
"cellParticleCount", totalCells_m);
78 cellStartingIdx_m = hash_type(
"cellStartingIdx", totalCells_m + 1);
82 hash_type cellPermutationForward(
"cell permutation forward", totalCells_m);
83 hash_type cellPermutationBackward(
"cell permutation backward", totalCells_m);
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>;
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);
97 localPrefixSum(i) = update;
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);
106 ghostPrefixSum(i) = update;
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;
122 const size_type ghostIdx = numLocalCells + ghostPrefixSum(i);
123 cellPermutationForward(i) = ghostIdx;
124 cellPermutationBackward(ghostIdx) = i;
128 cellPermutationForward_m = cellPermutationForward;
129 cellPermutationBackward_m = cellPermutationBackward;
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))
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;
156 if (numAttributesInSpace == 0) {
160 pc.template forAllAttributes<MemorySpace>(
161 [boundaryIndicesMirror = Kokkos::create_mirror_view_and_copy(
162 MemorySpace(), boundaryIndices)]<
typename Attribute>(Attribute& att) {
163 att->internalCopy(boundaryIndicesMirror);
171 pc.setLocalNum(numLoc + numBoundaryParticles);
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);
184 Kokkos::Array<bool, Dim> periodic;
185 for (
unsigned d = 0; d < Dim; ++d) {
186 periodic[d] = this->getParticleBC()[2 * d] == BC::PERIODIC;
189 bool anyPeriodic =
false;
190 for (
unsigned d = 0; d < Dim; ++d) {
191 anyPeriodic = anyPeriodic || periodic[d];
193 if (!anyPeriodic)
return;
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();
200 constexpr auto is = std::make_index_sequence<Dim>();
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)) {
210 Kokkos::Sum<size_type>(numBoundaryParticles));
211 if (numBoundaryParticles == 0) {
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)) {
222 boundaryIndices(sum) = i;
232 detail::copyAttributes(pc, boundaryIndices);
235 for (
unsigned d = 0; d < Dim; ++d) {
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(
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;
252 IpplTimings::stopTimer(timer);
255 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
256 template <
class ParticleContainer>
258 ParticleContainer& pc) {
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);
267 static IpplTimings::TimerRef ParticleUpdateTimer = IpplTimings::getTimer(
"updateParticle");
268 IpplTimings::startTimer(ParticleUpdateTimer);
270 int nRanks = Comm->size();
284 static IpplTimings::TimerRef locateTimer = IpplTimings::getTimer(
"locateParticles");
285 IpplTimings::startTimer(locateTimer);
288 size_type localnum = pc.getLocalNum();
296 locate_type particleRanks(
"particles' MPI ranks");
297 locate_type particleRankOffsets(
"particles' MPI rank offsets", localnum + 1);
304 bool_type invalidParticles(
"validity of particles", localnum);
309 locate_type rankSendCount_dview(
"rankSendCount Device", nRanks);
314 locate_type destinationRanks_dview(
"destinationRanks Device", nRanks);
319 const auto [nInvalid, nDestinationRanks] =
320 locateParticles(pc, particleRanks, particleRankOffsets, invalidParticles,
321 rankSendCount_dview, destinationRanks_dview);
324 auto rankSendCount_hview =
325 Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), rankSendCount_dview);
328 auto destinationRanks_hview =
329 Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), destinationRanks_dview);
332 IpplTimings::stopTimer(locateTimer);
338 static IpplTimings::TimerRef preprocTimer = IpplTimings::getTimer(
"sendPreprocess");
339 IpplTimings::startTimer(preprocTimer);
341 std::fill(this->nRecvs_m.begin(), this->nRecvs_m.end(), 0);
343 this->window_m.fence(0);
346 for (
size_t ridx = 0; ridx < nDestinationRanks; ridx++) {
347 int rank = destinationRanks_hview[ridx];
348 if (rank == Comm->rank()) {
352 const int* src_ptr = &rankSendCount_hview(rank);
353 this->window_m.template put<int>(src_ptr, rank, Comm->rank());
355 this->window_m.fence(0);
357 IpplTimings::stopTimer(preprocTimer);
361 static IpplTimings::TimerRef sendTimer = IpplTimings::getTimer(
"particleSend");
362 IpplTimings::startTimer(sendTimer);
364 std::vector<MPI_Request> requests(0);
366 int tag = Comm->next_tag(mpi::tag::P_SPATIAL_LAYOUT, mpi::tag::P_LAYOUT_CYCLE);
368 for (
size_t ridx = 0; ridx < nDestinationRanks; ridx++) {
369 int rank = destinationRanks_hview[ridx];
370 if (rank == Comm->rank()) {
373 hash_type hash(
"hash", rankSendCount_hview(rank));
374 fillHash(rank, particleRanks, particleRankOffsets, hash);
375 pc.sendToRank(rank, tag, requests, hash);
378 IpplTimings::stopTimer(sendTimer);
382 static IpplTimings::TimerRef destroyTimer = IpplTimings::getTimer(
"particleDestroy");
383 IpplTimings::startTimer(destroyTimer);
385 pc.internalDestroy(invalidParticles, nInvalid);
388 IpplTimings::stopTimer(destroyTimer);
392 static IpplTimings::TimerRef recvTimer = IpplTimings::getTimer(
"particleRecv");
393 IpplTimings::startTimer(recvTimer);
395 for (
int rank = 0; rank < nRanks; ++rank) {
396 if (this->nRecvs_m[rank] > 0) {
397 pc.recvFromRank(rank, tag, this->nRecvs_m[rank]);
400 IpplTimings::stopTimer(recvTimer);
402 IpplTimings::startTimer(sendTimer);
404 if (requests.size() > 0) {
405 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
407 IpplTimings::stopTimer(sendTimer);
409 IpplTimings::stopTimer(ParticleUpdateTimer);
412 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
413 template <
class ParticleContainer>
415 particleExchange(pc);
419 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
421 int rank,
const locate_type& ranks) {
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));
437 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
439 int rank,
const locate_type& ranks,
const locate_type& offsets, hash_type& hash) {
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) {
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);
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;
460 if (
final && belongs_to_rank) {
464 if (belongs_to_rank) {
473 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
480 std::set<int> neighborSet;
481 for (
const auto& componentNeighbors : neighbors) {
482 for (
const auto& nrank : componentNeighbors) {
483 neighborSet.insert(nrank);
488 locate_type flatNeighbors(
"Nearest neighbors IDs", neighborSet.size());
489 auto hostMirror = Kokkos::create_mirror_view(flatNeighbors);
492 for (
const auto& neighbor : neighborSet) {
493 hostMirror(i) = neighbor;
497 Kokkos::deep_copy(flatNeighbors, hostMirror);
499 return flatNeighbors;
504 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
507 const locate_type& neighbors_view)
const {
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;
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);
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; });
532 Kokkos::View<size_type, position_memory_space> counter(
"counter");
533 Kokkos::deep_copy(counter, 0);
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;
542 return nonNeighborRanks;
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>;
561 locate_type counts(
"counts", localNum);
562 locate_type outsideIds(
"Particles outside of neighborhood", localNum);
565 size_type outsideCount = 0;
567 size_type invalidCount = 0;
570 locate_type neighbors_view = getFlatNeighbors(this->flayout_m.getNeighbors());
586 static IpplTimings::TimerRef neighborSearch = IpplTimings::getTimer(
"neighborSearch");
587 IpplTimings::startTimer(neighborSearch);
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);
595 size_type count = inCurr;
596 invalid(i) = !inCurr;
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)) {
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;
622 bool increment[2] = {invalid(i), !inCurr};
628 invalidCount = red_val.count[0];
629 outsideCount = red_val.count[1];
631 IpplTimings::stopTimer(neighborSearch);
634 static IpplTimings::TimerRef nonNeighboringParticles =
635 IpplTimings::getTimer(
"nonNeighboringParticles");
636 IpplTimings::startTimer(nonNeighboringParticles);
638 using mdrange_type = Kokkos::MDRangePolicy<Kokkos::Rank<2>, position_execution_space>;
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);
654 if (positionInRegion(is, positions(pId), regions(rank), overlap)) {
655 Kokkos::atomic_inc(&outsideCounts(i));
659 Kokkos::parallel_for(
660 "ParticleSpatialLayout::leftParticles()", policy_type(0, outsideCount),
661 KOKKOS_LAMBDA(
const size_t& i) { counts(outsideIds(i)) += outsideCounts(i); });
664 IpplTimings::stopTimer(nonNeighboringParticles);
666 IpplTimings::startTimer(neighborSearch);
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);
674 rankOffsets(i + 1) = localSum + count_i;
681 auto total_assignments = Kokkos::create_mirror_view(Kokkos::subview(rankOffsets, localNum));
682 Kokkos::deep_copy(total_assignments, Kokkos::subview(rankOffsets, localNum));
684 Kokkos::resize(ranks, total_assignments());
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;
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;
706 IpplTimings::stopTimer(neighborSearch);
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;
727 IpplTimings::stopTimer(nonNeighboringParticles);
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));
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;
752 Kokkos::deep_copy(temp, rankSends);
755 return {invalidCount, temp};
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,
764 return ((pos[Idx] > region[Idx].min() - overlap) && ...)
765 && ((pos[Idx] <= region[Idx].max() + overlap) && ...);
768 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
769 KOKKOS_INLINE_FUNCTION
constexpr
770 typename ParticleSpatialOverlapLayout<T, Dim, Mesh, Properties...>::FlatCellIndex_t
773 hash_type cellPermutationForward) {
774 return cellPermutationForward(cellIndex.dot(cellStrides));
777 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
778 KOKKOS_INLINE_FUNCTION
constexpr
784 for (size_type d = 0; d < Dim; ++d) {
785 ndIndex[d] = nonPermutedIndex % numCells[d];
786 nonPermutedIndex /= numCells[d];
791 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
792 KOKKOS_INLINE_FUNCTION
constexpr
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);
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,
809 return !((index[Idx] == 0 || index[Idx] == numCells[Idx] - 1) || ...);
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;
820 if (num_attributes_in_space == 0) {
824 pc.template forAllAttributes<MemorySpace>(
825 [newIndexMirror = Kokkos::create_mirror_view_and_copy(
826 MemorySpace(), newIndex)]<
typename Attribute>(Attribute& att) {
827 att->applyPermutation(newIndexMirror);
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);
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;
852 using int_type =
typename particle_neighbor_list_type::value_type;
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);
860 using range_policy = Kokkos::RangePolicy<position_execution_space>;
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;
877 Kokkos::parallel_scan(
878 "CalcStartingIndices", range_policy(0, totalCells),
879 KOKKOS_LAMBDA(
const size_t i, int_type& localSum,
bool isFinal) {
881 cellStartingIdx(i) = localSum;
883 localSum += cellParticleCount(i);
887 Kokkos::subview(cellStartingIdx, Kokkos::make_pair(totalCells, totalCells + 1)),
891 Kokkos::deep_copy(cellCurrentIdx, cellStartingIdx);
894 hash_type newIndex(
"newIndex", numLoc);
895 hash_type newCellIndex(
"cellIndex", numLoc);
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;
913 detail::sortParticles(pc, newIndex);
917 auto numLocalParticles =
918 Kokkos::create_mirror_view(Kokkos::subview(cellStartingIdx, numLocalCells));
919 Kokkos::deep_copy(numLocalParticles, Kokkos::subview(cellStartingIdx, numLocalCells));
921 numLocalParticles_m = numLocalParticles();
922 pc.setLocalNum(numLocalParticles_m);
925 cellIndex_m = newCellIndex;
927 IpplTimings::stopTimer(cellBuildTimer);
930 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
931 KOKKOS_INLINE_FUNCTION
constexpr
933 Properties...>::cell_particle_neighbor_list_type
936 const hash_type& cellPermutationForward) {
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;
947 auto neighborCellIndex = cellIndex;
948 for (
unsigned d = 0; d < Dim; ++d) {
949 neighborCellIndex(d) += (temp % 3) - 1;
953 neighborIndices[neighborIdx] =
954 toFlatCellIndex(neighborCellIndex, cellStrides, cellPermutationForward);
956 return neighborIndices;
959 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
963 this->rlayout_m.gethLocalRegions()(Comm->rank()),
964 cellStartingIdx_m, cellIndex_m, cellParticleCount_m,
965 cellPermutationForward_m, cellPermutationBackward_m);
968 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
971 Properties...>::particle_neighbor_list_type
975 const auto locCellIndex =
976 getCellIndex(pos, particleNeighborData.region, particleNeighborData.cellStrides,
977 particleNeighborData.cellWidth);
978 constexpr size_type numNeighbors = detail::countHypercubes(Dim);
981 const auto neighbors = getCellNeighbors(locCellIndex, particleNeighborData.cellStrides,
982 particleNeighborData.cellPermutationForward);
985 size_type totalParticleInNeighbors = 0;
986 size_type maxParticleInNeighbors = 0;
988 Kokkos::Array<typename hash_type::value_type, numNeighbors> neighborSizes;
989 Kokkos::Array<typename hash_type::value_type, numNeighbors> neighborOffsets;
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;
1000 particle_neighbor_list_type neighborList(
"Neighbor list", totalParticleInNeighbors);
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;
1013 return neighborList;
1016 template <
typename T,
unsigned Dim,
class Mesh,
typename... Properties>
1019 Properties...>::particle_neighbor_list_type
1023 constexpr size_type numNeighbors = detail::countHypercubes(Dim);
1026 const auto locCellIndexFlat = particleNeighborData.cellIndex(particleIndex);
1027 const auto locCellIndex =
1028 toCellIndex(particleNeighborData.cellPermutationBackward(locCellIndexFlat),
1029 particleNeighborData.numCells);
1032 const auto neighbors = getCellNeighbors(locCellIndex, particleNeighborData.cellStrides,
1033 particleNeighborData.cellPermutationForward);
1036 size_type totalParticleInNeighbors = 0;
1037 size_type maxParticleInNeighbors = 0;
1039 Kokkos::Array<size_type, numNeighbors> neighborSizes;
1040 Kokkos::Array<size_type, numNeighbors + 1> neighborOffsets;
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;
1048 neighborOffsets[numNeighbors] = totalParticleInNeighbors;
1051 particle_neighbor_list_type neighborList(
"Neigbor list", totalParticleInNeighbors);
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;
1066 return neighborList;
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);
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;
1083 constexpr auto numCellNeighbors = detail::countHypercubes(Dim);
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) {
1098 const auto cellParticleOffset = cellStartingIdx(cellIdxFlat);
1099 const auto numCellParticles = cellParticleCount(cellIdxFlat);
1102 const auto cellIdx = toCellIndex(cellPermutationBackward(cellIdxFlat), numCells);
1103 const auto cellNeighbors =
1104 getCellNeighbors(cellIdx, cellStrides, cellPermutationForward);
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);
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;
1123 if (neighborIdx == particleIdx) {
1127 f(particleIdx, neighborIdx);
1133 IpplTimings::stopTimer(interactionTimer);
Definition FieldLayout.h:166
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 ®ion, 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 ®ion, 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 ®ion, 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 ParticleSpatialLayout.hpp:38