Skip to content

Commit 2825a93

Browse files
authored
[PWGCF] correlations.cxx: prompt + non-prompt separation (#18432)
1 parent 54da334 commit 2825a93

1 file changed

Lines changed: 74 additions & 11 deletions

File tree

‎PWGCF/Tasks/correlations.cxx‎

Lines changed: 74 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
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

Comments
 (0)