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 <TRandom.h>
24#include <vector>
25#include <iostream>
26#include <numeric>
27#include <algorithm>
28#include <fairlogger/Logger.h>
29
30namespace o2::iotof
31{
32
33o2::iotof::Segmentation* Digitizer::sSegmentation = nullptr;
34//_______________________________________________________________________
36{
37 const int numberOfChips = mGeometry->getSize();
38 mChips.resize(numberOfChips);
39 for (int i = numberOfChips; i--;) {
40 mChips[i].setChipIndex(i);
45
51 }
52
53 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
54
55 LOG(info) << "Initializing IOTOF digitizer";
56 LOG(info) << " Time resolution: " << digitizerParams.timeResolution * 1e3 << " ps";
57 LOG(info) << " Charge threshold: " << digitizerParams.chargeThreshold << " electrons";
58 LOG(info) << " Detection efficiency: " << digitizerParams.efficiency * 100 << " %";
59 LOG(info) << " Continuous mode: " << (mContinuous ? "ON" : "OFF");
60 sSegmentation = o2::iotof::Segmentation::Instance();
61}
62
63//_______________________________________________________________________
64void Digitizer::process(const std::vector<o2::itsmft::Hit>* hits, int evID, int srcID)
65{
66 // Digitize hits from a single event
67 LOG(debug) << "Digitizing IOTOF hits: " << hits->size() << " hits from event " << evID << " source " << srcID;
68
69 if (!hits || hits->empty()) {
70 return;
71 }
72
73 // Sort hits by detector ID for better cache locality
74 std::vector<int> hitIdx(hits->size());
75 std::iota(hitIdx.begin(), hitIdx.end(), 0);
76 std::sort(hitIdx.begin(), hitIdx.end(),
77 [hits](int lhs, int rhs) {
78 return (*hits)[lhs].GetDetectorID() < (*hits)[rhs].GetDetectorID();
79 });
80
81 // Process each hit
82 for (int i : hitIdx) {
83 processHit((*hits)[i], evID, srcID);
84 }
85
86 // In triggered mode, flush output after each event
87 if (!mContinuous) {
88 LOG(debug) << "Inner flushing for non-continuous mode";
90 }
91}
92
93//_______________________________________________________________________
94void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID)
95{
96 // Process a single hit and create a digit if it passes all cuts
97
98 // Apply efficiency cut
99 if (!isEfficient()) {
100 LOG(debug) << "Hit rejected by efficiency cut";
101 return;
102 }
103
104 // Get detector element ID
105 const int chipID = hit.GetDetectorID();
106 auto& chip = mChips[chipID];
107 if (chip.isDisabled()) {
108 LOG(debug) << "Hit rejected because chip " << chipID << " is disabled";
109 return;
110 }
111
112 // Convert energy loss to charge (number of electrons)
113 float energyLoss = hit.GetEnergyLoss(); // in GeV
114 int charge = energyToCharge(energyLoss);
115 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
116 int electronsPerStep = static_cast<int>(charge / digitizerParams.nSimSteps);
117
118 // Apply charge threshold
119 if (charge < digitizerParams.chargeThreshold) {
120 LOG(debug) << "Hit rejected by charge threshold: " << charge << " < " << digitizerParams.chargeThreshold;
121 return;
122 }
123
124 // Get hit time and apply smearing
125 // Hit time is in seconds, convert to ns and add event time
126 double hitTime = hit.GetTime() * sec2ns; // convert to ns
127 double eventTimeNS = mEventTime.getTimeNS(); // event time since orbit 0
128 double absoluteTime = hitTime + eventTimeNS; // absolute time
129 double smearedTime = smearTime(absoluteTime); // apply detector resolution
130
131 if (chipID < 0 || chipID >= mGeometry->getSize() || mGeometry->getSize() < 1) {
132 LOG(debug) << "Invalid detector ID: " << chipID << ", geometry size: " << mGeometry->getSize();
133 return; // invalid detector ID
134 }
135
136 // Create the digit with time information
137 o2::MCCompLabel label(hit.GetTrackID(), evID, srcID, false);
138 const int roFrameAbs = 0; // For now, we can set this to 0 or calculate based on time if needed
139 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
140
141 float** respMatrix = nullptr;
142 int rowStart = 0, colStart = 0, rowSpan = 0, colSpan = 0;
143 stepping(hit, respMatrix, rowStart, colStart, rowSpan, colSpan);
144
145 for (int irow = rowSpan; irow--;) {
146 uint16_t rowIS = irow + rowStart;
147 for (int icol = colSpan; icol--;) {
148 uint16_t colIS = icol + colStart;
149 float nEleResp = respMatrix[irow][icol];
150 if (!nEleResp) {
151 continue;
152 }
153 const int nElectronsSampled = gRandom->Poisson(electronsPerStep * nEleResp);
154 // Noise can be added here if needed
155
156 registerDigits(chip, roFrameAbs, smearedTime, nROF,
157 static_cast<uint16_t>(rowIS), static_cast<uint16_t>(colIS), nElectronsSampled, label);
158 }
159 }
160
161 for (int irow = 0; irow < rowSpan; ++irow) {
162 delete[] respMatrix[irow];
163 }
164 delete[] respMatrix;
165}
166
167void Digitizer::stepping(const o2::itsmft::Hit& hit, float**& respMatrix, int& rowStart, int& colStart, int& rowSpan, int& colSpan)
168{
169 const auto& matrix = mGeometry->getMatrixL2G(hit.GetDetectorID());
170 const int chipID = hit.GetDetectorID();
171 const int subdetectorID = mGeometry->getIOTOFLayer(chipID);
172
173 auto xyzPositionStart(matrix ^ (hit.GetPosStart())); // start position in sensor frame
174 auto xyzPositionEnd(matrix ^ (hit.GetPos())); // end position in sensor frame
175
176 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
177 const auto stepVector = (xyzPositionEnd - xyzPositionStart) / digitizerParams.nSimSteps;
178 xyzPositionStart = xyzPositionStart + stepVector * 0.5f; // center the start position in the middle of the step
179 xyzPositionEnd = xyzPositionEnd - stepVector * 0.5f; // center the end position in the middle of the step
180
181 rowStart = -1;
182 colStart = -1;
183 int rowEnd = -1, colEnd = -1, nSkip = 0, nSteps = digitizerParams.nSimSteps;
184 while (!sSegmentation->localToDetector(xyzPositionStart.X(), xyzPositionStart.Z(), rowStart, colStart, mGeometry->getIOTOFLayer(chipID))) {
185 if (++nSkip > digitizerParams.nSimSteps) { // additional check to add: should we exclude something?
186 LOG(debug) << "Hit position out of bounds for detector ID " << chipID;
187 return; // hit is outside the active area
188 }
189 xyzPositionStart += stepVector;
190 }
191
192 while (!sSegmentation->localToDetector(xyzPositionEnd.X(), xyzPositionEnd.Z(), rowEnd, colEnd, mGeometry->getIOTOFLayer(chipID))) {
193 if (++nSkip > digitizerParams.nSimSteps) { // additional check to add: should we exclude something?
194 LOG(debug) << "Hit position out of bounds for detector ID " << chipID;
195 return; // hit is outside the active area
196 }
197 xyzPositionEnd += stepVector;
198 }
199
200 if (rowStart > rowEnd) {
201 std::swap(rowStart, rowEnd);
202 }
203 if (colStart > colEnd) {
204 std::swap(colStart, colEnd);
205 }
206
207 // Expand the range to take into account the effects of charge sharing
208 rowStart -= digitizerParams.responseMatrixSize / 2;
209 rowEnd += digitizerParams.responseMatrixSize / 2;
210 rowStart = std::max(rowStart, 0);
211 colStart = std::max(colStart, 0);
212
213 rowEnd = std::min(rowEnd, (subdetectorID == 0 ? sSegmentation->mITofSpecsConfig.NRows : sSegmentation->mOTofSpecsConfig.NRows) - 1);
214 colEnd = std::min(colEnd, (subdetectorID == 0 ? sSegmentation->mITofSpecsConfig.NCols : sSegmentation->mOTofSpecsConfig.NCols) - 1);
215 rowSpan = rowEnd - rowStart + 1;
216 colSpan = colEnd - colStart + 1;
217
218 respMatrix = new float*[rowSpan];
219 for (int i = 0; i < rowSpan; ++i) {
220 respMatrix[i] = new float[colSpan]();
221 }
222
223 int rowPrev = -1, colPrev = -1, row = 0, col = 0;
224 if (!respMatrix || rowSpan <= 0 || colSpan <= 0) {
225 return;
226 }
227 if (nSkip) {
228 nSteps -= nSkip;
229 }
230
231 auto& currentPosLocal = xyzPositionStart;
232 for (int iStep = nSteps; iStep--;) {
233 sSegmentation->localToDetector(currentPosLocal.X(), currentPosLocal.Z(), row, col, subdetectorID);
234 if (row != rowPrev || col != colPrev) {
235 rowPrev = row;
236 colPrev = col;
237 }
238
239 currentPosLocal += stepVector; // Move to the next step position
240
241 for (int irow = digitizerParams.responseMatrixSize; irow--;) {
242 int rowDest = row + irow - (digitizerParams.responseMatrixSize / 2) - rowStart; // destination row in the respMatrix
243 if (rowDest < 0 || rowDest >= rowSpan) {
244 continue;
245 }
246 for (int icol = digitizerParams.responseMatrixSize; icol--;) {
247 int colDest = col + icol - (digitizerParams.responseMatrixSize / 2) - colStart; // destination column in the respMatrix
248 if (colDest < 0 || colDest >= colSpan) {
249 continue;
250 }
251 respMatrix[rowDest][colDest] += 1.;
252 }
253 }
254 }
255}
256
257//_______________________________________________________________________
258double Digitizer::smearTime(double time) const
259{
260 // Apply Gaussian smearing to simulate detector time resolution
261 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
262 if (digitizerParams.timeResolution > 0) {
263 return time + gRandom->Gaus(0, digitizerParams.timeResolution);
264 }
265 return time;
266}
267
268//_______________________________________________________________________
269int Digitizer::energyToCharge(float energyLoss) const
270{
271 // Convert energy loss (GeV) to number of electrons
272 // Typical value: 3.6 eV per electron-hole pair in silicon
273 // energyLoss is in GeV, energyToNElectrons is electrons per GeV
274 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
275 return static_cast<int>(energyLoss * digitizerParams.energyToNElectrons);
276}
277
278//_______________________________________________________________________
279bool Digitizer::isEfficient() const
280{
281 // Apply efficiency cut using random number
282 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
283 return gRandom->Uniform() < digitizerParams.efficiency;
284}
285
286//_______________________________________________________________________
288{
289 LOG(info) << "Filling output container with digits from chips";
290 LOG(debug) << "Number of chips: " << mChips.size();
291
292 const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
293
295 rof.setFirstEntry(mDigits->size()); // index of the first digit
296
297 const auto* extraLabelBuffer = mExtraLabelBuffer.empty() ? nullptr : mExtraLabelBuffer.front().get();
298 for (auto& chip : mChips) {
299
300 if (chip.isDisabled()) {
301 continue;
302 }
303
305
306 if (chip.isEmpty()) {
307 continue;
308 }
309
310 auto& chipDigits = chip.getDigits();
311 for (const auto& [key, digit] : chipDigits) {
312
313 if (digit.getCharge() < digitizerParams.chargeThreshold) {
314 continue; // skip digits below threshold
315 }
316
317 int digitID = mDigits->size();
318 mDigits->emplace_back(digit.getChipIndex(), digit.getRow(), digit.getColumn(), digit.getCharge(), digit.getTime());
319 if (mMCLabels) {
320 mMCLabels->addElement(digitID, digit.getLabel().mLabel);
321 }
322 auto labelRef = digit.getLabel();
323
324 while (mMCLabels && extraLabelBuffer != nullptr && labelRef.mNext >= 0) {
325 labelRef = (*extraLabelBuffer)[labelRef.mNext];
326 mMCLabels->addElement(digitID, labelRef.mLabel);
327 }
328 }
329 chipDigits.clear(); // clear chip digits after copying to output
330 }
331
332 rof.setNEntries(mDigits->size() - rof.getFirstEntry()); // number of digits
333 rof.setBCData(mContinuous ? mROFRecordIR : mEventTime);
334 mROFRecords->push_back(rof);
335 LOG(debug) << "Created ROF record with " << mDigits->size() << " digits";
336
337 // extraLabelBuffer.clear(); // clear buffer for extra labels
338 // mExtraLabelBuffer.emplace_back(mExtraLabelBuffer.front().release()); // move current buffer to the end
339 // mExtraLabelBuffer.pop_front();
340}
341
342void Digitizer::registerDigits(Chip& chip, uint32_t roFrame, double time, int nROF,
343 uint16_t row, uint16_t col, int nElectrons, o2::MCCompLabel& label)
344{
345 (void)nROF;
346
348 o2::iotof::LabeledDigit* existingDigit = chip.findDigit(key);
349 if (!existingDigit) {
350 // No existing digit, create a new one
351 chip.addDigit(row, col, nElectrons, time, label);
352 } else {
353 // Digit already exists, update charge and labels
354 const int storedCharge = existingDigit->getCharge();
355 existingDigit->setCharge(storedCharge + nElectrons);
356 existingDigit->setTime(std::min(existingDigit->getTime(), time));
357 if (existingDigit->getLabel().mLabel == label) {
358 return; // don't store the same label twice
359 }
360 std::vector<o2::iotof::McLabelRef>* extra = getExtraLabelBuffer(roFrame);
361 auto labelRef = existingDigit->getLabel();
362 const auto next = static_cast<int>(extra->size());
363 extra->emplace_back(label, labelRef.mNext);
364 labelRef.mNext = next;
365 existingDigit->setLabel(labelRef);
366 }
367}
368
369} // 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, o2::MCCompLabel label)
Definition Chip.cxx:35
Int_t getChipIndex() const
Definition Chip.h:67
double getTime() const
Definition Digit.h:38
void setTime(double time)
Definition Digit.h:35
static UInt_t getOrderingKey(UShort_t chipindex, UShort_t row, UShort_t col)
Definition Digit.h:40
void init()
Initialize the digitizer.
Definition Digitizer.cxx:35
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:64
int getIOTOFLayer(int index) const
McLabelRef getLabel() const
Definition Digit.h:67
void setLabel(McLabelRef label)
Definition Digit.h:66
ChipSpecifics mITofSpecsConfig
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
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
double getTimeNS() const
get time in ns from orbit=0/bc=0
int mNext
eventual next contribution to the same pixel
Definition Digit.h:53
o2::MCCompLabel mLabel
hit label
Definition Digit.h:52
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< int > row