Skip to content

Commit 726cbee

Browse files
sawenzelclaude
andcommitted
Fix stale trackID mapping between events in the MC stack
This fixes a problem in the MC stack track index mapping and adds a unit test. - Stack::Reset() cleared everything the event owns except mIndexMap, the map from transport trackID to MCTrack index. - A worker therefore carried one event's mapping into the next. TrackIDs restart at zero per event, so a hit could be reattached to an MCTrack index of an earlier event. - The hit update loop also dereferenced the lookup without checking for end(). - Reset() now clears the map. The lookup goes through Detector::updatedTrackIndex and returns -1 for a track that was not kept, as UpdateTrackIndex already does for track references. https://its.cern.ch/jira/browse/O2-7134 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 68bd709 commit 726cbee

3 files changed

Lines changed: 92 additions & 2 deletions

File tree

Detectors/Base/include/DetectorsBase/Detector.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,14 @@ class Detector : public FairDetector
163163
// FIXME: make private friend of stack?
164164
virtual void updateHitTrackIndices(std::map<int, int> const&) = 0;
165165

166+
// the index a hit's track ended up at after the stack filtered the event;
167+
// a pruned track has no entry in the mapping and gets the invalid index -1
168+
static int updatedTrackIndex(std::map<int, int> const& indexmapping, int trackID)
169+
{
170+
const auto iter = indexmapping.find(trackID);
171+
return iter != indexmapping.end() ? iter->second : -1;
172+
}
173+
166174
// interfaces to attach properly encoded hit information to a FairMQ message
167175
// and to decode it
168176
virtual void attachHits(fair::mq::Channel&, fair::mq::Parts&) = 0;
@@ -323,8 +331,7 @@ class DetImpl : public o2::base::Detector
323331
// them via a probe integer until we get a nullptr
324332
while (auto hits = static_cast<Det*>(this)->Det::getHits(probe++)) {
325333
for (auto& hit : *hits) {
326-
auto iter = indexmapping.find(hit.GetTrackID());
327-
hit.SetTrackID(iter->second);
334+
hit.SetTrackID(updatedTrackIndex(indexmapping, hit.GetTrackID()));
328335
}
329336
}
330337
}

Detectors/Base/src/Stack.cxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,7 @@ void Stack::Reset()
605605
mPrimaryParticles.clear();
606606
mTrackRefs->clear();
607607
mTrackIDtoParticlesEntry.clear();
608+
mIndexMap.clear();
608609
mHitCounter = 0;
609610
}
610611

Detectors/Base/test/testStack.cxx

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,15 @@
1313
#define BOOST_TEST_MAIN
1414
#define BOOST_TEST_DYN_LINK
1515
#include <boost/test/unit_test.hpp>
16+
#include "DetectorsBase/Detector.h"
1617
#include "DetectorsBase/Stack.h"
18+
#include "SimulationDataFormat/BaseHits.h"
1719
#include "TFile.h"
1820
#include "TMCProcess.h"
21+
#include "TRefArray.h"
22+
#include <map>
23+
#include <string>
24+
#include <vector>
1925

2026
using namespace o2;
2127

@@ -88,3 +94,79 @@ BOOST_AUTO_TEST_CASE(Stack_isFromRadDecay_test)
8894
BOOST_CHECK(!st.isFromRadDecay(-1));
8995
BOOST_CHECK(!st.isFromRadDecay(1000000000));
9096
}
97+
98+
namespace
99+
{
100+
// A test detector to exercise hit creation and its interaction with the MCStack
101+
class TestDetector : public o2::base::Detector
102+
{
103+
public:
104+
// the name is turned into a DetID, so it has to be one of the real detectors
105+
TestDetector() : o2::base::Detector("ITS", true) {}
106+
107+
void updateHitTrackIndices(std::map<int, int> const& indexmapping) override
108+
{
109+
for (auto& hit : mHits) {
110+
hit.SetTrackID(updatedTrackIndex(indexmapping, hit.GetTrackID()));
111+
}
112+
}
113+
114+
std::vector<o2::BaseHit> mHits;
115+
116+
// rest of the interface, unused here
117+
std::string getHitBranchNames(int) const override { return {}; }
118+
void attachHits(fair::mq::Channel&, fair::mq::Parts&) override {}
119+
void fillHitBranch(TTree&, fair::mq::Parts&, int&) override {}
120+
void collectHits(int, fair::mq::Parts&, int&) override {}
121+
void mergeHitEntriesAndFlush(int, TTree&, std::vector<int> const&, std::vector<int> const&,
122+
std::vector<int> const&) override {}
123+
void mergeHitEntries(TTree&, TTree&, std::vector<int> const&, std::vector<int> const&,
124+
std::vector<int> const&) override {}
125+
void InitializeO2Detector() override {}
126+
void initializeLate() override {}
127+
Bool_t ProcessHits(FairVolume* = nullptr) override { return kFALSE; }
128+
void Register() override {}
129+
void Reset() override {}
130+
void ConstructGeometry() override {}
131+
};
132+
133+
// Transport one primary with n secondaries, so that the stack builds its mapping
134+
void transportOnePrimary(o2::data::Stack& st, int nsecondaries)
135+
{
136+
int ntr = 0;
137+
st.PushTrack(1, -1, 11, 0., 0., 1., 1., 0., 0., 0., 0., 0., 0., 0., kPPrimary, ntr, 1., 1);
138+
st.SetCurrentTrack(0);
139+
for (int i = 0; i < nsecondaries; ++i) {
140+
st.PushTrack(1, 0, 11, 0., 0., 0.1, 0.1, 0., 0., 0., 0., 0., 0., 0., kPHadronic, ntr, 1., 1);
141+
}
142+
st.FinishPrimary();
143+
}
144+
} // namespace
145+
146+
// A pruned track has no entry in the mapping
147+
BOOST_AUTO_TEST_CASE(Unmapped_trackID_yields_invalid_index)
148+
{
149+
const std::map<int, int> indexmapping{{0, 0}, {1, 1}};
150+
151+
BOOST_CHECK_EQUAL(o2::base::Detector::updatedTrackIndex(indexmapping, 1), 1);
152+
BOOST_CHECK_EQUAL(o2::base::Detector::updatedTrackIndex(indexmapping, 99), -1);
153+
}
154+
155+
// The mapping is per event and must not survive Reset()
156+
BOOST_AUTO_TEST_CASE(Stack_does_not_reuse_index_map_of_previous_event)
157+
{
158+
TestDetector det;
159+
TRefArray detlist;
160+
detlist.Add(&det);
161+
162+
o2::data::Stack st;
163+
transportOnePrimary(st, 20); // event 1: trackIDs 0 to 20
164+
st.UpdateTrackIndex(&detlist);
165+
st.Reset();
166+
167+
transportOnePrimary(st, 1); // event 2: trackIDs 0 and 1 only
168+
det.mHits.emplace_back(15); // only valid in event 1
169+
st.UpdateTrackIndex(&detlist);
170+
171+
BOOST_CHECK_EQUAL(det.mHits[0].GetTrackID(), -1);
172+
}

0 commit comments

Comments
 (0)