476 std::vector<KDLeaf>& a_leaves,
478 const Real a_weightMedianCellWidths,
479 const RealVect& a_dx,
480 const RealVect& a_probLo,
481 const FArrayBox& a_cellCounts)
noexcept
483 CH_TIME(
"ParticleManagement::buildKDQuotaLeaves");
487 if (a_particles.empty() || a_ppc <= 0) {
496 const auto firstUnmergeable = std::stable_partition(a_particles.begin(),
499 const IntVect cell = kdCellKeyOf(a_p.position, a_probLo, a_dx);
503 return a_cellCounts.box().contains(cell)
504 ? static_cast<int>(a_cellCounts(cell, 0)) > a_ppc
508 const std::size_t numMergeable =
static_cast<std::size_t
>(firstUnmergeable - a_particles.begin());
510 for (std::size_t idx = numMergeable; idx < a_particles.size(); idx++) {
511 RealVect boxLo, boxHi;
512 kdBBox(boxLo, boxHi, a_particles, idx, idx + 1);
514 a_leaves.push_back(
KDLeaf{idx, idx + 1, boxLo, boxHi});
517 if (numMergeable == 0) {
535 auto centroidCellOf = [&](
const std::size_t a_lo,
const std::size_t a_hi) -> std::pair<IntVect, Real> {
536 RealVect centroid = RealVect::Zero;
539 for (std::size_t idx = a_lo; idx < a_hi; idx++) {
540 centroid += a_particles[idx].weight * a_particles[idx].position;
541 totalW += a_particles[idx].weight;
548 centroid = a_particles[a_lo].position;
551 return {
kdCellKeyOf(centroid, a_probLo, a_dx), totalW};
569 const auto byWeight = [](
const KDCand& a_lhs,
const KDCand& a_rhs)
noexcept ->
bool {
570 return a_lhs.weight < a_rhs.weight;
576 FArrayBox& used = a_used;
582 std::vector<KDCand> heap;
583 std::vector<KDLeaf> finalLeaves;
585 heap.reserve(numMergeable);
586 finalLeaves.reserve(numMergeable);
589 const auto [rootCell, rootWeight] = centroidCellOf(0, numMergeable);
594 CH_assert(used.box().contains(rootCell));
597 heap.push_back(KDCand{rootWeight, numMergeable, 0, numMergeable, rootCell});
600 while (!heap.empty()) {
601 std::pop_heap(heap.begin(), heap.end(), byWeight);
603 const KDCand node = heap.back();
606 if (node.count < 2) {
607 RealVect boxLo, boxHi;
608 kdBBox(boxLo, boxHi, a_particles, node.lo, node.hi);
610 finalLeaves.push_back(
KDLeaf{node.lo, node.hi, boxLo, boxHi});
619 RealVect nodeBoxLo, nodeBoxHi;
620 kdBBox(nodeBoxLo, nodeBoxHi, a_particles, node.lo, node.hi);
622 const Real nodeSpan =
kdMaxAxisSpan(nodeBoxLo, nodeBoxHi, a_dx);
623 const int splitAxis = (nodeBoxHi - nodeBoxLo).maxDir(
true);
629 const std::size_t mid = (nodeSpan <= a_weightMedianCellWidths)
633 const auto [cellLeft, weightLeft] = centroidCellOf(node.lo, mid);
634 const auto [cellRight, weightRight] = centroidCellOf(mid, node.hi);
636 CH_assert(used.box().contains(cellLeft));
637 CH_assert(used.box().contains(cellRight));
639 used(node.cell, 0)--;
641 used(cellRight, 0)++;
647 if (nodeSpan <= s_kdMaxLeafExtent && (used(cellLeft, 0) > a_ppc || used(cellRight, 0) > a_ppc)) {
649 used(cellRight, 0)--;
650 used(node.cell, 0)++;
652 finalLeaves.push_back(
KDLeaf{node.lo, node.hi, nodeBoxLo, nodeBoxHi});
657 heap.push_back(KDCand{weightLeft, mid - node.lo, node.lo, mid, cellLeft});
658 std::push_heap(heap.begin(), heap.end(), byWeight);
660 heap.push_back(KDCand{weightRight, node.hi - mid, mid, node.hi, cellRight});
661 std::push_heap(heap.begin(), heap.end(), byWeight);
664 for (
const KDLeaf& leaf : finalLeaves) {
665 a_leaves.push_back(leaf);
672 std::size_t coveredMembers = 0;
674 for (
const KDLeaf& bl : a_leaves) {
675 CH_assert(bl.hi > bl.lo);
676 CH_assert(bl.hi <= a_particles.size());
678 coveredMembers += bl.hi - bl.lo;
681 CH_assert(coveredMembers == a_particles.size());
833 EBAMRFAB& a_cellHistogram,
834 EBAMRFAB& a_leafQuota,
837 const Real a_weightMedianCellWidths,
839 const bool a_capWeights,
841 const Gather& a_gather,
842 const Combine& a_combine,
843 const Scatter& a_scatter,
844 const Allocator& a_allocateID,
845 const PosValid& a_isPositionValid,
846 const PatchRegular& a_isPatchRegular)
848 using namespace detail;
861 operator<(
const KDBoxKey& a_rhs)
const noexcept
863 return (volume != a_rhs.volume) ? (volume < a_rhs.volume) : (anchor < a_rhs.anchor);
898 CH_TIMERS(
"ParticleManagement::mergeKDCarve");
899 CH_TIMER(
"ParticleManagement::mergeKDCarve::build_classify", t_build);
900 CH_TIMER(
"ParticleManagement::mergeKDCarve::carve_exchange", t_carve);
901 CH_TIMER(
"ParticleManagement::mergeKDCarve::remove_consumed", t_remove);
902 CH_TIMER(
"ParticleManagement::mergeKDCarve::place_results", t_place);
904 const std::string realm = a_particles.
getRealm();
906 const RealVect probLo = a_amr.
getProbLo();
913 EBAMRFAB& histogram = a_cellHistogram;
914 EBAMRFAB& leafQuota = a_leafQuota;
916 CH_assert(histogram[0]->nComp() == 1);
917 CH_assert(leafQuota[0]->nComp() == 1);
918 CH_assert(histogram[0]->ghostVect() >= IntVect::Unit);
919 CH_assert(leafQuota[0]->ghostVect() >= IntVect::Unit);
921 const int myRank = procID();
922 const int numRanks = numProc();
937 std::vector<KDMember> members;
957 struct SelfClaimEntry
964 std::vector<PatchWork> patchWork;
979 std::vector<MergeParticle<Packed>> gatheredParticles;
980 std::vector<std::pair<ParticleID, std::size_t>> particlesByID;
981 std::vector<RuntimeBox> myBoxes;
982 std::vector<ParticleID> consumedIDs;
983 std::vector<MergedResult> mergedResults;
994 std::vector<SelfClaimEntry> selfClaims;
999 std::size_t combinedCountUpperBound = 0;
1001 for (
int lvl = 0; lvl <= finestLevel; lvl++) {
1002 const DisjointBoxLayout& dbl = a_amr.
getGrids(realm)[lvl];
1003 const DataIterator& dit = dbl.dataIterator();
1004 const int nbox = dit.size();
1006#pragma omp parallel for schedule(runtime) reduction(+ : combinedCountUpperBound)
1007 for (
int mybox = 0; mybox < nbox; mybox++) {
1008 combinedCountUpperBound += a_particles[lvl][dit[mybox]].size();
1012 gatheredParticles.reserve(combinedCountUpperBound);
1013 particlesByID.reserve(combinedCountUpperBound);
1014 selfClaims.reserve(combinedCountUpperBound);
1015 consumedIDs.reserve(combinedCountUpperBound);
1021 std::vector<KDLeaf> leaves;
1022 std::vector<KDMember> members;
1028 std::vector<Packed> payloads;
1029 std::vector<Real> weights;
1030 std::vector<RealVect> positions;
1043 for (
int lvl = 0; lvl <= finestLevel; lvl++) {
1044 const DisjointBoxLayout& dbl = a_amr.
getGrids(realm)[lvl];
1045 const DataIterator& dit = dbl.dataIterator();
1047 const RealVect dx = a_amr.
getDx()[lvl] * RealVect::Unit;
1049 const int nbox = dit.size();
1055 for (
int mybox = 0; mybox < nbox; mybox++) {
1056 const DataIndex& din = dit[mybox];
1064 const bool patchRegular = a_isPatchRegular(lvl, din);
1066 const auto positionValid = [&](
const RealVect& a_pos) ->
bool {
1067 return patchRegular || a_isPositionValid(a_pos);
1070 const BaseFab<bool>& exposureDin = (*exposure[lvl])[din];
1072 std::vector<MergeParticle<Packed>> combined;
1073 combined.reserve(leaf.
size());
1075 for (std::size_t i = 0; i < leaf.
size(); i++) {
1083 p.
payload = a_gather(leaf, i);
1085 combined.push_back(p);
1091 gatheredParticles.push_back(p);
1092 particlesByID.emplace_back(p.
globalID, gatheredParticles.size() - 1);
1095 patchWork.push_back(PatchWork{lvl, din, dx, dbl[din]});
1096 const int patchIdx =
static_cast<int>(patchWork.size()) - 1;
1098 if (combined.empty()) {
1106 FArrayBox& cellCounts = (*histogram[lvl])[din];
1107 FArrayBox& quota = (*leafQuota[lvl])[din];
1109 kdFillCellHistogram(cellCounts, combined, probLo, dx);
1115 FArrayBox cellWeights(cellCounts.box(), 1);
1116 cellWeights.setVal(0.0);
1119 const IntVect cell = kdCellKeyOf(p.position, probLo, dx);
1121 if (cellWeights.box().contains(cell)) {
1122 cellWeights(cell, 0) += p.weight;
1126 kdCapCellWeights(combined, cellCounts, cellWeights, a_ppc, a_splitPlacement, probLo, dx, positionValid);
1129 buildKDQuotaLeaves(combined, quota, leaves, a_ppc, a_weightMedianCellWidths, dx, probLo, cellCounts);
1131 for (
const KDLeaf& bl : leaves) {
1133 members.reserve(bl.hi - bl.lo);
1135 bool anyLocal =
false;
1139 ParticleID anchor = combined[bl.lo].globalID;
1141 for (std::size_t idx = bl.lo; idx < bl.hi; idx++) {
1142 members.push_back(KDMember{combined[idx].globalID, combined[idx].ownerRank});
1143 anchor = std::min(anchor, combined[idx].globalID);
1148 if (!combined[idx].isGhost) {
1158 Real leafVolume = 1.0;
1160 for (
int dir = 0; dir < SpaceDim; dir++) {
1161 leafVolume *= (bl.boxHi[dir] - bl.boxLo[dir]);
1168 const bool unmergeable = kdMaxAxisSpan(bl.boxLo, bl.boxHi, dx) > s_kdMaxLeafExtent;
1177 std::vector<bool> exposed(bl.hi - bl.lo,
false);
1178 bool anyExposed =
false;
1179 bool anyGhost =
false;
1181 for (std::size_t idx = bl.lo; idx < bl.hi; idx++) {
1182 if (combined[idx].isGhost) {
1188 const IntVect cell = kdCellKeyOf(combined[idx].position, probLo, dx);
1190 CH_assert(exposureDin.box().contains(cell));
1192 const bool isExposed = exposureDin(cell, 0);
1194 exposed[idx - bl.lo] = isExposed;
1195 anyExposed = anyExposed || isExposed;
1199 for (std::size_t idx = bl.lo; idx < bl.hi; idx++) {
1203 if (!combined[idx].isGhost && exposed[idx - bl.lo]) {
1205 selfClaims.push_back(SelfClaimEntry{combined[idx].globalID, KDBoxKey{}, -1});
1212 if (!anyExposed && !anyGhost) {
1228 if (bl.hi - bl.lo >= 2) {
1229 RealVect centroid = RealVect::Zero;
1236 payloads.reserve(bl.hi - bl.lo);
1237 weights.reserve(bl.hi - bl.lo);
1239 if (needPositions) {
1240 positions.reserve(bl.hi - bl.lo);
1243 for (std::size_t idx = bl.lo; idx < bl.hi; idx++) {
1248 payloads.push_back(p.
payload);
1249 weights.push_back(p.
weight);
1251 if (needPositions) {
1261 kdCheckCentroid(centroid, totalW, bl.boxLo, bl.boxHi);
1272 placed = kdPlaceMerged<Packed>(a_placement, centroid, totalW, bl.boxLo, bl.boxHi, weights, positions);
1274 bool placedValid = positionValid(placed);
1276 if (!placedValid && placed != centroid) {
1278 placedValid = positionValid(placed);
1288 merged.
payload = a_combine(payloads.data(), weights.data(), payloads.size());
1290 mergedResults.push_back(MergedResult{merged, patchIdx});
1292 for (std::size_t idx = bl.lo; idx < bl.hi; idx++) {
1293 consumedIDs.push_back(combined[idx].globalID);
1302 if (members.size() >= 2) {
1303 const KDBoxKey key{leafVolume, anchor};
1305 const int boxIdx =
static_cast<int>(myBoxes.size());
1306 myBoxes.push_back(RuntimeBox{key, patchIdx, members});
1308 for (
const KDMember& m : members) {
1309 if (m.owner == myRank) {
1313 selfClaims.push_back(SelfClaimEntry{m.id, key, boxIdx});
1320 selfClaims.push_back(SelfClaimEntry{members[0].id, KDBoxKey{}, -1});
1332 std::sort(particlesByID.begin(), particlesByID.end(), [](
const auto& a_lhs,
const auto& a_rhs) {
1333 return a_lhs.first < a_rhs.first;
1336 std::size_t writeIdx = 0;
1338 for (std::size_t readIdx = 0; readIdx < particlesByID.size();) {
1339 std::size_t runEnd = readIdx + 1;
1341 while (runEnd < particlesByID.size() && particlesByID[runEnd].first == particlesByID[readIdx].first) {
1345 std::size_t chosen = readIdx;
1346 for (std::size_t k = readIdx; k < runEnd; k++) {
1347 if (!gatheredParticles[particlesByID[k].second].isGhost) {
1354 if (writeIdx != chosen) {
1355 particlesByID[writeIdx] = particlesByID[chosen];
1361 particlesByID.resize(writeIdx);
1367 const auto it = std::lower_bound(particlesByID.begin(),
1368 particlesByID.end(),
1370 [](
const std::pair<ParticleID, std::size_t>& a_entry,
const ParticleID a_key) {
1371 return a_entry.first < a_key;
1374 CH_assert(it != particlesByID.end() && it->first == a_id);
1376 return gatheredParticles[it->second];
1382 std::sort(selfClaims.begin(), selfClaims.end(), [](
const SelfClaimEntry& a_lhs,
const SelfClaimEntry& a_rhs) {
1383 return a_lhs.id < a_rhs.id;
1390 std::vector<std::vector<KDClaim>> claimSendByRank(numRanks);
1392 for (std::size_t boxIdx = 0; boxIdx < myBoxes.size(); boxIdx++) {
1393 const RuntimeBox& box = myBoxes[boxIdx];
1395 for (
const KDMember& m : box.members) {
1396 if (m.owner != myRank) {
1397 claimSendByRank[m.owner].push_back(KDClaim{m.id, box.key, myRank,
static_cast<int>(boxIdx)});
1404 std::vector<KDClaim> incomingClaims = kdExchangeByRank(claimSendByRank);
1405 std::sort(incomingClaims.begin(), incomingClaims.end(), [](
const KDClaim& a_lhs,
const KDClaim& a_rhs) {
1406 return a_lhs.memberID < a_rhs.memberID;
1409 auto claimsRange = [&incomingClaims](
const ParticleID a_id) {
1410 const auto lo = std::lower_bound(incomingClaims.begin(),
1411 incomingClaims.end(),
1413 [](
const KDClaim& a_c,
const ParticleID a_key) {
1414 return a_c.memberID < a_key;
1416 const auto hi = std::upper_bound(incomingClaims.begin(),
1417 incomingClaims.end(),
1419 [](
const ParticleID a_key,
const KDClaim& a_c) {
1420 return a_key < a_c.memberID;
1423 return std::make_pair(lo, hi);
1451 std::vector<WinnerEntry> nominalWinner;
1452 nominalWinner.reserve(selfClaims.size());
1454 for (std::size_t idx = 0; idx < selfClaims.size();) {
1457 std::size_t idxEnd = idx + 1;
1459 while (idxEnd < selfClaims.size() && selfClaims[idxEnd].id ==
id) {
1463 bool haveWinner =
false;
1465 RankID bestRank = myRank;
1466 int bestBoxIdx = -1;
1468 for (std::size_t k = idx; k < idxEnd; k++) {
1469 if (selfClaims[k].boxIdx < 0) {
1473 if (!haveWinner || selfClaims[k].key < bestKey) {
1475 bestKey = selfClaims[k].key;
1477 bestBoxIdx = selfClaims[k].boxIdx;
1481 const auto range = claimsRange(
id);
1483 for (
auto cit = range.first; cit != range.second; ++cit) {
1484 const KDClaim& c = *cit;
1486 if (!haveWinner || c.key < bestKey) {
1489 bestRank = c.proposerRank;
1495 bestBoxIdx = c.proposerBoxIdx;
1500 nominalWinner.push_back(WinnerEntry{id, Winner{bestRank, bestBoxIdx, bestKey}});
1508 auto findWinner = [&nominalWinner](
const ParticleID a_id) ->
const Winner* {
1509 const auto it = std::lower_bound(nominalWinner.begin(),
1510 nominalWinner.end(),
1512 [](
const WinnerEntry& a_entry,
const ParticleID a_key) {
1513 return a_entry.id < a_key;
1516 return (it != nominalWinner.end() && it->id == a_id) ? &it->winner :
nullptr;
1524 std::vector<std::vector<KDVerdict>> verdictSendByRank(numRanks);
1526 for (std::size_t idx = 0; idx < incomingClaims.size();) {
1527 const ParticleID id = incomingClaims[idx].memberID;
1529 std::size_t idxEnd = idx + 1;
1531 while (idxEnd < incomingClaims.size() && incomingClaims[idxEnd].memberID ==
id) {
1535 const Winner* winner = findWinner(
id);
1542 CH_assert(winner !=
nullptr);
1544 for (std::size_t k = idx; k < idxEnd; k++) {
1545 const KDClaim& c = incomingClaims[k];
1554 const bool won = (c.proposerRank == winner->rank) && (c.proposerBoxIdx == winner->boxIdx);
1556 verdictSendByRank[c.proposerRank].push_back(KDVerdict{id, c.proposerBoxIdx, won});
1562 const std::vector<KDVerdict> incomingVerdicts = kdExchangeByRank(verdictSendByRank);
1571 std::vector<std::vector<ParticleID>> wonForeignByBox(myBoxes.size());
1573 for (
const KDVerdict& v : incomingVerdicts) {
1575 wonForeignByBox[v.claimantBoxIdx].push_back(v.memberID);
1579 for (std::vector<ParticleID>& won : wonForeignByBox) {
1580 std::sort(won.begin(), won.end());
1584 std::vector<std::vector<KDCommit>> commitSendByRank(numRanks);
1586 for (std::size_t boxIdx = 0; boxIdx < myBoxes.size(); boxIdx++) {
1587 const RuntimeBox& box = myBoxes[boxIdx];
1589 std::vector<ParticleID> survivingLocal;
1590 std::vector<ParticleID> survivingForeign;
1592 for (
const KDMember& m : box.members) {
1593 if (m.owner == myRank) {
1594 const Winner* winner = findWinner(m.id);
1596 if (winner !=
nullptr && winner->rank == myRank && winner->boxIdx ==
static_cast<int>(boxIdx)) {
1597 survivingLocal.push_back(m.id);
1601 const std::vector<ParticleID>& won = wonForeignByBox[boxIdx];
1603 if (std::binary_search(won.begin(), won.end(), m.id)) {
1604 survivingForeign.push_back(m.id);
1609 const std::size_t totalSurvivors = survivingLocal.size() + survivingForeign.size();
1611 bool committed =
false;
1613 if (totalSurvivors >= 2) {
1614 RealVect centroid = RealVect::Zero;
1616 RealVect surviveBoxLo, surviveBoxHi;
1617 bool haveSurviveBox =
false;
1623 payloads.reserve(totalSurvivors);
1624 weights.reserve(totalSurvivors);
1626 if (needPositions) {
1627 positions.reserve(totalSurvivors);
1630 auto accumulate = [&](
const ParticleID a_id) {
1633 payloads.push_back(p.
payload);
1634 weights.push_back(p.
weight);
1636 if (needPositions) {
1643 if (!haveSurviveBox) {
1646 haveSurviveBox =
true;
1649 for (
int dir = 0; dir < SpaceDim; dir++) {
1650 surviveBoxLo[dir] = std::min(surviveBoxLo[dir], p.
position[dir]);
1651 surviveBoxHi[dir] = std::max(surviveBoxHi[dir], p.
position[dir]);
1660 for (
const ParticleID id : survivingForeign) {
1666 kdCheckCentroid(centroid, totalW, surviveBoxLo, surviveBoxHi);
1672 placed = kdPlaceMerged<Packed>(a_placement, centroid, totalW, surviveBoxLo, surviveBoxHi, weights, positions);
1678 const PatchWork& boxPatch = patchWork[box.patchIdx];
1680 bool placedValid = a_isPatchRegular(boxPatch.level, boxPatch.din) || a_isPositionValid(placed);
1682 if (!placedValid && placed != centroid) {
1684 placedValid = a_isPositionValid(placed);
1696 merged.
payload = a_combine(payloads.data(), weights.data(), payloads.size());
1698 mergedResults.push_back(MergedResult{merged, box.patchIdx});
1702 consumedIDs.push_back(
id);
1725 for (
const ParticleID id : survivingForeign) {
1726 commitSendByRank[findParticle(
id).ownerRank].push_back(KDCommit{id, committed});
1731 const std::vector<KDCommit> incomingCommits = kdExchangeByRank(commitSendByRank);
1733 for (
const KDCommit& c : incomingCommits) {
1735 consumedIDs.push_back(c.memberID);
1739 std::sort(consumedIDs.begin(), consumedIDs.end());
1742 CH_assert(std::is_sorted(consumedIDs.begin(), consumedIDs.end()));
1748 for (
const PatchWork& pw : patchWork) {
1753 while (i < leaf.
size()) {
1754 if (!leaf.
isGhost(i) && std::binary_search(consumedIDs.begin(), consumedIDs.end(), leaf.
particleID(i))) {
1769 std::vector<std::vector<MergeParticle<Packed>>> scatterByDestRank(numRanks);
1771 auto insertHere = [&](
const MergeParticle<Packed>& a_p,
const int a_level,
const DataIndex& a_din) {
1774 a_scatter(leaf, a_p);
1777 for (
const MergedResult& mr : mergedResults) {
1778 const PatchWork& pw = patchWork[mr.patchIdx];
1780 RealVect boxRealLo, boxRealHi;
1782 kdBoxRealBounds(boxRealLo, boxRealHi, pw.box, pw.dx, probLo);
1784 bool inOwnBox =
true;
1786 for (
int dir = 0; dir < SpaceDim && inOwnBox; dir++) {
1787 if (mr.particle.position[dir] < boxRealLo[dir] || mr.particle.position[dir] >= boxRealHi[dir]) {
1793 insertHere(mr.particle, pw.level, pw.din);
1801 MayDay::Error(
"ParticleManagement::mergeKDCarve -- merged particle not found in any patch");
1804 if (dst.rank == myRank) {
1805 const DataIndex din = a_amr.
getLevelTiles(realm)[dst.level]->getMyGrids().at(dst.gridIndex);
1807 insertHere(mr.particle, dst.level, din);
1813 scatterByDestRank[dst.rank].push_back(corrected);
1817 const std::vector<MergeParticle<Packed>> incomingScattered = kdExchangeByRank(scatterByDestRank);
1822 if (!dst.valid || dst.rank != myRank) {
1823 MayDay::Error(
"ParticleManagement::mergeKDCarve -- incoming scattered particle not "
1824 "found in any of this rank's own patches");
1827 const DataIndex din = a_amr.
getLevelTiles(realm)[dst.level]->getMyGrids().at(dst.gridIndex);
1832 insertHere(corrected, dst.
level, din);
1902 EBAMRFAB& a_cellHistogram,
1903 EBAMRFAB& a_leafQuota,
1906 const Real a_weightMedianCellWidths,
1908 const bool a_capWeights,
1910 const Gather& a_gather,
1911 const Combine& a_combine,
1912 const Scatter& a_scatter,
1913 const Allocator& a_allocateID,
1914 const PosValid& a_isPositionValid,
1915 const PatchRegular& a_isPatchRegular)
1917 using namespace detail;
1919 CH_TIMERS(
"ParticleManagement::mergeKDInterior");
1920 CH_TIMER(
"ParticleManagement::mergeKDInterior::build", t_build);
1921 CH_TIMER(
"ParticleManagement::mergeKDInterior::commit", t_commit);
1922 CH_TIMER(
"ParticleManagement::mergeKDInterior::remove_place", t_place);
1924 const std::string realm = a_particles.
getRealm();
1926 const RealVect probLo = a_amr.
getProbLo();
1930 CH_assert(a_interior.
getRealm() == realm);
1933 EBAMRFAB& histogram = a_cellHistogram;
1934 EBAMRFAB& leafQuota = a_leafQuota;
1936 CH_assert(histogram[0]->nComp() == 1);
1937 CH_assert(leafQuota[0]->nComp() == 1);
1938 CH_assert(histogram[0]->ghostVect() >= IntVect::Unit);
1939 CH_assert(leafQuota[0]->ghostVect() >= IntVect::Unit);
1941 const int myRank = procID();
1947 std::vector<KDLeaf> leaves;
1952 std::vector<Packed> payloads;
1953 std::vector<Real> weights;
1954 std::vector<RealVect> positions;
1960 for (
int lvl = 0; lvl <= finestLevel; lvl++) {
1961 const DisjointBoxLayout& dbl = a_amr.
getGrids(realm)[lvl];
1962 const DataIterator& dit = dbl.dataIterator();
1964 const RealVect dx = a_amr.
getDx()[lvl] * RealVect::Unit;
1966 const int nbox = dit.size();
1970 for (
int mybox = 0; mybox < nbox; mybox++) {
1971 const DataIndex& din = dit[mybox];
1979 const bool patchRegular = a_isPatchRegular(lvl, din);
1981 const auto positionValid = [&](
const RealVect& a_pos) ->
bool {
1982 return patchRegular || a_isPositionValid(a_pos);
1988 std::vector<MergeParticle<Packed>> combined;
1989 combined.reserve(leaf.
size());
1991 for (std::size_t i = 0; i < leaf.
size(); i++) {
1999 p.
payload = a_gather(leaf, i);
2001 combined.push_back(p);
2004 if (combined.empty()) {
2014 FArrayBox& cellCounts = (*histogram[lvl])[din];
2015 FArrayBox& quota = (*leafQuota[lvl])[din];
2017 kdFillCellHistogram(cellCounts, combined, probLo, dx);
2023 FArrayBox cellWeights(cellCounts.box(), 1);
2024 cellWeights.setVal(0.0);
2027 const IntVect cell = kdCellKeyOf(p.position, probLo, dx);
2029 if (cellWeights.box().contains(cell)) {
2030 cellWeights(cell, 0) += p.weight;
2034 kdCapCellWeights(combined, cellCounts, cellWeights, a_ppc, a_splitPlacement, probLo, dx, positionValid);
2037 buildKDQuotaLeaves(combined, quota, leaves, a_ppc, a_weightMedianCellWidths, dx, probLo, cellCounts);
2042 std::vector<ParticleID> consumedIDs;
2043 std::vector<MergeParticle<Packed>> mergedResults;
2045 for (
const KDLeaf& bl : leaves) {
2046 if (bl.hi - bl.lo < 2) {
2051 if (kdMaxAxisSpan(bl.boxLo, bl.boxHi, dx) > s_kdMaxLeafExtent) {
2062 bool anyGhost =
false;
2064 for (std::size_t idx = bl.lo; idx < bl.hi && !anyGhost; idx++) {
2065 anyGhost = combined[idx].isGhost;
2072 RealVect centroid = RealVect::Zero;
2079 payloads.reserve(bl.hi - bl.lo);
2080 weights.reserve(bl.hi - bl.lo);
2082 if (needPositions) {
2083 positions.reserve(bl.hi - bl.lo);
2086 for (std::size_t idx = bl.lo; idx < bl.hi; idx++) {
2093 payloads.push_back(p.
payload);
2094 weights.push_back(p.
weight);
2096 if (needPositions) {
2106 kdCheckCentroid(centroid, totalW, bl.boxLo, bl.boxHi);
2113 RealVect placed = kdPlaceMerged<Packed>(a_placement, centroid, totalW, bl.boxLo, bl.boxHi, weights, positions);
2115 if (!positionValid(placed)) {
2116 if (placed == centroid) {
2122 if (!positionValid(placed)) {
2134 merged.
payload = a_combine(payloads.data(), weights.data(), payloads.size());
2136 mergedResults.push_back(merged);
2138 for (std::size_t idx = bl.lo; idx < bl.hi; idx++) {
2139 consumedIDs.push_back(combined[idx].globalID);
2145 if (mergedResults.empty()) {
2153 std::sort(consumedIDs.begin(), consumedIDs.end());
2156 CH_assert(std::is_sorted(consumedIDs.begin(), consumedIDs.end()));
2161 while (i < leaf.
size()) {
2162 if (!leaf.
isGhost(i) && std::binary_search(consumedIDs.begin(), consumedIDs.end(), leaf.
particleID(i))) {
2171 RealVect boxRealLo, boxRealHi;
2173 kdBoxRealBounds(boxRealLo, boxRealHi, dbl[din], dx, probLo);
2176 bool inOwnBox =
true;
2178 for (
int dir = 0; dir < SpaceDim && inOwnBox; dir++) {
2179 if (mr.position[dir] < boxRealLo[dir] || mr.position[dir] >= boxRealHi[dir]) {
2185 a_scatter(a_interior[lvl][din], mr);
2195 if (!dst.valid || dst.rank != myRank) {
2196 MayDay::Error(
"ParticleManagement::mergeKDInterior -- merged particle left this rank's own patches");
2199 const DataIndex dstDin = a_amr.
getLevelTiles(realm)[dst.level]->getMyGrids().at(dst.gridIndex);
2201 a_scatter(a_interior[dst.level][dstDin], mr);