25#include <TCollection.h>
36#include <fairlogger/Logger.h>
45 const int numberOfChips = mGeometry->
getSize();
46 mChips.resize(numberOfChips);
50 const int nReadOutCols = specsConfig.NCols / digitizerParams.nColsPerGroup + 1;
51 for (
int i = numberOfChips;
i--;) {
52 mChips[
i].setChipIndex(
i);
65 mChips[
i].resizeTDCStates(nReadOutCols);
66 for (
auto& tdcStates : mChips[
i].getTDCStates()) {
67 for (
int iT = 0; iT < 4; ++iT) {
68 tdcStates[iT] = -o2::constants::math::VeryBig;
72 mChips[
i].resizeMaskStates(nReadOutCols);
73 for (
auto& maskStates : mChips[
i].getMaskStates()) {
74 maskStates = -o2::constants::math::VeryBig;
78 LOG(info) <<
"Initializing IOTOF digitizer";
79 LOG(info) <<
" Time resolution: " << digitizerParams.timeResolution * 1e3 <<
" ps";
80 LOG(info) <<
" Charge threshold: " << digitizerParams.chargeThreshold <<
" electrons";
81 LOG(info) <<
" Continuous mode: " << (mContinuous ?
"ON" :
"OFF");
83 sSegmentation = o2::iotof::Segmentation::Instance();
90 LOG(
debug) <<
"Digitizing IOTOF hits: " << hits->size() <<
" hits from event " << evID <<
" source " << srcID;
92 if (!hits || hits->empty()) {
97 std::vector<int> hitIdx(hits->size());
98 std::iota(hitIdx.begin(), hitIdx.end(), 0);
99 std::sort(hitIdx.begin(), hitIdx.end(),
100 [hits](
int lhs,
int rhs) {
101 return (*hits)[lhs].GetDetectorID() < (*hits)[rhs].GetDetectorID();
105 for (
int i : hitIdx) {
106 processHit((*hits)[
i], evID, srcID);
111 LOG(
debug) <<
"Inner flushing for non-continuous mode";
117void Digitizer::processHit(
const o2::itsmft::Hit& hit,
int evID,
int srcID)
124 if (chipID < 0 || chipID >= mGeometry->
getSize() || mGeometry->
getSize() < 1) {
125 LOG(
debug) <<
"Invalid detector ID: " << chipID <<
", geometry size: " << mGeometry->
getSize();
130 auto& chip = mChips[chipID];
131 if (chip.isDisabled()) {
132 LOG(
debug) <<
"Hit rejected because chip " << chipID <<
" is disabled";
138 int charge = energyToCharge(energyLoss);
140 int electronsPerStep =
static_cast<int>(
charge / digitizerParams.nSimSteps);
143 if (
charge < digitizerParams.chargeThreshold) {
144 LOG(
debug) <<
"Hit rejected by charge threshold: " <<
charge <<
" < " << digitizerParams.chargeThreshold;
150 double hitTime = hit.
GetTime() * sec2ns;
152 double hitTimeWrtBC = hitTime + eventTimeInBC;
156 const int roFrameAbs = 0;
159 float** respMatrix =
nullptr;
160 float** avgHitLocalX =
nullptr;
161 float** avgHitLocalZ =
nullptr;
162 int rowStart = 0, colStart = 0, rowSpan = 0, colSpan = 0;
163 stepping(hit, respMatrix, avgHitLocalX, avgHitLocalZ, rowStart, colStart, rowSpan, colSpan);
165 float xPixelCenter = 0.0f, zPixelCenter = 0.0f;
166 for (
int irow = rowSpan; irow--;) {
167 uint16_t rowIS = irow + rowStart;
168 for (
int icol = colSpan; icol--;) {
169 uint16_t colIS = icol + colStart;
170 float nEleResp = respMatrix[irow][icol];
176 sSegmentation->
detectorToLocal(rowIS, colIS, xPixelCenter, zPixelCenter, subdetectorID);
177 if (!isEfficient(avgHitLocalX[irow][icol] - xPixelCenter, avgHitLocalZ[irow][icol] - zPixelCenter)) {
178 LOG(
debug) <<
"Hit rejected by efficiency cut at pixel (row,col) = (" << rowIS <<
", " << colIS <<
")";
181 double smearedTime = smearTime(hitTimeWrtBC, avgHitLocalX[irow][icol] - xPixelCenter, avgHitLocalZ[irow][icol] - zPixelCenter);
183 const int nElectronsSampled = gRandom->Poisson(electronsPerStep * nEleResp);
186 registerDigits(chip, roFrameAbs, smearedTime, nROF,
187 static_cast<uint16_t
>(rowIS),
static_cast<uint16_t
>(colIS), nElectronsSampled,
label);
191 for (
int irow = 0; irow < rowSpan; ++irow) {
192 delete[] respMatrix[irow];
193 delete[] avgHitLocalX[irow];
194 delete[] avgHitLocalZ[irow];
197 delete[] avgHitLocalX;
198 delete[] avgHitLocalZ;
201void Digitizer::stepping(
const o2::itsmft::Hit& hit,
float**& respMatrix,
float**& avgHitLocalX,
float**& avgHitLocalZ,
int& rowStart,
int& colStart,
int& rowSpan,
int& colSpan)
207 auto xyzPositionStart(matrix ^ (hit.
GetPosStart()));
208 auto xyzPositionEnd(matrix ^ (hit.
GetPos()));
211 auto stepVector = (xyzPositionEnd - xyzPositionStart) / digitizerParams.nSimSteps;
212 xyzPositionStart = xyzPositionStart + stepVector * 0.5f;
216 int rowEnd = -1, colEnd = -1, nSkip = 0, nSteps = digitizerParams.nSimSteps;
217 while (!sSegmentation->
localToDetector(xyzPositionStart.X(), xyzPositionStart.Z(), rowStart, colStart, mGeometry->
getIOTOFLayer(chipID))) {
218 if (++nSkip > digitizerParams.nSimSteps) {
219 LOG(
debug) <<
"Hit position out of bounds for detector ID " << chipID;
222 xyzPositionStart += stepVector;
227 xyzPositionEnd = xyzPositionStart + stepVector * (nSteps - 1 - nSkip);
229 if (++nSkip > digitizerParams.nSimSteps) {
230 LOG(
debug) <<
"Hit position out of bounds for detector ID " << chipID;
233 xyzPositionEnd -= stepVector;
239 if (rowStart > rowEnd) {
240 std::swap(rowStart, rowEnd);
242 if (colStart > colEnd) {
243 std::swap(colStart, colEnd);
247 rowStart -= digitizerParams.responseMatrixSize / 2;
248 rowEnd += digitizerParams.responseMatrixSize / 2;
249 rowStart = std::max(rowStart, 0);
250 colStart = std::max(colStart, 0);
253 rowEnd = std::min(rowEnd, (specsConfig.NRows) - 1);
254 colEnd = std::min(colEnd, (specsConfig.NCols) - 1);
255 rowSpan = rowEnd - rowStart + 1;
256 colSpan = colEnd - colStart + 1;
257 if (rowSpan <= 0 || colSpan <= 0) {
261 respMatrix =
new float*[rowSpan];
262 avgHitLocalX =
new float*[rowSpan];
263 avgHitLocalZ =
new float*[rowSpan];
264 for (
int i = 0;
i < rowSpan; ++
i) {
265 respMatrix[
i] =
new float[colSpan]();
266 avgHitLocalX[
i] =
new float[colSpan]();
267 avgHitLocalZ[
i] =
new float[colSpan]();
270 if (!respMatrix || !avgHitLocalX || !avgHitLocalZ) {
274 int rowPrev = -1, colPrev = -1,
row = 0,
col = 0, nSkipPassive = 0;
275 auto pixelStartPosLocal = xyzPositionStart;
276 auto pixelCurrentPosLocal = xyzPositionStart;
277 for (
int iStep{0}; iStep < nSteps; ++iStep) {
278 pixelCurrentPosLocal = xyzPositionStart + iStep * stepVector;
281 if (!sSegmentation->
localToDetector(pixelCurrentPosLocal.X(), pixelCurrentPosLocal.Z(),
row,
col, subdetectorID)) {
282 LOG(
debug) <<
"Step is in passive area: (" << pixelCurrentPosLocal.X() <<
", " << pixelCurrentPosLocal.Z() <<
") is outside the active area of chip " << subdetectorID;
290 if (
row != rowPrev ||
col != colPrev) {
293 if (rowPrev != -1 && colPrev != -1) {
294 const int irow = rowPrev - rowStart;
295 const int icol = colPrev - colStart;
296 avgHitLocalX[irow][icol] = 0.5f * (pixelStartPosLocal.X() + pixelCurrentPosLocal.X() - (nSkipPassive + 1) * stepVector.X());
297 avgHitLocalZ[irow][icol] = 0.5f * (pixelStartPosLocal.Z() + pixelCurrentPosLocal.Z() - (nSkipPassive + 1) * stepVector.Z());
298 pixelStartPosLocal = pixelCurrentPosLocal;
307 for (
int irow = digitizerParams.responseMatrixSize; irow--;) {
308 int rowDest =
row + irow - (digitizerParams.responseMatrixSize / 2) - rowStart;
309 if (rowDest < 0 || rowDest >= rowSpan) {
312 for (
int icol = digitizerParams.responseMatrixSize; icol--;) {
313 int colDest =
col + icol - (digitizerParams.responseMatrixSize / 2) - colStart;
314 if (colDest < 0 || colDest >= colSpan) {
317 respMatrix[rowDest][colDest] += 1.;
323 if (rowPrev != -1 && colPrev != -1) {
324 const int irow = rowPrev - rowStart;
325 const int icol = colPrev - colStart;
327 avgHitLocalX[irow][icol] = 0.5f * (pixelStartPosLocal.X() + pixelCurrentPosLocal.X() - nSkipPassive * stepVector.X());
328 avgHitLocalZ[irow][icol] = 0.5f * (pixelStartPosLocal.Z() + pixelCurrentPosLocal.Z() - nSkipPassive * stepVector.Z());
333double Digitizer::smearTime(
double time,
const float x,
const float y)
const
338 float resolution = digitizerParams.timeResolution;
339 if (mScaledResolutionMap) {
340 int bin = mScaledResolutionMap->FindBin(
x * o2::iotof::Digitizer::cm2um,
y * o2::iotof::Digitizer::cm2um);
341 resolution = mScaledResolutionMap->GetBinContent(bin);
342 LOG(
debug) <<
"Time resolution map check: x=" <<
x * o2::iotof::Digitizer::cm2um <<
", y=" <<
y * o2::iotof::Digitizer::cm2um <<
", bin=" << bin <<
", resolution=" << resolution;
344 float timeOfArrivalOffset = 0.;
345 if (mTimeOfArrivalMap) {
346 int bin = mTimeOfArrivalMap->FindBin(
x * o2::iotof::Digitizer::cm2um,
y * o2::iotof::Digitizer::cm2um);
347 timeOfArrivalOffset = mTimeOfArrivalMap->GetBinContent(bin) * o2::iotof::Digitizer::ps2ns;
348 LOG(
debug) <<
"Time of arrival map check: x=" <<
x * o2::iotof::Digitizer::cm2um <<
", y=" <<
y * o2::iotof::Digitizer::cm2um <<
", bin=" << bin <<
", time offset=" << timeOfArrivalOffset;
351 if (digitizerParams.timeResolution > 0) {
352 return time + gRandom->Gaus(timeOfArrivalOffset, resolution);
358int Digitizer::energyToCharge(
float energyLoss)
const
364 return static_cast<int>(energyLoss * digitizerParams.energyToNElectrons);
370 if (!mResolutionMap) {
371 LOG(warn) <<
"No resolution map available to prepare scaled resolution map.";
376 const float nominalTimeResolution = digitizerParams.timeResolution;
377 const float minimumResolution = mResolutionMap->GetMinimum();
378 if (minimumResolution <= 0) {
379 LOG(warn) <<
"Minimum resolution in the map is non-positive, cannot prepare scaled resolution map.";
383 mScaledResolutionMap =
dynamic_cast<TH2D*
>(mResolutionMap->Clone(
"hScaledResolutionMap"));
384 mScaledResolutionMap->SetDirectory(
nullptr);
386 for (
int binX = 1; binX <= mScaledResolutionMap->GetNbinsX(); ++binX) {
387 for (
int binY = 1; binY <= mScaledResolutionMap->GetNbinsY(); ++binY) {
388 float originalValue = mScaledResolutionMap->GetBinContent(binX, binY);
389 float scalingValue = originalValue / minimumResolution;
390 mScaledResolutionMap->SetBinContent(binX, binY, scalingValue * nominalTimeResolution);
396bool Digitizer::isEfficient(
const float x,
const float z)
const
400 if (mEfficiencyMap) {
402 int bin = mEfficiencyMap->FindBin(
x * o2::iotof::Digitizer::cm2um,
z * o2::iotof::Digitizer::cm2um);
403 float efficiency = mEfficiencyMap->GetBinContent(bin);
404 LOG(
debug) <<
"Efficiency map check: x=" <<
x * o2::iotof::Digitizer::cm2um <<
", z=" <<
z * o2::iotof::Digitizer::cm2um <<
", bin=" << bin <<
", efficiency=" << efficiency;
405 return gRandom->Uniform() < efficiency;
407 return gRandom->Uniform() < digitizerParams.efficiency;
413 LOG(info) <<
"Filling output container with digits from chips";
414 LOG(
debug) <<
"Number of chips: " << mChips.size();
421 const auto* extraLabelBuffer = mExtraLabelBuffer.empty() ? nullptr : mExtraLabelBuffer.front().get();
422 for (
auto& chip : mChips) {
424 if (chip.isDisabled()) {
430 if (chip.isEmpty()) {
434 auto& chipDigits = chip.getDigits();
435 auto& tdcStates = chip.getTDCStates();
436 auto& maskStates = chip.getMaskStates();
438 for (
const auto& [
key, digit] : chipDigits) {
440 if (digit.getCharge() < digitizerParams.chargeThreshold) {
444 const int colInGroup = digit.getColumn() / digitizerParams.nColsPerGroup;
445 const double digitTime = digit.getTime();
447 if (digitTime - maskStates[colInGroup] < digitizerParams.maskWindow) {
452 for (
int iT = 0; iT < 4; ++iT) {
453 if (digitTime - tdcStates[colInGroup][iT] > digitizerParams.tdcBusyTime) {
459 if (available == -1) {
463 tdcStates[colInGroup][available] = digitTime;
464 maskStates[colInGroup] = digitTime;
466 int digitID = mDigits->size();
467 mDigits->emplace_back(digit.getChipIndex(), digit.getRow(), digit.getColumn(), digit.getCharge(), digit.getTime(), digit.getBc(), digit.getTdc());
469 mMCLabels->
addElement(digitID, digit.getLabel().mLabel);
471 auto labelRef = digit.getLabel();
473 while (mMCLabels && extraLabelBuffer !=
nullptr && labelRef.mNext >= 0) {
474 labelRef = (*extraLabelBuffer)[labelRef.mNext];
475 mMCLabels->
addElement(digitID, labelRef.mLabel);
482 rof.
setBCData(mContinuous ? mROFRecordIR : mEventTime);
483 mROFRecords->push_back(rof);
484 LOG(
debug) <<
"Created ROF record with " << mDigits->size() <<
" digits";
491void Digitizer::registerDigits(
Chip& chip, uint32_t roFrame,
double time,
int nROF,
498 uint64_t nbc =
static_cast<uint64_t
>(
time / o2::constants::lhc::LHCBunchSpacingNS);
499 int tdc = int((
time - nbc * o2::constants::lhc::LHCBunchSpacingNS) / digitizerParams.tdcBin);
500 nbc += mEventTime.
toLong();
502 double absoluteTime = tdc * digitizerParams.tdcBin + nbc * o2::constants::lhc::LHCBunchSpacingNS;
507 if (!existingDigit) {
512 const int storedCharge = existingDigit->
getCharge();
513 existingDigit->
setCharge(storedCharge + nElectrons);
518 std::vector<o2::iotof::McLabelRef>* extra = getExtraLabelBuffer(roFrame);
519 auto labelRef = existingDigit->
getLabel();
520 const auto next =
static_cast<int>(extra->size());
521 extra->emplace_back(
label, labelRef.mNext);
522 labelRef.
mNext = next;
525 LOG(
debug) <<
"Registered digit at (row,col) = (" <<
row <<
", " <<
col <<
") with charge: " << nElectrons <<
", time: " <<
time <<
", nbc: " << nbc <<
", tdc: " << tdc;
Definition of the ALICE3 TOF digitizer.
math_utils::Point3D< T > GetPos() const
unsigned short GetDetectorID() const
static const DPLDigitizerParam & Instance()
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...
void addDigit(UShort_t row, UShort_t col, Int_t charge, double time, ULong64_t bc, Int_t tdc, o2::MCCompLabel label)
o2::iotof::LabeledDigit * findDigit(DigitKey key)
reset points container
void setTime(double time)
static DigitKey getOrderingKey(ULong64_t bc, UInt_t tdc, uint16_t row, uint16_t col)
void prepareScaledResolutionMap()
void init()
Initialize the digitizer.
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.
int getIOTOFLayer(int index) const
McLabelRef getLabel() const
void setLabel(McLabelRef label)
Segmentation and response for pixels in inner and outer TOF of the ALICE 3 apparatus.
bool detectorToLocal(L row, L col, T &xRow, T &zCol, const int subDetectorID) const
bool localToDetector(float x, float z, int &iRow, int &iCol, const int subDetectorID) const
void setCharge(Int_t charge)
Set the charge of the digit.
Int_t getCharge() const
Get the accumulated charged of the digit.
math_utils::Point3D< Float_t > GetPosStart() const
void setBCData(const BCData &bc)
void setFirstEntry(int idx)
int getFirstEntry() const
GLuint GLsizei const GLchar * label
GLdouble GLdouble GLdouble z
double getTimeOffsetWrtBC() const
int mNext
eventual next contribution to the same pixel
o2::MCCompLabel mLabel
hit label
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"