19#include <unordered_map>
23#include <Math/SMatrix.h>
24#include <Math/SVector.h>
36constexpr float TukeyC = 4.685f;
37constexpr float TukeyC2 = TukeyC * TukeyC;
38constexpr float InitialScale2 = 5.f;
39constexpr float MinScale2 = 1.f;
40constexpr float MedianToSigma = 1.4826f;
41constexpr float VertexShiftZTol = 0.01f;
42constexpr float VertexShiftR2Tol = 1.e-4f;
43constexpr int MaxFitIterations = 10;
44constexpr int MaxSeedsPerCluster = 32;
45constexpr float MinRelativePeakSupport = 0.1f;
46constexpr int MaxHistogramBins = 0x7fff;
47constexpr float TieTolerance = 1e-5f;
52 const auto symTime =
line.mTime.makeSymmetrical();
53 tCenter = symTime.getTimeStamp();
55 if (!Line::getZAtBeam(line,
beamX,
beamY, zBeam)) {
59 if (!std::isfinite(zBeam) || o2::gpu::CAMath::Abs(zBeam) >
maxZ) {
72 explicit VertexSeed(
const std::shared_ptr<BoundedMemoryResource>& mr) :
contributors(mr.get()),
assigned(mr.get()) {}
80 bool isUsableSeed() const noexcept
86void compactSeeds(bounded_vector<VertexSeed>& seeds)
88 seeds.erase(std::remove_if(seeds.begin(), seeds.end(), [](
const VertexSeed& seed) {
89 return !seed.isUsableSeed();
95 explicit Histogram2D(
const std::shared_ptr<BoundedMemoryResource>& mr) :
bins(mr.
get()) {}
105 int getIndex(
const int tBin,
const int zBin)
const noexcept
107 return (tBin *
nZBins) + zBin;
110 std::pair<int, int> decodeIndex(
const int index)
const noexcept
115 int getTimeBin(
const float time)
const noexcept
117 if (
time < timeMin) {
120 const auto bin =
static_cast<int>((
time -
timeMin) / timeBinSize);
121 return (bin >= 0 && bin < nTimeBins) ? bin : -1;
124 int getZBin(
const float z)
const noexcept
129 const auto bin =
static_cast<int>((
z -
zMin) / zBinSize);
130 return (bin >= 0 && bin < nZBins) ? bin : -1;
133 void fill(
const float time,
const float z,
const float weight)
noexcept
135 const auto tBin = getTimeBin(
time);
136 const auto zBin = getZBin(
z);
137 if (tBin < 0 || zBin < 0) {
143 int findPeakBin() const noexcept
145 float bestWeight = 0.f;
147 for (
int index = 0; index < static_cast<int>(
bins.size()); ++
index) {
148 if (bins[
index] > bestWeight) {
156 void suppressBin(
const int index)
noexcept
163 void suppressNeighborhood(
const int index,
const int radiusTime,
const int radiusZ)
noexcept
168 const auto [tBin, zBin] = decodeIndex(
index);
169 for (
int dt = -radiusTime; dt <= radiusTime; ++dt) {
170 const auto tt = tBin + dt;
171 if (tt < 0 || tt >= nTimeBins) {
174 for (
int dz = -radiusZ; dz <= radiusZ; ++dz) {
175 const auto zz = zBin + dz;
176 if (zz < 0 || zz >= nZBins) {
184 float getNeighborhoodSum(
const int index,
const int radiusTime,
const int radiusZ)
const noexcept
189 const auto [tBin, zBin] = decodeIndex(
index);
191 for (
int dt = -radiusTime; dt <= radiusTime; ++dt) {
192 const auto tt = tBin + dt;
193 if (tt < 0 || tt >= nTimeBins) {
196 for (
int dz = -radiusZ; dz <= radiusZ; ++dz) {
197 const auto zz = zBin + dz;
198 if (zz < 0 || zz >= nZBins) {
210 float getTimeBinCenter(
const int tBin)
const noexcept
215 float getZBinCenter(
const int zBin)
const noexcept
217 return zMin + ((
static_cast<float>(zBin) + 0.5f) *
zBinSize);
220 TimeEstBC getTimeInterval(
const int tBin)
const noexcept
222 const auto lowFloat =
timeMin + (
static_cast<float>(tBin) * timeBinSize);
224 const auto low = std::max<double>(0., std::floor(lowFloat));
225 const auto high = std::max(low + 1., (
double)std::ceil(highFloat));
226 constexpr auto maxTS = std::numeric_limits<TimeStampType>::max();
227 const auto clampedLow = std::min<double>(low, maxTS - 1.);
228 const auto width = std::min<double>(high - clampedLow, std::numeric_limits<TimeStampErrorType>::max());
232 TimeEstBC getTimeNeighborhoodInterval(
const int tBin,
const int radius)
const noexcept
234 const auto lowBin = std::max(0, tBin - radius);
235 const auto highBin = std::min(nTimeBins - 1, tBin + radius);
236 const auto lowFloat =
timeMin + (
static_cast<float>(lowBin) * timeBinSize);
237 const auto highFloat =
timeMin + (
static_cast<float>(highBin + 1) * timeBinSize);
238 const auto low = std::max<double>(0., std::floor(lowFloat));
239 const auto high = std::max(low + 1., (
double)std::ceil(highFloat));
240 constexpr auto maxTS = std::numeric_limits<TimeStampType>::max();
241 const auto clampedLow = std::min<double>(low, maxTS - 1.);
242 const auto width = std::min<double>(high - clampedLow, std::numeric_limits<TimeStampErrorType>::max());
250 SeedHistogram(std::span<const int>
members,
251 std::span<const LineRef> lineRefs,
252 std::span<const Line> lines,
253 const Settings& settings)
254 : mMembers(
members), mLineRefs(lineRefs), mSeedMemberRadiusTime(settings.seedMemberRadiusTime), mSeedMemberRadiusZ(settings.seedMemberRadiusZ), mMemoryPool(settings.memoryPool), mHistogram(mMemoryPool)
256 const auto zBinSize = 0.25f * settings.clusterCut;
259 float minZ = std::numeric_limits<float>::max();
260 float maxZ = std::numeric_limits<float>::lowest();
261 float minTime = std::numeric_limits<float>::max();
262 float maxTime = std::numeric_limits<float>::lowest();
263 for (
const auto lineRefIdx : mMembers) {
266 minTime = std::min(minTime, mLineRefs[lineRefIdx].
tCenter);
267 maxTime = std::max(maxTime, mLineRefs[lineRefIdx].
tCenter);
270 const auto dz = std::max(0.f,
maxZ -
minZ);
271 const auto dt = std::max(0.f, maxTime - minTime);
272 mHistogram.nZBins = 1 +
static_cast<int>(dz /
zBinSize);
273 mHistogram.nTimeBins = 1 +
static_cast<int>(dt /
timeBinSize);
274 if (mHistogram.nTimeBins * mHistogram.nZBins > MaxHistogramBins) {
275 if (mHistogram.nTimeBins > mHistogram.nZBins) {
276 mHistogram.nTimeBins = std::max(1, (MaxHistogramBins - 1) / std::max(1, mHistogram.nZBins));
278 mHistogram.nZBins = std::max(1, (MaxHistogramBins - 1) / std::max(1, mHistogram.nTimeBins));
282 mHistogram.timeBinSize = std::max(
timeBinSize, dt / (
float)std::max(1, mHistogram.nTimeBins));
283 mHistogram.zBinSize = std::max(
zBinSize, dz / (
float)std::max(1, mHistogram.nZBins));
284 const auto paddedTime = 0.5f * ((float)mHistogram.nTimeBins * mHistogram.timeBinSize - dt);
285 const auto paddedZ = 0.5f * ((float)mHistogram.nZBins * mHistogram.zBinSize - dz);
286 mHistogram.timeMin = minTime - paddedTime;
287 mHistogram.zMin =
minZ - paddedZ;
288 mHistogram.bins.assign((
size_t)mHistogram.nTimeBins * (size_t)mHistogram.nZBins, 0.f);
290 for (
const auto lineRefIdx : mMembers) {
291 mHistogram.fill(mLineRefs[lineRefIdx].
tCenter, mLineRefs[lineRefIdx].
zBeam, 1.f);
295 int findPeakBin() const noexcept
297 return mHistogram.findPeakBin();
300 float getPeakSupport(
const int peakIndex)
const noexcept
302 return mHistogram.getNeighborhoodSum(peakIndex, mSeedMemberRadiusTime, mSeedMemberRadiusZ);
305 bounded_vector<int> collectLocalMembers(
const int peakIndex,
const int radiusTime,
const int radiusZ)
const
307 bounded_vector<int> localMembers(mMemoryPool.get());
308 localMembers.reserve(mMembers.size());
309 const auto [timeBin, zBin] = mHistogram.decodeIndex(peakIndex);
310 for (
const auto lineRefIdx : mMembers) {
311 const auto memberTimeBin = mHistogram.getTimeBin(mLineRefs[lineRefIdx].
tCenter);
312 const auto memberZBin = mHistogram.getZBin(mLineRefs[lineRefIdx].
zBeam);
313 if (memberTimeBin < 0 || memberZBin < 0) {
316 if (o2::gpu::GPUCommonMath::Abs(memberTimeBin - timeBin) > radiusTime) {
319 if (o2::gpu::GPUCommonMath::Abs(memberZBin - zBin) > radiusZ) {
322 localMembers.push_back(lineRefIdx);
327 TimeEstBC getPeakTimeInterval(
const int peakIndex,
const int radius = 0) const noexcept
329 return mHistogram.getTimeNeighborhoodInterval(mHistogram.decodeIndex(peakIndex).first, radius);
332 float getPeakZCenter(
const int peakIndex)
const noexcept
334 return mHistogram.getZBinCenter(mHistogram.decodeIndex(peakIndex).second);
337 void suppressPeak(
const int peakIndex)
noexcept
339 mHistogram.suppressBin(peakIndex);
342 void suppressPeakNeighborhood(
const int peakIndex)
noexcept
344 mHistogram.suppressNeighborhood(peakIndex, mSeedMemberRadiusTime, mSeedMemberRadiusZ);
348 float medianTimeError(std::span<const Line> lines)
const
350 bounded_vector<float> errors(mMemoryPool.get());
351 errors.reserve(mMembers.size());
352 for (
const auto lineRefIdx : mMembers) {
353 errors.push_back(
static_cast<float>(lines[mLineRefs[lineRefIdx].
lineIndex].mTime.getTimeStampError()));
355 std::sort(errors.begin(), errors.end());
356 return errors.empty() ? 1.f : std::max(1.f, errors[errors.size() / 2]);
359 std::span<const int> mMembers;
360 std::span<const LineRef> mLineRefs;
361 int mSeedMemberRadiusTime = 1;
362 int mSeedMemberRadiusZ = 2;
363 std::shared_ptr<BoundedMemoryResource> mMemoryPool;
364 Histogram2D mHistogram;
367float updateScale2(
const std::span<const float> chi2s,
const std::shared_ptr<BoundedMemoryResource>& mr)
noexcept
373 bounded_vector<float> sorted(chi2s.begin(), chi2s.end(), mr.get());
374 std::sort(sorted.begin(), sorted.end());
375 const auto median = sorted[sorted.size() / 2];
377 for (
auto&
value : sorted) {
378 value = o2::gpu::GPUCommonMath::Abs(
value - median);
380 std::sort(sorted.begin(), sorted.end());
381 const auto mad = sorted[sorted.size() / 2];
385 return std::max(MinScale2, MedianToSigma * mad);
391 void add(
const Line& line,
const float weight)
noexcept
400 for (
int i = 0;
i < 3; ++
i) {
401 for (
int j =
i;
j < 3; ++
j) {
407 for (
int i = 0;
i < 3; ++
i) {
412 bool solve(std::array<float, 3>& vertexOut)
const noexcept
414 SymMatrix3 inv{mMatrix};
415 if (!inv.InvertFast()) {
418 const auto solution = inv * mRhs;
419 vertexOut[0] =
static_cast<float>(-solution(0));
420 vertexOut[1] =
static_cast<float>(-solution(1));
421 vertexOut[2] =
static_cast<float>(-solution(2));
422 return std::isfinite(vertexOut[0]) && std::isfinite(vertexOut[1]) && std::isfinite(vertexOut[2]);
430VertexSeed fitSeed(
const VertexSeed& initialSeed,
432 std::span<const LineRef> lineRefs,
433 std::span<const Line> lines,
434 const std::shared_ptr<BoundedMemoryResource>& mr,
435 const float pairCut2)
438 seed.vertex = initialSeed.vertex;
439 seed.time = initialSeed.time;
440 seed.scale2 = initialSeed.scale2;
442 seed.contributors.clear();
443 seed.assigned.clear();
448 for (
int iteration = 0; iteration < MaxFitIterations; ++iteration) {
450 TimeEstBC commonTime{};
451 bool hasCommonTime =
false;
453 const auto scale2 = std::max(
seed.scale2, MinScale2);
454 const auto tukeyFactor = 1.f / (
scale2 * TukeyC2);
456 for (
const auto lineRefIdx :
members) {
457 const auto lineIdx = lineRefs[lineRefIdx].lineIndex;
458 const auto&
line = lines[lineIdx];
459 if (!
line.mTime.isCompatible(
seed.time)) {
462 if (hasCommonTime && !
line.mTime.isCompatible(commonTime)) {
473 if (!hasCommonTime) {
474 commonTime =
line.mTime;
475 hasCommonTime =
true;
477 commonTime +=
line.mTime;
481 vertexFit.add(line,
weight);
490 std::array<float, 3> updatedVertex{};
491 if (!vertexFit.solve(updatedVertex)) {
496 const auto dz = o2::gpu::GPUCommonMath::Abs(updatedVertex[2] -
seed.vertex[2]);
497 const auto oldR2 = (
seed.vertex[0] *
seed.vertex[0]) + (
seed.vertex[1] *
seed.vertex[1]);
498 const auto newR2 = (updatedVertex[0] * updatedVertex[0]) + (updatedVertex[1] * updatedVertex[1]);
499 const auto dr2 = o2::gpu::GPUCommonMath::Abs(newR2 - oldR2);
501 seed.vertex = updatedVertex;
502 seed.time = commonTime;
503 bounded_vector<float> updatedChi2s{mr.get()};
508 seed.scale2 = updateScale2(updatedChi2s, mr);
512 if (sameContributors && dz < VertexShiftZTol && dr2 < VertexShiftR2Tol) {
520size_t countSharedContributors(std::span<const int> lhs, std::span<const int> rhs)
noexcept
523 auto lhsIt =
lhs.begin();
524 auto rhsIt =
rhs.begin();
525 while (lhsIt !=
lhs.end() && rhsIt !=
rhs.end()) {
526 if (*lhsIt == *rhsIt) {
530 }
else if (*lhsIt < *rhsIt) {
539bounded_vector<int> collectCompatibleContributors(
const VertexSeed& seed,
541 std::span<const LineRef> lineRefs,
542 std::span<const Line> lines,
543 const std::shared_ptr<BoundedMemoryResource>& mr,
544 const float pairCut2)
548 for (
const auto lineRefIdx :
members) {
549 const auto lineIdx = lineRefs[lineRefIdx].lineIndex;
550 const auto&
line = lines[lineIdx];
551 if (!
line.mTime.isCompatible(
seed.time)) {
563void deduplicateSeeds(bounded_vector<VertexSeed>& seeds,
const Settings& settings)
565 if (seeds.size() < 2) {
569 std::sort(seeds.begin(), seeds.end(), [](
const VertexSeed& lhs,
const VertexSeed& rhs) {
570 if (lhs.contributors.size() != rhs.contributors.size()) {
571 return lhs.contributors.size() > rhs.contributors.size();
574 return lhs.scale2 < rhs.scale2;
576 return lhs.vertex[2] <
rhs.vertex[2];
579 const auto dedupZCut = settings.seedDedupZCut > 0.f ? settings.seedDedupZCut : 0.25f * settings.clusterCut;
580 for (
size_t i = 0;
i < seeds.size(); ++
i) {
581 auto& candidate = seeds[
i];
582 if (!candidate.isUsableSeed()) {
583 candidate.valid =
false;
586 bool duplicate =
false;
587 for (
size_t j = 0;
j <
i; ++
j) {
588 const auto& kept = seeds[
j];
589 if (!kept.isUsableSeed()) {
592 if (!candidate.time.isCompatible(kept.time)) {
595 const auto shared = countSharedContributors(candidate.contributors, kept.contributors);
596 const auto minSize = std::min(candidate.contributors.size(), kept.contributors.size());
597 const auto zDelta = o2::gpu::GPUCommonMath::Abs(candidate.vertex[2] - kept.vertex[2]);
598 const bool clearlyWorse = kept.contributors.size() > candidate.contributors.size() ||
600 const bool overlapDuplicate = shared > 0 && shared * 2 >=
minSize;
601 const bool nearbyDuplicate =
zDelta < dedupZCut && (shared > 0 || clearlyWorse);
602 if (overlapDuplicate || nearbyDuplicate) {
608 candidate.valid =
false;
614void deduplicateRefittedSeeds(bounded_vector<VertexSeed>& seeds,
const Settings& settings)
616 if (seeds.size() < 2) {
620 std::sort(seeds.begin(), seeds.end(), [](
const VertexSeed& lhs,
const VertexSeed& rhs) {
621 if (lhs.contributors.size() != rhs.contributors.size()) {
622 return lhs.contributors.size() > rhs.contributors.size();
625 return lhs.scale2 < rhs.scale2;
627 return lhs.vertex[2] <
rhs.vertex[2];
630 const auto zCut = settings.refitDedupZCut > 0.f ? settings.refitDedupZCut : 0.25f * settings.clusterCut;
631 for (
size_t i = 0;
i < seeds.size(); ++
i) {
632 auto& candidate = seeds[
i];
633 if (!candidate.isUsableSeed()) {
634 candidate.valid =
false;
637 bool duplicate =
false;
638 for (
size_t j = 0;
j <
i; ++
j) {
639 const auto& kept = seeds[
j];
640 if (!kept.isUsableSeed()) {
643 if (!candidate.time.isCompatible(kept.time)) {
646 const auto shared = countSharedContributors(candidate.contributors, kept.contributors);
647 const auto minSize = std::min(candidate.contributors.size(), kept.contributors.size());
648 const auto zDelta = o2::gpu::GPUCommonMath::Abs(candidate.vertex[2] - kept.vertex[2]);
649 const bool overlapDuplicate = shared > 0 && shared * 2 >=
minSize;
650 const bool lowSupportPair = std::min(candidate.contributors.size(), kept.contributors.size()) < 4;
651 const bool clearlyWorse = kept.contributors.size() > candidate.contributors.size() ||
653 const bool geometricDuplicate =
zDelta < zCut && (lowSupportPair || clearlyWorse);
654 if (overlapDuplicate || geometricDuplicate) {
660 candidate.valid =
false;
666struct OrderedComponent {
667 explicit OrderedComponent(
const std::shared_ptr<BoundedMemoryResource>& mr) :
members(mr.
get()) {}
672bounded_vector<bounded_vector<int>> buildCoarseClusters(std::span<const LineRef> lineRefs,
673 std::span<const Line> lines,
674 const Settings& settings)
676 bounded_vector<bounded_vector<int>>
clusters(settings.memoryPool.get());
677 if (lineRefs.size() < 2) {
681 bounded_vector<int> sortedByLower(lineRefs.size(), settings.memoryPool.get());
682 std::iota(sortedByLower.begin(), sortedByLower.end(), 0);
683 std::sort(sortedByLower.begin(), sortedByLower.end(), [&](
const int lhs,
const int rhs) {
684 const auto lhsLower = lines[lineRefs[lhs].lineIndex].mTime.lower();
685 const auto rhsLower = lines[lineRefs[rhs].lineIndex].mTime.lower();
686 if (lhsLower != rhsLower) {
687 return lhsLower < rhsLower;
689 return lineRefs[lhs].lineIndex < lineRefs[rhs].lineIndex;
692 const auto coarseZWindow = settings.coarseZWindow > 0.f ? settings.coarseZWindow : settings.clusterCut;
693 bounded_vector<int> parent(lineRefs.size(), settings.memoryPool.get());
694 bounded_vector<int> componentSize(lineRefs.size(), 1, settings.memoryPool.get());
695 std::iota(parent.begin(), parent.end(), 0);
696 float minZ = std::numeric_limits<float>::max();
697 float maxZ = std::numeric_limits<float>::lowest();
698 for (
const auto& lineRef : lineRefs) {
699 minZ = std::min(
minZ, lineRef.zBeam);
700 maxZ = std::max(
maxZ, lineRef.zBeam);
702 const auto nZBins = std::max(1, 1 +
static_cast<int>((
maxZ -
minZ) / coarseZWindow));
703 auto getZBin = [&](
const float z) {
704 return std::clamp(
static_cast<int>((
z -
minZ) / coarseZWindow), 0,
nZBins - 1);
707 auto findRoot = [&](
int idx) {
709 while (parent[root] != root) {
712 while (parent[idx] != idx) {
713 const auto next = parent[
idx];
720 auto unite = [&](
const int lhs,
const int rhs) {
721 auto lhsRoot = findRoot(lhs);
722 auto rhsRoot = findRoot(rhs);
723 if (lhsRoot == rhsRoot) {
726 if (componentSize[lhsRoot] < componentSize[rhsRoot]) {
727 std::swap(lhsRoot, rhsRoot);
729 parent[rhsRoot] = lhsRoot;
730 componentSize[lhsRoot] += componentSize[rhsRoot];
733 using ActiveEntry = std::pair<TimeStampType, int>;
734 bounded_vector<ActiveEntry> activeEntries(settings.memoryPool.get());
735 std::priority_queue<ActiveEntry, bounded_vector<ActiveEntry>, std::greater<>> activeByUpper(std::greater<>{}, std::move(activeEntries));
736 bounded_vector<uint8_t> activeMask(lineRefs.size(), 0, settings.memoryPool.get());
737 bounded_vector<bounded_vector<int>> activeByZBin(settings.memoryPool.get());
738 activeByZBin.reserve(
nZBins);
739 for (
int iBin = 0; iBin <
nZBins; ++iBin) {
740 activeByZBin.emplace_back();
742 for (
const auto lineRefIdx : sortedByLower) {
743 const auto& lineRef = lineRefs[lineRefIdx];
744 const auto&
line = lines[lineRef.lineIndex];
745 const auto currentLower =
line.mTime.lower();
747 while (!activeByUpper.empty() && activeByUpper.top().first < currentLower) {
748 activeMask[activeByUpper.top().second] = 0;
752 const auto zBin = getZBin(lineRef.zBeam);
753 for (
int neighborBin = std::max(0, zBin - 1); neighborBin <= std::min(
nZBins - 1, zBin + 1); ++neighborBin) {
754 auto& bucket = activeByZBin[neighborBin];
756 for (
size_t readPos = 0; readPos < bucket.size(); ++readPos) {
757 const auto oLineRefIdx = bucket[readPos];
758 if (!activeMask[oLineRefIdx]) {
761 bucket[writePos++] = oLineRefIdx;
762 const auto& oLineRef = lineRefs[oLineRefIdx];
763 if (o2::gpu::GPUCommonMath::Abs(lineRef.zBeam - oLineRef.zBeam) >= coarseZWindow) {
766 const auto& otherLine = lines[oLineRef.lineIndex];
767 if (
line.mTime.isCompatible(otherLine.mTime)) {
768 unite(lineRefIdx, oLineRefIdx);
771 bucket.resize(writePos);
774 activeMask[lineRefIdx] = 1;
775 activeByUpper.emplace(
line.mTime.upper(), lineRefIdx);
776 activeByZBin[zBin].push_back(lineRefIdx);
779 std::unordered_map<int, bounded_vector<int>>
components;
781 for (
int lineRefIdx = 0; lineRefIdx < static_cast<int>(lineRefs.size()); ++lineRefIdx) {
782 const auto root = findRoot(lineRefIdx);
783 auto [it, inserted] =
components.try_emplace(root, std::pmr::polymorphic_allocator<int>{settings.memoryPool.get()});
785 it->second.push_back(lineRefIdx);
788 bounded_vector<OrderedComponent> orderedComponents(settings.memoryPool.get());
795 std::sort(
members.begin(),
members.end(), [&](
const int lhs,
const int rhs) {
796 const auto lhsLower = lines[lineRefs[lhs].lineIndex].mTime.lower();
797 const auto rhsLower = lines[lineRefs[rhs].lineIndex].mTime.lower();
798 if (lhsLower != rhsLower) {
799 return lhsLower < rhsLower;
801 return lineRefs[lhs].lineIndex < lineRefs[rhs].lineIndex;
803 orderedComponents.emplace_back(settings.memoryPool);
804 orderedComponents.back().center = lineRefs[
members.front()].tCenter;
805 orderedComponents.back().members = std::move(
members);
808 std::sort(orderedComponents.begin(), orderedComponents.end(), [](
const auto& lhs,
const auto& rhs) {
809 if (o2::gpu::GPUCommonMath::Abs(lhs.center - rhs.center) > TieTolerance) {
810 return lhs.center < rhs.center;
812 return lhs.members.front() <
rhs.members.front();
814 clusters.reserve(orderedComponents.size());
815 for (
auto& component : orderedComponents) {
821bounded_vector<VertexSeed> buildSeeds(std::span<const int>
members,
822 std::span<const LineRef> lineRefs,
823 std::span<const Line> lines,
824 const Settings& settings)
826 SeedHistogram histogram(
members, lineRefs, lines, settings);
827 bounded_vector<VertexSeed> seeds(settings.memoryPool.get());
828 seeds.reserve(MaxSeedsPerCluster);
829 float leadingPeakSupport = 0.f;
831 while (
static_cast<int>(seeds.size()) < MaxSeedsPerCluster) {
832 const auto peak = histogram.findPeakBin();
836 const auto peakSupport = histogram.getPeakSupport(peak);
837 if (peakSupport < 2.f) {
840 if (leadingPeakSupport <= 0.f) {
841 leadingPeakSupport = peakSupport;
842 }
else if (peakSupport < std::max(2.f, MinRelativePeakSupport * leadingPeakSupport)) {
845 auto localMembers = histogram.collectLocalMembers(peak, 0, 0);
846 if (localMembers.size() < 2) {
847 localMembers = histogram.collectLocalMembers(peak, settings.seedMemberRadiusTime, settings.seedMemberRadiusZ);
849 if (localMembers.size() < 2) {
850 histogram.suppressPeak(peak);
854 VertexSeed
seed(settings.memoryPool);
855 seed.vertex = {settings.beamX, settings.beamY, histogram.getPeakZCenter(peak)};
856 seed.time = histogram.getPeakTimeInterval(peak);
857 seed.scale2 = InitialScale2;
859 auto fitted = fitSeed(seed, localMembers, lineRefs, lines, settings.memoryPool, settings.pairCut2);
860 if (fitted.valid && fitted.contributors.size() >= 2) {
861 seeds.push_back(std::move(fitted));
862 histogram.suppressPeakNeighborhood(peak);
864 histogram.suppressPeak(peak);
871void assignLinesToSeeds(bounded_vector<VertexSeed>& seeds,
873 std::span<const LineRef> lineRefs,
874 std::span<const Line> lines,
875 const float pairCut2)
877 for (
auto& seed : seeds) {
878 seed.assigned.clear();
881 for (
const auto lineRefIdx :
members) {
882 const auto lineIdx = lineRefs[lineRefIdx].lineIndex;
883 const auto&
line = lines[lineIdx];
886 float bestScore = std::numeric_limits<float>::max();
888 float bestZResidual = std::numeric_limits<float>::max();
890 for (
int seedIdx = 0; seedIdx < static_cast<int>(seeds.size()); ++seedIdx) {
891 const auto&
seed = seeds[seedIdx];
892 if (!
seed.valid ||
seed.contributors.size() < 2) {
895 if (!
line.mTime.isCompatible(
seed.time)) {
899 const auto distance2 = Line::getDistance2FromPoint(line,
seed.vertex);
900 if (distance2 >= pairCut2) {
904 const auto score = distance2 / std::max(
seed.scale2, MinScale2);
905 const auto zResidual = o2::gpu::GPUCommonMath::Abs(lineRefs[lineRefIdx].
zBeam -
seed.vertex[2]);
906 const auto multiplicity =
seed.contributors.size();
908 const auto betterScore = score + TieTolerance < bestScore;
909 const auto betterMultiplicity = o2::gpu::GPUCommonMath::Abs(score - bestScore) <= TieTolerance && multiplicity > bestMult;
910 const auto betterZ = o2::gpu::GPUCommonMath::Abs(score - bestScore) <= TieTolerance &&
911 multiplicity == bestMult && zResidual + constants::Tolerance < bestZResidual;
912 if (betterScore || betterMultiplicity || betterZ) {
915 bestMult = multiplicity;
916 bestZResidual = zResidual;
921 seeds[bestSeed].assigned.push_back(lineRefIdx);
926ClusterWithLines materializeCluster(
const VertexSeed& seed,
927 std::span<const LineRef> lineRefs,
928 std::span<const Line> lines,
929 const std::shared_ptr<BoundedMemoryResource>& mr)
931 bounded_vector<int> lineIndices{mr.get()};
932 lineIndices.reserve(
seed.contributors.size());
934 lineIndices.push_back(lineRefs[lineRefIdx].
lineIndex);
936 std::sort(lineIndices.begin(), lineIndices.end());
937 lineIndices.erase(std::unique(lineIndices.begin(), lineIndices.end()), lineIndices.end());
939 if (lineIndices.size() < 2) {
940 return {ClusterLines{}, bounded_vector<int>{mr.get()}};
943 ClusterLines
fit{std::span<const int>{lineIndices.data(), lineIndices.size()}, lines};
944 return {std::move(fit), std::move(lineIndices)};
952 if (lines.size() < 2) {
956 bounded_vector<LineRef> refs(settings.
memoryPool.get());
957 refs.reserve(lines.size());
958 for (
int lineIdx = 0; lineIdx < static_cast<int>(lines.size()); ++lineIdx) {
959 LineRef
ref(lines[lineIdx], lineIdx, settings.
beamX, settings.
beamY, settings.
maxZ);
965 if (refs.size() < 2) {
969 const auto coarseClusters = buildCoarseClusters(refs, lines, settings);
971 for (
const auto&
members : coarseClusters) {
972 auto seeds = buildSeeds(
members, refs, lines, settings);
977 for (
auto& seed : seeds) {
978 if (!seed.isUsableSeed()) {
993 deduplicateSeeds(seeds, settings);
998 for (
auto& seed : seeds) {
999 if (seed.assigned.size() < 2) {
1003 seed = fitSeed(seed, seed.assigned, refs, lines, settings.
memoryPool, settings.
pairCut2);
1004 if (!seed.isUsableSeed()) {
1009 compactSeeds(seeds);
1010 deduplicateRefittedSeeds(seeds, settings);
1011 for (
auto& refit : seeds) {
1012 auto cluster = materializeCluster(refit, refs, lines, settings.
memoryPool);
1013 if (cluster.fit.getSize() < 2) {
1016 if (!cluster.fit.isValid()) {
1019 clusters.push_back(std::move(cluster));
std::vector< double > sum
bounded_vector< int > members
bounded_vector< int > assigned
bounded_vector< int > contributors
bounded_vector< float > bins
GLuint GLuint GLfloat weight
GLint GLenum GLint components
GLsizei const GLfloat * value
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat maxZ
GLdouble GLdouble GLdouble z
double component(const Vec3 &vector, int dimension)
int32_t const char int32_t line
constexpr int UnusedIndex
constexpr float Tolerance
bounded_vector< ClusterWithLines > buildClusters(std::span< const Line > lines, const Settings &settings)
int const float const TrackSeed< NLayers > & seed
uint32_t TimeStampErrorType
const float const float beamY
const float const float const float const float std::array< float, 3 > std::array< float, 3 > & direction
const track::TrackFitContext< NLayers > & fit
if(!cell.getPxPyPzGlo(direction))
const float const float const float const float std::array< float, 3 > & origin
D const SVectorGPU< T, D > & rhs
R median(std::vector< T > v)
constexpr auto getIndex(const container_T &container, typename container_T::const_iterator iter) -> typename container_T::source_type
static float getDistance2FromPoint(const Line &line, const std::array< float, 3 > &point)
std::shared_ptr< BoundedMemoryResource > memoryPool
std::vector< Cluster > clusters