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