13#ifndef CD_PARTICLEMANAGEMENTIMPLEM_H
14#define CD_PARTICLEMANAGEMENTIMPLEM_H
32#include <CD_NamespaceHeader.H>
41 const auto replaced = [&a_str](
const std::string& a_replacement) ->
void {
42 const std::string msg =
"ParticleManagement::mergeMethodFromString - '" + a_str +
"' has been replaced by " +
45 MayDay::Abort(msg.c_str());
48 if (a_str ==
"none") {
51 else if (a_str ==
"kd_cell") {
54 else if (a_str ==
"kd_patch") {
57 else if (a_str ==
"kd_amr") {
60 else if (a_str ==
"nn_amr") {
63 else if (a_str ==
"reinitialize") {
66 else if (a_str ==
"external") {
69 else if (a_str ==
"kd_carve") {
70 replaced(
"'kd_amr' with ItoSolver.kd_amr_boundary = carve");
74 else if (a_str ==
"kd_skin_nn") {
75 replaced(
"'kd_amr' with ItoSolver.kd_amr_boundary = nn");
79 else if (a_str ==
"nn_pair_tree") {
80 replaced(
"'nn_amr' with ItoSolver.nn_search = tree");
84 else if (a_str ==
"nn_pair_hash") {
85 replaced(
"'nn_amr' with ItoSolver.nn_search = hash");
89 else if (a_str ==
"nn_pair_onecell") {
90 replaced(
"'nn_amr' with ItoSolver.nn_search = onecell");
94 else if (a_str ==
"nn_sfc") {
98 MayDay::Abort(
"ParticleManagement::mergeMethodFromString - 'nn_sfc' has been removed; use 'kd_cell'");
102 else if (a_str ==
"equal_weight_kd" || a_str ==
"equal_weight_kd_sampled" || a_str ==
"reinitialize_bvh") {
103 replaced(
"'kd_cell' with ItoSolver.kd_partition = weight and ItoSolver.kd_placement = " +
104 std::string(a_str ==
"equal_weight_kd" ?
"centroid"
105 : (a_str ==
"equal_weight_kd_sampled" ?
"sample" :
"random")));
110 MayDay::Abort((
"ParticleManagement::mergeMethodFromString - unknown merge method '" + a_str +
"'").c_str());
119 if (a_str ==
"weight") {
122 else if (a_str ==
"count") {
125 else if (a_str ==
"hybrid") {
128 else if (a_str ==
"weight_capped") {
132 const std::string msg =
"ParticleManagement::kdPartitionFromString - unknown partition '" + a_str +
133 "' (expected 'weight', 'count', 'hybrid' or 'weight_capped')";
134 MayDay::Abort(msg.c_str());
143 if (a_str ==
"centroid") {
146 else if (a_str ==
"sample") {
149 else if (a_str ==
"random") {
153 const std::string msg =
"ParticleManagement::kdPlacementFromString - unknown placement '" + a_str +
154 "' (expected 'centroid', 'sample' or 'random')";
155 MayDay::Abort(msg.c_str());
164 if (a_str ==
"center") {
167 else if (a_str ==
"jitter") {
170 else if (a_str ==
"cell") {
174 const std::string msg =
"ParticleManagement::kdSplitPlacementFromString - unknown split placement '" + a_str +
175 "' (expected 'center', 'jitter' or 'cell')";
176 MayDay::Abort(msg.c_str());
185 if (a_str ==
"carve") {
188 else if (a_str ==
"nn") {
192 const std::string msg =
"ParticleManagement::kdAmrBoundaryFromString - unknown boundary rule '" + a_str +
193 "' (expected 'carve' or 'nn')";
194 MayDay::Abort(msg.c_str());
203 if (a_str ==
"tree") {
206 else if (a_str ==
"hash") {
209 else if (a_str ==
"onecell") {
213 const std::string msg =
"ParticleManagement::nnSearchFromString - unknown search backend '" + a_str +
214 "' (expected 'tree', 'hash' or 'onecell')";
215 MayDay::Abort(msg.c_str());
243 const RealVect& a_parent,
244 const RealVect& a_cellLo,
245 const RealVect& a_dx,
246 const Real a_spacing)
noexcept
254 for (
int dir = 0; dir < SpaceDim; dir++) {
255 const Real cellLo = a_cellLo[dir];
256 const Real cellHi = cellLo + a_dx[dir] * (1.0 - 1.0e-12);
263 lo = std::max(cellLo, a_parent[dir] - 0.5 * a_spacing);
264 hi = std::min(cellHi, a_parent[dir] + 0.5 * a_spacing);
273template <
class P, Real P::*weight, RealVect P::*position>
276 const int a_maxLeaves,
277 const Real a_weightMedianLength,
278 const bool a_capWeights,
279 const std::function<RealVect(
const RealVect&)>& a_splitPosition,
281 std::vector<std::pair<const P*, const P*>>& a_leaves)
noexcept
283 CH_TIME(
"ParticleManagement::buildKDCellLeaves");
297 thread_local std::vector<P> s_cur;
298 thread_local std::vector<P> s_nxt;
299 thread_local std::vector<NodeRange> s_curNodes;
300 thread_local std::vector<NodeRange> s_nxtNodes;
304 const std::size_t numInput = a_particles.size();
305 if (numInput == 0 || a_maxLeaves <= 0) {
309 constexpr Real splitThresh = 2.0 - std::numeric_limits<Real>::min();
310 const std::size_t maxLeaves =
static_cast<std::size_t
>(a_maxLeaves);
316 s_cur.reserve(numInput + 2 * maxLeaves);
317 s_cur.insert(s_cur.end(), a_particles.begin(), a_particles.end());
321 for (
const P& p : s_cur) {
330 if (a_capWeights && a_maxLeaves > 1) {
331 const long long cap = std::max(1LL,
static_cast<long long>(std::ceil(W /
static_cast<Real
>(a_maxLeaves))));
333 const std::size_t numOriginal = s_cur.size();
335 for (std::size_t i = 0; i < numOriginal; i++) {
336 const long long w =
static_cast<long long>(std::llround(s_cur[i].*weight));
340 const long long numPieces = (w + cap - 1) / cap;
341 const std::vector<long long> pieces = partitionParticleWeights<long long>(w, numPieces);
343 if (!pieces.empty()) {
344 const P parent = s_cur[i];
346 s_cur[i].*weight =
static_cast<Real
>(pieces[0]);
348 for (std::size_t k = 1; k < pieces.size(); k++) {
350 daughter.*weight =
static_cast<Real
>(pieces[k]);
351 daughter.*position = a_splitPosition(parent.*position);
354 a_particleReconcile(s_cur[i], daughter, parent);
356 s_cur.push_back(std::move(daughter));
364 s_curNodes.push_back({
static_cast<std::size_t
>(0), s_cur.size(), W});
368 bool keepGoing =
true;
369 while (keepGoing && s_curNodes.size() < maxLeaves) {
378 std::size_t splitBudget = maxLeaves - s_curNodes.size();
380 for (std::size_t ni = 0; ni < s_curNodes.size(); ni++) {
381 const NodeRange node = s_curNodes[ni];
384 P*
const beg = s_cur.data() + node.lo;
385 P*
const end = s_cur.data() + node.hi;
386 const std::size_t n = node.hi - node.lo;
387 const Real Wn = node.w;
392 RealVect loCorner = +std::numeric_limits<Real>::max() * RealVect::Unit;
393 RealVect hiCorner = -std::numeric_limits<Real>::max() * RealVect::Unit;
395 for (P* p = beg; p != end; ++p) {
396 const RealVect& pos = (*p).*position;
398 for (
int dir = 0; dir < SpaceDim; dir++) {
399 loCorner[dir] = std::min(pos[dir], loCorner[dir]);
400 hiCorner[dir] = std::max(pos[dir], hiCorner[dir]);
404 const int splitDir = (hiCorner - loCorner).maxDir(
true);
409 const bool weightMedian = (hiCorner[splitDir] - loCorner[splitDir]) <= a_weightMedianLength;
415 const bool splittable = weightMedian ? (node.w > splitThresh) : (n >= 2);
417 if (splittable && splitBudget > 0) {
420 std::sort(beg, end, [splitDir](
const P& p1,
const P& p2) ->
bool {
421 return (p1.*position)[splitDir] < (p2.*position)[splitDir];
429 const std::size_t mid = n / 2;
432 for (std::size_t i = 0; i < mid; i++) {
433 wl += beg[i].*weight;
436 const std::size_t leftLo = s_nxt.size();
437 s_nxt.insert(s_nxt.end(), beg, beg + mid);
438 s_nxtNodes.push_back({leftLo, s_nxt.size(), wl});
440 const std::size_t rightLo = s_nxt.size();
441 s_nxt.insert(s_nxt.end(), beg + mid, end);
442 s_nxtNodes.push_back({rightLo, s_nxt.size(), Wn - wl});
452 Real wr = Wn - beg[0].*weight;
454 for (std::size_t i = 1; i < n; i++) {
455 const Real& w = beg[id].*weight;
459 wr = Wn - wl - beg[id].*weight;
466 const P med = beg[id];
467 const Real pw = med.*weight;
468 const Real dw = wr - wl;
470 CH_assert(wl + wr + pw == Wn);
473 bool hasExtraL =
false;
474 bool hasExtraR =
false;
478 if (pw >= splitThresh && pw >= std::abs(dw)) {
482 const Real ddw = pw - dw;
483 const long long Nsplit = (
long long)ddw;
486 const long long Nr = Nsplit / 2;
487 const long long Nl = Nsplit - Nr;
489 dwl += (ddw / Nsplit) * Nl;
490 dwr += (ddw / Nsplit) * Nr;
493 if (dwl > 0.0 && dwr > 0.0) {
498 extraL.*position = a_splitPosition(med.*position);
499 extraR.*position = a_splitPosition(med.*position);
501 CH_assert(dwl >= 1.0);
502 CH_assert(dwr >= 1.0);
507 extraL.*weight = dwl;
508 extraR.*weight = dwr;
510 a_particleReconcile(extraL, extraR, med);
515 else if (dwl > 0.0 && dwr == 0.0) {
517 extraL.*position = a_splitPosition(med.*position);
518 CH_assert(dwl >= 1.0);
520 extraL.*weight = dwl;
523 else if (dwl == 0.0 && dwr > 0.0) {
525 extraR.*position = a_splitPosition(med.*position);
526 CH_assert(dwr >= 1.0);
528 extraR.*weight = dwr;
532 MayDay::Abort(
"ParticleManagement::buildKDCellLeaves - logic bust");
549 CH_assert(std::abs(wl + wr - Wn) <= Wn * std::numeric_limits<Real>::epsilon() * 16);
550 CH_assert(std::abs(wl - wr) <= 1.0);
555 const std::size_t leftLo = s_nxt.size();
556 s_nxt.insert(s_nxt.end(), beg, beg +
id);
558 s_nxt.push_back(std::move(extraL));
560 s_nxtNodes.push_back({leftLo, s_nxt.size(), wl});
562 const std::size_t rightLo = s_nxt.size();
563 s_nxt.insert(s_nxt.end(), beg +
id + 1, end);
565 s_nxt.push_back(std::move(extraR));
567 s_nxtNodes.push_back({rightLo, s_nxt.size(), wr});
573 const std::size_t lo = s_nxt.size();
574 s_nxt.insert(s_nxt.end(), s_cur.begin() + node.lo, s_cur.begin() + node.hi);
575 s_nxtNodes.push_back({lo, s_nxt.size(), node.w});
580 s_curNodes.swap(s_nxtNodes);
584 a_leaves.reserve(s_curNodes.size());
585 for (
const NodeRange& r : s_curNodes) {
586 const P*
const base = s_cur.data() + r.lo;
587 a_leaves.emplace_back(base, base + (r.hi - r.lo));
593template <
class Packed, Real Packed::*packWeight, RealVect Packed::*packPosition,
class P,
class Traits>
594inline ParticleMerger<P, Traits>
596 const Real a_weightMedianCellWidths,
597 const bool a_capWeights,
599 std::function<RealVect()> a_probLo,
605 CH_TIMERS(
"ParticleManagement::makeKDCellMerger");
606 CH_TIMER(
"ParticleManagement::makeKDCellMerger::populate", t1);
607 CH_TIMER(
"ParticleManagement::makeKDCellMerger::build_kd", t2);
608 CH_TIMER(
"ParticleManagement::makeKDCellMerger::scatter", t3);
611 thread_local std::vector<Packed> particles;
614 particles.reserve(a_particles.
size());
618 for (std::size_t i = 0; i < a_particles.
size(); i++) {
619 Packed p = a_gather(a_particles, i);
621 particles.emplace_back(std::move(p));
625 if (W < 2.0 || a_ppc <= 0) {
631 const Real dxCell = a_cellInfo.getDx();
632 const RealVect cellLo = a_probLo() + dxCell * RealVect(a_cellInfo.getGridIndex());
633 const bool regular = a_cellInfo.getVolFrac() >= 1.0;
637 const Real spacing = dxCell / std::pow(
static_cast<Real
>(std::max(1, a_ppc)), 1.0 / SpaceDim);
639 const std::function<RealVect(
const RealVect&)> splitPosition = [=](
const RealVect& a_parent) -> RealVect {
648 thread_local std::vector<std::pair<const Packed*, const Packed*>> leaves;
651 detail::buildKDCellLeaves<Packed, packWeight, packPosition>(particles,
653 a_weightMedianCellWidths * a_cellInfo.getDx(),
662 for (
const auto& leaf : leaves) {
663 a_scatterLeaf(a_particles, leaf.first, leaf.second, a_cellInfo);
669template <
class Context,
class P,
class Traits>
670inline ParticleMerger<P, Traits>
673 std::function<RealVect()> a_probLo)
noexcept
676 CH_TIME(
"ParticleManagement::makeReinitializeMerger");
682 const auto [numPhysical, context] = a_aggregate(a_particles);
684 if (numPhysical <= 0LL) {
688 const std::vector<long long> weights = partitionParticleWeights(numPhysical, (
long long)a_ppc);
690 const Real dx = a_cellInfo.getDx();
691 const Real kappa = a_cellInfo.getVolFrac();
692 const RealVect cellPos = a_probLo() + dx * (a_cellInfo.getGridIndex() + 0.5 * RealVect::Unit);
693 const RealVect& validLo = a_cellInfo.getValidLo();
694 const RealVect& validHi = a_cellInfo.getValidHi();
695 const RealVect& bndryCentroid = a_cellInfo.getBndryCentroid();
696 const RealVect& bndryNormal = a_cellInfo.getBndryNormal();
700 for (
const long long wt : weights) {
701 const RealVect x =
Random::randomPosition(cellPos, validLo, validHi, bndryCentroid, bndryNormal, dx, kappa);
702 a_emit(a_particles, x, wt, context);
707template <
typename P,
typename Traits,
typename T,
typename>
711 CH_TIME(
"ParticleManagement::removePhysicalParticles(SoA)");
713 constexpr T zero = (T)0;
715 if (a_numPhysPartToRemove < zero) {
716 MayDay::Error(
"ParticleManagement::removePhysicalParticles(SoA) - 'a_numPhysPartoToRemove < 0'");
719 const std::size_t numComp = a_particles.size();
725 T minWeight = std::numeric_limits<T>::max();
726 for (std::size_t i = 0; i < numComp; i++) {
727 minWeight = std::min(minWeight, (T)a_particles.weight(i));
731 for (std::size_t i = 0; i < numComp; i++) {
732 const T diff1 = (T)a_particles.weight(i) - minWeight;
733 const T diff2 = a_numPhysPartToRemove - numRemoved;
735 CH_assert(diff1 >= zero);
736 CH_assert(diff2 >= zero);
738 const T r = std::max(0LL, std::min(diff1, diff2));
740 a_particles.weight(i) -= 1.0 * r;
745 if (a_numPhysPartToRemove - numRemoved > zero) {
746 const T numCompParticles = (T)numComp;
747 const T uniformWeight = (a_numPhysPartToRemove - numRemoved) / numCompParticles;
748 const T uniformRemainder = (a_numPhysPartToRemove - numRemoved) % numCompParticles;
750 if (uniformWeight > zero) {
753 double*
const w = a_particles.weightColumn();
756 w[i] -= 1.0 * uniformWeight;
759 numRemoved += uniformWeight *
static_cast<T
>(numComp);
762 if (uniformRemainder > zero) {
765 for (std::size_t i = 0; i < numComp; i++) {
768 const T w = std::min((T)a_particles.weight(i), uniformRemainder - W);
770 a_particles.weight(i) -= 1.0 * w;
775 if (W == uniformRemainder) {
782 CH_assert(numRemoved == a_numPhysPartToRemove);
786template <
typename P,
typename Traits>
790 CH_TIME(
"ParticleManagement::deleteParticles(SoA)");
794 while (i < a_particles.size()) {
795 if (a_particles.weight(i) < a_weightThresh) {
796 a_particles.remove(i);
804template <
typename T,
typename>
806partitionParticleWeights(std::vector<T>& a_weights,
const T a_numPhysicalParticles,
const T a_maxCompParticles)
noexcept
812 constexpr T zero = (T)0;
813 constexpr T one = (T)1;
815 if (a_maxCompParticles > zero) {
816 if (a_numPhysicalParticles <= a_maxCompParticles) {
817 a_weights.assign(a_numPhysicalParticles, one);
820 const T W = a_numPhysicalParticles / a_maxCompParticles;
821 T r = a_numPhysicalParticles % a_maxCompParticles;
824 a_weights.assign(a_maxCompParticles, W);
826 for (std::size_t i = 0; i < a_weights.size() && r > zero; i++) {
832 a_weights.assign(1, r);
838template <
typename T,
typename>
840partitionParticleWeights(
const T a_numPhysicalParticles,
const T a_maxCompParticles)
noexcept
844 partitionParticleWeights(ret, a_numPhysicalParticles, a_maxCompParticles);
851template <
typename T,
typename>
853partitionParticles(
const T a_numParticles)
856 const T quotient = a_numParticles / numProc();
857 const T remainder = a_numParticles % numProc();
859 Vector<T> particlesPerRank(numProc(), quotient);
861 for (
int i = 0; i < remainder; i++) {
862 particlesPerRank[i]++;
865 return particlesPerRank[procID()];
867 return a_numParticles;
873template <
typename P,
typename Traits,
typename T,
typename>
876 const T a_numParticles,
877 const std::function<RealVect()>& a_distribution)
883 const T numParticles = detail::partitionParticles(a_numParticles);
885 for (T t = 0; t < numParticles; t++) {
886 a_particles.
append(a_distribution(), 1.0);
890template <
typename P,
typename Traits,
typename T,
typename>
893 const T a_numParticles,
894 const RealVect& a_center,
895 const Real a_radius)
noexcept
897 CH_TIME(
"ParticleManagement::drawGaussianParticles(SoA)");
899 std::normal_distribution<Real> gauss(0.0, a_radius);
901 auto ranGauss = [&]() -> RealVect {
905 drawRandomParticles(a_particles, a_numParticles, ranGauss);
908template <
typename P,
typename Traits,
typename T,
typename>
911 const T a_numParticles,
912 const RealVect& a_loCorner,
913 const RealVect& a_hiCorner)
noexcept
915 CH_TIME(
"ParticleManagement::drawBoxParticles(SoA)");
917 CH_assert(a_hiCorner >= a_loCorner);
919 auto ranBox = [&]() -> RealVect {
925 drawRandomParticles(a_particles, a_numParticles, ranBox);
928template <
typename P,
typename Traits,
typename T,
typename>
931 const T a_numParticles,
932 const RealVect& a_center,
933 const Real a_radius)
noexcept
935 CH_TIME(
"ParticleManagement::drawSphereParticles(SoA)");
937 auto ranSphere = [&]() -> RealVect {
938 RealVect x = std::numeric_limits<Real>::max() * RealVect::Unit;
940 while (x.vectorLength() > a_radius) {
941 for (
int d = 0; d < SpaceDim; d++) {
949 drawRandomParticles(a_particles, a_numParticles, ranSphere);
953#include <CD_NamespaceFooter.H>
Declaration of a namespace for SIMD-decorated loops over SoA particles.
Namespace containing various particle management utilities.
File containing some useful static methods related to random number generation.
Class for the cell-information that is often queried when merging particles inside a cell.
Definition CD_CellInfo.H:26
Arena-backed Struct-of-Arrays particle container for a single grid patch.
Definition CD_ParticleSoA.H:655
void append(const RealVect &a_position, const double a_weight)
Append one particle with a default-constructed payload.
Definition CD_ParticleSoA.H:962
std::size_t size() const noexcept
Number of particles currently stored.
Definition CD_ParticleSoA.H:882
void clear() noexcept
Drop all particles (keeps the arena; invalidates the cell sort unless there was nothing to drop).
Definition CD_ParticleSoA.H:915
static Real get(T &a_distribution)
For getting a random number from a user-supplied distribution. T must be a distribution for which we ...
Definition CD_RandomImplem.H:219
static RealVect getDirection()
Get a random direction in space.
Definition CD_RandomImplem.H:182
static Real getUniformReal11()
Get a uniform real number on the interval [-1,1].
Definition CD_RandomImplem.H:166
static Real getUniformReal01()
Get a uniform real number on the interval [0,1].
Definition CD_RandomImplem.H:158
static RealVect randomPosition(const RealVect &a_lo, const RealVect &a_hi) noexcept
Return a random position in the cube (a_lo, a_hi);.
Definition CD_RandomImplem.H:286
ALWAYS_INLINE void loop(const ParticleSoA< P, Traits > &a_soa, Functor &&a_kernel)
Launch a kernel over every particle in a ParticleSoA, decorating the loop with CD_PRAGMA_SIMD.
Definition CD_ParticleLoops.H:87
RealVect kdSplitDisplace(const KDSplitPlacement a_placement, const RealVect &a_parent, const RealVect &a_cellLo, const RealVect &a_dx, const Real a_spacing) noexcept
Place one piece of a split particle.
Definition CD_ParticleManagementImplem.H:242
void buildKDCellLeaves(const std::vector< P > &a_particles, const int a_maxLeaves, const Real a_weightMedianLength, const bool a_capWeights, const std::function< RealVect(const RealVect &)> &a_splitPosition, const BinaryParticleReconcile< P > &a_particleReconcile, std::vector< std::pair< const P *, const P * > > &a_leaves) noexcept
Build a KD partition of a list of particles and return the leaf particle ranges.
Definition CD_ParticleManagementImplem.H:275
Namespace for various particle management tools.
Definition CD_KDParticleMerge.H:33
KDAmrBoundary kdAmrBoundaryFromString(const std::string &a_str) noexcept
Turn an input string into a kd AMR boundary rule.
Definition CD_ParticleManagementImplem.H:183
KDSplitPlacement kdSplitPlacementFromString(const std::string &a_str) noexcept
Turn an input string into a split placement rule.
Definition CD_ParticleManagementImplem.H:162
KDSplitPlacement
Where the pieces of a split particle go.
Definition CD_ParticleManagement.H:65
@ Cell
Each piece drawn uniformly in the owning cell.
@ Jitter
Each piece drawn from the local mean interparticle spacing around the parent, truncated to the cell.
@ Center
Every piece at the parent's position. No spatial perturbation, duplicate positions.
std::function< void(P &p1, P &p2, const P &p0)> BinaryParticleReconcile
Declaration of a reconciliation function when splitting particles.
Definition CD_ParticleManagement.H:207
ParticleMerger< P, Traits > makeKDCellMerger(const Real a_weightMedianCellWidths, const bool a_capWeights, const KDSplitPlacement a_splitPlacement, std::function< RealVect()> a_probLo, std::function< Packed(const ParticleSoA< P, Traits > &, std::size_t)> a_gather, BinaryParticleReconcile< Packed > a_reconcile, std::function< void(ParticleSoA< P, Traits > &, const Packed *, const Packed *, const CellInfo &)> a_scatterLeaf) noexcept
Create a per-cell KD-tree super-particle merger as a reusable ParticleMerger.
Definition CD_ParticleManagementImplem.H:595
KDPartition
How a kd merge divides a node into two children.
Definition CD_ParticleManagement.H:48
@ WeightCapped
PROTOTYPE. Cap every particle at the target leaf weight, then split at the weight median.
@ Weight
Split at the weight median at every node; halves carry near-equal weight.
@ Count
Split at the count median at every node; halves hold near-equal particle counts.
@ Hybrid
Count median while the node is wider than the crossover length, weight median below it.
KDAmrBoundary
How the AMR-scope kd merge resolves the leaves that touch a patch or rank boundary.
Definition CD_ParticleManagement.H:94
@ Nn
Merge the interior with the kd tree and the boundary skin with nearest-neighbour pairs.
@ Carve
Arbitrate contested particles between patches (z-buffer carve).
KDPartition kdPartitionFromString(const std::string &a_str) noexcept
Turn an input string into a kd partition rule.
Definition CD_ParticleManagementImplem.H:117
ParticleMerger< P, Traits > makeReinitializeMerger(std::function< std::pair< long long, Context >(const ParticleSoA< P, Traits > &)> a_aggregate, std::function< void(ParticleSoA< P, Traits > &, const RealVect &, long long, const Context &)> a_emit, std::function< RealVect()> a_probLo) noexcept
Create a reinitialize super-particle merger as a reusable ParticleMerger.
Definition CD_ParticleManagementImplem.H:671
NNSearch nnSearchFromString(const std::string &a_str) noexcept
Turn an input string into a nearest-neighbour search backend.
Definition CD_ParticleManagementImplem.H:201
NNSearch
How the AMR-scope nearest-neighbour merge finds a particle's merge candidates.
Definition CD_ParticleManagement.H:105
@ Tree
One whole-patch PointCloudBVH per patch.
@ OneCell
One PointCloudBVH per occupied cell; merge distance structurally fixed at 1 cell.
@ Hash
One whole-patch PointCloudHashGrid per patch.
KDPlacement
Where a kd merge puts the particle a leaf reduces to.
Definition CD_ParticleManagement.H:80
@ Random
A uniformly random point in the leaf's bounding box.
@ Centroid
The leaf's weighted centroid.
@ Sample
One of the leaf's own particles, drawn with probability proportional to weight.
ParticleMergeMethod
The super-particle merge methods, named by the scope they group particles over.
Definition CD_ParticleManagement.H:118
@ KdPatch
Patch: mergeKDPatch, one kd tree per patch, patch-local (no ghosts).
@ NnAmr
AMR: nearest-neighbour pair merge over the hierarchy – see NNSearch.
@ KdCell
Cell: makeKDCellMerger, one kd tree per cell.
@ KdAmr
AMR: one kd tree per patch, resolved across patches – see KDAmrBoundary.
@ External
Cell: caller-supplied per-cell merger.
@ Reinitialize
Cell: makeReinitializeMerger; positions discarded and redrawn.
ParticleMergeMethod mergeMethodFromString(const std::string &a_str) noexcept
Map a merge-method selector string to a ParticleMergeMethod.
Definition CD_ParticleManagementImplem.H:37
KDPlacement kdPlacementFromString(const std::string &a_str) noexcept
Turn an input string into a kd placement rule.
Definition CD_ParticleManagementImplem.H:141