@@ -118,6 +118,22 @@ using SMatrix5 = o2::track::SMatrix5;
118118constexpr std::array<int , 10 > NDetElemCh = {4 , 4 , 4 , 4 , 18 , 18 , 26 , 26 , 26 , 26 };
119119constexpr std::array<int , 11 > SNDetElemCh = {0 , 4 , 8 , 12 , 16 , 34 , 52 , 78 , 104 , 130 , 156 };
120120
121+ // compute minimum difference between azimuthal angles
122+ static float getDeltaPhi (float phi1, float phi2)
123+ {
124+ float dphi = phi1 - phi2;
125+ static constexpr float pi = TMath::Pi ();
126+ static constexpr float twopi = pi;
127+ if (dphi > pi) {
128+ dphi -= twopi;
129+ }
130+ if (dphi < pi) {
131+ dphi += twopi;
132+ }
133+ return dphi;
134+ };
135+
136+
121137struct GlobalMuonMatching {
122138
123139 static constexpr int GlobalTrackTypeMax = 2 ;
@@ -138,6 +154,15 @@ struct GlobalMuonMatching {
138154 int matchRanking{-1 };
139155 };
140156
157+ struct MchTrackInfo {
158+ int64_t index{-1 };
159+ int nMatchAttempts{-1 };
160+ // vector of MFT-MCH matching candidates
161+ std::vector<MatchingCandidate> matchingCandidates;
162+ // vector of vectors of MFT-MCH matching candidates from mixed events
163+ std::vector<std::vector<MatchingCandidate>> mixedMatchingCandidates;
164+ };
165+
141166 // // Variables for selecting tagged muons
142167 struct : ConfigurableGroup {
143168 Configurable<int > cfgMuonTaggingNCrossedMftPlanesLow{" cfgMuonTaggingNCrossedMftPlanesLow" , 5 , " " };
@@ -299,6 +324,7 @@ struct GlobalMuonMatching {
299324 std::array<int32_t , 2 > mLastMchAmbiguousBcSlice {};
300325
301326 int32_t mMatchCandidateCounter {0 };
327+ std::unordered_map<int64_t , MchTrackInfo> mMchTrackInfos ;
302328 std::unordered_map<int64_t , std::vector<int32_t >> mMchTrackToCandidateIndices ;
303329 std::unordered_map<int64_t , std::vector<MatchingCandidate>> mMchTrackMatchingCandidates ;
304330 std::unordered_map<int64_t , int32_t > mFwdTrackToGmmCandTrkIndex ;
@@ -1119,6 +1145,41 @@ struct GlobalMuonMatching {
11191145 return std::fabs (deltaTrackTime) <= trackTimeResTot;
11201146 }
11211147
1148+ template <class EVT , class BC , class TMUON , class TMFTS >
1149+ int getMftMchMatchAttempts (EVT const & collisions,
1150+ BC const & bcs,
1151+ TMUON const & mchTrack,
1152+ TMFTS const & mftTracks)
1153+ {
1154+ if (!mchTrack.has_collision ()) {
1155+ return 0 ;
1156+ }
1157+ const auto & collMch = collisions.rawIteratorAt (mchTrack.collisionId ());
1158+ const auto & bcMch = bcs.rawIteratorAt (collMch.bcId ());
1159+
1160+ int attempts{0 };
1161+ for (const auto & mftTrack : mftTracks) {
1162+ if (!mftTrack.has_collision ()) {
1163+ continue ;
1164+ }
1165+
1166+ const auto & collMft = collisions.rawIteratorAt (mftTrack.collisionId ());
1167+ const auto & bcMft = bcs.rawIteratorAt (collMft.bcId ());
1168+
1169+ int64_t deltaBc = static_cast <int64_t >(bcMft.globalBC ()) - static_cast <int64_t >(bcMch.globalBC ());
1170+ double deltaBcNS = o2::constants::lhc::LHCBunchSpacingNS * deltaBc;
1171+ double deltaTrackTime = mftTrack.trackTime () - mchTrack.trackTime () + deltaBcNS;
1172+ double trackTimeResTot = mftTrack.trackTimeRes () + mchTrack.trackTimeRes ();
1173+
1174+ if (std::fabs (deltaTrackTime) > trackTimeResTot) {
1175+ continue ;
1176+ }
1177+ attempts += 1 ;
1178+ }
1179+
1180+ return attempts;
1181+ }
1182+
11221183 template <class EVT , class BC , class TMUON , class TMFT >
11231184 void prepareMatchingCandidates (EVT const & collisions,
11241185 BC const & bcs,
@@ -1140,6 +1201,9 @@ struct GlobalMuonMatching {
11401201
11411202 // initialize the MCH track parameters, which will be updated by the realignment if enabled
11421203 mMchTrackPars .try_emplace (mchTrackIndex, TrackParExt (fwdtrackutils::getTrackParCovFwd (muonTrack, muonTrack), muonTrack.nClusters ()));
1204+
1205+ auto & mchTrackInfo = mMchTrackInfos [mchTrackIndex];
1206+ mchTrackInfo.index = mchTrackIndex;
11431207 }
11441208
11451209 for (const auto & mftTrack : mftTracks) {
@@ -1170,12 +1234,26 @@ struct GlobalMuonMatching {
11701234 continue ;
11711235 }
11721236
1237+ auto & mchTrackInfo = mMchTrackInfos [mchTrackIndex];
1238+ mchTrackInfo.index = mchTrackIndex;
1239+ mchTrackInfo.matchingCandidates .emplace_back (MatchingCandidate{
1240+ .muonTrackId = muonTrack.globalIndex (),
1241+ .mftTrackId = mftTrackIndex,
1242+ .matchScore = muonTrack.matchScoreMCHMFT (),
1243+ .matchChi2 = muonTrack.chi2MatchMCHMFT ()});
1244+
11731245 mMatchingCandidates [mchTrackIndex].emplace_back (MatchingCandidate{
11741246 .muonTrackId = muonTrack.globalIndex (),
11751247 .mftTrackId = mftTrackIndex,
11761248 .matchScore = muonTrack.matchScoreMCHMFT (),
11771249 .matchChi2 = muonTrack.chi2MatchMCHMFT ()});
11781250 }
1251+
1252+ // set the number of match attempts for this track
1253+ for (auto & mchTrackInfo : mMchTrackInfos ) {
1254+ const auto & mchTrack = muonTracks.rawIteratorAt (mchTrackInfo.first );
1255+ mchTrackInfo.second .nMatchAttempts = getMftMchMatchAttempts (collisions, bcs, mchTrack, mftTracks);
1256+ }
11791257 } else {
11801258 // build matching candidates from all time-compatible MFT-MCH pairs
11811259 for (const auto & muonTrack : muonTracks) {
@@ -1191,10 +1269,20 @@ struct GlobalMuonMatching {
11911269 continue ;
11921270 }
11931271
1272+ auto & mchTrackInfo = mMchTrackInfos [mchTrackIndex];
1273+ mchTrackInfo.index = mchTrackIndex;
1274+ mchTrackInfo.matchingCandidates .emplace_back (MatchingCandidate{
1275+ .mftTrackId = mftTrack.globalIndex ()});
1276+
11941277 mMatchingCandidates [mchTrackIndex].emplace_back (MatchingCandidate{
11951278 .mftTrackId = mftTrack.globalIndex ()});
11961279 }
11971280 }
1281+
1282+ // set the number of match attempts for this track
1283+ for (auto & mchTrackInfo : mMchTrackInfos ) {
1284+ mchTrackInfo.second .nMatchAttempts = mchTrackInfo.second .matchingCandidates .size ();
1285+ }
11981286 }
11991287
12001288 // sort the vectors of matching candidates in ascending order based on the matching chi2 value
@@ -1207,6 +1295,85 @@ struct GlobalMuonMatching {
12071295 }
12081296 }
12091297
1298+ template <class EVT , class BC , class TMUON , class TMFT >
1299+ void prepareEventMixingMatchingCandidates (EVT const & collisions,
1300+ BC const & bcs,
1301+ TMUON const & muonTracks,
1302+ TMFT const & mftTracks,
1303+ MyMFTCovariances const & mftCovs)
1304+ {
1305+ LOGF (info, " Filling mixed matching candidate tables" );
1306+
1307+ constexpr float deltaPhiMax = TMath::Pi () / 10 ;
1308+
1309+ for (auto & [mchIndex1, mchTrackInfo1] : mMchTrackInfos ) {
1310+ const auto & mchTrack1 = muonTracks.rawIteratorAt (mchIndex1);
1311+
1312+ if (!mchTrack1.has_collision ()) {
1313+ continue ;
1314+ }
1315+
1316+ const auto & collision1 = collisions.rawIteratorAt (mchTrack1.collisionId ());
1317+ const auto & bc1 = bcs.rawIteratorAt (collision1.bcId ());
1318+
1319+ auto phi1 = std::atan2 (mchTrack1.y (), mchTrack1.x ());
1320+ auto r1 = std::hypot (mchTrack1.x (), mchTrack1.y ());
1321+
1322+ auto nAttempts1 = mchTrackInfo1.nMatchAttempts ;
1323+
1324+ auto vz1 = collision1.posZ ();
1325+
1326+ for (const auto & [mchIndex2, mchTrackInfo2] : mMchTrackInfos ) {
1327+ const auto & mchTrack2 = muonTracks.rawIteratorAt (mchIndex2);
1328+
1329+ if (!mchTrack2.has_collision ()) {
1330+ continue ;
1331+ }
1332+
1333+ const auto & collision2 = collisions.rawIteratorAt (mchTrack2.collisionId ());
1334+ const auto & bc2 = bcs.rawIteratorAt (collision2.bcId ());
1335+
1336+ auto deltaBc = std::abs (static_cast <int64_t >(bc2.globalBC ()) - static_cast <int64_t >(bc1.globalBC ()));
1337+ if (deltaBc < 3564 ) {
1338+ continue ;
1339+ }
1340+
1341+ auto phi2 = std::atan2 (mchTrack2.y (), mchTrack2.x ());
1342+ auto deltaPhi = std::fabs (getDeltaPhi (phi1, phi2));
1343+ if (deltaPhi > deltaPhiMax) {
1344+ continue ;
1345+ }
1346+
1347+ auto r2 = std::hypot (mchTrack2.x (), mchTrack2.y ());
1348+ auto deltaR = r2 - r1;
1349+ if (deltaR > 10 ) {
1350+ continue ;
1351+ }
1352+
1353+ auto nAttempts2 = mchTrackInfo2.nMatchAttempts ;
1354+
1355+ float deltaAttempts = nAttempts2 - nAttempts1;
1356+ float deltaAttemptsRel = (nAttempts1 > 0 ) ? deltaAttempts / nAttempts1 : 0 ;
1357+ if (deltaAttemptsRel > 0.1 ) {
1358+ continue ;
1359+ }
1360+
1361+ auto vz2 = collision2.posZ ();
1362+ auto deltaZ = std::fabs (vz2 - vz1);
1363+ if (deltaZ > 1 ) {
1364+ continue ;
1365+ }
1366+
1367+ // add the candidates of MCH track #2 to the list of mixed candidates of track #1
1368+ mchTrackInfo1.mixedMatchingCandidates .push_back (mchTrackInfo2.matchingCandidates );
1369+ // update the muon track index of the mixed candidates to the index of track #1
1370+ for (auto & candidate : mchTrackInfo1.mixedMatchingCandidates .back ()) {
1371+ candidate.muonTrackId = mchIndex1;
1372+ }
1373+ }
1374+ }
1375+ }
1376+
12101377 template <typename TMFT , typename TMFTCOV >
12111378 o2::track::TrackParCovFwd transformMft (TMFT & mftTrack, TMFTCOV const & mftTrackCov)
12121379 {
@@ -1653,9 +1820,11 @@ struct GlobalMuonMatching {
16531820 mMchTrackToCandidateIndices .clear ();
16541821 mMchTrackMatchingCandidates .clear ();
16551822 mFwdTrackToGmmCandTrkIndex .clear ();
1823+ mMchTrackInfos .clear ();
16561824
16571825 LOGF (info, " Preparing candidates" );
16581826 prepareMatchingCandidates (collisions, bcs, muonTracks, mftTracks, mftCovs);
1827+ prepareEventMixingMatchingCandidates (collisions, bcs, muonTracks, mftTracks, mftCovs);
16591828
16601829 LOGF (info, " Processing candidates" );
16611830 processMatchingCandidates (collisions, muonTracks, mftTracks, mftCovs, clusters);
0 commit comments