6161#include < experimental/type_traits>
6262#include < iterator>
6363#include < memory>
64+ #include < set>
6465#include < string>
6566#include < tuple>
6667#include < type_traits>
@@ -105,7 +106,7 @@ struct CorrelationTask {
105106 O2_DEFINE_CONFIGURABLE (cfgTwoTrackCut, float , -1 , " Two track cut: -1 = off; >0 otherwise distance value (suggested: 0.02)" );
106107 O2_DEFINE_CONFIGURABLE (cfgTwoTrackCutMinRadius, float , 0 .8f , " Two track cut: radius in m from which two track cuts are applied" );
107108 O2_DEFINE_CONFIGURABLE (cfgLocalEfficiency, int , 0 , " 0 = OFF and 1 = ON for local efficiency" );
108- O2_DEFINE_CONFIGURABLE (cfgDropStepRECO, bool , false , " choice to drop step RECO if efficiency correction is used" )
109+ O2_DEFINE_CONFIGURABLE (cfgDropStepRECO, bool , false , " choice to drop step RECO if efficiency correction is used" );
109110 O2_DEFINE_CONFIGURABLE (cfgCentBinsForMC, int , 0 , " 0 = OFF and 1 = ON for data like multiplicity/centrality bins for MC steps" );
110111 O2_DEFINE_CONFIGURABLE (cfgRequireRecoCollision, int , 1 , " 0 = all generated collisions; 1 = only generated collisions with exactly 1 reconstructed collision; 2 = select reconstructed collision with largest number of contributors per generated collision" )
111112 O2_DEFINE_CONFIGURABLE (cfgTrackBitMask, uint16_t , 0 , " BitMask for track selection systematics; refer to the enum TrackSelectionCuts in filtering task" );
@@ -118,6 +119,7 @@ struct CorrelationTask {
118119 O2_DEFINE_CONFIGURABLE (cfgEfficiencyTrigger, std::string, " " , " CCDB path to efficiency object for trigger particles" )
119120 O2_DEFINE_CONFIGURABLE (cfgEfficiencyAssociated, std::string, " " , " CCDB path to efficiency object for associated particles" )
120121 O2_DEFINE_CONFIGURABLE (cfgUseRun3MagField, bool , false , " false: Run 2 GRPObject; true: Run 3 GRPMagField" )
122+ O2_DEFINE_CONFIGURABLE (cfgMultWeight, std::string, " " , " Multiplicity/Centrality weight" )
121123
122124 O2_DEFINE_CONFIGURABLE (cfgNoMixedEvents, int , 5 , " Number of mixed events per event" )
123125 O2_DEFINE_CONFIGURABLE (cfgRejectMixedPhiProngEvents, bool , true , " Reject associated hadrons from either mixed-phi prong event" )
@@ -132,6 +134,8 @@ struct CorrelationTask {
132134 O2_DEFINE_CONFIGURABLE (cfgPtDepMLbkg, std::vector<float >, {}, " pT interval for ML training" )
133135 O2_DEFINE_CONFIGURABLE (cfgPtCentDepMLbkgSel, std::vector<float >, {}, " Bkg ML selection" )
134136 O2_DEFINE_CONFIGURABLE (cfgPtCentDepMLpromptSel, std::vector<float >, {}, " Prompt ML selection" )
137+ O2_DEFINE_CONFIGURABLE (cfgPtCentDepMLnonpromptSel, std::vector<float >, {}, " Non-prompt ML selection (maximum cut on the non-prompt score)" )
138+ O2_DEFINE_CONFIGURABLE (cfgMcClassSel, int , -1 , " MC class for 2-prong efficiency: -1=auto(promptSel empty?all:prompt), 0=all, 1=prompt, 2=non-prompt" )
135139
136140 std::unordered_map<int64_t , int64_t > cfTrackToCFCollision;
137141
@@ -183,6 +187,7 @@ struct CorrelationTask {
183187 bool mPairCuts = false ;
184188 THn* mEfficiencyTrigger = nullptr ;
185189 THn* mEfficiencyAssociated = nullptr ;
190+ TH1 * mMultWeight = nullptr ;
186191 bool efficiencyLoaded = false ;
187192 } cfg;
188193
@@ -220,6 +225,14 @@ struct CorrelationTask {
220225 if (!cfgPtCentDepMLpromptSel->empty () && cfgPtCentDepMLpromptSel->size () != cfgPtDepMLbkg->size ()) {
221226 LOGF (fatal, " cfgPtDepMLbkg and cfgPtCentDepMLpromptSel must be same size." );
222227 }
228+ if (!cfgPtCentDepMLnonpromptSel->empty () && cfgPtCentDepMLnonpromptSel->size () != cfgPtDepMLbkg->size ())
229+ LOGF (fatal, " cfgPtDepMLbkg and cfgPtCentDepMLnonpromptSel must be same size." );
230+ if (cfgPtCentDepMLpromptSel->size () > 0 )
231+ LOGF (info, " Prompt selection scores in use." );
232+ else
233+ LOGF (info, " Using inclusive selections." );
234+ if (cfgPtCentDepMLnonpromptSel->size () > 0 )
235+ LOGF (info, " Non-prompt selection scores in use." );
223236 }
224237 registry.add (" yields" , " multiplicity/centrality vs pT vs eta" , {HistType::kTH3F , {{100 , 0 , 100 , " /multiplicity/centrality" }, {40 , 0 , 20 , " p_{T}" }, {100 , -2 , 2 , " #eta" }}});
225238 registry.add (" etaphi" , " multiplicity/centrality vs eta vs phi" , {HistType::kTH3F , {{100 , 0 , 100 , " multiplicity/centrality" }, {100 , -2 , 2 , " #eta" }, {200 , 0 , o2::constants::math::TwoPI, " #varphi" }}});
@@ -229,6 +242,7 @@ struct CorrelationTask {
229242 const AxisSpec& a = AxisSpec (axisInvMass);
230243 AxisSpec axisSpecMass = {1000 , a.binEdges [0 ], a.binEdges [a.getNbins ()]};
231244 registry.add (" invMass" , " 2-prong invariant mass (GeV/c^2)" , {HistType::kTHnSparseF , {axisSpecMass, axisPtTrigger, axisMultiplicity, axisVertex}});
245+ registry.add (" invMassCorrected" , " 2-prong invariant mass with efficiency correction (GeV/c^2)" , {HistType::kTHnSparseF , {axisSpecMass, axisPtTrigger, axisMultiplicity, axisVertex}});
232246 if (doprocessSame2Prong2Prong || doprocessSame2Prong2ProngML) {
233247 registry.add (" invMassTwoPart" , " 2D 2-prong invariant mass (GeV/c^2)" , {HistType::kTHnSparseF , {axisSpecMass, axisSpecMass, axisPtTrigger, axisPtAssoc, axisMultiplicity}});
234248 registry.add (" invMassTwoPartDPhi" , " 2D 2-prong invariant mass (GeV/c^2)" , {HistType::kTHnSparseF , {axisSpecMass, axisSpecMass, axisPtTrigger, axisPtAssoc, axisDeltaPhi}});
@@ -395,12 +409,16 @@ struct CorrelationTask {
395409 return (track.decay () != aod::cf2prongtrack::D0ToPiK ||
396410 (track.mlProbD0 ()[0 ] < cfgPtCentDepMLbkgSel->at (idx) && // not background
397411 (cfgPtCentDepMLpromptSel->empty () ||
398- track.mlProbD0 ()[1 ] > cfgPtCentDepMLpromptSel->at (idx)))) && // prompt
412+ track.mlProbD0 ()[1 ] > cfgPtCentDepMLpromptSel->at (idx)) && // prompt
413+ (cfgPtCentDepMLnonpromptSel->empty () ||
414+ track.mlProbD0 ()[2 ] < cfgPtCentDepMLnonpromptSel->at (idx)))) && // not non-prompt
399415
400416 ((track.decay () != aod::cf2prongtrack::D0barToKPi && track.decay () != aod::cf2prongtrack::D0barToKPiExclusive) ||
401417 (track.mlProbD0bar ()[0 ] < cfgPtCentDepMLbkgSel->at (idx) && // not background
402418 (cfgPtCentDepMLpromptSel->empty () ||
403- track.mlProbD0bar ()[1 ] > cfgPtCentDepMLpromptSel->at (idx)))); // prompt
419+ track.mlProbD0bar ()[1 ] > cfgPtCentDepMLpromptSel->at (idx)) && // prompt
420+ (cfgPtCentDepMLnonpromptSel->empty () ||
421+ track.mlProbD0bar ()[2 ] < cfgPtCentDepMLnonpromptSel->at (idx)))); // not non-prompt
404422 }
405423
406424 template <class T >
@@ -428,6 +446,8 @@ struct CorrelationTask {
428446 using HasInvMass = decltype (std::declval<T&>().invMass());
429447 template <class T >
430448 using HasPDGCode = decltype (std::declval<T&>().pdgCode());
449+ template <class T >
450+ using HasTrackType = decltype (std::declval<T&>().trackType());
431451
432452 template <typename TCollision, typename TTracks1, typename TTracks2>
433453 void fillQA (const TCollision& collision, float multiplicity, float posZ, const TTracks1& tracks1, const TTracks2& tracks2)
@@ -442,7 +462,15 @@ struct CorrelationTask {
442462 continue ;
443463 }
444464 }
465+ float triggerWeight = 1 .0f ;
466+ if (cfg.mMultWeight ) {
467+ triggerWeight *= cfg.mMultWeight ->GetBinContent (cfg.mMultWeight ->FindBin (multiplicity));
468+ }
469+ if (cfg.mEfficiencyTrigger ) {
470+ triggerWeight *= getEfficiencyCorrection (cfg.mEfficiencyTrigger , track1.eta (), track1.pt (), multiplicity, collision.posZ ());
471+ }
445472 registry.fill (HIST (" invMass" ), track1.invMass (), track1.pt (), multiplicity, posZ);
473+ registry.fill (HIST (" invMassCorrected" ), track1.invMass (), track1.pt (), multiplicity, posZ, triggerWeight);
446474 for (const auto & track2 : tracks2) {
447475 if constexpr (std::experimental::is_detected<HasInvMass, typename TTracks2::iterator>::value && std::experimental::is_detected<HasDecay, typename TTracks2::iterator>::value) {
448476 if (doprocessSame2Prong2Prong || doprocessMixed2Prong2Prong || doprocessSame2Prong2ProngML || doprocessMixed2Prong2ProngML) {
@@ -628,6 +656,9 @@ struct CorrelationTask {
628656 }
629657 }
630658
659+ static std::set<std::tuple<int , int >> duplicates; // workaround for old derived datasets involving 2prongs when cfgDecayParticleMask = 0
660+ duplicates.clear ();
661+
631662 for (const auto & track1 : tracks1) {
632663 // LOGF(info, "Track %f | %f | %f %d %d", track1.eta(), track1.phi(), track1.pt(), track1.isGlobalTrack(), track1.isGlobalTrackSDD());
633664
@@ -637,6 +668,15 @@ struct CorrelationTask {
637668 }
638669 }
639670
671+ if constexpr (std::experimental::is_detected<HasProng0Id, typename TTracks1::iterator>::value) {
672+ if (cfgDecayParticleMask == 0 ) {
673+ auto a = std::make_tuple (track1.cfTrackProng0Id (), track1.cfTrackProng1Id ()); // workaround allows for use of D0bar in old derived datasets
674+ if (duplicates.contains (a))
675+ continue ;
676+ duplicates.insert (a);
677+ }
678+ }
679+
640680 // sign check and PDG code special cases
641681 if constexpr (std::experimental::is_detected<HasPDGCode, typename TTracks1::iterator>::value) {
642682 // If the MC trigger particle is on the trigger PDG code list, we will accept them regardless of their charge.
@@ -691,6 +731,9 @@ struct CorrelationTask {
691731
692732 float triggerWeight = eventWeight;
693733 if constexpr (step == CorrelationContainer::kCFStepCorrected ) {
734+ if (cfg.mMultWeight ) {
735+ triggerWeight *= cfg.mMultWeight ->GetBinContent (cfg.mMultWeight ->FindBin (multiplicity));
736+ }
694737 if (cfg.mEfficiencyTrigger ) {
695738 triggerWeight *= getEfficiencyCorrection (cfg.mEfficiencyTrigger , track1.eta (), track1.pt (), multiplicity, posZ);
696739 }
@@ -777,6 +820,7 @@ struct CorrelationTask {
777820 // If decay attributes are found for the second track/particle, we assume 2p-2p correlation
778821 if constexpr (std::experimental::is_detected<HasMcDecay, typename TTracks2::iterator>::value) {
779822 if ((((track2.mcDecay ()) != aod::cf2prongtrack::D0ToPiK) && ((track2.mcDecay ()) != aod::cf2prongtrack::D0barToKPiExclusive)) || (!cfgPtCentDepMLpromptSel->empty () && (track2.decay () & aod::cf2prongmcpart::Prompt) == 0 )) {
823+
780824 continue ;
781825 }
782826 } else if constexpr (std::experimental::is_detected<HasDecay, typename TTracks2::iterator>::value) {
@@ -929,6 +973,11 @@ struct CorrelationTask {
929973 }
930974 LOGF (info, " Loaded efficiency histogram for associated particles from %s (%p)" , cfgEfficiencyAssociated.value .c_str (), static_cast <void *>(cfg.mEfficiencyAssociated ));
931975 }
976+ if (cfgMultWeight.value .empty () == false ) {
977+ TFile* pf = TFile::Open (cfgMultWeight.value .c_str (), " READ" );
978+ cfg.mMultWeight = reinterpret_cast <TH1 *>(pf->Get (" hWeight" ));
979+ LOGF (info, " Loaded multiplicity weight histogram for associated particles from %s" , cfgMultWeight.value .c_str ());
980+ }
932981 cfg.efficiencyLoaded = true ;
933982 }
934983
@@ -998,6 +1047,7 @@ struct CorrelationTask {
9981047 same->fillEvent (multiplicity, CorrelationContainer::kCFStepReconstructed );
9991048 fillCorrelations<CorrelationContainer::kCFStepReconstructed >(same, tracks1, tracks2, multiplicity, collision.posZ (), field, 1 .0f );
10001049 }
1050+
10011051 if (hasEfficiency) {
10021052 same->fillEvent (multiplicity, CorrelationContainer::kCFStepCorrected );
10031053 fillCorrelations<CorrelationContainer::kCFStepCorrected >(same, tracks1, tracks2, multiplicity, collision.posZ (), field, 1 .0f );
@@ -1363,6 +1413,19 @@ struct CorrelationTask {
13631413 }
13641414 // Primaries
13651415 p2indexCache.clear ();
1416+ std::set<int > signalMC; // mc global index cache
1417+ std::set<int > reflectionMC; // mc global index cache
1418+ // Resolve the MC class to use for the efficiency denominator/numerator.
1419+ // cfgMcClassSel: -1=auto (promptSel empty -> all, else prompt), 0=all, 1=prompt, 2=non-prompt.
1420+ int mcClass = cfgMcClassSel.value ;
1421+ if (mcClass < 0 ) {
1422+ mcClass = cfgPtCentDepMLpromptSel->empty () ? 0 : 1 ;
1423+ }
1424+ auto mcClassSel = [&](const auto & mp) -> bool {
1425+ bool isP = (mp.decay () & aod::cf2prongmcpart::Prompt) != 0 ;
1426+ bool isNP = (mp.decay () & aod::cf2prongmcpart::NonPrompt) != 0 ;
1427+ return (mcClass == 0 ) || (mcClass == 1 && isP) || (mcClass == 2 && isNP);
1428+ };
13661429 for (const auto & mcParticle : mcParticles) {
13671430 if (std::find (cfgMcTriggerPDGs->begin (), cfgMcTriggerPDGs->end (), mcParticle.pdgCode ()) != cfgMcTriggerPDGs->end ()) {
13681431 if ((mcParticle.mcDecay () != aod::cf2prongtrack::D0ToPiK) && (mcParticle.mcDecay () != aod::cf2prongtrack::D0barToKPiExclusive)) {
@@ -1372,9 +1435,8 @@ struct CorrelationTask {
13721435 continue ; // daughters not found
13731436 }
13741437 if constexpr (!reflectionSpec) {
1375- if (cfgPtCentDepMLpromptSel-> empty () || ( mcParticle. decay () & aod::cf2prongmcpart::Prompt) != 0 ) {
1438+ if (mcClassSel ( mcParticle))
13761439 same->getTrackHistEfficiency ()->Fill (CorrelationContainer::MC , mcParticle.eta (), mcParticle.pt (), 4 , multiplicity, mcCollision.posZ ());
1377- }
13781440 }
13791441 p2indexCache.push_back (mcParticle.globalIndex ());
13801442 }
@@ -1409,17 +1471,18 @@ struct CorrelationTask {
14091471 return false ;
14101472 }
14111473 const auto & mcParticle = mcParticles.iteratorAt (*m - mcParticles.begin ().globalIndex ());
1474+ // check MC class and return true if not the target class (wont be a fake)
1475+ if (!mcClassSel (mcParticle))
1476+ return true ; // a valid candidate but not the target class. Return true so we won't register a fake.
14121477 if constexpr (!reflectionSpec) {
1413- if (!cfgPtCentDepMLpromptSel->empty () && (mcParticle.decay () & aod::cf2prongmcpart::Prompt) == 0 ) {
1414- return true ; // a valid candidate but not a prompt
1415- }
14161478 same->getTrackHistEfficiency ()->Fill (CorrelationContainer::RecoPrimaries, mcParticle.eta (), mcParticle.pt (), 4 , multiplicity, mcCollision.posZ ());
14171479 } else {
1418- if ((mcParticle.mcDecay () == aod::cf2prongtrack::D0barToKPiExclusive && (p2track.decay () == aod::cf2prongtrack::D0barToKPiExclusive || p2track.decay () == aod::cf2prongtrack::D0barToKPi)) ||
1419- (mcParticle.mcDecay () == aod::cf2prongtrack::D0ToPiK && p2track.decay () == aod::cf2prongtrack::D0ToPiK)) {
1480+ if (!signalMC.contains (mcParticle.globalIndex ()) && ((mcParticle.mcDecay () == aod::cf2prongtrack::D0barToKPiExclusive && (p2track.decay () == aod::cf2prongtrack::D0barToKPiExclusive || p2track.decay () == aod::cf2prongtrack::D0barToKPi)) || (mcParticle.mcDecay () == aod::cf2prongtrack::D0ToPiK && p2track.decay () == aod::cf2prongtrack::D0ToPiK))) {
14201481 registry.fill (HIST (" invMassSignal" ), p2track.invMass (), p2track.pt (), multiplicity);
1421- } else { // one particle may be filled into both histograms through duplicates
1482+ signalMC.insert (mcParticle.globalIndex ());
1483+ } else if (!reflectionMC.contains (mcParticle.globalIndex ()) && !signalMC.contains (mcParticle.globalIndex ()) && ((mcParticle.mcDecay () == aod::cf2prongtrack::D0barToKPiExclusive && p2track.decay () == aod::cf2prongtrack::D0ToPiK) || (mcParticle.mcDecay () == aod::cf2prongtrack::D0ToPiK && (p2track.decay () == aod::cf2prongtrack::D0barToKPi || p2track.decay () == aod::cf2prongtrack::D0barToKPiExclusive)))) {
14221484 registry.fill (HIST (" invMassReflected" ), p2track.invMass (), p2track.pt (), multiplicity);
1485+ reflectionMC.insert (mcParticle.globalIndex ());
14231486 }
14241487 }
14251488 return true ;
0 commit comments