Project
Loading...
Searching...
No Matches
GPUTPCNNClusterizerKernels.cxx
Go to the documentation of this file.
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
14
15#include "clusterFinderDefs.h"
16#include "PackedCharge.h"
18#include "GPUConstantMem.h"
19#include "GPUTPCClusterFinder.h"
20#include "GPUTPCGeometry.h"
21
22using namespace o2::gpu;
23using namespace o2::gpu::tpccf;
24
25#include "CfConsts.h"
26#include "CfUtils.h"
27#include "ClusterAccumulator.h"
28#ifdef __METAL__
29namespace o2::OrtDataType
30{
31struct Float16_t {
32 half val;
33 Float16_t() = default;
34 explicit Float16_t(float v) : val(v) {}
35 float ToFloat() const { return (float)val; }
36 explicit operator float() const { return ToFloat(); }
37};
38} // namespace o2::OrtDataType
39#else
41#endif
42
43#if !defined(GPUCA_GPUCODE)
44#include "GPUHostDataTypes.h"
45#include "MCLabelAccumulator.h"
46#endif
47
48#ifdef GPUCA_GPUCODE
49#include "GPUTPCCFClusterizer.inc"
50#endif
51
52static_assert(GPUTPCNNClusterizerKernels::SCRATCH_PAD_WORK_GROUP_SIZE == GPUTPCCFClusterizer::SCRATCH_PAD_WORK_GROUP_SIZE, "Work group sizes do not match");
53
54// Defining individual thread functions for data filling, determining the class label and running the CF clusterizer
55template <>
56GPUdii() void GPUTPCNNClusterizerKernels::Thread<GPUTPCNNClusterizerKernels::runCfClusterizer>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& processors, uint8_t sector, int8_t dtype, int8_t withMC, uint32_t batchStart)
57{
58 uint32_t glo_idx = get_global_id(0);
59 auto& clusterer = processors.tpcClusterer[sector];
60 auto& clustererNN = processors.tpcNNClusterer[sector];
61 CfArray2D<PackedCharge> chargeMap(reinterpret_cast<PackedCharge*>(clusterer.mPchargeMap));
62 CPU_ONLY(MCLabelAccumulator labelAcc(clusterer));
63 tpc::ClusterNative* clusterOut = clusterer.mPclusterByRow;
64 int8_t isAccepted = (clustererNN.mNnClusterizerUseClassification ? (clustererNN.mOutputDataClass[CAMath::Min(glo_idx, (uint32_t)clusterer.mPmemory->counters.nClusters - 1)] > 0) : 1);
65 GPUTPCCFClusterizer::computeClustersImpl(get_num_groups(0), get_local_size(0), get_group_id(0), get_local_id(0), clusterer, clusterer.mPmemory->frag, smem, chargeMap, clusterer.mPfilteredPeakPositions, clusterer.Param().rec, CPU_PTR(&labelAcc), clusterer.mPmemory->counters.nClusters, clusterer.mNMaxClusterPerRow, clusterer.mPclusterInRow, clusterOut, clusterer.mPclusterPosInRow, isAccepted);
66}
67
68template <>
69GPUdii() void GPUTPCNNClusterizerKernels::Thread<GPUTPCNNClusterizerKernels::fillInputNNCPU>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& processors, uint8_t sector, int8_t dtype, int8_t withMC, uint32_t batchStart)
70{
71 auto& clusterer = processors.tpcClusterer[sector];
72 auto& clustererNN = processors.tpcNNClusterer[sector];
73
74 const uint32_t glo_idx = get_global_id(0);
75 if (glo_idx + batchStart >= clusterer.mPmemory->counters.nClusters || glo_idx >= (uint32_t)clustererNN.mNnClusterizerBatchedMode) {
76 return;
77 }
78
79 uint32_t write_idx = glo_idx * clustererNN.mNnClusterizerElementSize;
80
81 CfArray2D<PackedCharge> chargeMap(reinterpret_cast<PackedCharge*>(clusterer.mPchargeMap));
82 CfArray2D<uint8_t> isPeakMap(clusterer.mPpeakMap);
83 CfChargePos peak = clusterer.mPfilteredPeakPositions[CAMath::Min(glo_idx + batchStart, (uint32_t)(clusterer.mPmemory->counters.nClusters - 1))];
84 const int32_t row = static_cast<int>(peak.row());
85 const int32_t pad = static_cast<int>(peak.pad());
86 const int32_t time = static_cast<int>(peak.time());
87 const float central_charge = static_cast<float>(chargeMap[peak].unpack());
88 const float inverse_charge = 1.f / central_charge;
89
90 const int32_t row_offset = GPUTPCNNClusterizerKernels::rowOffset(row, clustererNN.mNnClusterizerSizeInputRow);
91 const int32_t iroc_row = 63 + clustererNN.mNnClusterizerSizeInputRow;
92 const int32_t maxrow = o2::tpc::constants::MAXGLOBALPADROW + clustererNN.mNnClusterizerSizeInputRow;
93 const int32_t npads_row = GPUTPCGeometry::NPads(row);
94 float output_value = clustererNN.mNnClusterizerBoundaryFillValue;
95
96 for (int32_t target_row = -clustererNN.mNnClusterizerSizeInputRow + row; target_row <= clustererNN.mNnClusterizerSizeInputRow + row; ++target_row) {
97 uint8_t is_boundary = (target_row < 0) || (target_row >= o2::tpc::constants::MAXGLOBALPADROW);
98 const int32_t p_local = pad + (is_boundary ? 0 : GPUTPCNNClusterizerKernels::padOffset(row, target_row));
99 const int32_t npads_reference = is_boundary ? 0 : GPUTPCGeometry::NPads(target_row - row_offset);
100
101 for (int32_t target_pad = -clustererNN.mNnClusterizerSizeInputPad + p_local; target_pad <= clustererNN.mNnClusterizerSizeInputPad + p_local; ++target_pad) {
102 is_boundary = is_boundary || GPUTPCNNClusterizerKernels::isBoundary(target_row + row_offset, target_pad, maxrow, iroc_row, npads_row, npads_reference);
103
104 for (int32_t target_time = -clustererNN.mNnClusterizerSizeInputTime + time; target_time <= clustererNN.mNnClusterizerSizeInputTime + time; ++target_time) {
105 if (is_boundary || target_time < 0 || target_time >= clustererNN.maxAllowedTimebin) {
106 // Fill boundary value
107 output_value = clustererNN.mNnClusterizerBoundaryFillValue;
108 if (dtype == 0) {
109 clustererNN.mInputData_32[write_idx] = output_value;
110 } else {
111 clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)output_value;
112 }
113 } else {
114 CfChargePos tmp_pos(target_row, target_pad, target_time);
115 output_value = chargeMap[tmp_pos].unpack() * inverse_charge;
116 if (dtype == 0) {
117 clustererNN.mInputData_32[write_idx] = output_value;
118 } else {
119 clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)output_value;
120 }
121 }
122 // if((CAMath::Abs(static_cast<float>(clustererNN.mInputData_16_Test[write_idx]) - static_cast<float>(clustererNN.mInputData_16[write_idx])) > 1e-4) && ((glo_idx + batchStart) < clusterer.mPmemory->counters.nClusters)) {
123 // printf("Warning: Input data mismatch at index %d, %d - row, pad, time: %d, %d, %d : %f -> %f\n", glo_idx, glo_idx + batchStart, r, p, t,
124 // static_cast<float>(clustererNN.mInputData_16_Test[write_idx]), static_cast<float>(clustererNN.mInputData_16[write_idx]));
125 // }
126 write_idx++;
127 }
128 }
129 }
130
131 if (clustererNN.mNnClusterizerAddIndexData) {
132 if (dtype == 0) {
133 clustererNN.mInputData_32[write_idx] = static_cast<float>(sector) / o2::tpc::constants::MAXSECTOR;
134 clustererNN.mInputData_32[write_idx + 1] = static_cast<float>(row) / o2::tpc::constants::MAXGLOBALPADROW;
135 clustererNN.mInputData_32[write_idx + 2] = static_cast<float>(pad) / npads_row;
136 } else {
137 clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)(static_cast<float>(sector) / o2::tpc::constants::MAXSECTOR);
138 clustererNN.mInputData_16[write_idx + 1] = (OrtDataType::Float16_t)(static_cast<float>(row) / o2::tpc::constants::MAXGLOBALPADROW);
139 clustererNN.mInputData_16[write_idx + 2] = (OrtDataType::Float16_t)(static_cast<float>(pad) / npads_row);
140 }
141 }
142
143 if (!clustererNN.mNnClusterizerSetDeconvolutionFlags) {
144 clustererNN.mClusterFlags[2 * glo_idx] = 0;
145 clustererNN.mClusterFlags[2 * glo_idx + 1] = 0;
146
147 for (uint16_t i = 0; i < 8; ++i) {
148 Delta2 d = cfconsts::InnerNeighbors[i];
149 CfChargePos tmp_pos = peak.delta(d);
150 clustererNN.mClusterFlags[2 * glo_idx] += CfUtils::isPeak(isPeakMap[tmp_pos]);
151 }
152 clustererNN.mClusterFlags[2 * glo_idx + 1] = clustererNN.mClusterFlags[2 * glo_idx];
153 }
154}
155
156template <>
157GPUdii() void GPUTPCNNClusterizerKernels::Thread<GPUTPCNNClusterizerKernels::fillInputNNGPU>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& processors, uint8_t sector, int8_t dtype, int8_t withMC, uint32_t batchStart)
158{
159 // Statically quantized S8S8 ONNX models with graph-contained scaling expose
160 // FP32 graph boundaries and must use dtype == 0 here. Their QuantizeLinear
161 // nodes execute on the ONNX GPU stream. dtype == 2 is reserved for models
162 // with true external INT8 I/O and requires matching external scale metadata.
163 const uint32_t glo_idx = get_global_id(0);
164 auto& clusterer = processors.tpcClusterer[sector];
165 auto& clustererNN = processors.tpcNNClusterer[sector];
166
167 if (glo_idx >= (uint32_t)clustererNN.mNnClusterizerBatchedMode * clustererNN.mNnClusterizerRowTimeSizeThreads) {
168 return;
169 }
170
171 const uint32_t base_idx = glo_idx / clustererNN.mNnClusterizerRowTimeSizeThreads;
172 const uint32_t transient_index = glo_idx - (base_idx * clustererNN.mNnClusterizerRowTimeSizeThreads);
173
174 // Early exit for out-of-bounds threads
175 if (base_idx + batchStart >= clusterer.mPmemory->counters.nClusters) {
176 return;
177 }
178 CfArray2D<PackedCharge> chargeMap(reinterpret_cast<PackedCharge*>(clusterer.mPchargeMap));
179 CfArray2D<uint8_t> isPeakMap(clusterer.mPpeakMap);
180
181 // Use dedicated neural network shared memory arrays for warp-level caching
182 // First thread in each warp loads shared data
183 CfChargePos peak = clusterer.mPfilteredPeakPositions[CAMath::Min(base_idx + batchStart, (uint32_t)(clusterer.mPmemory->counters.nClusters - 1))];
184 const float central_charge = chargeMap[peak].unpack();
185 const int32_t row = static_cast<int>(peak.row());
186 const int32_t pad = static_cast<int>(peak.pad());
187 const int32_t time = static_cast<int>(peak.time());
188
189 // Handle index data with fewer branches
190 if (clustererNN.mNnClusterizerAddIndexData && transient_index >= clustererNN.mNnClusterizerRowTimeSize) {
191 uint32_t write_idx = base_idx * clustererNN.mNnClusterizerElementSize + clustererNN.mNnClusterizerChargeArraySize;
192 const int32_t npads = GPUTPCGeometry::NPads(row);
193 if (dtype == 0) {
194 clustererNN.mInputData_32[write_idx] = static_cast<float>(sector) / o2::tpc::constants::MAXSECTOR;
195 clustererNN.mInputData_32[write_idx + 1] = static_cast<float>(row) / o2::tpc::constants::MAXGLOBALPADROW;
196 clustererNN.mInputData_32[write_idx + 2] = static_cast<float>(pad) / npads;
197 } else {
198 clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)(static_cast<float>(sector) / o2::tpc::constants::MAXSECTOR);
199 clustererNN.mInputData_16[write_idx + 1] = (OrtDataType::Float16_t)(static_cast<float>(row) / o2::tpc::constants::MAXGLOBALPADROW);
200 clustererNN.mInputData_16[write_idx + 2] = (OrtDataType::Float16_t)(static_cast<float>(pad) / npads);
201 }
202 }
203
204 // Main data processing - optimize index calculations
205 if (transient_index < clustererNN.mNnClusterizerRowTimeSize) {
206 // Optimize 3D index calculation
207 const int32_t row_idx = transient_index / clustererNN.mNnClusterizerFullTimeSize;
208 const int32_t time_idx = transient_index - row_idx * clustererNN.mNnClusterizerFullTimeSize;
209 int32_t write_idx = base_idx * clustererNN.mNnClusterizerElementSize + row_idx * clustererNN.mNnClusterizerPadTimeSize + time_idx;
210
211 // Early boundary check for row
212 const int32_t target_row = row + row_idx - clustererNN.mNnClusterizerSizeInputRow;
213 float output_value = clustererNN.mNnClusterizerBoundaryFillValue;
214
215 if ((row < 63 && target_row > 62) || (target_row < 0) || (row > 62 && target_row < 63) || (target_row >= o2::tpc::constants::MAXGLOBALPADROW)) {
216 for (uint32_t target_pad = 0; target_pad < clustererNN.mNnClusterizerFullPadSize; ++target_pad) {
217 if (dtype == 0) {
218 clustererNN.mInputData_32[write_idx] = output_value;
219 } else {
220 clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)output_value;
221 }
222 write_idx += clustererNN.mNnClusterizerFullTimeSize;
223 }
224 return;
225 } else {
226 // Calculate offsets
227 const int32_t target_time = time + time_idx - clustererNN.mNnClusterizerSizeInputTime;
228 const uint8_t is_time_boundary = (target_time < 0) || (target_time >= clustererNN.maxAllowedTimebin);
229 const float inverse_central_charge = 1.f / central_charge; // multiply by inverse is cheaper than divide
230 const int32_t p_local = pad + GPUTPCNNClusterizerKernels::padOffset(row, target_row);
231 const int32_t npads = GPUTPCGeometry::NPads(target_row);
232
233 const int32_t start_pad = -clustererNN.mNnClusterizerSizeInputPad + p_local;
234 const int32_t end_pad = clustererNN.mNnClusterizerSizeInputPad + p_local;
235
236 for (int32_t target_pad = start_pad; target_pad <= end_pad; ++target_pad) {
237 if (target_pad >= npads || target_pad < 0 || is_time_boundary) {
238 output_value = clustererNN.mNnClusterizerBoundaryFillValue;
239 } else {
240 CfChargePos pos(target_row, target_pad, target_time);
241 // one load + one multiply
242 output_value = chargeMap[pos].unpack() * inverse_central_charge;
243 }
244 if (dtype == 0) {
245 clustererNN.mInputData_32[write_idx] = output_value;
246 } else {
247 clustererNN.mInputData_16[write_idx] = (OrtDataType::Float16_t)output_value;
248 }
249 write_idx += clustererNN.mNnClusterizerFullTimeSize;
250 }
251 return;
252 }
253 }
254}
255
256template <>
257GPUdii() void GPUTPCNNClusterizerKernels::Thread<GPUTPCNNClusterizerKernels::determineClass1Labels>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& processors, uint8_t sector, int8_t dtype, int8_t withMC, uint32_t batchStart)
258{
259 uint32_t glo_idx = get_global_id(0);
260 auto& clusterer = processors.tpcClusterer[sector];
261 auto& clustererNN = processors.tpcNNClusterer[sector];
262 if (glo_idx + batchStart >= clusterer.mPmemory->counters.nClusters || glo_idx >= (uint32_t)clustererNN.mNnClusterizerBatchedMode) {
263 return;
264 }
265 if (clustererNN.mNnClusterizerUseClassification) {
266 if (dtype == 0) {
267 clustererNN.mOutputDataClass[glo_idx + batchStart] = (int32_t)(clustererNN.mModelProbabilities_32[glo_idx] > clustererNN.mNnClassThreshold);
268 } else {
269 clustererNN.mOutputDataClass[glo_idx + batchStart] = (int32_t)((clustererNN.mModelProbabilities_16[glo_idx]).ToFloat() > clustererNN.mNnClassThreshold);
270 }
271 } else {
272 clustererNN.mOutputDataClass[glo_idx + batchStart] = 1;
273 }
274}
275
276template <>
277GPUdii() void GPUTPCNNClusterizerKernels::Thread<GPUTPCNNClusterizerKernels::determineClass2Labels>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& processors, uint8_t sector, int8_t dtype, int8_t withMC, uint32_t batchStart)
278{
279 uint32_t glo_idx = get_global_id(0);
280 auto& clusterer = processors.tpcClusterer[sector];
281 auto& clustererNN = processors.tpcNNClusterer[sector];
282 if (glo_idx + batchStart >= clusterer.mPmemory->counters.nClusters || glo_idx >= (uint32_t)clustererNN.mNnClusterizerBatchedMode) {
283 return;
284 }
285 if (clustererNN.mNnClusterizerUseClassification) {
286 uint32_t elem_iterator = glo_idx * clustererNN.mNnClusterizerModelClassNumOutputNodes;
287 float current_max_prob = 0.f; // If the neural network doesn't contain the softmax as a last layer, the outputs can range in [-infty, infty]
288 uint32_t class_label = 0;
289 for (uint32_t pIdx = elem_iterator; pIdx < elem_iterator + clustererNN.mNnClusterizerModelClassNumOutputNodes; pIdx++) {
290 if (pIdx == elem_iterator) {
291 if (dtype == 0) {
292 current_max_prob = clustererNN.mModelProbabilities_32[pIdx];
293 } else {
294 current_max_prob = static_cast<float>(clustererNN.mModelProbabilities_16[pIdx]);
295 }
296 } else {
297 if (dtype == 0) {
298 current_max_prob = CAMath::Max(current_max_prob, clustererNN.mModelProbabilities_32[pIdx]);
299 } else {
300 current_max_prob = CAMath::Max(current_max_prob, clustererNN.mModelProbabilities_16[pIdx].ToFloat());
301 }
302 }
303 }
304 // uint32_t class_label = std::distance(elem_iterator, std::max_element(elem_iterator, elem_iterator + clustererNN.mNnClusterizerModelClassNumOutputNodes)); // Multiple outputs of the class network are the probabilities for each class. The highest one "wins"
305 clustererNN.mOutputDataClass[glo_idx + batchStart] = class_label;
306 if (class_label > 1) {
307 clustererNN.mClusterFlags[2 * glo_idx] = 1;
308 clustererNN.mClusterFlags[2 * glo_idx + 1] = 1;
309 }
310 } else {
311 clustererNN.mOutputDataClass[glo_idx + batchStart] = 1;
312 }
313}
314
315template <>
316GPUdii() void GPUTPCNNClusterizerKernels::Thread<GPUTPCNNClusterizerKernels::publishClass1Regression>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& processors, uint8_t sector, int8_t dtype, int8_t withMC, uint32_t batchStart)
317{
318 uint32_t glo_idx = get_global_id(0);
319 auto& clusterer = processors.tpcClusterer[sector];
320 auto& clustererNN = processors.tpcNNClusterer[sector];
321 if (glo_idx >= (uint32_t)clustererNN.mNnClusterizerBatchedMode) {
322 return;
323 }
324
325 uint32_t maxClusterNum = clusterer.mPmemory->counters.nClusters;
326 uint32_t full_glo_idx = glo_idx + batchStart;
327 int32_t model_output_index = glo_idx * clustererNN.mNnClusterizerModelReg1NumOutputNodes;
328
329 CfArray2D<PackedCharge> chargeMap(reinterpret_cast<PackedCharge*>(clusterer.mPchargeMap));
330 uint32_t peakIndex = CAMath::Min(full_glo_idx, maxClusterNum - 1);
331 CfChargePos peak = clusterer.mPfilteredPeakPositions[peakIndex];
332 float central_charge = static_cast<float>(chargeMap[peak].unpack());
333
334 CPU_ONLY(MCLabelAccumulator labelAccElem(clusterer));
335 MCLabelAccumulator* labelAcc = CPU_PTR(&labelAccElem);
336
337 if (full_glo_idx >= maxClusterNum) {
338 if (withMC) {
339 ClusterAccumulator dummy_pc;
340 CPU_ONLY(labelAcc->collect(peak, central_charge));
341 GPUTPCCFClusterizer::buildCluster(
342 clusterer.Param().rec,
343 chargeMap,
344 peak,
345 smem.posBcast,
346 smem.buf,
347 smem.innerAboveThreshold,
348 &dummy_pc,
350 }
351 return;
352 }
353
354 tpc::ClusterNative* clusterOut = clusterer.mPclusterByRow;
355
357
358 if (withMC) {
359 ClusterAccumulator dummy_pc;
360 CPU_ONLY(labelAcc->collect(peak, central_charge));
361 GPUTPCCFClusterizer::buildCluster(
362 clusterer.Param().rec,
363 chargeMap,
364 peak,
365 smem.posBcast,
366 smem.buf,
367 smem.innerAboveThreshold,
368 &dummy_pc,
370 }
371 if ((clusterer.mPmemory->frag).isOverlap(peak.time())) {
372 if (clusterer.mPclusterPosInRow) {
373 clusterer.mPclusterPosInRow[full_glo_idx] = clusterer.mNMaxClusterPerRow;
374 }
375 return;
376 }
377
378 bool notSinglePad = false, notSingleTime = false;
379 for (uint16_t i = 0; i < 8; i++) {
380 Delta2 d = cfconsts::InnerNeighbors[i];
381 CfChargePos tmp_pos = peak.delta(d);
382 float v = static_cast<float>(chargeMap[tmp_pos].unpack());
383 notSinglePad |= (d.x != 0) && (v > 0.f);
384 notSingleTime |= (d.y != 0) && (v > 0.f);
385 }
386
387 float publishPadPosition = 0.f, publishTimePosition = 0.f;
388 if (dtype == 0) {
389 publishPadPosition = static_cast<float>(peak.pad()) + clustererNN.mOutputDataReg1_32[model_output_index];
390 publishTimePosition = static_cast<float>(peak.time()) + clustererNN.mOutputDataReg1_32[model_output_index + 1];
391 isBoundaryPublish(full_glo_idx, static_cast<int32_t>(peak.row()), publishPadPosition, publishTimePosition);
392 pc.setFull(central_charge * clustererNN.mOutputDataReg1_32[model_output_index + 4],
393 publishPadPosition,
394 notSinglePad ? clustererNN.mOutputDataReg1_32[model_output_index + 2] : 0.f,
395 (clusterer.mPmemory->frag).start + publishTimePosition,
396 notSingleTime ? clustererNN.mOutputDataReg1_32[model_output_index + 3] : 0.f,
397 clustererNN.mClusterFlags[2 * glo_idx],
398 clustererNN.mClusterFlags[2 * glo_idx + 1]);
399 } else {
400 publishPadPosition = static_cast<float>(peak.pad()) + clustererNN.mOutputDataReg1_16[model_output_index].ToFloat();
401 publishTimePosition = static_cast<float>(peak.time()) + clustererNN.mOutputDataReg1_16[model_output_index + 1].ToFloat();
402 isBoundaryPublish(full_glo_idx, static_cast<int32_t>(peak.row()), publishPadPosition, publishTimePosition);
403 pc.setFull(central_charge * clustererNN.mOutputDataReg1_16[model_output_index + 4].ToFloat(),
404 publishPadPosition,
405 notSinglePad ? clustererNN.mOutputDataReg1_16[model_output_index + 2].ToFloat() : 0.f,
406 (clusterer.mPmemory->frag).start + publishTimePosition,
407 notSingleTime ? clustererNN.mOutputDataReg1_16[model_output_index + 3].ToFloat() : 0.f,
408 clustererNN.mClusterFlags[2 * glo_idx],
409 clustererNN.mClusterFlags[2 * glo_idx + 1]);
410 }
411
412 // if (boundaryFlag != 0) { // Prints the entire NN input for the given index
413 // // Build a simple buffer manually (float with 3 decimals)
414 // const int MAX_CHARS = 4096;
415 // char buffer[MAX_CHARS];
416 // int pos = 0;
417 //
418 // auto appendChar = [&](char c) {
419 // if (pos < MAX_CHARS - 1) buffer[pos++] = c;
420 // };
421 // auto appendStr = [&](const char* s) {
422 // while (*s && pos < MAX_CHARS - 1) buffer[pos++] = *s++;
423 // };
424 // auto appendUInt = [&](uint32_t v) {
425 // char tmp[16]; int tp = 0;
426 // if (v == 0) { appendChar('0'); return; }
427 // while (v && tp < 16) { tmp[tp++] = char('0' + (v % 10)); v /= 10; }
428 // while (tp--) appendChar(tmp[tp]);
429 // };
430 // auto appendInt = [&](int v) {
431 // if (v < 0) { appendChar('-'); v = -v; }
432 // appendUInt((uint32_t)v);
433 // };
434 // auto appendFloat = [&](float f) {
435 // if (f < 0) { appendChar('-'); f = -f; }
436 // int ip = (int)f;
437 // float frac = f - (float)ip;
438 // appendInt(ip);
439 // appendChar('.');
440 // for (int i = 0; i < 3; i++) {
441 // frac *= 10.f;
442 // int d = (int)frac;
443 // appendChar((char)('0' + (d < 0 ? 0 : (d > 9 ? 9 : d))));
444 // frac -= d;
445 // if (frac < 0) frac = 0;
446 // }
447 // };
448 //
449 // appendStr("(NN CLUS) DEBUG: Boundary cluster detected (sector ");
450 // appendUInt(sector);
451 // appendStr(", row ");
452 // appendUInt(peak.row());
453 // appendStr(", pad ");
454 // appendFloat(publishPadPosition);
455 // appendStr(", time ");
456 // appendFloat(publishTimePosition);
457 // appendStr(") [glo_idx=");
458 // appendUInt(glo_idx);
459 // appendStr(" elemSize=");
460 // appendInt(clustererNN.mNnClusterizerElementSize);
461 // appendStr(" dtype=");
462 // appendInt(dtype);
463 // appendStr("] INPUT:");
464 //
465 // int elemSize = clustererNN.mNnClusterizerElementSize;
466 // int baseIdx = glo_idx * elemSize;
467 //
468 // int maxPrint = elemSize;
469 // for (int i = 0; i < maxPrint; ++i) {
470 // appendChar(' ');
471 // float v = (dtype == 0) ? clustererNN.mInputData_16[baseIdx + i].ToFloat()
472 // : clustererNN.mInputData_32[baseIdx + i];
473 // appendFloat(v);
474 // if (pos > (MAX_CHARS - 32)) { appendStr(" ..."); break; }
475 // }
476 //
477 // buffer[pos] = 0;
478 // printf("%s\n", buffer);
479 // }
480
481 tpc::ClusterNative myCluster;
482 bool rejectCluster = !pc.toNative(peak, central_charge, myCluster, clusterer.Param(), chargeMap);
483 if (clustererNN.mNnClusterizerUseClassification) {
484 rejectCluster |= (clustererNN.mOutputDataClass[peakIndex] <= 0);
485 }
486 if (rejectCluster) {
487 if (clusterer.mPclusterPosInRow) {
488 clusterer.mPclusterPosInRow[full_glo_idx] = clusterer.mNMaxClusterPerRow;
489 }
490 return;
491 }
492
493 uint32_t rowIndex = 0;
494 if (clusterOut != nullptr) {
495 rowIndex = GPUTPCCFClusterizer::sortIntoBuckets(
496 clusterer,
497 myCluster,
498 peak.row(),
499 clusterer.mNMaxClusterPerRow,
500 clusterer.mPclusterInRow,
501 clusterOut);
502 if (clusterer.mPclusterPosInRow != nullptr) {
503 clusterer.mPclusterPosInRow[full_glo_idx] = rowIndex;
504 }
505 } else if (clusterer.mPclusterPosInRow) {
506 rowIndex = clusterer.mPclusterPosInRow[full_glo_idx];
507 }
508 CPU_ONLY(labelAcc->commit(peak.row(), rowIndex, clusterer.mNMaxClusterPerRow));
509}
510
511template <>
512GPUdii() void GPUTPCNNClusterizerKernels::Thread<GPUTPCNNClusterizerKernels::publishClass2Regression>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& processors, uint8_t sector, int8_t dtype, int8_t withMC, uint32_t batchStart)
513{
514 uint32_t glo_idx = get_global_id(0);
515 auto& clusterer = processors.tpcClusterer[sector];
516 auto& clustererNN = processors.tpcNNClusterer[sector];
517 if (glo_idx >= (uint32_t)clustererNN.mNnClusterizerBatchedMode) {
518 return;
519 }
520
521 uint32_t maxClusterNum = clusterer.mPmemory->counters.nClusters;
522 CfArray2D<PackedCharge> chargeMap(reinterpret_cast<PackedCharge*>(clusterer.mPchargeMap));
523 CfChargePos peak = clusterer.mPfilteredPeakPositions[CAMath::Min(glo_idx + batchStart, (uint32_t)(clusterer.mPmemory->counters.nClusters - 1))];
524 float central_charge = static_cast<float>(chargeMap[peak].unpack());
525
526 CPU_ONLY(MCLabelAccumulator labelAccElem(clusterer));
527 MCLabelAccumulator* labelAcc = CPU_PTR(&labelAccElem);
528 tpc::ClusterNative* clusterOut = clusterer.mPclusterByRow;
529 uint32_t full_glo_idx = glo_idx + batchStart;
530
531 if (full_glo_idx >= maxClusterNum) {
532 if (withMC) {
533 ClusterAccumulator dummy_pc;
534 CPU_ONLY(labelAcc->collect(peak, central_charge));
535 GPUTPCCFClusterizer::buildCluster(
536 clusterer.Param().rec,
537 chargeMap,
538 peak,
539 smem.posBcast,
540 smem.buf,
541 smem.innerAboveThreshold,
542 &dummy_pc,
544 }
545 return;
546 }
547
548 uint32_t model_output_index = glo_idx * clustererNN.mNnClusterizerModelReg2NumOutputNodes;
549
551
552 if (withMC) {
553 ClusterAccumulator dummy_pc;
554 CPU_ONLY(labelAcc->collect(peak, central_charge));
555 GPUTPCCFClusterizer::buildCluster(
556 clusterer.Param().rec,
557 chargeMap,
558 peak,
559 smem.posBcast,
560 smem.buf,
561 smem.innerAboveThreshold,
562 &dummy_pc,
564 }
565 if ((clusterer.mPmemory->frag).isOverlap(peak.time())) {
566 if (clusterer.mPclusterPosInRow) {
567 clusterer.mPclusterPosInRow[full_glo_idx] = clusterer.mNMaxClusterPerRow;
568 }
569 return;
570 }
571
572 // Cluster 1
573 float publishPadPosition = 0.f, publishTimePosition = 0.f;
574 if (dtype == 0) {
575 publishPadPosition = static_cast<float>(peak.pad()) + clustererNN.mOutputDataReg2_32[model_output_index];
576 publishTimePosition = static_cast<float>(peak.time()) + clustererNN.mOutputDataReg2_32[model_output_index + 1];
577 isBoundaryPublish(full_glo_idx, static_cast<int32_t>(peak.row()), publishPadPosition, publishTimePosition);
578 pc.setFull(central_charge * clustererNN.mOutputDataReg2_32[model_output_index + 8],
579 publishPadPosition,
580 clustererNN.mOutputDataReg2_32[model_output_index + 4],
581 (clusterer.mPmemory->frag).start + publishTimePosition,
582 clustererNN.mOutputDataReg2_32[model_output_index + 6],
583 clustererNN.mClusterFlags[2 * glo_idx],
584 clustererNN.mClusterFlags[2 * glo_idx + 1]);
585 } else {
586 publishPadPosition = static_cast<float>(peak.pad()) + clustererNN.mOutputDataReg2_16[model_output_index].ToFloat();
587 publishTimePosition = static_cast<float>(peak.time()) + clustererNN.mOutputDataReg2_16[model_output_index + 1].ToFloat();
588 isBoundaryPublish(full_glo_idx, static_cast<int32_t>(peak.row()), publishPadPosition, publishTimePosition);
589 pc.setFull(central_charge * clustererNN.mOutputDataReg2_16[model_output_index + 8].ToFloat(),
590 publishPadPosition,
591 clustererNN.mOutputDataReg2_16[model_output_index + 4].ToFloat(),
592 (clusterer.mPmemory->frag).start + publishTimePosition,
593 clustererNN.mOutputDataReg2_16[model_output_index + 6].ToFloat(),
594 clustererNN.mClusterFlags[2 * glo_idx],
595 clustererNN.mClusterFlags[2 * glo_idx + 1]);
596 }
597
598 tpc::ClusterNative myCluster;
599 bool rejectCluster = !pc.toNative(peak, central_charge, myCluster, clusterer.Param(), chargeMap);
600 if (clustererNN.mNnClusterizerUseClassification) {
601 rejectCluster |= (clustererNN.mOutputDataClass[CAMath::Min(full_glo_idx, (uint32_t)clusterer.mPmemory->counters.nClusters - 1)] <= 0);
602 }
603 if (rejectCluster) {
604 if (clusterer.mPclusterPosInRow) {
605 clusterer.mPclusterPosInRow[full_glo_idx] = clusterer.mNMaxClusterPerRow;
606 }
607 return;
608 }
609
610 uint32_t rowIndex = 0;
611 if (clusterOut != nullptr) {
612 rowIndex = GPUTPCCFClusterizer::sortIntoBuckets(
613 clusterer,
614 myCluster,
615 peak.row(),
616 clusterer.mNMaxClusterPerRow,
617 clusterer.mPclusterInRow,
618 clusterOut);
619 if (clusterer.mPclusterPosInRow != nullptr) {
620 clusterer.mPclusterPosInRow[full_glo_idx] = rowIndex;
621 }
622 } else if (clusterer.mPclusterPosInRow) {
623 rowIndex = clusterer.mPclusterPosInRow[full_glo_idx];
624 }
625 CPU_ONLY(labelAcc->commit(peak.row(), rowIndex, clusterer.mNMaxClusterPerRow));
626
627 // Cluster 2
628 if (dtype == 0) {
629 publishPadPosition = static_cast<float>(peak.pad()) + clustererNN.mOutputDataReg2_32[model_output_index + 1];
630 publishTimePosition = static_cast<float>(peak.time()) + clustererNN.mOutputDataReg2_32[model_output_index + 3];
631 isBoundaryPublish(full_glo_idx, static_cast<int32_t>(peak.row()), publishPadPosition, publishTimePosition);
632 pc.setFull(central_charge * clustererNN.mOutputDataReg2_32[model_output_index + 9],
633 publishPadPosition,
634 clustererNN.mOutputDataReg2_32[model_output_index + 5],
635 (clusterer.mPmemory->frag).start + publishTimePosition,
636 clustererNN.mOutputDataReg2_32[model_output_index + 7],
637 clustererNN.mClusterFlags[2 * glo_idx],
638 clustererNN.mClusterFlags[2 * glo_idx + 1]);
639 } else {
640 publishPadPosition = static_cast<float>(peak.pad()) + clustererNN.mOutputDataReg2_16[model_output_index + 1].ToFloat();
641 publishTimePosition = static_cast<float>(peak.time()) + clustererNN.mOutputDataReg2_16[model_output_index + 3].ToFloat();
642 isBoundaryPublish(full_glo_idx, static_cast<int32_t>(peak.row()), publishPadPosition, publishTimePosition);
643 pc.setFull(central_charge * clustererNN.mOutputDataReg2_16[model_output_index + 9].ToFloat(),
644 publishPadPosition,
645 clustererNN.mOutputDataReg2_16[model_output_index + 5].ToFloat(),
646 (clusterer.mPmemory->frag).start + publishTimePosition,
647 clustererNN.mOutputDataReg2_16[model_output_index + 7].ToFloat(),
648 clustererNN.mClusterFlags[2 * glo_idx],
649 clustererNN.mClusterFlags[2 * glo_idx + 1]);
650 }
651
652 rejectCluster = !pc.toNative(peak, central_charge, myCluster, clusterer.Param(), chargeMap);
653 if (clustererNN.mNnClusterizerUseClassification) {
654 rejectCluster |= (clustererNN.mOutputDataClass[CAMath::Min(full_glo_idx, (uint32_t)clusterer.mPmemory->counters.nClusters - 1)] <= 0);
655 }
656 if (rejectCluster) {
657 if (clusterer.mPclusterPosInRow) {
658 clusterer.mPclusterPosInRow[full_glo_idx] = clusterer.mNMaxClusterPerRow;
659 }
660 return;
661 }
662
663 if (clusterOut != nullptr) {
664 rowIndex = GPUTPCCFClusterizer::sortIntoBuckets(
665 clusterer,
666 myCluster,
667 peak.row(),
668 clusterer.mNMaxClusterPerRow,
669 clusterer.mPclusterInRow,
670 clusterOut);
671 if (clusterer.mPclusterPosInRow != nullptr) {
672 clusterer.mPclusterPosInRow[full_glo_idx] = rowIndex;
673 }
674 } else if (clusterer.mPclusterPosInRow) {
675 rowIndex = clusterer.mPclusterPosInRow[full_glo_idx];
676 }
677 // CPU_ONLY(labelAcc->commit(peak.row(), rowIndex, clusterer.mNMaxClusterPerRow)); // -> Is this needed? How to handle MC labels for split clusters?
678}
679
680// ---------------------------------
681template <>
682GPUdii() void GPUTPCNNClusterizerKernels::Thread<GPUTPCNNClusterizerKernels::publishDeconvolutionFlags>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& processors, uint8_t sector, int8_t dtype, int8_t withMC, uint batchStart)
683{
684 // Implements identical publishing logic as the heuristic clusterizer and deconvolution kernel
685 uint32_t glo_idx = get_global_id(0);
686 auto& clusterer = processors.tpcClusterer[sector];
687 auto& clustererNN = processors.tpcNNClusterer[sector];
688 if (glo_idx + batchStart >= clusterer.mPmemory->counters.nClusters || glo_idx >= (uint32_t)clustererNN.mNnClusterizerBatchedMode) {
689 return;
690 }
691 CfArray2D<PackedCharge> chargeMap(reinterpret_cast<PackedCharge*>(clusterer.mPchargeMap));
692 CfChargePos peak = clusterer.mPfilteredPeakPositions[glo_idx + batchStart];
693
694 clustererNN.mClusterFlags[2 * glo_idx] = 0;
695 clustererNN.mClusterFlags[2 * glo_idx + 1] = 0;
696 for (int i = 0; i < 8; i++) {
697 Delta2 d = cfconsts::InnerNeighbors[i];
698 CfChargePos tmp_pos = peak.delta(d);
699 PackedCharge charge = chargeMap[tmp_pos];
700 clustererNN.mClusterFlags[2 * glo_idx] += (d.y != 0 && charge.isSplit());
701 clustererNN.mClusterFlags[2 * glo_idx + 1] += (d.x != 0 && charge.isSplit());
702 }
703 for (int i = 0; i < 16; i++) {
704 Delta2 d = cfconsts::OuterNeighbors[i];
705 CfChargePos tmp_pos = peak.delta(d);
706 PackedCharge charge = chargeMap[tmp_pos];
707 clustererNN.mClusterFlags[2 * glo_idx] += (d.y != 0 && charge.isSplit() && !charge.has3x3Peak());
708 clustererNN.mClusterFlags[2 * glo_idx + 1] += (d.x != 0 && charge.isSplit() && !charge.has3x3Peak());
709 }
710}
711
712// THe following arithmetic is done because the network is trained with a split between IROC and OROC boundary
713GPUd() int32_t GPUTPCNNClusterizerKernels::padOffset(int32_t row_ref, int32_t row_current)
714{
715 if (row_current < 0 || row_current >= o2::tpc::constants::MAXGLOBALPADROW) {
716 return 0; // Short-circuit for out-of-bound rows
717 } else {
718 return (int)((GPUTPCGeometry::NPads(row_current) - GPUTPCGeometry::NPads(row_ref)) / 2);
719 }
720}
721
722GPUd() int32_t GPUTPCNNClusterizerKernels::rowOffset(int32_t row, int32_t offset)
723{
724 return (row > 62 ? offset : 0);
725}
726
727GPUd() bool GPUTPCNNClusterizerKernels::isBoundary(int32_t row, int32_t pad, int32_t maxrow, int32_t iroc_row, int32_t npads_row, int32_t npads_reference)
728{
729 if (pad < 0) { // Faster short-circuit
730 return true;
731 } else if (row < 63) {
732 return (pad >= npads_row);
733 } else if (row < iroc_row) { // to account for the gap between IROC and OROC. Charge will be set to the boundary fill value in order to signal boundaries to the neural network
734 return true;
735 } else if (row < maxrow) {
736 return (pad >= npads_reference);
737 } else {
738 return true;
739 }
740}
741
742GPUd() bool GPUTPCNNClusterizerKernels::isBoundaryPublish(int32_t idx, int32_t row, float& pad, float& time)
743{
744 if (pad < 0) {
745 // printf("(NN CLUS) WARNING: Boundary detected, idx = %d, pad < 0: row %d, pad %f (%d, %d), time %f (%d, %d)\n", idx, row, pad, 0, static_cast<int>(GPUTPCGeometry::NPads(row)), time, 0, TPC_MAX_FRAGMENT_LEN_GPU);
746 pad = 0.f;
747 return true;
748 } else if (pad >= static_cast<int>(GPUTPCGeometry::NPads(row))) {
749 // printf("(NN CLUS) WARNING: Boundary detected, idx = %d, pad >= static_cast<int>(GPUTPCGeometry::NPads(row): row %d, pad %f (%d, %d), time %f (%d, %d)\n", idx, row, pad, 0, static_cast<int>(GPUTPCGeometry::NPads(row)), time, 0, TPC_MAX_FRAGMENT_LEN_GPU);
750 pad = static_cast<float>(GPUTPCGeometry::NPads(row) - 1);
751 return true;
752 } else if (time < 0) {
753 // printf("(NN CLUS) WARNING: Boundary detected, idx = %d, time < 0: row %d, pad %f (%d, %d), time %f (%d, %d)\n", idx, row, pad, 0, static_cast<int>(GPUTPCGeometry::NPads(row)), time, 0, TPC_MAX_FRAGMENT_LEN_GPU);
754 time = 0.f;
755 return true;
756 } else if (time >= TPC_MAX_FRAGMENT_LEN_GPU) {
757 // printf("(NN CLUS) WARNING: Boundary detected, idx = %d, time >= TPC_MAX_FRAGMENT_LEN_GPU: row %d, pad %f (%d, %d), time %f (%d, %d)\n", idx, row, pad, 0, static_cast<int>(GPUTPCGeometry::NPads(row)), time, 0, TPC_MAX_FRAGMENT_LEN_GPU);
758 time = static_cast<float>(TPC_MAX_FRAGMENT_LEN_GPU - 1);
759 return true;
760 } else {
761 return false;
762 }
763}
int16_t charge
Definition RawEventData.h:5
int16_t time
Definition RawEventData.h:4
int32_t i
#define get_local_size(dim)
#define get_local_id(dim)
#define get_num_groups(dim)
#define GPUCA_THREAD_INFO_PROVIDE
#define get_global_id(dim)
#define get_group_id(dim)
GPUdii() void GPUTPCNNClusterizerKernels
GPUd() int32_t GPUTPCNNClusterizerKernels
uint16_t pos
Definition RawData.h:3
void collect(const CfChargePos &pos, float q)
void commit(tpccf::Row row, uint32_t indexInRow, uint32_t maxElemsPerBucket)
#define TPC_MAX_FRAGMENT_LEN_GPU
#define CPU_ONLY(x)
#define CPU_PTR(x)
const GLdouble * v
Definition glcorearb.h:832
GLintptr offset
Definition glcorearb.h:660
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLuint GLfloat * val
Definition glcorearb.h:1582
GLuint start
Definition glcorearb.h:469
int16_t y
int16_t x
std::vector< int > row