23#include <TCollection.h>
32#include <fairlogger/Logger.h>
41 const int numberOfChips = mGeometry->
getSize();
42 mChips.resize(numberOfChips);
43 for (
int i = numberOfChips;
i--;) {
44 mChips[
i].setChipIndex(
i);
58 if (!digitizerParams.efficiencyFilePath.empty()) {
59 loadEfficiencyMap(digitizerParams.efficiencyFilePath);
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();
74 LOG(
debug) <<
"Digitizing IOTOF hits: " << hits->size() <<
" hits from event " << evID <<
" source " << srcID;
76 if (!hits || hits->empty()) {
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();
89 for (
int i : hitIdx) {
90 processHit((*hits)[
i], evID, srcID);
95 LOG(
debug) <<
"Inner flushing for non-continuous mode";
101void Digitizer::processHit(
const o2::itsmft::Hit& hit,
int evID,
int srcID)
107 if (chipID < 0 || chipID >= mGeometry->
getSize() || mGeometry->
getSize() < 1) {
108 LOG(
debug) <<
"Invalid detector ID: " << chipID <<
", geometry size: " << mGeometry->
getSize();
113 auto& chip = mChips[chipID];
114 if (chip.isDisabled()) {
115 LOG(
debug) <<
"Hit rejected because chip " << chipID <<
" is disabled";
121 int charge = energyToCharge(energyLoss);
123 int electronsPerStep =
static_cast<int>(
charge / digitizerParams.nSimSteps);
126 if (
charge < digitizerParams.chargeThreshold) {
127 LOG(
debug) <<
"Hit rejected by charge threshold: " <<
charge <<
" < " << digitizerParams.chargeThreshold;
133 double hitTime = hit.
GetTime() * sec2ns;
135 double hitTimeWrtBC = hitTime + eventTimeInBC;
136 double smearedTime = smearTime(hitTimeWrtBC);
140 const int roFrameAbs = 0;
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);
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];
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;
166 const int nElectronsSampled = gRandom->Poisson(electronsPerStep * nEleResp);
169 registerDigits(chip, roFrameAbs, smearedTime, nROF,
170 static_cast<uint16_t
>(rowIS),
static_cast<uint16_t
>(colIS), nElectronsSampled,
label);
174 for (
int irow = 0; irow < rowSpan; ++irow) {
175 delete[] respMatrix[irow];
176 delete[] avgHitLocalX[irow];
177 delete[] avgHitLocalZ[irow];
180 delete[] avgHitLocalX;
181 delete[] avgHitLocalZ;
184void Digitizer::stepping(
const o2::itsmft::Hit& hit,
float**& respMatrix,
float**& avgHitLocalX,
float**& avgHitLocalZ,
int& rowStart,
int& colStart,
int& rowSpan,
int& colSpan)
186 LOG(
debug) <<
"\n\nPerforming stepping";
191 auto xyzPositionStart(matrix ^ (hit.
GetPosStart()));
192 auto xyzPositionEnd(matrix ^ (hit.
GetPos()));
195 const auto stepVector = (xyzPositionEnd - xyzPositionStart) / digitizerParams.nSimSteps;
196 xyzPositionStart = xyzPositionStart + stepVector * 0.5f;
197 xyzPositionEnd = xyzPositionEnd - stepVector * 0.5f;
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) {
204 LOG(
debug) <<
"Hit position out of bounds for detector ID " << chipID;
207 xyzPositionStart += stepVector;
211 if (++nSkip > digitizerParams.nSimSteps) {
212 LOG(
debug) <<
"Hit position out of bounds for detector ID " << chipID;
215 xyzPositionEnd -= stepVector;
218 if (rowStart > rowEnd) {
219 std::swap(rowStart, rowEnd);
221 if (colStart > colEnd) {
222 std::swap(colStart, colEnd);
226 rowStart -= digitizerParams.responseMatrixSize / 2;
227 rowEnd += digitizerParams.responseMatrixSize / 2;
228 rowStart = std::max(rowStart, 0);
229 colStart = std::max(colStart, 0);
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;
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]();
246 if (!respMatrix || !avgHitLocalX || !avgHitLocalZ || rowSpan <= 0 || colSpan <= 0) {
253 int rowPrev = -1, colPrev = -1,
row = 0,
col = 0;
254 auto pixelCurrentPosLocal = xyzPositionStart;
255 auto pixelStartPosLocal = xyzPositionStart;
256 for (
int iStep = nSteps; iStep--;) {
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;
267 if (
row != rowPrev ||
col != colPrev) {
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());
280 pixelStartPosLocal = pixelCurrentPosLocal;
283 pixelCurrentPosLocal += stepVector;
285 for (
int irow = digitizerParams.responseMatrixSize; irow--;) {
286 int rowDest =
row + irow - (digitizerParams.responseMatrixSize / 2) - rowStart;
287 if (rowDest < 0 || rowDest >= rowSpan) {
290 for (
int icol = digitizerParams.responseMatrixSize; icol--;) {
291 int colDest =
col + icol - (digitizerParams.responseMatrixSize / 2) - colStart;
292 if (colDest < 0 || colDest >= colSpan) {
295 respMatrix[rowDest][colDest] += 1.;
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());
310double Digitizer::smearTime(
double time)
const
314 if (digitizerParams.timeResolution > 0) {
315 return time + gRandom->Gaus(0, digitizerParams.timeResolution);
321int Digitizer::energyToCharge(
float energyLoss)
const
327 return static_cast<int>(energyLoss * digitizerParams.energyToNElectrons);
331void Digitizer::loadEfficiencyMap(
const std::string& filePath)
334 TFile*
file = TFile::Open(filePath.c_str());
335 if (!file || !
file->IsOpen()) {
336 LOG(error) <<
"Failed to open efficiency map file: " << filePath;
340 auto* rawMap =
dynamic_cast<TH2D*
>(
file->Get(
"hEfficiencyMap"));
342 LOG(error) <<
"Failed to retrieve efficiency map from file: " << filePath;
343 LOG(error) <<
"Available keys in the file:";
344 TIter next(
file->GetListOfKeys());
346 while ((
key =
dynamic_cast<TKey*
>(next()))) {
347 LOG(error) <<
" " <<
key->GetName() <<
" (" <<
key->GetClassName() <<
")";
352 mEfficiencyMap =
dynamic_cast<TH2D*
>(rawMap->Clone(
"mEfficiencyMap"));
353 mEfficiencyMap->SetDirectory(
nullptr);
359bool Digitizer::isEfficient(
const float x,
const float z)
const
363 if (mEfficiencyMap) {
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;
370 return gRandom->Uniform() < digitizerParams.efficiency;
376 LOG(info) <<
"Filling output container with digits from chips";
377 LOG(
debug) <<
"Number of chips: " << mChips.size();
384 const auto* extraLabelBuffer = mExtraLabelBuffer.empty() ? nullptr : mExtraLabelBuffer.front().get();
385 for (
auto& chip : mChips) {
387 if (chip.isDisabled()) {
393 if (chip.isEmpty()) {
397 auto& chipDigits = chip.getDigits();
398 for (
const auto& [
key, digit] : chipDigits) {
400 if (digit.getCharge() < digitizerParams.chargeThreshold) {
404 int digitID = mDigits->size();
405 mDigits->emplace_back(digit.getChipIndex(), digit.getRow(), digit.getColumn(), digit.getCharge(), digit.getTime(), digit.getBc(), digit.getTdc());
407 mMCLabels->
addElement(digitID, digit.getLabel().mLabel);
409 auto labelRef = digit.getLabel();
411 while (mMCLabels && extraLabelBuffer !=
nullptr && labelRef.mNext >= 0) {
412 labelRef = (*extraLabelBuffer)[labelRef.mNext];
413 mMCLabels->
addElement(digitID, labelRef.mLabel);
420 rof.
setBCData(mContinuous ? mROFRecordIR : mEventTime);
421 mROFRecords->push_back(rof);
422 LOG(
debug) <<
"Created ROF record with " << mDigits->size() <<
" digits";
429void Digitizer::registerDigits(
Chip& chip, uint32_t roFrame,
double time,
int nROF,
438 nbc += mEventTime.
toLong();
440 LOG(
debug) <<
"nbc: " << nbc <<
"\ttdc: " << tdc;
445 if (!existingDigit) {
450 const int storedCharge = existingDigit->
getCharge();
451 existingDigit->
setCharge(storedCharge + nElectrons);
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;
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...
o2::iotof::LabeledDigit * findDigit(ULong64_t key)
reset points container
void addDigit(UShort_t row, UShort_t col, Int_t charge, double time, ULong64_t bc, Int_t tdc, o2::MCCompLabel label)
static ULong64_t getOrderingKey(ULong64_t bc, UShort_t row, UShort_t col)
void setTime(double time)
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)
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.
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
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLdouble GLdouble GLdouble z
constexpr double LHCBunchSpacingNS
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"