Skip to content

Commit fce05cf

Browse files
[ALICE3] added segmentation to the particle propagation in TOF3 (#15591)
* adding stepping in digitization * added segmentation to the digitization * added non-segmented version, with configurable choice * clang format * removed redundant hit response in the middle, nsteps=1 is now default * Charge threshold 0 by defult, using efficiency set by the digitizer Changed chargeThreshold from 75 to 0. * Please consider the following formatting changes --------- Co-authored-by: ALICE Action Bot <alibuild@cern.ch>
1 parent 656fd89 commit fce05cf

4 files changed

Lines changed: 129 additions & 24 deletions

File tree

Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,29 +39,29 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache
3939
static const char* getIOTOFVolPattern() { return sIOTOFVolumeName.c_str(); }
4040

4141
// Inner TOF
42-
const int getITOFNumberOfChips() { return mNumberOfChipsIOTOF[0]; }
42+
const int getITOFNumberOfChips() const { return mNumberOfChipsIOTOF[0]; }
4343
static const char* getITOFLayerPattern() { return sITOFLayerName.c_str(); }
4444
static const char* getITOFStavePattern() { return sITOFStaveName.c_str(); }
4545
static const char* getITOFModulePattern() { return sITOFModuleName.c_str(); }
4646
static const char* getITOFChipPattern() { return sITOFChipName.c_str(); }
4747
static const char* getITOFSensorPattern() { return sITOFSensorName.c_str(); }
4848

4949
// Outer TOF
50-
const int getOTOFNumberOfChips() { return mNumberOfChipsIOTOF[1]; }
50+
const int getOTOFNumberOfChips() const { return mNumberOfChipsIOTOF[1]; }
5151
static const char* getOTOFLayerPattern() { return sOTOFLayerName.c_str(); }
5252
static const char* getOTOFStavePattern() { return sOTOFStaveName.c_str(); }
5353
static const char* getOTOFModulePattern() { return sOTOFModuleName.c_str(); }
5454
static const char* getOTOFChipPattern() { return sOTOFChipName.c_str(); }
5555
static const char* getOTOFSensorPattern() { return sOTOFSensorName.c_str(); }
5656

5757
// Forward TOF
58-
const int getFTOFNumberOfChips() { return mNumberOfChipsFTOF; }
58+
const int getFTOFNumberOfChips() const { return mNumberOfChipsFTOF; }
5959
static const char* getFTOFLayerPattern() { return sFTOFLayerName.c_str(); }
6060
static const char* getFTOFChipPattern() { return sFTOFChipName.c_str(); }
6161
static const char* getFTOFSensorPattern() { return sFTOFSensorName.c_str(); }
6262

6363
// Backward TOF
64-
const int getBTOFNumberOfChips() { return mNumberOfChipsBTOF; }
64+
const int getBTOFNumberOfChips() const { return mNumberOfChipsBTOF; }
6565
static const char* getBTOFLayerPattern() { return sBTOFLayerName.c_str(); }
6666
static const char* getBTOFChipPattern() { return sBTOFChipName.c_str(); }
6767
static const char* getBTOFSensorPattern() { return sBTOFSensorName.c_str(); }

Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/DPLDigitizerParam.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ struct DPLDigitizerParam : public o2::conf::ConfigurableParamHelper<DPLDigitizer
2727
float noisePerPixel = DEFNoisePerPixel(); ///< ALPIDE Noise per channel
2828

2929
double timeOffset = 0.; ///< time offset (in seconds!) to calculate ROFrame from hit time
30-
int chargeThreshold = 75; ///< charge threshold in Nelectrons
30+
int chargeThreshold = 0; ///< charge threshold in Nelectrons
3131
int minChargeToAccount = 7; ///< minimum charge contribution to account
32-
int nSimSteps = 475; ///< number of steps in response simulation
32+
int nSimSteps = 1; ///< number of steps in response simulation
3333
float energyToNElectrons = 1. / 3.6e-9; // conversion of eloss to Nelectrons
34+
int responseMatrixSize = 1; ///< size of the response matrix (odd number)
3435

3536
std::string noiseFilePath{}; ///< optional noise masks file path. FIXME to be removed once switch to CCDBFetcher
3637

Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ class Digitizer : public TObject
9393
void registerDigits(Chip& chip, uint32_t roFrame, double time, int nROF,
9494
uint16_t row, uint16_t col, int nElectrons, o2::MCCompLabel& label);
9595

96+
void stepping(const o2::itsmft::Hit& hit, float**& respMatrix, int& rowStart, int& colStart, int& rowSpan, int& colSpan);
97+
9698
/// Apply time smearing to simulate detector resolution
9799
double smearTime(double time) const;
98100

Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx

Lines changed: 120 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
///
1818

1919
#include "IOTOFSimulation/Digitizer.h"
20+
#include "IOTOFSimulation/DPLDigitizerParam.h"
2021
#include "DetectorsRaw/HBFUtils.h"
2122

2223
#include <TRandom.h>
@@ -30,7 +31,6 @@ namespace o2::iotof
3031
{
3132

3233
o2::iotof::Segmentation* Digitizer::sSegmentation = nullptr;
33-
3434
//_______________________________________________________________________
3535
void Digitizer::init()
3636
{
@@ -100,7 +100,7 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID)
100100
}
101101

102102
// Get detector element ID
103-
int chipID = hit.GetDetectorID();
103+
const int chipID = hit.GetDetectorID();
104104
auto& chip = mChips[chipID];
105105
if (chip.isDisabled()) {
106106
LOG(debug) << "Hit rejected because chip " << chipID << " is disabled";
@@ -110,6 +110,8 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID)
110110
// Convert energy loss to charge (number of electrons)
111111
float energyLoss = hit.GetEnergyLoss(); // in GeV
112112
int charge = energyToCharge(energyLoss);
113+
const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
114+
int electronsPerStep = static_cast<int>(charge / digitizerParams.nSimSteps);
113115

114116
// Apply charge threshold
115117
if (charge < mChargeThreshold) {
@@ -128,25 +130,126 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID)
128130
LOG(debug) << "Invalid detector ID: " << chipID << ", geometry size: " << mGeometry->getSize();
129131
return; // invalid detector ID
130132
}
133+
134+
// Create the digit with time information
135+
o2::MCCompLabel label(hit.GetTrackID(), evID, srcID, false);
136+
const int roFrameAbs = 0; // For now, we can set this to 0 or calculate based on time if needed
137+
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
138+
139+
float** respMatrix = nullptr;
140+
int rowStart = 0, colStart = 0, rowSpan = 0, colSpan = 0;
141+
stepping(hit, respMatrix, rowStart, colStart, rowSpan, colSpan);
142+
143+
for (int irow = rowSpan; irow--;) {
144+
uint16_t rowIS = irow + rowStart;
145+
for (int icol = colSpan; icol--;) {
146+
uint16_t colIS = icol + colStart;
147+
float nEleResp = respMatrix[irow][icol];
148+
if (!nEleResp) {
149+
continue;
150+
}
151+
const int nElectronsSampled = gRandom->Poisson(electronsPerStep * nEleResp);
152+
// Noise can be added here if needed
153+
154+
registerDigits(chip, roFrameAbs, smearedTime, nROF,
155+
static_cast<uint16_t>(rowIS), static_cast<uint16_t>(colIS), nElectronsSampled, label);
156+
}
157+
}
158+
159+
for (int irow = 0; irow < rowSpan; ++irow) {
160+
delete[] respMatrix[irow];
161+
}
162+
delete[] respMatrix;
163+
}
164+
165+
void Digitizer::stepping(const o2::itsmft::Hit& hit, float**& respMatrix, int& rowStart, int& colStart, int& rowSpan, int& colSpan)
166+
{
131167
const auto& matrix = mGeometry->getMatrixL2G(hit.GetDetectorID());
168+
const int chipID = hit.GetDetectorID();
169+
const int subdetectorID = mGeometry->getIOTOFLayer(chipID);
170+
171+
auto xyzPositionStart(matrix ^ (hit.GetPosStart())); // start position in sensor frame
172+
auto xyzPositionEnd(matrix ^ (hit.GetPos())); // end position in sensor frame
173+
174+
const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance();
175+
const auto stepVector = (xyzPositionEnd - xyzPositionStart) / digitizerParams.nSimSteps;
176+
xyzPositionStart = xyzPositionStart + stepVector * 0.5f; // center the start position in the middle of the step
177+
xyzPositionEnd = xyzPositionEnd - stepVector * 0.5f; // center the end position in the middle of the step
178+
179+
rowStart = -1;
180+
colStart = -1;
181+
int rowEnd = -1, colEnd = -1, nSkip = 0, nSteps = digitizerParams.nSimSteps;
182+
while (!sSegmentation->localToDetector(xyzPositionStart.X(), xyzPositionStart.Z(), rowStart, colStart, mGeometry->getIOTOFLayer(chipID))) {
183+
if (++nSkip > digitizerParams.nSimSteps) { // additional check to add: should we exclude something?
184+
LOG(debug) << "Hit position out of bounds for detector ID " << chipID;
185+
return; // hit is outside the active area
186+
}
187+
xyzPositionStart += stepVector;
188+
}
189+
190+
while (!sSegmentation->localToDetector(xyzPositionEnd.X(), xyzPositionEnd.Z(), rowEnd, colEnd, mGeometry->getIOTOFLayer(chipID))) {
191+
if (++nSkip > digitizerParams.nSimSteps) { // additional check to add: should we exclude something?
192+
LOG(debug) << "Hit position out of bounds for detector ID " << chipID;
193+
return; // hit is outside the active area
194+
}
195+
xyzPositionEnd += stepVector;
196+
}
132197

133-
math_utils::Vector3D<float> xyzPositionStart(matrix ^ (hit.GetPosStart())); // start position in sensor frame
134-
// math_utils::Vector3D<float> xyzPositionEnd(matrix ^ (hit.GetPos())); // end position in sensor frame
198+
if (rowStart > rowEnd) {
199+
std::swap(rowStart, rowEnd);
200+
}
201+
if (colStart > colEnd) {
202+
std::swap(colStart, colEnd);
203+
}
135204

136-
int row = 0; // Will be determined from start hit position
137-
int col = 0; // Will be determined from start hit position
205+
// Expand the range to take into account the effects of charge sharing
206+
rowStart -= digitizerParams.responseMatrixSize / 2;
207+
rowEnd += digitizerParams.responseMatrixSize / 2;
208+
rowStart = std::max(rowStart, 0);
209+
colStart = std::max(colStart, 0);
138210

139-
if (!sSegmentation->localToDetector(xyzPositionStart.X(), xyzPositionStart.Z(), row, col, mGeometry->getIOTOFLayer(chipID))) {
140-
LOG(debug) << "Hit position out of bounds for detector ID " << chipID;
141-
return; // hit is outside the active area
211+
rowEnd = std::min(rowEnd, (subdetectorID == 0 ? sSegmentation->mITofSpecsConfig.NRows : sSegmentation->mOTofSpecsConfig.NRows) - 1);
212+
colEnd = std::min(colEnd, (subdetectorID == 0 ? sSegmentation->mITofSpecsConfig.NCols : sSegmentation->mOTofSpecsConfig.NCols) - 1);
213+
rowSpan = rowEnd - rowStart + 1;
214+
colSpan = colEnd - colStart + 1;
215+
216+
respMatrix = new float*[rowSpan];
217+
for (int i = 0; i < rowSpan; ++i) {
218+
respMatrix[i] = new float[colSpan]();
142219
}
143220

144-
// Create the digit with time information
145-
o2::MCCompLabel label(hit.GetTrackID(), evID, srcID, false);
146-
const int roFrameAbs = 0; // For now, we can set this to 0 or calculate based on time if needed
147-
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
221+
int rowPrev = -1, colPrev = -1, row = 0, col = 0;
222+
if (!respMatrix || rowSpan <= 0 || colSpan <= 0) {
223+
return;
224+
}
225+
if (nSkip) {
226+
nSteps -= nSkip;
227+
}
148228

149-
registerDigits(chip, roFrameAbs, smearedTime, nROF, static_cast<uint16_t>(row), static_cast<uint16_t>(col), charge, label);
229+
auto& currentPosLocal = xyzPositionStart;
230+
for (int iStep = nSteps; iStep--;) {
231+
sSegmentation->localToDetector(currentPosLocal.X(), currentPosLocal.Z(), row, col, subdetectorID);
232+
if (row != rowPrev || col != colPrev) {
233+
rowPrev = row;
234+
colPrev = col;
235+
}
236+
237+
currentPosLocal += stepVector; // Move to the next step position
238+
239+
for (int irow = digitizerParams.responseMatrixSize; irow--;) {
240+
int rowDest = row + irow - (digitizerParams.responseMatrixSize / 2) - rowStart; // destination row in the respMatrix
241+
if (rowDest < 0 || rowDest >= rowSpan) {
242+
continue;
243+
}
244+
for (int icol = digitizerParams.responseMatrixSize; icol--;) {
245+
int colDest = col + icol - (digitizerParams.responseMatrixSize / 2) - colStart; // destination column in the respMatrix
246+
if (colDest < 0 || colDest >= colSpan) {
247+
continue;
248+
}
249+
respMatrix[rowDest][colDest] += 1.;
250+
}
251+
}
252+
}
150253
}
151254

152255
//_______________________________________________________________________
@@ -200,10 +303,9 @@ void Digitizer::fillOutputContainer()
200303
auto& chipDigits = chip.getDigits();
201304
for (const auto& [key, digit] : chipDigits) {
202305

203-
/// Charge threshold not implemented yet
204-
/// if (digit.getCharge() < mChargeThreshold) {
205-
/// continue; // skip digits below threshold
206-
/// }
306+
if (digit.getCharge() < mChargeThreshold) {
307+
continue; // skip digits below threshold
308+
}
207309

208310
int digitID = mDigits->size();
209311
mDigits->emplace_back(digit.getChipIndex(), digit.getRow(), digit.getColumn(), digit.getCharge(), digit.getTime());

0 commit comments

Comments
 (0)