|
chombo-discharge
|
Internal-only implementation details of the ParticleManagement API. More...
Classes | |
| struct | KDLeaf |
| One leaf of a kd tree: a contiguous index range into the (in-place reordered) particle buffer, plus its axis-aligned bounding box. More... | |
| struct | NNCellKeyHasher |
| Hash functor for NNCellKey – REQUIRED whenever NNCellKey is used as a hash-map key; NNCellKey must never be used as an ordered (std::map or std::set) key. More... | |
| struct | NNFallbackCandidate |
| One additional ranked candidate beyond a query's primary nearest neighbor. More... | |
| struct | NNMergeEdge |
| One candidate merge edge: a single particle's own nearest-neighbor query result. More... | |
| struct | NNMergeProposal |
| A cross-patch merge proposal: one particle's own nearest-neighbor query result, bundled as a self-contained, atomic pair for shipment to whichever rank owns the candidate. More... | |
| struct | NNMergeResult |
| One resolved merge: the two consumed particles' ids and the newly created merged particle (not yet placed into any patch – see placeMergedParticles()). More... | |
| struct | NNMergeVerdict |
| The judge's reply to one proposal. More... | |
| struct | NNPooledParticle |
| Pooled per-round record for one particle: the particle plus its cached boundary-exposure flag. More... | |
Typedefs | |
| using | NNCellKey = std::pair< int, IntVect > |
| A cell key together with the AMR level it was computed on – see cellKeyOf()'s warning. | |
Functions | |
| template<typename Packed > | |
| void | buildKDQuotaLeaves (std::vector< MergeParticle< Packed > > &a_particles, FArrayBox &a_used, std::vector< KDLeaf > &a_leaves, const int a_ppc, const Real a_splitWeightLeafDx, const RealVect &a_dx, const RealVect &a_probLo, const FArrayBox &a_cellCounts) noexcept |
| Build one whole-patch kd tree: partition a_particles by position into leaves, each of which becomes exactly one super-particle, subject to a live per-cell leaf quota of a_ppc. | |
| template<typename T > | |
| std::vector< T > | kdExchangeByRank (const std::vector< std::vector< T > > &a_sendByRank) |
| Generic Alltoallv-style exchange for a trivially-copyable record type: send a per-destination-rank bucket, return the flat concatenation of everything received. | |
| template<typename Packed > | |
| void | kdBBox (RealVect &a_boxLo, RealVect &a_boxHi, const std::vector< MergeParticle< Packed > > &a_particles, const std::size_t a_lo, const std::size_t a_hi) noexcept |
| Axis-aligned bounding box of particles[a_lo, a_hi). | |
| Real | kdMaxAxisSpan (const RealVect &a_boxLo, const RealVect &a_boxHi, const RealVect &a_dx) noexcept |
| Largest per-axis extent of [a_boxLo,a_boxHi], expressed as a fraction of that axis's own cell width. | |
| IntVect | kdCellKeyOf (const RealVect &a_position, const RealVect &a_probLo, const RealVect &a_dx) noexcept |
| The unclamped, position-derived cell index a physical position falls in. | |
| bool | kdIntVectLess (const IntVect &a_lhs, const IntVect &a_rhs) noexcept |
| Strict weak ordering over IntVect, lexicographic component-wise. | |
| template<typename Packed > | |
| void | kdFillCellHistogram (FArrayBox &a_counts, const std::vector< MergeParticle< Packed > > &a_particles, const RealVect &a_probLo, const RealVect &a_dx) noexcept |
| Tally a per-cell particle-count histogram over one patch's gathered particles. | |
| template<typename Packed > | |
| std::size_t | kdSplitCountMedian (std::vector< MergeParticle< Packed > > &a_particles, const std::size_t a_lo, const std::size_t a_hi, const int a_axis) noexcept |
| Split particles[a_lo,a_hi) in place by the longest axis at the count-median – the sole split rule used by buildKDQuotaLeaves(). | |
| template<typename Packed > | |
| std::size_t | kdSplitWeightMedian (std::vector< MergeParticle< Packed > > &a_particles, const std::size_t a_lo, const std::size_t a_hi, const int a_axis) noexcept |
| Split particles[a_lo,a_hi) in place by the longest axis at the WEIGHT median – the plane with half this node's total particle weight on either side. | |
| void | kdBoxRealBounds (RealVect &a_boxLo, RealVect &a_boxHi, const Box &a_box, const RealVect &a_dx, const RealVect &a_probLo) noexcept |
| Real-space bounds of a box: the half-open region [lo, hi) that its cells cover. | |
| void | kdCheckCentroid (const RealVect &a_centroid, const Real a_totalWeight, const RealVect &a_boxLo, const RealVect &a_boxHi) noexcept |
| Debug-only sanity check on a merged particle's position: a weighted centroid of positions that all lie within [a_boxLo,a_boxHi], using strictly positive weights, is mathematically guaranteed to itself lie within that same box. | |
| IntVect | cellKeyOf (const RealVect &a_position, const RealVect &a_probLo, const RealVect &a_dx) noexcept |
| The unclamped, position-derived cell index a given physical position falls in. | |
| Real | nnMergeDistSquared (const RealVect &a_a, const RealVect &a_b) noexcept |
| Squared Euclidean distance between two physical positions. | |
| int | nnMergeChebyshevCellDistance (const IntVect &a_a, const IntVect &a_b) noexcept |
| Chebyshev (max-per-axis) distance between two cell-index vectors – see CD_NearestNeighborParticleMerge.H's a_maxCellDistance docs. | |
| bool | nnMergeCrossLevelTooFar (const RealVect &a_posA, const int a_levelA, const RealVect &a_posB, const int a_levelB, const RealVect &a_probLo, const std::vector< RealVect > &a_dxByLevel, const int a_maxCellDistance) noexcept |
| a_maxCellDistance check for two positions that may be on DIFFERENT AMR levels. | |
| void | nnMergeWeightedCentroid (RealVect &a_mergedPos, Real &a_mergedWeight, const RealVect &a_posA, const Real a_weightA, const RealVect &a_posB, const Real a_weightB) noexcept |
| Weighted-centroid merge of two positions/weights – shared by resolveTrivialTier() and judgeProposals() so the formula is written exactly once. | |
| Real | nnMergePointToCellDistSquared (const RealVect &a_point, const IntVect &a_cell, const RealVect &a_probLo, const RealVect &a_dx) noexcept |
| Squared distance from a physical point to a cell's axis-aligned bounding box (0 if the point is inside). | |
| template<typename T > | |
| std::vector< T > | nnMergeExchangeByRank (const std::vector< std::vector< T > > &a_sendByRank) |
| Generic alltoallv-style exchange: send a per-destination-rank bucket of trivially- copyable records, return the flat concatenation of everything received. | |
| template<typename Packed > | |
| bool | nnMergeAnyCellCrowded (const std::vector< MergeParticle< Packed > > &a_localValid, const std::vector< MergeParticle< Packed > > &a_ghosts, const RealVect &a_probLo, const RealVect &a_dx, const NNCellBudget &a_cellBudget) noexcept |
| Exact per-cell crowding pre-filter shared by buildNNSpatialIndex()/buildNNCellSpatialIndex(): is any cell in this patch's local+ghost particle set over its a_cellBudget? | |
| template<class P , Real P::* weight, RealVect P::* position> | |
| void | buildEqualWeightKDLeaves (const std::vector< P > &a_particles, const int a_maxLeaves, const BinaryParticleReconcile< P > &a_particleReconcile, std::vector< std::pair< const P *, const P * > > &a_leaves) noexcept |
| Build an equal-weight KD partition of a list of particles and return the leaf particle ranges. | |
| template<class P , RealVect P::* position> | |
| void | mergeAdjacentNearest (std::vector< P > &a_particles, const std::size_t a_target, const std::function< void(P &, const P &)> &a_combine) noexcept |
| Merge an already spatially-ordered particle list down to a target count by repeatedly combining the nearest adjacent pair. | |
| template<typename T , typename > | |
| T | partitionParticles (const T a_numParticles) |
| Partition particles so that all MPI rank draw a cumulative number of particles equal to a_numParticles. | |
Internal-only implementation details of the ParticleManagement API.
Internal-only implementation details of the ParticleManagement API (continued).
These helpers have no external callers; they are the algorithms sitting behind the public factory/wrapper functions (make*Merger, partitionParticleWeights) and are not part of the public interface. Public callers qualify them with detail::.
Reopens the detail namespace introduced above to house partitionParticles alongside the other internal helpers; see the first detail block for the full rationale. Not part of the public interface – public callers qualify with detail::.
| using ParticleManagement::detail::NNCellKey = typedef std::pair<int, IntVect> |
A cell key together with the AMR level it was computed on – see cellKeyOf()'s warning.
Two NNCellKeys are equal only if BOTH the level and the cell index match; a coarse- level cell and an unrelated fine-level cell that happen to share the same bare IntVect are correctly kept distinct because their level differs. This is the key type for every live per-cell count pooled across multiple levels (see a_liveCellCount in resolveTrivialTier()/ judgeProposals()/findNearestNeighborCandidates()).
|
inlinenoexcept |
Build an equal-weight KD partition of a list of particles and return the leaf particle ranges.
Partitions the input particles into at most a_maxLeaves spatial groups ("leaves") whose total weights are as equal as possible (they differ by at most one physical particle between sibling nodes). At each split the particles are partitioned along the longest bounding-box axis at the weight-median; the median particle may itself be split so the two halves balance, in which case a_particleReconcile fixes up the daughters' non-weight members. This is the partitioning primitive behind super-particle merging: the caller reduces each returned leaf range to a single merged particle.
Implementation note: the tree is built in a flat, reusable per-thread scratch arena (two ping-ponged contiguous particle buffers + a flat range list), so there is no per-node heap allocation, no pointer chasing, and the scratch is reused across calls on the same thread.
Template parameters: P -> Particle type exposing the weight/position data members below P::*weight -> Pointer to the Real data member holding the particle weight P::*position -> Pointer to the RealVect data member holding the particle position.
P must be an AoS-style particle type carrying weight and position as data members – MergeParticle is the one this library provides, and the one every in-tree caller uses. ParticleSoA payload types (ItoParticle, ...) store those two in the container rather than the payload, so pack the columns into such an intermediary AoS structure first.
| [in] | a_particles | Input particles (copied into internal scratch; not modified). |
| [in] | a_maxLeaves | Maximum number of leaf nodes (the per-cell target particle count). |
| [in] | a_particleReconcile | Reconciliation callback applied to the two daughters when the median is split. |
| [out] | a_leaves | Filled with one {begin,end} pointer pair per leaf. |
|
inlinenoexcept |
Build one whole-patch kd tree: partition a_particles by position into leaves, each of which becomes exactly one super-particle, subject to a live per-cell leaf quota of a_ppc.
a_particles is reordered IN PLACE, so each returned leaf's [lo,hi) range is contiguous in the reordered buffer; no particle is ever copied or split synthetically.
A leaf collapses to exactly ONE super-particle at its own weighted centroid, so the number of leaves whose centroid falls in a cell is that cell's post-merge population. That count is tracked live in a per-cell map while splitting, and a split that would push a cell past a_ppc is refused. Splitting takes the heaviest candidate leaf first and stops when nothing may legally be split.
Splits are along the longest real-space axis and are deliberately UNSNAPPED to the grid, so a leaf may straddle a cell face and draw members from either side. The split plane is the count median while the node is wider than a_splitWeightLeafDx cell widths, and the weight median once it is narrower: above that scale a split apportions leaves between cells, which is a question of counts; below it the weight median drives the resulting super-particles toward equal weight.
The quota constrains only leaves that can actually merge. mergeKDCarve() and mergeKDPatch() veto any leaf wider than s_kdMaxLeafExtent and leaves its members behind individually, so such a leaf costs its cell its full member count rather than one particle; the quota therefore does not block splitting it further. As a result a cell can finish slightly above a_ppc when a leaf is both too wide to merge and in a cell already at quota, since both available choices overshoot.
Particles in cells at or below a_ppc are excluded from the partition and emitted as singleton leaves. A singleton never merges (the merge threshold is two members), so an uncrowded cell cannot be drained by a neighbour's merge.
| Packed | See MergeParticle. |
| [in,out] | a_particles | Particles to partition; reordered in place. |
| [in,out] | a_used | Live per-cell leaf quota for this patch, zeroed on entry. Its box must cover every cell a leaf centroid can fall in, i.e. it needs at least one ghost cell. |
| [out] | a_leaves | Every resulting leaf, in no particular order. |
| [in] | a_ppc | Target particles per cell, enforced here as a per-cell ceiling on leaf count. |
| [in] | a_splitWeightLeafDx | Leaf size, in cell widths, at or below which the split plane switches from the count median to the weight median. 0 disables the weight median. |
| [in] | a_dx | This patch's own cell spacing. |
| [in] | a_probLo | Physical position of the domain's low corner, for computing cell indices. |
| [in] | a_cellCounts | Per-cell TRUE particle-count histogram (see kdFillCellHistogram()). A cell is crowded, and its particles eligible to merge, iff its count exceeds a_ppc. A cell outside this box reads as 0 (empty): unlike a_used, whose box coverage is a hard precondition, this lookup guards rather than asserts, since a particle's own cell is always in-box but the guard costs nothing and documents the intent. |
|
inlinenoexcept |
The unclamped, position-derived cell index a given physical position falls in.
The cell-key convention used throughout for the crowding trigger and a_maxCellDistance. Not clamped to any patch's box, so a ghost buckets into its true origin cell and particles in different patches' local frames stay directly comparable.
| [in] | a_position | Physical position. |
| [in] | a_probLo | Physical position of the domain's low corner. |
| [in] | a_dx | Cell spacing (per direction) of the position's OWN level. |
|
inlinenoexcept |
Axis-aligned bounding box of particles[a_lo, a_hi).
| [out] | a_boxLo | Low corner. |
| [out] | a_boxHi | High corner. |
| [in] | a_particles | Particle buffer. |
| [in] | a_lo | Start of the range (inclusive). |
| [in] | a_hi | End of the range (exclusive). |
|
inlinenoexcept |
Real-space bounds of a box: the half-open region [lo, hi) that its cells cover.
Half-open on purpose – the high corner is the OUTER face of the last cell, so testing pos >= lo && pos < hi per direction partitions space between abutting boxes with no gap and no double-cover, which is exactly the "is this particle still in its own patch" test both callers make.
| [out] | a_boxLo | Low corner (inclusive). |
| [out] | a_boxHi | High corner (exclusive). |
| [in] | a_box | Box whose bounds are wanted. |
| [in] | a_dx | Grid spacing at the box's level. |
| [in] | a_probLo | Lower-left corner of the physical domain. |
|
inlinenoexcept |
The unclamped, position-derived cell index a physical position falls in.
Same convention as the nearest-neighbor merge's cellKeyOf() – not clamped to any patch's box, so positions in different patches' local frames stay directly comparable.
| [in] | a_position | Physical position. |
| [in] | a_probLo | Physical position of the domain's low corner. |
| [in] | a_dx | Cell spacing. |
|
inlinenoexcept |
Debug-only sanity check on a merged particle's position: a weighted centroid of positions that all lie within [a_boxLo,a_boxHi], using strictly positive weights, is mathematically guaranteed to itself lie within that same box.
CH_assert this explicitly (rather than trust the arithmetic silently) so that a stray sign error, a mis-set/negative weight, or a wrong id feeding the wrong position into the accumulation surfaces immediately as an assertion in a DEBUG build, instead of silently producing a merged superparticle placed somewhere non-physical. A relative tolerance accounts for floating- point round-off in the summation, not for any real violation.
| [in] | a_centroid | Computed weighted centroid. |
| [in] | a_totalWeight | Sum of weights used to compute a_centroid. |
| [in] | a_boxLo | Low corner of the bounding box every contributing position lies within. |
| [in] | a_boxHi | High corner of the bounding box every contributing position lies within. |
|
inline |
Generic Alltoallv-style exchange for a trivially-copyable record type: send a per-destination-rank bucket, return the flat concatenation of everything received.
A deliberate, self-contained duplicate of the nearest-neighbor merge's nnMergeExchangeByRank() (same MPI_Alltoall/Alltoallv shape) rather than a shared dependency – that function lives in a header that transitively pulls in EBGeometry, which this file must not depend on (see this file's own
). In a serial (non-MPI) build, or with a single rank, returns this rank's own bucket unchanged.
| T | Trivially-copyable record type (KDClaim, KDVerdict, KDCommit, or MergeParticle<Packed> with Packed itself trivially copyable). |
| [in] | a_sendByRank | One bucket per destination rank (size == number of ranks). |
|
inlinenoexcept |
Tally a per-cell particle-count histogram over one patch's gathered particles.
Built from every particle's own position, so it is unaffected by how buildKDQuotaLeaves() later partitions the space – a node-local count is NOT a safe substitute, see that function's own docs for why. Shared by mergeKDCarve()/mergeKDPatch()/mergeKDInterior(): all three need the same ground truth, and the invariant that it comes from true positions rather than from the partition is easy to break if each keeps its own copy.
a_counts must include at least one ghost cell, since a gathered ghost sits up to one cell outside the patch's own box.
The count is stored in a_counts' Real elements but is always a non-negative integer. A double represents every integer exactly up to 2^53, so consumers – e.g. the crowding test in buildKDQuotaLeaves() – may static_cast<int> it losslessly for any realistic per-cell count.
| Packed | See MergeParticle. |
| [out] | a_counts | Per-cell counts, zeroed here before tallying. |
| [in] | a_particles | Gathered particles for this patch. |
| [in] | a_probLo | Physical position of the domain's low corner. |
| [in] | a_dx | This patch's own cell spacing. |
|
inlinenoexcept |
Strict weak ordering over IntVect, lexicographic component-wise.
Used to sort/binary-search the per-patch, per-cell TRUE particle-count histogram (see buildKDQuotaLeaves()'s own docs) – IntVect has no built-in total order.
| [in] | a_lhs | Left-hand cell index. |
| [in] | a_rhs | Right-hand cell index. |
|
inlinenoexcept |
Largest per-axis extent of [a_boxLo,a_boxHi], expressed as a fraction of that axis's own cell width.
The correctness backstop buildKDQuotaLeaves(), mergeKDCarve() and mergeKDPatch() rely on to decide whether a leaf is compact enough to merge: unlike an aggregate VOLUME ratio (a product across axes), this cannot be fooled by an anisotropic (long-and-thin) bounding box – e.g. a box 5 cells long in one axis and 0.2 cells wide in another has volume ratio 1.0 (deceptively "one cell's worth") but a maxAxisSpan of 5.0 (correctly flagged as still spanning several cells). Particles hugging a curved EB surface, or funneled along a narrow L-shaped coarse-fine strip, produce exactly this kind of elongated cluster.
| [in] | a_boxLo | Low corner. |
| [in] | a_boxHi | High corner. |
| [in] | a_dx | Cell spacing. |
|
inlinenoexcept |
Split particles[a_lo,a_hi) in place by the longest axis at the count-median – the sole split rule used by buildKDQuotaLeaves().
The split point is the position-sorted median particle (via nth_element), balancing COUNT as evenly as possible – mirrors equal_weight_kd's own single-cell logic, but by count, never by weight, and with NO synthetic particle splitting (a particle is never divided across the two children). nth_element divides the range in half by count however the positions are distributed, so a tightly clustered node still splits evenly.
| [in,out] | a_particles | Particle buffer; reordered in [a_lo,a_hi). |
| [in] | a_lo | Start of the range (inclusive). |
| [in] | a_hi | End of the range (exclusive); a_hi - a_lo must be >= 2. |
| [in] | a_axis | Split axis – the node's longest box axis, precomputed once by the caller (which already needs the box for the span test) rather than re-derived here from a second bounding box. |
|
inlinenoexcept |
Split particles[a_lo,a_hi) in place by the longest axis at the WEIGHT median – the plane with half this node's total particle weight on either side.
Used instead of the count median so that the resulting leaves carry roughly equal total weight, and therefore so do the super-particles they collapse into. Uniform super-particle weight is what keeps statistical noise down, and is the defining property of the equal_weight_kd merge.
Unlike equal_weight_kd this NEVER divides a particle synthetically, so exact equality is not attainable: once a single particle's weight exceeds a leaf's target weight, that particle alone sets a floor no partition can get under. The bound equal_weight_kd guarantees (max/min <= 2) is bought precisely by that synthetic division and is therefore out of reach here by construction.
A weight median can land very unevenly BY COUNT – one dominant particle carrying half the node's weight forces a 1-vs-(n-1) split. That is harmless here only because buildKDQuotaLeaves() enforces a hard per-cell leaf quota independently of how the split plane behaves: an uneven split can cost merge quality but cannot leave a cell above target. Under any of the earlier, quota-less formulations the same degeneracy was fatal.
| [in,out] | a_particles | Particle buffer; reordered in [a_lo,a_hi). |
| [in] | a_lo | Start of the range (inclusive). |
| [in] | a_hi | End of the range (exclusive); a_hi - a_lo must be >= 2. |
| [in] | a_axis | Split axis – the node's longest box axis, precomputed once by the caller (which already needs the box for the span test) rather than re-derived here from a second bounding box. |
|
inlinenoexcept |
Merge an already spatially-ordered particle list down to a target count by repeatedly combining the nearest adjacent pair.
The particles are assumed to be in a spatially coherent order (e.g. sorted along a space-filling curve by the caller), so adjacency in the list approximates spatial proximity. The closest adjacent pair (by position) is combined repeatedly until a_target particles remain. Combining the second particle into the first is delegated to a_combine (the caller decides the payload semantics, e.g. a weight-weighted centroid and energy). This does NOT split, so it cannot increase the count – it is a no-op when the list already has a_target or fewer particles (the caller handles the split direction). Operates in place on per-thread scratch (allocation-free after warmup); on return a_particles holds the surviving merged particles.
| [in,out] | a_particles | Spatially-ordered particle list; overwritten with the merged result. |
| [in] | a_target | Desired number of particles. |
| [in] | a_combine | Callback that combines the second particle into the first. |
|
inlinenoexcept |
Exact per-cell crowding pre-filter shared by buildNNSpatialIndex()/buildNNCellSpatialIndex(): is any cell in this patch's local+ghost particle set over its a_cellBudget?
A count-only tally keyed as the crowding trigger (cellKeyOf(pos, probLo, dx), local and ghost alike); checks the true per-cell maximum, not an average density (a low-average patch can still hold one over-threshold cell).
| Packed | See MergeParticle. |
| [in] | a_localValid | This patch's own full valid-particle set. |
| [in] | a_ghosts | Every ghost shipped to this patch this round. |
| [in] | a_probLo | Physical position of the domain's low corner. |
| [in] | a_dx | Cell spacing of THIS PATCH's own level. |
| [in] | a_cellBudget | The count each cell is drained down to – see NNCellBudget. |
|
inlinenoexcept |
Chebyshev (max-per-axis) distance between two cell-index vectors – see CD_NearestNeighborParticleMerge.H's a_maxCellDistance docs.
| [in] | a_a | First cell index. |
| [in] | a_b | Second cell index. |
|
inlinenoexcept |
a_maxCellDistance check for two positions that may be on DIFFERENT AMR levels.
Raw cell indices computed with two different dx values are not directly comparable, so both positions are re-expressed in the finer of the two levels' cell-size units (higher AMR level = finer) before taking the Chebyshev distance. Reduces to the same-level case when a_levelA == a_levelB.
| [in] | a_posA | First participant's position. |
| [in] | a_levelA | First participant's AMR level. |
| [in] | a_posB | Second participant's position. |
| [in] | a_levelB | Second participant's AMR level. |
| [in] | a_probLo | Physical position of the domain's low corner. |
| [in] | a_dxByLevel | Cell spacing per AMR level. |
| [in] | a_maxCellDistance | The cap (see CD_NearestNeighborParticleMerge.H's a_maxCellDistance docs). |
|
inlinenoexcept |
Squared Euclidean distance between two physical positions.
| [in] | a_a | First position. |
| [in] | a_b | Second position. |
|
inline |
Generic alltoallv-style exchange: send a per-destination-rank bucket of trivially- copyable records, return the flat concatenation of everything received.
Packs one bucket per rank and exchanges via MPI_Alltoall (byte counts) + MPI_Alltoallv (payload). In a serial (non-MPI) build, or with a single rank, this returns this rank's own bucket unchanged.
| T | Trivially-copyable record type (NNMergeProposal<Packed>, NNMergeVerdict, or MergeParticle<Packed> – all satisfy this given Packed itself is required to). |
| [in] | a_sendByRank | One bucket per destination rank (size == number of ranks). |
|
inlinenoexcept |
Squared distance from a physical point to a cell's axis-aligned bounding box (0 if the point is inside).
| [in] | a_point | Physical position. |
| [in] | a_cell | Cell index. |
| [in] | a_probLo | Physical position of the domain's low corner. |
| [in] | a_dx | Cell spacing. |
|
inlinenoexcept |
Weighted-centroid merge of two positions/weights – shared by resolveTrivialTier() and judgeProposals() so the formula is written exactly once.
| [out] | a_mergedPos | Weighted-centroid position. |
| [out] | a_mergedWeight | Summed weight. |
| [in] | a_posA | First particle's position. |
| [in] | a_weightA | First particle's weight. |
| [in] | a_posB | Second particle's position. |
| [in] | a_weightB | Second particle's weight. |
|
inline |
Partition particles so that all MPI rank draw a cumulative number of particles equal to a_numParticles.
| [in] | a_numParticles | Total number of particles to be drawn. |