1717#include < array>
1818#ifndef GPUCA_GPUCODE
1919#include < cmath>
20+ #else
21+ #include " GPUCommonAlgorithm.h" // warp reductions of WarpFit
2022#endif
2123#include " ITStracking/Cluster.h"
2224#include " ITStracking/Cell.h"
@@ -141,27 +143,19 @@ GPUdi() bool getCellLineAtBeam(const CellSeed& cell, const float beamX, const fl
141143 return true ;
142144}
143145
144- // / Least-squares vertex fit over a set of lines (the normal equations AX = -B).
145- class ClusterLines final
146+ // / Normal equations AX = -B of the point closest to a set of lines, and their solution. Base of the
147+ // / sequential fit (ClusterLines) and of the device warp-parallel fit (WarpFit), which only differ in how they accumulate.
148+ class LineFit
146149{
147150 public:
148- GPUhdDefault () ClusterLines() = default ;
149- // / Fit over lines[lineIndices[i]]; lineIndices must be sorted and unique.
150- ClusterLines (gsl::span<const int > lineIndices, gsl::span<const Line> lines);
151-
152- // / Accumulate one line into the normal equations and the time estimate.
153- GPUhdi () void add (const Line& line)
151+ // / Accumulate one line
152+ GPUhdi () bool add (const Line& line)
154153 {
155154 const double d0 = line.cosinesDirector [0 ], d1 = line.cosinesDirector [1 ], d2 = line.cosinesDirector [2 ];
156155 const double o0 = line.originPoint [0 ], o1 = line.originPoint [1 ], o2 = line.originPoint [2 ];
157156 const double det = d0 * d0 + d1 * d1 + d2 * d2; // == 1 for a normalised director
158157 if (det <= 0 .) {
159- return ;
160- }
161- if (mNContributors <= 0 ) {
162- mTime = line.mTime ;
163- } else {
164- mTime += line.mTime ;
158+ return false ;
165159 }
166160 mA [0 ] += (det - d0 * d0) / det;
167161 mA [1 ] += (-d0 * d1) / det;
@@ -173,20 +167,19 @@ class ClusterLines final
173167 mB [0 ] += (d0 * dDotO - det * o0) / det;
174168 mB [1 ] += (d1 * dDotO - det * o1) / det;
175169 mB [2 ] += (d2 * dDotO - det * o2) / det;
176- ++ mNContributors ;
170+ return true ;
177171 }
178172
179- // / Solve the symmetric 3x3 system into an external vertex (= -A^-1 B)
173+ // / Solve the symmetric 3x3 system into vertex (= -A^-1 B); false if it is singular
180174 GPUhdi () bool solve (float vertex[3 ]) const
181175 {
182- const double a = mA [0 ], b = mA [1 ], c = mA [2 ], d = mA [3 ], e = mA [4 ], f = mA [5 ];
183- const double c00 = d * f - e * e;
184- const double c01 = c * e - b * f;
185- const double c02 = b * e - c * d;
186- const double c11 = a * f - c * c;
187- const double c12 = b * c - a * e;
188- const double c22 = a * d - b * b;
189- const double det = a * c00 + b * c01 + c * c02;
176+ const double c00 = mA [3 ] * mA [5 ] - mA [4 ] * mA [4 ];
177+ const double c01 = mA [2 ] * mA [4 ] - mA [1 ] * mA [5 ];
178+ const double c02 = mA [1 ] * mA [4 ] - mA [2 ] * mA [3 ];
179+ const double c11 = mA [0 ] * mA [5 ] - mA [2 ] * mA [2 ];
180+ const double c12 = mA [1 ] * mA [2 ] - mA [0 ] * mA [4 ];
181+ const double c22 = mA [0 ] * mA [3 ] - mA [1 ] * mA [1 ];
182+ const double det = mA [0 ] * c00 + mA [1 ] * c01 + mA [2 ] * c02;
190183 if (o2::gpu::GPUCommonMath::Abs (det) < 1 .e -12 ) {
191184 return false ;
192185 }
@@ -203,6 +196,49 @@ class ClusterLines final
203196 // / Solve into the stored vertex, setting the validity flag
204197 GPUhdi () void computeClusterCentroid () { mIsValid = solve (mVertex ); }
205198
199+ GPUhdi () bool isValid () const noexcept { return mIsValid ; }
200+ GPUhdi () const float * getVertex () const noexcept { return mVertex ; }
201+ GPUhdi () int getSize () const noexcept { return mNContributors ; }
202+ GPUhdi () int getNContributors () const noexcept { return mNContributors ; }
203+ GPUhdi () const TimeEstBC& getTimeStamp () const noexcept { return mTime ; }
204+ GPUhdi () float getR2 () const noexcept { return (mVertex [0 ] * mVertex [0 ]) + (mVertex [1 ] * mVertex [1 ]); }
205+ GPUhdi () float getR () const noexcept { return o2::gpu::GPUCommonMath::Sqrt (getR2 ()); }
206+ GPUhdi () const float * getRMS2 () const noexcept { return mRMS2 ; } // {XX, XY, YY, XZ, YZ, ZZ}
207+ GPUhdi () float getAvgDistance2 () const noexcept { return mAvgDistance2 ; }
208+
209+ protected:
210+ double mA [6 ] = {0 ., 0 ., 0 ., 0 ., 0 ., 0 .}; // AX=B, packed symmetric normal matrix
211+ double mB [3 ] = {0 ., 0 ., 0 .}; // AX=B, right-hand side
212+ float mVertex [3 ] = {0 .f , 0 .f , 0 .f }; // cluster centroid position
213+ float mRMS2 [6 ] = {0 .f , 0 .f , 0 .f , 0 .f , 0 .f , 0 .f }; // mean DCA components about the vertex
214+ float mAvgDistance2 = 0 .f; // mean squared distance about the vertex
215+ int mNContributors = 0 ; // lines accumulated into the normal equations
216+ bool mIsValid = false ; // true if the linear system was solved successfully
217+ TimeEstBC mTime ; // intersection of the contributors' time intervals
218+ };
219+
220+ // / Least-squares vertex fit over a set of lines (the normal equations AX = -B).
221+ class ClusterLines final : public LineFit
222+ {
223+ public:
224+ GPUhdDefault () ClusterLines() = default ;
225+ // / Fit over lines[lineIndices[i]]; lineIndices must be sorted and unique.
226+ ClusterLines (gsl::span<const int > lineIndices, gsl::span<const Line> lines);
227+
228+ // / Accumulate one line into the normal equations and the time estimate.
229+ GPUhdi () void add (const Line& line)
230+ {
231+ if (!LineFit::add (line)) {
232+ return ;
233+ }
234+ if (mNContributors <= 0 ) {
235+ mTime = line.mTime ;
236+ } else {
237+ mTime += line.mTime ;
238+ }
239+ ++mNContributors ;
240+ }
241+
206242 // / Running-mean update of the RMS2 and average distance about the stored vertex
207243 GPUhdi () void addResidual (const Line& line) { addResidual (line, mVertex ); }
208244
@@ -220,28 +256,66 @@ class ClusterLines final
220256 mAvgDistance2 += (d2 - mAvgDistance2 ) / static_cast <float >(mResidualCount );
221257 }
222258
223- GPUhdi () bool isValid () const noexcept { return mIsValid ; }
224- GPUhdi () const float * getVertex () const noexcept { return mVertex ; }
225- GPUhdi () const float * getRMS2 () const noexcept { return mRMS2 ; } // {XX, XY, YY, XZ, YZ, ZZ}
226- GPUhdi () float getAvgDistance2 () const noexcept { return mAvgDistance2 ; }
227- GPUhdi () int getSize () const noexcept { return mNContributors ; }
228- GPUhdi () int getNContributors () const noexcept { return mNContributors ; }
229- GPUhdi () const TimeEstBC& getTimeStamp () const noexcept { return mTime ; }
230- GPUhdi () float getR2 () const noexcept { return (mVertex [0 ] * mVertex [0 ]) + (mVertex [1 ] * mVertex [1 ]); }
231- GPUhdi () float getR () const noexcept { return o2::gpu::GPUCommonMath::Sqrt (getR2 ()); }
232259 bool operator ==(const ClusterLines& rhs) const noexcept ;
233260
234261 private:
235- double mA [6 ] = {0 ., 0 ., 0 ., 0 ., 0 ., 0 .}; // AX=B, packed symmetric normal matrix
236- double mB [3 ] = {0 ., 0 ., 0 .}; // AX=B, right-hand side
237- float mVertex [3 ] = {0 .f , 0 .f , 0 .f }; // cluster centroid position
238- float mRMS2 [6 ] = {0 .f , 0 .f , 0 .f , 0 .f , 0 .f , 0 .f };
239- float mAvgDistance2 = 0 .f;
240- int mNContributors = 0 ;
241- int mResidualCount = 0 ;
242- bool mIsValid = false ; // true if the linear system was solved successfully
243- TimeEstBC mTime ;
262+ int mResidualCount = 0 ; // lines folded into the running means
263+ };
264+
265+ #ifdef GPUCA_GPUCODE
266+ // / Device counterpart of ClusterLines: each lane of a warp add()s its own subset of lines, then reduce() leaves
267+ // / the full sums, contributor count and time intersection in every lane
268+ struct WarpFit : public LineFit {
269+ uint32_t tLo = 0 , tHi = 0xffffffffu ;
270+
271+ GPUdi () void add (const Line& l)
272+ {
273+ if (LineFit::add (l)) {
274+ tLo = o2::gpu::GPUCommonMath::Max (tLo, l.mTime .lower ());
275+ tHi = o2::gpu::GPUCommonMath::Min (tHi, l.mTime .upper ());
276+ ++mNContributors ;
277+ }
278+ }
279+
280+ template <int Width>
281+ GPUdi () void reduce ()
282+ {
283+ for (int i = 0 ; i < 6 ; ++i) {
284+ mA [i] = warp_reduce_add<Width>(mA [i]);
285+ }
286+ for (int i = 0 ; i < 3 ; ++i) {
287+ mB [i] = warp_reduce_add<Width>(mB [i]);
288+ }
289+ tLo = warp_reduce_max<Width>(tLo);
290+ tHi = warp_reduce_min<Width>(tHi);
291+ mNContributors = warp_reduce_add<Width>(mNContributors );
292+ mTime = TimeEstBC{tLo, tHi - tLo};
293+ }
294+
295+ GPUdi () void addResidual (const Line& l, const float vertex[3 ])
296+ {
297+ float dca[6 ];
298+ Line::getDCAComponents (l, vertex, dca);
299+ for (int i = 0 ; i < 6 ; ++i) {
300+ rms2Sum[i] += dca[i];
301+ }
302+ dist2Sum += Line::getDistance2FromPoint (l, vertex);
303+ }
304+
305+ // / Means over the n lines of the whole warp, left in every lane
306+ template <int Width>
307+ GPUdi () void reduceResiduals (const int n)
308+ {
309+ for (int i = 0 ; i < 6 ; ++i) {
310+ mRMS2 [i] = warp_reduce_add<Width>(rms2Sum[i]) / n;
311+ }
312+ mAvgDistance2 = warp_reduce_add<Width>(dist2Sum) / n;
313+ }
314+
315+ double rms2Sum[6 ] = {0 ., 0 ., 0 ., 0 ., 0 ., 0 .};
316+ double dist2Sum = 0 .;
244317};
318+ #endif
245319
246320} // namespace o2::its
247321#endif /* O2_ITS_CLUSTERLINES_H */
0 commit comments