Skip to content

Commit 6339a5a

Browse files
committed
Fix isFromRadDecay ancestry walk and add a unit test
The query added in #15470 gave two wrong answers. Primaries returned true, because mTrackIDtoParticlesEntry is meaningless for them - primaries never enter mParticles - so the lookup landed on an unrelated secondary. Descendants of a radioactive decay returned false, because the `imo > 0` guard skipped buffer entry 0, which after FinishPrimary() is the first secondary of the current primary. Walking trackIDs and stopping at mNumberOfPrimaryParticles removes both, and the method becomes const, binds a reference instead of copying the MCTrack, and drops the two includes MCTrack.h already provides. The new test case fails on all four affected checks without this change.
1 parent 9f97491 commit 6339a5a

2 files changed

Lines changed: 68 additions & 32 deletions

File tree

Detectors/Base/include/DetectorsBase/Stack.h

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@
2424
#include "SimulationDataFormat/ParticleStatus.h"
2525
#include "Rtypes.h"
2626
#include "TParticle.h"
27-
#include "TVirtualMC.h"
28-
#include "TMCProcess.h"
27+
2928
#include <map>
3029
#include <memory>
3130
#include <stack>
@@ -211,7 +210,10 @@ class Stack : public FairGenericStack
211210
/// query if a track is a direct **or** indirect daughter of a parentID
212211
/// if trackid is same as parentid it returns true
213212
bool isTrackDaughterOf(int /*trackid*/, int /*parentid*/) const;
214-
bool isFromRadDecay(const int id);
213+
/// query if a track originates, directly or indirectly, from a radioactive decay
214+
/// only meaningful during transport, before selectTracks() remaps mother indices
215+
bool isFromRadDecay(int trackid) const;
216+
215217
bool isCurrentTrackDaughterOf(int parentid) const;
216218

217219
// returns the index of the currently transported primary
@@ -349,39 +351,29 @@ inline int Stack::getMotherTrackId(int trackid) const
349351
return mParticles[entryinParticles].getMotherTrackId();
350352
}
351353

352-
inline bool Stack::isFromRadDecay(const int id)
354+
inline bool Stack::isFromRadDecay(int trackid) const
353355
{
354-
// Check whether particle originates directly or indirectly from radioactive decay
356+
// Check whether particle originates directly or indirectly from radioactive decay.
357+
// Walks up the mother chain until a primary is reached. Only meaningful during
358+
// transport, since selectTracks() later rewrites the mother indices in mParticles.
355359
//
356-
if (id < 0 || id >= static_cast<int>(mTrackIDtoParticlesEntry.size())) {
357-
return false;
358-
}
359-
const auto entry = mTrackIDtoParticlesEntry[id];
360-
if (entry < 0 || entry >= static_cast<int>(mParticles.size()))
361-
return false;
362-
auto part = (mParticles[entry]);
363-
364-
// primary particle ?
365-
if (part.getProcess() == 0)
366-
return false;
367-
// particle directly from radioactive decay ?
368-
if (part.getProcess() == kPRadDecay) {
369-
return true;
370-
}
371-
372-
// search in particle history
373-
auto imo = mTrackIDtoParticlesEntry[part.getMotherTrackId()];
374-
auto isRad = false;
375-
while (imo > 0) {
376-
auto mother = (mParticles[imo]);
377-
if (mother.getProcess() == kPRadDecay) {
378-
isRad = true;
379-
break;
360+
// Note that primaries are not kept in mParticles and that mTrackIDtoParticlesEntry
361+
// is meaningless for them, so the chain has to be terminated on the trackID itself.
362+
for (int id = trackid; id >= mNumberOfPrimaryParticles;) {
363+
if (id >= static_cast<int>(mTrackIDtoParticlesEntry.size())) {
364+
return false;
365+
}
366+
const auto entry = mTrackIDtoParticlesEntry[id];
367+
if (entry < 0 || entry >= static_cast<int>(mParticles.size())) {
368+
return false;
369+
}
370+
const auto& part = mParticles[entry];
371+
if (part.getProcess() == kPRadDecay) {
372+
return true;
380373
}
381-
part = mother;
382-
imo = mTrackIDtoParticlesEntry[mother.getMotherTrackId()];
374+
id = part.getMotherTrackId();
383375
}
384-
return isRad;
376+
return false;
385377
}
386378

387379
inline bool Stack::isCurrentTrackDaughterOf(int parentid) const

Detectors/Base/test/testStack.cxx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,47 @@ BOOST_AUTO_TEST_CASE(Stack_test)
4444
BOOST_CHECK(inst->getPrimaries().size() == 2);
4545
}
4646
}
47+
48+
// convenience wrapper to push a track and return the assigned trackID
49+
static int pushTrack(o2::data::Stack& st, int parentId, TMCProcess proc)
50+
{
51+
int trackId;
52+
st.PushTrack(1, parentId, 0, 0., 0., 0., 10., 5., 5., 5., 0.1, 0., 0., 0., proc, trackId, 1., 1);
53+
return trackId;
54+
}
55+
56+
// unit test for the radioactive-decay ancestry query
57+
BOOST_AUTO_TEST_CASE(Stack_isFromRadDecay_test)
58+
{
59+
o2::data::Stack st;
60+
61+
// two primaries; note that primaries do not enter mParticles, only secondaries do
62+
const auto prim0 = pushTrack(st, -1, kPPrimary);
63+
const auto prim1 = pushTrack(st, -1, kPPrimary);
64+
65+
// a radioactive decay product of the second primary, and its descendants.
66+
// this is deliberately the *first* secondary of the primary, so that it lands
67+
// in the first entry of the particle buffer
68+
const auto radDecay = pushTrack(st, prim1, kPRadDecay);
69+
const auto radChild = pushTrack(st, radDecay, kPHadronic);
70+
const auto radGrandChild = pushTrack(st, radChild, kPHadronic);
71+
72+
// a plain secondary of the second primary: no radioactive decay anywhere in its history
73+
const auto ordinary = pushTrack(st, prim1, kPHadronic);
74+
75+
// primaries can never come from a radioactive decay
76+
BOOST_CHECK(!st.isFromRadDecay(prim0));
77+
BOOST_CHECK(!st.isFromRadDecay(prim1));
78+
79+
// a secondary whose ancestry ends in a primary must terminate the search with false
80+
BOOST_CHECK(!st.isFromRadDecay(ordinary));
81+
82+
// directly and indirectly from a radioactive decay
83+
BOOST_CHECK(st.isFromRadDecay(radDecay));
84+
BOOST_CHECK(st.isFromRadDecay(radChild));
85+
BOOST_CHECK(st.isFromRadDecay(radGrandChild));
86+
87+
// out-of-range track IDs are rejected rather than looked up
88+
BOOST_CHECK(!st.isFromRadDecay(-1));
89+
BOOST_CHECK(!st.isFromRadDecay(1000000000));
90+
}

0 commit comments

Comments
 (0)