2626#include " PWGHF/DataModel/TrackIndexSkimmingTables.h"
2727#include " PWGHF/Utils/utilsEvSelHf.h"
2828#include " PWGHF/Utils/utilsUpcHf.h"
29+ #include " PWGLF/DataModel/mcCentrality.h"
2930#include " PWGUD/Core/UPCHelpers.h"
3031
3132#include " Common/CCDB/ctpRateFetcher.h"
5859#include < cstdint>
5960#include < numeric>
6061#include < string>
62+ #include < unordered_map>
6163#include < vector>
6264
6365using namespace o2 ;
@@ -82,6 +84,12 @@ enum CandTypeSel {
8284};
8385} // namespace
8486struct HfTaskD0 {
87+ enum RecoEventStatus {
88+ NoRecoCollision = 0 ,
89+ NoSelectedRecoCollision,
90+ SelectedRecoCollision
91+ };
92+
8593 Configurable<int > selectionFlagD0{" selectionFlagD0" , 1 , " Selection Flag for D0" };
8694 Configurable<int > selectionFlagD0bar{" selectionFlagD0bar" , 1 , " Selection Flag for D0bar" };
8795 Configurable<double > yCandGenMax{" yCandGenMax" , 0.5 , " max. gen particle rapidity" };
@@ -92,6 +100,7 @@ struct HfTaskD0 {
92100 Configurable<int > selectionPid{" selectionPid" , 1 , " Selection Flag for reco PID candidates" };
93101 Configurable<std::vector<double >> binsPt{" binsPt" , std::vector<double >{hf_cuts_d0_to_pi_k::vecBinsPt}, " pT bin limits" };
94102 Configurable<int > centEstimator{" centEstimator" , 0 , " Centrality estimation (None: 0, FT0C: 2, FT0M: 3)" };
103+ Configurable<bool > fillEventStatus{" fillEventStatus" , false , " Use generated centrality in hSparseAcc and append reco-event status (requires storeCentrality)" };
95104 Configurable<int > occEstimator{" occEstimator" , 0 , " Occupancy estimation (None: 0, ITS: 1, FT0C: 2)" };
96105 Configurable<bool > storeCentrality{" storeCentrality" , false , " Flag to store centrality information" };
97106 Configurable<bool > storeOccupancyAndIR{" storeOccupancyAndIR" , false , " Flag to store occupancy information and interaction rate" };
@@ -125,6 +134,7 @@ struct HfTaskD0 {
125134 using CollisionsCent = soa::Join<aod::Collisions, aod::EvSels, aod::PVMults, aod::CentFT0Ms, aod::CentFT0Cs>;
126135 using CollisionsWithMcLabels = soa::Join<aod::Collisions, aod::McCollisionLabels, aod::EvSels, aod::PVMults>;
127136 using CollisionsWithMcLabelsCent = soa::Join<aod::Collisions, aod::McCollisionLabels, aod::EvSels, aod::PVMults, aod::CentFT0Ms, aod::CentFT0Cs>;
137+ using McCollisionsWithCentrality = soa::Join<aod::McCollisions, aod::McCentFT0Cs, aod::McCentFT0Ms>;
128138 using TracksSelQuality = soa::Join<aod::TracksExtra, aod::TracksWMc>;
129139 using TracksWPid = soa::Join<o2::aod::FullTracks, aod::TracksDCA, o2::aod::TrackSelection, aod::TracksPidPi, aod::PidTpcTofFullPi, aod::TracksPidKa, aod::PidTpcTofFullKa, aod::TracksPidPr, aod::PidTpcTofFullPr>;
130140 // using TracksWithExtra = o2::soa::Join<o2::aod::FullTracks, o2::aod::TrackSelection>;
@@ -331,13 +341,22 @@ struct HfTaskD0 {
331341 std::vector<AxisSpec> axesAcc = {thnAxisGenPtD, thnAxisGenPtB, thnAxisY, thnAxisOrigin, thnAxisNumPvContr};
332342
333343 if (storeCentrality) {
334- axesAcc.push_back (thnAxisCent );
344+ axesAcc.emplace_back (thnConfigAxisCent, fillEventStatus ? " Generated centrality (%) " : " Centrality " );
335345 }
336346 // interaction rate only store in Data and MC Reco. Level
337347 if (storeOccupancyAndIR) {
338348 axesAcc.push_back (thnAxisOccupancy);
339349 }
340350
351+ if (fillEventStatus) {
352+ if (!storeCentrality) {
353+ LOGP (fatal, " fillEventStatus requires storeCentrality=true." );
354+ }
355+ if (centEstimator != CentralityEstimator::FT0C && centEstimator != CentralityEstimator::FT0M ) {
356+ LOGP (fatal, " centEstimator must be FT0C (2) or FT0M (3)." );
357+ }
358+ axesAcc.emplace_back (3 , -0.5 , 2.5 , " Reco event status (0: no reco, 1: none selected, 2: selected)" );
359+ }
341360 registry.add (" hSparseAcc" , " Thn for generated D0 from charm and beauty" , HistType::kTHnSparseD , axesAcc);
342361 registry.get <THnSparse>(HIST (" hSparseAcc" ))->Sumw2 ();
343362 }
@@ -484,7 +503,11 @@ struct HfTaskD0 {
484503 registry.add (" QAtracks/hDCAxy_GapC" , " Gap C; DCA xy" , {HistType::kTH1F , {{400 , -2 , 2 .}}});
485504 registry.add (" QAtracks/hDCAz_GapC" , " Gap C; DCA z" , {HistType::kTH1F , {{400 , -4 , 4 .}}});
486505
487- hfEvSel.addHistograms (registry);
506+ if (fillEventStatus && (doprocessMcWithDCAFitterN || doprocessMcWithDCAFitterNCent || doprocessMcWithKFParticle || doprocessMcWithDCAFitterNMl || doprocessMcWithDCAFitterNMlCent || doprocessMcWithKFParticleMl)) {
507+ hfEvSel.init (registry);
508+ } else {
509+ hfEvSel.addHistograms (registry);
510+ }
488511
489512 ccdb->setURL (ccdbUrl);
490513 ccdb->setCaching (true );
@@ -970,7 +993,7 @@ struct HfTaskD0 {
970993 soa::Join<aod::McParticles, aod::HfCand2ProngMcGen> const & mcParticles,
971994 TracksSelQuality const &,
972995 CollType const & collisions,
973- aod::McCollisions const &,
996+ McCollisionsWithCentrality const &,
974997 BCsType const &)
975998 {
976999 // MC rec.
@@ -1255,7 +1278,31 @@ struct HfTaskD0 {
12551278 }
12561279 }
12571280 }
1258- // MC gen.
1281+ // Classify reco associations once per collision, not once per generated particle.
1282+ // The task's hfEvSel.* configuration must match the candidate creator's cuts.
1283+ std::unordered_map<int64_t , int > recoEventStatus;
1284+ if (fillEventStatus) {
1285+ for (const auto & collision : collisions) {
1286+ if (!collision.has_mcCollision ()) {
1287+ continue ;
1288+ }
1289+ float unusedCentrality{-1 .f };
1290+ const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask <true , CentralityEstimator::None, BCsType>(collision, unusedCentrality, ccdb, registry);
1291+ bool selected = rejectionMask == 0 ;
1292+ if constexpr (std::is_same_v<CollType, CollisionsWithMcLabelsCent>) {
1293+ if (centEstimator != CentralityEstimator::None) {
1294+ const auto recoCentrality = getCentralityColl (collision, centEstimator);
1295+ selected = selected && recoCentrality >= hfEvSel.centralityMin && recoCentrality <= hfEvSel.centralityMax ;
1296+ }
1297+ }
1298+ auto & status = recoEventStatus[collision.mcCollisionId ()];
1299+ const int currentStatus = selected ? SelectedRecoCollision : NoSelectedRecoCollision;
1300+ status = std::max (status, currentStatus);
1301+ }
1302+ }
1303+
1304+ // MC gen. Each particle is filled once, even for split or unreconstructed events.
1305+ // Upstream event rejection can clear flagMcMatchGen: this is not an unfiltered truth sample.
12591306 for (const auto & particle : mcParticles) {
12601307 if (std::abs (particle.flagMcMatchGen ()) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) {
12611308 if (yCandGenMax >= 0 . && std::abs (RecoDecay::y (particle.pVector (), o2::constants::physics::MassD0)) > yCandGenMax) {
@@ -1267,6 +1314,8 @@ struct HfTaskD0 {
12671314 registry.fill (HIST (" hPtGen" ), ptGen);
12681315 registry.fill (HIST (" hPtVsYGen" ), ptGen, yGen);
12691316
1317+ int eventStatus = NoRecoCollision;
1318+
12701319 unsigned maxNumContrib = 0 ;
12711320 float cent{-1 .f };
12721321 float occ{-1 .f };
@@ -1288,32 +1337,47 @@ struct HfTaskD0 {
12881337 }
12891338 }
12901339
1340+ if (fillEventStatus) {
1341+ const auto mcCollision = particle.template mcCollision_as <McCollisionsWithCentrality>();
1342+ const auto eventEntry = recoEventStatus.find (mcCollision.globalIndex ());
1343+ eventStatus = eventEntry == recoEventStatus.end () ? NoRecoCollision : eventEntry->second ;
1344+ // Replace the existing centrality coordinate, including zero-reco events.
1345+ cent = centEstimator == CentralityEstimator::FT0C ? mcCollision.centFT0C () : mcCollision.centFT0M ();
1346+ }
1347+ const auto fillGeneratedSparse = [&](auto ... coordinates) {
1348+ if (fillEventStatus) {
1349+ registry.fill (HIST (" hSparseAcc" ), coordinates..., eventStatus);
1350+ } else {
1351+ registry.fill (HIST (" hSparseAcc" ), coordinates...);
1352+ }
1353+ };
1354+
12911355 if (particle.originMcGen () == RecoDecay::OriginType::Prompt) {
12921356 registry.fill (HIST (" hPtGenPrompt" ), ptGen);
12931357 registry.fill (HIST (" hYGenPrompt" ), yGen);
12941358 registry.fill (HIST (" hPtVsYGenPrompt" ), ptGen, yGen);
12951359 if (storeCentrality && storeOccupancyAndIR) {
1296- registry. fill ( HIST ( " hSparseAcc " ), ptGen, ptGenB, yGen, 1 , maxNumContrib, cent, occ);
1360+ fillGeneratedSparse ( ptGen, ptGenB, yGen, 1 , maxNumContrib, cent, occ);
12971361 } else if (storeCentrality && !storeOccupancyAndIR) {
1298- registry. fill ( HIST ( " hSparseAcc " ), ptGen, ptGenB, yGen, 1 , maxNumContrib, cent);
1362+ fillGeneratedSparse ( ptGen, ptGenB, yGen, 1 , maxNumContrib, cent);
12991363 } else if (!storeCentrality && storeOccupancyAndIR) {
1300- registry. fill ( HIST ( " hSparseAcc " ), ptGen, ptGenB, yGen, 1 , maxNumContrib, occ);
1364+ fillGeneratedSparse ( ptGen, ptGenB, yGen, 1 , maxNumContrib, occ);
13011365 } else {
1302- registry. fill ( HIST ( " hSparseAcc " ), ptGen, ptGenB, yGen, 1 , maxNumContrib);
1366+ fillGeneratedSparse ( ptGen, ptGenB, yGen, 1 , maxNumContrib);
13031367 }
13041368 } else {
13051369 ptGenB = mcParticles.rawIteratorAt (particle.idxBhadMotherPart ()).pt ();
13061370 registry.fill (HIST (" hPtGenNonPrompt" ), ptGen);
13071371 registry.fill (HIST (" hYGenNonPrompt" ), yGen);
13081372 registry.fill (HIST (" hPtVsYGenNonPrompt" ), ptGen, yGen);
13091373 if (storeCentrality && storeOccupancyAndIR) {
1310- registry. fill ( HIST ( " hSparseAcc " ), ptGen, ptGenB, yGen, 2 , maxNumContrib, cent, occ);
1374+ fillGeneratedSparse ( ptGen, ptGenB, yGen, 2 , maxNumContrib, cent, occ);
13111375 } else if (storeCentrality && !storeOccupancyAndIR) {
1312- registry. fill ( HIST ( " hSparseAcc " ), ptGen, ptGenB, yGen, 2 , maxNumContrib, cent);
1376+ fillGeneratedSparse ( ptGen, ptGenB, yGen, 2 , maxNumContrib, cent);
13131377 } else if (!storeCentrality && storeOccupancyAndIR) {
1314- registry. fill ( HIST ( " hSparseAcc " ), ptGen, ptGenB, yGen, 2 , maxNumContrib, occ);
1378+ fillGeneratedSparse ( ptGen, ptGenB, yGen, 2 , maxNumContrib, occ);
13151379 } else {
1316- registry. fill ( HIST ( " hSparseAcc " ), ptGen, ptGenB, yGen, 2 , maxNumContrib);
1380+ fillGeneratedSparse ( ptGen, ptGenB, yGen, 2 , maxNumContrib);
13171381 }
13181382 }
13191383 registry.fill (HIST (" hEtaGen" ), particle.eta ());
@@ -1325,7 +1389,7 @@ struct HfTaskD0 {
13251389 soa::Join<aod::McParticles, aod::HfCand2ProngMcGen> const & mcParticles,
13261390 TracksSelQuality const & tracks,
13271391 CollisionsWithMcLabels const & collisions,
1328- aod::McCollisions const & mcCollisions,
1392+ McCollisionsWithCentrality const & mcCollisions,
13291393 aod::BcFullInfos const & bcs)
13301394 {
13311395 processMc<aod::hf_cand::VertexerType::DCAFitter, false >(selectedD0CandidatesMc, mcParticles, tracks, collisions, mcCollisions, bcs);
@@ -1336,7 +1400,7 @@ struct HfTaskD0 {
13361400 soa::Join<aod::McParticles, aod::HfCand2ProngMcGen> const & mcParticles,
13371401 TracksSelQuality const & tracks,
13381402 CollisionsWithMcLabelsCent const & collisions,
1339- aod::McCollisions const & mcCollisions,
1403+ McCollisionsWithCentrality const & mcCollisions,
13401404 aod::BcFullInfos const & bcs)
13411405 {
13421406 processMc<aod::hf_cand::VertexerType::DCAFitter, false >(selectedD0CandidatesMc, mcParticles, tracks, collisions, mcCollisions, bcs);
@@ -1347,7 +1411,7 @@ struct HfTaskD0 {
13471411 soa::Join<aod::McParticles, aod::HfCand2ProngMcGen> const & mcParticles,
13481412 TracksSelQuality const & tracks,
13491413 CollisionsWithMcLabels const & collisions,
1350- aod::McCollisions const & mcCollisions,
1414+ McCollisionsWithCentrality const & mcCollisions,
13511415 aod::BcFullInfos const & bcs)
13521416 {
13531417 processMc<aod::hf_cand::VertexerType::KfParticle, false >(selectedD0CandidatesMcKF, mcParticles, tracks, collisions, mcCollisions, bcs);
@@ -1359,7 +1423,7 @@ struct HfTaskD0 {
13591423 soa::Join<aod::McParticles, aod::HfCand2ProngMcGen> const & mcParticles,
13601424 TracksSelQuality const & tracks,
13611425 CollisionsWithMcLabels const & collisions,
1362- aod::McCollisions const & mcCollisions,
1426+ McCollisionsWithCentrality const & mcCollisions,
13631427 aod::BcFullInfos const & bcs)
13641428 {
13651429 processMc<aod::hf_cand::VertexerType::DCAFitter, true >(selectedD0CandidatesMlMc, mcParticles, tracks, collisions, mcCollisions, bcs);
@@ -1370,7 +1434,7 @@ struct HfTaskD0 {
13701434 soa::Join<aod::McParticles, aod::HfCand2ProngMcGen> const & mcParticles,
13711435 TracksSelQuality const & tracks,
13721436 CollisionsWithMcLabelsCent const & collisions,
1373- aod::McCollisions const & mcCollisions,
1437+ McCollisionsWithCentrality const & mcCollisions,
13741438 aod::BcFullInfos const & bcs)
13751439 {
13761440 processMc<aod::hf_cand::VertexerType::DCAFitter, true >(selectedD0CandidatesMlMc, mcParticles, tracks, collisions, mcCollisions, bcs);
@@ -1381,7 +1445,7 @@ struct HfTaskD0 {
13811445 soa::Join<aod::McParticles, aod::HfCand2ProngMcGen> const & mcParticles,
13821446 TracksSelQuality const & tracks,
13831447 CollisionsWithMcLabels const & collisions,
1384- aod::McCollisions const & mcCollisions,
1448+ McCollisionsWithCentrality const & mcCollisions,
13851449 aod::BcFullInfos const & bcs)
13861450 {
13871451 processMc<aod::hf_cand::VertexerType::KfParticle, true >(selectedD0CandidatesMlMcKF, mcParticles, tracks, collisions, mcCollisions, bcs);
0 commit comments