Skip to content

Commit d4d86bc

Browse files
committed
Make isFromRadDecay walk the mother chain on trackIDs
The new query returned the wrong answer for two common cases, both of which come down to how the stack stores its particles during transport. Primaries never enter mParticles - only secondaries do - and mTrackIDtoParticlesEntry is written for every pushed track, so for a primary it points at whichever secondary happened to be next in the buffer. Asking about a primary therefore inspected an unrelated secondary, and returned true whenever that secondary came from a radioactive decay. Since the buffer is emptied after every primary, its first entry is the first secondary of the current primary, so the `imo > 0` loop guard also skipped exactly that particle and lost any radioactive decay recorded there, together with all of its descendants. Walking the chain on trackIDs instead of on buffer entries removes both problems: primaries are the first mNumberOfPrimaryParticles trackIDs, so that single comparison ends the search without a lookup, and every remaining step is a genuine secondary. The method becomes const and takes the parameter by value like its neighbours, the two includes are dropped again because MCTrack.h already provides TMCProcess, and the declaration gains the note that the answer is only meaningful during transport, since selectTracks() rewrites the mother indices afterwards. The accompanying test builds a small stack with a radioactive decay as the first secondary of its primary and checks the direct, indirect and negative cases; it fails on all four of the affected checks before this change.
1 parent 2be94fd commit d4d86bc

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)