@@ -1205,6 +1205,342 @@ struct AnalysisPrefilterSelection {
12051205 PROCESS_SWITCH (AnalysisPrefilterSelection, processDummy, " Do nothing" , true );
12061206};
12071207
1208+ struct AnalysisEventMixing {
1209+ OutputObj<THashList> fOutputList {" output" };
1210+ // Here one should provide the list of electron and muon candidate cuts in the same order as specified in the above
1211+ // single particle selection tasks to preserve the correspondence between the track cut name and its
1212+ // bit position in the cuts bitmap
1213+ // TODO: Create a configurable to specify exactly on which of the bits one should run the event mixing
1214+ Configurable<std::string> fConfigTrackCuts {" cfgTrackCuts" , " " , " Comma separated list of barrel track cuts" };
1215+ Configurable<std::string> fConfigMuonCuts {" cfgMuonCuts" , " " , " Comma separated list of muon cuts" };
1216+ Configurable<int > fConfigMixingDepth {" cfgMixingDepth" , 100 , " Number of Events stored for event mixing" };
1217+ Configurable<std::string> fConfigAddEventMixingHistogram {" cfgAddEventMixingHistogram" , " " , " Comma separated list of histograms" };
1218+ Configurable<std::string> ccdburl{" ccdburl" , " http://alice-ccdb.cern.ch" , " url of the ccdb repository" };
1219+ Configurable<std::string> grpmagPath{" grpmagPath" , " GLO/Config/GRPMagField" , " CCDB path of the GRPMagField object" };
1220+ Configurable<bool > fConfigAmbiguousHist {" cfgAmbiHist" , false , " Enable Ambiguous histograms for time association studies" };
1221+ Configurable<std::string> ccdbPathFlow{" ccdb-path-flow" , " Users/c/chizh/FlowResolution" , " path to the ccdb object for flow resolution factors" };
1222+ Configurable<bool > fConfigFlowReso {" cfgFlowReso" , false , " Enable loading of flow resolution factors from CCDB" };
1223+ Configurable<bool > fConfigSingleMuCumulants {" cfgSingleMuCumulants" , false , " Enable loading of flow resolution factors from CCDB" };
1224+ Configurable<std::string> fConfigAddJSONHistograms {" cfgAddJSONHistograms" , " " , " Histograms in JSON format" };
1225+
1226+ Service<o2::ccdb::BasicCCDBManager> ccdb;
1227+
1228+ o2::parameters::GRPMagField* grpmag = nullptr ;
1229+ TH1D* ResoFlowSP = nullptr ; // Resolution factors for flow analysis, this will be loaded from CCDB
1230+ TH1D* ResoFlowEP = nullptr ; // Resolution factors for flow analysis, this will be loaded from CCDB
1231+ TH2D* SingleMuv22m = nullptr ; // Single muon v22, loaded from CCDB
1232+ TH2D* SingleMuv24m = nullptr ; // Single muon v24, loaded from CCDB
1233+ TH2D* SingleMuv22p = nullptr ; // Single antimuon v22, loaded from CCDB
1234+ TH2D* SingleMuv24p = nullptr ; // Single antimuon v24, loaded from CCDB
1235+ int fCurrentRun ; // needed to detect if the run changed and trigger update of calibrations etc.
1236+
1237+ Filter filterEventSelected = aod::dqanalysisflags::isEventSelected == 1 ;
1238+ Filter filterTrackSelected = aod::dqanalysisflags::isBarrelSelected > 0 ;
1239+ Filter filterMuonTrackSelected = aod::dqanalysisflags::isMuonSelected > 0 ;
1240+
1241+ HistogramManager* fHistMan ;
1242+ // NOTE: The bit mask is required to run pairing just based on the desired electron/muon candidate cuts
1243+ uint32_t fTwoTrackFilterMask = 0 ;
1244+ uint32_t fTwoMuonFilterMask = 0 ;
1245+ std::vector<std::vector<TString>> fTrackHistNames ;
1246+ std::vector<std::vector<TString>> fMuonHistNames ;
1247+ std::vector<std::vector<TString>> fTrackMuonHistNames ;
1248+
1249+ NoBinningPolicy<aod::dqanalysisflags::MixingHash> hashBin;
1250+
1251+ void init (o2::framework::InitContext& context)
1252+ {
1253+ if (context.mOptions .get <bool >(" processDummy" )) {
1254+ return ;
1255+ }
1256+
1257+ fCurrentRun = 0 ;
1258+
1259+ ccdb->setURL (ccdburl.value );
1260+ ccdb->setCaching (true );
1261+ ccdb->setLocalObjectValidityChecking ();
1262+
1263+ VarManager::SetDefaultVarNames ();
1264+ fHistMan = new HistogramManager (" analysisHistos" , " aa" , VarManager::kNVars );
1265+ fHistMan ->SetUseDefaultVariableNames (kTRUE );
1266+ fHistMan ->SetDefaultVarNames (VarManager::fgVariableNames, VarManager::fgVariableUnits);
1267+
1268+ // Keep track of all the histogram class names to avoid composing strings in the event mixing pairing
1269+ TString histNames = " " ;
1270+ if (context.mOptions .get <bool >(" processBarrelSkimmed" ) || context.mOptions .get <bool >(" processBarrelVnSkimmed" )) {
1271+ TString cutNames = fConfigTrackCuts .value ;
1272+ if (!cutNames.IsNull ()) {
1273+ std::unique_ptr<TObjArray> objArray (cutNames.Tokenize (" ," ));
1274+ for (int icut = 0 ; icut < objArray->GetEntries (); ++icut) {
1275+ std::vector<TString> names = {
1276+ Form (" PairsBarrelMEPM_%s" , objArray->At (icut)->GetName ()),
1277+ Form (" PairsBarrelMEPP_%s" , objArray->At (icut)->GetName ()),
1278+ Form (" PairsBarrelMEMM_%s" , objArray->At (icut)->GetName ())};
1279+ histNames += Form (" %s;%s;%s;" , names[0 ].Data (), names[1 ].Data (), names[2 ].Data ());
1280+ fTrackHistNames .push_back (names);
1281+ fTwoTrackFilterMask |= (static_cast <uint32_t >(1 ) << icut);
1282+ }
1283+ }
1284+ }
1285+ if (context.mOptions .get <bool >(" processMuonSkimmed" ) || context.mOptions .get <bool >(" processMuonVnSkimmed" ) || context.mOptions .get <bool >(" processMuonVnCentrSkimmed" ) || context.mOptions .get <bool >(" processMuonVnExtraSkimmed" )) {
1286+ TString cutNames = fConfigMuonCuts .value ;
1287+ if (!cutNames.IsNull ()) {
1288+ std::unique_ptr<TObjArray> objArray (cutNames.Tokenize (" ," ));
1289+ for (int icut = 0 ; icut < objArray->GetEntries (); ++icut) {
1290+ std::vector<TString> names = {
1291+ Form (" PairsMuonMEPM_%s" , objArray->At (icut)->GetName ()),
1292+ Form (" PairsMuonMEPP_%s" , objArray->At (icut)->GetName ()),
1293+ Form (" PairsMuonMEMM_%s" , objArray->At (icut)->GetName ())};
1294+ if (fConfigAmbiguousHist ) {
1295+ histNames += Form (" %s;%s;%s;%s_unambiguous;%s_unambiguous;%s_unambiguous;" , names[0 ].Data (), names[1 ].Data (), names[2 ].Data (), names[0 ].Data (), names[1 ].Data (), names[2 ].Data ());
1296+ } else {
1297+ histNames += Form (" %s;%s;%s;" , names[0 ].Data (), names[1 ].Data (), names[2 ].Data ());
1298+ }
1299+ fMuonHistNames .push_back (names);
1300+ fTwoMuonFilterMask |= (static_cast <uint32_t >(1 ) << icut);
1301+ }
1302+ }
1303+ }
1304+ if (context.mOptions .get <bool >(" processBarrelMuonSkimmed" )) {
1305+ TString cutNamesBarrel = fConfigTrackCuts .value ;
1306+ TString cutNamesMuon = fConfigMuonCuts .value ;
1307+ if (!cutNamesBarrel.IsNull () && !cutNamesMuon.IsNull ()) {
1308+ std::unique_ptr<TObjArray> objArrayBarrel (cutNamesBarrel.Tokenize (" ," ));
1309+ std::unique_ptr<TObjArray> objArrayMuon (cutNamesMuon.Tokenize (" ," ));
1310+ if (objArrayBarrel->GetEntries () == objArrayMuon->GetEntries ()) { // one must specify equal number of barrel and muon cuts
1311+ for (int icut = 0 ; icut < objArrayBarrel->GetEntries (); ++icut) {
1312+ std::vector<TString> names = {
1313+ Form (" PairsEleMuMEPM_%s_%s" , objArrayBarrel->At (icut)->GetName (), objArrayMuon->At (icut)->GetName ()),
1314+ Form (" PairsEleMuMEPP_%s_%s" , objArrayBarrel->At (icut)->GetName (), objArrayMuon->At (icut)->GetName ()),
1315+ Form (" PairsEleMuMEMM_%s_%s" , objArrayBarrel->At (icut)->GetName (), objArrayMuon->At (icut)->GetName ())};
1316+ histNames += Form (" %s;%s;%s;" , names[0 ].Data (), names[1 ].Data (), names[2 ].Data ());
1317+ fTrackMuonHistNames .push_back (names);
1318+ fTwoTrackFilterMask |= (static_cast <uint32_t >(1 ) << icut);
1319+ fTwoMuonFilterMask |= (static_cast <uint32_t >(1 ) << icut);
1320+ }
1321+ }
1322+ }
1323+ }
1324+
1325+ DefineHistograms (fHistMan , histNames.Data (), fConfigAddEventMixingHistogram ); // define all histograms
1326+ // Additional histograms via JSON
1327+ dqhistograms::AddHistogramsFromJSON (fHistMan , fConfigAddJSONHistograms .value .c_str ());
1328+ VarManager::SetUseVars (fHistMan ->GetUsedVars ()); // provide the list of required variables so that VarManager knows what to fill
1329+ fOutputList .setObject (fHistMan ->GetMainHistogramList ());
1330+ }
1331+
1332+ template <uint32_t TEventFillMap, int TPairType, typename TTracks1, typename TTracks2>
1333+ void runMixedPairing (TTracks1 const & tracks1, TTracks2 const & tracks2)
1334+ {
1335+
1336+ unsigned int ncuts = fTrackHistNames .size ();
1337+ std::vector<std::vector<TString>> histNames = fTrackHistNames ;
1338+ if constexpr (TPairType == pairTypeMuMu) {
1339+ ncuts = fMuonHistNames .size ();
1340+ histNames = fMuonHistNames ;
1341+ }
1342+ if constexpr (TPairType == pairTypeEMu) {
1343+ ncuts = fTrackMuonHistNames .size ();
1344+ histNames = fTrackMuonHistNames ;
1345+ }
1346+
1347+ uint32_t twoTrackFilter = 0 ;
1348+ uint32_t mult_dimuons = 0 ;
1349+ for (auto & track1 : tracks1) {
1350+ for (auto & track2 : tracks2) {
1351+ if constexpr (TPairType == VarManager::kDecayToMuMu ) {
1352+ twoTrackFilter = static_cast <uint32_t >(track1.isMuonSelected ()) & static_cast <uint32_t >(track2.isMuonSelected ()) & fTwoMuonFilterMask ;
1353+ }
1354+ if (twoTrackFilter && track1.sign () * track2.sign () < 0 ) {
1355+ mult_dimuons++;
1356+ }
1357+ } // end for (track2)
1358+ } // end for (track1)
1359+ VarManager::fgValues[VarManager::kMultDimuonsME ] = mult_dimuons;
1360+
1361+ twoTrackFilter = 0 ;
1362+ for (auto & track1 : tracks1) {
1363+ for (auto & track2 : tracks2) {
1364+ if constexpr (TPairType == VarManager::kDecayToEE ) {
1365+ twoTrackFilter = static_cast <uint32_t >(track1.isBarrelSelected ()) & static_cast <uint32_t >(track2.isBarrelSelected ()) & fTwoTrackFilterMask ;
1366+ }
1367+ if constexpr (TPairType == VarManager::kDecayToMuMu ) {
1368+ twoTrackFilter = static_cast <uint32_t >(track1.isMuonSelected ()) & static_cast <uint32_t >(track2.isMuonSelected ()) & fTwoMuonFilterMask ;
1369+ if (fConfigSingleMuCumulants ) {
1370+ VarManager::FillTwoMixEventsCumulants (SingleMuv22m, SingleMuv24m, SingleMuv22p, SingleMuv24p, track1, track2);
1371+ }
1372+ }
1373+ if constexpr (TPairType == VarManager::kElectronMuon ) {
1374+ twoTrackFilter = static_cast <uint32_t >(track1.isBarrelSelected ()) & static_cast <uint32_t >(track2.isMuonSelected ()) & fTwoTrackFilterMask ;
1375+ }
1376+
1377+ if (!twoTrackFilter) { // the tracks must have at least one filter bit in common to continue
1378+ continue ;
1379+ }
1380+ VarManager::FillPairME<TEventFillMap, TPairType>(track1, track2);
1381+
1382+ for (unsigned int icut = 0 ; icut < ncuts; icut++) {
1383+ if (twoTrackFilter & (static_cast <uint32_t >(1 ) << icut)) {
1384+ if (track1.sign () * track2.sign () < 0 ) {
1385+ fHistMan ->FillHistClass (histNames[icut][0 ].Data (), VarManager::fgValues);
1386+ if (fConfigAmbiguousHist && !(track1.isAmbiguous () || track2.isAmbiguous ())) {
1387+ fHistMan ->FillHistClass (Form (" %s_unambiguous" , histNames[icut][0 ].Data ()), VarManager::fgValues);
1388+ }
1389+ } else {
1390+ if (track1.sign () > 0 ) {
1391+ fHistMan ->FillHistClass (histNames[icut][1 ].Data (), VarManager::fgValues);
1392+ if (fConfigAmbiguousHist && !(track1.isAmbiguous () || track2.isAmbiguous ())) {
1393+ fHistMan ->FillHistClass (Form (" %s_unambiguous" , histNames[icut][1 ].Data ()), VarManager::fgValues);
1394+ }
1395+ } else {
1396+ fHistMan ->FillHistClass (histNames[icut][2 ].Data (), VarManager::fgValues);
1397+ if (fConfigAmbiguousHist && !(track1.isAmbiguous () || track2.isAmbiguous ())) {
1398+ fHistMan ->FillHistClass (Form (" %s_unambiguous" , histNames[icut][2 ].Data ()), VarManager::fgValues);
1399+ }
1400+ }
1401+ }
1402+ } // end if (filter bits)
1403+ } // end for (cuts)
1404+ } // end for (track2)
1405+ } // end for (track1)
1406+ }
1407+
1408+ // barrel-barrel and muon-muon event mixing
1409+ template <int TPairType, uint32_t TEventFillMap, typename TEvents, typename TTracks>
1410+ void runSameSide (TEvents& events, TTracks const & tracks, Preslice<TTracks>& preSlice)
1411+ {
1412+ if (events.size () > 0 && fCurrentRun != events.begin ().runNumber ()) {
1413+ grpmag = ccdb->getForTimeStamp <o2::parameters::GRPMagField>(grpmagPath, events.begin ().timestamp ());
1414+ if (grpmag != nullptr ) {
1415+ VarManager::SetMagneticField (grpmag->getNominalL3Field ());
1416+ } else {
1417+ LOGF (fatal, " GRP object is not available in CCDB at timestamp=%llu" , events.begin ().timestamp ());
1418+ }
1419+ if (fConfigFlowReso ) {
1420+ TString PathFlow = ccdbPathFlow.value ;
1421+ TString ccdbPathFlowSP = Form (" %s/ScalarProduct" , PathFlow.Data ());
1422+ TString ccdbPathFlowEP = Form (" %s/EventPlane" , PathFlow.Data ());
1423+ ResoFlowSP = ccdb->getForTimeStamp <TH1D>(ccdbPathFlowSP.Data (), events.begin ().timestamp ());
1424+ ResoFlowEP = ccdb->getForTimeStamp <TH1D>(ccdbPathFlowEP.Data (), events.begin ().timestamp ());
1425+ if (ResoFlowSP == nullptr || ResoFlowEP == nullptr ) {
1426+ LOGF (fatal, " Resolution factor is not available in CCDB at timestamp=%llu" , events.begin ().timestamp ());
1427+ }
1428+ }
1429+ if (fConfigSingleMuCumulants ) {
1430+ TString PathFlow = ccdbPathFlow.value ;
1431+ TString ccdbPathMuv22m = Form (" %s/SingleMuv22m" , PathFlow.Data ());
1432+ TString ccdbPathMuv24m = Form (" %s/SingleMuv24m" , PathFlow.Data ());
1433+ TString ccdbPathMuv22p = Form (" %s/SingleMuv22p" , PathFlow.Data ());
1434+ TString ccdbPathMuv24p = Form (" %s/SingleMuv24p" , PathFlow.Data ());
1435+ SingleMuv22m = ccdb->getForTimeStamp <TH2D>(ccdbPathMuv22m.Data (), events.begin ().timestamp ());
1436+ SingleMuv24m = ccdb->getForTimeStamp <TH2D>(ccdbPathMuv24m.Data (), events.begin ().timestamp ());
1437+ SingleMuv22p = ccdb->getForTimeStamp <TH2D>(ccdbPathMuv22p.Data (), events.begin ().timestamp ());
1438+ SingleMuv24p = ccdb->getForTimeStamp <TH2D>(ccdbPathMuv24p.Data (), events.begin ().timestamp ());
1439+ if (SingleMuv22m == nullptr || SingleMuv24m == nullptr || SingleMuv22p == nullptr || SingleMuv24p == nullptr ) {
1440+ LOGF (fatal, " Single muon cumulants are not available in CCDB at timestamp=%llu" , events.begin ().timestamp ());
1441+ }
1442+ }
1443+ fCurrentRun = events.begin ().runNumber ();
1444+ }
1445+
1446+ events.bindExternalIndices (&tracks);
1447+ int mixingDepth = fConfigMixingDepth .value ;
1448+ for (auto & [event1, event2] : selfCombinations (hashBin, mixingDepth, -1 , events, events)) {
1449+ VarManager::ResetValues (0 , VarManager::kNVars );
1450+ VarManager::FillEvent<TEventFillMap>(event1, VarManager::fgValues);
1451+
1452+ auto tracks1 = tracks.sliceBy (preSlice, event1.globalIndex ());
1453+ tracks1.bindExternalIndices (&events);
1454+
1455+ auto tracks2 = tracks.sliceBy (preSlice, event2.globalIndex ());
1456+ tracks2.bindExternalIndices (&events);
1457+
1458+ VarManager::FillTwoMixEvents<TEventFillMap>(event1, event2, tracks1, tracks2);
1459+ if (fConfigFlowReso ) {
1460+ VarManager::FillTwoMixEventsFlowResoFactor (ResoFlowSP, ResoFlowEP);
1461+ }
1462+ runMixedPairing<TEventFillMap, TPairType>(tracks1, tracks2);
1463+ } // end event loop
1464+ }
1465+
1466+ // barrel-muon event mixing
1467+ template <uint32_t TEventFillMap, typename TEvents, typename TTracks, typename TMuons>
1468+ void runBarrelMuon (TEvents& events, TTracks const & tracks, TMuons const & muons)
1469+ {
1470+ if (events.size () > 0 && fCurrentRun != events.begin ().runNumber ()) {
1471+ grpmag = ccdb->getForTimeStamp <o2::parameters::GRPMagField>(grpmagPath, events.begin ().timestamp ());
1472+ if (grpmag != nullptr ) {
1473+ VarManager::SetMagneticField (grpmag->getNominalL3Field ());
1474+ } else {
1475+ LOGF (fatal, " GRP object is not available in CCDB at timestamp=%llu" , events.begin ().timestamp ());
1476+ }
1477+ fCurrentRun = events.begin ().runNumber ();
1478+ }
1479+
1480+ events.bindExternalIndices (&muons);
1481+
1482+ for (auto & [event1, event2] : selfCombinations (hashBin, 100 , -1 , events, events)) {
1483+ VarManager::ResetValues (0 , VarManager::kNVars );
1484+ VarManager::FillEvent<TEventFillMap>(event1, VarManager::fgValues);
1485+
1486+ auto tracks1 = tracks.sliceBy (perEventsSelectedT, event1.globalIndex ());
1487+ tracks1.bindExternalIndices (&events);
1488+
1489+ auto muons2 = muons.sliceBy (perEventsSelectedM, event2.globalIndex ());
1490+ muons2.bindExternalIndices (&events);
1491+
1492+ runMixedPairing<TEventFillMap, pairTypeEMu>(tracks1, muons2);
1493+ } // end event loop
1494+ }
1495+
1496+ Preslice<soa::Filtered<MyBarrelTracksSelected>> perEventsSelectedT = aod::reducedtrack::reducedeventId;
1497+ Preslice<soa::Filtered<MyMuonTracksSelected>> perEventsSelectedM = aod::reducedmuon::reducedeventId;
1498+
1499+ void processBarrelSkimmed (soa::Filtered<MyEventsHashSelected>& events, soa::Filtered<MyBarrelTracksSelected> const & tracks)
1500+ {
1501+ runSameSide<pairTypeEE, gkEventFillMap>(events, tracks, perEventsSelectedT);
1502+ }
1503+ void processMuonSkimmed (soa::Filtered<MyEventsHashSelected>& events, soa::Filtered<MyMuonTracksSelected> const & muons)
1504+ {
1505+ runSameSide<pairTypeMuMu, gkEventFillMap>(events, muons, perEventsSelectedM);
1506+ }
1507+ void processBarrelMuonSkimmed (soa::Filtered<MyEventsHashSelected>& events, soa::Filtered<MyBarrelTracksSelected> const & tracks, soa::Filtered<MyMuonTracksSelected> const & muons)
1508+ {
1509+ runBarrelMuon<gkEventFillMap>(events, tracks, muons);
1510+ }
1511+ void processBarrelVnSkimmed (soa::Filtered<MyEventsHashSelectedQvector>& events, soa::Filtered<MyBarrelTracksSelected> const & tracks)
1512+ {
1513+ runSameSide<pairTypeEE, gkEventFillMapWithQvector>(events, tracks, perEventsSelectedT);
1514+ }
1515+ void processMuonVnSkimmed (soa::Filtered<MyEventsHashSelectedQvector>& events, soa::Filtered<MyMuonTracksSelected> const & muons)
1516+ {
1517+ runSameSide<pairTypeMuMu, gkEventFillMapWithQvector>(events, muons, perEventsSelectedM);
1518+ }
1519+ void processMuonVnCentrSkimmed (soa::Filtered<MyEventsHashSelectedQvectorCentr>& events, soa::Filtered<MyMuonTracksSelected> const & muons)
1520+ {
1521+ runSameSide<pairTypeMuMu, gkEventFillMapWithQvectorCentr>(events, muons, perEventsSelectedM);
1522+ }
1523+ void processMuonVnExtraSkimmed (soa::Filtered<MyEventsHashSelectedQvectorExtra>& events, soa::Filtered<MyMuonTracksSelected> const & muons)
1524+ {
1525+ runSameSide<pairTypeMuMu, gkEventFillMapWithCovQvectorExtraWithRefFlow>(events, muons, perEventsSelectedM);
1526+ }
1527+ // TODO: This is a dummy process function for the case when the user does not want to run any of the process functions (no event mixing)
1528+ // If there is no process function enabled, the workflow hangs
1529+ void processDummy (MyEvents&)
1530+ {
1531+ // do nothing
1532+ }
1533+
1534+ PROCESS_SWITCH (AnalysisEventMixing, processBarrelSkimmed, " Run barrel-barrel mixing on skimmed tracks" , false );
1535+ PROCESS_SWITCH (AnalysisEventMixing, processMuonSkimmed, " Run muon-muon mixing on skimmed muons" , false );
1536+ PROCESS_SWITCH (AnalysisEventMixing, processBarrelMuonSkimmed, " Run barrel-muon mixing on skimmed tracks/muons" , false );
1537+ PROCESS_SWITCH (AnalysisEventMixing, processBarrelVnSkimmed, " Run barrel-barrel vn mixing on skimmed tracks" , false );
1538+ PROCESS_SWITCH (AnalysisEventMixing, processMuonVnSkimmed, " Run muon-muon vn mixing on skimmed tracks" , false );
1539+ PROCESS_SWITCH (AnalysisEventMixing, processMuonVnCentrSkimmed, " Run muon-muon vn mixing on skimmed tracks from central framework" , false );
1540+ PROCESS_SWITCH (AnalysisEventMixing, processMuonVnExtraSkimmed, " Run muon-muon vn mixing on skimmed tracks from GFW" , false );
1541+ PROCESS_SWITCH (AnalysisEventMixing, processDummy, " Dummy function" , false );
1542+ };
1543+
12081544// Run the same-event pairing
12091545// This task assumes that both legs of the resonance fulfill the same cuts (symmetric decay channel)
12101546// Runs combinatorics for barrel-barrel, muon-muon and barrel-muon combinations
0 commit comments