Skip to content

Commit d8a8978

Browse files
committed
ITS: new CPU + GPU seeding vertexer
Adds a seeding vertexer that runs as a prepended tracker pass (diamond trackleting -> cells -> lines -> parallel seeding), on both the CPU and GPU traits, replacing the per-ROF CPU vertexer for the seeding step.
1 parent 1d64083 commit d8a8978

27 files changed

Lines changed: 2622 additions & 99 deletions

DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/Vertex.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include "GPUCommonDef.h"
1616
#ifndef GPUCA_GPUCODE_DEVICE
1717
#include <type_traits>
18+
#include <unordered_map>
19+
#include <utility>
1820
#endif
1921
#include "ReconstructionDataFormats/Vertex.h"
2022
#include "SimulationDataFormat/MCCompLabel.h"
@@ -25,6 +27,36 @@ namespace o2::its
2527
// NOTE: this uses the internal asymmetrical time reprenstation!
2628
using Vertex = o2::dataformats::Vertex<o2::its::TimeEstBC>;
2729
using VertexLabel = std::pair<o2::MCCompLabel, float>;
30+
31+
#ifndef GPUCA_GPUCODE_DEVICE
32+
/// Majority-vote MC label of a vertex: the most frequent (source, event) among its
33+
/// contributors, flagged fake when no label reaches more than half of them. Templated
34+
/// on the container so both bounded_vector and std::vector callers share one copy
35+
template <typename Container>
36+
VertexLabel computeMainVertexLabel(const Container& elements)
37+
{
38+
// we only care about the source&event of the tracks, not the trackId
39+
auto composeVtxLabel = [](const o2::MCCompLabel& lbl) -> o2::MCCompLabel {
40+
return {o2::MCCompLabel::maxTrackID(), lbl.getEventID(), lbl.getSourceID(), lbl.isFake()};
41+
};
42+
std::unordered_map<o2::MCCompLabel, size_t> frequency;
43+
for (const auto& element : elements) {
44+
++frequency[composeVtxLabel(element)];
45+
}
46+
o2::MCCompLabel elem{};
47+
size_t maxCount = 0;
48+
for (const auto& [key, count] : frequency) {
49+
if (count > maxCount) {
50+
maxCount = count;
51+
elem = key;
52+
}
53+
}
54+
if (maxCount <= 1) { // need >50%
55+
elem.setFakeFlag();
56+
}
57+
return std::make_pair(elem, static_cast<float>(maxCount) / static_cast<float>(elements.size()));
58+
}
59+
#endif
2860
} // namespace o2::its
2961

3062
#ifndef GPUCA_GPUCODE_DEVICE
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
// Copyright 2019-2020 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.
11+
12+
/// \file ClusterLinesGPU.h
13+
/// \brief device-side line + N-line vertex fit for the GPU seeding vertexer.
14+
15+
#ifndef O2_ITS_CLUSTERLINES_GPU_H
16+
#define O2_ITS_CLUSTERLINES_GPU_H
17+
18+
#include "DataFormatsITS/TimeEstBC.h"
19+
#include "GPUCommonDef.h"
20+
#include "GPUCommonMath.h"
21+
#include "ITStracking/LineProjection.h"
22+
23+
namespace o2::its::gpu
24+
{
25+
26+
using LineWindow = o2::its::LineWindow;
27+
28+
struct LineProjSoA {
29+
float* z{nullptr}; // projected z at the beamline; sort key and binary-search key, kept dense
30+
o2::its::TimeEstBC* t{nullptr}; // time interval of the line
31+
int* idx{nullptr}; // sorted slot -> original line index
32+
int* rof{nullptr}; // ROF of the line
33+
};
34+
35+
struct VertexCand {
36+
float x, y, z;
37+
float rms2[6];
38+
float avgDist2;
39+
int nGood;
40+
float seed[3];
41+
o2::its::TimeEstBC time;
42+
int size;
43+
uint8_t ok; // 1 if the candidate passed the fit cuts
44+
uint8_t keep; // 1 if it survived duplicate suppression (subset of ok)
45+
uint8_t fine;
46+
};
47+
48+
// Device-side line: origin point + unit direction, with a time stamp
49+
struct GPULine {
50+
GPUhdDefault() GPULine() = default;
51+
52+
GPUhdi() GPULine(const float origin[3], const float direction[3], const o2::its::TimeEstBC& t) : mTime(t)
53+
{
54+
const float norm = o2::gpu::GPUCommonMath::Sqrt(direction[0] * direction[0] +
55+
direction[1] * direction[1] +
56+
direction[2] * direction[2]);
57+
const float inv = norm > 0.f ? 1.f / norm : 0.f;
58+
for (int i = 0; i < 3; ++i) {
59+
originPoint[i] = origin[i];
60+
cosinesDirector[i] = direction[i] * inv;
61+
}
62+
}
63+
64+
// Squared distance from a point to the (infinite) line: |delta - (delta.u) u|^2
65+
GPUhdi() static float getDistance2FromPoint(const GPULine& line, const float point[3])
66+
{
67+
float delta[3];
68+
float proj = 0.f;
69+
for (int i = 0; i < 3; ++i) {
70+
delta[i] = point[i] - line.originPoint[i];
71+
proj += delta[i] * line.cosinesDirector[i];
72+
}
73+
float d2 = 0.f;
74+
for (int i = 0; i < 3; ++i) {
75+
const float residual = delta[i] - proj * line.cosinesDirector[i];
76+
d2 += residual * residual;
77+
}
78+
return d2;
79+
}
80+
81+
GPUhdi() static void getDCAComponents(const GPULine& line, const float point[3], float out[6])
82+
{
83+
float delta[3];
84+
float proj = 0.f;
85+
for (int i = 0; i < 3; ++i) {
86+
delta[i] = line.originPoint[i] - point[i];
87+
proj += delta[i] * line.cosinesDirector[i];
88+
}
89+
float r[3];
90+
for (int i = 0; i < 3; ++i) {
91+
r[i] = delta[i] - proj * line.cosinesDirector[i];
92+
}
93+
out[0] = r[0]; // (0,0) XX
94+
out[1] = o2::gpu::GPUCommonMath::Hypot(r[0], r[1]); // (0,1) XY
95+
out[2] = r[1]; // (1,1) YY
96+
out[3] = o2::gpu::GPUCommonMath::Hypot(r[0], r[2]); // (0,2) XZ
97+
out[4] = o2::gpu::GPUCommonMath::Hypot(r[1], r[2]); // (1,2) YZ
98+
out[5] = r[2]; // (2,2) ZZ
99+
}
100+
101+
float originPoint[3] = {0.f, 0.f, 0.f};
102+
float cosinesDirector[3] = {0.f, 0.f, 0.f};
103+
o2::its::TimeEstBC mTime;
104+
};
105+
106+
class GPUClusterLinesFit
107+
{
108+
public:
109+
GPUhdDefault() GPUClusterLinesFit() = default;
110+
111+
// Add one line's contribution: A_ij += (delta_ij*|d|^2 - d_i*d_j)/|d|^2,
112+
// b_i += (d_i*(d.o) - |d|^2*o_i)/|d|^2. For a unit director |d|^2 == 1.
113+
GPUhdi() void add(const GPULine& line)
114+
{
115+
const double d0 = line.cosinesDirector[0], d1 = line.cosinesDirector[1], d2 = line.cosinesDirector[2];
116+
const double o0 = line.originPoint[0], o1 = line.originPoint[1], o2 = line.originPoint[2];
117+
const double det = d0 * d0 + d1 * d1 + d2 * d2; // == 1 for a normalised director
118+
if (det <= 0.) {
119+
return;
120+
}
121+
if (mNContributors <= 0) {
122+
mTime = line.mTime;
123+
} else {
124+
mTime += line.mTime;
125+
}
126+
mA[0] += (det - d0 * d0) / det;
127+
mA[1] += (-d0 * d1) / det;
128+
mA[2] += (-d0 * d2) / det;
129+
mA[3] += (det - d1 * d1) / det;
130+
mA[4] += (-d1 * d2) / det;
131+
mA[5] += (det - d2 * d2) / det;
132+
const double dDotO = d0 * o0 + d1 * o1 + d2 * o2;
133+
mB[0] += (d0 * dDotO - det * o0) / det;
134+
mB[1] += (d1 * dDotO - det * o1) / det;
135+
mB[2] += (d2 * dDotO - det * o2) / det;
136+
++mNContributors;
137+
}
138+
139+
// Solve the symmetric system and write the vertex (= -A^-1 B)
140+
GPUhdi() bool solve(float vertex[3]) const
141+
{
142+
const double a = mA[0], b = mA[1], c = mA[2], d = mA[3], e = mA[4], f = mA[5];
143+
const double c00 = d * f - e * e;
144+
const double c01 = c * e - b * f;
145+
const double c02 = b * e - c * d;
146+
const double c11 = a * f - c * c;
147+
const double c12 = b * c - a * e;
148+
const double c22 = a * d - b * b;
149+
const double det = a * c00 + b * c01 + c * c02;
150+
if (o2::gpu::GPUCommonMath::Abs(det) < 1.e-12) {
151+
return false;
152+
}
153+
const double invDet = 1. / det;
154+
const double x0 = (c00 * mB[0] + c01 * mB[1] + c02 * mB[2]) * invDet;
155+
const double x1 = (c01 * mB[0] + c11 * mB[1] + c12 * mB[2]) * invDet;
156+
const double x2 = (c02 * mB[0] + c12 * mB[1] + c22 * mB[2]) * invDet;
157+
vertex[0] = static_cast<float>(-x0);
158+
vertex[1] = static_cast<float>(-x1);
159+
vertex[2] = static_cast<float>(-x2);
160+
return true;
161+
}
162+
163+
GPUhdi() void addResidual(const GPULine& line, const float vertex[3])
164+
{
165+
float dca[6];
166+
GPULine::getDCAComponents(line, vertex, dca);
167+
const float d2 = GPULine::getDistance2FromPoint(line, vertex);
168+
++mResidualCount;
169+
const float inv = 1.f / static_cast<float>(mResidualCount);
170+
for (int i = 0; i < 6; ++i) {
171+
mRMS2[i] += (dca[i] - mRMS2[i]) * inv;
172+
}
173+
mAvgDistance2 += (d2 - mAvgDistance2) * inv;
174+
}
175+
176+
GPUhdi() int getNContributors() const { return mNContributors; }
177+
GPUhdi() const float* getRMS2() const { return mRMS2; } // Packed symmetric covariance in {XX, XY, YY, XZ, YZ, ZZ} order
178+
GPUhdi() float getAvgDistance2() const { return mAvgDistance2; }
179+
GPUhdi() const o2::its::TimeEstBC& getTimeStamp() const { return mTime; }
180+
181+
private:
182+
double mA[6] = {0., 0., 0., 0., 0., 0.};
183+
double mB[3] = {0., 0., 0.};
184+
int mNContributors = 0;
185+
float mRMS2[6] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f};
186+
float mAvgDistance2 = 0.f;
187+
int mResidualCount = 0;
188+
o2::its::TimeEstBC mTime;
189+
};
190+
191+
} // namespace o2::its::gpu
192+
193+
#endif /* O2_ITS_CLUSTERLINES_GPU_H */

0 commit comments

Comments
 (0)