diff --git a/PWGLF/DataModel/LFNonPromptCascadeTables.h b/PWGLF/DataModel/LFNonPromptCascadeTables.h index d9d2c30ac97..0ea616d8853 100644 --- a/PWGLF/DataModel/LFNonPromptCascadeTables.h +++ b/PWGLF/DataModel/LFNonPromptCascadeTables.h @@ -121,6 +121,7 @@ DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); DECLARE_SOA_COLUMN(CentFV0A, centFV0A, float); DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); DECLARE_SOA_COLUMN(MultNTracksGlobal, multNTracksGlobal, int); +DECLARE_SOA_COLUMN(MultNTracksNP, multNTracksNP, int); DECLARE_SOA_COLUMN(ToiMask, toiMask, uint32_t); DECLARE_SOA_COLUMN(RunNumber, runNumber, int); DECLARE_SOA_COLUMN(NoSameBunchPileup, noSameBunchPileup, bool); @@ -454,13 +455,14 @@ DECLARE_SOA_TABLE(NPCascTableGen, "AOD", "NPCASCTABLEGen", DECLARE_SOA_TABLE(NPMCChargedTable, "AOD", "NPMCChargedTABLE", NPCascadeTable::PtGen, NPCascadeTable::PtRec, - NPCascadeTable::MultNTracksGlobal, + NPCascadeTable::MultNTracksNP, NPCascadeTable::MultGen); DECLARE_SOA_TABLE(NPCollisionTable, "AOD", "NPCollisionTABLE", NPCascadeTable::RunNumber, NPCascadeTable::GlobalBC, aod::collision::NumContrib, NPCascadeTable::MultNTracksGlobal, + NPCascadeTable::MultNTracksNP, NPCascadeTable::CentFT0M, NPCascadeTable::MultFT0M); DECLARE_SOA_INDEX_COLUMN_FULL(NPCollision, npCollision, int32_t, NPCollisionTable, ""); diff --git a/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx b/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx index 79c74df4307..59639154225 100644 --- a/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx +++ b/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx @@ -203,6 +203,7 @@ struct NonPromptCascadeTask { using CollisionCandidatesRun3MC = soa::Join; using TracksWithLabel = soa::Join; using TracksWithSel = soa::Join; + using TracksWithLabelSel = soa::Join; Preslice perCollision = aod::track::collisionId; Preslice perCollisionMC = aod::track::collisionId; @@ -818,185 +819,294 @@ struct NonPromptCascadeTask { // tracks: Join // mcCollisions: aod::McCollisions // mcParticles : aod::McParticles - void processdNdetaMC(CollisionCandidatesRun3MC const& colls, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, - TracksWithLabel const& tracks) + TracksWithLabelSel const& tracks) { - //------------------------------------------------------------ - // Downscaling output table by BC as there is no pileup in MC - //------------------------------------------------------------ - int ds = 1; - //------------------------------------------------------------- - // MC mult for all MC coll - //-------------------------------------------------------------- - std::vector mcMult(mcCollisions.size(), 0); - for (auto const& mcp : mcParticles) { - int mcid = mcp.mcCollisionId(); - if (mcid < 0 || static_cast(mcid) >= mcMult.size()) - continue; + // ------------------------------------------------------------ + // Helper: accepted generated charged primary + // ------------------------------------------------------------ + auto isAcceptedMCParticle = [&](auto const& mcp) { + if (!mcp.isPhysicalPrimary()) { + return false; + } + if (std::abs(mcp.eta()) > cfgEtaCutdNdeta) { + return false; + } - // apply your primary/eta/charge definition here - if (!mcp.isPhysicalPrimary()) - continue; - if (std::abs(mcp.eta()) > cfgEtaCutdNdeta) - continue; int q = 0; if (auto pdg = pdgDB->GetParticle(mcp.pdgCode())) { q = static_cast(std::round(pdg->Charge() / 3.0)); } - if (q == 0) - continue; + return q != 0; + }; + // ------------------------------------------------------------ + // Helper: accepted reconstructed track + // Use same cuts as data. + // ------------------------------------------------------------ + auto isAcceptedRecoTrack = [&](auto const& trk) { + if (std::fabs(trk.eta()) >= cfgEtaCutdNdeta) { + return false; + } + if (trk.tpcNClsFound() < 80) { + return false; + } + if (trk.tpcNClsCrossedRows() < 100) { + return false; + } + if (!trk.isGlobalTrack()) { + return false; + } + return true; + }; + + // ------------------------------------------------------------ + // 1) MC charged multiplicity per MC collision + // mcid: mc collision row of mc particle + // ------------------------------------------------------------ + std::vector mcMult(mcCollisions.size(), 0); + // std::cout << "mcCollisions size:" << mcCollisions.size() << std::endl; + for (auto const& mcp : mcParticles) { + const int mcid = mcp.mcCollisionId(); + // std::cout << "mcid:" << mcid << std::endl; + if (mcid < 0 || static_cast(mcid) >= mcMult.size()) { + std::cout << "0 This should never happen ?" << std::endl; + continue; + } + if (!isAcceptedMCParticle(mcp)) { + continue; + } ++mcMult[mcid]; } // ------------------------------------------------------------ - // Build mapping: (aod::Collisions row id used by tracks.collisionId()) - // -> dense index in 'colls' (0..colls.size()-1) - // We assume col.globalIndex() refers to the original aod::Collisions row. + // 2) Map aod::Collisions row id -> dense index in 'colls' // ------------------------------------------------------------ int maxCollRowId = -1; for (auto const& trk : tracks) { maxCollRowId = std::max(maxCollRowId, static_cast(trk.collisionId())); } + std::vector collRowIdToDense(maxCollRowId + 1, -1); - int dense = 0; + int denseIdx = 0; for (auto const& col : colls) { - const int collRowId = col.globalIndex(); // row id in aod::Collisions - if (collRowId >= 0 && static_cast(collRowId) < collRowIdToDense.size()) { - collRowIdToDense[collRowId] = dense; + const int collRowId = col.globalIndex(); + + if (collRowId >= 0 && collRowId <= maxCollRowId) { + collRowIdToDense[collRowId] = denseIdx; + } else { + std::cout << "1 This should never happen ?" << std::endl; } - ++dense; + ++denseIdx; } // ------------------------------------------------------------ - // Reco multiplicity per *dense collision index in colls* + // 3) Reco multiplicity per reconstructed collision // ------------------------------------------------------------ std::vector recoMultDense(colls.size(), 0); + for (auto const& trk : tracks) { - if (std::abs(trk.eta()) > cfgEtaCutdNdeta) { + if (!isAcceptedRecoTrack(trk)) { continue; } + const int collRowId = trk.collisionId(); - if (collRowId < 0 || static_cast(collRowId) >= collRowIdToDense.size()) { + // std::cout << collRowId << std::endl; + if (collRowId < 0 || collRowId > maxCollRowId) { + // std::cout << "2 This should never happen ?" << std::endl; continue; } const int dIdx = collRowIdToDense[collRowId]; - if (dIdx >= 0) { - ++recoMultDense[dIdx]; + if (dIdx < 0) { + // Tracks has collId which is not in the list created above + std::cout << "3 This should never happen ?" << std::endl; + continue; } + ++recoMultDense[dIdx]; } // ------------------------------------------------------------ - // MC bookkeeping: index by ROW INDEX (0..size-1), not globalIndex() + // 4) Bookkeeping // ------------------------------------------------------------ std::vector isReco(mcParticles.size(), 0); - std::vector isRecoMult(mcParticles.size(), 0.f); - std::vector mcReconstructed(mcCollisions.size(), 0); - // Optional cache of MC multiplicity per MC collision - std::vector mcMultCache(mcCollisions.size(), -1.f); + // MC collision has a reconstructed collision in 'colls'. + // This must be independent of reco-track cuts. + std::vector mcCollisionHasRecoCollision(mcCollisions.size(), 0); + + for (auto const& col : colls) { + const int mcid = col.mcCollisionId(); + if (mcid >= 0 && static_cast(mcid) < mcCollisionHasRecoCollision.size()) { + mcCollisionHasRecoCollision[mcid] = 1; + } else { + std::cout << "4 This should never happen ?" << std::endl; + } + } + + // Downscale output table per MC collision + std::vector writeMcCollision(mcCollisions.size(), 0); + int ds = 1; + for (auto const& mcColl : mcCollisions) { + const int mcid = mcColl.globalIndex(); + if (mcid < 0 || static_cast(mcid) >= writeMcCollision.size()) { + std::cout << "5 This should never happen ?" << std::endl; + continue; + } + if ((ds % cfgDownscaleMB) == 0) { + writeMcCollision[mcid] = 1; + } + ++ds; + } + + std::vector writeRecoCollision(colls.size(), 0); + for (int iColl = 0; iColl < static_cast(colls.size()); ++iColl) { + if (((iColl + 1) % cfgDownscaleMB) == 0) { + writeRecoCollision[iColl] = 1; + } + } // ------------------------------------------------------------ - // Single pass over tracks: fill RM for tracks whose collision is in colls + // 5) Matched reconstructed tracks // ------------------------------------------------------------ for (auto const& trk : tracks) { - // Accept reco track - if (std::abs(trk.eta()) > cfgEtaCutdNdeta) { + if (!isAcceptedRecoTrack(trk)) { continue; } - // Map track's collision row id -> dense colls index const int collRowId = trk.collisionId(); - if (collRowId < 0 || static_cast(collRowId) >= collRowIdToDense.size()) { + if (collRowId < 0 || collRowId > maxCollRowId) { continue; } + const int dIdx = collRowIdToDense[collRowId]; if (dIdx < 0) { - continue; // this track's collision is not in our 'colls' view + continue; } - // Get the collision row (dense index in colls view) auto col = colls.rawIteratorAt(dIdx); - // MC collision id (row index in aod::McCollisions) const int mcCollId = col.mcCollisionId(); + const float multReco = recoMultDense[dIdx]; + const float ptReco = trk.pt(); + if (mcCollId < 0 || static_cast(mcCollId) >= mcCollisions.size()) { + if (writeRecoCollision[dIdx]) { + // Fake: accepted reco track whose reconstructed collision has no valid MC collision label. + NPMCNTable(-1.f, ptReco, multReco, -1.f); + } continue; } - mcReconstructed[mcCollId] = 1; - // MC particle id (row index in aod::McParticles) const int mcPid = trk.mcParticleId(); if (mcPid < 0 || static_cast(mcPid) >= mcParticles.size()) { + if (writeMcCollision[mcCollId]) { + // Fake: accepted reco track with invalid or missing MC particle label. + NPMCNTable(-2.f, ptReco, multReco, -2.f); + } continue; } - // MC multiplicity for that MC collision (cache) - float mult = mcMultCache[mcCollId]; - if (mult < 0.f) { - std::vector tmp; - mult = mcMult[mcCollId]; - mcMultCache[mcCollId] = mult; - } - auto mcPar = mcParticles.rawIteratorAt(mcPid); - // Apply the same acceptance as in MC multiplicity definition - if (!mcPar.isPhysicalPrimary()) { - continue; - } - if (std::abs(mcPar.eta()) > cfgEtaCutdNdeta) { + const int mcParCollId = mcPar.mcCollisionId(); + if (mcParCollId != mcCollId) { + if (writeMcCollision[mcCollId]) { + // Fake: reco collision and particle label point to different MC collisions. + NPMCNTable(-3.f, ptReco, multReco, -3.f); + } continue; } - int q = 0; - if (auto pdgEntry = pdgDB->GetParticle(mcPar.pdgCode())) { - q = static_cast(std::round(pdgEntry->Charge() / 3.0)); - } - if (q == 0) { + if (!isAcceptedMCParticle(mcPar)) { + if (writeMcCollision[mcCollId]) { + // Feed-in: accepted reco track matched to truth outside fiducial phase space. + NPMCNTable(-4.f, ptReco, multReco, -4.f); + } continue; } - // Mark reconstructed MC particle (now that it truly passed & matched) isReco[mcPid] = 1; - isRecoMult[mcPid] = mult; - const float multReco = col.multNTracksGlobal(); // or recoMultDense[dIdx] - const float ptReco = trk.pt(); + const float multMC = mcMult[mcCollId]; const float ptMC = mcPar.pt(); - mRegistrydNdeta.fill(HIST("hdNdetaRM/hdNdetaRM"), mult, multReco, ptMC, ptReco); - if (ds % cfgDownscaleMB == 0) { - NPMCNTable(ptMC, ptReco, mult, multReco); + mRegistrydNdeta.fill(HIST("hdNdetaRM/hdNdetaRM"), + multMC, + multReco, + ptMC, + ptReco); + + if (writeMcCollision[mcCollId]) { + // Matched: accepted truth particle reconstructed inside the fiducial reco phase space. + NPMCNTable(ptMC, ptReco, multReco, multMC); } - ds++; } // ------------------------------------------------------------ - // MC particles with no reco track (iterate by row index) + // 6) MC particles with no accepted reco track, + // but from MC collisions that have a reconstructed collision // ------------------------------------------------------------ for (int pid = 0; pid < static_cast(mcParticles.size()); ++pid) { - if (!isReco[pid]) { - auto mcp = mcParticles.rawIteratorAt(pid); - mRegistrydNdeta.fill(HIST("hdNdetaRM/hdNdetaRMNotInRecoTrk"), isRecoMult[pid], mcp.pt()); - NPMCNTable(mcp.pt(), -1, isRecoMult[pid], -1); + if (isReco[pid]) { + continue; + } + + auto mcp = mcParticles.rawIteratorAt(pid); + + if (!isAcceptedMCParticle(mcp)) { + continue; + } + + const int mcid = mcp.mcCollisionId(); + if (mcid < 0 || static_cast(mcid) >= mcMult.size()) { + continue; + } + + if (!mcCollisionHasRecoCollision[mcid]) { + continue; + } + + const float multMC = mcMult[mcid]; + + mRegistrydNdeta.fill(HIST("hdNdetaRM/hdNdetaRMNotInRecoTrk"), + multMC, + mcp.pt()); + + if (writeMcCollision[mcid]) { + // Missed track: accepted truth particle in a reconstructed MC collision, but no accepted reco track. + NPMCNTable(mcp.pt(), -1.f, -1.f, multMC); } } // ------------------------------------------------------------ - // Unreconstructed MC collisions (iterate by row index) + // 7) MC particles from MC collisions with no reconstructed collision // ------------------------------------------------------------ for (int mcid = 0; mcid < static_cast(mcCollisions.size()); ++mcid) { - if (!mcReconstructed[mcid]) { - std::vector mcptvec; - const int mult = mcMult[mcid]; - for (auto const& pt : mcptvec) { - mRegistrydNdeta.fill(HIST("hdNdetaRM/hdNdetaRMNotInRecoCol"), mult, pt); - NPMCNTable(pt, -2, mult, -2); + if (mcCollisionHasRecoCollision[mcid]) { + continue; + } + + const float multMC = mcMult[mcid]; + + for (auto const& mcp : mcParticles) { + if (mcp.mcCollisionId() != mcid) { + continue; + } + + if (!isAcceptedMCParticle(mcp)) { + continue; + } + + mRegistrydNdeta.fill(HIST("hdNdetaRM/hdNdetaRMNotInRecoCol"), + multMC, + mcp.pt()); + + if (writeMcCollision[mcid]) { + // Missed collision: accepted truth particle from an MC collision with no reconstructed collision. + NPMCNTable(mcp.pt(), -2.f, -2.f, multMC); } } } @@ -1022,6 +1132,20 @@ struct NonPromptCascadeTask { } ds++; } + auto tracksThisColl = tracks.sliceBy(perCollisionSel, coll.globalIndex()); + int multreco = 0; + std::vector recoPts; + // std::cout << "tracks:" << tracksThisColl.size() << std::endl; + for (auto const& track : tracksThisColl) { + // std::cout << track.pt() << " tracks " << track.isGlobalTrack() << std::endl; + if (std::fabs(track.eta()) < cfgEtaCutdNdeta && track.tpcNClsFound() >= 80 && track.tpcNClsCrossedRows() >= 100) { + if (track.isGlobalTrack()) { + multreco++; + recoPts.push_back(track.pt()); + } + } + } + mRegistrydNdeta.fill(HIST("hdNdetaData"), multreco); if (writeFlag) { if (mRunNumber != bc.runNumber()) { mRunNumber = bc.runNumber(); @@ -1030,23 +1154,13 @@ struct NonPromptCascadeTask { globalBC, coll.numContrib(), coll.multNTracksGlobal(), + multreco, coll.centFT0M(), coll.multFT0M()); - auto collIdx = NPCollsTable.lastIndex(); - auto tracksThisColl = tracks.sliceBy(perCollisionSel, coll.globalIndex()); - float multreco = 0.; - // std::cout << "tracks:" << tracksThisColl.size() << std::endl; - for (auto const& track : tracksThisColl) { - // std::cout << track.pt() << " tracks " << track.isGlobalTrack() << std::endl; - if (std::fabs(track.eta()) < cfgEtaCutdNdeta && track.tpcNClsFound() >= 80 && track.tpcNClsCrossedRows() >= 100) { - if (track.isGlobalTrack()) { - multreco++; - NPRecoCandTable(collIdx, track.pt()); - } - } + for (auto const pt : recoPts) { + NPRecoCandTable(collIdx, pt); } - mRegistrydNdeta.fill(HIST("hdNdetaData"), multreco); } } } @@ -1077,7 +1191,7 @@ struct NonPromptCascadeTask { } float centFT0M = coll.centFT0M(); float multFT0M = coll.multFT0M(); - NPCollsTable(mRunNumber, globalBC, coll.numContrib(), coll.multNTracksGlobal(), centFT0M, multFT0M); + NPCollsTable(mRunNumber, globalBC, coll.numContrib(), coll.multNTracksGlobal(), 0, centFT0M, multFT0M); } } };