24#include <TCollection.h>
35#include <fairlogger/Logger.h>
44 const int numberOfChips = mGeometry->
getSize();
45 mChips.resize(numberOfChips);
49 const int nReadOutCols = specsConfig.NCols / digitizerParams.nColsPerGroup + 1;
50 for (
int i = numberOfChips;
i--;) {
51 mChips[
i].setChipIndex(
i);
64 mChips[
i].resizeTDCStates(nReadOutCols);
65 for (
auto& tdcStates : mChips[
i].getTDCStates()) {
66 tdcStates[0] = -999.f;
67 tdcStates[1] = -999.f;
71 LOG(info) <<
"Initializing IOTOF digitizer";
72 LOG(info) <<
" Time resolution: " << digitizerParams.timeResolution * 1e3 <<
" ps";
73 LOG(info) <<
" Charge threshold: " << digitizerParams.chargeThreshold <<
" electrons";
74 LOG(info) <<
" Continuous mode: " << (mContinuous ?
"ON" :
"OFF");
76 loadMap(mEfficiencyMap, digitizerParams.efficiencyMapPath,
"hEfficiencyMap");
77 if (!mEfficiencyMap) {
78 LOG(info) <<
"No efficiency map loaded, using uniform efficiency: " << digitizerParams.efficiency * 100 <<
" %";
81 loadMap(mResolutionMap, digitizerParams.resolutionMapPath,
"hSigmaPixel");
82 prepareScaledResolutionMap();
83 if (!mResolutionMap) {
84 LOG(info) <<
"No resolution map loaded, using uniform time resolution: " << digitizerParams.timeResolution * 1e3 <<
" ps";
87 loadMap(mTimeOfArrivalMap, digitizerParams.timeOfArrivalMapPath,
"toa_pixel");
88 if (!mTimeOfArrivalMap) {
89 LOG(info) <<
"No time of arrival map loaded";
92 sSegmentation = o2::iotof::Segmentation::Instance();
99 LOG(
debug) <<
"Digitizing IOTOF hits: " << hits->size() <<
" hits from event " << evID <<
" source " << srcID;
101 if (!hits || hits->empty()) {
106 std::vector<int> hitIdx(hits->size());
107 std::iota(hitIdx.begin(), hitIdx.end(), 0);
108 std::sort(hitIdx.begin(), hitIdx.end(),
109 [hits](
int lhs,
int rhs) {
110 return (*hits)[lhs].GetDetectorID() < (*hits)[rhs].GetDetectorID();
114 for (
int i : hitIdx) {
115 processHit((*hits)[
i], evID, srcID);
120 LOG(
debug) <<
"Inner flushing for non-continuous mode";
126void Digitizer::processHit(
const o2::itsmft::Hit& hit,
int evID,
int srcID)
133 if (chipID < 0 || chipID >= mGeometry->
getSize() || mGeometry->
getSize() < 1) {
134 LOG(
debug) <<
"Invalid detector ID: " << chipID <<
", geometry size: " << mGeometry->
getSize();
139 auto& chip = mChips[chipID];
140 if (chip.isDisabled()) {
141 LOG(
debug) <<
"Hit rejected because chip " << chipID <<
" is disabled";
147 int charge = energyToCharge(energyLoss);
149 int electronsPerStep =
static_cast<int>(
charge / digitizerParams.nSimSteps);
152 if (
charge < digitizerParams.chargeThreshold) {
153 LOG(
debug) <<
"Hit rejected by charge threshold: " <<
charge <<
" < " << digitizerParams.chargeThreshold;
159 double hitTime = hit.
GetTime() * sec2ns;
161 double hitTimeWrtBC = hitTime + eventTimeInBC;
165 const int roFrameAbs = 0;
168 float** respMatrix =
nullptr;
169 float** avgHitLocalX =
nullptr;
170 float** avgHitLocalZ =
nullptr;
171 int rowStart = 0, colStart = 0, rowSpan = 0, colSpan = 0;
172 stepping(hit, respMatrix, avgHitLocalX, avgHitLocalZ, rowStart, colStart, rowSpan, colSpan);
174 float xPixelCenter = 0.0f, zPixelCenter = 0.0f;
175 for (
int irow = rowSpan; irow--;) {
176 uint16_t rowIS = irow + rowStart;
177 for (
int icol = colSpan; icol--;) {
178 uint16_t colIS = icol + colStart;
179 float nEleResp = respMatrix[irow][icol];
185 sSegmentation->
detectorToLocal(rowIS, colIS, xPixelCenter, zPixelCenter, subdetectorID);
186 if (!isEfficient(avgHitLocalX[irow][icol] - xPixelCenter, avgHitLocalZ[irow][icol] - zPixelCenter)) {
187 LOG(
debug) <<
"Hit rejected by efficiency cut at pixel (row,col) = (" << rowIS <<
", " << colIS <<
")";
190 double smearedTime = smearTime(hitTimeWrtBC, avgHitLocalX[irow][icol] - xPixelCenter, avgHitLocalZ[irow][icol] - zPixelCenter);
192 const int nElectronsSampled = gRandom->Poisson(electronsPerStep * nEleResp);
195 registerDigits(chip, roFrameAbs, smearedTime, nROF,
196 static_cast<uint16_t
>(rowIS),
static_cast<uint16_t
>(colIS), nElectronsSampled,
label);
200 for (
int irow = 0; irow < rowSpan; ++irow) {
201 delete[] respMatrix[irow];
202 delete[] avgHitLocalX[irow];
203 delete[] avgHitLocalZ[irow];
206 delete[] avgHitLocalX;
207 delete[] avgHitLocalZ;
210void Digitizer::stepping(
const o2::itsmft::Hit& hit,
float**& respMatrix,
float**& avgHitLocalX,
float**& avgHitLocalZ,
int& rowStart,
int& colStart,
int& rowSpan,
int& colSpan)
216 auto xyzPositionStart(matrix ^ (hit.
GetPosStart()));
217 auto xyzPositionEnd(matrix ^ (hit.
GetPos()));
220 auto stepVector = (xyzPositionEnd - xyzPositionStart) / digitizerParams.nSimSteps;
221 xyzPositionStart = xyzPositionStart + stepVector * 0.5f;
225 int rowEnd = -1, colEnd = -1, nSkip = 0, nSteps = digitizerParams.nSimSteps;
226 while (!sSegmentation->
localToDetector(xyzPositionStart.X(), xyzPositionStart.Z(), rowStart, colStart, mGeometry->
getIOTOFLayer(chipID))) {
227 if (++nSkip > digitizerParams.nSimSteps) {
228 LOG(
debug) <<
"Hit position out of bounds for detector ID " << chipID;
231 xyzPositionStart += stepVector;
236 xyzPositionEnd = xyzPositionStart + stepVector * (nSteps - 1 - nSkip);
238 if (++nSkip > digitizerParams.nSimSteps) {
239 LOG(
debug) <<
"Hit position out of bounds for detector ID " << chipID;
242 xyzPositionEnd -= stepVector;
248 if (rowStart > rowEnd) {
249 std::swap(rowStart, rowEnd);
251 if (colStart > colEnd) {
252 std::swap(colStart, colEnd);
256 rowStart -= digitizerParams.responseMatrixSize / 2;
257 rowEnd += digitizerParams.responseMatrixSize / 2;
258 rowStart = std::max(rowStart, 0);
259 colStart = std::max(colStart, 0);
262 rowEnd = std::min(rowEnd, (specsConfig.NRows) - 1);
263 colEnd = std::min(colEnd, (specsConfig.NCols) - 1);
264 rowSpan = rowEnd - rowStart + 1;
265 colSpan = colEnd - colStart + 1;
266 if (rowSpan <= 0 || colSpan <= 0) {
270 respMatrix =
new float*[rowSpan];
271 avgHitLocalX =
new float*[rowSpan];
272 avgHitLocalZ =
new float*[rowSpan];
273 for (
int i = 0;
i < rowSpan; ++
i) {
274 respMatrix[
i] =
new float[colSpan]();
275 avgHitLocalX[
i] =
new float[colSpan]();
276 avgHitLocalZ[
i] =
new float[colSpan]();
279 if (!respMatrix || !avgHitLocalX || !avgHitLocalZ) {
283 int rowPrev = -1, colPrev = -1,
row = 0,
col = 0, nSkipPassive = 0;
284 auto pixelStartPosLocal = xyzPositionStart;
285 auto pixelCurrentPosLocal = xyzPositionStart;
286 for (
int iStep{0}; iStep < nSteps; ++iStep) {
287 pixelCurrentPosLocal = xyzPositionStart + iStep * stepVector;
290 if (!sSegmentation->
localToDetector(pixelCurrentPosLocal.X(), pixelCurrentPosLocal.Z(),
row,
col, subdetectorID)) {
291 LOG(
debug) <<
"Step is in passive area: (" << pixelCurrentPosLocal.X() <<
", " << pixelCurrentPosLocal.Z() <<
") is outside the active area of chip " << subdetectorID;
299 if (
row != rowPrev ||
col != colPrev) {
302 if (rowPrev != -1 && colPrev != -1) {
303 const int irow = rowPrev - rowStart;
304 const int icol = colPrev - colStart;
305 avgHitLocalX[irow][icol] = 0.5f * (pixelStartPosLocal.X() + pixelCurrentPosLocal.X() - (nSkipPassive + 1) * stepVector.X());
306 avgHitLocalZ[irow][icol] = 0.5f * (pixelStartPosLocal.Z() + pixelCurrentPosLocal.Z() - (nSkipPassive + 1) * stepVector.Z());
307 pixelStartPosLocal = pixelCurrentPosLocal;
316 for (
int irow = digitizerParams.responseMatrixSize; irow--;) {
317 int rowDest =
row + irow - (digitizerParams.responseMatrixSize / 2) - rowStart;
318 if (rowDest < 0 || rowDest >= rowSpan) {
321 for (
int icol = digitizerParams.responseMatrixSize; icol--;) {
322 int colDest =
col + icol - (digitizerParams.responseMatrixSize / 2) - colStart;
323 if (colDest < 0 || colDest >= colSpan) {
326 respMatrix[rowDest][colDest] += 1.;
332 if (rowPrev != -1 && colPrev != -1) {
333 const int irow = rowPrev - rowStart;
334 const int icol = colPrev - colStart;
336 avgHitLocalX[irow][icol] = 0.5f * (pixelStartPosLocal.X() + pixelCurrentPosLocal.X() - nSkipPassive * stepVector.X());
337 avgHitLocalZ[irow][icol] = 0.5f * (pixelStartPosLocal.Z() + pixelCurrentPosLocal.Z() - nSkipPassive * stepVector.Z());
342double Digitizer::smearTime(
double time,
const float x,
const float y)
const
347 float resolution = digitizerParams.timeResolution;
348 if (mScaledResolutionMap) {
349 int bin = mScaledResolutionMap->FindBin(
x * o2::iotof::Digitizer::cm2um,
y * o2::iotof::Digitizer::cm2um);
350 resolution = mScaledResolutionMap->GetBinContent(bin);
351 LOG(
debug) <<
"Time resolution map check: x=" <<
x * o2::iotof::Digitizer::cm2um <<
", y=" <<
y * o2::iotof::Digitizer::cm2um <<
", bin=" << bin <<
", resolution=" << resolution;
353 float timeOfArrivalOffset = 0.;
354 if (mTimeOfArrivalMap) {
355 int bin = mTimeOfArrivalMap->FindBin(
x * o2::iotof::Digitizer::cm2um,
y * o2::iotof::Digitizer::cm2um);
356 timeOfArrivalOffset = mTimeOfArrivalMap->GetBinContent(bin) * o2::iotof::Digitizer::ps2ns;
357 LOG(
debug) <<
"Time of arrival map check: x=" <<
x * o2::iotof::Digitizer::cm2um <<
", y=" <<
y * o2::iotof::Digitizer::cm2um <<
", bin=" << bin <<
", time offset=" << timeOfArrivalOffset;
360 if (digitizerParams.timeResolution > 0) {
361 return time + gRandom->Gaus(timeOfArrivalOffset, resolution);
367int Digitizer::energyToCharge(
float energyLoss)
const
373 return static_cast<int>(energyLoss * digitizerParams.energyToNElectrons);
378void Digitizer::loadMap(TH2D*& map,
const std::string&
path,
const char* mapName)
382 static constexpr std::string_view ccdbPrefix =
"ccdb://";
388 TH2D* rawMap =
nullptr;
389 TFile*
file =
nullptr;
391 if (
path.rfind(ccdbPrefix, 0) == 0) {
392 const std::string
ccdbPath =
path.substr(ccdbPrefix.size());
393 LOG(info) <<
"Loading " << mapName <<
" from CCDB: " <<
ccdbPath;
396 LOG(error) <<
"Failed to retrieve " << mapName <<
" from CCDB path: " <<
ccdbPath;
400 LOG(info) <<
"Loading " << mapName <<
" from file: " <<
path;
402 if (!file || !
file->IsOpen()) {
403 LOG(error) <<
"Failed to open file: " <<
path;
407 rawMap =
dynamic_cast<TH2D*
>(
file->Get(mapName));
409 LOG(error) <<
"Failed to retrieve " << mapName <<
" from file: " <<
path;
410 LOG(error) <<
"Available keys in the file:";
411 TIter next(
file->GetListOfKeys());
413 while ((
key =
dynamic_cast<TKey*
>(next()))) {
414 LOG(error) <<
" " <<
key->GetName() <<
" (" <<
key->GetClassName() <<
")";
422 map =
dynamic_cast<TH2D*
>(rawMap->Clone());
423 map->SetDirectory(
nullptr);
424 LOG(info) <<
"Loaded " << mapName <<
" (" << map->GetNbinsX() <<
" x " << map->GetNbinsY() <<
" bins)";
432void Digitizer::prepareScaledResolutionMap()
434 if (!mResolutionMap) {
435 LOG(warn) <<
"No resolution map available to prepare scaled resolution map.";
440 const float nominalTimeResolution = digitizerParams.timeResolution;
441 const float minimumResolution = mResolutionMap->GetMinimum();
442 if (minimumResolution <= 0) {
443 LOG(warn) <<
"Minimum resolution in the map is non-positive, cannot prepare scaled resolution map.";
447 mScaledResolutionMap =
dynamic_cast<TH2D*
>(mResolutionMap->Clone(
"hScaledResolutionMap"));
448 mScaledResolutionMap->SetDirectory(
nullptr);
450 for (
int binX = 1; binX <= mScaledResolutionMap->GetNbinsX(); ++binX) {
451 for (
int binY = 1; binY <= mScaledResolutionMap->GetNbinsY(); ++binY) {
452 float originalValue = mScaledResolutionMap->GetBinContent(binX, binY);
453 float scalingValue = originalValue / minimumResolution;
454 mScaledResolutionMap->SetBinContent(binX, binY, scalingValue * nominalTimeResolution);
460bool Digitizer::isEfficient(
const float x,
const float z)
const
464 if (mEfficiencyMap) {
466 int bin = mEfficiencyMap->FindBin(
x * o2::iotof::Digitizer::cm2um,
z * o2::iotof::Digitizer::cm2um);
467 float efficiency = mEfficiencyMap->GetBinContent(bin);
468 LOG(
debug) <<
"Efficiency map check: x=" <<
x * o2::iotof::Digitizer::cm2um <<
", z=" <<
z * o2::iotof::Digitizer::cm2um <<
", bin=" << bin <<
", efficiency=" << efficiency;
469 return gRandom->Uniform() < efficiency;
471 return gRandom->Uniform() < digitizerParams.efficiency;
477 LOG(info) <<
"Filling output container with digits from chips";
478 LOG(
debug) <<
"Number of chips: " << mChips.size();
485 const auto* extraLabelBuffer = mExtraLabelBuffer.empty() ? nullptr : mExtraLabelBuffer.front().get();
486 for (
auto& chip : mChips) {
488 if (chip.isDisabled()) {
494 if (chip.isEmpty()) {
498 auto& chipDigits = chip.getDigits();
499 auto& tdcStates = chip.getTDCStates();
501 for (
const auto& [
key, digit] : chipDigits) {
503 if (digit.getCharge() < digitizerParams.chargeThreshold) {
507 const int colInGroup = digit.getColumn() / digitizerParams.nColsPerGroup;
508 const double digitTime = digit.getTime();
509 if (digitTime - tdcStates[colInGroup][0] < digitizerParams.tdcBusyTime && digitTime - tdcStates[colInGroup][1] < digitizerParams.tdcBusyTime) {
512 }
else if (digitTime - tdcStates[colInGroup][0] > digitizerParams.tdcBusyTime) {
513 tdcStates[colInGroup][0] = digitTime;
514 }
else if (digitTime - tdcStates[colInGroup][1] > digitizerParams.tdcBusyTime) {
515 tdcStates[colInGroup][1] = digitTime;
518 int digitID = mDigits->size();
519 mDigits->emplace_back(digit.getChipIndex(), digit.getRow(), digit.getColumn(), digit.getCharge(), digit.getTime(), digit.getBc(), digit.getTdc());
521 mMCLabels->
addElement(digitID, digit.getLabel().mLabel);
523 auto labelRef = digit.getLabel();
525 while (mMCLabels && extraLabelBuffer !=
nullptr && labelRef.mNext >= 0) {
526 labelRef = (*extraLabelBuffer)[labelRef.mNext];
527 mMCLabels->
addElement(digitID, labelRef.mLabel);
534 rof.
setBCData(mContinuous ? mROFRecordIR : mEventTime);
535 mROFRecords->push_back(rof);
536 LOG(
debug) <<
"Created ROF record with " << mDigits->size() <<
" digits";
543void Digitizer::registerDigits(
Chip& chip, uint32_t roFrame,
double time,
int nROF,
550 uint64_t nbc =
static_cast<uint64_t
>(
time / o2::constants::lhc::LHCBunchSpacingNS);
551 int tdc = int((
time - nbc * o2::constants::lhc::LHCBunchSpacingNS) / digitizerParams.tdcBin);
552 nbc += mEventTime.
toLong();
554 double absoluteTime = tdc * digitizerParams.tdcBin + nbc * o2::constants::lhc::LHCBunchSpacingNS;
559 if (!existingDigit) {
564 const int storedCharge = existingDigit->
getCharge();
565 existingDigit->
setCharge(storedCharge + nElectrons);
570 std::vector<o2::iotof::McLabelRef>* extra = getExtraLabelBuffer(roFrame);
571 auto labelRef = existingDigit->
getLabel();
572 const auto next =
static_cast<int>(extra->size());
573 extra->emplace_back(
label, labelRef.mNext);
574 labelRef.
mNext = next;
577 LOG(
debug) <<
"Registered digit at (row,col) = (" <<
row <<
", " <<
col <<
") with charge: " << nElectrons <<
", time: " <<
time <<
", nbc: " << nbc <<
", tdc: " << tdc;
std::string ccdbPath(const std::string badChannelType)
Definition of the ALICE3 TOF digitizer.
math_utils::Point3D< T > GetPos() const
unsigned short GetDetectorID() const
static BasicCCDBManager & instance()
T * get(std::string const &path)
retrieve an object of type T from CCDB as stored under path; will use the timestamp member
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 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
GLsizei const GLchar *const * path
GLdouble GLdouble GLdouble z
int32_t const char * file
double getTimeOffsetWrtBC() const
int mNext
eventual next contribution to the same pixel
o2::MCCompLabel mLabel
hit label
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"