Project
Loading...
Searching...
No Matches
GPUReconstructionConvert.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#ifndef GPUCA_STANDALONE
17#include "TPCBase/Sector.h"
19#include "TPCBase/Mapper.h"
20#endif
21
23#include "TPCFastTransformPOD.h"
24#include "GPUTPCClusterData.h"
25#include "GPUO2DataTypes.h"
26#include "GPUDataTypesIO.h"
27#include "GPUTPCGeometry.h"
28#include "AliHLTTPCRawCluster.h" // VS: It can not be removed. Used in line 93.
29#include "GPUParam.h"
30#include "GPULogging.h"
31#include <algorithm>
32#include <vector>
33
34#include "clusterFinderDefs.h"
40#include "TPCBase/RDHUtils.h"
41#include "TPCBase/CRU.h"
43
44#include <oneapi/tbb.h>
45
46using namespace o2::gpu;
47using namespace o2::tpc;
48using namespace o2::tpc::constants;
49using namespace std::string_literals;
50
51void GPUReconstructionConvert::ConvertRun2RawToNative(o2::tpc::ClusterNativeAccess& native, std::unique_ptr<ClusterNative[]>& nativeBuffer, const AliHLTTPCRawCluster** rawClusters, uint32_t* nRawClusters)
52{
53 memset((void*)&native, 0, sizeof(native));
54 for (uint32_t i = 0; i < NSECTORS; i++) {
55 for (uint32_t j = 0; j < nRawClusters[i]; j++) {
56 native.nClusters[i][rawClusters[i][j].GetPadRow()]++;
57 }
58 native.nClustersTotal += nRawClusters[i];
59 }
60 nativeBuffer.reset(new ClusterNative[native.nClustersTotal]);
61 native.clustersLinear = nativeBuffer.get();
62 native.setOffsetPtrs();
63 for (uint32_t i = 0; i < NSECTORS; i++) {
64 for (uint32_t j = 0; j < GPUTPCGeometry::NROWS; j++) {
65 native.nClusters[i][j] = 0;
66 }
67 for (uint32_t j = 0; j < nRawClusters[i]; j++) {
68 const AliHLTTPCRawCluster& org = rawClusters[i][j];
69 int32_t row = org.GetPadRow();
70 ClusterNative& c = nativeBuffer[native.clusterOffset[i][row] + native.nClusters[i][row]++];
71 c.setTimeFlags(org.GetTime(), org.GetFlags());
72 c.setPad(org.GetPad());
73 c.setSigmaTime(CAMath::Sqrt(org.GetSigmaTime2()));
74 c.setSigmaPad(CAMath::Sqrt(org.GetSigmaPad2()));
75 c.qMax = org.GetQMax();
76 c.qTotPacked = org.GetCharge();
77 }
78 }
79}
80
82{
83 float retVal = 0;
84 for (uint32_t i = 0; i < NSECTORS; i++) {
85 for (uint32_t j = 0; j < GPUTPCGeometry::NROWS; j++) {
86 for (uint32_t k = 0; k < native.nClusters[i][j]; k++) {
87 if (native.clusters[i][j][k].getTime() > retVal) {
88 retVal = native.clusters[i][j][k].getTime();
89 }
90 }
91 }
92 }
93 return ceil(retVal);
94}
95
97{
98 float retVal = 0;
99 for (uint32_t i = 0; i < NSECTORS; i++) {
100 for (uint32_t k = 0; k < digits.nTPCDigits[i]; k++) {
101 if (digits.tpcDigits[i][k].getTimeStamp() > retVal) {
102 retVal = digits.tpcDigits[i][k].getTimeStamp();
103 }
104 }
105 }
106 return ceil(retVal);
107}
108
110{
111 float retVal = 0;
112 for (uint32_t i = 0; i < NSECTORS; i++) {
113 int32_t firstHBF = zspages.sector[i].count[0] ? o2::raw::RDHUtils::getHeartBeatOrbit(*(const o2::header::RAWDataHeader*)zspages.sector[i].zsPtr[0][0]) : 0;
114 for (uint32_t j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) {
115 for (uint32_t k = 0; k < zspages.sector[i].count[j]; k++) {
116 const char* page = (const char*)zspages.sector[i].zsPtr[j][k];
117 for (uint32_t l = 0; l < zspages.sector[i].nZSPtr[j][k]; l++) {
118 o2::header::RAWDataHeader* rdh = (o2::header::RAWDataHeader*)(page + l * TPCZSHDR::TPC_ZS_PAGE_SIZE);
119 TPCZSHDR* hdr = (TPCZSHDR*)(page + l * TPCZSHDR::TPC_ZS_PAGE_SIZE + sizeof(o2::header::RAWDataHeader));
120 int32_t nTimeBinSpan = hdr->nTimeBinSpan;
122 TPCZSHDRV2* hdr2 = (TPCZSHDRV2*)hdr;
123 if (hdr2->flags & TPCZSHDRV2::ZSFlags::nTimeBinSpanBit8) {
124 nTimeBinSpan += 256;
125 }
126 }
127 uint32_t timeBin = (hdr->timeOffset + (o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstHBF) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN + nTimeBinSpan;
128 if (timeBin > retVal) {
129 retVal = timeBin;
130 }
131 }
132 }
133 }
134 }
135 return ceil(retVal);
136}
137
138// ------------------------------------------------- TPC ZS -------------------------------------------------
139
140#ifndef GPUCA_RUN2
141namespace o2::gpu
142{
143namespace // anonymous
144{
145
146// ------------------------------------------------- TPC ZS General -------------------------------------------------
147
148typedef std::array<int64_t, TPCZSHDR::TPC_ZS_PAGE_SIZE / sizeof(int64_t)> zsPage;
149
150struct zsEncoder {
156 const o2::InteractionRecord* ir = nullptr;
157 const GPUParam* param = nullptr;
158 bool padding = false;
159 int32_t lastEndpoint = -2, lastTime = -1, lastRow = GPUTPCGeometry::NROWS;
161 int64_t hbf = -1, nexthbf = 0;
162 zsPage* page = nullptr;
163 uint8_t* pagePtr = nullptr;
167 bool needAnotherPage = false;
170 void ZSfillEmpty(void* ptr, int32_t shift, uint32_t feeId, int32_t orbit, int32_t linkid);
171 static void ZSstreamOut(uint16_t* bufIn, uint32_t& lenIn, uint8_t* bufOut, uint32_t& lenOut, uint32_t nBits);
172 int64_t getHbf(int64_t timestamp) { return (timestamp * LHCBCPERTIMEBIN + bcShiftInFirstHBF) / o2::constants::lhc::LHCMaxBunches; }
173};
174
175inline void zsEncoder::ZSfillEmpty(void* ptr, int32_t shift, uint32_t feeId, int32_t orbit, int32_t linkid)
176{
179 o2::raw::RDHUtils::setHeartBeatBC(*rdh, shift);
181 o2::raw::RDHUtils::setVersion(*rdh, o2::raw::RDHUtils::getVersion<o2::header::RAWDataHeader>());
182 o2::raw::RDHUtils::setFEEID(*rdh, feeId);
184 o2::raw::RDHUtils::setLinkID(*rdh, linkid);
187}
188
189inline void zsEncoder::ZSstreamOut(uint16_t* bufIn, uint32_t& lenIn, uint8_t* bufOut, uint32_t& lenOut, uint32_t nBits)
190{
191 uint32_t byte = 0, bits = 0;
192 uint32_t mask = (1 << nBits) - 1;
193 for (uint32_t i = 0; i < lenIn; i++) {
194 byte |= (bufIn[i] & mask) << bits;
195 bits += nBits;
196 while (bits >= 8) {
197 bufOut[lenOut++] = (uint8_t)(byte & 0xFF);
198 byte = byte >> 8;
199 bits -= 8;
200 }
201 }
202 if (bits) {
203 bufOut[lenOut++] = byte;
204 }
205 lenIn = 0;
206}
207
208static inline auto ZSEncoderGetDigits(const GPUTrackingInOutDigits& in, int32_t i) { return in.tpcDigits[i]; }
209static inline auto ZSEncoderGetNDigits(const GPUTrackingInOutDigits& in, int32_t i) { return in.nTPCDigits[i]; }
210#ifndef GPUCA_STANDALONE
211using DigitArray = std::array<gsl::span<const o2::tpc::Digit>, o2::tpc::Sector::MAXSECTOR>;
212static inline auto ZSEncoderGetDigits(const DigitArray& in, int32_t i) { return in[i].data(); }
213static inline auto ZSEncoderGetNDigits(const DigitArray& in, int32_t i) { return in[i].size(); }
214#endif
215
216// ------------------------------------------------- TPC ZS Original Row-based ZS -------------------------------------------------
217
218struct zsEncoderRow : public zsEncoder {
219 std::array<uint16_t, TPCZSHDR::TPC_ZS_PAGE_SIZE> streamBuffer = {};
220 std::array<uint8_t, TPCZSHDR::TPC_ZS_PAGE_SIZE> streamBuffer8 = {};
221 TPCZSHDR* hdr = nullptr;
223 uint8_t* nSeq = nullptr;
228 constexpr static int32_t RAWLNK = rdh_utils::UserLogicLinkID;
229
230 bool checkInput(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k);
231 bool writeSubPage();
232 void init() { encodeBits = zsVersion == 2 ? TPCZSHDR::TPC_ZS_NBITS_V2 : TPCZSHDR::TPC_ZS_NBITS_V1; }
233 void initPage() {}
234 uint32_t encodeSequence(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k);
235
236 bool sort(const o2::tpc::Digit a, const o2::tpc::Digit b);
237 void decodePage(std::vector<o2::tpc::Digit>& outputBuffer, const zsPage* page, uint32_t endpoint, uint32_t firstOrbit, uint32_t triggerBC = 0);
238};
239
240inline bool zsEncoderRow::sort(const o2::tpc::Digit a, const o2::tpc::Digit b)
241{
242 int32_t endpointa = GPUTPCGeometry::GetRegion(a.getRow());
243 int32_t endpointb = GPUTPCGeometry::GetRegion(b.getRow());
244 endpointa = 2 * endpointa + (a.getRow() >= GPUTPCGeometry::GetRegionStart(endpointa) + GPUTPCGeometry::GetRegionRows(endpointa) / 2);
245 endpointb = 2 * endpointb + (b.getRow() >= GPUTPCGeometry::GetRegionStart(endpointb) + GPUTPCGeometry::GetRegionRows(endpointb) / 2);
246 if (endpointa != endpointb) {
247 return endpointa <= endpointb;
248 }
249 if (a.getTimeStamp() != b.getTimeStamp()) {
250 return a.getTimeStamp() < b.getTimeStamp();
251 }
252 if (a.getRow() != b.getRow()) {
253 return a.getRow() < b.getRow();
254 }
255 return a.getPad() < b.getPad();
256}
257
258bool zsEncoderRow::checkInput(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k)
259{
260 seqLen = 1;
261 if (lastRow != tmpBuffer[k].getRow()) {
262 endpointStart = GPUTPCGeometry::GetRegionStart(curRegion);
263 endpoint = curRegion * 2;
264 if (tmpBuffer[k].getRow() >= endpointStart + GPUTPCGeometry::GetRegionRows(curRegion) / 2) {
265 endpoint++;
266 endpointStart += GPUTPCGeometry::GetRegionRows(curRegion) / 2;
267 }
268 }
269 for (uint32_t l = k + 1; l < tmpBuffer.size(); l++) {
270 if (tmpBuffer[l].getRow() == tmpBuffer[k].getRow() && tmpBuffer[l].getTimeStamp() == tmpBuffer[k].getTimeStamp() && tmpBuffer[l].getPad() == tmpBuffer[l - 1].getPad() + 1) {
271 seqLen++;
272 } else {
273 break;
274 }
275 }
276 if (lastEndpoint >= 0 && lastTime != -1 && (int32_t)hdr->nTimeBinSpan + tmpBuffer[k].getTimeStamp() - lastTime >= 256) {
277 lastEndpoint = -1;
278 }
279 if (endpoint == lastEndpoint) {
280 uint32_t sizeChk = (uint32_t)(pagePtr - reinterpret_cast<uint8_t*>(page)); // already written
281 sizeChk += 2 * (nRowsInTB + (tmpBuffer[k].getRow() != lastRow && tmpBuffer[k].getTimeStamp() == lastTime)); // TB HDR
282 sizeChk += streamSize8; // in stream buffer
283 sizeChk += (lastTime != tmpBuffer[k].getTimeStamp()) && ((sizeChk + (streamSize * encodeBits + 7) / 8) & 1); // time bin alignment
284 sizeChk += (tmpBuffer[k].getTimeStamp() != lastTime || tmpBuffer[k].getRow() != lastRow) ? 3 : 0; // new row overhead
285 sizeChk += (lastTime != -1 && tmpBuffer[k].getTimeStamp() > lastTime) ? ((tmpBuffer[k].getTimeStamp() - lastTime - 1) * 2) : 0; // empty time bins
286 sizeChk += 2; // sequence metadata
287 const uint32_t streamSizeChkBits = streamSize * encodeBits + ((lastTime != tmpBuffer[k].getTimeStamp() && (streamSize * encodeBits) % 8) ? (8 - (streamSize * encodeBits) % 8) : 0);
288 if (sizeChk + (encodeBits + streamSizeChkBits + 7) / 8 > TPCZSHDR::TPC_ZS_PAGE_SIZE) {
289 lastEndpoint = -1;
290 } else if (sizeChk + (seqLen * encodeBits + streamSizeChkBits + 7) / 8 > TPCZSHDR::TPC_ZS_PAGE_SIZE) {
291 seqLen = ((TPCZSHDR::TPC_ZS_PAGE_SIZE - sizeChk) * 8 - streamSizeChkBits) / encodeBits;
292 }
293 // sizeChk += (seqLen * encodeBits + streamSizeChkBits + 7) / 8;
294 // printf("Endpoint %d (%d), Pos %d, Chk %d, Len %d, rows %d, StreamSize %d %d, time %d (%d), row %d (%d), pad %d\n", endpoint, lastEndpoint, (int32_t) (pagePtr - reinterpret_cast<uint8_t*>(page)), sizeChk, seqLen, nRowsInTB, streamSize8, streamSize, (int32_t)tmpBuffer[k].getTimeStamp(), lastTime, (int32_t)tmpBuffer[k].getRow(), lastRow, tmpBuffer[k].getPad());
295 }
296 return endpoint != lastEndpoint || tmpBuffer[k].getTimeStamp() != lastTime;
297}
298
299bool zsEncoderRow::writeSubPage()
300{
301 if (pagePtr != reinterpret_cast<uint8_t*>(page)) {
302 pagePtr += 2 * nRowsInTB;
303 ZSstreamOut(streamBuffer.data(), streamSize, streamBuffer8.data(), streamSize8, encodeBits);
304 pagePtr = std::copy(streamBuffer8.data(), streamBuffer8.data() + streamSize8, pagePtr);
305 if (pagePtr - reinterpret_cast<uint8_t*>(page) > 8192) {
306 throw std::runtime_error("internal error during ZS encoding");
307 }
308 streamSize8 = 0;
309 for (int32_t l = 1; l < nRowsInTB; l++) {
310 curTBHdr->rowAddr1()[l - 1] += 2 * nRowsInTB;
311 }
312 }
313 return endpoint != lastEndpoint;
314}
315
316uint32_t zsEncoderRow::encodeSequence(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k)
317{
318 if (tmpBuffer[k].getTimeStamp() != lastTime) {
319 if (lastTime != -1) {
320 hdr->nTimeBinSpan += tmpBuffer[k].getTimeStamp() - lastTime - 1;
321 pagePtr += (tmpBuffer[k].getTimeStamp() - lastTime - 1) * 2;
322 }
323 hdr->nTimeBinSpan++;
324 if ((pagePtr - reinterpret_cast<uint8_t*>(page)) & 1) {
325 pagePtr++;
326 }
327 curTBHdr = reinterpret_cast<TPCZSTBHDR*>(pagePtr);
328 curTBHdr->rowMask |= (endpoint & 1) << 15;
329 nRowsInTB = 0;
330 lastRow = GPUTPCGeometry::NROWS;
331 }
332 if (tmpBuffer[k].getRow() != lastRow) {
333 curTBHdr->rowMask |= 1 << (tmpBuffer[k].getRow() - endpointStart);
334 ZSstreamOut(streamBuffer.data(), streamSize, streamBuffer8.data(), streamSize8, encodeBits);
335 if (nRowsInTB) {
336 curTBHdr->rowAddr1()[nRowsInTB - 1] = (pagePtr - reinterpret_cast<uint8_t*>(page)) + streamSize8;
337 }
338 nRowsInTB++;
339 nSeq = streamBuffer8.data() + streamSize8++;
340 *nSeq = 0;
341 }
342 (*nSeq)++;
343 streamBuffer8[streamSize8++] = tmpBuffer[k].getPad();
345 for (int32_t l = 0; l < seqLen; l++) {
346 streamBuffer[streamSize++] = (uint16_t)(tmpBuffer[k + l].getChargeFloat() * encodeBitsFactor + 0.5f);
347 }
348 return seqLen;
349}
350
351void zsEncoderRow::decodePage(std::vector<o2::tpc::Digit>& outputBuffer, const zsPage* decPage, uint32_t decEndpoint, uint32_t firstOrbit, uint32_t triggerBC)
352{
353 const uint8_t* decPagePtr = reinterpret_cast<const uint8_t*>(decPage);
354 const o2::header::RAWDataHeader* rdh = (const o2::header::RAWDataHeader*)decPagePtr;
355 if (o2::raw::RDHUtils::getMemorySize(*rdh) == sizeof(o2::header::RAWDataHeader)) {
356 return;
357 }
358 decPagePtr += sizeof(o2::header::RAWDataHeader);
359 const TPCZSHDR* decHDR = reinterpret_cast<const TPCZSHDR*>(decPagePtr);
360 decPagePtr += sizeof(*decHDR);
361 if (decHDR->version != 1 && decHDR->version != 2) {
362 throw std::runtime_error("invalid ZS version "s + std::to_string(decHDR->version) + " (1 or 2 expected)"s);
363 }
364 const float decodeBitsFactor = 1.f / (1 << (encodeBits - 10));
365 uint32_t mask = (1 << encodeBits) - 1;
366 int32_t cruid = decHDR->cruID;
367 uint32_t sector = cruid / 10;
368 if (sector != iSector) {
369 throw std::runtime_error("invalid TPC sector");
370 }
371 int32_t region = cruid % 10;
372 if ((uint32_t)region != decEndpoint / 2) {
373 throw std::runtime_error("CRU ID / endpoint mismatch");
374 }
375 int32_t nRowsRegion = GPUTPCGeometry::GetRegionRows(region);
376
377 int32_t timeBin = (decHDR->timeOffset + (uint64_t)(o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstOrbit) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN;
378 for (int32_t l = 0; l < decHDR->nTimeBinSpan; l++) {
379 if ((decPagePtr - reinterpret_cast<const uint8_t*>(decPage)) & 1) {
380 decPagePtr++;
381 }
382 const TPCZSTBHDR* tbHdr = reinterpret_cast<const TPCZSTBHDR*>(decPagePtr);
383 bool upperRows = tbHdr->rowMask & 0x8000;
384 if (tbHdr->rowMask != 0 && ((upperRows) ^ ((decEndpoint & 1) != 0))) {
385 throw std::runtime_error("invalid endpoint");
386 }
387 const int32_t rowOffset = GPUTPCGeometry::GetRegionStart(region) + (upperRows ? (nRowsRegion / 2) : 0);
388 const int32_t nRows = upperRows ? (nRowsRegion - nRowsRegion / 2) : (nRowsRegion / 2);
389 const int32_t nRowsUsed = __builtin_popcount((uint32_t)(tbHdr->rowMask & 0x7FFF));
390 decPagePtr += nRowsUsed ? (2 * nRowsUsed) : 2;
391 int32_t rowPos = 0;
392 for (int32_t m = 0; m < nRows; m++) {
393 if ((tbHdr->rowMask & (1 << m)) == 0) {
394 continue;
395 }
396 const uint8_t* rowData = rowPos == 0 ? decPagePtr : (reinterpret_cast<const uint8_t*>(decPage) + tbHdr->rowAddr1()[rowPos - 1]);
397 const int32_t nSeqRead = *rowData;
398 const uint8_t* adcData = rowData + 2 * nSeqRead + 1;
399 int32_t nADC = (rowData[2 * nSeqRead] * encodeBits + 7) / 8;
400 decPagePtr += 1 + 2 * nSeqRead + nADC;
401 uint32_t byte = 0, bits = 0, posXbits = 0;
402 std::array<uint16_t, TPCZSHDR::TPC_ZS_PAGE_SIZE> decBuffer;
403 for (int32_t n = 0; n < nADC; n++) {
404 byte |= *(adcData++) << bits;
405 bits += 8;
406 while (bits >= encodeBits) {
407 decBuffer[posXbits++] = byte & mask;
408 byte = byte >> encodeBits;
409 bits -= encodeBits;
410 }
411 }
412 posXbits = 0;
413 for (int32_t n = 0; n < nSeqRead; n++) {
414 const int32_t decSeqLen = rowData[(n + 1) * 2] - (n ? rowData[n * 2] : 0);
415 for (int32_t o = 0; o < decSeqLen; o++) {
416 outputBuffer.emplace_back(o2::tpc::Digit{cruid, decBuffer[posXbits++] * decodeBitsFactor, (tpccf::Row)(rowOffset + m), (tpccf::Pad)(rowData[n * 2 + 1] + o), timeBin + l});
417 }
418 }
419 rowPos++;
420 }
421 }
422}
423
424// ------------------------------------------------- TPC ZS Link Based ZS -------------------------------------------------
425
426#ifndef GPUCA_STANDALONE
427struct zsEncoderLinkBased : public zsEncoder {
428 TPCZSHDRV2* hdr = nullptr;
433 bool finishPage = false;
434 std::vector<uint16_t> adcValues = {};
435 std::bitset<80> bitmask = {};
436
437 void createBitmask(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k);
438 void init();
439 bool sort(const o2::tpc::Digit a, const o2::tpc::Digit b);
440};
441
442void zsEncoderLinkBased::init()
443{
444 encodeBits = TPCZSHDRV2::TPC_ZS_NBITS_V34;
445 for (int32_t i = 0; i < 5; i++) {
446 for (int32_t j = 0; j < 32; j++) {
448 }
449 }
450 for (int32_t iCRU = 0; iCRU < 2; iCRU++) {
451 for (int32_t iChannel = 0; iChannel < 80; iChannel++) {
452 int32_t sampaOnFEC = 0, channelOnSAMPA = 0;
453 Mapper::getSampaAndChannelOnFEC(iCRU, iChannel, sampaOnFEC, channelOnSAMPA);
454 if (inverseChannelMapping[sampaOnFEC][channelOnSAMPA] != -1 && inverseChannelMapping[sampaOnFEC][channelOnSAMPA] != iChannel) {
455 GPUError("ERROR: Channel conflict: %d %d: %d vs %d", sampaOnFEC, channelOnSAMPA, inverseChannelMapping[sampaOnFEC][channelOnSAMPA], iChannel);
456 throw std::runtime_error("ZS error");
457 }
458 inverseChannelMapping[sampaOnFEC][channelOnSAMPA] = iChannel;
459 }
460 }
461 for (int32_t i = 0; i < 5; i++) {
462 for (int32_t j = 0; j < 32; j++) {
463 if (inverseChannelMapping[i][j] == -1) {
464 GPUError("ERROR: Map missing for sampa %d channel %d", i, j);
465 throw std::runtime_error("ZS error");
466 }
467 }
468 }
469}
470
471void zsEncoderLinkBased::createBitmask(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k)
472{
473 const auto& mapper = Mapper::instance();
474 nSamples = 0;
475 adcValues.clear();
476 bitmask.reset();
477 uint32_t l;
478 for (l = k; l < tmpBuffer.size(); l++) {
479 const auto& a = tmpBuffer[l];
480 int32_t cruinsector = GPUTPCGeometry::GetRegion(a.getRow());
481 o2::tpc::GlobalPadNumber pad = mapper.globalPadNumber(o2::tpc::PadPos(a.getRow(), a.getPad()));
482 o2::tpc::FECInfo fec = mapper.fecInfo(pad);
483 o2::tpc::CRU cru = cruinsector;
484 int32_t fecInPartition = fec.getIndex() - mapper.getPartitionInfo(cru.partition()).getSectorFECOffset();
485 int32_t tmpEndpoint = 2 * cruinsector + (fecInPartition >= (mapper.getPartitionInfo(cru.partition()).getNumberOfFECs() + 1) / 2);
486 if (l == k) {
487 link = fecInPartition;
488 endpoint = tmpEndpoint;
489 } else if (endpoint != tmpEndpoint || link != fecInPartition || tmpBuffer[l].getTimeStamp() != tmpBuffer[k].getTimeStamp()) {
490 break;
491 }
493 bitmask[channel] = 1;
494 adcValues.emplace_back((uint16_t)(a.getChargeFloat() * encodeBitsFactor + 0.5f));
495 }
496 nSamples = l - k;
497}
498
499bool zsEncoderLinkBased::sort(const o2::tpc::Digit a, const o2::tpc::Digit b)
500{
501 // Fixme: this is blasphemy... one shoult precompute all values and sort an index array
502 int32_t cruinsectora = GPUTPCGeometry::GetRegion(a.getRow());
503 int32_t cruinsectorb = GPUTPCGeometry::GetRegion(b.getRow());
504 if (cruinsectora != cruinsectorb) {
505 return cruinsectora < cruinsectorb;
506 }
507 const auto& mapper = Mapper::instance();
508 o2::tpc::GlobalPadNumber pada = mapper.globalPadNumber(o2::tpc::PadPos(a.getRow(), a.getPad()));
509 o2::tpc::GlobalPadNumber padb = mapper.globalPadNumber(o2::tpc::PadPos(b.getRow(), b.getPad()));
510 o2::tpc::FECInfo feca = mapper.fecInfo(pada);
511 o2::tpc::FECInfo fecb = mapper.fecInfo(padb);
512 o2::tpc::CRU cru = cruinsectora;
513 int32_t fecInPartitiona = feca.getIndex() - mapper.getPartitionInfo(cru.partition()).getSectorFECOffset();
514 int32_t fecInPartitionb = fecb.getIndex() - mapper.getPartitionInfo(cru.partition()).getSectorFECOffset();
515
516 int32_t endpointa = 2 * cruinsectora + (fecInPartitiona >= (mapper.getPartitionInfo(cru.partition()).getNumberOfFECs() + 1) / 2);
517 int32_t endpointb = 2 * cruinsectorb + (fecInPartitionb >= (mapper.getPartitionInfo(cru.partition()).getNumberOfFECs() + 1) / 2);
518 if (endpointa != endpointb) {
519 return endpointa < endpointb;
520 }
521 if (a.getTimeStamp() != b.getTimeStamp()) {
522 return a.getTimeStamp() < b.getTimeStamp();
523 }
524 if (fecInPartitiona != fecInPartitionb) {
525 return fecInPartitiona < fecInPartitionb;
526 }
528}
529
530// ------------------------------------------------- TPC Improved Link Based ZS -------------------------------------------------
531
532struct zsEncoderImprovedLinkBased : public zsEncoderLinkBased {
533 bool checkInput(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k);
534 uint32_t encodeSequence(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k);
535 void decodePage(std::vector<o2::tpc::Digit>& outputBuffer, const zsPage* page, uint32_t endpoint, uint32_t firstOrbit, uint32_t triggerBC = 0);
536 bool writeSubPage();
537 void initPage();
538
539 constexpr static int32_t RAWLNK = rdh_utils::ILBZSLinkID;
540};
541
542bool zsEncoderImprovedLinkBased::checkInput(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k)
543{
544 createBitmask(tmpBuffer, k);
546 if (firstTimebinInPage != -1 && tmpBuffer[k].getTimeStamp() - firstTimebinInPage >= 1 << (sizeof(hdr->nTimeBinSpan) * 8)) {
547 finishPage = true;
548 }
549 if (!finishPage) {
550 uint32_t sizeChk = (uint32_t)(pagePtr - reinterpret_cast<uint8_t*>(page));
552 if (TPCZSHDRV2::TIGHTLY_PACKED_V3) {
553 sizeChk += (nSamples * TPCZSHDRV2::TPC_ZS_NBITS_V34 + 127) / 128 * 16;
554 } else {
555 sizeChk += (nSamples + 2 * TPCZSHDRV2::SAMPLESPER64BIT - 1) / (2 * TPCZSHDRV2::SAMPLESPER64BIT) * 16;
556 }
557 if (sizeChk > TPCZSHDR::TPC_ZS_PAGE_SIZE) {
558 finishPage = true;
559 }
560 }
561 return finishPage;
562}
563
564uint32_t zsEncoderImprovedLinkBased::encodeSequence(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k)
565{
567 pagePtr += sizeof(*tbHdr);
568 tbHdr->bunchCrossing = (tmpBuffer[k].getTimeStamp() - firstTimebinInPage) * LHCBCPERTIMEBIN;
569 tbHdr->magicWord = o2::tpc::zerosupp_link_based::CommonHeader::MagicWordLinkZS;
570 tbHdr->bitMaskHigh = (bitmask >> 64).to_ulong();
571 tbHdr->bitMaskLow = (bitmask & std::bitset<80>(0xFFFFFFFFFFFFFFFFlu)).to_ulong();
572 tbHdr->syncOffsetBC = 0;
573 tbHdr->fecInPartition = link;
574 hdr->nTimeBinSpan = tmpBuffer[k].getTimeStamp() - firstTimebinInPage;
575 hdr->nTimebinHeaders++;
576 if (TPCZSHDRV2::TIGHTLY_PACKED_V3) {
577 tbHdr->numWordsPayload = (nSamples * TPCZSHDRV2::TPC_ZS_NBITS_V34 + 127) / 128; // tightly packed ADC samples
578 uint32_t tmp = 0;
579 uint32_t tmpIn = nSamples;
580 ZSstreamOut(adcValues.data(), tmpIn, pagePtr, tmp, encodeBits);
581 } else {
582 tbHdr->numWordsPayload = (nSamples + 2 * TPCZSHDRV2::SAMPLESPER64BIT - 1) / (2 * TPCZSHDRV2::SAMPLESPER64BIT);
583 uint64_t* payloadPtr = (uint64_t*)pagePtr;
584 for (uint32_t i = 0; i < 2 * tbHdr->numWordsPayload; i++) {
585 payloadPtr[i] = 0;
586 }
587 for (uint32_t i = 0; i < nSamples; i++) {
588 payloadPtr[i / TPCZSHDRV2::SAMPLESPER64BIT] |= ((uint64_t)adcValues[i]) << ((i % TPCZSHDRV2::SAMPLESPER64BIT) * TPCZSHDRV2::TPC_ZS_NBITS_V34);
589 }
590 }
591 pagePtr += tbHdr->numWordsPayload * 16;
592 return nSamples;
593}
594
595bool zsEncoderImprovedLinkBased::writeSubPage()
596{
597 return finishPage;
598}
599
600void zsEncoderImprovedLinkBased::initPage()
601{
602 hdr->magicWord = o2::tpc::zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader;
603 hdr->nTimebinHeaders = 0;
604 hdr->firstZSDataOffset = 0;
605}
606
607void zsEncoderImprovedLinkBased::decodePage(std::vector<o2::tpc::Digit>& outputBuffer, const zsPage* decPage, uint32_t decEndpoint, uint32_t firstOrbit, uint32_t triggerBC)
608{
609 const auto& mapper = Mapper::instance();
610 const uint8_t* decPagePtr = reinterpret_cast<const uint8_t*>(decPage);
611 const o2::header::RAWDataHeader* rdh = (const o2::header::RAWDataHeader*)decPagePtr;
612 if (o2::raw::RDHUtils::getMemorySize(*rdh) == sizeof(o2::header::RAWDataHeader)) {
613 return;
614 }
615 decPagePtr += sizeof(o2::header::RAWDataHeader);
616 const TPCZSHDRV2* decHDR = reinterpret_cast<const TPCZSHDRV2*>(decPagePtr);
617 decPagePtr += sizeof(*decHDR);
618 if (decHDR->version != ZSVersion::ZSVersionLinkBasedWithMeta) {
619 throw std::runtime_error("invalid ZS version "s + std::to_string(decHDR->version) + " ("s + std::to_string(ZSVersion::ZSVersionLinkBasedWithMeta) + " expected)"s);
620 }
621 if (decHDR->magicWord != o2::tpc::zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader) {
622 throw std::runtime_error("Magic word missing");
623 }
624 const float decodeBitsFactor = 1.f / (1 << (encodeBits - 10));
625 uint32_t mask = (1 << encodeBits) - 1;
626 int32_t cruid = decHDR->cruID;
627 uint32_t sector = cruid / 10;
628 if (sector != iSector) {
629 throw std::runtime_error("invalid TPC sector");
630 }
631 int32_t region = cruid % 10;
632 decPagePtr += decHDR->firstZSDataOffset * 16;
633 for (uint32_t i = 0; i < decHDR->nTimebinHeaders; i++) {
635#if 0 // Decoding using the function for the original linkZS
636 o2::tpc::CRU cru = cruid % 10;
637 const int32_t feeLink = tbHdr->fecInPartition - (decEndpoint & 1) * ((mapper.getPartitionInfo(cru.partition()).getNumberOfFECs() + 1) / 2);
638 auto fillADC = [&outputBuffer](int32_t cru, int32_t rowInSector, int32_t padInRow, int32_t timeBin, float adcValue) {
639 outputBuffer.emplace_back(o2::tpc::Digit{cruid, adcValue, rowInSector, padInRow, timeBin});
640 return true;
641 };
642 size_t size = sizeof(*tbHdr) + tbHdr->numWordsPayload * 16;
643 raw_processing_helpersa::processZSdata((const char*)decPagePtr, size, rdh_utils::getFEEID(cruid, decEndpoint & 1, feeLink), o2::raw::RDHUtils::getHeartBeatOrbit(*rdh), firstOrbit, decHDR->timeOffset, fillADC);
644#else // Decoding directly
645 if (!tbHdr->isLinkZS()) {
646 throw std::runtime_error("ZS TB Hdr does not have linkZS magic word");
647 }
648 int32_t timeBin = (int32_t(decHDR->timeOffset) + int32_t(tbHdr->bunchCrossing) + (int32_t)(o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstOrbit) * o2::constants::lhc::LHCMaxBunches - triggerBC) / LHCBCPERTIMEBIN;
649 if (timeBin < 0) {
650 LOGP(debug, "zsEncoderImprovedLinkBased::decodePage skipping digits hdr->tOff {} + hdr->bc {} + (orbit {} - firstOrbit {}) * maxBunch {} - triggerBC {} = {} < 0", decHDR->timeOffset, tbHdr->bunchCrossing, o2::raw::RDHUtils::getHeartBeatOrbit(*rdh), firstOrbit, o2::constants::lhc::LHCMaxBunches, triggerBC, timeBin);
651 continue;
652 }
653 const uint8_t* adcData = (const uint8_t*)(decPagePtr + sizeof(*tbHdr));
654 const auto& bitmask = tbHdr->getChannelBits();
655 int32_t nADC = bitmask.count();
656 std::vector<uint16_t> decBuffer(nADC);
657 if (TPCZSHDRV2::TIGHTLY_PACKED_V3) {
658 uint32_t byte = 0, bits = 0, posXbits = 0;
659 while (posXbits < nADC) {
660 byte |= *(adcData++) << bits;
661 bits += 8;
662 while (bits >= encodeBits) {
663 decBuffer[posXbits++] = byte & mask;
664 byte = byte >> encodeBits;
665 bits -= encodeBits;
666 }
667 }
668 } else {
669 const uint64_t* adcData64 = (const uint64_t*)adcData;
670 for (int32_t j = 0; j < nADC; j++) {
671 decBuffer[j] = (adcData64[j / TPCZSHDRV2::SAMPLESPER64BIT] >> ((j % TPCZSHDRV2::SAMPLESPER64BIT) * TPCZSHDRV2::TPC_ZS_NBITS_V34)) & mask;
672 }
673 }
674 for (int32_t j = 0, k = 0; j < bitmask.size(); j++) {
675 if (bitmask[j]) {
676 int32_t sampaOnFEC = 0, channelOnSAMPA = 0;
677 mapper.getSampaAndChannelOnFEC(cruid, j, sampaOnFEC, channelOnSAMPA);
678 const auto padSecPos = mapper.padSecPos(cruid, tbHdr->fecInPartition, sampaOnFEC, channelOnSAMPA);
679 const auto& padPos = padSecPos.getPadPos();
680 outputBuffer.emplace_back(o2::tpc::Digit{cruid, decBuffer[k++] * decodeBitsFactor, (tpccf::Row)padPos.getRow(), (tpccf::Pad)padPos.getPad(), timeBin});
681 }
682 }
683#endif
684 decPagePtr += sizeof(*tbHdr) + tbHdr->numWordsPayload * 16;
685 }
686}
687
688// ------------------------------------------------- TPC ZS Dense Link Based ZS -------------------------------------------------
689
690struct zsEncoderDenseLinkBased : public zsEncoderLinkBased {
691 bool checkInput(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k);
692 uint32_t encodeSequence(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k);
693 void decodePage(std::vector<o2::tpc::Digit>& outputBuffer, const zsPage* page, uint32_t endpoint, uint32_t firstOrbit, uint32_t triggerBC = 0);
694 bool writeSubPage();
695 void initPage();
696 void amendPageErrorMessage(std::ostringstream& oss, const o2::header::RAWDataHeader* rdh, const TPCZSHDRV2* decHDR, const uint8_t* payloadEnd, const uint8_t* decPagePtr, uint32_t nOutput);
697
699 std::vector<uint8_t> sequenceBuffer;
700 std::vector<uint16_t> sequenceBufferADC;
701
702 constexpr static int32_t RAWLNK = rdh_utils::DLBZSLinkID;
703 constexpr static int32_t v2nbits = 10;
704};
705
706bool zsEncoderDenseLinkBased::checkInput(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k)
707{
708 createBitmask(tmpBuffer, k);
710 uint16_t newTimeBin = tmpBuffer[k].getTimeStamp() - firstTimebinInPage;
711 bool retVall = finishPage || newTimeBin != curTimeBin;
712 return retVall;
713}
714
715uint32_t zsEncoderDenseLinkBased::encodeSequence(std::vector<o2::tpc::Digit>& tmpBuffer, uint32_t k)
716{
717 if (sequenceBuffer.size() == 0) {
718 uint16_t bc = (int64_t)tmpBuffer[k].getTimeStamp() * LHCBCPERTIMEBIN - (int64_t)hbf * o2::constants::lhc::LHCMaxBunches;
719 if (zsVersion == ZSVersion::ZSVersionDenseLinkBasedV2) {
720 bc &= 0xFFC;
721 }
722 sequenceBuffer.emplace_back(bc << 4);
723 sequenceBuffer.emplace_back(bc >> 4);
724 curTimeBin = tmpBuffer[k].getTimeStamp() - firstTimebinInPage;
725 hdr->nTimeBinSpan = curTimeBin & 0xFF;
726 if (curTimeBin & 0x100) {
727 hdr->flags |= TPCZSHDRV2::ZSFlags::nTimeBinSpanBit8;
728 }
729 hdr->nTimebinHeaders++;
730 }
731 sequenceBuffer[0]++;
732
733 sequenceBuffer.emplace_back(link);
734 uint8_t* plink = &sequenceBuffer.back();
735
736 std::bitset<10> bitmaskL2;
737 for (int32_t i = 9; i >= 0; i--) {
738 bitmaskL2.set(i, ((bitmask >> (i * 8)) & std::bitset<80>(0xFF)).any());
739 }
740 if (bitmaskL2.all()) {
741 *plink |= 0b00100000;
742 } else {
743 *plink |= (bitmaskL2.to_ulong() >> 2) & 0b11000000;
744 sequenceBuffer.emplace_back(bitmaskL2.to_ulong() & 0xFF);
745 }
746
747 for (int32_t i = 0; i < 10; i++) {
748 if (bitmaskL2.test(i)) {
749 sequenceBuffer.emplace_back(((bitmask >> (i * 8)) & std::bitset<80>(0xFF)).to_ulong());
750 }
751 }
752
753 static_assert(TPCZSHDRV2::TPC_ZS_NBITS_V34 == 12);
754 if (nSamples) {
755 sequenceBufferADC.insert(sequenceBufferADC.end(), adcValues.begin(), adcValues.end());
756 }
757
758 return nSamples;
759}
760
761bool zsEncoderDenseLinkBased::writeSubPage()
762{
764 if (sequenceBufferADC.size()) {
765 bool need12bit = zsVersion != ZSVersion::ZSVersionDenseLinkBasedV2;
766 uint32_t needNow = 0;
767 if (zsVersion == ZSVersion::ZSVersionDenseLinkBasedV2) {
768 for (uint32_t i = 0; i < sequenceBufferADC.size(); i++) {
769 if (sequenceBufferADC[i] >= (1 << v2nbits)) {
770 need12bit = true;
771 break;
772 }
773 }
774 }
775 uint32_t encodeBitsBlock = encodeBits;
776 if (!need12bit) {
777 encodeBitsBlock = v2nbits;
778 sequenceBuffer[0] |= 0x10;
779 }
780 sequenceBuffer.resize(offset + (sequenceBufferADC.size() * encodeBitsBlock + 7) / 8);
781 uint32_t tmp = 0;
782 uint32_t tmpIn = sequenceBufferADC.size();
783 ZSstreamOut(sequenceBufferADC.data(), tmpIn, sequenceBuffer.data() + offset, tmp, encodeBitsBlock);
784 sequenceBufferADC.clear();
785 }
786
787 if (sequenceBuffer.size()) {
788 uint32_t sizeLeft = TPCZSHDR::TPC_ZS_PAGE_SIZE - (pagePtr - (uint8_t*)page) - sizeof(TPCZSHDRV2) - (hdr->flags & TPCZSHDRV2::ZSFlags::TriggerWordPresent ? TPCZSHDRV2::TRIGGER_WORD_SIZE : 0);
790 uint32_t fill = std::min(sizeLeft, size);
791 memcpy(pagePtr, sequenceBuffer.data(), fill);
792 pagePtr += fill;
793 if (size != fill) {
795 sequenceBuffer.erase(sequenceBuffer.begin(), sequenceBuffer.begin() + fill);
796 } else {
797 sequenceBuffer.clear();
798 }
799 finishPage = finishPage || size >= sizeLeft || needAnotherPage;
800 }
801
802 return finishPage;
803}
804
805void zsEncoderDenseLinkBased::initPage()
806{
807 hdr->magicWord = o2::tpc::zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader;
808 hdr->nTimebinHeaders = 0;
809 memcpy(pagePtr, sequenceBuffer.data(), sequenceBuffer.size());
810 hdr->firstZSDataOffset = sequenceBuffer.size() + sizeof(o2::header::RAWDataHeader);
811 pagePtr += sequenceBuffer.size();
812 sequenceBuffer.clear();
813 hdr->flags = 0;
814}
815
816void zsEncoderDenseLinkBased::decodePage(std::vector<o2::tpc::Digit>& outputBuffer, const zsPage* decPage, uint32_t decEndpoint, uint32_t firstOrbit, uint32_t triggerBC)
817{
818 const auto& mapper = Mapper::instance();
819 const uint8_t* decPagePtr = reinterpret_cast<const uint8_t*>(decPage);
820 const o2::header::RAWDataHeader* rdh = (const o2::header::RAWDataHeader*)decPagePtr;
821 if (o2::raw::RDHUtils::getMemorySize(*rdh) == sizeof(o2::header::RAWDataHeader)) {
822 return;
823 }
824 const TPCZSHDRV2* decHDR = reinterpret_cast<const TPCZSHDRV2*>(decPagePtr + o2::raw::RDHUtils::getMemorySize(*rdh) - sizeof(TPCZSHDRV2));
825 decPagePtr += sizeof(o2::header::RAWDataHeader);
826 if (decHDR->version < ZSVersion::ZSVersionDenseLinkBased || decHDR->version > ZSVersion::ZSVersionDenseLinkBasedV2) {
827 throw std::runtime_error("invalid ZS version "s + std::to_string(decHDR->version) + " ("s + std::to_string(ZSVersion::ZSVersionDenseLinkBased) + " - "s + std::to_string(ZSVersion::ZSVersionDenseLinkBasedV2) + " expected)"s);
828 }
829 if (decHDR->magicWord != o2::tpc::zerosupp_link_based::CommonHeader::MagicWordLinkZSMetaHeader) {
830 throw std::runtime_error("Magic word missing");
831 }
832 const uint8_t* payloadEnd = ((const uint8_t*)decPage) + o2::raw::RDHUtils::getMemorySize(*rdh) - sizeof(TPCZSHDRV2) - ((decHDR->flags & TPCZSHDRV2::ZSFlags::TriggerWordPresent) ? TPCZSHDRV2::TRIGGER_WORD_SIZE : 0);
833 const float decodeBitsFactor = 1.f / (1 << (encodeBits - 10));
834 int32_t cruid = decHDR->cruID;
835 uint32_t sector = cruid / 10;
836 if (sector != iSector) {
837 throw std::runtime_error("invalid TPC sector");
838 }
839 int32_t region = cruid % 10;
840 decPagePtr += decHDR->firstZSDataOffset - sizeof(o2::header::RAWDataHeader);
841 std::vector<uint8_t> tmpBuffer;
842 bool extendFailure = false;
843 uint32_t nOutput = 0;
844 uint32_t minTimeBin = -1, maxTimeBin = 0;
845 for (uint32_t i = 0; i < decHDR->nTimebinHeaders; i++) {
846 int32_t sizeLeftInPage = payloadEnd - decPagePtr;
847 if (sizeLeftInPage <= 0) {
848 throw std::runtime_error("Decoding ran beyond end of page before processing extended timebin");
849 }
851 if (o2::raw::RDHUtils::getMemorySize(*rdh) != TPCZSHDR::TPC_ZS_PAGE_SIZE) {
852 throw std::runtime_error("pageExtends signaled, but current page is not full");
853 }
854
855 const uint8_t* pageNext = ((const uint8_t*)decPage) + TPCZSHDR::TPC_ZS_PAGE_SIZE;
856 const o2::header::RAWDataHeader* rdhNext = (const o2::header::RAWDataHeader*)pageNext;
857
858 if ((uint16_t)(o2::raw::RDHUtils::getPageCounter(*rdh) + 1) != o2::raw::RDHUtils::getPageCounter(*rdhNext)) {
859 GPUError("Incomplete HBF: Payload extended to next page, but next page missing in stream (packet counters %d %d)", (int32_t)o2::raw::RDHUtils::getPageCounter(*rdh), (int32_t)o2::raw::RDHUtils::getPageCounter(*rdhNext));
860 extendFailure = true;
861 decPagePtr = payloadEnd; // Next 8kb page is missing in stream, cannot decode remaining data, skip it
862 break;
863 }
864
865 const TPCZSHDRV2* hdrNext = reinterpret_cast<const TPCZSHDRV2*>(pageNext + o2::raw::RDHUtils::getMemorySize(*rdhNext) - sizeof(TPCZSHDRV2));
866 tmpBuffer.resize(sizeLeftInPage + hdrNext->firstZSDataOffset - sizeof(o2::header::RAWDataHeader));
867 memcpy(tmpBuffer.data(), decPagePtr, sizeLeftInPage);
868 memcpy(tmpBuffer.data() + sizeLeftInPage, pageNext + sizeof(o2::header::RAWDataHeader), hdrNext->firstZSDataOffset - sizeof(o2::header::RAWDataHeader));
869 decPagePtr = tmpBuffer.data();
870 payloadEnd = decPagePtr + tmpBuffer.size();
871 }
872 uint8_t linkCount = *((const uint8_t*)decPagePtr) & 0x0F;
873 uint16_t linkBC = (*((const uint16_t*)decPagePtr) & 0xFFF0) >> 4;
874 bool v2Flag = decHDR->version == ZSVersion::ZSVersionDenseLinkBasedV2 && *((const uint8_t*)decPagePtr) & 0x10;
875 if (decHDR->version == ZSVersion::ZSVersionDenseLinkBasedV2) {
876 linkBC &= 0xFFC;
877 }
878 decPagePtr += sizeof(uint16_t);
879 std::vector<int32_t> links;
880 std::vector<std::bitset<80>> bitmasks;
881 uint32_t nTotalSamples = 0;
882 for (uint32_t l = 0; l < linkCount; l++) {
883 uint8_t decLinkX = *((const uint8_t*)decPagePtr);
884 decPagePtr += sizeof(uint8_t);
885 uint8_t decLink = decLinkX & 0b00011111;
886 std::bitset<10> bitmaskL2;
887 if (decLinkX & 0b00100000) {
888 bitmaskL2.set();
889 } else {
890 bitmaskL2 = std::bitset<10>(((((uint16_t)decLinkX) & 0b11000000) << 2) | (uint16_t)*((const uint8_t*)decPagePtr));
891 decPagePtr += sizeof(uint8_t);
892 }
893
894 std::bitset<80> bitmask(0);
895 for (int32_t i = 0; i < 10; i++) {
896 if (bitmaskL2.test(i)) {
897 bitmask |= std::bitset<80>(*((const uint8_t*)decPagePtr)) << i * 8;
898 decPagePtr += sizeof(uint8_t);
899 }
900 }
901 links.emplace_back(decLink);
902 bitmasks.emplace_back(bitmask);
903 nTotalSamples += bitmask.count();
904 }
905
906 const uint8_t* adcData = (const uint8_t*)decPagePtr;
907 int32_t encodeBitsBlock = v2Flag ? v2nbits : encodeBits;
908 decPagePtr += (nTotalSamples * encodeBitsBlock + 7) / 8;
909
910 // time bin might be smaller 0 due to triggerBC
911 int32_t timeBin = (int32_t(linkBC) + (int32_t)(o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstOrbit) * o2::constants::lhc::LHCMaxBunches - int32_t(triggerBC)) / LHCBCPERTIMEBIN;
912 if (timeBin < 0 || nTotalSamples == 0) {
913 if (timeBin < 0 && nTotalSamples > 0) {
914 LOGP(debug, "zsEncoderDenseLinkBased::decodePage skipping digits (linkBC {} + orbit {} - firstOrbit {}) * maxBunch {} - triggerBC {} = {} < 0, nTotalSamples {}", linkBC, o2::raw::RDHUtils::getHeartBeatOrbit(*rdh), firstOrbit, o2::constants::lhc::LHCMaxBunches, triggerBC, timeBin, nTotalSamples);
915 }
916 continue;
917 }
918 if (timeBin > maxTimeBin) {
919 maxTimeBin = timeBin;
920 }
921 if (timeBin < minTimeBin) {
922 minTimeBin = timeBin;
923 }
924
925 std::vector<uint16_t> samples(nTotalSamples);
926 uint32_t mask = (1 << encodeBitsBlock) - 1;
927 uint32_t byte = 0, bits = 0, posXbits = 0;
928 while (posXbits < nTotalSamples) {
929 byte |= *(adcData++) << bits;
930 bits += 8;
931 while (bits >= encodeBitsBlock && posXbits < nTotalSamples) {
932 samples[posXbits++] = byte & mask;
933 byte = byte >> encodeBitsBlock;
934 bits -= encodeBitsBlock;
935 }
936 }
937 uint32_t samplePos = 0;
938
939 for (uint32_t l = 0; l < linkCount; l++) {
940 uint8_t decLink = links[l];
941 const auto& bitmask = bitmasks[l];
942 int32_t nADC = bitmask.count();
943
944 for (int32_t j = 0; j < bitmask.size(); j++) {
945 if (bitmask[j]) {
946 int32_t sampaOnFEC = 0, channelOnSAMPA = 0;
947 mapper.getSampaAndChannelOnFEC(cruid, j, sampaOnFEC, channelOnSAMPA);
948 const auto padSecPos = mapper.padSecPos(cruid, decLink, sampaOnFEC, channelOnSAMPA);
949 const auto& padPos = padSecPos.getPadPos();
950 outputBuffer.emplace_back(o2::tpc::Digit{cruid, samples[samplePos++] * decodeBitsFactor, (tpccf::Row)padPos.getRow(), (tpccf::Pad)padPos.getPad(), timeBin});
951 nOutput++;
952 }
953 }
954 }
955 }
956
957 int32_t hdrMinTimeBin = (int32_t(decHDR->timeOffset) + int32_t(o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstOrbit) * o2::constants::lhc::LHCMaxBunches - triggerBC);
958 if (triggerBC > 0 && hdrMinTimeBin < 0) {
959 hdrMinTimeBin = 0;
960 }
961 hdrMinTimeBin /= LHCBCPERTIMEBIN;
962 int32_t hdrMaxTimeBin = hdrMinTimeBin + decHDR->nTimeBinSpan + ((decHDR->flags & TPCZSHDRV2::ZSFlags::nTimeBinSpanBit8) ? 256 : 0);
963
964 if (!extendFailure && nOutput != decHDR->nADCsamples) {
965 std::ostringstream oss;
966 oss << "Number of decoded digits " << nOutput << " does not match value from MetaInfo " << decHDR->nADCsamples;
967 amendPageErrorMessage(oss, rdh, decHDR, nullptr, nullptr, nOutput);
968 throw std::runtime_error(oss.str());
969 }
970
971 if (decHDR->nADCsamples && (minTimeBin < hdrMinTimeBin || maxTimeBin > hdrMaxTimeBin)) {
972 std::ostringstream oss;
973 oss << "Incorrect time bin range in MetaInfo, header reports " << hdrMinTimeBin << " - " << hdrMaxTimeBin << "(timeOffset: " << decHDR->timeOffset << " + (orbit: " << o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) << " - firstOrbit " << firstOrbit << ") * LHCMaxBunches - triggerBC: " << triggerBC << ", decoded data is " << minTimeBin << " - " << maxTimeBin;
974 amendPageErrorMessage(oss, rdh, decHDR, payloadEnd, decPagePtr, nOutput);
975 throw std::runtime_error(oss.str());
976 }
977
978 if (decHDR->nTimebinHeaders && payloadEnd - decPagePtr < 0) {
979 std::ostringstream oss;
980 oss << "Decoding ran over end of page";
981 amendPageErrorMessage(oss, rdh, decHDR, payloadEnd, decPagePtr, nOutput);
982 throw std::runtime_error(oss.str());
983 }
984 if (decHDR->nTimebinHeaders && payloadEnd - decPagePtr >= 2 * o2::raw::RDHUtils::GBTWord128) {
985 std::ostringstream oss;
986 oss << "Decoding didn't reach end of page";
987 amendPageErrorMessage(oss, rdh, decHDR, payloadEnd, decPagePtr, nOutput);
988 throw std::runtime_error(oss.str());
989 }
990}
991
992void zsEncoderDenseLinkBased::amendPageErrorMessage(std::ostringstream& oss, const o2::header::RAWDataHeader* rdh, const TPCZSHDRV2* decHDR, const uint8_t* payloadEnd, const uint8_t* decPagePtr, uint32_t nOutput)
993{
994 if (payloadEnd && decPagePtr) {
995 oss << " (payloadEnd " << (void*)payloadEnd << " - decPagePtr " << (void*)decPagePtr << " - " << (payloadEnd - decPagePtr) << " bytes left, " << nOutput << " of " << decHDR->nADCsamples << " digits decoded)\n";
996 } else {
997 oss << "\n";
998 }
999 constexpr size_t bufferSize = 3 * std::max(sizeof(*rdh), sizeof(*decHDR)) + 1;
1000 char dumpBuffer[bufferSize];
1001 for (size_t i = 0; i < sizeof(*rdh); i++) {
1002 snprintf(dumpBuffer + 3 * i, 4, "%02X ", (int32_t)((uint8_t*)rdh)[i]);
1003 }
1004 oss << "RDH of page: " << dumpBuffer << "\n";
1005 for (size_t i = 0; i < sizeof(*decHDR); i++) {
1006 snprintf(dumpBuffer + 3 * i, 4, "%02X ", (int32_t)((uint8_t*)decHDR)[i]);
1007 }
1008 oss << "Meta header of page: " << dumpBuffer << "\n";
1009}
1010
1011#endif // !GPUCA_STANDALONE
1012
1013// ------------------------------------------------- TPC ZS Main Encoder -------------------------------------------------
1014
1015template <class T>
1016struct zsEncoderRun : public T {
1017 uint32_t run(std::vector<zsPage>* buffer, std::vector<o2::tpc::Digit>& tmpBuffer, size_t* totalSize = nullptr);
1018 size_t compare(std::vector<zsPage>* buffer, std::vector<o2::tpc::Digit>& tmpBuffer);
1019
1020 using T::bcShiftInFirstHBF;
1021 using T::checkInput;
1022 using T::curRegion;
1023 using T::decodePage;
1024 using T::encodeBits;
1025 using T::encodeBitsFactor;
1026 using T::encodeSequence;
1027 using T::endpoint;
1028 using T::firstTimebinInPage;
1029 using T::getHbf;
1030 using T::hbf;
1031 using T::hdr;
1032 using T::init;
1033 using T::initPage;
1034 using T::ir;
1035 using T::iSector;
1036 using T::lastEndpoint;
1037 using T::lastRow;
1038 using T::lastTime;
1039 using T::needAnotherPage;
1040 using T::nexthbf;
1041 using T::outputEndpoint;
1042 using T::outputRegion;
1043 using T::packetCounter;
1044 using T::padding;
1045 using T::page;
1046 using T::pageCounter;
1047 using T::pagePtr;
1048 using T::param;
1049 using T::raw;
1050 using T::sort;
1051 using T::writeSubPage;
1052 using T::ZSfillEmpty;
1053 using T::zsVersion;
1054};
1055
1056template <class T>
1057inline uint32_t zsEncoderRun<T>::run(std::vector<zsPage>* buffer, std::vector<o2::tpc::Digit>& tmpBuffer, size_t* totalSize)
1058{
1059 uint32_t totalPages = 0;
1060 zsPage singleBuffer;
1061#ifndef GPUCA_STANDALONE
1062 int32_t maxhbf = 0;
1063 int32_t minhbf = o2::constants::lhc::LHCMaxBunches;
1064#endif
1065 bcShiftInFirstHBF = ir ? ir->bc : 0;
1066 int32_t orbitShift = ir ? ir->orbit : 0;
1067 int32_t rawcru = 0;
1068 int32_t rawendpoint = 0;
1069 (void)(rawcru + rawendpoint); // avoid compiler warning
1070 encodeBitsFactor = (1 << (encodeBits - 10));
1071
1072 std::sort(tmpBuffer.begin(), tmpBuffer.end(), [this](const o2::tpc::Digit a, const o2::tpc::Digit b) { return sort(a, b); });
1073 for (uint32_t k = 0; k <= tmpBuffer.size();) {
1074 bool mustWritePage = false, mustWriteSubPage = false;
1075 if (needAnotherPage) {
1076 needAnotherPage = false;
1077 mustWritePage = true;
1078 } else {
1079 if (k < tmpBuffer.size()) {
1080 if (tmpBuffer[k].getTimeStamp() != lastTime) {
1081 nexthbf = getHbf(tmpBuffer[k].getTimeStamp());
1082 if (nexthbf < 0) {
1083 throw std::runtime_error("Received digit before the defined first orbit");
1084 }
1085 if (hbf != nexthbf) {
1086 lastEndpoint = -2;
1087 mustWritePage = true;
1088 }
1089 }
1090 if (lastRow != tmpBuffer[k].getRow()) {
1091 curRegion = GPUTPCGeometry::GetRegion(tmpBuffer[k].getRow());
1092 }
1093 mustWriteSubPage = checkInput(tmpBuffer, k);
1094 } else {
1095 nexthbf = -1;
1096 mustWritePage = true;
1097 }
1098 }
1099 if (mustWritePage || mustWriteSubPage) {
1100 mustWritePage |= writeSubPage();
1101
1102 if (page && mustWritePage) {
1103 if constexpr (std::is_same_v<T, struct zsEncoderDenseLinkBased>) {
1104 if ((pagePtr - (uint8_t*)page) % o2::raw::RDHUtils::GBTWord128) {
1105 pagePtr += o2::raw::RDHUtils::GBTWord128 - (pagePtr - (uint8_t*)page) % o2::raw::RDHUtils::GBTWord128;
1106 }
1107 uint8_t* triggerWord = nullptr;
1108 if (hbf != nexthbf || endpoint != lastEndpoint) {
1109 if ((pagePtr - (uint8_t*)page) + sizeof(TPCZSHDRV2) + o2::tpc::TPCZSHDRV2::TRIGGER_WORD_SIZE <= TPCZSHDR::TPC_ZS_PAGE_SIZE) {
1110 if ((pagePtr - (uint8_t*)page) % (2 * o2::raw::RDHUtils::GBTWord128)) {
1111 pagePtr += o2::raw::RDHUtils::GBTWord128; // align to 256 bit, size constrained cannot be affected by this
1112 }
1114 } else {
1115 needAnotherPage = true;
1116 }
1117 if (this->sequenceBuffer.size()) {
1118 needAnotherPage = true;
1119 }
1120 }
1122 triggerWord = pagePtr;
1123 pagePtr += o2::tpc::TPCZSHDRV2::TRIGGER_WORD_SIZE;
1124 }
1125 if ((pagePtr - (uint8_t*)page) % (2 * o2::raw::RDHUtils::GBTWord128) == 0) {
1126 pagePtr += o2::raw::RDHUtils::GBTWord128; // align to 128bit mod 256
1127 }
1128 TPCZSHDRV2* pagehdr = (TPCZSHDRV2*)pagePtr;
1129 pagePtr += sizeof(TPCZSHDRV2);
1130 if (pagePtr - (uint8_t*)page > TPCZSHDR::TPC_ZS_PAGE_SIZE) {
1131 throw std::runtime_error("TPC ZS page overflow");
1132 }
1133 memcpy(pagehdr, hdr, sizeof(*hdr));
1134 if (triggerWord) {
1135 memset(triggerWord, 0, o2::tpc::TPCZSHDRV2::TRIGGER_WORD_SIZE);
1136 }
1137 }
1138 const rdh_utils::FEEIDType rawfeeid = rdh_utils::getFEEID(rawcru, rawendpoint, this->RAWLNK);
1139 if (totalSize) {
1140 *totalSize += !std::is_same_v<T, struct zsEncoderDenseLinkBased> && (lastEndpoint == -1 || hbf == nexthbf) ? TPCZSHDR::TPC_ZS_PAGE_SIZE : (pagePtr - (uint8_t*)page);
1141 }
1142 size_t size = !std::is_same_v<T, struct zsEncoderDenseLinkBased> && (padding || lastEndpoint == -1 || hbf == nexthbf) ? TPCZSHDR::TPC_ZS_PAGE_SIZE : (pagePtr - (uint8_t*)page);
1143 size = CAMath::nextMultipleOf<o2::raw::RDHUtils::GBTWord128>(size);
1144#ifndef GPUCA_STANDALONE
1145 if (raw) {
1146 raw->addData(rawfeeid, rawcru, 0, rawendpoint, *ir + hbf * o2::constants::lhc::LHCMaxBunches, gsl::span<char>((char*)page + sizeof(o2::header::RAWDataHeader), (char*)page + size), true, 0, 2);
1147 maxhbf = std::max<int32_t>(maxhbf, hbf);
1148 minhbf = std::min<int32_t>(minhbf, hbf);
1149 } else
1150#endif
1151 {
1153 o2::raw::RDHUtils::setHeartBeatOrbit(*rdh, hbf + orbitShift);
1154 o2::raw::RDHUtils::setHeartBeatBC(*rdh, bcShiftInFirstHBF);
1156 o2::raw::RDHUtils::setVersion(*rdh, o2::raw::RDHUtils::getVersion<o2::header::RAWDataHeader>());
1157 o2::raw::RDHUtils::setFEEID(*rdh, rawfeeid);
1159 o2::raw::RDHUtils::setLinkID(*rdh, this->RAWLNK);
1162 }
1163 }
1164 if (k >= tmpBuffer.size() && !needAnotherPage) {
1165 break;
1166 }
1167 }
1168 if (mustWritePage) {
1169 if (!needAnotherPage) {
1170 if (hbf != nexthbf) {
1171 pageCounter = 0;
1172 }
1175 hbf = nexthbf;
1176 lastTime = -1;
1178 }
1179 if (raw) {
1180 page = &singleBuffer;
1181 } else {
1182 if (buffer[outputEndpoint].size() == 0 && nexthbf > orbitShift) {
1183 buffer[outputEndpoint].emplace_back();
1184 ZSfillEmpty(&buffer[outputEndpoint].back(), bcShiftInFirstHBF, rdh_utils::getFEEID(iSector * 10 + outputEndpoint / 2, outputEndpoint & 1, this->RAWLNK), orbitShift, this->RAWLNK); // Emplace empty page with RDH containing beginning of TF
1185 if (totalSize) {
1186 *totalSize += sizeof(o2::header::RAWDataHeader);
1187 }
1188 totalPages++;
1189 }
1190 buffer[outputEndpoint].emplace_back();
1191 page = &buffer[outputEndpoint].back();
1192 }
1193 pagePtr = reinterpret_cast<uint8_t*>(page);
1194 std::fill(page->begin(), page->end(), 0);
1196 if constexpr (std::is_same_v<T, struct zsEncoderDenseLinkBased>) {
1197 hdr = &this->hdrBuffer;
1198 } else {
1199 hdr = reinterpret_cast<decltype(hdr)>(pagePtr);
1200 pagePtr += sizeof(*hdr);
1201 }
1203 hdr->cruID = iSector * 10 + outputRegion;
1204 hdr->nTimeBinSpan = 0;
1205 hdr->nADCsamples = 0;
1206 rawcru = iSector * 10 + outputRegion;
1207 rawendpoint = outputEndpoint & 1;
1208 hdr->timeOffset = (int64_t)(needAnotherPage ? firstTimebinInPage : tmpBuffer[k].getTimeStamp()) * LHCBCPERTIMEBIN - (int64_t)hbf * o2::constants::lhc::LHCMaxBunches;
1209 firstTimebinInPage = tmpBuffer[k].getTimeStamp();
1210 initPage();
1211 totalPages++;
1212 }
1213 if (needAnotherPage) {
1214 continue;
1215 }
1216 uint32_t nEncoded = encodeSequence(tmpBuffer, k);
1217 lastTime = tmpBuffer[k].getTimeStamp();
1218 lastRow = tmpBuffer[k].getRow();
1219 hdr->nADCsamples += nEncoded;
1220 k += nEncoded;
1221 }
1222 if (raw) {
1223#ifndef GPUCA_STANDALONE
1224 if (iSector == 0) {
1225 for (int32_t i = minhbf; i <= maxhbf; i++) {
1226 raw->addData(46208, 360, rdh_utils::SACLinkID, 0, *ir + i * o2::constants::lhc::LHCMaxBunches, gsl::span<char>((char*)&singleBuffer, (char*)&singleBuffer), true, 0, 4);
1227 }
1228 }
1229#endif
1230 } else {
1231 for (uint32_t j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) {
1232 if (buffer[j].size() == 0) {
1233 buffer[j].emplace_back();
1234 ZSfillEmpty(&buffer[j].back(), bcShiftInFirstHBF, rdh_utils::getFEEID(iSector * 10 + j / 2, j & 1, this->RAWLNK), orbitShift, this->RAWLNK);
1235 totalPages++;
1236 }
1237 }
1238 }
1239 return totalPages;
1240}
1241
1242template <class T>
1243size_t zsEncoderRun<T>::compare(std::vector<zsPage>* buffer, std::vector<o2::tpc::Digit>& tmpBuffer)
1244{
1245 size_t nErrors = 0;
1246 std::vector<o2::tpc::Digit> compareBuffer;
1247 compareBuffer.reserve(tmpBuffer.size());
1248 for (uint32_t j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) {
1249 uint32_t firstOrbit = ir ? ir->orbit : 0;
1250 for (uint32_t k = 0; k < buffer[j].size(); k++) {
1251 zsPage* decPage = &buffer[j][k];
1252 decodePage(compareBuffer, decPage, j, firstOrbit);
1253 }
1254 }
1255 if (tmpBuffer.size() != compareBuffer.size()) {
1256 nErrors += tmpBuffer.size();
1257 printf("Number of clusters mismatch %d %d\n", (int32_t)tmpBuffer.size(), (int32_t)compareBuffer.size());
1258 } else {
1259 for (uint32_t j = 0; j < tmpBuffer.size(); j++) {
1260 const float decodeBitsFactor = (1 << (encodeBits - 10));
1261 const float c = CAMath::Round(tmpBuffer[j].getChargeFloat() * decodeBitsFactor) / decodeBitsFactor;
1262 int32_t ok = c == compareBuffer[j].getChargeFloat() && (int32_t)tmpBuffer[j].getTimeStamp() == (int32_t)compareBuffer[j].getTimeStamp() && (int32_t)tmpBuffer[j].getPad() == (int32_t)compareBuffer[j].getPad() && (int32_t)tmpBuffer[j].getRow() == (int32_t)compareBuffer[j].getRow();
1263 if (ok) {
1264 continue;
1265 }
1266 nErrors++;
1267 printf("%4u: OK %d: Charge %3d %3d Time %4d %4d Pad %3d %3d Row %3d %3d\n", j, ok,
1268 (int32_t)c, (int32_t)compareBuffer[j].getChargeFloat(), (int32_t)tmpBuffer[j].getTimeStamp(), (int32_t)compareBuffer[j].getTimeStamp(), (int32_t)tmpBuffer[j].getPad(), (int32_t)compareBuffer[j].getPad(), (int32_t)tmpBuffer[j].getRow(), (int32_t)compareBuffer[j].getRow());
1269 }
1270 }
1271 return nErrors;
1272}
1273
1274} // anonymous namespace
1275} // namespace o2::gpu
1276#endif // !GPUCA_RUN2
1277
1278template <class S>
1279void GPUReconstructionConvert::RunZSEncoder(const S& in, std::unique_ptr<uint64_t[]>* outBuffer, uint32_t* outSizes, o2::raw::RawFileWriter* raw, const o2::InteractionRecord* ir, const GPUParam& param, int32_t version, bool verify, float threshold, bool padding, std::function<void(std::vector<o2::tpc::Digit>&)> digitsFilter)
1280{
1281 // Pass in either outBuffer / outSizes, to fill standalone output buffers, or raw to use RawFileWriter
1282 // ir is the interaction record for time bin 0
1283 if (((outBuffer == nullptr) ^ (outSizes == nullptr)) || ((raw != nullptr) && (ir == nullptr)) || !((outBuffer == nullptr) ^ (raw == nullptr)) || (raw && verify)) {
1284 throw std::runtime_error("Invalid parameters");
1285 }
1286#ifndef GPUCA_RUN2
1287 std::vector<zsPage> buffer[NSECTORS][GPUTrackingInOutZS::NENDPOINTS];
1288 struct tmpReductionResult {
1289 uint32_t totalPages = 0;
1290 size_t totalSize = 0;
1291 size_t nErrors = 0;
1292 size_t digitsInput = 0;
1293 size_t digitsEncoded = 0;
1294 };
1295 auto reduced = tbb::parallel_reduce(tbb::blocked_range<uint32_t>(0, NSECTORS), tmpReductionResult(), [&](const auto range, auto red) {
1296 for (uint32_t i = range.begin(); i < range.end(); i++) {
1297 std::vector<o2::tpc::Digit> tmpBuffer;
1298 red.digitsInput += ZSEncoderGetNDigits(in, i);
1299 tmpBuffer.resize(ZSEncoderGetNDigits(in, i));
1300 if (threshold > 0.f && !digitsFilter) {
1301 auto it = std::copy_if(ZSEncoderGetDigits(in, i), ZSEncoderGetDigits(in, i) + ZSEncoderGetNDigits(in, i), tmpBuffer.begin(), [threshold](auto& v) { return v.getChargeFloat() >= threshold; });
1302 tmpBuffer.resize(std::distance(tmpBuffer.begin(), it));
1303 } else {
1304 std::copy(ZSEncoderGetDigits(in, i), ZSEncoderGetDigits(in, i) + ZSEncoderGetNDigits(in, i), tmpBuffer.begin());
1305 }
1306
1307 if (digitsFilter) {
1308 digitsFilter(tmpBuffer);
1309 if (threshold > 0.f) {
1310 std::vector<o2::tpc::Digit> tmpBuffer2 = std::move(tmpBuffer);
1311 tmpBuffer = std::vector<o2::tpc::Digit>(tmpBuffer2.size());
1312 auto it = std::copy_if(tmpBuffer2.begin(), tmpBuffer2.end(), tmpBuffer.begin(), [threshold](auto& v) { return v.getChargeFloat() >= threshold; });
1313 tmpBuffer.resize(std::distance(tmpBuffer.begin(), it));
1314 }
1315 }
1316 red.digitsEncoded += tmpBuffer.size();
1317
1318 auto runZS = [&](auto& encoder) {
1319 encoder.zsVersion = version;
1320 encoder.init();
1321 red.totalPages += encoder.run(buffer[i], tmpBuffer, &red.totalSize);
1322 if (verify) {
1323 red.nErrors += encoder.compare(buffer[i], tmpBuffer); // Verification
1324 }
1325 };
1326
1327 if (version >= ZSVersion::ZSVersionRowBased10BitADC && version <= ZSVersion::ZSVersionRowBased12BitADC) {
1328 zsEncoderRun<zsEncoderRow> enc{{{.iSector = i, .raw = raw, .ir = ir, .param = &param, .padding = padding}}};
1329 runZS(enc);
1330 } else if (version >= ZSVersion::ZSVersionLinkBasedWithMeta && version <= ZSVersion::ZSVersionDenseLinkBasedV2) {
1331#ifndef GPUCA_STANDALONE
1332 if (version == ZSVersion::ZSVersionLinkBasedWithMeta) {
1333 zsEncoderRun<zsEncoderImprovedLinkBased> enc{{{{.iSector = i, .raw = raw, .ir = ir, .param = &param, .padding = padding}}}};
1334 runZS(enc);
1335 } else if (version >= ZSVersion::ZSVersionDenseLinkBased && version <= ZSVersion::ZSVersionDenseLinkBasedV2) {
1336 zsEncoderRun<zsEncoderDenseLinkBased> enc{{{{.iSector = i, .raw = raw, .ir = ir, .param = &param, .padding = padding}}}};
1337 runZS(enc);
1338 }
1339#else
1340 throw std::runtime_error("Link based ZS encoding not supported in standalone build");
1341#endif
1342 } else {
1343 throw std::runtime_error("Invalid ZS version "s + std::to_string(version) + ", cannot decode"s);
1344 }
1345 }
1346 return red; }, [&](const auto& red1, const auto& red2) {
1347 auto red = red1;
1348 red.totalPages += red2.totalPages;
1349 red.totalSize += red2.totalSize;
1350 red.nErrors += red2.nErrors;
1351 red.digitsInput += red2.digitsInput;
1352 red.digitsEncoded += red2.digitsEncoded;
1353 return red; });
1354
1355 if (outBuffer) {
1356 outBuffer->reset(new uint64_t[reduced.totalPages * TPCZSHDR::TPC_ZS_PAGE_SIZE / sizeof(uint64_t)]);
1357 uint64_t offset = 0;
1358 for (uint32_t i = 0; i < NSECTORS; i++) {
1359 for (uint32_t j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) {
1360 memcpy((char*)outBuffer->get() + offset, buffer[i][j].data(), buffer[i][j].size() * TPCZSHDR::TPC_ZS_PAGE_SIZE);
1361 offset += buffer[i][j].size() * TPCZSHDR::TPC_ZS_PAGE_SIZE;
1362 outSizes[i * GPUTrackingInOutZS::NENDPOINTS + j] = buffer[i][j].size();
1363 }
1364 }
1365 }
1366 if (reduced.nErrors) {
1367 GPUError("ERROR: %lu INCORRECT SAMPLES DURING ZS ENCODING VERIFICATION!!!", reduced.nErrors);
1368 } else if (verify) {
1369 GPUInfo("ENCODING VERIFICATION PASSED");
1370 }
1371 GPUInfo("TOTAL ENCODED SIZE: %lu (%lu of %lu digits encoded)", reduced.totalSize, reduced.digitsEncoded, reduced.digitsInput);
1372#endif
1373}
1374
1375template void GPUReconstructionConvert::RunZSEncoder<GPUTrackingInOutDigits>(const GPUTrackingInOutDigits&, std::unique_ptr<uint64_t[]>*, uint32_t*, o2::raw::RawFileWriter*, const o2::InteractionRecord*, const GPUParam&, int32_t, bool, float, bool, std::function<void(std::vector<o2::tpc::Digit>&)> digitsFilter);
1376#ifndef GPUCA_STANDALONE
1377template void GPUReconstructionConvert::RunZSEncoder<DigitArray>(const DigitArray&, std::unique_ptr<uint64_t[]>*, uint32_t*, o2::raw::RawFileWriter*, const o2::InteractionRecord*, const GPUParam&, int32_t, bool, float, bool, std::function<void(std::vector<o2::tpc::Digit>&)> digitsFilter);
1378#endif
1379
1380void GPUReconstructionConvert::RunZSEncoderCreateMeta(const uint64_t* buffer, const uint32_t* sizes, void** ptrs, GPUTrackingInOutZS* out)
1381{
1382 uint64_t offset = 0;
1383 for (uint32_t i = 0; i < NSECTORS; i++) {
1384 for (uint32_t j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) {
1385 ptrs[i * GPUTrackingInOutZS::NENDPOINTS + j] = (char*)buffer + offset;
1386 offset += sizes[i * GPUTrackingInOutZS::NENDPOINTS + j] * TPCZSHDR::TPC_ZS_PAGE_SIZE;
1387 out->sector[i].zsPtr[j] = &ptrs[i * GPUTrackingInOutZS::NENDPOINTS + j];
1388 out->sector[i].nZSPtr[j] = &sizes[i * GPUTrackingInOutZS::NENDPOINTS + j];
1389 out->sector[i].count[j] = 1;
1390 }
1391 }
1392}
1393
1394void GPUReconstructionConvert::RunZSFilter(std::unique_ptr<o2::tpc::Digit[]>* buffers, const o2::tpc::Digit* const* ptrs, size_t* nsb, const size_t* ns, const GPUParam& param, bool zs12bit, float threshold)
1395{
1396 for (uint32_t i = 0; i < NSECTORS; i++) {
1397 if (buffers[i].get() != ptrs[i] || nsb != ns) {
1398 throw std::runtime_error("Not owning digits");
1399 }
1400 uint32_t j = 0;
1401 const uint32_t decodeBits = zs12bit ? TPCZSHDR::TPC_ZS_NBITS_V2 : TPCZSHDR::TPC_ZS_NBITS_V1;
1402 const float decodeBitsFactor = (1 << (decodeBits - 10));
1403 for (uint32_t k = 0; k < ns[i]; k++) {
1404 if (buffers[i][k].getChargeFloat() >= threshold) {
1405 if (k > j) {
1406 buffers[i][j] = buffers[i][k];
1407 }
1408 if (zs12bit) {
1409 buffers[i][j].setCharge(CAMath::Round(buffers[i][j].getChargeFloat() * decodeBitsFactor) / decodeBitsFactor);
1410 } else {
1411 buffers[i][j].setCharge(CAMath::Round(buffers[i][j].getChargeFloat()));
1412 }
1413 j++;
1414 }
1415 }
1416 nsb[i] = j;
1417 }
1418}
1419
1420#ifndef GPUCA_STANDALONE
1421namespace o2::gpu::internal
1422{
1423template <class T>
1424static inline auto GetDecoder_internal(const GPUParam* param, int32_t version)
1425{
1426 std::shared_ptr<T> enc = std::make_shared<T>();
1427 if (param == nullptr) {
1428 static GPUParam dummyParam;
1429 param = &dummyParam;
1430 }
1431 enc->param = param;
1432 enc->zsVersion = version;
1433 enc->init();
1434 return [enc](std::vector<o2::tpc::Digit>& outBuffer, const void* page, uint32_t firstTfOrbit, uint32_t triggerBC = 0) {
1436 if (o2::raw::RDHUtils::getMemorySize(rdh) == sizeof(o2::header::RAWDataHeader)) {
1437 return;
1438 }
1439 if (o2::raw::RDHUtils::getDetectorField(rdh) != 2) {
1440 return;
1441 }
1442 o2::tpc::CRU cru(o2::tpc::rdh_utils::getCRU(rdh));
1443 enc->iSector = cru.sector();
1444 int32_t endpoint = cru.region() * 2 + o2::tpc::rdh_utils::getEndPoint(rdh);
1445 enc->decodePage(outBuffer, (const zsPage*)page, endpoint, firstTfOrbit, triggerBC);
1446 };
1447}
1448} // namespace o2::gpu::internal
1449
1450std::function<void(std::vector<o2::tpc::Digit>&, const void*, uint32_t, uint32_t)> GPUReconstructionConvert::GetDecoder(int32_t version, const GPUParam* param)
1451{
1453 return o2::gpu::internal::GetDecoder_internal<zsEncoderRow>(param, version);
1455 return o2::gpu::internal::GetDecoder_internal<zsEncoderImprovedLinkBased>(param, version);
1457 return o2::gpu::internal::GetDecoder_internal<zsEncoderDenseLinkBased>(param, version);
1458 } else {
1459 throw std::runtime_error("Invalid ZS version "s + std::to_string(version) + ", cannot create decoder"s);
1460 }
1461}
1462#endif
uint16_t padding
Definition of the TPC Digit.
std::ostringstream debug
uint64_t orbit
Definition RawEventData.h:6
uint64_t bc
Definition RawEventData.h:5
unsigned short int uint16_t
unsigned int uint32_t
int32_t i
uint8_t * nSeq
int32_t outputEndpoint
std::array< uint8_t, TPCZSHDR::TPC_ZS_PAGE_SIZE > streamBuffer8
int64_t nexthbf
uint32_t zsVersion
TPCZSTBHDR * curTBHdr
int32_t lastEndpoint
TPCZSHDR * hdr
uint32_t encodeBits
zsPage * page
uint32_t iSector
uint32_t streamSize8
float encodeBitsFactor
uint32_t streamSize
std::array< uint16_t, TPCZSHDR::TPC_ZS_PAGE_SIZE > streamBuffer
int32_t curRegion
std::bitset< 80 > bitmask
uint8_t * pagePtr
int32_t endpointStart
int32_t inverseChannelMapping[5][32]
int32_t lastTime
TPCZSHDRV2 hdrBuffer
uint32_t pageCounter
int32_t nSamples
int32_t lastRow
int32_t bcShiftInFirstHBF
uint16_t curTimeBin
std::vector< uint8_t > sequenceBuffer
int32_t nRowsInTB
std::vector< uint16_t > sequenceBufferADC
std::vector< uint16_t > adcValues
int32_t firstTimebinInPage
uint32_t packetCounter
o2::raw::RawFileWriter * raw
int32_t outputRegion
int32_t retVal
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual sector
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error s
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events Show standalone event Run event generator maximum time bin of continuous for triggered for automatic continuous for automatic continuous triggered uint64_t
configStandalone Reconstruction rec_tpc multiplier for the combined cluster track error during track following area in cm for the search of for z only used if decrease removal by when above this local occupancy rowx Shift of TPC revert cluster flag edge bit distance to edge exceeds this ZOffset for secondary multiple of radius max P of electron K BB bands crossing max P of electron p BB bands crossing max P of electron d BB bands crossing max P of electron t BB bands crossing min accepted dEdxMaxTPC of the track Additive extra cluster error for Y2 if edge flag set Additive extra cluster error for Y2 if single set Additive extra cluster error for Y2 if or single set Additive extra cluster error for Z2 if or single set Extra errors to add to track when crossing depending on addErrorsCECrossing Inner portion of y range in sector that is not used in searching for extrapolated track candidates factor2YS for track merging factor2K for track merging Margin in cm of Y position when rejecting edge clusters based on uncorrected track Y Max fraction of shared clusters for tracklet Except pad from cluster finding if total number of charges in a fragment is above this Except pad from cluster finding if number of consecutive charges in a fragment is above this Threshold where a timebin is considered disabling the noisy pad check for that pad Treat signal above this minimum as saturated Smoothing factor for the exponential Highly Ionising Particle tail filter Number of timebins to use for the averaging Add additional custom track errors when crossing relative as fraction of Do N fit passes in final fit of< 0:reject at max -n hits") AddOptionRTC(rejectIFCLowRadiusCluster, uint8_t, 1, "", 0, "Reject clusters that get the IFC mask error during refit") AddOptionRTC(dEdxTruncLow, uint8_t, 2, "", 0, "Low truncation threshold, fraction of 128") AddOptionRTC(dEdxTruncHigh, uint8_t, 77, "", 0, "High truncation threshold, fraction of 128") AddOptionRTC(extrapolationTracking, int8_t, 1, "", 0, "Enable Extrapolation Tracking(prolong tracks to adjacent sectors to find short segments)") AddOptionRTC(disableRefitAttachment, uint8_t, 0, "", 0, "Bitmask to disable certain attachment steps during refit(1:attachment, 2:propagation, 4:loop following, 8:mirroring)") AddOptionRTC(rejectionStrategy, uint8_t, o2::gpu::GPUSettings::RejectionStrategyA, "", 0, "Enable rejection of TPC clusters for compression(0=no, 1=strategy A, 2=strategy B)") AddOptionRTC(mergeLoopersAfterburner, uint8_t, 1, "", 0, "Run afterburner for additional looper merging") AddOptionRTC(compressionTypeMask, uint8_t, o2::gpu::GPUSettings::CompressionFull, "", 0, "TPC Compression mode bits(1=truncate charge/width LSB, 2=differences, 4=track-model)") AddOptionRTC(compressionSortOrder, uint8_t, o2::gpu::GPUSettings::SortTime, "", 0, "Sort order of TPC compression(0=time, 1=pad, 2=Z-time-pad, 3=Z-pad-time, 4=no sorting(use incoming order))") AddOptionRTC(sigBitsCharge, uint8_t, 4, "", 0, "Number of significant bits for TPC cluster charge in compression mode 1") AddOptionRTC(sigBitsWidth, uint8_t, 3, "", 0, "Number of significant bits for TPC cluster width in compression mode 1") AddOptionRTC(dropLoopers, uint8_t, 0, "", 0, "Drop looping tracks starting from second loop") AddOptionRTC(mergerInterpolateErrors, uint8_t, 1, "", 0, "Use interpolation instead of extrapolation for chi2 based cluster rejection") AddOptionRTC(mergerInterpolateRejectAlsoOnCurrentPosition, uint8_t, 1, "", 0, "When using mergerInterpolateErrors, reject based on chi2 twice computed with interpolated and current track position starting from NDF > mergerNonInterpolateRejectMinNDF Merge tracks accross the central electrode Enable PID response Add sub threshold clusters in TPC dEdx computation OR mask of TPC flags that will reject the cluster in alternative dEdx Reject edge clusters based on uncorrected track Y during track fit Increase decrease removal by when on this number of pads from the edge h configStandalone Reconstruction rec_trd Max chi2 for TRD tracklets to be matched to a track Chi2 cut for strict matching mode Number of sigmas for ITS TPC track time error estimate Addition to search road around track prolongation along Y in cm Optimal tracklet rphi resolution in maximum chi2 per attached tracklet for TRD tracks take into account the full likelihood of the slope including exponential tails Tracks with less attached tracklets are discarded after the tracking Post trigger Pile up integration time in BCs fits all iterations but the last one in projections Use material lookup table for TPC refit Apply workarounds to avoid cyclic merge should not be needed tpc dyn configStandalone Processing proc_rtc Replace constant variables static by constexpr expressions Compile RTC in deterministic with NO_FAST_MATH flags and GPUCA_DETERMINISTIC_MODE define Use RTC to optimize GPU code h configStandalone Processing proc_rtctech Use a file lock to serialize access to the cache folder int32_t
Header to collect LHC related constants.
uint32_t j
Definition RawData.h:0
uint8_t endpoint
Definition RawData.h:0
uint32_t c
Definition RawData.h:2
uint32_t version
Definition RawData.h:8
Utility class to write detectors data to (multiple) raw data file(s) respecting CRU format.
POD correction map.
TBranch * ptr
definitions to deal with the link based zero suppression format
Definitions of TPC Zero Suppression Data Headers.
static void RunZSEncoder(const S &in, std::unique_ptr< uint64_t[]> *outBuffer, uint32_t *outSizes, o2::raw::RawFileWriter *raw, const o2::InteractionRecord *ir, const GPUParam &param, int32_t version, bool verify, float threshold=0.f, bool padding=false, std::function< void(std::vector< o2::tpc::Digit > &)> digitsFilter=nullptr)
static std::function< void(std::vector< o2::tpc::Digit > &, const void *, uint32_t, uint32_t)> GetDecoder(int32_t version, const GPUParam *param)
static void RunZSFilter(std::unique_ptr< o2::tpc::Digit[]> *buffers, const o2::tpc::Digit *const *ptrs, size_t *nsb, const size_t *ns, const GPUParam &param, bool zs12bit, float threshold)
static int32_t GetMaxTimeBin(const o2::tpc::ClusterNativeAccess &native)
static void RunZSEncoderCreateMeta(const uint64_t *buffer, const uint32_t *sizes, void **ptrs, GPUTrackingInOutZS *out)
static void ConvertRun2RawToNative(o2::tpc::ClusterNativeAccess &native, std::unique_ptr< o2::tpc::ClusterNative[]> &nativeBuffer, const AliHLTTPCRawCluster **rawClusters, uint32_t *nRawClusters)
void addData(uint16_t feeid, uint16_t cru, uint8_t lnk, uint8_t endpoint, const IR &ir, const gsl::span< char > data, bool preformatted=false, uint32_t trigger=0, uint32_t detField=0)
unsigned char partition() const
Definition CRU.h:63
unsigned char getSampaChannel() const
Definition FECInfo.h:45
unsigned char getIndex() const
Definition FECInfo.h:41
unsigned char getSampaChip() const
Definition FECInfo.h:44
static Mapper & instance(const std::string mappingDir="")
Definition Mapper.h:44
static constexpr void getSampaAndChannelOnFEC(const int cruID, const size_t rawFECChannel, int &sampaOnFEC, int &channelOnSAMPA)
Definition Mapper.h:275
static constexpr int MAXSECTOR
Definition Sector.h:44
std::array< gsl::span< const o2::tpc::Digit >, Sector::MAXSECTOR > DigitArray
GLdouble n
Definition glcorearb.h:1982
const GLfloat * m
Definition glcorearb.h:4066
GLsizeiptr size
Definition glcorearb.h:659
const GLdouble * v
Definition glcorearb.h:832
GLuint GLsizei const GLuint const GLintptr const GLsizeiptr * sizes
Definition glcorearb.h:2595
GLsizei samples
Definition glcorearb.h:1309
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLenum GLint * range
Definition glcorearb.h:1899
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * bits
Definition glcorearb.h:4150
GLintptr offset
Definition glcorearb.h:660
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLenum GLfloat param
Definition glcorearb.h:271
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLint GLuint mask
Definition glcorearb.h:291
const GBTLinkAttributes links[kNGBTLinks]
Definition RawWriter.h:54
RAWDataHeaderV7 RAWDataHeader
void dumpBuffer(gsl::span< const std::byte > buffer, std::ostream &out=std::cout, size_t maxbytes=std::numeric_limits< size_t >::max())
Definition DumpBuffer.h:139
uint64_t getTimeStamp(o2::framework::ProcessingContext &pc)
uint16_t FEEIDType
Definition RDHUtils.h:26
Global TPC definitions and constants.
@ ZSVersionDenseLinkBased
@ ZSVersionLinkBasedWithMeta
@ ZSVersionDenseLinkBasedV2
@ ZSVersionRowBased10BitADC
@ ZSVersionRowBased12BitADC
unsigned short GlobalPadNumber
global pad number
Definition Defs.h:114
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
int32_t GetCharge() const
uint16_t GetFlags() const
int32_t GetPadRow() const
uint32_t orbit
LHC orbit.
uint16_t bc
bunch crossing ID of interaction
const o2::tpc::Digit * tpcDigits[NSECTORS]
GPUTrackingInOutZSSector sector[NSECTORS]
static void setLinkID(H &rdh, uint8_t v, NOTPTR(H))
Definition RDHUtils.h:258
static void setDetectorField(H &rdh, uint32_t v, NOTPTR(H))
Definition RDHUtils.h:584
static void setPageCounter(H &rdh, uint16_t v, NOTPTR(H))
Definition RDHUtils.h:559
static void setMemorySize(H &rdh, uint16_t v, NOTPTR(H))
Definition RDHUtils.h:242
static void setVersion(H &rdh, uint8_t v, NOTPTR(H))
Definition RDHUtils.h:90
static void setPacketCounter(H &rdh, uint8_t v, NOTPTR(H))
Definition RDHUtils.h:274
static void setHeartBeatOrbit(RDHv4 &rdh, uint32_t v)
Definition RDHUtils.h:364
static void setFEEID(RDHv4 &rdh, uint16_t v)
Definition RDHUtils.h:148
unsigned int nClusters[constants::MAXSECTOR][constants::MAXGLOBALPADROW]
const ClusterNative * clusters[constants::MAXSECTOR][constants::MAXGLOBALPADROW]
unsigned int clusterOffset[constants::MAXSECTOR][constants::MAXGLOBALPADROW]
const ClusterNative * clustersLinear
unsigned short nTimebinHeaders
unsigned char magicWord
unsigned short firstZSDataOffset
unsigned char nTimeBinSpan
unsigned char version
unsigned short timeOffset
unsigned short cruID
unsigned short nADCsamples
unsigned short rowMask
void compare(std::string_view s1, std::string_view s2)
o2::InteractionRecord ir(0, 0)
std::vector< Digit > digits
std::vector< int > row