Skip to content

Commit a0af1e3

Browse files
committed
Fix cluster traversal order and propagation robustness in CalculatedEdx
1 parent 66f8031 commit a0af1e3

2 files changed

Lines changed: 34 additions & 11 deletions

File tree

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ class CalculatedEdx
163163
/// fill missing clusters with minimum charge (method=0) or minimum charge/2 (method=1)
164164
void fillMissingClusters(int missingClusters[4], float minChargeTot, float minChargeMax, int method, std::array<std::vector<float>, 5>& chargeTotROC, std::array<std::vector<float>, 5>& chargeMaxROC);
165165

166-
void handleSameRowClusters(o2::tpc::TrackTPC& track, std::map<std::pair<unsigned char, unsigned char>, std::vector<int>>& clustersByRow, std::map<std::pair<unsigned char, unsigned char>, o2::tpc::ClusterNative>& combinedClustersByRow, std::map<int, std::tuple<unsigned char, unsigned char, unsigned int>>& clusterReferencesByIndex);
166+
/// \param rowOrder (sector, row) keys in the order they are first encountered while scanning the track's native cluster references (0..nClusterReferences-1), i.e. the track's true physical row-traversal order
167+
void handleSameRowClusters(o2::tpc::TrackTPC& track, std::vector<std::pair<unsigned char, unsigned char>>& rowOrder, std::map<std::pair<unsigned char, unsigned char>, std::vector<int>>& clustersByRow, std::map<std::pair<unsigned char, unsigned char>, o2::tpc::ClusterNative>& combinedClustersByRow, std::map<int, std::tuple<unsigned char, unsigned char, unsigned int>>& clusterReferencesByIndex);
167168

168169
/// get the truncated mean for the input track with the truncation range, charge type, region and corrections
169170
/// the cluster charge is normalized by effective length*gain, you can turn off the normalization by setting all corrections to false

Detectors/TPC/calibration/src/CalculatedEdx.cxx

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ void CalculatedEdx::fillMissingClusters(int missingClusters[4], float minChargeT
7676
}
7777
}
7878

79-
void CalculatedEdx::handleSameRowClusters(o2::tpc::TrackTPC& track, std::map<std::pair<unsigned char, unsigned char>, std::vector<int>>& clustersByRow, std::map<std::pair<unsigned char, unsigned char>, o2::tpc::ClusterNative>& combinedClustersByRow, std::map<int, std::tuple<unsigned char, unsigned char, unsigned int>>& clusterReferencesByIndex)
79+
void CalculatedEdx::handleSameRowClusters(o2::tpc::TrackTPC& track, std::vector<std::pair<unsigned char, unsigned char>>& rowOrder, std::map<std::pair<unsigned char, unsigned char>, std::vector<int>>& clustersByRow, std::map<std::pair<unsigned char, unsigned char>, o2::tpc::ClusterNative>& combinedClustersByRow, std::map<int, std::tuple<unsigned char, unsigned char, unsigned int>>& clusterReferencesByIndex)
8080
{
8181
// get number of clusters
8282
const int nClusters = track.getNClusterReferences();
@@ -91,8 +91,13 @@ void CalculatedEdx::handleSameRowClusters(o2::tpc::TrackTPC& track, std::map<std
9191

9292
track.getClusterReference(*mTPCTrackClIdxVecInput, iCl, sectorIndex, rowIndex, clusterIndexNumb);
9393

94+
const auto rowKey = std::make_pair(sectorIndex, rowIndex);
95+
if (clustersByRow.find(rowKey) == clustersByRow.end()) {
96+
rowOrder.emplace_back(rowKey);
97+
}
98+
9499
// add the cluster index to the corresponding (sector, row) key in clustersByRow
95-
clustersByRow[{sectorIndex, rowIndex}].emplace_back(iCl);
100+
clustersByRow[rowKey].emplace_back(iCl);
96101

97102
// store the reference data in clusterReferencesByIndex
98103
clusterReferencesByIndex[iCl] = std::make_tuple(sectorIndex, rowIndex, clusterIndexNumb);
@@ -180,11 +185,12 @@ void CalculatedEdx::calculatedEdx(o2::tpc::TrackTPC& track, dEdxInfo& output, Av
180185
float scCorr = 1.0f;
181186

182187
// handle same (sector, row) clusters
188+
std::vector<std::pair<unsigned char, unsigned char>> rowOrder;
183189
std::map<std::pair<unsigned char, unsigned char>, std::vector<int>> clustersByRow;
184190
std::map<std::pair<unsigned char, unsigned char>, o2::tpc::ClusterNative> combinedClustersByRow;
185191
std::map<int, std::tuple<unsigned char, unsigned char, unsigned int>> clusterReferencesByIndex;
186192

187-
handleSameRowClusters(track, clustersByRow, combinedClustersByRow, clusterReferencesByIndex);
193+
handleSameRowClusters(track, rowOrder, clustersByRow, combinedClustersByRow, clusterReferencesByIndex);
188194

189195
o2::utils::TreeStreamRedirector* debugStreamer = nullptr;
190196
o2::tpc::TrackTPC trackOrig;
@@ -195,8 +201,9 @@ void CalculatedEdx::calculatedEdx(o2::tpc::TrackTPC& track, dEdxInfo& output, Av
195201
trackOrig = track; // pristine track, before refit/propagation mutates it cluster-by-cluster below
196202
}
197203

198-
// loop over the clusters
199-
for (const auto& [rowKey, clusterIndices] : clustersByRow) {
204+
// loop over the clusters in the track's true physical row-traversal order (rowOrder)
205+
for (const auto& rowKey : rowOrder) {
206+
const auto& clusterIndices = clustersByRow.at(rowKey);
200207
const unsigned char rowIndex = rowKey.second;
201208
int clusterIdx = clusterIndices[0];
202209
const o2::tpc::ClusterNative& clConst = track.getCluster(*mTPCTrackClIdxVecInput, clusterIdx, *mClusterIndex);
@@ -280,12 +287,27 @@ void CalculatedEdx::calculatedEdx(o2::tpc::TrackTPC& track, dEdxInfo& output, Av
280287
check = (mRefit->RefitTrackAsGPU(track, false, true) < 0) ? false : true;
281288
} else if (mPropagateTrack) {
282289
// propagate this track to the plane X=xk (cm) in the field "b" (kG)
283-
track.rotate(o2::math_utils::detail::sector2Angle<float>(sectorIndex));
284-
check = o2::base::Propagator::Instance()->PropagateToXBxByBz(track, xPosition, 0.999f, 2., o2::base::Propagator::MatCorrType::USEMatCorrLUT);
290+
// snapshot the fit state first and roll it back on failure
291+
// rotate() rejecting the frame change, or PropagateToXBxByBz failing mid-step e.g. its material-LUT lookup driving the state into an unphysical regime for a difficult trajectory
292+
// so that a single bad row doesn't leave the track corrupted for every subsequent row's propagation attempt
293+
const o2::track::TrackParCov trackBackup = track;
294+
check = track.rotate(o2::math_utils::detail::sector2Angle<float>(sectorIndex));
295+
if (check) {
296+
check = o2::base::Propagator::Instance()->PropagateToXBxByBz(track, xPosition, 0.999f, 0.5f, o2::base::Propagator::MatCorrType::USEMatCorrLUT);
297+
}
298+
if (!check) {
299+
static_cast<o2::track::TrackParCov&>(track) = trackBackup;
300+
}
285301
} else if (mPropagateParams) {
286-
// propagate the params of the track instead of full propagation
287-
track.rotateParam(o2::math_utils::detail::sector2Angle<float>(sectorIndex));
288-
check = track.propagateParamTo(xPosition, mFieldNominalGPUBz);
302+
// propagate the params of the track instead of full propagation; same rollback rationale as mPropagateTrack above
303+
const o2::track::TrackParCov trackBackup = track;
304+
check = track.rotateParam(o2::math_utils::detail::sector2Angle<float>(sectorIndex));
305+
if (check) {
306+
check = track.propagateParamTo(xPosition, mFieldNominalGPUBz);
307+
}
308+
if (!check) {
309+
static_cast<o2::track::TrackParCov&>(track) = trackBackup;
310+
}
289311
}
290312

291313
if (!check || std::isnan(track.getParam(1))) {

0 commit comments

Comments
 (0)