From 999225eba59e9ac5d34ff4dded9e17d7e49bf67b Mon Sep 17 00:00:00 2001 From: marcellocosti Date: Wed, 16 Sep 2026 13:03:59 +0200 Subject: [PATCH 1/4] Improve digit efficiency in stepping --- .../ALICE3/IOTOF/macros/CheckDigitsIOTOF.C | 5 +- .../ALICE3/IOTOF/simulation/src/Digitizer.cxx | 59 ++++++++++--------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C index d83db9f0f03d2..6caf2eb471b50 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/CheckDigitsIOTOF.C @@ -75,7 +75,8 @@ void addTLines(float pitch) gPad->Update(); } -void CheckDigitsIOTOF(std::string digifile = "tf3digits.root", std::string hitfile = "o2sim_HitsTF3.root", std::string inputGeom = "o2sim_geometry.root") +void CheckDigitsIOTOF(std::string digifile = "tf3digits.root", std::string hitfile = "o2sim_HitsTF3.root", std::string inputGeom = "o2sim_geometry.root", + std::string cfgStr = "IOTOFBase.segmentedInnerTOF=true;IOTOFBase.segmentedOuterTOF=true;IOTOFBase.enableForwardTOF=false;IOTOFBase.enableBackwardTOF=false;") { gStyle->SetPalette(55); @@ -85,7 +86,7 @@ void CheckDigitsIOTOF(std::string digifile = "tf3digits.root", std::string hitfi using o2::iotof::Digit; using o2::itsmft::Hit; - o2::conf::ConfigurableParam::updateFromString("IOTOFBase.segmentedInnerTOF=true;IOTOFBase.segmentedOuterTOF=true;IOTOFBase.enableForwardTOF=false;IOTOFBase.enableBackwardTOF=false"); + o2::conf::ConfigurableParam::updateFromString(cfgStr); auto seg = o2::iotof::Segmentation::Instance(); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx index 67f651af919c6..6209395c99e48 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx @@ -110,38 +110,12 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID) return; } - // middle position of the hit in the sensor frame - const auto& matrix = mGeometry->getMatrixL2G(chipID); - auto xyzPositionStart = matrix ^ hit.GetPosStart(); - auto xyzPositionEnd = matrix ^ hit.GetPos(); - const auto xMid = 0.5f * (xyzPositionStart.X() + xyzPositionEnd.X()); - const auto zMid = 0.5f * (xyzPositionStart.Z() + xyzPositionEnd.Z()); - // move this to the local pixel coordinates for the efficiency map - int row, col; - float xPixelCenter, zPixelCenter; - if (!sSegmentation->localToDetector(xMid, zMid, row, col, mGeometry->getIOTOFLayer(chipID))) { - LOG(debug) << "Hit rejected because position (" << xMid << ", " << zMid << ") is outside the active area of chip " << chipID; - return; // hit is outside the active area - } - sSegmentation->detectorToLocalUnchecked(row, col, xPixelCenter, zPixelCenter, mGeometry->getIOTOFLayer(chipID)); - - if (!isEfficient(xMid - xPixelCenter, zMid - zPixelCenter)) { - LOG(debug) << "Hit rejected by efficiency cut"; - return; - } - // Convert energy loss to charge (number of electrons) float energyLoss = hit.GetEnergyLoss(); // in GeV int charge = energyToCharge(energyLoss); const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance(); int electronsPerStep = static_cast(charge / digitizerParams.nSimSteps); - // Apply charge threshold - if (charge < digitizerParams.chargeThreshold) { - LOG(debug) << "Hit rejected by charge threshold: " << charge << " < " << digitizerParams.chargeThreshold; - return; - } - // Get hit time and apply smearing // Hit time is in seconds, convert to ns and add event time double hitTime = hit.GetTime() * sec2ns; // convert to ns @@ -172,6 +146,11 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID) continue; } const int nElectronsSampled = gRandom->Poisson(electronsPerStep * nEleResp); + // Apply charge threshold cut taking into account fraction of charge in the pixel + if (nElectronsSampled < digitizerParams.chargeThreshold) { + LOG(debug) << "Hit rejected by charge threshold: " << nElectronsSampled << " < " << digitizerParams.chargeThreshold; + continue; + } // Noise can be added here if needed registerDigits(chip, roFrameAbs, smearedTime, nROF, @@ -242,7 +221,6 @@ void Digitizer::stepping(const o2::itsmft::Hit& hit, float**& respMatrix, int& r respMatrix[i] = new float[colSpan](); } - int rowPrev = -1, colPrev = -1, row = 0, col = 0; if (!respMatrix || rowSpan <= 0 || colSpan <= 0) { return; } @@ -250,14 +228,37 @@ void Digitizer::stepping(const o2::itsmft::Hit& hit, float**& respMatrix, int& r nSteps -= nSkip; } + int rowPrev = -1, colPrev = -1, row = 0, col = 0; auto& currentPosLocal = xyzPositionStart; + float xPixelCenter, zPixelCenter; for (int iStep = nSteps; iStep--;) { - sSegmentation->localToDetector(currentPosLocal.X(), currentPosLocal.Z(), row, col, subdetectorID); + + // Check whether the mid-position of the step is within the active area of the chip + if (!sSegmentation->localToDetector(currentPosLocal.X(), currentPosLocal.Z(), row, col, subdetectorID)) { + LOG(debug) << "Step is in passive area: (" << currentPosLocal.X() << ", " << currentPosLocal.Z() << ") is outside the active area of chip " << subdetectorID; + currentPosLocal += stepVector; + continue; + } + + // Update the pixel center coordinates if the row or column has changed if (row != rowPrev || col != colPrev) { + if (!sSegmentation->detectorToLocal(row, col, xPixelCenter, zPixelCenter, subdetectorID)) { + LOG(debug) << "Failed to get pixel center for row " << row << ", col " << col << ", chip " << chipID; + currentPosLocal += stepVector; + continue; + } rowPrev = row; colPrev = col; } + // Apply efficiency cut based on the step position relative to the pixel center + if (!isEfficient(currentPosLocal.X() - xPixelCenter, currentPosLocal.Z() - zPixelCenter)) { + LOG(debug) << "Step rejected by efficiency cut"; + currentPosLocal += stepVector; + continue; + } + + LOG(debug) << "Step accepted: (" << currentPosLocal.X() << ", " << currentPosLocal.Z() << ") in chip " << subdetectorID; currentPosLocal += stepVector; // Move to the next step position for (int irow = digitizerParams.responseMatrixSize; irow--;) { @@ -407,7 +408,7 @@ void Digitizer::registerDigits(Chip& chip, uint32_t roFrame, double time, int nR int tdc = int((time - nbc * o2::constants::lhc::LHCBunchSpacingNS) / digitizerParams.tdcBin); nbc += mEventTime.toLong(); - LOG(debug) << nbc << "\t" << tdc; + LOG(debug) << "nbc: " << nbc << "\ttdc: " << tdc; double absoluteTime = tdc * digitizerParams.tdcBin * 1.e-9 + nbc * o2::constants::lhc::LHCBunchSpacingNS; auto key = o2::iotof::Digit::getOrderingKey(nbc, row, col); From b874eb4872fa8709bbfb726e8aa222bcd514a7d4 Mon Sep 17 00:00:00 2001 From: marcellocosti Date: Wed, 16 Sep 2026 14:16:33 +0200 Subject: [PATCH 2/4] Switch on stepping --- .../simulation/include/IOTOFSimulation/DPLDigitizerParam.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/DPLDigitizerParam.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/DPLDigitizerParam.h index 2f9b0c3c2e090..7f96b8e509d0c 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/DPLDigitizerParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/DPLDigitizerParam.h @@ -34,7 +34,7 @@ struct DPLDigitizerParam : public o2::conf::ConfigurableParamHelper Date: Wed, 16 Sep 2026 17:36:14 +0200 Subject: [PATCH 3/4] Implement Mario and Giorgio comments --- .../include/IOTOFSimulation/Digitizer.h | 2 +- .../ALICE3/IOTOF/simulation/src/Digitizer.cxx | 94 ++++++++++++------- 2 files changed, 63 insertions(+), 33 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h index 117bc88ca3ab0..6d55e33d5461b 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h @@ -83,7 +83,7 @@ class Digitizer : public TObject void registerDigits(Chip& chip, uint32_t roFrame, double time, int nROF, uint16_t row, uint16_t col, int nElectrons, o2::MCCompLabel& label); - void stepping(const o2::itsmft::Hit& hit, float**& respMatrix, int& rowStart, int& colStart, int& rowSpan, int& colSpan); + void stepping(const o2::itsmft::Hit& hit, float**& respMatrix, float**& avgHitLocalX, float**& avgHitLocalZ, int& rowStart, int& colStart, int& rowSpan, int& colSpan); /// Apply time smearing to simulate detector resolution double smearTime(double time) const; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx index 6209395c99e48..d3bbb8fd590e6 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx @@ -104,6 +104,12 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID) // Get detector element ID const int chipID = hit.GetDetectorID(); + if (chipID < 0 || chipID >= mGeometry->getSize() || mGeometry->getSize() < 1) { + LOG(debug) << "Invalid detector ID: " << chipID << ", geometry size: " << mGeometry->getSize(); + return; // invalid detector ID + } + const int subdetectorID = mGeometry->getIOTOFLayer(chipID); + auto& chip = mChips[chipID]; if (chip.isDisabled()) { LOG(debug) << "Hit rejected because chip " << chipID << " is disabled"; @@ -116,6 +122,12 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID) const auto& digitizerParams = o2::iotof::DPLDigitizerParam::Instance(); int electronsPerStep = static_cast(charge / digitizerParams.nSimSteps); + // Apply charge threshold + if (charge < digitizerParams.chargeThreshold) { + LOG(debug) << "Hit rejected by charge threshold: " << charge << " < " << digitizerParams.chargeThreshold; + return; + } + // Get hit time and apply smearing // Hit time is in seconds, convert to ns and add event time double hitTime = hit.GetTime() * sec2ns; // convert to ns @@ -123,20 +135,18 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID) double hitTimeWrtBC = hitTime + eventTimeInBC; // hit time wrt bc double smearedTime = smearTime(hitTimeWrtBC); - if (chipID < 0 || chipID >= mGeometry->getSize() || mGeometry->getSize() < 1) { - LOG(debug) << "Invalid detector ID: " << chipID << ", geometry size: " << mGeometry->getSize(); - return; // invalid detector ID - } - // Create the digit with time information o2::MCCompLabel label(hit.GetTrackID(), evID, srcID, false); const int roFrameAbs = 0; // For now, we can set this to 0 or calculate based on time if needed 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 float** respMatrix = nullptr; + float** avgHitLocalX = nullptr; + float** avgHitLocalZ = nullptr; int rowStart = 0, colStart = 0, rowSpan = 0, colSpan = 0; - stepping(hit, respMatrix, rowStart, colStart, rowSpan, colSpan); + stepping(hit, respMatrix, avgHitLocalX, avgHitLocalZ, rowStart, colStart, rowSpan, colSpan); + float xPixelCenter = 0.0f, zPixelCenter = 0.0f; for (int irow = rowSpan; irow--;) { uint16_t rowIS = irow + rowStart; for (int icol = colSpan; icol--;) { @@ -145,12 +155,15 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID) if (!nEleResp) { continue; } - const int nElectronsSampled = gRandom->Poisson(electronsPerStep * nEleResp); - // Apply charge threshold cut taking into account fraction of charge in the pixel - if (nElectronsSampled < digitizerParams.chargeThreshold) { - LOG(debug) << "Hit rejected by charge threshold: " << nElectronsSampled << " < " << digitizerParams.chargeThreshold; + + // Apply efficiency cut based on the hit segment mean position relative to the pixel center + sSegmentation->detectorToLocal(rowIS, colIS, xPixelCenter, zPixelCenter, subdetectorID); + if (!isEfficient(avgHitLocalX[irow][icol] - xPixelCenter, avgHitLocalZ[irow][icol] - zPixelCenter)) { + LOG(debug) << "Hit rejected by efficiency cut at pixel (" << rowIS << ", " << colIS << ") in chip " << chipID; continue; } + + const int nElectronsSampled = gRandom->Poisson(electronsPerStep * nEleResp); // Noise can be added here if needed registerDigits(chip, roFrameAbs, smearedTime, nROF, @@ -160,12 +173,17 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID) for (int irow = 0; irow < rowSpan; ++irow) { delete[] respMatrix[irow]; + delete[] avgHitLocalX[irow]; + delete[] avgHitLocalZ[irow]; } delete[] respMatrix; + delete[] avgHitLocalX; + delete[] avgHitLocalZ; } -void Digitizer::stepping(const o2::itsmft::Hit& hit, float**& respMatrix, int& rowStart, int& colStart, int& rowSpan, int& colSpan) +void Digitizer::stepping(const o2::itsmft::Hit& hit, float**& respMatrix, float**& avgHitLocalX, float**& avgHitLocalZ, int& rowStart, int& colStart, int& rowSpan, int& colSpan) { + LOG(debug) << "\n\nPerforming stepping"; const int chipID = hit.GetDetectorID(); const auto& matrix = mGeometry->getMatrixL2G(chipID); const int subdetectorID = mGeometry->getIOTOFLayer(chipID); @@ -217,11 +235,16 @@ void Digitizer::stepping(const o2::itsmft::Hit& hit, float**& respMatrix, int& r colSpan = colEnd - colStart + 1; respMatrix = new float*[rowSpan]; + avgHitLocalX = new float*[rowSpan]; + avgHitLocalZ = new float*[rowSpan]; for (int i = 0; i < rowSpan; ++i) { respMatrix[i] = new float[colSpan](); + avgHitLocalX[i] = new float[colSpan](); + avgHitLocalZ[i] = new float[colSpan](); } - if (!respMatrix || rowSpan <= 0 || colSpan <= 0) { + if (!respMatrix || !avgHitLocalX || !avgHitLocalZ + || rowSpan <= 0 || colSpan <= 0) { return; } if (nSkip) { @@ -229,37 +252,36 @@ void Digitizer::stepping(const o2::itsmft::Hit& hit, float**& respMatrix, int& r } int rowPrev = -1, colPrev = -1, row = 0, col = 0; - auto& currentPosLocal = xyzPositionStart; - float xPixelCenter, zPixelCenter; + auto pixelCurrentPosLocal = xyzPositionStart; + auto pixelStartPosLocal = xyzPositionStart; for (int iStep = nSteps; iStep--;) { - // Check whether the mid-position of the step is within the active area of the chip - if (!sSegmentation->localToDetector(currentPosLocal.X(), currentPosLocal.Z(), row, col, subdetectorID)) { - LOG(debug) << "Step is in passive area: (" << currentPosLocal.X() << ", " << currentPosLocal.Z() << ") is outside the active area of chip " << subdetectorID; - currentPosLocal += stepVector; + // Step does not contribute if it is in the passive area + if (!sSegmentation->localToDetector(pixelCurrentPosLocal.X(), pixelCurrentPosLocal.Z(), row, col, subdetectorID)) { + LOG(debug) << "Step is in passive area: (" << pixelCurrentPosLocal.X() << ", " << pixelCurrentPosLocal.Z() << ") is outside the active area of chip " << subdetectorID; + pixelCurrentPosLocal += stepVector; continue; } - // Update the pixel center coordinates if the row or column has changed + // The step has reached another pixel, compute mean hit segment positions + // for pixel efficiency evaluation and reset the start position for the next pixel if (row != rowPrev || col != colPrev) { - if (!sSegmentation->detectorToLocal(row, col, xPixelCenter, zPixelCenter, subdetectorID)) { - LOG(debug) << "Failed to get pixel center for row " << row << ", col " << col << ", chip " << chipID; - currentPosLocal += stepVector; - continue; + + // Finalize the previous pixel + if (rowPrev != -1 && colPrev != -1) { + const int irow = rowPrev - rowStart; + const int icol = colPrev - colStart; + avgHitLocalX[irow][icol] = 0.5f * (pixelStartPosLocal.X() + pixelCurrentPosLocal.X() - stepVector.X()); + avgHitLocalZ[irow][icol] = 0.5f * (pixelStartPosLocal.Z() + pixelCurrentPosLocal.Z() - stepVector.Z()); } + + // Start the new pixel rowPrev = row; colPrev = col; + pixelStartPosLocal = pixelCurrentPosLocal; } - // Apply efficiency cut based on the step position relative to the pixel center - if (!isEfficient(currentPosLocal.X() - xPixelCenter, currentPosLocal.Z() - zPixelCenter)) { - LOG(debug) << "Step rejected by efficiency cut"; - currentPosLocal += stepVector; - continue; - } - - LOG(debug) << "Step accepted: (" << currentPosLocal.X() << ", " << currentPosLocal.Z() << ") in chip " << subdetectorID; - currentPosLocal += stepVector; // Move to the next step position + pixelCurrentPosLocal += stepVector; // Move to the next step position for (int irow = digitizerParams.responseMatrixSize; irow--;) { int rowDest = row + irow - (digitizerParams.responseMatrixSize / 2) - rowStart; // destination row in the respMatrix @@ -275,6 +297,14 @@ void Digitizer::stepping(const o2::itsmft::Hit& hit, float**& respMatrix, int& r } } } + + // Finalize the last pixel + if (rowPrev != -1 && colPrev != -1) { + const int irow = rowPrev - rowStart; + const int icol = colPrev - colStart; + avgHitLocalX[irow][icol] = 0.5f * (pixelStartPosLocal.X() + pixelCurrentPosLocal.X() - stepVector.X()); + avgHitLocalZ[irow][icol] = 0.5f * (pixelStartPosLocal.Z() + pixelCurrentPosLocal.Z() - stepVector.Z()); + } } //_______________________________________________________________________ From bfad165b34832f1086bb64039c0508aa6bac6133 Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Wed, 16 Sep 2026 15:37:09 +0000 Subject: [PATCH 4/4] Please consider the following formatting changes --- Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx index d3bbb8fd590e6..3070851431fee 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx @@ -243,8 +243,7 @@ void Digitizer::stepping(const o2::itsmft::Hit& hit, float**& respMatrix, float* avgHitLocalZ[i] = new float[colSpan](); } - if (!respMatrix || !avgHitLocalX || !avgHitLocalZ - || rowSpan <= 0 || colSpan <= 0) { + if (!respMatrix || !avgHitLocalX || !avgHitLocalZ || rowSpan <= 0 || colSpan <= 0) { return; } if (nSkip) {