Skip to content

Commit 3f9b8d7

Browse files
committed
AOD producer: improve MC collision labels for embedded events
In embedding scenarios where a single background event is reused across multiple collisions, e.g.: Collision 0: Background 0 + Signal 0 Collision 1: Background 0 + Signal 1 Collision 2: Background 0 + Signal 2 Collision 3: Background 0 + Signal 3 Collision 4: Background 0 + Signal 4 the primary vertexer may return MC labels all pointing to "Background 0". This caused an ambiguous lookup in the AOD producer, resulting in all reconstructed collisions being incorrectly associated to the same MCCollision entry. This is addressed by collecting all MCCollision candidates matching a given (sourceID, eventID) label and disambiguating via bunch-crossing time, picking the MCCollision whose BC is closest to the reconstructed vertex time. Note: a more robust long-term solution would be to extend the primary vertexer to return the full set of contributing MC labels per found vertex, enabling simpler and more reliable disambiguation strategies. This relates to https://its.cern.ch/jira/browse/O2-6840.
1 parent 34b3d56 commit 3f9b8d7

2 files changed

Lines changed: 56 additions & 20 deletions

File tree

Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ using DataRequest = o2::globaltracking::DataRequest;
4848

4949
namespace o2::aodproducer
5050
{
51+
/// helper struct to keep mapping of colIndex to MC labels and bunch crossing
52+
struct MCColInfo {
53+
int colIndex;
54+
int sourceID;
55+
int eventID;
56+
int64_t bc; // global bunch crossing
57+
};
58+
5159
/// A structure or container to organize bunch crossing data of a timeframe
5260
/// and to facilitate fast lookup and search within bunch crossings.
5361
class BunchCrossings
@@ -661,7 +669,7 @@ class AODProducerWorkflowDPL : public Task
661669
const gsl::span<const o2::dataformats::VtxTrackRef>& primVer2TRefs,
662670
const gsl::span<const GIndex>& GIndices,
663671
const o2::globaltracking::RecoContainer& data,
664-
const std::vector<std::vector<int>>& mcColToEvSrc);
672+
const std::vector<MCColInfo>& mcColToEvSrc);
665673

666674
template <typename MCTrackLabelCursorType, typename MCMFTTrackLabelCursorType, typename MCFwdTrackLabelCursorType>
667675
void fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTrackLabelCursor,

Detectors/AOD/src/AODProducerWorkflowSpec.cxx

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,13 +1084,13 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader&
10841084
const gsl::span<const o2::dataformats::VtxTrackRef>& primVer2TRefs,
10851085
const gsl::span<const GIndex>& GIndices,
10861086
const o2::globaltracking::RecoContainer& data,
1087-
const std::vector<std::vector<int>>& mcColToEvSrc)
1087+
const std::vector<MCColInfo>& mcColToEvSrc)
10881088
{
10891089
int NSources = 0;
10901090
int NEvents = 0;
10911091
for (auto& p : mcColToEvSrc) {
1092-
NSources = std::max(p[1], NSources);
1093-
NEvents = std::max(p[2], NEvents);
1092+
NSources = std::max(p.sourceID, NSources);
1093+
NEvents = std::max(p.eventID, NEvents);
10941094
}
10951095
NSources++; // 0 - indexed
10961096
NEvents++;
@@ -1166,9 +1166,9 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader&
11661166

11671167
size_t offset = 0;
11681168
for (auto& colInfo : mcColToEvSrc) { // loop over "<eventID, sourceID> <-> combined MC col. ID" key pairs
1169-
int event = colInfo[2];
1170-
int source = colInfo[1];
1171-
int mcColId = colInfo[0];
1169+
int event = colInfo.eventID;
1170+
int source = colInfo.sourceID;
1171+
int mcColId = colInfo.colIndex;
11721172
std::vector<MCTrack> const& mcParticles = mcReader.getTracks(source, event);
11731173
LOG(debug) << "Event=" << event << " source=" << source << " collision=" << mcColId;
11741174
auto& preselect = mToStore[source][event];
@@ -2179,10 +2179,8 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc)
21792179
zdcChannelsT);
21802180
}
21812181

2182-
// keep track event/source id for each mc-collision
2183-
// using map and not unordered_map to ensure
2184-
// correct ordering when iterating over container elements
2185-
std::vector<std::vector<int>> mcColToEvSrc;
2182+
// keep track of event_id + source_id + bc for each mc-collision
2183+
std::vector<MCColInfo> mcColToEvSrc;
21862184

21872185
if (mUseMC) {
21882186
using namespace o2::aodmchelpers;
@@ -2255,13 +2253,13 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc)
22552253
0,
22562254
sourceID);
22572255
}
2258-
mcColToEvSrc.emplace_back(std::vector<int>{iCol, sourceID, eventID}); // point background and injected signal events to one collision
2256+
mcColToEvSrc.emplace_back(MCColInfo{iCol, sourceID, eventID, globalBC}); // point background and injected signal events to one collision
22592257
}
22602258
}
22612259
}
22622260

22632261
std::sort(mcColToEvSrc.begin(), mcColToEvSrc.end(),
2264-
[](const std::vector<int>& left, const std::vector<int>& right) { return (left[0] < right[0]); });
2262+
[](const MCColInfo& left, const MCColInfo& right) { return (left.colIndex < right.colIndex); });
22652263

22662264
// vector of FDD amplitudes
22672265
int16_t aFDDAmplitudesA[8] = {0u}, aFDDAmplitudesC[8] = {0u};
@@ -2360,16 +2358,46 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc)
23602358
}
23612359

23622360
if (mUseMC) {
2363-
// filling MC collision labels
2361+
// Fill MC collision labels using information from the primary vertexer.
23642362
mcColLabelsCursor.reserve(primVerLabels.size());
2365-
for (auto& label : primVerLabels) {
2366-
auto it = std::find_if(mcColToEvSrc.begin(), mcColToEvSrc.end(),
2367-
[&label](const auto& mcColInfo) { return mcColInfo[1] == label.getSourceID() && mcColInfo[2] == label.getEventID(); });
2363+
for (size_t ivert = 0; ivert < primVerLabels.size(); ++ivert) {
2364+
const auto& label = primVerLabels[ivert];
2365+
2366+
// Collect all MC collision candidates matching this (sourceID, eventID) label.
2367+
// In the non-embedding case there is exactly one candidate. In the embedding
2368+
// case the same (sourceID, eventID) pair can appear in multiple collisions,
2369+
// so we need to disambiguate.
2370+
std::vector<std::pair<int32_t, int64_t>> candidates; // (colIndex, bc)
2371+
for (const auto& colInfo : mcColToEvSrc) {
2372+
if (colInfo.sourceID == label.getSourceID() &&
2373+
colInfo.eventID == label.getEventID()) {
2374+
candidates.emplace_back(colInfo.colIndex, colInfo.bc);
2375+
}
2376+
}
2377+
23682378
int32_t mcCollisionID = -1;
2369-
if (it != mcColToEvSrc.end()) {
2370-
mcCollisionID = it->at(0);
2379+
if (candidates.size() == 1) {
2380+
mcCollisionID = candidates[0].first;
2381+
} else if (candidates.size() > 1) {
2382+
// Disambiguate by BC: pick the MCCollision whose BC is closest
2383+
// to the reconstructed collision's BC.
2384+
// TODO: Consider a complementary strategy using the MC labels of tracks
2385+
// associated to the primary vertex, and/or by allowing the primary
2386+
// vertexer to return multiple MC collision labels per vertex.
2387+
const auto& timeStamp = primVertices[ivert].getTimeStamp();
2388+
const double interactionTime = timeStamp.getTimeStamp() * 1E3; // us -> ns
2389+
const auto recoBC = relativeTime_to_GlobalBC(interactionTime);
2390+
int64_t bestDiff = std::numeric_limits<int64_t>::max();
2391+
for (const auto& [colIndex, bc] : candidates) {
2392+
const auto bcDiff = std::abs(static_cast<int64_t>(bc) - static_cast<int64_t>(recoBC));
2393+
if (bcDiff < bestDiff) {
2394+
bestDiff = bcDiff;
2395+
mcCollisionID = colIndex;
2396+
}
2397+
}
23712398
}
2372-
uint16_t mcMask = 0; // todo: set mask using normalized weights?
2399+
2400+
uint16_t mcMask = 0; // TODO: set mask using normalised weights
23732401
mcColLabelsCursor(mcCollisionID, mcMask);
23742402
}
23752403
}

0 commit comments

Comments
 (0)