Skip to content

Commit 945bf43

Browse files
committed
Fix CalculatedEdx subthreshold direction bug, add allowChangeClusters style
outer-row cut, align correction order and sin^2(phi) cap with online GPUdEdx
1 parent c13e262 commit 945bf43

2 files changed

Lines changed: 107 additions & 47 deletions

File tree

Detectors/TPC/calibration/include/TPCCalibration/CalculatedEdx.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ class CalculatedEdx
152152
/// \param maxMissingCl maximum number of missing clusters for subthreshold check
153153
void setMaxMissingCl(int maxMissingCl) { mMaxMissingCl = maxMissingCl; }
154154

155+
/// \param n subthreshold clusters are not filled within min(nRows/2, n) rows of the track's outer end, mirroring the online tracker's allowChangeClusters gate,
156+
// set to 0 to fill every 1-row gap regardless of its position on the track
157+
void setSubThreshEdgeRows(int n) { mSubThreshEdgeRows = n; }
158+
155159
/// set the debug streamer for a given output file; a new streamer is only created the first time a given debugRootFile is seen,
156160
/// so different calculatedEdx() calls using different debugRootFile names each get their own independent debug file
157161
void setStreamer(const char* debugRootFile)
@@ -174,6 +178,9 @@ class CalculatedEdx
174178
/// \return returns maxMissingCl for subthreshold cluster treatment
175179
int getMaxMissingCl() { return mMaxMissingCl; }
176180

181+
/// \return returns the outer-end row exclusion for subthreshold cluster treatment
182+
int getSubThreshEdgeRows() const { return mSubThreshEdgeRows; }
183+
177184
/// \return returns the number of rows where refit/propagation failed (row.propagationFailed) since the last resetDebugCounters()
178185
long getNPropagationFailed() const { return mNPropagationFailed; }
179186

@@ -342,10 +349,11 @@ class CalculatedEdx
342349
bool isShared;
343350
bool isCombined;
344351
bool isDeadRegion;
345-
bool propagationFailed; ///< true if refit/propagation to this row failed, or the resulting track param is NaN
346-
int missingClusters; ///< number of skipped rows since the previous entry in rowData (i.e. rowIndex - previous rowIndex - 1); same for every settings entry since rowOrder does not depend on the settings
347-
bool sameSectorAsPrevRow; ///< true if this row's sector equals the previous entry in rowData's sector
348-
bool missingClusterGapDeadOrEdge; ///< true if any of the missingClusters skipped row(s) would land on a dead channel or off the padrow edge
352+
bool propagationFailed; ///< true if refit/propagation to this row failed, or the resulting track param is NaN
353+
int missingClusters; ///< number of skipped rows since the previous entry in rowData (i.e. rowIndex - previous rowIndex - 1); same for every settings entry since rowOrder does not depend on the settings
354+
bool sameSectorAsPrevRow; ///< true if this row's sector equals the previous entry in rowData's sector
355+
bool missingClusterGapDeadOrEdge; ///< true if any of the missingClusters skipped row(s) would land on a dead channel or off the padrow edge
356+
std::vector<int> inputClusterIndices; ///< indices of the input clusters merged into this row: positions in the caller-supplied clusters vector (externally-supplied-cluster overload) or the track's cluster-reference list (reference overload). Streamed to "dEdxDebugCl" so external tooling can map a row back to its input cluster(s)
349357
};
350358

351359
/// gather, for every (sector, row) of the track's row-traversal order, performing the refit/propagation to each cluster row exactly once
@@ -397,6 +405,7 @@ class CalculatedEdx
397405
std::unique_ptr<o2::gpu::GPUO2InterfaceRefit> mRefit{nullptr}; ///< TPC refitter used for TPC tracks refit during the reconstruction
398406

399407
int mMaxMissingCl{1}; ///< maximum number of missing clusters for subthreshold check
408+
int mSubThreshEdgeRows{30}; ///< no subthreshold fill within min(nRows/2, this) rows of the track's outer end 0 disables
400409
float mFieldNominalGPUBz{5}; ///< magnetic field in kG, used for track propagation
401410
bool mPropagateTrack{false}; ///< propagating the track instead of performing a refit (faster than refit)
402411
bool mPropagateParams{false}; ///< propagating the parameters instead of full propagation (faster than track propagation)

Detectors/TPC/calibration/src/CalculatedEdx.cxx

Lines changed: 94 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#include "GPUO2InterfaceUtils.h"
2929
#include "GPUTPCGMMergedTrackHit.h"
3030

31+
#include <cstdlib>
32+
3133
using namespace o2::tpc;
3234

3335
CalculatedEdx::CalculatedEdx()
@@ -373,21 +375,26 @@ void CalculatedEdx::gatherRowClusterDataForRow(o2::tpc::TrackTPC& track, const o
373375
row.gain = mCalibCont.getGain(sectorIndex, rowIndex, pad);
374376
row.gainResidual = mCalibCont.getResidualGain(sectorIndex, rowIndex, pad);
375377

376-
// number of rows skipped since the previous row in rowOrder
377-
row.missingClusters = rowIndex - rowIndexOld - 1;
378+
// number of rows skipped between this row and the previous entry in rowOrder
378379
row.sameSectorAsPrevRow = (sectorIndexOld == sectorIndex);
380+
row.missingClusters = (rowIndexOld == 255) ? 0 : (std::abs(static_cast<int>(rowIndex) - static_cast<int>(rowIndexOld)) - 1);
379381

380382
// veto the gap as a subthreshold candidate if any of its missing row(s) would land on a dead channel or off the padrow edge
381383
row.missingClusterGapDeadOrEdge = false;
382-
if (row.missingClusters > 0 && row.missingClusters <= mMaxMissingCl && row.sameSectorAsPrevRow) {
384+
if (row.missingClusters > 0 && row.missingClusters <= mMaxMissingCl) {
383385
const o2::gpu::GPUTPCGeometry gpuGeom;
384386
const RowClusterData& prevRow = rowData.back();
385-
const float yPrev = gpuGeom.LinearPad2Y(sectorIndex, prevRow.rowIndex, prevRow.clPad);
386-
const float yCur = gpuGeom.LinearPad2Y(sectorIndex, rowIndex, row.clPad);
387+
// bracket the gap by its lower/upper real row, independent of the rowOrder direction
388+
const int rowLo = std::min<int>(rowIndex, rowIndexOld);
389+
const int rowHi = std::max<int>(rowIndex, rowIndexOld);
390+
const float padLo = (rowLo == static_cast<int>(rowIndexOld)) ? prevRow.clPad : row.clPad;
391+
const float padHi = (rowHi == static_cast<int>(rowIndexOld)) ? prevRow.clPad : row.clPad;
392+
const float yLo = gpuGeom.LinearPad2Y(sectorIndex, rowLo, padLo);
393+
const float yHi = gpuGeom.LinearPad2Y(sectorIndex, rowHi, padHi);
387394
for (int k = 1; k <= row.missingClusters; ++k) {
388-
const unsigned char missingRow = prevRow.rowIndex + k;
395+
const unsigned char missingRow = static_cast<unsigned char>(rowLo + k);
389396
const float frac = static_cast<float>(k) / (row.missingClusters + 1);
390-
const float missingPad = gpuGeom.LinearY2Pad(sectorIndex, missingRow, yPrev + (yCur - yPrev) * frac);
397+
const float missingPad = gpuGeom.LinearY2Pad(sectorIndex, missingRow, yLo + (yHi - yLo) * frac);
391398
if (missingPad < 0.f || missingPad >= gpuGeom.NPads(missingRow)) {
392399
row.missingClusterGapDeadOrEdge = true;
393400
break;
@@ -442,6 +449,7 @@ void CalculatedEdx::gatherRowClusterData(o2::tpc::TrackTPC& track, const std::ve
442449
const bool isShared = clusterInfos[clusterIdx].isShared;
443450

444451
gatherRowClusterDataForRow(track, cl, sectorIndex, rowIndex, isCombined, isShared, rowIndexOld, sectorIndexOld, occupancyROC, rowData);
452+
rowData.back().inputClusterIndices = clusterIndices; // positions in the caller-supplied clusters vector merged into this row
445453

446454
rowIndexOld = rowIndex;
447455
sectorIndexOld = sectorIndex;
@@ -492,7 +500,11 @@ void CalculatedEdx::calculatedEdxFromRowData(const std::vector<RowClusterData>&
492500
occupancyVector.reserve(rowData.size());
493501
}
494502

495-
for (const auto& row : rowData) {
503+
// a gap is not filled as a subthreshold cluster when the row that closes it sits within the outermost min(nRows/2, mSubThreshEdgeRows) rows
504+
const int edgeRowCut = std::min<int>(static_cast<int>(rowData.size()) / 2, mSubThreshEdgeRows);
505+
506+
for (size_t iRowData = 0; iRowData < rowData.size(); ++iRowData) {
507+
const auto& row = rowData[iRowData];
496508
if (mDebug) {
497509
occupancyVector.emplace_back(row.occupancy);
498510
}
@@ -550,24 +562,28 @@ void CalculatedEdx::calculatedEdxFromRowData(const std::vector<RowClusterData>&
550562
chargeTot /= effectiveLength;
551563
chargeMax /= effectiveLength;
552564
};
565+
566+
const bool gainFullApplied = (settings.correctionMask & CorrectionFlags::GainFull) == CorrectionFlags::GainFull;
567+
float topoChargeTot = chargeTot;
568+
float topoChargeMax = chargeMax;
569+
if (gainFullApplied) {
570+
gain = row.gain;
571+
chargeTot /= gain;
572+
chargeMax /= gain;
573+
} else {
574+
topoChargeTot *= row.gain;
575+
topoChargeMax *= row.gain;
576+
}
577+
578+
// topology correction
553579
if ((settings.correctionMask & CorrectionFlags::TopologyPol) == CorrectionFlags::TopologyPol) {
554-
effectiveLengthTot = getTrackTopologyCorrectionPol(row.trackSnapshot, row.cl, row.region, chargeTot, ChargeType::Tot, row.threshold);
555-
effectiveLengthMax = getTrackTopologyCorrectionPol(row.trackSnapshot, row.cl, row.region, chargeMax, ChargeType::Max, row.threshold);
580+
effectiveLengthTot = getTrackTopologyCorrectionPol(row.trackSnapshot, row.cl, row.region, topoChargeTot, ChargeType::Tot, row.threshold);
581+
effectiveLengthMax = getTrackTopologyCorrectionPol(row.trackSnapshot, row.cl, row.region, topoChargeMax, ChargeType::Max, row.threshold);
556582
chargeTot /= effectiveLengthTot;
557583
chargeMax /= effectiveLengthMax;
558584
};
559585

560-
// get gain
561-
if ((settings.correctionMask & CorrectionFlags::GainFull) == CorrectionFlags::GainFull) {
562-
gain = row.gain;
563-
};
564-
if ((settings.correctionMask & CorrectionFlags::GainResidual) == CorrectionFlags::GainResidual) {
565-
gainResidual = row.gainResidual;
566-
};
567-
chargeTot /= gain * gainResidual;
568-
chargeMax /= gain * gainResidual;
569-
570-
// get dEdx correction on tgl and sector plane
586+
// residual dE/dx correction on tgl and sector plane
571587
if ((settings.correctionMask & CorrectionFlags::dEdxResidual) == CorrectionFlags::dEdxResidual) {
572588
corrTot = mCalibCont.getResidualCorrection(row.stackID, ChargeType::Tot, row.trackSnapshot.getTgl(), row.trackSnapshot.getSnp());
573589
corrMax = mCalibCont.getResidualCorrection(row.stackID, ChargeType::Max, row.trackSnapshot.getTgl(), row.trackSnapshot.getSnp());
@@ -579,6 +595,13 @@ void CalculatedEdx::calculatedEdxFromRowData(const std::vector<RowClusterData>&
579595
};
580596
};
581597

598+
// residual gain map
599+
if ((settings.correctionMask & CorrectionFlags::GainResidual) == CorrectionFlags::GainResidual) {
600+
gainResidual = row.gainResidual;
601+
chargeTot /= gainResidual;
602+
chargeMax /= gainResidual;
603+
};
604+
582605
// space-charge dEdx corrections
583606
const float time = row.clTime - trackTime0; // ToDo: get correct time from ITS-TPC track if possible
584607
if ((settings.correctionMask & CorrectionFlags::dEdxSC) == CorrectionFlags::dEdxSC) {
@@ -630,12 +653,13 @@ void CalculatedEdx::calculatedEdxFromRowData(const std::vector<RowClusterData>&
630653
<< "residualCorrMax=" << corrMax
631654
<< "scCorr=" << scCorr
632655
<< "occupancy=" << row.occupancy
656+
<< "inputClusterIndices=" << row.inputClusterIndices
633657
<< "\n";
634658
};
635659

636660
// find missing clusters
637661
const int missingClusters = row.missingClusters;
638-
if ((missingClusters > 0) && (missingClusters <= mMaxMissingCl) && !row.missingClusterGapDeadOrEdge) {
662+
if ((missingClusters > 0) && (missingClusters <= mMaxMissingCl) && !row.missingClusterGapDeadOrEdge && (static_cast<int>(iRowData) >= edgeRowCut)) {
639663
if ((settings.clusterMask & ClusterFlags::ExcludeSectorBoundaries) == ClusterFlags::ExcludeSectorBoundaries) {
640664
if (row.sameSectorAsPrevRow) {
641665
if (row.stack == GEMstack::IROCgem) {
@@ -899,8 +923,13 @@ void CalculatedEdx::calculatedEdx(o2::tpc::TrackTPC& track, dEdxInfo& output, Av
899923
trackOrig = track; // pristine track, before refit/propagation mutates it cluster-by-cluster below
900924
}
901925

926+
// a gap is not filled as a subthreshold cluster when the row that closes it sits within the outermost min(nRows/2, mSubThreshEdgeRows) rows
927+
const int edgeRowCut = std::min<int>(static_cast<int>(rowOrder.size()) / 2, mSubThreshEdgeRows);
928+
size_t iRowData = 0;
929+
902930
// loop over the clusters in the track's true physical row-traversal order (rowOrder)
903931
for (const auto& rowKey : rowOrder) {
932+
const int iRowInTrack = static_cast<int>(iRowData++);
904933
const auto& clusterIndices = clustersByRow.at(rowKey);
905934
const unsigned char rowIndex = rowKey.second;
906935
int clusterIdx = clusterIndices[0];
@@ -1033,24 +1062,29 @@ void CalculatedEdx::calculatedEdx(o2::tpc::TrackTPC& track, dEdxInfo& output, Av
10331062
chargeTot /= effectiveLength;
10341063
chargeMax /= effectiveLength;
10351064
};
1065+
1066+
const float fullGainMapGain = mCalibCont.getGain(sectorIndex, rowIndex, pad);
1067+
const bool gainFullApplied = (correctionMask & CorrectionFlags::GainFull) == CorrectionFlags::GainFull;
1068+
float topoChargeTot = chargeTot;
1069+
float topoChargeMax = chargeMax;
1070+
if (gainFullApplied) {
1071+
gain = fullGainMapGain;
1072+
chargeTot /= gain;
1073+
chargeMax /= gain;
1074+
} else {
1075+
topoChargeTot *= fullGainMapGain;
1076+
topoChargeMax *= fullGainMapGain;
1077+
}
1078+
1079+
// topology correction
10361080
if ((correctionMask & CorrectionFlags::TopologyPol) == CorrectionFlags::TopologyPol) {
1037-
effectiveLengthTot = getTrackTopologyCorrectionPol(track, cl, region, chargeTot, ChargeType::Tot, threshold);
1038-
effectiveLengthMax = getTrackTopologyCorrectionPol(track, cl, region, chargeMax, ChargeType::Max, threshold);
1081+
effectiveLengthTot = getTrackTopologyCorrectionPol(track, cl, region, topoChargeTot, ChargeType::Tot, threshold);
1082+
effectiveLengthMax = getTrackTopologyCorrectionPol(track, cl, region, topoChargeMax, ChargeType::Max, threshold);
10391083
chargeTot /= effectiveLengthTot;
10401084
chargeMax /= effectiveLengthMax;
10411085
};
10421086

1043-
// get gain
1044-
if ((correctionMask & CorrectionFlags::GainFull) == CorrectionFlags::GainFull) {
1045-
gain = mCalibCont.getGain(sectorIndex, rowIndex, pad);
1046-
};
1047-
if ((correctionMask & CorrectionFlags::GainResidual) == CorrectionFlags::GainResidual) {
1048-
gainResidual = mCalibCont.getResidualGain(sectorIndex, rowIndex, pad);
1049-
};
1050-
chargeTot /= gain * gainResidual;
1051-
chargeMax /= gain * gainResidual;
1052-
1053-
// get dEdx correction on tgl and sector plane
1087+
// residual dE/dx correction on tgl and sector plane
10541088
if ((correctionMask & CorrectionFlags::dEdxResidual) == CorrectionFlags::dEdxResidual) {
10551089
corrTot = mCalibCont.getResidualCorrection(stackID, ChargeType::Tot, track.getTgl(), track.getSnp());
10561090
corrMax = mCalibCont.getResidualCorrection(stackID, ChargeType::Max, track.getTgl(), track.getSnp());
@@ -1062,6 +1096,13 @@ void CalculatedEdx::calculatedEdx(o2::tpc::TrackTPC& track, dEdxInfo& output, Av
10621096
};
10631097
};
10641098

1099+
// residual gain map
1100+
if ((correctionMask & CorrectionFlags::GainResidual) == CorrectionFlags::GainResidual) {
1101+
gainResidual = mCalibCont.getResidualGain(sectorIndex, rowIndex, pad);
1102+
chargeTot /= gainResidual;
1103+
chargeMax /= gainResidual;
1104+
};
1105+
10651106
// space-charge dEdx corrections
10661107
const float time = clTime - track.getTime0(); // ToDo: get correct time from ITS-TPC track if possible
10671108
if ((correctionMask & CorrectionFlags::dEdxSC) == CorrectionFlags::dEdxSC) {
@@ -1116,22 +1157,28 @@ void CalculatedEdx::calculatedEdx(o2::tpc::TrackTPC& track, dEdxInfo& output, Av
11161157
<< "residualCorrMax=" << corrMax
11171158
<< "scCorr=" << scCorr
11181159
<< "occupancy=" << occupancy
1160+
<< "inputClusterIndices=" << clusterIndices
11191161
<< "\n";
11201162
};
11211163

11221164
// find missing clusters - deliberately evaluated before (independent of) this row's own excludeCl status
1123-
int missingClusters = rowIndex - rowIndexOld - 1;
1165+
int missingClusters = (rowIndexOld == 255) ? 0 : (std::abs(static_cast<int>(rowIndex) - static_cast<int>(rowIndexOld)) - 1);
11241166

11251167
// veto the gap as a subthreshold candidate if any of its missing row(s) would land on a dead channel or off the padrow edge
11261168
bool missingClusterGapDeadOrEdge = false;
1127-
if (missingClusters > 0 && missingClusters <= mMaxMissingCl && sectorIndexOld == sectorIndex) {
1169+
if (missingClusters > 0 && missingClusters <= mMaxMissingCl) {
11281170
const o2::gpu::GPUTPCGeometry gpuGeom;
1129-
const float yPrev = gpuGeom.LinearPad2Y(sectorIndex, rowIndexOld, clPadOld);
1130-
const float yCur = gpuGeom.LinearPad2Y(sectorIndex, rowIndex, clPad);
1171+
// bracket the gap by its lower/upper real row, independent of the reference-list direction
1172+
const int rowLo = std::min<int>(rowIndex, rowIndexOld);
1173+
const int rowHi = std::max<int>(rowIndex, rowIndexOld);
1174+
const float padLo = (rowLo == static_cast<int>(rowIndexOld)) ? clPadOld : clPad;
1175+
const float padHi = (rowHi == static_cast<int>(rowIndexOld)) ? clPadOld : clPad;
1176+
const float yLo = gpuGeom.LinearPad2Y(sectorIndex, rowLo, padLo);
1177+
const float yHi = gpuGeom.LinearPad2Y(sectorIndex, rowHi, padHi);
11311178
for (int k = 1; k <= missingClusters; ++k) {
1132-
const unsigned char missingRow = rowIndexOld + k;
1179+
const unsigned char missingRow = static_cast<unsigned char>(rowLo + k);
11331180
const float frac = static_cast<float>(k) / (missingClusters + 1);
1134-
const float missingPad = gpuGeom.LinearY2Pad(sectorIndex, missingRow, yPrev + (yCur - yPrev) * frac);
1181+
const float missingPad = gpuGeom.LinearY2Pad(sectorIndex, missingRow, yLo + (yHi - yLo) * frac);
11351182
if (missingPad < 0.f || missingPad >= gpuGeom.NPads(missingRow)) {
11361183
missingClusterGapDeadOrEdge = true;
11371184
break;
@@ -1145,7 +1192,7 @@ void CalculatedEdx::calculatedEdx(o2::tpc::TrackTPC& track, dEdxInfo& output, Av
11451192
}
11461193
}
11471194

1148-
if ((missingClusters > 0) && (missingClusters <= mMaxMissingCl) && !missingClusterGapDeadOrEdge) {
1195+
if ((missingClusters > 0) && (missingClusters <= mMaxMissingCl) && !missingClusterGapDeadOrEdge && (iRowInTrack >= edgeRowCut)) {
11491196
if ((clusterMask & ClusterFlags::ExcludeSectorBoundaries) == ClusterFlags::ExcludeSectorBoundaries) {
11501197
if (sectorIndexOld == sectorIndex) {
11511198
if (stack == GEMstack::IROCgem) {
@@ -1341,7 +1388,11 @@ float CalculatedEdx::getTrackTopologyCorrectionPol(const o2::tpc::TrackTPC& trac
13411388
{
13421389
const float snp = std::abs(track.getSnp());
13431390
const float tgl = track.getTgl();
1344-
const float snp2 = snp * snp;
1391+
constexpr float maxSnp2 = 0.99f;
1392+
float snp2 = snp * snp;
1393+
if (snp2 > maxSnp2) {
1394+
snp2 = maxSnp2;
1395+
}
13451396
const float tgl2 = tgl * tgl;
13461397
const float sec2 = 1.f / (1.f - snp2);
13471398
const float tanTheta = std::sqrt(tgl2 * sec2);

0 commit comments

Comments
 (0)