@@ -689,6 +689,8 @@ bool TrackerACTS<nLayers>::acceptTrack(const Acts::BoundTrackParameters& inner,
689689 const std::array<const SpacePoint*, 3 > hits{seed.bottom , seed.middle , seed.top };
690690 std::array<int , o2::its::TrackITSExt::MaxClusters> clusterIndices;
691691 clusterIndices.fill (-1 );
692+ std::array<int , nLayers> internalIndices;
693+ internalIndices.fill (-1 );
692694 uint32_t pattern = 0 ;
693695 std::array<uint64_t , 3 > keys{};
694696
@@ -708,8 +710,8 @@ bool TrackerACTS<nLayers>::acceptTrack(const Acts::BoundTrackParameters& inner,
708710 if (mAcceptedClusters .contains (keys[i])) {
709711 return false ;
710712 }
711- // The output track must refer to the original input-cluster index,
712- // not its position in the TimeFrame's unsorted collection.
713+ // Keep the internal index for later MC lookup; publish the external one.
714+ internalIndices[hit-> layer ] = hit-> clusterId ;
713715 clusterIndices[hit->layer ] = mTimeFrame ->getClusterExternalIndex (hit->layer , hit->clusterId );
714716 if (clusterIndices[hit->layer ] < 0 ) {
715717 return false ;
@@ -751,56 +753,99 @@ bool TrackerACTS<nLayers>::acceptTrack(const Acts::BoundTrackParameters& inner,
751753 o2Track.getTimeStamp ().setTimeStampError (maxHalfROF);
752754 }
753755
754- // Keep track-label output in lockstep with the accepted tracks.
755- o2::MCCompLabel label;
756- if (mTimeFrame ->hasMCinformation ()) {
757- std::vector<std::pair<o2::MCCompLabel, unsigned int >> counts;
758- for (const auto * hit : hits) {
759- for (const auto & candidate : mTimeFrame ->getClusterLabels (hit->layer , hit->clusterId )) {
756+ // Labels are computed once, after all ROFs, from the internal indices.
757+ mTimeFrame ->getTracks ().emplace_back (std::move (o2Track));
758+ mAcceptedTrackClusterIndices .push_back (internalIndices);
759+ for (const auto key : keys) {
760+ mAcceptedClusters .insert (key);
761+ }
762+ return true ;
763+ }
764+
765+ template <int nLayers>
766+ void TrackerACTS<nLayers>::computeTracksMClabels ()
767+ {
768+ if (!mTimeFrame || !mTimeFrame ->hasMCinformation ()) {
769+ return ;
770+ }
771+
772+ auto & tracks = mTimeFrame ->getTracks ();
773+ auto & labels = mTimeFrame ->getTracksLabel ();
774+
775+ if (tracks.size () != mAcceptedTrackClusterIndices .size ()) {
776+ LOGP (fatal, " ACTS MC labeling: {} tracks but {} internal cluster reference sets" ,
777+ tracks.size (), mAcceptedTrackClusterIndices .size ());
778+ return ;
779+ }
780+
781+ // There must be exactly one label for each accepted track, in track order.
782+ labels.clear ();
783+ labels.reserve (tracks.size ());
784+
785+ struct LabelCount {
786+ o2::MCCompLabel label;
787+ int count;
788+ int lastHit;
789+ };
790+
791+ for (size_t iTrack = 0 ; iTrack < tracks.size (); ++iTrack) {
792+ auto & track = tracks[iTrack];
793+ const auto & indices = mAcceptedTrackClusterIndices [iTrack];
794+ std::vector<LabelCount> counts;
795+ int nHits = 0 ;
796+
797+ for (int layer = 0 ; layer < nLayers; ++layer) {
798+ if (indices[layer] < 0 ) {
799+ continue ;
800+ }
801+ ++nHits;
802+ for (const auto & candidate : mTimeFrame ->getClusterLabels (layer, indices[layer])) {
760803 if (!candidate.isValid ()) {
761804 continue ;
762805 }
763806
764- auto it = std::find_if (counts.begin (), counts.end (), [&](const auto & entry) {
765- return entry.first == candidate;
807+ auto it = std::find_if (counts.begin (), counts.end (), [&candidate ](const auto & entry) {
808+ return entry.label == candidate;
766809 });
767810 if (it == counts.end ()) {
768- counts.emplace_back (candidate, 1u );
769- } else {
770- ++it->second ;
811+ counts.push_back ({candidate, 1 , nHits});
812+ } else if (it->lastHit != nHits) {
813+ // Count a given truth label at most once per attached cluster.
814+ ++it->count ;
815+ it->lastHit = nHits;
771816 }
772817 }
773818 }
774- if (!counts.empty ()) {
775- const auto best = std::max_element (counts.begin (), counts.end (),
776- [](const auto & a, const auto & b) {
777- return a.second < b.second ;
778- });
779- label = best->first ;
780- if (best->second < hits.size ()) {
781- label.setFakeFlag ();
819+ o2::MCCompLabel winner;
820+ uint32_t pattern = track.getPattern () & 0x0000ffffu ;
821+ if (counts.empty ()) {
822+ winner.setFakeFlag ();
823+ } else {
824+ const auto best = std::max_element (counts.begin (), counts.end (), [](const auto & a, const auto & b) {
825+ return a.count < b.count ;
826+ });
827+ winner = best->label ;
828+ if (best->count != nHits) {
829+ winner.setFakeFlag ();
782830 }
783- uint32_t correctMask = pattern;
784- for ( const auto * hit : hits) {
785- bool correct = false ;
786- for ( const auto & candidate : mTimeFrame -> getClusterLabels (hit-> layer , hit-> clusterId ) ) {
787- correct |= candidate. isValid () && candidate == best-> first ;
831+ // Preserve the standard TrackITS pattern convention: bits [16, 31]
832+ // indicate clusters compatible with the representative MC label.
833+ for ( int layer = 0 ; layer < nLayers; ++layer) {
834+ if (indices[ layer] < 0 ) {
835+ continue ;
788836 }
789- if (correct) {
790- correctMask |= (1u << (16 + hit->layer ));
837+ for (const auto & candidate : mTimeFrame ->getClusterLabels (layer, indices[layer])) {
838+ if (candidate.isValid () && candidate == best->label ) {
839+ pattern |= (1u << (16 + layer));
840+ break ;
841+ }
791842 }
792843 }
793- o2Track.setPattern (correctMask);
794844 }
845+ track.setPattern (pattern);
846+ labels.emplace_back (winner);
795847 }
796- mTimeFrame ->getTracks ().emplace_back (std::move (o2Track));
797- if (mTimeFrame ->hasMCinformation ()) {
798- mTimeFrame ->getTracksLabel ().emplace_back (label);
799- }
800- for (const auto key : keys) {
801- mAcceptedClusters .insert (key);
802- }
803- return true ;
848+ LOGP (info, " ACTS MC labeling: {} tracks, {} labels" , tracks.size (), labels.size ());
804849}
805850
806851template <int nLayers>
@@ -813,9 +858,9 @@ void TrackerACTS<nLayers>::clustersToTracks()
813858
814859 double totalTime = 0 .;
815860 mAcceptedClusters .clear ();
861+ mAcceptedTrackClusterIndices .clear ();
816862 LOG (info) << " ==== TRK ACTS Tracking ====" ;
817863
818- // Initialize ACTS tracking geometry
819864 auto & actsGeometryManager = o2::acts::TrackingGeometryManager::instance ();
820865 const auto & trackingGeometry = actsGeometryManager.get ();
821866 (void )trackingGeometry;
@@ -842,6 +887,11 @@ void TrackerACTS<nLayers>::clustersToTracks()
842887 totalTime += evaluateTask ([this ]() { findTracks (); }, StateNames[mCurState ]);
843888 }
844889
890+ // Run MC labeling once, after every accepted track has been published.
891+ if (mTimeFrame ->hasMCinformation ()) {
892+ computeTracksMClabels ();
893+ }
894+
845895 LOG (info) << " === TimeFrame " << mTimeFrameCounter << " completed in: " << totalTime << " ms ===" ;
846896 ++mTimeFrameCounter ;
847897 mTotalTime += totalTime;
0 commit comments