@@ -2909,6 +2909,14 @@ struct HStrangeCorrelation {
29092909 // AnyTrack any truth PairLossTriggerAnyCollision
29102910 // AnyTrackK0 truth any PairLossV0AnyCollision
29112911 // AnyTrackBoth any any both stages at once
2912+ // Final final final fully selected, both in one collision
2913+ // "final" means the object has a reconstructed counterpart that survives
2914+ // every selection the reconstructed correlation applies, and for the pair
2915+ // both counterparts must live in the same reconstructed collision. Final/
2916+ // is therefore the direct truth-coordinate counterpart of the ordinary
2917+ // reconstructed correlation: Rec/Final isolates what is left once pair
2918+ // loss is divided out -- duplicate reconstructed objects, fakes and bin
2919+ // migration.
29122920 // Every folder has the same three objects -- sameEvent/K0Short, hTrigger,
29132921 // hK0Short -- and each of them is filled at the level its own folder
29142922 // prescribes, so a folder can be normalised without looking at any other.
@@ -2928,6 +2936,9 @@ struct HStrangeCorrelation {
29282936 histos.add (" ClosureTest/PairLossK0/AnyTrackK0/hK0Short" , " truth K0s with a V0-candidate match in any associated collision;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}" , kTH3F , {axesConfigurations.axisPtQA , axesConfigurations.axisEta , axesConfigurations.axisPhi });
29292937 histos.add (" ClosureTest/PairLossK0/AnyTrackBoth/hTrigger" , " truth triggers with a reconstructed-track match in any associated collision;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}" , kTH3F , {axesConfigurations.axisPtQA , axesConfigurations.axisEta , axesConfigurations.axisPhi });
29302938 histos.add (" ClosureTest/PairLossK0/AnyTrackBoth/hK0Short" , " truth K0s with a V0-candidate match in any associated collision;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}" , kTH3F , {axesConfigurations.axisPtQA , axesConfigurations.axisEta , axesConfigurations.axisPhi });
2939+ histos.add (" ClosureTest/PairLossK0/Final/sameEvent/K0Short" , " truth h-K0 pairs whose trigger and K0 both have a fully selected reconstructed counterpart in the same reconstructed collision" , kTHnF , {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim});
2940+ histos.add (" ClosureTest/PairLossK0/Final/hTrigger" , " truth triggers with a fully selected reconstructed counterpart;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}" , kTH3F , {axesConfigurations.axisPtQA , axesConfigurations.axisEta , axesConfigurations.axisPhi });
2941+ histos.add (" ClosureTest/PairLossK0/Final/hK0Short" , " truth K0s with a fully selected reconstructed counterpart;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}" , kTH3F , {axesConfigurations.axisPtQA , axesConfigurations.axisEta , axesConfigurations.axisPhi });
29312942 }
29322943 for (int i = 0 ; i < AssocParticleTypes; i++) {
29332944 if (TESTBIT (doCorrelation, i)) {
@@ -5104,7 +5115,9 @@ struct HStrangeCorrelation {
51045115 aod::McParticles const & mcParticles,
51055116 aod::V0MCCores const & v0MCCores,
51065117 V0DatasWithoutTrackX const & v0Candidates,
5107- TracksCompleteMC const & tracks)
5118+ TracksCompleteMC const & tracks,
5119+ aod::TriggerTracks const & triggerTracks,
5120+ aod::AssocV0s const & associatedV0s)
51085121 {
51095122
51105123 // Reproduce the first processPairLossK0MC stages without changing the
@@ -5119,6 +5132,11 @@ struct HStrangeCorrelation {
51195132 // candidate pointing back to it, in any associated collision
51205133 // (stage PairLossV0AnyCollision)
51215134 // AnyTrackBoth both requirements at the same time
5135+ // Final the strictest stage: both the trigger and the K0 must have a
5136+ // *fully selected* reconstructed counterpart -- the very
5137+ // TriggerTracks and AssocV0s entries, passing the very final
5138+ // selections, that the reconstructed correlation is built from --
5139+ // and both in one and the same reconstructed collision
51225140 auto fillPairLossK0TruthAndAnyTrack = [&]() {
51235141 if (!pairLossK0Configurations.doClosureTestStages ) {
51245142 return ;
@@ -5161,6 +5179,125 @@ struct HStrangeCorrelation {
51615179 }
51625180 }
51635181
5182+ // Final-stage bookkeeping. A truth object counts here only if it has at least
5183+ // one fully selected reconstructed counterpart, where "fully selected" means
5184+ // the exact same conditions the reconstructed correlation imposes in
5185+ // fillCorrelationsV0(): the object is in the TriggerTracks / AssocV0s table
5186+ // to begin with, and it then passes isValidTrigger() resp. the whole V0
5187+ // selection chain including the dE/dx compatibility bit and the peak mass
5188+ // window. The selections are read from the filter tables instead of being
5189+ // recomputed from raw tracks so that this stage cannot drift away from the
5190+ // reconstructed analysis it exists to be compared against.
5191+ //
5192+ // The sets are built per reconstructed collision and a pair is required to be
5193+ // final within one and the same collision: the reconstructed same-event
5194+ // correlation only ever pairs a trigger with a V0 sitting in the same vertex,
5195+ // so a pair split across two reconstructed vertices of one MC collision must
5196+ // not count as final here either.
5197+ //
5198+ // N.B.: the reconstructed autocorrelation rejection (trigger track identical
5199+ // to a V0 daughter track) is deliberately not replicated. It is a no-op as
5200+ // soon as the trigger is required to be a physical primary, because
5201+ // mcTrue(IndexK0) forces the daughters to be genuine -- hence secondary --
5202+ // K0 decay products.
5203+ std::vector<std::pair<std::unordered_set<int64_t >, std::unordered_set<int64_t >>> pairLossFinalPerCollision;
5204+ for (auto const & collision : recCollisions) {
5205+ std::unordered_set<int64_t > finalTriggerMcIds;
5206+ std::unordered_set<int64_t > finalK0McIds;
5207+
5208+ const auto finalTriggerSlice = triggerTracks.sliceBy (collisionSliceTracks, collision.globalIndex ());
5209+ for (auto const & triggerEntry : finalTriggerSlice) {
5210+ auto track = triggerEntry.track_as <TracksCompleteMC>();
5211+ if (!track.has_mcParticle ()) {
5212+ continue ;
5213+ }
5214+ if (!isValidTrigger (track, triggerEntry.isLeading ())) {
5215+ continue ;
5216+ }
5217+ if (trackSelection.checkForITSTPCMissmatchMC && bitcheck (triggerEntry.mcMask (), 13 )) {
5218+ continue ;
5219+ }
5220+ if (masterConfigurations.doTriggPhysicalPrimary && !triggerEntry.mcPhysicalPrimary ()) {
5221+ continue ;
5222+ }
5223+ finalTriggerMcIds.insert (track.mcParticleId ());
5224+ }
5225+
5226+ const auto finalV0Slice = associatedV0s.sliceBy (collisionSliceV0s, collision.globalIndex ());
5227+ for (auto const & assocEntry : finalV0Slice) {
5228+ auto v0 = assocEntry.v0Core_as <V0DatasWithoutTrackX>();
5229+ const auto v0MC = v0MCCores.iteratorAt (v0.globalIndex ());
5230+ if (v0MC.particleIdMC () < 0 || v0MC.pdgCode () != PDG_t::kK0Short || !assocEntry.mcTrue (IndexK0)) {
5231+ continue ;
5232+ }
5233+ auto positiveTrack = v0.posTrack_as <TracksCompleteMC>();
5234+ auto negativeTrack = v0.negTrack_as <TracksCompleteMC>();
5235+ bool passesFinalSelection = true ;
5236+ if (masterConfigurations.doPPAnalysis ) {
5237+ passesFinalSelection = v0.v0radius () >= v0Selection.v0RadiusMin && v0.v0radius () <= v0Selection.v0RadiusMax &&
5238+ std::abs (v0.dcapostopv ()) >= v0Selection.dcapostopv && std::abs (v0.dcanegtopv ()) >= v0Selection.dcanegtopv &&
5239+ v0.v0cosPA () >= v0Selection.v0cospa && v0.dcaV0daughters () <= v0Selection.dcaV0dau ;
5240+ } else {
5241+ const float dcaCut = v0Selection.dcaDaugToPVForK0s == 0 .0f ? v0Selection.dcaMesonToPV : v0Selection.dcaDaugToPVForK0s ;
5242+ const bool passesLifetime = v0.distovertotmom (collision.posX (), collision.posY (), collision.posZ ()) *
5243+ o2::constants::physics::MassK0Short <
5244+ v0Selection.lifetimecutK0S ;
5245+ const bool passesDaughterDcaAndArmenteros = std::abs (v0.dcapostopv ()) > dcaCut && std::abs (v0.dcanegtopv ()) > dcaCut &&
5246+ v0.qtarm () * v0Selection.armPodCut > std::abs (v0.alpha ());
5247+ passesFinalSelection = v0SelectedPbPb (v0) && passesLifetime && passesDaughterDcaAndArmenteros;
5248+ }
5249+ passesFinalSelection = passesFinalSelection && positiveTrack.tpcNClsCrossedRows () >= trackSelection.minTPCNCrossedRowsAssociated &&
5250+ negativeTrack.tpcNClsCrossedRows () >= trackSelection.minTPCNCrossedRowsAssociated ;
5251+ if (trackSelection.checksRequireTPCChi2 ) {
5252+ passesFinalSelection = passesFinalSelection && positiveTrack.tpcChi2NCl () >= trackSelection.minTPCChi2PerClusterAssociated &&
5253+ negativeTrack.tpcChi2NCl () >= trackSelection.minTPCChi2PerClusterAssociated ;
5254+ }
5255+ if (trackSelection.requireClusterInITS ) {
5256+ passesFinalSelection = passesFinalSelection && positiveTrack.itsNCls () >= trackSelection.minITSClustersForDaughterTracks &&
5257+ negativeTrack.itsNCls () >= trackSelection.minITSClustersForDaughterTracks ;
5258+ }
5259+ passesFinalSelection = passesFinalSelection && assocEntry.compatible (IndexK0, trackSelection.dEdxCompatibility ) &&
5260+ (!doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary ()) &&
5261+ assocEntry.invMassNSigma (IndexK0) > -massWindowConfigurations.maxPeakNSigma &&
5262+ assocEntry.invMassNSigma (IndexK0) < massWindowConfigurations.maxPeakNSigma &&
5263+ v0.pt () >= axisRanges[2 ][0 ] && v0.pt () <= axisRanges[2 ][1 ];
5264+ if (!passesFinalSelection) {
5265+ continue ;
5266+ }
5267+ finalK0McIds.insert (v0MC.particleIdMC ());
5268+ }
5269+
5270+ pairLossFinalPerCollision.emplace_back (std::move (finalTriggerMcIds), std::move (finalK0McIds));
5271+ }
5272+
5273+ // Object-level membership, used only for the single-particle spectra: at
5274+ // least one collision in which the object is fully selected. The pair
5275+ // histogram uses pairLossHasFinalPair() instead, which is stricter.
5276+ auto pairLossHasFinalTrigger = [&](int64_t mcId) {
5277+ for (auto const & perCollision : pairLossFinalPerCollision) {
5278+ if (perCollision.first .count (mcId) > 0 ) {
5279+ return true ;
5280+ }
5281+ }
5282+ return false ;
5283+ };
5284+ auto pairLossHasFinalK0 = [&](int64_t mcId) {
5285+ for (auto const & perCollision : pairLossFinalPerCollision) {
5286+ if (perCollision.second .count (mcId) > 0 ) {
5287+ return true ;
5288+ }
5289+ }
5290+ return false ;
5291+ };
5292+ auto pairLossHasFinalPair = [&](int64_t triggerMcId, int64_t k0McId) {
5293+ for (auto const & perCollision : pairLossFinalPerCollision) {
5294+ if (perCollision.first .count (triggerMcId) > 0 && perCollision.second .count (k0McId) > 0 ) {
5295+ return true ;
5296+ }
5297+ }
5298+ return false ;
5299+ };
5300+
51645301 for (auto const & collision : recCollisions) {
51655302 if (static_cast <int64_t >(collision.globalIndex ()) != pairLossBestCollisionId) {
51665303 continue ;
@@ -5234,15 +5371,19 @@ struct HStrangeCorrelation {
52345371
52355372 // One entry per object per folder, at the level that folder prescribes:
52365373 // the trigger is at truth level in Truth/ and AnyTrackK0/, at any level in
5237- // AnyTrack/ and AnyTrackBoth/; the K0 is at truth level in Truth/ and
5238- // AnyTrack/, at any level in AnyTrackK0/ and AnyTrackBoth/.
5374+ // AnyTrack/ and AnyTrackBoth/, at fully-selected level in Final/; the K0 is
5375+ // at truth level in Truth/ and AnyTrack/, at any level in AnyTrackK0/ and
5376+ // AnyTrackBoth/, at fully-selected level in Final/.
52395377 for (auto const & truthTrigger : pairLossTruthTriggers) {
52405378 histos.fill (HIST (" ClosureTest/PairLossK0/Truth/hTrigger" ), truthTrigger.pt , truthTrigger.eta , truthTrigger.phi );
52415379 histos.fill (HIST (" ClosureTest/PairLossK0/AnyTrackK0/hTrigger" ), truthTrigger.pt , truthTrigger.eta , truthTrigger.phi );
52425380 if (pairLossAnyTrackMcParticleIds.find (truthTrigger.globalIndex ) != pairLossAnyTrackMcParticleIds.end ()) {
52435381 histos.fill (HIST (" ClosureTest/PairLossK0/AnyTrack/hTrigger" ), truthTrigger.pt , truthTrigger.eta , truthTrigger.phi );
52445382 histos.fill (HIST (" ClosureTest/PairLossK0/AnyTrackBoth/hTrigger" ), truthTrigger.pt , truthTrigger.eta , truthTrigger.phi );
52455383 }
5384+ if (pairLossHasFinalTrigger (truthTrigger.globalIndex )) {
5385+ histos.fill (HIST (" ClosureTest/PairLossK0/Final/hTrigger" ), truthTrigger.pt , truthTrigger.eta , truthTrigger.phi );
5386+ }
52465387 }
52475388 for (auto const & truthK0 : pairLossTruthK0s) {
52485389 histos.fill (HIST (" ClosureTest/PairLossK0/Truth/hK0Short" ), truthK0.pt , truthK0.eta , truthK0.phi );
@@ -5251,6 +5392,9 @@ struct HStrangeCorrelation {
52515392 histos.fill (HIST (" ClosureTest/PairLossK0/AnyTrackK0/hK0Short" ), truthK0.pt , truthK0.eta , truthK0.phi );
52525393 histos.fill (HIST (" ClosureTest/PairLossK0/AnyTrackBoth/hK0Short" ), truthK0.pt , truthK0.eta , truthK0.phi );
52535394 }
5395+ if (pairLossHasFinalK0 (truthK0.globalIndex )) {
5396+ histos.fill (HIST (" ClosureTest/PairLossK0/Final/hK0Short" ), truthK0.pt , truthK0.eta , truthK0.phi );
5397+ }
52545398 }
52555399
52565400 for (auto const & truthTrigger : pairLossTruthTriggers) {
@@ -5281,6 +5425,9 @@ struct HStrangeCorrelation {
52815425 if (triggerHasAnyTrack && k0HasAnyV0) {
52825426 histos.fill (HIST (" ClosureTest/PairLossK0/AnyTrackBoth/sameEvent/K0Short" ), truthDeltaPhi, truthDeltaEta, truthK0.pt , truthTrigger.pt , pairLossBestCollisionVtxZ, pairLossBestCollisionMultiplicity);
52835427 }
5428+ if (pairLossHasFinalPair (truthTrigger.globalIndex , truthK0.globalIndex )) {
5429+ histos.fill (HIST (" ClosureTest/PairLossK0/Final/sameEvent/K0Short" ), truthDeltaPhi, truthDeltaEta, truthK0.pt , truthTrigger.pt , pairLossBestCollisionVtxZ, pairLossBestCollisionMultiplicity);
5430+ }
52845431 }
52855432 }
52865433 return ;
0 commit comments