Skip to content

Commit 0a9a2a2

Browse files
sawenzelalcaliva
authored andcommitted
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 14c92a8 commit 0a9a2a2

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
@@ -42,6 +42,14 @@ using DataRequest = o2::globaltracking::DataRequest;
4242

4343
namespace o2::aodproducer
4444
{
45+
/// helper struct to keep mapping of colIndex to MC labels and bunch crossing
46+
struct MCColInfo {
47+
int colIndex;
48+
int sourceID;
49+
int eventID;
50+
int64_t bc; // global bunch crossing
51+
};
52+
4553
/// A structure or container to organize bunch crossing data of a timeframe
4654
/// and to facilitate fast lookup and search within bunch crossings.
4755
class BunchCrossings
@@ -612,7 +620,7 @@ class AODProducerWorkflowDPL : public Task
612620
const gsl::span<const o2::dataformats::VtxTrackRef>& primVer2TRefs,
613621
const gsl::span<const GIndex>& GIndices,
614622
const o2::globaltracking::RecoContainer& data,
615-
const std::vector<std::vector<int>>& mcColToEvSrc);
623+
const std::vector<MCColInfo>& mcColToEvSrc);
616624

617625
template <typename MCTrackLabelCursorType, typename MCMFTTrackLabelCursorType, typename MCFwdTrackLabelCursorType>
618626
void fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTrackLabelCursor,

Detectors/AOD/src/AODProducerWorkflowSpec.cxx

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -910,13 +910,13 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader&
910910
const gsl::span<const o2::dataformats::VtxTrackRef>& primVer2TRefs,
911911
const gsl::span<const GIndex>& GIndices,
912912
const o2::globaltracking::RecoContainer& data,
913-
const std::vector<std::vector<int>>& mcColToEvSrc)
913+
const std::vector<MCColInfo>& mcColToEvSrc)
914914
{
915915
int NSources = 0;
916916
int NEvents = 0;
917917
for (auto& p : mcColToEvSrc) {
918-
NSources = std::max(p[1], NSources);
919-
NEvents = std::max(p[2], NEvents);
918+
NSources = std::max(p.sourceID, NSources);
919+
NEvents = std::max(p.eventID, NEvents);
920920
}
921921
NSources++; // 0 - indexed
922922
NEvents++;
@@ -992,9 +992,9 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader&
992992

993993
size_t offset = 0;
994994
for (auto& colInfo : mcColToEvSrc) { // loop over "<eventID, sourceID> <-> combined MC col. ID" key pairs
995-
int event = colInfo[2];
996-
int source = colInfo[1];
997-
int mcColId = colInfo[0];
995+
int event = colInfo.eventID;
996+
int source = colInfo.sourceID;
997+
int mcColId = colInfo.colIndex;
998998
std::vector<MCTrack> const& mcParticles = mcReader.getTracks(source, event);
999999
LOG(debug) << "Event=" << event << " source=" << source << " collision=" << mcColId;
10001000
auto& preselect = mToStore[source][event];
@@ -1931,10 +1931,8 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc)
19311931
zdcChannelsT);
19321932
}
19331933

1934-
// keep track event/source id for each mc-collision
1935-
// using map and not unordered_map to ensure
1936-
// correct ordering when iterating over container elements
1937-
std::vector<std::vector<int>> mcColToEvSrc;
1934+
// keep track of event_id + source_id + bc for each mc-collision
1935+
std::vector<MCColInfo> mcColToEvSrc;
19381936

19391937
if (mUseMC) {
19401938
using namespace o2::aodmchelpers;
@@ -2007,13 +2005,13 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc)
20072005
0,
20082006
sourceID);
20092007
}
2010-
mcColToEvSrc.emplace_back(std::vector<int>{iCol, sourceID, eventID}); // point background and injected signal events to one collision
2008+
mcColToEvSrc.emplace_back(MCColInfo{iCol, sourceID, eventID, globalBC}); // point background and injected signal events to one collision
20112009
}
20122010
}
20132011
}
20142012

20152013
std::sort(mcColToEvSrc.begin(), mcColToEvSrc.end(),
2016-
[](const std::vector<int>& left, const std::vector<int>& right) { return (left[0] < right[0]); });
2014+
[](const MCColInfo& left, const MCColInfo& right) { return (left.colIndex < right.colIndex); });
20172015

20182016
// vector of FDD amplitudes
20192017
int16_t aFDDAmplitudesA[8] = {0u};
@@ -2095,16 +2093,46 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc)
20952093
}
20962094

20972095
if (mUseMC) {
2098-
// filling MC collision labels
2096+
// Fill MC collision labels using information from the primary vertexer.
20992097
mcColLabelsCursor.reserve(primVerLabels.size());
2100-
for (auto& label : primVerLabels) {
2101-
auto it = std::find_if(mcColToEvSrc.begin(), mcColToEvSrc.end(),
2102-
[&label](const auto& mcColInfo) { return mcColInfo[1] == label.getSourceID() && mcColInfo[2] == label.getEventID(); });
2098+
for (size_t ivert = 0; ivert < primVerLabels.size(); ++ivert) {
2099+
const auto& label = primVerLabels[ivert];
2100+
2101+
// Collect all MC collision candidates matching this (sourceID, eventID) label.
2102+
// In the non-embedding case there is exactly one candidate. In the embedding
2103+
// case the same (sourceID, eventID) pair can appear in multiple collisions,
2104+
// so we need to disambiguate.
2105+
std::vector<std::pair<int32_t, int64_t>> candidates; // (colIndex, bc)
2106+
for (const auto& colInfo : mcColToEvSrc) {
2107+
if (colInfo.sourceID == label.getSourceID() &&
2108+
colInfo.eventID == label.getEventID()) {
2109+
candidates.emplace_back(colInfo.colIndex, colInfo.bc);
2110+
}
2111+
}
2112+
21032113
int32_t mcCollisionID = -1;
2104-
if (it != mcColToEvSrc.end()) {
2105-
mcCollisionID = it->at(0);
2114+
if (candidates.size() == 1) {
2115+
mcCollisionID = candidates[0].first;
2116+
} else if (candidates.size() > 1) {
2117+
// Disambiguate by BC: pick the MCCollision whose BC is closest
2118+
// to the reconstructed collision's BC.
2119+
// TODO: Consider a complementary strategy using the MC labels of tracks
2120+
// associated to the primary vertex, and/or by allowing the primary
2121+
// vertexer to return multiple MC collision labels per vertex.
2122+
const auto& timeStamp = primVertices[ivert].getTimeStamp();
2123+
const double interactionTime = timeStamp.getTimeStamp() * 1E3; // us -> ns
2124+
const auto recoBC = relativeTime_to_GlobalBC(interactionTime);
2125+
int64_t bestDiff = std::numeric_limits<int64_t>::max();
2126+
for (const auto& [colIndex, bc] : candidates) {
2127+
const auto bcDiff = std::abs(static_cast<int64_t>(bc) - static_cast<int64_t>(recoBC));
2128+
if (bcDiff < bestDiff) {
2129+
bestDiff = bcDiff;
2130+
mcCollisionID = colIndex;
2131+
}
2132+
}
21062133
}
2107-
uint16_t mcMask = 0; // todo: set mask using normalized weights?
2134+
2135+
uint16_t mcMask = 0; // TODO: set mask using normalised weights
21082136
mcColLabelsCursor(mcCollisionID, mcMask);
21092137
}
21102138
}

0 commit comments

Comments
 (0)