diff --git a/PWGHF/D2H/Tasks/taskD0.cxx b/PWGHF/D2H/Tasks/taskD0.cxx index 10d26c5e22f..2ee8b96410b 100644 --- a/PWGHF/D2H/Tasks/taskD0.cxx +++ b/PWGHF/D2H/Tasks/taskD0.cxx @@ -26,6 +26,7 @@ #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" #include "PWGHF/Utils/utilsEvSelHf.h" #include "PWGHF/Utils/utilsUpcHf.h" +#include "PWGLF/DataModel/mcCentrality.h" #include "PWGUD/Core/UPCHelpers.h" #include "Common/CCDB/ctpRateFetcher.h" @@ -58,6 +59,7 @@ #include #include #include +#include #include using namespace o2; @@ -82,6 +84,12 @@ enum CandTypeSel { }; } // namespace struct HfTaskD0 { + enum RecoEventStatus { + NoRecoCollision = 0, + NoSelectedRecoCollision, + SelectedRecoCollision + }; + Configurable selectionFlagD0{"selectionFlagD0", 1, "Selection Flag for D0"}; Configurable selectionFlagD0bar{"selectionFlagD0bar", 1, "Selection Flag for D0bar"}; Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen particle rapidity"}; @@ -92,6 +100,7 @@ struct HfTaskD0 { Configurable selectionPid{"selectionPid", 1, "Selection Flag for reco PID candidates"}; Configurable> binsPt{"binsPt", std::vector{hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits"}; Configurable centEstimator{"centEstimator", 0, "Centrality estimation (None: 0, FT0C: 2, FT0M: 3)"}; + Configurable fillEventStatus{"fillEventStatus", false, "Use generated centrality in hSparseAcc and append reco-event status (requires storeCentrality)"}; Configurable occEstimator{"occEstimator", 0, "Occupancy estimation (None: 0, ITS: 1, FT0C: 2)"}; Configurable storeCentrality{"storeCentrality", false, "Flag to store centrality information"}; Configurable storeOccupancyAndIR{"storeOccupancyAndIR", false, "Flag to store occupancy information and interaction rate"}; @@ -125,6 +134,7 @@ struct HfTaskD0 { using CollisionsCent = soa::Join; using CollisionsWithMcLabels = soa::Join; using CollisionsWithMcLabelsCent = soa::Join; + using McCollisionsWithCentrality = soa::Join; using TracksSelQuality = soa::Join; using TracksWPid = soa::Join; // using TracksWithExtra = o2::soa::Join; @@ -331,13 +341,22 @@ struct HfTaskD0 { std::vector axesAcc = {thnAxisGenPtD, thnAxisGenPtB, thnAxisY, thnAxisOrigin, thnAxisNumPvContr}; if (storeCentrality) { - axesAcc.push_back(thnAxisCent); + axesAcc.emplace_back(thnConfigAxisCent, fillEventStatus ? "Generated centrality (%)" : "Centrality"); } // interaction rate only store in Data and MC Reco. Level if (storeOccupancyAndIR) { axesAcc.push_back(thnAxisOccupancy); } + if (fillEventStatus) { + if (!storeCentrality) { + LOGP(fatal, "fillEventStatus requires storeCentrality=true."); + } + if (centEstimator != CentralityEstimator::FT0C && centEstimator != CentralityEstimator::FT0M) { + LOGP(fatal, "centEstimator must be FT0C (2) or FT0M (3)."); + } + axesAcc.emplace_back(3, -0.5, 2.5, "Reco event status (0: no reco, 1: none selected, 2: selected)"); + } registry.add("hSparseAcc", "Thn for generated D0 from charm and beauty", HistType::kTHnSparseD, axesAcc); registry.get(HIST("hSparseAcc"))->Sumw2(); } @@ -484,7 +503,11 @@ struct HfTaskD0 { registry.add("QAtracks/hDCAxy_GapC", "Gap C; DCA xy", {HistType::kTH1F, {{400, -2, 2.}}}); registry.add("QAtracks/hDCAz_GapC", "Gap C; DCA z", {HistType::kTH1F, {{400, -4, 4.}}}); - hfEvSel.addHistograms(registry); + if (fillEventStatus && (doprocessMcWithDCAFitterN || doprocessMcWithDCAFitterNCent || doprocessMcWithKFParticle || doprocessMcWithDCAFitterNMl || doprocessMcWithDCAFitterNMlCent || doprocessMcWithKFParticleMl)) { + hfEvSel.init(registry); + } else { + hfEvSel.addHistograms(registry); + } ccdb->setURL(ccdbUrl); ccdb->setCaching(true); @@ -970,7 +993,7 @@ struct HfTaskD0 { soa::Join const& mcParticles, TracksSelQuality const&, CollType const& collisions, - aod::McCollisions const&, + McCollisionsWithCentrality const&, BCsType const&) { // MC rec. @@ -1255,7 +1278,31 @@ struct HfTaskD0 { } } } - // MC gen. + // Classify reco associations once per collision, not once per generated particle. + // The task's hfEvSel.* configuration must match the candidate creator's cuts. + std::unordered_map recoEventStatus; + if (fillEventStatus) { + for (const auto& collision : collisions) { + if (!collision.has_mcCollision()) { + continue; + } + float unusedCentrality{-1.f}; + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, unusedCentrality, ccdb, registry); + bool selected = rejectionMask == 0; + if constexpr (std::is_same_v) { + if (centEstimator != CentralityEstimator::None) { + const auto recoCentrality = getCentralityColl(collision, centEstimator); + selected = selected && recoCentrality >= hfEvSel.centralityMin && recoCentrality <= hfEvSel.centralityMax; + } + } + auto& status = recoEventStatus[collision.mcCollisionId()]; + const int currentStatus = selected ? SelectedRecoCollision : NoSelectedRecoCollision; + status = std::max(status, currentStatus); + } + } + + // MC gen. Each particle is filled once, even for split or unreconstructed events. + // Upstream event rejection can clear flagMcMatchGen: this is not an unfiltered truth sample. for (const auto& particle : mcParticles) { if (std::abs(particle.flagMcMatchGen()) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { if (yCandGenMax >= 0. && std::abs(RecoDecay::y(particle.pVector(), o2::constants::physics::MassD0)) > yCandGenMax) { @@ -1267,6 +1314,8 @@ struct HfTaskD0 { registry.fill(HIST("hPtGen"), ptGen); registry.fill(HIST("hPtVsYGen"), ptGen, yGen); + int eventStatus = NoRecoCollision; + unsigned maxNumContrib = 0; float cent{-1.f}; float occ{-1.f}; @@ -1288,18 +1337,33 @@ struct HfTaskD0 { } } + if (fillEventStatus) { + const auto mcCollision = particle.template mcCollision_as(); + const auto eventEntry = recoEventStatus.find(mcCollision.globalIndex()); + eventStatus = eventEntry == recoEventStatus.end() ? NoRecoCollision : eventEntry->second; + // Replace the existing centrality coordinate, including zero-reco events. + cent = centEstimator == CentralityEstimator::FT0C ? mcCollision.centFT0C() : mcCollision.centFT0M(); + } + const auto fillGeneratedSparse = [&](auto... coordinates) { + if (fillEventStatus) { + registry.fill(HIST("hSparseAcc"), coordinates..., eventStatus); + } else { + registry.fill(HIST("hSparseAcc"), coordinates...); + } + }; + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { registry.fill(HIST("hPtGenPrompt"), ptGen); registry.fill(HIST("hYGenPrompt"), yGen); registry.fill(HIST("hPtVsYGenPrompt"), ptGen, yGen); if (storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 1, maxNumContrib, cent, occ); + fillGeneratedSparse(ptGen, ptGenB, yGen, 1, maxNumContrib, cent, occ); } else if (storeCentrality && !storeOccupancyAndIR) { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 1, maxNumContrib, cent); + fillGeneratedSparse(ptGen, ptGenB, yGen, 1, maxNumContrib, cent); } else if (!storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 1, maxNumContrib, occ); + fillGeneratedSparse(ptGen, ptGenB, yGen, 1, maxNumContrib, occ); } else { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 1, maxNumContrib); + fillGeneratedSparse(ptGen, ptGenB, yGen, 1, maxNumContrib); } } else { ptGenB = mcParticles.rawIteratorAt(particle.idxBhadMotherPart()).pt(); @@ -1307,13 +1371,13 @@ struct HfTaskD0 { registry.fill(HIST("hYGenNonPrompt"), yGen); registry.fill(HIST("hPtVsYGenNonPrompt"), ptGen, yGen); if (storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 2, maxNumContrib, cent, occ); + fillGeneratedSparse(ptGen, ptGenB, yGen, 2, maxNumContrib, cent, occ); } else if (storeCentrality && !storeOccupancyAndIR) { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 2, maxNumContrib, cent); + fillGeneratedSparse(ptGen, ptGenB, yGen, 2, maxNumContrib, cent); } else if (!storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 2, maxNumContrib, occ); + fillGeneratedSparse(ptGen, ptGenB, yGen, 2, maxNumContrib, occ); } else { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 2, maxNumContrib); + fillGeneratedSparse(ptGen, ptGenB, yGen, 2, maxNumContrib); } } registry.fill(HIST("hEtaGen"), particle.eta()); @@ -1325,7 +1389,7 @@ struct HfTaskD0 { soa::Join const& mcParticles, TracksSelQuality const& tracks, CollisionsWithMcLabels const& collisions, - aod::McCollisions const& mcCollisions, + McCollisionsWithCentrality const& mcCollisions, aod::BcFullInfos const& bcs) { processMc(selectedD0CandidatesMc, mcParticles, tracks, collisions, mcCollisions, bcs); @@ -1336,7 +1400,7 @@ struct HfTaskD0 { soa::Join const& mcParticles, TracksSelQuality const& tracks, CollisionsWithMcLabelsCent const& collisions, - aod::McCollisions const& mcCollisions, + McCollisionsWithCentrality const& mcCollisions, aod::BcFullInfos const& bcs) { processMc(selectedD0CandidatesMc, mcParticles, tracks, collisions, mcCollisions, bcs); @@ -1347,7 +1411,7 @@ struct HfTaskD0 { soa::Join const& mcParticles, TracksSelQuality const& tracks, CollisionsWithMcLabels const& collisions, - aod::McCollisions const& mcCollisions, + McCollisionsWithCentrality const& mcCollisions, aod::BcFullInfos const& bcs) { processMc(selectedD0CandidatesMcKF, mcParticles, tracks, collisions, mcCollisions, bcs); @@ -1359,7 +1423,7 @@ struct HfTaskD0 { soa::Join const& mcParticles, TracksSelQuality const& tracks, CollisionsWithMcLabels const& collisions, - aod::McCollisions const& mcCollisions, + McCollisionsWithCentrality const& mcCollisions, aod::BcFullInfos const& bcs) { processMc(selectedD0CandidatesMlMc, mcParticles, tracks, collisions, mcCollisions, bcs); @@ -1370,7 +1434,7 @@ struct HfTaskD0 { soa::Join const& mcParticles, TracksSelQuality const& tracks, CollisionsWithMcLabelsCent const& collisions, - aod::McCollisions const& mcCollisions, + McCollisionsWithCentrality const& mcCollisions, aod::BcFullInfos const& bcs) { processMc(selectedD0CandidatesMlMc, mcParticles, tracks, collisions, mcCollisions, bcs); @@ -1381,7 +1445,7 @@ struct HfTaskD0 { soa::Join const& mcParticles, TracksSelQuality const& tracks, CollisionsWithMcLabels const& collisions, - aod::McCollisions const& mcCollisions, + McCollisionsWithCentrality const& mcCollisions, aod::BcFullInfos const& bcs) { processMc(selectedD0CandidatesMlMcKF, mcParticles, tracks, collisions, mcCollisions, bcs);