Project
Loading...
Searching...
No Matches
Digitizer.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
18
22
23#include <TCollection.h>
24#include <TFile.h>
25#include <TKey.h>
26#include <TRandom.h>
27
28#include <vector>
29#include <iostream>
30#include <numeric>
31#include <algorithm>
32#include <fairlogger/Logger.h>
33
34namespace o2::iotof
35{
36
37o2::iotof::Segmentation* Digitizer::sSegmentation = nullptr;
38//_______________________________________________________________________
40{
41 const int numberOfChips = mGeometry->getSize();
42 mChips.resize(numberOfChips);
43 for (int i = numberOfChips; i--;) {
44 mChips[i].setChipIndex(i);
49
55 }
56
57 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
58 if (!digitizerParams.efficiencyFilePath.empty()) {
59 loadEfficiencyMap(digitizerParams.efficiencyFilePath);
60 }
61
62 LOG(info) << "Initializing IOTOF digitizer";
63 LOG(info) << " Time resolution: " << digitizerParams.timeResolution * 1e3 << " ps";
64 LOG(info) << " Charge threshold: " << digitizerParams.chargeThreshold << " electrons";
65 LOG(info) << " Detection efficiency: " << digitizerParams.efficiency * 100 << " %";
66 LOG(info) << " Continuous mode: " << (mContinuous ? "ON" : "OFF");
67 sSegmentation = o2::iotof::Segmentation::Instance();
68}
69
70//_______________________________________________________________________
71void Digitizer::process(const std::vector<o2::itsmft::Hit>* hits, int evID, int srcID)
72{
73 // Digitize hits from a single event
74 LOG(debug) << "Digitizing IOTOF hits: " << hits->size() << " hits from event " << evID << " source " << srcID;
75
76 if (!hits || hits->empty()) {
77 return;
78 }
79
80 // Sort hits by detector ID for better cache locality
81 std::vector<int> hitIdx(hits->size());
82 std::iota(hitIdx.begin(), hitIdx.end(), 0);
83 std::sort(hitIdx.begin(), hitIdx.end(),
84 [hits](int lhs, int rhs) {
85 return (*hits)[lhs].GetDetectorID() < (*hits)[rhs].GetDetectorID();
86 });
87
88 // Process each hit
89 for (int i : hitIdx) {
90 processHit((*hits)[i], evID, srcID);
91 }
92
93 // In triggered mode, flush output after each event
94 if (!mContinuous) {
95 LOG(debug) << "Inner flushing for non-continuous mode";
97 }
98}
99
100//_______________________________________________________________________
101void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID)
102{
103 // Process a single hit and create a digit if it passes all cuts
104
105 // Get detector element ID
106 const int chipID = hit.GetDetectorID();
107 if (chipID < 0 || chipID >= mGeometry->getSize() || mGeometry->getSize() < 1) {
108 LOG(debug) << "Invalid detector ID: " << chipID << ", geometry size: " << mGeometry->getSize();
109 return; // invalid detector ID
110 }
111 const int subdetectorID = mGeometry->getIOTOFLayer(chipID);
112
113 auto& chip = mChips[chipID];
114 if (chip.isDisabled()) {
115 LOG(debug) << "Hit rejected because chip " << chipID << " is disabled";
116 return;
117 }
118
119 // Convert energy loss to charge (number of electrons)
120 float energyLoss = hit.GetEnergyLoss(); // in GeV
121 int charge = energyToCharge(energyLoss);
122 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
123 int electronsPerStep = static_cast<int>(charge / digitizerParams.nSimSteps);
124
125 // Apply charge threshold
126 if (charge < digitizerParams.chargeThreshold) {
127 LOG(debug) << "Hit rejected by charge threshold: " << charge << " < " << digitizerParams.chargeThreshold;
128 return;
129 }
130
131 // Get hit time and apply smearing
132 // Hit time is in seconds, convert to ns and add event time
133 double hitTime = hit.GetTime() * sec2ns; // convert to ns
134 double eventTimeInBC = mEventTime.getTimeOffsetWrtBC(); // event time wrt bc
135 double hitTimeWrtBC = hitTime + eventTimeInBC; // hit time wrt bc
136 double smearedTime = smearTime(hitTimeWrtBC);
137
138 // Create the digit with time information
139 o2::MCCompLabel label(hit.GetTrackID(), evID, srcID, false);
140 const int roFrameAbs = 0; // For now, we can set this to 0 or calculate based on time if needed
141 const int nROF = 1; // For now, we can assume the signal is contained in one ROF, this can be extended to multiple ROFs based on the time
142
143 float** respMatrix = nullptr;
144 float** avgHitLocalX = nullptr;
145 float** avgHitLocalZ = nullptr;
146 int rowStart = 0, colStart = 0, rowSpan = 0, colSpan = 0;
147 stepping(hit, respMatrix, avgHitLocalX, avgHitLocalZ, rowStart, colStart, rowSpan, colSpan);
148
149 float xPixelCenter = 0.0f, zPixelCenter = 0.0f;
150 for (int irow = rowSpan; irow--;) {
151 uint16_t rowIS = irow + rowStart;
152 for (int icol = colSpan; icol--;) {
153 uint16_t colIS = icol + colStart;
154 float nEleResp = respMatrix[irow][icol];
155 if (!nEleResp) {
156 continue;
157 }
158
159 // Apply efficiency cut based on the hit segment mean position relative to the pixel center
160 sSegmentation->detectorToLocal(rowIS, colIS, xPixelCenter, zPixelCenter, subdetectorID);
161 if (!isEfficient(avgHitLocalX[irow][icol] - xPixelCenter, avgHitLocalZ[irow][icol] - zPixelCenter)) {
162 LOG(debug) << "Hit rejected by efficiency cut at pixel (" << rowIS << ", " << colIS << ") in chip " << chipID;
163 continue;
164 }
165
166 const int nElectronsSampled = gRandom->Poisson(electronsPerStep * nEleResp);
167 // Noise can be added here if needed
168
169 registerDigits(chip, roFrameAbs, smearedTime, nROF,
170 static_cast<uint16_t>(rowIS), static_cast<uint16_t>(colIS), nElectronsSampled, label);
171 }
172 }
173
174 for (int irow = 0; irow < rowSpan; ++irow) {
175 delete[] respMatrix[irow];
176 delete[] avgHitLocalX[irow];
177 delete[] avgHitLocalZ[irow];
178 }
179 delete[] respMatrix;
180 delete[] avgHitLocalX;
181 delete[] avgHitLocalZ;
182}
183
184void Digitizer::stepping(const o2::itsmft::Hit& hit, float**& respMatrix, float**& avgHitLocalX, float**& avgHitLocalZ, int& rowStart, int& colStart, int& rowSpan, int& colSpan)
185{
186 LOG(debug) << "\n\nPerforming stepping";
187 const int chipID = hit.GetDetectorID();
188 const auto& matrix = mGeometry->getMatrixL2G(chipID);
189 const int subdetectorID = mGeometry->getIOTOFLayer(chipID);
190
191 auto xyzPositionStart(matrix ^ (hit.GetPosStart())); // start position in sensor frame
192 auto xyzPositionEnd(matrix ^ (hit.GetPos())); // end position in sensor frame
193
194 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
195 const auto stepVector = (xyzPositionEnd - xyzPositionStart) / digitizerParams.nSimSteps;
196 xyzPositionStart = xyzPositionStart + stepVector * 0.5f; // center the start position in the middle of the step
197 xyzPositionEnd = xyzPositionEnd - stepVector * 0.5f; // center the end position in the middle of the step
198
199 rowStart = -1;
200 colStart = -1;
201 int rowEnd = -1, colEnd = -1, nSkip = 0, nSteps = digitizerParams.nSimSteps;
202 while (!sSegmentation->localToDetector(xyzPositionStart.X(), xyzPositionStart.Z(), rowStart, colStart, mGeometry->getIOTOFLayer(chipID))) {
203 if (++nSkip > digitizerParams.nSimSteps) { // additional check to add: should we exclude something?
204 LOG(debug) << "Hit position out of bounds for detector ID " << chipID;
205 return; // hit is outside the active area
206 }
207 xyzPositionStart += stepVector;
208 }
209
210 while (!sSegmentation->localToDetector(xyzPositionEnd.X(), xyzPositionEnd.Z(), rowEnd, colEnd, mGeometry->getIOTOFLayer(chipID))) {
211 if (++nSkip > digitizerParams.nSimSteps) { // additional check to add: should we exclude something?
212 LOG(debug) << "Hit position out of bounds for detector ID " << chipID;
213 return; // hit is outside the active area
214 }
215 xyzPositionEnd -= stepVector;
216 }
217
218 if (rowStart > rowEnd) {
219 std::swap(rowStart, rowEnd);
220 }
221 if (colStart > colEnd) {
222 std::swap(colStart, colEnd);
223 }
224
225 // Expand the range to take into account the effects of charge sharing
226 rowStart -= digitizerParams.responseMatrixSize / 2;
227 rowEnd += digitizerParams.responseMatrixSize / 2;
228 rowStart = std::max(rowStart, 0);
229 colStart = std::max(colStart, 0);
230
231 const auto& specsConfig = ChipSpecificsParam::Instance();
232 rowEnd = std::min(rowEnd, (specsConfig.NRows) - 1);
233 colEnd = std::min(colEnd, (specsConfig.NCols) - 1);
234 rowSpan = rowEnd - rowStart + 1;
235 colSpan = colEnd - colStart + 1;
236
237 respMatrix = new float*[rowSpan];
238 avgHitLocalX = new float*[rowSpan];
239 avgHitLocalZ = new float*[rowSpan];
240 for (int i = 0; i < rowSpan; ++i) {
241 respMatrix[i] = new float[colSpan]();
242 avgHitLocalX[i] = new float[colSpan]();
243 avgHitLocalZ[i] = new float[colSpan]();
244 }
245
246 if (!respMatrix || !avgHitLocalX || !avgHitLocalZ || rowSpan <= 0 || colSpan <= 0) {
247 return;
248 }
249 if (nSkip) {
250 nSteps -= nSkip;
251 }
252
253 int rowPrev = -1, colPrev = -1, row = 0, col = 0;
254 auto pixelCurrentPosLocal = xyzPositionStart;
255 auto pixelStartPosLocal = xyzPositionStart;
256 for (int iStep = nSteps; iStep--;) {
257
258 // Step does not contribute if it is in the passive area
259 if (!sSegmentation->localToDetector(pixelCurrentPosLocal.X(), pixelCurrentPosLocal.Z(), row, col, subdetectorID)) {
260 LOG(debug) << "Step is in passive area: (" << pixelCurrentPosLocal.X() << ", " << pixelCurrentPosLocal.Z() << ") is outside the active area of chip " << subdetectorID;
261 pixelCurrentPosLocal += stepVector;
262 continue;
263 }
264
265 // The step has reached another pixel, compute mean hit segment positions
266 // for pixel efficiency evaluation and reset the start position for the next pixel
267 if (row != rowPrev || col != colPrev) {
268
269 // Finalize the previous pixel
270 if (rowPrev != -1 && colPrev != -1) {
271 const int irow = rowPrev - rowStart;
272 const int icol = colPrev - colStart;
273 avgHitLocalX[irow][icol] = 0.5f * (pixelStartPosLocal.X() + pixelCurrentPosLocal.X() - stepVector.X());
274 avgHitLocalZ[irow][icol] = 0.5f * (pixelStartPosLocal.Z() + pixelCurrentPosLocal.Z() - stepVector.Z());
275 }
276
277 // Start the new pixel
278 rowPrev = row;
279 colPrev = col;
280 pixelStartPosLocal = pixelCurrentPosLocal;
281 }
282
283 pixelCurrentPosLocal += stepVector; // Move to the next step position
284
285 for (int irow = digitizerParams.responseMatrixSize; irow--;) {
286 int rowDest = row + irow - (digitizerParams.responseMatrixSize / 2) - rowStart; // destination row in the respMatrix
287 if (rowDest < 0 || rowDest >= rowSpan) {
288 continue;
289 }
290 for (int icol = digitizerParams.responseMatrixSize; icol--;) {
291 int colDest = col + icol - (digitizerParams.responseMatrixSize / 2) - colStart; // destination column in the respMatrix
292 if (colDest < 0 || colDest >= colSpan) {
293 continue;
294 }
295 respMatrix[rowDest][colDest] += 1.;
296 }
297 }
298 }
299
300 // Finalize the last pixel
301 if (rowPrev != -1 && colPrev != -1) {
302 const int irow = rowPrev - rowStart;
303 const int icol = colPrev - colStart;
304 avgHitLocalX[irow][icol] = 0.5f * (pixelStartPosLocal.X() + pixelCurrentPosLocal.X() - stepVector.X());
305 avgHitLocalZ[irow][icol] = 0.5f * (pixelStartPosLocal.Z() + pixelCurrentPosLocal.Z() - stepVector.Z());
306 }
307}
308
309//_______________________________________________________________________
310double Digitizer::smearTime(double time) const
311{
312 // Apply Gaussian smearing to simulate detector time resolution
313 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
314 if (digitizerParams.timeResolution > 0) {
315 return time + gRandom->Gaus(0, digitizerParams.timeResolution);
316 }
317 return time;
318}
319
320//_______________________________________________________________________
321int Digitizer::energyToCharge(float energyLoss) const
322{
323 // Convert energy loss (GeV) to number of electrons
324 // Typical value: 3.6 eV per electron-hole pair in silicon
325 // energyLoss is in GeV, energyToNElectrons is electrons per GeV
326 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
327 return static_cast<int>(energyLoss * digitizerParams.energyToNElectrons);
328}
329
330//_______________________________________________________________________
331void Digitizer::loadEfficiencyMap(const std::string& filePath)
332{
333 // Load the efficiency map from a file
334 TFile* file = TFile::Open(filePath.c_str());
335 if (!file || !file->IsOpen()) {
336 LOG(error) << "Failed to open efficiency map file: " << filePath;
337 return;
338 }
339
340 auto* rawMap = dynamic_cast<TH2D*>(file->Get("hEfficiencyMap"));
341 if (!rawMap) {
342 LOG(error) << "Failed to retrieve efficiency map from file: " << filePath;
343 LOG(error) << "Available keys in the file:";
344 TIter next(file->GetListOfKeys());
345 TKey* key;
346 while ((key = dynamic_cast<TKey*>(next()))) {
347 LOG(error) << " " << key->GetName() << " (" << key->GetClassName() << ")";
348 }
349 file->Close();
350 return;
351 }
352 mEfficiencyMap = dynamic_cast<TH2D*>(rawMap->Clone("mEfficiencyMap"));
353 mEfficiencyMap->SetDirectory(nullptr); // Detach from file to avoid deletion when file is closed
354
355 file->Close();
356}
357
358//_______________________________________________________________________
359bool Digitizer::isEfficient(const float x, const float z) const
360{
361 // Apply efficiency cut using random number
362 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
363 if (mEfficiencyMap) {
364 // int bin = mEfficiencyMap->FindBin(x * o2::iotof::Digitizer::cm2um, z * o2::iotof::Digitizer::cm2um);
365 int bin = mEfficiencyMap->FindBin(x * o2::iotof::Digitizer::cm2um, z * o2::iotof::Digitizer::cm2um);
366 float efficiency = mEfficiencyMap->GetBinContent(bin);
367 LOG(debug) << "Efficiency map check: x=" << x * o2::iotof::Digitizer::cm2um << ", z=" << z * o2::iotof::Digitizer::cm2um << ", bin=" << bin << ", efficiency=" << efficiency;
368 return gRandom->Uniform() < efficiency;
369 }
370 return gRandom->Uniform() < digitizerParams.efficiency;
371}
372
373//_______________________________________________________________________
375{
376 LOG(info) << "Filling output container with digits from chips";
377 LOG(debug) << "Number of chips: " << mChips.size();
378
379 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
380
382 rof.setFirstEntry(mDigits->size()); // index of the first digit
383
384 const auto* extraLabelBuffer = mExtraLabelBuffer.empty() ? nullptr : mExtraLabelBuffer.front().get();
385 for (auto& chip : mChips) {
386
387 if (chip.isDisabled()) {
388 continue;
389 }
390
392
393 if (chip.isEmpty()) {
394 continue;
395 }
396
397 auto& chipDigits = chip.getDigits();
398 for (const auto& [key, digit] : chipDigits) {
399
400 if (digit.getCharge() < digitizerParams.chargeThreshold) {
401 continue; // skip digits below threshold
402 }
403
404 int digitID = mDigits->size();
405 mDigits->emplace_back(digit.getChipIndex(), digit.getRow(), digit.getColumn(), digit.getCharge(), digit.getTime(), digit.getBc(), digit.getTdc());
406 if (mMCLabels) {
407 mMCLabels->addElement(digitID, digit.getLabel().mLabel);
408 }
409 auto labelRef = digit.getLabel();
410
411 while (mMCLabels && extraLabelBuffer != nullptr && labelRef.mNext >= 0) {
412 labelRef = (*extraLabelBuffer)[labelRef.mNext];
413 mMCLabels->addElement(digitID, labelRef.mLabel);
414 }
415 }
416 chipDigits.clear(); // clear chip digits after copying to output
417 }
418
419 rof.setNEntries(mDigits->size() - rof.getFirstEntry()); // number of digits
420 rof.setBCData(mContinuous ? mROFRecordIR : mEventTime);
421 mROFRecords->push_back(rof);
422 LOG(debug) << "Created ROF record with " << mDigits->size() << " digits";
423
424 // extraLabelBuffer.clear(); // clear buffer for extra labels
425 // mExtraLabelBuffer.emplace_back(mExtraLabelBuffer.front().release()); // move current buffer to the end
426 // mExtraLabelBuffer.pop_front();
427}
428
429void Digitizer::registerDigits(Chip& chip, uint32_t roFrame, double time, int nROF,
430 uint16_t row, uint16_t col, int nElectrons, o2::MCCompLabel& label)
431{
432 (void)nROF;
433
434 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
435
436 uint64_t nbc = static_cast<uint64_t>(time / o2::constants::lhc::LHCBunchSpacingNS);
437 int tdc = int((time - nbc * o2::constants::lhc::LHCBunchSpacingNS) / digitizerParams.tdcBin);
438 nbc += mEventTime.toLong();
439
440 LOG(debug) << "nbc: " << nbc << "\ttdc: " << tdc;
441 double absoluteTime = tdc * digitizerParams.tdcBin * 1.e-9 + nbc * o2::constants::lhc::LHCBunchSpacingNS;
442
444 o2::iotof::LabeledDigit* existingDigit = chip.findDigit(key);
445 if (!existingDigit) {
446 // No existing digit, create a new one
447 chip.addDigit(row, col, nElectrons, absoluteTime, nbc, tdc, label);
448 } else {
449 // Digit already exists, update charge and labels
450 const int storedCharge = existingDigit->getCharge();
451 existingDigit->setCharge(storedCharge + nElectrons);
452 existingDigit->setTime(std::min(existingDigit->getTime(), time));
453 if (existingDigit->getLabel().mLabel == label) {
454 return; // don't store the same label twice
455 }
456 std::vector<o2::iotof::McLabelRef>* extra = getExtraLabelBuffer(roFrame);
457 auto labelRef = existingDigit->getLabel();
458 const auto next = static_cast<int>(extra->size());
459 extra->emplace_back(label, labelRef.mNext);
460 labelRef.mNext = next;
461 existingDigit->setLabel(labelRef);
462 }
463}
464
465} // namespace o2::iotof
std::ostringstream debug
int16_t charge
Definition RawEventData.h:5
int16_t time
Definition RawEventData.h:4
int32_t i
uint32_t col
Definition RawData.h:4
Definition of the ALICE3 TOF digitizer.
StringRef key
int GetTrackID() const
Definition BaseHits.h:30
V GetEnergyLoss() const
Definition BaseHits.h:103
math_utils::Point3D< T > GetPos() const
Definition BaseHits.h:67
E GetTime() const
Definition BaseHits.h:71
unsigned short GetDetectorID() const
Definition BaseHits.h:73
void addElement(uint32_t dataindex, TruthElement const &element, bool noElement=false)
const Mat3D & getMatrixL2G(int sensID) const
Container for similated points connected to a given TOF Chip This will be used in order to allow a mo...
Definition Chip.h:41
o2::iotof::LabeledDigit * findDigit(ULong64_t key)
reset points container
Definition Chip.h:89
void addDigit(UShort_t row, UShort_t col, Int_t charge, double time, ULong64_t bc, Int_t tdc, o2::MCCompLabel label)
Definition Chip.cxx:35
double getTime() const
Definition Digit.h:39
static ULong64_t getOrderingKey(ULong64_t bc, UShort_t row, UShort_t col)
Definition Digit.h:43
void setTime(double time)
Definition Digit.h:36
void init()
Initialize the digitizer.
Definition Digitizer.cxx:39
void fillOutputContainer()
Flush the output container.
void process(const std::vector< o2::itsmft::Hit > *hits, int evID, int srcID)
Steer conversion of hits to digits.
Definition Digitizer.cxx:71
int getIOTOFLayer(int index) const
McLabelRef getLabel() const
Definition Digit.h:74
void setLabel(McLabelRef label)
Definition Digit.h:73
bool detectorToLocal(L row, L col, T &xRow, T &zCol, const int subDetectorID)
bool localToDetector(float x, float z, int &iRow, int &iCol, const int subDetectorID)
void setCharge(Int_t charge)
Set the charge of the digit.
Definition Digit.h:62
Int_t getCharge() const
Get the accumulated charged of the digit.
Definition Digit.h:49
math_utils::Point3D< Float_t > GetPosStart() const
Definition Hit.h:60
void setNEntries(int n)
Definition ROFRecord.h:48
void setBCData(const BCData &bc)
Definition ROFRecord.h:44
void setFirstEntry(int idx)
Definition ROFRecord.h:47
int getFirstEntry() const
Definition ROFRecord.h:63
GLint GLenum GLint x
Definition glcorearb.h:403
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
constexpr double LHCBunchSpacingNS
int32_t const char * file
int mNext
eventual next contribution to the same pixel
Definition Digit.h:60
o2::MCCompLabel mLabel
hit label
Definition Digit.h:59
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< int > row