From 29cf2788a8a90fece24e11fdf73a8b1d929a8e69 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Thu, 24 Sep 2026 16:17:06 +0200 Subject: [PATCH 1/2] Write tracked V0s, cascades and 3-bodies in collision order This fixes the row order of the tracked strangeness tables written by the AOD producer. - The tracked V0, cascade and 3-body rows were written in strangeness-tracker order, which is not always collision order. - Analyses slicing them by collision then abort with "TraCascIndices index fIndexCollisions is not sorted". - The rows are now written in the per-collision order already built in prepareStrangenessTracking. - The track index of a row no longer comes from a running counter, so a skipped strange track cannot shift the rows after it. https://its.cern.ch/jira/browse/O2-7197 Co-Authored-By: Claude Opus 5.5 --- Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 06f2cfb0301e5..e72eef88a966f 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -1536,7 +1536,6 @@ template void AODProducerWorkflowDPL::fillStrangenessTrackingTables(const o2::globaltracking::RecoContainer& recoData, V0C& v0Curs, CC& cascCurs, D3BC& d3BodyCurs) { int itsTableIdx = -1; - int sTrkID = 0; int nV0 = 0; int nCasc = 0; int nD3Body = 0; @@ -1555,7 +1554,10 @@ void AODProducerWorkflowDPL::fillStrangenessTrackingTables(const o2::globaltrack cascCurs.reserve(nCasc); d3BodyCurs.reserve(nD3Body); - for (const auto& sTrk : recoData.getStrangeTracks()) { + // Write the rows grouped by collision, the order analyses slice these tables in + auto sTracks = recoData.getStrangeTracks(); + for (const auto& collStrTrk : mCollisionStrTrk) { + const auto& sTrk = sTracks[collStrTrk.second]; auto ITSIndex = GIndex{sTrk.mITSRef, GIndex::ITS}; auto item = mGIDToTableID.find(ITSIndex); if (item != mGIDToTableID.end()) { @@ -1565,7 +1567,7 @@ void AODProducerWorkflowDPL::fillStrangenessTrackingTables(const o2::globaltrack continue; } if (sTrk.mPartType == dataformats::kStrkV0) { - v0Curs(mStrTrkIndices[sTrkID++], + v0Curs(mStrTrkIndices[collStrTrk.second], itsTableIdx, sTrk.mDecayRef, sTrk.mDecayVtx[0], @@ -1577,7 +1579,7 @@ void AODProducerWorkflowDPL::fillStrangenessTrackingTables(const o2::globaltrack sTrk.mTopoChi2, sTrk.getAverageClusterSize()); } else if (sTrk.mPartType == dataformats::kStrkCascade) { - cascCurs(mStrTrkIndices[sTrkID++], + cascCurs(mStrTrkIndices[collStrTrk.second], itsTableIdx, sTrk.mDecayRef, sTrk.mDecayVtx[0], @@ -1589,7 +1591,7 @@ void AODProducerWorkflowDPL::fillStrangenessTrackingTables(const o2::globaltrack sTrk.mTopoChi2, sTrk.getAverageClusterSize()); } else { - d3BodyCurs(mStrTrkIndices[sTrkID++], + d3BodyCurs(mStrTrkIndices[collStrTrk.second], itsTableIdx, sTrk.mDecayRef, sTrk.mDecayVtx[0], From d526624d43c033664aec300bb4fd3cfb8a3e0bac Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Thu, 24 Sep 2026 17:25:52 +0200 Subject: [PATCH 2/2] Keep strange tracks in decay order also with one vertexer thread This makes the order of the tracked strangeness tables within a collision the same in MC as in data. - SVertexer sorted the strange tracks by decay only when running with more than one thread; MC reconstruction runs it with one. - The strange tracks are now sorted by decay in all cases. - The AOD producer groups them by collision with a stable sort, so the decay order within a collision is kept. https://its.cern.ch/jira/browse/O2-7197 Co-Authored-By: Claude Opus 5.5 --- Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 2 +- Detectors/Vertexing/src/SVertexer.cxx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index e72eef88a966f..d67925b1e3503 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -1527,7 +1527,7 @@ void AODProducerWorkflowDPL::prepareStrangenessTracking(const o2::globaltracking std::exclusive_scan(mVertexStrLUT.begin(), mVertexStrLUT.end(), mVertexStrLUT.begin(), 0); // sort by collision ID - std::sort(mCollisionStrTrk.begin(), mCollisionStrTrk.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); + std::stable_sort(mCollisionStrTrk.begin(), mCollisionStrTrk.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); mStrTrkIndices.clear(); mStrTrkIndices.resize(mCollisionStrTrk.size(), -1); } diff --git a/Detectors/Vertexing/src/SVertexer.cxx b/Detectors/Vertexing/src/SVertexer.cxx index 75ddefadf4c9e..44e78fd911056 100644 --- a/Detectors/Vertexing/src/SVertexer.cxx +++ b/Detectors/Vertexing/src/SVertexer.cxx @@ -225,8 +225,8 @@ void SVertexer::produceOutput(o2::framework::ProcessingContext& pc) std::vector sortIdx(strTracksTmp.size()); std::iota(sortIdx.begin(), sortIdx.end(), 0); - // if mNTreads > 1 we need to sort tracks, clus and MCLabs by their mDecayRef - if (mNThreads > 1 && mNStrangeTracks > 1) { + // sort tracks, clus and MCLabs by their mDecayRef, also with one thread, so that they follow the vertex order + if (mNStrangeTracks > 1) { std::sort(sortIdx.begin(), sortIdx.end(), [&strTracksTmp](int i1, int i2) { return strTracksTmp[i1].mDecayRef < strTracksTmp[i2].mDecayRef; }); }