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
14
17#include "MathUtils/Cartesian.h"
20#include "ITS3Base/SpecsV2.h"
21#include "Framework/Logger.h"
22
23#include <TRandom.h>
24#include <vector>
25#include <numeric>
26
27using o2::itsmft::Hit;
32
33using namespace o2::its3;
34
36{
37 const int numOfChips = mGeometry->getNumberOfChips();
38 mChips.resize(numOfChips);
39 for (int i = numOfChips; i--;) {
40 mChips[i].setChipIndex(i);
41 if (mDeadChanMap != nullptr) {
42 mChips[i].disable(mDeadChanMap->isFullChipMasked(i));
43 mChips[i].setDeadChanMap(mDeadChanMap);
44 }
45 }
46
47 if (mParams.getAlpSimResponse() == nullptr) {
48 std::string responseFile = "$(O2_ROOT)/share/Detectors/ITSMFT/data/AlpideResponseData/AlpideResponseData.root";
49 LOGP(info, "Loading AlpideSimRespnse from file: {}", responseFile);
50 auto file = TFile::Open(responseFile.data());
51 mAlpSimResp = (o2::itsmft::AlpideSimResponse*)file->Get("response0"); // We use by default the alpide response for Vbb=0V
52 mParams.setAlpSimResponse(mAlpSimResp);
53 }
54 mParams.print();
56}
57
58void Digitizer::process(const std::vector<itsmft::Hit>* hits, int evID, int srcID)
59{
60 // digitize single event, the time must have been set beforehand
61
62 LOG(info) << "Digitizing " << mGeometry->getName() << " hits of entry " << evID << " from source "
63 << srcID << " at time " << mEventTime << " ROFrame = " << mNewROFrame << ")"
64 << " cont.mode: " << isContinuous()
65 << " Min/Max ROFrames " << mROFrameMin << "/" << mROFrameMax;
66
67 // is there something to flush ?
68 if (mNewROFrame > mROFrameMin) {
69 fillOutputContainer(mNewROFrame - 1); // flush out all frame preceding the new one
70 }
71
72 int nHits = hits->size();
73 std::vector<int> hitIdx(nHits);
74 std::iota(std::begin(hitIdx), std::end(hitIdx), 0);
75 // sort hits to improve memory access
76 std::sort(hitIdx.begin(), hitIdx.end(),
77 [hits](auto lhs, auto rhs) {
78 return (*hits)[lhs].GetDetectorID() < (*hits)[rhs].GetDetectorID();
79 });
80 for (int i : hitIdx) {
81 processHit((*hits)[i], mROFrameMax, evID, srcID);
82 }
83 // in the triggered mode store digits after every MC event
84 // TODO: in the real triggered mode this will not be needed, this is actually for the
85 // single event processing only
86 if (!mParams.isContinuous()) {
87 fillOutputContainer(mROFrameMax);
88 }
89}
90
92{
93 // assign event time in ns
94 mEventTime = irt;
95 if (!mParams.isContinuous()) {
96 mROFrameMin = 0; // in triggered mode reset the frame counters
97 mROFrameMax = 0;
98 }
99 // RO frame corresponding to provided time
100 mCollisionTimeWrtROF = mEventTime.timeInBCNS; // in triggered mode the ROF starts at BC (is there a delay?)
101 if (mParams.isContinuous()) {
102 auto nbc = mEventTime.differenceInBC(mIRFirstSampledTF);
103 if (mCollisionTimeWrtROF < 0 && nbc > 0) {
104 nbc--;
105 }
106 mNewROFrame = nbc / mParams.getROFrameLengthInBC();
107 // in continuous mode depends on starts of periodic readout frame
108 mCollisionTimeWrtROF += (nbc % mParams.getROFrameLengthInBC()) * o2::constants::lhc::LHCBunchSpacingNS;
109 } else {
110 mNewROFrame = 0;
111 }
112
113 if (mNewROFrame < mROFrameMin) {
114 LOG(error) << "New ROFrame " << mNewROFrame << " (" << irt << ") precedes currently cashed " << mROFrameMin;
115 throw std::runtime_error("deduced ROFrame precedes already processed one");
116 }
117
118 if (mParams.isContinuous() && mROFrameMax < mNewROFrame) {
119 mROFrameMax = mNewROFrame - 1; // all frames up to this are finished
120 }
121}
122
123void Digitizer::fillOutputContainer(uint32_t frameLast)
124{
125 // fill output with digits from min.cached up to requested frame, generating the noise beforehand
126 if (frameLast > mROFrameMax) {
127 frameLast = mROFrameMax;
128 }
129 // make sure all buffers for extra digits are created up to the maxFrame
130 getExtraDigBuffer(mROFrameMax);
131
132 LOG(info) << "Filling " << mGeometry->getName() << " digits output for RO frames " << mROFrameMin << ":"
133 << frameLast;
134
136
137 // we have to write chips in RO increasing order, therefore have to loop over the frames here
138 for (; mROFrameMin <= frameLast; mROFrameMin++) {
139 rcROF.setROFrame(mROFrameMin);
140 rcROF.setFirstEntry(mDigits->size()); // start of current ROF in digits
141
142 auto& extra = *(mExtraBuff.front().get());
143 for (size_t iChip{0}; iChip < mChips.size(); ++iChip) {
144 auto& chip = mChips[iChip];
145 if (constants::detID::isDetITS3(iChip)) { // Check if this is a chip of ITS3
146 chip.addNoise(mROFrameMin, mROFrameMin, &mParams, SuperSegmentation::mNRows, SuperSegmentation::mNCols);
147 } else {
148 chip.addNoise(mROFrameMin, mROFrameMin, &mParams);
149 }
150 auto& buffer = chip.getPreDigits();
151 if (buffer.empty()) {
152 continue;
153 }
154 auto itBeg = buffer.begin();
155 auto iter = itBeg;
156 ULong64_t maxKey = chip.getOrderingKey(mROFrameMin + 1, 0, 0) - 1; // fetch digits with key below that
157 for (; iter != buffer.end(); ++iter) {
158 if (iter->first > maxKey) {
159 break; // is the digit ROFrame from the key > the max requested frame
160 }
161 auto& preDig = iter->second; // preDigit
162 if (preDig.charge >= mParams.getChargeThreshold()) {
163 int digID = mDigits->size();
164 mDigits->emplace_back(chip.getChipIndex(), preDig.row, preDig.col, preDig.charge);
165 mMCLabels->addElement(digID, preDig.labelRef.label);
166 auto& nextRef = preDig.labelRef; // extra contributors are in extra array
167 while (nextRef.next >= 0) {
168 nextRef = extra[nextRef.next];
169 mMCLabels->addElement(digID, nextRef.label);
170 }
171 }
172 }
173 buffer.erase(itBeg, iter);
174 }
175 // finalize ROF record
176 rcROF.setNEntries(mDigits->size() - rcROF.getFirstEntry()); // number of digits
177 if (isContinuous()) {
178 rcROF.getBCData().setFromLong(mIRFirstSampledTF.toLong() + mROFrameMin * mParams.getROFrameLengthInBC());
179 } else {
180 rcROF.getBCData() = mEventTime; // RS TODO do we need to add trigger delay?
181 }
182 if (mROFRecords != nullptr) {
183 mROFRecords->push_back(rcROF);
184 }
185 extra.clear(); // clear container for extra digits of the mROFrameMin ROFrame
186 // and move it as a new slot in the end
187 mExtraBuff.emplace_back(mExtraBuff.front().release());
188 mExtraBuff.pop_front();
189 }
190}
191
192void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID)
193{
194 // convert single hit to digits
195 int chipID = hit.GetDetectorID();
196 auto& chip = mChips[chipID];
197 if (chip.isDisabled()) {
198 return;
199 }
200 float timeInROF = hit.GetTime() * sec2ns;
201 if (timeInROF > 20e3) {
202 const int maxWarn = 10;
203 static int warnNo = 0;
204 if (warnNo < maxWarn) {
205 LOG(warning) << "Ignoring hit with time_in_event = " << timeInROF << " ns"
206 << ((++warnNo < maxWarn) ? "" : " (suppressing further warnings)");
207 }
208 return;
209 }
210 if (isContinuous()) {
211 timeInROF += mCollisionTimeWrtROF;
212 }
213 // calculate RO Frame for this hit
214 if (timeInROF < 0) {
215 timeInROF = 0.;
216 }
217 float tTot = mParams.getSignalShape().getMaxDuration();
218 // frame of the hit signal start wrt event ROFrame
219 int roFrameRel = int(timeInROF * mParams.getROFrameLengthInv());
220 // frame of the hit signal end wrt event ROFrame: in the triggered mode we read just 1 frame
221 uint32_t roFrameRelMax = mParams.isContinuous() ? (timeInROF + tTot) * mParams.getROFrameLengthInv() : roFrameRel;
222 int nFrames = roFrameRelMax + 1 - roFrameRel;
223 uint32_t roFrameMax = mNewROFrame + roFrameRelMax;
224 if (roFrameMax > maxFr) {
225 maxFr = roFrameMax; // if signal extends beyond current maxFrame, increase the latter
226 }
227
228 // here we start stepping in the depth of the sensor to generate charge diffision
229 float nStepsInv = mParams.getNSimStepsInv();
230 int nSteps = mParams.getNSimSteps();
231 int detID{hit.GetDetectorID()};
232 int layer = mGeometry->getLayer(detID);
233 const auto& matrix = mGeometry->getMatrixL2G(detID);
234 bool innerBarrel{layer < 3};
235 math_utils::Vector3D<float> xyzLocS, xyzLocE;
236 xyzLocS = matrix ^ (hit.GetPosStart()); // Global hit coordinates to local detector coordinates
237 xyzLocE = matrix ^ (hit.GetPos());
238 if (innerBarrel) {
239 // transform the point on the curved surface to a flat one
240 float xFlatE{0.f}, yFlatE{0.f}, xFlatS{0.f}, yFlatS{0.f};
241 SuperSegmentations[layer].curvedToFlat(xyzLocS.X(), xyzLocS.Y(), xFlatS, yFlatS);
242 SuperSegmentations[layer].curvedToFlat(xyzLocE.X(), xyzLocE.Y(), xFlatE, yFlatE);
243 // update the local coordinates with the flattened ones
244 xyzLocS.SetXYZ(xFlatS, yFlatS, xyzLocS.Z());
245 xyzLocE.SetXYZ(xFlatE, yFlatE, xyzLocE.Z());
246 }
247
249 step -= xyzLocS;
250 step *= nStepsInv; // position increment at each step
251 // the electrons will be injected in the middle of each step
252 math_utils::Vector3D<float> stepH(step * 0.5);
253 xyzLocS += stepH; // Adjust start position to the middle of the first step
254 xyzLocE -= stepH; // Adjust end position to the middle of the last step
255 int rowS = -1, colS = -1, rowE = -1, colE = -1, nSkip = 0;
256 if (innerBarrel) {
257 // get entrance pixel row and col
258 while (!SuperSegmentations[layer].localToDetector(xyzLocS.X(), xyzLocS.Z(), rowS, colS)) { // guard-ring ?
259 if (++nSkip >= nSteps) {
260 return; // did not enter to sensitive matrix
261 }
262 xyzLocS += step;
263 }
264 // get exit pixel row and col
265 while (!SuperSegmentations[layer].localToDetector(xyzLocE.X(), xyzLocE.Z(), rowE, colE)) { // guard-ring ?
266 if (++nSkip >= nSteps) {
267 return; // did not enter to sensitive matrix
268 }
269 xyzLocE -= step;
270 }
271 } else {
272 // get entrance pixel row and col
273 while (!Segmentation::localToDetector(xyzLocS.X(), xyzLocS.Z(), rowS, colS)) { // guard-ring ?
274 if (++nSkip >= nSteps) {
275 return; // did not enter to sensitive matrix
276 }
277 xyzLocS += step;
278 }
279 // get exit pixel row and col
280 while (!Segmentation::localToDetector(xyzLocE.X(), xyzLocE.Z(), rowE, colE)) { // guard-ring ?
281 if (++nSkip >= nSteps) {
282 return; // did not enter to sensitive matrix
283 }
284 xyzLocE -= step;
285 }
286 }
287
288 // estimate the limiting min/max row and col where the non-0 response is possible
289 if (rowS > rowE) {
290 std::swap(rowS, rowE);
291 }
292 if (colS > colE) {
293 std::swap(colS, colE);
294 }
295 rowS -= AlpideRespSimMat::NPix / 2;
296 rowE += AlpideRespSimMat::NPix / 2;
297 if (rowS < 0) {
298 rowS = 0;
299 }
300
301 int maxNrows{innerBarrel ? SuperSegmentation::mNRows : Segmentation::NRows};
302 int maxNcols{innerBarrel ? SuperSegmentation::mNCols : Segmentation::NCols};
303 if (rowE >= maxNrows) {
304 rowE = maxNrows - 1;
305 }
306 colS -= AlpideRespSimMat::NPix / 2;
307 colE += AlpideRespSimMat::NPix / 2;
308 if (colS < 0) {
309 colS = 0;
310 }
311 if (colE >= maxNcols) {
312 colE = maxNcols - 1;
313 }
314 int rowSpan = rowE - rowS + 1, colSpan = colE - colS + 1; // size of plaquet where some response is expected
315 float respMatrix[rowSpan][colSpan]; // response accumulated here
316 std::fill(&respMatrix[0][0], &respMatrix[0][0] + rowSpan * colSpan, 0.f);
317
318 float nElectrons = hit.GetEnergyLoss() * mParams.getEnergyToNElectrons(); // total number of deposited electrons
319 nElectrons *= nStepsInv; // N electrons injected per step
320 if (nSkip != 0) {
321 nSteps -= nSkip;
322 }
323 //
324 int rowPrev = -1, colPrev = -1, row, col;
325 float cRowPix = 0.f, cColPix = 0.f; // local coordinated of the current pixel center
326
327 // take into account that the AlpideSimResponse depth defintion has different min/max boundaries
328 // although the max should coincide with the surface of the epitaxial layer, which in the chip
329 // local coordinates has Y = +SensorLayerThickness/2
331 xyzLocS.SetY(xyzLocS.Y() + mAlpSimResp->getDepthMax() - thickness / 2.);
332 // collect charge in evey pixel which might be affected by the hit
333 for (int iStep = nSteps; iStep--;) {
334 // Get the pixel ID
335 if (innerBarrel) {
336 SuperSegmentations[layer].localToDetector(xyzLocS.X(), xyzLocS.Z(), row, col);
337 } else {
338 Segmentation::localToDetector(xyzLocS.X(), xyzLocS.Z(), row, col);
339 }
340 if (row != rowPrev || col != colPrev) { // update pixel and coordinates of its center
341 if (innerBarrel) {
342 if (!SuperSegmentations[layer].detectorToLocal(row, col, cRowPix, cColPix)) {
343 continue;
344 }
345 } else if (!Segmentation::detectorToLocal(row, col, cRowPix, cColPix)) {
346 continue; // should not happen
347 }
348 rowPrev = row;
349 colPrev = col;
350 }
351 bool flipCol = false, flipRow = false;
352 // note that response needs coordinates along column row (locX) (locZ) then depth (locY)
353 double rowMax{0.5f * (innerBarrel ? SuperSegmentation::mPitchRow : Segmentation::PitchRow)};
354 double colMax{0.5f * (innerBarrel ? SuperSegmentation::mPitchCol : Segmentation::PitchCol)};
355 auto rspmat = mAlpSimResp->getResponse(xyzLocS.X() - cRowPix, xyzLocS.Z() - cColPix, xyzLocS.Y(), flipRow, flipCol, rowMax, colMax);
356
357 xyzLocS += step;
358 if (rspmat == nullptr) {
359 continue;
360 }
361
362 for (int irow = AlpideRespSimMat::NPix; irow--;) {
363 int rowDest = row + irow - AlpideRespSimMat::NPix / 2 - rowS; // destination row in the respMatrix
364 if (rowDest < 0 || rowDest >= rowSpan) {
365 continue;
366 }
367 for (int icol = AlpideRespSimMat::NPix; icol--;) {
368 int colDest = col + icol - AlpideRespSimMat::NPix / 2 - colS; // destination column in the respMatrix
369 if (colDest < 0 || colDest >= colSpan) {
370 continue;
371 }
372 respMatrix[rowDest][colDest] += rspmat->getValue(irow, icol, flipRow, flipCol);
373 }
374 }
375 }
376
377 // fire the pixels assuming Poisson(n_response_electrons)
378 o2::MCCompLabel lbl(hit.GetTrackID(), evID, srcID, false);
379 auto roFrameAbs = mNewROFrame + roFrameRel;
380 for (int irow = rowSpan; irow--;) {
381 uint16_t rowIS = irow + rowS;
382 for (int icol = colSpan; icol--;) {
383 float nEleResp = respMatrix[irow][icol];
384 if (nEleResp <= 1.e-36) {
385 continue;
386 }
387 int nEle = gRandom->Poisson(nElectrons * nEleResp); // total charge in given pixel
388 // ignore charge which have no chance to fire the pixel
389 if (nEle < mParams.getMinChargeToAccount()) {
390 continue;
391 }
392 uint16_t colIS = icol + colS;
393 registerDigits(chip, roFrameAbs, timeInROF, nFrames, rowIS, colIS, nEle, lbl);
394 }
395 }
396}
397
398void Digitizer::registerDigits(o2::itsmft::ChipDigitsContainer& chip, uint32_t roFrame, float tInROF, int nROF,
399 uint16_t row, uint16_t col, int nEle, o2::MCCompLabel& lbl)
400{
401 // Register digits for given pixel, accounting for the possible signal contribution to
402 // multiple ROFrame. The signal starts at time tInROF wrt the start of provided roFrame
403 // In every ROFrame we check the collected signal during strobe
404
405 float tStrobe = mParams.getStrobeDelay() - tInROF; // strobe start wrt signal start
406 for (int i = 0; i < nROF; i++) {
407 uint32_t roFr = roFrame + i;
408 int nEleROF = mParams.getSignalShape().getCollectedCharge(nEle, tStrobe, tStrobe + mParams.getStrobeLength());
409 tStrobe += mParams.getROFrameLength(); // for the next ROF
410
411 // discard too small contributions, they have no chance to produce a digit
412 if (nEleROF < mParams.getMinChargeToAccount()) {
413 continue;
414 }
415 if (roFr > mEventROFrameMax) {
416 mEventROFrameMax = roFr;
417 }
418 if (roFr < mEventROFrameMin) {
419 mEventROFrameMin = roFr;
420 }
421 auto key = chip.getOrderingKey(roFr, row, col);
422 PreDigit* pd = chip.findDigit(key);
423 if (pd == nullptr) {
424 chip.addDigit(key, roFr, row, col, nEleROF, lbl);
425 } else { // there is already a digit at this slot, account as PreDigitExtra contribution
426 pd->charge += nEleROF;
427 if (pd->labelRef.label == lbl) { // don't store the same label twice
428 continue;
429 }
430 ExtraDig* extra = getExtraDigBuffer(roFr);
431 int& nxt = pd->labelRef.next;
432 bool skip = false;
433 while (nxt >= 0) {
434 if ((*extra)[nxt].label == lbl) { // don't store the same label twice
435 skip = true;
436 break;
437 }
438 nxt = (*extra)[nxt].next;
439 }
440 if (skip) {
441 continue;
442 }
443 // new predigit will be added in the end of the chain
444 nxt = extra->size();
445 extra->emplace_back(lbl);
446 }
447 }
448}
int32_t i
Definition of a container to keep Monte Carlo truth external to simulation objects.
uint32_t col
Definition RawData.h:4
Definition of the SegmentationAlpide class.
Definition of the ITS 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
short GetDetectorID() const
Definition BaseHits.h:73
void addElement(uint32_t dataindex, TruthElement const &element, bool noElement=false)
const char * getName() const
const Mat3D & getMatrixL2G(int sensID) const
bool isContinuous() const
Definition Digitizer.h:62
void setEventTime(const o2::InteractionTimeRecord &irt)
Definition Digitizer.cxx:91
void process(const std::vector< itsmft::Hit > *hits, int evID, int srcID)
Steer conversion of hits to digits.
Definition Digitizer.cxx:58
void fillOutputContainer(uint32_t maxFrame=0xffffffff)
Segmentation and response for pixels in ITS3 upgrade.
int getLayer(int index) const
Get chip layer, from 0.
float getCollectedCharge(float totalNEle, float tMin, float tMax) const
bool getResponse(float vRow, float vCol, float cDepth, AlpideRespSimMat &dest) const
Container for similated points connected to a given chip.
void addDigit(ULong64_t key, UInt_t roframe, UShort_t row, UShort_t col, int charge, o2::MCCompLabel lbl)
o2::itsmft::PreDigit * findDigit(ULong64_t key)
static ULong64_t getOrderingKey(UInt_t roframe, UShort_t row, UShort_t col)
Get global ordering key made of readout frame, column and row.
int getMinChargeToAccount() const
Definition DigiParams.h:82
void setAlpSimResponse(const o2::itsmft::AlpideSimResponse *par)
Definition DigiParams.h:94
const o2::itsmft::AlpideSimResponse * getAlpSimResponse() const
Definition DigiParams.h:93
float getROFrameLengthInv() const
Definition DigiParams.h:59
const SignalShape & getSignalShape() const
Definition DigiParams.h:96
float getEnergyToNElectrons() const
Definition DigiParams.h:85
int getROFrameLengthInBC() const
Definition DigiParams.h:54
float getStrobeDelay() const
Definition DigiParams.h:62
bool isContinuous() const
Definition DigiParams.h:52
int getChargeThreshold() const
Definition DigiParams.h:81
float getNSimStepsInv() const
Definition DigiParams.h:84
float getROFrameLength() const
Definition DigiParams.h:58
int getNSimSteps() const
Definition DigiParams.h:83
float getStrobeLength() const
Definition DigiParams.h:65
Int_t getNumberOfChips() const
math_utils::Point3D< Float_t > GetPosStart() const
Definition Hit.h:60
bool isFullChipMasked(int chip) const
Definition NoiseMap.h:186
void setNEntries(int n)
Definition ROFRecord.h:48
const BCData & getBCData() const
Definition ROFRecord.h:58
void setFirstEntry(int idx)
Definition ROFRecord.h:47
int getFirstEntry() const
Definition ROFRecord.h:63
void setROFrame(ROFtype rof)
Definition ROFRecord.h:45
static constexpr float SensorLayerThickness
static bool localToDetector(float x, float z, int &iRow, int &iCol)
static constexpr float PitchCol
static constexpr float PitchRow
static bool detectorToLocal(L row, L col, T &xRow, T &zCol)
GLuint buffer
Definition glcorearb.h:655
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
constexpr double LHCBunchSpacingNS
bool isDetITS3(T detID)
Definition SpecsV2.h:161
constexpr float thickness
Definition SpecsV2.h:114
const std::array< SegmentationSuperAlpide, constants::nLayers > SuperSegmentations
Segmentation array.
value_T step
Definition TrackUtils.h:42
int64_t differenceInBC(const InteractionRecord &other) const
void setFromLong(int64_t l)
double timeInBCNS
time in NANOSECONDS relative to orbit/bc
int next
eventual next contribution to the same pixel
Definition PreDigit.h:36
o2::MCCompLabel label
hit label
Definition PreDigit.h:35
int charge
N electrons.
Definition PreDigit.h:46
PreDigitLabelRef labelRef
label and reference to the next one
Definition PreDigit.h:47
IR getFirstSampledTFIR() const
get TF and HB (abs) for this IR
Definition HBFUtils.h:74
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< int > row