Skip to content

Commit 4c28bdc

Browse files
committed
ITS GPU seeding vertexer: add clamps to adaptive low mult threshold, and propate cells towards beamline
1 parent fe47e4f commit 4c28bdc

10 files changed

Lines changed: 127 additions & 17 deletions

File tree

‎Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ struct TrackingKernels {
211211
int* lineSlots,
212212
const float beamX,
213213
const float beamY,
214+
const float bz,
215+
const float curvatureScale,
214216
const float maxZ,
215217
const float minPt,
216218
float* linesZs,

‎Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ void TrackerTraitsGPU<NLayers>::computeVertexCandidates(const int iteration)
266266
mTimeFrameGPU->getDeviceLineSlots(),
267267
mTimeFrameGPU->getBeamX(),
268268
mTimeFrameGPU->getBeamY(),
269+
this->getBz(),
270+
this->mTrkParams[iteration].VtxLineCurvatureScale,
269271
this->mTrkParams[iteration].VtxMaxZPositionAllowed,
270272
this->mTrkParams[iteration].VtxLineMinPt,
271273
mTimeFrameGPU->getDeviceLineZs(),
@@ -451,13 +453,13 @@ void TrackerTraitsGPU<NLayers>::computeVertices(const int iteration)
451453
}
452454
}
453455
}
454-
const float sigThreshold = goodSig > 0.f ? goodSig * std::sqrt(static_cast<float>(std::max(rofLoad, 1.))) : 0.f;
456+
const float debrisThreshold = goodSig > 0.f ? getDebrisThreshold(goodSig, rofLoad, suppressLowMultDebris) : 0.f;
455457

456458
for (const int p : accepted) {
457459
const auto& c = cands[p];
458460
if (!rofVertices[rofId].empty()) {
459461
if (goodSig > 0.f) {
460-
if (c.nGood <= sigThreshold) {
462+
if (c.nGood < debrisThreshold) {
461463
continue;
462464
}
463465
} else if (c.size < suppressLowMultDebris) {
@@ -503,6 +505,9 @@ void TrackerTraitsGPU<NLayers>::computeVertices(const int iteration)
503505
}
504506
}
505507
}
508+
if (!this->mTrkParams[iteration].PassFlags[IterationStep::MarkVerticesAsUPC]) { // UPC ROFs are near-empty by construction
509+
pruneOverpopulatedRofs(rofVertices, rofLabels, this->mTrkParams[iteration].VtxOverpopulatedRofNSigma, suppressLowMultDebris, constants::VtxOverpopulatedRofTrimFraction);
510+
}
506511

507512
for (int rofId = 0; rofId < nRofs; ++rofId) {
508513
for (auto& vertex : rofVertices[rofId]) {

‎Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -874,22 +874,23 @@ GPUg() void dedupCellsKernel(
874874
const int ownedClustersCut,
875875
const float beamX,
876876
const float beamY,
877+
const float bz,
878+
const float curvatureScale,
877879
const float maxZ,
878880
const float minPt,
879881
int* cellAccepted)
880882
{
881883
for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < nCells; i += blockDim.x * gridDim.x) {
882884
const CellSeed& cell = cells[i];
883885
std::array<float, 3> origin, direction;
884-
if (!cell.getPxPyPzGlo(direction)) {
886+
if (!getCellLineAtBeam(cell, beamX, beamY, bz, curvatureScale, origin, direction)) {
885887
cellAccepted[i] = 0;
886888
continue;
887889
}
888890
const bool owned0 = static_cast<uint32_t>(clusterOwners[0][cell.getFirstClusterIndex()]) == static_cast<uint32_t>(i);
889891
const bool owned1 = static_cast<uint32_t>(clusterOwners[1][cell.getSecondClusterIndex()]) == static_cast<uint32_t>(i);
890892
const bool owned2 = static_cast<uint32_t>(clusterOwners[2][cell.getThirdClusterIndex()]) == static_cast<uint32_t>(i);
891893
const bool keepCell = (static_cast<int>(owned0) + static_cast<int>(owned1) + static_cast<int>(owned2)) >= 3 - ownedClustersCut;
892-
cell.getXYZGlo(origin);
893894
const float dx = origin[0] - beamX;
894895
const float dy = origin[1] - beamY;
895896
const float den = direction[0] * direction[0] + direction[1] * direction[1];
@@ -910,6 +911,8 @@ GPUg() void linearizeCellsKernel(
910911
int* lineRof,
911912
const float beamX,
912913
const float beamY,
914+
const float bz,
915+
const float curvatureScale,
913916
float* lineZs,
914917
o2::its::TimeEstBC* lineTimes,
915918
int* lineClusters, // 3 per line (L0,L1,L2 cluster ids), for the host-side MC label derivation
@@ -923,8 +926,7 @@ GPUg() void linearizeCellsKernel(
923926
}
924927
const CellSeed& cell = cells[i];
925928
std::array<float, 3> origin, direction;
926-
cell.getXYZGlo(origin);
927-
cell.getPxPyPzGlo(direction);
929+
getCellLineAtBeam(cell, beamX, beamY, bz, curvatureScale, origin, direction); // accepted by dedupCellsKernel: succeeds
928930
lines[slot] = o2::its::Line{origin.data(), direction.data(), cell.getTimeStamp()};
929931
lineRof[slot] = deviceUpperBound(rofFramesClustersL1, 0, nRofsL1 + 1, cell.getSecondClusterIndex()) - 1;
930932
float zAtBeam;
@@ -1577,6 +1579,8 @@ void TrackingKernels<NLayers>::linearizeCellsToLinesHandler(const int nCells,
15771579
int* lineSlots, // nCells + 1 scratch: accept flags, scanned in place into slots
15781580
const float beamX,
15791581
const float beamY,
1582+
const float bz,
1583+
const float curvatureScale,
15801584
const float maxZ,
15811585
const float minPt,
15821586
float* linesZs,
@@ -1593,6 +1597,8 @@ void TrackingKernels<NLayers>::linearizeCellsToLinesHandler(const int nCells,
15931597
ownedClustersCut,
15941598
beamX,
15951599
beamY,
1600+
bz,
1601+
curvatureScale,
15961602
maxZ,
15971603
minPt,
15981604
lineSlots);
@@ -1608,6 +1614,8 @@ void TrackingKernels<NLayers>::linearizeCellsToLinesHandler(const int nCells,
16081614
lineRof,
16091615
beamX,
16101616
beamY,
1617+
bz,
1618+
curvatureScale,
16111619
linesZs,
16121620
lineTimes,
16131621
lineClusters,

‎Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <cmath>
2020
#endif
2121
#include "ITStracking/Cluster.h"
22+
#include "ITStracking/Cell.h"
2223
#include "ITSMFTTracking/Constants.h"
2324
#include "ITStracking/Tracklet.h"
2425
#include "GPUCommonDef.h"
@@ -121,6 +122,25 @@ struct Line final {
121122
TimeEstBC mTime;
122123
};
123124

125+
GPUdi() bool getCellLineAtBeam(const CellSeed& cell, const float beamX, const float beamY, const float bz, const float curvatureScale, std::array<float, 3>& origin, std::array<float, 3>& direction)
126+
{
127+
cell.getXYZGlo(origin);
128+
if (!cell.getPxPyPzGlo(direction)) {
129+
return false;
130+
}
131+
o2::track::TrackParametrization<float> par{cell};
132+
par.setQ2Pt(par.getQ2Pt() * curvatureScale);
133+
std::array<float, 3> dcaOrigin, dcaDirection;
134+
if (par.propagateParamToDCA({beamX, beamY, 0.f}, bz)) {
135+
par.getXYZGlo(dcaOrigin);
136+
if (par.getPxPyPzGlo(dcaDirection)) {
137+
origin = dcaOrigin;
138+
direction = dcaDirection;
139+
}
140+
}
141+
return true;
142+
}
143+
124144
/// Least-squares vertex fit over a set of lines (the normal equations AX = -B).
125145
class ClusterLines final
126146
{

‎Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ struct TrackingParameters {
151151
int VertPerRofThreshold = 0; // max vertices in a ROF for the UPC pass to still run on it
152152
float VtxPhiCut = -1.f;
153153
float VtxLineMinPt = -1.f;
154+
float VtxLineCurvatureScale = 0.5f;
154155
float VtxMaxZPositionAllowed = -1.f;
155156
float VtxClusterCut = -1.f;
156157
float VtxPairCut = -1.f;
@@ -165,6 +166,7 @@ struct TrackingParameters {
165166
float VtxGoodContributorsSignificance = -1.f;
166167
int VtxClusterContributorsCut = -1;
167168
int VtxSuppressLowMultDebris = -1;
169+
float VtxOverpopulatedRofNSigma = -1.f;
168170
};
169171

170172
struct VertexingParameters {

‎Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexUtils.h‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@
1919
#include "SimulationDataFormat/MCCompLabel.h"
2020
#include "ITStracking/Configuration.h"
2121

22+
#include "Framework/Logger.h"
23+
24+
#include <algorithm>
25+
#include <cmath>
2226
#include <limits>
2327
#include <unordered_map>
2428
#include <utility>
29+
#include <vector>
2530

2631
namespace o2::its
2732
{
@@ -60,6 +65,65 @@ inline Vertex makeDiamondVertex(const TrackingParameters& trkParam)
6065
return diamond;
6166
}
6267

68+
/// Good lines a further vertex needs to not count as debris in a ROF that already has one: goodSig * sqrt(ROF load), clamped to
69+
/// [constants::VtxMinGoodThreshold, suppressLowMultDebris] unless the debris cut is off (UPC pass).
70+
inline float getDebrisThreshold(const float goodSig, const double rofLoad, const int suppressLowMultDebris)
71+
{
72+
const float threshold = goodSig * std::sqrt(static_cast<float>(std::max(rofLoad, 1.)));
73+
if (suppressLowMultDebris < constants::VtxMinGoodThreshold) {
74+
return threshold;
75+
}
76+
return std::clamp(threshold, constants::VtxMinGoodThreshold, static_cast<float>(suppressLowMultDebris));
77+
}
78+
79+
/// Caps ROFs whose vertex count is an outlier of the TF
80+
template <typename VtxVec, typename LabVec>
81+
int pruneOverpopulatedRofs(std::vector<VtxVec>& rofVertices, std::vector<LabVec>& rofLabels, const float nSigma, const int minContributors, const float trimFraction)
82+
{
83+
const int nRofs = static_cast<int>(rofVertices.size());
84+
if (nSigma <= 0.f || nRofs == 0) {
85+
return 0;
86+
}
87+
std::vector<int> counts(nRofs);
88+
for (int r = 0; r < nRofs; ++r) {
89+
counts[r] = static_cast<int>(rofVertices[r].size());
90+
}
91+
std::vector<int> sorted(counts);
92+
std::sort(sorted.begin(), sorted.end());
93+
const int nUsed = std::max(1, nRofs - std::max(1, static_cast<int>(trimFraction * nRofs)));
94+
double sum = 0.;
95+
for (int i = 0; i < nUsed; ++i) {
96+
sum += sorted[i];
97+
}
98+
const double mean = sum / nUsed;
99+
const double threshold = mean + nSigma * std::sqrt(mean + 1.);
100+
int removed = 0;
101+
for (int r = 0; r < nRofs; ++r) {
102+
if (counts[r] <= threshold) {
103+
continue;
104+
}
105+
auto& vtx = rofVertices[r];
106+
const bool withLabels = static_cast<int>(rofLabels.size()) == nRofs && rofLabels[r].size() == vtx.size();
107+
size_t out = 1; // the largest vertex always stays
108+
for (size_t i = 1; i < vtx.size(); ++i) {
109+
if (vtx[i].getNContributors() >= minContributors) {
110+
vtx[out] = vtx[i];
111+
if (withLabels) {
112+
rofLabels[r][out] = rofLabels[r][i];
113+
}
114+
++out;
115+
}
116+
}
117+
LOGP(info, "Seeding vertexer: overpopulated ROF {} pruned {} -> {} vertices (threshold {:.1f}, mean {:.2f} per ROF)", r, vtx.size(), out, threshold, mean);
118+
removed += static_cast<int>(vtx.size() - out);
119+
vtx.erase(vtx.begin() + out, vtx.end());
120+
if (withLabels) {
121+
rofLabels[r].erase(rofLabels[r].begin() + out, rofLabels[r].end());
122+
}
123+
}
124+
return removed;
125+
}
126+
63127
} // namespace o2::its
64128

65129
#endif /* O2_ITS_TRACKING_VERTEXUTILS_H_ */

‎Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ std::vector<TrackingParameters> TrackingMode::getTrackingParameters(TrackingMode
308308
seedingPass.VertPerRofThreshold = vc.vertPerRofThreshold;
309309
seedingPass.VtxPhiCut = vc.phiCut;
310310
seedingPass.VtxLineMinPt = vc.lineMinPt;
311+
seedingPass.VtxLineCurvatureScale = vc.lineCurvatureScale;
311312
seedingPass.VtxMaxZPositionAllowed = vc.maxZPositionAllowed;
312313
seedingPass.VtxClusterCut = vc.clusterCut;
313314
seedingPass.VtxPairCut = vc.pairCut;
@@ -322,6 +323,7 @@ std::vector<TrackingParameters> TrackingMode::getTrackingParameters(TrackingMode
322323
seedingPass.VtxGoodContributorsSignificance = vc.goodContributorsSignificance;
323324
seedingPass.VtxClusterContributorsCut = vc.clusterContributorsCut;
324325
seedingPass.VtxSuppressLowMultDebris = vc.suppressLowMultDebris;
326+
seedingPass.VtxOverpopulatedRofNSigma = vc.overpopulatedRofNSigma;
325327
std::vector<TrackingParameters> seedingPasses;
326328
seedingPasses.push_back(seedingPass);
327329

‎Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ void TrackerTraits<NLayers>::computeVertexCandidates(const int iteration)
305305
const float lineMinPt = mTrkParams[iteration].VtxLineMinPt;
306306
const float beamX = mTimeFrame->getBeamX();
307307
const float beamY = mTimeFrame->getBeamY();
308+
const float lineCurvatureScale = mTrkParams[iteration].VtxLineCurvatureScale;
308309
auto makeKey = [](float attribute, int cellIdx) -> size_t {
309310
const uint32_t attributeInt = std::bit_cast<uint32_t>(attribute);
310311
return (static_cast<size_t>(attributeInt) << 32) | static_cast<uint32_t>(cellIdx);
@@ -327,8 +328,7 @@ void TrackerTraits<NLayers>::computeVertexCandidates(const int iteration)
327328
kCl1[k] = c1;
328329
kCl2[k] = cell.getThirdClusterIndex();
329330
std::array<float, 3> origin, direction;
330-
cell.getXYZGlo(origin);
331-
if (!cell.getPxPyPzGlo(direction)) {
331+
if (!getCellLineAtBeam(cell, beamX, beamY, getBz(), lineCurvatureScale, origin, direction)) {
332332
return;
333333
}
334334
kGeomOk[k] = 1;
@@ -711,11 +711,11 @@ void TrackerTraits<NLayers>::computeVertices(const int iteration)
711711
}
712712
}
713713
}
714-
const float sigThreshold = goodSig > 0.f ? goodSig * std::sqrt(static_cast<float>(std::max(rofLoad, 1.))) : 0.f;
714+
const float debrisThreshold = goodSig > 0.f ? getDebrisThreshold(goodSig, rofLoad, suppressLowMultDebris) : 0.f;
715715
for (const int p : accepted) {
716716
if (!rofVertices[rofId].empty()) {
717717
if (goodSig > 0.f) {
718-
if (nGoodCand[p] <= sigThreshold) {
718+
if (nGoodCand[p] < debrisThreshold) {
719719
continue;
720720
}
721721
} else if (static_cast<int>(cand[p].getSize()) < suppressLowMultDebris) {
@@ -758,6 +758,9 @@ void TrackerTraits<NLayers>::computeVertices(const int iteration)
758758
});
759759
});
760760
}
761+
if (!tp.PassFlags[IterationStep::MarkVerticesAsUPC]) { // UPC ROFs are near-empty by construction
762+
pruneOverpopulatedRofs(rofVertices, rofLabels, tp.VtxOverpopulatedRofNSigma, suppressLowMultDebris, constants::VtxOverpopulatedRofTrimFraction);
763+
}
761764
for (int rofId{0}; rofId < nRofs; ++rofId) {
762765
for (auto& vertex : rofVertices[rofId]) {
763766
mTimeFrame->addPrimaryVertex(vertex);

‎Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/Constants.h‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ constexpr int MinNumberOfConcurrentSeeds = (1 << 8); // minimum chunk size for
4141
constexpr int MaxNumberOfConcurrentSeeds = (1 << 12); // maximum chunk size for a worker for the final track fit/extraploation step
4242
constexpr float MaxTrackSeedQ2Pt = 1.e3f; // maximum q/pt for track seeds
4343

44-
constexpr int MaxBootstrapPasses = 5; // beam bootstrap: cap on the re-trackleting passes
45-
constexpr float BeamConvergence2 = 5.e-3f * 5.e-3f; // beam bootstrap: stop below a (50 um)^2 beam shift
44+
constexpr int MaxBootstrapPasses = 5; // beam bootstrap: cap on the re-trackleting passes
45+
constexpr float BeamConvergence2 = 5.e-3f * 5.e-3f; // beam bootstrap: stop below a (50 um)^2 beam shift
46+
constexpr float VtxMinGoodThreshold = 2.f; // seeding emit: floor of the k*sqrt(ROF load) debris threshold (bounds included)
47+
constexpr float VtxOverpopulatedRofTrimFraction = 0.02f; // overpopulated-ROF pruning: busiest fraction of ROFs left out of the per-ROF mean
4648

4749
namespace helpers
4850
{

‎Detectors/ITSMFT/common/tracking/include/ITSMFTTracking/ITSTrackingConfigParam.h‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,17 @@ struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper<VertexerPa
4242
float maxZPositionAllowed = 25.f; // 4x sZ of the beam
4343

4444
// Artefacts selections
45-
int clusterContributorsCut = 2; // minimum number of contributors for an accepted final vertex
46-
int suppressLowMultDebris = 16; // suppress all vertices below this threshold if a vertex was already found in a rof
47-
float lineMinPt = 0.10f; // drop soft lines before the density scan
48-
float fineZWindow = 0.010f; // second, narrow density pass (dip search); <=0 disables
45+
int clusterContributorsCut = 2; // minimum number of contributors for an accepted final vertex
46+
int suppressLowMultDebris = 16; // suppress all vertices below this threshold if a vertex was already found in a rof
47+
float lineMinPt = 0.10f; // drop soft lines before the density scan
48+
float lineCurvatureScale = 0.5f; // q/pT scale when propagating a seeding line to its xy-DCA to the beam
49+
float fineZWindow = 0.010f; // second, narrow density pass (dip search); <=0 disables
4950
int fineMinDensity = 8;
5051
float fineMaxDrift = 0.005f; // |z_fit - z_seed| cap on fine-only candidates; <=0 disables
5152
float goodLineChi2Cut = 5.f;
5253
float goodLinePtCut = 0.5f;
53-
float goodContributorsSignificance = 0.070f; // emit threshold k, scaled by sqrt(ROF load); <=0 disables
54+
float goodContributorsSignificance = 0.070f; // emit threshold k, scaled by sqrt(ROF load) and clamped to [constants::VtxMinGoodThreshold (2), suppressLowMultDebris], bounds included; <=0 disables
55+
float overpopulatedRofNSigma = -1.f; // a ROF with more seeding vertices than mean + overpopulatedRofNSigma*sqrt(mean+1) of its TF keeps its largest vertex and those with >= suppressLowMultDebris contributors (not in the UPC pass); <=0 disables
5456
float duplicateZScale = 0.7f; // per-candidate dedup radius scale/sqrt(size); <=0 uses duplicateZCut
5557
int seedMemberRadiusTime = 0;
5658
int seedMemberRadiusZ = 2;

0 commit comments

Comments
 (0)