chombo-discharge
Loading...
Searching...
No Matches
Namespaces | Classes | Typedefs | Enumerations | Functions
ParticleManagement Namespace Reference

Namespace for various particle management tools. More...

Namespaces

namespace  detail
 Internal-only implementation details of the ParticleManagement API.
 

Classes

struct  MergeParticle
 Minimal, payload-agnostic description of one particle as input to a distributed merge. More...
 
class  NNCellBudget
 The per-cell particle count this merge drains each cell down to. More...
 

Typedefs

template<typename Packed >
using KDPayloadCombine = std::function< Packed(const Packed *a_payloads, const Real *a_weights, const std::size_t a_count)>
 Illustrative signature of the N-ary combine callback: produce a merged particle's opaque payload from an arbitrary-size group of parents' payloads and weights.
 
template<typename Packed >
using PayloadCombine = std::function< Packed(const Packed &a_first, const Real a_firstWeight, const Packed &a_second, const Real a_secondWeight)>
 Illustrative signature of the combine callback: produce a merged particle's opaque payload from its two parents' payloads and weights.
 
using PositionValid = std::function< bool(const RealVect &a_mergedPosition)>
 Illustrative signature of the position-validity predicate for a would-be merged particle.
 
using IDAllocator = std::function< ParticleID()>
 Illustrative signature of the fresh-id allocator: produce one globally-unique id for a newly created merged particle.
 
template<typename Packed >
using MergeCommittedCallback = std::function< void(const MergeParticle< Packed > &a_first, const MergeParticle< Packed > &a_second, const MergeParticle< Packed > &a_merged)>
 Optional per-merge diagnostic callback: observe every individual merge as it commits.
 
template<class P , class Traits = ParticleTraits<P>>
using ParticleMerger = std::function< void(ParticleSoA< P, Traits > &a_particles, const CellInfo &a_cellInfo, const int a_numTargetParticles)>
 Concept for splitting/merging particles.
 
template<class P >
using BinaryParticleReconcile = std::function< void(P &p1, P &p2, const P &p0)>
 Declaration of a reconciliation function when splitting particles.
 

Enumerations

enum class  ParticleMergeMethod {
  None , EqualWeightKD , Reinitialize , ReinitializeBVH ,
  NnSfc , NnPairTree , NnPairOneCell , NnPairHash ,
  KdCarve , KdPatch , KdSkinNn , External
}
 The specific super-particle merge methods. More...
 

Functions

template<typename P , typename Packed , typename Traits = ParticleTraits<P>, typename Gather , typename Combine , typename Scatter , typename Allocator , typename PosValid >
void mergeKDCarve (ParticleContainer< P, Traits > &a_particles, EBAMRFAB &a_cellHistogram, EBAMRFAB &a_leafQuota, const AmrMesh &a_amr, const int a_ppc, const Real a_splitWeightLeafDx, const Gather &a_gather, const Combine &a_combine, const Scatter &a_scatter, const Allocator &a_allocateID, const PosValid &a_isPositionValid)
 Run one non-iterative pass of the kd-tree carve merge over every patch this rank owns.
 
template<typename P , typename Packed , typename Traits = ParticleTraits<P>, typename Gather , typename Combine , typename Scatter , typename Allocator , typename PosValid >
void mergeKDPatch (ParticleContainer< P, Traits > &a_particles, EBAMRFAB &a_cellHistogram, EBAMRFAB &a_leafQuota, const AmrMesh &a_amr, const int a_ppc, const Real a_splitWeightLeafDx, const Gather &a_gather, const Combine &a_combine, const Scatter &a_scatter, const Allocator &a_allocateID, const PosValid &a_isPositionValid)
 Run one patch-local kd-tree merge over every patch this rank owns.
 
template<typename P , typename Packed , typename Traits = ParticleTraits<P>, typename Gather , typename Combine , typename Scatter , typename Allocator , typename PosValid >
void mergeKDInterior (ParticleContainer< P, Traits > &a_particles, ParticleContainer< P, Traits > &a_interior, EBAMRFAB &a_cellHistogram, EBAMRFAB &a_leafQuota, const AmrMesh &a_amr, const int a_ppc, const Real a_splitWeightLeafDx, const Gather &a_gather, const Combine &a_combine, const Scatter &a_scatter, const Allocator &a_allocateID, const PosValid &a_isPositionValid)
 Run the uncontested tier of the kd merge, splitting the input into merged and leftover.
 
template<typename P , typename Packed , typename Traits = ParticleTraits<P>, typename Gather , typename Combine , typename Scatter , typename Allocator , typename PosValid = std::function<bool(const RealVect&)>, typename OnMerge = std::function< void(const MergeParticle<Packed>&, const MergeParticle<Packed>&, const MergeParticle<Packed>&)>>
void mergeNearestNeighborsTree (ParticleContainer< P, Traits > &a_particles, const AmrMesh &a_amr, const NNCellBudget &a_cellBudget, const Gather &a_gather, const Combine &a_combine, const Scatter &a_scatter, const Allocator &a_allocateID, const bool a_iterateLocalTierToConvergence=false, const int a_maxFallbackCandidates=0, const std::optional< int > a_maxCellDistance=std::nullopt, const int a_ghostWidth=1, const PosValid &a_isPositionValid=[](const RealVect &) { return true;}, unsigned long long *a_crossLevelMergeCount=nullptr, const OnMerge &a_onMergeCommitted=[](const MergeParticle< Packed > &, const MergeParticle< Packed > &, const MergeParticle< Packed > &) { })
 Run one full round (one timestep's worth) of the nearest-neighbor merge algorithm.
 
template<typename P , typename Packed , typename Traits = ParticleTraits<P>, typename Gather , typename Combine , typename Scatter , typename Allocator , typename PosValid = std::function<bool(const RealVect&)>, typename OnMerge = std::function< void(const MergeParticle<Packed>&, const MergeParticle<Packed>&, const MergeParticle<Packed>&)>>
void mergeNearestNeighborsHash (ParticleContainer< P, Traits > &a_particles, const AmrMesh &a_amr, const NNCellBudget &a_cellBudget, const Gather &a_gather, const Combine &a_combine, const Scatter &a_scatter, const Allocator &a_allocateID, const bool a_iterateLocalTierToConvergence=false, const int a_maxFallbackCandidates=0, const std::optional< int > a_maxCellDistance=std::nullopt, const int a_ghostWidth=1, const PosValid &a_isPositionValid=[](const RealVect &) { return true;}, unsigned long long *a_crossLevelMergeCount=nullptr, const OnMerge &a_onMergeCommitted=[](const MergeParticle< Packed > &, const MergeParticle< Packed > &, const MergeParticle< Packed > &) { })
 Run one full round of the nearest-neighbor merge algorithm, identical in every respect to mergeNearestNeighborsTree() except that the per-patch spatial search is backed by an EBGeometry::PointCloudHashGrid (a uniform-grid index) instead of an EBGeometry::PointCloudBVH.
 
template<typename P , typename Packed , typename Traits = ParticleTraits<P>, typename Gather , typename Combine , typename Scatter , typename Allocator , typename PosValid = std::function<bool(const RealVect&)>, typename OnMerge = std::function< void(const MergeParticle<Packed>&, const MergeParticle<Packed>&, const MergeParticle<Packed>&)>>
void mergeNearestNeighborsOneCell (ParticleContainer< P, Traits > &a_particles, const AmrMesh &a_amr, const NNCellBudget &a_cellBudget, const Gather &a_gather, const Combine &a_combine, const Scatter &a_scatter, const Allocator &a_allocateID, const bool a_iterateLocalTierToConvergence=false, const int a_maxFallbackCandidates=0, const PosValid &a_isPositionValid=[](const RealVect &) { return true;}, unsigned long long *a_crossLevelMergeCount=nullptr, const OnMerge &a_onMergeCommitted=[](const MergeParticle< Packed > &, const MergeParticle< Packed > &, const MergeParticle< Packed > &) { })
 Run one full round of the one-cell nearest-neighbor merge: the same distributed propose/judge/verdict protocol as mergeNearestNeighborsTree(), but with candidates found via a per-cell PointCloudBVH spatial index (see NNCellSpatialIndex) instead of one whole-patch tree.
 
ParticleMergeMethod mergeMethodFromString (const std::string &a_str) noexcept
 Map a merge-algorithm selector string to a ParticleMergeMethod.
 
template<class Packed , Real Packed::* packWeight, RealVect Packed::* packPosition, class P , class Traits = ParticleTraits<P>>
ParticleMerger< P, Traits > makeSfcNearestNeighborMerger (std::function< Packed(const ParticleSoA< P, Traits > &, std::size_t)> a_gather, std::function< void(Packed &, const Packed &)> a_combine, std::function< void(ParticleSoA< P, Traits > &, const Packed &)> a_scatter) noexcept
 Create a space-filling-curve nearest-neighbor super-particle merger as a reusable ParticleMerger.
 
template<class Packed , Real Packed::* packWeight, RealVect Packed::* packPosition, class P , class Traits = ParticleTraits<P>>
ParticleMerger< P, Traits > makeEqualWeightKDMerger (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 an equal-weight KD-tree super-particle merger as a reusable ParticleMerger.
 
template<class Context , class P , class Traits = ParticleTraits<P>>
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.
 
template<typename P , typename Traits , typename T , typename >
void removePhysicalParticles (ParticleSoA< P, Traits > &a_particles, const T a_numPhysPartToRemove) noexcept
 Remove physical particles from an SoA container (operates on the container-owned weight column).
 
template<typename P , typename Traits >
void deleteParticles (ParticleSoA< P, Traits > &a_particles, const Real a_weightThresh) noexcept
 Remove particles from an SoA container if their weight is below the threshold (swap-and-pop).
 
template<typename T , typename >
std::vector< T > partitionParticleWeights (const T a_numPhysicalParticles, const T a_maxCompParticles) noexcept
 Partition particle weights among a number of computational particles.
 
template<typename P , typename Traits , typename T , typename >
void drawRandomParticles (ParticleSoA< P, Traits > &a_particles, const T a_numParticles, const std::function< RealVect()> &a_distribution)
 Draw random SoA particles into a free buffer, distributed according to a predefined distribution.
 
template<typename P , typename Traits , typename T , typename >
void drawGaussianParticles (ParticleSoA< P, Traits > &a_particles, const T a_numParticles, const RealVect &a_center, const Real a_radius) noexcept
 Draw Gaussian-distributed particles into an SoA container (unit weight; positions only).
 
template<typename P , typename Traits , typename T , typename >
void drawBoxParticles (ParticleSoA< P, Traits > &a_particles, const T a_numParticles, const RealVect &a_loCorner, const RealVect &a_hiCorner) noexcept
 Draw box-distributed particles into an SoA container (unit weight; positions only).
 
template<typename P , typename Traits , typename T , typename >
void drawSphereParticles (ParticleSoA< P, Traits > &a_particles, const T a_numParticles, const RealVect &a_center, const Real a_radius) noexcept
 Draw sphere-distributed particles into an SoA container (unit weight; positions only).
 

Detailed Description

Namespace for various particle management tools.

Typedef Documentation

◆ BinaryParticleReconcile

template<class P >
using ParticleManagement::BinaryParticleReconcile = typedef std::function<void(P& p1, P& p2, const P& p0)>

Declaration of a reconciliation function when splitting particles.

This is, for example, passed into the KD-tree equal-weight partitioning structure, which may split a particle such that the weight of the two nodes differ by at most one physical particle. By default, that method will call the particle copy constructor, but this function permits the user to input a reconciliation function that manipulates other class members in the split particles.

◆ IDAllocator

using ParticleManagement::IDAllocator = typedef std::function<ParticleID()>

Illustrative signature of the fresh-id allocator: produce one globally-unique id for a newly created merged particle.

Documents the expected callback shape; consumed as a template parameter (not this std::function). Called once per committed merge (trivial-tier or judge-side). The caller must ensure ids never collide – not just within one call, but across every rank and every past/future call for the life of the simulation; a rank-namespaced counter (this rank's index times a stride plus a monotonically increasing local count) suffices.

Returns
A fresh globally-unique particle id.

◆ KDPayloadCombine

template<typename Packed >
using ParticleManagement::KDPayloadCombine = typedef std::function< Packed(const Packed* a_payloads, const Real* a_weights, const std::size_t a_count)>

Illustrative signature of the N-ary combine callback: produce a merged particle's opaque payload from an arbitrary-size group of parents' payloads and weights.

Deliberately N-ary, not pairwise like the nearest-neighbor merge's PayloadCombine – leaves/boxes here are groups by construction (kd-tree leaves, carve boxes), not pairs off a k-NN graph, so the natural callback shape takes a whole group at once rather than needing a caller-chosen fold order for reproducibility. This alias documents the expected shape only; it is NOT the type used at the call sites (consumed as a template parameter so it inlines on the per-merge hot path). Position and total weight are handled by the algorithm directly (weighted centroid, summed weight); this callback produces only the opaque Packed payload.

Parameters
[in]a_payloadsPointer to the first of a_count parents' payloads.
[in]a_weightsPointer to the first of a_count parents' weights, parallel to a_payloads.
[in]a_countNumber of parents being combined (always >= 2 – see the merge threshold in mergeKDCarve()/mergeKDPatch()'s own docs).
Returns
The merged particle's payload.

◆ MergeCommittedCallback

template<typename Packed >
using ParticleManagement::MergeCommittedCallback = typedef std::function<void(const MergeParticle<Packed>& a_first, const MergeParticle<Packed>& a_second, const MergeParticle<Packed>& a_merged)>

Optional per-merge diagnostic callback: observe every individual merge as it commits.

Consumed as a template parameter (not required to be exactly this std::function). Called once per committed merge (trivial-tier or judge-side) with both parents' full pre-merge data and the resulting merged particle (finalized but not yet placed; see placeMergedParticles()). Default is a no-op. Intended for invariant checks, e.g. recomputing the weighted centroid from the parents and comparing against a_merged.position.

Parameters
[in]a_firstFirst parent (pre-merge, full data).
[in]a_secondSecond parent (pre-merge, full data).
[in]a_mergedThe resulting merged particle.

◆ ParticleMerger

template<class P , class Traits = ParticleTraits<P>>
using ParticleManagement::ParticleMerger = typedef std::function< void(ParticleSoA<P, Traits>& a_particles, const CellInfo& a_cellInfo, const int a_numTargetParticles)>

Concept for splitting/merging particles.

Parameters
[in,out]a_particlesParticles to be merged/split
[in]a_cellInfoCell info
[in]a_numTargetParticlesNumber of target particles

◆ PayloadCombine

template<typename Packed >
using ParticleManagement::PayloadCombine = typedef std::function< Packed(const Packed& a_first, const Real a_firstWeight, const Packed& a_second, const Real a_secondWeight)>

Illustrative signature of the combine callback: produce a merged particle's opaque payload from its two parents' payloads and weights.

This alias documents the expected callback shape only; it is NOT the type used at the call sites. resolveTrivialTier()/judgeProposals() take the combine callback as a template parameter (not this std::function) so it inlines on the per-merge hot path. Position and weight are handled by the algorithm directly (weighted centroid, summed weight); this callback produces only the opaque Packed payload (e.g. weighted-average energy for ItoParticle).

Parameters
[in]a_firstFirst parent's payload.
[in]a_firstWeightFirst parent's weight.
[in]a_secondSecond parent's payload.
[in]a_secondWeightSecond parent's weight.
Returns
The merged particle's payload.

◆ PositionValid

using ParticleManagement::PositionValid = typedef std::function<bool(const RealVect& a_mergedPosition)>

Illustrative signature of the position-validity predicate for a would-be merged particle.

Documents the expected callback shape; consumed as a template parameter (not this std::function) at the call sites, for the same inlining reason as PayloadCombine. Called with the weighted-centroid position already computed, once per candidate pair that is otherwise eligible to merge (never on a pair already rejected for another reason), so a caller doing real geometric work here pays only when it matters. Default is always-true. A pair that fails is treated like a_maxCellDistance's "too far" case: not proposed, a fallback tried if available, otherwise left unmatched with no side effects.

Parameters
[in]a_mergedPositionThe candidate merge's weighted-centroid position.
Returns
True iff a particle may exist at this position (e.g. false inside an embedded boundary).

Enumeration Type Documentation

◆ ParticleMergeMethod

The specific super-particle merge methods.

Enumerator
None 

No merging.

EqualWeightKD 

Cell: makeEqualWeightKDMerger.

Reinitialize 

Cell: makeReinitializeMerger.

ReinitializeBVH 

Cell: makeEqualWeightKDMerger with reinitialized leaf positions.

NnSfc 

Cell: makeSfcNearestNeighborMerger.

NnPairTree 

AMR: mergeNearestNeighborsTree (whole-patch PointCloudBVH search).

NnPairOneCell 

AMR: mergeNearestNeighborsOneCell (per-cell PointCloudBVH search, Chebyshev distance 1 only).

NnPairHash 

AMR: mergeNearestNeighborsHash (whole-patch PointCloudHashGrid search).

KdCarve 

AMR: mergeKDCarve (whole-patch kd-tree build, arbitrated patch boundaries).

KdPatch 

AMR: mergeKDPatch (same build, patch-local – no ghosts, no contested particles).

KdSkinNn 

AMR: mergeKDInterior for the uncontested tier, nearest-neighbor pairs for the skin.

External 

Cell: caller-supplied per-cell merger.

Function Documentation

◆ deleteParticles()

template<typename P , typename Traits >
void ParticleManagement::deleteParticles ( ParticleSoA< P, Traits > &  a_particles,
const Real  a_weightThresh 
)
inlinenoexcept

Remove particles from an SoA container if their weight is below the threshold (swap-and-pop).

Parameters
[in,out]a_particlesInput SoA particle container.
[in]a_weightThreshWeight threshold for removal.
Template Parameters
PPayload type.
TraitsColumn descriptor for the payload.

◆ drawBoxParticles()

template<typename P , typename Traits , typename T , typename >
void ParticleManagement::drawBoxParticles ( ParticleSoA< P, Traits > &  a_particles,
const T  a_numParticles,
const RealVect &  a_loCorner,
const RealVect &  a_hiCorner 
)
inlinenoexcept

Draw box-distributed particles into an SoA container (unit weight; positions only).

Each rank draws its own a_numParticles share with weight 1; routing to owners happens when the buffer is added to a ParticleContainer.

Parameters
[out]a_particlesOutput SoA buffer (cleared on entry).
[in]a_numParticlesNumber of particles.
[in]a_loCornerLow corner of box.
[in]a_hiCornerHigh corner of box.
Template Parameters
PPayload type.
TraitsColumn descriptor for the payload.
TIntegral count type.

◆ drawGaussianParticles()

template<typename P , typename Traits , typename T , typename >
void ParticleManagement::drawGaussianParticles ( ParticleSoA< P, Traits > &  a_particles,
const T  a_numParticles,
const RealVect &  a_center,
const Real  a_radius 
)
inlinenoexcept

Draw Gaussian-distributed particles into an SoA container (unit weight; positions only).

Each rank draws its own a_numParticles share with weight 1; routing to owners happens when the buffer is added to a ParticleContainer.

Parameters
[out]a_particlesOutput SoA buffer (cleared on entry).
[in]a_numParticlesNumber of particles.
[in]a_centerGaussian blob center.
[in]a_radiusGaussian blob radius.
Template Parameters
PPayload type.
TraitsColumn descriptor for the payload.
TIntegral count type.

◆ drawRandomParticles()

template<typename P , typename Traits , typename T , typename >
void ParticleManagement::drawRandomParticles ( ParticleSoA< P, Traits > &  a_particles,
const T  a_numParticles,
const std::function< RealVect()> &  a_distribution 
)
inline

Draw random SoA particles into a free buffer, distributed according to a predefined distribution.

Fills a free-standing ParticleSoA leaf (a buffer) rather than an AMR container. MPI-aware – it partitions a_numParticles across ranks (total = a_numParticles) and each rank draws its own share (unit weight, default payload). Downstream code adds the buffer to a ParticleContainer (e.g. addParticlesDestructive) to route the particles to their owning patch/level/rank.

Parameters
[out]a_particlesSoA buffer to fill (cleared first).
[in]a_numParticlesTotal number of particles across all ranks.
[in]a_distributionDistribution functor returning a random position.
Template Parameters
PPayload type.
TraitsColumn descriptor for the payload.
TIntegral count type.

◆ drawSphereParticles()

template<typename P , typename Traits , typename T , typename >
void ParticleManagement::drawSphereParticles ( ParticleSoA< P, Traits > &  a_particles,
const T  a_numParticles,
const RealVect &  a_center,
const Real  a_radius 
)
inlinenoexcept

Draw sphere-distributed particles into an SoA container (unit weight; positions only).

Each rank draws its own a_numParticles share with weight 1; routing to owners happens when the buffer is added to a ParticleContainer.

Parameters
[out]a_particlesOutput SoA buffer (cleared on entry).
[in]a_numParticlesNumber of particles.
[in]a_centerSphere center.
[in]a_radiusSphere radius.
Template Parameters
PPayload type.
TraitsColumn descriptor for the payload.
TIntegral count type.

◆ makeEqualWeightKDMerger()

template<class Packed , Real Packed::* packWeight, RealVect Packed::* packPosition, class P , class Traits = ParticleTraits<P>>
ParticleMerger< P, Traits > ParticleManagement::makeEqualWeightKDMerger ( 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 
)
inlinenoexcept

Create an equal-weight KD-tree super-particle merger as a reusable ParticleMerger.

Returns a merger functor that partitions the particle set in a single SoA cell into at most a_numTargetParticles equal-weight KD leaves and reduces each leaf to one output particle.

The algorithm:

  1. Packs each SoA slot into a lightweight intermediate (Packed) via a_gather and accumulates total weight W.
  2. Returns early if W < 2 or the target is non-positive.
  3. Calls detail::buildEqualWeightKDLeaves to partition the packed list into at most a_numTargetParticles leaves, using a_reconcile to fix up daughter particles whenever the median is split.
  4. Clears the SoA container, then reduces each leaf by calling a_scatterLeaf once per leaf.

a_scatterLeaf receives the raw [first, last) pointer range of the leaf (pointers into the internal KD scratch buffer, valid only until the next call) and the current CellInfo, so it can implement either a weighted-centroid reduction or a spatially-reinitialised reduction (e.g. random point in the leaf bounding box for non-cut-cells).

Thread-local scratch vectors are reused across cells — no per-cell heap allocation after warmup.

Template Parameters
PackedLightweight AoS intermediate type (e.g. MergeParticle<Packed>). Must be copyable.
packWeightMember-data pointer Real Packed::* naming the weight. Forwarded to detail::buildEqualWeightKDLeaves for median splitting.
packPositionMember-data pointer RealVect Packed::* naming the position. Forwarded to detail::buildEqualWeightKDLeaves for bounding-box splitting.
PSoA payload type.
TraitsColumn descriptor for P (defaults to ParticleTraits

).

Parameters
[in]a_gatherPer-slot gather: (const ParticleSoA<P,Traits>&, std::size_t) -> Packed.
[in]a_reconcileDaughter reconciliation: (Packed& p1, Packed& p2, const Packed& p0) -> void.
[in]a_scatterLeafLeaf reducer: (ParticleSoA<P,Traits>&, const Packed* first, const Packed* last, const CellInfo&) -> void. Appends exactly one merged particle per call.
Returns
A ParticleMerger<P, Traits> ready to be stored in e.g. ItoSolver::m_particleCellMerger.

◆ makeReinitializeMerger()

template<class Context , class P , class Traits = ParticleTraits<P>>
ParticleMerger< P, Traits > ParticleManagement::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 
)
inlinenoexcept

Create a reinitialize super-particle merger as a reusable ParticleMerger.

Returns a merger functor that collapses all physical particles in a cell into at most a_numTargetParticles new computational particles, placing them at random positions within the cell.

The algorithm:

  1. Calls a_aggregate once on the input SoA to obtain the total physical-particle count and an arbitrary caller-defined context (e.g. the weight-averaged energy for ItoParticle).
  2. Returns early if the physical count is non-positive or the target is non-positive.
  3. Calls partitionParticleWeights to divide the physical count into at most a_numTargetParticles integer weights.
  4. Clears the SoA; for each weight draws a random position in the cell via Random::randomPosition and calls a_emit once.

The cell position is computed as a_probLo + dx * (gridIndex + 0.5), matching the formula used in ItoSolver::reinitializeParticles.

Template Parameters
ContextArbitrary aggregated payload type (e.g. Real for average energy). Must be copyable and default-constructible.
PSoA payload type.
TraitsColumn descriptor for P (defaults to ParticleTraits

).

Parameters
[in]a_aggregateAggregate functor: (const ParticleSoA<P,Traits>&) -> pair<long long, Context>. Returns total physical particle count and a context consumed by a_emit.
[in]a_emitPer-particle emit: (ParticleSoA<P,Traits>&, const RealVect& pos, long long weight, const Context&) -> void. Appends exactly one particle.
[in]a_probLoLazy supplier of the domain lower-left corner (from AmrMesh::getProbLo). Called on every merge invocation; capture the solver's AmrMesh reference so the call is deferred until after AMR setup completes.
Returns
A ParticleMerger<P, Traits> ready to be stored in e.g. ItoSolver::m_particleCellMerger.

◆ makeSfcNearestNeighborMerger()

template<class Packed , Real Packed::* packWeight, RealVect Packed::* packPosition, class P , class Traits = ParticleTraits<P>>
ParticleMerger< P, Traits > ParticleManagement::makeSfcNearestNeighborMerger ( std::function< Packed(const ParticleSoA< P, Traits > &, std::size_t)>  a_gather,
std::function< void(Packed &, const Packed &)>  a_combine,
std::function< void(ParticleSoA< P, Traits > &, const Packed &)>  a_scatter 
)
inlinenoexcept

Create a space-filling-curve nearest-neighbor super-particle merger as a reusable ParticleMerger.

Returns a merger functor that reduces (or grows) the particle set in a single SoA cell to the target count without forcing equal weights.

When the cell contains more particles than the target:

  1. The intermediate list is sorted along a 21-bit Hilbert curve computed from particle positions.
  2. detail::mergeAdjacentNearest merges the closest adjacent pair repeatedly until the target count is reached, delegating payload arithmetic to a_combine.

When the cell contains fewer particles than the target: the heaviest particle (by packWeight) is repeatedly split into two co-located halves (weight divided by two, all other fields copied) until the target is reached or no particle has weight >= 2.

The three user-supplied lambdas encode the particle-type-specific semantics:

  • a_gather packs one SoA slot into the lightweight intermediate type Packed.
  • a_combine merges the second Packed particle into the first (weight-weighted centroid is the typical choice, but the caller decides).
  • a_scatter appends one surviving Packed particle back to the (already-cleared) SoA.

Implementation note: thread-local scratch vectors are reused across cells so there is no per-cell heap allocation after the first call on a given thread.

Template Parameters
PackedLightweight AoS intermediate type (e.g. MergeParticle<Packed>). Must be copyable.
packWeightMember-data pointer Real Packed::* naming the weight. Used only for the split path.
packPositionMember-data pointer RealVect Packed::* naming the position. Used for Hilbert-key computation and forwarded to detail::mergeAdjacentNearest.
PSoA payload type.
TraitsColumn descriptor for P (defaults to ParticleTraits

).

Parameters
[in]a_gatherPer-slot gather: (const ParticleSoA<P,Traits>&, std::size_t) -> Packed.
[in]a_combineIn-place merge: (Packed& first, const Packed& second) -> void.
[in]a_scatterAppend one merged particle: (ParticleSoA<P,Traits>&, const Packed&) -> void.
Returns
A ParticleMerger<P, Traits> ready to be stored in e.g. ItoSolver::m_particleCellMerger.

◆ mergeKDCarve()

template<typename P , typename Packed , typename Traits = ParticleTraits<P>, typename Gather , typename Combine , typename Scatter , typename Allocator , typename PosValid >
void ParticleManagement::mergeKDCarve ( ParticleContainer< P, Traits > &  a_particles,
EBAMRFAB &  a_cellHistogram,
EBAMRFAB &  a_leafQuota,
const AmrMesh a_amr,
const int  a_ppc,
const Real  a_splitWeightLeafDx,
const Gather &  a_gather,
const Combine &  a_combine,
const Scatter &  a_scatter,
const Allocator &  a_allocateID,
const PosValid &  a_isPositionValid 
)
inline

Run one non-iterative pass of the kd-tree carve merge over every patch this rank owns.

Orchestrates, in order:

  1. Build one kd tree per patch over its local particles plus every ghost. The caller must have filled ghosts at width 1; this function does not fill them itself.
  2. Classify each leaf as unmergeable (too wide to merge), interior (no boundary exposure and no ghost member), or a boundary box.
  3. Commit interior leaves immediately, with no communication.
  4. Resolve boundary boxes against competing patches in three fixed exchange rounds – claim, nominal verdict, commit-or-release – keyed by (AABB volume, anchor id).
  5. Collapse a leaf or box only if at least two members survive and a_isPositionValid accepts the centroid, then place the result. Anything else is left untouched.

The round count is fixed, not a convergence loop; further reduction comes from calling again.

Template Parameters
PSoA payload type.
PackedOpaque reconciliation payload type (see MergeParticle).
TraitsColumn descriptor for P (defaults to ParticleTraits

).

Template Parameters
GatherCallable (const ParticleSoA<P,Traits>&, std::size_t) -> Packed.
CombineSee KDPayloadCombine – N-ary, not pairwise.
ScatterCallable (ParticleSoA<P,Traits>&, const MergeParticle<Packed>&) -> void.
AllocatorCallable () -> ParticleID, a fresh globally-unique id per committed merge.
PosValidCallable (const RealVect&) -> bool; false leaves the group's members untouched.
Parameters
[in]a_amrAMR mesh – source of the particle ghost masks and grid layout.
[in,out]a_particlesThe real particle container being merged.
[in,out]a_cellHistogramCaller-owned per-cell scratch (1 component, >= 1 ghost cell, this container's realm) holding the cell occupancy. Contents on entry are irrelevant – it is overwritten per patch. Owned by the caller so the allocation happens once per regrid rather than once per call.
[in,out]a_leafQuotaCaller-owned per-cell scratch, same shape and lifetime as a_cellHistogram, holding the live per-cell merge quota.
[in]a_ppcTarget particles per cell.
[in]a_splitWeightLeafDxLeaf size, in cell widths, at or below which the tree build switches from count-median to weight-median splitting – forwarded verbatim to buildKDQuotaLeaves(). 0 disables the weight median entirely.
[in]a_gatherPer-slot gather callback.
[in]a_combineN-ary opaque-payload combine callback.
[in]a_scatterPer-merged-particle scatter callback.
[in]a_allocateIDFresh-id allocator.
[in]a_isPositionValidPosition-validity predicate.

◆ mergeKDInterior()

template<typename P , typename Packed , typename Traits = ParticleTraits<P>, typename Gather , typename Combine , typename Scatter , typename Allocator , typename PosValid >
void ParticleManagement::mergeKDInterior ( ParticleContainer< P, Traits > &  a_particles,
ParticleContainer< P, Traits > &  a_interior,
EBAMRFAB &  a_cellHistogram,
EBAMRFAB &  a_leafQuota,
const AmrMesh a_amr,
const int  a_ppc,
const Real  a_splitWeightLeafDx,
const Gather &  a_gather,
const Combine &  a_combine,
const Scatter &  a_scatter,
const Allocator &  a_allocateID,
const PosValid &  a_isPositionValid 
)
inline

Run the uncontested tier of the kd merge, splitting the input into merged and leftover.

Same tree build and per-cell quota as mergeKDCarve(), but with no arbitration tier at all. In order:

  1. Build one kd tree per patch over that patch's own particles plus every ghost, which the caller must already have filled at width 1. Ghosts are never merged, but they take part in the partition: a ghost marks the leaf it lands in as contested, and the quota needs the cell's true occupancy.
  2. Commit every leaf that holds at least two members, is within detail::s_kdMaxLeafExtent, holds no ghost, and whose centroid a_isPositionValid accepts. Each such leaf becomes one super-particle in a_interior and its members are removed from a_particles.
  3. Everything else is left untouched in a_particles – leaves holding a ghost, leaves too wide to collapse, and leaves with a single member.

"Holds no ghost" is the whole safety condition, and it needs no communication to evaluate. Every member of such a leaf is resident in this patch, and any other patch that draws one of those members into a leaf of its own sees it as a ghost, so that leaf fails this same test there. No two patches can commit the same particle and no weight is counted twice. Boundary exposure is deliberately not consulted: an exposed but uncontested leaf merges here.

The result is a split, not a finished merge – a_particles is left holding exactly the contested particles, for a caller that means to reduce them by some other means.

Template Parameters
PSoA payload type.
PackedOpaque reconciliation payload type (see MergeParticle).
TraitsColumn descriptor for P (defaults to ParticleTraits

).

Template Parameters
GatherCallable (const ParticleSoA<P,Traits>&, std::size_t) -> Packed.
CombineSee KDPayloadCombine – N-ary, not pairwise.
ScatterCallable (ParticleSoA<P,Traits>&, const MergeParticle<Packed>&) -> void.
AllocatorCallable () -> ParticleID, a fresh globally-unique id per committed merge.
PosValidCallable (const RealVect&) -> bool; false leaves the leaf's members untouched.
Parameters
[in,out]a_particlesParticles to reduce. On return holds only what was not committed.
[in,out]a_interiorReceives one super-particle per committed leaf. Must be allocated on the same realm as a_particles, since a leaf is written to the patch it was built on.
[in,out]a_cellHistogramCaller-owned per-cell scratch; see mergeKDCarve()'s own docs.
[in,out]a_leafQuotaCaller-owned per-cell scratch; see mergeKDCarve()'s own docs.
[in]a_amrAMR mesh – source of the grid layout.
[in]a_ppcTarget particles per cell.
[in]a_splitWeightLeafDxLeaf size, in cell widths, at or below which the tree build switches from count-median to weight-median splitting – forwarded verbatim to buildKDQuotaLeaves(). 0 disables the weight median entirely.
[in]a_gatherPer-slot gather callback.
[in]a_combineN-ary opaque-payload combine callback.
[in]a_scatterPer-merged-particle scatter callback.
[in]a_allocateIDFresh-id allocator.
[in]a_isPositionValidPosition-validity predicate.

◆ mergeKDPatch()

template<typename P , typename Packed , typename Traits = ParticleTraits<P>, typename Gather , typename Combine , typename Scatter , typename Allocator , typename PosValid >
void ParticleManagement::mergeKDPatch ( ParticleContainer< P, Traits > &  a_particles,
EBAMRFAB &  a_cellHistogram,
EBAMRFAB &  a_leafQuota,
const AmrMesh a_amr,
const int  a_ppc,
const Real  a_splitWeightLeafDx,
const Gather &  a_gather,
const Combine &  a_combine,
const Scatter &  a_scatter,
const Allocator &  a_allocateID,
const PosValid &  a_isPositionValid 
)
inline

Run one patch-local kd-tree merge over every patch this rank owns.

Same tree build and per-cell quota as mergeKDCarve(), with no boundary tier:

  1. Build one kd tree per patch over that patch's own particles. The caller must NOT have filled ghosts; a ghost here would be merged locally while its true owner merges it too.
  2. Every leaf whose largest per-axis extent is within detail::s_kdMaxLeafExtent commits immediately, regardless of boundary exposure. No leaf is ever contested, so there is no claim/verdict/commit exchange and no communication at all.
  3. A leaf collapses to one merged particle only if it has at least two members and a_isPositionValid accepts the centroid; otherwise its members are left untouched.
  4. Merged results are placed by a cheap point-in-the-owning-patch test, falling back to ParticleContainer::findDestination().

Each patch therefore reduces only the particles it owns. Cheaper than mergeKDCarve() and free of communication, at the cost of no coordination across a patch boundary: a cell straddled by two patches is reduced by each of them independently.

Template Parameters
PSoA payload type.
PackedOpaque reconciliation payload type (see MergeParticle).
TraitsColumn descriptor for P (defaults to ParticleTraits

).

Template Parameters
GatherCallable (const ParticleSoA<P,Traits>&, std::size_t) -> Packed.
CombineSee KDPayloadCombine – N-ary, not pairwise.
ScatterCallable (ParticleSoA<P,Traits>&, const MergeParticle<Packed>&) -> void.
AllocatorCallable () -> ParticleID, a fresh globally-unique id per committed merge.
PosValidCallable (const RealVect&) -> bool; false leaves the leaf's members untouched.
Parameters
[in,out]a_particlesThe real particle container being merged.
[in,out]a_cellHistogramCaller-owned per-cell scratch; see mergeKDCarve()'s own docs.
[in,out]a_leafQuotaCaller-owned per-cell scratch; see mergeKDCarve()'s own docs.
[in]a_amrAMR mesh – source of the grid layout.
[in]a_ppcTarget particles per cell.
[in]a_splitWeightLeafDxLeaf size, in cell widths, at or below which the tree build switches from count-median to weight-median splitting – forwarded verbatim to buildKDQuotaLeaves(). 0 disables the weight median entirely.
[in]a_gatherPer-slot gather callback.
[in]a_combineN-ary opaque-payload combine callback.
[in]a_scatterPer-merged-particle scatter callback.
[in]a_allocateIDFresh-id allocator.
[in]a_isPositionValidPosition-validity predicate.

◆ mergeMethodFromString()

ParticleMergeMethod ParticleManagement::mergeMethodFromString ( const std::string &  a_str)
inlinenoexcept

Map a merge-algorithm selector string to a ParticleMergeMethod.

Valid input options are: 'none' -> None 'equal_weight_kd' -> EqualWeightKD 'reinitialize' -> Reinitialize 'reinitialize_bvh' -> ReinitializeBVH 'nn_sfc' -> NnSfc 'nn_pair_tree' -> NnPairTree 'nn_pair_onecell' -> NnPairOneCell 'nn_pair_hash' -> NnPairHash 'kd_carve' -> KdCarve 'kd_patch' -> KdPatch 'kd_skin_nn' -> KdSkinNn 'external' -> External

Parameters
[in]a_strString selector. Must take one of the above strings.
Returns
The corresponding method; aborts on an unknown selector.

◆ mergeNearestNeighborsHash()

template<typename P , typename Packed , typename Traits = ParticleTraits<P>, typename Gather , typename Combine , typename Scatter , typename Allocator , typename PosValid = std::function<bool(const RealVect&)>, typename OnMerge = std::function< void(const MergeParticle<Packed>&, const MergeParticle<Packed>&, const MergeParticle<Packed>&)>>
void ParticleManagement::mergeNearestNeighborsHash ( ParticleContainer< P, Traits > &  a_particles,
const AmrMesh a_amr,
const NNCellBudget a_cellBudget,
const Gather &  a_gather,
const Combine &  a_combine,
const Scatter &  a_scatter,
const Allocator &  a_allocateID,
const bool  a_iterateLocalTierToConvergence = false,
const int  a_maxFallbackCandidates = 0,
const std::optional< int >  a_maxCellDistance = std::nullopt,
const int  a_ghostWidth = 1,
const PosValid &  a_isPositionValid = [](const RealVect&) { return true; },
unsigned long long *  a_crossLevelMergeCount = nullptr,
const OnMerge &  a_onMergeCommitted = [](const MergeParticle<Packed>&, const MergeParticle<Packed>&, const MergeParticle<Packed>&) { } 
)
inline

Run one full round of the nearest-neighbor merge algorithm, identical in every respect to mergeNearestNeighborsTree() except that the per-patch spatial search is backed by an EBGeometry::PointCloudHashGrid (a uniform-grid index) instead of an EBGeometry::PointCloudBVH.

See mergeNearestNeighborsTree() for the full algorithm description, parameter contracts, and defaults – every one of them applies here unchanged; only the underlying point-cloud index differs (see detail::mergeNearestNeighborsRoundImpl(), which both this function and mergeNearestNeighborsTree() forward to, selecting the point-cloud type via its Cloud template parameter). PointCloudHashGrid trades PointCloudBVH's adaptivity to non-uniform density for an O(1) expected build/query cost at near-uniform density; prefer it when a crowded cell's neighborhood is not also badly clustered in density, and measure before relying on it being faster in a given workload (see PointCloudHashGrid's own docs in EBGeometry for when it is and is not a good fit).

Template Parameters
P,Packed,Traits,Gather,Combine,Scatter,Allocator,PosValid,OnMergeSee mergeNearestNeighborsTree().
Parameters
a_amr,a_particles,a_cellBudget,a_gather,a_combine,a_scatter,a_allocateID,a_iterateLocalTierToConvergence,a_maxFallbackCandidates,a_maxCellDistance,a_ghostWidth,a_isPositionValid,a_crossLevelMergeCount,a_onMergeCommittedSee mergeNearestNeighborsTree().

◆ mergeNearestNeighborsOneCell()

template<typename P , typename Packed , typename Traits = ParticleTraits<P>, typename Gather , typename Combine , typename Scatter , typename Allocator , typename PosValid = std::function<bool(const RealVect&)>, typename OnMerge = std::function< void(const MergeParticle<Packed>&, const MergeParticle<Packed>&, const MergeParticle<Packed>&)>>
void ParticleManagement::mergeNearestNeighborsOneCell ( ParticleContainer< P, Traits > &  a_particles,
const AmrMesh a_amr,
const NNCellBudget a_cellBudget,
const Gather &  a_gather,
const Combine &  a_combine,
const Scatter &  a_scatter,
const Allocator &  a_allocateID,
const bool  a_iterateLocalTierToConvergence = false,
const int  a_maxFallbackCandidates = 0,
const PosValid &  a_isPositionValid = [](const RealVect&) { return true; },
unsigned long long *  a_crossLevelMergeCount = nullptr,
const OnMerge &  a_onMergeCommitted = [](const MergeParticle<Packed>&, const MergeParticle<Packed>&, const MergeParticle<Packed>&) { } 
)
inline

Run one full round of the one-cell nearest-neighbor merge: the same distributed propose/judge/verdict protocol as mergeNearestNeighborsTree(), but with candidates found via a per-cell PointCloudBVH spatial index (see NNCellSpatialIndex) instead of one whole-patch tree.

Orchestrates, in order: 1. gatherMergeParticles() (ghosts assumed already filled by the caller, exactly as mergeNearestNeighborsTree() requires – see its own docs). 2. Per patch: buildNNCellSpatialIndex(), then findNearestNeighborCandidatesOneCell() + resolveTrivialTier(), optionally repeated to convergence (see a_iterateLocalTierToConvergence). 3. finishMergeRound() for everything the local tier could not resolve.

The merge distance is structurally fixed at Chebyshev cell distance 1 – only the query's own cell and its Moore-adjacent neighbors are ever visited (see findNearestNeighborCandidatesOneCell()) – so, unlike mergeNearestNeighborsTree(), this function takes no a_maxCellDistance parameter, and requires exactly a width-1 particle ghost halo (already filled by the caller) rather than a configurable a_ghostWidth.

Template Parameters
PSoA payload type (e.g. ItoParticle).
PackedOpaque reconciliation payload type (see MergeParticle).
TraitsColumn descriptor for P (defaults to ParticleTraits

).

Template Parameters
GatherCallable (const ParticleSoA<P,Traits>&, std::size_t) -> Packed.
CombineSee PayloadCombine.
ScatterCallable (ParticleSoA<P,Traits>&, const MergeParticle<Packed>&) -> void.
Parameters
[in,out]a_particlesThe real particle container to merge, for every patch this rank owns.
[in]a_amrAMR mesh – source of ghost masks, per-level dx, and the patch layout.
[in]a_cellBudgetThe count each cell is drained down to – see NNCellBudget.
[in]a_gatherPer-slot gather callback (see Gather).
[in]a_combineOpaque-payload combine callback (see PayloadCombine).
[in]a_scatterPer-surviving-particle scatter callback (see Scatter).
[in]a_allocateIDFresh-id allocator (see IDAllocator). Mandatory, same requirement as mergeNearestNeighborsTree().
[in]a_iterateLocalTierToConvergenceDefault false. See mergeNearestNeighborsTree()'s own docs.
[in]a_maxFallbackCandidatesForwarded to resolveTrivialTier(). Default 0 (disabled). See mergeNearestNeighborsTree()'s own docs.
[in]a_isPositionValidForwarded to resolveTrivialTier()/judgeProposals() (see PositionValid). Default always-true.
[in,out]a_crossLevelMergeCountOptional diagnostic counter (nullptr, the default, disables it). See mergeNearestNeighborsTree()'s own docs.
[in]a_onMergeCommittedOptional per-merge diagnostic callback (see MergeCommittedCallback); default is a no-op.

◆ mergeNearestNeighborsTree()

template<typename P , typename Packed , typename Traits = ParticleTraits<P>, typename Gather , typename Combine , typename Scatter , typename Allocator , typename PosValid = std::function<bool(const RealVect&)>, typename OnMerge = std::function< void(const MergeParticle<Packed>&, const MergeParticle<Packed>&, const MergeParticle<Packed>&)>>
void ParticleManagement::mergeNearestNeighborsTree ( ParticleContainer< P, Traits > &  a_particles,
const AmrMesh a_amr,
const NNCellBudget a_cellBudget,
const Gather &  a_gather,
const Combine &  a_combine,
const Scatter &  a_scatter,
const Allocator &  a_allocateID,
const bool  a_iterateLocalTierToConvergence = false,
const int  a_maxFallbackCandidates = 0,
const std::optional< int >  a_maxCellDistance = std::nullopt,
const int  a_ghostWidth = 1,
const PosValid &  a_isPositionValid = [](const RealVect&) { return true; },
unsigned long long *  a_crossLevelMergeCount = nullptr,
const OnMerge &  a_onMergeCommitted = [](const MergeParticle<Packed>&, const MergeParticle<Packed>&, const MergeParticle<Packed>&) { } 
)
inline

Run one full round (one timestep's worth) of the nearest-neighbor merge algorithm.

Orchestrates, in order:

  1. Ghost-fill – fresh, exactly once, strictly before any of the below, via ParticleContainer::fillGhostParticles() with the AmrMesh particle ghost masks. This function does not fill ghosts itself: the caller fills them first, and must have registered a width-1 particle ghost mask before the grids were (re)built (a mask is only built during a regrid, so registering it late leaves it empty). See ItoSolver::mergeSuperparticlesNearestNeighbor for a concrete caller.
  2. Per patch: findNearestNeighborCandidates(), then resolveTrivialTier(). If a_iterateLocalTierToConvergence is true, this pair repeats (reusing the step-1 ghost-fill) until no further local commits occur. See a_iterateLocalTierToConvergence below.
  3. generateProposals() for everything the trivial tier could not resolve.
  4. Exchange proposals via nnMergeExchangeByRank() (an MPI_Alltoall/Alltoallv helper): this rank's own bucket (addressed to itself) is handed directly to judgeProposals() in-process, every other bucket is shipped to its destination rank and the incoming proposals collected.
  5. judgeProposals() over the pooled incoming proposals.
  6. Exchange verdicts (nnMergeExchangeByRank(), the other direction) and applyVerdicts().
  7. placeMergedParticles(), then exchange and apply the scatter bucket (nnMergeExchangeByRank()).

Fresh, globally-unique ids for newly created particles must be allocated by the caller such that they can never collide with an existing particle's id or another rank's freshly allocated id within the same round; a rank-namespaced counter suffices (see a_allocateID / IDAllocator).

This function does a SINGLE cross-patch exchange (propose/judge/verdict happens exactly once) regardless of a_iterateLocalTierToConvergence: a cell more than roughly 2x over threshold is not necessarily fully drained by the cross-patch protocol in one call, and the caller is still expected to invoke this once per timestep, relying on repeated calls (and, in a real simulation, particle motion between them) for further convergence of anything the cross-patch protocol itself left unresolved. What a_iterateLocalTierToConvergence changes is only the LOCAL (trivial-tier) portion of a single call – see its own documentation below.

Template Parameters
PSoA payload type (e.g. ItoParticle).
PackedOpaque reconciliation payload type for the merge decision layer (see MergeParticle) – typically much smaller than P's full column set (e.g. just energy for ItoParticle; velocity/mobility/diffusion are already established as safe to drop across a merge, since they are recomputed from field interpolation immediately afterward).
TraitsColumn descriptor for P (defaults to ParticleTraits

).

Template Parameters
GatherCallable (const ParticleSoA<P,Traits>&, std::size_t) -> Packed – packs one SoA slot's payload columns into the opaque Packed representation.
CombineSee PayloadCombine.
ScatterCallable (ParticleSoA<P,Traits>&, const MergeParticle<Packed>&) -> void – appends one surviving/merged particle back into the real SoA container.
Parameters
[in,out]a_particlesThe real particle container to merge, for every patch this rank owns.
[in,out]a_crossLevelMergeCountOptional diagnostic counter (nullptr, the default, disables it entirely): incremented once for every merge this call commits (trivial-tier or judge-side) whose two participants were on DIFFERENT AMR levels. NOT reset by this function – accumulates across a_iterateLocalTierToConvergence's internal iterations and across repeated calls if the caller keeps reusing the same counter, so it is safe to use to track cross-level activity over several rounds.
[in]a_amrAMR mesh – source of ghost masks, per-level dx, and the patch layout used to place merged particles.
[in]a_cellBudgetThe count each cell is drained down to – see NNCellBudget.
[in]a_gatherPer-slot gather callback (see Gather).
[in]a_combineOpaque-payload combine callback (see PayloadCombine).
[in]a_scatterPer-surviving-particle scatter callback (see Scatter).
[in]a_allocateIDFresh-id allocator (see IDAllocator). Mandatory – there is no universally safe default; the caller must supply a scheme that stays collision-free across every rank and every past/future call.
[in]a_iterateLocalTierToConvergenceDefault false. When false, findNearestNeighborCandidates() and resolveTrivialTier() each run once. When true, that candidate-search-plus-trivial-tier pair repeats (reusing the same ghost-fill from step 1; only which local particles are still alive changes) until a full local pass commits zero further trivial merges, and only then does generateProposals()/judgeProposals() run (still exactly once, unaffected by this option). It helps because a single pass fixes each query's edge once: if that candidate is consumed by a closer edge first, the query is left unmatched for the rest of the pass even though another partner may exist nearby. The gain is modest – re-running only helps when a particle's best available choice actually changes between iterations; a_maxFallbackCandidates below addresses the more common, stable loss.
[in]a_maxFallbackCandidatesForwarded to resolveTrivialTier() (see its docs). Default 0 (disabled). Lets a query blocked from a good local partner (because that partner is already "busy" with its own outgoing commitment – a condition iteration alone does not clear) fall back to its next nearest candidates. A single fallback recovers most of the achievable improvement.
[in]a_maxCellDistanceForwarded to resolveTrivialTier() (see its docs). std::nullopt (default) disables it. A physical cap, in whole grid cells, on how far apart two particles may be and still merge – e.g. 1 permits merging within the same cell or any Moore-adjacent cell, never farther. Composes with the options above.
[in]a_ghostWidthWidth, in whole cells, of the particle ghost halo the CALLER has already filled and of the registered particle ghost mask this function fetches to compute boundary-exposure. Must equal the filled/registered width and be identical on every rank – that is what makes a trivially-merged (non-exposed) pair provably invisible to every other patch, so it is the load-bearing invariant against double-merging. Double-merge safety does NOT depend on a_maxCellDistance; a_ghostWidth only needs to be >= a_maxCellDistance for cross-patch merges to actually reach that far (a candidate that many cells across a boundary is a merge candidate only if it lies inside the halo). Default 1, which – with the default unbounded a_maxCellDistance – caps cross-patch merges at one cell.
[in]a_isPositionValidForwarded to resolveTrivialTier()/judgeProposals() (see PositionValid). Default always-true. Composes with the options above – e.g. an embedded-boundary point query for an EB-aware caller (see ItoSolver::mergeSuperparticlesNearestNeighbor).
[in]a_onMergeCommittedOptional per-merge diagnostic callback (see MergeCommittedCallback); default is a no-op.

◆ partitionParticleWeights()

template<typename T , typename >
std::vector< T > ParticleManagement::partitionParticleWeights ( const T  a_numPhysicalParticles,
const T  a_maxCompParticles 
)
inlinenoexcept

Partition particle weights among a number of computational particles.

Parameters
[in]a_numPhysicalParticlesNumber of physical particles.
[in]a_maxCompParticlesMaximum number of computational particles
Returns
Return value

◆ removePhysicalParticles()

template<typename P , typename Traits , typename T , typename >
void ParticleManagement::removePhysicalParticles ( ParticleSoA< P, Traits > &  a_particles,
const T  a_numPhysPartToRemove 
)
inlinenoexcept

Remove physical particles from an SoA container (operates on the container-owned weight column).

Parameters
[in,out]a_particlesInput SoA particle container.
[in]a_numPhysPartToRemoveNumber of physical particles to remove.
Note
May fail if particle weights are not (close to) integers.
Template Parameters
PPayload type.
TraitsColumn descriptor for the payload.
TIntegral type for the physical-particle count.