Project
Loading...
Searching...
No Matches
VertexerTraits.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
12
13#include <algorithm>
14#include <memory>
15#include <ranges>
16#include <span>
17#include <unordered_map>
18
19#include <oneapi/tbb/blocked_range.h>
20#include <oneapi/tbb/parallel_for.h>
21#include <oneapi/tbb/combinable.h>
22
35
36namespace o2::its
37{
39
40namespace
41{
42
43template <TrackletMode Mode, bool EvalRun, int NLayers>
44void trackleterKernelHost(
45 const gsl::span<const Cluster>& clustersNextLayer, // 0 2
46 const gsl::span<const Cluster>& clustersCurrentLayer, // 1 1
47 const gsl::span<uint8_t>& usedClustersNextLayer, // 0 2
48 const int* indexTableNext,
49 const float phiCut,
50 bounded_vector<Tracklet>& tracklets,
51 gsl::span<int> foundTracklets,
52 const IndexTableUtils<NLayers>& utils,
53 const TimeEstBC& timErr,
54 gsl::span<int> rofFoundTrackletsOffsets,
55 const int globalOffsetNextLayer,
56 const int globalOffsetCurrentLayer,
57 const int maxTrackletsPerCluster)
58{
59 const int PhiBins{utils.getNphiBins()};
60 const int ZBins{utils.getNzBins()};
61 // loop on layer1 clusters
62 for (int iCurrentLayerClusterIndex = 0; iCurrentLayerClusterIndex < clustersCurrentLayer.size(); ++iCurrentLayerClusterIndex) {
63 int storedTracklets{0};
64 const Cluster& currentCluster{clustersCurrentLayer[iCurrentLayerClusterIndex]};
65 const int4 selectedBinsRect{o2::its::getBinsRect(currentCluster, (int)Mode + 1, 0.f, 0.f, 100.f, phiCut / 2, utils)};
66 if (selectedBinsRect.x >= 0) {
67 int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1};
68 if (phiBinsNum < 0) {
69 phiBinsNum += PhiBins;
70 }
71 // loop on phi bins next layer
72 for (int iPhiBin{selectedBinsRect.y}, iPhiCount{0}; iPhiCount < phiBinsNum && storedTracklets < maxTrackletsPerCluster; iPhiBin = ++iPhiBin == PhiBins ? 0 : iPhiBin, iPhiCount++) {
73 const int firstBinIndex{utils.getBinIndex(selectedBinsRect.x, iPhiBin)};
74 const int firstRowClusterIndex{indexTableNext[firstBinIndex]};
75 const int maxRowClusterIndex{indexTableNext[firstBinIndex + ZBins]};
76 // loop on clusters next layer
77 for (int iNextLayerClusterIndex{firstRowClusterIndex}; iNextLayerClusterIndex < maxRowClusterIndex && iNextLayerClusterIndex < static_cast<int>(clustersNextLayer.size()) && storedTracklets < maxTrackletsPerCluster; ++iNextLayerClusterIndex) {
78 if (usedClustersNextLayer[iNextLayerClusterIndex]) {
79 continue;
80 }
81 const Cluster& nextCluster{clustersNextLayer[iNextLayerClusterIndex]};
82 if (math_utils::isPhiDifferenceBelow(currentCluster.phi, nextCluster.phi, phiCut)) {
83 if (storedTracklets < maxTrackletsPerCluster) {
84 if constexpr (!EvalRun) {
85 if constexpr (Mode == TrackletMode::Layer0Layer1) {
86 tracklets[rofFoundTrackletsOffsets[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{globalOffsetNextLayer + iNextLayerClusterIndex, globalOffsetCurrentLayer + iCurrentLayerClusterIndex, nextCluster, currentCluster, timErr};
87 } else {
88 tracklets[rofFoundTrackletsOffsets[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{globalOffsetCurrentLayer + iCurrentLayerClusterIndex, globalOffsetNextLayer + iNextLayerClusterIndex, currentCluster, nextCluster, timErr};
89 }
90 }
91 ++storedTracklets;
92 }
93 }
94 }
95 }
96 }
97 if constexpr (EvalRun) {
98 foundTracklets[iCurrentLayerClusterIndex] += storedTracklets;
99 } else {
100 rofFoundTrackletsOffsets[iCurrentLayerClusterIndex] += storedTracklets;
101 }
102 }
103}
104
105void trackletSelectionKernelHost(
106 const Cluster* clusters0, // global layer 0 clusters
107 const Cluster* clusters1, // global layer 1 clusters
108 gsl::span<unsigned char> usedClusters0, // global layer 0 used clusters
109 gsl::span<unsigned char> usedClusters2, // global layer 2 used clusters
110 const gsl::span<const Tracklet>& tracklets01,
111 const gsl::span<const Tracklet>& tracklets12,
112 bounded_vector<uint8_t>& usedTracklets,
113 const gsl::span<int> foundTracklets01,
114 const gsl::span<int> foundTracklets12,
115 bounded_vector<Line>& lines,
116 const gsl::span<const o2::MCCompLabel>& trackletLabels,
117 bounded_vector<o2::MCCompLabel>& linesLabels,
118 const int nLayer1Clusters,
119 const float tanLambdaCut,
120 const float phiCut,
121 const int maxTracklets)
122{
123 int offset01{0}, offset12{0};
124 for (int iCurrentLayerClusterIndex{0}; iCurrentLayerClusterIndex < nLayer1Clusters; ++iCurrentLayerClusterIndex) {
125 int validTracklets{0};
126 const int endTracklet01 = offset01 + foundTracklets01[iCurrentLayerClusterIndex];
127 const int endTracklet12 = offset12 + foundTracklets12[iCurrentLayerClusterIndex];
128 for (int iTracklet12{offset12}; iTracklet12 < endTracklet12 && validTracklets != maxTracklets; ++iTracklet12) {
129 const auto& tracklet12{tracklets12[iTracklet12]};
130 for (int iTracklet01{offset01}; iTracklet01 < endTracklet01 && validTracklets != maxTracklets; ++iTracklet01) {
131 if (usedTracklets[iTracklet01]) {
132 continue;
133 }
134
135 const auto& tracklet01{tracklets01[iTracklet01]};
136 if (!tracklet01.getTimeStamp().isCompatible(tracklet12.getTimeStamp())) {
137 continue;
138 }
139
140 const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(tracklet01.tanLambda - tracklet12.tanLambda)};
141 if (deltaTanLambda >= tanLambdaCut) {
142 continue;
143 }
144 if (math_utils::isPhiDifferenceBelow(tracklet01.phi, tracklet12.phi, phiCut) && validTracklets != maxTracklets) {
145 usedClusters0[tracklet01.firstClusterIndex] = 1;
146 usedClusters2[tracklet12.secondClusterIndex] = 1;
147 usedTracklets[iTracklet01] = true;
148 lines.emplace_back(tracklet01, clusters0, clusters1);
149 if (!trackletLabels.empty()) {
150 linesLabels.emplace_back(trackletLabels[iTracklet01]);
151 }
152 ++validTracklets;
153 }
154 }
155 }
156 offset01 += foundTracklets01[iCurrentLayerClusterIndex];
157 offset12 += foundTracklets12[iCurrentLayerClusterIndex];
158 }
159}
160} // namespace
161
162template <int NLayers>
164{
165 mTaskArena->execute([&] { mTimeFrame->initialise(trackingParams, 3); });
166}
167
168template <int NLayers>
169void VertexerTraits<NLayers>::updateVertexingParameters(const std::vector<VertexingParameters>& vrtPar)
170{
171 mVrtParams = vrtPar;
172 mIndexTableUtils.setTrackingParameters(vrtPar[0]);
173 for (auto& par : mVrtParams) {
174 par.phiSpan = static_cast<int>(std::ceil(mIndexTableUtils.getNphiBins() * par.phiCut / o2::constants::math::TwoPI));
175 par.zSpan = static_cast<int>(std::ceil(par.zCut * mIndexTableUtils.getInverseZCoordinate(0)));
176 }
177}
178
179// Main functions
180template <int NLayers>
182{
183 mTaskArena->execute([&] {
184 tbb::parallel_for(0, mTimeFrame->getNrof(1), [&](const short pivotRofId) {
185 bool skip = skipROF(iteration, pivotRofId);
186 const auto& rofRange01 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 0, pivotRofId);
187 for (auto targetRofId = rofRange01.getFirstEntry(); targetRofId < rofRange01.getEntriesBound(); ++targetRofId) {
188 const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(0, targetRofId, 1, pivotRofId);
189 trackleterKernelHost<TrackletMode::Layer0Layer1, true>(
190 !skip ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span<Cluster>(), // Clusters to be matched with the next layer in target rof
191 !skip ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span<Cluster>(), // Clusters to be matched with the current layer in pivot rof
192 mTimeFrame->getUsedClustersROF(targetRofId, 0), // Span of the used clusters in the target rof
193 mTimeFrame->getIndexTable(targetRofId, 0).data(), // Index table to access the data on the next layer in target rof
194 mVrtParams[iteration].phiCut,
195 mTimeFrame->getTracklets()[0], // Flat tracklet buffer
196 mTimeFrame->getNTrackletsCluster(pivotRofId, 0), // Span of the number of tracklets per each cluster in pivot rof
197 mIndexTableUtils,
198 timeErr,
199 gsl::span<int>(), // Offset in the tracklet buffer
200 0,
201 0,
202 mVrtParams[iteration].maxTrackletsPerCluster);
203 }
204 const auto& rofRange12 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 2, pivotRofId);
205 for (auto targetRofId = rofRange12.getFirstEntry(); targetRofId < rofRange12.getEntriesBound(); ++targetRofId) {
206 const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(2, targetRofId, 1, pivotRofId);
207 trackleterKernelHost<TrackletMode::Layer1Layer2, true>(
208 !skip ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span<Cluster>(),
209 !skip ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span<Cluster>(),
210 mTimeFrame->getUsedClustersROF(targetRofId, 2),
211 mTimeFrame->getIndexTable(targetRofId, 2).data(),
212 mVrtParams[iteration].phiCut,
213 mTimeFrame->getTracklets()[1],
214 mTimeFrame->getNTrackletsCluster(pivotRofId, 1), // Span of the number of tracklets per each cluster in pivot rof
215 mIndexTableUtils,
216 timeErr,
217 gsl::span<int>(), // Offset in the tracklet buffer
218 0,
219 0,
220 mVrtParams[iteration].maxTrackletsPerCluster);
221 }
222 mTimeFrame->getNTrackletsROF(pivotRofId, 0) = std::accumulate(mTimeFrame->getNTrackletsCluster(pivotRofId, 0).begin(), mTimeFrame->getNTrackletsCluster(pivotRofId, 0).end(), 0);
223 mTimeFrame->getNTrackletsROF(pivotRofId, 1) = std::accumulate(mTimeFrame->getNTrackletsCluster(pivotRofId, 1).begin(), mTimeFrame->getNTrackletsCluster(pivotRofId, 1).end(), 0);
224 });
225
226 mTimeFrame->computeTrackletsPerROFScans();
227 if (auto tot0 = mTimeFrame->getTotalTrackletsTF(0), tot1 = mTimeFrame->getTotalTrackletsTF(1);
228 tot0 == 0 || tot1 == 0) {
229 return;
230 } else {
231 mTimeFrame->getTracklets()[0].resize(tot0);
232 mTimeFrame->getTracklets()[1].resize(tot1);
233 }
234
235 tbb::parallel_for(0, mTimeFrame->getNrof(1), [&](const short pivotRofId) {
236 bool skip = skipROF(iteration, pivotRofId);
237 const int globalOffsetPivot = mTimeFrame->getSortedStartIndex(pivotRofId, 1);
238 const auto& rofRange01 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 0, pivotRofId);
239 for (auto targetRofId = rofRange01.getFirstEntry(); targetRofId < rofRange01.getEntriesBound(); ++targetRofId) {
240 const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(0, targetRofId, 1, pivotRofId);
241 trackleterKernelHost<TrackletMode::Layer0Layer1, false>(
242 !skip ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span<Cluster>(),
243 !skip ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span<Cluster>(),
244 mTimeFrame->getUsedClustersROF(targetRofId, 0),
245 mTimeFrame->getIndexTable(targetRofId, 0).data(),
246 mVrtParams[iteration].phiCut,
247 mTimeFrame->getTracklets()[0],
248 mTimeFrame->getNTrackletsCluster(pivotRofId, 0),
249 mIndexTableUtils,
250 timeErr,
251 mTimeFrame->getExclusiveNTrackletsCluster(pivotRofId, 0),
252 mTimeFrame->getSortedStartIndex(targetRofId, 0),
253 globalOffsetPivot,
254 mVrtParams[iteration].maxTrackletsPerCluster);
255 }
256 const auto& rofRange12 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 2, pivotRofId);
257 for (auto targetRofId = rofRange12.getFirstEntry(); targetRofId < rofRange12.getEntriesBound(); ++targetRofId) {
258 const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(2, targetRofId, 1, pivotRofId);
259 trackleterKernelHost<TrackletMode::Layer1Layer2, false>(
260 !skip ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span<Cluster>(),
261 !skip ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span<Cluster>(),
262 mTimeFrame->getUsedClustersROF(targetRofId, 2),
263 mTimeFrame->getIndexTable(targetRofId, 2).data(),
264 mVrtParams[iteration].phiCut,
265 mTimeFrame->getTracklets()[1],
266 mTimeFrame->getNTrackletsCluster(pivotRofId, 1),
267 mIndexTableUtils,
268 timeErr,
269 mTimeFrame->getExclusiveNTrackletsCluster(pivotRofId, 1),
270 mTimeFrame->getSortedStartIndex(targetRofId, 2),
271 globalOffsetPivot,
272 mVrtParams[iteration].maxTrackletsPerCluster);
273 }
274 });
275 });
276
278 if (mTimeFrame->hasMCinformation()) {
279 for (const auto& trk : mTimeFrame->getTracklets()[0]) {
281 int sortedId0{trk.firstClusterIndex};
282 int sortedId1{trk.secondClusterIndex};
283 for (const auto& lab0 : mTimeFrame->getClusterLabels(0, mTimeFrame->getClusters()[0][sortedId0].clusterId)) {
284 for (const auto& lab1 : mTimeFrame->getClusterLabels(1, mTimeFrame->getClusters()[1][sortedId1].clusterId)) {
285 if (lab0 == lab1 && lab0.isValid()) {
286 label = lab0;
287 break;
288 }
289 }
290 if (label.isValid()) {
291 break;
292 }
293 }
294 mTimeFrame->getTrackletsLabel(0).emplace_back(label);
295 }
296 }
297}
298
299template <int NLayers>
301{
302 mTaskArena->execute([&] {
303 tbb::combinable<int> totalLines{0};
304 tbb::parallel_for(
305 tbb::blocked_range<short>(0, (short)mTimeFrame->getNrof(1)),
306 [&](const tbb::blocked_range<short>& Rofs) {
307 for (short pivotRofId = Rofs.begin(); pivotRofId < Rofs.end(); ++pivotRofId) {
308 if (mTimeFrame->getFoundTracklets(pivotRofId, 0).empty() || skipROF(iteration, pivotRofId)) {
309 continue;
310 }
311 mTimeFrame->getLines(pivotRofId).reserve(std::min(mTimeFrame->getFoundTracklets(pivotRofId, 0).size(), mTimeFrame->getNTrackletsCluster(pivotRofId, 0).size() * constants::MaxSelectedTrackletsPerCluster));
312 bounded_vector<uint8_t> usedTracklets(mTimeFrame->getFoundTracklets(pivotRofId, 0).size(), 0, mMemoryPool.get());
313 trackletSelectionKernelHost(
314 mTimeFrame->getClusters()[0].data(),
315 mTimeFrame->getClusters()[1].data(),
316 mTimeFrame->getUsedClusters(0),
317 mTimeFrame->getUsedClusters(2),
318 mTimeFrame->getFoundTracklets(pivotRofId, 0),
319 mTimeFrame->getFoundTracklets(pivotRofId, 1),
320 usedTracklets,
321 mTimeFrame->getNTrackletsCluster(pivotRofId, 0),
322 mTimeFrame->getNTrackletsCluster(pivotRofId, 1),
323 mTimeFrame->getLines(pivotRofId),
324 mTimeFrame->getLabelsFoundTracklets(pivotRofId, 0),
325 mTimeFrame->getLinesLabel(pivotRofId),
326 static_cast<int>(mTimeFrame->getClustersOnLayer(pivotRofId, 1).size()),
327 mVrtParams[iteration].tanLambdaCut,
328 mVrtParams[iteration].phiCut,
329 constants::MaxSelectedTrackletsPerCluster);
330 totalLines.local() += mTimeFrame->getLines(pivotRofId).size();
331 }
332 });
333 mTimeFrame->setNLinesTotal(totalLines.combine(std::plus<int>()));
334 });
335
336 // from here on we do not use tracklets anymore, so let's free them
337 deepVectorClear(mTimeFrame->getTracklets());
338}
339
340template <int NLayers>
342{
343 const int nRofs = mTimeFrame->getNrof(1);
344 std::vector<std::vector<Vertex>> rofVertices(nRofs);
345 std::vector<std::vector<VertexLabel>> rofLabels(nRofs);
346 const float pairCut2 = mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut;
347 const float duplicateZCut = mVrtParams[iteration].duplicateZCut > 0.f ? mVrtParams[iteration].duplicateZCut : std::max(4.f * mVrtParams[iteration].pairCut, 0.5f * mVrtParams[iteration].clusterCut);
348 const float duplicateDistance2Cut = mVrtParams[iteration].duplicateDistance2Cut > 0.f ? mVrtParams[iteration].duplicateDistance2Cut : std::max(16.f * pairCut2, 0.0625f * mVrtParams[iteration].clusterCut * mVrtParams[iteration].clusterCut);
350 settings.beamX = mTimeFrame->getBeamX();
351 settings.beamY = mTimeFrame->getBeamY();
352 settings.pairCut = mVrtParams[iteration].pairCut;
353 settings.pairCut2 = pairCut2;
354 settings.clusterCut = mVrtParams[iteration].clusterCut;
355 settings.coarseZWindow = mVrtParams[iteration].coarseZWindow;
356 settings.seedDedupZCut = mVrtParams[iteration].seedDedupZCut;
357 settings.refitDedupZCut = mVrtParams[iteration].refitDedupZCut;
358 settings.duplicateZCut = duplicateZCut;
359 settings.duplicateDistance2Cut = duplicateDistance2Cut;
360 settings.finalSelectionZCut = mVrtParams[iteration].finalSelectionZCut;
361 settings.maxZ = mVrtParams[iteration].maxZPositionAllowed;
362 settings.seedMemberRadiusTime = mVrtParams[iteration].seedMemberRadiusTime;
363 settings.seedMemberRadiusZ = mVrtParams[iteration].seedMemberRadiusZ;
364 settings.memoryPool = mMemoryPool;
365
366 const auto processROF = [&](const int rofId) {
367 if (skipROF(iteration, rofId)) {
368 return;
369 }
370 auto& lines = mTimeFrame->getLines(rofId);
371 auto clusters = line_vertexer::buildClusters(std::span<const Line>{lines.data(), lines.size()}, settings);
372 deepVectorClear(lines); // not needed after
373 auto clusterBeamDistance2 = [&](const line_vertexer::ClusterWithLines& cluster) {
374 return (mTimeFrame->getBeamX() - cluster.getVertex()[0]) * (mTimeFrame->getBeamX() - cluster.getVertex()[0]) +
375 (mTimeFrame->getBeamY() - cluster.getVertex()[1]) * (mTimeFrame->getBeamY() - cluster.getVertex()[1]);
376 };
377 auto clusterBetter = [&](const line_vertexer::ClusterWithLines& lhs, const line_vertexer::ClusterWithLines& rhs) {
378 if (lhs.getSize() != rhs.getSize()) {
379 return lhs.getSize() > rhs.getSize();
380 }
381 if (o2::gpu::GPUCommonMath::Abs(lhs.getAvgDistance2() - rhs.getAvgDistance2()) > constants::Tolerance) {
382 return lhs.getAvgDistance2() < rhs.getAvgDistance2();
383 }
384 const auto lhsBeam = clusterBeamDistance2(lhs);
385 const auto rhsBeam = clusterBeamDistance2(rhs);
386 if (o2::gpu::GPUCommonMath::Abs(lhsBeam - rhsBeam) > constants::Tolerance) {
387 return lhsBeam < rhsBeam;
388 }
389 return lhs.getVertex()[2] < rhs.getVertex()[2];
390 };
391
392 // Cluster deduplication by local non-maximum suppression in time/space
393 std::sort(clusters.begin(), clusters.end(), clusterBetter);
394 float minClusterZ = std::numeric_limits<float>::max();
395 for (const auto& cluster : clusters) {
396 minClusterZ = std::min(minClusterZ, cluster.getVertex()[2]);
397 }
398 bounded_vector<line_vertexer::ClusterWithLines> deduplicated(mMemoryPool.get());
399 deduplicated.reserve(clusters.size());
400 std::unordered_map<int, std::vector<int>> keptByZBin;
401 for (auto& candidate : clusters) {
402 bool duplicate = false;
403 const auto candidateZ = candidate.getVertex()[2];
404 const auto zBin = static_cast<int>(std::floor((candidateZ - minClusterZ) / settings.duplicateZCut));
405 for (int neighborBin = zBin - 1; neighborBin <= zBin + 1 && !duplicate; ++neighborBin) {
406 const auto found = keptByZBin.find(neighborBin);
407 if (found == keptByZBin.end()) {
408 continue;
409 }
410 for (const auto ownerId : found->second) {
411 const auto& owner = deduplicated[ownerId];
412 if (!candidate.getTimeStamp().isCompatible(owner.getTimeStamp())) {
413 continue;
414 }
415 if (o2::gpu::GPUCommonMath::Abs(candidate.getVertex()[2] - owner.getVertex()[2]) >= settings.duplicateZCut) {
416 continue;
417 }
418 const auto dx = candidate.getVertex()[0] - owner.getVertex()[0];
419 const auto dy = candidate.getVertex()[1] - owner.getVertex()[1];
420 const auto dz = candidate.getVertex()[2] - owner.getVertex()[2];
421 const auto distance2 = math_utils::SqSum(dx, dy, dz);
422 if (distance2 < settings.duplicateDistance2Cut) {
423 duplicate = true;
424 break;
425 }
426 }
427 }
428 if (duplicate) {
429 continue;
430 }
431
432 const auto ownerId = static_cast<int>(deduplicated.size());
433 keptByZBin[zBin].push_back(ownerId);
434 deduplicated.push_back(std::move(candidate));
435 }
436 clusters = std::move(deduplicated);
437 int nClusters = static_cast<int>(clusters.size());
438
439 // Vertex filtering with score-based local NMS
440 std::sort(clusters.begin(), clusters.end(), clusterBetter);
441 std::vector<int> candidateIndices;
442 candidateIndices.reserve(nClusters);
443 for (int iCluster{0}; iCluster < nClusters; ++iCluster) {
444 const bool zCompatible = o2::gpu::GPUCommonMath::Abs(clusters[iCluster].getVertex()[2]) < mVrtParams[iteration].maxZPositionAllowed;
445
446 if (zCompatible) {
447 candidateIndices.push_back(iCluster);
448 }
449 }
450
451 if (candidateIndices.empty()) {
452 return;
453 }
454
455 auto countSharedLabels = [](const line_vertexer::ClusterWithLines& lhs, const line_vertexer::ClusterWithLines& rhs) {
456 size_t shared = 0;
457 auto lhsIt = lhs.getLabels().begin();
458 auto rhsIt = rhs.getLabels().begin();
459 while (lhsIt != lhs.getLabels().end() && rhsIt != rhs.getLabels().end()) {
460 if (*lhsIt == *rhsIt) {
461 ++shared;
462 ++lhsIt;
463 ++rhsIt;
464 } else if (*lhsIt < *rhsIt) {
465 ++lhsIt;
466 } else {
467 ++rhsIt;
468 }
469 }
470 return shared;
471 };
472
473 float minCandidateZ = std::numeric_limits<float>::max();
474 for (const auto clusterId : candidateIndices) {
475 minCandidateZ = std::min(minCandidateZ, clusters[clusterId].getVertex()[2]);
476 }
477 std::unordered_map<int, std::vector<int>> selectedByZBin;
478 std::vector<int> selectedIndices;
479 selectedIndices.reserve(candidateIndices.size());
480 for (const auto clusterId : candidateIndices) {
481 const auto& candidate = clusters[clusterId];
482 const auto candidateZ = candidate.getVertex()[2];
483 const auto zBin = static_cast<int>((candidateZ - minCandidateZ) / settings.finalSelectionZCut);
484 bool suppressed = false;
485 for (int neighborBin = zBin - 1; neighborBin <= zBin + 1 && !suppressed; ++neighborBin) {
486 const auto found = selectedByZBin.find(neighborBin);
487 if (found == selectedByZBin.end()) {
488 continue;
489 }
490 for (const auto selectedId : found->second) {
491 const auto& selected = clusters[selectedId];
492 if (!candidate.getTimeStamp().isCompatible(selected.getTimeStamp())) {
493 continue;
494 }
495 const auto zDelta = o2::gpu::GPUCommonMath::Abs(candidateZ - selected.getVertex()[2]);
496 const auto sharedLabels = countSharedLabels(candidate, selected);
497 const auto minSize = std::min(candidate.getSize(), selected.getSize());
498 const bool overlapDuplicate = sharedLabels > 0 && sharedLabels * 4 >= minSize;
499 const bool strongZDuplicate = zDelta < settings.finalSelectionZCut;
500 const bool clearlyBetterMultiplicity = selected.getSize() >= candidate.getSize() + 3;
501 const bool clearlyBetterQuality = selected.getSize() > candidate.getSize() &&
502 selected.getAvgDistance2() + constants::Tolerance < 0.8f * candidate.getAvgDistance2();
503 const bool weakCandidate = clearlyBetterMultiplicity || clearlyBetterQuality;
504 if (overlapDuplicate || (strongZDuplicate && weakCandidate)) {
505 suppressed = true;
506 break;
507 }
508 }
509 }
510 if (suppressed) {
511 continue;
512 }
513 selectedByZBin[zBin].push_back(clusterId);
514 selectedIndices.push_back(clusterId);
515 }
516
517 // sort vertices by their multiplicity to opt. suppress lower mult. debris
518 std::vector<int> sortedIndices(selectedIndices.size());
519 std::iota(sortedIndices.begin(), sortedIndices.end(), 0);
520 std::sort(sortedIndices.begin(), sortedIndices.end(), [&selectedIndices, &clusters](int i, int j) {
521 return clusters[selectedIndices[i]].getSize() > clusters[selectedIndices[j]].getSize();
522 });
523 for (const auto sortedId : sortedIndices) {
524 const auto& cluster = clusters[selectedIndices[sortedId]];
525 const auto beamDistance2 = clusterBeamDistance2(cluster);
526 if (!(beamDistance2 < mVrtParams[iteration].NSigmaCut)) {
527 continue;
528 }
529 if (cluster.getSize() < mVrtParams[iteration].clusterContributorsCut) {
530 continue;
531 }
532 if (!rofVertices[rofId].empty() && cluster.getSize() < mVrtParams[iteration].suppressLowMultDebris) {
533 continue;
534 }
535
536 Vertex vertex{cluster.getVertex(),
537 cluster.getRMS2(),
538 (ushort)cluster.getSize(),
539 cluster.getAvgDistance2()};
540 if (mVrtParams[iteration].PassFlags[IterationStep::MarkVerticesAsUPC]) {
541 vertex.setFlags(Vertex::UPCMode);
542 }
543 vertex.setTimeStamp(cluster.getTimeStamp());
544 rofVertices[rofId].push_back(vertex);
545 if (mTimeFrame->hasMCinformation()) {
546 auto& lineLabels = mTimeFrame->getLinesLabel(rofId);
547 bounded_vector<o2::MCCompLabel> labels(mMemoryPool.get());
548 for (auto& index : cluster.getLabels()) {
549 labels.push_back(lineLabels[index]);
550 }
551 const auto mainLabel = computeMainVertexLabel(labels);
552 rofLabels[rofId].push_back(mainLabel);
553 }
554 }
555 };
556
557 if (mTaskArena->max_concurrency() <= 1) {
558 for (int rofId{0}; rofId < nRofs; ++rofId) {
559 processROF(rofId);
560 }
561 } else {
562 mTaskArena->execute([&] {
563 tbb::parallel_for(0, nRofs, [&](const int rofId) {
564 processROF(rofId);
565 });
566 });
567 }
568 // add vertices, these anyways get sorted afterward
569 for (int rofId{0}; rofId < nRofs; ++rofId) {
570 for (auto& vertex : rofVertices[rofId]) {
571 mTimeFrame->addPrimaryVertex(vertex);
572 }
573 if (mTimeFrame->hasMCinformation()) {
574 for (auto& label : rofLabels[rofId]) {
575 mTimeFrame->addPrimaryVertexLabel(label);
576 }
577 }
578 }
579}
580
581template <int NLayers>
583{
584 LOGP(info, "Using truth seeds as vertices; will skip computations");
585 const auto dc = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root");
586 const auto irs = dc->getEventRecords();
588 int64_t roFrameLengthInBC = o2::itsmft::DPLAlpideParam<o2::detectors::DetID::ITS>::Instance().getROFLengthInBC(1);
590 const int iSrc = 0; // take only events from collision generator
591 auto eveId2colId = dc->getCollisionIndicesForSource(iSrc);
592 for (int iEve{0}; iEve < mcReader.getNEvents(iSrc); ++iEve) {
593 const auto& ir = irs[eveId2colId[iEve]];
594 if (!ir.isDummy()) { // do we need this, is this for diffractive events?
595 const auto& eve = mcReader.getMCEventHeader(iSrc, iEve);
596 auto bc = (ir - raw::HBFUtils::Instance().getFirstSampledTFIR()).toLong() - roFrameBiasInBC;
597 if (bc < 0) { // event happened before TF
598 continue;
599 }
600 Vertex vert;
601 vert.getTimeStamp().setTimeStamp(bc);
602 vert.getTimeStamp().setTimeStampError(roFrameLengthInBC / 2);
603 // set minimum to 1 sometimes for diffractive events there is nothing acceptance
604 vert.setNContributors(std::max(1L, std::ranges::count_if(mcReader.getTracks(iSrc, iEve), [](const auto& trk) {
605 if (!trk.isPrimary() || trk.GetPt() < 0.05 || std::abs(trk.GetEta()) > 1.1) {
606 return false;
607 }
608 const auto* p = o2::O2DatabasePDG::Instance()->GetParticle(trk.GetPdgCode());
609 return (!p) ? false : p->Charge() != 0;
610 })));
611 vert.setXYZ((float)eve.GetX(), (float)eve.GetY(), (float)eve.GetZ());
612 vert.setChi2(1); // not used as constraint
613 constexpr float cov = 25e-4;
614 vert.setSigmaX(cov);
615 vert.setSigmaY(cov);
616 vert.setSigmaZ(cov);
617 mTimeFrame->addPrimaryVertex(vert);
618 o2::MCCompLabel mcLbl(o2::MCCompLabel::maxTrackID(), iEve, iSrc, false);
619 VertexLabel lbl(mcLbl, 1.0);
620 mTimeFrame->addPrimaryVertexLabel(lbl);
621 }
622 mcReader.releaseTracksForSourceAndEvent(iSrc, iEve);
623 }
624 LOGP(info, "Imposed {} pv collisions from mc-truth", mTimeFrame->getPrimaryVertices().size());
625}
626
627template <int NLayers>
628void VertexerTraits<NLayers>::setNThreads(int n, std::shared_ptr<tbb::task_arena>& arena)
629{
630 if (arena == nullptr) {
631 mTaskArena = std::make_shared<tbb::task_arena>(std::abs(n));
632 LOGP(info, "Setting seeding vertexer with {} threads.", n);
633 } else {
634 mTaskArena = arena;
635 }
636}
637
638template <int NLayers>
639bool VertexerTraits<NLayers>::skipROF(int iteration, int rof) const
640{
641 return mVrtParams[iteration].PassFlags[IterationStep::SkipROFsAboveThreshold] &&
642 (int)mTimeFrame->getROFVertexLookupTableView().getVertices(1, rof).getEntries() > mVrtParams[iteration].vertPerRofThreshold;
643}
644
645template class VertexerTraits<7>;
646} // namespace o2::its
size_t minSize
uint64_t vertex
Definition RawEventData.h:9
uint64_t bc
Definition RawEventData.h:5
int32_t i
Mode
Definition Utils.h:89
float phiCut
Definition Tracker.cxx:213
std::vector< o2::MCCompLabel > labels
uint32_t j
Definition RawData.h:0
Utility functions for vertex handling.
Class to compute the primary vertex in ITS from tracklets.
int nClusters
static constexpr int maxTrackID()
static TDatabasePDG * Instance()
HMPID cluster implementation.
Definition Cluster.h:27
virtual void initialise(const TrackingParameters &trackingParams)
virtual void updateVertexingParameters(const std::vector< VertexingParameters > &vrtPar)
virtual void computeTracklets(const int iteration)
static DigitizationContext * loadFromFile(std::string_view filename="")
size_t getNEvents(int source) const
Get number of events.
o2::dataformats::MCEventHeader const & getMCEventHeader(int source, int event) const
retrieves the MCEventHeader for a given eventID and sourceID
void releaseTracksForSourceAndEvent(int source, int event)
API to ask releasing tracks and track references (freeing memory) for source + event.
std::vector< MCTrack > const & getTracks(int source, int event) const
variant returning all tracks for source and event at once
GLdouble n
Definition glcorearb.h:1982
GLuint index
Definition glcorearb.h:781
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
std::pair< o2::MCCompLabel, float > VertexLabel
Definition Vertex.h:27
const float zDelta
return getBinsRect(layerIndex, currentCluster.phi, zMean, zDelta, maxdeltaphi, utils)
o2::track::TrackParametrization< float > par
VertexLabel computeMainVertexLabel(const Container &elements)
Definition VertexUtils.h:37
void deepVectorClear(std::vector< T > &vec)
std::vector< Cluster > getClusters(int event)
Common utility functions.
void empty(int)
int32_t w
std::shared_ptr< BoundedMemoryResource > memoryPool
o2::InteractionRecord ir(0, 0)
std::vector< Cluster > clusters
std::vector< Tracklet64 > tracklets